diff --git a/notebooks/Jungfrau/Jungfrau_Gain_Correct_and_Verify_NBC.ipynb b/notebooks/Jungfrau/Jungfrau_Gain_Correct_and_Verify_NBC.ipynb index aed7d9f9684891c1aff3b330e84b5ea124897003..ba512df7877a8868ae79454b68e80a610bb9ca1c 100644 --- a/notebooks/Jungfrau/Jungfrau_Gain_Correct_and_Verify_NBC.ipynb +++ b/notebooks/Jungfrau/Jungfrau_Gain_Correct_and_Verify_NBC.ipynb @@ -39,7 +39,7 @@ "\n", "# Parameters affecting corrected data.\n", "relative_gain = True # do relative gain correction.\n", - "strixel_sensor = False # reordering for strixel detector layout.\n", + "strixel_sensor = \"\" # reordering for strixel detector layout. Possible strixels to choose from are A0123 and A1256.\n", "strixel_double_norm = 2.0 # normalization to use for double-size pixels, only applied for strixel sensors.\n", "limit_trains = 0 # ONLY FOR TESTING. process only first N trains, Use 0 to process all.\n", "chunks_ids = 32 # HDF chunk size for memoryCell and frameNumber.\n", @@ -95,7 +95,7 @@ "from cal_tools.calcat_interface import JUNGFRAU_CalibrationData\n", "from cal_tools.enums import BadPixels\n", "from cal_tools.files import DataFile\n", - "from cal_tools.jungfraulib import JungfrauCtrl\n", + "from cal_tools.jungfrau.jungfraulib import JungfrauCtrl\n", "from cal_tools.plotting import init_jungfrau_geom\n", "from cal_tools.step_timing import StepTimer\n", "from cal_tools.tools import (\n", @@ -382,8 +382,13 @@ "outputs": [], "source": [ "if strixel_sensor:\n", - " from cal_tools.jfstrixel import STRIXEL_SHAPE as strixel_frame_shape, double_pixel_indices, to_strixel\n", - " Ydouble, Xdouble = double_pixel_indices()\n", + " from cal_tools.jungfrau.jfstrixel import get_strixel_parameters, to_strixel\n", + " strx_params = get_strixel_parameters(strixel_sensor)\n", + "\n", + " strixel_shape = strx_params[\"frame_shape\"]\n", + " Ydouble = strx_params.get(\"ydouble\", slice(None))\n", + " Xdouble = strx_params.get(\"xdouble\", slice(None))\n", + "\n", " print('Strixel sensor transformation enabled')" ] }, @@ -400,7 +405,7 @@ " \n", " # Copy gain over first to keep it at the original 3 for low gain.\n", " if strixel_sensor:\n", - " to_strixel(g, out=gain_corr[index, ...])\n", + " to_strixel(g, out=gain_corr[index, ...], kind=strixel_sensor)\n", " else:\n", " gain_corr[index, ...] = g\n", "\n", @@ -457,9 +462,9 @@ " msk = np.choose(g, np.moveaxis(mask_cell, -1, 0))\n", "\n", " if strixel_sensor:\n", - " to_strixel(d, out=data_corr[index, ...])\n", + " to_strixel(d, out=data_corr[index, ...], kind=strixel_sensor)\n", " data_corr[index, :, Ydouble, Xdouble] /= strixel_double_norm\n", - " to_strixel(msk, out=mask_corr[index, ...])\n", + " to_strixel(msk, out=mask_corr[index, ...], kind=strixel_sensor)\n", " else:\n", " data_corr[index, ...] = d\n", " mask_corr[index, ...] = msk" @@ -583,7 +588,7 @@ " \n", " # Determine total output shape.\n", " if strixel_sensor:\n", - " oshape = (*ishape[:-2], *strixel_frame_shape)\n", + " oshape = (*ishape[:-2], *strixel_shape)\n", " else:\n", " oshape = ishape\n", "\n", @@ -812,6 +817,12 @@ " vmin=_corrected_vmin, vmax=_corrected_vmax, cmap=\"jet\"\n", ")\n", "\n", + "if strixel_sensor:\n", + " if strixel_sensor == \"A1256\":\n", + " aspect = 1/3\n", + " else: # A0123\n", + " aspect = 10\n", + "\n", "if not strixel_sensor:\n", " geom.plot_data_fast(\n", " corrected_mean,\n", @@ -820,8 +831,9 @@ " **mean_plot_kwargs\n", " )\n", "else:\n", - " ax.imshow(corrected_mean.squeeze(), aspect=10, **mean_plot_kwargs)\n", - " \n", + " corr = ax.imshow(corrected_mean.squeeze(), aspect=aspect, **mean_plot_kwargs)\n", + " plt.colorbar(corr)\n", + "\n", "ax.set_title(f'{karabo_id} - Mean CORRECTED', size=18)\n", "\n", "plt.show()" @@ -847,7 +859,8 @@ " **mean_plot_kwargs\n", " )\n", "else:\n", - " ax.imshow(corrected_mean.squeeze(), aspect=10, **mean_plot_kwargs)\n", + " corr = ax.imshow(corrected_masked_mean.squeeze(), aspect=aspect, **mean_plot_kwargs)\n", + " plt.colorbar(corr)\n", "\n", "ax.set_title(f'{karabo_id} - Mean CORRECTED with mask', size=18)\n", "\n", @@ -878,7 +891,8 @@ " **single_plot_kwargs\n", " )\n", "else:\n", - " ax.imshow(corrected_train.squeeze(), aspect=10, **single_plot_kwargs)\n", + " corr = ax.imshow(corrected_train.squeeze(), aspect=aspect, **single_plot_kwargs)\n", + " plt.colorbar(corr)\n", "\n", "ax.set_title(f\"{karabo_id} - CORRECTED train: {tid}\", size=18)\n", "\n", @@ -1048,7 +1062,9 @@ " colorbar={'shrink': 1, 'pad': 0.01},\n", " )\n", "else:\n", - " ax.imshow(np.log2(mask_train).squeeze(), vmin=0, vmax=32, cmap='jet', aspect=10)\n", + " mask = ax.imshow(\n", + " mask_train.squeeze(), vmin=0, vmax=32, cmap='jet', aspect=aspect)\n", + " plt.colorbar(mask)\n", "\n", "plt.show()" ] diff --git a/notebooks/Jungfrau/Jungfrau_dark_analysis_all_gains_burst_mode_NBC.ipynb b/notebooks/Jungfrau/Jungfrau_dark_analysis_all_gains_burst_mode_NBC.ipynb index 5a8fc2d9917208951302c01a412d9ef24a84769a..e30b4be5656909ed64f1f102242fa7bc361f8907 100644 --- a/notebooks/Jungfrau/Jungfrau_dark_analysis_all_gains_burst_mode_NBC.ipynb +++ b/notebooks/Jungfrau/Jungfrau_dark_analysis_all_gains_burst_mode_NBC.ipynb @@ -90,7 +90,8 @@ "\n", "from XFELDetAna.plotting.heatmap import heatmapPlot\n", "from XFELDetAna.plotting.histogram import histPlot\n", - "from cal_tools import jungfraulib, step_timing\n", + "from cal_tools import step_timing\n", + "from cal_tools.jungfrau import jungfraulib\n", "from cal_tools.enums import BadPixels, JungfrauGainMode\n", "from cal_tools.tools import (\n", " get_dir_creation_date,\n", diff --git a/src/cal_tools/jfstrixel.py b/src/cal_tools/jfstrixel.py deleted file mode 100644 index 189b036f6cddf4aaa8ba802e908a999afb6cdfe3..0000000000000000000000000000000000000000 --- a/src/cal_tools/jfstrixel.py +++ /dev/null @@ -1,161 +0,0 @@ - -import numpy as np - - -REGULAR_SHAPE = (512, 1024) -STRIXEL_SHAPE = (86, 3090) - - -def _normal_indices(): - """Build normal size pixel indices.""" - - # Normal pixels - yin = np.arange(256) - xin = np.arange(1024) - - Yin, Xin = np.meshgrid(yin, xin) - Yout, Xout = np.meshgrid(yin // 3, (xin // 256 * 774) + (xin % 256) * 3) - Xout += (yin % 3).astype(int)[None, :] - - return Yout, Xout, Yin, Xin - - -def _gap_indices(in_gap_offset=0, out_gap_offset=0, - xout_factor=+1, yout_offset=0): - """Build one half of double size gap pixel indices.""" - - igap = np.arange(3) - yin = np.arange(256) - - Yin, Xin = np.meshgrid(yin, igap * 256 + 255 + in_gap_offset) - Yout, Xout = np.meshgrid(yin // 6 * 2, igap * 774 + 765 + out_gap_offset) - Xout += xout_factor * (yin % 6).astype(int)[None, :] - Yout += yout_offset - - return Yout, Xout, Yin, Xin - - -def transformation_indices2d(): - """Build 2D strixel transformation index arrays.""" - - # Each of this index sets contains four 2D index arrays - # Yout, Xout, Yin, Xin from different parts constituting the full - # strixel frame. They are each concatenated across these parts into - # four final index arrays to be used for translating between the - # regular frame and the strixel frame. - index_sets = [ - _normal_indices(), - - # Left gap - _gap_indices(0, 0, +1, 0), _gap_indices(0, 0, +1, 1), - - # Right gap - _gap_indices(1, 11, -1, 0), _gap_indices(1, 11, -1, 1) - ] - - # Yout, Xout, Yin, Xin - # Casting to int64 improves indexing performance by up to 30%. - return [np.concatenate(index_set).astype(np.int64) - for index_set in zip(*index_sets)] - - -def transformation_indices1d(): - """Build 1D strixel transformation index arrays. - - Internally this function reduces the 2D index arrays to a single - dimension to operate on raveled data arrays. This improves the - transformation performance substantially by up to 3x. - """ - - Yout, Xout, Yin, Xin = transformation_indices2d() - - regular_pixel_idx = np.arange(np.prod(REGULAR_SHAPE), dtype=np.uint32) \ - .reshape(REGULAR_SHAPE) - strixel_pixel_idx = np.empty(STRIXEL_SHAPE, dtype=np.int64) - strixel_pixel_idx.fill(-1) - strixel_pixel_idx[Yout, Xout] = regular_pixel_idx[Yin, Xin] - - Iout = np.where(strixel_pixel_idx.ravel() != -1)[0].astype(np.int64) - Iin = strixel_pixel_idx.ravel()[Iout].astype(np.int64) - - return Iout, Iin - - -def double_pixel_indices(): - """Build index arrays for double-size pixels. - - In raw data, the entire columns 255, 256, 511, 512, 767 and 768 - are double-size pixels. After strixelation, these end up in columns - 765-776, 1539-1550 and 2313-2324 on rows 0-85 or 0-83, with a set - of four columns with 86 rows followed by a set of 84 and 86 again. - - This function builds the index arrays for double pixels after - strixelation. - - Returns: - (ndarray, ndarray) 2D index arrays for double pixel Y and X. - """ - - Ydouble = [] - Xdouble = [] - - for double_col in [765, 1539, 2313]: - for col in range(double_col, double_col+12): - for row in range(84 if ((col-double_col) // 4) == 1 else 86): - Ydouble.append(row) - Xdouble.append(col) - - return np.array(Ydouble), np.array(Xdouble) - - -def to_strixel(data, out=None): - """Transform from regular to strixel geometry. - - Only the last two axes are considered for transformation, input data - may have any number of additional axes in front. - - Args: - data (array_like): Data in regular geometry. - out (array_like, optional): Buffer for transformed output, a new - one is allocated if omitted. Must match all non-frame axes - of input data and able to hold strixel frame. - - Returns: - (array_like) Data in strixel geometry. - """ - - if out is None: - out = np.zeros((*data.shape[:-2], *STRIXEL_SHAPE), dtype=data.dtype) - - out.reshape(*out.shape[:-2], -1)[..., Iout] = data.reshape( - *data.shape[:-2], -1)[..., Iin] - - return out - - -def from_strixel(data, out=None): - """Transform from strixel to regular geometry. - - Only the last two axes are considered for transformation, input data - may have any number of additional axes in front. - - Args: - data (array_like): Data in strixel geometry. - out (array_like, optional): Buffer for transformed output, a new - one is allocated if omitted. Must match all non-frame axes - of input data and able to hold regular frame. - - Returns: - (array_like): Data in regular geometry. - """ - - if out is None: - out = np.zeros((*data.shape[:-2], *REGULAR_SHAPE), dtype=data.dtype) - - out.reshape(*out.shape[:-2], -1)[..., Iin] = data.reshape( - *data.shape[:-2], -1)[..., Iout] - - return out - - -Iout, Iin = transformation_indices1d() diff --git a/src/cal_tools/jungfrau/__init__.py b/src/cal_tools/jungfrau/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/cal_tools/jungfrau/jfstrixel.py b/src/cal_tools/jungfrau/jfstrixel.py new file mode 100644 index 0000000000000000000000000000000000000000..b388e5063430295174da65bbdee4a53476073796 --- /dev/null +++ b/src/cal_tools/jungfrau/jfstrixel.py @@ -0,0 +1,120 @@ + +from functools import lru_cache +from pathlib import Path + +import numpy as np + +REGULAR_SHAPE = (512, 1024) +DIR_PATH = package_directory = Path(__file__).resolve().parent + + +@lru_cache +def get_strixel_parameters(kind): + """Returns a dictionary of strixel parameters stored in .npz file + based on the given kind. + + Args: + kind (str): Specifies the type of strixel parameters to retrieve. + There is two possible values: "A0123" or "A1256" + Returns: + (dict): Dictionary contating the strixel parameters. + """ + strx_parameters = {} + + if kind == "A0123": + file_path = DIR_PATH / "strixel_cols_A0123-lut_mask.npz" + elif kind == "A1256": + file_path = DIR_PATH / "strixel_rows_A1256-lut_mask.npz" + + with np.load(file_path) as data: + for k in data.files: + strx_parameters[k] = data[k] + + return strx_parameters + + +def store_double_pixel_indices(): + """Build index arrays for double-size pixels. + + In raw data for A0123 strixel detector, + the entire columns 255, 256, 511, 512, 767 and 768 + are double-size pixels. After strixelation, these end up in columns + 765-776, 1539-1550 and 2313-2324 on rows 0-85 or 0-83, with a set + of four columns with 86 rows followed by a set of 84 and 86 again. + + This function builds the index arrays for double pixels after + strixelation and stores it in the available A0123 .npz file. + """ + + ydouble = [] + xdouble = [] + file_path = DIR_PATH / "strixel_cols_A0123-lut_mask.npz" + + with np.load(file_path) as data: + for double_col in [765, 1539, 2313]: + for col in range(double_col, double_col+12): + for row in range(84 if ((col-double_col) // 4) == 1 else 86): + ydouble.append(row) + xdouble.append(col) + np.savez(file_path, **data, ydouble=ydouble, xdouble=xdouble) + + +def to_strixel(data, out=None, kind="A0123"): + """Transform from regular to strixel geometry. + + Only the last two axes are considered for transformation, input data + may have any number of additional axes in front. + + Args: + data (array_like): Data in regular geometry. + out (array_like, optional): Buffer for transformed output, a new + one is allocated if omitted. Must match all non-frame axes + of input data and able to hold strixel frame. + + Returns: + (array_like) Data in strixel geometry. + """ + + if kind is None: + return data + + strx = get_strixel_parameters(kind) + + if out is None: + out = np.zeros( + (*data.shape[:-2], *strx["frame_shape"]), dtype=data.dtype) + + out.reshape(*out.shape[:-2], -1)[..., ~strx["mask"]] = data.reshape( + *data.shape[:-2], -1)[..., strx["lut"]] + + return out + + +def from_strixel(data, out=None, kind="A0123"): + """Transform from strixel to regular geometry. + + Only the last two axes are considered for transformation, input data + may have any number of additional axes in front. + + Args: + data (array_like): Data in strixel geometry. + out (array_like, optional): Buffer for transformed output, a new + one is allocated if omitted. Must match all non-frame axes + of input data and able to hold regular frame. + + Returns: + (array_like): Data in regular geometry. + """ + + if kind is None: + return data + + strx = get_strixel_parameters(kind) + + if out is None: + out = np.zeros((*data.shape[:-2], *REGULAR_SHAPE), dtype=data.dtype) + + out.reshape(*out.shape[:-2], -1)[..., strx["lut"]] = data.reshape( + *data.shape[:-2], -1)[..., strx["mask"]] + + return out diff --git a/src/cal_tools/jungfraulib.py b/src/cal_tools/jungfrau/jungfraulib.py similarity index 100% rename from src/cal_tools/jungfraulib.py rename to src/cal_tools/jungfrau/jungfraulib.py diff --git a/src/cal_tools/jungfrau/strixel_cols_A0123-lut_mask.npz b/src/cal_tools/jungfrau/strixel_cols_A0123-lut_mask.npz new file mode 100644 index 0000000000000000000000000000000000000000..fcf08e279709b7a8a64c858fdd932d3c7738c172 Binary files /dev/null and b/src/cal_tools/jungfrau/strixel_cols_A0123-lut_mask.npz differ diff --git a/src/cal_tools/jungfrau/strixel_rows_A1256-lut_mask.npz b/src/cal_tools/jungfrau/strixel_rows_A1256-lut_mask.npz new file mode 100644 index 0000000000000000000000000000000000000000..be31b53bb7bb0fe2cf1060468e21438793c33dd3 Binary files /dev/null and b/src/cal_tools/jungfrau/strixel_rows_A1256-lut_mask.npz differ diff --git a/tests/test_jungfraulib.py b/tests/test_jungfraulib.py index c7fb5258ba2c0035cbad2668e4bdc35131866855..00a3ae5b9f2e4761360733d95f62e308da08016e 100644 --- a/tests/test_jungfraulib.py +++ b/tests/test_jungfraulib.py @@ -1,7 +1,7 @@ import pytest from extra_data import RunDirectory -from cal_tools.jungfraulib import JungfrauCtrl, sort_runs_by_gain +from cal_tools.jungfrau.jungfraulib import JungfrauCtrl, sort_runs_by_gain # TODO: replace with mocked RAW data as in tests/test_agipdlib.py JF = JungfrauCtrl(