/// Callback to construct processes (uses the G4 particle table)
      virtual void constructProcesses(G4VUserPhysicsList* physics_list)   { 
        this->Geant4PhysicsList::constructProcesses(physics_list);
        info("+++ Constructing optical_photon processes:");
        info("+++              G4OpAbsorption G4OpRayleigh G4OpMieHG G4OpBoundaryProcess");
        G4ParticleTable*      table = G4ParticleTable::GetParticleTable();
        G4ParticleDefinition* particle = table->FindParticle("opticalphoton");
        if (0 == particle) {
          except("++ Cannot resolve 'opticalphoton' particle definition!");
        }

        G4OpBoundaryProcess*  fBoundaryProcess           = new G4OpBoundaryProcess();
        G4OpAbsorption*       fAbsorptionProcess         = new G4OpAbsorption();
        G4OpRayleigh*         fRayleighScatteringProcess = new G4OpRayleigh();
        G4OpMieHG*            fMieHGScatteringProcess    = new G4OpMieHG();

        fAbsorptionProcess->SetVerboseLevel(m_verbosity);
        fRayleighScatteringProcess->SetVerboseLevel(m_verbosity);
        fMieHGScatteringProcess->SetVerboseLevel(m_verbosity);
        fBoundaryProcess->SetVerboseLevel(m_verbosity);

        G4ProcessManager* pmanager = particle->GetProcessManager();
        pmanager->AddDiscreteProcess(fAbsorptionProcess);
        pmanager->AddDiscreteProcess(fRayleighScatteringProcess);
        pmanager->AddDiscreteProcess(fMieHGScatteringProcess);
        pmanager->AddDiscreteProcess(fBoundaryProcess);
      }
