Exemple #1
0
#!/usr/bin/env python

import numpy as np
import pytest

from math import ceil

from larndsim import consts

consts.load_detector_properties("larndsim/detector_properties/singlecube.yaml",
                                "larndsim/pixel_layouts/layout-singlecube.yaml")

from larndsim import drifting

class TestDrifting:
    """
    Drifting module testing
    """
    tracks = np.zeros((1, 22))
    tracks = np.core.records.fromarrays(tracks.transpose(), 
                                        names="eventID, z_end, trackID, tran_diff, z_start, x_end, y_end, n_electrons, pdgId, x_start, y_start, t_start, dx, long_diff, pixel_plane, t_end, dEdx, dE, t, y, x, z",
                                        formats = "i8, f8, i8, f8, f8, f8, f8, i8, i8, f8, f8, f8, f8, f8, i8, f8, f8, f8, f8, f8, f8, f8")
    tracks["z"] = np.random.uniform(consts.module_borders[0][2][0], consts.module_borders[0][2][1], 1)
    tracks["x"] = np.random.uniform(consts.module_borders[0][0][0], consts.module_borders[0][0][1], 1)
    tracks["y"] = np.random.uniform(consts.module_borders[0][1][0], consts.module_borders[0][1][1], 1)
    tracks["n_electrons"] = np.random.uniform(1e6, 1e7, 1)

    def test_lifetime(self):
        """
        Testing the lifetime correction
        """
