From d5619629280648b2db2baa2dc0aaa01175fdef71 Mon Sep 17 00:00:00 2001
From: Nicholas Styles <nicholas.styles@desy.de>
Date: Tue, 10 Nov 2020 11:44:00 +0000
Subject: [PATCH] Merge branch 'speedupOfMMStripsResponseSim' into '21.9'

Improve the CPU usage of the MM strips response simulation

See merge request atlas/athena!38002
---
 .../MM_StripsResponseSimulation.h             |  3 +-
 .../src/MM_StripsResponseSimulation.cxx       | 85 ++++++++++++-------
 2 files changed, 55 insertions(+), 33 deletions(-)

diff --git a/MuonSpectrometer/MuonDigitization/MM_Digitization/MM_Digitization/MM_StripsResponseSimulation.h b/MuonSpectrometer/MuonDigitization/MM_Digitization/MM_Digitization/MM_StripsResponseSimulation.h
index 2ed48255b45..e6deffa1d1a 100644
--- a/MuonSpectrometer/MuonDigitization/MM_Digitization/MM_Digitization/MM_StripsResponseSimulation.h
+++ b/MuonSpectrometer/MuonDigitization/MM_Digitization/MM_Digitization/MM_StripsResponseSimulation.h
@@ -182,7 +182,8 @@ private:
   mutable Athena::MsgStreamMember m_msg = Athena::MsgStreamMember("MMStripResponseSimulation");
 
   // seperate random number generation for performance monitoring
-  float getTransversDiffusion(float posY);
+  float generateTransverseDiffusion(float posY);
+  float getTransverseDiffusion(float posY);
   float getLongitudinalDiffusion(float posY);
   float getEffectiveCharge();
   float getPathLengthTraveled();
diff --git a/MuonSpectrometer/MuonDigitization/MM_Digitization/src/MM_StripsResponseSimulation.cxx b/MuonSpectrometer/MuonDigitization/MM_Digitization/src/MM_StripsResponseSimulation.cxx
index 18216e7d3c9..7cfdbd7de16 100644
--- a/MuonSpectrometer/MuonDigitization/MM_Digitization/src/MM_StripsResponseSimulation.cxx
+++ b/MuonSpectrometer/MuonDigitization/MM_Digitization/src/MM_StripsResponseSimulation.cxx
@@ -233,23 +233,9 @@ void MM_StripsResponseSimulation::whichStrips( const float & hitx,
 			<< pathLengthTraveled
 			);
 
-		for (auto& Electron : IonizationCluster.getElectrons()){
-
-		//	m_longitudinalDiffusionFunction->SetParameters(1.0, 0., initialPosition.Y()*m_longitudinalDiffusionSigma);
-
-		//	if ( m_longitudinalDiffusionSigma == 0 || m_transverseDiffusionSigma == 0) {
-
-		//		m_transverseDiffusionFunction->SetParameters( initialPosition.Y()*m_transverseDiffusionSigma , 0.0);
-
-		//	} else {
-
-		//		m_transverseDiffusionFunction->SetParameters( initialPosition.Y()*m_transverseDiffusionSigma , 1.0);
-
-		//	}
-
-			Electron->setOffsetPosition(getTransversDiffusion(initialPosition.Y()) , getLongitudinalDiffusion(initialPosition.Y()) );
-
-		}
+        for (auto& Electron : IonizationCluster.getElectrons()) {
+            Electron->setOffsetPosition(getTransverseDiffusion(initialPosition.Y()) , getLongitudinalDiffusion(initialPosition.Y()) );
+        }
 
 		IonizationCluster.propagateElectrons( lorentzAngle , m_driftVelocity );
 
@@ -337,28 +323,63 @@ void MM_StripsResponseSimulation::whichStrips( const float & hitx,
 
 } // end of whichStrips()
 
