Skip to content
Snippets Groups Projects
PlotFromCalDB_NBC.ipynb 36.2 KiB
Newer Older
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Statistical analysis of calibration factors#\n",
    "\n",
    "Author: Mikhail Karnevskiy, Version 0.2\n",
    "Plot calibration constants retrieved from the cal. DB.\n",
    "\n",
    "To be visualized, calibration constants are averaged per group of pixels. Plots shows calibration constant over time for each constant.\n",
    "\n",
    "Values shown in plots are saved in h5 files."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "start_date = \"2019-01-01\" # date to start investigation interval from\n",
    "end_date = \"NOW\" # date to end investigation interval at, can be \"now\"\n",
    "dclass=\"jungfrau\" # Detector class\n",
    "modules = [\"Jungfrau_M035\"] # detector entry in the DB to investigate\n",
    "submodules = [2] # module index of a modular detector (1 for Q1M1 of AGIPD), range allowed \n",
    "constants = ['RelativeGain'] # constants to plot\n",
    "nconstants = 20 # Number of time stamps to plot. If not 0, overcome start_date.\n",
    "max_time = 15 # max time margin in minutes to match bad pixels\n",
    "nMemToShow = 16 # Number of memory cells to be shown in plots\n",
    "gain_setting = [0,1,2] # gain stages\n",
    "bias_voltage = [90, 180] # Bias voltage\n",
    "temperature = [291] # Operation temperature\n",
    "integration_time = [4.96, 10, 50, 250] # Integration time\n",
    "pixels_x=[1024] # number of pixels along X axis\n",
    "pixels_y=[512] # number of pixels along Y axis\n",
    "in_vacuum = [0] # 0 if detector is operated in room pressure\n",
    "memory_cells = [1, 16] # number of memory cells\n",
    "acquisition_rate = [1.1] # aquisition rate\n",
    "parameter_names = ['bias_voltage', 'integration_time', 'pixels_x', 'pixels_y', 'temperature', 'memory_cells'] # names of parameters\n",
    "separate_plot = ['gain_setting', 'memory_cells', 'integration_time'] # Plot on separate plots\n",
    "x_labels = ['Sensor Temperature', 'Integration Time'] # parameters to be shown on X axis: Acquisition rate, Memory cells, Sensor Temperature, Integration Time\n",
    "photon_energy = 9.2 # Photon energy of the beam\n",
    "out_folder = \"/gpfs/exfel/data/scratch/karnem/test_bla4/\" # output folder\n",
    "use_existing = \"\" # If not empty, constants stored in given folder will be used\n",
    "cal_db_interface = \"tcp://max-exfl-cal001:8016\" # the database interface to use\n",
    "cal_db_timeout = 180000 # timeout on caldb requests\",\n",
    "plot_range = 3 # range for plotting in units of median absolute deviations\n",
    "spShape = [256, 64] # Shape of superpixel\n",
    "sp_name = 'ASIC IDs' # name of superpixel\n",
    "gain_titles = ['High gain', 'Medium gain', 'Low gain'] # Title inset related to gain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import copy\n",
    "import datetime\n",
    "import os\n",
    "import sys\n",
    "import warnings\n",
    "\n",
    "import dateutil.parser\n",
    "import numpy as np\n",
    "\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "from cal_tools.ana_tools import (\n",
    "    HMType,\n",
    "    IMType,\n",
    "    combine_constants,\n",
    "    combine_lists,\n",
    "    get_range,\n",
    "    hm_combine,\n",
    "    load_data_from_hdf5,\n",
    "    save_dict_to_hdf5,\n",
    ")\n",
    "from cal_tools.tools import get_from_db, get_random_db_interface\n",
    "from iCalibrationDB import Conditions, ConstantMetaData, Constants, Detectors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Prepare variables\n",
    "submodules = [\"Q{}M{}\".format(x // 4 + 1, x % 4 + 1) for x in submodules]\n",
    "\n",
    "# No submodules for small detectors\n",
    "if dclass not in ['AGIPD', 'LPD']:\n",
    "    submodules = ['']\n",
    "\n",
    "# 0 is considered as None.\n",
    "acquisition_rate = [x if x > 0 else None for x in acquisition_rate]\n",
    "\n",
    "nMem = max(memory_cells) # Number of mem Cells to store\n",
    "\n",
    "parameters = [globals()[x] for x in parameter_names]\n",
    "\n",
    "# Empty list from the command line may not work\n",
    "if separate_plot == ['']:\n",
    "    separate_plot = []\n",
    "\n",
    "# Mapping between consatnts and their bad pixel maps\n",
    "constantsDark = {\"SlopesFF\": 'BadPixelsFF',\n",
    "                 'SlopesPC': 'BadPixelsPC',\n",
    "                 'SlopesCI': 'BadPixelsCI',\n",
    "                 'Noise': 'BadPixelsDark',\n",
    "                 'Offset': 'BadPixelsDark'}\n",
    "print('Bad pixels data: ', constantsDark)\n",
    "\n",
    "# Define parameters in order to perform loop over time stamps\n",
    "start = datetime.datetime.now() if start_date.upper() == \"NOW\" else dateutil.parser.parse(\n",
    "    start_date)\n",
    "end = datetime.datetime.now() if end_date.upper() == \"NOW\" else dateutil.parser.parse(\n",
    "    end_date)\n",
    "\n",
    "# Create output folder\n",
    "os.makedirs(out_folder, exist_ok=True)\n",
    "\n",
    "print('CalDB Interface: {}'.format(cal_db_interface))\n",
    "print('Start time at: ', start)\n",
    "print('End time at: ', end)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "parameter_list = combine_lists(*parameters, names=parameter_names)\n",
    "print(parameter_list)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Retrieve list of meta-data\n",
    "constant_versions = []\n",
    "constant_parameters = []\n",
    "constantBP_versions = []\n",
    "\n",
    "# Loop over constants\n",
    "for c, const in enumerate(constants):\n",
    "    \n",
    "    for db_module in modules:\n",
    "        det = getattr(Detectors, db_module)\n",
    "        \n",
    "        if dclass in ['AGIPD', 'LPD']:\n",
    "            det = getattr(det, submodules[0])\n",
    "        \n",
    "        # Get getector conditions\n",
    "        if dclass=='CCD':\n",
    "            dconstants = getattr(Constants, dclass)(det.detector_type)\n",
    "        else:\n",
    "            dconstants = getattr(Constants, dclass)\n",
    "\n",
    "        if use_existing != \"\":\n",
    "            break\n",
    "\n",
    "        # Loop over parameters\n",
    "        for pars in parameter_list:\n",
    "\n",
    "            if (const in [\"Offset\", \"Noise\", \"SlopesPC\", \"SlopesCI\", \"RelativeGain\"] or \"DARK\" in const.upper()):\n",
    "                dcond = Conditions.Dark\n",
    "                mcond = getattr(dcond, dclass)(**pars)\n",
    "            else:\n",
    "                dcond = Conditions.Illuminated\n",
    "                mcond = getattr(dcond, dclass)(**pars,\n",
    "                                    photon_energy=photon_energy)\n",
    "\n",
    "\n",
    "\n",
    "            print('Request: ', const, 'with paramters:', pars)\n",
    "            # Request Constant versions for given parameters and module\n",
    "            data = get_from_db(det,\n",
    "                               getattr(dconstants,\n",
    "                                       const)(),\n",
    "                               copy.deepcopy(mcond), None,\n",
    "                               cal_db_interface,\n",
    "                               creation_time=start,\n",
    "                               verbosity=0,\n",
    "                               timeout=cal_db_timeout,\n",
    "                               meta_only=True,\n",
    "                               version_info=True)\n",
    "\n",
    "            if not isinstance(data, list):\n",
    "                    continue\n",
    "\n",
    "            if const in constantsDark:\n",
    "                # Request BP constant versions\n",
    "                print('constantDark:', constantsDark[const], )        \n",
    "                dataBP = get_from_db(det,\n",
    "                                     getattr(dconstants, \n",
    "                                             constantsDark[const])(),\n",
    "                                     copy.deepcopy(mcond), None,\n",
    "                                     cal_db_interface,\n",
    "                                     creation_time=start,\n",
    "                                     verbosity=0,\n",
    "                                     timeout=cal_db_timeout,\n",
    "                                     meta_only=True,\n",
    "                                     version_info=True)\n",
    "\n",
    "                if not isinstance(dataBP, list):\n",
    "                    constant_versions += data\n",
    "                    constant_parameters += [copy.deepcopy(pars)]*len(data)\n",
    "                    constantBP_versions += [None]*len(data)\n",
    "                    continue\n",
    "\n",
    "                for d in data:\n",
    "                    # Match proper BP constant version\n",
    "                    # and get constant version within\n",
    "                    # requested time range\n",
    "                    if d is None:\n",
    "                        print('Time or data is not found!')\n",
    "                        continue\n",
    "\n",
    "                    dt = dateutil.parser.parse(d['begin_at'])\n",
    "\n",
    "                    if (dt.replace(tzinfo=None) > end or \n",
    "                        (nconstants==0 and dt.replace(tzinfo=None) < start)):\n",
    "                        continue\n",
    " \n",
    "                    closest_BP = None\n",
    "                    closest_BPtime = None\n",
    "                    found_BPmatch = False\n",
    "            \n",
    "                    for dBP in dataBP:\n",
    "                        if dBP is None:\n",
    "                            constantBP_versions.append(None)\n",
    "                            constant_versions.append(d)\n",
    "                            constant_parameters.append(copy.deepcopy(pars))\n",
    "                            print(\"Bad pixels are not found!\")\n",
    "                            continue\n",
    "\n",
    "                        dt = dateutil.parser.parse(d['begin_at'])\n",
    "                        dBPt = dateutil.parser.parse(dBP['begin_at'])\n",
    "\n",
    "                        if dt == dBPt:\n",
    "                            found_BPmatch = True\n",
    "                        else:\n",
    "\n",
    "                            if np.abs(dBPt-dt).total_seconds() < (max_time*60):\n",
    "                                if closest_BP is None:\n",
    "                                    closest_BP = dBP\n",
    "                                    closest_BPtime = dBPt\n",
    "                                else:\n",
    "                                    if np.abs(dBPt-dt) < np.abs(closest_BPtime-dt):\n",
    "                                        closest_BP = dBP\n",
    "                                        closest_BPtime = dBPt\n",
    "\n",
    "                            if dataBP.index(dBP) ==  len(dataBP)-1:\n",
    "                                if closest_BP:\n",
    "                                    dBP = closest_BP\n",
    "                                    dBPt = closest_BPtime\n",
    "                                    found_BPmatch = True\n",
    "                                else:\n",
    "                                    print('Bad pixels are not found!')\n",
    "\n",
    "                        if found_BPmatch:\n",
    "                            print(\"Found constant {}: begin at {}\".format(const, dt))\n",
    "                            print(\"Found bad pixels at {}\".format(dBPt))\n",
    "                            constantBP_versions.append(dBP)\n",
    "                            constant_versions.append(d)\n",
    "                            constant_parameters.append(copy.deepcopy(pars))\n",
    "                            found_BPmatch = False\n",
    "                            break\n",
    "                if not found_BPmatch:\n",
    "                    print('Bad pixels are not matched')\n",
    "                    constantBP_versions.append(None)\n",
    "                    constant_versions.append(d)\n",
    "                    constant_parameters.append(copy.deepcopy(pars))\n",
    "                \n",
    "            else:\n",
    "                constant_versions += data\n",
    "                constant_parameters += [copy.deepcopy(pars)]*len(data)\n",
    "                constantBP_versions += [None]*len(data)\n",
    "\n",
    "# Remove dublications\n",
    "constant_versions_tmp = []\n",
    "constant_parameters_tmp = []\n",
    "constantBP_versions_tmp = []\n",
    "for i, x in enumerate(constant_versions):\n",
    "    if x not in constant_versions_tmp:\n",
    "        constant_versions_tmp.append(x)\n",
    "        constant_parameters_tmp.append(constant_parameters[i])\n",
    "        if i<len(constantBP_versions):\n",
    "            constantBP_versions_tmp.append(constantBP_versions[i])\n",
    "constant_versions=constant_versions_tmp\n",
    "constantBP_versions=constantBP_versions_tmp\n",
    "constant_parameters=constant_parameters_tmp\n",
    "\n",
    "print('Number of stored constant versions is {}'.format(len(constant_versions)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "def prepare_to_store(a, nMem):\n",
    "    \"\"\"\n",
    "    Different constants for AGIPD and LPD may have different array shape.\n",
    "    This function unify array shape.\n",
    "    \"\"\"\n",
    "    if dclass in ['AGIPD', 'LPD']:\n",
    "        shape = list(a.shape[:2])+[nMem]\n",
    "        b = np.full(shape, np.nan)\n",
    "        b[:, :, :a.shape[2]] = a[:, :, :]\n",
    "        return b\n",
    "    else:\n",
    "        return a\n",
    "\n",
    "def get_rebined(a, rebin):\n",
    "    \"\"\"\n",
    "    Group of pixels are formed here for better visialization\n",
    "    \"\"\"\n",
    "    if dclass in ['AGIPD', 'LPD', 'jungfrau']:\n",
    "        return a.reshape(\n",
    "                int(a.shape[0] / rebin[0]),\n",
    "                rebin[0],\n",
    "                int(a.shape[1] / rebin[1]),\n",
    "                rebin[1],\n",
    "                a.shape[2],\n",
    "                a.shape[3])\n",
    "        \n",
    "    else:\n",
    "        return a[:,:,0].reshape(\n",
    "                int(a.shape[0] / rebin[0]),\n",
    "                rebin[0],\n",
    "                int(a.shape[1] / rebin[1]),\n",
    "                rebin[1])\n",
    "        \n",
    "\n",
    "def modify_const(const, data, isBP = False):\n",
    "    \"\"\"\n",
    "    Shape of an array for some constants changes over time.\n",
    "    Modification is needed to unify shape of array and\n",
    "    make possible to show constants on the same plot.\n",
    "    \"\"\"\n",
    "    if dclass==\"jungfrau\" and data.shape[1] == 512:\n",
    "        data = data.swapaxes(0, 1)\n",
    "        return data\n",
    "    \n",
    "    if dclass==\"AGIPD\":\n",
    "        const = const.split('_')[0]\n",
    "        if const in ['SlopesFF']:\n",
    "            if (len(data.shape) == 4):\n",
    "                data = data[:, :, :, 0][..., None]\n",
    "            else:\n",
    "                data = data[..., None]\n",
    "\n",
    "            if data.shape[2]<3:\n",
    "                data = data[:,:,0,None]\n",
    "\n",
    "        if not isBP:\n",
    "            if data.shape[0] != 128:\n",
    "                data = data.swapaxes(0, 2).swapaxes(1, 3).swapaxes(2, 3)\n",
    "\n",
    "            # Copy slope medium to be saved later\n",
    "            if const in ['SlopesPC']:\n",
    "                data[:, :, :, 1] = data[:, :, :, 3]\n",
    "        else:\n",
    "            if const in ['SlopesPC']:\n",
    "                if len(data.shape) == 3:\n",
    "                    data = data[:, :, :, None].repeat(10, axis=3)\n",
    "\n",
    "            if data.shape[0] != 128:\n",
    "                data = data.swapaxes(0, 1).swapaxes(1, 2)\n",
    "\n",
    "        if len(data.shape) < 4:\n",
    "            print(data.shape, \"Unexpected shape!\")\n",
    "        return data\n",
    "    \n",
    "    if dclass==\"LPD\":\n",
    "        const = const.split('_')[0]\n",
    "        if const in ['SlopesFF']:\n",
    "            data = data[..., None, None]\n",
    "\n",
    "        if(len(data.shape)==5):\n",
    "            data = data[:,:,:,:,0]\n",
    "\n",
    "        if len(data.shape) < 4:\n",
    "            print(data.shape, \"Unexpected shape!\")\n",
    "\n",
    "        if data.shape[0] != 256:\n",
    "            data = data.swapaxes(0, 2).swapaxes(1,3).swapaxes(2,3) \n",
    "    \n",
    "    return data\n",
    "\n",
    "ret_constants = {}\n",
    "constant_data = ConstantMetaData()\n",
    "constant_BP = ConstantMetaData()\n",
    "\n",
    "# sort over begin_at\n",
    "idxs, _ = zip(*sorted(enumerate(constant_versions), \n",
    "                     key=lambda x: x[1]['begin_at'], reverse=True))\n",
    "\n",
    "for i in idxs:\n",
    "    const = constant_versions[i]['data_set_name'].split('/')[-2]\n",
    "    qm = constant_versions[i]['physical_device']['name']\n",
    "    # fix naming for Jungfrau039\n",
    "    if qm == 'Jungfrau1':\n",
    "        qm = 'JungfrauM039'\n",
    "    # use submodule name for big detectors\n",
    "    if dclass in ['AGIPD', 'LPD']:\n",
    "        qm = submodules[0]\n",
    "    # Add insets for parameters\n",
    "    for key in separate_plot:\n",
    "        # Several constant already contains gain stages\n",
    "        if key == 'gain_setting' and dclass in ['AGIPD', 'LPD', 'jungfrau']:\n",
    "            val = 0\n",
    "        else:\n",
    "            val = constant_parameters[i][key]\n",
    "        const = '{}_{}{}'.format(const, key[0], val)\n",
    "        \n",
    "    if not const in ret_constants:\n",
    "        ret_constants[const] = {}\n",
    "    if not qm in ret_constants[const]:\n",
    "            ret_constants[const][qm] = []\n",
    "            \n",
    "    if nconstants>0 and len(ret_constants[const][qm])>=nconstants:\n",
    "        continue\n",
    "        \n",
    "    constant_data.retrieve_from_version_info(constant_versions[i])\n",
    "    cdata = constant_data.calibration_constant.data\n",
    "    ctime = constant_data.calibration_constant_version.begin_at\n",
    "    cdata = modify_const(const, cdata)\n",
    "    print(\"constant: {}, module {}, begin_at {}\".format(const, qm, ctime))\n",
    "    \n",
    "    if constantBP_versions[i]:\n",
    "        constant_BP.retrieve_from_version_info(constantBP_versions[i])\n",
    "        cdataBP = constant_BP.calibration_constant.data\n",
    "        cdataBP = modify_const(const, cdataBP, True)\n",
    "        \n",
    "        if cdataBP.shape != cdata.shape:\n",
    "            print('Wrong bad pixel shape! {}, expected {}'.format(cdataBP.shape, cdata.shape))\n",
    "            cdataBP = np.full_like(cdata, IMType.NO_BPMAP.value)\n",
    "        \n",
    "        # Apply bad pixel mask\n",
    "        cdataABP = np.copy(cdata)\n",
    "        cdataABP[cdataBP > 0] = np.nan\n",
    "    \n",
    "        # Create superpixels for constants with BP applied\n",
    "        cdataABP = get_rebined(cdataABP, spShape)\n",
    "        toStoreBP = np.nanmean(cdataABP, axis=(1, 3))\n",
    "        toStoreBPStd = np.nanstd(cdataABP, axis=(1, 3))\n",
    "\n",
    "        # Prepare number of bad pixels per superpixels\n",
    "        cdataBP = get_rebined(cdataBP, spShape)\n",
    "        cdataNBP = np.nansum(cdataBP > 0, axis=(1, 3))\n",
    "\n",
    "    # Create superpixels for constants without BP applied\n",
    "    cdata = get_rebined(cdata, spShape)\n",
    "    toStoreStd = np.nanstd(cdata, axis=(1, 3))\n",
    "    toStore = np.nanmean(cdata, axis=(1, 3))\n",
    "    \n",
    "    if not constantBP_versions[i]:\n",
    "        toStoreBP = np.full_like(toStore, IMType.NO_BPMAP.value)\n",
    "        toStoreBPStd = np.full_like(toStore, IMType.NO_BPMAP.value)\n",
    "        cdataNBP = np.full_like(toStore, IMType.NO_BPMAP.value)\n",
    "    \n",
    "    # Convert parameters to dict\n",
    "    dpar = {p.name: p.value for p in constant_data.detector_condition.parameters}\n",
    "    \n",
    "    # Several constants have dimensions running over gain.\n",
    "    # All gain stages are stored as separate arrays.\n",
    "    if len(toStore.shape)==4:\n",
    "        for i in range(3):\n",
    "            if i>0 and 'gain_setting' in separate_plot:\n",
    "                const = const.replace('_g{}'.format(i-1), '_g{}'.format(i))\n",
    "            # FF has only high gain\n",
    "            if 'SlopesFF' in const and i>0:\n",
    "                continue\n",
    "            # PC only high and medium.\n",
    "            if 'SlopesPC' in const and i>1:\n",
    "                continue\n",
    "                \n",
    "            if not const in ret_constants:\n",
    "                ret_constants[const] = {}\n",
    "            if not qm in ret_constants[const]:\n",
    "                ret_constants[const][qm] = []\n",
    "            print(\"Store values in dict\", const, qm, ctime)\n",
    "            ret_constants[const][qm].append({'ctime': ctime,\n",
    "                                     'nBP': prepare_to_store(cdataNBP[:,:,:,i], nMem),\n",
    "                                     'dataBP': prepare_to_store(toStoreBP[:,:,:,i], nMem),\n",
    "                                     'dataBPStd': prepare_to_store(toStoreBPStd[:,:,:,i], nMem),\n",
    "                                     'data': prepare_to_store(toStore[:,:,:,i], nMem),\n",
    "                                     'dataStd': prepare_to_store(toStoreStd[:,:,:,i], nMem),\n",
    "                                     'mdata': dpar}) \n",
    "        \n",
    "        \n",
    "        \n",
    "    else:\n",
    "        print(\"Store values in dict\", const, qm, ctime)\n",
    "        ret_constants[const][qm].append({'ctime': ctime,\n",
    "                                     'nBP': cdataNBP,\n",
    "                                     'dataBP': toStoreBP,\n",
    "                                     'dataBPStd': toStoreBPStd,\n",
    "                                     'data': toStore,\n",
    "                                     'dataStd': toStoreStd,\n",
    "                                     'mdata': dpar})  \n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "if use_existing == \"\":\n",
    "    print('Save data to {}/CalDBAna_{}_{}_{}.h5'.format(out_folder, dclass, db_module, submodules[0]))\n",
    "    save_dict_to_hdf5(ret_constants,\n",
    "                      '{}/CalDBAna_{}_{}_{}.h5'.format(out_folder, dclass, db_module, submodules[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if use_existing == \"\":\n",
    "    fpath = '{}/CalDBAna_{}_{}_{}.h5'.format(out_folder, dclass, db_module, submodules[0])\n",
    "    fpath = '{}/CalDBAna_{}_{}_{}.h5'.format(use_existing, dclass, db_module, submodules[0])\n",
    "\n",
    "print('Load data from {}'.format(fpath))\n",
    "ret_constants = load_data_from_hdf5(fpath)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# For AGIPD and LPD\n",
    "# Combine FF and PC data to calculate Gain\n",
    "# Estimate Noise in units of electrons\n",
    "\n",
    "ret_constants[\"Gain_g0\"] = {}\n",
    "ret_constants[\"Noise-e_g0\"] = {}\n",
    "\n",
    "for mod in list(range(16)):\n",
    "    # The check is perform inside the for loop\n",
    "    # in order to use break\n",
    "    # This make code more readable\n",
    "    if (\"SlopesFF_g0\" not in ret_constants or\n",
    "            \"SlopesPC_g0\" not in ret_constants):\n",
    "        break\n",
    "\n",
    "    qm = \"Q{}M{}\".format(mod // 4 + 1, mod % 4 + 1)\n",
    "    if (qm not in ret_constants[\"SlopesFF_g0\"] or\n",
    "            qm not in ret_constants[\"SlopesPC_g0\"]):\n",
    "        continue\n",
    "\n",
    "    print(qm)\n",
    "    ret_constants[\"Gain_g0\"][qm] = {}\n",
    "\n",
    "    dataFF = ret_constants[\"SlopesFF_g0\"][qm]\n",
    "    dataPC = ret_constants[\"SlopesPC_g0\"][qm]\n",
    "\n",
    "    if (len(dataFF) == 0 or len(dataPC) == 0):\n",
    "        continue\n",
    "\n",
    "    ctimesFF = np.array(dataFF[\"ctime\"])\n",
    "    ctimesPC = np.array(dataPC[\"ctime\"])\n",
    "\n",
    "    ctime, icomb = combine_constants(ctimesFF, ctimesPC)\n",
    "\n",
    "    cdataPC_vs_time = np.array(dataPC[\"data\"])[...]\n",
    "    cdataFF_vs_time = np.array(dataFF[\"data\"])[...]\n",
    "\n",
    "    cdataFF_vs_time = np.nanmedian(cdataFF_vs_time, axis=3)[..., None]\n",
    "\n",
    "    cdataFF_vs_time /= np.nanmedian(cdataFF_vs_time, axis=(1, 2, 3))[:, None,\n",
    "                       None, None]\n",
    "    cdataPC_vs_time /= np.nanmedian(cdataPC_vs_time, axis=(1, 2, 3))[:, None,\n",
    "                       None, None]\n",
    "\n",
    "    gain_vs_time = []\n",
    "    for iFF, iPC in icomb:\n",
    "        gain_vs_time.append(cdataFF_vs_time[iFF] * cdataPC_vs_time[iPC])\n",
    "\n",
    "    print('Shape of gain array: ', np.array(gain_vs_time).shape)\n",
    "    \n",
    "    ctime_ts = [t.timestamp() for t in ctime]\n",
    "    \n",
    "    ret_constants[\"Gain_g0\"][qm][\"ctime\"] = ctime\n",
    "    ret_constants[\"Gain_g0\"][qm][\"data\"] = np.array(gain_vs_time)\n",
    "\n",
    "    if \"Noise_g0\" not in ret_constants:\n",
    "        continue\n",
    "\n",
    "    if qm not in ret_constants[\"Noise_g0\"]:\n",
    "        continue\n",
    "\n",
    "    dataN = ret_constants[\"Noise_g0\"][qm]\n",
    "    if len(dataN) == 0:\n",
    "        continue\n",
    "\n",
    "    ret_constants[\"Noise-e_g0\"][qm] = {}\n",
    "            \n",
    "    ctimesG = np.array(ctime)\n",
    "    ctimesN = np.array(dataN[\"ctime\"])\n",
    "\n",
    "    ctime, icomb = combine_constants(ctimesG, ctimesN)\n",
    "\n",
    "    cdataG_vs_time = np.array(gain_vs_time)\n",
    "    cdataN_vs_time = np.array(dataN[\"data\"])[...]\n",
    "\n",
    "    data_vs_time = []\n",
    "    for iG, iN in icomb:\n",
    "        data_vs_time.append(\n",
    "            cdataN_vs_time[iN] * adu_to_photon / cdataG_vs_time[iG])\n",
    "\n",
    "    print('Shape of gain array: ',np.array(gain_vs_time).shape)\n",
    "    ctime_ts = [t.timestamp() for t in ctime]\n",
    "    ret_constants[\"Noise-e_g0\"][qm][\"ctime\"] = ctime\n",
    "    ret_constants[\"Noise-e_g0\"][qm][\"data\"] = np.array(data_vs_time)\n",
    "    \n",
    "save_dict_to_hdf5({k:v for k,v in ret_constants.items() if k in ['Gain_g0', 'Noise-e_g0']},\n",
    "                  '{}/CalDBAna_{}_Gain.h5'.format(out_folder, dclass))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Parameters for plotting\n",
    "\n",
    "keys = {\n",
    "    'Mean': ['data', '', 'Mean over pixels'],\n",
    "    'std': ['dataStd', '', '$\\sigma$ over pixels'],\n",
    "    'MeanBP': ['dataBP', 'Good pixels only', 'Mean over pixels'],\n",
    "    'NBP': ['nBP', '', 'Fraction of BP'],\n",
    "    'stdBP': ['dataBPStd', 'Good pixels only', '$\\sigma$ over pixels'],\n",
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "print('Plot calibration constants')\n",
    "\n",
    "# loop over constat type\n",
    "for const, modules in ret_constants.items():\n",
    "    \n",
    "    # split key to constant name and list of insets\n",
    "    const = const.split(\"_\")\n",
    "    gain = [int(x[1]) for x in const if 'g' in x]\n",
    "    gain = gain[0] if len(gain)>0 else None\n",
    "    \n",
    "    print('Const: {}'.format(const))\n",
    "    # summary over modules\n",
    "    mod_data = {}\n",
    "    mod_names = []\n",
    "    mod_times = []\n",
    "    # Loop over modules\n",
    "    for mod, data in modules.items():\n",
    "        print('Module: {}'.format(mod))\n",
    "\n",
    "        ctimes = np.array(data[\"ctime\"])\n",
    "        ctimes_ticks = [x.strftime('%y-%m-%d') for x in ctimes]\n",
    "\n",
    "        if (\"mdata\" in data):\n",
    "            cmdata = np.array(data[\"mdata\"])\n",
    "            for i, tick in enumerate(ctimes_ticks):\n",
    "                for entr in x_labels:\n",
    "                    key  = entr[0].upper()\n",
    "                    val = cmdata[i].get(entr, None)\n",
    "                    if val is not None:\n",
    "                        ctimes_ticks[i] += ', {}={:.1f}'.format(key, val)\n",
    "                        \n",
    "        sort_ind = np.argsort(ctimes_ticks)\n",
    "        ctimes_ticks = list(np.array(ctimes_ticks)[sort_ind])\n",
    "\n",
    "        # Create sorted by data dataset\n",
    "        rdata = {}\n",
    "        for key, item in keys.items():\n",
    "            if item[0] in data:\n",
    "                rdata[key] = np.array(data[item[0]])[sort_ind]\n",
    "                \n",
    "        # Limit number of memory cells to show\n",
    "        # with respect to one available in data\n",
    "        if len(rdata['Mean'].shape)==4:\n",
    "            nMem = min(nMemToShow, rdata['Mean'].shape[3])\n",
    "        if len(rdata['Mean'].shape)<4:\n",
    "            nMem = 1\n",
    "\n",
    "        nTimes = rdata['Mean'].shape[0]\n",
    "        nPixels = rdata['Mean'].shape[1] * rdata['Mean'].shape[2]\n",
    "        nBins = nMem * nPixels\n",
    "\n",
    "        # Avoid too low values\n",
    "        if const[0] in [\"Noise10Hz\", \"Offset10Hz\"]:\n",
    "            rdata['Mean'][rdata['Mean'] < 0.1] = np.nan\n",
    "            if 'MeanBP' in rdata:\n",
    "                rdata['MeanBP'][rdata['MeanBP'] < 0.1] = np.nan\n",
    "            if 'NBP' in rdata:\n",
    "                rdata['NBP'] = rdata['NBP'].astype(float)\n",
    "                rdata['NBP'][rdata['NBP'] == spShape[0]*spShape[1]] = np.nan\n",
    "                rdata[\"NBP\"] = rdata[\"NBP\"] / (spShape[0] * spShape[1]) * 100\n",
    "\n",
    "        # Reshape: ASICs over cells for plotting\n",
    "        pdata = {}\n",
    "        for key in rdata:\n",
    "            if len(rdata[key].shape)<3:\n",
    "                continue\n",
    "            if len(rdata[key].shape)==4:\n",
    "                # take first nMem memory cells\n",
    "                pdata[key] = rdata[key][:, :, :, :nMem].reshape(\n",
    "                nTimes, nBins).swapaxes(0, 1)\n",
    "            else:\n",
    "                # case of no memory cells\n",
    "                pdata[key] = rdata[key].reshape(nTimes, nBins).swapaxes(0, 1)\n",
    "\n",
    "        # Summary over ASICs\n",
    "        adata = {}\n",
    "        for key in rdata:\n",
    "            if len(rdata[key].shape)<3 or nMem==1:\n",
    "                continue\n",
    "            adata[key] = np.nanmean(rdata[key], axis=(1, 2)).swapaxes(0, 1)\n",
    "\n",
    "        # Summary information over modules\n",
    "        for key in pdata:\n",
    "            if key not in mod_data:\n",
    "                mod_data[key] = []\n",
    "            if key == 'NBP':\n",
    "                mod_data[key].append(np.nansum(pdata[key], axis=0))\n",
    "            else:\n",
    "                mod_data[key].append(np.nanmean(pdata[key], axis=0))\n",
    "        mod_names.append(mod)\n",
    "        mod_times.append(ctimes[sort_ind])\n",
    "\n",
    "        # Plotting\n",
    "        for key in pdata:\n",
    "\n",
    "            if len(pdata[key].shape)<2:\n",
    "                continue\n",
    "\n",
    "            vmin,vmax = get_range(pdata[key][::-1].flatten(), plot_range)\n",
    "                unit = '[%]'\n",
    "                title = 'BadPixelsDark'\n",
    "            else:\n",
    "                unit = '[ADU]'\n",
    "            title += ', module {}'.format(mod)\n",
    "            if keys[key][1] != '':\n",
    "                title += ', {}'.format(keys[key][1])\n",
    "            if gain is not None:\n",
    "                title += ', {}'.format(gain_titles[gain])\n",
    "            cb_label = '{}, {} {}'.format(const[0], keys[key][2], unit)\n",
    "\n",
    "            fname = '{}/{}_{}'.format(out_folder, const[0], mod.replace('_', ''))\n",
    "            for item in const[1:]:\n",
    "                fname = '{}_{}'.format(fname, item)\n",
    "            fname = '{}_ASIC_{}.png'.format(fname, key)\n",
    "            if nMem>1:\n",
    "                htype=HMType.INSET_AXIS\n",
    "            else:      \n",
    "                htype=HMType.mro\n",
    "\n",
    "            hm_combine(pdata[key][::-1].astype(float), htype=htype, mem_cells=nMem,\n",
    "                      x_label='Creation Time', y_label=sp_name,\n",
    "                      y_ticks=np.arange(0, nBins+1, nBins//16),\n",
    "                       y_ticklabels=np.arange(0, nPixels+1, nPixels//16),\n",
    "                      x_ticklabels=ctimes_ticks,\n",
    "                      x_ticks=np.arange(len(ctimes_ticks))+0.3,\n",
    "                      title=title, cb_label=cb_label,\n",
    "                      vmin=vmin, vmax=vmax,\n",
    "                      fname=fname,\n",
    "                      pad=[0.125, 0.125, 0.12, 0.185])\n",
    "\n",
    "            if nMem>1:\n",
    "                vmin,vmax = get_range(adata[key][::-1].flatten(), plot_range)\n",
    "                hm_combine(adata[key].astype(float), htype=HMType.mro,\n",
    "                      x_label='Creation Time', y_label='Memory cell ID',\n",
    "                      x_ticklabels=ctimes_ticks,\n",
    "                      x_ticks=np.arange(len(ctimes_ticks))+0.3,\n",
    "                      title=title, cb_label=cb_label,\n",
    "                      fname=fname.replace('ASIC', 'MEM'),\n",
    "                      vmin=vmin, vmax=vmax)\n",
    "\n",
    "            plt.show()\n",
    "    # Summary over modules\n",
    "    for key in mod_data:\n",
    "        if dclass in ['AGIPD', 'LPD']:\n",
    "            continue\n",
    "\n",
    "        if key == 'NBP':\n",
    "            unit = '[%]'\n",
    "            title = 'BadPixelsDark'\n",
    "        else:\n",
    "            unit = '[ADU]'\n",
    "            title = const[0]\n",
    "\n",
    "        title += ', module {}'.format(mod)\n",
    "        if keys[key][1] != '':\n",
    "            title += ', {}'.format(keys[key][1])\n",
    "        if gain is not None:\n",
    "            title += ', {}'.format(gain_titles[gain])\n",
    "\n",
    "        fname = '{}/{}_{}'.format(out_folder, const[0], 'all')\n",
    "        for item in const[1:]:\n",
    "            fname = '{}_{}'.format(fname, item)\n",
    "        fname = '{}_ASIC_{}.png'.format(fname, key)\n",
    "\n",
    "        fig = plt.figure(figsize=(12,12) )\n",
    "        for i in range(len(mod_data[key])):\n",
    "            plt.scatter(mod_times[i], mod_data[key][i], label=mod_names[i])\n",
    "        plt.grid()\n",
    "        plt.xlabel('Creation Time')\n",
    "        plt.ylabel('{}, {} {}'.format(const[0], keys[key][2], unit))  \n",
    "        plt.legend(loc='best guess')\n",
    "        plt.title(title)\n",
    "        fig.savefig(fname)"
   ]
  }
 ],
 "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.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}