Beispiel #1
0
    def __init__(self, key, colour: str = None, blocking=False, buffer_dist=0,
                 ac: dict = AIRCRAFT_LIST['Default'],
                 wind_vel: float = 0, wind_dir: float = 0):
        super().__init__(key, colour, blocking, buffer_dist)
        delattr(self, '_colour')

        self._layers = [
            TemporalPopulationEstimateLayer(f'_strike_risk_tpe_{key}', buffer_dist=buffer_dist),
            RoadsLayer(f'_strike_risk_roads_{key}', buffer_dist=buffer_dist)]

        self.aircraft = casex.AircraftSpecs(casex.enums.AircraftType.FIXED_WING, ac['width'], ac['length'], ac['mass'])
        self.aircraft.set_ballistic_drag_coefficient(ac['bal_drag_coeff'])
        self.aircraft.set_ballistic_frontal_area(ac['frontal_area'])
        self.aircraft.set_glide_speed_ratio(ac['glide_speed'], ac['glide_ratio'])
        self.aircraft.set_glide_drag_coefficient(ac['glide_drag_coeff'])

        self.alt = ac['cruise_alt']
        self.vel = ac['cruise_speed']
        self.wind_vel = wind_vel
        # !! This is the direction the wind is COMING FROM !! #
        self.wind_dir = np.deg2rad(
            (((wind_dir - 180) % 360) - 90) % 360
        )

        self.event_prob = ac['failure_prob']

        self.bm = BallisticModel(self.aircraft)
        self.gm = GlideDescentModel(self.aircraft)
