diff --git a/notebooks/Jungfrau/create_gain_map.ipynb b/notebooks/Jungfrau/create_gain_map.ipynb
index 4979af6d12af715c47e33c3a35d467618ad76270..5583c6608f16c313a0b52427dd018389fbceb890 100755
--- a/notebooks/Jungfrau/create_gain_map.ipynb
+++ b/notebooks/Jungfrau/create_gain_map.ipynb
@@ -25,14 +25,14 @@
    "outputs": [],
    "source": [
     "in_folder = '/gpfs/exfel/exp/MID/202330/p900322/raw'\n",
-    "fit_path = '/gpfs/exfel/data/scratch/mramilli/DATA/MID/p900322/gain_fit' # fit files directory\n",
+    "# fit_path = '/gpfs/exfel/data/scratch/mramilli/DATA/MID/p900322/gain_fit' # fit files directory\n",
     "metadata_folder = ''  # Directory containing calibration_metadata.yml when run by xfel-calibrate\n",
-    "g0_run = 94  # Number of high gain\n",
+    "g0_runs = [94]  # Number of high gain\n",
     "g_map_old_dir = '/gpfs/exfel/data/user/mramilli/jungfrau/module_PSI_gainmaps/M302'\n",
     "gain_map_file = '/gainMaps_M109_Burst_Fix_20230523.h5'  # path\n",
     "out_folder = \"/gpfs/exfel/data/scratch/ahmedk/test/jf_ff/gain_maps\"\n",
     "karabo_id = 'MID_EXP_JF500K2'  # karabo prefix of Jungfrau devices\n",
-    "da_name = 'JNGFR02'\n",
+    "karabo_da = ['JNGFR02']\n",
     "_fit_func = 'CHARGE_SHARING'  # function used to fit the single photon peak\n",
     "gains = [0, 1, 2]\n",
     "# Parameter conditions\n",
@@ -40,8 +40,7 @@
     "integration_time = -1  # the detector acquisition rate, use 0 to try to auto-determine\n",
     "gain_setting = -1\n",
     "gain_mode = -1\n",
-    "memory_cells = 16\n",
-    "mode = 'Burst'\n",
+    "memory_cells = -1\n",
     "adc_fit = True\n",
     "is_strixel = False\n",
     "# Condition limits\n",
@@ -55,11 +54,11 @@
     "db_output = False\n",
     "send_bpix = False\n",
     "gain_map_name = 'gain_map_g0'\n",
-    "\n",
     "g0_name_new = 'gainMap_fit'  # name of the data structure in the fit files\n",
     "E_ph = 8.048  # photon energy of the peak fitted\n",
     "badpixel_threshold_sigma = 3.  # number of std in gain distribution to mark bad pixels\n",
     "_roi = [0, 1024, 0, 256]  # ROI to consider to evaluate gain distribution (for bad pixels evaluation)\n",
+    "control_src_template = '{}/DET/CONTROL'\n",
     "\n",
     "# CALCAT API parameters\n",
     "cal_db_interface = \"tcp://max-exfl-cal-001:8020\"  # the database interface to use\n",
@@ -93,6 +92,8 @@
     "from pathlib import Path\n",
     "from XFELDetAna.plotting.histogram import histPlot\n",
     "from XFELDetAna.plotting.heatmap import heatmapPlot\n",
+    "from cal_tools.jungfraulib import JungfrauCtrl\n",
+    "from extra_data import RunDirectory\n",
     "import matplotlib.pyplot as plt\n",
     "%matplotlib inline\n"
    ]
@@ -110,14 +111,35 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "if memory_cells > 1:\n",
-    "    mode = 'Burst'\n",
-    "\n",
-    "fit_path = Path(fit_path)\n",
-    "\n",
+    "g0_run = g0_runs[0]\n",
+    "fit_path = Path(out_folder)  # TODO\n",
+    "da_name = karabo_da[0]\n",
     "proposal = list(filter(None, in_folder.strip('/').split('/')))[-2]\n",
     "file_loc = f\"proposal:{proposal} runs:{g0_run}\"\n",
