diff --git a/cal_tools/cal_tools/agipdlib.py b/cal_tools/cal_tools/agipdlib.py index 0049762e2474511e75b508d3640510c4cb7b7bb9..a098b07b83a7af795b8000c0aced7f4eca62c6bb 100644 --- a/cal_tools/cal_tools/agipdlib.py +++ b/cal_tools/cal_tools/agipdlib.py @@ -15,17 +15,16 @@ from cal_tools.agipdutils import ( match_asic_borders, melt_snowy_pixels, ) -from cal_tools.enums import BadPixels, SnowResolution +from cal_tools.enums import AgipdGainMode, BadPixels, SnowResolution from cal_tools.tools import get_constant_from_db_and_time -from iCalibrationDB import Conditions, Constants +from iCalibrationDB import Conditions from cal_tools.cython import agipdalgs as calgs def get_num_cells(fname, loc, module): with h5py.File(fname, "r") as f: - cells = \ - f[f"INSTRUMENT/{loc}/DET/{module}CH0:xtdf/image/cellId"][()] + cells = f[f"INSTRUMENT/{loc}/DET/{module}CH0:xtdf/image/cellId"][()] if cells.shape[0] == 0: return None maxcell = np.max(cells) @@ -77,7 +76,7 @@ def get_acq_rate(fast_paths: Tuple[str, str, int], fast_data_file = Path(fast_data_file) if fast_data_file.is_file(): fast_data_path = f'INSTRUMENT/{karabo_id}/DET/{module}CH0:xtdf/image/pulseId' # noqa - with h5py.File(fast_data_file) as fin: + with h5py.File(fast_data_file, "r") as fin: if fast_data_path in fin: # pulses is of shape (NNNN, 1), of type uint8. # Squeeze out the data, and subtract the 3rd entry from the 2nd @@ -98,7 +97,7 @@ def get_gain_setting(fname: str, h5path_ctrl: str) -> int: gain-setting 1: setupr@dark=8, setupr@slopespc=40 gain-setting 0: setupr@dark=0, setupr@slopespc=32 - patternTypeIndex 1: High-gian + patternTypeIndex 1: High-gain patternTypeIndex 2: Medium-gain patternTypeIndex 3: Low-gain patternTypeIndex 4: SlopesPC @@ -129,6 +128,17 @@ def get_gain_setting(fname: str, h5path_ctrl: str) -> int: raise ValueError('Could not derive gain setting from setupr and patternTypeIndex') # noqa +def get_gain_mode(fname: str, h5path_ctrl: str) -> AgipdGainMode: + """Returns the gain mode (adaptive or fixed) from slow data""" + + h5path_run = h5path_ctrl.replace("CONTROL/", "RUN/", 1) + h5path_gainmode = f'{h5path_run}/gainModeIndex/value' + with h5py.File(fname, "r") as fd: + if h5path_gainmode in fd: + return AgipdGainMode(fd[h5path_gainmode][0]) + return AgipdGainMode.ADAPTIVE_GAIN + + def get_bias_voltage(fname: str, karabo_id_control: str, module: Optional[int] = 0) -> int: """Read the voltage information from the FPGA device of module 0. @@ -155,10 +165,15 @@ def get_bias_voltage(fname: str, karabo_id_control: str, class AgipdCorrections: - def __init__(self, max_cells, max_pulses, - h5_data_path="INSTRUMENT/SPB_DET_AGIPD1M-1/DET/{}CH0:xtdf/", - h5_index_path="INDEX/SPB_DET_AGIPD1M-1/DET/{}CH0:xtdf/", - corr_bools: Optional[dict] = None): + def __init__( + self, + max_cells, + max_pulses, + h5_data_path="INSTRUMENT/SPB_DET_AGIPD1M-1/DET/{}CH0:xtdf/", + h5_index_path="INDEX/SPB_DET_AGIPD1M-1/DET/{}CH0:xtdf/", + corr_bools: Optional[dict] = None, + gain_mode: AgipdGainMode = AgipdGainMode.ADAPTIVE_GAIN, + ): """ Initialize an AgipdCorrections Class @@ -187,9 +202,7 @@ class AgipdCorrections: const_yaml = metadata["retrieved-constants"] for mod in modules: - qm = f"Q{mod // 4 + 1}M{mod % 4 + 1}" - agipd_corr.initialize_from_yaml(karabo_da, - const_yaml, mod) + agipd_corr.initialize_from_yaml(karabo_da, const_yaml, mod) data_shape = (n_images_max, 512, 128) agipd_corr.allocate_images(data_shape, n_cores_files) @@ -215,6 +228,7 @@ class AgipdCorrections: self.pulses_lst = list(range(*max_pulses)) \ if not (len(max_pulses) == 1 and max_pulses[0] == 0) else max_pulses # noqa self.max_cells = max_cells + self.gain_mode = gain_mode # Correction parameters self.baseline_corr_noise_threshold = -1000 @@ -286,7 +300,7 @@ class AgipdCorrections: data_dict = self.shared_dict[i_proc] data_dict['moduleIdx'][0] = module_idx try: - f = h5py.File(file_name, 'r') + f = h5py.File(file_name, "r") group = f[agipd_base]["image"] (_, first_index, last_index, @@ -315,8 +329,7 @@ class AgipdCorrections: data_dict['rawgain'][:n_img] = raw_data[:, 1] data_dict['cellId'][:n_img] = allcells[firange] data_dict['pulseId'][:n_img] = allpulses[firange] - data_dict['trainId'][:n_img] = np.squeeze( - group['trainId'][:][firange]) + data_dict['trainId'][:n_img] = np.squeeze(group['trainId'][:][firange]) # noqa except Exception as e: print(f'Error during reading data from file {file_name}: {e}') print(f'Error traceback: {traceback.format_exc()}') @@ -350,14 +363,14 @@ class AgipdCorrections: return trains = data_dict['trainId'][:n_img] - with h5py.File(ofile_name, 'w') as outfile: + with h5py.File(ofile_name, "w") as outfile: # Copy any other data from the input file. # This includes indexes, so it's important that the corrected data # we write is aligned with the raw data. - with h5py.File(file_name, 'r') as infile: - self.copy_and_sanitize_non_cal_data(infile, outfile, - agipd_base, - idx_base, trains) + with h5py.File(file_name, "r") as infile: + self.copy_and_sanitize_non_cal_data( + infile, outfile, agipd_base, idx_base, trains + ) # All corrected data goes in a /INSTRUMENT/.../image group image_grp = outfile[data_path] @@ -484,35 +497,35 @@ class AgipdCorrections: gain = self.shared_dict[i_proc]['gain'][first:last] cellid = self.shared_dict[i_proc]['cellId'][first:last] - # first evaluate the gain into 0, 1, 2 --> high, medium, low - t0 = self.thresholds[module_idx][0] - t1 = self.thresholds[module_idx][1] + if self.gain_mode is AgipdGainMode.ADAPTIVE_GAIN: + # first evaluate the gain into 0, 1, 2 --> high, medium, low + t0 = self.thresholds[module_idx][0] + t1 = self.thresholds[module_idx][1] - # load raw_data and rgain to be used during gain_correction, - # if requested - if self.corr_bools.get('melt_snow'): - self.shared_dict[i_proc]['t0_rgain'][first:last] = \ - rawgain / t0[cellid, ...] - self.shared_dict[i_proc]['raw_data'][first:last] = np.copy(data) - - # Often most pixels are in high-gain, so it's more efficient to - # set the whole output block to zero than select the right pixels. - gain[:] = 0 - # exceeding first threshold means data is medium or low gain - gain[rawgain > t0[cellid, ...]] = 1 - # exceeding also second threshold means data is low gain - gain[rawgain > t1[cellid, ...]] = 2 + if self.corr_bools.get("melt_snow"): + # load raw_data and rgain to be used during gain_correction + self.shared_dict[i_proc]["t0_rgain"][first:last] = rawgain / t0[cellid, ...] # noqa + self.shared_dict[i_proc]["raw_data"][first:last] = np.copy(data) # noqa + + # Often most pixels are in high-gain, so it's more efficient to + # set the whole output block to zero than select the right pixels. + gain[:] = 0 + # exceeding first threshold means data is medium or low gain + gain[rawgain > t0[cellid, ...]] = 1 + # exceeding also second threshold means data is low gain + gain[rawgain > t1[cellid, ...]] = 2 + else: + # the enum values map 1, 2, 3 to (fixed) gain modes + gain[:] = self.gain_mode - 1 offsetb = self.offset[module_idx][:, cellid] # force into high or medium gain if requested - if self.corr_bools.get('force_mg_if_below'): - gain[(gain == 2) & ( - (data - offsetb[1]) < self.mg_hard_threshold)] = 1 + if self.corr_bools.get("force_mg_if_below"): + gain[(gain == 2) & ((data - offsetb[1]) < self.mg_hard_threshold)] = 1 # noqa - if self.corr_bools.get('force_hg_if_below'): - gain[(gain > 0) & ( - (data - offsetb[0]) < self.hg_hard_threshold)] = 0 + if self.corr_bools.get("force_hg_if_below"): + gain[(gain > 0) & ((data - offsetb[0]) < self.hg_hard_threshold)] = 0 # noqa # choose constants according to gain setting off = calgs.gain_choose(gain, offsetb) @@ -545,8 +558,9 @@ class AgipdCorrections: cellid = self.shared_dict[i_proc]['cellId'][first:last] # output is saved in sharedmem to pass for correct_agipd() # as this function takes about 3 seconds. - self.shared_dict[i_proc]['msk'][first:last] = \ - calgs.gain_choose_int(gain, self.mask[module_idx][:, cellid]) + self.shared_dict[i_proc]["msk"][first:last] = calgs.gain_choose_int( + gain, self.mask[module_idx][:, cellid] + ) if hasattr(self, "rel_gain"): # Get the correct rel_gain depending on cell-id @@ -618,14 +632,12 @@ class AgipdCorrections: # if baseline correction was not requested # msk and rel_corr will still be empty shared_mem arrays if not any(self.blc_bools): - msk = calgs.gain_choose_int(gain, - self.mask[module_idx][:, cellid]) + msk = calgs.gain_choose_int(gain, self.mask[module_idx][:, cellid]) # same for relative gain and then bad pixel mask if hasattr(self, "rel_gain"): # Get the correct rel_gain depending on cell-id - rel_corr = calgs.gain_choose(gain, - self.rel_gain[module_idx][:, cellid]) # noqa + rel_corr = calgs.gain_choose(gain, self.rel_gain[module_idx][:, cellid]) # noqa # Correct for relative gain if self.corr_bools.get("rel_gain") and hasattr(self, "rel_gain"): @@ -688,11 +700,11 @@ class AgipdCorrections: # Copy the data across into the existing shared-memory array mask[...] = msk[...] - def get_valid_image_idx(self, idx_base: str, infile: str, - index_v: Optional[int] = 2): - """ Return the indices of valid data - """ - if index_v == 2: + def get_valid_image_idx( + self, idx_base: str, infile: str, raw_format_version: int = 2 + ): + """Return the indices of valid data""" + if raw_format_version == 2: count = np.squeeze(infile[idx_base + "image/count"]) first = np.squeeze(infile[idx_base + "image/first"]) if np.count_nonzero(count != 0) == 0: @@ -717,13 +729,16 @@ class AgipdCorrections: # Creating an array of validated indices. # If all indices were validated this array will be the same, # as what is stored at /DET/image/trainId - valid_indices = np.concatenate([np.arange(validf[i], - validf[i]+validc[i]) - for i in range(validf.size)], - axis=0) + valid_indices = np.concatenate( + [ + np.arange(validf[i], validf[i] + validc[i]) + for i in range(validf.size) + ], + axis=0, + ) valid_indices = np.squeeze(valid_indices).astype(np.int32) - elif index_v == 1: + elif raw_format_version == 1: status = np.squeeze(infile[idx_base + "image/status"]) if np.count_nonzero(status != 0) == 0: raise IOError(f"File {infile} has no valid counts") @@ -741,10 +756,9 @@ class AgipdCorrections: valid_indices = None else: raise AttributeError( - f"Not a known raw format version: {index_v}") + f"Not a known raw format version: {raw_format_version}") - return (valid, first_index, last_index, idxtrains, - valid_indices) + return (valid, first_index, last_index, idxtrains, valid_indices) def apply_selected_pulses(self, i_proc: int) -> int: """Select sharedmem data indices to correct based on selected @@ -950,13 +964,11 @@ class AgipdCorrections: # Extract parameters through identifying # unique trains, index and numbers. - uq, fidxv, cntsv = np.unique(trains, return_index=True, - return_counts=True) + uq, fidxv, cntsv = np.unique(trains, return_index=True, return_counts=True) # noqa # Validate calculated CORR INDEX contents by checking # difference between trainId stored in RAW data and trains from - train_diff = np.isin(np.array(infile["/INDEX/trainId"]), uq, - invert=True) + train_diff = np.isin(np.array(infile["/INDEX/trainId"]), uq, invert=True) # noqa # Insert zeros for missing trains. # fidxv and cntsv should have same length as @@ -1083,7 +1095,8 @@ class AgipdCorrections: self.offset[module_idx][...] = cons_data["Offset"].transpose()[...] self.noise[module_idx][...] = cons_data["Noise"].transpose()[...] - self.thresholds[module_idx][...] = cons_data["ThresholdsDark"].transpose()[:3, ...] # noqa + if self.gain_mode is AgipdGainMode.ADAPTIVE_GAIN: + self.thresholds[module_idx][...] = cons_data["ThresholdsDark"].transpose()[:3,...] # noqa if self.corr_bools.get("low_medium_gap"): t0 = self.thresholds[module_idx][0] @@ -1227,20 +1240,18 @@ class AgipdCorrections: return - def initialize_from_yaml(self, karabo_da: str, - const_yaml: Dict[str, Any], - module_idx: int - ) -> Dict[str, Any]: + def initialize_from_yaml( + self, karabo_da: str, const_yaml: Dict[str, Any], module_idx: int + ) -> Dict[str, Any]: """Initialize calibration constants from a yaml file - :param karabo-da: karabo data aggerator - :param const_yaml: (Dict) from the "retrieved-constants" part of a yaml - file in pre-notebook, which consists of metadata of either the constant + :param karabo_da: a karabo data aggregator + :param const_yaml: from the "retrieved-constants" part of a yaml file + from pre-notebook, which consists of metadata of either the constant file path or the empty constant shape, and the creation-time of the retrieved constants - :param module_idx: Index of module. - :return when: Dictionary of retrieved constants with - their creation-time. + :param module_idx: Index of module + :return when: dict of retrieved constants with their creation-time """ # string of the device name. @@ -1250,10 +1261,6 @@ class AgipdCorrections: for cname, mdata in const_yaml[karabo_da]["constants"].items(): when[cname] = mdata["creation-time"] if when[cname]: - # This path is only used when testing new flat fields from - # file during development: it takes ages to test using all - # cells. Consequently, the shape needs to be fixed when less - # cells are used. with h5py.File(mdata["file-path"], "r") as cf: cons_data[cname] = np.copy(cf[f"{db_module}/{cname}/0/data"]) # noqa else: @@ -1324,15 +1331,21 @@ class AgipdCorrections: """ - const_dict = \ - assemble_constant_dict(self.corr_bools, self.pc_bools, - memory_cells, bias_voltage, gain_setting, - acquisition_rate, photon_energy, - beam_energy=None, only_dark=only_dark) - - cons_data, when = \ - self.retrieve_constant_and_time(karabo_id, karabo_da, const_dict, - cal_db_interface, creation_time) + const_dict = assemble_constant_dict( + self.corr_bools, + self.pc_bools, + memory_cells, + bias_voltage, + gain_setting, + acquisition_rate, + photon_energy, + beam_energy=None, + only_dark=only_dark, + ) + + cons_data, when = self.retrieve_constant_and_time( + karabo_id, karabo_da, const_dict, cal_db_interface, creation_time + ) self.init_constants(cons_data, when, module_idx) @@ -1346,23 +1359,17 @@ class AgipdCorrections: :param constant_shape: Shape of expected constants (gain, cells, x, y) """ for module_idx in modules: - self.offset[module_idx] = sharedmem.empty(constant_shape, - dtype='f4') - self.thresholds[module_idx] = sharedmem.empty(constant_shape, - dtype='f4') - self.noise[module_idx] = sharedmem.empty(constant_shape, - dtype='f4') - - self.md_additional_offset[module_idx] = sharedmem.empty( - constant_shape[1:], dtype='f4') - self.rel_gain[module_idx] = sharedmem.empty(constant_shape, - dtype='f4') - self.frac_high_med[module_idx] = sharedmem.empty(constant_shape[1], - dtype='f4') - - self.mask[module_idx] = sharedmem.empty(constant_shape, dtype='i4') - self.xray_cor[module_idx] = sharedmem.empty(constant_shape[1:], - dtype='f4') + self.offset[module_idx] = sharedmem.empty(constant_shape, dtype="f4") # noqa + if self.gain_mode is AgipdGainMode.ADAPTIVE_GAIN: + self.thresholds[module_idx] = sharedmem.empty(constant_shape, dtype="f4") # noqa + self.noise[module_idx] = sharedmem.empty(constant_shape, dtype="f4") # noqa + + self.md_additional_offset[module_idx] = sharedmem.empty(constant_shape[1:], dtype="f4") # noqa + self.rel_gain[module_idx] = sharedmem.empty(constant_shape, dtype="f4") # noqa + self.frac_high_med[module_idx] = sharedmem.empty(constant_shape[1], dtype="f4") # noqa + + self.mask[module_idx] = sharedmem.empty(constant_shape, dtype="i4") + self.xray_cor[module_idx] = sharedmem.empty(constant_shape[1:], dtype="f4") # noqa def allocate_images(self, shape, n_cores_files): """ @@ -1376,26 +1383,18 @@ class AgipdCorrections: self.shared_dict = [] for i in range(n_cores_files): self.shared_dict.append({}) - self.shared_dict[i]['cellId'] = sharedmem.empty(shape[0], - dtype='u2') - self.shared_dict[i]['pulseId'] = sharedmem.empty(shape[0], - dtype='u8') - self.shared_dict[i]['trainId'] = sharedmem.empty(shape[0], - dtype='u8') - self.shared_dict[i]['moduleIdx'] = sharedmem.empty(1, dtype='i4') - self.shared_dict[i]['nImg'] = sharedmem.empty(1, dtype='i4') - self.shared_dict[i]['mask'] = sharedmem.empty(shape, dtype='u4') - self.shared_dict[i]['data'] = sharedmem.empty(shape, dtype='f4') - self.shared_dict[i]['rawgain'] = sharedmem.empty(shape, - dtype='u2') - self.shared_dict[i]['gain'] = sharedmem.empty(shape, dtype='u1') - self.shared_dict[i]['blShift'] = sharedmem.empty(shape[0], - dtype='f4') + self.shared_dict[i]["cellId"] = sharedmem.empty(shape[0], dtype="u2") # noqa + self.shared_dict[i]["pulseId"] = sharedmem.empty(shape[0], dtype="u8") # noqa + self.shared_dict[i]["trainId"] = sharedmem.empty(shape[0], dtype="u8") # noqa + self.shared_dict[i]["moduleIdx"] = sharedmem.empty(1, dtype="i4") + self.shared_dict[i]["nImg"] = sharedmem.empty(1, dtype="i4") + self.shared_dict[i]["mask"] = sharedmem.empty(shape, dtype="u4") + self.shared_dict[i]["data"] = sharedmem.empty(shape, dtype="f4") + self.shared_dict[i]["rawgain"] = sharedmem.empty(shape, dtype="u2") + self.shared_dict[i]["gain"] = sharedmem.empty(shape, dtype="u1") + self.shared_dict[i]["blShift"] = sharedmem.empty(shape[0], dtype="f4") # noqa # Parameters shared between image-wise correction functions - self.shared_dict[i]['msk'] = sharedmem.empty(shape, dtype='i4') - self.shared_dict[i]['raw_data'] = sharedmem.empty(shape, - dtype='f4') - self.shared_dict[i]['rel_corr'] = sharedmem.empty(shape, - dtype='f4') - self.shared_dict[i]['t0_rgain'] = sharedmem.empty(shape, - dtype='u2') + self.shared_dict[i]["msk"] = sharedmem.empty(shape, dtype="i4") + self.shared_dict[i]["raw_data"] = sharedmem.empty(shape, dtype="f4") # noqa + self.shared_dict[i]["rel_corr"] = sharedmem.empty(shape, dtype="f4") # noqa + self.shared_dict[i]["t0_rgain"] = sharedmem.empty(shape, dtype="u2") # noqa diff --git a/cal_tools/cal_tools/agipdutils.py b/cal_tools/cal_tools/agipdutils.py index 9d58ccf83ba9cc692a043c64c562d78f1c1afb99..44cac4c87b1a0ffc8c8ba4628e38d1f68056672e 100644 --- a/cal_tools/cal_tools/agipdutils.py +++ b/cal_tools/cal_tools/agipdutils.py @@ -2,18 +2,27 @@ import copy from typing import Tuple import numpy as np -from cal_tools.enums import BadPixels, SnowResolution -from scipy.signal import cwt, ricker +from cal_tools.enums import AgipdGainMode, BadPixels, SnowResolution +from scipy.signal import cwt, find_peaks_cwt, ricker from sklearn.mixture import GaussianMixture from sklearn.preprocessing import StandardScaler -def assemble_constant_dict(corr_bools, pc_bools, memory_cells, bias_voltage, - gain_setting, acquisition_rate, - photon_energy, beam_energy=None, only_dark=False): +def assemble_constant_dict( + corr_bools, + pc_bools, + memory_cells, + bias_voltage, + gain_setting, + acquisition_rate, + photon_energy, + beam_energy=None, + only_dark=False, + gain_mode=AgipdGainMode.ADAPTIVE_GAIN, +): """ Assemble a dictionary with the iCalibrationDB constant names and - the operating conditions for retrieveing the required constants + the operating conditions for retrieving the required constants for correction. :param corr_bools: (Dict) A dict of booleans for applying @@ -23,22 +32,25 @@ def assemble_constant_dict(corr_bools, pc_bools, memory_cells, bias_voltage, :param bias_voltage: (Int) Bias Voltage :param gain_setting: (Float) Gain setting :param acquisition_rate: (Float) Acquisition rate - :param photon_energy: (Float) Photong energy + :param photon_energy: (Float) Photon energy :param beam_energy: (Float) Beam Energy - :param only_dark: (Bool) Indicating a retrieval for dark - constants only from db - :return: const_dict: (Dict) An assembeld dictionary that can be used + :param only_dark: (Bool) Indicating a retrieval for dark constants only from db + :param gain_mode: Operation mode of the detector (default to adaptive gain) + :return: const_dict: (Dict) An assembled dictionary that can be used to retrieve the required constants """ darkcond = [ - "Dark", { + "Dark", + { "memory_cells": memory_cells, "bias_voltage": bias_voltage, "acquisition_rate": acquisition_rate, "gain_setting": gain_setting, + "gain_mode": gain_mode, "pixels_x": 512, - "pixels_y": 128, } + "pixels_y": 128, + }, ] const_dict = { "Offset": ["zeros", (128, 512, memory_cells, 3), darkcond], @@ -47,28 +59,21 @@ def assemble_constant_dict(corr_bools, pc_bools, memory_cells, bias_voltage, "BadPixelsDark": ["zeros", (128, 512, memory_cells, 3), darkcond], } - if not (corr_bools.get('only_offset') or only_dark): - + if not (corr_bools.get("only_offset") or only_dark): if any(pc_bools): - const_dict["BadPixelsPC"] = \ - ["zeros", (memory_cells, 128, 512), darkcond] - const_dict["SlopesPC"] = \ - ["ones", (128, 512, memory_cells, 10), darkcond] + const_dict["BadPixelsPC"] = ["zeros", (memory_cells, 128, 512), darkcond] + const_dict["SlopesPC"] = ["ones", (128, 512, memory_cells, 10), darkcond] if corr_bools.get("xray_corr"): # Add illuminated conditions illumcond = [ - "Illuminated", { - "beam_energy": beam_energy, - "photon_energy": photon_energy - } + "Illuminated", + {"beam_energy": beam_energy, "photon_energy": photon_energy}, ] illumcond[1].update(darkcond[1]) - const_dict["BadPixelsFF"] = ["zeros", (128, 512, memory_cells), - illumcond] - const_dict["SlopesFF"] = ["ones", (128, 512, memory_cells, 2), - illumcond] + const_dict["BadPixelsFF"] = ["zeros", (128, 512, memory_cells), illumcond] + const_dict["SlopesFF"] = ["ones", (128, 512, memory_cells, 2), illumcond] return const_dict diff --git a/cal_tools/cal_tools/enums.py b/cal_tools/cal_tools/enums.py index 4abafdc10b646d7d81c92de9912f8b29f27f1e13..21d4b4f08eee3d8c95c2fbb09fe42dc177d1bff4 100644 --- a/cal_tools/cal_tools/enums.py +++ b/cal_tools/cal_tools/enums.py @@ -1,4 +1,4 @@ -from enum import Enum, IntFlag +from enum import Enum, IntEnum, IntFlag class BadPixels(IntFlag): @@ -48,3 +48,12 @@ class SnowResolution(Enum): """ NONE = "none" INTERPOLATE = "interpolate" + + +class AgipdGainMode(IntEnum): + """Gain Modes where the values is 0 if Adaptive, or High/Medium/Low.""" + + ADAPTIVE_GAIN = 0 + FIXED_HIGH_GAIN = 1 + FIXED_MEDIUM_GAIN = 2 + FIXED_LOW_GAIN = 3 diff --git a/cal_tools/cal_tools/tools.py b/cal_tools/cal_tools/tools.py index d4015a558d56e6ea3a2daa5d24ba9fb104378b70..7572e7d18911d92abfba522c31dcf8f453745461 100644 --- a/cal_tools/cal_tools/tools.py +++ b/cal_tools/cal_tools/tools.py @@ -78,7 +78,7 @@ def map_modules_from_folder(in_folder, run, path_template, karabo_da, sequences_qm = {} for inset in karabo_da: module_idx = int(inset[-2:]) - name = f"Q{module_idx // 4 + 1}M{module_idx % 4 + 1}" + name = module_index_to_qm(module_idx) module_files[name] = Queue() sequences_qm[name] = 0 mod_ids[name] = module_idx @@ -539,8 +539,7 @@ def get_from_db(karabo_id: str, karabo_da: str, if ntries > 0: if load_data and meta_only: mdata_const = metadata.calibration_constant_version - fpath = Path(mdata_const.hdf5path, - mdata_const.filename) + fpath = Path(mdata_const.hdf5path, mdata_const.filename) with h5py.File(fpath, "r") as f: arr = f[f"{mdata_const.h5path}/data"][()] metadata.calibration_constant.data = arr @@ -673,6 +672,14 @@ def get_constant_from_db_and_time(karabo_id: str, karabo_da: str, return data, None +def module_index_to_qm(index: int, total_modules: int = 16): + """Maps module index (0-indexed) to quadrant + module string (1-indexed)""" + assert index < total_modules, f'{index} is greater than {total_modules}' + modules_per_quad = total_modules // 4 + quad, mod = divmod(index, modules_per_quad) + return f"Q{quad+1}M{mod+1}" + + class CalibrationMetadata(dict): """Convenience class: dictionary stored in metadata YAML file diff --git a/notebooks/AGIPD/AGIPD_Correct_and_Verify.ipynb b/notebooks/AGIPD/AGIPD_Correct_and_Verify.ipynb index c4cbf1ef2759f6ade29cb6f4861bca470707e085..27de082393d224644b97335bc69b1c0ca92266a4 100644 --- a/notebooks/AGIPD/AGIPD_Correct_and_Verify.ipynb +++ b/notebooks/AGIPD/AGIPD_Correct_and_Verify.ipynb @@ -14,12 +14,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-21T11:30:06.730220Z", - "start_time": "2019-02-21T11:30:06.658286Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "in_folder = \"/gpfs/exfel/exp/HED/202031/p900174/raw\" # the folder to read data from, required\n", @@ -51,7 +46,7 @@ "gain_setting = 0.1 # the gain setting, use 0.1 to try to auto-determine\n", "photon_energy = 9.2 # photon energy in keV\n", "overwrite = True # set to True if existing data should be overwritten\n", - "max_pulses = [0, 500, 1] # range list [st, end, step] of maximum pulse indices within a train. 3 allowed maximum list input elements. \n", + "max_pulses = [0, 500, 1] # range list [st, end, step] of maximum pulse indices within a train. 3 allowed maximum list input elements.\n", "mem_cells_db = 0 # set to a value different than 0 to use this value for DB queries\n", "cell_id_preview = 1 # cell Id used for preview in single-shot plots\n", "\n", @@ -71,7 +66,7 @@ "xray_gain = False # do relative gain correction based on xray data\n", "blc_noise = False # if set, baseline correction via noise peak location is attempted\n", "blc_stripes = False # if set, baseline corrected via stripes\n", - "blc_hmatch = False # if set, base line correction via histogram matching is attempted \n", + "blc_hmatch = False # if set, base line correction via histogram matching is attempted\n", "match_asics = False # if set, inner ASIC borders are matched to the same signal level\n", "adjust_mg_baseline = False # adjust medium gain baseline to match highest high gain value\n", "zero_nans = False # set NaN values in corrected data to 0\n", @@ -104,21 +99,19 @@ "metadata": {}, "outputs": [], "source": [ - "import copy\n", - "import gc\n", "import itertools\n", "import math\n", + "import multiprocessing\n", "import re\n", "import traceback\n", "import warnings\n", "from datetime import timedelta\n", - "from multiprocessing import Pool\n", "from pathlib import Path\n", - "from time import perf_counter, sleep, time\n", + "from time import perf_counter\n", "\n", "import tabulate\n", "from dateutil import parser\n", - "from IPython.display import HTML, Latex, Markdown, display\n", + "from IPython.display import Latex, Markdown, display\n", "\n", "warnings.filterwarnings('ignore')\n", "import matplotlib\n", @@ -126,11 +119,8 @@ "import yaml\n", "from extra_data import RunDirectory, stack_detector_data\n", "from extra_geom import AGIPD_1MGeometry, AGIPD_500K2GGeometry\n", - "from iCalibrationDB import Detectors\n", "from matplotlib import cm as colormap\n", "from matplotlib.colors import LogNorm\n", - "from matplotlib.ticker import FormatStrFormatter, LinearLocator\n", - "from mpl_toolkits.mplot3d import Axes3D\n", "\n", "matplotlib.use(\"agg\")\n", "%matplotlib inline\n", @@ -141,22 +131,19 @@ "sns.set_context(\"paper\", font_scale=1.4)\n", "sns.set_style(\"ticks\")\n", "\n", + "import cal_tools\n", "import seaborn as sns\n", "from cal_tools.agipdlib import (\n", " AgipdCorrections,\n", " get_acq_rate,\n", + " get_gain_mode,\n", " get_gain_setting,\n", " get_num_cells,\n", ")\n", "from cal_tools.ana_tools import get_range\n", "from cal_tools.cython import agipdalgs as calgs\n", - "from cal_tools.enums import BadPixels\n", + "from cal_tools.enums import AgipdGainMode, BadPixels\n", "from cal_tools.step_timing import StepTimer\n", - "from cal_tools.tools import (\n", - " CalibrationMetadata,\n", - " get_dir_creation_date,\n", - " map_modules_from_folder,\n", - ")\n", "\n", "sns.set()\n", "sns.set_context(\"paper\", font_scale=1.4)\n", @@ -188,7 +175,7 @@ "source": [ "# Fill dictionaries comprising bools and arguments for correction and data analysis\n", "\n", - "# Here the herarichy and dependability for correction booleans are defined\n", + "# Here the hierarchy and dependability for correction booleans are defined\n", "corr_bools = {}\n", "\n", "# offset is at the bottom of AGIPD correction pyramid.\n", @@ -213,8 +200,17 @@ " corr_bools[\"common_mode\"] = common_mode\n", " corr_bools[\"melt_snow\"] = melt_snow\n", " corr_bools[\"mask_zero_std\"] = mask_zero_std\n", - " corr_bools[\"low_medium_gap\"] = low_medium_gap \n", - " " + " corr_bools[\"low_medium_gap\"] = low_medium_gap\n", + "\n", + "# Many corrections don't apply to fixed gain mode; will explicitly disable later if detected\n", + "disable_for_fixed_gain = [\n", + " \"adjust_mg_baseline\",\n", + " \"blc_set_min\",\n", + " \"force_hg_if_below\",\n", + " \"force_mg_if_below\",\n", + " \"low_medium_gap\",\n", + " \"melt_snow\"\n", + "]" ] }, { @@ -262,12 +258,8 @@ " karabo_da = [\"AGIPD{:02d}\".format(i) for i in modules]\n", "else:\n", " modules = [int(x[-2:]) for x in karabo_da]\n", - " \n", - "def mod_name(modno):\n", - " return f\"Q{modno // 4 + 1}M{modno % 4 + 1}\"\n", "\n", - "print(\"Process modules: \", ', '.join(\n", - " [mod_name(x) for x in modules]))\n", + "print(\"Process modules:\", ', '.join(cal_tools.tools.module_index_to_qm(x) for x in modules))\n", "print(f\"Detector in use is {karabo_id}\")\n", "print(f\"Instrument {instrument}\")\n", "print(f\"Detector instance {dinstance}\")" @@ -280,17 +272,17 @@ "outputs": [], "source": [ "# Display Information about the selected pulses indices for correction.\n", - "pulses_lst = list(range(*max_pulses)) if not (len(max_pulses)==1 and max_pulses[0]==0) else max_pulses \n", + "pulses_lst = list(range(*max_pulses)) if not (len(max_pulses)==1 and max_pulses[0]==0) else max_pulses\n", "\n", "try:\n", - " if len(pulses_lst) > 1: \n", + " if len(pulses_lst) > 1:\n", " print(\"A range of {} pulse indices is selected: from {} to {} with a step of {}\"\n", " .format(len(pulses_lst), pulses_lst[0] , pulses_lst[-1] + (pulses_lst[1] - pulses_lst[0]),\n", " pulses_lst[1] - pulses_lst[0]))\n", " else:\n", - " print(\"one pulse is selected: a pulse of idx {}\".format(pulses_lst[0]))\n", + " print(f\"one pulse is selected: a pulse of idx {pulses_lst[0]}\")\n", "except Exception as e:\n", - " raise ValueError('max_pulses input Error: {}'.format(e))" + " raise ValueError(f\"max_pulses input Error: {e}\")" ] }, { @@ -300,9 +292,9 @@ "outputs": [], "source": [ "# set everything up filewise\n", - "mmf = map_modules_from_folder(str(in_folder), run, path_template,\n", - " karabo_da, sequences)\n", - "mapped_files, mod_ids, total_sequences, sequences_qm, _ = mmf\n", + "mapped_files, _, total_sequences, _, _ = cal_tools.tools.map_modules_from_folder(\n", + " str(in_folder), run, path_template, karabo_da, sequences\n", + ")\n", "file_list = []\n", "\n", "# ToDo: Split table over pages\n", @@ -357,12 +349,11 @@ "# Evaluate creation time\n", "creation_time = None\n", "if use_dir_creation_date:\n", - " creation_time = get_dir_creation_date(str(in_folder), run)\n", + " creation_time = cal_tools.tools.get_dir_creation_date(str(in_folder), run)\n", " offset = parser.parse(creation_date_offset)\n", - " delta = timedelta(hours=offset.hour,\n", - " minutes=offset.minute, seconds=offset.second)\n", + " delta = timedelta(hours=offset.hour, minutes=offset.minute, seconds=offset.second)\n", " creation_time += delta\n", - " \n", + "\n", "# Evaluate gain setting\n", "if gain_setting == 0.1:\n", " if creation_time.replace(tzinfo=None) < parser.parse('2020-01-31'):\n", @@ -376,7 +367,9 @@ " print(e)\n", " print(\"Set gain setting to 0\")\n", " gain_setting = 0\n", - " " + "\n", + "# Evaluate gain mode (operation mode)\n", + "gain_mode = get_gain_mode(control_fn, h5path_ctrl)" ] }, { @@ -386,8 +379,26 @@ "outputs": [], "source": [ "print(f\"Using {creation_time} as creation time\")\n", - "print(f\"Operating conditions are:\\n• Bias voltage: {bias_voltage}\\n• Memory cells: {mem_cells_db}\\n\"\n", - " f\"• Acquisition rate: {acq_rate}\\n• Gain setting: {gain_setting}\\n• Photon Energy: {photon_energy}\\n\")" + "print(\"Operating conditions are:\")\n", + "print(f\"• Bias voltage: {bias_voltage}\")\n", + "print(f\"• Memory cells: {mem_cells_db}\")\n", + "print(f\"• Acquisition rate: {acq_rate}\")\n", + "print(f\"• Gain setting: {gain_setting}\")\n", + "print(f\"• Gain mode: {gain_mode.name}\")\n", + "print(f\"• Photon Energy: {photon_energy}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if gain_mode:\n", + " for to_disable in disable_for_fixed_gain:\n", + " if corr_bools.get(to_disable, False):\n", + " print(f\"Warning: {to_disable} correction was requested, but does not apply to fixed gain mode\")\n", + " corr_bools[to_disable] = False" ] }, { @@ -403,10 +414,14 @@ "metadata": {}, "outputs": [], "source": [ - "agipd_corr = AgipdCorrections(max_cells, max_pulses,\n", - " h5_data_path=h5path,\n", - " h5_index_path=h5path_idx,\n", - " corr_bools=corr_bools)\n", + "agipd_corr = AgipdCorrections(\n", + " max_cells,\n", + " max_pulses,\n", + " h5_data_path=h5path,\n", + " h5_index_path=h5path_idx,\n", + " corr_bools=corr_bools,\n", + " gain_mode=gain_mode\n", + ")\n", "\n", "agipd_corr.baseline_corr_noise_threshold = -blc_noise_threshold\n", "agipd_corr.hg_hard_threshold = hg_hard_threshold\n", @@ -420,6 +435,15 @@ "agipd_corr.ff_gain = ff_gain" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "module_index_to_karabo_da = {mod: da for (mod, da) in zip(modules, karabo_da)}" + ] + }, { "cell_type": "code", "execution_count": null, @@ -429,28 +453,37 @@ "# Retrieve calibration constants to RAM\n", "agipd_corr.allocate_constants(modules, (3, mem_cells_db, 512, 128))\n", "\n", - "metadata = CalibrationMetadata(out_folder)\n", + "metadata = cal_tools.tools.CalibrationMetadata(out_folder)\n", "# NOTE: this notebook will not overwrite calibration metadata file\n", "const_yaml = metadata.get(\"retrieved-constants\", {})\n", "\n", - "# retrive constants\n", "def retrieve_constants(mod):\n", " \"\"\"\n", " Retrieve calibration constants and load them to shared memory\n", - " \n", + "\n", " Metadata for constants is taken from yml file or retrieved from the DB\n", " \"\"\"\n", - " err = ''\n", - " # TODO: parallelize over modules\n", - " k_da = karabo_da[mod]\n", + " err = \"\"\n", + " k_da = module_index_to_karabo_da[mod]\n", " try:\n", " # check if there is a yaml file in out_folder that has the device constants.\n", " if k_da in const_yaml:\n", " when = agipd_corr.initialize_from_yaml(k_da, const_yaml, mod)\n", " else:\n", - " # TODO: should we save what is found here in metadata?\n", - " when = agipd_corr.initialize_from_db(karabo_id, k_da, cal_db_interface, creation_time, mem_cells_db, bias_voltage,\n", - " photon_energy, gain_setting, acq_rate, mod, False)\n", + " # TODO: replace with proper retrieval (as done in pre-correction)\n", + " when = agipd_corr.initialize_from_db(\n", + " karabo_id,\n", + " k_da,\n", + " cal_db_interface,\n", + " creation_time,\n", + " mem_cells_db,\n", + " bias_voltage,\n", + " photon_energy,\n", + " gain_setting,\n", + " acq_rate,\n", + " mod,\n", + " False,\n", + " )\n", " except Exception as e:\n", " err = f\"Error: {e}\\nError traceback: {traceback.format_exc()}\"\n", " when = None\n", @@ -458,7 +491,7 @@ "\n", "\n", "ts = perf_counter()\n", - "with Pool(processes=len(modules)) as pool:\n", + "with multiprocessing.Pool(processes=len(modules)) as pool:\n", " const_out = pool.map(retrieve_constants, modules)\n", "print(f\"Constants were loaded in {perf_counter()-ts:.01f}s\")" ] @@ -470,7 +503,7 @@ "outputs": [], "source": [ "# allocate memory for images and hists\n", - "n_images_max = max_cells*256\n", + "n_images_max = max_cells * 256\n", "data_shape = (n_images_max, 512, 128)\n", "agipd_corr.allocate_images(data_shape, n_cores_files)" ] @@ -497,7 +530,7 @@ "source": [ "def imagewise_chunks(img_counts):\n", " \"\"\"Break up the loaded data into chunks of up to chunk_size\n", - " \n", + "\n", " Yields (file data slot, start index, stop index)\n", " \"\"\"\n", " for i_proc, n_img in enumerate(img_counts):\n", @@ -518,17 +551,13 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": true - }, + "metadata": {}, "outputs": [], "source": [ - "with Pool() as pool:\n", + "with multiprocessing.Pool() as pool:\n", " for file_batch in batches(file_list, n_cores_files):\n", " # TODO: Move some printed output to logging or similar\n", - " print(f\"Processing next {len(file_batch)} files:\")\n", - " for file_name in file_batch:\n", - " print(\" \", file_name)\n", + " print(f\"Processing next {len(file_batch)} files\")\n", " step_timer.start()\n", " img_counts = pool.starmap(agipd_corr.read_file, zip(range(len(file_batch)), file_batch,\n", " [not common_mode]*len(file_batch)))\n", @@ -549,7 +578,7 @@ " # Perform image-wise correction\n", " pool.starmap(agipd_corr.baseline_correction, imagewise_chunks(img_counts))\n", " step_timer.done_step(\"Base-line shift correction\")\n", - " \n", + "\n", " if common_mode:\n", " # Perform cross-file correction parallel over asics\n", " pool.starmap(agipd_corr.cm_correction, itertools.product(\n", @@ -587,9 +616,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": true - }, + "metadata": {}, "outputs": [], "source": [ "# if the yml file contains \"retrieved-constants\", that means a leading\n", @@ -598,7 +625,7 @@ "timestamps = {}\n", "\n", "for i, (error, modno, when, k_da) in enumerate(const_out):\n", - " qm = mod_name(modno)\n", + " qm = cal_tools.tools.module_index_to_qm(modno)\n", " # expose errors while applying correction\n", " if error:\n", " print(\"Error: {}\".format(error) )\n", @@ -607,7 +634,7 @@ " if fst_print:\n", " print(\"Constants are retrieved with creation time: \")\n", " fst_print = False\n", - " \n", + "\n", " module_timestamps = {}\n", "\n", " # If correction is crashed\n", @@ -655,8 +682,7 @@ " Z = data.T\n", "\n", " # Plot the surface.\n", - " surf = ax.plot_surface(X, Y, Z, cmap=colormap.coolwarm,\n", - " linewidth=0, antialiased=False)\n", + " ax.plot_surface(X, Y, Z, cmap=colormap.coolwarm, linewidth=0, antialiased=False)\n", " ax.set_xlabel(x_axis)\n", " ax.set_ylabel(y_axis)\n", " ax.set_zlabel(\"Counts\")\n", @@ -683,7 +709,7 @@ "source": [ "def get_trains_data(run_folder, source, include, detector_id, tid=None, modules=16, fillvalue=np.nan):\n", " \"\"\"Load single train for all module\n", - " \n", + "\n", " :param run_folder: Path to folder with data\n", " :param source: Data source to be loaded\n", " :param include: Inset of file name to be considered\n", @@ -696,7 +722,7 @@ " tid, data = run_data.select(f'{detector_id}/DET/*', source).train_from_id(tid)\n", " else:\n", " tid, data = next(iter(run_data.select(f'{detector_id}/DET/*', source).trains(require_all=True)))\n", - " \n", + "\n", " return tid, stack_detector_data(train=data, data=source, fillvalue=fillvalue, modules=modules)" ] }, @@ -794,7 +820,7 @@ "print(f\"Gain statistics in %\")\n", "table = [[f'{gains[gains==0].size/gains.size*100:.02f}',\n", " f'{gains[gains==1].size/gains.size*100:.03f}',\n", - " f'{gains[gains==2].size/gains.size*100:.03f}']] \n", + " f'{gains[gains==2].size/gains.size*100:.03f}']]\n", "md = display(Latex(tabulate.tabulate(table, tablefmt='latex',\n", " headers=[\"High\", \"Medium\", \"Low\"])))" ] @@ -809,9 +835,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "pulse_range = [np.min(pulseId[pulseId>=0]), np.max(pulseId[pulseId>=0])]\n", @@ -885,12 +909,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:33.226396Z", - "start_time": "2019-02-18T17:29:27.027758Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", @@ -926,12 +945,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:33.761015Z", - "start_time": "2019-02-18T17:29:33.227922Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", @@ -944,24 +958,19 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:35.903487Z", - "start_time": "2019-02-18T17:29:33.762568Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", "ax = fig.add_subplot(111)\n", "vmin, vmax = get_range(corrected[cell_id_preview], 5, -50)\n", "nbins = np.int((vmax + 50) / 2)\n", - "h = ax.hist(corrected[cell_id_preview].flatten(), \n", - " bins=nbins, range=(-50, vmax), \n", + "h = ax.hist(corrected[cell_id_preview].flatten(),\n", + " bins=nbins, range=(-50, vmax),\n", " histtype='stepfilled', log=True)\n", - "_ = plt.xlabel('[ADU]')\n", - "_ = plt.ylabel('Counts')\n", - "_ = ax.grid()" + "plt.xlabel('[ADU]')\n", + "plt.ylabel('Counts')\n", + "ax.grid()" ] }, { @@ -971,18 +980,13 @@ "outputs": [], "source": [ "display(Markdown('### Mean CORRECTED Preview ###\\n'))\n", - "display(Markdown(f'A mean across one train \\n'))" + "display(Markdown(f'A mean across one train\\n'))" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:39.369686Z", - "start_time": "2019-02-18T17:29:35.905152Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", @@ -995,12 +999,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:49.217848Z", - "start_time": "2019-02-18T17:29:39.371232Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", @@ -1012,16 +1011,16 @@ "nbins = np.int((vmax + 100) / 5)\n", "h = ax.hist(corrected.flatten(), bins=nbins,\n", " range=(-100, vmax), histtype='step', log=True, label = 'All')\n", - "_ = ax.hist(corrected[gains == 0].flatten(), bins=nbins, range=(-100, vmax),\n", - " alpha=0.5, log=True, label='High gain', color='green')\n", - "_ = ax.hist(corrected[gains == 1].flatten(), bins=nbins, range=(-100, vmax),\n", - " alpha=0.5, log=True, label='Medium gain', color='red')\n", - "_ = ax.hist(corrected[gains == 2].flatten(), bins=nbins,\n", - " range=(-100, vmax), alpha=0.5, log=True, label='Low gain', color='yellow')\n", - "_ = ax.legend()\n", - "_ = ax.grid()\n", - "_ = plt.xlabel('[ADU]')\n", - "_ = plt.ylabel('Counts')" + "ax.hist(corrected[gains == 0].flatten(), bins=nbins, range=(-100, vmax),\n", + " alpha=0.5, log=True, label='High gain', color='green')\n", + "ax.hist(corrected[gains == 1].flatten(), bins=nbins, range=(-100, vmax),\n", + " alpha=0.5, log=True, label='Medium gain', color='red')\n", + "ax.hist(corrected[gains == 2].flatten(), bins=nbins, range=(-100, vmax),\n", + " alpha=0.5, log=True, label='Low gain', color='yellow')\n", + "ax.legend()\n", + "ax.grid()\n", + "plt.xlabel('[ADU]')\n", + "plt.ylabel('Counts')" ] }, { @@ -1037,12 +1036,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:49.641675Z", - "start_time": "2019-02-18T17:29:49.224167Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", @@ -1053,9 +1047,7 @@ }, { "cell_type": "markdown", - "metadata": { - "collapsed": true - }, + "metadata": {}, "source": [ "## Bad Pixels ##\n", "The mask contains dedicated entries for all pixels and memory cells as well as all three gains stages. Each mask entry is encoded in 32 bits as:" @@ -1064,12 +1056,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:49.651913Z", - "start_time": "2019-02-18T17:29:49.643556Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "table = []\n", @@ -1092,24 +1079,17 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:50.086169Z", - "start_time": "2019-02-18T17:29:49.653391Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", "ax = fig.add_subplot(111)\n", - "ax = geom.plot_data_fast(np.log2(mask[cell_id_preview]), ax=ax, vmin=0, vmax=32, cmap=\"jet\")" + "geom.plot_data_fast(np.log2(mask[cell_id_preview]), ax=ax, vmin=0, vmax=32, cmap=\"jet\")" ] }, { "cell_type": "markdown", - "metadata": { - "collapsed": true - }, + "metadata": {}, "source": [ "### Percentage of Bad Pixels across one train ###" ] @@ -1117,18 +1097,12 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:51.686562Z", - "start_time": "2019-02-18T17:29:50.088883Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", "ax = fig.add_subplot(111)\n", - "ax = geom.plot_data_fast(np.mean(mask>0, axis=0),\n", - " vmin=0, ax=ax, vmax=1, cmap=\"jet\")" + "geom.plot_data_fast(np.mean(mask>0, axis=0), vmin=0, ax=ax, vmax=1, cmap=\"jet\")" ] }, { @@ -1141,12 +1115,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-18T17:29:55.483270Z", - "start_time": "2019-02-18T17:29:53.664226Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(20, 10))\n", diff --git a/notebooks/AGIPD/AGIPD_Retrieve_Constants_Precorrection.ipynb b/notebooks/AGIPD/AGIPD_Retrieve_Constants_Precorrection.ipynb index 8126a0a01a063d298eb2d950f61978d08f923d4f..b92a901a9c82422651601445255b6ab2df4b0d96 100644 --- a/notebooks/AGIPD/AGIPD_Retrieve_Constants_Precorrection.ipynb +++ b/notebooks/AGIPD/AGIPD_Retrieve_Constants_Precorrection.ipynb @@ -14,15 +14,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-21T11:30:06.730220Z", - "start_time": "2019-02-21T11:30:06.658286Z" - } - }, + "metadata": {}, "outputs": [], "source": [ - "cluster_profile = \"noDB\"\n", "in_folder = \"/gpfs/exfel/exp/SPB/202030/p900119/raw\" # the folder to read data from, required\n", "out_folder = \"/gpfs/exfel/data/scratch/ahmedk/test/AGIPD_\" # the folder to output to, required\n", "sequences = [-1] # sequences to correct, set to -1 for all, range allowed\n", @@ -57,7 +51,7 @@ "xray_gain = True # do relative gain correction based on xray data\n", "blc_noise = False # if set, baseline correction via noise peak location is attempted\n", "blc_stripes = False # if set, baseline corrected via stripes\n", - "blc_hmatch = False # if set, base line correction via histogram matching is attempted \n", + "blc_hmatch = False # if set, base line correction via histogram matching is attempted\n", "match_asics = False # if set, inner ASIC borders are matched to the same signal level\n", "adjust_mg_baseline = False # adjust medium gain baseline to match highest high gain value" ] @@ -69,8 +63,7 @@ "outputs": [], "source": [ "# Fill dictionaries comprising bools and arguments for correction and data analysis\n", - "\n", - "# Here the herarichy and dependability for correction booleans are defined \n", + "# Here the hierarichy and dependencies for correction booleans are defined \n", "corr_bools = {}\n", "\n", "# offset is at the bottom of AGIPD correction pyramid.\n", @@ -91,27 +84,18 @@ "metadata": {}, "outputs": [], "source": [ - "import sys\n", - "from collections import OrderedDict\n", - "from functools import partial\n", "from typing import List, Tuple\n", "\n", - "import h5py\n", "import matplotlib\n", "import numpy as np\n", "\n", "matplotlib.use(\"agg\")\n", - "import multiprocessing as mp\n", + "import multiprocessing\n", "from datetime import timedelta\n", "from pathlib import Path\n", "\n", "import matplotlib.pyplot as plt\n", - "from cal_tools.agipdlib import get_gain_setting\n", - "from cal_tools.tools import (\n", - " CalibrationMetadata,\n", - " get_dir_creation_date,\n", - " map_modules_from_folder,\n", - ")\n", + "from cal_tools import agipdlib, tools\n", "from dateutil import parser\n", "from iCalibrationDB import Conditions, Constants, Detectors" ] @@ -125,7 +109,7 @@ "# slopes_ff_from_files left as str for now\n", "in_folder = Path(in_folder)\n", "out_folder = Path(out_folder)\n", - "metadata = CalibrationMetadata(out_folder)" + "metadata = tools.CalibrationMetadata(out_folder)" ] }, { @@ -138,7 +122,7 @@ "\n", "creation_time = None\n", "if use_dir_creation_date:\n", - " creation_time = get_dir_creation_date(str(in_folder), run)\n", + " creation_time = tools.get_dir_creation_date(str(in_folder), run)\n", " offset = parser.parse(creation_date_offset)\n", " delta = timedelta(hours=offset.hour, minutes=offset.minute, seconds=offset.second)\n", " creation_time += delta\n", @@ -150,13 +134,7 @@ "print(f\"Outputting to {out_folder}\")\n", "out_folder.mkdir(parents=True, exist_ok=True)\n", "\n", - "import warnings\n", - "\n", - "warnings.filterwarnings('ignore')\n", - "\n", - "from cal_tools.agipdlib import SnowResolution\n", - "\n", - "melt_snow = False if corr_bools[\"only_offset\"] else SnowResolution.NONE" + "melt_snow = False if corr_bools[\"only_offset\"] else agipdlib.SnowResolution.NONE" ] }, { @@ -174,14 +152,18 @@ " gain_setting = None\n", " else:\n", " try:\n", - " gain_setting = get_gain_setting(str(control_fn), h5path_ctrl)\n", + " gain_setting = agipdlib.get_gain_setting(str(control_fn), h5path_ctrl)\n", " except Exception as e:\n", " print(f'ERROR: while reading gain setting from: \\n{control_fn}')\n", " print(e)\n", " print(\"Set gain setting to 0\")\n", " gain_setting = 0\n", "\n", + "# Evaluate gain mode (operation mode)\n", + "gain_mode = agipdlib.get_gain_mode(control_fn, h5path_ctrl)\n", + " \n", "print(f\"Gain setting: {gain_setting}\")\n", + "print(f\"Gain mode: {gain_mode.name}\")\n", "print(f\"Detector in use is {karabo_id}\")\n", "\n", "\n", @@ -210,24 +192,6 @@ " modules = [int(x[-2:]) for x in karabo_da]" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-21T11:30:07.974174Z", - "start_time": "2019-02-21T11:30:07.914832Z" - } - }, - "outputs": [], - "source": [ - "# set everything up filewise\n", - "print(f\"Checking the files before retrieving constants\")\n", - "mmf = map_modules_from_folder(str(in_folder), run, path_template, karabo_da, sequences)\n", - "\n", - "mapped_files, mod_ids, total_sequences, sequences_qm, _ = mmf" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -241,30 +205,12 @@ "metadata": {}, "outputs": [], "source": [ - "def retrieve_constants(karabo_id: str, bias_voltage: int, max_cells: float,\n", - " acq_rate: float, gain_setting: float, photon_energy: float,\n", - " only_dark: bool, nodb_with_dark: bool, \n", - " cal_db_interface: str, creation_time: str, \n", - " corr_bools: dict, pc_bools: List[bool],\n", - " inp: Tuple[str, str, str, int]\n", - " ) -> Tuple[str, str, float, float, str, dict]:\n", + "def retrieve_constants(\n", + " qm_files: List[Path], qm: str, karabo_da: str, idx: int\n", + ") -> Tuple[str, str, float, float, str, dict]:\n", " \"\"\"\n", - " Retreive constant for each module in parallel and produce a dictionary\n", - " with the creation-time and constant file path.\n", - " \n", - " :param karabo_id: (STR) Karabo ID\n", - " :param bias_voltage: (FLOAT) Bias Voltage\n", - " :param max_cells: (INT) Memory cells\n", - " :param acq_rate: (FLOAT) Acquisition Rate\n", - " :param gain_setting: (FLOAT) Gain setting\n", - " :param photon_energy: (FLOAT) Photon Energy\n", - " :param only_dark: (BOOL) only retrieve dark constants\n", - " :param nodb_with_dark: (BOOL) no constant retrieval even for dark\n", - " :param cal_db_interface: (STR) the database interface port\n", - " :param creation_time: (STR) raw data creation time\n", - " :param corr_bools: (DICT) A dictionary with bools for applying requested corrections\n", - " :param pc_bools: (LIST) list of bools to retrieve pulse capacitor constants\n", - " :param inp: (LIST) input for the parallel cluster of the partial function\n", + " Retrieve constants for a module.\n", + "\n", " :return:\n", " qm: module virtual name i.e. Q1M1.\n", " karabo_da: karabo data aggregator.\n", @@ -274,78 +220,104 @@ " mdata_dict: (DICT) dictionary with the metadata for the retrieved constants.\n", " \"\"\"\n", "\n", - " import sys\n", - " import traceback\n", - "\n", - " import numpy as np\n", - " from cal_tools.agipdlib import get_acq_rate, get_num_cells\n", - " from cal_tools.agipdutils import assemble_constant_dict\n", - " from cal_tools.tools import get_from_db\n", - " from iCalibrationDB import Conditions, Constants, Detectors\n", - "\n", " err = None\n", - "\n", - " qm_files, qm, karabo_da, idx = inp\n", - " # get number of memory cells from a sequence file with image data\n", - " for f in qm_files:\n", - " if not max_cells:\n", - " max_cells = get_num_cells(f, karabo_id, idx)\n", - " if max_cells is None:\n", - " if f != qm_files[-1]:\n", - " continue\n", - " else:\n", - " raise ValueError(f\"No raw images found for {qm} for all sequences\")\n", - " else:\n", - " cells = np.arange(max_cells)\n", - " # get out of the loop,\n", - " # if max_cells is successfully calculated. \n", + " if max_cells != 0:\n", + " # either use overriding notebook parameter\n", + " local_max_cells = max_cells\n", + " else:\n", + " # or look around in sequence files\n", + " for f in qm_files:\n", + " local_max_cells = agipdlib.get_num_cells(f, karabo_id, idx)\n", + " if local_max_cells is not None:\n", " break\n", + " # maybe we never found this in a sequence file...\n", + " if local_max_cells is None:\n", + " raise ValueError(f\"No raw images found for {qm} for all sequences\")\n", "\n", - " if acq_rate == 0.:\n", - " acq_rate = get_acq_rate((f, karabo_id, idx))\n", - "\n", - " # avoid creating retireving constant, if requested.\n", - " if not nodb_with_dark:\n", - " const_dict = assemble_constant_dict(corr_bools, pc_bools, max_cells, bias_voltage,\n", - " gain_setting, acq_rate, photon_energy,\n", - " beam_energy=None, only_dark=only_dark)\n", - "\n", - " # Retrieve multiple constants through an input dictionary\n", - " # to return a dict of useful metadata.\n", - " mdata_dict = dict()\n", - " mdata_dict['constants'] = dict()\n", - " mdata_dict['physical-detector-unit'] = None # initialization\n", - "\n", - " for cname, cval in const_dict.items():\n", - " # saving metadata in a dict\n", - " mdata_dict['constants'][cname] = dict()\n", + " if acq_rate == 0:\n", + " local_acq_rate = agipdlib.get_acq_rate(fast_paths=(f, karabo_id, idx))\n", + " else:\n", + " local_acq_rate = acq_rate\n", + "\n", + " # avoid retrieving constant, if requested.\n", + " if nodb_with_dark:\n", + " return\n", + "\n", + " const_dict = agipdlib.assemble_constant_dict(\n", + " corr_bools,\n", + " pc_bools,\n", + " local_max_cells,\n", + " bias_voltage,\n", + " gain_setting,\n", + " local_acq_rate,\n", + " photon_energy,\n", + " gain_mode=gain_mode,\n", + " beam_energy=None,\n", + " only_dark=only_dark,\n", + " )\n", + "\n", + " # Retrieve multiple constants through an input dictionary\n", + " # to return a dict of useful metadata.\n", + " mdata_dict = dict()\n", + " mdata_dict[\"constants\"] = dict()\n", + " mdata_dict[\"physical-detector-unit\"] = None # initialization\n", + "\n", + " for const_name, (const_init_fun, const_shape, (cond_type, cond_param)) in const_dict.items():\n", + " if gain_mode and const_name in (\"ThresholdsDark\",):\n", + " continue\n", + " \n", + " # saving metadata in a dict\n", + " const_mdata = dict()\n", + " mdata_dict[\"constants\"][const_name] = const_mdata\n", + "\n", + " if slopes_ff_from_files and const_name in [\"SlopesFF\", \"BadPixelsFF\"]:\n", + " const_mdata[\"file-path\"] = f\"{slopes_ff_from_files}/slopesff_bpmask_module_{qm}.h5\"\n", + " const_mdata[\"creation-time\"] = \"00:00:00\"\n", + " continue\n", + " \n", + " if gain_mode and const_name in (\"BadPixelsPC\", \"SlopesPC\", \"BadPixelsFF\", \"SlopesFF\"):\n", + " param_copy = cond_param.copy()\n", + " del param_copy[\"gain_mode\"]\n", + " condition = getattr(Conditions, cond_type).AGIPD(**param_copy)\n", + " else:\n", + " condition = getattr(Conditions, cond_type).AGIPD(**cond_param)\n", + "\n", + " _, mdata = tools.get_from_db(\n", + " karabo_id,\n", + " karabo_da,\n", + " getattr(Constants.AGIPD, const_name)(),\n", + " condition,\n", + " getattr(np, const_init_fun)(const_shape),\n", + " cal_db_interface,\n", + " creation_time,\n", + " meta_only=True,\n", + " verbosity=0,\n", + " )\n", + " mdata_const = mdata.calibration_constant_version\n", + " # check if constant was sucessfully retrieved.\n", + " if mdata.comm_db_success:\n", + " const_mdata[\"file-path\"] = (\n", + " f\"{mdata_const.hdf5path}\" f\"{mdata_const.filename}\"\n", + " )\n", + " const_mdata[\"creation-time\"] = f\"{mdata_const.begin_at}\"\n", + " mdata_dict[\"physical-detector-unit\"] = mdata_const.device_name\n", + " else:\n", + " const_mdata[\"file-path\"] = const_dict[const_name][:2]\n", + " const_mdata[\"creation-time\"] = None\n", "\n", - " if slopes_ff_from_files and cname in [\"SlopesFF\", \"BadPixelsFF\"]:\n", - " mdata_dict['constants'][cname][\"file-path\"] = f\"{slopes_ff_from_files}/slopesff_bpmask_module_{qm}.h5\"\n", - " mdata_dict['constants'][cname][\"creation-time\"] = \"00:00:00\"\n", - " else:\n", - " try:\n", - " condition = getattr(Conditions, cval[2][0]).AGIPD(**cval[2][1])\n", - " co, mdata = \\\n", - " get_from_db(karabo_id, karabo_da, getattr(Constants.AGIPD, cname)(),\n", - " condition, getattr(np, cval[0])(cval[1]),\n", - " cal_db_interface, creation_time, meta_only=True, verbosity=0)\n", - " mdata_const = mdata.calibration_constant_version\n", - " device_name = mdata.calibration_constant_version.device_name\n", - " # check if constant was sucessfully retrieved.\n", - " if mdata.comm_db_success:\n", - " mdata_dict['constants'][cname][\"file-path\"] = f\"{mdata_const.hdf5path}\" \\\n", - " f\"{mdata_const.filename}\"\n", - " mdata_dict['constants'][cname][\"creation-time\"] = f\"{mdata_const.begin_at}\"\n", - " mdata_dict['physical-detector-unit'] = mdata_const.device_name\n", - " else:\n", - " mdata_dict['constants'][cname][\"file-path\"] = const_dict[cname][:2]\n", - " mdata_dict['constants'][cname][\"creation-time\"] = None\n", - " except Exception as e:\n", - " err = f\"Error: {e}, Traceback: {traceback.format_exc()}\"\n", - " print(err)\n", - "\n", - " return qm, mdata_dict, karabo_da, acq_rate, max_cells, err\n", + " return qm, mdata_dict, karabo_da, acq_rate, local_max_cells, err" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# set everything up filewise\n", + "mapped_files, _, _, _, _ = tools.map_modules_from_folder(\n", + " str(in_folder), run, path_template, karabo_da, sequences\n", + ")\n", "\n", "pc_bools = [corr_bools.get(\"rel_gain\"),\n", " corr_bools.get(\"adjust_mg_baseline\"),\n", @@ -358,52 +330,63 @@ "only_dark = False\n", "nodb_with_dark = False\n", "if not nodb:\n", - " only_dark=(calfile != \"\")\n", + " only_dark = (calfile != \"\")\n", "if calfile != \"\" and not corr_bools[\"only_offset\"]:\n", " nodb_with_dark = nodb\n", "\n", "# A dict to connect virtual device\n", "# to actual device name.\n", - "for i, k_da in zip(modules, karabo_da):\n", - " qm = f\"Q{i//4+1}M{i%4+1}\"\n", + "for module_index, k_da in zip(modules, karabo_da):\n", + " qm = tools.module_index_to_qm(module_index)\n", " if qm in mapped_files and not mapped_files[qm].empty():\n", " device = getattr(getattr(Detectors, dinstance), qm)\n", - " qm_files = [str(mapped_files[qm].get()) for _ in range(mapped_files[qm].qsize())]\n", - "\n", + " # TODO: make map_modules_from_folder just return list(s)\n", + " qm_files = [Path(mapped_files[qm].get()) for _ in range(mapped_files[qm].qsize())]\n", " else:\n", - " print(f\"Skipping {qm}\")\n", " continue\n", "\n", - " inp.append((qm_files, qm, k_da, i))\n", - "\n", - "p = partial(retrieve_constants, karabo_id, bias_voltage, max_cells, \n", - " acq_rate, gain_setting, photon_energy, only_dark, nodb_with_dark, \n", - " cal_db_interface, creation_time, \n", - " corr_bools, pc_bools)\n", - "\n", - "with mp.Pool(processes=nmods) as pool:\n", - " results = pool.map(p, inp)\n", - "\n", + " inp.append((qm_files, qm, k_da, module_index))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "with multiprocessing.Pool(processes=nmods) as pool:\n", + " results = pool.starmap(retrieve_constants, inp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "mod_dev = dict()\n", "mdata_dict = dict()\n", - "for r in results:\n", - " if r:\n", - " qm, md_dict, karabo_da, acq_rate, max_cells, err = r\n", - " mod_dev[karabo_da] = {\"mod\": qm, \"err\": err}\n", - " if err:\n", - " print(f\"Error for module {qm}: {err}\")\n", - " mdata_dict[karabo_da] = md_dict\n", + "for qm, md_dict, karabo_da, acq_rate, max_cells, err in results:\n", + " mod_dev[karabo_da] = {\"mod\": qm, \"err\": err}\n", + " if err:\n", + " print(f\"Error for module {qm}: {err}\")\n", + " mdata_dict[karabo_da] = md_dict\n", "# check if it is requested not to retrieve any constants from the database\n", - "if not nodb_with_dark:\n", - " metadata.update({\"retrieved-constants\": mdata_dict})\n", - " \n", - " print(\"\\nRetrieved constants for modules: \",\n", - " f\"{[', '.join([f'Q{x//4+1}M{x%4+1}' for x in modules])]}\")\n", - " print(f\"Operating conditions are:\\n• Bias voltage: {bias_voltage}\\n• Memory cells: {max_cells}\\n\"\n", - " f\"• Acquisition rate: {acq_rate}\\n• Gain setting: {gain_setting}\\n• Photon Energy: {photon_energy}\\n\")\n", - " print(f\"Constant metadata is saved under \\\"retrieved-constants\\\" in calibration_metadata.yml\\n\")\n", + "if nodb_with_dark:\n", + " print(\"No constants were retrieved as calibrated files will be used.\")\n", "else:\n", - " print(\"No constants were retrieved as calibrated files will be used.\")" + " metadata.update({\"retrieved-constants\": mdata_dict})\n", + "\n", + " print(\"\\nRetrieved constants for modules:\",\n", + " ', '.join([tools.module_index_to_qm(x) for x in modules]))\n", + " print(f\"Operating conditions are:\")\n", + " print(f\"• Bias voltage: {bias_voltage}\")\n", + " print(f\"• Memory cells: {max_cells}\")\n", + " print(f\"• Acquisition rate: {acq_rate}\")\n", + " print(f\"• Gain mode: {gain_mode.name}\")\n", + " print(f\"• Gain setting: {gain_setting}\")\n", + " print(f\"• Photon Energy: {photon_energy}\")\n", + " print(\"Constant metadata is saved under \\\"retrieved-constants\\\" in calibration_metadata.yml\\n\")" ] }, { @@ -412,12 +395,11 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"Constants are retrieved with creation time: \")\n", - "i = 0\n", + "print(\"Constants are retrieved with creation time:\")\n", "timestamps = {}\n", "\n", "for k_da, dinfo in mod_dev.items():\n", - " print(dinfo[\"mod\"], \":\")\n", + " print(f\"{dinfo['mod']}:\")\n", " module_timestamps = {}\n", " module_name = dinfo[\"mod\"]\n", " if k_da in mdata_dict:\n", @@ -425,10 +407,8 @@ " if hasattr(mdata[\"creation-time\"], 'strftime'):\n", " mdata[\"creation-time\"] = mdata[\"creation-time\"].strftime('%y-%m-%d %H:%M')\n", " print(f'{cname:.<12s}', mdata[\"creation-time\"])\n", - " # Store few time stamps if exists\n", - " # Add NA to keep array structure\n", " for cname in ['Offset', 'SlopesPC', 'SlopesFF']:\n", - " if not k_da in mdata_dict or dinfo[\"err\"]:\n", + " if k_da not in mdata_dict or dinfo[\"err\"]:\n", " module_timestamps[cname] = \"Err\"\n", " else:\n", " if cname in mdata_dict[k_da]:\n", @@ -440,13 +420,6 @@ " module_timestamps[cname] = \"NA\"\n", " timestamps[module_name] = module_timestamps\n", "\n", - " i += 1\n", - " if sequences:\n", - " seq_num = sequences[0]\n", - " else:\n", - " # if sequences[0] changed to None as it was -1\n", - " seq_num = 0\n", - "\n", "time_summary = metadata.setdefault(\"retrieved-constants\", {}).setdefault(\"time-summary\", {})\n", "time_summary[\"SAll\"] = timestamps\n", "\n", diff --git a/notebooks/AGIPD/Characterize_AGIPD_Gain_Darks_NBC.ipynb b/notebooks/AGIPD/Characterize_AGIPD_Gain_Darks_NBC.ipynb index 8d11bae6992695d8f4e4220f9b5b2c7b0a27b8de..22ee77702d3b9e17891e95e63686c2a7eb083f21 100644 --- a/notebooks/AGIPD/Characterize_AGIPD_Gain_Darks_NBC.ipynb +++ b/notebooks/AGIPD/Characterize_AGIPD_Gain_Darks_NBC.ipynb @@ -16,23 +16,17 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-20T12:42:51.255184Z", - "start_time": "2019-02-20T12:42:51.225500Z" - } - }, + "metadata": {}, "outputs": [], "source": [ - "cluster_profile = \"noDB\" # The ipcluster profile to use\n", - "in_folder = \"/gpfs/exfel/d/raw/DETLAB/202031/p900172/\" # path to input data, required\n", - "out_folder = \"/gpfs/exfel/data/scratch/ahmedk/test/miniHalfAGIPD\" # path to output to, required\n", + "in_folder = \"/gpfs/exfel/d/raw/CALLAB/202031/p900113\" # path to input data, required\n", + "out_folder = \"/gpfs/exfel/data/scratch/hammerd/agipd-fixed-gain\" # path to output to, required\n", "sequences = [0] # sequence files to evaluate.\n", "modules = [-1] # list of modules to evaluate, RANGE ALLOWED\n", - "run_high = 84 # run number in which high gain data was recorded, required\n", - "run_med = 87 # run number in which medium gain data was recorded, required\n", - "run_low = 88 # run number in which low gain data was recorded, required\n", - "operation_mode = 'ADAPTIVE_GAIN' # Detector operation mode, optional\n", + "run_high = 9985 # run number in which high gain data was recorded, required\n", + "run_med = 9984 # run number in which medium gain data was recorded, required\n", + "run_low = 9983 # run number in which low gain data was recorded, required\n", + "operation_mode = \"ADAPTIVE_GAIN\" # Detector operation mode, optional (defaults to \"ADAPTIVE_GAIN\")\n", "\n", "karabo_id = \"HED_DET_AGIPD500K2G\" # karabo karabo_id\n", "karabo_da = ['-1'] # a list of data aggregators names, Default [-1] for selecting all data aggregators\n", @@ -40,9 +34,9 @@ "path_template = 'RAW-R{:04d}-{}-S{:05d}.h5' # the template to use to access data\n", "h5path = '/INSTRUMENT/{}/DET/{}:xtdf/image' # path in the HDF5 file to images\n", "h5path_idx = '/INDEX/{}/DET/{}:xtdf/image' # path in the HDF5 file to images\n", - "h5path_ctrl = '/CONTROL/{}/MDL/FPGA_COMP_TEST' # path to control information\n", - "karabo_id_control = \"SPB_IRU_AGIPD1M1\" # karabo-id for control device '\n", - "karabo_da_control = \"AGIPD1MCTRL00\" # karabo DA for control infromation\n", + "h5path_ctrl = '/CONTROL/{}/MDL/FPGA_COMP' # path to control information\n", + "karabo_id_control = \"HED_EXP_AGIPD500K2G\" # karabo-id for control device '\n", + "karabo_da_control = \"AGIPD500K2G00\" # karabo DA for control infromation\n", "\n", "use_dir_creation_date = True # use dir creation date as data production reference date\n", "cal_db_interface = \"tcp://max-exfl016:8020\" # the database interface to use\n", @@ -58,10 +52,13 @@ "rawversion = 2 # RAW file format version\n", "\n", "thresholds_offset_sigma = 3. # offset sigma thresholds for offset deduced bad pixels\n", - "thresholds_offset_hard = [0, 0] # For setting the same threshold offset for the 3 gains. Left for backcompatability. Default [0, 0] to take the following parameters.\n", - "thresholds_offset_hard_hg = [3000, 7000] # High-gain thresholds in absolute ADU terms for offset deduced bad pixels\n", - "thresholds_offset_hard_mg = [6000, 10000] # Medium-gain thresholds in absolute ADU terms for offset deduced bad pixels\n", - "thresholds_offset_hard_lg = [6000, 10000] # Low-gain thresholds in absolute ADU terms for offset deduced bad pixels\n", + "thresholds_offset_hard = [0, 0] # For setting the same threshold offset for the 3 gains. Left for backcompatability. Default [0, 0] to take the following parameters.\n", + "thresholds_offset_hard_hg = [3000, 7000] # High-gain thresholds in absolute ADU terms for offset deduced bad pixels\n", + "thresholds_offset_hard_mg = [6000, 10000] # Medium-gain thresholds in absolute ADU terms for offset deduced bad pixels\n", + "thresholds_offset_hard_lg = [6000, 10000] # Low-gain thresholds in absolute ADU terms for offset deduced bad pixels\n", + "thresholds_offset_hard_hg_fixed = [3500, 6500] # Same as thresholds_offset_hard_hg, but for fixed gain operation\n", + "thresholds_offset_hard_mg_fixed = [3500, 6500] # Same as thresholds_offset_hard_mg, but for fixed gain operation\n", + "thresholds_offset_hard_lg_fixed = [3500, 6500] # Same as thresholds_offset_hard_lg, but for fixed gain operation\n", "\n", "thresholds_noise_sigma = 5. # noise sigma thresholds for offset deduced bad pixels\n", "thresholds_noise_hard = [0, 0] # For setting the same threshold noise for the 3 gains. Left for backcompatability. Default [0, 0] to take the following parameters.\n", @@ -77,30 +74,20 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-20T12:42:52.599660Z", - "start_time": "2019-02-20T12:42:51.472138Z" - } - }, + "metadata": {}, "outputs": [], "source": [ - "import warnings\n", - "\n", - "# imports and things that do not usually need to be changed\n", - "from datetime import datetime\n", - "\n", - "import dateutil.parser\n", - "\n", - "warnings.filterwarnings('ignore')\n", "import os\n", "from collections import OrderedDict\n", - "from typing import List, Tuple\n", + "from datetime import datetime\n", + "from typing import Tuple\n", "\n", + "import dateutil.parser\n", "import h5py\n", "import matplotlib\n", "import numpy as np\n", "import tabulate\n", + "from cal_tools.enums import BadPixels\n", "\n", "matplotlib.use('agg')\n", "import matplotlib.pyplot as plt\n", @@ -108,8 +95,16 @@ "\n", "%matplotlib inline\n", "\n", - "from cal_tools.agipdlib import get_bias_voltage, get_gain_setting\n", - "from cal_tools.enums import BadPixels\n", + "import multiprocessing\n", + "\n", + "from cal_tools.agipdlib import (\n", + " get_acq_rate,\n", + " get_bias_voltage,\n", + " get_gain_mode,\n", + " get_gain_setting,\n", + " get_num_cells,\n", + ")\n", + "from cal_tools.enums import AgipdGainMode\n", "from cal_tools.plotting import (\n", " create_constant_overview,\n", " plot_badpix_3d,\n", @@ -119,30 +114,29 @@ "from cal_tools.tools import (\n", " get_dir_creation_date,\n", " get_from_db,\n", - " get_notebook_name,\n", " get_pdu_from_db,\n", " get_random_db_interface,\n", " get_report,\n", " map_gain_stages,\n", - " parse_runs,\n", + " module_index_to_qm,\n", " run_prop_seq_from_path,\n", " save_const_to_h5,\n", " send_to_db,\n", ")\n", + "from iCalibrationDB import Conditions, Constants, Detectors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# insert control device if format string (does nothing otherwise)\n", + "h5path_ctrl = h5path_ctrl.format(karabo_id_control)\n", "\n", - "# make sure a cluster is running with ipcluster start --n=32, give it a while to start\n", - "from ipyparallel import Client\n", - "\n", - "view = Client(profile=cluster_profile)[:]\n", - "view.use_dill()\n", - "\n", - "from iCalibrationDB import Conditions, Constants, Detectors, Versions\n", - "\n", - "gains = np.arange(3)\n", - "\n", - "IL_MODE = interlaced\n", "max_cells = mem_cells\n", - " \n", + "\n", "offset_runs = OrderedDict()\n", "offset_runs[\"high\"] = run_high\n", "offset_runs[\"med\"] = run_med\n", @@ -172,12 +166,25 @@ " nmods = 8\n", "\n", "control_names = [f'{in_folder}/r{r:04d}/RAW-R{r:04d}-{karabo_da_control}-S00000.h5'\n", - " for r in (run_high, run_med, run_low)] \n", + " for r in (run_high, run_med, run_low)]\n", + "\n", + "if operation_mode not in (\"ADAPTIVE_GAIN\", \"FIXED_GAIN\"):\n", + " print(f\"WARNING: unknown operation_mode \\\"{operation_mode}\\\" parameter set\")\n", + "run_gain_modes = [get_gain_mode(fn, h5path_ctrl) for fn in control_names]\n", + "if all(gm == AgipdGainMode.ADAPTIVE_GAIN for gm in run_gain_modes):\n", + " fixed_gain_mode = False\n", + " if operation_mode == \"FIXED_GAIN\":\n", + " print(\"WARNING: operation_mode parameter is FIXED_GAIN, slow data indicates adaptive gain\")\n", + "elif run_gain_modes == [AgipdGainMode.FIXED_HIGH_GAIN, AgipdGainMode.FIXED_MEDIUM_GAIN, AgipdGainMode.FIXED_LOW_GAIN]:\n", + " if operation_mode == \"ADAPTIVE_GAIN\":\n", + " print(\"WARNING: operation_mode parameter ix ADAPTIVE_GAIN, slow data indicates fixed gain\")\n", + " fixed_gain_mode = True\n", + "else:\n", + " print(f'Something is clearly wrong; slow data indicates gain modes {run_gain_modes}')\n", "\n", "print(f\"Detector in use is {karabo_id}\")\n", "print(f\"Instrument {instrument}\")\n", - "print(f\"Detector instance {dinstance}\")\n", - "print(f\"Operation mode is {operation_mode}\")" + "print(f\"Detector instance {dinstance}\")" ] }, { @@ -188,9 +195,6 @@ "source": [ "runs = [run_high, run_med, run_low]\n", "\n", - "if \"{\" in h5path_ctrl:\n", - " h5path_ctrl = h5path_ctrl.format(karabo_id_control)\n", - "\n", "if gain_setting == 0.1:\n", " if creation_time.replace(tzinfo=None) < dateutil.parser.parse('2020-01-31'):\n", " print(\"Set gain-setting to None for runs taken before 2020-01-31\")\n", @@ -205,7 +209,7 @@ " gsettings.append(get_gain_setting(control_fname, h5path_ctrl))\n", " if not all(g == gsettings[0] for g in gsettings):\n", " raise ValueError(f\"Different gain settings for the 3 input runs {gsettings}\")\n", - " gain_setting = gsettings[0] \n", + " gain_setting = gsettings[0]\n", " except Exception as e:\n", " print(f'Error while reading gain setting from: \\n{control_fname}')\n", " print(f'Error: {e}')\n", @@ -218,12 +222,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-20T12:42:52.608214Z", - "start_time": "2019-02-20T12:42:52.601257Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "if karabo_da[0] == '-1':\n", @@ -235,7 +234,7 @@ "h5path = h5path.format(karabo_id, receiver_id)\n", "h5path_idx = h5path_idx.format(karabo_id, receiver_id)\n", "\n", - "if bias_voltage == 0: \n", + "if bias_voltage == 0:\n", " # Read the bias voltage from files, if recorded.\n", " # If not available, make use of the historical voltage the detector is running at\n", " bias_voltage = get_bias_voltage(control_names[0], karabo_id_control)\n", @@ -246,12 +245,13 @@ "print(f\"Memory cells: {mem_cells}/{max_cells}\")\n", "print(\"Runs: {}\".format([ v for v in offset_runs.values()]))\n", "print(f\"Sequences: {sequences}\")\n", - "print(f\"Interlaced mode: {IL_MODE}\")\n", + "print(f\"Interlaced mode: {interlaced}\")\n", "print(f\"Using DB: {db_output}\")\n", "print(f\"Input: {in_folder}\")\n", "print(f\"Output: {out_folder}\")\n", "print(f\"Bias voltage: {bias_voltage}V\")\n", - "print(f\"Gain setting: {gain_setting}\")" + "print(f\"Gain setting: {gain_setting}\")\n", + "print(f\"Operation mode is {'fixed' if fixed_gain_mode else 'adaptive'} gain mode\")" ] }, { @@ -264,12 +264,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-20T12:42:54.024731Z", - "start_time": "2019-02-20T12:42:53.901555Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "# set everything up filewise\n", @@ -291,99 +286,116 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2019-02-20T10:50:55.839958Z", - "start_time": "2019-02-20T10:50:55.468134Z" - }, - "scrolled": true - }, + "metadata": {}, "outputs": [], "source": [ - "import copy\n", - "from functools import partial\n", - "\n", - "\n", - "def characterize_module(il_mode: bool,\n", - " cells: int,\n", - " bp_thresh: Tuple[List[int], float, List[int], float], \n", - " rawversion: int,\n", - " loc: str, \n", - " acq_rate: float,\n", - " h5path: str,\n", - " h5path_idx: str,\n", - " control_names: List[str],\n", - " karabo_id_control: str,\n", - " inp: Tuple[str, int, int]) -> Tuple[np.array, np.array, np.array, np.array, int, np.array, int, float]:\n", - " import copy\n", - "\n", - " import h5py\n", - " import numpy as np\n", - " from cal_tools.agipdlib import get_acq_rate, get_num_cells\n", - " from cal_tools.enums import BadPixels\n", - "\n", - " fast_data_filename, channel, gg = inp\n", - " \n", - " if cells == 0:\n", - " cells = get_num_cells(fast_data_filename, loc, channel)\n", + "if thresholds_offset_hard != [0, 0]:\n", + " # if set, this will override the individual parameters\n", + " thresholds_offset_hard = [thresholds_offset_hard] * 3\n", + "elif fixed_gain_mode:\n", + " thresholds_offset_hard = [\n", + " thresholds_offset_hard_hg_fixed,\n", + " thresholds_offset_hard_mg_fixed,\n", + " thresholds_offset_hard_lg_fixed,\n", + " ]\n", + "else:\n", + " thresholds_offset_hard = [\n", + " thresholds_offset_hard_hg,\n", + " thresholds_offset_hard_mg,\n", + " thresholds_offset_hard_lg,\n", + " ]\n", + "print(f\"Will use the following hard offset thresholds\")\n", + "for name, value in zip((\"High\", \"Medium\", \"Low\"), thresholds_offset_hard):\n", + " print(f\"- {name} gain: {value}\")\n", + "\n", + "if thresholds_noise_hard != [0, 0]:\n", + " thresholds_noise_hard = [thresholds_noise_hard] * 3\n", + "else:\n", + " thresholds_noise_hard = [\n", + " thresholds_noise_hard_hg,\n", + " thresholds_noise_hard_mg,\n", + " thresholds_noise_hard_lg,\n", + " ]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def characterize_module(fast_data_filename: str, channel: int, gg: int) -> Tuple[np.array, np.array, np.array, np.array, int, np.array, int, float]:\n", + " if max_cells == 0:\n", + " num_cells = get_num_cells(fast_data_filename, karabo_id, channel)\n", + " else:\n", + " num_cells = max_cells\n", + "\n", + " print(f\"Using {num_cells} memory cells\")\n", "\n", - " print(f\"Using {cells} memory cells\")\n", - " \n", " if acq_rate == 0.:\n", " slow_paths = control_names[gg], karabo_id_control\n", - " fast_paths = fast_data_filename, loc, channel\n", - " acq_rate = get_acq_rate(fast_paths, slow_paths)\n", + " fast_paths = fast_data_filename, karabo_id, channel\n", + " local_acq_rate = get_acq_rate(fast_paths, slow_paths)\n", + " else:\n", + " local_acq_rate = acq_rate\n", "\n", - " thresholds_offset, thresholds_offset_sigma, thresholds_noise, thresholds_noise_sigma = bp_thresh \n", - " thresholds_offset_hard = thresholds_offset[gg]\n", - " thresholds_noise_hard = thresholds_noise[gg]\n", - " \n", - " h5path = h5path.format(channel)\n", - " h5path_idx = h5path_idx.format(channel)\n", - " \n", - " with h5py.File(fast_data_filename, \"r\", driver=\"core\") as infile:\n", + " local_thresholds_offset_hard = thresholds_offset_hard[gg]\n", + " local_thresholds_noise_hard = thresholds_noise_hard[gg]\n", + "\n", + " h5path_f = h5path.format(channel)\n", + " h5path_idx_f = h5path_idx.format(channel)\n", + "\n", + " with h5py.File(fast_data_filename, \"r\") as infile:\n", " if rawversion == 2:\n", - " count = np.squeeze(infile[f\"{h5path_idx}/count\"])\n", - " first = np.squeeze(infile[f\"{h5path_idx}/first\"])\n", + " count = np.squeeze(infile[f\"{h5path_idx_f}/count\"])\n", + " first = np.squeeze(infile[f\"{h5path_idx_f}/first\"])\n", " last_index = int(first[count != 0][-1]+count[count != 0][-1])\n", " first_index = int(first[count != 0][0])\n", " else:\n", - " status = np.squeeze(infile[f\"{h5path_idx}/status\"])\n", + " status = np.squeeze(infile[f\"{h5path_idx_f}/status\"])\n", " if np.count_nonzero(status != 0) == 0:\n", " return\n", - " last = np.squeeze(infile[f\"{h5path_idx}/last\"])\n", - " first = np.squeeze(infile[f\"{h5path_idx}/first\"])\n", + " last = np.squeeze(infile[f\"{h5path_idx_f}/last\"])\n", + " first = np.squeeze(infile[f\"{h5path_idx_f}/first\"])\n", " last_index = int(last[status != 0][-1]) + 1\n", " first_index = int(first[status != 0][0])\n", - " im = np.array(infile[f\"{h5path}/data\"][first_index:last_index,...]) \n", - " cellIds = np.squeeze(infile[f\"{h5path}/cellId\"][first_index:last_index,...]) \n", - "\n", - " if il_mode:\n", - " ga = im[1::2, 0, ...]\n", + " im = np.array(infile[f\"{h5path_f}/data\"][first_index:last_index,...])\n", + " cellIds = np.squeeze(infile[f\"{h5path_f}/cellId\"][first_index:last_index,...])\n", + " \n", + " if interlaced:\n", + " if not fixed_gain_mode:\n", + " ga = im[1::2, 0, ...]\n", " im = im[0::2, 0, ...].astype(np.float32)\n", " cellIds = cellIds[::2]\n", " else:\n", - " ga = im[:, 1, ...]\n", + " if not fixed_gain_mode:\n", + " ga = im[:, 1, ...]\n", " im = im[:, 0, ...].astype(np.float32)\n", "\n", " im = np.rollaxis(im, 2)\n", " im = np.rollaxis(im, 2, 1)\n", "\n", - " ga = np.rollaxis(ga, 2)\n", - " ga = np.rollaxis(ga, 2, 1)\n", - "\n", - " mcells = cells #max(cells, np.max(cellIds)+1)\n", - " offset = np.zeros((im.shape[0], im.shape[1], mcells))\n", - " gains = np.zeros((im.shape[0], im.shape[1], mcells))\n", - " noise = np.zeros((im.shape[0], im.shape[1], mcells))\n", - " gains_std = np.zeros((im.shape[0], im.shape[1], mcells))\n", + " if not fixed_gain_mode:\n", + " ga = np.rollaxis(ga, 2)\n", + " ga = np.rollaxis(ga, 2, 1)\n", " \n", - " for cc in np.unique(cellIds[cellIds < mcells]):\n", + " offset = np.zeros((im.shape[0], im.shape[1], num_cells))\n", + " noise = np.zeros((im.shape[0], im.shape[1], num_cells))\n", + "\n", + " if fixed_gain_mode:\n", + " gains = None\n", + " gains_std = None\n", + " else:\n", + " gains = np.zeros((im.shape[0], im.shape[1], num_cells))\n", + " gains_std = np.zeros((im.shape[0], im.shape[1], num_cells))\n", + "\n", + " for cc in np.unique(cellIds[cellIds < num_cells]):\n", " cellidx = cellIds == cc\n", " offset[...,cc] = np.median(im[..., cellidx], axis=2)\n", " noise[...,cc] = np.std(im[..., cellidx], axis=2)\n", - " gains[...,cc] = np.median(ga[..., cellidx], axis=2)\n", - " gains_std[...,cc] = np.std(ga[..., cellidx], axis=2)\n", + " if not fixed_gain_mode:\n", + " gains[...,cc] = np.median(ga[..., cellidx], axis=2)\n", + " gains_std[...,cc] = np.std(ga[..., cellidx], axis=2)\n", "\n", " # bad pixels\n", " bp = np.zeros(offset.shape, np.uint32)\n", @@ -393,47 +405,43 @@ "\n", " bp[(offset < offset_mn-thresholds_offset_sigma*offset_std) |\n", " (offset > offset_mn+thresholds_offset_sigma*offset_std)] |= BadPixels.OFFSET_OUT_OF_THRESHOLD.value\n", - " bp[(offset < thresholds_offset_hard[0]) | (\n", - " offset > thresholds_offset_hard[1])] |= BadPixels.OFFSET_OUT_OF_THRESHOLD.value\n", + " bp[(offset < local_thresholds_offset_hard[0]) | (\n", + " offset > local_thresholds_offset_hard[1])] |= BadPixels.OFFSET_OUT_OF_THRESHOLD.value\n", " bp[~np.isfinite(offset)] |= BadPixels.OFFSET_NOISE_EVAL_ERROR.value\n", "\n", " # noise related bad pixels\n", " noise_mn = np.nanmedian(noise, axis=(0,1))\n", - " noise_std = np.nanstd(noise, axis=(0,1)) \n", + " noise_std = np.nanstd(noise, axis=(0,1))\n", " bp[(noise < noise_mn-thresholds_noise_sigma*noise_std) |\n", " (noise > noise_mn+thresholds_noise_sigma*noise_std)] |= BadPixels.NOISE_OUT_OF_THRESHOLD.value\n", - " bp[(noise < thresholds_noise_hard[0]) | (noise > thresholds_noise_hard[1])] |= BadPixels.NOISE_OUT_OF_THRESHOLD.value\n", + " bp[(noise < local_thresholds_noise_hard[0]) | (noise > local_thresholds_noise_hard[1])] |= BadPixels.NOISE_OUT_OF_THRESHOLD.value\n", " bp[~np.isfinite(noise)] |= BadPixels.OFFSET_NOISE_EVAL_ERROR.value\n", "\n", - " return offset, noise, gains, gains_std, gg, bp, cells, acq_rate\n", - "\n", + " return offset, noise, gains, gains_std, gg, bp, num_cells, local_acq_rate" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "offset_g = OrderedDict()\n", "noise_g = OrderedDict()\n", - "gain_g = OrderedDict()\n", - "gainstd_g = OrderedDict()\n", "badpix_g = OrderedDict()\n", - "gg = 0\n", - "\n", + "if not fixed_gain_mode:\n", + " gain_g = OrderedDict()\n", + " gainstd_g = OrderedDict()\n", + " \n", "start = datetime.now()\n", "all_cells = []\n", "all_acq_rate = []\n", "\n", - "if thresholds_offset_hard == [0, 0]:\n", - " thresholds_offset_hard = [thresholds_offset_hard_hg, thresholds_offset_hard_mg, thresholds_offset_hard_lg]\n", - "else:\n", - " thresholds_offset_hard = [thresholds_offset_hard] * 3\n", - "\n", - "if thresholds_noise_hard == [0, 0]:\n", - " thresholds_noise_hard = [thresholds_noise_hard_hg, thresholds_noise_hard_mg, thresholds_noise_hard_lg]\n", - "else:\n", - " thresholds_noise_hard = [thresholds_noise_hard] * 3\n", - "\n", - " \n", "inp = []\n", - "for gain, mapped_files in gain_mapped_files.items():\n", + "for gg, (gain, mapped_files) in enumerate(gain_mapped_files.items()):\n", " dones = []\n", " for i in modules:\n", - " qm = f\"Q{i//4+1}M{i%4+1}\"\n", + " qm = module_index_to_qm(i)\n", " if qm in mapped_files and not mapped_files[qm].empty():\n", " fname_in = mapped_files[qm].get()\n", " print(\"Process file: \", fname_in)\n", @@ -441,38 +449,30 @@ " else:\n", " continue\n", " inp.append((fname_in, i, gg))\n", - " \n", - " gg += 1\n", - "\n", - "p = partial(characterize_module, IL_MODE, max_cells,\n", - " (thresholds_offset_hard, thresholds_offset_sigma,\n", - " thresholds_noise_hard, thresholds_noise_sigma),\n", - " rawversion, karabo_id, acq_rate, h5path, h5path_idx,\n", - " control_names, karabo_id_control)\n", "\n", - "# Don't remove. Used for Debugging.\n", - "#results = list(map(p, inp))\n", - "results = view.map_sync(p, inp)\n", + "with multiprocessing.Pool() as pool:\n", + " results = pool.starmap(characterize_module, inp)\n", "\n", - "for ii, r in enumerate(results):\n", - " offset, noise, gains, gains_std, gg, bp, thiscell, thisacq = r\n", + "for offset, noise, gains, gains_std, gg, bp, thiscell, thisacq in results:\n", " all_cells.append(thiscell)\n", " all_acq_rate.append(thisacq)\n", " for i in modules:\n", - " qm = f\"Q{i//4+1}M{i%4+1}\"\n", + " qm = module_index_to_qm(i)\n", " if qm not in offset_g:\n", " offset_g[qm] = np.zeros((offset.shape[0], offset.shape[1], offset.shape[2], 3))\n", " noise_g[qm] = np.zeros_like(offset_g[qm])\n", - " gain_g[qm] = np.zeros_like(offset_g[qm])\n", - " gainstd_g[qm] = np.zeros_like(offset_g[qm])\n", " badpix_g[qm] = np.zeros_like(offset_g[qm], np.uint32)\n", + " if not fixed_gain_mode:\n", + " gain_g[qm] = np.zeros_like(offset_g[qm])\n", + " gainstd_g[qm] = np.zeros_like(offset_g[qm])\n", "\n", " offset_g[qm][...,gg] = offset\n", " noise_g[qm][...,gg] = noise\n", - " gain_g[qm][...,gg] = gains\n", - " gainstd_g[qm][..., gg] = gains_std\n", " badpix_g[qm][...,gg] = bp\n", - " \n", + " if not fixed_gain_mode:\n", + " gain_g[qm][...,gg] = gains\n", + " gainstd_g[qm][..., gg] = gains_std\n", + "\n", "\n", "duration = (datetime.now() - start).total_seconds()\n", "\n", @@ -489,11 +489,12 @@ "outputs": [], "source": [ "# Add a badpixel due to bad gain separation\n", - "for g in range(2):\n", - " # Bad pixels during bad gain separation.\n", - " # Fraction of pixels in the module with separation lower than \"thresholds_gain_sigma\".\n", - " bad_sep = (gain_g[qm][..., g+1] - gain_g[qm][..., g]) / np.sqrt(gainstd_g[qm][..., g+1]**2 + gainstd_g[qm][..., g]**2)\n", - " badpix_g[qm][...,g+1][(bad_sep)<thresholds_gain_sigma]|= BadPixels.GAIN_THRESHOLDING_ERROR.value" + "if not fixed_gain_mode:\n", + " for g in range(2):\n", + " # Bad pixels during bad gain separation.\n", + " # Fraction of pixels in the module with separation lower than \"thresholds_gain_sigma\".\n", + " bad_sep = (gain_g[qm][..., g+1] - gain_g[qm][..., g]) / np.sqrt(gainstd_g[qm][..., g+1]**2 + gainstd_g[qm][..., g]**2)\n", + " badpix_g[qm][...,g+1][(bad_sep)<thresholds_gain_sigma]|= BadPixels.GAIN_THRESHOLDING_ERROR.value" ] }, { @@ -506,42 +507,35 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-06T09:38:18.220833Z", - "start_time": "2018-12-06T09:38:17.926616Z" - } - }, + "metadata": {}, "outputs": [], "source": [ - "thresholds_g = {}\n", - "for qm in gain_g.keys():\n", - " thresholds_g[qm] = np.zeros((gain_g[qm].shape[0], gain_g[qm].shape[1], gain_g[qm].shape[2], 5))\n", - " thresholds_g[qm][...,0] = (gain_g[qm][...,1]+gain_g[qm][...,0])/2\n", - " thresholds_g[qm][...,1] = (gain_g[qm][...,2]+gain_g[qm][...,1])/2\n", - " for i in range(3):\n", - " thresholds_g[qm][...,2+i] = gain_g[qm][...,i]" + "if not fixed_gain_mode:\n", + " thresholds_g = {}\n", + " for qm in gain_g.keys():\n", + " thresholds_g[qm] = np.zeros((gain_g[qm].shape[0], gain_g[qm].shape[1], gain_g[qm].shape[2], 5))\n", + " thresholds_g[qm][...,0] = (gain_g[qm][...,1]+gain_g[qm][...,0])/2\n", + " thresholds_g[qm][...,1] = (gain_g[qm][...,2]+gain_g[qm][...,1])/2\n", + " for i in range(3):\n", + " thresholds_g[qm][...,2+i] = gain_g[qm][...,i]" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-06T09:38:18.234582Z", - "start_time": "2018-12-06T09:38:18.222838Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "res = OrderedDict()\n", "for i in modules:\n", - " qm = \"Q{}M{}\".format(i//4+1, i%4+1)\n", - " res[qm] = {'Offset': offset_g[qm],\n", - " 'Noise': noise_g[qm],\n", - " 'ThresholdsDark': thresholds_g[qm],\n", - " 'BadPixelsDark': badpix_g[qm] \n", - " }" + " qm = module_index_to_qm(i)\n", + " res[qm] = {\n", + " 'Offset': offset_g[qm],\n", + " 'Noise': noise_g[qm],\n", + " 'BadPixelsDark': badpix_g[qm]\n", + " }\n", + " if not fixed_gain_mode:\n", + " res[qm]['ThresholdsDark'] = thresholds_g[qm]" ] }, { @@ -567,27 +561,40 @@ "# Create the modules dict of karabo_das and PDUs\n", "qm_dict = OrderedDict()\n", "for i, k_da in zip(modules, karabo_da):\n", - " qm = f\"Q{i//4+1}M{i%4+1}\"\n", - " qm_dict[qm] = {\"karabo_da\": k_da,\n", - " \"db_module\": \"\"}" + " qm = module_index_to_qm(i)\n", + " qm_dict[qm] = {\n", + " \"karabo_da\": k_da,\n", + " \"db_module\": \"\"\n", + " }" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, + "outputs": [], + "source": [ + "# set the operating condition\n", + "# note: iCalibrationDB only adds gain_mode if it is truthy, so we don't need to handle None\n", + "condition = Conditions.Dark.AGIPD(memory_cells=max_cells,\n", + " bias_voltage=bias_voltage,\n", + " acquisition_rate=acq_rate,\n", + " gain_setting=gain_setting,\n", + " gain_mode=fixed_gain_mode)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Retrieve existing constants for comparison\n", - "clist = [\"Offset\", \"Noise\", \"ThresholdsDark\", \"BadPixelsDark\"]\n", "old_const = {}\n", "old_mdata = {}\n", "detinst = getattr(Detectors, dinstance)\n", "\n", "print('Retrieve pre-existing constants for comparison.')\n", - "\n", "for qm in res:\n", " qm_db = qm_dict[qm]\n", " karabo_da = qm_db[\"karabo_da\"]\n", @@ -595,13 +602,7 @@ " dconst = getattr(Constants.AGIPD, const)()\n", " dconst.data = res[qm][const]\n", "\n", - " # Setting conditions\n", - " condition = Conditions.Dark.AGIPD(memory_cells=max_cells,\n", - " bias_voltage=bias_voltage,\n", - " acquisition_rate=acq_rate,\n", - " gain_setting=gain_setting)\n", - "\n", - " # This should be used in case of running notebook \n", + " # This should be used in case of running notebook\n", " # by a different method other than myMDC which already\n", " # sends CalCat info.\n", " # TODO: Set db_module to \"\" by default in the first cell\n", @@ -632,12 +633,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-06T09:49:32.449330Z", - "start_time": "2018-12-06T09:49:20.231607Z" - } - }, + "metadata": {}, "outputs": [], "source": [ "md = None\n", @@ -649,21 +645,16 @@ " dconst = getattr(Constants.AGIPD, const)()\n", " dconst.data = res[qm][const]\n", "\n", - " # set the operating condition\n", - " condition = Conditions.Dark.AGIPD(memory_cells=max_cells,\n", - " bias_voltage=bias_voltage,\n", - " acquisition_rate=acq_rate,\n", - " gain_setting=gain_setting)\n", " if db_output:\n", - " md = send_to_db(db_module, karabo_id, dconst, condition, file_loc, \n", + " md = send_to_db(db_module, karabo_id, dconst, condition, file_loc,\n", " report, cal_db_interface, creation_time=creation_time,\n", " timeout=cal_db_timeout)\n", "\n", " if local_output:\n", - " md = save_const_to_h5(db_module, karabo_id, dconst, condition, dconst.data, \n", + " md = save_const_to_h5(db_module, karabo_id, dconst, condition, dconst.data,\n", " file_loc, report, creation_time, out_folder)\n", " print(f\"Calibration constant {const} is stored locally.\\n\")\n", - " \n", + "\n", " print(\"Constants parameter conditions are:\\n\")\n", " print(f\"• memory_cells: {max_cells}\\n• bias_voltage: {bias_voltage}\\n\"\n", " f\"• acquisition_rate: {acq_rate}\\n• gain_setting: {gain_setting}\\n\"\n", @@ -673,14 +664,12 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "mnames=[]\n", "for i in modules:\n", - " qm = f\"Q{i//4+1}M{i % 4+1}\"\n", + " qm = module_index_to_qm(i)\n", " mnames.append(qm)\n", " display(Markdown(f'## Position of the module {qm} and its ASICs##'))\n", "show_processed_modules(dinstance, constants=None, mnames=mnames, mode=\"position\")" @@ -705,13 +694,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "ExecuteTime": { - "end_time": "2018-12-06T09:49:14.540552Z", - "start_time": "2018-12-06T09:49:13.009674Z" - }, - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "cell = 3\n", @@ -729,9 +712,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "cell = 3\n", @@ -749,9 +730,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": true - }, + "metadata": {}, "outputs": [], "source": [ "cell = 3\n", @@ -762,9 +741,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "cols = {BadPixels.NOISE_OUT_OF_THRESHOLD.value: (BadPixels.NOISE_OUT_OF_THRESHOLD.name, '#FF000080'),\n", @@ -772,18 +749,18 @@ " BadPixels.OFFSET_OUT_OF_THRESHOLD.value: (BadPixels.OFFSET_OUT_OF_THRESHOLD.name, '#00FF0080'),\n", " BadPixels.GAIN_THRESHOLDING_ERROR.value: (BadPixels.GAIN_THRESHOLDING_ERROR.name, '#FF40FF40'),\n", " BadPixels.OFFSET_OUT_OF_THRESHOLD.value | BadPixels.NOISE_OUT_OF_THRESHOLD.value: ('OFFSET_OUT_OF_THRESHOLD + NOISE_OUT_OF_THRESHOLD', '#DD00DD80'),\n", - " BadPixels.OFFSET_OUT_OF_THRESHOLD.value | BadPixels.NOISE_OUT_OF_THRESHOLD.value | \n", + " BadPixels.OFFSET_OUT_OF_THRESHOLD.value | BadPixels.NOISE_OUT_OF_THRESHOLD.value |\n", " BadPixels.GAIN_THRESHOLDING_ERROR.value: ('MIXED', '#BFDF009F')}\n", "\n", "if high_res_badpix_3d:\n", " display(Markdown(\"\"\"\n", - " \n", + "\n", " ## Global Bad Pixel Behaviour ##\n", "\n", - " The following plots show the results of bad pixel evaluation for all evaluated memory cells. \n", - " Cells are stacked in the Z-dimension, while pixels values in x/y are rebinned with a factor of 2. \n", - " This excludes single bad pixels present only in disconnected pixels. \n", - " Hence, any bad pixels spanning at least 4 pixels in the x/y-plane, or across at least two memory cells are indicated. \n", + " The following plots show the results of bad pixel evaluation for all evaluated memory cells.\n", + " Cells are stacked in the Z-dimension, while pixels values in x/y are rebinned with a factor of 2.\n", + " This excludes single bad pixels present only in disconnected pixels.\n", + " Hence, any bad pixels spanning at least 4 pixels in the x/y-plane, or across at least two memory cells are indicated.\n", " Colors encode the bad pixel type, or mixed type.\n", "\n", " \"\"\"))\n", @@ -808,9 +785,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "create_constant_overview(offset_g, \"Offset (ADU)\", max_cells, 4000, 8000,\n", @@ -820,9 +795,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "create_constant_overview(noise_g, \"Noise (ADU)\", max_cells, 0, 100,\n", @@ -832,24 +805,23 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ - "# Plot only three gain threshold maps.\n", - "bp_thresh = OrderedDict()\n", - "for mod, con in badpix_g.items():\n", - " bp_thresh[mod] = np.zeros((con.shape[0], con.shape[1], con.shape[2], 5), dtype=con.dtype)\n", - " bp_thresh[mod][...,:2] = con[...,:2]\n", - " bp_thresh[mod][...,2:] = con\n", + "if not fixed_gain_mode:\n", + " # Plot only three gain threshold maps.\n", + " bp_thresh = OrderedDict()\n", + " for mod, con in badpix_g.items():\n", + " bp_thresh[mod] = np.zeros((con.shape[0], con.shape[1], con.shape[2], 5), dtype=con.dtype)\n", + " bp_thresh[mod][...,:2] = con[...,:2]\n", + " bp_thresh[mod][...,2:] = con\n", "\n", "\n", - "create_constant_overview(thresholds_g, \"Threshold (ADU)\", max_cells, 4000, 10000, 5,\n", - " badpixels=[bp_thresh, np.nan],\n", - " gmap=['HG-MG Threshold', 'MG-LG Threshold', 'High gain', 'Medium gain', 'low gain'],\n", - " marker=['d','d','','','']\n", - " )" + " create_constant_overview(thresholds_g, \"Threshold (ADU)\", max_cells, 4000, 10000, 5,\n", + " badpixels=[bp_thresh, np.nan],\n", + " gmap=['HG-MG Threshold', 'MG-LG Threshold', 'High gain', 'Medium gain', 'low gain'],\n", + " marker=['d','d','','','']\n", + " )" ] }, { @@ -901,10 +873,10 @@ " for bit in bits:\n", " l_data_old.append(np.count_nonzero(old_const['BadPixelsDark'][:, :, :, gain] & bit.value))\n", "\n", - " l_data_name = ['All bad pixels', 'NOISE_OUT_OF_THRESHOLD', \n", + " l_data_name = ['All bad pixels', 'NOISE_OUT_OF_THRESHOLD',\n", " 'OFFSET_OUT_OF_THRESHOLD', 'OFFSET_NOISE_EVAL_ERROR', 'GAIN_THRESHOLDING_ERROR']\n", "\n", - " l_threshold = ['', f'{thresholds_noise_sigma}' f'{thresholds_noise_hard[gain]}', \n", + " l_threshold = ['', f'{thresholds_noise_sigma}' f'{thresholds_noise_hard[gain]}',\n", " f'{thresholds_offset_sigma}' f'{thresholds_offset_hard[gain]}',\n", " '', f'{thresholds_gain_sigma}']\n", "\n", @@ -927,9 +899,9 @@ "\n", "'''))\n", "if len(table)>0:\n", - " md = display(Latex(tabulate.tabulate(table, tablefmt='latex', \n", - " headers=[\"Pixel type\", \"Threshold\", \n", - " \"New constant\", \"Old constant\"]))) " + " md = display(Latex(tabulate.tabulate(table, tablefmt='latex',\n", + " headers=[\"Pixel type\", \"Threshold\",\n", + " \"New constant\", \"Old constant\"])))" ] }, { @@ -938,20 +910,27 @@ "metadata": {}, "outputs": [], "source": [ - "header = ['Parameter', \n", - " \"New constant\", \"Old constant \", \n", - " \"New constant\", \"Old constant \", \n", + "header = ['Parameter',\n", + " \"New constant\", \"Old constant \",\n", + " \"New constant\", \"Old constant \",\n", " \"New constant\", \"Old constant \",\n", " \"New constant\", \"Old constant \"]\n", "\n", - "for const in ['Offset', 'Noise', 'ThresholdsDark']:\n", - " if const != 'ThresholdsDark':\n", - " table = [['','High gain', 'High gain', 'Medium gain', 'Medium gain', 'Low gain', 'Low gain']]\n", - " else:\n", + "if fixed_gain_mode:\n", + " constants = ['Offset', 'Noise']\n", + "else:\n", + " constants = ['Offset', 'Noise', 'ThresholdsDark']\n", + "\n", + "for const in constants:\n", + "\n", + " if const == 'ThresholdsDark':\n", " table = [['','HG-MG threshold', 'HG-MG threshold', 'MG-LG threshold', 'MG-LG threshold']]\n", - " for qm in res.keys():\n", + " else:\n", + " table = [['','High gain', 'High gain', 'Medium gain', 'Medium gain', 'Low gain', 'Low gain']]\n", "\n", + " for qm in res.keys():\n", " data = np.copy(res[qm][const])\n", + "\n", " if const == 'ThresholdsDark':\n", " data[...,0][res[qm]['BadPixelsDark'][...,0]>0] = np.nan\n", " data[...,1][res[qm]['BadPixelsDark'][...,1]>0] = np.nan\n", @@ -984,15 +963,8 @@ " table.append(line)\n", "\n", " display(Markdown('### {} [ADU], good pixels only ###'.format(const)))\n", - " md = display(Latex(tabulate.tabulate(table, tablefmt='latex', headers=header))) " + " md = display(Latex(tabulate.tabulate(table, tablefmt='latex', headers=header)))" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -1015,5 +987,5 @@ } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 4 } diff --git a/setup.py b/setup.py index 2e1236355e348595f1d11d4b53ef3909ea6db652..f4310aefeec6475f4a78c47f3bc2160a5cb5a87f 100644 --- a/setup.py +++ b/setup.py @@ -79,7 +79,7 @@ setup( }, ext_modules=extensions, install_requires=[ - "iCalibrationDB @ git+ssh://git@git.xfel.eu:10022/detectors/cal_db_interactive.git@2.0.1", # noqa + "iCalibrationDB @ git+ssh://git@git.xfel.eu:10022/detectors/cal_db_interactive.git@2.0.4", # noqa "nbparameterise @ git+ssh://git@git.xfel.eu:10022/detectors/nbparameterise.git@0.3", # noqa "XFELDetectorAnalysis @ git+ssh://git@git.xfel.eu:10022/karaboDevices/pyDetLib.git@2.5.6-2.10.0#subdirectory=lib", # noqa "Cython==0.29.21", diff --git a/tests/test_cal_tools.py b/tests/test_cal_tools.py index 7fe26e12a9fb96a9a38f006311726da13ae46559..c7656d1770a4bec28f935eae2c282306940b1b60 100644 --- a/tests/test_cal_tools.py +++ b/tests/test_cal_tools.py @@ -3,7 +3,7 @@ from pathlib import Path import pytest from cal_tools.plotting import show_processed_modules -from cal_tools.tools import get_dir_creation_date +from cal_tools.tools import get_dir_creation_date, module_index_to_qm def test_show_processed_modules(): @@ -30,3 +30,19 @@ def test_dir_creation_date(): date = get_dir_creation_date(folder, 9999) assert isinstance(date, datetime) assert str(date) == '2019-12-16 08:52:25.196603' + + +def test_module_index_to_qm(): + + assert module_index_to_qm(0) == 'Q1M1' + assert module_index_to_qm(1) == 'Q1M2' + assert module_index_to_qm(4) == 'Q2M1' + assert module_index_to_qm(6) == 'Q2M3' + + assert module_index_to_qm(4, 5) == 'Q5M1' + + with pytest.raises(AssertionError): + module_index_to_qm(18) + + with pytest.raises(AssertionError): + module_index_to_qm(7, 5)