From d92e3a5e55fa430c0b2ada9ae10baa48b936721d Mon Sep 17 00:00:00 2001 From: Andre Sailer <andre.philippe.sailer@cern.ch> Date: Fri, 1 Jul 2016 14:52:48 +0000 Subject: [PATCH] Add plugin to skip events and still stay reproducible Use same algorithm as used in Marlin EventSeeder --- DDG4/include/DDG4/Geant4EventSeed.h | 205 ++++++++++++++++++++++++++++ DDG4/plugins/Geant4EventSeed.cpp | 89 ++++++++++++ doc/doxygen/DD4hepGroups.h | 3 + 3 files changed, 297 insertions(+) create mode 100644 DDG4/include/DDG4/Geant4EventSeed.h create mode 100644 DDG4/plugins/Geant4EventSeed.cpp diff --git a/DDG4/include/DDG4/Geant4EventSeed.h b/DDG4/include/DDG4/Geant4EventSeed.h new file mode 100644 index 000000000..4c4c70475 --- /dev/null +++ b/DDG4/include/DDG4/Geant4EventSeed.h @@ -0,0 +1,205 @@ +// $Id: $ +//========================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------------- +// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN) +// All rights reserved. +// +// For the licensing terms see $DD4hepINSTALL/LICENSE. +// For the list of contributors see $DD4hepINSTALL/doc/CREDITS. +// +// Author : A.Sailer +// +//========================================================================== +#ifndef DD4HEP_DDG4_GEANT4EVENTSEED_H +#define DD4HEP_DDG4_GEANT4EVENTSEED_H + +// Framework include files +#include "DDG4/Geant4RunAction.h" + +/// Namespace for the AIDA detector description toolkit +namespace DD4hep { + + /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit + namespace Simulation { + + /** + * \addtogroup Geant4RunActions + * @{ + * \package EventSeed + * \brief Set the event seed for each event + * + * This plugins allows one to skip events and still be reproducible. + * We are using jenkins_hash from an intial seed, runID and eventID + * see http://burtleburtle.net/bob/hash/evahash.html + * + * \author A.Sailer + * \version 1.0 + * @} + */ + class Geant4EventSeed: public Geant4RunAction { + + protected: + unsigned int m_initialSeed; + unsigned int m_runID; + std::string m_type; + bool m_initialised; + public: + /// Standard constructor with initializing arguments + Geant4EventSeed(Geant4Context*, const std::string& ); + /// Default destructor + virtual ~Geant4EventSeed(); + /// begin-of-run callback + void begin(const G4Run*); + /// begin-of-event callback + void beginEvent(const G4Event*); + }; + + /* + + Hashing for random seed as used in Marlin EventSeeder processor + + Original source by Bob Jenkins + + http://www.burtleburtle.net/bob/hash/doobs.html + + Hash a variable-length key into a 32-bit value + + */ + +#define hashsize(n) ( 1U << (n) ) +#define hashmask(n) ( hashsize ( n ) - 1 ) + + + /* + -------------------------------------------------------------------- + mix -- mix 3 32-bit values reversibly. + For every delta with one or two bits set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, + * If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. + * If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) + mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. + -------------------------------------------------------------------- + */ +#define mix(a,b,c) \ + { \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ + } + + /* + -------------------------------------------------------------------- + jenkins_hash() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + initval : can be any 4-byte value + Returns a 32-bit value. Every bit of the key affects every bit of + the return value. Every 1-bit and 2-bit delta achieves avalanche. + About 6*len+35 instructions. + + The best hash table sizes are powers of 2. There is no need to do + mod a prime (mod is sooo slow!). If you need less than 32 bits, + use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); + In which case, the hash table should have hashsize(10) elements. + + If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h); + + By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this + code any way you wish, private, educational, or commercial. It's free. + + See http://burtleburtle.net/bob/hash/evahash.html + Use for hash table lookup, or anything where one collision in 2^^32 is + acceptable. Do NOT use for cryptographic purposes. + -------------------------------------------------------------------- + */ + unsigned jenkins_hash ( unsigned char *k, unsigned length, unsigned initval ) + { + unsigned a, b; + unsigned c = initval; + unsigned len = length; + + a = b = 0x9e3779b9; + + while ( len >= 12 ) { + a += ( k[0] + ( (unsigned)k[1] << 8 ) + + ( (unsigned)k[2] << 16 ) + + ( (unsigned)k[3] << 24 ) ); + b += ( k[4] + ( (unsigned)k[5] << 8 ) + + ( (unsigned)k[6] << 16 ) + + ( (unsigned)k[7] << 24 ) ); + c += ( k[8] + ( (unsigned)k[9] << 8 ) + + ( (unsigned)k[10] << 16 ) + + ( (unsigned)k[11] << 24 ) ); + + mix ( a, b, c ); + + k += 12; + len -= 12; + } + + c += length; + + switch ( len ) { + case 11: c += ( (unsigned)k[10] << 24 ); + case 10: c += ( (unsigned)k[9] << 16 ); + case 9 : c += ( (unsigned)k[8] << 8 ); + /* First byte of c reserved for length */ + case 8 : b += ( (unsigned)k[7] << 24 ); + case 7 : b += ( (unsigned)k[6] << 16 ); + case 6 : b += ( (unsigned)k[5] << 8 ); + case 5 : b += k[4]; + case 4 : a += ( (unsigned)k[3] << 24 ); + case 3 : a += ( (unsigned)k[2] << 16 ); + case 2 : a += ( (unsigned)k[1] << 8 ); + case 1 : a += k[0]; + } + + mix ( a, b, c ); + + return c; + } + + /// calculate hash from initialSeed, eventID and runID + unsigned int hash( unsigned int initialSeed, unsigned int eventNumber, unsigned int runNumber ){ + unsigned int seed = 0; + unsigned char * c = (unsigned char *) &eventNumber ; + seed = jenkins_hash( c, sizeof eventNumber, seed) ; + + c = (unsigned char *) &runNumber ; + seed = jenkins_hash( c, sizeof runNumber, seed) ; + + c = (unsigned char *) &initialSeed ; + seed = jenkins_hash( c, sizeof initialSeed, seed) ; + + return seed; + } + + } // End namespace Simulation +} // End namespace DD4hep + +#endif // DD4HEP_DDG4_GEANT4EVENTSEED_H diff --git a/DDG4/plugins/Geant4EventSeed.cpp b/DDG4/plugins/Geant4EventSeed.cpp new file mode 100644 index 000000000..ccc9cd5a9 --- /dev/null +++ b/DDG4/plugins/Geant4EventSeed.cpp @@ -0,0 +1,89 @@ +// $Id: $ +//========================================================================== +// AIDA Detector description implementation for LCD +//-------------------------------------------------------------------------- +// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN) +// All rights reserved. +// +// For the licensing terms see $DD4hepINSTALL/LICENSE. +// For the list of contributors see $DD4hepINSTALL/doc/CREDITS. +// +// Author : A.Sailer +// +//========================================================================= + +//Class include file +#include "DDG4/Geant4EventSeed.h" + +// Framework include files +#include "DD4hep/InstanceCount.h" +#include "DD4hep/Printout.h" + +#include "DDG4/Geant4EventAction.h" +#include "DDG4/Geant4Random.h" +#include "DDG4/Factories.h" + +#include "CLHEP/Random/EngineFactory.h" + +//Geant includes +#include <G4Run.hh> +#include <G4Event.hh> + +using namespace DD4hep::Simulation; + +/// Standard constructor +Geant4EventSeed::Geant4EventSeed(Geant4Context* c, const std::string& typ) : Geant4RunAction(c, typ), + m_initialSeed(0), + m_runID(0), + m_type(typ), + m_initialised(false) +{ + Geant4Action::runAction().callAtBegin(this,&Geant4EventSeed::begin); + Geant4Action::eventAction().callAtBegin(this,&Geant4EventSeed::beginEvent); + InstanceCount::increment(this); +} + +/// Default destructor +Geant4EventSeed::~Geant4EventSeed() { + InstanceCount::decrement(this); +} + +/// begin-of-run callback +void Geant4EventSeed::begin(const G4Run* run) { + + if(not m_initialised){ + m_initialised = true; + m_initialSeed = Geant4Random::instance()->engine()->getSeed(); + } + + m_runID = run->GetRunID(); + + DD4hep::printout( PrintLevel::INFO, m_type, + "Get RunID: runID=%u\n", + m_runID ); + +} + +/// begin-of-event callback +void Geant4EventSeed::beginEvent(const G4Event* evt) { + + Geant4Random *rndm = Geant4Random::instance(); + + unsigned int eventID = evt->GetEventID(); + unsigned int newSeed = hash( m_initialSeed, eventID, m_runID ); + + DD4hep::printout( PrintLevel::INFO, m_type, + "At beginEvent: eventID=%u, runID=%u" \ + " initialSeed=%u, newSeed=%u" , + evt->GetEventID(), m_runID, + m_initialSeed, newSeed ); + + rndm->setSeed( newSeed ); + + if ( DD4hep::printLevel() <= PrintLevel::DEBUG ) { + rndm->showStatus(); + } + +} + +DECLARE_GEANT4ACTION(Geant4EventSeed) diff --git a/doc/doxygen/DD4hepGroups.h b/doc/doxygen/DD4hepGroups.h index 939a97110..a95bb5272 100644 --- a/doc/doxygen/DD4hepGroups.h +++ b/doc/doxygen/DD4hepGroups.h @@ -93,6 +93,9 @@ namespace UTIL {} * \defgroup Geant4ActionPlugin \brief Plugins that are a Geant4Actions + * \defgroup Geant4RunActions + \brief Plugins that are a Geant4RunActions + * \defgroup SurfacePlugin \brief Plugins to manipulate surfaces automatically -- GitLab