-    "report = get_report(metadata_folder)"
+    "report = get_report(metadata_folder)\n",
+    "in_folder = Path(in_folder)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "run_folder = in_folder / f'r{g0_run:04d}'\n",
+    "control_src = control_src_template.format(karabo_id, karabo_da[0])\n",
+    "\n",
+    "ctrl_data = JungfrauCtrl(RunDirectory(run_folder), control_src)\n",
+    "\n",
+    "if memory_cells < 0:\n",
+    "    memory_cells, _ = ctrl_data.get_memory_cells()\n",
+    "\n",
+    "print(f'Memory  cells: {memory_cells}')\n",
+    "\n",
+    "if memory_cells > 1:\n",
+    "    mode = 'Burst'\n",
+    "else:\n",
+    "    mode = 'Single'"
    ]
   },
   {
@@ -135,7 +157,8 @@
     "    if is_strixel:\n",
     "        g0_new = np.transpose(from_strixel(np.transpose(g0_new)))\n",
     "\n",
-    "    print(g0_new.shape)\n",
+    "    print(\"Fit gain data shape:\", g0_new.shape)\n",
+    "\n",
     "    if integration_time < 0  and 'integration_time' in f.attrs.keys():\n",
     "        integration_time = np.float32(f.attrs['integration_time'])\n",
     "        print(f'Integration time: {integration_time}')\n",
@@ -147,7 +170,7 @@
     "        print(f'bias voltage: {bias_voltage}')\n",
     "    else:\n",
     "        print(f'bias voltage not found!\\nUsing default value {bias_voltage}')\n",
-    "        \n",
+    "\n",
     "    # if 'karabo_id' in f.attrs.keys():\n",
     "    #     karabo_id = str(f.attrs['karabo_id'])\n",
     "    #     print(f'Karabo ID: {karabo_id}')\n",
diff --git a/notebooks/Jungfrau/gainCal_JF_Create_Spectra_Histos.ipynb b/notebooks/Jungfrau/gainCal_JF_Create_Spectra_Histos.ipynb
index e9076a528c6c55d0f7b1ac91fb0025ed0bf1eccf..4dbe7b8aa4867430272e543529b48c4f0622fdf4 100644
--- a/notebooks/Jungfrau/gainCal_JF_Create_Spectra_Histos.ipynb
+++ b/notebooks/Jungfrau/gainCal_JF_Create_Spectra_Histos.ipynb
@@ -19,30 +19,56 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "instrument = 'HED'\n",
-    "cycle = 202231\n",
-    "proposal = 900297\n",
-    "in_folder = f'/gpfs/exfel/exp/{instrument}/{cycle}/p{proposal:06d}/raw'\n",
-    "fit_out_folder = f'/gpfs/exfel/data/scratch/ahmedk/test/FFDATA/{instrument}/p{proposal:06d}/gain_fit'\n",
+    "in_folder = '/gpfs/exfel/exp/HED/202231/p900297/raw'\n",
+    "out_folder = '/gpfs/exfel/data/scratch/ahmedk/test/FFDATA/HED/p900297/gain_fit'\n",
+    "metadata_folder = ''  # Directory containing calibration_metadata.yml when run by xfel-calibrate\n",
     "g0_runs = [26, ]  # can be a list of runs\n",
     "sensor_size = [512, 1024]  # size of the array in the 'row' and 'col' dimensions\n",
     "chunked_trains = 1000  # Number of trains per chunk.\n",
     "gains = [0, 1, 2]  # gain bit values\n",
-    "mod_n = 1  # module number\n",
     "karabo_id = 'HED_IA1_JF500K1'  # karabo prefix of Jungfrau devices\n",
-    "da_name = f'JNGFR{mod_n:02d}'\n",
+    "karabo_da = [\"JNGFR01\"]\n",
+    "da_template = 'JNGFR{:02d}'\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",
-    "bias_voltage = 180.  # bias voltage - Set to -1 to derive from CONTROL file.\n",
-    "integration_time = 10.  # integration time - Set to -1 to derive from CONTROL file.\n",
-    "gain_mode = 0  # number of memory cells - Set to -1 to derive from CONTROL file.\n",
-    "memory_cells = 1  # number of memory cells - Set to -1 to derive from CONTROL file.\n",
-    "sc_start = 15  # storage cell start value - should be derived from CONTROL file\n",
+    "bias_voltage = -1  # bias voltage - Set to -1 to derive from CONTROL file.\n",
+    "integration_time = -1  # integration time - Set to -1 to derive from CONTROL file.\n",
+    "gain_mode = -1  # number of memory cells - Set to -1 to derive from CONTROL file.\n",
+    "gain_setting = -1\n",
+    "\n",
+    "memory_cells = -1  # number of memory cells - Set to -1 to derive from CONTROL file.\n",
+    "sc_start = -1  # storage cell start value - should be derived from CONTROL file\n",
     "h_bins_s = 0.2  # bin width in ADC units of the histogram\n",
     "h_range = (-5., 10.)  # histogram range in ADC units\n",
     "block_size = [128, 64]  # dimension of the chunks in 'row' and 'col'\n",
-    "det_src = f'{karabo_id}/DET/{da_name}:daqOutput'\n",
-    "control_src = f'{karabo_id}/DET/CONTROL'\n",
-    "extra_dims = ['cell', 'row', 'col']  # labels for the DataArray dims after the first"
+    "det_src_template = '{}/DET/{}:daqOutput'\n",
+    "control_src_template = '{}/DET/CONTROL'\n",
+    "extra_dims = ['cell', 'row', 'col']  # labels for the DataArray dims after the first\n",
+    "fit_path = '/gpfs/exfel/data/scratch/mramilli/DATA/MID/p900322/gain_fit' # fit files directory\n",
+    "gain_map_file = '/gainMaps_M109_Burst_Fix_20230523.h5'  # path\n",
+    "_fit_func = 'CHARGE_SHARING'  # function used to fit the single photon peak\n",
+    "\n",
+    "mode = 'Burst'  # TODO: CONFIRM MY DECISION TO REMOVE MODE DECLARATION FROM ALL NOTEBOOKS.\n",
+    "adc_fit = True\n",
+    "is_strixel = False\n",
+    "# Condition limits\n",
+    "bias_voltage_lims = [0, 200]\n",
+    "integration_time_lims = [0.1, 1000]\n",
+    "\n",
+    "g_map_old = ''  # '/gpfs/exfel/data/user/mramilli/jungfrau/module_PSI_gainmaps/M302/gainMaps_M302_2022-02-23.h5'  # old gain map file path to calculate gain ratios G0/G1 and G1/G2. Set to \"\" to get last gain constant from the database.\n",
+    "old_gain_dataset_name = 'gain_map_g0'  # name of the data structure in the old gain map\n",
+    "correct_offset = True  # correct the photon peak value with a pedestal fit position\n",
+    "db_output = False\n",
+    "send_bpix = False\n",
+    "gain_map_name = 'gain_map_g0'\n",
+    "\n",
+    "g0_name_new = 'gainMap_fit'  # name of the data structure in the fit files\n",
+    "E_ph = 8.048  # photon energy of the peak fitted\n",
+    "badpixel_threshold_sigma = 3.  # number of std in gain distribution to mark bad pixels\n",
+    "_roi = [0, 1024, 0, 256]  # ROI to consider to evaluate gain distribution (for bad pixels evaluation)\n",
+    "\n",
+    "# CALCAT API parameters\n",
+    "cal_db_interface = \"tcp://max-exfl-cal-001:8020\"  # the database interface to use\n",
+    "cal_db_timeout = 180000"
    ]
   },
   {
@@ -67,7 +93,20 @@
     "from cal_tools.jungfrau import jungfrau_ff\n",
     "from cal_tools.jungfraulib import JungfrauCtrl\n",
     "from cal_tools.tools import calcat_creation_time\n",
-    "from cal_tools.step_timing import StepTimer"
+    "from cal_tools.step_timing import StepTimer\n",
+    "from pathlib import Path"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "in_folder = Path(in_folder)\n",
+    "out_folder = Path(out_folder)\n",
+    "out_folder.mkdir(exist_ok=True, parents=True)\n",
+    "print(f'Creating directory {out_folder}')"
    ]
   },
   {
@@ -76,9 +115,9 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "if not os.path.exists(fit_out_folder):\n",
-    "    os.makedirs(fit_out_folder)\n",
-    "    print('Creating directory {}'.format(fit_out_folder))"
+    "det_src = det_src_template.format(karabo_id, karabo_da[0])\n",
+    "control_src = control_src_template.format(karabo_id, karabo_da[0])\n",
+    "da_name = karabo_da[0]  # TODO fix this"
    ]
   },
   {
@@ -89,8 +128,8 @@
    "source": [
     "begin_stuff = time.localtime()\n",
     "\n",
-    "from pathlib import Path\n",
-    "first_run_folder = Path(in_folder) / f'r{g0_runs[0]:04d}'\n",
+    "\n",
+    "first_run_folder = in_folder / f'r{g0_runs[0]:04d}'\n",
     "ctrl_data = JungfrauCtrl(RunDirectory(first_run_folder), control_src)\n",
     "\n",
     "if memory_cells < 0 and sc_start < 0:\n",
@@ -121,7 +160,7 @@
     "h_n_bins = int((h_range[1] - h_range[0])/h_bins_s)\n",
     "h_bins = np.linspace(h_range[0], h_range[1], h_n_bins)\n",
     "\n",
-    "h_spectra = np.zeros((h_n_bins-1, memory_cells, sensor_size[0], sensor_size[1]), dtype=np.int)\n",
+    "h_spectra = np.zeros((h_n_bins-1, memory_cells, sensor_size[0], sensor_size[1]), dtype=np.int32)\n",
     "edges = None"
    ]
   },
