280void LDMXCascadeInterface::captureHistory() {
282 if (!collider)
return;
284 G4IntraNucleiCascader* cascader = collider->theIntraNucleiCascader;
285 if (!cascader)
return;
287 G4CascadeHistory* g4_history = cascader->theCascadeHistory;
288 if (!g4_history)
return;
290 last_history_.setIncidentTrackId(incident_track_id_);
292 if (cascader->tnuclei) {
293 last_history_.setTargetNucleus(cascader->tnuclei->getA(),
294 cascader->tnuclei->getZ());
297 const std::vector<G4CascadeHistory::HistoryEntry>& entries =
298 g4_history->theHistory;
300 if (entries.empty()) {
305 std::vector<int> parent_ids(entries.size(), -1);
307 for (
size_t i = 0; i < entries.size(); ++i) {
308 const auto& entry = entries[i];
309 for (
int d = 0; d < entry.n && d < 10; ++d) {
310 int daughter_id = entry.dId[d];
311 if (daughter_id >= 0 &&
312 static_cast<size_t>(daughter_id) < entries.size()) {
313 parent_ids[daughter_id] =
static_cast<int>(i);
319 std::vector<ldmx::CascadeStep> steps;
320 steps.reserve(entries.size());
322 for (
size_t i = 0; i < entries.size(); ++i) {
323 const auto& entry = entries[i];
324 int parent_id = parent_ids[i];
328 const G4CascadParticle& cpart = entry.cpart;
329 const G4InuclElementaryParticle& particle = cpart.getParticle();
331 step.setHistoryId(cpart.getHistoryId());
332 step.setParentId(parent_id);
333 step.setPdgId(getPdgCode(particle.type()));
336 G4LorentzVector mom = cpart.getMomentum();
337 step.setMomentum(mom.px() * 1000.0, mom.py() * 1000.0, mom.pz() * 1000.0,
340 const G4ThreeVector& pos = cpart.getPosition();
341 step.setPosition(pos.x(), pos.y(), pos.z());
343 step.setGeneration(cpart.getGeneration());
344 step.setZone(cpart.getCurrentZone());
346 std::vector<int> daughter_ids;
347 for (
int d = 0; d < entry.n && d < 10; ++d) {
348 daughter_ids.push_back(entry.dId[d]);
350 step.setDaughterIds(daughter_ids);
352 bool interacted = (entry.n > 0);
353 step.setInteracted(interacted);
357 bool escaped = !interacted && cpart.getGeneration() >= 0;
358 step.setEscaped(escaped);
359 step.setTargetPdgId(0);
362 int generation = cpart.getGeneration();
364 if (generation == 0) {
365 stage = ldmx::CascadeStage::INCIDENT;
366 }
else if (generation == 1) {
367 stage = ldmx::CascadeStage::PRIMARY;
368 }
else if (!interacted && !escaped) {
369 stage = ldmx::CascadeStage::ABSORBED;
370 }
else if (escaped && generation <= 2) {
371 stage = ldmx::CascadeStage::PREEQUILIBRIUM;
373 stage = ldmx::CascadeStage::CASCADE;
375 step.setStage(stage);
377 steps.push_back(std::move(step));
381 for (
auto& step : steps) {
382 if (!step.didInteract())
continue;
384 int bullet_charge = getCharge(step.getPdgId());
385 int bullet_baryon = getBaryonNumber(step.getPdgId());
386 int daughter_charge = 0;
387 int daughter_baryon = 0;
389 for (
int daughter_id : step.getDaughterIds()) {
390 for (
const auto& s : steps) {
391 if (s.getHistoryId() == daughter_id) {
392 daughter_charge += getCharge(s.getPdgId());
393 daughter_baryon += getBaryonNumber(s.getPdgId());
399 int delta_charge = daughter_charge - bullet_charge;
400 int delta_baryon = daughter_baryon - bullet_baryon;
401 step.setTargetPdgId(inferTargetPdg(delta_charge, delta_baryon));
404 last_history_.reserve(steps.size());
405 for (
auto& step : steps) {
406 last_history_.addStep(std::move(step));
410 double total_escaped_energy = 0.0;
411 int escaped_protons = 0;
412 int escaped_neutrons = 0;
414 for (
const auto& step : last_history_.getSteps()) {
415 if (step.didEscape()) {
416 total_escaped_energy += step.getKineticEnergy();
417 int pdg = step.getPdgId();
420 else if (pdg == 2112)
425 double excitation_energy =
426 last_history_.getIncidentEnergy() - total_escaped_energy;
427 if (excitation_energy < 0) excitation_energy = 0;
428 last_history_.setExcitationEnergy(excitation_energy);
430 int target_a = last_history_.getTargetA();
431 int target_z = last_history_.getTargetZ();
432 int residual_a = target_a - escaped_protons - escaped_neutrons;
433 int residual_z = target_z - escaped_protons;
434 if (residual_a < 0) residual_a = 0;
435 if (residual_z < 0) residual_z = 0;
436 last_history_.setResidualNucleus(residual_a, residual_z);
439void LDMXCascadeInterface::captureDeexcitationProducts(
440 G4HadFinalState* finalState) {
441 if (!finalState)
return;
443 int n_secondaries = finalState->GetNumberOfSecondaries();
444 if (n_secondaries == 0)
return;
448 struct EscapedParticle {
453 std::vector<EscapedParticle> cascade_escaped;
455 for (
const auto& step : last_history_.getSteps()) {
456 if (step.didEscape()) {
457 cascade_escaped.push_back({step.getPdgId(), step.getEnergy(),
false});
461 int next_history_id = 0;
462 for (
const auto& step : last_history_.getSteps()) {
463 if (step.getHistoryId() >= next_history_id) {
464 next_history_id = step.getHistoryId() + 1;
468 for (
int i = 0; i < n_secondaries; ++i) {
469 G4HadSecondary* secondary = finalState->GetSecondary(i);
470 if (!secondary)
continue;
472 const G4DynamicParticle* dyn_particle = secondary->GetParticle();
473 if (!dyn_particle)
continue;
475 int pdg = dyn_particle->GetPDGcode();
476 double energy = dyn_particle->GetTotalEnergy();
478 bool is_deexcitation =
true;
479 const double energy_tolerance = 1.0;
481 for (
auto& escaped : cascade_escaped) {
482 if (!escaped.matched_ && escaped.pdg_ == pdg &&
483 std::abs(escaped.energy_ - energy) < energy_tolerance) {
484 escaped.matched_ =
true;
485 is_deexcitation =
false;
490 if (is_deexcitation) {
492 step.setHistoryId(next_history_id++);
493 step.setParentId(-2);
496 G4ThreeVector mom = dyn_particle->GetMomentum();
497 step.setMomentum(mom.x(), mom.y(), mom.z(), energy);
498 step.setPosition(0, 0, 0);
499 step.setGeneration(-1);
501 step.setInteracted(
false);
502 step.setEscaped(
true);
503 step.setStage(ldmx::CascadeStage::DEEXCITATION);
505 last_history_.addStep(std::move(step));
Thread-local storage for cascade histories during simulation.