291 lines
14 KiB
Python
291 lines
14 KiB
Python
|
|
import os
|
|
import numpy as np
|
|
from ROOT import TH1D, TH2D, TCanvas, TF1, TFile
|
|
from multiprocessing import Pool
|
|
from array import array
|
|
import h5py
|
|
from multiprocessing import Pool
|
|
import aare
|
|
|
|
_cfg = {} ### global config
|
|
_aduToKev3DMap = None ### global caliFile
|
|
_pedestalAduFrame = None ### global pedestalAduFrame
|
|
_noiseEneFrame = None ### global noiseEneFrame
|
|
nChunks = 16
|
|
clusterSize2Photon = 7
|
|
|
|
def init(cfg):
|
|
global _cfg
|
|
global _aduToKev3DMap
|
|
_cfg = cfg
|
|
_aduToKev3DMap = cfg['caliFile']
|
|
Roi = cfg['Roi']
|
|
global nChunks
|
|
nChunks = _cfg.get('NChunks', 16)
|
|
|
|
def convertAduFrameToEnergyFrame(aduFrame):
|
|
if _aduToKev3DMap is None:
|
|
return aduFrame
|
|
NX, NY = _cfg['NX'], _cfg['NY']
|
|
if _aduToKev3DMap.shape[0] == 1640:
|
|
idxFrame = (np.abs(aduFrame//10)).astype(np.int32)
|
|
idxFrame[idxFrame > _aduToKev3DMap.shape[0]-2] = _aduToKev3DMap.shape[0]-2
|
|
NY, NX = _aduToKev3DMap.shape[1:3]
|
|
_energyFrame0 = _aduToKev3DMap[idxFrame, np.arange(NY).reshape(-1, 1), np.arange(NX)]
|
|
_energyFrame1 = _aduToKev3DMap[idxFrame+1, np.arange(NY).reshape(-1, 1), np.arange(NX)]
|
|
energyFrame = _energyFrame0 + (_energyFrame1 - _energyFrame0)/10 * (np.abs(aduFrame) - idxFrame*10)
|
|
energyFrame *= np.sign(aduFrame)
|
|
|
|
else:
|
|
aduPerBin = 10
|
|
nExtraBins = 1000 // aduPerBin
|
|
idxFrame = (aduFrame//aduPerBin).astype(np.int32) + nExtraBins
|
|
idxFrame[idxFrame > _aduToKev3DMap.shape[0]-2] = _aduToKev3DMap.shape[0]-2
|
|
NY, NX = _aduToKev3DMap.shape[1:3]
|
|
_energyFrame0 = _aduToKev3DMap[idxFrame, np.arange(NY).reshape(-1, 1), np.arange(NX)]
|
|
_energyFrame1 = _aduToKev3DMap[idxFrame+1, np.arange(NY).reshape(-1, 1), np.arange(NX)]
|
|
energyFrame = _energyFrame0 + (_energyFrame1 - _energyFrame0)/aduPerBin * (aduFrame - (idxFrame-nExtraBins)*aduPerBin)
|
|
if 'energyScalingFactor' in _cfg:
|
|
energyFrame *= _cfg['energyScalingFactor']
|
|
return energyFrame
|
|
|
|
def bookHistograms(energy, suffix='', energyBinWidth=0.1, isMC=False):
|
|
histMaxEnergy = energy * 5.5
|
|
nEnergyBins = int(histMaxEnergy // energyBinWidth)
|
|
roi_x0, roi_x1, roi_y0, roi_y1 = _cfg['Roi'][0], _cfg['Roi'][1], _cfg['Roi'][2], _cfg['Roi'][3]
|
|
roi_width = roi_x1 - roi_x0
|
|
roi_height = roi_y1 - roi_y0
|
|
interpolationBins = 10
|
|
|
|
histograms = {}
|
|
histograms['h1_ClusterEnergy'] = TH1D(
|
|
f'h1_ClusterEnergy{suffix}', f'h1_ClusterEnergy{suffix}', nEnergyBins, -1, histMaxEnergy
|
|
)
|
|
histograms['h1_ClusterEnergy'].SetTitle('Cluster Energy;Energy [keV];Counts')
|
|
|
|
histograms['h1_PixelEnergy'] = TH1D(
|
|
f'h1_PixelEnergy{suffix}', f'h1_PixelEnergy{suffix}', nEnergyBins, -1, histMaxEnergy
|
|
)
|
|
histograms['h1_PixelEnergy'].SetTitle('Pixel Energy;Energy [keV];Counts')
|
|
|
|
histograms['h2_HitsSumFrame'] = TH2D(
|
|
f'h2_HitsSumFrame{suffix}', f'h2_HitsSumFrame{suffix}', roi_width, roi_x0, roi_x1, roi_height, roi_y0, roi_y1
|
|
)
|
|
histograms['h2_HitsSumFrame'].SetTitle('Hits Sum Frame;X [pixel];Y [pixel];Energy Sum [keV]')
|
|
|
|
histograms['h2_DoubleHitsSumFrame'] = TH2D(
|
|
f'h2_DoubleHitsSumFrame{suffix}', f'h2_DoubleHitsSumFrame{suffix}', roi_width, roi_x0, roi_x1, roi_height, roi_y0, roi_y1
|
|
)
|
|
histograms['h2_DoubleHitsSumFrame'].SetTitle('Double Hits Sum Frame;X [pixel];Y [pixel];Energy Sum [keV]')
|
|
|
|
histograms['h2_ClusterSizeVsEnergy'] = TH2D(
|
|
f'h2_ClusterSizeVsEnergy{suffix}', f'h2_ClusterSizeVsEnergy{suffix}', nEnergyBins, -1, histMaxEnergy, 30, 0, 30
|
|
)
|
|
histograms['h2_ClusterSizeVsEnergy'].SetTitle('Cluster Size vs Energy;Energy [keV];Cluster Size [pixel]')
|
|
|
|
histograms['h2_DiameterVsEnergy'] = TH2D(
|
|
f'h2_DiameterVsEnergy{suffix}', f'h2_DiameterVsEnergy{suffix}', nEnergyBins, -1, histMaxEnergy, 10, 0, 10
|
|
)
|
|
histograms['h2_DiameterVsEnergy'].SetTitle('Cluster Diameter vs Energy;Energy [keV];Cluster Diameter [pixel]')
|
|
|
|
for hist in histograms.values():
|
|
hist.SetDirectory(0)
|
|
|
|
return histograms
|
|
|
|
def _processFrames(idxChunk): ### for both single and double photon events, using
|
|
startFrame, endFrame = _cfg['NFrame'] * idxChunk // nChunks, min(_cfg['NFrame'] * (idxChunk + 1) // nChunks, _cfg['NFrame'])
|
|
print(f'Processing frames from {startFrame} to {endFrame}...')
|
|
Energy = _cfg['energy']
|
|
selectionRange = _cfg['selectionRange']
|
|
signalFileNames = _cfg['signalFileNames']
|
|
NX = _cfg['NX']
|
|
NY = _cfg['NY']
|
|
headerSize = _cfg['headerSize']
|
|
Roi = _cfg['Roi']
|
|
nFramePerFile = os.path.getsize(f'{signalFileNames[0]}') // (NX * NY + 56) // 2
|
|
_hists = bookHistograms(Energy, f'_{idxChunk}')
|
|
|
|
### create a cluster finder
|
|
nxROI = Roi[1] - Roi[0]
|
|
nyROI = Roi[3] - Roi[2]
|
|
CF = aare.VarClusterFinder((nxROI, nyROI), 5) ### arg1: frame size, arg2: threshold
|
|
CF.set_peripheralThresholdFactor(3)
|
|
CF.set_noiseMap(_noiseEneFrame)
|
|
|
|
if 'writeClusters' in _cfg and _cfg['writeClusters'] == True:
|
|
h5_1Photon_file = h5py.File(f'{_cfg["outputFolder"]}/Clusters_1Photon_chunk{idxChunk}.h5', 'w')
|
|
dset_1Photon_clusters = h5_1Photon_file.create_dataset(
|
|
'clusters', (0, 3, 3), maxshape=(None, 3, 3), dtype='f4',
|
|
chunks=True, compression='gzip'
|
|
)
|
|
dset_1photon_refs = h5_1Photon_file.create_dataset(
|
|
'referencePoint', (0, 2), maxshape=(None, 2), dtype='i4',
|
|
chunks=True
|
|
)
|
|
|
|
cluster_1Photon_list = []
|
|
refpoint_1Photon_list = []
|
|
|
|
h5_2Photon_file = h5py.File(f'{_cfg["outputFolder"]}/Clusters_2Photon_CS{clusterSize2Photon}_chunk{idxChunk}.h5', 'w')
|
|
dset_2Photon_clusters = h5_2Photon_file.create_dataset(
|
|
'clusters', (0, clusterSize2Photon, clusterSize2Photon), maxshape=(None, clusterSize2Photon, clusterSize2Photon), dtype='f4',
|
|
chunks=True, compression='gzip'
|
|
)
|
|
dset_2Photon_refs = h5_2Photon_file.create_dataset(
|
|
'referencePoint', (0, 2), maxshape=(None, 2), dtype='i4',
|
|
chunks=True
|
|
)
|
|
cluster_2Photon_list = []
|
|
refpoint_2Photon_list = []
|
|
|
|
def flush_h5_buffer():
|
|
old_len = dset_1Photon_clusters.shape[0]
|
|
new_len = old_len + len(cluster_1Photon_list)
|
|
dset_1Photon_clusters.resize((new_len, 3, 3))
|
|
dset_1photon_refs.resize((new_len, 2))
|
|
dset_1Photon_clusters[old_len:new_len] = cluster_1Photon_list
|
|
dset_1photon_refs[old_len:new_len] = refpoint_1Photon_list
|
|
cluster_1Photon_list.clear()
|
|
refpoint_1Photon_list.clear()
|
|
|
|
old_len = dset_2Photon_clusters.shape[0]
|
|
new_len = old_len + len(cluster_2Photon_list)
|
|
dset_2Photon_clusters.resize((new_len, clusterSize2Photon, clusterSize2Photon))
|
|
dset_2Photon_refs.resize((new_len, 2))
|
|
dset_2Photon_clusters[old_len:new_len] = cluster_2Photon_list
|
|
dset_2Photon_refs[old_len:new_len] = refpoint_2Photon_list
|
|
cluster_2Photon_list.clear()
|
|
refpoint_2Photon_list.clear()
|
|
|
|
for idxFrame in range(startFrame, endFrame):
|
|
if idxFrame % 10000 == 0:
|
|
print(f'Processing frame {idxFrame}...')
|
|
idxFile, idxFrame = divmod(idxFrame, nFramePerFile)
|
|
try:
|
|
if 'nFiber' in _cfg and _cfg['nFiber'] == 2:
|
|
offset = (headerSize + idxFrame * (NX * NY//2 + headerSize)) * 2
|
|
fileName1 = signalFileNames[idxFile]
|
|
fileName2 = fileName1.replace('d0', 'd1')
|
|
_signalAduFrame1 = np.fromfile(f'{fileName1}', dtype=np.uint16, offset=offset, count=NX * NY //2).astype(np.int32).reshape(NY//2, NX)
|
|
_signalAduFrame2 = np.fromfile(f'{fileName2}', dtype=np.uint16, offset=offset, count=NX * NY //2).astype(np.int32).reshape(NY//2, NX)
|
|
signalAduFrame = np.concatenate([_signalAduFrame1, _signalAduFrame2], axis=0)
|
|
del _signalAduFrame1, _signalAduFrame2
|
|
else:
|
|
offset = (headerSize + idxFrame * (NX * NY + headerSize)) * 2
|
|
signalAduFrame = np.fromfile(f'{signalFileNames[idxFile]}', dtype=np.uint16, offset=offset, count=NX * NY).astype(np.int32).reshape(NX, NY)
|
|
except Exception as e:
|
|
print(f'Error reading frame {idxFrame} from file {signalFileNames[idxFile]}: {e}, stop processing this chunk ({startFrame}-{endFrame})')
|
|
return _hists
|
|
|
|
signalAduFrame = signalAduFrame - _pedestalAduFrame
|
|
signalEneFrame = convertAduFrameToEnergyFrame(signalAduFrame)
|
|
|
|
CF.find_clusters_X(signalEneFrame[Roi[2]:Roi[3], Roi[0]:Roi[1]])
|
|
clusters = CF.hits()
|
|
for idxCluster in range(len(clusters)):
|
|
clusterSize = clusters['size'][idxCluster]
|
|
energy = clusters['energy'][idxCluster]
|
|
_hists['h1_ClusterEnergy'].Fill(energy)
|
|
xs = clusters['cols'][idxCluster][:clusterSize]
|
|
ys = clusters['rows'][idxCluster][:clusterSize]
|
|
enes = clusters['enes'][idxCluster][:clusterSize]
|
|
|
|
if energy < 5:
|
|
continue
|
|
|
|
### detect single photon events
|
|
if 2 * Energy - selectionRange < energy < 2 * Energy + selectionRange:
|
|
for i in range(len(xs)):
|
|
_hists['h2_DoubleHitsSumFrame'].Fill(xs[i]+Roi[0], ys[i]+Roi[2], enes[i])
|
|
if 'writeClusters' in _cfg and _cfg['writeClusters'] == True:
|
|
ref_x = (min(xs) + max(xs)) // 2 - (clusterSize2Photon // 2) ### refered to the lower-left corner of the cluster
|
|
ref_y = (min(ys) + max(ys)) // 2 - (clusterSize2Photon // 2)
|
|
cluster_2Photon = np.zeros((clusterSize2Photon, clusterSize2Photon), dtype=np.float32)
|
|
for i in range(len(xs)):
|
|
x_rel = xs[i] - int(ref_x)
|
|
y_rel = ys[i] - int(ref_y)
|
|
if 0 <= x_rel < clusterSize2Photon and 0 <= y_rel < clusterSize2Photon:
|
|
cluster_2Photon[y_rel, x_rel] = enes[i]
|
|
cluster_2Photon_list.append(cluster_2Photon)
|
|
refpoint_2Photon_list.append([int(ref_x)+Roi[0], int(ref_y)+Roi[2]])
|
|
|
|
_hists['h2_ClusterSizeVsEnergy'].Fill(energy, clusterSize)
|
|
_diameterX = max(xs) - min(xs) + 1
|
|
_diameterY = max(ys) - min(ys) + 1
|
|
_diameter = max(_diameterX, _diameterY)
|
|
_hists['h2_DiameterVsEnergy'].Fill(energy, _diameter)
|
|
for i in range(len(xs)):
|
|
_hists['h1_PixelEnergy'].Fill(enes[i])
|
|
_hists['h2_HitsSumFrame'].Fill(xs[i]+Roi[0], ys[i]+Roi[2], enes[i])
|
|
|
|
if 'writeClusters' in _cfg and _cfg['writeClusters'] == True:
|
|
flush_h5_buffer()
|
|
h5_1Photon_file.close()
|
|
h5_2Photon_file.close()
|
|
return _hists
|
|
|
|
def process():
|
|
with Pool(_cfg['NThread'], maxtasksperchild=1) as p:
|
|
results = p.map(_processFrames, range(nChunks))
|
|
|
|
global hists
|
|
hists = bookHistograms(_cfg['energy'])
|
|
for h in hists.values():
|
|
h.SetDirectory(0)
|
|
|
|
for _hists in results:
|
|
for key in hists.keys():
|
|
hists[key].Add(_hists[key])
|
|
del _hists
|
|
|
|
outputTFileName = f'/home/xie_x1/MLXID/DataProcess/Results/{_cfg["runName"]}.root'
|
|
outputTFile = TFile(outputTFileName, 'RECREATE')
|
|
for hist in hists.values():
|
|
hist.Write()
|
|
outputTFile.Close()
|
|
|
|
def getPedestalAndNoise():
|
|
NX, NY = _cfg['NX'], _cfg['NY']
|
|
pedestalFileName = _cfg['pedestalFileName']
|
|
if 'nFiber' in _cfg and _cfg['nFiber'] == 2:
|
|
pedestalFileName2 = pedestalFileName.replace('d0', 'd1')
|
|
_pedestalAduFrames1 = np.fromfile(f'{pedestalFileName}', dtype=np.uint16).astype(np.int32)
|
|
_pedestalAduFrames1 = _pedestalAduFrames1.reshape(-1, NX * NY //2 + 56)
|
|
_pedestalAduFrames1 = _pedestalAduFrames1[:, 56:].reshape(-1, NY//2, NX)
|
|
_pedestalAduFrames2 = np.fromfile(f'{pedestalFileName2}', dtype=np.uint16).astype(np.int32)
|
|
_pedestalAduFrames2 = _pedestalAduFrames2.reshape(-1, NX * NY //2 + 56)
|
|
_pedestalAduFrames2 = _pedestalAduFrames2[:, 56:].reshape(-1, NY//2, NX)
|
|
pedestalAduFrames = np.concatenate([_pedestalAduFrames1, _pedestalAduFrames2], axis=1)
|
|
del _pedestalAduFrames1, _pedestalAduFrames2
|
|
else:
|
|
pedestalAduFrames = np.fromfile(f'{pedestalFileName}', dtype=np.uint16).astype(np.int32)
|
|
pedestalAduFrames = pedestalAduFrames.reshape(-1, NX * NY + 56)
|
|
pedestalAduFrames = pedestalAduFrames[:, 56:].reshape(-1, NX, NY)
|
|
|
|
pedestalAduFrames = pedestalAduFrames[pedestalAduFrames.shape[0]//10:] # skip the first 10% frames
|
|
global _pedestalAduFrame, _noiseEneFrame
|
|
_pedestalAduFrame = np.mean(pedestalAduFrames, axis=0)
|
|
noiseAduFrame = np.std(pedestalAduFrames, axis=0, ddof=1)
|
|
with Pool(16) as p:
|
|
pedestalEneFrames = p.map(convertAduFrameToEnergyFrame, pedestalAduFrames)
|
|
_noiseEneFrame = np.std(pedestalEneFrames, axis=0, ddof=1)
|
|
print(f'Average noise = {np.mean(_noiseEneFrame):.3f} keV; {np.mean(noiseAduFrame):.3f} ADU')
|
|
del pedestalAduFrames, pedestalEneFrames
|
|
|
|
def getPedestalAndNoise_simplified():
|
|
NX, NY = _cfg['NX'], _cfg['NY']
|
|
pedestalFileName = _cfg['pedestalFileName']
|
|
pedestalAduFrames = np.fromfile(f'{pedestalFileName}', dtype=np.uint16).astype(np.int32)
|
|
pedestalAduFrames = pedestalAduFrames.reshape(-1, NX * NY + 56)
|
|
pedestalAduFrames = pedestalAduFrames[:, 56:].reshape(-1, NX, NY)
|
|
pedestalAduFrames = pedestalAduFrames[pedestalAduFrames.shape[0]//10:] # skip the first 10% frames
|
|
global _pedestalAduFrame, _noiseEneFrame
|
|
_pedestalAduFrame = np.mean(pedestalAduFrames, axis=0)
|
|
noiseAduFrame = np.std(pedestalAduFrames, axis=0, ddof=1)
|
|
_noiseEneFrame = convertAduFrameToEnergyFrame(noiseAduFrame)
|
|
|
|
print(f'Average noise = {np.mean(_noiseEneFrame):.3f} keV; {np.mean(noiseAduFrame):.3f} ADU')
|
|
del pedestalAduFrames
|