205 const G4Element* element) {
206 int z =
static_cast<int>(element->GetZ());
211 size_t n_isotopes = element->GetNumberOfIsotopes();
213 if (n_isotopes > 0) {
215 const G4IsotopeVector* isotopes = element->GetIsotopeVector();
216 const G4double* rel_ab = element->GetRelativeAbundanceVector();
218 for (
size_t j = 0; j < n_isotopes; ++j) {
219 int iso_z = (*isotopes)[j]->GetZ();
220 int a = (*isotopes)[j]->GetN();
221 double abundance = rel_ab[j];
222 if (abundance <= 0)
continue;
224 int target_code = 1000000000 + iso_z * 10000 + a * 10;
227 ldmx_log(warn) <<
"No cross-section spline available for target "
228 << target_code <<
" (Z=" << iso_z <<
", A=" << a
229 <<
") from element " << element->GetName()
230 <<
" — skipping it (computing on the fly would be "
231 <<
"prohibitively slow).";
240 auto driver = std::make_unique<genie::GEVGDriver>();
241 genie::InitialState initial_state(target_code, 11);
242 driver->SetEventGeneratorList(
243 genie::RunOpt::Instance()->EventGeneratorList());
244 driver->SetUnphysEventMask(*genie::RunOpt::Instance()->UnphysEventMask());
245 driver->Configure(initial_state);
246 driver->UseSplines();
251 ldmx_log(info) <<
"Auto-discovered target " << target_code <<
" (Z=" << z
252 <<
", A=" << a <<
", abundance=" << abundance <<
")";
256 int a =
static_cast<int>(std::round(element->GetAtomicMassAmu()));
257 int target_code = 1000000000 + z * 10000 + a * 10;
260 ldmx_log(warn) <<
"No cross-section spline available for target "
261 << target_code <<
" (Z=" << z <<
", A=" << a
262 <<
") from element " << element->GetName()
263 <<
" — skipping it (computing on the fly would be "
264 <<
"prohibitively slow).";
273 auto driver = std::make_unique<genie::GEVGDriver>();
274 genie::InitialState initial_state(target_code, 11);
275 driver->SetEventGeneratorList(
276 genie::RunOpt::Instance()->EventGeneratorList());
277 driver->SetUnphysEventMask(*genie::RunOpt::Instance()->UnphysEventMask());
278 driver->Configure(initial_state);
279 driver->UseSplines();
284 ldmx_log(info) <<
"Auto-discovered target " << target_code <<
" (Z=" << z
285 <<
", A=" << a <<
") from element " << element->GetName();
290 namespace vc = simcore::g4user::volumechecks;
294 using Test = bool (*)(G4LogicalVolume*,
const std::string&);
295 Test include_volume_test =
nullptr;
297 include_volume_test = &vc::isInEcal;
299 include_volume_test = &vc::isInEcalOld;
301 include_volume_test = &vc::isInTargetOnly;
303 include_volume_test = &vc::isInTargetRegion;
305 include_volume_test = &vc::isInHcal;
307 include_volume_test = &vc::nameContains;
311 for (G4LogicalVolume* volume : *G4LogicalVolumeStore::GetInstance()) {
315 G4Material* mat = volume->GetMaterial();
317 const G4ElementVector* els = mat->GetElementVector();
318 for (
size_t i = 0; i < mat->GetNumberOfElements(); ++i) {
325 ldmx_log(warn) <<
"Auto-discovery found no target isotopes in volume(s) "
327 <<
" volume(s) matched). Electronuclear interactions will "
328 <<
"not fire — check the discover_volume setting.";
330 ldmx_log(info) <<
"Auto-discovered " <<
evg_drivers_.size()
331 <<
" target isotope(s) from " << n_volumes
341 const G4Track& track, G4double ,
342 G4ForceCondition* ) {
343 if (!
IsApplicable(*track.GetParticleDefinition()))
return DBL_MAX;
354 G4double energy_geant4 = track.GetDynamicParticle()->GetTotalEnergy();
355 double energy_gev = energy_geant4 / CLHEP::GeV;
357 if (energy_gev < 0.001)
return DBL_MAX;
360 G4ThreeVector dir = track.GetMomentumDirection();
363 double electron_mass_gev = 0.000510999;
364 double elec_p = std::sqrt(energy_gev * energy_gev -
365 electron_mass_gev * electron_mass_gev);
366 if (elec_p <= 0)
return DBL_MAX;
368 TLorentzVector e_p4(elec_p * dir.x(), elec_p * dir.y(), elec_p * dir.z(),
371 G4Material* material = track.GetMaterial();
372 const G4ElementVector* elements = material->GetElementVector();
373 const G4double* atom_density = material->GetVecNbOfAtomsPerVolume();
374 size_t n_elements = material->GetNumberOfElements();
379 for (
size_t i = 0; i < n_elements; ++i) {
380 int z =
static_cast<int>((*elements)[i]->GetZ());
382 G4double element_sigma = 0;
385 for (
const auto& [driver_idx, abundance] : it->second) {
387 double xsec_genie =
evg_drivers_[driver_idx]->XSecSum(e_p4);
390 (xsec_genie / genie::units::millibarn) * CLHEP::millibarn;
391 element_sigma += abundance * xsec_g4;
396 sigma += atom_density[i] * element_sigma;
400 return sigma > DBL_MIN ? 1.0 / sigma : DBL_MAX;
404 const G4Track& track,
const G4Step& step) {
407 auto seed = G4Random::getTheEngine()->getSeed();
408 ldmx_log(debug) <<
"Initializing GENIE random seed: " << seed;
409 genie::utils::app_init::RandGen(seed);
413 aParticleChange.Initialize(track);
417 G4ProcessManager* pman = track.GetDefinition()->GetProcessManager();
418 for (
int i_proc = 0; i_proc < pman->GetProcessList()->size(); i_proc++) {
419 G4VProcess* p = (*(pman->GetProcessList()))[i_proc];
421 pman->SetProcessActivation(p,
false);
428 G4double energy_geant4 = step.GetPostStepPoint()->GetTotalEnergy();
429 double energy_gev = energy_geant4 / CLHEP::GeV;
431 G4ThreeVector dir = track.GetMomentumDirection();
433 ldmx_log(info) <<
"GENIE electronuclear interaction at E = " << energy_gev
437 double electron_mass_gev = 0.000510999;
438 double elec_p = std::sqrt(energy_gev * energy_gev -
439 electron_mass_gev * electron_mass_gev);
440 TLorentzVector e_p4(elec_p * dir.x(), elec_p * dir.y(), elec_p * dir.z(),
444 G4Material* material = track.GetMaterial();
445 const G4ElementVector* elements = material->GetElementVector();
446 size_t n_elements = material->GetNumberOfElements();
448 int selected_driver = -1;
450 if (n_elements == 1) {
452 int z =
static_cast<int>((*elements)[0]->GetZ());
455 if (it->second.size() == 1) {
456 selected_driver = it->second[0].first;
460 for (
const auto& [idx, ab] : it->second) total_ab += ab;
461 double rand_val = G4UniformRand() * total_ab;
463 for (
const auto& [idx, ab] : it->second) {
465 if (rand_val <= running) {
466 selected_driver = idx;
470 if (selected_driver < 0) selected_driver = it->second.back().first;
477 int selected_element =
static_cast<int>(n_elements) - 1;
478 for (
size_t i = 0; i < n_elements - 1; ++i) {
480 selected_element =
static_cast<int>(i);
485 int z =
static_cast<int>((*elements)[selected_element]->GetZ());
488 if (it->second.size() == 1) {
489 selected_driver = it->second[0].first;
493 std::vector<double> weights;
495 for (
const auto& [idx, ab] : it->second) {
497 double w = ab * xsec;
498 weights.push_back(w);
501 double r = G4UniformRand() * total_w;
503 for (
size_t j = 0; j < it->second.size(); ++j) {
504 running += weights[j];
506 selected_driver = it->second[j].first;
510 if (selected_driver < 0) selected_driver = it->second.back().first;
515 if (selected_driver < 0) {
517 ldmx_log(error) <<
"No matching GENIE target found for material "
518 << material->GetName() <<
" — returning unchanged track";
519 return G4VDiscreteProcess::PostStepDoIt(track, step);
523 genie::EventRecord* genie_event =
nullptr;
524 while (!genie_event) {
525 genie_event =
evg_drivers_[selected_driver]->GenerateEvent(e_p4);
530 G4EventManager::GetEventManager()->GetUserInformation());
534 hepmc3_genie->write_data(hepmc3_ldmx_genie);
535 ev_info->addHepMC3GenEvent(hepmc3_ldmx_genie);
538 ev_info->incWeight(genie_event->Weight());
542 int n_entries = genie_event->GetEntries();
543 int n_secondaries = 0;
544 for (
int i = 0; i < n_entries; ++i) {
545 auto* p =
static_cast<genie::GHepParticle*
>((*genie_event)[i]);
546 if (p->Status() == 1) ++n_secondaries;
549 aParticleChange.SetNumberOfSecondaries(n_secondaries);
552 G4ThreeVector position = step.GetPostStepPoint()->GetPosition();
553 G4double global_time = step.GetPostStepPoint()->GetGlobalTime();
556 for (
int i = 0; i < n_entries; ++i) {
557 auto* p =
static_cast<genie::GHepParticle*
>((*genie_event)[i]);
558 if (p->Status() != 1)
continue;
561 G4ParticleDefinition* particle_def =
nullptr;
564 if (pdg > 1000000000) {
565 int ion_z = (pdg / 10000) % 1000;
566 int ion_a = (pdg / 10) % 1000;
567 particle_def = G4IonTable::GetIonTable()->GetIon(ion_z, ion_a, 0.);
569 particle_def = G4ParticleTable::GetParticleTable()->FindParticle(pdg);
573 ldmx_log(warn) <<
"Could not find G4ParticleDefinition for PDG=" << pdg
574 <<
" — skipping this secondary";
578 G4ThreeVector momentum(p->Px() * CLHEP::GeV, p->Py() * CLHEP::GeV,
579 p->Pz() * CLHEP::GeV);
581 auto* dynamic_particle =
new G4DynamicParticle(particle_def, momentum);
582 auto* secondary_track =
583 new G4Track(dynamic_particle, global_time, position);
584 secondary_track->SetParentID(track.GetTrackID());
586 aParticleChange.AddSecondary(secondary_track);
590 aParticleChange.ProposeTrackStatus(fStopAndKill);
594 return G4VDiscreteProcess::PostStepDoIt(track, step);