diff --git a/notebooks/AGIPD/Characterize_AGIPD_Gain_Darks_NBC.ipynb b/notebooks/AGIPD/Characterize_AGIPD_Gain_Darks_NBC.ipynb index 2bd52013ec6a1cd7a7acb855c444dca9990b5e3b..36d88640f1fb95559d49b23eb2e5ccd114342da9 100644 --- a/notebooks/AGIPD/Characterize_AGIPD_Gain_Darks_NBC.ipynb +++ b/notebooks/AGIPD/Characterize_AGIPD_Gain_Darks_NBC.ipynb @@ -40,6 +40,7 @@ "cal_db_timeout = 3000000 # timeout on caldb requests\"\n", "local_output = True # output constants locally\n", "db_output = False # output constants to database\n", + "sort_runs = True # Sort the selected dark runs. This flag is added for old data (e.g. 900174 r0011).\n", "\n", "mem_cells = 0 # number of memory cells used, set to 0 to automatically infer\n", "bias_voltage = 0 # bias voltage, set to 0 to use stored value in slow data.\n", @@ -95,7 +96,7 @@ "from collections import OrderedDict\n", "from datetime import timedelta\n", "from pathlib import Path\n", - "from typing import List, Tuple\n", + "from typing import Tuple\n", "\n", "import matplotlib\n", "import numpy as np\n", @@ -141,16 +142,8 @@ "# insert control device if format string (does nothing otherwise)\n", "ctrl_src = ctrl_source_template.format(karabo_id_control)\n", "\n", - "runs_dict = OrderedDict()\n", "run_numbers = [run_high, run_med, run_low]\n", "\n", - "for gain_idx, (run_name, run_number) in enumerate(zip([\"high\", \"med\", \"low\"], run_numbers)):\n", - " runs_dict[run_name] = {\n", - " \"number\": run_number,\n", - " \"gain\": gain_idx,\n", - " \"dc\": RunDirectory(f'{in_folder}/r{run_number:04d}/')\n", - " }\n", - "\n", "creation_time=None\n", "if use_dir_creation_date:\n", " creation_time = get_dir_creation_date(in_folder, run_high)\n", @@ -205,35 +198,35 @@ "Path(out_folder).mkdir(parents=True, exist_ok=True)\n", "\n", "mod_image_size = []\n", - "for run_dict in runs_dict.values():\n", + "for run in run_numbers:\n", " missing_modules = [] # modules with no images within a run.\n", " n_trains_list = [] # list of the number of trains for each module within a run.\n", " # This is important in case of no slurm parallelization over modules is done.\n", " # (e.g. running notebook interactively)\n", " for m in modules:\n", " # validate that there are trains for the selected modules and run.\n", - " dc = run_dict[\"dc\"].select(\n", + " dc = RunDirectory(f'{in_folder}/r{run:04d}/').select(\n", " instrument_src.format(m), \"*\", require_all=True)\n", " n_trains = len(dc.train_ids)\n", "\n", " if n_trains == 0:\n", - " print(f\"WARNING: No images for module AGIPD{m:02d}, run {run_dict['number']}.\")\n", + " print(f\"WARNING: No images for module AGIPD{m:02d}, run {run}.\")\n", " missing_modules.append(m)\n", " # Raise a warning if the module has less trains than expected.\n", " elif n_trains < min_trains:\n", - " print(f\"WARNING: AGIPD{m:02d}, run {run_dict['number']} \"\n", + " print(f\"WARNING: AGIPD{m:02d}, run {run} \"\n", " f\"has trains less than minimum trains: {min_trains}.\")\n", " else:\n", " print(f\"Processing {max_trains if max_trains < n_trains else n_trains} \"\n", - " f\"for AGIPD{m:02d}, run {run_dict['number']} \")\n", + " f\"for AGIPD{m:02d}, run {run} \")\n", "\n", " n_trains_list.append(n_trains)\n", " mod_image_size.append(np.product(dc[instrument_src.format(m), \"image.data\"].shape) * 2 / 1e9)\n", "\n", " if max(n_trains_list) == 0:\n", - " raise ValueError(f\"No images to process for run: {run_dict['number']}\")\n", + " raise ValueError(f\"No images to process for run: {run}\")\n", " elif max(n_trains_list) < min_trains:\n", - " raise ValueError(f\"{run_dict['number']} has less than minimum trains: {min_trains}\")\n", + " raise ValueError(f\"{run} has less than minimum trains: {min_trains}\")\n", "\n", "# Update modules and karabo_da lists based on available modules to processes.\n", "modules = [m for m in modules if m not in missing_modules]\n", @@ -264,10 +257,13 @@ "\n", "agipd_ctrl_dark = AgipdCtrlRuns(\n", " raw_folder=in_folder,\n", - " runs=[r[\"number\"] for r in runs_dict.values()],\n", + " runs=run_numbers,\n", " image_src=instrument_src_mod,\n", " ctrl_src=ctrl_src,\n", + " sort_dark_runs_enabled=sort_runs\n", ")\n", + "# Update run_numbers list in case it was sorted.\n", + "run_numbers = agipd_ctrl_dark.runs\n", "if mem_cells == 0:\n", " mem_cells = agipd_ctrl_dark.get_memory_cells()\n", "\n", @@ -278,7 +274,6 @@ " bias_voltage = agipd_ctrl_dark.get_bias_voltage(karabo_id_control)\n", "\n", "fixed_gain_mode = False\n", - "agipd_ctrl_dark.validate_gain_modes()\n", "if gain_mode == -1:\n", " gain_mode = agipd_ctrl_dark.gain_modes\n", " fixed_gain_mode = agipd_ctrl_dark.fixed_gain_mode()\n", @@ -386,16 +381,16 @@ "print(f\"Will use {parallel_num_procs} processes with {parallel_num_threads} threads each\")\n", "\n", "def characterize_module(\n", - " channel: int, runs_dict: dict,\n", + " channel: int, gain_run: Tuple[int, int],\n", ") -> Tuple[int, int, np.array, np.array, np.array, np.array, np.array]:\n", "\n", + " gain_index, run = gain_run\n", " # Select the corresponding module channel.\n", " instrument_src_mod = instrument_src.format(channel)\n", "\n", - " run_dc = runs_dict[\"dc\"].select(instrument_src_mod, require_all=True)\n", + " run_dc = RunDirectory(f'{in_folder}/r{run:04d}/').select(instrument_src_mod, require_all=True)\n", " if max_trains != 0:\n", " run_dc = run_dc.select_trains(np.s_[:max_trains])\n", - " gain_index = runs_dict[\"gain\"]\n", "\n", " # Read module's image and cellId data.\n", " im = run_dc[instrument_src_mod, \"image.data\"].ndarray()\n", @@ -478,16 +473,16 @@ "source": [ "with multiprocessing.Pool(processes=parallel_num_procs) as pool:\n", " results = pool.starmap(\n", - " characterize_module, itertools.product(modules, list(runs_dict.values())))\n", + " characterize_module, itertools.product(modules, list(enumerate(run_numbers))))\n", "\n", "# mapped values for processing 2 modules example:\n", - "# [\n", - "# 0, {\"gain\": 0, \"run_number\": <run-high>, \"dc\": <high-dc>},\n", - "# 0, {\"gain\": 1, \"run_number\": <run-med>, \"dc\": <med-dc>},\n", - "# 0, {\"gain\": 2, \"run_number\": <run-low>, \"dc\": <low-dc>},\n", - "# 1, {\"gain\": 0, \"run_number\": <run-high>, \"dc\": <high-dc>},\n", - "# 1, {\"gain\": 1, \"run_number\": <run-med>, \"dc\": <med-dc>},\n", - "# 1, {\"gain\": 2, \"run_number\": <run-low>, \"dc\": <low-dc>},\n", + "# [(0, (0, 9013))\n", + "# 0, (0, run-high),\n", + "# 0, (1, run-med),\n", + "# 0, (2, run-low),\n", + "# 1, (0, run-high),\n", + "# 1, (1, run-med),\n", + "# 1, (2, run-low),,\n", "# ]" ] }, diff --git a/src/cal_tools/agipdlib.py b/src/cal_tools/agipdlib.py index d743044f16e7015e7382164d13806b5770d1d171..6707c0d9bd8eeaa1ef04d5694912cb1cdfbc64c5 100644 --- a/src/cal_tools/agipdlib.py +++ b/src/cal_tools/agipdlib.py @@ -308,6 +308,7 @@ class AgipdCtrlRuns: runs: List[int] image_src: str ctrl_src: str + sort_dark_runs_enabled: bool = False adaptive_gain_modes = [AgipdGainMode.ADAPTIVE_GAIN] * 3 fixed_gain_modes = [ @@ -325,6 +326,8 @@ class AgipdCtrlRuns: ctrl_src=self.ctrl_src, ) for r in self.runs] self.gain_modes = self.get_gain_modes() + if self.sort_dark_runs_enabled: + self.sort_dark_runs() def _validate_same_value(self, name, values): if len(set(values)) != 1: @@ -334,29 +337,47 @@ class AgipdCtrlRuns: f" with values of {values}, respectively.") def sort_dark_runs(self): - assert len(self.run_ctrls) == 3, f"AGIPD dark runs are expected to be 3. {len(self.run_ctrls)} runs are given." # noqa - # TODO: complete sorting - - def validate_gain_modes(self): - """Validate the runs' gain modes arrangement. + """Order dark runs based on run patterns for Adaptive mode + or gain modes for Fixed mode. + """ + assert len(self.runs) == 3, f"AGIPD dark runs are expected to be 3. {len(self.runs)} runs are given." # noqa + # Expected patterns: + # XRay: 0, DarkHG: 1, DarkMG: 2, DarkLG: 3, PC: 4 and CS: 5. + sort_by = None + sort_values = [] + if self.gain_modes == self.adaptive_gain_modes: # Adaptive gain # sort by patterns + # Patterns -> DarkHG: 1, DarkMG: 2, DarkLG: 3 + if "AGIPD1M" in self.ctrl_src: + sort_by = "patternTypeIndex" + elif "AGIPD500K" in self.ctrl_src: + sort_by = "expTypeIndex" + + for c in self.run_ctrls: + sort_values.append( + c.run_dc[self.ctrl_src, sort_by].as_single_value()) - Raises: - ValueError: Runs are a mix of adaptive and fixed gains - ValueError: Unexpected gain modes for the dark runs""" - if self.gain_modes in [ - self.adaptive_gain_modes, - self.fixed_gain_modes - ]: - return # all good. # Check if a mix of adaptive and fixed gain runs. - if any(gm == AgipdGainMode.ADAPTIVE_GAIN for gm in self.gain_modes): + elif any(gm == AgipdGainMode.ADAPTIVE_GAIN for gm in self.gain_modes): raise ValueError( f"Given runs {self.runs} have a mix of ADAPTIVE and " f"FIXED gain modes: {self.gain_modes}.") - else: # Unsorted fixed gain runs. - raise ValueError( - "Wrong arrangement of given dark runs. Given runs' " - f"gain_modes are {self.gain_modes} for runs: {self.runs}.") + else: # Fixed gain: Patterns is X-Ray: 0 for all runs. + sort_by = "gainModeIndex" + sort_values = [int(gm) for gm in self.gain_modes] + + zipped_lists = zip(sort_values, self.runs, self.run_ctrls) + + # Sort the lists based on the patterns + sorted_zipped_lists = sorted(zipped_lists, key=lambda item: item[0]) + _, sorted_runs, sorted_run_ctrls = zip(*sorted_zipped_lists) + if sorted_runs != self.runs: + Warning("Given dark runs are unsorted. Runs will be sorted from" + f" {self.runs} with {sort_by}:" + f" {sort_values} to {sorted_runs}.") + # Update run_ctrls and runs order + self.runs = list(sorted_runs) + self.run_ctrls = list(sorted_run_ctrls) + self.gain_modes = self.get_gain_modes() def fixed_gain_mode(self): """Check if runs are in fixed gain mode. diff --git a/tests/test_agipdlib.py b/tests/test_agipdlib.py index 17f29f655170df2a4a26ccfcdf5952c006c63370..89ef4054df964fb3fc4b8a44f32b267dd66ac702 100644 --- a/tests/test_agipdlib.py +++ b/tests/test_agipdlib.py @@ -203,16 +203,12 @@ def test_get_gain_mode(mock_agipd1m_run): assert gain_mode == 0 -""" -Testing `AgipdCtrlRuns` +"""Testing `AgipdCtrlRuns`""" -Runs used: -FIXED: /gpfs/exfel/exp/CALLAB/202130/p900203/raw/[9011,9012,9013] -ADAPTIVE: /gpfs/exfel/exp/CALLAB/202130/p900203/raw/[9015,9016,9017] -""" TEST_RAW_FOLDER = "/gpfs/exfel/exp/CALLAB/202130/p900203/raw/" SPB_FIXED_RUNS = [9011, 9012, 9013] SPB_ADAPTIVE_RUNS = [9015, 9016, 9017] + FIXED_CTRL_RUNS = AgipdCtrlRuns( raw_folder=TEST_RAW_FOLDER, runs=SPB_FIXED_RUNS, @@ -294,21 +290,39 @@ def test_raise_fixed_gain_mode(): @pytest.mark.requires_gpfs -def test_raise_validate_gain_modes(): - adaptive_fixed_ctrls = AgipdCtrlRuns( +@pytest.mark.parametrize( + "runs,expected", + [ + ([9013, 9011, 9012], [9011, 9012, 9013]), + ([9017, 9016, 9015], [9015, 9016, 9017]), + ], +) +def test_sort_dark_runs(runs, expected): + runs_ctrls = AgipdCtrlRuns( raw_folder=TEST_RAW_FOLDER, - runs=[9011, 9016, 9017], + runs=runs, image_src=SPB_AGIPD_INST_SRC, ctrl_src=CTRL_SRC, ) + runs_ctrls.sort_dark_runs() + assert runs_ctrls.runs == expected + + +def test_raise_sort_dark_runs(): with pytest.raises(ValueError): - adaptive_fixed_ctrls.validate_gain_modes() + adaptive_fixed_ctrls = AgipdCtrlRuns( + raw_folder=TEST_RAW_FOLDER, + runs=[9011, 9016, 9017], + image_src=SPB_AGIPD_INST_SRC, + ctrl_src=CTRL_SRC, + sort_dark_runs_enabled=True + ) - unsorted_fixed_gain_ctrls = AgipdCtrlRuns( + adaptive_fixed_ctrls = AgipdCtrlRuns( raw_folder=TEST_RAW_FOLDER, - runs=[9013, 9011, 9012], + runs=[9011, 9016, 9017], image_src=SPB_AGIPD_INST_SRC, ctrl_src=CTRL_SRC, ) with pytest.raises(ValueError): - unsorted_fixed_gain_ctrls.validate_gain_modes() + adaptive_fixed_ctrls.sort_dark_runs() diff --git a/tests/test_reference_runs/callab_tests.py b/tests/test_reference_runs/callab_tests.py index 9e41d4c7768ca1c3e9f55703d0f6a9ae13290ed2..f16a1eba6ca7968690680e3f95d7eeecddac3dca 100644 --- a/tests/test_reference_runs/callab_tests.py +++ b/tests/test_reference_runs/callab_tests.py @@ -25,9 +25,10 @@ automated_test_config = { "out-folder": "{}/{}/{}", # "/gpfs/exfel/exp/SPB/202131/p900215/raw" "in-folder": "/gpfs/exfel/exp/CALLAB/202130/p900203/raw", - "run-high": "9011", # Original run: "91" + # Unsorted dark runs + "run-high": "9013", # Original run "93" "run-med": "9012", # Original run: "92" - "run-low": "9013", # Original run "93" + "run-low": "9011", # Original run: "91" "karabo-id-control": "SPB_IRU_AGIPD1M1", "karabo-id": "SPB_DET_AGIPD1M-1", "ctrl-source-template": "{}/MDL/FPGA_COMP", @@ -173,9 +174,10 @@ automated_test_config = { "out-folder": "{}/{}/{}", # "/gpfs/exfel/exp/HED/202131/p900228/raw" "in-folder": "/gpfs/exfel/exp/CALLAB/202130/p900203/raw", + # Unsorted dark runs "run-high": "9023", # Original run: "25", - "run-med": "9024", # Original run: "26", - "run-low": "9025", # Original run: "27", + "run-med": "9025", # Original run: "27", + "run-low": "9024", # Original run: "26", "karabo-id-control": "HED_EXP_AGIPD500K2G", "karabo-id": "HED_DET_AGIPD500K2G", "ctrl-source-template": "{}/MDL/FPGA_COMP",