Пример #1
0

def diff_and_concatenate(true1, true2, reco1, reco2):
    d1 = true1 - reco1
    d2 = true2 - reco2
    diff_matrix = np.concatenate((d1, d2))
    return diff_matrix


diff_r_matrix = diff_and_concatenate(true_r1, true_r2, reco_r1, reco_r2)
diff_phi_matrix = diff_and_concatenate(true_phi1, true_phi2, reco_phi1,
                                       reco_phi2)
diff_z_matrix = diff_and_concatenate(true_z1, true_z2, reco_z1, reco_z2)

### read sensor positions from database
DataSiPM = db.DataSiPMsim_only('petalo', 0)
DataSiPM_idx = DataSiPM.set_index('SensorID')

speed_in_vacuum = 0.299792458  # * units.mm / units.ps
ave_speed_in_LXe = 0.210  #* units.mm / units.ps

### Positions
pos_1 = np.array([reco_x1, reco_y1, reco_z1]).transpose()
pos_2 = np.array([reco_x2, reco_y2, reco_z2]).transpose()

sipm_pos_1 = np.array([
    DataSiPM_idx.loc[first_sipm1].X, DataSiPM_idx.loc[first_sipm1].Y,
    DataSiPM_idx.loc[first_sipm1].Z
]).transpose()
sipm_pos_2 = np.array([
    DataSiPM_idx.loc[first_sipm2].X, DataSiPM_idx.loc[first_sipm2].Y,
Пример #2
0
import sys
import numpy as np
import pandas as pd

import antea.database.load_db as db
import antea.reco.reco_functions as rf
import antea.reco.mctrue_functions as mcf

### read sensor positions from database
#DataSiPM     = db.DataSiPM('petalo', 0) # ring
DataSiPM = db.DataSiPMsim_only('petalo', 0)  # full body PET
DataSiPM_idx = DataSiPM.set_index('SensorID')

start = int(sys.argv[1])
numb = int(sys.argv[2])
threshold = int(sys.argv[3])

folder = 'in_folder_name'
file_full = folder + 'full_body_195cm.{0:03d}.pet.h5'
evt_file = 'out_folder_name/full_body_195cm_r_map.{0}_{1}_{2}'.format(
    start, numb, threshold)

true_r1, true_r2 = [], []
var_phi1, var_phi2 = [], []
var_z1, var_z2 = [], []

touched_sipms1, touched_sipms2 = [], []

for ifile in range(start, start + numb):

    file_name = file_full.format(ifile)
Пример #3
0
def characterize_coincidences(input_file: str, output_file: str, rmap: str):
    """
    This function extracts the relevant information on position, time,
    sensor response, kind (point-like or not)) of the
    two gamma interactions of a coincidences.
    """

    DataSiPM = db.DataSiPMsim_only('petalo', 0)  # full body PET
    DataSiPM_idx = DataSiPM.set_index('SensorID')

    ### parameters for single photoelectron convolution in SiPM response
    tau_sipm = [100, 15000]
    time_window = 5000
    time = np.arange(0, time_window)
    spe_resp, norm = shf.normalize_sipm_shaping(time, tau_sipm)

    thr_r = 4  # threshold use to create R map (pe)
    thr_phi = 4  # threshold on charge to reconstruct phi (pe)
    thr_z = 4  # threshold on charge to reconstruct z (pe)
    thr_e = 2  # threshold on charge (pe)

    n_pe = 1  # number of first photoelectrons to be considered for timestamp
    sigma_sipm = 40  #ps SiPM jitter
    sigma_elec = 30  #ps electronic jitter

    Rpos = load_map(rmap,
                    group="Radius",
                    node="f4pes150bins",
                    x_name="PhiRms",
                    y_name="Rpos",
                    u_name="RposUncertainty")

    c0 = c1 = 0

    true_r1, true_phi1, true_z1 = [], [], []
    reco_r1, reco_phi1, reco_z1 = [], [], []
    true_r2, true_phi2, true_z2 = [], [], []
    reco_r2, reco_phi2, reco_z2 = [], [], []

    sns_response1, sns_response2 = [], []

    ### PETsys thresholds to extract the timestamp
    timestamp_thr = 0.25
    first_sipm1 = []
    first_sipm2 = []
    first_time1 = []
    first_time2 = []
    true_time1, true_time2 = [], []
    touched_sipms1, touched_sipms2 = [], []
    photo1, photo2 = [], []
    max_hit_distance1, max_hit_distance2 = [], []

    event_ids = []

    try:
        sns_response = load_mcsns_response(input_file)
    except ValueError:
        print(f'File {input_file} not found')
        exit()
    except OSError:
        print(f'File {input_file} not found')
        exit()
    except KeyError:
        print(f'No object named MC/sns_response in file {input_file}')
        exit()
    print(f'Analyzing file {input_file}')

    fluct_sns_response = snsf.apply_charge_fluctuation(sns_response,
                                                       DataSiPM_idx)

    tof_bin_size = read_sensor_bin_width_from_conf(input_file, tof=True)

    particles = load_mcparticles(input_file)
    hits = load_mchits(input_file)
    tof_response = load_mcTOFsns_response(input_file)

    events = particles.event_id.unique()
    charge_range = (
        2000, 2250
    )  # range to select photopeak - to be adjusted to the specific case
    print(f'Number of events in file = {len(events)}')

    for evt in events:

        evt_sns = fluct_sns_response[fluct_sns_response.event_id == evt]
        evt_sns = rf.find_SiPMs_over_threshold(evt_sns, threshold=thr_e)
        if len(evt_sns) == 0:
            continue

        ids_over_thr = evt_sns.sensor_id.astype('int64').values

        evt_parts = particles[particles.event_id == evt]
        evt_hits = hits[hits.event_id == evt]
        evt_tof = tof_response[tof_response.event_id == evt]
        evt_tof = evt_tof[evt_tof.sensor_id.isin(-ids_over_thr)]
        if len(evt_tof) == 0:
            continue

        ### If one wants to select only pure photoelectric events
        ### add the following lines
        #select, true_pos = mcf.select_photoelectric(evt_parts, evt_hits)
        #if not select: continue
        #if (len(true_pos) == 1) & (evt_hits.energy.sum() > 0.511):
        #    continue

        pos1, pos2, q1, q2, true_pos1, true_pos2, true_t1, true_t2, sns1, sns2 = rf.reconstruct_coincidences(
            evt_sns, charge_range, DataSiPM_idx, evt_parts, evt_hits)
        if len(pos1) == 0 or len(pos2) == 0:
            c0 += 1
            continue

        q1 = np.array(q1)
        q2 = np.array(q2)
        pos1 = np.array(pos1)
        pos2 = np.array(pos2)

        r1, phi1, z1 = rf.reconstruct_position(q1, pos1, Rpos, thr_r, thr_phi,
                                               thr_z)
        r2, phi2, z2 = rf.reconstruct_position(q2, pos2, Rpos, thr_r, thr_phi,
                                               thr_z)

        if (r1 > 1.e8) or (r2 > 1.e8):
            c1 += 1
            continue

        ## Use absolute times in units of ps
        times = evt_tof.time_bin.values * tof_bin_size / units.ps
        ## add SiPM jitter, if different from zero
        if sigma_sipm > 0:
            times = np.round(np.random.normal(times, sigma_sipm))
        evt_tof.insert(len(evt_tof.columns), 'time',
                       times.astype(int))  # here we have bins of 1 ps

        ## produce a TOF dataframe with convolved time response
        tof_sns = evt_tof.sensor_id.unique()

        evt_tof_exp_dist = []
        for s_id in tof_sns:
            tdc_conv = shf.sipm_shaping_convolution(evt_tof, spe_resp, s_id,
                                                    time_window)
            tdc_conv_df = shf.build_convoluted_df(evt, s_id, tdc_conv)
            if sigma_elec > 0:
                tdc_conv_df = tdc_conv_df.assign(
                    time=np.random.normal(tdc_conv_df.time.values, sigma_elec))
            tdc_conv_df = tdc_conv_df[tdc_conv_df.charge > timestamp_thr /
                                      norm]
            tdc_conv_df = tdc_conv_df[tdc_conv_df.time ==
                                      tdc_conv_df.time.min()]
            evt_tof_exp_dist.append(tdc_conv_df)
        evt_tof_exp_dist = pd.concat(evt_tof_exp_dist)

        try:
            min_id1, min_id2, min_t1, min_t2 = rf.find_coincidence_timestamps(
                evt_tof_exp_dist, sns1, sns2, n_pe)
            ave_pos1 = rf.calculate_average_SiPM_pos(min_id1, DataSiPM_idx)
            ave_pos2 = rf.calculate_average_SiPM_pos(min_id2, DataSiPM_idx)
            first_sipm1.append(ave_pos1)
            first_sipm2.append(ave_pos2)
        except WaveformEmptyTable:
            print(f'TOF dataframe has no minimum time for event {evt}')
            _, _, min_t1, min_t2 = [-1], [-1], -1, -1
            first_sipm1.append(np.array([0, 0, 0]))
            first_sipm2.append(np.array([0, 0, 0]))

        first_time1.append(min_t1)
        first_time2.append(min_t2)

        ## extract information about the interaction being photoelectric
        phot, phot_pos = mcf.select_photoelectric(evt_parts, evt_hits)
        if not phot:
            phot1 = False
            phot2 = False
        else:
            scalar_prod = true_pos1.dot(phot_pos[0])
            if scalar_prod > 0:
                phot1 = True
                phot2 = False
            else:
                phot1 = False
                phot2 = True

            if len(phot_pos) == 2:
                if scalar_prod > 0:
                    phot2 = True
                else:
                    phot1 = True

        ## extract information about the interaction being photoelectric-like
        distances1 = rf.find_hit_distances_from_true_pos(evt_hits, true_pos1)
        max_dist1 = distances1.max()
        distances2 = rf.find_hit_distances_from_true_pos(evt_hits, true_pos2)
        max_dist2 = distances2.max()

        event_ids.append(evt)
        reco_r1.append(r1)
        reco_phi1.append(phi1)
        reco_z1.append(z1)
        true_r1.append(np.sqrt(true_pos1[0]**2 + true_pos1[1]**2))
        true_phi1.append(np.arctan2(true_pos1[1], true_pos1[0]))
        true_z1.append(true_pos1[2])
        sns_response1.append(sum(q1))
        touched_sipms1.append(len(q1))
        true_time1.append(true_t1 / units.ps)
        photo1.append(phot1)
        max_hit_distance1.append(max_dist1)
        reco_r2.append(r2)
        reco_phi2.append(phi2)
        reco_z2.append(z2)
        true_r2.append(np.sqrt(true_pos2[0]**2 + true_pos2[1]**2))
        true_phi2.append(np.arctan2(true_pos2[1], true_pos2[0]))
        true_z2.append(true_pos2[2])
        sns_response2.append(sum(q2))
        touched_sipms2.append(len(q2))
        true_time2.append(true_t2 / units.ps)
        photo2.append(phot2)
        max_hit_distance2.append(max_dist2)

    a_true_r1 = np.array(true_r1)
    a_true_phi1 = np.array(true_phi1)
    a_true_z1 = np.array(true_z1)
    a_reco_r1 = np.array(reco_r1)
    a_reco_phi1 = np.array(reco_phi1)
    a_reco_z1 = np.array(reco_z1)
    a_sns_response1 = np.array(sns_response1)
    a_touched_sipms1 = np.array(touched_sipms1)
    a_first_sipm1 = np.array(first_sipm1)
    a_first_time1 = np.array(first_time1)
    a_true_time1 = np.array(true_time1)
    a_photo1 = np.array(photo1)
    a_max_hit_distance1 = np.array(max_hit_distance1)

    a_true_r2 = np.array(true_r2)
    a_true_phi2 = np.array(true_phi2)
    a_true_z2 = np.array(true_z2)
    a_reco_r2 = np.array(reco_r2)
    a_reco_phi2 = np.array(reco_phi2)
    a_reco_z2 = np.array(reco_z2)
    a_sns_response2 = np.array(sns_response2)
    a_touched_sipms2 = np.array(touched_sipms2)
    a_first_sipm2 = np.array(first_sipm2)
    a_first_time2 = np.array(first_time2)
    a_true_time2 = np.array(true_time2)
    a_photo2 = np.array(photo2)
    a_max_hit_distance2 = np.array(max_hit_distance2)

    a_event_ids = np.array(event_ids)

    np.savez(output_file,
             a_true_r1=a_true_r1,
             a_true_phi1=a_true_phi1,
             a_true_z1=a_true_z1,
             a_true_r2=a_true_r2,
             a_true_phi2=a_true_phi2,
             a_true_z2=a_true_z2,
             a_reco_r1=a_reco_r1,
             a_reco_phi1=a_reco_phi1,
             a_reco_z1=a_reco_z1,
             a_reco_r2=a_reco_r2,
             a_reco_phi2=a_reco_phi2,
             a_reco_z2=a_reco_z2,
             a_touched_sipms1=a_touched_sipms1,
             a_touched_sipms2=a_touched_sipms2,
             a_sns_response1=a_sns_response1,
             a_sns_response2=a_sns_response2,
             a_first_sipm1=a_first_sipm1,
             a_first_time1=a_first_time1,
             a_first_sipm2=a_first_sipm2,
             a_first_time2=a_first_time2,
             a_true_time1=a_true_time1,
             a_true_time2=a_true_time2,
             a_photo1=a_photo1,
             a_photo2=a_photo2,
             a_max_hit_distance1=a_max_hit_distance1,
             a_max_hit_distance2=a_max_hit_distance2,
             a_event_ids=a_event_ids)

    print(f'Not a coincidence: {c0}')
    print(f'Not passing threshold to reconstruct position = {c1}')
Пример #4
0
def build_r_map(input_file: str, output_file: str, threshold: float):
    """
    This function extracts the true radial position
    and the variance of the SiPM positions
    in phi and z for each gamma interaction.
    """

    DataSiPM = db.DataSiPMsim_only('petalo', 0)  # full body PET
    DataSiPM_idx = DataSiPM.set_index('SensorID')

    try:
        sns_response = pd.read_hdf(input_file, 'MC/sns_response')
    except ValueError:
        print(f'File {input_file} not found')
        exit()
    except OSError:
        print(f'File {input_file} not found')
        exit()
    except KeyError:
        print(f'No object named MC/sns_response in file {input_file}')
        exit()
    print(f'Analyzing file {input_file}')

    sel_df = rf.find_SiPMs_over_threshold(sns_response, threshold)

    particles = pd.read_hdf(input_file, 'MC/particles')
    hits = pd.read_hdf(input_file, 'MC/hits')
    events = particles.event_id.unique()

    true_r1, true_r2 = [], []
    var_phi1, var_phi2 = [], []
    var_z1, var_z2 = [], []

    touched_sipms1, touched_sipms2 = [], []

    for evt in events:

        ### Select photoelectric events only
        evt_parts = particles[particles.event_id == evt]
        evt_hits = hits[hits.event_id == evt]
        select, true_pos = mcf.select_photoelectric(evt_parts, evt_hits)
        if not select: continue

        sns_resp = sel_df[sel_df.event_id == evt]
        if len(sns_resp) == 0: continue

        _, _, pos1, pos2, q1, q2 = rf.assign_sipms_to_gammas(
            sns_resp, true_pos, DataSiPM_idx)

        if len(pos1) > 0:
            pos_phi = rf.from_cartesian_to_cyl(np.array(pos1))[:, 1]
            _, var_phi = rf.phi_mean_var(pos_phi, q1)

            pos_z = np.array(pos1)[:, 2]
            mean_z = np.average(pos_z, weights=q1)
            var_z = np.average((pos_z - mean_z)**2, weights=q1)
            r = np.sqrt(true_pos[0][0]**2 + true_pos[0][1]**2)

            var_phi1.append(var_phi)
            var_z1.append(var_z)
            touched_sipms1.append(len(pos1))
            true_r1.append(r)

        else:
            var_phi1.append(1.e9)
            var_z1.append(1.e9)
            touched_sipms1.append(1.e9)
            true_r1.append(1.e9)

        if len(pos2) > 0:
            pos_phi = rf.from_cartesian_to_cyl(np.array(pos2))[:, 1]
            _, var_phi = rf.phi_mean_var(pos_phi, q2)

            pos_z = np.array(pos2)[:, 2]
            mean_z = np.average(pos_z, weights=q2)
            var_z = np.average((pos_z - mean_z)**2, weights=q2)
            r = np.sqrt(true_pos[1][0]**2 + true_pos[1][1]**2)

            var_phi2.append(var_phi)
            var_z2.append(var_z)
            touched_sipms2.append(len(pos2))
            true_r2.append(r)

        else:
            var_phi2.append(1.e9)
            var_z2.append(1.e9)
            touched_sipms2.append(1.e9)
            true_r2.append(1.e9)

    a_true_r1 = np.array(true_r1)
    a_true_r2 = np.array(true_r2)
    a_var_phi1 = np.array(var_phi1)
    a_var_phi2 = np.array(var_phi2)
    a_var_z1 = np.array(var_z1)
    a_var_z2 = np.array(var_z2)

    a_touched_sipms1 = np.array(touched_sipms1)
    a_touched_sipms2 = np.array(touched_sipms2)

    np.savez(output_file,
             a_true_r1=a_true_r1,
             a_true_r2=a_true_r2,
             a_var_phi1=a_var_phi1,
             a_var_phi2=a_var_phi2,
             a_var_z1=a_var_z1,
             a_var_z2=a_var_z2,
             a_touched_sipms1=a_touched_sipms1,
             a_touched_sipms2=a_touched_sipms2)