示例#1
0
 def test_empty_inputs(self):
     rupture = mock.Mock()
     sites = [mock.Mock()]
     imts = [mock.Mock()]
     gsims = [mock.Mock()]
     with self.assertRaises(ValueError):
         GmfComputer(rupture, [], imts, gsims)
     with self.assertRaises(ValueError):
         GmfComputer(rupture, sites, [], gsims)
     with self.assertRaises(ValueError):
         GmfComputer(rupture, sites, imts, [])
示例#2
0
    def pre_execute(self):
        """
        Read the site collection and initialize GmfComputer, tags and seeds
        """
        super(ScenarioCalculator, self).pre_execute()
        trunc_level = self.oqparam.truncation_level
        correl_model = readinput.get_correl_model(self.oqparam)
        n_gmfs = self.oqparam.number_of_ground_motion_fields
        rupture = readinput.get_rupture(self.oqparam)
        self.gsims = readinput.get_gsims(self.oqparam)
        self.rlzs_assoc = readinput.get_rlzs_assoc(self.oqparam)

        # filter the sites
        self.sitecol = filters.filter_sites_by_distance_to_rupture(
            rupture, self.oqparam.maximum_distance, self.sitecol)
        if self.sitecol is None:
            raise RuntimeError('All sites were filtered out! '
                               'maximum_distance=%s km' %
                               self.oqparam.maximum_distance)
        self.tags = numpy.array(
            sorted(['scenario-%010d' % i for i in range(n_gmfs)]),
            (bytes, 100))
        self.computer = GmfComputer(rupture, self.sitecol, self.oqparam.imtls,
                                    self.gsims, trunc_level, correl_model)
        rnd = random.Random(self.oqparam.random_seed)
        self.tag_seed_pairs = [(tag, rnd.randint(0, calc.MAX_INT))
                               for tag in self.tags]
        self.sescollection = [{
            tag: Rupture(tag, seed, rupture)
            for tag, seed in self.tag_seed_pairs
        }]
示例#3
0
 def pre_execute(self):
     """
     Read the site collection and initialize GmfComputer and seeds
     """
     oq = self.oqparam
     cinfo = source.CompositionInfo.fake(readinput.get_gsim_lt(oq))
     self.datastore['csm_info'] = cinfo
     if 'rupture_model' not in oq.inputs:
         logging.warn('There is no rupture_model, the calculator will just '
                      'import data without performing any calculation')
         super().pre_execute()
         return
     self.rup = readinput.get_rupture(oq)
     self.gsims = readinput.get_gsims(oq)
     self.cmaker = ContextMaker(self.gsims, oq.maximum_distance,
                                {'filter_distance': oq.filter_distance})
     super().pre_execute()
     self.datastore['oqparam'] = oq
     self.rlzs_assoc = cinfo.get_rlzs_assoc()
     E = oq.number_of_ground_motion_fields
     events = numpy.zeros(E, readinput.stored_event_dt)
     events['eid'] = numpy.arange(E)
     ebr = EBRupture(self.rup, 0, self.sitecol.sids, events)
     self.datastore['events'] = ebr.events
     rupser = calc.RuptureSerializer(self.datastore)
     rupser.save([ebr])
     rupser.close()
     self.computer = GmfComputer(ebr, self.sitecol, oq.imtls, self.cmaker,
                                 oq.truncation_level, oq.correl_model)