@@ -131,93 +170,94 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "def read_conditions_and_retrieve_constants(\n",
-    "    run_dc,\n",
-    "    run_number,\n",
-    "    integration_time,\n",
-    "    bias_voltage,\n",
-    "    gain_mode,\n",
-    "    creation_time,\n",
-    "):\n",
-    "    \"\"\"Retrieve offset and noise calibration constants\n",
-    "    for one jungfrau module.\n",
-    "    Args:\n",
-    "        run_dc(extra_data.DataCollection):\n",
-    "        run_number(int):\n",
-    "        integration_time(float)\n",
-    "        bias_voltage(float):\n",
-    "        gain_mode(int):\n",
-    "        creation_time():\n",
-    "    Returns:\n",
-    "        ndarray, ndarray: Offset and Noise calibration constants.\n",
-    "    \"\"\"\n",
-    "    \n",
-    "    ## Retrieve DB constants\n",
-    "    ctrl_data = JungfrauCtrl(run_dc, control_src)\n",
-    "    if integration_time < 0:\n",
-    "        integration_time = ctrl_data.get_integration_time()\n",
-    "    if bias_voltage < 0:\n",
-    "        bias_voltage = ctrl_data.get_bias_voltage()\n",
-    "    # gain_setting = ctrl_data.get_gain_setting() TODO: Where is the gain_setting?\n",
-    "    if gain_mode < 0:\n",
-    "        gain_mode = ctrl_data.get_gain_mode()\n",
-    "\n",
-    "    # TODO: clarify this way in getting the control data.\n",
-    "    # We are using RUN. We can of course update and start using `as_single_value` method.\n",
-    "    print(f\"Memory cells: {memory_cells:d}\")\n",
-    "    print(f\"Exposure Time: {integration_time:1.2f} us\")\n",
-    "    print(f\"Bias Voltage: {bias_voltage:3.1f} V\")\n",
-    "\n",
-    "    # Run's creation time:\n",
-    "    creation_time = calcat_creation_time(in_folder, run_number, creation_time)\n",
-    "    print(f\"Creation time: {creation_time}\")\n",
-    "\n",
-    "    jf_cal = JUNGFRAU_CalibrationData(\n",
-    "        detector_name=karabo_id,\n",
-    "        sensor_bias_voltage=bias_voltage,\n",
-    "        event_at=creation_time,\n",
-    "        modules=da_name,\n",
-    "        memory_cells=memory_cells,\n",
-    "        integration_time=integration_time,\n",
-    "        # No gain setting 1? I use 0 for now as I don't see gain_setting used anywhere for the 3 notebooks.\n",
-    "        gain_setting=0,\n",
-    "        gain_mode=gain_mode,\n",
-    "        client=rest_cfg.calibration_client(),\n",
+    "# def read_conditions_and_retrieve_constants(\n",
+    "#     run_dc,\n",
+    "#     run_number,\n",
+    "#     integration_time,\n",
+    "#     bias_voltage,\n",
+    "#     gain_mode,\n",
+    "#     creation_time,\n",
+    "# ):\n",
+    "#     \"\"\"Retrieve offset and noise calibration constants\n",
+    "#     for one jungfrau module.\n",
+    "#     Args:\n",
+    "#         run_dc(extra_data.DataCollection):\n",
+    "#         run_number(int):\n",
+    "#         integration_time(float)\n",
+    "#         bias_voltage(float):\n",
+    "#         gain_mode(int):\n",
+    "#         creation_time():\n",
+    "#     Returns:\n",
+    "#         ndarray, ndarray: Offset and Noise calibration constants.\n",
+    "#     \"\"\"\n",
+    "run_folder = in_folder / f'r{g0_runs[0]:04d}' # first run for now\n",
+    "run_number = g0_runs[0]\n",
+    "## Retrieve DB constants\n",
+    "ctrl_data = JungfrauCtrl(RunDirectory(run_folder), control_src)\n",
+    "if integration_time < 0:\n",
+    "    integration_time = ctrl_data.get_integration_time()\n",
+    "if bias_voltage < 0:\n",
+    "    bias_voltage = ctrl_data.get_bias_voltage()\n",
+    "# gain_setting = ctrl_data.get_gain_setting() TODO: Where is the gain_setting?\n",
+    "if gain_mode < 0:\n",
+    "    gain_mode = ctrl_data.get_gain_mode()\n",
+    "\n",
+    "# TODO: clarify this way in getting the control data.\n",
+    "# We are using RUN. We can of course update and start using `as_single_value` method.\n",
+    "print(f\"Memory cells: {memory_cells:d}\")\n",
+    "print(f\"Exposure Time: {integration_time:1.2f} us\")\n",
+    "print(f\"Bias Voltage: {bias_voltage:3.1f} V\")\n",
+    "\n",
+    "# Run's creation time:\n",
+    "creation_time = calcat_creation_time(in_folder, run_number, creation_time)\n",
+    "print(f\"Creation time: {creation_time}\")\n",
+    "\n",
+    "jf_cal = JUNGFRAU_CalibrationData(\n",
+    "    detector_name=karabo_id,\n",
+    "    sensor_bias_voltage=bias_voltage,\n",
+    "    event_at=creation_time,\n",
+    "    modules=karabo_da[0],\n",
+    "    memory_cells=memory_cells,\n",
+    "    integration_time=integration_time,\n",
+    "    # No gain setting 1? I use 0 for now as I don't see gain_setting used anywhere for the 3 notebooks.\n",
+    "    gain_setting=0,\n",
+    "    gain_mode=gain_mode,\n",
+    "    client=rest_cfg.calibration_client(),\n",
+    ")\n",
+    "jf_metadata = jf_cal.metadata(calibrations=[\"Offset10Hz\", \"Noise10Hz\"])\n",
+    "# TODO: display CCV timestamp\n",
+    "const_data = jf_cal.ndarray_map(metadata=jf_metadata)\n",
+    "\n",
+    "constants_data = const_data[karabo_da[0]]\n",
+    "\n",
+    "if \"Offset10Hz\" in constants_data:\n",
+    "    offset_map = np.array(\n",
+    "        np.transpose(np.swapaxes(constants_data[\"Offset10Hz\"], 0, 1)),\n",
+    "        dtype=np.float32\n",
     "    )\n",
