Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/archon droplet #223

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions lcls2_producers/prod_config_rix.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
# list empty.
detectors = ['hsd', 'rix_fim0', 'rix_fim1', 'crix_w8', 'c_piranha']
# integrating_detectors = []
integrating_detectors = ['andor_dir', 'andor_vls', 'andor_norm', 'archon']
integrating_detectors = ['archon']
# integrating_detectors = ['andor_dir', 'andor_vls', 'andor_norm', 'archon']
# Comment: the first integrating detector will set the sub-sampling of all
# integrating detectors.
slow_detectors = [] # NOT IMPLEMENTED
Expand Down Expand Up @@ -45,11 +46,31 @@ def getROIs(run):
return ret_dict


def getDetImages(run):
def get_droplet2photon(run):
ret_dict = {}

if run>0:
ret_dict['archon'] = {}
if run > 0:
# ##### Archon setup #####
d2p_dict = {}
# droplet args
d2p_dict['droplet'] = {
'threshold': 65,
# 'thresholdLow': 0,
'thresADU': 0, # discard droplet whose total ADU is below this value
'useRms': False
}

# droplet2Photons args
d2p_dict['d2p'] = {
'aduspphot': 400,
# 'roi_mask': np.load('path_to_mask.npy'),
'cputime': True
}
d2p_dict['nData'] = None
d2p_dict['get_photon_img'] = False

ret_dict['archon'] = d2p_dict

return ret_dict


Expand Down
47 changes: 42 additions & 5 deletions lcls2_producers/smd_producer.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't see why json is now imported.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use it to print the droplet argument dictionary nicely to the log. This makes it easier to read the rather intricate dictionary:
print(json.dumps(d2p_args[detname], indent=4))

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python

import numpy as np
import json
import psana
import time
from datetime import datetime
Expand Down Expand Up @@ -56,6 +57,8 @@ def define_dets(run, det_list):
wfs_int_args = config.get_wf_integrate(run)
if "get_wf_hitfinder" in dir(config):
wfs_hitfinder_args = config.get_wf_hitfinder(run)
if "get_droplet2photon" in dir(config):
d2p_args = config.get_droplet2photon(run)

dets = []

Expand Down Expand Up @@ -109,10 +112,42 @@ def define_dets(run, det_list):
thisROIFunc.addFunc(projectionFunc(axis=proj_ax))
det.addFunc(thisROIFunc)

if detname in dimgs_args:
# Detector image (det.raw.image())
dimg_func = detImageFunc(**dimgs_args[detname])
det.addFunc(dimg_func)
if detname in d2p_args:
if rank == 0:
print(f"\n\nSETTING UP DROPLET TO PHOTON PIPELINE FOR DETECTOR {det._name}")
print(json.dumps(d2p_args[detname], indent=4))
print("\n")
if 'nData' in d2p_args[detname]: # defines how sparsifcation is saved
nData = d2p_args[detname].pop('nData')
else:
nData = None
if 'get_photon_img' in d2p_args[detname]: # save unsparsified image or not
unsparsify = d2p_args[detname].pop('get_photon_img')
else:
unsparsify = False

# Make individual analysis functions
# (i) droplet
droplet_dict = d2p_args[detname]['droplet']
dropfunc = dropletFunc(**droplet_dict)
# (ii) droplet2Photon
d2p_dict = d2p_args[detname]['d2p']
drop2phot_func = droplet2Photons(**d2p_dict)
# (iii) sparsify
sparsify = sparsifyFunc(nData=nData)

# Assemble pipeline last to first
if unsparsify:
unsparsify_func = unsparsifyFunc()
drop2phot_func.addFunc(unsparsify_func)
drop2phot_func.addFunc(sparsify)
dropfunc.addFunc(drop2phot_func)
det.addFunc(dropfunc)

# if detname in dimgs_args:
# # Detector image (det.raw.image())
# dimg_func = detImageFunc(**dimgs_args[detname])
# det.addFunc(dimg_func)

if detname in wfs_int_args:
# Waveform integration
Expand Down Expand Up @@ -151,7 +186,7 @@ def define_dets(run, det_list):
projectionFunc,
imageFunc,
)
from smalldata_tools.ana_funcs.sparsifyFunc import sparsifyFunc
from smalldata_tools.ana_funcs.sparsifyFunc import sparsifyFunc, unsparsifyFunc
from smalldata_tools.ana_funcs.waveformFunc import WfIntegration, SimpleHitFinder
from smalldata_tools.ana_funcs.waveformFunc import getCMPeakFunc, templateFitFunc
from smalldata_tools.ana_funcs.waveformFunc import (
Expand All @@ -162,6 +197,8 @@ def define_dets(run, det_list):
)
from smalldata_tools.ana_funcs.droplet import dropletFunc
from smalldata_tools.ana_funcs.photons import photonFunc
from smalldata_tools.ana_funcs.droplet2Photons import droplet2Photons