示例#4
0
 def pre_execute(self):
     """
     Read the site collection and initialize GmfComputer and seeds
     """
     super(ScenarioCalculator, self).pre_execute()
     oq = self.oqparam
     gsim_lt = readinput.get_gsim_lt(oq)
     cinfo = source.CompositionInfo.fake(gsim_lt)
     self.datastore['csm_info'] = cinfo
     self.datastore['oqparam'] = oq
     self.rlzs_assoc = cinfo.get_rlzs_assoc()
     if 'rupture_model' not in oq.inputs:
         logging.warn('There is no rupture_model, the calculator will just '
                      'import data without performing any calculation')
         return
     ebr, self.sitecol = readinput.get_rupture_sitecol(oq, self.sitecol)
     self.gsims = readinput.get_gsims(oq)
     self.datastore['events'] = ebr.events
     rupser = calc.RuptureSerializer(self.datastore)
     rupser.save([ebr])
     rupser.close()
     trunc_level = oq.truncation_level
     correl_model = oq.get_correl_model()
     self.computer = GmfComputer(ebr, self.sitecol, oq.imtls,
                                 ContextMaker(self.gsims), trunc_level,
                                 correl_model)
示例#5
0
 def pre_execute(self):
     """
     Read the site collection and initialize GmfComputer, etags and seeds
     """
     super(ScenarioCalculator, self).pre_execute()
     oq = self.oqparam
     trunc_level = oq.truncation_level
     correl_model = readinput.get_correl_model(oq)
     n_gmfs = oq.number_of_ground_motion_fields
     rupture = readinput.get_rupture(oq)
     self.gsims = readinput.get_gsims(oq)
     maxdist = oq.maximum_distance['default']
     with self.monitor('filtering sites', autoflush=True):
         self.sitecol = filters.filter_sites_by_distance_to_rupture(
             rupture, maxdist, self.sitecol)
     if self.sitecol is None:
         raise RuntimeError(
             'All sites were filtered out! maximum_distance=%s km' %
             maxdist)
     self.etags = numpy.array(
         sorted(['scenario-%010d~ses=1' % i for i in range(n_gmfs)]),
         (bytes, 100))
     self.computer = GmfComputer(rupture, self.sitecol, oq.imtls,
                                 self.gsims, trunc_level, correl_model)
     gsim_lt = readinput.get_gsim_lt(oq)
     cinfo = source.CompositionInfo.fake(gsim_lt)
     self.datastore['csm_info'] = cinfo
     self.rlzs_assoc = cinfo.get_rlzs_assoc()
示例#6
0
def calculate_gmfs_filter(source_model, gsimlt, filter1, cmake,
                          gsim_list, recMeshExposure, matrixMagsMin,
                          matrixMagsStep, matrixDistsMin, matrixDistsStep,
                          GMPEmatrix, imts, trunc_level):
    # The source filter will return only sites within the integration distance
    gmfs_median = []
    # properties = []
    # Source-Site Filtering
    for source in source_model:
        trt = source.trt
        gmpe_lt_index = gsimlt.all_trts.index(trt)

        for src, s_sites in filter1(source[:]):
            hypo_list = []
            Mag = []
            for rup in src.iter_ruptures():
                Mag.append(round(rup.mag, 1))
                hypo_rup = rup.hypocenter
                hypo_list.append(hypo_rup)

            rupsHypo = Mesh.from_points_list(hypo_list)
            if gsim_list[gmpe_lt_index].REQUIRES_DISTANCES == {'rjb'}:
                distRupExposure = np.around(
                        RectangularMesh.get_joyner_boore_distance(
                                recMeshExposure, rupsHypo))
            elif gsim_list[gmpe_lt_index].REQUIRES_DISTANCES == {'rrup'}:
                distRupExposure = np.around(RectangularMesh.get_min_distance(
                        recMeshExposure, rupsHypo))

            filteringIndex = []
            for i in range(len(Mag)):
                indexMag = int((Mag[i] - matrixMagsMin) / matrixMagsStep)
                indexDist = int((distRupExposure[i] - matrixDistsMin)
                                / matrixDistsStep)
                filteringIndex.append(GMPEmatrix[indexDist, indexMag])

            src_iter = src.iter_ruptures()
            filteredRup = list(compress(src_iter, filteringIndex))

            for rup in filteredRup:
                gmf_computer = GmfComputer(rup, s_sites, imts, cmake,
                                           truncation_level=trunc_level)
                gmf_median = {}
                # if we have more than one gsim per trt, we need to do this
                # for gsim in gsim_list:
                #     gmf_median[gsim] = gmf_computer.compute(gsim,
                # num_events=1)
                gmf_median[gsim_list[gmpe_lt_index]] = gmf_computer.compute(
                                    gsim_list[gmpe_lt_index], num_events=1)
                gmf_median['rate'] = rup.occurrence_rate
                gmf_median['sites'] = s_sites
                gmfs_median.append(gmf_median)
                # FiltMag = str(rup.mag)
                # FiltHypo = str(rup.hypocenter)
                # FiltRate = str(rup.occurrence_rate)
                # properties.append([FiltMag,FiltHypo,FiltRate])
    return gmfs_median