Exemple #2
0
def run_simulation(input_filename,
                   pixel_layout,
                   detector_properties,
                   output_filename='',
                   n_tracks=100000):
    """
    Command-line interface to run the simulation of a pixelated LArTPC

    Args:
        input_filename (str): path of the edep-sim input file
        output_filename (str): path of the HDF5 output file. If not specified
            the output is added to the input file.
        pixel_layout (str): path of the YAML file containing the pixel
            layout and connection details.
        detector_properties (str): path of the YAML file containing
            the detector properties
        n_tracks (int): number of tracks to be simulated
    """
    start_simulation = time()

    from cupy.cuda.nvtx import RangePush, RangePop

    RangePush("run_simulation")

    print(logo)
    print("**************************\nLOADING SETTINGS AND INPUT\n**************************")
    print("Pixel layout file:", pixel_layout)
    print("Detector propeties file:", detector_properties)
    print("edep-sim input file:", input_filename)
    RangePush("load_detector_properties")
    consts.load_detector_properties(detector_properties, pixel_layout)
    RangePop()

    RangePush("load_larndsim_modules")
    # Here we load the modules after loading the detector properties
    # maybe can be implemented in a better way?
    from larndsim import quenching, drifting, detsim, pixels_from_track, fee
    RangePop()

    RangePush("load_hd5_file")
    # First of all we load the edep-sim output
    # For this sample we need to invert $z$ and $y$ axes
    with h5py.File(input_filename, 'r') as f:
        tracks = np.array(f['segments'])
    RangePop()

    RangePush("slicing_and_swapping")
    tracks = tracks[:n_tracks]

    x_start = np.copy(tracks['x_start'] )
    x_end = np.copy(tracks['x_end'])
    x = np.copy(tracks['x'])

    tracks['x_start'] = np.copy(tracks['z_start'])
    tracks['x_end'] = np.copy(tracks['z_end'])
    tracks['x'] = np.copy(tracks['z'])

    tracks['z_start'] = x_start
    tracks['z_end'] = x_end
    tracks['z'] = x
    RangePop()

    TPB = 256
    BPG = ceil(tracks.shape[0] / TPB)

    print("*******************\nSTARTING SIMULATION\n*******************")
    # We calculate the number of electrons after recombination (quenching module)
    # and the position and number of electrons after drifting (drifting module)
    print("Quenching electrons...",end='')
    start_quenching = time()
    RangePush("quench")
    quenching.quench[BPG,TPB](tracks, consts.birks)
    RangePop()
    end_quenching = time()
    print(f" {end_quenching-start_quenching:.2f} s")

    print("Drifting electrons...",end='')
    start_drifting = time()
    RangePush("drift")
    drifting.drift[BPG,TPB](tracks)
    RangePop()
    end_drifting = time()
    print(f" {end_drifting-start_drifting:.2f} s")
    step = 1
    adc_tot_list = cp.empty((0,fee.MAX_ADC_VALUES))
    adc_tot_ticks_list = cp.empty((0,fee.MAX_ADC_VALUES))
    MAX_TRACKS_PER_PIXEL = 5
    backtracked_id_tot = cp.empty((0,fee.MAX_ADC_VALUES,MAX_TRACKS_PER_PIXEL))
    unique_pix_tot = cp.empty((0,2))
    tot_events = 0
    
    tot_evids = np.unique(tracks['eventID'])
    # We divide the sample in portions that can be processed by the GPU
    tracks_batch_runtimes = []
    for ievd in tqdm(range(0, tot_evids.shape[0], step), desc='Simulating pixels...'):
        start_tracks_batch = time()
        first_event = tot_evids[ievd]
        last_event = tot_evids[min(ievd+step, tot_evids.shape[0]-1)]

        if first_event == last_event:
            last_event += 1

        evt_tracks = tracks[(tracks['eventID']>=first_event) & (tracks['eventID']<last_event)]
        first_trk_id = np.where(tracks['eventID']==evt_tracks['eventID'][0])[0][0]
        
        for itrk in range(0, evt_tracks.shape[0], 600):
            selected_tracks = evt_tracks[itrk:itrk+600]

            RangePush("event_id_map")
            # Here we build a map between tracks and event IDs
            event_ids = selected_tracks['eventID']
            unique_eventIDs = np.unique(event_ids)
            event_id_map = np.searchsorted(unique_eventIDs, event_ids)
            RangePop()

            # We find the pixels intersected by the projection of the tracks on
            # the anode plane using the Bresenham's algorithm. We also take into
            # account the neighboring pixels, due to the transverse diffusion of the charges.
            RangePush("pixels_from_track")
            longest_pix = ceil(max(selected_tracks["dx"])/consts.pixel_pitch)
            max_radius = ceil(max(selected_tracks["tran_diff"])*5/consts.pixel_pitch)
            MAX_PIXELS = int((longest_pix*4+6)*max_radius*1.5)
            MAX_ACTIVE_PIXELS = int(longest_pix*1.5)
            active_pixels = cp.full((selected_tracks.shape[0], MAX_ACTIVE_PIXELS, 2), -1, dtype=np.int32)
            neighboring_pixels = cp.full((selected_tracks.shape[0], MAX_PIXELS, 2), -1, dtype=np.int32)
            n_pixels_list = cp.zeros(shape=(selected_tracks.shape[0]))
            threadsperblock = 128
            blockspergrid = ceil(selected_tracks.shape[0] / threadsperblock)

            if not active_pixels.shape[1]:
                continue
                
            pixels_from_track.get_pixels[blockspergrid,threadsperblock](selected_tracks,
                                                                        active_pixels,
                                                                        neighboring_pixels,
                                                                        n_pixels_list,
                                                                        max_radius+1)
            RangePop()

            RangePush("unique_pix")
            shapes = neighboring_pixels.shape
            joined = neighboring_pixels.reshape(shapes[0]*shapes[1],2)
            unique_pix = cupy_unique_axis0(joined)
            unique_pix = unique_pix[(unique_pix[:,0] != -1) & (unique_pix[:,1] != -1),:]
            RangePop()
            
            if not unique_pix.shape[0]:
                continue

            RangePush("time_intervals")
            # Here we find the longest signal in time and we store an array with the start in time of each track
            max_length = cp.array([0])
            track_starts = cp.empty(selected_tracks.shape[0])
            # d_track_starts = cuda.to_device(track_starts)
            threadsperblock = 128
            blockspergrid = ceil(selected_tracks.shape[0] / threadsperblock)
            detsim.time_intervals[blockspergrid,threadsperblock](track_starts, max_length,  event_id_map, selected_tracks)
            RangePop()

            RangePush("tracks_current")
            # Here we calculate the induced current on each pixel
            signals = cp.zeros((selected_tracks.shape[0],
                                neighboring_pixels.shape[1],
                                cp.asnumpy(max_length)[0]), dtype=np.float32)
            threadsperblock = (1,1,64)
            blockspergrid_x = ceil(signals.shape[0] / threadsperblock[0])
            blockspergrid_y = ceil(signals.shape[1] / threadsperblock[1])
            blockspergrid_z = ceil(signals.shape[2] / threadsperblock[2])
            blockspergrid = (blockspergrid_x, blockspergrid_y, blockspergrid_z)
            detsim.tracks_current[blockspergrid,threadsperblock](signals,
                                                                 neighboring_pixels,
                                                                 selected_tracks)
            RangePop()

            RangePush("pixel_index_map")
            # Here we create a map between tracks and index in the unique pixel array
            pixel_index_map = cp.full((selected_tracks.shape[0], neighboring_pixels.shape[1]), -1)
            compare = neighboring_pixels[..., np.newaxis, :] == unique_pix
            indices = cp.where(cp.logical_and(compare[..., 0], compare[..., 1]))
            pixel_index_map[indices[0], indices[1]] = indices[2]
            RangePop()

            RangePush("sum_pixels_signals")
            # Here we combine the induced current on the same pixels by different tracks
            threadsperblock = (8,8,8)
            blockspergrid_x = ceil(signals.shape[0] / threadsperblock[0])
            blockspergrid_y = ceil(signals.shape[1] / threadsperblock[1])
            blockspergrid_z = ceil(signals.shape[2] / threadsperblock[2])
            blockspergrid = (blockspergrid_x, blockspergrid_y, blockspergrid_z)
            pixels_signals = cp.zeros((len(unique_pix), len(consts.time_ticks)*3))
            detsim.sum_pixel_signals[blockspergrid,threadsperblock](pixels_signals,
                                                                    signals,
                                                                    track_starts,
                                                                    pixel_index_map)
            RangePop()

            RangePush("get_adc_values")
            # Here we simulate the electronics response (the self-triggering cycle) and the signal digitization
            time_ticks = cp.linspace(0, len(unique_eventIDs)*consts.time_interval[1]*3, pixels_signals.shape[1]+1)
            integral_list = cp.zeros((pixels_signals.shape[0], fee.MAX_ADC_VALUES))
            adc_ticks_list = cp.zeros((pixels_signals.shape[0], fee.MAX_ADC_VALUES))
            TPB = 128
            BPG = ceil(pixels_signals.shape[0] / TPB)

            rng_states = create_xoroshiro128p_states(TPB * BPG, seed=ievd)
            
            fee.get_adc_values[BPG,TPB](pixels_signals,
                                        time_ticks,
                                        integral_list,
                                        adc_ticks_list,
                                        consts.time_interval[1]*3*tot_events,
                                        rng_states)
            adc_list = fee.digitize(integral_list)
            RangePop()

            RangePush("track_pixel_map")
            # Mapping between unique pixel array and track array index
            track_pixel_map = cp.full((unique_pix.shape[0], MAX_TRACKS_PER_PIXEL), -1)
            TPB = 32
            BPG = ceil(unique_pix.shape[0] / TPB)
            detsim.get_track_pixel_map[BPG, TPB](track_pixel_map, unique_pix, neighboring_pixels)
            RangePop()

            RangePush("backtracking")
            # Here we backtrack the ADC counts to the Geant4 tracks
            TPB = 128
            BPG = ceil(adc_list.shape[0] / TPB)
            backtracked_id = cp.full((adc_list.shape[0], adc_list.shape[1], MAX_TRACKS_PER_PIXEL), -1)
            detsim.backtrack_adcs[BPG,TPB](selected_tracks,
                                           adc_list,
                                           adc_ticks_list,
                                           track_pixel_map,
                                           event_id_map,
                                           unique_eventIDs,
                                           backtracked_id,
                                           first_trk_id+itrk)
            RangePop()
            adc_tot_list = cp.concatenate((adc_tot_list, adc_list), axis=0)
            adc_tot_ticks_list = cp.concatenate((adc_tot_ticks_list, adc_ticks_list), axis=0)
            unique_pix_tot = cp.concatenate((unique_pix_tot, unique_pix), axis=0)
            backtracked_id_tot = cp.concatenate((backtracked_id_tot, backtracked_id), axis=0)
        
        tot_events += step

        end_tracks_batch = time()
        tracks_batch_runtimes.append(end_tracks_batch - start_tracks_batch)

    print(f"- total time: {sum(tracks_batch_runtimes):.2f} s")
    if len(tracks_batch_runtimes) > 1:
        print(f"- excluding first iteration: {sum(tracks_batch_runtimes[1:]):.2f} s")

    RangePush("Exporting to HDF5")
    # Here we export the result in a HDF5 file.
    fee.export_to_hdf5(cp.asnumpy(adc_tot_list),
                       cp.asnumpy(adc_tot_ticks_list),
                       cp.asnumpy(unique_pix_tot),
                       cp.asnumpy(backtracked_id_tot),
                       output_filename)
    RangePop()

    with h5py.File(output_filename, 'a') as f:
        f.create_dataset("tracks", data=tracks)
    
    print("Output saved in:", output_filename)

    RangePop()
    end_simulation = time()
    print(f"run_simulation elapsed time: {end_simulation-start_simulation:.2f} s")