from smalldata_tools.ana_funcs.azimuthalBinning import azimuthalBinning

import psplot
Expand Down
32 changes: 0 additions & 32 deletions smalldata_tools/ana_funcs/roi_rebin.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,38 +337,6 @@ def process(self, data):
return ret_dict


class detImageFunc(DetObjectFunc):
"""
/!\/!\/!\ Only for LCLS-II. /!\/!\/!\
Return the results of det.raw.image from psana.

Typically useful for special detectors like the Archon, which
has special pixels that are handled in the image method.
"""

def __init__(self, **kwargs):
self._name = kwargs.get("name", "dimage")
super(detImageFunc, self).__init__(**kwargs)

def setFromDet(self, det):
super(detImageFunc, self).setFromDet(det)
self._run = det.run.runnum
self._det = det.det

def process(self, data):
# pedestal application should happen when data is produced from calib
img = self._det.raw.image(self._run, data)
ret_dict = {"image": img}

# store for further processing
self.dat = img
subfuncResults = self.processFuncs()
for k in subfuncResults:
for kk in subfuncResults[k]:
ret_dict["%s_%s" % (k, kk)] = subfuncResults[k][kk]
return ret_dict


# DEBUG ME WITH MASKED ARRAY#
class imageFunc(DetObjectFunc):
"""
Expand Down
8 changes: 6 additions & 2 deletions smalldata_tools/ana_funcs/sparsifyFunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
class sparsifyFunc(DetObjectFunc):
"""
Function to sparsify a passed array (2 or 3-d input)
nData: if passed, make output array rectangular (for storing in event based smlData)
if a dictionary w/ data, row, col is passed, only make rectangular
nData: changes the way sparse data are saved:
If an int is passed, make the output array rectangular of length
nData (fixed shape for all events).
If None (default), store coordinate in a continuous long vector
If 0 uses ragged array (not recommended)
If a dictionary w/ data, row, col is passed, only make rectangular
"""

def __init__(self, **kwargs):
Expand Down
67 changes: 57 additions & 10 deletions smalldata_tools/lcls2/DetObject.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,21 +510,68 @@ def getData(self, evt):
return


class ArchonObject(CameraObject):
class ImageObject(CameraObject):
def __init__(self, det, run, **kwargs):
"""
Extension of CameraObject for detector that should use the psana image method instead of the
usual calib method.
For now (1/22/2025), this is only applicable to the Archon detector.
"""
super().__init__(det, run, **kwargs)
self.params_to_img()
return

def params_to_img(self):
"""
Convert all parameters from the calib shape to the image shape:
"""
# pedestals
self.ped = self._to_img_shape(self.ped)

# rms
self.rms = self._to_img_shape(self.rms)

# gain
self.gain = self._to_img_shape(self.gain)

# masks
self.mask = self._to_img_shape(self.mask)
self.cmask = self._to_img_shape(self.cmask)
return

def _to_img_shape(self, arr):
if arr is None:
return None
else:
return self.det.raw.image(None, arr)

def getData(self, evt):
super().getData(evt)
if self.common_mode < 0:
dat = self.det.raw.raw(evt)
elif self.common_mode in [0, 30]:
dat = self.det.raw.calib(evt)
if dat is None:
return
self.evt.dat = self.det.raw.image(None, dat)
return


class ArchonObject(ImageObject):
def __init__(self, det, run, **kwargs):
super(ArchonObject, self).__init__(det, run, **kwargs)
self.mask = None
self.cmask = None
# self.mask = None
# self.cmask = None
self.common_mode = kwargs.get("common_mode", 30) # default to calib
if self.common_mode is None:
self.common_mode = 30
return

def getData(self, evt):
super(ArchonObject, self).getData(evt)
# def getData(self, evt):
# super(ArchonObject, self).getData(evt)

if self.common_mode <= 0:
self.evt.dat = self.det.raw.raw(evt)
elif self.common_mode == 30:
self.evt.dat = self.det.raw.calib(evt)
return
# if self.common_mode <= 0:
# self.evt.dat = self.det.raw.raw(evt)
# elif self.common_mode == 30:
# self.evt.dat = self.det.raw.calib(evt)
# return
Loading