示例#7
0
 def get_displacement_field(self,
                            rupture,
                            sitecol,
                            cmaker,
                            num_events=1,
                            truncation_level=None,
                            correlation_model=None):
     """
     Returns the field of displacements (m) and ground motion values
     """
     gmf_loc = self._get_gmv_field_location()
     # Gets the ground motion fields
     gmf_computer = GmfComputer(rupture, sitecol,
                                [str(imt) for imt in self.imts], cmaker,
                                truncation_level, correlation_model)
     gmfs = gmf_computer.compute(self, num_events, seed=None)
     # Get the PGA field
     gmv = gmfs[gmf_loc[0]]
     # Return the critical acceleration and proportion of mapped area
     properties = self._setup_properties(gmf_computer.sctx,
                                         gmf_computer.rupture)
     # Get probability of failure. If PGA < a_c, no failure is possible,
     # whilst for PGA > a_c the probability of any individual location
     # observing displacement is equal to the proportion of mapped area
     p_failure = np.zeros_like(gmv)
     for i in range(num_events):
         p_failure[:,
                   i] = properties["pma"] * (gmv[:, i] >= properties["a_c"])
     # Sample the occurence of landsliding.
     mask = np.random.uniform(0., 1., p_failure.shape) <= p_failure
     displacement = np.zeros([1, len(gmf_computer.sctx.sids), num_events],
                             dtype=np.float32)
     if not np.any(mask):
         # No displacement at any site - return zeros and the ground motion
         # fields
         return displacement, gmfs, p_failure
     # If any displacement is registered then need to turn a_c into
     # array of shape [nsites, num_events]
     properties["a_c"] = np.tile(
         # Re-shape to 1-D vector then repeat for num. events
         np.reshape(properties["a_c"], [properties["n"], 1]),
         num_events)
     # Calculate number of cycles factor
     n_cycles = self._get_number_cycles(gmf_computer.rupture.mag)
     # Calculate ratio of acceleration to critical acceleration
     a_c_ais = properties["a_c"][mask] / gmv[mask]
     # Expected displacement factor returns lower and upper bounds, assumed
     # to be uniformly distributed
     e_d_ub, e_d_lb = self._get_expected_displacement_factor(a_c_ais)
     # Final displacement sampling uniformly between the upper and
     # lower bound displacement factors - as described by Equation 4-25
     displacement[0][mask] = M_PER_INCH * n_cycles * gmv[mask] *\
         np.random.uniform(e_d_lb, e_d_ub, a_c_ais.shape)
     return displacement, gmfs, p_failure