def run_simulation(input_filename,
                   pixel_layout,
                   detector_properties,
                   output_filename='',
                   n_tracks=100000):
    """
    Command-line interface to run the simulation of a pixelated LArTPC

    Args:
        input_filename (str): path of the edep-sim input file
        output_filename (str): path of the HDF5 output file. If not specified
            the output is added to the input file.
        pixel_layout (str): path of the YAML file containing the pixel
            layout and connection details.
        detector_properties (str): path of the YAML file containing
            the detector properties
        n_tracks (int): number of tracks to be simulated
    """
    print(logo)
    print(
        "**************************\nLOADING SETTINGS AND INPUT\n**************************"
    )
    print("Pixel layout file:", pixel_layout)
    print("Detector propeties file:", detector_properties)
    print("edep-sim input file:", input_filename)
    consts.load_detector_properties(detector_properties, pixel_layout)

    # Here we load the modules after loading the detector properties
    # maybe can be implemented in a better way?
    from larndsim import quenching, drifting, detsim, pixels_from_track, fee

    # First of all we load the edep-sim output
    # For this sample we need to invert $z$ and $y$ axes
    with h5py.File(input_filename, 'r') as f:
        tracks = np.array(f['segments'])

    tracks = tracks[:n_tracks]

    y_start = np.copy(tracks['y_start'])
    y_end = np.copy(tracks['y_end'])
    y = np.copy(tracks['y'])

    tracks['y_start'] = np.copy(tracks['z_start'])
    tracks['y_end'] = np.copy(tracks['z_end'])
    tracks['y'] = np.copy(tracks['z'])

    tracks['z_start'] = y_start
    tracks['z_end'] = y_end
    tracks['z'] = y

    TPB = 256
    BPG = ceil(tracks.shape[0] / TPB)

    print("*******************\nSTARTING SIMULATION\n*******************")
    # We calculate the number of electrons after recombination (quenching module)
    # and the position and number of electrons after drifting (drifting module)
    print("Quenching electrons...", end='')
    start_quenching = time()
    quenching.quench[BPG, TPB](tracks, consts.birks)
    end_quenching = time()
    print(f" {end_quenching-start_quenching:.2f} s")

    print("Drifting electrons...", end='')
    start_drifting = time()
    drifting.drift[BPG, TPB](tracks)
    end_drifting = time()
    print(f" {end_drifting-start_drifting:.2f} s")
    step = 200
    adc_tot_list = np.empty((1, fee.MAX_ADC_VALUES))
    adc_tot_ticks_list = np.empty((1, fee.MAX_ADC_VALUES))
    backtracked_id_tot = np.empty((1, fee.MAX_ADC_VALUES, 5))
    unique_pix_tot = np.empty((1, 2))
    tot_events = 0

    # We divide the sample in portions that can be processed by the GPU
    for itrk in tqdm(range(0, tracks.shape[0], step),
                     desc='Simulating pixels...'):
        selected_tracks = tracks[itrk:itrk + step]

        # Here we build a map between tracks and event IDs
        unique_eventIDs = np.unique(selected_tracks['eventID'])
        event_id_map = np.zeros_like(selected_tracks['eventID'])
        for iev, evID in enumerate(selected_tracks['eventID']):
            event_id_map[iev] = np.where(evID == unique_eventIDs)[0]
        d_event_id_map = cuda.to_device(event_id_map)

        # We find the pixels intersected by the projection of the tracks on
        # the anode plane using the Bresenham's algorithm. We also take into
        # account the neighboring pixels, due to the transverse diffusion of the charges.
        longest_pix = ceil(max(selected_tracks["dx"]) / consts.pixel_size[0])
        max_radius = ceil(
            max(selected_tracks["tran_diff"]) * 5 / consts.pixel_size[0])
        MAX_PIXELS = (longest_pix * 4 + 6) * max_radius * 2
        MAX_ACTIVE_PIXELS = longest_pix * 2
        active_pixels = np.full(
            (selected_tracks.shape[0], MAX_ACTIVE_PIXELS, 2),
            -1,
            dtype=np.int32)
        neighboring_pixels = np.full((selected_tracks.shape[0], MAX_PIXELS, 2),
                                     -1,
                                     dtype=np.int32)
        n_pixels_list = np.zeros(shape=(selected_tracks.shape[0]))
        threadsperblock = 128
        blockspergrid = ceil(selected_tracks.shape[0] / threadsperblock)
        pixels_from_track.get_pixels[blockspergrid,
                                     threadsperblock](selected_tracks,
                                                      active_pixels,
                                                      neighboring_pixels,
                                                      n_pixels_list,
                                                      max_radius + 1)
        shapes = neighboring_pixels.shape
        joined = neighboring_pixels.reshape(shapes[0] * shapes[1], 2)
        unique_pix = np.unique(joined, axis=0)
        unique_pix = unique_pix[(unique_pix[:, 0] != -1) &
                                (unique_pix[:, 1] != -1), :]

        # Here we find the longest signal in time and we store an array with the start in time of each track
        max_length = np.array([0])
        track_starts = np.empty(selected_tracks.shape[0])
        d_track_starts = cuda.to_device(track_starts)
        threadsperblock = 128
        blockspergrid = ceil(selected_tracks.shape[0] / threadsperblock)
        detsim.time_intervals[blockspergrid,
                              threadsperblock](d_track_starts, max_length,
                                               d_event_id_map, selected_tracks)

        # Here we calculate the induced current on each pixel
        signals = np.zeros((selected_tracks.shape[0],
                            neighboring_pixels.shape[1], max_length[0]),
                           dtype=np.float32)
        threadsperblock = (4, 4, 4)
        blockspergrid_x = ceil(signals.shape[0] / threadsperblock[0])
        blockspergrid_y = ceil(signals.shape[1] / threadsperblock[1])
        blockspergrid_z = ceil(signals.shape[2] / threadsperblock[2])
        blockspergrid = (blockspergrid_x, blockspergrid_y, blockspergrid_z)
        d_signals = cuda.to_device(signals)
        detsim.tracks_current[blockspergrid,
                              threadsperblock](d_signals, neighboring_pixels,
                                               selected_tracks)
        # Here we create a map between tracks and index in the unique pixel array
        pixel_index_map = np.full(
            (selected_tracks.shape[0], neighboring_pixels.shape[1]), -1)

        for itr in range(neighboring_pixels.shape[0]):
            for ipix in range(neighboring_pixels.shape[1]):
                pID = neighboring_pixels[itr][ipix]
                if pID[0] >= 0 and pID[1] >= 0:
                    try:
                        index = np.where((unique_pix[:, 0] == pID[0])
                                         & (unique_pix[:, 1] == pID[1]))
                    except IndexError:
                        print(index, "More pixels than maximum value")
                    pixel_index_map[itr, ipix] = index[0]

        d_pixel_index_map = cuda.to_device(pixel_index_map)
        # Here we combine the induced current on the same pixels by different tracks
        threadsperblock = (8, 8, 8)
        blockspergrid_x = ceil(d_signals.shape[0] / threadsperblock[0])
        blockspergrid_y = ceil(d_signals.shape[1] / threadsperblock[1])
        blockspergrid_z = ceil(d_signals.shape[2] / threadsperblock[2])
        blockspergrid = (blockspergrid_x, blockspergrid_y, blockspergrid_z)
        pixels_signals = np.zeros(
            (len(unique_pix),
             len(consts.time_ticks) * len(unique_eventIDs) * 2))
        d_pixels_signals = cuda.to_device(pixels_signals)
        detsim.sum_pixel_signals[blockspergrid,
                                 threadsperblock](d_pixels_signals, d_signals,
                                                  d_track_starts,
                                                  d_pixel_index_map)
        # Here we simulate the electronics response (the self-triggering cycle) and the signal digitization
        time_ticks = np.linspace(
            0,
            len(unique_eventIDs) * consts.time_interval[1] * 2,
            pixels_signals.shape[1] + 1)
        integral_list = np.zeros((pixels_signals.shape[0], fee.MAX_ADC_VALUES))
        adc_ticks_list = np.zeros(
            (pixels_signals.shape[0], fee.MAX_ADC_VALUES))
        TPB = 128
        BPG = ceil(pixels_signals.shape[0] / TPB)

        rng_states = create_xoroshiro128p_states(TPB * BPG, seed=itrk)
        fee.get_adc_values[BPG, TPB](d_pixels_signals, time_ticks,
                                     integral_list, adc_ticks_list,
                                     consts.time_interval[1] * 2 * tot_events,
                                     rng_states)

        adc_list = fee.digitize(integral_list)
        track_pixel_map = np.full((unique_pix.shape[0], 5), -1)
        backtracked_id = np.full(
            (adc_list.shape[0], adc_list.shape[1], track_pixel_map.shape[1]),
            -1)

        # Here we backtrack the ADC counts to the Geant4 tracks
        detsim.get_track_pixel_map(track_pixel_map, unique_pix,
                                   neighboring_pixels)
        detsim.backtrack_adcs(selected_tracks, adc_list, adc_ticks_list,
                              track_pixel_map, event_id_map, backtracked_id)
        adc_tot_list = np.append(adc_tot_list, adc_list, axis=0)
        adc_tot_ticks_list = np.append(adc_tot_ticks_list,
                                       adc_ticks_list,
                                       axis=0)
        unique_pix_tot = np.append(unique_pix_tot, unique_pix, axis=0)
        backtracked_id_tot = np.append(backtracked_id_tot,
                                       backtracked_id,
                                       axis=0)
        tot_events += len(unique_eventIDs)

    unique_pix_tot = unique_pix_tot[1:]
    adc_tot_list = adc_tot_list[1:]
    adc_tot_ticks_list = adc_tot_ticks_list[1:]
    backtracked_id_tot = backtracked_id_tot[1:]

    # Here we export the result in a HDF5 file.
    if output_filename:
        fee.export_to_hdf5(adc_tot_list, adc_tot_ticks_list, unique_pix_tot,
                           backtracked_id_tot, output_filename)
    else:
        fee.export_to_hdf5(adc_tot_list, adc_tot_ticks_list, unique_pix_tot,
                           backtracked_id_tot, input_filename)

    print("Output saved in:",
          output_filename if output_filename else input_filename)
