{ "cells": [ { "cell_type": "markdown", "id": "49b6577f-96a5-4dd2-bdd9-da661b2c4619", "metadata": {}, "source": [ "# Gotthard2 Dark Image Characterization\n", "\n", "Author: European XFEL Detector Group, Version: 1.0" ] }, { "cell_type": "code", "execution_count": null, "id": "818e24e8", "metadata": {}, "outputs": [], "source": [ "in_folder = \"/gpfs/exfel/exp/FXE/202221/p003225/raw\" # the folder to read data from, required\n", "out_folder = \"/gpfs/exfel/data/scratch/ahmedk/test/gotthard2/darks\" # the folder to output to, required\n", "run_high = 50 # run number for G0 dark run, required\n", "run_med = 51 # run number for G1 dark run, required\n", "run_low = 52 # run number for G2 dark run, required\n", "sequences = [-1] # sequences to correct, set to [-1] for all, range allowed\n", "sequences_per_node = 1 # number of sequence files per node if notebook executed through xfel-calibrate, set to 0 to not run SLURM parallel\n", "\n", "# Parameters used to access raw data.\n", "karabo_id = \"FXE_XAD_G2XES\" # karabo prefix of Gotthard-II devices\n", "karabo_da = [\"GH201\"] # data aggregators\n", "receiver_template = \"RECEIVER\" # receiver template used to read INSTRUMENT keys.\n", "control_template = \"CONTROL\" # control template used to read CONTROL keys.\n", "instrument_source_template = '{}/DET/{}:daqOutput' # template for source name (filled with karabo_id & receiver_id). e.g. 'SPB_IRDA_JF4M/DET/JNGFR01:daqOutput'\n", "ctrl_source_template = '{}/DET/{}' # 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", "# Parameters for the calibration database.\n", "use_dir_creation_date = True\n", "cal_db_interface = \"tcp://max-exfl017:8020\" # calibration DB interface to use\n", "cal_db_timeout = 300000 # timeout on caldb requests\n", "db_output = False # Output constants to the calibration database\n", "local_output = True # Output constants locally\n", "\n", "# Conditions used for injected calibration constants.\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", "single_photon = -1 # Detector single photon mode (High/Low CDS), set to -1 to use value in raw file.\n", "\n", "# Parameters used during selecting raw data trains.\n", "min_trains = 1 # Minimum number of trains that should be available to process dark constants. Default 1.\n", "max_trains = 1000 # Maximum number of trains to use for processing dark constants. Set to 0 to use all available trains.\n", "badpixel_threshold_sigma = 5. # bad pixels defined by values outside n times this std from median\n", "\n", "# Don't delete! myMDC sends this by default.\n", "operation_mode = '' # Detector dark run acquiring operation mode, optional" ] }, { "cell_type": "code", "execution_count": null, "id": "8085f9aa", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import multiprocessing\n", "import pasha as psh\n", "from IPython.display import Markdown, display\n", "from extra_data import RunDirectory\n", "from pathlib import Path\n", "\n", "from cal_tools.enums import BadPixels\n", "from cal_tools.gotthard2 import gotthard2algs, gotthard2lib\n", "from cal_tools.step_timing import StepTimer\n", "from cal_tools.tools import (\n", " get_dir_creation_date,\n", " get_from_db,\n", " get_constant_from_db_and_time,\n", " get_pdu_from_db,\n", " get_report,\n", " save_const_to_h5,\n", " send_to_db,\n", ")\n", "from iCalibrationDB import Conditions, Constants\n", "from XFELDetAna.plotting.heatmap import heatmapPlot" ] }, { "cell_type": "code", "execution_count": null, "id": "18fe4379", "metadata": {}, "outputs": [], "source": [ "run_nums = [run_high, run_med, run_low]\n", "in_folder = Path(in_folder)\n", "out_folder = Path(out_folder)\n", "out_folder.mkdir(exist_ok=True)\n", "\n", "print(f\"Process modules: {karabo_da}\")\n", "\n", "run_dc = RunDirectory(in_folder / f\"r{run_high:04d}\")\n", "file_loc = f\"proposal:{run_dc.run_metadata()['proposalNumber']} runs:{run_high} {run_med} {run_low}\" # noqa\n", "\n", "instrument_src = instrument_source_template.format(\n", " karabo_id, receiver_template)\n", "ctrl_src = ctrl_source_template.format(\n", " karabo_id_control, control_template)\n", "\n", "# Read report path to associate it later with injected constants.\n", "report = get_report(out_folder)\n", "\n", "if use_dir_creation_date:\n", " creation_time = get_dir_creation_date(in_folder, run_high)\n", " print(f\"Using {creation_time.isoformat()} as creation time\")\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", "if not karabo_id_control:\n", " karabo_id_control = karabo_id" ] }, { "cell_type": "code", "execution_count": null, "id": "108be688", "metadata": {}, "outputs": [], "source": [ "step_timer = StepTimer()" ] }, { "cell_type": "code", "execution_count": null, "id": "ff9149fc", "metadata": {}, "outputs": [], "source": [ "# Read parameter conditions and validate the values for the three runs.\n", "\n", "step_timer.start()\n", "run_dcs_dict = dict()\n", "\n", "ctrl_src = ctrl_source_template.format(karabo_id_control, control_template)\n", "conditions = {\n", " \"bias_voltage\": set(),\n", " \"exposure_time\": set(),\n", " \"exposure_period\": set(),\n", " #\"operation_mode\": set(),\n", " #\"single_photon\": set(),\n", "}\n", "for gain, run in enumerate(run_nums):\n", " run_dc = RunDirectory(in_folder / f\"r{run:04d}/\")\n", " run_dcs_dict[run] = [gain, run_dc]\n", "\n", " # Read slow data.\n", " g2ctrl = gotthard2lib.Gotthard2Ctrl(\n", " run_dc=run_dc, ctrl_src=ctrl_src)\n", " conditions[\"bias_voltage\"].add(g2ctrl.get_bias_voltage() if bias_voltage == -1 else bias_voltage) # noqa\n", " conditions[\"exposure_time\"].add(g2ctrl.get_exposure_time() if exposure_time == -1 else exposure_time) # noqa\n", " conditions[\"exposure_period\"].add(g2ctrl.get_exposure_period() if exposure_period == -1 else exposure_period) # noqa\n", "# conditions[\"single_photon\"].add(g2ctrl.get_single_photon() if single_photon == -1 else single_photon) # noqa\n", "# conditions[\"operation_mode\"].add(g2ctrl.get_operation_mode() if operation_mode == -1 else operation_mode) # noqa\n", "\n", "for c, v in conditions.items():\n", " assert len(v) == 1, f\"{c} value is not the same for the three runs.\"\n", "\n", "bias_voltage = conditions[\"bias_voltage\"].pop()\n", "print(\"Bias voltage: \", bias_voltage)\n", "exposure_time = conditions[\"exposure_time\"].pop()\n", "print(\"Exposure time: \", exposure_time)\n", "exposure_period = conditions[\"exposure_period\"].pop()\n", "print(\"Exposure period: \", exposure_period)\n", "# single_photon = conditions[\"single_photon\"].pop()\n", "# operation_mode = conditions[\"operation_mode\"].pop()" ] }, { "cell_type": "code", "execution_count": null, "id": "ac9c5dc3-bc66-4e7e-b6a1-360259be535c", "metadata": {}, "outputs": [], "source": [ "def specify_trains_to_process(\n", " img_key_data: \"extra_data.KeyData\", # noqa\n", " max_trains: int = 0,\n", " min_trains: int = 0,\n", "):\n", " \"\"\"Specify total number of trains to process.\n", " Based on given min_trains and max_trains, if given.\n", "\n", " Print number of trains to process and number of empty trains.\n", " Raise ValueError if specified trains are less than min_trains.\n", " \"\"\"\n", " # Specifies total number of trains to proccess.\n", " n_trains = img_key_data.shape[0]\n", " all_trains = len(img_key_data.train_ids)\n", " print(f\"{mod} has {all_trains - n_trains} \"\n", " f\"trains with empty frames out of {all_trains} trains\")\n", "\n", " if n_trains < min_trains:\n", " raise ValueError(\n", " f\"Less than {min_trains} trains are available in RAW data.\"\n", " \" Not enough data to process darks.\")\n", "\n", " if max_trains > 0:\n", " n_trains = min(n_trains, max_trains)\n", "\n", " print(f\"Processing {n_trains} trains.\")\n", "\n", " return n_trains" ] }, { "cell_type": "code", "execution_count": null, "id": "3c59c11d", "metadata": {}, "outputs": [], "source": [ "# set the operating condition\n", "# TODO: add the final conditions for constants.\n", "condition = Conditions.Dark.Gotthard2(\n", " bias_voltage=bias_voltage,\n", " exposure_time=exposure_time,\n", " exposure_period=exposure_period,\n", " #operation_mode=operation_mode,\n", " #single_photon=single_photon,\n", ")\n", "\n", "db_modules = get_pdu_from_db(\n", " karabo_id=karabo_id,\n", " karabo_da=karabo_da,\n", " constant=Constants.Gotthard2.LUT(),\n", " condition=condition,\n", " cal_db_interface=cal_db_interface,\n", " snapshot_at=creation_time)\n" ] }, { "cell_type": "code", "execution_count": null, "id": "e2eb2fc0-df9c-4887-9691-f81474f8c131", "metadata": {}, "outputs": [], "source": [ "def convert_train(wid, index, tid, d):\n", " \"\"\"Convert a Gotthard2 train from 12bit to 10bit.\"\"\"\n", " gotthard2algs.convert_to_10bit(d[instr_mod_src][\"data.adc\"], lut, data_10bit[index, ...])\n" ] }, { "cell_type": "code", "execution_count": null, "id": "4e8ffeae", "metadata": {}, "outputs": [], "source": [ "# Calculate noise and offset per pixel and global average, std and median\n", "noise_map = dict()\n", "offset_map = dict()\n", "badpixels_map = dict()\n", "\n", "context = psh.context.ProcessContext(num_workers=multiprocessing.cpu_count())\n", "\n", "for mod in karabo_da:\n", "\n", " # Retrieve LUT constant\n", " lut, time = get_constant_from_db_and_time(\n", " constant=Constants.Gotthard2.LUT(),\n", " condition=condition,\n", " empty_constant=None,\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", "\n", " # Path to pixels ADC values\n", " instr_mod_src = instrument_src.format(int(mod[-2:]))\n", "\n", " # TODO: Validate the final shape to store constants.\n", " cshape = (1280, 2, 3)\n", "\n", " offset_map[mod] = context.alloc(shape=cshape, dtype=np.float32)\n", " noise_map[mod] = context.alloc(like=offset_map[mod])\n", " badpixels_map[mod] = context.alloc(like=offset_map[mod], dtype=np.uint32)\n", "\n", " for run_num, [gain, run_dc] in run_dcs_dict.items():\n", " step_timer.start()\n", " n_trains = specify_trains_to_process(run_dc[instr_mod_src, \"data.adc\"])\n", "\n", " # Select requested number of trains to process.\n", " dc = run_dc.select(\n", " instr_mod_src, require_all=True).select_trains(np.s_[:n_trains]) # noqa\n", "\n", " step_timer.done_step(\"preparing raw data\")\n", "\n", " step_timer.start()\n", " # Convert 12bit data to 10bit\n", " data_10bit = context.alloc(\n", " shape=dc[instr_mod_src, \"data.adc\"].shape,\n", " dtype=np.float32\n", " )\n", " context.map(convert_train, dc)\n", " step_timer.done_step(\"convert to 10bit\")\n", "\n", " step_timer.start()\n", "\n", " # Split even and odd data to calculate the two storage cell constants.\n", " # Detector always operates in burst mode.\n", " even_data = data_10bit[:, ::2, :]\n", " odd_data = data_10bit[:, 1::2, :]\n", " data_gain = dc[instr_mod_src, \"data.gain\"].ndarray()\n", " even_gain = data_gain[:, ::2, :]\n", " odd_gain = data_gain[:, 1::2, :]\n", " def offset_noise_cell(wid, index, d):\n", " offset_map[mod][:, index, gain] = np.mean(d, axis=(0, 1))\n", " noise_map[mod][:, index, gain] = np.std(d, axis=(0, 1))\n", "\n", " context.map(offset_noise_cell, (even_data, odd_data))\n", "\n", " raw_g = 3 if gain == 2 else gain\n", " def badpixels_cell(wid, index, g):\n", " \"\"\"Check if there are wrong bad gain values.\n", " Indicate pixels with wrong gain value across all trains for each cell.\"\"\"\n", " badpixels_map[mod][np.mean(g, axis=(0, 1)) != raw_g, index,\n", " gain] |= BadPixels.WRONG_GAIN_VALUE.value\n", " context.map(badpixels_cell, (even_gain, odd_gain))\n", "\n", " step_timer.done_step(\"Processing darks\")" ] }, { "cell_type": "code", "execution_count": null, "id": "3fc17e05-17ab-4ac4-9e79-c95399278bb9", "metadata": {}, "outputs": [], "source": [ "def print_bp_entry(bp):\n", " print(f\"{bp.name:<30s} {bp.value:032b} -> {int(bp.value)}\")\n", "\n", "print_bp_entry(BadPixels.NOISE_OUT_OF_THRESHOLD)\n", "print_bp_entry(BadPixels.OFFSET_NOISE_EVAL_ERROR)\n", "print_bp_entry(BadPixels.WRONG_GAIN_VALUE)\n", "\n", "def eval_bpidx(d):\n", " mdn = np.nanmedian(d, axis=(0))[None, :, :]\n", " std = np.nanstd(d, axis=(0))[None, :, :] \n", " idx = (d > badpixel_threshold_sigma*std+mdn) | (d < (-badpixel_threshold_sigma)*std+mdn)\n", "\n", " return idx" ] }, { "cell_type": "code", "execution_count": null, "id": "9409d10f", "metadata": {}, "outputs": [], "source": [ "badpixels_map[mod][~np.isfinite(noise_map[mod])]" ] }, { "cell_type": "code", "execution_count": null, "id": "40c34cc5-fe93-4b83-bf39-f465f37c40b4", "metadata": {}, "outputs": [], "source": [ "step_timer.start()\n", "g_name = ['G0', 'G1', 'G2']\n", "\n", "for mod in karabo_da:\n", " display(Markdown(f\"### Badpixels for module {mod}:\"))\n", "\n", " badpixels_map[mod][~np.isfinite(offset_map[mod])] |= BadPixels.OFFSET_NOISE_EVAL_ERROR.value\n", " badpixels_map[mod][eval_bpidx(noise_map[mod])] |= BadPixels.NOISE_OUT_OF_THRESHOLD.value\n", "\n", " badpixels_map[mod][~np.isfinite(noise_map[mod])] |= BadPixels.OFFSET_NOISE_EVAL_ERROR.value\n", "\n", "\n", " for cell in [0, 1]:\n", " fig, ax = plt.subplots(figsize=(10, 5))\n", " for g_idx in [0, 1, 2]: \n", " ax.plot(badpixels_map[mod][:, cell, g_idx], label=f\"G{g_idx} Bad pixel map\")\n", " ax.set_xticks(np.arange(0, 1281, 80))\n", " ax.set_xlabel(\"Stripes #\")\n", " ax.set_xlabel(\"BadPixels\")\n", " ax.set_title(f'Cell {cell} - Module {mod}')\n", " ax.set_ylim([0, BadPixels.WRONG_GAIN_VALUE.value])\n", " ax.set_yticks([0,\n", " BadPixels.NOISE_OUT_OF_THRESHOLD.value,\n", " BadPixels.OFFSET_NOISE_EVAL_ERROR.value,\n", " BadPixels.WRONG_GAIN_VALUE.value])\n", " ax.legend()\n", " pass\n", "step_timer.done_step(f'Creating bad pixels constant and plotting it.')" ] }, { "cell_type": "code", "execution_count": null, "id": "e5131112", "metadata": {}, "outputs": [], "source": [ "step_timer.start()\n", "g_name = ['G0', 'G1', 'G2']\n", "\n", "for mod in karabo_da:\n", " display(Markdown(f\"### Badpixels for module {mod}:\"))\n", "\n", " badpixels_map[mod][~np.isfinite(offset_map[mod])] |= BadPixels.OFFSET_NOISE_EVAL_ERROR.value\n", " badpixels_map[mod][eval_bpidx(noise_map[mod])] |= BadPixels.NOISE_OUT_OF_THRESHOLD.value\n", " badpixels_map[mod][~np.isfinite(noise_map[mod])] |= BadPixels.OFFSET_NOISE_EVAL_ERROR.value\n", "\n", " for g_idx in [0, 1, 2]:\n", " # for cell in [0, 1]:\n", " heatmapPlot(\n", " badpixels_map[mod][:, :, g_idx],\n", " y_label=\"Stripes\",\n", " x_label=\"Cells\",\n", "# lut_label=unit,\n", " \n", " vmin=0, vmax=5,\n", " title=f\"Even / Odd Offset map G{g_idx} - Module {mod}\", # TODO: add PDU name({pdu})',\n", " )\n", " pass\n", "step_timer.done_step(f'Creating bad pixels constant and plotting it.')" ] }, { "cell_type": "code", "execution_count": null, "id": "fde8e1cf-bc74-462f-b6e5-cfee8279090d", "metadata": {}, "outputs": [], "source": [ "from XFELDetAna.plotting.heatmap import heatmapPlot\n", "unit = '[ADCu]'\n", "\n", "for mod in karabo_da:\n", " for _, [gain, _] in run_dcs_dict.items():\n", " heatmapPlot(\n", " offset_map[mod][:, :, gain],\n", " y_label=\"Stripes\",\n", " x_label=\"Cells\",\n", " lut_label=unit,\n", " x_ticks=[0.5, 1.5],\n", " x_ticklabels=[\"EVEN\", \"ODD\"],\n", " x_tick_rotation=0,\n", " title=f\"Even / Odd Offset map G{gain} - Module {mod}\", # TODO: add PDU name({pdu})',\n", " )\n", " heatmapPlot(\n", " noise_map[mod][:, :, gain],\n", " y_label=\"Stripes\",\n", " x_label=\"Cells\",\n", " lut_label=unit,\n", " x_ticks=[0.5, 1.5],\n", " x_ticklabels=[\"EVEN\", \"ODD\"],\n", " x_tick_rotation=0,\n", " title=f\"Even / Odd noise map G{gain} - Module {mod}\", # TODO: add PDU name({pdu})',\n", " )\n", " pass" ] }, { "cell_type": "code", "execution_count": null, "id": "c8777cfe", "metadata": {}, "outputs": [], "source": [ "for mod in karabo_da:\n", " for cons, cname in zip([offset_map, noise_map], [\"Offset\", \"Noise\"]):\n", " for cell in [0, 1]:\n", " fig, ax = plt.subplots(figsize=(10, 5))\n", " for g_idx in [0, 1, 2]:\n", " ax.plot(cons[mod][:, cell, g_idx], label=f\"G{g_idx} {cname} map\")\n", "\n", " ax.set_xlabel(\"Stripes #\")\n", " ax.set_xlabel(cname)\n", " ax.set_title(f\"{cname} map - Cell {cell} - Module {mod}\"), # TODO: add PDU name({pdu})'\n", " ax.legend()\n", " pass" ] }, { "cell_type": "code", "execution_count": null, "id": "1c4eddf7-7d6e-49f4-8cbb-12d2bc496a8f", "metadata": {}, "outputs": [], "source": [ "step_timer.start()\n", "for mod, db_mod in zip(karabo_da, db_modules):\n", " constants = {\n", " 'Offset': offset_map[mod],\n", " 'Noise': noise_map[mod],\n", " }\n", "\n", " md = None\n", "\n", " for key, const_data in constants.items():\n", "\n", " const = getattr(Constants.Gotthard2, key)()\n", " const.data = const_data\n", "\n", " if db_output:\n", " md = send_to_db(\n", " db_module=db_mod,\n", " karabo_id=karabo_id,\n", " constant=const,\n", " condition=condition,\n", " file_loc=file_loc,\n", " report_path=report,\n", " cal_db_interface=cal_db_interface,\n", " creation_time=creation_time,\n", " timeout=cal_db_timeout,\n", " )\n", " if local_output:\n", " md = save_const_to_h5(\n", " db_module=db_mod,\n", " karabo_id=karabo_id,\n", " constant=const,\n", " condition=condition,\n", " data=const.data,\n", " file_loc=file_loc,\n", " report=report,\n", " creation_time=creation_time,\n", " out_folder=out_folder,\n", " )\n", " print(f\"Calibration constant {key} is stored locally at {out_folder}.\\n\")\n", "\n", "print(\"Constants parameter conditions are:\\n\")\n", "# TODO: add the final conditions for constants.\n", "print(\n", " f\"• Bias voltage: {bias_voltage}\\n\"\n", " f\"• Exposure time: {exposure_time}\\n\"\n", " f\"• Exposure period: {exposure_period}\\n\"\n", " f\"• Operation mode: {operation_mode}\\n\"\n", " f\"• Single photon: {single_photon}\\n\"\n", " f\"• Creation time: {md.calibration_constant_version.begin_at if md is not None else creation_time}\\n\") # noqa\n", "step_timer.done_step(\"Injecting constants.\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.8.11 ('.cal4_venv': venv)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.11" }, "vscode": { "interpreter": { "hash": "25ceec0b6126c0ccf883616a02d86b8eaec8ca3fe33700925044adbe0a704e39" } } }, "nbformat": 4, "nbformat_minor": 5 }