示例#8
0
 def pre_execute(self):
     """
     Read the site collection and initialize GmfComputer and seeds
     """
     oq = self.oqparam
     cinfo = logictree.FullLogicTree.fake(readinput.get_gsim_lt(oq))
     self.realizations = cinfo.get_realizations()
     self.datastore['full_lt'] = cinfo
     if 'rupture_model' not in oq.inputs:
         logging.warning(
             'There is no rupture_model, the calculator will just '
             'import data without performing any calculation')
         super().pre_execute()
         return
     self.rup = readinput.get_rupture(oq)
     self.gsims = readinput.get_gsims(oq)
     R = len(self.gsims)
     self.cmaker = ContextMaker(
         '*', self.gsims, {
             'maximum_distance': oq.maximum_distance,
             'filter_distance': oq.filter_distance
         })
     super().pre_execute()
     self.datastore['oqparam'] = oq
     self.store_rlz_info({})
     rlzs_by_gsim = cinfo.get_rlzs_by_gsim(0)
     E = oq.number_of_ground_motion_fields
     n_occ = numpy.array([E])
     ebr = EBRupture(self.rup, 0, 0, n_occ)
     ebr.e0 = 0
     events = numpy.zeros(E * R, events_dt)
     for rlz, eids in ebr.get_eids_by_rlz(rlzs_by_gsim).items():
         events[rlz * E:rlz * E + E]['id'] = eids
         events[rlz * E:rlz * E + E]['rlz_id'] = rlz
     self.datastore['events'] = self.events = events
     rupser = calc.RuptureSerializer(self.datastore)
     rup_array = get_rup_array([ebr], self.src_filter())
     if len(rup_array) == 0:
         maxdist = oq.maximum_distance(self.rup.tectonic_region_type,
                                       self.rup.mag)
         raise RuntimeError('There are no sites within the maximum_distance'
                            ' of %s km from the rupture' % maxdist)
     rupser.save(rup_array)
     rupser.close()
     self.computer = GmfComputer(ebr, self.sitecol, oq.imtls, self.cmaker,
                                 oq.truncation_level, oq.correl_model,
                                 self.amplifier)
     M32 = (numpy.float32, len(self.oqparam.imtls))
     self.sig_eps_dt = [('eid', numpy.uint64), ('sig', M32), ('eps', M32)]
示例#9
0
def main(params):
    # example with 2x2=4 realizations with weights .36, .24, .24, .16
    inp = read_input(params)
    print(inp)
    print(inp.gsim_lt.get_rlzs_by_gsim_trt())
    acc = AccumDict(accum=[])
    ebrs = sample_ebruptures(inp.groups, inp.cmakerdict)
    ne = sum(ebr.n_occ for ebr in ebrs)
    print('There are %d ruptures and %d events' % (len(ebrs), ne))
    df = get_ebr_df(ebrs, inp.cmakerdict)
    print(df.groupby('rlz').count())  # there are 8, 9, 11, 8 events per rlz
    for ebr in ebrs:
        cmaker = inp.cmakerdict[ebr.rupture.tectonic_region_type]
        gmf_dict, _ = GmfComputer(ebr, inp.sitecol, cmaker).compute_all()
        acc += gmf_dict
    print(pandas.DataFrame(acc))
示例#10
0
 def calculate_from_rupture(self, rupture, rup_sitecol=None):
     """Method to generate scenario ground motion
     for a specific rupture
     """
     if rup_sitecol is None:
         rup_sitecol = self.sitecol
     computer = GmfComputer(rupture,
                            rup_sitecol,
                            self.imts, [self.gsim],
                            truncation_level=0)
     gmf = computer.compute(self.gsim, 1)
     gmf = gmf.flatten()
     print 'gmf', gmf
     self.rupture_scenario = rupture
     self.rupture_gmf = gmf
     self.rupture_gmf_mmi = rsa2mmi8(gmf, period=1.0)
示例#11
0
def calc_gmfs_no_IM_filter(source_model, imts, gsim_list, trunc_level,
                           gsimlt, filter1, cmake):
    gmfs_median = []
    for source in source_model:
        trt = source.trt
        gmpe_lt_index = gsimlt.all_trts.index(trt)
        for src, s_sites in filter1(source[:]):
            src_iter = src.iter_ruptures()
            for rup in src_iter:
                gmf_computer = GmfComputer(rup, s_sites, imts, cmake,
                                           truncation_level=trunc_level)
                gmf_median = {}
                gmf_median[gsim_list[gmpe_lt_index]] = gmf_computer.compute(
                                    gsim_list[gmpe_lt_index], num_events=1)
                gmf_median['rate'] = rup.occurrence_rate
                gmf_median['sites'] = s_sites

                gmfs_median.append(gmf_median)
    return gmfs_median