Exemple #4
0
#!/usr/bin/env python

import numpy as np
import pytest

from larndsim import consts

consts.load_detector_properties("larndsim/detector_properties/module0.yaml",
                                "larndsim/pixel_layouts/multi_tile_layout-2.1.16.yaml")

from larndsim import detsim
from larndsim import drifting, quenching, pixels_from_track

from math import ceil

class TestTrackCurrent:
    tracks = np.zeros((10, 29))
    tracks["z_start"] = np.random.uniform(consts.tpc_borders[0][2][0], consts.tpc_borders[0][2][1], 10)
    tracks["z_end"] = np.random.uniform(consts.tpc_borders[0][2][0], consts.tpc_borders[0][2][0]+2, 10)
    tracks["z"] = (tracks["z_end"]+tracks["z_start"])/2.
    tracks["y_start"] = np.random.uniform(consts.tpc_borders[0][1][0], consts.tpc_borders[0][1][0]+2, 10)
    tracks["y_end"] = np.random.uniform(consts.tpc_borders[0][1][0], consts.tpc_borders[0][1][0]+2, 10)
    tracks["x_start"] = np.random.uniform(consts.tpc_borders[0][0][0], consts.tpc_borders[0][0][0]+2, 10)
    tracks["x_end"] = np.random.uniform(consts.tpc_borders[0][0][0], consts.tpc_borders[0][0][0]+2, 10)
    tracks["x"] = (tracks["x_end"]+tracks["x_start"])/2.
    tracks["y"] = (tracks["y_end"]+tracks["y_start"])/2.
    tracks["dx"] = np.sqrt((tracks["x_end"]-tracks["x_start"])**2+(tracks["y_end"]-tracks["y_start"])**2+(tracks["z_end"]-tracks["z_start"])**2)
    tracks["dEdx"] = [2]*10
    tracks["dE"] = tracks["dEdx"]*tracks["dx"]
    tracks["tran_diff"] = [1e-1]*10
    tracks["long_diff"] = [1e-1]*10