Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:14:26

0001 //==========================================================================
0002 //  AIDA Detector description implementation 
0003 //--------------------------------------------------------------------------
0004 // Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
0005 // All rights reserved.
0006 //
0007 // For the licensing terms see $DD4hepINSTALL/LICENSE.
0008 // For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
0009 //
0010 // Author     : M.Frank
0011 //
0012 //==========================================================================
0013 
0014 // Framework include files
0015 #include <DD4hep/Printout.h>
0016 #include <DDG4/Geant4InputHandling.h>
0017 #include <DDG4/Geant4Primary.h>
0018 #include <DDG4/Geant4Context.h>
0019 #include <DDG4/Geant4Action.h>
0020 #include <DDG4/Geant4PrimaryHandler.h>
0021 #include <CLHEP/Units/SystemOfUnits.h>
0022 #include <CLHEP/Units/PhysicalConstants.h>
0023 
0024 // Geant4 include files
0025 #include <G4Event.hh>
0026 #include <G4PrimaryVertex.hh>
0027 #include <G4PrimaryParticle.hh>
0028 #include <G4ParticleDefinition.hh>
0029 
0030 // C/C++ include files
0031 #include <stdexcept>
0032 #include <limits>
0033 #include <cmath>
0034 
0035 using namespace dd4hep::sim;
0036 using PropertyMask = dd4hep::detail::ReferenceBitMask<int>;
0037 
0038 /// Create a vertex object from the Geant4 primary vertex
0039 Geant4Vertex* dd4hep::sim::createPrimary(const G4PrimaryVertex* g4)      {        
0040   Geant4Vertex* v = new Geant4Vertex();
0041   v->x = g4->GetX0();
0042   v->y = g4->GetY0();
0043   v->z = g4->GetZ0();
0044   v->time = g4->GetT0();
0045   return v;
0046 }
0047 
0048 /// Create a particle object from the Geant4 primary particle
0049 Geant4Particle* 
0050 dd4hep::sim::createPrimary(int particle_id, 
0051                            const Geant4Vertex* v,
0052                            const G4PrimaryParticle* g4p)
0053 {
0054   Geant4ParticleHandle p = new Geant4Particle();
0055   p->id           = particle_id;
0056   p->reason       = 0;
0057   p->pdgID        = g4p->GetPDGcode();
0058   p->psx          = g4p->GetPx();
0059   p->psy          = g4p->GetPy();
0060   p->psz          = g4p->GetPz();
0061   p->time         = g4p->GetProperTime();
0062   p->properTime   = g4p->GetProperTime();
0063   p->vsx          = v->x;
0064   p->vsy          = v->y;
0065   p->vsz          = v->z;
0066   p->vex          = v->x;
0067   p->vey          = v->y;
0068   p->vez          = v->z;
0069   //p->definition   = g4p->GetG4code();
0070   //p->process      = 0;
0071   p->spin[0]      = 0;
0072   p->spin[1]      = 0;
0073   p->spin[2]      = 0;
0074   p->colorFlow[0] = 0;
0075   p->colorFlow[0] = 0;
0076   p->mass         = g4p->GetMass();
0077   p->charge       = int(3.0 * g4p->GetCharge());
0078   PropertyMask status(p->status);
0079   status.set(G4PARTICLE_GEN_STABLE);
0080   return p;
0081 }
0082 
0083 /// Helper to recursively build a DDG4 interaction from an existing G4 interaction (primary vertex)
0084 static void collectPrimaries(Geant4PrimaryMap*         pm,
0085                              Geant4PrimaryInteraction* interaction, 
0086                              Geant4Vertex*             particle_origine,
0087                              G4PrimaryParticle*        gp)
0088 {
0089   //if the particle is in the map, we do not have to do anything
0090   if ( pm->get(gp) )  {
0091     return;
0092   }
0093 
0094   int pid = int(interaction->particles.size());
0095   Geant4Particle* p = createPrimary(pid,particle_origine,gp);
0096   G4PrimaryParticle* dau = gp->GetDaughter();
0097   PropertyMask status(p->status);
0098   int mask = interaction->mask;
0099   
0100   interaction->particles.emplace(p->id,p);
0101   status.set(G4PARTICLE_PRIMARY);
0102   p->mask = mask;
0103   particle_origine->out.insert(p->id);
0104   // Insert pair in map. Do not forget to increase reference count!
0105   pm->insert(gp,p);
0106 
0107   if ( dau )   {
0108     Geant4Vertex* dv = new Geant4Vertex(*particle_origine);
0109     PropertyMask reason(p->reason);
0110     reason.set(G4PARTICLE_HAS_SECONDARIES);
0111 
0112     dv->mask = mask;
0113     dv->in.insert(p->id);
0114 
0115     interaction->vertices[mask].emplace_back(dv) ;
0116 
0117     for(; dau; dau = dau->GetNext())
0118       collectPrimaries(pm, interaction, dv, dau);
0119   }
0120 }
0121 
0122 /// Import a Geant4 interaction defined by a primary vertex into a DDG4 interaction record
0123 Geant4PrimaryInteraction* 
0124 dd4hep::sim::createPrimary(int mask,
0125                            Geant4PrimaryMap* pm,
0126                            std::set<G4PrimaryVertex*>const& primaries)
0127 {
0128   Geant4PrimaryInteraction* interaction = new Geant4PrimaryInteraction();
0129   interaction->locked = true;
0130   interaction->mask = mask;
0131   for (auto const& gv: primaries) {
0132     Geant4Vertex* v = createPrimary(gv);
0133     v->mask = mask;
0134     interaction->vertices[mask].emplace_back(v);
0135     for (G4PrimaryParticle *gp = gv->GetPrimary(); gp; gp = gp->GetNext()) {
0136       collectPrimaries(pm, interaction, v, gp);
0137     }
0138   }
0139   return interaction;
0140 }
0141 
0142 /// Initialize the generation of one event
0143 int dd4hep::sim::generationInitialization(const Geant4Action* /* caller */,
0144                                           const Geant4Context* context)
0145 {
0146   /**
0147    *  This action should register all event extension required for the further
0148    *  processing. We want to avoid that every client has to check if a given
0149    *  object is present or not and then later install the required data structures.
0150    */
0151   context->event().addExtension(new Geant4PrimaryMap());
0152 
0153   // The final set of created particles in the simulation.
0154   context->event().addExtension(new Geant4ParticleMap());
0155 
0156   //
0157   // The Geant4PrimaryEvent extension contains a whole set of
0158   // Geant4PrimaryInteraction objects each may represent a complete
0159   // interaction. Particles and vertices may be unbiased.
0160   // This is the input to the translator forming the final
0161   // Geant4PrimaryInteraction (see below) containing rebiased particle
0162   // and vertex maps.
0163   Geant4PrimaryEvent* evt = new Geant4PrimaryEvent();
0164   context->event().addExtension(evt);
0165   //
0166   // The Geant4PrimaryInteraction extension contains the final
0167   // vertices and particles ready to be injected to Geant4.
0168   Geant4PrimaryInteraction* inter = new Geant4PrimaryInteraction();
0169   inter->setNextPID(-1);
0170   context->event().addExtension(inter);
0171   return 1;
0172 }
0173 
0174 /// Append input interaction to global output
0175 static void appendInteraction(const Geant4Action* caller,
0176                               Geant4PrimaryInteraction* output,
0177                               Geant4PrimaryInteraction* input)
0178 {
0179   Geant4PrimaryInteraction::ParticleMap::iterator ip, ipend;
0180   for( ip=input->particles.begin(), ipend=input->particles.end(); ip != ipend; ++ip )  {
0181     Geant4Particle* p = (*ip).second;
0182     output->particles.emplace(p->id,p->addRef());
0183   }
0184   Geant4PrimaryInteraction::VertexMap::iterator ivfnd, iv, ivend;
0185   for( iv=input->vertices.begin(), ivend=input->vertices.end(); iv != ivend; ++iv )   {
0186     int theMask = input->mask;
0187     ivfnd = output->vertices.find(theMask);
0188     if ( ivfnd != output->vertices.end() )   {
0189       caller->abortRun("Duplicate primary interaction identifier!",
0190                        "Cannot handle 2 interactions with identical identifiers!");
0191     }
0192     for(Geant4Vertex* vtx :  (*iv).second )
0193       output->vertices[theMask].emplace_back( vtx->addRef() );
0194   }
0195 }
0196 
0197 static void rebaseParticles(Geant4PrimaryInteraction::ParticleMap& particles, int &offset)    {
0198   Geant4PrimaryInteraction::ParticleMap::iterator ip, ipend;
0199   int mx_id = offset;
0200   // Now move begin and end-vertex of all primary and generator particles accordingly
0201   for( ip=particles.begin(), ipend=particles.end(); ip != ipend; ++ip )  {
0202     Geant4ParticleHandle p((*ip).second);
0203     p.offset(offset);
0204     mx_id = p->id+1 > mx_id ? p->id+1 : mx_id;
0205   }
0206   offset = mx_id;
0207 }
0208 
0209 static void rebaseVertices(Geant4PrimaryInteraction::VertexMap& vertices, int part_offset)    {
0210   Geant4PrimaryInteraction::VertexMap::iterator iv, ivend;
0211   std::set<int> in, out;
0212   std::set<int>::iterator i;
0213   // Now move begin and end-vertex of all primary vertices accordingly
0214   for(iv=vertices.begin(), ivend=vertices.end(); iv != ivend; ++iv)  {
0215     for( Geant4Vertex* v : (*iv).second ){ 
0216       in  = v->in;
0217       out = v->out;
0218       for(in=v->in, i=in.begin(), v->in.clear(); i != in.end(); ++i)
0219         v->in.insert((*i)+part_offset);
0220       for(out=v->out, i=out.begin(), v->out.clear(); i != out.end(); ++i)
0221         v->out.insert((*i)+part_offset);
0222     }
0223   }
0224 }
0225 /// Merge all interactions present in the context
0226 int dd4hep::sim::mergeInteractions(const Geant4Action* caller,
0227                                    const Geant4Context* context)
0228 {
0229   typedef Geant4PrimaryEvent::Interaction  Interaction;
0230   typedef std::vector<Interaction*>        Interactions;
0231   Geant4Event& event = context->event();
0232   Geant4PrimaryEvent* evt = event.extension<Geant4PrimaryEvent>();
0233   Interaction* output = event.extension<Interaction>();
0234   Interactions inter  = evt->interactions();
0235   int particle_offset = 0;
0236 
0237   for(Interactions::const_iterator i=inter.begin(); i != inter.end(); ++i)  {
0238     Interaction* interaction = *i;
0239     int vertex_offset = particle_offset;
0240     if ( !interaction->applyMask() )   {
0241       caller->abortRun("Found single interaction with multiple primary vertices!",
0242                        "Cannot merge individual interactions with more than one primary!");
0243     }
0244     rebaseParticles(interaction->particles,particle_offset);
0245     rebaseVertices(interaction->vertices,vertex_offset);
0246     appendInteraction(caller,output,interaction);
0247   }
0248   output->setNextPID(particle_offset);
0249   Geant4PrimaryInteraction::ParticleMap::iterator ip, ipend;
0250   caller->debug("+++ Merging MC input record from %d interactions:",(int)inter.size());
0251   for( ip=output->particles.begin(), ipend=output->particles.end(); ip != ipend; ++ip )
0252     Geant4ParticleHandle((*ip).second).dump1(DEBUG,caller->name(),"Merged particles");
0253   return 1;
0254 }
0255 
0256 /// Boost particles of one interaction identified by its mask
0257 int dd4hep::sim::boostInteraction(const Geant4Action* caller,
0258                                   Geant4PrimaryEvent::Interaction* inter,
0259                                   double alpha)
0260 {
0261 #define SQR(x)  (x*x)
0262   Geant4PrimaryEvent::Interaction::VertexMap::iterator iv;
0263   Geant4PrimaryEvent::Interaction::ParticleMap::iterator ip;
0264   double gamma = std::sqrt(1 + SQR(tan(alpha)));
0265   double betagamma = std::tan(alpha);
0266 
0267   if ( inter->locked )  {
0268     caller->abortRun("Locked interactions may not be boosted!",
0269                      "Cannot boost interactions with a native G4 primary record!");
0270   }
0271   else if ( alpha != 0.0 )  {
0272     // Now move begin and end-vertex of all primary vertices accordingly
0273     for(iv=inter->vertices.begin(); iv != inter->vertices.end(); ++iv){  
0274       for( Geant4Vertex* v :  (*iv).second ) {
0275         double t = gamma * v->time + betagamma * v->x / CLHEP::c_light;
0276         double x = gamma * v->x + betagamma * CLHEP::c_light * v->time;
0277         double y = v->y;
0278         double z = v->z;
0279         v->x = x;
0280         v->y = y;
0281         v->z = z;
0282         v->time = t;
0283       }
0284     }
0285     // Now move begin and end-vertex of all primary and generator particles accordingly
0286     for(ip=inter->particles.begin(); ip != inter->particles.end(); ++ip)  {
0287       Geant4ParticleHandle p = (*ip).second;
0288       double t = gamma * p->time + betagamma * p->vsx / CLHEP::c_light;
0289       double x = gamma * p->vsx + betagamma * CLHEP::c_light * p->time;
0290       double y = p->vsy;
0291       double z = p->vsz;
0292       
0293       double m  = p->mass;
0294       double e2 = SQR(p->psx)+SQR(p->psy)+SQR(p->psz)+SQR(m);
0295       double px = betagamma * std::sqrt(e2) + gamma * p->psx;
0296       double py = p->psy;
0297       double pz = p->psz;
0298       
0299       p->vsx = x;
0300       p->vsy = y;
0301       p->vsz = z;
0302       p->time = t;
0303       
0304       p->psx = px;
0305       p->psy = py;
0306       p->psz = pz;
0307     }
0308   }
0309   return 1;
0310 }
0311 
0312 /// Smear the primary vertex of an interaction
0313 int dd4hep::sim::smearInteraction(const Geant4Action* caller,
0314                                   Geant4PrimaryEvent::Interaction* inter,
0315                                   double dx, double dy, double dz, double dt)
0316 {
0317   Geant4PrimaryEvent::Interaction::VertexMap::iterator iv;
0318   Geant4PrimaryEvent::Interaction::ParticleMap::iterator ip;
0319 
0320   if ( inter->locked )  {
0321     caller->abortRun("Locked interactions may not be smeared!",
0322                      "Cannot smear interactions with a native G4 primary record!");
0323   }
0324   
0325   // Now move begin and end-vertex of all primary vertices accordingly
0326   for(iv=inter->vertices.begin(); iv != inter->vertices.end(); ++iv)  {
0327     for( Geant4Vertex* v : (*iv).second ){
0328       v->x += dx;
0329       v->y += dy;
0330       v->z += dz;
0331       v->time += dt;
0332     }
0333   }
0334   // Now move begin and end-vertex of all primary and generator particles accordingly
0335   for(ip=inter->particles.begin(); ip != inter->particles.end(); ++ip)  {
0336     Geant4Particle* p = (*ip).second;
0337     p->vsx += dx;
0338     p->vsy += dy;
0339     p->vsz += dz;
0340     p->vex += dx;
0341     p->vey += dy;
0342     p->vez += dz;
0343     p->time += dt;
0344   }
0345   return 1;
0346 }
0347 
0348 static G4PrimaryParticle* createG4Primary(const Geant4ParticleHandle p)  {
0349   G4PrimaryParticle* g4 = 0;
0350   const G4ParticleDefinition* def = p.definition();
0351   /// First check against particle masses, which may be unequal to the generator particle mass.
0352   double energy = p.energy();
0353   double mom2   = (p->psx*p->psx) + (p->psy*p->psy) + (p->psz*p->psz);
0354   double mass2  = energy*energy - mom2;
0355   if ( mass2 < 0e0 )   {
0356     if ( def )   {
0357       mass2 = def->GetPDGMass() * def->GetPDGMass();
0358     }
0359     energy = std::sqrt(mom2 + mass2);
0360     if ( std::fabs(p.energy()-energy) > 0e0 /* 1e-10 */ )  {
0361       dd4hep::printout(dd4hep::INFO,"createG4Primary",
0362                        "Change particle %s energy from %10.5f MeV by %g ppm to avoid negative Energy^2",
0363                        (def) ? def->GetParticleName().c_str() : "???", p.energy(), std::fabs(p.energy()-energy)*1e6);
0364     }
0365   }
0366   if ( 0 != p->pdgID )   {
0367     // For ions we use the pdgID of the definition, in case we had to zero the excitation level, see Geant4Particle.cpp
0368     const int pdgID =  p->pdgID < 1000000000 ? p->pdgID : p.definition()->GetPDGEncoding();
0369     g4 = new G4PrimaryParticle(pdgID, p->psx, p->psy, p->psz, energy);
0370   }
0371   else   {
0372     g4 = new G4PrimaryParticle(def, p->psx, p->psy, p->psz, energy);
0373     g4->SetCharge(double(p.charge())/3.0);
0374   }
0375   // The particle is fully defined with the 4-vector set above, setting the mass isn't necessary, not
0376   // using the 4-vector, means the PDG mass is used, and the momentum is scaled if the mass is set here
0377   // g4->SetMass(p->mass);
0378   return g4;
0379 }
0380 
0381 static std::vector< std::pair<Geant4Particle*,G4PrimaryParticle*> >
0382 getRelevant(std::set<int>& visited,
0383             std::map<int,G4PrimaryParticle*>& prim,
0384             Geant4PrimaryInteraction::ParticleMap& pm,
0385             const Geant4PrimaryConfig& primaryConfig,
0386             const Geant4ParticleHandle p)
0387 {
0388   typedef std::vector< std::pair<Geant4Particle*,G4PrimaryParticle*> > Primaries;
0389   using dd4hep::printout;
0390   
0391   Primaries res;
0392   visited.insert(p->id);
0393   PropertyMask status(p->status);
0394   if ( status.isSet(G4PARTICLE_GEN_STABLE) )  {
0395     bool rejectParticle = false
0396       or (primaryConfig.m_rejectPDGs.count(abs(p->pdgID)) != 0) // quarks, gluon, "strings", W, Z etc.
0397       ;
0398     printout(dd4hep::DEBUG, "Input",
0399              "Checking rejection of stable: PDG(%-10d), Definition(%s), reject(%s)",
0400              p->pdgID,
0401              p.definition() ? "true" : "false",
0402              rejectParticle ? "true" : "false");
0403     if (not rejectParticle and prim.find(p->id) == prim.end() )  {
0404       G4PrimaryParticle* p4 = createG4Primary(p);
0405       prim[p->id] = p4;
0406       res.emplace_back(p,p4);
0407     }
0408   }
0409   else if ( p->daughters.size() > 0 )  {
0410     const Geant4Particle::Particles& dau = p->daughters;
0411     int first_daughter = *(dau.begin());
0412     Geant4ParticleHandle dp = pm[first_daughter];
0413     double en = p.energy();
0414     double me = en > std::numeric_limits<double>::epsilon() ? p->mass / en : 0.0;
0415     //  fix by S.Morozov for real != 0
0416     double proper_time = fabs(dp->time-p->time) * me;
0417     double proper_time_Precision = pow(10.,-DBL_DIG)*fabs(me)*fmax(fabs(p->time),fabs(dp->time));
0418     bool isProperTimeZero = (fabs(proper_time) <= fabs(proper_time_Precision));
0419 
0420     // -- remove original if ---
0421     bool rejectParticle = not p.definition()                    // completely unknown to geant4
0422       or (primaryConfig.m_rejectPDGs.count(abs(p->pdgID)) != 0) // quarks, gluon, "strings", W, Z etc.
0423       or (isProperTimeZero and p.definition()->GetPDGStable() ) // initial state electrons, etc.
0424       or (isProperTimeZero and primaryConfig.m_zeroTimePDGs.count(abs(p->pdgID)) != 0 )  // charged 'documentation' leptons, e.g. in lepton pairs w/ FSR
0425       or (status.isSet(G4PARTICLE_GEN_DOCUMENTATION) || status.isSet(G4PARTICLE_GEN_BEAM) || status.isSet(G4PARTICLE_GEN_OTHER))  // documentation generator status
0426       or false;
0427 
0428     printout(dd4hep::DEBUG, "Input",
0429              "Checking rejection: PDG(%-10d), Definition(%s), isProperTimeZero(%s, %3.15f), stable(%s), doc(%s), reject(%s)",
0430              p->pdgID,
0431              p.definition() ? "true" : "false",
0432              isProperTimeZero ? "true" : "false", proper_time,
0433              (bool(p.definition()) ? p.definition()->GetPDGStable() : false)  ? "true" : "false",
0434              status.isSet(G4PARTICLE_GEN_DOCUMENTATION) || status.isSet(G4PARTICLE_GEN_BEAM) || status.isSet(G4PARTICLE_GEN_OTHER) ? "true" : "false",
0435              rejectParticle ? "true" : "false");
0436     // end running simulation if we have a really inconsistent record, that is unrejected stable particle with children
0437     bool failStableWithChildren = (not rejectParticle and p.definition()->GetPDGStable());
0438     if (failStableWithChildren) {
0439       printout(dd4hep::FATAL,"Input",
0440                "+++ Stable particle (PDG: %-10d) with daughters! check your MC record, adapt particle.tbl file...",
0441                p->pdgID);
0442       throw std::runtime_error("Cannot Simmulate this MC Record");
0443     }
0444     if (not rejectParticle) {
0445       std::map<int, G4PrimaryParticle*>::iterator ip4 = prim.find(p->id);
0446       G4PrimaryParticle* p4 = (ip4 == prim.end()) ? 0 : (*ip4).second;
0447       if ( !p4 )  {
0448         p4 = createG4Primary(p);
0449         p4->SetProperTime(proper_time);
0450         prim[p->id] = p4;
0451         Primaries daughters;
0452         for(Geant4Particle::Particles::const_iterator i=dau.begin(); i!=dau.end(); ++i)  {
0453           if ( visited.find(*i) == visited.end() )  {
0454             Primaries tmp = getRelevant(visited,prim,pm,primaryConfig,pm[*i]);
0455             daughters.insert(daughters.end(), tmp.begin(),tmp.end());
0456           }
0457         }
0458         for(Primaries::iterator i=daughters.begin(); i!=daughters.end(); ++i)
0459           p4->SetDaughter((*i).second);
0460       }
0461       res.emplace_back(p,p4);
0462     }
0463     else  {
0464       for(Geant4Particle::Particles::const_iterator i=dau.begin(); i!=dau.end(); ++i)  {
0465         if ( visited.find(*i) == visited.end() )  {
0466           Primaries tmp = getRelevant(visited,prim,pm,primaryConfig,pm[*i]);
0467           res.insert(res.end(), tmp.begin(),tmp.end());
0468         }
0469       }
0470     }
0471   }
0472   return res;
0473 }
0474 
0475 /// Generate all primary vertices corresponding to the merged interaction
0476 int dd4hep::sim::generatePrimaries(const Geant4Action* caller,
0477                                    const Geant4Context* context,
0478                                    G4Event* event)
0479 {
0480   typedef std::vector< std::pair<Geant4Particle*,G4PrimaryParticle*> > Primaries;
0481   typedef Geant4PrimaryInteraction Interaction;
0482   Geant4PrimaryMap* primaries   = context->event().extension<Geant4PrimaryMap>();
0483   Interaction*      interaction = context->event().extension<Interaction>();
0484   Interaction::ParticleMap& pm  = interaction->particles;
0485   Interaction::VertexMap&   vm  = interaction->vertices;
0486   std::map<int,G4PrimaryParticle*> prim;
0487   std::set<int> visited;
0488 
0489   auto const* primHandler = dynamic_cast<const Geant4PrimaryHandler*>(caller);
0490   auto const& primaryConfig = primHandler ? primHandler->m_primaryConfig : Geant4PrimaryConfig();
0491 
0492   caller->debug("PrimaryConfiguration:%s", primaryConfig.toString().c_str());
0493 
0494   if ( interaction->locked )  {
0495     caller->abortRun("Locked interactions may not be used to generate primaries!",
0496                      "Cannot handle a native G4 primary record!");
0497     return 0;
0498   }
0499   else   {
0500     Geant4PrimaryInteraction::VertexMap::iterator ivfnd, iv, ivend;
0501     for(Interaction::VertexMap::const_iterator iend=vm.end(),i=vm.begin(); i!=iend; ++i)  {
0502       for( Geant4Vertex* v : (*i).second ){
0503 
0504         int num_part = 0;
0505         G4PrimaryVertex* v4 = new G4PrimaryVertex(v->x,v->y,v->z,v->time);
0506         event->AddPrimaryVertex(v4);
0507         caller->print("+++++ G4PrimaryVertex at (%+.2e,%+.2e,%+.2e) [mm] %+.2e [ns]",
0508                       v->x/CLHEP::mm,v->y/CLHEP::mm,v->z/CLHEP::mm,v->time/CLHEP::ns);
0509         for(Geant4Vertex::Particles::const_iterator ip=v->out.begin(); ip!=v->out.end(); ++ip)  {
0510           Geant4ParticleHandle p = pm[*ip];
0511           if ( p->daughters.size() > 0 )  {
0512             PropertyMask mask(p->reason);
0513             mask.set(G4PARTICLE_HAS_SECONDARIES);
0514           }
0515           if ( p->parents.size() == 0 )  {
0516             Primaries relevant = getRelevant(visited,prim,pm,primaryConfig,p);
0517             for(Primaries::const_iterator j=relevant.begin(); j!= relevant.end(); ++j)  {
0518               Geant4ParticleHandle r = (*j).first;
0519               G4PrimaryParticle* p4 = (*j).second;
0520               PropertyMask reason(r->reason);
0521               char text[64];
0522           
0523               reason.set(G4PARTICLE_PRIMARY);
0524               v4->SetPrimary(p4);
0525               ::snprintf(text,sizeof(text),"-> G4Primary[%3d]",num_part);
0526               r.dumpWithMomentum(caller->outputLevel()-1,caller->name(),text);
0527               ++num_part;
0528             }
0529           }
0530         }
0531         if(caller->outputLevel() <= VERBOSE){
0532           v4->Print();
0533         }
0534       }
0535     }
0536     for( const auto& vtx : prim )   {
0537       Geant4ParticleHandle p = pm[vtx.first];
0538       primaries->insert(vtx.second, p);
0539     }
0540   }
0541   return 1;
0542 }