示例#12
0
 def calculate_from_pts(self):
     """Generates ruptures for each pt source and calculates ground motion
     field.
     :returns gmfs:
         Set of ruptures and associated parameters for ground motion
         calculations  
     """
     for pt in self.sources:
         #        rupture_mags = []
         #        rupture_hypocenter = []
         ruptures = pt.iter_ruptures()
         for rupture in ruptures:
             computer = GmfComputer(rupture,
                                    self.sitecol,
                                    self.imts, [self.gsim],
                                    truncation_level=0)
             gmf = computer.compute(self.gsim, 1)
             gmf = gmf.flatten()
             self.rupture_list.append(rupture)
             self.gmf_list.append(gmf)
示例#13
0
 def pre_execute(self):
     """
     Read the site collection and initialize GmfComputer and seeds
     """
     super(ScenarioCalculator, self).pre_execute()
     oq = self.oqparam
     trunc_level = oq.truncation_level
     correl_model = oq.get_correl_model()
     ebr, self.sitecol = readinput.get_rupture_sitecol(oq, self.sitecol)
     self.gsims = readinput.get_gsims(oq)
     self.datastore['events'] = ebr.events
     rupser = calc.RuptureSerializer(self.datastore)
     rupser.save([ebr])
     rupser.close()
     self.computer = GmfComputer(ebr, self.sitecol, oq.imtls, self.gsims,
                                 trunc_level, correl_model)
     gsim_lt = readinput.get_gsim_lt(oq)
     cinfo = source.CompositionInfo.fake(gsim_lt)
     self.datastore['csm_info'] = cinfo
     self.rlzs_assoc = cinfo.get_rlzs_assoc()
示例#14
0
    def create_ruptures(self):
        oqparam = models.oqparam(self.job.id)
        self.imts = map(from_string, oqparam.imtls)
        self.rupture = readinput.get_rupture(oqparam)

        # check filtering
        trunc_level = oqparam.truncation_level
        maximum_distance = oqparam.maximum_distance
        self.sites = filters.filter_sites_by_distance_to_rupture(
            self.rupture, maximum_distance, self.site_collection)
        if self.sites is None:
            raise RuntimeError('All sites where filtered out! '
                               'maximum_distance=%s km' % maximum_distance)

        # create ses output
        output = models.Output.objects.create(oq_job=self.job,
                                              display_name='SES Collection',
                                              output_type='ses')
        self.ses_coll = models.SESCollection.create(output=output)

        # create gmf output
        output = models.Output.objects.create(oq_job=self.job,
                                              display_name="GMF",
                                              output_type="gmf_scenario")
        self.gmf = models.Gmf.objects.create(output=output)

        with self.monitor('saving ruptures', autoflush=True):
            self.tags = [
                'scenario-%010d' % i
                for i in xrange(oqparam.number_of_ground_motion_fields)
            ]
            _, self.rupids, self.seeds = create_db_ruptures(
                self.rupture, self.ses_coll, self.tags,
                self.oqparam.random_seed)

        correlation_model = models.get_correl_model(
            models.OqJob.objects.get(pk=self.job.id))
        gsim = valid.gsim(oqparam.gsim)
        self.computer = GmfComputer(self.rupture, self.sites, oqparam.imtls,
                                    [gsim], trunc_level, correlation_model)
