From 4d18f67ea95c047158b4500211d06a3b94b7d79b Mon Sep 17 00:00:00 2001
From: ahmedk <karim.ahmed@xfel.eu>
Date: Thu, 21 Nov 2024 11:15:31 +0100
Subject: [PATCH] change names detector_type to sensor_type and add validation
 in get_sensor_type

---
 .../Characterize_Darks_Gotthard2_NBC.ipynb    |  8 +++----
 .../Gotthard2/Correction_Gotthard2_NBC.ipynb  | 24 +++++++++----------
 .../Summary_Darks_Gotthard2_NBC.ipynb         | 11 ++++-----
 src/cal_tools/gotthard2/gotthard2lib.py       | 23 ++++++++++++++----
 4 files changed, 39 insertions(+), 27 deletions(-)

diff --git a/notebooks/Gotthard2/Characterize_Darks_Gotthard2_NBC.ipynb b/notebooks/Gotthard2/Characterize_Darks_Gotthard2_NBC.ipynb
index e7dbfc9cd..bf2384afb 100644
--- a/notebooks/Gotthard2/Characterize_Darks_Gotthard2_NBC.ipynb
+++ b/notebooks/Gotthard2/Characterize_Darks_Gotthard2_NBC.ipynb
@@ -52,7 +52,7 @@
     "acquisition_rate = -1.  # Detector acquisition rate (1.1/4.5), set to -1 to use value in raw file.\n",
     "single_photon = -1  # Detector single photon mode (High/Low CDS), set to -1 to use value in raw file.\n",
     "# This is used for the summary notebook\n",
-    "gh2_detector = \"\"  # Specifies the GH2 detector type ('50um' or '25um'). This parameter can be used to manually set the detector type if the code cannot automatically determine it from the control data.\n",
+    "sensor_type = \"\"  # Specifies the GH2 sensor type: '50um', '25um', or \"\" to automatically decide from RUN data.\n",
     "\n",
     "# Parameters used for calculating calibration constants\n",
     "bpix_noise_nitrs = 5  # number of maximum iterations to actively try to reach correct number of badpixels of NOISE_OUT_OF_THRESHOLD type. Leave -1 to keep iterating until finding maximum number of badpixels.\n",
@@ -208,9 +208,9 @@
     "print(\"Single photon: \", single_photon)\n",
     "acquisition_rate = conditions[\"acquisition_rate\"].pop()\n",
     "print(\"Acquisition rate: \", acquisition_rate)\n",