/*
CollimationParticleGun::CollimationParticleGun(      
                                const G4String& particleName, 
                                G4double energy,
                                G4ThreeVector position, 
                                G4ThreeVector momentumDirection)
  : G4VUserPrimaryGeneratorAction(), fParticleGun(0)
*/
CollimationParticleGun::CollimationParticleGun() : G4VUserPrimaryGeneratorAction(), ParticleGun(nullptr), particle(nullptr)
{
	//1 proton at a time
	ParticleGun  = new G4ParticleGun(1);

	//In the future this could be Pb ions or anything really
	G4String particleName = "proton";

	G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
	particle = particleTable->FindParticle(particleName);

	ParticleGun->SetParticleDefinition(particle);
}
void CollimationEventAction::SetOutputParticle(double x, double px, double y, double py, double p)
{
	OutputParticle->x = x;
	OutputParticle->xp = px;
	OutputParticle->y = y;
	OutputParticle->yp = py;

	G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
	G4ParticleDefinition* particle = particleTable->FindParticle("proton");
	const G4double mp = particle->GetPDGMass();
	double pp= sqrt(p*p + mp*mp);

	OutputParticle->p = pp;
	size_t oldprec = std::cout.precision(15);
	//std::cout << "OUT: " << x << "\t" << px << "\t" << y << "\t" << py << "\t" << pp << std::endl;
	std::cout.precision(oldprec);
}
/// Callback to construct particle decays
void Geant4PhysicsListActionSequence::constructDecays(G4VUserPhysicsList* physics_pointer)  {
  G4ParticleTable* pt = G4ParticleTable::GetParticleTable();
  G4ParticleTable::G4PTblDicIterator* iter = pt->GetIterator();
  // Add Decay Process
  G4Decay* decay = new G4Decay();
  info("ConstructDecays %p",physics_pointer);
  iter->reset();
  while ((*iter)())  {
    G4ParticleDefinition* p = iter->value();
    G4ProcessManager* mgr = p->GetProcessManager();
    if (decay->IsApplicable(*p))  {
      mgr->AddProcess(decay);
      // set ordering for PostStepDoIt and AtRestDoIt
      mgr->SetProcessOrdering(decay, idxPostStep);
      mgr->SetProcessOrdering(decay, idxAtRest);
    }
  }
}
/*
// For using ROOT classes.
// When using ROOT classes, it have to be thread local or mutex locked.
// I don't know _G4MT_TLS_ is truely needed or not.
// https://indico.cern.ch/event/226961/material-old/0/0?contribId=0
// In case without, this code looks like working well...
G4ThreadLocal TF1 *fEneFnc_G4MT_TLS_ = 0;
*/
BIPrimaryGeneratorAction::BIPrimaryGeneratorAction()
   : G4VUserPrimaryGeneratorAction(),
     fProtonGun(0),
     fEneFnc(NULL)
{
   G4AutoLock lock(&mutexInPGA);

   Int_t seed = time(NULL) + G4Threading::G4GetThreadId() * 100000;
   gRandom->SetSeed(seed);
/*
   if(!fEneFnc_G4MT_TLS_) fEneFnc_G4MT_TLS_ = new TF1("fEneFnc", "expo(0)+expo(2)+gaus(4)", 0., 30.);
   fEneFnc = fEneFnc_G4MT_TLS_;
*/
   fEneFnc = new TF1("fEneFnc", "expo(0)+expo(2)+gaus(4)", 0., 30.);
   fEneFnc->SetParameter(0, 2.67164e+01);
   fEneFnc->SetParameter(1, -8.35969e-01);
   fEneFnc->SetParameter(2, 2.15801e+01);
   fEneFnc->SetParameter(3, -7.66820e-02);
   fEneFnc->SetParameter(4, 3.79820e+09);
   fEneFnc->SetParameter(5, 8.94835e+00);
   fEneFnc->SetParameter(6, 3.04310e+00);
   fEnergy = fEneFnc->GetRandom() * MeV;   
   
   G4int nPar = 1;
   fProtonGun = new G4ParticleGun(nPar);

   fZPosition = -300.*mm;
   G4ParticleTable *parTable = G4ParticleTable::GetParticleTable();

   G4ParticleDefinition *proton = parTable->FindParticle("proton");
   fProtonGun->SetParticleDefinition(proton);
   fProtonGun->SetParticlePosition(G4ThreeVector(0., 0., fZPosition));
   fProtonGun->SetParticleMomentumDirection(G4ThreeVector(0., 0., 1.));
   fProtonGun->SetParticleEnergy(fEnergy);

   TFile *file = new TFile("randomSource.root", "OPEN");
   fHisSource = (TH2D*)file->Get("fHisMap");
   fHisSource->SetName("fHisSource");

   DefineCommands();
}
G4Event* SimG4GeantinosFromEdmTool::g4Event() {
  G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
  auto theEvent = new G4Event();

  G4ParticleDefinition* particleDefPos = particleTable->FindParticle("chargedgeantino");
  G4ParticleDefinition* particleDefNeg = particleTable->FindParticle("chargedgeantinonegative");
  G4ParticleDefinition* particleDefNeutral = particleTable->FindParticle("geantino");

  const fcc::MCParticleCollection* mcparticles = m_genParticles.get();
  for (const auto& mcparticle : *mcparticles) {
    const fcc::ConstGenVertex& v = mcparticle.startVertex();
    G4PrimaryVertex* g4Vertex = new G4PrimaryVertex(v.x() * sim::edm2g4::length,
                                                    v.y() * sim::edm2g4::length,
                                                    v.z() * sim::edm2g4::length,
                                                    v.ctau() / Gaudi::Units::c_light * sim::edm2g4::length);
    const fcc::BareParticle& mccore = mcparticle.core();
    G4PrimaryParticle* part = nullptr;
    if (mcparticle.charge() > 0) {
      part = new G4PrimaryParticle(particleDefPos);
    } else if (mcparticle.charge() < 0) {
      part = new G4PrimaryParticle(particleDefNeg);
    } else {
      part = new G4PrimaryParticle(particleDefNeutral);
    }

    part->SetMass(mccore.p4.mass);
    part->SetCharge(mcparticle.charge());
    double pX = mccore.p4.px * sim::edm2g4::energy;
    double pY = mccore.p4.py * sim::edm2g4::energy;
    double pZ = mccore.p4.pz * sim::edm2g4::energy;
    part->SetUserInformation(new sim::ParticleInformation(mcparticle));
    part->SetMomentum(pX, pY, pZ);
    g4Vertex->SetPrimary(part);
    theEvent->AddPrimaryVertex(g4Vertex);
  }
  return theEvent;
}
/*
// For using ROOT classes.
// When using ROOT classes, it have to be thread local or mutex locked.
// I don't know _G4MT_TLS_ is truely needed or not.
// https://indico.cern.ch/event/226961/material-old/0/0?contribId=0
// In case without, this code looks like working well...
G4ThreadLocal TF1 *fEneFnc_G4MT_TLS_ = 0;
*/
BIPrimaryGeneratorAction::BIPrimaryGeneratorAction(BeamType beamType, G4bool gridFlag, G4bool quarterFlag)
   : G4VUserPrimaryGeneratorAction(),
     fParticleGun(nullptr),
     fInputFile(nullptr),
     fHisSource(nullptr),
     fEneFnc(nullptr),
     fAngFnc(nullptr)
{
   fBeamType = beamType;

   fForGrid = gridFlag;

   fUseQuarter = quarterFlag;
   if(fUseQuarter) fPhiLimit = 0.5 * CLHEP::pi;
   else fPhiLimit = 2. * CLHEP::pi;
   
   fDx = (458. - 78.) / 15.;
   fDy = (332 - 152) / (log10(60.) - log10(20.));

   G4AutoLock lock(&mutexInPGA);
   
   Int_t seed = G4UniformRand() * 1000000;
   G4cout << "Seed of PGA = " << seed << G4endl;
   gRandom->SetSeed(seed);
   
   G4int nPar = 1;
   fParticleGun = new G4ParticleGun(nPar);

   fZPosition = -300.*mm;
   //fZPosition = -160.*mm; // Minimum distance for new beam

   fParVec = G4ThreeVector(0., 0., 1.);
   
   G4ParticleTable *parTable = G4ParticleTable::GetParticleTable();
   G4ParticleDefinition *proton = parTable->FindParticle("proton");
   fParticleGun->SetParticleDefinition(proton);
   fParticleGun->SetParticlePosition(G4ThreeVector(0., 0., fZPosition));
   fParticleGun->SetParticleMomentumDirection(fParVec);
   fParticleGun->SetParticleEnergy(fEnergy);

   fInputFile = new TFile("randomSource.root", "OPEN");
   fHisSource = (TH2D*)fInputFile->Get("HisMap");
   fHisSource->SetName("fHisSource");
   
   DefineCommands();

// Pointer of Function is not good for readable code?
// And also, use if statement does not make program slow.
   if(fBeamType == kFirstBeam){
      fEneFnc = new TF1("fncEne", "exp([0]*x)", 0., 100.);
      fEneFnc->SetParameter(0, -4.77205e-02);
      
      GunFuncPointer = &BIPrimaryGeneratorAction::FirstBeamGun;
   }
   else if(fBeamType == kSecondBeam)
      GunFuncPointer = &BIPrimaryGeneratorAction::SecondBeamGun;
   
   else if(fBeamType == kThirdBeam){
      fEneFnc = new TF1("fncEne", "exp([0]*x)", 0., 100.);
      fEneFnc->SetParameter(0, -4.77205e-02);
      
      fAngFnc = new TF1("fAngFnc", "exp([0]*x)", 0., 20.);
      fAngFnc->SetParameter(0, -8.98131e-02);

      GunFuncPointer = &BIPrimaryGeneratorAction::ThirdBeamGun;
   }
   else{
      G4cout << "Beam type is wrong.  Please check it." << G4endl;
      exit(0);
   }
}
void Geant4ExtraParticles::constructParticle(Constructor& ) {
  if (m_pdgfile.empty()) return;

  info("pdgfile: %s",m_pdgfile.c_str());

  G4ParticleTable *theParticleTable = G4ParticleTable::GetParticleTable();
  std::ifstream pdgFile( m_pdgfile.c_str(), std::ifstream::in );

  if (!pdgFile.is_open()) {
    except("Could not open pdgfile: %s",m_pdgfile.c_str());
    return;
  }

  info("opened pdgfile: %s",m_pdgfile.c_str());

  while ( !pdgFile.eof() ) {
    // read line
    std::string linebuf;
    getline( pdgFile, linebuf );

    // ignore comments
    if (linebuf.substr(0,1) == "#") continue;
    if (linebuf.substr(0,2) == "//") continue;

    // ignore empty lines
    if (linebuf.empty()) continue;

    // parse line
    int pdg;
    std::string nam;
    double charge;
    double mass;
    double width;
    double lifetime;

    std::istringstream istr(linebuf);

    istr >> pdg >> nam >> charge >> mass >> width >> lifetime;

    // do add particles that don't fly
    // if (lifetime == 0) continue;

    if(width<0) width = 0;

    // normalize to G4 units
    mass *= GeV;

    if (charge != 0) {
      charge /= 3.;
    }

    if (lifetime > 0) {
      lifetime = lifetime*mm/c_light;
    }

    if (width == 0 && lifetime > 0) {
      width = hbar_Planck/lifetime;
    }

    // don't add if the particle already exists
    G4ParticleDefinition* p = theParticleTable->FindParticle(pdg);
    if ( !p ) {

      if (abs(pdg)>80 && abs(pdg)<=100) {
        // don't add generator-specific particles
      } else {
        /*
          if (pdg==5122) {
          G4cout << "Lambda_b0: " << "PDG=" << pdg << ", name=" << name << ", chrg=" << charge
          << ", mass=" << mass << ", width=" << width << ", lifetime=" << lifetime << "\n";
          G4cout << "debug: mass=" << 5.62 << ", width =" << 1.39e-12/6.582e-16 << ", lifetime=" << 1.39e-12 << "\n";
          }
        //*/
        p = new G4ParticleDefinition(
                                     nam,        // name
                                     mass,       // mass
                                     width,      // width
                                     charge,     // charge
                                     0,                                      // 2*spin
                                     0,          // parity
                                     0,          // C-conjugation
                                     0,          // 2*isospin
                                     0,          // 2*isospin3
                                     0,          // G-parity
                                     "extra",    // type
                                     0,          // lepton number
                                     0,          // baryon number
                                     pdg,        // PDG encoding
                                     width==0?true:false,      // stable
                                     lifetime,   // lifetime
                                     NULL,       // decay table
                                     false);      // short lived
      }
    }
  }

  G4cout << "Loaded extra particles using file: " << m_pdgfile << G4endl;
}