示例#15
0
 def pre_execute(self):
     """
     Read the site collection and initialize GmfComputer and seeds
     """
     super(ScenarioCalculator, self).pre_execute()
     oq = self.oqparam
     trunc_level = oq.truncation_level
     correl_model = oq.get_correl_model()
     rup = readinput.get_rupture(oq)
     rup.seed = self.oqparam.random_seed
     self.gsims = readinput.get_gsims(oq)
     maxdist = oq.maximum_distance['default']
     with self.monitor('filtering sites', autoflush=True):
         self.sitecol = filters.filter_sites_by_distance_to_rupture(
             rup, maxdist, self.sitecol)
     if self.sitecol is None:
         raise RuntimeError(
             'All sites were filtered out! maximum_distance=%s km' %
             maxdist)
     # eid, ses, occ, sample
     events = numpy.zeros(oq.number_of_ground_motion_fields,
                          calc.stored_event_dt)
     events['eid'] = numpy.arange(oq.number_of_ground_motion_fields)
     rupture = calc.EBRupture(rup, self.sitecol.sids, events, 0, 0)
     rupture.sidx = 0
     rupture.eidx1 = 0
     rupture.eidx2 = len(events)
     self.datastore['sids'] = self.sitecol.sids
     self.datastore['events/grp-00'] = events
     array, nbytes = calc.RuptureSerializer.get_array_nbytes([rupture])
     self.datastore.extend('ruptures/grp-00', array, nbytes=nbytes)
     self.computer = GmfComputer(rupture, self.sitecol, oq.imtls,
                                 self.gsims, trunc_level, correl_model)
     gsim_lt = readinput.get_gsim_lt(oq)
     cinfo = source.CompositionInfo.fake(gsim_lt)
     self.datastore['csm_info'] = cinfo
     self.rlzs_assoc = cinfo.get_rlzs_assoc()