-    "    jf_metadata = jf_cal.metadata(calibrations=[\"Offset10Hz\", \"Noise10Hz\"])\n",
-    "    # TODO: display CCV timestamp\n",
-    "    const_data = jf_cal.ndarray_map(metadata=jf_metadata)\n",
-    "\n",
-    "    constants_data = const_data[da_name]\n",
-    "\n",
-    "    if \"Offset10Hz\" in constants_data:\n",
-    "        offset = np.array(\n",
-    "            np.transpose(np.swapaxes(constants_data[\"Offset10Hz\"], 0, 1)),\n",
-    "            dtype=np.float32\n",
-    "        )\n",
-    "        print(f'Retrieved Offset Map {offset.shape}')\n",
-    "    else:\n",
-    "        # Create offset empty constant.\n",
-    "        offset = np.zeros((3, memory_cells, 512, 1024), dtype=np.float32)\n",
-    "        print(\"Offset map not found\")\n",
-    "\n",
-    "    if \"Noise10Hz\" in constants_data:\n",
-    "        noise = np.array(\n",
-    "            np.transpose(np.swapaxes(constants_data[\"Noise10Hz\"], 0, 1)),\n",
-    "            dtype=np.float32\n",
-    "        )\n",
-    "        print(f'Retrieved Noise Map {noise.shape}')\n",
-    "    else:\n",
-    "        noise = None\n",
-    "        print(\"Noise map not found\")\n",
-    "\n",
-    "    if memory_cells > 1:\n",
-    "        # move from x, y, cell, gain to cell, x, y, gain\n",
-    "        offset = np.moveaxis(offset, [0, 1], [1, 2])\n",
-    "    else:\n",
-    "        offset = np.squeeze(offset)\n",
-    "\n",
-    "    return offset, noise"
+    "    print(f'Retrieved Offset Map {offset_map.shape}')\n",
+    "else:\n",
+    "    # Create offset empty constant.\n",
+    "    offset_map = np.zeros((3, memory_cells, 512, 1024), dtype=np.float32)\n",
+    "    print(\"Offset map not found\")\n",
+    "\n",
+    "if \"Noise10Hz\" in constants_data:\n",
+    "    noise_map = np.array(\n",
+    "        np.transpose(np.swapaxes(constants_data[\"Noise10Hz\"], 0, 1)),\n",
+    "        dtype=np.float32\n",
+    "    )\n",
+    "    print(f'Retrieved Noise Map {noise_map.shape}')\n",
+    "else:\n",
+    "    noise_map = None\n",
+    "    print(\"Noise map not found\")\n",
+    "\n",
+    "if memory_cells > 1:\n",
+    "    # move from x, y, cell, gain to cell, x, y, gain\n",
+    "    offset_map = np.moveaxis(offset_map, [0, 1], [1, 2])\n",
+    "else:\n",
+    "    offset_map = np.squeeze(offset_map)\n",
+    "\n",
+    "    # return offset, noise"
    ]
   },
   {
@@ -299,22 +339,22 @@
     "    \n",
     "    raw_fname_tmp = f'/RAW-R{g0_run:04d}-{da_name}' + '-S{:05d}.h5'\n",
     "\n",
-    "    run_folder = in_folder + f'/r{g0_run:04d}'\n",
+    "    run_folder = in_folder / f'r{g0_run:04d}'\n",
     "\n",
-    "    run_dc = open_run(proposal=proposal, run=g0_run, data='raw')\n",
+    "    run_dc = RunDirectory(run_folder)\n",
     "\n",
     "    run_dc.info()\n",
     "\n",
     "    run_dc = run_dc.select(selection)\n",
     "\n",
-    "    offset_map, noise_map = read_conditions_and_retrieve_constants(\n",
-    "        run_dc=run_dc,\n",
-    "        run_number=g0_run,\n",
-    "        integration_time=integration_time,\n",
-    "        bias_voltage=bias_voltage,\n",
-    "        gain_mode=gain_mode,\n",
-    "        creation_time=creation_time,\n",
-    "        )\n",
+    "    # offset_map, noise_map = read_conditions_and_retrieve_constants(\n",
+    "    #     run_dc=run_dc,\n",
+    "    #     run_number=g0_run,\n",
+    "    #     integration_time=integration_time,\n",
+    "    #     bias_voltage=bias_voltage,\n",
+    "    #     gain_mode=gain_mode,\n",
+    "    #     creation_time=creation_time,\n",
+    "    #     )\n",
     "\n",
     "    ## Offset subtraction & Histogram filling\n",
     "    ### performs offset subtraction and fills the histogram\n",
@@ -390,7 +430,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "fout_h_path = f'{fit_out_folder}/{fout_temp}_Histo.h5'\n",
+    "fout_h_path = f'{out_folder}/{fout_temp}_Histo.h5'\n",
     "hists = np.transpose(h_spectra)  # this is to make it compatible with my other notebooks, can be removed\n",
     "with h5file(fout_h_path, 'w') as fout_h:    \n",
     "    print(f\"Saving histograms at {fout_h_path}.\")\n",
diff --git a/notebooks/Jungfrau/gainCal_JF_Fit_Spectra_Histos.ipynb b/notebooks/Jungfrau/gainCal_JF_Fit_Spectra_Histos.ipynb
index 367c0c6ac93a20d4b35b9333b7aa6a681a80ded6..a1299a58d9189e11576c35e973dcb7bbb61271e0 100644
--- a/notebooks/Jungfrau/gainCal_JF_Fit_Spectra_Histos.ipynb
+++ b/notebooks/Jungfrau/gainCal_JF_Fit_Spectra_Histos.ipynb
@@ -1,7 +1,6 @@
 {
  "cells": [
   {
-   "attachments": {},
    "cell_type": "markdown",
    "metadata": {},
    "source": [
@@ -18,33 +17,34 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "instrument = 'HED'\n",
-    "cycle = 202231\n",
-    "proposal = 900297\n",
-    "g0_runs = [26,]  # can be a list of runs\n",
-    "dir_date_iso = ''  ## string to save the date of the creation of the run \n",
-    "                  ## to be used when injecting constants (third separate nb)\n",
-    "filepath = f'/gpfs/exfel/exp/{instrument}/{cycle}/p{proposal:06d}/raw'\n",
+    "in_folder = \"/gpfs/exfel/exp/HED/202231/p900297/raw\"\n",
+    "out_folder = \"/gpfs/exfel/data/scratch/ahmedk/test/FFDATA/HED/p900297/gain_fit\"\n",
+    "runs = [26]  # can be a list of runs\n",
+    "dir_date_iso = \"\"  ## string to save the date of the creation of the run\n",
+    "\n",
+    "# to be used when injecting constants (third separate nb)\n",
     "sensor_size = [512, 1024]  # size of the array in the 'row' and 'col' dimensions\n",
     "chunk_size = 10  # n of trains per chunk\n",
     "block_size = [128, 64]  # dimension of the chunks in 'row' and 'col'\n",
     "mod_n = 1  # module number\n",
-    "karabo_id = 'HED_IA1_JF500K1'  # karabo prefix of Jungfrau devices\n",
-    "da_name = f'JNGFR{mod_n:02d}'\n",
-    "da_control = 'JNGFRCTRL00'\n",
+    "karabo_id = \"HED_IA1_JF500K1\"  # karabo prefix of Jungfrau devices\n",
+    "da_name = f\"JNGFR{mod_n:02d}\"\n",
+    "da_control = \"JNGFRCTRL00\"\n",
+    "ctrl_source_template = \"{}/DET/CONTROL\"  # template for control source name (filled with karabo_id_control)\n",
+    "karabo_id_control = \"\"  # if control is on a different ID, set to empty string if it is the same a karabo-id\n",
     "\n",
+    "bias_voltage = 180.0  # bias voltage - should be derived from CONTROL file\n",
+    "integration_time = 10.0  # integration time - should be derived from CONTROL file.\n",
     "memory_cells = 1  # number of memory cells - should be derived from CONTROL file\n",
-    "bias_voltage = 180.  # bias voltage - should be derived from CONTROL file\n",
-    "integration_time = 10.  # integration time - should be derived from CONTROL file\n",
     "sc_start = 15  # storage cell start value - should be derived from CONTROL file\n",
-    "_fit_func = 'CHARGE_SHARING' ## which function will be used to fit the histogram\n",
-    "_h_range = (200., 350.)  # range of the histogram in x-axis units\n",
-    "rebin = 1 \n",
+    "gain_mode = 0  # number of memory cells - Set to -1 to derive from CONTROL file.\n",
+    "\n",
+    "_fit_func = \"CHARGE_SHARING\"  ## which function will be used to fit the histogram\n",
+    "_h_range = (200.0, 350.0)  # range of the histogram in x-axis units\n",
+    "rebin = 1\n",
     "# parameters for the peak finder\n",
-    "n_sigma = 20.  # n of sigma abov pedestal threshold\n",
-    "ratio = 0.99  # ratio of the next peak amplitude in the peak_finder\n",
-    "mode = 'Single'\n",
-    "out_folder = f'/gpfs/exfel/data/scratch/mramilli/DATA/{instrument}/p{proposal:06d}/gain_fit'"
+    "n_sigma = 20.0  # n of sigma abov pedestal threshold\n",
+    "ratio = 0.99  # ratio of the next peak amplitude in the peak_finder"
    ]
   },
   {
@@ -53,16 +53,19 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "import numpy as np\n",
-    "from h5py import File as h5file\n",
-    "import logging\n",
+    "import multiprocessing\n",
     "import time\n",
-    "import os\n",
-    "\n",
-    "from pathlib import Path\n",
     "from functools import partial\n",
-    "import multiprocessing\n",
-    "import jungfrau_ff"
+    "from logging import warning\n",
+    "from pathlib import Path\n",
+    "\n",
+    "from cal_tools.jungfrau import jungfrau_ff\n",
+    "import numpy as np\n",
+    "from cal_tools.jungfraulib import JungfrauCtrl\n",
+    "from extra_data import RunDirectory\n",
+    "from h5py import File as h5file\n",
+    "import pasha as psh\n",
+    "import time"
    ]
   },
   {
@@ -72,9 +75,11 @@
    "outputs": [],
    "source": [
     "out_folder = Path(out_folder)\n",
-    "if not os.path.exists(out_folder):\n",
-    "    os.makedirs(out_folder)\n",
-    "    print('Creating directory {}'.format(out_folder))"
+    "out_folder.mkdir(parents=True, exist_ok=True)\n",
+    "in_folder = Path(in_folder)\n",
+    "\n",
+    "if karabo_id_control == \"\":\n",
+    "    karabo_id_control = karabo_id"
    ]
   },
   {
@@ -90,27 +95,55 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "h_spectra = None\n",
+    "edges = None\n",
+    "noise_map = None\n",
+    "\n",
+    "ctrl_src = ctrl_source_template.format(karabo_id_control)\n",
+    "run_dc = RunDirectory(in_folder / f\"r{runs[0]:04d}\")\n",
+    "\n",
+    "ctrl_data = JungfrauCtrl(run_dc, ctrl_src)\n",
+    "\n",
+    "if memory_cells < 0:\n",
+    "    memory_cells, sc_start = ctrl_data.get_memory_cells()\n",
+    "\n",
+    "    mem_cells_name = \"single cell\" if memory_cells == 1 else \"burst\"\n",
+    "    print(f\"Run is in {mem_cells_name} mode.\\nStorage cell start: {sc_start:02d}\")\n",
+    "else:\n",
+    "    mem_cells_name = \"single cell\" if memory_cells == 1 else \"burst\"\n",
+    "    print(\n",
+    "        f\"Run is in manually set to {mem_cells_name} mode. With {memory_cells} memory cells\"\n",
+    "    )\n",
+    "\n",
+    "mode = \"Single\"\n",
     "if memory_cells > 1:\n",
-    "    mode = 'Burst'\n",
-    "file_h_name = f'/R{g0_runs[0]:04d}_Gain_{mode}_Spectra_{da_name}_Histo.h5' # histogram file name\n",
+    "    mode = \"Burst\"\n",
+    "\n",
+    "file_h_name = (\n",
+    "    f\"R{runs[0]:04d}_Gain_{mode}_Spectra_{da_name}_Histo.h5\"  # histogram file name\n",
+    ")\n",
     "\n",
     "begin_stuff = time.localtime()\n",
-    "i_cut = file_h_name.find('_Histo')\n",
+    "i_cut = file_h_name.find(\"_Histo\")\n",
     "fout_temp = file_h_name[:i_cut]\n",
+    "fout_temp += f\"_{_fit_func}_Fit\"\n",
+    "\n",
+    "print(f\"block_size: {block_size}\")\n",
+    "\n",
+    "if integration_time < 0:\n",
+    "    integration_time = ctrl_data.get_integration_time()\n",
+    "if bias_voltage < 0:\n",
+    "    bias_voltage = ctrl_data.get_bias_voltage()\n",
+    "# if gain_setting < 0:\n",
+    "#     gain_setting = ctrl_data.get_gain_setting()\n",
+    "if gain_mode < 0:\n",
+    "    gain_mode = ctrl_data.get_gain_mode()\n",
     "\n",
-    "fout_temp += f'_{_fit_func}_Fit'\n",
-    "lout_n = fout_temp\n",
-    "fout_n = fout_temp\n",
-    "log_path = out_folder\n",
-    "logger = logging.getLogger()\n",
-    "fhandler = logging.FileHandler(filename='{}/{}.log'.format(log_path, lout_n), mode='a')\n",
-    "formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')\n",
-    "fhandler.setFormatter(formatter)\n",
-    "logger.addHandler(fhandler)\n",
-    "logger.setLevel(logging.DEBUG)\n",
-    "\n",
-    "logging.info('chunk_size: {}'.format(chunk_size))\n",
-    "logging.info('block_size: {}'.format(block_size))"
+    "print(f\"Integration time is {integration_time} us\")\n",
+    "# print(f\"Gain setting is {gain_setting} (run settings: {ctrl_data.run_settings})\")\n",
+    "print(f\"Gain mode is {gain_mode} ({ctrl_data.run_mode})\")\n",
+    "print(f\"Bias voltage is {bias_voltage} V\")\n",
+    "print(f\"Number of memory cells are {memory_cells}\")"
    ]
   },
   {
@@ -126,50 +159,40 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "h_spectra = None\n",
-    "edges = None\n",
-    "noise_map = None\n",
-    "# transposition here is just to make saved histo compatible with my other notebooks \n",
-    "with h5file(out_folder + file_h_name, 'r') as f:\n",
-    "    logging.info(f'opening histo in file {file_h_name}')\n",
-    "    if 'histos' in f.keys():\n",
-    "        h_spectra = np.transpose(np.array(f['histos']))\n",
-    "        logging.info(f'histogram found: {h_spectra.shape}')\n",
+    "# transposition here is just to make saved histo compatible with my other notebooks\n",
+    "with h5file(out_folder / file_h_name, \"r\") as f:\n",
+    "    print(f\"opening histo in file {file_h_name}\")\n",
+    "    if \"histos\" in f.keys():\n",
+    "        h_spectra = np.transpose(np.array(f[\"histos\"]))\n",
+    "        print(f\"histogram found: {h_spectra.shape}\")\n",
     "    else:\n",
-    "        raise AttributeError('No histo in file!')\n",
-    "        \n",
-    "    edges = np.array(f['edges'])\n",
-    "    x = (edges[1:] + edges[:-1])/2.\n",
-    "    if 'noise_map' in f.keys():\n",
-    "        noise_map = np.array(f['noise_map'])\n",
-    "        logging.info(f'noise map found: {noise_map.shape}')\n",
+    "        raise AttributeError(\"No histo in file!\")\n",
+    "\n",
+    "    edges = np.array(f[\"edges\"])\n",
+    "\n",
+    "    x = (edges[1:] + edges[:-1]) / 2.0\n",
+    "    if \"noise_map\" in f.keys():\n",
+    "        noise_map = np.array(f[\"noise_map\"])\n",
+    "        print(f\"noise map found: {noise_map.shape}\")\n",
     "    else:\n",
-    "        logging.info('noise map not found!')\n",
-    "        \n",
-    "    if 'integration_time' in f.attrs.keys():\n",
-    "        integration_time = np.float32(f.attrs['integration_time'])\n",
+    "        warning(\"noise map not found!\")\n",
+    "\n",
+    "    print(\"Reading control data from histogram files.\")\n",
+    "    if \"integration_time\" in f.attrs.keys():\n",
+    "        integration_time = np.float32(f.attrs[\"integration_time\"])\n",
     "    else:\n",
-    "        logging.info(f'integration_time not found! using default value{integration_time}')\n",
-    "    if 'bias_voltage' in f.attrs.keys():\n",
-    "        bias_voltage = np.float32(f.attrs['bias_voltage'])\n",
+    "        print(f\"integration_time is not found! using default value{integration_time}\")\n",
+    "    if \"bias_voltage\" in f.attrs.keys():\n",
+    "        bias_voltage = np.float32(f.attrs[\"bias_voltage\"])\n",
     "    else:\n",
-    "        logging.info(f'integration_time not found! using default value{integration_time}')\n",
-    "        \n",
-    "    if 'dir_date_iso' in f.attrs.keys():\n",
-    "        dir_date_iso = str(f.attrs['dir_date_iso'])\n",
+    "        warning(f\"bias_voltage not found! using default value: {bias_voltage}\")\n",
+    "\n",
+    "    if \"creation_time\" in f.attrs.keys():\n",
+    "        dir_date_iso = str(f.attrs[\"creation_time\"])\n",
     "        if len(dir_date_iso) == 0:\n",
-    "            logging.info('warning: dir date is empty string')\n",
+    "            warning(\"Dir date is empty string\")\n",
     "    else:\n",
-    "        logging.info('dir_date not found!')"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "x, h_spectra = set_histo_range(x, h_spectra, _h_range) "
+    "        print(\"dir_date not found!\")"
    ]
   },
   {
@@ -182,20 +205,27 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "scrolled": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
-    "chunks = jungfrau_ff.chunk_Multi([h_spectra, noise_map], block_size)\n",
-    "print(np.array(chunks).shape)\n",
-    "\n",
+    "chunks = jungfrau_ff.chunk_Multi([h_spectra], block_size)\n",
     "pool = multiprocessing.Pool()\n",
     "\n",
-    "partial_fit = partial(jungfrau_ff.fit_histogram, x, _fit_func, n_sigma, rebin, ratio)\n",
+    "st = time.perf_counter()\n",
     "\n",
-    "logging.info('starting spectra fit')\n",
+    "partial_fit = partial(\n",
+    "    jungfrau_ff.fit_histogram,\n",
+    "    x,\n",
+    "    _fit_func,\n",
+    "    n_sigma,\n",
+    "    rebin,\n",
+    "    ratio,\n",
+    "    noise_map,\n",
+    ")\n",
+    "\n",
+    "print(\"starting spectra fit\")\n",
     "r_maps = pool.map(partial_fit, chunks)\n",
+    "print(\"r_maps calculation\", time.perf_counter() - st)\n",
     "\n",
     "g0_map = np.zeros((memory_cells, sensor_size[0], sensor_size[1]), dtype=np.float32)\n",
     "sigma_map = np.zeros((memory_cells, sensor_size[0], sensor_size[1]), dtype=np.float32)\n",
@@ -204,26 +234,31 @@
     "\n",
     "for i, r in enumerate(r_maps):\n",
     "    g0_chk, sigma_chk, chi2ndf_chk, alpha_chk = r\n",
-    "    \n",
-    "    n_blocks_col = int(g0_map.shape[-1]/block_size[1])\n",
-    "    irow = int(np.floor(i/n_blocks_col)) * block_size[0]\n",
-    "    icol = i%n_blocks_col * block_size[1]\n",
-    "    \n",
-    "    g0_map[..., irow:irow+block_size[0], icol:icol+block_size[1]] = g0_chk\n",
-    "    sigma_map[..., irow:irow+block_size[0], icol:icol+block_size[1]] = sigma_chk\n",
-    "    chi2ndf_map[..., irow:irow+block_size[0], icol:icol+block_size[1]] = chi2ndf_chk\n",
-    "    alpha_map[..., irow:irow+block_size[0], icol:icol+block_size[1]] = alpha_chk\n",
-    "    \n",
+    "\n",
+    "    n_blocks_col = int(g0_map.shape[-1] / block_size[1])\n",
+    "    irow = int(np.floor(i / n_blocks_col)) * block_size[0]\n",
+    "    icol = i % n_blocks_col * block_size[1]\n",
+    "\n",
+    "    g0_map[..., irow : irow + block_size[0], icol : icol + block_size[1]] = g0_chk\n",
+    "    sigma_map[..., irow : irow + block_size[0], icol : icol + block_size[1]] = sigma_chk\n",
+    "    chi2ndf_map[\n",
+    "        ..., irow : irow + block_size[0], icol : icol + block_size[1]\n",
+    "    ] = chi2ndf_chk\n",
+    "    alpha_map[..., irow : irow + block_size[0], icol : icol + block_size[1]] = alpha_chk\n",
+    "\n",
+    "print(\"loading r_maps calculation results\", time.perf_counter() - st)\n",
+    "\n",
     "pool.close()\n",
-    "logging.info('... done')"
+    "\n",
+    "print(\"... done\")"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## final steps ##\n",
-    "### saving fit results ###"
+    "## Final steps\n",
+    "### Saving fit results"
    ]
   },
   {
@@ -232,33 +267,33 @@
    "metadata": {},
    "outputs": [],
    "source": [
+    "fout_path = f\"{out_folder}/{fout_temp}.h5\"\n",
     "\n",
-    "fout_path = f'{out_folder}/{fout_n}.h5'\n",
-    "logging.info(fout_path)\n",
-    "with h5file(fout_path, 'w') as fout:\n",
-    "    logging.info('saving noise map ...')\n",
+    "with h5file(fout_path, \"w\") as fout:\n",
+    "    print(\"saving noise map ...\")\n",
     "\n",
     "    ##trasposition is to make it compatible with my other nb\n",
     "    dset_noi = fout.create_dataset(\"noise_map\", data=np.transpose(noise_map))\n",
-    "    logging.info('saving fit results ...')\n",
+    "    print(\"saving fit results ...\")\n",
     "\n",
     "    dset_chi2 = fout.create_dataset(\"chi2map\", data=np.transpose(chi2ndf_map))\n",
     "    dset_gmap_fit = fout.create_dataset(\"gainMap_fit\", data=np.transpose(g0_map))\n",
     "    dset_std = fout.create_dataset(\"sigmamap\", data=np.transpose(sigma_map))\n",
     "    dset_alpha = fout.create_dataset(\"alphamap\", data=np.transpose(alpha_map))\n",
+    "    fout.attrs[\"memory_cells\"] = memory_cells  # TODO: Why memory cells are not saved here. What about the other conditions??\n",
     "    fout.attrs[\"integration_time\"] = integration_time\n",
     "    fout.attrs[\"bias_voltage\"] = bias_voltage\n",
     "    fout.attrs[\"dir_date_iso\"] = dir_date_iso\n",
-    "    fout.attrs['karabo_id'] = karabo_id\n",
-    "    fout.attrs['da_name'] = da_name\n",
+    "    fout.attrs[\"karabo_id\"] = karabo_id\n",
+    "    fout.attrs[\"da_name\"] = da_name\n",
     "\n",
-    "logging.info('closing')"
+    "print(\"closing\")"
    ]
   }
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": ".cal2_venv",
    "language": "python",
    "name": "python3"
   },
