def get_adc_values(pixels_signals, time_ticks, adc_list, adc_ticks_list, time_padding, rng_states): """ Implementation of self-trigger logic Args: pixels_signals (:obj:`numpy.ndarray`): list of induced currents for each pixel time_ticks (:obj:`numpy.ndarray`): list of time ticks for each pixel adc_list (:obj:`numpy.ndarray`): list of integrated charges for each pixel adc_ticks_list (:obj:`numpy.ndarray`): list of the time ticks that correspond to each integrated charge. """ ip = cuda.grid(1) if ip < pixels_signals.shape[0]: curre = pixels_signals[ip] ic = 0 iadc = 0 q_sum = xoroshiro128p_normal_float32(rng_states, ip) * RESET_NOISE_CHARGE * consts.e_charge while ic < curre.shape[0]: q = curre[ic]*consts.t_sampling q_sum += q q_noise = xoroshiro128p_normal_float32(rng_states, ip) * UNCORRELATED_NOISE_CHARGE * consts.e_charge if q_sum + q_noise >= DISCRIMINATION_THRESHOLD: interval = round((3 * CLOCK_CYCLE + ADC_HOLD_DELAY * CLOCK_CYCLE) / consts.t_sampling) integrate_end = ic+interval while ic <= integrate_end and ic < curre.shape[0]: q = curre[ic] * consts.t_sampling q_sum += q ic += 1 adc = q_sum + xoroshiro128p_normal_float32(rng_states, ip) * UNCORRELATED_NOISE_CHARGE * consts.e_charge if adc < DISCRIMINATION_THRESHOLD: ic += round(CLOCK_CYCLE / consts.t_sampling) continue if iadc >= MAX_ADC_VALUES: print("More ADC values than possible, ", MAX_ADC_VALUES) break adc_list[ip][iadc] = adc adc_ticks_list[ip][iadc] = time_ticks[ic]+time_padding ic += round(CLOCK_CYCLE / consts.t_sampling) q_sum = xoroshiro128p_normal_float32(rng_states, ip) * RESET_NOISE_CHARGE * consts.e_charge iadc += 1 ic += 1
def rng_kernel_float32(states, out, count, distribution): thread_id = cuda.grid(1) for i in range(count): if distribution == UNIFORM: out[thread_id * count + i] = xoroshiro128p_uniform_float32(states, thread_id) elif distribution == NORMAL: out[thread_id * count + i] = xoroshiro128p_normal_float32(states, thread_id)
def mutation(inp_weights, n_ia, n_weights, rng_states, prob=0.1): # Thread id in a 1D block tx = cuda.threadIdx.x # Block id in a 1D grid ty = cuda.blockIdx.x if ty < n_ia and tx < n_weights: a = xoroshiro128p_uniform_float32(rng_states, cuda.grid(1)) if a < prob: inp_weights[ty][tx] += (xoroshiro128p_normal_float32( rng_states, cuda.grid(1))) / 5.
def generate_rand(rng_states, xtr, cov, Xepi, d): i = cuda.grid(1) ntr, dx = xtr.shape if i < ntr: # Generate random points for nepi in range(Nepi): for dx in range(dx): Xepi[i, dx, nepi] = xtr[i, dx] + \ math.sqrt(float(cov)) * \ xoroshiro128p_normal_float32( rng_states, i) # Compute distances for nepi in range(Nepi): smallest = 1e9 for nt in range(ntr): dist = 0 for dx in range(dx): dist += (Xepi[i, dx, nepi] - xtr[nt, dx])**2 if dist < smallest: smallest = dist d[i, nepi] = smallest
def tracks_current_mc(signals, pixels, tracks, response, rng_states): """ This CUDA kernel calculates the charge induced on the pixels by the input tracks using a MC method Args: signals (:obj:`numpy.ndarray`): empty 3D array with dimensions S x P x T, where S is the number of track segments, P is the number of pixels, and T is the number of time ticks. The output is stored here. pixels (:obj:`numpy.ndarray`): 2D array with dimensions S x P , where S is the number of track segments, P is the number of pixels and contains the pixel ID number. tracks (:obj:`numpy.ndarray`): 2D array containing the detector segments. response (:obj:`numpy.ndarray`): 3D array containing the tabulated response. rng_states (:obj:`numpy.ndarray`): array of random states for noise generation """ itrk, ipix, it = cuda.grid(3) ntrk, _, _ = cuda.gridsize(3) if itrk < signals.shape[0] and ipix < signals.shape[ 1] and it < signals.shape[2]: t = tracks[itrk] pID = pixels[itrk][ipix] pID_x, pID_y, pID_plane = id2pixel(pID) if pID_x >= 0 and pID_y >= 0: # Pixel coordinates x_p, y_p = get_pixel_coordinates(pID) x_p += detector.PIXEL_PITCH / 2 y_p += detector.PIXEL_PITCH / 2 if t["z_start"] < t["z_end"]: start = (t["x_start"], t["y_start"], t["z_start"]) end = (t["x_end"], t["y_end"], t["z_end"]) else: end = (t["x_start"], t["y_start"], t["z_start"]) start = (t["x_end"], t["y_end"], t["z_end"]) t_start = max( TIME_INTERVAL[0], round((t["t_start"] - detector.TIME_PADDING) / detector.TIME_SAMPLING) * detector.TIME_SAMPLING) time_tick = t_start + it * detector.TIME_SAMPLING segment = (end[0] - start[0], end[1] - start[1], end[2] - start[2]) length = sqrt(segment[0]**2 + segment[1]**2 + segment[2]**2) direction = (segment[0] / length, segment[1] / length, segment[2] / length) sigmas = (t["tran_diff"], t["tran_diff"], t["long_diff"]) impact_factor = sqrt(response.shape[0]**2 + response.shape[1]**2 ) * detector.RESPONSE_BIN_SIZE subsegment_start, subsegment_end = overlapping_segment( x_p, y_p, start, end, impact_factor) subsegment = (subsegment_end[0] - subsegment_start[0], subsegment_end[1] - subsegment_start[1], subsegment_end[2] - subsegment_start[2]) subsegment_length = sqrt(subsegment[0]**2 + subsegment[1]**2 + subsegment[2]**2) if subsegment_length == 0: return nstep = max(round(subsegment_length / MIN_STEP_SIZE), 1) step = subsegment_length / nstep # refine step size charge = t["n_electrons"] * (subsegment_length / length) / ( nstep * MC_SAMPLE_MULTIPLIER) total_current = 0 rng_state = (rng_states[itrk + ntrk * ipix], ) for istep in range(nstep): for _ in range(MC_SAMPLE_MULTIPLIER): x = subsegment_start[0] + step * (istep + 0.5) * direction[0] y = subsegment_start[1] + step * (istep + 0.5) * direction[1] z = subsegment_start[2] + step * (istep + 0.5) * direction[2] z += xoroshiro128p_normal_float32(rng_state, 0) * sigmas[2] t0 = abs(z - TPC_BORDERS[t["pixel_plane"]][2][0] ) / detector.V_DRIFT - detector.TIME_WINDOW if not t0 < time_tick < t0 + detector.TIME_WINDOW: continue x += xoroshiro128p_normal_float32(rng_state, 0) * sigmas[0] y += xoroshiro128p_normal_float32(rng_state, 0) * sigmas[1] x_dist = abs(x_p - x) y_dist = abs(y_p - y) if x_dist > detector.RESPONSE_BIN_SIZE * response.shape[0]: continue if y_dist > detector.RESPONSE_BIN_SIZE * response.shape[1]: continue total_current += charge * get_closest_waveform( x_dist, y_dist, time_tick - t0, response) signals[itrk, ipix, it] = total_current
def get_adc_values(pixels_signals, pixels_signals_tracks, time_ticks, adc_list, adc_ticks_list, time_padding, rng_states, current_fractions, pixel_thresholds): """ Implementation of self-trigger logic Args: pixels_signals (:obj:`numpy.ndarray`): list of induced currents for each pixel pixels_signals_tracks (:obj:`numpy.ndarray`): list of induced currents for each track that induces current on each pixel time_ticks (:obj:`numpy.ndarray`): list of time ticks for each pixel adc_list (:obj:`numpy.ndarray`): list of integrated charges for each pixel adc_ticks_list (:obj:`numpy.ndarray`): list of the time ticks that correspond to each integrated charge time_padding (float): time interval to add to each time tick. rng_states (:obj:`numpy.ndarray`): array of random states for noise generation current_fractions (:obj:`numpy.ndarray`): 2D array that will contain the fraction of current induced on the pixel by each track pixel_thresholds(: obj: `numpy.ndarray`): list of discriminator thresholds for each pixel """ ip = cuda.grid(1) if ip < pixels_signals.shape[0]: curre = pixels_signals[ip] ic = 0 iadc = 0 adc_busy = 0 last_reset = 0 q_sum = xoroshiro128p_normal_float32(rng_states, ip) * RESET_NOISE_CHARGE while ic < curre.shape[0] or adc_busy > 0: if iadc >= MAX_ADC_VALUES: print("More ADC values than possible,", MAX_ADC_VALUES) break q = 0 if BUFFER_RISETIME > 0: conv_start = max( last_reset, floor(ic - 10 * BUFFER_RISETIME / detector.TIME_SAMPLING)) for jc in range(conv_start, min(ic + 1, curre.shape[0])): w = exp( (jc - ic) * detector.TIME_SAMPLING / BUFFER_RISETIME ) * (1 - exp(-detector.TIME_SAMPLING / BUFFER_RISETIME)) q += curre[jc] * detector.TIME_SAMPLING * w for itrk in range(current_fractions.shape[2]): current_fractions[ip][iadc][ itrk] += pixels_signals_tracks[ip][jc][ itrk] * detector.TIME_SAMPLING * w elif ic < curre.shape[0]: q += curre[ic] * detector.TIME_SAMPLING for itrk in range(current_fractions.shape[2]): current_fractions[ip][iadc][itrk] += pixels_signals_tracks[ ip][ic][itrk] * detector.TIME_SAMPLING q_sum += q q_noise = xoroshiro128p_normal_float32( rng_states, ip) * UNCORRELATED_NOISE_CHARGE disc_noise = xoroshiro128p_normal_float32(rng_states, ip) * DISCRIMINATOR_NOISE if adc_busy > 0: adc_busy -= 1 if q_sum + q_noise >= pixel_thresholds[ ip] + disc_noise and adc_busy == 0: interval = round( (3 * CLOCK_CYCLE + ADC_HOLD_DELAY * CLOCK_CYCLE) / detector.TIME_SAMPLING) integrate_end = ic + interval ic += 1 while ic <= integrate_end: q = 0 if BUFFER_RISETIME > 0: conv_start = max( last_reset, floor(ic - 10 * BUFFER_RISETIME / detector.TIME_SAMPLING)) for jc in range(conv_start, min(ic + 1, curre.shape[0])): w = exp( (jc - ic) * detector.TIME_SAMPLING / BUFFER_RISETIME) * (1 - exp( -detector.TIME_SAMPLING / BUFFER_RISETIME)) q += curre[jc] * detector.TIME_SAMPLING * w for itrk in range(current_fractions.shape[2]): current_fractions[ip][iadc][ itrk] += pixels_signals_tracks[ip][jc][ itrk] * detector.TIME_SAMPLING * w elif ic < curre.shape[0]: q += curre[ic] * detector.TIME_SAMPLING for itrk in range(current_fractions.shape[2]): current_fractions[ip][iadc][ itrk] += pixels_signals_tracks[ip][ic][ itrk] * detector.TIME_SAMPLING q_sum += q ic += 1 adc = q_sum + xoroshiro128p_normal_float32( rng_states, ip) * UNCORRELATED_NOISE_CHARGE disc_noise = xoroshiro128p_normal_float32( rng_states, ip) * DISCRIMINATOR_NOISE if adc < pixel_thresholds[ip] + disc_noise: ic += round(RESET_CYCLES * CLOCK_CYCLE / detector.TIME_SAMPLING) q_sum = xoroshiro128p_normal_float32( rng_states, ip) * RESET_NOISE_CHARGE for itrk in range(current_fractions.shape[2]): current_fractions[ip][iadc][itrk] = 0 last_reset = ic continue tot_backtracked = 0 for itrk in range(current_fractions.shape[2]): tot_backtracked += current_fractions[ip][iadc][itrk] for itrk in range(current_fractions.shape[2]): current_fractions[ip][iadc][itrk] /= tot_backtracked adc_list[ip][iadc] = adc crossing_time_tick = min((ic, len(time_ticks) - 1)) # handle case when tick extends past end of current array post_adc_ticks = max((ic - crossing_time_tick, 0)) #+2-tick delay from when the PACMAN receives the trigger and when it registers it. adc_ticks_list[ip][iadc] = time_ticks[ crossing_time_tick] + time_padding - 2 + post_adc_ticks ic += round(RESET_CYCLES * CLOCK_CYCLE / detector.TIME_SAMPLING) last_reset = ic adc_busy = round(ADC_BUSY_DELAY * CLOCK_CYCLE / detector.TIME_SAMPLING) q_sum = xoroshiro128p_normal_float32(rng_states, ip) * RESET_NOISE_CHARGE iadc += 1 continue ic += 1