Beispiel #2
0
    def __init__(self,
                 key,
                 colour: str = None,
                 blocking=False,
                 buffer_dist=0,
                 ac_width: float = 2,
                 ac_length: float = 2,
                 ac_mass: float = 2,
                 ac_glide_ratio: float = 12,
                 ac_glide_speed: float = 15,
                 ac_glide_drag_coeff: float = 0.1,
                 ac_ballistic_drag_coeff: float = 0.8,
                 ac_ballistic_frontal_area: float = 0.2,
                 ac_failure_prob: float = 5e-3,
                 alt: float = 120,
                 vel: float = 18,
                 wind_vel: float = 5,
                 wind_dir: float = 90):
        super().__init__(key, colour, blocking, buffer_dist)
        delattr(self, '_colour')

        self._layers = [
            TemporalPopulationEstimateLayer(f'_strike_risk_tpe_{key}',
                                            buffer_dist=buffer_dist),
            RoadsLayer(f'_strike_risk_roads_{key}', buffer_dist=buffer_dist)
        ]

        self.aircraft = casex.AircraftSpecs(
            casex.enums.AircraftType.FIXED_WING, ac_width, ac_length, ac_mass)
        self.aircraft.set_ballistic_drag_coefficient(ac_ballistic_drag_coeff)
        self.aircraft.set_ballistic_frontal_area(ac_ballistic_frontal_area)
        self.aircraft.set_glide_speed_ratio(ac_glide_speed, ac_glide_ratio)
        self.aircraft.set_glide_drag_coefficient(ac_glide_drag_coeff)

        self.alt = alt
        self.vel = vel
        self.wind_vel = wind_vel
        # !! This is the direction the wind is COMING FROM !! #
        self.wind_dir = np.deg2rad((((wind_dir - 180) % 360) - 90) % 360)

        self.event_prob = ac_failure_prob

        self.bm = BallisticModel(self.aircraft)
        self.gm = GlideDescentModel(self.aircraft)
    def test_full_risk_map(self):

        bm = BallisticModel(self.aircraft)
        gm = GlideDescentModel(self.aircraft)
        fm = FatalityModel(0.3, 1e6, 34)
        ac_mass = self.aircraft.mass

        x, y = np.mgrid[0:self.raster_shape[0], 0:self.raster_shape[1]]
        eval_grid = np.vstack((x.ravel(), y.ravel())).T

        samples = 5000
        # Conjure up our distributions for various things
        alt = ss.norm(self.alt, 5).rvs(samples)
        vel = ss.norm(self.vel, 2.5).rvs(samples)
        wind_vels = ss.norm(self.wind_vel, 1).rvs(samples)
        wind_dirs = bearing_to_angle(
            ss.norm(self.wind_dir, np.deg2rad(5)).rvs(samples))
        wind_vel_y = wind_vels * np.sin(wind_dirs)
        wind_vel_x = wind_vels * np.cos(wind_dirs)

        (bm_mean,
         bm_cov), v_ib, a_ib = bm.transform(alt, vel,
                                            ss.uniform(0, 360).rvs(samples),
                                            wind_vel_y, wind_vel_x, 0, 0)
        (gm_mean,
         gm_cov), v_ig, a_ig = gm.transform(alt, vel,
                                            ss.uniform(0, 360).rvs(samples),
                                            wind_vel_y, wind_vel_x, 0, 0)
        sm_b = StrikeModel(self.raster_grid, self.resolution**2,
                           self.aircraft.width, a_ib)
        sm_g = StrikeModel(self.raster_grid, self.resolution**2,
                           self.aircraft.width, a_ig)
        premult = sm_b.premult_mat + sm_g.premult_mat

        offset_y, offset_x = self.raster_shape[0] // 2, self.raster_shape[
            1] // 2
        bm_pdf = ss.multivariate_normal(
            bm_mean + np.array([offset_y, offset_x]), bm_cov).pdf(eval_grid)
        gm_pdf = ss.multivariate_normal(
            gm_mean + np.array([offset_y, offset_x]), gm_cov).pdf(eval_grid)
        pdf = bm_pdf + gm_pdf
        pdf = pdf.reshape(self.raster_shape)

        padded_pdf = np.zeros(
            ((self.raster_shape[0] * 3) + 1, (self.raster_shape[1] * 3) + 1))
        padded_pdf[self.raster_shape[0]:self.raster_shape[0] * 2,
                   self.raster_shape[1]:self.raster_shape[1] * 2] = pdf
        padded_pdf = padded_pdf * self.event_prob
        padded_centre_y, padded_centre_x = self.raster_shape[
            0] + offset_y, self.raster_shape[1] + offset_x
        impact_ke_b = velocity_to_kinetic_energy(ac_mass, v_ib)
        impact_ke_g = velocity_to_kinetic_energy(ac_mass, v_ig)

        # Check if CUDA toolkit available through env var otherwise fallback to CPU bound numba version
        if not os.getenv('CUDA_HOME'):
            print('CUDA NOT found, falling back to Numba JITed CPU code')
            # Leaving parallelisation to Numba seems to be faster
            res = wrap_all_pipeline(self.raster_shape, padded_pdf,
                                    padded_centre_y, padded_centre_x, premult)

        else:

            res = np.zeros(self.raster_shape, dtype=float)
            threads_per_block = (32, 32)  # 1024 max per block
            blocks_per_grid = (int(
                np.ceil(self.raster_shape[1] / threads_per_block[1])),
                               int(
                                   np.ceil(self.raster_shape[0] /
                                           threads_per_block[0])))
            print('CUDA found, using config <<<' + str(blocks_per_grid) + ',' +
                  str(threads_per_block) + '>>>')
            wrap_pipeline_cuda[blocks_per_grid,
                               threads_per_block](self.raster_shape,
                                                  padded_pdf, padded_centre_y,
                                                  padded_centre_x, premult,
                                                  res)

        # Alternative joblib parallelisation
        # res = jl.Parallel(n_jobs=-1, prefer='threads', verbose=1)(
        #     jl.delayed(wrap_row_pipeline)(c, self.raster_shape, padded_pdf, (padded_centre_y, padded_centre_x), sm)
        #     for c in range(self.raster_shape[0]))

        strike_pdf = res
        # snapped_points = [snap_coords_to_grid(self.raster_indices, *coords) for coords in self.path_coords]

        import matplotlib.pyplot as mpl
        import matplotlib.colors as mc
        fig1, ax1 = mpl.subplots(1, 1)
        m1 = ax1.matshow(self.raster_grid, norm=mc.LogNorm())
        fig1.colorbar(m1, label='Population Density [people/km$^2$]')
        ax1.set_title(f'Population Density at t={self.hour}')
        ax1.set_xticks([0, self.raster_shape[1] - 1])
        ax1.set_yticks([0, self.raster_shape[0] - 1])
        ax1.set_xticklabels(
            [self.test_bound_coords[0], self.test_bound_coords[2]], )
        ax1.set_yticklabels(
            [self.test_bound_coords[3], self.test_bound_coords[1]], )
        fig1.tight_layout()
        fig1.savefig(f'tests/layers/figs/tpe_t{self.hour}.png',
                     bbox_inches='tight')
        fig1.show()

        if self.serialise:
            np.savetxt(f'strike_map_t{self.hour}', strike_pdf, delimiter=',')

        fig2, ax2 = mpl.subplots(1, 1)
        m2 = ax2.matshow(strike_pdf)
        fig2.colorbar(m2, label='Strike Risk [h$^{-1}$]')
        ax2.set_title(f'Strike Risk Map at t={self.hour}')
        ax2.set_xticks([0, self.raster_shape[1] - 1])
        ax2.set_yticks([0, self.raster_shape[0] - 1])
        ax2.set_xticklabels(
            [self.test_bound_coords[0], self.test_bound_coords[2]], )
        ax2.set_yticklabels(
            [self.test_bound_coords[3], self.test_bound_coords[1]], )
        fig2.tight_layout()
        fig2.savefig(f'tests/layers/figs/risk_strike_t{self.hour}.png',
                     bbox_inches='tight')
        fig2.show()

        fatality_pdf = fm.transform(strike_pdf,
                                    impact_ke=impact_ke_g) + fm.transform(
                                        strike_pdf, impact_ke=impact_ke_b)
        if self.serialise:
            np.savetxt(f'fatality_map_t{self.hour}',
                       fatality_pdf,
                       delimiter=',')

        fig3, ax3 = mpl.subplots(1, 1)
        m3 = ax3.matshow(fatality_pdf)
        fig3.colorbar(m3, label='Fatality Risk [h$^{-1}$]')
        ax3.set_title(f'Fatality Risk Map at t={self.hour}')
        ax3.set_xticks([0, self.raster_shape[1] - 1])
        ax3.set_yticks([0, self.raster_shape[0] - 1])
        ax3.set_xticklabels(
            [self.test_bound_coords[0], self.test_bound_coords[2]], )
        ax3.set_yticklabels(
            [self.test_bound_coords[3], self.test_bound_coords[1]], )
        fig3.tight_layout()
        fig3.savefig(f'tests/layers/figs/risk_fatality_t{self.hour}.png',
                     bbox_inches='tight')
        fig3.show()

        import rasterio
        from rasterio import transform
        trans = transform.from_bounds(*self.test_bound_coords,
                                      *self.raster_shape)
        rds = rasterio.open(
            f'tests/layers/tiffs/fatality_risk_h{self.hour}.tif',
            'w',
            driver='GTiff',
            count=1,
            dtype=rasterio.float64,
            crs='EPSG:4326',
            transform=trans,
            compress='lzw',
            width=self.raster_shape[0],
            height=self.raster_shape[1])
        rds.write(fatality_pdf, 1)
        rds.close()
    def test_ballistic_dist(self):
        """
        Profile ballistic model impact distance distributions in the North East Down frame with wind compensation
        """
        make_plot = True  # Set flag to plot result
        samples = 3000

        loc_x, loc_y = 0, 0

        # Conjure up our distributions for various things
        alt_mean = 50
        alt_std = 5

        vx_mean = 18
        vx_std = 2.5

        # In degrees!
        track_mean = 60
        track_std = 2

        # In degrees!
        wind_dir_mean = 120
        wind_dir_std = 5
        wind_vel_mean = 10
        wind_vel_std = 2

        alt = ss.norm(alt_mean, alt_std).rvs(samples)
        vel = ss.norm(vx_mean, vx_std).rvs(samples)
        track = np.deg2rad(ss.norm(track_mean, track_std).rvs(samples))
        wind_vel = ss.norm(wind_vel_mean, wind_vel_std).rvs(samples)
        wind_dir = bearing_to_angle(
            np.deg2rad(ss.norm(wind_dir_mean, wind_dir_std).rvs(samples)))

        wind_vel_x = wind_vel * np.cos(wind_dir)
        wind_vel_y = wind_vel * np.sin(wind_dir)

        bm = BallisticModel(self.ac)
        (means, cov), v_i, a_i = bm.transform(alt, vel, track, wind_vel_y,
                                              wind_vel_x, loc_x, loc_y)
        dist = ss.multivariate_normal(mean=means, cov=cov)

        if make_plot:
            # Make a sampling grid for plotting
            x, y = np.mgrid[(loc_x - 5):(loc_x + 95):0.5,
                            (loc_y - 35):(loc_y + 40):0.5]
            pos = np.vstack([x.ravel(), y.ravel()])
            # Sample KDE PDF on these points
            density = dist.pdf(pos.T)
            # Plot sampled KDE PDF
            import matplotlib.pyplot as mpl
            fig, ax = mpl.subplots(1, 1, figsize=(8, 6))
            sc = ax.scatter(x, y, c=density)
            cbar = fig.colorbar(sc)
            cbar.set_label('Probability')
            ax.set_xlabel('Distance [m]')
            ax.set_ylabel('Distance [m]')
            ax.set_title(
                f'Ballistic Ground Impact Probability Density \n'
                f' Altitude $\sim \mathcal{{N}}({alt_mean},{alt_std}^2)$m,'
                f' Groundspeed $\sim \mathcal{{N}}({vx_mean},{vx_std}^2)$m/s,'
                f' Track $\sim \mathcal{{N}}({track_mean},{track_std}^2)$deg,\n'
                f' Wind speed $\sim \mathcal{{N}}({wind_vel_mean},{wind_vel_std}^2)$m/s,'
                f' Wind bearing $\sim \mathcal{{N}}({wind_dir_mean},{wind_dir_std}^2)$deg'
            )
            ax.arrow(
                loc_x,
                loc_y,
                vx_mean * np.cos(bearing_to_angle(np.deg2rad(track_mean))),
                vx_mean * np.sin(bearing_to_angle(np.deg2rad(track_mean))),
                label='UAS Track',
                width=1,
                color='blue')
            ax.arrow(loc_x,
                     loc_y,
                     wind_vel_mean *
                     np.cos(bearing_to_angle(np.deg2rad(wind_dir_mean))),
                     wind_vel_mean *
                     np.sin(bearing_to_angle(np.deg2rad(wind_dir_mean))),
                     label='Wind Direction',
                     width=1,
                     color='red')
            fig.show()
    def annotate(self,
                 data: List[gpd.GeoDataFrame],
                 raster_data: Tuple[Dict[str, np.array], np.array],
                 resolution=20,
                 **kwargs) -> Overlay:
        import geoviews as gv
        import scipy.stats as ss
        import joblib as jl

        bounds = (raster_data[0]['Longitude'].min(),
                  raster_data[0]['Latitude'].min(),
                  raster_data[0]['Longitude'].max(),
                  raster_data[0]['Latitude'].max())

        line_coords = list(self.dataframe.iloc[0].geometry.coords)
        # Snap the line string nodes to the raster grid
        snapped_points = [
            snap_coords_to_grid(raster_data[0], *coords)
            for coords in line_coords
        ]
        # Generate pairs of consecutive (x,y) coords
        path_pairs = list(map(list, zip(snapped_points, snapped_points[1:])))
        headings = []
        for i in range(1, len(line_coords)):
            prev = line_coords[i - 1]
            next = line_coords[i]
            x = np.sin(next[0] - prev[0]) * np.cos(next[1])
            y = np.cos(prev[1]) * np.sin(next[1]) - np.sin(prev[1]) * np.cos(
                next[1]) * np.cos(next[0] - prev[0])
            angle = (np.arctan2(x, y) + (2 * np.pi)) % (2 * np.pi)
            headings.append(angle)
        # Feed these pairs into the Bresenham algo to find the intermediate points
        path_grid_points = [
            bresenham.make_line(*pair[0], *pair[1]) for pair in path_pairs
        ]
        for idx, segment in enumerate(path_grid_points):
            n = len(segment)
            point_headings = np.full(n, headings[idx])
            path_grid_points[idx] = np.column_stack(
                (np.array(segment), point_headings))
        # Bring all these points together and remove duplicate coords
        # Flip left to right as bresenham spits out in (y,x) order
        path_grid_points = np.unique(np.concatenate(path_grid_points, axis=0),
                                     axis=0)

        bm = BallisticModel(self.aircraft)

        samples = 1000
        # Conjure up our distributions for various things
        alt = ss.norm(self.alt, 5).rvs(samples)
        vel = ss.norm(self.vel, 2.5).rvs(samples)
        wind_vels = ss.norm(self.wind_vel, 1).rvs(samples)
        wind_dirs = bearing_to_angle(
            ss.norm(self.wind_dir, np.deg2rad(5)).rvs(samples))
        wind_vel_y = wind_vels * np.sin(wind_dirs)
        wind_vel_x = wind_vels * np.cos(wind_dirs)

        # Create grid on which to evaluate each point of path with its pdf
        raster_shape = raster_data[1].shape
        x, y = np.mgrid[0:raster_shape[0], 0:raster_shape[1]]
        eval_grid = np.vstack((x.ravel(), y.ravel())).T

        def wrap_hdg_dists(alt, vel, hdg, wind_vel_y, wind_vel_x):
            (mean, cov), v_i, a_i = bm.transform(
                alt, vel,
                ss.norm(hdg, np.deg2rad(2)).rvs(samples), wind_vel_y,
                wind_vel_x, 0, 0)
            return hdg, (mean / resolution, cov / resolution, v_i, a_i)

        njobs = 1 if len(headings) < 3 else -1

        # Hardcode backend to prevent Qt freaking out
        res = jl.Parallel(n_jobs=njobs, backend='threading', verbose=1)(
            jl.delayed(wrap_hdg_dists)(alt, vel, hdg, wind_vel_y, wind_vel_x)
            for hdg in headings)
        dists_for_hdg = dict(res)

        def point_distr(c):
            dist_params = dists_for_hdg[c[2]]
            pdf = np.array(ss.multivariate_normal(
                dist_params[0] + np.array([c[0], c[1]]),
                dist_params[1]).pdf(eval_grid),
                           dtype=np.longdouble)
            return pdf

        sm = StrikeModel(raster_data[1].ravel(), resolution * resolution,
                         self.aircraft.width)
        fm = FatalityModel(0.5, 1e6, 34)
        ac_mass = self.aircraft.mass

        def wrap_pipeline(path_point_state):
            impact_pdf = point_distr(path_point_state)
            impact_vel = dists_for_hdg[path_point_state[2]][2]
            impact_angle = dists_for_hdg[path_point_state[2]][3]
            impact_ke = velocity_to_kinetic_energy(ac_mass, impact_vel)

            strike_pdf = sm.transform(impact_pdf, impact_angle=impact_angle)
            fatality_pdf = fm.transform(strike_pdf, impact_ke=impact_ke)

            return fatality_pdf, fatality_pdf.max(), strike_pdf.max()

        res = jl.Parallel(n_jobs=-1, backend='threading',
                          verbose=1)(jl.delayed(wrap_pipeline)(c)
                                     for c in path_grid_points)
        fatality_pdfs = [r[0] for r in res]
        # PDFs come out in input order so sorting not required
        pathwise_fatality_maxs = np.array([r[1] for r in res],
                                          dtype=np.longdouble)
        pathwise_strike_maxs = np.array([r[2] for r in res],
                                        dtype=np.longdouble)

        import matplotlib.pyplot as mpl
        import tempfile
        import subprocess
        fig, ax = mpl.subplots(1, 1)
        path_dist = self.dataframe.to_crs('EPSG:27700').iloc[0].geometry.length
        ax.set_yscale('log')
        ax.set_ylim(bottom=1e-18)
        x = np.linspace(0, path_dist, len(pathwise_fatality_maxs))
        ax.axhline(
            y=np.median(pathwise_fatality_maxs),
            c='y',
            label='Fatality Median')  # This seems to be as stable as fsum
        ax.plot(x, pathwise_fatality_maxs[::-1], c='r', label='Fatality Risk')
        ax.axhline(y=np.median(pathwise_strike_maxs),
                   c='g',
                   label='Strike Median')  # This seems to be as stable as fsum
        ax.plot(x, pathwise_strike_maxs[::-1], c='b', label='Strike Risk')
        ax.legend()
        ax.set_ylabel('Risk [$h^{-1}$]')
        ax.set_xlabel('Path Distance [m]')
        ax.set_title('Casualty Risk along path')

        tmppath = tempfile.mkstemp()[1] + '.png'
        fig.savefig(tmppath)
        subprocess.run("explorer " + tmppath)

        risk_map = np.sum(fatality_pdfs,
                          axis=0).reshape(raster_shape) * self.event_prob

        risk_raster = gv.Image(risk_map,
                               vdims=['fatality_risk'],
                               bounds=bounds).options(
                                   alpha=0.7,
                                   cmap='viridis',
                                   tools=['hover'],
                                   clipping_colors={'min': (0, 0, 0, 0)})
        risk_raster = risk_raster.redim.range(fatality_risk=(risk_map.min() +
                                                             1e-15,
                                                             risk_map.max()))
        print('Max probability of fatality: ', risk_map.max())

        return Overlay([
            gv.Contours(self.dataframe).opts(line_width=4,
                                             line_color='magenta'), risk_raster
        ])
