{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "from pathlib import Path\n",
    "import numpy as np\n",
    "\n",
    "dataDir = Path('/scratch/guenther/MCParticleData')\n",
    "file = 'bsphiphi500.h5'\n",
    "zReference = 8520.\n",
    "zMagnetParams = [5212.38, 406.609, -1102.35, -498.039]\n",
    "xParams = [18.6195, -5.55793]\n",
    "xLayers = [0,3,4,7,8,11]\n",
    "uvLayers = [1,2,5,6,9,10]\n",
    "\n",
    "with pd.HDFStore(dataDir/file, mode='r') as eventData:\n",
    "    tmp = []\n",
    "    # event loop\n",
    "    for iEvent in range(0, 500):\n",
    "        hits = eventData[str(iEvent)]\n",
    "        tmp.append(hits.copy())\n",
    "    allHits = pd.concat(tmp, ignore_index=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Hough Histogram Studies\n",
    "## Occupancy of the Detector\n",
    "* data contains all x hits of 500 events in the range [-3000,3000]mm\n",
    "* the first plot shows the coarse distribution of these hits along x\n",
    "* the occupancy is higher in the central region than at the edges as expected\n",
    "\n",
    "## Hough Histogram Binning\n",
    "* the idea is to adjust the histogram's bin width along x such that every bin contains the same number of hits on average\n",
    "* two approaches have been tested, one of which is shown below\n",
    "1. in a **first attempt**, I fitted the (right) flank of the occupancy distribution with a 5th order polynomial to describe the occupancy for most of the range\n",
    "    - to find regions of equal content, i.e. the desired bins, the culumative distribution must be calculated from which the quantiles (i.e. 500 quantiles for 500 bins) can (in theory) be calculated by $x_{Q}=F^{-1}(p)$ where $p$ is the \"probability\" given by $i_{bin}/N_{Bins}$\n",
    "    - for polynomials of order higher than 3 this is, however, not analytically doable\n",
    "    - other functions might be possible, but since there is an easier approach, I didn't look further into this\n",
    "2. the **second approach** numerically calculates the quantiles for the full occupancy distribution using `pandas.qcut` function\n",
    "    - this function directly gives the bin edges containing an equal amount of x hits\n",
    "    - the bin edges are then enumerated and this enumeration is plotted against the edges, which is shown for different number of bins below\n",
    "    - the resulting distribution is fitted with $f(x)=p_0+\\frac{p_1\\cdot x}{1+|p_2\\cdot x|}$, which is chosen because it's computionally faster than e.g. a Sigmoid\n",
    "    - the fitted function gives a direct mapping $x\\to i_{Bin}$ when downcasted to integer values\n",
    "    - the resulting minimal and maximal width of the bins is shown in the table and the last plot below for different total numbers of bins\n",
    "    - 1000 bins have minimal bin width of 1.625mm which is of the order of the Hough Projection's resolution for high momentum tracks\n",
    "    - the parameters for this scenario are $p_0=505.675291$, $p_1=0.453316164$ and $p_2=5.57027462\\times 10^{-4}$\n",
    "    - to avoid negative bin numbers arising from the imperfection of the parametrisation, the parameter $p_0$ can be shifted by +2.5 for 1000 bins\n",
    "    - also because of the imperfection of the parametrisation, the real number of bins later on will be slightly higher, which is also shown in the table\n",
    "    - the inverse. i.e. the mapping $i_{bin}\\to x$ is $x=\\frac{p_0-i_{Bin}}{p_0\\cdot p_2-p_1-p_2\\cdot i_{Bin}}$ for $i<p_0$ and $x=\\frac{p_0+i_{Bin}}{p_0\\cdot p_2+p_1-p_2\\cdot i_{Bin}}$ else"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "BINS=600\n",
      "shift=1.1\n",
      "[ 3.03414161e+02  2.71945159e-01 -5.56801117e-04]\n",
      "at -3000 = 0\n",
      "at 3000 = 610\n",
      "at -3000 = -0.9960452190164233\n",
      "at 3000 = 610.0243669660781\n",
      "\n",
      "BINS=700\n",
      "shift=1.5\n",
      "[ 3.53980141e+02  3.17291325e-01 -5.56893995e-04]\n",
      "at -3000 = 0\n",
      "at 3000 = 711\n",
      "at -3000 = -0.9359283090630584\n",
      "at 3000 = 711.8962100461829\n",
      "\n",
      "BINS=800\n",
      "shift=1.8\n",
      "[ 4.04541076e+02  3.62631197e-01 -5.56941658e-04]\n",
      "at -3000 = 0\n",
      "at 3000 = 813\n",
      "at -3000 = -0.9838529169481944\n",
      "at 3000 = 813.6660040895719\n",
      "\n",
      "BINS=900\n",
      "shift=2.2\n",
      "[4.55109246e+02 4.07977502e-01 5.56998216e-04]\n",
      "at -3000 = 0\n",
      "at 3000 = 915\n",
      "at -3000 = -0.9217380096175134\n",
      "at 3000 = 915.5402300772885\n",
      "\n",
      "BINS=1000\n",
      "shift=5.5\n",
      "[5.05675291e+02 4.53316164e-01 5.57027462e-04]\n",
      "at -3000 = 2\n",
      "at 3000 = 1020\n",
      "at -3000 = 2.037684447748063\n",
      "at 3000 = 1020.3128978501425\n",
      "\n",
      "BINS=1100\n",
      "shift=4.9\n",
      "[5.56237804e+02 4.98644231e-01 5.57020439e-04]\n",
      "at -3000 = 1\n",
      "at 3000 = 1121\n",
      "at -3000 = 1.0860058517370135\n",
      "at 3000 = 1121.1896027202529\n",
      "\n",
      "BINS=1200\n",
      "shift=3.2\n",
      "[6.06805431e+02 5.43977948e-01 5.57024839e-04]\n",
      "at -3000 = 0\n",
      "at 3000 = 1220\n",
      "at -3000 = -0.95986992994915\n",
      "at 3000 = 1220.9707309946493\n",
      "\n",
      "BINS=1300\n",
      "shift=3.6\n",
      "[ 6.57370222e+02  5.89309937e-01 -5.57035442e-04]\n",
      "at -3000 = 0\n",
      "at 3000 = 1322\n",
      "at -3000 = -0.9015253398330287\n",
      "at 3000 = 1322.841969580662\n",
      "\n",
      "BINS=1400\n",
      "shift=3.9\n",
      "[7.07935490e+02 6.34651601e-01 5.57051233e-04]\n",
      "at -3000 = 0\n",
      "at 3000 = 1424\n",
      "at -3000 = -0.9482046457474098\n",
      "at 3000 = 1424.6191845963053\n",
      "\n",
      "BINS=1500\n",
      "shift=4.2\n",
      "[ 7.58500798e+02  6.80003618e-01 -5.57081680e-04]\n",
      "at -3000 = 0\n",
      "at 3000 = 1526\n",
      "at -3000 = -0.992094855437017\n",
      "at 3000 = 1526.3936918183863\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>NBINS</th>\n",
       "      <th>nBinsReal</th>\n",
       "      <th>minBinWidth</th>\n",
       "      <th>maxBinWidth</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>600</td>\n",
       "      <td>610</td>\n",
       "      <td>2.750</td>\n",
       "      <td>72.399902</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>700</td>\n",
       "      <td>712</td>\n",
       "      <td>2.375</td>\n",
       "      <td>61.944386</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>800</td>\n",
       "      <td>813</td>\n",
       "      <td>2.000</td>\n",
       "      <td>54.544922</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>900</td>\n",
       "      <td>916</td>\n",
       "      <td>1.750</td>\n",
       "      <td>49.346866</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1000</td>\n",
       "      <td>1017</td>\n",
       "      <td>1.625</td>\n",
       "      <td>44.815297</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>1100</td>\n",
       "      <td>1119</td>\n",
       "      <td>1.375</td>\n",
       "      <td>40.286656</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>1200</td>\n",
       "      <td>1221</td>\n",
       "      <td>1.375</td>\n",
       "      <td>36.824951</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>1300</td>\n",
       "      <td>1323</td>\n",
       "      <td>1.250</td>\n",
       "      <td>34.324951</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>1400</td>\n",
       "      <td>1424</td>\n",
       "      <td>1.125</td>\n",
       "      <td>31.924148</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>1500</td>\n",
       "      <td>1527</td>\n",
       "      <td>1.000</td>\n",
       "      <td>30.625244</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   NBINS  nBinsReal  minBinWidth  maxBinWidth\n",
       "0    600        610        2.750    72.399902\n",
       "1    700        712        2.375    61.944386\n",
       "2    800        813        2.000    54.544922\n",
       "3    900        916        1.750    49.346866\n",
       "4   1000       1017        1.625    44.815297\n",
       "5   1100       1119        1.375    40.286656\n",
       "6   1200       1221        1.375    36.824951\n",
       "7   1300       1323        1.250    34.324951\n",
       "8   1400       1424        1.125    31.924148\n",
       "9   1500       1527        1.000    30.625244"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 31920 characters]",
      "text/plain": [
       "<Figure size 864x720 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 160696 characters]",
      "text/plain": [
       "<Figure size 1728x720 with 10 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 12972 characters]",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from scipy.optimize import curve_fit\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "\n",
    "data = allHits.loc[(allHits['pc'].isin(xLayers)) & (allHits['x']<=3200) & (allHits['x']>=-3200)]['x']\n",
    "\n",
    "urax = data.plot.hist(bins=25, title='Integrated SciFi Hit Distribution (X Layers)', figsize=(12,10), fontsize=20, edgecolor='black')\n",
    "urax.set_xlabel('x [mm]', fontsize=24)\n",
    "urax.set_ylabel('Count', fontsize=24)\n",
    "urax.title.set_size(24)\n",
    "#out_0,equal_bins_0 = pd.qcut(data, q=25, retbins=True)\n",
    "#urax0 = data.plot.hist(bins=equal_bins_0, title='Integrated SciFi Hit Distribution (X Layers)', figsize=(12,10), fontsize=20, edgecolor='black')\n",
    "#urax0.set_xlabel('x [mm]', fontsize=24)\n",
    "#urax0.set_ylabel('Count', fontsize=24)\n",
    "#urax0.title.set_size(24)\n",
    "def fit3(x, p0, p1, p2, p3, p4, p5, p6, p7):\n",
    "    return p0 + p1 * x + p2 * x**2 + p3 * x**3 + p4 * x**4 + p5 * x**5 + p6 * x**6 + p7 * x**7\n",
    "\n",
    "def fitFastSigmoid(x, p0, p1, p2 ):\n",
    "    return p0 + p1 * x / (1 + abs(x * p2) ) \n",
    "\n",
    "def FastSigmoidInverse(y, p0, p1, p2):\n",
    "    return (y - p0) / (p0 * p2 + p1 - p2 * y)#(p0 - y) / (p0 * p2 - p1 - p2 * y)\n",
    "\n",
    "NBINS=[600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500]\n",
    "SHIFTS = [1.1, 1.5, 1.8, 2.2, 5.5, 4.9, 3.2, 3.6, 3.9, 4.2]\n",
    "minBinWidths = []\n",
    "maxBinWidths = []\n",
    "nBinsReal = []\n",
    "fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(24, 10))\n",
    "for i, BINS in enumerate(NBINS):\n",
    "    out,equal_bins = pd.qcut(data, q=BINS, retbins=True)\n",
    "    binWidths = np.array([ abs(equal_bins[i] - equal_bins[i+1]) for i in range(len(equal_bins)-1)])\n",
    "    minBinWidths.append( binWidths.min() )\n",
    "    maxBinWidths.append( binWidths.max() )\n",
    "    #equal_bincenters = np.array([0.5 * ( equal_bins[i] + equal_bins[i+1] ) for i in range(len(equal_bins)-1)] ) \n",
    "    popt, pcov = curve_fit( fitFastSigmoid, xdata=equal_bins[:-1], ydata=np.arange(0, BINS) )\n",
    "    if i<5:\n",
    "        axes[0,i].plot(equal_bins[:-1], np.arange(0, BINS), '.', label='data')\n",
    "        axes[0,i].plot(equal_bins[:-1], fitFastSigmoid(equal_bins[:-1], *popt), label=f'p0={popt[0]}\\np1={popt[1]}\\np2={popt[2]}\\n')\n",
    "        axes[0,i].legend()\n",
    "    else:\n",
    "        axes[1,i-5].plot(equal_bins[:-1], np.arange(0, BINS), '.', label='data')\n",
    "        axes[1,i-5].plot(equal_bins[:-1], fitFastSigmoid(equal_bins[:-1], *popt), label=f'p0={popt[0]}\\np1={popt[1]}\\np2={popt[2]}\\n')\n",
    "        axes[1,i-5].legend()\n",
    "    nBinsReal.append(abs(int(fitFastSigmoid(-3000, popt[0], popt[1], popt[2])) - int(fitFastSigmoid(3000, popt[0], popt[1], popt[2]))))\n",
    "    ###############33\n",
    "    # SHIFT A BIT UPWARDS TO AVOID NEGATIVE INDICES p0+2.5\n",
    "    ################################3#\n",
    "    shift = SHIFTS[i]\n",
    "    print(f'BINS={BINS}')\n",
    "    print(f'shift={shift}')\n",
    "    print(popt)\n",
    "    print(f'at -3000 = {int(fitFastSigmoid(-3000., popt[0]+shift, popt[1], popt[2]))}')\n",
    "    print(f'at 3000 = {int(fitFastSigmoid(3000., popt[0]+shift, popt[1], popt[2]))}')\n",
    "    print(f'at -3000 = {fitFastSigmoid(-3000., popt[0]+shift, popt[1], popt[2])}')\n",
    "    print(f'at 3000 = {fitFastSigmoid(3000., popt[0]+shift, popt[1], popt[2])}')\n",
    "    #print(f'inv at 0 = {FastSigmoidInverse(506, popt[0]+shift, popt[1], popt[2])}')\n",
    "    #print(f'inv at 1016 = {FastSigmoidInverse(1016, popt[0]+shift, popt[1], popt[2])}')\n",
    "    print()\n",
    "binData = pd.DataFrame({'NBINS':NBINS, 'nBinsReal': nBinsReal,'minBinWidth':minBinWidths, 'maxBinWidth':maxBinWidths})\n",
    "ax = binData.plot(x='NBINS', y=['minBinWidth', 'maxBinWidth'], logy=True)\n",
    "binData\n",
    "#out,equal_bins = pd.qcut(data, q=1000, retbins=True)\n",
    "#fig, axis = plt.subplots(figsize=(12,10))\n",
    "#axis.set_title('Mapping Bin Number to x Position', fontsize=24)\n",
    "#axis.plot(equal_bins[:-1], np.arange(0,1000), '.' )\n",
    "#axis.set_xlabel('x [mm]', fontsize=24)\n",
    "#axis.set_ylabel('Bin Number', fontsize=24)\n",
    "#axis.tick_params(axis='both', which='major', labelsize=20)\n",
    "#axis.tick_params(axis='both', which='minor', labelsize=16)\n",
    "#popt, pcov = curve_fit( fitFastSigmoid, xdata=equal_bins[:-1], ydata=np.arange(0, 1000) )\n",
    "#axis.plot(equal_bins[:-1], fitFastSigmoid(equal_bins[:-1], *popt), label=f'p0={popt[0]}\\np1={popt[1]}\\np2={popt[2]}\\n', color='red', markersize=24)\n",
    "#axis.legend(fontsize=20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.10.6 (conda)",
   "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.10.6"
  },
  "vscode": {
   "interpreter": {
    "hash": "a2eff8b4da8b8eebf5ee2e5f811f31a557e0a202b4d2f04f849b065340a6eda6"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