-float MM_StripsResponseSimulation::getTransversDiffusion(float posY){
-  if ( m_longitudinalDiffusionSigma == 0 || m_transverseDiffusionSigma == 0) {
 
-	  m_transverseDiffusionFunction->SetParameter(2, posY*m_transverseDiffusionSigma);
-	  m_transverseDiffusionFunction->SetParameter(5, 0.0);
 
-	} else {
-	  m_transverseDiffusionFunction->SetParameter(2, posY*m_transverseDiffusionSigma);
-    m_transverseDiffusionFunction->SetParameter(5, 1.0);
-	}
-  return m_transverseDiffusionFunction->GetRandom(); 
+float MM_StripsResponseSimulation::generateTransverseDiffusion(float posY) {
+    // this is a helper function used in getTransverseDiffusion, generating double gaussian distributions
+
+    // avoid division by zero in calculation of scale if ypos is 0
+    // also protect against negative values of ypos which should never happen
+    if (posY <= 0) posY = 0.001;
+
+    // need to scale weigths since initial distributions were not normalized
+    double scale = 0.001/(posY*m_transverseDiffusionSigma);
+
+    double uni = m_random->Uniform(0, 1.0+scale);
+    if (uni < scale) return m_random->Gaus(0.0, 1.0);
+    return m_random->Gaus(0., m_transverseDiffusionSigma * posY);
+}
+
+
+float MM_StripsResponseSimulation::getTransverseDiffusion(float posY) {
+    // the random numbers are generate from the following function:
+    // "1.*TMath::Exp(-TMath::Power(x,2.)/(2.*[0]*[0])) + 0.001*TMath::Exp(-TMath::Power(x,2)/(2.*[1]*[1]))"
+    // in the range from -1 to 1
+    // Since sampling from a TF1 where its parameters are changed for every random number is slow,
+    // the following approach is used to generate random numbers from the mixture of two Gaussian:
+    // https://stats.stackexchange.com/questions/226834/sampling-from-a-mixture-of-two-gamma-distributions/226837#226837
+    // this approach seems to be around 20000 times faster
+
+    // if one of the diffusions is off, the tail is not present
+    if (m_longitudinalDiffusionSigma == 0 || m_transverseDiffusionSigma == 0) {
+        float tmp =  m_random->Gaus(0.0, posY*m_transverseDiffusionSigma);
+        // limit random number to be -1 < x < 1
+        while (std::abs(tmp) > 1.) tmp =  m_random->Gaus(0.0, posY*m_transverseDiffusionSigma);
+        return tmp;
+    }
+    float tmp = generateTransverseDiffusion(posY);
+    while (std::abs(tmp) > 1.) {tmp = generateTransverseDiffusion(posY);}
+
+    return tmp;
   }
 
-float MM_StripsResponseSimulation::getLongitudinalDiffusion(float posY){
-  m_longitudinalDiffusionFunction->SetParameters(1.0, 0., posY*m_longitudinalDiffusionSigma);
-  return m_longitudinalDiffusionFunction->GetRandom();
+float MM_StripsResponseSimulation::getLongitudinalDiffusion(float posY) {
+  float tmp = m_random->Gaus(0.0, posY*m_longitudinalDiffusionSigma);
+  // We only want random numbers between -5 and 5
+  while (std::abs(tmp) > 5) { tmp = m_random->Gaus(0.0, posY*m_longitudinalDiffusionSigma); }
+  return tmp;
 }
 
-float  MM_StripsResponseSimulation::getEffectiveCharge(){return m_polyaFunction->GetRandom();}
+float  MM_StripsResponseSimulation::getEffectiveCharge() {return m_polyaFunction->GetRandom();}
+
+float MM_StripsResponseSimulation::getPathLengthTraveled() {
+    float rndGaus = m_random->Gaus(m_interactionDensityMean, m_interactionDensitySigma);
+
+
+    // gaussian random number should be in the range from 0 to 10
+    while (rndGaus < 0. || rndGaus > 10.) {rndGaus = m_random->Gaus(m_interactionDensityMean, m_interactionDensitySigma);}
 
-float MM_StripsResponseSimulation::getPathLengthTraveled(){
-	return  ( 1. / m_interactionDensityFunction->GetRandom() ) * -1. * log( m_random->Uniform() );
+    return  ( 1. / rndGaus) * -1. * log( m_random->Uniform() );
 }
 
 
-- 
GitLab