210 const G4Element* element) {
211 int z =
static_cast<int>(element->GetZ());
216 size_t n_isotopes = element->GetNumberOfIsotopes();
218 if (n_isotopes > 0) {
220 const G4IsotopeVector* isotopes = element->GetIsotopeVector();
221 const G4double* rel_ab = element->GetRelativeAbundanceVector();
223 for (
size_t j = 0; j < n_isotopes; ++j) {
224 int iso_z = (*isotopes)[j]->GetZ();
225 int a = (*isotopes)[j]->GetN();
226 double abundance = rel_ab[j];
227 if (abundance <= 0)
continue;
229 int target_code = 1000000000 + iso_z * 10000 + a * 10;
232 ldmx_log(warn) <<
"No cross-section spline available for target "
233 << target_code <<
" (Z=" << iso_z <<
", A=" << a
234 <<
") from element " << element->GetName()
235 <<
" — skipping it (computing on the fly would be "
236 <<
"prohibitively slow).";
245 auto driver = std::make_unique<genie::GEVGDriver>();
246 genie::InitialState initial_state(target_code, 11);
247 driver->SetEventGeneratorList(
248 genie::RunOpt::Instance()->EventGeneratorList());
249 driver->SetUnphysEventMask(*genie::RunOpt::Instance()->UnphysEventMask());
250 driver->Configure(initial_state);
251 driver->UseSplines();
256 ldmx_log(info) <<
"Auto-discovered target " << target_code <<
" (Z=" << z
257 <<
", A=" << a <<
", abundance=" << abundance <<
")";
261 int a =
static_cast<int>(std::round(element->GetAtomicMassAmu()));
262 int target_code = 1000000000 + z * 10000 + a * 10;
265 ldmx_log(warn) <<
"No cross-section spline available for target "
266 << target_code <<
" (Z=" << z <<
", A=" << a
267 <<
") from element " << element->GetName()
268 <<
" — skipping it (computing on the fly would be "
269 <<
"prohibitively slow).";
278 auto driver = std::make_unique<genie::GEVGDriver>();
279 genie::InitialState initial_state(target_code, 11);
280 driver->SetEventGeneratorList(
281 genie::RunOpt::Instance()->EventGeneratorList());
282 driver->SetUnphysEventMask(*genie::RunOpt::Instance()->UnphysEventMask());
283 driver->Configure(initial_state);
284 driver->UseSplines();
289 ldmx_log(info) <<
"Auto-discovered target " << target_code <<
" (Z=" << z
290 <<
", A=" << a <<
") from element " << element->GetName();
295 namespace vc = simcore::g4user::volumechecks;
299 using Test = bool (*)(G4LogicalVolume*,
const std::string&);
300 Test include_volume_test =
nullptr;
302 include_volume_test = &vc::isInEcal;
304 include_volume_test = &vc::isInEcalOld;
306 include_volume_test = &vc::isInTargetOnly;
308 include_volume_test = &vc::isInTargetRegion;
310 include_volume_test = &vc::isInHcal;
312 include_volume_test = &vc::nameContains;
316 for (G4LogicalVolume* volume : *G4LogicalVolumeStore::GetInstance()) {
320 G4Material* mat = volume->GetMaterial();
322 const G4ElementVector* els = mat->GetElementVector();
323 for (
size_t i = 0; i < mat->GetNumberOfElements(); ++i) {
330 ldmx_log(warn) <<
"Auto-discovery found no target isotopes in volume(s) "
332 <<
" volume(s) matched). Electronuclear interactions will "
333 <<
"not fire — check the discover_volume setting.";
335 ldmx_log(info) <<
"Auto-discovered " <<
evg_drivers_.size()
336 <<
" target isotope(s) from " << n_volumes
346 const G4Track& track, G4double ,
347 G4ForceCondition* ) {
348 if (!
IsApplicable(*track.GetParticleDefinition()))
return DBL_MAX;
359 G4double energy_geant4 = track.GetDynamicParticle()->GetTotalEnergy();
360 double energy_gev = energy_geant4 / CLHEP::GeV;
362 if (energy_gev < 0.001)
return DBL_MAX;
365 G4ThreeVector dir = track.GetMomentumDirection();
368 double electron_mass_gev = 0.000510999;
369 double elec_p = std::sqrt(energy_gev * energy_gev -
370 electron_mass_gev * electron_mass_gev);
371 if (elec_p <= 0)
return DBL_MAX;
373 TLorentzVector e_p4(elec_p * dir.x(), elec_p * dir.y(), elec_p * dir.z(),
376 G4Material* material = track.GetMaterial();
377 const G4ElementVector* elements = material->GetElementVector();
378 const G4double* atom_density = material->GetVecNbOfAtomsPerVolume();
379 size_t n_elements = material->GetNumberOfElements();
384 for (
size_t i = 0; i < n_elements; ++i) {
385 int z =
static_cast<int>((*elements)[i]->GetZ());
387 G4double element_sigma = 0;
390 for (
const auto& [driver_idx, abundance] : it->second) {
392 double xsec_genie =
evg_drivers_[driver_idx]->XSecSum(e_p4);
395 (xsec_genie / genie::units::millibarn) * CLHEP::millibarn;
396 element_sigma += abundance * xsec_g4;
401 sigma += atom_density[i] * element_sigma;
405 return sigma > DBL_MIN ? 1.0 / sigma : DBL_MAX;
409 const G4Track& track,
const G4Step& step) {
412 auto seed = G4Random::getTheEngine()->getSeed();
413 ldmx_log(debug) <<
"Initializing GENIE random seed: " << seed;
414 genie::utils::app_init::RandGen(seed);
418 aParticleChange.Initialize(track);
422 G4ProcessManager* pman = track.GetDefinition()->GetProcessManager();
423 for (
int i_proc = 0; i_proc < pman->GetProcessList()->size(); i_proc++) {
424 G4VProcess* p = (*(pman->GetProcessList()))[i_proc];
426 pman->SetProcessActivation(p,
false);
433 G4double energy_geant4 = step.GetPostStepPoint()->GetTotalEnergy();
434 double energy_gev = energy_geant4 / CLHEP::GeV;
436 G4ThreeVector dir = track.GetMomentumDirection();
438 ldmx_log(info) <<
"GENIE electronuclear interaction at E = " << energy_gev
442 double electron_mass_gev = 0.000510999;
443 double elec_p = std::sqrt(energy_gev * energy_gev -
444 electron_mass_gev * electron_mass_gev);
445 TLorentzVector e_p4(elec_p * dir.x(), elec_p * dir.y(), elec_p * dir.z(),
449 G4Material* material = track.GetMaterial();
450 const G4ElementVector* elements = material->GetElementVector();
451 size_t n_elements = material->GetNumberOfElements();
453 int selected_driver = -1;
455 if (n_elements == 1) {
457 int z =
static_cast<int>((*elements)[0]->GetZ());
460 if (it->second.size() == 1) {
461 selected_driver = it->second[0].first;
465 for (
const auto& [idx, ab] : it->second) total_ab += ab;
466 double rand_val = G4UniformRand() * total_ab;
468 for (
const auto& [idx, ab] : it->second) {
470 if (rand_val <= running) {
471 selected_driver = idx;
475 if (selected_driver < 0) selected_driver = it->second.back().first;
482 int selected_element =
static_cast<int>(n_elements) - 1;
483 for (
size_t i = 0; i < n_elements - 1; ++i) {
485 selected_element =
static_cast<int>(i);
490 int z =
static_cast<int>((*elements)[selected_element]->GetZ());
493 if (it->second.size() == 1) {
494 selected_driver = it->second[0].first;
498 std::vector<double> weights;
500 for (
const auto& [idx, ab] : it->second) {
502 double w = ab * xsec;
503 weights.push_back(w);
506 double r = G4UniformRand() * total_w;
508 for (
size_t j = 0; j < it->second.size(); ++j) {
509 running += weights[j];
511 selected_driver = it->second[j].first;
515 if (selected_driver < 0) selected_driver = it->second.back().first;
520 if (selected_driver < 0) {
522 ldmx_log(error) <<
"No matching GENIE target found for material "
523 << material->GetName() <<
" — returning unchanged track";
524 return G4VDiscreteProcess::PostStepDoIt(track, step);
528 genie::EventRecord* genie_event =
nullptr;
529 while (!genie_event) {
530 genie_event =
evg_drivers_[selected_driver]->GenerateEvent(e_p4);
535 G4EventManager::GetEventManager()->GetUserInformation());
539 hepmc3_genie->write_data(hepmc3_ldmx_genie);
540 ev_info->addHepMC3GenEvent(hepmc3_ldmx_genie);
543 ev_info->incWeight(genie_event->Weight());
547 int n_entries = genie_event->GetEntries();
548 int n_secondaries = 0;
549 for (
int i = 0; i < n_entries; ++i) {
550 auto* p =
static_cast<genie::GHepParticle*
>((*genie_event)[i]);
551 if (p->Status() == 1) ++n_secondaries;
554 aParticleChange.SetNumberOfSecondaries(n_secondaries);
557 G4ThreeVector position = step.GetPostStepPoint()->GetPosition();
558 G4double global_time = step.GetPostStepPoint()->GetGlobalTime();
561 for (
int i = 0; i < n_entries; ++i) {
562 auto* p =
static_cast<genie::GHepParticle*
>((*genie_event)[i]);
563 if (p->Status() != 1)
continue;
566 G4ParticleDefinition* particle_def =
nullptr;
569 if (pdg > 1000000000) {
570 int ion_z = (pdg / 10000) % 1000;
571 int ion_a = (pdg / 10) % 1000;
572 particle_def = G4IonTable::GetIonTable()->GetIon(ion_z, ion_a, 0.);
574 particle_def = G4ParticleTable::GetParticleTable()->FindParticle(pdg);
578 ldmx_log(warn) <<
"Could not find G4ParticleDefinition for PDG=" << pdg
579 <<
" — skipping this secondary";
583 G4ThreeVector momentum(p->Px() * CLHEP::GeV, p->Py() * CLHEP::GeV,
584 p->Pz() * CLHEP::GeV);
586 auto* dynamic_particle =
new G4DynamicParticle(particle_def, momentum);
587 auto* secondary_track =
588 new G4Track(dynamic_particle, global_time, position);
589 secondary_track->SetParentID(track.GetTrackID());
591 aParticleChange.AddSecondary(secondary_track);
595 aParticleChange.ProposeTrackStatus(fStopAndKill);
599 return G4VDiscreteProcess::PostStepDoIt(track, step);