Beispiel #6
0
def _make_strike_grid(aircraft, airspeed, altitude, failure_prob, pop_grid,
                      resolution, wind_direction, wind_speed):
    bm = BallisticModel(aircraft)
    gm = GlideDescentModel(aircraft)
    raster_shape = pop_grid.shape
    x, y = np.mgrid[0:raster_shape[0], 0:raster_shape[1]]
    eval_grid = np.vstack((x.ravel(), y.ravel())).T
    samples = 5000
    # Conjure up our distributions for various things
    alt = ss.norm(altitude, 5).rvs(samples)
    vel = ss.norm(airspeed, 2.5).rvs(samples)
    wind_vels = ss.norm(wind_speed, 1).rvs(samples)
    wind_dirs = bearing_to_angle(
        ss.norm(wind_direction, np.deg2rad(5)).rvs(samples))
    wind_vel_y = wind_vels * np.sin(wind_dirs)
    wind_vel_x = wind_vels * np.cos(wind_dirs)
    (bm_mean,
     bm_cov), v_ib, a_ib = bm.transform(alt, vel,
                                        ss.uniform(0, 360).rvs(samples),
                                        wind_vel_y, wind_vel_x, 0, 0)
    (gm_mean,
     gm_cov), v_ig, a_ig = gm.transform(alt, vel,
                                        ss.uniform(0, 360).rvs(samples),
                                        wind_vel_y, wind_vel_x, 0, 0)
    sm_b = StrikeModel(pop_grid, resolution**2, aircraft.width, a_ib)
    sm_g = StrikeModel(pop_grid, resolution**2, aircraft.width, a_ig)
    premult = sm_b.premult_mat + sm_g.premult_mat
    offset_y, offset_x = raster_shape[0] // 2, raster_shape[1] // 2
    bm_pdf = ss.multivariate_normal(bm_mean + np.array([offset_y, offset_x]),
                                    bm_cov).pdf(eval_grid)
    gm_pdf = ss.multivariate_normal(gm_mean + np.array([offset_y, offset_x]),
                                    gm_cov).pdf(eval_grid)
    pdf = bm_pdf + gm_pdf
    pdf = pdf.reshape(raster_shape)
    padded_pdf = np.zeros(
        ((raster_shape[0] * 3) + 1, (raster_shape[1] * 3) + 1))
    padded_pdf[raster_shape[0]:raster_shape[0] * 2,
               raster_shape[1]:raster_shape[1] * 2] = pdf
    padded_pdf = padded_pdf * failure_prob
    padded_centre_y, padded_centre_x = raster_shape[
        0] + offset_y, raster_shape[1] + offset_x

    # Check if CUDA toolkit available through env var otherwise fallback to CPU bound numba version
    if not os.getenv('CUDA_HOME'):
        print('CUDA NOT found, falling back to Numba JITed CPU code')
        # Leaving parallelisation to Numba seems to be faster
        res = wrap_all_pipeline(raster_shape, padded_pdf, padded_centre_y,
                                padded_centre_x, premult)

    else:

        res = np.zeros(raster_shape, dtype=float)
        threads_per_block = (32, 32)  # 1024 max per block
        blocks_per_grid = (int(np.ceil(raster_shape[1] /
                                       threads_per_block[1])),
                           int(np.ceil(raster_shape[0] /
                                       threads_per_block[0])))
        print('CUDA found, using config <<<' + str(blocks_per_grid) + ',' +
              str(threads_per_block) + '>>>')
        wrap_pipeline_cuda[blocks_per_grid,
                           threads_per_block](raster_shape, padded_pdf,
                                              padded_centre_y, padded_centre_x,
                                              premult, res)
    return res, (v_ib, v_ig)
