diff --git a/notebooks/Gotthard2/Correction_Gotthard2_NBC.ipynb b/notebooks/Gotthard2/Correction_Gotthard2_NBC.ipynb index 9aa7bab8c50a44d7cdb511e5f501d3c3415fc838..621025e921ae29eb57967758c5fbe8a89672c5ce 100644 --- a/notebooks/Gotthard2/Correction_Gotthard2_NBC.ipynb +++ b/notebooks/Gotthard2/Correction_Gotthard2_NBC.ipynb @@ -35,23 +35,26 @@ "karabo_id_control = \"\" # Control karabo ID. Set to empty string to use the karabo-id\n", "\n", "# Parameters for calibration database.\n", - "use_dir_creation_date = True # use the creation data of the input dir for database queries\n", - "cal_db_interface = \"tcp://max-exfl017:8017#8025\" # the database interface to use\n", - "cal_db_timeout = 180000 # timeout on caldb requests\n", + "use_dir_creation_date = True # use the creation data of the input dir for database queries.\n", + "cal_db_interface = \"tcp://max-exfl017:8017#8025\" # the database interface to use.\n", + "cal_db_timeout = 180000 # timeout on caldb requests.\n", + "overwrite_creation_time = \"\" # To overwrite the measured creation_time. Required Format: YYYY-MM-DD HR:MN:SC.00 e.g. \"2022-06-28 13:00:00.00\"\n", "\n", "# Parameters affecting corrected data.\n", - "gain_correction = True # do relative gain correction\n", + "constants_file = \"/gpfs/exfel/data/scratch/ahmedk/dont_remove/gotthard2/constants/calibration_constants_GH2.h5\" # Retrieve constants from local.\n", + "offset_correction = True # apply offset correction. This can be disabled to only apply LUT or apply LUT and gain correction for non-linear differential results.\n", + "gain_correction = True # apply gain correction.\n", "\n", "# Parameter conditions.\n", "bias_voltage = -1 # Detector bias voltage, set to -1 to use value in raw file.\n", - "exposure_time = -1 # Detector exposure time, set to -1 to use value in raw file.\n", - "exposure_period = -1 # Detector exposure period, set to -1 to use value in raw file.\n", - "operation_mode = -1 # Detector operation mode (1.1/4.5), set to -1 to use value in raw file.\n", + "exposure_time = -1. # Detector exposure time, set to -1 to use value in raw file.\n", + "exposure_period = -1. # Detector exposure period, set to -1 to use value in raw file.\n", + "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", "\n", "# Parameters for plotting\n", "skip_plots = False # exit after writing corrected files\n", - "pulse_idx_preview = 3 # pulse index to preview. The following even/odd pulse index is used for preview. # TODO: update to pulseId preview.\n", + "pulse_idx_preview = 3 # pulse index to preview. The following even/odd pulse index is used for preview. # TODO: update to pulseId preview.\n", "\n", "\n", "def balance_sequences(in_folder, run, sequences, sequences_per_node, karabo_da):\n", @@ -66,7 +69,7 @@ "metadata": {}, "outputs": [], "source": [ - "import warnings\n", + "import datetime\n", "from functools import partial\n", "\n", "import h5py\n", @@ -85,9 +88,6 @@ " get_dir_creation_date,\n", ")\n", "from iCalibrationDB import Conditions, Constants\n", - "warnings.filterwarnings('ignore')\n", - "\n", - "prettyPlotting = True\n", "\n", "%matplotlib inline" ] @@ -107,22 +107,19 @@ "if not karabo_id_control:\n", " karabo_id_control = karabo_id\n", "\n", - "instrument_src = instrument_source_template.format(\n", - " karabo_id, receiver_template)\n", + "instrument_src = instrument_source_template.format(karabo_id, receiver_template)\n", "ctrl_src = ctrl_source_template.format(karabo_id_control, control_template)\n", "\n", "print(f\"Process modules: {karabo_da} for run {run}\")\n", "\n", "creation_time = None\n", - "if use_dir_creation_date:\n", + "if overwrite_creation_time:\n", + " creation_time = datetime.datetime.strptime(\n", + " overwrite_creation_time, \"%Y-%m-%d %H:%M:%S.%f\"\n", + " )\n", + "elif use_dir_creation_date:\n", " creation_time = get_dir_creation_date(in_folder, run)\n", - " print(f\"Using {creation_time} as creation time\")\n", - "\n", - "# TODO: Remove later\n", - "import datetime\n", - "creation_time=datetime.datetime.strptime(\n", - " \"2022-06-28 13:00:00.00\",\n", - " \"%Y-%m-%d %H:%M:%S.%f\")\n" + " print(f\"Using {creation_time} as creation time\")" ] }, { @@ -137,11 +134,13 @@ " possible_patterns = list(f\"*{mod}*.h5\" for mod in karabo_da)\n", "else:\n", " possible_patterns = list(\n", - " f\"*{mod}-S{s:05d}.h5\" for mod in karabo_da for s in sequences)\n", + " f\"*{mod}-S{s:05d}.h5\" for mod in karabo_da for s in sequences\n", + " )\n", "\n", "run_folder = Path(in_folder / f\"r{run:04d}\")\n", - "seq_files = [f for f in run_folder.glob(\"*.h5\") if any(\n", - " f.match(p) for p in possible_patterns)]\n", + "seq_files = [\n", + " f for f in run_folder.glob(\"*.h5\") if any(f.match(p) for p in possible_patterns)\n", + "]\n", "\n", "seq_files = sorted(seq_files)\n", "\n", @@ -170,13 +169,18 @@ "source": [ "# Read slow data\n", "run_dc = RunDirectory(run_folder)\n", - "g2ctrl = gotthard2lib.Gotthard2Ctrl(\n", - " run_dc=run_dc, ctrl_src=ctrl_src)\n", - "bias_voltage = g2ctrl.get_bias_voltage()\n", - "exposure_time = g2ctrl.get_exposure_time()\n", - "exposure_period = g2ctrl.get_exposure_period()\n", - "operation_mode = g2ctrl.get_operation_mode()\n", - "single_photon = g2ctrl.get_single_photon()" + "g2ctrl = gotthard2lib.Gotthard2Ctrl(run_dc=run_dc, ctrl_src=ctrl_src)\n", + "\n", + "if bias_voltage == -1:\n", + " bias_voltage = g2ctrl.get_bias_voltage()\n", + "if exposure_time == -1:\n", + " exposure_time = g2ctrl.get_exposure_time()\n", + "if exposure_period == -1:\n", + " exposure_period = g2ctrl.get_exposure_period()\n", + "if acquisition_rate == -1:\n", + " acquisition_rate = g2ctrl.get_acquisition_rate()\n", + "if single_photon == -1:\n", + " single_photon = g2ctrl.get_single_photon()" ] }, { @@ -190,86 +194,108 @@ { "cell_type": "code", "execution_count": null, - "id": "1cdbe818", + "id": "5717d722", "metadata": {}, "outputs": [], "source": [ - "condition = Conditions.Dark.Gotthard2(\n", - " bias_voltage=bias_voltage,\n", - " exposure_time=exposure_time,\n", - " exposure_period=exposure_period,\n", - " # TODO: put back with new data.\n", - " # And decide what to do with old data missing these parameters.\n", - "# single_photon=single_photon,\n", - "# operation_mode=operation_mode,\n", - ")\n", - "\n", - "\n", - "def get_constants_for_module(mod: str):\n", - " \"\"\"Get calibration constants for given module for Gotthard-II.\"\"\"\n", - " when = {}\n", - " retrieval_function = partial(\n", - " get_constant_from_db_and_time,\n", - " karabo_id=karabo_id,\n", - " karabo_da=mod,\n", - " cal_db_interface=cal_db_interface,\n", - " creation_time=creation_time,\n", - " timeout=cal_db_timeout,\n", - " print_once=False,\n", - " )\n", - "\n", - " constants = {\n", - " \"LUT\": ((1280, 2, 4096), np.uint16), # TODO: what to do if LUT was not retrieved.\n", - " \"Offset\": ((1280, 2, 3), np.float32),\n", - " \"Noise\": ((1280, 2, 3), np.float32),\n", - " \"BadPixelsDark\": ((1280, 2, 3), np.uint32),\n", - " }\n", - " cons_data = {}\n", - " for cname, cempty in constants.items():\n", - " cons_data[cname], when[cname] = retrieval_function(\n", - " condition=condition,\n", - " constant=getattr(Constants.Gotthard2, cname)(),\n", - " empty_constant=np.zeros(cempty[0], dtype=cempty[1])\n", - " )\n", + "# Used for old FXE runs before adding Gotthard2 to CALCAT\n", "\n", - " bpix = cons_data[\"BadPixelsDark\"]\n", - " if gain_correction:\n", - " for cname in [\"BadPixelsFF\", \"RelativeGain\"]:\n", - " cons_data[cname], when[cname] = retrieval_function(\n", - " condition=condition,\n", - " constant=getattr(Constants.Gotthard2, cname)(),\n", - " empty_constant=None\n", - " )\n", - " # combine masks\n", - " if cons_data.get(\"BadPixelsFF\", None) is not None:\n", - " bpix |= cons_data[\"BadPixelsFF\"]\n", - " return (\n", - " cons_data[\"LUT\"], cons_data[\"Offset\"],\n", - " bpix, cons_data[\"RelativeGain\"], mod, when,\n", - " )\n", - "\n", - "with multiprocessing.Pool() as pool:\n", - " r = pool.map(get_constants_for_module, karabo_da)" + "constants = {}\n", + "if constants_file:\n", + " for mod in karabo_da:\n", + " constants[mod] = {}\n", + " # load constants temporarily using defined local paths.\n", + " with h5py.File(constants_file, \"r\") as cfile:\n", + " constants[mod][\"lut\"] = cfile[\"LUT\"][()]\n", + " constants[mod][\"offset\"] = cfile[\"offset_map\"][()].astype(np.float32)\n", + " constants[mod][\"gain\"] = cfile[\"gain_map\"][()].astype(np.float32)\n", + " constants[mod][\"badpixels\"] = cfile[\"bpix_ff\"][()].astype(np.uint32)" ] }, { "cell_type": "code", "execution_count": null, - "id": "8ddbc1ba", + "id": "1cdbe818", "metadata": {}, "outputs": [], "source": [ - "# Print timestamps for the retrieved constants.\n", - "constants = {}\n", - "for lut_map, offset_map, bpix, gain_map, mod, when in r:\n", - " print(f'Constants for module {mod}:')\n", - " for const in when:\n", - " print(f' {const} injected at {when[const]}')\n", - "\n", - " if gain_map is None:\n", - " print(\"No gain map found\")\n", - " gain_correction = False\n", - " constants[mod] = (lut_map, offset_map, bpix, gain_map)" + "if not constants_file:\n", + " condition = Conditions.Dark.Gotthard2(\n", + " bias_voltage=bias_voltage,\n", + " exposure_time=exposure_time,\n", + " exposure_period=exposure_period,\n", + " single_photon=single_photon,\n", + " acquisition_rate=acquisition_rate,\n", + " )\n", + "\n", + " def get_constants_for_module(mod: str):\n", + " \"\"\"Get calibration constants for given module for Gotthard2.\"\"\"\n", + " when = {}\n", + " retrieval_function = partial(\n", + " get_constant_from_db_and_time,\n", + " karabo_id=karabo_id,\n", + " karabo_da=mod,\n", + " cal_db_interface=cal_db_interface,\n", + " creation_time=creation_time,\n", + " timeout=cal_db_timeout,\n", + " print_once=False,\n", + " )\n", + " empty_lut = (np.arange(2 ** 12).astype(np.float64) * 2 ** 10 / 2 ** 12).astype(\n", + " np.uint16\n", + " )\n", + " empty_lut = np.stack(1280 * [np.stack([empty_lut] * 2)], axis=0)\n", + " constants = {\n", + " \"LUT\": empty_lut,\n", + " \"Offset\": np.zeros((1280, 2, 3), dtype=np.float32),\n", + " \"Noise\": np.zeros((1280, 2, 3), dtype=np.float32),\n", + " \"BadPixelsDark\": np.zeros((1280, 2, 3), dtype=np.uint32),\n", + " }\n", + " cons_data = {}\n", + " for cname, cempty in constants.items():\n", + " cons_data[cname], when[cname] = retrieval_function(\n", + " condition=condition,\n", + " constant=getattr(Constants.Gotthard2, cname)(),\n", + " empty_constant=cempty,\n", + " )\n", + "\n", + " bpix = cons_data[\"BadPixelsDark\"]\n", + " if gain_correction:\n", + " for cname in [\"BadPixelsFF\", \"RelativeGain\"]:\n", + " cons_data[cname], when[cname] = retrieval_function(\n", + " condition=condition,\n", + " constant=getattr(Constants.Gotthard2, cname)(),\n", + " empty_constant=None,\n", + " )\n", + " # combine masks\n", + " if cons_data.get(\"BadPixelsFF\", None) is not None:\n", + " bpix |= cons_data[\"BadPixelsFF\"]\n", + " return (\n", + " cons_data[\"LUT\"],\n", + " cons_data[\"Offset\"],\n", + " bpix,\n", + " cons_data.get(\"RelativeGain\", None),\n", + " mod,\n", + " when,\n", + " )\n", + "\n", + " with multiprocessing.Pool() as pool:\n", + " r = pool.map(get_constants_for_module, karabo_da)\n", + "\n", + " # Print timestamps for the retrieved constants.\n", + " constants = {}\n", + " for lut_map, offset_map, bpix, gain_map, mod, when in r:\n", + " constants[mod] = {}\n", + " print(f\"Constants for module {mod}:\")\n", + " for const in when:\n", + " print(f\" {const} injected at {when[const]}\")\n", + "\n", + " if gain_map is None:\n", + " print(\"No gain map found\")\n", + " gain_correction = False\n", + " constants[mod][\"lut\"] = lut_map\n", + " constants[mod][\"offset\"] = offset_map\n", + " constants[mod][\"gain\"] = gain_map\n", + " constants[mod][\"badpixels\"] = bpix" ] }, { @@ -291,12 +317,15 @@ "source": [ "def correct_train(wid, index, d):\n", " g = gain[index]\n", - " gotthard2algs.convert_to_10bit(d, lut, data_corr[index, ...])\n", + " gotthard2algs.convert_to_10bit(d, constants[mod][\"lut\"], data_corr[index, ...])\n", " gotthard2algs.correct_train(\n", - " data_corr[index, ...], mask[index, ...], g,\n", - " offset_map,\n", - " gain_map,\n", - " bpix,\n", + " data_corr[index, ...],\n", + " mask[index, ...],\n", + " g,\n", + " constants[mod][\"offset\"],\n", + " constants[mod][\"gain\"],\n", + " constants[mod][\"badpixels\"],\n", + " apply_offset=offset_correction,\n", " apply_gain=gain_correction,\n", " )" ] @@ -312,7 +341,7 @@ " # This is used in case receiver template consists of\n", " # karabo data aggregator index. e.g. detector at DETLAB\n", " instr_mod_src = instrument_src.format(mod[-2:])\n", - " data_path = \"INSTRUMENT/\"+instr_mod_src+\"/data\"\n", + " data_path = \"INSTRUMENT/\" + instr_mod_src + \"/data\"\n", " for raw_file in seq_files:\n", " step_timer.start()\n", "\n", @@ -324,24 +353,24 @@ " data = dc[instr_mod_src, \"data.adc\"].ndarray()\n", " gain = dc[instr_mod_src, \"data.gain\"].ndarray()\n", " step_timer.start()\n", - " lut, offset_map, bpix, gain_map = constants[mod]\n", "\n", " # Allocate shared arrays.\n", " data_corr = context.alloc(shape=dshape, dtype=np.float32)\n", " mask = context.alloc(shape=dshape, dtype=np.uint32)\n", " context.map(correct_train, data)\n", " step_timer.done_step(\"Correcting one sequence file\")\n", + "\n", " step_timer.start()\n", "\n", " # Provided PSI gain map has 0 values. Set inf values to nan.\n", + " # TODO: This can maybe be removed after creating XFEL gain maps.?\n", " data_corr[np.isinf(data_corr)] = np.nan\n", " # Create CORR files and add corrected data sources.\n", " # Exclude raw data images (data/adc)\n", - " with h5py.File(out_file, 'w') as ofile:\n", + " with h5py.File(out_file, \"w\") as ofile:\n", " # Copy RAW non-calibrated sources.\n", - " with h5py.File(raw_file, 'r') as sfile:\n", - " h5_copy_except.h5_copy_except_paths(\n", - " sfile, ofile, [f\"{data_path}/adc\"])\n", + " with h5py.File(raw_file, \"r\") as sfile:\n", + " h5_copy_except.h5_copy_except_paths(sfile, ofile, [f\"{data_path}/adc\"])\n", " # Create datasets with the available corrected data\n", " ddset = ofile.create_dataset(\n", " f\"{data_path}/adc\",\n", @@ -381,8 +410,9 @@ "outputs": [], "source": [ "if skip_plots:\n", - " print('Skipping plots')\n", + " print(\"Skipping plots\")\n", " import sys\n", + "\n", " sys.exit(0)" ] }, @@ -398,7 +428,8 @@ " mod_dcs[mod] = {}\n", " with RunDirectory(out_folder) as out_dc:\n", " tid, mod_dcs[mod][\"train_corr_data\"] = next(\n", - " out_dc[instr_mod_src, \"data.adc\"].trains())\n", + " out_dc[instr_mod_src, \"data.adc\"].trains()\n", + " )\n", " with RunDirectory(run_folder) as in_dc:\n", " train_dict = in_dc.train_from_id(tid)[1][instr_mod_src]\n", " mod_dcs[mod][\"train_raw_data\"] = train_dict[\"data.adc\"]\n", @@ -420,16 +451,17 @@ " raw_data = mod_dcs[mod][\"train_raw_data\"]\n", " fig, ax = plt.subplots(figsize=(20, 10))\n", " im = ax.plot(np.mean(raw_data, axis=0))\n", - " ax.set_title(f'Module {mod}')\n", + " ax.set_title(f\"Module {mod}\")\n", " ax.set_xlabel(\"Strip #\", size=15)\n", " ax.set_ylabel(\"12-bit ADC output\", size=15)\n", + " plt.show()\n", "step_timer.done_step(\"Plotting raw data\")" ] }, { "cell_type": "code", "execution_count": null, - "id": "512b48a6-9e2d-4fcc-b075-3698297177bf", + "id": "11337d95", "metadata": {}, "outputs": [], "source": [ @@ -441,9 +473,12 @@ " corr_data = mod_dcs[mod][\"train_corr_data\"]\n", " fig, ax = plt.subplots(figsize=(20, 10))\n", " im = ax.plot(np.mean(corr_data, axis=0))\n", - " ax.set_title(f'Module {mod}')\n", - " ax.set_xlabel(\"Strip #\", size=15)\n", - " ax.set_ylabel(\"10-bit KeV. output\", size=15)\n", + " ax.set_title(f\"Module {mod}\")\n", + " ax.set_xlabel(\"Strip #\", size=20)\n", + " ax.set_ylabel(\"10-bit KeV. output\", size=20)\n", + " plt.xticks(fontsize=15)\n", + " plt.yticks(fontsize=15)\n", + " plt.show()\n", "step_timer.done_step(\"Plotting corrected data\")" ] }, @@ -457,8 +492,10 @@ "# Validate given \"pulse_idx_preview\"\n", "\n", "if pulse_idx_preview + 1 > data.shape[1]:\n", - " print(f\"WARNING: selected pulse_idx_preview {pulse_idx_preview} is not available in data.\"\n", - " \" Previewing 1st pulse.\")\n", + " print(\n", + " f\"WARNING: selected pulse_idx_preview {pulse_idx_preview} is not available in data.\"\n", + " \" Previewing 1st pulse.\"\n", + " )\n", " pulse_idx_preview = 1\n", "\n", "if data.shape[1] == 1:\n", @@ -466,7 +503,9 @@ " even_pulse = None\n", "else:\n", " odd_pulse = pulse_idx_preview if pulse_idx_preview % 2 else pulse_idx_preview + 1\n", - " even_pulse = pulse_idx_preview if not (pulse_idx_preview % 2) else pulse_idx_preview + 1\n", + " even_pulse = (\n", + " pulse_idx_preview if not (pulse_idx_preview % 2) else pulse_idx_preview + 1\n", + " )\n", "\n", "if pulse_idx_preview + 1 > data.shape[1]:\n", " pulse_idx_preview = 1\n", @@ -489,23 +528,16 @@ " ax.plot(raw_data[odd_pulse], label=f\"Odd Pulse {odd_pulse}\")\n", " if even_pulse:\n", " ax.plot(raw_data[even_pulse], label=f\"Even Pulse {even_pulse}\")\n", - " ax.set_title(f'Module {mod}')\n", - " ax.set_xlabel(\"Strip #\", size=15)\n", - " ax.set_ylabel(\"12-bit ADC output\", size=15)\n", + " ax.set_title(f\"Module {mod}\")\n", + " ax.set_xlabel(\"Strip #\", size=20)\n", + " ax.set_ylabel(\"12-bit ADC RAW\", size=20)\n", + " plt.xticks(fontsize=15)\n", + " plt.yticks(fontsize=15)\n", " ax.legend()\n", + " plt.show()\n", "step_timer.done_step(\"Plotting RAW odd/even pulses.\")" ] }, - { - "cell_type": "markdown", - "id": "f1ddac79-5590-4bdb-9cfa-147d73f5dd5f", - "metadata": {}, - "source": [ - "### Single RAW Train Preview\n", - "\n", - "A single image from the first RAW train" - ] - }, { "cell_type": "code", "execution_count": null, @@ -521,10 +553,11 @@ " ax.plot(corr_data[odd_pulse], label=f\"Odd Pulse {odd_pulse}\")\n", " if even_pulse:\n", " ax.plot(corr_data[even_pulse], label=f\"Even Pulse {even_pulse}\")\n", - " ax.set_title(f'Module {mod}')\n", + " ax.set_title(f\"Module {mod}\")\n", " ax.set_xlabel(\"Strip #\", size=15)\n", - " ax.set_ylabel(\"10-bit KeV. output\", size=15)\n", + " ax.set_ylabel(\"10-bit KeV CORRECTED\", size=15)\n", " ax.legend()\n", + " plt.show()\n", "step_timer.done_step(\"Plotting CORRECTED odd/even pulses.\")" ] } @@ -546,6 +579,11 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.11" + }, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } } }, "nbformat": 4, diff --git a/src/cal_tools/gotthard2/gotthard2lib.py b/src/cal_tools/gotthard2/gotthard2lib.py index c7f29721b6dd7a28d9ff4cbe9aa84bfc79e9b86b..8a0a32b5d974f11c510abf5165440eb89333faed 100644 --- a/src/cal_tools/gotthard2/gotthard2lib.py +++ b/src/cal_tools/gotthard2/gotthard2lib.py @@ -25,15 +25,13 @@ class Gotthard2Ctrl(): return(round( self.run_dc[self.ctrl_src, "exposurePeriod"].as_single_value(), 4)) # noqa - def get_operation_mode(self): - if "operationMode.value" in self.run_dc.keys_for_source( - self.ctrl_src): + def get_acquisition_rate(self): + if "acquisitionRate.value" in self.run_dc.keys_for_source(self.ctrl_src): # noqa return( float(self.run_dc.get_run_value( - self.ctrl_src, "operationMode"))) + self.ctrl_src, "acquisitionRate"))) def get_single_photon(self): - if "singlePhoton.value" in self.run_dc.keys_for_source( - self.ctrl_src): + if "singlePhoton.value" in self.run_dc.keys_for_source(self.ctrl_src): # noqa return( bool(self.run_dc[self.ctrl_src, "singlePhoton"].as_single_value())) # noqa diff --git a/src/cal_tools/gotthard2algs.pyx b/src/cal_tools/gotthard2algs.pyx index f588d47b46c74eb538c061e4a90da9b4a1469f34..606de03f484234f38075414cade4ef23e79bcde6 100644 --- a/src/cal_tools/gotthard2algs.pyx +++ b/src/cal_tools/gotthard2algs.pyx @@ -31,6 +31,7 @@ def correct_train( float[:, :, :] offset_map, float[:, :, :] gain_map, unsigned int[:, :, :] bpix_map, + unsigned short short apply_offset = 1, unsigned short short apply_gain = 1, ): """Correct Gotthard2 raw data. @@ -50,7 +51,8 @@ def correct_train( if g == 3: g = 2 raw_val = data[pulse, x] - raw_val -= offset_map[x, cell, g] + if apply_offset == 1: + raw_val -= offset_map[x, cell, g] if apply_gain == 1: raw_val /= gain_map[x, cell, g] mask[pulse, x] = bpix_map[x, cell, g]