diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2fdf6de413be5d0aed6eb9955d2b843b83f53d40..37e30d5ddb63cfc36ad8953621679e903d258aad 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -108,6 +108,7 @@ include:
   - local: 'benchmarks/dis/config.yml'
     #- local: 'benchmarks/dvmp/config.yml'
   - local: 'benchmarks/dvcs/config.yml'
+  - local: 'benchmarks/diffractive_vmp/config.yml'
   - local: 'benchmarks/tcs/config.yml'
   - local: 'benchmarks/u_omega/config.yml'
   - local: 'benchmarks/single/config.yml'
@@ -118,6 +119,7 @@ summary:
   needs:
     - "dis:results"
     - "dvcs:results"
+    - "diffractive_vmp:results"
     - "tcs:results"
     - "u_omega:results"
     - "single:results"
diff --git a/benchmarks/diffractive_vmp/analysis/simple_analysis.cxx b/benchmarks/diffractive_vmp/analysis/simple_analysis.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c54d8b37dc139ab96f82dd1f4cc49af7a3e0969d
--- /dev/null
+++ b/benchmarks/diffractive_vmp/analysis/simple_analysis.cxx
@@ -0,0 +1,391 @@
+#include <cmath>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include <algorithm>
+#include <utility>
+
+#include <TH1D.h>
+#include <TH2D.h>
+#include <TFitResult.h>
+#include <TRandom3.h>
+#include <TCanvas.h>
+#include <TTreeReader.h>
+#include <TTreeReaderArray.h>
+#include <TChain.h>
+#include <TFile.h>
+#include <TLorentzVector.h>
+
+#include <TLorentzRotation.h>
+#include <TVector2.h>
+#include <TVector3.h>
+
+#define PI            3.1415926
+#define MASS_ELECTRON 0.00051
+#define MASS_PROTON   0.93827
+#define MASS_PION     0.13957
+#define MASS_KAON     0.493667
+#define MASS_AU197    183.45406466643374
+
+
+auto giveme_t_method_L(TLorentzVector eIn, 
+					   TLorentzVector eOut, 
+					   TLorentzVector pIn, 
+					   TLorentzVector vmOut)
+{
+	TLorentzVector aInVec(pIn.Px()*197,pIn.Py()*197,pIn.Pz()*197,sqrt(pIn.Px()*197*pIn.Px()*197 + pIn.Py()*197*pIn.Py()*197 + pIn.Pz()*197*pIn.Pz()*197 + MASS_AU197*MASS_AU197) );
+	double method_L = 0;
+	TLorentzVector a_beam_scattered = aInVec-(vmOut+eOut-eIn);
+	double p_Aplus = a_beam_scattered.E()+a_beam_scattered.Pz();
+	double p_TAsquared = TMath::Power(a_beam_scattered.Pt(),2);
+	double p_Aminus = (MASS_AU197*MASS_AU197 + p_TAsquared) / p_Aplus;
+	TLorentzVector a_beam_scattered_corr; 
+	a_beam_scattered_corr.SetPxPyPzE(a_beam_scattered.Px(),a_beam_scattered.Py(),(p_Aplus-p_Aminus)/2., (p_Aplus+p_Aminus)/2. );
+	method_L = -(a_beam_scattered_corr-aInVec).Mag2();
+
+	return method_L;
+}
+
+
+int diffractive_vm_simple_analysis(TString rec_file, TString outputfile)
+{	
+// read our configuration	
+TString name_of_input = (TString) rec_file;
+std::cout << "Input file = " << name_of_input << endl;
+auto tree = new TChain("events");
+tree->Add(name_of_input);
+TTreeReader tree_reader(tree);       // !the tree reader
+
+TTreeReaderArray<int> mc_genStatus_array = {tree_reader, "MCParticles.generatorStatus"};
+// MC particle pz array for each MC particle
+TTreeReaderArray<float> mc_px_array = {tree_reader, "MCParticles.momentum.x"};
+TTreeReaderArray<float> mc_py_array = {tree_reader, "MCParticles.momentum.y"};
+TTreeReaderArray<float> mc_pz_array = {tree_reader, "MCParticles.momentum.z"};
+TTreeReaderArray<double> mc_mass_array = {tree_reader, "MCParticles.mass"};
+TTreeReaderArray<int> mc_pdg_array = {tree_reader, "MCParticles.PDG"};
+
+//Reconstructed EcalEndcapNClusters
+TTreeReaderArray<float> em_energy_array = {tree_reader, "EcalEndcapNClusters.energy"};
+TTreeReaderArray<float> em_x_array = {tree_reader, "EcalEndcapNClusters.position.x"};
+TTreeReaderArray<float> em_y_array = {tree_reader, "EcalEndcapNClusters.position.y"};
+TTreeReaderArray<float> emhits_x_array = {tree_reader, "EcalEndcapNRecHits.position.x"};
+TTreeReaderArray<float> emhits_y_array = {tree_reader, "EcalEndcapNRecHits.position.y"};
+TTreeReaderArray<float> emhits_energy_array = {tree_reader, "EcalEndcapNRecHits.energy"};
+
+TTreeReaderArray<unsigned int> em_rec_id_array = {tree_reader, "EcalEndcapNClustersAssociations.recID"};
+TTreeReaderArray<unsigned int> em_sim_id_array = {tree_reader, "EcalEndcapNClustersAssociations.simID"};
+
+// Reconstructed particles pz array for each reconstructed particle
+TTreeReaderArray<float> reco_px_array = {tree_reader, "ReconstructedChargedParticles.momentum.x"};
+TTreeReaderArray<float> reco_py_array = {tree_reader, "ReconstructedChargedParticles.momentum.y"};
+TTreeReaderArray<float> reco_pz_array = {tree_reader, "ReconstructedChargedParticles.momentum.z"};
+TTreeReaderArray<float> reco_charge_array = {tree_reader, "ReconstructedChargedParticles.charge"};
+
+TTreeReaderArray<unsigned int> rec_id = {tree_reader, "ReconstructedChargedParticlesAssociations.recID"};
+TTreeReaderArray<unsigned int> sim_id = {tree_reader, "ReconstructedChargedParticlesAssociations.simID"};
+
+TString output_name_dir = outputfile+"_output.root";
+cout << "Output file = " << output_name_dir << endl;
+TFile* output = new TFile(output_name_dir,"RECREATE");
+
+//events
+TH1D* h_Q2_e = new TH1D("h_Q2_e",";Q^{2}_{e,MC}",100,0,20);
+TH1D* h_y_e = new TH1D("h_y_e",";y_{e,MC}",100,0,1);
+TH1D* h_energy_MC = new TH1D("h_energy_MC",";E_{MC} (GeV)",100,0,20);
+TH1D* h_t_MC = new TH1D("h_t_MC",";t_{MC}; counts",100,0,0.2);
+
+TH1D* h_Q2REC_e = new TH1D("h_Q2REC_e",";Q^{2}_{e,REC}",100,0,20);
+TH1D* h_yREC_e = new TH1D("h_yREC_e",";y_{e,REC}",100,0,1);
+TH1D* h_energy_REC = new TH1D("h_energy_REC",";E_{REC} (GeV)",100,0,20);
+TH1D* h_trk_energy_REC = new TH1D("h_trk_energy_REC",";E_{REC} (GeV)",100,0,20);
+TH1D* h_trk_Epz_REC = new TH1D("h_trk_Epz_REC",";E - p_{z} (GeV)",200,0,50);
+
+//track
+TH1D* h_eta = new TH1D("h_eta",";#eta",100,-5,5);
+TH2D* h_trk_energy_res = new TH2D("h_trk_energy_res",";E_{MC} (GeV); E_{MC}-E_{REC}/E_{MC} track-base ",100,0,20,1000,-1,1);
+TH2D* h_trk_Pt_res = new TH2D("h_trk_Pt_res",";p_{T,MC} (GeV); P_{T,MC}-P_{T,REC}/P_{T,MC} track-base ",100,0,15,1000,-1,1);
+TH1D* h_Epz_REC = new TH1D("h_Epz_REC",";E - p_{z} (GeV)",200,0,50);
+
+//VM & t
+TH1D* h_VM_mass_REC = new TH1D("h_VM_mass_REC",";mass (GeV)",200,0,4);
+TH1D* h_VM_pt_REC = new TH1D("h_VM_pt_REC",";p_{T} (GeV/c)",200,0,2);
+TH2D* h_VM_res = new TH2D("h_VM_res",";p_{T,MC} (GeV); p_{T,MC}-E_{T,REC}/p_{T,MC}",100,0,2,1000,-1,1);
+TH1D* h_t_REC = new TH1D("h_t_REC",";t_{REC} (GeV^{2}); counts",100,0,0.2);
+TH1D* h_t_trk_REC = new TH1D("h_t_trk_REC",";t_{REC}(GeV^{2}) track-base; counts",100,0,0.2);
+TH1D* h_t_combo_REC = new TH1D("h_t_combo_REC",";t_{combo,REC}(GeV^{2}); counts",100,0,0.2);
+TH2D* h_t_res = new TH2D("h_t_res",";t_{MC} (GeV^{2}); t_{MC}-t_{REC}/t_{MC}",100,0,0.2,1000,-10,10);
+TH2D* h_trk_t_res = new TH2D("h_trk_t_res",";t_{MC} (GeV^{2}); t_{MC}-t_{REC}/t_{MC} track-base",100,0,0.2,1000,-10,10);
+TH2D* h_t_2D = new TH2D("h_t_2D",";t_{MC} (GeV^{2}); t_{REC} (GeV^{2}) track-base",100,0,0.2,100,0,0.2);
+TH2D* h_t_REC_2D = new TH2D("h_t_REC_2D",";t_{trk,REC} (GeV^{2}); t_{EEMC,REC} (GeV^{2})",100,0,0.2,100,0,0.2);
+TH2D* h_t_RECMC_2D = new TH2D("h_t_RECMC_2D",";t_{MC} (GeV^{2}); t_{trk,REC} / t_{EEMC,REC} ",100,0,0.2,200,-10,10);
+
+//energy clus
+TH2D* h_emClus_position_REC = new TH2D("h_emClus_position_REC",";x (mm);y (mm)",80,-800,800,80,-800,800);
+TH2D* h_emHits_position_REC = new TH2D("h_emHits_position_REC",";x (mm);y (mm)",80,-800,800,80,-800,800);
+TH2D* h_energy_res = new TH2D("h_energy_res",";E_{MC} (GeV); E_{MC}-E_{REC}/E_{MC} emcal",100,0,20,1000,-1,1);
+TH1D* h_energy_calibration_REC = new TH1D("h_energy_calibration_REC",";E (GeV)",200,0,2);
+TH1D* h_EoverP_REC = new TH1D("h_EoverP_REC",";E/p",200,0,2);
+TH1D* h_ClusOverHit_REC = new TH1D("h_ClusOverHit_REC",";cluster energy / new cluster energy",200,0,2);
+
+tree_reader.SetEntriesRange(0, tree->GetEntries());
+while (tree_reader.Next()) {
+
+	/*
+	Beam particles
+	*/
+	TLorentzVector ebeam(0,0,0,0);
+	TLorentzVector pbeam(0,0,0,0);
+
+	TLorentzVector vmMC(0,0,0,0);
+	TLorentzVector kplusMC(0,0,0,0);
+	TLorentzVector kminusMC(0,0,0,0);
+
+	//MC level
+	TLorentzVector scatMC(0,0,0,0);
+	int mc_elect_index=-1;
+	double maxPt=-99.;
+	for(int imc=0;imc<mc_px_array.GetSize();imc++){
+		TVector3 mctrk(mc_px_array[imc],mc_py_array[imc],mc_pz_array[imc]);	
+		if(mc_genStatus_array[imc]==4){//4 is Sartre.
+			if(mc_pdg_array[imc]==11) ebeam.SetVectM(mctrk, MASS_ELECTRON);
+				if(mc_pdg_array[imc]==2212) pbeam.SetVectM(mctrk, MASS_PROTON);
+		}
+		if(mc_genStatus_array[imc]!=1) continue;
+		if(mc_pdg_array[imc]==11 	
+			&& mctrk.Perp()>maxPt){
+			maxPt=mctrk.Perp();
+			mc_elect_index=imc;
+			scatMC.SetVectM(mctrk,mc_mass_array[imc]);
+		}
+		if(mc_pdg_array[imc]==321
+			&& mc_genStatus_array[imc]==1) kplusMC.SetVectM(mctrk,MASS_KAON);
+		if(mc_pdg_array[imc]==-321
+			&& mc_genStatus_array[imc]==1) kminusMC.SetVectM(mctrk,MASS_KAON);
+
+	}
+	vmMC=kplusMC+kminusMC;
+	//protection.
+	if(ebeam.E()==pbeam.E() && ebeam.E()==0) {
+		std::cout << "problem with MC incoming beams" << std::endl;
+		continue;
+	}
+	TLorentzVector qbeam=ebeam-scatMC;
+	double Q2=-(qbeam).Mag2();  
+	double pq=pbeam.Dot(qbeam);
+	double y= pq/pbeam.Dot(ebeam);
+	
+	//MC level phase space cut
+	if(Q2<1.||Q2>10.) continue;
+	if(y<0.01||y>0.85) continue;
+
+	h_Q2_e->Fill(Q2);
+	h_y_e->Fill(y);
+	h_energy_MC->Fill(scatMC.E());
+
+	double t_MC=0.;
+	if(vmMC.E()!=0 
+		&& fabs(vmMC.Rapidity())<3.5)
+	{
+		double method_E = -(qbeam-vmMC).Mag2();
+		t_MC=method_E;
+		h_t_MC->Fill( method_E );
+	}
+
+	//rec level
+	//leading cluster
+	double maxEnergy=-99.;
+	double xpos=-999.;
+	double ypos=-999.;
+	for(int iclus=0;iclus<em_energy_array.GetSize();iclus++){
+		if(em_energy_array[iclus]>maxEnergy){
+			maxEnergy=em_energy_array[iclus];
+			xpos=em_x_array[iclus];
+			ypos=em_y_array[iclus];
+		}
+	}
+	//leading hit energy
+	double maxHitEnergy=0.01;//threshold 10 MeV
+	double xhitpos=-999.;
+	double yhitpos=-999.;
+	int hit_index=-1;
+	for(int ihit=0;ihit<emhits_energy_array.GetSize();ihit++){	
+		if(emhits_energy_array[ihit]>maxHitEnergy){
+			maxHitEnergy=emhits_energy_array[ihit];
+			        xhitpos=emhits_x_array[ihit];
+			        yhitpos=emhits_y_array[ihit];
+			        hit_index=ihit;
+		}
+	}
+	//sum over all 3x3 towers around the leading tower
+	double xClus=xhitpos*maxHitEnergy;
+	double yClus=yhitpos*maxHitEnergy;
+	for(int ihit=0;ihit<emhits_energy_array.GetSize();ihit++){
+		double hitenergy=emhits_energy_array[ihit];
+		double x=emhits_x_array[ihit];
+		double y=emhits_y_array[ihit];
+		double d=sqrt( (x-xhitpos)*(x-xhitpos) + (y-yhitpos)*(y-yhitpos));
+		if(d<70. && ihit!=hit_index && hitenergy>0.01)  {
+			maxHitEnergy+=hitenergy;//clustering around leading tower 3 crystal = 60mm.
+			xClus+=x*hitenergy;
+			yClus+=y*hitenergy;
+		}
+	}
+	
+	h_ClusOverHit_REC->Fill( maxEnergy / maxHitEnergy );
+	//weighted average cluster position.
+	xClus = xClus/maxHitEnergy;
+	yClus = yClus/maxHitEnergy;
+	double radius=sqrt(xClus*xClus+yClus*yClus);
+	if( radius>550. ) continue; //geometric acceptance cut
+	//4.4% energy calibration.
+	double clusEnergy=1.044*maxHitEnergy; 
+
+	h_energy_REC->Fill(clusEnergy);
+	//ratio of reco / truth Energy
+	h_energy_calibration_REC->Fill( clusEnergy / scatMC.E() );
+	//energy resolution
+	double res= (scatMC.E()-clusEnergy)/scatMC.E();
+	h_energy_res->Fill(scatMC.E(), res);
+	h_emClus_position_REC->Fill(xpos,ypos);//default clustering position
+	h_emHits_position_REC->Fill(xClus,yClus); //self clustering position
+	
+	//association of rec level scat' e
+	int rec_elect_index=-1;
+	for(int i=0;i<sim_id.GetSize();i++){
+		if(sim_id[i]==mc_elect_index){
+			//find the rec_id
+			rec_elect_index = rec_id[i];
+		}
+	}
+    
+    TLorentzVector scatMCmatchREC(0,0,0,0);
+    TLorentzVector scatREC(0,0,0,0);
+    TLorentzVector scatClusEREC(0,0,0,0);
+    TLorentzVector hfs(0,0,0,0);
+    TLorentzVector particle(0,0,0,0);
+    TLorentzVector kplusREC(0,0,0,0);
+    TLorentzVector kminusREC(0,0,0,0);
+    TLorentzVector vmREC(0,0,0,0);
+
+    double maxP=-1.;
+    //track loop
+	for(int itrk=0;itrk<reco_pz_array.GetSize();itrk++){
+		TVector3 trk(reco_px_array[itrk],reco_py_array[itrk],reco_pz_array[itrk]);
+		if(rec_elect_index!=-1
+			&& itrk==rec_elect_index){
+			scatMCmatchREC.SetVectM(trk,MASS_ELECTRON);//Reserved to calculate t.
+		}
+		if(trk.Mag()>maxP){
+			//track-base 4 vector
+			maxP=trk.Mag();
+			scatREC.SetVectM(trk,MASS_ELECTRON);
+
+			//use emcal energy to define 4 vector
+			double p = sqrt(clusEnergy*clusEnergy- MASS_ELECTRON*MASS_ELECTRON );
+			double eta=scatREC.Eta();
+			double phi=scatREC.Phi();
+			double pt = TMath::Sin(scatREC.Theta())*p;
+			scatClusEREC.SetPtEtaPhiM(pt,eta,phi,MASS_ELECTRON);
+		}
+	}
+	//loop over track again;
+	for(int itrk=0;itrk<reco_pz_array.GetSize();itrk++){
+		TVector3 trk(reco_px_array[itrk],reco_py_array[itrk],reco_pz_array[itrk]);
+		particle.SetVectM(trk,MASS_PION);//assume pions;
+		if(itrk!=rec_elect_index) {
+    		hfs += particle; //hfs 4vector sum.
+    		//selecting phi->kk daughters;
+    		h_eta->Fill(trk.Eta());
+    		if(fabs(trk.Eta())<3.0){
+    			if(reco_charge_array[itrk]>0) kplusREC.SetVectM(trk,MASS_KAON);
+    			if(reco_charge_array[itrk]<0) kminusREC.SetVectM(trk,MASS_KAON);
+    		}
+		}
+	}
+	//4vector of VM;
+	if(kplusREC.E()!=0. && kminusREC.E()!=0.){
+		vmREC=kplusREC+kminusREC;
+	}
+
+	//track-base e' energy REC;
+	h_trk_energy_REC->Fill(scatMCmatchREC.E());
+	
+	//track-base e' energy resolution;
+	res= (scatMC.E()-scatMCmatchREC.E())/scatMC.E();
+	h_trk_energy_res->Fill(scatMC.E(), res);
+	
+	//track-base e' pt resolution;
+	res= (scatMC.Pt()-scatMCmatchREC.Pt())/scatMC.Pt();
+	h_trk_Pt_res->Fill(scatMC.Pt(), res);
+
+	//track-base Epz scat' e
+	double EpzREC= (scatMCmatchREC+hfs).E() - (scatMCmatchREC+hfs).Pz();
+	h_trk_Epz_REC->Fill( EpzREC );
+
+	//EEMC cluster Epz scat' e
+	EpzREC= (scatClusEREC+hfs).E() - (scatClusEREC+hfs).Pz();
+	h_Epz_REC->Fill( EpzREC );
+
+	//E over p
+	double EoverP=scatClusEREC.E() / scatMCmatchREC.P();
+	h_EoverP_REC->Fill( EoverP );
+
+	//cluster-base DIS kine;
+	TLorentzVector qbeamREC=ebeam-scatClusEREC;
+	double Q2REC=-(qbeamREC).Mag2();  
+	double pqREC=pbeam.Dot(qbeamREC);
+	double yREC= pqREC/pbeam.Dot(ebeam);
+	h_Q2REC_e->Fill(Q2REC);
+	h_yREC_e->Fill(yREC);
+
+	//Event selection:
+	if( EpzREC<27||EpzREC>40 ) continue;
+	if( EoverP<0.8||EoverP>1.18 ) continue;		
+
+	//MC level phase space cut
+	if(Q2REC<1.||Q2REC>10.) continue;
+	if(yREC<0.01||yREC>0.85) continue;
+
+	//VM rec
+	if(vmREC.E()==0) continue;
+	double phi_mass = vmREC.M();
+	h_VM_mass_REC->Fill(phi_mass);
+	h_VM_pt_REC->Fill(vmREC.Pt());
+
+	//select phi mass and rapidity window 
+	if( fabs(phi_mass-1.02)<0.02
+    		&& fabs(vmREC.Rapidity())<3.5 ){
+    	//2 versions: track and energy cluster:
+	double t_trk_REC = giveme_t_method_L(ebeam,scatMCmatchREC,pbeam,vmREC);
+    	double t_REC = giveme_t_method_L(ebeam,scatClusEREC,pbeam,vmREC);
+    	h_t_trk_REC->Fill( t_trk_REC );
+    	h_t_REC->Fill( t_REC );
+    	h_t_REC_2D->Fill(t_trk_REC,t_REC);
+    	if( (t_trk_REC/t_REC) > 0.5 && (t_trk_REC/t_REC) < 1.5 ){
+    		h_t_combo_REC->Fill( (t_trk_REC+t_REC)/2. );//w=1./(fabs(1.0-(t_trk_REC/t_REC)))
+    	} 
+    	h_t_RECMC_2D->Fill(t_MC,t_trk_REC/t_REC);
+
+    	//t track resolution 
+	res= (t_MC-t_trk_REC)/t_MC;
+	h_trk_t_res->Fill(t_MC, res);
+	
+    	//t EEMC resolution;
+	res= (t_MC-t_REC)/t_MC;
+	h_t_res->Fill(t_MC, res);
+	
+	//2D t
+	h_t_2D->Fill(t_MC,t_trk_REC);
+
+    	//VM pt resolution;
+    	res= (vmMC.Pt()-vmREC.Pt())/vmMC.Pt();
+	h_VM_res->Fill(vmMC.Pt(), res);
+    	}
+
+}
+output->Write();
+output->Close();
+
+return 0;
+}
diff --git a/benchmarks/diffractive_vmp/config.yml b/benchmarks/diffractive_vmp/config.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c05a9a80dc0ccef5ce8981aa4a57409fc034a766
--- /dev/null
+++ b/benchmarks/diffractive_vmp/config.yml
@@ -0,0 +1,37 @@
+diffractive_vmp:compile:
+  stage: compile
+  extends: .compile_benchmark
+  script:
+    - compile_analyses.py diffractive_vmp
+
+diffractive_vmp:generate:
+  stage: generate
+  extends: .phy_benchmark
+  needs: ["common:detector", "diffractive_vmp:compile"]
+  parallel:
+    matrix:
+      - VM: jpsi
+  timeout: 1 hours
+  script:
+    - bash benchmarks/diffractive_vmp/get.sh --config diffractive_${vm}
+
+diffractive_vmp:simulate:
+  stage: simulate
+  extends: .phy_benchmark
+  needs: ["diffractive_vmp:generate"]
+  parallel:
+    matrix:
+      - VM: jpsi
+  timeout: 96 hour
+  script:
+    - bash benchmarks/diffractive_vmp/diffractive_vmp.sh --config diffractive_${vm}
+  retry:
+    max: 2
+    when:
+      - runner_system_failure
+
+diffractive_vmp:results:
+  stage: collect
+  needs: ["diffractive_vmp:simulate"]
+  script:
+    - collect_tests.py diffractive_vmp
diff --git a/benchmarks/diffractive_vmp/diffractive_vmp.sh b/benchmarks/diffractive_vmp/diffractive_vmp.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8fce7dcab0b4333fe3fb239ef9059531fe76130b
--- /dev/null
+++ b/benchmarks/diffractive_vmp/diffractive_vmp.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+source strict-mode.sh
+
+## =============================================================================
+## Run the Diffractive VMP benchmarks in 5 steps:
+## 1. Parse the command line and setup environment
+## 2. Detector simulation through ddsim
+## 3. Digitization and reconstruction through Juggler
+## 4. Root-based Physics analyses
+## 5. Finalize
+## =============================================================================
+
+## make sure we launch this script from the project root directory
+PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/../..
+pushd ${PROJECT_ROOT}
+
+echo "Running the Diffractive VMP benchmarks"
+
+
+## =============================================================================
+## Step 1: Setup the environment variables
+##
+## First parse the command line flags.
+## This sets the following environment variables:
+## - CONFIG:   The specific generator configuration
+## - EBEAM:    The electron beam energy
+## - PBEAM:    The ion beam energy
+## - LEADING:  Leading particle of interest (J/psi)
+#export REQUIRE_LEADING=1
+source parse_cmd.sh $@
+
+## We also need the following benchmark-specific variables:
+##
+## - BENCHMARK_TAG: Unique identified for this benchmark process.
+## - BEAM_TAG:      Identifier for the chosen beam configuration
+## - INPUT_PATH:    Path for generator-level input to the benchmarks
+## - TMP_PATH:      Path for temporary data (not exported as artifacts)
+## - RESULTS_PATH:  Path for benchmark output figures and files
+##
+## You can read dvmp/env.sh for more in-depth explanations of the variables.
+source benchmarks/diffractive_vmp/env.sh
+
+## Get a unique file names based on the configuration options
+GEN_FILE=${INPUT_PATH}/gen-${CONFIG}_${JUGGLER_N_EVENTS}.hepmc
+
+SIM_FILE=${TMP_PATH}/sim-${CONFIG}.edm4hep.root
+SIM_LOG=${TMP_PATH}/sim-${CONFIG}.log
+
+
+REC_FILE=${TMP_PATH}/rec-${CONFIG}.root
+REC_LOG=${TMP_PATH}/sim-${CONFIG}.log
+
+PLOT_TAG=${CONFIG}
+
+## =============================================================================
+## Step 2: Run the simulation
+echo "Running Geant4 simulation"
+ls -lrth 
+ls -lrth input
+echo ${TMP_PATH}
+ls -lrth ${TMP_PATH}
+ddsim --runType batch \
+      --part.minimalKineticEnergy 100*GeV  \
+      --filter.tracker edep0 \
+      -v WARNING \
+      --numberOfEvents ${JUGGLER_N_EVENTS} \
+      --compactFile ${DETECTOR_PATH}/${DETECTOR_CONFIG}.xml \
+      --inputFiles ${GEN_FILE} \
+      --outputFile ${SIM_FILE}
+if [ "$?" -ne "0" ] ; then
+  echo "ERROR running ddsim"
+  exit 1
+fi
+
+## =============================================================================
+## Step 3: Run digitization & reconstruction
+echo "Running the digitization and reconstruction"
+## FIXME Need to figure out how to pass file name to juggler from the commandline
+## the tracker_reconstruction.py options file uses the following environment
+## variables:
+## - JUGGLER_SIM_FILE:    input detector simulation
+## - JUGGLER_REC_FILE:    output reconstructed data
+## - JUGGLER_N_EVENTS:    number of events to process (part of global environment)
+## - DETECTOR:    detector package (part of global environment)
+export JUGGLER_SIM_FILE=${SIM_FILE}
+export JUGGLER_REC_FILE=${REC_FILE}
+if [ ${RECO} == "eicrecon" ] ; then
+  eicrecon ${JUGGLER_SIM_FILE} -Ppodio:output_file=${JUGGLER_REC_FILE}
+  if [[ "$?" -ne "0" ]] ; then
+    echo "ERROR running eicrecon"
+    exit 1
+  fi
+fi
+
+if [[ ${RECO} == "juggler" ]] ; then
+  gaudirun.py options/reconstruction.py || [ $? -eq 4 ]
+  if [ "$?" -ne "0" ] ; then
+    echo "ERROR running juggler"
+    exit 1
+  fi
+fi
+## =============================================================================
+## Step 4: Analysis
+
+## write a temporary configuration file for the analysis script
+CONFIG="${TMP_PATH}/${PLOT_TAG}.json"
+cat << EOF > ${CONFIG}
+{
+  "rec_file": "${REC_FILE}",
+  "vm_name": "${LEADING}",
+  "detector": "${DETECTOR_CONFIG}",
+  "output_prefix": "${RESULTS_PATH}/${PLOT_TAG}",
+  "test_tag": "${LEADING}_${BEAM_TAG}"
+}
+EOF
+#cat ${CONFIG}
+
+## run the analysis script with this configuration
+root -b -q "benchmarks/exclusive/diffractive_vmp/analysis/simple_analysis.cxx+(\"${CONFIG}\")"
+if [ "$?" -ne "0" ] ; then
+  echo "ERROR running vm_mass script"
+  exit 1
+fi
+
+## =============================================================================
+## Step 5: finalize
+echo "Finalizing Diffractive VMP benchmark"
+
+## Copy over reconsturction artifacts as long as we don't have
+## too many events
+if [ "${JUGGLER_N_EVENTS}" -lt "500" ] ; then 
+  cp ${REC_FILE} ${RESULTS_PATH}
+fi
+
+## Always move over log files to the results path
+mv ${REC_LOG} ${RESULTS_PATH}
+
+
+## cleanup output files
+#rm -f ${REC_FILE} ${SIM_FILE} ## --> not needed for CI
+
+## =============================================================================
+## All done!
+echo "Diffractive VMP benchmarks complete"
diff --git a/benchmarks/diffractive_vmp/env.sh b/benchmarks/diffractive_vmp/env.sh
new file mode 100644
index 0000000000000000000000000000000000000000..f1abbfc4b4ec54a6af8383e01774e1cab26c5bc0
--- /dev/null
+++ b/benchmarks/diffractive_vmp/env.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+source strict-mode.sh
+
+## =============================================================================
+## Local configuration variables for this particular benchmark 
+## It defines the following additional variables: 
+##
+##  - BENCHMARK_TAG:          Tag to identify this particular benchmark
+##  - BEAM_TAG                Tag to identify the beam configuration
+##  - INPUT_PATH:             Path for generator-level input to the benchmarks
+##  - TMP_PATH:               Path for temporary data (not exported as artifacts)
+##  - RESULTS_PATH:           Path for benchmark output figures and files
+##
+## This script assumes that EBEAM and PBEAM are set as part of the
+## calling script (usually as command line argument).
+##
+## =============================================================================
+
+## Tag for the local benchmark. Should be the same as the directory name for
+## this particular benchmark set (for clarity). 
+## This tag is used for the output artifacts directory (results/${JUGGLER_TAG}) 
+## and a tag in some of the output files.
+export BENCHMARK_TAG="diffractive_vmp"
+echo "Setting up the local environment for the ${BENCHMARK_TAG^^} benchmarks"
+
+## Extra beam tag to identify the desired beam configuration
+export BEAM_TAG="${EBEAM}on${PBEAM}"
+
+if [[ ! -d "input" ]] ; then
+  echo " making local link to input "
+  mkdir_local_data_link  input
+fi
+
+## Data path for input data (generator-level hepmc file)
+INPUT_PATH="input/${BENCHMARK_TAG}/${BEAM_TAG}"
+#export INPUT_PATH=`realpath ${INPUT_PATH}`
+mkdir -p ${INPUT_PATH}
+echo "INPUT_PATH:             ${INPUT_PATH}"
+
+
+## Data path for temporary data (not exported as artifacts)
+TMP_PATH=${LOCAL_DATA_PATH}/tmp/${BEAM_TAG}
+#export TMP_PATH=`realpath ${TMP_PATH}`
+mkdir -p ${TMP_PATH}
+echo "TMP_PATH:               ${TMP_PATH}"
+
+## Data path for benchmark output (plots and reconstructed files
+## if not too big).
+RESULTS_PATH="results/${BENCHMARK_TAG}/${BEAM_TAG}"
+mkdir -p ${RESULTS_PATH}
+export RESULTS_PATH=`realpath ${RESULTS_PATH}`
+echo "RESULTS_PATH:           ${RESULTS_PATH}"
+
+## =============================================================================
+## That's all!
+echo "Local environment setup complete."
diff --git a/benchmarks/diffractive_vmp/get.sh b/benchmarks/diffractive_vmp/get.sh
new file mode 100644
index 0000000000000000000000000000000000000000..d458caaeb354d57d371a8ab81fe4d8de96028c86
--- /dev/null
+++ b/benchmarks/diffractive_vmp/get.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+source strict-mode.sh
+
+## =============================================================================
+## Standin for a proper pythia generation process, similar to how we
+## generate events for DVMP
+## Runs in 5 steps:
+##   1. Parse the command line and setup the environment
+##   2. Check if we can download the file
+##   3. Finalize
+## =============================================================================
+## =============================================================================
+
+## make sure we launch this script from the project root directory
+PROJECT_ROOT="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"/../..
+pushd ${PROJECT_ROOT}
+
+## =============================================================================
+## Step 1: Setup the environment variables
+## First parse the command line flags.
+## This sets the following environment variables:
+## - CONFIG:   The specific generator configuration --> not currenlty used FIXME
+## - EBEAM:    The electron beam energy --> not currently used FIXME
+## - PBEAM:    The ion beam energy --> not currently used FIXME
+source parse_cmd.sh $@
+
+## To run the generator, we need the following global variables:
+##
+## - LOCAL_PREFIX:      Place to cache local packages and data
+## - JUGGLER_N_EVENTS:  Number of events to process
+## - JUGGLER_RNG_SEED:  Random seed for event generation.
+##
+## defined in common_bench repo
+## You can ready bin/env.sh for more in-depth explanations of the variables
+## and how they can be controlled.
+
+
+## We also need the following benchmark-specific variables:
+##
+## - BENCHMARK_TAG: Unique identified for this benchmark process.
+## - INPUT_PATH:    Path for generator-level input to the benchmarks
+## - TMP_PATH:      Path for temporary data (not exported as artifacts)
+##
+## You can read dvmp/env.sh for more in-depth explanations of the variables.
+source benchmarks/diffractive_vmp/env.sh
+
+## Get a unique file name prefix based on the configuration options
+GEN_TAG=gen-${CONFIG}_${JUGGLER_N_EVENTS} ## Generic file prefix
+
+## =============================================================================
+## Step 2: Check if we can find the file
+if [ -f "${INPUT_PATH}/${GEN_TAG}.hepmc" ]; then
+  echo "Found cached generator output for $GEN_TAG, no need to rerun"
+  exit 0
+fi
+
+## =============================================================================
+## Step 3: Copy the file (about 180 lines per event in DIS NC files)
+nlines=$((190*${JUGGLER_N_EVENTS}))
+DATA_URL=S3/eictest/EPIC/EVGEN/EXCLUSIVE/DIFFRACTIVE_JPSI_ABCONV/Sartre/Coherent/sartre_bnonsat_Au_jpsi_ab_eAu_1_000.hepmc.gz
+mc config host add S3 https://dtn01.sdcc.bnl.gov:9000 ${S3_ACCESS_KEY} ${S3_SECRET_KEY}
+mc head -n ${nlines} ${DATA_URL} | sanitize_hepmc3 > ${TMP_PATH}/${GEN_TAG}.hepmc
+if [[ "$?" -ne "0" ]] ; then
+  echo "ERROR downloading file"
+  exit 1
+fi
+
+## =============================================================================
+## Step 4: Finally, move relevant output into the artifacts directory and clean up
+## =============================================================================
+echo "Moving generator output to ${INPUT_PATH}/${GEN_TAG}.hepmc"
+mv ${TMP_PATH}/${GEN_TAG}.hepmc ${INPUT_PATH}/${GEN_TAG}.hepmc
+## this step only matters for local execution
+echo "Cleaning up"
+## does nothing
+
+## =============================================================================
+## All done!
+echo "$BENCHMARK_TAG event generation complete"