Beispiel #7
0
class StrikeRiskLayer(BlockableDataLayer):
    def __init__(self, key, colour: str = None, blocking=False, buffer_dist=0,
                 ac: dict = AIRCRAFT_LIST['Default'],
                 wind_vel: float = 0, wind_dir: float = 0):
        super().__init__(key, colour, blocking, buffer_dist)
        delattr(self, '_colour')

        self._layers = [
            TemporalPopulationEstimateLayer(f'_strike_risk_tpe_{key}', buffer_dist=buffer_dist),
            RoadsLayer(f'_strike_risk_roads_{key}', buffer_dist=buffer_dist)]

        self.aircraft = casex.AircraftSpecs(casex.enums.AircraftType.FIXED_WING, ac['width'], ac['length'], ac['mass'])
        self.aircraft.set_ballistic_drag_coefficient(ac['bal_drag_coeff'])
        self.aircraft.set_ballistic_frontal_area(ac['frontal_area'])
        self.aircraft.set_glide_speed_ratio(ac['glide_speed'], ac['glide_ratio'])
        self.aircraft.set_glide_drag_coefficient(ac['glide_drag_coeff'])

        self.alt = ac['cruise_alt']
        self.vel = ac['cruise_speed']
        self.wind_vel = wind_vel
        # !! This is the direction the wind is COMING FROM !! #
        self.wind_dir = np.deg2rad(
            (((wind_dir - 180) % 360) - 90) % 360
        )

        self.event_prob = ac['failure_prob']

        self.bm = BallisticModel(self.aircraft)
        self.gm = GlideDescentModel(self.aircraft)

    def preload_data(self):
        [layer.preload_data() for layer in self._layers]

    def generate(self, bounds_polygon, raster_shape, resolution=30, hour: int = 8, **kwargs):
        risk_map, _ = self.make_strike_map(bounds_polygon, hour, raster_shape, resolution)

        bounds = bounds_polygon.bounds
        flipped_bounds = (bounds[1], bounds[0], bounds[3], bounds[2])
        risk_raster = gv.Image(risk_map, vdims=['strike_risk'], bounds=flipped_bounds).options(
            alpha=0.7,
            colorbar=True, colorbar_opts={'title': 'Person Strike Risk [h^-1]'},
            cmap='viridis',
            tools=['hover'],
            clipping_colors={
                'min': (0, 0, 0, 0)})
        import rasterio
        from rasterio import transform
        trans = transform.from_bounds(*flipped_bounds, *raster_shape)
        p = os.path.expanduser(f'~/GroundRiskMaps')
        if not os.path.exists(p):
            os.mkdir(p)
        rds = rasterio.open(p + f'/strike_risk_{hour}h_ac{hash(self.aircraft)}.tif',
                            'w', driver='GTiff', count=1, dtype=rasterio.float64, crs='EPSG:4326', transform=trans,
                            compress='lzw', width=raster_shape[0], height=raster_shape[1])
        rds.write(risk_map, 1)
        rds.close()

        return risk_raster, risk_map, None

    def make_strike_map(self, bounds_polygon, hour, raster_shape, resolution):
        generated_layers = [
            layer.generate(bounds_polygon, raster_shape, hour=hour, resolution=resolution) for layer in self._layers]
        raster_grid = np.flipud(np.sum(
            [remove_raster_nans(res[1]) for res in generated_layers if
             res[1] is not None],
            axis=0))
        raster_shape = raster_grid.shape
        x, y = np.mgrid[0:raster_shape[0], 0:raster_shape[1]]
        eval_grid = np.vstack((x.ravel(), y.ravel())).T
        samples = 5000
        # Conjure up our distributions for various things
        alt = ss.norm(self.alt, 5).rvs(samples)
        vel = ss.norm(self.vel, 2.5).rvs(samples)
        wind_vels = ss.norm(self.wind_vel, 1).rvs(samples)
        wind_dirs = bearing_to_angle(ss.norm(self.wind_dir, np.deg2rad(5)).rvs(samples))
        wind_vel_y = wind_vels * np.sin(wind_dirs)
        wind_vel_x = wind_vels * np.cos(wind_dirs)
        (bm_mean, bm_cov), v_ib, a_ib = self.bm.transform(alt, vel,
                                                          ss.uniform(0, 360).rvs(samples),
                                                          wind_vel_y, wind_vel_x,
                                                          0, 0)
        (gm_mean, gm_cov), v_ig, a_ig = self.gm.transform(alt, vel,
                                                          ss.uniform(0, 360).rvs(samples),
                                                          wind_vel_y, wind_vel_x,
                                                          0, 0)
        sm_b = StrikeModel(raster_grid, resolution ** 2, self.aircraft.width, a_ib)
        sm_g = StrikeModel(raster_grid, resolution ** 2, self.aircraft.width, a_ig)
        premult = sm_b.premult_mat + sm_g.premult_mat
        offset_y, offset_x = raster_shape[0] // 2, raster_shape[1] // 2
        bm_pdf = ss.multivariate_normal(bm_mean + np.array([offset_y, offset_x]), bm_cov).pdf(eval_grid)
        gm_pdf = ss.multivariate_normal(gm_mean + np.array([offset_y, offset_x]), gm_cov).pdf(eval_grid)
        pdf = bm_pdf + gm_pdf
        pdf = pdf.reshape(raster_shape)
        padded_pdf = np.zeros(((raster_shape[0] * 3) + 1, (raster_shape[1] * 3) + 1))
        padded_pdf[raster_shape[0]:raster_shape[0] * 2, raster_shape[1]:raster_shape[1] * 2] = pdf
        padded_pdf = padded_pdf * self.event_prob
        padded_centre_y, padded_centre_x = raster_shape[0] + offset_y, raster_shape[1] + offset_x
        # Check if CUDA toolkit available through env var otherwise fallback to CPU bound numba version
        if not os.getenv('CUDA_HOME'):
            print('CUDA NOT found, falling back to Numba JITed CPU code')
            # Leaving parallelisation to Numba seems to be faster
            risk_map = wrap_all_pipeline(raster_shape, padded_pdf, padded_centre_y, padded_centre_x, premult)

        else:

            risk_map = np.zeros(raster_shape, dtype=float)
            threads_per_block = (32, 32)  # 1024 max per block
            blocks_per_grid = (
                int(np.ceil(raster_shape[1] / threads_per_block[1])),
                int(np.ceil(raster_shape[0] / threads_per_block[0]))
            )
            print('CUDA found, using config <<<' + str(blocks_per_grid) + ',' + str(threads_per_block) + '>>>')
            wrap_pipeline_cuda[blocks_per_grid, threads_per_block](raster_shape, padded_pdf, padded_centre_y,
                                                                   padded_centre_x, premult, risk_map)
        ac_mass = self.aircraft.mass
        impact_kes = (velocity_to_kinetic_energy(ac_mass, v_ib), velocity_to_kinetic_energy(ac_mass, v_ig))

        return risk_map, impact_kes

    def clear_cache(self):
        pass