@@ -276,5 +311,5 @@
   }
  },
  "nbformat": 4,
- "nbformat_minor": 1
+ "nbformat_minor": 4
 }
diff --git a/notebooks/Jungfrau/gainCal_JF_Fit_sendDB_NBC.ipynb b/notebooks/Jungfrau/gainCal_JF_Fit_sendDB_NBC.ipynb
index 9d7d7d4975ac91e2fd7a5edafdd65d96875f33f9..f145000e831d458ec8f005e6d92ab94dbef4d95d 100755
--- a/notebooks/Jungfrau/gainCal_JF_Fit_sendDB_NBC.ipynb
+++ b/notebooks/Jungfrau/gainCal_JF_Fit_sendDB_NBC.ipynb
@@ -19,11 +19,11 @@
     "out_folder = \"/gpfs/exfel/data/scratch/ahmedk/test/jf_ff/gain_maps\"\n",
     "metadata_folder = ''  # Directory containing calibration_metadata.yml when run by xfel-calibrate\n",
     "\n",
-    "g0_run = 94\n",
+    "g0_runs = [94]\n",
     "\n",
     "# Detector module parameters.\n",
     "karabo_id = 'MID_EXP_JF500K2'\n",
-    "da_name = 'JNGFR02'\n",
+    "karabo_da = ['JNGFR02']\n",
     "\n",
     "# Parameter conditions\n",
     "bias_voltage = -1  # detector bias voltage\n",
