// // ******************************************************************** // * License and Disclaimer * // * * // * The Geant4 software is copyright of the Copyright Holders of * // * the Geant4 Collaboration. It is provided under the terms and * // * conditions of the Geant4 Software License, included in the file * // * LICENSE and available at http://cern.ch/geant4/license . These * // * include a list of copyright holders. * // * * // * Neither the authors of this software system, nor their employing * // * institutes,nor the agencies providing financial support for this * // * work make any representation or warranty, express or implied, * // * regarding this software system or assume any liability for its * // * use. Please see the license in the file LICENSE and URL above * // * for the full disclaimer and the limitation of liability. * // * * // * This code implementation is the result of the scientific and * // * technical work of the GEANT4 collaboration. * // * By using, copying, modifying or distributing the software (or * // * any work based on the software) you agree to acknowledge its * // * use in resulting scientific publications, and indicate your * // * acceptance of all terms of the Geant4 Software license. * // ******************************************************************** // /// \file SteppingAction.cc /// \brief Implementation of the SteppingAction class #include "SteppingAction.hh" #include "Run.hh" #include "G4DecayProducts.hh" #include "G4DecayTable.hh" #include "G4LossTableManager.hh" #include "G4ParticleDefinition.hh" #include "G4ParticleTypes.hh" #include "G4Step.hh" #include "G4StepPoint.hh" #include "G4SystemOfUnits.hh" #include "G4TouchableHistory.hh" #include "G4Track.hh" #include "G4VDecayChannel.hh" #include "G4VPhysicalVolume.hh" #include "G4VTouchable.hh" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... SteppingAction::SteppingAction() : G4UserSteppingAction() { Initialize(); } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... SteppingAction::~SteppingAction() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... void SteppingAction::Initialize() { // Initialization needed at the beginning of each Run fRunPtr = nullptr; fToleranceEPviolations = 1.0 * CLHEP::eV; //***LOOKHERE*** fPrimaryParticleId = 0; fPrimaryParticleInitialKineticEnergy = 0.0; fPrimaryParticleInitialTotalEnergy = 0.0; fPrimaryParticleInitialMomentum = 0.0; fPrimaryParticleInitialBeta = 1.0; fPrimaryParticleInitialGamma = 1.0; fPrimaryParticleInitial3Momentum = G4ThreeVector(0.0, 0.0, 0.0); fPrimaryParticleInitialPosition = G4ThreeVector(0.0, 0.0, 0.0); fMaxEkin_deltaMax = 0.0; fMaxEtot_deltaMax = 0.0; fMaxP_deltaMax = 0.0; fMaxPdir_deltaMax = 0.0; fMaxMass_deltaMax1 = 0.0; fMaxMass_deltaMax2 = 0.0; fMaxMass_deltaMax3 = 0.0; fMeanMass_deltaMax3 = 0.0; fMaxBeta_deltaMax1 = 0.0; fMaxBeta_deltaMax2 = 0.0; fMaxGamma_deltaMax1 = 0.0; fMaxGamma_deltaMax2 = 0.0; fMaxGamma_deltaMax3 = 0.0; fMaxT_proper_deltaMax = 0.0; fMaxT_lab_deltaMax = 0.0; fMaxMc_truth_rPos_deltaMax = 0.0; fMeanMc_truth_rPos_deltaMax = 0.0; fMeanDeltaR_primaryDecay = 0.0; fMinDeltaR_primaryDecay = 9999999.9; fMaxDeltaR_primaryDecay = -9999999.9; fMeanR_primaryDecay = 0.0; fMinR_primaryDecay = 9999999.9; fMaxR_primaryDecay = -9999999.9; fMeanX_primaryDecay = 0.0; fMinX_primaryDecay = 9999999.9; fMaxX_primaryDecay = -9999999.9; fMeanY_primaryDecay = 0.0; fMinY_primaryDecay = 9999999.9; fMaxY_primaryDecay = -9999999.9; fMeanZ_primaryDecay = 0.0; fMinZ_primaryDecay = 9999999.9; fMaxZ_primaryDecay = -9999999.9; fMeanDeltaAngle_primaryDecay = 0.0; fMinDeltaAngle_primaryDecay = 9999999.9; fMaxDeltaAngle_primaryDecay = -9999999.9; fMeanDeltaEkin_primaryDecay = 0.0; fMinDeltaEkin_primaryDecay = 9999999.9; fMaxDeltaEkin_primaryDecay = -9999999.9; fMeanEkin_primaryDecay = 0.0; fMinEkin_primaryDecay = 9999999.9; fMaxEkin_primaryDecay = -9999999.9; fMeanPx_primaryDecay = 0.0; fMinPx_primaryDecay = 9999999.9; fMaxPx_primaryDecay = -9999999.9; fMeanPy_primaryDecay = 0.0; fMinPy_primaryDecay = 9999999.9; fMaxPy_primaryDecay = -9999999.9; fMeanPz_primaryDecay = 0.0; fMinPz_primaryDecay = 9999999.9; fMaxPz_primaryDecay = -9999999.9; fMinUnderestimated_mc_truth_rPos_delta = 9999999.9; fMaxOverestimated_mc_truth_rPos_delta = -9999999.9; fMeanUnderestimated_mc_truth_rPos_delta = 0.0; fMeanOverestimated_mc_truth_rPos_delta = 0.0; fMinUnderestimated_rDeltaPos = 9999999.9; fMaxOverestimated_rDeltaPos = -9999999.9; fMeanUnderestimated_rDeltaPos = 0.0; fMeanOverestimated_rDeltaPos = 0.0; fMaxFloat_rDeltaPos_deltaMax = -9999999.9; fMeanViolationE_primaryDecay = 0.0; fMinViolationE_primaryDecay = 9999999.9; fMaxViolationE_primaryDecay = -9999999.9; fMeanViolationPx_primaryDecay = 0.0; fMinViolationPx_primaryDecay = 9999999.9; fMaxViolationPx_primaryDecay = -9999999.9; fMeanViolationPy_primaryDecay = 0.0; fMinViolationPy_primaryDecay = 9999999.9; fMaxViolationPy_primaryDecay = -9999999.9; fMeanViolationPz_primaryDecay = 0.0; fMinViolationPz_primaryDecay = 9999999.9; fMaxViolationPz_primaryDecay = -9999999.9; } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... void SteppingAction::UserSteppingAction(const G4Step* theStep) { // Store the information about the ID and the kinetic energy of the primary particle, // at the first step of the first event. // Note that for the kinetic energy, we are considering the "pre-step" point of such first step. if (theStep->GetTrack()->GetParentID() == 0 && theStep->GetTrack()->GetCurrentStepNumber() == 1) { fPrimaryParticleId = theStep->GetTrack()->GetDefinition()->GetPDGEncoding(); fPrimaryParticleInitialKineticEnergy = theStep->GetPreStepPoint()->GetKineticEnergy(); fPrimaryParticleInitialTotalEnergy = theStep->GetPreStepPoint()->GetTotalEnergy(); fPrimaryParticleInitial3Momentum = theStep->GetPreStepPoint()->GetMomentum(); fPrimaryParticleInitialMomentum = fPrimaryParticleInitial3Momentum.mag(); fPrimaryParticleInitialPosition = theStep->GetPreStepPoint()->GetPosition(); fPrimaryParticleInitialBeta = theStep->GetPreStepPoint()->GetBeta(); fPrimaryParticleInitialGamma = theStep->GetPreStepPoint()->GetGamma(); // As tolerance for EP violations, consider the max value between the default value // and 1 billionth of the initial, primary particle kinetic energy. if (fToleranceEPviolations < fPrimaryParticleInitialKineticEnergy * 1.0e-9) { fToleranceEPviolations = fPrimaryParticleInitialKineticEnergy * 1.0e-9; } // Set the values of this run to the Run object if (fRunPtr) { fRunPtr->SetPrimaryParticleId(fPrimaryParticleId); fRunPtr->SetPrimaryParticleInitialKineticEnergy(fPrimaryParticleInitialKineticEnergy); fRunPtr->SetPrimaryParticleInitialTotalEnergy(fPrimaryParticleInitialTotalEnergy); fRunPtr->SetPrimaryParticleInitialMomentum(fPrimaryParticleInitialMomentum); fRunPtr->SetPrimaryParticleInitialBeta(fPrimaryParticleInitialBeta); fRunPtr->SetPrimaryParticleInitialGamma(fPrimaryParticleInitialGamma); fRunPtr->SetPrimaryParticleInitial3Momentum(fPrimaryParticleInitial3Momentum); fRunPtr->SetPrimaryParticleInitialPosition(fPrimaryParticleInitialPosition); fRunPtr->SetToleranceEPviolations(ToleranceEPviolations()); fRunPtr->SetToleranceDeltaDecayRadius(ToleranceDeltaDecayRadius()); fRunPtr->SetIsPreassignedDecayEnabled(IsPreassignedDecayEnabled()); fRunPtr->SetIsBoostToLabEnabled(IsBoostToLabEnabled()); } // Use the preassigned decay is enabled if (IsPreassignedDecayEnabled() && (!theStep->GetTrack()->GetDefinition()->GetPDGStable())) { G4DynamicParticle* dynamicParent = const_cast(theStep->GetTrack()->GetDynamicParticle()); if (dynamicParent != nullptr) { G4DecayProducts* decayProducts = (G4DecayProducts*)(dynamicParent->GetPreAssignedDecayProducts()); if (decayProducts == nullptr) { G4ParticleDefinition* parentDef = theStep->GetTrack()->GetDefinition(); G4DecayTable* decayTable = (parentDef == nullptr ? nullptr : parentDef->GetDecayTable()); if (decayTable != nullptr) { G4double parentMass = dynamicParent->GetMass(); G4VDecayChannel* decayChannel = decayTable->SelectADecayChannel(parentMass); if (decayChannel != nullptr) { decayProducts = decayChannel->DecayIt(parentMass); if (!decayProducts->IsChecked()) decayProducts->DumpInfo(); if (IsBoostToLabEnabled()) { // boost all decay products to laboratory frame decayProducts->Boost(dynamicParent->GetTotalEnergy(), dynamicParent->GetMomentumDirection()); } } else { decayProducts = new G4DecayProducts(*dynamicParent); } dynamicParent->SetPreAssignedDecayProducts(decayProducts); } } else { G4cout << "WARNING : already present preassign decay !" << G4endl; } } } } // G4cout << theStep->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName() << G4endl; // If the primary decays somewhere inside the World volume, get the information about the decay if (theStep->GetTrack()->GetParentID() == 0 && theStep->GetPostStepPoint()->GetProcessDefinedStep() != nullptr && theStep->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName().find("Decay") != std::string::npos) { // Get properties of the primary particle when it decays //--- Get values in different ways and check their consistency --- // Kinetic energy of the primary particle at the decay const G4double ekin_dynamicParticle = theStep->GetTrack()->GetDynamicParticle()->GetKineticEnergy(); const G4double ekin_track = theStep->GetTrack()->GetKineticEnergy(); const G4double ekin_postStepPoint = theStep->GetPostStepPoint()->GetKineticEnergy(); const G4double ekin_deltaMax = std::max(std::abs(ekin_dynamicParticle - ekin_track), std::abs(ekin_dynamicParticle - ekin_postStepPoint)); // G4cout << "\t ekin_deltaMax [eV] = " << ekin_deltaMax / CLHEP::eV << G4endl; const G4double ekin_val = ekin_dynamicParticle; // To be used later // Total energy of the primary particle at the decay const G4double etot_dynamicParticle = theStep->GetTrack()->GetDynamicParticle()->GetTotalEnergy(); const G4double etot_track = theStep->GetTrack()->GetTotalEnergy(); const G4double etot_postStepPoint = theStep->GetPostStepPoint()->GetTotalEnergy(); const G4double etot_deltaMax = std::max(std::abs(etot_dynamicParticle - etot_track), std::abs(etot_dynamicParticle - etot_postStepPoint)); // G4cout << "\t etot_deltaMax [eV] = " << etot_deltaMax / CLHEP::eV << G4endl; const G4double etot_val = etot_dynamicParticle; // To be used later // Module of the 3-momentum of the primary particle at the decay const G4double p_dynamicParticle = theStep->GetTrack()->GetDynamicParticle()->GetMomentum().mag(); const G4double p_track = theStep->GetTrack()->GetMomentum().mag(); const G4double p_postStepPoint = theStep->GetPostStepPoint()->GetMomentum().mag(); const G4double p_deltaMax = std::max(std::abs(p_dynamicParticle - p_track), std::abs(p_dynamicParticle - p_postStepPoint)); // G4cout << "\t p_deltaMax [eV] = " << p_deltaMax / CLHEP::eV << G4endl; const G4double p_val = p_dynamicParticle; // To be used later // 3-momentum direction (adimensional) of the primary particle at the decay const G4ThreeVector pdir_dynamicParticle = theStep->GetTrack()->GetDynamicParticle()->GetMomentumDirection(); const G4ThreeVector pdir_track = theStep->GetTrack()->GetMomentumDirection(); const G4ThreeVector pdir_postStepPoint = theStep->GetPostStepPoint()->GetMomentumDirection(); const G4double pdir_x_deltaMax = std::max(std::abs(pdir_dynamicParticle.x() - pdir_track.x()), std::abs(pdir_dynamicParticle.x() - pdir_postStepPoint.x())); const G4double pdir_y_deltaMax = std::max(std::abs(pdir_dynamicParticle.y() - pdir_track.y()), std::abs(pdir_dynamicParticle.y() - pdir_postStepPoint.y())); const G4double pdir_z_deltaMax = std::max(std::abs(pdir_dynamicParticle.z() - pdir_track.z()), std::abs(pdir_dynamicParticle.z() - pdir_postStepPoint.z())); const G4double pdir_deltaMax = std::max(std::max(pdir_x_deltaMax, pdir_y_deltaMax), pdir_z_deltaMax); // G4cout << "\t pdir_deltaMax = " << pdir_deltaMax << G4endl; // Mass of the primary particle at the decay const G4double mass_dynamicParticle = theStep->GetTrack()->GetDynamicParticle()->GetMass(); const G4double mass_preStepPoint = theStep->GetPreStepPoint()->GetMass(); const G4double mass_postStepPoint = theStep->GetPostStepPoint()->GetMass(); const G4double mass_from_etot_ekin = etot_val - ekin_val; const G4double mass_from4mom = std::sqrt(etot_val * etot_val - p_val * p_val); G4double mass_deltaMax1 = std::max(std::abs(mass_dynamicParticle - mass_preStepPoint), std::abs(mass_dynamicParticle - mass_postStepPoint)); G4double mass_deltaMax2 = std::abs(mass_dynamicParticle - mass_from_etot_ekin); G4double mass_deltaMax3 = std::abs(mass_dynamicParticle - mass_from4mom); fMeanMass_deltaMax3 += mass_deltaMax3; // G4cout << "\t mass_deltaMax{1,2,3} [eV] = " << mass_deltaMax1 / CLHEP::eV << "\t" // << mass_deltaMax2 / CLHEP::eV << "\t" << mass_deltaMax3 / CLHEP::eV << G4endl; const G4double mass_val = mass_dynamicParticle; // To be used later // Lorentz beta of the primary particle at the decay // The following line works only for G4 versions >= 10.7 const G4double beta_dynamicParticle = theStep->GetTrack()->GetDynamicParticle()->GetBeta(); const G4double beta_postStepPoint = theStep->GetPostStepPoint()->GetBeta(); // Before-10.7 const G4double beta_dynamicParticle = beta_postStepPoint; const G4double beta_velocity_track = theStep->GetTrack()->GetVelocity() / CLHEP::c_light; const G4double beta_velocity_postStepPoint = theStep->GetPostStepPoint()->GetVelocity() / CLHEP::c_light; const G4double beta_p_over_etot = p_val / etot_val; G4double beta_deltaMax1 = std::max(std::abs(beta_dynamicParticle - beta_postStepPoint), std::abs(beta_dynamicParticle - beta_velocity_track)); beta_deltaMax1 = std::max(beta_deltaMax1, std::abs(beta_dynamicParticle - beta_velocity_postStepPoint)); const G4double beta_deltaMax2 = std::abs(beta_dynamicParticle - beta_p_over_etot); // G4cout << "\t beta_deltaMax{1,2} = " << beta_deltaMax1 << " , " << beta_deltaMax2 << G4endl; const G4double beta_val = beta_dynamicParticle; // To be used later // Lorentz gamma of the primary particle at the decay const G4double gamma_postStepPoint = theStep->GetPostStepPoint()->GetGamma(); const G4double gamma_from_e_over_m = etot_val / mass_val; const G4double gamma_deltaMax1 = std::abs(gamma_postStepPoint - gamma_from_e_over_m); G4double gamma_from_beta = 0.0; G4double gamma_deltaMax2 = 0.0; G4double gamma_deltaMax3 = 0.0; if (beta_val < 1.0) { gamma_from_beta = 1.0 / std::sqrt(1.0 - beta_val * beta_val); gamma_deltaMax2 = std::abs(gamma_postStepPoint - gamma_from_beta); gamma_deltaMax3 = std::abs(gamma_from_e_over_m - gamma_from_beta); } const G4double gamma_val = gamma_postStepPoint; // To be used later; // G4cout << "\t gamma_deltaMax{1,2,3} = " << gamma_deltaMax1 << " , " << gamma_deltaMax2 // << " , " << gamma_deltaMax3 << " ; gamma_postStepPoint = " << gamma_postStepPoint // << " ; gamma_from_e_over_m = " << gamma_from_e_over_m << " ; gamma_from_beta = " // << gamma_from_beta << G4endl; // Proper time of the primary particle at the decay const G4double t_proper_track = theStep->GetTrack()->GetProperTime(); const G4double t_proper_postStepPoint = theStep->GetPostStepPoint()->GetProperTime(); const G4double t_proper_deltaMax = std::abs(t_proper_track - t_proper_postStepPoint); // G4cout << "\t t_proper_deltaMax [fs] = " << t_proper_deltaMax / femtosecond << G4endl; const G4double t_proper_val = t_proper_track; // To be used later // Lab time of the primary particle at the decay // (Note: it would be wrong to trying to compute this lab time from the // above proper time via the simple formula: // const G4double t_lab_from_gamma = t_proper_val * gamma_val; // because the gamma value of the primary particle has changed // during its lifetime.) const G4double t_local_track = theStep->GetTrack()->GetLocalTime(); const G4double t_local_postStepPoint = theStep->GetPostStepPoint()->GetLocalTime(); const G4double t_global_track = theStep->GetTrack()->GetGlobalTime(); const G4double t_global_postStepPoint = theStep->GetPostStepPoint()->GetGlobalTime(); G4double t_lab_deltaMax = std::max(std::abs(t_local_track - t_local_postStepPoint), std::abs(t_local_track - t_global_track)); t_lab_deltaMax = std::max(t_lab_deltaMax, std::abs(t_local_track - t_global_postStepPoint)); // G4cout << "\t t_lab_deltaMax [fs] = " << t_lab_deltaMax / femtosecond << G4endl; const G4double t_lab_val = t_local_track; // To be used later // "MC-truth" decay radius of the primary particle at the decay // (defined as the one that would happen if there are neither magnetic field effects // nor interactions with matter). const G4double primaryBeta = fPrimaryParticleInitialMomentum / fPrimaryParticleInitialTotalEnergy; const G4double mc_truth_rPos1 = t_lab_val * fPrimaryParticleInitialBeta * CLHEP::c_light; const G4double mc_truth_rPos2 = t_lab_val * primaryBeta * CLHEP::c_light; const G4double mc_truth_rPos_deltaMax = std::abs(mc_truth_rPos1 - mc_truth_rPos2); fMeanMc_truth_rPos_deltaMax += mc_truth_rPos_deltaMax; // G4cout << "\t mc_truth_rPos_deltaMax [mum] = " // << mc_truth_rPos_deltaMax / CLHEP::micrometer << G4endl; if (mc_truth_rPos_deltaMax > ToleranceDeltaDecayRadius()) { // G4cout << std::setprecision(6) // << " Large : mc_truth_rPos_deltaMax [mum]=" // << mc_truth_rPos_deltaMax / CLHEP::micrometer // << " ; " << mc_truth_rPos1 << " , " << mc_truth_rPos2 << " mm" << G4endl; if (fRunPtr) fRunPtr->IncrementNumber_mc_truth_rPos_deltaMax_above(); } const G4double mc_truth_rPos_val = mc_truth_rPos1; // To be used later // Keep note of the biggest discrepancies fMaxEkin_deltaMax = std::max(fMaxEkin_deltaMax, ekin_deltaMax); fMaxEtot_deltaMax = std::max(fMaxEtot_deltaMax, etot_deltaMax); fMaxP_deltaMax = std::max(fMaxP_deltaMax, p_deltaMax); fMaxPdir_deltaMax = std::max(fMaxPdir_deltaMax, pdir_deltaMax); fMaxMass_deltaMax1 = std::max(fMaxMass_deltaMax1, mass_deltaMax1); fMaxMass_deltaMax2 = std::max(fMaxMass_deltaMax2, mass_deltaMax2); fMaxMass_deltaMax3 = std::max(fMaxMass_deltaMax3, mass_deltaMax3); fMaxBeta_deltaMax1 = std::max(fMaxBeta_deltaMax1, beta_deltaMax1); fMaxBeta_deltaMax2 = std::max(fMaxBeta_deltaMax2, beta_deltaMax2); fMaxGamma_deltaMax1 = std::max(fMaxGamma_deltaMax1, gamma_deltaMax1); fMaxGamma_deltaMax2 = std::max(fMaxGamma_deltaMax2, gamma_deltaMax2); fMaxGamma_deltaMax3 = std::max(fMaxGamma_deltaMax3, gamma_deltaMax3); fMaxT_lab_deltaMax = std::max(fMaxT_lab_deltaMax, t_lab_deltaMax); fMaxT_proper_deltaMax = std::max(fMaxT_proper_deltaMax, t_proper_deltaMax); fMaxMc_truth_rPos_deltaMax = std::max(fMaxMc_truth_rPos_deltaMax, mc_truth_rPos_deltaMax); //--- End consistency checks --- // Global position const G4double xPos = theStep->GetPostStepPoint()->GetPosition().x(); const G4double yPos = theStep->GetPostStepPoint()->GetPosition().y(); const G4double zPos = theStep->GetPostStepPoint()->GetPosition().z(); const G4double rPos = std::sqrt(xPos * xPos + yPos * yPos + zPos * zPos); // I have verified that for this case in which only primaries are considered, the // "GetGlobalTime()" is the same as "GetLocalTime()" (the one we use). // Moreover, this value is also the same as "GetProperTime()"*gamma . G4double tPos = theStep->GetPostStepPoint()->GetLocalTime(); // The "MC-truth" decay radius is defined as the one that would happen if there are // neither magnetic field effects nor interactions with matter. const G4double mc_truth_rPos = tPos * fPrimaryParticleInitialBeta * CLHEP::c_light; const G4double rDeltaPos = mc_truth_rPos - rPos; const G4double eKin = theStep->GetPostStepPoint()->GetKineticEnergy(); const G4double xMom = theStep->GetPostStepPoint()->GetMomentum().x(); const G4double yMom = theStep->GetPostStepPoint()->GetMomentum().y(); const G4double zMom = theStep->GetPostStepPoint()->GetMomentum().z(); // The compute here the angular deflection, in degrees, between the initial direction of // the primary particle - which is along the x-axis, and its direction when it decays. G4double xDirection = std::min(theStep->GetPostStepPoint()->GetMomentumDirection().x(), 1.0); if (xDirection < -1.0) xDirection = -1.0; const G4double deflection_angle_in_degrees = 57.29 * std::acos(xDirection); const G4double delta_ekin = fPrimaryParticleInitialKineticEnergy - eKin; // G4cout << std::setprecision(6) // << " Decay: tPos[ns]=" << tPos << " ; rPos[mm]=" << rPos << " ; deltaR[mum]=" // << rDeltaPos /CLHEP::micrometer << " ; deltaEkin[MeV]=" << delta_ekin // << " ; deltaAngle(deg)=" << deflection_angle_in_degrees << G4endl; // If the absolute difference between the "MC-truth" decay radius and the real one is above a // given threshold, then we notify this special situation in the output, with "LARGE_DELTA_R" // for post-processing evaluation. Moreover, in this case, if the "MC-truth" decay radius is // smaller than the real one, then we count this unexpected occurrence and we further notify // this special situation in the output with "***UNEXPECTED***" for post-processing // evaluation. if (std::abs(rDeltaPos) > ToleranceDeltaDecayRadius()) { // G4cout << "\t LARGE_DELTA_R : mc_truth_rPos[mm]=" << mc_truth_rPos // << " ; rPos[mm]=" << rPos; if (rDeltaPos < 0.0) { // G4cout << "\t ***UNEXPECTED***"; if (fRunPtr) fRunPtr->IncrementNumberUnexpectedDecays(); } // G4cout << G4endl; } fMeanDeltaR_primaryDecay += rDeltaPos; fMinDeltaR_primaryDecay = std::min(fMinDeltaR_primaryDecay, rDeltaPos); fMaxDeltaR_primaryDecay = std::max(fMaxDeltaR_primaryDecay, rDeltaPos); fMeanR_primaryDecay += rPos; fMinR_primaryDecay = std::min(fMinR_primaryDecay, rPos); fMaxR_primaryDecay = std::max(fMaxR_primaryDecay, rPos); fMeanX_primaryDecay += xPos; fMinX_primaryDecay = std::min(fMinX_primaryDecay, xPos); fMaxX_primaryDecay = std::max(fMaxX_primaryDecay, xPos); fMeanY_primaryDecay += yPos; fMinY_primaryDecay = std::min(fMinY_primaryDecay, yPos); fMaxY_primaryDecay = std::max(fMaxY_primaryDecay, yPos); fMeanZ_primaryDecay += zPos; fMinZ_primaryDecay = std::min(fMinZ_primaryDecay, zPos); fMaxZ_primaryDecay = std::max(fMaxZ_primaryDecay, zPos); fMeanDeltaAngle_primaryDecay += deflection_angle_in_degrees; fMinDeltaAngle_primaryDecay = std::min(fMinDeltaAngle_primaryDecay, deflection_angle_in_degrees); fMaxDeltaAngle_primaryDecay = std::max(fMaxDeltaAngle_primaryDecay, deflection_angle_in_degrees); fMeanDeltaEkin_primaryDecay += delta_ekin; fMinDeltaEkin_primaryDecay = std::min(fMinDeltaEkin_primaryDecay, delta_ekin); fMaxDeltaEkin_primaryDecay = std::max(fMaxDeltaEkin_primaryDecay, delta_ekin); fMeanEkin_primaryDecay += eKin; fMinEkin_primaryDecay = std::min(fMinEkin_primaryDecay, eKin); fMaxEkin_primaryDecay = std::max(fMaxEkin_primaryDecay, eKin); fMeanPx_primaryDecay += xMom; fMinPx_primaryDecay = std::min(fMinPx_primaryDecay, xMom); fMaxPx_primaryDecay = std::max(fMaxPx_primaryDecay, xMom); fMeanPy_primaryDecay += yMom; fMinPy_primaryDecay = std::min(fMinPy_primaryDecay, yMom); fMaxPy_primaryDecay = std::max(fMaxPy_primaryDecay, yMom); fMeanPz_primaryDecay += zMom; fMinPz_primaryDecay = std::min(fMinPz_primaryDecay, zMom); fMaxPz_primaryDecay = std::max(fMaxPz_primaryDecay, zMom); //--- Extra checks --- // Compute the "MC-truth" decay radius using the proper time of the primary particle when // it decays. // To do this, we would need an "effective" or "average" Lorentz beta and gamma of the primary // particle during its lifetime, whereas in practice we have only the Lorentz beta and gamma // values at the beginning and at the end when it decays. So, we can get only either an // overestimate of the "MC-truth" decay radius - by using the initial Lorentz beta and gamma - // or an underestimate of it - by using the Lorentz beta and gamma at the decay. // We want to check the average values and the largest values of these wrong estimates. const G4double underestimated_mc_truth_rPos = t_proper_val * gamma_val * beta_val * CLHEP::c_light; const G4double overestimated_mc_truth_rPos = t_proper_val * fPrimaryParticleInitialGamma * fPrimaryParticleInitialBeta * CLHEP::c_light; const G4double underestimated_mc_truth_rPos_delta = underestimated_mc_truth_rPos - mc_truth_rPos_val; const G4double overestimated_mc_truth_rPos_delta = overestimated_mc_truth_rPos - mc_truth_rPos_val; fMeanUnderestimated_mc_truth_rPos_delta += underestimated_mc_truth_rPos_delta; fMeanOverestimated_mc_truth_rPos_delta += overestimated_mc_truth_rPos_delta; // G4cout << "\t underestimated_mc_truth_rPos_delta [mum] = " // << underestimated_mc_truth_rPos_delta / CLHEP::micrometer // << " ; overestimated_mc_truth_rPos_delta [mum] = " // << overestimated_mc_truth_rPos_delta / CLHEP::micrometer << G4endl; if (-underestimated_mc_truth_rPos_delta > ToleranceDeltaDecayRadius()) { // G4cout << std::setprecision(6) // << " Large : underestimated_mc_truth_rPos_delta [mum]=" // << underestimated_mc_truth_rPos_delta / CLHEP::micrometer // << " ; " << underestimated_mc_truth_rPos << " , " // << mc_truth_rPos_val << " mm" << G4endl; if (fRunPtr) fRunPtr->IncrementNumber_underestimated_mc_truth_rPos_delta_above(); } if (overestimated_mc_truth_rPos_delta > ToleranceDeltaDecayRadius()) { // G4cout << std::setprecision(6) // << " Large : overestimated_mc_truth_rPos_delta [mum]=" // << overestimated_mc_truth_rPos_delta / CLHEP::micrometer // << " ; " << overestimated_mc_truth_rPos << " , " // << mc_truth_rPos_val << " mm" << G4endl; if (fRunPtr) fRunPtr->IncrementNumber_overestimated_mc_truth_rPos_delta_above(); } const G4double underestimated_rDeltaPos = underestimated_mc_truth_rPos - rPos; const G4double overestimated_rDeltaPos = overestimated_mc_truth_rPos - rPos; fMeanUnderestimated_rDeltaPos += underestimated_rDeltaPos; fMeanOverestimated_rDeltaPos += overestimated_rDeltaPos; // G4cout << std::setprecision(6) // << "\t underestimated_rDeltaPos=" << underestimated_rDeltaPos/CLHEP::micrometer // << " ; overestimated_rDeltaPos=" << overestimated_rDeltaPos/CLHEP::micrometer // << " mum" << G4endl; if (-underestimated_rDeltaPos > ToleranceDeltaDecayRadius()) { if (fRunPtr) fRunPtr->IncrementNumberLargeUnderestimates(); } if (overestimated_rDeltaPos > ToleranceDeltaDecayRadius()) { if (fRunPtr) fRunPtr->IncrementNumberLargeOverestimates(); } // Keep note of the biggest discrepancies fMinUnderestimated_mc_truth_rPos_delta = std::min(fMinUnderestimated_mc_truth_rPos_delta, underestimated_mc_truth_rPos_delta); fMaxOverestimated_mc_truth_rPos_delta = std::max(fMaxOverestimated_mc_truth_rPos_delta, overestimated_mc_truth_rPos_delta); fMinUnderestimated_rDeltaPos = std::min(fMinUnderestimated_rDeltaPos, underestimated_rDeltaPos); fMaxOverestimated_rDeltaPos = std::max(fMaxOverestimated_rDeltaPos, overestimated_rDeltaPos); //--- End extra checks --- // Check numerical errors due to the use of float instead of double : try out // several, equivalent computations, taking the one with the largest numerical error. const G4float float_xPos = static_cast(theStep->GetPostStepPoint()->GetPosition().x()); const G4float float_yPos = static_cast(theStep->GetPostStepPoint()->GetPosition().y()); const G4float float_zPos = static_cast(theStep->GetPostStepPoint()->GetPosition().z()); const G4float float_rPos = std::sqrt(float_xPos * float_xPos + float_yPos * float_yPos + float_zPos * float_zPos); const G4float float_tPos = static_cast(theStep->GetPostStepPoint()->GetLocalTime()); const G4float float_initialBeta1 = static_cast(fPrimaryParticleInitialBeta); const G4float float_initialBeta2 = static_cast(fPrimaryParticleInitialMomentum) / static_cast(fPrimaryParticleInitialTotalEnergy); const G4float float_initialGamma = static_cast(fPrimaryParticleInitialGamma); const G4float float_initialBeta3 = std::sqrt(float_initialGamma * float_initialGamma - 1.0) / float_initialGamma; const G4float float_c_light = static_cast(CLHEP::c_light); const G4float float_mc_truth_rPos1 = float_tPos * float_initialBeta1 * float_c_light; const G4float float_mc_truth_rPos2 = float_tPos * float_initialBeta2 * float_c_light; const G4float float_mc_truth_rPos3 = float_tPos * float_initialBeta3 * float_c_light; const G4float float_rDeltaPos_0 = static_cast(rDeltaPos); const G4float float_rDeltaPos_1 = float_mc_truth_rPos1 - float_rPos; const G4float float_rDeltaPos_2 = float_mc_truth_rPos2 - float_rPos; const G4float float_rDeltaPos_3 = float_mc_truth_rPos3 - float_rPos; const G4float float_rDeltaPos_4 = static_cast(mc_truth_rPos) - float_rPos; const G4float float_rDeltaPos_5 = float_mc_truth_rPos1 - static_cast(rPos); const G4float float_rDeltaPos_6 = float_mc_truth_rPos2 - static_cast(rPos); const G4float float_rDeltaPos_7 = float_mc_truth_rPos3 - static_cast(rPos); G4double rDeltaPos_deltaMax = std::max(std::abs(float_rDeltaPos_0 - rDeltaPos), std::abs(float_rDeltaPos_1 - rDeltaPos)); rDeltaPos_deltaMax = std::max(rDeltaPos_deltaMax, std::abs(float_rDeltaPos_2 - rDeltaPos)); rDeltaPos_deltaMax = std::max(rDeltaPos_deltaMax, std::abs(float_rDeltaPos_3 - rDeltaPos)); rDeltaPos_deltaMax = std::max(rDeltaPos_deltaMax, std::abs(float_rDeltaPos_4 - rDeltaPos)); rDeltaPos_deltaMax = std::max(rDeltaPos_deltaMax, std::abs(float_rDeltaPos_5 - rDeltaPos)); rDeltaPos_deltaMax = std::max(rDeltaPos_deltaMax, std::abs(float_rDeltaPos_6 - rDeltaPos)); rDeltaPos_deltaMax = std::max(rDeltaPos_deltaMax, std::abs(float_rDeltaPos_7 - rDeltaPos)); // G4cout << std::setprecision(6) << " rDeltaPos_deltaMax[mum]=" // << rDeltaPos_deltaMax / CLHEP::micrometer << G4endl; fMaxFloat_rDeltaPos_deltaMax = std::max(fMaxFloat_rDeltaPos_deltaMax, rDeltaPos_deltaMax); // Get properties of the decay products and check the energy-momentum conservation of the // decay std::size_t nSec = theStep->GetNumberOfSecondariesInCurrentStep(); const std::vector* ptrVecSecondaries = theStep->GetSecondaryInCurrentStep(); G4double deltaE = 0.0, deltaPx = 0.0, deltaPy = 0.0, deltaPz = 0.0; if (nSec > 0 && ptrVecSecondaries != nullptr) { G4double sumEsecondaries = 0.0; G4ThreeVector sumPsecondaries(0.0, 0.0, 0.0); for (std::size_t i = 0; i < nSec; ++i) { if ((*ptrVecSecondaries)[i]) { sumEsecondaries += (*ptrVecSecondaries)[i]->GetTotalEnergy(); sumPsecondaries += (*ptrVecSecondaries)[i]->GetMomentum(); } } deltaE = sumEsecondaries - theStep->GetPostStepPoint()->GetTotalEnergy(); fMeanViolationE_primaryDecay += deltaE; fMinViolationE_primaryDecay = std::min(fMinViolationE_primaryDecay, deltaE); fMaxViolationE_primaryDecay = std::max(fMaxViolationE_primaryDecay, deltaE); if (std::abs(deltaE) > ToleranceEPviolations()) { if (fRunPtr) fRunPtr->IncrementNumberEviolations(); } deltaPx = sumPsecondaries.x() - xMom; fMeanViolationPx_primaryDecay += deltaPx; fMinViolationPx_primaryDecay = std::min(fMinViolationPx_primaryDecay, deltaPx); fMaxViolationPx_primaryDecay = std::max(fMaxViolationPx_primaryDecay, deltaPx); deltaPy = sumPsecondaries.y() - yMom; fMeanViolationPy_primaryDecay += deltaPy; fMinViolationPy_primaryDecay = std::min(fMinViolationPy_primaryDecay, deltaPy); fMaxViolationPy_primaryDecay = std::max(fMaxViolationPy_primaryDecay, deltaPy); deltaPz = sumPsecondaries.z() - zMom; fMeanViolationPz_primaryDecay += deltaPz; fMinViolationPz_primaryDecay = std::min(fMinViolationPz_primaryDecay, deltaPz); fMaxViolationPz_primaryDecay = std::max(fMaxViolationPz_primaryDecay, deltaPz); if (std::abs(deltaPx) > ToleranceEPviolations() || std::abs(deltaPy) > ToleranceEPviolations() || std::abs(deltaPz) > ToleranceEPviolations()) { if (fRunPtr) fRunPtr->IncrementNumberPviolations(); } } else { if (fRunPtr) fRunPtr->IncrementNumberBadPrimaryDecays(); } if (fRunPtr) { fRunPtr->IncrementNumberDecays(); fRunPtr->SetDecayT(tPos); fRunPtr->SetDecayR_mc_truth(mc_truth_rPos); fRunPtr->SetDecayR(rPos); fRunPtr->SetDecayX(xPos); fRunPtr->SetDecayY(yPos); fRunPtr->SetDecayZ(zPos); fRunPtr->SetDeltaDecayR(rDeltaPos); fRunPtr->SetDeflectionAngle(deflection_angle_in_degrees); fRunPtr->SetDeltaEkin(delta_ekin); fRunPtr->SetDecayEkin(eKin); fRunPtr->SetDecayPx(xMom); fRunPtr->SetDecayPy(yMom); fRunPtr->SetDecayPz(zMom); fRunPtr->SetDecayEtotViolation(deltaE); fRunPtr->SetDecayPxViolation(deltaPx); fRunPtr->SetDecayPyViolation(deltaPy); fRunPtr->SetDecayPzViolation(deltaPz); fRunPtr->SetMaxEkin_deltaMax(ekin_deltaMax); fRunPtr->SetMaxEtot_deltaMax(etot_deltaMax); fRunPtr->SetMaxP_deltaMax(p_deltaMax); fRunPtr->SetMaxPdir_deltaMax(pdir_deltaMax); fRunPtr->SetMaxMass_deltaMax1(mass_deltaMax1); fRunPtr->SetMaxMass_deltaMax2(mass_deltaMax2); fRunPtr->SetMaxMass_deltaMax3(mass_deltaMax3); fRunPtr->SetMaxBeta_deltaMax1(beta_deltaMax1); fRunPtr->SetMaxBeta_deltaMax2(beta_deltaMax2); fRunPtr->SetMaxGamma_deltaMax1(gamma_deltaMax1); fRunPtr->SetMaxGamma_deltaMax2(gamma_deltaMax2); fRunPtr->SetMaxGamma_deltaMax3(gamma_deltaMax3); fRunPtr->SetMaxT_proper_deltaMax(t_proper_deltaMax); fRunPtr->SetMaxT_lab_deltaMax(t_lab_deltaMax); fRunPtr->SetMaxMc_truth_rPos_deltaMax(mc_truth_rPos_deltaMax); fRunPtr->SetMinUnderestimated_mc_truth_rPos_delta(underestimated_mc_truth_rPos_delta); fRunPtr->SetMaxOverestimated_mc_truth_rPos_delta(overestimated_mc_truth_rPos_delta); fRunPtr->SetMinUnderestimated_rDeltaPos(underestimated_rDeltaPos); fRunPtr->SetMaxOverestimated_rDeltaPos(overestimated_rDeltaPos); fRunPtr->SetMaxFloat_rDeltaPos_deltaMax(fMaxFloat_rDeltaPos_deltaMax); } } } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......