diff --git a/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_DeadPixelTool.h b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_DeadPixelTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..06cea1d78e5740acc3cdeef3a071759004934853
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_DeadPixelTool.h
@@ -0,0 +1,37 @@
+/*
+	Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef AFP_CALIBRATION_AFP_DEADPIXELTOOL_H
+#define AFP_CALIBRATION_AFP_DEADPIXELTOOL_H
+
+// Package includes
+#include "AFP_Calibration/IAFP_GenericPixelTool.h"
+
+// ROOT includes
+#include "TH2F.h"
+
+// STL includes
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility>
+ 
+class AFP_DeadPixelTool : public IAFP_GenericPixelTool {
+public:
+	AFP_DeadPixelTool() : m_range(0.0) {}
+	
+	int Identify(std::shared_ptr<const TH2F> input, std::vector<TH2F>& output) const override;
+
+	void setRange(float r) {m_range=r;}
+	float getRange() const {return m_range;}
+
+private:
+	
+	float m_range; // To prevent pixels be identified as dead when there is low statistics
+	
+	double getNeighbours(std::shared_ptr<const TH2F> input, int row_ID, int col_ID) const;
+	std::vector<std::pair<int,int>> getLegitPixels(std::shared_ptr<const TH2F> input, const int col_ID, const int row_ID) const;
+};
+
+#endif // AFP_CALIBRATION_AFP_DEADPIXELTOOL_H
diff --git a/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_NoisyPixelTool.h b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_NoisyPixelTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..c7e2b7fadab3ece4f4961052eed339d92813af1c
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_NoisyPixelTool.h
@@ -0,0 +1,58 @@
+/*
+	Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef AFP_CALIBRATION_AFP_NOISYPIXELTOOL_H
+#define AFP_CALIBRATION_AFP_NOISYPIXELTOOL_H
+
+// Package includes
+#include "AFP_Calibration/IAFP_GenericPixelTool.h"
+
+// ROOT includes
+#include "TF1.h"
+#include "TH2F.h"
+
+// STL includes
+#include <string>
+#include <vector>
+#include <memory>
+#include <tuple>
+#include <utility>
+ 
+class AFP_NoisyPixelTool : public IAFP_GenericPixelTool {
+public:
+	AFP_NoisyPixelTool() : m_threshNoisy(1.4), m_threshLEff(0.6), m_sensitivity(0.0), m_methods({"2_ROW","2_COL"}) {}
+	
+	int Identify(std::shared_ptr<const TH2F> input, std::vector<TH2F>& output) const override;
+	
+	std::vector<std::vector<double>> makeFits(std::shared_ptr<const TH2F> input) const;
+	
+	void setThreshNoisy(double t) {m_threshNoisy=t;}
+	double getThreshNoisy() const {return m_threshNoisy;}
+	
+	void setThreshLeff(double t) {m_threshLEff=t;}
+	double getThreshLeff() const {return m_threshLEff;}
+	
+	void setSensitivity(double s) {m_sensitivity=s;}
+	double getSensitivity() const {return m_sensitivity;}
+	
+	void setMethods(std::vector<std::string> m) {m_methods=m;}
+	std::vector<std::string> getMethods() const {return m_methods;}
+
+private:
+	
+	double m_threshNoisy; // Minimum efficiency of pixels to be classified as noisy
+	double m_threshLEff; // Maximum efficiency of pixels to be classified as low efficiency
+	double m_sensitivity; // with higher value, the algorithm will be more prone to find noisy pixels near low efficiency ones
+	
+	std::vector<std::string> m_methods; // methods to be used for identification of noisy pixels; available: \"2_ROW\", \"2_COL\", \"4_PIX\", \"8_PIX\", and \"FIT\""};
+	
+	std::vector<std::pair<int,int>> getLegitPixels(std::shared_ptr<const TH2F> input, const int col_ID, const int row_ID, const std::string& method) const;
+	
+	TH2F countInactivePixelsAround(std::shared_ptr<const TH2F> input) const;
+	double getNeighbours(std::shared_ptr<const TH2F> input, int row_ID, int col_ID) const;
+	std::tuple<TH2F,TH2F,TH2F,TH2F> findLEffAndNoisyPixels(std::shared_ptr<const TH2F> input, const TH2F& inact, const std::string& method) const;
+	void filterLEffPixelsAroundNoisy(std::shared_ptr<const TH2F> input, const TH2F& inact, TH2F& fleff, TH2F& fnoisy, TH2F& eleff, TH2F& enoisy, const std::string& method) const;
+};
+
+#endif // AFP_CALIBRATION_AFP_NOISYPIXELTOOL_H
diff --git a/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_PixelHistoFiller.h b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_PixelHistoFiller.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ffe1d505b8110bec16d5aee68b74a93a2983e1c
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_PixelHistoFiller.h
@@ -0,0 +1,56 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef AFP_CALIBRATION_AFP_PIXELHISTOFILLER_H
+#define AFP_CALIBRATION_AFP_PIXELHISTOFILLER_H
+
+// Framework includes
+#include "AthenaBaseComps/AthAlgorithm.h"
+#include "StoreGate/ReadHandleKey.h"
+#include "xAODEventInfo/EventInfo.h"
+#include "xAODForward/AFPSiHitContainer.h"
+#include "xAODForward/AFPSiHit.h"
+#include "AFP_Geometry/AFP_constants.h"
+
+// STL includes
+#include <string>
+#include <vector>
+#include <memory>
+
+// ROOT includes
+#include "TFile.h"
+#include "TH2F.h"
+
+/**
+ * @class AFP_PixelHistoFiller
+ **/
+
+class AFP_PixelHistoFiller : public AthAlgorithm {
+public:
+  AFP_PixelHistoFiller(const std::string& name, ISvcLocator* pSvcLocator);
+  virtual ~AFP_PixelHistoFiller() = default;
+
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute() override;
+  virtual StatusCode finalize() override;
+
+private:	
+	static const int m_nStations=4;
+	static const int m_nLayers=4;
+	int m_nPixelsX;
+	int m_nPixelsY;
+	
+	std::vector<TH2F> m_pixelHits[m_nStations][m_nLayers];
+	
+	Gaudi::Property<int> m_LBRangeLength{this, "LBRangeLength",5, "How many lumiblocks should be merged together to have reasonable statistics"};
+	
+	SG::ReadHandleKey<xAOD::EventInfo> m_eventInfoKey { this, "EventInfoKey", "EventInfo", "name of EventInfo container" };
+	SG::ReadHandleKey<xAOD::AFPSiHitContainer> m_afpHitContainerKey{ this ,"AFPHitContainerKey", "AFPSiHitContainer", "name of AFPSiHitContainer" };
+};
+
+#ifndef __CINT__
+  CLASS_DEF( AFP_PixelHistoFiller , 161453345 , 1 )
+#endif
+
+#endif // AFP_CALIBRATION_AFP_PIXELHISTOFILLER_H
diff --git a/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_PixelIdentifier.h b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_PixelIdentifier.h
new file mode 100644
index 0000000000000000000000000000000000000000..1d67e7c04c036c06695d2479feee6ce007cfa543
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/AFP_PixelIdentifier.h
@@ -0,0 +1,45 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+#ifndef AFP_CALIBRATION_AFP_PIXELIDENTIFIER_H
+#define AFP_CALIBRATION_AFP_PIXELIDENTIFIER_H
+
+#include "AFP_Geometry/AFP_constants.h"
+#include "AFP_Calibration/AFP_DeadPixelTool.h"
+#include "AFP_Calibration/AFP_NoisyPixelTool.h"
+
+// STL includes
+#include <string>
+#include <vector>
+#include <memory>
+#include <utility>
+
+// ROOT includes
+#include "TFile.h"
+#include "TH2F.h"
+
+
+class AFP_PixelIdentifier {
+public:
+	AFP_PixelIdentifier(const std::string input_name="AFP_PixelHistoFiller.root", const std::string output_name="AFP_PixelIdentifier.root", std::vector<std::string> m_pixelTools_names={"AFP_DeadPixel", "AFP_NoisyPixel"});
+	~AFP_PixelIdentifier() = default;
+
+	int execute();
+  
+private:
+	std::unique_ptr<TFile> m_input_file;
+	std::unique_ptr<TFile> m_output_file;
+	
+	static const int m_nStations=4;
+	static const int m_nLayers=4;
+	int m_nPixelsX;
+	int m_nPixelsY;
+	
+	std::vector<TH2F> m_pixelHits[m_nStations][m_nLayers];
+	
+	std::vector<std::string> m_pixelTools_names;
+	std::vector<std::unique_ptr<IAFP_GenericPixelTool>> m_pixelTools;
+};
+
+
+#endif // AFP_CALIBRATION_AFP_PIXELIDENTIFIER_H
diff --git a/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/ATLAS_CHECK_THREAD_SAFETY b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/ATLAS_CHECK_THREAD_SAFETY
new file mode 100644
index 0000000000000000000000000000000000000000..df3b2b4c2e86219b5f7824fd5a3e22a0939fab03
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/ATLAS_CHECK_THREAD_SAFETY
@@ -0,0 +1 @@
+ForwardDetectors/AFP/AFP_Calibration
\ No newline at end of file
diff --git a/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/IAFP_GenericPixelTool.h b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/IAFP_GenericPixelTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..be5f714c15396806a5d676cf2d7c8dcfe810522c
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/AFP_Calibration/IAFP_GenericPixelTool.h
@@ -0,0 +1,22 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+#ifndef AFP_CALIBRATION_IAFP_GENERICPIXELTOOL_H
+#define AFP_CALIBRATION_IAFP_GENERICPIXELTOOL_H
+
+#include <vector>
+#include <memory>
+
+#include "TH2F.h"
+
+/**
+ * @class IAFP_GenericPixelTool
+ * @brief Base class for all pixel identifier tool
+ **/
+ 
+class IAFP_GenericPixelTool{
+public:
+	virtual int Identify(std::shared_ptr<const TH2F> input, std::vector<TH2F>& output) const = 0;
+}; 
+
+#endif // AFP_CALIBRATION_IAFP_GENERICPIXELTOOL_H
diff --git a/ForwardDetectors/AFP/AFP_Calibration/CMakeLists.txt b/ForwardDetectors/AFP/AFP_Calibration/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bd49226cb4dca6b9a4deb6fc2f3ffb207af512f3
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+
+# Declare the package name:
+atlas_subdir( AFP_Calibration )
+
+find_package( ROOT COMPONENTS MathCore Core Tree Hist RIO pthread )
+
+atlas_add_library( AFP_CalibrationLib
+                   src/*.cxx
+                   PUBLIC_HEADERS AFP_Calibration
+                   INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} 
+                   LINK_LIBRARIES ${ROOT_LIBRARIES} AFP_Geometry AthenaBaseComps AthenaKernel GaudiKernel StoreGateLib xAODEventInfo xAODForward )
+
+atlas_add_component( AFP_Calibration
+                     src/components/*.cxx
+                     LINK_LIBRARIES AFP_CalibrationLib )
+
+# Install files from the package:
+atlas_install_joboptions( scripts/*.py )
+
+atlas_add_executable( run_AFP_PixelIdentifier
+   scripts/run_AFP_PixelIdentifier.cxx
+   LINK_LIBRARIES AFP_CalibrationLib )
diff --git a/ForwardDetectors/AFP/AFP_Calibration/scripts/AFP_PixelHistoFiller.py b/ForwardDetectors/AFP/AFP_Calibration/scripts/AFP_PixelHistoFiller.py
new file mode 100644
index 0000000000000000000000000000000000000000..67487946dcc6a027084ccd43576b64a71ebc70fa
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/scripts/AFP_PixelHistoFiller.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+
+"""
+The AFP Calibration Loop, filling the histograms, usage e.g.:
+    athena AFP_Calibration/AFP_PixelHistoFiller.py --filesInput="/afs/cern.ch/user/p/pbalek/workspace/public/data17_13TeV.00338480.calibration_AFP.AOD/data17_13TeV.00338480.calibration_AFP.AOD._lb0000._SFO-1._0007.root"
+"""
+
+
+# it's important to have "POOLAccess" here. With "AthenaAccess" or others, the EventContext won't be filled properly
+jps.AthenaCommonFlags.AccessMode = "POOLAccess" 
+
+# overwrite with --evtMax
+jps.AthenaCommonFlags.EvtMax=-1
+	
+# overwrite with --filesInput
+jps.AthenaCommonFlags.FilesInput = ["/afs/cern.ch/user/p/pbalek/workspace/public/data17_13TeV.00338480.calibration_AFP.AOD/data17_13TeV.00338480.calibration_AFP.AOD._lb0000._SFO-1._0007.root"]
+	
+algo = CfgMgr.AFP_PixelHistoFiller("AFP_PixelHistoFiller")
+athAlgSeq += algo
+
diff --git a/ForwardDetectors/AFP/AFP_Calibration/scripts/run_AFP_PixelIdentifier.cxx b/ForwardDetectors/AFP/AFP_Calibration/scripts/run_AFP_PixelIdentifier.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..80102b1a33e312232b20adb747a9c0d3f734f378
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/scripts/run_AFP_PixelIdentifier.cxx
@@ -0,0 +1,58 @@
+// Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+
+
+#include <TROOT.h>
+#include <TFile.h>
+
+#include <string>
+#include <boost/program_options.hpp>
+
+#include "AFP_Calibration/AFP_PixelIdentifier.h"
+
+
+int main(int argc, char *argv[])
+{
+	
+	std::string input_file_name;
+	std::string output_file_name;
+	std::vector<std::string> pixel_tools_list;
+	
+	boost::program_options::options_description main_options("main options");
+	
+	main_options.add_options()
+	("input_file",boost::program_options::value<std::string>(&input_file_name)->default_value("AFP_PixelHistoFiller.root"),"name of output root file")
+	("output_file",boost::program_options::value<std::string>(&output_file_name)->default_value("AFP_PixelIdentifier.root"),"name of output root file")
+	("pixel_tools_list",boost::program_options::value<std::vector<std::string> >(&pixel_tools_list)->multitoken(), "list of AFP pixel tools");
+	;
+	
+	boost::program_options::variables_map vm;
+
+	try
+	{
+		boost::program_options::store(boost::program_options::parse_command_line(argc, argv, main_options), vm);
+		boost::program_options::notify(vm);
+	}
+	catch(std::exception& e)
+	{
+		std::cerr << "Bad command line argument" << std::endl;
+		std::cerr << e.what() << std::endl;
+		return 1;
+	}
+	
+	if(pixel_tools_list.empty())
+	{
+		pixel_tools_list.push_back("AFP_DeadPixel");
+		pixel_tools_list.push_back("AFP_NoisyPixel");
+	}
+	
+	AFP_PixelIdentifier identifier(input_file_name, output_file_name, pixel_tools_list);
+	identifier.execute();
+	
+	return 0;
+}
+
+
+
+
+
+
diff --git a/ForwardDetectors/AFP/AFP_Calibration/src/AFP_DeadPixelTool.cxx b/ForwardDetectors/AFP/AFP_Calibration/src/AFP_DeadPixelTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6e4372dbca5b0de75128eb3d67f864ebbc9d904f
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/src/AFP_DeadPixelTool.cxx
@@ -0,0 +1,118 @@
+/*
+	Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "AFP_Calibration/AFP_DeadPixelTool.h"
+
+int AFP_DeadPixelTool::Identify(std::shared_ptr<const TH2F> input, std::vector<TH2F>& output) const
+{
+	if(output.size()!=1)
+	{
+		return 0;
+	}
+	
+	TH2F tmp_dead_pixels(*static_cast<TH2F*>(input->Clone("tmp_dead_pixels")));
+	tmp_dead_pixels.Reset();
+	
+	TH2F& deadpixels_output = output.at(0);
+	deadpixels_output.SetName(Form("deadpixels_%s",deadpixels_output.GetName()));
+	deadpixels_output.SetTitle(Form("dead pixels, %s",deadpixels_output.GetTitle()));
+	deadpixels_output.Reset();
+	
+	auto maxHitValue=input->GetMaximum();
+	
+	// first iteration (find singular dead pixels)
+	for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+	{
+		for(int row_ID=1; row_ID<=input->GetNbinsX(); row_ID++)
+		{
+			if(input->GetBinContent(row_ID, col_ID)==0 )
+			{
+				double nNeighbours=getNeighbours(input, row_ID,col_ID);
+
+				int sum_neighbours=0;
+				int inactive_pixels_around=0;
+				
+				auto legit_pixels=getLegitPixels(input, col_ID, row_ID);
+				for(auto legpix : legit_pixels)
+				{
+					sum_neighbours+=input->GetBinContent(legpix.first, legpix.second);
+					if(input->GetBinContent(legpix.first, legpix.second)<1) ++inactive_pixels_around;
+				}
+	
+				if(inactive_pixels_around<4 && round(sum_neighbours/nNeighbours)>=m_range*maxHitValue)
+				{
+					deadpixels_output.Fill(row_ID, col_ID);
+					tmp_dead_pixels.SetBinContent(row_ID,col_ID, 1);
+				}
+			}
+		}
+	}
+	
+	// second iteration (find groups of dead pixels)
+	bool newDEAD=true;
+	while(newDEAD)
+	{	
+		newDEAD=false;
+		for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+		{
+			for(int row_ID=1; row_ID<=input->GetNbinsX(); row_ID++)
+			{	
+				if(tmp_dead_pixels.GetBinContent(row_ID,col_ID)==0 && input->GetBinContent(row_ID, col_ID)==0)
+				{
+					double nNeighbours=getNeighbours(input, row_ID,col_ID);
+
+					int sum_neighbours=0;
+					int inactive_pixels_around=0;
+					int dead_pixels_around=0;
+					
+					auto legit_pixels=getLegitPixels(input, col_ID, row_ID);
+					for(auto legpix : legit_pixels)
+					{
+						sum_neighbours+=input->GetBinContent(legpix.first, legpix.second);
+						if(input->GetBinContent(legpix.first, legpix.second)<1) ++inactive_pixels_around;
+					
+						if(tmp_dead_pixels.GetBinContent(legpix.first, legpix.second)>0) dead_pixels_around++;
+					}
+
+				
+					if(dead_pixels_around>0 && inactive_pixels_around>3 && round(sum_neighbours/nNeighbours)>=m_range*maxHitValue)
+					{
+						deadpixels_output.Fill(row_ID, col_ID);
+						tmp_dead_pixels.SetBinContent(row_ID,col_ID, 1);
+						newDEAD=true;
+					}
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+std::vector<std::pair<int,int>> AFP_DeadPixelTool::getLegitPixels(std::shared_ptr<const TH2F> input, const int col_ID, const int row_ID) const
+{	
+	std::vector<std::pair<int,int>> legit_pixels={};
+	
+	for(int r=-1; r<2; r++)
+	{
+		for(int c=-1; c<2; c++)
+		{
+			if( (row_ID+r)>=1 && (col_ID+c)>=1 && (col_ID+c)<=input->GetNbinsY() && (row_ID+r)<=input->GetNbinsX() && (r!=0 || c!=0))
+			{
+				legit_pixels.push_back(std::pair<int,int>(row_ID+r,col_ID+c));
+			}
+		}
+	}
+	
+	return legit_pixels;
+}
+
+
+double AFP_DeadPixelTool::getNeighbours(std::shared_ptr<const TH2F> input, int row_ID, int col_ID) const
+{
+	if( row_ID!=input->GetNbinsX() && row_ID!=1 && col_ID!=1 && col_ID!=input->GetNbinsY()) return 8.; // inner pixels
+	else if( (row_ID==input->GetNbinsX() || row_ID==1) && (col_ID==1 || col_ID==input->GetNbinsY())) return 3.; // corner pixels
+	else return 5; // pixels along edges
+}
diff --git a/ForwardDetectors/AFP/AFP_Calibration/src/AFP_NoisyPixelTool.cxx b/ForwardDetectors/AFP/AFP_Calibration/src/AFP_NoisyPixelTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0c3e99659f3565f6ab6a7151a48cae376fc051ff
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/src/AFP_NoisyPixelTool.cxx
@@ -0,0 +1,409 @@
+/*
+	Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "AFP_Calibration/AFP_NoisyPixelTool.h"
+
+int AFP_NoisyPixelTool::Identify(std::shared_ptr<const TH2F> input, std::vector<TH2F>& output) const
+{
+	if(output.size()!=1)
+	{
+		return 0;
+	}
+	
+	output.reserve(4);
+	
+	TH2F& template_output = output.at(0);
+	template_output.Reset();
+	
+	TH2F tmp_output1(template_output);
+	tmp_output1.SetNameTitle(Form("leffpixels_found_%s",template_output.GetName()), Form("low efficiency pixels, found, %s", template_output.GetTitle()));
+	output.push_back(tmp_output1);
+	TH2F& leffpixels_found_output = output.at(1);
+	
+	TH2F tmp_output2(template_output);
+	tmp_output2.SetNameTitle(Form("noisypixels_eff_%s",template_output.GetName()), Form("noisy pixels, efficiency, %s", template_output.GetTitle()));
+	output.push_back(tmp_output2);
+	TH2F& noisypixels_eff_output = output.at(2);
+	
+	TH2F tmp_output3(template_output);
+	tmp_output3.SetNameTitle(Form("leffpixels_eff_%s",template_output.GetName()), Form("low efficiency pixels, efficiency, %s", template_output.GetTitle()));
+	output.push_back(tmp_output3);
+	TH2F& leffpixels_eff_output = output.at(3);
+		
+	TH2F& noisypixels_found_output = output.at(0);
+	noisypixels_found_output.SetNameTitle(Form("noisypixels_found_%s",template_output.GetName()), Form("noisy pixels, found, %s", template_output.GetTitle()));
+
+	
+	if(input->GetMaximum()<0.5) return 0;
+
+	std::vector<TH2F> vec_found_leff{},vec_found_noisy{},vec_eff_leff{},vec_eff_noisy{};
+
+	for(const auto& method : m_methods)
+	{
+		TH2F tmp_eff_noisy, tmp_eff_leff;
+		TH2F tmp_found_leff, tmp_found_noisy;
+		
+		if(method!="FIT")
+		{
+			// count inactive pixels around each pixel
+			TH2F tmp_inact_pix_around=countInactivePixelsAround(input);
+			
+			// find low eff. and noisy pixels
+			std::tie(tmp_found_leff,tmp_found_noisy,tmp_eff_leff, tmp_eff_noisy) = findLEffAndNoisyPixels(input, tmp_inact_pix_around, method);
+			
+			// reconsider some pixels
+			filterLEffPixelsAroundNoisy(input, tmp_inact_pix_around, tmp_found_leff,tmp_found_noisy,tmp_eff_leff,tmp_eff_noisy, method);
+		}
+		else
+		{
+			// method is "FIT"
+			
+			tmp_eff_noisy=TH2F(*input);
+			tmp_eff_noisy.SetName("tmp_eff_noisy");
+			tmp_eff_noisy.Reset();
+			tmp_eff_leff=TH2F(*input);
+			tmp_eff_leff.SetName("tmp_eff_leff");
+			tmp_eff_leff.Reset();
+			tmp_found_noisy=TH2F(*input);
+			tmp_found_noisy.SetName("tmp_found_noisy");
+			tmp_found_noisy.Reset();
+			tmp_found_leff=TH2F(*input);
+			tmp_found_leff.SetName("tmp_found_leff");
+			tmp_found_leff.Reset();
+			
+			auto p=makeFits(input);
+			
+			const double Threshold_LEFF=5.;
+			const double Threshold_NOISY=(m_threshNoisy-1)*100;
+			
+			for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+			{
+  				for(int row_ID=1; row_ID<=input->GetNbinsX(); row_ID++)
+  				{
+  					double ratio_leff=0, ratio_noisy=0;
+  					double gauss_helper_12 = (col_ID-p[1][row_ID])/p[2][row_ID];
+  					double gauss_helper_45 = (col_ID-p[4][row_ID])/p[5][row_ID];
+	 				double fit = p[0][row_ID]*exp(-0.5*gauss_helper_12*gauss_helper_12)
+	 				           + p[3][row_ID]*exp(-0.5*gauss_helper_45*gauss_helper_45) + p[6][row_ID];
+					double sigma=std::sqrt(fit);
+					if(sigma<1) sigma=1;
+					
+					double bin_content=input->GetBinContent(row_ID,col_ID);
+					
+					if(fit>=bin_content) ratio_leff = std::abs(fit-bin_content)/sigma;
+					else                 ratio_noisy = std::abs(fit-bin_content)/sigma;
+					
+					if(bin_content!=0)
+					{
+						if( ratio_leff	> Threshold_LEFF) tmp_found_leff.SetBinContent(row_ID,col_ID,1);
+						if( ratio_noisy	> Threshold_NOISY) tmp_found_noisy.SetBinContent(row_ID,col_ID,1);
+					}
+					tmp_eff_leff.SetBinContent(row_ID,col_ID,100.*ratio_leff);
+					tmp_eff_noisy.SetBinContent(row_ID,col_ID,100.*ratio_noisy);
+				}
+			}
+		}
+		
+		// save output for this method
+		vec_found_leff.push_back(tmp_found_leff);
+		vec_found_noisy.push_back(tmp_found_noisy);
+		vec_eff_leff.push_back(tmp_eff_leff);
+		vec_eff_noisy.push_back(tmp_eff_noisy);	
+	}
+	
+	// sum found pixels for all methods, set efficiency to the maximum of all methods
+	for(unsigned int m=0; m<m_methods.size(); m++)
+	{	
+		for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+		{
+  			for(int row_ID=1; row_ID<=input->GetNbinsX(); row_ID++)
+  			{	
+				noisypixels_found_output.Fill(row_ID,col_ID, vec_found_noisy[m].GetBinContent(row_ID,col_ID));
+				leffpixels_found_output.Fill(row_ID,col_ID, vec_found_leff[m].GetBinContent(row_ID,col_ID));
+				
+				if(m==0)
+				{
+					noisypixels_eff_output.SetBinContent(row_ID,col_ID, vec_eff_noisy[m].GetBinContent(row_ID,col_ID));
+					leffpixels_eff_output.SetBinContent(row_ID,col_ID, vec_eff_leff[m].GetBinContent(row_ID,col_ID));
+				}
+				else
+				{ 
+					if(noisypixels_eff_output.GetBinContent(row_ID,col_ID)<vec_eff_noisy[m].GetBinContent(row_ID,col_ID))
+					{
+						noisypixels_eff_output.SetBinContent(row_ID,col_ID, vec_eff_noisy[m].GetBinContent(row_ID,col_ID));
+					}
+					if(leffpixels_eff_output.GetBinContent(row_ID,col_ID)>vec_eff_leff[m].GetBinContent(row_ID,col_ID))
+					{
+						leffpixels_eff_output.SetBinContent(row_ID,col_ID, vec_eff_leff[m].GetBinContent(row_ID,col_ID));
+					}
+				}
+			}
+		}
+	}
+
+	// report only pixels that are identified by all methods
+	for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+	{
+  		for(int row_ID=1; row_ID<=input->GetNbinsX(); row_ID++)
+  		{	
+			if(noisypixels_found_output.GetBinContent(row_ID,col_ID)==m_methods.size())	noisypixels_found_output.SetBinContent(row_ID,col_ID,1);
+			else noisypixels_found_output.SetBinContent(row_ID,col_ID,0);
+			
+			if(leffpixels_found_output.GetBinContent(row_ID,col_ID)==m_methods.size())	leffpixels_found_output.SetBinContent(row_ID,col_ID,1);
+			else leffpixels_found_output.SetBinContent(row_ID,col_ID,0);
+		}
+	}
+
+	return 0;
+}
+
+
+std::vector<std::pair<int,int>> AFP_NoisyPixelTool::getLegitPixels(std::shared_ptr<const TH2F> input, const int col_ID, const int row_ID, const std::string& method="8_PIX") const
+{
+	// method="8_PIX" means to investigate all pixels around
+	
+	std::vector<std::pair<int,int>> legit_pixels={};
+	
+	for(int r=-1; r<2; r++)
+	{
+		for(int c=-1; c<2; c++)
+		{
+			if( (row_ID+r)>=1 && (col_ID+c)>=1 && (col_ID+c)<=input->GetNbinsY() && (row_ID+r)<=input->GetNbinsX() && (r!=0 || c!=0))
+			{
+				if( (method=="2_ROW" && c==0) || (method=="2_COL" && r==0)
+					|| (method=="4_PIX" && (c==0 || r==0)) || (method=="8_PIX") )
+				{
+					legit_pixels.push_back(std::pair<int,int>(row_ID+r,col_ID+c));
+				}
+			}
+		}
+	}
+	
+	return legit_pixels;
+}
+
+
+TH2F AFP_NoisyPixelTool::countInactivePixelsAround(std::shared_ptr<const TH2F> input) const
+{
+	TH2F tmp_inact_pix_around(*input);
+	tmp_inact_pix_around.SetName("tmp_inact_pix_around");
+	tmp_inact_pix_around.Reset();
+			
+	for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+	{
+		for(int row_ID=1; row_ID<=input->GetNbinsX(); row_ID++)
+		{					
+			int inactive_pixels_around=0;
+			auto legit_pixels=getLegitPixels(input, col_ID, row_ID);
+			
+			for(auto legpix : legit_pixels)
+			{
+				if(input->GetBinContent(legpix.first, legpix.second)<1) ++inactive_pixels_around;
+			}
+
+			tmp_inact_pix_around.SetBinContent(row_ID,col_ID, inactive_pixels_around+0.01);
+		}
+	}
+	
+	return tmp_inact_pix_around;
+}
+
+
+double AFP_NoisyPixelTool::getNeighbours(std::shared_ptr<const TH2F> input, int row_ID, int col_ID) const
+{
+	if( row_ID!=input->GetNbinsX() && row_ID!=1 && col_ID!=1 && col_ID!=input->GetNbinsY()) return 8.;
+	else if( (row_ID==input->GetNbinsX() || row_ID==1) && (col_ID==1 || col_ID==input->GetNbinsY())) return 3.;
+	else return 5;
+}
+
+
+std::tuple<TH2F,TH2F,TH2F,TH2F> AFP_NoisyPixelTool::findLEffAndNoisyPixels(std::shared_ptr<const TH2F> input, const TH2F& tmp_inact_pix_around, const std::string& method) const
+{
+	TH2F tmp_found_leff(*input);
+	tmp_found_leff.SetName("tmp_found_leff");
+	tmp_found_leff.Reset();
+	
+	TH2F tmp_found_noisy(*input);
+	tmp_found_noisy.SetName("tmp_found_noisy");
+	tmp_found_noisy.Reset();
+	
+	TH2F tmp_eff_leff(*input);
+	tmp_eff_leff.SetName("tmp_eff_leff");
+	tmp_eff_leff.Reset();
+	
+	TH2F tmp_eff_noisy(*input);
+	tmp_eff_noisy.SetName("tmp_eff_noisy");
+	tmp_eff_noisy.Reset();
+	
+	for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+	{
+		for(int row_ID=1; row_ID<=input->GetNbinsX(); row_ID++)
+		{
+			if(input->GetBinContent(row_ID, col_ID)!=0 && tmp_inact_pix_around.GetBinContent(row_ID, col_ID)<0.1)
+			{	
+				double npixels=getNeighbours(input, row_ID, col_ID);
+				
+				double sum=0.;
+				auto legit_pixels=getLegitPixels(input, col_ID, row_ID, method);
+				for(auto legpix : legit_pixels)
+				{
+					sum+=input->GetBinContent(legpix.first,legpix.second);
+				}
+				
+				double av_noisy=0;
+				double av_leff=0;
+				
+				// TODO: something about edges?
+				if(method=="2_ROW" || method=="2_COL")
+				{
+					av_noisy=sum/(std::abs(npixels-4)*0.5);
+					av_leff=sum/2.;
+				}
+				else if(method=="4_PIX") 
+				{
+					av_noisy=sum/(std::ceil(npixels*0.5));
+					av_leff=sum/4.;
+				}
+				else if(method=="8_PIX")
+				{
+					av_noisy=sum/(npixels);
+					av_leff=sum/8.;
+				}
+				
+				
+				double ratio_leff  = input->GetBinContent(row_ID,col_ID)/av_leff;
+				double ratio_noisy = input->GetBinContent(row_ID,col_ID)/av_noisy;
+				tmp_eff_leff.SetBinContent(row_ID,col_ID,100.*ratio_leff);
+				tmp_eff_noisy.SetBinContent(row_ID,col_ID,100.*ratio_noisy);
+				
+				if(ratio_leff  < m_threshLEff) tmp_found_leff.SetBinContent(row_ID,col_ID,1.); 
+				if(ratio_noisy > m_threshNoisy) tmp_found_noisy.SetBinContent(row_ID,col_ID,1.);	
+			}
+		}
+	}
+	
+	return {tmp_found_leff,tmp_found_noisy,tmp_eff_leff,tmp_eff_noisy};
+}
+
+
+void AFP_NoisyPixelTool::filterLEffPixelsAroundNoisy(std::shared_ptr<const TH2F> input, const TH2F& tmp_inact_pix_around, TH2F& tmp_found_leff, TH2F& tmp_found_noisy, TH2F& tmp_eff_leff, TH2F& tmp_eff_noisy, const std::string& method) const
+{	
+	for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+	{
+		for(int row_ID=1; row_ID<=input->GetNbinsX(); row_ID++)
+		{	
+  			if(input->GetBinContent(row_ID,col_ID)==0) continue;
+  					
+  			int inactive_pixels_around=tmp_inact_pix_around.GetBinContent(row_ID, col_ID);
+  			int leff_pixels_around=0;
+			auto legit_pixels=getLegitPixels(input, col_ID,row_ID);
+			for(auto legpix : legit_pixels)
+			{
+				if(tmp_found_leff.GetBinContent(legpix.first,legpix.second)>0) leff_pixels_around++;
+			}
+  			
+			if(leff_pixels_around!=0  && inactive_pixels_around==0)
+			{
+				//if low eff. pixels around > 0, recalculate avarage
+				double re_av=0.;
+				int npix=0;
+				
+				auto legit_pixels=getLegitPixels(input, col_ID,row_ID, method);
+				for(auto legpix : legit_pixels)
+				{
+					if(tmp_found_leff.GetBinContent(legpix.first,legpix.second)==0) 
+					{
+						re_av+=input->GetBinContent(legpix.first,legpix.second);
+						npix++;
+					}
+				}
+			
+				if(npix==0)
+				{
+					re_av=0;
+					for(auto legpix : legit_pixels)
+					{
+						re_av+=input->GetBinContent(legpix.first,legpix.second);
+					}
+					
+					if(re_av/input->GetBinContent(row_ID,col_ID)<1.5 && re_av/input->GetBinContent(row_ID,col_ID)>0.5) re_av=1;
+				}
+				else
+				{
+					re_av/=npix;
+				}
+						
+				double ratio_noisy = m_sensitivity+input->GetBinContent(row_ID,col_ID)/re_av;
+				double ratio_leff  = input->GetBinContent(row_ID,col_ID)/re_av; 
+				if(re_av==1)
+				{
+					tmp_eff_leff.SetBinContent(row_ID,col_ID,1);
+					tmp_eff_noisy.SetBinContent(row_ID,col_ID,1);
+				}
+						
+				if(row_ID!=input->GetNbinsX() && row_ID!=1 && col_ID!=1 && col_ID!=input->GetNbinsY())
+				{
+					if( ((ratio_noisy>m_threshNoisy && re_av!=1) || re_av==1) )
+					{
+						tmp_found_noisy.SetBinContent(row_ID,col_ID,1);
+						if(re_av!=1) tmp_eff_noisy.SetBinContent(row_ID,col_ID,100.*ratio_noisy);		
+					}
+					if(	((ratio_leff<m_threshLEff && re_av!=1) || re_av==1) )
+					{
+						tmp_found_leff.SetBinContent(row_ID,col_ID,1);  	
+						if(re_av!=1) tmp_eff_leff.SetBinContent(row_ID,col_ID,100.*ratio_leff);
+					}
+				}
+			}
+		}
+	}
+	
+	return;
+}
+			
+
+std::vector<std::vector<double>> AFP_NoisyPixelTool::makeFits(std::shared_ptr<const TH2F> input) const
+{
+	const int nParams=7;
+	std::vector<std::vector<double>> params(nParams, std::vector<double>(input->GetNbinsX()+1, 0.));
+	
+	for(int row_ID=0; row_ID<=input->GetNbinsX(); row_ID++)
+	{
+		std::string srow_ID = std::to_string(row_ID);
+		std::unique_ptr<TH1F> hist(new TH1F(("ROW"+srow_ID).c_str(), ("ROW"+srow_ID).c_str(), input->GetNbinsY(),  0.5,  input->GetNbinsX()+0.5));
+		
+		for(int col_ID=1; col_ID<=input->GetNbinsY(); col_ID++)
+		{  
+			hist->Fill(col_ID, 1.0*input->GetBinContent(row_ID,col_ID)); 
+		}
+		
+		//Set double gaussian fit with starting parameters
+		std::shared_ptr<TF1> FIT_2(new TF1("gaus","gaus(0)+gaus(3)+[6]",0,input->GetNbinsY(),nParams));
+		if(row_ID==1)
+		{
+			FIT_2->SetParameters(25000,hist->GetMean(),hist->GetStdDev()/std::sqrt(2),
+			                      5000,hist->GetMean(),hist->GetStdDev()/std::sqrt(2),
+			                     std::max(hist->GetBinContent(2),std::max(hist->GetBinContent(3),hist->GetBinContent(4))) );
+		}
+		else
+		{
+			for(int par=0;par<nParams;++par)
+			{
+				FIT_2->SetParameter(par,params.at(par).at(row_ID-1));
+			}
+		}
+		
+		//fit
+		hist->Fit(FIT_2.get(),"Q");
+		
+		for(int par=0;par<nParams;++par)
+		{
+			params.at(par).at(row_ID-1)=FIT_2->GetParameter(par);
+		}
+	}
+	
+	return params;
+}
+
+
diff --git a/ForwardDetectors/AFP/AFP_Calibration/src/AFP_PixelHistoFiller.cxx b/ForwardDetectors/AFP/AFP_Calibration/src/AFP_PixelHistoFiller.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0c5c78e2a2728327c27ad0365fa50ba5e25a9655
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/src/AFP_PixelHistoFiller.cxx
@@ -0,0 +1,125 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "AFP_Calibration/AFP_PixelHistoFiller.h"
+
+
+AFP_PixelHistoFiller::AFP_PixelHistoFiller(const std::string& name, ISvcLocator* pSvcLocator) :
+  AthAlgorithm(name, pSvcLocator)
+{
+}
+
+
+StatusCode AFP_PixelHistoFiller::initialize()
+{
+	ATH_CHECK( m_eventInfoKey.initialize() );
+	ATH_CHECK( m_afpHitContainerKey.initialize() );
+	
+	m_nPixelsX=AFP_CONSTANTS::SiT_Pixel_amount_x;
+	m_nPixelsY=AFP_CONSTANTS::SiT_Pixel_amount_y;
+	
+	for(int st=0;st<m_nStations;++st)
+	{
+		for(int la=0;la<m_nLayers;++la)
+		{
+			m_pixelHits[st][la].clear();
+		}
+	}
+	
+	return StatusCode::SUCCESS;
+}
+
+
+StatusCode AFP_PixelHistoFiller::execute()
+{
+	const EventContext& ctx = Gaudi::Hive::currentContext();
+	
+	// get event info
+	SG::ReadHandle<xAOD::EventInfo> eventInfo (m_eventInfoKey, ctx);
+	if (!eventInfo.isValid())
+	{
+		ATH_MSG_WARNING("cannot get eventInfo");
+		return StatusCode::SUCCESS;
+	}
+	else ATH_MSG_DEBUG("successfully got eventInfo");
+	
+	// make sure all the histograms are defined
+	int current_lb=eventInfo->lumiBlock();
+		
+	if(static_cast<int>(m_pixelHits[0][0].size())-1<current_lb/m_LBRangeLength)
+	{
+		for(int st=0;st<m_nStations;++st)
+		{
+			for(int la=0;la<m_nLayers;++la)
+			{
+				for(int add=static_cast<int>(m_pixelHits[st][la].size());add<=current_lb/m_LBRangeLength;++add)
+				{
+					TH2F hist(Form("pixel_hits_lb_%d_%d_station_%d_layer_%d", add*m_LBRangeLength,(add+1)*m_LBRangeLength-1,st,la), 
+					          Form("pixel hits, lb %d-%d, station %d, layer %d", add*m_LBRangeLength,(add+1)*m_LBRangeLength-1,st,la),
+					          m_nPixelsX,0.5,m_nPixelsX+0.5, m_nPixelsY,0.5,m_nPixelsY+1);
+					
+					m_pixelHits[st][la].push_back(hist);
+				}
+			}
+		}
+	}
+	
+	int lb_index=current_lb/m_LBRangeLength;
+	
+	SG::ReadHandle<xAOD::AFPSiHitContainer> afpHitContainer (m_afpHitContainerKey, ctx);
+	if (!afpHitContainer.isValid())
+	{
+		ATH_MSG_WARNING("cannot get AFPSiHitContainer");
+		return StatusCode::SUCCESS;
+	}
+	else ATH_MSG_DEBUG("successfully got AFPSiHitContainer");
+	
+  	for (const xAOD::AFPSiHit* hit : *afpHitContainer)
+  	{
+		int st=hit->stationID();
+		int la=hit->pixelLayerID();
+		
+		if(st<0 || m_nStations<=st)
+		{
+			ATH_MSG_INFO("stationID = " <<st<<", but expected values are from 0 to "<<m_nStations-1 );
+			continue;
+		}
+		if(la<0 || m_nLayers<=la)
+		{
+			ATH_MSG_INFO("pixelLayerID = " <<la<<", but expected values are from 0 to "<<m_nLayers-1 );
+			continue;
+		}
+		
+		m_pixelHits[st][la].at(lb_index).Fill(hit->pixelRowIDChip(),hit->pixelColIDChip());
+	}
+	
+	return StatusCode::SUCCESS;
+}
+
+
+StatusCode AFP_PixelHistoFiller::finalize()
+{	
+	std::unique_ptr<TFile> output_file(new TFile("AFP_PixelHistoFiller.root","recreate"));
+	
+	TH1I lb("LBRangeLength","LBRangeLength",2,0,2);
+	lb.Fill(0.5);
+	lb.Fill(1.5,m_LBRangeLength);
+	lb.Write();
+	
+	for(int st=0;st<m_nStations;++st)
+	{
+		for(int la=0;la<m_nLayers;++la)
+		{
+			for(TH2F& hist: m_pixelHits[st][la])
+			{
+				hist.Write();
+			}
+		}
+	}
+	
+	output_file->Close();
+	
+	return StatusCode::SUCCESS;
+}
+
diff --git a/ForwardDetectors/AFP/AFP_Calibration/src/AFP_PixelIdentifier.cxx b/ForwardDetectors/AFP/AFP_Calibration/src/AFP_PixelIdentifier.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..990f142a6e3b84c590c99e5e3755eb867fe8d8ae
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/src/AFP_PixelIdentifier.cxx
@@ -0,0 +1,78 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "AFP_Calibration/AFP_PixelIdentifier.h"
+
+
+AFP_PixelIdentifier::AFP_PixelIdentifier(const std::string input_name, const std::string output_name, std::vector<std::string> pixelTools) :
+	m_pixelTools_names(pixelTools)
+{
+	m_input_file=std::make_unique<TFile>(input_name.c_str(),"read");
+	m_output_file=std::make_unique<TFile>(output_name.c_str(),"recreate");
+	
+	for(auto pixel_name: m_pixelTools_names)
+	{
+		if(pixel_name=="AFP_DeadPixel")
+		{
+			m_pixelTools.push_back(std::make_unique<AFP_DeadPixelTool>());
+		}
+		else if(pixel_name=="AFP_NoisyPixel")
+		{
+			m_pixelTools.push_back(std::make_unique<AFP_NoisyPixelTool>());
+		}
+	}
+	
+	AFP_CONSTANTS afp_consts;
+	m_nPixelsX=afp_consts.SiT_Pixel_amount_x;
+	m_nPixelsY=afp_consts.SiT_Pixel_amount_y;
+}
+
+
+int AFP_PixelIdentifier::execute()
+{
+	if(m_pixelTools.empty())
+	{
+		return 0;
+	}
+	
+	std::unique_ptr<TH1I> hist_lb(static_cast<TH1I*>(m_input_file->Get("LBRangeLength")));
+	int LBRangeLength=hist_lb->GetBinContent(2)/hist_lb->GetBinContent(1);
+	
+	for(int st=0;st<m_nStations;++st)
+	{
+		for(int la=0;la<m_nLayers;++la)
+		{
+			int lbIdx=0;
+			while(true) // repeat until there are valid histograms
+			{
+				std::shared_ptr<const TH2F> pixelHits(dynamic_cast<TH2F*>(m_input_file->Get(
+				    Form("pixel_hits_lb_%d_%d_station_%d_layer_%d", lbIdx*LBRangeLength, (lbIdx+1)*LBRangeLength-1, st,la)
+				)));
+				
+				if(!pixelHits.get()) break;
+				
+				for(auto &pixelTool : m_pixelTools)
+				{
+					std::vector<TH2F> output{TH2F(Form("lb_%d_%d_station_%d_layer_%d",lbIdx*LBRangeLength,(lbIdx+1)*LBRangeLength-1,st,la),
+					                              Form("lb %d-%d, station %d, layer %d", lbIdx*LBRangeLength,(lbIdx+1)*LBRangeLength-1,st,la),
+					                              m_nPixelsX,0.5,m_nPixelsX+0.5, m_nPixelsY,0.5,m_nPixelsY+1)};
+					
+					pixelTool->Identify(pixelHits,output);
+					
+					for(TH2F& out : output)
+					{
+						out.Write();
+					}
+				}
+				
+				++lbIdx;
+			}
+		}
+	}
+	
+	m_output_file->Close();
+	return 0;
+}
+
+
diff --git a/ForwardDetectors/AFP/AFP_Calibration/src/components/AFP_Calibration_entries.cxx b/ForwardDetectors/AFP/AFP_Calibration/src/components/AFP_Calibration_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a6caa786e1bc405db8737de1f9197a3292f5e38d
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/src/components/AFP_Calibration_entries.cxx
@@ -0,0 +1,6 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "AFP_Calibration/AFP_PixelHistoFiller.h"
+DECLARE_COMPONENT( AFP_PixelHistoFiller )
diff --git a/ForwardDetectors/AFP/AFP_Calibration/test/test_AFP_Calibration.sh b/ForwardDetectors/AFP/AFP_Calibration/test/test_AFP_Calibration.sh
new file mode 100644
index 0000000000000000000000000000000000000000..12e91959b4daee272941c463b933cb14155a4380
--- /dev/null
+++ b/ForwardDetectors/AFP/AFP_Calibration/test/test_AFP_Calibration.sh
@@ -0,0 +1,45 @@
+# Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+
+# test the whole AFP calibration loop
+
+# prerequisite - have AODs from AFP_Calibration stream
+# cd /afs/cern.ch/work/p/pbalek/workspace/public/data17_13TeV.00338480.calibration_AFP.AOD
+# asetup Athena,master,latest,here
+# for inputfile in `ls -d -1 /afs/cern.ch/work/p/pbalek/public/data17_13TeV.00338480.calibration_AFP.daq.RAW/*`
+# 	do python ~/AFP/reco_master_Athena/athena/ForwardDetectors/ForwardRec/python/AFPRecConfig.py --filesInput=${inputfile}
+# 	mv AOD.pool.root `echo $inputfile | sed -e 's/daq.RAW/AOD/g' -e 's/data$/root/'`
+# done
+
+# move to temp folder
+cd /tmp/${USER}
+setupATLAS
+asetup Athena,master,latest,here
+
+# setup this branch, e.g.
+cd /afs/cern.ch/work/p/pbalek/public/afp_calibration_athena/build
+# cmake ../athena/Projects/WorkDir; make 
+source x86_64-centos7-gcc11-opt/setup.sh
+cd /tmp/${USER}
+
+
+# step 1 - produce histograms
+mkdir input1; cd input1
+athena AFP_Calibration/AFP_PixelHistoFiller.py --filesInput="/afs/cern.ch/user/p/pbalek/workspace/public/data17_13TeV.00338480.calibration_AFP.AOD/data17_13TeV.00338480.calibration_AFP.AOD._lb0000._SFO-1._0007.root"
+# now we have output1/AFP_PixelHistoFiller.root
+cd ..
+
+mkdir input2; cd input2
+athena AFP_Calibration/AFP_PixelHistoFiller.py --filesInput="/afs/cern.ch/user/p/pbalek/workspace/public/data17_13TeV.00338480.calibration_AFP.AOD/data17_13TeV.00338480.calibration_AFP.AOD._lb0000._SFO-2._0002.root,/afs/cern.ch/user/p/pbalek/workspace/public/data17_13TeV.00338480.calibration_AFP.AOD/data17_13TeV.00338480.calibration_AFP.AOD._lb0000._SFO-2._0005.root"
+# now we have output2/AFP_PixelHistoFiller.root
+cd ..
+
+mkdir input3; cd input3
+athena AFP_Calibration/AFP_PixelHistoFiller.py --filesInput="/afs/cern.ch/user/p/pbalek/workspace/public/data17_13TeV.00338480.calibration_AFP.AOD/data17_13TeV.00338480.calibration_AFP.AOD._lb0000._SFO-3._0003.root"
+# now we have output3/AFP_PixelHistoFiller.root
+cd ..
+
+
+# step 2 - merge and identify dead/noisy pixels
+hadd -f AFP_PixelHistoFiller.root input*/AFP_PixelHistoFiller.root
+run_AFP_PixelIdentifier
+# now we have AFP_PixelIdentifier.root - that is our output
diff --git a/ForwardDetectors/ForwardRec/python/AFPRecConfig.py b/ForwardDetectors/ForwardRec/python/AFPRecConfig.py
index 4475f5a5bcedab3259349acabee8fc50fdfefc52..168ccb41da17be09131cf59cfdda2247ee7183f3 100644
--- a/ForwardDetectors/ForwardRec/python/AFPRecConfig.py
+++ b/ForwardDetectors/ForwardRec/python/AFPRecConfig.py
@@ -104,8 +104,10 @@ if __name__ == "__main__":
     
     ConfigFlags.Input.Files = ["/afs/cern.ch/work/p/pbalek/public/data17_13TeV.00338480.physics_Main.daq.RAW/data17_13TeV.00338480.physics_Main.daq.RAW._lb0275._SFO-7._0007.data"]
     
-    ConfigFlags.Exec.MaxEvents=500
-    ConfigFlags.Concurrency.NumThreads=4
+    ConfigFlags.Output.doWriteAOD = True
+    ConfigFlags.Output.AODFileName = "AOD.pool.root"
+    ConfigFlags.Exec.MaxEvents = 500
+    ConfigFlags.Concurrency.NumThreads = 4
  
     ConfigFlags.fillFromArgs() # enable unit tests to switch only parts of reco: python -m HIRecConfig.HIRecConfig HeavyIon.doGlobal = 0 and so on
     ConfigFlags.lock()
@@ -113,8 +115,6 @@ if __name__ == "__main__":
     
     from AthenaConfiguration.MainServicesConfig import MainServicesCfg
     acc = MainServicesCfg(ConfigFlags)
-    acc.getEventAlgo("SGInputLoader").FailIfNoProxy = True # enforce no missing data
-    
     
     from ByteStreamCnvSvc.ByteStreamConfig import ByteStreamReadCfg
     acc.merge(ByteStreamReadCfg(ConfigFlags))
@@ -131,4 +131,3 @@ if __name__ == "__main__":
         import sys
         sys.exit(-1)
 
-