{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Pulse Capacitor Characterisation Summary\n",
    "\n",
    "This notebook is used as a dependency notebook for a pulse capacitor characterisation to provide summary for all modules of the AGIPD."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "in_folder = \"/gpfs/exfel/exp/HED/202430/p900438/raw/\" # path to input data, required\n",
    "out_folder = \"/gpfs/exfel/exp/HED/202430/p900438/scratch/rovensky/pc/\" # path to output to, required\n",
    "metadata_folder = \"\"  # Directory containing calibration_metadata.yml when run by xfel-calibrate\n",
    "runs = [] # runs to use, required, range allowed\n",
    "\n",
    "karabo_id_control = \"HED_DET_AGIPD65K1\"  # karabo-id for the control device e.g. \"MID_EXP_AGIPD1M1\", or \"SPB_IRU_AGIPD1M1\"\n",
    "karabo_id = \"HED_DET_AGIPD65K1\"\n",
    "ctrl_source_template = '{}/MDL/FPGA_COMP' # path to control information\n",
    "\n",
    "creation_time = \"\" # To overwrite the measured creation_time. Required Format: YYYY-MM-DD HR:MN:SC e.g. \"2022-06-28 13:00:00\"\n",
    "creation_date_offset = \"00:00:00\" # add an offset to creation date, e.g. to get different constants\n",
    "cal_db_timeout = 3000000 # timeout on caldb requests\"\n",
    "cal_db_interface = \"tcp://max-exfl-cal001:8015#8045\"\n",
    "db_output = False\n",
    "\n",
    "bias_voltage = -1  # detector bias voltage, negative values for auto-detection.\n",
    "mem_cells = -1  # number of memory cells used, negative values for auto-detection.\n",
    "acq_rate = -1.  # the detector acquisition rate, negative values for auto-detection.\n",
    "gain_setting = -1  # gain setting can have value 0 or 1, negative values for auto-detection.\n",
    "integration_time = -1  # integration time, negative values for auto-detection."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "from pathlib import Path\n",
    "from dateutil import parser\n",
    "from datetime import timedelta\n",
    "\n",
    "import h5py\n",
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.gridspec as gridspec\n",
    "import matplotlib.cm as cm\n",
    "import numpy as np\n",
    "import tabulate\n",
    "import multiprocessing\n",
    "\n",
    "from cal_tools.agipdlib import AgipdCtrl\n",
    "from cal_tools.ana_tools import get_range\n",
    "from cal_tools.tools import (\n",
    "    calcat_creation_time,\n",
    "    module_index_to_qm,\n",
    "    get_from_db,\n",
    "    get_pdu_from_db,\n",
    "    get_report,\n",
    "    send_to_db\n",
    ")\n",
    "from cal_tools.plotting import agipd_single_module_geometry\n",
    "\n",
    "from extra_data import RunDirectory\n",
    "from extra_geom import AGIPD_1MGeometry, AGIPD_500K2GGeometry\n",
    "from IPython.display import Latex, display\n",
    "from XFELDetAna.plotting.simpleplot import simplePlot\n",
    "\n",
    "from iCalibrationDB import Conditions, Constants\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Evaluate creation time\n",
    "creation_time = calcat_creation_time(in_folder, runs[0], creation_time)\n",
    "offset = parser.parse(creation_date_offset)\n",
    "delta = timedelta(hours=offset.hour, minutes=offset.minute, seconds=offset.second)\n",
    "creation_time += delta\n",
    "print(f\"Creation time: {creation_time}\\n\")\n",
    "\n",
    "# Get operation conditions\n",
    "ctrl_source = ctrl_source_template.format(karabo_id_control)\n",
    "run_folder = f'{in_folder}/r{runs[0]:04d}/'\n",
    "raw_dc = RunDirectory(run_folder)\n",
    "instrument_src_mod = list(raw_dc.detector_sources)[0]\n",
    "\n",
    "agipd_cond = AgipdCtrl(\n",
    "    run_dc=raw_dc,\n",
    "    image_src=instrument_src_mod,\n",
    "    ctrl_src=ctrl_source,\n",
    "    raise_error=False,  # to be able to process very old data without mosetting value\n",
    ")\n",
    "if mem_cells == -1:\n",
    "    mem_cells = agipd_cond.get_num_cells()\n",
    "if mem_cells is None:\n",
    "    raise ValueError(f\"No raw images found in {run_folder}\")\n",
    "if acq_rate == -1.:\n",
    "    acq_rate = agipd_cond.get_acq_rate()\n",
    "if gain_setting == -1:\n",
    "    gain_setting = agipd_cond.get_gain_setting(creation_time)\n",
    "if bias_voltage == -1:\n",
    "    bias_voltage = agipd_cond.get_bias_voltage(karabo_id_control)\n",
    "if integration_time == -1:\n",
    "    integration_time = agipd_cond.get_integration_time()\n",
    "\n",
    "# Evaluate detector instance for mapping\n",
    "instance = karabo_id_control.split(\"_\")[-1]\n",
    "agipd_instances = {'AGIPD65K1': [1, agipd_single_module_geometry()],\n",
    "                   'AGIPD500K2G': [8, AGIPD_500K2GGeometry.from_origin()], \n",
    "                   'AGIPD1M1': [16, AGIPD_1MGeometry.from_quad_positions(quad_pos=[(-525, 625),\n",
    "                                                                                   (-550, -10),\n",
    "                                                                                   (520, -160),\n",
    "                                                                                   (542.5, 475),\n",
    "                                                                                  ])]\n",
    "                  }\n",
    "\n",
    "try:\n",
    "    nmods = agipd_instances[instance][0]\n",
    "    geom =  agipd_instances[instance][1]\n",
    "except KeyError as ke:\n",
    "    print('The provided AGIPD instance is not recognised. Available instances are: \\\n",
    "    AGIPD65K1,\\nAGIPD500K2G,\\nAGIPD1M1')\n",
    "\n",
    "print(f\"Using {creation_time} as creation time\\n\")\n",
    "print(f\"Operating conditions are:\\n• Bias voltage: {bias_voltage}\\n• Memory cells: {mem_cells}\\n\"\n",
    "      f\"• Acquisition rate: {acq_rate}\\n• Gain setting: {gain_setting}\\n• Integration time: {integration_time}\\n\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ml - high gain slope\n",
    "# bl - high gain intercept\n",
    "# devl - absolute relative deviation from linearity for high gain\n",
    "# mh - medium gain slope\n",
    "# bh - medium gain intercept\n",
    "# oh, ch, ah - parameters of hook function fit to medium gain (only if requested)\n",
    "# devh - absolute relative deviation from linearity for linear part of medium gain\n",
    "\n",
    "keys = ['ml', 'bl', 'mh', 'bh', 'BadPixelsPC']\n",
    "keys_file = [\"ml\", \"bl\", \"devl\", \"mh\", \"bh\", \"oh\", \"ch\", \"ah\", \"devh\"]\n",
    "    \n",
    "fit_data = {}\n",
    "bad_pixels = {}\n",
    "modules = []\n",
    "karabo_da = []\n",
    "constants_files = []\n",
    "\n",
    "out_folder = Path(out_folder)\n",
    "constants_files = sorted(out_folder.glob('*.h5'), \n",
    "                         key=lambda f: (len(f.stem), f.stem))        \n",
    "        \n",
    "for f in constants_files:\n",
    "    mod = int(f.stem.split(\"_\")[-1])\n",
    "    qm = module_index_to_qm(mod)\n",
    "    fit_data[mod] = {}\n",
    "    with h5py.File(f, 'r') as hf:\n",
    "        bad_pixels[mod] = hf[f'/{qm}/BadPixelsPC/0/data'][()]\n",
    "        for key in keys_file:\n",
    "            fit_data[mod][key]= hf[f'/{qm}/{key}/0/data'][()]\n",
    "        \n",
    "    modules.append(mod)\n",
    "    karabo_da.append(f\"AGIPD{mod:02d}\")\n",
    "    print(f'Data available for AGIPD{mod:02d}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def slope_dict_to_arr(d):\n",
    "    key_to_index = {\n",
    "                    \"ml\": 0,\n",
    "                    \"bl\": 1,\n",
    "                    \"devl\": 2,\n",
    "                    \"mh\": 3,\n",
    "                    \"bh\": 4,\n",
    "                    \"oh\": 5,\n",
    "                    \"ch\": 6,\n",
    "                    \"ah\": 7,\n",
    "                    \"devh\": 8,\n",
    "                    \"tresh\": 9\n",
    "    }\n",
    "\n",
    "    arr = np.zeros([11]+list(d[\"ml\"].shape), np.float32)\n",
    "    for key, item in d.items():\n",
    "        if key not in key_to_index:\n",
    "            continue\n",
    "        arr[key_to_index[key],...] = item\n",
    "        \n",
    "    return arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# set the operating condition\n",
    "condition = Conditions.Dark.AGIPD(memory_cells=mem_cells, \n",
    "                                  bias_voltage=bias_voltage,\n",
    "                                  acquisition_rate=acq_rate, \n",
    "                                  gain_setting=gain_setting,\n",
    "                                  integration_time=integration_time)\n",
    "\n",
    "db_modules = get_pdu_from_db(karabo_id, karabo_da, Constants.AGIPD.SlopesPC(),\n",
    "                             condition, cal_db_interface,\n",
    "                             snapshot_at=creation_time)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "proposal = list(filter(None, in_folder.strip('/').split('/')))[-2]\n",
    "file_loc = proposal + ' ' + ' '.join(list(map(str,runs)))\n",
    "\n",
    "report = get_report(metadata_folder)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "md = None\n",
    "\n",
    "if db_output:\n",
    "    for mod, pdu in zip(modules, db_modules):\n",
    "        for const in [\"SlopesPC\", \"BadPixelsPC\"]:\n",
    "\n",
    "            dbconst = getattr(Constants.AGIPD, const)()\n",
    "\n",
    "            if const == \"SlopesPC\":\n",
    "                dbconst.data = slope_dict_to_arr(fit_data[mod])\n",
    "            else:\n",
    "                dbconst.data = bad_pixels[mod]\n",
    "\n",
    "\n",
    "            md = send_to_db(pdu, karabo_id, dbconst, condition,\n",
    "                            file_loc, report, cal_db_interface,\n",
    "                            creation_time=creation_time,\n",
    "                           variant=1)\n",
    "\n",
    "    print(\"Constants injected with the following conditions:\\n\")\n",
    "    print(f\"• memory_cells: {mem_cells}\\n• bias_voltage: {bias_voltage}\\n\"\n",
    "          f\"• acquisition_rate: {acq_rate}\\n• gain_setting: {gain_setting}\\n\"\n",
    "          f\"• integration_time: {integration_time}\\n\"\n",
    "          f\"• creation_time: {md.calibration_constant_version.begin_at if md is not None else creation_time}\\n\")\n",
    "else:\n",
    "    print('Injection to DB not requested.')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Remove keys which won't be used for comparison plots and add BP to the rest of data \n",
    "for mod in modules:\n",
    "    fit_data[mod]['BadPixelsPC'] = bad_pixels[mod]\n",
    "    \n",
    "    for key in keys_file:\n",
    "        if key not in keys:\n",
    "            del fit_data[mod][key]\n",
    "            \n",
    "    for key in keys:\n",
    "        fit_data[mod][key] = fit_data[mod][key].swapaxes(1,2) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def retrieve_old_PC(mod):\n",
    "    dconst = getattr(Constants.AGIPD, 'SlopesPC')()\n",
    "    old_PC = get_from_db(karabo_id=karabo_id,\n",
    "            karabo_da=karabo_da[mod],\n",
    "            constant=dconst,\n",
    "            condition=condition,\n",
    "            empty_constant=None,\n",
    "            cal_db_interface=cal_db_interface,\n",
    "            creation_time=creation_time-timedelta(seconds=1) if creation_time else None,\n",
    "            strategy=\"pdu_prior_in_time\",\n",
    "            verbosity=1,\n",
    "            timeout=cal_db_timeout)\n",
    "    return old_PC\n",
    "\n",
    "with multiprocessing.Pool(processes=len(modules)) as pool:\n",
    "    old_PC_consts = pool.map(retrieve_old_PC, range(len(modules)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create the arrays that will be used for figures.\n",
    "# Each array correponds to the data for all processed modules.\n",
    "\n",
    "pixel_range = [0,0,512,128]\n",
    "const_data = {}\n",
    "old_const = {}\n",
    "const_order = [0, 1, 3, 4]\n",
    "\n",
    "for (key, c) in zip(keys, const_order):\n",
    "    const_data[key] = np.full((nmods, mem_cells, 512, 128), np.nan)\n",
    "    old_const[key] = np.full((nmods, mem_cells, 512, 128), np.nan)\n",
    "    for cnt, mn in enumerate(modules):\n",
    "        if key in fit_data[mn]:\n",
    "            const_data[key][cnt,:,pixel_range[0]:pixel_range[2],\n",
    "                               pixel_range[1]:pixel_range[3]] = fit_data[mn][key]\n",
    "            if np.any(old_PC_consts[0][0]):\n",
    "                old_const[key][cnt,:,pixel_range[0]:pixel_range[2],\n",
    "                               pixel_range[1]:pixel_range[3]] = old_PC_consts[cnt][0][c].swapaxes(1,2)\n",
    "\n",
    "const_data['BadPixelsPC'] = np.full((nmods, mem_cells, 512, 128), np.nan)\n",
    "for cnt, mn in enumerate(modules):\n",
    "    const_data['BadPixelsPC'][cnt,:,pixel_range[0]:pixel_range[2],\n",
    "                              pixel_range[1]:pixel_range[3]] = fit_data[mn]['BadPixelsPC']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary across pixels ##\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gain_data = {'HG': {},\n",
    "             'MG': {},\n",
    "             'BP': {}\n",
    "            }\n",
    "\n",
    "old_gain_data = {'HG': {},\n",
    "                 'MG': {}\n",
    "                }\n",
    "      \n",
    "for key in ['ml', 'bl']:\n",
    "    gain_data['HG'][key] = const_data[key]\n",
    "    old_gain_data['HG'][key] = old_const[key]\n",
    "for key in ['mh', 'bh']:\n",
    "    gain_data['MG'][key] = const_data[key]\n",
    "    old_gain_data['MG'][key] = old_const[key]\n",
    "gain_data['BP']['BadPixelsPC'] = const_data['BadPixelsPC']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_definition(data, g, key):\n",
    "    titel = ['Difference to previous', 'Percentage difference']\n",
    "    gs = gridspec.GridSpec(1, 2)\n",
    "    fig = plt.figure(figsize=(15, 7))\n",
    "    plt.suptitle(f'{g}', fontsize=16)\n",
    "    \n",
    "    for pos in range(0,2):\n",
    "        vmin, vmax = get_range(data[pos], 2)\n",
    "        vmax = max(vmax, abs(vmin))\n",
    "        ax = fig.add_subplot(gs[0, pos])\n",
    "        ticks = [-1, 0, 1] if np.isnan(vmin) else [vmin, (vmin+vmax)/2, vmax]\n",
    "        geom.plot_data_fast(data[pos],\n",
    "                                vmin=vmin, vmax=vmax, ax=ax, cmap=\"RdBu\", figsize=(13,7),\n",
    "                                colorbar={'shrink': 1,\n",
    "                                          'pad': 0.04,\n",
    "                                          'fraction': 0.1,\n",
    "                                         })\n",
    "        colorbar = ax.images[0].colorbar\n",
    "        colorbar.ax.set_yticklabels([\"{:.1f}\".format(tk) for tk in colorbar.get_ticks()])\n",
    "        if pos == 1:\n",
    "            colorbar.set_label('%')\n",
    "        ax.set_title(f\"{titel[pos]}: {key}\", fontsize=14)\n",
    "        ax.set_xlabel(\"Columns\", fontsize=13)\n",
    "        ax.set_ylabel(\"Rows\", fontsize=13)\n",
    "\n",
    "def plot_diff_consts(old_const, new_const, g, ratio=False):\n",
    "    if ratio:\n",
    "        old_data = old_const['HG']['ml'] / old_const['MG']['mh']\n",
    "        new_data = new_const['HG']['ml'] / new_const['MG']['mh']\n",
    "        data1 = np.nanmean((new_data - old_data), axis=1)\n",
    "        data2 = np.nanmean((new_data - old_data)/old_data*100, axis=1)\n",
    "        data = [data1, data2]\n",
    "        key ='Slopes ratio HG/MG'\n",
    "        plot_definition(data, g, key)\n",
    "    else:\n",
    "        for i, key in enumerate(old_const[g].keys()):\n",
    "            data1 = np.nanmean((new_const[g][key] - old_const[g][key]), axis=1)\n",
    "            data2 = np.nanmean((new_const[g][key] - old_const[g][key])/old_const[g][key]*100, axis=1)\n",
    "            data = [data1, data2]\n",
    "            plot_definition(data, g, key)\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "for gain in old_gain_data.keys():\n",
    "    plot_diff_consts(old_gain_data, gain_data, gain)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plot_diff_consts(old_gain_data, gain_data, 'Ratio HG/MG', ratio=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "g_label = ['High gain', 'Medium gain', 'Bad pixels PC']\n",
    "for idx, g in enumerate(gain_data.keys()):\n",
    "    gs = gridspec.GridSpec(1, 2)\n",
    "    fig = plt.figure(figsize=(15, 7))\n",
    "    plt.suptitle(f'{g_label[idx]}', fontsize=16)\n",
    "    \n",
    "    for i, key in enumerate(gain_data[g].keys()):\n",
    "        if key is 'BadPixelsPC':\n",
    "            data = np.nanmean(gain_data[g][key]>0, axis=1)\n",
    "            vmin, vmax = (0,1)\n",
    "            ax = fig.add_subplot(gs[0, :])\n",
    "            ticks = [0, 0.5, 1]\n",
    "            \n",
    "        else:\n",
    "            data = np.nanmean(gain_data[g][key], axis=1)\n",
    "            vmin, vmax = get_range(data, 5)\n",
    "            ax = fig.add_subplot(gs[0, i])\n",
    "        geom.plot_data_fast(data,\n",
    "                                vmin=vmin, vmax=vmax, ax=ax, cmap=\"jet\", figsize=(13,7),\n",
    "                                colorbar={'shrink': 1,\n",
    "                                          'pad': 0.04,\n",
    "                                          'fraction': 0.1,\n",
    "                                          \n",
    "                                         })\n",
    "        colorbar = ax.images[0].colorbar\n",
    "        ax.set_title(key, fontsize=14)\n",
    "        ax.set_xlabel('Columns', fontsize=13)\n",
    "        ax.set_ylabel('Rows', fontsize=13)\n",
    "        \n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary across cells ##\n",
    "\n",
    "Good pixels only."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ratio = gain_data['HG']['ml'] / gain_data['MG']['mh']\n",
    "\n",
    "fig = plt.figure(figsize=(7, 7))\n",
    "ax = fig.add_subplot(111)\n",
    "data = np.nanmean(ratio, axis=1)\n",
    "vmin, vmax = get_range(data, 5)\n",
    "ax = geom.plot_data_fast(data,\n",
    "                                vmin=vmin, vmax=vmax, ax=ax, cmap=\"jet\", figsize=(6,7),\n",
    "                                colorbar={'shrink': 1,\n",
    "                                          'pad': 0.04,\n",
    "                                          'fraction': 0.1\n",
    "                                         })\n",
    "colorbar = ax.images[0].colorbar\n",
    "colorbar.set_label('HG slope / MG slope', fontsize=13)\n",
    "ax.set_title('High/Medium Gain Slope Ratio', fontsize=14)\n",
    "ax.set_xlabel('Columns', fontsize=13)\n",
    "ax.set_ylabel('Rows', fontsize=13)\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "for idx, g in enumerate(gain_data.keys()):\n",
    "    \n",
    "    for key in gain_data[g].keys():\n",
    "        data = np.copy(gain_data[g][key])\n",
    "        if key=='BadPixelsPC':\n",
    "            data = data>0\n",
    "        else:\n",
    "            data[gain_data['BP']['BadPixelsPC']>0] = np.nan\n",
    "\n",
    "        d = []\n",
    "        for i in range(nmods):\n",
    "            d.append({'x': np.arange(data[i].shape[0]),\n",
    "                      'y': np.nanmean(data[i], axis=(1,2)),\n",
    "                      'drawstyle': 'steps-pre',\n",
    "                      'label': f'{modules[i]}',\n",
    "                      'linewidth': 2,\n",
    "                      'linestyle': '--' if i>7 else '-'\n",
    "                      })\n",
    "\n",
    "        fig = plt.figure(figsize=(12, 6))\n",
    "        plt.suptitle(f'{g_label[idx]} - {key}', fontsize=16)\n",
    "        ax = fig.add_subplot(111)\n",
    "\n",
    "        _ = simplePlot(d, xrange=(-12, 510),\n",
    "                            x_label='Memory Cell ID',\n",
    "                            y_label=key,\n",
    "                            use_axis=ax,\n",
    "                            legend='top-left-frame-ncol8',)\n",
    "        ylim = ax.get_ylim()\n",
    "        ax.set_ylim(ylim[0], ylim[1] + np.abs(ylim[1]-ylim[0])*0.2)\n",
    "        ax.grid()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = []\n",
    "for i in range(nmods):\n",
    "    d.append({'x': np.arange(ratio[i].shape[0]),\n",
    "              'y': np.nanmedian(ratio[i], axis=(1,2)),\n",
    "              'drawstyle': 'steps-pre',\n",
    "              'label': f'{modules[i]}',\n",
    "              'linewidth': 2,\n",
    "              'linestyle': '--' if i>7 else '-'\n",
    "              })\n",
    "\n",
    "fig = plt.figure(figsize=(12, 6))\n",
    "plt.suptitle('High/Medium Gain Slope Ratio', fontsize=16)\n",
    "ax = fig.add_subplot(111)\n",
    "\n",
    "_ = simplePlot(d, xrange=(-12, 510),\n",
    "                    x_label='Memory Cell ID',\n",
    "                    y_label='Gain ratio ml/mh',\n",
    "                    use_axis=ax,\n",
    "                    legend='top-left-frame-ncol8',)\n",
    "ylim = ax.get_ylim()\n",
    "ax.set_ylim(ylim[0], ylim[1] + np.abs(ylim[1]-ylim[0])*0.2)\n",
    "ax.grid()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "table = []\n",
    "ratio_old = old_gain_data['HG']['ml'] / old_gain_data['MG']['mh']\n",
    "for cnt, mod in enumerate(modules):\n",
    "        \n",
    "    table.append((mod,\n",
    "                  f\"{np.nanmedian(ratio[cnt]):0.1f} +- {np.nanstd(ratio[cnt]):0.2f}\",\n",
    "                  f\"{np.nanmedian(ratio_old[cnt]):0.1f} +- {np.nanstd(ratio_old[cnt]):0.2f}\",\n",
    "                  f\"{np.nanmedian(gain_data['BP']['BadPixelsPC'][cnt]>0)*100:0.1f} ({np.nansum(gain_data['BP']['BadPixelsPC'][cnt]>0)})\"\n",
    "                ))\n",
    "\n",
    "all_HM = []\n",
    "all_HM_old = []\n",
    "for mod in range(len(modules)):\n",
    "    all_HM.extend(ratio[mod])\n",
    "    all_HM_old.extend(ratio_old[mod])\n",
    "all_HM = np.array(all_HM)\n",
    "all_HM_old = np.array(all_HM_old)\n",
    "\n",
    "all_MSK = np.array([list(msk) for msk in gain_data['BP']['BadPixelsPC']])\n",
    "\n",
    "table.append(('overall',\n",
    "              f\"{np.nanmean(all_HM):0.1f} +- {np.nanstd(all_HM):0.2f}\",\n",
    "              f\"{np.nanmean(all_HM_old):0.1f} +- {np.nanstd(all_HM_old):0.2f}\",\n",
    "              f\"{np.nanmean(all_MSK>0)*100:0.1f} ({np.nansum(all_MSK>0)})\"\n",
    "            ))\n",
    "\n",
    "md = display(Latex(tabulate.tabulate(table, tablefmt='latex',\n",
    "                                     headers=[\"Module\", \n",
    "                                              \"HG/MG Ratio\",\n",
    "                                              \"Previous HG/MG Ratio\",\n",
    "                                              \"Bad pixels [%(Count)]\"])))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary high-medium gain ratio (good pixels only) + histograms"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "colors = cm.rainbow(np.linspace(0, 1, nmods))\n",
    "\n",
    "gs = gridspec.GridSpec(1, 2)\n",
    "fig = plt.figure(figsize=(17, 8))\n",
    "\n",
    "\n",
    "ratio[gain_data['BP']['BadPixelsPC'] > 0] = np.nan\n",
    "data = np.nanmean(ratio, axis=1)\n",
    "vmin, vmax = get_range(data, 5)\n",
    "ax = fig.add_subplot(gs[0, 0])\n",
    "geom.plot_data_fast(data,\n",
    "                        vmin=vmin, vmax=vmax, ax=ax, cmap=\"jet\", figsize=(12.5,7),\n",
    "                        colorbar={'shrink': 1,\n",
    "                                  'pad': 0.04,\n",
    "                                  'fraction': 0.1\n",
    "                                 })\n",
    "colorbar = ax.images[0].colorbar\n",
    "colorbar.set_label('HG/MG', fontsize=12)\n",
    "ax.set_xlabel('Columns', fontsize=12)\n",
    "ax.set_ylabel('Rows', fontsize=12)\n",
    "\n",
    "ax = fig.add_subplot(gs[0,1])\n",
    "for cnt, mod in enumerate(modules):\n",
    "    h, e = np.histogram(ratio[cnt].flatten(), bins=100, range=(vmin, vmax))\n",
    "    ax.plot(e[:-1], h, color=colors[cnt],linewidth=2, label=f'{mod}', alpha=0.8)\n",
    "    ax.set_xlabel('High/Medium Gain Ratio', fontsize=13)\n",
    "    ax.set_ylabel('Counts', fontsize=13)\n",
    "    plt.ticklabel_format(axis='y', style='sci', scilimits=(0,0))\n",
    "ax.grid()\n",
    "ax.legend()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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"
  },
  "latex_envs": {
   "LaTeX_envs_menu_present": true,
   "autocomplete": true,
   "bibliofile": "biblio.bib",
   "cite_by": "apalike",
   "current_citInitial": 1,
   "eqLabelWithNumbers": true,
   "eqNumInitial": 1,
   "hotkeys": {
    "equation": "Ctrl-E",
    "itemize": "Ctrl-I"
   },
   "labels_anchors": false,
   "latex_user_defs": false,
   "report_style_numbering": false,
   "user_envs_cfg": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}