diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 8e6b14505114f3f3a67d5e1e6c82754ca864d543..a9c8ca4b56840d45828662a76a8767881ad41cd8 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,5 +1,8 @@
-.github/ @lobis
+.github/* @lobis
 * @jgalan
+* @juanangp
+* @lobis
+* @nkx111
 *.py @lobis
 *.cmake @lobis
 CMakelists.txt @lobis
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 921e41185ba921d83c7ee4e52e94ab34c67e03e9..7b510cbd0e07a13391a8775c2c1f62f830cb9242 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -24,10 +24,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout repo
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Setup Python
-        uses: actions/setup-python@v3
+        uses: actions/setup-python@v5
         with:
           python-version: 3.12
 
@@ -47,10 +47,10 @@ jobs:
           doxygen REST.conf
 
       - name: Upload artifact
-        uses: actions/upload-pages-artifact@v1
+        uses: actions/upload-pages-artifact@v3
         with:
           path: doc/doxygen/html
 
       - name: Deploy to GitHub Pages
         id: deployment
-        uses: actions/deploy-pages@v1
+        uses: actions/deploy-pages@v4
diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml
index ff452373c6ef991e23330394da535409d333004f..56e61353fed62cbf5c8670bafe9b5b0dd5884be5 100644
--- a/.github/workflows/validation.yml
+++ b/.github/workflows/validation.yml
@@ -34,7 +34,7 @@ jobs:
     container:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - name: Build and install
         uses: rest-for-physics/framework/.github/actions/build@master
         with:
@@ -53,7 +53,7 @@ jobs:
     container:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - name: Build and install
         uses: rest-for-physics/framework/.github/actions/build@master
         with:
@@ -73,7 +73,7 @@ jobs:
           key: ${{ env.BRANCH_NAME }}-${{ github.sha }}
           path: ${{ env.REST_PATH }}
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: rest-install
           path: ${{ env.REST_PATH }}
@@ -86,7 +86,7 @@ jobs:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics
     needs: [ framework-install ]
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           repository: rest-for-physics/framework
           path: framework
@@ -109,7 +109,7 @@ jobs:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics
     needs: [ framework-install ]
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           repository: rest-for-physics/framework
           path: framework
@@ -155,7 +155,7 @@ jobs:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics
     needs: [ framework-install ]
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           repository: rest-for-physics/framework
           path: framework
@@ -199,7 +199,7 @@ jobs:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics
     needs: [ framework-install ]
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           repository: rest-for-physics/framework
           path: framework
@@ -230,7 +230,7 @@ jobs:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics
     needs: [ framework-install ]
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           repository: rest-for-physics/framework
           path: framework
@@ -248,7 +248,7 @@ jobs:
         run: |
           source ${{ env.REST_PATH }}/thisREST.sh
           cd framework/pipeline/trex
-          wget https://sultan.unizar.es/trexdm-readouts/readouts_v2.4.root
+          wget https://rest-for-physics.github.io/trexdm-readouts/readouts_v2.4.root
           restManager --c 01_raw.rml --f R01928_tagTest_Vm_250_Vd_160_Pr_6_Gain_0x0_Shape_0xF_Clock_0x4-068.aqs
           restManager --c 02_signal.rml --f RawData_01928.root
           restManager --c 03_hits.rml --f Signals_01928.root
@@ -259,7 +259,7 @@ jobs:
           restRoot -b -q ValidateDetectorParams.C'("Hits_01928.root")'
           python3 validateStreamer.py
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: TREXHits
           path: framework/pipeline/trex/Hits_01928.root
@@ -283,7 +283,7 @@ jobs:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics
     needs: [ framework-install ]
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           repository: rest-for-physics/framework
           path: framework
@@ -303,7 +303,7 @@ jobs:
           cd ${{ env.REST_PATH }}/examples
           restManager --c saveMetadataFile.rml --o meta.root
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: Metadata
           path: ${{ env.REST_PATH }}/examples/meta.root
@@ -315,7 +315,7 @@ jobs:
           restG4 NLDBD.rml
           restRoot -b -q Validate.C'("Run00001_NLDBD_Test.root")'
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: NLDBD
           path: ${{ env.REST_PATH }}/examples/restG4/01.NLDBD/Run00001_NLDBD_Test.root
@@ -330,7 +330,7 @@ jobs:
           restManager --c g4EvSelectionIDsFromFile.rml --f Run00001_NLDBD_Test_g4Analysis.root
           restRoot -b -q ValidateIDsFromFile.C'("Run00001_NLDBD_Test_EvSelectionIDsFromFile.root")'
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: EventSelection
           path: framework/pipeline/selection/IDs.txt
@@ -344,7 +344,7 @@ jobs:
           export REST_FOIL=1
           restG4 alphas.rml
       #      - name: Upload Artifacts
-      #        uses: actions/upload-artifact@v3
+      #        uses: actions/upload-artifact@v4
       #        with:
       #          name: Alphas
       #          path: ${{ env.REST_PATH }}/examples/restG4/08.Alphas/data/Run_5MeV_1um.root
@@ -360,7 +360,7 @@ jobs:
           restManager --c processing.rml --f ../restG4/08.Alphas/data/Run_5MeV_1um.root
           restRoot -b -q Validate.C'("data/Response_5MeV_1um.root")'
       #       - name: Upload Artifacts
-      #         uses: actions/upload-artifact@v3
+      #         uses: actions/upload-artifact@v4
       #         with:
       #           name: AlphaTrack
       #           path: ${{ env.REST_PATH }}/examples/restG4/08.Alphas/data/Run_5MeV_1um.root
@@ -374,7 +374,7 @@ jobs:
     container:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics-reference-jun2022
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - name: Build and install
         uses: rest-for-physics/framework/.github/actions/build@master
         with:
@@ -405,7 +405,7 @@ jobs:
       image: ghcr.io/lobis/root-geant4-garfield:rest-for-physics-reference-jun2022
     needs: [ framework-install-reference ]
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           repository: rest-for-physics/framework
           path: framework
@@ -425,7 +425,7 @@ jobs:
           cd ${{ env.REST_PATH }}/examples
           restManager --c saveMetadataFile.rml --o meta.root
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: MetadataRef
           path: ${{ env.REST_PATH }}/examples/meta.root
@@ -437,7 +437,7 @@ jobs:
           restG4 NLDBD.rml
           restRoot -b -q Validate.C'("Run00001_NLDBD_Test.root")'
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: NLDBDRef
           path: ${{ env.REST_PATH }}/examples/restG4/01.NLDBD/Run00001_NLDBD_Test.root
@@ -452,7 +452,7 @@ jobs:
           restManager --c g4EvSelectionIDsFromFile.rml --f Run00001_NLDBD_Test_g4Analysis.root
           restRoot -b -q ValidateIDsFromFile.C'("Run00001_NLDBD_Test_EvSelectionIDsFromFile.root")'
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: EventSelectionRef
           path: framework/pipeline/selection/IDs.txt
@@ -462,7 +462,7 @@ jobs:
           source ${{ env.REST_PATH }}/thisREST.sh
           cd ${{ env.REST_PATH }}/examples/restG4/07.FullChainDecay/
           restG4 fullChain.rml -o Run00001_U238_FullChainDecay.root
-          restRoot -b -q Validate.C'("Run00001_U238_FullChainDecay.root", 15)'
+          restRoot -b -q Validate.C'("Run00001_U238_FullChainDecay.root", 16)'
           restG4 singleDecay.rml -o Run00002_U238_SingleChainDecay.root
           restRoot -b -q Validate.C'("Run00002_U238_SingleChainDecay.root", 1)'
           export REST_ISOTOPE=Be7
@@ -477,7 +477,7 @@ jobs:
           export REST_FOIL=1
           restG4 alphas.rml
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: AlphasRef
           path: ${{ env.REST_PATH }}/examples/restG4/08.Alphas/data/Run_5MeV_1um.root
@@ -493,7 +493,7 @@ jobs:
           restManager --c processing.rml --f ../restG4/08.Alphas/data/Run_5MeV_1um.root
           restRoot -b -q Validate.C'("data/Response_5MeV_1um.root")'
       - name: Upload Artifacts
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: AlphaTrack
           path: ${{ env.REST_PATH }}/examples/restG4/08.Alphas/data/Run_5MeV_1um.root
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6d4e7928f6273af793c224f66133a2ef762530e4..557218d49378de207039b1c40eb63ff597f0e4e3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,6 +66,11 @@ endif ()
 set(external_include_dirs)
 set(external_libs)
 
+if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+    set(external_include_dirs ${external_include_dirs} "/usr/local/include/")
+    message(STATUS "Adding BREW include directory: /usr/local/include")
+endif (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+
 set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${DECAY_PATH}/cmake
                       ${CMAKE_MODULE_PATH})
 set(CMAKE_MACOSX_RPATH 1)
@@ -188,6 +193,27 @@ else ()
     set(REST_MPFR OFF)
 endif (${REST_MPFR} MATCHES "ON")
 
+if (((${REST_ALL_LIBS} MATCHES "ON") AND (NOT DEFINED RESTLIB_AXION))
+    OR (${RESTLIB_AXION} MATCHES "ON"))
+    # GSL #### Find GSL
+    find_package(GSL REQUIRED)
+
+    # Include GSL directories
+    set(external_include_dirs ${external_include_dirs} ${GSL_INCLUDE_DIRS})
+
+    # Link GSL libraries
+    set(external_libs ${external_libs};${GSL_LIBRARIES})
+
+    message(STATUS "Found GSL libraries : ${GSL_LIBRARIES}")
+    message(STATUS "GSL headers : ${GSL_INCLUDE_DIRS}")
+
+    set(REST_GSL ON)
+else ()
+    set(REST_GSL OFF)
+    message(STATUS "GSL libraries not required")
+endif (((${REST_ALL_LIBS} MATCHES "ON") AND (NOT DEFINED RESTLIB_AXION))
+       OR (${RESTLIB_AXION} MATCHES "ON"))
+
 # CURL  #####
 find_library(CURL_LIB curl)
 if (NOT ${CURL_LIB} STREQUAL "CURL_LIB-NOTFOUND")
@@ -335,7 +361,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin") # we must call library install here in
 endif ()
 
 # Copy pcm files
-if (CMAKE_SYSTEM_NAME MATCHES "Darwin" OR CMAKE_SYSTEM_NAME MATCHES "Windows")
+if (CMAKE_SYSTEM_NAME MATCHES "Windows")
 
     # Copy pcm files to bin
     install(
diff --git a/README.md b/README.md
index 42d99934dcf2667465904f9ee6b58973602d4d0b..8183f1c825636d81471fb7ffab4d87df93dff42b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # The REST Framework
 [![DOI](https://zenodo.org/badge/324291710.svg)](http://doi.org/10.5281/zenodo.4528985)
-[![pipeline status](https://gitlab.cern.ch/rest-for-physics/framework/badges/master/pipeline.svg)](https://gitlab.cern.ch/rest-for-physics/framework/-/commits/master)
+[![pipeline status](https://github.com/rest-for-physics/framework/actions/workflows/validation.yml/badge.svg)](https://github.com/rest-for-physics/framework)
 [![website](https://img.shields.io/badge/user-guide-E8B6FF.svg)](https://rest-for-physics.github.io)
 [![api](https://img.shields.io/badge/user-API-FFCA78.svg)](https://rest-for-physics.github.io/framework/)
 [![forum](https://img.shields.io/badge/user-forum-AAFF90.svg)](https://rest-forum.unizar.es/)
@@ -77,6 +77,8 @@ A major change at 2.3 will prevent from backwards compatibility, since class nam
 - PandaX-III: Searching for neutrinoless double beta decay with high pressure 136Xe gas time projection chambers. [X. Chen et al., Science China Physics, Mechanics & Astronomy 60, 061011 (2017)](https://doi.org/10.1007/s11433-017-9028-0) [arXiv:1610.08883](https://arxiv.org/abs/1610.08883).
 
 ## Presentations
+- Background model studies for IAXO using the REST-for-Physics framework, Luis Obis, [2024-Apr, VIEnna Workshop on Simulations 2024  (VIEWS24), Vienna](https://indico.cern.ch/event/1275551/contributions/5858816/).
+- REST-for-Physics framework for Geant4 simulations and data analysis, Álvaro Ezquerro, [2024-Apr, VIEnna Workshop on Simulations 2024  (VIEWS24), Vienna](https://indico.cern.ch/event/1275551/contributions/5858814/).
 - REST-for-Physics, Luis Obis, [2022-May, ROOT Users Workshop, FermiLab](https://indico.fnal.gov/event/23628/contributions/240755/).
 - REST v2.0 : A data analysis and simulation framework for micro-patterned readout detectors., Javier Galan, [2016-Dec, 8th Symposium on Large TPCs for low-energy rare event detection, Paris](https://indico.cern.ch/event/473362/contributions/2334838/).
 
diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in
index e925304f81ca66355b5e55afcd9ebee5e1094254..feb51315ef060c0466246054cdab3d3785fa362f 100644
--- a/cmake/cmake_uninstall.cmake.in
+++ b/cmake/cmake_uninstall.cmake.in
@@ -8,10 +8,10 @@ string(REGEX REPLACE "\n" ";" files "${files}")
 foreach(file ${files})
   message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
   if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
-    exec_program(
-      "${CMAKE_COMMAND}" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+    execute_process(
+	  COMMAND ${CMAKE_COMMAND} -E rm -f $ENV{DESTDIR}${file}
       OUTPUT_VARIABLE rm_out
-      RETURN_VALUE rm_retval
+	  RESULT_VARIABLE rm_retval
       )
     if(NOT "${rm_retval}" STREQUAL 0)
       message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
diff --git a/cmake/thisREST.cmake b/cmake/thisREST.cmake
index d0cd41b6269187dfb9eff24d764740fceaa3e8ab..450284ef2697d3b77eb898551c0e149b5d91c278 100644
--- a/cmake/thisREST.cmake
+++ b/cmake/thisREST.cmake
@@ -1,5 +1,40 @@
 # Write thisREST.[c]sh to INSTALL directory
 
+# Check if the LCG environment was loaded. Then it will be sourced in
+# thisREST.sh
+set(loadLCG "")
+if (DEFINED ENV{LCG_VERSION})
+    # Retrieve the value of the environment variable
+    set(LCG_VERSION $ENV{LCG_VERSION})
+
+    # Print the value of the environment variable in CMake
+    message(STATUS "Found LCG environment! Version: ${LCG_VERSION}")
+
+    set(C_INCLUDE_PATH "$ENV{C_INCLUDE_PATH}")
+
+    string(FIND "${C_INCLUDE_PATH}" ":" COLON_POSITION)
+
+    if (COLON_POSITION GREATER_EQUAL 0)
+        # Extract the substring from the beginning up to the position of the
+        # first ':'
+        string(SUBSTRING "${C_INCLUDE_PATH}" 0 "${COLON_POSITION}" C_CLEAN)
+    else ()
+        # If ':' is not found, use the entire string
+        set(C_CLEAN "${C_INCLUDE_PATH}")
+    endif ()
+
+    string(REPLACE "include" "setup.sh" LCG_SETUP "${C_CLEAN}")
+
+    # Print the modified string
+    message(STATUS "Original path: ${C_CLEAN}")
+    message(STATUS "Modified path: ${LCG_SETUP}")
+
+    set(loadLCG
+        "\# We load the LCG_${LCG_VERSION} environment.\necho \\\"Loading\ LCG_${LCG_VERSION}\ environment\\\"\nsource ${LCG_SETUP}\n"
+    )
+
+endif ()
+
 # We identify the thisroot.sh script for the corresponding ROOT version
 execute_process(
     COMMAND root-config --prefix
@@ -97,6 +132,8 @@ file( WRITE \${CMAKE_INSTALL_PREFIX}/thisREST.sh
 
 \"\#!/bin/bash
 
+${loadLCG}
+
 \# check active shell by checking for existence of _VERSION variable
 if [[ -n \\\"\\\${BASH_VERSION}\\\" ]]; then
     thisdir=\\\$(cd \\\$(dirname \\\${BASH_ARGV[0]}); pwd)
diff --git a/data/distributions/CosmicsCry.root b/data/distributions/CosmicsCry.root
new file mode 100644
index 0000000000000000000000000000000000000000..7f6dace145e326e0a81accd94a2e8480bd8bfa56
Binary files /dev/null and b/data/distributions/CosmicsCry.root differ
diff --git a/data/distributions/Fe55.root b/data/distributions/Fe55.root
new file mode 100644
index 0000000000000000000000000000000000000000..94b2d0150d56b9d965bc9cbede01ca971314e714
Binary files /dev/null and b/data/distributions/Fe55.root differ
diff --git a/doc/doxygen/images/component_hitmap.png b/doc/doxygen/images/component_hitmap.png
new file mode 100644
index 0000000000000000000000000000000000000000..ba18c0a1c3e3d695772ff8ce77ba0eff67e57fa4
Binary files /dev/null and b/doc/doxygen/images/component_hitmap.png differ
diff --git a/doc/doxygen/images/component_spectra.png b/doc/doxygen/images/component_spectra.png
new file mode 100644
index 0000000000000000000000000000000000000000..d206ced4e644112ef658e66694a236f1fadda6b8
Binary files /dev/null and b/doc/doxygen/images/component_spectra.png differ
diff --git a/doc/doxygen/images/radialstrippedmask.png b/doc/doxygen/images/radialstrippedmask.png
new file mode 100644
index 0000000000000000000000000000000000000000..b4c928a32a06f7ad04112a65a71e502017d410ce
Binary files /dev/null and b/doc/doxygen/images/radialstrippedmask.png differ
diff --git a/examples/masks.rml b/examples/masks.rml
index 15e8a9c2ba40d4522fd47b159e0b051ea155f2d8..da33120f037ae3df0e68ecd247bbafacb95ef92c 100644
--- a/examples/masks.rml
+++ b/examples/masks.rml
@@ -14,6 +14,17 @@
         <parameter name="stripsGap" value="12mm"/>
         <parameter name="stripsThickness" value="1.2mm"/>
     </TRestStrippedMask>
+	<TRestRadialStrippedMask name="radialStrips" verboseLevel="warning">
+		<parameter name="maskRadius" value="19.4cm" />
+		<parameter name="offset" value="(0,0)cm" />
+		<parameter name="rotationAngle" value="30degrees" />
+
+		<parameter name="initialRadius" value="5.4cm" />
+		<parameter name="internalRegionRadius" value="0cm" />
+
+		<parameter name="stripsAngle" value="60degrees" />
+		<parameter name="stripsThickness" value="0.7cm" />
+	</TRestRadialStrippedMask>
     <TRestRingsMask name="rings" verboseLevel="warning">
         <parameter name="maskRadius" value="9cm"/>
         <parameter name="offset" value="(3,3)mm"/>
diff --git a/macros/REST_AddComponentDataSet.C b/macros/REST_AddComponentDataSet.C
new file mode 100644
index 0000000000000000000000000000000000000000..f0c60044c59461b07fa8e870c818765190c8fbfa
--- /dev/null
+++ b/macros/REST_AddComponentDataSet.C
@@ -0,0 +1,46 @@
+#include "TRestComponent.h"
+#include "TRestTask.h"
+
+#ifndef RestTask_AddComponent
+#define RestTask_AddComponent
+
+//*******************************************************************************************************
+//*** Description: This macro will load from an RML the component chosen in the arguments and it
+//*** will write it inside the file given as outputFile
+//***
+//*** --------------
+//*** Usage: restManager AddComponentDataSet components.rml sectionName [outputFile] [componentName] [update]
+//***
+//*** Arguments description:
+//***
+//*** - cfgFile: The RML configuration file where the component definition can be found.
+//*** - sectionName: The section name used to select a component inside the RML file.
+//*** - outputFile: The file where the component is written, by default is components.root.
+//*** - componentName: This argument allows to change the component name stored in the output file.
+//***                  By default it will take the same value as section name.
+//*** - update: If disabled it will create a new file erasing any other previously added components.
+//***           It is enabled by default.
+//***
+//*******************************************************************************************************
+
+Int_t REST_AddComponentDataSet(std::string cfgFile, std::string sectionName,
+                               std::string outputFile = "components.root", std::string componentName = "",
+                               Bool_t update = true) {
+    TRestComponentDataSet comp(cfgFile.c_str(), sectionName.c_str());
+    comp.Initialize();
+
+    TFile* f;
+    if (update)
+        f = TFile::Open(outputFile.c_str(), "UPDATE");
+    else
+        f = TFile::Open(outputFile.c_str(), "RECREATE");
+
+    if (componentName == "") componentName = sectionName;
+
+    comp.Write(componentName.c_str());
+
+    f->Close();
+
+    return 0;
+}
+#endif
diff --git a/macros/REST_AddComponentFormula.C b/macros/REST_AddComponentFormula.C
new file mode 100644
index 0000000000000000000000000000000000000000..940866c3799c36ee58bd10eb1c9fd437f6e14abd
--- /dev/null
+++ b/macros/REST_AddComponentFormula.C
@@ -0,0 +1,46 @@
+#include "TRestComponent.h"
+#include "TRestTask.h"
+
+#ifndef RestTask_AddComponentFormula
+#define RestTask_AddComponentFormula
+
+//*******************************************************************************************************
+//*** Description: This macro will load from an RML the component chosen in the arguments and it
+//*** will write it inside the file given as outputFile
+//***
+//*** --------------
+//*** Usage: restManager AddComponentFormula components.rml sectionName [outputFile] [componentName] [update]
+//***
+//*** Arguments description:
+//***
+//*** - cfgFile: The RML configuration file where the component definition can be found.
+//*** - sectionName: The section name used to select a component inside the RML file.
+//*** - outputFile: The file where the component is written, by default is components.root.
+//*** - componentName: This argument allows to change the component name stored in the output file.
+//***                  By default it will take the same value as section name.
+//*** - update: If disabled it will create a new file erasing any other previously added components.
+//***           It is enabled by default.
+//***
+//*******************************************************************************************************
+
+Int_t REST_AddComponentFormula(std::string cfgFile, std::string sectionName,
+                               std::string outputFile = "components.root", std::string componentName = "",
+                               Bool_t update = true) {
+    TRestComponentFormula comp(cfgFile.c_str(), sectionName.c_str());
+    comp.Initialize();
+
+    TFile* f;
+    if (update)
+        f = TFile::Open(outputFile.c_str(), "UPDATE");
+    else
+        f = TFile::Open(outputFile.c_str(), "RECREATE");
+
+    if (componentName == "") componentName = sectionName;
+
+    comp.Write(componentName.c_str());
+
+    f->Close();
+
+    return 0;
+}
+#endif
diff --git a/macros/REST_CheckValidRuns.C b/macros/REST_CheckValidRuns.C
index 2031a7eb966ce4b6a704b256f462dd096f3825bd..18592be6ec46f2bbaa24d10a03bce732ae368881 100644
--- a/macros/REST_CheckValidRuns.C
+++ b/macros/REST_CheckValidRuns.C
@@ -37,15 +37,14 @@ namespace fs = std::filesystem;
 //*** CAUTION: Be aware that any non-REST file in the list will be removed if you use purge=1
 //***
 //*******************************************************************************************************
-Int_t REST_CheckValidRuns(TString namePattern, Bool_t purge = false) {
+Int_t REST_CheckValidRuns(std::string namePattern, Bool_t purge = false) {
     TGeoManager::SetVerboseLevel(0);
 
     vector<std::string> filesNotWellClosed;
 
     TRestStringOutput RESTLog;
 
-    string a = TRestTools::Execute((string)("ls -d -1 " + namePattern));
-    vector<string> b = Split(a, "\n");
+    std::vector<std::string> b = TRestTools::GetFilesMatchingPattern(namePattern, true);
 
     Double_t totalTime = 0;
     int cont = 0;
@@ -76,11 +75,12 @@ Int_t REST_CheckValidRuns(TString namePattern, Bool_t purge = false) {
         }
 
         RESTLog << "Run time (hours) : " << run->GetRunLength() / 3600. << RESTendl;
-        if (run->GetRunLength() > 0) totalTime += run->GetRunLength() / 3600.;
+        RESTLog << "Entries : " << run->GetEntries() << RESTendl;
 
-        if (run->GetEndTimestamp() == 0 || run->GetRunLength() < 0) {
+        if (run->GetEndTimestamp() == 0 || run->GetRunLength() < 0 || run->GetEntries() == 0) {
             filesNotWellClosed.push_back(filename);
-        }
+        } else if (run->GetRunLength() > 0)
+            totalTime += run->GetRunLength() / 3600.;
 
         delete run;
 
@@ -100,7 +100,6 @@ Int_t REST_CheckValidRuns(TString namePattern, Bool_t purge = false) {
         if (purge) {
             RESTLog << "---------------------------" << RESTendl;
             RESTLog << "The files have been removed" << RESTendl;
-            RESTLog << "---------------------------" << RESTendl;
         }
     }
 
diff --git a/macros/REST_ListMacros.C b/macros/REST_ListMacros.C
index 3059259af92f836238723f0d68c4790394e7605a..4333dead861a5aff3539d02f5154805d435954cf 100644
--- a/macros/REST_ListMacros.C
+++ b/macros/REST_ListMacros.C
@@ -13,40 +13,21 @@ using namespace std;
 //*** Lists all the official macros together with its documentation
 //***
 //*******************************************************************************************************
-Int_t REST_ListMacros() {
+Int_t REST_ListMacros(int details = 0, std::string onlyThisMacro = "") {
     string macrosPath = (string)getenv("REST_PATH") + "/macros";
     vector<string> directories = TRestTools::GetSubdirectories(macrosPath);
 
-    cout << "Directory : " << macrosPath << endl;
+    cout << endl;
+    cout << "Entering directory : " << macrosPath << endl;
     vector<string> main_files = TRestTools::GetFilesMatchingPattern(macrosPath + "/REST_*.C");
-    for (int m = 0; m < main_files.size(); m++) {
-        cout << "     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
-        cout << "     ++   "
-             << " Macro : " << TRestTools::SeparatePathAndName(main_files[m]).second << endl;
-        std::ifstream t(main_files[m]);
-        std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
-
-        std::vector<string> lines = REST_StringHelper::Split(str, "\n");
-
-        cout << "     ++   " << endl;
-        for (int l = 0; l < lines.size(); l++)
-            if (lines[l].find("//*** ") != string::npos)
-                cout << "     ++   " << lines[l].substr(6, -1) << endl;
-        cout << "     ++   " << endl;
-    }
-    cout << "     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
-
-    for (int n = 0; n < directories.size(); n++) {
-        cout << "Directory : " << directories[n] << endl;
-        if (directories[n].find("pipeline") != string::npos) continue;
-        if (directories[n].find("/macros/mac/") != string::npos) continue;
-        vector<string> files = TRestTools::GetFilesMatchingPattern(directories[n] + "/REST_*.C");
-
-        for (int m = 0; m < files.size(); m++) {
+    if (details) {
+        for (int m = 0; m < main_files.size(); m++) {
+            std::string macro = TRestTools::SeparatePathAndName(main_files[m]).second;
+            if (!onlyThisMacro.empty() && onlyThisMacro != macro) continue;
             cout << "     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
             cout << "     ++   "
-                 << " Macro : " << TRestTools::SeparatePathAndName(files[m]).second << endl;
-            std::ifstream t(files[m]);
+                 << " Macro : " << macro << endl;
+            std::ifstream t(main_files[m]);
             std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
 
             std::vector<string> lines = REST_StringHelper::Split(str, "\n");
@@ -58,6 +39,53 @@ Int_t REST_ListMacros() {
             cout << "     ++   " << endl;
         }
         cout << "     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
+    } else {
+        for (int m = 0; m < main_files.size(); m++) {
+            cout << " + " << TRestTools::SeparatePathAndName(main_files[m]).second << endl;
+        }
+    }
+
+    for (int n = 0; n < directories.size(); n++) {
+        cout << endl;
+        cout << "Entering directory : " << directories[n] << endl;
+        if (directories[n].find("pipeline") != string::npos) continue;
+        if (directories[n].find("/macros/mac/") != string::npos) continue;
+        vector<string> files = TRestTools::GetFilesMatchingPattern(directories[n] + "/REST_*.C");
+
+        if (details) {
+            for (int m = 0; m < files.size(); m++) {
+                std::string macro = TRestTools::SeparatePathAndName(files[m]).second;
+                if (!onlyThisMacro.empty() && onlyThisMacro != macro) continue;
+                cout << "     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
+                     << endl;
+                cout << "     ++    Macro : " << macro << endl;
+                std::ifstream t(files[m]);
+                std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
+
+                std::vector<string> lines = REST_StringHelper::Split(str, "\n");
+
+                cout << "     ++   " << endl;
+                for (int l = 0; l < lines.size(); l++)
+                    if (lines[l].find("//*** ") != string::npos)
+                        cout << "     ++   " << lines[l].substr(6, -1) << endl;
+                cout << "     ++   " << endl;
+            }
+            cout << "     ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
+        } else {
+            for (int m = 0; m < files.size(); m++) {
+                cout << " + " << TRestTools::SeparatePathAndName(files[m]).second << endl;
+            }
+            std::cout << std::endl;
+            std::cout << " ------- " << std::endl;
+            std::cout << "IMPORTANT. To get a more detailed macro documentation enable the details argument:"
+                      << std::endl;
+            std::cout << "Execute: restListMacros 1" << std::endl;
+            std::cout << "OR if you want to show only the documentation of a given macro, add the macro.C "
+                         "filename as argument"
+                      << std::endl;
+            std::cout << "Execute: restListMacros 1 REST_CheckValidRuns.C" << std::endl;
+            std::cout << " ------- " << std::endl;
+        }
     }
     return 0;
 }
diff --git a/macros/REST_OpenInputFile.C b/macros/REST_OpenInputFile.C
index 3087791c229c46aa8918860dd204963ca6b153b6..0f1af5cd6f14e97c571df5883208cb0499f96e01 100644
--- a/macros/REST_OpenInputFile.C
+++ b/macros/REST_OpenInputFile.C
@@ -46,7 +46,8 @@ void REST_OpenInputFile(const std::string& fileName) {
         printf("\n%s\n", " - dSet->PrintMetadata()");
         printf("%s\n", " - dSet->GetDataFrame().GetColumnNames()");
         printf("%s\n\n", " - dSet->GetTree()->GetEntries()");
-        printf("%s\n\n", " - dSet->GetDataFrame().Display({\"colName1,colName2\"})->Print()");
+        printf("%s\n", " - dSet->GetDataFrame().Display(\"\")->Print()");
+        printf("%s\n\n", " - dSet->GetDataFrame().Display({\"colName1\", \"colName2\"})->Print()");
         if (dSet) delete dSet;
         dSet = new TRestDataSet();
         dSet->EnableMultiThreading(false);
diff --git a/pipeline/panda-x/P3AutoChain.rml b/pipeline/panda-x/P3AutoChain.rml
index 75327d268054030b33359318d8aaee10cbf1d23d..e4b5043914f09a2aa2591406e9399a77fff83838 100644
--- a/pipeline/panda-x/P3AutoChain.rml
+++ b/pipeline/panda-x/P3AutoChain.rml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
-
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <!--In this example, when launching the analysis, we are doing following operations inside TRestManager:
 1. Initialize TRestRun
 1.1 set input and output file for TRestRun(here input and output is in "globals")
@@ -13,25 +12,17 @@
 2.4.1 add additional observables in the process
 3. Add a task to call TRestProcessRunner to run
 -->
-
 <TRestManager name="CoBoDataAnalysis" title="Example" verboseLevel="info">
-
-    <TRestRun name="SJTU_Proto">
-        <parameter name="runNumber" value="auto"/>//change this value to "auto" to enable database
-        <parameter name="inputFileName" value="auto"/>
-    </TRestRun>
-
+    <TRestRun name="SJTU_Proto"><parameter name="runNumber" value="auto"/>//change this value to "auto" to enable database
+        <parameter name="inputFileName" value="auto"/></TRestRun>
     <TRestProcessRunner name="Processor" verboseLevel="info">
         <parameter name="eventsToProcess" value="0"/>
         <parameter name="threadNumber" value="2"/>
-
         <parameter name="inputAnalysisStorage" value="on"/>
         <parameter name="inputEventStorage" value="off"/>
         <parameter name="outputEventStorage" value="on"/>
-
         <addProcess type="TRestRawMultiCoBoAsAdToSignalProcess" name="virtualDAQ" value="ON"/>
-        <addProcess type="TRestRawSignalAnalysisProcess" name="sAna" value="ON">
-            <parameter name="pointsOverThreshold" value="3"/>
+        <addProcess type="TRestRawSignalAnalysisProcess" name="sAna" value="ON"><parameter name="pointsOverThreshold" value="3"/>
             /// We define all observables except MinValue because it is not yet in validation chain
             <observable name="BaseLineMean" value="ON"/>
             <observable name="BaseLineSigmaMean" value="ON"/>
@@ -61,31 +52,20 @@
             <parameter name="zeroSuppression" value="true"/>
             <parameter name="nPointsOverThreshold" value="3"/>
         </addProcess>
-
         <addProcess type="TRestRealTimeDrawingProcess" name="rD" value="ON" drawInterval="1000">
             <TRestAnalysisPlot name="TriggerRatePlot" previewPlot="false">
                 <canvas size="(1000,800)" save="TriggerRate.png"/>
-                <plot name="TriggerRate" title="Trigger Rate" xlabel="Seconds From Start" ylabel="Counts" value="ON"
-                      stats="ON">
+                <plot name="TriggerRate" title="Trigger Rate" xlabel="Seconds From Start" ylabel="Counts" value="ON" stats="ON">
                     <variable name="rateAna_SecondsFromStart" range="auto" nbins="100"/>
                 </plot>
             </TRestAnalysisPlot>
         </addProcess>
-
-
     </TRestProcessRunner>
-
     <addTask type="processEvents" value="ON"/>
-
-
     <globals>
         <searchPath value="$ENV{REST_INPUTDATA}/definitions/"/>
         <parameter name="mainDataPath" value=""/>
-
         <parameter name="pointThreshold" value="3"/>
     </globals>
-
 </TRestManager>
-
-
-        <!--parameter here is accessible to all the classes-->
+<!--parameter here is accessible to all the classes-->
diff --git a/projects/README.md b/projects/README.md
index fcfcb2674130d6625e94c6826463bded0841b207..620fc62339a31b8b26966933b6dc3f174ab7eb10 100644
--- a/projects/README.md
+++ b/projects/README.md
@@ -10,4 +10,4 @@ List of projects included:
 
 ----
 
-**⚠ WARNING: REST is under continous development.** This README is offered to you by the REST community. Your HELP is needed to keep this file up to date. You are very welcome to contribute fixing typos, updating information or adding new contributions to REST. See also our [Contribution Guide](https://lfna.unizar.es/rest-development/REST_v2/-/blob/master/CONTRIBUTING.md).
+**⚠ WARNING: REST is under continuous development.** This README is offered to you by the REST community. Your HELP is needed to keep this file up to date. You are very welcome to contribute fixing typos, updating information or adding new contributions to REST. See also our [Contribution Guide](https://lfna.unizar.es/rest-development/REST_v2/-/blob/master/CONTRIBUTING.md).
diff --git a/scripts/generateVersionHeader.py b/scripts/generateVersionHeader.py
index 29cc2171210139acf9776b7dd94f3408a9506d8d..1d85a9b388b14c4295e19038d73647b4a447e8ed 100755
--- a/scripts/generateVersionHeader.py
+++ b/scripts/generateVersionHeader.py
@@ -159,29 +159,29 @@ print(
 )
 print("\n")
 print(
-    "Once your PR has been accepted and merged, you should generate a new Git tag at the master branch.\n"
-)
-print(
-    "-----> git tag -a v"
-    + str(a)
-    + "."
-    + str(b)
-    + "."
-    + str(c)
-    + ' -m "Update to version v'
-    + str(a)
-    + "."
-    + str(b)
-    + "."
-    + str(c)
-    + '"\n'
-)
-print(
-    "And push the changes to repository. You should also push your branch to GitHub if you have not already.\n"
-)
-print("-----> git push origin v" + str(a) + "." + str(b) + "." + str(c) + "\n")
-print(
-    "IMPORTANT. Summarize the changes in the tag generated at the Gitlab repository website.\n"
+    "Once your PR has been accepted and merged, you should generate a new Git tag and RELEASE at the master branch.\n"
 )
+# print(
+#    "-----> git tag -a v"
+#    + str(a)
+#    + "."
+#    + str(b)
+#    + "."
+#    + str(c)
+#    + ' -m "Update to version v'
+#    + str(a)
+#    + "."
+#    + str(b)
+#    + "."
+#    + str(c)
+#    + '"\n'
+# )
+# print(
+#    "And push the changes to repository. You should also push your branch to GitHub if you have not already.\n"
+# )
+# print("-----> git push origin v" + str(a) + "." + str(b) + "." + str(c) + "\n")
+# print(
+#    "IMPORTANT. Summarize the changes in the tag generated at the Gitlab repository website.\n"
+# )
 
 exit(0)
diff --git a/source/bin/restRoot.cxx b/source/bin/restRoot.cxx
index 17e2a1e0ae514a175d2322c3c2bfdb050f2da2f1..758516f476186e871c5bcd3a71da8e4ff03282df 100644
--- a/source/bin/restRoot.cxx
+++ b/source/bin/restRoot.cxx
@@ -61,7 +61,7 @@ int main(int argc, char* argv[]) {
                     printf("\n");
                     printf(" restRoot --m [0,1]\n");
                     printf("\n");
-                    printf(" Option 0 will disable macro loading. Option 1 is the default.\n");
+                    printf(" Option 0 will disable macro loading. Option 0 is the default.\n");
                     printf("\n");
                     exit(0);
             }
@@ -81,7 +81,18 @@ int main(int argc, char* argv[]) {
     gROOT->ProcessLine("#include <TRestPhysics.h>");
     gROOT->ProcessLine("#include <TRestSystemOfUnits.h>");
     if (loadMacros) {
-        if (!silent) printf("= Loading macros ...\n");
+        if (!silent) {
+            printf("= Loading macros ...\n");
+            printf(
+                "= HINT. Loading all macros may require a long time till you get access to the interactive "
+                "shell\n");
+            printf("= HINT. You may use `restListMacros` outside `restRoot` to check the available macros\n");
+            printf(
+                "= HINT. Then, you may execute the macro externally by using: `restManager MacroName "
+                "[ARGUMENTS]\n");
+            printf("= HINT. `MacroName` is the name of the macro after removing the macro name header\n");
+            printf("= HINT. E.g. REST_Detector_XYZ(arg1,arg2) may be called as: restManager XYZ arg1 arg2\n");
+        }
         vector<string> macroFiles;
         const vector<string> patterns = {
             REST_PATH + "/macros/REST_*.C",   // framework
diff --git a/source/framework/CMakeLists.txt b/source/framework/CMakeLists.txt
index 731c59cd70f02ec32e5d776875af8ad4ab45cabe..b6440e4dc7661cf7a78cf12d5adfea642398c661 100644
--- a/source/framework/CMakeLists.txt
+++ b/source/framework/CMakeLists.txt
@@ -4,7 +4,7 @@ link_libraries("-fPIC")
 
 add_subdirectory(external)
 
-set(contents external/tinyxml tools core analysis masks)
+set(contents external/tinyxml tools core analysis masks sensitivity)
 
 file(GLOB_RECURSE addon_src "tiny*cpp" "startup.cpp")
 
diff --git a/source/framework/analysis/inc/TRestDataSetGainMap.h b/source/framework/analysis/inc/TRestDataSetGainMap.h
index 50000a01bf750d883ed7ad8c405d06fe72c76703..617c5f0aa0e10a73b3db0a2146671085efe2e820 100644
--- a/source/framework/analysis/inc/TRestDataSetGainMap.h
+++ b/source/framework/analysis/inc/TRestDataSetGainMap.h
@@ -33,6 +33,7 @@
 #include <TTree.h>
 #include <TVector2.h>
 
+#include "TRestCut.h"
 #include "TRestDataSet.h"
 #include "TRestMetadata.h"
 
@@ -42,13 +43,26 @@ class TRestDataSetGainMap : public TRestMetadata {
     class Module;
 
    private:
-    std::string fObservable = "";          //"rawAna_ThresholdIntegral"; //<
-    std::string fSpatialObservableX = "";  //"hitsAna_xMean"; //<
-    std::string fSpatialObservableY = "";  //"hitsAna_yMean"; //<
+    /// Observable that will be used to calculate the gain map
+    std::string fObservable = "";  //<
 
-    std::vector<Module> fModulesCal = {};
-    std::string fCalibFileName = "";
-    std::string fOutputFileName = "";
+    /// Observable that will be used to segmentize the gain map in the x direction
+    std::string fSpatialObservableX = "";  //<
+
+    /// Observable that will be used to segmentize the gain map in the y direction
+    std::string fSpatialObservableY = "";  //<
+
+    /// List of modules
+    std::vector<Module> fModulesCal = {};  //<
+
+    /// Name of the file that contains the calibration data
+    std::string fCalibFileName = "";  //<
+
+    /// Name of the file where the gain map was (or will be) exported
+    std::string fOutputFileName = "";  //<
+
+    /// Cut to be applied to the calibration data
+    TRestCut* fCut = nullptr;  //<
 
     void Initialize() override;
     void InitFromConfigFile() override;
@@ -69,14 +83,18 @@ class TRestDataSetGainMap : public TRestMetadata {
     std::string GetObservable() const { return fObservable; }
     std::string GetSpatialObservableX() const { return fSpatialObservableX; }
     std::string GetSpatialObservableY() const { return fSpatialObservableY; }
+    TRestCut* GetCut() const { return fCut; }
 
+    Module* GetModule(const size_t index = 0);
     Module* GetModule(const int planeID, const int moduleID);
     double GetSlopeParameter(const int planeID, const int moduleID, const double x, const double y);
     double GetInterceptParameter(const int planeID, const int moduleID, const double x, const double y);
+    double GetSlopeParameterFullSpc(const int planeID, const int moduleID);
+    double GetInterceptParameterFullSpc(const int planeID, const int moduleID);
 
     void SetCalibrationFileName(const std::string& fileName) { fCalibFileName = fileName; }
     void SetOutputFileName(const std::string& fileName) { fOutputFileName = fileName; }
-    void SetModuleCalibration(const Module& moduleCal);
+    void SetModule(const Module& moduleCal);
     void SetObservable(const std::string& observable) { fObservable = observable; }
     void SetSpatialObservableX(const std::string& spatialObservableX) {
         fSpatialObservableX = spatialObservableX;
@@ -84,52 +102,106 @@ class TRestDataSetGainMap : public TRestMetadata {
     void SetSpatialObservableY(const std::string& spatialObservableY) {
         fSpatialObservableY = spatialObservableY;
     }
+    void SetCut(TRestCut* cut) { fCut = cut; }
 
     void Import(const std::string& fileName);
     void Export(const std::string& fileName = "");
 
     TRestDataSetGainMap& operator=(TRestDataSetGainMap& src);
 
-   public:
     void PrintMetadata() override;
 
     void GenerateGainMap();
-    void CalibrateDataSet(const std::string& dataSetFileName, std::string outputFileName = "");
+    void CalibrateDataSet(const std::string& dataSetFileName, std::string outputFileName = "",
+                          std::vector<std::string> excludeColumns = {});
 
     TRestDataSetGainMap();
     TRestDataSetGainMap(const char* configFilename, std::string name = "");
     ~TRestDataSetGainMap();
 
-    ClassDefOverride(TRestDataSetGainMap, 1);
+    ClassDefOverride(TRestDataSetGainMap, 2);
 
     class Module {
        private:
-        const TRestDataSetGainMap* p = nullptr;  //<! Pointer to the parent class
-       public:                                   /// Members that will be written to the ROOT file.
-        Int_t fPlaneId = -1;                     //< // Plane ID
-        Int_t fModuleId = -1;                    //< // Module ID
-
-        std::vector<double> fEnergyPeaks = {};
-        std::vector<TVector2> fRangePeaks = {};  //{TVector2(230000, 650000), TVector2(40000, 230000)};
-        TVector2 fCalibRange = TVector2(0, 0);   //< // Calibration range
-        Int_t fNBins = 100;                      //< // Number of bins for the spectrum histograms
-        std::string fDefinitionCut = "";         //"TREXsides_tagId == 2"; //<
-
-        Int_t fNumberOfSegmentsX = 1;                   //<
-        Int_t fNumberOfSegmentsY = 1;                   //<
-        TVector2 fReadoutRange = TVector2(-1, 246.24);  //< // Readout dimensions
-        std::set<double> fSplitX = {};                  //<
-        std::set<double> fSplitY = {};                  //<
-
-        std::string fDataSetFileName = "";  //< // File name for the calibration dataset
-
-        std::vector<std::vector<double>> fSlope = {};      //<
+        /// Pointer to the parent class
+        const TRestDataSetGainMap* p = nullptr;  //<!
+
+        std::pair<double, double> FitPeaks(TH1F* hSeg, TGraph* gr);
+        std::pair<double, double> UpdateCalibrationFits(TH1F* hSeg, TGraph* gr);
+
+       public:
+        /// Plane ID (unique identifier). Although it is not linked to any TRestDetectorReadout it is
+        /// recommended to use the same.
+        Int_t fPlaneId = -1;  //<
+
+        // Module ID (unique identifier). Although it is not linked to any TRestDetectorReadout it is
+        // recommended to use the same.
+        Int_t fModuleId = -1;  //<
+
+        /// Energy of the peaks to be used for the calibration.
+        std::vector<double> fEnergyPeaks = {};  //<
+
+        /// Range of the peaks to be used for the calibration. If empty it will be automatically calculated.
+        std::vector<TVector2> fRangePeaks = {};  //<
+
+        /// Calibration range. If fCalibRange.X()>=fCalibRange.Y() the range will be automatically calculated.
+        TVector2 fCalibRange = TVector2(0, 0);  //<
+
+        /// Number of bins for the spectrum histograms.
+        Int_t fNBins = 100;  //<
+
+        /// Cut that defines which events are from this module.
+        std::string fDefinitionCut = "";  //<
+
+        /// Number of segments in the x direction.
+        Int_t fNumberOfSegmentsX = 1;  //<
+
+        /// Number of segments in the y direction.
+        Int_t fNumberOfSegmentsY = 1;  //<
+
+        /// Readout dimensions
+        TVector2 fReadoutRange = TVector2(-1, 246.24);  //<
+
+        /// Split points in the x direction.
+        std::set<double> fSplitX = {};  //<
+
+        /// Split points in the y direction.
+        std::set<double> fSplitY = {};  //<
+
+        /// Name of the file that contains the calibration data. If empty, it will use its parent
+        /// TRestDataSetGainMap::fCalibFileName.
+        std::string fDataSetFileName = "";  //<
+
+        /// Array containing the slope of the linear fit for each segment.
+        std::vector<std::vector<double>> fSlope = {};  //<
+
+        /// Slope of the calibration linear fit of whole module
+        double fFullSlope = 0;  //<
+
+        /// Intercept of the calibration linear fit of whole module
+        double fFullIntercept = 0;  //<
+
+        /// Array containing the intercept of the linear fit for each segment.
         std::vector<std::vector<double>> fIntercept = {};  //<
 
-        bool fZeroPoint = false;  //< Zero point will be automatically added if there are less than 2 peaks
-        bool fAutoRangePeaks = true;  //< Automatic range peaks
-        std::vector<std::vector<TH1F*>> fSegSpectra = {};
-        std::vector<std::vector<TGraph*>> fSegLinearFit = {};
+        /// Add zero point to the calibration linear fit. Zero point will be automatically added if there are
+        /// less than 2 points.
+        bool fZeroPoint = false;  //<
+
+        /// Automatic range for the peaks fitting. See GenerateGainMap() for more information of the logic.
+        bool fAutoRangePeaks = true;  //<
+
+        /// Array containing the observable spectrum for each segment.
+        std::vector<std::vector<TH1F*>> fSegSpectra = {};  //<
+
+        /// Array containing the calibration linear fit for each segment.
+        std::vector<std::vector<TGraph*>> fSegLinearFit = {};  //<
+
+        /// Spectrum of the observable for the whole module.
+        TH1F* fFullSpectrum = nullptr;  //<
+
+        /// Calibration linear fit for the whole module.
+        TGraph* fFullLinearFit = nullptr;  //<
 
        public:
         void AddPeak(const double& energyPeak, const TVector2& rangePeak = TVector2(0, 0)) {
@@ -139,9 +211,12 @@ class TRestDataSetGainMap : public TRestMetadata {
 
         void LoadConfigFromTiXmlElement(const TiXmlElement* module);
 
+        TRestDataSetGainMap* GetParent() const { return const_cast<TRestDataSetGainMap*>(p); }
         std::pair<int, int> GetIndexMatrix(const double x, const double y) const;
         double GetSlope(const double x, const double y) const;
         double GetIntercept(const double x, const double y) const;
+        double GetSlopeFullSpc() const { return fFullSlope; };
+        double GetInterceptFullSpc() const { return fFullIntercept; };
 
         Int_t GetPlaneId() const { return fPlaneId; }
         Int_t GetModuleId() const { return fModuleId; }
@@ -155,24 +230,27 @@ class TRestDataSetGainMap : public TRestMetadata {
         std::set<double> GetSplitX() const { return fSplitX; }
         std::set<double> GetSplitY() const { return fSplitY; }
         std::string GetDataSetFileName() const { return fDataSetFileName; }
-        TVector2 GetReadoutRangeVar() const { return fReadoutRange; }
+        TVector2 GetReadoutRange() const { return fReadoutRange; }
 
         void DrawSpectrum(const bool drawFits = true, const int color = -1, TCanvas* c = nullptr);
         void DrawSpectrum(const TVector2& position, bool drawFits = true, int color = -1,
                           TCanvas* c = nullptr);
         void DrawSpectrum(const int index_x, const int index_y, bool drawFits = true, int color = -1,
                           TCanvas* c = nullptr);
-        void DrawFullSpectrum();
+        void DrawFullSpectrum(const bool drawFits = true, const int color = -1, TCanvas* c = nullptr);
 
-        void DrawLinearFit();
+        void DrawLinearFit(TCanvas* c = nullptr);
         void DrawLinearFit(const TVector2& position, TCanvas* c = nullptr);
         void DrawLinearFit(const int index_x, const int index_y, TCanvas* c = nullptr);
 
-        void DrawGainMap(const int peakNumber = 0);
+        void DrawGainMap(const int peakNumber = 0, const bool fullModuleAsRef = true);
 
         void Refit(const TVector2& position, const double energy, const TVector2& range);
         void Refit(const size_t x, const size_t y, const size_t peakNumber, const TVector2& range);
+        void RefitFullSpc(const double energy, const TVector2& range);
+        void RefitFullSpc(const size_t peakNumber, const TVector2& range);
         void UpdateCalibrationFits(const size_t x, const size_t y);
+        void UpdateCalibrationFitsFullSpc();
 
         void SetPlaneId(const Int_t& planeId) { fPlaneId = planeId; }
         void SetModuleId(const Int_t& moduleId) { fModuleId = moduleId; }
diff --git a/source/framework/analysis/inc/TRestEventSelectionProcess.h b/source/framework/analysis/inc/TRestEventSelectionProcess.h
index 1f762885ac961903002bc291111c8cf1927975b2..ddff7927fe9d46a3d8dc565106e285335ee1656d 100644
--- a/source/framework/analysis/inc/TRestEventSelectionProcess.h
+++ b/source/framework/analysis/inc/TRestEventSelectionProcess.h
@@ -24,17 +24,16 @@
 #define RestProcess_TRestEventSelectionProcess
 
 #include <TH1D.h>
+#include <TRestEventProcess.h>
 
 #include <iostream>
 
-#include "TRestEventProcess.h"
-
 //! A template process to serve as an example to create new TRestRawSignalEventProcess
 class TRestEventSelectionProcess : public TRestEventProcess {
    private:
     TRestEvent* fEvent;  //!
-    std::string fFileWithIDs = "";
-    std::string fConditions = "";
+    std::string fFileWithIDs;
+    std::string fConditions;
     std::vector<Int_t> fList;
 
     /// A list with the event ids that have been selected.
diff --git a/source/framework/analysis/inc/TRestEventTimeSelectionProcess.h b/source/framework/analysis/inc/TRestEventTimeSelectionProcess.h
new file mode 100644
index 0000000000000000000000000000000000000000..98604e53b17939e4818bac8f03caaeabbf7b6650
--- /dev/null
+++ b/source/framework/analysis/inc/TRestEventTimeSelectionProcess.h
@@ -0,0 +1,104 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see http://gifna.unizar.es/trex                  *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see http://www.gnu.org/licenses/.                             *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef RestProcess_TRestEventTimeSelectionProcess
+#define RestProcess_TRestEventTimeSelectionProcess
+
+#include <TRestEventProcess.h>
+
+#include <iostream>
+
+class TRestEventTimeSelectionProcess : public TRestEventProcess {
+   private:
+    TRestEvent* fEvent;  //!
+    std::string fFileWithTimes;
+    Bool_t fIsActiveTime;
+    Char_t fDelimiter;
+    Long_t fTimeOffsetInSeconds;
+    Long_t fTimeStartMarginInSeconds;
+    Long_t fTimeEndMarginInSeconds;
+    std::vector<std::pair<std::string, std::string>> fStartEndTimes;
+
+    /// Information about the events processed
+
+    Int_t fNEventsRejected;
+    Int_t fNEventsSelected;
+    Double_t fTotalTimeInSeconds;
+
+    void Initialize() override;
+
+   protected:
+   public:
+    RESTValue GetInputEvent() const override { return fEvent; }
+    RESTValue GetOutputEvent() const override { return fEvent; }
+
+    void InitProcess() override;
+    TRestEvent* ProcessEvent(TRestEvent* inputEvent) override;
+    void EndProcess() override;
+
+    void PrintMetadata() override;
+
+    // Constructor
+    TRestEventTimeSelectionProcess();
+    // Destructor
+    ~TRestEventTimeSelectionProcess() {}
+
+    const char* GetProcessName() const override { return "EventTimeSelectionProcess"; }
+
+    std::string GetFileWithTimes() const { return fFileWithTimes; }
+
+    Bool_t GetIsActiveTime() const { return fIsActiveTime; }
+    Char_t GetDelimiter() const { return fDelimiter; }
+
+    std::vector<std::pair<std::string, std::string>> GetStartEndTimes() const { return fStartEndTimes; }
+    std::string GetTimeStampCut(std::string timeStampObsName = "timeStamp", Bool_t useOffset = true,
+                                Bool_t useMargins = true, Int_t nTimes = -1);
+    Int_t GetNEventsRejected() const { return fNEventsRejected; }
+    Int_t GetNEventsSelected() const { return fNEventsSelected; }
+    Double_t GetTotalTimeInSeconds() const { return fTotalTimeInSeconds; }
+    Long_t GetTimeOffsetInSeconds() const { return fTimeOffsetInSeconds; }
+    Long_t GetTimeStartMarginInSeconds() const { return fTimeStartMarginInSeconds; }
+    Long_t GetTimeEndMarginInSeconds() const { return fTimeEndMarginInSeconds; }
+
+    Double_t CalculateTotalTimeInSeconds();
+    static std::vector<std::pair<std::string, std::string>> ReadFileWithTimes(std::string fileWithTimes,
+                                                                              Char_t delimiter = ',');
+
+    void SetAsActiveTime() { fIsActiveTime = true; }
+    void SetAsDeadTime() { fIsActiveTime = false; }
+    void SetFileWithTimes(const std::string& fileWithTimes) { fFileWithTimes = fileWithTimes; }
+    void SetIsActiveTime(Bool_t isActiveTime) { fIsActiveTime = isActiveTime; }
+    void SetDelimiter(Char_t delimiter) { fDelimiter = delimiter; }
+    void SetStartEndTimes(const std::vector<std::pair<std::string, std::string>>& startEndTimes) {
+        fStartEndTimes = startEndTimes;
+    }
+    void SetTimeOffsetInSeconds(Long_t timeOffsetInSeconds) { fTimeOffsetInSeconds = timeOffsetInSeconds; }
+    void SetTimeStartMarginInSeconds(Long_t timeStartMarginInSeconds) {
+        fTimeStartMarginInSeconds = timeStartMarginInSeconds;
+    }
+    void SetTimeEndMarginInSeconds(Long_t timeEndMarginInSeconds) {
+        fTimeEndMarginInSeconds = timeEndMarginInSeconds;
+    }
+
+    ClassDefOverride(TRestEventTimeSelectionProcess, 1);
+};
+#endif
diff --git a/source/framework/analysis/src/TRestDataSetCalibration.cxx b/source/framework/analysis/src/TRestDataSetCalibration.cxx
index d9869d29d27ee81e05eaf49c9835bab3c4dbd467..f6794f3a9849191265316c7649bd47b94fe111ba 100644
--- a/source/framework/analysis/src/TRestDataSetCalibration.cxx
+++ b/source/framework/analysis/src/TRestDataSetCalibration.cxx
@@ -192,7 +192,7 @@ void TRestDataSetCalibration::Calibrate() {
 
     if (fCalibFile.empty()) {
         auto histo = dataSet.GetDataFrame().Histo1D(
-            {"spectrum", "spectrum", fNBins, fCalibRange.X(), fCalibRange.X()}, fCalObservable);
+            {"spectrum", "spectrum", fNBins, fCalibRange.X(), fCalibRange.Y()}, fCalObservable);
         spectrum = std::unique_ptr<TH1F>(static_cast<TH1F*>(histo->DrawClone()));
 
         // Get position of the maximum
diff --git a/source/framework/analysis/src/TRestDataSetGainMap.cxx b/source/framework/analysis/src/TRestDataSetGainMap.cxx
index 53a5fa0a0069a7ef1b66f6258e7acbf5230fc532..eab96f4247a75880a99791b191b172b923f1c4b5 100644
--- a/source/framework/analysis/src/TRestDataSetGainMap.cxx
+++ b/source/framework/analysis/src/TRestDataSetGainMap.cxx
@@ -186,6 +186,8 @@ void TRestDataSetGainMap::InitFromConfigFile() {
         moduleDefinition = GetNextElement(moduleDefinition);
     }
 
+    fCut = (TRestCut*)InstantiateChildMetadata("TRestCut");
+
     if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Debug) PrintMetadata();
 }
 
@@ -194,30 +196,38 @@ void TRestDataSetGainMap::InitFromConfigFile() {
 ///
 void TRestDataSetGainMap::GenerateGainMap() {
     for (auto& mod : fModulesCal) {
-        RESTInfo << "Calibrating plane " << mod.GetPlaneId() << " module " << mod.GetModuleId() << RESTendl;
+        RESTInfo << "Generating gain map of plane " << mod.GetPlaneId() << " module " << mod.GetModuleId()
+                 << RESTendl;
         mod.GenerateGainMap();
         if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) {
             mod.DrawSpectrum();
+            mod.DrawFullSpectrum();
             mod.DrawGainMap();
         }
     }
 }
 
 /////////////////////////////////////////////
-/// \brief Function to calibrate a dataset.
+/// \brief Function to calibrate a dataset with this gain map.
 ///
 /// \param dataSetFileName the name of the root file where the TRestDataSet to be calibrated is stored.
 /// \param outputFileName the name of the output (root) file where the calibrated TRestDataSet will be
-/// exported. If empty, the output file will be named as the input file with the suffix "_cc". E.g.
-/// "data/myDataSet.root" -> "data/myDataSet_cc.root".
+/// exported. If empty, the output file will be named as the input file plus the name of the
+/// TRestDataSetGainMap. E.g. "data/myDataSet.root" -> "data/myDataSet_<gmName>.root".
+/// \param excludeColumns a vector of strings with the names of the columns to be excluded from the
+/// output file. If empty, all columns will be included. If "all" is in the list, all columns will be
+/// excluded except the calibrated observable, the calibrated observable with no segmentation and
+/// the plane-module identifier (pmID).
 ///
-void TRestDataSetGainMap::CalibrateDataSet(const std::string& dataSetFileName, std::string outputFileName) {
+void TRestDataSetGainMap::CalibrateDataSet(const std::string& dataSetFileName, std::string outputFileName,
+                                           std::vector<std::string> excludeColumns) {
     if (fModulesCal.empty()) {
         RESTError << "TRestDataSetGainMap::CalibrateDataSet: No modules defined." << RESTendl;
         return;
     }
 
     TRestDataSet dataSet;
+    dataSet.EnableMultiThreading(true);
     dataSet.Import(dataSetFileName);
     auto dataFrame = dataSet.GetDataFrame();
 
@@ -253,18 +263,54 @@ void TRestDataSetGainMap::CalibrateDataSet(const std::string& dataSetFileName, s
     dataFrame = dataFrame.Define(calibObsName, calibrate,
                                  {fObservable, fSpatialObservableX, fSpatialObservableY, pmIDname});
 
+    // Define a new column with the calibrated observable for the whole module calibration
+    auto calibrateFullSpc = [this](double val, int pmID) {
+        for (auto& m : fModulesCal) {
+            if (pmID == m.GetPlaneId() * 10 + m.GetModuleId())
+                return m.GetSlopeFullSpc() * val + m.GetInterceptFullSpc();
+        }
+        // RESTError << "TRestDataSetGainMap::CalibrateDataSet: Module not found for pmID " << pmID <<
+        // RESTendl;
+        return std::numeric_limits<double>::quiet_NaN();
+    };
+    std::string calibObsNameFullSpc = (std::string)GetName() + "_";
+    calibObsNameFullSpc +=
+        GetObservable().erase(0, GetObservable().find("_") + 1);  // remove the "rawAna_" part
+    calibObsNameFullSpc += "_NoSegmentation";
+    dataFrame = dataFrame.Define(calibObsNameFullSpc, calibrateFullSpc, {fObservable, pmIDname});
+
     dataSet.SetDataFrame(dataFrame);
 
     // Format the output file name and export the dataSet
     if (outputFileName.empty()) outputFileName = dataSetFileName;
-
     if (outputFileName == dataSetFileName) {  // TRestDataSet cannot be overwritten
-        outputFileName = outputFileName.substr(0, outputFileName.find_last_of(".")) + "_cc.";
+        std::string gmName = GetName();
+        outputFileName = outputFileName.substr(0, outputFileName.find_last_of("."));  // remove extension
+        outputFileName += "_" + gmName;
         outputFileName += TRestTools::GetFileNameExtension(dataSetFileName);
     }
 
-    RESTInfo << "Exporting calibrated dataSet to " << outputFileName << RESTendl;
-    dataSet.Export(outputFileName);
+    // Export dataset. Exclude columns if requested.
+    std::set<std::string> excludeCol;  // vector with the explicit column names to be excluded
+    auto columns = dataSet.GetDataFrame().GetColumnNames();
+    // Get the columns to be excluded from the list of columns. It accepts wildcards "*" and "?"
+    for (auto& eC : excludeColumns) {
+        if (eC.find("*") != std::string::npos || eC.find("?") != std::string::npos) {
+            for (auto& c : columns)
+                if (MatchString(c, eC)) excludeCol.insert(c);
+        } else if (std::find(columns.begin(), columns.end(), eC) != columns.end())
+            excludeCol.insert(eC);
+    }
+    // Remove the calibObsName, calibObsNameFullSpc and pmIDname from the list of columns to be excluded
+    excludeCol.erase(calibObsName);
+    excludeCol.erase(calibObsNameFullSpc);
+    excludeCol.erase(pmIDname);
+
+    RESTDebug << "Excluding columns: ";
+    for (auto& c : excludeCol) RESTDebug << c << ", ";
+    RESTDebug << RESTendl;
+
+    dataSet.Export(outputFileName, std::vector<std::string>(excludeCol.begin(), excludeCol.end()));
 
     // Add this TRestDataSetGainMap metadata to the output file
     TFile* f = TFile::Open(outputFileName.c_str(), "UPDATE");
@@ -273,6 +319,21 @@ void TRestDataSetGainMap::CalibrateDataSet(const std::string& dataSetFileName, s
     delete f;
 }
 
+/////////////////////////////////////////////
+/// \brief Function to retrieve the module calibration by index. Default is 0.
+///
+///
+TRestDataSetGainMap::Module* TRestDataSetGainMap::GetModule(const size_t index) {
+    if (index < fModulesCal.size()) return &fModulesCal[index];
+
+    RESTError << "No ModuleCalibration with index " << index;
+    if (fModulesCal.empty())
+        RESTError << ". There are no modules defined." << RESTendl;
+    else
+        RESTError << ". Max index is " << fModulesCal.size() - 1 << RESTendl;
+    return nullptr;
+}
+
 /////////////////////////////////////////////
 /// \brief Function to retrieve the module calibration with planeID and moduleID
 ///
@@ -297,6 +358,16 @@ double TRestDataSetGainMap::GetSlopeParameter(const int planeID, const int modul
     return moduleCal->GetSlope(x, y);
 }
 
+/////////////////////////////////////////////
+/// \brief Function to get the slope parameter of the whole module with
+/// planeID and moduleID.
+///
+double TRestDataSetGainMap::GetSlopeParameterFullSpc(const int planeID, const int moduleID) {
+    Module* moduleCal = GetModule(planeID, moduleID);
+    if (moduleCal == nullptr) return 0;  // return numeric_limits<double>::quiet_NaN()
+    return moduleCal->GetSlopeFullSpc();
+}
+
 /////////////////////////////////////////////
 /// \brief Function to get the intercept parameter of the module with
 /// planeID and moduleID at physical position (x,y)
@@ -309,6 +380,16 @@ double TRestDataSetGainMap::GetInterceptParameter(const int planeID, const int m
     return moduleCal->GetIntercept(x, y);
 }
 
+/////////////////////////////////////////////
+/// \brief Function to get the intercept parameter of the whole module with
+/// planeID and moduleID.
+///
+double TRestDataSetGainMap::GetInterceptParameterFullSpc(const int planeID, const int moduleID) {
+    Module* moduleCal = GetModule(planeID, moduleID);
+    if (moduleCal == nullptr) return 0;  // return numeric_limits<double>::quiet_NaN()
+    return moduleCal->GetInterceptFullSpc();
+}
+
 /////////////////////////////////////////////
 /// \brief Function to get a list (set) of the plane IDs
 ///
@@ -346,6 +427,7 @@ TRestDataSetGainMap& TRestDataSetGainMap::operator=(TRestDataSetGainMap& src) {
     fObservable = src.GetObservable();
     fSpatialObservableX = src.GetSpatialObservableX();
     fSpatialObservableY = src.GetSpatialObservableY();
+    fCut = src.GetCut();
     fModulesCal.clear();
     for (auto pID : src.GetPlaneIDs())
         for (auto mID : src.GetModuleIDs(pID)) fModulesCal.push_back(*src.GetModule(pID, mID));
@@ -356,7 +438,7 @@ TRestDataSetGainMap& TRestDataSetGainMap::operator=(TRestDataSetGainMap& src) {
 /// \brief Function to set a module calibration. If the module calibration
 /// already exists (same planeId and moduleId), it will be replaced.
 ///
-void TRestDataSetGainMap::SetModuleCalibration(const Module& moduleCal) {
+void TRestDataSetGainMap::SetModule(const Module& moduleCal) {
     for (auto& i : fModulesCal) {
         if (i.GetPlaneId() == moduleCal.GetPlaneId() && i.GetModuleId() == moduleCal.GetModuleId()) {
             i = moduleCal;
@@ -431,7 +513,16 @@ void TRestDataSetGainMap::Export(const std::string& fileName) {
 void TRestDataSetGainMap::PrintMetadata() {
     TRestMetadata::PrintMetadata();
     RESTMetadata << " Calibration dataset: " << fCalibFileName << RESTendl;
+    if (fCut) {
+        RESTMetadata << " Cuts applied: ";
+        /* print only cutStrings and paramCut because
+        TRestDataSet::MakeCut() uses fCut->GetCutStrings() and fCut->GetParamCut() */
+        for (const auto& cut : fCut->GetCutStrings()) RESTMetadata << cut << ", " << RESTendl;
+        for (const auto& cut : fCut->GetParamCut())
+            RESTMetadata << cut.first << " " << cut.second << ", " << RESTendl;
+    }
     RESTMetadata << " Output file: " << fOutputFileName << RESTendl;
+    RESTMetadata << RESTendl;
     RESTMetadata << " Number of planes:  " << GetNumberOfPlanes() << RESTendl;
     RESTMetadata << " Number of modules: " << GetNumberOfModules() << RESTendl;
     RESTMetadata << " Calibration observable: " << fObservable << RESTendl;
@@ -628,10 +719,11 @@ void TRestDataSetGainMap::Module::GenerateGainMap() {
     }
     if (!TRestTools::isDataSet(dsFileName)) RESTWarning << dsFileName << " is not a dataset." << p->RESTendl;
     TRestDataSet dataSet;
+    dataSet.EnableMultiThreading(true);
     dataSet.Import(dsFileName);
     fDataSetFileName = dsFileName;
 
-    SetSplits();
+    dataSet.SetDataFrame(dataSet.MakeCut(p->GetCut()));
 
     if (fSplitX.empty()) SetSplitX();
     if (fSplitY.empty()) SetSplitY();
@@ -663,20 +755,28 @@ void TRestDataSetGainMap::Module::GenerateGainMap() {
                   << p->RESTendl;
     }
 
+    // --- Definition of histogram whole module ---
+    std::string hModuleName = "hSpc_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
+    delete fFullSpectrum;
+    fFullSpectrum = new TH1F(hModuleName.c_str(), "", fNBins, fCalibRange.X(), fCalibRange.Y());
+
+    // build the spectrum for the whole module
+    std::string cut = fDefinitionCut;
+    if (cut.empty()) cut = "1";
+    auto histoMod = dataSet.GetDataFrame().Filter(cut).Histo1D(
+        {"tempMod", "", fNBins, fCalibRange.X(), fCalibRange.Y()}, GetObservable());
+    std::unique_ptr<TH1F> hpuntMod = std::unique_ptr<TH1F>(static_cast<TH1F*>(histoMod->Clone()));
+    fFullSpectrum->Add(hpuntMod.get());
+
     //--- Definition of histogram matrix ---
     std::vector<std::vector<TH1F*>> h(fNumberOfSegmentsX, std::vector<TH1F*>(fNumberOfSegmentsY, nullptr));
     for (size_t i = 0; i < h.size(); i++) {
         for (size_t j = 0; j < h.at(0).size(); j++) {
-            std::string name = "hSpc_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId) + "_" +
-                               std::to_string(i) + "_" + std::to_string(j);
+            std::string name = hModuleName + "_" + std::to_string(i) + "_" + std::to_string(j);
             h[i][j] = new TH1F(name.c_str(), "", fNBins, fCalibRange.X(),
                                fCalibRange.Y());  // h[column][row] equivalent to h[x][y]
         }
     }
-    std::vector<std::vector<double>> calParSlope(fNumberOfSegmentsX,
-                                                 std::vector<double>(fNumberOfSegmentsY, 0));
-    std::vector<std::vector<double>> calParIntercept(fNumberOfSegmentsX,
-                                                     std::vector<double>(fNumberOfSegmentsY, 0));
 
     // build the spectrum for each segment
     auto itX = fSplitX.begin();
@@ -713,111 +813,130 @@ void TRestDataSetGainMap::Module::GenerateGainMap() {
     }
 
     //--- Fit every peak energy for every segment ---
+    std::vector<std::vector<double>> calParSlope(fNumberOfSegmentsX,
+                                                 std::vector<double>(fNumberOfSegmentsY, 0));
+    std::vector<std::vector<double>> calParIntercept(fNumberOfSegmentsX,
+                                                     std::vector<double>(fNumberOfSegmentsY, 0));
     fSegLinearFit = std::vector(h.size(), std::vector<TGraph*>(h.at(0).size(), nullptr));
-    for (size_t i = 0; i < h.size(); i++) {
-        for (size_t j = 0; j < h.at(0).size(); j++) {
-            RESTExtreme << "Segment[" << i << "][" << j << "]" << p->RESTendl;
-            // Search for peaks --> peakPos
-            std::unique_ptr<TSpectrum> s(new TSpectrum(2 * fEnergyPeaks.size() + 1));
-            std::vector<double> peakPos;
-            s->Search(h[i][j], 2, "goff", 0.1);
-            for (int k = 0; k < s->GetNPeaks(); k++) peakPos.push_back(s->GetPositionX()[k]);
-            std::sort(peakPos.begin(), peakPos.end(), std::greater<double>());
-            const double ratio = peakPos.size() == 0
-                                     ? 1
-                                     : peakPos.front() / fEnergyPeaks.front();  // to estimate peak position
-
-            // Initialize graph for linear fit
-            std::shared_ptr<TGraph> gr;
-            gr = std::shared_ptr<TGraph>(new TGraph());
-            gr->SetName("grFit");
-            gr->SetTitle((";" + GetObservable() + ";energy").c_str());
-
-            // Fit every energy peak
-            int c = 0;
-            double mu = 0;
-            for (const auto& energy : fEnergyPeaks) {
-                RESTExtreme << "\t fitting energy " << DoubleToString(energy, "%g") << p->RESTendl;
-                // estimation of the peak position is between start and end
-                double pos = energy * ratio;
-                double start = pos * 0.8;
-                double end = pos * 1.2;
-                if (fRangePeaks.at(c).X() < fRangePeaks.at(c).Y()) {  // if range is defined use it
-                    start = fRangePeaks.at(c).X();
-                    end = fRangePeaks.at(c).Y();
-                }
+    for (size_t i = 0; i < h.size(); i++)
+        for (int j = 0; j < (int)h.at(0).size(); j++) {
+            fSegLinearFit[i][j] = new TGraph();
+            auto [intercept, slope] = FitPeaks(h[i][j], fSegLinearFit[i][j]);
+            calParSlope[i][j] = slope;
+            calParIntercept[i][j] = intercept;
+        }
+    fSlope = calParSlope;
+    fIntercept = calParIntercept;
+    fSegSpectra = h;
+
+    //--- Fit every peak energy for the whole module ---
+    delete fFullLinearFit;
+    fFullLinearFit = new TGraph();
+    auto [intercept, slope] = FitPeaks(fFullSpectrum, fFullLinearFit);
+    fFullSlope = slope;
+    fFullIntercept = intercept;
+}
+
+std::pair<double, double> TRestDataSetGainMap::Module::FitPeaks(TH1F* hSeg, TGraph* gr) {
+    if (!hSeg) {
+        RESTError << "No histogram for fitting" << p->RESTendl;
+        return std::make_pair(0, 0);
+    }
+    if (hSeg->Integral() == 0) {
+        RESTError << "Empty spectrum " << hSeg->GetName() << p->RESTendl;
+        return std::make_pair(0, 0);
+    }
+    std::shared_ptr<TGraph> graph = std::shared_ptr<TGraph>(new TGraph());
+    RESTExtreme << "Fitting peaks for " << hSeg->GetName() << p->RESTendl;
+
+    // Search for peaks --> peakPos
+    std::unique_ptr<TSpectrum> s(new TSpectrum(2 * fEnergyPeaks.size() + 1));
+    std::vector<double> peakPos;
+    s->Search(hSeg, 2, "goff", 0.1);
+    for (int k = 0; k < s->GetNPeaks(); k++) peakPos.push_back(s->GetPositionX()[k]);
+    std::sort(peakPos.begin(), peakPos.end(), std::greater<double>());
+    const double ratio =
+        peakPos.size() == 0 ? 1 : peakPos.front() / fEnergyPeaks.front();  // to estimate peak position
+
+    // Initialize graph for linear fit
+    graph->SetName("grFit");
+    graph->SetTitle((";" + GetObservable() + ";energy").c_str());
+
+    // Fit every energy peak
+    int c = 0;
+    double mu = 0;
+    for (const auto& energy : fEnergyPeaks) {
+        RESTExtreme << "\t fitting energy " << DoubleToString(energy, "%g") << p->RESTendl;
+        // estimation of the peak position is between start and end
+        double pos = energy * ratio;
+        double start = pos * 0.8;
+        double end = pos * 1.2;
+        if (fRangePeaks.at(c).X() < fRangePeaks.at(c).Y()) {  // if range is defined use it
+            start = fRangePeaks.at(c).X();
+            end = fRangePeaks.at(c).Y();
+        }
 
-                do {
-                    if (fAutoRangePeaks) {
-                        if (peakPos.size() > 0) {
-                            // Find the peak position that is between start and end
+        do {
+            if (fAutoRangePeaks) {
+                if (peakPos.size() > 0) {
+                    // Find the peak position that is between start and end
+                    pos = peakPos.at(0);
+                    while (!(start < pos && pos < end)) {
+                        // if none of the peak position is
+                        // between start and end, use the greater one.
+                        if (pos == peakPos.back()) {
                             pos = peakPos.at(0);
-                            while (!(start < pos && pos < end)) {
-                                // if none of the peak position is
-                                // between start and end, use the greater one.
-                                if (pos == peakPos.back()) {
-                                    pos = peakPos.at(0);
-                                    break;
-                                }
-                                pos = *std::next(std::find(peakPos.begin(), peakPos.end(),
-                                                           pos));  // get next peak position
-                            }
-                            peakPos.erase(std::find(peakPos.begin(), peakPos.end(),
-                                                    pos));  // remove this peak position from the list
-                            // final estimation of the peak range (idem fitting window) with this peak
-                            // position pos
-                            start = pos * 0.8;
-                            end = pos * 1.2;
-                            const double relDist = peakPos.size() > 0 ? (pos - peakPos.front()) / pos : 999;
-                            if (relDist < 0.2) {  // if the next peak is too close reduce the window width
-                                start = pos * (1 - relDist / 2);
-                                end = pos * (1 + relDist / 2);
-                            }
+                            break;
                         }
+                        pos = *std::next(std::find(peakPos.begin(), peakPos.end(),
+                                                   pos));  // get next peak position
                     }
-
-                    std::string name = "g" + std::to_string(c);
-                    TF1* g = new TF1(name.c_str(), "gaus", start, end);
-                    RESTExtreme << "\t\tat " << DoubleToString(pos, "%.3g") << ". Range("
-                                << DoubleToString(start, "%.3g") << ", " << DoubleToString(end, "%.3g") << ")"
-                                << p->RESTendl;
-
-                    if (h[i][j]->GetFunction(name.c_str()))  // remove previous fit
-                        h[i][j]->GetListOfFunctions()->Remove(h[i][j]->GetFunction(name.c_str()));
-
-                    h[i][j]->Fit(g, "R+Q0");  // use 0 to not draw the fit but save it
-                    mu = g->GetParameter(1);
-                    RESTExtreme << "\t\tgaus mean " << DoubleToString(mu, "%g") << p->RESTendl;
-                } while (fAutoRangePeaks && peakPos.size() > 0 &&
-                         !(start < mu && mu < end));  // avoid small peaks on main peak tail
-                gr->SetPoint(c++, mu, energy);
-            }
-            s.reset();  // delete s;
-
-            if (fZeroPoint) gr->SetPoint(c++, 0, 0);
-            while (gr->GetN() < 2) {  // minimun 2 points needed for linear fit
-                gr->SetPoint(c++, 0, 0);
-                SetZeroPoint(true);
-                RESTDebug << "Not enough points for linear fit. Adding and setting zero point to true"
-                          << p->RESTendl;
+                    peakPos.erase(std::find(peakPos.begin(), peakPos.end(),
+                                            pos));  // remove this peak position from the list
+                    // final estimation of the peak range (idem fitting window) with this peak
+                    // position pos
+                    start = pos * 0.8;
+                    end = pos * 1.2;
+                    const double relDist = peakPos.size() > 0 ? (pos - peakPos.front()) / pos : 999;
+                    if (relDist < 0.2) {  // if the next peak is too close reduce the window width
+                        start = pos * (1 - relDist / 2);
+                        end = pos * (1 + relDist / 2);
+                    }
+                }
             }
 
-            // Linear fit
-            std::unique_ptr<TF1> linearFit;
-            linearFit = std::unique_ptr<TF1>(new TF1("linearFit", "pol1"));
-            gr->Fit("linearFit", "SQ");  // Q for quiet mode
+            std::string name = "g" + std::to_string(c);
+            TF1* g = new TF1(name.c_str(), "gaus", start, end);
+            RESTExtreme << "\t\tat " << DoubleToString(pos, "%.3g") << ". Range("
+                        << DoubleToString(start, "%.3g") << ", " << DoubleToString(end, "%.3g") << ")"
+                        << p->RESTendl;
 
-            fSegLinearFit.at(i).at(j) = (TGraph*)gr->Clone();
+            if (hSeg->GetFunction(name.c_str()))  // remove previous fit
+                hSeg->GetListOfFunctions()->Remove(hSeg->GetFunction(name.c_str()));
 
-            const double slope = linearFit->GetParameter(1);
-            const double intercept = linearFit->GetParameter(0);
-            calParSlope.at(i).at(j) = slope;
-            calParIntercept.at(i).at(j) = intercept;
-        }
+            hSeg->Fit(g, "R+Q0");  // use 0 to not draw the fit but save it
+            mu = g->GetParameter(1);
+            RESTExtreme << "\t\tgaus mean " << DoubleToString(mu, "%g") << p->RESTendl;
+        } while (fAutoRangePeaks && peakPos.size() > 0 &&
+                 !(start < mu && mu < end));  // avoid small peaks on main peak tail
+        graph->SetPoint(c++, mu, energy);
     }
-    fSegSpectra = h;
-    fSlope = calParSlope;
-    fIntercept = calParIntercept;
+    s.reset();  // delete s;
+
+    if (fZeroPoint) graph->SetPoint(c++, 0, 0);
+    while (graph->GetN() < 2) {  // minimun 2 points needed for linear fit
+        graph->SetPoint(c++, 0, 0);
+        SetZeroPoint(true);
+        RESTDebug << "Not enough points for linear fit. Adding and setting zero point to true" << p->RESTendl;
+    }
+
+    // Linear fit
+    std::unique_ptr<TF1> linearFit;
+    linearFit = std::unique_ptr<TF1>(new TF1("linearFit", "pol1"));
+    graph->Fit("linearFit", "SQ");  // Q for quiet mode
+
+    if (gr) *gr = *(TGraph*)graph->Clone();  // if nullptr is passed, do not copy the graph
+    return std::make_pair(linearFit->GetParameter(0), linearFit->GetParameter(1));
 }
 
 /////////////////////////////////////////////
@@ -879,6 +998,55 @@ void TRestDataSetGainMap::Module::Refit(const size_t x, const size_t y, const si
     UpdateCalibrationFits(x, y);
 }
 
+/////////////////////////////////////////////
+/// \brief Function to fit again manually a peak for the whole module spectrum. The
+/// calibration curve is updated after the fit.
+///
+/// \param energyPeak The energy of the peak to be fitted (in physical units).
+/// \param range The range for the fitting of the peak (in the observables corresponding units).
+///
+void TRestDataSetGainMap::Module::RefitFullSpc(const double energyPeak, const TVector2& range) {
+    int peakNumber = -1;
+    for (size_t i = 0; i < fEnergyPeaks.size(); i++)
+        if (fEnergyPeaks.at(i) == energyPeak) {
+            peakNumber = i;
+            break;
+        }
+    if (peakNumber == -1) {
+        RESTError << "Energy " << energyPeak << " not found in the list of energy peaks" << p->RESTendl;
+        return;
+    }
+    RefitFullSpc((size_t)peakNumber, range);
+}
+
+/////////////////////////////////////////////
+/// \brief Function to fit again manually a peak for the whole module spectrum. The
+/// calibration curve is updated after the fit.
+///
+/// \param peakNumber The index of the peak to be fitted.
+/// \param range The range for the fitting of the peak (in the observables corresponding units).
+///
+void TRestDataSetGainMap::Module::RefitFullSpc(const size_t peakNumber, const TVector2& range) {
+    if (!fFullSpectrum) {
+        RESTError << "No gain map found. Use GenerateGainMap() first." << p->RESTendl;
+        return;
+    }
+    if (peakNumber >= fEnergyPeaks.size()) {
+        RESTError << "Peak with index " << peakNumber << " not found" << p->RESTendl;
+        return;
+    }
+
+    // Refit the desired peak
+    std::string name = "g" + std::to_string(peakNumber);
+    TF1* g = new TF1(name.c_str(), "gaus", range.X(), range.Y());
+    while (fFullSpectrum->GetFunction(name.c_str()))  // clear previous fits for this peakNumber
+        fFullSpectrum->GetListOfFunctions()->Remove(fFullSpectrum->GetFunction(name.c_str()));
+    fFullSpectrum->Fit(g, "R+Q0");  // use 0 to not draw the fit but save it
+
+    // Change the point of the graph
+    UpdateCalibrationFitsFullSpc();
+}
+
 /////////////////////////////////////////////
 /// \brief Function to update the calibration curve for a given segment of the module. The calibration
 /// curve is cleared and then the means of the gaussian fits for each energy peak are added. If there are
@@ -900,6 +1068,25 @@ void TRestDataSetGainMap::Module::UpdateCalibrationFits(const size_t x, const si
     TH1F* h = fSegSpectra.at(x).at(y);
     TGraph* gr = fSegLinearFit.at(x).at(y);
 
+    auto [intercept, slope] = UpdateCalibrationFits(h, gr);
+    fSlope[x][y] = slope;
+    fIntercept[x][y] = intercept;
+}
+
+std::pair<double, double> TRestDataSetGainMap::Module::UpdateCalibrationFits(TH1F* h, TGraph* gr) {
+    if (!h) {
+        RESTError << "No histogram for updating fits" << p->RESTendl;
+        return std::make_pair(0, 0);
+    }
+    if (!gr) {
+        RESTError << "No graph for updating fits" << p->RESTendl;
+        return std::make_pair(0, 0);
+    }
+    if (h->Integral() == 0) {
+        RESTError << "Empty spectrum " << h->GetName() << p->RESTendl;
+        return std::make_pair(0, 0);
+    }
+
     // Clear the points of the graph
     for (size_t i = 0; i < fEnergyPeaks.size(); i++) gr->RemovePoint(i);
     // Add the new points to the graph
@@ -909,7 +1096,7 @@ void TRestDataSetGainMap::Module::UpdateCalibrationFits(const size_t x, const si
         TF1* g = h->GetFunction(fitName.c_str());
         if (!g) {
             RESTWarning << "No fit ( " << fitName << " ) found for energy peak " << fEnergyPeaks[i]
-                        << " in segment " << x << "," << y << p->RESTendl;
+                        << " in histogram " << h->GetName() << p->RESTendl;
             continue;
         }
         gr->SetPoint(c++, g->GetParameter(1), fEnergyPeaks[i]);
@@ -918,8 +1105,6 @@ void TRestDataSetGainMap::Module::UpdateCalibrationFits(const size_t x, const si
     // Add zero points if needed (if there are less than 2 points)
     while (gr->GetN() < 2) {
         gr->SetPoint(c++, 0, 0);
-        RESTWarning << "Not enough points for linear fit at segment (" << x << ", " << y
-                    << "). Adding zero point." << p->RESTendl;
     }
 
     // Refit the calibration curve
@@ -929,8 +1114,19 @@ void TRestDataSetGainMap::Module::UpdateCalibrationFits(const size_t x, const si
     else
         lf = new TF1("linearFit", "pol1");
     gr->Fit(lf, "SQ");  // Q for quiet mode
-    fSlope.at(x).at(y) = lf->GetParameter(1);
-    fIntercept.at(x).at(y) = lf->GetParameter(0);
+
+    return std::make_pair(lf->GetParameter(0), lf->GetParameter(1));
+}
+
+/////////////////////////////////////////////
+/// \brief Function to update the calibration curve for the whole module. The calibration
+/// curve is cleared and then the means of the gaussian fits for each energy peak are added. If there are
+/// less than 2 fits, zero points are added. Then, the calibration curve is refitted (linearFit).
+///
+void TRestDataSetGainMap::Module::UpdateCalibrationFitsFullSpc() {
+    auto [intercept, slope] = UpdateCalibrationFits(fFullSpectrum, fFullLinearFit);
+    fFullSlope = slope;
+    fFullIntercept = intercept;
 }
 
 /////////////////////////////////////////////
@@ -1021,6 +1217,10 @@ void TRestDataSetGainMap::Module::DrawSpectrum(const int index_x, const int inde
         RESTError << "Index out of range." << p->RESTendl;
         return;
     }
+    if (!fSegSpectra[index_x][index_y]) {
+        RESTError << "No Spectrum for segment (" << index_x << ", " << index_y << ")." << p->RESTendl;
+        return;
+    }
 
     if (!c) {
         std::string t = "spectrum_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId) + "_" +
@@ -1044,7 +1244,8 @@ void TRestDataSetGainMap::Module::DrawSpectrum(const int index_x, const int inde
     if (drawFits)
         for (size_t c = 0; c < fEnergyPeaks.size(); c++) {
             auto fit = fSegSpectra[index_x][index_y]->GetFunction(("g" + std::to_string(c)).c_str());
-            if (!fit) RESTWarning << "Fit for energy peak" << fEnergyPeaks[c] << " not found." << p->RESTendl;
+            if (!fit)
+                RESTWarning << "Fit for energy peak " << fEnergyPeaks[c] << " not found." << p->RESTendl;
             if (!fit) continue;
             fit->SetLineColor(c + 2 != colorT ? c + 2 : ++colorT); /* does not work with kRed, kBlue, etc.
                   as they are not defined with the same number as the first 10 basic colors. See
@@ -1095,30 +1296,41 @@ void TRestDataSetGainMap::Module::DrawSpectrum(const bool drawFits, const int co
 
     for (size_t i = 0; i < fSegSpectra.size(); i++) {
         for (size_t j = 0; j < fSegSpectra[i].size(); j++) {
-            c->cd(i + 1 + fSegSpectra[i].size() * j);
-            DrawSpectrum(i, fSegSpectra[i].size() - 1 - j, drawFits, color, c);
+            int pad = fSegSpectra.size() * (fSegSpectra[i].size() - 1) + 1 + i - fSegSpectra.size() * j;
+            c->cd(pad);
+            DrawSpectrum(i, j, drawFits, color, c);
         }
     }
 }
-void TRestDataSetGainMap::Module::DrawFullSpectrum() {
-    if (fSegSpectra.size() == 0) {
-        RESTError << "Spectra matrix is empty." << p->RESTendl;
+void TRestDataSetGainMap::Module::DrawFullSpectrum(const bool drawFits, const int color, TCanvas* c) {
+    if (!fFullSpectrum) {
+        RESTError << "Spectrum is empty." << p->RESTendl;
         return;
     }
-    TH1F* sumHist =
-        new TH1F("fullSpc", "", fSegSpectra[0][0]->GetNbinsX(), fSegSpectra[0][0]->GetXaxis()->GetXmin(),
-                 fSegSpectra[0][0]->GetXaxis()->GetXmax());
 
-    sumHist->SetTitle(("Full spectrum;" + GetObservable() + ";counts").c_str());
-    for (size_t i = 0; i < fSegSpectra.size(); i++) {
-        for (size_t j = 0; j < fSegSpectra.at(0).size(); j++) {
-            sumHist->Add(fSegSpectra[i][j]);
-        }
+    if (!c) {
+        std::string t = "fullSpc_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
+        c = new TCanvas(t.c_str(), t.c_str());
     }
-    std::string t = "fullSpc_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
-    TCanvas* c = new TCanvas(t.c_str(), t.c_str());
     c->cd();
-    sumHist->Draw();
+
+    fFullSpectrum->SetTitle(("Full spectrum;" + GetObservable() + ";counts").c_str());
+
+    if (color > 0) fFullSpectrum->SetLineColor(color);
+    size_t colorT = fFullSpectrum->GetLineColor();
+    fFullSpectrum->Draw("same");
+
+    if (drawFits)
+        for (size_t c = 0; c < fEnergyPeaks.size(); c++) {
+            auto fit = fFullSpectrum->GetFunction(("g" + std::to_string(c)).c_str());
+            if (!fit) RESTWarning << "Fit for energy peak" << fEnergyPeaks[c] << " not found." << p->RESTendl;
+            if (!fit) continue;
+            fit->SetLineColor(c + 2 != colorT ? c + 2 : ++colorT); /* does not work with kRed, kBlue, etc.
+                  as they are not defined with the same number as the first 10 basic colors. See
+                  https://root.cern.ch/doc/master/classTColor.html#C01 and
+                  https://root.cern.ch/doc/master/classTColor.html#C02 */
+            fit->Draw("same");
+        }
 }
 
 void TRestDataSetGainMap::Module::DrawLinearFit(const TVector2& position, TCanvas* c) {
@@ -1136,6 +1348,11 @@ void TRestDataSetGainMap::Module::DrawLinearFit(const int index_x, const int ind
         RESTError << "Index out of range." << p->RESTendl;
         return;
     }
+    if (!fSegLinearFit[index_x][index_y]) {
+        RESTError << "No linear fit for segment (" << index_x << ", " << index_y << ")." << p->RESTendl;
+        return;
+    }
+
     if (!c) {
         std::string t = "linearFit_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId) + "_" +
                         std::to_string(index_x) + "_" + std::to_string(index_y);
@@ -1152,25 +1369,58 @@ void TRestDataSetGainMap::Module::DrawLinearFit(const int index_x, const int ind
     fSegLinearFit[index_x][index_y]->Draw("AL*");
 }
 
-void TRestDataSetGainMap::Module::DrawLinearFit() {
-    std::string t = "linearFits_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
-    TCanvas* myCanvas = new TCanvas(t.c_str(), t.c_str());
-    myCanvas->Divide(fSegLinearFit.size(), fSegLinearFit.at(0).size());
+void TRestDataSetGainMap::Module::DrawLinearFit(TCanvas* c) {
+    if (fSegLinearFit.size() == 0) {
+        RESTError << "Spectra matrix is empty." << p->RESTendl;
+        return;
+    }
+    if (!c) {
+        std::string t = "linearFits_" + std::to_string(fPlaneId) + "_" + std::to_string(fModuleId);
+        c = new TCanvas(t.c_str(), t.c_str());
+    }
+
+    size_t nPads = 0;
+    for (const auto& object : *c->GetListOfPrimitives())
+        if (object->InheritsFrom(TVirtualPad::Class())) ++nPads;
+    if (nPads != 0 && nPads != fSegLinearFit.size() * fSegLinearFit.at(0).size()) {
+        RESTError << "Canvas " << c->GetName() << " has " << nPads << " pads, but "
+                  << fSegLinearFit.size() * fSegLinearFit.at(0).size() << " are needed." << p->RESTendl;
+        return;
+    } else if (nPads == 0)
+        c->Divide(fSegLinearFit.size(), fSegLinearFit.at(0).size());
+
     for (size_t i = 0; i < fSegLinearFit.size(); i++) {
         for (size_t j = 0; j < fSegLinearFit[i].size(); j++) {
-            myCanvas->cd(i + 1 + fSegLinearFit[i].size() * j);
-            // fSegLinearFit[i][j]->Draw("AL*");
-            DrawLinearFit(i, fSegSpectra[i].size() - 1 - j, myCanvas);
+            int pad = fSegLinearFit.size() * (fSegLinearFit[i].size() - 1) + 1 + i - fSegLinearFit.size() * j;
+            c->cd(pad);
+            DrawLinearFit(i, j, c);
         }
     }
 }
 
-void TRestDataSetGainMap::Module::DrawGainMap(const int peakNumber) {
+/////////////////////////////////////////////
+/// \brief Function to draw the relative gain map for a given energy peak of the module.
+///
+/// \param peakNumber The index of the peak to be drawn (remember they are in descending order).
+/// \param fullModuleAsRef If true, it will use the peak position at the full module spectrum
+/// as reference for the gain map. If false, it will use the centered segment of the module
+/// as reference.
+///
+void TRestDataSetGainMap::Module::DrawGainMap(const int peakNumber, bool fullModuleAsRef) {
     if (peakNumber < 0 || peakNumber >= (int)fEnergyPeaks.size()) {
         RESTError << "Peak number out of range (peakNumber should be between 0 and "
                   << fEnergyPeaks.size() - 1 << " )" << p->RESTendl;
         return;
     }
+    if (fSegLinearFit.size() == 0) {
+        RESTError << "Linear fit matrix is empty." << p->RESTendl;
+        return;
+    }
+    if (!fFullLinearFit) {
+        RESTError << "Full linear fit is empty." << p->RESTendl;
+        return;
+    }
+
     double peakEnergy = fEnergyPeaks[peakNumber];
     std::string title = "Gain map for energy " + DoubleToString(peakEnergy, "%g") + ";" +
                         GetSpatialObservableX() + ";" + GetSpatialObservableY();  // + " keV";
@@ -1178,11 +1428,15 @@ void TRestDataSetGainMap::Module::DrawGainMap(const int peakNumber) {
                     std::to_string(fModuleId);
     TCanvas* gainMap = new TCanvas(t.c_str(), t.c_str());
     gainMap->cd();
-    TH2F* hGainMap = new TH2F(("h" + t).c_str(), title.c_str(), fNumberOfSegmentsY, fReadoutRange.X(),
-                              fReadoutRange.Y(), fNumberOfSegmentsX, fReadoutRange.X(), fReadoutRange.Y());
-
-    const double peakPosRef =
-        fSegLinearFit[(fNumberOfSegmentsX - 1) / 2][(fNumberOfSegmentsY - 1) / 2]->GetPointX(peakNumber);
+    TH2F* hGainMap = new TH2F(("h" + t).c_str(), title.c_str(), fNumberOfSegmentsX, fReadoutRange.X(),
+                              fReadoutRange.Y(), fNumberOfSegmentsY, fReadoutRange.X(), fReadoutRange.Y());
+
+    double peakPosRef = fFullLinearFit->GetPointX(peakNumber);
+    if (!fullModuleAsRef) {
+        int index_x = fNumberOfSegmentsX > 0 ? (fNumberOfSegmentsX - 1) / 2 : 0;
+        int index_y = fNumberOfSegmentsY > 0 ? (fNumberOfSegmentsY - 1) / 2 : 0;
+        peakPosRef = fSegLinearFit[index_x][index_y]->GetPointX(peakNumber);
+    }
 
     auto itX = fSplitX.begin();
     for (size_t i = 0; i < fSegLinearFit.size(); i++) {
@@ -1192,8 +1446,11 @@ void TRestDataSetGainMap::Module::DrawGainMap(const int peakNumber) {
             auto xUpper = *std::next(itX);
             auto yLower = *itY;
             auto yUpper = *std::next(itY);
-            hGainMap->Fill((xUpper + xLower) / 2., (yUpper + yLower) / 2.,
-                           fSegLinearFit[i][j]->GetPointX(peakNumber) / peakPosRef);
+            float xMean = (xUpper + xLower) / 2.;
+            float yMean = (yUpper + yLower) / 2.;
+            auto [index_x, index_y] = GetIndexMatrix(xMean, yMean);
+            if (!fSegLinearFit[index_x][index_y]) continue;
+            hGainMap->Fill(xMean, yMean, fSegLinearFit[index_x][index_y]->GetPointX(peakNumber) / peakPosRef);
             itY++;
         }
         itX++;
@@ -1281,5 +1538,9 @@ void TRestDataSetGainMap::Module::Print() const {
         }
         RESTMetadata << p->RESTendl;
     }
+    RESTMetadata << p->RESTendl;
+    RESTMetadata << " Full slope: " << DoubleToString(fFullSlope, "%.3e") << p->RESTendl;
+    RESTMetadata << " Full intercept: " << DoubleToString(fFullIntercept, "%+.3e") << p->RESTendl;
+
     RESTMetadata << "-----------------------------------------------" << p->RESTendl;
 }
diff --git a/source/framework/analysis/src/TRestEventRateAnalysisProcess.cxx b/source/framework/analysis/src/TRestEventRateAnalysisProcess.cxx
index d1e0eb49377eec32ee1087a616938bb8997ef5aa..93c9103b13667d63720283f028830103b9560aa9 100644
--- a/source/framework/analysis/src/TRestEventRateAnalysisProcess.cxx
+++ b/source/framework/analysis/src/TRestEventRateAnalysisProcess.cxx
@@ -65,7 +65,7 @@
 ///
 /// <hr>
 ///
-/// \warning **⚠ REST is under continous development.** This documentation
+/// \warning **⚠ REST is under continuous development.** This documentation
 /// is offered to you by the REST community. Your HELP is needed to keep this
 /// code up to date. Your feedback will be worth to support this software, please
 /// report any problems/suggestions you may find will using it at [The REST Framework
diff --git a/source/framework/analysis/src/TRestEventSelectionProcess.cxx b/source/framework/analysis/src/TRestEventSelectionProcess.cxx
index 6b168819c7bee0fc2dd4d6ab9486c26a0062d852..f0f7405754243de8cddf7dfac48f95cb24fed089 100644
--- a/source/framework/analysis/src/TRestEventSelectionProcess.cxx
+++ b/source/framework/analysis/src/TRestEventSelectionProcess.cxx
@@ -23,7 +23,7 @@
 //////////////////////////////////////////////////////////////////////////
 /// The TRestEventSelectionProcess allows procesing of selected events only.
 ///
-/// There are two ways of selecting events:
+/// There are three ways of selecting events:
 ///
 /// * Providing a txt file with the IDs of the events to be processed (fileWithIDs).
 /// It reads the list, if an event is not in the list it returns NULL,
@@ -32,6 +32,11 @@
 /// * Providing a root file (fileWithIDs) and the conditions to select the events (conditions).
 /// Only events that satisfy the conditions will be processed.
 ///
+/// * Not providing any fileWithIDs (empty string). In this case, the process will use the TRestAnalysisTree
+/// of the processing file itself to evaluate the conditions and select the events. Make sure that the
+/// analysis processes that generate the obervables needed for the conditions are executed before this
+/// process. See TRestAnalysisTree::EvaluateCuts for more information on conditions format.
+///
 /// Examples for rml files:
 /// <addProcess type="TRestEventSelectionProcess" name="evSelection" fileWithIDs="/path/to/file/IDs.txt"
 /// value="ON"  verboseLevel="info"/>
@@ -41,7 +46,7 @@
 ///
 /// <hr>
 ///
-/// \warning **? REST is under continous development.** This documentation
+/// \warning ** REST is under continuous development.** This documentation
 /// is offered to you by the REST community. Your HELP is needed to keep this code
 /// up to date. Your feedback will be worth to support this software, please report
 /// any problems/suggestions you may find while using it at [The REST Framework
@@ -66,9 +71,13 @@
 /// 2021-Mar: Read IDs from root with conditions
 ///              David Diez
 ///
+/// 2024-Jun: Use of the processing file itself (no need for external fileWithIDs)
+///              Alvaro Ezquerro
+///
 /// \class      TRestEventSelectionProcess
 /// \author     Javier Galan
 /// \author     David Diez
+/// \author     Alvaro Ezquerro
 ///
 /// <hr>
 ///
@@ -76,6 +85,7 @@
 #include "TRestEventSelectionProcess.h"
 
 using namespace std;
+
 ClassImp(TRestEventSelectionProcess);
 
 ///////////////////////////////////////////////
@@ -112,9 +122,10 @@ void TRestEventSelectionProcess::InitProcess() {
             File.close();
         }
     } else if (TRestTools::GetFileNameExtension(fFileWithIDs) == "root") {
-        TRestRun* run = new TRestRun(fFileWithIDs);
-        fList = run->GetEventIdsWithConditions(fConditions);
-        delete run;
+        TRestRun run(fFileWithIDs);
+        fList = run.GetEventIdsWithConditions(fConditions);
+    } else {
+        RESTDebug << "TRestEventSelectionProcess: using the processing file itself." << RESTendl;
     }
 }
 
@@ -124,8 +135,14 @@ void TRestEventSelectionProcess::InitProcess() {
 TRestEvent* TRestEventSelectionProcess::ProcessEvent(TRestEvent* inputEvent) {
     fEvent = inputEvent;
 
-    for (unsigned int i = 0; i < fList.size(); i++) {
-        if (fList[i] == fEvent->GetID()) {
+    if (fFileWithIDs.empty()) {
+        if (this->GetAnalysisTree()->EvaluateCuts(fConditions)) {
+            return fEvent;
+        }
+    }
+
+    for (auto id : fList) {
+        if (id == fEvent->GetID()) {
             return fEvent;
         }
     }
@@ -140,9 +157,7 @@ void TRestEventSelectionProcess::PrintMetadata() {
     BeginPrintProcess();
 
     RESTMetadata << "File with IDs: " << fFileWithIDs << RESTendl;
-    if (fFileWithIDs.substr(fFileWithIDs.length() - 4) == "root") {
-        RESTMetadata << "Conditions: " << fConditions << RESTendl;
-    }
+    RESTMetadata << "Conditions: " << fConditions << RESTendl;
 
     EndPrintProcess();
 }
diff --git a/source/framework/analysis/src/TRestEventTimeSelectionProcess.cxx b/source/framework/analysis/src/TRestEventTimeSelectionProcess.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9a2db9f5d674faaff760b9de76ef566e7dc204d6
--- /dev/null
+++ b/source/framework/analysis/src/TRestEventTimeSelectionProcess.cxx
@@ -0,0 +1,354 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see http://gifna.unizar.es/trex                  *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see http://www.gnu.org/licenses/.                             *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+//////////////////////////////////////////////////////////////////////////
+/// The TRestEventTimeSelectionProcess allows processing of events that are within
+/// a certain time range. The time ranges are read from a file that contains
+/// the start and end times of the periods. The file should have the following
+/// format (it can have more columns separated by the same delimiter, but the first
+/// two columns should be the start and end times and all the other columns
+/// will be ignored):
+/// \code
+///     # This is a comment
+///     # Start time,End time
+///     2021-01-01 00:00:00,2021-01-01 01:00:00
+///     2021-01-01 02:00:00,2021-01-01 03:00:00
+///     2021-01-01 04:00:00,2021-01-01 05:00:00
+/// \endcode
+/// The default delimiter is a comma, but it can be changed by setting the
+/// `delimiter` parameter (single character only). The time format is
+/// given by the accepted formats of the StringToTimeStamp function.
+///
+/// The time ranges can be active or dead periods of time. If the time ranges
+/// are active periods of time, the events that are within the time ranges will
+/// be selected. If the time ranges are dead periods of time, the events that
+/// are outside the time ranges will be selected. Set the `isActiveTime` parameter
+/// to `true` to select the events within the time ranges (they represent active periods
+/// of time) or to `false` to select the events outside the time ranges (they represent
+/// dead periods of time).
+///
+/// The number of events selected, rejected and the total time selected are stored in the
+/// metadata members. The total time selected is calculated as the sum of the time
+/// of all the time ranges, so it represents the total active time in the case of
+/// active periods of time, or the total dead time in the case of dead periods of time.
+///
+/// ### Parameters
+/// * **fileWithTimes**: name of the file that contains the time ranges.
+/// * **isActiveTime**: if `true` (default) the time ranges represent active periods of time, if `false` the
+/// time ranges represent dead periods of time.
+/// * **delimiter**: delimiter used in the file with the time ranges (default is `,`).
+/// * **offsetTimeInSeconds**: offset time in seconds to be added to the event time (default is 0). This is
+/// useful to correct the time of the events if needed. This number of seconds will be added to the event time
+/// before checking if it is within the time ranges.
+/// * **startMarginTimeInSeconds**: margin time in seconds to be added to the start time of the time ranges
+/// (default is 0). This is useful to consider the events that are close to the start time of the time ranges.
+/// * **endMarginTimeInSeconds**: margin time in seconds to be subtracted from the end time of the time ranges
+/// (default is 0). This is useful to consider the events that are close to the end time of the time ranges.
+///
+/// ### Observables
+/// The process does not produce event observables but it keeps track of the number of events selected and
+/// rejected and the total time of the time ranges in the metadata members:
+/// * **nEventsRejected**: number of events rejected.
+/// * **nEventsSelected**: number of events selected.
+///
+/// ### Examples
+/// Examples for rml files:
+/// \code
+///    <addProcess type="TRestEventTimeSelectionProcess" name="timeSel" value="ON" verboseLevel="info">
+///        <parameter name="isActiveTime" value="true" /> <!-- default is true-->
+///        <parameter name="fileWithTimes" value="/path/to/fileWithTimes.txt" />
+///        <parameter name="timeOffsetInSeconds" value="-58" /> <!-- default is 0 -->
+///        <parameter name="timeStartMarginInSeconds" value="5" /> <!-- default is 0 -->
+///        <parameter name="timeEndMarginInSeconds" value="5" /> <!-- default is 0 -->
+///    </addProcess>
+/// \endcode <hr>
+///
+/// \warning ** REST is under continuous development.** This documentation
+/// is offered to you by the REST community. Your HELP is needed to keep this code
+/// up to date. Your feedback will be worth to support this software, please report
+/// any problems/suggestions you may find while using it at [The REST Framework
+/// forum](http://ezpc10.unizar.es). You are welcome to contribute fixing typos,
+/// updating information or adding/proposing new contributions. See also our
+/// <a href="https://github.com/rest-for-physics/framework/blob/master/CONTRIBUTING.md">Contribution
+/// Guide</a>.
+///
+///
+///--------------------------------------------------------------------------
+///
+/// RESTsoft - Software for Rare Event Searches with TPCs
+///
+/// History of developments:
+/// 2025-Jan - First version of the code
+///              Alvaro Ezquerro
+///
+/// \class      TRestEventTimeSelectionProcess
+/// \author     Alvaro Ezquerro
+///
+/// <hr>
+///
+
+#include "TRestEventTimeSelectionProcess.h"
+
+using namespace std;
+
+ClassImp(TRestEventTimeSelectionProcess);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestEventTimeSelectionProcess::TRestEventTimeSelectionProcess() { Initialize(); }
+
+///////////////////////////////////////////////
+/// \brief Function to initialize input/output event members and define the
+/// section name
+///
+void TRestEventTimeSelectionProcess::Initialize() {
+    SetSectionName(this->ClassName());
+    fEvent = nullptr;
+    fFileWithTimes = "";
+    fIsActiveTime = true;
+    fDelimiter = ',';
+    fStartEndTimes.clear();
+    fTimeOffsetInSeconds = 0;
+    fTimeStartMarginInSeconds = 0;
+    fTimeEndMarginInSeconds = 0;
+    fNEventsRejected = 0;
+    fNEventsSelected = 0;
+    fTotalTimeInSeconds = 0;
+}
+
+///////////////////////////////////////////////
+/// \brief Process initialization.
+///
+///
+void TRestEventTimeSelectionProcess::InitProcess() {
+    // Read the file with the time ranges
+    if (!fFileWithTimes.empty()) {
+        fStartEndTimes = ReadFileWithTimes(fFileWithTimes, fDelimiter);
+    }
+    fTotalTimeInSeconds = CalculateTotalTimeInSeconds();
+    fNEventsRejected = 0;
+    fNEventsSelected = 0;
+}
+
+std::vector<std::pair<std::string, std::string>> TRestEventTimeSelectionProcess::ReadFileWithTimes(
+    std::string fileWithTimes, Char_t delimiter) {
+    std::vector<std::pair<std::string, std::string>> startEndTimes;
+    string line;
+    ifstream file(fileWithTimes);
+    if (file.is_open()) {
+        while (getline(file, line)) {
+            if (line[0] == '#') {  // understand as comment
+                continue;
+            }
+            std::istringstream lineStream(line);
+            std::string startDate, endDate;
+            if (std::getline(lineStream, startDate, delimiter) &&
+                std::getline(lineStream, endDate, delimiter)) {
+                // check if the time format is correct. TODO: use better way to check
+                // (StringToTimeStamp usually returns a negative big number if not)
+                if (StringToTimeStamp(startDate) < 0 || StringToTimeStamp(endDate) < 0) {
+                    continue;
+                }
+
+                startEndTimes.emplace_back(startDate, endDate);
+            }
+        }
+        file.close();
+    }
+    return startEndTimes;
+}
+
+///////////////////////////////////////////////
+/// \brief Function to calculate the total time in seconds of all the time ranges
+/// (active or dead periods of time). It takes into account the time offset,
+/// and both the start and end margins.
+///
+Double_t TRestEventTimeSelectionProcess::CalculateTotalTimeInSeconds() {
+    Double_t totalTime = 0;
+    for (auto id : fStartEndTimes) {
+        TTimeStamp startTime = TTimeStamp(StringToTimeStamp(id.first), 0);
+        TTimeStamp endTime = TTimeStamp(StringToTimeStamp(id.second), 0);
+        // Reduce the time by the margin in both sides
+        startTime.Add(TTimeStamp(fTimeStartMarginInSeconds));
+        endTime.Add(TTimeStamp(-fTimeEndMarginInSeconds));
+        auto timeDiff = endTime.AsDouble() - startTime.AsDouble();
+        if (timeDiff < 0) {
+            RESTDebug << "End time is before start time in time range: " << id.first << " to " << id.second
+                      << RESTendl;
+            continue;
+        }
+        totalTime += endTime.AsDouble() - startTime.AsDouble();
+    }
+    return totalTime;
+}
+
+///////////////////////////////////////////////
+/// \brief The main processing event function
+///
+TRestEvent* TRestEventTimeSelectionProcess::ProcessEvent(TRestEvent* inputEvent) {
+    fEvent = inputEvent;
+
+    TTimeStamp eventTime = fEvent->GetTimeStamp();
+    eventTime.Add(TTimeStamp(fTimeOffsetInSeconds));
+
+    Bool_t isInsideAnyTimeRange = false;
+    for (auto id : fStartEndTimes) {
+        TTimeStamp startTime = TTimeStamp(StringToTimeStamp(id.first), 0);
+        TTimeStamp endTime = TTimeStamp(StringToTimeStamp(id.second), 0);
+        // Reduce the time by the margin in both sides
+        startTime.Add(TTimeStamp(fTimeStartMarginInSeconds));
+        endTime.Add(TTimeStamp(-fTimeEndMarginInSeconds));
+        if (eventTime >= startTime && eventTime <= endTime) {
+            isInsideAnyTimeRange = true;
+            break;
+        }
+    }
+
+    // Decide if the event is selected or rejected based on the time ranges
+    // and their meaning (active or dead periods of time).
+    if (fIsActiveTime) {             // time ranges represent active periods of time
+        if (isInsideAnyTimeRange) {  // time is inside an active period of time
+            fNEventsSelected++;
+            return fEvent;
+        }
+    } else {                          // time ranges represent dead periods of time
+        if (!isInsideAnyTimeRange) {  // time is outside all dead period of time
+            fNEventsSelected++;
+            return fEvent;
+        }
+    }
+
+    // rejected events are not returned
+    fNEventsRejected++;
+    return nullptr;
+}
+
+///////////////////////////////////////////////
+/// \brief Function to include required actions after all events have been
+/// processed.
+///
+void TRestEventTimeSelectionProcess::EndProcess() {
+    // Write here the jobs to do when all the events are processed
+}
+
+///////////////////////////////////////////////
+/// \brief Function to get the cut string that reproduce the time selection
+/// done by this process (useful for TRestDataSet::MakeCut() for example).
+/// \note The cut string can be really long if there are many time ranges and
+/// this may cause the following error
+/// 'Error in <TTreeFormula::Compile>:  Too many operators !' when trying to
+/// use the cut in TTree->Draw(). In such case, use
+/// \code
+/// ROOT::v5::TFormula::SetMaxima(10000) // or any other big enough number
+/// \endcode
+/// to increase the maximum number of operators allowed in a formula.
+///
+std::string TRestEventTimeSelectionProcess::GetTimeStampCut(std::string timeStampObsName, Bool_t useOffset,
+                                                            Bool_t useMargins, Int_t nTimes) {
+    std::string timeCut = "";
+    std::string timeStampObsNameWithOffset = timeStampObsName;
+    if (useOffset && fTimeOffsetInSeconds != 0) {
+        timeStampObsNameWithOffset += "+" + to_string(fTimeOffsetInSeconds);
+    }
+    if (nTimes < 0) nTimes = fStartEndTimes.size();
+    Int_t c = 0;
+    for (auto id : fStartEndTimes) {
+        if (c++ >= nTimes) break;
+        auto startTime = StringToTimeStamp(id.first);
+        auto endTime = StringToTimeStamp(id.second);
+        // Reduce the time by the margin in both sides
+        if (useMargins) {
+            startTime += fTimeStartMarginInSeconds;
+            endTime -= fTimeEndMarginInSeconds;
+        }
+
+        if (startTime >= endTime) {
+            continue;
+        }
+
+        // Build the cut string
+        if (!timeCut.empty()) {
+            if (fIsActiveTime)
+                timeCut += " || ";  // inside ANY time range
+            else
+                timeCut += " && ";  // outside ALL time ranges
+        }
+        if (!fIsActiveTime) timeCut += "!";  // NOT inside the time range
+        // inside the time range
+        timeCut += "(";
+        timeCut += "(" + timeStampObsNameWithOffset + ">=" + to_string(startTime) + ")";
+        timeCut += " && ";
+        timeCut += "(" + timeStampObsNameWithOffset + "<=" + to_string(endTime) + ")";
+        timeCut += ")";
+    }
+    return timeCut;
+}
+///////////////////////////////////////////////
+/// \brief Prints on screen the process data members
+///
+void TRestEventTimeSelectionProcess::PrintMetadata() {
+    BeginPrintProcess();
+    std::string typeOfTime = fIsActiveTime ? "Active" : "Dead";
+
+    RESTMetadata << "File with times: " << fFileWithTimes << RESTendl;
+    // print periods
+    RESTMetadata << "Offset time: " << fTimeOffsetInSeconds << " seconds" << RESTendl;
+    RESTMetadata << "Start margin time: " << fTimeStartMarginInSeconds << " seconds" << RESTendl;
+    RESTMetadata << "End margin time: " << fTimeEndMarginInSeconds << " seconds" << RESTendl;
+    RESTMetadata << typeOfTime << " time periods: " << RESTendl;
+    for (auto id : fStartEndTimes) {
+        RESTMetadata << id.first << " to " << id.second << RESTendl;
+        TTimeStamp startTime = TTimeStamp(StringToTimeStamp(id.first), 0);
+        TTimeStamp endTime = TTimeStamp(StringToTimeStamp(id.second), 0);
+    }
+
+    // Get total time in seconds
+    TTimeStamp totalTime = TTimeStamp(fTotalTimeInSeconds, 0);
+    if (!fStartEndTimes.empty()) {
+        TTimeStamp firstTime = TTimeStamp(StringToTimeStamp(fStartEndTimes.front().first), 0);
+        TTimeStamp lastTime = TTimeStamp(StringToTimeStamp(fStartEndTimes.back().second), 0);
+        totalTime = lastTime - firstTime;
+    }
+
+    double fractionOfTime = fTotalTimeInSeconds / totalTime.AsDouble() * 100;
+    std::string fractionOfTimeStr = StringWithPrecision(fractionOfTime, 4) + " %";
+    if ((Int_t)(fTotalTimeInSeconds / 24 / 3600) != 0)  // order of days
+        RESTMetadata << "Total " << typeOfTime << " time: " << fTotalTimeInSeconds / 24 / 3600 << " days"
+                     << " (" << fractionOfTimeStr << ")" << RESTendl;
+    else if ((Int_t)(fTotalTimeInSeconds / 3600) != 0)  // order of hours
+        RESTMetadata << "Total " << typeOfTime << " time: " << fTotalTimeInSeconds / 3600 << " hours"
+                     << " (" << fractionOfTimeStr << ")" << RESTendl;
+    else if ((Int_t)(fTotalTimeInSeconds / 60) != 0)  // order of minutes
+        RESTMetadata << "Total " << typeOfTime << " time: " << fTotalTimeInSeconds / 60 << " minutes"
+                     << " (" << fractionOfTimeStr << ")" << RESTendl;
+    else
+        RESTMetadata << "Total " << typeOfTime << " time: " << fTotalTimeInSeconds << " seconds"
+                     << " (" << fractionOfTimeStr << ")" << RESTendl;
+
+    RESTMetadata << "Number of events rejected: " << fNEventsRejected << " ("
+                 << fNEventsRejected * 1. / (fNEventsRejected + fNEventsSelected) * 100 << " %)" << RESTendl;
+    RESTMetadata << "Number of events selected: " << fNEventsSelected << " ("
+                 << fNEventsSelected * 1. / (fNEventsRejected + fNEventsSelected) * 100 << " %)" << RESTendl;
+
+    EndPrintProcess();
+}
diff --git a/source/framework/core/inc/TRestDataSet.h b/source/framework/core/inc/TRestDataSet.h
index 4efe5699eb05f2bde6827623650492043dc846ba..c70eae202cdb8558ca14f97377b1da7ffb5f79e1 100644
--- a/source/framework/core/inc/TRestDataSet.h
+++ b/source/framework/core/inc/TRestDataSet.h
@@ -82,7 +82,7 @@ class TRestDataSet : public TRestMetadata {
     std::map<std::string, RelevantQuantity> fQuantity;  //<
 
     /// Parameter cuts over the selected dataset
-    TRestCut* fCut = nullptr;
+    TRestCut* fCut = nullptr;  //<
 
     /// The total integrated run time of selected files
     Double_t fTotalDuration = 0;  //<
@@ -103,14 +103,16 @@ class TRestDataSet : public TRestMetadata {
     std::vector<std::string> fImportedFiles;  //<
 
     /// A list of new columns together with its corresponding expressions added to the dataset
-    std::vector<std::pair<std::string, std::string>> fColumnNameExpressions;
+    std::vector<std::pair<std::string, std::string>> fColumnNameExpressions;  //<
 
     /// A flag to enable Multithreading during dataframe generation
     Bool_t fMT = false;  //<
 
-    inline auto GetAddedColumns() const { return fColumnNameExpressions; }
+    // If the dataframe was defined externally it will be true
+    Bool_t fExternal = false;  //<
+
     /// The resulting RDF::RNode object after initialization
-    ROOT::RDF::RNode fDataSet = ROOT::RDataFrame(0);  //!
+    ROOT::RDF::RNode fDataFrame = ROOT::RDataFrame(0);  //!
 
     /// A pointer to the generated tree
     TChain* fTree = nullptr;  //!
@@ -120,29 +122,39 @@ class TRestDataSet : public TRestMetadata {
    protected:
     virtual std::vector<std::string> FileSelection();
 
+    void RegenerateTree(std::vector<std::string> finalList = {});
+
    public:
     /// Gives access to the RDataFrame
     ROOT::RDF::RNode GetDataFrame() const {
-        if (fTree == nullptr) RESTWarning << "DataFrame has not been yet initialized" << RESTendl;
-        return fDataSet;
+        if (!fExternal && fTree == nullptr)
+            RESTWarning << "DataFrame has not been yet initialized" << RESTendl;
+        return fDataFrame;
     }
 
-    void SetDataFrame(const ROOT::RDF::RNode& dS) { fDataSet = dS; }
-
     void EnableMultiThreading(Bool_t enable = true) { fMT = enable; }
 
     /// Gives access to the tree
     TTree* GetTree() const {
+        if (fTree == nullptr && fExternal) {
+            RESTInfo << "The tree is not accessible. Only GetDataFrame can be used in an externally "
+                        "generated dataset"
+                     << RESTendl;
+            RESTInfo << "You may write a tree using GetDataFrame()->Snapshot(\"MyTree\", \"output.root\");"
+                     << RESTendl;
+            return fTree;
+        }
+
         if (fTree == nullptr) {
             RESTError << "Tree has not been yet initialized" << RESTendl;
-            RESTError << "You should invoke TRestDataSet::GenerateDataSet() before trying to access the tree"
-                      << RESTendl;
+            RESTError << "You should invoke TRestDataSet::GenerateDataSet() or " << RESTendl;
+            RESTError << "TRestDataSet::Import( fname ) before trying to access the tree" << RESTendl;
         }
         return fTree;
     }
 
     /// Number of variables (or observables)
-    size_t GetNumberOfColumns() { return fDataSet.GetColumnNames().size(); }
+    size_t GetNumberOfColumns() { return fDataFrame.GetColumnNames().size(); }
 
     /// Number of variables (or observables)
     size_t GetNumberOfBranches() { return GetNumberOfColumns(); }
@@ -167,6 +179,7 @@ class TRestDataSet : public TRestMetadata {
     inline auto GetFilterLowerThan() const { return fFilterLowerThan; }
     inline auto GetFilterEqualsTo() const { return fFilterEqualsTo; }
     inline auto GetQuantity() const { return fQuantity; }
+    inline auto GetAddedColumns() const { return fColumnNameExpressions; }
     inline auto GetCut() const { return fCut; }
     inline auto IsMergedDataSet() const { return fMergedDataset; }
 
@@ -174,16 +187,25 @@ class TRestDataSet : public TRestMetadata {
     inline void SetFilePattern(const std::string& pattern) { fFilePattern = pattern; }
     inline void SetQuantity(const std::map<std::string, RelevantQuantity>& quantity) { fQuantity = quantity; }
 
+    void SetTotalTimeInSeconds(Double_t seconds) { fTotalDuration = seconds; }
+    void SetDataFrame(const ROOT::RDF::RNode& dS) {
+        fDataFrame = dS;
+        fExternal = true;
+    }
+
     TRestDataSet& operator=(TRestDataSet& dS);
     Bool_t Merge(const TRestDataSet& dS);
     void Import(const std::string& fileName);
     void Import(std::vector<std::string> fileNames);
-    void Export(const std::string& filename);
+    void Export(const std::string& filename, std::vector<std::string> excludeColumns = {});
 
     ROOT::RDF::RNode MakeCut(const TRestCut* cut);
-
+    ROOT::RDF::RNode ApplyRange(size_t from, size_t to);
+    ROOT::RDF::RNode Range(size_t from, size_t to);
     ROOT::RDF::RNode DefineColumn(const std::string& columnName, const std::string& formula);
 
+    size_t GetEntries();
+
     void PrintMetadata() override;
     void Initialize() override;
 
@@ -193,6 +215,6 @@ class TRestDataSet : public TRestMetadata {
     TRestDataSet(const char* cfgFileName, const std::string& name = "");
     ~TRestDataSet();
 
-    ClassDefOverride(TRestDataSet, 5);
+    ClassDefOverride(TRestDataSet, 8);
 };
 #endif
diff --git a/source/framework/core/inc/TRestDataSetPlot.h b/source/framework/core/inc/TRestDataSetPlot.h
index fec0c6266954e34e8e9fd11552b68c749e806bb3..d957631b21f7caf9286508e1f7466bb7e65fad0c 100644
--- a/source/framework/core/inc/TRestDataSetPlot.h
+++ b/source/framework/core/inc/TRestDataSetPlot.h
@@ -95,6 +95,7 @@ class TRestDataSetPlot : public TRestMetadata {
         std::vector<std::pair<std::array<std::string, 3>, TVector2>> variablePos;
         std::vector<std::pair<std::array<std::string, 3>, TVector2>> metadataPos;
         std::vector<std::pair<std::array<std::string, 3>, TVector2>> obsPos;
+        std::vector<std::pair<std::array<std::string, 3>, TVector2>> expPos;
 
         TRestCut* panelCut = nullptr;
 
@@ -182,6 +183,6 @@ class TRestDataSetPlot : public TRestMetadata {
     TRestDataSetPlot(const char* configFilename, std::string name = "");
     ~TRestDataSetPlot();
 
-    ClassDefOverride(TRestDataSetPlot, 1);
+    ClassDefOverride(TRestDataSetPlot, 2);
 };
 #endif
diff --git a/source/framework/core/inc/TRestMetadata.h b/source/framework/core/inc/TRestMetadata.h
index d87a601395bf9db074fa52c596b401fc6c99fc6a..32f6c20322cbed1acf0a7a8ba7c26123f28153b1 100644
--- a/source/framework/core/inc/TRestMetadata.h
+++ b/source/framework/core/inc/TRestMetadata.h
@@ -43,6 +43,10 @@
 #include "TRestVersion.h"
 #include "tinyxml.h"
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 /* We keep using REST_RELEASE, REST_VERSION(2,X,Y) and REST_VERSION_CODE
    to determine the installed REST version and avoid too much prototyping
 
@@ -286,6 +290,10 @@ class TRestMetadata : public TNamed {
 
     inline Bool_t isCleanState() const { return fCleanState; }
 
+    UInt_t GetVersionMajor() const;
+    UInt_t GetVersionMinor() const;
+    UInt_t GetVersionPatch() const;
+
     Int_t GetVersionCode();
     /// Returns a std::string with the path used for data storage
     inline TString GetDataPath() {
@@ -332,8 +340,8 @@ class TRestMetadata : public TNamed {
     ~TRestMetadata();
 
     // Making class constructors protected to keep this class abstract
-    TRestMetadata& operator=(const TRestMetadata&) = delete;
-    TRestMetadata(const TRestMetadata&) = delete;
+    TRestMetadata& operator=(const TRestMetadata&);
+    TRestMetadata(const TRestMetadata&);
 
     /// Call CINT to generate streamers for this class
     ClassDef(TRestMetadata, 9);
diff --git a/source/framework/core/inc/TRestRun.h b/source/framework/core/inc/TRestRun.h
index c927d58803565d3df171df8be88abf813ee950e5..a830bd6aa38e6344b6783db7d72505640e5b8c37 100644
--- a/source/framework/core/inc/TRestRun.h
+++ b/source/framework/core/inc/TRestRun.h
@@ -60,6 +60,8 @@ class TRestRun : public TRestMetadata {
     bool fHangUpEndFile = false;           //!
     bool fFromRML = false;                 //!
 
+    Long64_t fFeminosDaqTotalEvents = 0;  //!
+
     void InitFromConfigFile() override;
 
    private:
@@ -90,8 +92,8 @@ class TRestRun : public TRestMetadata {
         GetEntry(fCurrentEvent + 1);
     }
 
-    TString FormFormat(const TString& FilenameFormat);
-    TFile* MergeToOutputFile(std::vector<std::string> filefullnames, std::string outputfilename = "");
+    TString FormFormat(const TString& filenameFormat);
+    TFile* MergeToOutputFile(std::vector<std::string> fileFullNames, std::string outputFileName = "");
     TFile* FormOutputFile();
     TFile* UpdateOutputFile();
 
@@ -143,7 +145,7 @@ class TRestRun : public TRestMetadata {
     inline TFile* GetInputFile() const { return fInputFile; }
     inline TFile* GetOutputFile() const { return fOutputFile; }
     inline int GetCurrentEntry() const { return fCurrentEvent; }
-    inline Long64_t GetBytesReaded() const { return fBytesRead; }
+    inline Long64_t GetBytesRead() const { return fBytesRead; }
     Long64_t GetTotalBytes();
     Long64_t GetEntries() const;
 
@@ -176,6 +178,7 @@ class TRestRun : public TRestMetadata {
     std::vector<std::string> GetMetadataNames();
     std::vector<std::string> GetMetadataTitles();
     inline int GetNumberOfMetadata() const { return fMetadata.size(); }
+    Double_t GetElapsedTimeSeconds() const { return fEndTime - fStartTime; }
 
     inline std::string GetMetadataMember(const std::string& instr) { return ReplaceMetadataMember(instr); }
     std::string ReplaceMetadataMembers(const std::string& instr, Int_t precision = 8);
@@ -191,7 +194,6 @@ class TRestRun : public TRestMetadata {
     inline void SetOutputFileName(const std::string& s) { fOutputFileName = s; }
     void SetExtProcess(TRestEventProcess* p);
     inline void SetCurrentEntry(int i) { fCurrentEvent = i; }
-    // void AddFileTask(TRestFileTask* t) { fFileTasks.push_back(t); }
     void SetInputEvent(TRestEvent* event);
     inline void SetRunNumber(Int_t number) { fRunNumber = number; }
     inline void SetParentRunNumber(Int_t number) { fParentRunNumber = number; }
@@ -215,6 +217,10 @@ class TRestRun : public TRestMetadata {
     inline void SetNFilesSplit(int n) { fNFilesSplit = n; }
     inline void HangUpEndFile() { fHangUpEndFile = true; }
     inline void ReleaseEndFile() { fHangUpEndFile = false; }
+
+    inline void SetFeminosDaqTotalEvents(Long64_t n) { fFeminosDaqTotalEvents = n; }
+    inline Long64_t GetFeminosDaqTotalEvents() const { return fFeminosDaqTotalEvents; }
+
     // Printers
     void PrintStartDate();
     void PrintEndDate();
@@ -222,7 +228,9 @@ class TRestRun : public TRestMetadata {
     void PrintMetadata() override;
     inline void PrintAllMetadata() {
         PrintMetadata();
-        for (unsigned int i = 0; i < fMetadata.size(); i++) fMetadata[i]->PrintMetadata();
+        for (unsigned int i = 0; i < fMetadata.size(); i++) {
+            fMetadata[i]->PrintMetadata();
+        }
     }
     inline void PrintTrees() const {
         if (fEventTree != nullptr) {
@@ -237,7 +245,9 @@ class TRestRun : public TRestMetadata {
         }
     }
     void PrintObservables() {
-        if (fAnalysisTree != nullptr) fAnalysisTree->PrintObservables();
+        if (fAnalysisTree != nullptr) {
+            fAnalysisTree->PrintObservables();
+        }
     }
 
     inline void PrintEvent() const { fInputEvent->PrintEvent(); }
diff --git a/source/framework/core/inc/TRestSystemOfUnits.h b/source/framework/core/inc/TRestSystemOfUnits.h
index 782979d52dc85d1a4ca5b617507bd26ec5fdc7b7..5d21fe2ddcd813cd88ca9f4e86d21b495417e50c 100644
--- a/source/framework/core/inc/TRestSystemOfUnits.h
+++ b/source/framework/core/inc/TRestSystemOfUnits.h
@@ -1,15 +1,24 @@
-///______________________________________________________________________________
-///______________________________________________________________________________
-///______________________________________________________________________________
-///
-///
-///             RESTSoft : Software for Rare Event Searches with TPCs
-///
-///             TRestSystemOfUnits.h
-///
-///             Feb 2016:   First concept
-///                 author : Javier Galan
-///_______________________________________________________________________________
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see http://gifna.unizar.es/trex                  *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see http://www.gnu.org/licenses/.                             *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
 
 #ifndef RestCore_TRestSystemOfUnits
 #define RestCore_TRestSystemOfUnits
@@ -105,11 +114,16 @@ AddUnit(us, REST_Units::Time, 1.);
 AddUnit(ms, REST_Units::Time, 1.e-3);
 AddUnit(s, REST_Units::Time, 1.e-6);
 AddUnit(Hz, REST_Units::Time, 1.e6);
-AddUnit(minu, REST_Units::Time, 1.67e-8);
-AddUnit(hr, REST_Units::Time, 2.78e-10);
-AddUnit(day, REST_Units::Time, 1.16e-11);
-AddUnit(mon, REST_Units::Time, 3.85e-13);
-AddUnit(yr, REST_Units::Time, 3.17e-14);
+AddUnit(minu, REST_Units::Time, 1.e-6 / 60.);
+AddUnit(minutes, REST_Units::Time, 1.e-6 / 60.);
+AddUnit(hr, REST_Units::Time, 1e-6 / 3600.);
+AddUnit(hours, REST_Units::Time, 1e-6 / 3600.);
+AddUnit(day, REST_Units::Time, 1e-6 / 3600. / 24.);
+AddUnit(days, REST_Units::Time, 1e-6 / 3600. / 24.);
+AddUnit(mon, REST_Units::Time, 1e-6 / 3600. / 24. / 30);
+AddUnit(months, REST_Units::Time, 1e-6 / 3600. / 24. / 30);
+AddUnit(yr, REST_Units::Time, 1e-6 / 3600. / 24. / 365.25);
+AddUnit(years, REST_Units::Time, 1e-6 / 3600. / 24. / 365.25);
 
 // length unit multiplier
 AddUnit(nm, REST_Units::Length, 1e6);
diff --git a/source/framework/core/inc/TRestVersion.h b/source/framework/core/inc/TRestVersion.h
index 3cfdf91aae5c1eb584de57e4fa5857008d0437cf..eb622bc7f259e6d5a078c6e105c2c89b3fb6601a 100644
--- a/source/framework/core/inc/TRestVersion.h
+++ b/source/framework/core/inc/TRestVersion.h
@@ -12,12 +12,12 @@
  * #endif
  *
  */
-#define REST_RELEASE "2.4.1"
-#define REST_RELEASE_DATE "Sat Dec 16"
-#define REST_RELEASE_TIME "11:14:21 CET 2023"
-#define REST_RELEASE_NAME "Igor G. Irastorza"
-#define REST_GIT_COMMIT "6b9d0650"
-#define REST_VERSION_CODE 132097
+#define REST_RELEASE "2.4.3"
+#define REST_RELEASE_DATE "Fri May  3"
+#define REST_RELEASE_TIME "17:36:10 CEST 2024"
+#define REST_RELEASE_NAME "Steven Weinberg"
+#define REST_GIT_COMMIT "bc42645d"
+#define REST_VERSION_CODE 132099
 #define REST_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
 #define REST_SCHEMA_EVOLUTION "ON"
 #endif
diff --git a/source/framework/core/src/TRestAnalysisTree.cxx b/source/framework/core/src/TRestAnalysisTree.cxx
index 01d2c6622b1322f87536d3cebfbd6043f949f990..d52950b244bb14222361367e73668f5d4c42d1f2 100644
--- a/source/framework/core/src/TRestAnalysisTree.cxx
+++ b/source/framework/core/src/TRestAnalysisTree.cxx
@@ -623,6 +623,7 @@ Double_t TRestAnalysisTree::GetDblObservableValue(Int_t n) {
     }
 
     if (GetObservableType(n) == "int") return GetObservableValue<int>(n);
+    if (GetObservableType(n) == "float") return GetObservableValue<float>(n);
     if (GetObservableType(n) == "double") return GetObservableValue<double>(n);
 
     cout << "TRestAnalysisTree::GetDblObservableValue. Type " << GetObservableType(n)
diff --git a/source/framework/core/src/TRestBrowser.cxx b/source/framework/core/src/TRestBrowser.cxx
index b26c9d2a730fb229df571941f7003dba02e19fe0..16db4685ec9574b3ac58aa2327073dd8cd080d3b 100644
--- a/source/framework/core/src/TRestBrowser.cxx
+++ b/source/framework/core/src/TRestBrowser.cxx
@@ -301,7 +301,7 @@ Bool_t TRestBrowser::LoadEventId(Int_t eventID, Int_t subEventID) {
 
     if (fRestRun->GetAnalysisTree() != nullptr && fRestRun->GetAnalysisTree()->GetEntries() > 0) {
         TRestEvent* event = fRestRun->GetEventWithID(eventID, subEventID);
-        if (event != nullptr) {
+        if (event == nullptr) {
             RESTWarning << "Event ID : " << eventID << " with sub ID : " << subEventID << " not found!"
                         << RESTendl;
             return kFALSE;
diff --git a/source/framework/core/src/TRestDataSet.cxx b/source/framework/core/src/TRestDataSet.cxx
index a3d13b94d12c37b5d8f921ad601b0ae6795f2196..09c2eaa954e5b67ea105151e128f9a6e7801d52d 100644
--- a/source/framework/core/src/TRestDataSet.cxx
+++ b/source/framework/core/src/TRestDataSet.cxx
@@ -169,6 +169,26 @@
 /// [2] d.GetTree()->GetEntries()
 /// \endcode
 ///
+/// Example 3 Automatically importing a dataset using restRoot
+///
+/// \code
+/// restRoot Dataset_FinalBabyIAXO_XMM_mM_P14.root
+///
+///  REST dataset file identified. It contains a valid TRestDataSet.
+///
+///  Importing dataset /nfs/dust/iaxo/user/jgalan//Dataset_FinalBabyIAXO_XMM_mM_P14.root as `dSet`
+///
+///  The dataset is ready. You may now access the dataset using:
+///
+///   - dSet->PrintMetadata()
+///   - dSet->GetDataFrame().GetColumnNames()
+///   - dSet->GetTree()->GetEntries()
+///
+///   - dSet->GetDataFrame().Display("")->Print()
+///   - dSet->GetDataFrame().Display({"colName1,colName2"})->Print()
+/// [0]
+/// \endcode
+///
 /// ### Relevant quantities
 ///
 /// Sometimes we will be willing that our dataset contains few variables
@@ -241,6 +261,21 @@
 /// where `SolarFlux`,`GeneratorArea` and `Nsim` are the given names of
 /// the relevant quantities inside the dataset.
 ///
+/// ### Adding cuts
+///
+/// It is also possible to add cuts used to filter the data that will
+/// be stored inside the dataset. We can do that including a TRestCut
+/// definition inside the TRestDataSet.
+///
+/// For example, the following cut definition would discard entries
+/// with unexpected values inside the specified column, `process_status`.
+///
+/// \code
+///    <TRestCut>
+///            <cut name="goodData" value="!TMath::IsNaN(process_status)"/>
+///    </TRestCut>
+/// \endcode
+///
 ///----------------------------------------------------------------------
 ///
 /// REST-for-Physics - Software for Rare Event Searches Toolkit
@@ -347,30 +382,40 @@ void TRestDataSet::GenerateDataSet() {
         ROOT::DisableImplicitMT();
 
     RESTInfo << "Initializing dataset" << RESTendl;
-    fDataSet = ROOT::RDataFrame("AnalysisTree", fFileSelection);
+    fDataFrame = ROOT::RDataFrame("AnalysisTree", fFileSelection);
 
     RESTInfo << "Making cuts" << RESTendl;
-    fDataSet = MakeCut(fCut);
+    fDataFrame = MakeCut(fCut);
 
     // Adding new user columns added to the dataset
     for (const auto& [cName, cExpression] : fColumnNameExpressions) {
         RESTInfo << "Adding column to dataset: " << cName << RESTendl;
         finalList.emplace_back(cName);
-        fDataSet = DefineColumn(cName, cExpression);
+        fDataFrame = DefineColumn(cName, cExpression);
     }
 
+    RegenerateTree(finalList);
+
+    RESTInfo << " - Dataset generated!" << RESTendl;
+}
+
+///////////////////////////////////////////////
+/// \brief It regenerates the tree so that it is an exact copy of the present DataFrame
+///
+void TRestDataSet::RegenerateTree(std::vector<std::string> finalList) {
     RESTInfo << "Generating snapshot." << RESTendl;
     std::string user = getenv("USER");
     std::string fOutName = "/tmp/rest_output_" + user + ".root";
-    fDataSet.Snapshot("AnalysisTree", fOutName, finalList);
+    if (!finalList.empty())
+        fDataFrame.Snapshot("AnalysisTree", fOutName, finalList);
+    else
+        fDataFrame.Snapshot("AnalysisTree", fOutName);
 
     RESTInfo << "Re-importing analysis tree." << RESTendl;
-    fDataSet = ROOT::RDataFrame("AnalysisTree", fOutName);
+    fDataFrame = ROOT::RDataFrame("AnalysisTree", fOutName);
 
     TFile* f = TFile::Open(fOutName.c_str());
     fTree = (TChain*)f->Get("AnalysisTree");
-
-    RESTInfo << " - Dataset generated!" << RESTendl;
 }
 
 ///////////////////////////////////////////////
@@ -391,15 +436,12 @@ std::vector<std::string> TRestDataSet::FileSelection() {
 
     std::vector<std::string> fileNames = TRestTools::GetFilesMatchingPattern(fFilePattern);
 
-    if (!fileNames.empty()) {
-        RESTInfo << "TRestDataSet::FileSelection. Starting file selection." << RESTendl;
-        RESTInfo << "Total files : " << fileNames.size() << RESTendl;
-        RESTInfo << "This process may take long computation time in case there are many files." << RESTendl;
-    }
+    RESTInfo << "TRestDataSet::FileSelection. Starting file selection." << RESTendl;
+    RESTInfo << "Total files : " << fileNames.size() << RESTendl;
+    RESTInfo << "This process may take long computation time in case there are many files." << RESTendl;
 
     fTotalDuration = 0;
-    std::cout << "Total files : " << fileNames.size() << std::endl;
-    std::cout << "Processing file selection .";
+    std::cout << "Processing file selection.";
     int cnt = 1;
     for (const auto& file : fileNames) {
         if (cnt % 100 == 0) {
@@ -413,6 +455,7 @@ std::vector<std::string> TRestDataSet::FileSelection() {
         double runEnd = run.GetEndTimestamp();
 
         if (runStart < time_stamp_start || runEnd > time_stamp_end) {
+            RESTInfo << "Rejecting file out of date range: " << file << RESTendl;
             continue;
         }
 
@@ -485,13 +528,31 @@ std::vector<std::string> TRestDataSet::FileSelection() {
 }
 
 ///////////////////////////////////////////////
-/// \brief This function apply a TRestCut to the dataframe
+/// \brief This method returns a RDataFrame node with the number of
+/// samples inside the dataset by selecting a range. It will not
+/// modify internally the dataset. See ApplyRange to modify internally
+/// the dataset.
+///
+ROOT::RDF::RNode TRestDataSet::Range(size_t from, size_t to) { return fDataFrame.Range(from, to); }
+
+///////////////////////////////////////////////
+/// \brief This method reduces the number of samples inside the
+/// dataset by selecting a range.
+///
+ROOT::RDF::RNode TRestDataSet::ApplyRange(size_t from, size_t to) {
+    fDataFrame = fDataFrame.Range(from, to);
+    RegenerateTree();
+    return fDataFrame;
+}
+
+///////////////////////////////////////////////
+/// \brief This function applies a TRestCut to the dataframe
 /// and returns a dataframe with the applied cuts. Note that
 /// the cuts are not applied directly to the dataframe on
-/// TRestDataSet, to do so you should do fDataSet = MakeCut(fCut);
+/// TRestDataSet, to do so you should do fDataFrame = MakeCut(fCut);
 ///
 ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) {
-    auto df = fDataSet;
+    auto df = fDataFrame;
 
     if (cut == nullptr) return df;
 
@@ -528,6 +589,20 @@ ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) {
     return df;
 }
 
+///////////////////////////////////////////////
+/// \brief It returns the number of entries found inside fDataFrame
+/// and prints out a warning if the number of entries inside the
+/// tree is not the same.
+///
+size_t TRestDataSet::GetEntries() {
+    auto nEntries = fDataFrame.Count();
+    if (*nEntries == (long long unsigned int)GetTree()->GetEntries()) return *nEntries;
+    RESTWarning << "TRestDataSet::GetEntries. Number of tree entries is not the same as RDataFrame entries."
+                << RESTendl;
+    RESTWarning << "Returning RDataFrame entries" << RESTendl;
+    return *nEntries;
+}
+
 ///////////////////////////////////////////////
 /// \brief This function will add a new column to the RDataFrame using
 /// the same scheme as the usual RDF::Define method, but it will on top of
@@ -541,7 +616,7 @@ ROOT::RDF::RNode TRestDataSet::MakeCut(const TRestCut* cut) {
 /// \endcode
 ///
 ROOT::RDF::RNode TRestDataSet::DefineColumn(const std::string& columnName, const std::string& formula) {
-    auto df = fDataSet;
+    auto df = fDataFrame;
 
     std::string evalFormula = formula;
     for (auto const& [name, properties] : fQuantity)
@@ -783,10 +858,42 @@ void TRestDataSet::InitFromConfigFile() {
 /// Snapshot of the current dataset, i.e. in standard TTree format, together with a copy
 /// of the TRestDataSet instance that contains the conditions used to generate the dataset.
 ///
-void TRestDataSet::Export(const std::string& filename) {
+void TRestDataSet::Export(const std::string& filename, std::vector<std::string> excludeColumns) {
     RESTInfo << "Exporting dataset" << RESTendl;
+
+    std::vector<std::string> columns = fDataFrame.GetColumnNames();
+    if (!excludeColumns.empty()) {
+        columns.erase(std::remove_if(columns.begin(), columns.end(),
+                                     [&excludeColumns](std::string elem) {
+                                         return std::find(excludeColumns.begin(), excludeColumns.end(),
+                                                          elem) != excludeColumns.end();
+                                     }),
+                      columns.end());
+
+        RESTInfo << "Re-Generating snapshot." << RESTendl;
+        std::string user = getenv("USER");
+        std::string fOutName = "/tmp/rest_output_" + user + ".root";
+        fDataFrame.Snapshot("AnalysisTree", fOutName, columns);
+
+        RESTInfo << "Re-importing analysis tree." << RESTendl;
+        fDataFrame = ROOT::RDataFrame("AnalysisTree", fOutName);
+
+        TFile* f = TFile::Open(fOutName.c_str());
+        fTree = (TChain*)f->Get("AnalysisTree");
+    }
+
     if (TRestTools::GetFileNameExtension(filename) == "txt" ||
         TRestTools::GetFileNameExtension(filename) == "csv") {
+        if (excludeColumns.empty()) {
+            RESTInfo << "Re-Generating snapshot." << RESTendl;
+            std::string user = getenv("USER");
+            std::string fOutName = "/tmp/rest_output_" + user + ".root";
+            fDataFrame.Snapshot("AnalysisTree", fOutName);
+
+            TFile* f = TFile::Open(fOutName.c_str());
+            fTree = (TChain*)f->Get("AnalysisTree");
+        }
+
         std::vector<std::string> dataTypes;
         for (int n = 0; n < fTree->GetListOfBranches()->GetEntries(); n++) {
             std::string bName = fTree->GetListOfBranches()->At(n)->GetName();
@@ -845,7 +952,7 @@ void TRestDataSet::Export(const std::string& filename) {
         fprintf(f, "###\n");
         fprintf(f, "### Data starts here\n");
 
-        auto obsNames = fDataSet.GetColumnNames();
+        auto obsNames = fDataFrame.GetColumnNames();
         std::string obsListStr = "";
         for (const auto& l : obsNames) {
             if (!obsListStr.empty()) obsListStr += ":";
@@ -873,15 +980,18 @@ void TRestDataSet::Export(const std::string& filename) {
 
         return;
     } else if (TRestTools::GetFileNameExtension(filename) == "root") {
-        fDataSet.Snapshot("AnalysisTree", filename);
+        fDataFrame.Snapshot("AnalysisTree", filename);
 
         TFile* f = TFile::Open(filename.c_str(), "UPDATE");
-        this->Write();
+        std::string name = this->GetName();
+        if (name.empty()) name = "mock";
+        this->Write(name.c_str());
         f->Close();
     } else {
         RESTWarning << "TRestDataSet::Export. Extension " << TRestTools::GetFileNameExtension(filename)
                     << " not recognized" << RESTendl;
     }
+    RESTInfo << "Dataset generated: " << filename << RESTendl;
 }
 
 ///////////////////////////////////////////////
@@ -903,6 +1013,7 @@ TRestDataSet& TRestDataSet::operator=(TRestDataSet& dS) {
     fFilterLowerThan = dS.GetFilterLowerThan();
     fFilterEqualsTo = dS.GetFilterEqualsTo();
     fQuantity = dS.GetQuantity();
+    fColumnNameExpressions = dS.GetAddedColumns();
     fTotalDuration = dS.GetTotalTimeInSeconds();
     fCut = dS.GetCut();
 
@@ -969,7 +1080,7 @@ void TRestDataSet::Import(const std::string& fileName) {
     else
         ROOT::DisableImplicitMT();
 
-    fDataSet = ROOT::RDataFrame("AnalysisTree", fileName);
+    fDataFrame = ROOT::RDataFrame("AnalysisTree", fileName);
 
     fTree = (TChain*)file->Get("AnalysisTree");
 }
@@ -1035,7 +1146,7 @@ void TRestDataSet::Import(std::vector<std::string> fileNames) {
     }
 
     RESTInfo << "Opening list of files. First file: " << fileNames[0] << RESTendl;
-    fDataSet = ROOT::RDataFrame("AnalysisTree", fileNames);
+    fDataFrame = ROOT::RDataFrame("AnalysisTree", fileNames);
 
     if (fTree != nullptr) {
         delete fTree;
diff --git a/source/framework/core/src/TRestDataSetPlot.cxx b/source/framework/core/src/TRestDataSetPlot.cxx
index ec25cf6864dc42cf4698a8ac316b0af5acf63699..d65e118aaf79acd9eed4f898b026c74eb32c5ea9 100644
--- a/source/framework/core/src/TRestDataSetPlot.cxx
+++ b/source/framework/core/src/TRestDataSetPlot.cxx
@@ -91,8 +91,12 @@
 /// * **precision**: Precision for the values to be written.
 /// * **value**: If true/ON panel is displayed, otherwise is ignored
 /// Different keys are provided: `metadata` is meant for the metadata info inside the
-/// TRestDataSet, `variable` for a predefined variable e.g. rate and `observable` for an
-/// observable value. All the keys have the same structure which is detailed below:
+/// TRestDataSet (as a RelevantQuantity), `variable` for a predefined variable e.g. rate,
+/// `observable` for an observable value and `expression` for a mathematical expression
+/// that can contain any of the previous. Note that the time-related variables _startTime_,
+/// _endTime_ and _runLength_ (then _meanRate_ too) are obtained from the TRestDataSet and
+/// not the RDataFrame of the TRestDataSet, thus they are not afected by the cuts.
+/// All the keys have the same structure which is detailed below:
 /// * **value**: Name of the metadata, variable or observable value.
 /// * **label**: String of the label that will be written before the observable value.
 /// * **units**: String with the units to be appended to the label.
@@ -107,6 +111,10 @@
 ///          <metadata value="[TRestRun::fRunTag]" label="Run tag" x="0.25" y="0.82" />
 ///          <variable value="[[entries]]" label="Entries" x="0.25" y="0.58" />
 ///          <observable value="alphaTrackAna_angle" label="Mean Angle" units="rad" x="0.25" y="0.01" />
+///          <expression value="cos(alphaTrackAna_angle)^2" label="Cosine of the mean angle" units="" x="0.25"
+///          y="0.12" />
+///          <expression value="[TRestDetector::fDriftField]*[TRestDetector::fPressure]" label="Drift field"
+///          units="V/cm" x="0.25" y="0.24" />
 ///          <addCut name="Fiducial"/>
 ///    </panel>
 /// \endcode
@@ -124,8 +132,10 @@
 /// * **timeDisplay**: If true/ON time display is set in the X axis
 /// * **norm**: Normalization constant in which the plot will be normalized, e.g. use `1` in
 /// case you want to normalize by 1.
-/// * **scale**: Multiply all the histogram bins by a constant given by scale. If you want to
-/// use the size of the bin to normalize you should write down `binSize`
+/// * **scale**: Divide all the histogram bins by a constant given by scale. You may use any
+/// mathematical expression in combination with the special keywords: `binSize`, `entries`,
+/// `runLength` (in hours) and `integral`. Adding a scale will make the histogram to be
+/// drawn with errors. To avoid this, set parameter option to 'hist' in the histogram.
 /// * **value**: If true/ON plot is displayed, otherwise is ignored
 /// * **save**: String with the name of the output file in which the plot will be saved
 /// in a separated file, several formats are supported (root, pdf, eps, jpg,...)
@@ -252,6 +262,8 @@
 /// 2023-04: First implementation of TRestDataSetPlot, based on TRestAnalysisPlot
 /// JuanAn Garcia
 ///
+/// 2024-05: Extend some functionalities, Álvaro Ezquerro
+///
 /// \class TRestDataSetPlot
 /// \author: JuanAn Garcia   e-mail: juanangp@unizar.es
 ///
@@ -389,6 +401,19 @@ void TRestDataSetPlot::ReadPanelInfo() {
 
             observable = GetNextElement(observable);
         }
+        TiXmlElement* expression = GetElement("expression", panelele);
+        while (expression != nullptr) {
+            std::array<std::string, 3> label;
+            label[0] = GetParameter("value", expression, "");
+            label[1] = GetParameter("label", expression, "");
+            label[2] = GetParameter("units", expression, "");
+            double posX = StringToDouble(GetParameter("x", expression, "0.1"));
+            double posY = StringToDouble(GetParameter("y", expression, "0.1"));
+
+            panel.expPos.push_back(std::make_pair(label, TVector2(posX, posY)));
+
+            expression = GetNextElement(expression);
+        }
 
         fPanels.push_back(panel);
         panelele = GetNextElement(panelele);
@@ -505,7 +530,50 @@ void TRestDataSetPlot::GenerateDataSetFromFilePattern(TRestDataSet& dataSet) {
     std::map<std::string, TRestDataSet::RelevantQuantity> quantity;
 
     for (auto& panel : fPanels) {
-        // Add obserbables from panel info, both variables and cuts
+        // Add metadata and observables from expression key from panel info
+        for (auto& [key, posLabel] : panel.expPos) {
+            // look for metadata which are surrounded by [] but not [[]] (variables)
+            auto&& [exp, label, units] = key;
+            std::string text = exp;
+            while (text.find_last_of('[') != std::string::npos) {
+                int squareBracketCorrector = 0;
+                size_t posOpen = text.find_last_of('[');
+                size_t posClose = text.find_first_of(']', posOpen);
+                if (posOpen != 0) {
+                    if (text[posOpen - 1] == '[') {
+                        squareBracketCorrector = 1;
+                    }
+                }
+                std::string varOrMeta = text.substr(posOpen - squareBracketCorrector,
+                                                    posClose + 1 - posOpen + 2 * squareBracketCorrector);
+                if (squareBracketCorrector == 0) {
+                    // Here varOrMeta is a metadata
+                    // Add metadata to relevant quantity from the panel info
+                    TRestDataSet::RelevantQuantity quant;
+                    quant.metadata = varOrMeta;
+                    quant.strategy = "unique";
+                    quantity[label] = quant;
+                }
+                text = Replace(text, varOrMeta, "1");
+            }
+
+            // look for observables (characterized by having a _ in the name)
+            while (text.find("_") != std::string::npos) {
+                size_t pos_ = text.find("_");
+                size_t beginning = text.find_last_of(" -+*/)(^%", pos_) + 1;
+                size_t end = text.find_first_of(" -+*/)(^%", pos_);
+                std::string obs = text.substr(beginning, end - beginning);
+                text = Replace(text, obs, "1");
+                obsList.push_back(obs);
+            }
+
+            if (!(isAExpression(text) || isANumber(text)))
+                RESTWarning << "The expression " << exp
+                            << " has not been correctly parsed into variables, metadata and observables"
+                            << RESTendl;
+        }
+
+        // Add observables from observable key of panel info
         for (auto& [key, posLabel] : panel.obsPos) {
             auto&& [obs, label, units] = key;
             obsList.push_back(obs);
@@ -535,6 +603,7 @@ void TRestDataSetPlot::GenerateDataSetFromFilePattern(TRestDataSet& dataSet) {
 ///
 void TRestDataSetPlot::PlotCombinedCanvas() {
     TRestDataSet dataSet;
+    dataSet.EnableMultiThreading(true);
 
     // Import dataSet
     dataSet.Import(fDataSetName);
@@ -586,6 +655,41 @@ void TRestDataSetPlot::PlotCombinedCanvas() {
         paramMap["[[runLength]]"] = StringWithPrecision(runLength, panel.precision);
         paramMap["[[entries]]"] = StringWithPrecision(entries, panel.precision);
         paramMap["[[meanRate]]"] = StringWithPrecision(meanRate, panel.precision);
+
+        paramMap["[[cutNames]]"] = "";
+        paramMap["[[cuts]]"] = "";
+        if (fCut) {
+            for (const auto& cut : fCut->GetCuts()) {
+                if (paramMap["[[cutNames]]"].empty())
+                    paramMap["[[cutNames]]"] += cut.GetName();
+                else
+                    paramMap["[[cutNames]]"] += "," + (std::string)cut.GetName();
+                if (paramMap["[[cuts]]"].empty())
+                    paramMap["[[cuts]]"] += cut.GetTitle();
+                else
+                    paramMap["[[cuts]]"] += " && " + (std::string)cut.GetTitle();
+            }
+        }
+
+        paramMap["[[panelCutNames]]"] = "";
+        paramMap["[[panelCuts]]"] = "";
+        if (panel.panelCut) {
+            for (const auto& cut : panel.panelCut->GetCuts()) {
+                if (paramMap["[[panelCutNames]]"].empty())
+                    paramMap["[[panelCutNames]]"] += cut.GetName();
+                else
+                    paramMap["[[panelCutNames]]"] += "," + (std::string)cut.GetName();
+                if (paramMap["[[panelCuts]]"].empty())
+                    paramMap["[[panelCuts]]"] += cut.GetTitle();
+                else
+                    paramMap["[[panelCuts]]"] += " && " + (std::string)cut.GetTitle();
+            }
+        }
+
+        RESTInfo << "Global cuts: " << paramMap["[[cuts]]"] << RESTendl;
+        if (!paramMap["[[panelCuts]]"].empty())
+            RESTInfo << "Additional panel cuts: " << paramMap["[[panelCuts]]"] << RESTendl;
+
         // Replace panel variables and generate a TLatex label
         for (const auto& [key, posLabel] : panel.variablePos) {
             auto&& [variable, label, units] = key;
@@ -633,6 +737,35 @@ void TRestDataSetPlot::PlotCombinedCanvas() {
             panel.text.emplace_back(new TLatex(posLabel.X(), posLabel.Y(), lab.c_str()));
         }
 
+        // Replace any expression and generate TLatex label
+        for (const auto& [key, posLabel] : panel.expPos) {
+            auto&& [text, label, units] = key;
+            std::string var = text;
+
+            // replace variables
+            for (const auto& [param, val] : paramMap) {
+                var = Replace(var, param, val);
+            }
+            // replace metadata
+            for (const auto& [name, quant] : quantity) {
+                var = Replace(var, name, quant.value);
+            }
+            // replace observables
+            for (const auto& obs : dataFrame.GetColumnNames()) {
+                if (var.find(obs) == std::string::npos) continue;
+                // here there should be a checking that the mean(obs) can be calculated
+                // (checking obs data type?)
+                double value = *dataFrame.Mean(obs);
+                var = Replace(var, obs, DoubleToString(value));
+            }
+            var = Replace(var, "[", "(");
+            var = Replace(var, "]", ")");
+            var = EvaluateExpression(var);
+
+            std::string lab = label + ": " + var + " " + units;
+            panel.text.emplace_back(new TLatex(posLabel.X(), posLabel.Y(), lab.c_str()));
+        }
+
         // Draw the labels inside the pad
         for (const auto& text : panel.text) {
             text->SetTextColor(1);
@@ -691,13 +824,19 @@ void TRestDataSetPlot::PlotCombinedCanvas() {
             }
             // Scale histos
             if (plots.scale != "") {
-                Double_t scale = 1.;
-                if (plots.scale == "binSize") {
-                    scale = 1. / hist.histo->GetXaxis()->GetBinWidth(1);
-                } else {
-                    scale = StringToDouble(plots.scale);
-                }
-                hist.histo->Scale(scale);
+                std::string inputScale = plots.scale;
+                double binSize = hist.histo->GetXaxis()->GetBinWidth(1);
+                double entries = hist.histo->GetEntries();
+                double runLength = dataSet.GetTotalTimeInSeconds() / 3600.;  // in hours
+                double integral = hist.histo->Integral("width");
+
+                inputScale = Replace(inputScale, "binSize", DoubleToString(binSize));
+                inputScale = Replace(inputScale, "entries", DoubleToString(entries));
+                inputScale = Replace(inputScale, "runLength", DoubleToString(runLength));
+                inputScale = Replace(inputScale, "integral", DoubleToString(integral));
+
+                std::string scale = "1./(" + inputScale + ")";
+                hist.histo->Scale(StringToDouble(EvaluateExpression(scale)));  // -1 if 'scale' isn't valid
             }
 
             // Add histos to the THStack
@@ -881,6 +1020,12 @@ void TRestDataSetPlot::PrintMetadata() {
                          << " Pos (" << posLabel.X() << ", " << posLabel.Y() << ")" << RESTendl;
         }
         RESTMetadata << "****************" << RESTendl;
+        for (auto& [key, posLabel] : panel.expPos) {
+            auto&& [obs, label, units] = key;
+            RESTMetadata << "Label Expression " << obs << ", label " << label << ", units " << units
+                         << " Pos (" << posLabel.X() << ", " << posLabel.Y() << ")" << RESTendl;
+        }
+        RESTMetadata << "****************" << RESTendl;
     }
     RESTMetadata << "-------------------" << RESTendl;
 
diff --git a/source/framework/core/src/TRestEveEventViewer.cxx b/source/framework/core/src/TRestEveEventViewer.cxx
index a99acf00f6caeb2194c87e0bacf0ceec9f541c39..ad4080e8390874938aabd0dab7a130e8dc0e0631 100644
--- a/source/framework/core/src/TRestEveEventViewer.cxx
+++ b/source/framework/core/src/TRestEveEventViewer.cxx
@@ -47,7 +47,7 @@ TRestEveEventViewer::~TRestEveEventViewer() {
 
 void InitializePerspectiveView() {
     const std::map<TString, TGLViewer::ECameraType> projectionsMap = {
-        {"Projection XY", TGLViewer::kCameraOrthoXnOY},  //
+        {"Projection XY", TGLViewer::kCameraOrthoXOY},   //
         {"Projection XZ", TGLViewer::kCameraOrthoXnOZ},  //
         {"Projection YZ", TGLViewer::kCameraOrthoZOY}    //
     };
diff --git a/source/framework/core/src/TRestEvent.cxx b/source/framework/core/src/TRestEvent.cxx
index 7acb1831f136348d5d58aa2900e976bf599038e7..191dda2f679f37a9611279d27b09b5feae9902a2 100644
--- a/source/framework/core/src/TRestEvent.cxx
+++ b/source/framework/core/src/TRestEvent.cxx
@@ -49,6 +49,8 @@
 
 #include "TRestEvent.h"
 
+#include "TRestRun.h"
+
 using namespace std;
 
 ClassImp(TRestEvent);
@@ -170,7 +172,11 @@ void TRestEvent::RestartPad(Int_t nElements) {
 
 void TRestEvent::InitializeWithMetadata(TRestRun* run) { Initialize(); }
 
-void TRestEvent::InitializeReferences(TRestRun* run) { fRun = run; }
+void TRestEvent::InitializeReferences(TRestRun* run) {
+    fRun = run;
+    SetRunOrigin(fRun->GetRunNumber());
+    SetSubRunOrigin(fRun->GetSubRunNumber());
+}
 
 //////////////////////////////////////////////////////////////////////////
 /// Run to print event data info on console
diff --git a/source/framework/core/src/TRestGDMLParser.cxx b/source/framework/core/src/TRestGDMLParser.cxx
index e5076430308b6efa5ec442d4e4bf540d6a6c48ff..b0023d864274ae533508f4cbfb9e54c45686dd49 100644
--- a/source/framework/core/src/TRestGDMLParser.cxx
+++ b/source/framework/core/src/TRestGDMLParser.cxx
@@ -1,9 +1,6 @@
 #include "TRestGDMLParser.h"
 
 #include <filesystem>
-#ifdef __APPLE__
-#include <unistd.h>
-#endif
 
 using namespace std;
 
diff --git a/source/framework/core/src/TRestMetadata.cxx b/source/framework/core/src/TRestMetadata.cxx
index 718f62daab1d0c312ebc87ee45901f2f93d12d64..8a416e43895c4fa5dbaf03c5322e7769d131bdbd 100644
--- a/source/framework/core/src/TRestMetadata.cxx
+++ b/source/framework/core/src/TRestMetadata.cxx
@@ -512,6 +512,28 @@ TRestMetadata::TRestMetadata() : RESTendl(this) {
 #endif
 }
 
+TRestMetadata::TRestMetadata(const TRestMetadata&) : RESTendl(this) {
+    fStore = true;
+    fElementGlobal = nullptr;
+    fElement = nullptr;
+    fVerboseLevel = gVerbose;
+    fVariables.clear();
+    fConstants.clear();
+    fHostmgr = nullptr;
+
+    fConfigFileName = "null";
+    configBuffer = "";
+    RESTMetadata.setlength(100);
+
+#ifdef WIN32
+    fOfficialRelease = true;
+    fCleanState = true;
+#else
+    if (TRestTools::Execute("rest-config --release") == "Yes") fOfficialRelease = true;
+    if (TRestTools::Execute("rest-config --clean") == "Yes") fCleanState = true;
+#endif
+}
+
 ///////////////////////////////////////////////
 /// \brief constructor
 ///
@@ -1262,7 +1284,7 @@ void TRestMetadata::ExpandIncludeFile(TiXmlElement* e) {
             url = _filename;
         }
 
-        filename = TRestTools::DownloadRemoteFile(url);
+        filename = TRestTools::DownloadRemoteFile(url, true);
     } else {
         filename = SearchFile(_filename);
     }
@@ -2549,6 +2571,8 @@ void TRestMetadata::ReadOneParameter(string name, string value) {
                         Double_t valueY = REST_Units::ConvertValueToRESTUnits(value.Y(), unit);
                         Double_t valueZ = REST_Units::ConvertValueToRESTUnits(value.Z(), unit);
                         *(TVector3*)datamember = TVector3(valueX, valueY, valueZ);
+                    } else if (datamember.type == "string") {
+                        // We just ignore this case
                     } else {
                         RESTWarning << this->ClassName() << " find unit definition in parameter: " << name
                                     << ", but the corresponding data member doesn't support it. Data "
@@ -2678,3 +2702,18 @@ void TRestMetadata::Merge(const TRestMetadata& metadata) {
         fName = metadata.GetName();
     }
 }
+
+UInt_t TRestMetadata::GetVersionMajor() const {
+    TString major = fVersion(0, fVersion.First('.'));
+    return major.Atoi();
+}
+
+UInt_t TRestMetadata::GetVersionMinor() const {
+    TString minor = fVersion(fVersion.First('.') + 1, fVersion.Last('.'));
+    return minor.Atoi();
+}
+
+UInt_t TRestMetadata::GetVersionPatch() const {
+    TString patch = fVersion(fVersion.Last('.') + 1, fVersion.Length());
+    return patch.Atoi();
+}
diff --git a/source/framework/core/src/TRestProcessRunner.cxx b/source/framework/core/src/TRestProcessRunner.cxx
index 85a7efbd0f5abbaa9b1e4a09907d1be28ca07b45..2ff4d4ff08fd5a55b47d435136aa01b2067484f8 100644
--- a/source/framework/core/src/TRestProcessRunner.cxx
+++ b/source/framework/core/src/TRestProcessRunner.cxx
@@ -52,14 +52,14 @@ high_resolution_clock::time_point tS, tE;
 #endif
 
 int ncalculated = 10;
-Long64_t bytesReaded_last = 0;
-Double_t prog_last = 0;
-Int_t prog_last_printed = 0;
+Long64_t bytesReadLast = 0;
+Double_t progressLast = 0;
+Int_t progressLastPrinted = 0;
 vector<Long64_t> bytesAdded(ncalculated, 0);
 vector<Double_t> progAdded(ncalculated, 0);
-int poscalculated = 0;
+int positionCalculated = 0;
 int printInterval = 200000;  // 0.2s
-int inputtreeentries = 0;
+int inputTreeEntries = 0;
 
 ClassImp(TRestProcessRunner);
 
@@ -428,7 +428,7 @@ void TRestProcessRunner::RunProcess() {
     fProcessedEvents = 0;
     fRunInfo->ResetEntry();
     fRunInfo->SetCurrentEntry(fFirstEntry);
-    inputtreeentries = fRunInfo->GetEntries();
+    inputTreeEntries = fRunInfo->GetEntries();
 
     // set root mutex
     //!!!!!!!!!!!!Important!!!!!!!!!!!!
@@ -1087,10 +1087,12 @@ void TRestProcessRunner::PrintProcessedEvents(Int_t rateE) {
         double progspeed = progsum / ncalculated / printInterval * 1000000;
 
         double prog = 0;
-        if (fEventsToProcess == REST_MAXIMUM_EVENTS && fRunInfo->GetFileProcess() != nullptr)
+        if (fRunInfo->GetFeminosDaqTotalEvents() > 0) {
+            prog = fProcessedEvents / (double)fRunInfo->GetFeminosDaqTotalEvents() * 100;
+        } else if (fEventsToProcess == REST_MAXIMUM_EVENTS && fRunInfo->GetFileProcess() != nullptr)
         // Nevents is unknown, reading external data file
         {
-            prog = fRunInfo->GetBytesReaded() / (double)fRunInfo->GetTotalBytes() * 100;
+            prog = fRunInfo->GetBytesRead() / (double)fRunInfo->GetTotalBytes() * 100;
         } else if (fRunInfo->GetFileProcess() != nullptr)
         // Nevents is known, reading external data file
         {
@@ -1098,15 +1100,13 @@ void TRestProcessRunner::PrintProcessedEvents(Int_t rateE) {
         } else if (fEventsToProcess == REST_MAXIMUM_EVENTS)
         // Nevents is unknown, reading root file
         {
-            prog = fRunInfo->GetCurrentEntry() / (double)inputtreeentries * 100;
-        }
-
-        else {
+            prog = fRunInfo->GetCurrentEntry() / (double)inputTreeEntries * 100;
+        } else {
             prog = fProcessedEvents / (double)fEventsToProcess * 100;
         }
 
         char* buffer = new char[500]();
-        if (fRunInfo->GetFileProcess() != nullptr) {
+        if (fRunInfo->GetFileProcess() != nullptr && speedbyte > 0) {
             sprintf(buffer, "%d Events (%.1fMB/s), ", fProcessedEvents, speedbyte / 1024 / 1024);
         } else {
             sprintf(buffer, "%d Events, ", fProcessedEvents);
@@ -1115,9 +1115,9 @@ void TRestProcessRunner::PrintProcessedEvents(Int_t rateE) {
         string s1(buffer);
 
         if (fProcStatus == kNormal) {
-            sprintf(buffer, "%.1f min ETA, (Pause: \"p\") ", (100 - prog_last) / progspeed / 60);
+            sprintf(buffer, "%.1f min ETA, (Pause: \"p\") ", (100 - progressLast) / progspeed / 60);
         } else {
-            sprintf(buffer, "%.1f min ETA, (Pause Disabled) ", (100 - prog_last) / progspeed / 60);
+            sprintf(buffer, "%.1f min ETA, (Pause Disabled) ", (100 - progressLast) / progspeed / 60);
         }
         string s2(buffer);
 
@@ -1133,22 +1133,22 @@ void TRestProcessRunner::PrintProcessedEvents(Int_t rateE) {
         delete[] buffer;
 
         if (REST_Display_CompatibilityMode) {
-            if (((int)prog) != prog_last_printed) {
+            if (((int)prog) != progressLastPrinted) {
                 cout << s1 << s2 << s3 << endl;
-                prog_last_printed = (int)prog;
+                progressLastPrinted = (int)prog;
             }
         } else if (fThreads[0]->GetVerboseLevel() < TRestStringOutput::REST_Verbose_Level::REST_Debug) {
             printf("%s", (s1 + s2 + s3 + "\r").c_str());
             fflush(stdout);
         }
 
-        bytesAdded[poscalculated] = fRunInfo->GetBytesReaded() - bytesReaded_last;
-        bytesReaded_last = fRunInfo->GetBytesReaded();
-        progAdded[poscalculated] = prog - prog_last;
-        prog_last = prog;
+        bytesAdded[positionCalculated] = fRunInfo->GetBytesRead() - bytesReadLast;
+        bytesReadLast = fRunInfo->GetBytesRead();
+        progAdded[positionCalculated] = prog - progressLast;
+        progressLast = prog;
 
-        poscalculated++;
-        if (poscalculated >= ncalculated) poscalculated -= ncalculated;
+        positionCalculated++;
+        if (positionCalculated >= ncalculated) positionCalculated -= ncalculated;
     }
 }
 
diff --git a/source/framework/core/src/TRestRun.cxx b/source/framework/core/src/TRestRun.cxx
index 31f4da4e453bb3b76a9781c39ea1e7677bd6bf5c..af011ab61c97dfbb863adfb2fad391435fe3ec98 100644
--- a/source/framework/core/src/TRestRun.cxx
+++ b/source/framework/core/src/TRestRun.cxx
@@ -515,9 +515,8 @@ void TRestRun::ReadInputFileTrees() {
 
             if (fNFilesSplit > 0) {  // fNFilesSplit=1: split to 1 additional file
                 RESTEssential << "Linking analysis tree from split data files" << RESTendl;
-                fAnalysisTree =
-                    (TRestAnalysisTree*)
-                        fAnalysisTree->Clone();  // we must make a copy to have TBrowser correctly browsed.
+                fAnalysisTree = (TRestAnalysisTree*)fAnalysisTree->Clone();  // we must make a copy to have
+                                                                             // TBrowser correctly browsed.
                 for (int i = 1; i <= fNFilesSplit; i++) {
                     string filename = fInputFile->GetName() + (string) "." + ToString(i);
                     RESTInfo << filename << " --> ";
@@ -1368,7 +1367,8 @@ Long64_t TRestRun::GetEntries() const {
     if (fAnalysisTree != nullptr) {
         return fAnalysisTree->GetEntries();
     }
-    return REST_MAXIMUM_EVENTS;
+    return fEntriesSaved;
+    //   return REST_MAXIMUM_EVENTS;
 }
 
 // Getters
diff --git a/source/framework/core/src/TRestSystemOfUnits.cxx b/source/framework/core/src/TRestSystemOfUnits.cxx
index ef01c59fc285684e04696315b821b9ab4b40fa38..ad8a58ea45cd7d319fe1b493f7cffab00f47164d 100644
--- a/source/framework/core/src/TRestSystemOfUnits.cxx
+++ b/source/framework/core/src/TRestSystemOfUnits.cxx
@@ -1,3 +1,25 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see http://gifna.unizar.es/trex                  *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see http://www.gnu.org/licenses/.                             *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
 #include <iostream>
 #include <limits>
 
@@ -45,10 +67,16 @@ map<string, pair<int, double>> __ListOfRESTUnits;  // name, {type, scale}
 ///
 /// History of developments:
 ///
-/// 2017-Nov:   First concept and implementation of REST_Units namespace.
-/// \author     Javier Galan
+/// 2016-Feb:  First concept of REST standard system of units
+///            Javier Galán
+///
+/// 2017-Aug:  Major upgrades
+///            Kaixiang Ni
 ///
+/// \class TRestSystemOfUnits
 /// \namespace REST_Units
+/// \author     Javier Galan
+/// \author     Kaixiang Ni
 ///
 /// <hr>
 namespace REST_Units {
@@ -280,7 +308,7 @@ TRestSystemOfUnits::TRestSystemOfUnits(string unitsStr) {
                         orderprefix = -1;
                     } else if (unitsStr[pos1 - 1] == '-' || unitsStr[pos1 - 1] == '*') {
                     } else {
-                        RESTWarning << "illegeal unit combiner \"" << unitsStr[pos1 - 1] << "\"" << RESTendl;
+                        RESTWarning << "illegal unit combiner \"" << unitsStr[pos1 - 1] << "\"" << RESTendl;
                     }
                 }
 
diff --git a/source/framework/core/src/startup.cpp b/source/framework/core/src/startup.cpp
index 8e248120821597a0189fa093161594116b45c16f..8bd31a4ada3194bd084d79b16e558af3f1a898b8 100644
--- a/source/framework/core/src/startup.cpp
+++ b/source/framework/core/src/startup.cpp
@@ -4,10 +4,6 @@
 #include <Windows.h>
 #endif  // WIN32
 
-#ifdef __APPLE__
-#include <unistd.h>
-#endif
-
 #include "RVersion.h"
 #include "TEnv.h"
 #include "TRestDataBase.h"
@@ -84,7 +80,7 @@ struct __REST_CONST_INIT {
         if (_REST_USERHOME == nullptr) {
             _REST_USERHOME = getenv("HOME");
         } else {
-            cout << "REST_HOME is set to " << _REST_USERHOME << endl;
+            // cout << "REST_HOME is set to " << _REST_USERHOME << endl;
             // create the directory if it does not exist
             std::filesystem::create_directories(_REST_USERHOME);
         }
diff --git a/source/framework/masks/inc/TRestPatternMask.h b/source/framework/masks/inc/TRestPatternMask.h
index 12979a56090f71d9688efe4b24269150f9e0a156..c16c6a89ac0e5231fae531db85a68f537b0c149b 100644
--- a/source/framework/masks/inc/TRestPatternMask.h
+++ b/source/framework/masks/inc/TRestPatternMask.h
@@ -29,7 +29,7 @@
 /// An abstract class used to encapsulate different mask pattern class definitions.
 class TRestPatternMask : public TRestMetadata {
    private:
-    /// It is used to introduce an offset on the pattern
+    /// It is used to introduce an offset on the pattern (not the mask, mask is always centered)
     TVector2 fOffset = TVector2(0, 0);  //<
 
     /// An angle (in radians) used to introduce a rotation to the pattern
diff --git a/source/framework/masks/inc/TRestRadialStrippedMask.h b/source/framework/masks/inc/TRestRadialStrippedMask.h
new file mode 100644
index 0000000000000000000000000000000000000000..b8d95caf2def20e7f9a48b6d1db04f468e9213ab
--- /dev/null
+++ b/source/framework/masks/inc/TRestRadialStrippedMask.h
@@ -0,0 +1,70 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef REST_TRestRadialStrippedMask
+#define REST_TRestRadialStrippedMask
+
+#include <TRestPatternMask.h>
+
+/// A class used to define a stripped mask pattern
+class TRestRadialStrippedMask : public TRestPatternMask {
+   private:
+    void Initialize() override;
+
+    /// The periodity of the stripped structure in radians
+    Double_t fStripsAngle = TMath::Pi() / 3;  //<
+
+    /// The width of the stripped structure in mm
+    Double_t fStripsThickness = 0.5;  //<
+
+    /// The spacers structure will be effective from this radius, in mm. Default is from 20 mm.
+    Double_t fInitialRadius = 20.;  //<
+
+    /// Radius of an internal circular region defined inside the fInitialRadius. If 0, there will be no region
+    Double_t fInternalRegionRadius = 0.;  //<
+
+    /// It defines the maximum number of cells/regions in each axis
+    Int_t fModulus = 10;
+
+   public:
+    virtual Int_t GetRegion(Double_t& x, Double_t& y) override;
+
+    /// It returns the gap/periodicity of the strips in degrees
+    Double_t GetStripsAngle() { return fStripsAngle * units("degrees"); }
+
+    /// It returns the thickness of the strips in mm
+    Double_t GetStripsThickness() { return fStripsThickness; }
+
+    /// It returns the modulus used to define a finite set of ids
+    Int_t GetModulus() { return fModulus; }
+
+    void PrintMetadata() override;
+    void PrintMaskMembers() override;
+    void PrintMask() override;
+
+    TRestRadialStrippedMask();
+    TRestRadialStrippedMask(const char* cfgFileName, std::string name = "");
+    ~TRestRadialStrippedMask();
+
+    ClassDefOverride(TRestRadialStrippedMask, 1);
+};
+#endif
diff --git a/source/framework/masks/src/TRestPatternMask.cxx b/source/framework/masks/src/TRestPatternMask.cxx
index 74fa9143cdbc6de2b9bcd649ac0f2e24b645467b..5388888b3a40358f46e2ddb0e37dcecb6d939ca2 100644
--- a/source/framework/masks/src/TRestPatternMask.cxx
+++ b/source/framework/masks/src/TRestPatternMask.cxx
@@ -167,7 +167,7 @@ TCanvas* TRestPatternMask::DrawMonteCarlo(Int_t nSamples) {
         delete fCanvas;
         fCanvas = NULL;
     }
-    fCanvas = new TCanvas("canv", "This is the canvas title", 1400, 1200);
+    fCanvas = new TCanvas("canv", "This is the canvas title", 1200, 1200);
     fCanvas->Draw();
 
     TPad* pad1 = new TPad("pad1", "This is pad1", 0.01, 0.02, 0.99, 0.97);
@@ -183,17 +183,19 @@ TCanvas* TRestPatternMask::DrawMonteCarlo(Int_t nSamples) {
     TRandom3* rnd = new TRandom3(0);
 
     for (int n = 0; n < nSamples; n++) {
-        Double_t x = 2.5 * (rnd->Rndm() - 0.5) * fMaskRadius;
-        Double_t y = 2.5 * (rnd->Rndm() - 0.5) * fMaskRadius;
+        Double_t xO = 2.5 * (rnd->Rndm() - 0.5) * fMaskRadius;
+        Double_t yO = 2.5 * (rnd->Rndm() - 0.5) * fMaskRadius;
+        Double_t x = xO;
+        Double_t y = yO;
 
         Int_t id = GetRegion(x, y);
 
         if (points.count(id) == 0) {
             std::vector<TVector2> a;
-            a.push_back(TVector2(x, y));
+            a.push_back(TVector2(xO, yO));
             points[id] = a;
         } else {
-            points[id].push_back(TVector2(x, y));
+            points[id].push_back(TVector2(xO, yO));
         }
     }
 
diff --git a/source/framework/masks/src/TRestRadialStrippedMask.cxx b/source/framework/masks/src/TRestRadialStrippedMask.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6985e92c3f80338976b39111e510d07ad4648ab5
--- /dev/null
+++ b/source/framework/masks/src/TRestRadialStrippedMask.cxx
@@ -0,0 +1,221 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+/////////////////////////////////////////////////////////////////////////
+/// This class defines a stripped pattern. It defines a periodicity
+/// and a thickness for the strips. The method TRestRadialStrippedMask::GetRegion
+/// will return a unique id for each region in between strips.
+///
+/// The stripped structure is centered in (0,0) and it can be shifted using
+/// the offset defined inside TRestPatternMask. The pattern will be only
+/// delimited by the limits imposed inside TRestPatternMask.
+///
+/// ### Specific stripped metadata parameters
+///
+/// * **stripsAngle**: This parameter defines the strips angular periodicity.
+/// * **stripsThickness**: The thickness of the strips.
+/// * **modulus**: A number that defines the range of ids used to identify
+/// the different regions inside the stripped pattern. If modulus is 10,
+/// then we will only be able to identify up to 10 unique regions. If a
+/// larger amount of regions is found, it will happen that two regions will
+/// be assigned the same id.
+///
+/// ### Common pattern metadata parameters
+///
+/// On top of the metadata class parameters, we may define common pattern
+/// parameters to induce an offset and rotation to the pattern.
+///
+/// * **offset**: A parameter to shift the pattern window mask.
+/// * **rotationAngle**: An angle given in radians to rotate the pattern.
+/// * **maskRadius**: A radius defining the limits of the circular mask.
+///
+/// ### Examples
+///
+/// Mask pattern RML definitions can be found inside the file
+/// `REST_PATH/examples/masks.rml`.
+///
+/// The following definition ilustrates a complete RML implementation of a
+/// TRestRadialStrippedMask.
+///
+/// \code
+///	<TRestRadialStrippedMask name="strongback" verboseLevel="warning">
+///		<parameter name="maskRadius" value="30cm" />
+///		<parameter name="offset" value="(5,5)cm" />
+///		<parameter name="rotationAngle" value="30degrees" />
+///
+///		<parameter name="stripsAngle" value="60degrees" />
+///		<parameter name="stripsThickness" value="2.5cm" />
+///	</TRestRadialStrippedMask>
+/// \endcode
+///
+/// The basic use of this class is provided by the TRestRadialStrippedMask::GetRegion
+/// method. For example:
+///
+/// \code
+///     TRestRadialStrippedMask mask("masks.rml", "radialStrips");
+///     Int_t id = mask.GetRegion( 12.5, 4.3 );
+/// 	std::cout << "Region id is : " << id << endl;
+/// \endcode
+///
+/// The following figure may be generated using the TRestPatternMask::DrawMonteCarlo
+/// method.
+///
+/// \code
+///     TRestRadialStrippedMask mask("masks.rml", "radialStrips");
+///     TCanvas *c = mask.DrawMonteCarlo(30000);
+///		c->Draw();
+///     c->Print("radialstrippedmask.png");
+/// \endcode
+///
+/// \htmlonly <style>div.image img[src="radialstrippedmask.png"]{width:500px;}</style> \endhtmlonly
+/// ![An illustration of the montecarlo mask test using DrawMonteCarlo](strippedmask.png)
+///
+///----------------------------------------------------------------------
+///
+/// REST-for-Physics - Software for Rare Event Searches Toolkit
+///
+/// History of developments:
+///
+/// 2022-05: First implementation of TRestRadialStrippedMask
+/// Javier Galan
+///
+/// \class TRestRadialStrippedMask
+/// \author: Javier Galan - javier.galan@unizar.es
+///
+/// <hr>
+///
+
+#include "TRestRadialStrippedMask.h"
+
+#include "TRandom3.h"
+
+ClassImp(TRestRadialStrippedMask);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestRadialStrippedMask::TRestRadialStrippedMask() : TRestPatternMask() { Initialize(); }
+
+/////////////////////////////////////////////
+/// \brief Constructor loading data from a config file
+///
+/// If no configuration path is defined using TRestMetadata::SetConfigFilePath
+/// the path to the config file must be specified using full path, absolute or
+/// relative.
+///
+/// The default behaviour is that the config file must be specified with
+/// full path, absolute or relative.
+///
+/// \param cfgFileName A const char* giving the path to an RML file.
+/// \param name The name of the specific metadata. It will be used to find the
+/// corresponding TRestRadialStrippedMask section inside the RML.
+///
+TRestRadialStrippedMask::TRestRadialStrippedMask(const char* cfgFileName, std::string name)
+    : TRestPatternMask(cfgFileName) {
+    Initialize();
+
+    LoadConfigFromFile(fConfigFileName, name);
+
+    if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata();
+}
+
+///////////////////////////////////////////////
+/// \brief Default destructor
+///
+TRestRadialStrippedMask::~TRestRadialStrippedMask() {}
+
+///////////////////////////////////////////////
+/// \brief Function to initialize input/output event members and define
+/// the section name
+///
+void TRestRadialStrippedMask::Initialize() {
+    SetSectionName(this->ClassName());
+    SetType("RadialStripped");
+}
+
+///////////////////////////////////////////////
+/// \brief It returns a number identifying the region where the particle
+/// with coordinates (x,y) felt in. The method returns 0 if the particle
+/// hits the pattern.
+///
+/// The particle will be counter-rotated to emulate the mask rotation
+/// using the method TRestPatternMask::ApplyCommonMaskTransformation
+///
+Int_t TRestRadialStrippedMask::GetRegion(Double_t& x, Double_t& y) {
+    if (TRestPatternMask::GetRegion(x, y)) return 0;
+
+    Double_t d = TMath::Sqrt(x * x + y * y);
+
+    if (d < fInitialRadius) {
+        if (fInternalRegionRadius > 0 && d < fInternalRegionRadius) return 1;
+
+        return 0;
+    }
+
+    TVector2 point(x, y);
+    Double_t phi = point.Phi();
+
+    /// phi determines the region where the point is found
+    Int_t region = (Int_t)(phi / fStripsAngle);
+    region = 2 + region % fMaxRegions;
+
+    Double_t angle = 0;
+    /// Checking if we hit an arm
+    while (angle < 2 * TMath::Pi()) {
+        if (point.Y() < fStripsThickness / 2. && point.Y() > -fStripsThickness / 2. && point.X() >= 0)
+            return 0;
+
+        point = point.Rotate(fStripsAngle);
+        angle += fStripsAngle;
+    }
+
+    return 1 + region % fModulus;
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the complete information about the metadata members from this class
+///
+void TRestRadialStrippedMask::PrintMetadata() {
+    TRestPatternMask::PrintMetadata();
+
+    PrintMaskMembers();
+    RESTMetadata << "++++" << RESTendl;
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestRingsMask,
+/// including common pattern headers, but without common metadata headers.
+///
+void TRestRadialStrippedMask::PrintMask() {
+    PrintCommonPatternMembers();
+    RESTMetadata << "----" << RESTendl;
+    PrintMaskMembers();
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestRingsMask,
+/// excluding common metadata headers.
+///
+void TRestRadialStrippedMask::PrintMaskMembers() {
+    RESTMetadata << " - Strips angle : " << fStripsAngle * units("degrees") << " degrees" << RESTendl;
+    RESTMetadata << " - Strips thickness : " << fStripsThickness << " mm" << RESTendl;
+}
diff --git a/source/framework/sensitivity/inc/TRestComponent.h b/source/framework/sensitivity/inc/TRestComponent.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e22722a6a77bb0984f1590ba3dd0fff905616ef
--- /dev/null
+++ b/source/framework/sensitivity/inc/TRestComponent.h
@@ -0,0 +1,196 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef REST_TRestComponent
+#define REST_TRestComponent
+
+#include <TCanvas.h>
+#include <THn.h>
+#include <TRandom3.h>
+
+#include <ROOT/RDataFrame.hxx>
+#include <ROOT/RVec.hxx>
+
+#include "TRestDataSet.h"
+#include "TRestMetadata.h"
+#include "TRestResponse.h"
+
+/// It defines a background/signal model distribution in a given parameter space (tipically x,y,en)
+class TRestComponent : public TRestMetadata {
+   protected:
+    /// It defines the component type (unknown/signal/background)
+    std::string fNature = "unknown";  //<
+
+    /// A list with the branches that will be used to create the distribution space
+    std::vector<std::string> fVariables;  //<
+
+    /// The range of each of the variables used to create the PDF distribution
+    std::vector<TVector2> fRanges;  //<
+
+    /// The number of bins in which we should divide each variable
+    std::vector<Int_t> fNbins;  //<
+
+    /// It is used to parameterize a set of distribution densities (e.g. WIMP or axion mass)
+    std::string fParameter = "";  //<
+
+    /// It defines the nodes of the parameterization (Initialized by the dataset)
+    std::vector<Double_t> fParameterizationNodes;  //<
+
+    /// It defines the first parametric node value in case of automatic parameter generation
+    Double_t fFirstParameterValue = 0;  //<
+
+    /// It defines the upper limit for the automatic parametric node values generation
+    Double_t fLastParameterValue = 0;  //<
+
+    /// It defines the increasing step for automatic parameter list generation
+    Double_t fStepParameterValue = 0;  //<
+
+    /// It true the parametric values automatically generated will grow exponentially
+    Bool_t fExponential = false;  //<
+
+    /// It is used to define the node that will be accessed for rate retrieval
+    Int_t fActiveNode = -1;  //<
+
+    /// The generated N-dimensional variable space density for a given node
+    std::vector<THnD*> fNodeDensity;  //<
+
+    /// It introduces a fixed number of samples (if 0 it will take all available samples)
+    Int_t fSamples = 0;  //<
+
+    /// Enables or disables the interpolation at TRestComponentDataSet::GetRawRate
+    Bool_t fInterpolation = true;  //<
+
+    /// A pointer to the detector response
+    TRestResponse* fResponse = nullptr;  //<
+
+    /// A precision used to select the node value with a given range defined as a fraction of the value
+    Float_t fPrecision = 0.01;  //<
+
+    /// Internal process random generator
+    TRandom3* fRandom = nullptr;  //!
+
+    /// Seed used in random generator
+    UInt_t fSeed = 0;  //<
+
+    /// A canvas for drawing the active node component
+    TCanvas* fCanvas = nullptr;  //!
+
+    Bool_t HasDensity() { return !fNodeDensity.empty(); }
+
+    /// It returns true if the node has been properly identified
+    Bool_t ValidNode(Double_t node) {
+        SetActiveNode(node);
+        if (GetActiveNode() >= 0) return true;
+        return false;
+    }
+
+    Int_t GetVariableIndex(std::string varName);
+
+    void InitFromConfigFile() override;
+
+    virtual void FillHistograms() = 0;
+
+   public:
+    void Initialize() override;
+    void RegenerateHistograms(UInt_t seed = 0);
+
+    void RegenerateParametricNodes(Double_t from, Double_t to, Double_t step, Bool_t expIncrease = false);
+
+    /// It returns true if any nodes have been defined.
+    Bool_t HasNodes() { return !fParameterizationNodes.empty(); }
+
+    virtual void RegenerateActiveNodeDensity() {}
+
+    std::string GetNature() const { return fNature; }
+    TRestResponse* GetResponse() const { return fResponse; }
+    Float_t GetPrecision() { return fPrecision; }
+    size_t GetDimensions() { return fVariables.size(); }
+    Int_t GetSamples() { return fSamples; }
+    Int_t GetActiveNode() { return fActiveNode; }
+    Double_t GetActiveNodeValue() {
+        if (fActiveNode >= 0 && fActiveNode < (Int_t)fParameterizationNodes.size())
+            return fParameterizationNodes[fActiveNode];
+        return 0;
+    }
+    std::vector<Double_t> GetParameterizationNodes() { return fParameterizationNodes; }
+
+    std::vector<std::string> GetVariables() const { return fVariables; }
+    std::vector<TVector2> GetRanges() const { return fRanges; }
+    std::vector<Int_t> GetNbins() const { return fNbins; }
+
+    Double_t GetRawRate(std::vector<Double_t> point);
+    Double_t GetTotalRate();
+    Double_t GetMaxRate();
+    Double_t GetAllNodesIntegratedRate();
+    Double_t GetNormalizedRate(std::vector<Double_t> point);
+    Double_t GetRate(std::vector<Double_t> point);
+
+    Double_t GetBinCenter(Int_t nDim, const Int_t bin);
+
+    void SetPrecision(const Float_t& pr) { fPrecision = pr; }
+
+    Int_t FindActiveNode(Double_t node);
+    Int_t SetActiveNode(Double_t node);
+    Int_t SetActiveNode(Int_t n) {
+        fActiveNode = n;
+        return fActiveNode;
+    }
+
+    void SetSamples(Int_t samples) { fSamples = samples; }
+
+    Bool_t Interpolation() { return fInterpolation; }
+    void EnableInterpolation() { fInterpolation = true; }
+    void DisableInterpolation() { fInterpolation = false; }
+
+    THnD* GetDensityForNode(Double_t value);
+    THnD* GetDensityForActiveNode();
+    THnD* GetDensity() { return GetDensityForActiveNode(); }
+
+    TH1D* GetHistogram(Double_t node, std::string varName);
+    TH2D* GetHistogram(Double_t node, std::string varName1, std::string varName2);
+    TH3D* GetHistogram(Double_t node, std::string varName1, std::string varName2, std::string varName3);
+
+    TH1D* GetHistogram(std::string varName);
+    TH2D* GetHistogram(std::string varName1, std::string varName2);
+    TH3D* GetHistogram(std::string varName1, std::string varName2, std::string varName3);
+
+    ROOT::RVecD GetRandom();
+
+    ROOT::RDF::RNode GetMonteCarloDataFrame(Int_t N = 100);
+
+    TCanvas* DrawComponent(std::vector<std::string> drawVariables, std::vector<std::string> scanVariables,
+                           Int_t binScanSize = 1, TString drawOption = "");
+
+    void LoadResponse(const TRestResponse& resp);
+
+    void PrintMetadata() override;
+
+    void PrintStatistics();
+    void PrintNodes();
+
+    TRestComponent(const char* cfgFileName, const std::string& name = "");
+    TRestComponent();
+    ~TRestComponent();
+
+    ClassDefOverride(TRestComponent, 6);
+};
+#endif
diff --git a/source/framework/sensitivity/inc/TRestComponentDataSet.h b/source/framework/sensitivity/inc/TRestComponentDataSet.h
new file mode 100644
index 0000000000000000000000000000000000000000..59e2762680cf3d509e62d33942ee294694c5344e
--- /dev/null
+++ b/source/framework/sensitivity/inc/TRestComponentDataSet.h
@@ -0,0 +1,95 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef REST_TRestComponentDataSet
+#define REST_TRestComponentDataSet
+
+#include <THn.h>
+
+#include "TRestComponent.h"
+#include "TRestDataSet.h"
+
+/// It defines a background/signal model distribution in a given parameter space (tipically x,y,en)
+class TRestComponentDataSet : public TRestComponent {
+   private:
+    /// A list with the dataset columns used to weight the distribution density and define rate
+    std::vector<std::string> fWeights;  //<
+
+    /// It defines the number of entries in the sample for each parameterization node (Initialized by the
+    /// dataset)
+    std::vector<Int_t> fNSimPerNode;  //<
+
+    /// It defines the total number of entries for each parameterization node (Initialized by the dataset)
+    std::vector<Int_t> fTotalSamples;  //<
+
+    /// The filename of the dataset used
+    std::vector<std::string> fDataSetFileNames;  //<
+
+    /// TODO we need to define multiple datasets and weigth. The weight will be used
+    /// to create a model, such as weighting different background contaminations or
+    /// different signal coupling contributions.
+
+    /// TODO Then we probably need here a `std::vector <TRestDataSet>` and another vector
+    /// with the weights (isotope activity/flux/etc).
+
+    /// The dataset used to initialize the distribution
+    TRestDataSet fDataSet;  //!
+
+    /// It helps to split large datasets when extracting the parameterization nodes
+    long long unsigned int fSplitEntries = 600000000;
+
+    /// It creates a sample subset using a range definition
+    TVector2 fDFRange = TVector2(0, 0);
+
+    /// It is true of the dataset was loaded without issues
+    Bool_t fDataSetLoaded = false;  //!
+
+    Bool_t ValidDataSet();
+
+   protected:
+    void RegenerateActiveNodeDensity() override;
+
+    std::vector<Double_t> ExtractParameterizationNodes();
+    std::vector<Int_t> ExtractNodeStatistics();
+    void FillHistograms() override;
+
+    Bool_t VariablesOk();
+    Bool_t WeightsOk();
+
+    Bool_t LoadDataSets();
+
+   public:
+    Bool_t IsDataSetLoaded() { return fDataSetLoaded; }
+
+    void PrintStatistics();
+
+    void PrintMetadata() override;
+    void Initialize() override;
+    void InitFromConfigFile() override;
+
+    TRestComponentDataSet();
+    TRestComponentDataSet(const char* cfgFileName, const std::string& name);
+    ~TRestComponentDataSet();
+
+    ClassDefOverride(TRestComponentDataSet, 4);
+};
+#endif
diff --git a/source/framework/sensitivity/inc/TRestComponentFormula.h b/source/framework/sensitivity/inc/TRestComponentFormula.h
new file mode 100644
index 0000000000000000000000000000000000000000..1913c5f3f928a03d19b503f8e5156de859376cc8
--- /dev/null
+++ b/source/framework/sensitivity/inc/TRestComponentFormula.h
@@ -0,0 +1,57 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef REST_TRestComponentFormula
+#define REST_TRestComponentFormula
+
+#include <TFormula.h>
+
+#include "TRestComponent.h"
+#include "TRestDataSet.h"
+
+/// It defines an analytical component model distribution in a given parameter space (tipically x,y,en)
+class TRestComponentFormula : public TRestComponent {
+   private:
+    /// A vector of formulas that will be added up to integrate a given rate
+    std::vector<TFormula> fFormulas;
+
+    /// The formulas should be expressed in the following units
+    std::string fFormulaUnits = "cm^-2*keV^-1";  //<
+
+   protected:
+    void InitFromConfigFile() override;
+
+    void FillHistograms() override;
+
+   public:
+    Double_t GetFormulaRate(std::vector<Double_t> point);
+
+    void PrintMetadata() override;
+
+    void Initialize() override;
+    TRestComponentFormula(const char* cfgFileName, const std::string& name);
+    TRestComponentFormula();
+    ~TRestComponentFormula();
+
+    ClassDefOverride(TRestComponentFormula, 1);
+};
+#endif
diff --git a/source/framework/sensitivity/inc/TRestExperiment.h b/source/framework/sensitivity/inc/TRestExperiment.h
new file mode 100644
index 0000000000000000000000000000000000000000..440ec351223887b9768e7121bdc4ca9d5dc8f5cd
--- /dev/null
+++ b/source/framework/sensitivity/inc/TRestExperiment.h
@@ -0,0 +1,102 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef REST_TRestExperiment
+#define REST_TRestExperiment
+
+#include <TRandom3.h>
+
+#include "TRestComponent.h"
+#include "TRestDataSet.h"
+#include "TRestMetadata.h"
+
+/// It includes a model definition and experimental data used to obtain a final experimental sensitivity
+class TRestExperiment : public TRestMetadata {
+   private:
+    /// The exposure time. If 0 it will be extracted from the tracking dataset (In us, standard REST unit)
+    Double_t fExposureTime = 0;  //<
+
+    /// A pointer to the background component
+    TRestComponent* fBackground = nullptr;  //<
+
+    /// A pointer to the signal component
+    TRestComponent* fSignal = nullptr;  //<
+
+    /// It defines the filename used to load the dataset
+    std::string fExperimentalDataSet = "";  //<
+
+    /// It contains the experimental data (should contain same columns as the components)
+    TRestDataSet fExperimentalData;  //<
+
+    /// If enabled it means that the experimental data was MC-generated
+    Bool_t fMockData = false;  //<
+
+    /// Only if it is true we will be able to calculate the LogLikelihood
+    Bool_t fDataReady = false;  //<
+
+    /// The mock dataset will be generated using the mean counts instead of a real MonteCarlo
+    Bool_t fUseAverage = false;  //<
+
+    /// It keeps track on the number of counts inside the dataset
+    Int_t fExperimentalCounts = 0;  //<
+
+    /// Internal process random generator
+    TRandom3* fRandom = nullptr;  //!
+
+    /// Seed used in random generator
+    UInt_t fSeed = 0;  //<
+
+   protected:
+    void InitFromConfigFile() override;
+
+   public:
+    void GenerateMockDataSet(Bool_t useAverage = false);
+    Int_t GetExperimentalCounts() const { return fExperimentalCounts; }
+
+    Bool_t IsMockData() const { return fMockData; }
+    Bool_t IsDataReady() const { return fDataReady; }
+
+    void SetExposureInSeconds(const Double_t exposure) { fExposureTime = exposure / units("s"); }
+    void SetSignal(TRestComponent* comp) { fSignal = comp; }
+    void SetBackground(TRestComponent* comp) { fBackground = comp; }
+
+    void SetExperimentalDataSet(const std::string& filename);
+
+    Double_t GetExposureInSeconds() const { return fExposureTime * units("s"); }
+    TRestComponent* GetBackground() const { return fBackground; }
+    TRestComponent* GetSignal() const { return fSignal; }
+    TRestDataSet GetExperimentalDataSet() const { return fExperimentalData; }
+    ROOT::RDF::RNode GetExperimentalDataFrame() const { return fExperimentalData.GetDataFrame(); }
+
+    void PrintExperimentalData() { GetExperimentalDataFrame().Display("")->Print(); }
+
+    void Initialize() override;
+
+    void PrintMetadata() override;
+
+    TRestExperiment(const char* cfgFileName, const std::string& name = "");
+    TRestExperiment();
+    ~TRestExperiment();
+
+    ClassDefOverride(TRestExperiment, 2);
+};
+#endif
diff --git a/source/framework/sensitivity/inc/TRestExperimentList.h b/source/framework/sensitivity/inc/TRestExperimentList.h
new file mode 100644
index 0000000000000000000000000000000000000000..ca0b60e0299ee89171f00818d6d42e1519d82c44
--- /dev/null
+++ b/source/framework/sensitivity/inc/TRestExperimentList.h
@@ -0,0 +1,109 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef REST_TRestExperimentList
+#define REST_TRestExperimentList
+
+#include "TRestExperiment.h"
+#include "TRestMetadata.h"
+
+/// A helper metadata class to create a list of TRestExperiment instances
+class TRestExperimentList : public TRestMetadata {
+   private:
+    /// A fullpath filename pattern helping to initialize the component files vector
+    std::string fComponentPattern = "";  //<
+
+    /// A vector with filenames containing the components
+    std::vector<std::string> fComponentFiles;  //<
+
+    /// A fullpath filename pattern helping to initialize the dataset files vector
+    std::string fDataSetPattern = "";  //<
+
+    /// A vector with filenames containing the datasets with experimental data
+    std::vector<std::string> fDataSetFilenames;  //<
+
+    /// A file where we define experiment components, exposureTime, and tracking data of each experiment
+    std::string fExperimentsFile = "";  //< Exposure/TrackingData - SignalComponent - BackgroundComponent
+
+    /// A table with the experiment file information
+    std::vector<std::vector<std::string> > fExperimentsTable;  //<
+
+    /// A vector with a list of experiments includes the background components in this model
+    std::vector<TRestExperiment*> fExperiments;  //<
+
+    /// The fusioned list of parameterization nodes found at each experiment signal
+    std::vector<Double_t> fParameterizationNodes;  //<
+
+    /// The mock dataset will be generated using the mean counts instead of a real MonteCarlo
+    Bool_t fUseAverage = false;  //<
+
+    /// If not zero this will be the common exposure time in micro-seconds (standard REST units)
+    Double_t fExposureTime = 0;
+
+    /// In case an exposure time is given it defines how to assign time to each experiment (equal/ksvz).
+    std::string fExposureStrategy = "equal";
+
+    /// The factor used on the exponential exposure time as a function of the experiment number
+    Double_t fExposureFactor = 0;
+
+    /// If not null this will be the common signal used in each experiment
+    TRestComponent* fSignal = nullptr;  //<
+
+    /// If not null this will be the common signal used in each experiment
+    TRestComponent* fBackground = nullptr;  //<
+
+   protected:
+    TRestComponent* GetComponent(std::string compName);
+
+    void InitFromConfigFile() override;
+
+   public:
+    void Initialize() override;
+
+    void SetExposure(const Double_t& exposure) { fExposureTime = exposure; }
+    void SetSignal(TRestComponent* comp) { fSignal = comp; }
+    void SetBackground(TRestComponent* comp) { fBackground = comp; }
+
+    void ExtractExperimentParameterizationNodes();
+    std::vector<Double_t> GetParameterizationNodes() { return fParameterizationNodes; }
+    void PrintParameterizationNodes();
+
+    std::vector<TRestExperiment*> GetExperiments() { return fExperiments; }
+    TRestExperiment* GetExperiment(const size_t& n) {
+        if (n >= GetNumberOfExperiments())
+            return nullptr;
+        else
+            return fExperiments[n];
+    }
+
+    size_t GetNumberOfExperiments() { return fExperiments.size(); }
+
+    void PrintMetadata() override;
+
+    TRestExperimentList(const char* cfgFileName, const std::string& name);
+
+    TRestExperimentList();
+    ~TRestExperimentList();
+
+    ClassDefOverride(TRestExperimentList, 2);
+};
+#endif
diff --git a/source/framework/sensitivity/inc/TRestResponse.h b/source/framework/sensitivity/inc/TRestResponse.h
new file mode 100644
index 0000000000000000000000000000000000000000..25bdb4ed168e86e36e7123f3224336fbd992aff6
--- /dev/null
+++ b/source/framework/sensitivity/inc/TRestResponse.h
@@ -0,0 +1,92 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef REST_TRestResponse
+#define REST_TRestResponse
+
+#include "TRestMetadata.h"
+
+/// A response matrix that might be applied to a given component inside a TRestComponent
+class TRestResponse : public TRestMetadata {
+   private:
+    /// The filename used to import the response matrix
+    std::string fFilename = "";
+
+    /// It defines the variable name for which the response should be applied to
+    std::string fVariable = "";
+
+    /// First element of the response matrix (input/incident, output/detected)
+    TVector2 fOrigin = TVector2(0, 0);
+
+    /// The resolution of the response matrix (binning)
+    Double_t fBinSize = 0.1;  //<
+
+    /// The response matrix
+    std::vector<std::vector<Float_t>> fResponseMatrix;  //<
+
+    /// Determines if the response matrix has been transposed
+    Bool_t fTransposed = false;  //<
+
+    /// It allows to decide if the returned response should be interpolated (default:false)
+    Bool_t fInterpolation = false;  //!
+
+   public:
+    void SetBinSize(Double_t bSize) { fBinSize = bSize; }
+    void SetResponseFilename(std::string responseFile) { fFilename = responseFile; }
+    void SetOrigin(const TVector2& v) { fOrigin = v; }
+    void SetVariable(const std::string& var) { fVariable = var; }
+
+    Bool_t ApplyInterpolation() { return fInterpolation; }
+    void Interpolate(Bool_t interpolate = true) { fInterpolation = interpolate; }
+
+    Double_t GetBinSize() const { return fBinSize; }
+    std::string GetResponseFilename() const { return fFilename; }
+    TVector2 GetOrigin() const { return fOrigin; }
+    std::string GetVariable() const { return fVariable; }
+
+    TVector2 GetInputRange() const {
+        return TVector2(fOrigin.X(), fOrigin.X() + fResponseMatrix[0].size() * fBinSize);
+    }
+
+    TVector2 GetOutputRange() const {
+        return TVector2(fOrigin.Y(), fOrigin.Y() + fResponseMatrix.size() * fBinSize);
+    }
+
+    void Initialize() override;
+
+    void LoadResponse(Bool_t transpose = true);
+
+    std::vector<std::pair<Double_t, Double_t>> GetResponse(Double_t input);
+
+    void PrintResponseMatrix(Int_t fromRow, Int_t toRow);
+
+    void PrintMetadata() override;
+
+    std::vector<std::vector<Float_t>> GetMatrix() const { return fResponseMatrix; }
+
+    TRestResponse(const char* cfgFileName, const std::string& name = "");
+    TRestResponse();
+    ~TRestResponse();
+
+    ClassDefOverride(TRestResponse, 1);
+};
+#endif
diff --git a/source/framework/sensitivity/inc/TRestSensitivity.h b/source/framework/sensitivity/inc/TRestSensitivity.h
new file mode 100644
index 0000000000000000000000000000000000000000..c60350f9e0bba8792d26f01d4d965985740f489f
--- /dev/null
+++ b/source/framework/sensitivity/inc/TRestSensitivity.h
@@ -0,0 +1,102 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+#ifndef REST_TRestSensitivity
+#define REST_TRestSensitivity
+
+#include "TRestExperiment.h"
+
+/// It combines a number of experimental conditions allowing to calculate a combined experimental sensitivity
+class TRestSensitivity : public TRestMetadata {
+   private:
+    /// A list of experimental conditions included to get a final sensitivity plot
+    std::vector<TRestExperiment*> fExperiments;  //!
+
+    /// The fusioned list of parameterization nodes found at each experiment signal
+    std::vector<Double_t> fParameterizationNodes;  //<
+
+    /// A vector of calculated sensitivity curves defined as a funtion of the parametric node
+    std::vector<std::vector<Double_t>> fCurves;  //<
+
+    /// A flag that will frozen adding more experiments in the future.
+    Bool_t fFrozen = false;  //<  Only needed if we add experiments by other means than RML
+
+    /// It is used to generate a histogram with the signal distribution produced with different signal samples
+    TH1D* fSignalTest = nullptr;  //<
+
+    /// A canvas to draw
+    TCanvas* fCanvas = nullptr;  //!
+
+   protected:
+    void InitFromConfigFile() override;
+
+    Double_t UnbinnedLogLikelihood(const TRestExperiment* experiment, Double_t node, Double_t g4 = 0);
+    Double_t ApproachByFactor(Double_t node, Double_t g4, Double_t chi0, Double_t target, Double_t factor);
+
+   public:
+    void Initialize() override;
+
+    void ExtractExperimentParameterizationNodes(Bool_t rescan = false);
+    std::vector<Double_t> GetParameterizationNodes() { return fParameterizationNodes; }
+    void PrintParameterizationNodes();
+
+    Double_t GetCoupling(Double_t node, Double_t sigma = 2, Double_t precision = 0.01);
+    void AddCurve(const std::vector<Double_t>& curve) { fCurves.push_back(curve); }
+    void ImportCurve(const std::vector<Double_t>& curve) { AddCurve(curve); }
+    void GenerateCurve();
+    void GenerateCurves(Int_t N);
+
+    std::vector<Double_t> GetCurve(size_t n = 0);
+    std::vector<Double_t> GetAveragedCurve();
+    std::vector<std::vector<Double_t>> GetLevelCurves(const std::vector<Double_t>& levels);
+
+    void ExportCurve(std::string fname, Double_t factor = 1.e-10, Double_t power = 0.25, int n = 0);
+    void ExportAveragedCurve(std::string fname, Double_t factor = 1.e-10, Double_t power = 0.25);
+
+    TH1D* SignalStatisticalTest(Double_t node, Int_t N);
+
+    void Freeze() { fFrozen = true; }
+
+    std::vector<TRestExperiment*> GetExperiments() { return fExperiments; }
+    TRestExperiment* GetExperiment(const size_t& n) {
+        if (n >= GetNumberOfExperiments())
+            return nullptr;
+        else
+            return fExperiments[n];
+    }
+
+    size_t GetNumberOfExperiments() { return fExperiments.size(); }
+    size_t GetNumberOfCurves() { return fCurves.size(); }
+    size_t GetNumberOfNodes() { return fParameterizationNodes.size(); }
+
+    void PrintMetadata() override;
+
+    TCanvas* DrawCurves();
+    TCanvas* DrawLevelCurves();
+
+    TRestSensitivity(const char* cfgFileName, const std::string& name = "");
+    TRestSensitivity();
+    ~TRestSensitivity();
+
+    ClassDefOverride(TRestSensitivity, 2);
+};
+#endif
diff --git a/source/framework/sensitivity/src/TRestComponent.cxx b/source/framework/sensitivity/src/TRestComponent.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c723bd37407f499e67680b2595740446d5cbd827
--- /dev/null
+++ b/source/framework/sensitivity/src/TRestComponent.cxx
@@ -0,0 +1,861 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+/////////////////////////////////////////////////////////////////////////
+/// This class allows to ...
+///
+///
+///----------------------------------------------------------------------
+///
+/// REST-for-Physics - Software for Rare Event Searches Toolkit
+///
+/// History of developments:
+///
+/// 2023-December: First implementation of TRestComponent
+/// Javier Galan
+///
+/// \class TRestComponent
+/// \author: Javier Galan (javier.galan.lacarra@cern.ch)
+///
+/// <hr>
+///
+#include "TRestComponent.h"
+
+#include <TKey.h>
+#include <TLatex.h>
+
+#include <numeric>
+
+ClassImp(TRestComponent);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestComponent::TRestComponent() {}
+
+/////////////////////////////////////////////
+/// \brief Constructor loading data from a config file
+///
+/// If no configuration path is defined using TRestMetadata::SetConfigFilePath
+/// the path to the config file must be specified using full path, absolute or
+/// relative.
+///
+/// The default behaviour is that the config file must be specified with
+/// full path, absolute or relative.
+///
+/// \param cfgFileName A const char* giving the path to an RML file.
+/// \param name The name of the specific metadata. It will be used to find the
+/// corresponding TRestAxionMagneticField section inside the RML.
+///
+TRestComponent::TRestComponent(const char* cfgFileName, const std::string& name)
+    : TRestMetadata(cfgFileName) {
+    RESTDebug << "Entering TRestComponent constructor( cfgFileName, name )" << RESTendl;
+    RESTDebug << "File: " << cfgFileName << " Name: " << name << RESTendl;
+}
+
+///////////////////////////////////////////////
+/// \brief Default destructor
+///
+TRestComponent::~TRestComponent() {}
+
+///////////////////////////////////////////////
+/// \brief It initializes the random number. We avoid to define the section name
+/// here since we will never define a TRestComponent section in our RML file,
+/// since this class is pure virtual. It will be the inherited class the
+/// responsible to define the section name.
+///
+void TRestComponent::Initialize() {
+    //   SetSectionName(this->ClassName());
+
+    /// Avoiding double initialization
+    if (!fNodeDensity.empty() && fRandom) return;
+
+    if (!fRandom) {
+        delete fRandom;
+        fRandom = nullptr;
+    }
+
+    fRandom = new TRandom3(fSeed);
+    fSeed = fRandom->TRandom::GetSeed();
+
+    if (fStepParameterValue > 0) {
+        RegenerateParametricNodes(fFirstParameterValue, fLastParameterValue, fStepParameterValue,
+                                  fExponential);
+    } else {
+        if (!fParameterizationNodes.empty()) FillHistograms();
+    }
+}
+
+/////////////////////////////////////////////
+/// \brief It will produce a histogram with the distribution defined using the
+/// variables and the weights for each of the parameter nodes.
+///
+/// fPrecision is used to define the active node
+///
+void TRestComponent::RegenerateHistograms(UInt_t seed) {
+    fNodeDensity.clear();
+
+    fSeed = seed;
+    FillHistograms();
+}
+
+/////////////////////////////////////////////
+/// \brief It allows to produce a parameter nodes list providing the initial
+/// value, the final value and the step. We might chose the step growing in
+/// linear increase steps or exponential. Linear is the default value.
+///
+void TRestComponent::RegenerateParametricNodes(Double_t from, Double_t to, Double_t step,
+                                               Bool_t expIncrease) {
+    fStepParameterValue = step;
+    fFirstParameterValue = from;
+    fLastParameterValue = to;
+    fExponential = expIncrease;
+
+    fParameterizationNodes.clear();
+
+    if (expIncrease) {
+        for (double p = from; p < to; p *= step) fParameterizationNodes.push_back(p);
+    } else {
+        for (double p = from; p < to; p += step) fParameterizationNodes.push_back(p);
+    }
+
+    if (fParameterizationNodes.empty()) return;
+    RegenerateHistograms(fSeed);
+}
+
+///////////////////////////////////////////
+/// \brief It returns the position of the fVariable element for the variable
+/// name given by argument.
+///
+Int_t TRestComponent::GetVariableIndex(std::string varName) {
+    int n = 0;
+    for (const auto& v : fVariables) {
+        if (v == varName) return n;
+        n++;
+    }
+
+    return -1;
+}
+
+///////////////////////////////////////////////
+/// \brief It returns the intensity/rate (in seconds) corresponding to the
+/// generated distribution or formula evaluated at the position of the parameter
+/// space given by point.
+///
+/// The response matrix (if defined) will be used to convolute the expected rate.
+/// The TRestResponse metadata class defines the variable where the response will
+/// be applied.
+///
+Double_t TRestComponent::GetRate(std::vector<Double_t> point) {
+    if (!fResponse) {
+        return GetRawRate(point);
+    }
+
+    std::string responseVariable = fResponse->GetVariable();
+    Int_t respVarIndex = GetVariableIndex(responseVariable);
+
+    if (respVarIndex == -1) {
+        RESTError << "The response variable `" << responseVariable << "`, defined inside TRestResponse,"
+                  << RESTendl;
+        RESTError << "could not be found inside the component." << RESTendl;
+        RESTError << "Please, check the component variable names." << RESTendl;
+        return 0;
+    }
+
+    std::vector<std::pair<Double_t, Double_t> > response = fResponse->GetResponse(point[respVarIndex]);
+
+    Double_t rate = 0;
+    for (const auto& resp : response) {
+        std::vector<Double_t> newPoint = point;
+        newPoint[respVarIndex] = resp.first;
+        rate += resp.second * GetRawRate(newPoint);
+    }
+
+    return rate;
+}
+
+///////////////////////////////////////////////
+/// \brief It returns the intensity/rate (in seconds) corresponding to the
+/// generated distribution or formula evaluated at the position of the parameter
+/// space given by point.
+///
+/// The rate returned by the TRestComponent::GetRawRate method will be normalized
+/// to the corresponding parameter space. Thus, if the parameter consists of
+/// 2-spatial dimensions and 1-energy dimension, the returned rate will be
+/// expressed in standard REST units as, s-1 mm-2 keV-1.
+///
+/// The returned value may be recovered back with the desired units using
+/// the REST_Units namespace.
+///
+/// \code
+/// component->GetNormalizedRate( {0,0,0} ) * units("cm^-2*keV^-1")
+/// \endcode
+///
+/// The response matrix (if defined) will be used to convolute the expected rate.
+/// The TRestResponse metadata class defines the variable where the response will
+/// be applied.
+///
+Double_t TRestComponent::GetNormalizedRate(std::vector<Double_t> point) {
+    Double_t normFactor = 1;
+    for (size_t n = 0; n < GetDimensions(); n++) normFactor *= fNbins[n] / (fRanges[n].Y() - fRanges[n].X());
+
+    return normFactor * GetRate(point);
+}
+
+///////////////////////////////////////////////
+/// \brief It returns the intensity/rate (in seconds) corresponding to the
+/// generated distribution or formula evaluated at the position of the parameter
+/// space given by point. The returned rate is integrated to the granularity
+/// of the parameter space (cell size). To get a normalized rate use
+/// TRestComponent::GetNormalizedRate.
+///
+/// The size of the point vector must have the same dimension as the dimensions
+/// of the distribution.
+///
+/// If interpolation is enabled (which is disabled by default) the rate will be
+/// evaluated using interpolation with neighbour histogram cells.
+///
+/// Interpolation technique extracted from:
+/// https://math.stackexchange.com/questions/1342364/formula-for-n-dimensional-linear-interpolation
+///
+/// 𝑓(𝑥0,𝑥1,𝑥2)=𝐴000(1−𝑥0)(1−𝑥1)(1−𝑥2)+𝐴001𝑥0(1−𝑥1)(1−𝑥2)+𝐴010(1−𝑥0)𝑥1(1−𝑥2)⋯+𝐴111𝑥0𝑥1𝑥
+///
+Double_t TRestComponent::GetRawRate(std::vector<Double_t> point) {
+    if (point.size() != GetDimensions()) {
+        RESTError << "The size of the point given is : " << point.size() << RESTendl;
+        RESTError << "The density distribution dimensions are : " << GetDimensions() << RESTendl;
+        RESTError << "Point must have the same dimension as the distribution" << RESTendl;
+        return 0;
+    }
+
+    if (!HasNodes() && !HasDensity()) {
+        RESTError << "TRestComponent::GetRawRate. The component has no nodes!" << RESTendl;
+        RESTError << "Try calling TRestComponent::Initialize" << RESTendl;
+
+        RESTInfo << "Trying to initialize" << RESTendl;
+        Initialize();
+        if (HasNodes())
+            RESTInfo << "Sucess!" << RESTendl;
+        else
+            return 0;
+    }
+
+    if (HasNodes() && fActiveNode == -1) {
+        RESTError << "TRestComponent::GetRawRate. Active node has not been defined" << RESTendl;
+        return 0;
+    }
+
+    for (size_t n = 0; n < point.size(); n++) {
+        // The point is outside boundaries
+        if (point[n] < fRanges[n].X() || point[n] > fRanges[n].Y()) return 0;
+    }
+
+    Int_t centerBin[GetDimensions()];
+    Double_t centralDensity = GetDensity()->GetBinContent(GetDensity()->GetBin(point.data()), centerBin);
+    if (!Interpolation()) return centralDensity;
+
+    std::vector<Int_t> direction;
+    std::vector<Double_t> nDist;
+    for (size_t dim = 0; dim < GetDimensions(); dim++) {
+        Double_t x1 = GetBinCenter(dim, centerBin[dim] - 1);
+        Double_t x2 = GetBinCenter(dim, centerBin[dim] + 1);
+
+        if (centerBin[dim] == 1 || centerBin[dim] == fNbins[dim]) {
+            direction.push_back(0);
+            nDist.push_back(0);
+        } else if (x2 - point[dim] > point[dim] - x1) {
+            // we chose left bin (x1) since it is closer than right bin
+            direction.push_back(-1);
+            nDist.push_back(1 - 2 * (point[dim] - x1) / (x2 - x1));
+        } else {
+            direction.push_back(1);
+            nDist.push_back(1 - 2 * (x2 - point[dim]) / (x2 - x1));
+        }
+    }
+
+    // In 3-dimensions we got 8 points to interpolate
+    // In 4-dimensions we would get 16 points to interpolate
+    // ...
+    Int_t nPoints = (Int_t)TMath::Power(2, (Int_t)GetDimensions());
+
+    Double_t sum = 0;
+    for (int n = 0; n < nPoints; n++) {
+        std::vector<int> cell = REST_StringHelper::IntegerToBinary(n, GetDimensions());
+
+        Double_t weightDistance = 1;
+        int cont = 0;
+        for (const auto& c : cell) {
+            if (c == 0)
+                weightDistance *= (1 - nDist[cont]);
+            else
+                weightDistance *= nDist[cont];
+            cont++;
+        }
+
+        for (size_t k = 0; k < cell.size(); k++) cell[k] = cell[k] * direction[k] + centerBin[k];
+
+        Double_t density = GetDensity()->GetBinContent(cell.data());
+        sum += density * weightDistance;
+    }
+
+    return sum;
+}
+
+///////////////////////////////////////////////
+/// \brief This method integrates the rate to all the parameter space defined in the density function.
+/// The result will be returned in s-1.
+///
+Double_t TRestComponent::GetTotalRate() {
+    THnD* dHist = GetDensityForActiveNode();
+    if (!dHist) return 0;
+
+    Double_t integral = 0;
+    for (Int_t n = 0; n < dHist->GetNbins(); ++n) {
+        Int_t centerBin[GetDimensions()];
+        std::vector<Double_t> point;
+
+        dHist->GetBinContent(n, centerBin);
+        for (size_t d = 0; d < GetDimensions(); ++d) point.push_back(GetBinCenter(d, centerBin[d]));
+
+        Bool_t skip = false;
+        for (size_t d = 0; d < GetDimensions(); ++d) {
+            if (point[d] < fRanges[d].X() || point[d] > fRanges[d].Y()) skip = true;
+        }
+        if (!skip) integral += GetRate(point);
+    }
+
+    return integral;
+}
+
+///////////////////////////////////////////////
+/// \brief This method returns the total rate for the node that has the highest contribution
+/// The result will be returned in s-1.
+///
+Double_t TRestComponent::GetMaxRate() {
+    Double_t maxRate = 0;
+    for (size_t n = 0; n < fParameterizationNodes.size(); n++) {
+        SetActiveNode((Int_t)n);
+        Double_t rate = GetTotalRate();
+        if (rate > maxRate) maxRate = rate;
+    }
+    return maxRate;
+}
+
+///////////////////////////////////////////////
+/// \brief This method returns the integrated total rate for all the nodes
+/// The result will be returned in s-1.
+///
+Double_t TRestComponent::GetAllNodesIntegratedRate() {
+    Double_t rate = 0;
+    for (size_t n = 0; n < fParameterizationNodes.size(); n++) {
+        SetActiveNode((Int_t)n);
+        rate += GetTotalRate();
+    }
+    return rate;
+}
+
+///////////////////////////////////////////////
+/// \brief It returns the bin center of the given component dimension.
+///
+/// It required implementation since I did not find a method inside THnD. Surprising.
+///
+Double_t TRestComponent::GetBinCenter(Int_t nDim, const Int_t bin) {
+    return fRanges[nDim].X() + (fRanges[nDim].Y() - fRanges[nDim].X()) * ((double)bin - 0.5) / fNbins[nDim];
+}
+
+ROOT::RVecD TRestComponent::GetRandom() {
+    Double_t* tuple = new Double_t[GetDimensions()];
+
+    ROOT::RVecD result;
+    if (!GetDensity()) {
+        for (size_t n = 0; n < GetDimensions(); n++) result.push_back(0);
+        RESTWarning << "TRestComponent::GetRandom. Component might not be initialized! Use "
+                       "TRestComponent::Initialize()."
+                    << RESTendl;
+        return result;
+    }
+
+    GetDensity()->GetRandom(tuple);
+
+    for (size_t n = 0; n < GetDimensions(); n++) result.push_back(tuple[n]);
+    return result;
+}
+
+ROOT::RDF::RNode TRestComponent::GetMonteCarloDataFrame(Int_t N) {
+    ROOT::RDF::RNode df = ROOT::RDataFrame(N);
+
+    // Function to fill the RDataFrame using GetRandom method
+    auto fillRndm = [&]() {
+        ROOT::RVecD randomValues = GetRandom();
+        return randomValues;
+    };
+    df = df.Define("Rndm", fillRndm);
+
+    // Creating dedicated columns for each GetRandom component
+    for (size_t i = 0; i < fVariables.size(); ++i) {
+        auto varName = fVariables[i];
+        auto FillRand = [i](const ROOT::RVecD& randomValues) { return randomValues[i]; };
+        df = df.Define(varName, FillRand, {"Rndm"});
+    }
+
+    /* Excluding Rndm from df */
+    std::vector<std::string> columns = df.GetColumnNames();
+    std::vector<std::string> excludeColumns = {"Rndm"};
+    columns.erase(std::remove_if(columns.begin(), columns.end(),
+                                 [&excludeColumns](std::string elem) {
+                                     return std::find(excludeColumns.begin(), excludeColumns.end(), elem) !=
+                                            excludeColumns.end();
+                                 }),
+                  columns.end());
+
+    std::string user = getenv("USER");
+    std::string fOutName = "/tmp/rest_output_" + user + ".root";
+    df.Snapshot("AnalysisTree", fOutName, columns);
+
+    df = ROOT::RDataFrame("AnalysisTree", fOutName);
+
+    return df;
+}
+
+///////////////////////////////////////////////
+/// \brief A method allowing to draw a series of plots representing the density distributions.
+///
+/// The method will produce 1- or 2-dimensional histograms of the `drawVariables` given in the
+/// argument. A third scan variable must be provided in order to show the distribution slices
+/// along the scan variable.
+///
+/// The binScanSize argument can be used to define the binSize of the scanning variables.
+///
+TCanvas* TRestComponent::DrawComponent(std::vector<std::string> drawVariables,
+                                       std::vector<std::string> scanVariables, Int_t binScanSize,
+                                       TString drawOption) {
+    if (drawVariables.size() > 2 || drawVariables.size() == 0) {
+        RESTError << "TRestComponent::DrawComponent. The number of variables to be drawn must "
+                     "be 1 or 2!"
+                  << RESTendl;
+        return fCanvas;
+    }
+
+    if (scanVariables.size() > 2 || scanVariables.size() == 0) {
+        RESTError << "TRestComponent::DrawComponent. The number of variables to be scanned must "
+                     "be 1 or 2!"
+                  << RESTendl;
+        return fCanvas;
+    }
+
+    //// Checking the number of plots to be generated
+    std::vector<int> scanIndexes;
+    for (const auto& x : scanVariables) scanIndexes.push_back(GetVariableIndex(x));
+
+    Int_t nPlots = 1;
+    size_t n = 0;
+    for (const auto& x : scanIndexes) {
+        if (fNbins[x] % binScanSize != 0) {
+            RESTWarning << "The variable " << scanVariables[n] << " contains " << fNbins[x]
+                        << " bins and it doesnt match with a bin size " << binScanSize << RESTendl;
+            RESTWarning << "The bin size must be a multiple of the number of bins." << RESTendl;
+            RESTWarning << "Redefining bin size to 1." << RESTendl;
+            binScanSize = 1;
+        }
+        nPlots *= fNbins[x] / binScanSize;
+        n++;
+    }
+
+    /// Finding canvas division scheme
+    Int_t nPlotsX = 0;
+    Int_t nPlotsY = 0;
+
+    if (scanIndexes.size() == 2) {
+        nPlotsX = fNbins[scanIndexes[0]] / binScanSize;
+        nPlotsY = fNbins[scanIndexes[1]] / binScanSize;
+    } else {
+        nPlotsX = TRestTools::CanvasDivisions(nPlots)[1];
+        nPlotsY = TRestTools::CanvasDivisions(nPlots)[0];
+    }
+
+    RESTInfo << "Number of plots to be generated: " << nPlots << RESTendl;
+    RESTInfo << "Canvas size : " << nPlotsX << " x " << nPlotsY << RESTendl;
+
+    //// Setting up the canvas with the appropriate number of divisions
+    if (fCanvas != nullptr) {
+        delete fCanvas;
+        fCanvas = nullptr;
+    }
+
+    fCanvas = new TCanvas(this->GetName(), this->GetName(), 0, 0, nPlotsX * 640, nPlotsY * 480);
+    fCanvas->Divide(nPlotsX, nPlotsY, 0.01, 0.01);
+
+    std::vector<int> variableIndexes;
+    for (const auto& x : drawVariables) variableIndexes.push_back(GetVariableIndex(x));
+
+    for (int n = 0; n < nPlotsX; n++)
+        for (int m = 0; m < nPlotsY; m++) {
+            TPad* pad = (TPad*)fCanvas->cd(n * nPlotsY + m + 1);
+            pad->SetFixedAspectRatio(true);
+
+            THnD* hnd = GetDensity();
+
+            int binXo = binScanSize * n + 1;
+            int binXf = binScanSize * n + binScanSize;
+            int binYo = binScanSize * m + 1;
+            int binYf = binScanSize * m + binScanSize;
+
+            if (scanVariables.size() == 2) {
+                hnd->GetAxis(scanIndexes[0])->SetRange(binXo, binXf);
+                hnd->GetAxis(scanIndexes[1])->SetRange(binYo, binYf);
+            } else if (scanVariables.size() == 1) {
+                binXo = binScanSize * nPlotsY * n + binScanSize * m + 1;
+                binXf = binScanSize * nPlotsY * n + binScanSize * m + binScanSize;
+                hnd->GetAxis(scanIndexes[0])->SetRange(binXo, binXf);
+            }
+
+            if (variableIndexes.size() == 1) {
+                TH1D* h1 = hnd->Projection(variableIndexes[0]);
+                std::string hName;
+
+                if (scanIndexes.size() == 2)
+                    hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) +
+                            ") " + scanVariables[1] + "(" + IntegerToString(binYo) + ", " +
+                            IntegerToString(binYf) + ") ";
+
+                if (scanIndexes.size() == 1)
+                    hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) +
+                            ") ";
+
+                TH1D* newh = (TH1D*)h1->Clone(hName.c_str());
+                newh->SetTitle(hName.c_str());
+                newh->SetStats(false);
+                newh->GetXaxis()->SetTitle((TString)drawVariables[0]);
+                newh->SetMarkerStyle(kFullCircle);
+                newh->Draw("PLC PMC");
+
+                TString entriesStr = "Entries: " + IntegerToString(newh->GetEntries());
+                TLatex* textLatex = new TLatex(0.62, 0.825, entriesStr);
+                textLatex->SetNDC();
+                textLatex->SetTextColor(1);
+                textLatex->SetTextSize(0.05);
+                textLatex->Draw("same");
+                delete h1;
+            }
+
+            if (variableIndexes.size() == 2) {
+                TH2D* h2 = hnd->Projection(variableIndexes[0], variableIndexes[1]);
+
+                std::string hName;
+                if (scanIndexes.size() == 2)
+                    hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) +
+                            ") " + scanVariables[1] + "(" + IntegerToString(binYo) + ", " +
+                            IntegerToString(binYf) + ") ";
+
+                if (scanIndexes.size() == 1)
+                    hName = scanVariables[0] + "(" + IntegerToString(binXo) + ", " + IntegerToString(binXf) +
+                            ") ";
+
+                TH2D* newh = (TH2D*)h2->Clone(hName.c_str());
+                newh->SetStats(false);
+                newh->GetXaxis()->SetTitle((TString)drawVariables[0]);
+                newh->GetYaxis()->SetTitle((TString)drawVariables[1]);
+                newh->SetTitle(hName.c_str());
+                newh->Draw(drawOption);
+
+                TString entriesStr = "Entries: " + IntegerToString(newh->GetEntries());
+                TLatex* textLatex = new TLatex(0.62, 0.825, entriesStr);
+                textLatex->SetNDC();
+                textLatex->SetTextColor(1);
+                textLatex->SetTextSize(0.05);
+                textLatex->Draw("same");
+                delete h2;
+            }
+        }
+
+    return fCanvas;
+}
+
+///////////////////////////////////////////////
+/// \brief
+///
+void TRestComponent::LoadResponse(const TRestResponse& resp) {
+    if (fResponse) {
+        delete fResponse;
+        fResponse = nullptr;
+    }
+
+    fResponse = (TRestResponse*)resp.Clone("response");
+    if (fResponse) fResponse->LoadResponse();
+
+    fResponse->PrintMetadata();
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux
+///
+void TRestComponent::PrintMetadata() {
+    TRestMetadata::PrintMetadata();
+
+    RESTMetadata << "Component nature : " << fNature << RESTendl;
+    RESTMetadata << " " << RESTendl;
+
+    RESTMetadata << "Random seed : " << fSeed << RESTendl;
+    if (fSamples) RESTMetadata << "Samples : " << fSamples << RESTendl;
+    RESTMetadata << " " << RESTendl;
+
+    if (fVariables.size() != fRanges.size())
+        RESTWarning << "The number of variables does not match with the number of defined ranges!"
+                    << RESTendl;
+
+    else if (fVariables.size() != fNbins.size())
+        RESTWarning << "The number of variables does not match with the number of defined bins!" << RESTendl;
+    else {
+        int n = 0;
+        RESTMetadata << " === Variables === " << RESTendl;
+        for (const auto& varName : fVariables) {
+            RESTMetadata << " - Name: " << varName << " Range: (" << fRanges[n].X() << ", " << fRanges[n].Y()
+                         << ") bins: " << fNbins[n] << RESTendl;
+            n++;
+        }
+    }
+
+    if (!fParameterizationNodes.empty()) {
+        RESTMetadata << " " << RESTendl;
+        RESTMetadata << " === Parameterization === " << RESTendl;
+        RESTMetadata << "- Parameter : " << fParameter << RESTendl;
+
+        RESTMetadata << " - Number of parametric nodes : " << fParameterizationNodes.size() << RESTendl;
+        RESTMetadata << " " << RESTendl;
+        RESTMetadata << " Use : PrintNodes() for additional info" << RESTendl;
+
+        if (fStepParameterValue > 0) {
+            RESTMetadata << " " << RESTendl;
+            RESTMetadata << " Nodes were automatically generated using these parameters" << RESTendl;
+            RESTMetadata << " - First node : " << fFirstParameterValue << RESTendl;
+            RESTMetadata << " - Upper limit node : " << fLastParameterValue << RESTendl;
+            RESTMetadata << " - Increasing step : " << fStepParameterValue << RESTendl;
+            if (fExponential)
+                RESTMetadata << " - Increases exponentially" << RESTendl;
+            else
+                RESTMetadata << " - Increases linearly" << RESTendl;
+        }
+    }
+
+    if (fResponse) {
+        RESTMetadata << " " << RESTendl;
+        RESTMetadata << "A response matrix was loaded inside the component" << RESTendl;
+        RESTMetadata << "You may get more details using TRestComponent::GetResponse()->PrintMetadata()"
+                     << RESTendl;
+    }
+
+    RESTMetadata << "----" << RESTendl;
+}
+
+/////////////////////////////////////////////
+/// \brief It prints out on screen the values of the parametric node
+///
+void TRestComponent::PrintNodes() {
+    std::cout << " - Number of nodes : " << fParameterizationNodes.size() << std::endl;
+    for (const auto& x : fParameterizationNodes) std::cout << x << " ";
+    std::cout << std::endl;
+}
+
+/////////////////////////////////////////////
+/// \brief It customizes the retrieval of XML data values of this class
+///
+void TRestComponent::InitFromConfigFile() {
+    TRestMetadata::InitFromConfigFile();
+
+    auto ele = GetElement("cVariable");
+    while (ele != nullptr) {
+        std::string name = GetParameter("name", ele, "");
+        TVector2 v = Get2DVectorParameterWithUnits("range", ele, TVector2(-1, -1));
+        Int_t bins = StringToInteger(GetParameter("bins", ele, "0"));
+
+        if (name.empty() || (v.X() == -1 && v.Y() == -1) || bins == 0) {
+            RESTWarning << "TRestComponentFormula::fVariable. Problem with definition." << RESTendl;
+            RESTWarning << "Name: " << name << " range: (" << v.X() << ", " << v.Y() << ") bins: " << bins
+                        << RESTendl;
+        } else {
+            fVariables.push_back(name);
+            fRanges.push_back(v);
+            fNbins.push_back(bins);
+        }
+
+        ele = GetNextElement(ele);
+    }
+
+    if (fNbins.size() == 0)
+        RESTError << "TRestComponent::InitFromConfigFile. No cVariables where found!" << RESTendl;
+
+    if (fResponse) {
+        delete fResponse;
+        fResponse = nullptr;
+    }
+
+    fResponse = (TRestResponse*)this->InstantiateChildMetadata("Response");
+    if (fResponse) fResponse->LoadResponse();
+}
+
+/////////////////////////////////////////////
+/// \brief It returns the position of the fParameterizationNodes
+/// element for the variable name given by argument.
+///
+Int_t TRestComponent::FindActiveNode(Double_t node) {
+    int n = 0;
+    for (const auto& v : fParameterizationNodes) {
+        Double_t pUp = node * (1 + fPrecision / 2);
+        Double_t pDown = node * (1 - fPrecision / 2);
+        if (v > pDown && v < pUp) {
+            fActiveNode = n;
+            return fActiveNode;
+        }
+        n++;
+    }
+
+    return -1;
+}
+
+/////////////////////////////////////////////
+/// \brief It returns the position of the fParameterizationNodes
+/// element for the variable name given by argument.
+///
+Int_t TRestComponent::SetActiveNode(Double_t node) {
+    int n = 0;
+    for (const auto& v : fParameterizationNodes) {
+        Double_t pUp = node * (1 + fPrecision / 2);
+        Double_t pDown = node * (1 - fPrecision / 2);
+        if (v > pDown && v < pUp) {
+            fActiveNode = n;
+            return fActiveNode;
+        }
+        n++;
+    }
+
+    RESTError << "Parametric node : " << node << " was not found in component" << RESTendl;
+    RESTError << "Keeping previous node as active : " << fParameterizationNodes[fActiveNode] << RESTendl;
+    PrintNodes();
+
+    return fActiveNode;
+}
+
+/////////////////////////////////////////////
+/// \brief
+///
+THnD* TRestComponent::GetDensityForNode(Double_t node) {
+    int n = 0;
+    for (const auto& x : fParameterizationNodes) {
+        if (x == node) {
+            return fNodeDensity[n];
+        }
+        n++;
+    }
+
+    RESTError << "Parametric node : " << node << " was not found in component" << RESTendl;
+    PrintNodes();
+    return nullptr;
+}
+
+/////////////////////////////////////////////
+/// \brief
+///
+THnD* TRestComponent::GetDensityForActiveNode() {
+    if (fActiveNode >= 0) return fNodeDensity[fActiveNode];
+
+    RESTError << "The active node is invalid" << RESTendl;
+    PrintNodes();
+    return nullptr;
+}
+
+/////////////////////////////////////////////
+/// \brief It returns a 1-dimensional projected histogram for the variable names
+/// provided in the argument
+///
+TH1D* TRestComponent::GetHistogram(Double_t node, std::string varName) {
+    SetActiveNode(node);
+    return GetHistogram(varName);
+}
+
+/////////////////////////////////////////////
+/// \brief It returns a 1-dimensional projected histogram for the variable names
+/// provided in the argument. It will recover the histogram corresponding to
+/// the active node.
+///
+TH1D* TRestComponent::GetHistogram(std::string varName) {
+    if (fActiveNode < 0) return nullptr;
+
+    Int_t v1 = GetVariableIndex(varName);
+
+    if (v1 >= 0 && GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1);
+
+    return nullptr;
+}
+
+/////////////////////////////////////////////
+/// \brief It returns the 2-dimensional projected histogram for the variable names
+/// provided in the argument
+///
+TH2D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::string varName2) {
+    SetActiveNode(node);
+    return GetHistogram(varName1, varName2);
+}
+
+/////////////////////////////////////////////
+/// \brief It returns a 2-dimensional projected histogram for the variable names
+/// provided in the argument. It will recover the histogram corresponding to
+/// the active node.
+///
+TH2D* TRestComponent::GetHistogram(std::string varName1, std::string varName2) {
+    if (fActiveNode < 0) return nullptr;
+
+    Int_t v1 = GetVariableIndex(varName1);
+    Int_t v2 = GetVariableIndex(varName2);
+
+    if (v1 >= 0 && v2 >= 0)
+        if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2);
+
+    return nullptr;
+}
+
+/////////////////////////////////////////////
+/// \brief It returns the 3-dimensional projected histogram for the variable names
+/// provided in the argument
+///
+TH3D* TRestComponent::GetHistogram(Double_t node, std::string varName1, std::string varName2,
+                                   std::string varName3) {
+    SetActiveNode(node);
+    return GetHistogram(varName1, varName2, varName3);
+}
+
+/////////////////////////////////////////////
+/// \brief It returns a 3-dimensional projected histogram for the variable names
+/// provided in the argument. It will recover the histogram corresponding to
+/// the active node.
+///
+TH3D* TRestComponent::GetHistogram(std::string varName1, std::string varName2, std::string varName3) {
+    if (fActiveNode < 0) return nullptr;
+
+    Int_t v1 = GetVariableIndex(varName1);
+    Int_t v2 = GetVariableIndex(varName2);
+    Int_t v3 = GetVariableIndex(varName3);
+
+    if (v1 >= 0 && v2 >= 0 && v3 >= 0)
+        if (GetDensityForActiveNode()) return GetDensityForActiveNode()->Projection(v1, v2, v3);
+
+    return nullptr;
+}
diff --git a/source/framework/sensitivity/src/TRestComponentDataSet.cxx b/source/framework/sensitivity/src/TRestComponentDataSet.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..94d1ef7bc17decceb64b73b463cdaf6bc52bca3a
--- /dev/null
+++ b/source/framework/sensitivity/src/TRestComponentDataSet.cxx
@@ -0,0 +1,562 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+/////////////////////////////////////////////////////////////////////////
+/// This class ...
+///
+/// \code
+///    <TRestComponentDataSet name="agSignal_vacuum">
+///    <!-- We add here all the axion-photon coupling components -->
+///        <dataset filename="${DUST}/Dataset_BabyIAXO_XMM_mm_Vacuum.root" weight="1" />
+///
+///        <variable name="final_posX" range="(-10,10)mm" bins="10" />
+///        <variable name="final_posY" range="(-10,10)mm" bins="10" />
+///        <variable name="final_energy" range="(0,10)keV" bins="20" />
+///
+///        <parameter name="weights" value="{NGamma}"/>
+///        <parameter name="parameter" value="final_mass" />
+///        <parameter name="parameterizationNodes" value="" />
+///    </TRestComponentDataSet>
+/// \endcode
+///
+/// \code
+///     restRoot
+///     [0] TRestComponentDataSet comp("components.rml", "agSignal_vacuum");
+///     [1] comp.LoadDataSets()
+///     [2] TFile *f = TFile::Open("vacuumComponent.root", "RECREATE");
+///     [3] comp.Write("agSignal_vacuum");
+/// \endcode
+///
+/// \code
+///     restRoot vacuumComponents.root
+///     [0] TCanvas *c = agVacuum->DrawComponent( { "final_posX", "final_posY"}, {"final_energy"}, 2);
+///	    [1] c->Print("component_hitmaps.png");
+/// \endcode
+///
+/// \htmlonly <style>div.image img[src="component_hitmap.png"]{width:750px;}</style> \endhtmlonly
+/// ![A 2-dimensional histogram scan versus the `final_energy` observable, generated by the DrawComponent
+/// method](component_hitmap.png)
+///
+/// \code
+///     restRoot vacuumComponents.root
+///     [0] TCanvas *c = agVacuum->DrawComponent( { "final_energy"}, {"final_posX", "final_posY"}, 2);
+///	    [1] c->Print("component_hitmaps.png");
+/// \endcode
+///
+/// In both cases each plot will regroup 2 bins.
+///
+/// \htmlonly <style>div.image img[src="component_spectra.png"]{width:750px;}</style> \endhtmlonly
+/// ![A 1-dimensional histogram scan versus the `final_posX` and `final_posY` observables, generated by the
+/// DrawComponent method](component_spectra.png)
+///
+///----------------------------------------------------------------------
+///
+/// REST-for-Physics - Software for Rare Event Searches Toolkit
+///
+/// History of developments:
+///
+/// 2023-December: First implementation of TRestComponentDataSet
+/// Javier Galan
+///
+/// \class TRestComponentDataSet
+/// \author: Javier Galan (javier.galan.lacarra@cern.ch)
+///
+/// <hr>
+///
+#include "TRestComponentDataSet.h"
+
+#include <TKey.h>
+
+#include <numeric>
+
+ClassImp(TRestComponentDataSet);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestComponentDataSet::TRestComponentDataSet() {}
+
+///////////////////////////////////////////////
+/// \brief Default destructor
+///
+TRestComponentDataSet::~TRestComponentDataSet() {}
+
+/////////////////////////////////////////////
+/// \brief Constructor loading data from a config file
+///
+/// If no configuration path is defined using TRestMetadata::SetConfigFilePath
+/// the path to the config file must be specified using full path, absolute or
+/// relative.
+///
+/// The default behaviour is that the config file must be specified with
+/// full path, absolute or relative.
+///
+/// \param cfgFileName A const char* giving the path to an RML file.
+/// \param name The name of the specific metadata. It will be used to find the
+/// corresponding TRestAxionMagneticField section inside the RML.
+///
+TRestComponentDataSet::TRestComponentDataSet(const char* cfgFileName, const std::string& name)
+    : TRestComponent(cfgFileName) {
+    LoadConfigFromFile(fConfigFileName, name);
+
+    if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata();
+}
+
+///////////////////////////////////////////////
+/// \brief It will initialize the data frame with the filelist and column names
+/// (or observables) that have been defined by the user.
+///
+void TRestComponentDataSet::Initialize() {
+    TRestComponent::Initialize();
+
+    SetSectionName(this->ClassName());
+
+    LoadDataSets();
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux
+///
+void TRestComponentDataSet::PrintMetadata() {
+    TRestComponent::PrintMetadata();
+
+    if (!fDataSetFileNames.empty()) {
+        RESTMetadata << " " << RESTendl;
+        RESTMetadata << " == Dataset filenames ==" << RESTendl;
+
+        for (const auto& x : fDataSetFileNames) RESTMetadata << "- " << x << RESTendl;
+
+        RESTMetadata << " " << RESTendl;
+    }
+
+    if (fDFRange.X() != 0 || fDFRange.Y() != 0) {
+        RESTMetadata << " DataFrame range: ( " << fDFRange.X() << ", " << fDFRange.Y() << ")" << RESTendl;
+        RESTMetadata << " " << RESTendl;
+    }
+
+    if (!fParameter.empty() && fParameterizationNodes.empty()) {
+        RESTMetadata << "This component has no nodes!" << RESTendl;
+        RESTMetadata << " Use: LoadDataSets() to initialize the nodes" << RESTendl;
+    }
+
+    if (!fWeights.empty()) {
+        RESTMetadata << " " << RESTendl;
+        RESTMetadata << " == Weights ==" << RESTendl;
+
+        for (const auto& x : fWeights) RESTMetadata << "- " << x << RESTendl;
+
+        RESTMetadata << " " << RESTendl;
+    }
+
+    RESTMetadata << " Use : PrintStatistics() to check node statistics" << RESTendl;
+    RESTMetadata << "----" << RESTendl;
+}
+
+/////////////////////////////////////////////
+/// \brief It prints out the statistics available for each parametric node
+///
+void TRestComponentDataSet::PrintStatistics() {
+    if (fNSimPerNode.empty() && IsDataSetLoaded()) fNSimPerNode = ExtractNodeStatistics();
+
+    if (!HasNodes() && !IsDataSetLoaded()) {
+        RESTWarning << "TRestComponentDataSet::PrintStatistics. Empty nodes and no dataset loaded!"
+                    << RESTendl;
+        RESTWarning << "Invoking TRestComponentDataSet::Initialize() might solve the problem" << RESTendl;
+        return;
+    }
+
+    auto result = std::accumulate(fNSimPerNode.begin(), fNSimPerNode.end(), 0);
+    RESTInfo << "Total counts : " << result << RESTendl;
+    std::cout << std::endl;
+
+    RESTInfo << " Parameter node statistics (" << fParameter << ")" << RESTendl;
+    int n = 0;
+    for (const auto& p : fParameterizationNodes) {
+        RESTInfo << " - Value : " << p << " Counts: " << fNSimPerNode[n] << RESTendl;
+        n++;
+    }
+}
+
+/////////////////////////////////////////////
+/// \brief It customizes the retrieval of XML data values of this class
+///
+void TRestComponentDataSet::InitFromConfigFile() {
+    TRestComponent::InitFromConfigFile();
+
+    auto ele = GetElement("dataset");
+    while (ele != nullptr) {
+        fDataSetFileNames.push_back(GetParameter("filename", ele, ""));
+        ele = GetNextElement(ele);
+    }
+
+    if (!fDataSetFileNames.empty()) Initialize();
+}
+
+/////////////////////////////////////////////
+/// \brief It will produce a histogram with the distribution defined using the
+/// variables and the weights for each of the parameter nodes.
+///
+/// fPrecision is used to define the active node
+///
+void TRestComponentDataSet::FillHistograms() {
+    if (!fNodeDensity.empty()) return;
+
+    if (fNbins.size() == 0) {
+        RESTError
+            << "TRestComponentDataSet::FillHistograms. Trying to fill histograms but no variables found!"
+            << RESTendl;
+        return;
+    }
+
+    fNSimPerNode = ExtractNodeStatistics();
+
+    if (!IsDataSetLoaded()) {
+        RESTError << "TRestComponentDataSet::FillHistograms. Dataset has not been initialized!" << RESTendl;
+        return;
+    }
+
+    if (fParameterizationNodes.empty()) {
+        RESTWarning << "Nodes have not been defined" << RESTendl;
+        RESTWarning << "The full dataset will be used to generate the density distribution" << RESTendl;
+        fParameterizationNodes.push_back(-137);
+    }
+
+    RESTInfo << "Generating N-dim histograms" << RESTendl;
+    int nIndex = 0;
+    for (const auto& node : fParameterizationNodes) {
+        Int_t from = 0;
+        Int_t to = 0;
+        if (fSamples > 0 && fTotalSamples[nIndex] - fSamples > 0) {
+            from = fRandom->Integer(fTotalSamples[nIndex] - fSamples);
+            to = from + fSamples;
+            fNSimPerNode[nIndex] = fSamples;
+        }
+
+        ROOT::RDF::RNode df = ROOT::RDataFrame(0);
+        //// Yet not tested in the case when we want to define a unique node without filters
+        //// Needs to be improved
+        if (fParameterizationNodes.size() == 1 && node == -137) {
+            RESTInfo << "Creating component with no parameters (full dataset used)" << RESTendl;
+            df = fDataSet.GetDataFrame().Range(from, to);
+            fParameterizationNodes.clear();
+        } else {
+            RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node)
+                     << RESTendl;
+            Double_t pUp = node * (1 + fPrecision / 2);
+            Double_t pDown = node * (1 - fPrecision / 2);
+            std::string filter = fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " +
+                                 DoubleToString(pDown);
+            df = fDataSet.GetDataFrame().Filter(filter).Range(from, to);
+        }
+
+        Int_t* bins = new Int_t[fNbins.size()];
+        Double_t* xmin = new Double_t[fNbins.size()];
+        Double_t* xmax = new Double_t[fNbins.size()];
+
+        for (size_t n = 0; n < fNbins.size(); n++) {
+            bins[n] = fNbins[n];
+            xmin[n] = fRanges[n].X();
+            xmax[n] = fRanges[n].Y();
+        }
+
+        TString hName = fParameter + "_" + DoubleToString(node);
+        if (fParameterizationNodes.empty()) hName = "full";
+
+        std::vector<std::string> varsAndWeight = fVariables;
+
+        if (!fWeights.empty()) {
+            std::string weightsStr = "";
+            for (size_t n = 0; n < fWeights.size(); n++) {
+                if (n > 0) weightsStr += "*";
+
+                weightsStr += fWeights[n];
+            }
+            df = df.Define("componentWeight", weightsStr);
+            varsAndWeight.push_back("componentWeight");
+        }
+
+        auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight);
+        THnD* hNd = new THnD(*hn);
+        hNd->Scale(1. / fNSimPerNode[nIndex]);
+
+        fNodeDensity.push_back(hNd);
+        fActiveNode = nIndex;
+        nIndex++;
+    }
+}
+
+/////////////////////////////////////////////
+/// \brief It will regenerate the density histogram for the active node. It is
+/// practical in the case when the number of samples fSamples is lower than the total
+/// number of samples. The density distribution will be then re-generated with a
+/// different random sample.
+///
+void TRestComponentDataSet::RegenerateActiveNodeDensity() {
+    if (fActiveNode >= 0 && fNodeDensity[fActiveNode]) {
+        delete fNodeDensity[fActiveNode];
+    } else {
+        RESTError << "TRestComponentDataSet::RegenerateActiveNode. Active node undefined!" << RESTendl;
+        return;
+    }
+
+    Int_t from = 0;
+    Int_t to = 0;
+    if (fSamples > 0 && fTotalSamples[fActiveNode] - fSamples > 0) {
+        from = fRandom->Integer(fTotalSamples[fActiveNode] - fSamples);
+        to = from + fSamples;
+        fNSimPerNode[fActiveNode] = fSamples;
+    }
+
+    Double_t node = GetActiveNodeValue();
+    RESTInfo << "Creating THnD for parameter " << fParameter << ": " << DoubleToString(node) << RESTendl;
+
+    ROOT::RDF::RNode df = ROOT::RDataFrame(0);
+    Double_t pUp = node * (1 + fPrecision / 2);
+    Double_t pDown = node * (1 - fPrecision / 2);
+    std::string filter =
+        fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown);
+    df = fDataSet.GetDataFrame().Filter(filter).Range(from, to);
+
+    Int_t* bins = new Int_t[fNbins.size()];
+    Double_t* xmin = new Double_t[fNbins.size()];
+    Double_t* xmax = new Double_t[fNbins.size()];
+
+    for (size_t n = 0; n < fNbins.size(); n++) {
+        bins[n] = fNbins[n];
+        xmin[n] = fRanges[n].X();
+        xmax[n] = fRanges[n].Y();
+    }
+
+    TString hName = fParameter + "_" + DoubleToString(node);
+    if (fParameterizationNodes.empty()) hName = "full";
+
+    std::vector<std::string> varsAndWeight = fVariables;
+
+    if (!fWeights.empty()) {
+        std::string weightsStr = "";
+        for (size_t n = 0; n < fWeights.size(); n++) {
+            if (n > 0) weightsStr += "*";
+
+            weightsStr += fWeights[n];
+        }
+        df = df.Define("componentWeight", weightsStr);
+        varsAndWeight.push_back("componentWeight");
+    }
+
+    auto hn = df.HistoND({hName, hName, (int)fNbins.size(), bins, xmin, xmax}, varsAndWeight);
+    THnD* hNd = new THnD(*hn);
+    hNd->Scale(1. / fNSimPerNode[fActiveNode]);
+
+    fNodeDensity[fActiveNode] = hNd;
+}
+
+/////////////////////////////////////////////
+/// \brief It returns a vector with all the different values found on
+/// the dataset column for the user given parameterization variable.
+///
+/// If fParameterizationNodes has already been initialized it will
+/// directly return its value.
+///
+std::vector<Double_t> TRestComponentDataSet::ExtractParameterizationNodes() {
+    if (!fParameterizationNodes.empty()) return fParameterizationNodes;
+
+    RESTInfo << "Extracting parameterization nodes" << RESTendl;
+
+    std::vector<double> vs;
+    if (!IsDataSetLoaded()) {
+        RESTError << "TRestComponentDataSet::ExtractParameterizationNodes. Dataset has not been initialized!"
+                  << RESTendl;
+        return vs;
+    }
+
+    auto GetUniqueElements = [](const std::vector<double>& vec) {
+        std::set<double> uniqueSet(vec.begin(), vec.end());
+        return std::vector<double>(uniqueSet.begin(), uniqueSet.end());
+    };
+
+    for (size_t n = 0; n < 1 + fDataSet.GetEntries() / fSplitEntries; n++) {
+        auto nEn = fDataSet.Range(n * fSplitEntries, (n + 1) * fSplitEntries).Count();
+        auto parValues = fDataSet.Range(n * fSplitEntries, (n + 1) * fSplitEntries).Take<double>(fParameter);
+        std::vector<double> uniqueVec = GetUniqueElements(*parValues);
+        vs.insert(vs.end(), uniqueVec.begin(), uniqueVec.end());
+    }
+
+    return vs;
+}
+
+/////////////////////////////////////////////
+/// \brief It returns a vector with the number of entries found for each
+/// parameterization node.
+///
+/// If fNSimPerNode has already been initialized it will directly return its value.
+///
+/// fPrecision will be used to include a thin range where to select
+/// the node values. The value defines the range with a fraction proportional to
+/// the parameter value.
+///
+std::vector<Int_t> TRestComponentDataSet::ExtractNodeStatistics() {
+    if (!fNSimPerNode.empty()) return fNSimPerNode;
+
+    fTotalSamples.clear();
+
+    std::vector<Int_t> stats;
+    if (!IsDataSetLoaded()) {
+        RESTError << "TRestComponentDataSet::ExtractNodeStatistics. Dataset has not been initialized!"
+                  << RESTendl;
+        return stats;
+    }
+
+    RESTInfo << "Counting statistics for each node ..." << RESTendl;
+    RESTInfo << "Number of nodes : " << fParameterizationNodes.size() << RESTendl;
+    for (const auto& p : fParameterizationNodes) {
+        Double_t pUp = p * (1 + fPrecision / 2);
+        Double_t pDown = p * (1 - fPrecision / 2);
+        std::string filter =
+            fParameter + " < " + DoubleToString(pUp) + " && " + fParameter + " > " + DoubleToString(pDown);
+        RESTInfo << "Counting stats for : " << fParameter << " = " << p << RESTendl;
+        auto nEv = fDataSet.GetDataFrame().Filter(filter).Count();
+        fTotalSamples.push_back(*nEv);
+        RESTInfo << "Total entries for " << fParameter << ":" << p << " = " << *nEv << RESTendl;
+        if (fSamples != 0) {
+            nEv = fDataSet.GetDataFrame().Filter(filter).Range(fSamples).Count();
+        }
+
+        if ((Int_t)*nEv < fSamples) {
+            RESTWarning << "The number of requested samples (" << fSamples
+                        << ") is higher than the number of dataset entries (" << *nEv << ")" << RESTendl;
+        }
+        RESTInfo << "Samples to be used for " << fParameter << ":" << p << " = " << *nEv << RESTendl;
+        stats.push_back(*nEv);
+    }
+    return stats;
+}
+
+/////////////////////////////////////////////
+/// \brief A method responsible to import a list of TRestDataSet into fDataSet
+/// and check that the variables and weights defined by the user can be found
+/// inside the dataset.
+///
+Bool_t TRestComponentDataSet::LoadDataSets() {
+    if (fDataSetFileNames.empty()) {
+        fDataSetLoaded = false;
+        return fDataSetLoaded;
+    }
+
+    RESTInfo << "Loading datasets" << RESTendl;
+
+    std::vector<std::string> fullFileNames;
+    for (const auto& name : fDataSetFileNames) {
+        // TODO we get here a list of files. However, we will need to weight each dataset with a factor
+        // to consider the contribution of each background component.
+        // Of course, we could previously take a factor into account already in the dataset, through the
+        // definition of a new column. But being this way would allow us to play around with the
+        // background model without having to regenerate the dataset.
+        std::string fileName = SearchFile(name);
+        if (fileName.empty()) {
+            RESTError << "TRestComponentDataSet::LoadDataSet. Error loading file : " << name << RESTendl;
+            RESTError << "Does the file exist?" << RESTendl;
+            RESTError << "You may use `<globals> <searchPath ...` to indicate the path location" << RESTendl;
+            return false;
+        }
+        fullFileNames.push_back(fileName);
+    }
+
+    fDataSet.Import(fullFileNames);
+    fDataSetLoaded = true;
+
+    if (fDFRange.X() != 0 || fDFRange.Y() != 0)
+        fDataSet.ApplyRange((size_t)fDFRange.X(), (size_t)fDFRange.Y());
+
+    if (fDataSet.GetTree() == nullptr) {
+        RESTError << "Problem loading dataset from file list :" << RESTendl;
+        for (const auto& f : fDataSetFileNames) RESTError << " - " << f << RESTendl;
+        return false;
+    }
+
+    if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) fDataSet.PrintMetadata();
+
+    if (VariablesOk() && WeightsOk()) {
+        fParameterizationNodes = ExtractParameterizationNodes();
+        RESTInfo << "Filling histograms" << RESTendl;
+        FillHistograms();
+        return fDataSetLoaded;
+    }
+
+    return fDataSetLoaded;
+}
+
+/////////////////////////////////////////////
+/// \brief It returns true if all variables have been found inside TRestDataSet
+///
+Bool_t TRestComponentDataSet::VariablesOk() {
+    Bool_t ok = true;
+    std::vector cNames = fDataSet.GetDataFrame().GetColumnNames();
+
+    for (const auto& var : fVariables)
+        if (std::count(cNames.begin(), cNames.end(), var) == 0) {
+            RESTError << "Variable ---> " << var << " <--- NOT found on dataset" << RESTendl;
+            ok = false;
+        }
+    return ok;
+}
+
+/////////////////////////////////////////////
+/// \brief It returns true if all weights have been found inside TRestDataSet
+///
+Bool_t TRestComponentDataSet::WeightsOk() {
+    Bool_t ok = true;
+    std::vector cNames = fDataSet.GetDataFrame().GetColumnNames();
+
+    for (const auto& var : fWeights) {
+        if (!isANumber(var) && std::count(cNames.begin(), cNames.end(), var) == 0) {
+            RESTError << "Weight ---> " << var << " <--- NOT found on dataset" << RESTendl;
+            ok = false;
+        }
+    }
+    return ok;
+}
+
+/////////////////////////////////////////////
+/// \brief Takes care of initializing datasets if have not been initialized.
+/// On sucess it returns true.
+///
+Bool_t TRestComponentDataSet::ValidDataSet() {
+    if (!IsDataSetLoaded()) {
+        RESTWarning << "TRestComponentDataSet::ValidDataSet. Dataset has not been loaded" << RESTendl;
+        RESTWarning << "Try calling TRestComponentDataSet::Initialize()" << RESTendl;
+
+        RESTInfo << "Trying to load datasets" << RESTendl;
+        LoadDataSets();
+        if (IsDataSetLoaded()) {
+            RESTInfo << "Sucess!" << RESTendl;
+        } else {
+            RESTError << "Failed loading datasets" << RESTendl;
+            return false;
+        }
+    }
+
+    if (HasNodes() && fActiveNode == -1) {
+        RESTError << "TRestComponentDataSet::ValidDataSet. Active node has not been defined" << RESTendl;
+        return false;
+    }
+    return true;
+}
diff --git a/source/framework/sensitivity/src/TRestComponentFormula.cxx b/source/framework/sensitivity/src/TRestComponentFormula.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..db5d518fd2eb73c28ef6a58d43c154454aac088c
--- /dev/null
+++ b/source/framework/sensitivity/src/TRestComponentFormula.cxx
@@ -0,0 +1,269 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+/////////////////////////////////////////////////////////////////////////
+/// This class allows to ...
+///
+///
+///    <TRestComponentFormula name="DummyFormulaComponent">
+///            <variable name="final_posX" range="(-10,10)mm" bins="100" />
+///            <variable name="final_posY" range="(-1,1)cm" bins="100" />
+///            <variable name="final_energy" range="(0,10)keV" bins="20" />
+///
+///            <parameter name="units" value="cm^-2*keV^-1" />
+///
+///			   // A spatial gaussian component contribution (energy normalized)
+///            <formula name="gaus" expression="1E-${REST_BCK_LEVEL} *
+/// TMath::Exp(-[final_posX]*[final_posX]-[final_posY]*[final_posY])/(1+[final_energy])" />
+///			   // A flat contribution
+///            <formula name="flat" expression="1E-7" />
+///     </TRestComponentFormula>
+///
+///----------------------------------------------------------------------
+///
+/// REST-for-Physics - Software for Rare Event Searches Toolkit
+///
+/// History of developments:
+///
+/// 2023-December: First implementation of TRestComponentFormula
+/// Javier Galan
+///
+/// \class TRestComponentFormula
+/// \author: Javier Galan (javier.galan.lacarra@cern.ch)
+///
+/// <hr>
+///
+#include "TRestComponentFormula.h"
+
+#include <numeric>
+
+#include "TKey.h"
+
+ClassImp(TRestComponentFormula);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestComponentFormula::TRestComponentFormula() { Initialize(); }
+
+/////////////////////////////////////////////
+/// \brief Constructor loading data from a config file
+///
+/// If no configuration path is defined using TRestMetadata::SetConfigFilePath
+/// the path to the config file must be specified using full path, absolute or
+/// relative.
+///
+/// The default behaviour is that the config file must be specified with
+/// full path, absolute or relative.
+///
+/// \param cfgFileName A const char* giving the path to an RML file.
+/// \param name The name of the specific metadata. It will be used to find the
+/// corresponding TRestAxionMagneticField section inside the RML.
+///
+TRestComponentFormula::TRestComponentFormula(const char* cfgFileName, const std::string& name)
+    : TRestComponent(cfgFileName) {
+    Initialize();
+
+    LoadConfigFromFile(fConfigFileName, name);
+
+    if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata();
+}
+
+///////////////////////////////////////////////
+/// \brief Default destructor
+///
+TRestComponentFormula::~TRestComponentFormula() {}
+
+///////////////////////////////////////////////
+/// \brief It will initialize the data frame with the filelist and column names
+/// (or observables) that have been defined by the user.
+///
+void TRestComponentFormula::Initialize() {
+    TRestComponent::Initialize();
+
+    SetSectionName(this->ClassName());
+
+    FillHistograms();
+}
+
+///////////////////////////////////////////////
+/// \brief It returns the intensity/rate (in seconds) corresponding to the
+/// formula evaluated at the position of the parameter space given by point
+/// and integrated to the parameter space cell volume.
+///
+/// The size of the point vector must have the same dimension as the dimensions
+/// of the variables of the distribution.
+///
+Double_t TRestComponentFormula::GetFormulaRate(std::vector<Double_t> point) {
+    if (fVariables.size() != point.size()) {
+        RESTError << "Point should have same dimensions as number of variables!" << RESTendl;
+        return 0;
+    }
+
+    Double_t result = 0;
+    for (auto& formula : fFormulas) {
+        for (size_t n = 0; n < fVariables.size(); n++) formula.SetParameter(fVariables[n].c_str(), point[n]);
+
+        result += formula.EvalPar(nullptr);
+    }
+
+    Double_t normFactor = 1;
+    for (size_t n = 0; n < GetDimensions(); n++) {
+        normFactor *= (fRanges[n].Y() - fRanges[n].X()) / fNbins[n];
+    }
+
+    return normFactor * result / units(fFormulaUnits);
+}
+
+/////////////////////////////////////////////
+/// \brief It will produce a histogram with the distribution using the formula
+/// contributions.
+///
+/// For the moment this method will just fill one node (without fParameter). But
+/// if the component expression depends on the node parameter it might require
+/// further development.
+///
+/// TODO: The histogram is filled just by evaluating the formula, but it would
+/// be more realistic that we fill the histograms with a number N of entries
+/// that mimic a MC generation scheme similar to TRestComponentDataSet.
+///
+void TRestComponentFormula::FillHistograms() {
+    if (fFormulas.empty()) return;
+
+    if (GetDimensions() == 0) {
+        RESTError << "TRestComponentFormula::FillHistograms. No variables defined!" << RESTendl;
+        RESTError << "Did you add a <cVariable entry?" << RESTendl;
+        return;
+    }
+
+    RESTInfo << "Generating N-dim histogram for " << GetName() << RESTendl;
+
+    TString hName = "formula";
+
+    Int_t* bins = new Int_t[fNbins.size()];
+    Double_t* xlow = new Double_t[fNbins.size()];
+    Double_t* xhigh = new Double_t[fNbins.size()];
+
+    for (size_t n = 0; n < fNbins.size(); n++) {
+        bins[n] = fNbins[n];
+        xlow[n] = fRanges[n].X();
+        xhigh[n] = fRanges[n].Y();
+    }
+
+    THnD* hNd = new THnD(hName, hName, fNbins.size(), bins, xlow, xhigh);
+
+    // Calculate the bin width in each dimension
+    std::vector<double> binWidths;
+    for (size_t i = 0; i < fNbins.size(); ++i) {
+        double width = static_cast<double>(xhigh[i] - xlow[i]) / bins[i];
+        binWidths.push_back(width);
+    }
+
+    // Nested loop to iterate over each bin and print its center
+    std::vector<int> binIndices(fNbins.size(), 0);  // Initialize bin indices to 0 in each dimension
+
+    bool carry = false;
+    while (!carry) {
+        // Calculate the center of the current bin in each dimension
+        std::vector<double> binCenter;
+        for (size_t i = 0; i < fNbins.size(); ++i)
+            binCenter.push_back(xlow[i] + (binIndices[i] + 0.5) * binWidths[i]);
+
+        hNd->Fill(binCenter.data(), GetFormulaRate(binCenter));
+
+        // Update bin indices for the next iteration
+        carry = true;
+        for (size_t i = 0; i < fNbins.size(); ++i) {
+            binIndices[i]++;
+            if (binIndices[i] < bins[i]) {
+                carry = false;
+                break;
+            }
+            binIndices[i] = 0;
+        }
+    }
+
+    fNodeDensity.clear();
+    fNodeDensity.push_back(hNd);
+    fActiveNode = 0;  // For the moment only 1-node!
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux
+///
+void TRestComponentFormula::PrintMetadata() {
+    TRestComponent::PrintMetadata();
+
+    RESTMetadata << " " << RESTendl;
+    RESTMetadata << "Formula units: " << fFormulaUnits << RESTendl;
+
+    if (!fFormulas.empty()) {
+        RESTMetadata << " " << RESTendl;
+        RESTMetadata << " == Contributions implemented inside the component ==" << RESTendl;
+
+        for (const auto& x : fFormulas)
+            RESTMetadata << "- " << x.GetName() << " = " << x.GetExpFormula() << RESTendl;
+
+        RESTMetadata << " " << RESTendl;
+    }
+
+    RESTMetadata << "----" << RESTendl;
+}
+
+/////////////////////////////////////////////
+/// \brief It customizes the retrieval of XML data values of this class
+///
+void TRestComponentFormula::InitFromConfigFile() {
+    TRestComponent::InitFromConfigFile();
+
+    if (!fFormulas.empty()) return;
+
+    /// For some reason I need to do this manually. Dont understand why!
+    fFormulaUnits = GetParameter("formulaUnits");
+
+    auto ele = GetElement("formula");
+    while (ele != nullptr) {
+        std::string name = GetParameter("name", ele, "");
+        std::string expression = GetParameter("expression", ele, "");
+
+        if (expression.empty()) {
+            RESTWarning << "TRestComponentFormula::InitFromConfigFile. Invalid formula" << RESTendl;
+        } else {
+            TFormula formula(name.c_str(), expression.c_str());
+
+            for (Int_t n = 0; n < formula.GetNpar(); n++) {
+                if (std::find(fVariables.begin(), fVariables.end(), formula.GetParName(n)) ==
+                    fVariables.end()) {
+                    RESTError << "Variable : " << formula.GetParName(n) << " not found in component! "
+                              << RESTendl;
+                    RESTError << "TRestComponentFormula evaluation will lead to wrong results!" << RESTendl;
+                }
+            }
+
+            for (const auto& varName : fVariables) formula.SetParameter(varName.c_str(), 0.0);
+
+            fFormulas.push_back(formula);
+        }
+
+        ele = GetNextElement(ele);
+    }
+}
diff --git a/source/framework/sensitivity/src/TRestExperiment.cxx b/source/framework/sensitivity/src/TRestExperiment.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b3e41075641c25553ffad2ec972b9f6ec86c6207
--- /dev/null
+++ b/source/framework/sensitivity/src/TRestExperiment.cxx
@@ -0,0 +1,303 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+/////////////////////////////////////////////////////////////////////////
+/// Documentation TOBE written
+///
+///
+///----------------------------------------------------------------------
+///
+/// REST-for-Physics - Software for Rare Event Searches Toolkit
+///
+/// History of developments:
+///
+/// 2022-December: First implementation of TRestExperiment
+/// Javier Galan
+///
+/// \class TRestExperiment
+/// \author: Javier Galan (javier.galan.lacarra@cern.ch)
+///
+/// <hr>
+///
+#include "TRestExperiment.h"
+
+ClassImp(TRestExperiment);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestExperiment::TRestExperiment() { Initialize(); }
+
+///////////////////////////////////////////////
+/// \brief Default destructor
+///
+TRestExperiment::~TRestExperiment() {}
+
+/////////////////////////////////////////////
+/// \brief Constructor loading data from a config file
+///
+/// If no configuration path is defined using TRestMetadata::SetConfigFilePath
+/// the path to the config file must be specified using full path, absolute or
+/// relative.
+///
+/// The default behaviour is that the config file must be specified with
+/// full path, absolute or relative.
+///
+/// \param cfgFileName A const char* giving the path to an RML file.
+/// \param name The name of the specific metadata. It will be used to find the
+/// corresponding TRestAxionMagneticField section inside the RML.
+///
+TRestExperiment::TRestExperiment(const char* cfgFileName, const std::string& name)
+    : TRestMetadata(cfgFileName) {
+    LoadConfigFromFile(fConfigFileName, name);
+}
+
+///////////////////////////////////////////////
+/// \brief It will initialize the data frame with the filelist and column names
+/// (or observables) that have been defined by the user.
+///
+void TRestExperiment::Initialize() {
+    SetSectionName(this->ClassName());
+
+    if (!fRandom) {
+        delete fRandom;
+        fRandom = nullptr;
+    }
+
+    fRandom = new TRandom3(fSeed);
+    fSeed = fRandom->TRandom::GetSeed();
+}
+
+void TRestExperiment::GenerateMockDataSet(Bool_t useAverage) {
+    fUseAverage = useAverage;
+
+    if (!fBackground) {
+        RESTError << "TRestExperiment::GenerateMockData. Background component was not initialized!"
+                  << RESTendl;
+        return;
+    }
+
+    if (fExposureTime <= 0) {
+        RESTError << "The experimental exposure time has not been defined" << RESTendl;
+        RESTError << "This time is required to create the mock dataset" << RESTendl;
+    }
+
+    Double_t meanCounts = GetBackground()->GetTotalRate() * fExposureTime * units("s");
+
+    Int_t N = fRandom->Poisson(meanCounts);
+    if (fUseAverage) N = (Int_t)meanCounts;
+    RESTInfo << "Experiment: " << GetName() << " Generating mock dataset. Counts: " << N << RESTendl;
+
+    ROOT::RDF::RNode df = fBackground->GetMonteCarloDataFrame(N);
+
+    fExperimentalData.SetDataFrame(df);
+    fExperimentalData.SetTotalTimeInSeconds(fExposureTime * units("s"));
+
+    fExperimentalCounts = *fExperimentalData.GetDataFrame().Count();
+
+    fMockData = true;
+    fDataReady = true;
+}
+
+void TRestExperiment::SetExperimentalDataSet(const std::string& filename) {
+    fExperimentalDataSet = SearchFile(filename);
+    fExperimentalData.Import(fExperimentalDataSet);
+
+    /// fExposureTime is in standard REST units : us
+    fExposureTime = fExperimentalData.GetTotalTimeInSeconds() / units("s");
+    fExperimentalCounts = *fExperimentalData.GetDataFrame().Count();
+
+    fMockData = false;
+    fDataReady = true;
+
+    if (!fSignal || !fBackground) {
+        RESTWarning << "TRestExperiment::SetExperimentalDataSet. Signal and background components must "
+                       "be available before atempt to load experimental data"
+                    << RESTendl;
+        fDataReady = false;
+        return;
+    }
+
+    std::vector<std::string> columns = fExperimentalData.GetDataFrame().GetColumnNames();
+    for (const auto& v : fSignal->GetVariables()) {
+        if (std::find(columns.begin(), columns.end(), v) == columns.end()) {
+            RESTError << "TRestExperiment::SetExperimentalDataSetFile a component variable was not found in "
+                         "the dataset!"
+                      << RESTendl;
+            fDataReady = false;
+            return;
+        }
+    }
+}
+
+/////////////////////////////////////////////
+/// \brief It customizes the retrieval of XML data values of this class
+///
+void TRestExperiment::InitFromConfigFile() {
+    TRestMetadata::InitFromConfigFile();
+
+    int cont = 0;
+    TRestMetadata* md = (TRestMetadata*)this->InstantiateChildMetadata(cont);
+    while (md != nullptr) {
+        if (md->InheritsFrom("TRestComponent")) {
+            TRestComponent* comp = (TRestComponent*)md;
+            if (ToLower(comp->GetNature()) == "background")
+                fBackground = comp;
+            else if (ToLower(comp->GetNature()) == "signal")
+                fSignal = comp;
+            else
+                RESTWarning << "TRestExperiment::InitFromConfigFile. Unknown component!" << RESTendl;
+        }
+        cont++;
+        md = (TRestMetadata*)this->InstantiateChildMetadata(cont);
+    }
+
+    auto ele = GetElement("addComponent");
+    if (ele != nullptr) {
+        std::string filename = GetParameter("filename", ele, "");
+        std::string component = GetParameter("component", ele, "");
+
+        if (filename.empty())
+            RESTWarning
+                << "TRestExperiment. There is a problem with `filename` definition inside <addComponent."
+                << RESTendl;
+        if (component.empty())
+            RESTWarning
+                << "TRestExperiment. There is a problem with `component` definition inside <addComponent."
+                << RESTendl;
+
+        if (TRestTools::fileExists(filename) && TRestTools::isRootFile(filename)) {
+            TFile* file = TFile::Open(filename.c_str(), "READ");
+            if (file != nullptr) {
+                TRestComponent* comp = file->Get<TRestComponent>(component.c_str());
+                if (comp) {
+                    if (ToLower(comp->GetNature()) == "signal")
+                        fSignal = comp;
+                    else if (ToLower(comp->GetNature()) == "background")
+                        fBackground = comp;
+                    else
+                        RESTError << "TRestExperiment::InitFromConfigFile. Component : " << component
+                                  << ". Nature unknown!" << RESTendl;
+                } else
+                    RESTError << "TRestExperiment::InitFromConfigFile. Component : " << component
+                              << " not found! File : " << filename << RESTendl;
+            }
+        }
+    }
+
+    if (fExposureTime > 0 && fExperimentalDataSet.empty()) {
+        GenerateMockDataSet(fUseAverage);
+    } else if (fExposureTime == 0 && !fExperimentalDataSet.empty()) {
+        SetExperimentalDataSet(fExperimentalDataSet);
+    } else {
+        RESTWarning << "The exposure time is not zero but a experimental data filename was defined!"
+                    << RESTendl;
+        RESTWarning << "The exposure time will be recovered from the dataset duration. To avoid confusion is"
+                    << RESTendl;
+        RESTWarning << "better that you set exposure time to zero inside the RML definition," << RESTendl;
+        RESTError
+            << " or do not define a dataset if you wish to generate mock data using the exposure time given"
+            << RESTendl;
+    }
+
+    if (!fSignal) {
+        RESTError << "TRestExperiment : " << GetName() << RESTendl;
+        RESTError << "There was a problem initiazing the signal component!" << RESTendl;
+        return;
+    }
+
+    if (!fBackground) {
+        RESTError << "TRestExperiment : " << GetName() << RESTendl;
+        RESTError << "There was a problem initiazing the background component!" << RESTendl;
+        return;
+    }
+
+    /// Checking that signal/background/tracking got the same variable names and ranges
+    if (fSignal->GetVariables() != fBackground->GetVariables()) {
+        RESTError << "TRestExperiment : " << GetName() << RESTendl;
+        RESTError << "Background and signal components got different variable names or variable ordering!"
+                  << RESTendl;
+        RESTError << "This will lead to undesired results during Likelihood calculation!" << RESTendl;
+        return;
+    }
+
+    if (fSignal->GetNbins() != fBackground->GetNbins()) {
+        RESTError << "TRestExperiment : " << GetName() << RESTendl;
+        RESTError << "Background and signal components got different binning values!" << RESTendl;
+        RESTError << "This will lead to undesired results during Likelihood calculation!" << RESTendl;
+        return;
+    }
+
+    cont = 0;
+    std::vector<TVector2> bckRanges = fBackground->GetRanges();
+    std::vector<TVector2> sgnlRanges = fSignal->GetRanges();
+    for (const TVector2& sRange : sgnlRanges) {
+        if (sRange.X() != bckRanges[cont].X() || sRange.Y() != bckRanges[cont].Y()) {
+            RESTError << "TRestExperiment : " << GetName() << RESTendl;
+            RESTError << "Background and signal components got different range definitions!" << RESTendl;
+            RESTError << "This will lead to undesired results during Likelihood calculation!" << RESTendl;
+            return;
+        }
+        cont++;
+    }
+
+    Initialize();
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux
+///
+void TRestExperiment::PrintMetadata() {
+    TRestMetadata::PrintMetadata();
+
+    RESTMetadata << "Random seed : " << fSeed << RESTendl;
+    RESTMetadata << " " << RESTendl;
+    if (fExposureTime > 0) {
+        RESTMetadata << " - Exposure time : " << fExposureTime * units("s") << " seconds" << RESTendl;
+        RESTMetadata << " - Exposure time : " << fExposureTime * units("hr") << " hours" << RESTendl;
+        RESTMetadata << " - Exposure time : " << fExposureTime * units("day") << " days" << RESTendl;
+    }
+
+    if (fSignal) RESTMetadata << " - Signal component : " << fSignal->GetName() << RESTendl;
+
+    if (fBackground) RESTMetadata << " - Background component : " << fBackground->GetName() << RESTendl;
+
+    if (fMockData) {
+        RESTMetadata << " " << RESTendl;
+        if (fMockData) {
+            RESTMetadata << "The dataset was MC-generated" << RESTendl;
+            if (fUseAverage)
+                RESTMetadata
+                    << " - The number of counts in dataset was generated with the mean background counts"
+                    << RESTendl;
+
+        } else {
+            RESTMetadata << "The dataset was loaded from an existing dataset file" << RESTendl;
+            if (!fExperimentalDataSet.empty())
+                RESTMetadata << " - Experimental dataset file : " << fExperimentalDataSet << RESTendl;
+        }
+    }
+
+    RESTMetadata << " - Experimental counts : " << fExperimentalCounts << RESTendl;
+
+    RESTMetadata << "----" << RESTendl;
+}
diff --git a/source/framework/sensitivity/src/TRestExperimentList.cxx b/source/framework/sensitivity/src/TRestExperimentList.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b370fcb9e61fcb4c2f8985dee76537d9556a4e5e
--- /dev/null
+++ b/source/framework/sensitivity/src/TRestExperimentList.cxx
@@ -0,0 +1,317 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+/////////////////////////////////////////////////////////////////////////
+/// Documentation TOBE written
+///
+///
+///----------------------------------------------------------------------
+///
+/// REST-for-Physics - Software for Rare Event Searches Toolkit
+///
+/// History of developments:
+///
+/// 2022-December: First implementation of TRestExperimentList
+/// Javier Galan
+///
+/// \class TRestExperimentList
+/// \author: Javier Galan (javier.galan.lacarra@cern.ch)
+///
+/// <hr>
+///
+#include "TRestExperimentList.h"
+
+ClassImp(TRestExperimentList);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestExperimentList::TRestExperimentList() { Initialize(); }
+
+///////////////////////////////////////////////
+/// \brief Default destructor
+///
+TRestExperimentList::~TRestExperimentList() {}
+
+/////////////////////////////////////////////
+/// \brief Constructor loading data from a config file
+///
+/// If no configuration path is defined using TRestMetadata::SetConfigFilePath
+/// the path to the config file must be specified using full path, absolute or
+/// relative.
+///
+/// The default behaviour is that the config file must be specified with
+/// full path, absolute or relative.
+///
+/// \param cfgFileName A const char* giving the path to an RML file.
+/// \param name The name of the specific metadata.
+///
+TRestExperimentList::TRestExperimentList(const char* cfgFileName, const std::string& name)
+    : TRestMetadata(cfgFileName) {
+    LoadConfigFromFile(fConfigFileName, name);
+}
+
+///////////////////////////////////////////////
+/// \brief It will initialize the data frame with the filelist and column names
+/// (or observables) that have been defined by the user.
+///
+void TRestExperimentList::Initialize() { SetSectionName(this->ClassName()); }
+
+/////////////////////////////////////////////
+/// \brief It customizes the retrieval of XML data values of this class
+///
+void TRestExperimentList::InitFromConfigFile() {
+    TRestMetadata::InitFromConfigFile();
+
+    if (!fExperimentsFile.empty() && fExperiments.empty()) {
+        TRestTools::ReadASCIITable(fExperimentsFile, fExperimentsTable);
+
+        for (auto& row : fExperimentsTable)
+            for (auto& el : row) el = REST_StringHelper::ReplaceMathematicalExpressions(el);
+
+        if (fExperimentsTable.empty()) {
+            RESTError << "TRestExperimentList::InitFromConfigFile. The experiments table is empty!"
+                      << RESTendl;
+            return;
+        }
+
+        Int_t nTableColumns = fExperimentsTable[0].size();
+
+        int cont = 0;
+        TRestComponent* comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component");
+        while (comp != nullptr) {
+            if (ToLower(comp->GetNature()) == "background")
+                fBackground = comp;
+            else if (ToLower(comp->GetNature()) == "signal")
+                fSignal = comp;
+            else
+                RESTWarning << "TRestExperimentList::InitFromConfigFile. Unknown component!" << RESTendl;
+
+            cont++;
+            comp = (TRestComponent*)this->InstantiateChildMetadata(cont, "Component");
+        }
+
+        Int_t nExpectedColumns = 3;
+        if (fSignal) nExpectedColumns--;
+        if (fBackground) nExpectedColumns--;
+        if (fExposureTime > 0) nExpectedColumns--;
+
+        if (nExpectedColumns == 0) {
+            RESTError << "TRestExperimentList::InitFromConfigFile. At least one free parameter required! "
+                         "(Exposure/Background/Signal)"
+                      << RESTendl;
+            return;
+        }
+
+        if (nExpectedColumns != nTableColumns) {
+            std::string expectedColumns = "";
+            if (fExposureTime == 0) expectedColumns += "exposure";
+            if (!fSignal) {
+                if (fExposureTime == 0) expectedColumns += "/";
+                expectedColumns += "signal";
+            }
+            if (!fBackground) {
+                if (fExposureTime == 0 || !fSignal) expectedColumns += "/";
+                expectedColumns += "background";
+            }
+
+            RESTError << "TRestExperimentList::InitFromConfigFile. Number of expected columns does not match "
+                         "the number of table columns"
+                      << RESTendl;
+            RESTError << "Number of table columns : " << nTableColumns << RESTendl;
+            RESTError << "Number of expected columns : " << nExpectedColumns << RESTendl;
+            RESTError << "Expected columns : " << expectedColumns << RESTendl;
+            return;
+        }
+
+        fComponentFiles = TRestTools::GetFilesMatchingPattern(fComponentPattern);
+
+        Bool_t generateMockData = false;
+        for (const auto& experimentRow : fExperimentsTable) {
+            TRestExperiment* experiment = new TRestExperiment();
+
+            std::string rowStr = "";
+            for (const auto& el : experimentRow) {
+                rowStr += el + " ";
+            }
+
+            RESTInfo << "TRestExperimentList. Loading experiment: " << rowStr << RESTendl;
+
+            int column = 0;
+            if (fExposureTime == 0) {
+                if (REST_StringHelper::isANumber(experimentRow[column])) {
+                    experiment->SetExposureInSeconds(
+                        REST_StringHelper::StringToDouble(experimentRow[column]));
+                    // We will generate mock data once we load the background component
+                    generateMockData = true;
+                } else if (TRestTools::isRootFile(experimentRow[column])) {
+                    // We load the file with the dataset into the experimental data
+                    std::string fname = SearchFile(experimentRow[column]);
+                    experiment->SetExperimentalDataSet(fname);
+                    RESTWarning << "Loading experimental data havent been tested yet!" << RESTendl;
+                    RESTWarning
+                        << "It might require further development. Remove these lines once it works smooth!"
+                        << RESTendl;
+                } else {
+                    RESTError << experimentRow[column] << " is not a exposure time or an experimental dataset"
+                              << RESTendl;
+                }
+                column++;
+            } else {
+                if (ToLower(fExposureStrategy) == "unique") {
+                    experiment->SetExposureInSeconds(fExposureTime * units("s"));
+                    // We will generate mock data once we load the background component
+                    generateMockData = true;
+                }
+            }
+
+            if (!fSignal) {
+                if (GetComponent(experimentRow[column])) {
+                    TRestComponent* sgnl = (TRestComponent*)GetComponent(experimentRow[column])->Clone();
+                    sgnl->SetName((TString)experimentRow[column]);
+                    experiment->SetSignal(sgnl);
+                } else {
+                    RESTError << "TRestExperimentList. Signal component : " << experimentRow[column]
+                              << " not found!" << RESTendl;
+                }
+                column++;
+            } else {
+                experiment->SetSignal(fSignal);
+            }
+
+            if (!fBackground) {
+                if (GetComponent(experimentRow[column])) {
+                    TRestComponent* bck = (TRestComponent*)GetComponent(experimentRow[column])->Clone();
+                    experiment->SetBackground(bck);
+                } else {
+                    RESTError << "TRestExperimentList. Background component : " << experimentRow[column]
+                              << " not found!" << RESTendl;
+                }
+            } else {
+                experiment->SetBackground(fBackground);
+            }
+
+            if (generateMockData) {
+                RESTInfo << "TRestExperimentList. Generating mock dataset" << RESTendl;
+                experiment->GenerateMockDataSet(fUseAverage);
+            }
+
+            if (experiment->GetSignal() && experiment->GetBackground()) {
+                experiment->SetName(experiment->GetSignal()->GetName());
+                fExperiments.push_back(experiment);
+            }
+        }
+
+        if (fExposureTime > 0 && ToLower(fExposureStrategy) == "exp") {
+            ExtractExperimentParameterizationNodes();
+
+            Double_t sum = 0;
+            for (size_t n = 0; n < fExperiments.size(); n++) sum += TMath::Exp((double)n * fExposureFactor);
+
+            Double_t A = fExposureTime * units("s") / sum;
+            for (size_t n = 0; n < fExperiments.size(); n++) {
+                fExperiments[n]->SetExposureInSeconds(A * TMath::Exp((double)n * fExposureFactor));
+                fExperiments[n]->GenerateMockDataSet(fUseAverage);
+            }
+        }
+
+        if (fExposureTime > 0 && ToLower(fExposureStrategy) == "power") {
+            ExtractExperimentParameterizationNodes();
+
+            Double_t sum = 0;
+            for (size_t n = 0; n < fExperiments.size(); n++) sum += TMath::Power((double)n, fExposureFactor);
+
+            Double_t A = fExposureTime * units("s") / sum;
+            for (size_t n = 0; n < fExperiments.size(); n++) {
+                fExperiments[n]->SetExposureInSeconds(A * TMath::Power((double)n, fExposureFactor));
+                fExperiments[n]->GenerateMockDataSet(fUseAverage);
+            }
+        }
+
+        if (fExposureTime > 0 && ToLower(fExposureStrategy) == "equal") {
+            ExtractExperimentParameterizationNodes();
+
+            for (size_t n = 0; n < fExperiments.size(); n++) {
+                fExperiments[n]->SetExposureInSeconds(fExposureTime * units("s") / fExperiments.size());
+                fExperiments[n]->GenerateMockDataSet(fUseAverage);
+            }
+        }
+    }
+}
+
+/////////////////////////////////////////////
+/// \brief It scans all the experiment signals parametric nodes to build a complete list
+/// of nodes used to build a complete sensitivity curve. Some experiments may be
+/// sensitivy to a particular node, while others may be sensitivy to another. If more
+/// than one experiment is sensitivy to a given node, the sensitivity will be combined
+/// later on.
+///
+void TRestExperimentList::ExtractExperimentParameterizationNodes() {
+    fParameterizationNodes.clear();
+
+    for (const auto& experiment : fExperiments) {
+        std::vector<Double_t> nodes = experiment->GetSignal()->GetParameterizationNodes();
+        fParameterizationNodes.insert(fParameterizationNodes.end(), nodes.begin(), nodes.end());
+
+        std::sort(fParameterizationNodes.begin(), fParameterizationNodes.end());
+        auto last = std::unique(fParameterizationNodes.begin(), fParameterizationNodes.end());
+        fParameterizationNodes.erase(last, fParameterizationNodes.end());
+    }
+}
+
+void TRestExperimentList::PrintParameterizationNodes() {
+    std::cout << "Experiment list nodes: ";
+    for (const auto& node : fParameterizationNodes) std::cout << node << "\t";
+    std::cout << std::endl;
+}
+
+TRestComponent* TRestExperimentList::GetComponent(std::string compName) {
+    TRestComponent* component = nullptr;
+    for (const auto& c : fComponentFiles) {
+        TFile* f = TFile::Open(c.c_str(), "READ");
+        TObject* obj = f->Get((TString)compName);
+
+        if (!obj) continue;
+
+        if (obj->InheritsFrom("TRestComponent")) {
+            return (TRestComponent*)obj;
+        } else {
+            RESTError << "An object named : " << compName
+                      << " exists inside the file, but it does not inherit from TRestComponent" << RESTendl;
+        }
+    }
+
+    return component;
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux
+///
+void TRestExperimentList::PrintMetadata() {
+    TRestMetadata::PrintMetadata();
+
+    RESTMetadata << "Number of experiments loaded: " << fExperiments.size() << RESTendl;
+
+    if (fUseAverage) RESTMetadata << "Average MonteCarlo counts generation was enabled" << RESTendl;
+
+    RESTMetadata << "----" << RESTendl;
+}
diff --git a/source/framework/sensitivity/src/TRestResponse.cxx b/source/framework/sensitivity/src/TRestResponse.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1c9c769bf8d78921a6d782c29b064728e1837504
--- /dev/null
+++ b/source/framework/sensitivity/src/TRestResponse.cxx
@@ -0,0 +1,248 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+/////////////////////////////////////////////////////////////////////////
+/// Documentation TOBE written
+///
+///
+///----------------------------------------------------------------------
+///
+/// REST-for-Physics - Software for Rare Event Searches Toolkit
+///
+/// History of developments:
+///
+/// 2023-December: First implementation of TRestResponse
+/// Javier Galan
+///
+/// \class TRestResponse
+/// \author: Javier Galan (javier.galan.lacarra@cern.ch)
+///
+/// <hr>
+///
+#include "TRestResponse.h"
+
+ClassImp(TRestResponse);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestResponse::TRestResponse() { Initialize(); }
+
+/////////////////////////////////////////////
+/// \brief Constructor loading data from a config file
+///
+/// If no configuration path is defined using TRestMetadata::SetConfigFilePath
+/// the path to the config file must be specified using full path, absolute or
+/// relative.
+///
+/// The default behaviour is that the config file must be specified with
+/// full path, absolute or relative.
+///
+/// \param cfgFileName A const char* giving the path to an RML file.
+/// \param name The name of the specific metadata. It will be used to find the
+/// corresponding TRestAxionMagneticField section inside the RML.
+///
+TRestResponse::TRestResponse(const char* cfgFileName, const std::string& name) : TRestMetadata(cfgFileName) {
+    Initialize();
+
+    LoadConfigFromFile(fConfigFileName, name);
+
+    if (GetVerboseLevel() >= TRestStringOutput::REST_Verbose_Level::REST_Info) PrintMetadata();
+}
+
+///////////////////////////////////////////////
+/// \brief Default destructor
+///
+TRestResponse::~TRestResponse() {}
+
+///////////////////////////////////////////////
+/// \brief It will initialize the data frame with the filelist and column names
+/// (or observables) that have been defined by the user.
+///
+void TRestResponse::Initialize() { SetSectionName(this->ClassName()); }
+
+///////////////////////////////////////////////
+/// \brief It loads into the fResponseMatrix data member the response from a file.
+///
+/// For the moment only binary data files with format .N???f have been implemented.
+///
+/// The response file should be arranged in a way that the more internal `std::vector`
+/// (row) inside the `std::vector <std::vector>` table corresponds to a specific
+/// transformed energy (output). For example, if we have the incident particle energy
+/// (input) and the expected detected energy response (output) for that energy, then
+/// each row in the matrix corresponds to a detected energy and each element of that
+/// row defines the fraction of incident energies that contributed to that detected
+/// energy.
+///
+/// Thats why we may need to transpose the matrix, so that when we can extract a row
+/// (detected energy) from the matrix directly, such as fMatrix[n], and we just get
+/// the vector that should convolute with the Signal(Ei) that is a vector of signals
+/// as a function of incident energy. The resulting scalar product will give the
+/// expected signal at the detection energy.
+///
+void TRestResponse::LoadResponse(Bool_t transpose) {
+    if (fFilename == "") {
+        RESTError << "TRestResponse::LoadResponse. The response filename was not defined" << RESTendl;
+        return;
+    }
+
+    std::string fullFilename = SearchFile(fFilename);
+    if (fullFilename.empty()) {
+        RESTError << "TRestResponse::LoadResponse. The response filename was not found!" << RESTendl;
+        RESTError << "Filename : " << fFilename << RESTendl;
+        RESTError << "You may want to define <seachPath inside <globals> definition" << RESTendl;
+        return;
+    }
+
+    std::string extension = TRestTools::GetFileNameExtension(fFilename);
+    if (!extension.empty() && extension[0] == 'N' && extension.back() == 'f') {
+        TRestTools::ReadBinaryTable(fullFilename, fResponseMatrix);
+
+        fTransposed = false;
+        if (transpose) {
+            fTransposed = transpose;
+            TRestTools::TransposeTable(fResponseMatrix);
+        }
+
+        return;
+    }
+
+    RESTError << "Extension format - " << extension << " - not recognized!" << RESTendl;
+}
+
+/////////////////////////////////////////////
+/// \brief This method will return a vector of std::pair, each pair will contain the
+/// output energy together with the corresponding response (or efficiency), for the
+/// given input energy.
+///
+/// The output value will be mapped following the binning and the origin given on the
+/// metadata members.
+///
+std::vector<std::pair<Double_t, Double_t>> TRestResponse::GetResponse(Double_t input) {
+    std::vector<std::pair<Double_t, Double_t>> response;
+
+    if (fResponseMatrix.empty()) {
+        RESTError << "TRestResponse::GetResponse. Response matrix has not been loaded yet!" << RESTendl;
+        return response;
+    }
+
+    if (input < GetInputRange().X() || input > GetInputRange().Y()) {
+        RESTError << "TRestResponse::GetResponse. The input value " << input << " is outside range!"
+                  << RESTendl;
+        return response;
+    }
+
+    if (!fInterpolation) {
+        Int_t bin = (Int_t)((input - fOrigin.X()) / fBinSize);
+
+        for (std::size_t n = 0; n < fResponseMatrix[bin].size(); n++) {
+            Double_t output = fOrigin.Y() + ((double)n + 0.5) * fBinSize;
+            Double_t value = fResponseMatrix[bin][n];
+
+            std::pair<Double_t, Double_t> outp{output, value};
+
+            response.push_back(outp);
+        }
+
+        return response;
+    }
+
+    Int_t binLeft = (Int_t)((input - fBinSize / 2. - fOrigin.X()) / fBinSize);
+    Int_t binRight = binLeft + 1;
+
+    Double_t distLeft = (input - fBinSize / 2. + fOrigin.X()) - binLeft * fBinSize;
+
+    if (input <= GetInputRange().X() + fBinSize / 2. || input >= GetInputRange().Y() - fBinSize / 2.)
+        binRight = binLeft;
+
+    /*
+    std::cout << "Top : " << GetInputRange().Y() - fBinSize/2. << std::endl;
+    std::cout << "binLeft : " << binLeft << std::endl;
+    std::cout << "binRight : " << binRight << std::endl;
+    std::cout << "dLeft : " << distLeft << std::endl;
+    std::cout << "dLeft/fBinSize : " << distLeft/fBinSize << std::endl;
+    std::cout << "1 - distLeft/fBinSize : " << 1 - distLeft/fBinSize << std::endl;
+    */
+
+    for (std::size_t n = 0; n < fResponseMatrix[binLeft].size(); n++) {
+        Double_t output = fOrigin.Y() + ((double)n + 0.5) * fBinSize;
+
+        Double_t value = fResponseMatrix[binLeft][n] * (1 - distLeft / fBinSize) +
+                         fResponseMatrix[binRight][n] * distLeft / fBinSize;
+
+        std::pair<Double_t, Double_t> outp{output, value};
+
+        response.push_back(outp);
+
+        /*
+        std::cout << "n: " << n << " output : " << output << std::endl;
+        std::cout << "response: " << response << std::endl;
+        */
+    }
+
+    return response;
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux
+///
+void TRestResponse::PrintResponseMatrix(Int_t fromRow = 0, Int_t toRow = 0) {
+    TRestTools::PrintTable(fResponseMatrix, fromRow, toRow);
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux
+///
+void TRestResponse::PrintMetadata() {
+    TRestMetadata::PrintMetadata();
+
+    RESTMetadata << "Response file : " << fFilename << RESTendl;
+    RESTMetadata << "Variable : " << fVariable << RESTendl;
+    RESTMetadata << "Bin size : " << fBinSize << RESTendl;
+    RESTMetadata << " " << RESTendl;
+
+    if (!fResponseMatrix.empty()) {
+        RESTMetadata << "Response matrix has been loaded" << RESTendl;
+        RESTMetadata << " - Number of columns: " << fResponseMatrix[0].size() << RESTendl;
+        RESTMetadata << " - Number of rows : " << fResponseMatrix.size() << RESTendl;
+        RESTMetadata << " - Input range : " << GetInputRange().X() << " - " << GetInputRange().Y()
+                     << RESTendl;
+        RESTMetadata << " - Output range : " << GetOutputRange().X() << " - " << GetOutputRange().Y()
+                     << RESTendl;
+
+        if (fTransposed) {
+            RESTMetadata << " " << RESTendl;
+            RESTMetadata << "Original matrix was transposed" << RESTendl;
+        }
+    } else {
+        RESTMetadata << "Response matrix has NOT been loaded" << RESTendl;
+        RESTMetadata << "Try calling TRestResponse::LoadResponse()" << RESTendl;
+    }
+    if (fInterpolation) {
+        RESTMetadata << " " << RESTendl;
+        RESTMetadata << "Interpolation is enabled" << RESTendl;
+    } else {
+        RESTMetadata << " " << RESTendl;
+        RESTMetadata << "Interpolation is disabled" << RESTendl;
+    }
+    RESTMetadata << "----" << RESTendl;
+}
diff --git a/source/framework/sensitivity/src/TRestSensitivity.cxx b/source/framework/sensitivity/src/TRestSensitivity.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b9dc668936c328df413d8e50d4a7d1461995877e
--- /dev/null
+++ b/source/framework/sensitivity/src/TRestSensitivity.cxx
@@ -0,0 +1,621 @@
+/*************************************************************************
+ * This file is part of the REST software framework.                     *
+ *                                                                       *
+ * Copyright (C) 2016 GIFNA/TREX (University of Zaragoza)                *
+ * For more information see https://gifna.unizar.es/trex                 *
+ *                                                                       *
+ * REST is free software: you can redistribute it and/or modify          *
+ * it under the terms of the GNU General Public License as published by  *
+ * the Free Software Foundation, either version 3 of the License, or     *
+ * (at your option) any later version.                                   *
+ *                                                                       *
+ * REST is distributed in the hope that it will be useful,               *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
+ * GNU General Public License for more details.                          *
+ *                                                                       *
+ * You should have a copy of the GNU General Public License along with   *
+ * REST in $REST_PATH/LICENSE.                                           *
+ * If not, see https://www.gnu.org/licenses/.                            *
+ * For the list of contributors see $REST_PATH/CREDITS.                  *
+ *************************************************************************/
+
+/////////////////////////////////////////////////////////////////////////
+/// Documentation TOBE written
+///
+///
+///----------------------------------------------------------------------
+///
+/// REST-for-Physics - Software for Rare Event Searches Toolkit
+///
+/// History of developments:
+///
+/// 2022-December: First implementation of TRestSensitivity
+/// Javier Galan
+///
+/// \class TRestSensitivity
+/// \author: Javier Galan (javier.galan.lacarra@cern.ch)
+///
+/// <hr>
+///
+#include <TRestExperimentList.h>
+#include <TRestSensitivity.h>
+
+ClassImp(TRestSensitivity);
+
+///////////////////////////////////////////////
+/// \brief Default constructor
+///
+TRestSensitivity::TRestSensitivity() { Initialize(); }
+
+///////////////////////////////////////////////
+/// \brief Default destructor
+///
+TRestSensitivity::~TRestSensitivity() {}
+
+/////////////////////////////////////////////
+/// \brief Constructor loading data from a config file
+///
+/// If no configuration path is defined using TRestMetadata::SetConfigFilePath
+/// the path to the config file must be specified using full path, absolute or
+/// relative.
+///
+/// The default behaviour is that the config file must be specified with
+/// full path, absolute or relative.
+///
+/// \param cfgFileName A const char* giving the path to an RML file.
+/// \param name The name of the specific metadata. It will be used to find the
+/// corresponding TRestAxionMagneticField section inside the RML.
+///
+TRestSensitivity::TRestSensitivity(const char* cfgFileName, const std::string& name)
+    : TRestMetadata(cfgFileName) {
+    LoadConfigFromFile(fConfigFileName, name);
+}
+
+///////////////////////////////////////////////
+/// \brief It will initialize the data frame with the filelist and column names
+/// (or observables) that have been defined by the user.
+///
+void TRestSensitivity::Initialize() { SetSectionName(this->ClassName()); }
+
+///////////////////////////////////////////////
+/// \brief It will return a value of the coupling, g4, such that (chi-chi0) gets
+/// closer to the target value given by argument. The factor will be used to
+/// increase or decrease the coupling, and evaluate the likelihood.
+///
+Double_t TRestSensitivity::ApproachByFactor(Double_t node, Double_t g4, Double_t chi0, Double_t target,
+                                            Double_t factor) {
+    if (factor == 1) {
+        return 0;
+    }
+
+    /// Coarse movement to get to Chi2 above target
+    Double_t Chi2 = 0;
+    do {
+        Chi2 = 0;
+        for (const auto& exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, node, g4);
+
+        g4 = factor * g4;
+    } while (Chi2 - chi0 < target);
+    g4 = g4 / factor;
+
+    /// Coarse movement to get to Chi2 below target (/2)
+    do {
+        Chi2 = 0;
+        for (const auto& exp : fExperiments) Chi2 += -2 * UnbinnedLogLikelihood(exp, node, g4);
+
+        g4 = g4 / factor;
+    } while (Chi2 - chi0 > target);
+
+    return g4 * factor;
+}
+
+void TRestSensitivity::GenerateCurve() {
+    ExtractExperimentParameterizationNodes();
+
+    if (GetNumberOfCurves() > 0)
+        for (const auto& exp : fExperiments) {
+            exp->GenerateMockDataSet();
+        }
+
+    RESTInfo << "Generating sensitivity curve" << RESTendl;
+    std::vector<Double_t> curve;
+    for (const auto& node : fParameterizationNodes) {
+        RESTInfo << "Generating node : " << node << RESTendl;
+        curve.push_back(GetCoupling(node));
+    }
+    fCurves.push_back(curve);
+
+    RESTInfo << "Curve has been generated. You may use now TRestSensitivity::ExportCurve( fname.txt )."
+             << RESTendl;
+}
+
+void TRestSensitivity::GenerateCurves(Int_t N) {
+    /*
+    std::cout << "TRestSensitivity::GenerateCurves." << std::endl;
+    std::cout << "There is a potential memory leak when generating several curves." << std::endl;
+    std::cout << "This code needs to be reviewed" << std::endl;
+    return;
+    */
+
+    for (int n = 0; n < N; n++) GenerateCurve();
+}
+
+std::vector<Double_t> TRestSensitivity::GetCurve(size_t n) {
+    if (n >= GetNumberOfCurves()) {
+        RESTWarning << "Requested curve number : " << n << " but only " << GetNumberOfCurves() << " generated"
+                    << RESTendl;
+        return std::vector<Double_t>();
+    }
+    return fCurves[n];
+}
+
+std::vector<Double_t> TRestSensitivity::GetAveragedCurve() {
+    if (GetNumberOfCurves() <= 0) return std::vector<Double_t>();
+
+    std::vector<double> averagedCurve(fCurves[0].size(), 0.0);  // Initialize with zeros
+
+    for (const auto& row : fCurves) {
+        for (size_t i = 0; i < row.size(); ++i) {
+            averagedCurve[i] += row[i];
+        }
+    }
+
+    for (double& avg : averagedCurve) {
+        avg /= static_cast<double>(fCurves.size());
+    }
+
+    return averagedCurve;
+}
+
+void TRestSensitivity::ExportAveragedCurve(std::string fname, Double_t factor, Double_t power) {
+    std::vector<Double_t> curve = GetAveragedCurve();
+    if (curve.empty()) std::cout << "Curve is empty" << std::endl;
+    if (curve.empty()) return;
+
+    // Open a file for writing
+    std::ofstream outputFile(fname);
+
+    // Check if the file is opened successfully
+    if (!outputFile) {
+        RESTError << "TRestSensitivity::ExportCurve. Error opening file for writing!" << RESTendl;
+        return;
+    }
+
+    if (fParameterizationNodes.size() != curve.size()) {
+        RESTError << "TRestSensitivity::ExportCurve. Curve has not been properly generated" << RESTendl;
+        RESTError << "Parameterization nodes: " << fParameterizationNodes.size() << RESTendl;
+        RESTError << "Try invoking TRestSensitivity::GenerateCurve" << RESTendl;
+        return;
+    }
+
+    int m = 0;
+    for (const auto& node : fParameterizationNodes) {
+        outputFile << node << " " << factor * TMath::Power(curve[m], power) << std::endl;
+        m++;
+    }
+
+    outputFile.close();
+
+    RESTInfo << "TRestSensitivity::ExportCurve. File has been written successfully!" << RESTendl;
+}
+
+void TRestSensitivity::ExportCurve(std::string fname, Double_t factor, Double_t power, int n) {
+    std::vector<Double_t> curve = GetCurve(n);
+    if (curve.empty()) return;
+
+    // Open a file for writing
+    std::ofstream outputFile(fname);
+
+    // Check if the file is opened successfully
+    if (!outputFile) {
+        RESTError << "TRestSensitivity::ExportCurve. Error opening file for writing!" << RESTendl;
+        return;
+    }
+
+    if (fParameterizationNodes.size() != curve.size()) {
+        RESTError << "TRestSensitivity::ExportCurve. Curve has not been properly generated" << RESTendl;
+        RESTError << "Try invoking TRestSensitivity::GenerateCurve" << RESTendl;
+        return;
+    }
+
+    int m = 0;
+    for (const auto& node : fParameterizationNodes) {
+        outputFile << node << " " << factor * TMath::Power(curve[m], power) << std::endl;
+        m++;
+    }
+
+    outputFile.close();
+
+    RESTInfo << "TRestSensitivity::ExportCurve. File has been written successfully!" << RESTendl;
+}
+
+///////////////////////////////////////////////
+/// \brief It will return the coupling value for which Chi=sigma
+///
+Double_t TRestSensitivity::GetCoupling(Double_t node, Double_t sigma, Double_t precision) {
+    Double_t Chi2_0 = 0;
+    for (const auto& exp : fExperiments) {
+        Chi2_0 += -2 * UnbinnedLogLikelihood(exp, node, 0);
+    }
+
+    Double_t target = sigma * sigma;
+
+    Double_t g4 = 0.5;
+
+    g4 = ApproachByFactor(node, g4, Chi2_0, target, 2);
+    g4 = ApproachByFactor(node, g4, Chi2_0, target, 1.2);
+    g4 = ApproachByFactor(node, g4, Chi2_0, target, 1.02);
+    g4 = ApproachByFactor(node, g4, Chi2_0, target, 1.0002);
+
+    return g4;
+}
+
+///////////////////////////////////////////////
+/// \brief It returns the Log(L) for the experiment and coupling given by argument.
+///
+Double_t TRestSensitivity::UnbinnedLogLikelihood(const TRestExperiment* experiment, Double_t node,
+                                                 Double_t g4) {
+    Double_t lhood = 0;
+    if (!experiment->IsDataReady()) {
+        RESTError << "TRestSensitivity::UnbinnedLogLikelihood. Experiment " << experiment->GetName()
+                  << " is not ready!" << RESTendl;
+        return lhood;
+    }
+
+    if (!experiment->GetSignal()->HasNodes()) {
+        RESTError << "Experiment signal : " << experiment->GetSignal()->GetName() << " has no nodes!"
+                  << RESTendl;
+        return lhood;
+    }
+
+    /// We check if the signal component is sensitive to that particular node
+    /// If not, this experiment will not contribute to that node
+    Int_t nd = experiment->GetSignal()->FindActiveNode(node);
+    if (nd >= 0)
+        experiment->GetSignal()->SetActiveNode(nd);
+    else {
+        RESTWarning << "Node : " << node << " not found in signal : " << experiment->GetSignal()->GetName()
+                    << RESTendl;
+        return 0.0;
+    }
+
+    /// We could check if background has also components, but for the moment we do not have a background
+    /// for each node, although it could be the case, if for example the background depends on the detector
+    /// conditions. For example, higher pressure inside the detector gains in signal sensitivity but
+    /// it will produce also higher background.
+
+    if (experiment->GetBackground()->HasNodes()) {
+        RESTWarning
+            << "TRestSensitivity::UnbinnedLogLikelihood is not ready to have background parameter nodes!"
+            << RESTendl;
+        return 0.0;
+    }
+
+    Double_t signal = g4 * experiment->GetSignal()->GetTotalRate() * experiment->GetExposureInSeconds();
+
+    lhood = -signal;
+
+    if (experiment->GetExperimentalCounts() == 0) return lhood;
+
+    if (ROOT::IsImplicitMTEnabled()) ROOT::DisableImplicitMT();
+
+    std::vector<std::vector<Double_t>> trackingData;
+    for (const auto& var : experiment->GetSignal()->GetVariables()) {
+        auto values = experiment->GetExperimentalDataFrame().Take<Double_t>(var);
+        std::vector<Double_t> vDbl = std::move(*values);
+        trackingData.push_back(vDbl);
+    }
+
+    for (size_t n = 0; n < trackingData[0].size(); n++) {
+        std::vector<Double_t> point;
+        for (size_t m = 0; m < trackingData.size(); m++) point.push_back(trackingData[m][n]);
+
+        Double_t bckRate = experiment->GetBackground()->GetRate(point);
+        Double_t sgnlRate = experiment->GetSignal()->GetRate(point);
+
+        Double_t expectedRate = bckRate + g4 * sgnlRate;
+        lhood += TMath::Log(expectedRate);
+    }
+
+    return lhood;
+}
+
+///////////////////////////////////////////////
+/// \brief
+///
+TH1D* TRestSensitivity::SignalStatisticalTest(Double_t node, Int_t N) {
+    std::vector<Double_t> couplings;
+    for (int n = 0; n < N; n++) {
+        for (const auto& exp : fExperiments) exp->GetSignal()->RegenerateActiveNodeDensity();
+
+        Double_t coupling = TMath::Sqrt(TMath::Sqrt(GetCoupling(node)));
+        couplings.push_back(coupling);
+    }
+
+    // Directly assign the minimum and maximum values
+    double min_value = *std::min_element(couplings.begin(), couplings.end());
+    double max_value = *std::max_element(couplings.begin(), couplings.end());
+
+    if (fSignalTest) delete fSignalTest;
+    fSignalTest = new TH1D("SignalTest", "A signal test", 100, 0.9 * min_value, 1.1 * max_value);
+    for (const auto& coup : couplings) fSignalTest->Fill(coup);
+
+    return fSignalTest;
+}
+
+/////////////////////////////////////////////
+/// \brief It customizes the retrieval of XML data values of this class
+///
+void TRestSensitivity::InitFromConfigFile() {
+    TRestMetadata::InitFromConfigFile();
+
+    int cont = 0;
+    TRestMetadata* metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment");
+    while (metadata != nullptr) {
+        cont++;
+        if (metadata->InheritsFrom("TRestExperimentList")) {
+            TRestExperimentList* experimentsList = (TRestExperimentList*)metadata;
+            std::vector<TRestExperiment*> exList = experimentsList->GetExperiments();
+            fExperiments.insert(fExperiments.end(), exList.begin(), exList.end());
+        } else if (metadata->InheritsFrom("TRestExperiment")) {
+            fExperiments.push_back((TRestExperiment*)metadata);
+        }
+
+        metadata = (TRestMetadata*)this->InstantiateChildMetadata(cont, "Experiment");
+    }
+
+    Initialize();
+}
+
+/////////////////////////////////////////////
+/// \brief This method is used to obtain the list of curves that satisfy that each value inside
+/// the curve is placed at a specified level. E.g. if we provide a level 0.5, then the corresponding
+/// curve will be constructed with the central value extracted at each parameter point.
+//
+/// We may then construct the profile of the sensitivity curves at 98%, 95% and 68% C.L. as follows:
+///
+/// \code
+/// TRestSensitivity::GetLevelCurves( {0.01, 0.025, 0.16, 0.84, 0.975, 0.99} );
+/// \endcode
+///
+std::vector<std::vector<Double_t>> TRestSensitivity::GetLevelCurves(const std::vector<Double_t>& levels) {
+    std::vector<std::vector<Double_t>> curves(levels.size());
+
+    for (const auto& l : levels) {
+        if (l >= 1 || l <= 0) {
+            RESTError << "The level value should be between 0 and 1" << RESTendl;
+            return curves;
+        }
+    }
+
+    std::vector<int> intLevels;
+    for (const auto& l : levels) {
+        int val = (int)round(l * fCurves.size());
+        if (val >= (int)fCurves.size()) val = fCurves.size() - 1;
+        if (val < 0) val = 0;
+
+        intLevels.push_back(val);
+    }
+
+    for (size_t m = 0; m < fCurves[0].size(); m++) {
+        std::vector<Double_t> v;
+        for (size_t n = 0; n < fCurves.size(); n++) v.push_back(fCurves[n][m]);
+
+        std::sort(v.begin(), v.begin() + v.size());
+
+        for (size_t n = 0; n < intLevels.size(); n++) curves[n].push_back(v[intLevels[n]]);
+    }
+
+    return curves;
+}
+
+TCanvas* TRestSensitivity::DrawCurves() {
+    if (fCanvas != NULL) {
+        delete fCanvas;
+        fCanvas = NULL;
+    }
+    fCanvas = new TCanvas("canv", "This is the canvas title", 600, 450);
+    fCanvas->Draw();
+
+    TPad* pad1 = new TPad("pad1", "This is pad1", 0.01, 0.02, 0.99, 0.97);
+    // pad1->Divide(2, 2);
+    pad1->Draw();
+
+    ////// Drawing reflectivity versus angle
+    //   pad1->cd()->SetLogx();
+    pad1->cd()->SetRightMargin(0.09);
+    pad1->cd()->SetLeftMargin(0.15);
+    pad1->cd()->SetBottomMargin(0.15);
+
+    std::vector<TGraph*> graphs;
+
+    for (size_t n = 0; n < 20; n++) {
+        std::string grname = "gr" + IntegerToString(n);
+        TGraph* gr = new TGraph();
+        gr->SetName(grname.c_str());
+        for (size_t m = 0; m < this->GetCurve(n).size(); m++)
+            gr->SetPoint(gr->GetN(), fParameterizationNodes[m],
+                         TMath::Sqrt(TMath::Sqrt(this->GetCurve(n)[m])));
+
+        gr->SetLineColorAlpha(kBlue + n, 0.3);
+        gr->SetLineWidth(1);
+        graphs.push_back(gr);
+    }
+
+    TGraph* avGr = new TGraph();
+    std::vector<Double_t> avCurve = GetAveragedCurve();
+    for (size_t m = 0; m < avCurve.size(); m++)
+        avGr->SetPoint(avGr->GetN(), fParameterizationNodes[m], TMath::Sqrt(TMath::Sqrt(avCurve[m])));
+    avGr->SetLineColor(kBlack);
+    avGr->SetLineWidth(2);
+
+    graphs[0]->GetXaxis()->SetLimits(0, 0.25);
+    //   graphs[0]->GetHistogram()->SetMaximum(1);
+    //   graphs[0]->GetHistogram()->SetMinimum(lowRange);
+
+    graphs[0]->GetXaxis()->SetTitle("mass [eV]");
+    graphs[0]->GetXaxis()->SetTitleSize(0.05);
+    graphs[0]->GetXaxis()->SetLabelSize(0.05);
+    graphs[0]->GetYaxis()->SetTitle("g_{a#gamma} [10^{-10} GeV^{-1}]");
+    graphs[0]->GetYaxis()->SetTitleOffset(1.5);
+    graphs[0]->GetYaxis()->SetTitleSize(0.05);
+    // graphs[0]->GetYaxis()->SetLabelSize(0.05);
+    // graphs[0]->GetYaxis()->SetLabelOffset(0.0);
+    // pad1->cd()->SetLogy();
+    graphs[0]->Draw("AL");
+    for (unsigned int n = 1; n < graphs.size(); n++) graphs[n]->Draw("L");
+    avGr->Draw("L");
+
+    /*
+Double_t lx1 = 0.6, ly1 = 0.75, lx2 = 0.9, ly2 = 0.95;
+if (eLegendCoords.size() > 0) {
+    lx1 = eLegendCoords[0];
+    ly1 = eLegendCoords[1];
+    lx2 = eLegendCoords[2];
+    ly2 = eLegendCoords[3];
+}
+TLegend* legend = new TLegend(lx1, ly1, lx2, ly2);
+
+legend->SetTextSize(0.03);
+legend->SetHeader("Energies", "C");  // option "C" allows to center the header
+for (unsigned int n = 0; n < energies.size(); n++) {
+    std::string lname = "gr" + IntegerToString(n);
+    std::string ltitle = DoubleToString(energies[n]) + " keV";
+
+    legend->AddEntry(lname.c_str(), ltitle.c_str(), "l");
+}
+legend->Draw();
+    */
+
+    return fCanvas;
+}
+
+TCanvas* TRestSensitivity::DrawLevelCurves() {
+    if (fCanvas != NULL) {
+        delete fCanvas;
+        fCanvas = NULL;
+    }
+    fCanvas = new TCanvas("canv", "This is the canvas title", 500, 400);
+    fCanvas->Draw();
+    fCanvas->SetLeftMargin(0.15);
+    fCanvas->SetRightMargin(0.04);
+    fCanvas->SetLogx();
+
+    std::vector<std::vector<Double_t>> levelCurves = GetLevelCurves({0.025, 0.16, 0.375, 0.625, 0.84, 0.975});
+
+    std::vector<TGraph*> graphs;
+    for (size_t n = 0; n < levelCurves.size(); n++) {
+        std::string grname = "gr" + IntegerToString(n);
+        TGraph* gr = new TGraph();
+        gr->SetName(grname.c_str());
+        for (size_t m = 0; m < levelCurves[n].size(); m++)
+            gr->SetPoint(gr->GetN(), fParameterizationNodes[m], TMath::Sqrt(TMath::Sqrt(levelCurves[n][m])));
+
+        gr->SetLineColor(kBlack);
+        gr->SetLineWidth(1);
+        graphs.push_back(gr);
+    }
+
+    TGraph* centralGr = new TGraph();
+    std::vector<Double_t> centralCurve = GetLevelCurves({0.5})[0];
+    for (size_t m = 0; m < centralCurve.size(); m++)
+        centralGr->SetPoint(centralGr->GetN(), fParameterizationNodes[m],
+                            TMath::Sqrt(TMath::Sqrt(centralCurve[m])));
+    centralGr->SetLineColor(kBlack);
+    centralGr->SetLineWidth(2);
+    centralGr->SetMarkerSize(0.1);
+
+    graphs[0]->GetYaxis()->SetRangeUser(0, 0.5);
+    graphs[0]->GetXaxis()->SetRangeUser(0.001, 0.25);
+    graphs[0]->GetXaxis()->SetLimits(0.0001, 0.25);
+    graphs[0]->GetXaxis()->SetTitle("mass [eV]");
+    graphs[0]->GetXaxis()->SetTitleSize(0.04);
+    graphs[0]->GetXaxis()->SetTitleOffset(1.15);
+    graphs[0]->GetXaxis()->SetLabelSize(0.04);
+
+    //   graphs[0]->GetYaxis()->SetLabelFont(43);
+    graphs[0]->GetYaxis()->SetTitle("g_{a#gamma} [10^{-10} GeV^{-1}]");
+    graphs[0]->GetYaxis()->SetTitleOffset(1.5);
+    graphs[0]->GetYaxis()->SetTitleSize(0.04);
+    graphs[0]->GetYaxis()->SetLabelSize(0.04);
+    // graphs[0]->GetYaxis()->SetLabelOffset(0);
+    // graphs[0]->GetYaxis()->SetLabelFont(43);
+    graphs[0]->Draw("AL");
+
+    TGraph* randomGr = new TGraph();
+    std::vector<Double_t> randomCurve = fCurves[13];
+    for (size_t m = 0; m < randomCurve.size(); m++)
+        randomGr->SetPoint(randomGr->GetN(), fParameterizationNodes[m],
+                           TMath::Sqrt(TMath::Sqrt(randomCurve[m])));
+    randomGr->SetLineColor(kBlack);
+    randomGr->SetLineWidth(1);
+    randomGr->SetMarkerSize(0.3);
+    randomGr->SetMarkerStyle(4);
+
+    std::vector<TGraph*> shadeGraphs;
+
+    int M = (int)levelCurves.size();
+    for (int x = 0; x < M / 2; x++) {
+        TGraph* shade = new TGraph();
+        int N = levelCurves[0].size();
+        for (size_t m = 0; m < levelCurves[0].size(); m++)
+            shade->SetPoint(shade->GetN(), fParameterizationNodes[m],
+                            TMath::Sqrt(TMath::Sqrt(levelCurves[x][m])));
+        for (int m = N - 1; m >= 0; --m)
+            shade->SetPoint(shade->GetN(), fParameterizationNodes[m],
+                            TMath::Sqrt(TMath::Sqrt(levelCurves[M - 1 - x][m])));
+        shade->SetFillColorAlpha(kRed, 0.25);
+        shade->Draw("f");
+        shadeGraphs.push_back(shade);
+    }
+
+    for (unsigned int n = 1; n < graphs.size(); n++) graphs[n]->Draw("Lsame");
+    randomGr->Draw("LPsame");
+    // centralGr->Draw("Lsame");
+
+    return fCanvas;
+}
+
+/////////////////////////////////////////////
+/// \brief It scans all the experiment signals parametric nodes to build a complete list
+/// of nodes used to build a complete sensitivity curve. Some experiments may be
+/// sensitivy to a particular node, while others may be sensitivy to another. If more
+/// than one experiment is sensitivy to a given node, the sensitivity will be combined
+/// later on.
+///
+void TRestSensitivity::ExtractExperimentParameterizationNodes(Bool_t rescan) {
+    if (fParameterizationNodes.empty() || rescan) {
+        fParameterizationNodes.clear();
+
+        for (const auto& experiment : fExperiments) {
+            std::vector<Double_t> nodes = experiment->GetSignal()->GetParameterizationNodes();
+            fParameterizationNodes.insert(fParameterizationNodes.end(), nodes.begin(), nodes.end());
+
+            std::sort(fParameterizationNodes.begin(), fParameterizationNodes.end());
+            auto last = std::unique(fParameterizationNodes.begin(), fParameterizationNodes.end());
+            fParameterizationNodes.erase(last, fParameterizationNodes.end());
+        }
+    }
+}
+
+void TRestSensitivity::PrintParameterizationNodes() {
+    std::cout << "Curve sensitivity nodes: ";
+    for (const auto& node : fParameterizationNodes) std::cout << node << "\t";
+    std::cout << std::endl;
+}
+
+/////////////////////////////////////////////
+/// \brief Prints on screen the information about the metadata members of TRestAxionSolarFlux
+///
+void TRestSensitivity::PrintMetadata() {
+    TRestMetadata::PrintMetadata();
+
+    RESTMetadata << " - Number of parameterization nodes : " << GetNumberOfNodes() << RESTendl;
+    RESTMetadata << " - Number of experiments loaded : " << GetNumberOfExperiments() << RESTendl;
+    RESTMetadata << " - Number of sensitivity curves generated : " << GetNumberOfCurves() << RESTendl;
+    RESTMetadata << " " << RESTendl;
+    RESTMetadata << " You may access experiment info using TRestSensitivity::GetExperiment(n)" << RESTendl;
+
+    RESTMetadata << "----" << RESTendl;
+}
diff --git a/source/framework/tools/inc/TRestPhysics.h b/source/framework/tools/inc/TRestPhysics.h
index dca1f063788b7d37b23f2499b57d569ca1b8ecb3..cd4fc365ca07c9d644c5aa528d017167e5b2673a 100644
--- a/source/framework/tools/inc/TRestPhysics.h
+++ b/source/framework/tools/inc/TRestPhysics.h
@@ -49,7 +49,7 @@ constexpr double kBoltzman = 1.380E-23;
 constexpr double hPlanck = 1.054E-34;
 
 /// A meter in eV
-constexpr double PhMeterIneV = 5067731.236453719;  // 8.0655447654281218E5;// 506.773123645372;
+constexpr double PhMeterIneV = 5067731.236453719;
 /// A second in eV (using natural units)
 constexpr double secondIneV = 1519225802531030.2;
 /// Electron charge in natural units
@@ -76,16 +76,16 @@ TVector3 GetPlaneVectorIntersection(const TVector3& pos, const TVector3& dir, TV
                                     TVector3 const& a);
 
 TVector3 GetParabolicVectorIntersection(const TVector3& pos, const TVector3& dir, const Double_t alpha,
-                                        const Double_t R3, const Double_t lMirr);
+                                        const Double_t R3);
 
-TVector3 GetHyperbolicVectorIntersection(const TVector3& pos, const TVector3& dir, const Double_t alpha,
-                                         const Double_t R3, const Double_t lMirr, const Double_t focal);
+TVector3 GetHyperbolicVectorIntersection(const TVector3& pos, const TVector3& dir, const Double_t beta,
+                                         const Double_t R3, const Double_t focal);
 
 TVector3 GetConeNormal(const TVector3& pos, const Double_t alpha, const Double_t R = 0);
 
 TVector3 GetParabolicNormal(const TVector3& pos, const Double_t alpha, const Double_t R3);
 
-TVector3 GetHyperbolicNormal(const TVector3& pos, const Double_t alpha, const Double_t R3,
+TVector3 GetHyperbolicNormal(const TVector3& pos, const Double_t beta, const Double_t R3,
                              const Double_t focal);
 
 TMatrixD GetConeMatrix(const TVector3& d, const Double_t cosTheta);
diff --git a/source/framework/tools/inc/TRestStringHelper.h b/source/framework/tools/inc/TRestStringHelper.h
index 879e5ee4348b4d97617a1066ef10d66620143313..c34c8cbcfaf378bc2290ea45d819d53431a9716d 100644
--- a/source/framework/tools/inc/TRestStringHelper.h
+++ b/source/framework/tools/inc/TRestStringHelper.h
@@ -35,6 +35,7 @@ Float_t StringToFloat(std::string in);
 Double_t StringToDouble(std::string in);
 Int_t StringToInteger(std::string in);
 std::string IntegerToString(Int_t n, std::string format = "%d");
+std::vector<int> IntegerToBinary(int number, size_t dimension = 0);
 std::string DoubleToString(Double_t d, std::string format = "%8.6e");
 Bool_t StringToBool(std::string booleanString);
 Long64_t StringToLong(std::string in);
@@ -45,6 +46,7 @@ std::vector<std::string> Split(std::string in, std::string separator, bool allow
 std::vector<double> StringToElements(std::string in, std::string separator);
 std::vector<double> StringToElements(std::string in, std::string headChar, std::string separator,
                                      std::string tailChar);
+std::string RemoveDelimiters(std::string in);
 std::string RemoveWhiteSpaces(std::string in);
 std::string Replace(std::string in, std::string thisString, std::string byThisString, size_t fromPosition = 0,
                     Int_t N = 0);
diff --git a/source/framework/tools/inc/TRestTools.h b/source/framework/tools/inc/TRestTools.h
index 58836b4bf27cdef5db8f4a7728f2fdbb342735cc..68f2fa9e354a740578097523e500ae1fa708d9a4 100644
--- a/source/framework/tools/inc/TRestTools.h
+++ b/source/framework/tools/inc/TRestTools.h
@@ -42,6 +42,10 @@
 #define EXTERN_IMP
 #endif
 
+#ifndef WIN32
+#include <unistd.h>
+#endif
+
 const std::string PARAMETER_NOT_FOUND_STR = "NO_SUCH_PARA";
 const double PARAMETER_NOT_FOUND_DBL = -99999999;
 
@@ -63,6 +67,8 @@ class TRestTools {
                               Int_t skipLines = 0, std::string separator = "\t");
     static int ReadASCIITable(std::string fName, std::vector<std::vector<Float_t>>& data, Int_t skipLines = 0,
                               std::string separator = "\t");
+    static int ReadASCIITable(std::string fName, std::vector<std::vector<std::string>>& data,
+                              Int_t skipLines = 0, std::string separator = "\t");
 
     static int ReadCSVFile(std::string fName, std::vector<std::vector<Double_t>>& data, Int_t skipLines = 0);
     static int ReadCSVFile(std::string fName, std::vector<std::vector<Float_t>>& data, Int_t skipLines = 0);
@@ -119,18 +125,20 @@ class TRestTools {
     static std::string GetPureFileName(const std::string& fullPathFileName);
     static std::string SearchFileInPath(std::vector<std::string> path, std::string filename);
     static bool CheckFileIsAccessible(const std::string&);
-    static std::vector<std::string> GetFilesMatchingPattern(std::string pattern);
+    static std::vector<std::string> GetFilesMatchingPattern(std::string pattern, bool unlimited = false);
     static int ConvertVersionCode(std::string in);
     static std::istream& GetLine(std::istream& is, std::string& t);
 
     static std::string Execute(std::string cmd);
 
-    static std::string DownloadRemoteFile(const std::string& remoteFile);
+    static std::string DownloadRemoteFile(const std::string& remoteFile, bool pidPrefix = false);
     static int DownloadRemoteFile(std::string remoteFile, std::string localFile);
     static int UploadToServer(std::string localFile, std::string remoteFile, std::string methodUrl = "");
 
     static std::string POSTRequest(const std::string& url, const std::map<std::string, std::string>& keys);
     static void ChangeDirectory(const std::string& toDirectory);
+
+    static std::vector<int> CanvasDivisions(int n);
 };
 
 namespace REST_InitTools {
diff --git a/source/framework/tools/src/TRestPhysics.cxx b/source/framework/tools/src/TRestPhysics.cxx
index 5b6dc3098964cfb2bc662fc8a675574a94fedc73..ab272147f269b37eecacc757373b16601b8a0be8 100644
--- a/source/framework/tools/src/TRestPhysics.cxx
+++ b/source/framework/tools/src/TRestPhysics.cxx
@@ -85,14 +85,14 @@ TVector3 GetPlaneVectorIntersection(const TVector3& pos, const TVector3& dir, co
 
 //////////////////////////////////////////////
 /// This method will find the intersection between a vector and a parabolic shape where `alpha` is the angle
-/// between the optical axis and the paraboloid at the plane where the paraboloid has a radius of `R3`.
-/// The paraboloid is rotationally symmetric around the optical axis. `alpha` in rad.
-/// The region in which the intersection can happen here is between `-lMirr` and 0 on the z (optical) axis
+/// between the z-axis and the paraboloid at the plane where the paraboloid has a radius of `R3`.
+/// The paraboloid is rotationally symmetric around the z-axis. `alpha` in rad.
+/// The region in which the intersection can happen here is in negative direction on the z-axis.
 ///
-/// In case no intersection is found this method returns the unmodified input position
+/// In case no intersection is found this method returns the unmodified input position.
 ///
 TVector3 GetParabolicVectorIntersection(const TVector3& pos, const TVector3& dir, const Double_t alpha,
-                                        const Double_t R3, const Double_t lMirr) {
+                                        const Double_t R3) {
     Double_t e = 2 * R3 * TMath::Tan(alpha);
     Double_t a = dir.X() * dir.X() + dir.Y() * dir.Y();
     Double_t b = 2 * (pos.X() * dir.X() + pos.Y() * dir.Y()) + e * dir.Z();
@@ -101,9 +101,11 @@ TVector3 GetParabolicVectorIntersection(const TVector3& pos, const TVector3& dir
     if (a != 0) {
         Double_t root1 = (-half_b - TMath::Sqrt(half_b * half_b - a * c)) / a;
         Double_t root2 = (-half_b + TMath::Sqrt(half_b * half_b - a * c)) / a;
-        if (pos.Z() + root1 * dir.Z() > -lMirr and pos.Z() + root1 * dir.Z() < 0) {
+        Double_t int1 = pos.Z() + root1 * dir.Z();
+        Double_t int2 = pos.Z() + root2 * dir.Z();
+        if (int1 < 0 and int2 < 0 and int1 > int2) {
             return pos + root1 * dir;
-        } else if (pos.Z() + root2 * dir.Z() > -lMirr and pos.Z() + root2 * dir.Z() < 0) {
+        } else if (int1 < 0 and int2 < 0 and int1 < int2) {
             return pos + root2 * dir;
         }
         return pos;
@@ -112,17 +114,17 @@ TVector3 GetParabolicVectorIntersection(const TVector3& pos, const TVector3& dir
 }
 
 //////////////////////////////////////////////
-/// This method will find the intersection between a vector and a hyperbolic shape where 3 * `alpha` is the
-/// angle between the optical axis and the hyperboloid at the plane where the hyperboloid has a radius of
-/// `R3`. The hyperboloid is rotationally symmetric around the optical axis. `alpha` in rad. The region in
-/// which the intersection can happen here is between 0 and `lMirr` on the `z` (optical) axis
+/// This method will find the intersection between a vector and a hyperbolic shape where beta = 3 * `alpha` is
+/// the angle between the z-axis and the hyperboloid at the plane where the hyperboloid has a radius of `R3`.
+/// The hyperboloid is rotationally symmetric around the z-axis. `alpha` in rad. The region in which the
+/// intersection can happen here is in positive direction on the z-axis.
 ///
-/// In case no intersection is found this method returns the unmodified input position
+/// In case no intersection is found this method returns the unmodified input position.
 ///
-TVector3 GetHyperbolicVectorIntersection(const TVector3& pos, const TVector3& dir, const Double_t alpha,
-                                         const Double_t R3, const Double_t lMirr, const Double_t focal) {
-    Double_t beta = 3 * alpha;
+TVector3 GetHyperbolicVectorIntersection(const TVector3& pos, const TVector3& dir, const Double_t beta,
+                                         const Double_t R3, const Double_t focal) {
     Double_t e = 2 * R3 * TMath::Tan(beta);
+    Double_t alpha = beta / 3;
     /// Just replaced here *TMath::Cot by /TMath::Tan to fix compilation issues
     Double_t g = 2 * R3 * TMath::Tan(beta) / (focal + R3 / TMath::Tan(2 * alpha));
     Double_t a = dir.X() * dir.X() + dir.Y() * dir.Y() - g * dir.Z() * dir.Z();
@@ -131,12 +133,13 @@ TVector3 GetHyperbolicVectorIntersection(const TVector3& pos, const TVector3& di
     Double_t c = pos.X() * pos.X() + pos.Y() * pos.Y() - R3 * R3 + e * pos.Z() - g * pos.Z() * pos.Z();
     Double_t root1 = (-half_b - TMath::Sqrt(half_b * half_b - a * c)) / a;
     Double_t root2 = (-half_b + TMath::Sqrt(half_b * half_b - a * c)) / a;
-    if (pos.Z() + root1 * dir.Z() > 0 and pos.Z() + root1 * dir.Z() < lMirr) {
+    Double_t int1 = pos.Z() + root1 * dir.Z();
+    Double_t int2 = pos.Z() + root2 * dir.Z();
+    if (int1 > 0 and int2 > 0 and int1 < int2) {
         return pos + root1 * dir;
-    } else if (pos.Z() + root2 * dir.Z() > 0 and pos.Z() + root2 * dir.Z() < lMirr) {
+    } else if (int1 > 0 and int2 > 0 and int1 > int2) {
         return pos + root2 * dir;
     }
-
     return pos;
 }
 
@@ -199,7 +202,7 @@ TVector3 GetConeNormal(const TVector3& pos, const Double_t alpha, const Double_t
 ///////////////////////////////////////////////
 /// \brief This method returns the normal vector on a parabolic surface pointing towards the inside
 /// of the paraboloid. `pos` is the origin point of the normal vector on the parabolic plane and
-/// `alpha` is the angle between the paraboloid and the optical (z) axis at the plane where the
+/// `alpha` is the angle between the paraboloid and the z-axis at the plane where the
 /// paraboloid has the radius `R3`.
 ///
 TVector3 GetParabolicNormal(const TVector3& pos, const Double_t alpha, const Double_t R3) {
@@ -214,13 +217,13 @@ TVector3 GetParabolicNormal(const TVector3& pos, const Double_t alpha, const Dou
 ///////////////////////////////////////////////
 /// \brief This method returns the normal vector on a hyperbolic surface pointing towards the inside
 /// of the hyperboloid. `pos` is the origin point of the normal vector on the hyperbolic plane and
-/// `beta` is the angle between the hyperboloid and the optical (z) axis at the plane where the
+/// `beta` is the angle between the hyperboloid and the z-axis at the plane where the
 /// hyperboloid has the radius `R3`.
 ///
-TVector3 GetHyperbolicNormal(const TVector3& pos, const Double_t alpha, const Double_t R3,
+TVector3 GetHyperbolicNormal(const TVector3& pos, const Double_t beta, const Double_t R3,
                              const Double_t focal) {
     TVector3 normalVec = pos;
-    Double_t beta = 3 * alpha;
+    Double_t alpha = beta / 3;
     /// Just replaced here *TMath::Cot by /TMath::Tan to fix compilation issues
     Double_t m = 1 / (R3 * TMath::Tan(beta) * (1 - 2 * pos.Z() / (focal + R3 / TMath::Tan(2 * alpha))) /
                       TMath::Sqrt(R3 * R3 - R3 * 2 * TMath::Tan(beta) * pos.Z() *
diff --git a/source/framework/tools/src/TRestStringHelper.cxx b/source/framework/tools/src/TRestStringHelper.cxx
index 0d96327b617543c68db8ba7904fca4a92fa5c3e2..101e56a9973a8589b545e749f9c0971c3de3b47f 100644
--- a/source/framework/tools/src/TRestStringHelper.cxx
+++ b/source/framework/tools/src/TRestStringHelper.cxx
@@ -256,11 +256,13 @@ vector<string> REST_StringHelper::Split(string in, string separator, bool allowB
 ///////////////////////////////////////////////
 /// \brief Convert the input string into a  vector of double elements
 ///
+/// The method will remove any delimiters found in the string (), [] or {}.
+///
 /// e.g. Input: "1,2,3,4", Output: {1.,2.,3.,4.}
 ///
 vector<double> REST_StringHelper::StringToElements(string in, string separator) {
     vector<double> result;
-    vector<string> vec_str = REST_StringHelper::Split(in, separator);
+    vector<string> vec_str = REST_StringHelper::Split(RemoveDelimiters(in), separator);
     for (unsigned int i = 0; i < vec_str.size(); i++) {
         double temp = REST_StringHelper::StringToDouble(vec_str[i]);
         result.push_back(temp);
@@ -294,6 +296,19 @@ vector<double> REST_StringHelper::StringToElements(string in, string headChar, s
     return result;
 }
 
+///////////////////////////////////////////////
+/// \brief Returns the input string removing any delimiters ({[]})
+///
+string REST_StringHelper::RemoveDelimiters(string in) {
+    string out = in;
+    size_t pos = out.find_first_of("+-*/e^%");
+    while ((pos = out.find_first_of("({[]})")) != string::npos) {
+        out.erase(pos, 1);
+    }
+
+    return out;
+}
+
 ///////////////////////////////////////////////
 /// \brief Returns the input string removing all white spaces.
 ///
@@ -464,7 +479,7 @@ Int_t REST_StringHelper::DiffString(const string& source, const string& target)
 }
 
 ///////////////////////////////////////////////
-/// \brief Replace every occurences of **thisSring** by **byThisString** inside
+/// \brief Replace any occurences of **thisSring** by **byThisString** inside
 /// string **in**.
 ///
 string REST_StringHelper::Replace(string in, string thisString, string byThisString, size_t fromPosition,
@@ -618,6 +633,37 @@ Int_t REST_StringHelper::StringToInteger(string in) {
 ///
 string REST_StringHelper::IntegerToString(Int_t n, string format) { return Form(format.c_str(), n); }
 
+///////////////////////////////////////////////
+/// \brief It returns an integer vector with the binary digits decomposed.
+///
+/// Example: IntegerToBinary(7) will return { 1, 1, 1 }.
+///
+/// Optionally we can fix the minimum number of digits to be returned, so that
+/// it will be filled with zeros to the left.
+///
+/// Example: IntegerToBinary(9,8) will return { 0, 0, 0, 0, 1, 0, 0, 1 }.
+///
+std::vector<int> REST_StringHelper::IntegerToBinary(int number, size_t dimension) {
+    std::vector<int> binaryNumber;
+
+    if (number == 0) {
+        binaryNumber.insert(binaryNumber.begin(), dimension, 0);
+        if (binaryNumber.empty()) binaryNumber.push_back(0);
+        return binaryNumber;
+    }
+
+    while (number > 0) {
+        int digit = number % 2;
+        binaryNumber.insert(binaryNumber.begin(), digit);
+        number /= 2;
+    }
+
+    if (dimension > binaryNumber.size())
+        binaryNumber.insert(binaryNumber.begin(), dimension - binaryNumber.size(), 0);
+
+    return binaryNumber;
+}
+
 ///////////////////////////////////////////////
 /// \brief Gets a string from a double
 ///
diff --git a/source/framework/tools/src/TRestStringOutput.cxx b/source/framework/tools/src/TRestStringOutput.cxx
index fe9b3041ac9db751f209de667d0edc1e1a031727..d4c5e2f23d3207ea6913508d79a3fd236ff56169 100644
--- a/source/framework/tools/src/TRestStringOutput.cxx
+++ b/source/framework/tools/src/TRestStringOutput.cxx
@@ -9,10 +9,6 @@ using namespace std;
 #include <conio.h>
 #endif  // WIN32
 
-#ifdef __APPLE__
-#include <unistd.h>
-#endif
-
 int Console::GetWidth() {
 #ifdef WIN32
     CONSOLE_SCREEN_BUFFER_INFO csbi;
diff --git a/source/framework/tools/src/TRestTools.cxx b/source/framework/tools/src/TRestTools.cxx
index 0af3237fa53e6abebb54309e30952cc3cd03e989..5c868261cba4e78dc9cdfc9dcea30d1bf0b22146 100644
--- a/source/framework/tools/src/TRestTools.cxx
+++ b/source/framework/tools/src/TRestTools.cxx
@@ -174,6 +174,8 @@ template int TRestTools::PrintTable<Int_t>(std::vector<std::vector<Int_t>> data,
 template int TRestTools::PrintTable<Float_t>(std::vector<std::vector<Float_t>> data, Int_t start, Int_t end);
 template int TRestTools::PrintTable<Double_t>(std::vector<std::vector<Double_t>> data, Int_t start,
                                               Int_t end);
+template int TRestTools::PrintTable<std::string>(std::vector<std::vector<std::string>> data, Int_t start,
+                                                 Int_t end);
 
 ///////////////////////////////////////////////
 /// \brief Writes the contents of the vector table given as argument to `fname`.
@@ -507,6 +509,58 @@ template std::vector<Float_t> TRestTools::GetColumnFromTable<Float_t>(
 template std::vector<Double_t> TRestTools::GetColumnFromTable<Double_t>(
     const std::vector<std::vector<Double_t>>& data, unsigned int column);
 
+template std::vector<std::string> TRestTools::GetColumnFromTable<std::string>(
+    const std::vector<std::vector<std::string>>& data, unsigned int column);
+
+///////////////////////////////////////////////
+/// \brief Reads an ASCII file containing a table with values
+///
+/// This method will open the file fName. This file should contain a tabulated
+/// ASCII table containing any format values. The values on the table will be
+/// loaded in the matrix provided through the argument `data`. The content of
+/// `data` will be cleared in this method.
+///
+/// If any header in the file is present, it should be skipped using the argument
+///`skipLines` or preceding any line inside the header using `#`.
+///
+/// This table will just split the ASCII elements inside a std::string matrix
+///
+int TRestTools::ReadASCIITable(string fName, std::vector<std::vector<std::string>>& data, Int_t skipLines,
+                               std::string separator) {
+    if (!TRestTools::isValidFile((string)fName)) {
+        cout << "TRestTools::ReadASCIITable. Error" << endl;
+        cout << "Cannot open file : " << fName << endl;
+        return 0;
+    }
+
+    data.clear();
+
+    std::ifstream fin(fName);
+
+    // First we create a table with string values
+    std::vector<std::vector<string>> values;
+
+    for (string line; std::getline(fin, line);) {
+        if (skipLines > 0) {
+            skipLines--;
+            continue;
+        }
+
+        if (line.find("#") == string::npos) {
+            std::istringstream in(line);
+
+            std::string token;
+            std::vector<std::string> tokens;
+            while (std::getline(in, token, (char)separator[0])) {
+                tokens.push_back(token);
+            }
+            data.push_back(tokens);
+        }
+    }
+
+    return 1;
+}
+
 ///////////////////////////////////////////////
 /// \brief Reads an ASCII file containing a table with values
 ///
@@ -915,7 +969,11 @@ bool TRestTools::CheckFileIsAccessible(const std::string& filename) {
 /// \brief Returns a list of files whose name match the pattern string. Key word
 /// is "*". e.g. abc00*.root
 ///
-vector<string> TRestTools::GetFilesMatchingPattern(string pattern) {
+/// Argument unlimited will fix an issue with the number of files being to high.
+/// However, it causes issues when searching/listing the macros.
+/// The default value for unlimited is `false`.
+///
+vector<string> TRestTools::GetFilesMatchingPattern(string pattern, bool unlimited) {
     std::vector<string> outputFileNames;
     if (pattern != "") {
         vector<string> items = Split(pattern, "\n");
@@ -944,11 +1002,25 @@ vector<string> TRestTools::GetFilesMatchingPattern(string pattern) {
                     }
                 }
 #else
-                string a = Execute("find " + item);
-                auto b = Split(a, "\n");
+                auto path_name = SeparatePathAndName(item);
+                if (unlimited) {
+                    std::string currentDir = filesystem::current_path();
+                    ChangeDirectory(path_name.first);
+                    string a = Execute("find -type f -name \'" + path_name.second + "\'");
+                    ChangeDirectory(currentDir);
+                    auto b = Split(a, "\n");
+
+                    for (unsigned int i = 0; i < b.size(); i++) {
+                        outputFileNames.push_back(path_name.first + "/" + b[i]);
+                    }
 
-                for (unsigned int i = 0; i < b.size(); i++) {
-                    outputFileNames.push_back(b[i]);
+                } else {
+                    string a = Execute("find " + item);
+                    auto b = Split(a, "\n");
+
+                    for (unsigned int i = 0; i < b.size(); i++) {
+                        outputFileNames.push_back(b[i]);
+                    }
                 }
 #endif
 
@@ -1049,7 +1121,7 @@ std::istream& TRestTools::GetLine(std::istream& is, std::string& t) {
 /// will be used, including scp, wget. Downloads to REST_USER_PATH + "/download/" + filename
 /// by default.
 ///
-std::string TRestTools::DownloadRemoteFile(const string& url) {
+std::string TRestTools::DownloadRemoteFile(const string& url, bool pidPrefix) {
     string pureName = TRestTools::GetPureFileName(url);
     if (pureName.empty()) {
         RESTWarning << "error! (TRestTools::DownloadRemoteFile): url is not a file!" << RESTendl;
@@ -1061,7 +1133,8 @@ std::string TRestTools::DownloadRemoteFile(const string& url) {
     if (url.find("local:") == 0) {
         return Replace(url, "local:", "");
     } else {
-        string fullpath = REST_USER_PATH + "/download/" + pureName;
+        string fullpath =
+            REST_USER_PATH + "/download/" + (pidPrefix ? "PID_" + ToString(getpid()) + "_" : "") + pureName;
         int out;
         int attempts = 10;
         do {
@@ -1081,7 +1154,6 @@ std::string TRestTools::DownloadRemoteFile(const string& url) {
             return "";
         }
     }
-    return "";
 }
 
 ///////////////////////////////////////////////
@@ -1249,6 +1321,58 @@ int TRestTools::UploadToServer(string localFile, string remoteFile, string metho
 
 void TRestTools::ChangeDirectory(const string& toDirectory) { filesystem::current_path(toDirectory); }
 
+///////////////////////////////////////////////
+/// \brief It returns a vector with 2 components {a,b}, the components satisfy that `a x b = n`,
+/// being the ratio a/b as close to 1 as possible.
+///
+/// This method can be used to help dividing a canvas that will contain a number `n` of plots.
+///
+/// If `n` is a prime number, then the pair generated will be `n x 1`.
+///
+std::vector<int> TRestTools::CanvasDivisions(int n) {
+    std::vector<int> r;
+    for (int i = 2; i * i <= n; i += 1 + (i > 2)) {
+        while ((n % i) == 0) {
+            r.push_back(i);
+            n /= i;
+        }
+    }
+    if (n != 1) r.push_back(n);
+
+    while (r.size() > 2) {
+        // We multiply the 2 lowest elements and
+        // replace the elements in the vector by the result
+        auto min1 = std::min_element(r.begin(), r.end());
+        int low1 = *min1;
+
+        // Remove the first element equal to min1 (efficient way)
+        auto it = std::find(r.begin(), r.end(), low1);
+        if (it != r.end()) {
+            std::iter_swap(it, r.end() - 1);
+            r.erase(r.end() - 1);
+        }
+
+        auto min2 = std::min_element(r.begin(), r.end());
+        int low2 = *min2;
+
+        // Remove the first element equal to min2 (efficient way)
+        it = std::find(r.begin(), r.end(), low2);
+        if (it != r.end()) {
+            std::iter_swap(it, r.end() - 1);
+            r.erase(r.end() - 1);
+        }
+
+        int resultado = low1 * low2;
+        r.push_back(resultado);
+    }
+
+    std::sort(r.begin(), r.end());
+
+    if (r.size() == 1) r.push_back(1);
+
+    return r;
+}
+
 string ValueWithQuantity::ToString() const {
     string unit;
     auto value = fValue;
diff --git a/source/libraries/axion b/source/libraries/axion
index 21375385fc4cad92bda60ba29f641b9afad9339e..3802d108bed118f4777ca142273aabaf368770c0 160000
--- a/source/libraries/axion
+++ b/source/libraries/axion
@@ -1 +1 @@
-Subproject commit 21375385fc4cad92bda60ba29f641b9afad9339e
+Subproject commit 3802d108bed118f4777ca142273aabaf368770c0
diff --git a/source/libraries/legacy b/source/libraries/legacy
index 83142430760cb35c2b3c597b24c1d884a01c0fff..caa4e2540f87b33071b1ae8e21f277aa69e60861 160000
--- a/source/libraries/legacy
+++ b/source/libraries/legacy
@@ -1 +1 @@
-Subproject commit 83142430760cb35c2b3c597b24c1d884a01c0fff
+Subproject commit caa4e2540f87b33071b1ae8e21f277aa69e60861