Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:21:56

0001 /////////////////////////////////////////////////////////////////////////////////////
0002 // EcoMug: Efficient COsmic MUon Generator                                         //
0003 // Copyright (C) 2021 Davide Pagano <davide.pagano@unibs.it>                       //
0004 // EcoMug is based on the following work:                                          //
0005 // D. Pagano, G. Bonomi, A. Donzella, A. Zenoni, G. Zumerle, N. Zurlo,             //
0006 // "EcoMug: an Efficient COsmic MUon Generator for cosmic-ray muons applications", //
0007 // doi:10.1016/j.nima.2021.165732                                                  //
0008 //                                                                                 //
0009 // This program is free software: you can redistribute it and/or modify            //
0010 // it under the terms of the GNU General Public License as published by            //
0011 // the Free Software Foundation, either version 3 of the License, or               //
0012 // (at your option) any later version.                                             //
0013 //                                                                                 //
0014 // This program is distributed in the hope that it will be useful,                 //
0015 // but WITHOUT ANY WARRANTY; without even the implied warranty of                  //
0016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                   //
0017 // GNU General Public License for more details.                                    //
0018 //                                                                                 //
0019 // You should have received a copy of the GNU General Public License               //
0020 // along with this program.  If not, see <https://www.gnu.org/licenses/>.          //
0021 /////////////////////////////////////////////////////////////////////////////////////
0022 
0023 #ifndef G4MAIN_ECOMUG_H
0024 #define G4MAIN_ECOMUG_H
0025 
0026 #include <array>
0027 #include <functional>
0028 #include <random>
0029 
0030 //! Fast generation of random numbers
0031 //! This class is based on the xoroshiro128+ generator.
0032 //! https://prng.di.unimi.it/
0033 class EMRandom
0034 {
0035  public:
0036   EMRandom()
0037   {
0038     std::random_device rd;
0039     std::mt19937 gen(rd());
0040     std::uniform_int_distribution<uint64_t> dis(0, std::numeric_limits<uint64_t>::max());
0041     s[0] = 12345.;  // dis(gen);
0042     s[1] = 12345;   // dis(gen);
0043   };
0044 
0045   void SetSeed(uint64_t seed)
0046   {
0047     s[0] = seed;
0048     s[1] = seed;
0049   };
0050 
0051   double GenerateRandomDouble()
0052   {
0053     uint64_t x = next();
0054     return to_double(x);
0055   };
0056 
0057   double GenerateRandomDouble(double x1, double x2)
0058   {
0059     return (x2 - x1) * GenerateRandomDouble() + x1;
0060   };
0061 
0062   int64_t rotl(const uint64_t x, int k)
0063   {
0064     return (x << k) | (x >> (64 - k));
0065   };
0066 
0067   uint64_t next()
0068   {
0069     const uint64_t s0 = s[0];
0070     uint64_t s1 = s[1];
0071     const uint64_t result = s0 + s1;
0072     s1 ^= s0;
0073     s[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14);
0074     s[1] = rotl(s1, 36);
0075     return result;
0076   };
0077 
0078   double to_double(uint64_t x)
0079   {
0080     union U
0081     {
0082       uint64_t i;
0083       double d;
0084     };
0085     U u = {UINT64_C(0x3FF) << 52 | x >> 12};
0086     return u.d - 1.0;
0087   };
0088 
0089   uint64_t s[2];
0090 };
0091 ///////////////////////////////////////////////////////////////
0092 ///////////////////////////////////////////////////////////////
0093 
0094 //! Class for maximization based on "Whale Optimization Algorithm"
0095 //! doi:10.1016/j.advengsoft.2016.01.008
0096 class EMMaximization
0097 {
0098  private:
0099   EMRandom mRandom;
0100   size_t mPopSize;
0101   size_t mNIter;
0102   int mGenMethod;  // 0 = sky, 1 = cylinder, 2 = hspere
0103   double m_a;
0104   double m_a2;
0105   std::vector<std::vector<double> > mRanges;
0106   std::vector<std::vector<double> > mPopulation;
0107   double mBestCost;
0108   std::vector<double> mBestSolution;
0109   std::function<double(double, double)> mFunc;
0110 
0111  public:
0112   EMMaximization(const EMRandom& random, int genMethod)
0113     : mRandom(random)
0114     , mPopSize(200)
0115     , mNIter(500)
0116     , mGenMethod(genMethod)
0117     , m_a(0.)
0118     , m_a2(0.)
0119     , mBestCost(-1.)
0120   {
0121     // cppcheck-suppress [useInitializationList]
0122     mFunc = &DefaultJ;
0123   };
0124   ///////////////////////////////////////////////////////////////
0125 
0126   static double DefaultJ(double p, double theta)
0127   {
0128     double n = std::max(0.1, 2.856 - 0.655 * log(p));
0129     return 1600 * pow(p, 0.279) * pow(cos(theta), n);
0130   }
0131   ///////////////////////////////////////////////////////////////
0132 
0133   void SetParameters(double minP, double maxP, double minTheta, double maxTheta)
0134   {
0135     mRanges.push_back({minP, maxP});
0136     mRanges.push_back({minTheta, maxTheta});
0137   }
0138   ///////////////////////////////////////////////////////////////
0139 
0140   void SetParameters(double minP, double maxP, double minTheta, double maxTheta, double minPhi, double maxPhi)
0141   {
0142     mRanges.push_back({minP, maxP});
0143     mRanges.push_back({minTheta, maxTheta});
0144     mRanges.push_back({minPhi, maxPhi});
0145     mRanges.push_back({0, M_PI / 2.});
0146   }
0147   ///////////////////////////////////////////////////////////////
0148 
0149   void SetFunction(std::function<double(double, double)> func)
0150   {
0151     mFunc = func;
0152   }
0153   ///////////////////////////////////////////////////////////////
0154 
0155   double SkyFunc(double p, double theta)
0156   {
0157     return mFunc(p, theta) * cos(theta) * sin(theta);
0158   }
0159   ///////////////////////////////////////////////////////////////
0160 
0161   double CylFunc(double p, double theta)
0162   {
0163     return mFunc(p, theta) * pow(sin(theta), 2);
0164   }
0165   ///////////////////////////////////////////////////////////////
0166 
0167   double HSFunc(double p, double theta, double phi, double theta0)
0168   {
0169     return mFunc(p, theta) * (sin(theta0) * sin(theta) * cos(phi) + cos(theta0) * cos(theta)) * sin(theta);
0170   }
0171   ///////////////////////////////////////////////////////////////
0172 
0173   double Evaluate(std::vector<double>& v)
0174   {
0175     if (mGenMethod == 0)
0176     {
0177       return SkyFunc(v[0], v[1]);
0178     }
0179     else if (mGenMethod == 1)
0180     {
0181       return CylFunc(v[0], v[1]);
0182     }
0183     else
0184     {
0185       return HSFunc(v[0], v[1], v[2], v[3]);
0186     }
0187     return -1;
0188   }
0189   ///////////////////////////////////////////////////////////////
0190 
0191   void Evaluate()
0192   {
0193     double value;
0194     for (size_t i = 0; i < mPopSize; ++i)
0195     {
0196       value = Evaluate(mPopulation[i]);
0197       if (value > mBestCost)
0198       {
0199         mBestCost = value;
0200         mBestSolution = mPopulation[i];
0201       }
0202     }
0203   }
0204   ///////////////////////////////////////////////////////////////
0205 
0206   void Init()
0207   {
0208     size_t dim = mRanges.size();
0209     mPopulation.resize(mPopSize);
0210     for (size_t i = 0; i < mPopSize; ++i)
0211     {
0212       mPopulation[i].resize(dim);
0213       for (size_t j = 0; j < dim; ++j)
0214       {
0215         mPopulation[i][j] = mRandom.GenerateRandomDouble(mRanges[j][0], mRanges[j][1]);
0216       }
0217     }
0218   }
0219   ///////////////////////////////////////////////////////////////
0220 
0221   void UpdateParameters(size_t t)
0222   {
0223     m_a = 2. - t * (2. / mNIter);
0224     m_a2 = -1. + t * ((-1.) / mNIter);
0225   }
0226   ///////////////////////////////////////////////////////////////
0227 
0228   void Move()
0229   {
0230     double r1, r2, A, C, b, l, rw, p, D_tmp, D_best, distance;
0231     std::vector<double> tmp;
0232     for (size_t i = 0; i < mPopulation.size(); ++i)
0233     {
0234       r1 = mRandom.GenerateRandomDouble();
0235       r2 = mRandom.GenerateRandomDouble();
0236       A = 2 * m_a * r1 - m_a;
0237       C = 2 * r2;
0238       b = 1.;
0239       l = (m_a2 - 1) * mRandom.GenerateRandomDouble() + 1;
0240       p = mRandom.GenerateRandomDouble();
0241 
0242       for (size_t j = 0; j < mPopulation[0].size(); ++j)
0243       {
0244         if (p < 0.5)
0245         {
0246           if (fabs(A) >= 1)
0247           {
0248             rw = floor(mRandom.GenerateRandomDouble() * mPopulation.size());
0249             tmp = mPopulation[rw];
0250             D_tmp = fabs(C * tmp[j] - mPopulation[i][j]);
0251             mPopulation[i][j] = tmp[j] - A * D_tmp;
0252           }
0253           else
0254           {
0255             D_best = fabs(C * mBestSolution[j] - mPopulation[i][j]);
0256             mPopulation[i][j] = mBestSolution[j] - A * D_best;
0257           }
0258         }
0259         else
0260         {
0261           distance = fabs(mBestSolution[j] - mPopulation[i][j]);
0262           mPopulation[i][j] = distance * exp(b * l) * cos(l * 2 * M_PI) + mBestSolution[j];
0263         }
0264         if (mPopulation[i][j] < mRanges[j][0]) mPopulation[i][j] = mRanges[j][0];
0265         if (mPopulation[i][j] > mRanges[j][1]) mPopulation[i][j] = mRanges[j][1];
0266       }
0267     }
0268   }
0269   ///////////////////////////////////////////////////////////////
0270 
0271   double Maximize()
0272   {
0273     Init();
0274     Evaluate();
0275     for (size_t iter = 1; iter < mNIter; ++iter)
0276     {
0277       UpdateParameters(iter);
0278       Move();
0279       Evaluate();
0280     }
0281     return mBestCost;
0282   }
0283 };
0284 ///////////////////////////////////////////////////////////////
0285 ///////////////////////////////////////////////////////////////
0286 
0287 //! Class for the generation of cosmic muons
0288 class EcoMug
0289 {
0290   friend class EMRandom;
0291 
0292  public:
0293   /// Possible generation methods
0294   enum EMGeometry
0295   {
0296     Sky,       ///< generation from a plane (flat sky)
0297     Cylinder,  ///< generation from a cylinder
0298     HSphere    ///< generation from a half-sphere
0299   };
0300 
0301  private:
0302   EMGeometry mGenMethod;
0303   std::array<double, 3> mGenerationPosition;
0304   double mGenerationTheta;
0305   double mGenerationPhi;
0306   double mGenerationMomentum;
0307   double mMinimumMomentum;
0308   double mMaximumMomentum;
0309   double mMinimumTheta;
0310   double mMaximumTheta;
0311   double mMinimumPhi;
0312   double mMaximumPhi;
0313   int mCharge;
0314   double mCylinderMinPositionPhi;
0315   double mCylinderMaxPositionPhi;
0316   double mHSphereMinPositionPhi;
0317   double mHSphereMaxPositionPhi;
0318   double mHSphereMinPositionTheta;
0319   double mHSphereMaxPositionTheta;
0320   double mHSphereCosMinPositionTheta;
0321   double mHSphereCosMaxPositionTheta;
0322   double mJPrime;
0323   double mN;
0324   double mRandAccRej;
0325   double mPhi0;
0326   double mTheta0;
0327   bool mAccepted;
0328   std::array<double, 2> mSkySize;
0329   std::array<double, 3> mSkyCenterPosition;
0330   double mCylinderHeight;
0331   double mCylinderRadius;
0332   std::array<double, 3> mCylinderCenterPosition;
0333   double mHSphereRadius;
0334   //  double mMaxFuncSkyCylinder;
0335   std::array<double, 3> mHSphereCenterPosition;
0336   EMRandom mRandom;
0337   std::array<double, 3> mMaxJ;
0338   std::array<double, 3> mMaxCustomJ;
0339   std::function<double(double, double)> mJ;
0340 
0341  public:
0342   // Default constructor
0343   EcoMug()
0344     : mGenMethod(Sky)
0345     , mGenerationPosition({{0., 0., 0.}})
0346     , mGenerationTheta(0.)
0347     , mGenerationPhi(0.)
0348     , mGenerationMomentum(0.)
0349     , mMinimumMomentum(0.01)
0350     , mMaximumMomentum(1000.)
0351     , mMinimumTheta(0.)
0352     , mMaximumTheta(M_PI / 2.)
0353     , mMinimumPhi(0.)
0354     , mMaximumPhi(2. * M_PI)
0355     , mCharge(1)
0356     , mCylinderMinPositionPhi(0.)
0357     , mCylinderMaxPositionPhi(2. * M_PI)
0358     , mHSphereMinPositionPhi(0.)
0359     , mHSphereMaxPositionPhi(2. * M_PI)
0360     , mHSphereMinPositionTheta(0.)
0361     , mHSphereMaxPositionTheta(M_PI / 2.)
0362     , mHSphereCosMinPositionTheta(1.)
0363     , mHSphereCosMaxPositionTheta(0.)
0364     , mJPrime(0.)
0365     , mN(0.)
0366     , mRandAccRej(0.)
0367     , mPhi0(0.)
0368     , mTheta0(0.)
0369     , mAccepted(false)
0370     , mSkySize({{0., 0.}})
0371     , mSkyCenterPosition({{0., 0., 0.}})
0372     , mCylinderHeight(0.)
0373     , mCylinderRadius(0.)
0374     , mCylinderCenterPosition({{0., 0., 0.}})
0375     , mHSphereRadius(0.)
0376     ,
0377     /* mMaxFuncSkyCylinder(5.3176), */ mHSphereCenterPosition({{0., 0., 0.}})
0378   {
0379     // cppcheck-suppress [useInitializationList]
0380     mMaxJ = {-1., -1., -1.};
0381     // cppcheck-suppress [useInitializationList]
0382     mMaxCustomJ = {-1., -1., -1.};
0383   };
0384 
0385   ///////////////////////////////////////////////////////////////
0386   // Methods to access the parameters of the generated muon
0387   ///////////////////////////////////////////////////////////////
0388   /// Get the generation position
0389   const std::array<double, 3>& GetGenerationPosition() const
0390   {
0391     return mGenerationPosition;
0392   };
0393   /// Get the generation momentum
0394   double GetGenerationMomentum() const
0395   {
0396     return mGenerationMomentum;
0397   };
0398   /// Get the generation momentum
0399   void GetGenerationMomentum(std::array<double, 3>& momentum) const
0400   {
0401     momentum = {
0402         mGenerationMomentum * sin(mGenerationTheta) * cos(mGenerationPhi),
0403         mGenerationMomentum * sin(mGenerationTheta) * sin(mGenerationPhi),
0404         mGenerationMomentum * cos(mGenerationTheta)};
0405   };
0406   /// Get the generation theta
0407   double GetGenerationTheta() const
0408   {
0409     return mGenerationTheta;
0410   };
0411   /// Get the generation phi
0412   double GetGenerationPhi() const
0413   {
0414     return mGenerationPhi;
0415   };
0416   /// Get charge
0417   int GetCharge() const
0418   {
0419     return mCharge;
0420   };
0421   ///////////////////////////////////////////////////////////////
0422 
0423   ///////////////////////////////////////////////////////////////
0424   // Methods for the geometry of the generation
0425   ///////////////////////////////////////////////////////////////
0426   /// Set generation from sky
0427   void SetUseSky()
0428   {
0429     mGenMethod = Sky;
0430   };
0431   /// Set cylindrical generation
0432   void SetUseCylinder()
0433   {
0434     mGenMethod = Cylinder;
0435   };
0436   /// Set half-sphere generation
0437   void SetUseHSphere()
0438   {
0439     mGenMethod = HSphere;
0440   };
0441   /// Set the generation method (Sky, Cylinder or HSphere)
0442   void SetGenerationMethod(EMGeometry genM)
0443   {
0444     mGenMethod = genM;
0445   };
0446 
0447   /// Get the generation method (Sky, Cylinder or HSphere)
0448   EMGeometry GetGenerationMethod() const
0449   {
0450     return mGenMethod;
0451   };
0452   ///////////////////////////////////////////////////////////////
0453 
0454   ///////////////////////////////////////////////////////////////
0455   // Common methods to all geometries
0456   ///////////////////////////////////////////////////////////////
0457   /// Set the differential flux J. Accepted functions are like
0458   /// double J(double momentum, double theta)
0459   /// momentum has to be in GeV/c and theta in radians
0460   void SetDifferentialFlux(std::function<double(double, double)> J)
0461   {
0462     mJ = J;
0463   };
0464   /// Set the seed for the internal PRNG (if 0 a random seed is used)
0465   void SetSeed(uint64_t seed)
0466   {
0467     if (seed > 0) mRandom.SetSeed(seed);
0468   };
0469   /// Set minimum generation Momentum
0470   void SetMinimumMomentum(double momentum)
0471   {
0472     mMinimumMomentum = momentum;
0473   };
0474   /// Set maximum generation Momentum
0475   void SetMaximumMomentum(double momentum)
0476   {
0477     mMaximumMomentum = momentum;
0478   };
0479   /// Set minimum generation Theta
0480   void SetMinimumTheta(double theta)
0481   {
0482     mMinimumTheta = theta;
0483   };
0484   /// Set maximum generation Theta
0485   void SetMaximumTheta(double theta)
0486   {
0487     mMaximumTheta = theta;
0488   };
0489   /// Set minimum generation Phi
0490   void SetMinimumPhi(double phi)
0491   {
0492     mMinimumPhi = phi;
0493   };
0494   /// Set maximum generation Phi
0495   void SetMaximumPhi(double phi)
0496   {
0497     mMaximumPhi = phi;
0498   };
0499 
0500   /// Get minimum generation Momentum
0501   double GetMinimumMomentum() const
0502   {
0503     return mMinimumMomentum;
0504   };
0505   /// Get maximum generation Momentum
0506   double GetMaximumMomentum() const
0507   {
0508     return mMaximumMomentum;
0509   };
0510   /// Get minimum generation Theta
0511   double GetMinimumTheta() const
0512   {
0513     return mMinimumTheta;
0514   };
0515   /// Get maximum generation Theta
0516   double GetMaximumTheta() const
0517   {
0518     return mMaximumTheta;
0519   };
0520   /// Get minimum generation Phi
0521   double GetMinimumPhi() const
0522   {
0523     return mMinimumPhi;
0524   };
0525   /// Get maximum generation Phi
0526   double GetMaximumPhi() const
0527   {
0528     return mMaximumPhi;
0529   };
0530   ///////////////////////////////////////////////////////////////
0531 
0532   ///////////////////////////////////////////////////////////////
0533   // Methods for the plane-based generation
0534   ///////////////////////////////////////////////////////////////
0535   /// Set sky size
0536   void SetSkySize(const std::array<double, 2>& size)
0537   {
0538     mSkySize = size;
0539   };
0540 
0541   /// Set sky center position
0542   void SetSkyCenterPosition(const std::array<double, 3>& position)
0543   {
0544     mSkyCenterPosition = position;
0545   };
0546   ///////////////////////////////////////////////////////////////
0547 
0548   ///////////////////////////////////////////////////////////////
0549   // Methods for the cylinder-based generation
0550   ///////////////////////////////////////////////////////////////
0551   /// Set cylinder radius
0552   void SetCylinderRadius(double radius)
0553   {
0554     mCylinderRadius = radius;
0555   };
0556   /// Set cylinder height
0557   void SetCylinderHeight(double height)
0558   {
0559     mCylinderHeight = height;
0560   };
0561   /// Set cylinder center position
0562   void SetCylinderCenterPosition(const std::array<double, 3>& position)
0563   {
0564     mCylinderCenterPosition = position;
0565   };
0566   void SetCylinderMinPositionPhi(double phi)
0567   {
0568     mCylinderMinPositionPhi = phi;
0569   };
0570   void SetCylinderMaxPositionPhi(double phi)
0571   {
0572     mCylinderMaxPositionPhi = phi;
0573   };
0574   /// Get cylinder radius
0575   double GetCylinderRadius() const
0576   {
0577     return mCylinderRadius;
0578   };
0579   /// Get cylinder height
0580   double GetCylinderHeight() const
0581   {
0582     return mCylinderHeight;
0583   };
0584   /// Get cylinder center position
0585   const std::array<double, 3>& GetCylinderCenterPosition() const
0586   {
0587     return mCylinderCenterPosition;
0588   };
0589   ///////////////////////////////////////////////////////////////
0590 
0591   ///////////////////////////////////////////////////////////////
0592   // Methods for the half sphere-based generation
0593   ///////////////////////////////////////////////////////////////
0594   /// Set half-sphere radius
0595   void SetHSphereRadius(double radius)
0596   {
0597     mHSphereRadius = radius;
0598   };
0599   /// Set half-sphere center position
0600   void SetHSphereCenterPosition(const std::array<double, 3>& position)
0601   {
0602     mHSphereCenterPosition = position;
0603   };
0604   void SetHSphereMinPositionPhi(double phi)
0605   {
0606     mHSphereMinPositionPhi = phi;
0607   };
0608   void SetHSphereMaxPositionPhi(double phi)
0609   {
0610     mHSphereMaxPositionPhi = phi;
0611   };
0612   void SetHSphereMinPositionTheta(double theta)
0613   {
0614     mHSphereMinPositionTheta = theta;
0615     mHSphereCosMinPositionTheta = cos(mHSphereMinPositionTheta);
0616   };
0617   void SetHSphereMaxPositionTheta(double theta)
0618   {
0619     mHSphereMaxPositionTheta = theta;
0620     mHSphereCosMaxPositionTheta = cos(mHSphereMaxPositionTheta);
0621   };
0622   /// Get half-sphere radius
0623   double GetHSphereRadius() const
0624   {
0625     return mHSphereRadius;
0626   };
0627   /// Get half-sphere center position
0628   const std::array<double, 3>& GetHSphereCenterPosition() const
0629   {
0630     return mHSphereCenterPosition;
0631   };
0632   ///////////////////////////////////////////////////////////////
0633 
0634  private:
0635   double F1Cumulative(double x)
0636   {
0637     return 1. - 8.534790171171021 / pow(x + 2.68, 87. / 40.);
0638   };
0639 
0640   double F1Inverse(double x)
0641   {
0642     return (2.68 - 2.68 * pow(1. - x, 40. / 87.)) / pow(1. - x, 40. / 87.);
0643   };
0644 
0645   double maxSkyJFunc()
0646   {
0647     return 1600 * pow(mMaximumMomentum, 0.279) * pow(cos(0.76158), 1.1) * sin(0.76158);
0648   };
0649 
0650   double maxCylJFunc()
0651   {
0652     return 1600 * pow(mMaximumMomentum, 0.279) * pow(cos(1.35081), 0.1) * pow(sin(1.35081), 2);
0653   };
0654 
0655   double maxHSJFunc()
0656   {
0657     return 1600 * pow(mMaximumMomentum, 0.279) * pow(cos(1.26452), 0.1) * (sin(1.26452) * sin(1.26452) + cos(1.26452) * cos(1.26452)) * sin(1.26452);
0658   };
0659 
0660   double GenerateMomentumF1()
0661   {
0662     double z = mRandom.GenerateRandomDouble(F1Cumulative(mMinimumMomentum), F1Cumulative(mMaximumMomentum));
0663     return F1Inverse(z);
0664   };
0665 
0666   void GeneratePositionSky()
0667   {
0668     mGenerationPosition[0] = mRandom.GenerateRandomDouble(mSkyCenterPosition[0] - mSkySize[0] / 2., mSkyCenterPosition[0] + mSkySize[0] / 2.);
0669     mGenerationPosition[1] = mRandom.GenerateRandomDouble(mSkyCenterPosition[1] - mSkySize[1] / 2., mSkyCenterPosition[1] + mSkySize[1] / 2.);
0670     mGenerationPosition[2] = mSkyCenterPosition[2];
0671   };
0672 
0673   void GeneratePositionCylinder()
0674   {
0675     mPhi0 = mRandom.GenerateRandomDouble(mCylinderMinPositionPhi, mCylinderMaxPositionPhi);
0676     mGenerationPosition[0] = mCylinderCenterPosition[0] + mCylinderRadius * cos(mPhi0);
0677     mGenerationPosition[1] = mCylinderCenterPosition[1] + mCylinderRadius * sin(mPhi0);
0678     mGenerationPosition[2] = mRandom.GenerateRandomDouble(mCylinderCenterPosition[2] - mCylinderHeight / 2., mCylinderCenterPosition[2] + mCylinderHeight / 2.);
0679   };
0680 
0681   void ComputeMaximumCustomJ()
0682   {
0683     EMMaximization maximizer(mRandom, mGenMethod);
0684     maximizer.SetFunction(mJ);
0685     if (mGenMethod == 0 || mGenMethod == 1)
0686     {
0687       maximizer.SetParameters(mMinimumMomentum, mMaximumMomentum, mMinimumTheta, mMaximumTheta);
0688     }
0689     else
0690     {
0691       maximizer.SetParameters(mMinimumMomentum, mMaximumMomentum, mMinimumTheta, mMaximumTheta, mMinimumPhi, mMaximumPhi);
0692     }
0693     mMaxCustomJ[mGenMethod] = maximizer.Maximize();
0694   };
0695 
0696   void ComputeMaximum()
0697   {
0698     EMMaximization maximizer(mRandom, mGenMethod);
0699     if (mGenMethod == 0 || mGenMethod == 1)
0700     {
0701       maximizer.SetParameters(mMinimumMomentum, mMaximumMomentum, mMinimumTheta, mMaximumTheta);
0702     }
0703     else
0704     {
0705       maximizer.SetParameters(mMinimumMomentum, mMaximumMomentum, mMinimumTheta, mMaximumTheta, mMinimumPhi, mMaximumPhi);
0706     }
0707     mMaxJ[mGenMethod] = maximizer.Maximize();
0708   };
0709 
0710  public:
0711   ///////////////////////////////////////////////////////////////
0712   /// Generate a cosmic muon from the pre-defined J
0713   ///////////////////////////////////////////////////////////////
0714   void Generate()
0715   {
0716     mAccepted = false;
0717 
0718     if (mMaxJ[mGenMethod] < 0) ComputeMaximum();
0719 
0720     // Sky or cylinder generation
0721     if (mGenMethod == Sky || mGenMethod == Cylinder)
0722     {
0723       // Generation of the momentum and theta angle
0724       while (!mAccepted)
0725       {
0726         mRandAccRej = mRandom.GenerateRandomDouble();
0727         mGenerationTheta = mRandom.GenerateRandomDouble(mMinimumTheta, mMaximumTheta);
0728         mGenerationMomentum = GenerateMomentumF1();
0729         mN = 2.856 - 0.655 * log(mGenerationMomentum);
0730         if (mN < 0.1) mN = 0.1;
0731 
0732         if (mGenMethod == Sky)
0733         {
0734           mJPrime = 1600 * pow(mGenerationMomentum, 0.279) * pow(cos(mGenerationTheta), mN + 1) * sin(mGenerationTheta);
0735           if (mMaxJ[mGenMethod] * mRandAccRej < mJPrime) mAccepted = true;
0736         }
0737 
0738         if (mGenMethod == Cylinder)
0739         {
0740           mJPrime = 1600 * pow(mGenerationMomentum, 0.279) * pow(cos(mGenerationTheta), mN) * pow(sin(mGenerationTheta), 2);
0741           if (mMaxJ[mGenMethod] * mRandAccRej < mJPrime) mAccepted = true;
0742         }
0743       }
0744       mGenerationTheta = M_PI - mGenerationTheta;
0745 
0746       // Generation of the position and phi angle
0747       if (mGenMethod == Sky)
0748       {
0749         GeneratePositionSky();
0750         mGenerationPhi = mRandom.GenerateRandomDouble(mMinimumPhi, mMaximumPhi);
0751       }
0752       if (mGenMethod == Cylinder)
0753       {
0754         mAccepted = false;
0755         GeneratePositionCylinder();
0756         while (!mAccepted)
0757         {
0758           mRandAccRej = mRandom.GenerateRandomDouble();
0759           mGenerationPhi = mRandom.GenerateRandomDouble(mMinimumPhi, mMaximumPhi);
0760           if (mRandAccRej < fabs(cos(mGenerationPhi))) mAccepted = true;
0761         }
0762         mGenerationPhi = mGenerationPhi + mPhi0;
0763         if (mGenerationPhi >= 2. * M_PI) mGenerationPhi -= 2. * M_PI;
0764 
0765         // Check if the muon is inward
0766         if (sin(mGenerationTheta) * cos(mGenerationPhi) * mGenerationPosition[0] + sin(mGenerationTheta) * sin(mGenerationPhi) * mGenerationPosition[1] > 0) Generate();
0767       }
0768     }
0769 
0770     // Half-sphere generation
0771     if (mGenMethod == HSphere)
0772     {
0773       // Generation point on the half-sphere
0774       mPhi0 = mRandom.GenerateRandomDouble(mHSphereMinPositionPhi, mHSphereMaxPositionPhi);
0775       while (!mAccepted)
0776       {
0777         mRandAccRej = mRandom.GenerateRandomDouble();
0778         mTheta0 = acos(mRandom.GenerateRandomDouble(mHSphereCosMaxPositionTheta, mHSphereCosMinPositionTheta));
0779         mGenerationTheta = mRandom.GenerateRandomDouble(mMinimumTheta, mMaximumTheta);
0780         mGenerationPhi = mRandom.GenerateRandomDouble(mMinimumPhi, mMaximumPhi);
0781         mGenerationMomentum = GenerateMomentumF1();
0782         mN = 2.856 - 0.655 * log(mGenerationMomentum);
0783         if (mN < 0.1) mN = 0.1;
0784 
0785         mJPrime = 1600 * pow(mGenerationMomentum, 0.279) * pow(cos(mGenerationTheta), mN) * (sin(mGenerationTheta) * sin(mTheta0) * cos(mGenerationPhi) + cos(mGenerationTheta) * cos(mTheta0)) * sin(mGenerationTheta);
0786         if (mMaxJ[mGenMethod] * mRandAccRej < mJPrime) mAccepted = true;
0787       }
0788 
0789       mGenerationPosition[0] = mHSphereRadius * sin(mTheta0) * cos(mPhi0) + mHSphereCenterPosition[0];
0790       mGenerationPosition[1] = mHSphereRadius * sin(mTheta0) * sin(mPhi0) + mHSphereCenterPosition[1];
0791       mGenerationPosition[2] = mHSphereRadius * cos(mTheta0) + mHSphereCenterPosition[2];
0792 
0793       mGenerationTheta = M_PI - mGenerationTheta;
0794       mGenerationPhi = mGenerationPhi + mPhi0;
0795       if (mGenerationPhi >= 2 * M_PI) mGenerationPhi -= 2 * M_PI;
0796 
0797       mGenerationPhi += M_PI;
0798       if (mGenerationPhi >= 2 * M_PI) mGenerationPhi -= 2 * M_PI;
0799     }
0800 
0801     // Generate the charge
0802     if (mRandom.GenerateRandomDouble(-100, 128) >= 0)
0803       mCharge = 1;
0804     else
0805       mCharge = -1;
0806   };
0807   ///////////////////////////////////////////////////////////////
0808 
0809   ///////////////////////////////////////////////////////////////
0810   /// Generate a cosmic muon for the user-defined J
0811   ///////////////////////////////////////////////////////////////
0812   void GenerateFromCustomJ()
0813   {
0814     mAccepted = false;
0815 
0816     if (mMaxCustomJ[mGenMethod] < 0) ComputeMaximumCustomJ();
0817 
0818     // Sky or cylinder generation
0819     if (mGenMethod == Sky || mGenMethod == Cylinder)
0820     {
0821       // Generation of the momentum and theta angle
0822       while (!mAccepted)
0823       {
0824         mRandAccRej = mRandom.GenerateRandomDouble();
0825         mGenerationTheta = mRandom.GenerateRandomDouble(mMinimumTheta, mMaximumTheta);
0826         mGenerationMomentum = mRandom.GenerateRandomDouble(mMinimumMomentum, mMaximumMomentum);
0827 
0828         if (mGenMethod == Sky)
0829         {
0830           mJPrime = mJ(mGenerationMomentum, mGenerationTheta) * cos(mGenerationTheta) * sin(mGenerationTheta);
0831           if (mMaxCustomJ[mGenMethod] * mRandAccRej < mJPrime) mAccepted = true;
0832         }
0833 
0834         if (mGenMethod == Cylinder)
0835         {
0836           mJPrime = mJ(mGenerationMomentum, mGenerationTheta) * pow(sin(mGenerationTheta), 2) * cos(mGenerationPhi);
0837           if (mMaxCustomJ[mGenMethod] * mRandAccRej < mJPrime) mAccepted = true;
0838         }
0839       }
0840       mGenerationTheta = M_PI - mGenerationTheta;
0841 
0842       // Generation of the position and phi angle
0843       if (mGenMethod == Sky)
0844       {
0845         GeneratePositionSky();
0846         mGenerationPhi = mRandom.GenerateRandomDouble(mMinimumPhi, mMaximumPhi);
0847       }
0848       if (mGenMethod == Cylinder)
0849       {
0850         mAccepted = false;
0851         GeneratePositionCylinder();
0852         while (!mAccepted)
0853         {
0854           mRandAccRej = mRandom.GenerateRandomDouble();
0855           mGenerationPhi = mRandom.GenerateRandomDouble(mMinimumPhi, mMaximumPhi);
0856           if (mRandAccRej < fabs(cos(mGenerationPhi))) mAccepted = true;
0857         }
0858         mGenerationPhi = mGenerationPhi + mPhi0;
0859         if (mGenerationPhi >= 2. * M_PI) mGenerationPhi -= 2. * M_PI;
0860 
0861         // Check if the muon is inward
0862         if (sin(mGenerationTheta) * cos(mGenerationPhi) * mGenerationPosition[0] + sin(mGenerationTheta) * sin(mGenerationPhi) * mGenerationPosition[1] > 0) Generate();
0863       }
0864     }
0865 
0866     // Half-sphere generation
0867     if (mGenMethod == HSphere)
0868     {
0869       // Generation point on the half-sphere
0870       mPhi0 = mRandom.GenerateRandomDouble(mHSphereMinPositionPhi, mHSphereMaxPositionPhi);
0871       while (!mAccepted)
0872       {
0873         mRandAccRej = mRandom.GenerateRandomDouble();
0874         mTheta0 = acos(mRandom.GenerateRandomDouble(mHSphereCosMaxPositionTheta, mHSphereCosMinPositionTheta));
0875         mGenerationTheta = mRandom.GenerateRandomDouble(mMinimumTheta, mMaximumTheta);
0876         mGenerationPhi = mRandom.GenerateRandomDouble(mMinimumPhi, mMaximumPhi);
0877         mGenerationMomentum = mRandom.GenerateRandomDouble(mMinimumMomentum, mMaximumMomentum);
0878 
0879         mJPrime = mJ(mGenerationMomentum, mGenerationTheta) * (sin(mTheta0) * sin(mGenerationTheta) * cos(mGenerationPhi) + cos(mTheta0) * cos(mGenerationTheta)) * sin(mGenerationTheta);
0880         if (mMaxCustomJ[mGenMethod] * mRandAccRej < mJPrime) mAccepted = true;
0881       }
0882 
0883       mGenerationPosition[0] = mHSphereRadius * sin(mTheta0) * cos(mPhi0) + mHSphereCenterPosition[0];
0884       mGenerationPosition[1] = mHSphereRadius * sin(mTheta0) * sin(mPhi0) + mHSphereCenterPosition[1];
0885       mGenerationPosition[2] = mHSphereRadius * cos(mTheta0) + mHSphereCenterPosition[2];
0886 
0887       mGenerationTheta = M_PI - mGenerationTheta;
0888       mGenerationPhi = mGenerationPhi + mPhi0;
0889       if (mGenerationPhi >= 2 * M_PI) mGenerationPhi -= 2 * M_PI;
0890 
0891       mGenerationPhi += M_PI;
0892       if (mGenerationPhi >= 2 * M_PI) mGenerationPhi -= 2 * M_PI;
0893     }
0894 
0895     // Generate the charge
0896     if (mRandom.GenerateRandomDouble(-100, 128) >= 0)
0897       mCharge = 1;
0898     else
0899       mCharge = -1;
0900   };
0901   ///////////////////////////////////////////////////////////////
0902 };
0903 ///////////////////////////////////////////////////////////////
0904 ///////////////////////////////////////////////////////////////
0905 
0906 #endif