示例#16
0
def event_based(proxies, full_lt, oqparam, dstore, monitor):
    """
    Compute GMFs and optionally hazard curves
    """
    alldata = AccumDict(accum=[])
    sig_eps = []
    times = []  # rup_id, nsites, dt
    hcurves = {}  # key -> poes
    trt_smr = proxies[0]['trt_smr']
    fmon = monitor('filtering ruptures', measuremem=False)
    cmon = monitor('computing gmfs', measuremem=False)
    with dstore:
        trt = full_lt.trts[trt_smr // len(full_lt.sm_rlzs)]
        srcfilter = SourceFilter(dstore['sitecol'],
                                 oqparam.maximum_distance(trt))
        rupgeoms = dstore['rupgeoms']
        rlzs_by_gsim = full_lt._rlzs_by_gsim(trt_smr)
        param = vars(oqparam).copy()
        param['imtls'] = oqparam.imtls
        param['min_iml'] = oqparam.min_iml
        param['maximum_distance'] = oqparam.maximum_distance(trt)
        cmaker = ContextMaker(trt, rlzs_by_gsim, param)
        min_mag = getdefault(oqparam.minimum_magnitude, trt)
        for proxy in proxies:
            t0 = time.time()
            with fmon:
                if proxy['mag'] < min_mag:
                    continue
                sids = srcfilter.close_sids(proxy, trt)
                if len(sids) == 0:  # filtered away
                    continue
                proxy.geom = rupgeoms[proxy['geom_id']]
                ebr = proxy.to_ebr(cmaker.trt)  # after the geometry is set
                try:
                    computer = GmfComputer(ebr,
                                           srcfilter.sitecol.filtered(sids),
                                           cmaker, oqparam.correl_model,
                                           oqparam.cross_correl,
                                           oqparam._amplifier,
                                           oqparam._sec_perils)
                except FarAwayRupture:
                    continue
            with cmon:
                data = computer.compute_all(sig_eps)
            dt = time.time() - t0
            times.append((computer.ebrupture.id, len(computer.ctx.sids), dt))
            for key in data:
                alldata[key].extend(data[key])
    for key, val in sorted(alldata.items()):
        if key in 'eid sid rlz':
            alldata[key] = U32(alldata[key])
        else:
            alldata[key] = F32(alldata[key])
    gmfdata = strip_zeros(pandas.DataFrame(alldata))
    if len(gmfdata) and oqparam.hazard_curves_from_gmfs:
        hc_mon = monitor('building hazard curves', measuremem=False)
        for (sid, rlz), df in gmfdata.groupby(['sid', 'rlz']):
            with hc_mon:
                poes = calc.gmvs_to_poes(df, oqparam.imtls,
                                         oqparam.ses_per_logic_tree_path)
                for m, imt in enumerate(oqparam.imtls):
                    hcurves[rsi2str(rlz, sid, imt)] = poes[m]
    times = numpy.array([tup + (monitor.task_no, ) for tup in times], time_dt)
    times.sort(order='rup_id')
    if not oqparam.ground_motion_fields:
        gmfdata = ()
    return dict(gmfdata=gmfdata,
                hcurves=hcurves,
                times=times,
                sig_eps=numpy.array(sig_eps, sig_eps_dt(oqparam.imtls)))
示例#17
0
    def get_displacement_field(self,
                               rupture,
                               sitecol,
                               cmaker,
                               num_events=1,
                               truncation_level=None,
                               correlation_model=None):
        """
        Returns the field of displacements
        """
        # Calculate the ground motion fields
        gmf_loc = self._get_gmv_field_location()
        gmf_computer = GmfComputer(rupture, sitecol,
                                   [str(imt) for imt in self.imts], cmaker,
                                   truncation_level, correlation_model)
        gmfs = gmf_computer.compute(self, num_events, seed=None)
        # Get the PGA field - should have the dimension [nsites, num_events]
        gmvs = gmfs[gmf_loc[0]]
        # Get site and rupture related properties
        properties = self._setup_properties(gmf_computer.sctx,
                                            gmf_computer.rupture)

        # Determine the probability of failure
        # Both the pga threshold and the settlement need to take the same shape
        # as the ground motion values - in this case a 2-D form is needed
        for key in ["pga_threshold", "settlement"]:
            # Tile
            properties[key] = np.tile(
                # Re-shape to 1-D vector then repeat for num. events
                np.reshape(properties[key], [properties["n"], 1]),
                num_events)

        p_failure = np.zeros_like(gmvs)
        for j in range(p_failure.shape[1]):
            p_failure[:, j] = self.get_failure_model(gmf_computer.sctx,
                                                     gmvs[:, j], properties)
        # Setup displacement field
        displacement = np.zeros([2, len(gmf_computer.sctx.sids), num_events],
                                dtype=np.float32)
        # Sample the field
        mask = np.random.uniform(0., 1., p_failure.shape) <= p_failure
        if not np.any(mask):
            # No liquefaction is observed - return a field of zeros!
            # Note that there are two intensity measures, so to speak, which
            # represent lateral spread and settlement respectively
            # Return the zero displacement fields and the GMFs
            return displacement, gmfs, p_failure

        # Some sites observe liquefaction - now to calculate lateral
        # spread and settlement
        # Calculate PGA to threshold PGA ratio
        pga_pgat = gmvs[mask] / properties["pga_threshold"][mask]

        # Calculate lateral spread
        lateral_spread = np.zeros_like(pga_pgat)
        # Get the displacement correction factor - which is a function of mag
        for (low, high), (m, c) in D_LATERAL_SPREAD:
            dls_idx = np.logical_and(pga_pgat >= low, pga_pgat < high)
            if np.any(dls_idx):
                lateral_spread[dls_idx] = M_PER_INCH * (
                    properties["kdelta"] * (m * pga_pgat[dls_idx] + c))
        displacement[0][mask] = np.copy(lateral_spread)
        # Settlement is a simple scalar function
        displacement[1][mask] = properties["settlement"][mask]
        return displacement, gmfs, p_failure