-    "if not gh2_detector:\n",
-    "    gh2_detector = g2ctrl.get_det_type()\n",
-    "print(f\"Processing {gh2_detector} Gotthard2.\")"
+    "if not sensor_type:\n",
+    "    sensor_type = g2ctrl.get_sensor_type()\n",
+    "print(f\"Processing {sensor_type} Gotthard2.\")"
    ]
   },
   {
diff --git a/notebooks/Gotthard2/Correction_Gotthard2_NBC.ipynb b/notebooks/Gotthard2/Correction_Gotthard2_NBC.ipynb
index 022c74bff..478719287 100644
--- a/notebooks/Gotthard2/Correction_Gotthard2_NBC.ipynb
+++ b/notebooks/Gotthard2/Correction_Gotthard2_NBC.ipynb
@@ -84,7 +84,7 @@
     "acquisition_rate = -1.  # Detector acquisition rate (1.1/4.5), set to -1 to use value in raw file.\n",
     "single_photon = -1  # Detector single photon mode (High/Low CDS), set to -1 to use value in raw file.\n",
     "reverse_second_module = -1  # Reverse 25um GH2 second module before interleaving. set to -1 to use value in raw file to reverse based on `CTRL/reverseSlaveReadOutMode`'s value.\n",
-    "gh2_detector = \"\"  # Specifies the GH2 detector type ('50um' or '25um'). This parameter can be used to manually set the detector type if the code cannot automatically determine it from the control data.\n",
+    "sensor_type = \"\"  # Specifies the GH2 sensor type: '50um', '25um', or \"\" to automatically decide from RUN data.\n",
     "\n",
     "# Parameters for plotting\n",
     "skip_plots = False  # exit after writing corrected files\n",
@@ -189,17 +189,17 @@
     "    acquisition_rate = g2ctrl.get_acquisition_rate()\n",
     "if single_photon == -1:\n",
     "    single_photon = g2ctrl.get_single_photon()\n",
-    "if not gh2_detector:\n",
-    "    gh2_detector = g2ctrl.get_det_type()\n",
+    "if not sensor_type:\n",
+    "    sensor_type = g2ctrl.get_sensor_type()\n",
     "\n",
     "# Check if the GH2 detector type is \"50um\" and validate that only one data aggregator is provided\n",
-    "if gh2_detector == \"50um\" and len(karabo_da) > 1:\n",
+    "if sensor_type == \"50um\" and len(karabo_da) > 1:\n",
     "    raise ValueError(\n",
     "        f\"karabo_da has more than one aggregator {karabo_da}, \"\n",
     "        \"but the GH2 detector type is specified as '50um', which expects only one aggregator.\"\n",
     "    )\n",
     "\n",
-    "if reverse_second_module == -1 and gh2_detector == \"25um\":\n",
+    "if reverse_second_module == -1 and sensor_type == \"25um\":\n",
     "    reverse_second_module = not g2ctrl.second_module_reversed()\n",
     "    if reverse_second_module:\n",
     "        warning(\n",
@@ -211,7 +211,7 @@
     "print(\"Exposure Period:\", exposure_period)\n",
     "print(\"Acquisition Rate:\", acquisition_rate)\n",
     "print(\"Single Photon:\", single_photon)\n",
-    "print(f\"Processing {gh2_detector} Gotthard2.\")"
+    "print(f\"Processing {sensor_type} Gotthard2.\")"
    ]
   },
   {
@@ -444,7 +444,7 @@
     "        print(f\"Correcting {n_trains} for {raw_file}.\")\n",
     "\n",
     "    # Initialize GH2 data and gain arrays to store in corrected files.\n",
-    "    if gh2_detector == \"25um\":\n",
+    "    if sensor_type == \"25um\":\n",
     "        dshape_stored = (dc[data_sources[0], \"data.adc\"].shape[:2] + (1280 * 2,))\n",
     "        data_stored = np.zeros(dshape_stored, dtype=np.float32)\n",
     "        gain_stored = np.zeros(dshape_stored, dtype=np.uint8)\n",
@@ -486,7 +486,7 @@
     "        # Create CORR files and add corrected data sections.\n",
     "        image_counts = dc[src, \"data.adc\"].data_counts(labelled=False)\n",
     "\n",
-    "        if gh2_detector == \"25um\":\n",
+    "        if sensor_type == \"25um\":\n",
     "            data_stored[..., i::2] = data_corr\n",
     "            gain_stored[..., i::2] = gain\n",
     "            mask_stored[..., i::2] = mask\n",
@@ -577,14 +577,14 @@
     "    )\n",
     "    mask = out_dc[corr_data_source, \"data.mask\"].train_from_id(tid)[1]\n",
     "\n",
-    "if gh2_detector == \"25um\":\n",
+    "if sensor_type == \"25um\":\n",
     "    mod_dcs[corr_data_source][\"train_raw_data\"] = np.zeros((data_corr.shape[1], 1280 * 2), dtype=np.float32)\n",
     "    mod_dcs[corr_data_source][\"train_raw_gain\"] = np.zeros((data_corr.shape[1], 1280 * 2), dtype=np.uint8)\n",
     "\n",
     "for i, src in enumerate(data_sources):\n",
     "    with H5File(first_seq_raw) as in_dc:\n",
     "        train_dict = in_dc.train_from_id(tid)[1][src]\n",
-    "        if gh2_detector == \"25um\":\n",
+    "        if sensor_type == \"25um\":\n",
     "            if reverse_second_module and i == 1:\n",
     "                data = np.flip(train_dict[\"data.adc\"], axis=-1)\n",
     "                gain = np.flip(train_dict[\"data.gain\"], axis=-1)\n",
@@ -621,8 +621,8 @@
     "display(Markdown(\"### Mean RAW and CORRECTED across pulses for one train:\"))\n",
     "display(Markdown(f\"Train: {tid}\"))\n",
     "\n",
-    "# Set title format based on gh2_detector\n",
-    "if gh2_detector == \"50um\":\n",
+    "# Set title format based on sensor_type\n",
+    "if sensor_type == \"50um\":\n",
     "    title_format = f\"{{}} data for {karabo_da} ({db_modules})\"\n",
     "else:\n",
     "    title_format = f\"Interleaved {{}} data for {karabo_da} ({db_modules})\"\n",
diff --git a/notebooks/Gotthard2/Summary_Darks_Gotthard2_NBC.ipynb b/notebooks/Gotthard2/Summary_Darks_Gotthard2_NBC.ipynb
index b4f6df31e..ecd41edee 100644
--- a/notebooks/Gotthard2/Summary_Darks_Gotthard2_NBC.ipynb
+++ b/notebooks/Gotthard2/Summary_Darks_Gotthard2_NBC.ipynb
@@ -31,7 +31,7 @@
     "ctrl_source_template = '{karabo_id}/DET/{control_template}'  # template for control source name (filled with karabo_id_control)\n",
     "karabo_id_control = \"\"  # Control karabo ID. Set to empty string to use the karabo-id\n",
     "\n",
-    "gh2_detector = \"\"  # Specifies the GH2 detector type ('50um' or '25um'). This parameter can be used to manually set the detector type if the code cannot automatically determine it from the control data.\n",
+    "sensor_type = \"\"  # Specifies the GH2 sensor type: '50um', '25um', or \"\" to automatically decide from RUN data.\n",
     "\n",
     "# Parameters to be used for injecting dark calibration constants.\n",
     "local_output = True  # Boolean indicating that local constants were stored in the out_folder\n",
@@ -93,12 +93,9 @@
     "        karabo_id_control=karabo_id_control,\n",
     "        control_template=control_template\n",
     "    ))\n",
-    "if not gh2_detector:\n",
-    "    # Unfortunately we can't check if we can't validate this value\n",
-    "    # as the notebook will have only one karabo_da anyway.\n",
-    "    # Dark processing split nodes over each karabo_da\n",
-    "    gh2_detector = g2ctrl.get_det_type()\n",
-    "print(f\"Processing {gh2_detector} Gotthard2.\")"
+    "if not sensor_type:\n",
+    "    sensor_type = g2ctrl.get_sensor_type()\n",
+    "print(f\"Processing {sensor_type} Gotthard2.\")"
    ]
   },
   {
diff --git a/src/cal_tools/gotthard2/gotthard2lib.py b/src/cal_tools/gotthard2/gotthard2lib.py
index e6a8267c8..e13e5ad1e 100644
--- a/src/cal_tools/gotthard2/gotthard2lib.py
+++ b/src/cal_tools/gotthard2/gotthard2lib.py
@@ -63,16 +63,31 @@ class Gotthard2Ctrl():
             return bool(
                 self.run_dc[self.ctrl_src, "singlePhoton"].as_single_value())
 
-    def get_det_type(self):
+    def get_sensor_type(self):
         """GH2 rxHostname is a vector of bytes objects.
         GH2 25um has two host names unlike 50um which has one.
 
         Returns:
             str: return if its a 25um or 50um detector.
         """
-
-        hostname = self.run_dc.get_run_value(self.ctrl_src, "rxHostname")
-        return "25um" if hostname[1].decode("utf-8") else "50um"
+        import numpy as np
+
+        hostnames = self.run_dc.get_run_value(self.ctrl_src, "rxHostname")
+        num_hosts = np.count_nonzero(hostnames != b'')
+        if num_hosts == 0:
+            raise ValueError(
+                "No hosts are present in RUN/rxHostname. "
+                "Unable to determine the sensor type for this data. ")
+
+        if num_hosts == 1:
+            return "50um"
+        elif num_hosts == 2:
+            return "25um"
+        else:
+            raise ValueError(
+                f"RUN/rxHostname has more host names ({num_hosts}) than expected. "
+                "Expected either 1 hostname for 50um or 2 host names for 25um."
+            )
 
     def second_module_reversed(self):
         """Check if reverseSlaveReadOutMode is True."""
-- 
GitLab