@@ -85,12 +85,12 @@
    "source": [
     "if gain_setting == 1:\n",
     "    gain_map_name = 'gain_map_hg0'\n",
-    "\n",
+    "da_name = karabo_da[0]\n",
     "g_map_old_dir = Path(g_map_old_dir)\n",
     "in_folder = Path(in_folder)\n",
     "out_folder = Path(out_folder)\n",
     "gain_file = g_map_old_dir / gain_map_file\n",
-    "\n",
+    "g0_run = g0_runs[0]\n",
     "\n",
     "# Run's creation time:\n",
     "creation_time = calcat_creation_time(in_folder, g0_run, creation_time)\n",
diff --git a/notebooks/Jungfrau/jungfrau_ff.py b/notebooks/Jungfrau/jungfrau_ff.py
deleted file mode 100644
index b152d9521291768fedee8ba9af5e2c1aafaa9649..0000000000000000000000000000000000000000
--- a/notebooks/Jungfrau/jungfrau_ff.py
+++ /dev/null
@@ -1,508 +0,0 @@
-import numpy as np
-from XFELDetAna.detectors.jungfrau.fitFuncs import (
-    charge_sharing,
-    chi_square_fit,
-    double_peak,
-    gauss,
-    histogram_errors,
-)
-
-
-def chunk_trains(data, train_chk):
-    """
-    chunks data along the train dimension
-
-    Args:
-    * data (list): input data arrays, each at least 1-D
-    * train_chk (int): number of trains per chunk
-
-    Returns: list of chunked data
-    """
-    n_trains = int(data[0].shape[0])
-    chunks = []
-    for i in range(0, n_trains, train_chk):
-        this_chunk = []
-        this_chunk = [data[0][i:min(i+train_chk, n_trains)]]
-        for j in range(1, len(data)):
-            if data[j] is not None:
-                this_chunk.append(data[j][i:min(i+train_chk, n_trains)])
-            else:
-                this_chunk.append(None)
-
-        chunks.append(this_chunk)
-    return chunks
-
-
-def chunk_Multi(data, block_size):
-    """
-    chunks input array along the 'row' and 'col' dimensions
-
-    Args:
-    * data (list): input data arrays, each at least 2-D
-    * block_size (list, int): [chunk_row, chunk_col],
-        dimension of the 2-D chunk
-
-    Returns: list of chunked data
-    """
-    intervals_r = range(0, data[0].shape[-2], block_size[0])
-    intervals_c = range(0, data[0].shape[-1], block_size[1])
-
-    chunks = []
-
-    for irow in intervals_r:
-        for icol in intervals_c:
-            this_chunk = []
-            this_chunk = [
-                data[0][
-                    ..., irow:irow + block_size[0],
-                    icol:icol + block_size[1]
-                    ]
-                ]
-            for j in range(1, len(data)):
-                if data[j] is not None:
-                    this_chunk.append(
-                        data[j][..., irow:irow + block_size[0],
-                                icol:icol + block_size[1]]
-                        )
-                else:
-                    this_chunk.append(None)
-            chunks.append(this_chunk)
-
-    return chunks
-
-
-def subtract_offset(offset_map, _inp):
-    """
-    perform offset subtraction on raw data
-
-    Args:
-    * offset_map (xarray, float): map with offset constants,
-        with shape (3, memory_cells, row, col)
-    * _inp (list): input data as:
-        * _inp[0]: raw images, with shape (trains, memory_cells, row, col)
-        * _inp[1]: gain bit map, with shape (trains, memory_cells, row, col)
-
-    Returns: offset subtracted images
-    """
-    imgs = _inp[0]
-    gbit = _inp[1]
-
-    n_cells = imgs.shape[1]
-
-    for cell in range(n_cells):
-        this_cell_gbit = gbit.sel(cell=cell)
-
-        this_cell_off = offset_map[:, cell]
-
-        _o = np.choose(
-            this_cell_gbit, (
-                np.expand_dims(this_cell_off[0], axis=0),
-                np.expand_dims(this_cell_off[1], axis=0),
-                np.expand_dims(this_cell_off[2], axis=0)
-            )
-        )
-
-        imgs.loc[dict(cell=cell)] -= _o
-
-    return imgs
-
-
-def fill_histogram(h_bins, _inp):
-    """
-    Fills an histogram with shape (n_bins-1, memory_cells, n_rows, n_cols)
-    from input images.
-
-    Args:
-    * h_bins (list, float): the binning of the x-axis
-    * _inp (list): data to be histogrammed, as:
-        * _inp[0]: images, with shape (trains, memory_cells, row, col)
-
-    Returns: histogram bin counts, bin edges.
-
-    """
-    imgs = _inp[0]
-    n_cells = imgs.shape[1]
-    n_rows = imgs.shape[2]
-    n_cols = imgs.shape[3]
-
-    h_chk = np.zeros((len(h_bins)-1, n_cells, n_rows, n_cols), dtype=np.int)
-
-    e_chk = None
-
-    for cell in range(n_cells):
-        for row in range(n_rows):
-            for col in range(n_cols):
-
-                this_cell = imgs.sel(cell=cell)
-                this_pix = this_cell.isel(row=row, col=col)
-
-                _h, _e = np.histogram(this_pix, bins=h_bins)
-                h_chk[..., cell, row, col] += _h
-
-                if e_chk is None:
-                    e_chk = np.array(_e)
-
-    return h_chk, e_chk
-
-
-# peak finder to find the first photon peak and/or the pedestal peak
-# can/should be replaced with smthg more efficient
-def peak_position(x, h, thr=75, dist=30., ratio=0.4):
-    """
-    finds peaks in the histogram
-    very rough, self-made function, should be replaced by better one
-
-    Args:
-    * x (list, float): histogram bin centers
-    * h (list, float): histogram bin counts
-    * thr (int): lower threshold along x-axis in ADC units to look for a peak
-    * dist (float): minimal distance between the last found peak and the next
-    * ratio (float): minimal ratio between the last found peak and the next
-
-    Returns: list of sorted peak positions along the x-axis,
-        index of the peak with lowes value along x-axis
-    """
-    imin = 0
-    peak_bins = []
-    if len(x) == len(h):
-        for i in range(0, len(x)):
-            if x[i] > thr:
-                imin = i
-                break
-        sorted_i = np.argsort(h[imin:])
-        peak_bins.append(sorted_i[-1] + imin)
-        low_h = ratio * h[sorted_i[-1] + imin]
-        for j in range(1, len(sorted_i)):
-            ibin = sorted_i[-1 - j] + imin
-            if h[ibin] > low_h:
-                min_dist = (x[peak_bins] - x[ibin])**2. > dist**2.
-                if np.all(min_dist):
-                    peak_bins.append(ibin)
-    else:
-        raise AttributeError(
-            "axis and histogram arrays have "
-            f"different length: {len(x)} and {len(h)}")
-    return peak_bins, imin
-
-
-def fit_charge_sharing(x, y, yerr, sigma0, n_sigma, ratio):
-    """
-    fits histogram with single photon function with charge sharing tail
-
-    Args:
-    * x (array, float): x values
-    * y (array, float): y values
-    * yerr (array, float): errors of the y values
-    * sigma0 (float): rough estimate of peak variance
-    * n_sigma (int): to calculate threshold of the peak finder as
-        thr = n_sigma * sigma0
-    * ratio (float): ratio parameter of the peak finder
-
-    Returns: peak position, peak variance, chi2/ndf,
-        charge sharing parameter.
-    """
-
-    norm = np.sum(y) * (x[1] - x[0])
-    alpha0 = 0.3
-    thr = n_sigma * sigma0
-    _peaks, _ = peak_position(x, y, thr, ratio=ratio)
-
-    q0 = -1.
-    if len(_peaks) > 0:
-        q0 = np.ma.min(x[_peaks])
-
-    q = -1.
-    sigma = -1.
-    alpha = -1.
-    chi2ndf = -1.
-
-    if q0 > -1.:
-        limit_q = (q0-sigma0, q0+sigma0)
-        limit_sigma = (0.1*sigma0, 2.*sigma0)
-        limit_alpha = (0.01*alpha0, 1.)
-        limit_norm = (np.sqrt(norm), 3.*norm)
-        res = chi_square_fit(
-            charge_sharing, x, y, yerr,
-            fit_range=(0.5*q0, q0+1.5*sigma0),
-            ped=0.,
-            q=q0,
-            sigma=sigma0,
-            alpha=alpha0,
-            norm=norm,
-            limit_q=limit_q,
-            limit_sigma=limit_sigma,
-            limit_alpha=limit_alpha,
-            limit_norm=limit_norm,
-            fix_ped=True,
-            print_level=0,
-            pedantic=False
-        )
-        values = res[0]
-        chi2 = res[2]
-        ndf = res[3]
-        is_valid = res[4]
-
-        if is_valid:
-            q = values['q']
-            sigma = values['sigma']
-            alpha = values['alpha']
-            chi2ndf = chi2/ndf
-        else:
-            q = -1000.
-            sigma = -1000.
-            alpha = -1000.
-            chi2ndf = -1000.
-    return q, sigma, chi2ndf, alpha
-
-
-def fit_double_charge_sharing(x, y, yerr, sigma0, n_sigma, ratio):
-    """
-    fits histogram with sum of two single photon functions
-    with charge sharing tail.
-
-    Args:
-    * x (array, float): x values
-    * y (array, float): y values
-    * yerr (array, float): errors of the y values
-    * sigma0 (float): rough estimate of peak variance
-    * n_sigma (int): to calculate threshold of the peak finder as
-        thr = n_sigma * sigma0
-    * ratio (float): ratio parameter of the peak finder
-
-    Returns: peak position, peak variance, chi2/ndf,
-        charge sharing parameter (all of them of the first peak)
-
-    """
-    norm = np.sum(y) * (x[1] - x[0])
-    alpha0 = 0.3
-    thr = n_sigma * sigma0
-    _peaks, _ = peak_position(x, y, thr, ratio=ratio)
-
-    qa = -1.
-    if len(_peaks) > 0:
-        qa = np.ma.min(x[_peaks])
-
-    q = -1.
-    sigma = -1.
-    alpha = -1.
-    chi2ndf = -1.
-
-    if qa > -1.:
-
-        qb = 1.1 * qa
-        limit_q_a = (qa - sigma0, qa + sigma0)
-        limit_sigma_a = (0.1 * sigma0, 2. * sigma0)
-        limit_alpha = (0.01, 1.)
-        limit_norm_a = (np.ma.sqrt(0.1 * norm), 2. * norm)
-        limit_q_b = (qb - sigma0, qb + sigma0)
-        limit_sigma_b = (0.1 * sigma0, 2. * sigma0)
-        limit_norm_b = (np.ma.sqrt(0.1 * norm), 2 * norm)
-
-        res = chi_square_fit(
-            double_peak, x, y, yerr,
-            fit_range=(0.5*qa, qb + sigma0),
-            ped=0.,
-            q_a=qa,
-            sigma_a=sigma0,
-            alpha=alpha0,
-            norm_a=norm,
-            q_b=qb,
-            sigma_b=sigma0,
-            norm_b=norm,
-            limit_q_a=limit_q_a,
-            limit_sigma_a=limit_sigma_a,
-            limit_alpha=limit_alpha,
-            limit_norm_a=limit_norm_a,
-            limit_q_b=limit_q_b,
-            limit_sigma_b=limit_sigma_b,
-            limit_norm_b=limit_norm_b,
-            fix_ped=True,
-            print_level=0,
-            pedantic=False
-        )
-        values = res[0]
-        chi2 = res[2]
-        ndf = res[3]
-        is_valid = res[4]
-
-        if is_valid:
-            q = values['q_a']
-            sigma = values['sigma_a']
-            alpha = values['alpha']
-            chi2ndf = chi2/ndf
-        else:
-            q = -1000.
-            sigma = -1000.
-            alpha = -1000.
-            chi2ndf = -1000.
-
-    return q, sigma, chi2ndf, alpha
-
-
-def fit_gauss(x, y, yerr, sigma0, n_sigma, ratio):
-    """
-    fits histogram with a gaussian function
-
-    Args:
-    * x (array, float): x values
-    * y (array, float): y values
-    * yerr (array, float): errors of the y values
-    * sigma0 (float): rough estimate of peak variance
-    * n_sigma (int): to calculate threshold of the peak finder
-        as thr = n_sigma * sigma0
-    * ratio (float): ratio parameter of the peak finder
-
-    Returns: peak position, peak variance, chi2/ndf,
-        charge sharing parameter (last one alway == 0)
-        all of them are kept for compatibility with the wrap around function.
-
-    """
-    norm = np.sum(y) * (x[1] - x[0])/np.sqrt(2.*np.pi*sigma0**2)
-    thr = n_sigma * sigma0
-    _peaks, _ = peak_position(x, y, thr, ratio=ratio)
-
-    q0 = -1000.
-    if len(_peaks) > 0:
-        q0 = np.ma.min(x[_peaks])
-
-    q = -1.
-    sigma = -1.
-    alpha = -1.
-    chi2ndf = -1.
-
-    if q0 > -1000.:
-        limit_amp = (0.1*norm, 10.*norm)
-        limit_mean = (q0-sigma0, q0+sigma0)
-        limit_sigma = (0.1*sigma0, 10.*sigma0)
-
-        res = chi_square_fit(gauss, x, y, yerr,
-                             fit_range=[q0 - 2.*sigma0, q0 + 2.*sigma0],
-                             amp=norm,
-                             mean=q0,
-                             sigma=sigma0,
-                             limit_amp=limit_amp,
-                             limit_mean=limit_mean,
-                             limit_sigma=limit_sigma,
-                             print_level=0,
-                             pedantic=False)
-        values = res[0]
-        chi2 = res[2]
-        ndf = res[3]
-        is_valid = res[4]
-
-        if is_valid:
-            q = values['mean']
-            sigma = values['sigma']
-            alpha = 0.
-            chi2ndf = chi2/ndf
-        else:
-            q = -1000.
-            sigma = -1000.
-            alpha = -1000.
-            chi2ndf = -1000.
-
-    return q, sigma, chi2ndf, alpha
-
-
-def rebin_histo(h, x, r):
-
-    if len(x) % r == 0:
-        h_out = None
-        x_out = x[::r]
-
-        for i in range(0, len(h), r):
-            if h_out is not None:
-                h_out = np.append(h_out, np.sum(h[i:i+r], axis=0))
-            else:
-                h_out = np.array(np.sum(h[i:i+r], axis=0))
-
-    else:
-        raise AttributeError('Rebin factor is not exact divisor of bins!')
-
-    return h_out, x_out
-
-
-def set_histo_range(_x, _h, _h_range):
-    """
-    reduces the histogram bin axis to the range specified
-
-    Args:
-    * _x (array, float): the bin centers array
-    * _h (array, integers): the histogram with shape
-        (bins, cells, columns, row)
-    * _h_range (tuple, float): the (liminf, limsup) of the desired range
-
-    Returns: the new bin centers array and the new histogram
-
-    """
-
-    i_min = (np.abs(_x - _h_range[0])).argmin()
-    i_max = (np.abs(_x - _h_range[1])).argmin()
-
-    return _x[i_min:i_max], _h[i_min:i_max]
-
-
-def fit_histogram(x, _fit_func, n_sigma, rebin, ratio, _inp):
-    """
-    wrap around function for fitting of histogram
-
-    Args:
-    * x (list, float): - bin centers along x
-    * _fit_func (string): which function to use for fit
-         * CHARGE_SHARING: single peak with charge sharing tail
-         * CHARGE_SHARING_2: sum of two CHARGE_SHARING
-         * GAUSS: gaussian function
-    * n_sigma (int): to calculate threshold of the peak finder
-        as thr = n_sigma * sigma0
-    * ratio (float): ratio parameter of the peak finder
-    * _input (list): contains the data to preform the fit
-         * _input[0]: histogram bin counts with shape
-            (n_bins, memory_cells, row, col)
-         * _input[1]: noise map with shape (3, memory_cells, row, col)
-
-    Returns: map of peak values, map of peak variances,
-        map of chi2/ndf, map of charge sharing parameter values
-    """
-
-    _funcs = dict(
-        CHARGE_SHARING=fit_charge_sharing,
-        GAUSS=fit_gauss,
-        CHARGE_SHARING_2=fit_double_charge_sharing,
-        )
-
-    fit_func = _funcs[_fit_func]
-
-    histo = _inp[0]
-    noise = _inp[1]
-
-    n_cells = histo.shape[1]
-    n_rows = histo.shape[2]
-    n_cols = histo.shape[3]
-
-    sigma0 = 15.
-
-    g0_chk = np.zeros((n_cells, n_rows, n_cols), dtype=np.float32)
-    sigma_chk = np.zeros((n_cells, n_rows, n_cols), dtype=np.float32)
-    chi2ndf_chk = np.zeros((n_cells, n_rows, n_cols), dtype=np.float32)
-    alpha_chk = np.zeros((n_cells, n_rows, n_cols), dtype=np.float32)
-
-    for cell in range(n_cells):
-        for row in range(n_rows):
-            for col in range(n_cols):
-
-                y = histo[..., cell, row, col]
-                y, _x = rebin_histo(y, x, rebin)
-                yerr = histogram_errors(y)
-
-                if noise[0, cell, row, col] > 0.:
-                    sigma0 = noise[0, cell, row, col]
-
-                q, sigma, chi2ndf, alpha = fit_func(
-                    _x, y, yerr, sigma0, n_sigma, ratio)
-
-                g0_chk[cell, row, col] = q
-                sigma_chk[cell, row, col] = sigma
-                chi2ndf_chk[cell, row, col] = chi2ndf
-                alpha_chk[cell, row, col] = alpha
-
-    return g0_chk, sigma_chk, chi2ndf_chk, alpha_chk
diff --git a/src/xfel_calibrate/calibrate.py b/src/xfel_calibrate/calibrate.py
index fa14605bf1068aa43b0e512332b607ed0afb0b74..6d2d3104921b1dc3e881b2807d3104bd58eeace6 100755
--- a/src/xfel_calibrate/calibrate.py
+++ b/src/xfel_calibrate/calibrate.py
@@ -576,12 +576,9 @@ def run(argv=None):
     # Ensure files are opened as UTF-8 by default, regardless of environment.
     if "readthedocs.org" not in sys.executable:
         locale.setlocale(locale.LC_CTYPE, ('en_US', 'UTF-8'))
-
     if argv is None:
         argv = sys.argv
-
     args, nb_details = parse_argv_and_load_nb(argv)
-
     concurrency = nb_details.concurrency
     concurrency_par = args["concurrency_par"] or concurrency['parameter']
     if concurrency_par == concurrency['parameter']: