From 373888198369f1fa88e7d86bdee8d157b926f4c9 Mon Sep 17 00:00:00 2001 From: nstelter-slac Date: Tue, 26 Mar 2024 16:57:43 -0700 Subject: [PATCH 1/3] ENH: add detectorInfo class so we can handle other detectors, starting with epixM --- calibrationSuite/basicSuiteScript.py | 115 ++++++++++++--------------- calibrationSuite/detectorInfo.py | 62 +++++++++++++++ calibrationSuite/psana1Base.py | 6 +- calibrationSuite/psana2Base.py | 31 ++++++-- suite_scripts/rixSuiteConfig.py | 1 + 5 files changed, 142 insertions(+), 73 deletions(-) create mode 100644 calibrationSuite/detectorInfo.py diff --git a/calibrationSuite/basicSuiteScript.py b/calibrationSuite/basicSuiteScript.py index 2a2fd97..f54438b 100755 --- a/calibrationSuite/basicSuiteScript.py +++ b/calibrationSuite/basicSuiteScript.py @@ -18,7 +18,7 @@ from calibrationSuite.fitFunctions import * from calibrationSuite.ancillaryMethods import * from calibrationSuite.argumentParser import ArgumentParser - +from calibrationSuite.detectorInfo import DetectorInfo import os if os.getenv("foo") == "1": @@ -39,24 +39,8 @@ def __init__(self, analysisType="scan"): print("in BasicSuiteScript, inheriting from PsanaBase, type is psana%d" %(self.psanaType)) logger.info("in BasicSuiteScript, inheriting from PsanaBase, type is psana%d" %(self.psanaType)) - args = ArgumentParser().parse_args() - logger.info("parsed cmdline args: " + str(args)) ##mymodule = importlib.import_module(full_module_name) - # if the SUITE_CONFIG env var is set use that, otherwise if the cmd line arg is set use that. - # if neither are set, use the default 'suiteConfig.py' file. - defaultConfigFileName = "suiteConfig.py" - secondaryConfigFileName = defaultConfigFileName if args.configFile is None else args.configFile - # secondaryConfigFileName is returned if env var not set - configFileName = os.environ.get("SUITE_CONFIG", secondaryConfigFileName) - config = self.importConfigFile(configFileName) - if config is None: - print("\ncould not find or read config file: " + configFileName) - print("please set SUITE_CONFIG env-var or use the '-cf' cmd-line arg to specify a valid config file") - print("exiting...") - sys.exit(1) - experimentHash = config.experimentHash - self.gainModes = {"FH": 0, "FM": 1, "FL": 2, "AHL-H": 3, "AML-M": 4, "AHL-L": 5, "AML-L": 6} self.ePix10k_cameraTypes = {1: "Epix10ka", 4: "Epix10kaQuad", 16: "Epix10ka2M"} self.camera = 0 @@ -65,19 +49,21 @@ def __init__(self, analysisType="scan"): logging.info("output dir: " + self.outputDir) ##self.outputDir = '/tmp' + self.detectorInfo = DetectorInfo(self.experimentHash['detectorType']) + self.className = self.__class__.__name__ try: - self.location = experimentHash["location"] + self.location = self.experimentHash["location"] except: pass try: - self.exp = experimentHash["exp"] + self.exp = self.experimentHash["exp"] except: pass try: ##if True: - self.ROIfileNames = experimentHash["ROIs"] + self.ROIfileNames = self.experimentHash["ROIs"] self.ROIs = [] for f in self.ROIfileNames: self.ROIs.append(np.load(f + ".npy")) @@ -93,11 +79,11 @@ def __init__(self, analysisType="scan"): self.ROI = None self.ROIs = None try: - self.singlePixels = experimentHash["singlePixels"] + self.singlePixels = self.experimentHash["singlePixels"] except: self.singlePixels = None try: - self.regionSlice = experimentHash["regionSlice"] + self.regionSlice = self.experimentHash["regionSlice"] except: self.regionSlice = None if self.regionSlice is not None: @@ -109,13 +95,13 @@ def __init__(self, analysisType="scan"): self.sliceEdges = [sc[0][1] - sc[0][0], sc[1][1] - sc[1][0]] try: - self.fluxSource = experimentHash["fluxSource"] + self.fluxSource = self.experimentHash["fluxSource"] try: - self.fluxChannels = experimentHash["fluxChannels"] + self.fluxChannels = self.experimentHash["fluxChannels"] except: self.fluxChannels = range(8, 16) ## wave8 try: - self.fluxSign = experimentHash["fluxSign"] + self.fluxSign = self.experimentHash["fluxSign"] except: self.fluxSign = 1 except: @@ -135,27 +121,27 @@ def __init__(self, analysisType="scan"): ## for standalone analysis self.file = None - if args.files is not None: - self.file = args.files + if self.args.files is not None: + self.file = self.args.files self.label = "" - if args.label is not None: - self.label = args.label - - ## analyzing xtc - if args.run is not None: - self.run = args.run - if args.camera is not None: - self.camera = args.camera - if args.exp is not None: - self.exp = args.exp - if args.location is not None: - self.location = args.location - if args.maxNevents is not None: - self.maxNevents = args.maxNevents - if args.skipNevents is not None: - self.skipNevents = args.skipNevents - if args.path is not None: - self.outputDir = args.path + if self.args.label is not None: + self.label = self.args.label + + ## analyzing xtcs + if self.args.run is not None: + self.run = self.args.run + if self.args.camera is not None: + self.camera = self.args.camera + if self.args.exp is not None: + self.exp = self.args.exp + if self.args.location is not None: + self.location = self.args.location + if self.args.maxNevents is not None: + self.maxNevents = self.args.maxNevents + if self.args.skipNevents is not None: + self.skipNevents = self.args.skipNevents + if self.args.path is not None: + self.outputDir = self.args.path # if set, output folders will be relative to OUTPUT_ROOT # if not, they will be relative to the current script file self.outputDir = os.getenv("OUTPUT_ROOT", "") + self.outputDir @@ -172,31 +158,32 @@ def __init__(self, analysisType="scan"): #os.makedirs(self.outputDir) # give dir read, write, execute permissions #os.chmod(self.outputDir, 0o777) - self.detObj = args.detObj - if args.threshold is not None: - self.threshold = eval(args.threshold) + self.detObj = self.args.detObj + if self.args.threshold is not None: + self.threshold = eval(self.args.threshold) else: self.threshold = None - if args.fluxCut is not None: - self.fluxCut = args.fluxCut + if self.args.fluxCut is not None: + self.fluxCut = self.args.fluxCut try: - self.runRange = eval(args.runRange) ## in case needed + self.runRange = eval(self.args.runRange) ## in case needed except: self.runRange = None - self.fivePedestalRun = args.fivePedestalRun ## in case needed - self.fakePedestal = args.fakePedestal ## in case needed + self.fivePedestalRun = self.args.fivePedestalRun ## in case needed + self.fakePedestal = self.args.fakePedestal ## in case needed if self.fakePedestal is not None: self.fakePedestalFrame = np.load(self.fakePedestal) ##cast to uint32??? - if args.detType == "": + if self.args.detType == "": ## assume epix10k for now - if args.nModules is not None: - self.detType = self.ePix10k_cameraTypes[args.nModules] + if self.args.nModules is not None: + self.detectorInfo.setNModules(self.args.nModules) + self.detType = self.detectorInfo.getCameraType() else: - self.detType = args.detType + self.detType = self.args.detType - self.special = args.special + self.special = self.args.special ## done with configuration self.ds = None @@ -242,7 +229,7 @@ def rowCommonModeCorrection(self, frame, arbitraryCut=1000): ## cut keeps photons in common mode - e.g. set to <<1 photon ##rand = np.random.random() - for r in range(self.detRows): + for r in range(self.detectorInfo.nRows): colOffset = 0 ##for b in range(0, self.detNbanks): for b in range(0, 2): @@ -277,22 +264,22 @@ def colCommonModeCorrection(self, frame, arbitraryCut=1000): ##for b in range(0, 2): try: colCM = np.median( - frame[rowOffset : rowOffset + self.detRowsPerBank, c][ - frame[rowOffset : rowOffset + self.detRowsPerBank, c] < arbitraryCut + frame[rowOffset : rowOffset + self.detectorInfo.nRowsPerBank, c][ + frame[rowOffset : rowOffset + self.detectorInfo.nRowsPerBank, c] < arbitraryCut ] ) ##if r == 280 and rand > 0.999: ##print(b, frame[r, colOffset:colOffset + self.detColsPerBank], rowCM, rowCM 0.999: ##print(frame[r, colOffset:colOffset + self.detColsPerBank], np.median(frame[r, colOffset:colOffset + self.detColsPerBank])) except: colCM = -666 print("colCM problem") logger.error("colCM problem") - print(frame[rowOffset : rowOffset + self.detRowsPerBank], c) - rowOffset += self.detRowsPerBank + print(frame[rowOffset : rowOffset + self.detectorInfo.nRowsPerBank], c) + rowOffset += self.detectorInfo.nRowsPerBank return frame def isBeamEvent(self, evt): diff --git a/calibrationSuite/detectorInfo.py b/calibrationSuite/detectorInfo.py new file mode 100644 index 0000000..a80f720 --- /dev/null +++ b/calibrationSuite/detectorInfo.py @@ -0,0 +1,62 @@ +############################################################################## +## This file is part of 'SLAC Beamtime Calibration Suite'. +## It is subject to the license terms in the LICENSE.txt file found in the +## top-level directory of this distribution and at: +## https://confluence.slac.stanford.edu/display/ppareg/LICENSE.html. +## No part of 'SLAC Beamtime Calibration Suite', including this file, +## may be copied, modified, propagated, or distributed except according to +## the terms contained in the LICENSE.txt file. +############################################################################## +class DetectorInfo: + def __init__(self, detType): + + # declare these here in case any setup_X functions don't + # and -1 so caller knows things are not setup (non-0 to avoid error on divide) + self.nRows = -1 + self.nCols = -1 + self.nColsPerBank = -1 + self.detNbanks = -1 + self.nBanksCol = -1 + self.nRowsPerBank = -1 + self.dimension = -1 + + knownTypes = ['epixhr', 'epixM', 'rixsCCD'] + if detType not in knownTypes: + raise Exception("type %s not in known types" % (detType, knownTypes)) + + self.ePix10kCameraTypes = {1: "Epix10ka", 4: "Epix10kaQuad", 16: "Epix10ka2M"} + self.chosenCameraType = None + + if detType == 'epixhr': + self.setup_epixhr() + elif detType == 'epixM': + self.setup_epixM() + elif detType == 'rixsCCD': + self.setup_rixsCCD() + + def setNModules(self, n): + self.choosenCameraType = self.ePix10kCameraTypes[n] + + def getCameraType(self): + return self.choosenCameraType + + def setup_epixhr(self, version=0): + self.nRows = 288 + self.nCols = 666 + self.nColsPerBank = 96 + self.detNbanks = int(self.nCols / self.nColsPerBank) + self.nBanksCol = 2 + self.nRowsPerBank = int(self.nRows / self.nBanksCol) + + #need to still implement getGainMode() + #self.gainMode = self.getGainMode() + self.preferredCommonMode = 'regionCommonMode' + self.clusterShape = [3,3] + + def setup_epixM(self, version=0): + #todo: setup detector here + temp = 0 #make python happy + + def setup_rixsCCD(self, version=0): + #todo: setup detector here + temp = 0 #make python happy diff --git a/calibrationSuite/psana1Base.py b/calibrationSuite/psana1Base.py index 0713974..bd6754a 100755 --- a/calibrationSuite/psana1Base.py +++ b/calibrationSuite/psana1Base.py @@ -11,6 +11,7 @@ from PSCalib.NDArrIO import load_txt import logging import sys +from calibrationSuite.argumentParser import ArgumentParser logger = logging.getLogger(__name__) @@ -27,7 +28,10 @@ def __init__(self, analysisType="scan"): self.g0cut = 1 << 14 self.gainBitsMask = self.g0cut - 1 - ## self.setupPsana() + self.args = ArgumentParser().parse_args() + logger.info("parsed cmdline args: " + str(self.args)) + + ## self.setupPsana() def get_ds(self, run=None): if run is None: diff --git a/calibrationSuite/psana2Base.py b/calibrationSuite/psana2Base.py index f1c67eb..b3e8d84 100755 --- a/calibrationSuite/psana2Base.py +++ b/calibrationSuite/psana2Base.py @@ -8,6 +8,7 @@ ## the terms contained in the LICENSE.txt file. ############################################################################## from psana import * +from calibrationSuite.argumentParser import ArgumentParser ##from PSCalib.NDArrIO import load_txt @@ -48,12 +49,32 @@ def __init__(self, analysisType="scan"): self.allowed_timestamp_mismatch = 1000 + self.args = ArgumentParser().parse_args() + logger.info("parsed cmdline args: " + str(self.args)) + + # if the SUITE_CONFIG env var is set use that, otherwise if the cmd line arg is set use that + # if neither are set, use the default 'suiteConfig.py' file + defaultConfigFileName = "suiteConfig.py" + secondaryConfigFileName = defaultConfigFileName if self.args.configFile is None else self.args.configFile + # secondaryConfigFileName is returned if env var not set + configFileName = os.environ.get("SUITE_CONFIG", secondaryConfigFileName) + config = self.importConfigFile(configFileName) + if config is None: + print("\ncould not find or read config file: " + configFileName) + print("please set SUITE_CONFIG env-var or use the '-cf' cmd-line arg to specify a valid config file") + print("exiting...") + sys.exit(1) + self.experimentHash = config.experimentHash + knownTypes = ['epixhr', 'epixM', 'rixsCCD'] + if self.experimentHash['detectorType'] not in knownTypes: + print ("type %s not in known types" %(self.experimentHash['detectorType']), knownTypes) + return -1 ##self.setupPsana() def get_ds(self, run=None): if run is None: run = self.run - return DataSource(exp=self.exp, run=run, intg_det="epixhr", max_events=self.maxNevents) + return DataSource(exp=self.exp, run=run, intg_det=self.experimentHash['detectorType'], max_events=self.maxNevents) def setupPsana(self): ##print("have built basic script class, exp %s run %d" %(self.exp, self.run)) @@ -73,17 +94,11 @@ def setupPsana(self): ## self.det = Detector('%s.0:%s.%d' %(self.location, self.detType, self.camera), self.ds.env()) ## make this less dumb to accomodate epixM etc. ## use a dict etc. - self.det = self.myrun.Detector("epixhr") + self.det = self.myrun.Detector(self.experimentHash['detectorType']) if self.det is None: print("no det object for epixhr, what? Pretend it's ok.") ##raise Exception ## could set to None and reset with first frame I guess, or does the det object know? - self.detRows = 288 - self.detCols = 384 - self.detColsPerBank = 96 - self.detNbanks = int(self.detCols / self.detColsPerBank) - self.detNbanksCol = 2 ## assumes four asics - self.detRowsPerBank = int(self.detRows / self.detNbanksCol) self.timing = self.myrun.Detector("timing") self.desiredCodes = {"120Hz": 272, "4kHz": 273, "5kHz": 274} diff --git a/suite_scripts/rixSuiteConfig.py b/suite_scripts/rixSuiteConfig.py index fa3e718..756c162 100755 --- a/suite_scripts/rixSuiteConfig.py +++ b/suite_scripts/rixSuiteConfig.py @@ -12,6 +12,7 @@ ##experimentHash = {'exp':'mfxx1005021', 'location':'MfxEndstation', 'fluxSource':'MFX-USR-DIO', 'fluxChannels':[11], 'fluxSign':-1} ##experimentHash = {'exp':'rixc00121', 'location':'RixEndstation', experimentHash = { + "detectorType": "epixhr", "exp": "rixx1003721", "location": "RixEndstation", "fluxSource": "MfxDg1BmMon", From f5f2647fb336717ace6dd4de2dfa8500ce40d5b9 Mon Sep 17 00:00:00 2001 From: nstelter-slac Date: Fri, 29 Mar 2024 16:36:34 -0700 Subject: [PATCH 2/3] STY: move config-import function to base class --- calibrationSuite/basicSuiteScript.py | 10 ---------- calibrationSuite/psana2Base.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/calibrationSuite/basicSuiteScript.py b/calibrationSuite/basicSuiteScript.py index f54438b..544017c 100755 --- a/calibrationSuite/basicSuiteScript.py +++ b/calibrationSuite/basicSuiteScript.py @@ -9,7 +9,6 @@ ############################################################################## import argparse import numpy as np -import importlib.util import matplotlib.pyplot as plt from matplotlib.ticker import AutoMinorLocator import sys @@ -192,15 +191,6 @@ def __init__(self, analysisType="scan"): ##self.setupPsana() ##do this later or skip for -file - def importConfigFile(self, file_path): - if not os.path.exists(file_path): - print(f"The file '{file_path}' does not exist") - return None - spec = importlib.util.spec_from_file_location("config", file_path) - config_module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(config_module) - return config_module - def setROI(self, roiFile=None, roi=None): """Call with both file name and roi to save roi to file and use, just name to load, diff --git a/calibrationSuite/psana2Base.py b/calibrationSuite/psana2Base.py index b3e8d84..8490b3c 100755 --- a/calibrationSuite/psana2Base.py +++ b/calibrationSuite/psana2Base.py @@ -9,6 +9,7 @@ ############################################################################## from psana import * from calibrationSuite.argumentParser import ArgumentParser +import importlib.util ##from PSCalib.NDArrIO import load_txt @@ -71,6 +72,15 @@ def __init__(self, analysisType="scan"): return -1 ##self.setupPsana() + def importConfigFile(self, file_path): + if not os.path.exists(file_path): + print(f"The file '{file_path}' does not exist") + return None + spec = importlib.util.spec_from_file_location("config", file_path) + config_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(config_module) + return config_module + def get_ds(self, run=None): if run is None: run = self.run From 61f528e08e18ae97f790b85a226a53f1a555cf7a Mon Sep 17 00:00:00 2001 From: nstelter-slac Date: Mon, 1 Apr 2024 13:53:20 -0700 Subject: [PATCH 3/3] ENH: Move arg-parsing and config file import also to psana1base --- calibrationSuite/psana1Base.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/calibrationSuite/psana1Base.py b/calibrationSuite/psana1Base.py index bd6754a..530da32 100755 --- a/calibrationSuite/psana1Base.py +++ b/calibrationSuite/psana1Base.py @@ -11,6 +11,7 @@ from PSCalib.NDArrIO import load_txt import logging import sys +import os from calibrationSuite.argumentParser import ArgumentParser logger = logging.getLogger(__name__) @@ -31,8 +32,35 @@ def __init__(self, analysisType="scan"): self.args = ArgumentParser().parse_args() logger.info("parsed cmdline args: " + str(self.args)) + # if the SUITE_CONFIG env var is set use that, otherwise if the cmd line arg is set use that + # if neither are set, use the default 'suiteConfig.py' file + defaultConfigFileName = "suiteConfig.py" + secondaryConfigFileName = defaultConfigFileName if self.args.configFile is None else self.args.configFile + # secondaryConfigFileName is returned if env var not set + configFileName = os.environ.get("SUITE_CONFIG", secondaryConfigFileName) + config = self.importConfigFile(configFileName) + if config is None: + print("\ncould not find or read config file: " + configFileName) + print("please set SUITE_CONFIG env-var or use the '-cf' cmd-line arg to specify a valid config file") + print("exiting...") + sys.exit(1) + self.experimentHash = config.experimentHash + knownTypes = ['epixhr', 'epixM', 'rixsCCD'] + if self.experimentHash['detectorType'] not in knownTypes: + print ("type %s not in known types" %(self.experimentHash['detectorType']), knownTypes) + return -1 + ## self.setupPsana() + def importConfigFile(self, file_path): + if not os.path.exists(file_path): + print(f"The file '{file_path}' does not exist") + return None + spec = importlib.util.spec_from_file_location("config", file_path) + config_module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(config_module) + return config_module + def get_ds(self, run=None): if run is None: run = self.run