Geant4 Cross Reference |
1 // -*- C++ -*- 2 // 3 // ----------------------------------------------------------------------- 4 // HEP Random 5 // --- RandPoisson --- 6 // class implementation file 7 // ----------------------------------------------------------------------- 8 // This file is part of Geant4 (simulation toolkit for HEP). 9 10 // ======================================================================= 11 // Gabriele Cosmo - Created: 5th September 1995 12 // - Added not static Shoot() method: 17th May 1996 13 // - Algorithm now operates on doubles: 31st Oct 1996 14 // - Added methods to shoot arrays: 28th July 1997 15 // - Added check in case xm=-1: 4th February 1998 16 // J.Marraffino - Added default mean as attribute and 17 // operator() with mean: 16th Feb 1998 18 // Gabriele Cosmo - Relocated static data from HepRandom: 5th Jan 1999 19 // M Fischler - put and get to/from streams 12/15/04 20 // M Fischler - put/get to/from streams uses pairs of ulongs when 21 // + storing doubles avoid problems with precision 22 // 4/14/05 23 // Mark Fischler - Repaired BUG - when mean > 2 billion, was returning 24 // mean instead of the proper value. 01/13/06 25 // ======================================================================= 26 27 #include "CLHEP/Random/RandPoisson.h" 28 #include "CLHEP/Units/PhysicalConstants.h" 29 #include "CLHEP/Random/DoubConv.h" 30 #include <cmath> // for std::floor() 31 #include <iostream> 32 #include <string> 33 #include <vector> 34 35 namespace CLHEP { 36 37 std::string RandPoisson::name() const {return "RandPoisson";} 38 HepRandomEngine & RandPoisson::engine() {return *localEngine;} 39 40 // Initialisation of static data 41 CLHEP_THREAD_LOCAL double RandPoisson::status_st[3] = {0., 0., 0.}; 42 CLHEP_THREAD_LOCAL double RandPoisson::oldm_st = -1.0; 43 const double RandPoisson::meanMax_st = 2.0E9; 44 45 RandPoisson::~RandPoisson() { 46 } 47 48 double RandPoisson::operator()() { 49 return double(fire( defaultMean )); 50 } 51 52 double RandPoisson::operator()( double mean ) { 53 return double(fire( mean )); 54 } 55 56 double gammln(double xx) { 57 58 // Returns the value ln(Gamma(xx) for xx > 0. Full accuracy is obtained for 59 // xx > 1. For 0 < xx < 1. the reflection formula (6.1.4) can be used first. 60 // (Adapted from Numerical Recipes in C) 61 62 static const double cof[6] = {76.18009172947146,-86.50532032941677, 63 24.01409824083091, -1.231739572450155, 64 0.1208650973866179e-2, -0.5395239384953e-5}; 65 int j; 66 double x = xx - 1.0; 67 double tmp = x + 5.5; 68 tmp -= (x + 0.5) * std::log(tmp); 69 double ser = 1.000000000190015; 70 71 for ( j = 0; j <= 5; j++ ) { 72 x += 1.0; 73 ser += cof[j]/x; 74 } 75 return -tmp + std::log(2.5066282746310005*ser); 76 } 77 78 static 79 double normal (HepRandomEngine* eptr) // mf 1/13/06 80 { 81 double r; 82 double v1,v2,fac; 83 do { 84 v1 = 2.0 * eptr->flat() - 1.0; 85 v2 = 2.0 * eptr->flat() - 1.0; 86 r = v1*v1 + v2*v2; 87 } while ( r > 1.0 ); 88 89 fac = std::sqrt(-2.0*std::log(r)/r); 90 return v2*fac; 91 } 92 93 long RandPoisson::shoot(double xm) { 94 95 // Returns as a floating-point number an integer value that is a random 96 // deviation drawn from a Poisson distribution of mean xm, using flat() 97 // as a source of uniform random numbers. 98 // (Adapted from Numerical Recipes in C) 99 100 double em, t, y; 101 double sq, alxm, g1; 102 double om = getOldMean(); 103 HepRandomEngine* anEngine = HepRandom::getTheEngine(); 104 105 double* pstatus = getPStatus(); 106 sq = pstatus[0]; 107 alxm = pstatus[1]; 108 g1 = pstatus[2]; 109 110 if( xm == -1 ) return 0; 111 if( xm < 12.0 ) { 112 if( xm != om ) { 113 setOldMean(xm); 114 g1 = std::exp(-xm); 115 } 116 em = -1; 117 t = 1.0; 118 do { 119 em += 1.0; 120 t *= anEngine->flat(); 121 } while( t > g1 ); 122 } 123 else if ( xm < getMaxMean() ) { 124 if ( xm != om ) { 125 setOldMean(xm); 126 sq = std::sqrt(2.0*xm); 127 alxm = std::log(xm); 128 g1 = xm*alxm - gammln(xm + 1.0); 129 } 130 do { 131 do { 132 y = std::tan(CLHEP::pi*anEngine->flat()); 133 em = sq*y + xm; 134 } while( em < 0.0 ); 135 em = std::floor(em); 136 t = 0.9*(1.0 + y*y)* std::exp(em*alxm - gammln(em + 1.0) - g1); 137 } while( anEngine->flat() > t ); 138 } 139 else { 140 em = xm + std::sqrt(xm) * normal (anEngine); // mf 1/13/06 141 if ( static_cast<long>(em) < 0 ) 142 em = static_cast<long>(xm) >= 0 ? xm : getMaxMean(); 143 } 144 setPStatus(sq,alxm,g1); 145 return long(em); 146 } 147 148 void RandPoisson::shootArray(const int size, long* vect, double m1) 149 { 150 for( long* v = vect; v != vect + size; ++v ) 151 *v = shoot(m1); 152 } 153 154 long RandPoisson::shoot(HepRandomEngine* anEngine, double xm) { 155 156 // Returns as a floating-point number an integer value that is a random 157 // deviation drawn from a Poisson distribution of mean xm, using flat() 158 // of a given Random Engine as a source of uniform random numbers. 159 // (Adapted from Numerical Recipes in C) 160 161 double em, t, y; 162 double sq, alxm, g1; 163 double om = getOldMean(); 164 165 double* pstatus = getPStatus(); 166 sq = pstatus[0]; 167 alxm = pstatus[1]; 168 g1 = pstatus[2]; 169 170 if( xm == -1 ) return 0; 171 if( xm < 12.0 ) { 172 if( xm != om ) { 173 setOldMean(xm); 174 g1 = std::exp(-xm); 175 } 176 em = -1; 177 t = 1.0; 178 do { 179 em += 1.0; 180 t *= anEngine->flat(); 181 } while( t > g1 ); 182 } 183 else if ( xm < getMaxMean() ) { 184 if ( xm != om ) { 185 setOldMean(xm); 186 sq = std::sqrt(2.0*xm); 187 alxm = std::log(xm); 188 g1 = xm*alxm - gammln(xm + 1.0); 189 } 190 do { 191 do { 192 y = std::tan(CLHEP::pi*anEngine->flat()); 193 em = sq*y + xm; 194 } while( em < 0.0 ); 195 em = std::floor(em); 196 t = 0.9*(1.0 + y*y)* std::exp(em*alxm - gammln(em + 1.0) - g1); 197 } while( anEngine->flat() > t ); 198 } 199 else { 200 em = xm + std::sqrt(xm) * normal (anEngine); // mf 1/13/06 201 if ( static_cast<long>(em) < 0 ) 202 em = static_cast<long>(xm) >= 0 ? xm : getMaxMean(); 203 } 204 setPStatus(sq,alxm,g1); 205 return long(em); 206 } 207 208 void RandPoisson::shootArray(HepRandomEngine* anEngine, const int size, 209 long* vect, double m1) 210 { 211 for( long* v = vect; v != vect + size; ++v ) 212 *v = shoot(anEngine,m1); 213 } 214 215 long RandPoisson::fire() { 216 return long(fire( defaultMean )); 217 } 218 219 long RandPoisson::fire(double xm) { 220 221 // Returns as a floating-point number an integer value that is a random 222 // deviation drawn from a Poisson distribution of mean xm, using flat() 223 // as a source of uniform random numbers. 224 // (Adapted from Numerical Recipes in C) 225 226 double em, t, y; 227 double sq, alxm, g1; 228 229 sq = status[0]; 230 alxm = status[1]; 231 g1 = status[2]; 232 233 if( xm == -1 ) return 0; 234 if( xm < 12.0 ) { 235 if( xm != oldm ) { 236 oldm = xm; 237 g1 = std::exp(-xm); 238 } 239 em = -1; 240 t = 1.0; 241 do { 242 em += 1.0; 243 t *= localEngine->flat(); 244 } while( t > g1 ); 245 } 246 else if ( xm < meanMax ) { 247 if ( xm != oldm ) { 248 oldm = xm; 249 sq = std::sqrt(2.0*xm); 250 alxm = std::log(xm); 251 g1 = xm*alxm - gammln(xm + 1.0); 252 } 253 do { 254 do { 255 y = std::tan(CLHEP::pi*localEngine->flat()); 256 em = sq*y + xm; 257 } while( em < 0.0 ); 258 em = std::floor(em); 259 t = 0.9*(1.0 + y*y)* std::exp(em*alxm - gammln(em + 1.0) - g1); 260 } while( localEngine->flat() > t ); 261 } 262 else { 263 em = xm + std::sqrt(xm) * normal (localEngine.get()); // mf 1/13/06 264 if ( static_cast<long>(em) < 0 ) 265 em = static_cast<long>(xm) >= 0 ? xm : getMaxMean(); 266 } 267 status[0] = sq; status[1] = alxm; status[2] = g1; 268 return long(em); 269 } 270 271 void RandPoisson::fireArray(const int size, long* vect ) 272 { 273 for( long* v = vect; v != vect + size; ++v ) 274 *v = fire( defaultMean ); 275 } 276 277 void RandPoisson::fireArray(const int size, long* vect, double m1) 278 { 279 for( long* v = vect; v != vect + size; ++v ) 280 *v = fire( m1 ); 281 } 282 283 std::ostream & RandPoisson::put ( std::ostream & os ) const { 284 long pr=os.precision(20); 285 std::vector<unsigned long> t(2); 286 os << " " << name() << "\n"; 287 os << "Uvec" << "\n"; 288 t = DoubConv::dto2longs(meanMax); 289 os << meanMax << " " << t[0] << " " << t[1] << "\n"; 290 t = DoubConv::dto2longs(defaultMean); 291 os << defaultMean << " " << t[0] << " " << t[1] << "\n"; 292 t = DoubConv::dto2longs(status[0]); 293 os << status[0] << " " << t[0] << " " << t[1] << "\n"; 294 t = DoubConv::dto2longs(status[1]); 295 os << status[1] << " " << t[0] << " " << t[1] << "\n"; 296 t = DoubConv::dto2longs(status[2]); 297 os << status[2] << " " << t[0] << " " << t[1] << "\n"; 298 t = DoubConv::dto2longs(oldm); 299 os << oldm << " " << t[0] << " " << t[1] << "\n"; 300 os.precision(pr); 301 return os; 302 } 303 304 std::istream & RandPoisson::get ( std::istream & is ) { 305 std::string inName; 306 is >> inName; 307 if (inName != name()) { 308 is.clear(std::ios::badbit | is.rdstate()); 309 std::cerr << "Mismatch when expecting to read state of a " 310 << name() << " distribution\n" 311 << "Name found was " << inName 312 << "\nistream is left in the badbit state\n"; 313 return is; 314 } 315 if (possibleKeywordInput(is, "Uvec", meanMax)) { 316 std::vector<unsigned long> t(2); 317 is >> meanMax >> t[0] >> t[1]; meanMax = DoubConv::longs2double(t); 318 is >> defaultMean >> t[0] >> t[1]; defaultMean = DoubConv::longs2double(t); 319 is >> status[0] >> t[0] >> t[1]; status[0] = DoubConv::longs2double(t); 320 is >> status[1] >> t[0] >> t[1]; status[1] = DoubConv::longs2double(t); 321 is >> status[2] >> t[0] >> t[1]; status[2] = DoubConv::longs2double(t); 322 is >> oldm >> t[0] >> t[1]; oldm = DoubConv::longs2double(t); 323 return is; 324 } 325 // is >> meanMax encompassed by possibleKeywordInput 326 is >> defaultMean >> status[0] >> status[1] >> status[2]; 327 return is; 328 } 329 330 } // namespace CLHEP 331 332