From e1f6234fea6594db0991ec320aedaca723830c2a Mon Sep 17 00:00:00 2001 From: majacquet Date: Mon, 19 Feb 2024 18:47:12 +0100 Subject: [PATCH 01/82] Creation of a pseudo-transportation actor for photons --- core/opengate_core/opengate_core.cpp | 6 +- ...ateOptnComptSplittingForTransportation.cpp | 200 +++++++++++++ .../GateOptnComptSplittingForTransportation.h | 141 ++++++++++ .../opengate_lib/GateOptnForceFreeFlight.cpp | 129 +++++++++ .../opengate_lib/GateOptnForceFreeFlight.h | 107 +++++++ ...GateOptrComptPseudoTransportationActor.cpp | 264 ++++++++++++++++++ .../GateOptrComptPseudoTransportationActor.h | 121 ++++++++ ...GateOptrComptPseudoTransportationActor.cpp | 19 ++ opengate/actors/actorbuilders.py | 2 + opengate/actors/miscactors.py | 23 ++ .../src/test072_pseudo_transportation.py | 178 ++++++++++++ 11 files changed, 1187 insertions(+), 3 deletions(-) create mode 100644 core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h create mode 100644 core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h create mode 100644 core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h create mode 100644 core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp create mode 100644 opengate/tests/src/test072_pseudo_transportation.py diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index caa789c86..24f95db23 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -289,10 +289,10 @@ void init_GateSimulationStatisticsActor(py::module &); void init_GatePhaseSpaceActor(py::module &); -// void init_GateComptonSplittingActor(py::module &); - void init_GateOptrComptSplittingActor(py::module &m); +void init_GateOptrComptPseudoTransportationActor(py::module &m); + void init_GateBOptrBremSplittingActor(py::module &m); void init_G4VBiasingOperator(py::module &m); @@ -487,8 +487,8 @@ PYBIND11_MODULE(opengate_core, m) { init_GateLETActor(m); init_GateSimulationStatisticsActor(m); init_GatePhaseSpaceActor(m); - // init_GateComptonSplittingActor(m); init_GateBOptrBremSplittingActor(m); + init_GateOptrComptPseudoTransportationActor(m); init_GateOptrComptSplittingActor(m); init_GateHitsCollectionActor(m); init_GateMotionVolumeActor(m); diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp new file mode 100644 index 000000000..bb3ac7112 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp @@ -0,0 +1,200 @@ +// +// ******************************************************************** +// * 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 GateOptnComptSplittingForTransportation.cc +/// \brief Implementation of the GateOptnComptSplittingForTransportation class + +#include "GateOptnComptSplittingForTransportation.h" +#include "G4BiasingProcessInterface.hh" + +#include "G4DynamicParticle.hh" +#include "G4SystemOfUnits.hh" +#include "G4Gamma.hh" +#include "G4ParticleChange.hh" +#include "G4ParticleChangeForGamma.hh" +#include "G4VEmProcess.hh" +#include"G4Gamma.hh" +#include "G4ComptonScattering.hh" +#include "G4PhotoElectricEffect.hh" +#include "G4RayleighScattering.hh" +#include "G4GammaConversion.hh" +#include "G4Exception.hh" +#include "G4TrackStatus.hh" +#include "G4ProcessType.hh" +#include +#include "G4TrackingManager.hh" + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnComptSplittingForTransportation::GateOptnComptSplittingForTransportation(G4String name) + : G4VBiasingOperation(name), fSplittingFactor(1), fRussianRoulette(false), fParticleChange() {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnComptSplittingForTransportation::~GateOptnComptSplittingForTransportation() { +} + + + +G4VParticleChange *GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing(const G4BiasingProcessInterface *callingProcess, const G4Track *track,const G4Step *step, G4bool &) { + +//Here we generate for the first the compton process, given that this function (ApplyFinalStateBiasing) is called when there is a compton interaction +//Then the interaction location of the compton process will always be the same + + + G4double globalTime = step->GetTrack()->GetGlobalTime(); + const G4ThreeVector position = step->GetPostStepPoint()->GetPosition(); + G4int nCalls = 1; + G4int splittingFactor = ceil(fSplittingFactor); + G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor)/splittingFactor; + G4bool isRightAngle = false; + G4double gammaWeight = 0; + G4int nbSecondaries = 0; + G4VParticleChange* processFinalState = nullptr; + G4ParticleChangeForGamma* castedProcessInitFinalState = nullptr; + + processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + // In case we don't want to split (a bit faster) i.e no biaising or no splitting low weights particles. + + if ((fSplittingFactor == 1 && fRussianRoulette == false) || track->GetWeight() < fWeightThreshold) + return processFinalState; + + castedProcessInitFinalState = (G4ParticleChangeForGamma*) processFinalState; + nbSecondaries = processFinalState->GetNumberOfSecondaries(); + + + fParticleChange.Initialize(*track); + fParticleChange.ProposeWeight(track->GetWeight()); + fParticleChange.ProposeTrackStatus(castedProcessInitFinalState->GetTrackStatus()); + fParticleChange.ProposeEnergy(castedProcessInitFinalState->GetProposedKineticEnergy()); + fParticleChange.ProposeMomentumDirection(castedProcessInitFinalState->GetProposedMomentumDirection()); + fParticleChange.SetSecondaryWeightByProcess(true); + + + + +//If there is cut on secondary particles, there is a probability that the electron is not simulated +//Then, if the compton process created it, we add the gien electron to the ParticleChange object + if (nbSecondaries == 1) { + G4Track* initElectronTrack = castedProcessInitFinalState->GetSecondary(0); + initElectronTrack->SetWeight(track->GetWeight()); + fParticleChange.AddSecondary(initElectronTrack); + } + + processFinalState->Clear(); + castedProcessInitFinalState->Clear(); + + //There is here the biasing process : + // Since G4VParticleChange class does not allow to retrieve scattered gamma information, we need to cast the type G4ParticleChangeForGamma + // to the G4VParticleChange object. We then call the process (biasWrapper(compt)) fSplittingFactor times (Here, the difference with the other version + // of splitting is the primary particle will be killed and its weight does not count) to generate, at last, fSplittingFactor gamma + // according to the compton interaction process. If the gamma track is ok regarding the russian roulette algorithm (no russian roulette + //, or within the acceptance angle, or not killed by the RR process), we add it to the primary track. + // If an electron is generated (above the range cut), we also generate it. + // A tremendous advantage is there is no need to use by ourself Klein-Nishina formula or other. So, if the physics list used takes into account + // the doppler broadening or other fine effects, this will be also taken into account by the MC simulation. + // PS : The first gamma is then the primary particle, but all the other splitted particle (electron of course AND gamma) must be considered + // as secondary particles, even though generated gamma will not be cut here by the applied cut. + + G4int simulationTrackID = 0; + while (nCalls <= splittingFactor) { + gammaWeight = track->GetWeight() / fSplittingFactor; + G4double initGammaWeight = track->GetWeight(); + G4VParticleChange* processGammaSplittedFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + G4ParticleChangeForGamma* castedProcessGammaSplittedFinalState = (G4ParticleChangeForGamma*) processGammaSplittedFinalState; + const G4ThreeVector momentum = castedProcessGammaSplittedFinalState-> GetProposedMomentumDirection(); + G4double energy = castedProcessGammaSplittedFinalState-> GetProposedKineticEnergy(); + G4double cosTheta = fVectorDirector * castedProcessInitFinalState->GetProposedMomentumDirection(); + G4double theta = std::acos(cosTheta); + G4double splittingProbability = G4UniformRand(); + + if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { + if ((fRussianRoulette == true) && (theta > fMaxTheta)) { + G4double probability = G4UniformRand(); + if (probability < 1/fSplittingFactor) { + // Specific case where the russian roulette probability is 1/splitting. Each particle generated, with a 1/split probability + //will have a 1/split probability to survive with a final weight of Initial weights * 1/split * split = Initial weight + gammaWeight = gammaWeight*fSplittingFactor; + G4Track* gammaTrack = new G4Track(*track); + gammaTrack->SetWeight(gammaWeight); + gammaTrack->SetKineticEnergy(energy); + gammaTrack->SetMomentumDirection(momentum); + gammaTrack->SetPosition(position); + fParticleChange.AddSecondary(gammaTrack); + simulationTrackID ++; + if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { + G4Track* electronTrack = processGammaSplittedFinalState->GetSecondary(0); + electronTrack->SetWeight(gammaWeight); + fParticleChange.AddSecondary(electronTrack); + simulationTrackID ++; + } + } + } + + + if ((fRussianRoulette == false) || ((fRussianRoulette == true) && (theta <= fMaxTheta))) { + G4Track* gammaTrack = new G4Track(*track); + gammaTrack->SetWeight(gammaWeight); + gammaTrack->SetKineticEnergy(energy); + gammaTrack->SetMomentumDirection(momentum); + gammaTrack->SetPosition(position); + fParticleChange.AddSecondary(gammaTrack); + simulationTrackID ++; + if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { + G4Track* electronTrack = processGammaSplittedFinalState->GetSecondary(0); + electronTrack->SetWeight(gammaWeight); + fParticleChange.AddSecondary(electronTrack); + simulationTrackID ++; + } + } + } + nCalls++; + processGammaSplittedFinalState->Clear(); + castedProcessGammaSplittedFinalState->Clear(); + + + } + //Probe generation, sent in 5 directions, in order to provide informations before the track of the real photons about + //the geometries they will cross. + if (fUseProbes) { + std::vector v = {{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}}; + + for (G4int nbProbe = 0; nbProbe < 5; nbProbe++) { + G4Track* gammaTrack = new G4Track(*track); + gammaTrack->SetGoodForTrackingFlag(1); + gammaTrack->SetWeight(track->GetWeight() / fSplittingFactor); + gammaTrack->SetKineticEnergy(track->GetKineticEnergy()); + gammaTrack->SetMomentumDirection(fRot * v[nbProbe]); + gammaTrack->SetPosition(position); + fParticleChange.AddSecondary(gammaTrack); + simulationTrackID ++; + + } + } + return &fParticleChange; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h new file mode 100644 index 000000000..c0d8580aa --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h @@ -0,0 +1,141 @@ +// +// ******************************************************************** +// * 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 GateOptnComptSplittingForTransportation.h +/// \brief Definition of the GateOptnComptSplittingForTransportation class +// + +#ifndef GateOptnComptSplittingForTransportation_h +#define GateOptnComptSplittingForTransportation_h 1 + +#include "G4ParticleChange.hh" +#include "G4VBiasingOperation.hh" + +class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { +public: + // -- Constructor : + GateOptnComptSplittingForTransportation(G4String name); + + // -- destructor: + virtual ~GateOptnComptSplittingForTransportation(); + +public: + // ---------------------------------------------- + // -- Methods from G4VBiasingOperation interface: + // ---------------------------------------------- + // -- Unused: + virtual const G4VBiasingInteractionLaw * + ProvideOccurenceBiasingInteractionLaw(const G4BiasingProcessInterface *, + G4ForceCondition &) { + return 0; + } + + // --Used: + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); + + // -- Unsued: + virtual G4double DistanceToApplyOperation(const G4Track *, G4double, + G4ForceCondition *) { + return DBL_MAX; + } + virtual G4VParticleChange *GenerateBiasingFinalState(const G4Track *, + const G4Step *) { + return 0; + } + +public: + // ---------------------------------------------- + // -- Additional methods, specific to this class: + // ---------------------------------------------- + // -- Splitting factor: + void SetSplittingFactor(G4double splittingFactor) { + fSplittingFactor = splittingFactor; + } + G4double GetSplittingFactor() const { return fSplittingFactor; } + + +void SetWeightThreshold(G4double weightThreshold) { + fWeightThreshold = weightThreshold; + } + G4double GetWeightThreshold() const { return fWeightThreshold; } + + + void SetRussianRoulette(G4bool russianRoulette){ + fRussianRoulette = russianRoulette; + } + + G4bool GetRussianRoulette() const { return fRussianRoulette; } + + void SetVectorDirector(G4ThreeVector vectorDirector){ + fVectorDirector = vectorDirector; + } + + void SetRotationMatrix(G4RotationMatrix rot){ + fRot = rot; + } + + G4ThreeVector GetVectorDirector() const {return fVectorDirector;} + + + void SetMaxTheta(G4double maxTheta){ + fMaxTheta = maxTheta; + } + + G4double GetMaxTheta() const {return fMaxTheta;} + + void SetUseOfProbes(G4bool p){fUseProbes = p;} + + + + void SetMinWeightOfParticle(G4double minWeightOfParticle){ + fMinWeightOfParticle= minWeightOfParticle; + } + + G4double GetMinWeightOfParticle() const {return fMinWeightOfParticle;} + + + G4VParticleChange* GetParticleChange() { + G4VParticleChange* particleChange = &fParticleChange; + return particleChange;} + + +private: + G4double fSplittingFactor; + G4double fWeightThreshold; + G4ParticleChange fParticleChange; + G4bool fRussianRoulette; + G4ThreeVector fVectorDirector; + G4double fMaxTheta; + G4double fMinWeightOfParticle; + G4bool fUseProbes; + G4RotationMatrix fRot; + //G4DynamicParticle* fSplitParticle; + //G4Track* fGammaTrack; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp new file mode 100644 index 000000000..af0828b4e --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp @@ -0,0 +1,129 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +#include "GateOptnForceFreeFlight.h" +#include "G4ILawForceFreeFlight.hh" +#include "G4BiasingProcessInterface.hh" +#include "G4Step.hh" + + + +// This operator is used to transport the particle without interaction, and then correct the weight of the particle +//according the probablity for the photon to not interact within the matter. To do that, we use a GEANT4 modifed Interaction Law (G4ILawForceFreeFlight), which modify, +//for the biased process the probability to occur : Never. + +GateOptnForceFreeFlight ::GateOptnForceFreeFlight (G4String name) + : G4VBiasingOperation ( name ), + fOperationComplete ( true ) +{ + fForceFreeFlightInteractionLaw = new G4ILawForceFreeFlight("LawForOperation"+name); +} + +GateOptnForceFreeFlight ::~GateOptnForceFreeFlight () +{ + if ( fForceFreeFlightInteractionLaw ) delete fForceFreeFlightInteractionLaw; +} + + + +const G4VBiasingInteractionLaw* GateOptnForceFreeFlight ::ProvideOccurenceBiasingInteractionLaw +( const G4BiasingProcessInterface*, G4ForceCondition& proposeForceCondition ) +{ + fOperationComplete = false; + proposeForceCondition = Forced; + return fForceFreeFlightInteractionLaw; +} + +G4VParticleChange* GateOptnForceFreeFlight ::ApplyFinalStateBiasing( const G4BiasingProcessInterface* callingProcess, + const G4Track* track, + const G4Step* step, + G4bool& forceFinalState) +{ + + + // -- If the track is reaching the volume boundary, its free flight ends. In this case, its zero + // -- weight is brought back to non-zero value: its initial weight is restored by the first + // -- ApplyFinalStateBiasing operation called, and the weight for force free flight is applied + // -- is applied by each operation. + // -- If the track is not reaching the volume boundary, it zero weight flight continues. + fParticleChange.Initialize( *track ); + forceFinalState = true; + + fProposedWeight *= fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()]; + if (fUseProbes){ + if (track->IsGoodForTracking() ==0){ + + if (fProposedWeight < fRussianRouletteProbability * fMinWeight){ + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } + + if ((fProposedWeight < fMinWeight) && (fProposedWeight >= fRussianRouletteProbability * fMinWeight)) { + G4double probability = G4UniformRand(); + if (probability > fRussianRouletteProbability){ + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } + else { + fProposedWeight = fProposedWeight/fRussianRouletteProbability; + } + } + + fParticleChange.ProposeWeight(fProposedWeight); + fOperationComplete = true; + if (track->IsGoodForTracking() ==1){ + fParticleChange.ProposeWeight(fProposedWeight); + fOperationComplete = true; + } + } + } + else { + if (fProposedWeight < fRussianRouletteProbability * fMinWeight){ + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } + + if ((fProposedWeight < fMinWeight) && (fProposedWeight >= fRussianRouletteProbability * fMinWeight)) { + G4double probability = G4UniformRand(); + if (probability > fRussianRouletteProbability){ + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } + else { + fProposedWeight = fProposedWeight/fRussianRouletteProbability; + } + } + + fParticleChange.ProposeWeight(fProposedWeight); + fOperationComplete = true; + } +return &fParticleChange; +} + +void GateOptnForceFreeFlight ::AlongMoveBy( const G4BiasingProcessInterface* callingProcess, const G4Step*, G4double weightChange ) + +{ + fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()] = weightChange; +} diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h new file mode 100644 index 000000000..30c32692d --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h @@ -0,0 +1,107 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +// +// +//--------------------------------------------------------------- +// +// GateOptnForceFreeFlight +// +// Class Description: +// A G4VBiasingOperation physics-based biasing operation. +// If forces the physics process to not act on the track. +// In this implementation (meant for the ForceCollision +// operator) the free flight is done under zero weight for +// the track, and the action is meant to accumulate the weight +// change for making this uninteracting flight, +// cumulatedWeightChange. +// When the track reaches the current volume boundary, its +// weight is restored with value : +// initialWeight * cumulatedWeightChange +// +//--------------------------------------------------------------- +// Initial version Nov. 2013 M. Verderi +#ifndef GateOptnForceFreeFlight_h +#define GateOptnForceFreeFlight_h 1 + +#include "G4VBiasingOperation.hh" +#include "G4ForceCondition.hh" +#include "G4ParticleChange.hh" // -- ยงยง should add a dedicated "weight change only" particle change +class G4ILawForceFreeFlight; + + +class GateOptnForceFreeFlight : public G4VBiasingOperation { +public: + // -- Constructor : + GateOptnForceFreeFlight (G4String name); + // -- destructor: + virtual ~GateOptnForceFreeFlight (); + +public: + // -- Methods from G4VBiasingOperation interface: + // ------------------------------------------- + // -- Used: + virtual const G4VBiasingInteractionLaw* ProvideOccurenceBiasingInteractionLaw( const G4BiasingProcessInterface*, G4ForceCondition& ); + virtual void AlongMoveBy( const G4BiasingProcessInterface*, const G4Step*, G4double ); + virtual G4VParticleChange* ApplyFinalStateBiasing( const G4BiasingProcessInterface*, const G4Track*, const G4Step*, G4bool&); + // -- Unused: + virtual G4double DistanceToApplyOperation( const G4Track*, + G4double, + G4ForceCondition*) {return DBL_MAX;} + virtual G4VParticleChange* GenerateBiasingFinalState( const G4Track*, + const G4Step* ) {return 0;} + + +public: + // -- Additional methods, specific to this class: + // ---------------------------------------------- + // -- return concrete type of interaction law: + G4ILawForceFreeFlight* GetForceFreeFlightLaw() { + return fForceFreeFlightInteractionLaw; + } + // -- initialization for weight: + //void ResetInitialTrackWeight(G4double w) {fInitialTrackWeight = w; fCumulatedWeightChange = 1.0;} + + + void SetMinWeight(G4double w){fMinWeight = w;} + void SetUseOfProbes(G4bool p){fUseProbes = p;} + G4double GetTrackWeight(){return fProposedWeight;} + void SetTrackWeight(G4double w){fProposedWeight = w;} + void SetRussianRouletteProbability(G4double p){fRussianRouletteProbability= p;} + G4bool OperationComplete() const { return fOperationComplete; } + +private: + G4ILawForceFreeFlight* fForceFreeFlightInteractionLaw; + std::map fWeightChange; + G4bool fUseProbes; + G4double fMinWeight, + fRussianRouletteProbability; + G4ParticleChange fParticleChange; + G4bool fOperationComplete; + G4double fProposedWeight; + +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp new file mode 100644 index 000000000..0a9d3799b --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -0,0 +1,264 @@ +// +// ******************************************************************** +// * 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 GateOptrComptPseudoTransportationActor.cc +/// \brief Implementation of the GateOptrComptPseudoTransportationActor class + +#include "GateHelpersDict.h" +#include "GateHelpersImage.h" + +#include "G4BiasingProcessInterface.hh" +#include "G4Gamma.hh" +#include "G4LogicalVolumeStore.hh" +#include "G4PhysicalVolumeStore.hh" +#include "GateOptnComptSplittingForTransportation.h" +#include "GateOptrComptPseudoTransportationActor.h" +#include "G4ProcessManager.hh" +#include "G4VEmProcess.hh" +#include "G4EmCalculator.hh" +#include "G4ProcessVector.hh" +#include "G4ParticleTable.hh" +#include "G4Gamma.hh" +#include "CLHEP/Units/SystemOfUnits.h" +#include "G4TrackStatus.hh" +#include "G4UserTrackingAction.hh" +#include "G4RunManager.hh" + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor(py::dict &user_info) + : G4VBiasingOperator("ComptSplittingOperator"), + GateVActor(user_info, false) { + fMotherVolumeName = DictGetStr(user_info, "mother"); + fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); + fWeightThreshold = DictGetDouble(user_info, "weight_threshold"); + fRelativeMinWeightOfParticle = DictGetDouble(user_info, "relative_min_weight_of_particle"); + //Since the russian roulette uses as a probability 1/splitting, we need to have a double, + //but the splitting factor provided by the user is logically an int, so we need to change the type. + fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); + fRussianRoulette = DictGetBool(user_info, "russian_roulette"); + fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); + fMaxTheta = DictGetDouble(user_info,"max_theta"); + fRussianRouletteForFreeFlight = DictGetDouble(user_info,"russian_roulette_for_free_flight"); + fFreeFlightOperation = new GateOptnForceFreeFlight("freeFlightOperation"); + fComptSplittingOperation = new GateOptnComptSplittingForTransportation("comptSplittingOperation"); + fUseProbes = DictGetBool(user_info,"use_probes"); + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("BeginOfEventAction"); + fActions.insert("PostUserTrackingAction"); + isSplitted = false; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +void GateOptrComptPseudoTransportationActor::AttachAllLogicalDaughtersVolumes(G4LogicalVolume* volume){ + AttachTo(volume); + G4int nbOfDaughters = volume->GetNoDaughters(); + if (nbOfDaughters >0 ){ + for (int i = 0; i< nbOfDaughters;i++){ + G4LogicalVolume* logicalDaughtersVolume = volume->GetDaughter(i)->GetLogicalVolume(); + AttachAllLogicalDaughtersVolumes(logicalDaughtersVolume); + } + } +} + +void GateOptrComptPseudoTransportationActor::StartSimulationAction(){ + G4LogicalVolume* biasingVolume = G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + + //Here we need to attach all the daughters and daughters of daughters (...) to the biasing operator. + //To do that, I use the function AttachAllLogicalDaughtersVolumes. + AttachAllLogicalDaughtersVolumes(biasingVolume); + fComptSplittingOperation->SetSplittingFactor(fSplittingFactor); + fComptSplittingOperation->SetWeightThreshold(fWeightThreshold); + fComptSplittingOperation->SetMaxTheta(fMaxTheta); + fComptSplittingOperation->SetRussianRoulette(fRussianRoulette); + fComptSplittingOperation->SetUseOfProbes(fUseProbes); + fFreeFlightOperation->SetRussianRouletteProbability(fRussianRouletteForFreeFlight); + fFreeFlightOperation->SetUseOfProbes(fUseProbes); + + +} + +void GateOptrComptPseudoTransportationActor::StartRun() { + + // The way to behave of the russian roulette is the following : + // we provide a vector director and the theta angle acceptance, where theta = 0 is a vector colinear to the vector director + // Then if the track generated is on the acceptance angle, we add it to the primary track, and if it's not the case, we launch the russian roulette + if (fRotationVectorDirector){ + G4VPhysicalVolume* physBiasingVolume = G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume -> GetObjectRotationValue(); + fVectorDirector = rot * fVectorDirector; + fComptSplittingOperation->SetRotationMatrix(rot); + } + + fComptSplittingOperation->SetVectorDirector(fVectorDirector); + + +} + + + + +void GateOptrComptPseudoTransportationActor::SteppingAction (G4Step *step) { + + //The stepping action is used to kill particle we have too kill : + // - If the primary particle reach the biased boudary + // - KIll all the probes exiting the volume + // - Kill, if probes, particles wich have a weilght lower than the probes one + + if (fUseProbes) { + if ((fKillOthersParticles) && (step->GetTrack()->IsGoodForTracking() ==0)) + { + step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); + } + + if (step->GetPostStepPoint()->GetStepStatus()!= fWorldBoundary){ + if ((step->GetPostStepPoint()->GetPhysicalVolume ()->GetName() == "world") && (step->GetTrack()->IsGoodForTracking() == 1)){ + step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); + } + } + } + + if ((isSplitted ==true) && (step->GetPostStepPoint()->GetStepStatus()!= fWorldBoundary)){ + if ((step->GetPostStepPoint()->GetPhysicalVolume ()->GetName() == "world")) + { + step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); + isSplitted =false; + } + } + + + + +} + +void GateOptrComptPseudoTransportationActor::BeginOfEventAction(const G4Event *event) { + fKillOthersParticles = false; +} + +void GateOptrComptPseudoTransportationActor::StartTracking(const G4Track *track) { +fInitialWeight = track->GetWeight(); +} + + +//For the following operation the idea is the following : +//All the potential photon processes are biased. If a particle undergoes a compton interaction, we splitted it +//(ComptonSplittingForTransportation operation) and the particle generated are pseudo-transported with the ForceFreeFLight operation +// Since the occurence Biaising operation is called at the beginning of each track, and propose a different way to track the particle +//(with modified physics), it here returns other thing than 0 if we want to pseudo-transport the particle, so if its creatorProcess is the +//modified compton interaction + +G4VBiasingOperation * +GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation(const G4Track* track, const G4BiasingProcessInterface* callingProcess) +{ + + +if (track->GetCreatorProcess () !=0){ + if (track->GetCreatorProcess ()->GetProcessName() == "biasWrapper(compt)"){ + fFreeFlightOperation->SetMinWeight(fInitialWeight/fRelativeMinWeightOfParticle); + fFreeFlightOperation->SetTrackWeight(track->GetWeight()); + return fFreeFlightOperation; + } + + } + return 0; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +// Here we call the final state biasing operation called if one of the biased interaction (all photon interaction here) occurs. +//That's why we need here to apply some conditions to just split the initial track. + +G4VBiasingOperation * +GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( + const G4Track *track, const G4BiasingProcessInterface *callingProcess) { + G4String callingProcessName = "biasWrapper(compt)"; + + if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt") + { + if (track->GetCreatorProcess() ==0) { + isSplitted = true; + return fComptSplittingOperation; + } + if (track->GetCreatorProcess()-> GetProcessName() != "biasWrapper(compt)"){ + isSplitted = true; + return fComptSplittingOperation; + } + } + /* + */ + if (track->GetCreatorProcess() !=0){ + if(track->GetCreatorProcess()-> GetProcessName() == "biasWrapper(compt)"){ + return callingProcess->GetCurrentOccurenceBiasingOperation(); + } + } + + return 0; + + //return 0; +} + + + +void GateOptrComptPseudoTransportationActor::EndTracking() { +isSplitted =false; +} + + +void GateOptrComptPseudoTransportationActor::PostUserTrackingAction(const G4Track *track) { + +if (fUseProbes){ + if (track->IsGoodForTracking() == 1){ + G4double tmpWeight = fFreeFlightOperation->GetTrackWeight(); + if (NbOfProbe == 1) + { + fKillOthersParticles = false; + weight = tmpWeight; + } + else{ + if (tmpWeight > weight){ + weight = tmpWeight; + } + } + + NbOfProbe ++; + if ((NbOfProbe == 6)){ + if (weight < fInitialWeight/fRelativeMinWeightOfParticle){ + fKillOthersParticles = true; + } + NbOfProbe = 1; + weight = 0; + + } + } +} +} + + + + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h new file mode 100644 index 000000000..e4a7d1e26 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -0,0 +1,121 @@ +// +// ******************************************************************** +// * 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 GateOptrComptPseudoTransportationActor.h +/// \brief Definition of the GateOptrComptPseudoTransportationActor class +#ifndef GateOptrComptPseudoTransportationActor_h +#define GateOptrComptPseudoTransportationActor_h 1 + +#include "G4VBiasingOperator.hh" +#include "GateOptnForceFreeFlight.h" +#include "G4EmCalculator.hh" + +#include "GateVActor.h" +#include +#include +namespace py = pybind11; + +class GateOptnComptSplittingForTransportation; + +class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, + public GateVActor { +public: + GateOptrComptPseudoTransportationActor(py::dict &user_info); + virtual ~GateOptrComptPseudoTransportationActor() {} + +public: + // ------------------------- + // Optional from base class: + // ------------------------- + // -- Call at run start: + // virtual void BeginOfRunAction(const G4Run *run); + + // virtual void SteppingAction(G4Step* step); + + // -- Call at each track starting: + // virtual void PreUserTrackingAction( const G4Track* track ); + + G4double fSplittingFactor; + G4double fInitialWeight; + G4double fRelativeMinWeightOfParticle; + G4double fWeightThreshold; + G4bool fBiasPrimaryOnly; + G4bool fBiasOnlyOnce; + G4int fNInteractions = 0; + G4bool fRussianRoulette; + G4double fRussianRouletteForFreeFlight; + G4bool fRotationVectorDirector; + G4ThreeVector fVectorDirector; + G4double fMaxTheta; + G4bool isSplitted; + G4int NbOfTrack = 0; + G4int NbOfProbe = 1; + G4double weight = 0; + G4bool fKillOthersParticles = false; + G4bool fUseProbes = false; + // Unused but mandatory + + virtual void StartSimulationAction(); + virtual void StartRun(); + virtual void StartTracking(const G4Track *); + virtual void PostUserTrackingAction(const G4Track * track); + virtual void SteppingAction(G4Step*); + virtual void BeginOfEventAction(const G4Event*); + virtual void EndTracking(); + + +protected: + // ----------------------------- + // -- Mandatory from base class: + // ----------------------------- + // -- Unused: + void AttachAllLogicalDaughtersVolumes(G4LogicalVolume*); + virtual G4VBiasingOperation *ProposeNonPhysicsBiasingOperation( + const G4Track * /* track */, + const G4BiasingProcessInterface * /* callingProcess */) { + return 0; + } + + + // -- Used: + virtual G4VBiasingOperation *ProposeOccurenceBiasingOperation( + const G4Track * /* track */, + const G4BiasingProcessInterface * /* callingProcess */); + + virtual G4VBiasingOperation *ProposeFinalStateBiasingOperation( + const G4Track *track, const G4BiasingProcessInterface *callingProcess); + +private: + // -- Avoid compiler complaining for (wrong) method shadowing, + // -- this is because other virtual method with same name exists. + using G4VBiasingOperator::OperationApplied; + +private: + GateOptnForceFreeFlight *fFreeFlightOperation ; + GateOptnComptSplittingForTransportation *fComptSplittingOperation ; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp new file mode 100644 index 000000000..ae0344c08 --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp @@ -0,0 +1,19 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ +#include + +namespace py = pybind11; +#include "G4VBiasingOperator.hh" +#include "GateOptrComptPseudoTransportationActor.h" + +void init_GateOptrComptPseudoTransportationActor(py::module &m) { + + py::class_>( + m, "GateOptrComptPseudoTransportationActor") + .def(py::init()); +} diff --git a/opengate/actors/actorbuilders.py b/opengate/actors/actorbuilders.py index b93c54eb9..66d43925e 100644 --- a/opengate/actors/actorbuilders.py +++ b/opengate/actors/actorbuilders.py @@ -19,6 +19,7 @@ KillActor, BremSplittingActor, ComptSplittingActor, + ComptPseudoTransportationActor, ) from ..utility import make_builders @@ -44,5 +45,6 @@ KillActor, BremSplittingActor, ComptSplittingActor, + ComptPseudoTransportationActor, } actor_builders = make_builders(actor_type_names) diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 9d3aa9f35..0ad3ac911 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -463,6 +463,29 @@ def __init__(self, user_info): g4.GateOptrComptSplittingActor.__init__(self, user_info.__dict__) +class ComptPseudoTransportationActor(g4.GateOptrComptPseudoTransportationActor, ActorBase): + type_name = "ComptPseudoTransportationActor" + def set_default_user_info(user_info): + ActorBase.set_default_user_info(user_info) + deg = g4_units.deg + user_info.splitting_factor = 1 + user_info.weight_threshold = 0 + user_info.bias_primary_only = True + user_info.relative_min_weight_of_particle = 0 + user_info.bias_only_once = True + user_info.processes = ["compt","phot","conv","Rayl"] + user_info.russian_roulette = False + user_info.rotation_vector_director = False + user_info.vector_director = [0,0,1] + user_info.max_theta = 90*deg + user_info.russian_roulette_for_free_flight = 1/10 + user_info.use_probes = False; + + def __init__(self, user_info): + ActorBase.__init__(self, user_info) + g4.GateOptrComptPseudoTransportationActor.__init__(self, user_info.__dict__) + + class BremSplittingActor(g4.GateBOptrBremSplittingActor, ActorBase): type_name = "BremSplittingActor" diff --git a/opengate/tests/src/test072_pseudo_transportation.py b/opengate/tests/src/test072_pseudo_transportation.py new file mode 100644 index 000000000..967d8c4d0 --- /dev/null +++ b/opengate/tests/src/test072_pseudo_transportation.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +import uproot +import numpy as np +import matplotlib.pyplot as plt +import matplotlib as mpl +import scipy +from scipy.spatial.transform import Rotation +from opengate.tests import utility + + + +def validation_test(arr,NIST_data,nb_split,tol = 0.01): + tab_ekin = NIST_data[:, 0] + mu_att = NIST_data[:, -2] + + log_ekin = np.log(tab_ekin) + log_mu = np.log(mu_att) + + f_mu = scipy.interpolate.interp1d(log_ekin, log_mu, kind='cubic') + # print(arr["TrackCreatorProcess"]) + Tracks = arr[(arr["TrackCreatorProcess"] == 'biasWrapper(compt)') & (arr["KineticEnergy"] > 0.1) & (arr["ParticleName"] == "gamma") & (arr["Weight"] > 10**(-20))] + ekin_tracks = Tracks['KineticEnergy'] + x_vertex = Tracks["TrackVertexPosition_X"] + y_vertex = Tracks["TrackVertexPosition_Y"] + z_vertex = Tracks["TrackVertexPosition_Z"] + x = Tracks["PrePosition_X"] + y = Tracks["PrePosition_Y"] + z = Tracks["PrePosition_Z"] + weights = Tracks["Weight"] * nb_split + dist = np.sqrt((x-x_vertex)**2 + (y-y_vertex)**2 + (z-z_vertex)**2) + + G4_mu = -np.log(weights)/(0.1*dist*19.3) + X_com_mu = np.exp(f_mu(np.log(ekin_tracks))) + diff = (G4_mu - X_com_mu)/G4_mu + print("Median difference between mu calculated from XCOM database and from GEANT4 free flight operation:", np.round(100*np.median(diff),1),"%") + return np.median(diff) < tol + + + +if __name__ == "__main__": + paths = utility.get_default_test_paths( + __file__, "test072_pseudo_transportation", output_folder="test072" + ) + + # create the simulation + sim = gate.Simulation() + + # main options + ui = sim.user_info + ui.g4_verbose = False + + # ui.visu = True + # ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.EVENT + ui.number_of_threads = 1 + ui.random_seed = "auto" + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + um = gate.g4_units.um + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + gcm3 = gate.g4_units.g / gate.g4_units.cm3 + deg = gate.g4_units.deg + + # adapt world size + world = sim.world + world.size = [1.2 * m, 1.2 * m, 2 * m] + world.material = "G4_Galactic" + + ####### GEOMETRY TO IRRADIATE ############# + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + simple_collimation = sim.add_volume("Box", "colli_box") + simple_collimation.material = "G4_Galactic" + simple_collimation.mother = world.name + simple_collimation.size = [1 * m, 1 * m, 40 * cm] + simple_collimation.color = [0.3, 0.1, 0.8, 1] + + W_leaf = sim.add_volume("Box", "W_leaf") + W_leaf.mother = simple_collimation.name + W_leaf.size = [1*m,1*m,2*cm] + W_leaf.material = 'Tungsten' + leaf_translation =[] + for i in range(10): + leaf_translation.append([0,0, - 0.5*simple_collimation.size[2] + 0.5* W_leaf.size[2] + i * W_leaf.size[2]]) + print(leaf_translation) + W_leaf.translation =leaf_translation + W_leaf.color = [0.8, 0.2, 0.1, 1] + + ######## pseudo_transportation ACTOR ######### + nb_split = 5 + pseudo_transportation_actor = sim.add_actor("ComptPseudoTransportationActor", "pseudo_transportation_actor") + pseudo_transportation_actor.mother = simple_collimation.name + pseudo_transportation_actor.splitting_factor = nb_split + pseudo_transportation_actor.relative_min_weight_of_particle = np.inf + list_processes_to_bias = pseudo_transportation_actor.processes + + ##### PHASE SPACE plan ######" + plan= sim.add_volume("Box", "phsp") + plan.material = "G4_Galactic" + plan.mother = world.name + plan.size = [1*m,1*m,1*nm] + plan.color = [0.2, 1, 0.8, 1] + plan.translation = [0,0,- 20*cm - 1*nm] + + ####### gamma source ########### + source = sim.add_source("GenericSource", "source1") + source.particle = "gamma" + source.n = 1000 + source.position.type = "sphere" + source.position.radius = 1 * nm + source.direction.type = "momentum" + source.direction.momentum = [0, 0, -1] + source.energy.type = "mono" + source.energy.mono = 6 * MeV + source.position.translation = [0,0,18*cm] + + ####### PHASE SPACE ACTOR ############## + + phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") + phsp_actor.mother = plan.name + phsp_actor.attributes = [ + "EventID", + "TrackCreatorProcess", + "TrackVertexPosition", + "PrePosition", + "Weight", + "KineticEnergy", + "ParentID", + "ParticleName" + ] + + phsp_actor.output = paths.output / "test072_output_data.root" + + ##### MODIFIED PHYSICS LIST ############### + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + ### Perhaps avoid the user to call the below boolean function ? ### + sim.physics_manager.special_physics_constructors.G4GenericBiasingPhysics = True + sim.physics_manager.processes_to_bias.gamma = list_processes_to_bias + s = f"/process/em/UseGeneralProcess false" + sim.add_g4_command_before_init(s) + + + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * km + sim.physics_manager.global_production_cuts.positron = 1 * km + + output = sim.run() + + # + # # print results + stats = sim.output.get_actor("Stats") + h = sim.output.get_actor("PhaseSpace") + print(stats) + # + f_phsp = uproot.open(paths.output / "test072_output_data.root") + data_NIST_W = np.loadtxt(paths.data / "NIST_W.txt",delimiter = '|') + arr = f_phsp["PhaseSpace"].arrays() + # + is_ok = validation_test(arr,data_NIST_W,nb_split) + utility.test_ok(is_ok) From 7758dbd284133772df3533f9a834f3770a683c29 Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 21 Feb 2024 17:12:05 +0100 Subject: [PATCH 02/82] Correction of a bug linked to the russian roulette of particle weights --- .../opengate_lib/GateOptnForceFreeFlight.cpp | 50 +++++++++---------- .../opengate_lib/GateOptnForceFreeFlight.h | 5 ++ ...GateOptrComptPseudoTransportationActor.cpp | 5 +- .../GateOptrComptPseudoTransportationActor.h | 1 + 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp index af0828b4e..4bef07ce3 100644 --- a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp @@ -31,8 +31,10 @@ // This operator is used to transport the particle without interaction, and then correct the weight of the particle -//according the probablity for the photon to not interact within the matter. To do that, we use a GEANT4 modifed Interaction Law (G4ILawForceFreeFlight), which modify, -//for the biased process the probability to occur : Never. +//according to the probablity for the photon to not interact within the matter. To do that, we use a GEANT4 modifed Interaction Law (G4ILawForceFreeFlight), which modify, +//for the biased process the probability for the interaction to occur : Never. +//This occurence is called during the tracking for each step. Here the step is the largest possible, and one step correspond to the path of +//particle in the media. GateOptnForceFreeFlight ::GateOptnForceFreeFlight (G4String name) : G4VBiasingOperation ( name ), @@ -63,59 +65,57 @@ G4VParticleChange* GateOptnForceFreeFlight ::ApplyFinalStateBiasing( const G4Bia { - // -- If the track is reaching the volume boundary, its free flight ends. In this case, its zero - // -- weight is brought back to non-zero value: its initial weight is restored by the first - // -- ApplyFinalStateBiasing operation called, and the weight for force free flight is applied - // -- is applied by each operation. - // -- If the track is not reaching the volume boundary, it zero weight flight continues. fParticleChange.Initialize( *track ); forceFinalState = true; + fCountProcess ++; fProposedWeight *= fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()]; if (fUseProbes){ if (track->IsGoodForTracking() ==0){ - if (fProposedWeight < fRussianRouletteProbability * fMinWeight){ - fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); - return &fParticleChange; - } - - if ((fProposedWeight < fMinWeight) && (fProposedWeight >= fRussianRouletteProbability * fMinWeight)) { - G4double probability = G4UniformRand(); - if (probability > fRussianRouletteProbability){ + if (fProposedWeight < fRussianRouletteProbability * fMinWeight){ fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); return &fParticleChange; } - else { - fProposedWeight = fProposedWeight/fRussianRouletteProbability; + + if ((fProposedWeight < fMinWeight) && (fProposedWeight >= fRussianRouletteProbability * fMinWeight)) { + G4double probability = G4UniformRand(); + if (probability > fRussianRouletteProbability){ + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } + else { + fProposedWeight = fProposedWeight/fRussianRouletteProbability; + } } - } - fParticleChange.ProposeWeight(fProposedWeight); - fOperationComplete = true; - if (track->IsGoodForTracking() ==1){ fParticleChange.ProposeWeight(fProposedWeight); fOperationComplete = true; } - } + if (track->IsGoodForTracking() ==1){ + fParticleChange.ProposeWeight(fProposedWeight); + fOperationComplete = true; + } } else { - if (fProposedWeight < fRussianRouletteProbability * fMinWeight){ + if ((fProposedWeight < fRussianRouletteProbability * fMinWeight)|| ((fProposedWeight < fMinWeight) && (fSurvivedToRR == true))) { fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); return &fParticleChange; } - if ((fProposedWeight < fMinWeight) && (fProposedWeight >= fRussianRouletteProbability * fMinWeight)) { + if ((fProposedWeight < fMinWeight) && (fProposedWeight >= fRussianRouletteProbability * fMinWeight) && (fCountProcess == 4) && (fSurvivedToRR == false)) { G4double probability = G4UniformRand(); + if (probability > fRussianRouletteProbability){ fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); return &fParticleChange; } else { fProposedWeight = fProposedWeight/fRussianRouletteProbability; + //std::cout<<"lim "<< fMinWeight << " RR " <GetWeight(); +fFreeFlightOperation->SetSurvivedToRR(false); } @@ -175,12 +176,11 @@ fInitialWeight = track->GetWeight(); G4VBiasingOperation * GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation(const G4Track* track, const G4BiasingProcessInterface* callingProcess) { - - if (track->GetCreatorProcess () !=0){ if (track->GetCreatorProcess ()->GetProcessName() == "biasWrapper(compt)"){ fFreeFlightOperation->SetMinWeight(fInitialWeight/fRelativeMinWeightOfParticle); fFreeFlightOperation->SetTrackWeight(track->GetWeight()); + fFreeFlightOperation->SetCountProcess(0); return fFreeFlightOperation; } @@ -198,6 +198,7 @@ GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( const G4Track *track, const G4BiasingProcessInterface *callingProcess) { G4String callingProcessName = "biasWrapper(compt)"; + if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt") { if (track->GetCreatorProcess() ==0) { diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index e4a7d1e26..020ef2d73 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -76,6 +76,7 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4double weight = 0; G4bool fKillOthersParticles = false; G4bool fUseProbes = false; + G4bool fSurvivedRR = false; // Unused but mandatory virtual void StartSimulationAction(); From ad4544b0a5beba9cf6cee81975ccaaefea04a61d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:16:25 +0000 Subject: [PATCH 03/82] [pre-commit.ci] Automatic python and c++ formatting --- .../opengate_lib/GateOptnComptSplitting.cpp | 187 ++++++++------ .../opengate_lib/GateOptnComptSplitting.h | 38 ++- ...ateOptnComptSplittingForTransportation.cpp | 187 ++++++++------ .../GateOptnComptSplittingForTransportation.h | 46 ++-- .../opengate_lib/GateOptnForceFreeFlight.cpp | 145 ++++++----- .../opengate_lib/GateOptnForceFreeFlight.h | 82 +++--- ...GateOptrComptPseudoTransportationActor.cpp | 242 +++++++++--------- .../GateOptrComptPseudoTransportationActor.h | 20 +- .../GateOptrComptSplittingActor.cpp | 63 +++-- .../GateOptrComptSplittingActor.h | 3 +- ...GateOptrComptPseudoTransportationActor.cpp | 5 +- opengate/actors/miscactors.py | 20 +- opengate/managers.py | 6 +- .../src/test071_operator_russian_roulette.py | 70 ++--- .../src/test072_pseudo_transportation.py | 66 +++-- 15 files changed, 623 insertions(+), 557 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnComptSplitting.cpp index c4a5cde0b..8ee0ff264 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptnComptSplitting.cpp @@ -31,98 +31,114 @@ #include "G4BiasingProcessInterface.hh" #include "G4DynamicParticle.hh" -#include "G4SystemOfUnits.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" #include "G4Gamma.hh" #include "G4ParticleChange.hh" #include "G4ParticleChangeForGamma.hh" -#include"G4Gamma.hh" -#include "G4Exception.hh" +#include "G4SystemOfUnits.hh" #include "G4TrackStatus.hh" #include //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... GateOptnComptSplitting::GateOptnComptSplitting(G4String name) - : G4VBiasingOperation(name), fSplittingFactor(1), fRussianRoulette(false), fParticleChange() {} + : G4VBiasingOperation(name), fSplittingFactor(1), fRussianRoulette(false), + fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnComptSplitting::~GateOptnComptSplitting() { -} +GateOptnComptSplitting::~GateOptnComptSplitting() {} -G4VParticleChange *GateOptnComptSplitting::ApplyFinalStateBiasing(const G4BiasingProcessInterface *callingProcess, const G4Track *track,const G4Step *step, G4bool &) { +G4VParticleChange *GateOptnComptSplitting::ApplyFinalStateBiasing( + const G4BiasingProcessInterface *callingProcess, const G4Track *track, + const G4Step *step, G4bool &) { -//Here we generate for the first the "fake" compton process, given that this function (ApplyFinalStateBiasing) is called when there is a compton interaction -//Then the interaction location of the compton process will always be the same + // Here we generate for the first the "fake" compton process, given that this + // function (ApplyFinalStateBiasing) is called when there is a compton + // interaction Then the interaction location of the compton process will + // always be the same + // Initialisation of parameter for the split, because the photon is the + // primary particle, so it's a bit tricky -// Initialisation of parameter for the split, because the photon is the primary particle, so it's a bit tricky - G4double globalTime = step->GetTrack()->GetGlobalTime(); const G4ThreeVector position = step->GetPostStepPoint()->GetPosition(); G4int nCalls = 0; G4int splittingFactor = ceil(fSplittingFactor); - G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor)/splittingFactor; + G4double survivalProbabilitySplitting = + 1 - (splittingFactor - fSplittingFactor) / splittingFactor; G4bool isRightAngle = false; G4double gammaWeight = 0; G4int nbSecondaries = 0; - G4VParticleChange* processFinalState = nullptr; - G4ParticleChangeForGamma* castedProcessInitFinalState = nullptr; - + G4VParticleChange *processFinalState = nullptr; + G4ParticleChangeForGamma *castedProcessInitFinalState = nullptr; - while(isRightAngle ==false){ + while (isRightAngle == false) { gammaWeight = track->GetWeight() / fSplittingFactor; - processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - // In case we don't want to split (a bit faster) i.e no biaising or no splitting low weights particles. - - if ((fSplittingFactor == 1 && fRussianRoulette == false) || track->GetWeight() < fWeightThreshold) + processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + // In case we don't want to split (a bit faster) i.e no biaising or no + // splitting low weights particles. + + if ((fSplittingFactor == 1 && fRussianRoulette == false) || + track->GetWeight() < fWeightThreshold) return processFinalState; - - castedProcessInitFinalState = (G4ParticleChangeForGamma*) processFinalState; + + castedProcessInitFinalState = (G4ParticleChangeForGamma *)processFinalState; nbSecondaries = processFinalState->GetNumberOfSecondaries(); - G4ThreeVector initMomentum = castedProcessInitFinalState->GetProposedMomentumDirection(); - G4double cosTheta = fVectorDirector * initMomentum; + G4ThreeVector initMomentum = + castedProcessInitFinalState->GetProposedMomentumDirection(); + G4double cosTheta = fVectorDirector * initMomentum; G4double theta = std::acos(cosTheta); G4double splittingProbability = G4UniformRand(); - if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { - // If the number of compton interaction is too high, we simply return the process, instead of generating very low weight particles. + // If the number of compton interaction is too high, we simply return the + // process, instead of generating very low weight particles. if (track->GetWeight() <= fMinWeightOfParticle) { return processFinalState; } - // If the russian roulette is activated, we need to initialize the track with a primary particle which have the right angle - // That's why nCall is also incremented here, to avoid any bias in te number of gamma generated + // If the russian roulette is activated, we need to initialize the track + // with a primary particle which have the right angle That's why nCall is + // also incremented here, to avoid any bias in te number of gamma + // generated if ((fRussianRoulette == true) && (theta > fMaxTheta)) { G4double probability = G4UniformRand(); - if (probability < 1/fSplittingFactor) { - gammaWeight = gammaWeight*fSplittingFactor; + if (probability < 1 / fSplittingFactor) { + gammaWeight = gammaWeight * fSplittingFactor; isRightAngle = true; } } - if ((fRussianRoulette == false) || ((fRussianRoulette == true) && (theta <= fMaxTheta))) + if ((fRussianRoulette == false) || + ((fRussianRoulette == true) && (theta <= fMaxTheta))) isRightAngle = true; } - if (isRightAngle ==false) + if (isRightAngle == false) processFinalState->Clear(); - - // Little exception, if the splitting factor is too low compared to the acceptance angle, it's therefore possible to attain the splitting factor without - // any first track. For the moment, we kill the particle, since the russian roulette phenomena is applied and normally guaranties a non-biased operation. - if (nCalls >= fSplittingFactor){ + + // Little exception, if the splitting factor is too low compared to the + // acceptance angle, it's therefore possible to attain the splitting factor + // without any first track. For the moment, we kill the particle, since the + // russian roulette phenomena is applied and normally guaranties a + // non-biased operation. + if (nCalls >= fSplittingFactor) { fParticleChange.Initialize(*track); fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); return &fParticleChange; } - nCalls ++; + nCalls++; } - //Initialisation of the information about the track. - //We store the first gamma as the departure track, The first gamma is a primary particle but with its weight modified - //, since it can be one of the detected particles - + // Initialisation of the information about the track. + // We store the first gamma as the departure track, The first gamma is a + // primary particle but with its weight modified , since it can be one of the + //detected particles fParticleChange.Initialize(*track); fParticleChange.ProposeWeight(gammaWeight); @@ -135,11 +151,11 @@ G4VParticleChange *GateOptnComptSplitting::ApplyFinalStateBiasing(const G4Biasin fParticleChange.SetSecondaryWeightByProcess(true); - -//If there is cut on secondary particles, there is a probability that the electron is not simulated -//Then, if the compton process created it, we add the gien electron to the ParticleChange object + // If there is cut on secondary particles, there is a probability that the + // electron is not simulated Then, if the compton process created it, we add + // the gien electron to the ParticleChange object if (nbSecondaries == 1) { - G4Track* initElectronTrack = castedProcessInitFinalState->GetSecondary(0); + G4Track *initElectronTrack = castedProcessInitFinalState->GetSecondary(0); initElectronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(initElectronTrack); } @@ -147,61 +163,78 @@ G4VParticleChange *GateOptnComptSplitting::ApplyFinalStateBiasing(const G4Biasin processFinalState->Clear(); castedProcessInitFinalState->Clear(); - //There is here the biasing process : - // Since G4VParticleChange class does not allow to retrieve scattered gamma information, we need to cast the type G4ParticleChangeForGamma - // to the G4VParticleChange object. We then call the process (biasWrapper(compt)) fSplittingFactor -1 times (minus the number of call - // for the generation of the primary particle) to generate, at last, fSplittingFactor gamma - // according to the compton interaction process. If the gamma track is ok regarding the russian roulette algorithm (no russian roulette - //, or within the acceptance angle, or not killed by the RR process), we add it to the primary track. - // If an electron is generated (above the range cut), we also generate it. - // A tremendous advantage is there is no need to use by ourself Klein-Nishina formula or other. So, if the physics list used takes into account - // the doppler broadening or other fine effects, this will be also taken into account by the MC simulation. - // PS : The first gamma is then the primary particle, but all the other splitted particle (electron of course AND gamma) must be considered - // as secondary particles, even though generated gamma will not be cut here by the applied cut. - - + // There is here the biasing process : + // Since G4VParticleChange class does not allow to retrieve scattered gamma + // information, we need to cast the type G4ParticleChangeForGamma to the + // G4VParticleChange object. We then call the process (biasWrapper(compt)) + // fSplittingFactor -1 times (minus the number of call for the generation of + // the primary particle) to generate, at last, fSplittingFactor gamma + // according to the compton interaction process. If the gamma track is ok + // regarding the russian roulette algorithm (no russian roulette + //, or within the acceptance angle, or not killed by the RR process), we add + //it to the primary track. + // If an electron is generated (above the range cut), we also generate it. + // A tremendous advantage is there is no need to use by ourself Klein-Nishina + // formula or other. So, if the physics list used takes into account the + // doppler broadening or other fine effects, this will be also taken into + // account by the MC simulation. PS : The first gamma is then the primary + // particle, but all the other splitted particle (electron of course AND + // gamma) must be considered as secondary particles, even though generated + // gamma will not be cut here by the applied cut. + while (nCalls < splittingFactor) { gammaWeight = track->GetWeight() / fSplittingFactor; G4double initGammaWeight = track->GetWeight(); - G4VParticleChange* processGammaSplittedFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - G4ParticleChangeForGamma* castedProcessGammaSplittedFinalState = (G4ParticleChangeForGamma*) processGammaSplittedFinalState; - const G4ThreeVector momentum = castedProcessGammaSplittedFinalState-> GetProposedMomentumDirection(); - G4double energy = castedProcessGammaSplittedFinalState-> GetProposedKineticEnergy(); - G4double cosTheta = fVectorDirector * castedProcessInitFinalState->GetProposedMomentumDirection(); + G4VParticleChange *processGammaSplittedFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + G4ParticleChangeForGamma *castedProcessGammaSplittedFinalState = + (G4ParticleChangeForGamma *)processGammaSplittedFinalState; + const G4ThreeVector momentum = + castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(); + G4double energy = + castedProcessGammaSplittedFinalState->GetProposedKineticEnergy(); + G4double cosTheta = + fVectorDirector * + castedProcessInitFinalState->GetProposedMomentumDirection(); G4double theta = std::acos(cosTheta); G4double splittingProbability = G4UniformRand(); - - if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { - if ((fRussianRoulette == true) && (theta > fMaxTheta)) { + + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { + if ((fRussianRoulette == true) && (theta > fMaxTheta)) { G4double probability = G4UniformRand(); - if (probability < 1/fSplittingFactor) { - // Specific case where the russian roulette probability is 1/splitting. Each particle generated, with a 1/split probability - //will have a 1/split probability to survive with a final weight of Initial weights * 1/split * split = Initial weight - gammaWeight = gammaWeight*fSplittingFactor; - G4Track* gammaTrack = new G4Track(*track); + if (probability < 1 / fSplittingFactor) { + // Specific case where the russian roulette probability is + // 1/splitting. Each particle generated, with a 1/split probability + // will have a 1/split probability to survive with a final weight of + // Initial weights * 1/split * split = Initial weight + gammaWeight = gammaWeight * fSplittingFactor; + G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); gammaTrack->SetKineticEnergy(energy); gammaTrack->SetMomentumDirection(momentum); gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { - G4Track* electronTrack = processGammaSplittedFinalState->GetSecondary(0); + G4Track *electronTrack = + processGammaSplittedFinalState->GetSecondary(0); electronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(electronTrack); } } } - - if ((fRussianRoulette == false) || ((fRussianRoulette == true) && (theta <= fMaxTheta))) { - G4Track* gammaTrack = new G4Track(*track); + if ((fRussianRoulette == false) || + ((fRussianRoulette == true) && (theta <= fMaxTheta))) { + G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); gammaTrack->SetKineticEnergy(energy); gammaTrack->SetMomentumDirection(momentum); gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { - G4Track* electronTrack = processGammaSplittedFinalState->GetSecondary(0); + G4Track *electronTrack = + processGammaSplittedFinalState->GetSecondary(0); electronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(electronTrack); } @@ -210,8 +243,6 @@ G4VParticleChange *GateOptnComptSplitting::ApplyFinalStateBiasing(const G4Biasin nCalls++; processGammaSplittedFinalState->Clear(); castedProcessGammaSplittedFinalState->Clear(); - - } return &fParticleChange; } diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplitting.h b/core/opengate_core/opengate_lib/GateOptnComptSplitting.h index e814d4ad4..5b3397b59 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplitting.h +++ b/core/opengate_core/opengate_lib/GateOptnComptSplitting.h @@ -77,45 +77,37 @@ class GateOptnComptSplitting : public G4VBiasingOperation { } G4double GetSplittingFactor() const { return fSplittingFactor; } - -void SetWeightThreshold(G4double weightThreshold) { + void SetWeightThreshold(G4double weightThreshold) { fWeightThreshold = weightThreshold; } G4double GetWeightThreshold() const { return fWeightThreshold; } - - void SetRussianRoulette(G4bool russianRoulette){ + void SetRussianRoulette(G4bool russianRoulette) { fRussianRoulette = russianRoulette; } G4bool GetRussianRoulette() const { return fRussianRoulette; } - void SetVectorDirector(G4ThreeVector vectorDirector){ + void SetVectorDirector(G4ThreeVector vectorDirector) { fVectorDirector = vectorDirector; } - G4ThreeVector GetVectorDirector() const {return fVectorDirector;} - - - void SetMaxTheta(G4double maxTheta){ - fMaxTheta = maxTheta; - } + G4ThreeVector GetVectorDirector() const { return fVectorDirector; } - G4double GetMaxTheta() const {return fMaxTheta;} + void SetMaxTheta(G4double maxTheta) { fMaxTheta = maxTheta; } + G4double GetMaxTheta() const { return fMaxTheta; } - - void SetMinWeightOfParticle(G4double minWeightOfParticle){ - fMinWeightOfParticle= minWeightOfParticle; + void SetMinWeightOfParticle(G4double minWeightOfParticle) { + fMinWeightOfParticle = minWeightOfParticle; } - G4double GetMinWeightOfParticle() const {return fMinWeightOfParticle;} - - - G4VParticleChange* GetParticleChange() { - G4VParticleChange* particleChange = &fParticleChange; - return particleChange;} + G4double GetMinWeightOfParticle() const { return fMinWeightOfParticle; } + G4VParticleChange *GetParticleChange() { + G4VParticleChange *particleChange = &fParticleChange; + return particleChange; + } private: G4double fSplittingFactor; @@ -125,8 +117,8 @@ void SetWeightThreshold(G4double weightThreshold) { G4ThreeVector fVectorDirector; G4double fMaxTheta; G4double fMinWeightOfParticle; - //G4DynamicParticle* fSplitParticle; - //G4Track* fGammaTrack; + // G4DynamicParticle* fSplitParticle; + // G4Track* fGammaTrack; }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp index bb3ac7112..5d14e2866 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp +++ b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp @@ -30,76 +30,84 @@ #include "GateOptnComptSplittingForTransportation.h" #include "G4BiasingProcessInterface.hh" +#include "G4ComptonScattering.hh" #include "G4DynamicParticle.hh" -#include "G4SystemOfUnits.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" #include "G4Gamma.hh" +#include "G4GammaConversion.hh" #include "G4ParticleChange.hh" #include "G4ParticleChangeForGamma.hh" -#include "G4VEmProcess.hh" -#include"G4Gamma.hh" -#include "G4ComptonScattering.hh" #include "G4PhotoElectricEffect.hh" +#include "G4ProcessType.hh" #include "G4RayleighScattering.hh" -#include "G4GammaConversion.hh" -#include "G4Exception.hh" +#include "G4SystemOfUnits.hh" #include "G4TrackStatus.hh" -#include "G4ProcessType.hh" -#include #include "G4TrackingManager.hh" +#include "G4VEmProcess.hh" +#include //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnComptSplittingForTransportation::GateOptnComptSplittingForTransportation(G4String name) - : G4VBiasingOperation(name), fSplittingFactor(1), fRussianRoulette(false), fParticleChange() {} +GateOptnComptSplittingForTransportation:: + GateOptnComptSplittingForTransportation(G4String name) + : G4VBiasingOperation(name), fSplittingFactor(1), fRussianRoulette(false), + fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnComptSplittingForTransportation::~GateOptnComptSplittingForTransportation() { -} - - +GateOptnComptSplittingForTransportation:: + ~GateOptnComptSplittingForTransportation() {} -G4VParticleChange *GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing(const G4BiasingProcessInterface *callingProcess, const G4Track *track,const G4Step *step, G4bool &) { - -//Here we generate for the first the compton process, given that this function (ApplyFinalStateBiasing) is called when there is a compton interaction -//Then the interaction location of the compton process will always be the same +G4VParticleChange * +GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( + const G4BiasingProcessInterface *callingProcess, const G4Track *track, + const G4Step *step, G4bool &) { + // Here we generate for the first the compton process, given that this + // function (ApplyFinalStateBiasing) is called when there is a compton + // interaction Then the interaction location of the compton process will + // always be the same G4double globalTime = step->GetTrack()->GetGlobalTime(); const G4ThreeVector position = step->GetPostStepPoint()->GetPosition(); G4int nCalls = 1; G4int splittingFactor = ceil(fSplittingFactor); - G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor)/splittingFactor; + G4double survivalProbabilitySplitting = + 1 - (splittingFactor - fSplittingFactor) / splittingFactor; G4bool isRightAngle = false; G4double gammaWeight = 0; G4int nbSecondaries = 0; - G4VParticleChange* processFinalState = nullptr; - G4ParticleChangeForGamma* castedProcessInitFinalState = nullptr; + G4VParticleChange *processFinalState = nullptr; + G4ParticleChangeForGamma *castedProcessInitFinalState = nullptr; + + processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + // In case we don't want to split (a bit faster) i.e no biaising or no + // splitting low weights particles. - processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - // In case we don't want to split (a bit faster) i.e no biaising or no splitting low weights particles. - - if ((fSplittingFactor == 1 && fRussianRoulette == false) || track->GetWeight() < fWeightThreshold) + if ((fSplittingFactor == 1 && fRussianRoulette == false) || + track->GetWeight() < fWeightThreshold) return processFinalState; - - castedProcessInitFinalState = (G4ParticleChangeForGamma*) processFinalState; + + castedProcessInitFinalState = (G4ParticleChangeForGamma *)processFinalState; nbSecondaries = processFinalState->GetNumberOfSecondaries(); - fParticleChange.Initialize(*track); fParticleChange.ProposeWeight(track->GetWeight()); - fParticleChange.ProposeTrackStatus(castedProcessInitFinalState->GetTrackStatus()); - fParticleChange.ProposeEnergy(castedProcessInitFinalState->GetProposedKineticEnergy()); - fParticleChange.ProposeMomentumDirection(castedProcessInitFinalState->GetProposedMomentumDirection()); + fParticleChange.ProposeTrackStatus( + castedProcessInitFinalState->GetTrackStatus()); + fParticleChange.ProposeEnergy( + castedProcessInitFinalState->GetProposedKineticEnergy()); + fParticleChange.ProposeMomentumDirection( + castedProcessInitFinalState->GetProposedMomentumDirection()); fParticleChange.SetSecondaryWeightByProcess(true); - - - -//If there is cut on secondary particles, there is a probability that the electron is not simulated -//Then, if the compton process created it, we add the gien electron to the ParticleChange object + // If there is cut on secondary particles, there is a probability that the + // electron is not simulated Then, if the compton process created it, we add + // the gien electron to the ParticleChange object if (nbSecondaries == 1) { - G4Track* initElectronTrack = castedProcessInitFinalState->GetSecondary(0); + G4Track *initElectronTrack = castedProcessInitFinalState->GetSecondary(0); initElectronTrack->SetWeight(track->GetWeight()); fParticleChange.AddSecondary(initElectronTrack); } @@ -107,92 +115,109 @@ G4VParticleChange *GateOptnComptSplittingForTransportation::ApplyFinalStateBiasi processFinalState->Clear(); castedProcessInitFinalState->Clear(); - //There is here the biasing process : - // Since G4VParticleChange class does not allow to retrieve scattered gamma information, we need to cast the type G4ParticleChangeForGamma - // to the G4VParticleChange object. We then call the process (biasWrapper(compt)) fSplittingFactor times (Here, the difference with the other version - // of splitting is the primary particle will be killed and its weight does not count) to generate, at last, fSplittingFactor gamma - // according to the compton interaction process. If the gamma track is ok regarding the russian roulette algorithm (no russian roulette - //, or within the acceptance angle, or not killed by the RR process), we add it to the primary track. - // If an electron is generated (above the range cut), we also generate it. - // A tremendous advantage is there is no need to use by ourself Klein-Nishina formula or other. So, if the physics list used takes into account - // the doppler broadening or other fine effects, this will be also taken into account by the MC simulation. - // PS : The first gamma is then the primary particle, but all the other splitted particle (electron of course AND gamma) must be considered - // as secondary particles, even though generated gamma will not be cut here by the applied cut. - - G4int simulationTrackID = 0; + // There is here the biasing process : + // Since G4VParticleChange class does not allow to retrieve scattered gamma + // information, we need to cast the type G4ParticleChangeForGamma to the + // G4VParticleChange object. We then call the process (biasWrapper(compt)) + // fSplittingFactor times (Here, the difference with the other version of + // splitting is the primary particle will be killed and its weight does not + // count) to generate, at last, fSplittingFactor gamma according to the + // compton interaction process. If the gamma track is ok regarding the + // russian roulette algorithm (no russian roulette + //, or within the acceptance angle, or not killed by the RR process), we add + //it to the primary track. + // If an electron is generated (above the range cut), we also generate it. + // A tremendous advantage is there is no need to use by ourself Klein-Nishina + // formula or other. So, if the physics list used takes into account the + // doppler broadening or other fine effects, this will be also taken into + // account by the MC simulation. PS : The first gamma is then the primary + // particle, but all the other splitted particle (electron of course AND + // gamma) must be considered as secondary particles, even though generated + // gamma will not be cut here by the applied cut. + + G4int simulationTrackID = 0; while (nCalls <= splittingFactor) { gammaWeight = track->GetWeight() / fSplittingFactor; G4double initGammaWeight = track->GetWeight(); - G4VParticleChange* processGammaSplittedFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - G4ParticleChangeForGamma* castedProcessGammaSplittedFinalState = (G4ParticleChangeForGamma*) processGammaSplittedFinalState; - const G4ThreeVector momentum = castedProcessGammaSplittedFinalState-> GetProposedMomentumDirection(); - G4double energy = castedProcessGammaSplittedFinalState-> GetProposedKineticEnergy(); - G4double cosTheta = fVectorDirector * castedProcessInitFinalState->GetProposedMomentumDirection(); + G4VParticleChange *processGammaSplittedFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + G4ParticleChangeForGamma *castedProcessGammaSplittedFinalState = + (G4ParticleChangeForGamma *)processGammaSplittedFinalState; + const G4ThreeVector momentum = + castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(); + G4double energy = + castedProcessGammaSplittedFinalState->GetProposedKineticEnergy(); + G4double cosTheta = + fVectorDirector * + castedProcessInitFinalState->GetProposedMomentumDirection(); G4double theta = std::acos(cosTheta); G4double splittingProbability = G4UniformRand(); - - if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { - if ((fRussianRoulette == true) && (theta > fMaxTheta)) { + + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { + if ((fRussianRoulette == true) && (theta > fMaxTheta)) { G4double probability = G4UniformRand(); - if (probability < 1/fSplittingFactor) { - // Specific case where the russian roulette probability is 1/splitting. Each particle generated, with a 1/split probability - //will have a 1/split probability to survive with a final weight of Initial weights * 1/split * split = Initial weight - gammaWeight = gammaWeight*fSplittingFactor; - G4Track* gammaTrack = new G4Track(*track); + if (probability < 1 / fSplittingFactor) { + // Specific case where the russian roulette probability is + // 1/splitting. Each particle generated, with a 1/split probability + // will have a 1/split probability to survive with a final weight of + // Initial weights * 1/split * split = Initial weight + gammaWeight = gammaWeight * fSplittingFactor; + G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); gammaTrack->SetKineticEnergy(energy); gammaTrack->SetMomentumDirection(momentum); gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); - simulationTrackID ++; + simulationTrackID++; if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { - G4Track* electronTrack = processGammaSplittedFinalState->GetSecondary(0); + G4Track *electronTrack = + processGammaSplittedFinalState->GetSecondary(0); electronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(electronTrack); - simulationTrackID ++; + simulationTrackID++; } } } - - if ((fRussianRoulette == false) || ((fRussianRoulette == true) && (theta <= fMaxTheta))) { - G4Track* gammaTrack = new G4Track(*track); + if ((fRussianRoulette == false) || + ((fRussianRoulette == true) && (theta <= fMaxTheta))) { + G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); gammaTrack->SetKineticEnergy(energy); gammaTrack->SetMomentumDirection(momentum); gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); - simulationTrackID ++; + simulationTrackID++; if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { - G4Track* electronTrack = processGammaSplittedFinalState->GetSecondary(0); + G4Track *electronTrack = + processGammaSplittedFinalState->GetSecondary(0); electronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(electronTrack); - simulationTrackID ++; + simulationTrackID++; } } } nCalls++; processGammaSplittedFinalState->Clear(); castedProcessGammaSplittedFinalState->Clear(); - - } - //Probe generation, sent in 5 directions, in order to provide informations before the track of the real photons about - //the geometries they will cross. + // Probe generation, sent in 5 directions, in order to provide informations + // before the track of the real photons about the geometries they will cross. if (fUseProbes) { - std::vector v = {{0,0,-1},{0,1,0},{0,-1,0},{1,0,0},{-1,0,0}}; + std::vector v = { + {0, 0, -1}, {0, 1, 0}, {0, -1, 0}, {1, 0, 0}, {-1, 0, 0}}; for (G4int nbProbe = 0; nbProbe < 5; nbProbe++) { - G4Track* gammaTrack = new G4Track(*track); + G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetGoodForTrackingFlag(1); gammaTrack->SetWeight(track->GetWeight() / fSplittingFactor); gammaTrack->SetKineticEnergy(track->GetKineticEnergy()); gammaTrack->SetMomentumDirection(fRot * v[nbProbe]); gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); - simulationTrackID ++; - - } + simulationTrackID++; + } } return &fParticleChange; } diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h index c0d8580aa..09bef7f9f 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h +++ b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h @@ -38,7 +38,7 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { public: // -- Constructor : GateOptnComptSplittingForTransportation(G4String name); - + // -- destructor: virtual ~GateOptnComptSplittingForTransportation(); @@ -78,51 +78,41 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { } G4double GetSplittingFactor() const { return fSplittingFactor; } - -void SetWeightThreshold(G4double weightThreshold) { + void SetWeightThreshold(G4double weightThreshold) { fWeightThreshold = weightThreshold; } G4double GetWeightThreshold() const { return fWeightThreshold; } - - void SetRussianRoulette(G4bool russianRoulette){ + void SetRussianRoulette(G4bool russianRoulette) { fRussianRoulette = russianRoulette; } G4bool GetRussianRoulette() const { return fRussianRoulette; } - void SetVectorDirector(G4ThreeVector vectorDirector){ + void SetVectorDirector(G4ThreeVector vectorDirector) { fVectorDirector = vectorDirector; } - void SetRotationMatrix(G4RotationMatrix rot){ - fRot = rot; - } - - G4ThreeVector GetVectorDirector() const {return fVectorDirector;} + void SetRotationMatrix(G4RotationMatrix rot) { fRot = rot; } + G4ThreeVector GetVectorDirector() const { return fVectorDirector; } - void SetMaxTheta(G4double maxTheta){ - fMaxTheta = maxTheta; - } + void SetMaxTheta(G4double maxTheta) { fMaxTheta = maxTheta; } - G4double GetMaxTheta() const {return fMaxTheta;} + G4double GetMaxTheta() const { return fMaxTheta; } - void SetUseOfProbes(G4bool p){fUseProbes = p;} + void SetUseOfProbes(G4bool p) { fUseProbes = p; } - - - void SetMinWeightOfParticle(G4double minWeightOfParticle){ - fMinWeightOfParticle= minWeightOfParticle; + void SetMinWeightOfParticle(G4double minWeightOfParticle) { + fMinWeightOfParticle = minWeightOfParticle; } - G4double GetMinWeightOfParticle() const {return fMinWeightOfParticle;} - - - G4VParticleChange* GetParticleChange() { - G4VParticleChange* particleChange = &fParticleChange; - return particleChange;} + G4double GetMinWeightOfParticle() const { return fMinWeightOfParticle; } + G4VParticleChange *GetParticleChange() { + G4VParticleChange *particleChange = &fParticleChange; + return particleChange; + } private: G4double fSplittingFactor; @@ -134,8 +124,8 @@ void SetWeightThreshold(G4double weightThreshold) { G4double fMinWeightOfParticle; G4bool fUseProbes; G4RotationMatrix fRot; - //G4DynamicParticle* fSplitParticle; - //G4Track* fGammaTrack; + // G4DynamicParticle* fSplitParticle; + // G4Track* fGammaTrack; }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp index 4bef07ce3..3942cf1a2 100644 --- a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp @@ -24,106 +24,109 @@ // ******************************************************************** // #include "GateOptnForceFreeFlight.h" -#include "G4ILawForceFreeFlight.hh" #include "G4BiasingProcessInterface.hh" +#include "G4ILawForceFreeFlight.hh" #include "G4Step.hh" - - -// This operator is used to transport the particle without interaction, and then correct the weight of the particle -//according to the probablity for the photon to not interact within the matter. To do that, we use a GEANT4 modifed Interaction Law (G4ILawForceFreeFlight), which modify, -//for the biased process the probability for the interaction to occur : Never. -//This occurence is called during the tracking for each step. Here the step is the largest possible, and one step correspond to the path of -//particle in the media. - -GateOptnForceFreeFlight ::GateOptnForceFreeFlight (G4String name) - : G4VBiasingOperation ( name ), - fOperationComplete ( true ) -{ - fForceFreeFlightInteractionLaw = new G4ILawForceFreeFlight("LawForOperation"+name); +// This operator is used to transport the particle without interaction, and then +// correct the weight of the particle +// according to the probablity for the photon to not interact within the matter. +// To do that, we use a GEANT4 modifed Interaction Law (G4ILawForceFreeFlight), +// which modify, for the biased process the probability for the interaction to +// occur : Never. This occurence is called during the tracking for each step. +// Here the step is the largest possible, and one step correspond to the path of +// particle in the media. + +GateOptnForceFreeFlight ::GateOptnForceFreeFlight(G4String name) + : G4VBiasingOperation(name), fOperationComplete(true) { + fForceFreeFlightInteractionLaw = + new G4ILawForceFreeFlight("LawForOperation" + name); } -GateOptnForceFreeFlight ::~GateOptnForceFreeFlight () -{ - if ( fForceFreeFlightInteractionLaw ) delete fForceFreeFlightInteractionLaw; +GateOptnForceFreeFlight ::~GateOptnForceFreeFlight() { + if (fForceFreeFlightInteractionLaw) + delete fForceFreeFlightInteractionLaw; } - - -const G4VBiasingInteractionLaw* GateOptnForceFreeFlight ::ProvideOccurenceBiasingInteractionLaw -( const G4BiasingProcessInterface*, G4ForceCondition& proposeForceCondition ) -{ +const G4VBiasingInteractionLaw * +GateOptnForceFreeFlight ::ProvideOccurenceBiasingInteractionLaw( + const G4BiasingProcessInterface *, + G4ForceCondition &proposeForceCondition) { fOperationComplete = false; proposeForceCondition = Forced; return fForceFreeFlightInteractionLaw; } -G4VParticleChange* GateOptnForceFreeFlight ::ApplyFinalStateBiasing( const G4BiasingProcessInterface* callingProcess, - const G4Track* track, - const G4Step* step, - G4bool& forceFinalState) -{ +G4VParticleChange *GateOptnForceFreeFlight ::ApplyFinalStateBiasing( + const G4BiasingProcessInterface *callingProcess, const G4Track *track, + const G4Step *step, G4bool &forceFinalState) { + fParticleChange.Initialize(*track); + forceFinalState = true; + fCountProcess++; - fParticleChange.Initialize( *track ); - forceFinalState = true; - fCountProcess ++; + fProposedWeight *= + fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()]; + if (fUseProbes) { + if (track->IsGoodForTracking() == 0) { - fProposedWeight *= fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()]; - if (fUseProbes){ - if (track->IsGoodForTracking() ==0){ - - if (fProposedWeight < fRussianRouletteProbability * fMinWeight){ - fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); - return &fParticleChange; - } - - if ((fProposedWeight < fMinWeight) && (fProposedWeight >= fRussianRouletteProbability * fMinWeight)) { - G4double probability = G4UniformRand(); - if (probability > fRussianRouletteProbability){ - fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); - return &fParticleChange; - } - else { - fProposedWeight = fProposedWeight/fRussianRouletteProbability; - } - } - - fParticleChange.ProposeWeight(fProposedWeight); - fOperationComplete = true; - } - if (track->IsGoodForTracking() ==1){ - fParticleChange.ProposeWeight(fProposedWeight); - fOperationComplete = true; - } - } - else { - if ((fProposedWeight < fRussianRouletteProbability * fMinWeight)|| ((fProposedWeight < fMinWeight) && (fSurvivedToRR == true))) { + if (fProposedWeight < fRussianRouletteProbability * fMinWeight) { fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); return &fParticleChange; } - if ((fProposedWeight < fMinWeight) && (fProposedWeight >= fRussianRouletteProbability * fMinWeight) && (fCountProcess == 4) && (fSurvivedToRR == false)) { + if ((fProposedWeight < fMinWeight) && + (fProposedWeight >= fRussianRouletteProbability * fMinWeight)) { G4double probability = G4UniformRand(); - - if (probability > fRussianRouletteProbability){ + if (probability > fRussianRouletteProbability) { fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); return &fParticleChange; - } - else { - fProposedWeight = fProposedWeight/fRussianRouletteProbability; - //std::cout<<"lim "<< fMinWeight << " RR " <IsGoodForTracking() == 1) { fParticleChange.ProposeWeight(fProposedWeight); fOperationComplete = true; + } + } else { + if ((fProposedWeight < fRussianRouletteProbability * fMinWeight) || + ((fProposedWeight < fMinWeight) && (fSurvivedToRR == true))) { + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } + + if ((fProposedWeight < fMinWeight) && + (fProposedWeight >= fRussianRouletteProbability * fMinWeight) && + (fCountProcess == 4) && (fSurvivedToRR == false)) { + G4double probability = G4UniformRand(); + + if (probability > fRussianRouletteProbability) { + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } else { + fProposedWeight = fProposedWeight / fRussianRouletteProbability; + // std::cout<<"lim "<< fMinWeight << " RR " + // <GetWrappedProcess()->GetProcessName()] = weightChange; + fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()] = + weightChange; } diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h index cf00f0964..3a1682d71 100644 --- a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h @@ -27,7 +27,7 @@ // //--------------------------------------------------------------- // -// GateOptnForceFreeFlight +// GateOptnForceFreeFlight // // Class Description: // A G4VBiasingOperation physics-based biasing operation. @@ -46,67 +46,73 @@ #ifndef GateOptnForceFreeFlight_h #define GateOptnForceFreeFlight_h 1 -#include "G4VBiasingOperation.hh" #include "G4ForceCondition.hh" #include "G4ParticleChange.hh" // -- ยงยง should add a dedicated "weight change only" particle change +#include "G4VBiasingOperation.hh" class G4ILawForceFreeFlight; - -class GateOptnForceFreeFlight : public G4VBiasingOperation { +class GateOptnForceFreeFlight : public G4VBiasingOperation { public: // -- Constructor : - GateOptnForceFreeFlight (G4String name); + GateOptnForceFreeFlight(G4String name); // -- destructor: - virtual ~GateOptnForceFreeFlight (); - + virtual ~GateOptnForceFreeFlight(); + public: // -- Methods from G4VBiasingOperation interface: // ------------------------------------------- // -- Used: - virtual const G4VBiasingInteractionLaw* ProvideOccurenceBiasingInteractionLaw( const G4BiasingProcessInterface*, G4ForceCondition& ); - virtual void AlongMoveBy( const G4BiasingProcessInterface*, const G4Step*, G4double ); - virtual G4VParticleChange* ApplyFinalStateBiasing( const G4BiasingProcessInterface*, const G4Track*, const G4Step*, G4bool&); + virtual const G4VBiasingInteractionLaw * + ProvideOccurenceBiasingInteractionLaw(const G4BiasingProcessInterface *, + G4ForceCondition &); + virtual void AlongMoveBy(const G4BiasingProcessInterface *, const G4Step *, + G4double); + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); // -- Unused: - virtual G4double DistanceToApplyOperation( const G4Track*, - G4double, - G4ForceCondition*) {return DBL_MAX;} - virtual G4VParticleChange* GenerateBiasingFinalState( const G4Track*, - const G4Step* ) {return 0;} - + virtual G4double DistanceToApplyOperation(const G4Track *, G4double, + G4ForceCondition *) { + return DBL_MAX; + } + virtual G4VParticleChange *GenerateBiasingFinalState(const G4Track *, + const G4Step *) { + return 0; + } public: // -- Additional methods, specific to this class: // ---------------------------------------------- // -- return concrete type of interaction law: - G4ILawForceFreeFlight* GetForceFreeFlightLaw() { + G4ILawForceFreeFlight *GetForceFreeFlightLaw() { return fForceFreeFlightInteractionLaw; } // -- initialization for weight: - //void ResetInitialTrackWeight(G4double w) {fInitialTrackWeight = w; fCumulatedWeightChange = 1.0;} - + // void ResetInitialTrackWeight(G4double w) {fInitialTrackWeight = w; + // fCumulatedWeightChange = 1.0;} - void SetMinWeight(G4double w){fMinWeight = w;} - void SetUseOfProbes(G4bool p){fUseProbes = p;} - G4double GetTrackWeight(){return fProposedWeight;} - void SetTrackWeight(G4double w){fProposedWeight = w;} - void SetRussianRouletteProbability(G4double p){fRussianRouletteProbability= p;} - void SetCountProcess(G4int N){fCountProcess = N;} - void SetSurvivedToRR(G4bool b){fSurvivedToRR = b;} - G4bool GetSurvivedToRR(){return fSurvivedToRR;} + void SetMinWeight(G4double w) { fMinWeight = w; } + void SetUseOfProbes(G4bool p) { fUseProbes = p; } + G4double GetTrackWeight() { return fProposedWeight; } + void SetTrackWeight(G4double w) { fProposedWeight = w; } + void SetRussianRouletteProbability(G4double p) { + fRussianRouletteProbability = p; + } + void SetCountProcess(G4int N) { fCountProcess = N; } + void SetSurvivedToRR(G4bool b) { fSurvivedToRR = b; } + G4bool GetSurvivedToRR() { return fSurvivedToRR; } G4bool OperationComplete() const { return fOperationComplete; } - + private: - G4ILawForceFreeFlight* fForceFreeFlightInteractionLaw; - std::map fWeightChange; + G4ILawForceFreeFlight *fForceFreeFlightInteractionLaw; + std::map fWeightChange; G4bool fUseProbes; - G4double fMinWeight, - fRussianRouletteProbability; - G4ParticleChange fParticleChange; - G4bool fOperationComplete; - G4double fProposedWeight; - G4int fCountProcess; - G4bool fSurvivedToRR; - + G4double fMinWeight, fRussianRouletteProbability; + G4ParticleChange fParticleChange; + G4bool fOperationComplete; + G4double fProposedWeight; + G4int fCountProcess; + G4bool fSurvivedToRR; }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 79c5cb772..9db37db28 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -30,42 +30,46 @@ #include "GateHelpersDict.h" #include "GateHelpersImage.h" +#include "CLHEP/Units/SystemOfUnits.h" #include "G4BiasingProcessInterface.hh" +#include "G4EmCalculator.hh" #include "G4Gamma.hh" #include "G4LogicalVolumeStore.hh" +#include "G4ParticleTable.hh" #include "G4PhysicalVolumeStore.hh" -#include "GateOptnComptSplittingForTransportation.h" -#include "GateOptrComptPseudoTransportationActor.h" #include "G4ProcessManager.hh" -#include "G4VEmProcess.hh" -#include "G4EmCalculator.hh" #include "G4ProcessVector.hh" -#include "G4ParticleTable.hh" -#include "G4Gamma.hh" -#include "CLHEP/Units/SystemOfUnits.h" +#include "G4RunManager.hh" #include "G4TrackStatus.hh" #include "G4UserTrackingAction.hh" -#include "G4RunManager.hh" +#include "G4VEmProcess.hh" +#include "GateOptnComptSplittingForTransportation.h" +#include "GateOptrComptPseudoTransportationActor.h" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor(py::dict &user_info) +GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( + py::dict &user_info) : G4VBiasingOperator("ComptSplittingOperator"), GateVActor(user_info, false) { fMotherVolumeName = DictGetStr(user_info, "mother"); fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); fWeightThreshold = DictGetDouble(user_info, "weight_threshold"); - fRelativeMinWeightOfParticle = DictGetDouble(user_info, "relative_min_weight_of_particle"); - //Since the russian roulette uses as a probability 1/splitting, we need to have a double, - //but the splitting factor provided by the user is logically an int, so we need to change the type. + fRelativeMinWeightOfParticle = + DictGetDouble(user_info, "relative_min_weight_of_particle"); + // Since the russian roulette uses as a probability 1/splitting, we need to + // have a double, but the splitting factor provided by the user is logically + // an int, so we need to change the type. fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); fRussianRoulette = DictGetBool(user_info, "russian_roulette"); fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); - fMaxTheta = DictGetDouble(user_info,"max_theta"); - fRussianRouletteForFreeFlight = DictGetDouble(user_info,"russian_roulette_for_free_flight"); + fMaxTheta = DictGetDouble(user_info, "max_theta"); + fRussianRouletteForFreeFlight = + DictGetDouble(user_info, "russian_roulette_for_free_flight"); fFreeFlightOperation = new GateOptnForceFreeFlight("freeFlightOperation"); - fComptSplittingOperation = new GateOptnComptSplittingForTransportation("comptSplittingOperation"); - fUseProbes = DictGetBool(user_info,"use_probes"); + fComptSplittingOperation = + new GateOptnComptSplittingForTransportation("comptSplittingOperation"); + fUseProbes = DictGetBool(user_info, "use_probes"); fActions.insert("StartSimulationAction"); fActions.insert("SteppingAction"); fActions.insert("BeginOfEventAction"); @@ -75,191 +79,187 @@ GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor(p //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -void GateOptrComptPseudoTransportationActor::AttachAllLogicalDaughtersVolumes(G4LogicalVolume* volume){ +void GateOptrComptPseudoTransportationActor::AttachAllLogicalDaughtersVolumes( + G4LogicalVolume *volume) { AttachTo(volume); G4int nbOfDaughters = volume->GetNoDaughters(); - if (nbOfDaughters >0 ){ - for (int i = 0; i< nbOfDaughters;i++){ - G4LogicalVolume* logicalDaughtersVolume = volume->GetDaughter(i)->GetLogicalVolume(); + if (nbOfDaughters > 0) { + for (int i = 0; i < nbOfDaughters; i++) { + G4LogicalVolume *logicalDaughtersVolume = + volume->GetDaughter(i)->GetLogicalVolume(); AttachAllLogicalDaughtersVolumes(logicalDaughtersVolume); } } } -void GateOptrComptPseudoTransportationActor::StartSimulationAction(){ - G4LogicalVolume* biasingVolume = G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); +void GateOptrComptPseudoTransportationActor::StartSimulationAction() { + G4LogicalVolume *biasingVolume = + G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); - //Here we need to attach all the daughters and daughters of daughters (...) to the biasing operator. - //To do that, I use the function AttachAllLogicalDaughtersVolumes. + // Here we need to attach all the daughters and daughters of daughters (...) + // to the biasing operator. To do that, I use the function + // AttachAllLogicalDaughtersVolumes. AttachAllLogicalDaughtersVolumes(biasingVolume); fComptSplittingOperation->SetSplittingFactor(fSplittingFactor); fComptSplittingOperation->SetWeightThreshold(fWeightThreshold); fComptSplittingOperation->SetMaxTheta(fMaxTheta); fComptSplittingOperation->SetRussianRoulette(fRussianRoulette); fComptSplittingOperation->SetUseOfProbes(fUseProbes); - fFreeFlightOperation->SetRussianRouletteProbability(fRussianRouletteForFreeFlight); + fFreeFlightOperation->SetRussianRouletteProbability( + fRussianRouletteForFreeFlight); fFreeFlightOperation->SetUseOfProbes(fUseProbes); - - } void GateOptrComptPseudoTransportationActor::StartRun() { // The way to behave of the russian roulette is the following : - // we provide a vector director and the theta angle acceptance, where theta = 0 is a vector colinear to the vector director - // Then if the track generated is on the acceptance angle, we add it to the primary track, and if it's not the case, we launch the russian roulette - if (fRotationVectorDirector){ - G4VPhysicalVolume* physBiasingVolume = G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); - auto rot = physBiasingVolume -> GetObjectRotationValue(); + // we provide a vector director and the theta angle acceptance, where theta = + // 0 is a vector colinear to the vector director Then if the track generated + // is on the acceptance angle, we add it to the primary track, and if it's not + // the case, we launch the russian roulette + if (fRotationVectorDirector) { + G4VPhysicalVolume *physBiasingVolume = + G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume->GetObjectRotationValue(); fVectorDirector = rot * fVectorDirector; fComptSplittingOperation->SetRotationMatrix(rot); } - + fComptSplittingOperation->SetVectorDirector(fVectorDirector); - - } +void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { + // The stepping action is used to kill particle we have too kill : + // - If the primary particle reach the biased boudary + // - KIll all the probes exiting the volume + // - Kill, if probes, particles wich have a weilght lower than the probes one - -void GateOptrComptPseudoTransportationActor::SteppingAction (G4Step *step) { - - //The stepping action is used to kill particle we have too kill : - // - If the primary particle reach the biased boudary - // - KIll all the probes exiting the volume - // - Kill, if probes, particles wich have a weilght lower than the probes one - if (fUseProbes) { - if ((fKillOthersParticles) && (step->GetTrack()->IsGoodForTracking() ==0)) - { + if ((fKillOthersParticles) && + (step->GetTrack()->IsGoodForTracking() == 0)) { step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); } - if (step->GetPostStepPoint()->GetStepStatus()!= fWorldBoundary){ - if ((step->GetPostStepPoint()->GetPhysicalVolume ()->GetName() == "world") && (step->GetTrack()->IsGoodForTracking() == 1)){ + if (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary) { + if ((step->GetPostStepPoint()->GetPhysicalVolume()->GetName() == + "world") && + (step->GetTrack()->IsGoodForTracking() == 1)) { step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); } } } - if ((isSplitted ==true) && (step->GetPostStepPoint()->GetStepStatus()!= fWorldBoundary)){ - if ((step->GetPostStepPoint()->GetPhysicalVolume ()->GetName() == "world")) - { + if ((isSplitted == true) && + (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { + if ((step->GetPostStepPoint()->GetPhysicalVolume()->GetName() == "world")) { step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); - isSplitted =false; + isSplitted = false; } } - - - - } -void GateOptrComptPseudoTransportationActor::BeginOfEventAction(const G4Event *event) { +void GateOptrComptPseudoTransportationActor::BeginOfEventAction( + const G4Event *event) { fKillOthersParticles = false; } -void GateOptrComptPseudoTransportationActor::StartTracking(const G4Track *track) { -fInitialWeight = track->GetWeight(); -fFreeFlightOperation->SetSurvivedToRR(false); +void GateOptrComptPseudoTransportationActor::StartTracking( + const G4Track *track) { + fInitialWeight = track->GetWeight(); + fFreeFlightOperation->SetSurvivedToRR(false); } - -//For the following operation the idea is the following : -//All the potential photon processes are biased. If a particle undergoes a compton interaction, we splitted it -//(ComptonSplittingForTransportation operation) and the particle generated are pseudo-transported with the ForceFreeFLight operation -// Since the occurence Biaising operation is called at the beginning of each track, and propose a different way to track the particle -//(with modified physics), it here returns other thing than 0 if we want to pseudo-transport the particle, so if its creatorProcess is the -//modified compton interaction +// For the following operation the idea is the following : +// All the potential photon processes are biased. If a particle undergoes a +// compton interaction, we splitted it (ComptonSplittingForTransportation +//operation) and the particle generated are pseudo-transported with the +//ForceFreeFLight operation +// Since the occurence Biaising operation is called at the beginning of each +// track, and propose a different way to track the particle +//(with modified physics), it here returns other thing than 0 if we want to +//pseudo-transport the particle, so if its creatorProcess is the modified +// compton interaction G4VBiasingOperation * -GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation(const G4Track* track, const G4BiasingProcessInterface* callingProcess) -{ -if (track->GetCreatorProcess () !=0){ - if (track->GetCreatorProcess ()->GetProcessName() == "biasWrapper(compt)"){ - fFreeFlightOperation->SetMinWeight(fInitialWeight/fRelativeMinWeightOfParticle); +GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation( + const G4Track *track, const G4BiasingProcessInterface *callingProcess) { + if (track->GetCreatorProcess() != 0) { + if (track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") { + fFreeFlightOperation->SetMinWeight(fInitialWeight / + fRelativeMinWeightOfParticle); fFreeFlightOperation->SetTrackWeight(track->GetWeight()); fFreeFlightOperation->SetCountProcess(0); return fFreeFlightOperation; } - } return 0; } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -// Here we call the final state biasing operation called if one of the biased interaction (all photon interaction here) occurs. -//That's why we need here to apply some conditions to just split the initial track. +// Here we call the final state biasing operation called if one of the biased +// interaction (all photon interaction here) occurs. +// That's why we need here to apply some conditions to just split the initial +// track. G4VBiasingOperation * GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( const G4Track *track, const G4BiasingProcessInterface *callingProcess) { - G4String callingProcessName = "biasWrapper(compt)"; - + G4String callingProcessName = "biasWrapper(compt)"; - if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt") - { - if (track->GetCreatorProcess() ==0) { - isSplitted = true; - return fComptSplittingOperation; - } - if (track->GetCreatorProcess()-> GetProcessName() != "biasWrapper(compt)"){ - isSplitted = true; - return fComptSplittingOperation; + if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt") { + if (track->GetCreatorProcess() == 0) { + isSplitted = true; + return fComptSplittingOperation; + } + if (track->GetCreatorProcess()->GetProcessName() != "biasWrapper(compt)") { + isSplitted = true; + return fComptSplittingOperation; + } } - } - /* - */ - if (track->GetCreatorProcess() !=0){ - if(track->GetCreatorProcess()-> GetProcessName() == "biasWrapper(compt)"){ - return callingProcess->GetCurrentOccurenceBiasingOperation(); + /* + */ + if (track->GetCreatorProcess() != 0) { + if (track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") { + return callingProcess->GetCurrentOccurenceBiasingOperation(); + } } - } - - return 0; - - //return 0; -} + return 0; + // return 0; +} void GateOptrComptPseudoTransportationActor::EndTracking() { -isSplitted =false; + isSplitted = false; } +void GateOptrComptPseudoTransportationActor::PostUserTrackingAction( + const G4Track *track) { -void GateOptrComptPseudoTransportationActor::PostUserTrackingAction(const G4Track *track) { - -if (fUseProbes){ - if (track->IsGoodForTracking() == 1){ - G4double tmpWeight = fFreeFlightOperation->GetTrackWeight(); - if (NbOfProbe == 1) - { - fKillOthersParticles = false; - weight = tmpWeight; - } - else{ - if (tmpWeight > weight){ + if (fUseProbes) { + if (track->IsGoodForTracking() == 1) { + G4double tmpWeight = fFreeFlightOperation->GetTrackWeight(); + if (NbOfProbe == 1) { + fKillOthersParticles = false; weight = tmpWeight; + } else { + if (tmpWeight > weight) { + weight = tmpWeight; + } } - } - NbOfProbe ++; - if ((NbOfProbe == 6)){ - if (weight < fInitialWeight/fRelativeMinWeightOfParticle){ - fKillOthersParticles = true; - } + NbOfProbe++; + if ((NbOfProbe == 6)) { + if (weight < fInitialWeight / fRelativeMinWeightOfParticle) { + fKillOthersParticles = true; + } NbOfProbe = 1; weight = 0; - + } } } } -} - - - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index 020ef2d73..31cf53846 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -29,9 +29,9 @@ #ifndef GateOptrComptPseudoTransportationActor_h #define GateOptrComptPseudoTransportationActor_h 1 +#include "G4EmCalculator.hh" #include "G4VBiasingOperator.hh" #include "GateOptnForceFreeFlight.h" -#include "G4EmCalculator.hh" #include "GateVActor.h" #include @@ -41,7 +41,7 @@ namespace py = pybind11; class GateOptnComptSplittingForTransportation; class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, - public GateVActor { + public GateVActor { public: GateOptrComptPseudoTransportationActor(py::dict &user_info); virtual ~GateOptrComptPseudoTransportationActor() {} @@ -82,27 +82,25 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, virtual void StartSimulationAction(); virtual void StartRun(); virtual void StartTracking(const G4Track *); - virtual void PostUserTrackingAction(const G4Track * track); - virtual void SteppingAction(G4Step*); - virtual void BeginOfEventAction(const G4Event*); + virtual void PostUserTrackingAction(const G4Track *track); + virtual void SteppingAction(G4Step *); + virtual void BeginOfEventAction(const G4Event *); virtual void EndTracking(); - protected: // ----------------------------- // -- Mandatory from base class: // ----------------------------- // -- Unused: - void AttachAllLogicalDaughtersVolumes(G4LogicalVolume*); + void AttachAllLogicalDaughtersVolumes(G4LogicalVolume *); virtual G4VBiasingOperation *ProposeNonPhysicsBiasingOperation( const G4Track * /* track */, const G4BiasingProcessInterface * /* callingProcess */) { return 0; } - // -- Used: - virtual G4VBiasingOperation *ProposeOccurenceBiasingOperation( + virtual G4VBiasingOperation *ProposeOccurenceBiasingOperation( const G4Track * /* track */, const G4BiasingProcessInterface * /* callingProcess */); @@ -115,8 +113,8 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, using G4VBiasingOperator::OperationApplied; private: - GateOptnForceFreeFlight *fFreeFlightOperation ; - GateOptnComptSplittingForTransportation *fComptSplittingOperation ; + GateOptnForceFreeFlight *fFreeFlightOperation; + GateOptnComptSplittingForTransportation *fComptSplittingOperation; }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptrComptSplittingActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptSplittingActor.cpp index 430e46178..c17e5ccfb 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptSplittingActor.cpp @@ -30,17 +30,16 @@ #include "GateHelpersDict.h" #include "GateHelpersImage.h" +#include "CLHEP/Units/SystemOfUnits.h" #include "G4BiasingProcessInterface.hh" #include "G4Gamma.hh" #include "G4LogicalVolumeStore.hh" +#include "G4ParticleTable.hh" #include "G4PhysicalVolumeStore.hh" -#include "GateOptnComptSplitting.h" -#include "GateOptrComptSplittingActor.h" #include "G4ProcessManager.hh" #include "G4ProcessVector.hh" -#include "G4ParticleTable.hh" -#include "G4Gamma.hh" -#include "CLHEP/Units/SystemOfUnits.h" +#include "GateOptnComptSplitting.h" +#include "GateOptrComptSplittingActor.h" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -51,14 +50,15 @@ GateOptrComptSplittingActor::GateOptrComptSplittingActor(py::dict &user_info) fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); fWeightThreshold = DictGetDouble(user_info, "weight_threshold"); fMinWeightOfParticle = DictGetDouble(user_info, "min_weight_of_particle"); - //Since the russian roulette uses as a probablity 1/splitting, we need to have a double, - //but the splitting factor provided by the user is logically an int, so we need to change the type. + // Since the russian roulette uses as a probablity 1/splitting, we need to + // have a double, but the splitting factor provided by the user is logically + // an int, so we need to change the type. fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); fBiasPrimaryOnly = DictGetBool(user_info, "bias_primary_only"); fBiasOnlyOnce = DictGetBool(user_info, "bias_only_once"); fRussianRoulette = DictGetBool(user_info, "russian_roulette"); fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); - fMaxTheta = DictGetDouble(user_info,"max_theta"); + fMaxTheta = DictGetDouble(user_info, "max_theta"); fComptSplittingOperation = new GateOptnComptSplitting("ComptSplittingOperation"); fActions.insert("StartSimulationAction"); @@ -66,22 +66,26 @@ GateOptrComptSplittingActor::GateOptrComptSplittingActor(py::dict &user_info) //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -void GateOptrComptSplittingActor::AttachAllLogicalDaughtersVolumes(G4LogicalVolume* volume){ +void GateOptrComptSplittingActor::AttachAllLogicalDaughtersVolumes( + G4LogicalVolume *volume) { AttachTo(volume); G4int nbOfDaughters = volume->GetNoDaughters(); - if (nbOfDaughters >0 ){ - for (int i = 0; i< nbOfDaughters;i++){ - G4LogicalVolume* logicalDaughtersVolume = volume->GetDaughter(i)->GetLogicalVolume(); + if (nbOfDaughters > 0) { + for (int i = 0; i < nbOfDaughters; i++) { + G4LogicalVolume *logicalDaughtersVolume = + volume->GetDaughter(i)->GetLogicalVolume(); AttachAllLogicalDaughtersVolumes(logicalDaughtersVolume); } } } -void GateOptrComptSplittingActor::StartSimulationAction(){ - G4LogicalVolume* biasingVolume = G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); +void GateOptrComptSplittingActor::StartSimulationAction() { + G4LogicalVolume *biasingVolume = + G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); - //Here we need to attach all the daughters and daughters of daughters (...) to the biasing operator. - //To do that, I use the function AttachAllLogicalDaughtersVolumes. + // Here we need to attach all the daughters and daughters of daughters (...) + // to the biasing operator. To do that, I use the function + // AttachAllLogicalDaughtersVolumes. AttachAllLogicalDaughtersVolumes(biasingVolume); fComptSplittingOperation->SetSplittingFactor(fSplittingFactor); fComptSplittingOperation->SetWeightThreshold(fWeightThreshold); @@ -93,26 +97,24 @@ void GateOptrComptSplittingActor::StartSimulationAction(){ void GateOptrComptSplittingActor::StartRun() { // The way to behave of the russian roulette is the following : - // we provide a vector director and the theta angle acceptance, where theta = 0 is a vector colinear to the vector director - // Then if the track generated is on the acceptance angle, we add it to the primary track, and if it's not the case, we launch the russian roulette - if (fRotationVectorDirector){ - G4VPhysicalVolume* physBiasingVolume = G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); - auto rot = physBiasingVolume -> GetObjectRotationValue(); + // we provide a vector director and the theta angle acceptance, where theta = + // 0 is a vector colinear to the vector director Then if the track generated + // is on the acceptance angle, we add it to the primary track, and if it's not + // the case, we launch the russian roulette + if (fRotationVectorDirector) { + G4VPhysicalVolume *physBiasingVolume = + G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume->GetObjectRotationValue(); fVectorDirector = rot * fVectorDirector; } - + fComptSplittingOperation->SetVectorDirector(fVectorDirector); - } - void GateOptrComptSplittingActor::StartTracking(const G4Track *track) { fNInteractions = 0; - } - - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... G4VBiasingOperation * @@ -122,13 +124,8 @@ GateOptrComptSplittingActor::ProposeFinalStateBiasingOperation( return 0; if (fBiasOnlyOnce && (fNInteractions > 0)) return 0; - fNInteractions ++; + fNInteractions++; return fComptSplittingOperation; - } - - - - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrComptSplittingActor.h b/core/opengate_core/opengate_lib/GateOptrComptSplittingActor.h index fc376a3d1..c5f4ead04 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptSplittingActor.h @@ -73,13 +73,12 @@ class GateOptrComptSplittingActor : public G4VBiasingOperator, virtual void StartTracking(const G4Track *); virtual void EndTracking() {} - protected: // ----------------------------- // -- Mandatory from base class: // ----------------------------- // -- Unused: - void AttachAllLogicalDaughtersVolumes(G4LogicalVolume*); + void AttachAllLogicalDaughtersVolumes(G4LogicalVolume *); virtual G4VBiasingOperation *ProposeNonPhysicsBiasingOperation( const G4Track * /* track */, const G4BiasingProcessInterface * /* callingProcess */) { diff --git a/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp index ae0344c08..a5cb86c35 100644 --- a/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp @@ -12,8 +12,9 @@ namespace py = pybind11; void init_GateOptrComptPseudoTransportationActor(py::module &m) { - py::class_>( + py::class_< + GateOptrComptPseudoTransportationActor, G4VBiasingOperator, GateVActor, + std::unique_ptr>( m, "GateOptrComptPseudoTransportationActor") .def(py::init()); } diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 10fb305c5..501147cc5 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -444,6 +444,7 @@ def __init__(self, user_info): class ComptSplittingActor(g4.GateOptrComptSplittingActor, ActorBase): type_name = "ComptSplittingActor" + def set_default_user_info(user_info): ActorBase.set_default_user_info(user_info) deg = g4_units.deg @@ -455,16 +456,19 @@ def set_default_user_info(user_info): user_info.processes = ["compt"] user_info.russian_roulette = False user_info.rotation_vector_director = False - user_info.vector_director = [0,0,1] - user_info.max_theta = 90*deg + user_info.vector_director = [0, 0, 1] + user_info.max_theta = 90 * deg def __init__(self, user_info): ActorBase.__init__(self, user_info) g4.GateOptrComptSplittingActor.__init__(self, user_info.__dict__) -class ComptPseudoTransportationActor(g4.GateOptrComptPseudoTransportationActor, ActorBase): +class ComptPseudoTransportationActor( + g4.GateOptrComptPseudoTransportationActor, ActorBase +): type_name = "ComptPseudoTransportationActor" + def set_default_user_info(user_info): ActorBase.set_default_user_info(user_info) deg = g4_units.deg @@ -473,13 +477,13 @@ def set_default_user_info(user_info): user_info.bias_primary_only = True user_info.relative_min_weight_of_particle = 0 user_info.bias_only_once = True - user_info.processes = ["compt","phot","conv","Rayl"] + user_info.processes = ["compt", "phot", "conv", "Rayl"] user_info.russian_roulette = False user_info.rotation_vector_director = False - user_info.vector_director = [0,0,1] - user_info.max_theta = 90*deg - user_info.russian_roulette_for_free_flight = 1/10 - user_info.use_probes = False; + user_info.vector_director = [0, 0, 1] + user_info.max_theta = 90 * deg + user_info.russian_roulette_for_free_flight = 1 / 10 + user_info.use_probes = False def __init__(self, user_info): ActorBase.__init__(self, user_info) diff --git a/opengate/managers.py b/opengate/managers.py index 53134d638..826430808 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -346,9 +346,9 @@ class PhysicsListManager(GateObject): ) special_physics_constructor_classes["G4OpticalPhysics"] = g4.G4OpticalPhysics special_physics_constructor_classes["G4EmDNAPhysics"] = g4.G4EmDNAPhysics - special_physics_constructor_classes[ - "G4GenericBiasingPhysics" - ] = g4.G4GenericBiasingPhysics + special_physics_constructor_classes["G4GenericBiasingPhysics"] = ( + g4.G4GenericBiasingPhysics + ) def __init__(self, physics_manager, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/opengate/tests/src/test071_operator_russian_roulette.py b/opengate/tests/src/test071_operator_russian_roulette.py index efa78636d..6aea92929 100644 --- a/opengate/tests/src/test071_operator_russian_roulette.py +++ b/opengate/tests/src/test071_operator_russian_roulette.py @@ -8,52 +8,57 @@ from opengate.tests import utility - -def validation_test_RR(arr_data,rotation,vector_of_direction,theta,nb_splitting,tol_weights = 0.08): - arr_data = arr_data[(arr_data["TrackCreatorProcess"] != 'phot') & (arr_data["TrackCreatorProcess"] != 'eBrem') & ( - arr_data["TrackCreatorProcess"] != 'eIoni') & (arr_data['ParticleName'] == 'gamma')] +def validation_test_RR( + arr_data, rotation, vector_of_direction, theta, nb_splitting, tol_weights=0.08 +): + arr_data = arr_data[ + (arr_data["TrackCreatorProcess"] != "phot") + & (arr_data["TrackCreatorProcess"] != "eBrem") + & (arr_data["TrackCreatorProcess"] != "eIoni") + & (arr_data["ParticleName"] == "gamma") + ] EventID = arr_data["EventID"] list_of_weights = [] weights = 0 for i in range(len(EventID)): if i == 0: - weights+= arr_data["Weight"][i] - else : - if EventID[i] == EventID[i -1]: + weights += arr_data["Weight"][i] + else: + if EventID[i] == EventID[i - 1]: weights += arr_data["Weight"][i] - else : + else: list_of_weights.append(weights) - weights =arr_data["Weight"][i] + weights = arr_data["Weight"][i] list_of_weights = np.array(list_of_weights) mean_weights = np.mean(list_of_weights) bool_weight = False - if 1-tol_weights theta))) + bool_russian_roulette_1 = bool( + 1 - np.sum((tab_theta[weights == 1 / nb_splitting] > theta)) + ) bool_russian_roulette_2 = bool(1 - np.sum((tab_theta[weights == 1] <= theta))) - print('Average weight of :',mean_weights) + print("Average weight of :", mean_weights) if bool_russian_roulette_1 and bool_russian_roulette_2 and bool_weight: return True - else : + else: return False - - - if __name__ == "__main__": paths = utility.get_default_test_paths( __file__, "test071test_operator_compt_splitting_RR", output_folder="test071" @@ -68,7 +73,7 @@ def validation_test_RR(arr_data,rotation,vector_of_direction,theta,nb_splitting, # ui.visu = True # ui.visu_type = "vrml" ui.check_volumes_overlap = False - #ui.running_verbose_level = gate.logger.EVENT + # ui.running_verbose_level = gate.logger.EVENT ui.number_of_threads = 1 ui.random_seed = "auto" @@ -85,7 +90,6 @@ def validation_test_RR(arr_data,rotation,vector_of_direction,theta,nb_splitting, gcm3 = gate.g4_units.g / gate.g4_units.cm3 deg = gate.g4_units.deg - # adapt world size world = sim.world world.size = [0.25 * m, 0.25 * m, 0.25 * m] @@ -121,13 +125,13 @@ def validation_test_RR(arr_data,rotation,vector_of_direction,theta,nb_splitting, ####### Compton Splitting ACTOR ######### nb_split = 19.4 - theta_max = 90*deg + theta_max = 90 * deg compt_splitting_actor = sim.add_actor("ComptSplittingActor", "ComptSplittingW") compt_splitting_actor.mother = W_tubs.name compt_splitting_actor.splitting_factor = nb_split compt_splitting_actor.russian_roulette = True compt_splitting_actor.rotation_vector_director = True - compt_splitting_actor.vector_director = [0,0,-1] + compt_splitting_actor.vector_director = [0, 0, -1] compt_splitting_actor.max_theta = theta_max list_processes_to_bias = compt_splitting_actor.processes @@ -137,7 +141,7 @@ def validation_test_RR(arr_data,rotation,vector_of_direction,theta,nb_splitting, plan_tubs.material = "G4_Galactic" plan_tubs.mother = world.name plan_tubs.rmin = W_tubs.rmax - plan_tubs.rmax = plan_tubs.rmin + 1 * nm + plan_tubs.rmax = plan_tubs.rmin + 1 * nm plan_tubs.dz = 0.5 * m plan_tubs.color = [0.2, 1, 0.8, 1] plan_tubs.rotation = rotation @@ -176,19 +180,17 @@ def validation_test_RR(arr_data,rotation,vector_of_direction,theta,nb_splitting, sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option2" ## Perhaps avoid the user to call the below boolean function ? ### sim.physics_manager.special_physics_constructors.G4GenericBiasingPhysics = True - sim.physics_manager.processes_to_bias.gamma= list_processes_to_bias + sim.physics_manager.processes_to_bias.gamma = list_processes_to_bias #### Extremely important, it seems that GEANT4, for almost all physics lists, encompass all the photon processes in GammaGeneralProc #### Therefore if we provide the name of the real process (here compt) without deactivating GammaGeneralProcess, it will not find the #### process to bias and the biasing will fail s = f"/process/em/UseGeneralProcess false" sim.add_g4_command_before_init(s) - sim.physics_manager.global_production_cuts.gamma = 1 *m + sim.physics_manager.global_production_cuts.gamma = 1 * m sim.physics_manager.global_production_cuts.electron = 1 * um sim.physics_manager.global_production_cuts.positron = 1 * km - - output = sim.run() # @@ -199,5 +201,7 @@ def validation_test_RR(arr_data,rotation,vector_of_direction,theta,nb_splitting, f_data = uproot.open(paths.output / "test071_output_data_RR.root") arr_data = f_data["PhaseSpace"].arrays() - is_ok = validation_test_RR(arr_data,rotation,compt_splitting_actor.vector_director,theta_max,nb_split) + is_ok = validation_test_RR( + arr_data, rotation, compt_splitting_actor.vector_director, theta_max, nb_split + ) utility.test_ok(is_ok) diff --git a/opengate/tests/src/test072_pseudo_transportation.py b/opengate/tests/src/test072_pseudo_transportation.py index 967d8c4d0..ea981641f 100644 --- a/opengate/tests/src/test072_pseudo_transportation.py +++ b/opengate/tests/src/test072_pseudo_transportation.py @@ -11,18 +11,22 @@ from opengate.tests import utility - -def validation_test(arr,NIST_data,nb_split,tol = 0.01): +def validation_test(arr, NIST_data, nb_split, tol=0.01): tab_ekin = NIST_data[:, 0] mu_att = NIST_data[:, -2] log_ekin = np.log(tab_ekin) log_mu = np.log(mu_att) - f_mu = scipy.interpolate.interp1d(log_ekin, log_mu, kind='cubic') + f_mu = scipy.interpolate.interp1d(log_ekin, log_mu, kind="cubic") # print(arr["TrackCreatorProcess"]) - Tracks = arr[(arr["TrackCreatorProcess"] == 'biasWrapper(compt)') & (arr["KineticEnergy"] > 0.1) & (arr["ParticleName"] == "gamma") & (arr["Weight"] > 10**(-20))] - ekin_tracks = Tracks['KineticEnergy'] + Tracks = arr[ + (arr["TrackCreatorProcess"] == "biasWrapper(compt)") + & (arr["KineticEnergy"] > 0.1) + & (arr["ParticleName"] == "gamma") + & (arr["Weight"] > 10 ** (-20)) + ] + ekin_tracks = Tracks["KineticEnergy"] x_vertex = Tracks["TrackVertexPosition_X"] y_vertex = Tracks["TrackVertexPosition_Y"] z_vertex = Tracks["TrackVertexPosition_Z"] @@ -30,16 +34,19 @@ def validation_test(arr,NIST_data,nb_split,tol = 0.01): y = Tracks["PrePosition_Y"] z = Tracks["PrePosition_Z"] weights = Tracks["Weight"] * nb_split - dist = np.sqrt((x-x_vertex)**2 + (y-y_vertex)**2 + (z-z_vertex)**2) + dist = np.sqrt((x - x_vertex) ** 2 + (y - y_vertex) ** 2 + (z - z_vertex) ** 2) - G4_mu = -np.log(weights)/(0.1*dist*19.3) + G4_mu = -np.log(weights) / (0.1 * dist * 19.3) X_com_mu = np.exp(f_mu(np.log(ekin_tracks))) - diff = (G4_mu - X_com_mu)/G4_mu - print("Median difference between mu calculated from XCOM database and from GEANT4 free flight operation:", np.round(100*np.median(diff),1),"%") + diff = (G4_mu - X_com_mu) / G4_mu + print( + "Median difference between mu calculated from XCOM database and from GEANT4 free flight operation:", + np.round(100 * np.median(diff), 1), + "%", + ) return np.median(diff) < tol - if __name__ == "__main__": paths = utility.get_default_test_paths( __file__, "test072_pseudo_transportation", output_folder="test072" @@ -91,31 +98,41 @@ def validation_test(arr,NIST_data,nb_split,tol = 0.01): simple_collimation.color = [0.3, 0.1, 0.8, 1] W_leaf = sim.add_volume("Box", "W_leaf") - W_leaf.mother = simple_collimation.name - W_leaf.size = [1*m,1*m,2*cm] - W_leaf.material = 'Tungsten' - leaf_translation =[] + W_leaf.mother = simple_collimation.name + W_leaf.size = [1 * m, 1 * m, 2 * cm] + W_leaf.material = "Tungsten" + leaf_translation = [] for i in range(10): - leaf_translation.append([0,0, - 0.5*simple_collimation.size[2] + 0.5* W_leaf.size[2] + i * W_leaf.size[2]]) + leaf_translation.append( + [ + 0, + 0, + -0.5 * simple_collimation.size[2] + + 0.5 * W_leaf.size[2] + + i * W_leaf.size[2], + ] + ) print(leaf_translation) - W_leaf.translation =leaf_translation + W_leaf.translation = leaf_translation W_leaf.color = [0.8, 0.2, 0.1, 1] ######## pseudo_transportation ACTOR ######### nb_split = 5 - pseudo_transportation_actor = sim.add_actor("ComptPseudoTransportationActor", "pseudo_transportation_actor") + pseudo_transportation_actor = sim.add_actor( + "ComptPseudoTransportationActor", "pseudo_transportation_actor" + ) pseudo_transportation_actor.mother = simple_collimation.name pseudo_transportation_actor.splitting_factor = nb_split pseudo_transportation_actor.relative_min_weight_of_particle = np.inf list_processes_to_bias = pseudo_transportation_actor.processes ##### PHASE SPACE plan ######" - plan= sim.add_volume("Box", "phsp") + plan = sim.add_volume("Box", "phsp") plan.material = "G4_Galactic" plan.mother = world.name - plan.size = [1*m,1*m,1*nm] + plan.size = [1 * m, 1 * m, 1 * nm] plan.color = [0.2, 1, 0.8, 1] - plan.translation = [0,0,- 20*cm - 1*nm] + plan.translation = [0, 0, -20 * cm - 1 * nm] ####### gamma source ########### source = sim.add_source("GenericSource", "source1") @@ -127,7 +144,7 @@ def validation_test(arr,NIST_data,nb_split,tol = 0.01): source.direction.momentum = [0, 0, -1] source.energy.type = "mono" source.energy.mono = 6 * MeV - source.position.translation = [0,0,18*cm] + source.position.translation = [0, 0, 18 * cm] ####### PHASE SPACE ACTOR ############## @@ -141,7 +158,7 @@ def validation_test(arr,NIST_data,nb_split,tol = 0.01): "Weight", "KineticEnergy", "ParentID", - "ParticleName" + "ParticleName", ] phsp_actor.output = paths.output / "test072_output_data.root" @@ -157,7 +174,6 @@ def validation_test(arr,NIST_data,nb_split,tol = 0.01): s = f"/process/em/UseGeneralProcess false" sim.add_g4_command_before_init(s) - sim.physics_manager.global_production_cuts.gamma = 1 * mm sim.physics_manager.global_production_cuts.electron = 1 * km sim.physics_manager.global_production_cuts.positron = 1 * km @@ -171,8 +187,8 @@ def validation_test(arr,NIST_data,nb_split,tol = 0.01): print(stats) # f_phsp = uproot.open(paths.output / "test072_output_data.root") - data_NIST_W = np.loadtxt(paths.data / "NIST_W.txt",delimiter = '|') + data_NIST_W = np.loadtxt(paths.data / "NIST_W.txt", delimiter="|") arr = f_phsp["PhaseSpace"].arrays() # - is_ok = validation_test(arr,data_NIST_W,nb_split) + is_ok = validation_test(arr, data_NIST_W, nb_split) utility.test_ok(is_ok) From 07779d6a63514adf3ad9a96cd6690a2450d0aa49 Mon Sep 17 00:00:00 2001 From: majacquet Date: Mon, 26 Feb 2024 18:26:46 +0100 Subject: [PATCH 04/82] Modification of users command line to use the pseudo transporation actor, suppression of probe system and improvement of the russian roulette on the particle weights --- ...ateOptnComptSplittingForTransportation.cpp | 28 ++----- .../GateOptnComptSplittingForTransportation.h | 22 +----- .../opengate_lib/GateOptnForceFreeFlight.cpp | 73 ++++++++----------- .../opengate_lib/GateOptnForceFreeFlight.h | 8 +- ...GateOptrComptPseudoTransportationActor.cpp | 64 ++-------------- .../GateOptrComptPseudoTransportationActor.h | 6 +- 6 files changed, 55 insertions(+), 146 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp index 5d14e2866..81ed83525 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp +++ b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp @@ -51,7 +51,7 @@ GateOptnComptSplittingForTransportation:: GateOptnComptSplittingForTransportation(G4String name) - : G4VBiasingOperation(name), fSplittingFactor(1), fRussianRoulette(false), + : G4VBiasingOperation(name), fSplittingFactor(1), fRussianRouletteForAngle(false), fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -86,8 +86,7 @@ GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( // In case we don't want to split (a bit faster) i.e no biaising or no // splitting low weights particles. - if ((fSplittingFactor == 1 && fRussianRoulette == false) || - track->GetWeight() < fWeightThreshold) + if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) return processFinalState; castedProcessInitFinalState = (G4ParticleChangeForGamma *)processFinalState; @@ -155,7 +154,7 @@ GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { - if ((fRussianRoulette == true) && (theta > fMaxTheta)) { + if ((fRussianRouletteForAngle == true) && (theta > fMaxTheta)) { G4double probability = G4UniformRand(); if (probability < 1 / fSplittingFactor) { // Specific case where the russian roulette probability is @@ -180,8 +179,8 @@ GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( } } - if ((fRussianRoulette == false) || - ((fRussianRoulette == true) && (theta <= fMaxTheta))) { + if ((fRussianRouletteForAngle == false) || + ((fRussianRouletteForAngle == true) && (theta <= fMaxTheta))) { G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); gammaTrack->SetKineticEnergy(energy); @@ -202,23 +201,6 @@ GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( processGammaSplittedFinalState->Clear(); castedProcessGammaSplittedFinalState->Clear(); } - // Probe generation, sent in 5 directions, in order to provide informations - // before the track of the real photons about the geometries they will cross. - if (fUseProbes) { - std::vector v = { - {0, 0, -1}, {0, 1, 0}, {0, -1, 0}, {1, 0, 0}, {-1, 0, 0}}; - - for (G4int nbProbe = 0; nbProbe < 5; nbProbe++) { - G4Track *gammaTrack = new G4Track(*track); - gammaTrack->SetGoodForTrackingFlag(1); - gammaTrack->SetWeight(track->GetWeight() / fSplittingFactor); - gammaTrack->SetKineticEnergy(track->GetKineticEnergy()); - gammaTrack->SetMomentumDirection(fRot * v[nbProbe]); - gammaTrack->SetPosition(position); - fParticleChange.AddSecondary(gammaTrack); - simulationTrackID++; - } - } return &fParticleChange; } diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h index 09bef7f9f..72ee18bdb 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h +++ b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h @@ -78,16 +78,10 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { } G4double GetSplittingFactor() const { return fSplittingFactor; } - void SetWeightThreshold(G4double weightThreshold) { - fWeightThreshold = weightThreshold; + void SetRussianRouletteForAngle(G4bool russianRoulette) { + fRussianRouletteForAngle = russianRoulette; } - G4double GetWeightThreshold() const { return fWeightThreshold; } - void SetRussianRoulette(G4bool russianRoulette) { - fRussianRoulette = russianRoulette; - } - - G4bool GetRussianRoulette() const { return fRussianRoulette; } void SetVectorDirector(G4ThreeVector vectorDirector) { fVectorDirector = vectorDirector; @@ -101,13 +95,6 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { G4double GetMaxTheta() const { return fMaxTheta; } - void SetUseOfProbes(G4bool p) { fUseProbes = p; } - - void SetMinWeightOfParticle(G4double minWeightOfParticle) { - fMinWeightOfParticle = minWeightOfParticle; - } - - G4double GetMinWeightOfParticle() const { return fMinWeightOfParticle; } G4VParticleChange *GetParticleChange() { G4VParticleChange *particleChange = &fParticleChange; @@ -116,13 +103,10 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { private: G4double fSplittingFactor; - G4double fWeightThreshold; G4ParticleChange fParticleChange; - G4bool fRussianRoulette; + G4bool fRussianRouletteForAngle; G4ThreeVector fVectorDirector; G4double fMaxTheta; - G4double fMinWeightOfParticle; - G4bool fUseProbes; G4RotationMatrix fRot; // G4DynamicParticle* fSplitParticle; // G4Track* fGammaTrack; diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp index 3942cf1a2..125797d55 100644 --- a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp @@ -67,58 +67,49 @@ G4VParticleChange *GateOptnForceFreeFlight ::ApplyFinalStateBiasing( fProposedWeight *= fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()]; - if (fUseProbes) { - if (track->IsGoodForTracking() == 0) { - if (fProposedWeight < fRussianRouletteProbability * fMinWeight) { - fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); - return &fParticleChange; - } - if ((fProposedWeight < fMinWeight) && - (fProposedWeight >= fRussianRouletteProbability * fMinWeight)) { - G4double probability = G4UniformRand(); - if (probability > fRussianRouletteProbability) { - fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); - return &fParticleChange; - } else { - fProposedWeight = fProposedWeight / fRussianRouletteProbability; - } - } + if (fRussianRouletteForWeights){ + G4int nbOfOrderOfMagnitude =std::log10(fInitialWeight/fMinWeight); - fParticleChange.ProposeWeight(fProposedWeight); - fOperationComplete = true; - } - if (track->IsGoodForTracking() == 1) { - fParticleChange.ProposeWeight(fProposedWeight); - fOperationComplete = true; - } - } else { - if ((fProposedWeight < fRussianRouletteProbability * fMinWeight) || - ((fProposedWeight < fMinWeight) && (fSurvivedToRR == true))) { + if ((fProposedWeight < fMinWeight) || ((fProposedWeight < 0.1 *fInitialWeight) && (fNbOfRussianRoulette == nbOfOrderOfMagnitude - 1))) { fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + fNbOfRussianRoulette = 0; return &fParticleChange; } - if ((fProposedWeight < fMinWeight) && - (fProposedWeight >= fRussianRouletteProbability * fMinWeight) && - (fCountProcess == 4) && (fSurvivedToRR == false)) { - G4double probability = G4UniformRand(); - if (probability > fRussianRouletteProbability) { - fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); - return &fParticleChange; - } else { - fProposedWeight = fProposedWeight / fRussianRouletteProbability; - // std::cout<<"lim "<< fMinWeight << " RR " - // <= RRprobability *fInitialWeight) && (fProposedWeight < 10*RRprobability *fInitialWeight)){ + if (probability > 10*RRprobability){ + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + fNbOfRussianRoulette = 0; + return &fParticleChange; + } + else{ + fProposedWeight = fProposedWeight/(10*RRprobability); + fNbOfRussianRoulette= fNbOfRussianRoulette + i -1; + } + } } } - fParticleChange.ProposeWeight(fProposedWeight); - fOperationComplete = true; } + else{ + if (fProposedWeight < fMinWeight) { + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } + } + fParticleChange.ProposeWeight(fProposedWeight); + fOperationComplete = true; return &fParticleChange; } diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h index 3a1682d71..c631d7b9e 100644 --- a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h @@ -92,7 +92,7 @@ class GateOptnForceFreeFlight : public G4VBiasingOperation { // fCumulatedWeightChange = 1.0;} void SetMinWeight(G4double w) { fMinWeight = w; } - void SetUseOfProbes(G4bool p) { fUseProbes = p; } + void SetInitialWeight(G4double w) { fInitialWeight = w; } G4double GetTrackWeight() { return fProposedWeight; } void SetTrackWeight(G4double w) { fProposedWeight = w; } void SetRussianRouletteProbability(G4double p) { @@ -100,19 +100,21 @@ class GateOptnForceFreeFlight : public G4VBiasingOperation { } void SetCountProcess(G4int N) { fCountProcess = N; } void SetSurvivedToRR(G4bool b) { fSurvivedToRR = b; } + void SetRussianRouletteForWeights(G4bool rr) { fRussianRouletteForWeights = rr; } G4bool GetSurvivedToRR() { return fSurvivedToRR; } G4bool OperationComplete() const { return fOperationComplete; } private: G4ILawForceFreeFlight *fForceFreeFlightInteractionLaw; std::map fWeightChange; - G4bool fUseProbes; - G4double fMinWeight, fRussianRouletteProbability; + G4bool fRussianRouletteForWeights; + G4double fMinWeight, fRussianRouletteProbability,fInitialWeight; G4ParticleChange fParticleChange; G4bool fOperationComplete; G4double fProposedWeight; G4int fCountProcess; G4bool fSurvivedToRR; + G4int fNbOfRussianRoulette; }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 9db37db28..5a610d445 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -54,26 +54,22 @@ GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( GateVActor(user_info, false) { fMotherVolumeName = DictGetStr(user_info, "mother"); fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); - fWeightThreshold = DictGetDouble(user_info, "weight_threshold"); fRelativeMinWeightOfParticle = DictGetDouble(user_info, "relative_min_weight_of_particle"); // Since the russian roulette uses as a probability 1/splitting, we need to // have a double, but the splitting factor provided by the user is logically // an int, so we need to change the type. fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); - fRussianRoulette = DictGetBool(user_info, "russian_roulette"); + fRussianRouletteForAngle = DictGetBool(user_info, "russian_roulette_for_angle"); fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); + fRussianRouletteForWeights = DictGetBool(user_info, "russian_roulette_for_weights"); fMaxTheta = DictGetDouble(user_info, "max_theta"); - fRussianRouletteForFreeFlight = - DictGetDouble(user_info, "russian_roulette_for_free_flight"); fFreeFlightOperation = new GateOptnForceFreeFlight("freeFlightOperation"); fComptSplittingOperation = new GateOptnComptSplittingForTransportation("comptSplittingOperation"); - fUseProbes = DictGetBool(user_info, "use_probes"); fActions.insert("StartSimulationAction"); fActions.insert("SteppingAction"); fActions.insert("BeginOfEventAction"); - fActions.insert("PostUserTrackingAction"); isSplitted = false; } @@ -101,13 +97,9 @@ void GateOptrComptPseudoTransportationActor::StartSimulationAction() { // AttachAllLogicalDaughtersVolumes. AttachAllLogicalDaughtersVolumes(biasingVolume); fComptSplittingOperation->SetSplittingFactor(fSplittingFactor); - fComptSplittingOperation->SetWeightThreshold(fWeightThreshold); fComptSplittingOperation->SetMaxTheta(fMaxTheta); - fComptSplittingOperation->SetRussianRoulette(fRussianRoulette); - fComptSplittingOperation->SetUseOfProbes(fUseProbes); - fFreeFlightOperation->SetRussianRouletteProbability( - fRussianRouletteForFreeFlight); - fFreeFlightOperation->SetUseOfProbes(fUseProbes); + fComptSplittingOperation->SetRussianRouletteForAngle(fRussianRouletteForAngle); + fFreeFlightOperation->SetRussianRouletteForWeights(fRussianRouletteForWeights); } void GateOptrComptPseudoTransportationActor::StartRun() { @@ -130,26 +122,6 @@ void GateOptrComptPseudoTransportationActor::StartRun() { void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { - // The stepping action is used to kill particle we have too kill : - // - If the primary particle reach the biased boudary - // - KIll all the probes exiting the volume - // - Kill, if probes, particles wich have a weilght lower than the probes one - - if (fUseProbes) { - if ((fKillOthersParticles) && - (step->GetTrack()->IsGoodForTracking() == 0)) { - step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); - } - - if (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary) { - if ((step->GetPostStepPoint()->GetPhysicalVolume()->GetName() == - "world") && - (step->GetTrack()->IsGoodForTracking() == 1)) { - step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); - } - } - } - if ((isSplitted == true) && (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { if ((step->GetPostStepPoint()->GetPhysicalVolume()->GetName() == "world")) { @@ -166,8 +138,10 @@ void GateOptrComptPseudoTransportationActor::BeginOfEventAction( void GateOptrComptPseudoTransportationActor::StartTracking( const G4Track *track) { + //std::cout << "Begin o Track"<GetWeight(); - fFreeFlightOperation->SetSurvivedToRR(false); + fFreeFlightOperation->SetInitialWeight(fInitialWeight); + //fFreeFlightOperation->SetSurvivedToRR(false); } // For the following operation the idea is the following : @@ -235,31 +209,7 @@ void GateOptrComptPseudoTransportationActor::EndTracking() { isSplitted = false; } -void GateOptrComptPseudoTransportationActor::PostUserTrackingAction( - const G4Track *track) { - if (fUseProbes) { - if (track->IsGoodForTracking() == 1) { - G4double tmpWeight = fFreeFlightOperation->GetTrackWeight(); - if (NbOfProbe == 1) { - fKillOthersParticles = false; - weight = tmpWeight; - } else { - if (tmpWeight > weight) { - weight = tmpWeight; - } - } - NbOfProbe++; - if ((NbOfProbe == 6)) { - if (weight < fInitialWeight / fRelativeMinWeightOfParticle) { - fKillOthersParticles = true; - } - NbOfProbe = 1; - weight = 0; - } - } - } -} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index 31cf53846..576a41dc7 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -65,8 +65,8 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4bool fBiasPrimaryOnly; G4bool fBiasOnlyOnce; G4int fNInteractions = 0; - G4bool fRussianRoulette; - G4double fRussianRouletteForFreeFlight; + G4bool fRussianRouletteForAngle; + G4bool fRussianRouletteForWeights; G4bool fRotationVectorDirector; G4ThreeVector fVectorDirector; G4double fMaxTheta; @@ -77,12 +77,12 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4bool fKillOthersParticles = false; G4bool fUseProbes = false; G4bool fSurvivedRR = false; + // Unused but mandatory virtual void StartSimulationAction(); virtual void StartRun(); virtual void StartTracking(const G4Track *); - virtual void PostUserTrackingAction(const G4Track *track); virtual void SteppingAction(G4Step *); virtual void BeginOfEventAction(const G4Event *); virtual void EndTracking(); From f065c7c96656c048b467a8ebaa6f3eed7d3f5e2e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:27:26 +0000 Subject: [PATCH 05/82] [pre-commit.ci] Automatic python and c++ formatting --- .../opengate_lib/GateOptnComptSplitting.cpp | 5 ++- ...ateOptnComptSplittingForTransportation.cpp | 7 ++-- .../GateOptnComptSplittingForTransportation.h | 2 -- .../opengate_lib/GateOptnForceFreeFlight.cpp | 34 +++++++++---------- .../opengate_lib/GateOptnForceFreeFlight.h | 6 ++-- ...GateOptrComptPseudoTransportationActor.cpp | 25 +++++++------- .../GateOptrComptPseudoTransportationActor.h | 2 +- 7 files changed, 40 insertions(+), 41 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnComptSplitting.cpp index 8ee0ff264..be7799cd8 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptnComptSplitting.cpp @@ -33,7 +33,6 @@ #include "G4DynamicParticle.hh" #include "G4Exception.hh" #include "G4Gamma.hh" -#include "G4Gamma.hh" #include "G4ParticleChange.hh" #include "G4ParticleChangeForGamma.hh" #include "G4SystemOfUnits.hh" @@ -138,7 +137,7 @@ G4VParticleChange *GateOptnComptSplitting::ApplyFinalStateBiasing( // Initialisation of the information about the track. // We store the first gamma as the departure track, The first gamma is a // primary particle but with its weight modified , since it can be one of the - //detected particles + // detected particles fParticleChange.Initialize(*track); fParticleChange.ProposeWeight(gammaWeight); @@ -172,7 +171,7 @@ G4VParticleChange *GateOptnComptSplitting::ApplyFinalStateBiasing( // according to the compton interaction process. If the gamma track is ok // regarding the russian roulette algorithm (no russian roulette //, or within the acceptance angle, or not killed by the RR process), we add - //it to the primary track. + // it to the primary track. // If an electron is generated (above the range cut), we also generate it. // A tremendous advantage is there is no need to use by ourself Klein-Nishina // formula or other. So, if the physics list used takes into account the diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp index 81ed83525..6bdec6e6b 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp +++ b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp @@ -34,7 +34,6 @@ #include "G4DynamicParticle.hh" #include "G4Exception.hh" #include "G4Gamma.hh" -#include "G4Gamma.hh" #include "G4GammaConversion.hh" #include "G4ParticleChange.hh" #include "G4ParticleChangeForGamma.hh" @@ -51,8 +50,8 @@ GateOptnComptSplittingForTransportation:: GateOptnComptSplittingForTransportation(G4String name) - : G4VBiasingOperation(name), fSplittingFactor(1), fRussianRouletteForAngle(false), - fParticleChange() {} + : G4VBiasingOperation(name), fSplittingFactor(1), + fRussianRouletteForAngle(false), fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -124,7 +123,7 @@ GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( // compton interaction process. If the gamma track is ok regarding the // russian roulette algorithm (no russian roulette //, or within the acceptance angle, or not killed by the RR process), we add - //it to the primary track. + // it to the primary track. // If an electron is generated (above the range cut), we also generate it. // A tremendous advantage is there is no need to use by ourself Klein-Nishina // formula or other. So, if the physics list used takes into account the diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h index 72ee18bdb..5dff8f3f1 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h +++ b/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h @@ -82,7 +82,6 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { fRussianRouletteForAngle = russianRoulette; } - void SetVectorDirector(G4ThreeVector vectorDirector) { fVectorDirector = vectorDirector; } @@ -95,7 +94,6 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { G4double GetMaxTheta() const { return fMaxTheta; } - G4VParticleChange *GetParticleChange() { G4VParticleChange *particleChange = &fParticleChange; return particleChange; diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp index 125797d55..53d6ba3a9 100644 --- a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp @@ -68,41 +68,41 @@ G4VParticleChange *GateOptnForceFreeFlight ::ApplyFinalStateBiasing( fProposedWeight *= fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()]; + if (fRussianRouletteForWeights) { + G4int nbOfOrderOfMagnitude = std::log10(fInitialWeight / fMinWeight); - if (fRussianRouletteForWeights){ - G4int nbOfOrderOfMagnitude =std::log10(fInitialWeight/fMinWeight); - - if ((fProposedWeight < fMinWeight) || ((fProposedWeight < 0.1 *fInitialWeight) && (fNbOfRussianRoulette == nbOfOrderOfMagnitude - 1))) { + if ((fProposedWeight < fMinWeight) || + ((fProposedWeight < 0.1 * fInitialWeight) && + (fNbOfRussianRoulette == nbOfOrderOfMagnitude - 1))) { fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); fNbOfRussianRoulette = 0; return &fParticleChange; } - - if ((fProposedWeight < 0.1 *fInitialWeight) && (fCountProcess == 4)) { + if ((fProposedWeight < 0.1 * fInitialWeight) && (fCountProcess == 4)) { G4double probability = G4UniformRand(); for (int i = 2; i <= nbOfOrderOfMagnitude; i++) { - G4double RRprobability = 1/std::pow(10,i); - if (fProposedWeight * 1/std::pow(10,fNbOfRussianRoulette) < 10*RRprobability*fMinWeight ){ + G4double RRprobability = 1 / std::pow(10, i); + if (fProposedWeight * 1 / std::pow(10, fNbOfRussianRoulette) < + 10 * RRprobability * fMinWeight) { fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); fNbOfRussianRoulette = 0; return &fParticleChange; } - if ((fProposedWeight >= RRprobability *fInitialWeight) && (fProposedWeight < 10*RRprobability *fInitialWeight)){ - if (probability > 10*RRprobability){ + if ((fProposedWeight >= RRprobability * fInitialWeight) && + (fProposedWeight < 10 * RRprobability * fInitialWeight)) { + if (probability > 10 * RRprobability) { fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); fNbOfRussianRoulette = 0; return &fParticleChange; + } else { + fProposedWeight = fProposedWeight / (10 * RRprobability); + fNbOfRussianRoulette = fNbOfRussianRoulette + i - 1; } - else{ - fProposedWeight = fProposedWeight/(10*RRprobability); - fNbOfRussianRoulette= fNbOfRussianRoulette + i -1; - } - } + } } } - } - else{ + } else { if (fProposedWeight < fMinWeight) { fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); return &fParticleChange; diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h index c631d7b9e..398f27851 100644 --- a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h @@ -100,7 +100,9 @@ class GateOptnForceFreeFlight : public G4VBiasingOperation { } void SetCountProcess(G4int N) { fCountProcess = N; } void SetSurvivedToRR(G4bool b) { fSurvivedToRR = b; } - void SetRussianRouletteForWeights(G4bool rr) { fRussianRouletteForWeights = rr; } + void SetRussianRouletteForWeights(G4bool rr) { + fRussianRouletteForWeights = rr; + } G4bool GetSurvivedToRR() { return fSurvivedToRR; } G4bool OperationComplete() const { return fOperationComplete; } @@ -108,7 +110,7 @@ class GateOptnForceFreeFlight : public G4VBiasingOperation { G4ILawForceFreeFlight *fForceFreeFlightInteractionLaw; std::map fWeightChange; G4bool fRussianRouletteForWeights; - G4double fMinWeight, fRussianRouletteProbability,fInitialWeight; + G4double fMinWeight, fRussianRouletteProbability, fInitialWeight; G4ParticleChange fParticleChange; G4bool fOperationComplete; G4double fProposedWeight; diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 5a610d445..5e8bf38ac 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -60,9 +60,11 @@ GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( // have a double, but the splitting factor provided by the user is logically // an int, so we need to change the type. fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); - fRussianRouletteForAngle = DictGetBool(user_info, "russian_roulette_for_angle"); + fRussianRouletteForAngle = + DictGetBool(user_info, "russian_roulette_for_angle"); fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); - fRussianRouletteForWeights = DictGetBool(user_info, "russian_roulette_for_weights"); + fRussianRouletteForWeights = + DictGetBool(user_info, "russian_roulette_for_weights"); fMaxTheta = DictGetDouble(user_info, "max_theta"); fFreeFlightOperation = new GateOptnForceFreeFlight("freeFlightOperation"); fComptSplittingOperation = @@ -98,8 +100,10 @@ void GateOptrComptPseudoTransportationActor::StartSimulationAction() { AttachAllLogicalDaughtersVolumes(biasingVolume); fComptSplittingOperation->SetSplittingFactor(fSplittingFactor); fComptSplittingOperation->SetMaxTheta(fMaxTheta); - fComptSplittingOperation->SetRussianRouletteForAngle(fRussianRouletteForAngle); - fFreeFlightOperation->SetRussianRouletteForWeights(fRussianRouletteForWeights); + fComptSplittingOperation->SetRussianRouletteForAngle( + fRussianRouletteForAngle); + fFreeFlightOperation->SetRussianRouletteForWeights( + fRussianRouletteForWeights); } void GateOptrComptPseudoTransportationActor::StartRun() { @@ -138,21 +142,21 @@ void GateOptrComptPseudoTransportationActor::BeginOfEventAction( void GateOptrComptPseudoTransportationActor::StartTracking( const G4Track *track) { - //std::cout << "Begin o Track"<GetWeight(); fFreeFlightOperation->SetInitialWeight(fInitialWeight); - //fFreeFlightOperation->SetSurvivedToRR(false); + // fFreeFlightOperation->SetSurvivedToRR(false); } // For the following operation the idea is the following : // All the potential photon processes are biased. If a particle undergoes a // compton interaction, we splitted it (ComptonSplittingForTransportation -//operation) and the particle generated are pseudo-transported with the -//ForceFreeFLight operation +// operation) and the particle generated are pseudo-transported with the +// ForceFreeFLight operation // Since the occurence Biaising operation is called at the beginning of each // track, and propose a different way to track the particle //(with modified physics), it here returns other thing than 0 if we want to -//pseudo-transport the particle, so if its creatorProcess is the modified +// pseudo-transport the particle, so if its creatorProcess is the modified // compton interaction G4VBiasingOperation * @@ -209,7 +213,4 @@ void GateOptrComptPseudoTransportationActor::EndTracking() { isSplitted = false; } - - - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index 576a41dc7..b4c96de5d 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -77,7 +77,7 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4bool fKillOthersParticles = false; G4bool fUseProbes = false; G4bool fSurvivedRR = false; - + // Unused but mandatory virtual void StartSimulationAction(); From af172fafe86ab436a34b9eda68cc58a351bf4208 Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 27 Feb 2024 18:16:52 +0100 Subject: [PATCH 06/82] Add of a test to verify the correctness of the russian roulette and add of a user function to remove or not a geometry holder (e.g air box) encompassing the volumes to bias --- ...GateOptrComptPseudoTransportationActor.cpp | 33 ++- .../GateOptrComptPseudoTransportationActor.h | 1 + opengate/actors/miscactors.py | 11 +- opengate/tests/data | 2 +- .../tests/src/test072_pseudo_transport_RR.py | 221 ++++++++++++++++++ 5 files changed, 254 insertions(+), 14 deletions(-) create mode 100644 opengate/tests/src/test072_pseudo_transport_RR.py diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 5a610d445..464ae192a 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -45,6 +45,7 @@ #include "G4VEmProcess.hh" #include "GateOptnComptSplittingForTransportation.h" #include "GateOptrComptPseudoTransportationActor.h" +#include "G4Exception.hh" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -53,6 +54,7 @@ GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( : G4VBiasingOperator("ComptSplittingOperator"), GateVActor(user_info, false) { fMotherVolumeName = DictGetStr(user_info, "mother"); + fAttachToLogicalHolder = DictGetBool(user_info, "attach_to_logical_holder"); fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); fRelativeMinWeightOfParticle = DictGetDouble(user_info, "relative_min_weight_of_particle"); @@ -77,7 +79,13 @@ GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( void GateOptrComptPseudoTransportationActor::AttachAllLogicalDaughtersVolumes( G4LogicalVolume *volume) { - AttachTo(volume); + if (fAttachToLogicalHolder == false){ + if (volume->GetName() != G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName)->GetName()){ + AttachTo(volume); + } + } + if (fAttachToLogicalHolder == true) + AttachTo(volume); G4int nbOfDaughters = volume->GetNoDaughters(); if (nbOfDaughters > 0) { for (int i = 0; i < nbOfDaughters; i++) { @@ -138,10 +146,23 @@ void GateOptrComptPseudoTransportationActor::BeginOfEventAction( void GateOptrComptPseudoTransportationActor::StartTracking( const G4Track *track) { - //std::cout << "Begin o Track"<GetWeight(); + /* +if (fInitialWeight < 5*std::pow(10,-5)){ + std::cout<<" WEIIIIIGGGHHT BUUUGGG, begin of track, weight :"<< fInitialWeight << " Energy : "<GetKineticEnergy()<< " Creation process : " <GetCreatorProcess ()->GetProcessName() << " track ID : "<< track->GetTrackID()<<" ParentID : "<GetParentID() <<" particle name : " <GetParticleDefinition()->GetParticleName()<< " volume of creation : " <GetVolume()->GetName()<SetInitialWeight(fInitialWeight); - //fFreeFlightOperation->SetSurvivedToRR(false); + G4Exception("", "", + FatalException, + ""); + +} +*/ + if (track->GetCreatorProcess() != 0) { + if ((track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") &&(track->GetParticleDefinition()->GetParticleName() == "gamma")) { + fInitialWeight = track->GetWeight(); + fFreeFlightOperation->SetInitialWeight(fInitialWeight); + } + } } // For the following operation the idea is the following : @@ -159,7 +180,7 @@ G4VBiasingOperation * GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation( const G4Track *track, const G4BiasingProcessInterface *callingProcess) { if (track->GetCreatorProcess() != 0) { - if (track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") { + if ((track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") && (track->GetParticleDefinition()->GetParticleName() == "gamma")){ fFreeFlightOperation->SetMinWeight(fInitialWeight / fRelativeMinWeightOfParticle); fFreeFlightOperation->SetTrackWeight(track->GetWeight()); @@ -195,7 +216,7 @@ GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( /* */ if (track->GetCreatorProcess() != 0) { - if (track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") { + if ((track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") &&(track->GetParticleDefinition()->GetParticleName() == "gamma")) { return callingProcess->GetCurrentOccurenceBiasingOperation(); } } diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index 576a41dc7..f69eab760 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -77,6 +77,7 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4bool fKillOthersParticles = false; G4bool fUseProbes = false; G4bool fSurvivedRR = false; + G4bool fAttachToLogicalHolder = true; // Unused but mandatory diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 501147cc5..adf8e966c 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -472,18 +472,15 @@ class ComptPseudoTransportationActor( def set_default_user_info(user_info): ActorBase.set_default_user_info(user_info) deg = g4_units.deg + user_info.attach_to_logical_holder = True user_info.splitting_factor = 1 - user_info.weight_threshold = 0 - user_info.bias_primary_only = True - user_info.relative_min_weight_of_particle = 0 - user_info.bias_only_once = True + user_info.relative_min_weight_of_particle = np.inf user_info.processes = ["compt", "phot", "conv", "Rayl"] - user_info.russian_roulette = False + user_info.russian_roulette_for_angle = False user_info.rotation_vector_director = False user_info.vector_director = [0, 0, 1] user_info.max_theta = 90 * deg - user_info.russian_roulette_for_free_flight = 1 / 10 - user_info.use_probes = False + user_info.russian_roulette_for_weights= False def __init__(self, user_info): ActorBase.__init__(self, user_info) diff --git a/opengate/tests/data b/opengate/tests/data index 6cb4e7d3b..d1c61ed4e 160000 --- a/opengate/tests/data +++ b/opengate/tests/data @@ -1 +1 @@ -Subproject commit 6cb4e7d3b5f2bcc0fbca58bf6ef8fc958e4cb6c8 +Subproject commit d1c61ed4e7bade0067ed9fa20660d52abcc28744 diff --git a/opengate/tests/src/test072_pseudo_transport_RR.py b/opengate/tests/src/test072_pseudo_transport_RR.py new file mode 100644 index 000000000..d4d7f8769 --- /dev/null +++ b/opengate/tests/src/test072_pseudo_transport_RR.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +import uproot +import numpy as np +import matplotlib.pyplot as plt +import matplotlib as mpl +import scipy +from scipy.spatial.transform import Rotation +from opengate.tests import utility + +def Ekin_per_event(weight,EventID,Ekin) : + l_edep = [] + Edep_per_event = 0 + last_EventID = 0 + for i in range(len(weight)): + if i > 0: + last_EventID = EventID[i - 1] + if EventID[i] != last_EventID and i > 0: + l_edep.append(Edep_per_event) + Edep_per_event = 0 + Edep_per_event += Ekin[i] * weight[i] + return(np.asarray(l_edep)) +def validation_test(arr_no_RR,arr_RR,nb_event,splitting_factor, tol=0.15): + + Tracks_no_RR = arr_no_RR[ + (arr_no_RR["TrackCreatorProcess"] == "biasWrapper(compt)") + & (arr_no_RR["ParticleName"] == "gamma")] + + Tracks_RR = arr_RR[ + (arr_RR["TrackCreatorProcess"] == "biasWrapper(compt)") + & (arr_RR["ParticleName"] == "gamma")] + + + Ekin_no_RR = Tracks_no_RR["KineticEnergy"] + w_no_RR = Tracks_no_RR["Weight"] + Event_ID_no_RR = Tracks_no_RR["EventID"] + + Ekin_per_event_no_RR = Ekin_per_event(w_no_RR,Event_ID_no_RR,Ekin_no_RR) + + + Ekin_RR = Tracks_RR["KineticEnergy"] + w_RR = Tracks_RR["Weight"] + Event_ID_RR = Tracks_RR["EventID"] + + bool_RR = (np.min(w_RR) > 0.1*1/splitting_factor) & (np.max(w_RR) < 1/splitting_factor) + + Ekin_per_event_RR = Ekin_per_event(w_RR, Event_ID_RR, Ekin_RR) + + mean_ekin_event_no_RR = np.sum(Ekin_per_event_no_RR)/nb_event + mean_ekin_event_RR = np.sum(Ekin_per_event_RR)/nb_event + diff = (mean_ekin_event_RR - mean_ekin_event_no_RR) / mean_ekin_event_RR + print("Mean kinetic energy per event without russian roulette :",np.round(1000*mean_ekin_event_no_RR,1), 'keV') + print("Mean kinetic energy per event with russian roulette :", np.round(1000*mean_ekin_event_RR,1), 'keV') + print("Percentage of difference :",diff*100,"%") + + return (abs(diff) < tol) &( bool_RR) + # mean_energy_per_history_no_RR = np.sum(Tracks_no_RR["KineticEnergy"]*Tracks_no_RR["Weight"])/nb_event + # mean_energy_per_history_RR = np.sum(Tracks_RR["KineticEnergy"] * Tracks_RR["Weight"]) / nb_event + + + # print(mean_energy_per_history_no_RR,mean_energy_per_history_RR) + + +if __name__ == "__main__": + + for j in range(2): + paths = utility.get_default_test_paths( + __file__, "test072_pseudo_transportation", output_folder="test072" + ) + + # create the simulation + sim = gate.Simulation() + + # main options + ui = sim.user_info + ui.g4_verbose = False + + # ui.visu = True + # ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.EVENT + ui.number_of_threads = 1 + ui.random_seed = "auto" + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + um = gate.g4_units.um + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + gcm3 = gate.g4_units.g / gate.g4_units.cm3 + deg = gate.g4_units.deg + + # adapt world size + world = sim.world + world.size = [1.2 * m, 1.2 * m, 2 * m] + world.material = "G4_Galactic" + + ####### GEOMETRY TO IRRADIATE ############# + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + simple_collimation = sim.add_volume("Box", "colli_box") + simple_collimation.material = "G4_Galactic" + simple_collimation.mother = world.name + simple_collimation.size = [1 * m, 1 * m, 40 * cm] + simple_collimation.color = [0.3, 0.1, 0.8, 1] + + W_leaf = sim.add_volume("Box", "W_leaf") + W_leaf.mother = simple_collimation.name + W_leaf.size = [1 * m, 1 * m, 0.5 * cm] + W_leaf.material = "Tungsten" + leaf_translation = [] + for i in range(10): + leaf_translation.append( + [ + 0, + 0, + -0.5 * simple_collimation.size[2] + + 0.5 * W_leaf.size[2] + + i * W_leaf.size[2], + ] + ) + W_leaf.translation = leaf_translation + W_leaf.color = [0.8, 0.2, 0.1, 1] + + ######## pseudo_transportation ACTOR ######### + nb_split = 5 + pseudo_transportation_actor = sim.add_actor( + "ComptPseudoTransportationActor", "pseudo_transportation_actor" + ) + pseudo_transportation_actor.mother = simple_collimation.name + pseudo_transportation_actor.splitting_factor = nb_split + pseudo_transportation_actor.relative_min_weight_of_particle = 10000 + if j ==0: + pseudo_transportation_actor.russian_roulette_for_weights = False + if j ==1 : + pseudo_transportation_actor.russian_roulette_for_weights = True + list_processes_to_bias = pseudo_transportation_actor.processes + + ##### PHASE SPACE plan ######" + plan = sim.add_volume("Box", "phsp") + plan.material = "G4_Galactic" + plan.mother = world.name + plan.size = [1 * m, 1 * m, 1 * nm] + plan.color = [0.2, 1, 0.8, 1] + plan.translation = [0, 0, -20 * cm - 1 * nm] + + ####### gamma source ########### + nb_event = 20000 + source = sim.add_source("GenericSource", "source1") + source.particle = "gamma" + source.n = nb_event + source.position.type = "sphere" + source.position.radius = 1 * nm + source.direction.type = "momentum" + source.direction.momentum = [0, 0, -1] + source.energy.type = "mono" + source.energy.mono = 6 * MeV + source.position.translation = [0, 0, 18 * cm] + + ####### PHASE SPACE ACTOR ############## + + phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") + phsp_actor.mother = plan.name + phsp_actor.attributes = [ + "EventID", + "TrackCreatorProcess", + "TrackVertexPosition", + "PrePosition", + "Weight", + "KineticEnergy", + "ParentID", + "ParticleName", + ] + data_name = "test072_output_data_RR_"+ str(j)+".root" + phsp_actor.output = paths.output / data_name + + ##### MODIFIED PHYSICS LIST ############### + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + ### Perhaps avoid the user to call the below boolean function ? ### + sim.physics_manager.special_physics_constructors.G4GenericBiasingPhysics = True + sim.physics_manager.processes_to_bias.gamma = list_processes_to_bias + s = f"/process/em/UseGeneralProcess false" + sim.add_g4_command_before_init(s) + + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * km + sim.physics_manager.global_production_cuts.positron = 1 * km + + output = sim.run(True) + + # + # # print results + stats = sim.output.get_actor("Stats") + h = sim.output.get_actor("PhaseSpace") + print(stats) + # + + f_1 = uproot.open(paths.output / "test072_output_data_RR_0.root") + f_2 = uproot.open(paths.output / "test072_output_data_RR_1.root") + + + arr_no_RR = f_1["PhaseSpace"].arrays() + arr_RR = f_2["PhaseSpace"].arrays() + + + is_ok = validation_test(arr_no_RR,arr_RR,nb_event,nb_split) + utility.test_ok(is_ok) From bbd1dbfcc27ab5b9380f26c93f742f93a3a01dfd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:24:08 +0000 Subject: [PATCH 07/82] [pre-commit.ci] Automatic python and c++ formatting --- ...GateOptrComptPseudoTransportationActor.cpp | 20 +++++-- .../GateOptrComptPseudoTransportationActor.h | 2 +- opengate/actors/miscactors.py | 2 +- .../tests/src/test072_pseudo_transport_RR.py | 56 +++++++++++-------- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 214b5e513..a2ea6d4c1 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -33,6 +33,7 @@ #include "CLHEP/Units/SystemOfUnits.h" #include "G4BiasingProcessInterface.hh" #include "G4EmCalculator.hh" +#include "G4Exception.hh" #include "G4Gamma.hh" #include "G4LogicalVolumeStore.hh" #include "G4ParticleTable.hh" @@ -45,7 +46,6 @@ #include "G4VEmProcess.hh" #include "GateOptnComptSplittingForTransportation.h" #include "GateOptrComptPseudoTransportationActor.h" -#include "G4Exception.hh" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -81,8 +81,10 @@ GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( void GateOptrComptPseudoTransportationActor::AttachAllLogicalDaughtersVolumes( G4LogicalVolume *volume) { - if (fAttachToLogicalHolder == false){ - if (volume->GetName() != G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName)->GetName()){ + if (fAttachToLogicalHolder == false) { + if (volume->GetName() != G4LogicalVolumeStore::GetInstance() + ->GetVolume(fMotherVolumeName) + ->GetName()) { AttachTo(volume); } } @@ -152,7 +154,9 @@ void GateOptrComptPseudoTransportationActor::StartTracking( const G4Track *track) { if (track->GetCreatorProcess() != 0) { - if ((track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") &&(track->GetParticleDefinition()->GetParticleName() == "gamma")) { + if ((track->GetCreatorProcess()->GetProcessName() == + "biasWrapper(compt)") && + (track->GetParticleDefinition()->GetParticleName() == "gamma")) { fInitialWeight = track->GetWeight(); fFreeFlightOperation->SetInitialWeight(fInitialWeight); } @@ -174,7 +178,9 @@ G4VBiasingOperation * GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation( const G4Track *track, const G4BiasingProcessInterface *callingProcess) { if (track->GetCreatorProcess() != 0) { - if ((track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") && (track->GetParticleDefinition()->GetParticleName() == "gamma")){ + if ((track->GetCreatorProcess()->GetProcessName() == + "biasWrapper(compt)") && + (track->GetParticleDefinition()->GetParticleName() == "gamma")) { fFreeFlightOperation->SetMinWeight(fInitialWeight / fRelativeMinWeightOfParticle); fFreeFlightOperation->SetTrackWeight(track->GetWeight()); @@ -210,7 +216,9 @@ GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( /* */ if (track->GetCreatorProcess() != 0) { - if ((track->GetCreatorProcess()->GetProcessName() == "biasWrapper(compt)") &&(track->GetParticleDefinition()->GetParticleName() == "gamma")) { + if ((track->GetCreatorProcess()->GetProcessName() == + "biasWrapper(compt)") && + (track->GetParticleDefinition()->GetParticleName() == "gamma")) { return callingProcess->GetCurrentOccurenceBiasingOperation(); } } diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index f69eab760..3b7d6b457 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -78,7 +78,7 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4bool fUseProbes = false; G4bool fSurvivedRR = false; G4bool fAttachToLogicalHolder = true; - + // Unused but mandatory virtual void StartSimulationAction(); diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index adf8e966c..eb9970b36 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -480,7 +480,7 @@ def set_default_user_info(user_info): user_info.rotation_vector_director = False user_info.vector_director = [0, 0, 1] user_info.max_theta = 90 * deg - user_info.russian_roulette_for_weights= False + user_info.russian_roulette_for_weights = False def __init__(self, user_info): ActorBase.__init__(self, user_info) diff --git a/opengate/tests/src/test072_pseudo_transport_RR.py b/opengate/tests/src/test072_pseudo_transport_RR.py index d4d7f8769..e39832182 100644 --- a/opengate/tests/src/test072_pseudo_transport_RR.py +++ b/opengate/tests/src/test072_pseudo_transport_RR.py @@ -10,7 +10,8 @@ from scipy.spatial.transform import Rotation from opengate.tests import utility -def Ekin_per_event(weight,EventID,Ekin) : + +def Ekin_per_event(weight, EventID, Ekin): l_edep = [] Edep_per_event = 0 last_EventID = 0 @@ -21,45 +22,56 @@ def Ekin_per_event(weight,EventID,Ekin) : l_edep.append(Edep_per_event) Edep_per_event = 0 Edep_per_event += Ekin[i] * weight[i] - return(np.asarray(l_edep)) -def validation_test(arr_no_RR,arr_RR,nb_event,splitting_factor, tol=0.15): + return np.asarray(l_edep) + + +def validation_test(arr_no_RR, arr_RR, nb_event, splitting_factor, tol=0.15): Tracks_no_RR = arr_no_RR[ (arr_no_RR["TrackCreatorProcess"] == "biasWrapper(compt)") - & (arr_no_RR["ParticleName"] == "gamma")] + & (arr_no_RR["ParticleName"] == "gamma") + ] Tracks_RR = arr_RR[ (arr_RR["TrackCreatorProcess"] == "biasWrapper(compt)") - & (arr_RR["ParticleName"] == "gamma")] - + & (arr_RR["ParticleName"] == "gamma") + ] Ekin_no_RR = Tracks_no_RR["KineticEnergy"] w_no_RR = Tracks_no_RR["Weight"] Event_ID_no_RR = Tracks_no_RR["EventID"] - Ekin_per_event_no_RR = Ekin_per_event(w_no_RR,Event_ID_no_RR,Ekin_no_RR) - + Ekin_per_event_no_RR = Ekin_per_event(w_no_RR, Event_ID_no_RR, Ekin_no_RR) Ekin_RR = Tracks_RR["KineticEnergy"] w_RR = Tracks_RR["Weight"] Event_ID_RR = Tracks_RR["EventID"] - bool_RR = (np.min(w_RR) > 0.1*1/splitting_factor) & (np.max(w_RR) < 1/splitting_factor) + bool_RR = (np.min(w_RR) > 0.1 * 1 / splitting_factor) & ( + np.max(w_RR) < 1 / splitting_factor + ) Ekin_per_event_RR = Ekin_per_event(w_RR, Event_ID_RR, Ekin_RR) - mean_ekin_event_no_RR = np.sum(Ekin_per_event_no_RR)/nb_event - mean_ekin_event_RR = np.sum(Ekin_per_event_RR)/nb_event + mean_ekin_event_no_RR = np.sum(Ekin_per_event_no_RR) / nb_event + mean_ekin_event_RR = np.sum(Ekin_per_event_RR) / nb_event diff = (mean_ekin_event_RR - mean_ekin_event_no_RR) / mean_ekin_event_RR - print("Mean kinetic energy per event without russian roulette :",np.round(1000*mean_ekin_event_no_RR,1), 'keV') - print("Mean kinetic energy per event with russian roulette :", np.round(1000*mean_ekin_event_RR,1), 'keV') - print("Percentage of difference :",diff*100,"%") - - return (abs(diff) < tol) &( bool_RR) + print( + "Mean kinetic energy per event without russian roulette :", + np.round(1000 * mean_ekin_event_no_RR, 1), + "keV", + ) + print( + "Mean kinetic energy per event with russian roulette :", + np.round(1000 * mean_ekin_event_RR, 1), + "keV", + ) + print("Percentage of difference :", diff * 100, "%") + + return (abs(diff) < tol) & (bool_RR) # mean_energy_per_history_no_RR = np.sum(Tracks_no_RR["KineticEnergy"]*Tracks_no_RR["Weight"])/nb_event # mean_energy_per_history_RR = np.sum(Tracks_RR["KineticEnergy"] * Tracks_RR["Weight"]) / nb_event - # print(mean_energy_per_history_no_RR,mean_energy_per_history_RR) @@ -141,9 +153,9 @@ def validation_test(arr_no_RR,arr_RR,nb_event,splitting_factor, tol=0.15): pseudo_transportation_actor.mother = simple_collimation.name pseudo_transportation_actor.splitting_factor = nb_split pseudo_transportation_actor.relative_min_weight_of_particle = 10000 - if j ==0: + if j == 0: pseudo_transportation_actor.russian_roulette_for_weights = False - if j ==1 : + if j == 1: pseudo_transportation_actor.russian_roulette_for_weights = True list_processes_to_bias = pseudo_transportation_actor.processes @@ -182,7 +194,7 @@ def validation_test(arr_no_RR,arr_RR,nb_event,splitting_factor, tol=0.15): "ParentID", "ParticleName", ] - data_name = "test072_output_data_RR_"+ str(j)+".root" + data_name = "test072_output_data_RR_" + str(j) + ".root" phsp_actor.output = paths.output / data_name ##### MODIFIED PHYSICS LIST ############### @@ -212,10 +224,8 @@ def validation_test(arr_no_RR,arr_RR,nb_event,splitting_factor, tol=0.15): f_1 = uproot.open(paths.output / "test072_output_data_RR_0.root") f_2 = uproot.open(paths.output / "test072_output_data_RR_1.root") - arr_no_RR = f_1["PhaseSpace"].arrays() arr_RR = f_2["PhaseSpace"].arrays() - - is_ok = validation_test(arr_no_RR,arr_RR,nb_event,nb_split) + is_ok = validation_test(arr_no_RR, arr_RR, nb_event, nb_split) utility.test_ok(is_ok) From d0ff62142481d5cf7113e1fc3ba3262198bb79f7 Mon Sep 17 00:00:00 2001 From: majacquet Date: Mon, 18 Mar 2024 10:09:38 +0100 Subject: [PATCH 08/82] Improvement of the pseudo-transportation. Each process creating gamma (except fluorescence) is split. Need to provide some tests to verify all features. --- .../GateOptnPairProdSplitting.cpp | 102 +++++++++++ .../opengate_lib/GateOptnPairProdSplitting.h | 53 ++++++ ...pp => GateOptnScatteredGammaSplitting.cpp} | 112 ++++-------- .../GateOptnScatteredGammaSplitting.h | 56 ++++++ .../GateOptnVGenericSplitting.cpp | 105 +++++++++++ ...ortation.h => GateOptnVGenericSplitting.h} | 28 +-- .../opengate_lib/GateOptneBremSplitting.cpp | 107 ++++++++++++ .../opengate_lib/GateOptneBremSplitting.h | 56 ++++++ ...GateOptrComptPseudoTransportationActor.cpp | 164 +++++++++++++----- .../GateOptrComptPseudoTransportationActor.h | 23 ++- opengate/actors/miscactors.py | 4 +- opengate/tests/data | 2 +- .../tests/src/test072_pseudo_transport_RR.py | 2 +- .../src/test072_pseudo_transportation.py | 2 +- 14 files changed, 671 insertions(+), 145 deletions(-) create mode 100644 core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h rename core/opengate_core/opengate_lib/{GateOptnComptSplittingForTransportation.cpp => GateOptnScatteredGammaSplitting.cpp} (60%) create mode 100644 core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h create mode 100644 core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp rename core/opengate_core/opengate_lib/{GateOptnComptSplittingForTransportation.h => GateOptnVGenericSplitting.h} (80%) create mode 100644 core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptneBremSplitting.h diff --git a/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp new file mode 100644 index 000000000..767f8a1be --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp @@ -0,0 +1,102 @@ +// +// ******************************************************************** +// * 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 GateOptnPairProdSplitting.cc +/// \brief Implementation of the GateOptnPairProdSplitting class + +#include "GateOptnPairProdSplitting.h" +#include "G4BiasingProcessInterface.hh" + +#include "G4ComptonScattering.hh" +#include "G4DynamicParticle.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" +#include "G4GammaConversion.hh" +#include "G4ParticleChange.hh" +#include "G4ParticleChangeForGamma.hh" +#include "G4ParticleChangeForLoss.hh" +#include "G4PhotoElectricEffect.hh" +#include "G4ProcessType.hh" +#include "G4RayleighScattering.hh" +#include "G4SystemOfUnits.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VEmProcess.hh" +#include + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnPairProdSplitting:: + GateOptnPairProdSplitting(G4String name) + : GateOptnVGenericSplitting(name), fParticleChange() {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnPairProdSplitting:: + ~GateOptnPairProdSplitting() {} + +G4VParticleChange * +GateOptnPairProdSplitting::ApplyFinalStateBiasing( const G4BiasingProcessInterface *callingProcess, const G4Track *track, const G4Step *step, G4bool &) { + + + G4int splittingFactor = ceil(fSplittingFactor); + G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor) / splittingFactor; + G4double particleWeight = 0; + + G4VParticleChange* processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + + if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) return processFinalState; + TrackInitializationGamma(&fParticleChange,processFinalState,track,fSplittingFactor); + + processFinalState->Clear(); + + G4int nCalls = 1; + while (nCalls <= splittingFactor) { + G4double splittingProbability = G4UniformRand(); + if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { + particleWeight = track->GetWeight() / fSplittingFactor; + G4VParticleChange *processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if (processFinalState->GetNumberOfSecondaries() >= 1 ) { + for(int i =0; i < processFinalState->GetNumberOfSecondaries();i++){ + G4Track *SecondaryTrack =processFinalState->GetSecondary(i); + SecondaryTrack->SetWeight(particleWeight); + fParticleChange.AddSecondary(SecondaryTrack); + } + } + } + nCalls++; + } + return &fParticleChange; +} + + + + + + + + + diff --git a/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h new file mode 100644 index 000000000..fd1f172c5 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h @@ -0,0 +1,53 @@ +// +// ******************************************************************** +// * 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 GateOptnPairProdSplitting.h +/// \brief Definition of the GateOptnPairProdSplitting class +// + +#ifndef GateOptnPairProdSplitting_h +#define GateOptnPairProdSplitting_h 1 + +#include "GateOptnVGenericSplitting.h" +#include "G4ParticleChange.hh" + +class GateOptnPairProdSplitting : public GateOptnVGenericSplitting { +public: + // -- Constructor : + GateOptnPairProdSplitting(G4String name); + + // -- destructor: + virtual ~GateOptnPairProdSplitting(); + + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); + +G4ParticleChange fParticleChange; + +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp similarity index 60% rename from core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp rename to core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp index 6bdec6e6b..8dbb41be6 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.cpp +++ b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp @@ -24,10 +24,10 @@ // ******************************************************************** // // -/// \file GateOptnComptSplittingForTransportation.cc -/// \brief Implementation of the GateOptnComptSplittingForTransportation class +/// \file GateOptnScatteredGammaSplitting.cc +/// \brief Implementation of the GateOptnScatteredGammaSplitting class -#include "GateOptnComptSplittingForTransportation.h" +#include "GateOptnScatteredGammaSplitting.h" #include "G4BiasingProcessInterface.hh" #include "G4ComptonScattering.hh" @@ -37,6 +37,7 @@ #include "G4GammaConversion.hh" #include "G4ParticleChange.hh" #include "G4ParticleChangeForGamma.hh" +#include "GateOptnVGenericSplitting.h" #include "G4PhotoElectricEffect.hh" #include "G4ProcessType.hh" #include "G4RayleighScattering.hh" @@ -48,18 +49,17 @@ //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnComptSplittingForTransportation:: - GateOptnComptSplittingForTransportation(G4String name) - : G4VBiasingOperation(name), fSplittingFactor(1), - fRussianRouletteForAngle(false), fParticleChange() {} +GateOptnScatteredGammaSplitting:: + GateOptnScatteredGammaSplitting(G4String name) + : GateOptnVGenericSplitting(name),fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnComptSplittingForTransportation:: - ~GateOptnComptSplittingForTransportation() {} +GateOptnScatteredGammaSplitting:: + ~GateOptnScatteredGammaSplitting() {} G4VParticleChange * -GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( +GateOptnScatteredGammaSplitting::ApplyFinalStateBiasing( const G4BiasingProcessInterface *callingProcess, const G4Track *track, const G4Step *step, G4bool &) { @@ -68,50 +68,22 @@ GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( // interaction Then the interaction location of the compton process will // always be the same - G4double globalTime = step->GetTrack()->GetGlobalTime(); const G4ThreeVector position = step->GetPostStepPoint()->GetPosition(); - G4int nCalls = 1; G4int splittingFactor = ceil(fSplittingFactor); - G4double survivalProbabilitySplitting = - 1 - (splittingFactor - fSplittingFactor) / splittingFactor; - G4bool isRightAngle = false; + G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor) / splittingFactor; G4double gammaWeight = 0; - G4int nbSecondaries = 0; - G4VParticleChange *processFinalState = nullptr; - G4ParticleChangeForGamma *castedProcessInitFinalState = nullptr; - processFinalState = - callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + + + G4VParticleChange* processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); // In case we don't want to split (a bit faster) i.e no biaising or no // splitting low weights particles. - if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) - return processFinalState; - - castedProcessInitFinalState = (G4ParticleChangeForGamma *)processFinalState; - nbSecondaries = processFinalState->GetNumberOfSecondaries(); - - fParticleChange.Initialize(*track); - fParticleChange.ProposeWeight(track->GetWeight()); - fParticleChange.ProposeTrackStatus( - castedProcessInitFinalState->GetTrackStatus()); - fParticleChange.ProposeEnergy( - castedProcessInitFinalState->GetProposedKineticEnergy()); - fParticleChange.ProposeMomentumDirection( - castedProcessInitFinalState->GetProposedMomentumDirection()); - fParticleChange.SetSecondaryWeightByProcess(true); - - // If there is cut on secondary particles, there is a probability that the - // electron is not simulated Then, if the compton process created it, we add - // the gien electron to the ParticleChange object - if (nbSecondaries == 1) { - G4Track *initElectronTrack = castedProcessInitFinalState->GetSecondary(0); - initElectronTrack->SetWeight(track->GetWeight()); - fParticleChange.AddSecondary(initElectronTrack); - } + if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) return processFinalState; + TrackInitializationGamma(&fParticleChange,processFinalState,track,fSplittingFactor); processFinalState->Clear(); - castedProcessInitFinalState->Clear(); + // There is here the biasing process : // Since G4VParticleChange class does not allow to retrieve scattered gamma @@ -133,66 +105,48 @@ GateOptnComptSplittingForTransportation::ApplyFinalStateBiasing( // gamma) must be considered as secondary particles, even though generated // gamma will not be cut here by the applied cut. - G4int simulationTrackID = 0; + + G4int nCalls = 1; while (nCalls <= splittingFactor) { gammaWeight = track->GetWeight() / fSplittingFactor; - G4double initGammaWeight = track->GetWeight(); - G4VParticleChange *processGammaSplittedFinalState = - callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - G4ParticleChangeForGamma *castedProcessGammaSplittedFinalState = - (G4ParticleChangeForGamma *)processGammaSplittedFinalState; - const G4ThreeVector momentum = - castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(); - G4double energy = - castedProcessGammaSplittedFinalState->GetProposedKineticEnergy(); - G4double cosTheta = - fVectorDirector * - castedProcessInitFinalState->GetProposedMomentumDirection(); - G4double theta = std::acos(cosTheta); + G4VParticleChange *processGammaSplittedFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + G4ParticleChangeForGamma *castedProcessGammaSplittedFinalState = (G4ParticleChangeForGamma *)processGammaSplittedFinalState; + const G4ThreeVector momentum = castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(); + G4double energy = castedProcessGammaSplittedFinalState->GetProposedKineticEnergy(); G4double splittingProbability = G4UniformRand(); - if (splittingProbability <= survivalProbabilitySplitting || - survivalProbabilitySplitting == 1) { - if ((fRussianRouletteForAngle == true) && (theta > fMaxTheta)) { - G4double probability = G4UniformRand(); - if (probability < 1 / fSplittingFactor) { - // Specific case where the russian roulette probability is - // 1/splitting. Each particle generated, with a 1/split probability - // will have a 1/split probability to survive with a final weight of - // Initial weights * 1/split * split = Initial weight - gammaWeight = gammaWeight * fSplittingFactor; + if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { + if (fRussianRouletteForAngle == true){ + G4double weightToApply = RussianRouletteForAngleSurvival(castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(),fVectorDirector,fMaxTheta,fSplittingFactor); + if (weightToApply != 0){ + gammaWeight = gammaWeight * weightToApply; G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); gammaTrack->SetKineticEnergy(energy); gammaTrack->SetMomentumDirection(momentum); gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); - simulationTrackID++; if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { - G4Track *electronTrack = - processGammaSplittedFinalState->GetSecondary(0); + G4Track *electronTrack = processGammaSplittedFinalState->GetSecondary(0); electronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(electronTrack); - simulationTrackID++; } } } - if ((fRussianRouletteForAngle == false) || - ((fRussianRouletteForAngle == true) && (theta <= fMaxTheta))) { + + else{ G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); gammaTrack->SetKineticEnergy(energy); gammaTrack->SetMomentumDirection(momentum); gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); - simulationTrackID++; if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { - G4Track *electronTrack = - processGammaSplittedFinalState->GetSecondary(0); + G4Track *electronTrack = processGammaSplittedFinalState->GetSecondary(0); electronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(electronTrack); - simulationTrackID++; + } } } diff --git a/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h new file mode 100644 index 000000000..56a6650cc --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h @@ -0,0 +1,56 @@ +// +// ******************************************************************** +// * 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 GateOptnScatteredGammaSplitting.h +/// \brief Definition of the GateOptnScatteredGammaSplitting class +// + +#ifndef GateOptnScatteredGammaSplitting_h +#define GateOptnScatteredGammaSplitting_h 1 + +#include "G4ParticleChange.hh" +#include "G4VBiasingOperation.hh" +#include "GateOptnVGenericSplitting.h" + +class GateOptnScatteredGammaSplitting : public GateOptnVGenericSplitting { +public: + // -- Constructor : + GateOptnScatteredGammaSplitting(G4String name); + + // -- destructor: + virtual ~GateOptnScatteredGammaSplitting(); + + + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); + + G4ParticleChange fParticleChange; + + +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp new file mode 100644 index 000000000..349a393ff --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp @@ -0,0 +1,105 @@ +// +// ******************************************************************** +// * 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 GateOptnVGenericSplitting.cc +/// \brief Implementation of the GateOptnVGenericSplitting class + +#include "GateOptnVGenericSplitting.h" +#include "G4BiasingProcessInterface.hh" + +#include "G4ComptonScattering.hh" +#include "G4DynamicParticle.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" +#include "G4GammaConversion.hh" +#include "G4ParticleChange.hh" +#include "G4ParticleChangeForGamma.hh" +#include "G4ParticleChangeForLoss.hh" +#include "G4PhotoElectricEffect.hh" +#include "G4ProcessType.hh" +#include "G4RayleighScattering.hh" +#include "G4SystemOfUnits.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VEmProcess.hh" +#include + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnVGenericSplitting:: + GateOptnVGenericSplitting(G4String name) + : G4VBiasingOperation(name), fParticleChange() {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnVGenericSplitting:: + ~GateOptnVGenericSplitting() {} + + +void GateOptnVGenericSplitting::TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split) { + + G4ParticleChangeForLoss* processFinalStateForLoss =( G4ParticleChangeForLoss* ) processFinalState ; + particleChange->Initialize(*track); + particleChange->ProposeTrackStatus(processFinalStateForLoss->GetTrackStatus() ); + particleChange->ProposeEnergy(processFinalStateForLoss->GetProposedKineticEnergy() ); + particleChange->ProposeMomentumDirection(processFinalStateForLoss->GetProposedMomentumDirection()); + particleChange->SetNumberOfSecondaries(fSplittingFactor); + particleChange->SetSecondaryWeightByProcess(true); + processFinalStateForLoss->Clear(); +} + +void GateOptnVGenericSplitting::TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split) { + G4ParticleChangeForGamma* processFinalStateForGamma = (G4ParticleChangeForGamma *)processFinalState; + particleChange->Initialize(*track); + particleChange->ProposeTrackStatus(processFinalStateForGamma->GetTrackStatus() ); + particleChange->ProposeEnergy(processFinalStateForGamma->GetProposedKineticEnergy() ); + particleChange->ProposeMomentumDirection(processFinalStateForGamma->GetProposedMomentumDirection() ); + particleChange->SetNumberOfSecondaries(fSplittingFactor); + particleChange->SetSecondaryWeightByProcess(true); + processFinalStateForGamma->Clear(); + + +} + +G4double GateOptnVGenericSplitting::RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split){ +G4double cosTheta =vectorDirector * dir; +G4double theta = std::acos(cosTheta); +G4double weightToApply = 1; +if (theta > fMaxTheta){ + G4double probability = G4UniformRand(); + if (probability <= 1 / split) { + weightToApply = split; + } + else{ + G4double weightToApply = 0; + } +} +return weightToApply; + +} + + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h similarity index 80% rename from core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h rename to core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h index 5dff8f3f1..ed75c9edd 100644 --- a/core/opengate_core/opengate_lib/GateOptnComptSplittingForTransportation.h +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h @@ -24,23 +24,23 @@ // ******************************************************************** // // -/// \file GateOptnComptSplittingForTransportation.h -/// \brief Definition of the GateOptnComptSplittingForTransportation class +/// \file GateOptnVGenericSplitting.h +/// \brief Definition of the GateOptnVGenericSplitting class // -#ifndef GateOptnComptSplittingForTransportation_h -#define GateOptnComptSplittingForTransportation_h 1 +#ifndef GateOptnVGenericSplitting_h +#define GateOptnVGenericSplitting_h 1 #include "G4ParticleChange.hh" #include "G4VBiasingOperation.hh" -class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { +class GateOptnVGenericSplitting : public G4VBiasingOperation { public: // -- Constructor : - GateOptnComptSplittingForTransportation(G4String name); + GateOptnVGenericSplitting(G4String name); // -- destructor: - virtual ~GateOptnComptSplittingForTransportation(); + virtual ~GateOptnVGenericSplitting(); public: // ---------------------------------------------- @@ -56,7 +56,7 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { // --Used: virtual G4VParticleChange * ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, - const G4Step *, G4bool &); + const G4Step *, G4bool &){return 0;}; // -- Unsued: virtual G4double DistanceToApplyOperation(const G4Track *, G4double, @@ -68,6 +68,15 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { return 0; } +// ---------------------------------------------- +// -- Methods for the generic splitting +// ---------------------------------------------- + +void TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); +void TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); +G4double RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split); + + public: // ---------------------------------------------- // -- Additional methods, specific to this class: @@ -99,15 +108,12 @@ class GateOptnComptSplittingForTransportation : public G4VBiasingOperation { return particleChange; } -private: G4double fSplittingFactor; G4ParticleChange fParticleChange; G4bool fRussianRouletteForAngle; G4ThreeVector fVectorDirector; G4double fMaxTheta; G4RotationMatrix fRot; - // G4DynamicParticle* fSplitParticle; - // G4Track* fGammaTrack; }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp b/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp new file mode 100644 index 000000000..f067220f9 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp @@ -0,0 +1,107 @@ +// +// ******************************************************************** +// * 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 GateOptneBremSplitting.cc +/// \brief Implementation of the GateOptneBremSplitting class + +#include "GateOptneBremSplitting.h" +#include "G4BiasingProcessInterface.hh" + +#include "G4ComptonScattering.hh" +#include "G4DynamicParticle.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" +#include "G4GammaConversion.hh" +#include "G4ParticleChange.hh" +#include "GateOptnVGenericSplitting.h" +#include "G4ParticleChangeForGamma.hh" +#include "G4ParticleChangeForLoss.hh" +#include "G4PhotoElectricEffect.hh" +#include "G4ProcessType.hh" +#include "G4RayleighScattering.hh" +#include "G4SystemOfUnits.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VEmProcess.hh" +#include + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptneBremSplitting:: + GateOptneBremSplitting(G4String name) + :GateOptnVGenericSplitting(name), fParticleChange() {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptneBremSplitting:: + ~GateOptneBremSplitting() {} + +G4VParticleChange * +GateOptneBremSplitting::ApplyFinalStateBiasing(const G4BiasingProcessInterface *callingProcess, const G4Track *track, const G4Step *step, G4bool &) { + + G4int splittingFactor = ceil(fSplittingFactor); + G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor) / splittingFactor; + G4VParticleChange* processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if ( fSplittingFactor == 1 ) return processFinalState; + if ( processFinalState->GetNumberOfSecondaries() == 0 ) return processFinalState; + + TrackInitializationChargedParticle(&fParticleChange,processFinalState, track,fSplittingFactor); + + + processFinalState->Clear(); + + + G4int nCalls = 1; + while ( nCalls <= fSplittingFactor ){ + G4double splittingProbability = G4UniformRand(); + if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { + processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if ( processFinalState->GetNumberOfSecondaries() >= 1 ) { + for(int i =0; i < processFinalState->GetNumberOfSecondaries();i++){ + G4double gammaWeight = track->GetWeight() / fSplittingFactor; + G4Track* gammaTrack = processFinalState->GetSecondary(i); + if (fRussianRouletteForAngle == true){ + G4double weightToApply = RussianRouletteForAngleSurvival(gammaTrack->GetMomentumDirection(),fVectorDirector,fMaxTheta,fSplittingFactor); + if (weightToApply != 0){ + gammaWeight = gammaWeight * weightToApply; + gammaTrack->SetWeight( gammaWeight); + fParticleChange.AddSecondary( gammaTrack ); + } + } + else { + gammaTrack->SetWeight( gammaWeight); + fParticleChange.AddSecondary(gammaTrack); + } + } + } + processFinalState->Clear(); + } + nCalls++; + } + return &fParticleChange; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptneBremSplitting.h b/core/opengate_core/opengate_lib/GateOptneBremSplitting.h new file mode 100644 index 000000000..cddd10ece --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptneBremSplitting.h @@ -0,0 +1,56 @@ +// +// ******************************************************************** +// * 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 GateOptneBremSplitting.h +/// \brief Definition of the GateOptneBremSplitting class +// + +#ifndef GateOptneBremSplitting_h +#define GateOptneBremSplitting_h 1 + +#include "G4ParticleChange.hh" +#include "G4VBiasingOperation.hh" +#include "GateOptnVGenericSplitting.h" + +class GateOptneBremSplitting : public GateOptnVGenericSplitting { +public: + // -- Constructor : + GateOptneBremSplitting(G4String name); + + // -- destructor: + virtual ~GateOptneBremSplitting(); + +public: + + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); + + G4ParticleChange fParticleChange; + +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index a2ea6d4c1..e7a2d1e5a 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -35,6 +35,8 @@ #include "G4EmCalculator.hh" #include "G4Exception.hh" #include "G4Gamma.hh" +#include "G4Positron.hh" +#include "G4Electron.hh" #include "G4LogicalVolumeStore.hh" #include "G4ParticleTable.hh" #include "G4PhysicalVolumeStore.hh" @@ -44,8 +46,12 @@ #include "G4TrackStatus.hh" #include "G4UserTrackingAction.hh" #include "G4VEmProcess.hh" -#include "GateOptnComptSplittingForTransportation.h" +#include "GateOptnScatteredGammaSplitting.h" +#include "GateOptneBremSplitting.h" +#include "GateOptnPairProdSplitting.h" #include "GateOptrComptPseudoTransportationActor.h" +#include "G4UImanager.hh" +#include "G4eplusAnnihilation.hh" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -69,12 +75,15 @@ GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( DictGetBool(user_info, "russian_roulette_for_weights"); fMaxTheta = DictGetDouble(user_info, "max_theta"); fFreeFlightOperation = new GateOptnForceFreeFlight("freeFlightOperation"); - fComptSplittingOperation = - new GateOptnComptSplittingForTransportation("comptSplittingOperation"); + fScatteredGammaSplittingOperation = new GateOptnScatteredGammaSplitting("comptSplittingOperation"); + feBremSplittingOperation = new GateOptneBremSplitting("eBremSplittingOperation"); + fPairProdSplittingOperation = new GateOptnPairProdSplitting("PairProdSplittingOperation"); fActions.insert("StartSimulationAction"); fActions.insert("SteppingAction"); fActions.insert("BeginOfEventAction"); isSplitted = false; + + } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -88,14 +97,18 @@ void GateOptrComptPseudoTransportationActor::AttachAllLogicalDaughtersVolumes( AttachTo(volume); } } - if (fAttachToLogicalHolder == true) + if (fAttachToLogicalHolder == true){ AttachTo(volume); + } G4int nbOfDaughters = volume->GetNoDaughters(); if (nbOfDaughters > 0) { for (int i = 0; i < nbOfDaughters; i++) { + G4String LogicalVolumeName = volume->GetDaughter(i)->GetLogicalVolume()->GetName(); G4LogicalVolume *logicalDaughtersVolume = volume->GetDaughter(i)->GetLogicalVolume(); AttachAllLogicalDaughtersVolumes(logicalDaughtersVolume); + if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end())) + fNameOfBiasedLogicalVolume.push_back(volume->GetDaughter(i)->GetLogicalVolume()->GetName()); } } } @@ -108,12 +121,22 @@ void GateOptrComptPseudoTransportationActor::StartSimulationAction() { // to the biasing operator. To do that, I use the function // AttachAllLogicalDaughtersVolumes. AttachAllLogicalDaughtersVolumes(biasingVolume); - fComptSplittingOperation->SetSplittingFactor(fSplittingFactor); - fComptSplittingOperation->SetMaxTheta(fMaxTheta); - fComptSplittingOperation->SetRussianRouletteForAngle( - fRussianRouletteForAngle); - fFreeFlightOperation->SetRussianRouletteForWeights( - fRussianRouletteForWeights); + for (int i = 0; i < fNameOfBiasedLogicalVolume.size(); i++) + fScatteredGammaSplittingOperation->SetSplittingFactor(fSplittingFactor); + fScatteredGammaSplittingOperation->SetMaxTheta(fMaxTheta); + fScatteredGammaSplittingOperation->SetRussianRouletteForAngle(fRussianRouletteForAngle); + + feBremSplittingOperation->SetSplittingFactor(fSplittingFactor); + feBremSplittingOperation->SetMaxTheta(fMaxTheta); + feBremSplittingOperation->SetRussianRouletteForAngle(fRussianRouletteForAngle); + + fPairProdSplittingOperation->SetSplittingFactor(fSplittingFactor); + + + + + fFreeFlightOperation->SetRussianRouletteForWeights(fRussianRouletteForWeights); + } void GateOptrComptPseudoTransportationActor::StartRun() { @@ -128,19 +151,48 @@ void GateOptrComptPseudoTransportationActor::StartRun() { G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); auto rot = physBiasingVolume->GetObjectRotationValue(); fVectorDirector = rot * fVectorDirector; - fComptSplittingOperation->SetRotationMatrix(rot); + fScatteredGammaSplittingOperation->SetRotationMatrix(rot); + feBremSplittingOperation->SetRotationMatrix(rot); } - fComptSplittingOperation->SetVectorDirector(fVectorDirector); + fScatteredGammaSplittingOperation->SetVectorDirector(fVectorDirector); + feBremSplittingOperation->SetVectorDirector(fVectorDirector); } + + void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { - if ((isSplitted == true) && - (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { - if ((step->GetPostStepPoint()->GetPhysicalVolume()->GetName() == "world")) { - step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); - isSplitted = false; + + if ((isSplitted == true) && (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { + G4String LogicalVolumeName = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end()) + && (LogicalVolumeName != fMotherVolumeName)) { + step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); + isSplitted = false; + } + } + + + if (fKillPrimaries) { + + G4String LogicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + G4String LogicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (fPassedByABiasedVolume == false){ + if (LogicalVolumeNamePreStep == fMotherVolumeName){ + fPassedByABiasedVolume =true; + fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); + ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); + } + } + + if ((fPassedByABiasedVolume)){ + if ((step->GetTrack()->GetTrackID() == ftrackIDAtTheEntrance) && (step->GetPostStepPoint()->GetKineticEnergy() == fKineticEnergyAtTheEntrance)){ + if ((!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNamePostStep ) !=fNameOfBiasedLogicalVolume.end())) && (LogicalVolumeNamePostStep != fMotherVolumeName)){ + //std::cout<GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); + } + } } } } @@ -148,15 +200,24 @@ void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { void GateOptrComptPseudoTransportationActor::BeginOfEventAction( const G4Event *event) { fKillOthersParticles = false; + fPassedByABiasedVolume=false; + fEventID = event->GetEventID(); + fEventIDKineticEnergy = event->GetPrimaryVertex(0)->GetPrimary(0)->GetKineticEnergy(); + + } void GateOptrComptPseudoTransportationActor::StartTracking( const G4Track *track) { - - if (track->GetCreatorProcess() != 0) { - if ((track->GetCreatorProcess()->GetProcessName() == - "biasWrapper(compt)") && - (track->GetParticleDefinition()->GetParticleName() == "gamma")) { + G4String CreationProcessName = "None"; + + if (track->GetCreatorProcess() != 0){ + CreationProcessName = track->GetCreatorProcess()->GetProcessName(); + } + + if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ + G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ fInitialWeight = track->GetWeight(); fFreeFlightOperation->SetInitialWeight(fInitialWeight); } @@ -177,12 +238,11 @@ void GateOptrComptPseudoTransportationActor::StartTracking( G4VBiasingOperation * GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation( const G4Track *track, const G4BiasingProcessInterface *callingProcess) { - if (track->GetCreatorProcess() != 0) { - if ((track->GetCreatorProcess()->GetProcessName() == - "biasWrapper(compt)") && - (track->GetParticleDefinition()->GetParticleName() == "gamma")) { - fFreeFlightOperation->SetMinWeight(fInitialWeight / - fRelativeMinWeightOfParticle); + + if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ + G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ + fFreeFlightOperation->SetMinWeight(fInitialWeight/fRelativeMinWeightOfParticle); fFreeFlightOperation->SetTrackWeight(track->GetWeight()); fFreeFlightOperation->SetCountProcess(0); return fFreeFlightOperation; @@ -201,35 +261,45 @@ GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation( G4VBiasingOperation * GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( const G4Track *track, const G4BiasingProcessInterface *callingProcess) { - G4String callingProcessName = "biasWrapper(compt)"; + G4String particleName = track->GetParticleDefinition()->GetParticleName(); + + G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); + G4String CreationProcessName = ""; + if (track->GetCreatorProcess() != 0){ + CreationProcessName = track->GetCreatorProcess()->GetProcessName(); + } + - if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt") { - if (track->GetCreatorProcess() == 0) { - isSplitted = true; - return fComptSplittingOperation; + if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ + if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ + return callingProcess->GetCurrentOccurenceBiasingOperation(); } - if (track->GetCreatorProcess()->GetProcessName() != "biasWrapper(compt)") { + } + + + if (((CreationProcessName != "biasWrapper(conv)") && (CreationProcessName != "biasWrapper(compt)")) + && (callingProcess->GetWrappedProcess()->GetProcessName() == "eBrem")){ + return feBremSplittingOperation; + } + + + + if (!(std::find(fCreationProcessNameList.begin(), fCreationProcessNameList.end(),CreationProcessName) != fCreationProcessNameList.end())){ + if ((callingProcess->GetWrappedProcess()->GetProcessName() == "compt") || (callingProcess->GetWrappedProcess()->GetProcessName() == "Rayl")){ isSplitted = true; - return fComptSplittingOperation; + return fScatteredGammaSplittingOperation; } - } - /* - */ - if (track->GetCreatorProcess() != 0) { - if ((track->GetCreatorProcess()->GetProcessName() == - "biasWrapper(compt)") && - (track->GetParticleDefinition()->GetParticleName() == "gamma")) { - return callingProcess->GetCurrentOccurenceBiasingOperation(); + if ((callingProcess->GetWrappedProcess()->GetProcessName() == "conv")){ + return fPairProdSplittingOperation; } + } + return 0; } - return 0; - - // return 0; -} void GateOptrComptPseudoTransportationActor::EndTracking() { isSplitted = false; } + //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index 3b7d6b457..d3871d079 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -32,13 +32,15 @@ #include "G4EmCalculator.hh" #include "G4VBiasingOperator.hh" #include "GateOptnForceFreeFlight.h" +#include "GateOptneBremSplitting.h" +#include "GateOptnPairProdSplitting.h" #include "GateVActor.h" #include #include namespace py = pybind11; -class GateOptnComptSplittingForTransportation; +class GateOptnScatteredGammaSplitting; class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, public GateVActor { @@ -78,7 +80,18 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4bool fUseProbes = false; G4bool fSurvivedRR = false; G4bool fAttachToLogicalHolder = true; - + G4bool fKillPrimaries = true; + G4bool fPassedByABiasedVolume= false; + G4double fKineticEnergyAtTheEntrance; + G4int ftrackIDAtTheEntrance; + G4int fEventID; + G4double fEventIDKineticEnergy; + G4bool ftestbool= false; + const G4VProcess* fAnnihilation =nullptr; + + std::vector fNameOfBiasedLogicalVolume = {}; + std::vector v_EventID = {}; + std::vector fCreationProcessNameList = {"biasWrapper(compt)","biasWrapper(Rayl)", "biasWrapper(eBrem)","biasWrapper(annihil)"}; // Unused but mandatory virtual void StartSimulationAction(); @@ -114,8 +127,10 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, using G4VBiasingOperator::OperationApplied; private: - GateOptnForceFreeFlight *fFreeFlightOperation; - GateOptnComptSplittingForTransportation *fComptSplittingOperation; + GateOptnForceFreeFlight* fFreeFlightOperation; + GateOptnScatteredGammaSplitting* fScatteredGammaSplittingOperation; + GateOptneBremSplitting* feBremSplittingOperation; + GateOptnPairProdSplitting* fPairProdSplittingOperation; }; #endif diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index eb9970b36..f8e082ec3 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -475,7 +475,9 @@ def set_default_user_info(user_info): user_info.attach_to_logical_holder = True user_info.splitting_factor = 1 user_info.relative_min_weight_of_particle = np.inf - user_info.processes = ["compt", "phot", "conv", "Rayl"] + user_info.gamma_processes = ["compt", "phot", "conv", "Rayl"] + user_info.electron_processes = ["eBrem"] + user_info.positron_processes = ["annihil","eBrem"] user_info.russian_roulette_for_angle = False user_info.rotation_vector_director = False user_info.vector_director = [0, 0, 1] diff --git a/opengate/tests/data b/opengate/tests/data index d1c61ed4e..8613c9fa7 160000 --- a/opengate/tests/data +++ b/opengate/tests/data @@ -1 +1 @@ -Subproject commit d1c61ed4e7bade0067ed9fa20660d52abcc28744 +Subproject commit 8613c9fa705acd90018239b0cd8e53180e871ebc diff --git a/opengate/tests/src/test072_pseudo_transport_RR.py b/opengate/tests/src/test072_pseudo_transport_RR.py index e39832182..3ed0038e4 100644 --- a/opengate/tests/src/test072_pseudo_transport_RR.py +++ b/opengate/tests/src/test072_pseudo_transport_RR.py @@ -157,7 +157,7 @@ def validation_test(arr_no_RR, arr_RR, nb_event, splitting_factor, tol=0.15): pseudo_transportation_actor.russian_roulette_for_weights = False if j == 1: pseudo_transportation_actor.russian_roulette_for_weights = True - list_processes_to_bias = pseudo_transportation_actor.processes + list_processes_to_bias = pseudo_transportation_actor.gamma_processes ##### PHASE SPACE plan ######" plan = sim.add_volume("Box", "phsp") diff --git a/opengate/tests/src/test072_pseudo_transportation.py b/opengate/tests/src/test072_pseudo_transportation.py index ea981641f..64f8e822f 100644 --- a/opengate/tests/src/test072_pseudo_transportation.py +++ b/opengate/tests/src/test072_pseudo_transportation.py @@ -124,7 +124,7 @@ def validation_test(arr, NIST_data, nb_split, tol=0.01): pseudo_transportation_actor.mother = simple_collimation.name pseudo_transportation_actor.splitting_factor = nb_split pseudo_transportation_actor.relative_min_weight_of_particle = np.inf - list_processes_to_bias = pseudo_transportation_actor.processes + list_processes_to_bias = pseudo_transportation_actor.gamma_processes ##### PHASE SPACE plan ######" plan = sim.add_volume("Box", "phsp") From dd00658ae9f838ad7f15e26384ab7bb2bd603140 Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 19 Mar 2024 16:02:16 +0100 Subject: [PATCH 09/82] Development of an actor which kill a track if this one passes throught the actor volume without any interaction --- core/opengate_core/opengate_core.cpp | 3 + .../GateKillNonInteractingParticleActor.cpp | 65 ++++++ .../GateKillNonInteractingParticleActor.h | 43 ++++ .../pyGateKillNonInteractingParticleActor.cpp | 19 ++ opengate/actors/actorbuilders.py | 2 + opengate/actors/miscactors.py | 36 ++++ opengate/managers.py | 3 + .../test072_kill_non_interacting_particles.py | 201 ++++++++++++++++++ 8 files changed, 372 insertions(+) create mode 100644 core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h create mode 100644 core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp create mode 100644 opengate/tests/src/test072_kill_non_interacting_particles.py diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index 86e314219..6ef2a9603 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -259,6 +259,8 @@ void init_GateARFTrainingDatasetActor(py::module &m); void init_GateKillActor(py::module &); +void init_GateKillNonInteractingParticleActor(py::module &); + void init_itk_image(py::module &); void init_GateImageNestedParameterisation(py::module &); @@ -493,6 +495,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GateARFActor(m); init_GateARFTrainingDatasetActor(m); init_GateKillActor(m); + init_GateKillNonInteractingParticleActor(m); init_GateDigiAttributeManager(m); init_GateVDigiAttribute(m); init_GateExceptionHandler(m); diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp new file mode 100644 index 000000000..b71198e0e --- /dev/null +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp @@ -0,0 +1,65 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ------------------------------------ -------------- */ + +#include "GateKillNonInteractingParticleActor.h" +#include "G4ios.hh" +#include "GateHelpers.h" +#include "GateHelpersDict.h" +#include "G4PhysicalVolumeStore.hh" +#include "G4LogicalVolumeStore.hh" + + +GateKillNonInteractingParticleActor::GateKillNonInteractingParticleActor(py::dict &user_info) + : GateVActor(user_info, false) { + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("PreUserTrackingAction"); +} + + + + +void GateKillNonInteractingParticleActor::ActorInitialize() {} + +void GateKillNonInteractingParticleActor::StartSimulationAction() { + fNbOfKilledParticles = 0; +} + + +void GateKillNonInteractingParticleActor::PreUserTrackingAction(const G4Track* track) { + fIsFirstStep = true; + fKineticEnergyAtTheEntrance = 0; + ftrackIDAtTheEntrance = 0; + fPassedByTheMotherVolume = false; + +} + +void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { + + G4String logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if ((fPassedByTheMotherVolume) && (step->GetPostStepPoint()->GetStepStatus() == 1)){ + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(),logicalVolumeNamePostStep ) !=fListOfVolumeAncestor.end()){ + if ((step->GetTrack()->GetTrackID() == ftrackIDAtTheEntrance) && (step->GetPostStepPoint()->GetKineticEnergy() == fKineticEnergyAtTheEntrance)){ + auto track = step->GetTrack(); + track->SetTrackStatus(fStopAndKill); + fNbOfKilledParticles++; + } + } +} + + G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName)->GetName(); + G4String physicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); + if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logNameMotherVolume) && (fIsFirstStep)){ + if ((fPassedByTheMotherVolume == false) && (physicalVolumeNamePreStep == fMotherVolumeName) && (step->GetPreStepPoint()->GetStepStatus() == 1)){ + fPassedByTheMotherVolume =true; + fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); + ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); + } + } + + fIsFirstStep = false; +} diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h new file mode 100644 index 000000000..a7cf58ccb --- /dev/null +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h @@ -0,0 +1,43 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#ifndef GateKillNonInteractingParticleActor_h +#define GateKillNonInteractingParticleActor_h + +#include "G4Cache.hh" +#include "GateVActor.h" +#include + +namespace py = pybind11; + +class GateKillNonInteractingParticleActor : public GateVActor { + +public: + // Constructor + GateKillNonInteractingParticleActor(py::dict &user_info); + + void ActorInitialize() override; + + void StartSimulationAction() override; + + // Main function called every step in attached volume + void SteppingAction(G4Step *) override; + + void PreUserTrackingAction(const G4Track*) override; + + std::vector fParticlesTypeToKill; + G4bool fPassedByTheMotherVolume= false; + G4double fKineticEnergyAtTheEntrance = 0; + G4int ftrackIDAtTheEntrance = 0; + G4bool fIsFirstStep = true; + std::vector fListOfVolumeAncestor; + + + long fNbOfKilledParticles{}; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp new file mode 100644 index 000000000..6b5cee6a2 --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp @@ -0,0 +1,19 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include + +namespace py = pybind11; + +#include "GateKillNonInteractingParticleActor.h" + +void init_GateKillNonInteractingParticleActor(py::module &m) { + py::class_, + GateVActor>(m, "GateKillNonInteractingParticleActor") + .def_readwrite("fListOfVolumeAncestor", &GateKillNonInteractingParticleActor::fListOfVolumeAncestor) + .def(py::init()); +} diff --git a/opengate/actors/actorbuilders.py b/opengate/actors/actorbuilders.py index 1756e64c1..122fea524 100644 --- a/opengate/actors/actorbuilders.py +++ b/opengate/actors/actorbuilders.py @@ -17,6 +17,7 @@ SourceInfoActor, TestActor, KillActor, + KillNonInteractingParticleActor, ) from .dynamicactors import DynamicGeometryActor from ..utility import make_builders @@ -42,6 +43,7 @@ ARFTrainingDatasetActor, TestActor, KillActor, + KillNonInteractingParticleActor, DynamicGeometryActor, } actor_builders = make_builders(actor_type_names) diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 5b12f1566..ec1316ccc 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -5,6 +5,7 @@ import numpy as np import time import platform +from anytree import Node, RenderTree import opengate_core as g4 from .base import ActorBase from ..exception import fatal @@ -427,3 +428,38 @@ def set_default_user_info(user_info): def __init__(self, user_info): ActorBase.__init__(self, user_info) g4.GateKillActor.__init__(self, user_info.__dict__) + + +class KillNonInteractingParticleActor(g4.GateKillNonInteractingParticleActor, ActorBase): + type_name = "KillNonInteractingParticleActor" + + def set_default_user_info(user_info): + ActorBase.set_default_user_info(user_info) + user_info.list_of_volume_name = [] + + + def __init__(self, user_info): + ActorBase.__init__(self, user_info) + g4.GateKillNonInteractingParticleActor.__init__(self, user_info.__dict__) + self.list_of_volume_name = user_info.list_of_volume_name + self.user_info.mother = user_info.mother + + + def initialize(self, volume_engine=None): + + super().initialize(volume_engine) + volume_tree = self.simulation.volume_manager.get_volume_tree() + dico_of_volume_tree = {} + for pre, _, node in RenderTree(volume_tree): + dico_of_volume_tree[str(node.name)] = node + volume_name = self.user_info.mother + while volume_name != 'world': + node = dico_of_volume_tree[volume_name] + volume_name = node.mother + self.list_of_volume_name.append(volume_name) + self.fListOfVolumeAncestor = self.list_of_volume_name + + + + + diff --git a/opengate/managers.py b/opengate/managers.py index 95d364fd4..332a8d488 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -887,6 +887,9 @@ def dump_volume_types(self): def dump_material_database_names(self): return list(self.material_database.filenames) + + def get_volume_tree(self): + return (self.volume_tree_root) def setter_hook_verbose_level(self, verbose_level): diff --git a/opengate/tests/src/test072_kill_non_interacting_particles.py b/opengate/tests/src/test072_kill_non_interacting_particles.py new file mode 100644 index 000000000..9e3d55729 --- /dev/null +++ b/opengate/tests/src/test072_kill_non_interacting_particles.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +from opengate.tests import utility +from scipy.spatial.transform import Rotation +import numpy as np +from anytree import Node,RenderTree +import uproot + + + + + + +def test072_test(entry_data, exit_data_1,exit_data_2): + liste_ekin =[] + liste_evtID = [] + liste_trackID = [] + evt_ID_entry_data = entry_data["EventID"] + j = 0 + i = 0 + while i < len(evt_ID_entry_data): + if j < len(exit_data_1["EventID"]) and evt_ID_entry_data[i] == exit_data_1["EventID"][j]: + TID_entry = entry_data["TrackID"][i] + TID_exit = exit_data_1["TrackID"][j] + Ekin_entry = entry_data["KineticEnergy"][i] + Ekin_exit = exit_data_1["KineticEnergy"][j] + + if (TID_entry == TID_exit) and (Ekin_exit == Ekin_entry): + liste_ekin.append(exit_data_1["KineticEnergy"][j]) + liste_evtID.append(exit_data_1["EventID"][j]) + liste_trackID.append(exit_data_1["TrackID"][j]) + if (j < len(exit_data_1["EventID"]) -1) and (exit_data_1["EventID"][j] == exit_data_1["EventID"][j+1]) : + i = i-1 + j+=1 + i+=1 + liste_ekin =np.asarray(liste_ekin) + print("Number of tracks to kill =", len(liste_ekin)) + print("Number of killed tracks =",(len(exit_data_1["EventID"]) - len(exit_data_2["EventID"]))) + + + return len(liste_ekin) == (len(exit_data_1["EventID"]) - len(exit_data_2["EventID"])) + + + + + + + + + +if __name__ == "__main__": + paths = utility.get_default_test_paths(__file__) + output_path = paths.output + + print(output_path) + # create the simulation + sim = gate.Simulation() + + # main options + ui = sim.user_info + ui.g4_verbose = False + # ui.visu = True + ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.logger.EVENT + ui.number_of_threads = 1 + ui.random_seed = "auto" + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + sec = gate.g4_units.s + gcm3 = gate.g4_units["g/cm3"] + + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + + # adapt world size + world = sim.world + world.size = [1 * m, 1 * m, 1 * m] + world.material = "G4_AIR" + + big_box = sim.add_volume("Box","big_box") + big_box.mother = world.name + big_box.material = 'G4_AIR' + big_box.size = [0.8*m,0.8*m,0.8*m] + + actor_box = sim.add_volume("Box", "actor_box") + actor_box.mother = big_box.name + actor_box.material = 'G4_AIR' + actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] + actor_box.translation = [0,0,-0.1*m] + + + + + source = sim.add_source("GenericSource", "photon_source") + source.particle = "gamma" + source.position.type = "box" + source.mother = world.name + source.position.size = [6 * cm, 6 * cm, 6 * cm] + source.position.translation = [0,0,0.3*m] + source.direction.type = "momentum" + source.force_rotation = True + # source1.direction.focus_point = [0*cm, 0*cm, -5 *cm] + source.direction.momentum = [0, 0, -1] + source.energy.type = "mono" + source.energy.mono = 6 * MeV + source.n= 1000 + + + + tungsten_leaves =sim.add_volume("Box","tungsten_leaves") + tungsten_leaves.mother = actor_box + tungsten_leaves.size = [0.6*m,0.6*m,0.3*cm] + tungsten_leaves.material = "Tungsten" + liste_translation_W = [] + for i in range(7) : + liste_translation_W.append([0,0,0.25*m - i*6*cm]) + tungsten_leaves.translation = liste_translation_W + tungsten_leaves.color = [0.9,0.,0.4,0.8] + + + kill_No_int_act = sim.add_actor("KillNonInteractingParticleActor","killact") + kill_No_int_act.mother = actor_box.name + + + entry_phase_space = sim.add_volume("Box","entry_phase_space") + entry_phase_space.mother = big_box + entry_phase_space.size = [0.8*m,0.8*m,1*nm] + entry_phase_space.material = "G4_AIR" + entry_phase_space.translation = [0,0,0.21*m] + entry_phase_space.color = [0.5,0.9,0.3,1] + + exit_phase_space_1 = sim.add_volume("Box","exit_phase_space_1") + exit_phase_space_1.mother = actor_box + exit_phase_space_1.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space_1.material = "G4_AIR" + exit_phase_space_1.translation = [0, 0, -0.3 * m + 1*nm] + exit_phase_space_1.color = [0.5, 0.9, 0.3, 1] + + exit_phase_space_2 = sim.add_volume("Box", "exit_phase_space_2") + exit_phase_space_2.mother = world.name + exit_phase_space_2.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space_2.material = "G4_AIR" + exit_phase_space_2.translation = [0, 0, -0.4 * m - 1 * nm] + exit_phase_space_2.color = [0.5, 0.9, 0.3, 1] + + # print(sim.volume_manager.dump_volume_tree()) + liste_phase_space_name = [entry_phase_space.name,exit_phase_space_1.name,exit_phase_space_2.name] + for name in liste_phase_space_name: + + phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_"+name) + phsp.mother = name + phsp.attributes = ["EventID","TrackID","KineticEnergy"] + name_phsp = "test072_" +name+".root" + phsp.output = output_path / name_phsp + + + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + sim.physics_manager.enable_decay = False + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * mm + sim.physics_manager.global_production_cuts.positron = 1 * mm + + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + + + # go ! + sim.run() + output = sim.output + stats = sim.output.get_actor("Stats") + print(stats) + + + entry_phsp = uproot.open(str(output_path) + "/test072_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) + exit_phase_space_1 = uproot.open(str(output_path) + "/test072_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) + exit_phase_space_2 = uproot.open( str(output_path) + "/test072_" + liste_phase_space_name[2] + ".root" + ":PhaseSpace_" + liste_phase_space_name[2]) + + + df_entry = entry_phsp.arrays() + df_exit_1 = exit_phase_space_1.arrays() + df_exit_2 = exit_phase_space_2.arrays() + + is_ok = test072_test(df_entry, df_exit_1, df_exit_2) + + utility.test_ok(is_ok) \ No newline at end of file From 1045e37182a0763a9c6a75bf1f49bfd9a0e71f85 Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 20 Mar 2024 15:50:34 +0100 Subject: [PATCH 10/82] Development of an actor which split particles at the entrance and/or at the exit of the volume --- core/opengate_core/opengate_core.cpp | 3 + .../GateSurfaceSplittingActor.cpp | 86 ++++++++++ .../opengate_lib/GateSurfaceSplittingActor.h | 45 +++++ .../pyGateSurfaceSplittingActor.cpp | 19 +++ opengate/actors/actorbuilders.py | 2 + opengate/actors/miscactors.py | 33 ++++ opengate/managers.py | 3 + ...73_geometrical_splitting_volume_surface.py | 161 ++++++++++++++++++ 8 files changed, 352 insertions(+) create mode 100644 core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h create mode 100644 core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp create mode 100644 opengate/tests/src/test073_geometrical_splitting_volume_surface.py diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index 86e314219..74506b5e4 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -259,6 +259,8 @@ void init_GateARFTrainingDatasetActor(py::module &m); void init_GateKillActor(py::module &); +void init_GateSurfaceSplittingActor(py::module &); + void init_itk_image(py::module &); void init_GateImageNestedParameterisation(py::module &); @@ -493,6 +495,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GateARFActor(m); init_GateARFTrainingDatasetActor(m); init_GateKillActor(m); + init_GateSurfaceSplittingActor(m); init_GateDigiAttributeManager(m); init_GateVDigiAttribute(m); init_GateExceptionHandler(m); diff --git a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp new file mode 100644 index 000000000..53b88c804 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp @@ -0,0 +1,86 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ------------------------------------ -------------- */ + +#include "GateSurfaceSplittingActor.h" +#include "G4ios.hh" +#include "GateHelpers.h" +#include "GateHelpersDict.h" +#include "G4LogicalVolumeStore.hh" + +GateSurfaceSplittingActor::GateSurfaceSplittingActor(py::dict &user_info) + : GateVActor(user_info, false) { + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("PreUserTrackingAction"); + fMotherVolumeName = DictGetStr(user_info,"mother"); + fWeightThreshold = DictGetBool(user_info,"weight_threshold"); + fSplittingFactor = DictGetInt(user_info,"splitting_factor"); + fSplitEnteringParticles = DictGetBool(user_info,"split_entering_particles"); + fSplitExitingParticles = DictGetBool(user_info,"split_exiting_particles"); +} + +void GateSurfaceSplittingActor::ActorInitialize() {} + +void GateSurfaceSplittingActor::StartSimulationAction() { fNbOfKilledParticles = 0; } + +void GateSurfaceSplittingActor::PreUserTrackingAction(const G4Track* track){ + + fIsFirstStep = true; +} + +void GateSurfaceSplittingActor::SteppingAction(G4Step *step) { + auto track = step->GetTrack(); + auto weight = track->GetWeight(); + + + if (weight >= fWeightThreshold){ + if (fSplitEnteringParticles){ + G4String logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (((fIsFirstStep) && (step->GetPreStepPoint()->GetStepStatus() ==1) && (logicalVolumeNamePreStep == fMotherVolumeName)) + || ((fIsFirstStep) && (track->GetLogicalVolumeAtVertex()->GetName() != logicalVolumeNamePreStep) && (track->GetLogicalVolumeAtVertex()->GetName() != fMotherVolumeName))) { + G4ThreeVector position = step->GetPreStepPoint()->GetPosition(); + G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentum(); + G4double ekin = step->GetPreStepPoint()->GetKineticEnergy(); + + const G4DynamicParticle* particleType = track->GetDynamicParticle(); + G4double time = step->GetPreStepPoint()->GetGlobalTime(); + G4TrackVector *trackVector = step->GetfSecondary(); + + for (int i = 0; i < fSplittingFactor -1 ; i++) { + G4DynamicParticle* particleTypeToAdd = new G4DynamicParticle(*particleType); + G4Track* clone = new G4Track(particleTypeToAdd ,time,position); + clone->SetKineticEnergy(ekin); + clone->SetMomentumDirection(momentum); + clone->SetWeight( weight/fSplittingFactor); + trackVector->push_back(clone); + } + step->GetPreStepPoint()->SetWeight(weight/fSplittingFactor); + step->GetPostStepPoint()->SetWeight(weight/fSplittingFactor); + track->SetWeight(weight/fSplittingFactor); + + + } + } + + + if(fSplitExitingParticles){ + G4String logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(),logicalVolumeNamePostStep) !=fListOfVolumeAncestor.end()){ + G4TrackVector *trackVector = step->GetfSecondary(); + for (int i = 0; i < fSplittingFactor-1; i++) { + G4Track* clone = new G4Track(*track); + clone->SetWeight( weight/fSplittingFactor); + trackVector->push_back(clone); + } + step->GetPostStepPoint()->SetWeight(weight/fSplittingFactor); + track->SetWeight(weight/fSplittingFactor); + } + } + + } + fIsFirstStep =false; +} diff --git a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h new file mode 100644 index 000000000..871342f7c --- /dev/null +++ b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h @@ -0,0 +1,45 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#ifndef GateSurfaceSplittingActor_h +#define GateSurfaceSplittingActor_h + +#include "G4Cache.hh" +#include "GateVActor.h" +#include + +namespace py = pybind11; + +class GateSurfaceSplittingActor : public GateVActor { + +public: + // Constructor + GateSurfaceSplittingActor(py::dict &user_info); + + void ActorInitialize() override; + + void StartSimulationAction() override; + + // Main function called every step in attached volume + void SteppingAction(G4Step *) override; + + void PreUserTrackingAction(const G4Track *) override; + + + G4bool fSplitEnteringParticles =false; + G4bool fSplitExitingParticles =false ; + G4int fSplittingFactor; + G4bool fIsFirstStep; + G4bool fWeightThreshold; + G4String fMotherVolumeName; + std::vector fListOfVolumeAncestor; + + + long fNbOfKilledParticles{}; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp b/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp new file mode 100644 index 000000000..984e7a937 --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp @@ -0,0 +1,19 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include + +namespace py = pybind11; + +#include "GateSurfaceSplittingActor.h" + +void init_GateSurfaceSplittingActor(py::module &m) { + py::class_,GateVActor>(m, "GateSurfaceSplittingActor") + .def(py::init()) + .def_readwrite("fListOfVolumeAncestor",&GateSurfaceSplittingActor::fListOfVolumeAncestor) + .def(py::init()); +} diff --git a/opengate/actors/actorbuilders.py b/opengate/actors/actorbuilders.py index 1756e64c1..1ffc79547 100644 --- a/opengate/actors/actorbuilders.py +++ b/opengate/actors/actorbuilders.py @@ -17,6 +17,7 @@ SourceInfoActor, TestActor, KillActor, + SurfaceSplittingActor, ) from .dynamicactors import DynamicGeometryActor from ..utility import make_builders @@ -42,6 +43,7 @@ ARFTrainingDatasetActor, TestActor, KillActor, + SurfaceSplittingActor, DynamicGeometryActor, } actor_builders = make_builders(actor_type_names) diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 5b12f1566..48f148fde 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -5,6 +5,7 @@ import numpy as np import time import platform +from anytree import Node, RenderTree import opengate_core as g4 from .base import ActorBase from ..exception import fatal @@ -427,3 +428,35 @@ def set_default_user_info(user_info): def __init__(self, user_info): ActorBase.__init__(self, user_info) g4.GateKillActor.__init__(self, user_info.__dict__) + +class SurfaceSplittingActor(g4.GateSurfaceSplittingActor, ActorBase): + type_name = "SurfaceSplittingActor" + + def set_default_user_info(user_info): + ActorBase.set_default_user_info(user_info) + user_info.list_of_volume_name = [] + user_info.splitting_factor = 1 + user_info.split_entering_particles = False + user_info.split_exiting_particles = False + user_info.weight_threshold = 0 + + def __init__(self, user_info): + ActorBase.__init__(self, user_info) + g4.GateSurfaceSplittingActor.__init__(self, user_info.__dict__) + self.list_of_volume_name = user_info.list_of_volume_name + self.user_info.mother = user_info.mother + + + def initialize(self, volume_engine=None): + + super().initialize(volume_engine) + volume_tree = self.simulation.volume_manager.get_volume_tree() + dico_of_volume_tree = {} + for pre, _, node in RenderTree(volume_tree): + dico_of_volume_tree[str(node.name)] = node + volume_name = self.user_info.mother + while volume_name != 'world': + node = dico_of_volume_tree[volume_name] + volume_name = node.mother + self.list_of_volume_name.append(volume_name) + self.fListOfVolumeAncestor = self.list_of_volume_name diff --git a/opengate/managers.py b/opengate/managers.py index 95d364fd4..332a8d488 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -887,6 +887,9 @@ def dump_volume_types(self): def dump_material_database_names(self): return list(self.material_database.filenames) + + def get_volume_tree(self): + return (self.volume_tree_root) def setter_hook_verbose_level(self, verbose_level): diff --git a/opengate/tests/src/test073_geometrical_splitting_volume_surface.py b/opengate/tests/src/test073_geometrical_splitting_volume_surface.py new file mode 100644 index 000000000..f3b2d8278 --- /dev/null +++ b/opengate/tests/src/test073_geometrical_splitting_volume_surface.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +from opengate.tests import utility +from scipy.spatial.transform import Rotation +import numpy as np +from anytree import Node,RenderTree +import uproot + + +def test073(entry_data, exit_data, splitting_factor): + splitted_particle_data_entry = entry_data[entry_data["TrackCreatorProcess"] == "none"] + splitted_particle_data_exit = exit_data[exit_data["TrackCreatorProcess"] == "none"] + + array_weight_1 = splitted_particle_data_entry["Weight"] + array_weight_2 = splitted_particle_data_exit["Weight"] + + + if (np.round(np.sum(array_weight_1),3) == 1) and len(array_weight_1) == splitting_factor: + if (np.round(np.sum(array_weight_2),3) == 1) and len(array_weight_2) == splitting_factor: + return True + else : return False + else : return False + + +if __name__ == "__main__": + paths = utility.get_default_test_paths(__file__) + output_path = paths.output + + print(output_path) + # create the simulation + sim = gate.Simulation() + + # main options + ui = sim.user_info + ui.g4_verbose = False + ui.visu = True + ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.logger.EVENT + ui.number_of_threads = 1 + ui.random_seed = "auto" + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + sec = gate.g4_units.s + gcm3 = gate.g4_units["g/cm3"] + + + # adapt world size + world = sim.world + world.size = [1 * m, 1 * m, 1 * m] + world.material = "G4_Galactic" + + big_box = sim.add_volume("Box","big_box") + big_box.mother = world.name + big_box.material = "G4_Galactic" + big_box.size = [0.8*m,0.8*m,0.8*m] + + actor_box = sim.add_volume("Box", "actor_box") + actor_box.mother = big_box.name + actor_box.material = "G4_Galactic" + actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] + actor_box.translation = [0,0,-0.1*m] + + + + + source_1 = sim.add_source("GenericSource", "elec_source_1") + source_1.particle = "e-" + source_1.position.type = "box" + source_1.mother = big_box.name + source_1.position.size = [1*cm,1*cm,1*cm] + source_1.position.translation = [0,0.35*m,0] + source_1.direction.type = "momentum" + source_1.direction.momentum = [0,-1,0] + source_1.energy.type = "mono" + source_1.energy.mono = 10 * MeV + source_1.n= 1 + + source_2 = sim.add_source("GenericSource", "elec_source_2") + source_2.particle = "e-" + source_2.position.type = "box" + source_2.mother = big_box.name + source_2.position.size = [1 * cm, 1 * cm, 1 * cm] + source_2.position.translation = [0, 0, -0.39 * m] + source_2.direction.type = "momentum" + source_2.direction.momentum = [0, 0, -1] + source_2.energy.type = "mono" + source_2.energy.mono = 10 * MeV + source_2.n = 1 + + + geom_splitting = sim.add_actor("SurfaceSplittingActor","splitting_act") + geom_splitting.mother = actor_box.name + geom_splitting.splitting_factor = 10 + geom_splitting.weight_threshold = 1 + geom_splitting.split_entering_particles = True + geom_splitting.split_exiting_particles = True + + + entry_phase_space = sim.add_volume("Box","entry_phase_space") + entry_phase_space.mother = actor_box + entry_phase_space.size = [0.6*m,1*nm,0.6*m] + entry_phase_space.material = "G4_Galactic" + entry_phase_space.translation = [0,0.3*m - 0.5*nm,0] + entry_phase_space.color = [0.5,0.9,0.3,1] + + exit_phase_space = sim.add_volume("Box", "exit_phase_space") + exit_phase_space.mother = world.name + exit_phase_space.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space.material = "G4_Galactic" + exit_phase_space.translation = [0, 0, -0.4 * m - 1 * nm] + exit_phase_space.color = [0.5, 0.9, 0.3, 1] + + # # print(sim.volume_manager.dump_volume_tree()) + liste_phase_space_name = [entry_phase_space.name,exit_phase_space.name,] + for name in liste_phase_space_name: + + phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_"+name) + phsp.mother = name + phsp.attributes = ["EventID","TrackID","Weight","PDGCode","TrackCreatorProcess"] + name_phsp = "test073_" +name+".root" + phsp.output = output_path / name_phsp + + + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + sim.physics_manager.enable_decay = False + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * mm + sim.physics_manager.global_production_cuts.positron = 1 * mm + + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + + + # go ! + sim.run() + output = sim.output + stats = sim.output.get_actor("Stats") + print(stats) + + # + entry_phsp = uproot.open(str(output_path) + "/test073_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) + exit_phase_space = uproot.open(str(output_path) + "/test073_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) + # + df_entry = entry_phsp.arrays() + df_exit = exit_phase_space.arrays() + # + is_ok = test073(df_entry, df_exit,geom_splitting.splitting_factor) + # + utility.test_ok(is_ok) \ No newline at end of file From 71e276da8607bc5eb47b8d345fb5da91f1f8dda7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:00:33 +0000 Subject: [PATCH 11/82] [pre-commit.ci] Automatic python and c++ formatting --- .../GateSurfaceSplittingActor.cpp | 84 +++++++++-------- .../opengate_lib/GateSurfaceSplittingActor.h | 6 +- .../pyGateSurfaceSplittingActor.cpp | 7 +- opengate/actors/miscactors.py | 8 +- opengate/managers.py | 4 +- ...73_geometrical_splitting_volume_surface.py | 93 ++++++++++++------- 6 files changed, 117 insertions(+), 85 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp index 53b88c804..1afbbf25d 100644 --- a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp @@ -6,28 +6,30 @@ ------------------------------------ -------------- */ #include "GateSurfaceSplittingActor.h" +#include "G4LogicalVolumeStore.hh" #include "G4ios.hh" #include "GateHelpers.h" #include "GateHelpersDict.h" -#include "G4LogicalVolumeStore.hh" GateSurfaceSplittingActor::GateSurfaceSplittingActor(py::dict &user_info) : GateVActor(user_info, false) { fActions.insert("StartSimulationAction"); fActions.insert("SteppingAction"); fActions.insert("PreUserTrackingAction"); - fMotherVolumeName = DictGetStr(user_info,"mother"); - fWeightThreshold = DictGetBool(user_info,"weight_threshold"); - fSplittingFactor = DictGetInt(user_info,"splitting_factor"); - fSplitEnteringParticles = DictGetBool(user_info,"split_entering_particles"); - fSplitExitingParticles = DictGetBool(user_info,"split_exiting_particles"); + fMotherVolumeName = DictGetStr(user_info, "mother"); + fWeightThreshold = DictGetBool(user_info, "weight_threshold"); + fSplittingFactor = DictGetInt(user_info, "splitting_factor"); + fSplitEnteringParticles = DictGetBool(user_info, "split_entering_particles"); + fSplitExitingParticles = DictGetBool(user_info, "split_exiting_particles"); } void GateSurfaceSplittingActor::ActorInitialize() {} -void GateSurfaceSplittingActor::StartSimulationAction() { fNbOfKilledParticles = 0; } +void GateSurfaceSplittingActor::StartSimulationAction() { + fNbOfKilledParticles = 0; +} -void GateSurfaceSplittingActor::PreUserTrackingAction(const G4Track* track){ +void GateSurfaceSplittingActor::PreUserTrackingAction(const G4Track *track) { fIsFirstStep = true; } @@ -36,51 +38,59 @@ void GateSurfaceSplittingActor::SteppingAction(G4Step *step) { auto track = step->GetTrack(); auto weight = track->GetWeight(); - - if (weight >= fWeightThreshold){ - if (fSplitEnteringParticles){ - G4String logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if (((fIsFirstStep) && (step->GetPreStepPoint()->GetStepStatus() ==1) && (logicalVolumeNamePreStep == fMotherVolumeName)) - || ((fIsFirstStep) && (track->GetLogicalVolumeAtVertex()->GetName() != logicalVolumeNamePreStep) && (track->GetLogicalVolumeAtVertex()->GetName() != fMotherVolumeName))) { + if (weight >= fWeightThreshold) { + if (fSplitEnteringParticles) { + G4String logicalVolumeNamePreStep = step->GetPreStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + if (((fIsFirstStep) && (step->GetPreStepPoint()->GetStepStatus() == 1) && + (logicalVolumeNamePreStep == fMotherVolumeName)) || + ((fIsFirstStep) && + (track->GetLogicalVolumeAtVertex()->GetName() != + logicalVolumeNamePreStep) && + (track->GetLogicalVolumeAtVertex()->GetName() != + fMotherVolumeName))) { G4ThreeVector position = step->GetPreStepPoint()->GetPosition(); G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentum(); G4double ekin = step->GetPreStepPoint()->GetKineticEnergy(); - - const G4DynamicParticle* particleType = track->GetDynamicParticle(); + + const G4DynamicParticle *particleType = track->GetDynamicParticle(); G4double time = step->GetPreStepPoint()->GetGlobalTime(); G4TrackVector *trackVector = step->GetfSecondary(); - - for (int i = 0; i < fSplittingFactor -1 ; i++) { - G4DynamicParticle* particleTypeToAdd = new G4DynamicParticle(*particleType); - G4Track* clone = new G4Track(particleTypeToAdd ,time,position); + + for (int i = 0; i < fSplittingFactor - 1; i++) { + G4DynamicParticle *particleTypeToAdd = + new G4DynamicParticle(*particleType); + G4Track *clone = new G4Track(particleTypeToAdd, time, position); clone->SetKineticEnergy(ekin); clone->SetMomentumDirection(momentum); - clone->SetWeight( weight/fSplittingFactor); + clone->SetWeight(weight / fSplittingFactor); trackVector->push_back(clone); } - step->GetPreStepPoint()->SetWeight(weight/fSplittingFactor); - step->GetPostStepPoint()->SetWeight(weight/fSplittingFactor); - track->SetWeight(weight/fSplittingFactor); - - + step->GetPreStepPoint()->SetWeight(weight / fSplittingFactor); + step->GetPostStepPoint()->SetWeight(weight / fSplittingFactor); + track->SetWeight(weight / fSplittingFactor); } } - - if(fSplitExitingParticles){ - G4String logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(),logicalVolumeNamePostStep) !=fListOfVolumeAncestor.end()){ + if (fSplitExitingParticles) { + G4String logicalVolumeNamePostStep = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), + logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { G4TrackVector *trackVector = step->GetfSecondary(); - for (int i = 0; i < fSplittingFactor-1; i++) { - G4Track* clone = new G4Track(*track); - clone->SetWeight( weight/fSplittingFactor); + for (int i = 0; i < fSplittingFactor - 1; i++) { + G4Track *clone = new G4Track(*track); + clone->SetWeight(weight / fSplittingFactor); trackVector->push_back(clone); } - step->GetPostStepPoint()->SetWeight(weight/fSplittingFactor); - track->SetWeight(weight/fSplittingFactor); + step->GetPostStepPoint()->SetWeight(weight / fSplittingFactor); + track->SetWeight(weight / fSplittingFactor); } } - } - fIsFirstStep =false; + fIsFirstStep = false; } diff --git a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h index 871342f7c..6adce44d1 100644 --- a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h @@ -29,16 +29,14 @@ class GateSurfaceSplittingActor : public GateVActor { void PreUserTrackingAction(const G4Track *) override; - - G4bool fSplitEnteringParticles =false; - G4bool fSplitExitingParticles =false ; + G4bool fSplitEnteringParticles = false; + G4bool fSplitExitingParticles = false; G4int fSplittingFactor; G4bool fIsFirstStep; G4bool fWeightThreshold; G4String fMotherVolumeName; std::vector fListOfVolumeAncestor; - long fNbOfKilledParticles{}; }; diff --git a/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp b/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp index 984e7a937..0d6fc94e9 100644 --- a/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp @@ -12,8 +12,11 @@ namespace py = pybind11; #include "GateSurfaceSplittingActor.h" void init_GateSurfaceSplittingActor(py::module &m) { - py::class_,GateVActor>(m, "GateSurfaceSplittingActor") + py::class_, + GateVActor>(m, "GateSurfaceSplittingActor") .def(py::init()) - .def_readwrite("fListOfVolumeAncestor",&GateSurfaceSplittingActor::fListOfVolumeAncestor) + .def_readwrite("fListOfVolumeAncestor", + &GateSurfaceSplittingActor::fListOfVolumeAncestor) .def(py::init()); } diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 48f148fde..54ef398f8 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -428,7 +428,8 @@ def set_default_user_info(user_info): def __init__(self, user_info): ActorBase.__init__(self, user_info) g4.GateKillActor.__init__(self, user_info.__dict__) - + + class SurfaceSplittingActor(g4.GateSurfaceSplittingActor, ActorBase): type_name = "SurfaceSplittingActor" @@ -445,8 +446,7 @@ def __init__(self, user_info): g4.GateSurfaceSplittingActor.__init__(self, user_info.__dict__) self.list_of_volume_name = user_info.list_of_volume_name self.user_info.mother = user_info.mother - - + def initialize(self, volume_engine=None): super().initialize(volume_engine) @@ -455,7 +455,7 @@ def initialize(self, volume_engine=None): for pre, _, node in RenderTree(volume_tree): dico_of_volume_tree[str(node.name)] = node volume_name = self.user_info.mother - while volume_name != 'world': + while volume_name != "world": node = dico_of_volume_tree[volume_name] volume_name = node.mother self.list_of_volume_name.append(volume_name) diff --git a/opengate/managers.py b/opengate/managers.py index bf3fca344..910e3be10 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -889,9 +889,9 @@ def dump_volume_types(self): def dump_material_database_names(self): return list(self.material_database.filenames) - + def get_volume_tree(self): - return (self.volume_tree_root) + return self.volume_tree_root def setter_hook_verbose_level(self, verbose_level): diff --git a/opengate/tests/src/test073_geometrical_splitting_volume_surface.py b/opengate/tests/src/test073_geometrical_splitting_volume_surface.py index f3b2d8278..30ea10d67 100644 --- a/opengate/tests/src/test073_geometrical_splitting_volume_surface.py +++ b/opengate/tests/src/test073_geometrical_splitting_volume_surface.py @@ -5,23 +5,30 @@ from opengate.tests import utility from scipy.spatial.transform import Rotation import numpy as np -from anytree import Node,RenderTree +from anytree import Node, RenderTree import uproot def test073(entry_data, exit_data, splitting_factor): - splitted_particle_data_entry = entry_data[entry_data["TrackCreatorProcess"] == "none"] + splitted_particle_data_entry = entry_data[ + entry_data["TrackCreatorProcess"] == "none" + ] splitted_particle_data_exit = exit_data[exit_data["TrackCreatorProcess"] == "none"] array_weight_1 = splitted_particle_data_entry["Weight"] array_weight_2 = splitted_particle_data_exit["Weight"] - - if (np.round(np.sum(array_weight_1),3) == 1) and len(array_weight_1) == splitting_factor: - if (np.round(np.sum(array_weight_2),3) == 1) and len(array_weight_2) == splitting_factor: + if (np.round(np.sum(array_weight_1), 3) == 1) and len( + array_weight_1 + ) == splitting_factor: + if (np.round(np.sum(array_weight_2), 3) == 1) and len( + array_weight_2 + ) == splitting_factor: return True - else : return False - else : return False + else: + return False + else: + return False if __name__ == "__main__": @@ -54,37 +61,33 @@ def test073(entry_data, exit_data, splitting_factor): sec = gate.g4_units.s gcm3 = gate.g4_units["g/cm3"] - # adapt world size world = sim.world world.size = [1 * m, 1 * m, 1 * m] world.material = "G4_Galactic" - big_box = sim.add_volume("Box","big_box") + big_box = sim.add_volume("Box", "big_box") big_box.mother = world.name big_box.material = "G4_Galactic" - big_box.size = [0.8*m,0.8*m,0.8*m] + big_box.size = [0.8 * m, 0.8 * m, 0.8 * m] actor_box = sim.add_volume("Box", "actor_box") actor_box.mother = big_box.name actor_box.material = "G4_Galactic" actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] - actor_box.translation = [0,0,-0.1*m] - - - + actor_box.translation = [0, 0, -0.1 * m] source_1 = sim.add_source("GenericSource", "elec_source_1") source_1.particle = "e-" source_1.position.type = "box" source_1.mother = big_box.name - source_1.position.size = [1*cm,1*cm,1*cm] - source_1.position.translation = [0,0.35*m,0] + source_1.position.size = [1 * cm, 1 * cm, 1 * cm] + source_1.position.translation = [0, 0.35 * m, 0] source_1.direction.type = "momentum" - source_1.direction.momentum = [0,-1,0] + source_1.direction.momentum = [0, -1, 0] source_1.energy.type = "mono" source_1.energy.mono = 10 * MeV - source_1.n= 1 + source_1.n = 1 source_2 = sim.add_source("GenericSource", "elec_source_2") source_2.particle = "e-" @@ -98,21 +101,19 @@ def test073(entry_data, exit_data, splitting_factor): source_2.energy.mono = 10 * MeV source_2.n = 1 - - geom_splitting = sim.add_actor("SurfaceSplittingActor","splitting_act") + geom_splitting = sim.add_actor("SurfaceSplittingActor", "splitting_act") geom_splitting.mother = actor_box.name geom_splitting.splitting_factor = 10 geom_splitting.weight_threshold = 1 geom_splitting.split_entering_particles = True geom_splitting.split_exiting_particles = True - - entry_phase_space = sim.add_volume("Box","entry_phase_space") + entry_phase_space = sim.add_volume("Box", "entry_phase_space") entry_phase_space.mother = actor_box - entry_phase_space.size = [0.6*m,1*nm,0.6*m] + entry_phase_space.size = [0.6 * m, 1 * nm, 0.6 * m] entry_phase_space.material = "G4_Galactic" - entry_phase_space.translation = [0,0.3*m - 0.5*nm,0] - entry_phase_space.color = [0.5,0.9,0.3,1] + entry_phase_space.translation = [0, 0.3 * m - 0.5 * nm, 0] + entry_phase_space.color = [0.5, 0.9, 0.3, 1] exit_phase_space = sim.add_volume("Box", "exit_phase_space") exit_phase_space.mother = world.name @@ -122,27 +123,33 @@ def test073(entry_data, exit_data, splitting_factor): exit_phase_space.color = [0.5, 0.9, 0.3, 1] # # print(sim.volume_manager.dump_volume_tree()) - liste_phase_space_name = [entry_phase_space.name,exit_phase_space.name,] + liste_phase_space_name = [ + entry_phase_space.name, + exit_phase_space.name, + ] for name in liste_phase_space_name: - phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_"+name) + phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_" + name) phsp.mother = name - phsp.attributes = ["EventID","TrackID","Weight","PDGCode","TrackCreatorProcess"] - name_phsp = "test073_" +name+".root" + phsp.attributes = [ + "EventID", + "TrackID", + "Weight", + "PDGCode", + "TrackCreatorProcess", + ] + name_phsp = "test073_" + name + ".root" phsp.output = output_path / name_phsp - sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" sim.physics_manager.enable_decay = False sim.physics_manager.global_production_cuts.gamma = 1 * mm sim.physics_manager.global_production_cuts.electron = 1 * mm sim.physics_manager.global_production_cuts.positron = 1 * mm - s = sim.add_actor("SimulationStatisticsActor", "Stats") s.track_types_flag = True - # go ! sim.run() output = sim.output @@ -150,12 +157,26 @@ def test073(entry_data, exit_data, splitting_factor): print(stats) # - entry_phsp = uproot.open(str(output_path) + "/test073_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) - exit_phase_space = uproot.open(str(output_path) + "/test073_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) + entry_phsp = uproot.open( + str(output_path) + + "/test073_" + + liste_phase_space_name[0] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[0] + ) + exit_phase_space = uproot.open( + str(output_path) + + "/test073_" + + liste_phase_space_name[1] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[1] + ) # df_entry = entry_phsp.arrays() df_exit = exit_phase_space.arrays() # - is_ok = test073(df_entry, df_exit,geom_splitting.splitting_factor) + is_ok = test073(df_entry, df_exit, geom_splitting.splitting_factor) # - utility.test_ok(is_ok) \ No newline at end of file + utility.test_ok(is_ok) From 01041fa5358e28ea179f98472ae26928e3209dc1 Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 20 Mar 2024 16:08:50 +0100 Subject: [PATCH 12/82] Update of the test name --- ....py => test074_kill_non_interacting_particles.py} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename opengate/tests/src/{test072_kill_non_interacting_particles.py => test074_kill_non_interacting_particles.py} (94%) diff --git a/opengate/tests/src/test072_kill_non_interacting_particles.py b/opengate/tests/src/test074_kill_non_interacting_particles.py similarity index 94% rename from opengate/tests/src/test072_kill_non_interacting_particles.py rename to opengate/tests/src/test074_kill_non_interacting_particles.py index 9e3d55729..7653c20fd 100644 --- a/opengate/tests/src/test072_kill_non_interacting_particles.py +++ b/opengate/tests/src/test074_kill_non_interacting_particles.py @@ -13,7 +13,7 @@ -def test072_test(entry_data, exit_data_1,exit_data_2): +def test074_test(entry_data, exit_data_1,exit_data_2): liste_ekin =[] liste_evtID = [] liste_trackID = [] @@ -165,7 +165,7 @@ def test072_test(entry_data, exit_data_1,exit_data_2): phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_"+name) phsp.mother = name phsp.attributes = ["EventID","TrackID","KineticEnergy"] - name_phsp = "test072_" +name+".root" + name_phsp = "test074_" +name+".root" phsp.output = output_path / name_phsp @@ -187,15 +187,15 @@ def test072_test(entry_data, exit_data_1,exit_data_2): print(stats) - entry_phsp = uproot.open(str(output_path) + "/test072_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) - exit_phase_space_1 = uproot.open(str(output_path) + "/test072_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) - exit_phase_space_2 = uproot.open( str(output_path) + "/test072_" + liste_phase_space_name[2] + ".root" + ":PhaseSpace_" + liste_phase_space_name[2]) + entry_phsp = uproot.open(str(output_path) + "/test074_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) + exit_phase_space_1 = uproot.open(str(output_path) + "/test074_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) + exit_phase_space_2 = uproot.open( str(output_path) + "/test074_" + liste_phase_space_name[2] + ".root" + ":PhaseSpace_" + liste_phase_space_name[2]) df_entry = entry_phsp.arrays() df_exit_1 = exit_phase_space_1.arrays() df_exit_2 = exit_phase_space_2.arrays() - is_ok = test072_test(df_entry, df_exit_1, df_exit_2) + is_ok = test074_test(df_entry, df_exit_1, df_exit_2) utility.test_ok(is_ok) \ No newline at end of file From 1483f92c07a3f8936e1e9f8c1dabb501092d1a87 Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 20 Mar 2024 16:12:31 +0100 Subject: [PATCH 13/82] Update of the test name --- ... test075_geometrical_splitting_volume_surface.py} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename opengate/tests/src/{test073_geometrical_splitting_volume_surface.py => test075_geometrical_splitting_volume_surface.py} (94%) diff --git a/opengate/tests/src/test073_geometrical_splitting_volume_surface.py b/opengate/tests/src/test075_geometrical_splitting_volume_surface.py similarity index 94% rename from opengate/tests/src/test073_geometrical_splitting_volume_surface.py rename to opengate/tests/src/test075_geometrical_splitting_volume_surface.py index f3b2d8278..5c7e044eb 100644 --- a/opengate/tests/src/test073_geometrical_splitting_volume_surface.py +++ b/opengate/tests/src/test075_geometrical_splitting_volume_surface.py @@ -9,7 +9,7 @@ import uproot -def test073(entry_data, exit_data, splitting_factor): +def test075(entry_data, exit_data, splitting_factor): splitted_particle_data_entry = entry_data[entry_data["TrackCreatorProcess"] == "none"] splitted_particle_data_exit = exit_data[exit_data["TrackCreatorProcess"] == "none"] @@ -35,7 +35,7 @@ def test073(entry_data, exit_data, splitting_factor): # main options ui = sim.user_info ui.g4_verbose = False - ui.visu = True + # ui.visu = True ui.visu_type = "vrml" ui.check_volumes_overlap = False # ui.running_verbose_level = gate.logger.EVENT @@ -128,7 +128,7 @@ def test073(entry_data, exit_data, splitting_factor): phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_"+name) phsp.mother = name phsp.attributes = ["EventID","TrackID","Weight","PDGCode","TrackCreatorProcess"] - name_phsp = "test073_" +name+".root" + name_phsp = "test075_" +name+".root" phsp.output = output_path / name_phsp @@ -150,12 +150,12 @@ def test073(entry_data, exit_data, splitting_factor): print(stats) # - entry_phsp = uproot.open(str(output_path) + "/test073_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) - exit_phase_space = uproot.open(str(output_path) + "/test073_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) + entry_phsp = uproot.open(str(output_path) + "/test075_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) + exit_phase_space = uproot.open(str(output_path) + "/test075_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) # df_entry = entry_phsp.arrays() df_exit = exit_phase_space.arrays() # - is_ok = test073(df_entry, df_exit,geom_splitting.splitting_factor) + is_ok = test075(df_entry, df_exit,geom_splitting.splitting_factor) # utility.test_ok(is_ok) \ No newline at end of file From 32ae380c2978dd41f8486c422d3362071043f071 Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 19 Apr 2024 15:10:57 +0200 Subject: [PATCH 14/82] work update --- .../GateKillNonInteractingParticleActor.cpp | 20 ++++++++-------- ...GateOptrComptPseudoTransportationActor.cpp | 23 ------------------- .../GateOptrComptPseudoTransportationActor.h | 1 - 3 files changed, 10 insertions(+), 34 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp index b71198e0e..bd264d37b 100644 --- a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp @@ -40,6 +40,16 @@ void GateKillNonInteractingParticleActor::PreUserTrackingAction(const G4Track* t void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { + G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName)->GetName(); + G4String physicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); + if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logNameMotherVolume) && (fIsFirstStep)){ + if ((fPassedByTheMotherVolume == false) && (physicalVolumeNamePreStep == fMotherVolumeName) && (step->GetPreStepPoint()->GetStepStatus() == 1)){ + fPassedByTheMotherVolume =true; + fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); + ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); + } + } + G4String logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); if ((fPassedByTheMotherVolume) && (step->GetPostStepPoint()->GetStepStatus() == 1)){ if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(),logicalVolumeNamePostStep ) !=fListOfVolumeAncestor.end()){ @@ -50,16 +60,6 @@ void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { } } } - - G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName)->GetName(); - G4String physicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); - if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logNameMotherVolume) && (fIsFirstStep)){ - if ((fPassedByTheMotherVolume == false) && (physicalVolumeNamePreStep == fMotherVolumeName) && (step->GetPreStepPoint()->GetStepStatus() == 1)){ - fPassedByTheMotherVolume =true; - fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); - ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); - } - } fIsFirstStep = false; } diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index e7a2d1e5a..141d73b77 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -172,29 +172,6 @@ void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { isSplitted = false; } } - - - if (fKillPrimaries) { - - G4String LogicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - G4String LogicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if (fPassedByABiasedVolume == false){ - if (LogicalVolumeNamePreStep == fMotherVolumeName){ - fPassedByABiasedVolume =true; - fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); - ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); - } - } - - if ((fPassedByABiasedVolume)){ - if ((step->GetTrack()->GetTrackID() == ftrackIDAtTheEntrance) && (step->GetPostStepPoint()->GetKineticEnergy() == fKineticEnergyAtTheEntrance)){ - if ((!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNamePostStep ) !=fNameOfBiasedLogicalVolume.end())) && (LogicalVolumeNamePostStep != fMotherVolumeName)){ - //std::cout<GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); - } - } - } - } } void GateOptrComptPseudoTransportationActor::BeginOfEventAction( diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index d3871d079..d60e88ad9 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -80,7 +80,6 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4bool fUseProbes = false; G4bool fSurvivedRR = false; G4bool fAttachToLogicalHolder = true; - G4bool fKillPrimaries = true; G4bool fPassedByABiasedVolume= false; G4double fKineticEnergyAtTheEntrance; G4int ftrackIDAtTheEntrance; From 8abfb780941736b29100480cacd559df91eca506 Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 19 Apr 2024 15:12:03 +0200 Subject: [PATCH 15/82] Update of work --- opengate/actors/miscactors.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 20bffa9c6..297892e0b 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -430,7 +430,6 @@ def __init__(self, user_info): g4.GateKillActor.__init__(self, user_info.__dict__) -<<<<<<< HEAD class ComptSplittingActor(g4.GateOptrComptSplittingActor, ActorBase): type_name = "ComptSplittingActor" @@ -553,4 +552,4 @@ def initialize(self, volume_engine=None): node = dico_of_volume_tree[volume_name] volume_name = node.mother self.list_of_volume_name.append(volume_name) - self.fListOfVolumeAncestor = self.list_of_volume_name \ No newline at end of file + self.fListOfVolumeAncestor = self.list_of_volume_name From ecf35b33376bac54d43f4c85252c913126fd84e4 Mon Sep 17 00:00:00 2001 From: majacquet Date: Sat, 20 Apr 2024 17:40:32 +0200 Subject: [PATCH 16/82] correction of a bug after the merge --- opengate/actors/miscactors.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 297892e0b..fc2a53541 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -529,16 +529,17 @@ class SurfaceSplittingActor(g4.GateSurfaceSplittingActor, ActorBase): def set_default_user_info(user_info): ActorBase.set_default_user_info(user_info) user_info.list_of_volume_name = [] + user_info.splitting_factor = 1 + user_info.split_entering_particles = False + user_info.split_exiting_particles = False + user_info.weight_threshold = 0 def __init__(self, user_info): ActorBase.__init__(self, user_info) g4.GateSurfaceSplittingActor.__init__(self, user_info.__dict__) self.list_of_volume_name = user_info.list_of_volume_name self.user_info.mother = user_info.mother - user_info.splitting_factor = 1 - user_info.split_entering_particles = False - user_info.split_exiting_particles = False - user_info.weight_threshold = 0 + def initialize(self, volume_engine=None): From 89213352448e9de47a83004270bd9f937003cd46 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 15:41:03 +0000 Subject: [PATCH 17/82] [pre-commit.ci] Automatic python and c++ formatting --- .../GateKillNonInteractingParticleActor.cpp | 58 ++++--- .../GateKillNonInteractingParticleActor.h | 5 +- .../GateOptnPairProdSplitting.cpp | 49 +++--- .../opengate_lib/GateOptnPairProdSplitting.h | 7 +- .../GateOptnScatteredGammaSplitting.cpp | 63 ++++---- .../GateOptnScatteredGammaSplitting.h | 3 - .../GateOptnVGenericSplitting.cpp | 70 ++++---- .../opengate_lib/GateOptnVGenericSplitting.h | 23 ++- .../opengate_lib/GateOptneBremSplitting.cpp | 69 ++++---- .../opengate_lib/GateOptneBremSplitting.h | 4 +- ...GateOptrComptPseudoTransportationActor.cpp | 149 ++++++++++-------- .../GateOptrComptPseudoTransportationActor.h | 20 +-- .../pyGateKillNonInteractingParticleActor.cpp | 7 +- opengate/actors/miscactors.py | 17 +- opengate/managers.py | 4 +- .../test074_kill_non_interacting_particles.py | 135 ++++++++-------- ...75_geometrical_splitting_volume_surface.py | 36 ++++- 17 files changed, 396 insertions(+), 323 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp index bd264d37b..5a6b544c7 100644 --- a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp @@ -6,60 +6,70 @@ ------------------------------------ -------------- */ #include "GateKillNonInteractingParticleActor.h" +#include "G4LogicalVolumeStore.hh" +#include "G4PhysicalVolumeStore.hh" #include "G4ios.hh" #include "GateHelpers.h" #include "GateHelpersDict.h" -#include "G4PhysicalVolumeStore.hh" -#include "G4LogicalVolumeStore.hh" - -GateKillNonInteractingParticleActor::GateKillNonInteractingParticleActor(py::dict &user_info) +GateKillNonInteractingParticleActor::GateKillNonInteractingParticleActor( + py::dict &user_info) : GateVActor(user_info, false) { fActions.insert("StartSimulationAction"); fActions.insert("SteppingAction"); fActions.insert("PreUserTrackingAction"); } - - - void GateKillNonInteractingParticleActor::ActorInitialize() {} void GateKillNonInteractingParticleActor::StartSimulationAction() { - fNbOfKilledParticles = 0; + fNbOfKilledParticles = 0; } - -void GateKillNonInteractingParticleActor::PreUserTrackingAction(const G4Track* track) { +void GateKillNonInteractingParticleActor::PreUserTrackingAction( + const G4Track *track) { fIsFirstStep = true; fKineticEnergyAtTheEntrance = 0; ftrackIDAtTheEntrance = 0; fPassedByTheMotherVolume = false; - } void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { - G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName)->GetName(); - G4String physicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); - if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logNameMotherVolume) && (fIsFirstStep)){ - if ((fPassedByTheMotherVolume == false) && (physicalVolumeNamePreStep == fMotherVolumeName) && (step->GetPreStepPoint()->GetStepStatus() == 1)){ - fPassedByTheMotherVolume =true; - fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); - ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); + G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance() + ->GetVolume(fMotherVolumeName) + ->GetName(); + G4String physicalVolumeNamePreStep = + step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); + if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != + logNameMotherVolume) && + (fIsFirstStep)) { + if ((fPassedByTheMotherVolume == false) && + (physicalVolumeNamePreStep == fMotherVolumeName) && + (step->GetPreStepPoint()->GetStepStatus() == 1)) { + fPassedByTheMotherVolume = true; + fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); + ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); } } - G4String logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if ((fPassedByTheMotherVolume) && (step->GetPostStepPoint()->GetStepStatus() == 1)){ - if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(),logicalVolumeNamePostStep ) !=fListOfVolumeAncestor.end()){ - if ((step->GetTrack()->GetTrackID() == ftrackIDAtTheEntrance) && (step->GetPostStepPoint()->GetKineticEnergy() == fKineticEnergyAtTheEntrance)){ + G4String logicalVolumeNamePostStep = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + if ((fPassedByTheMotherVolume) && + (step->GetPostStepPoint()->GetStepStatus() == 1)) { + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), + logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { + if ((step->GetTrack()->GetTrackID() == ftrackIDAtTheEntrance) && + (step->GetPostStepPoint()->GetKineticEnergy() == + fKineticEnergyAtTheEntrance)) { auto track = step->GetTrack(); track->SetTrackStatus(fStopAndKill); fNbOfKilledParticles++; + } } } -} - + fIsFirstStep = false; } diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h index a7cf58ccb..bb7aabe7a 100644 --- a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h @@ -27,16 +27,15 @@ class GateKillNonInteractingParticleActor : public GateVActor { // Main function called every step in attached volume void SteppingAction(G4Step *) override; - void PreUserTrackingAction(const G4Track*) override; + void PreUserTrackingAction(const G4Track *) override; std::vector fParticlesTypeToKill; - G4bool fPassedByTheMotherVolume= false; + G4bool fPassedByTheMotherVolume = false; G4double fKineticEnergyAtTheEntrance = 0; G4int ftrackIDAtTheEntrance = 0; G4bool fIsFirstStep = true; std::vector fListOfVolumeAncestor; - long fNbOfKilledParticles{}; }; diff --git a/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp index 767f8a1be..cf30c3a7d 100644 --- a/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp @@ -49,54 +49,49 @@ //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnPairProdSplitting:: - GateOptnPairProdSplitting(G4String name) +GateOptnPairProdSplitting::GateOptnPairProdSplitting(G4String name) : GateOptnVGenericSplitting(name), fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnPairProdSplitting:: - ~GateOptnPairProdSplitting() {} +GateOptnPairProdSplitting::~GateOptnPairProdSplitting() {} -G4VParticleChange * -GateOptnPairProdSplitting::ApplyFinalStateBiasing( const G4BiasingProcessInterface *callingProcess, const G4Track *track, const G4Step *step, G4bool &) { +G4VParticleChange *GateOptnPairProdSplitting::ApplyFinalStateBiasing( + const G4BiasingProcessInterface *callingProcess, const G4Track *track, + const G4Step *step, G4bool &) { - G4int splittingFactor = ceil(fSplittingFactor); - G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor) / splittingFactor; + G4double survivalProbabilitySplitting = + 1 - (splittingFactor - fSplittingFactor) / splittingFactor; G4double particleWeight = 0; - G4VParticleChange* processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + G4VParticleChange *processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) return processFinalState; - TrackInitializationGamma(&fParticleChange,processFinalState,track,fSplittingFactor); + if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) + return processFinalState; + TrackInitializationGamma(&fParticleChange, processFinalState, track, + fSplittingFactor); processFinalState->Clear(); G4int nCalls = 1; while (nCalls <= splittingFactor) { G4double splittingProbability = G4UniformRand(); - if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { particleWeight = track->GetWeight() / fSplittingFactor; - G4VParticleChange *processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - if (processFinalState->GetNumberOfSecondaries() >= 1 ) { - for(int i =0; i < processFinalState->GetNumberOfSecondaries();i++){ - G4Track *SecondaryTrack =processFinalState->GetSecondary(i); + G4VParticleChange *processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if (processFinalState->GetNumberOfSecondaries() >= 1) { + for (int i = 0; i < processFinalState->GetNumberOfSecondaries(); i++) { + G4Track *SecondaryTrack = processFinalState->GetSecondary(i); SecondaryTrack->SetWeight(particleWeight); fParticleChange.AddSecondary(SecondaryTrack); - } - } + } + } } nCalls++; } return &fParticleChange; } - - - - - - - - - diff --git a/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h index fd1f172c5..ef4128a81 100644 --- a/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h +++ b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h @@ -31,8 +31,8 @@ #ifndef GateOptnPairProdSplitting_h #define GateOptnPairProdSplitting_h 1 -#include "GateOptnVGenericSplitting.h" #include "G4ParticleChange.hh" +#include "GateOptnVGenericSplitting.h" class GateOptnPairProdSplitting : public GateOptnVGenericSplitting { public: @@ -42,12 +42,11 @@ class GateOptnPairProdSplitting : public GateOptnVGenericSplitting { // -- destructor: virtual ~GateOptnPairProdSplitting(); - virtual G4VParticleChange * + virtual G4VParticleChange * ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, const G4Step *, G4bool &); -G4ParticleChange fParticleChange; - + G4ParticleChange fParticleChange; }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp index 8dbb41be6..bbfe8acff 100644 --- a/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp @@ -37,7 +37,6 @@ #include "G4GammaConversion.hh" #include "G4ParticleChange.hh" #include "G4ParticleChangeForGamma.hh" -#include "GateOptnVGenericSplitting.h" #include "G4PhotoElectricEffect.hh" #include "G4ProcessType.hh" #include "G4RayleighScattering.hh" @@ -45,21 +44,19 @@ #include "G4TrackStatus.hh" #include "G4TrackingManager.hh" #include "G4VEmProcess.hh" +#include "GateOptnVGenericSplitting.h" #include //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnScatteredGammaSplitting:: - GateOptnScatteredGammaSplitting(G4String name) - : GateOptnVGenericSplitting(name),fParticleChange() {} +GateOptnScatteredGammaSplitting::GateOptnScatteredGammaSplitting(G4String name) + : GateOptnVGenericSplitting(name), fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnScatteredGammaSplitting:: - ~GateOptnScatteredGammaSplitting() {} +GateOptnScatteredGammaSplitting::~GateOptnScatteredGammaSplitting() {} -G4VParticleChange * -GateOptnScatteredGammaSplitting::ApplyFinalStateBiasing( +G4VParticleChange *GateOptnScatteredGammaSplitting::ApplyFinalStateBiasing( const G4BiasingProcessInterface *callingProcess, const G4Track *track, const G4Step *step, G4bool &) { @@ -70,21 +67,22 @@ GateOptnScatteredGammaSplitting::ApplyFinalStateBiasing( const G4ThreeVector position = step->GetPostStepPoint()->GetPosition(); G4int splittingFactor = ceil(fSplittingFactor); - G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor) / splittingFactor; + G4double survivalProbabilitySplitting = + 1 - (splittingFactor - fSplittingFactor) / splittingFactor; G4double gammaWeight = 0; - - - G4VParticleChange* processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + G4VParticleChange *processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); // In case we don't want to split (a bit faster) i.e no biaising or no // splitting low weights particles. - if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) return processFinalState; + if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) + return processFinalState; - TrackInitializationGamma(&fParticleChange,processFinalState,track,fSplittingFactor); + TrackInitializationGamma(&fParticleChange, processFinalState, track, + fSplittingFactor); processFinalState->Clear(); - // There is here the biasing process : // Since G4VParticleChange class does not allow to retrieve scattered gamma // information, we need to cast the type G4ParticleChangeForGamma to the @@ -105,20 +103,27 @@ GateOptnScatteredGammaSplitting::ApplyFinalStateBiasing( // gamma) must be considered as secondary particles, even though generated // gamma will not be cut here by the applied cut. - G4int nCalls = 1; while (nCalls <= splittingFactor) { gammaWeight = track->GetWeight() / fSplittingFactor; - G4VParticleChange *processGammaSplittedFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - G4ParticleChangeForGamma *castedProcessGammaSplittedFinalState = (G4ParticleChangeForGamma *)processGammaSplittedFinalState; - const G4ThreeVector momentum = castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(); - G4double energy = castedProcessGammaSplittedFinalState->GetProposedKineticEnergy(); + G4VParticleChange *processGammaSplittedFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + G4ParticleChangeForGamma *castedProcessGammaSplittedFinalState = + (G4ParticleChangeForGamma *)processGammaSplittedFinalState; + const G4ThreeVector momentum = + castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(); + G4double energy = + castedProcessGammaSplittedFinalState->GetProposedKineticEnergy(); G4double splittingProbability = G4UniformRand(); - if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { - if (fRussianRouletteForAngle == true){ - G4double weightToApply = RussianRouletteForAngleSurvival(castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(),fVectorDirector,fMaxTheta,fSplittingFactor); - if (weightToApply != 0){ + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { + if (fRussianRouletteForAngle == true) { + G4double weightToApply = RussianRouletteForAngleSurvival( + castedProcessGammaSplittedFinalState + ->GetProposedMomentumDirection(), + fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0) { gammaWeight = gammaWeight * weightToApply; G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); @@ -127,15 +132,15 @@ GateOptnScatteredGammaSplitting::ApplyFinalStateBiasing( gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { - G4Track *electronTrack = processGammaSplittedFinalState->GetSecondary(0); + G4Track *electronTrack = + processGammaSplittedFinalState->GetSecondary(0); electronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(electronTrack); } } } - - else{ + else { G4Track *gammaTrack = new G4Track(*track); gammaTrack->SetWeight(gammaWeight); gammaTrack->SetKineticEnergy(energy); @@ -143,10 +148,10 @@ GateOptnScatteredGammaSplitting::ApplyFinalStateBiasing( gammaTrack->SetPosition(position); fParticleChange.AddSecondary(gammaTrack); if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { - G4Track *electronTrack = processGammaSplittedFinalState->GetSecondary(0); + G4Track *electronTrack = + processGammaSplittedFinalState->GetSecondary(0); electronTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(electronTrack); - } } } diff --git a/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h index 56a6650cc..ff0a6b623 100644 --- a/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h +++ b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h @@ -43,14 +43,11 @@ class GateOptnScatteredGammaSplitting : public GateOptnVGenericSplitting { // -- destructor: virtual ~GateOptnScatteredGammaSplitting(); - virtual G4VParticleChange * ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, const G4Step *, G4bool &); G4ParticleChange fParticleChange; - - }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp index 349a393ff..63f673ddf 100644 --- a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp @@ -49,57 +49,63 @@ //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnVGenericSplitting:: - GateOptnVGenericSplitting(G4String name) +GateOptnVGenericSplitting::GateOptnVGenericSplitting(G4String name) : G4VBiasingOperation(name), fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnVGenericSplitting:: - ~GateOptnVGenericSplitting() {} +GateOptnVGenericSplitting::~GateOptnVGenericSplitting() {} +void GateOptnVGenericSplitting::TrackInitializationChargedParticle( + G4ParticleChange *particleChange, G4VParticleChange *processFinalState, + const G4Track *track, G4double split) { -void GateOptnVGenericSplitting::TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split) { - - G4ParticleChangeForLoss* processFinalStateForLoss =( G4ParticleChangeForLoss* ) processFinalState ; + G4ParticleChangeForLoss *processFinalStateForLoss = + (G4ParticleChangeForLoss *)processFinalState; particleChange->Initialize(*track); - particleChange->ProposeTrackStatus(processFinalStateForLoss->GetTrackStatus() ); - particleChange->ProposeEnergy(processFinalStateForLoss->GetProposedKineticEnergy() ); - particleChange->ProposeMomentumDirection(processFinalStateForLoss->GetProposedMomentumDirection()); + particleChange->ProposeTrackStatus( + processFinalStateForLoss->GetTrackStatus()); + particleChange->ProposeEnergy( + processFinalStateForLoss->GetProposedKineticEnergy()); + particleChange->ProposeMomentumDirection( + processFinalStateForLoss->GetProposedMomentumDirection()); particleChange->SetNumberOfSecondaries(fSplittingFactor); particleChange->SetSecondaryWeightByProcess(true); processFinalStateForLoss->Clear(); } -void GateOptnVGenericSplitting::TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split) { - G4ParticleChangeForGamma* processFinalStateForGamma = (G4ParticleChangeForGamma *)processFinalState; +void GateOptnVGenericSplitting::TrackInitializationGamma( + G4ParticleChange *particleChange, G4VParticleChange *processFinalState, + const G4Track *track, G4double split) { + G4ParticleChangeForGamma *processFinalStateForGamma = + (G4ParticleChangeForGamma *)processFinalState; particleChange->Initialize(*track); - particleChange->ProposeTrackStatus(processFinalStateForGamma->GetTrackStatus() ); - particleChange->ProposeEnergy(processFinalStateForGamma->GetProposedKineticEnergy() ); - particleChange->ProposeMomentumDirection(processFinalStateForGamma->GetProposedMomentumDirection() ); + particleChange->ProposeTrackStatus( + processFinalStateForGamma->GetTrackStatus()); + particleChange->ProposeEnergy( + processFinalStateForGamma->GetProposedKineticEnergy()); + particleChange->ProposeMomentumDirection( + processFinalStateForGamma->GetProposedMomentumDirection()); particleChange->SetNumberOfSecondaries(fSplittingFactor); particleChange->SetSecondaryWeightByProcess(true); processFinalStateForGamma->Clear(); - - } -G4double GateOptnVGenericSplitting::RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split){ -G4double cosTheta =vectorDirector * dir; -G4double theta = std::acos(cosTheta); -G4double weightToApply = 1; -if (theta > fMaxTheta){ - G4double probability = G4UniformRand(); - if (probability <= 1 / split) { - weightToApply = split; +G4double GateOptnVGenericSplitting::RussianRouletteForAngleSurvival( + G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta, + G4double split) { + G4double cosTheta = vectorDirector * dir; + G4double theta = std::acos(cosTheta); + G4double weightToApply = 1; + if (theta > fMaxTheta) { + G4double probability = G4UniformRand(); + if (probability <= 1 / split) { + weightToApply = split; + } else { + G4double weightToApply = 0; + } } - else{ - G4double weightToApply = 0; - } -} -return weightToApply; - + return weightToApply; } - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h index ed75c9edd..b27819519 100644 --- a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h @@ -56,7 +56,9 @@ class GateOptnVGenericSplitting : public G4VBiasingOperation { // --Used: virtual G4VParticleChange * ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, - const G4Step *, G4bool &){return 0;}; + const G4Step *, G4bool &) { + return 0; + }; // -- Unsued: virtual G4double DistanceToApplyOperation(const G4Track *, G4double, @@ -68,14 +70,19 @@ class GateOptnVGenericSplitting : public G4VBiasingOperation { return 0; } -// ---------------------------------------------- -// -- Methods for the generic splitting -// ---------------------------------------------- - -void TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); -void TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); -G4double RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split); + // ---------------------------------------------- + // -- Methods for the generic splitting + // ---------------------------------------------- + void TrackInitializationChargedParticle(G4ParticleChange *particleChange, + G4VParticleChange *processFinalState, + const G4Track *track, G4double split); + void TrackInitializationGamma(G4ParticleChange *particleChange, + G4VParticleChange *processFinalState, + const G4Track *track, G4double split); + G4double RussianRouletteForAngleSurvival(G4ThreeVector dir, + G4ThreeVector vectorDirector, + G4double maxTheta, G4double split); public: // ---------------------------------------------- diff --git a/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp b/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp index f067220f9..ce9ff2324 100644 --- a/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp @@ -36,7 +36,6 @@ #include "G4Gamma.hh" #include "G4GammaConversion.hh" #include "G4ParticleChange.hh" -#include "GateOptnVGenericSplitting.h" #include "G4ParticleChangeForGamma.hh" #include "G4ParticleChangeForLoss.hh" #include "G4PhotoElectricEffect.hh" @@ -46,53 +45,59 @@ #include "G4TrackStatus.hh" #include "G4TrackingManager.hh" #include "G4VEmProcess.hh" +#include "GateOptnVGenericSplitting.h" #include //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptneBremSplitting:: - GateOptneBremSplitting(G4String name) - :GateOptnVGenericSplitting(name), fParticleChange() {} +GateOptneBremSplitting::GateOptneBremSplitting(G4String name) + : GateOptnVGenericSplitting(name), fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptneBremSplitting:: - ~GateOptneBremSplitting() {} +GateOptneBremSplitting::~GateOptneBremSplitting() {} -G4VParticleChange * -GateOptneBremSplitting::ApplyFinalStateBiasing(const G4BiasingProcessInterface *callingProcess, const G4Track *track, const G4Step *step, G4bool &) { +G4VParticleChange *GateOptneBremSplitting::ApplyFinalStateBiasing( + const G4BiasingProcessInterface *callingProcess, const G4Track *track, + const G4Step *step, G4bool &) { G4int splittingFactor = ceil(fSplittingFactor); - G4double survivalProbabilitySplitting = 1 - (splittingFactor - fSplittingFactor) / splittingFactor; - G4VParticleChange* processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - if ( fSplittingFactor == 1 ) return processFinalState; - if ( processFinalState->GetNumberOfSecondaries() == 0 ) return processFinalState; - - TrackInitializationChargedParticle(&fParticleChange,processFinalState, track,fSplittingFactor); - + G4double survivalProbabilitySplitting = + 1 - (splittingFactor - fSplittingFactor) / splittingFactor; + G4VParticleChange *processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if (fSplittingFactor == 1) + return processFinalState; + if (processFinalState->GetNumberOfSecondaries() == 0) + return processFinalState; - processFinalState->Clear(); + TrackInitializationChargedParticle(&fParticleChange, processFinalState, track, + fSplittingFactor); + processFinalState->Clear(); G4int nCalls = 1; - while ( nCalls <= fSplittingFactor ){ + while (nCalls <= fSplittingFactor) { G4double splittingProbability = G4UniformRand(); - if (splittingProbability <= survivalProbabilitySplitting || survivalProbabilitySplitting == 1) { - processFinalState = callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); - if ( processFinalState->GetNumberOfSecondaries() >= 1 ) { - for(int i =0; i < processFinalState->GetNumberOfSecondaries();i++){ + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { + processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if (processFinalState->GetNumberOfSecondaries() >= 1) { + for (int i = 0; i < processFinalState->GetNumberOfSecondaries(); i++) { G4double gammaWeight = track->GetWeight() / fSplittingFactor; - G4Track* gammaTrack = processFinalState->GetSecondary(i); - if (fRussianRouletteForAngle == true){ - G4double weightToApply = RussianRouletteForAngleSurvival(gammaTrack->GetMomentumDirection(),fVectorDirector,fMaxTheta,fSplittingFactor); - if (weightToApply != 0){ + G4Track *gammaTrack = processFinalState->GetSecondary(i); + if (fRussianRouletteForAngle == true) { + G4double weightToApply = RussianRouletteForAngleSurvival( + gammaTrack->GetMomentumDirection(), fVectorDirector, fMaxTheta, + fSplittingFactor); + if (weightToApply != 0) { gammaWeight = gammaWeight * weightToApply; - gammaTrack->SetWeight( gammaWeight); - fParticleChange.AddSecondary( gammaTrack ); + gammaTrack->SetWeight(gammaWeight); + fParticleChange.AddSecondary(gammaTrack); } - } - else { - gammaTrack->SetWeight( gammaWeight); + } else { + gammaTrack->SetWeight(gammaWeight); fParticleChange.AddSecondary(gammaTrack); } } @@ -100,8 +105,8 @@ GateOptneBremSplitting::ApplyFinalStateBiasing(const G4BiasingProcessInterface * processFinalState->Clear(); } nCalls++; - } - return &fParticleChange; + } + return &fParticleChange; } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptneBremSplitting.h b/core/opengate_core/opengate_lib/GateOptneBremSplitting.h index cddd10ece..47319e536 100644 --- a/core/opengate_core/opengate_lib/GateOptneBremSplitting.h +++ b/core/opengate_core/opengate_lib/GateOptneBremSplitting.h @@ -44,13 +44,11 @@ class GateOptneBremSplitting : public GateOptnVGenericSplitting { virtual ~GateOptneBremSplitting(); public: - virtual G4VParticleChange * - ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, const G4Step *, G4bool &); G4ParticleChange fParticleChange; - }; #endif diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 141d73b77..62c6b503b 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -32,26 +32,26 @@ #include "CLHEP/Units/SystemOfUnits.h" #include "G4BiasingProcessInterface.hh" +#include "G4Electron.hh" #include "G4EmCalculator.hh" #include "G4Exception.hh" #include "G4Gamma.hh" -#include "G4Positron.hh" -#include "G4Electron.hh" #include "G4LogicalVolumeStore.hh" #include "G4ParticleTable.hh" #include "G4PhysicalVolumeStore.hh" +#include "G4Positron.hh" #include "G4ProcessManager.hh" #include "G4ProcessVector.hh" #include "G4RunManager.hh" #include "G4TrackStatus.hh" +#include "G4UImanager.hh" #include "G4UserTrackingAction.hh" #include "G4VEmProcess.hh" +#include "G4eplusAnnihilation.hh" +#include "GateOptnPairProdSplitting.h" #include "GateOptnScatteredGammaSplitting.h" #include "GateOptneBremSplitting.h" -#include "GateOptnPairProdSplitting.h" #include "GateOptrComptPseudoTransportationActor.h" -#include "G4UImanager.hh" -#include "G4eplusAnnihilation.hh" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -75,15 +75,16 @@ GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( DictGetBool(user_info, "russian_roulette_for_weights"); fMaxTheta = DictGetDouble(user_info, "max_theta"); fFreeFlightOperation = new GateOptnForceFreeFlight("freeFlightOperation"); - fScatteredGammaSplittingOperation = new GateOptnScatteredGammaSplitting("comptSplittingOperation"); - feBremSplittingOperation = new GateOptneBremSplitting("eBremSplittingOperation"); - fPairProdSplittingOperation = new GateOptnPairProdSplitting("PairProdSplittingOperation"); + fScatteredGammaSplittingOperation = + new GateOptnScatteredGammaSplitting("comptSplittingOperation"); + feBremSplittingOperation = + new GateOptneBremSplitting("eBremSplittingOperation"); + fPairProdSplittingOperation = + new GateOptnPairProdSplitting("PairProdSplittingOperation"); fActions.insert("StartSimulationAction"); fActions.insert("SteppingAction"); fActions.insert("BeginOfEventAction"); isSplitted = false; - - } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -97,18 +98,22 @@ void GateOptrComptPseudoTransportationActor::AttachAllLogicalDaughtersVolumes( AttachTo(volume); } } - if (fAttachToLogicalHolder == true){ + if (fAttachToLogicalHolder == true) { AttachTo(volume); } G4int nbOfDaughters = volume->GetNoDaughters(); if (nbOfDaughters > 0) { for (int i = 0; i < nbOfDaughters; i++) { - G4String LogicalVolumeName = volume->GetDaughter(i)->GetLogicalVolume()->GetName(); + G4String LogicalVolumeName = + volume->GetDaughter(i)->GetLogicalVolume()->GetName(); G4LogicalVolume *logicalDaughtersVolume = volume->GetDaughter(i)->GetLogicalVolume(); AttachAllLogicalDaughtersVolumes(logicalDaughtersVolume); - if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end())) - fNameOfBiasedLogicalVolume.push_back(volume->GetDaughter(i)->GetLogicalVolume()->GetName()); + if (!(std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeName) != fNameOfBiasedLogicalVolume.end())) + fNameOfBiasedLogicalVolume.push_back( + volume->GetDaughter(i)->GetLogicalVolume()->GetName()); } } } @@ -122,21 +127,20 @@ void GateOptrComptPseudoTransportationActor::StartSimulationAction() { // AttachAllLogicalDaughtersVolumes. AttachAllLogicalDaughtersVolumes(biasingVolume); for (int i = 0; i < fNameOfBiasedLogicalVolume.size(); i++) - fScatteredGammaSplittingOperation->SetSplittingFactor(fSplittingFactor); + fScatteredGammaSplittingOperation->SetSplittingFactor(fSplittingFactor); fScatteredGammaSplittingOperation->SetMaxTheta(fMaxTheta); - fScatteredGammaSplittingOperation->SetRussianRouletteForAngle(fRussianRouletteForAngle); + fScatteredGammaSplittingOperation->SetRussianRouletteForAngle( + fRussianRouletteForAngle); feBremSplittingOperation->SetSplittingFactor(fSplittingFactor); feBremSplittingOperation->SetMaxTheta(fMaxTheta); - feBremSplittingOperation->SetRussianRouletteForAngle(fRussianRouletteForAngle); + feBremSplittingOperation->SetRussianRouletteForAngle( + fRussianRouletteForAngle); fPairProdSplittingOperation->SetSplittingFactor(fSplittingFactor); - - - - fFreeFlightOperation->SetRussianRouletteForWeights(fRussianRouletteForWeights); - + fFreeFlightOperation->SetRussianRouletteForWeights( + fRussianRouletteForWeights); } void GateOptrComptPseudoTransportationActor::StartRun() { @@ -159,17 +163,20 @@ void GateOptrComptPseudoTransportationActor::StartRun() { feBremSplittingOperation->SetVectorDirector(fVectorDirector); } - - void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { - - if ((isSplitted == true) && (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { - G4String LogicalVolumeName = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end()) - && (LogicalVolumeName != fMotherVolumeName)) { - step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); - isSplitted = false; + if ((isSplitted == true) && + (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { + G4String LogicalVolumeName = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + if (!(std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeName) != fNameOfBiasedLogicalVolume.end()) && + (LogicalVolumeName != fMotherVolumeName)) { + step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); + isSplitted = false; } } } @@ -177,24 +184,27 @@ void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { void GateOptrComptPseudoTransportationActor::BeginOfEventAction( const G4Event *event) { fKillOthersParticles = false; - fPassedByABiasedVolume=false; + fPassedByABiasedVolume = false; fEventID = event->GetEventID(); - fEventIDKineticEnergy = event->GetPrimaryVertex(0)->GetPrimary(0)->GetKineticEnergy(); - - + fEventIDKineticEnergy = + event->GetPrimaryVertex(0)->GetPrimary(0)->GetKineticEnergy(); } void GateOptrComptPseudoTransportationActor::StartTracking( const G4Track *track) { G4String CreationProcessName = "None"; - - if (track->GetCreatorProcess() != 0){ + + if (track->GetCreatorProcess() != 0) { CreationProcessName = track->GetCreatorProcess()->GetProcessName(); } - - if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ - G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); - if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ + + if (track->GetParticleDefinition()->GetParticleName() == "gamma") { + G4String LogicalVolumeNameOfCreation = + track->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeNameOfCreation) != + fNameOfBiasedLogicalVolume.end()) { fInitialWeight = track->GetWeight(); fFreeFlightOperation->SetInitialWeight(fInitialWeight); } @@ -215,11 +225,16 @@ void GateOptrComptPseudoTransportationActor::StartTracking( G4VBiasingOperation * GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation( const G4Track *track, const G4BiasingProcessInterface *callingProcess) { - - if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ - G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); - if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ - fFreeFlightOperation->SetMinWeight(fInitialWeight/fRelativeMinWeightOfParticle); + + if (track->GetParticleDefinition()->GetParticleName() == "gamma") { + G4String LogicalVolumeNameOfCreation = + track->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeNameOfCreation) != + fNameOfBiasedLogicalVolume.end()) { + fFreeFlightOperation->SetMinWeight(fInitialWeight / + fRelativeMinWeightOfParticle); fFreeFlightOperation->SetTrackWeight(track->GetWeight()); fFreeFlightOperation->SetCountProcess(0); return fFreeFlightOperation; @@ -239,44 +254,46 @@ G4VBiasingOperation * GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( const G4Track *track, const G4BiasingProcessInterface *callingProcess) { G4String particleName = track->GetParticleDefinition()->GetParticleName(); - - G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); + + G4String LogicalVolumeNameOfCreation = + track->GetLogicalVolumeAtVertex()->GetName(); G4String CreationProcessName = ""; - if (track->GetCreatorProcess() != 0){ + if (track->GetCreatorProcess() != 0) { CreationProcessName = track->GetCreatorProcess()->GetProcessName(); } - - if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ - if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ + if (track->GetParticleDefinition()->GetParticleName() == "gamma") { + if (std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeNameOfCreation) != + fNameOfBiasedLogicalVolume.end()) { return callingProcess->GetCurrentOccurenceBiasingOperation(); } } - - if (((CreationProcessName != "biasWrapper(conv)") && (CreationProcessName != "biasWrapper(compt)")) - && (callingProcess->GetWrappedProcess()->GetProcessName() == "eBrem")){ + if (((CreationProcessName != "biasWrapper(conv)") && + (CreationProcessName != "biasWrapper(compt)")) && + (callingProcess->GetWrappedProcess()->GetProcessName() == "eBrem")) { return feBremSplittingOperation; - } + } - - - if (!(std::find(fCreationProcessNameList.begin(), fCreationProcessNameList.end(),CreationProcessName) != fCreationProcessNameList.end())){ - if ((callingProcess->GetWrappedProcess()->GetProcessName() == "compt") || (callingProcess->GetWrappedProcess()->GetProcessName() == "Rayl")){ + if (!(std::find(fCreationProcessNameList.begin(), + fCreationProcessNameList.end(), + CreationProcessName) != fCreationProcessNameList.end())) { + if ((callingProcess->GetWrappedProcess()->GetProcessName() == "compt") || + (callingProcess->GetWrappedProcess()->GetProcessName() == "Rayl")) { isSplitted = true; return fScatteredGammaSplittingOperation; } - if ((callingProcess->GetWrappedProcess()->GetProcessName() == "conv")){ - return fPairProdSplittingOperation; + if ((callingProcess->GetWrappedProcess()->GetProcessName() == "conv")) { + return fPairProdSplittingOperation; } - } - return 0; } - + return 0; +} void GateOptrComptPseudoTransportationActor::EndTracking() { isSplitted = false; } - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index d60e88ad9..728651958 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -32,8 +32,8 @@ #include "G4EmCalculator.hh" #include "G4VBiasingOperator.hh" #include "GateOptnForceFreeFlight.h" -#include "GateOptneBremSplitting.h" #include "GateOptnPairProdSplitting.h" +#include "GateOptneBremSplitting.h" #include "GateVActor.h" #include @@ -80,17 +80,19 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4bool fUseProbes = false; G4bool fSurvivedRR = false; G4bool fAttachToLogicalHolder = true; - G4bool fPassedByABiasedVolume= false; + G4bool fPassedByABiasedVolume = false; G4double fKineticEnergyAtTheEntrance; G4int ftrackIDAtTheEntrance; G4int fEventID; G4double fEventIDKineticEnergy; - G4bool ftestbool= false; - const G4VProcess* fAnnihilation =nullptr; + G4bool ftestbool = false; + const G4VProcess *fAnnihilation = nullptr; std::vector fNameOfBiasedLogicalVolume = {}; std::vector v_EventID = {}; - std::vector fCreationProcessNameList = {"biasWrapper(compt)","biasWrapper(Rayl)", "biasWrapper(eBrem)","biasWrapper(annihil)"}; + std::vector fCreationProcessNameList = { + "biasWrapper(compt)", "biasWrapper(Rayl)", "biasWrapper(eBrem)", + "biasWrapper(annihil)"}; // Unused but mandatory virtual void StartSimulationAction(); @@ -126,10 +128,10 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, using G4VBiasingOperator::OperationApplied; private: - GateOptnForceFreeFlight* fFreeFlightOperation; - GateOptnScatteredGammaSplitting* fScatteredGammaSplittingOperation; - GateOptneBremSplitting* feBremSplittingOperation; - GateOptnPairProdSplitting* fPairProdSplittingOperation; + GateOptnForceFreeFlight *fFreeFlightOperation; + GateOptnScatteredGammaSplitting *fScatteredGammaSplittingOperation; + GateOptneBremSplitting *feBremSplittingOperation; + GateOptnPairProdSplitting *fPairProdSplittingOperation; }; #endif diff --git a/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp index 6b5cee6a2..5de9fe12b 100644 --- a/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp +++ b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp @@ -12,8 +12,11 @@ namespace py = pybind11; #include "GateKillNonInteractingParticleActor.h" void init_GateKillNonInteractingParticleActor(py::module &m) { - py::class_, + py::class_, GateVActor>(m, "GateKillNonInteractingParticleActor") - .def_readwrite("fListOfVolumeAncestor", &GateKillNonInteractingParticleActor::fListOfVolumeAncestor) + .def_readwrite( + "fListOfVolumeAncestor", + &GateKillNonInteractingParticleActor::fListOfVolumeAncestor) .def(py::init()); } diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index fc2a53541..daf48fdd2 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -465,7 +465,7 @@ def set_default_user_info(user_info): user_info.relative_min_weight_of_particle = np.inf user_info.gamma_processes = ["compt", "phot", "conv", "Rayl"] user_info.electron_processes = ["eBrem"] - user_info.positron_processes = ["annihil","eBrem"] + user_info.positron_processes = ["annihil", "eBrem"] user_info.russian_roulette_for_angle = False user_info.rotation_vector_director = False user_info.vector_director = [0, 0, 1] @@ -490,23 +490,23 @@ def set_default_user_info(user_info): def __init__(self, user_info): ActorBase.__init__(self, user_info) g4.GateBOptrBremSplittingActor.__init__(self, user_info.__dict__) - -class KillNonInteractingParticleActor(g4.GateKillNonInteractingParticleActor, ActorBase): + +class KillNonInteractingParticleActor( + g4.GateKillNonInteractingParticleActor, ActorBase +): type_name = "KillNonInteractingParticleActor" def set_default_user_info(user_info): ActorBase.set_default_user_info(user_info) user_info.list_of_volume_name = [] - def __init__(self, user_info): ActorBase.__init__(self, user_info) g4.GateKillNonInteractingParticleActor.__init__(self, user_info.__dict__) self.list_of_volume_name = user_info.list_of_volume_name self.user_info.mother = user_info.mother - def initialize(self, volume_engine=None): super().initialize(volume_engine) @@ -515,7 +515,7 @@ def initialize(self, volume_engine=None): for pre, _, node in RenderTree(volume_tree): dico_of_volume_tree[str(node.name)] = node volume_name = self.user_info.mother - while volume_name != 'world': + while volume_name != "world": node = dico_of_volume_tree[volume_name] volume_name = node.mother self.list_of_volume_name.append(volume_name) @@ -525,7 +525,6 @@ def initialize(self, volume_engine=None): class SurfaceSplittingActor(g4.GateSurfaceSplittingActor, ActorBase): type_name = "SurfaceSplittingActor" - def set_default_user_info(user_info): ActorBase.set_default_user_info(user_info) user_info.list_of_volume_name = [] @@ -539,8 +538,6 @@ def __init__(self, user_info): g4.GateSurfaceSplittingActor.__init__(self, user_info.__dict__) self.list_of_volume_name = user_info.list_of_volume_name self.user_info.mother = user_info.mother - - def initialize(self, volume_engine=None): super().initialize(volume_engine) @@ -549,7 +546,7 @@ def initialize(self, volume_engine=None): for pre, _, node in RenderTree(volume_tree): dico_of_volume_tree[str(node.name)] = node volume_name = self.user_info.mother - while volume_name != 'world': + while volume_name != "world": node = dico_of_volume_tree[volume_name] volume_name = node.mother self.list_of_volume_name.append(volume_name) diff --git a/opengate/managers.py b/opengate/managers.py index 6add752ba..af762e6bb 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -988,9 +988,9 @@ def dump_volume_types(self): def dump_material_database_names(self): return list(self.material_database.filenames) - + def get_volume_tree(self): - return (self.volume_tree_root) + return self.volume_tree_root def get_volume_tree(self): return self.volume_tree_root diff --git a/opengate/tests/src/test074_kill_non_interacting_particles.py b/opengate/tests/src/test074_kill_non_interacting_particles.py index 7653c20fd..36616f210 100644 --- a/opengate/tests/src/test074_kill_non_interacting_particles.py +++ b/opengate/tests/src/test074_kill_non_interacting_particles.py @@ -5,23 +5,22 @@ from opengate.tests import utility from scipy.spatial.transform import Rotation import numpy as np -from anytree import Node,RenderTree +from anytree import Node, RenderTree import uproot - - - - -def test074_test(entry_data, exit_data_1,exit_data_2): - liste_ekin =[] +def test074_test(entry_data, exit_data_1, exit_data_2): + liste_ekin = [] liste_evtID = [] liste_trackID = [] evt_ID_entry_data = entry_data["EventID"] j = 0 i = 0 while i < len(evt_ID_entry_data): - if j < len(exit_data_1["EventID"]) and evt_ID_entry_data[i] == exit_data_1["EventID"][j]: + if ( + j < len(exit_data_1["EventID"]) + and evt_ID_entry_data[i] == exit_data_1["EventID"][j] + ): TID_entry = entry_data["TrackID"][i] TID_exit = exit_data_1["TrackID"][j] Ekin_entry = entry_data["KineticEnergy"][i] @@ -31,23 +30,22 @@ def test074_test(entry_data, exit_data_1,exit_data_2): liste_ekin.append(exit_data_1["KineticEnergy"][j]) liste_evtID.append(exit_data_1["EventID"][j]) liste_trackID.append(exit_data_1["TrackID"][j]) - if (j < len(exit_data_1["EventID"]) -1) and (exit_data_1["EventID"][j] == exit_data_1["EventID"][j+1]) : - i = i-1 - j+=1 - i+=1 - liste_ekin =np.asarray(liste_ekin) + if (j < len(exit_data_1["EventID"]) - 1) and ( + exit_data_1["EventID"][j] == exit_data_1["EventID"][j + 1] + ): + i = i - 1 + j += 1 + i += 1 + liste_ekin = np.asarray(liste_ekin) print("Number of tracks to kill =", len(liste_ekin)) - print("Number of killed tracks =",(len(exit_data_1["EventID"]) - len(exit_data_2["EventID"]))) - - - return len(liste_ekin) == (len(exit_data_1["EventID"]) - len(exit_data_2["EventID"])) - - - - - - + print( + "Number of killed tracks =", + (len(exit_data_1["EventID"]) - len(exit_data_2["EventID"])), + ) + return len(liste_ekin) == ( + len(exit_data_1["EventID"]) - len(exit_data_2["EventID"]) + ) if __name__ == "__main__": @@ -92,63 +90,56 @@ def test074_test(entry_data, exit_data_1,exit_data_2): world.size = [1 * m, 1 * m, 1 * m] world.material = "G4_AIR" - big_box = sim.add_volume("Box","big_box") + big_box = sim.add_volume("Box", "big_box") big_box.mother = world.name - big_box.material = 'G4_AIR' - big_box.size = [0.8*m,0.8*m,0.8*m] + big_box.material = "G4_AIR" + big_box.size = [0.8 * m, 0.8 * m, 0.8 * m] actor_box = sim.add_volume("Box", "actor_box") actor_box.mother = big_box.name - actor_box.material = 'G4_AIR' + actor_box.material = "G4_AIR" actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] - actor_box.translation = [0,0,-0.1*m] - - - + actor_box.translation = [0, 0, -0.1 * m] source = sim.add_source("GenericSource", "photon_source") source.particle = "gamma" source.position.type = "box" source.mother = world.name source.position.size = [6 * cm, 6 * cm, 6 * cm] - source.position.translation = [0,0,0.3*m] + source.position.translation = [0, 0, 0.3 * m] source.direction.type = "momentum" source.force_rotation = True # source1.direction.focus_point = [0*cm, 0*cm, -5 *cm] source.direction.momentum = [0, 0, -1] source.energy.type = "mono" source.energy.mono = 6 * MeV - source.n= 1000 - - + source.n = 1000 - tungsten_leaves =sim.add_volume("Box","tungsten_leaves") + tungsten_leaves = sim.add_volume("Box", "tungsten_leaves") tungsten_leaves.mother = actor_box - tungsten_leaves.size = [0.6*m,0.6*m,0.3*cm] + tungsten_leaves.size = [0.6 * m, 0.6 * m, 0.3 * cm] tungsten_leaves.material = "Tungsten" liste_translation_W = [] - for i in range(7) : - liste_translation_W.append([0,0,0.25*m - i*6*cm]) + for i in range(7): + liste_translation_W.append([0, 0, 0.25 * m - i * 6 * cm]) tungsten_leaves.translation = liste_translation_W - tungsten_leaves.color = [0.9,0.,0.4,0.8] + tungsten_leaves.color = [0.9, 0.0, 0.4, 0.8] - - kill_No_int_act = sim.add_actor("KillNonInteractingParticleActor","killact") + kill_No_int_act = sim.add_actor("KillNonInteractingParticleActor", "killact") kill_No_int_act.mother = actor_box.name - - entry_phase_space = sim.add_volume("Box","entry_phase_space") + entry_phase_space = sim.add_volume("Box", "entry_phase_space") entry_phase_space.mother = big_box - entry_phase_space.size = [0.8*m,0.8*m,1*nm] + entry_phase_space.size = [0.8 * m, 0.8 * m, 1 * nm] entry_phase_space.material = "G4_AIR" - entry_phase_space.translation = [0,0,0.21*m] - entry_phase_space.color = [0.5,0.9,0.3,1] + entry_phase_space.translation = [0, 0, 0.21 * m] + entry_phase_space.color = [0.5, 0.9, 0.3, 1] - exit_phase_space_1 = sim.add_volume("Box","exit_phase_space_1") + exit_phase_space_1 = sim.add_volume("Box", "exit_phase_space_1") exit_phase_space_1.mother = actor_box exit_phase_space_1.size = [0.6 * m, 0.6 * m, 1 * nm] exit_phase_space_1.material = "G4_AIR" - exit_phase_space_1.translation = [0, 0, -0.3 * m + 1*nm] + exit_phase_space_1.translation = [0, 0, -0.3 * m + 1 * nm] exit_phase_space_1.color = [0.5, 0.9, 0.3, 1] exit_phase_space_2 = sim.add_volume("Box", "exit_phase_space_2") @@ -159,38 +150,58 @@ def test074_test(entry_data, exit_data_1,exit_data_2): exit_phase_space_2.color = [0.5, 0.9, 0.3, 1] # print(sim.volume_manager.dump_volume_tree()) - liste_phase_space_name = [entry_phase_space.name,exit_phase_space_1.name,exit_phase_space_2.name] + liste_phase_space_name = [ + entry_phase_space.name, + exit_phase_space_1.name, + exit_phase_space_2.name, + ] for name in liste_phase_space_name: - phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_"+name) + phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_" + name) phsp.mother = name - phsp.attributes = ["EventID","TrackID","KineticEnergy"] - name_phsp = "test074_" +name+".root" + phsp.attributes = ["EventID", "TrackID", "KineticEnergy"] + name_phsp = "test074_" + name + ".root" phsp.output = output_path / name_phsp - sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" sim.physics_manager.enable_decay = False sim.physics_manager.global_production_cuts.gamma = 1 * mm sim.physics_manager.global_production_cuts.electron = 1 * mm sim.physics_manager.global_production_cuts.positron = 1 * mm - s = sim.add_actor("SimulationStatisticsActor", "Stats") s.track_types_flag = True - # go ! sim.run() output = sim.output stats = sim.output.get_actor("Stats") print(stats) - - entry_phsp = uproot.open(str(output_path) + "/test074_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) - exit_phase_space_1 = uproot.open(str(output_path) + "/test074_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) - exit_phase_space_2 = uproot.open( str(output_path) + "/test074_" + liste_phase_space_name[2] + ".root" + ":PhaseSpace_" + liste_phase_space_name[2]) - + entry_phsp = uproot.open( + str(output_path) + + "/test074_" + + liste_phase_space_name[0] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[0] + ) + exit_phase_space_1 = uproot.open( + str(output_path) + + "/test074_" + + liste_phase_space_name[1] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[1] + ) + exit_phase_space_2 = uproot.open( + str(output_path) + + "/test074_" + + liste_phase_space_name[2] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[2] + ) df_entry = entry_phsp.arrays() df_exit_1 = exit_phase_space_1.arrays() @@ -198,4 +209,4 @@ def test074_test(entry_data, exit_data_1,exit_data_2): is_ok = test074_test(df_entry, df_exit_1, df_exit_2) - utility.test_ok(is_ok) \ No newline at end of file + utility.test_ok(is_ok) diff --git a/opengate/tests/src/test075_geometrical_splitting_volume_surface.py b/opengate/tests/src/test075_geometrical_splitting_volume_surface.py index 44a22ecc7..58af57f28 100644 --- a/opengate/tests/src/test075_geometrical_splitting_volume_surface.py +++ b/opengate/tests/src/test075_geometrical_splitting_volume_surface.py @@ -10,7 +10,9 @@ def test075(entry_data, exit_data, splitting_factor): - splitted_particle_data_entry = entry_data[entry_data["TrackCreatorProcess"] == "none"] + splitted_particle_data_entry = entry_data[ + entry_data["TrackCreatorProcess"] == "none" + ] splitted_particle_data_exit = exit_data[exit_data["TrackCreatorProcess"] == "none"] @@ -130,8 +132,14 @@ def test075(entry_data, exit_data, splitting_factor): phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_" + name) phsp.mother = name - phsp.attributes = ["EventID","TrackID","Weight","PDGCode","TrackCreatorProcess"] - name_phsp = "test075_" +name+".root" + phsp.attributes = [ + "EventID", + "TrackID", + "Weight", + "PDGCode", + "TrackCreatorProcess", + ] + name_phsp = "test075_" + name + ".root" phsp.output = output_path / name_phsp sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" @@ -150,14 +158,28 @@ def test075(entry_data, exit_data, splitting_factor): print(stats) # - entry_phsp = uproot.open(str(output_path) + "/test075_" + liste_phase_space_name[0] + ".root" +":PhaseSpace_" + liste_phase_space_name[0]) - exit_phase_space = uproot.open(str(output_path) + "/test075_" + liste_phase_space_name[1] + ".root" + ":PhaseSpace_" + liste_phase_space_name[1]) + entry_phsp = uproot.open( + str(output_path) + + "/test075_" + + liste_phase_space_name[0] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[0] + ) + exit_phase_space = uproot.open( + str(output_path) + + "/test075_" + + liste_phase_space_name[1] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[1] + ) # df_entry = entry_phsp.arrays() df_exit = exit_phase_space.arrays() # - is_ok = test075(df_entry, df_exit,geom_splitting.splitting_factor) - + is_ok = test075(df_entry, df_exit, geom_splitting.splitting_factor) + utility.test_ok(is_ok) From 9447e826120e9f973da17f8ba319dfeacabe3272 Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 14 May 2024 15:11:29 +0200 Subject: [PATCH 18/82] Actor killing a particle at the volume exit if interaction --- core/opengate_core/opengate_core.cpp | 3 + .../GateKillInteractingParticleActor.cpp | 70 +++++++++++++++++++ .../GateKillInteractingParticleActor.h | 41 +++++++++++ .../pyGateKillInteractingParticleActor.cpp | 22 ++++++ opengate/actors/actorbuilders.py | 2 + opengate/actors/miscactors.py | 30 ++++++++ opengate/managers.py | 3 + 7 files changed, 171 insertions(+) create mode 100644 core/opengate_core/opengate_lib/GateKillInteractingParticleActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateKillInteractingParticleActor.h create mode 100644 core/opengate_core/opengate_lib/pyGateKillInteractingParticleActor.cpp diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index 1952ba68e..972eca4d3 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -294,6 +294,8 @@ void init_GateARFTrainingDatasetActor(py::module &m); void init_GateKillActor(py::module &); +void init_GateKillInteractingParticleActor(py::module &); + void init_itk_image(py::module &); void init_GateImageNestedParameterisation(py::module &); @@ -561,6 +563,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GateARFActor(m); init_GateARFTrainingDatasetActor(m); init_GateKillActor(m); + init_GateKillInteractingParticleActor(m); init_GateDigiAttributeManager(m); init_GateVDigiAttribute(m); init_GateExceptionHandler(m); diff --git a/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.cpp new file mode 100644 index 000000000..b6b4b4ba9 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.cpp @@ -0,0 +1,70 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ------------------------------------ -------------- */ + +#include "GateKillInteractingParticleActor.h" +#include "G4LogicalVolumeStore.hh" +#include "G4PhysicalVolumeStore.hh" +#include "G4ios.hh" +#include "GateHelpers.h" +#include "GateHelpersDict.h" + +GateKillInteractingParticleActor::GateKillInteractingParticleActor( + py::dict &user_info) + : GateVActor(user_info, false) { + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("PreUserTrackingAction"); +} + +void GateKillInteractingParticleActor::ActorInitialize() {} + +void GateKillInteractingParticleActor::StartSimulationAction() { + fNbOfKilledParticles = 0; +} + +void GateKillInteractingParticleActor::PreUserTrackingAction( + const G4Track *track) { + fIsFirstStep = true; +} + +void GateKillInteractingParticleActor::SteppingAction(G4Step *step) { + + G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance() + ->GetVolume(fMotherVolumeName) + ->GetName(); + G4String physicalVolumeNamePreStep = + step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); + if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != + logNameMotherVolume) && + (fIsFirstStep)) { + if ((physicalVolumeNamePreStep == fMotherVolumeName) && + (step->GetPreStepPoint()->GetStepStatus() == 1)) { + fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); + ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); + } + } + + G4String logicalVolumeNamePostStep = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + + if (step->GetPostStepPoint()->GetStepStatus() == 1){ + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), + logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { + if ((step->GetTrack()->GetTrackID() != ftrackIDAtTheEntrance) || + (step->GetPostStepPoint()->GetKineticEnergy() != fKineticEnergyAtTheEntrance)) { + auto track = step->GetTrack(); + track->SetTrackStatus(fStopAndKill); + fNbOfKilledParticles++; + } + fKineticEnergyAtTheEntrance = 0; + ftrackIDAtTheEntrance = 0; + } + } + fIsFirstStep = false; +} diff --git a/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.h b/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.h new file mode 100644 index 000000000..9f0b003e4 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.h @@ -0,0 +1,41 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#ifndef GateKillInteractingParticleActor_h +#define GateKillInteractingParticleActor_h + +#include "G4Cache.hh" +#include "GateVActor.h" +#include + +namespace py = pybind11; + +class GateKillInteractingParticleActor : public GateVActor { + +public: + // Constructor + GateKillInteractingParticleActor(py::dict &user_info); + + void ActorInitialize() override; + + void StartSimulationAction() override; + + // Main function called every step in attached volume + void SteppingAction(G4Step *) override; + + void PreUserTrackingAction(const G4Track *) override; + + std::vector fParticlesTypeToKill; + G4double fKineticEnergyAtTheEntrance = 0; + G4int ftrackIDAtTheEntrance = 0; + G4bool fIsFirstStep = true; + std::vector fListOfVolumeAncestor; + + long fNbOfKilledParticles{}; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/pyGateKillInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/pyGateKillInteractingParticleActor.cpp new file mode 100644 index 000000000..fa089c4ef --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateKillInteractingParticleActor.cpp @@ -0,0 +1,22 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include + +namespace py = pybind11; + +#include "GateKillInteractingParticleActor.h" + +void init_GateKillInteractingParticleActor(py::module &m) { + py::class_, + GateVActor>(m, "GateKillInteractingParticleActor") + .def_readwrite( + "fListOfVolumeAncestor", + &GateKillInteractingParticleActor::fListOfVolumeAncestor) + .def(py::init()); +} diff --git a/opengate/actors/actorbuilders.py b/opengate/actors/actorbuilders.py index 8b6c2f697..ab0e4537e 100644 --- a/opengate/actors/actorbuilders.py +++ b/opengate/actors/actorbuilders.py @@ -17,6 +17,7 @@ SourceInfoActor, TestActor, KillActor, + KillInteractingParticleActor, BremSplittingActor, ComptSplittingActor, ) @@ -44,6 +45,7 @@ ARFTrainingDatasetActor, TestActor, KillActor, + KillInteractingParticleActor, BremSplittingActor, ComptSplittingActor, DynamicGeometryActor, diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 419082e6f..7124b10fc 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -6,6 +6,7 @@ import time import platform import opengate_core as g4 +from anytree import Node, RenderTree from .base import ActorBase from ..exception import fatal from ..geometry.utility import rot_np_as_g4, vec_np_as_g4, vec_g4_as_np @@ -429,6 +430,35 @@ def __init__(self, user_info): g4.GateKillActor.__init__(self, user_info.__dict__) +class KillInteractingParticleActor( + g4.GateKillInteractingParticleActor, ActorBase +): + type_name = "KillInteractingParticleActor" + + def set_default_user_info(user_info): + ActorBase.set_default_user_info(user_info) + user_info.list_of_volume_name = [] + + def __init__(self, user_info): + ActorBase.__init__(self, user_info) + g4.GateKillInteractingParticleActor.__init__(self, user_info.__dict__) + self.list_of_volume_name = user_info.list_of_volume_name + self.user_info.mother = user_info.mother + + def initialize(self, volume_engine=None): + + super().initialize(volume_engine) + volume_tree = self.simulation.volume_manager.get_volume_tree() + dico_of_volume_tree = {} + for pre, _, node in RenderTree(volume_tree): + dico_of_volume_tree[str(node.name)] = node + volume_name = self.user_info.mother + while volume_name != "world": + node = dico_of_volume_tree[volume_name] + volume_name = node.mother + self.list_of_volume_name.append(volume_name) + self.fListOfVolumeAncestor = self.list_of_volume_name + """ class ComptonSplittingActor(g4.GateComptonSplittingActor,ActorBase): type_name = "ComptonSplittingActor" diff --git a/opengate/managers.py b/opengate/managers.py index 738388e42..208c8d895 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -1033,6 +1033,9 @@ def dump_volume_types(self): def dump_material_database_names(self): return list(self.material_database.filenames) + + def get_volume_tree(self): + return (self.volume_tree_root) def setter_hook_verbose_level(self, verbose_level): From c1ebb7220b9e54f583c6568364b417a399ea2bfd Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 14 May 2024 15:12:05 +0200 Subject: [PATCH 19/82] Add a test to verify the killing actor --- .../src/test077_kill_interacting_particles.py | 216 ++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 opengate/tests/src/test077_kill_interacting_particles.py diff --git a/opengate/tests/src/test077_kill_interacting_particles.py b/opengate/tests/src/test077_kill_interacting_particles.py new file mode 100644 index 000000000..b36776d8f --- /dev/null +++ b/opengate/tests/src/test077_kill_interacting_particles.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +from opengate.tests import utility +from scipy.spatial.transform import Rotation +import numpy as np +from anytree import Node, RenderTree +import uproot + + +def test077_test(entry_data, exit_data_1, exit_data_2): + + + + liste_ekin = [] + liste_evtID = [] + liste_trackID = [] + evt_ID_entry_data = entry_data["EventID"] + j = 0 + i = 0 + while i < len(evt_ID_entry_data): + if ( + j < len(exit_data_1["EventID"]) + and evt_ID_entry_data[i] == exit_data_1["EventID"][j] + ): + TID_entry = entry_data["TrackID"][i] + TID_exit = exit_data_1["TrackID"][j] + Ekin_entry = entry_data["KineticEnergy"][i] + Ekin_exit = exit_data_1["KineticEnergy"][j] + + + if (TID_entry != TID_exit) or (Ekin_exit != Ekin_entry): + liste_ekin.append(exit_data_1["KineticEnergy"][j]) + liste_evtID.append(exit_data_1["EventID"][j]) + liste_trackID.append(exit_data_1["TrackID"][j]) + if (j < len(exit_data_1["EventID"]) - 1) and ( + exit_data_1["EventID"][j] == exit_data_1["EventID"][j + 1] + ): + i = i - 1 + j += 1 + i += 1 + liste_ekin = np.asarray(liste_ekin) + print("Number of tracks to kill =", len(liste_ekin)) + print( + "Number of killed tracks =", + (len(exit_data_1["EventID"]) - len(exit_data_2["EventID"])), + ) + + return len(liste_ekin) == ( + len(exit_data_1["EventID"]) - len(exit_data_2["EventID"]) + ) + + +if __name__ == "__main__": + paths = utility.get_default_test_paths(__file__) + output_path = paths.output + + print(output_path) + # create the simulation + sim = gate.Simulation() + + # main options + ui = sim.user_info + ui.g4_verbose = False + # ui.visu = True + ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.logger.EVENT + ui.number_of_threads = 1 + ui.random_seed = "auto" + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + sec = gate.g4_units.s + gcm3 = gate.g4_units["g/cm3"] + + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + + # adapt world size + world = sim.world + world.size = [1 * m, 1 * m, 1 * m] + world.material = "G4_AIR" + + big_box = sim.add_volume("Box", "big_box") + big_box.mother = world.name + big_box.material = "G4_AIR" + big_box.size = [0.8 * m, 0.8 * m, 0.8 * m] + + actor_box = sim.add_volume("Box", "actor_box") + actor_box.mother = big_box.name + actor_box.material = "G4_AIR" + actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] + actor_box.translation = [0, 0, -0.1 * m] + + source = sim.add_source("GenericSource", "photon_source") + source.particle = "gamma" + source.position.type = "box" + source.mother = world.name + source.position.size = [6 * cm, 6 * cm, 6 * cm] + source.position.translation = [0, 0, 0.3 * m] + source.direction.type = "momentum" + source.force_rotation = True + # source1.direction.focus_point = [0*cm, 0*cm, -5 *cm] + source.direction.momentum = [0, 0, -1] + source.energy.type = "mono" + source.energy.mono = 6 * MeV + source.n = 1000 + + tungsten_leaves = sim.add_volume("Box", "tungsten_leaves") + tungsten_leaves.mother = actor_box + tungsten_leaves.size = [0.6 * m, 0.6 * m, 0.3 * cm] + tungsten_leaves.material = "Tungsten" + liste_translation_W = [] + for i in range(7): + liste_translation_W.append([0, 0, 0.25 * m - i * 6 * cm]) + tungsten_leaves.translation = liste_translation_W + tungsten_leaves.color = [0.9, 0.0, 0.4, 0.8] + + kill_int_act = sim.add_actor("KillInteractingParticleActor", "killact") + kill_int_act.mother = actor_box.name + + entry_phase_space = sim.add_volume("Box", "entry_phase_space") + entry_phase_space.mother = big_box + entry_phase_space.size = [0.8 * m, 0.8 * m, 1 * nm] + entry_phase_space.material = "G4_AIR" + entry_phase_space.translation = [0, 0, 0.21 * m] + entry_phase_space.color = [0.5, 0.9, 0.3, 1] + + exit_phase_space_1 = sim.add_volume("Box", "exit_phase_space_1") + exit_phase_space_1.mother = actor_box + exit_phase_space_1.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space_1.material = "G4_AIR" + exit_phase_space_1.translation = [0, 0, -0.3 * m + 1 * nm] + exit_phase_space_1.color = [0.5, 0.9, 0.3, 1] + + exit_phase_space_2 = sim.add_volume("Box", "exit_phase_space_2") + exit_phase_space_2.mother = world.name + exit_phase_space_2.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space_2.material = "G4_AIR" + exit_phase_space_2.translation = [0, 0, -0.4 * m - 1 * nm] + exit_phase_space_2.color = [0.5, 0.9, 0.3, 1] + + # print(sim.volume_manager.dump_volume_tree()) + liste_phase_space_name = [ + entry_phase_space.name, + exit_phase_space_1.name, + exit_phase_space_2.name, + ] + for name in liste_phase_space_name: + + phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_" + name) + phsp.mother = name + phsp.attributes = ["EventID", "TrackID", "KineticEnergy"] + name_phsp = "test077_" + name + ".root" + phsp.output = output_path / name_phsp + + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + sim.physics_manager.enable_decay = False + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * mm + sim.physics_manager.global_production_cuts.positron = 1 * mm + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + + # go ! + sim.run() + output = sim.output + stats = sim.output.get_actor("Stats") + print(stats) + + entry_phsp = uproot.open( + str(output_path) + + "/test077_" + + liste_phase_space_name[0] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[0] + ) + exit_phase_space_1 = uproot.open( + str(output_path) + + "/test077_" + + liste_phase_space_name[1] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[1] + ) + exit_phase_space_2 = uproot.open( + str(output_path) + + "/test077_" + + liste_phase_space_name[2] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[2] + ) + + df_entry = entry_phsp.arrays() + df_exit_1 = exit_phase_space_1.arrays() + df_exit_2 = exit_phase_space_2.arrays() + + is_ok = test077_test(df_entry, df_exit_1, df_exit_2) + + utility.test_ok(is_ok) From 77d16f5f65bf1a130282c011eed6297a844f21c5 Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 24 May 2024 13:54:24 +0200 Subject: [PATCH 20/82] Actor creation which split an exiting particle at its last vertex --- core/opengate_core/opengate_core.cpp | 6 +- ...ptrLastVertexInteractionSplittingActor.cpp | 242 ++++++++++++++++++ ...eOptrLastVertexInteractionSplittingActor.h | 122 +++++++++ ...ptrLastVertexInteractionSplittingActor.cpp | 22 ++ opengate/actors/actorbuilders.py | 2 + opengate/actors/miscactors.py | 52 +++- opengate/managers.py | 6 +- 7 files changed, 437 insertions(+), 15 deletions(-) create mode 100644 core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h create mode 100644 core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index bff200f36..bb7cf5b17 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -336,10 +336,10 @@ void init_GateSimulationStatisticsActor(py::module &); void init_GatePhaseSpaceActor(py::module &); -// void init_GateComptonSplittingActor(py::module &); - void init_GateOptrComptSplittingActor(py::module &m); +void init_GateOptrLastVertexInteractionSplittingActor(py::module &m); + void init_GateBOptrBremSplittingActor(py::module &m); void init_G4VBiasingOperator(py::module &m); @@ -558,9 +558,9 @@ PYBIND11_MODULE(opengate_core, m) { init_GateLETActor(m); init_GateSimulationStatisticsActor(m); init_GatePhaseSpaceActor(m); - // init_GateComptonSplittingActor(m); init_GateBOptrBremSplittingActor(m); init_GateOptrComptSplittingActor(m); + init_GateOptrLastVertexInteractionSplittingActor(m); init_GateHitsCollectionActor(m); init_GateMotionVolumeActor(m); init_GateHitsAdderActor(m); diff --git a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp new file mode 100644 index 000000000..6181d6dd8 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp @@ -0,0 +1,242 @@ +// +// ******************************************************************** +// * 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 GateOptrLastVertexInteractionSplittingActor.cc +/// \brief Implementation of the GateOptrLastVertexInteractionSplittingActor class + +#include "GateHelpersDict.h" +#include "GateHelpersImage.h" + +#include "CLHEP/Units/SystemOfUnits.h" +#include "G4BiasingProcessInterface.hh" +#include "G4Gamma.hh" +#include "G4LogicalVolumeStore.hh" +#include "G4ParticleTable.hh" +#include "G4PhysicalVolumeStore.hh" +#include "G4ProcessManager.hh" +#include "G4TrackingManager.hh" +#include "G4ProcessVector.hh" +#include "GateOptnComptSplitting.h" +#include "GateOptrLastVertexInteractionSplittingActor.h" +#include "G4ParticleChangeForGamma.hh" +#include "G4VParticleChange.hh" + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptrLastVertexInteractionSplittingActor::GateOptrLastVertexInteractionSplittingActor(py::dict &user_info) + : G4VBiasingOperator("LastVertexInteractionOperator"), + GateVActor(user_info, false) { + fMotherVolumeName = DictGetStr(user_info, "mother"); + fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); + fWeightThreshold = DictGetDouble(user_info, "weight_threshold"); + fMinWeightOfParticle = DictGetDouble(user_info, "min_weight_of_particle"); + // Since the russian roulette uses as a probablity 1/splitting, we need to + // have a double, but the splitting factor provided by the user is logically + // an int, so we need to change the type. + fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); + fBiasPrimaryOnly = DictGetBool(user_info, "bias_primary_only"); + fBiasOnlyOnce = DictGetBool(user_info, "bias_only_once"); + fRussianRoulette = DictGetBool(user_info, "russian_roulette"); + fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); + fMaxTheta = DictGetDouble(user_info, "max_theta"); + fComptSplittingOperation = + new GateOptnComptSplitting("ComptSplittingOperation"); + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("BeginOfEventAction"); +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +void GateOptrLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* CurrentStep,G4Track track,G4Step step,G4VProcess* process,G4int splittingFactor){ +G4TrackVector *trackVector = CurrentStep->GetfSecondary(); +std::cout<PostStepDoIt(track,step); + + //Add of splitted primaries + G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; + const G4ThreeVector momentum =gammaProcessFinalState->GetProposedMomentumDirection(); + G4double energy = gammaProcessFinalState->GetProposedKineticEnergy(); + G4double globalTime = track.GetGlobalTime(); + G4double gammaWeight = track.GetWeight(); + const G4ThreeVector position = step.GetPostStepPoint()->GetPosition(); + G4Track *newTrack = new G4Track(track); + newTrack->SetWeight(gammaWeight); + newTrack->SetKineticEnergy(energy); + newTrack->SetMomentumDirection(momentum); + newTrack->SetPosition(position); + trackVector->push_back(newTrack); + + + // Add of splitted secondaries (electrons) + + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for(int j=0; j GetSecondary(j); + trackVector->push_back(newTrack); + } + } +} + + +void GateOptrLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* CurrentStep,G4Track track,G4Step step,G4VProcess* process,G4int splittingFactor){ +G4TrackVector *trackVector = CurrentStep->GetfSecondary(); +for (int i =0; i PostStepDoIt(track,step); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + std::cout<GetSecondary(j); + trackVector->push_back(newTrack); + } + } +} + +void GateOptrLastVertexInteractionSplittingActor::AttachToAllLogicalDaughtersVolumes( + G4LogicalVolume *volume) { + AttachTo(volume); + G4int nbOfDaughters = volume->GetNoDaughters(); + if (nbOfDaughters > 0) { + for (int i = 0; i < nbOfDaughters; i++) { + G4LogicalVolume *logicalDaughtersVolume = + volume->GetDaughter(i)->GetLogicalVolume(); + AttachToAllLogicalDaughtersVolumes(logicalDaughtersVolume); + } + } +} + +void GateOptrLastVertexInteractionSplittingActor::StartSimulationAction() { + G4LogicalVolume *biasingVolume = + G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + + // Here we need to attach all the daughters and daughters of daughters (...) + // to the biasing operator. To do that, I use the function + // AttachAllLogicalDaughtersVolumes. + AttachToAllLogicalDaughtersVolumes(biasingVolume); + fComptSplittingOperation->SetSplittingFactor(fSplittingFactor); + fComptSplittingOperation->SetWeightThreshold(fWeightThreshold); + fComptSplittingOperation->SetMaxTheta(fMaxTheta); + fComptSplittingOperation->SetRussianRoulette(fRussianRoulette); + fComptSplittingOperation->SetMinWeightOfParticle(fMinWeightOfParticle); +} + +void GateOptrLastVertexInteractionSplittingActor::StartRun() { + + // The way to behave of the russian roulette is the following : + // we provide a vector director and the theta angle acceptance, where theta = + // 0 is a vector colinear to the vector director Then if the track generated + // is on the acceptance angle, we add it to the primary track, and if it's not + // the case, we launch the russian roulette + if (fRotationVectorDirector) { + G4VPhysicalVolume *physBiasingVolume = + G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume->GetObjectRotationValue(); + fVectorDirector = rot * fVectorDirector; + } + + fComptSplittingOperation->SetVectorDirector(fVectorDirector); +} + +void GateOptrLastVertexInteractionSplittingActor::StartTracking(const G4Track *track) { + fNInteractions = 0; +} + + +void GateOptrLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4Step* step){ + G4String processName = "None"; + if (step->GetPostStepPoint()->GetProcessDefinedStep() !=0){ + processName = step->GetPostStepPoint()->GetProcessDefinedStep()-> GetProcessName(); + } + if ((std::find(fListOfProcesses.begin(),fListOfProcesses.end(),processName) != fListOfProcesses.end())){ + fTrackInformation = G4Track(*(step->GetTrack())); + fStepInformation = G4Step(*step); + fTrackInformation.SetKineticEnergy(fStepInformation.GetPreStepPoint()->GetKineticEnergy()); + fTrackInformation.SetMomentumDirection(fStepInformation.GetPreStepPoint()->GetMomentumDirection()); + fTrackInformation.SetTrackStatus(fAlive); + fTrackInformation.SetPolarization(fStepInformation.GetPreStepPoint()->GetPolarization()); + fProcessToSplit = processName; + fIDOfSplittedTrack = step->GetTrack()->GetTrackID(); + } + +} + +void GateOptrLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* CurrentStep,G4Track track,G4Step step,G4String processName){ + G4ParticleDefinition* particleDefinition = track.GetDefinition(); + G4ProcessManager* processManager = particleDefinition->GetProcessManager(); + G4ProcessVector* processList = processManager->GetProcessList(); + auto processToSplit = (*processList)[0]; + for (size_t i = 0; i size(); ++i) { + auto process = (*processList)[i]; + if (process->GetProcessName() == processName){ + processToSplit = process; + } + } + +if (processName == "compt"){ + ComptonSplitting(CurrentStep,track,step,processToSplit,10); +} +else { + std::cout<GetProcessName()<GetTrack()->GetTrackID(); + if ((fIsSplitted == true) && ( trackID == fIDOfSplittedTrack - 1)) + fIsSplitted = false; + + if (fIsSplitted == false){ + RememberLastProcessInformation(step); + G4String logicalVolumeNamePostStep = "None"; + if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(),logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(),fProcessToSplit) != fListOfProcesses.end()){ + CreateNewParticleAtTheLastVertex(step,fTrackInformation,fStepInformation,fProcessToSplit); + fIsSplitted = true; + fIDOfSplittedTrack = trackID; + fProcessToSplit = "None"; + } + } + } +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + + + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h new file mode 100644 index 000000000..f455598d9 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h @@ -0,0 +1,122 @@ +// +// ******************************************************************** +// * 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 GateOptrLastVertexInteractionSplittingActor.h +/// \brief Definition of the GateOptrLastVertexInteractionSplittingActor class +#ifndef GateOptrLastVertexInteractionSplittingActor_h +#define GateOptrLastVertexInteractionSplittingActor_h 1 + +#include "G4VBiasingOperator.hh" + +#include "GateVActor.h" +#include +#include +namespace py = pybind11; + +class GateOptnComptSplitting; + +class GateOptrLastVertexInteractionSplittingActor : public G4VBiasingOperator, + public GateVActor { +public: + GateOptrLastVertexInteractionSplittingActor(py::dict &user_info); + virtual ~GateOptrLastVertexInteractionSplittingActor() {} + +public: + // ------------------------- + // Optional from base class: + // ------------------------- + // -- Call at run start: + // virtual void BeginOfRunAction(const G4Run *run); + + // virtual void SteppingAction(G4Step* step); + + // -- Call at each track starting: + // virtual void PreUserTrackingAction( const G4Track* track ); + + G4double fSplittingFactor; + G4double fMinWeightOfParticle; + G4double fWeightThreshold; + G4bool fBiasPrimaryOnly; + G4bool fBiasOnlyOnce; + G4int fNInteractions = 0; + G4bool fRussianRoulette; + G4bool fRotationVectorDirector; + G4ThreeVector fVectorDirector; + G4double fMaxTheta; + G4Track fTrackInformation; + G4Step fStepInformation; + G4String fProcessToSplit = "None"; + G4int fIDOfSplittedTrack = 0; + G4bool fIsSplitted = false; + std::vector fListOfVolumeAncestor; + + std::vector fListOfProcesses = {"compt","conv","eBrem"}; + // Unused but mandatory + + virtual void StartSimulationAction(); + virtual void SteppingAction(G4Step *) override; + virtual void BeginOfEventAction(const G4Event *) override; + virtual void StartRun(); + virtual void StartTracking(const G4Track *); + virtual void EndTracking() {} + +protected: + // ----------------------------- + // -- Mandatory from base class: + // ----------------------------- + // -- Unused: + void AttachToAllLogicalDaughtersVolumes(G4LogicalVolume *); + void ComptonSplitting(G4Step* CurrentStep,G4Track track,G4Step step,G4VProcess* process,G4int splittingFactor); + void SecondariesSplitting(G4Step* CurrentStep,G4Track track,G4Step step,G4VProcess* process,G4int splittingFactor); + void RememberLastProcessInformation(G4Step*); + void CreateNewParticleAtTheLastVertex(G4Step*,G4Track,G4Step,G4String); + virtual G4VBiasingOperation *ProposeNonPhysicsBiasingOperation( + const G4Track * /* track */, + const G4BiasingProcessInterface * /* callingProcess */) { + return 0; + } + virtual G4VBiasingOperation *ProposeOccurenceBiasingOperation( + const G4Track * /* track */, + const G4BiasingProcessInterface * /* callingProcess */) { + return 0; + } + + // -- Used: + virtual G4VBiasingOperation *ProposeFinalStateBiasingOperation( + const G4Track *track, const G4BiasingProcessInterface *callingProcess){ + return 0; + } + +private: + // -- Avoid compiler complaining for (wrong) method shadowing, + // -- this is because other virtual method with same name exists. + using G4VBiasingOperator::OperationApplied; + +private: + GateOptnComptSplitting *fComptSplittingOperation; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp new file mode 100644 index 000000000..8fc59fa3b --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp @@ -0,0 +1,22 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ +#include + +namespace py = pybind11; +#include "G4VBiasingOperator.hh" +#include "GateOptrLastVertexInteractionSplittingActor.h" + +void init_GateOptrLastVertexInteractionSplittingActor(py::module &m) { + + py::class_>( + m, "GateOptrLastVertexInteractionSplittingActor") + .def_readwrite( + "fListOfVolumeAncestor", + &GateOptrLastVertexInteractionSplittingActor::fListOfVolumeAncestor) + .def(py::init()); +} diff --git a/opengate/actors/actorbuilders.py b/opengate/actors/actorbuilders.py index 8b6c2f697..1f3cd7089 100644 --- a/opengate/actors/actorbuilders.py +++ b/opengate/actors/actorbuilders.py @@ -19,6 +19,7 @@ KillActor, BremSplittingActor, ComptSplittingActor, + LastVertexInteractionSplittingActor, ) from .dynamicactors import DynamicGeometryActor from ..utility import make_builders @@ -46,6 +47,7 @@ KillActor, BremSplittingActor, ComptSplittingActor, + LastVertexInteractionSplittingActor, DynamicGeometryActor, } actor_builders = make_builders(actor_type_names) diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 419082e6f..bad189dd5 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -5,6 +5,7 @@ import numpy as np import time import platform +from anytree import Node, RenderTree import opengate_core as g4 from .base import ActorBase from ..exception import fatal @@ -429,17 +430,6 @@ def __init__(self, user_info): g4.GateKillActor.__init__(self, user_info.__dict__) -""" -class ComptonSplittingActor(g4.GateComptonSplittingActor,ActorBase): - type_name = "ComptonSplittingActor" - def set_default_user_info(user_info): - ActorBase.set_default_user_info(user_info) - user_info.SplittingFactor = 0 - - def __init__(self, user_info): - ActorBase.__init__(self, user_info) - g4.GateComptonSplittingActor.__init__(self, user_info.__dict__) -""" class ComptSplittingActor(g4.GateOptrComptSplittingActor, ActorBase): @@ -464,6 +454,46 @@ def __init__(self, user_info): g4.GateOptrComptSplittingActor.__init__(self, user_info.__dict__) +class LastVertexInteractionSplittingActor(g4.GateOptrLastVertexInteractionSplittingActor, ActorBase): + type_name = "LastVertexInteractionSplittingActor" + + def set_default_user_info(user_info): + ActorBase.set_default_user_info(user_info) + deg = g4_units.deg + user_info.splitting_factor = 1 + user_info.weight_threshold = 0 + user_info.bias_primary_only = True + user_info.min_weight_of_particle = 0 + user_info.bias_only_once = True + user_info.processes = ["phot"] + user_info.russian_roulette = False + user_info.rotation_vector_director = False + user_info.vector_director = [0, 0, 1] + user_info.max_theta = 90 * deg + user_info.list_of_volume_name = [] + + def __init__(self, user_info): + ActorBase.__init__(self, user_info) + g4.GateOptrLastVertexInteractionSplittingActor.__init__(self, user_info.__dict__) + self.list_of_volume_name = user_info.list_of_volume_name + self.user_info.mother = user_info.mother + + def initialize(self, volume_engine=None): + + super().initialize(volume_engine) + volume_tree = self.simulation.volume_manager.get_volume_tree() + dico_of_volume_tree = {} + for pre, _, node in RenderTree(volume_tree): + dico_of_volume_tree[str(node.name)] = node + volume_name = self.user_info.mother + while volume_name != "world": + node = dico_of_volume_tree[volume_name] + volume_name = node.mother + self.list_of_volume_name.append(volume_name) + self.fListOfVolumeAncestor = self.list_of_volume_name + print(self.fListOfVolumeAncestor) + + class BremSplittingActor(g4.GateBOptrBremSplittingActor, ActorBase): type_name = "BremSplittingActor" diff --git a/opengate/managers.py b/opengate/managers.py index d2f8620b4..20cc159ab 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -1033,7 +1033,11 @@ def dump_volume_types(self): for vt in self.volume_types: s += f"{vt} " return s - + + def get_volume_tree(self): + return self.volume_tree_root + + def dump_material_database_names(self): return list(self.material_database.filenames) From f81bbbbb51cf45bed9c082af1b7346c953043413 Mon Sep 17 00:00:00 2001 From: majacquet Date: Thu, 13 Jun 2024 10:17:27 +0200 Subject: [PATCH 21/82] improvement of the method, adding a unbiased splitting method for the pair production --- ...ptrLastVertexInteractionSplittingActor.cpp | 525 ++++++++++++++---- ...eOptrLastVertexInteractionSplittingActor.h | 79 ++- ...ptrLastVertexInteractionSplittingActor.cpp | 3 +- opengate/actors/miscactors.py | 9 +- .../src/test076_last_vertex_splittting.py | 240 ++++++++ 5 files changed, 686 insertions(+), 170 deletions(-) create mode 100755 opengate/tests/src/test076_last_vertex_splittting.py diff --git a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp index 6181d6dd8..b38ed6fdf 100644 --- a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp @@ -33,6 +33,7 @@ #include "CLHEP/Units/SystemOfUnits.h" #include "G4BiasingProcessInterface.hh" #include "G4Gamma.hh" +#include "G4Positron.hh" #include "G4LogicalVolumeStore.hh" #include "G4ParticleTable.hh" #include "G4PhysicalVolumeStore.hh" @@ -41,150 +42,182 @@ #include "G4ProcessVector.hh" #include "GateOptnComptSplitting.h" #include "GateOptrLastVertexInteractionSplittingActor.h" -#include "G4ParticleChangeForGamma.hh" #include "G4VParticleChange.hh" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptrLastVertexInteractionSplittingActor::GateOptrLastVertexInteractionSplittingActor(py::dict &user_info) - : G4VBiasingOperator("LastVertexInteractionOperator"), - GateVActor(user_info, false) { +GateOptrLastVertexInteractionSplittingActor::GateOptrLastVertexInteractionSplittingActor(py::dict &user_info):GateVActor(user_info, false) { fMotherVolumeName = DictGetStr(user_info, "mother"); fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); - fWeightThreshold = DictGetDouble(user_info, "weight_threshold"); - fMinWeightOfParticle = DictGetDouble(user_info, "min_weight_of_particle"); - // Since the russian roulette uses as a probablity 1/splitting, we need to - // have a double, but the splitting factor provided by the user is logically - // an int, so we need to change the type. fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); - fBiasPrimaryOnly = DictGetBool(user_info, "bias_primary_only"); - fBiasOnlyOnce = DictGetBool(user_info, "bias_only_once"); - fRussianRoulette = DictGetBool(user_info, "russian_roulette"); + fRussianRouletteForAngle = DictGetBool(user_info, "russian_roulette_for_angle"); fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); fMaxTheta = DictGetDouble(user_info, "max_theta"); - fComptSplittingOperation = - new GateOptnComptSplitting("ComptSplittingOperation"); fActions.insert("StartSimulationAction"); fActions.insert("SteppingAction"); fActions.insert("BeginOfEventAction"); + fActions.insert("BeginOfRunAction"); + fActions.insert("PreUserTrackingAction"); + fActions.insert("PostUserTrackingAction"); } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -void GateOptrLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* CurrentStep,G4Track track,G4Step step,G4VProcess* process,G4int splittingFactor){ -G4TrackVector *trackVector = CurrentStep->GetfSecondary(); -std::cout<PostStepDoIt(track,step); - - //Add of splitted primaries - G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; - const G4ThreeVector momentum =gammaProcessFinalState->GetProposedMomentumDirection(); - G4double energy = gammaProcessFinalState->GetProposedKineticEnergy(); - G4double globalTime = track.GetGlobalTime(); - G4double gammaWeight = track.GetWeight(); - const G4ThreeVector position = step.GetPostStepPoint()->GetPosition(); - G4Track *newTrack = new G4Track(track); - newTrack->SetWeight(gammaWeight); - newTrack->SetKineticEnergy(energy); - newTrack->SetMomentumDirection(momentum); - newTrack->SetPosition(position); - trackVector->push_back(newTrack); - - // Add of splitted secondaries (electrons) - - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for(int j=0; j GetSecondary(j); - trackVector->push_back(newTrack); +G4double GateOptrLastVertexInteractionSplittingActor::RussianRouletteForAngleSurvival(G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta,G4double split) { + G4double cosTheta = vectorDirector * dir; + G4double theta = std::acos(cosTheta); + G4double weightToApply = 1; + if (theta > fMaxTheta) { + G4double probability = G4UniformRand(); + if (probability <= 1 / split) { + weightToApply = split; + } else { + weightToApply = 0; } } + return weightToApply; +} + +G4Track* GateOptrLastVertexInteractionSplittingActor:: CreateComptonTrack(G4ParticleChangeForGamma* gammaProcess,G4Track track, G4double weight){ + G4double energy = gammaProcess->GetProposedKineticEnergy(); + G4double globalTime = track.GetGlobalTime(); + G4double newGammaWeight = weight; + const G4ThreeVector momentum =gammaProcess->GetProposedMomentumDirection(); + const G4ThreeVector position = track.GetPosition(); + G4Track *newTrack = new G4Track(track); + newTrack->SetWeight(newGammaWeight); + newTrack->SetKineticEnergy(energy); + newTrack->SetMomentumDirection(momentum); + newTrack->SetPosition(position); + return newTrack; } +void GateOptrLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* CurrentStep,G4Track track,const G4Step *step,G4VProcess* process){ +//Loop on process and add the secondary tracks to the current step secondary vector + + -void GateOptrLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* CurrentStep,G4Track track,G4Step step,G4VProcess* process,G4int splittingFactor){ G4TrackVector *trackVector = CurrentStep->GetfSecondary(); -for (int i =0; i PostStepDoIt(track,step); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - std::cout<GetSecondary(j); - trackVector->push_back(newTrack); +G4double gammaWeight = CurrentStep->GetTrack()->GetWeight()/fSplittingFactor; +G4int nCall = (G4int) fSplittingFactor; +for (int i =0; i PostStepDoIt(track,*step); + G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; + const G4ThreeVector momentum =gammaProcessFinalState->GetProposedMomentumDirection(); + + if (fRussianRouletteForAngle == true) { + G4double weightToApply = RussianRouletteForAngleSurvival(momentum,fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0){ + gammaWeight = gammaWeight * weightToApply; + G4Track* newTrack = CreateComptonTrack(gammaProcessFinalState,track,gammaWeight); + trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for(int j=0; j GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } } - } -} - -void GateOptrLastVertexInteractionSplittingActor::AttachToAllLogicalDaughtersVolumes( - G4LogicalVolume *volume) { - AttachTo(volume); - G4int nbOfDaughters = volume->GetNoDaughters(); - if (nbOfDaughters > 0) { - for (int i = 0; i < nbOfDaughters; i++) { - G4LogicalVolume *logicalDaughtersVolume = - volume->GetDaughter(i)->GetLogicalVolume(); - AttachToAllLogicalDaughtersVolumes(logicalDaughtersVolume); + + else { + G4Track* newTrack = CreateComptonTrack(gammaProcessFinalState,track,gammaWeight); + trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for(int j=0; j GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + + } } } } -void GateOptrLastVertexInteractionSplittingActor::StartSimulationAction() { - G4LogicalVolume *biasingVolume = - G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); - // Here we need to attach all the daughters and daughters of daughters (...) - // to the biasing operator. To do that, I use the function - // AttachAllLogicalDaughtersVolumes. - AttachToAllLogicalDaughtersVolumes(biasingVolume); - fComptSplittingOperation->SetSplittingFactor(fSplittingFactor); - fComptSplittingOperation->SetWeightThreshold(fWeightThreshold); - fComptSplittingOperation->SetMaxTheta(fMaxTheta); - fComptSplittingOperation->SetRussianRoulette(fRussianRoulette); - fComptSplittingOperation->SetMinWeightOfParticle(fMinWeightOfParticle); -} - -void GateOptrLastVertexInteractionSplittingActor::StartRun() { +void GateOptrLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* CurrentStep,G4Track track,const G4Step *step,G4VProcess* process){ +//Loop on process and add the secondary tracks to the current step secondary vector. - // The way to behave of the russian roulette is the following : - // we provide a vector director and the theta angle acceptance, where theta = - // 0 is a vector colinear to the vector director Then if the track generated - // is on the acceptance angle, we add it to the primary track, and if it's not - // the case, we launch the russian roulette - if (fRotationVectorDirector) { - G4VPhysicalVolume *physBiasingVolume = - G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); - auto rot = physBiasingVolume->GetObjectRotationValue(); - fVectorDirector = rot * fVectorDirector; +G4TrackVector *trackVector = CurrentStep->GetfSecondary(); +G4int nCall = (G4int) fSplittingFactor; +for (int i =0; i PostStepDoIt(track,*step); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for(int j=0; j GetSecondary(j); + trackVector->push_back(newTrack); + } } - - fComptSplittingOperation->SetVectorDirector(fVectorDirector); } -void GateOptrLastVertexInteractionSplittingActor::StartTracking(const G4Track *track) { - fNInteractions = 0; -} +void GateOptrLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4Step* step){ + // When an interesting process to split occurs, we remember the status of the track and the process at this current step + // some informations regarding the track info have to be changed because they were update according to the interaction that occured + // These informations are sotcked as a map object, binding the track ID with all the track objects and processes to split. + // Because in some cases, if a secondary was created before an interaction chain, this secondary will be track after the chain and + // without this association, we wll loose the information about the process occuring for this secondary. -void GateOptrLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4Step* step){ + G4String creatorProcessName = "None"; + if (step->GetTrack()->GetCreatorProcess() !=0) + creatorProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); G4String processName = "None"; + G4int trackID = step->GetTrack()->GetTrackID(); if (step->GetPostStepPoint()->GetProcessDefinedStep() !=0){ processName = step->GetPostStepPoint()->GetProcessDefinedStep()-> GetProcessName(); } + if ((step->GetTrack()->GetParticleDefinition()->GetParticleName() == "e+") && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))){ + processName = "annihil"; + } if ((std::find(fListOfProcesses.begin(),fListOfProcesses.end(),processName) != fListOfProcesses.end())){ - fTrackInformation = G4Track(*(step->GetTrack())); - fStepInformation = G4Step(*step); - fTrackInformation.SetKineticEnergy(fStepInformation.GetPreStepPoint()->GetKineticEnergy()); - fTrackInformation.SetMomentumDirection(fStepInformation.GetPreStepPoint()->GetMomentumDirection()); - fTrackInformation.SetTrackStatus(fAlive); - fTrackInformation.SetPolarization(fStepInformation.GetPreStepPoint()->GetPolarization()); - fProcessToSplit = processName; - fIDOfSplittedTrack = step->GetTrack()->GetTrackID(); + G4Track trackInformation = G4Track(*(step->GetTrack())); + trackInformation.SetKineticEnergy(step->GetPreStepPoint()->GetKineticEnergy()); + trackInformation.SetMomentumDirection(step->GetPreStepPoint()->GetMomentumDirection()); + trackInformation.SetTrackStatus(fAlive); + trackInformation.SetPolarization(step->GetPreStepPoint()->GetPolarization()); + trackInformation.SetTrackID(trackID); + if (auto search = fRememberedTracks.find(trackID); search != fRememberedTracks.end()){ + fRememberedTracks[trackID].push_back(trackInformation); + fRememberedProcesses[trackID].push_back(processName); + ; + } + else { + fRememberedTracks[trackID] = {trackInformation}; + fRememberedProcesses[trackID] = {processName}; + + + } } + else { + + + G4int parentID = step->GetTrack()->GetParentID(); + if (auto search = fRememberedTracks.find(trackID); search == fRememberedTracks.end()){ + if (auto search = fRememberedTracks.find(parentID); search != fRememberedTracks.end()){ + if (auto it = std::find(fRememberedProcesses[parentID].begin(),fRememberedProcesses[parentID].end(),creatorProcessName); it != fRememberedProcesses[parentID].end()){ + auto idx = it- fRememberedProcesses[parentID].begin(); + fRememberedTracks[trackID] = {fRememberedTracks[parentID][idx]}; + fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][idx]}; + } + else { + fRememberedTracks[trackID] = {fRememberedTracks[parentID][0]}; + fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][0]}; + + } + } + } + } } -void GateOptrLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* CurrentStep,G4Track track,G4Step step,G4String processName){ + +void GateOptrLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* CurrentStep,G4Track track,const G4Step* step,G4String processName){ + //We retrieve the process associated to the process name to split and we split according the process. + //Since for compton scattering, the gamma is not a secondary particles, this one need to have his own splitting function. + + G4ParticleDefinition* particleDefinition = track.GetDefinition(); G4ProcessManager* processManager = particleDefinition->GetProcessManager(); G4ProcessVector* processList = processManager->GetProcessList(); @@ -195,46 +228,310 @@ void GateOptrLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVert processToSplit = process; } } + if (processName == "compt"){ + ComptonSplitting(CurrentStep,track,step,processToSplit); + } + else { + SecondariesSplitting(CurrentStep,track,step,processToSplit); + } -if (processName == "compt"){ - ComptonSplitting(CurrentStep,track,step,processToSplit,10); } -else { - std::cout<GetProcessName()<GetTrack()->GetTrackID(); +if ((fEventIDOfInitialSplittedTrack != fEventID) || ((fEventIDOfInitialSplittedTrack == fEventID ) && (trackID < fTrackIDOfInitialTrack))) + { + G4String logicalVolumeNamePreStep = "None"; + if (step->GetPreStepPoint()->GetPhysicalVolume() !=0){ + logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + } + if (((step->GetPreStepPoint()->GetStepStatus() == 1) && (logicalVolumeNamePreStep == fMotherVolumeName)) || + ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logicalVolumeNamePreStep) && (step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() !=fMotherVolumeName))) { + fTrackIDOfInitialTrack = trackID; + fEventIDOfInitialSplittedTrack = fEventID; + fRememberedProcesses.clear(); + fRememberedTracks.clear(); + + } + else if (step->GetTrack()->GetParentID() == 0){ + fTrackIDOfInitialTrack = trackID; + fEventIDOfInitialSplittedTrack = fEventID; + fRememberedProcesses.clear(); + fRememberedTracks.clear(); + } + } + } + + +void GateOptrLastVertexInteractionSplittingActor::PostponeFirstAnnihilationTrackIfInteraction(G4Step* step,G4String processName){ + //In case the first gamma issued fromannihilation undergoes an interaction, in order to not bias the process + //We keep in memory the particle post step state (with its secondaries) and kill the particle and its secondaries. + //If the second photon from annihilation exiting the collimation system with an interaction or is absorbed within + //the collimation, the particle is subsequently resimulated, starting from the interaction point. + + + G4int trackID = step->GetTrack()->GetTrackID(); + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(),processName) != fListOfProcesses.end()){ + fSuspendForAnnihil = true; + G4Track trackToPostpone = G4Track(*(step->GetTrack())); + trackToPostpone.SetKineticEnergy(step->GetPostStepPoint()->GetKineticEnergy()); + trackToPostpone.SetMomentumDirection(step->GetPostStepPoint()->GetMomentumDirection()); + trackToPostpone.SetTrackStatus(step->GetTrack()->GetTrackStatus()); + trackToPostpone.SetPolarization(step->GetPostStepPoint()->GetPolarization()); + trackToPostpone.SetPosition(step->GetPostStepPoint()->GetPosition()); + trackToPostpone.SetTrackID(trackID); + fTracksToPostpone.push_back(trackToPostpone); + auto theTrack = fTracksToPostpone[0]; + + + auto secVec = step->GetfSecondary(); + for (int i = 0; i< secVec->size();i++){ + G4Track* sec = (*secVec)[i]; + G4Track copySec = G4Track((*sec)); + fTracksToPostpone.push_back(copySec); + } + + fTracksToPostpone[0].SetTrackID(trackID); + step->GetTrack()->SetTrackStatus( fKillTrackAndSecondaries); + + } } +void GateOptrLastVertexInteractionSplittingActor::RegenerationOfPostponedAnnihilationTrack(G4Step* step){ + +//If the second photon from annihilation suceed to exit the collimation system with at least one interaction or was absorbed. +//Resimulation of annihilation photons and its potential secondaries. + +G4TrackVector* currentSecondaries = step->GetfSecondary(); +for (int i =0; i < fTracksToPostpone.size();i ++){ + G4Track* trackToAdd = new G4Track(fTracksToPostpone[fTracksToPostpone.size() - 1 - i]); + trackToAdd->SetParentID(step->GetTrack()->GetTrackID() - 1); + + currentSecondaries->insert(currentSecondaries->begin(),trackToAdd); +} +G4Track* firstPostponedTrack = (*currentSecondaries)[0]; + +// Handle of case were the interaction killed the photon issued from annihilation, it will not be track at the following state +// and the boolean plus the track vector need to be reset + +if (firstPostponedTrack->GetTrackStatus() == 2){ + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); +} + +} + +void GateOptrLastVertexInteractionSplittingActor::HandleTrackIDIfPostponedAnnihilation(G4Step* step){ +// The ID is to modify trackID and the processes and tracks sotcked for a specific trackID associated to +// the postponed annihilation to respect the trackID order in GEANT4. Since in this case the second photon is tracked before the first one +// his trackID +=1. For the postponed one, he is the first secondary particle of the second photon tracks, his trackID is therefore equal +// to the second photon trackID +1 instead of the second photon trackID -1. +// The parentID of secondary particles are also modified because they are used in the rememberlastprocess function +// At last the value associated to a specific trackID in the process and track map are modified according to the new trackID. + + + + if (fSuspendForAnnihil){ + if (step->GetTrack()->GetTrackID() == fTracksToPostpone[0].GetTrackID() -1){ + fRememberedProcesses[step->GetTrack()->GetTrackID()] = fRememberedProcesses[step->GetTrack()->GetTrackID() + 1]; + fRememberedProcesses.erase(step->GetTrack()->GetTrackID() + 1); + fRememberedTracks[step->GetTrack()->GetTrackID()] = fRememberedTracks[step->GetTrack()->GetTrackID() + 1]; + fRememberedTracks.erase(step->GetTrack()->GetTrackID() + 1); + auto vecSec = step->GetSecondary(); + for (int i =0; i size(); i++) + { + (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() +1); + } + step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() +1); + } + if (step->GetTrack()->GetTrackID() == fTracksToPostpone[0].GetTrackID() + 1){ + auto vecSec = step->GetSecondary(); + for (int i =0; i size(); i++) + { + (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() -2); + } + step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() - 2); + } + } +} + + +void GateOptrLastVertexInteractionSplittingActor::BeginOfRunAction(const G4Run* run){ + + // The way to behave of the russian roulette is the following : + // we provide a vector director and the theta angle acceptance, where theta = + // 0 is a vector colinear to the vector director Then if the track generated + // is on the acceptance angle, we add it to the primary track, and if it's not + // the case, we launch the russian roulette + + + if (fRotationVectorDirector) { + G4VPhysicalVolume *physBiasingVolume = + G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume->GetObjectRotationValue(); + fVectorDirector = rot * fVectorDirector; + } } + + + void GateOptrLastVertexInteractionSplittingActor::BeginOfEventAction(const G4Event* event){ - fIDOfSplittedTrack = 0; + + + fParentID = -1; fIsSplitted = false; + fSecondaries = false; + fEventID = event ->GetEventID(); + fEventIDOfSplittedTrack = -1; + fTrackIDOfSplittedTrack = -1; + +} + + + +void GateOptrLastVertexInteractionSplittingActor::PreUserTrackingAction (const G4Track *track){ + fIsFirstStep = true; } + + + void GateOptrLastVertexInteractionSplittingActor::SteppingAction(G4Step* step) { - G4int trackID = step->GetTrack()->GetTrackID(); - if ((fIsSplitted == true) && ( trackID == fIDOfSplittedTrack - 1)) - fIsSplitted = false; + if (fIsFirstStep) { + HandleTrackIDIfPostponedAnnihilation(step); + + // To be sure to not split a track which has been already splitted, we put a condition on the trackID of + if ((fIsSplitted == true) && ( step->GetTrack()->GetTrackID() < fTrackIDOfSplittedTrack)){ + fIsSplitted = false; + } + + ResetProcessesForEnteringParticles(step); + } + G4int trackID = step->GetTrack()->GetTrackID(); + + + //std::cout<GetTrack()->GetTrackStatus()<<" "<GetPreStepPoint()->GetKineticEnergy()<<" " <GetPostStepPoint()->GetKineticEnergy()<<" "<GetPreStepPoint()->GetPosition()<<" "<GetPostStepPoint()->GetPosition()<GetTrack()->GetCreatorProcess() != 0) + creatorProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); + + + if ((!fSuspendForAnnihil )&& (process != "annihil") && (creatorProcessName == "annihil")){ + PostponeFirstAnnihilationTrackIfInteraction(step,process); + } + + // If the first annihilation photon exit the collimation and the process to split is annihilation + // We kill the second photon, because the annihilation will generate both the photons. + if ((!fSuspendForAnnihil ) && (creatorProcessName == "annihil") && (fTrackIDOfSplittedTrack == step->GetTrack()->GetParentID()) && (fEventID == fEventIDOfSplittedTrack )) + step->GetTrack()->SetTrackStatus(fStopAndKill); + + if (fSuspendForAnnihil){ + if (trackID == fTracksToPostpone[0].GetTrackID() - 1){ + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); + } + } + + G4String logicalVolumeNamePostStep = "None"; if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(),logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(),fProcessToSplit) != fListOfProcesses.end()){ - CreateNewParticleAtTheLastVertex(step,fTrackInformation,fStepInformation,fProcessToSplit); - fIsSplitted = true; - fIDOfSplittedTrack = trackID; - fProcessToSplit = "None"; + G4String processToSplit = "None"; + if (auto search = fRememberedProcesses.find(trackID); search != fRememberedProcesses.end()) + processToSplit = fRememberedProcesses[trackID].back(); + + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(),processToSplit) != fListOfProcesses.end()){ + + //Handle of pecularities (1): + + // If the process t split is the gamma issued from compton interaction, the electron primary generated have to be killed + // given that electron will be regenerated + + if ((processToSplit == "compt") && (step->GetTrack()->GetParticleDefinition()->GetParticleName() == "gamma")){ + auto secondaries = step->GetfSecondary(); + if (secondaries->size() > 0){ + G4Track* lastSecTrack = secondaries->back(); + lastSecTrack->SetTrackStatus(fStopAndKill); + } + } + + //Handle of pecularities (2): + + // If the process to split is the annihilation, the second photon, postponed or not, have to be killed + // the reset of the postpone is performed here, whereas the kill of the next annihilation photon, if not postponed + // is realised at the beginning of the step tracking. + + + if (processToSplit == "annihil"){ + if (fSuspendForAnnihil){ + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); + } + } + + + G4Track trackToSplit = fRememberedTracks[trackID].back(); + CreateNewParticleAtTheLastVertex(step,trackToSplit,trackToSplit.GetStep(),processToSplit); + + + fSecondaries = true; + fTrackIDOfSplittedTrack = trackToSplit.GetTrackID(); + fEventIDOfSplittedTrack = fEventID; + fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + } + } + + + if ((fSuspendForAnnihil) && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))){ + if (trackID == fTracksToPostpone[0].GetTrackID()) { + RegenerationOfPostponedAnnihilationTrack(step); } } + + fIsFirstStep = false; + } + + + + + +void GateOptrLastVertexInteractionSplittingActor::PostUserTrackingAction (const G4Track *track){ + if (fSecondaries){ + fIsSplitted = true; + fSecondaries = false; } } + + + //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h index f455598d9..24727beb1 100644 --- a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h @@ -29,17 +29,15 @@ #ifndef GateOptrLastVertexInteractionSplittingActor_h #define GateOptrLastVertexInteractionSplittingActor_h 1 -#include "G4VBiasingOperator.hh" - #include "GateVActor.h" +#include "G4ParticleChangeForGamma.hh" #include #include namespace py = pybind11; class GateOptnComptSplitting; -class GateOptrLastVertexInteractionSplittingActor : public G4VBiasingOperator, - public GateVActor { +class GateOptrLastVertexInteractionSplittingActor : public GateVActor { public: GateOptrLastVertexInteractionSplittingActor(py::dict &user_info); virtual ~GateOptrLastVertexInteractionSplittingActor() {} @@ -57,63 +55,50 @@ class GateOptrLastVertexInteractionSplittingActor : public G4VBiasingOperator, // virtual void PreUserTrackingAction( const G4Track* track ); G4double fSplittingFactor; - G4double fMinWeightOfParticle; - G4double fWeightThreshold; - G4bool fBiasPrimaryOnly; - G4bool fBiasOnlyOnce; - G4int fNInteractions = 0; - G4bool fRussianRoulette; + G4bool fRussianRouletteForAngle = false; G4bool fRotationVectorDirector; G4ThreeVector fVectorDirector; G4double fMaxTheta; - G4Track fTrackInformation; - G4Step fStepInformation; - G4String fProcessToSplit = "None"; - G4int fIDOfSplittedTrack = 0; + G4int fTrackIDOfSplittedTrack = 0; G4bool fIsSplitted = false; + G4bool fSecondaries = false; + G4int fParentID = -1; + G4int fEventID; + G4int fEventIDOfSplittedTrack; + G4int fEventIDOfInitialSplittedTrack; + G4int fTrackIDOfInitialTrack; + G4int fTrackIDOfInitialSplittedTrack = 0; + G4int ftmpTrackID; + G4bool fIsFirstStep = true; + G4bool fSuspendForAnnihil = false; + std::vector fTracksToPostpone; + + std::map> fRememberedTracks; + std::map> fRememberedProcesses; + std::vector fListOfVolumeAncestor; - std::vector fListOfProcesses = {"compt","conv","eBrem"}; + std::vector fListOfProcesses = {"compt","eBrem","annihil","conv","phot"}; // Unused but mandatory - virtual void StartSimulationAction(); virtual void SteppingAction(G4Step *) override; virtual void BeginOfEventAction(const G4Event *) override; - virtual void StartRun(); - virtual void StartTracking(const G4Track *); - virtual void EndTracking() {} + virtual void BeginOfRunAction(const G4Run *run) override; + virtual void PreUserTrackingAction(const G4Track* track) override; + virtual void PostUserTrackingAction(const G4Track* track) override; -protected: - // ----------------------------- - // -- Mandatory from base class: - // ----------------------------- - // -- Unused: - void AttachToAllLogicalDaughtersVolumes(G4LogicalVolume *); - void ComptonSplitting(G4Step* CurrentStep,G4Track track,G4Step step,G4VProcess* process,G4int splittingFactor); - void SecondariesSplitting(G4Step* CurrentStep,G4Track track,G4Step step,G4VProcess* process,G4int splittingFactor); + G4Track* CreateComptonTrack(G4ParticleChangeForGamma*,G4Track, G4double); + void ComptonSplitting(G4Step* CurrentStep,G4Track track,const G4Step* step,G4VProcess* process); + void SecondariesSplitting(G4Step* CurrentStep,G4Track track,const G4Step* step,G4VProcess* process); void RememberLastProcessInformation(G4Step*); - void CreateNewParticleAtTheLastVertex(G4Step*,G4Track,G4Step,G4String); - virtual G4VBiasingOperation *ProposeNonPhysicsBiasingOperation( - const G4Track * /* track */, - const G4BiasingProcessInterface * /* callingProcess */) { - return 0; - } - virtual G4VBiasingOperation *ProposeOccurenceBiasingOperation( - const G4Track * /* track */, - const G4BiasingProcessInterface * /* callingProcess */) { - return 0; - } + void CreateNewParticleAtTheLastVertex(G4Step*,G4Track,const G4Step*,G4String); + void ResetProcessesForEnteringParticles(G4Step * step); + void PostponeFirstAnnihilationTrackIfInteraction(G4Step *step,G4String processName); + void RegenerationOfPostponedAnnihilationTrack(G4Step *step); + void HandleTrackIDIfPostponedAnnihilation(G4Step* step); + G4double RussianRouletteForAngleSurvival(G4ThreeVector, G4ThreeVector, G4double, G4double); - // -- Used: - virtual G4VBiasingOperation *ProposeFinalStateBiasingOperation( - const G4Track *track, const G4BiasingProcessInterface *callingProcess){ - return 0; - } -private: - // -- Avoid compiler complaining for (wrong) method shadowing, - // -- this is because other virtual method with same name exists. - using G4VBiasingOperator::OperationApplied; private: GateOptnComptSplitting *fComptSplittingOperation; diff --git a/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp index 8fc59fa3b..ec54a8077 100644 --- a/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp @@ -7,12 +7,11 @@ #include namespace py = pybind11; -#include "G4VBiasingOperator.hh" #include "GateOptrLastVertexInteractionSplittingActor.h" void init_GateOptrLastVertexInteractionSplittingActor(py::module &m) { - py::class_>( m, "GateOptrLastVertexInteractionSplittingActor") .def_readwrite( diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index bad189dd5..5ae64b3d9 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -461,14 +461,9 @@ def set_default_user_info(user_info): ActorBase.set_default_user_info(user_info) deg = g4_units.deg user_info.splitting_factor = 1 - user_info.weight_threshold = 0 - user_info.bias_primary_only = True - user_info.min_weight_of_particle = 0 - user_info.bias_only_once = True - user_info.processes = ["phot"] - user_info.russian_roulette = False + user_info.russian_roulette_for_angle = False user_info.rotation_vector_director = False - user_info.vector_director = [0, 0, 1] + user_info.vector_director = [0, 0, -1] user_info.max_theta = 90 * deg user_info.list_of_volume_name = [] diff --git a/opengate/tests/src/test076_last_vertex_splittting.py b/opengate/tests/src/test076_last_vertex_splittting.py new file mode 100755 index 000000000..fbd10e5db --- /dev/null +++ b/opengate/tests/src/test076_last_vertex_splittting.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +import uproot +import numpy as np +from scipy.spatial.transform import Rotation +from opengate.tests import utility + + +def bool_validation_test(dico_parameters, tol): + keys = dico_parameters.keys() + liste_diff_max = [] + for key in keys: + liste_diff_max.append(np.max(dico_parameters[key])) + liste_diff_max = np.asarray(liste_diff_max) + max_diff = np.max(liste_diff_max) + print( + "Maximal error (mean or std dev) measured between the analog and the biased simulation:", + np.round(max_diff, 2), + "%", + ) + if max_diff <= 100 * tol: + return True + else: + return False + + +def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): + arr_ref = arr_ref[ + (arr_ref["TrackCreatorProcess"] == "compt") + | (arr_ref["TrackCreatorProcess"] == "none") + ] + arr_data = arr_data[ + (arr_data["TrackCreatorProcess"] != "phot") + & (arr_data["TrackCreatorProcess"] != "eBrem") + & (arr_data["TrackCreatorProcess"] != "eIoni") + ] + + EventID = arr_data["EventID"] + weights = arr_data["Weight"][EventID == EventID[0]] + val_weights = np.round(weights[0], 4) + bool_val_weights = 1 / nb_split == val_weights + print( + "Sum of electron and photon weights for the first event simulated:", + np.round(np.sum(weights), 2), + ) + print("Len of the weights vector for the first event:", len(weights)) + condition_weights = np.round(np.sum(weights), 4) > 2 * ( + 1 - tol_weights + ) and np.round(np.sum(weights), 4) < 2 * (1 + tol_weights) + condition_len = len(weights) > 2 * nb_split * (1 - tol_weights) and len( + weights + ) < 2 * nb_split * (1 + tol_weights) + bool_weights = condition_weights and condition_len + keys = ["KineticEnergy", "PreDirection_X", "PreDirection_Y", "PreDirection_Z"] + + arr_ref_phot = arr_ref[arr_ref["ParticleName"] == "gamma"] + arr_ref_elec = arr_ref[arr_ref["ParticleName"] == "e-"] + + arr_data_phot = arr_data[arr_data["ParticleName"] == "gamma"] + arr_data_elec = arr_data[arr_data["ParticleName"] == "e-"] + + keys_dico = ["ref", "data"] + dico_arr_phot = {} + dico_arr_elec = {} + + dico_arr_phot["ref"] = arr_ref_phot + dico_arr_phot["data"] = arr_data_phot + + dico_arr_elec["ref"] = arr_ref_elec + dico_arr_elec["data"] = arr_data_elec + dico_comp_data = {} + + for key in keys: + arr_data = [] + for key_dico in keys_dico: + mean_elec = np.mean(dico_arr_phot[key_dico][key]) + mean_phot = np.mean(dico_arr_elec[key_dico][key]) + std_elec = np.std(dico_arr_phot[key_dico][key]) + std_phot = np.std(dico_arr_elec[key_dico][key]) + arr_data += [mean_elec, mean_phot, std_elec, std_phot] + dico_comp_data[key] = 100 * np.abs( + np.array( + [ + (arr_data[0] - arr_data[4]) / arr_data[0], + (arr_data[1] - arr_data[5]) / arr_data[1], + (arr_data[2] - arr_data[6]) / arr_data[6], + (arr_data[3] - arr_data[7]) / arr_data[3], + ] + ) + ) + bool_test = bool_validation_test(dico_comp_data, tol) + bool_tot = bool_test and bool_weights and bool_val_weights + return bool_tot + + +if __name__ == "__main__": + paths = utility.get_default_test_paths( + __file__, "test071test_operator_compt_splitting", output_folder="test071" + ) + + # create the simulation + sim = gate.Simulation() + + # main options + ui = sim.user_info + ui.g4_verbose = False + ui.visu = True + ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.logger.EVENT + ui.number_of_threads = 1 + #123456 bug free + # 4665 seg fault + #46656 annihil + ui.random_seed = 641 + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + um = gate.g4_units.um + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + deg = gate.g4_units.deg + gcm3 = gate.g4_units.g / gate.g4_units.cm3 + + # adapt world size + world = sim.world + world.size = [0.25 * m, 0.25 * m, 0.25 * m] + world.material = "G4_WATER" + + ####### GEOMETRY TO IRRADIATE ############# + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + + W_tubs = sim.add_volume("Tubs", "W_box") + W_tubs.material = "Tungsten" + W_tubs.mother = world.name + + W_tubs.rmin = 0 + W_tubs.rmax = 0.5 *cm + W_tubs.dz = 0.05 * m + W_tubs.color = [0.8, 0.2, 0.1, 1] + angle_x = 45 + angle_y = 70 + angle_z = 80 + + rotation = Rotation.from_euler( + "xyz", [angle_y, angle_y, angle_z], degrees=True + ).as_matrix() + W_tubs.rotation = rotation + + ####### Compton Splitting ACTOR ######### + nb_split = 4 + vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") + vertex_splitting_actor.mother = W_tubs.name + vertex_splitting_actor.splitting_factor = nb_split + vertex_splitting_actor.russian_roulette_for_angle = False + vertex_splitting_actor.rotation_vector_director = False + vertex_splitting_actor.max_theta = 10 * deg + vertex_splitting_actor.vector_director = [0,0,-1] + + ##### PHASE SPACE plan ######" + plan_tubs = sim.add_volume("Tubs", "phsp_tubs") + plan_tubs.material = "G4_Galactic" + plan_tubs.mother = world.name + plan_tubs.rmin = W_tubs.rmax + 1*nm + plan_tubs.rmax = plan_tubs.rmin + 1 * nm + plan_tubs.dz = 0.5 * m + plan_tubs.color = [0.2, 1, 0.8, 1] + plan_tubs.rotation = rotation + + ####### Electron source ########### + source = sim.add_source("GenericSource", "source1") + source.particle = "gamma" + source.n = 1 + source.position.type = "sphere" + source.position.radius = 1 * nm + source.direction.type = "momentum" + # source.direction.momentum = [0,0,-1] + source.direction.momentum = np.dot(rotation, np.array([0, 0, -1])) + source.energy.type = "mono" + source.energy.mono = 4 * MeV + + ####### PHASE SPACE ACTOR ############## + + phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") + phsp_actor.mother = plan_tubs.name + phsp_actor.attributes = [ + "EventID", + "TrackID", + "Weight", + "ParticleName", + "KineticEnergy", + "PreDirection", + "TrackCreatorProcess", + ] + + phsp_actor.output = paths.output / "test075_output_data.root" + + ##### MODIFIED PHYSICS LIST ############### + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + #### Extremely important, it seems that GEANT4, for almost all physics lists, encompass all the photon processes in GammaGeneralProc + #### Therefore if we provide the name of the real process (here compt) without deactivating GammaGeneralProcess, it will not find the + #### process to bias and the biasing will fail + s = f"/process/em/UseGeneralProcess false" + sim.add_g4_command_before_init(s) + + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * um + sim.physics_manager.global_production_cuts.positron = 1 * um + + output = sim.run() + + # + # print results + stats = sim.output.get_actor("Stats") + h = sim.output.get_actor("PhaseSpace") + print(stats) + # + # f_data = uproot.open(paths.output / "test071_output_data.root") + # f_ref_data = uproot.open(paths.data / "test071_ref_data.root") + # arr_data = f_data["PhaseSpace"].arrays() + # arr_ref_data = f_ref_data["PhaseSpace"].arrays() + # # + # is_ok = validation_test(arr_ref_data, arr_data, nb_split) + # utility.test_ok(is_ok) + From d5eb52c8048b1c7803b07f28b8f29c30b10a5a42 Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 18 Jun 2024 13:38:09 +0200 Subject: [PATCH 22/82] Add angular RR for all processes + Bug corrections on postponed pair production + tracking --- core/opengate_core/opengate_core.cpp | 4 +- ...ateLastVertexInteractionSplittingActor.cpp | 643 ++++++++++++++++++ ...GateLastVertexInteractionSplittingActor.h} | 53 +- .../GateLastVertexSplittingPostStepDoIt.h | 75 ++ ...ptrLastVertexInteractionSplittingActor.cpp | 539 --------------- ...teLastVertexInteractionSplittingActor.cpp} | 12 +- opengate/actors/miscactors.py | 4 +- 7 files changed, 752 insertions(+), 578 deletions(-) create mode 100644 core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp rename core/opengate_core/opengate_lib/{GateOptrLastVertexInteractionSplittingActor.h => GateLastVertexInteractionSplittingActor.h} (76%) create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h delete mode 100644 core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp rename core/opengate_core/opengate_lib/{pyGateOptrLastVertexInteractionSplittingActor.cpp => pyGateLastVertexInteractionSplittingActor.cpp} (51%) diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index bb7cf5b17..fc2f928b9 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -338,7 +338,7 @@ void init_GatePhaseSpaceActor(py::module &); void init_GateOptrComptSplittingActor(py::module &m); -void init_GateOptrLastVertexInteractionSplittingActor(py::module &m); +void init_GateLastVertexInteractionSplittingActor(py::module &m); void init_GateBOptrBremSplittingActor(py::module &m); @@ -560,7 +560,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GatePhaseSpaceActor(m); init_GateBOptrBremSplittingActor(m); init_GateOptrComptSplittingActor(m); - init_GateOptrLastVertexInteractionSplittingActor(m); + init_GateLastVertexInteractionSplittingActor(m); init_GateHitsCollectionActor(m); init_GateMotionVolumeActor(m); init_GateHitsAdderActor(m); diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp new file mode 100644 index 000000000..3b18610c6 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -0,0 +1,643 @@ +// +// ******************************************************************** +// * 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 GateLastVertexInteractionSplittingActor.cc +/// \brief Implementation of the GateLastVertexInteractionSplittingActor class + +#include "GateHelpersDict.h" +#include "GateHelpersImage.h" + +#include "CLHEP/Units/SystemOfUnits.h" +#include "G4BiasingProcessInterface.hh" +#include "G4Gamma.hh" +#include "G4Positron.hh" +#include "G4LogicalVolumeStore.hh" +#include "G4ParticleTable.hh" +#include "G4PhysicalVolumeStore.hh" +#include "G4ProcessManager.hh" +#include "G4TrackingManager.hh" +#include "G4ProcessVector.hh" +#include "GateOptnComptSplitting.h" +#include "GateLastVertexInteractionSplittingActor.h" +#include "GateLastVertexSplittingPostStepDoIt.h" +#include "G4VParticleChange.hh" + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateLastVertexInteractionSplittingActor::GateLastVertexInteractionSplittingActor(py::dict &user_info) : GateVActor(user_info, false) +{ + fMotherVolumeName = DictGetStr(user_info, "mother"); + fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); + fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); + fRussianRouletteForAngle = DictGetBool(user_info, "russian_roulette_for_angle"); + fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); + fMaxTheta = DictGetDouble(user_info, "max_theta"); + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("BeginOfEventAction"); + fActions.insert("BeginOfRunAction"); + fActions.insert("PreUserTrackingAction"); + fActions.insert("PostUserTrackingAction"); +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +G4double GateLastVertexInteractionSplittingActor::RussianRouletteForAngleSurvival(G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta, G4double split) +{ + G4double cosTheta = vectorDirector * dir; + G4double theta = std::acos(cosTheta); + G4double weightToApply = 1; + if (theta > fMaxTheta) + { + G4double probability = G4UniformRand(); + if (probability <= 1 / split) + { + weightToApply = split; + } + else + { + weightToApply = 0; + } + } + return weightToApply; +} + +G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) +{ + G4double energy = gammaProcess->GetProposedKineticEnergy(); + G4double globalTime = track.GetGlobalTime(); + G4double newGammaWeight = weight; + const G4ThreeVector momentum = gammaProcess->GetProposedMomentumDirection(); + const G4ThreeVector position = track.GetPosition(); + G4Track *newTrack = new G4Track(track); + + newTrack->SetWeight(newGammaWeight); + newTrack->SetKineticEnergy(energy); + newTrack->SetMomentumDirection(momentum); + newTrack->SetPosition(position); + return newTrack; +} + +void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step *CurrentStep, G4Track track, const G4Step *step, G4VProcess *process) +{ + // Loop on process and add the secondary tracks to the current step secondary vector + + G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + G4double gammaWeight = 0; + G4int nCall = (G4int)fSplittingFactor; + for (int i = 0; i < nCall; i++) + { + GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; + G4VParticleChange *processFinalState = emProcess->PostStepDoIt(track, *step); + G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; + const G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); + gammaWeight = CurrentStep->GetTrack()->GetWeight() / fSplittingFactor; + if (fRussianRouletteForAngle == true) + { + G4double weightToApply = RussianRouletteForAngleSurvival(momentum, fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0) + { + gammaWeight = gammaWeight * weightToApply; + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, track, gammaWeight); + trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) + { + G4Track *newTrack = processFinalState->GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } + } + + else + { + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, track, gammaWeight); + trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) + { + G4Track *newTrack = processFinalState->GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } + processFinalState->Clear(); + gammaProcessFinalState->Clear(); + } +} + +void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *CurrentStep, G4Track track, const G4Step *step, G4VProcess *process) +{ + // Loop on process and add the secondary tracks to the current step secondary vector. + G4String particleName = track.GetParticleDefinition()->GetParticleName(); + G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + G4double gammaWeight = 0; + G4int nCall = (G4int)fSplittingFactor; + for (int i = 0; i < nCall; i++) + { + G4VParticleChange *processFinalState = nullptr; + + if (process->GetProcessName() == "eBrem") + { + GateBremPostStepDoIt *bremProcess = (GateBremPostStepDoIt *)process; + processFinalState = bremProcess->GateBremPostStepDoIt::PostStepDoIt(track, *step); + } + else + { + GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; + processFinalState = emProcess->PostStepDoIt(track, *step); + } + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) + { + gammaWeight = CurrentStep->GetTrack()->GetWeight() / fSplittingFactor; + G4Track *newTrack = processFinalState->GetSecondary(j); + if ((fRussianRouletteForAngle == true) && (particleName == "gamma")) + { + const G4ThreeVector momentum = newTrack->GetMomentumDirection(); + G4double weightToApply = RussianRouletteForAngleSurvival(momentum, fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0) + { + gammaWeight = gammaWeight * weightToApply; + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } + else + { + std::cout<GetKineticEnergy()<SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } + processFinalState->Clear(); + } +} + +void GateLastVertexInteractionSplittingActor::ClearRememberedTracksAndSteps(std::map rememberedTracks, std::map> rememberedSteps) +{ + std::vector trackToKill = {}; + for (auto it = rememberedTracks.begin(); it != rememberedTracks.end(); ++it) + { + std::vector vector = it->second; + for (auto it2 = vector.begin(); it2 != vector.end(); it2++){ + if ((std::find(trackToKill.begin(), trackToKill.end(), *it2) == trackToKill.end())){ + trackToKill.push_back(*it2); + } + } + } + + for (auto it = trackToKill.begin(); it != trackToKill.end(); ++it){ + delete *it; + } + +std::vector stepToKill = {}; + for (auto it = rememberedSteps.begin(); it != rememberedSteps.end(); ++it) + { + std::vector vector = it->second; + for (auto it2 = vector.begin(); it2 != vector.end(); it2++){ + if ((std::find(stepToKill.begin(), stepToKill.end(), *it2) == stepToKill.end())){ + stepToKill.push_back(*it2); + } + } + } + + for (auto it = stepToKill.begin(); it != stepToKill.end(); ++it){ + delete *it; + } +} + +void GateLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4Step *step) +{ + + // When an interesting process to split occurs, we remember the status of the track and the process at this current step + // some informations regarding the track info have to be changed because they were update according to the interaction that occured + // These informations are stocked as a map object, binding the track ID with all the track objects and processes to split. + // Because in some cases, if a secondary was created before an interaction chain, this secondary will be track after the chain and + // without this association, we wll loose the information about the process occuring for this secondary. + + G4String creatorProcessName = "None"; + if (step->GetTrack()->GetCreatorProcess() != 0) + creatorProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); + G4String processName = "None"; + G4int trackID = step->GetTrack()->GetTrackID(); + G4int parentID = step->GetTrack()->GetParentID(); + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) + { + processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + } + if ((step->GetTrack()->GetParticleDefinition()->GetParticleName() == "e+") && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))) + { + processName = "annihil"; + } + + if ((std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processName) != fListOfProcesses.end())) + { + G4Track *trackInformation = new G4Track(*(step->GetTrack())); + G4Step *stepInformation = new G4Step(*(step)); + + G4StepPoint *stepPoint = nullptr; + if ((processName == "annihil") && (step->GetTrack()->GetKineticEnergy() == 0)) + { + stepPoint = step->GetPostStepPoint(); + } + else + { + stepPoint = step->GetPreStepPoint(); + } + + trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy()); + trackInformation->SetMomentumDirection(stepPoint->GetMomentumDirection()); + trackInformation->SetTrackStatus(fAlive); + trackInformation->SetPolarization(stepPoint->GetPolarization()); + trackInformation->SetTrackID(trackID); + + if (auto search = fRememberedTracks.find(trackID); search != fRememberedTracks.end()) + { + fRememberedTracks[trackID].push_back(trackInformation); + fRememberedProcesses[trackID].push_back(processName); + fRememberedSteps[trackID].push_back(stepInformation); + } + + else + { + fRememberedTracks[trackID] = {trackInformation}; + fRememberedProcesses[trackID] = {processName}; + fRememberedSteps[trackID] = {stepInformation}; + } + } + + else + { + if (auto search = fRememberedTracks.find(trackID); search == fRememberedTracks.end()) + { + if (auto search = fRememberedTracks.find(parentID); search != fRememberedTracks.end()) + { + if (auto it = std::find(fRememberedProcesses[parentID].begin(), fRememberedProcesses[parentID].end(), creatorProcessName); it != fRememberedProcesses[parentID].end()) + { + auto idx = it - fRememberedProcesses[parentID].begin(); + fRememberedTracks[trackID] = {fRememberedTracks[parentID][idx]}; + fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][idx]}; + fRememberedSteps[trackID] = {fRememberedSteps[parentID][idx]}; + } + else + { + fRememberedTracks[trackID] = {fRememberedTracks[parentID][0]}; + fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][0]}; + fRememberedSteps[trackID] = {fRememberedSteps[parentID][0]}; + } + } + } + } +} + +void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step *CurrentStep, G4Track track, const G4Step *step, G4String processName) +{ + // We retrieve the process associated to the process name to split and we split according the process. + // Since for compton scattering, the gamma is not a secondary particles, this one need to have his own splitting function. + + G4ParticleDefinition *particleDefinition = track.GetDefinition(); + G4ProcessManager *processManager = particleDefinition->GetProcessManager(); + G4ProcessVector *processList = processManager->GetProcessList(); + G4VProcess *processToSplit = nullptr; + for (size_t i = 0; i < processList->size(); ++i) + { + auto process = (*processList)[i]; + if (process->GetProcessName() == processName) + { + processToSplit = process; + } + } + if (processName == "compt") + { + ComptonSplitting(CurrentStep, track, step, processToSplit); + } + else + { + SecondariesSplitting(CurrentStep, track, step, processToSplit); + } +} + +void GateLastVertexInteractionSplittingActor::ResetProcessesForEnteringParticles(G4Step *step) +{ + + // This function reset the processes and track registered to be split each time a particle enters into a volume. + // Here a new particle incoming is either a particle entering into the volume ( first pre step is a boundary of a mother volume + // or first step trigger the actor and the vertex is not either the mother volume or a the fdaughter volume) or an event generated within + // the volume (the particle did not enter into the volume and its parentID = 0). An additionnal condition is set with the trackID to ensure + // particles crossing twice the volume (after either a compton or pair prod) are not splitted. + + G4int trackID = step->GetTrack()->GetTrackID(); + if ((fEventIDOfInitialSplittedTrack != fEventID) || ((fEventIDOfInitialSplittedTrack == fEventID) && (trackID < fTrackIDOfInitialTrack))) + { + G4String logicalVolumeNamePreStep = "None"; + if (step->GetPreStepPoint()->GetPhysicalVolume() != 0) + { + logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + } + if (((step->GetPreStepPoint()->GetStepStatus() == 1) && (logicalVolumeNamePreStep == fMotherVolumeName)) || + ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logicalVolumeNamePreStep) && (step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != fMotherVolumeName))) + { + fTrackIDOfInitialTrack = trackID; + fEventIDOfInitialSplittedTrack = fEventID; + fWeightOfEnteringParticle = step->GetTrack()->GetWeight(); + ClearRememberedTracksAndSteps(fRememberedTracks,fRememberedSteps); + fRememberedProcesses.clear(); + fRememberedTracks.clear(); + fRememberedSteps.clear(); + } + else if (step->GetTrack()->GetParentID() == 0) + { + fTrackIDOfInitialTrack = trackID; + fEventIDOfInitialSplittedTrack = fEventID; + fWeightOfEnteringParticle = step->GetTrack()->GetWeight(); + ClearRememberedTracksAndSteps(fRememberedTracks,fRememberedSteps); + fRememberedProcesses.clear(); + fRememberedTracks.clear(); + fRememberedSteps.clear(); + } + } +} + +void GateLastVertexInteractionSplittingActor::PostponeFirstAnnihilationTrackIfInteraction(G4Step *step, G4String processName) +{ + // In case the first gamma issued from annihilation undergoes an interaction, in order to not bias the process + // We keep in memory the particle post step state (with its secondaries) and kill the particle and its secondaries. + // If the second photon from annihilation exiting the collimation system with an interaction or is absorbed within + // the collimation, the particle is subsequently resimulated, starting from the interaction point. + + G4int trackID = step->GetTrack()->GetTrackID(); + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processName) != fListOfProcesses.end()) + { + fSuspendForAnnihil = true; + G4Track trackToPostpone = G4Track(*(step->GetTrack())); + trackToPostpone.SetKineticEnergy(step->GetPostStepPoint()->GetKineticEnergy()); + trackToPostpone.SetMomentumDirection(step->GetPostStepPoint()->GetMomentumDirection()); + trackToPostpone.SetTrackStatus(step->GetTrack()->GetTrackStatus()); + trackToPostpone.SetPolarization(step->GetPostStepPoint()->GetPolarization()); + trackToPostpone.SetPosition(step->GetPostStepPoint()->GetPosition()); + trackToPostpone.SetTrackID(trackID); + fTracksToPostpone.push_back(trackToPostpone); + auto theTrack = fTracksToPostpone[0]; + + auto secVec = step->GetfSecondary(); + for (int i = 0; i < secVec->size(); i++) + { + G4Track *sec = (*secVec)[i]; + G4Track copySec = G4Track((*sec)); + fTracksToPostpone.push_back(copySec); + } + + fTracksToPostpone[0].SetTrackID(trackID); + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + } +} + +void GateLastVertexInteractionSplittingActor::RegenerationOfPostponedAnnihilationTrack(G4Step *step) +{ + + // If the second photon from annihilation suceed to exit the collimation system with at least one interaction or was absorbed. + // Resimulation of annihilation photons and its potential secondaries. + + G4TrackVector *currentSecondaries = step->GetfSecondary(); + for (int i = 0; i < fTracksToPostpone.size(); i++) + { + G4Track *trackToAdd = new G4Track(fTracksToPostpone[fTracksToPostpone.size() - 1 - i]); + trackToAdd->SetParentID(step->GetTrack()->GetTrackID() - 1); + + currentSecondaries->insert(currentSecondaries->begin(), trackToAdd); + } + G4Track *firstPostponedTrack = (*currentSecondaries)[0]; + + // Handle of case were the interaction killed the photon issued from annihilation, it will not be track at the following state + // and the boolean plus the track vector need to be reset + + if (firstPostponedTrack->GetTrackStatus() == 2) + { + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); + } +} + +void GateLastVertexInteractionSplittingActor::HandleTrackIDIfPostponedAnnihilation(G4Step *step) +{ + // The ID is to modify trackID and the processes and tracks sotcked for a specific trackID associated to + // the postponed annihilation to respect the trackID order in GEANT4. Since in this case the second photon is tracked before the first one + // his trackID +=1. For the postponed one, he is the first secondary particle of the second photon tracks, his trackID is therefore equal + // to the second photon trackID +1 instead of the second photon trackID -1. + // The parentID of secondary particles are also modified because they are used in the rememberlastprocess function + // At last the value associated to a specific trackID in the process and track map are modified according to the new trackID. + + if (fSuspendForAnnihil) + { + if (step->GetTrack()->GetTrackID() == fTracksToPostpone[0].GetTrackID() - 1) + { + fRememberedProcesses[step->GetTrack()->GetTrackID()] = fRememberedProcesses[step->GetTrack()->GetTrackID() + 1]; + fRememberedProcesses.erase(step->GetTrack()->GetTrackID() + 1); + fRememberedSteps[step->GetTrack()->GetTrackID()] = fRememberedSteps[step->GetTrack()->GetTrackID() + 1]; + fRememberedSteps.erase(step->GetTrack()->GetTrackID() + 1); + fRememberedTracks[step->GetTrack()->GetTrackID()] = fRememberedTracks[step->GetTrack()->GetTrackID() + 1]; + fRememberedTracks.erase(step->GetTrack()->GetTrackID() + 1); + auto vecSec = step->GetSecondary(); + for (int i = 0; i < vecSec->size(); i++) + { + (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() + 1); + } + step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() + 1); + } + if (step->GetTrack()->GetTrackID() == fTracksToPostpone[0].GetTrackID() + 1) + { + auto vecSec = step->GetSecondary(); + for (int i = 0; i < vecSec->size(); i++) + { + (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() - 2); + } + step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() - 2); + } + } +} + +void GateLastVertexInteractionSplittingActor::BeginOfRunAction(const G4Run *run) +{ + + // The way to behave of the russian roulette is the following : + // we provide a vector director and the theta angle acceptance, where theta = + // 0 is a vector colinear to the vector director Then if the track generated + // is on the acceptance angle, we add it to the primary track, and if it's not + // the case, we launch the russian roulette + + if (fRotationVectorDirector) + { + G4VPhysicalVolume *physBiasingVolume = + G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume->GetObjectRotationValue(); + fVectorDirector = rot * fVectorDirector; + } +} + +void GateLastVertexInteractionSplittingActor::BeginOfEventAction(const G4Event *event) +{ + + fParentID = -1; + fEventID = event->GetEventID(); + if (fEventID%30000 == 0) + std::cout <GetTrack()->GetCreatorProcess() != 0) + creatorProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); + + if (fIsFirstStep) + { + + HandleTrackIDIfPostponedAnnihilation(step); + ResetProcessesForEnteringParticles(step); + } + + G4int trackID = step->GetTrack()->GetTrackID(); + + if (step->GetTrack()->GetWeight() >= fWeightOfEnteringParticle) + { + RememberLastProcessInformation(step); + G4String process = "None"; + if (auto search = fRememberedProcesses.find(trackID); search != fRememberedProcesses.end()) + { + process = fRememberedProcesses[trackID].back(); + } + // std::cout<GetTrack()->GetVertexPosition()<<" "<GetTrack()->GetParticleDefinition()->GetParticleName()<<" "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" "<GetTrack()->GetTrackID()<<" "<GetTrack()->GetTrackStatus()<<" "<GetPreStepPoint()->GetKineticEnergy()<<" " <GetPostStepPoint()->GetKineticEnergy()<<" "<GetPreStepPoint()->GetPosition()<<" "<GetPostStepPoint()->GetPosition()<GetTrack()->GetParentID()) && (fEventID == fEventIDOfSplittedTrack)) + { + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + } + + if ((!fSuspendForAnnihil) && (process != "annihil") && (creatorProcessName == "annihil") && (step->GetTrack()->GetTrackStatus() != 3)) + { + G4int parentID = step->GetTrack()->GetParentID(); + if (auto search = fRememberedProcesses.find(parentID); search != fRememberedProcesses.end()) + { + if (fRememberedProcesses[trackID].back() == "annihil") + PostponeFirstAnnihilationTrackIfInteraction(step, process); + } + } + // If the first annihilation photon exit the collimation and the process to split is annihilation + // We kill the second photon, because the annihilation will generate both the photons. + + if (fSuspendForAnnihil) + { + if (trackID == fTracksToPostpone[0].GetTrackID() - 1) + { + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); + } + } + + G4String logicalVolumeNamePostStep = "None"; + if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + + if (((step->GetTrack()->GetTrackStatus() != 2) && (step->GetTrack()->GetTrackStatus() != 3)) && (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) + { + G4String processToSplit = "None"; + if (auto search = fRememberedProcesses.find(trackID); search != fRememberedProcesses.end()) + processToSplit = fRememberedProcesses[trackID].back(); + + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processToSplit) != fListOfProcesses.end()) + { + std::cout<GetTrack()->GetKineticEnergy()<GetTrack()->GetParticleDefinition()->GetParticleName() == "gamma")) + { + auto secondaries = step->GetfSecondary(); + if (secondaries->size() > 0) + { + G4Track *lastSecTrack = secondaries->back(); + lastSecTrack->SetTrackStatus(fStopAndKill); + } + } + + // Handle of pecularities (2): + + // If the process to split is the annihilation, the second photon, postponed or not, have to be killed + // the reset of the postpone is performed here, whereas the kill of the next annihilation photon, if not postponed + // is realised at the beginning of the step tracking. + + if (processToSplit == "annihil") + { + if (fSuspendForAnnihil) + { + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); + } + } + + G4Track *trackToSplit = fRememberedTracks[trackID].back(); + G4Step *stepToSplit = fRememberedSteps[trackID].back(); + CreateNewParticleAtTheLastVertex(step, *trackToSplit, stepToSplit, processToSplit); + + fTrackIDOfSplittedTrack = trackToSplit->GetTrackID(); + fEventIDOfSplittedTrack = fEventID; + fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + } + } + + if ((fSuspendForAnnihil) && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))) + { + if (trackID == fTracksToPostpone[0].GetTrackID()) + { + RegenerationOfPostponedAnnihilationTrack(step); + } + } + + fIsFirstStep = false; +} + +void GateLastVertexInteractionSplittingActor::PostUserTrackingAction(const G4Track *track) +{ +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h similarity index 76% rename from core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h rename to core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index 24727beb1..b79c5cc7a 100644 --- a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -24,35 +24,24 @@ // ******************************************************************** // /// -/// \file GateOptrLastVertexInteractionSplittingActor.h -/// \brief Definition of the GateOptrLastVertexInteractionSplittingActor class -#ifndef GateOptrLastVertexInteractionSplittingActor_h -#define GateOptrLastVertexInteractionSplittingActor_h 1 +/// \file GateLastVertexInteractionSplittingActor.h +/// \brief Definition of the GateLastVertexInteractionSplittingActor class +#ifndef GateLastVertexInteractionSplittingActor_h +#define GateLastVertexInteractionSplittingActor_h 1 #include "GateVActor.h" #include "G4ParticleChangeForGamma.hh" +#include "G4VEnergyLossProcess.hh" #include #include namespace py = pybind11; -class GateOptnComptSplitting; -class GateOptrLastVertexInteractionSplittingActor : public GateVActor { -public: - GateOptrLastVertexInteractionSplittingActor(py::dict &user_info); - virtual ~GateOptrLastVertexInteractionSplittingActor() {} +class GateLastVertexInteractionSplittingActor : public GateVActor { public: - // ------------------------- - // Optional from base class: - // ------------------------- - // -- Call at run start: - // virtual void BeginOfRunAction(const G4Run *run); - - // virtual void SteppingAction(G4Step* step); - - // -- Call at each track starting: - // virtual void PreUserTrackingAction( const G4Track* track ); + GateLastVertexInteractionSplittingActor(py::dict &user_info); + virtual ~GateLastVertexInteractionSplittingActor() {} G4double fSplittingFactor; G4bool fRussianRouletteForAngle = false; @@ -60,8 +49,6 @@ class GateOptrLastVertexInteractionSplittingActor : public GateVActor { G4ThreeVector fVectorDirector; G4double fMaxTheta; G4int fTrackIDOfSplittedTrack = 0; - G4bool fIsSplitted = false; - G4bool fSecondaries = false; G4int fParentID = -1; G4int fEventID; G4int fEventIDOfSplittedTrack; @@ -71,15 +58,16 @@ class GateOptrLastVertexInteractionSplittingActor : public GateVActor { G4int ftmpTrackID; G4bool fIsFirstStep = true; G4bool fSuspendForAnnihil = false; + G4double fWeightOfEnteringParticle = 0; std::vector fTracksToPostpone; - std::map> fRememberedTracks; + std::map fRememberedTracks; + std::map> fRememberedSteps; std::map> fRememberedProcesses; std::vector fListOfVolumeAncestor; - std::vector fListOfProcesses = {"compt","eBrem","annihil","conv","phot"}; - // Unused but mandatory + std::vector fListOfProcesses = {"compt","annihil","eBrem","conv","phot"}; virtual void SteppingAction(G4Step *) override; virtual void BeginOfEventAction(const G4Event *) override; @@ -87,21 +75,28 @@ class GateOptrLastVertexInteractionSplittingActor : public GateVActor { virtual void PreUserTrackingAction(const G4Track* track) override; virtual void PostUserTrackingAction(const G4Track* track) override; + //Pure splitting functions + G4double RussianRouletteForAngleSurvival(G4ThreeVector, G4ThreeVector, G4double, G4double); G4Track* CreateComptonTrack(G4ParticleChangeForGamma*,G4Track, G4double); void ComptonSplitting(G4Step* CurrentStep,G4Track track,const G4Step* step,G4VProcess* process); void SecondariesSplitting(G4Step* CurrentStep,G4Track track,const G4Step* step,G4VProcess* process); + + + //Handling the remembered processes to replay void RememberLastProcessInformation(G4Step*); void CreateNewParticleAtTheLastVertex(G4Step*,G4Track,const G4Step*,G4String); void ResetProcessesForEnteringParticles(G4Step * step); + void ClearRememberedTracksAndSteps(std::map, std::map>); + + + //Edge case to handle the bias in annihilation + //FIXME : The triple annihilation is not handled for the moment void PostponeFirstAnnihilationTrackIfInteraction(G4Step *step,G4String processName); void RegenerationOfPostponedAnnihilationTrack(G4Step *step); void HandleTrackIDIfPostponedAnnihilation(G4Step* step); - G4double RussianRouletteForAngleSurvival(G4ThreeVector, G4ThreeVector, G4double, G4double); - - + + -private: - GateOptnComptSplitting *fComptSplittingOperation; }; #endif diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h new file mode 100644 index 000000000..d7880138a --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h @@ -0,0 +1,75 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +/// +#ifndef GateLastVertexSplittingPostStepDoIt_h +#define GateLastVertexSplittingPostStepDoIt_h + + +#include "G4VEnergyLossProcess.hh" +#include "G4VEmProcess.hh" +#include "G4VParticleChange.hh" +#include + + + + +class GateBremPostStepDoIt : public G4VEnergyLossProcess { +public : + +GateBremPostStepDoIt(); + +~ GateBremPostStepDoIt(); + +virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & step) override +{ + const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); + currentCouple = couple; + G4VParticleChange* particleChange = G4VEnergyLossProcess::PostStepDoIt(track,step); + return particleChange; +} + + +}; + + +class GateGammaEmPostStepDoIt : public G4VEmProcess { +public : + +GateGammaEmPostStepDoIt(); + +~ GateGammaEmPostStepDoIt(); + +virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & step) override +{ + const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); + currentCouple = couple; + G4VParticleChange* particleChange = G4VEmProcess::PostStepDoIt(track,step); + return particleChange; +} + + +}; +#endif diff --git a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp deleted file mode 100644 index b38ed6fdf..000000000 --- a/core/opengate_core/opengate_lib/GateOptrLastVertexInteractionSplittingActor.cpp +++ /dev/null @@ -1,539 +0,0 @@ -// -// ******************************************************************** -// * 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 GateOptrLastVertexInteractionSplittingActor.cc -/// \brief Implementation of the GateOptrLastVertexInteractionSplittingActor class - -#include "GateHelpersDict.h" -#include "GateHelpersImage.h" - -#include "CLHEP/Units/SystemOfUnits.h" -#include "G4BiasingProcessInterface.hh" -#include "G4Gamma.hh" -#include "G4Positron.hh" -#include "G4LogicalVolumeStore.hh" -#include "G4ParticleTable.hh" -#include "G4PhysicalVolumeStore.hh" -#include "G4ProcessManager.hh" -#include "G4TrackingManager.hh" -#include "G4ProcessVector.hh" -#include "GateOptnComptSplitting.h" -#include "GateOptrLastVertexInteractionSplittingActor.h" -#include "G4VParticleChange.hh" - -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... - -GateOptrLastVertexInteractionSplittingActor::GateOptrLastVertexInteractionSplittingActor(py::dict &user_info):GateVActor(user_info, false) { - fMotherVolumeName = DictGetStr(user_info, "mother"); - fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); - fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); - fRussianRouletteForAngle = DictGetBool(user_info, "russian_roulette_for_angle"); - fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); - fMaxTheta = DictGetDouble(user_info, "max_theta"); - fActions.insert("StartSimulationAction"); - fActions.insert("SteppingAction"); - fActions.insert("BeginOfEventAction"); - fActions.insert("BeginOfRunAction"); - fActions.insert("PreUserTrackingAction"); - fActions.insert("PostUserTrackingAction"); -} - -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... - - -G4double GateOptrLastVertexInteractionSplittingActor::RussianRouletteForAngleSurvival(G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta,G4double split) { - G4double cosTheta = vectorDirector * dir; - G4double theta = std::acos(cosTheta); - G4double weightToApply = 1; - if (theta > fMaxTheta) { - G4double probability = G4UniformRand(); - if (probability <= 1 / split) { - weightToApply = split; - } else { - weightToApply = 0; - } - } - return weightToApply; -} - -G4Track* GateOptrLastVertexInteractionSplittingActor:: CreateComptonTrack(G4ParticleChangeForGamma* gammaProcess,G4Track track, G4double weight){ - G4double energy = gammaProcess->GetProposedKineticEnergy(); - G4double globalTime = track.GetGlobalTime(); - G4double newGammaWeight = weight; - const G4ThreeVector momentum =gammaProcess->GetProposedMomentumDirection(); - const G4ThreeVector position = track.GetPosition(); - G4Track *newTrack = new G4Track(track); - newTrack->SetWeight(newGammaWeight); - newTrack->SetKineticEnergy(energy); - newTrack->SetMomentumDirection(momentum); - newTrack->SetPosition(position); - return newTrack; -} - -void GateOptrLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* CurrentStep,G4Track track,const G4Step *step,G4VProcess* process){ -//Loop on process and add the secondary tracks to the current step secondary vector - - - -G4TrackVector *trackVector = CurrentStep->GetfSecondary(); -G4double gammaWeight = CurrentStep->GetTrack()->GetWeight()/fSplittingFactor; -G4int nCall = (G4int) fSplittingFactor; -for (int i =0; i PostStepDoIt(track,*step); - G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; - const G4ThreeVector momentum =gammaProcessFinalState->GetProposedMomentumDirection(); - - if (fRussianRouletteForAngle == true) { - G4double weightToApply = RussianRouletteForAngleSurvival(momentum,fVectorDirector, fMaxTheta, fSplittingFactor); - if (weightToApply != 0){ - gammaWeight = gammaWeight * weightToApply; - G4Track* newTrack = CreateComptonTrack(gammaProcessFinalState,track,gammaWeight); - trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for(int j=0; j GetSecondary(j); - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - } - } - - else { - G4Track* newTrack = CreateComptonTrack(gammaProcessFinalState,track,gammaWeight); - trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for(int j=0; j GetSecondary(j); - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - - } - } - } -} - - -void GateOptrLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* CurrentStep,G4Track track,const G4Step *step,G4VProcess* process){ -//Loop on process and add the secondary tracks to the current step secondary vector. - -G4TrackVector *trackVector = CurrentStep->GetfSecondary(); -G4int nCall = (G4int) fSplittingFactor; -for (int i =0; i PostStepDoIt(track,*step); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for(int j=0; j GetSecondary(j); - trackVector->push_back(newTrack); - } - } -} - -void GateOptrLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4Step* step){ - - // When an interesting process to split occurs, we remember the status of the track and the process at this current step - // some informations regarding the track info have to be changed because they were update according to the interaction that occured - // These informations are sotcked as a map object, binding the track ID with all the track objects and processes to split. - // Because in some cases, if a secondary was created before an interaction chain, this secondary will be track after the chain and - // without this association, we wll loose the information about the process occuring for this secondary. - - G4String creatorProcessName = "None"; - if (step->GetTrack()->GetCreatorProcess() !=0) - creatorProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); - G4String processName = "None"; - G4int trackID = step->GetTrack()->GetTrackID(); - if (step->GetPostStepPoint()->GetProcessDefinedStep() !=0){ - processName = step->GetPostStepPoint()->GetProcessDefinedStep()-> GetProcessName(); - } - if ((step->GetTrack()->GetParticleDefinition()->GetParticleName() == "e+") && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))){ - processName = "annihil"; - } - if ((std::find(fListOfProcesses.begin(),fListOfProcesses.end(),processName) != fListOfProcesses.end())){ - G4Track trackInformation = G4Track(*(step->GetTrack())); - trackInformation.SetKineticEnergy(step->GetPreStepPoint()->GetKineticEnergy()); - trackInformation.SetMomentumDirection(step->GetPreStepPoint()->GetMomentumDirection()); - trackInformation.SetTrackStatus(fAlive); - trackInformation.SetPolarization(step->GetPreStepPoint()->GetPolarization()); - trackInformation.SetTrackID(trackID); - if (auto search = fRememberedTracks.find(trackID); search != fRememberedTracks.end()){ - fRememberedTracks[trackID].push_back(trackInformation); - fRememberedProcesses[trackID].push_back(processName); - ; - } - else { - fRememberedTracks[trackID] = {trackInformation}; - fRememberedProcesses[trackID] = {processName}; - - - } - } - else { - - - G4int parentID = step->GetTrack()->GetParentID(); - if (auto search = fRememberedTracks.find(trackID); search == fRememberedTracks.end()){ - if (auto search = fRememberedTracks.find(parentID); search != fRememberedTracks.end()){ - if (auto it = std::find(fRememberedProcesses[parentID].begin(),fRememberedProcesses[parentID].end(),creatorProcessName); it != fRememberedProcesses[parentID].end()){ - auto idx = it- fRememberedProcesses[parentID].begin(); - fRememberedTracks[trackID] = {fRememberedTracks[parentID][idx]}; - fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][idx]}; - } - else { - fRememberedTracks[trackID] = {fRememberedTracks[parentID][0]}; - fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][0]}; - - - } - } - } - } -} - - -void GateOptrLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* CurrentStep,G4Track track,const G4Step* step,G4String processName){ - //We retrieve the process associated to the process name to split and we split according the process. - //Since for compton scattering, the gamma is not a secondary particles, this one need to have his own splitting function. - - - G4ParticleDefinition* particleDefinition = track.GetDefinition(); - G4ProcessManager* processManager = particleDefinition->GetProcessManager(); - G4ProcessVector* processList = processManager->GetProcessList(); - auto processToSplit = (*processList)[0]; - for (size_t i = 0; i size(); ++i) { - auto process = (*processList)[i]; - if (process->GetProcessName() == processName){ - processToSplit = process; - } - } - if (processName == "compt"){ - ComptonSplitting(CurrentStep,track,step,processToSplit); - } - else { - SecondariesSplitting(CurrentStep,track,step,processToSplit); - } - -} - - -void GateOptrLastVertexInteractionSplittingActor::ResetProcessesForEnteringParticles(G4Step * step){ - -// This function reset the processes and track registered to be split each time a particle enters into a volume. -// Here a new particle incoming is either a particle entering into the volume ( first pre step is a boundary of a mother volume -// or first step trigger the actor and the vertex is not either the mother volume or a the fdaughter volume) or an event generated within -// the volume (the particle did not enter into the volume and its parentID = 0). An additionnal condition is set with the trackID to ensure -// particles crossing twice the volume (after either a compton or pair prod) are not splitted. - - -G4int trackID = step->GetTrack()->GetTrackID(); -if ((fEventIDOfInitialSplittedTrack != fEventID) || ((fEventIDOfInitialSplittedTrack == fEventID ) && (trackID < fTrackIDOfInitialTrack))) - { - G4String logicalVolumeNamePreStep = "None"; - if (step->GetPreStepPoint()->GetPhysicalVolume() !=0){ - logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - } - if (((step->GetPreStepPoint()->GetStepStatus() == 1) && (logicalVolumeNamePreStep == fMotherVolumeName)) || - ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logicalVolumeNamePreStep) && (step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() !=fMotherVolumeName))) { - fTrackIDOfInitialTrack = trackID; - fEventIDOfInitialSplittedTrack = fEventID; - fRememberedProcesses.clear(); - fRememberedTracks.clear(); - - } - else if (step->GetTrack()->GetParentID() == 0){ - fTrackIDOfInitialTrack = trackID; - fEventIDOfInitialSplittedTrack = fEventID; - fRememberedProcesses.clear(); - fRememberedTracks.clear(); - } - } - } - - -void GateOptrLastVertexInteractionSplittingActor::PostponeFirstAnnihilationTrackIfInteraction(G4Step* step,G4String processName){ - //In case the first gamma issued fromannihilation undergoes an interaction, in order to not bias the process - //We keep in memory the particle post step state (with its secondaries) and kill the particle and its secondaries. - //If the second photon from annihilation exiting the collimation system with an interaction or is absorbed within - //the collimation, the particle is subsequently resimulated, starting from the interaction point. - - - G4int trackID = step->GetTrack()->GetTrackID(); - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(),processName) != fListOfProcesses.end()){ - fSuspendForAnnihil = true; - G4Track trackToPostpone = G4Track(*(step->GetTrack())); - trackToPostpone.SetKineticEnergy(step->GetPostStepPoint()->GetKineticEnergy()); - trackToPostpone.SetMomentumDirection(step->GetPostStepPoint()->GetMomentumDirection()); - trackToPostpone.SetTrackStatus(step->GetTrack()->GetTrackStatus()); - trackToPostpone.SetPolarization(step->GetPostStepPoint()->GetPolarization()); - trackToPostpone.SetPosition(step->GetPostStepPoint()->GetPosition()); - trackToPostpone.SetTrackID(trackID); - fTracksToPostpone.push_back(trackToPostpone); - auto theTrack = fTracksToPostpone[0]; - - - auto secVec = step->GetfSecondary(); - for (int i = 0; i< secVec->size();i++){ - G4Track* sec = (*secVec)[i]; - G4Track copySec = G4Track((*sec)); - fTracksToPostpone.push_back(copySec); - } - - fTracksToPostpone[0].SetTrackID(trackID); - step->GetTrack()->SetTrackStatus( fKillTrackAndSecondaries); - - } -} - -void GateOptrLastVertexInteractionSplittingActor::RegenerationOfPostponedAnnihilationTrack(G4Step* step){ - -//If the second photon from annihilation suceed to exit the collimation system with at least one interaction or was absorbed. -//Resimulation of annihilation photons and its potential secondaries. - -G4TrackVector* currentSecondaries = step->GetfSecondary(); -for (int i =0; i < fTracksToPostpone.size();i ++){ - G4Track* trackToAdd = new G4Track(fTracksToPostpone[fTracksToPostpone.size() - 1 - i]); - trackToAdd->SetParentID(step->GetTrack()->GetTrackID() - 1); - - currentSecondaries->insert(currentSecondaries->begin(),trackToAdd); -} -G4Track* firstPostponedTrack = (*currentSecondaries)[0]; - -// Handle of case were the interaction killed the photon issued from annihilation, it will not be track at the following state -// and the boolean plus the track vector need to be reset - -if (firstPostponedTrack->GetTrackStatus() == 2){ - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); -} - -} - -void GateOptrLastVertexInteractionSplittingActor::HandleTrackIDIfPostponedAnnihilation(G4Step* step){ -// The ID is to modify trackID and the processes and tracks sotcked for a specific trackID associated to -// the postponed annihilation to respect the trackID order in GEANT4. Since in this case the second photon is tracked before the first one -// his trackID +=1. For the postponed one, he is the first secondary particle of the second photon tracks, his trackID is therefore equal -// to the second photon trackID +1 instead of the second photon trackID -1. -// The parentID of secondary particles are also modified because they are used in the rememberlastprocess function -// At last the value associated to a specific trackID in the process and track map are modified according to the new trackID. - - - - if (fSuspendForAnnihil){ - if (step->GetTrack()->GetTrackID() == fTracksToPostpone[0].GetTrackID() -1){ - fRememberedProcesses[step->GetTrack()->GetTrackID()] = fRememberedProcesses[step->GetTrack()->GetTrackID() + 1]; - fRememberedProcesses.erase(step->GetTrack()->GetTrackID() + 1); - fRememberedTracks[step->GetTrack()->GetTrackID()] = fRememberedTracks[step->GetTrack()->GetTrackID() + 1]; - fRememberedTracks.erase(step->GetTrack()->GetTrackID() + 1); - auto vecSec = step->GetSecondary(); - for (int i =0; i size(); i++) - { - (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() +1); - } - step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() +1); - } - if (step->GetTrack()->GetTrackID() == fTracksToPostpone[0].GetTrackID() + 1){ - auto vecSec = step->GetSecondary(); - for (int i =0; i size(); i++) - { - (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() -2); - } - step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() - 2); - } - } -} - - -void GateOptrLastVertexInteractionSplittingActor::BeginOfRunAction(const G4Run* run){ - - // The way to behave of the russian roulette is the following : - // we provide a vector director and the theta angle acceptance, where theta = - // 0 is a vector colinear to the vector director Then if the track generated - // is on the acceptance angle, we add it to the primary track, and if it's not - // the case, we launch the russian roulette - - - if (fRotationVectorDirector) { - G4VPhysicalVolume *physBiasingVolume = - G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); - auto rot = physBiasingVolume->GetObjectRotationValue(); - fVectorDirector = rot * fVectorDirector; - } -} - - - - - -void GateOptrLastVertexInteractionSplittingActor::BeginOfEventAction(const G4Event* event){ - - - fParentID = -1; - fIsSplitted = false; - fSecondaries = false; - fEventID = event ->GetEventID(); - fEventIDOfSplittedTrack = -1; - fTrackIDOfSplittedTrack = -1; - -} - - - -void GateOptrLastVertexInteractionSplittingActor::PreUserTrackingAction (const G4Track *track){ - fIsFirstStep = true; -} - - - - - - -void GateOptrLastVertexInteractionSplittingActor::SteppingAction(G4Step* step) { - if (fIsFirstStep) { - HandleTrackIDIfPostponedAnnihilation(step); - - // To be sure to not split a track which has been already splitted, we put a condition on the trackID of - if ((fIsSplitted == true) && ( step->GetTrack()->GetTrackID() < fTrackIDOfSplittedTrack)){ - fIsSplitted = false; - } - - ResetProcessesForEnteringParticles(step); - } - G4int trackID = step->GetTrack()->GetTrackID(); - - - //std::cout<GetTrack()->GetTrackStatus()<<" "<GetPreStepPoint()->GetKineticEnergy()<<" " <GetPostStepPoint()->GetKineticEnergy()<<" "<GetPreStepPoint()->GetPosition()<<" "<GetPostStepPoint()->GetPosition()<GetTrack()->GetCreatorProcess() != 0) - creatorProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); - - - if ((!fSuspendForAnnihil )&& (process != "annihil") && (creatorProcessName == "annihil")){ - PostponeFirstAnnihilationTrackIfInteraction(step,process); - } - - // If the first annihilation photon exit the collimation and the process to split is annihilation - // We kill the second photon, because the annihilation will generate both the photons. - if ((!fSuspendForAnnihil ) && (creatorProcessName == "annihil") && (fTrackIDOfSplittedTrack == step->GetTrack()->GetParentID()) && (fEventID == fEventIDOfSplittedTrack )) - step->GetTrack()->SetTrackStatus(fStopAndKill); - - if (fSuspendForAnnihil){ - if (trackID == fTracksToPostpone[0].GetTrackID() - 1){ - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); - } - } - - - G4String logicalVolumeNamePostStep = "None"; - if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - - if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(),logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { - G4String processToSplit = "None"; - if (auto search = fRememberedProcesses.find(trackID); search != fRememberedProcesses.end()) - processToSplit = fRememberedProcesses[trackID].back(); - - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(),processToSplit) != fListOfProcesses.end()){ - - //Handle of pecularities (1): - - // If the process t split is the gamma issued from compton interaction, the electron primary generated have to be killed - // given that electron will be regenerated - - if ((processToSplit == "compt") && (step->GetTrack()->GetParticleDefinition()->GetParticleName() == "gamma")){ - auto secondaries = step->GetfSecondary(); - if (secondaries->size() > 0){ - G4Track* lastSecTrack = secondaries->back(); - lastSecTrack->SetTrackStatus(fStopAndKill); - } - } - - //Handle of pecularities (2): - - // If the process to split is the annihilation, the second photon, postponed or not, have to be killed - // the reset of the postpone is performed here, whereas the kill of the next annihilation photon, if not postponed - // is realised at the beginning of the step tracking. - - - if (processToSplit == "annihil"){ - if (fSuspendForAnnihil){ - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); - } - } - - - G4Track trackToSplit = fRememberedTracks[trackID].back(); - CreateNewParticleAtTheLastVertex(step,trackToSplit,trackToSplit.GetStep(),processToSplit); - - - fSecondaries = true; - fTrackIDOfSplittedTrack = trackToSplit.GetTrackID(); - fEventIDOfSplittedTrack = fEventID; - fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; - step->GetTrack()->SetTrackStatus(fStopAndKill); - } - } - } - - - if ((fSuspendForAnnihil) && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))){ - if (trackID == fTracksToPostpone[0].GetTrackID()) { - RegenerationOfPostponedAnnihilationTrack(step); - } - } - - fIsFirstStep = false; - } - - - - - -void GateOptrLastVertexInteractionSplittingActor::PostUserTrackingAction (const G4Track *track){ - if (fSecondaries){ - fIsSplitted = true; - fSecondaries = false; - } -} - - - - -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... - - - -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp similarity index 51% rename from core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp rename to core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp index ec54a8077..4a6a8c130 100644 --- a/core/opengate_core/opengate_lib/pyGateOptrLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp @@ -7,15 +7,15 @@ #include namespace py = pybind11; -#include "GateOptrLastVertexInteractionSplittingActor.h" +#include "GateLastVertexInteractionSplittingActor.h" -void init_GateOptrLastVertexInteractionSplittingActor(py::module &m) { +void init_GateLastVertexInteractionSplittingActor(py::module &m) { - py::class_>( - m, "GateOptrLastVertexInteractionSplittingActor") + py::class_>( + m, "GateLastVertexInteractionSplittingActor") .def_readwrite( "fListOfVolumeAncestor", - &GateOptrLastVertexInteractionSplittingActor::fListOfVolumeAncestor) + &GateLastVertexInteractionSplittingActor::fListOfVolumeAncestor) .def(py::init()); } diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 5ae64b3d9..b82ee5c26 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -454,7 +454,7 @@ def __init__(self, user_info): g4.GateOptrComptSplittingActor.__init__(self, user_info.__dict__) -class LastVertexInteractionSplittingActor(g4.GateOptrLastVertexInteractionSplittingActor, ActorBase): +class LastVertexInteractionSplittingActor(g4.GateLastVertexInteractionSplittingActor, ActorBase): type_name = "LastVertexInteractionSplittingActor" def set_default_user_info(user_info): @@ -469,7 +469,7 @@ def set_default_user_info(user_info): def __init__(self, user_info): ActorBase.__init__(self, user_info) - g4.GateOptrLastVertexInteractionSplittingActor.__init__(self, user_info.__dict__) + g4.GateLastVertexInteractionSplittingActor.__init__(self, user_info.__dict__) self.list_of_volume_name = user_info.list_of_volume_name self.user_info.mother = user_info.mother From 098094f39681357bc8df52d4557986a64037a725 Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 19 Jun 2024 14:59:03 +0200 Subject: [PATCH 23/82] Add of a pecularity to handle exiting positron --- ...ateLastVertexInteractionSplittingActor.cpp | 52 +++++++++++++++---- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 3b18610c6..b192c62e8 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -90,6 +90,7 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleC G4double energy = gammaProcess->GetProposedKineticEnergy(); G4double globalTime = track.GetGlobalTime(); G4double newGammaWeight = weight; + G4ThreeVector polarization = gammaProcess->GetProposedPolarization(); const G4ThreeVector momentum = gammaProcess->GetProposedMomentumDirection(); const G4ThreeVector position = track.GetPosition(); G4Track *newTrack = new G4Track(track); @@ -98,6 +99,7 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleC newTrack->SetKineticEnergy(energy); newTrack->SetMomentumDirection(momentum); newTrack->SetPosition(position); + newTrack->SetPolarization(polarization); return newTrack; } @@ -108,6 +110,9 @@ void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step *CurrentSt G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; G4int nCall = (G4int)fSplittingFactor; + if (fSplittingFactor == 1){ + fSplittingFactor = 1.0001; + } for (int i = 0; i < nCall; i++) { GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; @@ -148,6 +153,8 @@ void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step *CurrentSt processFinalState->Clear(); gammaProcessFinalState->Clear(); } + if (fSplittingFactor == 1.0001) + fSplittingFactor = 1; } void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *CurrentStep, G4Track track, const G4Step *step, G4VProcess *process) @@ -157,6 +164,7 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *Curre G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; G4int nCall = (G4int)fSplittingFactor; + for (int i = 0; i < nCall; i++) { G4VParticleChange *processFinalState = nullptr; @@ -189,7 +197,6 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *Curre } else { - std::cout<GetKineticEnergy()<SetWeight(gammaWeight); trackVector->push_back(newTrack); } @@ -274,7 +281,7 @@ void GateLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4S trackInformation->SetMomentumDirection(stepPoint->GetMomentumDirection()); trackInformation->SetTrackStatus(fAlive); trackInformation->SetPolarization(stepPoint->GetPolarization()); - trackInformation->SetTrackID(trackID); + //trackInformation->SetPosition(stepPoint->GetPosition()); if (auto search = fRememberedTracks.find(trackID); search != fRememberedTracks.end()) { @@ -517,20 +524,22 @@ void GateLastVertexInteractionSplittingActor::PreUserTrackingAction(const G4Trac void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { - + + G4String particleName = step->GetTrack()->GetParticleDefinition()->GetParticleName(); G4String creatorProcessName = "None"; if (step->GetTrack()->GetCreatorProcess() != 0) creatorProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); if (fIsFirstStep) { - HandleTrackIDIfPostponedAnnihilation(step); ResetProcessesForEnteringParticles(step); } G4int trackID = step->GetTrack()->GetTrackID(); - + //std::cout<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetWeight()<GetTrack()->GetWeight() >= fWeightOfEnteringParticle) { RememberLastProcessInformation(step); @@ -546,6 +555,10 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); } + if ((particleName == "e-") && (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) && (creatorProcessName == "conv") && (fTrackIDOfSplittedTrack == step->GetTrack()->GetParentID()) && (fEventID == fEventIDOfSplittedTrack)){ + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + if ((!fSuspendForAnnihil) && (process != "annihil") && (creatorProcessName == "annihil") && (step->GetTrack()->GetTrackStatus() != 3)) { G4int parentID = step->GetTrack()->GetParentID(); @@ -574,19 +587,25 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) if (((step->GetTrack()->GetTrackStatus() != 2) && (step->GetTrack()->GetTrackStatus() != 3)) && (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) { G4String processToSplit = "None"; - if (auto search = fRememberedProcesses.find(trackID); search != fRememberedProcesses.end()) + G4Track* trackToSplit = nullptr; + G4Step* stepToSplit = nullptr; + + if (auto search = fRememberedProcesses.find(trackID); search != fRememberedProcesses.end()){ processToSplit = fRememberedProcesses[trackID].back(); + trackToSplit = fRememberedTracks[trackID].back(); + stepToSplit = fRememberedSteps[trackID].back(); + } + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processToSplit) != fListOfProcesses.end()) { - std::cout<GetTrack()->GetKineticEnergy()<GetTrack()->GetParticleDefinition()->GetParticleName() == "gamma")) + if ((processToSplit == "compt") && (particleName == "gamma")) { auto secondaries = step->GetfSecondary(); if (secondaries->size() > 0) @@ -610,9 +629,20 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) fTracksToPostpone.clear(); } } + // Handle of pecularities 3 : If the positron which created one or more brem photons exits + // all the brems photons will be killed before their tracking, and the conv processes will then be replayed + + if ((particleName == "e+") &&(processToSplit != "None")) { + G4int parentID = step->GetTrack()->GetParentID(); + processToSplit = fRememberedProcesses[parentID].back(); + trackToSplit = fRememberedTracks[parentID].back(); + stepToSplit = fRememberedSteps[parentID].back(); + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + } + + + - G4Track *trackToSplit = fRememberedTracks[trackID].back(); - G4Step *stepToSplit = fRememberedSteps[trackID].back(); CreateNewParticleAtTheLastVertex(step, *trackToSplit, stepToSplit, processToSplit); fTrackIDOfSplittedTrack = trackToSplit->GetTrackID(); @@ -634,6 +664,8 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) fIsFirstStep = false; } + + void GateLastVertexInteractionSplittingActor::PostUserTrackingAction(const G4Track *track) { } From 244e6d9ab6d2dc648a87e1fb0826153ad82a6109 Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 25 Jun 2024 11:40:24 +0200 Subject: [PATCH 24/82] change the way to split, the old one being biased, work in progress --- ...ateLastVertexInteractionSplittingActor.cpp | 284 +++++++++++------- .../GateLastVertexInteractionSplittingActor.h | 13 +- .../GateLastVertexSplittingPostStepDoIt.h | 157 +++++++++- .../src/test076_last_vertex_splittting.py | 22 +- .../test076_last_vertex_splittting_distrib.py | 234 +++++++++++++++ 5 files changed, 590 insertions(+), 120 deletions(-) create mode 100755 opengate/tests/src/test076_last_vertex_splittting_distrib.py diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index b192c62e8..2f6d125e9 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -39,11 +39,13 @@ #include "G4PhysicalVolumeStore.hh" #include "G4ProcessManager.hh" #include "G4TrackingManager.hh" +#include "G4TrackStatus.hh" #include "G4ProcessVector.hh" #include "GateOptnComptSplitting.h" #include "GateLastVertexInteractionSplittingActor.h" #include "GateLastVertexSplittingPostStepDoIt.h" #include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -103,44 +105,25 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleC return newTrack; } -void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step *CurrentStep, G4Track track, const G4Step *step, G4VProcess *process) +void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step *CurrentStep, G4Track *track, const G4Step *step, G4VProcess *process) { // Loop on process and add the secondary tracks to the current step secondary vector G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; - G4int nCall = (G4int)fSplittingFactor; - if (fSplittingFactor == 1){ - fSplittingFactor = 1.0001; - } - for (int i = 0; i < nCall; i++) - { - GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - G4VParticleChange *processFinalState = emProcess->PostStepDoIt(track, *step); - G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; - const G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); - gammaWeight = CurrentStep->GetTrack()->GetWeight() / fSplittingFactor; - if (fRussianRouletteForAngle == true) - { - G4double weightToApply = RussianRouletteForAngleSurvival(momentum, fVectorDirector, fMaxTheta, fSplittingFactor); - if (weightToApply != 0) - { - gammaWeight = gammaWeight * weightToApply; - G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, track, gammaWeight); - trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) - { - G4Track *newTrack = processFinalState->GetSecondary(j); - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - } - } - else + GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; + G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*track, *step); + G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; + const G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); + gammaWeight = fWeightOfEnteringParticle/ fSplittingFactor; + if (fRussianRouletteForAngle == true) + { + G4double weightToApply = RussianRouletteForAngleSurvival(momentum, fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0) { - G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, track, gammaWeight); + gammaWeight = gammaWeight * weightToApply; + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *track, gammaWeight); trackVector->push_back(newTrack); G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); for (int j = 0; j < NbOfSecondaries; j++) @@ -150,59 +133,74 @@ void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step *CurrentSt trackVector->push_back(newTrack); } } - processFinalState->Clear(); - gammaProcessFinalState->Clear(); } - if (fSplittingFactor == 1.0001) - fSplittingFactor = 1; + + else + { + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *track, gammaWeight); + trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) + { + G4Track *newTrack = processFinalState->GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } + processFinalState->Clear(); + gammaProcessFinalState->Clear(); + + } -void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *CurrentStep, G4Track track, const G4Step *step, G4VProcess *process) +void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *CurrentStep, G4Track* track, const G4Step *step, G4VProcess *process) { // Loop on process and add the secondary tracks to the current step secondary vector. - G4String particleName = track.GetParticleDefinition()->GetParticleName(); + + G4String particleName = track->GetParticleDefinition()->GetParticleName(); G4TrackVector *trackVector = CurrentStep->GetfSecondary(); - G4double gammaWeight = 0; - G4int nCall = (G4int)fSplittingFactor; - - for (int i = 0; i < nCall; i++) + G4double gammaWeight = 0; + G4VParticleChange *processFinalState = nullptr; + if (process->GetProcessName() == "eBrem") { - G4VParticleChange *processFinalState = nullptr; - - if (process->GetProcessName() == "eBrem") - { - GateBremPostStepDoIt *bremProcess = (GateBremPostStepDoIt *)process; - processFinalState = bremProcess->GateBremPostStepDoIt::PostStepDoIt(track, *step); - } - else - { - GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - processFinalState = emProcess->PostStepDoIt(track, *step); + GateBremPostStepDoIt *bremProcess = (GateBremPostStepDoIt *)process; + processFinalState = bremProcess->GateBremPostStepDoIt::PostStepDoIt(*track, *step); + } + else + { + GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *) process ; + if (track->GetTrackStatus() == 0) + processFinalState = emProcess->PostStepDoIt(*track, *step); + if (track->GetTrackStatus() == 1){ + GateplusannihilAtRestDoIt* eplusAnnihilProcess = (GateplusannihilAtRestDoIt*) process; + eplusAnnihilProcess ->GateplusannihilAtRestDoIt::AtRestDoIt(*track, *step); } - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) + } + + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + if (NbOfSecondaries > 0) { + gammaWeight = fWeightOfEnteringParticle/fSplittingFactor; + G4Track *newTrack = processFinalState->GetSecondary(0); + std::cout<GetKineticEnergy()<GetTrack()->GetWeight() / fSplittingFactor; - G4Track *newTrack = processFinalState->GetSecondary(j); - if ((fRussianRouletteForAngle == true) && (particleName == "gamma")) - { - const G4ThreeVector momentum = newTrack->GetMomentumDirection(); - G4double weightToApply = RussianRouletteForAngleSurvival(momentum, fVectorDirector, fMaxTheta, fSplittingFactor); - if (weightToApply != 0) - { - gammaWeight = gammaWeight * weightToApply; - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - } - else + const G4ThreeVector momentum = newTrack->GetMomentumDirection(); + G4double weightToApply = RussianRouletteForAngleSurvival(momentum, fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0) { + gammaWeight = gammaWeight * weightToApply; newTrack->SetWeight(gammaWeight); trackVector->push_back(newTrack); } } - processFinalState->Clear(); + else + { + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } } + processFinalState->Clear(); + } void GateLastVertexInteractionSplittingActor::ClearRememberedTracksAndSteps(std::map rememberedTracks, std::map> rememberedSteps) @@ -213,6 +211,7 @@ void GateLastVertexInteractionSplittingActor::ClearRememberedTracksAndSteps(std: std::vector vector = it->second; for (auto it2 = vector.begin(); it2 != vector.end(); it2++){ if ((std::find(trackToKill.begin(), trackToKill.end(), *it2) == trackToKill.end())){ + //Method set pour clear les doublons au lieu de find. trackToKill.push_back(*it2); } } @@ -279,7 +278,11 @@ void GateLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4S trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy()); trackInformation->SetMomentumDirection(stepPoint->GetMomentumDirection()); - trackInformation->SetTrackStatus(fAlive); + if ((processName =="annihil") && (step->GetTrack()->GetKineticEnergy() == 0)) + trackInformation->SetTrackStatus(fStopButAlive); + else{ + trackInformation->SetTrackStatus(fAlive); + } trackInformation->SetPolarization(stepPoint->GetPolarization()); //trackInformation->SetPosition(stepPoint->GetPosition()); @@ -322,14 +325,14 @@ void GateLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4S } } -void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step *CurrentStep, G4Track track, const G4Step *step, G4String processName) +void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step *CurrentStep, G4Track* track, const G4Step *step, G4String processName) { // We retrieve the process associated to the process name to split and we split according the process. // Since for compton scattering, the gamma is not a secondary particles, this one need to have his own splitting function. - - G4ParticleDefinition *particleDefinition = track.GetDefinition(); + G4ParticleDefinition *particleDefinition = track->GetDefinition(); G4ProcessManager *processManager = particleDefinition->GetProcessManager(); G4ProcessVector *processList = processManager->GetProcessList(); + G4VProcess *processToSplit = nullptr; for (size_t i = 0; i < processList->size(); ++i) { @@ -341,12 +344,15 @@ void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G } if (processName == "compt") { + ComptonSplitting(CurrentStep, track, step, processToSplit); } - else + + else if (processName != "eBrem") { SecondariesSplitting(CurrentStep, track, step, processToSplit); } + } void GateLastVertexInteractionSplittingActor::ResetProcessesForEnteringParticles(G4Step *step) @@ -511,7 +517,7 @@ void GateLastVertexInteractionSplittingActor::BeginOfEventAction(const G4Event * fParentID = -1; fEventID = event->GetEventID(); - if (fEventID%30000 == 0) + //if (fEventID%30000 == 0) std::cout <GetTrack()->GetParticleDefinition()->GetParticleName(); G4String creatorProcessName = "None"; if (step->GetTrack()->GetCreatorProcess() != 0) @@ -532,15 +537,84 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) if (fIsFirstStep) { - HandleTrackIDIfPostponedAnnihilation(step); + //HandleTrackIDIfPostponedAnnihilation(step); ResetProcessesForEnteringParticles(step); } G4int trackID = step->GetTrack()->GetTrackID(); - //std::cout<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetWeight()<GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + + + G4String processName = "None"; + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) + processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processName) != fListOfProcesses.end()){ + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + + + + if (step->GetTrack()->GetTrackStatus() == 2){ + delete fTrackToSplit; + delete fStepToSplit; + fTrackToSplit = new G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); + fStepToSplit = new G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); + std::cout <<"IN"<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTrack()->GetTrackStatus()<<" "< 27666){ + std::cout << fIsSplitted<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTrack()->GetTrackStatus()<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTrack()->GetTrackStatus()<GetTrack()->GetTrackStatus() != 2) && (step->GetTrack()->GetTrackStatus() != 3)) && (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) + { + + if (fSplitCounter < fWeightOfEnteringParticle) { + delete fTrackToSplit; + delete fStepToSplit; + fTrackToSplit = new G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); + fStepToSplit = new G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); + CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, fProcessToSplit); + //std::cout<GetfSecondary(); + G4Track* theTrack= trackVector->back(); + fSplitCounter += theTrack->GetWeight(); + if (fEventID > 27666){ + std::cout << fSplitCounter<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTrack()->GetTrackStatus()<GetWeight()<= fWeightOfEnteringParticle) { + delete fTrackToSplit; + delete fStepToSplit; + fTrackToSplit = nullptr; + fStepToSplit = nullptr; + //std::cout<<"lol"<SetTrackStatus(fStopAndKill); + } + } + } + + + } - if (step->GetTrack()->GetWeight() >= fWeightOfEnteringParticle) + if (fIsSplitted == false) { RememberLastProcessInformation(step); G4String process = "None"; @@ -550,15 +624,19 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) } // std::cout<GetTrack()->GetVertexPosition()<<" "<GetTrack()->GetParticleDefinition()->GetParticleName()<<" "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" "<GetTrack()->GetTrackID()<<" "<GetTrack()->GetTrackStatus()<<" "<GetPreStepPoint()->GetKineticEnergy()<<" " <GetPostStepPoint()->GetKineticEnergy()<<" "<GetPreStepPoint()->GetPosition()<<" "<GetPostStepPoint()->GetPosition()<GetTrack()->GetParentID()) && (fEventID == fEventIDOfSplittedTrack)) { step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); } + */ if ((particleName == "e-") && (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) && (creatorProcessName == "conv") && (fTrackIDOfSplittedTrack == step->GetTrack()->GetParentID()) && (fEventID == fEventIDOfSplittedTrack)){ step->GetTrack()->SetTrackStatus(fStopAndKill); } + + /* if ((!fSuspendForAnnihil) && (process != "annihil") && (creatorProcessName == "annihil") && (step->GetTrack()->GetTrackStatus() != 3)) { G4int parentID = step->GetTrack()->GetParentID(); @@ -568,6 +646,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) PostponeFirstAnnihilationTrackIfInteraction(step, process); } } + // If the first annihilation photon exit the collimation and the process to split is annihilation // We kill the second photon, because the annihilation will generate both the photons. @@ -579,33 +658,33 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) fTracksToPostpone.clear(); } } - + */ G4String logicalVolumeNamePostStep = "None"; if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); if (((step->GetTrack()->GetTrackStatus() != 2) && (step->GetTrack()->GetTrackStatus() != 3)) && (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) { - G4String processToSplit = "None"; - G4Track* trackToSplit = nullptr; - G4Step* stepToSplit = nullptr; if (auto search = fRememberedProcesses.find(trackID); search != fRememberedProcesses.end()){ - processToSplit = fRememberedProcesses[trackID].back(); - trackToSplit = fRememberedTracks[trackID].back(); - stepToSplit = fRememberedSteps[trackID].back(); + fProcessToSplit = fRememberedProcesses[trackID].back(); + fTrackToSplit = new G4Track(*fRememberedTracks[trackID].back()); + fStepToSplit = new G4Step(*fRememberedSteps[trackID].back()); } - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processToSplit) != fListOfProcesses.end()) + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), fProcessToSplit) != fListOfProcesses.end()) { + fTrackIDOfSplittedTrack = trackID; + fEventIDOfSplittedTrack = fEventID; + fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; // Handle of pecularities (1): // If the process t split is the gamma issued from compton interaction, the electron primary generated have to be killed // given that electron will be regenerated - if ((processToSplit == "compt") && (particleName == "gamma")) + if ((fProcessToSplit == "compt") && (particleName == "gamma")) { auto secondaries = step->GetfSecondary(); if (secondaries->size() > 0) @@ -620,8 +699,8 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) // If the process to split is the annihilation, the second photon, postponed or not, have to be killed // the reset of the postpone is performed here, whereas the kill of the next annihilation photon, if not postponed // is realised at the beginning of the step tracking. - - if (processToSplit == "annihil") + /* + if (fProcessToSplit == "annihil") { if (fSuspendForAnnihil) { @@ -629,30 +708,30 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) fTracksToPostpone.clear(); } } + */ // Handle of pecularities 3 : If the positron which created one or more brem photons exits // all the brems photons will be killed before their tracking, and the conv processes will then be replayed - if ((particleName == "e+") &&(processToSplit != "None")) { + if ((particleName == "e+") &&(fProcessToSplit != "None")) { G4int parentID = step->GetTrack()->GetParentID(); - processToSplit = fRememberedProcesses[parentID].back(); - trackToSplit = fRememberedTracks[parentID].back(); - stepToSplit = fRememberedSteps[parentID].back(); + fProcessToSplit = fRememberedProcesses[parentID].back(); + delete fTrackToSplit; + delete fStepToSplit; + fTrackToSplit = new G4Track(*fRememberedTracks[parentID].back()); + fStepToSplit = new G4Step(*fRememberedSteps[parentID].back()); step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + fTrackIDOfInitialTrack = parentID; } - - - - CreateNewParticleAtTheLastVertex(step, *trackToSplit, stepToSplit, processToSplit); - - fTrackIDOfSplittedTrack = trackToSplit->GetTrackID(); - fEventIDOfSplittedTrack = fEventID; - fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; + fIsSplitted = true; + + CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, fProcessToSplit); step->GetTrack()->SetTrackStatus(fStopAndKill); } } } + /* if ((fSuspendForAnnihil) && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))) { if (trackID == fTracksToPostpone[0].GetTrackID()) @@ -660,6 +739,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) RegenerationOfPostponedAnnihilationTrack(step); } } + */ fIsFirstStep = false; } diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index b79c5cc7a..e8877ae4b 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -59,6 +59,13 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4bool fIsFirstStep = true; G4bool fSuspendForAnnihil = false; G4double fWeightOfEnteringParticle = 0; + G4double fSplitCounter = 0; + G4bool fIsSplitted = false; + + G4Track* fTrackToSplit = nullptr; + G4Step* fStepToSplit = nullptr; + G4String fProcessToSplit = "None"; + std::vector fTracksToPostpone; std::map fRememberedTracks; @@ -78,13 +85,13 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { //Pure splitting functions G4double RussianRouletteForAngleSurvival(G4ThreeVector, G4ThreeVector, G4double, G4double); G4Track* CreateComptonTrack(G4ParticleChangeForGamma*,G4Track, G4double); - void ComptonSplitting(G4Step* CurrentStep,G4Track track,const G4Step* step,G4VProcess* process); - void SecondariesSplitting(G4Step* CurrentStep,G4Track track,const G4Step* step,G4VProcess* process); + void ComptonSplitting(G4Step* CurrentStep,G4Track* track,const G4Step* step,G4VProcess* process); + void SecondariesSplitting(G4Step* CurrentStep,G4Track* track,const G4Step* step,G4VProcess* process); //Handling the remembered processes to replay void RememberLastProcessInformation(G4Step*); - void CreateNewParticleAtTheLastVertex(G4Step*,G4Track,const G4Step*,G4String); + void CreateNewParticleAtTheLastVertex(G4Step*,G4Track*,const G4Step*,G4String); void ResetProcessesForEnteringParticles(G4Step * step); void ClearRememberedTracksAndSteps(std::map, std::map>); diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h index d7880138a..22b75a0f8 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h @@ -31,6 +31,18 @@ #include "G4VEnergyLossProcess.hh" #include "G4VEmProcess.hh" #include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "G4PhysicalConstants.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4Gamma.hh" +#include "G4Electron.hh" +#include "G4Positron.hh" +#include "G4eeToTwoGammaModel.hh" +#include "G4EmBiasingManager.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilationEntanglementClipBoard.hh" +#include "G4EmParameters.hh" +#include "G4PhysicsModelCatalog.hh" #include @@ -62,14 +74,155 @@ GateGammaEmPostStepDoIt(); ~ GateGammaEmPostStepDoIt(); -virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & step) override +virtual G4VParticleChange * PostStepDoIt(const G4Track & track, const G4Step & step) override { const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); - currentCouple = couple; + + std::cout<GetParticleName()<GetParticleName()<GetMaterialCutsCouple()); + G4int idx = (G4int)CurrentMaterialCutsCoupleIndex(); + G4double ene(0.0); + G4VEmModel* model = SelectModel(ene, idx); + + // define new weight for primary and secondaries + G4double weight = fParticleChange.GetParentWeight(); + + // sample secondaries + secParticles.clear(); + G4double gammaCut = GetGammaEnergyCut(); + model->SampleSecondaries(&secParticles, MaterialCutsCouple(), + track.GetDynamicParticle(), gammaCut); + + G4int num0 = (G4int)secParticles.size(); + // splitting or Russian roulette + if(biasManager) { + if(biasManager->SecondaryBiasingRegion(idx)) { + G4double eloss = 0.0; + weight *= biasManager->ApplySecondaryBiasing( + secParticles, track, model, &fParticleChange, eloss, + idx, gammaCut, step.GetPostStepPoint()->GetSafety()); + if(eloss > 0.0) { + eloss += fParticleChange.GetLocalEnergyDeposit(); + fParticleChange.ProposeLocalEnergyDeposit(eloss); + } + } + } + + // save secondaries + G4int num = (G4int)secParticles.size(); + + // Check that entanglement is switched on... (the following flag is + // set by /process/em/QuantumEntanglement). + G4bool entangled = G4EmParameters::Instance()->QuantumEntanglement(); + // ...and that we have two gammas with both gammas' energies above + // gammaCut (entanglement is only programmed for e+ e- -> gamma gamma). + G4bool entangledgammagamma = false; + if (entangled) { + if (num == 2) { + entangledgammagamma = true; + for (const auto* p: secParticles) { + if (p->GetDefinition() != G4Gamma::Gamma() || + p->GetKineticEnergy() < gammaCut) { + entangledgammagamma = false; + } + } + } + } + + // Prepare a shared pointer for psossible use below. If it is used, the + // shared pointer is copied into the tracks through G4EntanglementAuxInfo. + // This ensures the clip board lasts until both tracks are destroyed. + std::shared_ptr clipBoard; + if (entangledgammagamma) { + clipBoard = std::make_shared(); + clipBoard->SetParentParticleDefinition(track.GetDefinition()); + } + + if(num > 0) { + fParticleChange.SetNumberOfSecondaries(num); + G4double edep = fParticleChange.GetLocalEnergyDeposit(); + G4double time = track.GetGlobalTime(); + + for (G4int i=0; iGetParticleDefinition(); + G4double e = dp->GetKineticEnergy(); + G4bool good = true; + if(ApplyCuts()) { + if (p == G4Gamma::Gamma()) { + if (e < gammaCut) { good = false; } + } else if (p == G4Electron::Electron()) { + if (e < GetElectronEnergyCut()) { good = false; } + } + // added secondary if it is good + } + if (good) { + G4Track* t = new G4Track(dp, time, track.GetPosition()); + t->SetTouchableHandle(track.GetTouchableHandle()); + if (entangledgammagamma) { + // entangledgammagamma is only true when there are only two gammas + // (See code above where entangledgammagamma is calculated.) + if (i == 0) { // First gamma + clipBoard->SetTrackA(t); + } else if (i == 1) { // Second gamma + clipBoard->SetTrackB(t); + } + t->SetAuxiliaryTrackInformation + (G4PhysicsModelCatalog::GetModelID("model_GammaGammaEntanglement"),new G4EntanglementAuxInfo(clipBoard)); + } + if (biasManager) { + t->SetWeight(weight * biasManager->GetWeight(i)); + } else { + t->SetWeight(weight); + } + pParticleChange->AddSecondary(t); + + // define type of secondary + if(i < mainSecondaries) { t->SetCreatorModelID(secID); } + else if(i < num0) { + if(p == G4Gamma::Gamma()) { + t->SetCreatorModelID(fluoID); + } else { + t->SetCreatorModelID(augerID); + } + } else { + t->SetCreatorModelID(biasID); + } + /* + G4cout << "Secondary(post step) has weight " << t->GetWeight() + << ", Ekin= " << t->GetKineticEnergy()/MeV << " MeV " + << GetProcessName() << " fluoID= " << fluoID + << " augerID= " << augerID < 2 * ( + 1 - tol_weights + ) and np.round(np.sum(weights), 4) < 2 * (1 + tol_weights) + condition_len = len(weights) > 2 * nb_split * (1 - tol_weights) and len( + weights + ) < 2 * nb_split * (1 + tol_weights) + bool_weights = condition_weights and condition_len + keys = ["KineticEnergy", "PreDirection_X", "PreDirection_Y", "PreDirection_Z"] + + arr_ref_phot = arr_ref[arr_ref["ParticleName"] == "gamma"] + arr_ref_elec = arr_ref[arr_ref["ParticleName"] == "e-"] + + arr_data_phot = arr_data[arr_data["ParticleName"] == "gamma"] + arr_data_elec = arr_data[arr_data["ParticleName"] == "e-"] + + keys_dico = ["ref", "data"] + dico_arr_phot = {} + dico_arr_elec = {} + + dico_arr_phot["ref"] = arr_ref_phot + dico_arr_phot["data"] = arr_data_phot + + dico_arr_elec["ref"] = arr_ref_elec + dico_arr_elec["data"] = arr_data_elec + dico_comp_data = {} + + for key in keys: + arr_data = [] + for key_dico in keys_dico: + mean_elec = np.mean(dico_arr_phot[key_dico][key]) + mean_phot = np.mean(dico_arr_elec[key_dico][key]) + std_elec = np.std(dico_arr_phot[key_dico][key]) + std_phot = np.std(dico_arr_elec[key_dico][key]) + arr_data += [mean_elec, mean_phot, std_elec, std_phot] + dico_comp_data[key] = 100 * np.abs( + np.array( + [ + (arr_data[0] - arr_data[4]) / arr_data[0], + (arr_data[1] - arr_data[5]) / arr_data[1], + (arr_data[2] - arr_data[6]) / arr_data[6], + (arr_data[3] - arr_data[7]) / arr_data[3], + ] + ) + ) + bool_test = bool_validation_test(dico_comp_data, tol) + bool_tot = bool_test and bool_weights and bool_val_weights + return bool_tot + + +if __name__ == "__main__": + paths = utility.get_default_test_paths( + __file__, "test076_last_vertex_splitting", output_folder="test076" + ) + for simu_ID in range(1,2) : + # create the simulation + sim = gate.Simulation() + + # main options + ui = sim.user_info + ui.g4_verbose = False + # ui.visu = True + ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.logger.EVENT + ui.number_of_threads = 1 + # 1236566 seg fault + ui.random_seed = 1234567 + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + um = gate.g4_units.um + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + deg = gate.g4_units.deg + gcm3 = gate.g4_units.g / gate.g4_units.cm3 + + # adapt world size + world = sim.world + world.size = [0.25 * m, 0.25 * m, 1.5 * m] + world.material = "G4_Galactic" + + ####### GEOMETRY TO IRRADIATE ############# + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + + W_tubs = sim.add_volume("Tubs", "W_box") + W_tubs.material = "Tungsten" + W_tubs.mother = world.name + + W_tubs.rmin = 0 + W_tubs.rmax = 1* cm + W_tubs.dz = 0.5 * m + W_tubs.color = [0.8, 0.2, 0.1, 1] + angle_x = 0 + angle_y = 0 + angle_z = 0 + + rotation = Rotation.from_euler( + "xyz", [angle_y, angle_y, angle_z], degrees=True + ).as_matrix() + W_tubs.rotation = rotation + if simu_ID == 1 : + ####### Last vertex splitting ACTOR ######### + nb_split = 1 + vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") + vertex_splitting_actor.mother = W_tubs.name + vertex_splitting_actor.splitting_factor = nb_split + vertex_splitting_actor.russian_roulette_for_angle = False + + ##### PHASE SPACE plan ######" + plan_tubs = sim.add_volume("Tubs", "phsp_tubs") + plan_tubs.material = "G4_Galactic" + plan_tubs.mother = world.name + plan_tubs.rmin = W_tubs.rmax + 1*cm + plan_tubs.rmax = plan_tubs.rmin + 1 * nm + plan_tubs.dz = 0.05 * m + plan_tubs.color = [0.2, 1, 0.8, 1] + plan_tubs.rotation = rotation + + ####### Electron source ########### + source = sim.add_source("GenericSource", "source1") + source.particle = "gamma" + source.n = 1000000 + if simu_ID == 1 : + source.n = source.n/(nb_split) + source.position.type = "sphere" + source.position.radius = 1 * nm + source.direction.type = "momentum" + # source.direction.momentum = [0,0,-1] + source.direction.momentum = np.dot(rotation, np.array([0, 0, -1])) + source.energy.type = "mono" + source.position.translation = [0.4*cm,0.4*cm,0] + source.force_rotation=True + source.energy.mono = 4* MeV + + ####### PHASE SPACE ACTOR ############## + + phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") + phsp_actor.mother = plan_tubs.name + phsp_actor.attributes = [ + "EventID", + "TrackID", + "Weight", + "ParticleName", + "KineticEnergy", + "PreDirection", + "TrackCreatorProcess", + ] + if simu_ID == 0: + phsp_actor.output = paths.output / "test076_output_data_ref.root" + else : + phsp_actor.output = paths.output / "test076_output_data_split.root" + + ##### MODIFIED PHYSICS LIST ############### + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option2" + #### Extremely important, it seems that GEANT4, for almost all physics lists, encompass all the photon processes in GammaGeneralProc + #### Therefore if we provide the name of the real process (here compt) without deactivating GammaGeneralProcess, it will not find the + #### process to bias and the biasing will fail + s = f"/process/em/UseGeneralProcess false" + sim.add_g4_command_before_init(s) + + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * m + sim.physics_manager.global_production_cuts.positron = 1 * um + + output = sim.run() + + # + # print results + stats = sim.output.get_actor("Stats") + h = sim.output.get_actor("PhaseSpace") + print(stats) + + From c71c7d649f244caeedecde9b65cae27212cd1bac Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 25 Jun 2024 13:28:58 +0200 Subject: [PATCH 25/82] Correction of Russian roulette bug --- .../opengate_lib/GateOptnVGenericSplitting.cpp | 2 +- .../GateOptrComptPseudoTransportationActor.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp index 349a393ff..d69496bd3 100644 --- a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp @@ -94,7 +94,7 @@ if (theta > fMaxTheta){ weightToApply = split; } else{ - G4double weightToApply = 0; + weightToApply = 0; } } return weightToApply; diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 141d73b77..96d17099f 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -52,6 +52,7 @@ #include "GateOptrComptPseudoTransportationActor.h" #include "G4UImanager.hh" #include "G4eplusAnnihilation.hh" +#include "GateOptnVGenericSplitting.h" //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -163,12 +164,14 @@ void GateOptrComptPseudoTransportationActor::StartRun() { void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { + + if ((isSplitted == true) && (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { G4String LogicalVolumeName = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end()) && (LogicalVolumeName != fMotherVolumeName)) { - step->GetTrack()->SetTrackStatus(G4TrackStatus::fStopAndKill); + step->GetTrack()->SetTrackStatus(fStopAndKill); isSplitted = false; } } @@ -262,7 +265,7 @@ GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( if (!(std::find(fCreationProcessNameList.begin(), fCreationProcessNameList.end(),CreationProcessName) != fCreationProcessNameList.end())){ - if ((callingProcess->GetWrappedProcess()->GetProcessName() == "compt") || (callingProcess->GetWrappedProcess()->GetProcessName() == "Rayl")){ + if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt"){ isSplitted = true; return fScatteredGammaSplittingOperation; } From ca0de09edaf4694b9841069520b9b73b2385a143 Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 25 Jun 2024 15:32:34 +0200 Subject: [PATCH 26/82] Add russian roulette for annihilation photons --- .../GateOptnVGenericSplitting.cpp | 2 +- .../opengate_lib/GateOptnVGenericSplitting.h | 2 +- ...GateOptrComptPseudoTransportationActor.cpp | 51 ++++++++++++------- .../GateOptrComptPseudoTransportationActor.h | 3 +- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp index d69496bd3..ba894cf04 100644 --- a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp @@ -88,7 +88,7 @@ G4double GateOptnVGenericSplitting::RussianRouletteForAngleSurvival(G4ThreeVecto G4double cosTheta =vectorDirector * dir; G4double theta = std::acos(cosTheta); G4double weightToApply = 1; -if (theta > fMaxTheta){ +if (theta > maxTheta){ G4double probability = G4UniformRand(); if (probability <= 1 / split) { weightToApply = split; diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h index ed75c9edd..0ed33e052 100644 --- a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h @@ -74,7 +74,7 @@ class GateOptnVGenericSplitting : public G4VBiasingOperation { void TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); void TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); -G4double RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split); +static G4double RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split); public: diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 96d17099f..2de1c0e16 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -122,7 +122,6 @@ void GateOptrComptPseudoTransportationActor::StartSimulationAction() { // to the biasing operator. To do that, I use the function // AttachAllLogicalDaughtersVolumes. AttachAllLogicalDaughtersVolumes(biasingVolume); - for (int i = 0; i < fNameOfBiasedLogicalVolume.size(); i++) fScatteredGammaSplittingOperation->SetSplittingFactor(fSplittingFactor); fScatteredGammaSplittingOperation->SetMaxTheta(fMaxTheta); fScatteredGammaSplittingOperation->SetRussianRouletteForAngle(fRussianRouletteForAngle); @@ -133,9 +132,6 @@ void GateOptrComptPseudoTransportationActor::StartSimulationAction() { fPairProdSplittingOperation->SetSplittingFactor(fSplittingFactor); - - - fFreeFlightOperation->SetRussianRouletteForWeights(fRussianRouletteForWeights); } @@ -163,19 +159,39 @@ void GateOptrComptPseudoTransportationActor::StartRun() { void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { - - - + G4String creationProcessName = "None"; + if (step->GetTrack()->GetCreatorProcess() != 0){ + creationProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); + } - if ((isSplitted == true) && (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { - G4String LogicalVolumeName = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end()) - && (LogicalVolumeName != fMotherVolumeName)) { - step->GetTrack()->SetTrackStatus(fStopAndKill); - isSplitted = false; - } +if ((fIsFirstStep) && (fRussianRouletteForAngle)){ + G4String LogicalVolumeNameOfCreation = step->GetTrack()->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ + if (creationProcessName == "biasWrapper(annihil)"){ + auto dir = step->GetPreStepPoint()->GetMomentumDirection(); + G4double w = GateOptnVGenericSplitting::RussianRouletteForAngleSurvival(dir,fVectorDirector,fMaxTheta,fSplittingFactor); + if (w == 0) + { + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + else { + step->GetTrack()->SetWeight(step->GetTrack()->GetWeight() * w); + } + } } } + +if ((isSplitted == true) && (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { + G4String LogicalVolumeName = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end()) + && (LogicalVolumeName != fMotherVolumeName)) { + step->GetTrack()->SetTrackStatus(fStopAndKill); + isSplitted = false; + } +} + +fIsFirstStep = false; +} void GateOptrComptPseudoTransportationActor::BeginOfEventAction( const G4Event *event) { @@ -189,12 +205,13 @@ void GateOptrComptPseudoTransportationActor::BeginOfEventAction( void GateOptrComptPseudoTransportationActor::StartTracking( const G4Track *track) { - G4String CreationProcessName = "None"; - + G4String creationProcessName = "None"; + fIsFirstStep = true; if (track->GetCreatorProcess() != 0){ - CreationProcessName = track->GetCreatorProcess()->GetProcessName(); + creationProcessName = track->GetCreatorProcess()->GetProcessName(); } + if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index d60e88ad9..77064f94f 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -86,11 +86,12 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4int fEventID; G4double fEventIDKineticEnergy; G4bool ftestbool= false; + G4bool fIsFirstStep = false; const G4VProcess* fAnnihilation =nullptr; std::vector fNameOfBiasedLogicalVolume = {}; std::vector v_EventID = {}; - std::vector fCreationProcessNameList = {"biasWrapper(compt)","biasWrapper(Rayl)", "biasWrapper(eBrem)","biasWrapper(annihil)"}; + std::vector fCreationProcessNameList = {"biasWrapper(compt)", "biasWrapper(eBrem)","biasWrapper(annihil)"}; // Unused but mandatory virtual void StartSimulationAction(); From 101d021978a056031d4d8e4cbecdb38ab0170b49 Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 25 Jun 2024 15:33:37 +0200 Subject: [PATCH 27/82] supression of rayleigh biasing --- opengate/actors/miscactors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index fc2a53541..56fb51443 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -463,7 +463,7 @@ def set_default_user_info(user_info): user_info.attach_to_logical_holder = True user_info.splitting_factor = 1 user_info.relative_min_weight_of_particle = np.inf - user_info.gamma_processes = ["compt", "phot", "conv", "Rayl"] + user_info.gamma_processes = ["compt", "phot", "conv"] user_info.electron_processes = ["eBrem"] user_info.positron_processes = ["annihil","eBrem"] user_info.russian_roulette_for_angle = False From e6c0b5b52065d2965d4f9688d604d0bddd25ec10 Mon Sep 17 00:00:00 2001 From: majacquet Date: Thu, 27 Jun 2024 13:39:33 +0200 Subject: [PATCH 28/82] Modification of Rayleigh behaviour --- .../opengate_lib/GateOptnForceFreeFlight.cpp | 8 +++++++- opengate/actors/miscactors.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp index 53d6ba3a9..990a5629f 100644 --- a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp @@ -118,6 +118,12 @@ void GateOptnForceFreeFlight ::AlongMoveBy( G4double weightChange) { - fWeightChange[callingProcess->GetWrappedProcess()->GetProcessName()] = + G4String processName = callingProcess->GetWrappedProcess()->GetProcessName(); + if (processName != "Rayl"){ + fWeightChange[processName] = weightChange; + } + else { + fWeightChange[processName] = 1; + } } diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index df9d32c9d..280ff0469 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -463,7 +463,7 @@ def set_default_user_info(user_info): user_info.attach_to_logical_holder = True user_info.splitting_factor = 1 user_info.relative_min_weight_of_particle = np.inf - user_info.gamma_processes = ["compt", "phot", "conv"] + user_info.gamma_processes = ["compt", "phot", "conv","Rayl"] user_info.electron_processes = ["eBrem"] user_info.positron_processes = ["annihil", "eBrem"] user_info.russian_roulette_for_angle = False From 5503679b705d983d0ce59331aec3af8fa5667c5c Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 2 Jul 2024 16:07:46 +0200 Subject: [PATCH 29/82] Correction of generation bug implying brem and pair production --- ...ateLastVertexInteractionSplittingActor.cpp | 125 ++++++++++------ .../GateLastVertexInteractionSplittingActor.h | 2 +- .../GateLastVertexSplittingPostStepDoIt.h | 134 +----------------- 3 files changed, 88 insertions(+), 173 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 2f6d125e9..11571a011 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -157,6 +157,8 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *Curre { // Loop on process and add the secondary tracks to the current step secondary vector. + //std::cout<GetKineticEnergy()<<" "<GetDynamicParticle()->GetKineticEnergy()<<" "<GetMomentumDirection()<<" "<GetDynamicParticle()->GetMomentumDirection()<GetParticleDefinition()->GetParticleName(); G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; @@ -169,19 +171,24 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *Curre else { GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *) process ; - if (track->GetTrackStatus() == 0) + if (track->GetTrackStatus() == 0){ + //if (process->GetProcessName() == "annihil") + //std::cout<<"lol"<PostStepDoIt(*track, *step); + } if (track->GetTrackStatus() == 1){ GateplusannihilAtRestDoIt* eplusAnnihilProcess = (GateplusannihilAtRestDoIt*) process; - eplusAnnihilProcess ->GateplusannihilAtRestDoIt::AtRestDoIt(*track, *step); + + processFinalState = eplusAnnihilProcess ->GateplusannihilAtRestDoIt::AtRestDoIt(*track, *step); } } + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + if (NbOfSecondaries > 0) { gammaWeight = fWeightOfEnteringParticle/fSplittingFactor; G4Track *newTrack = processFinalState->GetSecondary(0); - std::cout<GetKineticEnergy()<GetMomentumDirection(); @@ -199,6 +206,10 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step *Curre trackVector->push_back(newTrack); } } + else { + processFinalState->Clear(); + SecondariesSplitting(CurrentStep,track, step, process); + } processFinalState->Clear(); } @@ -252,6 +263,7 @@ void GateLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4S G4String processName = "None"; G4int trackID = step->GetTrack()->GetTrackID(); G4int parentID = step->GetTrack()->GetParentID(); + //<GetTrack()->GetParticleDefinition()->GetParticleName()<<" "<GetTrack()->GetTrackStatus()<GetPostStepPoint()->GetProcessDefinedStep() != 0) { processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); @@ -267,18 +279,15 @@ void GateLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4S G4Step *stepInformation = new G4Step(*(step)); G4StepPoint *stepPoint = nullptr; - if ((processName == "annihil") && (step->GetTrack()->GetKineticEnergy() == 0)) - { - stepPoint = step->GetPostStepPoint(); - } - else - { - stepPoint = step->GetPreStepPoint(); - } + stepPoint = step->GetPreStepPoint(); + trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy()); + if ((processName == "eBrem") || ((processName == "annihil")&& step->GetTrack()->GetTrackStatus() == 1)){ + trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy() - step->GetTotalEnergyDeposit()); + } trackInformation->SetMomentumDirection(stepPoint->GetMomentumDirection()); - if ((processName =="annihil") && (step->GetTrack()->GetKineticEnergy() == 0)) + if ((processName =="annihil") && ((step->GetTrack()->GetTrackStatus() == 1))) trackInformation->SetTrackStatus(fStopButAlive); else{ trackInformation->SetTrackStatus(fAlive); @@ -310,15 +319,15 @@ void GateLastVertexInteractionSplittingActor::RememberLastProcessInformation(G4S if (auto it = std::find(fRememberedProcesses[parentID].begin(), fRememberedProcesses[parentID].end(), creatorProcessName); it != fRememberedProcesses[parentID].end()) { auto idx = it - fRememberedProcesses[parentID].begin(); - fRememberedTracks[trackID] = {fRememberedTracks[parentID][idx]}; + fRememberedTracks[trackID] = {new G4Track(*fRememberedTracks[parentID][idx])}; fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][idx]}; - fRememberedSteps[trackID] = {fRememberedSteps[parentID][idx]}; + fRememberedSteps[trackID] = {new G4Step(*fRememberedSteps[parentID][idx])}; } else { - fRememberedTracks[trackID] = {fRememberedTracks[parentID][0]}; + fRememberedTracks[trackID] = {new G4Track(*fRememberedTracks[parentID][0])}; fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][0]}; - fRememberedSteps[trackID] = {fRememberedSteps[parentID][0]}; + fRememberedSteps[trackID] = {new G4Step(*fRememberedSteps[parentID][0])}; } } } @@ -348,7 +357,7 @@ void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G ComptonSplitting(CurrentStep, track, step, processToSplit); } - else if (processName != "eBrem") + else { SecondariesSplitting(CurrentStep, track, step, processToSplit); } @@ -517,10 +526,9 @@ void GateLastVertexInteractionSplittingActor::BeginOfEventAction(const G4Event * fParentID = -1; fEventID = event->GetEventID(); - //if (fEventID%30000 == 0) - std::cout <GetTrack()->GetParticleDefinition()->GetParticleName(); G4String creatorProcessName = "None"; + + if (step->GetTrack()->GetCreatorProcess() != 0) creatorProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); + + //std::cout<GetTotalEnergyDeposit()<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPreStepPoint()->GetMomentumDirection()<GetTrack()->GetTrackID()<<" "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" "<GetTrack()->GetTrackID(); - if (fIsSplitted == true){ - + if ((step->GetTrack()->GetWeight() < fWeightOfEnteringParticle) && (fNotSplitted ==false)){ G4String logicalVolumeNamePostStep = "None"; if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) @@ -556,54 +569,59 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processName) != fListOfProcesses.end()){ + + if (particleName != "e+"){ + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processName) != fListOfProcesses.end()){ + step->GetTrack()->SetTrackStatus(fStopAndKill); + step->GetfSecondary()->clear(); + } + } + + + if ((particleName == "e+") && ((step->GetTrack()->GetTrackStatus() == 1)||(step->GetTrack()->GetTrackStatus() == 2))){ step->GetTrack()->SetTrackStatus(fStopAndKill); + step->GetfSecondary()->clear(); } - if (step->GetTrack()->GetTrackStatus() == 2){ + if ((step->GetTrack()->GetTrackStatus() == 2) || (step->GetTrack()->GetTrackStatus() == 3)){ + + /* delete fTrackToSplit; delete fStepToSplit; fTrackToSplit = new G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); fStepToSplit = new G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); - std::cout <<"IN"<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTrack()->GetTrackStatus()<<" "< 27666){ - std::cout << fIsSplitted<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTrack()->GetTrackStatus()<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTrack()->GetTrackStatus()<GetTrack()->GetTrackStatus() != 2) && (step->GetTrack()->GetTrackStatus() != 3)) && (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) { if (fSplitCounter < fWeightOfEnteringParticle) { + /* delete fTrackToSplit; delete fStepToSplit; fTrackToSplit = new G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); fStepToSplit = new G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); + */ CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, fProcessToSplit); //std::cout<GetfSecondary(); G4Track* theTrack= trackVector->back(); fSplitCounter += theTrack->GetWeight(); - if (fEventID > 27666){ - std::cout << fSplitCounter<<" "<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTrack()->GetTrackStatus()<GetWeight()<= fWeightOfEnteringParticle) { delete fTrackToSplit; delete fStepToSplit; fTrackToSplit = nullptr; fStepToSplit = nullptr; - //std::cout<<"lol"<SetTrackStatus(fStopAndKill); } @@ -612,11 +630,13 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) } + - - if (fIsSplitted == false) + if (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) { + //std::cout<GetTrack()->GetTrackStatus()<GetTrack()->GetTrackStatus()<GetTrack()->GetWeight() == fWeightOfEnteringParticle) && (creatorProcessName == "conv") && (fTrackIDOfSplittedTrack == step->GetTrack()->GetParentID()) && (fEventID == fEventIDOfSplittedTrack)){ step->GetTrack()->SetTrackStatus(fStopAndKill); } @@ -659,6 +681,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) } } */ + G4String logicalVolumeNamePostStep = "None"; if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); @@ -667,14 +690,17 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { if (auto search = fRememberedProcesses.find(trackID); search != fRememberedProcesses.end()){ + fProcessToSplit = fRememberedProcesses[trackID].back(); fTrackToSplit = new G4Track(*fRememberedTracks[trackID].back()); fStepToSplit = new G4Step(*fRememberedSteps[trackID].back()); + } if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), fProcessToSplit) != fListOfProcesses.end()) { + fTrackIDOfSplittedTrack = trackID; fEventIDOfSplittedTrack = fEventID; fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; @@ -694,6 +720,8 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) } } + + // Handle of pecularities (2): // If the process to split is the annihilation, the second photon, postponed or not, have to be killed @@ -713,20 +741,30 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) // all the brems photons will be killed before their tracking, and the conv processes will then be replayed if ((particleName == "e+") &&(fProcessToSplit != "None")) { + G4int parentID = step->GetTrack()->GetParentID(); fProcessToSplit = fRememberedProcesses[parentID].back(); delete fTrackToSplit; delete fStepToSplit; fTrackToSplit = new G4Track(*fRememberedTracks[parentID].back()); fStepToSplit = new G4Step(*fRememberedSteps[parentID].back()); - step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); fTrackIDOfInitialTrack = parentID; + + auto* vecSecondaries = step->GetfSecondary(); + vecSecondaries->clear(); + } + if (!((fProcessToSplit == "eBrem") && (particleName == "e-"))) + { + CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, fProcessToSplit); + fNotSplitted = false; + } + step->GetTrack()->SetTrackStatus(fStopAndKill); + - fIsSplitted = true; - CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, fProcessToSplit); - step->GetTrack()->SetTrackStatus(fStopAndKill); + + } } } @@ -742,6 +780,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) */ fIsFirstStep = false; + } diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index e8877ae4b..3a2e261b5 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -60,7 +60,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4bool fSuspendForAnnihil = false; G4double fWeightOfEnteringParticle = 0; G4double fSplitCounter = 0; - G4bool fIsSplitted = false; + G4bool fNotSplitted = true; G4Track* fTrackToSplit = nullptr; G4Step* fStepToSplit = nullptr; diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h index 22b75a0f8..a8a6d9701 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h @@ -77,10 +77,8 @@ GateGammaEmPostStepDoIt(); virtual G4VParticleChange * PostStepDoIt(const G4Track & track, const G4Step & step) override { const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); - - std::cout<GetParticleName()<GetParticleName()<GetMaterialCutsCouple()); - G4int idx = (G4int)CurrentMaterialCutsCoupleIndex(); - G4double ene(0.0); - G4VEmModel* model = SelectModel(ene, idx); - - // define new weight for primary and secondaries - G4double weight = fParticleChange.GetParentWeight(); - - // sample secondaries - secParticles.clear(); - G4double gammaCut = GetGammaEnergyCut(); - model->SampleSecondaries(&secParticles, MaterialCutsCouple(), - track.GetDynamicParticle(), gammaCut); - - G4int num0 = (G4int)secParticles.size(); - // splitting or Russian roulette - if(biasManager) { - if(biasManager->SecondaryBiasingRegion(idx)) { - G4double eloss = 0.0; - weight *= biasManager->ApplySecondaryBiasing( - secParticles, track, model, &fParticleChange, eloss, - idx, gammaCut, step.GetPostStepPoint()->GetSafety()); - if(eloss > 0.0) { - eloss += fParticleChange.GetLocalEnergyDeposit(); - fParticleChange.ProposeLocalEnergyDeposit(eloss); - } - } - } - - // save secondaries - G4int num = (G4int)secParticles.size(); - - // Check that entanglement is switched on... (the following flag is - // set by /process/em/QuantumEntanglement). - G4bool entangled = G4EmParameters::Instance()->QuantumEntanglement(); - // ...and that we have two gammas with both gammas' energies above - // gammaCut (entanglement is only programmed for e+ e- -> gamma gamma). - G4bool entangledgammagamma = false; - if (entangled) { - if (num == 2) { - entangledgammagamma = true; - for (const auto* p: secParticles) { - if (p->GetDefinition() != G4Gamma::Gamma() || - p->GetKineticEnergy() < gammaCut) { - entangledgammagamma = false; - } - } - } - } - - // Prepare a shared pointer for psossible use below. If it is used, the - // shared pointer is copied into the tracks through G4EntanglementAuxInfo. - // This ensures the clip board lasts until both tracks are destroyed. - std::shared_ptr clipBoard; - if (entangledgammagamma) { - clipBoard = std::make_shared(); - clipBoard->SetParentParticleDefinition(track.GetDefinition()); - } - - if(num > 0) { - fParticleChange.SetNumberOfSecondaries(num); - G4double edep = fParticleChange.GetLocalEnergyDeposit(); - G4double time = track.GetGlobalTime(); - - for (G4int i=0; iGetParticleDefinition(); - G4double e = dp->GetKineticEnergy(); - G4bool good = true; - if(ApplyCuts()) { - if (p == G4Gamma::Gamma()) { - if (e < gammaCut) { good = false; } - } else if (p == G4Electron::Electron()) { - if (e < GetElectronEnergyCut()) { good = false; } - } - // added secondary if it is good - } - if (good) { - G4Track* t = new G4Track(dp, time, track.GetPosition()); - t->SetTouchableHandle(track.GetTouchableHandle()); - if (entangledgammagamma) { - // entangledgammagamma is only true when there are only two gammas - // (See code above where entangledgammagamma is calculated.) - if (i == 0) { // First gamma - clipBoard->SetTrackA(t); - } else if (i == 1) { // Second gamma - clipBoard->SetTrackB(t); - } - t->SetAuxiliaryTrackInformation - (G4PhysicsModelCatalog::GetModelID("model_GammaGammaEntanglement"),new G4EntanglementAuxInfo(clipBoard)); - } - if (biasManager) { - t->SetWeight(weight * biasManager->GetWeight(i)); - } else { - t->SetWeight(weight); - } - pParticleChange->AddSecondary(t); - - // define type of secondary - if(i < mainSecondaries) { t->SetCreatorModelID(secID); } - else if(i < num0) { - if(p == G4Gamma::Gamma()) { - t->SetCreatorModelID(fluoID); - } else { - t->SetCreatorModelID(augerID); - } - } else { - t->SetCreatorModelID(biasID); - } - /* - G4cout << "Secondary(post step) has weight " << t->GetWeight() - << ", Ekin= " << t->GetKineticEnergy()/MeV << " MeV " - << GetProcessName() << " fluoID= " << fluoID - << " augerID= " << augerID < Date: Wed, 25 Sep 2024 19:11:21 +0200 Subject: [PATCH 30/82] Refactoring of last vertex splitting actor, combining a data tree structure and a source creation to regenerate event --- core/opengate_core/opengate_core.cpp | 8 +- .../opengate_lib/GateGenericSource.cpp | 7 +- ...ateLastVertexInteractionSplittingActor.cpp | 1031 ++--- .../GateLastVertexInteractionSplittingActor.h | 59 +- ...teLastVertexInteractionSplittingActorOld.h | 110 + ...LastVertexInteractionSplittingOldActor.cpp | 848 ++++ .../opengate_lib/GateLastVertexSource.cpp | 112 + .../opengate_lib/GateLastVertexSource.h | 85 + .../GateLastVertexSplittingDataContainer.h | 343 ++ .../GateLastVertexSplittingPostStepDoIt.h | 1 - .../GateLastVertexSplittingPostStepDoItOld.h | 110 + .../opengate_lib/GateSourceManager.cpp | 4 + .../opengate_lib/GateSourceManager.h | 16 + ...LastVertexInteractionSplittingActorOld.cpp | 21 + .../opengate_lib/pyGateLastVertexSource.cpp | 22 + core/opengate_core/opengate_lib/tree.hh | 3412 +++++++++++++++++ core/opengate_core/opengate_lib/tree_util.hh | 92 + opengate/actors/actorbuilders.py | 1 + opengate/actors/miscactors.py | 35 + opengate/sources/builders.py | 3 +- opengate/sources/generic.py | 18 + 21 files changed, 5692 insertions(+), 646 deletions(-) create mode 100644 core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActorOld.h create mode 100644 core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingOldActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSource.cpp create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSource.h create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoItOld.h create mode 100644 core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActorOld.cpp create mode 100644 core/opengate_core/opengate_lib/pyGateLastVertexSource.cpp create mode 100644 core/opengate_core/opengate_lib/tree.hh create mode 100644 core/opengate_core/opengate_lib/tree_util.hh diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index c32e48188..3f376726d 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -342,7 +342,9 @@ void init_GatePhaseSpaceActor(py::module &); void init_GateOptrComptSplittingActor(py::module &m); -void init_GateLastVertexInteractionSplittingActor(py::module &m); +void init_GateLastVertexInteractionSplittingActor(py::module &m); + +void init_GateLastVertexInteractionSplittingActorOld(py::module &m); void init_GateOptrComptPseudoTransportationActor(py::module &m); @@ -374,6 +376,8 @@ void init_GateVDigiAttribute(py::module &m); void init_GateVSource(py::module &); +void init_GateLastVertexSource(py::module &); + void init_GateExceptionHandler(py::module &); void init_GateNTuple(py::module &); @@ -545,6 +549,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GateImageNestedParameterisation(m); init_GateRepeatParameterisation(m); init_GateVSource(m); + init_GateLastVertexSource(m); init_GateSourceManager(m); init_GateGenericSource(m); init_GateTreatmentPlanPBSource(m); @@ -568,6 +573,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GateOptrComptPseudoTransportationActor(m); init_GateOptrComptSplittingActor(m); init_GateLastVertexInteractionSplittingActor(m); + init_GateLastVertexInteractionSplittingActorOld(m); init_GateHitsCollectionActor(m); init_GateMotionVolumeActor(m); init_GateHitsAdderActor(m); diff --git a/core/opengate_core/opengate_lib/GateGenericSource.cpp b/core/opengate_core/opengate_lib/GateGenericSource.cpp index 88748a25d..97ff9d0ec 100644 --- a/core/opengate_core/opengate_lib/GateGenericSource.cpp +++ b/core/opengate_core/opengate_lib/GateGenericSource.cpp @@ -143,13 +143,14 @@ double GateGenericSource::PrepareNextTime(double current_simulation_time) { if (fMaxN <= 0) { if (fEffectiveEventTime < fStartTime) return fStartTime; - if (fEffectiveEventTime >= fEndTime) + if (fEffectiveEventTime >= fEndTime){ return -1; - + } // get next time according to current fActivity double next_time = CalcNextTime(fEffectiveEventTime); - if (next_time >= fEndTime) + if (next_time >= fEndTime){ return -1; + } return next_time; } diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 09061f928..5e6d10d9c 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -46,6 +46,10 @@ #include "GateLastVertexInteractionSplittingActor.h" #include "GateLastVertexSplittingPostStepDoIt.h" #include "GateOptnComptSplitting.h" +#include "GateLastVertexSource.h" +#include "G4RunManager.hh" +#include + //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -55,8 +59,7 @@ GateLastVertexInteractionSplittingActor:: fMotherVolumeName = DictGetStr(user_info, "mother"); fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); - fRussianRouletteForAngle = - DictGetBool(user_info, "russian_roulette_for_angle"); + fRussianRouletteForAngle = DictGetBool(user_info, "russian_roulette_for_angle"); fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); fMaxTheta = DictGetDouble(user_info, "max_theta"); fActions.insert("StartSimulationAction"); @@ -65,30 +68,63 @@ GateLastVertexInteractionSplittingActor:: fActions.insert("BeginOfRunAction"); fActions.insert("PreUserTrackingAction"); fActions.insert("PostUserTrackingAction"); + fActions.insert("EndOfEventAction"); + } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -G4double -GateLastVertexInteractionSplittingActor::RussianRouletteForAngleSurvival( - G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta, - G4double split) { - G4double cosTheta = vectorDirector * dir; - G4double theta = std::acos(cosTheta); - G4double weightToApply = 1; - if (theta > fMaxTheta) { - G4double probability = G4UniformRand(); - if (probability <= 1 / split) { - weightToApply = split; - } else { - weightToApply = 0; - } +void GateLastVertexInteractionSplittingActor::print_tree(const tree& tr, tree::pre_order_iterator it, tree::pre_order_iterator end) + { + if(!tr.is_valid(it)) return; + int rootdepth=tr.depth(it); + std::cout << "-----" << std::endl; + while(it!=end) { + for(int i=0; iFindParticle(container.GetParticleNameToSplit()); + G4ThreeVector momentum = container.GetMomentum(); + G4double energy = container.GetEnergy(); + if (energy <0){ + energy = 0; + momentum = {0,0,0}; + } + G4int trackStatus = container.GetTrackStatus(); + G4ThreeVector position = container.GetVertexPosition(); + G4ThreeVector polarization = container.GetPolarization(); + + G4DynamicParticle* dynamicParticle = new G4DynamicParticle(particleDefinition,momentum,energy); + G4Track* aTrack = new G4Track(dynamicParticle,step->GetPreStepPoint()->GetGlobalTime(), position); + //std::cout<GetMomentumDirection()<SetPolarization(polarization); + if (trackStatus == 0){ + aTrack->SetTrackStatus(fAlive); } - return weightToApply; + if (trackStatus == 1){ + aTrack->SetTrackStatus(fStopButAlive); + } + if ((trackStatus == 2) || (trackStatus == 3)){ + aTrack->SetTrackStatus(fAlive); + } + aTrack->SetWeight(container.GetWeight()); + + return aTrack; + + } -G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack( - G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) { + +G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) { G4double energy = gammaProcess->GetProposedKineticEnergy(); G4double globalTime = track.GetGlobalTime(); G4double newGammaWeight = weight; @@ -105,445 +141,325 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack( return newTrack; } -void GateLastVertexInteractionSplittingActor::ComptonSplitting( - G4Step *CurrentStep, G4Track *track, const G4Step *step, - G4VProcess *process) { - // Loop on process and add the secondary tracks to the current step secondary - // vector +void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process, LastVertexDataContainer container) { G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; + G4Track* aTrack = CreateATrackFromContainer(container,initStep); GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*track, *step); - G4ParticleChangeForGamma *gammaProcessFinalState = - (G4ParticleChangeForGamma *)processFinalState; - const G4ThreeVector momentum = - gammaProcessFinalState->GetProposedMomentumDirection(); - gammaWeight = fWeightOfEnteringParticle / fSplittingFactor; - if (fRussianRouletteForAngle == true) { - G4double weightToApply = RussianRouletteForAngleSurvival( - momentum, fVectorDirector, fMaxTheta, fSplittingFactor); - if (weightToApply != 0) { - gammaWeight = gammaWeight * weightToApply; - G4Track *newTrack = - CreateComptonTrack(gammaProcessFinalState, *track, gammaWeight); - trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) { - G4Track *newTrack = processFinalState->GetSecondary(j); - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - } - } + G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*aTrack, *initStep); + G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; + const G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); + gammaWeight = aTrack->GetWeight()/ fSplittingFactor; - else { - G4Track *newTrack = - CreateComptonTrack(gammaProcessFinalState, *track, gammaWeight); + + + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *aTrack, gammaWeight); + trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) { + G4Track *newTrack = processFinalState->GetSecondary(j); + newTrack->SetWeight(gammaWeight); trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) { - G4Track *newTrack = processFinalState->GetSecondary(j); - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } } + processFinalState->Clear(); gammaProcessFinalState->Clear(); + delete aTrack; +} + + + +G4VProcess* GateLastVertexInteractionSplittingActor::GetProcessFromProcessName(G4String particleName, G4String pName){ + auto *particle_table = G4ParticleTable::GetParticleTable(); + G4ParticleDefinition *particleDefinition = particle_table->FindParticle(particleName); + G4ProcessManager *processManager = particleDefinition->GetProcessManager(); + G4ProcessVector *processList = processManager->GetProcessList(); + + G4VProcess* nullProcess = nullptr; + for (size_t i = 0; i < processList->size(); ++i) { + auto process = (*processList)[i]; + if (process->GetProcessName() == pName) { + return process; + } + } + return nullProcess; + } -void GateLastVertexInteractionSplittingActor::SecondariesSplitting( - G4Step *CurrentStep, G4Track *track, const G4Step *step, - G4VProcess *process) { - // Loop on process and add the secondary tracks to the current step secondary - // vector. - // std::cout<GetKineticEnergy()<<" - // "<GetDynamicParticle()->GetKineticEnergy()<<" - // "<GetMomentumDirection()<<" - // "<GetDynamicParticle()->GetMomentumDirection()<GetDefinition()->GetParticleName(); + G4VProcess* eIoniProcess = GetProcessFromProcessName(particleName, "eIoni"); + G4VProcess* eBremProcess = GetProcessFromProcessName(particleName, "eBrem"); + G4VParticleChange* eIoniProcessAlongState = eIoniProcess->AlongStepDoIt(*track, *step); + G4VParticleChange* eBremProcessAlongState = eBremProcess->AlongStepDoIt(*track, *step); + G4ParticleChangeForLoss* eIoniProcessAlongStateForLoss = (G4ParticleChangeForLoss*) eIoniProcessAlongState; + G4ParticleChangeForLoss* eBremProcessAlongStateForLoss = (G4ParticleChangeForLoss*) eBremProcessAlongState; + G4double LossEnergy = eIoniProcessAlongStateForLoss->GetLocalEnergyDeposit(); + G4ThreeVector momentum = eBremProcessAlongStateForLoss->GetProposedMomentumDirection(); + G4ThreeVector polarization = eBremProcessAlongStateForLoss->GetProposedPolarization(); + + track->SetKineticEnergy(track->GetKineticEnergy() - LossEnergy); + track->SetMomentumDirection(momentum); + track->SetPolarization(polarization); + GateBremPostStepDoIt *bremProcess = (GateBremPostStepDoIt *)process; + eIoniProcessAlongState->Clear(); + eBremProcessAlongState->Clear(); + + return bremProcess->GateBremPostStepDoIt::PostStepDoIt(*track, *step); + +} + +void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container) { + + G4Track* track = CreateATrackFromContainer(container,initStep); G4String particleName = track->GetParticleDefinition()->GetParticleName(); G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; G4VParticleChange *processFinalState = nullptr; if (process->GetProcessName() == "eBrem") { - GateBremPostStepDoIt *bremProcess = (GateBremPostStepDoIt *)process; - processFinalState = - bremProcess->GateBremPostStepDoIt::PostStepDoIt(*track, *step); - } else { + processFinalState = eBremProcessFinalState(track,initStep,process); + } + else { GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - if (track->GetTrackStatus() == 0) { - // if (process->GetProcessName() == "annihil") - // std::cout<<"lol"<PostStepDoIt(*track, *step); + if ((container.GetAnnihilationFlag() == "PostStep") && (track->GetKineticEnergy() > 0)){ + processFinalState = emProcess->PostStepDoIt(*track, *initStep); } - if (track->GetTrackStatus() == 1) { - GateplusannihilAtRestDoIt *eplusAnnihilProcess = - (GateplusannihilAtRestDoIt *)process; - - processFinalState = - eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*track, - *step); + if ((container.GetAnnihilationFlag() == "AtRest") || (track->GetKineticEnergy() == 0)) { + GateplusannihilAtRestDoIt *eplusAnnihilProcess = (GateplusannihilAtRestDoIt *)process; + processFinalState = eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*track,*initStep); } } - + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - - if (NbOfSecondaries > 0) { - gammaWeight = fWeightOfEnteringParticle / fSplittingFactor; - G4Track *newTrack = processFinalState->GetSecondary(0); - if ((fRussianRouletteForAngle == true) && (particleName == "gamma")) { - const G4ThreeVector momentum = newTrack->GetMomentumDirection(); - G4double weightToApply = RussianRouletteForAngleSurvival( - momentum, fVectorDirector, fMaxTheta, fSplittingFactor); - if (weightToApply != 0) { - gammaWeight = gammaWeight * weightToApply; - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - } else { + G4int idx = 0; + if (NbOfSecondaries >0) { + gammaWeight = track->GetWeight()/fSplittingFactor; + + G4double randomIdx = round((NbOfSecondaries -1) * G4UniformRand()); + idx = (G4int) randomIdx; + G4Track *newTrack = processFinalState->GetSecondary(idx); + if (!(isnan((newTrack->GetMomentumDirection())[0]))){ newTrack->SetWeight(gammaWeight); + newTrack->SetCreatorProcess(process); trackVector->push_back(newTrack); } - - for(int i = 1; i < NbOfSecondaries;i++){ - delete processFinalState->GetSecondary(i); + else{ + delete newTrack; + } + + + for(int i = 0; i < NbOfSecondaries;i++){ + if (i != idx) + delete processFinalState->GetSecondary(i); } - } else { - processFinalState->Clear(); - SecondariesSplitting(CurrentStep, track, step, process); } + + delete track; + + + + processFinalState->Clear(); + } -void GateLastVertexInteractionSplittingActor::ClearRememberedTracksAndSteps( - std::map rememberedTracks, - std::map> rememberedSteps) { - std::vector trackToKill = {}; - for (auto it = rememberedTracks.begin(); it != rememberedTracks.end(); ++it) { - std::vector vector = it->second; - for (auto it2 = vector.begin(); it2 != vector.end(); it2++) { - if ((std::find(trackToKill.begin(), trackToKill.end(), *it2) == - trackToKill.end())) { - // Method set pour clear les doublons au lieu de find. - trackToKill.push_back(*it2); - } - } - } - for (auto it = trackToKill.begin(); it != trackToKill.end(); ++it) { - delete *it; +void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* initStep,G4Step *step,LastVertexDataContainer container) { + // We retrieve the process associated to the process name to split and we + // split according the process. Since for compton scattering, the gamma is not + // a secondary particles, this one need to have his own splitting function. + G4VProcess* processToSplit = GetProcessFromProcessName(container.GetParticleNameToSplit(),container.GetProcessNameToSplit()); + G4String processName = container.GetProcessNameToSplit(); + if (processName == "compt") { + ComptonSplitting(initStep,step, processToSplit, container); } - std::vector stepToKill = {}; - for (auto it = rememberedSteps.begin(); it != rememberedSteps.end(); ++it) { - std::vector vector = it->second; - for (auto it2 = vector.begin(); it2 != vector.end(); it2++) { - if ((std::find(stepToKill.begin(), stepToKill.end(), *it2) == - stepToKill.end())) { - stepToKill.push_back(*it2); - } - } + else if((processName != "msc") && (processName != "conv")){ + SecondariesSplitting(initStep, step, processToSplit, container); } + +} + - for (auto it = stepToKill.begin(); it != stepToKill.end(); ++it) { - delete *it; +void GateLastVertexInteractionSplittingActor::CreateListOfbiasedVolume(G4LogicalVolume *volume) { + G4int nbOfDaughters = volume->GetNoDaughters(); + if (nbOfDaughters > 0) { + for (int i = 0; i < nbOfDaughters; i++) { + G4String LogicalVolumeName = volume->GetDaughter(i)->GetLogicalVolume()->GetName(); + G4LogicalVolume *logicalDaughtersVolume = volume->GetDaughter(i)->GetLogicalVolume(); + if (!(std::find(fListOfBiasedVolume.begin(),fListOfBiasedVolume.end(),LogicalVolumeName) != fListOfBiasedVolume.end())) + fListOfBiasedVolume.push_back(volume->GetDaughter(i)->GetLogicalVolume()->GetName()); + CreateListOfbiasedVolume(logicalDaughtersVolume); + } } } -void GateLastVertexInteractionSplittingActor::RememberLastProcessInformation( - G4Step *step) { - // When an interesting process to split occurs, we remember the status of the - // track and the process at this current step some informations regarding the - // track info have to be changed because they were update according to the - // interaction that occured These informations are stocked as a map object, - // binding the track ID with all the track objects and processes to split. - // Because in some cases, if a secondary was created before an interaction - // chain, this secondary will be track after the chain and without this - // association, we wll loose the information about the process occuring for - // this secondary. +void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ + G4String processName = "None"; + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) + processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + G4String creatorProcessName = "None"; if (step->GetTrack()->GetCreatorProcess() != 0) - creatorProcessName = - step->GetTrack()->GetCreatorProcess()->GetProcessName(); - G4String processName = "None"; - G4int trackID = step->GetTrack()->GetTrackID(); - G4int parentID = step->GetTrack()->GetParentID(); - //<GetTrack()->GetParticleDefinition()->GetParticleName()<<" - //"<GetTrack()->GetTrackStatus()<GetPostStepPoint()->GetProcessDefinedStep() != 0) { - processName = - step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); - } + creatorProcessName =step->GetTrack()->GetCreatorProcess()->GetProcessName(); + if ((step->GetTrack()->GetParticleDefinition()->GetParticleName() == "e+") && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))) { processName = "annihil"; } - if ((std::find(fListOfProcesses.begin(), fListOfProcesses.end(), - processName) != fListOfProcesses.end())) { - G4Track *trackInformation = new G4Track(*(step->GetTrack())); - G4Step *stepInformation = new G4Step(*(step)); - - G4StepPoint *stepPoint = nullptr; - stepPoint = step->GetPreStepPoint(); - - trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy()); - if ((processName == "eBrem") || ((processName == "annihil") && - step->GetTrack()->GetTrackStatus() == 1)) { - trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy() - - step->GetTotalEnergyDeposit()); + G4String annihilFlag = "None"; + if (processName == "annihil"){ + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0){ + //std::cout<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTotalEnergyDeposit()<GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ + annihilFlag = "PostStep"; + } + else if (processName != step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ + annihilFlag ="AtRest"; + } + //std::cout<SetMomentumDirection(stepPoint->GetMomentumDirection()); - if ((processName == "annihil") && - ((step->GetTrack()->GetTrackStatus() == 1))) - trackInformation->SetTrackStatus(fStopButAlive); - else { - trackInformation->SetTrackStatus(fAlive); + } + + + if (processName =="eBrem"){ + //std:cout<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTotalEnergyDeposit()<GetTrack()->GetTrackID()); + newContainer.SetParticleName(step->GetTrack()->GetDefinition()->GetParticleName()); + newContainer.SetCreationProcessName(creatorProcessName); + if (fTree.empty()){ + fTree.set_head(newContainer); } - trackInformation->SetPolarization(stepPoint->GetPolarization()); - // trackInformation->SetPosition(stepPoint->GetPosition()); - - if (auto search = fRememberedTracks.find(trackID); - search != fRememberedTracks.end()) { - fRememberedTracks[trackID].push_back(trackInformation); - fRememberedProcesses[trackID].push_back(processName); - fRememberedSteps[trackID].push_back(stepInformation); + + for (auto it = fTree.begin_post(); it != fTree.end_post(); ++it){ + LastVertexDataContainer container = *it; + G4int trackID = container.GetTrackID(); + + if (step->GetTrack()->GetParentID() == trackID){ + newContainer = container.ContainerFromParentInformation(step); + fTree.append_child(it,newContainer); + break; + + } } - else { - fRememberedTracks[trackID] = {trackInformation}; - fRememberedProcesses[trackID] = {processName}; - fRememberedSteps[trackID] = {stepInformation}; + for (auto it = fTree.begin_post(); it != fTree.end_post(); ++it){ + LastVertexDataContainer container = *it; + G4int trackID = container.GetTrackID(); + if (step->GetTrack()->GetTrackID() == trackID){ + fIterator = it; + break; + } } } - else { - if (auto search = fRememberedTracks.find(trackID); - search == fRememberedTracks.end()) { - if (auto search = fRememberedTracks.find(parentID); - search != fRememberedTracks.end()) { - if (auto it = std::find(fRememberedProcesses[parentID].begin(), - fRememberedProcesses[parentID].end(), - creatorProcessName); - it != fRememberedProcesses[parentID].end()) { - auto idx = it - fRememberedProcesses[parentID].begin(); - fRememberedTracks[trackID] = { - new G4Track(*fRememberedTracks[parentID][idx])}; - fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][idx]}; - fRememberedSteps[trackID] = { - new G4Step(*fRememberedSteps[parentID][idx])}; - } else { - fRememberedTracks[trackID] = { - new G4Track(*fRememberedTracks[parentID][0])}; - fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][0]}; - fRememberedSteps[trackID] = { - new G4Step(*fRememberedSteps[parentID][0])}; + + LastVertexDataContainer* container = &(*fIterator); + G4int trackID = container->GetTrackID(); + if ((processName != "Transportation") &&(processName !="None") && (processName !="Rayl")){ + if (step->GetTrack()->GetTrackID() == trackID){ + G4ThreeVector position = step->GetTrack()->GetPosition(); + G4ThreeVector prePosition = step->GetPreStepPoint()->GetPosition(); + G4ThreeVector momentum; + if ((processName == "annihil")) + momentum = step->GetPostStepPoint()->GetMomentumDirection(); + else{ + momentum = step->GetPreStepPoint()->GetMomentumDirection(); + } + G4ThreeVector polarization = step->GetPreStepPoint()->GetPolarization(); + G4String particleName = step->GetTrack()->GetDefinition()->GetParticleName(); + G4double energy = step->GetPreStepPoint()->GetKineticEnergy(); + G4double weight = step->GetTrack()->GetWeight(); + G4int trackStatus = step->GetTrack()->GetTrackStatus(); + G4int nbOfSecondaries = step->GetfSecondary()->size(); + G4double stepLength = step->GetStepLength(); + if (((processName == "annihil"))){ + energy -= (step->GetTotalEnergyDeposit()); } + container->SetSplittingParameters(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); + container->PushListOfSplittingParameters(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); } } - } } -void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex( - G4Step *CurrentStep, G4Track *track, const G4Step *step, - G4String processName) { - // We retrieve the process associated to the process name to split and we - // split according the process. Since for compton scattering, the gamma is not - // a secondary particles, this one need to have his own splitting function. - G4ParticleDefinition *particleDefinition = track->GetDefinition(); - G4ProcessManager *processManager = particleDefinition->GetProcessManager(); - G4ProcessVector *processList = processManager->GetProcessList(); - - G4VProcess *processToSplit = nullptr; - for (size_t i = 0; i < processList->size(); ++i) { - auto process = (*processList)[i]; - if (process->GetProcessName() == processName) { - processToSplit = process; - } - } - if (processName == "compt") { - - ComptonSplitting(CurrentStep, track, step, processToSplit); - } - else { - SecondariesSplitting(CurrentStep, track, step, processToSplit); - } -} -void GateLastVertexInteractionSplittingActor:: - ResetProcessesForEnteringParticles(G4Step *step) { - - // This function reset the processes and track registered to be split each - // time a particle enters into a volume. Here a new particle incoming is - // either a particle entering into the volume ( first pre step is a boundary - // of a mother volume or first step trigger the actor and the vertex is not - // either the mother volume or a the fdaughter volume) or an event generated - // within the volume (the particle did not enter into the volume and its - // parentID = 0). An additionnal condition is set with the trackID to ensure - // particles crossing twice the volume (after either a compton or pair prod) - // are not splitted. - - G4int trackID = step->GetTrack()->GetTrackID(); - if ((fEventIDOfInitialSplittedTrack != fEventID) || - ((fEventIDOfInitialSplittedTrack == fEventID) && - (trackID < fTrackIDOfInitialTrack))) { - G4String logicalVolumeNamePreStep = "None"; - if (step->GetPreStepPoint()->GetPhysicalVolume() != 0) { - logicalVolumeNamePreStep = step->GetPreStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); +G4bool GateLastVertexInteractionSplittingActor::IsParticleExitTheBiasedVolume(G4Step*step){ + G4String logicalVolumeNamePreStep = "None"; + G4String logicalVolumeNamePostStep = "None"; + if (step->GetPreStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + + + if (logicalVolumeNamePreStep != logicalVolumeNamePostStep){ + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()){ + return true; } - if (((step->GetPreStepPoint()->GetStepStatus() == 1) && - (logicalVolumeNamePreStep == fMotherVolumeName)) || - ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != - logicalVolumeNamePreStep) && - (step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != - fMotherVolumeName))) { - fTrackIDOfInitialTrack = trackID; - fEventIDOfInitialSplittedTrack = fEventID; - fWeightOfEnteringParticle = step->GetTrack()->GetWeight(); - ClearRememberedTracksAndSteps(fRememberedTracks, fRememberedSteps); - fRememberedProcesses.clear(); - fRememberedTracks.clear(); - fRememberedSteps.clear(); - } else if (step->GetTrack()->GetParentID() == 0) { - fTrackIDOfInitialTrack = trackID; - fEventIDOfInitialSplittedTrack = fEventID; - fWeightOfEnteringParticle = step->GetTrack()->GetWeight(); - ClearRememberedTracksAndSteps(fRememberedTracks, fRememberedSteps); - fRememberedProcesses.clear(); - fRememberedTracks.clear(); - fRememberedSteps.clear(); + /* + else if (std::find(fListOfBiasedVolume.begin(), fListOfBiasedVolume.end(), logicalVolumeNamePostStep) != fListOfBiasedVolume.end()) { + return false; + } + */ + else{ + return true; } } + return false; } -void GateLastVertexInteractionSplittingActor:: - PostponeFirstAnnihilationTrackIfInteraction(G4Step *step, - G4String processName) { - // In case the first gamma issued from annihilation undergoes an interaction, - // in order to not bias the process We keep in memory the particle post step - // state (with its secondaries) and kill the particle and its secondaries. If - // the second photon from annihilation exiting the collimation system with an - // interaction or is absorbed within the collimation, the particle is - // subsequently resimulated, starting from the interaction point. - - G4int trackID = step->GetTrack()->GetTrackID(); - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), - processName) != fListOfProcesses.end()) { - fSuspendForAnnihil = true; - G4Track trackToPostpone = G4Track(*(step->GetTrack())); - trackToPostpone.SetKineticEnergy( - step->GetPostStepPoint()->GetKineticEnergy()); - trackToPostpone.SetMomentumDirection( - step->GetPostStepPoint()->GetMomentumDirection()); - trackToPostpone.SetTrackStatus(step->GetTrack()->GetTrackStatus()); - trackToPostpone.SetPolarization( - step->GetPostStepPoint()->GetPolarization()); - trackToPostpone.SetPosition(step->GetPostStepPoint()->GetPosition()); - trackToPostpone.SetTrackID(trackID); - fTracksToPostpone.push_back(trackToPostpone); - auto theTrack = fTracksToPostpone[0]; - - auto secVec = step->GetfSecondary(); - for (int i = 0; i < secVec->size(); i++) { - G4Track *sec = (*secVec)[i]; - G4Track copySec = G4Track((*sec)); - fTracksToPostpone.push_back(copySec); - } - fTracksToPostpone[0].SetTrackID(trackID); - step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + +G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesAProcess(G4Step* step){ + G4String processName = "None"; + G4String particleName = step->GetTrack()->GetParticleDefinition()->GetParticleName(); + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) + processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + + if (std::find(fListOfProcessesAccordingParticles[particleName].begin(), fListOfProcessesAccordingParticles[particleName].end(), processName) != fListOfProcessesAccordingParticles[particleName].end()){ + return true; } -} + return false; -void GateLastVertexInteractionSplittingActor:: - RegenerationOfPostponedAnnihilationTrack(G4Step *step) { - // If the second photon from annihilation suceed to exit the collimation - // system with at least one interaction or was absorbed. Resimulation of - // annihilation photons and its potential secondaries. +} - G4TrackVector *currentSecondaries = step->GetfSecondary(); - for (int i = 0; i < fTracksToPostpone.size(); i++) { - G4Track *trackToAdd = - new G4Track(fTracksToPostpone[fTracksToPostpone.size() - 1 - i]); - trackToAdd->SetParentID(step->GetTrack()->GetTrackID() - 1); - currentSecondaries->insert(currentSecondaries->begin(), trackToAdd); - } - G4Track *firstPostponedTrack = (*currentSecondaries)[0]; +void GateLastVertexInteractionSplittingActor::StartSimulationAction (){ + fListOfProcessesAccordingParticles["gamma"] = {"compt","phot","conv"}; + fListOfProcessesAccordingParticles["e-"] = {"eBrem","eIoni","msc"}; + fListOfProcessesAccordingParticles["e+"] = {"eBrem","eIoni","msc","annihil"}; - // Handle of case were the interaction killed the photon issued from - // annihilation, it will not be track at the following state and the boolean - // plus the track vector need to be reset - if (firstPostponedTrack->GetTrackStatus() == 2) { - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); - } -} + G4LogicalVolume *biasingVolume = G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + fListOfBiasedVolume.push_back(biasingVolume->GetName()); + CreateListOfbiasedVolume(biasingVolume); -void GateLastVertexInteractionSplittingActor:: - HandleTrackIDIfPostponedAnnihilation(G4Step *step) { - // The ID is to modify trackID and the processes and tracks sotcked for a - // specific trackID associated to the postponed annihilation to respect the - // trackID order in GEANT4. Since in this case the second photon is tracked - // before the first one his trackID +=1. For the postponed one, he is the - // first secondary particle of the second photon tracks, his trackID is - // therefore equal to the second photon trackID +1 instead of the second - // photon trackID -1. The parentID of secondary particles are also modified - // because they are used in the rememberlastprocess function At last the value - // associated to a specific trackID in the process and track map are modified - // according to the new trackID. - - if (fSuspendForAnnihil) { - if (step->GetTrack()->GetTrackID() == - fTracksToPostpone[0].GetTrackID() - 1) { - fRememberedProcesses[step->GetTrack()->GetTrackID()] = - fRememberedProcesses[step->GetTrack()->GetTrackID() + 1]; - fRememberedProcesses.erase(step->GetTrack()->GetTrackID() + 1); - fRememberedSteps[step->GetTrack()->GetTrackID()] = - fRememberedSteps[step->GetTrack()->GetTrackID() + 1]; - fRememberedSteps.erase(step->GetTrack()->GetTrackID() + 1); - fRememberedTracks[step->GetTrack()->GetTrackID()] = - fRememberedTracks[step->GetTrack()->GetTrackID() + 1]; - fRememberedTracks.erase(step->GetTrack()->GetTrackID() + 1); - auto vecSec = step->GetSecondary(); - for (int i = 0; i < vecSec->size(); i++) { - (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() + 1); - } - step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() + 1); - } - if (step->GetTrack()->GetTrackID() == - fTracksToPostpone[0].GetTrackID() + 1) { - auto vecSec = step->GetSecondary(); - for (int i = 0; i < vecSec->size(); i++) { - (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() - 2); - } - step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() - 2); - } - } + auto* source = fSourceManager->FindSourceByName("source_vertex"); + fVertexSource = (GateLastVertexSource* ) source; } + void GateLastVertexInteractionSplittingActor::BeginOfRunAction( const G4Run *run) { - // The way to behave of the russian roulette is the following : - // we provide a vector director and the theta angle acceptance, where theta = - // 0 is a vector colinear to the vector director Then if the track generated - // is on the acceptance angle, we add it to the primary track, and if it's not - // the case, we launch the russian roulette - if (fRotationVectorDirector) { G4VPhysicalVolume *physBiasingVolume = G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); @@ -554,293 +470,172 @@ void GateLastVertexInteractionSplittingActor::BeginOfRunAction( void GateLastVertexInteractionSplittingActor::BeginOfEventAction( const G4Event *event) { - fParentID = -1; fEventID = event->GetEventID(); fEventIDOfSplittedTrack = -1; fTrackIDOfSplittedTrack = -1; fNotSplitted == true; + fIsAnnihilAlreadySplit = false; + //std::cout<GetTrack()->GetParticleDefinition()->GetParticleName(); + G4String logicalVolumeNamePreStep = "None"; + G4String logicalVolumeNamePostStep = "None"; + if (step->GetPreStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + + G4String particleName =step->GetTrack()->GetParticleDefinition()->GetParticleName(); G4String creatorProcessName = "None"; + G4String processName = "None"; if (step->GetTrack()->GetCreatorProcess() != 0) - creatorProcessName = - step->GetTrack()->GetCreatorProcess()->GetProcessName(); - - // std::cout<GetTotalEnergyDeposit()<<" - // "<GetPreStepPoint()->GetKineticEnergy()<<" - // "<GetPreStepPoint()->GetMomentumDirection()<GetTrack()->GetTrackID()<<" - // "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" - // "<GetTrack()->GetCreatorProcess()->GetProcessName(); + + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) + processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + + /* + if (processName == "annihil"){ + std::cout<<"ANNIHIL"<<" "<GetTrack()->GetTrackStatus()<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTotalEnergyDeposit()<GetSecondaryInCurrentStep(); + for (const G4Track* secondary : *secondaries){ + std::cout<GetKineticEnergy()<GetTrack()->SetTrackStatus(fStopAndKill); + //std::cout<GetPostStepPoint()->GetMomentumDirection()<GetTrack()->GetTrackID(); - - if ((step->GetTrack()->GetWeight() < fWeightOfEnteringParticle) && - (fNotSplitted == false)) { - - G4String logicalVolumeNamePostStep = "None"; - if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePostStep = step->GetPostStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); + + if (fActiveSource == "source_vertex"){ + //std::cout<GetPreStepPoint()->GetMomentumDirection()<FindSourceByName(fActiveSource); + GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; + LastVertexDataContainer container = vertexSource->GetLastVertexContainer(); + G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentumDirection(); + G4Step* copyInitStep = nullptr; + + G4String processToSplit = vertexSource->GetProcessToSplit(); + if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ + + if ((processToSplit != "annihil") || ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ + + step->GetfSecondary()->clear(); + if ((processToSplit != "msc") && (processToSplit != "conv")) { + fCopyInitStep= new G4Step(*step); + if (processToSplit == "eBrem"){ + fCopyInitStep->SetStepLength(container.GetStepLength()); + fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(container.GetEnergy()); - G4String processName = "None"; - if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) - processName = - step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + } + while (step->GetfSecondary()->size() == 0){ + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - if (particleName != "e+"){ - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processName) != fListOfProcesses.end()){ + } + } step->GetTrack()->SetTrackStatus(fStopAndKill); - for (int i = 0; i < step->GetfSecondary()->size(); i++){ - G4Track* track =(*(step->GetfSecondary()))[i]; - track->SetTrackStatus(fStopAndKill); + + if (processToSplit == "annihil"){ + fIsAnnihilAlreadySplit = true; + } } + else if ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ + step->GetfSecondary()->clear(); + step->GetTrack()->SetTrackStatus(fStopAndKill); } - } - - - if ((particleName == "e+") && ((step->GetTrack()->GetTrackStatus() == 1)||(step->GetTrack()->GetTrackStatus() == 2))){ - step->GetTrack()->SetTrackStatus(fStopAndKill); - for (int i = 0; i < step->GetfSecondary()->size(); i++){ - G4Track* track = (*(step->GetfSecondary()))[i]; - track->SetTrackStatus(fStopAndKill); + } - } - if ((step->GetTrack()->GetTrackStatus() == 2) || - (step->GetTrack()->GetTrackStatus() == 3)) { - - /* - delete fTrackToSplit; - delete fStepToSplit; - fTrackToSplit = new - G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); fStepToSplit - = new G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); - */ - - CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, - fProcessToSplit); + + else if (IsTheParticleUndergoesAProcess(step)){ + step->GetfSecondary()->clear(); + while (step->GetfSecondary()->size() == 0){ + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); + } + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + - if (((step->GetTrack()->GetTrackStatus() != 2) && - (step->GetTrack()->GetTrackStatus() != 3)) && - (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), - logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) { - - if (fSplitCounter < fWeightOfEnteringParticle) { - /* - delete fTrackToSplit; - delete fStepToSplit; - fTrackToSplit = new - G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); - fStepToSplit = new - G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); - */ - CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, - fProcessToSplit); - // std::cout<GetfSecondary(); - G4Track *theTrack = trackVector->back(); - fSplitCounter += theTrack->GetWeight(); - // std::cout<GetWeight()<= fWeightOfEnteringParticle) { - delete fTrackToSplit; - delete fStepToSplit; - fTrackToSplit = nullptr; - fStepToSplit = nullptr; - fSplitCounter = 0; - fNotSplitted = true; - fProcessToSplit = "None"; - theTrack->SetTrackStatus(fStopAndKill); - } + else if (IsParticleExitTheBiasedVolume(step)){ + fSplitCounter += 1; + if (fSplitCounter < fSplittingFactor){ + while (step->GetfSecondary()->size() == 0){ + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); + } } + else{ + delete fCopyInitStep; + fSplitCounter = 0; + fCounter = 0; + } + } + + fCounter ++; + } + + + fIsFirstStep = false; - if (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) { - // std::cout<GetTrack()->GetTrackStatus()<GetTrack()->GetTrackStatus()<GetTrack()->GetVertexPosition()<<" - // "<GetTrack()->GetParticleDefinition()->GetParticleName()<<" - // "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" - // "<GetTrack()->GetTrackID()<<" "<GetTrack()->GetTrackStatus()<<" - // "<GetPreStepPoint()->GetKineticEnergy()<<" " - // <GetPostStepPoint()->GetKineticEnergy()<<" - // "<GetPreStepPoint()->GetPosition()<<" - // "<GetPostStepPoint()->GetPosition()<GetTrack()->GetParentID()) && (fEventID == - fEventIDOfSplittedTrack)) - { - step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); - } - */ - if ((particleName == "e-") && - (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) && - (creatorProcessName == "conv") && - (fTrackIDOfSplittedTrack == step->GetTrack()->GetParentID()) && - (fEventID == fEventIDOfSplittedTrack)) { - step->GetTrack()->SetTrackStatus(fStopAndKill); - } + +} - /* - if ((!fSuspendForAnnihil) && (process != "annihil") && (creatorProcessName - == "annihil") && (step->GetTrack()->GetTrackStatus() != 3)) - { - G4int parentID = step->GetTrack()->GetParentID(); - if (auto search = fRememberedProcesses.find(parentID); search != - fRememberedProcesses.end()) - { - if (fRememberedProcesses[trackID].back() == "annihil") - PostponeFirstAnnihilationTrackIfInteraction(step, process); - } - } +void GateLastVertexInteractionSplittingActor::PostUserTrackingAction( + const G4Track *track) { - // If the first annihilation photon exit the collimation and the process to - split is annihilation - // We kill the second photon, because the annihilation will generate both - the photons. - - if (fSuspendForAnnihil) - { - if (trackID == fTracksToPostpone[0].GetTrackID() - 1) - { - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); - } } - */ - G4String logicalVolumeNamePostStep = "None"; - if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePostStep = step->GetPostStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - if (((step->GetTrack()->GetTrackStatus() != 2) && - (step->GetTrack()->GetTrackStatus() != 3)) && - (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), - logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) { +void GateLastVertexInteractionSplittingActor::EndOfEventAction( + const G4Event* event) { - if (auto search = fRememberedProcesses.find(trackID); - search != fRememberedProcesses.end()) { - fProcessToSplit = fRememberedProcesses[trackID].back(); - fTrackToSplit = new G4Track(*fRememberedTracks[trackID].back()); - fStepToSplit = new G4Step(*fRememberedSteps[trackID].back()); + if (fActiveSource != "source_vertex"){ + //print_tree(fTree,fTree.begin(),fTree.end()); + fVertexSource->SetNumberOfEventToSimulate(fListOfContainer.size()); + fVertexSource->SetNumberOfGeneratedEvent(0); + fVertexSource->SetListOfVertexToSimulate(fListOfContainer); + fTree.clear(); + fListOfContainer.clear(); } - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), - fProcessToSplit) != fListOfProcesses.end()) { - - fTrackIDOfSplittedTrack = trackID; - fEventIDOfSplittedTrack = fEventID; - fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; - - // Handle of pecularities (1): - - // If the process t split is the gamma issued from compton interaction, - // the electron primary generated have to be killed given that electron - // will be regenerated - - if ((fProcessToSplit == "compt") && (particleName == "gamma")) { - auto secondaries = step->GetfSecondary(); - if (secondaries->size() > 0) { - G4Track *lastSecTrack = secondaries->back(); - lastSecTrack->SetTrackStatus(fStopAndKill); - } - } - - // Handle of pecularities (2): - - // If the process to split is the annihilation, the second photon, - // postponed or not, have to be killed the reset of the postpone is - // performed here, whereas the kill of the next annihilation photon, if - // not postponed is realised at the beginning of the step tracking. - /* - if (fProcessToSplit == "annihil") - { - if (fSuspendForAnnihil) - { - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); - } - } - */ - // Handle of pecularities 3 : If the positron which created one or more - // brem photons exits all the brems photons will be killed before their - // tracking, and the conv processes will then be replayed - - if ((particleName == "e+") && (fProcessToSplit != "None")) { - - G4int parentID = step->GetTrack()->GetParentID(); - fProcessToSplit = fRememberedProcesses[parentID].back(); - delete fTrackToSplit; - delete fStepToSplit; - fTrackToSplit = new G4Track(*fRememberedTracks[parentID].back()); - fStepToSplit = new G4Step(*fRememberedSteps[parentID].back()); - fTrackIDOfInitialTrack = parentID; - - auto *vecSecondaries = step->GetfSecondary(); - vecSecondaries->clear(); - } - if (!((fProcessToSplit == "eBrem") && (particleName == "e-"))) { - CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, - fProcessToSplit); - fNotSplitted = false; - } - step->GetTrack()->SetTrackStatus(fStopAndKill); + auto* source = fSourceManager->FindSourceByName("source_vertex"); + GateLastVertexSource* vertexSource = (GateLastVertexSource*) source; + if (vertexSource->GetNumberOfGeneratedEvent() < vertexSource->GetNumberOfEventToSimulate()){ + fSourceManager->SetActiveSourcebyName("source_vertex"); } + fActiveSource = fSourceManager->GetActiveSourceName(); + } - } - - /* - if ((fSuspendForAnnihil) && ((step->GetTrack()->GetTrackStatus() == 1) || - (step->GetTrack()->GetTrackStatus() == 2))) - { - if (trackID == fTracksToPostpone[0].GetTrackID()) - { - RegenerationOfPostponedAnnihilationTrack(step); - } - } - */ - - fIsFirstStep = false; -} - -void GateLastVertexInteractionSplittingActor::PostUserTrackingAction( - const G4Track *track) {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index 62021496c..7e20585c1 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -34,6 +34,14 @@ #include "GateVActor.h" #include #include +#include "GateLastVertexSplittingDataContainer.h" +#include "tree.hh" +#include "tree_util.hh" +#include +#include "GateLastVertexSource.h" +#include "CLHEP/Vector/ThreeVector.h" +using CLHEP::Hep3Vector; + namespace py = pybind11; class GateLastVertexInteractionSplittingActor : public GateVActor { @@ -59,9 +67,17 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4double fWeightOfEnteringParticle = 0; G4double fSplitCounter = 0; G4bool fNotSplitted = true; + G4String fActiveSource = "None"; + G4bool fIsAnnihilAlreadySplit =false; + G4int fCounter; + GateLastVertexSource* fVertexSource = nullptr; + tree fTree; + tree::post_order_iterator fIterator; + std::vector fListOfContainer; + G4Track *fTrackToSplit = nullptr; - G4Step *fStepToSplit = nullptr; + G4Step* fCopyInitStep = nullptr; G4String fProcessToSplit = "None"; std::vector fTracksToPostpone; @@ -69,41 +85,40 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { std::map fRememberedTracks; std::map> fRememberedSteps; std::map> fRememberedProcesses; + std::map> fListOfProcessesAccordingParticles; + + std::vector fListOfVolumeAncestor; + std::vector fListOfBiasedVolume; std::vector fListOfProcesses = {"compt", "annihil", "eBrem", "conv", "phot"}; + virtual void StartSimulationAction() override; virtual void SteppingAction(G4Step *) override; virtual void BeginOfEventAction(const G4Event *) override; + virtual void EndOfEventAction(const G4Event *) override; virtual void BeginOfRunAction(const G4Run *run) override; virtual void PreUserTrackingAction(const G4Track *track) override; virtual void PostUserTrackingAction(const G4Track *track) override; // Pure splitting functions - G4double RussianRouletteForAngleSurvival(G4ThreeVector, G4ThreeVector, - G4double, G4double); G4Track *CreateComptonTrack(G4ParticleChangeForGamma *, G4Track, G4double); - void ComptonSplitting(G4Step *CurrentStep, G4Track *track, const G4Step *step, - G4VProcess *process); - void SecondariesSplitting(G4Step *CurrentStep, G4Track *track, - const G4Step *step, G4VProcess *process); - - // Handling the remembered processes to replay - void RememberLastProcessInformation(G4Step *); - void CreateNewParticleAtTheLastVertex(G4Step *, G4Track *, const G4Step *, - G4String); - void ResetProcessesForEnteringParticles(G4Step *step); - void ClearRememberedTracksAndSteps(std::map, - std::map>); - - // Edge case to handle the bias in annihilation - // FIXME : The triple annihilation is not handled for the moment - void PostponeFirstAnnihilationTrackIfInteraction(G4Step *step, - G4String processName); - void RegenerationOfPostponedAnnihilationTrack(G4Step *step); - void HandleTrackIDIfPostponedAnnihilation(G4Step *step); + void ComptonSplitting(G4Step* initStep,G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); + void SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); + + void CreateNewParticleAtTheLastVertex(G4Step*init,G4Step *current, LastVertexDataContainer); + G4Track* CreateATrackFromContainer(LastVertexDataContainer container, G4Step *step ); + G4bool IsTheParticleUndergoesAProcess(G4Step* step); + G4VProcess* GetProcessFromProcessName(G4String particleName, G4String pName); + G4VParticleChange* eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process); + + + void FillOfDataTree(G4Step *step); + G4bool IsParticleExitTheBiasedVolume(G4Step*step); + void CreateListOfbiasedVolume(G4LogicalVolume *volume); + void print_tree(const tree& tr, tree::pre_order_iterator it, tree::pre_order_iterator end); }; #endif diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActorOld.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActorOld.h new file mode 100644 index 000000000..3863a8b62 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActorOld.h @@ -0,0 +1,110 @@ +// +// ******************************************************************** +// * 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 GateLastVertexInteractionSplittingActorOld.h +/// \brief Definition of the GateLastVertexInteractionSplittingActorOld class +#ifndef GateLastVertexInteractionSplittingActorOld_h +#define GateLastVertexInteractionSplittingActorOld_h 1 + +#include "G4ParticleChangeForGamma.hh" +#include "G4VEnergyLossProcess.hh" +#include "GateVActor.h" +#include +#include +namespace py = pybind11; + +class GateLastVertexInteractionSplittingActorOld : public GateVActor { +public: + GateLastVertexInteractionSplittingActorOld(py::dict &user_info); + virtual ~GateLastVertexInteractionSplittingActorOld() {} + + G4double fSplittingFactor; + G4bool fRussianRouletteForAngle = false; + G4bool fRotationVectorDirector; + G4ThreeVector fVectorDirector; + G4double fMaxTheta; + G4int fTrackIDOfSplittedTrack = 0; + G4int fParentID = -1; + G4int fEventID; + G4int fEventIDOfSplittedTrack; + G4int fEventIDOfInitialSplittedTrack; + G4int fTrackIDOfInitialTrack; + G4int fTrackIDOfInitialSplittedTrack = 0; + G4int ftmpTrackID; + G4bool fIsFirstStep = true; + G4bool fSuspendForAnnihil = false; + G4double fWeightOfEnteringParticle = 0; + G4double fSplitCounter = 0; + G4bool fNotSplitted = true; + + G4Track *fTrackToSplit = nullptr; + G4Step *fStepToSplit = nullptr; + G4String fProcessToSplit = "None"; + + std::vector fTracksToPostpone; + + std::map fRememberedTracks; + std::map> fRememberedSteps; + std::map> fRememberedProcesses; + + std::vector fListOfVolumeAncestor; + + std::vector fListOfProcesses = {"compt", "annihil", "eBrem", "conv", + "phot"}; + + virtual void SteppingAction(G4Step *) override; + virtual void BeginOfEventAction(const G4Event *) override; + virtual void BeginOfRunAction(const G4Run *run) override; + virtual void PreUserTrackingAction(const G4Track *track) override; + virtual void PostUserTrackingAction(const G4Track *track) override; + + // Pure splitting functions + G4double RussianRouletteForAngleSurvival(G4ThreeVector, G4ThreeVector, + G4double, G4double); + G4Track *CreateComptonTrack(G4ParticleChangeForGamma *, G4Track, G4double); + void ComptonSplitting(G4Step *CurrentStep, G4Track *track, const G4Step *step, + G4VProcess *process); + void SecondariesSplitting(G4Step *CurrentStep, G4Track *track, + const G4Step *step, G4VProcess *process); + + // Handling the remembered processes to replay + void RememberLastProcessInformation(G4Step *); + void CreateNewParticleAtTheLastVertex(G4Step *, G4Track *, const G4Step *, + G4String); + void ResetProcessesForEnteringParticles(G4Step *step); + void ClearRememberedTracksAndSteps(std::map, + std::map>); + + // Edge case to handle the bias in annihilation + // FIXME : The triple annihilation is not handled for the moment + void PostponeFirstAnnihilationTrackIfInteraction(G4Step *step, + G4String processName); + void RegenerationOfPostponedAnnihilationTrack(G4Step *step); + void HandleTrackIDIfPostponedAnnihilation(G4Step *step); + G4bool IsParticleExitTheBiasedVolume(G4Step*step); +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingOldActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingOldActor.cpp new file mode 100644 index 000000000..fdb7a2f1e --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingOldActor.cpp @@ -0,0 +1,848 @@ +// +// ******************************************************************** +// * 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 GateLastVertexInteractionSplittingActorOld.cc +/// \brief Implementation of the GateLastVertexInteractionSplittingActorOld class + +#include "GateHelpersDict.h" +#include "GateHelpersImage.h" + +#include "CLHEP/Units/SystemOfUnits.h" +#include "G4BiasingProcessInterface.hh" +#include "G4Gamma.hh" +#include "G4LogicalVolumeStore.hh" +#include "G4ParticleTable.hh" +#include "G4PhysicalVolumeStore.hh" +#include "G4Positron.hh" +#include "G4ProcessManager.hh" +#include "G4ProcessVector.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "GateLastVertexInteractionSplittingActorOld.h" +#include "GateLastVertexSplittingPostStepDoItOld.h" +#include "GateOptnComptSplitting.h" + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateLastVertexInteractionSplittingActorOld:: + GateLastVertexInteractionSplittingActorOld(py::dict &user_info) + : GateVActor(user_info, false) { + fMotherVolumeName = DictGetStr(user_info, "mother"); + fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); + fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); + fRussianRouletteForAngle = + DictGetBool(user_info, "russian_roulette_for_angle"); + fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); + fMaxTheta = DictGetDouble(user_info, "max_theta"); + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("BeginOfEventAction"); + fActions.insert("BeginOfRunAction"); + fActions.insert("PreUserTrackingAction"); + fActions.insert("PostUserTrackingAction"); +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +G4double +GateLastVertexInteractionSplittingActorOld::RussianRouletteForAngleSurvival( + G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta, + G4double split) { + G4double cosTheta = vectorDirector * dir; + G4double theta = std::acos(cosTheta); + G4double weightToApply = 1; + if (theta > fMaxTheta) { + G4double probability = G4UniformRand(); + if (probability <= 1 / split) { + weightToApply = split; + } else { + weightToApply = 0; + } + } + return weightToApply; +} + +G4Track *GateLastVertexInteractionSplittingActorOld::CreateComptonTrack( + G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) { + G4double energy = gammaProcess->GetProposedKineticEnergy(); + G4double globalTime = track.GetGlobalTime(); + G4double newGammaWeight = weight; + G4ThreeVector polarization = gammaProcess->GetProposedPolarization(); + const G4ThreeVector momentum = gammaProcess->GetProposedMomentumDirection(); + const G4ThreeVector position = track.GetPosition(); + G4Track *newTrack = new G4Track(track); + + newTrack->SetWeight(newGammaWeight); + newTrack->SetKineticEnergy(energy); + newTrack->SetMomentumDirection(momentum); + newTrack->SetPosition(position); + newTrack->SetPolarization(polarization); + return newTrack; +} + +void GateLastVertexInteractionSplittingActorOld::ComptonSplitting( + G4Step *CurrentStep, G4Track *track, const G4Step *step, + G4VProcess *process) { + // Loop on process and add the secondary tracks to the current step secondary + // vector + + G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + G4double gammaWeight = 0; + + GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; + G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*track, *step); + G4ParticleChangeForGamma *gammaProcessFinalState = + (G4ParticleChangeForGamma *)processFinalState; + const G4ThreeVector momentum = + gammaProcessFinalState->GetProposedMomentumDirection(); + gammaWeight = fWeightOfEnteringParticle / fSplittingFactor; + if (fRussianRouletteForAngle == true) { + G4double weightToApply = RussianRouletteForAngleSurvival( + momentum, fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0) { + gammaWeight = gammaWeight * weightToApply; + G4Track *newTrack = + CreateComptonTrack(gammaProcessFinalState, *track, gammaWeight); + trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) { + G4Track *newTrack = processFinalState->GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } + } + + else { + G4Track *newTrack = + CreateComptonTrack(gammaProcessFinalState, *track, gammaWeight); + trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) { + G4Track *newTrack = processFinalState->GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } + processFinalState->Clear(); + gammaProcessFinalState->Clear(); +} + +void GateLastVertexInteractionSplittingActorOld::SecondariesSplitting( + G4Step *CurrentStep, G4Track *track, const G4Step *step, + G4VProcess *process) { + // Loop on process and add the secondary tracks to the current step secondary + // vector. + + // std::cout<GetKineticEnergy()<<" + // "<GetDynamicParticle()->GetKineticEnergy()<<" + // "<GetMomentumDirection()<<" + // "<GetDynamicParticle()->GetMomentumDirection()<GetParticleDefinition()->GetParticleName(); + G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + G4double gammaWeight = 0; + G4VParticleChange *processFinalState = nullptr; + if (process->GetProcessName() == "eBrem") { + GateBremPostStepDoIt *bremProcess = (GateBremPostStepDoIt *)process; + processFinalState = + bremProcess->GateBremPostStepDoIt::PostStepDoIt(*track, *step); + } else { + GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; + if (track->GetTrackStatus() == 0) { + // if (process->GetProcessName() == "annihil") + // std::cout<<"lol"<PostStepDoIt(*track, *step); + } + if (track->GetTrackStatus() == 1) { + GateplusannihilAtRestDoIt *eplusAnnihilProcess = + (GateplusannihilAtRestDoIt *)process; + + processFinalState = + eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*track, + *step); + } + } + + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + + if (NbOfSecondaries > 0) { + gammaWeight = fWeightOfEnteringParticle / fSplittingFactor; + G4Track *newTrack = processFinalState->GetSecondary(0); + if ((fRussianRouletteForAngle == true) && (particleName == "gamma")) { + const G4ThreeVector momentum = newTrack->GetMomentumDirection(); + G4double weightToApply = RussianRouletteForAngleSurvival( + momentum, fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0) { + gammaWeight = gammaWeight * weightToApply; + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + } else { + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + + for(int i = 1; i < NbOfSecondaries;i++){ + delete processFinalState->GetSecondary(i); + } + } else { + processFinalState->Clear(); + SecondariesSplitting(CurrentStep, track, step, process); + } + processFinalState->Clear(); +} + +void GateLastVertexInteractionSplittingActorOld::ClearRememberedTracksAndSteps( + std::map rememberedTracks, + std::map> rememberedSteps) { + std::vector trackToKill = {}; + for (auto it = rememberedTracks.begin(); it != rememberedTracks.end(); ++it) { + std::vector vector = it->second; + for (auto it2 = vector.begin(); it2 != vector.end(); it2++) { + if ((std::find(trackToKill.begin(), trackToKill.end(), *it2) == + trackToKill.end())) { + // Method set pour clear les doublons au lieu de find. + trackToKill.push_back(*it2); + } + } + } + + for (auto it = trackToKill.begin(); it != trackToKill.end(); ++it) { + delete *it; + } + + std::vector stepToKill = {}; + for (auto it = rememberedSteps.begin(); it != rememberedSteps.end(); ++it) { + std::vector vector = it->second; + for (auto it2 = vector.begin(); it2 != vector.end(); it2++) { + if ((std::find(stepToKill.begin(), stepToKill.end(), *it2) == + stepToKill.end())) { + stepToKill.push_back(*it2); + } + } + } + + for (auto it = stepToKill.begin(); it != stepToKill.end(); ++it) { + delete *it; + } +} + +void GateLastVertexInteractionSplittingActorOld::RememberLastProcessInformation( + G4Step *step) { + + // When an interesting process to split occurs, we remember the status of the + // track and the process at this current step some informations regarding the + // track info have to be changed because they were update according to the + // interaction that occured These informations are stocked as a map object, + // binding the track ID with all the track objects and processes to split. + // Because in some cases, if a secondary was created before an interaction + // chain, this secondary will be track after the chain and without this + // association, we wll loose the information about the process occuring for + // this secondary. + + G4String creatorProcessName = "None"; + if (step->GetTrack()->GetCreatorProcess() != 0) + creatorProcessName = + step->GetTrack()->GetCreatorProcess()->GetProcessName(); + G4String processName = "None"; + G4int trackID = step->GetTrack()->GetTrackID(); + G4int parentID = step->GetTrack()->GetParentID(); + //<GetTrack()->GetParticleDefinition()->GetParticleName()<<" + //"<GetTrack()->GetTrackStatus()<GetPostStepPoint()->GetProcessDefinedStep() != 0) { + processName = + step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + } + if ((step->GetTrack()->GetParticleDefinition()->GetParticleName() == "e+") && + ((step->GetTrack()->GetTrackStatus() == 1) || + (step->GetTrack()->GetTrackStatus() == 2))) { + processName = "annihil"; + } + + if ((std::find(fListOfProcesses.begin(), fListOfProcesses.end(), + processName) != fListOfProcesses.end())) { + G4Track *trackInformation = new G4Track(*(step->GetTrack())); + G4Step *stepInformation = new G4Step(*(step)); + + G4StepPoint *stepPoint = nullptr; + stepPoint = step->GetPreStepPoint(); + + trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy()); + if ((processName == "eBrem") || ((processName == "annihil") && + step->GetTrack()->GetTrackStatus() == 1)) { + trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy() - + step->GetTotalEnergyDeposit()); + } + trackInformation->SetMomentumDirection(stepPoint->GetMomentumDirection()); + if ((processName == "annihil") && + ((step->GetTrack()->GetTrackStatus() == 1))) + trackInformation->SetTrackStatus(fStopButAlive); + else { + trackInformation->SetTrackStatus(fAlive); + } + trackInformation->SetPolarization(stepPoint->GetPolarization()); + // trackInformation->SetPosition(stepPoint->GetPosition()); + + if (auto search = fRememberedTracks.find(trackID); + search != fRememberedTracks.end()) { + fRememberedTracks[trackID].push_back(trackInformation); + fRememberedProcesses[trackID].push_back(processName); + fRememberedSteps[trackID].push_back(stepInformation); + } + + else { + fRememberedTracks[trackID] = {trackInformation}; + fRememberedProcesses[trackID] = {processName}; + fRememberedSteps[trackID] = {stepInformation}; + } + } + + else { + if (auto search = fRememberedTracks.find(trackID); + search == fRememberedTracks.end()) { + if (auto search = fRememberedTracks.find(parentID); + search != fRememberedTracks.end()) { + if (auto it = std::find(fRememberedProcesses[parentID].begin(), + fRememberedProcesses[parentID].end(), + creatorProcessName); + it != fRememberedProcesses[parentID].end()) { + auto idx = it - fRememberedProcesses[parentID].begin(); + fRememberedTracks[trackID] = { + new G4Track(*fRememberedTracks[parentID][idx])}; + fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][idx]}; + fRememberedSteps[trackID] = { + new G4Step(*fRememberedSteps[parentID][idx])}; + } else { + fRememberedTracks[trackID] = { + new G4Track(*fRememberedTracks[parentID][0])}; + fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][0]}; + fRememberedSteps[trackID] = { + new G4Step(*fRememberedSteps[parentID][0])}; + } + } + } + } +} + +void GateLastVertexInteractionSplittingActorOld::CreateNewParticleAtTheLastVertex( + G4Step *CurrentStep, G4Track *track, const G4Step *step, + G4String processName) { + // We retrieve the process associated to the process name to split and we + // split according the process. Since for compton scattering, the gamma is not + // a secondary particles, this one need to have his own splitting function. + G4ParticleDefinition *particleDefinition = track->GetDefinition(); + G4ProcessManager *processManager = particleDefinition->GetProcessManager(); + G4ProcessVector *processList = processManager->GetProcessList(); + + G4VProcess *processToSplit = nullptr; + for (size_t i = 0; i < processList->size(); ++i) { + auto process = (*processList)[i]; + if (process->GetProcessName() == processName) { + processToSplit = process; + } + } + if (processName == "compt") { + + ComptonSplitting(CurrentStep, track, step, processToSplit); + } + + else { + SecondariesSplitting(CurrentStep, track, step, processToSplit); + } +} + +void GateLastVertexInteractionSplittingActorOld:: + ResetProcessesForEnteringParticles(G4Step *step) { + + // This function reset the processes and track registered to be split each + // time a particle enters into a volume. Here a new particle incoming is + // either a particle entering into the volume ( first pre step is a boundary + // of a mother volume or first step trigger the actor and the vertex is not + // either the mother volume or a the fdaughter volume) or an event generated + // within the volume (the particle did not enter into the volume and its + // parentID = 0). An additionnal condition is set with the trackID to ensure + // particles crossing twice the volume (after either a compton or pair prod) + // are not splitted. + + G4int trackID = step->GetTrack()->GetTrackID(); + if ((fEventIDOfInitialSplittedTrack != fEventID) || + ((fEventIDOfInitialSplittedTrack == fEventID) && + (trackID < fTrackIDOfInitialTrack))) { + G4String logicalVolumeNamePreStep = "None"; + if (step->GetPreStepPoint()->GetPhysicalVolume() != 0) { + logicalVolumeNamePreStep = step->GetPreStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + } + if (((step->GetPreStepPoint()->GetStepStatus() == 1) && + (logicalVolumeNamePreStep == fMotherVolumeName)) || + ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != + logicalVolumeNamePreStep) && + (step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != + fMotherVolumeName))) { + fTrackIDOfInitialTrack = trackID; + fEventIDOfInitialSplittedTrack = fEventID; + fWeightOfEnteringParticle = step->GetTrack()->GetWeight(); + ClearRememberedTracksAndSteps(fRememberedTracks, fRememberedSteps); + fRememberedProcesses.clear(); + fRememberedTracks.clear(); + fRememberedSteps.clear(); + } else if (step->GetTrack()->GetParentID() == 0) { + fTrackIDOfInitialTrack = trackID; + fEventIDOfInitialSplittedTrack = fEventID; + fWeightOfEnteringParticle = step->GetTrack()->GetWeight(); + ClearRememberedTracksAndSteps(fRememberedTracks, fRememberedSteps); + fRememberedProcesses.clear(); + fRememberedTracks.clear(); + fRememberedSteps.clear(); + } + } +} + +void GateLastVertexInteractionSplittingActorOld:: + PostponeFirstAnnihilationTrackIfInteraction(G4Step *step, + G4String processName) { + // In case the first gamma issued from annihilation undergoes an interaction, + // in order to not bias the process We keep in memory the particle post step + // state (with its secondaries) and kill the particle and its secondaries. If + // the second photon from annihilation exiting the collimation system with an + // interaction or is absorbed within the collimation, the particle is + // subsequently resimulated, starting from the interaction point. + + G4int trackID = step->GetTrack()->GetTrackID(); + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), + processName) != fListOfProcesses.end()) { + fSuspendForAnnihil = true; + G4Track trackToPostpone = G4Track(*(step->GetTrack())); + trackToPostpone.SetKineticEnergy( + step->GetPostStepPoint()->GetKineticEnergy()); + trackToPostpone.SetMomentumDirection( + step->GetPostStepPoint()->GetMomentumDirection()); + trackToPostpone.SetTrackStatus(step->GetTrack()->GetTrackStatus()); + trackToPostpone.SetPolarization( + step->GetPostStepPoint()->GetPolarization()); + trackToPostpone.SetPosition(step->GetPostStepPoint()->GetPosition()); + trackToPostpone.SetTrackID(trackID); + fTracksToPostpone.push_back(trackToPostpone); + auto theTrack = fTracksToPostpone[0]; + + auto secVec = step->GetfSecondary(); + for (int i = 0; i < secVec->size(); i++) { + G4Track *sec = (*secVec)[i]; + G4Track copySec = G4Track((*sec)); + fTracksToPostpone.push_back(copySec); + } + + fTracksToPostpone[0].SetTrackID(trackID); + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + } +} + +void GateLastVertexInteractionSplittingActorOld:: + RegenerationOfPostponedAnnihilationTrack(G4Step *step) { + + // If the second photon from annihilation suceed to exit the collimation + // system with at least one interaction or was absorbed. Resimulation of + // annihilation photons and its potential secondaries. + + G4TrackVector *currentSecondaries = step->GetfSecondary(); + for (int i = 0; i < fTracksToPostpone.size(); i++) { + G4Track *trackToAdd = + new G4Track(fTracksToPostpone[fTracksToPostpone.size() - 1 - i]); + trackToAdd->SetParentID(step->GetTrack()->GetTrackID() - 1); + + currentSecondaries->insert(currentSecondaries->begin(), trackToAdd); + } + G4Track *firstPostponedTrack = (*currentSecondaries)[0]; + + // Handle of case were the interaction killed the photon issued from + // annihilation, it will not be track at the following state and the boolean + // plus the track vector need to be reset + + if (firstPostponedTrack->GetTrackStatus() == 2) { + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); + } +} + +void GateLastVertexInteractionSplittingActorOld:: + HandleTrackIDIfPostponedAnnihilation(G4Step *step) { + // The ID is to modify trackID and the processes and tracks sotcked for a + // specific trackID associated to the postponed annihilation to respect the + // trackID order in GEANT4. Since in this case the second photon is tracked + // before the first one his trackID +=1. For the postponed one, he is the + // first secondary particle of the second photon tracks, his trackID is + // therefore equal to the second photon trackID +1 instead of the second + // photon trackID -1. The parentID of secondary particles are also modified + // because they are used in the rememberlastprocess function At last the value + // associated to a specific trackID in the process and track map are modified + // according to the new trackID. + + if (fSuspendForAnnihil) { + if (step->GetTrack()->GetTrackID() == + fTracksToPostpone[0].GetTrackID() - 1) { + fRememberedProcesses[step->GetTrack()->GetTrackID()] = + fRememberedProcesses[step->GetTrack()->GetTrackID() + 1]; + fRememberedProcesses.erase(step->GetTrack()->GetTrackID() + 1); + fRememberedSteps[step->GetTrack()->GetTrackID()] = + fRememberedSteps[step->GetTrack()->GetTrackID() + 1]; + fRememberedSteps.erase(step->GetTrack()->GetTrackID() + 1); + fRememberedTracks[step->GetTrack()->GetTrackID()] = + fRememberedTracks[step->GetTrack()->GetTrackID() + 1]; + fRememberedTracks.erase(step->GetTrack()->GetTrackID() + 1); + auto vecSec = step->GetSecondary(); + for (int i = 0; i < vecSec->size(); i++) { + (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() + 1); + } + step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() + 1); + } + if (step->GetTrack()->GetTrackID() == + fTracksToPostpone[0].GetTrackID() + 1) { + auto vecSec = step->GetSecondary(); + for (int i = 0; i < vecSec->size(); i++) { + (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() - 2); + } + step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() - 2); + } + } +} + +void GateLastVertexInteractionSplittingActorOld::BeginOfRunAction( + const G4Run *run) { + + // The way to behave of the russian roulette is the following : + // we provide a vector director and the theta angle acceptance, where theta = + // 0 is a vector colinear to the vector director Then if the track generated + // is on the acceptance angle, we add it to the primary track, and if it's not + // the case, we launch the russian roulette + + if (fRotationVectorDirector) { + G4VPhysicalVolume *physBiasingVolume = + G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume->GetObjectRotationValue(); + fVectorDirector = rot * fVectorDirector; + } +} + +void GateLastVertexInteractionSplittingActorOld::BeginOfEventAction( + const G4Event *event) { + + fParentID = -1; + fEventID = event->GetEventID(); + fEventIDOfSplittedTrack = -1; + fTrackIDOfSplittedTrack = -1; + fNotSplitted == true; +} + +void GateLastVertexInteractionSplittingActorOld::PreUserTrackingAction( + const G4Track *track) { + fIsFirstStep = true; +} + +void GateLastVertexInteractionSplittingActorOld::SteppingAction(G4Step *step) { + + G4String particleName = + step->GetTrack()->GetParticleDefinition()->GetParticleName(); + G4String creatorProcessName = "None"; + + if (step->GetTrack()->GetCreatorProcess() != 0) + creatorProcessName = + step->GetTrack()->GetCreatorProcess()->GetProcessName(); + + // std::cout<GetTotalEnergyDeposit()<<" + // "<GetPreStepPoint()->GetKineticEnergy()<<" + // "<GetPreStepPoint()->GetMomentumDirection()<GetTrack()->GetTrackID()<<" + // "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" + // "<GetTrack()->GetTrackID(); + + if ((step->GetTrack()->GetWeight() < fWeightOfEnteringParticle) && + (fNotSplitted == false)) { + + G4String logicalVolumeNamePostStep = "None"; + if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + + G4String processName = "None"; + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) + processName = + step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + + if (particleName != "e+"){ + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processName) != fListOfProcesses.end()){ + step->GetTrack()->SetTrackStatus(fStopAndKill); + for (int i = 0; i < step->GetfSecondary()->size(); i++){ + G4Track* track =(*(step->GetfSecondary()))[i]; + track->SetTrackStatus(fStopAndKill); + } + } + } + + + if ((particleName == "e+") && ((step->GetTrack()->GetTrackStatus() == 1)||(step->GetTrack()->GetTrackStatus() == 2))){ + step->GetTrack()->SetTrackStatus(fStopAndKill); + for (int i = 0; i < step->GetfSecondary()->size(); i++){ + G4Track* track = (*(step->GetfSecondary()))[i]; + track->SetTrackStatus(fStopAndKill); + } + } + + if ((step->GetTrack()->GetTrackStatus() == 2) || + (step->GetTrack()->GetTrackStatus() == 3)) { + + /* + delete fTrackToSplit; + delete fStepToSplit; + fTrackToSplit = new + G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); fStepToSplit + = new G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); + */ + + CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, + fProcessToSplit); + } + + if (((step->GetTrack()->GetTrackStatus() != 2) && + (step->GetTrack()->GetTrackStatus() != 3)) && + (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), + logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) { + + if (fSplitCounter < fWeightOfEnteringParticle) { + /* + delete fTrackToSplit; + delete fStepToSplit; + fTrackToSplit = new + G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); + fStepToSplit = new + G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); + */ + CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, + fProcessToSplit); + // std::cout<GetfSecondary(); + G4Track *theTrack = trackVector->back(); + fSplitCounter += theTrack->GetWeight(); + // std::cout<GetWeight()<= fWeightOfEnteringParticle) { + delete fTrackToSplit; + delete fStepToSplit; + fTrackToSplit = nullptr; + fStepToSplit = nullptr; + fSplitCounter = 0; + fNotSplitted = true; + fProcessToSplit = "None"; + theTrack->SetTrackStatus(fStopAndKill); + } + } + } + } + + if (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) { + // std::cout<GetTrack()->GetTrackStatus()<GetTrack()->GetTrackStatus()<GetTrack()->GetVertexPosition()<<" + // "<GetTrack()->GetParticleDefinition()->GetParticleName()<<" + // "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" + // "<GetTrack()->GetTrackID()<<" "<GetTrack()->GetTrackStatus()<<" + // "<GetPreStepPoint()->GetKineticEnergy()<<" " + // <GetPostStepPoint()->GetKineticEnergy()<<" + // "<GetPreStepPoint()->GetPosition()<<" + // "<GetPostStepPoint()->GetPosition()<GetTrack()->GetParentID()) && (fEventID == + fEventIDOfSplittedTrack)) + { + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + } + */ + + if ((particleName == "e-") && + (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) && + (creatorProcessName == "conv") && + (fTrackIDOfSplittedTrack == step->GetTrack()->GetParentID()) && + (fEventID == fEventIDOfSplittedTrack)) { + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + + /* + if ((!fSuspendForAnnihil) && (process != "annihil") && (creatorProcessName + == "annihil") && (step->GetTrack()->GetTrackStatus() != 3)) + { + G4int parentID = step->GetTrack()->GetParentID(); + if (auto search = fRememberedProcesses.find(parentID); search != + fRememberedProcesses.end()) + { + if (fRememberedProcesses[trackID].back() == "annihil") + PostponeFirstAnnihilationTrackIfInteraction(step, process); + } + } + + // If the first annihilation photon exit the collimation and the process to + split is annihilation + // We kill the second photon, because the annihilation will generate both + the photons. + + if (fSuspendForAnnihil) + { + if (trackID == fTracksToPostpone[0].GetTrackID() - 1) + { + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); + } + } + */ + + G4String logicalVolumeNamePostStep = "None"; + G4ThreeVector particleMomentum = step->GetPostStepPoint()->GetMomentumDirection(); + if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + + if (((step->GetTrack()->GetTrackStatus() != 2) && + (step->GetTrack()->GetTrackStatus() != 3)) && + (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), + logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())&& (particleMomentum[2] < 0)) { + + if (auto search = fRememberedProcesses.find(trackID); + search != fRememberedProcesses.end()) { + + fProcessToSplit = fRememberedProcesses[trackID].back(); + fTrackToSplit = new G4Track(*fRememberedTracks[trackID].back()); + fStepToSplit = new G4Step(*fRememberedSteps[trackID].back()); + } + + if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), + fProcessToSplit) != fListOfProcesses.end()) { + + fTrackIDOfSplittedTrack = trackID; + fEventIDOfSplittedTrack = fEventID; + fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; + + // Handle of pecularities (1): + + // If the process t split is the gamma issued from compton interaction, + // the electron primary generated have to be killed given that electron + // will be regenerated + + if ((fProcessToSplit == "compt") && (particleName == "gamma")) { + auto secondaries = step->GetfSecondary(); + if (secondaries->size() > 0) { + G4Track *lastSecTrack = secondaries->back(); + lastSecTrack->SetTrackStatus(fStopAndKill); + } + } + + // Handle of pecularities (2): + + // If the process to split is the annihilation, the second photon, + // postponed or not, have to be killed the reset of the postpone is + // performed here, whereas the kill of the next annihilation photon, if + // not postponed is realised at the beginning of the step tracking. + /* + if (fProcessToSplit == "annihil") + { + if (fSuspendForAnnihil) + { + fSuspendForAnnihil = false; + fTracksToPostpone.clear(); + } + } + */ + // Handle of pecularities 3 : If the positron which created one or more + // brem photons exits all the brems photons will be killed before their + // tracking, and the conv processes will then be replayed + + if ((particleName == "e+") && (fProcessToSplit != "None")) { + + G4int parentID = step->GetTrack()->GetParentID(); + fProcessToSplit = fRememberedProcesses[parentID].back(); + delete fTrackToSplit; + delete fStepToSplit; + fTrackToSplit = new G4Track(*fRememberedTracks[parentID].back()); + fStepToSplit = new G4Step(*fRememberedSteps[parentID].back()); + fTrackIDOfInitialTrack = parentID; + + auto *vecSecondaries = step->GetfSecondary(); + vecSecondaries->clear(); + } + if (!((fProcessToSplit == "eBrem") && (particleName == "e-"))) { + CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, + fProcessToSplit); + fNotSplitted = false; + } + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + } + } + + /* + if ((fSuspendForAnnihil) && ((step->GetTrack()->GetTrackStatus() == 1) || + (step->GetTrack()->GetTrackStatus() == 2))) + { + if (trackID == fTracksToPostpone[0].GetTrackID()) + { + RegenerationOfPostponedAnnihilationTrack(step); + } + } + */ + + fIsFirstStep = false; +} + +void GateLastVertexInteractionSplittingActorOld::PostUserTrackingAction( + const G4Track *track) {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateLastVertexSource.cpp b/core/opengate_core/opengate_lib/GateLastVertexSource.cpp new file mode 100644 index 000000000..e89c5a728 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSource.cpp @@ -0,0 +1,112 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include "GateLastVertexSource.h" +#include "G4ParticleTable.hh" +#include "GateHelpersDict.h" +#include + +GateLastVertexSource::GateLastVertexSource() : GateVSource() { +} + +GateLastVertexSource::~GateLastVertexSource() { +} + +void GateLastVertexSource::InitializeUserInfo(py::dict &user_info) { + GateVSource::InitializeUserInfo(user_info); + // get user info about activity or nb of events + fN = DictGetInt(user_info, "n"); + } + +double GateLastVertexSource::PrepareNextTime(double current_simulation_time) { + + /* + // If all N events have been generated, we stop (negative time) + if (fNumberOfGeneratedEvents >= fN){ + std::cout << "LV: "<< -1<<" "<< fNumberOfGeneratedEvents <<" "<< fN<= fN) + return -1; + + return fStartTime + 1; +} + +void GateLastVertexSource::PrepareNextRun() { + // The following compute the global transformation from + // the local volume (mother) to the world + GateVSource::PrepareNextRun(); + + // The global transformation to apply for the current RUN is known in : + // fGlobalTranslation & fGlobalRotation + + // init the number of generated events (here, for each run) + fNumberOfGeneratedEvents = 0; + +} + +void GateLastVertexSource::GenerateOnePrimary(G4Event* event, double current_simulation_time, G4int idx ){ + + if (fNumberOfGeneratedEvents >= fN){ + auto *particle_table = G4ParticleTable::GetParticleTable(); + auto *fParticleDefinition = particle_table->FindParticle("geantino"); + auto *particle = new G4PrimaryParticle(fParticleDefinition); + particle->SetKineticEnergy(0); + particle->SetMomentumDirection({1,0,0}); + particle->SetWeight(1); + auto *vertex = new G4PrimaryVertex({0,0,0}, current_simulation_time); + vertex->SetPrimary(particle); + event->AddPrimaryVertex(vertex); + } + else { + + + G4double energy = fListOfContainer[idx].GetEnergy(); + if (energy < 0){ + energy = 0; + } + fContainer = fListOfContainer[idx]; + G4ThreeVector position = fListOfContainer[idx].GetVertexPosition(); + G4ThreeVector momentum = fListOfContainer[idx].GetMomentum(); + G4String particleName = fListOfContainer[idx].GetParticleNameToSplit(); + G4double weight =fListOfContainer[idx].GetWeight(); + fProcessToSplit = fListOfContainer[idx].GetProcessNameToSplit(); + + auto &l = fThreadLocalData.Get(); + auto *particle_table = G4ParticleTable::GetParticleTable(); + auto *fParticleDefinition = particle_table->FindParticle(particleName); + auto *particle = new G4PrimaryParticle(fParticleDefinition); + particle->SetKineticEnergy(energy); + particle->SetMomentumDirection(momentum); + particle->SetWeight(weight); + auto *vertex = new G4PrimaryVertex(position, current_simulation_time); + vertex->SetPrimary(particle); + event->AddPrimaryVertex(vertex); + } + +} + + +void GateLastVertexSource::GeneratePrimaries(G4Event *event, + double current_simulation_time) { + + GenerateOnePrimary(event,current_simulation_time,fNumberOfGeneratedEvents); + fNumberOfGeneratedEvents++; + if (fNumberOfGeneratedEvents == fListOfContainer.size()){ + fListOfContainer.clear(); + } +} diff --git a/core/opengate_core/opengate_lib/GateLastVertexSource.h b/core/opengate_core/opengate_lib/GateLastVertexSource.h new file mode 100644 index 000000000..dd23c124d --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSource.h @@ -0,0 +1,85 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#ifndef GateLastVertexSource_h +#define GateLastVertexSource_h + +#include "GateAcceptanceAngleTesterManager.h" +#include "GateSingleParticleSource.h" +#include "GateVSource.h" +#include "GateLastVertexSplittingDataContainer.h" +#include + +namespace py = pybind11; + +/* + This is NOT a real source type but a template to help writing your own source + type. Copy-paste this file with a different name ("MyNewSource.hh") and start + building. You also need to copy : GateLastVertexSource.hh GateLastVertexSource.cpp + pyGateLastVertexSource.cpp + And add the source declaration in opengate_core.cpp + */ + +class GateLastVertexSource : public GateVSource { + +public: + GateLastVertexSource(); + + ~GateLastVertexSource() override; + + void InitializeUserInfo(py::dict &user_info) override; + + double PrepareNextTime(double current_simulation_time) override; + + void PrepareNextRun() override; + + void GeneratePrimaries(G4Event *event, double time) override; + + + void GenerateOnePrimary(G4Event *event, double time,G4int idx); + + + void SetListOfVertexToSimulate(std::vector list){ + fListOfContainer = list; + } + + void SetNumberOfGeneratedEvent(G4int nbEvent){ + fNumberOfGeneratedEvents = nbEvent; + } + + void SetNumberOfEventToSimulate(G4int N){ + fN = N; + } + + G4int GetNumberOfEventToSimulate(){ + return fN; + } + + G4int GetNumberOfGeneratedEvent(){ + return fNumberOfGeneratedEvents; + } + + G4String GetProcessToSplit(){ + return fProcessToSplit; + } + + LastVertexDataContainer GetLastVertexContainer(){ + return fContainer; + } + + +protected: + G4int fNumberOfGeneratedEvents = 0; + G4int fN = 0; + double fFloatValue; + std::vector fVectorValue; + std::vector fListOfContainer; + G4String fProcessToSplit = "None"; + LastVertexDataContainer fContainer; +}; + +#endif // GateLastVertexSource_h diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h new file mode 100644 index 000000000..112cc8062 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h @@ -0,0 +1,343 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +/// +#ifndef LastVertexDataContainer_h +#define LastVertexDataContainer_h + + +#include +#include "G4VEnergyLossProcess.hh" +#include "G4Track.hh" +#include "G4VEmProcess.hh" +#include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "G4PhysicalConstants.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4Gamma.hh" +#include "G4Electron.hh" +#include "G4Positron.hh" +#include "G4eeToTwoGammaModel.hh" +#include "G4EmBiasingManager.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilationEntanglementClipBoard.hh" +#include "G4EmParameters.hh" +#include "G4PhysicsModelCatalog.hh" + + +class LastVertexDataContainer{ + +public : + +LastVertexDataContainer(G4ThreeVector interactionPosition, G4ThreeVector momentum,G4ThreeVector polarization, G4double energy,G4int trackID,G4String creationProcessName){ + + fPositionToSplit= interactionPosition; + fMomentumToSplit = momentum; + fEnergyToSplit = energy; + fIsExit = false; + fToRegenerate = false; + fTrackID = trackID; + fCreationProcessName = creationProcessName; + fPolarizationToSplit = polarization; + +} + + +LastVertexDataContainer(){} + +~LastVertexDataContainer(){} + +void SetProcessNameToSplit(G4String processName){ + fProcessNameToSplit = processName; +} + +G4String GetProcessNameToSplit(){ + return fProcessNameToSplit; +} + +void SetEnergy(G4double energy){ + fEnergyToSplit =energy; +} + +G4double GetEnergy(){ + return fEnergyToSplit; +} + + +void SetWeight(G4double weight){ + fWeightToSplit =weight; +} + +G4double GetWeight(){ + return fWeightToSplit; +} + +void SetPolarization(G4ThreeVector polarization){ + fPolarizationToSplit = polarization; +} + +G4ThreeVector GetPolarization(){ + return fPolarizationToSplit; +} + +void SetMomentum(G4ThreeVector momentum){ + fMomentumToSplit =momentum; +} + +G4ThreeVector GetMomentum(){ + return fMomentumToSplit; +} + +void SetVertexPosition(G4ThreeVector position){ + fPositionToSplit = position; +} + +G4ThreeVector GetVertexPosition(){ + return fPositionToSplit; +} + +void SetExitingStatus(G4bool isExit){ + fIsExit = isExit; +} + +G4bool GetExitingStatus(){ + return fIsExit; +} + + +void SetRegenerationStatus(G4bool toRegenerate){ + fToRegenerate= toRegenerate; +} + +G4bool GetRegenerationStatus(){ + return fToRegenerate; +} + + + +void SetTrackID(G4int trackID ){ + fTrackID = trackID; +} + +G4int GetTrackID(){ + return fTrackID; +} + + +void SetParticleName(G4String name){ + fParticleName = name; +} + +G4String GetParticleName(){ + return fParticleName; +} + +void SetParticleNameToSplit(G4String name){ + fParticleNameToSplit = name; +} + +G4String GetParticleNameToSplit(){ + return fParticleNameToSplit; +} + + +void SetCreationProcessName(G4String creationProcessName){ + fCreationProcessName = creationProcessName; +} + +G4String GetCreationProcessName(){ + return fCreationProcessName; +} + + + +void SetTrackStatus(G4int trackStatus){ + fTrackStatusToSplit = trackStatus; +} + + +G4int GetTrackStatus(){ + return fTrackStatusToSplit; +} + + +void SetNbOfSecondaries(G4int nbSec){ + fNumberOfSecondariesToSplit = nbSec; +} + +G4int GetNbOfSecondaries(){ + return fNumberOfSecondariesToSplit; +} + +void SetAnnihilationFlag(G4String flag){ + fAnnihilProcessFlag = flag; +} + +G4String GetAnnihilationFlag(){ + return fAnnihilProcessFlag; +} + +void SetStepLength(G4double length){ + fStepLength = length; +} + +G4double GetStepLength(){ + return fStepLength; +} + + + +void SetSplittingParameters(G4String processName,G4double energy,G4ThreeVector momentum, G4ThreeVector position,G4ThreeVector polarization,G4String name,G4double weight,G4int trackStatus,G4int nbSec,G4String flag,G4double length, G4ThreeVector prePos){ + fProcessNameToSplit = processName; + fEnergyToSplit = energy; + fMomentumToSplit = momentum; + fPositionToSplit = position; + fPolarizationToSplit = polarization; + fParticleNameToSplit = name; + fWeightToSplit = weight; + fTrackStatusToSplit = trackStatus; + fNumberOfSecondariesToSplit = nbSec; + fAnnihilProcessFlag = flag; + fStepLength = length; + fPrePosition = prePos; +} + + + + + +void PushListOfSplittingParameters(G4String processName,G4double energy,G4ThreeVector momentum, G4ThreeVector position,G4ThreeVector polarization,G4String name,G4double weight, G4int trackStatus,G4int nbSec, G4String flag,G4double length,G4ThreeVector prePos){ + fVectorOfProcessToSplit.push_back(processName); + fVectorOfEnergyToSplit.push_back(energy); + fVectorOfMomentumToSplit.push_back(momentum); + fVectorOfPositionToSplit.push_back(position); + fVectorOfPolarizationToSplit.push_back(polarization); + fVectorOfParticleNameToSplit.push_back(name); + fVectorOfWeightToSplit.push_back(weight); + fVectorOfTrackStatusToSplit.push_back(trackStatus); + fVectorOfNumberOfSecondariesToSplit.push_back(nbSec); + fVectorOfAnnihilProcessFlag.push_back(flag); + fVectorOfStepLength.push_back(length); + fVectorOfPrePosition.push_back(prePos); + +} + + +LastVertexDataContainer ContainerFromParentInformation(G4Step* step){ + LastVertexDataContainer aContainer = LastVertexDataContainer(); + aContainer.fTrackID = step->GetTrack()->GetTrackID(); + aContainer.fParticleName = step->GetTrack()->GetDefinition()->GetParticleName(); + if (this->fProcessNameToSplit != "None"){ + if (this->fVectorOfProcessToSplit.size() !=0){ + G4ThreeVector vertexPosition = step->GetTrack()->GetVertexPosition(); + for (int i =0;ifVectorOfPositionToSplit.size();i++){ + if (vertexPosition == this->fVectorOfPositionToSplit[i]){ + aContainer.SetSplittingParameters(this->fVectorOfProcessToSplit[i],this->fVectorOfEnergyToSplit[i],this->fVectorOfMomentumToSplit[i],this->fVectorOfPositionToSplit[i],this->fVectorOfPolarizationToSplit[i],this->fVectorOfParticleNameToSplit[i],this->fVectorOfWeightToSplit[i], this->fVectorOfTrackStatusToSplit[i], this->fVectorOfNumberOfSecondariesToSplit[i], this->fVectorOfAnnihilProcessFlag[i],this->fVectorOfStepLength[i],this->fVectorOfPrePosition[i]); + return aContainer; + } + } + } + else{ + aContainer.SetSplittingParameters(this->fProcessNameToSplit,this->fEnergyToSplit,this->fMomentumToSplit,this->fPositionToSplit,this->fPolarizationToSplit,this->fParticleNameToSplit,this->fWeightToSplit, this->fTrackStatusToSplit, this->fNumberOfSecondariesToSplit, this->fAnnihilProcessFlag,this->fStepLength,this->fPrePosition); + return aContainer; + } + } + return aContainer; +} + + + + + + + +void DumpInfoToSplit(){ + std::cout<<"Particle name of the particle to split: "< fVectorOfMomentumToSplit; +std::vector fVectorOfPositionToSplit; +std::vector fVectorOfPolarizationToSplit; +std::vector fVectorOfEnergyToSplit; +std::vector fVectorOfProcessToSplit; +std::vector fVectorOfParticleNameToSplit; +std::vector fVectorOfWeightToSplit; +std::vectorfVectorOfTrackStatusToSplit; +std::vectorfVectorOfNumberOfSecondariesToSplit; +std::vectorfVectorOfAnnihilProcessFlag; +std::vectorfVectorOfStepLength; +std::vectorfVectorOfPrePosition; + + + + +}; + +#endif + + + + + + \ No newline at end of file diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h index 30e5b93c2..40b3fdbb4 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h @@ -63,7 +63,6 @@ virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & return particleChange; } - }; diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoItOld.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoItOld.h new file mode 100644 index 000000000..df30a39d7 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoItOld.h @@ -0,0 +1,110 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +/// +#ifndef GateLastVertexSplittingPostStepDoItOld_h +#define GateLastVertexSplittingPostStepDoItOld_h + + +#include "G4VEnergyLossProcess.hh" +#include "G4VEmProcess.hh" +#include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "G4PhysicalConstants.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4Gamma.hh" +#include "G4Electron.hh" +#include "G4Positron.hh" +#include "G4eeToTwoGammaModel.hh" +#include "G4EmBiasingManager.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilationEntanglementClipBoard.hh" +#include "G4EmParameters.hh" +#include "G4PhysicsModelCatalog.hh" +#include + + + + +class GateBremPostStepDoIt : public G4VEnergyLossProcess { +public : + +GateBremPostStepDoIt(); + +~ GateBremPostStepDoIt(); + +virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & step) override +{ + const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); + currentCouple = couple; + G4VParticleChange* particleChange = G4VEnergyLossProcess::PostStepDoIt(track,step); + return particleChange; +} + + +}; + + +class GateGammaEmPostStepDoIt : public G4VEmProcess { +public : + +GateGammaEmPostStepDoIt(); + +~ GateGammaEmPostStepDoIt(); + +virtual G4VParticleChange * PostStepDoIt(const G4Track & track, const G4Step & step) override +{ + const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); + currentCouple = couple; + G4VParticleChange* particleChange = G4VEmProcess::PostStepDoIt(track,step); + return particleChange; +} + + +}; + +class GateplusannihilAtRestDoIt : public G4eplusAnnihilation { +public : + +GateplusannihilAtRestDoIt(); +~ GateplusannihilAtRestDoIt(); + +virtual G4VParticleChange* AtRestDoIt(const G4Track& track, + const G4Step& step) override +// Performs the e+ e- annihilation when both particles are assumed at rest. + { + G4Track copyTrack = G4Track(track); + copyTrack.SetStep(&step); + G4VParticleChange* particleChange = G4eplusAnnihilation::AtRestDoIt(copyTrack,step); + return particleChange; + } +}; +#endif + + + + + + \ No newline at end of file diff --git a/core/opengate_core/opengate_lib/GateSourceManager.cpp b/core/opengate_core/opengate_lib/GateSourceManager.cpp index a1d191c89..df119ce11 100644 --- a/core/opengate_core/opengate_lib/GateSourceManager.cpp +++ b/core/opengate_core/opengate_lib/GateSourceManager.cpp @@ -181,6 +181,9 @@ void GateSourceManager::PrepareRunToStart(int run_id) { : std::to_string(G4Threading::G4GetThreadId())); } + + + void GateSourceManager::PrepareNextSource() { auto &l = fThreadLocalData.Get(); l.fNextActiveSource = nullptr; @@ -195,6 +198,7 @@ void GateSourceManager::PrepareNextSource() { l.fNextSimulationTime = t; } } + // If no next time in the current interval, active source is NULL } diff --git a/core/opengate_core/opengate_lib/GateSourceManager.h b/core/opengate_core/opengate_lib/GateSourceManager.h index accb5aef5..9914a3b92 100644 --- a/core/opengate_core/opengate_lib/GateSourceManager.h +++ b/core/opengate_core/opengate_lib/GateSourceManager.h @@ -61,6 +61,22 @@ class GateSourceManager : public G4VUserPrimaryGeneratorAction { // Return a source GateVSource *FindSourceByName(std::string name) const; + + + G4String GetActiveSourceName(){ + auto &l = fThreadLocalData.Get(); + if (l.fNextActiveSource !=0){ + G4String name = l.fNextActiveSource->fName; + return name; + } + return "None"; + } + + void SetActiveSourcebyName(G4String sourceName){ + auto &l = fThreadLocalData.Get(); + auto* source = FindSourceByName(sourceName); + l.fNextActiveSource = source; + } // [available on py side] start the simulation, master thread only void StartMasterThread(); diff --git a/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActorOld.cpp b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActorOld.cpp new file mode 100644 index 000000000..a15dc55e8 --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActorOld.cpp @@ -0,0 +1,21 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ +#include + +namespace py = pybind11; +#include "GateLastVertexInteractionSplittingActorOld.h" + +void init_GateLastVertexInteractionSplittingActorOld(py::module &m) { + + py::class_>( + m, "GateLastVertexInteractionSplittingActorOld") + .def_readwrite( + "fListOfVolumeAncestor", + &GateLastVertexInteractionSplittingActorOld::fListOfVolumeAncestor) + .def(py::init()); +} diff --git a/core/opengate_core/opengate_lib/pyGateLastVertexSource.cpp b/core/opengate_core/opengate_lib/pyGateLastVertexSource.cpp new file mode 100644 index 000000000..0cb59f146 --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateLastVertexSource.cpp @@ -0,0 +1,22 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include + +namespace py = pybind11; + +#include "GateLastVertexSource.h" + +void init_GateLastVertexSource(py::module &m) { + + py::class_(m, "GateLastVertexSource") + .def(py::init()) + .def("InitializeUserInfo", &GateLastVertexSource::InitializeUserInfo) + // If needed: add your own class functions that will be accessible from + // python side. + ; +} diff --git a/core/opengate_core/opengate_lib/tree.hh b/core/opengate_core/opengate_lib/tree.hh new file mode 100644 index 000000000..4f78d5b8a --- /dev/null +++ b/core/opengate_core/opengate_lib/tree.hh @@ -0,0 +1,3412 @@ + +// STL-like templated tree class. +// +// Copyright (C) 2001-2024 Kasper Peeters +// Distributed under the GNU General Public License version 3. +// +// Special permission to use tree.hh under the conditions of a +// different license can be requested from the author. + +/** \mainpage tree.hh + \author Kasper Peeters + \version 3.20 + \date 2024-04-12 + \see http://github.com/kpeeters/tree.hh/ + + The tree.hh library for C++ provides an STL-like container class + for n-ary trees, templated over the data stored at the + nodes. Various types of iterators are provided (post-order, + pre-order, and others). Where possible the access methods are + compatible with the STL or alternative algorithms are + available. +*/ + + +#ifndef tree_hh_ +#define tree_hh_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// A node in the tree, combining links to other nodes as well as the actual data. +template +class tree_node_ { // size: 5*4=20 bytes (on 32 bit arch), can be reduced by 8. + public: + tree_node_(); + tree_node_(const T&); + tree_node_(T&&); + + tree_node_ *parent; + tree_node_ *first_child, *last_child; + tree_node_ *prev_sibling, *next_sibling; + T data; +}; + +template +tree_node_::tree_node_() + : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0) + { + } + +template +tree_node_::tree_node_(const T& val) + : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), data(val) + { + } + +template +tree_node_::tree_node_(T&& val) + : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), data(val) + { + } + +// Throw an exception with a stacktrace. + +//template +//void throw_with_trace(const E& e) +// { +// throw boost::enable_error_info(e) +// << traced(boost::stacktrace::stacktrace()); +// } + +class navigation_error : public std::logic_error { + public: + navigation_error(const std::string& s) : std::logic_error(s) + { +// assert(1==0); +// std::ostringstream str; +// std::cerr << boost::stacktrace::stacktrace() << std::endl; +// str << boost::stacktrace::stacktrace(); +// stacktrace=str.str(); + } + +// virtual const char *what() const noexcept override +// { +// return (std::logic_error::what()+std::string("; ")+stacktrace).c_str(); +// } +// +// std::string stacktrace; +}; + +template > > +class tree { + protected: + typedef tree_node_ tree_node; + public: + /// Value of the data stored at a node. + typedef T value_type; + + class iterator_base; + class pre_order_iterator; + class post_order_iterator; + class sibling_iterator; + class leaf_iterator; + + tree(); // empty constructor + tree(const T&); // constructor setting given element as head + tree(const iterator_base&); + tree(const tree&); // copy constructor + tree(tree&&); // move constructor + ~tree(); + tree& operator=(const tree&); // copy assignment + tree& operator=(tree&&); // move assignment + + /// Base class for iterators, only pointers stored, no traversal logic. +#ifdef __SGI_STL_PORT + class iterator_base : public stlport::bidirectional_iterator { +#else + class iterator_base { +#endif + public: + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + iterator_base(); + iterator_base(tree_node *); + + T& operator*() const; + T* operator->() const; + + /// When called, the next increment/decrement skips children of this node. + void skip_children(); + void skip_children(bool skip); + /// Number of children of the node pointed to by the iterator. + unsigned int number_of_children() const; + + sibling_iterator begin() const; + sibling_iterator end() const; + + tree_node *node; + protected: + bool skip_current_children_; + }; + + /// Depth-first iterator, first accessing the node, then its children. + class pre_order_iterator : public iterator_base { + public: + pre_order_iterator(); + pre_order_iterator(tree_node *); + pre_order_iterator(const iterator_base&); + pre_order_iterator(const sibling_iterator&); + + bool operator==(const pre_order_iterator&) const; + bool operator!=(const pre_order_iterator&) const; + pre_order_iterator& operator++(); + pre_order_iterator& operator--(); + pre_order_iterator operator++(int); + pre_order_iterator operator--(int); + pre_order_iterator& operator+=(unsigned int); + pre_order_iterator& operator-=(unsigned int); + + pre_order_iterator& next_skip_children(); + }; + + /// Depth-first iterator, first accessing the children, then the node itself. + class post_order_iterator : public iterator_base { + public: + post_order_iterator(); + post_order_iterator(tree_node *); + post_order_iterator(const iterator_base&); + post_order_iterator(const sibling_iterator&); + + bool operator==(const post_order_iterator&) const; + bool operator!=(const post_order_iterator&) const; + post_order_iterator& operator++(); + post_order_iterator& operator--(); + post_order_iterator operator++(int); + post_order_iterator operator--(int); + post_order_iterator& operator+=(unsigned int); + post_order_iterator& operator-=(unsigned int); + + /// Set iterator to the first child as deep as possible down the tree. + void descend_all(); + }; + + /// Breadth-first iterator, using a queue + class breadth_first_queued_iterator : public iterator_base { + public: + breadth_first_queued_iterator(); + breadth_first_queued_iterator(tree_node *); + breadth_first_queued_iterator(const iterator_base&); + + bool operator==(const breadth_first_queued_iterator&) const; + bool operator!=(const breadth_first_queued_iterator&) const; + breadth_first_queued_iterator& operator++(); + breadth_first_queued_iterator operator++(int); + breadth_first_queued_iterator& operator+=(unsigned int); + + private: + std::queue traversal_queue; + }; + + /// The default iterator types throughout the tree class. + typedef pre_order_iterator iterator; + typedef breadth_first_queued_iterator breadth_first_iterator; + + /// Iterator which traverses only the nodes at a given depth from the root. + class fixed_depth_iterator : public iterator_base { + public: + fixed_depth_iterator(); + fixed_depth_iterator(tree_node *); + fixed_depth_iterator(const iterator_base&); + fixed_depth_iterator(const sibling_iterator&); + fixed_depth_iterator(const fixed_depth_iterator&); + + void swap(fixed_depth_iterator&, fixed_depth_iterator&); + fixed_depth_iterator& operator=(fixed_depth_iterator); + + bool operator==(const fixed_depth_iterator&) const; + bool operator!=(const fixed_depth_iterator&) const; + fixed_depth_iterator& operator++(); + fixed_depth_iterator& operator--(); + fixed_depth_iterator operator++(int); + fixed_depth_iterator operator--(int); + fixed_depth_iterator& operator+=(unsigned int); + fixed_depth_iterator& operator-=(unsigned int); + + tree_node *top_node; + }; + + /// Iterator which traverses only the nodes which are siblings of each other. + class sibling_iterator : public iterator_base { + public: + sibling_iterator(); + sibling_iterator(tree_node *); + sibling_iterator(const sibling_iterator&); + sibling_iterator(const iterator_base&); + + void swap(sibling_iterator&, sibling_iterator&); + sibling_iterator& operator=(sibling_iterator); + + bool operator==(const sibling_iterator&) const; + bool operator!=(const sibling_iterator&) const; + sibling_iterator& operator++(); + sibling_iterator& operator--(); + sibling_iterator operator++(int); + sibling_iterator operator--(int); + sibling_iterator& operator+=(unsigned int); + sibling_iterator& operator-=(unsigned int); + + tree_node *range_first() const; + tree_node *range_last() const; + tree_node *parent_; + private: + void set_parent_(); + }; + + /// Iterator which traverses only the leaves. + class leaf_iterator : public iterator_base { + public: + leaf_iterator(); + leaf_iterator(tree_node *, tree_node *top=0); + leaf_iterator(const sibling_iterator&); + leaf_iterator(const iterator_base&); + + bool operator==(const leaf_iterator&) const; + bool operator!=(const leaf_iterator&) const; + leaf_iterator& operator++(); + leaf_iterator& operator--(); + leaf_iterator operator++(int); + leaf_iterator operator--(int); + leaf_iterator& operator+=(unsigned int); + leaf_iterator& operator-=(unsigned int); + private: + tree_node *top_node; + }; + + /// Return iterator to the beginning of the tree. + inline pre_order_iterator begin() const; + /// Return iterator to the end of the tree. + inline pre_order_iterator end() const; + /// Return post-order iterator to the beginning of the tree. + post_order_iterator begin_post() const; + /// Return post-order end iterator of the tree. + post_order_iterator end_post() const; + /// Return fixed-depth iterator to the first node at a given depth from the given iterator. + /// If 'walk_back=true', a depth=0 iterator will be taken from the beginning of the sibling + /// range, not the current node. + fixed_depth_iterator begin_fixed(const iterator_base&, unsigned int, bool walk_back=true) const; + /// Return fixed-depth end iterator. + fixed_depth_iterator end_fixed(const iterator_base&, unsigned int) const; + /// Return breadth-first iterator to the first node at a given depth. + breadth_first_queued_iterator begin_breadth_first() const; + /// Return breadth-first end iterator. + breadth_first_queued_iterator end_breadth_first() const; + /// Return sibling iterator to the first child of given node. + static sibling_iterator begin(const iterator_base&); + /// Return sibling end iterator for children of given node. + static sibling_iterator end(const iterator_base&); + /// Return leaf iterator to the first leaf of the tree. + leaf_iterator begin_leaf() const; + /// Return leaf end iterator for entire tree. + leaf_iterator end_leaf() const; + /// Return leaf iterator to the first leaf of the subtree at the given node. + leaf_iterator begin_leaf(const iterator_base& top) const; + /// Return leaf end iterator for the subtree at the given node. + leaf_iterator end_leaf(const iterator_base& top) const; + + typedef std::vector path_t; + /// Return a path (to be taken from the 'top' node) corresponding to a node in the tree. + /// The first integer in path_t is the number of steps you need to go 'right' in the sibling + /// chain (so 0 if we go straight to the children). + path_t path_from_iterator(const iterator_base& iter, const iterator_base& top) const; + /// Return an iterator given a path from the 'top' node. + iterator iterator_from_path(const path_t&, const iterator_base& top) const; + + /// Return iterator to the parent of a node. Throws a `navigation_error` if the node + /// does not have a parent. + template static iter parent(iter); + /// Return iterator to the previous sibling of a node. + template static iter previous_sibling(iter); + /// Return iterator to the next sibling of a node. + template static iter next_sibling(iter); + /// Return iterator to the next node at a given depth. + template iter next_at_same_depth(iter) const; + + /// Erase all nodes of the tree. + void clear(); + /// Erase element at position pointed to by iterator, return incremented iterator. + template iter erase(iter); + /// Erase all children of the node pointed to by iterator. + void erase_children(const iterator_base&); + /// Erase all siblings to the right of the iterator. + void erase_right_siblings(const iterator_base&); + /// Erase all siblings to the left of the iterator. + void erase_left_siblings(const iterator_base&); + + /// Insert empty node as last/first child of node pointed to by position. + template iter append_child(iter position); + template iter prepend_child(iter position); + /// Insert node as last/first child of node pointed to by position. + template iter append_child(iter position, const T& x); + template iter append_child(iter position, T&& x); + template iter prepend_child(iter position, const T& x); + template iter prepend_child(iter position, T&& x); + /// Append the node (plus its children) at other_position as last/first child of position. + template iter append_child(iter position, iter other_position); + template iter prepend_child(iter position, iter other_position); + /// Append the nodes in the from-to range (plus their children) as last/first children of position. + template iter append_children(iter position, sibling_iterator from, sibling_iterator to); + template iter prepend_children(iter position, sibling_iterator from, sibling_iterator to); + + /// Short-hand to insert topmost node in otherwise empty tree. + pre_order_iterator set_head(const T& x); + pre_order_iterator set_head(T&& x); + /// Insert node as previous sibling of node pointed to by position. + template iter insert(iter position, const T& x); + template iter insert(iter position, T&& x); + /// Specialisation of previous member. + sibling_iterator insert(sibling_iterator position, const T& x); + sibling_iterator insert(sibling_iterator position, T&& x); + /// Insert node (with children) pointed to by subtree as previous sibling of node pointed to by position. + /// Does not change the subtree itself (use move_in or move_in_below for that). + template iter insert_subtree(iter position, const iterator_base& subtree); + /// Insert node as next sibling of node pointed to by position. + template iter insert_after(iter position, const T& x); + template iter insert_after(iter position, T&& x); + /// Insert node (with children) pointed to by subtree as next sibling of node pointed to by position. + template iter insert_subtree_after(iter position, const iterator_base& subtree); + + /// Replace node at 'position' with other node (keeping same children); 'position' becomes invalid. + template iter replace(iter position, const T& x); + /// Replace node at 'position' with subtree starting at 'from' (do not erase subtree at 'from'); see above. + template iter replace(iter position, const iterator_base& from); + /// Replace string of siblings (plus their children) with copy of a new string (with children); see above + sibling_iterator replace(sibling_iterator orig_begin, sibling_iterator orig_end, + sibling_iterator new_begin, sibling_iterator new_end); + + /// Move all children of node at 'position' to be siblings, returns position. + template iter flatten(iter position); + /// Move nodes in range to be children of 'position'. + template iter reparent(iter position, sibling_iterator begin, sibling_iterator end); + /// Move all child nodes of 'from' to be children of 'position'. + template iter reparent(iter position, iter from); + + /// Replace node with a new node, making the old node (plus subtree) a child of the new node. + template iter wrap(iter position, const T& x); + /// Replace the range of sibling nodes (plus subtrees), making these children of the new node. + template iter wrap(iter from, iter to, const T& x); + + /// Move 'source' node (plus its children) to become the next sibling of 'target'. + template iter move_after(iter target, iter source); + /// Move 'source' node (plus its children) to become the previous sibling of 'target'. + template iter move_before(iter target, iter source); + sibling_iterator move_before(sibling_iterator target, sibling_iterator source); + /// Move 'source' node (plus its children) to become the node at 'target' (erasing the node at 'target'). + template iter move_ontop(iter target, iter source); + + /// Extract the subtree starting at the indicated node, removing it from the original tree. + tree move_out(iterator); + /// Inverse of take_out: inserts the given tree as previous sibling of indicated node by a + /// move operation, that is, the given tree becomes empty. Returns iterator to the top node. + template iter move_in(iter, tree&); + /// As above, but now make the tree the last child of the indicated node. + template iter move_in_below(iter, tree&); + /// As above, but now make the tree the nth child of the indicated node (if possible). + template iter move_in_as_nth_child(iter, size_t, tree&); + + /// Merge with other tree, creating new branches and leaves only if they are not already present. + void merge(sibling_iterator, sibling_iterator, sibling_iterator, sibling_iterator, + bool duplicate_leaves=false); + /// As above, but using two trees with a single top node at the 'to' and 'from' positions. + void merge(iterator to, iterator from, bool duplicate_leaves); + /// Sort (std::sort only moves values of nodes, this one moves children as well). + void sort(sibling_iterator from, sibling_iterator to, bool deep=false); + template + void sort(sibling_iterator from, sibling_iterator to, StrictWeakOrdering comp, bool deep=false); + /// Compare two ranges of nodes (compares nodes as well as tree structure). + template + bool equal(const iter& one, const iter& two, const iter& three) const; + template + bool equal(const iter& one, const iter& two, const iter& three, BinaryPredicate) const; + template + bool equal_subtree(const iter& one, const iter& two) const; + template + bool equal_subtree(const iter& one, const iter& two, BinaryPredicate) const; + /// Extract a new tree formed by the range of siblings plus all their children. + tree subtree(sibling_iterator from, sibling_iterator to) const; + void subtree(tree&, sibling_iterator from, sibling_iterator to) const; + /// Exchange the node (plus subtree) with its sibling node (do nothing if no sibling present). + void swap(sibling_iterator it); + /// Exchange two nodes (plus subtrees). The iterators will remain valid and keep + /// pointing to the same nodes, which now sit at different locations in the tree. + void swap(iterator, iterator); + + /// Count the total number of nodes. + size_t size() const; + /// Count the total number of nodes below the indicated node (plus one). + size_t size(const iterator_base&) const; + /// Check if tree is empty. + bool empty() const; + /// Compute the depth to the root or to a fixed other iterator. + static int depth(const iterator_base&); + static int depth(const iterator_base&, const iterator_base&); + /// Compute the depth to the root, counting all levels for which predicate returns true. + template + static int depth(const iterator_base&, Predicate p); + /// Compute the depth distance between two nodes, counting all levels for which predicate returns true. + template + static int distance(const iterator_base& top, const iterator_base& bottom, Predicate p); + /// Determine the maximal depth of the tree. An empty tree has max_depth=-1. + int max_depth() const; + /// Determine the maximal depth of the tree with top node at the given position. + int max_depth(const iterator_base&) const; + /// Count the number of children of node at position. + static unsigned int number_of_children(const iterator_base&); + /// Count the number of siblings (left and right) of node at iterator. Total nodes at this level is +1. + unsigned int number_of_siblings(const iterator_base&) const; + /// Determine whether node at position is in the subtrees with indicated top node. + bool is_in_subtree(const iterator_base& position, const iterator_base& top) const; + /// Determine whether node at position is in the subtrees with root in the range. + bool is_in_subtree(const iterator_base& position, const iterator_base& begin, + const iterator_base& end) const; + /// Determine whether the iterator is an 'end' iterator and thus not actually pointing to a node. + bool is_valid(const iterator_base&) const; + /// Determine whether the iterator is one of the 'head' nodes at the top level, i.e. has no parent. + static bool is_head(const iterator_base&); + /// Find the lowest common ancestor of two nodes, that is, the deepest node such that + /// both nodes are descendants of it. + iterator lowest_common_ancestor(const iterator_base&, const iterator_base &) const; + + /// Determine the index of a node in the range of siblings to which it belongs. + unsigned int index(sibling_iterator it) const; + /// Inverse of 'index': return the n-th child of the node at position. + static sibling_iterator child(const iterator_base& position, unsigned int); + /// Return iterator to the sibling indicated by index + sibling_iterator sibling(const iterator_base& position, unsigned int) const; + + /// For debugging only: verify internal consistency by inspecting all pointers in the tree + /// (which will also trigger a valgrind error in case something got corrupted). + void debug_verify_consistency() const; + + /// Comparator class for iterators (compares pointer values; why doesn't this work automatically?) + class iterator_base_less { + public: + bool operator()(const typename tree::iterator_base& one, + const typename tree::iterator_base& two) const + { + return one.node < two.node; + } + }; + tree_node *head, *feet; // head/feet are always dummy; if an iterator points to them it is invalid + private: + tree_node_allocator alloc_; + void head_initialise_(); + void copy_(const tree& other); + + /// Comparator class for two nodes of a tree (used for sorting and searching). + template + class compare_nodes { + public: + compare_nodes(StrictWeakOrdering comp) : comp_(comp) {} + + bool operator()(const tree_node *a, const tree_node *b) const + { + return comp_(a->data, b->data); + } + private: + StrictWeakOrdering comp_; + }; + }; + +//template +//class iterator_base_less { +// public: +// bool operator()(const typename tree::iterator_base& one, +// const typename tree::iterator_base& two) const +// { +// txtout << "operatorclass<" << one.node < two.node << std::endl; +// return one.node < two.node; +// } +//}; + +// template +// bool operator<(const typename tree::iterator& one, +// const typename tree::iterator& two) +// { +// txtout << "operator< " << one.node < two.node << std::endl; +// if(one.node < two.node) return true; +// return false; +// } +// +// template +// bool operator==(const typename tree::iterator& one, +// const typename tree::iterator& two) +// { +// txtout << "operator== " << one.node == two.node << std::endl; +// if(one.node == two.node) return true; +// return false; +// } +// +// template +// bool operator>(const typename tree::iterator_base& one, +// const typename tree::iterator_base& two) +// { +// txtout << "operator> " << one.node < two.node << std::endl; +// if(one.node > two.node) return true; +// return false; +// } + + + +// Tree + +template +tree::tree() + { + head_initialise_(); + } + +template +tree::tree(const T& x) + { + head_initialise_(); + set_head(x); + } + +template +tree::tree(tree&& x) + { + head_initialise_(); + if(x.head->next_sibling!=x.feet) { // move tree if non-empty only + head->next_sibling=x.head->next_sibling; + feet->prev_sibling=x.feet->prev_sibling; + x.head->next_sibling->prev_sibling=head; + x.feet->prev_sibling->next_sibling=feet; + x.head->next_sibling=x.feet; + x.feet->prev_sibling=x.head; + } + } + +template +tree::tree(const iterator_base& other) + { + head_initialise_(); + set_head((*other)); + replace(begin(), other); + } + +template +tree::~tree() + { + clear(); + std::allocator_traits::destroy(alloc_, head); + std::allocator_traits::destroy(alloc_, feet); + std::allocator_traits::deallocate(alloc_, head, 1); + std::allocator_traits::deallocate(alloc_, feet, 1); + } + +template +void tree::head_initialise_() + { + head = std::allocator_traits::allocate(alloc_, 1, 0); + feet = std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, head, tree_node_()); + std::allocator_traits::construct(alloc_, feet, tree_node_()); + + head->parent=0; + head->first_child=0; + head->last_child=0; + head->prev_sibling=0; //head; + head->next_sibling=feet; //head; + + feet->parent=0; + feet->first_child=0; + feet->last_child=0; + feet->prev_sibling=head; + feet->next_sibling=0; + } + +template +tree& tree::operator=(const tree& other) + { + if(this != &other) + copy_(other); + return *this; + } + +template +tree& tree::operator=(tree&& x) + { + if(this != &x) { + clear(); // clear any existing data. + + head->next_sibling=x.head->next_sibling; + feet->prev_sibling=x.feet->prev_sibling; + x.head->next_sibling->prev_sibling=head; + x.feet->prev_sibling->next_sibling=feet; + x.head->next_sibling=x.feet; + x.feet->prev_sibling=x.head; + } + return *this; + } + +template +tree::tree(const tree& other) + { + head_initialise_(); + copy_(other); + } + +template +void tree::copy_(const tree& other) + { + clear(); + pre_order_iterator it=other.begin(), to=begin(); + while(it!=other.end()) { + to=insert(to, (*it)); + it.skip_children(); + ++it; + } + to=begin(); + it=other.begin(); + while(it!=other.end()) { + to=replace(to, it); + to.skip_children(); + it.skip_children(); + ++to; + ++it; + } + } + +template +void tree::clear() + { + if(head) + while(head->next_sibling!=feet) + erase(pre_order_iterator(head->next_sibling)); + } + +template +void tree::erase_children(const iterator_base& it) + { +// std::cout << "erase_children " << it.node << std::endl; + if(it.node==0) return; + + tree_node *cur=it.node->first_child; + tree_node *prev=0; + + while(cur!=0) { + prev=cur; + cur=cur->next_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->first_child=0; + it.node->last_child=0; +// std::cout << "exit" << std::endl; + } + +template +void tree::erase_right_siblings(const iterator_base& it) + { + if(it.node==0) return; + + tree_node *cur=it.node->next_sibling; + tree_node *prev=0; + + while(cur!=0) { + prev=cur; + cur=cur->next_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->next_sibling=0; + if(it.node->parent!=0) + it.node->parent->last_child=it.node; + } + +template +void tree::erase_left_siblings(const iterator_base& it) + { + if(it.node==0) return; + + tree_node *cur=it.node->prev_sibling; + tree_node *prev=0; + + while(cur!=0) { + prev=cur; + cur=cur->prev_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->prev_sibling=0; + if(it.node->parent!=0) + it.node->parent->first_child=it.node; + } + +template +template +iter tree::erase(iter it) + { + tree_node *cur=it.node; + assert(cur!=head); + iter ret=it; + ret.skip_children(); + ++ret; + erase_children(it); + if(cur->prev_sibling==0) { + cur->parent->first_child=cur->next_sibling; + } + else { + cur->prev_sibling->next_sibling=cur->next_sibling; + } + if(cur->next_sibling==0) { + cur->parent->last_child=cur->prev_sibling; + } + else { + cur->next_sibling->prev_sibling=cur->prev_sibling; + } + + std::allocator_traits::destroy(alloc_, cur); + std::allocator_traits::deallocate(alloc_, cur, 1); + return ret; + } + +template +typename tree::pre_order_iterator tree::begin() const + { + return pre_order_iterator(head->next_sibling); + } + +template +typename tree::pre_order_iterator tree::end() const + { + return pre_order_iterator(feet); + } + +template +typename tree::breadth_first_queued_iterator tree::begin_breadth_first() const + { + return breadth_first_queued_iterator(head->next_sibling); + } + +template +typename tree::breadth_first_queued_iterator tree::end_breadth_first() const + { + return breadth_first_queued_iterator(); + } + +template +typename tree::post_order_iterator tree::begin_post() const + { + tree_node *tmp=head->next_sibling; + if(tmp!=feet) { + while(tmp->first_child) + tmp=tmp->first_child; + } + return post_order_iterator(tmp); + } + +template +typename tree::post_order_iterator tree::end_post() const + { + return post_order_iterator(feet); + } + +template +typename tree::fixed_depth_iterator tree::begin_fixed(const iterator_base& pos, unsigned int dp, bool walk_back) const + { + typename tree::fixed_depth_iterator ret; + ret.top_node=pos.node; + + tree_node *tmp=pos.node; + unsigned int curdepth=0; + while(curdepthfirst_child==0) { + if(tmp->next_sibling==0) { + // try to walk up and then right again + do { + if(tmp==ret.top_node) + throw std::range_error("tree: begin_fixed out of range"); + tmp=tmp->parent; + if(tmp==0) + throw std::range_error("tree: begin_fixed out of range"); + --curdepth; + } while(tmp->next_sibling==0); + } + tmp=tmp->next_sibling; + } + tmp=tmp->first_child; + ++curdepth; + } + + // Now walk back to the first sibling in this range. + if(walk_back) + while(tmp->prev_sibling!=0) + tmp=tmp->prev_sibling; + + ret.node=tmp; + return ret; + } + +template +typename tree::fixed_depth_iterator tree::end_fixed(const iterator_base& pos, unsigned int dp) const + { + assert(1==0); // FIXME: not correct yet: use is_valid() as a temporary workaround + tree_node *tmp=pos.node; + unsigned int curdepth=1; + while(curdepthfirst_child==0) { + tmp=tmp->next_sibling; + if(tmp==0) + throw std::range_error("tree: end_fixed out of range"); + } + tmp=tmp->first_child; + ++curdepth; + } + return tmp; + } + +template +typename tree::sibling_iterator tree::begin(const iterator_base& pos) + { + assert(pos.node!=0); + if(pos.node->first_child==0) { + return end(pos); + } + return pos.node->first_child; + } + +template +typename tree::sibling_iterator tree::end(const iterator_base& pos) + { + sibling_iterator ret(0); + ret.parent_=pos.node; + return ret; + } + +template +typename tree::leaf_iterator tree::begin_leaf() const + { + tree_node *tmp=head->next_sibling; + if(tmp!=feet) { + while(tmp->first_child) + tmp=tmp->first_child; + } + return leaf_iterator(tmp); + } + +template +typename tree::leaf_iterator tree::end_leaf() const + { + return leaf_iterator(feet); + } + +template +typename tree::path_t tree::path_from_iterator(const iterator_base& iter, const iterator_base& top) const + { + path_t path; + tree_node *walk=iter.node; + + do { + if(path.size()>0) + walk=walk->parent; + int num=0; + while(walk!=top.node && walk->prev_sibling!=0 && walk->prev_sibling!=head) { + ++num; + walk=walk->prev_sibling; + } + path.push_back(num); + } + while(walk->parent!=0 && walk!=top.node); + + std::reverse(path.begin(), path.end()); + return path; + } + +template +typename tree::iterator tree::iterator_from_path(const path_t& path, const iterator_base& top) const + { + iterator it=top; + tree_node *walk=it.node; + + for(size_t step=0; step0) + walk=walk->first_child; + if(walk==0) + throw std::range_error("tree::iterator_from_path: no more nodes at step "+std::to_string(step)); + + for(int i=0; inext_sibling; + if(walk==0) + throw std::range_error("tree::iterator_from_path: out of siblings at step "+std::to_string(step)); + } + } + it.node=walk; + return it; + } + +template +typename tree::leaf_iterator tree::begin_leaf(const iterator_base& top) const + { + tree_node *tmp=top.node; + while(tmp->first_child) + tmp=tmp->first_child; + return leaf_iterator(tmp, top.node); + } + +template +typename tree::leaf_iterator tree::end_leaf(const iterator_base& top) const + { + return leaf_iterator(top.node, top.node); + } + +template +template +iter tree::parent(iter position) + { + if(position.node==0) + throw navigation_error("tree: attempt to navigate from null iterator."); + + if(position.node->parent==0) + throw navigation_error("tree: attempt to navigate up past head node."); + + return iter(position.node->parent); + } + +template +template +iter tree::previous_sibling(iter position) + { + assert(position.node!=0); + iter ret(position); + ret.node=position.node->prev_sibling; + return ret; + } + +template +template +iter tree::next_sibling(iter position) + { + assert(position.node!=0); + iter ret(position); + ret.node=position.node->next_sibling; + return ret; + } + +template +template +iter tree::next_at_same_depth(iter position) const + { + // We make use of a temporary fixed_depth iterator to implement this. + + typename tree::fixed_depth_iterator tmp(position.node); + + ++tmp; + return iter(tmp); + + // assert(position.node!=0); + // iter ret(position); + // + // if(position.node->next_sibling) { + // ret.node=position.node->next_sibling; + // } + // else { + // int relative_depth=0; + // upper: + // do { + // ret.node=ret.node->parent; + // if(ret.node==0) return ret; + // --relative_depth; + // } while(ret.node->next_sibling==0); + // lower: + // ret.node=ret.node->next_sibling; + // while(ret.node->first_child==0) { + // if(ret.node->next_sibling==0) + // goto upper; + // ret.node=ret.node->next_sibling; + // if(ret.node==0) return ret; + // } + // while(relative_depth<0 && ret.node->first_child!=0) { + // ret.node=ret.node->first_child; + // ++relative_depth; + // } + // if(relative_depth<0) { + // if(ret.node->next_sibling==0) goto upper; + // else goto lower; + // } + // } + // return ret; + } + +template +template +iter tree::append_child(iter position) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, tree_node_()); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->last_child!=0) { + position.node->last_child->next_sibling=tmp; + } + else { + position.node->first_child=tmp; + } + tmp->prev_sibling=position.node->last_child; + position.node->last_child=tmp; + tmp->next_sibling=0; + return tmp; + } + +template +template +iter tree::prepend_child(iter position) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, tree_node_()); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->first_child!=0) { + position.node->first_child->prev_sibling=tmp; + } + else { + position.node->last_child=tmp; + } + tmp->next_sibling=position.node->first_child; + position.node->prev_child=tmp; + tmp->prev_sibling=0; + return tmp; + } + +template +template +iter tree::append_child(iter position, const T& x) + { + // If your program fails here you probably used 'append_child' to add the top + // node to an empty tree. From version 1.45 the top element should be added + // using 'insert'. See the documentation for further information, and sorry about + // the API change. + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->last_child!=0) { + position.node->last_child->next_sibling=tmp; + } + else { + position.node->first_child=tmp; + } + tmp->prev_sibling=position.node->last_child; + position.node->last_child=tmp; + tmp->next_sibling=0; + return tmp; + } + +template +template +iter tree::append_child(iter position, T&& x) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); // Here is where the move semantics kick in + std::swap(tmp->data, x); + + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->last_child!=0) { + position.node->last_child->next_sibling=tmp; + } + else { + position.node->first_child=tmp; + } + tmp->prev_sibling=position.node->last_child; + position.node->last_child=tmp; + tmp->next_sibling=0; + return tmp; + } + +template +template +iter tree::prepend_child(iter position, const T& x) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->first_child!=0) { + position.node->first_child->prev_sibling=tmp; + } + else { + position.node->last_child=tmp; + } + tmp->next_sibling=position.node->first_child; + position.node->first_child=tmp; + tmp->prev_sibling=0; + return tmp; + } + +template +template +iter tree::prepend_child(iter position, T&& x) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); + + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->first_child!=0) { + position.node->first_child->prev_sibling=tmp; + } + else { + position.node->last_child=tmp; + } + tmp->next_sibling=position.node->first_child; + position.node->first_child=tmp; + tmp->prev_sibling=0; + return tmp; + } + +template +template +iter tree::append_child(iter position, iter other) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + sibling_iterator aargh=append_child(position, value_type()); + return replace(aargh, other); + } + +template +template +iter tree::prepend_child(iter position, iter other) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + sibling_iterator aargh=prepend_child(position, value_type()); + return replace(aargh, other); + } + +template +template +iter tree::append_children(iter position, sibling_iterator from, sibling_iterator to) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + iter ret=from; + + while(from!=to) { + insert_subtree(position.end(), from); + ++from; + } + return ret; + } + +template +template +iter tree::prepend_children(iter position, sibling_iterator from, sibling_iterator to) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + if(from==to) return from; // should return end of tree? + + iter ret; + do { + --to; + ret=insert_subtree(position.begin(), to); + } + while(to!=from); + + return ret; + } + +template +typename tree::pre_order_iterator tree::set_head(const T& x) + { + assert(head->next_sibling==feet); + return insert(iterator(feet), x); + } + +template +typename tree::pre_order_iterator tree::set_head(T&& x) + { + assert(head->next_sibling==feet); + return insert(iterator(feet), x); + } + +template +template +iter tree::insert(iter position, const T& x) + { + if(position.node==0) { + position.node=feet; // Backward compatibility: when calling insert on a null node, + // insert before the feet. + } + assert(position.node!=head); // Cannot insert before head. + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->next_sibling=position.node; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + + if(tmp->prev_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +template +iter tree::insert(iter position, T&& x) + { + if(position.node==0) { + position.node=feet; // Backward compatibility: when calling insert on a null node, + // insert before the feet. + } + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // Move semantics + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->next_sibling=position.node; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + + if(tmp->prev_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +typename tree::sibling_iterator tree::insert(sibling_iterator position, const T& x) + { + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->next_sibling=position.node; + if(position.node==0) { // iterator points to end of a subtree + tmp->parent=position.parent_; + tmp->prev_sibling=position.range_last(); + tmp->parent->last_child=tmp; + } + else { + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + } + + if(tmp->prev_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +typename tree::sibling_iterator tree::insert(sibling_iterator position, T&& x) + { + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // Move semantics + + tmp->first_child=0; + tmp->last_child=0; + + tmp->next_sibling=position.node; + if(position.node==0) { // iterator points to end of a subtree + tmp->parent=position.parent_; + tmp->prev_sibling=position.range_last(); + tmp->parent->last_child=tmp; + } + else { + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + } + + if(tmp->prev_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +template +iter tree::insert_after(iter position, const T& x) + { + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node; + tmp->next_sibling=position.node->next_sibling; + position.node->next_sibling=tmp; + + if(tmp->next_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->last_child=tmp; + } + else { + tmp->next_sibling->prev_sibling=tmp; + } + return tmp; + } + +template +template +iter tree::insert_after(iter position, T&& x) + { + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // move semantics + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node; + tmp->next_sibling=position.node->next_sibling; + position.node->next_sibling=tmp; + + if(tmp->next_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->last_child=tmp; + } + else { + tmp->next_sibling->prev_sibling=tmp; + } + return tmp; + } + +template +template +iter tree::insert_subtree(iter position, const iterator_base& subtree) + { + // insert dummy + iter it=insert(position, value_type()); + // replace dummy with subtree + return replace(it, subtree); + } + +template +template +iter tree::insert_subtree_after(iter position, const iterator_base& subtree) + { + // insert dummy + iter it=insert_after(position, value_type()); + // replace dummy with subtree + return replace(it, subtree); + } + +// template +// template +// iter tree::insert_subtree(sibling_iterator position, iter subtree) +// { +// // insert dummy +// iter it(insert(position, value_type())); +// // replace dummy with subtree +// return replace(it, subtree); +// } + +template +template +iter tree::replace(iter position, const T& x) + { +// kp::destructor(&position.node->data); +// kp::constructor(&position.node->data, x); + position.node->data=x; +// alloc_.destroy(position.node); +// alloc_.construct(position.node, x); + return position; + } + +template +template +iter tree::replace(iter position, const iterator_base& from) + { + assert(position.node!=head); + tree_node *current_from=from.node; + tree_node *start_from=from.node; + tree_node *current_to =position.node; + + // replace the node at position with head of the replacement tree at from +// std::cout << "warning!" << position.node << std::endl; + erase_children(position); +// std::cout << "no warning!" << std::endl; + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, (*from)); + tmp->first_child=0; + tmp->last_child=0; + if(current_to->prev_sibling==0) { + if(current_to->parent!=0) + current_to->parent->first_child=tmp; + } + else { + current_to->prev_sibling->next_sibling=tmp; + } + tmp->prev_sibling=current_to->prev_sibling; + if(current_to->next_sibling==0) { + if(current_to->parent!=0) + current_to->parent->last_child=tmp; + } + else { + current_to->next_sibling->prev_sibling=tmp; + } + tmp->next_sibling=current_to->next_sibling; + tmp->parent=current_to->parent; +// kp::destructor(¤t_to->data); + std::allocator_traits::destroy(alloc_, current_to); + std::allocator_traits::deallocate(alloc_, current_to, 1); + current_to=tmp; + + // only at this stage can we fix 'last' + tree_node *last=from.node->next_sibling; + + pre_order_iterator toit=tmp; + // copy all children + do { + assert(current_from!=0); + if(current_from->first_child != 0) { + current_from=current_from->first_child; + toit=append_child(toit, current_from->data); + } + else { + while(current_from->next_sibling==0 && current_from!=start_from) { + current_from=current_from->parent; + toit=parent(toit); + assert(current_from!=0); + } + current_from=current_from->next_sibling; + if(current_from!=last) { + toit=append_child(parent(toit), current_from->data); + } + } + } + while(current_from!=last); + + return current_to; + } + +template +typename tree::sibling_iterator tree::replace( + sibling_iterator orig_begin, + sibling_iterator orig_end, + sibling_iterator new_begin, + sibling_iterator new_end) + { + tree_node *orig_first=orig_begin.node; + tree_node *new_first=new_begin.node; + tree_node *orig_last=orig_first; + while((++orig_begin)!=orig_end) + orig_last=orig_last->next_sibling; + tree_node *new_last=new_first; + while((++new_begin)!=new_end) + new_last=new_last->next_sibling; + + // insert all siblings in new_first..new_last before orig_first + bool first=true; + pre_order_iterator ret; + while(1==1) { + pre_order_iterator tt=insert_subtree(pre_order_iterator(orig_first), pre_order_iterator(new_first)); + if(first) { + ret=tt; + first=false; + } + if(new_first==new_last) + break; + new_first=new_first->next_sibling; + } + + // erase old range of siblings + bool last=false; + tree_node *next=orig_first; + while(1==1) { + if(next==orig_last) + last=true; + next=next->next_sibling; + erase((pre_order_iterator)orig_first); + if(last) + break; + orig_first=next; + } + return ret; + } + +template +template +iter tree::flatten(iter position) + { + if(position.node->first_child==0) + return position; + + tree_node *tmp=position.node->first_child; + while(tmp) { + tmp->parent=position.node->parent; + tmp=tmp->next_sibling; + } + if(position.node->next_sibling) { + position.node->last_child->next_sibling=position.node->next_sibling; + position.node->next_sibling->prev_sibling=position.node->last_child; + } + else { + position.node->parent->last_child=position.node->last_child; + } + position.node->next_sibling=position.node->first_child; + position.node->next_sibling->prev_sibling=position.node; + position.node->first_child=0; + position.node->last_child=0; + + return position; + } + + +template +template +iter tree::reparent(iter position, sibling_iterator begin, sibling_iterator end) + { + tree_node *first=begin.node; + tree_node *last=first; + + assert(first!=position.node); + + if(begin==end) return begin; + // determine last node + while((++begin)!=end) { + last=last->next_sibling; + } + // move subtree + if(first->prev_sibling==0) { + first->parent->first_child=last->next_sibling; + } + else { + first->prev_sibling->next_sibling=last->next_sibling; + } + if(last->next_sibling==0) { + last->parent->last_child=first->prev_sibling; + } + else { + last->next_sibling->prev_sibling=first->prev_sibling; + } + if(position.node->first_child==0) { + position.node->first_child=first; + position.node->last_child=last; + first->prev_sibling=0; + } + else { + position.node->last_child->next_sibling=first; + first->prev_sibling=position.node->last_child; + position.node->last_child=last; + } + last->next_sibling=0; + + tree_node *pos=first; + for(;;) { + pos->parent=position.node; + if(pos==last) break; + pos=pos->next_sibling; + } + + return first; + } + +template +template iter tree::reparent(iter position, iter from) + { + if(from.node->first_child==0) return position; + return reparent(position, from.node->first_child, end(from)); + } + +template +template iter tree::wrap(iter position, const T& x) + { + assert(position.node!=0); + sibling_iterator fr=position, to=position; + ++to; + iter ret = insert(position, x); + reparent(ret, fr, to); + return ret; + } + +template +template iter tree::wrap(iter from, iter to, const T& x) + { + assert(from.node!=0); + iter ret = insert(from, x); + reparent(ret, from, to); + return ret; + } + +template +template iter tree::move_after(iter target, iter source) + { + tree_node *dst=target.node; + tree_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + if(dst->next_sibling) + if(dst->next_sibling==src) // already in the right spot + return source; + + // take src out of the tree + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + // connect it to the new point + if(dst->next_sibling!=0) dst->next_sibling->prev_sibling=src; + else dst->parent->last_child=src; + src->next_sibling=dst->next_sibling; + dst->next_sibling=src; + src->prev_sibling=dst; + src->parent=dst->parent; + return src; + } + +template +template iter tree::move_before(iter target, iter source) + { + tree_node *dst=target.node; + tree_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + if(dst->prev_sibling) + if(dst->prev_sibling==src) // already in the right spot + return source; + + // take src out of the tree + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + // connect it to the new point + if(dst->prev_sibling!=0) dst->prev_sibling->next_sibling=src; + else dst->parent->first_child=src; + src->prev_sibling=dst->prev_sibling; + dst->prev_sibling=src; + src->next_sibling=dst; + src->parent=dst->parent; + return src; + } + +// specialisation for sibling_iterators +template +typename tree::sibling_iterator tree::move_before(sibling_iterator target, + sibling_iterator source) + { + tree_node *dst=target.node; + tree_node *src=source.node; + tree_node *dst_prev_sibling; + if(dst==0) { // must then be an end iterator + dst_prev_sibling=target.parent_->last_child; + assert(dst_prev_sibling); + } + else dst_prev_sibling=dst->prev_sibling; + assert(src); + + if(dst==src) return source; + if(dst_prev_sibling) + if(dst_prev_sibling==src) // already in the right spot + return source; + + // take src out of the tree + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + // connect it to the new point + if(dst_prev_sibling!=0) dst_prev_sibling->next_sibling=src; + else target.parent_->first_child=src; + src->prev_sibling=dst_prev_sibling; + if(dst) { + dst->prev_sibling=src; + src->parent=dst->parent; + } + else { + src->parent=dst_prev_sibling->parent; + } + src->next_sibling=dst; + return src; + } + +template +template iter tree::move_ontop(iter target, iter source) + { + tree_node *dst=target.node; + tree_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + +// if(dst==src->prev_sibling) { +// +// } + + // remember connection points + tree_node *b_prev_sibling=dst->prev_sibling; + tree_node *b_next_sibling=dst->next_sibling; + tree_node *b_parent=dst->parent; + + // remove target + erase(target); + + // take src out of the tree + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else { + assert(src->parent!=0); + src->parent->first_child=src->next_sibling; + } + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else { + assert(src->parent!=0); + src->parent->last_child=src->prev_sibling; + } + + // connect it to the new point + if(b_prev_sibling!=0) b_prev_sibling->next_sibling=src; + else { + assert(b_parent!=0); + b_parent->first_child=src; + } + if(b_next_sibling!=0) b_next_sibling->prev_sibling=src; + else { + assert(b_parent!=0); + b_parent->last_child=src; + } + src->prev_sibling=b_prev_sibling; + src->next_sibling=b_next_sibling; + src->parent=b_parent; + return src; + } + + +template +tree tree::move_out(iterator source) + { + tree ret; + + // Move source node into the 'ret' tree. + ret.head->next_sibling = source.node; + ret.feet->prev_sibling = source.node; + + // Close the links in the current tree. + if(source.node->prev_sibling!=0) + source.node->prev_sibling->next_sibling = source.node->next_sibling; + + if(source.node->next_sibling!=0) + source.node->next_sibling->prev_sibling = source.node->prev_sibling; + + // If the moved-out node was a first or last child of + // the parent, adjust those links. + if(source.node->parent->first_child==source.node) { + if(source.node->next_sibling!=0) + source.node->parent->first_child=source.node->next_sibling; + else + source.node->parent->first_child=0; + } + if(source.node->parent->last_child==source.node) { + if(source.node->prev_sibling!=0) + source.node->parent->last_child=source.node->prev_sibling; + else + source.node->parent->last_child=0; + } + source.node->parent=0; + + // Fix source prev/next links. + source.node->prev_sibling = ret.head; + source.node->next_sibling = ret.feet; + + return ret; // A good compiler will move this, not copy. + } + +template +template iter tree::move_in(iter loc, tree& other) + { + if(other.head->next_sibling==other.feet) return loc; // other tree is empty + + tree_node *other_first_head = other.head->next_sibling; + tree_node *other_last_head = other.feet->prev_sibling; + + sibling_iterator prev(loc); + --prev; + + prev.node->next_sibling = other_first_head; + loc.node->prev_sibling = other_last_head; + other_first_head->prev_sibling = prev.node; + other_last_head->next_sibling = loc.node; + + // Adjust parent pointers. + tree_node *walk=other_first_head; + while(true) { + walk->parent=loc.node->parent; + if(walk==other_last_head) + break; + walk=walk->next_sibling; + } + + // Close other tree. + other.head->next_sibling=other.feet; + other.feet->prev_sibling=other.head; + + return other_first_head; + } + +template +template iter tree::move_in_below(iter loc, tree& other) + { + if(other.head->next_sibling==other.feet) return loc; // other tree is empty + + auto n = other.number_of_children(loc); + return move_in_as_nth_child(loc, n, other); + } + +template +template iter tree::move_in_as_nth_child(iter loc, size_t n, tree& other) + { + if(other.head->next_sibling==other.feet) return loc; // other tree is empty + + tree_node *other_first_head = other.head->next_sibling; + tree_node *other_last_head = other.feet->prev_sibling; + + if(n==0) { + if(loc.node->first_child==0) { + loc.node->first_child=other_first_head; + loc.node->last_child=other_last_head; + other_last_head->next_sibling=0; + other_first_head->prev_sibling=0; + } + else { + loc.node->first_child->prev_sibling=other_last_head; + other_last_head->next_sibling=loc.node->first_child; + loc.node->first_child=other_first_head; + other_first_head->prev_sibling=0; + } + } + else { + --n; + tree_node *walk = loc.node->first_child; + while(true) { + if(walk==0) + throw std::range_error("tree: move_in_as_nth_child position out of range"); + if(n==0) + break; + --n; + walk = walk->next_sibling; + } + if(walk->next_sibling==0) + loc.node->last_child=other_last_head; + else + walk->next_sibling->prev_sibling=other_last_head; + other_last_head->next_sibling=walk->next_sibling; + walk->next_sibling=other_first_head; + other_first_head->prev_sibling=walk; + } + + // Adjust parent pointers. + tree_node *walk=other_first_head; + while(true) { + walk->parent=loc.node; + if(walk==other_last_head) + break; + walk=walk->next_sibling; + } + + // Close other tree. + other.head->next_sibling=other.feet; + other.feet->prev_sibling=other.head; + + return other_first_head; + } + + +template +void tree::merge(sibling_iterator to1, sibling_iterator to2, + sibling_iterator from1, sibling_iterator from2, + bool duplicate_leaves) + { + sibling_iterator fnd; + while(from1!=from2) { + if((fnd=std::find(to1, to2, (*from1))) != to2) { // element found + if(from1.begin()==from1.end()) { // full depth reached + if(duplicate_leaves) + append_child(parent(to1), (*from1)); + } + else { // descend further + merge(fnd.begin(), fnd.end(), from1.begin(), from1.end(), duplicate_leaves); + } + } + else { // element missing + insert_subtree(to2, from1); + } + ++from1; + } + } + +template +void tree::merge(iterator to, iterator from, bool duplicate_leaves) + { + sibling_iterator to1(to); + sibling_iterator to2=to1; + ++to2; + sibling_iterator from1(from); + sibling_iterator from2=from1; + ++from2; + + merge(to1, to2, from1, from2, duplicate_leaves); + } + + +template +void tree::sort(sibling_iterator from, sibling_iterator to, bool deep) + { + std::less comp; + sort(from, to, comp, deep); + } + +template +template +void tree::sort(sibling_iterator from, sibling_iterator to, + StrictWeakOrdering comp, bool deep) + { + if(from==to) return; + // make list of sorted nodes + // CHECK: if multiset stores equivalent nodes in the order in which they + // are inserted, then this routine should be called 'stable_sort'. + std::multiset > nodes(comp); + sibling_iterator it=from, it2=to; + while(it != to) { + nodes.insert(it.node); + ++it; + } + // reassemble + --it2; + + // prev and next are the nodes before and after the sorted range + tree_node *prev=from.node->prev_sibling; + tree_node *next=it2.node->next_sibling; + typename std::multiset >::iterator nit=nodes.begin(), eit=nodes.end(); + if(prev==0) { + if((*nit)->parent!=0) // to catch "sorting the head" situations, when there is no parent + (*nit)->parent->first_child=(*nit); + } + else prev->next_sibling=(*nit); + + --eit; + while(nit!=eit) { + (*nit)->prev_sibling=prev; + if(prev) + prev->next_sibling=(*nit); + prev=(*nit); + ++nit; + } + // prev now points to the last-but-one node in the sorted range + if(prev) + prev->next_sibling=(*eit); + + // eit points to the last node in the sorted range. + (*eit)->next_sibling=next; + (*eit)->prev_sibling=prev; // missed in the loop above + if(next==0) { + if((*eit)->parent!=0) // to catch "sorting the head" situations, when there is no parent + (*eit)->parent->last_child=(*eit); + } + else next->prev_sibling=(*eit); + + if(deep) { // sort the children of each node too + sibling_iterator bcs(*nodes.begin()); + sibling_iterator ecs(*eit); + ++ecs; + while(bcs!=ecs) { + sort(begin(bcs), end(bcs), comp, deep); + ++bcs; + } + } + } + +template +template +bool tree::equal(const iter& one_, const iter& two, const iter& three_) const + { + std::equal_to comp; + return equal(one_, two, three_, comp); + } + +template +template +bool tree::equal_subtree(const iter& one_, const iter& two_) const + { + std::equal_to comp; + return equal_subtree(one_, two_, comp); + } + +template +template +bool tree::equal(const iter& one_, const iter& two, const iter& three_, BinaryPredicate fun) const + { + pre_order_iterator one(one_), three(three_); + +// if(one==two && is_valid(three) && three.number_of_children()!=0) +// return false; + while(one!=two && is_valid(three)) { + if(!fun(*one,*three)) + return false; + if(one.number_of_children()!=three.number_of_children()) + return false; + ++one; + ++three; + } + return true; + } + +template +template +bool tree::equal_subtree(const iter& one_, const iter& two_, BinaryPredicate fun) const + { + pre_order_iterator one(one_), two(two_); + + if(!fun(*one,*two)) return false; + if(number_of_children(one)!=number_of_children(two)) return false; + return equal(begin(one),end(one),begin(two),fun); + } + +template +tree tree::subtree(sibling_iterator from, sibling_iterator to) const + { + assert(from!=to); // if from==to, the range is empty, hence no tree to return. + + tree tmp; + tmp.set_head(value_type()); + tmp.replace(tmp.begin(), tmp.end(), from, to); + return tmp; + } + +template +void tree::subtree(tree& tmp, sibling_iterator from, sibling_iterator to) const + { + assert(from!=to); // if from==to, the range is empty, hence no tree to return. + + tmp.set_head(value_type()); + tmp.replace(tmp.begin(), tmp.end(), from, to); + } + +template +size_t tree::size() const + { + size_t i=0; + pre_order_iterator it=begin(), eit=end(); + while(it!=eit) { + ++i; + ++it; + } + return i; + } + +template +size_t tree::size(const iterator_base& top) const + { + size_t i=0; + pre_order_iterator it=top, eit=top; + eit.skip_children(); + ++eit; + while(it!=eit) { + ++i; + ++it; + } + return i; + } + +template +bool tree::empty() const + { + pre_order_iterator it=begin(), eit=end(); + return (it==eit); + } + +template +int tree::depth(const iterator_base& it) + { + tree_node* pos=it.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0) { + pos=pos->parent; + ++ret; + } + return ret; + } + +template +int tree::depth(const iterator_base& it, const iterator_base& root) + { + tree_node* pos=it.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0 && pos!=root.node) { + pos=pos->parent; + ++ret; + } + return ret; + } + +template +template +int tree::depth(const iterator_base& it, Predicate p) + { + tree_node* pos=it.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0) { + pos=pos->parent; + if(p(pos)) + ++ret; + } + return ret; + } + +template +template +int tree::distance(const iterator_base& top, const iterator_base& bottom, Predicate p) + { + tree_node* pos=bottom.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0 && pos!=top.node) { + pos=pos->parent; + if(p(pos)) + ++ret; + } + return ret; + } + +template +int tree::max_depth() const + { + int maxd=-1; + for(tree_node *it = head->next_sibling; it!=feet; it=it->next_sibling) + maxd=std::max(maxd, max_depth(it)); + + return maxd; + } + + +template +int tree::max_depth(const iterator_base& pos) const + { + tree_node *tmp=pos.node; + + if(tmp==0 || tmp==head || tmp==feet) return -1; + + int curdepth=0, maxdepth=0; + while(true) { // try to walk the bottom of the tree + while(tmp->first_child==0) { + if(tmp==pos.node) return maxdepth; + if(tmp->next_sibling==0) { + // try to walk up and then right again + do { + tmp=tmp->parent; + if(tmp==0) return maxdepth; + --curdepth; + } + while(tmp->next_sibling==0); + } + if(tmp==pos.node) return maxdepth; + tmp=tmp->next_sibling; + } + tmp=tmp->first_child; + ++curdepth; + maxdepth=std::max(curdepth, maxdepth); + } + } + +template +unsigned int tree::number_of_children(const iterator_base& it) + { + tree_node *pos=it.node->first_child; + if(pos==0) return 0; + + unsigned int ret=1; +// while(pos!=it.node->last_child) { +// ++ret; +// pos=pos->next_sibling; +// } + while((pos=pos->next_sibling)) + ++ret; + return ret; + } + +template +unsigned int tree::number_of_siblings(const iterator_base& it) const + { + tree_node *pos=it.node; + unsigned int ret=0; + // count forward + while(pos->next_sibling && + pos->next_sibling!=head && + pos->next_sibling!=feet) { + ++ret; + pos=pos->next_sibling; + } + // count backward + pos=it.node; + while(pos->prev_sibling && + pos->prev_sibling!=head && + pos->prev_sibling!=feet) { + ++ret; + pos=pos->prev_sibling; + } + + return ret; + } + +template +void tree::swap(sibling_iterator it) + { + tree_node *nxt=it.node->next_sibling; + if(nxt) { + if(it.node->prev_sibling) + it.node->prev_sibling->next_sibling=nxt; + else + it.node->parent->first_child=nxt; + nxt->prev_sibling=it.node->prev_sibling; + tree_node *nxtnxt=nxt->next_sibling; + if(nxtnxt) + nxtnxt->prev_sibling=it.node; + else + it.node->parent->last_child=it.node; + nxt->next_sibling=it.node; + it.node->prev_sibling=nxt; + it.node->next_sibling=nxtnxt; + } + } + +template +void tree::swap(iterator one, iterator two) + { + // if one and two are adjacent siblings, use the sibling swap + if(one.node->next_sibling==two.node) swap(one); + else if(two.node->next_sibling==one.node) swap(two); + else { + tree_node *nxt1=one.node->next_sibling; + tree_node *nxt2=two.node->next_sibling; + tree_node *pre1=one.node->prev_sibling; + tree_node *pre2=two.node->prev_sibling; + tree_node *par1=one.node->parent; + tree_node *par2=two.node->parent; + + // reconnect + one.node->parent=par2; + one.node->next_sibling=nxt2; + if(nxt2) nxt2->prev_sibling=one.node; + else par2->last_child=one.node; + one.node->prev_sibling=pre2; + if(pre2) pre2->next_sibling=one.node; + else par2->first_child=one.node; + + two.node->parent=par1; + two.node->next_sibling=nxt1; + if(nxt1) nxt1->prev_sibling=two.node; + else par1->last_child=two.node; + two.node->prev_sibling=pre1; + if(pre1) pre1->next_sibling=two.node; + else par1->first_child=two.node; + } + } + +// template +// tree::iterator tree::find_subtree( +// sibling_iterator subfrom, sibling_iterator subto, iterator from, iterator to, +// BinaryPredicate fun) const +// { +// assert(1==0); // this routine is not finished yet. +// while(from!=to) { +// if(fun(*subfrom, *from)) { +// +// } +// } +// return to; +// } + +template +bool tree::is_in_subtree(const iterator_base& it, const iterator_base& top) const + { + sibling_iterator first=top; + sibling_iterator last=first; + ++last; + return is_in_subtree(it, first, last); + } + +template +bool tree::is_in_subtree(const iterator_base& it, const iterator_base& begin, + const iterator_base& end) const + { + // FIXME: this should be optimised. + pre_order_iterator tmp=begin; + while(tmp!=end) { + if(tmp==it) return true; + ++tmp; + } + return false; + } + +template +bool tree::is_valid(const iterator_base& it) const + { + if(it.node==0 || it.node==feet || it.node==head) return false; + else return true; + } + +template +bool tree::is_head(const iterator_base& it) + { + if(it.node->parent==0) return true; + return false; + } + +template +typename tree::iterator tree::lowest_common_ancestor( + const iterator_base& one, const iterator_base& two) const + { + std::set parents; + + // Walk up from 'one' storing all parents. + iterator walk=one; + do { + walk=parent(walk); + parents.insert(walk); + } + while( walk.node->parent ); + + // Walk up from 'two' until we encounter a node in parents. + walk=two; + do { + walk=parent(walk); + if(parents.find(walk) != parents.end()) break; + } + while( walk.node->parent ); + + return walk; + } + +template +unsigned int tree::index(sibling_iterator it) const + { + unsigned int ind=0; + if(it.node->parent==0) { + while(it.node->prev_sibling!=head) { + it.node=it.node->prev_sibling; + ++ind; + } + } + else { + while(it.node->prev_sibling!=0) { + it.node=it.node->prev_sibling; + ++ind; + } + } + return ind; + } + +template +typename tree::sibling_iterator tree::sibling(const iterator_base& it, unsigned int num) const + { + tree_node *tmp; + if(it.node->parent==0) { + tmp=head->next_sibling; + while(num) { + tmp = tmp->next_sibling; + --num; + } + } + else { + tmp=it.node->parent->first_child; + while(num) { + assert(tmp!=0); + tmp = tmp->next_sibling; + --num; + } + } + return tmp; + } + +template +void tree::debug_verify_consistency() const + { + iterator it=begin(); + while(it!=end()) { + // std::cerr << *it << " (" << it.node << ")" << std::endl; + if(it.node->parent!=0) { + if(it.node->prev_sibling==0) + assert(it.node->parent->first_child==it.node); + else + assert(it.node->prev_sibling->next_sibling==it.node); + if(it.node->next_sibling==0) + assert(it.node->parent->last_child==it.node); + else + assert(it.node->next_sibling->prev_sibling==it.node); + } + ++it; + } + } + +template +typename tree::sibling_iterator tree::child(const iterator_base& it, unsigned int num) + { + tree_node *tmp=it.node->first_child; + while(num--) { + assert(tmp!=0); + tmp=tmp->next_sibling; + } + return tmp; + } + + + + +// Iterator base + +template +tree::iterator_base::iterator_base() + : node(0), skip_current_children_(false) + { + } + +template +tree::iterator_base::iterator_base(tree_node *tn) + : node(tn), skip_current_children_(false) + { + } + +template +T& tree::iterator_base::operator*() const + { + return node->data; + } + +template +T* tree::iterator_base::operator->() const + { + return &(node->data); + } + +template +bool tree::post_order_iterator::operator!=(const post_order_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::post_order_iterator::operator==(const post_order_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool tree::pre_order_iterator::operator!=(const pre_order_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::pre_order_iterator::operator==(const pre_order_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool tree::sibling_iterator::operator!=(const sibling_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::sibling_iterator::operator==(const sibling_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool tree::leaf_iterator::operator!=(const leaf_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::leaf_iterator::operator==(const leaf_iterator& other) const + { + if(other.node==this->node && other.top_node==this->top_node) return true; + else return false; + } + +template +typename tree::sibling_iterator tree::iterator_base::begin() const + { + if(node->first_child==0) + return end(); + + sibling_iterator ret(node->first_child); + ret.parent_=this->node; + return ret; + } + +template +typename tree::sibling_iterator tree::iterator_base::end() const + { + sibling_iterator ret(0); + ret.parent_=node; + return ret; + } + +template +void tree::iterator_base::skip_children() + { + skip_current_children_=true; + } + +template +void tree::iterator_base::skip_children(bool skip) + { + skip_current_children_=skip; + } + +template +unsigned int tree::iterator_base::number_of_children() const + { + tree_node *pos=node->first_child; + if(pos==0) return 0; + + unsigned int ret=1; + while(pos!=node->last_child) { + ++ret; + pos=pos->next_sibling; + } + return ret; + } + + + +// Pre-order iterator + +template +tree::pre_order_iterator::pre_order_iterator() + : iterator_base(0) + { + } + +template +tree::pre_order_iterator::pre_order_iterator(tree_node *tn) + : iterator_base(tn) + { + } + +template +tree::pre_order_iterator::pre_order_iterator(const iterator_base &other) + : iterator_base(other.node) + { + } + +template +tree::pre_order_iterator::pre_order_iterator(const sibling_iterator& other) + : iterator_base(other.node) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + this->skip_children(); + ++(*this); + } + } + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::operator++() + { + assert(this->node!=0); + if(!this->skip_current_children_ && this->node->first_child != 0) { + this->node=this->node->first_child; + } + else { + this->skip_current_children_=false; + while(this->node->next_sibling==0) { + this->node=this->node->parent; + if(this->node==0) + return *this; + } + this->node=this->node->next_sibling; + } + return *this; + } + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::operator--() + { + assert(this->node!=0); + if(this->node->prev_sibling) { + this->node=this->node->prev_sibling; + while(this->node->last_child) + this->node=this->node->last_child; + } + else { + this->node=this->node->parent; + if(this->node==0) + return *this; + } + return *this; +} + +template +typename tree::pre_order_iterator tree::pre_order_iterator::operator++(int) + { + pre_order_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::next_skip_children() + { + (*this).skip_children(); + (*this)++; + return *this; + } + +template +typename tree::pre_order_iterator tree::pre_order_iterator::operator--(int) +{ + pre_order_iterator copy = *this; + --(*this); + return copy; +} + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + + + +// Post-order iterator + +template +tree::post_order_iterator::post_order_iterator() + : iterator_base(0) + { + } + +template +tree::post_order_iterator::post_order_iterator(tree_node *tn) + : iterator_base(tn) + { + } + +template +tree::post_order_iterator::post_order_iterator(const iterator_base &other) + : iterator_base(other.node) + { + } + +template +tree::post_order_iterator::post_order_iterator(const sibling_iterator& other) + : iterator_base(other.node) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + this->skip_children(); + ++(*this); + } + } + +template +typename tree::post_order_iterator& tree::post_order_iterator::operator++() + { + assert(this->node!=0); + if(this->node->next_sibling==0) { + this->node=this->node->parent; + this->skip_current_children_=false; + } + else { + this->node=this->node->next_sibling; + if(this->skip_current_children_) { + this->skip_current_children_=false; + } + else { + while(this->node->first_child) + this->node=this->node->first_child; + } + } + return *this; + } + +template +typename tree::post_order_iterator& tree::post_order_iterator::operator--() + { + assert(this->node!=0); + if(this->skip_current_children_ || this->node->last_child==0) { + this->skip_current_children_=false; + while(this->node->prev_sibling==0) + this->node=this->node->parent; + this->node=this->node->prev_sibling; + } + else { + this->node=this->node->last_child; + } + return *this; + } + +template +typename tree::post_order_iterator tree::post_order_iterator::operator++(int) + { + post_order_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::post_order_iterator tree::post_order_iterator::operator--(int) + { + post_order_iterator copy = *this; + --(*this); + return copy; + } + + +template +typename tree::post_order_iterator& tree::post_order_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename tree::post_order_iterator& tree::post_order_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +template +void tree::post_order_iterator::descend_all() + { + assert(this->node!=0); + while(this->node->first_child) + this->node=this->node->first_child; + } + + +// Breadth-first iterator + +template +tree::breadth_first_queued_iterator::breadth_first_queued_iterator() + : iterator_base() + { + } + +template +tree::breadth_first_queued_iterator::breadth_first_queued_iterator(tree_node *tn) + : iterator_base(tn) + { + traversal_queue.push(tn); + } + +template +tree::breadth_first_queued_iterator::breadth_first_queued_iterator(const iterator_base& other) + : iterator_base(other.node) + { + traversal_queue.push(other.node); + } + +template +bool tree::breadth_first_queued_iterator::operator!=(const breadth_first_queued_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::breadth_first_queued_iterator::operator==(const breadth_first_queued_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +typename tree::breadth_first_queued_iterator& tree::breadth_first_queued_iterator::operator++() + { + assert(this->node!=0); + + // Add child nodes and pop current node + sibling_iterator sib=this->begin(); + while(sib!=this->end()) { + traversal_queue.push(sib.node); + ++sib; + } + traversal_queue.pop(); + if(traversal_queue.size()>0) + this->node=traversal_queue.front(); + else + this->node=0; + return (*this); + } + +template +typename tree::breadth_first_queued_iterator tree::breadth_first_queued_iterator::operator++(int) + { + breadth_first_queued_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::breadth_first_queued_iterator& tree::breadth_first_queued_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + + + +// Fixed depth iterator + +template +tree::fixed_depth_iterator::fixed_depth_iterator() + : iterator_base() + { + } + +template +tree::fixed_depth_iterator::fixed_depth_iterator(tree_node *tn) + : iterator_base(tn), top_node(0) + { + } + +template +tree::fixed_depth_iterator::fixed_depth_iterator(const iterator_base& other) + : iterator_base(other.node), top_node(0) + { + } + +template +tree::fixed_depth_iterator::fixed_depth_iterator(const sibling_iterator& other) + : iterator_base(other.node), top_node(0) + { + } + +template +tree::fixed_depth_iterator::fixed_depth_iterator(const fixed_depth_iterator& other) + : iterator_base(other.node), top_node(other.top_node) + { + } + +template +void tree::fixed_depth_iterator::swap(fixed_depth_iterator& first, fixed_depth_iterator& second) + { + std::swap(first.node, second.node); + std::swap(first.skip_current_children_, second.skip_current_children_); + std::swap(first.top_node, second.top_node); + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator=(fixed_depth_iterator other) + { + swap(*this, other); + return *this; + } + +template +bool tree::fixed_depth_iterator::operator==(const fixed_depth_iterator& other) const + { + if(other.node==this->node && other.top_node==top_node) return true; + else return false; + } + +template +bool tree::fixed_depth_iterator::operator!=(const fixed_depth_iterator& other) const + { + if(other.node!=this->node || other.top_node!=top_node) return true; + else return false; + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator++() + { + assert(this->node!=0); + + if(this->node->next_sibling) { + this->node=this->node->next_sibling; + } + else { + int relative_depth=0; + upper: + do { + if(this->node==this->top_node) { + this->node=0; // FIXME: return a proper fixed_depth end iterator once implemented + return *this; + } + this->node=this->node->parent; + if(this->node==0) return *this; + --relative_depth; + } while(this->node->next_sibling==0); + lower: + this->node=this->node->next_sibling; + while(this->node->first_child==0) { + if(this->node->next_sibling==0) + goto upper; + this->node=this->node->next_sibling; + if(this->node==0) return *this; + } + while(relative_depth<0 && this->node->first_child!=0) { + this->node=this->node->first_child; + ++relative_depth; + } + if(relative_depth<0) { + if(this->node->next_sibling==0) goto upper; + else goto lower; + } + } + return *this; + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator--() + { + assert(this->node!=0); + + if(this->node->prev_sibling) { + this->node=this->node->prev_sibling; + } + else { + int relative_depth=0; + upper: + do { + if(this->node==this->top_node) { + this->node=0; + return *this; + } + this->node=this->node->parent; + if(this->node==0) return *this; + --relative_depth; + } while(this->node->prev_sibling==0); + lower: + this->node=this->node->prev_sibling; + while(this->node->last_child==0) { + if(this->node->prev_sibling==0) + goto upper; + this->node=this->node->prev_sibling; + if(this->node==0) return *this; + } + while(relative_depth<0 && this->node->last_child!=0) { + this->node=this->node->last_child; + ++relative_depth; + } + if(relative_depth<0) { + if(this->node->prev_sibling==0) goto upper; + else goto lower; + } + } + return *this; + +// +// +// assert(this->node!=0); +// if(this->node->prev_sibling!=0) { +// this->node=this->node->prev_sibling; +// assert(this->node!=0); +// if(this->node->parent==0 && this->node->prev_sibling==0) // head element +// this->node=0; +// } +// else { +// tree_node *par=this->node->parent; +// do { +// par=par->prev_sibling; +// if(par==0) { // FIXME: need to keep track of this! +// this->node=0; +// return *this; +// } +// } while(par->last_child==0); +// this->node=par->last_child; +// } +// return *this; + } + +template +typename tree::fixed_depth_iterator tree::fixed_depth_iterator::operator++(int) + { + fixed_depth_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::fixed_depth_iterator tree::fixed_depth_iterator::operator--(int) + { + fixed_depth_iterator copy = *this; + --(*this); + return copy; + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --(num); + } + return (*this); + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --(num); + } + return *this; + } + + +// Sibling iterator + +template +tree::sibling_iterator::sibling_iterator() + : iterator_base() + { + set_parent_(); + } + +template +tree::sibling_iterator::sibling_iterator(tree_node *tn) + : iterator_base(tn) + { + set_parent_(); + } + +template +tree::sibling_iterator::sibling_iterator(const iterator_base& other) + : iterator_base(other.node) + { + set_parent_(); + } + +template +tree::sibling_iterator::sibling_iterator(const sibling_iterator& other) + : iterator_base(other), parent_(other.parent_) + { + } + +template +void tree::sibling_iterator::swap(sibling_iterator& first, sibling_iterator& second) + { + std::swap(first.node, second.node); + std::swap(first.skip_current_children_, second.skip_current_children_); + std::swap(first.parent_, second.parent_); + } + + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator=(sibling_iterator other) + { + swap(*this, other); + return *this; + } + +template +void tree::sibling_iterator::set_parent_() + { + parent_=0; + if(this->node==0) return; + if(this->node->parent!=0) + parent_=this->node->parent; + } + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator++() + { + if(this->node) + this->node=this->node->next_sibling; + return *this; + } + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator--() + { + if(this->node) this->node=this->node->prev_sibling; + else { + assert(parent_); + this->node=parent_->last_child; + } + return *this; +} + +template +typename tree::sibling_iterator tree::sibling_iterator::operator++(int) + { + sibling_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::sibling_iterator tree::sibling_iterator::operator--(int) + { + sibling_iterator copy = *this; + --(*this); + return copy; + } + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +template +typename tree::tree_node *tree::sibling_iterator::range_first() const + { + tree_node *tmp=parent_->first_child; + return tmp; + } + +template +typename tree::tree_node *tree::sibling_iterator::range_last() const + { + return parent_->last_child; + } + +// Leaf iterator + +template +tree::leaf_iterator::leaf_iterator() + : iterator_base(0), top_node(0) + { + } + +template +tree::leaf_iterator::leaf_iterator(tree_node *tn, tree_node *top) + : iterator_base(tn), top_node(top) + { + } + +template +tree::leaf_iterator::leaf_iterator(const iterator_base &other) + : iterator_base(other.node), top_node(0) + { + } + +template +tree::leaf_iterator::leaf_iterator(const sibling_iterator& other) + : iterator_base(other.node), top_node(0) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + ++(*this); + } + } + +template +typename tree::leaf_iterator& tree::leaf_iterator::operator++() + { + assert(this->node!=0); + if(this->node->first_child!=0) { // current node is no longer leaf (children got added) + while(this->node->first_child) + this->node=this->node->first_child; + } + else { + while(this->node->next_sibling==0) { + if (this->node->parent==0) return *this; + this->node=this->node->parent; + if (top_node != 0 && this->node==top_node) return *this; + } + this->node=this->node->next_sibling; + while(this->node->first_child) + this->node=this->node->first_child; + } + return *this; + } + +template +typename tree::leaf_iterator& tree::leaf_iterator::operator--() + { + assert(this->node!=0); + while (this->node->prev_sibling==0) { + if (this->node->parent==0) return *this; + this->node=this->node->parent; + if (top_node !=0 && this->node==top_node) return *this; + } + this->node=this->node->prev_sibling; + while(this->node->last_child) + this->node=this->node->last_child; + return *this; + } + +template +typename tree::leaf_iterator tree::leaf_iterator::operator++(int) + { + leaf_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::leaf_iterator tree::leaf_iterator::operator--(int) + { + leaf_iterator copy = *this; + --(*this); + return copy; + } + + +template +typename tree::leaf_iterator& tree::leaf_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename tree::leaf_iterator& tree::leaf_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +#endif + +// Local variables: +// tab-width: 3 +// End: diff --git a/core/opengate_core/opengate_lib/tree_util.hh b/core/opengate_core/opengate_lib/tree_util.hh new file mode 100644 index 000000000..bc69594f0 --- /dev/null +++ b/core/opengate_core/opengate_lib/tree_util.hh @@ -0,0 +1,92 @@ +/* + + A collection of miscellaneous utilities that operate on the templated + tree.hh class. + + + Copyright (C) 2001-2009 Kasper Peeters + + (At the moment this only contains a printing utility, thanks to Linda + Buisman ) + + This program is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#ifndef tree_util_hh_ +#define tree_util_hh_ + +#include +#include "tree.hh" + +namespace kptree { + +template +void print_tree_bracketed(const tree& t, std::ostream& str=std::cout); + +template +void print_subtree_bracketed(const tree& t, typename tree::iterator iRoot, + std::ostream& str=std::cout); + + + +// Iterate over all roots (the head) and print each one on a new line +// by calling printSingleRoot. + +template +void print_tree_bracketed(const tree& t, std::ostream& str) + { + int headCount = t.number_of_siblings(t.begin()); + int headNum = 0; + for(typename tree::sibling_iterator iRoots = t.begin(); iRoots != t.end(); ++iRoots, ++headNum) { + print_subtree_bracketed(t,iRoots,str); + if (headNum != headCount) { + str << std::endl; + } + } + } + + +// Print everything under this root in a flat, bracketed structure. + +template +void print_subtree_bracketed(const tree& t, typename tree::iterator iRoot, std::ostream& str) + { + if(t.empty()) return; + if (t.number_of_children(iRoot) == 0) { + str << *iRoot; + } + else { + // parent + str << *iRoot; + str << "("; + // child1, ..., childn + int siblingCount = t.number_of_siblings(t.begin(iRoot)); + int siblingNum; + typename tree::sibling_iterator iChildren; + for (iChildren = t.begin(iRoot), siblingNum = 0; iChildren != t.end(iRoot); ++iChildren, ++siblingNum) { + // recursively print child + print_subtree_bracketed(t,iChildren,str); + // comma after every child except the last one + if (siblingNum != siblingCount ) { + str << ", "; + } + } + str << ")"; + } + } + +} + +#endif diff --git a/opengate/actors/actorbuilders.py b/opengate/actors/actorbuilders.py index 166c5f1d3..237fa1239 100644 --- a/opengate/actors/actorbuilders.py +++ b/opengate/actors/actorbuilders.py @@ -20,6 +20,7 @@ BremSplittingActor, ComptSplittingActor, LastVertexInteractionSplittingActor, + LastVertexInteractionSplittingActorOld, ComptPseudoTransportationActor, KillNonInteractingParticleActor, SurfaceSplittingActor, diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index a763e3ae7..cb3baf9b0 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -486,6 +486,41 @@ def initialize(self, volume_engine=None): self.fListOfVolumeAncestor = self.list_of_volume_name print(self.fListOfVolumeAncestor) + +class LastVertexInteractionSplittingActorOld(g4.GateLastVertexInteractionSplittingActorOld, ActorBase): + type_name = "LastVertexInteractionSplittingActorOld" + + def set_default_user_info(user_info): + ActorBase.set_default_user_info(user_info) + deg = g4_units.deg + user_info.splitting_factor = 1 + user_info.russian_roulette_for_angle = False + user_info.rotation_vector_director = False + user_info.vector_director = [0, 0, -1] + user_info.max_theta = 90 * deg + user_info.list_of_volume_name = [] + + def __init__(self, user_info): + ActorBase.__init__(self, user_info) + g4.GateLastVertexInteractionSplittingActorOld.__init__(self, user_info.__dict__) + self.list_of_volume_name = user_info.list_of_volume_name + self.user_info.mother = user_info.mother + + def initialize(self, volume_engine=None): + + super().initialize(volume_engine) + volume_tree = self.simulation.volume_manager.get_volume_tree() + dico_of_volume_tree = {} + for pre, _, node in RenderTree(volume_tree): + dico_of_volume_tree[str(node.name)] = node + volume_name = self.user_info.mother + while volume_name != "world": + node = dico_of_volume_tree[volume_name] + volume_name = node.mother + self.list_of_volume_name.append(volume_name) + self.fListOfVolumeAncestor = self.list_of_volume_name + print(self.fListOfVolumeAncestor) + class ComptPseudoTransportationActor( g4.GateOptrComptPseudoTransportationActor, ActorBase ): diff --git a/opengate/sources/builders.py b/opengate/sources/builders.py index 14914fd8f..6d14ecfe8 100644 --- a/opengate/sources/builders.py +++ b/opengate/sources/builders.py @@ -1,4 +1,4 @@ -from .generic import GenericSource, TemplateSource +from .generic import GenericSource, TemplateSource, LastVertexSource from .voxelsources import VoxelsSource from .gansources import GANSource, GANPairsSource from .beamsources import IonPencilBeamSource, TreatmentPlanPBSource @@ -20,6 +20,7 @@ GANPairsSource, IonPencilBeamSource, TemplateSource, + LastVertexSource, PhaseSpaceSource, TreatmentPlanPBSource, PhotonFromIonDecaySource, diff --git a/opengate/sources/generic.py b/opengate/sources/generic.py index 701de7b59..110fdfc25 100644 --- a/opengate/sources/generic.py +++ b/opengate/sources/generic.py @@ -581,3 +581,21 @@ def initialize(self, run_timing_intervals): # initialize SourceBase.initialize(self, run_timing_intervals) + + +class LastVertexSource(SourceBase): + type_name = "LastVertexSource" + + @staticmethod + def set_default_user_info(user_info): + SourceBase.set_default_user_info(user_info) + user_info.n = 0 + + def create_g4_source(self): + return opengate_core.GateLastVertexSource() + + def __init__(self, user_info): + super().__init__(user_info) + + def initialize(self, run_timing_intervals): + SourceBase.initialize(self, run_timing_intervals) From 957103f7c40cabe6a4e889e82125f6c7fec80480 Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 27 Sep 2024 16:39:06 +0200 Subject: [PATCH 31/82] Add a particle kill according to its direction relative to a specified angle --- ...ateLastVertexInteractionSplittingActor.cpp | 79 +++++++++++-------- .../GateLastVertexInteractionSplittingActor.h | 3 +- opengate/actors/miscactors.py | 2 +- 3 files changed, 51 insertions(+), 33 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 5e6d10d9c..dba4b8f39 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -59,7 +59,7 @@ GateLastVertexInteractionSplittingActor:: fMotherVolumeName = DictGetStr(user_info, "mother"); fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); - fRussianRouletteForAngle = DictGetBool(user_info, "russian_roulette_for_angle"); + fAngularKill = DictGetBool(user_info, "angular_kill"); fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); fMaxTheta = DictGetDouble(user_info, "max_theta"); fActions.insert("StartSimulationAction"); @@ -89,6 +89,14 @@ void GateLastVertexInteractionSplittingActor::print_tree(const tree fMaxTheta) + return false; + return true; +} + G4Track* GateLastVertexInteractionSplittingActor::CreateATrackFromContainer(LastVertexDataContainer container, G4Step *step ){ auto *particle_table = G4ParticleTable::GetParticleTable(); @@ -156,14 +164,17 @@ void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *aTrack, gammaWeight); - trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) { - G4Track *newTrack = processFinalState->GetSecondary(j); - newTrack->SetWeight(gammaWeight); + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector,fMaxTheta) == false)) + delete newTrack; + else { trackVector->push_back(newTrack); + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) { + G4Track *newTrack = processFinalState->GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } } - processFinalState->Clear(); gammaProcessFinalState->Clear(); delete aTrack; @@ -217,6 +228,7 @@ G4VParticleChange* GateLastVertexInteractionSplittingActor::eBremProcessFinalSta void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container) { + G4Track* track = CreateATrackFromContainer(container,initStep); G4String particleName = track->GetParticleDefinition()->GetParticleName(); @@ -236,33 +248,38 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS processFinalState = eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*track,*initStep); } } + + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); G4int idx = 0; if (NbOfSecondaries >0) { gammaWeight = track->GetWeight()/fSplittingFactor; - - G4double randomIdx = round((NbOfSecondaries -1) * G4UniformRand()); - idx = (G4int) randomIdx; - G4Track *newTrack = processFinalState->GetSecondary(idx); - if (!(isnan((newTrack->GetMomentumDirection())[0]))){ - newTrack->SetWeight(gammaWeight); - newTrack->SetCreatorProcess(process); - trackVector->push_back(newTrack); - } - else{ - delete newTrack; - } - - - for(int i = 0; i < NbOfSecondaries;i++){ - if (i != idx) - delete processFinalState->GetSecondary(i); + G4bool alreadySplitted = false; + for (int i; i < NbOfSecondaries; i++){ + G4Track *newTrack = processFinalState->GetSecondary(i); + G4ThreeVector momentum = newTrack->GetMomentumDirection(); + if (!(isnan(momentum[0]))){ + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector,fMaxTheta) == false)){ + delete newTrack; + } + else if (alreadySplitted == false){ + newTrack->SetWeight(gammaWeight); + newTrack->SetCreatorProcess(process); + trackVector->push_back(newTrack); + alreadySplitted = true; + } + else{ + delete newTrack; + } + } + else { + delete newTrack; } + } } - + delete track; - @@ -476,7 +493,7 @@ void GateLastVertexInteractionSplittingActor::BeginOfEventAction( fTrackIDOfSplittedTrack = -1; fNotSplitted == true; fIsAnnihilAlreadySplit = false; - //std::cout<GetTrack()->GetMomentumDirection(),fVectorDirector,fMaxTheta) == true))){ + fListOfContainer.push_back((*fIterator)); + } step->GetTrack()->SetTrackStatus(fStopAndKill); //std::cout<GetPostStepPoint()->GetMomentumDirection()<GetPreStepPoint()->GetMomentumDirection()<FindSourceByName(fActiveSource); GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; LastVertexDataContainer container = vertexSource->GetLastVertexContainer(); @@ -600,8 +618,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { fCounter ++; } - - + fIsFirstStep = false; diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index 7e20585c1..f67ae3ccd 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -50,7 +50,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { virtual ~GateLastVertexInteractionSplittingActor() {} G4double fSplittingFactor; - G4bool fRussianRouletteForAngle = false; + G4bool fAngularKill; G4bool fRotationVectorDirector; G4ThreeVector fVectorDirector; G4double fMaxTheta; @@ -104,6 +104,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { virtual void PostUserTrackingAction(const G4Track *track) override; // Pure splitting functions + G4bool DoesParticleEmittedInSolidAngle(G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta); G4Track *CreateComptonTrack(G4ParticleChangeForGamma *, G4Track, G4double); void ComptonSplitting(G4Step* initStep,G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); void SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index cb3baf9b0..593047024 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -459,7 +459,7 @@ def set_default_user_info(user_info): ActorBase.set_default_user_info(user_info) deg = g4_units.deg user_info.splitting_factor = 1 - user_info.russian_roulette_for_angle = False + user_info.angular_kill = False user_info.rotation_vector_director = False user_info.vector_director = [0, 0, -1] user_info.max_theta = 90 * deg From 06eae5bb672f1ab9cf791a11a62fd4b27e867a23 Mon Sep 17 00:00:00 2001 From: majacquet Date: Thu, 3 Oct 2024 18:43:35 +0200 Subject: [PATCH 32/82] bug correction according exiting particle coming back to the biased volume --- ...ateLastVertexInteractionSplittingActor.cpp | 170 +++++++++--------- .../src/test076_last_vertex_splittting.py | 76 +++++--- 2 files changed, 139 insertions(+), 107 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index dba4b8f39..921f57698 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -112,8 +112,10 @@ G4Track* GateLastVertexInteractionSplittingActor::CreateATrackFromContainer(Last G4ThreeVector polarization = container.GetPolarization(); G4DynamicParticle* dynamicParticle = new G4DynamicParticle(particleDefinition,momentum,energy); - G4Track* aTrack = new G4Track(dynamicParticle,step->GetPreStepPoint()->GetGlobalTime(), position); - //std::cout<GetMomentumDirection()<GetPreStepPoint() != 0) + time = step->GetPreStepPoint()->GetGlobalTime(); + G4Track* aTrack = new G4Track(dynamicParticle,time, position); aTrack->SetPolarization(polarization); if (trackStatus == 0){ aTrack->SetTrackStatus(fAlive); @@ -133,15 +135,16 @@ G4Track* GateLastVertexInteractionSplittingActor::CreateATrackFromContainer(Last G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) { + G4double energy = gammaProcess->GetProposedKineticEnergy(); G4double globalTime = track.GetGlobalTime(); - G4double newGammaWeight = weight; + //G4double newGammaWeight = weight; G4ThreeVector polarization = gammaProcess->GetProposedPolarization(); const G4ThreeVector momentum = gammaProcess->GetProposedMomentumDirection(); const G4ThreeVector position = track.GetPosition(); G4Track *newTrack = new G4Track(track); - newTrack->SetWeight(newGammaWeight); + //newTrack->SetWeight(newGammaWeight); newTrack->SetKineticEnergy(energy); newTrack->SetMomentumDirection(momentum); newTrack->SetPosition(position); @@ -153,17 +156,19 @@ void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; + G4Track* aTrack = CreateATrackFromContainer(container,initStep); GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*aTrack, *initStep); G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; - const G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); - gammaWeight = aTrack->GetWeight()/ fSplittingFactor; + const G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); + //gammaWeight = aTrack->GetWeight()/ fSplittingFactor; G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *aTrack, gammaWeight); + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector,fMaxTheta) == false)) delete newTrack; else { @@ -171,13 +176,18 @@ void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); for (int j = 0; j < NbOfSecondaries; j++) { G4Track *newTrack = processFinalState->GetSecondary(j); - newTrack->SetWeight(gammaWeight); + //newTrack->SetWeight(gammaWeight); trackVector->push_back(newTrack); } } + processFinalState->Clear(); gammaProcessFinalState->Clear(); delete aTrack; + + + + } @@ -187,7 +197,6 @@ G4VProcess* GateLastVertexInteractionSplittingActor::GetProcessFromProcessName(G G4ParticleDefinition *particleDefinition = particle_table->FindParticle(particleName); G4ProcessManager *processManager = particleDefinition->GetProcessManager(); G4ProcessVector *processList = processManager->GetProcessList(); - G4VProcess* nullProcess = nullptr; for (size_t i = 0; i < processList->size(); ++i) { auto process = (*processList)[i]; @@ -229,7 +238,7 @@ G4VParticleChange* GateLastVertexInteractionSplittingActor::eBremProcessFinalSta void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container) { - + G4Track* track = CreateATrackFromContainer(container,initStep); G4String particleName = track->GetParticleDefinition()->GetParticleName(); G4TrackVector *trackVector = CurrentStep->GetfSecondary(); @@ -254,7 +263,7 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); G4int idx = 0; if (NbOfSecondaries >0) { - gammaWeight = track->GetWeight()/fSplittingFactor; + //gammaWeight = track->GetWeight()/fSplittingFactor; G4bool alreadySplitted = false; for (int i; i < NbOfSecondaries; i++){ G4Track *newTrack = processFinalState->GetSecondary(i); @@ -264,7 +273,7 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS delete newTrack; } else if (alreadySplitted == false){ - newTrack->SetWeight(gammaWeight); + //newTrack->SetWeight(gammaWeight); newTrack->SetCreatorProcess(process); trackVector->push_back(newTrack); alreadySplitted = true; @@ -281,10 +290,12 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS delete track; + processFinalState->Clear(); + } @@ -425,7 +436,7 @@ G4bool GateLastVertexInteractionSplittingActor::IsParticleExitTheBiasedVolume(G4 if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - + if (logicalVolumeNamePreStep != logicalVolumeNamePostStep){ if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()){ return true; @@ -435,9 +446,6 @@ G4bool GateLastVertexInteractionSplittingActor::IsParticleExitTheBiasedVolume(G4 return false; } */ - else{ - return true; - } } return false; } @@ -493,7 +501,8 @@ void GateLastVertexInteractionSplittingActor::BeginOfEventAction( fTrackIDOfSplittedTrack = -1; fNotSplitted == true; fIsAnnihilAlreadySplit = false; - std::cout<GetPreStepPoint()->GetPhysicalVolume() != 0) logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) @@ -525,18 +536,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); - - /* - if (processName == "annihil"){ - std::cout<<"ANNIHIL"<<" "<GetTrack()->GetTrackStatus()<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTotalEnergyDeposit()<GetSecondaryInCurrentStep(); - for (const G4Track* secondary : *secondaries){ - std::cout<GetKineticEnergy()<GetTrack()->SetTrackStatus(fStopAndKill); - //std::cout<GetPostStepPoint()->GetMomentumDirection()<FindSourceByName(fActiveSource); GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; LastVertexDataContainer container = vertexSource->GetLastVertexContainer(); - G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentumDirection(); - G4Step* copyInitStep = nullptr; - - G4String processToSplit = vertexSource->GetProcessToSplit(); - if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ - - if ((processToSplit != "annihil") || ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ - - step->GetfSecondary()->clear(); - if ((processToSplit != "msc") && (processToSplit != "conv")) { - fCopyInitStep= new G4Step(*step); - if (processToSplit == "eBrem"){ - fCopyInitStep->SetStepLength(container.GetStepLength()); - fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(container.GetEnergy()); - + if ((step->GetTrack()->GetWeight() == container.GetWeight())){ + G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentumDirection(); + G4String processToSplit = vertexSource->GetProcessToSplit(); + if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ + + if ((processToSplit != "annihil") || ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ + + step->GetfSecondary()->clear(); + //FIXME : list of process which are not splitable yet + if ((processToSplit != "msc") && (processToSplit != "conv") && (processToSplit != "eIoni")) { + fCopyInitStep= new G4Step(*step); + if (processToSplit == "eBrem"){ + fCopyInitStep->SetStepLength(container.GetStepLength()); + fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(container.GetEnergy()); + + } + while (step->GetfSecondary()->size() != 1){ + step->GetfSecondary()->clear(); + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); + + } } - while (step->GetfSecondary()->size() == 0){ - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - + step->GetTrack()->SetTrackStatus(fStopAndKill); + + if (processToSplit == "annihil"){ + fIsAnnihilAlreadySplit = true; + } } + + + else if ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ + step->GetfSecondary()->clear(); + step->GetTrack()->SetTrackStatus(fStopAndKill); } - step->GetTrack()->SetTrackStatus(fStopAndKill); - if (processToSplit == "annihil"){ - fIsAnnihilAlreadySplit = true; - } } - else if ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ + + + else if (IsTheParticleUndergoesAProcess(step)){ step->GetfSecondary()->clear(); + while (step->GetfSecondary()->size() != 1){ + step->GetfSecondary()->clear(); + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); + } step->GetTrack()->SetTrackStatus(fStopAndKill); + } - } - - else if (IsTheParticleUndergoesAProcess(step)){ - step->GetfSecondary()->clear(); - while (step->GetfSecondary()->size() == 0){ - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - } - step->GetTrack()->SetTrackStatus(fStopAndKill); - - } - + else if (IsParticleExitTheBiasedVolume(step)){ + fSplitCounter += 1; + if (fSplitCounter < fSplittingFactor){ + while (step->GetfSecondary()->size() != 1){ + step->GetfSecondary()->clear(); + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - else if (IsParticleExitTheBiasedVolume(step)){ - fSplitCounter += 1; - if (fSplitCounter < fSplittingFactor){ - while (step->GetfSecondary()->size() == 0){ - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - } - } - else{ - delete fCopyInitStep; - fSplitCounter = 0; - fCounter = 0; - } - + } + } + else{ + delete fCopyInitStep; + fCopyInitStep = nullptr; + fSplitCounter = 0; + } + + + //FIXME Debug case if splitting factor equal to 1, as It is used as a condition to enable the split + // I just set the weight to a very close value of the real one + if (fSplittingFactor != 1) + step->GetPostStepPoint()->SetWeight(container.GetWeight()/fSplittingFactor); + else + step->GetPostStepPoint()->SetWeight(container.GetWeight()*0.99999999); + } } - - fCounter ++; - } fIsFirstStep = false; diff --git a/opengate/tests/src/test076_last_vertex_splittting.py b/opengate/tests/src/test076_last_vertex_splittting.py index dbf9d5fa9..078452349 100755 --- a/opengate/tests/src/test076_last_vertex_splittting.py +++ b/opengate/tests/src/test076_last_vertex_splittting.py @@ -97,7 +97,7 @@ def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): if __name__ == "__main__": paths = utility.get_default_test_paths( - __file__, "test071test_operator_compt_splitting", output_folder="test071" + __file__, "test076_last_vertex_splitting", output_folder="test076" ) # create the simulation @@ -113,7 +113,10 @@ def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): ui.number_of_threads = 1 # 1236566 seg fault #12745 double compt - ui.random_seed = 73 + # ui.random_seed = 73 + # ui.random_seed = 2632 + # ui.random_seed = 45 + ui.random_seed = 444 # units m = gate.g4_units.m @@ -131,7 +134,7 @@ def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): # adapt world size world = sim.world world.size = [0.25 * m, 0.25 * m, 0.25 * m] - world.material = "G4_WATER" + world.material = "G4_Galactic" ####### GEOMETRY TO IRRADIATE ############# sim.volume_manager.material_database.add_material_weights( @@ -157,40 +160,57 @@ def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): "xyz", [angle_y, angle_y, angle_z], degrees=True ).as_matrix() W_tubs.rotation = rotation + bias = True - ####### Compton Splitting ACTOR ######### - nb_split = 25 - vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") - vertex_splitting_actor.mother = W_tubs.name - vertex_splitting_actor.splitting_factor = nb_split - vertex_splitting_actor.russian_roulette_for_angle = False + if bias : + ###### Last vertex Splitting ACTOR ######### + nb_split = 25 + vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") + vertex_splitting_actor.mother = W_tubs.name + vertex_splitting_actor.splitting_factor = nb_split + vertex_splitting_actor.angular_kill = True + vertex_splitting_actor.max_theta = 15*deg ##### PHASE SPACE plan ######" - plan_tubs = sim.add_volume("Tubs", "phsp_tubs") - plan_tubs.material = "G4_Galactic" - plan_tubs.mother = world.name - plan_tubs.rmin = W_tubs.rmax + 1*cm - plan_tubs.rmax = plan_tubs.rmin + 1 * nm - plan_tubs.dz = 0.05 * m - plan_tubs.color = [0.2, 1, 0.8, 1] - plan_tubs.rotation = rotation - - ####### Electron source ########### + # plan_tubs = sim.add_volume("Tubs", "phsp_tubs") + # plan_tubs.material = "G4_Galactic" + # plan_tubs.mother = world.name + # plan_tubs.rmin = W_tubs.rmax + 1*cm + # plan_tubs.rmax = plan_tubs.rmin + 1 * nm + # plan_tubs.dz = 0.05 * m + # plan_tubs.color = [0.2, 1, 0.8, 1] + # plan_tubs.rotation = rotation + + plan = sim.add_volume("Box", "plan_phsp") + plan.material = "G4_Galactic" + plan.size = [5*cm,5*cm,1*nm] + plan.translation = [0,0,-1*cm] + + + ####### gamma source ########### source = sim.add_source("GenericSource", "source1") source.particle = "gamma" - source.n = 1 + if bias : + source.n = 100 + else : + source.n = 10000000 source.position.type = "sphere" source.position.radius = 1 * nm source.direction.type = "momentum" # source.direction.momentum = [0,0,-1] source.direction.momentum = np.dot(rotation, np.array([0, 0, -1])) source.energy.type = "mono" - source.energy.mono = 4* MeV + source.energy.mono = 4 * MeV + # + ###### LastVertexSource ############# + if bias : + source_0 = sim.add_source("LastVertexSource", "source_vertex") + source_0.n = 1 ####### PHASE SPACE ACTOR ############## phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") - phsp_actor.mother = plan_tubs.name + phsp_actor.mother = plan.name phsp_actor.attributes = [ "EventID", "TrackID", @@ -198,12 +218,14 @@ def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): "ParticleName", "KineticEnergy", "PreDirection", + "PrePosition", "TrackCreatorProcess", ] + if bias : + phsp_actor.output = paths.output / ("test076_output_data_last_vertex_osef.root") + else : + phsp_actor.output = paths.output / ("test076_output_data_last_vertex_ref.root") - phsp_actor.output = paths.output / "test075_output_data.root" - - ##### MODIFIED PHYSICS LIST ############### s = sim.add_actor("SimulationStatisticsActor", "Stats") s.track_types_flag = True @@ -215,8 +237,8 @@ def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): sim.add_g4_command_before_init(s) sim.physics_manager.global_production_cuts.gamma = 1 * mm - sim.physics_manager.global_production_cuts.electron = 1 * km - sim.physics_manager.global_production_cuts.positron = 1 * um + sim.physics_manager.global_production_cuts.electron = 1000 * km + sim.physics_manager.global_production_cuts.positron = 1000 * km output = sim.run() From 61745551341a775ef53d1e5a982d169a8cf6c78e Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 15 Oct 2024 15:38:42 +0200 Subject: [PATCH 33/82] Modif of container structure to optimize time simulation --- ...ateLastVertexInteractionSplittingActor.cpp | 157 +++++++------ .../GateLastVertexInteractionSplittingActor.h | 1 + .../opengate_lib/GateLastVertexSource.cpp | 14 +- .../GateLastVertexSplittingDataContainer.h | 217 ++---------------- .../GateLastVertexSplittingPostStepDoIt.h | 8 + .../GateLastVertexSplittingSimpleContainer.h | 217 ++++++++++++++++++ 6 files changed, 334 insertions(+), 280 deletions(-) create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 921f57698..22cd2800b 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -98,8 +98,9 @@ G4bool GateLastVertexInteractionSplittingActor::DoesParticleEmittedInSolidAngle( } -G4Track* GateLastVertexInteractionSplittingActor::CreateATrackFromContainer(LastVertexDataContainer container, G4Step *step ){ - auto *particle_table = G4ParticleTable::GetParticleTable(); +G4Track* GateLastVertexInteractionSplittingActor::CreateATrackFromContainer(LastVertexDataContainer theContainer, G4Step *step ){ + auto *particle_table = G4ParticleTable::GetParticleTable(); + SimpleContainer container = theContainer.GetContainerToSplit(); G4ParticleDefinition *particleDefinition = particle_table->FindParticle(container.GetParticleNameToSplit()); G4ThreeVector momentum = container.GetMomentum(); G4double energy = container.GetEnergy(); @@ -236,10 +237,11 @@ G4VParticleChange* GateLastVertexInteractionSplittingActor::eBremProcessFinalSta } -void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container) { +void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer theContainer) { - G4Track* track = CreateATrackFromContainer(container,initStep); + G4Track* track = CreateATrackFromContainer(theContainer,initStep); + SimpleContainer container = theContainer.GetContainerToSplit(); G4String particleName = track->GetParticleDefinition()->GetParticleName(); G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; @@ -299,18 +301,19 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS } -void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* initStep,G4Step *step,LastVertexDataContainer container) { +void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* initStep,G4Step *step,LastVertexDataContainer theContainer) { // We retrieve the process associated to the process name to split and we // split according the process. Since for compton scattering, the gamma is not // a secondary particles, this one need to have his own splitting function. + SimpleContainer container = theContainer.GetContainerToSplit(); G4VProcess* processToSplit = GetProcessFromProcessName(container.GetParticleNameToSplit(),container.GetProcessNameToSplit()); G4String processName = container.GetProcessNameToSplit(); if (processName == "compt") { - ComptonSplitting(initStep,step, processToSplit, container); + ComptonSplitting(initStep,step, processToSplit, theContainer); } else if((processName != "msc") && (processName != "conv")){ - SecondariesSplitting(initStep, step, processToSplit, container); + SecondariesSplitting(initStep, step, processToSplit, theContainer); } } @@ -397,7 +400,6 @@ void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ } } - LastVertexDataContainer* container = &(*fIterator); G4int trackID = container->GetTrackID(); if ((processName != "Transportation") &&(processName !="None") && (processName !="Rayl")){ @@ -420,8 +422,10 @@ void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ if (((processName == "annihil"))){ energy -= (step->GetTotalEnergyDeposit()); } - container->SetSplittingParameters(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); - container->PushListOfSplittingParameters(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); + SimpleContainer containerToSplit = SimpleContainer(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); + container->SetContainerToSplit(containerToSplit); + container->PushListOfSplittingParameters(); + } } } @@ -548,84 +552,86 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { } } - - if (fActiveSource == "source_vertex"){ - - auto* source = fSourceManager->FindSourceByName(fActiveSource); - GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; - LastVertexDataContainer container = vertexSource->GetLastVertexContainer(); - if ((step->GetTrack()->GetWeight() == container.GetWeight())){ - G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentumDirection(); - G4String processToSplit = vertexSource->GetProcessToSplit(); - if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ - - if ((processToSplit != "annihil") || ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ - - step->GetfSecondary()->clear(); - //FIXME : list of process which are not splitable yet - if ((processToSplit != "msc") && (processToSplit != "conv") && (processToSplit != "eIoni")) { - fCopyInitStep= new G4Step(*step); - if (processToSplit == "eBrem"){ - fCopyInitStep->SetStepLength(container.GetStepLength()); - fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(container.GetEnergy()); + if (fOnlyTree == false){ + if (fActiveSource == "source_vertex"){ + + auto* source = fSourceManager->FindSourceByName(fActiveSource); + GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; + LastVertexDataContainer container = vertexSource->GetLastVertexContainer(); + if ((step->GetTrack()->GetWeight() == container.GetContainerToSplit().GetWeight())){ + G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentumDirection(); + G4String processToSplit = vertexSource->GetProcessToSplit(); + if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ + + if ((processToSplit != "annihil") || ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ + + step->GetfSecondary()->clear(); + //FIXME : list of process which are not splitable yet + if ((processToSplit != "msc") && (processToSplit != "conv") && (processToSplit != "eIoni")) { + fCopyInitStep= new G4Step(*step); + if (processToSplit == "eBrem"){ + fCopyInitStep->SetStepLength(container.GetContainerToSplit().GetStepLength()); + fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(container.GetContainerToSplit().GetEnergy()); + + } + while (step->GetfSecondary()->size() != 1){ + step->GetfSecondary()->clear(); + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); + + } } - while (step->GetfSecondary()->size() != 1){ - step->GetfSecondary()->clear(); - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - + step->GetTrack()->SetTrackStatus(fStopAndKill); + + if (processToSplit == "annihil"){ + fIsAnnihilAlreadySplit = true; + } } + + + else if ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ + step->GetfSecondary()->clear(); + step->GetTrack()->SetTrackStatus(fStopAndKill); } - step->GetTrack()->SetTrackStatus(fStopAndKill); - if (processToSplit == "annihil"){ - fIsAnnihilAlreadySplit = true; - } } - - else if ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ + + else if (IsTheParticleUndergoesAProcess(step)){ step->GetfSecondary()->clear(); + while (step->GetfSecondary()->size() != 1){ + step->GetfSecondary()->clear(); + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); + } step->GetTrack()->SetTrackStatus(fStopAndKill); + } - } - - - else if (IsTheParticleUndergoesAProcess(step)){ - step->GetfSecondary()->clear(); - while (step->GetfSecondary()->size() != 1){ - step->GetfSecondary()->clear(); - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - } - step->GetTrack()->SetTrackStatus(fStopAndKill); - - } - - else if (IsParticleExitTheBiasedVolume(step)){ - fSplitCounter += 1; - if (fSplitCounter < fSplittingFactor){ - while (step->GetfSecondary()->size() != 1){ - step->GetfSecondary()->clear(); - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); + else if (IsParticleExitTheBiasedVolume(step)){ + fSplitCounter += 1; + if (fSplitCounter < fSplittingFactor){ + while (step->GetfSecondary()->size() != 1){ + step->GetfSecondary()->clear(); + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - } - } - else{ - delete fCopyInitStep; - fCopyInitStep = nullptr; - fSplitCounter = 0; - } - + } + } + else{ + delete fCopyInitStep; + fCopyInitStep = nullptr; + fSplitCounter = 0; + } + - //FIXME Debug case if splitting factor equal to 1, as It is used as a condition to enable the split - // I just set the weight to a very close value of the real one - if (fSplittingFactor != 1) - step->GetPostStepPoint()->SetWeight(container.GetWeight()/fSplittingFactor); - else - step->GetPostStepPoint()->SetWeight(container.GetWeight()*0.99999999); - } + //FIXME Debug case if splitting factor equal to 1, as It is used as a condition to enable the split + // I just set the weight to a very close value of the real one + if (fSplittingFactor != 1) + step->GetPostStepPoint()->SetWeight(container.GetContainerToSplit().GetWeight()/fSplittingFactor); + else + step->GetPostStepPoint()->SetWeight(container.GetContainerToSplit().GetWeight()*0.99999999); + } + } } } @@ -655,12 +661,15 @@ void GateLastVertexInteractionSplittingActor::EndOfEventAction( fListOfContainer.clear(); } + if (fOnlyTree == false){ + auto* source = fSourceManager->FindSourceByName("source_vertex"); GateLastVertexSource* vertexSource = (GateLastVertexSource*) source; if (vertexSource->GetNumberOfGeneratedEvent() < vertexSource->GetNumberOfEventToSimulate()){ fSourceManager->SetActiveSourcebyName("source_vertex"); } fActiveSource = fSourceManager->GetActiveSourceName(); + } } diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index f67ae3ccd..4957e4f90 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -70,6 +70,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4String fActiveSource = "None"; G4bool fIsAnnihilAlreadySplit =false; G4int fCounter; + G4bool fOnlyTree = true; GateLastVertexSource* fVertexSource = nullptr; tree fTree; tree::post_order_iterator fIterator; diff --git a/core/opengate_core/opengate_lib/GateLastVertexSource.cpp b/core/opengate_core/opengate_lib/GateLastVertexSource.cpp index e89c5a728..b903d90c8 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSource.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexSource.cpp @@ -74,17 +74,17 @@ void GateLastVertexSource::GenerateOnePrimary(G4Event* event, double current_sim } else { - - G4double energy = fListOfContainer[idx].GetEnergy(); + SimpleContainer containerToSplit = fListOfContainer[idx].GetContainerToSplit(); + G4double energy = containerToSplit.GetEnergy(); if (energy < 0){ energy = 0; } fContainer = fListOfContainer[idx]; - G4ThreeVector position = fListOfContainer[idx].GetVertexPosition(); - G4ThreeVector momentum = fListOfContainer[idx].GetMomentum(); - G4String particleName = fListOfContainer[idx].GetParticleNameToSplit(); - G4double weight =fListOfContainer[idx].GetWeight(); - fProcessToSplit = fListOfContainer[idx].GetProcessNameToSplit(); + G4ThreeVector position = containerToSplit.GetVertexPosition(); + G4ThreeVector momentum = containerToSplit.GetMomentum(); + G4String particleName = containerToSplit.GetParticleNameToSplit(); + G4double weight =containerToSplit.GetWeight(); + fProcessToSplit = containerToSplit.GetProcessNameToSplit(); auto &l = fThreadLocalData.Get(); auto *particle_table = G4ParticleTable::GetParticleTable(); diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h index 112cc8062..0d63bd80a 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h @@ -45,96 +45,18 @@ #include "G4eplusAnnihilationEntanglementClipBoard.hh" #include "G4EmParameters.hh" #include "G4PhysicsModelCatalog.hh" +#include "GateLastVertexSplittingSimpleContainer.h" class LastVertexDataContainer{ public : -LastVertexDataContainer(G4ThreeVector interactionPosition, G4ThreeVector momentum,G4ThreeVector polarization, G4double energy,G4int trackID,G4String creationProcessName){ - - fPositionToSplit= interactionPosition; - fMomentumToSplit = momentum; - fEnergyToSplit = energy; - fIsExit = false; - fToRegenerate = false; - fTrackID = trackID; - fCreationProcessName = creationProcessName; - fPolarizationToSplit = polarization; - -} - LastVertexDataContainer(){} ~LastVertexDataContainer(){} -void SetProcessNameToSplit(G4String processName){ - fProcessNameToSplit = processName; -} - -G4String GetProcessNameToSplit(){ - return fProcessNameToSplit; -} - -void SetEnergy(G4double energy){ - fEnergyToSplit =energy; -} - -G4double GetEnergy(){ - return fEnergyToSplit; -} - - -void SetWeight(G4double weight){ - fWeightToSplit =weight; -} - -G4double GetWeight(){ - return fWeightToSplit; -} - -void SetPolarization(G4ThreeVector polarization){ - fPolarizationToSplit = polarization; -} - -G4ThreeVector GetPolarization(){ - return fPolarizationToSplit; -} - -void SetMomentum(G4ThreeVector momentum){ - fMomentumToSplit =momentum; -} - -G4ThreeVector GetMomentum(){ - return fMomentumToSplit; -} - -void SetVertexPosition(G4ThreeVector position){ - fPositionToSplit = position; -} - -G4ThreeVector GetVertexPosition(){ - return fPositionToSplit; -} - -void SetExitingStatus(G4bool isExit){ - fIsExit = isExit; -} - -G4bool GetExitingStatus(){ - return fIsExit; -} - - -void SetRegenerationStatus(G4bool toRegenerate){ - fToRegenerate= toRegenerate; -} - -G4bool GetRegenerationStatus(){ - return fToRegenerate; -} - void SetTrackID(G4int trackID ){ @@ -154,14 +76,6 @@ G4String GetParticleName(){ return fParticleName; } -void SetParticleNameToSplit(G4String name){ - fParticleNameToSplit = name; -} - -G4String GetParticleNameToSplit(){ - return fParticleNameToSplit; -} - void SetCreationProcessName(G4String creationProcessName){ fCreationProcessName = creationProcessName; @@ -172,95 +86,43 @@ G4String GetCreationProcessName(){ } - -void SetTrackStatus(G4int trackStatus){ - fTrackStatusToSplit = trackStatus; -} - - -G4int GetTrackStatus(){ - return fTrackStatusToSplit; -} - - -void SetNbOfSecondaries(G4int nbSec){ - fNumberOfSecondariesToSplit = nbSec; -} - -G4int GetNbOfSecondaries(){ - return fNumberOfSecondariesToSplit; -} - -void SetAnnihilationFlag(G4String flag){ - fAnnihilProcessFlag = flag; +void SetContainerToSplit(SimpleContainer container){ + fContainerToSplit = container; } -G4String GetAnnihilationFlag(){ - return fAnnihilProcessFlag; -} - -void SetStepLength(G4double length){ - fStepLength = length; -} -G4double GetStepLength(){ - return fStepLength; +SimpleContainer GetContainerToSplit(){ + return fContainerToSplit; } -void SetSplittingParameters(G4String processName,G4double energy,G4ThreeVector momentum, G4ThreeVector position,G4ThreeVector polarization,G4String name,G4double weight,G4int trackStatus,G4int nbSec,G4String flag,G4double length, G4ThreeVector prePos){ - fProcessNameToSplit = processName; - fEnergyToSplit = energy; - fMomentumToSplit = momentum; - fPositionToSplit = position; - fPolarizationToSplit = polarization; - fParticleNameToSplit = name; - fWeightToSplit = weight; - fTrackStatusToSplit = trackStatus; - fNumberOfSecondariesToSplit = nbSec; - fAnnihilProcessFlag = flag; - fStepLength = length; - fPrePosition = prePos; +void PushListOfSplittingParameters(){ + fVectorOfContainerToSplit.emplace_back(fContainerToSplit); } - -void PushListOfSplittingParameters(G4String processName,G4double energy,G4ThreeVector momentum, G4ThreeVector position,G4ThreeVector polarization,G4String name,G4double weight, G4int trackStatus,G4int nbSec, G4String flag,G4double length,G4ThreeVector prePos){ - fVectorOfProcessToSplit.push_back(processName); - fVectorOfEnergyToSplit.push_back(energy); - fVectorOfMomentumToSplit.push_back(momentum); - fVectorOfPositionToSplit.push_back(position); - fVectorOfPolarizationToSplit.push_back(polarization); - fVectorOfParticleNameToSplit.push_back(name); - fVectorOfWeightToSplit.push_back(weight); - fVectorOfTrackStatusToSplit.push_back(trackStatus); - fVectorOfNumberOfSecondariesToSplit.push_back(nbSec); - fVectorOfAnnihilProcessFlag.push_back(flag); - fVectorOfStepLength.push_back(length); - fVectorOfPrePosition.push_back(prePos); - -} - - LastVertexDataContainer ContainerFromParentInformation(G4Step* step){ LastVertexDataContainer aContainer = LastVertexDataContainer(); + aContainer.fTrackID = step->GetTrack()->GetTrackID(); aContainer.fParticleName = step->GetTrack()->GetDefinition()->GetParticleName(); - if (this->fProcessNameToSplit != "None"){ - if (this->fVectorOfProcessToSplit.size() !=0){ + if (this->fContainerToSplit.GetProcessNameToSplit() != "None"){ + if (this->fVectorOfContainerToSplit.size() !=0){ G4ThreeVector vertexPosition = step->GetTrack()->GetVertexPosition(); - for (int i =0;ifVectorOfPositionToSplit.size();i++){ - if (vertexPosition == this->fVectorOfPositionToSplit[i]){ - aContainer.SetSplittingParameters(this->fVectorOfProcessToSplit[i],this->fVectorOfEnergyToSplit[i],this->fVectorOfMomentumToSplit[i],this->fVectorOfPositionToSplit[i],this->fVectorOfPolarizationToSplit[i],this->fVectorOfParticleNameToSplit[i],this->fVectorOfWeightToSplit[i], this->fVectorOfTrackStatusToSplit[i], this->fVectorOfNumberOfSecondariesToSplit[i], this->fVectorOfAnnihilProcessFlag[i],this->fVectorOfStepLength[i],this->fVectorOfPrePosition[i]); + for (int i =0;ifVectorOfContainerToSplit.size();i++){ + if (vertexPosition == this->fVectorOfContainerToSplit[i].GetVertexPosition()){ + SimpleContainer tmpContainer = this->fVectorOfContainerToSplit[i]; + fContainerToSplit = SimpleContainer(tmpContainer.GetProcessNameToSplit(),tmpContainer.GetEnergy(),tmpContainer.GetMomentum(),tmpContainer.GetVertexPosition(),tmpContainer.GetPolarization(),tmpContainer.GetParticleNameToSplit(),tmpContainer.GetWeight(),tmpContainer.GetTrackStatus(),tmpContainer.GetNbOfSecondaries(),tmpContainer.GetAnnihilationFlag(),tmpContainer.GetStepLength(), tmpContainer.GetPrePositionToSplit()); return aContainer; } } } else{ - aContainer.SetSplittingParameters(this->fProcessNameToSplit,this->fEnergyToSplit,this->fMomentumToSplit,this->fPositionToSplit,this->fPolarizationToSplit,this->fParticleNameToSplit,this->fWeightToSplit, this->fTrackStatusToSplit, this->fNumberOfSecondariesToSplit, this->fAnnihilProcessFlag,this->fStepLength,this->fPrePosition); + SimpleContainer tmpContainer = this->fContainerToSplit; + fContainerToSplit = SimpleContainer(tmpContainer.GetProcessNameToSplit(),tmpContainer.GetEnergy(),tmpContainer.GetMomentum(),tmpContainer.GetVertexPosition(),tmpContainer.GetPolarization(),tmpContainer.GetParticleNameToSplit(),tmpContainer.GetWeight(),tmpContainer.GetTrackStatus(),tmpContainer.GetNbOfSecondaries(),tmpContainer.GetAnnihilationFlag(),tmpContainer.GetStepLength(),tmpContainer.GetPrePositionToSplit()); return aContainer; } } @@ -272,22 +134,6 @@ LastVertexDataContainer ContainerFromParentInformation(G4Step* step){ - -void DumpInfoToSplit(){ - std::cout<<"Particle name of the particle to split: "< fVectorOfMomentumToSplit; -std::vector fVectorOfPositionToSplit; -std::vector fVectorOfPolarizationToSplit; -std::vector fVectorOfEnergyToSplit; -std::vector fVectorOfProcessToSplit; -std::vector fVectorOfParticleNameToSplit; -std::vector fVectorOfWeightToSplit; -std::vectorfVectorOfTrackStatusToSplit; -std::vectorfVectorOfNumberOfSecondariesToSplit; -std::vectorfVectorOfAnnihilProcessFlag; -std::vectorfVectorOfStepLength; -std::vectorfVectorOfPrePosition; - - +SimpleContainer fContainerToSplit; +std::vector fVectorOfContainerToSplit; }; diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h index 40b3fdbb4..241eeab57 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h @@ -63,6 +63,14 @@ virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & return particleChange; } +virtual G4VParticleChange * AlongStepDoIt (const G4Track & track, const G4Step & step) override +{ + isIonisation = true; + G4VParticleChange* particleChange = G4VEnergyLossProcess::AlongStepDoIt(track,step); + return particleChange; +} + + }; diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h new file mode 100644 index 000000000..e3069bf2c --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h @@ -0,0 +1,217 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +/// +#ifndef SimpleContainer_h +#define SimpleContainer_h + + +#include +#include "G4VEnergyLossProcess.hh" +#include "G4Track.hh" +#include "G4VEmProcess.hh" +#include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "G4PhysicalConstants.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4Gamma.hh" +#include "G4Electron.hh" +#include "G4Positron.hh" +#include "G4eeToTwoGammaModel.hh" +#include "G4EmBiasingManager.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilationEntanglementClipBoard.hh" +#include "G4EmParameters.hh" +#include "G4PhysicsModelCatalog.hh" + + +class SimpleContainer{ + +public : +SimpleContainer(G4String processName,G4double energy,G4ThreeVector momentum, G4ThreeVector position,G4ThreeVector polarization,G4String name,G4double weight,G4int trackStatus,G4int nbSec,G4String flag,G4double length, G4ThreeVector prePos){ + + fProcessNameToSplit = processName; + fEnergyToSplit = energy; + fMomentumToSplit = momentum; + fPositionToSplit = position; + fPolarizationToSplit = polarization; + fParticleNameToSplit = name; + fWeightToSplit = weight; + fTrackStatusToSplit = trackStatus; + fNumberOfSecondariesToSplit = nbSec; + fAnnihilProcessFlag = flag; + fStepLength = length; + fPrePosition = prePos; + +} + + +SimpleContainer(){} + +~SimpleContainer(){} + +void SetProcessNameToSplit(G4String processName){ + fProcessNameToSplit = processName; +} + +G4String GetProcessNameToSplit(){ + return fProcessNameToSplit; +} + +void SetEnergy(G4double energy){ + fEnergyToSplit =energy; +} + +G4double GetEnergy(){ + return fEnergyToSplit; +} + + +void SetWeight(G4double weight){ + fWeightToSplit =weight; +} + +G4double GetWeight(){ + return fWeightToSplit; +} + +void SetPolarization(G4ThreeVector polarization){ + fPolarizationToSplit = polarization; +} + +G4ThreeVector GetPolarization(){ + return fPolarizationToSplit; +} + +void SetMomentum(G4ThreeVector momentum){ + fMomentumToSplit =momentum; +} + +G4ThreeVector GetMomentum(){ + return fMomentumToSplit; +} + +void SetVertexPosition(G4ThreeVector position){ + fPositionToSplit = position; +} + +G4ThreeVector GetVertexPosition(){ + return fPositionToSplit; +} + +void SetParticleNameToSplit(G4String name){ + fParticleNameToSplit = name; +} + +G4String GetParticleNameToSplit(){ + return fParticleNameToSplit; +} + + +void SetTrackStatus(G4int trackStatus){ + fTrackStatusToSplit = trackStatus; +} + + +G4int GetTrackStatus(){ + return fTrackStatusToSplit; +} + + +void SetNbOfSecondaries(G4int nbSec){ + fNumberOfSecondariesToSplit = nbSec; +} + +G4int GetNbOfSecondaries(){ + return fNumberOfSecondariesToSplit; +} + +void SetAnnihilationFlag(G4String flag){ + fAnnihilProcessFlag = flag; +} + +G4String GetAnnihilationFlag(){ + return fAnnihilProcessFlag; +} + +void SetStepLength(G4double length){ + fStepLength = length; +} + +G4double GetStepLength(){ + return fStepLength; +} + + + +void SetPrePositionToSplit(G4ThreeVector prePos){ + fPrePosition = prePos; +} + +G4ThreeVector GetPrePositionToSplit(){ + return fPrePosition; +} + + + + +void DumpInfoToSplit(){ + std::cout<<"Particle name of the particle to split: "< Date: Thu, 17 Oct 2024 12:13:36 +0200 Subject: [PATCH 34/82] Bug correction and time optimization of splitting process --- ...ateLastVertexInteractionSplittingActor.cpp | 484 ++++++++++-------- .../GateLastVertexInteractionSplittingActor.h | 24 +- .../GateLastVertexSplittingDataContainer.h | 15 +- 3 files changed, 291 insertions(+), 232 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 22cd2800b..774394c86 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -88,7 +88,6 @@ void GateLastVertexInteractionSplittingActor::print_tree(const treeFindParticle(container.GetParticleNameToSplit()); - G4ThreeVector momentum = container.GetMomentum(); - G4double energy = container.GetEnergy(); - if (energy <0){ - energy = 0; - momentum = {0,0,0}; - } - G4int trackStatus = container.GetTrackStatus(); - G4ThreeVector position = container.GetVertexPosition(); - G4ThreeVector polarization = container.GetPolarization(); - - G4DynamicParticle* dynamicParticle = new G4DynamicParticle(particleDefinition,momentum,energy); - G4double time = 0; - if (step->GetPreStepPoint() != 0) - time = step->GetPreStepPoint()->GetGlobalTime(); - G4Track* aTrack = new G4Track(dynamicParticle,time, position); - aTrack->SetPolarization(polarization); - if (trackStatus == 0){ - aTrack->SetTrackStatus(fAlive); - } - if (trackStatus == 1){ - aTrack->SetTrackStatus(fStopButAlive); - } - if ((trackStatus == 2) || (trackStatus == 3)){ - aTrack->SetTrackStatus(fAlive); + if (container.GetParticleNameToSplit() != "None"){ + G4ParticleDefinition *particleDefinition = particle_table->FindParticle(container.GetParticleNameToSplit()); + G4ThreeVector momentum = container.GetMomentum(); + G4double energy = container.GetEnergy(); + if (energy <0){ + energy = 0; + momentum = {0,0,0}; + } + G4int trackStatus = container.GetTrackStatus(); + G4ThreeVector position = container.GetVertexPosition(); + G4ThreeVector polarization = container.GetPolarization(); + G4DynamicParticle* dynamicParticle = new G4DynamicParticle(particleDefinition,momentum,energy); + G4double time = 0; + G4Track* aTrack = new G4Track(dynamicParticle,time, position); + aTrack->SetPolarization(polarization); + if (trackStatus == 0){ + aTrack->SetTrackStatus(fAlive); + } + if (trackStatus == 1){ + aTrack->SetTrackStatus(fStopButAlive); + } + if ((trackStatus == 2) || (trackStatus == 3)){ + aTrack->SetTrackStatus(fAlive); + } + aTrack->SetWeight(container.GetWeight()); + return aTrack; } - aTrack->SetWeight(container.GetWeight()); - return aTrack; + return nullptr; } @@ -150,6 +150,7 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleC newTrack->SetMomentumDirection(momentum); newTrack->SetPosition(position); newTrack->SetPolarization(polarization); + newTrack->SetWeight(weight); return newTrack; } @@ -158,41 +159,38 @@ void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; - G4Track* aTrack = CreateATrackFromContainer(container,initStep); - GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*aTrack, *initStep); + G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; - const G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); - //gammaWeight = aTrack->GetWeight()/ fSplittingFactor; + G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); + gammaWeight = fTrackToSplit->GetWeight()/ fSplittingFactor; - - G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *aTrack, gammaWeight); + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *fTrackToSplit, gammaWeight); - if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector,fMaxTheta) == false)) - delete newTrack; - else { + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector,fMaxTheta) == false)){ + delete newTrack; + ComptonSplitting(initStep,CurrentStep,process,container); + } + else{ trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) { - G4Track *newTrack = processFinalState->GetSecondary(j); - //newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } } - processFinalState->Clear(); - gammaProcessFinalState->Clear(); - delete aTrack; - - - + // Special case here, since we generate independently each particle, we will not attach an electron to exiting compton photon. + /* + G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + for (int j = 0; j < NbOfSecondaries; j++) { + G4Track *newTrack = processFinalState->GetSecondary(j); + newTrack->SetWeight(gammaWeight); + trackVector->push_back(newTrack); + } + */ + processFinalState->Clear(); + gammaProcessFinalState->Clear(); } - G4VProcess* GateLastVertexInteractionSplittingActor::GetProcessFromProcessName(G4String particleName, G4String pName){ auto *particle_table = G4ParticleTable::GetParticleTable(); G4ParticleDefinition *particleDefinition = particle_table->FindParticle(particleName); @@ -210,7 +208,7 @@ G4VProcess* GateLastVertexInteractionSplittingActor::GetProcessFromProcessName(G } -G4VParticleChange* GateLastVertexInteractionSplittingActor::eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process){ +G4Track GateLastVertexInteractionSplittingActor::eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process){ //It seem's that the the along step method apply only to brem results to no deposited energy but a change in momentum direction according to the process //Whereas the along step method applied to the ionisation well change the deposited energy but not the momentum. Then I apply both to have a correct //momentum and deposited energy before the brem effect. @@ -224,77 +222,85 @@ G4VParticleChange* GateLastVertexInteractionSplittingActor::eBremProcessFinalSta G4double LossEnergy = eIoniProcessAlongStateForLoss->GetLocalEnergyDeposit(); G4ThreeVector momentum = eBremProcessAlongStateForLoss->GetProposedMomentumDirection(); G4ThreeVector polarization = eBremProcessAlongStateForLoss->GetProposedPolarization(); + G4Track aTrack = G4Track(*track); - track->SetKineticEnergy(track->GetKineticEnergy() - LossEnergy); - track->SetMomentumDirection(momentum); - track->SetPolarization(polarization); - GateBremPostStepDoIt *bremProcess = (GateBremPostStepDoIt *)process; + aTrack.SetKineticEnergy(track->GetKineticEnergy() - LossEnergy); + aTrack.SetMomentumDirection(momentum); + aTrack.SetPolarization(polarization); eIoniProcessAlongState->Clear(); eBremProcessAlongState->Clear(); - - return bremProcess->GateBremPostStepDoIt::PostStepDoIt(*track, *step); + return aTrack; } void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer theContainer) { - - - G4Track* track = CreateATrackFromContainer(theContainer,initStep); SimpleContainer container = theContainer.GetContainerToSplit(); - G4String particleName = track->GetParticleDefinition()->GetParticleName(); + G4String particleName = fTrackToSplit->GetParticleDefinition()->GetParticleName(); G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4double gammaWeight = 0; G4VParticleChange *processFinalState = nullptr; - if (process->GetProcessName() == "eBrem") { - processFinalState = eBremProcessFinalState(track,initStep,process); - } - else { - GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - if ((container.GetAnnihilationFlag() == "PostStep") && (track->GetKineticEnergy() > 0)){ - processFinalState = emProcess->PostStepDoIt(*track, *initStep); + GateBremPostStepDoIt* bremProcess = nullptr; + GateGammaEmPostStepDoIt *emProcess = nullptr; + GateplusannihilAtRestDoIt *eplusAnnihilProcess = nullptr; + + if ((container.GetAnnihilationFlag() == "PostStep") && (fTrackToSplit->GetKineticEnergy() > 0)){ + emProcess = (GateGammaEmPostStepDoIt *)process; + } + if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { + eplusAnnihilProcess = (GateplusannihilAtRestDoIt *)process; + } + + + + + G4int NbOfSecondaries = 0; + while (NbOfSecondaries == 0){ + if (process->GetProcessName() == "eBrem") { + G4Track aTrack = eBremProcessFinalState(fTrackToSplit,initStep,process); + bremProcess = (GateBremPostStepDoIt*) process; + processFinalState = bremProcess->GateBremPostStepDoIt::PostStepDoIt(aTrack, *initStep); } - if ((container.GetAnnihilationFlag() == "AtRest") || (track->GetKineticEnergy() == 0)) { - GateplusannihilAtRestDoIt *eplusAnnihilProcess = (GateplusannihilAtRestDoIt *)process; - processFinalState = eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*track,*initStep); + else { + if ((container.GetAnnihilationFlag() == "PostStep") && (fTrackToSplit->GetKineticEnergy() > 0)){ + processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); + } + if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { + processFinalState = eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*fTrackToSplit,*initStep); + } + } + NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + if (NbOfSecondaries == 0){ + processFinalState->Clear(); } } - - - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); G4int idx = 0; - if (NbOfSecondaries >0) { - //gammaWeight = track->GetWeight()/fSplittingFactor; - G4bool alreadySplitted = false; - for (int i; i < NbOfSecondaries; i++){ - G4Track *newTrack = processFinalState->GetSecondary(i); - G4ThreeVector momentum = newTrack->GetMomentumDirection(); - if (!(isnan(momentum[0]))){ - if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector,fMaxTheta) == false)){ - delete newTrack; - } - else if (alreadySplitted == false){ - //newTrack->SetWeight(gammaWeight); - newTrack->SetCreatorProcess(process); - trackVector->push_back(newTrack); - alreadySplitted = true; - } - else{ - delete newTrack; - } + gammaWeight = fTrackToSplit->GetWeight()/fSplittingFactor; + G4bool IsPushBack =false; + for (int i; i < NbOfSecondaries; i++){ + G4Track *newTrack = processFinalState->GetSecondary(i); + G4ThreeVector momentum = newTrack->GetMomentumDirection(); + if (!(isnan(momentum[0]))){ + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector,fMaxTheta) == false)){ + delete newTrack; } else { - delete newTrack; + newTrack->SetWeight(gammaWeight); + newTrack->SetCreatorProcess(process); + trackVector->push_back(newTrack); + IsPushBack=true; + break; + } } + else { + delete newTrack; + } } - delete track; - - - - + if (IsPushBack == false) + SecondariesSplitting(initStep,CurrentStep,process,theContainer); processFinalState->Clear(); @@ -305,17 +311,21 @@ void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G // We retrieve the process associated to the process name to split and we // split according the process. Since for compton scattering, the gamma is not // a secondary particles, this one need to have his own splitting function. - SimpleContainer container = theContainer.GetContainerToSplit(); - G4VProcess* processToSplit = GetProcessFromProcessName(container.GetParticleNameToSplit(),container.GetProcessNameToSplit()); - G4String processName = container.GetProcessNameToSplit(); - if (processName == "compt") { - ComptonSplitting(initStep,step, processToSplit, theContainer); - } - else if((processName != "msc") && (processName != "conv")){ - SecondariesSplitting(initStep, step, processToSplit, theContainer); - } - + G4String processName = fProcessNameToSplit; + if ((fProcessToSplit == 0) || (fProcessToSplit == nullptr)){ + SimpleContainer container = theContainer.GetContainerToSplit(); + fProcessToSplit = GetProcessFromProcessName(container.GetParticleNameToSplit(),processName); + } + + if (processName == "compt") { + ComptonSplitting(initStep,step, fProcessToSplit, theContainer); + } + + else if((processName != "msc") && (processName != "conv")){ + SecondariesSplitting(initStep, step, fProcessToSplit, theContainer); + } + } @@ -352,28 +362,25 @@ void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ G4String annihilFlag = "None"; if (processName == "annihil"){ if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0){ - //std::cout<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTotalEnergyDeposit()<GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ + if (processName == step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ annihilFlag = "PostStep"; } else if (processName != step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ annihilFlag ="AtRest"; } - //std::cout<GetPreStepPoint()->GetKineticEnergy()<<" "<GetPostStepPoint()->GetKineticEnergy()<<" "<GetTotalEnergyDeposit()<GetTrack()->GetTrackID()); newContainer.SetParticleName(step->GetTrack()->GetDefinition()->GetParticleName()); newContainer.SetCreationProcessName(creatorProcessName); + + + if (fTree.empty()){ fTree.set_head(newContainer); } @@ -399,58 +406,65 @@ void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ } } } + - LastVertexDataContainer* container = &(*fIterator); - G4int trackID = container->GetTrackID(); - if ((processName != "Transportation") &&(processName !="None") && (processName !="Rayl")){ - if (step->GetTrack()->GetTrackID() == trackID){ - G4ThreeVector position = step->GetTrack()->GetPosition(); - G4ThreeVector prePosition = step->GetPreStepPoint()->GetPosition(); - G4ThreeVector momentum; - if ((processName == "annihil")) - momentum = step->GetPostStepPoint()->GetMomentumDirection(); - else{ - momentum = step->GetPreStepPoint()->GetMomentumDirection(); - } - G4ThreeVector polarization = step->GetPreStepPoint()->GetPolarization(); - G4String particleName = step->GetTrack()->GetDefinition()->GetParticleName(); - G4double energy = step->GetPreStepPoint()->GetKineticEnergy(); - G4double weight = step->GetTrack()->GetWeight(); - G4int trackStatus = step->GetTrack()->GetTrackStatus(); - G4int nbOfSecondaries = step->GetfSecondary()->size(); - G4double stepLength = step->GetStepLength(); - if (((processName == "annihil"))){ - energy -= (step->GetTotalEnergyDeposit()); - } - SimpleContainer containerToSplit = SimpleContainer(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); - container->SetContainerToSplit(containerToSplit); - container->PushListOfSplittingParameters(); - + + LastVertexDataContainer* container = &(*fIterator); + G4int trackID = container->GetTrackID(); + if ((processName != "Transportation") &&(processName !="None") && (processName !="Rayl")){ + if (step->GetTrack()->GetTrackID() == trackID){ + G4ThreeVector position = step->GetTrack()->GetPosition(); + G4ThreeVector prePosition = step->GetPreStepPoint()->GetPosition(); + G4ThreeVector momentum; + if ((processName == "annihil")) + momentum = step->GetPostStepPoint()->GetMomentumDirection(); + else{ + momentum = step->GetPreStepPoint()->GetMomentumDirection(); } + G4ThreeVector polarization = step->GetPreStepPoint()->GetPolarization(); + G4String particleName = step->GetTrack()->GetDefinition()->GetParticleName(); + G4double energy = step->GetPreStepPoint()->GetKineticEnergy(); + G4double weight = step->GetTrack()->GetWeight(); + G4int trackStatus = step->GetTrack()->GetTrackStatus(); + G4int nbOfSecondaries = step->GetfSecondary()->size(); + G4double stepLength = step->GetStepLength(); + if (((processName == "annihil"))){ + energy -= (step->GetTotalEnergyDeposit()); + } + SimpleContainer containerToSplit = SimpleContainer(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); + //std::cout<<"during saving " <SetContainerToSplit(containerToSplit); + container->PushListOfSplittingParameters(containerToSplit); + + //std::cout<GetContainerToSplit().GetProcessNameToSplit()<GetPreStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - - if (logicalVolumeNamePreStep != logicalVolumeNamePostStep){ + + if ((step->GetPostStepPoint()->GetStepStatus() == 1)) { + G4String logicalVolumeNamePostStep = "None"; + if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()){ return true; } - /* - else if (std::find(fListOfBiasedVolume.begin(), fListOfBiasedVolume.end(), logicalVolumeNamePostStep) != fListOfBiasedVolume.end()) { - return false; + /* + else if (std::find(fListOfBiasedVolume.begin(), fListOfBiasedVolume.end(), logicalVolumeNamePostStep) != fListOfBiasedVolume.end()) { + return false; + } + */ } - */ - } + if (step->GetPostStepPoint()->GetStepStatus() == 0) + return true; return false; } @@ -468,6 +482,14 @@ G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesAProcess(G return false; +} + +G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesALossEnergyProcess(G4Step* step){ + if (step->GetPostStepPoint()->GetKineticEnergy() - step->GetPreStepPoint()->GetKineticEnergy() != 0) + return true; + return false; + + } @@ -503,19 +525,34 @@ void GateLastVertexInteractionSplittingActor::BeginOfEventAction( fEventID = event->GetEventID(); fEventIDOfSplittedTrack = -1; fTrackIDOfSplittedTrack = -1; - fNotSplitted == true; fIsAnnihilAlreadySplit = false; if (fEventID%50000 == 0) - std::cout<FindSourceByName(fActiveSource); + GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; + fContainer = vertexSource->GetLastVertexContainer(); + fProcessNameToSplit = vertexSource->GetProcessToSplit(); + if (fProcessToSplit !=0){ + fProcessToSplit = nullptr; + } + if (fTrackToSplit !=0){ + delete fTrackToSplit; + fTrackToSplit = nullptr; + } + fTrackToSplit = CreateATrackFromContainer(fContainer); + } + + } void GateLastVertexInteractionSplittingActor::PreUserTrackingAction( const G4Track *track) { - + fToSplit =true; fIsFirstStep = true; } @@ -523,99 +560,104 @@ void GateLastVertexInteractionSplittingActor::PreUserTrackingAction( void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { - G4String logicalVolumeNamePreStep = "None"; - G4String logicalVolumeNamePostStep = "None"; - - if (step->GetPreStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - - G4String particleName =step->GetTrack()->GetParticleDefinition()->GetParticleName(); - G4String creatorProcessName = "None"; - G4String processName = "None"; - - if (step->GetTrack()->GetCreatorProcess() != 0) - creatorProcessName =step->GetTrack()->GetCreatorProcess()->GetProcessName(); - - if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) - processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); - - if (fActiveSource != "source_vertex"){ FillOfDataTree(step); + if (IsParticleExitTheBiasedVolume(step)){ if ((fAngularKill == false) || ((fAngularKill == true) && (DoesParticleEmittedInSolidAngle(step->GetTrack()->GetMomentumDirection(),fVectorDirector,fMaxTheta) == true))){ fListOfContainer.push_back((*fIterator)); } + step->GetTrack()->SetTrackStatus(fStopAndKill); } + + } + if (fOnlyTree == false){ if (fActiveSource == "source_vertex"){ - - auto* source = fSourceManager->FindSourceByName(fActiveSource); - GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; - LastVertexDataContainer container = vertexSource->GetLastVertexContainer(); - if ((step->GetTrack()->GetWeight() == container.GetContainerToSplit().GetWeight())){ - G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentumDirection(); - G4String processToSplit = vertexSource->GetProcessToSplit(); + + if (fIsFirstStep){ + fTrackID = step->GetTrack()->GetTrackID(); + fEkin = step->GetPostStepPoint()->GetKineticEnergy(); + } + else{ + if ((fTrackID == step->GetTrack()->GetTrackID()) && (fEkin != step->GetPreStepPoint()->GetKineticEnergy())){ + fToSplit =false; + } + else{ + fEkin = step->GetPostStepPoint()->GetKineticEnergy(); + } + + } + if (fToSplit) { + G4String creatorProcessName = "None"; + if (step->GetTrack()->GetCreatorProcess() != 0) + creatorProcessName =step->GetTrack()->GetCreatorProcess()->GetProcessName(); if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ - if ((processToSplit != "annihil") || ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ + if ((fProcessNameToSplit != "annihil") || ((fProcessNameToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ step->GetfSecondary()->clear(); //FIXME : list of process which are not splitable yet - if ((processToSplit != "msc") && (processToSplit != "conv") && (processToSplit != "eIoni")) { + if ((fProcessNameToSplit != "msc") && (fProcessNameToSplit != "conv") && (fProcessNameToSplit != "eIoni")) { fCopyInitStep= new G4Step(*step); - if (processToSplit == "eBrem"){ - fCopyInitStep->SetStepLength(container.GetContainerToSplit().GetStepLength()); - fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(container.GetContainerToSplit().GetEnergy()); - - } - while (step->GetfSecondary()->size() != 1){ - step->GetfSecondary()->clear(); - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); + if (fProcessNameToSplit == "eBrem"){ + fCopyInitStep->SetStepLength(fContainer.GetContainerToSplit().GetStepLength()); + fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(fContainer.GetContainerToSplit().GetEnergy()); } + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer); } step->GetTrack()->SetTrackStatus(fStopAndKill); - if (processToSplit == "annihil"){ + if (fProcessNameToSplit == "annihil"){ fIsAnnihilAlreadySplit = true; } } - else if ((processToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ + else if ((fProcessNameToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ step->GetfSecondary()->clear(); step->GetTrack()->SetTrackStatus(fStopAndKill); } } - - else if (IsTheParticleUndergoesAProcess(step)){ - step->GetfSecondary()->clear(); - while (step->GetfSecondary()->size() != 1){ - step->GetfSecondary()->clear(); - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - } - step->GetTrack()->SetTrackStatus(fStopAndKill); - + else { + + if (fIsFirstStep){ + if (fKilledBecauseOfProcess == false){ + fSplitCounter += 1; + } + else { + fKilledBecauseOfProcess = false; + } + + if (fSplitCounter < fSplittingFactor){ + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer); + } + } + + if (IsTheParticleUndergoesALossEnergyProcess(step)){ + //std::cout<<"kill because process"<GetfSecondary()->clear(); + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer); + step->GetTrack()->SetTrackStatus(fStopAndKill); + fKilledBecauseOfProcess = true; + } + + + } - + /* else if (IsParticleExitTheBiasedVolume(step)){ fSplitCounter += 1; if (fSplitCounter < fSplittingFactor){ - while (step->GetfSecondary()->size() != 1){ - step->GetfSecondary()->clear(); - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - - } + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); } else{ delete fCopyInitStep; @@ -630,8 +672,10 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { step->GetPostStepPoint()->SetWeight(container.GetContainerToSplit().GetWeight()/fSplittingFactor); else step->GetPostStepPoint()->SetWeight(container.GetContainerToSplit().GetWeight()*0.99999999); + } - } + */ + } } } @@ -644,6 +688,11 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { void GateLastVertexInteractionSplittingActor::PostUserTrackingAction( const G4Track *track) { + if ((fSplitCounter == fSplittingFactor) && (fKilledBecauseOfProcess == false)){ + delete fCopyInitStep; + fCopyInitStep = nullptr; + fSplitCounter = 0; + } } @@ -653,10 +702,13 @@ void GateLastVertexInteractionSplittingActor::EndOfEventAction( if (fActiveSource != "source_vertex"){ - //print_tree(fTree,fTree.begin(),fTree.end()); + + //print_tree(fTree,fTree.begin(),fTree.end()); fVertexSource->SetNumberOfEventToSimulate(fListOfContainer.size()); fVertexSource->SetNumberOfGeneratedEvent(0); fVertexSource->SetListOfVertexToSimulate(fListOfContainer); + + //fDataMap.clear(); fTree.clear(); fListOfContainer.clear(); } diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index 4957e4f90..bdb5092b1 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -59,34 +59,37 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4int fEventID; G4int fEventIDOfSplittedTrack; G4int fEventIDOfInitialSplittedTrack; - G4int fTrackIDOfInitialTrack; + G4int fTrackID; + G4double fEkin; G4int fTrackIDOfInitialSplittedTrack = 0; G4int ftmpTrackID; G4bool fIsFirstStep = true; G4bool fSuspendForAnnihil = false; G4double fWeightOfEnteringParticle = 0; G4double fSplitCounter = 0; - G4bool fNotSplitted = true; + G4bool fToSplit = true; G4String fActiveSource = "None"; G4bool fIsAnnihilAlreadySplit =false; G4int fCounter; - G4bool fOnlyTree = true; + G4bool fKilledBecauseOfProcess = false; + G4bool fFirstSplittedPart = true; + G4bool fOnlyTree = false; GateLastVertexSource* fVertexSource = nullptr; tree fTree; tree::post_order_iterator fIterator; std::vector fListOfContainer; + G4Track *fTrackToSplit = nullptr; G4Step* fCopyInitStep = nullptr; - G4String fProcessToSplit = "None"; + G4String fProcessNameToSplit; + G4VProcess* fProcessToSplit; + LastVertexDataContainer fContainer; std::vector fTracksToPostpone; - - std::map fRememberedTracks; - std::map> fRememberedSteps; - std::map> fRememberedProcesses; std::map> fListOfProcessesAccordingParticles; + std::map fDataMap; @@ -111,10 +114,11 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { void SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); void CreateNewParticleAtTheLastVertex(G4Step*init,G4Step *current, LastVertexDataContainer); - G4Track* CreateATrackFromContainer(LastVertexDataContainer container, G4Step *step ); + G4Track* CreateATrackFromContainer(LastVertexDataContainer container); G4bool IsTheParticleUndergoesAProcess(G4Step* step); + G4bool IsTheParticleUndergoesALossEnergyProcess(G4Step* step); G4VProcess* GetProcessFromProcessName(G4String particleName, G4String pName); - G4VParticleChange* eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process); + G4Track eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process); void FillOfDataTree(G4Step *step); diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h index 0d63bd80a..474d6cc84 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h @@ -97,8 +97,8 @@ SimpleContainer GetContainerToSplit(){ -void PushListOfSplittingParameters(){ - fVectorOfContainerToSplit.emplace_back(fContainerToSplit); +void PushListOfSplittingParameters(SimpleContainer container){ + fVectorOfContainerToSplit.emplace_back(container); } @@ -115,17 +115,20 @@ LastVertexDataContainer ContainerFromParentInformation(G4Step* step){ for (int i =0;ifVectorOfContainerToSplit.size();i++){ if (vertexPosition == this->fVectorOfContainerToSplit[i].GetVertexPosition()){ SimpleContainer tmpContainer = this->fVectorOfContainerToSplit[i]; - fContainerToSplit = SimpleContainer(tmpContainer.GetProcessNameToSplit(),tmpContainer.GetEnergy(),tmpContainer.GetMomentum(),tmpContainer.GetVertexPosition(),tmpContainer.GetPolarization(),tmpContainer.GetParticleNameToSplit(),tmpContainer.GetWeight(),tmpContainer.GetTrackStatus(),tmpContainer.GetNbOfSecondaries(),tmpContainer.GetAnnihilationFlag(),tmpContainer.GetStepLength(), tmpContainer.GetPrePositionToSplit()); + //std::cout<<"1 "<fContainerToSplit; - fContainerToSplit = SimpleContainer(tmpContainer.GetProcessNameToSplit(),tmpContainer.GetEnergy(),tmpContainer.GetMomentum(),tmpContainer.GetVertexPosition(),tmpContainer.GetPolarization(),tmpContainer.GetParticleNameToSplit(),tmpContainer.GetWeight(),tmpContainer.GetTrackStatus(),tmpContainer.GetNbOfSecondaries(),tmpContainer.GetAnnihilationFlag(),tmpContainer.GetStepLength(),tmpContainer.GetPrePositionToSplit()); + //std::cout<<"2 "< Date: Thu, 17 Oct 2024 12:44:13 +0200 Subject: [PATCH 35/82] speed up of Russian roulette calculation --- ...ateLastVertexInteractionSplittingActor.cpp | 63 +++++++++---------- .../GateLastVertexInteractionSplittingActor.h | 4 +- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 774394c86..f31cf11e6 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -88,14 +88,28 @@ void GateLastVertexInteractionSplittingActor::print_tree(const tree fMaxTheta) + if (cosTheta < fCosMaxTheta) return false; return true; } +G4VProcess* GateLastVertexInteractionSplittingActor::GetProcessFromProcessName(G4String particleName, G4String pName){ + auto *particle_table = G4ParticleTable::GetParticleTable(); + G4ParticleDefinition *particleDefinition = particle_table->FindParticle(particleName); + G4ProcessManager *processManager = particleDefinition->GetProcessManager(); + G4ProcessVector *processList = processManager->GetProcessList(); + G4VProcess* nullProcess = nullptr; + for (size_t i = 0; i < processList->size(); ++i) { + auto process = (*processList)[i]; + if (process->GetProcessName() == pName) { + return process; + } + } + return nullProcess; + +} G4Track* GateLastVertexInteractionSplittingActor::CreateATrackFromContainer(LastVertexDataContainer theContainer){ @@ -139,13 +153,11 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleC G4double energy = gammaProcess->GetProposedKineticEnergy(); G4double globalTime = track.GetGlobalTime(); - //G4double newGammaWeight = weight; G4ThreeVector polarization = gammaProcess->GetProposedPolarization(); const G4ThreeVector momentum = gammaProcess->GetProposedMomentumDirection(); const G4ThreeVector position = track.GetPosition(); G4Track *newTrack = new G4Track(track); - //newTrack->SetWeight(newGammaWeight); newTrack->SetKineticEnergy(energy); newTrack->SetMomentumDirection(momentum); newTrack->SetPosition(position); @@ -157,23 +169,20 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleC void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process, LastVertexDataContainer container) { G4TrackVector *trackVector = CurrentStep->GetfSecondary(); - G4double gammaWeight = 0; - GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); - gammaWeight = fTrackToSplit->GetWeight()/ fSplittingFactor; - G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *fTrackToSplit, gammaWeight); + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *fTrackToSplit, fWeight); - if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector,fMaxTheta) == false)){ + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector) == false)){ delete newTrack; ComptonSplitting(initStep,CurrentStep,process,container); } else{ - trackVector->push_back(newTrack); + trackVector->emplace_back(newTrack); } @@ -191,21 +200,6 @@ void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, } -G4VProcess* GateLastVertexInteractionSplittingActor::GetProcessFromProcessName(G4String particleName, G4String pName){ - auto *particle_table = G4ParticleTable::GetParticleTable(); - G4ParticleDefinition *particleDefinition = particle_table->FindParticle(particleName); - G4ProcessManager *processManager = particleDefinition->GetProcessManager(); - G4ProcessVector *processList = processManager->GetProcessList(); - G4VProcess* nullProcess = nullptr; - for (size_t i = 0; i < processList->size(); ++i) { - auto process = (*processList)[i]; - if (process->GetProcessName() == pName) { - return process; - } - } - return nullProcess; - -} G4Track GateLastVertexInteractionSplittingActor::eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process){ @@ -238,7 +232,6 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS SimpleContainer container = theContainer.GetContainerToSplit(); G4String particleName = fTrackToSplit->GetParticleDefinition()->GetParticleName(); G4TrackVector *trackVector = CurrentStep->GetfSecondary(); - G4double gammaWeight = 0; G4VParticleChange *processFinalState = nullptr; GateBremPostStepDoIt* bremProcess = nullptr; GateGammaEmPostStepDoIt *emProcess = nullptr; @@ -276,19 +269,18 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS } G4int idx = 0; - gammaWeight = fTrackToSplit->GetWeight()/fSplittingFactor; G4bool IsPushBack =false; for (int i; i < NbOfSecondaries; i++){ G4Track *newTrack = processFinalState->GetSecondary(i); G4ThreeVector momentum = newTrack->GetMomentumDirection(); if (!(isnan(momentum[0]))){ - if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector,fMaxTheta) == false)){ + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector) == false)){ delete newTrack; } else { - newTrack->SetWeight(gammaWeight); + newTrack->SetWeight(fWeight); newTrack->SetCreatorProcess(process); - trackVector->push_back(newTrack); + trackVector->emplace_back(newTrack); IsPushBack=true; break; @@ -505,6 +497,9 @@ void GateLastVertexInteractionSplittingActor::StartSimulationAction (){ auto* source = fSourceManager->FindSourceByName("source_vertex"); fVertexSource = (GateLastVertexSource* ) source; + + fCosMaxTheta = std::cos(fMaxTheta); + } @@ -543,6 +538,8 @@ void GateLastVertexInteractionSplittingActor::BeginOfEventAction( fTrackToSplit = nullptr; } fTrackToSplit = CreateATrackFromContainer(fContainer); + if (fTrackToSplit != 0) + fWeight = fTrackToSplit->GetWeight()/fSplittingFactor; } @@ -564,7 +561,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { FillOfDataTree(step); if (IsParticleExitTheBiasedVolume(step)){ - if ((fAngularKill == false) || ((fAngularKill == true) && (DoesParticleEmittedInSolidAngle(step->GetTrack()->GetMomentumDirection(),fVectorDirector,fMaxTheta) == true))){ + if ((fAngularKill == false) || ((fAngularKill == true) && (DoesParticleEmittedInSolidAngle(step->GetTrack()->GetMomentumDirection(),fVectorDirector) == true))){ fListOfContainer.push_back((*fIterator)); } @@ -603,7 +600,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { step->GetfSecondary()->clear(); //FIXME : list of process which are not splitable yet if ((fProcessNameToSplit != "msc") && (fProcessNameToSplit != "conv") && (fProcessNameToSplit != "eIoni")) { - fCopyInitStep= new G4Step(*step); + fCopyInitStep = new G4Step(*step); if (fProcessNameToSplit == "eBrem"){ fCopyInitStep->SetStepLength(fContainer.GetContainerToSplit().GetStepLength()); fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(fContainer.GetContainerToSplit().GetEnergy()); diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index bdb5092b1..7ad080cfd 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -54,6 +54,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4bool fRotationVectorDirector; G4ThreeVector fVectorDirector; G4double fMaxTheta; + G4double fCosMaxTheta; G4int fTrackIDOfSplittedTrack = 0; G4int fParentID = -1; G4int fEventID; @@ -74,6 +75,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4bool fKilledBecauseOfProcess = false; G4bool fFirstSplittedPart = true; G4bool fOnlyTree = false; + G4double fWeight; GateLastVertexSource* fVertexSource = nullptr; tree fTree; tree::post_order_iterator fIterator; @@ -108,7 +110,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { virtual void PostUserTrackingAction(const G4Track *track) override; // Pure splitting functions - G4bool DoesParticleEmittedInSolidAngle(G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta); + G4bool DoesParticleEmittedInSolidAngle(G4ThreeVector dir, G4ThreeVector vectorDirector); G4Track *CreateComptonTrack(G4ParticleChangeForGamma *, G4Track, G4double); void ComptonSplitting(G4Step* initStep,G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); void SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); From 102b6242d8eda2b73404b2a0733635c513f5a5ff Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 23 Oct 2024 17:49:24 +0200 Subject: [PATCH 36/82] Dev of a batching method to create splitted tracks --- ...ateLastVertexInteractionSplittingActor.cpp | 238 ++++++++---------- .../GateLastVertexInteractionSplittingActor.h | 13 +- opengate/actors/miscactors.py | 2 +- 3 files changed, 116 insertions(+), 137 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index f31cf11e6..eb53adad8 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -62,6 +62,7 @@ GateLastVertexInteractionSplittingActor:: fAngularKill = DictGetBool(user_info, "angular_kill"); fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); fMaxTheta = DictGetDouble(user_info, "max_theta"); + fBatchSize = DictGetDouble(user_info, "batch_size"); fActions.insert("StartSimulationAction"); fActions.insert("SteppingAction"); fActions.insert("BeginOfEventAction"); @@ -166,37 +167,37 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleC return newTrack; } -void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process, LastVertexDataContainer container) { +void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process, LastVertexDataContainer container, G4double batchSize) { - G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + //G4TrackVector *trackVector = CurrentStep->GetfSecondary(); GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); - G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; + for (int i = 0; i < batchSize; i++){ + G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); + G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; - G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); + G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); - G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *fTrackToSplit, fWeight); + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *fTrackToSplit, fWeight); - if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector) == false)){ - delete newTrack; - ComptonSplitting(initStep,CurrentStep,process,container); - } - else{ - trackVector->emplace_back(newTrack); - } + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector) == false)){ + delete newTrack; + } + else{ + fStackManager->PushOneTrack(newTrack); + } + + + // Special case here, since we generate independently each particle, we will not attach an electron to exiting compton photon, but we will the secondaries. + + if (processFinalState->GetNumberOfSecondaries()> 0){ + delete processFinalState->GetSecondary(0); + } - // Special case here, since we generate independently each particle, we will not attach an electron to exiting compton photon. - /* - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) { - G4Track *newTrack = processFinalState->GetSecondary(j); - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); + + processFinalState->Clear(); + gammaProcessFinalState->Clear(); } - */ - processFinalState->Clear(); - gammaProcessFinalState->Clear(); } @@ -228,10 +229,11 @@ G4Track GateLastVertexInteractionSplittingActor::eBremProcessFinalState(G4Track* } -void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer theContainer) { +void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer theContainer, G4double batchSize) { SimpleContainer container = theContainer.GetContainerToSplit(); G4String particleName = fTrackToSplit->GetParticleDefinition()->GetParticleName(); - G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + //G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + G4VParticleChange *processFinalState = nullptr; GateBremPostStepDoIt* bremProcess = nullptr; GateGammaEmPostStepDoIt *emProcess = nullptr; @@ -243,80 +245,86 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { eplusAnnihilProcess = (GateplusannihilAtRestDoIt *)process; } - - - - - G4int NbOfSecondaries = 0; - while (NbOfSecondaries == 0){ - if (process->GetProcessName() == "eBrem") { - G4Track aTrack = eBremProcessFinalState(fTrackToSplit,initStep,process); - bremProcess = (GateBremPostStepDoIt*) process; - processFinalState = bremProcess->GateBremPostStepDoIt::PostStepDoIt(aTrack, *initStep); - } - else { - if ((container.GetAnnihilationFlag() == "PostStep") && (fTrackToSplit->GetKineticEnergy() > 0)){ - processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); + for (int j = 0; j < batchSize;j++){ + G4int NbOfSecondaries = 0; + + while (NbOfSecondaries == 0){ + if (process->GetProcessName() == "eBrem") { + G4Track aTrack = eBremProcessFinalState(fTrackToSplit,initStep,process); + bremProcess = (GateBremPostStepDoIt*) process; + processFinalState = bremProcess->GateBremPostStepDoIt::PostStepDoIt(aTrack, *initStep); + } + else { + if ((container.GetAnnihilationFlag() == "PostStep") && (fTrackToSplit->GetKineticEnergy() > 0)){ + processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); + } + if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { + processFinalState = eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*fTrackToSplit,*initStep); + } } - if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { - processFinalState = eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*fTrackToSplit,*initStep); + NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + if (NbOfSecondaries == 0){ + processFinalState->Clear(); } } - NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - if (NbOfSecondaries == 0){ - processFinalState->Clear(); - } - } - G4int idx = 0; - G4bool IsPushBack =false; - for (int i; i < NbOfSecondaries; i++){ - G4Track *newTrack = processFinalState->GetSecondary(i); - G4ThreeVector momentum = newTrack->GetMomentumDirection(); - if (!(isnan(momentum[0]))){ - if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector) == false)){ - delete newTrack; + G4int idx = 0; + G4bool IsPushBack =false; + for (int i=0; i < NbOfSecondaries; i++){ + G4Track *newTrack = processFinalState->GetSecondary(i); + G4ThreeVector momentum = newTrack->GetMomentumDirection(); + + if (!(isnan(momentum[0]))){ + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector) == false)){ + delete newTrack; + } + else if (IsPushBack == true){ + delete newTrack; + } + else { + newTrack->SetWeight(fWeight); + newTrack->SetCreatorProcess(process); + //trackVector->emplace_back(newTrack); + fStackManager->PushOneTrack(newTrack); + //delete newTrack; + IsPushBack=true; + + } } else { - newTrack->SetWeight(fWeight); - newTrack->SetCreatorProcess(process); - trackVector->emplace_back(newTrack); - IsPushBack=true; - break; - + delete newTrack; } } - else { - delete newTrack; - } + processFinalState->Clear(); } - - if (IsPushBack == false) - SecondariesSplitting(initStep,CurrentStep,process,theContainer); - processFinalState->Clear(); - - } -void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* initStep,G4Step *step,LastVertexDataContainer theContainer) { +void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* initStep,G4Step *step,LastVertexDataContainer theContainer, G4double batchSize) { // We retrieve the process associated to the process name to split and we // split according the process. Since for compton scattering, the gamma is not // a secondary particles, this one need to have his own splitting function. G4String processName = fProcessNameToSplit; + G4int nbOfTrackAlreadyInStack = fStackManager->GetNTotalTrack(); if ((fProcessToSplit == 0) || (fProcessToSplit == nullptr)){ SimpleContainer container = theContainer.GetContainerToSplit(); fProcessToSplit = GetProcessFromProcessName(container.GetParticleNameToSplit(),processName); } if (processName == "compt") { - ComptonSplitting(initStep,step, fProcessToSplit, theContainer); + ComptonSplitting(initStep,step, fProcessToSplit, theContainer, batchSize); } else if((processName != "msc") && (processName != "conv")){ - SecondariesSplitting(initStep, step, fProcessToSplit, theContainer); + SecondariesSplitting(initStep, step, fProcessToSplit, theContainer,batchSize); } + fNumberOfTrackToSimulate = fStackManager->GetNTotalTrack() - nbOfTrackAlreadyInStack; + fNbOfBatchForExitingParticle ++; + if (fNbOfBatchForExitingParticle >500){ + fStackManager->clear(); + } + //stackManager->clear(); } @@ -424,11 +432,8 @@ void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ energy -= (step->GetTotalEnergyDeposit()); } SimpleContainer containerToSplit = SimpleContainer(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); - //std::cout<<"during saving " <SetContainerToSplit(containerToSplit); container->PushListOfSplittingParameters(containerToSplit); - - //std::cout<GetContainerToSplit().GetProcessNameToSplit()<GetStackManager(); } @@ -516,14 +523,18 @@ void GateLastVertexInteractionSplittingActor::BeginOfRunAction( void GateLastVertexInteractionSplittingActor::BeginOfEventAction( const G4Event *event) { - fParentID = -1; fEventID = event->GetEventID(); - fEventIDOfSplittedTrack = -1; - fTrackIDOfSplittedTrack = -1; fIsAnnihilAlreadySplit = false; + fNbOfBatchForExitingParticle = 0; if (fEventID%50000 == 0) std::cout<<"event ID : "<FindSourceByName(fActiveSource); @@ -543,8 +554,6 @@ void GateLastVertexInteractionSplittingActor::BeginOfEventAction( } - - } void GateLastVertexInteractionSplittingActor::PreUserTrackingAction( @@ -596,8 +605,7 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ if ((fProcessNameToSplit != "annihil") || ((fProcessNameToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ - - step->GetfSecondary()->clear(); + //FIXME : list of process which are not splitable yet if ((fProcessNameToSplit != "msc") && (fProcessNameToSplit != "conv") && (fProcessNameToSplit != "eIoni")) { fCopyInitStep = new G4Step(*step); @@ -606,9 +614,9 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(fContainer.GetContainerToSplit().GetEnergy()); } - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer); + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer, fBatchSize); } - step->GetTrack()->SetTrackStatus(fStopAndKill); + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); if (fProcessNameToSplit == "annihil"){ fIsAnnihilAlreadySplit = true; @@ -617,15 +625,14 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { else if ((fProcessNameToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ - step->GetfSecondary()->clear(); - step->GetTrack()->SetTrackStatus(fStopAndKill); + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); } } else { - if (fIsFirstStep){ + fNumberOfTrackToSimulate --; if (fKilledBecauseOfProcess == false){ fSplitCounter += 1; } @@ -633,45 +640,25 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { fKilledBecauseOfProcess = false; } - if (fSplitCounter < fSplittingFactor){ - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer); + if (fSplitCounter > fSplittingFactor){ + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + fStackManager->clear(); } } if (IsTheParticleUndergoesALossEnergyProcess(step)){ - //std::cout<<"kill because process"<GetfSecondary()->clear(); - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer); - step->GetTrack()->SetTrackStatus(fStopAndKill); + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); fKilledBecauseOfProcess = true; } - - - } - - /* - else if (IsParticleExitTheBiasedVolume(step)){ - fSplitCounter += 1; - if (fSplitCounter < fSplittingFactor){ - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,container); - } - else{ - delete fCopyInitStep; - fCopyInitStep = nullptr; - fSplitCounter = 0; + if (fIsFirstStep){ + if (fSplitCounter <= fSplittingFactor){ + if (fNumberOfTrackToSimulate == 0){ + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer,(fSplittingFactor - fSplitCounter +1)/fSplittingFactor * fBatchSize); + } + } } - - - //FIXME Debug case if splitting factor equal to 1, as It is used as a condition to enable the split - // I just set the weight to a very close value of the real one - if (fSplittingFactor != 1) - step->GetPostStepPoint()->SetWeight(container.GetContainerToSplit().GetWeight()/fSplittingFactor); - else - step->GetPostStepPoint()->SetWeight(container.GetContainerToSplit().GetWeight()*0.99999999); - - } - */ + } } } } @@ -683,29 +670,16 @@ void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { } -void GateLastVertexInteractionSplittingActor::PostUserTrackingAction( - const G4Track *track) { - if ((fSplitCounter == fSplittingFactor) && (fKilledBecauseOfProcess == false)){ - delete fCopyInitStep; - fCopyInitStep = nullptr; - fSplitCounter = 0; - } - - } - void GateLastVertexInteractionSplittingActor::EndOfEventAction( const G4Event* event) { - if (fActiveSource != "source_vertex"){ //print_tree(fTree,fTree.begin(),fTree.end()); fVertexSource->SetNumberOfEventToSimulate(fListOfContainer.size()); fVertexSource->SetNumberOfGeneratedEvent(0); fVertexSource->SetListOfVertexToSimulate(fListOfContainer); - - //fDataMap.clear(); fTree.clear(); fListOfContainer.clear(); } diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index 7ad080cfd..f290c08b8 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -40,6 +40,7 @@ #include #include "GateLastVertexSource.h" #include "CLHEP/Vector/ThreeVector.h" +#include "G4StackManager.hh" using CLHEP::Hep3Vector; namespace py = pybind11; @@ -76,10 +77,15 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4bool fFirstSplittedPart = true; G4bool fOnlyTree = false; G4double fWeight; + G4double fBatchSize; + G4int fNumberOfTrackToSimulate = 0; + G4int fNbOfBatchForExitingParticle=0; + G4int fTracksCounts=0; GateLastVertexSource* fVertexSource = nullptr; tree fTree; tree::post_order_iterator fIterator; std::vector fListOfContainer; + G4StackManager* fStackManager = nullptr; @@ -107,15 +113,14 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { virtual void EndOfEventAction(const G4Event *) override; virtual void BeginOfRunAction(const G4Run *run) override; virtual void PreUserTrackingAction(const G4Track *track) override; - virtual void PostUserTrackingAction(const G4Track *track) override; // Pure splitting functions G4bool DoesParticleEmittedInSolidAngle(G4ThreeVector dir, G4ThreeVector vectorDirector); G4Track *CreateComptonTrack(G4ParticleChangeForGamma *, G4Track, G4double); - void ComptonSplitting(G4Step* initStep,G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); - void SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container); + void ComptonSplitting(G4Step* initStep,G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container, G4double batchSize); + void SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container, G4double batchSize); - void CreateNewParticleAtTheLastVertex(G4Step*init,G4Step *current, LastVertexDataContainer); + void CreateNewParticleAtTheLastVertex(G4Step*init,G4Step *current, LastVertexDataContainer, G4double batchSize); G4Track* CreateATrackFromContainer(LastVertexDataContainer container); G4bool IsTheParticleUndergoesAProcess(G4Step* step); G4bool IsTheParticleUndergoesALossEnergyProcess(G4Step* step); diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 853b5e450..404d964f0 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -495,7 +495,7 @@ def set_default_user_info(user_info): user_info.vector_director = [0, 0, -1] user_info.max_theta = 90 * deg user_info.list_of_volume_name = [] - + user_info.batch_size = 1 def __init__(self, user_info): ActorBase.__init__(self, user_info) g4.GateLastVertexInteractionSplittingActor.__init__(self, user_info.__dict__) From 2dfaea550bca993691cb620acbf823faa6641c92 Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 29 Oct 2024 15:39:51 +0100 Subject: [PATCH 37/82] bug correction --- .../opengate_lib/GateLastVertexSplittingPostStepDoIt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h index 241eeab57..38a9dc2ea 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h @@ -65,7 +65,6 @@ virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & virtual G4VParticleChange * AlongStepDoIt (const G4Track & track, const G4Step & step) override { - isIonisation = true; G4VParticleChange* particleChange = G4VEnergyLossProcess::AlongStepDoIt(track,step); return particleChange; } From 21f148ac6321a06f36eea3315f6ba54b1a7979b1 Mon Sep 17 00:00:00 2001 From: majacquet Date: Mon, 4 Nov 2024 12:33:01 +0100 Subject: [PATCH 38/82] Add of a security break in case of infinite loop --- .../GateLastVertexInteractionSplittingActor.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index eb53adad8..0bf43bac9 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -248,6 +248,7 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS for (int j = 0; j < batchSize;j++){ G4int NbOfSecondaries = 0; + G4int count =0; while (NbOfSecondaries == 0){ if (process->GetProcessName() == "eBrem") { G4Track aTrack = eBremProcessFinalState(fTrackToSplit,initStep,process); @@ -266,6 +267,15 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS if (NbOfSecondaries == 0){ processFinalState->Clear(); } + count ++; + //Security break, in case of infinite loop + if (count > 10000){ + G4ExceptionDescription ed; + ed << " infinite loop detected during the track creation for the " <GetProcessName() <<" process"<AbortEvent(); + break; + } } G4int idx = 0; From 1ae349369eefd61fbe23b5090059aa4283eeb34f Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 8 Nov 2024 14:23:31 +0100 Subject: [PATCH 39/82] Add C++ code of the kill actor --- core/opengate_core/opengate_core.cpp | 3 + .../GateKillNonInteractingParticleActor.cpp | 74 +++++++++++++++++++ .../GateKillNonInteractingParticleActor.h | 40 ++++++++++ .../pyGateKillNonInteractingParticleActor.cpp | 22 ++++++ 4 files changed, 139 insertions(+) create mode 100644 core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h create mode 100644 core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index 760a93135..952ddbeb8 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -309,6 +309,8 @@ void init_GateARFTrainingDatasetActor(py::module &m); void init_GateKillActor(py::module &); +void init_GateKillNonInteractingParticleActor(py::module &); + void init_itk_image(py::module &); void init_GateImageNestedParameterisation(py::module &); @@ -586,6 +588,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GateARFActor(m); init_GateARFTrainingDatasetActor(m); init_GateKillActor(m); + init_GateKillNonInteractingParticleActor(m); init_GateDigiAttributeManager(m); init_GateVDigiAttribute(m); init_GateExceptionHandler(m); diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp new file mode 100644 index 000000000..0dc62d5a0 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp @@ -0,0 +1,74 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + ------------------------------------ -------------- */ + +#include "GateKillNonInteractingParticleActor.h" +#include "G4LogicalVolumeStore.hh" +#include "G4PhysicalVolumeStore.hh" +#include "G4ios.hh" +#include "GateHelpers.h" +#include "GateHelpersDict.h" + +GateKillNonInteractingParticleActor::GateKillNonInteractingParticleActor( + py::dict &user_info) + : GateVActor(user_info, false) { + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("PreUserTrackingAction"); +} + + +void GateKillNonInteractingParticleActor::StartSimulationAction() { + fNbOfKilledParticles = 0; +} + +void GateKillNonInteractingParticleActor::PreUserTrackingAction( + const G4Track *track) { + fIsFirstStep = true; + fKineticEnergyAtTheEntrance = 0; + ftrackIDAtTheEntrance = 0; + fPassedByTheMotherVolume = false; +} + +void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { + + G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance() + ->GetVolume(fMotherVolumeName) + ->GetName(); + G4String physicalVolumeNamePreStep = + step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); + if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != + logNameMotherVolume) && + (fIsFirstStep)) { + if ((fPassedByTheMotherVolume == false) && + (physicalVolumeNamePreStep == fMotherVolumeName) && + (step->GetPreStepPoint()->GetStepStatus() == 1)) { + fPassedByTheMotherVolume = true; + fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); + ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); + } + } + + G4String logicalVolumeNamePostStep = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + if ((fPassedByTheMotherVolume) && + (step->GetPostStepPoint()->GetStepStatus() == 1)) { + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), + logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { + if ((step->GetTrack()->GetTrackID() == ftrackIDAtTheEntrance) && + (step->GetPostStepPoint()->GetKineticEnergy() == + fKineticEnergyAtTheEntrance)) { + auto track = step->GetTrack(); + track->SetTrackStatus(fStopAndKill); + fNbOfKilledParticles++; + } + } + } + + fIsFirstStep = false; +} diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h new file mode 100644 index 000000000..b0e0066d1 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h @@ -0,0 +1,40 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#ifndef GateKillNonInteractingParticleActor_h +#define GateKillNonInteractingParticleActor_h + +#include "G4Cache.hh" +#include "GateVActor.h" +#include + +namespace py = pybind11; + +class GateKillNonInteractingParticleActor : public GateVActor { + +public: + // Constructor + GateKillNonInteractingParticleActor(py::dict &user_info); + + void StartSimulationAction() override; + + // Main function called every step in attached volume + void SteppingAction(G4Step *) override; + + void PreUserTrackingAction(const G4Track *) override; + + std::vector fParticlesTypeToKill; + G4bool fPassedByTheMotherVolume = false; + G4double fKineticEnergyAtTheEntrance = 0; + G4int ftrackIDAtTheEntrance = 0; + G4bool fIsFirstStep = true; + std::vector fListOfVolumeAncestor; + + long fNbOfKilledParticles{}; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp new file mode 100644 index 000000000..5de9fe12b --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp @@ -0,0 +1,22 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include + +namespace py = pybind11; + +#include "GateKillNonInteractingParticleActor.h" + +void init_GateKillNonInteractingParticleActor(py::module &m) { + py::class_, + GateVActor>(m, "GateKillNonInteractingParticleActor") + .def_readwrite( + "fListOfVolumeAncestor", + &GateKillNonInteractingParticleActor::fListOfVolumeAncestor) + .def(py::init()); +} From 051d05afe829596c10fd4419fa0b24ba9ef87f3e Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 8 Nov 2024 18:28:54 +0100 Subject: [PATCH 40/82] modify the python code to retrieve volumes_tree --- .../GateKillNonInteractingParticleActor.cpp | 23 ++++--------- opengate/actors/miscactors.py | 33 +++++++++++++++++++ opengate/managers.py | 5 +++ 3 files changed, 45 insertions(+), 16 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp index 0dc62d5a0..93eb2fa25 100644 --- a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp @@ -15,9 +15,6 @@ GateKillNonInteractingParticleActor::GateKillNonInteractingParticleActor( py::dict &user_info) : GateVActor(user_info, false) { - fActions.insert("StartSimulationAction"); - fActions.insert("SteppingAction"); - fActions.insert("PreUserTrackingAction"); } @@ -38,13 +35,11 @@ void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance() ->GetVolume(fMotherVolumeName) ->GetName(); - G4String physicalVolumeNamePreStep = - step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); - if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != - logNameMotherVolume) && - (fIsFirstStep)) { - if ((fPassedByTheMotherVolume == false) && - (physicalVolumeNamePreStep == fMotherVolumeName) && + G4String physicalVolumeNamePreStep = "None"; + if (step->GetPreStepPoint()->GetPhysicalVolume() !=0) + physicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); + if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logNameMotherVolume) && (fIsFirstStep)) { + if ((fPassedByTheMotherVolume == false) && (physicalVolumeNamePreStep == fMotherVolumeName) && (step->GetPreStepPoint()->GetStepStatus() == 1)) { fPassedByTheMotherVolume = true; fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); @@ -52,12 +47,8 @@ void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { } } - G4String logicalVolumeNamePostStep = step->GetPostStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - if ((fPassedByTheMotherVolume) && - (step->GetPostStepPoint()->GetStepStatus() == 1)) { + G4String logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if ((fPassedByTheMotherVolume) && (step->GetPostStepPoint()->GetStepStatus() == 1)) { if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { if ((step->GetTrack()->GetTrackID() == ftrackIDAtTheEntrance) && diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index f580f6abc..ffb1a5747 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -7,6 +7,8 @@ from ..serialization import dump_json from ..exception import warning from ..base import process_cls +from anytree import Node, RenderTree + def _setter_hook_stats_actor_output_filename(self, output_filename): @@ -298,6 +300,36 @@ def EndSimulationAction(self): self.number_of_killed_particles = self.GetNumberOfKilledParticles() +class KillNonInteractingParticleActor(ActorBase, g4.GateKillNonInteractingParticleActor): + + def __init__(self, *args, **kwargs): + ActorBase.__init__(self, *args, **kwargs) + self.__initcpp__() + self.list_of_volume_name = [] + + def __initcpp__(self): + g4.GateKillNonInteractingParticleActor.__init__(self, self.user_info) + self.AddActions( + {"StartSimulationAction","PreUserTrackingAction", "SteppingAction"} + ) + + def initialize(self): + ActorBase.initialize(self) + self.InitializeUserInput(self.user_info) + self.InitializeCpp() + volume_tree = self.simulation.volume_manager.get_volume_tree() + print(type(volume_tree)) + dico_of_volume_tree = {} + for pre, _, node in RenderTree(volume_tree): + dico_of_volume_tree[str(node.name)] = node + volume_name = self.user_info.attached_to + while volume_name != "world": + node = dico_of_volume_tree[volume_name] + volume_name = node.mother + self.list_of_volume_name.append(volume_name) + self.fListOfVolumeAncestor = self.list_of_volume_name + + def _setter_hook_particles(self, value): if isinstance(value, str): return [value] @@ -305,6 +337,7 @@ def _setter_hook_particles(self, value): return list(value) + class SplittingActorBase(ActorBase): # hints for IDE splitting_factor: int diff --git a/opengate/managers.py b/opengate/managers.py index a0de748be..2ab7ef5c3 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -73,6 +73,7 @@ from .actors.miscactors import ( SimulationStatisticsActor, KillActor, + KillNonInteractingParticleActor, SplittingActorBase, ComptSplittingActor, BremSplittingActor, @@ -107,6 +108,7 @@ "ARFTrainingDatasetActor": ARFTrainingDatasetActor, "SimulationStatisticsActor": SimulationStatisticsActor, "KillActor": KillActor, + "KillNonInteractingParticleActor":KillNonInteractingParticleActor, "BremSplittingActor": BremSplittingActor, "ComptSplittingActor": ComptSplittingActor, "DigitizerAdderActor": DigitizerAdderActor, @@ -1183,6 +1185,9 @@ def dump_volume_tree(self): # FIXME: pre should be used directly but cannot be encoded correctly in Windows s += len(pre) * " " + f"{node.name}\n" return s + + def get_volume_tree(self): + return self.volume_tree_root def print_volume_tree(self): print(self.dump_volume_tree()) From 03e7c401bc8c8355770963fca4b123ad6f4c2541 Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 8 Nov 2024 18:29:32 +0100 Subject: [PATCH 41/82] Add of a test to verify if killed or not --- .../test074_kill_non_interacting_particles.py | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 opengate/tests/src/test074_kill_non_interacting_particles.py diff --git a/opengate/tests/src/test074_kill_non_interacting_particles.py b/opengate/tests/src/test074_kill_non_interacting_particles.py new file mode 100644 index 000000000..b12a6f66d --- /dev/null +++ b/opengate/tests/src/test074_kill_non_interacting_particles.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +from opengate.tests import utility +from scipy.spatial.transform import Rotation +import numpy as np +from anytree import Node, RenderTree +import uproot + + +def test074_test(entry_data, exit_data_1, exit_data_2): + liste_ekin = [] + liste_evtID = [] + liste_trackID = [] + evt_ID_entry_data = entry_data["EventID"] + j = 0 + i = 0 + while i < len(evt_ID_entry_data): + if ( + j < len(exit_data_1["EventID"]) + and evt_ID_entry_data[i] == exit_data_1["EventID"][j] + ): + TID_entry = entry_data["TrackID"][i] + TID_exit = exit_data_1["TrackID"][j] + Ekin_entry = entry_data["KineticEnergy"][i] + Ekin_exit = exit_data_1["KineticEnergy"][j] + + if (TID_entry == TID_exit) and (Ekin_exit == Ekin_entry): + liste_ekin.append(exit_data_1["KineticEnergy"][j]) + liste_evtID.append(exit_data_1["EventID"][j]) + liste_trackID.append(exit_data_1["TrackID"][j]) + if (j < len(exit_data_1["EventID"]) - 1) and ( + exit_data_1["EventID"][j] == exit_data_1["EventID"][j + 1] + ): + i = i - 1 + j += 1 + i += 1 + liste_ekin = np.asarray(liste_ekin) + print("Number of tracks to kill =", len(liste_ekin)) + print( + "Number of killed tracks =", + (len(exit_data_1["EventID"]) - len(exit_data_2["EventID"])), + ) + + return len(liste_ekin) == ( + len(exit_data_1["EventID"]) - len(exit_data_2["EventID"]) + ) + + +if __name__ == "__main__": + paths = utility.get_default_test_paths(__file__) + output_path = paths.output + + + print(output_path) + # create the simulation + sim = gate.Simulation() + sim.output_dir = output_path + + # main options + ui = sim.user_info + ui.g4_verbose = False + # ui.visu = True + ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.logger.EVENT + ui.number_of_threads = 1 + ui.random_seed = "auto" + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + sec = gate.g4_units.s + gcm3 = gate.g4_units["g/cm3"] + + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + + # adapt world size + world = sim.world + world.size = [1 * m, 1 * m, 1 * m] + world.material = "G4_AIR" + + big_box = sim.add_volume("Box", "big_box") + big_box.mother = world.name + big_box.material = "G4_AIR" + big_box.size = [0.8 * m, 0.8 * m, 0.8 * m] + + actor_box = sim.add_volume("Box", "actor_box") + actor_box.mother = big_box.name + actor_box.material = "G4_AIR" + actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] + actor_box.translation = [0, 0, -0.1 * m] + + source = sim.add_source("GenericSource", "photon_source") + source.particle = "gamma" + source.position.type = "box" + source.mother = world.name + source.position.size = [6 * cm, 6 * cm, 6 * cm] + source.position.translation = [0, 0, 0.3 * m] + source.direction.type = "momentum" + source.direction_relative_to_attached_volume = True + # source1.direction.focus_point = [0*cm, 0*cm, -5 *cm] + source.direction.momentum = [0, 0, -1] + source.energy.type = "mono" + source.energy.mono = 6 * MeV + source.n = 5000 + + tungsten_leaves = sim.add_volume("Box", "tungsten_leaves") + tungsten_leaves.mother = actor_box + tungsten_leaves.size = [0.6 * m, 0.6 * m, 0.3 * cm] + tungsten_leaves.material = "Tungsten" + liste_translation_W = [] + for i in range(7): + liste_translation_W.append([0, 0, 0.25 * m - i * 6 * cm]) + tungsten_leaves.translation = liste_translation_W + tungsten_leaves.color = [0.9, 0.0, 0.4, 0.8] + + kill_No_int_act = sim.add_actor("KillNonInteractingParticleActor", "killact") + kill_No_int_act.attached_to = actor_box.name + + entry_phase_space = sim.add_volume("Box", "entry_phase_space") + entry_phase_space.mother = big_box + entry_phase_space.size = [0.8 * m, 0.8 * m, 1 * nm] + entry_phase_space.material = "G4_AIR" + entry_phase_space.translation = [0, 0, 0.21 * m] + entry_phase_space.color = [0.5, 0.9, 0.3, 1] + + exit_phase_space_1 = sim.add_volume("Box", "exit_phase_space_1") + exit_phase_space_1.mother = actor_box + exit_phase_space_1.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space_1.material = "G4_AIR" + exit_phase_space_1.translation = [0, 0, -0.3 * m + 1 * nm] + exit_phase_space_1.color = [0.5, 0.9, 0.3, 1] + + exit_phase_space_2 = sim.add_volume("Box", "exit_phase_space_2") + exit_phase_space_2.mother = world.name + exit_phase_space_2.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space_2.material = "G4_AIR" + exit_phase_space_2.translation = [0, 0, -0.4 * m - 1 * nm] + exit_phase_space_2.color = [0.5, 0.9, 0.3, 1] + + # print(sim.volume_manager.dump_volume_tree()) + liste_phase_space_name = [ + entry_phase_space.name, + exit_phase_space_1.name, + exit_phase_space_2.name, + ] + for name in liste_phase_space_name: + print(name) + phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_" + name) + phsp.attached_to = name + phsp.attributes = ["EventID", "TrackID", "KineticEnergy"] + name_phsp = "test074_" + name + ".root" + phsp.output_filename = name_phsp + + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + sim.physics_manager.enable_decay = False + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * mm + sim.physics_manager.global_production_cuts.positron = 1 * mm + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + + # go ! + sim.run() + print(s) + print(output_path) + entry_phsp = uproot.open( + str(output_path) + + "/test074_" + + liste_phase_space_name[0] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[0] + ) + exit_phase_space_1 = uproot.open( + str(output_path) + + "/test074_" + + liste_phase_space_name[1] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[1] + ) + exit_phase_space_2 = uproot.open( + str(output_path) + + "/test074_" + + liste_phase_space_name[2] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[2] + ) + + df_entry = entry_phsp.arrays() + df_exit_1 = exit_phase_space_1.arrays() + df_exit_2 = exit_phase_space_2.arrays() + + is_ok = test074_test(df_entry, df_exit_1, df_exit_2) + + utility.test_ok(is_ok) From 00abcd58c94ff7818168a5ba0cf3009fb959d401 Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 8 Nov 2024 18:31:59 +0100 Subject: [PATCH 42/82] test name modification --- opengate/actors/miscactors.py | 1 - .../test082_kill_non_interacting_particles.py | 212 ++++++++++++++++++ 2 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 opengate/tests/src/test082_kill_non_interacting_particles.py diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index ffb1a5747..db70a9f58 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -318,7 +318,6 @@ def initialize(self): self.InitializeUserInput(self.user_info) self.InitializeCpp() volume_tree = self.simulation.volume_manager.get_volume_tree() - print(type(volume_tree)) dico_of_volume_tree = {} for pre, _, node in RenderTree(volume_tree): dico_of_volume_tree[str(node.name)] = node diff --git a/opengate/tests/src/test082_kill_non_interacting_particles.py b/opengate/tests/src/test082_kill_non_interacting_particles.py new file mode 100644 index 000000000..c6fe7aba9 --- /dev/null +++ b/opengate/tests/src/test082_kill_non_interacting_particles.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +from opengate.tests import utility +from scipy.spatial.transform import Rotation +import numpy as np +from anytree import Node, RenderTree +import uproot + + +def test082_test(entry_data, exit_data_1, exit_data_2): + liste_ekin = [] + liste_evtID = [] + liste_trackID = [] + evt_ID_entry_data = entry_data["EventID"] + j = 0 + i = 0 + while i < len(evt_ID_entry_data): + if ( + j < len(exit_data_1["EventID"]) + and evt_ID_entry_data[i] == exit_data_1["EventID"][j] + ): + TID_entry = entry_data["TrackID"][i] + TID_exit = exit_data_1["TrackID"][j] + Ekin_entry = entry_data["KineticEnergy"][i] + Ekin_exit = exit_data_1["KineticEnergy"][j] + + if (TID_entry == TID_exit) and (Ekin_exit == Ekin_entry): + liste_ekin.append(exit_data_1["KineticEnergy"][j]) + liste_evtID.append(exit_data_1["EventID"][j]) + liste_trackID.append(exit_data_1["TrackID"][j]) + if (j < len(exit_data_1["EventID"]) - 1) and ( + exit_data_1["EventID"][j] == exit_data_1["EventID"][j + 1] + ): + i = i - 1 + j += 1 + i += 1 + liste_ekin = np.asarray(liste_ekin) + print("Number of tracks to kill =", len(liste_ekin)) + print( + "Number of killed tracks =", + (len(exit_data_1["EventID"]) - len(exit_data_2["EventID"])), + ) + + return len(liste_ekin) == ( + len(exit_data_1["EventID"]) - len(exit_data_2["EventID"]) + ) + + +if __name__ == "__main__": + paths = utility.get_default_test_paths(__file__) + output_path = paths.output + + + print(output_path) + # create the simulation + sim = gate.Simulation() + sim.output_dir = output_path + + # main options + ui = sim.user_info + ui.g4_verbose = False + # ui.visu = True + ui.visu_type = "vrml" + ui.check_volumes_overlap = False + # ui.running_verbose_level = gate.logger.EVENT + ui.number_of_threads = 1 + ui.random_seed = "auto" + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + sec = gate.g4_units.s + gcm3 = gate.g4_units["g/cm3"] + + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + + # adapt world size + world = sim.world + world.size = [1 * m, 1 * m, 1 * m] + world.material = "G4_AIR" + + big_box = sim.add_volume("Box", "big_box") + big_box.mother = world.name + big_box.material = "G4_AIR" + big_box.size = [0.8 * m, 0.8 * m, 0.8 * m] + + actor_box = sim.add_volume("Box", "actor_box") + actor_box.mother = big_box.name + actor_box.material = "G4_AIR" + actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] + actor_box.translation = [0, 0, -0.1 * m] + + source = sim.add_source("GenericSource", "photon_source") + source.particle = "gamma" + source.position.type = "box" + source.mother = world.name + source.position.size = [6 * cm, 6 * cm, 6 * cm] + source.position.translation = [0, 0, 0.3 * m] + source.direction.type = "momentum" + source.direction_relative_to_attached_volume = True + # source1.direction.focus_point = [0*cm, 0*cm, -5 *cm] + source.direction.momentum = [0, 0, -1] + source.energy.type = "mono" + source.energy.mono = 6 * MeV + source.n = 5000 + + tungsten_leaves = sim.add_volume("Box", "tungsten_leaves") + tungsten_leaves.mother = actor_box + tungsten_leaves.size = [0.6 * m, 0.6 * m, 0.3 * cm] + tungsten_leaves.material = "Tungsten" + liste_translation_W = [] + for i in range(7): + liste_translation_W.append([0, 0, 0.25 * m - i * 6 * cm]) + tungsten_leaves.translation = liste_translation_W + tungsten_leaves.color = [0.9, 0.0, 0.4, 0.8] + + kill_No_int_act = sim.add_actor("KillNonInteractingParticleActor", "killact") + kill_No_int_act.attached_to = actor_box.name + + entry_phase_space = sim.add_volume("Box", "entry_phase_space") + entry_phase_space.mother = big_box + entry_phase_space.size = [0.8 * m, 0.8 * m, 1 * nm] + entry_phase_space.material = "G4_AIR" + entry_phase_space.translation = [0, 0, 0.21 * m] + entry_phase_space.color = [0.5, 0.9, 0.3, 1] + + exit_phase_space_1 = sim.add_volume("Box", "exit_phase_space_1") + exit_phase_space_1.mother = actor_box + exit_phase_space_1.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space_1.material = "G4_AIR" + exit_phase_space_1.translation = [0, 0, -0.3 * m + 1 * nm] + exit_phase_space_1.color = [0.5, 0.9, 0.3, 1] + + exit_phase_space_2 = sim.add_volume("Box", "exit_phase_space_2") + exit_phase_space_2.mother = world.name + exit_phase_space_2.size = [0.6 * m, 0.6 * m, 1 * nm] + exit_phase_space_2.material = "G4_AIR" + exit_phase_space_2.translation = [0, 0, -0.4 * m - 1 * nm] + exit_phase_space_2.color = [0.5, 0.9, 0.3, 1] + + # print(sim.volume_manager.dump_volume_tree()) + liste_phase_space_name = [ + entry_phase_space.name, + exit_phase_space_1.name, + exit_phase_space_2.name, + ] + for name in liste_phase_space_name: + print(name) + phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_" + name) + phsp.attached_to = name + phsp.attributes = ["EventID", "TrackID", "KineticEnergy"] + name_phsp = "test082_" + name + ".root" + phsp.output_filename = name_phsp + + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + sim.physics_manager.enable_decay = False + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1 * mm + sim.physics_manager.global_production_cuts.positron = 1 * mm + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + + # go ! + sim.run() + print(s) + print(output_path) + entry_phsp = uproot.open( + str(output_path) + + "/test082_" + + liste_phase_space_name[0] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[0] + ) + exit_phase_space_1 = uproot.open( + str(output_path) + + "/test082_" + + liste_phase_space_name[1] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[1] + ) + exit_phase_space_2 = uproot.open( + str(output_path) + + "/test082_" + + liste_phase_space_name[2] + + ".root" + + ":PhaseSpace_" + + liste_phase_space_name[2] + ) + + df_entry = entry_phsp.arrays() + df_exit_1 = exit_phase_space_1.arrays() + df_exit_2 = exit_phase_space_2.arrays() + + is_ok = test082_test(df_entry, df_exit_1, df_exit_2) + + utility.test_ok(is_ok) From 33b7a4850848810e77b764ab99203853e2f84ec2 Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 8 Nov 2024 18:35:03 +0100 Subject: [PATCH 43/82] Add a short header at the beginning of the actor --- opengate/actors/miscactors.py | 5 + .../test074_kill_non_interacting_particles.py | 212 ------------------ 2 files changed, 5 insertions(+), 212 deletions(-) delete mode 100644 opengate/tests/src/test074_kill_non_interacting_particles.py diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index db70a9f58..c3d0b4663 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -302,6 +302,11 @@ def EndSimulationAction(self): class KillNonInteractingParticleActor(ActorBase, g4.GateKillNonInteractingParticleActor): + """ + If a particle, not generated within the volume at which our actor is attached, crosses the volume + without interaction, the particle is killed + """ + def __init__(self, *args, **kwargs): ActorBase.__init__(self, *args, **kwargs) self.__initcpp__() diff --git a/opengate/tests/src/test074_kill_non_interacting_particles.py b/opengate/tests/src/test074_kill_non_interacting_particles.py deleted file mode 100644 index b12a6f66d..000000000 --- a/opengate/tests/src/test074_kill_non_interacting_particles.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import opengate as gate -from opengate.tests import utility -from scipy.spatial.transform import Rotation -import numpy as np -from anytree import Node, RenderTree -import uproot - - -def test074_test(entry_data, exit_data_1, exit_data_2): - liste_ekin = [] - liste_evtID = [] - liste_trackID = [] - evt_ID_entry_data = entry_data["EventID"] - j = 0 - i = 0 - while i < len(evt_ID_entry_data): - if ( - j < len(exit_data_1["EventID"]) - and evt_ID_entry_data[i] == exit_data_1["EventID"][j] - ): - TID_entry = entry_data["TrackID"][i] - TID_exit = exit_data_1["TrackID"][j] - Ekin_entry = entry_data["KineticEnergy"][i] - Ekin_exit = exit_data_1["KineticEnergy"][j] - - if (TID_entry == TID_exit) and (Ekin_exit == Ekin_entry): - liste_ekin.append(exit_data_1["KineticEnergy"][j]) - liste_evtID.append(exit_data_1["EventID"][j]) - liste_trackID.append(exit_data_1["TrackID"][j]) - if (j < len(exit_data_1["EventID"]) - 1) and ( - exit_data_1["EventID"][j] == exit_data_1["EventID"][j + 1] - ): - i = i - 1 - j += 1 - i += 1 - liste_ekin = np.asarray(liste_ekin) - print("Number of tracks to kill =", len(liste_ekin)) - print( - "Number of killed tracks =", - (len(exit_data_1["EventID"]) - len(exit_data_2["EventID"])), - ) - - return len(liste_ekin) == ( - len(exit_data_1["EventID"]) - len(exit_data_2["EventID"]) - ) - - -if __name__ == "__main__": - paths = utility.get_default_test_paths(__file__) - output_path = paths.output - - - print(output_path) - # create the simulation - sim = gate.Simulation() - sim.output_dir = output_path - - # main options - ui = sim.user_info - ui.g4_verbose = False - # ui.visu = True - ui.visu_type = "vrml" - ui.check_volumes_overlap = False - # ui.running_verbose_level = gate.logger.EVENT - ui.number_of_threads = 1 - ui.random_seed = "auto" - - # units - m = gate.g4_units.m - km = gate.g4_units.km - mm = gate.g4_units.mm - cm = gate.g4_units.cm - nm = gate.g4_units.nm - Bq = gate.g4_units.Bq - MeV = gate.g4_units.MeV - keV = gate.g4_units.keV - sec = gate.g4_units.s - gcm3 = gate.g4_units["g/cm3"] - - sim.volume_manager.material_database.add_material_weights( - "Tungsten", - ["W"], - [1], - 19.3 * gcm3, - ) - - # adapt world size - world = sim.world - world.size = [1 * m, 1 * m, 1 * m] - world.material = "G4_AIR" - - big_box = sim.add_volume("Box", "big_box") - big_box.mother = world.name - big_box.material = "G4_AIR" - big_box.size = [0.8 * m, 0.8 * m, 0.8 * m] - - actor_box = sim.add_volume("Box", "actor_box") - actor_box.mother = big_box.name - actor_box.material = "G4_AIR" - actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] - actor_box.translation = [0, 0, -0.1 * m] - - source = sim.add_source("GenericSource", "photon_source") - source.particle = "gamma" - source.position.type = "box" - source.mother = world.name - source.position.size = [6 * cm, 6 * cm, 6 * cm] - source.position.translation = [0, 0, 0.3 * m] - source.direction.type = "momentum" - source.direction_relative_to_attached_volume = True - # source1.direction.focus_point = [0*cm, 0*cm, -5 *cm] - source.direction.momentum = [0, 0, -1] - source.energy.type = "mono" - source.energy.mono = 6 * MeV - source.n = 5000 - - tungsten_leaves = sim.add_volume("Box", "tungsten_leaves") - tungsten_leaves.mother = actor_box - tungsten_leaves.size = [0.6 * m, 0.6 * m, 0.3 * cm] - tungsten_leaves.material = "Tungsten" - liste_translation_W = [] - for i in range(7): - liste_translation_W.append([0, 0, 0.25 * m - i * 6 * cm]) - tungsten_leaves.translation = liste_translation_W - tungsten_leaves.color = [0.9, 0.0, 0.4, 0.8] - - kill_No_int_act = sim.add_actor("KillNonInteractingParticleActor", "killact") - kill_No_int_act.attached_to = actor_box.name - - entry_phase_space = sim.add_volume("Box", "entry_phase_space") - entry_phase_space.mother = big_box - entry_phase_space.size = [0.8 * m, 0.8 * m, 1 * nm] - entry_phase_space.material = "G4_AIR" - entry_phase_space.translation = [0, 0, 0.21 * m] - entry_phase_space.color = [0.5, 0.9, 0.3, 1] - - exit_phase_space_1 = sim.add_volume("Box", "exit_phase_space_1") - exit_phase_space_1.mother = actor_box - exit_phase_space_1.size = [0.6 * m, 0.6 * m, 1 * nm] - exit_phase_space_1.material = "G4_AIR" - exit_phase_space_1.translation = [0, 0, -0.3 * m + 1 * nm] - exit_phase_space_1.color = [0.5, 0.9, 0.3, 1] - - exit_phase_space_2 = sim.add_volume("Box", "exit_phase_space_2") - exit_phase_space_2.mother = world.name - exit_phase_space_2.size = [0.6 * m, 0.6 * m, 1 * nm] - exit_phase_space_2.material = "G4_AIR" - exit_phase_space_2.translation = [0, 0, -0.4 * m - 1 * nm] - exit_phase_space_2.color = [0.5, 0.9, 0.3, 1] - - # print(sim.volume_manager.dump_volume_tree()) - liste_phase_space_name = [ - entry_phase_space.name, - exit_phase_space_1.name, - exit_phase_space_2.name, - ] - for name in liste_phase_space_name: - print(name) - phsp = sim.add_actor("PhaseSpaceActor", "PhaseSpace_" + name) - phsp.attached_to = name - phsp.attributes = ["EventID", "TrackID", "KineticEnergy"] - name_phsp = "test074_" + name + ".root" - phsp.output_filename = name_phsp - - sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" - sim.physics_manager.enable_decay = False - sim.physics_manager.global_production_cuts.gamma = 1 * mm - sim.physics_manager.global_production_cuts.electron = 1 * mm - sim.physics_manager.global_production_cuts.positron = 1 * mm - - s = sim.add_actor("SimulationStatisticsActor", "Stats") - s.track_types_flag = True - - # go ! - sim.run() - print(s) - print(output_path) - entry_phsp = uproot.open( - str(output_path) - + "/test074_" - + liste_phase_space_name[0] - + ".root" - + ":PhaseSpace_" - + liste_phase_space_name[0] - ) - exit_phase_space_1 = uproot.open( - str(output_path) - + "/test074_" - + liste_phase_space_name[1] - + ".root" - + ":PhaseSpace_" - + liste_phase_space_name[1] - ) - exit_phase_space_2 = uproot.open( - str(output_path) - + "/test074_" - + liste_phase_space_name[2] - + ".root" - + ":PhaseSpace_" - + liste_phase_space_name[2] - ) - - df_entry = entry_phsp.arrays() - df_exit_1 = exit_phase_space_1.arrays() - df_exit_2 = exit_phase_space_2.arrays() - - is_ok = test074_test(df_entry, df_exit_1, df_exit_2) - - utility.test_ok(is_ok) From 2115b1f9908108fc66412d9e3aaa4147a378107b Mon Sep 17 00:00:00 2001 From: majacquet Date: Fri, 8 Nov 2024 19:26:17 +0100 Subject: [PATCH 44/82] actor adaptation for particle generated within the actorified volume --- .../GateKillNonInteractingParticleActor.cpp | 9 ++++--- opengate/actors/miscactors.py | 5 ++-- .../test082_kill_non_interacting_particles.py | 25 ++++++++----------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp index 93eb2fa25..f101683f4 100644 --- a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp +++ b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp @@ -11,6 +11,7 @@ #include "G4ios.hh" #include "GateHelpers.h" #include "GateHelpersDict.h" +#include "G4TransportationManager.hh" GateKillNonInteractingParticleActor::GateKillNonInteractingParticleActor( py::dict &user_info) @@ -31,6 +32,7 @@ void GateKillNonInteractingParticleActor::PreUserTrackingAction( } void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { + G4Navigator* navigator = G4TransportationManager::GetTransportationManager()->GetNavigatorForTracking(); G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance() ->GetVolume(fMotherVolumeName) @@ -38,15 +40,16 @@ void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { G4String physicalVolumeNamePreStep = "None"; if (step->GetPreStepPoint()->GetPhysicalVolume() !=0) physicalVolumeNamePreStep = step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); - if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logNameMotherVolume) && (fIsFirstStep)) { - if ((fPassedByTheMotherVolume == false) && (physicalVolumeNamePreStep == fMotherVolumeName) && - (step->GetPreStepPoint()->GetStepStatus() == 1)) { + if (((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != logNameMotherVolume) && (fIsFirstStep)) || ((fIsFirstStep) && (step->GetTrack()->GetParentID() == 0))) { + if ((fPassedByTheMotherVolume == false) && (((step->GetPreStepPoint()->GetStepStatus() == 1) && (physicalVolumeNamePreStep == fMotherVolumeName)) || ((fIsFirstStep) && (step->GetTrack()->GetParentID() == 0)))) { fPassedByTheMotherVolume = true; fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); } } + + G4String logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); if ((fPassedByTheMotherVolume) && (step->GetPostStepPoint()->GetStepStatus() == 1)) { if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index c3d0b4663..04fc5e151 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -303,8 +303,9 @@ def EndSimulationAction(self): class KillNonInteractingParticleActor(ActorBase, g4.GateKillNonInteractingParticleActor): """ - If a particle, not generated within the volume at which our actor is attached, crosses the volume - without interaction, the particle is killed + If a particle, not generated or generated within the volume at which our actor is attached, crosses the volume + without interaction, the particle is killed. Warning : this actor being based on energy measurement, Rayleigh photon + may not be killed. """ def __init__(self, *args, **kwargs): diff --git a/opengate/tests/src/test082_kill_non_interacting_particles.py b/opengate/tests/src/test082_kill_non_interacting_particles.py index c6fe7aba9..cc626163b 100644 --- a/opengate/tests/src/test082_kill_non_interacting_particles.py +++ b/opengate/tests/src/test082_kill_non_interacting_particles.py @@ -51,9 +51,6 @@ def test082_test(entry_data, exit_data_1, exit_data_2): if __name__ == "__main__": paths = utility.get_default_test_paths(__file__) output_path = paths.output - - - print(output_path) # create the simulation sim = gate.Simulation() sim.output_dir = output_path @@ -90,16 +87,16 @@ def test082_test(entry_data, exit_data_1, exit_data_2): # adapt world size world = sim.world world.size = [1 * m, 1 * m, 1 * m] - world.material = "G4_AIR" + world.material = "G4_Galactic" big_box = sim.add_volume("Box", "big_box") big_box.mother = world.name - big_box.material = "G4_AIR" + big_box.material = "G4_Galactic" big_box.size = [0.8 * m, 0.8 * m, 0.8 * m] actor_box = sim.add_volume("Box", "actor_box") actor_box.mother = big_box.name - actor_box.material = "G4_AIR" + actor_box.material = "G4_Galactic" actor_box.size = [0.6 * m, 0.6 * m, 0.6 * m] actor_box.translation = [0, 0, -0.1 * m] @@ -107,8 +104,8 @@ def test082_test(entry_data, exit_data_1, exit_data_2): source.particle = "gamma" source.position.type = "box" source.mother = world.name - source.position.size = [6 * cm, 6 * cm, 6 * cm] - source.position.translation = [0, 0, 0.3 * m] + source.position.size = [6 * cm, 6 * cm, 4 * cm] + source.position.translation = [0, 0, 0.205 * m] source.direction.type = "momentum" source.direction_relative_to_attached_volume = True # source1.direction.focus_point = [0*cm, 0*cm, -5 *cm] @@ -131,23 +128,23 @@ def test082_test(entry_data, exit_data_1, exit_data_2): kill_No_int_act.attached_to = actor_box.name entry_phase_space = sim.add_volume("Box", "entry_phase_space") - entry_phase_space.mother = big_box - entry_phase_space.size = [0.8 * m, 0.8 * m, 1 * nm] - entry_phase_space.material = "G4_AIR" - entry_phase_space.translation = [0, 0, 0.21 * m] + entry_phase_space.mother = actor_box.name + entry_phase_space.size = [0.6 * m, 0.6 * m, 1 * nm] + entry_phase_space.material = "G4_Galactic" + entry_phase_space.translation = [0, 0, 0.255* m] entry_phase_space.color = [0.5, 0.9, 0.3, 1] exit_phase_space_1 = sim.add_volume("Box", "exit_phase_space_1") exit_phase_space_1.mother = actor_box exit_phase_space_1.size = [0.6 * m, 0.6 * m, 1 * nm] - exit_phase_space_1.material = "G4_AIR" + exit_phase_space_1.material = "G4_Galactic" exit_phase_space_1.translation = [0, 0, -0.3 * m + 1 * nm] exit_phase_space_1.color = [0.5, 0.9, 0.3, 1] exit_phase_space_2 = sim.add_volume("Box", "exit_phase_space_2") exit_phase_space_2.mother = world.name exit_phase_space_2.size = [0.6 * m, 0.6 * m, 1 * nm] - exit_phase_space_2.material = "G4_AIR" + exit_phase_space_2.material = "G4_Galactic" exit_phase_space_2.translation = [0, 0, -0.4 * m - 1 * nm] exit_phase_space_2.color = [0.5, 0.9, 0.3, 1] From 220932b453d764521822f5f65a6950562549c9ce Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 12 Nov 2024 11:17:02 +0100 Subject: [PATCH 45/82] Add of an output actor retrieving the number of particles killed --- .../pyGateKillNonInteractingParticleActor.cpp | 1 + opengate/actors/miscactors.py | 36 +++++++++++++++++-- .../test082_kill_non_interacting_particles.py | 2 ++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp index 5de9fe12b..d841af3d0 100644 --- a/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp +++ b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp @@ -18,5 +18,6 @@ void init_GateKillNonInteractingParticleActor(py::module &m) { .def_readwrite( "fListOfVolumeAncestor", &GateKillNonInteractingParticleActor::fListOfVolumeAncestor) + .def_readwrite("number_of_killed_particles", &GateKillNonInteractingParticleActor::fNbOfKilledParticles) .def(py::init()); } diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 04fc5e151..372bf73ca 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -277,7 +277,6 @@ def SteppingAction(self, step, touchable): do_something() """ - class KillActor(ActorBase, g4.GateKillActor): def __init__(self, *args, **kwargs): @@ -300,6 +299,27 @@ def EndSimulationAction(self): self.number_of_killed_particles = self.GetNumberOfKilledParticles() +class ActorOutputKillNonInteractingParticleActor(ActorOutputBase): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.number_of_killed_particles = 0 + + + def get_processed_output(self): + d = {} + d["particles killed"] = self.number_of_killed_particles + return d + + def __str__(self): + s = "" + for k, v in self.get_processed_output().items(): + s = k + ": " + str(v) + s += "\n" + return(s) + + + class KillNonInteractingParticleActor(ActorBase, g4.GateKillNonInteractingParticleActor): """ @@ -310,13 +330,15 @@ class KillNonInteractingParticleActor(ActorBase, g4.GateKillNonInteractingPartic def __init__(self, *args, **kwargs): ActorBase.__init__(self, *args, **kwargs) + self._add_user_output(ActorOutputKillNonInteractingParticleActor, "kill_non_interacting_particles") self.__initcpp__() self.list_of_volume_name = [] + self.number_of_killed_particles = 0 def __initcpp__(self): g4.GateKillNonInteractingParticleActor.__init__(self, self.user_info) self.AddActions( - {"StartSimulationAction","PreUserTrackingAction", "SteppingAction"} + {"StartSimulationAction","PreUserTrackingAction", "SteppingAction","EndOfSimulationAction"} ) def initialize(self): @@ -335,6 +357,15 @@ def initialize(self): self.fListOfVolumeAncestor = self.list_of_volume_name + def EndSimulationAction(self): + self.user_output.kill_non_interacting_particles.number_of_killed_particles = self.number_of_killed_particles + + + def __str__(self): + s = self.user_output["kill_non_interacting_particles"].__str__() + return s + + def _setter_hook_particles(self, value): if isinstance(value, str): return [value] @@ -475,6 +506,7 @@ def initialize(self): process_cls(ActorOutputStatisticsActor) process_cls(SimulationStatisticsActor) process_cls(KillActor) +process_cls(KillNonInteractingParticleActor) process_cls(SplittingActorBase) process_cls(ComptSplittingActor) process_cls(BremSplittingActor) diff --git a/opengate/tests/src/test082_kill_non_interacting_particles.py b/opengate/tests/src/test082_kill_non_interacting_particles.py index cc626163b..cf433209e 100644 --- a/opengate/tests/src/test082_kill_non_interacting_particles.py +++ b/opengate/tests/src/test082_kill_non_interacting_particles.py @@ -206,4 +206,6 @@ def test082_test(entry_data, exit_data_1, exit_data_2): is_ok = test082_test(df_entry, df_exit_1, df_exit_2) + print(kill_No_int_act.user_output.kill_non_interacting_particles) + utility.test_ok(is_ok) From 53fc3938c83296946d9f9543ef7697a78b1aaf3d Mon Sep 17 00:00:00 2001 From: majacquet Date: Tue, 12 Nov 2024 19:02:19 +0100 Subject: [PATCH 46/82] Removal of conditionnal kill actors --- core/opengate_core/opengate_core.cpp | 9 -- .../GateKillInteractingParticleActor.cpp | 70 ----------- .../GateKillInteractingParticleActor.h | 41 ------- .../GateKillNonInteractingParticleActor.cpp | 75 ------------ .../GateKillNonInteractingParticleActor.h | 42 ------- ...teLastVertexInteractionSplittingActorOld.h | 110 ------------------ .../GateLastVertexSplittingPostStepDoItOld.h | 110 ------------------ .../pyGateKillInteractingParticleActor.cpp | 22 ---- .../pyGateKillNonInteractingParticleActor.cpp | 22 ---- ...LastVertexInteractionSplittingActorOld.cpp | 21 ---- opengate/actors/actorbuilders.py | 5 - opengate/actors/miscactors.py | 92 --------------- 12 files changed, 619 deletions(-) delete mode 100644 core/opengate_core/opengate_lib/GateKillInteractingParticleActor.cpp delete mode 100644 core/opengate_core/opengate_lib/GateKillInteractingParticleActor.h delete mode 100644 core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp delete mode 100644 core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h delete mode 100644 core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActorOld.h delete mode 100644 core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoItOld.h delete mode 100644 core/opengate_core/opengate_lib/pyGateKillInteractingParticleActor.cpp delete mode 100644 core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp delete mode 100644 core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActorOld.cpp diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index 7f57e1d1b..76a4c7089 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -302,12 +302,8 @@ void init_GateARFTrainingDatasetActor(py::module &m); void init_GateKillActor(py::module &); -void init_GateKillNonInteractingParticleActor(py::module &); - void init_GateSurfaceSplittingActor(py::module &); -void init_GateKillInteractingParticleActor(py::module &); - void init_itk_image(py::module &); void init_GateImageNestedParameterisation(py::module &); @@ -346,8 +342,6 @@ void init_GateOptrComptSplittingActor(py::module &m); void init_GateLastVertexInteractionSplittingActor(py::module &m); -void init_GateLastVertexInteractionSplittingActorOld(py::module &m); - void init_GateOptrComptPseudoTransportationActor(py::module &m); void init_GateBOptrBremSplittingActor(py::module &m); @@ -575,7 +569,6 @@ PYBIND11_MODULE(opengate_core, m) { init_GateOptrComptPseudoTransportationActor(m); init_GateOptrComptSplittingActor(m); init_GateLastVertexInteractionSplittingActor(m); - init_GateLastVertexInteractionSplittingActorOld(m); init_GateHitsCollectionActor(m); init_GateMotionVolumeActor(m); init_GateHitsAdderActor(m); @@ -588,9 +581,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GateARFActor(m); init_GateARFTrainingDatasetActor(m); init_GateKillActor(m); - init_GateKillNonInteractingParticleActor(m); init_GateSurfaceSplittingActor(m); - init_GateKillInteractingParticleActor(m); init_GateDigiAttributeManager(m); init_GateVDigiAttribute(m); init_GateExceptionHandler(m); diff --git a/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.cpp deleted file mode 100644 index b6b4b4ba9..000000000 --- a/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ------------------------------------ -------------- */ - -#include "GateKillInteractingParticleActor.h" -#include "G4LogicalVolumeStore.hh" -#include "G4PhysicalVolumeStore.hh" -#include "G4ios.hh" -#include "GateHelpers.h" -#include "GateHelpersDict.h" - -GateKillInteractingParticleActor::GateKillInteractingParticleActor( - py::dict &user_info) - : GateVActor(user_info, false) { - fActions.insert("StartSimulationAction"); - fActions.insert("SteppingAction"); - fActions.insert("PreUserTrackingAction"); -} - -void GateKillInteractingParticleActor::ActorInitialize() {} - -void GateKillInteractingParticleActor::StartSimulationAction() { - fNbOfKilledParticles = 0; -} - -void GateKillInteractingParticleActor::PreUserTrackingAction( - const G4Track *track) { - fIsFirstStep = true; -} - -void GateKillInteractingParticleActor::SteppingAction(G4Step *step) { - - G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance() - ->GetVolume(fMotherVolumeName) - ->GetName(); - G4String physicalVolumeNamePreStep = - step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); - if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != - logNameMotherVolume) && - (fIsFirstStep)) { - if ((physicalVolumeNamePreStep == fMotherVolumeName) && - (step->GetPreStepPoint()->GetStepStatus() == 1)) { - fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); - ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); - } - } - - G4String logicalVolumeNamePostStep = step->GetPostStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - - if (step->GetPostStepPoint()->GetStepStatus() == 1){ - if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), - logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { - if ((step->GetTrack()->GetTrackID() != ftrackIDAtTheEntrance) || - (step->GetPostStepPoint()->GetKineticEnergy() != fKineticEnergyAtTheEntrance)) { - auto track = step->GetTrack(); - track->SetTrackStatus(fStopAndKill); - fNbOfKilledParticles++; - } - fKineticEnergyAtTheEntrance = 0; - ftrackIDAtTheEntrance = 0; - } - } - fIsFirstStep = false; -} diff --git a/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.h b/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.h deleted file mode 100644 index 9f0b003e4..000000000 --- a/core/opengate_core/opengate_lib/GateKillInteractingParticleActor.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - -------------------------------------------------- */ - -#ifndef GateKillInteractingParticleActor_h -#define GateKillInteractingParticleActor_h - -#include "G4Cache.hh" -#include "GateVActor.h" -#include - -namespace py = pybind11; - -class GateKillInteractingParticleActor : public GateVActor { - -public: - // Constructor - GateKillInteractingParticleActor(py::dict &user_info); - - void ActorInitialize() override; - - void StartSimulationAction() override; - - // Main function called every step in attached volume - void SteppingAction(G4Step *) override; - - void PreUserTrackingAction(const G4Track *) override; - - std::vector fParticlesTypeToKill; - G4double fKineticEnergyAtTheEntrance = 0; - G4int ftrackIDAtTheEntrance = 0; - G4bool fIsFirstStep = true; - std::vector fListOfVolumeAncestor; - - long fNbOfKilledParticles{}; -}; - -#endif diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp deleted file mode 100644 index 5a6b544c7..000000000 --- a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ------------------------------------ -------------- */ - -#include "GateKillNonInteractingParticleActor.h" -#include "G4LogicalVolumeStore.hh" -#include "G4PhysicalVolumeStore.hh" -#include "G4ios.hh" -#include "GateHelpers.h" -#include "GateHelpersDict.h" - -GateKillNonInteractingParticleActor::GateKillNonInteractingParticleActor( - py::dict &user_info) - : GateVActor(user_info, false) { - fActions.insert("StartSimulationAction"); - fActions.insert("SteppingAction"); - fActions.insert("PreUserTrackingAction"); -} - -void GateKillNonInteractingParticleActor::ActorInitialize() {} - -void GateKillNonInteractingParticleActor::StartSimulationAction() { - fNbOfKilledParticles = 0; -} - -void GateKillNonInteractingParticleActor::PreUserTrackingAction( - const G4Track *track) { - fIsFirstStep = true; - fKineticEnergyAtTheEntrance = 0; - ftrackIDAtTheEntrance = 0; - fPassedByTheMotherVolume = false; -} - -void GateKillNonInteractingParticleActor::SteppingAction(G4Step *step) { - - G4String logNameMotherVolume = G4LogicalVolumeStore::GetInstance() - ->GetVolume(fMotherVolumeName) - ->GetName(); - G4String physicalVolumeNamePreStep = - step->GetPreStepPoint()->GetPhysicalVolume()->GetName(); - if ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != - logNameMotherVolume) && - (fIsFirstStep)) { - if ((fPassedByTheMotherVolume == false) && - (physicalVolumeNamePreStep == fMotherVolumeName) && - (step->GetPreStepPoint()->GetStepStatus() == 1)) { - fPassedByTheMotherVolume = true; - fKineticEnergyAtTheEntrance = step->GetPreStepPoint()->GetKineticEnergy(); - ftrackIDAtTheEntrance = step->GetTrack()->GetTrackID(); - } - } - - G4String logicalVolumeNamePostStep = step->GetPostStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - if ((fPassedByTheMotherVolume) && - (step->GetPostStepPoint()->GetStepStatus() == 1)) { - if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), - logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { - if ((step->GetTrack()->GetTrackID() == ftrackIDAtTheEntrance) && - (step->GetPostStepPoint()->GetKineticEnergy() == - fKineticEnergyAtTheEntrance)) { - auto track = step->GetTrack(); - track->SetTrackStatus(fStopAndKill); - fNbOfKilledParticles++; - } - } - } - - fIsFirstStep = false; -} diff --git a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h b/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h deleted file mode 100644 index bb7aabe7a..000000000 --- a/core/opengate_core/opengate_lib/GateKillNonInteractingParticleActor.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - -------------------------------------------------- */ - -#ifndef GateKillNonInteractingParticleActor_h -#define GateKillNonInteractingParticleActor_h - -#include "G4Cache.hh" -#include "GateVActor.h" -#include - -namespace py = pybind11; - -class GateKillNonInteractingParticleActor : public GateVActor { - -public: - // Constructor - GateKillNonInteractingParticleActor(py::dict &user_info); - - void ActorInitialize() override; - - void StartSimulationAction() override; - - // Main function called every step in attached volume - void SteppingAction(G4Step *) override; - - void PreUserTrackingAction(const G4Track *) override; - - std::vector fParticlesTypeToKill; - G4bool fPassedByTheMotherVolume = false; - G4double fKineticEnergyAtTheEntrance = 0; - G4int ftrackIDAtTheEntrance = 0; - G4bool fIsFirstStep = true; - std::vector fListOfVolumeAncestor; - - long fNbOfKilledParticles{}; -}; - -#endif diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActorOld.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActorOld.h deleted file mode 100644 index 3863a8b62..000000000 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActorOld.h +++ /dev/null @@ -1,110 +0,0 @@ -// -// ******************************************************************** -// * 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 GateLastVertexInteractionSplittingActorOld.h -/// \brief Definition of the GateLastVertexInteractionSplittingActorOld class -#ifndef GateLastVertexInteractionSplittingActorOld_h -#define GateLastVertexInteractionSplittingActorOld_h 1 - -#include "G4ParticleChangeForGamma.hh" -#include "G4VEnergyLossProcess.hh" -#include "GateVActor.h" -#include -#include -namespace py = pybind11; - -class GateLastVertexInteractionSplittingActorOld : public GateVActor { -public: - GateLastVertexInteractionSplittingActorOld(py::dict &user_info); - virtual ~GateLastVertexInteractionSplittingActorOld() {} - - G4double fSplittingFactor; - G4bool fRussianRouletteForAngle = false; - G4bool fRotationVectorDirector; - G4ThreeVector fVectorDirector; - G4double fMaxTheta; - G4int fTrackIDOfSplittedTrack = 0; - G4int fParentID = -1; - G4int fEventID; - G4int fEventIDOfSplittedTrack; - G4int fEventIDOfInitialSplittedTrack; - G4int fTrackIDOfInitialTrack; - G4int fTrackIDOfInitialSplittedTrack = 0; - G4int ftmpTrackID; - G4bool fIsFirstStep = true; - G4bool fSuspendForAnnihil = false; - G4double fWeightOfEnteringParticle = 0; - G4double fSplitCounter = 0; - G4bool fNotSplitted = true; - - G4Track *fTrackToSplit = nullptr; - G4Step *fStepToSplit = nullptr; - G4String fProcessToSplit = "None"; - - std::vector fTracksToPostpone; - - std::map fRememberedTracks; - std::map> fRememberedSteps; - std::map> fRememberedProcesses; - - std::vector fListOfVolumeAncestor; - - std::vector fListOfProcesses = {"compt", "annihil", "eBrem", "conv", - "phot"}; - - virtual void SteppingAction(G4Step *) override; - virtual void BeginOfEventAction(const G4Event *) override; - virtual void BeginOfRunAction(const G4Run *run) override; - virtual void PreUserTrackingAction(const G4Track *track) override; - virtual void PostUserTrackingAction(const G4Track *track) override; - - // Pure splitting functions - G4double RussianRouletteForAngleSurvival(G4ThreeVector, G4ThreeVector, - G4double, G4double); - G4Track *CreateComptonTrack(G4ParticleChangeForGamma *, G4Track, G4double); - void ComptonSplitting(G4Step *CurrentStep, G4Track *track, const G4Step *step, - G4VProcess *process); - void SecondariesSplitting(G4Step *CurrentStep, G4Track *track, - const G4Step *step, G4VProcess *process); - - // Handling the remembered processes to replay - void RememberLastProcessInformation(G4Step *); - void CreateNewParticleAtTheLastVertex(G4Step *, G4Track *, const G4Step *, - G4String); - void ResetProcessesForEnteringParticles(G4Step *step); - void ClearRememberedTracksAndSteps(std::map, - std::map>); - - // Edge case to handle the bias in annihilation - // FIXME : The triple annihilation is not handled for the moment - void PostponeFirstAnnihilationTrackIfInteraction(G4Step *step, - G4String processName); - void RegenerationOfPostponedAnnihilationTrack(G4Step *step); - void HandleTrackIDIfPostponedAnnihilation(G4Step *step); - G4bool IsParticleExitTheBiasedVolume(G4Step*step); -}; - -#endif diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoItOld.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoItOld.h deleted file mode 100644 index df30a39d7..000000000 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoItOld.h +++ /dev/null @@ -1,110 +0,0 @@ -// -// ******************************************************************** -// * 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. * -// ******************************************************************** -// -/// -#ifndef GateLastVertexSplittingPostStepDoItOld_h -#define GateLastVertexSplittingPostStepDoItOld_h - - -#include "G4VEnergyLossProcess.hh" -#include "G4VEmProcess.hh" -#include "G4VParticleChange.hh" -#include "G4eplusAnnihilation.hh" -#include "G4PhysicalConstants.hh" -#include "G4MaterialCutsCouple.hh" -#include "G4Gamma.hh" -#include "G4Electron.hh" -#include "G4Positron.hh" -#include "G4eeToTwoGammaModel.hh" -#include "G4EmBiasingManager.hh" -#include "G4EntanglementAuxInfo.hh" -#include "G4eplusAnnihilationEntanglementClipBoard.hh" -#include "G4EmParameters.hh" -#include "G4PhysicsModelCatalog.hh" -#include - - - - -class GateBremPostStepDoIt : public G4VEnergyLossProcess { -public : - -GateBremPostStepDoIt(); - -~ GateBremPostStepDoIt(); - -virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & step) override -{ - const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); - currentCouple = couple; - G4VParticleChange* particleChange = G4VEnergyLossProcess::PostStepDoIt(track,step); - return particleChange; -} - - -}; - - -class GateGammaEmPostStepDoIt : public G4VEmProcess { -public : - -GateGammaEmPostStepDoIt(); - -~ GateGammaEmPostStepDoIt(); - -virtual G4VParticleChange * PostStepDoIt(const G4Track & track, const G4Step & step) override -{ - const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); - currentCouple = couple; - G4VParticleChange* particleChange = G4VEmProcess::PostStepDoIt(track,step); - return particleChange; -} - - -}; - -class GateplusannihilAtRestDoIt : public G4eplusAnnihilation { -public : - -GateplusannihilAtRestDoIt(); -~ GateplusannihilAtRestDoIt(); - -virtual G4VParticleChange* AtRestDoIt(const G4Track& track, - const G4Step& step) override -// Performs the e+ e- annihilation when both particles are assumed at rest. - { - G4Track copyTrack = G4Track(track); - copyTrack.SetStep(&step); - G4VParticleChange* particleChange = G4eplusAnnihilation::AtRestDoIt(copyTrack,step); - return particleChange; - } -}; -#endif - - - - - - \ No newline at end of file diff --git a/core/opengate_core/opengate_lib/pyGateKillInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/pyGateKillInteractingParticleActor.cpp deleted file mode 100644 index fa089c4ef..000000000 --- a/core/opengate_core/opengate_lib/pyGateKillInteractingParticleActor.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - -------------------------------------------------- */ - -#include - -namespace py = pybind11; - -#include "GateKillInteractingParticleActor.h" - -void init_GateKillInteractingParticleActor(py::module &m) { - py::class_, - GateVActor>(m, "GateKillInteractingParticleActor") - .def_readwrite( - "fListOfVolumeAncestor", - &GateKillInteractingParticleActor::fListOfVolumeAncestor) - .def(py::init()); -} diff --git a/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp b/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp deleted file mode 100644 index 5de9fe12b..000000000 --- a/core/opengate_core/opengate_lib/pyGateKillNonInteractingParticleActor.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - -------------------------------------------------- */ - -#include - -namespace py = pybind11; - -#include "GateKillNonInteractingParticleActor.h" - -void init_GateKillNonInteractingParticleActor(py::module &m) { - py::class_, - GateVActor>(m, "GateKillNonInteractingParticleActor") - .def_readwrite( - "fListOfVolumeAncestor", - &GateKillNonInteractingParticleActor::fListOfVolumeAncestor) - .def(py::init()); -} diff --git a/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActorOld.cpp b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActorOld.cpp deleted file mode 100644 index a15dc55e8..000000000 --- a/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActorOld.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - -------------------------------------------------- */ -#include - -namespace py = pybind11; -#include "GateLastVertexInteractionSplittingActorOld.h" - -void init_GateLastVertexInteractionSplittingActorOld(py::module &m) { - - py::class_>( - m, "GateLastVertexInteractionSplittingActorOld") - .def_readwrite( - "fListOfVolumeAncestor", - &GateLastVertexInteractionSplittingActorOld::fListOfVolumeAncestor) - .def(py::init()); -} diff --git a/opengate/actors/actorbuilders.py b/opengate/actors/actorbuilders.py index b8644c5bd..eeae701de 100644 --- a/opengate/actors/actorbuilders.py +++ b/opengate/actors/actorbuilders.py @@ -17,13 +17,10 @@ SourceInfoActor, TestActor, KillActor, - KillInteractingParticleActor, BremSplittingActor, ComptSplittingActor, LastVertexInteractionSplittingActor, - LastVertexInteractionSplittingActorOld, ComptPseudoTransportationActor, - KillNonInteractingParticleActor, SurfaceSplittingActor, ) from .dynamicactors import DynamicGeometryActor @@ -50,12 +47,10 @@ ARFTrainingDatasetActor, TestActor, KillActor, - KillInteractingParticleActor, BremSplittingActor, ComptSplittingActor, LastVertexInteractionSplittingActor, ComptPseudoTransportationActor, - KillNonInteractingParticleActor, SurfaceSplittingActor, DynamicGeometryActor, } diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 404d964f0..ad2606d49 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -431,36 +431,6 @@ def __init__(self, user_info): g4.GateKillActor.__init__(self, user_info.__dict__) -class KillInteractingParticleActor( - g4.GateKillInteractingParticleActor, ActorBase -): - type_name = "KillInteractingParticleActor" - - def set_default_user_info(user_info): - ActorBase.set_default_user_info(user_info) - user_info.list_of_volume_name = [] - - def __init__(self, user_info): - ActorBase.__init__(self, user_info) - g4.GateKillInteractingParticleActor.__init__(self, user_info.__dict__) - self.list_of_volume_name = user_info.list_of_volume_name - self.user_info.mother = user_info.mother - - def initialize(self, volume_engine=None): - - super().initialize(volume_engine) - volume_tree = self.simulation.volume_manager.get_volume_tree() - dico_of_volume_tree = {} - for pre, _, node in RenderTree(volume_tree): - dico_of_volume_tree[str(node.name)] = node - volume_name = self.user_info.mother - while volume_name != "world": - node = dico_of_volume_tree[volume_name] - volume_name = node.mother - self.list_of_volume_name.append(volume_name) - self.fListOfVolumeAncestor = self.list_of_volume_name - - class ComptSplittingActor(g4.GateOptrComptSplittingActor, ActorBase): type_name = "ComptSplittingActor" @@ -518,40 +488,6 @@ def initialize(self, volume_engine=None): print(self.fListOfVolumeAncestor) -class LastVertexInteractionSplittingActorOld(g4.GateLastVertexInteractionSplittingActorOld, ActorBase): - type_name = "LastVertexInteractionSplittingActorOld" - - def set_default_user_info(user_info): - ActorBase.set_default_user_info(user_info) - deg = g4_units.deg - user_info.splitting_factor = 1 - user_info.russian_roulette_for_angle = False - user_info.rotation_vector_director = False - user_info.vector_director = [0, 0, -1] - user_info.max_theta = 90 * deg - user_info.list_of_volume_name = [] - - def __init__(self, user_info): - ActorBase.__init__(self, user_info) - g4.GateLastVertexInteractionSplittingActorOld.__init__(self, user_info.__dict__) - self.list_of_volume_name = user_info.list_of_volume_name - self.user_info.mother = user_info.mother - - def initialize(self, volume_engine=None): - - super().initialize(volume_engine) - volume_tree = self.simulation.volume_manager.get_volume_tree() - dico_of_volume_tree = {} - for pre, _, node in RenderTree(volume_tree): - dico_of_volume_tree[str(node.name)] = node - volume_name = self.user_info.mother - while volume_name != "world": - node = dico_of_volume_tree[volume_name] - volume_name = node.mother - self.list_of_volume_name.append(volume_name) - self.fListOfVolumeAncestor = self.list_of_volume_name - print(self.fListOfVolumeAncestor) - class ComptPseudoTransportationActor( g4.GateOptrComptPseudoTransportationActor, ActorBase ): @@ -593,34 +529,6 @@ def __init__(self, user_info): g4.GateBOptrBremSplittingActor.__init__(self, user_info.__dict__) -class KillNonInteractingParticleActor( - g4.GateKillNonInteractingParticleActor, ActorBase -): - type_name = "KillNonInteractingParticleActor" - - def set_default_user_info(user_info): - ActorBase.set_default_user_info(user_info) - user_info.list_of_volume_name = [] - - def __init__(self, user_info): - ActorBase.__init__(self, user_info) - g4.GateKillNonInteractingParticleActor.__init__(self, user_info.__dict__) - self.list_of_volume_name = user_info.list_of_volume_name - self.user_info.mother = user_info.mother - - def initialize(self, volume_engine=None): - - super().initialize(volume_engine) - volume_tree = self.simulation.volume_manager.get_volume_tree() - dico_of_volume_tree = {} - for pre, _, node in RenderTree(volume_tree): - dico_of_volume_tree[str(node.name)] = node - volume_name = self.user_info.mother - while volume_name != "world": - node = dico_of_volume_tree[volume_name] - volume_name = node.mother - self.list_of_volume_name.append(volume_name) - self.fListOfVolumeAncestor = self.list_of_volume_name class SurfaceSplittingActor(g4.GateSurfaceSplittingActor, ActorBase): From b9600a591dac14e0cdf8ef8221e6350d93bae11c Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 13 Nov 2024 10:48:36 +0100 Subject: [PATCH 47/82] files deletion --- ...LastVertexInteractionSplittingOldActor.cpp | 848 ------------------ .../GateSurfaceSplittingActor.cpp | 96 -- .../opengate_lib/GateSurfaceSplittingActor.h | 43 - .../pyGateSurfaceSplittingActor.cpp | 22 - 4 files changed, 1009 deletions(-) delete mode 100644 core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingOldActor.cpp delete mode 100644 core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp delete mode 100644 core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h delete mode 100644 core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingOldActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingOldActor.cpp deleted file mode 100644 index fdb7a2f1e..000000000 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingOldActor.cpp +++ /dev/null @@ -1,848 +0,0 @@ -// -// ******************************************************************** -// * 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 GateLastVertexInteractionSplittingActorOld.cc -/// \brief Implementation of the GateLastVertexInteractionSplittingActorOld class - -#include "GateHelpersDict.h" -#include "GateHelpersImage.h" - -#include "CLHEP/Units/SystemOfUnits.h" -#include "G4BiasingProcessInterface.hh" -#include "G4Gamma.hh" -#include "G4LogicalVolumeStore.hh" -#include "G4ParticleTable.hh" -#include "G4PhysicalVolumeStore.hh" -#include "G4Positron.hh" -#include "G4ProcessManager.hh" -#include "G4ProcessVector.hh" -#include "G4TrackStatus.hh" -#include "G4TrackingManager.hh" -#include "G4VParticleChange.hh" -#include "G4eplusAnnihilation.hh" -#include "GateLastVertexInteractionSplittingActorOld.h" -#include "GateLastVertexSplittingPostStepDoItOld.h" -#include "GateOptnComptSplitting.h" - -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... - -GateLastVertexInteractionSplittingActorOld:: - GateLastVertexInteractionSplittingActorOld(py::dict &user_info) - : GateVActor(user_info, false) { - fMotherVolumeName = DictGetStr(user_info, "mother"); - fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); - fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); - fRussianRouletteForAngle = - DictGetBool(user_info, "russian_roulette_for_angle"); - fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); - fMaxTheta = DictGetDouble(user_info, "max_theta"); - fActions.insert("StartSimulationAction"); - fActions.insert("SteppingAction"); - fActions.insert("BeginOfEventAction"); - fActions.insert("BeginOfRunAction"); - fActions.insert("PreUserTrackingAction"); - fActions.insert("PostUserTrackingAction"); -} - -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... - -G4double -GateLastVertexInteractionSplittingActorOld::RussianRouletteForAngleSurvival( - G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta, - G4double split) { - G4double cosTheta = vectorDirector * dir; - G4double theta = std::acos(cosTheta); - G4double weightToApply = 1; - if (theta > fMaxTheta) { - G4double probability = G4UniformRand(); - if (probability <= 1 / split) { - weightToApply = split; - } else { - weightToApply = 0; - } - } - return weightToApply; -} - -G4Track *GateLastVertexInteractionSplittingActorOld::CreateComptonTrack( - G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) { - G4double energy = gammaProcess->GetProposedKineticEnergy(); - G4double globalTime = track.GetGlobalTime(); - G4double newGammaWeight = weight; - G4ThreeVector polarization = gammaProcess->GetProposedPolarization(); - const G4ThreeVector momentum = gammaProcess->GetProposedMomentumDirection(); - const G4ThreeVector position = track.GetPosition(); - G4Track *newTrack = new G4Track(track); - - newTrack->SetWeight(newGammaWeight); - newTrack->SetKineticEnergy(energy); - newTrack->SetMomentumDirection(momentum); - newTrack->SetPosition(position); - newTrack->SetPolarization(polarization); - return newTrack; -} - -void GateLastVertexInteractionSplittingActorOld::ComptonSplitting( - G4Step *CurrentStep, G4Track *track, const G4Step *step, - G4VProcess *process) { - // Loop on process and add the secondary tracks to the current step secondary - // vector - - G4TrackVector *trackVector = CurrentStep->GetfSecondary(); - G4double gammaWeight = 0; - - GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*track, *step); - G4ParticleChangeForGamma *gammaProcessFinalState = - (G4ParticleChangeForGamma *)processFinalState; - const G4ThreeVector momentum = - gammaProcessFinalState->GetProposedMomentumDirection(); - gammaWeight = fWeightOfEnteringParticle / fSplittingFactor; - if (fRussianRouletteForAngle == true) { - G4double weightToApply = RussianRouletteForAngleSurvival( - momentum, fVectorDirector, fMaxTheta, fSplittingFactor); - if (weightToApply != 0) { - gammaWeight = gammaWeight * weightToApply; - G4Track *newTrack = - CreateComptonTrack(gammaProcessFinalState, *track, gammaWeight); - trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) { - G4Track *newTrack = processFinalState->GetSecondary(j); - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - } - } - - else { - G4Track *newTrack = - CreateComptonTrack(gammaProcessFinalState, *track, gammaWeight); - trackVector->push_back(newTrack); - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - for (int j = 0; j < NbOfSecondaries; j++) { - G4Track *newTrack = processFinalState->GetSecondary(j); - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - } - processFinalState->Clear(); - gammaProcessFinalState->Clear(); -} - -void GateLastVertexInteractionSplittingActorOld::SecondariesSplitting( - G4Step *CurrentStep, G4Track *track, const G4Step *step, - G4VProcess *process) { - // Loop on process and add the secondary tracks to the current step secondary - // vector. - - // std::cout<GetKineticEnergy()<<" - // "<GetDynamicParticle()->GetKineticEnergy()<<" - // "<GetMomentumDirection()<<" - // "<GetDynamicParticle()->GetMomentumDirection()<GetParticleDefinition()->GetParticleName(); - G4TrackVector *trackVector = CurrentStep->GetfSecondary(); - G4double gammaWeight = 0; - G4VParticleChange *processFinalState = nullptr; - if (process->GetProcessName() == "eBrem") { - GateBremPostStepDoIt *bremProcess = (GateBremPostStepDoIt *)process; - processFinalState = - bremProcess->GateBremPostStepDoIt::PostStepDoIt(*track, *step); - } else { - GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - if (track->GetTrackStatus() == 0) { - // if (process->GetProcessName() == "annihil") - // std::cout<<"lol"<PostStepDoIt(*track, *step); - } - if (track->GetTrackStatus() == 1) { - GateplusannihilAtRestDoIt *eplusAnnihilProcess = - (GateplusannihilAtRestDoIt *)process; - - processFinalState = - eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*track, - *step); - } - } - - G4int NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - - if (NbOfSecondaries > 0) { - gammaWeight = fWeightOfEnteringParticle / fSplittingFactor; - G4Track *newTrack = processFinalState->GetSecondary(0); - if ((fRussianRouletteForAngle == true) && (particleName == "gamma")) { - const G4ThreeVector momentum = newTrack->GetMomentumDirection(); - G4double weightToApply = RussianRouletteForAngleSurvival( - momentum, fVectorDirector, fMaxTheta, fSplittingFactor); - if (weightToApply != 0) { - gammaWeight = gammaWeight * weightToApply; - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - } else { - newTrack->SetWeight(gammaWeight); - trackVector->push_back(newTrack); - } - - for(int i = 1; i < NbOfSecondaries;i++){ - delete processFinalState->GetSecondary(i); - } - } else { - processFinalState->Clear(); - SecondariesSplitting(CurrentStep, track, step, process); - } - processFinalState->Clear(); -} - -void GateLastVertexInteractionSplittingActorOld::ClearRememberedTracksAndSteps( - std::map rememberedTracks, - std::map> rememberedSteps) { - std::vector trackToKill = {}; - for (auto it = rememberedTracks.begin(); it != rememberedTracks.end(); ++it) { - std::vector vector = it->second; - for (auto it2 = vector.begin(); it2 != vector.end(); it2++) { - if ((std::find(trackToKill.begin(), trackToKill.end(), *it2) == - trackToKill.end())) { - // Method set pour clear les doublons au lieu de find. - trackToKill.push_back(*it2); - } - } - } - - for (auto it = trackToKill.begin(); it != trackToKill.end(); ++it) { - delete *it; - } - - std::vector stepToKill = {}; - for (auto it = rememberedSteps.begin(); it != rememberedSteps.end(); ++it) { - std::vector vector = it->second; - for (auto it2 = vector.begin(); it2 != vector.end(); it2++) { - if ((std::find(stepToKill.begin(), stepToKill.end(), *it2) == - stepToKill.end())) { - stepToKill.push_back(*it2); - } - } - } - - for (auto it = stepToKill.begin(); it != stepToKill.end(); ++it) { - delete *it; - } -} - -void GateLastVertexInteractionSplittingActorOld::RememberLastProcessInformation( - G4Step *step) { - - // When an interesting process to split occurs, we remember the status of the - // track and the process at this current step some informations regarding the - // track info have to be changed because they were update according to the - // interaction that occured These informations are stocked as a map object, - // binding the track ID with all the track objects and processes to split. - // Because in some cases, if a secondary was created before an interaction - // chain, this secondary will be track after the chain and without this - // association, we wll loose the information about the process occuring for - // this secondary. - - G4String creatorProcessName = "None"; - if (step->GetTrack()->GetCreatorProcess() != 0) - creatorProcessName = - step->GetTrack()->GetCreatorProcess()->GetProcessName(); - G4String processName = "None"; - G4int trackID = step->GetTrack()->GetTrackID(); - G4int parentID = step->GetTrack()->GetParentID(); - //<GetTrack()->GetParticleDefinition()->GetParticleName()<<" - //"<GetTrack()->GetTrackStatus()<GetPostStepPoint()->GetProcessDefinedStep() != 0) { - processName = - step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); - } - if ((step->GetTrack()->GetParticleDefinition()->GetParticleName() == "e+") && - ((step->GetTrack()->GetTrackStatus() == 1) || - (step->GetTrack()->GetTrackStatus() == 2))) { - processName = "annihil"; - } - - if ((std::find(fListOfProcesses.begin(), fListOfProcesses.end(), - processName) != fListOfProcesses.end())) { - G4Track *trackInformation = new G4Track(*(step->GetTrack())); - G4Step *stepInformation = new G4Step(*(step)); - - G4StepPoint *stepPoint = nullptr; - stepPoint = step->GetPreStepPoint(); - - trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy()); - if ((processName == "eBrem") || ((processName == "annihil") && - step->GetTrack()->GetTrackStatus() == 1)) { - trackInformation->SetKineticEnergy(stepPoint->GetKineticEnergy() - - step->GetTotalEnergyDeposit()); - } - trackInformation->SetMomentumDirection(stepPoint->GetMomentumDirection()); - if ((processName == "annihil") && - ((step->GetTrack()->GetTrackStatus() == 1))) - trackInformation->SetTrackStatus(fStopButAlive); - else { - trackInformation->SetTrackStatus(fAlive); - } - trackInformation->SetPolarization(stepPoint->GetPolarization()); - // trackInformation->SetPosition(stepPoint->GetPosition()); - - if (auto search = fRememberedTracks.find(trackID); - search != fRememberedTracks.end()) { - fRememberedTracks[trackID].push_back(trackInformation); - fRememberedProcesses[trackID].push_back(processName); - fRememberedSteps[trackID].push_back(stepInformation); - } - - else { - fRememberedTracks[trackID] = {trackInformation}; - fRememberedProcesses[trackID] = {processName}; - fRememberedSteps[trackID] = {stepInformation}; - } - } - - else { - if (auto search = fRememberedTracks.find(trackID); - search == fRememberedTracks.end()) { - if (auto search = fRememberedTracks.find(parentID); - search != fRememberedTracks.end()) { - if (auto it = std::find(fRememberedProcesses[parentID].begin(), - fRememberedProcesses[parentID].end(), - creatorProcessName); - it != fRememberedProcesses[parentID].end()) { - auto idx = it - fRememberedProcesses[parentID].begin(); - fRememberedTracks[trackID] = { - new G4Track(*fRememberedTracks[parentID][idx])}; - fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][idx]}; - fRememberedSteps[trackID] = { - new G4Step(*fRememberedSteps[parentID][idx])}; - } else { - fRememberedTracks[trackID] = { - new G4Track(*fRememberedTracks[parentID][0])}; - fRememberedProcesses[trackID] = {fRememberedProcesses[parentID][0]}; - fRememberedSteps[trackID] = { - new G4Step(*fRememberedSteps[parentID][0])}; - } - } - } - } -} - -void GateLastVertexInteractionSplittingActorOld::CreateNewParticleAtTheLastVertex( - G4Step *CurrentStep, G4Track *track, const G4Step *step, - G4String processName) { - // We retrieve the process associated to the process name to split and we - // split according the process. Since for compton scattering, the gamma is not - // a secondary particles, this one need to have his own splitting function. - G4ParticleDefinition *particleDefinition = track->GetDefinition(); - G4ProcessManager *processManager = particleDefinition->GetProcessManager(); - G4ProcessVector *processList = processManager->GetProcessList(); - - G4VProcess *processToSplit = nullptr; - for (size_t i = 0; i < processList->size(); ++i) { - auto process = (*processList)[i]; - if (process->GetProcessName() == processName) { - processToSplit = process; - } - } - if (processName == "compt") { - - ComptonSplitting(CurrentStep, track, step, processToSplit); - } - - else { - SecondariesSplitting(CurrentStep, track, step, processToSplit); - } -} - -void GateLastVertexInteractionSplittingActorOld:: - ResetProcessesForEnteringParticles(G4Step *step) { - - // This function reset the processes and track registered to be split each - // time a particle enters into a volume. Here a new particle incoming is - // either a particle entering into the volume ( first pre step is a boundary - // of a mother volume or first step trigger the actor and the vertex is not - // either the mother volume or a the fdaughter volume) or an event generated - // within the volume (the particle did not enter into the volume and its - // parentID = 0). An additionnal condition is set with the trackID to ensure - // particles crossing twice the volume (after either a compton or pair prod) - // are not splitted. - - G4int trackID = step->GetTrack()->GetTrackID(); - if ((fEventIDOfInitialSplittedTrack != fEventID) || - ((fEventIDOfInitialSplittedTrack == fEventID) && - (trackID < fTrackIDOfInitialTrack))) { - G4String logicalVolumeNamePreStep = "None"; - if (step->GetPreStepPoint()->GetPhysicalVolume() != 0) { - logicalVolumeNamePreStep = step->GetPreStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - } - if (((step->GetPreStepPoint()->GetStepStatus() == 1) && - (logicalVolumeNamePreStep == fMotherVolumeName)) || - ((step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != - logicalVolumeNamePreStep) && - (step->GetTrack()->GetLogicalVolumeAtVertex()->GetName() != - fMotherVolumeName))) { - fTrackIDOfInitialTrack = trackID; - fEventIDOfInitialSplittedTrack = fEventID; - fWeightOfEnteringParticle = step->GetTrack()->GetWeight(); - ClearRememberedTracksAndSteps(fRememberedTracks, fRememberedSteps); - fRememberedProcesses.clear(); - fRememberedTracks.clear(); - fRememberedSteps.clear(); - } else if (step->GetTrack()->GetParentID() == 0) { - fTrackIDOfInitialTrack = trackID; - fEventIDOfInitialSplittedTrack = fEventID; - fWeightOfEnteringParticle = step->GetTrack()->GetWeight(); - ClearRememberedTracksAndSteps(fRememberedTracks, fRememberedSteps); - fRememberedProcesses.clear(); - fRememberedTracks.clear(); - fRememberedSteps.clear(); - } - } -} - -void GateLastVertexInteractionSplittingActorOld:: - PostponeFirstAnnihilationTrackIfInteraction(G4Step *step, - G4String processName) { - // In case the first gamma issued from annihilation undergoes an interaction, - // in order to not bias the process We keep in memory the particle post step - // state (with its secondaries) and kill the particle and its secondaries. If - // the second photon from annihilation exiting the collimation system with an - // interaction or is absorbed within the collimation, the particle is - // subsequently resimulated, starting from the interaction point. - - G4int trackID = step->GetTrack()->GetTrackID(); - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), - processName) != fListOfProcesses.end()) { - fSuspendForAnnihil = true; - G4Track trackToPostpone = G4Track(*(step->GetTrack())); - trackToPostpone.SetKineticEnergy( - step->GetPostStepPoint()->GetKineticEnergy()); - trackToPostpone.SetMomentumDirection( - step->GetPostStepPoint()->GetMomentumDirection()); - trackToPostpone.SetTrackStatus(step->GetTrack()->GetTrackStatus()); - trackToPostpone.SetPolarization( - step->GetPostStepPoint()->GetPolarization()); - trackToPostpone.SetPosition(step->GetPostStepPoint()->GetPosition()); - trackToPostpone.SetTrackID(trackID); - fTracksToPostpone.push_back(trackToPostpone); - auto theTrack = fTracksToPostpone[0]; - - auto secVec = step->GetfSecondary(); - for (int i = 0; i < secVec->size(); i++) { - G4Track *sec = (*secVec)[i]; - G4Track copySec = G4Track((*sec)); - fTracksToPostpone.push_back(copySec); - } - - fTracksToPostpone[0].SetTrackID(trackID); - step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); - } -} - -void GateLastVertexInteractionSplittingActorOld:: - RegenerationOfPostponedAnnihilationTrack(G4Step *step) { - - // If the second photon from annihilation suceed to exit the collimation - // system with at least one interaction or was absorbed. Resimulation of - // annihilation photons and its potential secondaries. - - G4TrackVector *currentSecondaries = step->GetfSecondary(); - for (int i = 0; i < fTracksToPostpone.size(); i++) { - G4Track *trackToAdd = - new G4Track(fTracksToPostpone[fTracksToPostpone.size() - 1 - i]); - trackToAdd->SetParentID(step->GetTrack()->GetTrackID() - 1); - - currentSecondaries->insert(currentSecondaries->begin(), trackToAdd); - } - G4Track *firstPostponedTrack = (*currentSecondaries)[0]; - - // Handle of case were the interaction killed the photon issued from - // annihilation, it will not be track at the following state and the boolean - // plus the track vector need to be reset - - if (firstPostponedTrack->GetTrackStatus() == 2) { - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); - } -} - -void GateLastVertexInteractionSplittingActorOld:: - HandleTrackIDIfPostponedAnnihilation(G4Step *step) { - // The ID is to modify trackID and the processes and tracks sotcked for a - // specific trackID associated to the postponed annihilation to respect the - // trackID order in GEANT4. Since in this case the second photon is tracked - // before the first one his trackID +=1. For the postponed one, he is the - // first secondary particle of the second photon tracks, his trackID is - // therefore equal to the second photon trackID +1 instead of the second - // photon trackID -1. The parentID of secondary particles are also modified - // because they are used in the rememberlastprocess function At last the value - // associated to a specific trackID in the process and track map are modified - // according to the new trackID. - - if (fSuspendForAnnihil) { - if (step->GetTrack()->GetTrackID() == - fTracksToPostpone[0].GetTrackID() - 1) { - fRememberedProcesses[step->GetTrack()->GetTrackID()] = - fRememberedProcesses[step->GetTrack()->GetTrackID() + 1]; - fRememberedProcesses.erase(step->GetTrack()->GetTrackID() + 1); - fRememberedSteps[step->GetTrack()->GetTrackID()] = - fRememberedSteps[step->GetTrack()->GetTrackID() + 1]; - fRememberedSteps.erase(step->GetTrack()->GetTrackID() + 1); - fRememberedTracks[step->GetTrack()->GetTrackID()] = - fRememberedTracks[step->GetTrack()->GetTrackID() + 1]; - fRememberedTracks.erase(step->GetTrack()->GetTrackID() + 1); - auto vecSec = step->GetSecondary(); - for (int i = 0; i < vecSec->size(); i++) { - (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() + 1); - } - step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() + 1); - } - if (step->GetTrack()->GetTrackID() == - fTracksToPostpone[0].GetTrackID() + 1) { - auto vecSec = step->GetSecondary(); - for (int i = 0; i < vecSec->size(); i++) { - (*vecSec)[i]->SetParentID(step->GetTrack()->GetTrackID() - 2); - } - step->GetTrack()->SetTrackID(step->GetTrack()->GetTrackID() - 2); - } - } -} - -void GateLastVertexInteractionSplittingActorOld::BeginOfRunAction( - const G4Run *run) { - - // The way to behave of the russian roulette is the following : - // we provide a vector director and the theta angle acceptance, where theta = - // 0 is a vector colinear to the vector director Then if the track generated - // is on the acceptance angle, we add it to the primary track, and if it's not - // the case, we launch the russian roulette - - if (fRotationVectorDirector) { - G4VPhysicalVolume *physBiasingVolume = - G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); - auto rot = physBiasingVolume->GetObjectRotationValue(); - fVectorDirector = rot * fVectorDirector; - } -} - -void GateLastVertexInteractionSplittingActorOld::BeginOfEventAction( - const G4Event *event) { - - fParentID = -1; - fEventID = event->GetEventID(); - fEventIDOfSplittedTrack = -1; - fTrackIDOfSplittedTrack = -1; - fNotSplitted == true; -} - -void GateLastVertexInteractionSplittingActorOld::PreUserTrackingAction( - const G4Track *track) { - fIsFirstStep = true; -} - -void GateLastVertexInteractionSplittingActorOld::SteppingAction(G4Step *step) { - - G4String particleName = - step->GetTrack()->GetParticleDefinition()->GetParticleName(); - G4String creatorProcessName = "None"; - - if (step->GetTrack()->GetCreatorProcess() != 0) - creatorProcessName = - step->GetTrack()->GetCreatorProcess()->GetProcessName(); - - // std::cout<GetTotalEnergyDeposit()<<" - // "<GetPreStepPoint()->GetKineticEnergy()<<" - // "<GetPreStepPoint()->GetMomentumDirection()<GetTrack()->GetTrackID()<<" - // "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" - // "<GetTrack()->GetTrackID(); - - if ((step->GetTrack()->GetWeight() < fWeightOfEnteringParticle) && - (fNotSplitted == false)) { - - G4String logicalVolumeNamePostStep = "None"; - if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePostStep = step->GetPostStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - - G4String processName = "None"; - if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) - processName = - step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); - - if (particleName != "e+"){ - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), processName) != fListOfProcesses.end()){ - step->GetTrack()->SetTrackStatus(fStopAndKill); - for (int i = 0; i < step->GetfSecondary()->size(); i++){ - G4Track* track =(*(step->GetfSecondary()))[i]; - track->SetTrackStatus(fStopAndKill); - } - } - } - - - if ((particleName == "e+") && ((step->GetTrack()->GetTrackStatus() == 1)||(step->GetTrack()->GetTrackStatus() == 2))){ - step->GetTrack()->SetTrackStatus(fStopAndKill); - for (int i = 0; i < step->GetfSecondary()->size(); i++){ - G4Track* track = (*(step->GetfSecondary()))[i]; - track->SetTrackStatus(fStopAndKill); - } - } - - if ((step->GetTrack()->GetTrackStatus() == 2) || - (step->GetTrack()->GetTrackStatus() == 3)) { - - /* - delete fTrackToSplit; - delete fStepToSplit; - fTrackToSplit = new - G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); fStepToSplit - = new G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); - */ - - CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, - fProcessToSplit); - } - - if (((step->GetTrack()->GetTrackStatus() != 2) && - (step->GetTrack()->GetTrackStatus() != 3)) && - (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), - logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())) { - - if (fSplitCounter < fWeightOfEnteringParticle) { - /* - delete fTrackToSplit; - delete fStepToSplit; - fTrackToSplit = new - G4Track(*fRememberedTracks[fTrackIDOfSplittedTrack].back()); - fStepToSplit = new - G4Step(*fRememberedSteps[fTrackIDOfSplittedTrack].back()); - */ - CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, - fProcessToSplit); - // std::cout<GetfSecondary(); - G4Track *theTrack = trackVector->back(); - fSplitCounter += theTrack->GetWeight(); - // std::cout<GetWeight()<= fWeightOfEnteringParticle) { - delete fTrackToSplit; - delete fStepToSplit; - fTrackToSplit = nullptr; - fStepToSplit = nullptr; - fSplitCounter = 0; - fNotSplitted = true; - fProcessToSplit = "None"; - theTrack->SetTrackStatus(fStopAndKill); - } - } - } - } - - if (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) { - // std::cout<GetTrack()->GetTrackStatus()<GetTrack()->GetTrackStatus()<GetTrack()->GetVertexPosition()<<" - // "<GetTrack()->GetParticleDefinition()->GetParticleName()<<" - // "<GetTrack()->GetWeight()<<" "<GetTrack()->GetParentID()<<" - // "<GetTrack()->GetTrackID()<<" "<GetTrack()->GetTrackStatus()<<" - // "<GetPreStepPoint()->GetKineticEnergy()<<" " - // <GetPostStepPoint()->GetKineticEnergy()<<" - // "<GetPreStepPoint()->GetPosition()<<" - // "<GetPostStepPoint()->GetPosition()<GetTrack()->GetParentID()) && (fEventID == - fEventIDOfSplittedTrack)) - { - step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); - } - */ - - if ((particleName == "e-") && - (step->GetTrack()->GetWeight() == fWeightOfEnteringParticle) && - (creatorProcessName == "conv") && - (fTrackIDOfSplittedTrack == step->GetTrack()->GetParentID()) && - (fEventID == fEventIDOfSplittedTrack)) { - step->GetTrack()->SetTrackStatus(fStopAndKill); - } - - /* - if ((!fSuspendForAnnihil) && (process != "annihil") && (creatorProcessName - == "annihil") && (step->GetTrack()->GetTrackStatus() != 3)) - { - G4int parentID = step->GetTrack()->GetParentID(); - if (auto search = fRememberedProcesses.find(parentID); search != - fRememberedProcesses.end()) - { - if (fRememberedProcesses[trackID].back() == "annihil") - PostponeFirstAnnihilationTrackIfInteraction(step, process); - } - } - - // If the first annihilation photon exit the collimation and the process to - split is annihilation - // We kill the second photon, because the annihilation will generate both - the photons. - - if (fSuspendForAnnihil) - { - if (trackID == fTracksToPostpone[0].GetTrackID() - 1) - { - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); - } - } - */ - - G4String logicalVolumeNamePostStep = "None"; - G4ThreeVector particleMomentum = step->GetPostStepPoint()->GetMomentumDirection(); - if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePostStep = step->GetPostStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - - if (((step->GetTrack()->GetTrackStatus() != 2) && - (step->GetTrack()->GetTrackStatus() != 3)) && - (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), - logicalVolumeNamePostStep) != fListOfVolumeAncestor.end())&& (particleMomentum[2] < 0)) { - - if (auto search = fRememberedProcesses.find(trackID); - search != fRememberedProcesses.end()) { - - fProcessToSplit = fRememberedProcesses[trackID].back(); - fTrackToSplit = new G4Track(*fRememberedTracks[trackID].back()); - fStepToSplit = new G4Step(*fRememberedSteps[trackID].back()); - } - - if (std::find(fListOfProcesses.begin(), fListOfProcesses.end(), - fProcessToSplit) != fListOfProcesses.end()) { - - fTrackIDOfSplittedTrack = trackID; - fEventIDOfSplittedTrack = fEventID; - fTrackIDOfInitialSplittedTrack = fTrackIDOfInitialTrack; - - // Handle of pecularities (1): - - // If the process t split is the gamma issued from compton interaction, - // the electron primary generated have to be killed given that electron - // will be regenerated - - if ((fProcessToSplit == "compt") && (particleName == "gamma")) { - auto secondaries = step->GetfSecondary(); - if (secondaries->size() > 0) { - G4Track *lastSecTrack = secondaries->back(); - lastSecTrack->SetTrackStatus(fStopAndKill); - } - } - - // Handle of pecularities (2): - - // If the process to split is the annihilation, the second photon, - // postponed or not, have to be killed the reset of the postpone is - // performed here, whereas the kill of the next annihilation photon, if - // not postponed is realised at the beginning of the step tracking. - /* - if (fProcessToSplit == "annihil") - { - if (fSuspendForAnnihil) - { - fSuspendForAnnihil = false; - fTracksToPostpone.clear(); - } - } - */ - // Handle of pecularities 3 : If the positron which created one or more - // brem photons exits all the brems photons will be killed before their - // tracking, and the conv processes will then be replayed - - if ((particleName == "e+") && (fProcessToSplit != "None")) { - - G4int parentID = step->GetTrack()->GetParentID(); - fProcessToSplit = fRememberedProcesses[parentID].back(); - delete fTrackToSplit; - delete fStepToSplit; - fTrackToSplit = new G4Track(*fRememberedTracks[parentID].back()); - fStepToSplit = new G4Step(*fRememberedSteps[parentID].back()); - fTrackIDOfInitialTrack = parentID; - - auto *vecSecondaries = step->GetfSecondary(); - vecSecondaries->clear(); - } - if (!((fProcessToSplit == "eBrem") && (particleName == "e-"))) { - CreateNewParticleAtTheLastVertex(step, fTrackToSplit, fStepToSplit, - fProcessToSplit); - fNotSplitted = false; - } - step->GetTrack()->SetTrackStatus(fStopAndKill); - } - } - } - - /* - if ((fSuspendForAnnihil) && ((step->GetTrack()->GetTrackStatus() == 1) || - (step->GetTrack()->GetTrackStatus() == 2))) - { - if (trackID == fTracksToPostpone[0].GetTrackID()) - { - RegenerationOfPostponedAnnihilationTrack(step); - } - } - */ - - fIsFirstStep = false; -} - -void GateLastVertexInteractionSplittingActorOld::PostUserTrackingAction( - const G4Track *track) {} - -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... - -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp deleted file mode 100644 index 1afbbf25d..000000000 --- a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - ------------------------------------ -------------- */ - -#include "GateSurfaceSplittingActor.h" -#include "G4LogicalVolumeStore.hh" -#include "G4ios.hh" -#include "GateHelpers.h" -#include "GateHelpersDict.h" - -GateSurfaceSplittingActor::GateSurfaceSplittingActor(py::dict &user_info) - : GateVActor(user_info, false) { - fActions.insert("StartSimulationAction"); - fActions.insert("SteppingAction"); - fActions.insert("PreUserTrackingAction"); - fMotherVolumeName = DictGetStr(user_info, "mother"); - fWeightThreshold = DictGetBool(user_info, "weight_threshold"); - fSplittingFactor = DictGetInt(user_info, "splitting_factor"); - fSplitEnteringParticles = DictGetBool(user_info, "split_entering_particles"); - fSplitExitingParticles = DictGetBool(user_info, "split_exiting_particles"); -} - -void GateSurfaceSplittingActor::ActorInitialize() {} - -void GateSurfaceSplittingActor::StartSimulationAction() { - fNbOfKilledParticles = 0; -} - -void GateSurfaceSplittingActor::PreUserTrackingAction(const G4Track *track) { - - fIsFirstStep = true; -} - -void GateSurfaceSplittingActor::SteppingAction(G4Step *step) { - auto track = step->GetTrack(); - auto weight = track->GetWeight(); - - if (weight >= fWeightThreshold) { - if (fSplitEnteringParticles) { - G4String logicalVolumeNamePreStep = step->GetPreStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - if (((fIsFirstStep) && (step->GetPreStepPoint()->GetStepStatus() == 1) && - (logicalVolumeNamePreStep == fMotherVolumeName)) || - ((fIsFirstStep) && - (track->GetLogicalVolumeAtVertex()->GetName() != - logicalVolumeNamePreStep) && - (track->GetLogicalVolumeAtVertex()->GetName() != - fMotherVolumeName))) { - G4ThreeVector position = step->GetPreStepPoint()->GetPosition(); - G4ThreeVector momentum = step->GetPreStepPoint()->GetMomentum(); - G4double ekin = step->GetPreStepPoint()->GetKineticEnergy(); - - const G4DynamicParticle *particleType = track->GetDynamicParticle(); - G4double time = step->GetPreStepPoint()->GetGlobalTime(); - G4TrackVector *trackVector = step->GetfSecondary(); - - for (int i = 0; i < fSplittingFactor - 1; i++) { - G4DynamicParticle *particleTypeToAdd = - new G4DynamicParticle(*particleType); - G4Track *clone = new G4Track(particleTypeToAdd, time, position); - clone->SetKineticEnergy(ekin); - clone->SetMomentumDirection(momentum); - clone->SetWeight(weight / fSplittingFactor); - trackVector->push_back(clone); - } - step->GetPreStepPoint()->SetWeight(weight / fSplittingFactor); - step->GetPostStepPoint()->SetWeight(weight / fSplittingFactor); - track->SetWeight(weight / fSplittingFactor); - } - } - - if (fSplitExitingParticles) { - G4String logicalVolumeNamePostStep = step->GetPostStepPoint() - ->GetPhysicalVolume() - ->GetLogicalVolume() - ->GetName(); - if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), - logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { - G4TrackVector *trackVector = step->GetfSecondary(); - for (int i = 0; i < fSplittingFactor - 1; i++) { - G4Track *clone = new G4Track(*track); - clone->SetWeight(weight / fSplittingFactor); - trackVector->push_back(clone); - } - step->GetPostStepPoint()->SetWeight(weight / fSplittingFactor); - track->SetWeight(weight / fSplittingFactor); - } - } - } - fIsFirstStep = false; -} diff --git a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h b/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h deleted file mode 100644 index 6adce44d1..000000000 --- a/core/opengate_core/opengate_lib/GateSurfaceSplittingActor.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - -------------------------------------------------- */ - -#ifndef GateSurfaceSplittingActor_h -#define GateSurfaceSplittingActor_h - -#include "G4Cache.hh" -#include "GateVActor.h" -#include - -namespace py = pybind11; - -class GateSurfaceSplittingActor : public GateVActor { - -public: - // Constructor - GateSurfaceSplittingActor(py::dict &user_info); - - void ActorInitialize() override; - - void StartSimulationAction() override; - - // Main function called every step in attached volume - void SteppingAction(G4Step *) override; - - void PreUserTrackingAction(const G4Track *) override; - - G4bool fSplitEnteringParticles = false; - G4bool fSplitExitingParticles = false; - G4int fSplittingFactor; - G4bool fIsFirstStep; - G4bool fWeightThreshold; - G4String fMotherVolumeName; - std::vector fListOfVolumeAncestor; - - long fNbOfKilledParticles{}; -}; - -#endif diff --git a/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp b/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp deleted file mode 100644 index 0d6fc94e9..000000000 --- a/core/opengate_core/opengate_lib/pyGateSurfaceSplittingActor.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* -------------------------------------------------- - Copyright (C): OpenGATE Collaboration - This software is distributed under the terms - of the GNU Lesser General Public Licence (LGPL) - See LICENSE.md for further details - -------------------------------------------------- */ - -#include - -namespace py = pybind11; - -#include "GateSurfaceSplittingActor.h" - -void init_GateSurfaceSplittingActor(py::module &m) { - py::class_, - GateVActor>(m, "GateSurfaceSplittingActor") - .def(py::init()) - .def_readwrite("fListOfVolumeAncestor", - &GateSurfaceSplittingActor::fListOfVolumeAncestor) - .def(py::init()); -} From 4e1d204480beeecababf79d9694c8ed343a26d4f Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 13 Nov 2024 14:31:26 +0100 Subject: [PATCH 48/82] Code adaptation for gate10.09 --- core/opengate_core/opengate_core.cpp | 3 - ...ateLastVertexInteractionSplittingActor.cpp | 42 ++++----- .../GateLastVertexInteractionSplittingActor.h | 2 +- opengate/actors/miscactors.py | 93 ++++++++++++++----- opengate/managers.py | 2 + 5 files changed, 93 insertions(+), 49 deletions(-) diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index 7bcdb6cf7..222ba8c26 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -309,8 +309,6 @@ void init_GateARFTrainingDatasetActor(py::module &m); void init_GateKillActor(py::module &); -void init_GateSurfaceSplittingActor(py::module &); - void init_itk_image(py::module &); void init_GateImageNestedParameterisation(py::module &); @@ -594,7 +592,6 @@ PYBIND11_MODULE(opengate_core, m) { init_GateARFActor(m); init_GateARFTrainingDatasetActor(m); init_GateKillActor(m); - init_GateSurfaceSplittingActor(m); init_GateDigiAttributeManager(m); init_GateVDigiAttribute(m); init_GateExceptionHandler(m); diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 0bf43bac9..3d4e345e0 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -56,21 +56,21 @@ GateLastVertexInteractionSplittingActor:: GateLastVertexInteractionSplittingActor(py::dict &user_info) : GateVActor(user_info, false) { - fMotherVolumeName = DictGetStr(user_info, "mother"); - fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); - fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); - fAngularKill = DictGetBool(user_info, "angular_kill"); - fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); - fMaxTheta = DictGetDouble(user_info, "max_theta"); - fBatchSize = DictGetDouble(user_info, "batch_size"); - fActions.insert("StartSimulationAction"); - fActions.insert("SteppingAction"); - fActions.insert("BeginOfEventAction"); - fActions.insert("BeginOfRunAction"); - fActions.insert("PreUserTrackingAction"); - fActions.insert("PostUserTrackingAction"); - fActions.insert("EndOfEventAction"); + + +} + + +void GateLastVertexInteractionSplittingActor::InitializeUserInput(py::dict &user_info) { +GateVActor::InitializeUserInput(user_info); +fMotherVolumeName = DictGetStr(user_info, "attached_to"); +fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); +fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); +fAngularKill = DictGetBool(user_info, "angular_kill"); +fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); +fMaxTheta = DictGetDouble(user_info, "max_theta"); +fBatchSize = DictGetDouble(user_info, "batch_size"); } //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -500,12 +500,15 @@ G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesALossEnerg } -void GateLastVertexInteractionSplittingActor::StartSimulationAction (){ + +void GateLastVertexInteractionSplittingActor::BeginOfRunAction( + const G4Run *run) { + fListOfProcessesAccordingParticles["gamma"] = {"compt","phot","conv"}; fListOfProcessesAccordingParticles["e-"] = {"eBrem","eIoni","msc"}; fListOfProcessesAccordingParticles["e+"] = {"eBrem","eIoni","msc","annihil"}; - + std::cout<GetVolume(fMotherVolumeName); fListOfBiasedVolume.push_back(biasingVolume->GetName()); CreateListOfbiasedVolume(biasingVolume); @@ -514,14 +517,7 @@ void GateLastVertexInteractionSplittingActor::StartSimulationAction (){ fVertexSource = (GateLastVertexSource* ) source; fCosMaxTheta = std::cos(fMaxTheta); - std::cout<<"batch size "<GetStackManager(); - -} - - -void GateLastVertexInteractionSplittingActor::BeginOfRunAction( - const G4Run *run) { if (fRotationVectorDirector) { G4VPhysicalVolume *physBiasingVolume = diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index f290c08b8..dfe0ee850 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -107,7 +107,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { std::vector fListOfProcesses = {"compt", "annihil", "eBrem", "conv", "phot"}; - virtual void StartSimulationAction() override; + void InitializeUserInput(py::dict &user_info) override; virtual void SteppingAction(G4Step *) override; virtual void BeginOfEventAction(const G4Event *) override; virtual void EndOfEventAction(const G4Event *) override; diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 588a9442d..4e588a9e5 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -409,38 +409,86 @@ def initialize(self): class LastVertexInteractionSplittingActor(ActorBase,g4.GateLastVertexInteractionSplittingActor): - type_name = "LastVertexInteractionSplittingActor" - - def set_default_user_info(user_info): - ActorBase.set_default_user_info(user_info) - deg = g4_units.deg - user_info.splitting_factor = 1 - user_info.angular_kill = False - user_info.rotation_vector_director = False - user_info.vector_director = [0, 0, -1] - user_info.max_theta = 90 * deg - user_info.list_of_volume_name = [] - user_info.batch_size = 1 - def __init__(self, user_info): - ActorBase.__init__(self, user_info) - g4.GateLastVertexInteractionSplittingActor.__init__(self, user_info.__dict__) - self.list_of_volume_name = user_info.list_of_volume_name - self.user_info.mother = user_info.mother - - def initialize(self, volume_engine=None): + """This splitting actor proposes an interaction splitting at the last particle vertex before the exit + of the biased volume. This actor can be usefull for application where collimation are important, + such as in medical LINAC (Linear Accelerator) simulations or radiation shielding. + """ + + user_info_defaults = { + "splitting_factor": ( + 1, + { + "doc": "Defines the number of particles exiting at each split process. Unlike other split actors, this splitting factor counts particles that actually exit, not just those generated.", + }, + ), + + "angular_kill": ( + False, + { + "doc": "If enabled, particles with momentum outside a specified angular range are killed.", + }, + ), + + "max_theta": ( + 90 * g4_units.deg, + { + "doc": "Defines the maximum angle (in degrees) from a central axis within which particles are retained. Particles with momentum beyond this angle are removed. The angular range spans from 0 to max_theta, measured from the vector_director", + }, + ), - super().initialize(volume_engine) + + "vector_director": ( + [0, 0, 1], + { + "doc": "Specifies the reference direction vector from which max_theta is measured. Particlesโ€™ angular range is calculated based on this direction.", + }, + ), + "rotation_vector_director": ( + False, + { + "doc": "If enabled, the vector_director rotates in alignment with any rotation applied to the biased volume attached to this actor.", + }, + ), + "batch_size": ( + 1, + { + "doc": "Defines a batch of number of processes to regenerate, calculated as batch_size * splitting_factor. The optimal value depends on the collimation setup; for example, a batch_size of 10 works well for LINAC head configurations.", + }, + ), + + + } + + + def __init__(self, *args, **kwargs): + ActorBase.__init__(self, *args, **kwargs) + self.__initcpp__() + self.list_of_volume_name = [] + + def __initcpp__(self): + g4.GateLastVertexInteractionSplittingActor.__init__(self, {"name": self.name}) + self.AddActions({"BeginOfRunAction", + "BeginOfEventAction", + "PreUserTrackingAction", + "SteppingAction", + "PostUserTrackingAction", + "EndOfEventAction"}) + + + def initialize(self): + ActorBase.initialize(self) + self.InitializeUserInput(self.user_info) + self.InitializeCpp() volume_tree = self.simulation.volume_manager.get_volume_tree() dico_of_volume_tree = {} for pre, _, node in RenderTree(volume_tree): dico_of_volume_tree[str(node.name)] = node - volume_name = self.user_info.mother + volume_name = self.user_info.attached_to while volume_name != "world": node = dico_of_volume_tree[volume_name] volume_name = node.mother self.list_of_volume_name.append(volume_name) self.fListOfVolumeAncestor = self.list_of_volume_name - print(self.fListOfVolumeAncestor) class BremSplittingActor(SplittingActorBase, g4.GateBOptrBremSplittingActor): # hints for IDE @@ -473,6 +521,7 @@ def initialize(self): process_cls(ActorOutputStatisticsActor) process_cls(SimulationStatisticsActor) process_cls(KillActor) +process_cls(LastVertexInteractionSplittingActor) process_cls(SplittingActorBase) process_cls(ComptSplittingActor) process_cls(BremSplittingActor) diff --git a/opengate/managers.py b/opengate/managers.py index 0627bcc02..9f85c4a7c 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -73,6 +73,7 @@ from .actors.miscactors import ( SimulationStatisticsActor, KillActor, + LastVertexInteractionSplittingActor, SplittingActorBase, ComptSplittingActor, BremSplittingActor, @@ -118,6 +119,7 @@ "DigitizerEnergyWindowsActor": DigitizerEnergyWindowsActor, "DigitizerHitsCollectionActor": DigitizerHitsCollectionActor, "PhaseSpaceActor": PhaseSpaceActor, + "LastVertexInteractionSplittingActor":LastVertexInteractionSplittingActor, } From 326d94b58e7e662a1e35a8bc29d491b5ed5f2c7a Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 13 Nov 2024 14:32:40 +0100 Subject: [PATCH 49/82] Test creation for last vertex splitting method --- .../src/test076_last_vertex_splittting.py | 258 ------------------ .../test076_last_vertex_splittting_distrib.py | 234 ---------------- .../src/test084_last_vertex_splittting.py | 210 ++++++++++++++ ...084_last_vertex_splittting_angular_kill.py | 172 ++++++++++++ 4 files changed, 382 insertions(+), 492 deletions(-) delete mode 100755 opengate/tests/src/test076_last_vertex_splittting.py delete mode 100755 opengate/tests/src/test076_last_vertex_splittting_distrib.py create mode 100755 opengate/tests/src/test084_last_vertex_splittting.py create mode 100755 opengate/tests/src/test084_last_vertex_splittting_angular_kill.py diff --git a/opengate/tests/src/test076_last_vertex_splittting.py b/opengate/tests/src/test076_last_vertex_splittting.py deleted file mode 100755 index 078452349..000000000 --- a/opengate/tests/src/test076_last_vertex_splittting.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import opengate as gate -import uproot -import numpy as np -from scipy.spatial.transform import Rotation -from opengate.tests import utility - - -def bool_validation_test(dico_parameters, tol): - keys = dico_parameters.keys() - liste_diff_max = [] - for key in keys: - liste_diff_max.append(np.max(dico_parameters[key])) - liste_diff_max = np.asarray(liste_diff_max) - max_diff = np.max(liste_diff_max) - print( - "Maximal error (mean or std dev) measured between the analog and the biased simulation:", - np.round(max_diff, 2), - "%", - ) - if max_diff <= 100 * tol: - return True - else: - return False - - -def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): - arr_ref = arr_ref[ - (arr_ref["TrackCreatorProcess"] == "compt") - | (arr_ref["TrackCreatorProcess"] == "none") - ] - arr_data = arr_data[ - (arr_data["TrackCreatorProcess"] != "phot") - & (arr_data["TrackCreatorProcess"] != "eBrem") - & (arr_data["TrackCreatorProcess"] != "eIoni") - ] - - EventID = arr_data["EventID"] - weights = arr_data["Weight"][EventID == EventID[0]] - val_weights = np.round(weights[0], 4) - bool_val_weights = 1 / nb_split == val_weights - print( - "Sum of electron and photon weights for the first event simulated:", - np.round(np.sum(weights), 2), - ) - print("Len of the weights vector for the first event:", len(weights)) - condition_weights = np.round(np.sum(weights), 4) > 2 * ( - 1 - tol_weights - ) and np.round(np.sum(weights), 4) < 2 * (1 + tol_weights) - condition_len = len(weights) > 2 * nb_split * (1 - tol_weights) and len( - weights - ) < 2 * nb_split * (1 + tol_weights) - bool_weights = condition_weights and condition_len - keys = ["KineticEnergy", "PreDirection_X", "PreDirection_Y", "PreDirection_Z"] - - arr_ref_phot = arr_ref[arr_ref["ParticleName"] == "gamma"] - arr_ref_elec = arr_ref[arr_ref["ParticleName"] == "e-"] - - arr_data_phot = arr_data[arr_data["ParticleName"] == "gamma"] - arr_data_elec = arr_data[arr_data["ParticleName"] == "e-"] - - keys_dico = ["ref", "data"] - dico_arr_phot = {} - dico_arr_elec = {} - - dico_arr_phot["ref"] = arr_ref_phot - dico_arr_phot["data"] = arr_data_phot - - dico_arr_elec["ref"] = arr_ref_elec - dico_arr_elec["data"] = arr_data_elec - dico_comp_data = {} - - for key in keys: - arr_data = [] - for key_dico in keys_dico: - mean_elec = np.mean(dico_arr_phot[key_dico][key]) - mean_phot = np.mean(dico_arr_elec[key_dico][key]) - std_elec = np.std(dico_arr_phot[key_dico][key]) - std_phot = np.std(dico_arr_elec[key_dico][key]) - arr_data += [mean_elec, mean_phot, std_elec, std_phot] - dico_comp_data[key] = 100 * np.abs( - np.array( - [ - (arr_data[0] - arr_data[4]) / arr_data[0], - (arr_data[1] - arr_data[5]) / arr_data[1], - (arr_data[2] - arr_data[6]) / arr_data[6], - (arr_data[3] - arr_data[7]) / arr_data[3], - ] - ) - ) - bool_test = bool_validation_test(dico_comp_data, tol) - bool_tot = bool_test and bool_weights and bool_val_weights - return bool_tot - - -if __name__ == "__main__": - paths = utility.get_default_test_paths( - __file__, "test076_last_vertex_splitting", output_folder="test076" - ) - - # create the simulation - sim = gate.Simulation() - - # main options - ui = sim.user_info - ui.g4_verbose = False - ui.visu = True - ui.visu_type = "vrml" - ui.check_volumes_overlap = False - # ui.running_verbose_level = gate.logger.EVENT - ui.number_of_threads = 1 - # 1236566 seg fault - #12745 double compt - # ui.random_seed = 73 - # ui.random_seed = 2632 - # ui.random_seed = 45 - ui.random_seed = 444 - - # units - m = gate.g4_units.m - km = gate.g4_units.km - mm = gate.g4_units.mm - um = gate.g4_units.um - cm = gate.g4_units.cm - nm = gate.g4_units.nm - Bq = gate.g4_units.Bq - MeV = gate.g4_units.MeV - keV = gate.g4_units.keV - deg = gate.g4_units.deg - gcm3 = gate.g4_units.g / gate.g4_units.cm3 - - # adapt world size - world = sim.world - world.size = [0.25 * m, 0.25 * m, 0.25 * m] - world.material = "G4_Galactic" - - ####### GEOMETRY TO IRRADIATE ############# - sim.volume_manager.material_database.add_material_weights( - "Tungsten", - ["W"], - [1], - 19.3 * gcm3, - ) - - W_tubs = sim.add_volume("Tubs", "W_box") - W_tubs.material = "Tungsten" - W_tubs.mother = world.name - - W_tubs.rmin = 0 - W_tubs.rmax = 0.4*cm - W_tubs.dz = 0.05 * m - W_tubs.color = [0.8, 0.2, 0.1, 1] - angle_x = 45 - angle_y = 70 - angle_z = 80 - - rotation = Rotation.from_euler( - "xyz", [angle_y, angle_y, angle_z], degrees=True - ).as_matrix() - W_tubs.rotation = rotation - bias = True - - if bias : - ###### Last vertex Splitting ACTOR ######### - nb_split = 25 - vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") - vertex_splitting_actor.mother = W_tubs.name - vertex_splitting_actor.splitting_factor = nb_split - vertex_splitting_actor.angular_kill = True - vertex_splitting_actor.max_theta = 15*deg - - ##### PHASE SPACE plan ######" - # plan_tubs = sim.add_volume("Tubs", "phsp_tubs") - # plan_tubs.material = "G4_Galactic" - # plan_tubs.mother = world.name - # plan_tubs.rmin = W_tubs.rmax + 1*cm - # plan_tubs.rmax = plan_tubs.rmin + 1 * nm - # plan_tubs.dz = 0.05 * m - # plan_tubs.color = [0.2, 1, 0.8, 1] - # plan_tubs.rotation = rotation - - plan = sim.add_volume("Box", "plan_phsp") - plan.material = "G4_Galactic" - plan.size = [5*cm,5*cm,1*nm] - plan.translation = [0,0,-1*cm] - - - ####### gamma source ########### - source = sim.add_source("GenericSource", "source1") - source.particle = "gamma" - if bias : - source.n = 100 - else : - source.n = 10000000 - source.position.type = "sphere" - source.position.radius = 1 * nm - source.direction.type = "momentum" - # source.direction.momentum = [0,0,-1] - source.direction.momentum = np.dot(rotation, np.array([0, 0, -1])) - source.energy.type = "mono" - source.energy.mono = 4 * MeV - # - ###### LastVertexSource ############# - if bias : - source_0 = sim.add_source("LastVertexSource", "source_vertex") - source_0.n = 1 - - ####### PHASE SPACE ACTOR ############## - - phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") - phsp_actor.mother = plan.name - phsp_actor.attributes = [ - "EventID", - "TrackID", - "Weight", - "ParticleName", - "KineticEnergy", - "PreDirection", - "PrePosition", - "TrackCreatorProcess", - ] - if bias : - phsp_actor.output = paths.output / ("test076_output_data_last_vertex_osef.root") - else : - phsp_actor.output = paths.output / ("test076_output_data_last_vertex_ref.root") - - - s = sim.add_actor("SimulationStatisticsActor", "Stats") - s.track_types_flag = True - sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" - #### Extremely important, it seems that GEANT4, for almost all physics lists, encompass all the photon processes in GammaGeneralProc - #### Therefore if we provide the name of the real process (here compt) without deactivating GammaGeneralProcess, it will not find the - #### process to bias and the biasing will fail - s = f"/process/em/UseGeneralProcess false" - sim.add_g4_command_before_init(s) - - sim.physics_manager.global_production_cuts.gamma = 1 * mm - sim.physics_manager.global_production_cuts.electron = 1000 * km - sim.physics_manager.global_production_cuts.positron = 1000 * km - - output = sim.run() - - # - # print results - stats = sim.output.get_actor("Stats") - h = sim.output.get_actor("PhaseSpace") - print(stats) - # - # f_data = uproot.open(paths.output / "test071_output_data.root") - # f_ref_data = uproot.open(paths.data / "test071_ref_data.root") - # arr_data = f_data["PhaseSpace"].arrays() - # arr_ref_data = f_ref_data["PhaseSpace"].arrays() - # # - # is_ok = validation_test(arr_ref_data, arr_data, nb_split) - # utility.test_ok(is_ok) - diff --git a/opengate/tests/src/test076_last_vertex_splittting_distrib.py b/opengate/tests/src/test076_last_vertex_splittting_distrib.py deleted file mode 100755 index 087ffeb5b..000000000 --- a/opengate/tests/src/test076_last_vertex_splittting_distrib.py +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import opengate as gate -import uproot -import numpy as np -from scipy.spatial.transform import Rotation -from opengate.tests import utility - - -def bool_validation_test(dico_parameters, tol): - keys = dico_parameters.keys() - liste_diff_max = [] - for key in keys: - liste_diff_max.append(np.max(dico_parameters[key])) - liste_diff_max = np.asarray(liste_diff_max) - max_diff = np.max(liste_diff_max) - print( - "Maximal error (mean or std dev) measured between the analog and the biased simulation:", - np.round(max_diff, 2), - "%", - ) - if max_diff <= 100 * tol: - return True - else: - return False - - -def validation_test(arr_ref, arr_data, nb_split, tol=0.2, tol_weights=0.04): - arr_ref = arr_ref[ - (arr_ref["TrackCreatorProcess"] == "compt") - | (arr_ref["TrackCreatorProcess"] == "none") - ] - arr_data = arr_data[ - (arr_data["TrackCreatorProcess"] != "phot") - & (arr_data["TrackCreatorProcess"] != "eBrem") - & (arr_data["TrackCreatorProcess"] != "eIoni") - ] - - EventID = arr_data["EventID"] - weights = arr_data["Weight"][EventID == EventID[0]] - val_weights = np.round(weights[0], 4) - bool_val_weights = 1 / nb_split == val_weights - print( - "Sum of electron and photon weights for the first event simulated:", - np.round(np.sum(weights), 2), - ) - print("Len of the weights vector for the first event:", len(weights)) - condition_weights = np.round(np.sum(weights), 4) > 2 * ( - 1 - tol_weights - ) and np.round(np.sum(weights), 4) < 2 * (1 + tol_weights) - condition_len = len(weights) > 2 * nb_split * (1 - tol_weights) and len( - weights - ) < 2 * nb_split * (1 + tol_weights) - bool_weights = condition_weights and condition_len - keys = ["KineticEnergy", "PreDirection_X", "PreDirection_Y", "PreDirection_Z"] - - arr_ref_phot = arr_ref[arr_ref["ParticleName"] == "gamma"] - arr_ref_elec = arr_ref[arr_ref["ParticleName"] == "e-"] - - arr_data_phot = arr_data[arr_data["ParticleName"] == "gamma"] - arr_data_elec = arr_data[arr_data["ParticleName"] == "e-"] - - keys_dico = ["ref", "data"] - dico_arr_phot = {} - dico_arr_elec = {} - - dico_arr_phot["ref"] = arr_ref_phot - dico_arr_phot["data"] = arr_data_phot - - dico_arr_elec["ref"] = arr_ref_elec - dico_arr_elec["data"] = arr_data_elec - dico_comp_data = {} - - for key in keys: - arr_data = [] - for key_dico in keys_dico: - mean_elec = np.mean(dico_arr_phot[key_dico][key]) - mean_phot = np.mean(dico_arr_elec[key_dico][key]) - std_elec = np.std(dico_arr_phot[key_dico][key]) - std_phot = np.std(dico_arr_elec[key_dico][key]) - arr_data += [mean_elec, mean_phot, std_elec, std_phot] - dico_comp_data[key] = 100 * np.abs( - np.array( - [ - (arr_data[0] - arr_data[4]) / arr_data[0], - (arr_data[1] - arr_data[5]) / arr_data[1], - (arr_data[2] - arr_data[6]) / arr_data[6], - (arr_data[3] - arr_data[7]) / arr_data[3], - ] - ) - ) - bool_test = bool_validation_test(dico_comp_data, tol) - bool_tot = bool_test and bool_weights and bool_val_weights - return bool_tot - - -if __name__ == "__main__": - paths = utility.get_default_test_paths( - __file__, "test076_last_vertex_splitting", output_folder="test076" - ) - for simu_ID in range(1,2) : - # create the simulation - sim = gate.Simulation() - - # main options - ui = sim.user_info - ui.g4_verbose = False - # ui.visu = True - ui.visu_type = "vrml" - ui.check_volumes_overlap = False - # ui.running_verbose_level = gate.logger.EVENT - ui.number_of_threads = 1 - # 1236566 seg fault - ui.random_seed = 1234567 - - # units - m = gate.g4_units.m - km = gate.g4_units.km - mm = gate.g4_units.mm - um = gate.g4_units.um - cm = gate.g4_units.cm - nm = gate.g4_units.nm - Bq = gate.g4_units.Bq - MeV = gate.g4_units.MeV - keV = gate.g4_units.keV - deg = gate.g4_units.deg - gcm3 = gate.g4_units.g / gate.g4_units.cm3 - - # adapt world size - world = sim.world - world.size = [0.25 * m, 0.25 * m, 1.5 * m] - world.material = "G4_Galactic" - - ####### GEOMETRY TO IRRADIATE ############# - sim.volume_manager.material_database.add_material_weights( - "Tungsten", - ["W"], - [1], - 19.3 * gcm3, - ) - - W_tubs = sim.add_volume("Tubs", "W_box") - W_tubs.material = "Tungsten" - W_tubs.mother = world.name - - W_tubs.rmin = 0 - W_tubs.rmax = 1* cm - W_tubs.dz = 0.5 * m - W_tubs.color = [0.8, 0.2, 0.1, 1] - angle_x = 0 - angle_y = 0 - angle_z = 0 - - rotation = Rotation.from_euler( - "xyz", [angle_y, angle_y, angle_z], degrees=True - ).as_matrix() - W_tubs.rotation = rotation - if simu_ID == 1 : - ####### Last vertex splitting ACTOR ######### - nb_split = 1 - vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") - vertex_splitting_actor.mother = W_tubs.name - vertex_splitting_actor.splitting_factor = nb_split - vertex_splitting_actor.russian_roulette_for_angle = False - - ##### PHASE SPACE plan ######" - plan_tubs = sim.add_volume("Tubs", "phsp_tubs") - plan_tubs.material = "G4_Galactic" - plan_tubs.mother = world.name - plan_tubs.rmin = W_tubs.rmax + 1*cm - plan_tubs.rmax = plan_tubs.rmin + 1 * nm - plan_tubs.dz = 0.05 * m - plan_tubs.color = [0.2, 1, 0.8, 1] - plan_tubs.rotation = rotation - - ####### Electron source ########### - source = sim.add_source("GenericSource", "source1") - source.particle = "gamma" - source.n = 1000000 - if simu_ID == 1 : - source.n = source.n/(nb_split) - source.position.type = "sphere" - source.position.radius = 1 * nm - source.direction.type = "momentum" - # source.direction.momentum = [0,0,-1] - source.direction.momentum = np.dot(rotation, np.array([0, 0, -1])) - source.energy.type = "mono" - source.position.translation = [0.4*cm,0.4*cm,0] - source.force_rotation=True - source.energy.mono = 4* MeV - - ####### PHASE SPACE ACTOR ############## - - phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") - phsp_actor.mother = plan_tubs.name - phsp_actor.attributes = [ - "EventID", - "TrackID", - "Weight", - "ParticleName", - "KineticEnergy", - "PreDirection", - "TrackCreatorProcess", - ] - if simu_ID == 0: - phsp_actor.output = paths.output / "test076_output_data_ref.root" - else : - phsp_actor.output = paths.output / "test076_output_data_split.root" - - ##### MODIFIED PHYSICS LIST ############### - - s = sim.add_actor("SimulationStatisticsActor", "Stats") - s.track_types_flag = True - sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option2" - #### Extremely important, it seems that GEANT4, for almost all physics lists, encompass all the photon processes in GammaGeneralProc - #### Therefore if we provide the name of the real process (here compt) without deactivating GammaGeneralProcess, it will not find the - #### process to bias and the biasing will fail - s = f"/process/em/UseGeneralProcess false" - sim.add_g4_command_before_init(s) - - sim.physics_manager.global_production_cuts.gamma = 1 * mm - sim.physics_manager.global_production_cuts.electron = 1 * m - sim.physics_manager.global_production_cuts.positron = 1 * um - - output = sim.run() - - # - # print results - stats = sim.output.get_actor("Stats") - h = sim.output.get_actor("PhaseSpace") - print(stats) - - diff --git a/opengate/tests/src/test084_last_vertex_splittting.py b/opengate/tests/src/test084_last_vertex_splittting.py new file mode 100755 index 000000000..956520b59 --- /dev/null +++ b/opengate/tests/src/test084_last_vertex_splittting.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +import uproot +import numpy as np +from scipy.spatial.transform import Rotation +from opengate.tests import utility + + + +def validation_test(arr_ref, arr_data, nb_split): + arr_ref = arr_ref[arr_ref["ParticleName"] == "gamma"] + arr_data = arr_data[arr_data["ParticleName"] == "gamma"] + + weight_data = np.round(np.mean(arr_data["Weight"]),4) + bool_weight = False + print("Weight mean is equal to",weight_data, "and need to be equal to",1/nb_split) + if weight_data == 1/nb_split: + bool_weight = True + + + bool_events = False + sigma = np.sqrt((len(arr_data["KineticEnergy"])/nb_split))*nb_split + nb_events_ref = len(arr_ref["KineticEnergy"]) + nb_events_data = len(arr_data["KineticEnergy"]) + print("Reference counts number:",nb_events_ref) + print("Biased counts number:", nb_events_data) + if nb_events_data - 4*sigma <= nb_events_ref <= nb_events_data + 4*sigma: + bool_events =True + + keys = ["KineticEnergy", "PreDirection_X","PreDirection_Y","PreDirection_Z","PrePosition_X","PrePosition_Y"] + + bool_distrib = True + for key in keys : + ref = arr_ref[key] + data = arr_data[key] + + mean_ref = np.mean(ref) + mean_data = np.mean(data) + + std_dev_ref = np.std(ref,ddof =1) + std_dev_data = np.std(data,ddof =1) + + std_err_ref = std_dev_ref/np.sqrt(len(ref)) + std_err_data= std_dev_data/(nb_split * np.sqrt(len(data)/nb_split)) + + print(key,"mean ref value:", np.round(mean_ref,3),"+-",np.round(std_err_ref,3)) + print(key,"mean data value:", np.round(mean_data,3),"+-",np.round(std_err_data,3)) + + + if (mean_data - 4 * np.sqrt(std_err_data**2 + std_err_ref**2) > mean_ref) or (mean_data + 4 * np.sqrt(std_err_data**2 + std_err_ref**2) max_theta])) + if len(l_theta[l_theta > max_theta]) == 0: + return True + else: + return False + + + +if __name__ == "__main__": + bias = True + paths = utility.get_default_test_paths( + __file__, "test084_last_vertex_splitting", output_folder="test084" + ) + + # create the simulation + sim = gate.Simulation() + + # main options + ui = sim.user_info + ui.g4_verbose = False + ui.visu = False + ui.visu_type = "vrml" + ui.check_volumes_overlap = False + ui.number_of_threads = 1 + ui.random_seed = 123456789 + + # units + m = gate.g4_units.m + km = gate.g4_units.km + mm = gate.g4_units.mm + um = gate.g4_units.um + cm = gate.g4_units.cm + nm = gate.g4_units.nm + Bq = gate.g4_units.Bq + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + deg = gate.g4_units.deg + gcm3 = gate.g4_units.g / gate.g4_units.cm3 + + # adapt world size + world = sim.world + world.size = [0.25 * m, 0.25 * m, 0.25 * m] + world.material = "G4_Galactic" + + ####### GEOMETRY TO IRRADIATE ############# + sim.volume_manager.material_database.add_material_weights( + "Tungsten", + ["W"], + [1], + 19.3 * gcm3, + ) + + W_tubs = sim.add_volume("Tubs", "W_box") + W_tubs.material = "Tungsten" + W_tubs.mother = world.name + + W_tubs.rmin = 0 + W_tubs.rmax = 0.4*cm + W_tubs.dz = 0.05 * m + W_tubs.color = [0.8, 0.2, 0.1, 1] + angle_x = 45 + angle_y = 70 + angle_z = 80 + + rotation = Rotation.from_euler( + "xyz", [angle_y, angle_y, angle_z], degrees=True + ).as_matrix() + W_tubs.rotation = rotation + + if bias : + ###### Last vertex Splitting ACTOR ######### + nb_split = 10 + vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") + vertex_splitting_actor.attached_to = W_tubs.name + vertex_splitting_actor.splitting_factor = nb_split + vertex_splitting_actor.angular_kill = True + vertex_splitting_actor.vector_director = [0,0,-1] + vertex_splitting_actor.max_theta = 15*deg + vertex_splitting_actor.batch_size = 10 + + + plan = sim.add_volume("Box", "plan_phsp") + plan.material = "G4_Galactic" + plan.size = [5*cm,5*cm,1*nm] + plan.translation = [0,0,-1*cm] + + if bias : + vector_director = np.array(vertex_splitting_actor.vector_director) + + + + + ####### gamma source ########### + source = sim.add_source("GenericSource", "source1") + source.particle = "gamma" + source.n = 100000 + if bias : + source.n = source.n/nb_split + + + source.position.type = "sphere" + source.position.radius = 1 * nm + source.direction.type = "momentum" + # source.direction.momentum = [0,0,-1] + source.direction.momentum = np.dot(rotation, np.array([0, 0, -1])) + source.energy.type = "mono" + source.energy.mono = 4 * MeV + + ###### LastVertexSource ############# + if bias : + source_0 = sim.add_source("LastVertexSource", "source_vertex") + source_0.n = 1 + + ####### PHASE SPACE ACTOR ############## + sim.output_dir =paths.output + phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") + phsp_actor.attached_to = plan.name + phsp_actor.attributes = [ + "EventID", + "TrackID", + "Weight", + "ParticleName", + "KineticEnergy", + "PreDirection", + "PrePosition", + "TrackCreatorProcess", + ] + if bias : + phsp_actor.output_filename ="test084_output_data_last_vertex_angular_kill.root" + + s = sim.add_actor("SimulationStatisticsActor", "Stats") + s.track_types_flag = True + sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" + s = f"/process/em/UseGeneralProcess false" + sim.g4_commands_before_init.append(s) + + sim.physics_manager.global_production_cuts.gamma = 1 * mm + sim.physics_manager.global_production_cuts.electron = 1000 * km + sim.physics_manager.global_production_cuts.positron = 1000 * km + + output = sim.run() + print(s) + + f_data = uproot.open(paths.output / "test084_output_data_last_vertex_angular_kill.root") + arr_data = f_data["PhaseSpace"].arrays() + is_ok = validation_test(arr_data, vector_director,vertex_splitting_actor.max_theta) + utility.test_ok(is_ok) + From 7be2414712c92bb4b21fbfa68383c1921f8c919a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:33:53 +0000 Subject: [PATCH 50/82] [pre-commit.ci] Automatic python and c++ formatting --- .../opengate_lib/GateGenericSource.cpp | 4 +- ...ateLastVertexInteractionSplittingActor.cpp | 676 +- .../GateLastVertexInteractionSplittingActor.h | 73 +- .../opengate_lib/GateLastVertexSource.cpp | 59 +- .../opengate_lib/GateLastVertexSource.h | 37 +- .../GateLastVertexSplittingDataContainer.h | 191 +- .../GateLastVertexSplittingPostStepDoIt.h | 123 +- .../GateLastVertexSplittingSimpleContainer.h | 238 +- .../opengate_lib/GateOptnForceFreeFlight.cpp | 8 +- .../GateOptnVGenericSplitting.cpp | 70 +- .../opengate_lib/GateOptnVGenericSplitting.h | 21 +- ...GateOptrComptPseudoTransportationActor.cpp | 80 +- .../GateOptrComptPseudoTransportationActor.h | 7 +- .../opengate_lib/GateSourceManager.cpp | 3 - .../opengate_lib/GateSourceManager.h | 13 +- ...ateLastVertexInteractionSplittingActor.cpp | 5 +- core/opengate_core/opengate_lib/tree.hh | 6222 +++++++++-------- core/opengate_core/opengate_lib/tree_util.hh | 108 +- opengate/actors/miscactors.py | 35 +- opengate/managers.py | 11 +- .../src/test077_kill_interacting_particles.py | 3 - .../src/test084_last_vertex_splittting.py | 110 +- ...084_last_vertex_splittting_angular_kill.py | 63 +- 23 files changed, 4093 insertions(+), 4067 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateGenericSource.cpp b/core/opengate_core/opengate_lib/GateGenericSource.cpp index 74d7e743a..a00707865 100644 --- a/core/opengate_core/opengate_lib/GateGenericSource.cpp +++ b/core/opengate_core/opengate_lib/GateGenericSource.cpp @@ -151,12 +151,12 @@ double GateGenericSource::PrepareNextTime(double current_simulation_time) { if (fMaxN <= 0) { if (fEffectiveEventTime < fStartTime) return fStartTime; - if (fEffectiveEventTime >= fEndTime){ + if (fEffectiveEventTime >= fEndTime) { return -1; } // get next time according to current fActivity double next_time = CalcNextTime(fEffectiveEventTime); - if (next_time >= fEndTime){ + if (next_time >= fEndTime) { return -1; } return next_time; diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp index 3d4e345e0..23f24c1d1 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -39,69 +39,70 @@ #include "G4Positron.hh" #include "G4ProcessManager.hh" #include "G4ProcessVector.hh" +#include "G4RunManager.hh" #include "G4TrackStatus.hh" #include "G4TrackingManager.hh" #include "G4VParticleChange.hh" #include "G4eplusAnnihilation.hh" #include "GateLastVertexInteractionSplittingActor.h" +#include "GateLastVertexSource.h" #include "GateLastVertexSplittingPostStepDoIt.h" #include "GateOptnComptSplitting.h" -#include "GateLastVertexSource.h" -#include "G4RunManager.hh" #include - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... GateLastVertexInteractionSplittingActor:: GateLastVertexInteractionSplittingActor(py::dict &user_info) - : GateVActor(user_info, false) { - - - + : GateVActor(user_info, false) {} + +void GateLastVertexInteractionSplittingActor::InitializeUserInput( + py::dict &user_info) { + GateVActor::InitializeUserInput(user_info); + fMotherVolumeName = DictGetStr(user_info, "attached_to"); + fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); + fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); + fAngularKill = DictGetBool(user_info, "angular_kill"); + fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); + fMaxTheta = DictGetDouble(user_info, "max_theta"); + fBatchSize = DictGetDouble(user_info, "batch_size"); } +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -void GateLastVertexInteractionSplittingActor::InitializeUserInput(py::dict &user_info) { -GateVActor::InitializeUserInput(user_info); -fMotherVolumeName = DictGetStr(user_info, "attached_to"); -fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); -fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); -fAngularKill = DictGetBool(user_info, "angular_kill"); -fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); -fMaxTheta = DictGetDouble(user_info, "max_theta"); -fBatchSize = DictGetDouble(user_info, "batch_size"); +void GateLastVertexInteractionSplittingActor::print_tree( + const tree &tr, + tree::pre_order_iterator it, + tree::pre_order_iterator end) { + if (!tr.is_valid(it)) + return; + int rootdepth = tr.depth(it); + std::cout << "-----" << std::endl; + while (it != end) { + for (int i = 0; i < tr.depth(it) - rootdepth; ++i) + std::cout << " "; + std::cout << (*it) << std::endl << std::flush; + ++it; + } + std::cout << "-----" << std::endl; } -//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... - -void GateLastVertexInteractionSplittingActor::print_tree(const tree& tr, tree::pre_order_iterator it, tree::pre_order_iterator end) - { - if(!tr.is_valid(it)) return; - int rootdepth=tr.depth(it); - std::cout << "-----" << std::endl; - while(it!=end) { - for(int i=0; iFindParticle(particleName); +G4VProcess *GateLastVertexInteractionSplittingActor::GetProcessFromProcessName( + G4String particleName, G4String pName) { + auto *particle_table = G4ParticleTable::GetParticleTable(); + G4ParticleDefinition *particleDefinition = + particle_table->FindParticle(particleName); G4ProcessManager *processManager = particleDefinition->GetProcessManager(); G4ProcessVector *processList = processManager->GetProcessList(); - G4VProcess* nullProcess = nullptr; + G4VProcess *nullProcess = nullptr; for (size_t i = 0; i < processList->size(); ++i) { auto process = (*processList)[i]; if (process->GetProcessName() == pName) { @@ -109,49 +110,49 @@ G4VProcess* GateLastVertexInteractionSplittingActor::GetProcessFromProcessName(G } } return nullProcess; - } -G4Track* GateLastVertexInteractionSplittingActor::CreateATrackFromContainer(LastVertexDataContainer theContainer){ +G4Track *GateLastVertexInteractionSplittingActor::CreateATrackFromContainer( + LastVertexDataContainer theContainer) { auto *particle_table = G4ParticleTable::GetParticleTable(); SimpleContainer container = theContainer.GetContainerToSplit(); - if (container.GetParticleNameToSplit() != "None"){ - G4ParticleDefinition *particleDefinition = particle_table->FindParticle(container.GetParticleNameToSplit()); + if (container.GetParticleNameToSplit() != "None") { + G4ParticleDefinition *particleDefinition = + particle_table->FindParticle(container.GetParticleNameToSplit()); G4ThreeVector momentum = container.GetMomentum(); G4double energy = container.GetEnergy(); - if (energy <0){ + if (energy < 0) { energy = 0; - momentum = {0,0,0}; + momentum = {0, 0, 0}; } G4int trackStatus = container.GetTrackStatus(); G4ThreeVector position = container.GetVertexPosition(); G4ThreeVector polarization = container.GetPolarization(); - G4DynamicParticle* dynamicParticle = new G4DynamicParticle(particleDefinition,momentum,energy); + G4DynamicParticle *dynamicParticle = + new G4DynamicParticle(particleDefinition, momentum, energy); G4double time = 0; - G4Track* aTrack = new G4Track(dynamicParticle,time, position); + G4Track *aTrack = new G4Track(dynamicParticle, time, position); aTrack->SetPolarization(polarization); - if (trackStatus == 0){ + if (trackStatus == 0) { aTrack->SetTrackStatus(fAlive); } - if (trackStatus == 1){ + if (trackStatus == 1) { aTrack->SetTrackStatus(fStopButAlive); } - if ((trackStatus == 2) || (trackStatus == 3)){ + if ((trackStatus == 2) || (trackStatus == 3)) { aTrack->SetTrackStatus(fAlive); } aTrack->SetWeight(container.GetWeight()); return aTrack; } - - return nullptr; - + return nullptr; } +G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack( + G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) { -G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) { - G4double energy = gammaProcess->GetProposedKineticEnergy(); G4double globalTime = track.GetGlobalTime(); G4ThreeVector polarization = gammaProcess->GetProposedPolarization(); @@ -167,56 +168,68 @@ G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleC return newTrack; } -void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process, LastVertexDataContainer container, G4double batchSize) { +void GateLastVertexInteractionSplittingActor::ComptonSplitting( + G4Step *initStep, G4Step *CurrentStep, G4VProcess *process, + LastVertexDataContainer container, G4double batchSize) { - //G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + // G4TrackVector *trackVector = CurrentStep->GetfSecondary(); GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; - for (int i = 0; i < batchSize; i++){ - G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); - G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; + for (int i = 0; i < batchSize; i++) { + G4VParticleChange *processFinalState = + emProcess->PostStepDoIt(*fTrackToSplit, *initStep); + G4ParticleChangeForGamma *gammaProcessFinalState = + (G4ParticleChangeForGamma *)processFinalState; - G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); + G4ThreeVector momentum = + gammaProcessFinalState->GetProposedMomentumDirection(); - G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *fTrackToSplit, fWeight); + G4Track *newTrack = + CreateComptonTrack(gammaProcessFinalState, *fTrackToSplit, fWeight); - if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector) == false)){ + if ((fAngularKill) && + (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(), + fVectorDirector) == false)) { delete newTrack; - } - else{ + } else { fStackManager->PushOneTrack(newTrack); } + // Special case here, since we generate independently each particle, we will + // not attach an electron to exiting compton photon, but we will the + // secondaries. - // Special case here, since we generate independently each particle, we will not attach an electron to exiting compton photon, but we will the secondaries. - - - if (processFinalState->GetNumberOfSecondaries()> 0){ + if (processFinalState->GetNumberOfSecondaries() > 0) { delete processFinalState->GetSecondary(0); } - processFinalState->Clear(); gammaProcessFinalState->Clear(); } } - - - -G4Track GateLastVertexInteractionSplittingActor::eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process){ - //It seem's that the the along step method apply only to brem results to no deposited energy but a change in momentum direction according to the process - //Whereas the along step method applied to the ionisation well change the deposited energy but not the momentum. Then I apply both to have a correct - //momentum and deposited energy before the brem effect. +G4Track GateLastVertexInteractionSplittingActor::eBremProcessFinalState( + G4Track *track, G4Step *step, G4VProcess *process) { + // It seem's that the the along step method apply only to brem results to no + // deposited energy but a change in momentum direction according to the + // process Whereas the along step method applied to the ionisation well change + // the deposited energy but not the momentum. Then I apply both to have a + // correct momentum and deposited energy before the brem effect. G4String particleName = track->GetDefinition()->GetParticleName(); - G4VProcess* eIoniProcess = GetProcessFromProcessName(particleName, "eIoni"); - G4VProcess* eBremProcess = GetProcessFromProcessName(particleName, "eBrem"); - G4VParticleChange* eIoniProcessAlongState = eIoniProcess->AlongStepDoIt(*track, *step); - G4VParticleChange* eBremProcessAlongState = eBremProcess->AlongStepDoIt(*track, *step); - G4ParticleChangeForLoss* eIoniProcessAlongStateForLoss = (G4ParticleChangeForLoss*) eIoniProcessAlongState; - G4ParticleChangeForLoss* eBremProcessAlongStateForLoss = (G4ParticleChangeForLoss*) eBremProcessAlongState; + G4VProcess *eIoniProcess = GetProcessFromProcessName(particleName, "eIoni"); + G4VProcess *eBremProcess = GetProcessFromProcessName(particleName, "eBrem"); + G4VParticleChange *eIoniProcessAlongState = + eIoniProcess->AlongStepDoIt(*track, *step); + G4VParticleChange *eBremProcessAlongState = + eBremProcess->AlongStepDoIt(*track, *step); + G4ParticleChangeForLoss *eIoniProcessAlongStateForLoss = + (G4ParticleChangeForLoss *)eIoniProcessAlongState; + G4ParticleChangeForLoss *eBremProcessAlongStateForLoss = + (G4ParticleChangeForLoss *)eBremProcessAlongState; G4double LossEnergy = eIoniProcessAlongStateForLoss->GetLocalEnergyDeposit(); - G4ThreeVector momentum = eBremProcessAlongStateForLoss->GetProposedMomentumDirection(); - G4ThreeVector polarization = eBremProcessAlongStateForLoss->GetProposedPolarization(); + G4ThreeVector momentum = + eBremProcessAlongStateForLoss->GetProposedMomentumDirection(); + G4ThreeVector polarization = + eBremProcessAlongStateForLoss->GetProposedPolarization(); G4Track aTrack = G4Track(*track); aTrack.SetKineticEnergy(track->GetKineticEnergy() - LossEnergy); @@ -225,83 +238,92 @@ G4Track GateLastVertexInteractionSplittingActor::eBremProcessFinalState(G4Track* eIoniProcessAlongState->Clear(); eBremProcessAlongState->Clear(); return aTrack; - } - -void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer theContainer, G4double batchSize) { +void GateLastVertexInteractionSplittingActor::SecondariesSplitting( + G4Step *initStep, G4Step *CurrentStep, G4VProcess *process, + LastVertexDataContainer theContainer, G4double batchSize) { SimpleContainer container = theContainer.GetContainerToSplit(); - G4String particleName = fTrackToSplit->GetParticleDefinition()->GetParticleName(); - //G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + G4String particleName = + fTrackToSplit->GetParticleDefinition()->GetParticleName(); + // G4TrackVector *trackVector = CurrentStep->GetfSecondary(); G4VParticleChange *processFinalState = nullptr; - GateBremPostStepDoIt* bremProcess = nullptr; + GateBremPostStepDoIt *bremProcess = nullptr; GateGammaEmPostStepDoIt *emProcess = nullptr; GateplusannihilAtRestDoIt *eplusAnnihilProcess = nullptr; - if ((container.GetAnnihilationFlag() == "PostStep") && (fTrackToSplit->GetKineticEnergy() > 0)){ + if ((container.GetAnnihilationFlag() == "PostStep") && + (fTrackToSplit->GetKineticEnergy() > 0)) { emProcess = (GateGammaEmPostStepDoIt *)process; } - if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { + if ((container.GetAnnihilationFlag() == "AtRest") || + (fTrackToSplit->GetKineticEnergy() == 0)) { eplusAnnihilProcess = (GateplusannihilAtRestDoIt *)process; } - for (int j = 0; j < batchSize;j++){ + for (int j = 0; j < batchSize; j++) { G4int NbOfSecondaries = 0; - - G4int count =0; - while (NbOfSecondaries == 0){ + + G4int count = 0; + while (NbOfSecondaries == 0) { if (process->GetProcessName() == "eBrem") { - G4Track aTrack = eBremProcessFinalState(fTrackToSplit,initStep,process); - bremProcess = (GateBremPostStepDoIt*) process; - processFinalState = bremProcess->GateBremPostStepDoIt::PostStepDoIt(aTrack, *initStep); - } - else { - if ((container.GetAnnihilationFlag() == "PostStep") && (fTrackToSplit->GetKineticEnergy() > 0)){ - processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); + G4Track aTrack = + eBremProcessFinalState(fTrackToSplit, initStep, process); + bremProcess = (GateBremPostStepDoIt *)process; + processFinalState = + bremProcess->GateBremPostStepDoIt::PostStepDoIt(aTrack, *initStep); + } else { + if ((container.GetAnnihilationFlag() == "PostStep") && + (fTrackToSplit->GetKineticEnergy() > 0)) { + processFinalState = + emProcess->PostStepDoIt(*fTrackToSplit, *initStep); } - if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { - processFinalState = eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*fTrackToSplit,*initStep); + if ((container.GetAnnihilationFlag() == "AtRest") || + (fTrackToSplit->GetKineticEnergy() == 0)) { + processFinalState = + eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt( + *fTrackToSplit, *initStep); } } NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); - if (NbOfSecondaries == 0){ + if (NbOfSecondaries == 0) { processFinalState->Clear(); } - count ++; - //Security break, in case of infinite loop - if (count > 10000){ + count++; + // Security break, in case of infinite loop + if (count > 10000) { G4ExceptionDescription ed; - ed << " infinite loop detected during the track creation for the " <GetProcessName() <<" process"<GetProcessName() << " process" << G4endl; + G4Exception( + "GateLastVertexInteractionSplittingActor::SecondariesSplitting", + "BIAS.LV1", JustWarning, ed); G4RunManager::GetRunManager()->AbortEvent(); break; } } G4int idx = 0; - G4bool IsPushBack =false; - for (int i=0; i < NbOfSecondaries; i++){ + G4bool IsPushBack = false; + for (int i = 0; i < NbOfSecondaries; i++) { G4Track *newTrack = processFinalState->GetSecondary(i); G4ThreeVector momentum = newTrack->GetMomentumDirection(); - - if (!(isnan(momentum[0]))){ - if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector) == false)){ + + if (!(isnan(momentum[0]))) { + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle( + momentum, fVectorDirector) == false)) { delete newTrack; - } - else if (IsPushBack == true){ + } else if (IsPushBack == true) { delete newTrack; - } - else { + } else { newTrack->SetWeight(fWeight); newTrack->SetCreatorProcess(process); - //trackVector->emplace_back(newTrack); + // trackVector->emplace_back(newTrack); fStackManager->PushOneTrack(newTrack); - //delete newTrack; - IsPushBack=true; - + // delete newTrack; + IsPushBack = true; } - } - else { + } else { delete newTrack; } } @@ -309,60 +331,68 @@ void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initS } } - -void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* initStep,G4Step *step,LastVertexDataContainer theContainer, G4double batchSize) { +void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex( + G4Step *initStep, G4Step *step, LastVertexDataContainer theContainer, + G4double batchSize) { // We retrieve the process associated to the process name to split and we // split according the process. Since for compton scattering, the gamma is not // a secondary particles, this one need to have his own splitting function. - G4String processName = fProcessNameToSplit; - G4int nbOfTrackAlreadyInStack = fStackManager->GetNTotalTrack(); - if ((fProcessToSplit == 0) || (fProcessToSplit == nullptr)){ - SimpleContainer container = theContainer.GetContainerToSplit(); - fProcessToSplit = GetProcessFromProcessName(container.GetParticleNameToSplit(),processName); - } - - if (processName == "compt") { - ComptonSplitting(initStep,step, fProcessToSplit, theContainer, batchSize); - } + G4String processName = fProcessNameToSplit; + G4int nbOfTrackAlreadyInStack = fStackManager->GetNTotalTrack(); + if ((fProcessToSplit == 0) || (fProcessToSplit == nullptr)) { + SimpleContainer container = theContainer.GetContainerToSplit(); + fProcessToSplit = GetProcessFromProcessName( + container.GetParticleNameToSplit(), processName); + } - else if((processName != "msc") && (processName != "conv")){ - SecondariesSplitting(initStep, step, fProcessToSplit, theContainer,batchSize); - } - fNumberOfTrackToSimulate = fStackManager->GetNTotalTrack() - nbOfTrackAlreadyInStack; - fNbOfBatchForExitingParticle ++; - if (fNbOfBatchForExitingParticle >500){ - fStackManager->clear(); - } - //stackManager->clear(); + if (processName == "compt") { + ComptonSplitting(initStep, step, fProcessToSplit, theContainer, batchSize); + } + else if ((processName != "msc") && (processName != "conv")) { + SecondariesSplitting(initStep, step, fProcessToSplit, theContainer, + batchSize); + } + fNumberOfTrackToSimulate = + fStackManager->GetNTotalTrack() - nbOfTrackAlreadyInStack; + fNbOfBatchForExitingParticle++; + if (fNbOfBatchForExitingParticle > 500) { + fStackManager->clear(); + } + // stackManager->clear(); } - -void GateLastVertexInteractionSplittingActor::CreateListOfbiasedVolume(G4LogicalVolume *volume) { +void GateLastVertexInteractionSplittingActor::CreateListOfbiasedVolume( + G4LogicalVolume *volume) { G4int nbOfDaughters = volume->GetNoDaughters(); if (nbOfDaughters > 0) { for (int i = 0; i < nbOfDaughters; i++) { - G4String LogicalVolumeName = volume->GetDaughter(i)->GetLogicalVolume()->GetName(); - G4LogicalVolume *logicalDaughtersVolume = volume->GetDaughter(i)->GetLogicalVolume(); - if (!(std::find(fListOfBiasedVolume.begin(),fListOfBiasedVolume.end(),LogicalVolumeName) != fListOfBiasedVolume.end())) - fListOfBiasedVolume.push_back(volume->GetDaughter(i)->GetLogicalVolume()->GetName()); + G4String LogicalVolumeName = + volume->GetDaughter(i)->GetLogicalVolume()->GetName(); + G4LogicalVolume *logicalDaughtersVolume = + volume->GetDaughter(i)->GetLogicalVolume(); + if (!(std::find(fListOfBiasedVolume.begin(), fListOfBiasedVolume.end(), + LogicalVolumeName) != fListOfBiasedVolume.end())) + fListOfBiasedVolume.push_back( + volume->GetDaughter(i)->GetLogicalVolume()->GetName()); CreateListOfbiasedVolume(logicalDaughtersVolume); } } } - -void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ +void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step *step) { G4String processName = "None"; if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) - processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); - + processName = + step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + G4String creatorProcessName = "None"; if (step->GetTrack()->GetCreatorProcess() != 0) - creatorProcessName =step->GetTrack()->GetCreatorProcess()->GetProcessName(); - + creatorProcessName = + step->GetTrack()->GetCreatorProcess()->GetProcessName(); + if ((step->GetTrack()->GetParticleDefinition()->GetParticleName() == "e+") && ((step->GetTrack()->GetTrackStatus() == 1) || (step->GetTrack()->GetTrackStatus() == 2))) { @@ -370,151 +400,154 @@ void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ } G4String annihilFlag = "None"; - if (processName == "annihil"){ - if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0){ - if (processName == step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ + if (processName == "annihil") { + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) { + if (processName == + step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()) { annihilFlag = "PostStep"; - } - else if (processName != step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ - annihilFlag ="AtRest"; + } else if (processName != step->GetPostStepPoint() + ->GetProcessDefinedStep() + ->GetProcessName()) { + annihilFlag = "AtRest"; } } } - - - if (fIsFirstStep){ + if (fIsFirstStep) { LastVertexDataContainer newContainer = LastVertexDataContainer(); newContainer.SetTrackID(step->GetTrack()->GetTrackID()); - newContainer.SetParticleName(step->GetTrack()->GetDefinition()->GetParticleName()); + newContainer.SetParticleName( + step->GetTrack()->GetDefinition()->GetParticleName()); newContainer.SetCreationProcessName(creatorProcessName); - - - if (fTree.empty()){ + if (fTree.empty()) { fTree.set_head(newContainer); } - - for (auto it = fTree.begin_post(); it != fTree.end_post(); ++it){ + + for (auto it = fTree.begin_post(); it != fTree.end_post(); ++it) { LastVertexDataContainer container = *it; G4int trackID = container.GetTrackID(); - - if (step->GetTrack()->GetParentID() == trackID){ - newContainer = container.ContainerFromParentInformation(step); - fTree.append_child(it,newContainer); - break; + if (step->GetTrack()->GetParentID() == trackID) { + newContainer = container.ContainerFromParentInformation(step); + fTree.append_child(it, newContainer); + break; } } - for (auto it = fTree.begin_post(); it != fTree.end_post(); ++it){ + for (auto it = fTree.begin_post(); it != fTree.end_post(); ++it) { LastVertexDataContainer container = *it; G4int trackID = container.GetTrackID(); - if (step->GetTrack()->GetTrackID() == trackID){ + if (step->GetTrack()->GetTrackID() == trackID) { fIterator = it; break; } } } - - - LastVertexDataContainer* container = &(*fIterator); + LastVertexDataContainer *container = &(*fIterator); G4int trackID = container->GetTrackID(); - if ((processName != "Transportation") &&(processName !="None") && (processName !="Rayl")){ - if (step->GetTrack()->GetTrackID() == trackID){ + if ((processName != "Transportation") && (processName != "None") && + (processName != "Rayl")) { + if (step->GetTrack()->GetTrackID() == trackID) { G4ThreeVector position = step->GetTrack()->GetPosition(); G4ThreeVector prePosition = step->GetPreStepPoint()->GetPosition(); G4ThreeVector momentum; - if ((processName == "annihil")) + if ((processName == "annihil")) momentum = step->GetPostStepPoint()->GetMomentumDirection(); - else{ + else { momentum = step->GetPreStepPoint()->GetMomentumDirection(); } G4ThreeVector polarization = step->GetPreStepPoint()->GetPolarization(); - G4String particleName = step->GetTrack()->GetDefinition()->GetParticleName(); + G4String particleName = + step->GetTrack()->GetDefinition()->GetParticleName(); G4double energy = step->GetPreStepPoint()->GetKineticEnergy(); G4double weight = step->GetTrack()->GetWeight(); G4int trackStatus = step->GetTrack()->GetTrackStatus(); G4int nbOfSecondaries = step->GetfSecondary()->size(); G4double stepLength = step->GetStepLength(); - if (((processName == "annihil"))){ + if (((processName == "annihil"))) { energy -= (step->GetTotalEnergyDeposit()); } - SimpleContainer containerToSplit = SimpleContainer(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); + SimpleContainer containerToSplit = + SimpleContainer(processName, energy, momentum, position, polarization, + particleName, weight, trackStatus, nbOfSecondaries, + annihilFlag, stepLength, prePosition); container->SetContainerToSplit(containerToSplit); container->PushListOfSplittingParameters(containerToSplit); - } } - - } - - -G4bool GateLastVertexInteractionSplittingActor::IsParticleExitTheBiasedVolume(G4Step*step){ - +G4bool GateLastVertexInteractionSplittingActor::IsParticleExitTheBiasedVolume( + G4Step *step) { if ((step->GetPostStepPoint()->GetStepStatus() == 1)) { G4String logicalVolumeNamePostStep = "None"; if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) - logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - - if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()){ + logicalVolumeNamePostStep = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), + logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()) { return true; } - /* - else if (std::find(fListOfBiasedVolume.begin(), fListOfBiasedVolume.end(), logicalVolumeNamePostStep) != fListOfBiasedVolume.end()) { - return false; - } - */ + /* + else if (std::find(fListOfBiasedVolume.begin(), fListOfBiasedVolume.end(), + logicalVolumeNamePostStep) != fListOfBiasedVolume.end()) { return false; } + */ + } if (step->GetPostStepPoint()->GetStepStatus() == 0) return true; return false; } - - -G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesAProcess(G4Step* step){ +G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesAProcess( + G4Step *step) { G4String processName = "None"; - G4String particleName = step->GetTrack()->GetParticleDefinition()->GetParticleName(); + G4String particleName = + step->GetTrack()->GetParticleDefinition()->GetParticleName(); if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) - processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); - - if (std::find(fListOfProcessesAccordingParticles[particleName].begin(), fListOfProcessesAccordingParticles[particleName].end(), processName) != fListOfProcessesAccordingParticles[particleName].end()){ + processName = + step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + + if (std::find(fListOfProcessesAccordingParticles[particleName].begin(), + fListOfProcessesAccordingParticles[particleName].end(), + processName) != + fListOfProcessesAccordingParticles[particleName].end()) { return true; } return false; - - } -G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesALossEnergyProcess(G4Step* step){ - if (step->GetPostStepPoint()->GetKineticEnergy() - step->GetPreStepPoint()->GetKineticEnergy() != 0) +G4bool GateLastVertexInteractionSplittingActor:: + IsTheParticleUndergoesALossEnergyProcess(G4Step *step) { + if (step->GetPostStepPoint()->GetKineticEnergy() - + step->GetPreStepPoint()->GetKineticEnergy() != + 0) return true; return false; - - } - - void GateLastVertexInteractionSplittingActor::BeginOfRunAction( const G4Run *run) { - fListOfProcessesAccordingParticles["gamma"] = {"compt","phot","conv"}; - fListOfProcessesAccordingParticles["e-"] = {"eBrem","eIoni","msc"}; - fListOfProcessesAccordingParticles["e+"] = {"eBrem","eIoni","msc","annihil"}; + fListOfProcessesAccordingParticles["gamma"] = {"compt", "phot", "conv"}; + fListOfProcessesAccordingParticles["e-"] = {"eBrem", "eIoni", "msc"}; + fListOfProcessesAccordingParticles["e+"] = {"eBrem", "eIoni", "msc", + "annihil"}; - std::cout<GetVolume(fMotherVolumeName); + std::cout << fMotherVolumeName << std::endl; + G4LogicalVolume *biasingVolume = + G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); fListOfBiasedVolume.push_back(biasingVolume->GetName()); CreateListOfbiasedVolume(biasingVolume); - auto* source = fSourceManager->FindSourceByName("source_vertex"); - fVertexSource = (GateLastVertexSource* ) source; + auto *source = fSourceManager->FindSourceByName("source_vertex"); + fVertexSource = (GateLastVertexSource *)source; fCosMaxTheta = std::cos(fMaxTheta); fStackManager = G4EventManager::GetEventManager()->GetStackManager(); @@ -532,175 +565,176 @@ void GateLastVertexInteractionSplittingActor::BeginOfEventAction( fEventID = event->GetEventID(); fIsAnnihilAlreadySplit = false; fNbOfBatchForExitingParticle = 0; - if (fEventID%50000 == 0) - std::cout<<"event ID : "<FindSourceByName(fActiveSource); - GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; + if (fActiveSource == "source_vertex") { + auto *source = fSourceManager->FindSourceByName(fActiveSource); + GateLastVertexSource *vertexSource = (GateLastVertexSource *)source; fContainer = vertexSource->GetLastVertexContainer(); fProcessNameToSplit = vertexSource->GetProcessToSplit(); - if (fProcessToSplit !=0){ + if (fProcessToSplit != 0) { fProcessToSplit = nullptr; } - if (fTrackToSplit !=0){ + if (fTrackToSplit != 0) { delete fTrackToSplit; fTrackToSplit = nullptr; } fTrackToSplit = CreateATrackFromContainer(fContainer); if (fTrackToSplit != 0) - fWeight = fTrackToSplit->GetWeight()/fSplittingFactor; + fWeight = fTrackToSplit->GetWeight() / fSplittingFactor; } - - } void GateLastVertexInteractionSplittingActor::PreUserTrackingAction( const G4Track *track) { - fToSplit =true; - fIsFirstStep = true; - + fToSplit = true; + fIsFirstStep = true; } void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { - - if (fActiveSource != "source_vertex"){ + if (fActiveSource != "source_vertex") { FillOfDataTree(step); - - if (IsParticleExitTheBiasedVolume(step)){ - if ((fAngularKill == false) || ((fAngularKill == true) && (DoesParticleEmittedInSolidAngle(step->GetTrack()->GetMomentumDirection(),fVectorDirector) == true))){ + + if (IsParticleExitTheBiasedVolume(step)) { + if ((fAngularKill == false) || + ((fAngularKill == true) && + (DoesParticleEmittedInSolidAngle( + step->GetTrack()->GetMomentumDirection(), fVectorDirector) == + true))) { fListOfContainer.push_back((*fIterator)); } - + step->GetTrack()->SetTrackStatus(fStopAndKill); } - - } - - - if (fOnlyTree == false){ - if (fActiveSource == "source_vertex"){ + if (fOnlyTree == false) { + if (fActiveSource == "source_vertex") { - if (fIsFirstStep){ + if (fIsFirstStep) { fTrackID = step->GetTrack()->GetTrackID(); fEkin = step->GetPostStepPoint()->GetKineticEnergy(); - } - else{ - if ((fTrackID == step->GetTrack()->GetTrackID()) && (fEkin != step->GetPreStepPoint()->GetKineticEnergy())){ - fToSplit =false; - } - else{ + } else { + if ((fTrackID == step->GetTrack()->GetTrackID()) && + (fEkin != step->GetPreStepPoint()->GetKineticEnergy())) { + fToSplit = false; + } else { fEkin = step->GetPostStepPoint()->GetKineticEnergy(); } - } if (fToSplit) { G4String creatorProcessName = "None"; if (step->GetTrack()->GetCreatorProcess() != 0) - creatorProcessName =step->GetTrack()->GetCreatorProcess()->GetProcessName(); - if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ - - if ((fProcessNameToSplit != "annihil") || ((fProcessNameToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ - - //FIXME : list of process which are not splitable yet - if ((fProcessNameToSplit != "msc") && (fProcessNameToSplit != "conv") && (fProcessNameToSplit != "eIoni")) { + creatorProcessName = + step->GetTrack()->GetCreatorProcess()->GetProcessName(); + if (((step->GetTrack()->GetParentID() == 0) && + (step->GetTrack()->GetTrackID() == 1)) || + ((creatorProcessName == "annihil") && + (step->GetTrack()->GetParentID() == 1))) { + + if ((fProcessNameToSplit != "annihil") || + ((fProcessNameToSplit == "annihil") && + (fIsAnnihilAlreadySplit == false))) { + + // FIXME : list of process which are not splitable yet + if ((fProcessNameToSplit != "msc") && + (fProcessNameToSplit != "conv") && + (fProcessNameToSplit != "eIoni")) { fCopyInitStep = new G4Step(*step); - if (fProcessNameToSplit == "eBrem"){ - fCopyInitStep->SetStepLength(fContainer.GetContainerToSplit().GetStepLength()); - fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(fContainer.GetContainerToSplit().GetEnergy()); - + if (fProcessNameToSplit == "eBrem") { + fCopyInitStep->SetStepLength( + fContainer.GetContainerToSplit().GetStepLength()); + fCopyInitStep->GetPreStepPoint()->SetKineticEnergy( + fContainer.GetContainerToSplit().GetEnergy()); } - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer, fBatchSize); + CreateNewParticleAtTheLastVertex(fCopyInitStep, step, fContainer, + fBatchSize); } step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); - - if (fProcessNameToSplit == "annihil"){ + + if (fProcessNameToSplit == "annihil") { fIsAnnihilAlreadySplit = true; - } } + } - - else if ((fProcessNameToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ + else if ((fProcessNameToSplit == "annihil") && + (fIsAnnihilAlreadySplit == true)) { step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); } - - } - + + } + else { - if (fIsFirstStep){ - fNumberOfTrackToSimulate --; - if (fKilledBecauseOfProcess == false){ + if (fIsFirstStep) { + fNumberOfTrackToSimulate--; + if (fKilledBecauseOfProcess == false) { fSplitCounter += 1; - } - else { + } else { fKilledBecauseOfProcess = false; } - if (fSplitCounter > fSplittingFactor){ + if (fSplitCounter > fSplittingFactor) { step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); fStackManager->clear(); } } - if (IsTheParticleUndergoesALossEnergyProcess(step)){ + if (IsTheParticleUndergoesALossEnergyProcess(step)) { step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); fKilledBecauseOfProcess = true; } - if (fIsFirstStep){ - if (fSplitCounter <= fSplittingFactor){ - if (fNumberOfTrackToSimulate == 0){ - CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer,(fSplittingFactor - fSplitCounter +1)/fSplittingFactor * fBatchSize); - } + if (fIsFirstStep) { + if (fSplitCounter <= fSplittingFactor) { + if (fNumberOfTrackToSimulate == 0) { + CreateNewParticleAtTheLastVertex( + fCopyInitStep, step, fContainer, + (fSplittingFactor - fSplitCounter + 1) / fSplittingFactor * + fBatchSize); + } } } } - } + } } } fIsFirstStep = false; - - - - } - void GateLastVertexInteractionSplittingActor::EndOfEventAction( - const G4Event* event) { - - if (fActiveSource != "source_vertex"){ - - //print_tree(fTree,fTree.begin(),fTree.end()); - fVertexSource->SetNumberOfEventToSimulate(fListOfContainer.size()); - fVertexSource->SetNumberOfGeneratedEvent(0); - fVertexSource->SetListOfVertexToSimulate(fListOfContainer); - fTree.clear(); - fListOfContainer.clear(); - } + const G4Event *event) { - if (fOnlyTree == false){ + if (fActiveSource != "source_vertex") { - auto* source = fSourceManager->FindSourceByName("source_vertex"); - GateLastVertexSource* vertexSource = (GateLastVertexSource*) source; - if (vertexSource->GetNumberOfGeneratedEvent() < vertexSource->GetNumberOfEventToSimulate()){ - fSourceManager->SetActiveSourcebyName("source_vertex"); - } - fActiveSource = fSourceManager->GetActiveSourceName(); - } - + // print_tree(fTree,fTree.begin(),fTree.end()); + fVertexSource->SetNumberOfEventToSimulate(fListOfContainer.size()); + fVertexSource->SetNumberOfGeneratedEvent(0); + fVertexSource->SetListOfVertexToSimulate(fListOfContainer); + fTree.clear(); + fListOfContainer.clear(); + } + + if (fOnlyTree == false) { + + auto *source = fSourceManager->FindSourceByName("source_vertex"); + GateLastVertexSource *vertexSource = (GateLastVertexSource *)source; + if (vertexSource->GetNumberOfGeneratedEvent() < + vertexSource->GetNumberOfEventToSimulate()) { + fSourceManager->SetActiveSourcebyName("source_vertex"); } + fActiveSource = fSourceManager->GetActiveSourceName(); + } +} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h index dfe0ee850..e5d0b333f 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -29,18 +29,17 @@ #ifndef GateLastVertexInteractionSplittingActor_h #define GateLastVertexInteractionSplittingActor_h 1 +#include "CLHEP/Vector/ThreeVector.h" #include "G4ParticleChangeForGamma.hh" +#include "G4StackManager.hh" #include "G4VEnergyLossProcess.hh" -#include "GateVActor.h" -#include -#include +#include "GateLastVertexSource.h" #include "GateLastVertexSplittingDataContainer.h" +#include "GateVActor.h" #include "tree.hh" #include "tree_util.hh" #include -#include "GateLastVertexSource.h" -#include "CLHEP/Vector/ThreeVector.h" -#include "G4StackManager.hh" +#include using CLHEP::Hep3Vector; namespace py = pybind11; @@ -71,35 +70,31 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { G4double fSplitCounter = 0; G4bool fToSplit = true; G4String fActiveSource = "None"; - G4bool fIsAnnihilAlreadySplit =false; + G4bool fIsAnnihilAlreadySplit = false; G4int fCounter; - G4bool fKilledBecauseOfProcess = false; + G4bool fKilledBecauseOfProcess = false; G4bool fFirstSplittedPart = true; G4bool fOnlyTree = false; G4double fWeight; G4double fBatchSize; G4int fNumberOfTrackToSimulate = 0; - G4int fNbOfBatchForExitingParticle=0; - G4int fTracksCounts=0; - GateLastVertexSource* fVertexSource = nullptr; + G4int fNbOfBatchForExitingParticle = 0; + G4int fTracksCounts = 0; + GateLastVertexSource *fVertexSource = nullptr; tree fTree; tree::post_order_iterator fIterator; std::vector fListOfContainer; - G4StackManager* fStackManager = nullptr; - - + G4StackManager *fStackManager = nullptr; G4Track *fTrackToSplit = nullptr; - G4Step* fCopyInitStep = nullptr; + G4Step *fCopyInitStep = nullptr; G4String fProcessNameToSplit; - G4VProcess* fProcessToSplit; + G4VProcess *fProcessToSplit; LastVertexDataContainer fContainer; std::vector fTracksToPostpone; - std::map> fListOfProcessesAccordingParticles; - std::map fDataMap; - - + std::map> fListOfProcessesAccordingParticles; + std::map fDataMap; std::vector fListOfVolumeAncestor; std::vector fListOfBiasedVolume; @@ -107,7 +102,7 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { std::vector fListOfProcesses = {"compt", "annihil", "eBrem", "conv", "phot"}; - void InitializeUserInput(py::dict &user_info) override; + void InitializeUserInput(py::dict &user_info) override; virtual void SteppingAction(G4Step *) override; virtual void BeginOfEventAction(const G4Event *) override; virtual void EndOfEventAction(const G4Event *) override; @@ -115,23 +110,33 @@ class GateLastVertexInteractionSplittingActor : public GateVActor { virtual void PreUserTrackingAction(const G4Track *track) override; // Pure splitting functions - G4bool DoesParticleEmittedInSolidAngle(G4ThreeVector dir, G4ThreeVector vectorDirector); + G4bool DoesParticleEmittedInSolidAngle(G4ThreeVector dir, + G4ThreeVector vectorDirector); G4Track *CreateComptonTrack(G4ParticleChangeForGamma *, G4Track, G4double); - void ComptonSplitting(G4Step* initStep,G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container, G4double batchSize); - void SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container, G4double batchSize); - - void CreateNewParticleAtTheLastVertex(G4Step*init,G4Step *current, LastVertexDataContainer, G4double batchSize); - G4Track* CreateATrackFromContainer(LastVertexDataContainer container); - G4bool IsTheParticleUndergoesAProcess(G4Step* step); - G4bool IsTheParticleUndergoesALossEnergyProcess(G4Step* step); - G4VProcess* GetProcessFromProcessName(G4String particleName, G4String pName); - G4Track eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process); - + void ComptonSplitting(G4Step *initStep, G4Step *CurrentStep, + G4VProcess *process, LastVertexDataContainer container, + G4double batchSize); + void SecondariesSplitting(G4Step *initStep, G4Step *CurrentStep, + G4VProcess *process, + LastVertexDataContainer container, + G4double batchSize); + + void CreateNewParticleAtTheLastVertex(G4Step *init, G4Step *current, + LastVertexDataContainer, + G4double batchSize); + G4Track *CreateATrackFromContainer(LastVertexDataContainer container); + G4bool IsTheParticleUndergoesAProcess(G4Step *step); + G4bool IsTheParticleUndergoesALossEnergyProcess(G4Step *step); + G4VProcess *GetProcessFromProcessName(G4String particleName, G4String pName); + G4Track eBremProcessFinalState(G4Track *track, G4Step *step, + G4VProcess *process); void FillOfDataTree(G4Step *step); - G4bool IsParticleExitTheBiasedVolume(G4Step*step); + G4bool IsParticleExitTheBiasedVolume(G4Step *step); void CreateListOfbiasedVolume(G4LogicalVolume *volume); - void print_tree(const tree& tr, tree::pre_order_iterator it, tree::pre_order_iterator end); + void print_tree(const tree &tr, + tree::pre_order_iterator it, + tree::pre_order_iterator end); }; #endif diff --git a/core/opengate_core/opengate_lib/GateLastVertexSource.cpp b/core/opengate_core/opengate_lib/GateLastVertexSource.cpp index b903d90c8..c870637a4 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSource.cpp +++ b/core/opengate_core/opengate_lib/GateLastVertexSource.cpp @@ -10,40 +10,38 @@ #include "GateHelpersDict.h" #include -GateLastVertexSource::GateLastVertexSource() : GateVSource() { -} +GateLastVertexSource::GateLastVertexSource() : GateVSource() {} -GateLastVertexSource::~GateLastVertexSource() { -} +GateLastVertexSource::~GateLastVertexSource() {} void GateLastVertexSource::InitializeUserInfo(py::dict &user_info) { GateVSource::InitializeUserInfo(user_info); // get user info about activity or nb of events fN = DictGetInt(user_info, "n"); - } +} double GateLastVertexSource::PrepareNextTime(double current_simulation_time) { /* // If all N events have been generated, we stop (negative time) if (fNumberOfGeneratedEvents >= fN){ - std::cout << "LV: "<< -1<<" "<< fNumberOfGeneratedEvents <<" "<< fN<= fN) - return -1; + if (fNumberOfGeneratedEvents >= fN) + return -1; - return fStartTime + 1; + return fStartTime + 1; } void GateLastVertexSource::PrepareNextRun() { @@ -56,34 +54,35 @@ void GateLastVertexSource::PrepareNextRun() { // init the number of generated events (here, for each run) fNumberOfGeneratedEvents = 0; - } -void GateLastVertexSource::GenerateOnePrimary(G4Event* event, double current_simulation_time, G4int idx ){ +void GateLastVertexSource::GenerateOnePrimary(G4Event *event, + double current_simulation_time, + G4int idx) { - if (fNumberOfGeneratedEvents >= fN){ + if (fNumberOfGeneratedEvents >= fN) { auto *particle_table = G4ParticleTable::GetParticleTable(); auto *fParticleDefinition = particle_table->FindParticle("geantino"); auto *particle = new G4PrimaryParticle(fParticleDefinition); particle->SetKineticEnergy(0); - particle->SetMomentumDirection({1,0,0}); + particle->SetMomentumDirection({1, 0, 0}); particle->SetWeight(1); - auto *vertex = new G4PrimaryVertex({0,0,0}, current_simulation_time); + auto *vertex = new G4PrimaryVertex({0, 0, 0}, current_simulation_time); vertex->SetPrimary(particle); event->AddPrimaryVertex(vertex); - } - else { + } else { - SimpleContainer containerToSplit = fListOfContainer[idx].GetContainerToSplit(); + SimpleContainer containerToSplit = + fListOfContainer[idx].GetContainerToSplit(); G4double energy = containerToSplit.GetEnergy(); - if (energy < 0){ + if (energy < 0) { energy = 0; } fContainer = fListOfContainer[idx]; - G4ThreeVector position = containerToSplit.GetVertexPosition(); + G4ThreeVector position = containerToSplit.GetVertexPosition(); G4ThreeVector momentum = containerToSplit.GetMomentum(); G4String particleName = containerToSplit.GetParticleNameToSplit(); - G4double weight =containerToSplit.GetWeight(); + G4double weight = containerToSplit.GetWeight(); fProcessToSplit = containerToSplit.GetProcessNameToSplit(); auto &l = fThreadLocalData.Get(); @@ -97,16 +96,14 @@ void GateLastVertexSource::GenerateOnePrimary(G4Event* event, double current_sim vertex->SetPrimary(particle); event->AddPrimaryVertex(vertex); } - } - void GateLastVertexSource::GeneratePrimaries(G4Event *event, - double current_simulation_time) { - - GenerateOnePrimary(event,current_simulation_time,fNumberOfGeneratedEvents); + double current_simulation_time) { + + GenerateOnePrimary(event, current_simulation_time, fNumberOfGeneratedEvents); fNumberOfGeneratedEvents++; - if (fNumberOfGeneratedEvents == fListOfContainer.size()){ + if (fNumberOfGeneratedEvents == fListOfContainer.size()) { fListOfContainer.clear(); } } diff --git a/core/opengate_core/opengate_lib/GateLastVertexSource.h b/core/opengate_core/opengate_lib/GateLastVertexSource.h index dd23c124d..eebf43e90 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSource.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSource.h @@ -9,9 +9,9 @@ #define GateLastVertexSource_h #include "GateAcceptanceAngleTesterManager.h" +#include "GateLastVertexSplittingDataContainer.h" #include "GateSingleParticleSource.h" #include "GateVSource.h" -#include "GateLastVertexSplittingDataContainer.h" #include namespace py = pybind11; @@ -19,9 +19,9 @@ namespace py = pybind11; /* This is NOT a real source type but a template to help writing your own source type. Copy-paste this file with a different name ("MyNewSource.hh") and start - building. You also need to copy : GateLastVertexSource.hh GateLastVertexSource.cpp - pyGateLastVertexSource.cpp - And add the source declaration in opengate_core.cpp + building. You also need to copy : GateLastVertexSource.hh + GateLastVertexSource.cpp pyGateLastVertexSource.cpp And add the source + declaration in opengate_core.cpp */ class GateLastVertexSource : public GateVSource { @@ -39,38 +39,25 @@ class GateLastVertexSource : public GateVSource { void GeneratePrimaries(G4Event *event, double time) override; + void GenerateOnePrimary(G4Event *event, double time, G4int idx); - void GenerateOnePrimary(G4Event *event, double time,G4int idx); - - - void SetListOfVertexToSimulate(std::vector list){ + void SetListOfVertexToSimulate(std::vector list) { fListOfContainer = list; } - void SetNumberOfGeneratedEvent(G4int nbEvent){ + void SetNumberOfGeneratedEvent(G4int nbEvent) { fNumberOfGeneratedEvents = nbEvent; } - void SetNumberOfEventToSimulate(G4int N){ - fN = N; - } - - G4int GetNumberOfEventToSimulate(){ - return fN; - } + void SetNumberOfEventToSimulate(G4int N) { fN = N; } - G4int GetNumberOfGeneratedEvent(){ - return fNumberOfGeneratedEvents; - } + G4int GetNumberOfEventToSimulate() { return fN; } - G4String GetProcessToSplit(){ - return fProcessToSplit; - } + G4int GetNumberOfGeneratedEvent() { return fNumberOfGeneratedEvents; } - LastVertexDataContainer GetLastVertexContainer(){ - return fContainer; - } + G4String GetProcessToSplit() { return fProcessToSplit; } + LastVertexDataContainer GetLastVertexContainer() { return fContainer; } protected: G4int fNumberOfGeneratedEvents = 0; diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h index 474d6cc84..92edcb5f0 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h @@ -27,139 +27,118 @@ #ifndef LastVertexDataContainer_h #define LastVertexDataContainer_h - -#include -#include "G4VEnergyLossProcess.hh" +#include "G4Electron.hh" +#include "G4EmBiasingManager.hh" +#include "G4EmParameters.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4Gamma.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4PhysicalConstants.hh" +#include "G4PhysicsModelCatalog.hh" +#include "G4Positron.hh" #include "G4Track.hh" #include "G4VEmProcess.hh" +#include "G4VEnergyLossProcess.hh" #include "G4VParticleChange.hh" -#include "G4eplusAnnihilation.hh" -#include "G4PhysicalConstants.hh" -#include "G4MaterialCutsCouple.hh" -#include "G4Gamma.hh" -#include "G4Electron.hh" -#include "G4Positron.hh" #include "G4eeToTwoGammaModel.hh" -#include "G4EmBiasingManager.hh" -#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilation.hh" #include "G4eplusAnnihilationEntanglementClipBoard.hh" -#include "G4EmParameters.hh" -#include "G4PhysicsModelCatalog.hh" #include "GateLastVertexSplittingSimpleContainer.h" +#include +class LastVertexDataContainer { -class LastVertexDataContainer{ - -public : - - -LastVertexDataContainer(){} - -~LastVertexDataContainer(){} - - - -void SetTrackID(G4int trackID ){ - fTrackID = trackID; -} - -G4int GetTrackID(){ - return fTrackID; -} - - -void SetParticleName(G4String name){ - fParticleName = name; -} - -G4String GetParticleName(){ - return fParticleName; -} - - -void SetCreationProcessName(G4String creationProcessName){ - fCreationProcessName = creationProcessName; -} - -G4String GetCreationProcessName(){ - return fCreationProcessName; -} - +public: + LastVertexDataContainer() {} -void SetContainerToSplit(SimpleContainer container){ - fContainerToSplit = container; -} + ~LastVertexDataContainer() {} + void SetTrackID(G4int trackID) { fTrackID = trackID; } -SimpleContainer GetContainerToSplit(){ - return fContainerToSplit; -} + G4int GetTrackID() { return fTrackID; } + void SetParticleName(G4String name) { fParticleName = name; } + G4String GetParticleName() { return fParticleName; } -void PushListOfSplittingParameters(SimpleContainer container){ - fVectorOfContainerToSplit.emplace_back(container); -} + void SetCreationProcessName(G4String creationProcessName) { + fCreationProcessName = creationProcessName; + } + G4String GetCreationProcessName() { return fCreationProcessName; } + void SetContainerToSplit(SimpleContainer container) { + fContainerToSplit = container; + } + SimpleContainer GetContainerToSplit() { return fContainerToSplit; } -LastVertexDataContainer ContainerFromParentInformation(G4Step* step){ - LastVertexDataContainer aContainer = LastVertexDataContainer(); + void PushListOfSplittingParameters(SimpleContainer container) { + fVectorOfContainerToSplit.emplace_back(container); + } - aContainer.fTrackID = step->GetTrack()->GetTrackID(); - aContainer.fParticleName = step->GetTrack()->GetDefinition()->GetParticleName(); - if (this->fContainerToSplit.GetProcessNameToSplit() != "None"){ - if (this->fVectorOfContainerToSplit.size() !=0){ - G4ThreeVector vertexPosition = step->GetTrack()->GetVertexPosition(); - for (int i =0;ifVectorOfContainerToSplit.size();i++){ - if (vertexPosition == this->fVectorOfContainerToSplit[i].GetVertexPosition()){ - SimpleContainer tmpContainer = this->fVectorOfContainerToSplit[i]; - //std::cout<<"1 "<GetTrack()->GetTrackID(); + aContainer.fParticleName = + step->GetTrack()->GetDefinition()->GetParticleName(); + if (this->fContainerToSplit.GetProcessNameToSplit() != "None") { + if (this->fVectorOfContainerToSplit.size() != 0) { + G4ThreeVector vertexPosition = step->GetTrack()->GetVertexPosition(); + for (int i = 0; i < this->fVectorOfContainerToSplit.size(); i++) { + if (vertexPosition == + this->fVectorOfContainerToSplit[i].GetVertexPosition()) { + SimpleContainer tmpContainer = this->fVectorOfContainerToSplit[i]; + // std::cout<<"1 "<fContainerToSplit; + // std::cout<<"2 "<fContainerToSplit; - //std::cout<<"2 "< fVectorOfContainerToSplit; +private: + G4String fParticleName = "None"; + G4int fTrackID = 0; + G4String fCreationProcessName = "None"; + SimpleContainer fContainerToSplit; + std::vector fVectorOfContainerToSplit; }; #endif - - - - - - \ No newline at end of file diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h index 38a9dc2ea..ba7b9b366 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h @@ -27,90 +27,77 @@ #ifndef GateLastVertexSplittingPostStepDoIt_h #define GateLastVertexSplittingPostStepDoIt_h - -#include "G4VEnergyLossProcess.hh" -#include "G4VEmProcess.hh" -#include "G4VParticleChange.hh" -#include "G4eplusAnnihilation.hh" -#include "G4PhysicalConstants.hh" -#include "G4MaterialCutsCouple.hh" -#include "G4Gamma.hh" #include "G4Electron.hh" -#include "G4Positron.hh" -#include "G4eeToTwoGammaModel.hh" #include "G4EmBiasingManager.hh" -#include "G4EntanglementAuxInfo.hh" -#include "G4eplusAnnihilationEntanglementClipBoard.hh" #include "G4EmParameters.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4Gamma.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4PhysicalConstants.hh" #include "G4PhysicsModelCatalog.hh" +#include "G4Positron.hh" +#include "G4VEmProcess.hh" +#include "G4VEnergyLossProcess.hh" +#include "G4VParticleChange.hh" +#include "G4eeToTwoGammaModel.hh" +#include "G4eplusAnnihilation.hh" +#include "G4eplusAnnihilationEntanglementClipBoard.hh" #include - - - class GateBremPostStepDoIt : public G4VEnergyLossProcess { -public : - -GateBremPostStepDoIt(); - -~ GateBremPostStepDoIt(); - -virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & step) override -{ - const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); - currentCouple = couple; - G4VParticleChange* particleChange = G4VEnergyLossProcess::PostStepDoIt(track,step); - return particleChange; -} - -virtual G4VParticleChange * AlongStepDoIt (const G4Track & track, const G4Step & step) override -{ - G4VParticleChange* particleChange = G4VEnergyLossProcess::AlongStepDoIt(track,step); - return particleChange; -} - +public: + GateBremPostStepDoIt(); + + ~GateBremPostStepDoIt(); + + virtual G4VParticleChange *PostStepDoIt(const G4Track &track, + const G4Step &step) override { + const G4MaterialCutsCouple *couple = + step.GetPreStepPoint()->GetMaterialCutsCouple(); + currentCouple = couple; + G4VParticleChange *particleChange = + G4VEnergyLossProcess::PostStepDoIt(track, step); + return particleChange; + } + virtual G4VParticleChange *AlongStepDoIt(const G4Track &track, + const G4Step &step) override { + G4VParticleChange *particleChange = + G4VEnergyLossProcess::AlongStepDoIt(track, step); + return particleChange; + } }; - class GateGammaEmPostStepDoIt : public G4VEmProcess { -public : - -GateGammaEmPostStepDoIt(); - -~ GateGammaEmPostStepDoIt(); - -virtual G4VParticleChange * PostStepDoIt(const G4Track & track, const G4Step & step) override -{ - const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); - currentCouple = couple; - G4VParticleChange* particleChange = G4VEmProcess::PostStepDoIt(track,step); - return particleChange; -} - - +public: + GateGammaEmPostStepDoIt(); + + ~GateGammaEmPostStepDoIt(); + + virtual G4VParticleChange *PostStepDoIt(const G4Track &track, + const G4Step &step) override { + const G4MaterialCutsCouple *couple = + step.GetPreStepPoint()->GetMaterialCutsCouple(); + currentCouple = couple; + G4VParticleChange *particleChange = G4VEmProcess::PostStepDoIt(track, step); + return particleChange; + } }; class GateplusannihilAtRestDoIt : public G4eplusAnnihilation { -public : - -GateplusannihilAtRestDoIt(); -~ GateplusannihilAtRestDoIt(); +public: + GateplusannihilAtRestDoIt(); + ~GateplusannihilAtRestDoIt(); -virtual G4VParticleChange* AtRestDoIt(const G4Track& track, - const G4Step& step) override -// Performs the e+ e- annihilation when both particles are assumed at rest. + virtual G4VParticleChange *AtRestDoIt(const G4Track &track, + const G4Step &step) override + // Performs the e+ e- annihilation when both particles are assumed at rest. { - G4Track copyTrack = G4Track(track); - copyTrack.SetStep(&step); - G4VParticleChange* particleChange = G4eplusAnnihilation::AtRestDoIt(copyTrack,step); - return particleChange; + G4Track copyTrack = G4Track(track); + copyTrack.SetStep(&step); + G4VParticleChange *particleChange = + G4eplusAnnihilation::AtRestDoIt(copyTrack, step); + return particleChange; } }; #endif - - - - - - \ No newline at end of file diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h index e3069bf2c..f39087b70 100644 --- a/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h @@ -27,191 +27,131 @@ #ifndef SimpleContainer_h #define SimpleContainer_h - -#include -#include "G4VEnergyLossProcess.hh" +#include "G4Electron.hh" +#include "G4EmBiasingManager.hh" +#include "G4EmParameters.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4Gamma.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4PhysicalConstants.hh" +#include "G4PhysicsModelCatalog.hh" +#include "G4Positron.hh" #include "G4Track.hh" #include "G4VEmProcess.hh" +#include "G4VEnergyLossProcess.hh" #include "G4VParticleChange.hh" -#include "G4eplusAnnihilation.hh" -#include "G4PhysicalConstants.hh" -#include "G4MaterialCutsCouple.hh" -#include "G4Gamma.hh" -#include "G4Electron.hh" -#include "G4Positron.hh" #include "G4eeToTwoGammaModel.hh" -#include "G4EmBiasingManager.hh" -#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilation.hh" #include "G4eplusAnnihilationEntanglementClipBoard.hh" -#include "G4EmParameters.hh" -#include "G4PhysicsModelCatalog.hh" - - -class SimpleContainer{ - -public : -SimpleContainer(G4String processName,G4double energy,G4ThreeVector momentum, G4ThreeVector position,G4ThreeVector polarization,G4String name,G4double weight,G4int trackStatus,G4int nbSec,G4String flag,G4double length, G4ThreeVector prePos){ - - fProcessNameToSplit = processName; - fEnergyToSplit = energy; - fMomentumToSplit = momentum; - fPositionToSplit = position; - fPolarizationToSplit = polarization; - fParticleNameToSplit = name; - fWeightToSplit = weight; - fTrackStatusToSplit = trackStatus; - fNumberOfSecondariesToSplit = nbSec; - fAnnihilProcessFlag = flag; - fStepLength = length; - fPrePosition = prePos; - -} - - -SimpleContainer(){} - -~SimpleContainer(){} - -void SetProcessNameToSplit(G4String processName){ - fProcessNameToSplit = processName; -} - -G4String GetProcessNameToSplit(){ - return fProcessNameToSplit; -} - -void SetEnergy(G4double energy){ - fEnergyToSplit =energy; -} - -G4double GetEnergy(){ - return fEnergyToSplit; -} - - -void SetWeight(G4double weight){ - fWeightToSplit =weight; -} - -G4double GetWeight(){ - return fWeightToSplit; -} - -void SetPolarization(G4ThreeVector polarization){ - fPolarizationToSplit = polarization; -} - -G4ThreeVector GetPolarization(){ - return fPolarizationToSplit; -} - -void SetMomentum(G4ThreeVector momentum){ - fMomentumToSplit =momentum; -} - -G4ThreeVector GetMomentum(){ - return fMomentumToSplit; -} +#include -void SetVertexPosition(G4ThreeVector position){ - fPositionToSplit = position; -} +class SimpleContainer { -G4ThreeVector GetVertexPosition(){ - return fPositionToSplit; -} +public: + SimpleContainer(G4String processName, G4double energy, G4ThreeVector momentum, + G4ThreeVector position, G4ThreeVector polarization, + G4String name, G4double weight, G4int trackStatus, + G4int nbSec, G4String flag, G4double length, + G4ThreeVector prePos) { -void SetParticleNameToSplit(G4String name){ - fParticleNameToSplit = name; -} + fProcessNameToSplit = processName; + fEnergyToSplit = energy; + fMomentumToSplit = momentum; + fPositionToSplit = position; + fPolarizationToSplit = polarization; + fParticleNameToSplit = name; + fWeightToSplit = weight; + fTrackStatusToSplit = trackStatus; + fNumberOfSecondariesToSplit = nbSec; + fAnnihilProcessFlag = flag; + fStepLength = length; + fPrePosition = prePos; + } -G4String GetParticleNameToSplit(){ - return fParticleNameToSplit; -} + SimpleContainer() {} + ~SimpleContainer() {} -void SetTrackStatus(G4int trackStatus){ - fTrackStatusToSplit = trackStatus; -} + void SetProcessNameToSplit(G4String processName) { + fProcessNameToSplit = processName; + } + G4String GetProcessNameToSplit() { return fProcessNameToSplit; } -G4int GetTrackStatus(){ - return fTrackStatusToSplit; -} + void SetEnergy(G4double energy) { fEnergyToSplit = energy; } + G4double GetEnergy() { return fEnergyToSplit; } -void SetNbOfSecondaries(G4int nbSec){ - fNumberOfSecondariesToSplit = nbSec; -} + void SetWeight(G4double weight) { fWeightToSplit = weight; } -G4int GetNbOfSecondaries(){ - return fNumberOfSecondariesToSplit; -} + G4double GetWeight() { return fWeightToSplit; } -void SetAnnihilationFlag(G4String flag){ - fAnnihilProcessFlag = flag; -} + void SetPolarization(G4ThreeVector polarization) { + fPolarizationToSplit = polarization; + } -G4String GetAnnihilationFlag(){ - return fAnnihilProcessFlag; -} + G4ThreeVector GetPolarization() { return fPolarizationToSplit; } -void SetStepLength(G4double length){ - fStepLength = length; -} + void SetMomentum(G4ThreeVector momentum) { fMomentumToSplit = momentum; } -G4double GetStepLength(){ - return fStepLength; -} + G4ThreeVector GetMomentum() { return fMomentumToSplit; } + void SetVertexPosition(G4ThreeVector position) { + fPositionToSplit = position; + } + G4ThreeVector GetVertexPosition() { return fPositionToSplit; } -void SetPrePositionToSplit(G4ThreeVector prePos){ - fPrePosition = prePos; -} + void SetParticleNameToSplit(G4String name) { fParticleNameToSplit = name; } -G4ThreeVector GetPrePositionToSplit(){ - return fPrePosition; -} + G4String GetParticleNameToSplit() { return fParticleNameToSplit; } + void SetTrackStatus(G4int trackStatus) { fTrackStatusToSplit = trackStatus; } + G4int GetTrackStatus() { return fTrackStatusToSplit; } + void SetNbOfSecondaries(G4int nbSec) { fNumberOfSecondariesToSplit = nbSec; } -void DumpInfoToSplit(){ - std::cout<<"Particle name of the particle to split: "<GetWrappedProcess()->GetProcessName(); - if (processName != "Rayl"){ - fWeightChange[processName] = - weightChange; - } - else { + if (processName != "Rayl") { + fWeightChange[processName] = weightChange; + } else { fWeightChange[processName] = 1; } } diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp index ba894cf04..8c22b6399 100644 --- a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp @@ -49,57 +49,63 @@ //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnVGenericSplitting:: - GateOptnVGenericSplitting(G4String name) +GateOptnVGenericSplitting::GateOptnVGenericSplitting(G4String name) : G4VBiasingOperation(name), fParticleChange() {} //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... -GateOptnVGenericSplitting:: - ~GateOptnVGenericSplitting() {} +GateOptnVGenericSplitting::~GateOptnVGenericSplitting() {} +void GateOptnVGenericSplitting::TrackInitializationChargedParticle( + G4ParticleChange *particleChange, G4VParticleChange *processFinalState, + const G4Track *track, G4double split) { -void GateOptnVGenericSplitting::TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split) { - - G4ParticleChangeForLoss* processFinalStateForLoss =( G4ParticleChangeForLoss* ) processFinalState ; + G4ParticleChangeForLoss *processFinalStateForLoss = + (G4ParticleChangeForLoss *)processFinalState; particleChange->Initialize(*track); - particleChange->ProposeTrackStatus(processFinalStateForLoss->GetTrackStatus() ); - particleChange->ProposeEnergy(processFinalStateForLoss->GetProposedKineticEnergy() ); - particleChange->ProposeMomentumDirection(processFinalStateForLoss->GetProposedMomentumDirection()); + particleChange->ProposeTrackStatus( + processFinalStateForLoss->GetTrackStatus()); + particleChange->ProposeEnergy( + processFinalStateForLoss->GetProposedKineticEnergy()); + particleChange->ProposeMomentumDirection( + processFinalStateForLoss->GetProposedMomentumDirection()); particleChange->SetNumberOfSecondaries(fSplittingFactor); particleChange->SetSecondaryWeightByProcess(true); processFinalStateForLoss->Clear(); } -void GateOptnVGenericSplitting::TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split) { - G4ParticleChangeForGamma* processFinalStateForGamma = (G4ParticleChangeForGamma *)processFinalState; +void GateOptnVGenericSplitting::TrackInitializationGamma( + G4ParticleChange *particleChange, G4VParticleChange *processFinalState, + const G4Track *track, G4double split) { + G4ParticleChangeForGamma *processFinalStateForGamma = + (G4ParticleChangeForGamma *)processFinalState; particleChange->Initialize(*track); - particleChange->ProposeTrackStatus(processFinalStateForGamma->GetTrackStatus() ); - particleChange->ProposeEnergy(processFinalStateForGamma->GetProposedKineticEnergy() ); - particleChange->ProposeMomentumDirection(processFinalStateForGamma->GetProposedMomentumDirection() ); + particleChange->ProposeTrackStatus( + processFinalStateForGamma->GetTrackStatus()); + particleChange->ProposeEnergy( + processFinalStateForGamma->GetProposedKineticEnergy()); + particleChange->ProposeMomentumDirection( + processFinalStateForGamma->GetProposedMomentumDirection()); particleChange->SetNumberOfSecondaries(fSplittingFactor); particleChange->SetSecondaryWeightByProcess(true); processFinalStateForGamma->Clear(); - - } -G4double GateOptnVGenericSplitting::RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split){ -G4double cosTheta =vectorDirector * dir; -G4double theta = std::acos(cosTheta); -G4double weightToApply = 1; -if (theta > maxTheta){ - G4double probability = G4UniformRand(); - if (probability <= 1 / split) { - weightToApply = split; +G4double GateOptnVGenericSplitting::RussianRouletteForAngleSurvival( + G4ThreeVector dir, G4ThreeVector vectorDirector, G4double maxTheta, + G4double split) { + G4double cosTheta = vectorDirector * dir; + G4double theta = std::acos(cosTheta); + G4double weightToApply = 1; + if (theta > maxTheta) { + G4double probability = G4UniformRand(); + if (probability <= 1 / split) { + weightToApply = split; + } else { + weightToApply = 0; + } } - else{ - weightToApply = 0; - } -} -return weightToApply; - + return weightToApply; } - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h index d7cebd78a..b89f0baee 100644 --- a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h @@ -70,15 +70,20 @@ class GateOptnVGenericSplitting : public G4VBiasingOperation { return 0; } + // ---------------------------------------------- + // -- Methods for the generic splitting + // ---------------------------------------------- -// ---------------------------------------------- -// -- Methods for the generic splitting -// ---------------------------------------------- - -void TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); -void TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); -static G4double RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split); - + void TrackInitializationChargedParticle(G4ParticleChange *particleChange, + G4VParticleChange *processFinalState, + const G4Track *track, G4double split); + void TrackInitializationGamma(G4ParticleChange *particleChange, + G4VParticleChange *processFinalState, + const G4Track *track, G4double split); + static G4double RussianRouletteForAngleSurvival(G4ThreeVector dir, + G4ThreeVector vectorDirector, + G4double maxTheta, + G4double split); public: // ---------------------------------------------- diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp index 13c5b3cfb..f973cc4aa 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -50,12 +50,9 @@ #include "G4eplusAnnihilation.hh" #include "GateOptnPairProdSplitting.h" #include "GateOptnScatteredGammaSplitting.h" +#include "GateOptnVGenericSplitting.h" #include "GateOptneBremSplitting.h" #include "GateOptrComptPseudoTransportationActor.h" -#include "G4UImanager.hh" -#include "G4eplusAnnihilation.hh" -#include "GateOptnVGenericSplitting.h" - //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -144,9 +141,8 @@ void GateOptrComptPseudoTransportationActor::StartSimulationAction() { fPairProdSplittingOperation->SetSplittingFactor(fSplittingFactor); - fFreeFlightOperation->SetRussianRouletteForWeights(fRussianRouletteForWeights); - - + fFreeFlightOperation->SetRussianRouletteForWeights( + fRussianRouletteForWeights); } void GateOptrComptPseudoTransportationActor::StartRun() { @@ -171,38 +167,47 @@ void GateOptrComptPseudoTransportationActor::StartRun() { void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { G4String creationProcessName = "None"; - if (step->GetTrack()->GetCreatorProcess() != 0){ - creationProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); - + if (step->GetTrack()->GetCreatorProcess() != 0) { + creationProcessName = + step->GetTrack()->GetCreatorProcess()->GetProcessName(); } - -if ((fIsFirstStep) && (fRussianRouletteForAngle)){ - G4String LogicalVolumeNameOfCreation = step->GetTrack()->GetLogicalVolumeAtVertex()->GetName(); - if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ - if (creationProcessName == "biasWrapper(annihil)"){ + + if ((fIsFirstStep) && (fRussianRouletteForAngle)) { + G4String LogicalVolumeNameOfCreation = + step->GetTrack()->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeNameOfCreation) != + fNameOfBiasedLogicalVolume.end()) { + if (creationProcessName == "biasWrapper(annihil)") { auto dir = step->GetPreStepPoint()->GetMomentumDirection(); - G4double w = GateOptnVGenericSplitting::RussianRouletteForAngleSurvival(dir,fVectorDirector,fMaxTheta,fSplittingFactor); - if (w == 0) - { + G4double w = GateOptnVGenericSplitting::RussianRouletteForAngleSurvival( + dir, fVectorDirector, fMaxTheta, fSplittingFactor); + if (w == 0) { step->GetTrack()->SetTrackStatus(fStopAndKill); - } - else { + } else { step->GetTrack()->SetWeight(step->GetTrack()->GetWeight() * w); } } + } } -} - -if ((isSplitted == true) && (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { - G4String LogicalVolumeName = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); - if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end()) - && (LogicalVolumeName != fMotherVolumeName)) { + + if ((isSplitted == true) && + (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { + G4String LogicalVolumeName = step->GetPostStepPoint() + ->GetPhysicalVolume() + ->GetLogicalVolume() + ->GetName(); + if (!(std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeName) != fNameOfBiasedLogicalVolume.end()) && + (LogicalVolumeName != fMotherVolumeName)) { step->GetTrack()->SetTrackStatus(fStopAndKill); isSplitted = false; + } } -} -fIsFirstStep = false; + fIsFirstStep = false; } void GateOptrComptPseudoTransportationActor::BeginOfEventAction( @@ -218,14 +223,17 @@ void GateOptrComptPseudoTransportationActor::StartTracking( const G4Track *track) { G4String creationProcessName = "None"; fIsFirstStep = true; - if (track->GetCreatorProcess() != 0){ + if (track->GetCreatorProcess() != 0) { creationProcessName = track->GetCreatorProcess()->GetProcessName(); } - - if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ - G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); - if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ + if (track->GetParticleDefinition()->GetParticleName() == "gamma") { + G4String LogicalVolumeNameOfCreation = + track->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeNameOfCreation) != + fNameOfBiasedLogicalVolume.end()) { fInitialWeight = track->GetWeight(); fFreeFlightOperation->SetInitialWeight(fInitialWeight); @@ -299,8 +307,10 @@ GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( return feBremSplittingOperation; } - if (!(std::find(fCreationProcessNameList.begin(), fCreationProcessNameList.end(),CreationProcessName) != fCreationProcessNameList.end())){ - if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt"){ + if (!(std::find(fCreationProcessNameList.begin(), + fCreationProcessNameList.end(), + CreationProcessName) != fCreationProcessNameList.end())) { + if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt") { isSplitted = true; return fScatteredGammaSplittingOperation; diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h index 629d31b69..c463a4e2c 100644 --- a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -85,13 +85,14 @@ class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, G4int ftrackIDAtTheEntrance; G4int fEventID; G4double fEventIDKineticEnergy; - G4bool ftestbool= false; + G4bool ftestbool = false; G4bool fIsFirstStep = false; - const G4VProcess* fAnnihilation =nullptr; + const G4VProcess *fAnnihilation = nullptr; std::vector fNameOfBiasedLogicalVolume = {}; std::vector v_EventID = {}; - std::vector fCreationProcessNameList = {"biasWrapper(compt)", "biasWrapper(eBrem)","biasWrapper(annihil)"}; + std::vector fCreationProcessNameList = { + "biasWrapper(compt)", "biasWrapper(eBrem)", "biasWrapper(annihil)"}; // Unused but mandatory diff --git a/core/opengate_core/opengate_lib/GateSourceManager.cpp b/core/opengate_core/opengate_lib/GateSourceManager.cpp index d0772fe15..61dfb8361 100644 --- a/core/opengate_core/opengate_lib/GateSourceManager.cpp +++ b/core/opengate_core/opengate_lib/GateSourceManager.cpp @@ -241,9 +241,6 @@ void GateSourceManager::PrepareRunToStart(int run_id) { : std::to_string(G4Threading::G4GetThreadId())); } - - - void GateSourceManager::PrepareNextSource() { auto &l = fThreadLocalData.Get(); l.fNextActiveSource = nullptr; diff --git a/core/opengate_core/opengate_lib/GateSourceManager.h b/core/opengate_core/opengate_lib/GateSourceManager.h index 1b559a07b..342cbaeab 100644 --- a/core/opengate_core/opengate_lib/GateSourceManager.h +++ b/core/opengate_core/opengate_lib/GateSourceManager.h @@ -64,20 +64,19 @@ class GateSourceManager : public G4VUserPrimaryGeneratorAction { // Return a source GateVSource *FindSourceByName(std::string name) const; - - - G4String GetActiveSourceName(){ - auto &l = fThreadLocalData.Get(); - if (l.fNextActiveSource !=0){ + + G4String GetActiveSourceName() { + auto &l = fThreadLocalData.Get(); + if (l.fNextActiveSource != 0) { G4String name = l.fNextActiveSource->fName; return name; } return "None"; } - void SetActiveSourcebyName(G4String sourceName){ + void SetActiveSourcebyName(G4String sourceName) { auto &l = fThreadLocalData.Get(); - auto* source = FindSourceByName(sourceName); + auto *source = FindSourceByName(sourceName); l.fNextActiveSource = source; } diff --git a/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp index 4a6a8c130..85f22a9ef 100644 --- a/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp +++ b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp @@ -11,8 +11,9 @@ namespace py = pybind11; void init_GateLastVertexInteractionSplittingActor(py::module &m) { - py::class_>( + py::class_< + GateLastVertexInteractionSplittingActor, GateVActor, + std::unique_ptr>( m, "GateLastVertexInteractionSplittingActor") .def_readwrite( "fListOfVolumeAncestor", diff --git a/core/opengate_core/opengate_lib/tree.hh b/core/opengate_core/opengate_lib/tree.hh index 4f78d5b8a..ef78833de 100644 --- a/core/opengate_core/opengate_lib/tree.hh +++ b/core/opengate_core/opengate_lib/tree.hh @@ -4,7 +4,7 @@ // Copyright (C) 2001-2024 Kasper Peeters // Distributed under the GNU General Public License version 3. // -// Special permission to use tree.hh under the conditions of a +// Special permission to use tree.hh under the conditions of a // different license can be requested from the author. /** \mainpage tree.hh @@ -18,1460 +18,1508 @@ nodes. Various types of iterators are provided (post-order, pre-order, and others). Where possible the access methods are compatible with the STL or alternative algorithms are - available. + available. */ - #ifndef tree_hh_ #define tree_hh_ +#include #include -#include -#include +#include #include -#include +#include #include -#include -#include +#include +#include #include -/// A node in the tree, combining links to other nodes as well as the actual data. -template +/// A node in the tree, combining links to other nodes as well as the actual +/// data. +template class tree_node_ { // size: 5*4=20 bytes (on 32 bit arch), can be reduced by 8. - public: - tree_node_(); - tree_node_(const T&); - tree_node_(T&&); - - tree_node_ *parent; - tree_node_ *first_child, *last_child; - tree_node_ *prev_sibling, *next_sibling; - T data; -}; - -template +public: + tree_node_(); + tree_node_(const T &); + tree_node_(T &&); + + tree_node_ *parent; + tree_node_ *first_child, *last_child; + tree_node_ *prev_sibling, *next_sibling; + T data; +}; + +template tree_node_::tree_node_() - : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0) - { - } - -template -tree_node_::tree_node_(const T& val) - : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), data(val) - { - } - -template -tree_node_::tree_node_(T&& val) - : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), data(val) - { - } + : parent(0), first_child(0), last_child(0), prev_sibling(0), + next_sibling(0) {} + +template +tree_node_::tree_node_(const T &val) + : parent(0), first_child(0), last_child(0), prev_sibling(0), + next_sibling(0), data(val) {} + +template +tree_node_::tree_node_(T &&val) + : parent(0), first_child(0), last_child(0), prev_sibling(0), + next_sibling(0), data(val) {} // Throw an exception with a stacktrace. -//template -//void throw_with_trace(const E& e) +// template +// void throw_with_trace(const E& e) // { // throw boost::enable_error_info(e) // << traced(boost::stacktrace::stacktrace()); // } class navigation_error : public std::logic_error { - public: - navigation_error(const std::string& s) : std::logic_error(s) - { -// assert(1==0); -// std::ostringstream str; -// std::cerr << boost::stacktrace::stacktrace() << std::endl; -// str << boost::stacktrace::stacktrace(); -// stacktrace=str.str(); - } - -// virtual const char *what() const noexcept override -// { -// return (std::logic_error::what()+std::string("; ")+stacktrace).c_str(); -// } -// -// std::string stacktrace; +public: + navigation_error(const std::string &s) : std::logic_error(s) { + // assert(1==0); + // std::ostringstream str; + // std::cerr << boost::stacktrace::stacktrace() << + //std::endl; str << boost::stacktrace::stacktrace(); stacktrace=str.str(); + } + + // virtual const char *what() const noexcept override + // { + // return (std::logic_error::what()+std::string("; + //")+stacktrace).c_str(); + // } + // + // std::string stacktrace; }; - -template > > + +template >> class tree { - protected: - typedef tree_node_ tree_node; - public: - /// Value of the data stored at a node. - typedef T value_type; - - class iterator_base; - class pre_order_iterator; - class post_order_iterator; - class sibling_iterator; - class leaf_iterator; - - tree(); // empty constructor - tree(const T&); // constructor setting given element as head - tree(const iterator_base&); - tree(const tree&); // copy constructor - tree(tree&&); // move constructor - ~tree(); - tree& operator=(const tree&); // copy assignment - tree& operator=(tree&&); // move assignment - - /// Base class for iterators, only pointers stored, no traversal logic. +protected: + typedef tree_node_ tree_node; + +public: + /// Value of the data stored at a node. + typedef T value_type; + + class iterator_base; + class pre_order_iterator; + class post_order_iterator; + class sibling_iterator; + class leaf_iterator; + + tree(); // empty constructor + tree(const T &); // constructor setting given element as head + tree(const iterator_base &); + tree(const tree &); // copy constructor + tree(tree &&); // move constructor + ~tree(); + tree & + operator=(const tree &); // copy assignment + tree & + operator=(tree &&); // move assignment + + /// Base class for iterators, only pointers stored, no traversal logic. #ifdef __SGI_STL_PORT - class iterator_base : public stlport::bidirectional_iterator { + class iterator_base : public stlport::bidirectional_iterator { #else - class iterator_base { + class iterator_base { #endif - public: - typedef T value_type; - typedef T* pointer; - typedef T& reference; - typedef size_t size_type; - typedef ptrdiff_t difference_type; - typedef std::bidirectional_iterator_tag iterator_category; - - iterator_base(); - iterator_base(tree_node *); - - T& operator*() const; - T* operator->() const; - - /// When called, the next increment/decrement skips children of this node. - void skip_children(); - void skip_children(bool skip); - /// Number of children of the node pointed to by the iterator. - unsigned int number_of_children() const; - - sibling_iterator begin() const; - sibling_iterator end() const; - - tree_node *node; - protected: - bool skip_current_children_; - }; - - /// Depth-first iterator, first accessing the node, then its children. - class pre_order_iterator : public iterator_base { - public: - pre_order_iterator(); - pre_order_iterator(tree_node *); - pre_order_iterator(const iterator_base&); - pre_order_iterator(const sibling_iterator&); - - bool operator==(const pre_order_iterator&) const; - bool operator!=(const pre_order_iterator&) const; - pre_order_iterator& operator++(); - pre_order_iterator& operator--(); - pre_order_iterator operator++(int); - pre_order_iterator operator--(int); - pre_order_iterator& operator+=(unsigned int); - pre_order_iterator& operator-=(unsigned int); - - pre_order_iterator& next_skip_children(); - }; - - /// Depth-first iterator, first accessing the children, then the node itself. - class post_order_iterator : public iterator_base { - public: - post_order_iterator(); - post_order_iterator(tree_node *); - post_order_iterator(const iterator_base&); - post_order_iterator(const sibling_iterator&); - - bool operator==(const post_order_iterator&) const; - bool operator!=(const post_order_iterator&) const; - post_order_iterator& operator++(); - post_order_iterator& operator--(); - post_order_iterator operator++(int); - post_order_iterator operator--(int); - post_order_iterator& operator+=(unsigned int); - post_order_iterator& operator-=(unsigned int); - - /// Set iterator to the first child as deep as possible down the tree. - void descend_all(); - }; - - /// Breadth-first iterator, using a queue - class breadth_first_queued_iterator : public iterator_base { - public: - breadth_first_queued_iterator(); - breadth_first_queued_iterator(tree_node *); - breadth_first_queued_iterator(const iterator_base&); - - bool operator==(const breadth_first_queued_iterator&) const; - bool operator!=(const breadth_first_queued_iterator&) const; - breadth_first_queued_iterator& operator++(); - breadth_first_queued_iterator operator++(int); - breadth_first_queued_iterator& operator+=(unsigned int); - - private: - std::queue traversal_queue; - }; - - /// The default iterator types throughout the tree class. - typedef pre_order_iterator iterator; - typedef breadth_first_queued_iterator breadth_first_iterator; - - /// Iterator which traverses only the nodes at a given depth from the root. - class fixed_depth_iterator : public iterator_base { - public: - fixed_depth_iterator(); - fixed_depth_iterator(tree_node *); - fixed_depth_iterator(const iterator_base&); - fixed_depth_iterator(const sibling_iterator&); - fixed_depth_iterator(const fixed_depth_iterator&); - - void swap(fixed_depth_iterator&, fixed_depth_iterator&); - fixed_depth_iterator& operator=(fixed_depth_iterator); - - bool operator==(const fixed_depth_iterator&) const; - bool operator!=(const fixed_depth_iterator&) const; - fixed_depth_iterator& operator++(); - fixed_depth_iterator& operator--(); - fixed_depth_iterator operator++(int); - fixed_depth_iterator operator--(int); - fixed_depth_iterator& operator+=(unsigned int); - fixed_depth_iterator& operator-=(unsigned int); - - tree_node *top_node; - }; - - /// Iterator which traverses only the nodes which are siblings of each other. - class sibling_iterator : public iterator_base { - public: - sibling_iterator(); - sibling_iterator(tree_node *); - sibling_iterator(const sibling_iterator&); - sibling_iterator(const iterator_base&); - - void swap(sibling_iterator&, sibling_iterator&); - sibling_iterator& operator=(sibling_iterator); - - bool operator==(const sibling_iterator&) const; - bool operator!=(const sibling_iterator&) const; - sibling_iterator& operator++(); - sibling_iterator& operator--(); - sibling_iterator operator++(int); - sibling_iterator operator--(int); - sibling_iterator& operator+=(unsigned int); - sibling_iterator& operator-=(unsigned int); - - tree_node *range_first() const; - tree_node *range_last() const; - tree_node *parent_; - private: - void set_parent_(); - }; - - /// Iterator which traverses only the leaves. - class leaf_iterator : public iterator_base { - public: - leaf_iterator(); - leaf_iterator(tree_node *, tree_node *top=0); - leaf_iterator(const sibling_iterator&); - leaf_iterator(const iterator_base&); - - bool operator==(const leaf_iterator&) const; - bool operator!=(const leaf_iterator&) const; - leaf_iterator& operator++(); - leaf_iterator& operator--(); - leaf_iterator operator++(int); - leaf_iterator operator--(int); - leaf_iterator& operator+=(unsigned int); - leaf_iterator& operator-=(unsigned int); - private: - tree_node *top_node; - }; - - /// Return iterator to the beginning of the tree. - inline pre_order_iterator begin() const; - /// Return iterator to the end of the tree. - inline pre_order_iterator end() const; - /// Return post-order iterator to the beginning of the tree. - post_order_iterator begin_post() const; - /// Return post-order end iterator of the tree. - post_order_iterator end_post() const; - /// Return fixed-depth iterator to the first node at a given depth from the given iterator. - /// If 'walk_back=true', a depth=0 iterator will be taken from the beginning of the sibling - /// range, not the current node. - fixed_depth_iterator begin_fixed(const iterator_base&, unsigned int, bool walk_back=true) const; - /// Return fixed-depth end iterator. - fixed_depth_iterator end_fixed(const iterator_base&, unsigned int) const; - /// Return breadth-first iterator to the first node at a given depth. - breadth_first_queued_iterator begin_breadth_first() const; - /// Return breadth-first end iterator. - breadth_first_queued_iterator end_breadth_first() const; - /// Return sibling iterator to the first child of given node. - static sibling_iterator begin(const iterator_base&); - /// Return sibling end iterator for children of given node. - static sibling_iterator end(const iterator_base&); - /// Return leaf iterator to the first leaf of the tree. - leaf_iterator begin_leaf() const; - /// Return leaf end iterator for entire tree. - leaf_iterator end_leaf() const; - /// Return leaf iterator to the first leaf of the subtree at the given node. - leaf_iterator begin_leaf(const iterator_base& top) const; - /// Return leaf end iterator for the subtree at the given node. - leaf_iterator end_leaf(const iterator_base& top) const; - - typedef std::vector path_t; - /// Return a path (to be taken from the 'top' node) corresponding to a node in the tree. - /// The first integer in path_t is the number of steps you need to go 'right' in the sibling - /// chain (so 0 if we go straight to the children). - path_t path_from_iterator(const iterator_base& iter, const iterator_base& top) const; - /// Return an iterator given a path from the 'top' node. - iterator iterator_from_path(const path_t&, const iterator_base& top) const; - - /// Return iterator to the parent of a node. Throws a `navigation_error` if the node - /// does not have a parent. - template static iter parent(iter); - /// Return iterator to the previous sibling of a node. - template static iter previous_sibling(iter); - /// Return iterator to the next sibling of a node. - template static iter next_sibling(iter); - /// Return iterator to the next node at a given depth. - template iter next_at_same_depth(iter) const; - - /// Erase all nodes of the tree. - void clear(); - /// Erase element at position pointed to by iterator, return incremented iterator. - template iter erase(iter); - /// Erase all children of the node pointed to by iterator. - void erase_children(const iterator_base&); - /// Erase all siblings to the right of the iterator. - void erase_right_siblings(const iterator_base&); - /// Erase all siblings to the left of the iterator. - void erase_left_siblings(const iterator_base&); - - /// Insert empty node as last/first child of node pointed to by position. - template iter append_child(iter position); - template iter prepend_child(iter position); - /// Insert node as last/first child of node pointed to by position. - template iter append_child(iter position, const T& x); - template iter append_child(iter position, T&& x); - template iter prepend_child(iter position, const T& x); - template iter prepend_child(iter position, T&& x); - /// Append the node (plus its children) at other_position as last/first child of position. - template iter append_child(iter position, iter other_position); - template iter prepend_child(iter position, iter other_position); - /// Append the nodes in the from-to range (plus their children) as last/first children of position. - template iter append_children(iter position, sibling_iterator from, sibling_iterator to); - template iter prepend_children(iter position, sibling_iterator from, sibling_iterator to); - - /// Short-hand to insert topmost node in otherwise empty tree. - pre_order_iterator set_head(const T& x); - pre_order_iterator set_head(T&& x); - /// Insert node as previous sibling of node pointed to by position. - template iter insert(iter position, const T& x); - template iter insert(iter position, T&& x); - /// Specialisation of previous member. - sibling_iterator insert(sibling_iterator position, const T& x); - sibling_iterator insert(sibling_iterator position, T&& x); - /// Insert node (with children) pointed to by subtree as previous sibling of node pointed to by position. - /// Does not change the subtree itself (use move_in or move_in_below for that). - template iter insert_subtree(iter position, const iterator_base& subtree); - /// Insert node as next sibling of node pointed to by position. - template iter insert_after(iter position, const T& x); - template iter insert_after(iter position, T&& x); - /// Insert node (with children) pointed to by subtree as next sibling of node pointed to by position. - template iter insert_subtree_after(iter position, const iterator_base& subtree); - - /// Replace node at 'position' with other node (keeping same children); 'position' becomes invalid. - template iter replace(iter position, const T& x); - /// Replace node at 'position' with subtree starting at 'from' (do not erase subtree at 'from'); see above. - template iter replace(iter position, const iterator_base& from); - /// Replace string of siblings (plus their children) with copy of a new string (with children); see above - sibling_iterator replace(sibling_iterator orig_begin, sibling_iterator orig_end, - sibling_iterator new_begin, sibling_iterator new_end); - - /// Move all children of node at 'position' to be siblings, returns position. - template iter flatten(iter position); - /// Move nodes in range to be children of 'position'. - template iter reparent(iter position, sibling_iterator begin, sibling_iterator end); - /// Move all child nodes of 'from' to be children of 'position'. - template iter reparent(iter position, iter from); - - /// Replace node with a new node, making the old node (plus subtree) a child of the new node. - template iter wrap(iter position, const T& x); - /// Replace the range of sibling nodes (plus subtrees), making these children of the new node. - template iter wrap(iter from, iter to, const T& x); - - /// Move 'source' node (plus its children) to become the next sibling of 'target'. - template iter move_after(iter target, iter source); - /// Move 'source' node (plus its children) to become the previous sibling of 'target'. - template iter move_before(iter target, iter source); - sibling_iterator move_before(sibling_iterator target, sibling_iterator source); - /// Move 'source' node (plus its children) to become the node at 'target' (erasing the node at 'target'). - template iter move_ontop(iter target, iter source); - - /// Extract the subtree starting at the indicated node, removing it from the original tree. - tree move_out(iterator); - /// Inverse of take_out: inserts the given tree as previous sibling of indicated node by a - /// move operation, that is, the given tree becomes empty. Returns iterator to the top node. - template iter move_in(iter, tree&); - /// As above, but now make the tree the last child of the indicated node. - template iter move_in_below(iter, tree&); - /// As above, but now make the tree the nth child of the indicated node (if possible). - template iter move_in_as_nth_child(iter, size_t, tree&); - - /// Merge with other tree, creating new branches and leaves only if they are not already present. - void merge(sibling_iterator, sibling_iterator, sibling_iterator, sibling_iterator, - bool duplicate_leaves=false); - /// As above, but using two trees with a single top node at the 'to' and 'from' positions. - void merge(iterator to, iterator from, bool duplicate_leaves); - /// Sort (std::sort only moves values of nodes, this one moves children as well). - void sort(sibling_iterator from, sibling_iterator to, bool deep=false); - template - void sort(sibling_iterator from, sibling_iterator to, StrictWeakOrdering comp, bool deep=false); - /// Compare two ranges of nodes (compares nodes as well as tree structure). - template - bool equal(const iter& one, const iter& two, const iter& three) const; - template - bool equal(const iter& one, const iter& two, const iter& three, BinaryPredicate) const; - template - bool equal_subtree(const iter& one, const iter& two) const; - template - bool equal_subtree(const iter& one, const iter& two, BinaryPredicate) const; - /// Extract a new tree formed by the range of siblings plus all their children. - tree subtree(sibling_iterator from, sibling_iterator to) const; - void subtree(tree&, sibling_iterator from, sibling_iterator to) const; - /// Exchange the node (plus subtree) with its sibling node (do nothing if no sibling present). - void swap(sibling_iterator it); - /// Exchange two nodes (plus subtrees). The iterators will remain valid and keep - /// pointing to the same nodes, which now sit at different locations in the tree. - void swap(iterator, iterator); - - /// Count the total number of nodes. - size_t size() const; - /// Count the total number of nodes below the indicated node (plus one). - size_t size(const iterator_base&) const; - /// Check if tree is empty. - bool empty() const; - /// Compute the depth to the root or to a fixed other iterator. - static int depth(const iterator_base&); - static int depth(const iterator_base&, const iterator_base&); - /// Compute the depth to the root, counting all levels for which predicate returns true. - template - static int depth(const iterator_base&, Predicate p); - /// Compute the depth distance between two nodes, counting all levels for which predicate returns true. - template - static int distance(const iterator_base& top, const iterator_base& bottom, Predicate p); - /// Determine the maximal depth of the tree. An empty tree has max_depth=-1. - int max_depth() const; - /// Determine the maximal depth of the tree with top node at the given position. - int max_depth(const iterator_base&) const; - /// Count the number of children of node at position. - static unsigned int number_of_children(const iterator_base&); - /// Count the number of siblings (left and right) of node at iterator. Total nodes at this level is +1. - unsigned int number_of_siblings(const iterator_base&) const; - /// Determine whether node at position is in the subtrees with indicated top node. - bool is_in_subtree(const iterator_base& position, const iterator_base& top) const; - /// Determine whether node at position is in the subtrees with root in the range. - bool is_in_subtree(const iterator_base& position, const iterator_base& begin, - const iterator_base& end) const; - /// Determine whether the iterator is an 'end' iterator and thus not actually pointing to a node. - bool is_valid(const iterator_base&) const; - /// Determine whether the iterator is one of the 'head' nodes at the top level, i.e. has no parent. - static bool is_head(const iterator_base&); - /// Find the lowest common ancestor of two nodes, that is, the deepest node such that - /// both nodes are descendants of it. - iterator lowest_common_ancestor(const iterator_base&, const iterator_base &) const; - - /// Determine the index of a node in the range of siblings to which it belongs. - unsigned int index(sibling_iterator it) const; - /// Inverse of 'index': return the n-th child of the node at position. - static sibling_iterator child(const iterator_base& position, unsigned int); - /// Return iterator to the sibling indicated by index - sibling_iterator sibling(const iterator_base& position, unsigned int) const; - - /// For debugging only: verify internal consistency by inspecting all pointers in the tree - /// (which will also trigger a valgrind error in case something got corrupted). - void debug_verify_consistency() const; - - /// Comparator class for iterators (compares pointer values; why doesn't this work automatically?) - class iterator_base_less { - public: - bool operator()(const typename tree::iterator_base& one, - const typename tree::iterator_base& two) const - { - return one.node < two.node; - } - }; - tree_node *head, *feet; // head/feet are always dummy; if an iterator points to them it is invalid - private: - tree_node_allocator alloc_; - void head_initialise_(); - void copy_(const tree& other); - - /// Comparator class for two nodes of a tree (used for sorting and searching). - template - class compare_nodes { - public: - compare_nodes(StrictWeakOrdering comp) : comp_(comp) {} - - bool operator()(const tree_node *a, const tree_node *b) const - { - return comp_(a->data, b->data); - } - private: - StrictWeakOrdering comp_; - }; - }; - -//template -//class iterator_base_less { + public: + typedef T value_type; + typedef T *pointer; + typedef T &reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + iterator_base(); + iterator_base(tree_node *); + + T &operator*() const; + T *operator->() const; + + /// When called, the next increment/decrement skips children of this node. + void skip_children(); + void skip_children(bool skip); + /// Number of children of the node pointed to by the iterator. + unsigned int number_of_children() const; + + sibling_iterator begin() const; + sibling_iterator end() const; + + tree_node *node; + + protected: + bool skip_current_children_; + }; + + /// Depth-first iterator, first accessing the node, then its children. + class pre_order_iterator : public iterator_base { + public: + pre_order_iterator(); + pre_order_iterator(tree_node *); + pre_order_iterator(const iterator_base &); + pre_order_iterator(const sibling_iterator &); + + bool operator==(const pre_order_iterator &) const; + bool operator!=(const pre_order_iterator &) const; + pre_order_iterator &operator++(); + pre_order_iterator &operator--(); + pre_order_iterator operator++(int); + pre_order_iterator operator--(int); + pre_order_iterator &operator+=(unsigned int); + pre_order_iterator &operator-=(unsigned int); + + pre_order_iterator &next_skip_children(); + }; + + /// Depth-first iterator, first accessing the children, then the node itself. + class post_order_iterator : public iterator_base { + public: + post_order_iterator(); + post_order_iterator(tree_node *); + post_order_iterator(const iterator_base &); + post_order_iterator(const sibling_iterator &); + + bool operator==(const post_order_iterator &) const; + bool operator!=(const post_order_iterator &) const; + post_order_iterator &operator++(); + post_order_iterator &operator--(); + post_order_iterator operator++(int); + post_order_iterator operator--(int); + post_order_iterator &operator+=(unsigned int); + post_order_iterator &operator-=(unsigned int); + + /// Set iterator to the first child as deep as possible down the tree. + void descend_all(); + }; + + /// Breadth-first iterator, using a queue + class breadth_first_queued_iterator : public iterator_base { + public: + breadth_first_queued_iterator(); + breadth_first_queued_iterator(tree_node *); + breadth_first_queued_iterator(const iterator_base &); + + bool operator==(const breadth_first_queued_iterator &) const; + bool operator!=(const breadth_first_queued_iterator &) const; + breadth_first_queued_iterator &operator++(); + breadth_first_queued_iterator operator++(int); + breadth_first_queued_iterator &operator+=(unsigned int); + + private: + std::queue traversal_queue; + }; + + /// The default iterator types throughout the tree class. + typedef pre_order_iterator iterator; + typedef breadth_first_queued_iterator breadth_first_iterator; + + /// Iterator which traverses only the nodes at a given depth from the root. + class fixed_depth_iterator : public iterator_base { + public: + fixed_depth_iterator(); + fixed_depth_iterator(tree_node *); + fixed_depth_iterator(const iterator_base &); + fixed_depth_iterator(const sibling_iterator &); + fixed_depth_iterator(const fixed_depth_iterator &); + + void swap(fixed_depth_iterator &, fixed_depth_iterator &); + fixed_depth_iterator &operator=(fixed_depth_iterator); + + bool operator==(const fixed_depth_iterator &) const; + bool operator!=(const fixed_depth_iterator &) const; + fixed_depth_iterator &operator++(); + fixed_depth_iterator &operator--(); + fixed_depth_iterator operator++(int); + fixed_depth_iterator operator--(int); + fixed_depth_iterator &operator+=(unsigned int); + fixed_depth_iterator &operator-=(unsigned int); + + tree_node *top_node; + }; + + /// Iterator which traverses only the nodes which are siblings of each other. + class sibling_iterator : public iterator_base { + public: + sibling_iterator(); + sibling_iterator(tree_node *); + sibling_iterator(const sibling_iterator &); + sibling_iterator(const iterator_base &); + + void swap(sibling_iterator &, sibling_iterator &); + sibling_iterator &operator=(sibling_iterator); + + bool operator==(const sibling_iterator &) const; + bool operator!=(const sibling_iterator &) const; + sibling_iterator &operator++(); + sibling_iterator &operator--(); + sibling_iterator operator++(int); + sibling_iterator operator--(int); + sibling_iterator &operator+=(unsigned int); + sibling_iterator &operator-=(unsigned int); + + tree_node *range_first() const; + tree_node *range_last() const; + tree_node *parent_; + + private: + void set_parent_(); + }; + + /// Iterator which traverses only the leaves. + class leaf_iterator : public iterator_base { + public: + leaf_iterator(); + leaf_iterator(tree_node *, tree_node *top = 0); + leaf_iterator(const sibling_iterator &); + leaf_iterator(const iterator_base &); + + bool operator==(const leaf_iterator &) const; + bool operator!=(const leaf_iterator &) const; + leaf_iterator &operator++(); + leaf_iterator &operator--(); + leaf_iterator operator++(int); + leaf_iterator operator--(int); + leaf_iterator &operator+=(unsigned int); + leaf_iterator &operator-=(unsigned int); + + private: + tree_node *top_node; + }; + + /// Return iterator to the beginning of the tree. + inline pre_order_iterator begin() const; + /// Return iterator to the end of the tree. + inline pre_order_iterator end() const; + /// Return post-order iterator to the beginning of the tree. + post_order_iterator begin_post() const; + /// Return post-order end iterator of the tree. + post_order_iterator end_post() const; + /// Return fixed-depth iterator to the first node at a given depth from the + /// given iterator. If 'walk_back=true', a depth=0 iterator will be taken from + /// the beginning of the sibling range, not the current node. + fixed_depth_iterator begin_fixed(const iterator_base &, unsigned int, + bool walk_back = true) const; + /// Return fixed-depth end iterator. + fixed_depth_iterator end_fixed(const iterator_base &, unsigned int) const; + /// Return breadth-first iterator to the first node at a given depth. + breadth_first_queued_iterator begin_breadth_first() const; + /// Return breadth-first end iterator. + breadth_first_queued_iterator end_breadth_first() const; + /// Return sibling iterator to the first child of given node. + static sibling_iterator begin(const iterator_base &); + /// Return sibling end iterator for children of given node. + static sibling_iterator end(const iterator_base &); + /// Return leaf iterator to the first leaf of the tree. + leaf_iterator begin_leaf() const; + /// Return leaf end iterator for entire tree. + leaf_iterator end_leaf() const; + /// Return leaf iterator to the first leaf of the subtree at the given node. + leaf_iterator begin_leaf(const iterator_base &top) const; + /// Return leaf end iterator for the subtree at the given node. + leaf_iterator end_leaf(const iterator_base &top) const; + + typedef std::vector path_t; + /// Return a path (to be taken from the 'top' node) corresponding to a node in + /// the tree. The first integer in path_t is the number of steps you need to + /// go 'right' in the sibling chain (so 0 if we go straight to the children). + path_t path_from_iterator(const iterator_base &iter, + const iterator_base &top) const; + /// Return an iterator given a path from the 'top' node. + iterator iterator_from_path(const path_t &, const iterator_base &top) const; + + /// Return iterator to the parent of a node. Throws a `navigation_error` if + /// the node does not have a parent. + template static iter parent(iter); + /// Return iterator to the previous sibling of a node. + template static iter previous_sibling(iter); + /// Return iterator to the next sibling of a node. + template static iter next_sibling(iter); + /// Return iterator to the next node at a given depth. + template iter next_at_same_depth(iter) const; + + /// Erase all nodes of the tree. + void clear(); + /// Erase element at position pointed to by iterator, return incremented + /// iterator. + template iter erase(iter); + /// Erase all children of the node pointed to by iterator. + void erase_children(const iterator_base &); + /// Erase all siblings to the right of the iterator. + void erase_right_siblings(const iterator_base &); + /// Erase all siblings to the left of the iterator. + void erase_left_siblings(const iterator_base &); + + /// Insert empty node as last/first child of node pointed to by position. + template iter append_child(iter position); + template iter prepend_child(iter position); + /// Insert node as last/first child of node pointed to by position. + template iter append_child(iter position, const T &x); + template iter append_child(iter position, T &&x); + template iter prepend_child(iter position, const T &x); + template iter prepend_child(iter position, T &&x); + /// Append the node (plus its children) at other_position as last/first child + /// of position. + template + iter append_child(iter position, iter other_position); + template + iter prepend_child(iter position, iter other_position); + /// Append the nodes in the from-to range (plus their children) as last/first + /// children of position. + template + iter append_children(iter position, sibling_iterator from, + sibling_iterator to); + template + iter prepend_children(iter position, sibling_iterator from, + sibling_iterator to); + + /// Short-hand to insert topmost node in otherwise empty tree. + pre_order_iterator set_head(const T &x); + pre_order_iterator set_head(T &&x); + /// Insert node as previous sibling of node pointed to by position. + template iter insert(iter position, const T &x); + template iter insert(iter position, T &&x); + /// Specialisation of previous member. + sibling_iterator insert(sibling_iterator position, const T &x); + sibling_iterator insert(sibling_iterator position, T &&x); + /// Insert node (with children) pointed to by subtree as previous sibling of + /// node pointed to by position. Does not change the subtree itself (use + /// move_in or move_in_below for that). + template + iter insert_subtree(iter position, const iterator_base &subtree); + /// Insert node as next sibling of node pointed to by position. + template iter insert_after(iter position, const T &x); + template iter insert_after(iter position, T &&x); + /// Insert node (with children) pointed to by subtree as next sibling of node + /// pointed to by position. + template + iter insert_subtree_after(iter position, const iterator_base &subtree); + + /// Replace node at 'position' with other node (keeping same children); + /// 'position' becomes invalid. + template iter replace(iter position, const T &x); + /// Replace node at 'position' with subtree starting at 'from' (do not erase + /// subtree at 'from'); see above. + template + iter replace(iter position, const iterator_base &from); + /// Replace string of siblings (plus their children) with copy of a new string + /// (with children); see above + sibling_iterator replace(sibling_iterator orig_begin, + sibling_iterator orig_end, + sibling_iterator new_begin, + sibling_iterator new_end); + + /// Move all children of node at 'position' to be siblings, returns position. + template iter flatten(iter position); + /// Move nodes in range to be children of 'position'. + template + iter reparent(iter position, sibling_iterator begin, sibling_iterator end); + /// Move all child nodes of 'from' to be children of 'position'. + template iter reparent(iter position, iter from); + + /// Replace node with a new node, making the old node (plus subtree) a child + /// of the new node. + template iter wrap(iter position, const T &x); + /// Replace the range of sibling nodes (plus subtrees), making these children + /// of the new node. + template iter wrap(iter from, iter to, const T &x); + + /// Move 'source' node (plus its children) to become the next sibling of + /// 'target'. + template iter move_after(iter target, iter source); + /// Move 'source' node (plus its children) to become the previous sibling of + /// 'target'. + template iter move_before(iter target, iter source); + sibling_iterator move_before(sibling_iterator target, + sibling_iterator source); + /// Move 'source' node (plus its children) to become the node at 'target' + /// (erasing the node at 'target'). + template iter move_ontop(iter target, iter source); + + /// Extract the subtree starting at the indicated node, removing it from the + /// original tree. + tree move_out(iterator); + /// Inverse of take_out: inserts the given tree as previous sibling of + /// indicated node by a move operation, that is, the given tree becomes empty. + /// Returns iterator to the top node. + template iter move_in(iter, tree &); + /// As above, but now make the tree the last child of the indicated node. + template iter move_in_below(iter, tree &); + /// As above, but now make the tree the nth child of the indicated node (if + /// possible). + template iter move_in_as_nth_child(iter, size_t, tree &); + + /// Merge with other tree, creating new branches and leaves only if they are + /// not already present. + void merge(sibling_iterator, sibling_iterator, sibling_iterator, + sibling_iterator, bool duplicate_leaves = false); + /// As above, but using two trees with a single top node at the 'to' and + /// 'from' positions. + void merge(iterator to, iterator from, bool duplicate_leaves); + /// Sort (std::sort only moves values of nodes, this one moves children as + /// well). + void sort(sibling_iterator from, sibling_iterator to, bool deep = false); + template + void sort(sibling_iterator from, sibling_iterator to, StrictWeakOrdering comp, + bool deep = false); + /// Compare two ranges of nodes (compares nodes as well as tree structure). + template + bool equal(const iter &one, const iter &two, const iter &three) const; + template + bool equal(const iter &one, const iter &two, const iter &three, + BinaryPredicate) const; + template + bool equal_subtree(const iter &one, const iter &two) const; + template + bool equal_subtree(const iter &one, const iter &two, BinaryPredicate) const; + /// Extract a new tree formed by the range of siblings plus all their + /// children. + tree subtree(sibling_iterator from, sibling_iterator to) const; + void subtree(tree &, sibling_iterator from, sibling_iterator to) const; + /// Exchange the node (plus subtree) with its sibling node (do nothing if no + /// sibling present). + void swap(sibling_iterator it); + /// Exchange two nodes (plus subtrees). The iterators will remain valid and + /// keep pointing to the same nodes, which now sit at different locations in + /// the tree. + void swap(iterator, iterator); + + /// Count the total number of nodes. + size_t size() const; + /// Count the total number of nodes below the indicated node (plus one). + size_t size(const iterator_base &) const; + /// Check if tree is empty. + bool empty() const; + /// Compute the depth to the root or to a fixed other iterator. + static int depth(const iterator_base &); + static int depth(const iterator_base &, const iterator_base &); + /// Compute the depth to the root, counting all levels for which predicate + /// returns true. + template + static int depth(const iterator_base &, Predicate p); + /// Compute the depth distance between two nodes, counting all levels for + /// which predicate returns true. + template + static int distance(const iterator_base &top, const iterator_base &bottom, + Predicate p); + /// Determine the maximal depth of the tree. An empty tree has max_depth=-1. + int max_depth() const; + /// Determine the maximal depth of the tree with top node at the given + /// position. + int max_depth(const iterator_base &) const; + /// Count the number of children of node at position. + static unsigned int number_of_children(const iterator_base &); + /// Count the number of siblings (left and right) of node at iterator. Total + /// nodes at this level is +1. + unsigned int number_of_siblings(const iterator_base &) const; + /// Determine whether node at position is in the subtrees with indicated top + /// node. + bool is_in_subtree(const iterator_base &position, + const iterator_base &top) const; + /// Determine whether node at position is in the subtrees with root in the + /// range. + bool is_in_subtree(const iterator_base &position, const iterator_base &begin, + const iterator_base &end) const; + /// Determine whether the iterator is an 'end' iterator and thus not actually + /// pointing to a node. + bool is_valid(const iterator_base &) const; + /// Determine whether the iterator is one of the 'head' nodes at the top + /// level, i.e. has no parent. + static bool is_head(const iterator_base &); + /// Find the lowest common ancestor of two nodes, that is, the deepest node + /// such that both nodes are descendants of it. + iterator lowest_common_ancestor(const iterator_base &, + const iterator_base &) const; + + /// Determine the index of a node in the range of siblings to which it + /// belongs. + unsigned int index(sibling_iterator it) const; + /// Inverse of 'index': return the n-th child of the node at position. + static sibling_iterator child(const iterator_base &position, unsigned int); + /// Return iterator to the sibling indicated by index + sibling_iterator sibling(const iterator_base &position, unsigned int) const; + + /// For debugging only: verify internal consistency by inspecting all pointers + /// in the tree (which will also trigger a valgrind error in case something + /// got corrupted). + void debug_verify_consistency() const; + + /// Comparator class for iterators (compares pointer values; why doesn't this + /// work automatically?) + class iterator_base_less { + public: + bool operator()( + const typename tree::iterator_base &one, + const typename tree::iterator_base &two) const { + return one.node < two.node; + } + }; + tree_node *head, *feet; // head/feet are always dummy; if an iterator points + // to them it is invalid +private: + tree_node_allocator alloc_; + void head_initialise_(); + void copy_(const tree &other); + + /// Comparator class for two nodes of a tree (used for sorting and searching). + template class compare_nodes { + public: + compare_nodes(StrictWeakOrdering comp) : comp_(comp) {} + + bool operator()(const tree_node *a, const tree_node *b) const { + return comp_(a->data, b->data); + } + + private: + StrictWeakOrdering comp_; + }; +}; + +// template +// class iterator_base_less { // public: -// bool operator()(const typename tree::iterator_base& one, -// const typename tree::iterator_base& two) const +// bool operator()(const typename tree::iterator_base& one, const typename tree::iterator_base& two) const // { -// txtout << "operatorclass<" << one.node < two.node << std::endl; -// return one.node < two.node; +// txtout << "operatorclass<" << one.node < two.node << +//std::endl; return one.node < two.node; // } -//}; +// }; // template // bool operator<(const typename tree::iterator& one, -// const typename tree::iterator& two) +// const typename tree::iterator& two) // { // txtout << "operator< " << one.node < two.node << std::endl; // if(one.node < two.node) return true; // return false; // } -// +// // template // bool operator==(const typename tree::iterator& one, -// const typename tree::iterator& two) +// const typename tree::iterator& two) // { // txtout << "operator== " << one.node == two.node << std::endl; // if(one.node == two.node) return true; // return false; // } -// +// // template -// bool operator>(const typename tree::iterator_base& one, -// const typename tree::iterator_base& two) +// bool operator>(const typename tree::iterator_base& +// one, const typename tree::iterator_base& two) // { // txtout << "operator> " << one.node < two.node << std::endl; // if(one.node > two.node) return true; // return false; // } +// Tree +template +tree::tree() { + head_initialise_(); +} -// Tree +template +tree::tree(const T &x) { + head_initialise_(); + set_head(x); +} + +template +tree::tree(tree &&x) { + head_initialise_(); + if (x.head->next_sibling != x.feet) { // move tree if non-empty only + head->next_sibling = x.head->next_sibling; + feet->prev_sibling = x.feet->prev_sibling; + x.head->next_sibling->prev_sibling = head; + x.feet->prev_sibling->next_sibling = feet; + x.head->next_sibling = x.feet; + x.feet->prev_sibling = x.head; + } +} + +template +tree::tree(const iterator_base &other) { + head_initialise_(); + set_head((*other)); + replace(begin(), other); +} + +template +tree::~tree() { + clear(); + std::allocator_traits::destroy(alloc_, head); + std::allocator_traits::destroy(alloc_, feet); + std::allocator_traits::deallocate(alloc_, head, 1); + std::allocator_traits::deallocate(alloc_, feet, 1); +} + +template +void tree::head_initialise_() { + head = std::allocator_traits::allocate(alloc_, 1, 0); + feet = std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, head, + tree_node_()); + std::allocator_traits::construct(alloc_, feet, + tree_node_()); + + head->parent = 0; + head->first_child = 0; + head->last_child = 0; + head->prev_sibling = 0; // head; + head->next_sibling = feet; // head; + + feet->parent = 0; + feet->first_child = 0; + feet->last_child = 0; + feet->prev_sibling = head; + feet->next_sibling = 0; +} + +template +tree &tree::operator=( + const tree &other) { + if (this != &other) + copy_(other); + return *this; +} + +template +tree & +tree::operator=(tree &&x) { + if (this != &x) { + clear(); // clear any existing data. + + head->next_sibling = x.head->next_sibling; + feet->prev_sibling = x.feet->prev_sibling; + x.head->next_sibling->prev_sibling = head; + x.feet->prev_sibling->next_sibling = feet; + x.head->next_sibling = x.feet; + x.feet->prev_sibling = x.head; + } + return *this; +} + +template +tree::tree(const tree &other) { + head_initialise_(); + copy_(other); +} + +template +void tree::copy_( + const tree &other) { + clear(); + pre_order_iterator it = other.begin(), to = begin(); + while (it != other.end()) { + to = insert(to, (*it)); + it.skip_children(); + ++it; + } + to = begin(); + it = other.begin(); + while (it != other.end()) { + to = replace(to, it); + to.skip_children(); + it.skip_children(); + ++to; + ++it; + } +} + +template +void tree::clear() { + if (head) + while (head->next_sibling != feet) + erase(pre_order_iterator(head->next_sibling)); +} template -tree::tree() - { - head_initialise_(); - } - -template -tree::tree(const T& x) - { - head_initialise_(); - set_head(x); - } - -template -tree::tree(tree&& x) - { - head_initialise_(); - if(x.head->next_sibling!=x.feet) { // move tree if non-empty only - head->next_sibling=x.head->next_sibling; - feet->prev_sibling=x.feet->prev_sibling; - x.head->next_sibling->prev_sibling=head; - x.feet->prev_sibling->next_sibling=feet; - x.head->next_sibling=x.feet; - x.feet->prev_sibling=x.head; - } - } - -template -tree::tree(const iterator_base& other) - { - head_initialise_(); - set_head((*other)); - replace(begin(), other); - } - -template -tree::~tree() - { - clear(); - std::allocator_traits::destroy(alloc_, head); - std::allocator_traits::destroy(alloc_, feet); - std::allocator_traits::deallocate(alloc_, head, 1); - std::allocator_traits::deallocate(alloc_, feet, 1); - } - -template -void tree::head_initialise_() - { - head = std::allocator_traits::allocate(alloc_, 1, 0); - feet = std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, head, tree_node_()); - std::allocator_traits::construct(alloc_, feet, tree_node_()); - - head->parent=0; - head->first_child=0; - head->last_child=0; - head->prev_sibling=0; //head; - head->next_sibling=feet; //head; - - feet->parent=0; - feet->first_child=0; - feet->last_child=0; - feet->prev_sibling=head; - feet->next_sibling=0; - } - -template -tree& tree::operator=(const tree& other) - { - if(this != &other) - copy_(other); - return *this; - } - -template -tree& tree::operator=(tree&& x) - { - if(this != &x) { - clear(); // clear any existing data. - - head->next_sibling=x.head->next_sibling; - feet->prev_sibling=x.feet->prev_sibling; - x.head->next_sibling->prev_sibling=head; - x.feet->prev_sibling->next_sibling=feet; - x.head->next_sibling=x.feet; - x.feet->prev_sibling=x.head; - } - return *this; - } - -template -tree::tree(const tree& other) - { - head_initialise_(); - copy_(other); - } - -template -void tree::copy_(const tree& other) - { - clear(); - pre_order_iterator it=other.begin(), to=begin(); - while(it!=other.end()) { - to=insert(to, (*it)); - it.skip_children(); - ++it; - } - to=begin(); - it=other.begin(); - while(it!=other.end()) { - to=replace(to, it); - to.skip_children(); - it.skip_children(); - ++to; - ++it; - } - } - -template -void tree::clear() - { - if(head) - while(head->next_sibling!=feet) - erase(pre_order_iterator(head->next_sibling)); - } - -template -void tree::erase_children(const iterator_base& it) - { -// std::cout << "erase_children " << it.node << std::endl; - if(it.node==0) return; - - tree_node *cur=it.node->first_child; - tree_node *prev=0; - - while(cur!=0) { - prev=cur; - cur=cur->next_sibling; - erase_children(pre_order_iterator(prev)); - std::allocator_traits::destroy(alloc_, prev); - std::allocator_traits::deallocate(alloc_, prev, 1); - } - it.node->first_child=0; - it.node->last_child=0; -// std::cout << "exit" << std::endl; - } - -template -void tree::erase_right_siblings(const iterator_base& it) - { - if(it.node==0) return; - - tree_node *cur=it.node->next_sibling; - tree_node *prev=0; - - while(cur!=0) { - prev=cur; - cur=cur->next_sibling; - erase_children(pre_order_iterator(prev)); - std::allocator_traits::destroy(alloc_, prev); - std::allocator_traits::deallocate(alloc_, prev, 1); - } - it.node->next_sibling=0; - if(it.node->parent!=0) - it.node->parent->last_child=it.node; - } - -template -void tree::erase_left_siblings(const iterator_base& it) - { - if(it.node==0) return; - - tree_node *cur=it.node->prev_sibling; - tree_node *prev=0; - - while(cur!=0) { - prev=cur; - cur=cur->prev_sibling; - erase_children(pre_order_iterator(prev)); - std::allocator_traits::destroy(alloc_, prev); - std::allocator_traits::deallocate(alloc_, prev, 1); - } - it.node->prev_sibling=0; - if(it.node->parent!=0) - it.node->parent->first_child=it.node; - } - -template -template -iter tree::erase(iter it) - { - tree_node *cur=it.node; - assert(cur!=head); - iter ret=it; - ret.skip_children(); - ++ret; - erase_children(it); - if(cur->prev_sibling==0) { - cur->parent->first_child=cur->next_sibling; - } - else { - cur->prev_sibling->next_sibling=cur->next_sibling; - } - if(cur->next_sibling==0) { - cur->parent->last_child=cur->prev_sibling; - } - else { - cur->next_sibling->prev_sibling=cur->prev_sibling; - } - - std::allocator_traits::destroy(alloc_, cur); - std::allocator_traits::deallocate(alloc_, cur, 1); - return ret; - } - -template -typename tree::pre_order_iterator tree::begin() const - { - return pre_order_iterator(head->next_sibling); - } - -template -typename tree::pre_order_iterator tree::end() const - { - return pre_order_iterator(feet); - } - -template -typename tree::breadth_first_queued_iterator tree::begin_breadth_first() const - { - return breadth_first_queued_iterator(head->next_sibling); - } - -template -typename tree::breadth_first_queued_iterator tree::end_breadth_first() const - { - return breadth_first_queued_iterator(); - } - -template -typename tree::post_order_iterator tree::begin_post() const - { - tree_node *tmp=head->next_sibling; - if(tmp!=feet) { - while(tmp->first_child) - tmp=tmp->first_child; - } - return post_order_iterator(tmp); - } - -template -typename tree::post_order_iterator tree::end_post() const - { - return post_order_iterator(feet); - } - -template -typename tree::fixed_depth_iterator tree::begin_fixed(const iterator_base& pos, unsigned int dp, bool walk_back) const - { - typename tree::fixed_depth_iterator ret; - ret.top_node=pos.node; - - tree_node *tmp=pos.node; - unsigned int curdepth=0; - while(curdepthfirst_child==0) { - if(tmp->next_sibling==0) { - // try to walk up and then right again - do { - if(tmp==ret.top_node) - throw std::range_error("tree: begin_fixed out of range"); - tmp=tmp->parent; - if(tmp==0) - throw std::range_error("tree: begin_fixed out of range"); - --curdepth; - } while(tmp->next_sibling==0); - } - tmp=tmp->next_sibling; - } - tmp=tmp->first_child; - ++curdepth; - } - - // Now walk back to the first sibling in this range. - if(walk_back) - while(tmp->prev_sibling!=0) - tmp=tmp->prev_sibling; - - ret.node=tmp; - return ret; - } - -template -typename tree::fixed_depth_iterator tree::end_fixed(const iterator_base& pos, unsigned int dp) const - { - assert(1==0); // FIXME: not correct yet: use is_valid() as a temporary workaround - tree_node *tmp=pos.node; - unsigned int curdepth=1; - while(curdepthfirst_child==0) { - tmp=tmp->next_sibling; - if(tmp==0) - throw std::range_error("tree: end_fixed out of range"); - } - tmp=tmp->first_child; - ++curdepth; - } - return tmp; - } - -template -typename tree::sibling_iterator tree::begin(const iterator_base& pos) - { - assert(pos.node!=0); - if(pos.node->first_child==0) { - return end(pos); - } - return pos.node->first_child; - } - -template -typename tree::sibling_iterator tree::end(const iterator_base& pos) - { - sibling_iterator ret(0); - ret.parent_=pos.node; - return ret; - } - -template -typename tree::leaf_iterator tree::begin_leaf() const - { - tree_node *tmp=head->next_sibling; - if(tmp!=feet) { - while(tmp->first_child) - tmp=tmp->first_child; +void tree::erase_children(const iterator_base &it) { + // std::cout << "erase_children " << it.node << std::endl; + if (it.node == 0) + return; + + tree_node *cur = it.node->first_child; + tree_node *prev = 0; + + while (cur != 0) { + prev = cur; + cur = cur->next_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->first_child = 0; + it.node->last_child = 0; + // std::cout << "exit" << std::endl; +} + +template +void tree::erase_right_siblings( + const iterator_base &it) { + if (it.node == 0) + return; + + tree_node *cur = it.node->next_sibling; + tree_node *prev = 0; + + while (cur != 0) { + prev = cur; + cur = cur->next_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->next_sibling = 0; + if (it.node->parent != 0) + it.node->parent->last_child = it.node; +} + +template +void tree::erase_left_siblings( + const iterator_base &it) { + if (it.node == 0) + return; + + tree_node *cur = it.node->prev_sibling; + tree_node *prev = 0; + + while (cur != 0) { + prev = cur; + cur = cur->prev_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->prev_sibling = 0; + if (it.node->parent != 0) + it.node->parent->first_child = it.node; +} + +template +template +iter tree::erase(iter it) { + tree_node *cur = it.node; + assert(cur != head); + iter ret = it; + ret.skip_children(); + ++ret; + erase_children(it); + if (cur->prev_sibling == 0) { + cur->parent->first_child = cur->next_sibling; + } else { + cur->prev_sibling->next_sibling = cur->next_sibling; + } + if (cur->next_sibling == 0) { + cur->parent->last_child = cur->prev_sibling; + } else { + cur->next_sibling->prev_sibling = cur->prev_sibling; + } + + std::allocator_traits::destroy(alloc_, cur); + std::allocator_traits::deallocate(alloc_, cur, 1); + return ret; +} + +template +typename tree::pre_order_iterator +tree::begin() const { + return pre_order_iterator(head->next_sibling); +} + +template +typename tree::pre_order_iterator +tree::end() const { + return pre_order_iterator(feet); +} + +template +typename tree::breadth_first_queued_iterator +tree::begin_breadth_first() const { + return breadth_first_queued_iterator(head->next_sibling); +} + +template +typename tree::breadth_first_queued_iterator +tree::end_breadth_first() const { + return breadth_first_queued_iterator(); +} + +template +typename tree::post_order_iterator +tree::begin_post() const { + tree_node *tmp = head->next_sibling; + if (tmp != feet) { + while (tmp->first_child) + tmp = tmp->first_child; + } + return post_order_iterator(tmp); +} + +template +typename tree::post_order_iterator +tree::end_post() const { + return post_order_iterator(feet); +} + +template +typename tree::fixed_depth_iterator +tree::begin_fixed(const iterator_base &pos, + unsigned int dp, + bool walk_back) const { + typename tree::fixed_depth_iterator ret; + ret.top_node = pos.node; + + tree_node *tmp = pos.node; + unsigned int curdepth = 0; + while (curdepth < dp) { // go down one level + while (tmp->first_child == 0) { + if (tmp->next_sibling == 0) { + // try to walk up and then right again + do { + if (tmp == ret.top_node) + throw std::range_error("tree: begin_fixed out of range"); + tmp = tmp->parent; + if (tmp == 0) + throw std::range_error("tree: begin_fixed out of range"); + --curdepth; + } while (tmp->next_sibling == 0); } - return leaf_iterator(tmp); - } - -template -typename tree::leaf_iterator tree::end_leaf() const - { - return leaf_iterator(feet); - } - -template -typename tree::path_t tree::path_from_iterator(const iterator_base& iter, const iterator_base& top) const - { - path_t path; - tree_node *walk=iter.node; - - do { - if(path.size()>0) - walk=walk->parent; - int num=0; - while(walk!=top.node && walk->prev_sibling!=0 && walk->prev_sibling!=head) { - ++num; - walk=walk->prev_sibling; - } - path.push_back(num); - } - while(walk->parent!=0 && walk!=top.node); - - std::reverse(path.begin(), path.end()); - return path; - } - -template -typename tree::iterator tree::iterator_from_path(const path_t& path, const iterator_base& top) const - { - iterator it=top; - tree_node *walk=it.node; - - for(size_t step=0; step0) - walk=walk->first_child; - if(walk==0) - throw std::range_error("tree::iterator_from_path: no more nodes at step "+std::to_string(step)); - - for(int i=0; inext_sibling; - if(walk==0) - throw std::range_error("tree::iterator_from_path: out of siblings at step "+std::to_string(step)); - } - } - it.node=walk; - return it; - } - -template -typename tree::leaf_iterator tree::begin_leaf(const iterator_base& top) const - { - tree_node *tmp=top.node; - while(tmp->first_child) - tmp=tmp->first_child; - return leaf_iterator(tmp, top.node); - } - -template -typename tree::leaf_iterator tree::end_leaf(const iterator_base& top) const - { - return leaf_iterator(top.node, top.node); - } + tmp = tmp->next_sibling; + } + tmp = tmp->first_child; + ++curdepth; + } + + // Now walk back to the first sibling in this range. + if (walk_back) + while (tmp->prev_sibling != 0) + tmp = tmp->prev_sibling; + + ret.node = tmp; + return ret; +} + +template +typename tree::fixed_depth_iterator +tree::end_fixed(const iterator_base &pos, + unsigned int dp) const { + assert(1 == + 0); // FIXME: not correct yet: use is_valid() as a temporary workaround + tree_node *tmp = pos.node; + unsigned int curdepth = 1; + while (curdepth < dp) { // go down one level + while (tmp->first_child == 0) { + tmp = tmp->next_sibling; + if (tmp == 0) + throw std::range_error("tree: end_fixed out of range"); + } + tmp = tmp->first_child; + ++curdepth; + } + return tmp; +} + +template +typename tree::sibling_iterator +tree::begin(const iterator_base &pos) { + assert(pos.node != 0); + if (pos.node->first_child == 0) { + return end(pos); + } + return pos.node->first_child; +} + +template +typename tree::sibling_iterator +tree::end(const iterator_base &pos) { + sibling_iterator ret(0); + ret.parent_ = pos.node; + return ret; +} + +template +typename tree::leaf_iterator +tree::begin_leaf() const { + tree_node *tmp = head->next_sibling; + if (tmp != feet) { + while (tmp->first_child) + tmp = tmp->first_child; + } + return leaf_iterator(tmp); +} + +template +typename tree::leaf_iterator +tree::end_leaf() const { + return leaf_iterator(feet); +} + +template +typename tree::path_t +tree::path_from_iterator( + const iterator_base &iter, const iterator_base &top) const { + path_t path; + tree_node *walk = iter.node; + + do { + if (path.size() > 0) + walk = walk->parent; + int num = 0; + while (walk != top.node && walk->prev_sibling != 0 && + walk->prev_sibling != head) { + ++num; + walk = walk->prev_sibling; + } + path.push_back(num); + } while (walk->parent != 0 && walk != top.node); + + std::reverse(path.begin(), path.end()); + return path; +} + +template +typename tree::iterator +tree::iterator_from_path( + const path_t &path, const iterator_base &top) const { + iterator it = top; + tree_node *walk = it.node; + + for (size_t step = 0; step < path.size(); ++step) { + if (step > 0) + walk = walk->first_child; + if (walk == 0) + throw std::range_error( + "tree::iterator_from_path: no more nodes at step " + + std::to_string(step)); + + for (int i = 0; i < path[step]; ++i) { + walk = walk->next_sibling; + if (walk == 0) + throw std::range_error( + "tree::iterator_from_path: out of siblings at step " + + std::to_string(step)); + } + } + it.node = walk; + return it; +} + +template +typename tree::leaf_iterator +tree::begin_leaf(const iterator_base &top) const { + tree_node *tmp = top.node; + while (tmp->first_child) + tmp = tmp->first_child; + return leaf_iterator(tmp, top.node); +} + +template +typename tree::leaf_iterator +tree::end_leaf(const iterator_base &top) const { + return leaf_iterator(top.node, top.node); +} template template -iter tree::parent(iter position) - { - if(position.node==0) - throw navigation_error("tree: attempt to navigate from null iterator."); - - if(position.node->parent==0) - throw navigation_error("tree: attempt to navigate up past head node."); +iter tree::parent(iter position) { + if (position.node == 0) + throw navigation_error("tree: attempt to navigate from null iterator."); - return iter(position.node->parent); - } + if (position.node->parent == 0) + throw navigation_error("tree: attempt to navigate up past head node."); + + return iter(position.node->parent); +} template template -iter tree::previous_sibling(iter position) - { - assert(position.node!=0); - iter ret(position); - ret.node=position.node->prev_sibling; - return ret; - } +iter tree::previous_sibling(iter position) { + assert(position.node != 0); + iter ret(position); + ret.node = position.node->prev_sibling; + return ret; +} template template -iter tree::next_sibling(iter position) - { - assert(position.node!=0); - iter ret(position); - ret.node=position.node->next_sibling; - return ret; - } +iter tree::next_sibling(iter position) { + assert(position.node != 0); + iter ret(position); + ret.node = position.node->next_sibling; + return ret; +} template template -iter tree::next_at_same_depth(iter position) const - { - // We make use of a temporary fixed_depth iterator to implement this. - - typename tree::fixed_depth_iterator tmp(position.node); - - ++tmp; - return iter(tmp); - - // assert(position.node!=0); - // iter ret(position); - // - // if(position.node->next_sibling) { - // ret.node=position.node->next_sibling; - // } - // else { - // int relative_depth=0; - // upper: - // do { - // ret.node=ret.node->parent; - // if(ret.node==0) return ret; - // --relative_depth; - // } while(ret.node->next_sibling==0); - // lower: - // ret.node=ret.node->next_sibling; - // while(ret.node->first_child==0) { - // if(ret.node->next_sibling==0) - // goto upper; - // ret.node=ret.node->next_sibling; - // if(ret.node==0) return ret; - // } - // while(relative_depth<0 && ret.node->first_child!=0) { - // ret.node=ret.node->first_child; - // ++relative_depth; - // } - // if(relative_depth<0) { - // if(ret.node->next_sibling==0) goto upper; - // else goto lower; - // } - // } - // return ret; - } +iter tree::next_at_same_depth(iter position) const { + // We make use of a temporary fixed_depth iterator to implement this. + + typename tree::fixed_depth_iterator tmp( + position.node); + + ++tmp; + return iter(tmp); + + // assert(position.node!=0); + // iter ret(position); + // + // if(position.node->next_sibling) { + // ret.node=position.node->next_sibling; + // } + // else { + // int relative_depth=0; + // upper: + // do { + // ret.node=ret.node->parent; + // if(ret.node==0) return ret; + // --relative_depth; + // } while(ret.node->next_sibling==0); + // lower: + // ret.node=ret.node->next_sibling; + // while(ret.node->first_child==0) { + // if(ret.node->next_sibling==0) + // goto upper; + // ret.node=ret.node->next_sibling; + // if(ret.node==0) return ret; + // } + // while(relative_depth<0 && ret.node->first_child!=0) { + // ret.node=ret.node->first_child; + // ++relative_depth; + // } + // if(relative_depth<0) { + // if(ret.node->next_sibling==0) goto upper; + // else goto lower; + // } + // } + // return ret; +} template template -iter tree::append_child(iter position) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp, tree_node_()); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->last_child!=0) { - position.node->last_child->next_sibling=tmp; - } - else { - position.node->first_child=tmp; - } - tmp->prev_sibling=position.node->last_child; - position.node->last_child=tmp; - tmp->next_sibling=0; - return tmp; - } +iter tree::append_child(iter position) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); + + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, + tree_node_()); + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node; + if (position.node->last_child != 0) { + position.node->last_child->next_sibling = tmp; + } else { + position.node->first_child = tmp; + } + tmp->prev_sibling = position.node->last_child; + position.node->last_child = tmp; + tmp->next_sibling = 0; + return tmp; +} template template -iter tree::prepend_child(iter position) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp, tree_node_()); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->first_child!=0) { - position.node->first_child->prev_sibling=tmp; - } - else { - position.node->last_child=tmp; - } - tmp->next_sibling=position.node->first_child; - position.node->prev_child=tmp; - tmp->prev_sibling=0; - return tmp; - } +iter tree::prepend_child(iter position) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); + + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, + tree_node_()); + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node; + if (position.node->first_child != 0) { + position.node->first_child->prev_sibling = tmp; + } else { + position.node->last_child = tmp; + } + tmp->next_sibling = position.node->first_child; + position.node->prev_child = tmp; + tmp->prev_sibling = 0; + return tmp; +} template template -iter tree::append_child(iter position, const T& x) - { - // If your program fails here you probably used 'append_child' to add the top - // node to an empty tree. From version 1.45 the top element should be added - // using 'insert'. See the documentation for further information, and sorry about - // the API change. - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp, x); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->last_child!=0) { - position.node->last_child->next_sibling=tmp; - } - else { - position.node->first_child=tmp; - } - tmp->prev_sibling=position.node->last_child; - position.node->last_child=tmp; - tmp->next_sibling=0; - return tmp; - } +iter tree::append_child(iter position, const T &x) { + // If your program fails here you probably used 'append_child' to add the top + // node to an empty tree. From version 1.45 the top element should be added + // using 'insert'. See the documentation for further information, and sorry + // about the API change. + assert(position.node != head); + assert(position.node != feet); + assert(position.node); + + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node; + if (position.node->last_child != 0) { + position.node->last_child->next_sibling = tmp; + } else { + position.node->first_child = tmp; + } + tmp->prev_sibling = position.node->last_child; + position.node->last_child = tmp; + tmp->next_sibling = 0; + return tmp; +} template template -iter tree::append_child(iter position, T&& x) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp); // Here is where the move semantics kick in - std::swap(tmp->data, x); - - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->last_child!=0) { - position.node->last_child->next_sibling=tmp; - } - else { - position.node->first_child=tmp; - } - tmp->prev_sibling=position.node->last_child; - position.node->last_child=tmp; - tmp->next_sibling=0; - return tmp; - } +iter tree::append_child(iter position, T &&x) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); + + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct( + alloc_, tmp); // Here is where the move semantics kick in + std::swap(tmp->data, x); + + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node; + if (position.node->last_child != 0) { + position.node->last_child->next_sibling = tmp; + } else { + position.node->first_child = tmp; + } + tmp->prev_sibling = position.node->last_child; + position.node->last_child = tmp; + tmp->next_sibling = 0; + return tmp; +} template template -iter tree::prepend_child(iter position, const T& x) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp, x); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->first_child!=0) { - position.node->first_child->prev_sibling=tmp; - } - else { - position.node->last_child=tmp; - } - tmp->next_sibling=position.node->first_child; - position.node->first_child=tmp; - tmp->prev_sibling=0; - return tmp; - } +iter tree::prepend_child(iter position, const T &x) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); + + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node; + if (position.node->first_child != 0) { + position.node->first_child->prev_sibling = tmp; + } else { + position.node->last_child = tmp; + } + tmp->next_sibling = position.node->first_child; + position.node->first_child = tmp; + tmp->prev_sibling = 0; + return tmp; +} template template -iter tree::prepend_child(iter position, T&& x) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp); - std::swap(tmp->data, x); - - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node; - if(position.node->first_child!=0) { - position.node->first_child->prev_sibling=tmp; - } - else { - position.node->last_child=tmp; - } - tmp->next_sibling=position.node->first_child; - position.node->first_child=tmp; - tmp->prev_sibling=0; - return tmp; - } +iter tree::prepend_child(iter position, T &&x) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); + + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); + + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node; + if (position.node->first_child != 0) { + position.node->first_child->prev_sibling = tmp; + } else { + position.node->last_child = tmp; + } + tmp->next_sibling = position.node->first_child; + position.node->first_child = tmp; + tmp->prev_sibling = 0; + return tmp; +} template template -iter tree::append_child(iter position, iter other) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); +iter tree::append_child(iter position, iter other) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); - sibling_iterator aargh=append_child(position, value_type()); - return replace(aargh, other); - } + sibling_iterator aargh = append_child(position, value_type()); + return replace(aargh, other); +} template template -iter tree::prepend_child(iter position, iter other) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); +iter tree::prepend_child(iter position, iter other) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); - sibling_iterator aargh=prepend_child(position, value_type()); - return replace(aargh, other); - } + sibling_iterator aargh = prepend_child(position, value_type()); + return replace(aargh, other); +} template template -iter tree::append_children(iter position, sibling_iterator from, sibling_iterator to) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - iter ret=from; - - while(from!=to) { - insert_subtree(position.end(), from); - ++from; - } - return ret; - } +iter tree::append_children(iter position, + sibling_iterator from, + sibling_iterator to) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); + + iter ret = from; + + while (from != to) { + insert_subtree(position.end(), from); + ++from; + } + return ret; +} template template -iter tree::prepend_children(iter position, sibling_iterator from, sibling_iterator to) - { - assert(position.node!=head); - assert(position.node!=feet); - assert(position.node); - - if(from==to) return from; // should return end of tree? - - iter ret; - do { - --to; - ret=insert_subtree(position.begin(), to); - } - while(to!=from); - - return ret; - } +iter tree::prepend_children(iter position, + sibling_iterator from, + sibling_iterator to) { + assert(position.node != head); + assert(position.node != feet); + assert(position.node); + + if (from == to) + return from; // should return end of tree? + + iter ret; + do { + --to; + ret = insert_subtree(position.begin(), to); + } while (to != from); + + return ret; +} template -typename tree::pre_order_iterator tree::set_head(const T& x) - { - assert(head->next_sibling==feet); - return insert(iterator(feet), x); - } +typename tree::pre_order_iterator +tree::set_head(const T &x) { + assert(head->next_sibling == feet); + return insert(iterator(feet), x); +} template -typename tree::pre_order_iterator tree::set_head(T&& x) - { - assert(head->next_sibling==feet); - return insert(iterator(feet), x); - } +typename tree::pre_order_iterator +tree::set_head(T &&x) { + assert(head->next_sibling == feet); + return insert(iterator(feet), x); +} template template -iter tree::insert(iter position, const T& x) - { - if(position.node==0) { - position.node=feet; // Backward compatibility: when calling insert on a null node, - // insert before the feet. - } - assert(position.node!=head); // Cannot insert before head. - - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp, x); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node->parent; - tmp->next_sibling=position.node; - tmp->prev_sibling=position.node->prev_sibling; - position.node->prev_sibling=tmp; - - if(tmp->prev_sibling==0) { - if(tmp->parent) // when inserting nodes at the head, there is no parent - tmp->parent->first_child=tmp; - } - else - tmp->prev_sibling->next_sibling=tmp; - return tmp; - } +iter tree::insert(iter position, const T &x) { + if (position.node == 0) { + position.node = feet; // Backward compatibility: when calling insert on a + // null node, insert before the feet. + } + assert(position.node != head); // Cannot insert before head. + + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node->parent; + tmp->next_sibling = position.node; + tmp->prev_sibling = position.node->prev_sibling; + position.node->prev_sibling = tmp; + + if (tmp->prev_sibling == 0) { + if (tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child = tmp; + } else + tmp->prev_sibling->next_sibling = tmp; + return tmp; +} template template -iter tree::insert(iter position, T&& x) - { - if(position.node==0) { - position.node=feet; // Backward compatibility: when calling insert on a null node, - // insert before the feet. - } - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp); - std::swap(tmp->data, x); // Move semantics - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node->parent; - tmp->next_sibling=position.node; - tmp->prev_sibling=position.node->prev_sibling; - position.node->prev_sibling=tmp; - - if(tmp->prev_sibling==0) { - if(tmp->parent) // when inserting nodes at the head, there is no parent - tmp->parent->first_child=tmp; - } - else - tmp->prev_sibling->next_sibling=tmp; - return tmp; - } - -template -typename tree::sibling_iterator tree::insert(sibling_iterator position, const T& x) - { - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp, x); - tmp->first_child=0; - tmp->last_child=0; - - tmp->next_sibling=position.node; - if(position.node==0) { // iterator points to end of a subtree - tmp->parent=position.parent_; - tmp->prev_sibling=position.range_last(); - tmp->parent->last_child=tmp; - } - else { - tmp->parent=position.node->parent; - tmp->prev_sibling=position.node->prev_sibling; - position.node->prev_sibling=tmp; - } - - if(tmp->prev_sibling==0) { - if(tmp->parent) // when inserting nodes at the head, there is no parent - tmp->parent->first_child=tmp; - } - else - tmp->prev_sibling->next_sibling=tmp; - return tmp; - } - -template -typename tree::sibling_iterator tree::insert(sibling_iterator position, T&& x) - { - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp); - std::swap(tmp->data, x); // Move semantics - - tmp->first_child=0; - tmp->last_child=0; - - tmp->next_sibling=position.node; - if(position.node==0) { // iterator points to end of a subtree - tmp->parent=position.parent_; - tmp->prev_sibling=position.range_last(); - tmp->parent->last_child=tmp; - } - else { - tmp->parent=position.node->parent; - tmp->prev_sibling=position.node->prev_sibling; - position.node->prev_sibling=tmp; - } - - if(tmp->prev_sibling==0) { - if(tmp->parent) // when inserting nodes at the head, there is no parent - tmp->parent->first_child=tmp; - } - else - tmp->prev_sibling->next_sibling=tmp; - return tmp; - } +iter tree::insert(iter position, T &&x) { + if (position.node == 0) { + position.node = feet; // Backward compatibility: when calling insert on a + // null node, insert before the feet. + } + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // Move semantics + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node->parent; + tmp->next_sibling = position.node; + tmp->prev_sibling = position.node->prev_sibling; + position.node->prev_sibling = tmp; + + if (tmp->prev_sibling == 0) { + if (tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child = tmp; + } else + tmp->prev_sibling->next_sibling = tmp; + return tmp; +} + +template +typename tree::sibling_iterator +tree::insert(sibling_iterator position, const T &x) { + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->next_sibling = position.node; + if (position.node == 0) { // iterator points to end of a subtree + tmp->parent = position.parent_; + tmp->prev_sibling = position.range_last(); + tmp->parent->last_child = tmp; + } else { + tmp->parent = position.node->parent; + tmp->prev_sibling = position.node->prev_sibling; + position.node->prev_sibling = tmp; + } + + if (tmp->prev_sibling == 0) { + if (tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child = tmp; + } else + tmp->prev_sibling->next_sibling = tmp; + return tmp; +} + +template +typename tree::sibling_iterator +tree::insert(sibling_iterator position, T &&x) { + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // Move semantics + + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->next_sibling = position.node; + if (position.node == 0) { // iterator points to end of a subtree + tmp->parent = position.parent_; + tmp->prev_sibling = position.range_last(); + tmp->parent->last_child = tmp; + } else { + tmp->parent = position.node->parent; + tmp->prev_sibling = position.node->prev_sibling; + position.node->prev_sibling = tmp; + } + + if (tmp->prev_sibling == 0) { + if (tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child = tmp; + } else + tmp->prev_sibling->next_sibling = tmp; + return tmp; +} template template -iter tree::insert_after(iter position, const T& x) - { - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp, x); - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node->parent; - tmp->prev_sibling=position.node; - tmp->next_sibling=position.node->next_sibling; - position.node->next_sibling=tmp; - - if(tmp->next_sibling==0) { - if(tmp->parent) // when inserting nodes at the head, there is no parent - tmp->parent->last_child=tmp; - } - else { - tmp->next_sibling->prev_sibling=tmp; - } - return tmp; - } +iter tree::insert_after(iter position, const T &x) { + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node->parent; + tmp->prev_sibling = position.node; + tmp->next_sibling = position.node->next_sibling; + position.node->next_sibling = tmp; + + if (tmp->next_sibling == 0) { + if (tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->last_child = tmp; + } else { + tmp->next_sibling->prev_sibling = tmp; + } + return tmp; +} template template -iter tree::insert_after(iter position, T&& x) - { - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp); - std::swap(tmp->data, x); // move semantics - tmp->first_child=0; - tmp->last_child=0; - - tmp->parent=position.node->parent; - tmp->prev_sibling=position.node; - tmp->next_sibling=position.node->next_sibling; - position.node->next_sibling=tmp; - - if(tmp->next_sibling==0) { - if(tmp->parent) // when inserting nodes at the head, there is no parent - tmp->parent->last_child=tmp; - } - else { - tmp->next_sibling->prev_sibling=tmp; - } - return tmp; - } +iter tree::insert_after(iter position, T &&x) { + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // move semantics + tmp->first_child = 0; + tmp->last_child = 0; + + tmp->parent = position.node->parent; + tmp->prev_sibling = position.node; + tmp->next_sibling = position.node->next_sibling; + position.node->next_sibling = tmp; + + if (tmp->next_sibling == 0) { + if (tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->last_child = tmp; + } else { + tmp->next_sibling->prev_sibling = tmp; + } + return tmp; +} template template -iter tree::insert_subtree(iter position, const iterator_base& subtree) - { - // insert dummy - iter it=insert(position, value_type()); - // replace dummy with subtree - return replace(it, subtree); - } +iter tree::insert_subtree( + iter position, const iterator_base &subtree) { + // insert dummy + iter it = insert(position, value_type()); + // replace dummy with subtree + return replace(it, subtree); +} template template -iter tree::insert_subtree_after(iter position, const iterator_base& subtree) - { - // insert dummy - iter it=insert_after(position, value_type()); - // replace dummy with subtree - return replace(it, subtree); - } +iter tree::insert_subtree_after( + iter position, const iterator_base &subtree) { + // insert dummy + iter it = insert_after(position, value_type()); + // replace dummy with subtree + return replace(it, subtree); +} // template // template -// iter tree::insert_subtree(sibling_iterator position, iter subtree) +// iter tree::insert_subtree(sibling_iterator position, +// iter subtree) // { // // insert dummy // iter it(insert(position, value_type())); @@ -1481,1929 +1529,1957 @@ iter tree::insert_subtree_after(iter position, const ite template template -iter tree::replace(iter position, const T& x) - { -// kp::destructor(&position.node->data); -// kp::constructor(&position.node->data, x); - position.node->data=x; -// alloc_.destroy(position.node); -// alloc_.construct(position.node, x); - return position; - } +iter tree::replace(iter position, const T &x) { + // kp::destructor(&position.node->data); + // kp::constructor(&position.node->data, x); + position.node->data = x; + // alloc_.destroy(position.node); + // alloc_.construct(position.node, x); + return position; +} template template -iter tree::replace(iter position, const iterator_base& from) - { - assert(position.node!=head); - tree_node *current_from=from.node; - tree_node *start_from=from.node; - tree_node *current_to =position.node; - - // replace the node at position with head of the replacement tree at from -// std::cout << "warning!" << position.node << std::endl; - erase_children(position); -// std::cout << "no warning!" << std::endl; - tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); - std::allocator_traits::construct(alloc_, tmp, (*from)); - tmp->first_child=0; - tmp->last_child=0; - if(current_to->prev_sibling==0) { - if(current_to->parent!=0) - current_to->parent->first_child=tmp; - } - else { - current_to->prev_sibling->next_sibling=tmp; - } - tmp->prev_sibling=current_to->prev_sibling; - if(current_to->next_sibling==0) { - if(current_to->parent!=0) - current_to->parent->last_child=tmp; - } - else { - current_to->next_sibling->prev_sibling=tmp; - } - tmp->next_sibling=current_to->next_sibling; - tmp->parent=current_to->parent; -// kp::destructor(¤t_to->data); - std::allocator_traits::destroy(alloc_, current_to); - std::allocator_traits::deallocate(alloc_, current_to, 1); - current_to=tmp; - - // only at this stage can we fix 'last' - tree_node *last=from.node->next_sibling; - - pre_order_iterator toit=tmp; - // copy all children - do { - assert(current_from!=0); - if(current_from->first_child != 0) { - current_from=current_from->first_child; - toit=append_child(toit, current_from->data); - } - else { - while(current_from->next_sibling==0 && current_from!=start_from) { - current_from=current_from->parent; - toit=parent(toit); - assert(current_from!=0); - } - current_from=current_from->next_sibling; - if(current_from!=last) { - toit=append_child(parent(toit), current_from->data); - } - } - } - while(current_from!=last); - - return current_to; - } - -template -typename tree::sibling_iterator tree::replace( - sibling_iterator orig_begin, - sibling_iterator orig_end, - sibling_iterator new_begin, - sibling_iterator new_end) - { - tree_node *orig_first=orig_begin.node; - tree_node *new_first=new_begin.node; - tree_node *orig_last=orig_first; - while((++orig_begin)!=orig_end) - orig_last=orig_last->next_sibling; - tree_node *new_last=new_first; - while((++new_begin)!=new_end) - new_last=new_last->next_sibling; - - // insert all siblings in new_first..new_last before orig_first - bool first=true; - pre_order_iterator ret; - while(1==1) { - pre_order_iterator tt=insert_subtree(pre_order_iterator(orig_first), pre_order_iterator(new_first)); - if(first) { - ret=tt; - first=false; - } - if(new_first==new_last) - break; - new_first=new_first->next_sibling; - } - - // erase old range of siblings - bool last=false; - tree_node *next=orig_first; - while(1==1) { - if(next==orig_last) - last=true; - next=next->next_sibling; - erase((pre_order_iterator)orig_first); - if(last) - break; - orig_first=next; - } - return ret; - } +iter tree::replace(iter position, + const iterator_base &from) { + assert(position.node != head); + tree_node *current_from = from.node; + tree_node *start_from = from.node; + tree_node *current_to = position.node; + + // replace the node at position with head of the replacement tree at from + // std::cout << "warning!" << position.node << std::endl; + erase_children(position); + // std::cout << "no warning!" << std::endl; + tree_node *tmp = + std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, (*from)); + tmp->first_child = 0; + tmp->last_child = 0; + if (current_to->prev_sibling == 0) { + if (current_to->parent != 0) + current_to->parent->first_child = tmp; + } else { + current_to->prev_sibling->next_sibling = tmp; + } + tmp->prev_sibling = current_to->prev_sibling; + if (current_to->next_sibling == 0) { + if (current_to->parent != 0) + current_to->parent->last_child = tmp; + } else { + current_to->next_sibling->prev_sibling = tmp; + } + tmp->next_sibling = current_to->next_sibling; + tmp->parent = current_to->parent; + // kp::destructor(¤t_to->data); + std::allocator_traits::destroy(alloc_, current_to); + std::allocator_traits::deallocate(alloc_, current_to, 1); + current_to = tmp; + + // only at this stage can we fix 'last' + tree_node *last = from.node->next_sibling; + + pre_order_iterator toit = tmp; + // copy all children + do { + assert(current_from != 0); + if (current_from->first_child != 0) { + current_from = current_from->first_child; + toit = append_child(toit, current_from->data); + } else { + while (current_from->next_sibling == 0 && current_from != start_from) { + current_from = current_from->parent; + toit = parent(toit); + assert(current_from != 0); + } + current_from = current_from->next_sibling; + if (current_from != last) { + toit = append_child(parent(toit), current_from->data); + } + } + } while (current_from != last); + + return current_to; +} + +template +typename tree::sibling_iterator +tree::replace(sibling_iterator orig_begin, + sibling_iterator orig_end, + sibling_iterator new_begin, + sibling_iterator new_end) { + tree_node *orig_first = orig_begin.node; + tree_node *new_first = new_begin.node; + tree_node *orig_last = orig_first; + while ((++orig_begin) != orig_end) + orig_last = orig_last->next_sibling; + tree_node *new_last = new_first; + while ((++new_begin) != new_end) + new_last = new_last->next_sibling; + + // insert all siblings in new_first..new_last before orig_first + bool first = true; + pre_order_iterator ret; + while (1 == 1) { + pre_order_iterator tt = insert_subtree(pre_order_iterator(orig_first), + pre_order_iterator(new_first)); + if (first) { + ret = tt; + first = false; + } + if (new_first == new_last) + break; + new_first = new_first->next_sibling; + } + + // erase old range of siblings + bool last = false; + tree_node *next = orig_first; + while (1 == 1) { + if (next == orig_last) + last = true; + next = next->next_sibling; + erase((pre_order_iterator)orig_first); + if (last) + break; + orig_first = next; + } + return ret; +} + +template +template +iter tree::flatten(iter position) { + if (position.node->first_child == 0) + return position; + + tree_node *tmp = position.node->first_child; + while (tmp) { + tmp->parent = position.node->parent; + tmp = tmp->next_sibling; + } + if (position.node->next_sibling) { + position.node->last_child->next_sibling = position.node->next_sibling; + position.node->next_sibling->prev_sibling = position.node->last_child; + } else { + position.node->parent->last_child = position.node->last_child; + } + position.node->next_sibling = position.node->first_child; + position.node->next_sibling->prev_sibling = position.node; + position.node->first_child = 0; + position.node->last_child = 0; + + return position; +} + +template +template +iter tree::reparent(iter position, + sibling_iterator begin, + sibling_iterator end) { + tree_node *first = begin.node; + tree_node *last = first; + + assert(first != position.node); + + if (begin == end) + return begin; + // determine last node + while ((++begin) != end) { + last = last->next_sibling; + } + // move subtree + if (first->prev_sibling == 0) { + first->parent->first_child = last->next_sibling; + } else { + first->prev_sibling->next_sibling = last->next_sibling; + } + if (last->next_sibling == 0) { + last->parent->last_child = first->prev_sibling; + } else { + last->next_sibling->prev_sibling = first->prev_sibling; + } + if (position.node->first_child == 0) { + position.node->first_child = first; + position.node->last_child = last; + first->prev_sibling = 0; + } else { + position.node->last_child->next_sibling = first; + first->prev_sibling = position.node->last_child; + position.node->last_child = last; + } + last->next_sibling = 0; + + tree_node *pos = first; + for (;;) { + pos->parent = position.node; + if (pos == last) + break; + pos = pos->next_sibling; + } + + return first; +} + +template +template +iter tree::reparent(iter position, iter from) { + if (from.node->first_child == 0) + return position; + return reparent(position, from.node->first_child, end(from)); +} + +template +template +iter tree::wrap(iter position, const T &x) { + assert(position.node != 0); + sibling_iterator fr = position, to = position; + ++to; + iter ret = insert(position, x); + reparent(ret, fr, to); + return ret; +} template template -iter tree::flatten(iter position) - { - if(position.node->first_child==0) - return position; - - tree_node *tmp=position.node->first_child; - while(tmp) { - tmp->parent=position.node->parent; - tmp=tmp->next_sibling; - } - if(position.node->next_sibling) { - position.node->last_child->next_sibling=position.node->next_sibling; - position.node->next_sibling->prev_sibling=position.node->last_child; - } - else { - position.node->parent->last_child=position.node->last_child; - } - position.node->next_sibling=position.node->first_child; - position.node->next_sibling->prev_sibling=position.node; - position.node->first_child=0; - position.node->last_child=0; - - return position; - } +iter tree::wrap(iter from, iter to, const T &x) { + assert(from.node != 0); + iter ret = insert(from, x); + reparent(ret, from, to); + return ret; +} +template +template +iter tree::move_after(iter target, iter source) { + tree_node *dst = target.node; + tree_node *src = source.node; + assert(dst); + assert(src); + + if (dst == src) + return source; + if (dst->next_sibling) + if (dst->next_sibling == src) // already in the right spot + return source; + + // take src out of the tree + if (src->prev_sibling != 0) + src->prev_sibling->next_sibling = src->next_sibling; + else + src->parent->first_child = src->next_sibling; + if (src->next_sibling != 0) + src->next_sibling->prev_sibling = src->prev_sibling; + else + src->parent->last_child = src->prev_sibling; + + // connect it to the new point + if (dst->next_sibling != 0) + dst->next_sibling->prev_sibling = src; + else + dst->parent->last_child = src; + src->next_sibling = dst->next_sibling; + dst->next_sibling = src; + src->prev_sibling = dst; + src->parent = dst->parent; + return src; +} template template -iter tree::reparent(iter position, sibling_iterator begin, sibling_iterator end) - { - tree_node *first=begin.node; - tree_node *last=first; - - assert(first!=position.node); - - if(begin==end) return begin; - // determine last node - while((++begin)!=end) { - last=last->next_sibling; - } - // move subtree - if(first->prev_sibling==0) { - first->parent->first_child=last->next_sibling; - } - else { - first->prev_sibling->next_sibling=last->next_sibling; - } - if(last->next_sibling==0) { - last->parent->last_child=first->prev_sibling; - } - else { - last->next_sibling->prev_sibling=first->prev_sibling; - } - if(position.node->first_child==0) { - position.node->first_child=first; - position.node->last_child=last; - first->prev_sibling=0; - } - else { - position.node->last_child->next_sibling=first; - first->prev_sibling=position.node->last_child; - position.node->last_child=last; - } - last->next_sibling=0; - - tree_node *pos=first; - for(;;) { - pos->parent=position.node; - if(pos==last) break; - pos=pos->next_sibling; - } - - return first; - } - -template -template iter tree::reparent(iter position, iter from) - { - if(from.node->first_child==0) return position; - return reparent(position, from.node->first_child, end(from)); - } - -template -template iter tree::wrap(iter position, const T& x) - { - assert(position.node!=0); - sibling_iterator fr=position, to=position; - ++to; - iter ret = insert(position, x); - reparent(ret, fr, to); - return ret; - } - -template -template iter tree::wrap(iter from, iter to, const T& x) - { - assert(from.node!=0); - iter ret = insert(from, x); - reparent(ret, from, to); - return ret; - } - -template -template iter tree::move_after(iter target, iter source) - { - tree_node *dst=target.node; - tree_node *src=source.node; - assert(dst); - assert(src); - - if(dst==src) return source; - if(dst->next_sibling) - if(dst->next_sibling==src) // already in the right spot - return source; - - // take src out of the tree - if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; - else src->parent->first_child=src->next_sibling; - if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; - else src->parent->last_child=src->prev_sibling; - - // connect it to the new point - if(dst->next_sibling!=0) dst->next_sibling->prev_sibling=src; - else dst->parent->last_child=src; - src->next_sibling=dst->next_sibling; - dst->next_sibling=src; - src->prev_sibling=dst; - src->parent=dst->parent; - return src; - } - -template -template iter tree::move_before(iter target, iter source) - { - tree_node *dst=target.node; - tree_node *src=source.node; - assert(dst); - assert(src); - - if(dst==src) return source; - if(dst->prev_sibling) - if(dst->prev_sibling==src) // already in the right spot - return source; - - // take src out of the tree - if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; - else src->parent->first_child=src->next_sibling; - if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; - else src->parent->last_child=src->prev_sibling; - - // connect it to the new point - if(dst->prev_sibling!=0) dst->prev_sibling->next_sibling=src; - else dst->parent->first_child=src; - src->prev_sibling=dst->prev_sibling; - dst->prev_sibling=src; - src->next_sibling=dst; - src->parent=dst->parent; - return src; - } +iter tree::move_before(iter target, iter source) { + tree_node *dst = target.node; + tree_node *src = source.node; + assert(dst); + assert(src); + + if (dst == src) + return source; + if (dst->prev_sibling) + if (dst->prev_sibling == src) // already in the right spot + return source; + + // take src out of the tree + if (src->prev_sibling != 0) + src->prev_sibling->next_sibling = src->next_sibling; + else + src->parent->first_child = src->next_sibling; + if (src->next_sibling != 0) + src->next_sibling->prev_sibling = src->prev_sibling; + else + src->parent->last_child = src->prev_sibling; + + // connect it to the new point + if (dst->prev_sibling != 0) + dst->prev_sibling->next_sibling = src; + else + dst->parent->first_child = src; + src->prev_sibling = dst->prev_sibling; + dst->prev_sibling = src; + src->next_sibling = dst; + src->parent = dst->parent; + return src; +} // specialisation for sibling_iterators template -typename tree::sibling_iterator tree::move_before(sibling_iterator target, - sibling_iterator source) - { - tree_node *dst=target.node; - tree_node *src=source.node; - tree_node *dst_prev_sibling; - if(dst==0) { // must then be an end iterator - dst_prev_sibling=target.parent_->last_child; - assert(dst_prev_sibling); - } - else dst_prev_sibling=dst->prev_sibling; - assert(src); - - if(dst==src) return source; - if(dst_prev_sibling) - if(dst_prev_sibling==src) // already in the right spot - return source; - - // take src out of the tree - if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; - else src->parent->first_child=src->next_sibling; - if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; - else src->parent->last_child=src->prev_sibling; - - // connect it to the new point - if(dst_prev_sibling!=0) dst_prev_sibling->next_sibling=src; - else target.parent_->first_child=src; - src->prev_sibling=dst_prev_sibling; - if(dst) { - dst->prev_sibling=src; - src->parent=dst->parent; - } - else { - src->parent=dst_prev_sibling->parent; - } - src->next_sibling=dst; - return src; - } - -template -template iter tree::move_ontop(iter target, iter source) - { - tree_node *dst=target.node; - tree_node *src=source.node; - assert(dst); - assert(src); - - if(dst==src) return source; - -// if(dst==src->prev_sibling) { -// -// } - - // remember connection points - tree_node *b_prev_sibling=dst->prev_sibling; - tree_node *b_next_sibling=dst->next_sibling; - tree_node *b_parent=dst->parent; - - // remove target - erase(target); - - // take src out of the tree - if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; - else { - assert(src->parent!=0); - src->parent->first_child=src->next_sibling; - } - if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; - else { - assert(src->parent!=0); - src->parent->last_child=src->prev_sibling; - } - - // connect it to the new point - if(b_prev_sibling!=0) b_prev_sibling->next_sibling=src; - else { - assert(b_parent!=0); - b_parent->first_child=src; - } - if(b_next_sibling!=0) b_next_sibling->prev_sibling=src; - else { - assert(b_parent!=0); - b_parent->last_child=src; - } - src->prev_sibling=b_prev_sibling; - src->next_sibling=b_next_sibling; - src->parent=b_parent; - return src; - } - - -template -tree tree::move_out(iterator source) - { - tree ret; - - // Move source node into the 'ret' tree. - ret.head->next_sibling = source.node; - ret.feet->prev_sibling = source.node; - - // Close the links in the current tree. - if(source.node->prev_sibling!=0) - source.node->prev_sibling->next_sibling = source.node->next_sibling; - - if(source.node->next_sibling!=0) - source.node->next_sibling->prev_sibling = source.node->prev_sibling; - - // If the moved-out node was a first or last child of - // the parent, adjust those links. - if(source.node->parent->first_child==source.node) { - if(source.node->next_sibling!=0) - source.node->parent->first_child=source.node->next_sibling; - else - source.node->parent->first_child=0; - } - if(source.node->parent->last_child==source.node) { - if(source.node->prev_sibling!=0) - source.node->parent->last_child=source.node->prev_sibling; - else - source.node->parent->last_child=0; - } - source.node->parent=0; - - // Fix source prev/next links. - source.node->prev_sibling = ret.head; - source.node->next_sibling = ret.feet; - - return ret; // A good compiler will move this, not copy. - } - -template -template iter tree::move_in(iter loc, tree& other) - { - if(other.head->next_sibling==other.feet) return loc; // other tree is empty - - tree_node *other_first_head = other.head->next_sibling; - tree_node *other_last_head = other.feet->prev_sibling; - - sibling_iterator prev(loc); - --prev; - - prev.node->next_sibling = other_first_head; - loc.node->prev_sibling = other_last_head; - other_first_head->prev_sibling = prev.node; - other_last_head->next_sibling = loc.node; - - // Adjust parent pointers. - tree_node *walk=other_first_head; - while(true) { - walk->parent=loc.node->parent; - if(walk==other_last_head) - break; - walk=walk->next_sibling; - } - - // Close other tree. - other.head->next_sibling=other.feet; - other.feet->prev_sibling=other.head; - - return other_first_head; - } - -template -template iter tree::move_in_below(iter loc, tree& other) - { - if(other.head->next_sibling==other.feet) return loc; // other tree is empty - - auto n = other.number_of_children(loc); - return move_in_as_nth_child(loc, n, other); - } - -template -template iter tree::move_in_as_nth_child(iter loc, size_t n, tree& other) - { - if(other.head->next_sibling==other.feet) return loc; // other tree is empty - - tree_node *other_first_head = other.head->next_sibling; - tree_node *other_last_head = other.feet->prev_sibling; - - if(n==0) { - if(loc.node->first_child==0) { - loc.node->first_child=other_first_head; - loc.node->last_child=other_last_head; - other_last_head->next_sibling=0; - other_first_head->prev_sibling=0; - } - else { - loc.node->first_child->prev_sibling=other_last_head; - other_last_head->next_sibling=loc.node->first_child; - loc.node->first_child=other_first_head; - other_first_head->prev_sibling=0; - } - } - else { - --n; - tree_node *walk = loc.node->first_child; - while(true) { - if(walk==0) - throw std::range_error("tree: move_in_as_nth_child position out of range"); - if(n==0) - break; - --n; - walk = walk->next_sibling; - } - if(walk->next_sibling==0) - loc.node->last_child=other_last_head; - else - walk->next_sibling->prev_sibling=other_last_head; - other_last_head->next_sibling=walk->next_sibling; - walk->next_sibling=other_first_head; - other_first_head->prev_sibling=walk; - } - - // Adjust parent pointers. - tree_node *walk=other_first_head; - while(true) { - walk->parent=loc.node; - if(walk==other_last_head) - break; - walk=walk->next_sibling; - } - - // Close other tree. - other.head->next_sibling=other.feet; - other.feet->prev_sibling=other.head; - - return other_first_head; - } - - -template -void tree::merge(sibling_iterator to1, sibling_iterator to2, - sibling_iterator from1, sibling_iterator from2, - bool duplicate_leaves) - { - sibling_iterator fnd; - while(from1!=from2) { - if((fnd=std::find(to1, to2, (*from1))) != to2) { // element found - if(from1.begin()==from1.end()) { // full depth reached - if(duplicate_leaves) - append_child(parent(to1), (*from1)); - } - else { // descend further - merge(fnd.begin(), fnd.end(), from1.begin(), from1.end(), duplicate_leaves); - } - } - else { // element missing - insert_subtree(to2, from1); - } - ++from1; - } - } - -template -void tree::merge(iterator to, iterator from, bool duplicate_leaves) - { - sibling_iterator to1(to); - sibling_iterator to2=to1; - ++to2; - sibling_iterator from1(from); - sibling_iterator from2=from1; - ++from2; - - merge(to1, to2, from1, from2, duplicate_leaves); - } - - -template -void tree::sort(sibling_iterator from, sibling_iterator to, bool deep) - { - std::less comp; - sort(from, to, comp, deep); - } +typename tree::sibling_iterator +tree::move_before(sibling_iterator target, + sibling_iterator source) { + tree_node *dst = target.node; + tree_node *src = source.node; + tree_node *dst_prev_sibling; + if (dst == 0) { // must then be an end iterator + dst_prev_sibling = target.parent_->last_child; + assert(dst_prev_sibling); + } else + dst_prev_sibling = dst->prev_sibling; + assert(src); + + if (dst == src) + return source; + if (dst_prev_sibling) + if (dst_prev_sibling == src) // already in the right spot + return source; + + // take src out of the tree + if (src->prev_sibling != 0) + src->prev_sibling->next_sibling = src->next_sibling; + else + src->parent->first_child = src->next_sibling; + if (src->next_sibling != 0) + src->next_sibling->prev_sibling = src->prev_sibling; + else + src->parent->last_child = src->prev_sibling; + + // connect it to the new point + if (dst_prev_sibling != 0) + dst_prev_sibling->next_sibling = src; + else + target.parent_->first_child = src; + src->prev_sibling = dst_prev_sibling; + if (dst) { + dst->prev_sibling = src; + src->parent = dst->parent; + } else { + src->parent = dst_prev_sibling->parent; + } + src->next_sibling = dst; + return src; +} + +template +template +iter tree::move_ontop(iter target, iter source) { + tree_node *dst = target.node; + tree_node *src = source.node; + assert(dst); + assert(src); + + if (dst == src) + return source; + + // if(dst==src->prev_sibling) { + // + // } + + // remember connection points + tree_node *b_prev_sibling = dst->prev_sibling; + tree_node *b_next_sibling = dst->next_sibling; + tree_node *b_parent = dst->parent; + + // remove target + erase(target); + + // take src out of the tree + if (src->prev_sibling != 0) + src->prev_sibling->next_sibling = src->next_sibling; + else { + assert(src->parent != 0); + src->parent->first_child = src->next_sibling; + } + if (src->next_sibling != 0) + src->next_sibling->prev_sibling = src->prev_sibling; + else { + assert(src->parent != 0); + src->parent->last_child = src->prev_sibling; + } + + // connect it to the new point + if (b_prev_sibling != 0) + b_prev_sibling->next_sibling = src; + else { + assert(b_parent != 0); + b_parent->first_child = src; + } + if (b_next_sibling != 0) + b_next_sibling->prev_sibling = src; + else { + assert(b_parent != 0); + b_parent->last_child = src; + } + src->prev_sibling = b_prev_sibling; + src->next_sibling = b_next_sibling; + src->parent = b_parent; + return src; +} + +template +tree +tree::move_out(iterator source) { + tree ret; + + // Move source node into the 'ret' tree. + ret.head->next_sibling = source.node; + ret.feet->prev_sibling = source.node; + + // Close the links in the current tree. + if (source.node->prev_sibling != 0) + source.node->prev_sibling->next_sibling = source.node->next_sibling; + + if (source.node->next_sibling != 0) + source.node->next_sibling->prev_sibling = source.node->prev_sibling; + + // If the moved-out node was a first or last child of + // the parent, adjust those links. + if (source.node->parent->first_child == source.node) { + if (source.node->next_sibling != 0) + source.node->parent->first_child = source.node->next_sibling; + else + source.node->parent->first_child = 0; + } + if (source.node->parent->last_child == source.node) { + if (source.node->prev_sibling != 0) + source.node->parent->last_child = source.node->prev_sibling; + else + source.node->parent->last_child = 0; + } + source.node->parent = 0; + + // Fix source prev/next links. + source.node->prev_sibling = ret.head; + source.node->next_sibling = ret.feet; + + return ret; // A good compiler will move this, not copy. +} + +template +template +iter tree::move_in(iter loc, tree &other) { + if (other.head->next_sibling == other.feet) + return loc; // other tree is empty + + tree_node *other_first_head = other.head->next_sibling; + tree_node *other_last_head = other.feet->prev_sibling; + + sibling_iterator prev(loc); + --prev; + + prev.node->next_sibling = other_first_head; + loc.node->prev_sibling = other_last_head; + other_first_head->prev_sibling = prev.node; + other_last_head->next_sibling = loc.node; + + // Adjust parent pointers. + tree_node *walk = other_first_head; + while (true) { + walk->parent = loc.node->parent; + if (walk == other_last_head) + break; + walk = walk->next_sibling; + } + + // Close other tree. + other.head->next_sibling = other.feet; + other.feet->prev_sibling = other.head; + + return other_first_head; +} + +template +template +iter tree::move_in_below(iter loc, tree &other) { + if (other.head->next_sibling == other.feet) + return loc; // other tree is empty + + auto n = other.number_of_children(loc); + return move_in_as_nth_child(loc, n, other); +} + +template +template +iter tree::move_in_as_nth_child(iter loc, size_t n, + tree &other) { + if (other.head->next_sibling == other.feet) + return loc; // other tree is empty + + tree_node *other_first_head = other.head->next_sibling; + tree_node *other_last_head = other.feet->prev_sibling; + + if (n == 0) { + if (loc.node->first_child == 0) { + loc.node->first_child = other_first_head; + loc.node->last_child = other_last_head; + other_last_head->next_sibling = 0; + other_first_head->prev_sibling = 0; + } else { + loc.node->first_child->prev_sibling = other_last_head; + other_last_head->next_sibling = loc.node->first_child; + loc.node->first_child = other_first_head; + other_first_head->prev_sibling = 0; + } + } else { + --n; + tree_node *walk = loc.node->first_child; + while (true) { + if (walk == 0) + throw std::range_error( + "tree: move_in_as_nth_child position out of range"); + if (n == 0) + break; + --n; + walk = walk->next_sibling; + } + if (walk->next_sibling == 0) + loc.node->last_child = other_last_head; + else + walk->next_sibling->prev_sibling = other_last_head; + other_last_head->next_sibling = walk->next_sibling; + walk->next_sibling = other_first_head; + other_first_head->prev_sibling = walk; + } + + // Adjust parent pointers. + tree_node *walk = other_first_head; + while (true) { + walk->parent = loc.node; + if (walk == other_last_head) + break; + walk = walk->next_sibling; + } + + // Close other tree. + other.head->next_sibling = other.feet; + other.feet->prev_sibling = other.head; + + return other_first_head; +} + +template +void tree::merge(sibling_iterator to1, + sibling_iterator to2, + sibling_iterator from1, + sibling_iterator from2, + bool duplicate_leaves) { + sibling_iterator fnd; + while (from1 != from2) { + if ((fnd = std::find(to1, to2, (*from1))) != to2) { // element found + if (from1.begin() == from1.end()) { // full depth reached + if (duplicate_leaves) + append_child(parent(to1), (*from1)); + } else { // descend further + merge(fnd.begin(), fnd.end(), from1.begin(), from1.end(), + duplicate_leaves); + } + } else { // element missing + insert_subtree(to2, from1); + } + ++from1; + } +} + +template +void tree::merge(iterator to, iterator from, + bool duplicate_leaves) { + sibling_iterator to1(to); + sibling_iterator to2 = to1; + ++to2; + sibling_iterator from1(from); + sibling_iterator from2 = from1; + ++from2; + + merge(to1, to2, from1, from2, duplicate_leaves); +} + +template +void tree::sort(sibling_iterator from, + sibling_iterator to, bool deep) { + std::less comp; + sort(from, to, comp, deep); +} template template -void tree::sort(sibling_iterator from, sibling_iterator to, - StrictWeakOrdering comp, bool deep) - { - if(from==to) return; - // make list of sorted nodes - // CHECK: if multiset stores equivalent nodes in the order in which they - // are inserted, then this routine should be called 'stable_sort'. - std::multiset > nodes(comp); - sibling_iterator it=from, it2=to; - while(it != to) { - nodes.insert(it.node); - ++it; - } - // reassemble - --it2; - - // prev and next are the nodes before and after the sorted range - tree_node *prev=from.node->prev_sibling; - tree_node *next=it2.node->next_sibling; - typename std::multiset >::iterator nit=nodes.begin(), eit=nodes.end(); - if(prev==0) { - if((*nit)->parent!=0) // to catch "sorting the head" situations, when there is no parent - (*nit)->parent->first_child=(*nit); - } - else prev->next_sibling=(*nit); - - --eit; - while(nit!=eit) { - (*nit)->prev_sibling=prev; - if(prev) - prev->next_sibling=(*nit); - prev=(*nit); - ++nit; - } - // prev now points to the last-but-one node in the sorted range - if(prev) - prev->next_sibling=(*eit); - - // eit points to the last node in the sorted range. - (*eit)->next_sibling=next; - (*eit)->prev_sibling=prev; // missed in the loop above - if(next==0) { - if((*eit)->parent!=0) // to catch "sorting the head" situations, when there is no parent - (*eit)->parent->last_child=(*eit); - } - else next->prev_sibling=(*eit); - - if(deep) { // sort the children of each node too - sibling_iterator bcs(*nodes.begin()); - sibling_iterator ecs(*eit); - ++ecs; - while(bcs!=ecs) { - sort(begin(bcs), end(bcs), comp, deep); - ++bcs; - } - } - } +void tree::sort(sibling_iterator from, + sibling_iterator to, + StrictWeakOrdering comp, bool deep) { + if (from == to) + return; + // make list of sorted nodes + // CHECK: if multiset stores equivalent nodes in the order in which they + // are inserted, then this routine should be called 'stable_sort'. + std::multiset> nodes(comp); + sibling_iterator it = from, it2 = to; + while (it != to) { + nodes.insert(it.node); + ++it; + } + // reassemble + --it2; + + // prev and next are the nodes before and after the sorted range + tree_node *prev = from.node->prev_sibling; + tree_node *next = it2.node->next_sibling; + typename std::multiset>::iterator + nit = nodes.begin(), + eit = nodes.end(); + if (prev == 0) { + if ((*nit)->parent != + 0) // to catch "sorting the head" situations, when there is no parent + (*nit)->parent->first_child = (*nit); + } else + prev->next_sibling = (*nit); + + --eit; + while (nit != eit) { + (*nit)->prev_sibling = prev; + if (prev) + prev->next_sibling = (*nit); + prev = (*nit); + ++nit; + } + // prev now points to the last-but-one node in the sorted range + if (prev) + prev->next_sibling = (*eit); + + // eit points to the last node in the sorted range. + (*eit)->next_sibling = next; + (*eit)->prev_sibling = prev; // missed in the loop above + if (next == 0) { + if ((*eit)->parent != + 0) // to catch "sorting the head" situations, when there is no parent + (*eit)->parent->last_child = (*eit); + } else + next->prev_sibling = (*eit); + + if (deep) { // sort the children of each node too + sibling_iterator bcs(*nodes.begin()); + sibling_iterator ecs(*eit); + ++ecs; + while (bcs != ecs) { + sort(begin(bcs), end(bcs), comp, deep); + ++bcs; + } + } +} template template -bool tree::equal(const iter& one_, const iter& two, const iter& three_) const - { - std::equal_to comp; - return equal(one_, two, three_, comp); - } +bool tree::equal(const iter &one_, const iter &two, + const iter &three_) const { + std::equal_to comp; + return equal(one_, two, three_, comp); +} template template -bool tree::equal_subtree(const iter& one_, const iter& two_) const - { - std::equal_to comp; - return equal_subtree(one_, two_, comp); - } +bool tree::equal_subtree(const iter &one_, + const iter &two_) const { + std::equal_to comp; + return equal_subtree(one_, two_, comp); +} template template -bool tree::equal(const iter& one_, const iter& two, const iter& three_, BinaryPredicate fun) const - { - pre_order_iterator one(one_), three(three_); - -// if(one==two && is_valid(three) && three.number_of_children()!=0) -// return false; - while(one!=two && is_valid(three)) { - if(!fun(*one,*three)) - return false; - if(one.number_of_children()!=three.number_of_children()) - return false; - ++one; - ++three; - } - return true; - } +bool tree::equal(const iter &one_, const iter &two, + const iter &three_, + BinaryPredicate fun) const { + pre_order_iterator one(one_), three(three_); + + // if(one==two && is_valid(three) && three.number_of_children()!=0) + // return false; + while (one != two && is_valid(three)) { + if (!fun(*one, *three)) + return false; + if (one.number_of_children() != three.number_of_children()) + return false; + ++one; + ++three; + } + return true; +} template template -bool tree::equal_subtree(const iter& one_, const iter& two_, BinaryPredicate fun) const - { - pre_order_iterator one(one_), two(two_); - - if(!fun(*one,*two)) return false; - if(number_of_children(one)!=number_of_children(two)) return false; - return equal(begin(one),end(one),begin(two),fun); - } - -template -tree tree::subtree(sibling_iterator from, sibling_iterator to) const - { - assert(from!=to); // if from==to, the range is empty, hence no tree to return. - - tree tmp; - tmp.set_head(value_type()); - tmp.replace(tmp.begin(), tmp.end(), from, to); - return tmp; - } - -template -void tree::subtree(tree& tmp, sibling_iterator from, sibling_iterator to) const - { - assert(from!=to); // if from==to, the range is empty, hence no tree to return. - - tmp.set_head(value_type()); - tmp.replace(tmp.begin(), tmp.end(), from, to); - } - -template -size_t tree::size() const - { - size_t i=0; - pre_order_iterator it=begin(), eit=end(); - while(it!=eit) { - ++i; - ++it; - } - return i; - } - -template -size_t tree::size(const iterator_base& top) const - { - size_t i=0; - pre_order_iterator it=top, eit=top; - eit.skip_children(); - ++eit; - while(it!=eit) { - ++i; - ++it; - } - return i; - } - -template -bool tree::empty() const - { - pre_order_iterator it=begin(), eit=end(); - return (it==eit); - } - -template -int tree::depth(const iterator_base& it) - { - tree_node* pos=it.node; - assert(pos!=0); - int ret=0; - while(pos->parent!=0) { - pos=pos->parent; - ++ret; - } - return ret; - } - -template -int tree::depth(const iterator_base& it, const iterator_base& root) - { - tree_node* pos=it.node; - assert(pos!=0); - int ret=0; - while(pos->parent!=0 && pos!=root.node) { - pos=pos->parent; - ++ret; - } - return ret; - } +bool tree::equal_subtree(const iter &one_, + const iter &two_, + BinaryPredicate fun) const { + pre_order_iterator one(one_), two(two_); + + if (!fun(*one, *two)) + return false; + if (number_of_children(one) != number_of_children(two)) + return false; + return equal(begin(one), end(one), begin(two), fun); +} + +template +tree +tree::subtree(sibling_iterator from, + sibling_iterator to) const { + assert(from != + to); // if from==to, the range is empty, hence no tree to return. + + tree tmp; + tmp.set_head(value_type()); + tmp.replace(tmp.begin(), tmp.end(), from, to); + return tmp; +} + +template +void tree::subtree(tree &tmp, sibling_iterator from, + sibling_iterator to) const { + assert(from != + to); // if from==to, the range is empty, hence no tree to return. + + tmp.set_head(value_type()); + tmp.replace(tmp.begin(), tmp.end(), from, to); +} + +template +size_t tree::size() const { + size_t i = 0; + pre_order_iterator it = begin(), eit = end(); + while (it != eit) { + ++i; + ++it; + } + return i; +} + +template +size_t tree::size(const iterator_base &top) const { + size_t i = 0; + pre_order_iterator it = top, eit = top; + eit.skip_children(); + ++eit; + while (it != eit) { + ++i; + ++it; + } + return i; +} + +template +bool tree::empty() const { + pre_order_iterator it = begin(), eit = end(); + return (it == eit); +} + +template +int tree::depth(const iterator_base &it) { + tree_node *pos = it.node; + assert(pos != 0); + int ret = 0; + while (pos->parent != 0) { + pos = pos->parent; + ++ret; + } + return ret; +} + +template +int tree::depth(const iterator_base &it, + const iterator_base &root) { + tree_node *pos = it.node; + assert(pos != 0); + int ret = 0; + while (pos->parent != 0 && pos != root.node) { + pos = pos->parent; + ++ret; + } + return ret; +} template template -int tree::depth(const iterator_base& it, Predicate p) - { - tree_node* pos=it.node; - assert(pos!=0); - int ret=0; - while(pos->parent!=0) { - pos=pos->parent; - if(p(pos)) - ++ret; - } - return ret; - } +int tree::depth(const iterator_base &it, Predicate p) { + tree_node *pos = it.node; + assert(pos != 0); + int ret = 0; + while (pos->parent != 0) { + pos = pos->parent; + if (p(pos)) + ++ret; + } + return ret; +} template template -int tree::distance(const iterator_base& top, const iterator_base& bottom, Predicate p) - { - tree_node* pos=bottom.node; - assert(pos!=0); - int ret=0; - while(pos->parent!=0 && pos!=top.node) { - pos=pos->parent; - if(p(pos)) - ++ret; - } - return ret; - } - -template -int tree::max_depth() const - { - int maxd=-1; - for(tree_node *it = head->next_sibling; it!=feet; it=it->next_sibling) - maxd=std::max(maxd, max_depth(it)); - - return maxd; - } - - -template -int tree::max_depth(const iterator_base& pos) const - { - tree_node *tmp=pos.node; - - if(tmp==0 || tmp==head || tmp==feet) return -1; - - int curdepth=0, maxdepth=0; - while(true) { // try to walk the bottom of the tree - while(tmp->first_child==0) { - if(tmp==pos.node) return maxdepth; - if(tmp->next_sibling==0) { - // try to walk up and then right again - do { - tmp=tmp->parent; - if(tmp==0) return maxdepth; - --curdepth; - } - while(tmp->next_sibling==0); - } - if(tmp==pos.node) return maxdepth; - tmp=tmp->next_sibling; - } - tmp=tmp->first_child; - ++curdepth; - maxdepth=std::max(curdepth, maxdepth); - } - } - -template -unsigned int tree::number_of_children(const iterator_base& it) - { - tree_node *pos=it.node->first_child; - if(pos==0) return 0; - - unsigned int ret=1; -// while(pos!=it.node->last_child) { -// ++ret; -// pos=pos->next_sibling; -// } - while((pos=pos->next_sibling)) - ++ret; - return ret; - } - -template -unsigned int tree::number_of_siblings(const iterator_base& it) const - { - tree_node *pos=it.node; - unsigned int ret=0; - // count forward - while(pos->next_sibling && - pos->next_sibling!=head && - pos->next_sibling!=feet) { - ++ret; - pos=pos->next_sibling; - } - // count backward - pos=it.node; - while(pos->prev_sibling && - pos->prev_sibling!=head && - pos->prev_sibling!=feet) { - ++ret; - pos=pos->prev_sibling; - } - - return ret; - } - -template -void tree::swap(sibling_iterator it) - { - tree_node *nxt=it.node->next_sibling; - if(nxt) { - if(it.node->prev_sibling) - it.node->prev_sibling->next_sibling=nxt; - else - it.node->parent->first_child=nxt; - nxt->prev_sibling=it.node->prev_sibling; - tree_node *nxtnxt=nxt->next_sibling; - if(nxtnxt) - nxtnxt->prev_sibling=it.node; - else - it.node->parent->last_child=it.node; - nxt->next_sibling=it.node; - it.node->prev_sibling=nxt; - it.node->next_sibling=nxtnxt; - } - } - -template -void tree::swap(iterator one, iterator two) - { - // if one and two are adjacent siblings, use the sibling swap - if(one.node->next_sibling==two.node) swap(one); - else if(two.node->next_sibling==one.node) swap(two); - else { - tree_node *nxt1=one.node->next_sibling; - tree_node *nxt2=two.node->next_sibling; - tree_node *pre1=one.node->prev_sibling; - tree_node *pre2=two.node->prev_sibling; - tree_node *par1=one.node->parent; - tree_node *par2=two.node->parent; - - // reconnect - one.node->parent=par2; - one.node->next_sibling=nxt2; - if(nxt2) nxt2->prev_sibling=one.node; - else par2->last_child=one.node; - one.node->prev_sibling=pre2; - if(pre2) pre2->next_sibling=one.node; - else par2->first_child=one.node; - - two.node->parent=par1; - two.node->next_sibling=nxt1; - if(nxt1) nxt1->prev_sibling=two.node; - else par1->last_child=two.node; - two.node->prev_sibling=pre1; - if(pre1) pre1->next_sibling=two.node; - else par1->first_child=two.node; - } - } +int tree::distance(const iterator_base &top, + const iterator_base &bottom, + Predicate p) { + tree_node *pos = bottom.node; + assert(pos != 0); + int ret = 0; + while (pos->parent != 0 && pos != top.node) { + pos = pos->parent; + if (p(pos)) + ++ret; + } + return ret; +} + +template +int tree::max_depth() const { + int maxd = -1; + for (tree_node *it = head->next_sibling; it != feet; it = it->next_sibling) + maxd = std::max(maxd, max_depth(it)); + + return maxd; +} + +template +int tree::max_depth(const iterator_base &pos) const { + tree_node *tmp = pos.node; + + if (tmp == 0 || tmp == head || tmp == feet) + return -1; + + int curdepth = 0, maxdepth = 0; + while (true) { // try to walk the bottom of the tree + while (tmp->first_child == 0) { + if (tmp == pos.node) + return maxdepth; + if (tmp->next_sibling == 0) { + // try to walk up and then right again + do { + tmp = tmp->parent; + if (tmp == 0) + return maxdepth; + --curdepth; + } while (tmp->next_sibling == 0); + } + if (tmp == pos.node) + return maxdepth; + tmp = tmp->next_sibling; + } + tmp = tmp->first_child; + ++curdepth; + maxdepth = std::max(curdepth, maxdepth); + } +} + +template +unsigned int +tree::number_of_children(const iterator_base &it) { + tree_node *pos = it.node->first_child; + if (pos == 0) + return 0; + + unsigned int ret = 1; + // while(pos!=it.node->last_child) { + // ++ret; + // pos=pos->next_sibling; + // } + while ((pos = pos->next_sibling)) + ++ret; + return ret; +} + +template +unsigned int tree::number_of_siblings( + const iterator_base &it) const { + tree_node *pos = it.node; + unsigned int ret = 0; + // count forward + while (pos->next_sibling && pos->next_sibling != head && + pos->next_sibling != feet) { + ++ret; + pos = pos->next_sibling; + } + // count backward + pos = it.node; + while (pos->prev_sibling && pos->prev_sibling != head && + pos->prev_sibling != feet) { + ++ret; + pos = pos->prev_sibling; + } + + return ret; +} + +template +void tree::swap(sibling_iterator it) { + tree_node *nxt = it.node->next_sibling; + if (nxt) { + if (it.node->prev_sibling) + it.node->prev_sibling->next_sibling = nxt; + else + it.node->parent->first_child = nxt; + nxt->prev_sibling = it.node->prev_sibling; + tree_node *nxtnxt = nxt->next_sibling; + if (nxtnxt) + nxtnxt->prev_sibling = it.node; + else + it.node->parent->last_child = it.node; + nxt->next_sibling = it.node; + it.node->prev_sibling = nxt; + it.node->next_sibling = nxtnxt; + } +} + +template +void tree::swap(iterator one, iterator two) { + // if one and two are adjacent siblings, use the sibling swap + if (one.node->next_sibling == two.node) + swap(one); + else if (two.node->next_sibling == one.node) + swap(two); + else { + tree_node *nxt1 = one.node->next_sibling; + tree_node *nxt2 = two.node->next_sibling; + tree_node *pre1 = one.node->prev_sibling; + tree_node *pre2 = two.node->prev_sibling; + tree_node *par1 = one.node->parent; + tree_node *par2 = two.node->parent; + + // reconnect + one.node->parent = par2; + one.node->next_sibling = nxt2; + if (nxt2) + nxt2->prev_sibling = one.node; + else + par2->last_child = one.node; + one.node->prev_sibling = pre2; + if (pre2) + pre2->next_sibling = one.node; + else + par2->first_child = one.node; + + two.node->parent = par1; + two.node->next_sibling = nxt1; + if (nxt1) + nxt1->prev_sibling = two.node; + else + par1->last_child = two.node; + two.node->prev_sibling = pre1; + if (pre1) + pre1->next_sibling = two.node; + else + par1->first_child = two.node; + } +} // template -// tree::iterator tree::find_subtree( -// sibling_iterator subfrom, sibling_iterator subto, iterator from, iterator to, -// BinaryPredicate fun) const +// tree::iterator tree::find_subtree( sibling_iterator subfrom, +// sibling_iterator subto, iterator from, iterator to, BinaryPredicate fun) +// const // { // assert(1==0); // this routine is not finished yet. // while(from!=to) { // if(fun(*subfrom, *from)) { -// +// // } // } // return to; // } template -bool tree::is_in_subtree(const iterator_base& it, const iterator_base& top) const - { - sibling_iterator first=top; - sibling_iterator last=first; - ++last; - return is_in_subtree(it, first, last); - } - -template -bool tree::is_in_subtree(const iterator_base& it, const iterator_base& begin, - const iterator_base& end) const - { - // FIXME: this should be optimised. - pre_order_iterator tmp=begin; - while(tmp!=end) { - if(tmp==it) return true; - ++tmp; - } - return false; - } - -template -bool tree::is_valid(const iterator_base& it) const - { - if(it.node==0 || it.node==feet || it.node==head) return false; - else return true; - } - -template -bool tree::is_head(const iterator_base& it) - { - if(it.node->parent==0) return true; - return false; - } - -template -typename tree::iterator tree::lowest_common_ancestor( - const iterator_base& one, const iterator_base& two) const - { - std::set parents; - - // Walk up from 'one' storing all parents. - iterator walk=one; - do { - walk=parent(walk); - parents.insert(walk); - } - while( walk.node->parent ); - - // Walk up from 'two' until we encounter a node in parents. - walk=two; - do { - walk=parent(walk); - if(parents.find(walk) != parents.end()) break; - } - while( walk.node->parent ); - - return walk; - } - -template -unsigned int tree::index(sibling_iterator it) const - { - unsigned int ind=0; - if(it.node->parent==0) { - while(it.node->prev_sibling!=head) { - it.node=it.node->prev_sibling; - ++ind; - } - } - else { - while(it.node->prev_sibling!=0) { - it.node=it.node->prev_sibling; - ++ind; - } - } - return ind; - } - -template -typename tree::sibling_iterator tree::sibling(const iterator_base& it, unsigned int num) const - { - tree_node *tmp; - if(it.node->parent==0) { - tmp=head->next_sibling; - while(num) { - tmp = tmp->next_sibling; - --num; - } - } - else { - tmp=it.node->parent->first_child; - while(num) { - assert(tmp!=0); - tmp = tmp->next_sibling; - --num; - } - } - return tmp; - } - -template -void tree::debug_verify_consistency() const - { - iterator it=begin(); - while(it!=end()) { - // std::cerr << *it << " (" << it.node << ")" << std::endl; - if(it.node->parent!=0) { - if(it.node->prev_sibling==0) - assert(it.node->parent->first_child==it.node); - else - assert(it.node->prev_sibling->next_sibling==it.node); - if(it.node->next_sibling==0) - assert(it.node->parent->last_child==it.node); - else - assert(it.node->next_sibling->prev_sibling==it.node); - } - ++it; - } - } - -template -typename tree::sibling_iterator tree::child(const iterator_base& it, unsigned int num) - { - tree_node *tmp=it.node->first_child; - while(num--) { - assert(tmp!=0); - tmp=tmp->next_sibling; - } - return tmp; - } +bool tree::is_in_subtree( + const iterator_base &it, const iterator_base &top) const { + sibling_iterator first = top; + sibling_iterator last = first; + ++last; + return is_in_subtree(it, first, last); +} +template +bool tree::is_in_subtree( + const iterator_base &it, const iterator_base &begin, + const iterator_base &end) const { + // FIXME: this should be optimised. + pre_order_iterator tmp = begin; + while (tmp != end) { + if (tmp == it) + return true; + ++tmp; + } + return false; +} +template +bool tree::is_valid(const iterator_base &it) const { + if (it.node == 0 || it.node == feet || it.node == head) + return false; + else + return true; +} +template +bool tree::is_head(const iterator_base &it) { + if (it.node->parent == 0) + return true; + return false; +} + +template +typename tree::iterator +tree::lowest_common_ancestor( + const iterator_base &one, const iterator_base &two) const { + std::set parents; + + // Walk up from 'one' storing all parents. + iterator walk = one; + do { + walk = parent(walk); + parents.insert(walk); + } while (walk.node->parent); + + // Walk up from 'two' until we encounter a node in parents. + walk = two; + do { + walk = parent(walk); + if (parents.find(walk) != parents.end()) + break; + } while (walk.node->parent); + + return walk; +} + +template +unsigned int tree::index(sibling_iterator it) const { + unsigned int ind = 0; + if (it.node->parent == 0) { + while (it.node->prev_sibling != head) { + it.node = it.node->prev_sibling; + ++ind; + } + } else { + while (it.node->prev_sibling != 0) { + it.node = it.node->prev_sibling; + ++ind; + } + } + return ind; +} + +template +typename tree::sibling_iterator +tree::sibling(const iterator_base &it, + unsigned int num) const { + tree_node *tmp; + if (it.node->parent == 0) { + tmp = head->next_sibling; + while (num) { + tmp = tmp->next_sibling; + --num; + } + } else { + tmp = it.node->parent->first_child; + while (num) { + assert(tmp != 0); + tmp = tmp->next_sibling; + --num; + } + } + return tmp; +} + +template +void tree::debug_verify_consistency() const { + iterator it = begin(); + while (it != end()) { + // std::cerr << *it << " (" << it.node << ")" << std::endl; + if (it.node->parent != 0) { + if (it.node->prev_sibling == 0) + assert(it.node->parent->first_child == it.node); + else + assert(it.node->prev_sibling->next_sibling == it.node); + if (it.node->next_sibling == 0) + assert(it.node->parent->last_child == it.node); + else + assert(it.node->next_sibling->prev_sibling == it.node); + } + ++it; + } +} + +template +typename tree::sibling_iterator +tree::child(const iterator_base &it, unsigned int num) { + tree_node *tmp = it.node->first_child; + while (num--) { + assert(tmp != 0); + tmp = tmp->next_sibling; + } + return tmp; +} // Iterator base template tree::iterator_base::iterator_base() - : node(0), skip_current_children_(false) - { - } + : node(0), skip_current_children_(false) {} template tree::iterator_base::iterator_base(tree_node *tn) - : node(tn), skip_current_children_(false) - { - } + : node(tn), skip_current_children_(false) {} template -T& tree::iterator_base::operator*() const - { - return node->data; - } +T &tree::iterator_base::operator*() const { + return node->data; +} template -T* tree::iterator_base::operator->() const - { - return &(node->data); - } +T *tree::iterator_base::operator->() const { + return &(node->data); +} template -bool tree::post_order_iterator::operator!=(const post_order_iterator& other) const - { - if(other.node!=this->node) return true; - else return false; - } +bool tree::post_order_iterator::operator!=( + const post_order_iterator &other) const { + if (other.node != this->node) + return true; + else + return false; +} template -bool tree::post_order_iterator::operator==(const post_order_iterator& other) const - { - if(other.node==this->node) return true; - else return false; - } +bool tree::post_order_iterator::operator==( + const post_order_iterator &other) const { + if (other.node == this->node) + return true; + else + return false; +} template -bool tree::pre_order_iterator::operator!=(const pre_order_iterator& other) const - { - if(other.node!=this->node) return true; - else return false; - } +bool tree::pre_order_iterator::operator!=( + const pre_order_iterator &other) const { + if (other.node != this->node) + return true; + else + return false; +} template -bool tree::pre_order_iterator::operator==(const pre_order_iterator& other) const - { - if(other.node==this->node) return true; - else return false; - } +bool tree::pre_order_iterator::operator==( + const pre_order_iterator &other) const { + if (other.node == this->node) + return true; + else + return false; +} template -bool tree::sibling_iterator::operator!=(const sibling_iterator& other) const - { - if(other.node!=this->node) return true; - else return false; - } +bool tree::sibling_iterator::operator!=( + const sibling_iterator &other) const { + if (other.node != this->node) + return true; + else + return false; +} template -bool tree::sibling_iterator::operator==(const sibling_iterator& other) const - { - if(other.node==this->node) return true; - else return false; - } +bool tree::sibling_iterator::operator==( + const sibling_iterator &other) const { + if (other.node == this->node) + return true; + else + return false; +} template -bool tree::leaf_iterator::operator!=(const leaf_iterator& other) const - { - if(other.node!=this->node) return true; - else return false; - } +bool tree::leaf_iterator::operator!=( + const leaf_iterator &other) const { + if (other.node != this->node) + return true; + else + return false; +} template -bool tree::leaf_iterator::operator==(const leaf_iterator& other) const - { - if(other.node==this->node && other.top_node==this->top_node) return true; - else return false; - } +bool tree::leaf_iterator::operator==( + const leaf_iterator &other) const { + if (other.node == this->node && other.top_node == this->top_node) + return true; + else + return false; +} template -typename tree::sibling_iterator tree::iterator_base::begin() const - { - if(node->first_child==0) - return end(); +typename tree::sibling_iterator +tree::iterator_base::begin() const { + if (node->first_child == 0) + return end(); - sibling_iterator ret(node->first_child); - ret.parent_=this->node; - return ret; - } + sibling_iterator ret(node->first_child); + ret.parent_ = this->node; + return ret; +} template -typename tree::sibling_iterator tree::iterator_base::end() const - { - sibling_iterator ret(0); - ret.parent_=node; - return ret; - } +typename tree::sibling_iterator +tree::iterator_base::end() const { + sibling_iterator ret(0); + ret.parent_ = node; + return ret; +} template -void tree::iterator_base::skip_children() - { - skip_current_children_=true; - } +void tree::iterator_base::skip_children() { + skip_current_children_ = true; +} template -void tree::iterator_base::skip_children(bool skip) - { - skip_current_children_=skip; - } +void tree::iterator_base::skip_children(bool skip) { + skip_current_children_ = skip; +} template -unsigned int tree::iterator_base::number_of_children() const - { - tree_node *pos=node->first_child; - if(pos==0) return 0; - - unsigned int ret=1; - while(pos!=node->last_child) { - ++ret; - pos=pos->next_sibling; - } - return ret; - } - +unsigned int +tree::iterator_base::number_of_children() const { + tree_node *pos = node->first_child; + if (pos == 0) + return 0; + unsigned int ret = 1; + while (pos != node->last_child) { + ++ret; + pos = pos->next_sibling; + } + return ret; +} // Pre-order iterator template -tree::pre_order_iterator::pre_order_iterator() - : iterator_base(0) - { - } - -template -tree::pre_order_iterator::pre_order_iterator(tree_node *tn) - : iterator_base(tn) - { - } - -template -tree::pre_order_iterator::pre_order_iterator(const iterator_base &other) - : iterator_base(other.node) - { - } - -template -tree::pre_order_iterator::pre_order_iterator(const sibling_iterator& other) - : iterator_base(other.node) - { - if(this->node==0) { - if(other.range_last()!=0) - this->node=other.range_last(); - else - this->node=other.parent_; - this->skip_children(); - ++(*this); - } - } - -template -typename tree::pre_order_iterator& tree::pre_order_iterator::operator++() - { - assert(this->node!=0); - if(!this->skip_current_children_ && this->node->first_child != 0) { - this->node=this->node->first_child; - } - else { - this->skip_current_children_=false; - while(this->node->next_sibling==0) { - this->node=this->node->parent; - if(this->node==0) - return *this; - } - this->node=this->node->next_sibling; - } - return *this; - } - -template -typename tree::pre_order_iterator& tree::pre_order_iterator::operator--() - { - assert(this->node!=0); - if(this->node->prev_sibling) { - this->node=this->node->prev_sibling; - while(this->node->last_child) - this->node=this->node->last_child; - } - else { - this->node=this->node->parent; - if(this->node==0) - return *this; - } - return *this; -} - -template -typename tree::pre_order_iterator tree::pre_order_iterator::operator++(int) - { - pre_order_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename tree::pre_order_iterator& tree::pre_order_iterator::next_skip_children() - { - (*this).skip_children(); - (*this)++; - return *this; - } - -template -typename tree::pre_order_iterator tree::pre_order_iterator::operator--(int) -{ +tree::pre_order_iterator::pre_order_iterator() + : iterator_base(0) {} + +template +tree::pre_order_iterator::pre_order_iterator( + tree_node *tn) + : iterator_base(tn) {} + +template +tree::pre_order_iterator::pre_order_iterator( + const iterator_base &other) + : iterator_base(other.node) {} + +template +tree::pre_order_iterator::pre_order_iterator( + const sibling_iterator &other) + : iterator_base(other.node) { + if (this->node == 0) { + if (other.range_last() != 0) + this->node = other.range_last(); + else + this->node = other.parent_; + this->skip_children(); + ++(*this); + } +} + +template +typename tree::pre_order_iterator & +tree::pre_order_iterator::operator++() { + assert(this->node != 0); + if (!this->skip_current_children_ && this->node->first_child != 0) { + this->node = this->node->first_child; + } else { + this->skip_current_children_ = false; + while (this->node->next_sibling == 0) { + this->node = this->node->parent; + if (this->node == 0) + return *this; + } + this->node = this->node->next_sibling; + } + return *this; +} + +template +typename tree::pre_order_iterator & +tree::pre_order_iterator::operator--() { + assert(this->node != 0); + if (this->node->prev_sibling) { + this->node = this->node->prev_sibling; + while (this->node->last_child) + this->node = this->node->last_child; + } else { + this->node = this->node->parent; + if (this->node == 0) + return *this; + } + return *this; +} + +template +typename tree::pre_order_iterator +tree::pre_order_iterator::operator++(int) { pre_order_iterator copy = *this; - --(*this); + ++(*this); return copy; } template -typename tree::pre_order_iterator& tree::pre_order_iterator::operator+=(unsigned int num) - { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } +typename tree::pre_order_iterator & +tree::pre_order_iterator::next_skip_children() { + (*this).skip_children(); + (*this)++; + return *this; +} template -typename tree::pre_order_iterator& tree::pre_order_iterator::operator-=(unsigned int num) - { - while(num>0) { - --(*this); - --num; - } - return (*this); - } +typename tree::pre_order_iterator +tree::pre_order_iterator::operator--(int) { + pre_order_iterator copy = *this; + --(*this); + return copy; +} +template +typename tree::pre_order_iterator & +tree::pre_order_iterator::operator+=(unsigned int num) { + while (num > 0) { + ++(*this); + --num; + } + return (*this); +} +template +typename tree::pre_order_iterator & +tree::pre_order_iterator::operator-=(unsigned int num) { + while (num > 0) { + --(*this); + --num; + } + return (*this); +} // Post-order iterator template -tree::post_order_iterator::post_order_iterator() - : iterator_base(0) - { - } - -template -tree::post_order_iterator::post_order_iterator(tree_node *tn) - : iterator_base(tn) - { - } - -template -tree::post_order_iterator::post_order_iterator(const iterator_base &other) - : iterator_base(other.node) - { - } - -template -tree::post_order_iterator::post_order_iterator(const sibling_iterator& other) - : iterator_base(other.node) - { - if(this->node==0) { - if(other.range_last()!=0) - this->node=other.range_last(); - else - this->node=other.parent_; - this->skip_children(); - ++(*this); - } - } - -template -typename tree::post_order_iterator& tree::post_order_iterator::operator++() - { - assert(this->node!=0); - if(this->node->next_sibling==0) { - this->node=this->node->parent; - this->skip_current_children_=false; - } - else { - this->node=this->node->next_sibling; - if(this->skip_current_children_) { - this->skip_current_children_=false; - } - else { - while(this->node->first_child) - this->node=this->node->first_child; - } - } - return *this; - } - -template -typename tree::post_order_iterator& tree::post_order_iterator::operator--() - { - assert(this->node!=0); - if(this->skip_current_children_ || this->node->last_child==0) { - this->skip_current_children_=false; - while(this->node->prev_sibling==0) - this->node=this->node->parent; - this->node=this->node->prev_sibling; - } - else { - this->node=this->node->last_child; - } - return *this; - } - -template -typename tree::post_order_iterator tree::post_order_iterator::operator++(int) - { - post_order_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename tree::post_order_iterator tree::post_order_iterator::operator--(int) - { - post_order_iterator copy = *this; - --(*this); - return copy; - } - - -template -typename tree::post_order_iterator& tree::post_order_iterator::operator+=(unsigned int num) - { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } - -template -typename tree::post_order_iterator& tree::post_order_iterator::operator-=(unsigned int num) - { - while(num>0) { - --(*this); - --num; - } - return (*this); - } - -template -void tree::post_order_iterator::descend_all() - { - assert(this->node!=0); - while(this->node->first_child) - this->node=this->node->first_child; - } +tree::post_order_iterator::post_order_iterator() + : iterator_base(0) {} +template +tree::post_order_iterator::post_order_iterator( + tree_node *tn) + : iterator_base(tn) {} -// Breadth-first iterator +template +tree::post_order_iterator::post_order_iterator( + const iterator_base &other) + : iterator_base(other.node) {} template -tree::breadth_first_queued_iterator::breadth_first_queued_iterator() - : iterator_base() - { - } +tree::post_order_iterator::post_order_iterator( + const sibling_iterator &other) + : iterator_base(other.node) { + if (this->node == 0) { + if (other.range_last() != 0) + this->node = other.range_last(); + else + this->node = other.parent_; + this->skip_children(); + ++(*this); + } +} template -tree::breadth_first_queued_iterator::breadth_first_queued_iterator(tree_node *tn) - : iterator_base(tn) - { - traversal_queue.push(tn); - } +typename tree::post_order_iterator & +tree::post_order_iterator::operator++() { + assert(this->node != 0); + if (this->node->next_sibling == 0) { + this->node = this->node->parent; + this->skip_current_children_ = false; + } else { + this->node = this->node->next_sibling; + if (this->skip_current_children_) { + this->skip_current_children_ = false; + } else { + while (this->node->first_child) + this->node = this->node->first_child; + } + } + return *this; +} template -tree::breadth_first_queued_iterator::breadth_first_queued_iterator(const iterator_base& other) - : iterator_base(other.node) - { - traversal_queue.push(other.node); - } +typename tree::post_order_iterator & +tree::post_order_iterator::operator--() { + assert(this->node != 0); + if (this->skip_current_children_ || this->node->last_child == 0) { + this->skip_current_children_ = false; + while (this->node->prev_sibling == 0) + this->node = this->node->parent; + this->node = this->node->prev_sibling; + } else { + this->node = this->node->last_child; + } + return *this; +} template -bool tree::breadth_first_queued_iterator::operator!=(const breadth_first_queued_iterator& other) const - { - if(other.node!=this->node) return true; - else return false; - } +typename tree::post_order_iterator +tree::post_order_iterator::operator++(int) { + post_order_iterator copy = *this; + ++(*this); + return copy; +} template -bool tree::breadth_first_queued_iterator::operator==(const breadth_first_queued_iterator& other) const - { - if(other.node==this->node) return true; - else return false; - } +typename tree::post_order_iterator +tree::post_order_iterator::operator--(int) { + post_order_iterator copy = *this; + --(*this); + return copy; +} template -typename tree::breadth_first_queued_iterator& tree::breadth_first_queued_iterator::operator++() - { - assert(this->node!=0); +typename tree::post_order_iterator & +tree::post_order_iterator::operator+=( + unsigned int num) { + while (num > 0) { + ++(*this); + --num; + } + return (*this); +} - // Add child nodes and pop current node - sibling_iterator sib=this->begin(); - while(sib!=this->end()) { - traversal_queue.push(sib.node); - ++sib; - } - traversal_queue.pop(); - if(traversal_queue.size()>0) - this->node=traversal_queue.front(); - else - this->node=0; - return (*this); - } +template +typename tree::post_order_iterator & +tree::post_order_iterator::operator-=( + unsigned int num) { + while (num > 0) { + --(*this); + --num; + } + return (*this); +} template -typename tree::breadth_first_queued_iterator tree::breadth_first_queued_iterator::operator++(int) - { - breadth_first_queued_iterator copy = *this; - ++(*this); - return copy; - } +void tree::post_order_iterator::descend_all() { + assert(this->node != 0); + while (this->node->first_child) + this->node = this->node->first_child; +} + +// Breadth-first iterator template -typename tree::breadth_first_queued_iterator& tree::breadth_first_queued_iterator::operator+=(unsigned int num) - { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } +tree::breadth_first_queued_iterator:: + breadth_first_queued_iterator() + : iterator_base() {} +template +tree::breadth_first_queued_iterator:: + breadth_first_queued_iterator(tree_node *tn) + : iterator_base(tn) { + traversal_queue.push(tn); +} +template +tree::breadth_first_queued_iterator:: + breadth_first_queued_iterator(const iterator_base &other) + : iterator_base(other.node) { + traversal_queue.push(other.node); +} + +template +bool tree::breadth_first_queued_iterator::operator!=( + const breadth_first_queued_iterator &other) const { + if (other.node != this->node) + return true; + else + return false; +} + +template +bool tree::breadth_first_queued_iterator::operator==( + const breadth_first_queued_iterator &other) const { + if (other.node == this->node) + return true; + else + return false; +} + +template +typename tree::breadth_first_queued_iterator & +tree::breadth_first_queued_iterator::operator++() { + assert(this->node != 0); + + // Add child nodes and pop current node + sibling_iterator sib = this->begin(); + while (sib != this->end()) { + traversal_queue.push(sib.node); + ++sib; + } + traversal_queue.pop(); + if (traversal_queue.size() > 0) + this->node = traversal_queue.front(); + else + this->node = 0; + return (*this); +} + +template +typename tree::breadth_first_queued_iterator +tree::breadth_first_queued_iterator::operator++(int) { + breadth_first_queued_iterator copy = *this; + ++(*this); + return copy; +} + +template +typename tree::breadth_first_queued_iterator & +tree::breadth_first_queued_iterator::operator+=( + unsigned int num) { + while (num > 0) { + ++(*this); + --num; + } + return (*this); +} // Fixed depth iterator template tree::fixed_depth_iterator::fixed_depth_iterator() - : iterator_base() - { - } - -template -tree::fixed_depth_iterator::fixed_depth_iterator(tree_node *tn) - : iterator_base(tn), top_node(0) - { - } - -template -tree::fixed_depth_iterator::fixed_depth_iterator(const iterator_base& other) - : iterator_base(other.node), top_node(0) - { - } - -template -tree::fixed_depth_iterator::fixed_depth_iterator(const sibling_iterator& other) - : iterator_base(other.node), top_node(0) - { - } - -template -tree::fixed_depth_iterator::fixed_depth_iterator(const fixed_depth_iterator& other) - : iterator_base(other.node), top_node(other.top_node) - { - } - -template -void tree::fixed_depth_iterator::swap(fixed_depth_iterator& first, fixed_depth_iterator& second) - { - std::swap(first.node, second.node); - std::swap(first.skip_current_children_, second.skip_current_children_); - std::swap(first.top_node, second.top_node); - } - -template -typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator=(fixed_depth_iterator other) - { - swap(*this, other); - return *this; - } - -template -bool tree::fixed_depth_iterator::operator==(const fixed_depth_iterator& other) const - { - if(other.node==this->node && other.top_node==top_node) return true; - else return false; - } - -template -bool tree::fixed_depth_iterator::operator!=(const fixed_depth_iterator& other) const - { - if(other.node!=this->node || other.top_node!=top_node) return true; - else return false; - } - -template -typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator++() - { - assert(this->node!=0); - - if(this->node->next_sibling) { - this->node=this->node->next_sibling; - } - else { - int relative_depth=0; - upper: - do { - if(this->node==this->top_node) { - this->node=0; // FIXME: return a proper fixed_depth end iterator once implemented - return *this; - } - this->node=this->node->parent; - if(this->node==0) return *this; - --relative_depth; - } while(this->node->next_sibling==0); - lower: - this->node=this->node->next_sibling; - while(this->node->first_child==0) { - if(this->node->next_sibling==0) - goto upper; - this->node=this->node->next_sibling; - if(this->node==0) return *this; - } - while(relative_depth<0 && this->node->first_child!=0) { - this->node=this->node->first_child; - ++relative_depth; - } - if(relative_depth<0) { - if(this->node->next_sibling==0) goto upper; - else goto lower; - } - } - return *this; - } - -template -typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator--() - { - assert(this->node!=0); - - if(this->node->prev_sibling) { - this->node=this->node->prev_sibling; - } - else { - int relative_depth=0; - upper: - do { - if(this->node==this->top_node) { - this->node=0; - return *this; - } - this->node=this->node->parent; - if(this->node==0) return *this; - --relative_depth; - } while(this->node->prev_sibling==0); - lower: - this->node=this->node->prev_sibling; - while(this->node->last_child==0) { - if(this->node->prev_sibling==0) - goto upper; - this->node=this->node->prev_sibling; - if(this->node==0) return *this; - } - while(relative_depth<0 && this->node->last_child!=0) { - this->node=this->node->last_child; - ++relative_depth; - } - if(relative_depth<0) { - if(this->node->prev_sibling==0) goto upper; - else goto lower; - } - } - return *this; + : iterator_base() {} -// -// -// assert(this->node!=0); -// if(this->node->prev_sibling!=0) { -// this->node=this->node->prev_sibling; -// assert(this->node!=0); -// if(this->node->parent==0 && this->node->prev_sibling==0) // head element -// this->node=0; -// } -// else { -// tree_node *par=this->node->parent; -// do { -// par=par->prev_sibling; -// if(par==0) { // FIXME: need to keep track of this! -// this->node=0; -// return *this; -// } -// } while(par->last_child==0); -// this->node=par->last_child; -// } -// return *this; - } - -template -typename tree::fixed_depth_iterator tree::fixed_depth_iterator::operator++(int) - { - fixed_depth_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename tree::fixed_depth_iterator tree::fixed_depth_iterator::operator--(int) - { - fixed_depth_iterator copy = *this; - --(*this); - return copy; - } - -template -typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator-=(unsigned int num) - { - while(num>0) { - --(*this); - --(num); - } - return (*this); - } - -template -typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator+=(unsigned int num) - { - while(num>0) { - ++(*this); - --(num); - } - return *this; - } +template +tree::fixed_depth_iterator::fixed_depth_iterator( + tree_node *tn) + : iterator_base(tn), top_node(0) {} + +template +tree::fixed_depth_iterator::fixed_depth_iterator( + const iterator_base &other) + : iterator_base(other.node), top_node(0) {} + +template +tree::fixed_depth_iterator::fixed_depth_iterator( + const sibling_iterator &other) + : iterator_base(other.node), top_node(0) {} + +template +tree::fixed_depth_iterator::fixed_depth_iterator( + const fixed_depth_iterator &other) + : iterator_base(other.node), top_node(other.top_node) {} + +template +void tree::fixed_depth_iterator::swap( + fixed_depth_iterator &first, fixed_depth_iterator &second) { + std::swap(first.node, second.node); + std::swap(first.skip_current_children_, second.skip_current_children_); + std::swap(first.top_node, second.top_node); +} + +template +typename tree::fixed_depth_iterator & +tree::fixed_depth_iterator::operator=( + fixed_depth_iterator other) { + swap(*this, other); + return *this; +} + +template +bool tree::fixed_depth_iterator::operator==( + const fixed_depth_iterator &other) const { + if (other.node == this->node && other.top_node == top_node) + return true; + else + return false; +} + +template +bool tree::fixed_depth_iterator::operator!=( + const fixed_depth_iterator &other) const { + if (other.node != this->node || other.top_node != top_node) + return true; + else + return false; +} + +template +typename tree::fixed_depth_iterator & +tree::fixed_depth_iterator::operator++() { + assert(this->node != 0); + + if (this->node->next_sibling) { + this->node = this->node->next_sibling; + } else { + int relative_depth = 0; + upper: + do { + if (this->node == this->top_node) { + this->node = 0; // FIXME: return a proper fixed_depth end iterator once + // implemented + return *this; + } + this->node = this->node->parent; + if (this->node == 0) + return *this; + --relative_depth; + } while (this->node->next_sibling == 0); + lower: + this->node = this->node->next_sibling; + while (this->node->first_child == 0) { + if (this->node->next_sibling == 0) + goto upper; + this->node = this->node->next_sibling; + if (this->node == 0) + return *this; + } + while (relative_depth < 0 && this->node->first_child != 0) { + this->node = this->node->first_child; + ++relative_depth; + } + if (relative_depth < 0) { + if (this->node->next_sibling == 0) + goto upper; + else + goto lower; + } + } + return *this; +} + +template +typename tree::fixed_depth_iterator & +tree::fixed_depth_iterator::operator--() { + assert(this->node != 0); + + if (this->node->prev_sibling) { + this->node = this->node->prev_sibling; + } else { + int relative_depth = 0; + upper: + do { + if (this->node == this->top_node) { + this->node = 0; + return *this; + } + this->node = this->node->parent; + if (this->node == 0) + return *this; + --relative_depth; + } while (this->node->prev_sibling == 0); + lower: + this->node = this->node->prev_sibling; + while (this->node->last_child == 0) { + if (this->node->prev_sibling == 0) + goto upper; + this->node = this->node->prev_sibling; + if (this->node == 0) + return *this; + } + while (relative_depth < 0 && this->node->last_child != 0) { + this->node = this->node->last_child; + ++relative_depth; + } + if (relative_depth < 0) { + if (this->node->prev_sibling == 0) + goto upper; + else + goto lower; + } + } + return *this; + + // + // + // assert(this->node!=0); + // if(this->node->prev_sibling!=0) { + // this->node=this->node->prev_sibling; + // assert(this->node!=0); + // if(this->node->parent==0 && this->node->prev_sibling==0) // head + //element this->node=0; + // } + // else { + // tree_node *par=this->node->parent; + // do { + // par=par->prev_sibling; + // if(par==0) { // FIXME: need to keep track of this! + // this->node=0; + // return *this; + // } + // } while(par->last_child==0); + // this->node=par->last_child; + // } + // return *this; +} +template +typename tree::fixed_depth_iterator +tree::fixed_depth_iterator::operator++(int) { + fixed_depth_iterator copy = *this; + ++(*this); + return copy; +} + +template +typename tree::fixed_depth_iterator +tree::fixed_depth_iterator::operator--(int) { + fixed_depth_iterator copy = *this; + --(*this); + return copy; +} + +template +typename tree::fixed_depth_iterator & +tree::fixed_depth_iterator::operator-=( + unsigned int num) { + while (num > 0) { + --(*this); + --(num); + } + return (*this); +} + +template +typename tree::fixed_depth_iterator & +tree::fixed_depth_iterator::operator+=( + unsigned int num) { + while (num > 0) { + ++(*this); + --(num); + } + return *this; +} // Sibling iterator template -tree::sibling_iterator::sibling_iterator() - : iterator_base() - { - set_parent_(); - } +tree::sibling_iterator::sibling_iterator() + : iterator_base() { + set_parent_(); +} template tree::sibling_iterator::sibling_iterator(tree_node *tn) - : iterator_base(tn) - { - set_parent_(); - } + : iterator_base(tn) { + set_parent_(); +} template -tree::sibling_iterator::sibling_iterator(const iterator_base& other) - : iterator_base(other.node) - { - set_parent_(); - } +tree::sibling_iterator::sibling_iterator( + const iterator_base &other) + : iterator_base(other.node) { + set_parent_(); +} template -tree::sibling_iterator::sibling_iterator(const sibling_iterator& other) - : iterator_base(other), parent_(other.parent_) - { - } +tree::sibling_iterator::sibling_iterator( + const sibling_iterator &other) + : iterator_base(other), parent_(other.parent_) {} template -void tree::sibling_iterator::swap(sibling_iterator& first, sibling_iterator& second) - { - std::swap(first.node, second.node); - std::swap(first.skip_current_children_, second.skip_current_children_); - std::swap(first.parent_, second.parent_); - } - +void tree::sibling_iterator::swap( + sibling_iterator &first, sibling_iterator &second) { + std::swap(first.node, second.node); + std::swap(first.skip_current_children_, second.skip_current_children_); + std::swap(first.parent_, second.parent_); +} template -typename tree::sibling_iterator& tree::sibling_iterator::operator=(sibling_iterator other) - { - swap(*this, other); - return *this; - } - +typename tree::sibling_iterator & +tree::sibling_iterator::operator=( + sibling_iterator other) { + swap(*this, other); + return *this; +} + template -void tree::sibling_iterator::set_parent_() - { - parent_=0; - if(this->node==0) return; - if(this->node->parent!=0) - parent_=this->node->parent; - } +void tree::sibling_iterator::set_parent_() { + parent_ = 0; + if (this->node == 0) + return; + if (this->node->parent != 0) + parent_ = this->node->parent; +} template -typename tree::sibling_iterator& tree::sibling_iterator::operator++() - { - if(this->node) - this->node=this->node->next_sibling; - return *this; - } +typename tree::sibling_iterator & +tree::sibling_iterator::operator++() { + if (this->node) + this->node = this->node->next_sibling; + return *this; +} template -typename tree::sibling_iterator& tree::sibling_iterator::operator--() - { - if(this->node) this->node=this->node->prev_sibling; - else { - assert(parent_); - this->node=parent_->last_child; - } - return *this; +typename tree::sibling_iterator & +tree::sibling_iterator::operator--() { + if (this->node) + this->node = this->node->prev_sibling; + else { + assert(parent_); + this->node = parent_->last_child; + } + return *this; } template -typename tree::sibling_iterator tree::sibling_iterator::operator++(int) - { - sibling_iterator copy = *this; - ++(*this); - return copy; - } +typename tree::sibling_iterator +tree::sibling_iterator::operator++(int) { + sibling_iterator copy = *this; + ++(*this); + return copy; +} template -typename tree::sibling_iterator tree::sibling_iterator::operator--(int) - { - sibling_iterator copy = *this; - --(*this); - return copy; - } +typename tree::sibling_iterator +tree::sibling_iterator::operator--(int) { + sibling_iterator copy = *this; + --(*this); + return copy; +} template -typename tree::sibling_iterator& tree::sibling_iterator::operator+=(unsigned int num) - { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } +typename tree::sibling_iterator & +tree::sibling_iterator::operator+=(unsigned int num) { + while (num > 0) { + ++(*this); + --num; + } + return (*this); +} template -typename tree::sibling_iterator& tree::sibling_iterator::operator-=(unsigned int num) - { - while(num>0) { - --(*this); - --num; - } - return (*this); - } +typename tree::sibling_iterator & +tree::sibling_iterator::operator-=(unsigned int num) { + while (num > 0) { + --(*this); + --num; + } + return (*this); +} template -typename tree::tree_node *tree::sibling_iterator::range_first() const - { - tree_node *tmp=parent_->first_child; - return tmp; - } +typename tree::tree_node * +tree::sibling_iterator::range_first() const { + tree_node *tmp = parent_->first_child; + return tmp; +} template -typename tree::tree_node *tree::sibling_iterator::range_last() const - { - return parent_->last_child; - } +typename tree::tree_node * +tree::sibling_iterator::range_last() const { + return parent_->last_child; +} // Leaf iterator template -tree::leaf_iterator::leaf_iterator() - : iterator_base(0), top_node(0) - { - } +tree::leaf_iterator::leaf_iterator() + : iterator_base(0), top_node(0) {} template -tree::leaf_iterator::leaf_iterator(tree_node *tn, tree_node *top) - : iterator_base(tn), top_node(top) - { - } +tree::leaf_iterator::leaf_iterator(tree_node *tn, + tree_node *top) + : iterator_base(tn), top_node(top) {} template -tree::leaf_iterator::leaf_iterator(const iterator_base &other) - : iterator_base(other.node), top_node(0) - { - } +tree::leaf_iterator::leaf_iterator( + const iterator_base &other) + : iterator_base(other.node), top_node(0) {} template -tree::leaf_iterator::leaf_iterator(const sibling_iterator& other) - : iterator_base(other.node), top_node(0) - { - if(this->node==0) { - if(other.range_last()!=0) - this->node=other.range_last(); - else - this->node=other.parent_; - ++(*this); - } - } - -template -typename tree::leaf_iterator& tree::leaf_iterator::operator++() - { - assert(this->node!=0); - if(this->node->first_child!=0) { // current node is no longer leaf (children got added) - while(this->node->first_child) - this->node=this->node->first_child; - } - else { - while(this->node->next_sibling==0) { - if (this->node->parent==0) return *this; - this->node=this->node->parent; - if (top_node != 0 && this->node==top_node) return *this; - } - this->node=this->node->next_sibling; - while(this->node->first_child) - this->node=this->node->first_child; - } - return *this; - } - -template -typename tree::leaf_iterator& tree::leaf_iterator::operator--() - { - assert(this->node!=0); - while (this->node->prev_sibling==0) { - if (this->node->parent==0) return *this; - this->node=this->node->parent; - if (top_node !=0 && this->node==top_node) return *this; - } - this->node=this->node->prev_sibling; - while(this->node->last_child) - this->node=this->node->last_child; - return *this; - } - -template -typename tree::leaf_iterator tree::leaf_iterator::operator++(int) - { - leaf_iterator copy = *this; - ++(*this); - return copy; - } - -template -typename tree::leaf_iterator tree::leaf_iterator::operator--(int) - { - leaf_iterator copy = *this; - --(*this); - return copy; - } - - -template -typename tree::leaf_iterator& tree::leaf_iterator::operator+=(unsigned int num) - { - while(num>0) { - ++(*this); - --num; - } - return (*this); - } +tree::leaf_iterator::leaf_iterator( + const sibling_iterator &other) + : iterator_base(other.node), top_node(0) { + if (this->node == 0) { + if (other.range_last() != 0) + this->node = other.range_last(); + else + this->node = other.parent_; + ++(*this); + } +} template -typename tree::leaf_iterator& tree::leaf_iterator::operator-=(unsigned int num) - { - while(num>0) { - --(*this); - --num; - } - return (*this); - } +typename tree::leaf_iterator & +tree::leaf_iterator::operator++() { + assert(this->node != 0); + if (this->node->first_child != + 0) { // current node is no longer leaf (children got added) + while (this->node->first_child) + this->node = this->node->first_child; + } else { + while (this->node->next_sibling == 0) { + if (this->node->parent == 0) + return *this; + this->node = this->node->parent; + if (top_node != 0 && this->node == top_node) + return *this; + } + this->node = this->node->next_sibling; + while (this->node->first_child) + this->node = this->node->first_child; + } + return *this; +} + +template +typename tree::leaf_iterator & +tree::leaf_iterator::operator--() { + assert(this->node != 0); + while (this->node->prev_sibling == 0) { + if (this->node->parent == 0) + return *this; + this->node = this->node->parent; + if (top_node != 0 && this->node == top_node) + return *this; + } + this->node = this->node->prev_sibling; + while (this->node->last_child) + this->node = this->node->last_child; + return *this; +} + +template +typename tree::leaf_iterator +tree::leaf_iterator::operator++(int) { + leaf_iterator copy = *this; + ++(*this); + return copy; +} + +template +typename tree::leaf_iterator +tree::leaf_iterator::operator--(int) { + leaf_iterator copy = *this; + --(*this); + return copy; +} + +template +typename tree::leaf_iterator & +tree::leaf_iterator::operator+=(unsigned int num) { + while (num > 0) { + ++(*this); + --num; + } + return (*this); +} + +template +typename tree::leaf_iterator & +tree::leaf_iterator::operator-=(unsigned int num) { + while (num > 0) { + --(*this); + --num; + } + return (*this); +} #endif diff --git a/core/opengate_core/opengate_lib/tree_util.hh b/core/opengate_core/opengate_lib/tree_util.hh index bc69594f0..2706b3e2a 100644 --- a/core/opengate_core/opengate_lib/tree_util.hh +++ b/core/opengate_core/opengate_lib/tree_util.hh @@ -1,13 +1,13 @@ -/* +/* - A collection of miscellaneous utilities that operate on the templated - tree.hh class. + A collection of miscellaneous utilities that operate on the templated + tree.hh class. - Copyright (C) 2001-2009 Kasper Peeters + Copyright (C) 2001-2009 Kasper Peeters - (At the moment this only contains a printing utility, thanks to Linda - Buisman ) + (At the moment this only contains a printing utility, thanks to Linda + Buisman ) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,72 +21,70 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - + */ #ifndef tree_util_hh_ #define tree_util_hh_ -#include #include "tree.hh" +#include namespace kptree { -template -void print_tree_bracketed(const tree& t, std::ostream& str=std::cout); - -template -void print_subtree_bracketed(const tree& t, typename tree::iterator iRoot, - std::ostream& str=std::cout); - +template +void print_tree_bracketed(const tree &t, std::ostream &str = std::cout); +template +void print_subtree_bracketed(const tree &t, typename tree::iterator iRoot, + std::ostream &str = std::cout); // Iterate over all roots (the head) and print each one on a new line // by calling printSingleRoot. -template -void print_tree_bracketed(const tree& t, std::ostream& str) - { - int headCount = t.number_of_siblings(t.begin()); - int headNum = 0; - for(typename tree::sibling_iterator iRoots = t.begin(); iRoots != t.end(); ++iRoots, ++headNum) { - print_subtree_bracketed(t,iRoots,str); - if (headNum != headCount) { - str << std::endl; - } - } - } - +template +void print_tree_bracketed(const tree &t, std::ostream &str) { + int headCount = t.number_of_siblings(t.begin()); + int headNum = 0; + for (typename tree::sibling_iterator iRoots = t.begin(); iRoots != t.end(); + ++iRoots, ++headNum) { + print_subtree_bracketed(t, iRoots, str); + if (headNum != headCount) { + str << std::endl; + } + } +} // Print everything under this root in a flat, bracketed structure. -template -void print_subtree_bracketed(const tree& t, typename tree::iterator iRoot, std::ostream& str) - { - if(t.empty()) return; - if (t.number_of_children(iRoot) == 0) { - str << *iRoot; - } - else { - // parent - str << *iRoot; - str << "("; - // child1, ..., childn - int siblingCount = t.number_of_siblings(t.begin(iRoot)); - int siblingNum; - typename tree::sibling_iterator iChildren; - for (iChildren = t.begin(iRoot), siblingNum = 0; iChildren != t.end(iRoot); ++iChildren, ++siblingNum) { - // recursively print child - print_subtree_bracketed(t,iChildren,str); - // comma after every child except the last one - if (siblingNum != siblingCount ) { - str << ", "; - } - } - str << ")"; - } - } - +template +void print_subtree_bracketed(const tree &t, typename tree::iterator iRoot, + std::ostream &str) { + if (t.empty()) + return; + if (t.number_of_children(iRoot) == 0) { + str << *iRoot; + } else { + // parent + str << *iRoot; + str << "("; + // child1, ..., childn + int siblingCount = t.number_of_siblings(t.begin(iRoot)); + int siblingNum; + typename tree::sibling_iterator iChildren; + for (iChildren = t.begin(iRoot), siblingNum = 0; iChildren != t.end(iRoot); + ++iChildren, ++siblingNum) { + // recursively print child + print_subtree_bracketed(t, iChildren, str); + // comma after every child except the last one + if (siblingNum != siblingCount) { + str << ", "; + } + } + str << ")"; + } } +} // namespace kptree + #endif diff --git a/opengate/actors/miscactors.py b/opengate/actors/miscactors.py index 802df587d..1322c1d1d 100644 --- a/opengate/actors/miscactors.py +++ b/opengate/actors/miscactors.py @@ -394,10 +394,12 @@ def initialize(self): self.InitializeCpp() -class LastVertexInteractionSplittingActor(ActorBase,g4.GateLastVertexInteractionSplittingActor): +class LastVertexInteractionSplittingActor( + ActorBase, g4.GateLastVertexInteractionSplittingActor +): """This splitting actor proposes an interaction splitting at the last particle vertex before the exit - of the biased volume. This actor can be usefull for application where collimation are important, - such as in medical LINAC (Linear Accelerator) simulations or radiation shielding. + of the biased volume. This actor can be usefull for application where collimation are important, + such as in medical LINAC (Linear Accelerator) simulations or radiation shielding. """ user_info_defaults = { @@ -407,22 +409,18 @@ class LastVertexInteractionSplittingActor(ActorBase,g4.GateLastVertexInteraction "doc": "Defines the number of particles exiting at each split process. Unlike other split actors, this splitting factor counts particles that actually exit, not just those generated.", }, ), - "angular_kill": ( False, { "doc": "If enabled, particles with momentum outside a specified angular range are killed.", }, ), - "max_theta": ( 90 * g4_units.deg, { "doc": "Defines the maximum angle (in degrees) from a central axis within which particles are retained. Particles with momentum beyond this angle are removed. The angular range spans from 0 to max_theta, measured from the vector_director", }, ), - - "vector_director": ( [0, 0, 1], { @@ -441,11 +439,8 @@ class LastVertexInteractionSplittingActor(ActorBase,g4.GateLastVertexInteraction "doc": "Defines a batch of number of processes to regenerate, calculated as batch_size * splitting_factor. The optimal value depends on the collimation setup; for example, a batch_size of 10 works well for LINAC head configurations.", }, ), - - } - def __init__(self, *args, **kwargs): ActorBase.__init__(self, *args, **kwargs) self.__initcpp__() @@ -453,14 +448,17 @@ def __init__(self, *args, **kwargs): def __initcpp__(self): g4.GateLastVertexInteractionSplittingActor.__init__(self, {"name": self.name}) - self.AddActions({"BeginOfRunAction", - "BeginOfEventAction", - "PreUserTrackingAction", - "SteppingAction", - "PostUserTrackingAction", - "EndOfEventAction"}) - - + self.AddActions( + { + "BeginOfRunAction", + "BeginOfEventAction", + "PreUserTrackingAction", + "SteppingAction", + "PostUserTrackingAction", + "EndOfEventAction", + } + ) + def initialize(self): ActorBase.initialize(self) self.InitializeUserInput(self.user_info) @@ -476,6 +474,7 @@ def initialize(self): self.list_of_volume_name.append(volume_name) self.fListOfVolumeAncestor = self.list_of_volume_name + class BremSplittingActor(SplittingActorBase, g4.GateBOptrBremSplittingActor): # hints for IDE processes: list diff --git a/opengate/managers.py b/opengate/managers.py index cd79c99ec..4d4f0422c 100644 --- a/opengate/managers.py +++ b/opengate/managers.py @@ -119,7 +119,7 @@ "DigitizerEnergyWindowsActor": DigitizerEnergyWindowsActor, "DigitizerHitsCollectionActor": DigitizerHitsCollectionActor, "PhaseSpaceActor": PhaseSpaceActor, - "LastVertexInteractionSplittingActor":LastVertexInteractionSplittingActor, + "LastVertexInteractionSplittingActor": LastVertexInteractionSplittingActor, } @@ -1194,19 +1194,18 @@ def dump_volume_types(self): for vt in self.volume_types: s += f"{vt} " return s - + def get_volume_tree(self): return self.volume_tree_root - - + def print_volume_types(self): print(self.dump_volume_types()) def dump_material_database_names(self): return list(self.material_database.filenames) - + def get_volume_tree(self): - return (self.volume_tree_root) + return self.volume_tree_root def get_volume_tree(self): return self.volume_tree_root diff --git a/opengate/tests/src/test077_kill_interacting_particles.py b/opengate/tests/src/test077_kill_interacting_particles.py index b36776d8f..fc342cdf5 100644 --- a/opengate/tests/src/test077_kill_interacting_particles.py +++ b/opengate/tests/src/test077_kill_interacting_particles.py @@ -11,8 +11,6 @@ def test077_test(entry_data, exit_data_1, exit_data_2): - - liste_ekin = [] liste_evtID = [] liste_trackID = [] @@ -29,7 +27,6 @@ def test077_test(entry_data, exit_data_1, exit_data_2): Ekin_entry = entry_data["KineticEnergy"][i] Ekin_exit = exit_data_1["KineticEnergy"][j] - if (TID_entry != TID_exit) or (Ekin_exit != Ekin_entry): liste_ekin.append(exit_data_1["KineticEnergy"][j]) liste_evtID.append(exit_data_1["EventID"][j]) diff --git a/opengate/tests/src/test084_last_vertex_splittting.py b/opengate/tests/src/test084_last_vertex_splittting.py index 956520b59..390c5d1ae 100755 --- a/opengate/tests/src/test084_last_vertex_splittting.py +++ b/opengate/tests/src/test084_last_vertex_splittting.py @@ -8,63 +8,81 @@ from opengate.tests import utility - def validation_test(arr_ref, arr_data, nb_split): arr_ref = arr_ref[arr_ref["ParticleName"] == "gamma"] arr_data = arr_data[arr_data["ParticleName"] == "gamma"] - weight_data = np.round(np.mean(arr_data["Weight"]),4) + weight_data = np.round(np.mean(arr_data["Weight"]), 4) bool_weight = False - print("Weight mean is equal to",weight_data, "and need to be equal to",1/nb_split) - if weight_data == 1/nb_split: + print( + "Weight mean is equal to", weight_data, "and need to be equal to", 1 / nb_split + ) + if weight_data == 1 / nb_split: bool_weight = True - bool_events = False - sigma = np.sqrt((len(arr_data["KineticEnergy"])/nb_split))*nb_split + sigma = np.sqrt((len(arr_data["KineticEnergy"]) / nb_split)) * nb_split nb_events_ref = len(arr_ref["KineticEnergy"]) nb_events_data = len(arr_data["KineticEnergy"]) - print("Reference counts number:",nb_events_ref) + print("Reference counts number:", nb_events_ref) print("Biased counts number:", nb_events_data) - if nb_events_data - 4*sigma <= nb_events_ref <= nb_events_data + 4*sigma: - bool_events =True - - keys = ["KineticEnergy", "PreDirection_X","PreDirection_Y","PreDirection_Z","PrePosition_X","PrePosition_Y"] + if nb_events_data - 4 * sigma <= nb_events_ref <= nb_events_data + 4 * sigma: + bool_events = True + + keys = [ + "KineticEnergy", + "PreDirection_X", + "PreDirection_Y", + "PreDirection_Z", + "PrePosition_X", + "PrePosition_Y", + ] bool_distrib = True - for key in keys : + for key in keys: ref = arr_ref[key] data = arr_data[key] mean_ref = np.mean(ref) mean_data = np.mean(data) - std_dev_ref = np.std(ref,ddof =1) - std_dev_data = np.std(data,ddof =1) - - std_err_ref = std_dev_ref/np.sqrt(len(ref)) - std_err_data= std_dev_data/(nb_split * np.sqrt(len(data)/nb_split)) + std_dev_ref = np.std(ref, ddof=1) + std_dev_data = np.std(data, ddof=1) - print(key,"mean ref value:", np.round(mean_ref,3),"+-",np.round(std_err_ref,3)) - print(key,"mean data value:", np.round(mean_data,3),"+-",np.round(std_err_data,3)) + std_err_ref = std_dev_ref / np.sqrt(len(ref)) + std_err_data = std_dev_data / (nb_split * np.sqrt(len(data) / nb_split)) + print( + key, + "mean ref value:", + np.round(mean_ref, 3), + "+-", + np.round(std_err_ref, 3), + ) + print( + key, + "mean data value:", + np.round(mean_data, 3), + "+-", + np.round(std_err_data, 3), + ) - if (mean_data - 4 * np.sqrt(std_err_data**2 + std_err_ref**2) > mean_ref) or (mean_data + 4 * np.sqrt(std_err_data**2 + std_err_ref**2) mean_ref + ) or (mean_data + 4 * np.sqrt(std_err_data**2 + std_err_ref**2) < mean_ref): bool_distrib = False if bool_distrib and bool_events and bool_weight: return True - else : + else: return False - - if __name__ == "__main__": for i in range(2): - if i == 0 : + if i == 0: bias = False - else : + else: bias = True paths = utility.get_default_test_paths( __file__, "test084_last_vertex_splitting", output_folder="test084" @@ -113,7 +131,7 @@ def validation_test(arr_ref, arr_data, nb_split): W_tubs.mother = world.name W_tubs.rmin = 0 - W_tubs.rmax = 0.4*cm + W_tubs.rmax = 0.4 * cm W_tubs.dz = 0.05 * m W_tubs.color = [0.8, 0.2, 0.1, 1] angle_x = 45 @@ -125,31 +143,30 @@ def validation_test(arr_ref, arr_data, nb_split): ).as_matrix() W_tubs.rotation = rotation - if bias : - ###### Last vertex Splitting ACTOR ######### + if bias: + ###### Last vertex Splitting ACTOR ######### nb_split = 10 - vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") + vertex_splitting_actor = sim.add_actor( + "LastVertexInteractionSplittingActor", "vertexSplittingW" + ) vertex_splitting_actor.attached_to = W_tubs.name vertex_splitting_actor.splitting_factor = nb_split vertex_splitting_actor.angular_kill = True - vertex_splitting_actor.vector_director = [0,0,-1] - vertex_splitting_actor.max_theta = 90*deg + vertex_splitting_actor.vector_director = [0, 0, -1] + vertex_splitting_actor.max_theta = 90 * deg vertex_splitting_actor.batch_size = 10 - plan = sim.add_volume("Box", "plan_phsp") plan.material = "G4_Galactic" - plan.size = [5*cm,5*cm,1*nm] - plan.translation = [0,0,-1*cm] - + plan.size = [5 * cm, 5 * cm, 1 * nm] + plan.translation = [0, 0, -1 * cm] ####### gamma source ########### source = sim.add_source("GenericSource", "source1") source.particle = "gamma" source.n = 100000 - if bias : - source.n = source.n/nb_split - + if bias: + source.n = source.n / nb_split source.position.type = "sphere" source.position.radius = 1 * nm @@ -157,15 +174,15 @@ def validation_test(arr_ref, arr_data, nb_split): # source.direction.momentum = [0,0,-1] source.direction.momentum = np.dot(rotation, np.array([0, 0, -1])) source.energy.type = "mono" - source.energy.mono = 4 * MeV + source.energy.mono = 4 * MeV # ###### LastVertexSource ############# - if bias : + if bias: source_0 = sim.add_source("LastVertexSource", "source_vertex") source_0.n = 1 ####### PHASE SPACE ACTOR ############## - sim.output_dir =paths.output + sim.output_dir = paths.output phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") phsp_actor.attached_to = plan.name phsp_actor.attributes = [ @@ -178,12 +195,11 @@ def validation_test(arr_ref, arr_data, nb_split): "PrePosition", "TrackCreatorProcess", ] - if bias : - phsp_actor.output_filename ="test084_output_data_last_vertex_biased.root" - else : + if bias: + phsp_actor.output_filename = "test084_output_data_last_vertex_biased.root" + else: phsp_actor.output_filename = "test084_output_data_last_vertex_ref.root" - s = sim.add_actor("SimulationStatisticsActor", "Stats") s.track_types_flag = True sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3" @@ -197,9 +213,6 @@ def validation_test(arr_ref, arr_data, nb_split): output = sim.run(True) print(s) - - - f_data = uproot.open(paths.output / "test084_output_data_last_vertex_biased.root") f_ref_data = uproot.open(paths.output / "test084_output_data_last_vertex_ref.root") arr_data = f_data["PhaseSpace"].arrays() @@ -207,4 +220,3 @@ def validation_test(arr_ref, arr_data, nb_split): # # is_ok = validation_test(arr_ref_data, arr_data, nb_split) utility.test_ok(is_ok) - diff --git a/opengate/tests/src/test084_last_vertex_splittting_angular_kill.py b/opengate/tests/src/test084_last_vertex_splittting_angular_kill.py index c9f058618..d1fa56cf2 100755 --- a/opengate/tests/src/test084_last_vertex_splittting_angular_kill.py +++ b/opengate/tests/src/test084_last_vertex_splittting_angular_kill.py @@ -8,28 +8,29 @@ from opengate.tests import utility - -def validation_test(arr_data,vector_director,max_theta): +def validation_test(arr_data, vector_director, max_theta): arr_data = arr_data[arr_data["ParticleName"] == "gamma"] - qt_mvt_data = arr_data[["PreDirection_X","PreDirection_Y","PreDirection_Z"]] - mom_data = np.zeros((len(qt_mvt_data["PreDirection_X"]),3)) - mom_data[:,0] += np.array(qt_mvt_data["PreDirection_X"]) + qt_mvt_data = arr_data[["PreDirection_X", "PreDirection_Y", "PreDirection_Z"]] + mom_data = np.zeros((len(qt_mvt_data["PreDirection_X"]), 3)) + mom_data[:, 0] += np.array(qt_mvt_data["PreDirection_X"]) mom_data[:, 1] += np.array(qt_mvt_data["PreDirection_Y"]) mom_data[:, 2] += np.array(qt_mvt_data["PreDirection_Z"]) - l_theta = np.zeros(len(mom_data[:,0])) + l_theta = np.zeros(len(mom_data[:, 0])) for i in range(len(l_theta)): theta = np.arccos(mom_data[i].dot(vector_director)) l_theta[i] = theta - print('Number of particles with an angle higher than max_theta:',len(l_theta[l_theta > max_theta])) + print( + "Number of particles with an angle higher than max_theta:", + len(l_theta[l_theta > max_theta]), + ) if len(l_theta[l_theta > max_theta]) == 0: return True else: return False - if __name__ == "__main__": bias = True paths = utility.get_default_test_paths( @@ -79,7 +80,7 @@ def validation_test(arr_data,vector_director,max_theta): W_tubs.mother = world.name W_tubs.rmin = 0 - W_tubs.rmax = 0.4*cm + W_tubs.rmax = 0.4 * cm W_tubs.dz = 0.05 * m W_tubs.color = [0.8, 0.2, 0.1, 1] angle_x = 45 @@ -91,36 +92,33 @@ def validation_test(arr_data,vector_director,max_theta): ).as_matrix() W_tubs.rotation = rotation - if bias : - ###### Last vertex Splitting ACTOR ######### + if bias: + ###### Last vertex Splitting ACTOR ######### nb_split = 10 - vertex_splitting_actor = sim.add_actor("LastVertexInteractionSplittingActor", "vertexSplittingW") + vertex_splitting_actor = sim.add_actor( + "LastVertexInteractionSplittingActor", "vertexSplittingW" + ) vertex_splitting_actor.attached_to = W_tubs.name vertex_splitting_actor.splitting_factor = nb_split vertex_splitting_actor.angular_kill = True - vertex_splitting_actor.vector_director = [0,0,-1] - vertex_splitting_actor.max_theta = 15*deg + vertex_splitting_actor.vector_director = [0, 0, -1] + vertex_splitting_actor.max_theta = 15 * deg vertex_splitting_actor.batch_size = 10 - plan = sim.add_volume("Box", "plan_phsp") plan.material = "G4_Galactic" - plan.size = [5*cm,5*cm,1*nm] - plan.translation = [0,0,-1*cm] + plan.size = [5 * cm, 5 * cm, 1 * nm] + plan.translation = [0, 0, -1 * cm] - if bias : + if bias: vector_director = np.array(vertex_splitting_actor.vector_director) - - - ####### gamma source ########### source = sim.add_source("GenericSource", "source1") source.particle = "gamma" source.n = 100000 - if bias : - source.n = source.n/nb_split - + if bias: + source.n = source.n / nb_split source.position.type = "sphere" source.position.radius = 1 * nm @@ -128,15 +126,15 @@ def validation_test(arr_data,vector_director,max_theta): # source.direction.momentum = [0,0,-1] source.direction.momentum = np.dot(rotation, np.array([0, 0, -1])) source.energy.type = "mono" - source.energy.mono = 4 * MeV + source.energy.mono = 4 * MeV ###### LastVertexSource ############# - if bias : + if bias: source_0 = sim.add_source("LastVertexSource", "source_vertex") source_0.n = 1 ####### PHASE SPACE ACTOR ############## - sim.output_dir =paths.output + sim.output_dir = paths.output phsp_actor = sim.add_actor("PhaseSpaceActor", "PhaseSpace") phsp_actor.attached_to = plan.name phsp_actor.attributes = [ @@ -149,8 +147,8 @@ def validation_test(arr_data,vector_director,max_theta): "PrePosition", "TrackCreatorProcess", ] - if bias : - phsp_actor.output_filename ="test084_output_data_last_vertex_angular_kill.root" + if bias: + phsp_actor.output_filename = "test084_output_data_last_vertex_angular_kill.root" s = sim.add_actor("SimulationStatisticsActor", "Stats") s.track_types_flag = True @@ -165,8 +163,9 @@ def validation_test(arr_data,vector_director,max_theta): output = sim.run() print(s) - f_data = uproot.open(paths.output / "test084_output_data_last_vertex_angular_kill.root") + f_data = uproot.open( + paths.output / "test084_output_data_last_vertex_angular_kill.root" + ) arr_data = f_data["PhaseSpace"].arrays() - is_ok = validation_test(arr_data, vector_director,vertex_splitting_actor.max_theta) + is_ok = validation_test(arr_data, vector_director, vertex_splitting_actor.max_theta) utility.test_ok(is_ok) - From 79184da5ab374f96b0e649a7f931b00072ca91b5 Mon Sep 17 00:00:00 2001 From: majacquet Date: Wed, 13 Nov 2024 14:53:51 +0100 Subject: [PATCH 51/82] Merge with last_vertex_spliting_actor --- .github/workflows/delocateWindows.py | 2 +- .github/workflows/main.yml | 1 + .readthedocs.yaml | 27 +- MANIFEST.in | 1 + autodoctest.py | 3 - core/opengate_core/opengate_core.cpp | 12 +- .../opengate_lib/GateGenericSource.cpp | 135 +- ...ateLastVertexInteractionSplittingActor.cpp | 707 ++++ .../GateLastVertexInteractionSplittingActor.h | 137 + .../opengate_lib/GateLastVertexSource.cpp | 112 + .../opengate_lib/GateLastVertexSource.h | 85 + .../GateLastVertexSplittingDataContainer.h | 165 + .../GateLastVertexSplittingPostStepDoIt.h | 116 + .../GateLastVertexSplittingSimpleContainer.h | 217 ++ .../opengate_lib/GateOptnForceFreeFlight.cpp | 129 + .../opengate_lib/GateOptnForceFreeFlight.h | 122 + .../GateOptnPairProdSplitting.cpp | 97 + .../opengate_lib/GateOptnPairProdSplitting.h | 52 + .../GateOptnScatteredGammaSplitting.cpp | 165 + .../GateOptnScatteredGammaSplitting.h | 53 + .../GateOptnVGenericSplitting.cpp | 105 + .../opengate_lib/GateOptnVGenericSplitting.h | 122 + .../opengate_lib/GateOptneBremSplitting.cpp | 112 + .../opengate_lib/GateOptneBremSplitting.h | 54 + ...GateOptrComptPseudoTransportationActor.cpp | 319 ++ .../GateOptrComptPseudoTransportationActor.h | 137 + .../opengate_lib/GateSPSEneDistribution.cpp | 53 +- .../opengate_lib/GateSPSEneDistribution.h | 9 +- .../opengate_lib/GateSourceManager.cpp | 4 + .../opengate_lib/GateSourceManager.h | 16 + .../opengate_core/opengate_lib/GateVActor.cpp | 4 +- ...ateLastVertexInteractionSplittingActor.cpp | 21 + .../opengate_lib/pyGateLastVertexSource.cpp | 22 + ...GateOptrComptPseudoTransportationActor.cpp | 20 + core/opengate_core/opengate_lib/tree.hh | 3412 +++++++++++++++++ core/opengate_core/opengate_lib/tree_util.hh | 92 + docs/requirements.txt | 4 + docs/run_rtd.sh | 3 +- docs/source/conf.py | 4 +- .../developer_guide_installation.rst | 32 +- docs/source/developer_guide/index.rst | 9 +- .../figures/example_lut_davis_model.png | Bin 0 -> 246838 bytes docs/source/figures/flowchart_lut_model.png | Bin 0 -> 101931 bytes .../generic_source_spectrum_discrete.png | Bin 0 -> 60728 bytes .../generic_source_spectrum_histogram.png | Bin 0 -> 53293 bytes docs/source/figures/kill_actor.png | Bin 0 -> 125552 bytes docs/source/figures/optigan_working.png | Bin 0 -> 423722 bytes .../reflection_types_and_microfacets.png | Bin 0 -> 39864 bytes docs/source/figures/surface-definition.png | Bin 0 -> 6184 bytes docs/source/user_guide/user_guide_actors.rst | 2 + .../user_guide_how_to_convert_example_1.rst | 331 ++ .../user_guide/user_guide_installation.rst | 2 +- docs/source/user_guide/user_guide_physics.rst | 58 + .../user_guide_reference_actors.rst | 44 +- .../user_guide_reference_simulation.rst | 2 +- docs/source/user_guide/user_guide_sources.rst | 164 +- docs/source/user_guide/user_guide_volumes.rst | 41 + opengate/actors/actoroutput.py | 126 +- opengate/actors/base.py | 10 + opengate/actors/dataitems.py | 90 +- opengate/actors/digitizers.py | 2 +- opengate/actors/doseactors.py | 41 +- opengate/actors/filters.py | 2 +- opengate/actors/miscactors.py | 85 + opengate/base.py | 23 +- opengate/bin/opengate_tests.py | 4 +- opengate/contrib/linacs/dicomrtplan.py | 413 +- .../contrib/linacs/elekta_versa_materials.db | 30 +- opengate/contrib/linacs/elektaversa.py | 1717 ++++++--- .../contrib/tps/treatmentPlanPhsSource.py | 52 +- opengate/data/rad_beta_spectrum.json | 425 ++ opengate/data/rad_gamma_spectrum.json | 84 + opengate/data/readme_rad_spectrum.md | 11 + opengate/engines.py | 30 +- opengate/geometry/materials.py | 19 +- opengate/geometry/utility.py | 6 +- opengate/geometry/volumes.py | 27 +- opengate/image.py | 54 +- opengate/managers.py | 61 +- opengate/sources/builders.py | 3 +- opengate/sources/generic.py | 151 +- opengate/sources/phidsources.py | 10 +- opengate/sources/phspsources.py | 9 +- opengate/tests/data | 2 +- opengate/tests/src/test008_dose_actor.py | 5 + opengate/tests/src/test009_voxels_dynamic.py | 1 + ...generic_source_energy_spectrum_discrete.py | 148 + ...eneric_source_energy_spectrum_histogram.py | 150 + .../tests/src/test019_linac_elekta_versa.py | 4 +- ...19_linac_elekta_versa_rt_plan_isocenter.py | 3 +- .../test019_linac_elekta_versa_with_mlc.py | 22 +- ...linac_elekta_versa_with_mlc_phsp_source.py | 11 +- ...019_linac_elekta_versa_with_mlc_rt_plan.py | 96 +- opengate/tests/src/test043_garf_helpers.py | 22 +- opengate/tests/src/test046_rad.py | 4 +- opengate/tests/src/test053_phid_helpers1.py | 8 +- opengate/tests/src/test053_production_cuts.py | 4 +- .../src/test066_spect_gaga_garf_helpers.py | 245 ++ ...op_simulation_criteria_multiple_runs_mt.py | 35 +- .../tests/src/test072_pseudo_transport_RR.py | 231 ++ .../src/test072_pseudo_transportation.py | 194 + .../src/test073_test3_intevo_tc99m_mt.py | 2 +- .../src/test073_test4_intevo_lu177_mt.py | 2 +- .../src/test073_test5_discovery_lu177_mt.py | 2 +- .../src/test073_test5_discovery_tc99m_mt.py | 2 +- .../test074_kill_non_interacting_particles.py | 212 + ...75_geometrical_splitting_volume_surface.py | 185 + .../src/test077_kill_interacting_particles.py | 216 ++ .../src/test083_kill_according_processes.py | 73 +- .../src/test084_last_vertex_splittting.py | 210 + ...084_last_vertex_splittting_angular_kill.py | 172 + opengate/tests/utility.py | 48 +- setup.py | 1 + 113 files changed, 12009 insertions(+), 1322 deletions(-) delete mode 100644 autodoctest.py create mode 100644 core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSource.cpp create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSource.h create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h create mode 100644 core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h create mode 100644 core/opengate_core/opengate_lib/GateOptnForceFreeFlight.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h create mode 100644 core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h create mode 100644 core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h create mode 100644 core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h create mode 100644 core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptneBremSplitting.h create mode 100644 core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp create mode 100644 core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h create mode 100644 core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp create mode 100644 core/opengate_core/opengate_lib/pyGateLastVertexSource.cpp create mode 100644 core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp create mode 100644 core/opengate_core/opengate_lib/tree.hh create mode 100644 core/opengate_core/opengate_lib/tree_util.hh create mode 100644 docs/source/figures/example_lut_davis_model.png create mode 100644 docs/source/figures/flowchart_lut_model.png create mode 100644 docs/source/figures/generic_source_spectrum_discrete.png create mode 100644 docs/source/figures/generic_source_spectrum_histogram.png create mode 100644 docs/source/figures/kill_actor.png create mode 100644 docs/source/figures/optigan_working.png create mode 100644 docs/source/figures/reflection_types_and_microfacets.png create mode 100644 docs/source/figures/surface-definition.png create mode 100644 docs/source/user_guide/user_guide_how_to_convert_example_1.rst create mode 100644 opengate/data/rad_beta_spectrum.json create mode 100644 opengate/data/rad_gamma_spectrum.json create mode 100644 opengate/data/readme_rad_spectrum.md create mode 100755 opengate/tests/src/test010_generic_source_energy_spectrum_discrete.py create mode 100755 opengate/tests/src/test010_generic_source_energy_spectrum_histogram.py create mode 100644 opengate/tests/src/test066_spect_gaga_garf_helpers.py create mode 100644 opengate/tests/src/test072_pseudo_transport_RR.py create mode 100644 opengate/tests/src/test072_pseudo_transportation.py create mode 100644 opengate/tests/src/test074_kill_non_interacting_particles.py create mode 100644 opengate/tests/src/test075_geometrical_splitting_volume_surface.py create mode 100644 opengate/tests/src/test077_kill_interacting_particles.py create mode 100755 opengate/tests/src/test084_last_vertex_splittting.py create mode 100755 opengate/tests/src/test084_last_vertex_splittting_angular_kill.py diff --git a/.github/workflows/delocateWindows.py b/.github/workflows/delocateWindows.py index 9ebc7b572..3991c1089 100644 --- a/.github/workflows/delocateWindows.py +++ b/.github/workflows/delocateWindows.py @@ -48,7 +48,7 @@ def hash_filename(filepath, blocksize=65536): def find_dll_dependencies(dll_filepath, lib_dir): print(dll_filepath) - if dll_filepath in global_dll_deps.keys(): + if dll_filepath in global_dll_deps: return global_dll_deps[dll_filepath] else: dll_deps = {} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dbcf6ff56..80cd2a02c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -544,6 +544,7 @@ jobs: pip install pandas numpy python compile_opengate_tests_results.py - name: Pushes to another repository + continue-on-error: true uses: cpina/github-action-push-to-another-repository@main env: API_TOKEN_GITHUB: ${{ secrets.PUSH_OPENGATE_TESTS_RESULTS }} diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f245c32f1..bd16e0d42 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -6,30 +6,9 @@ build: os: "ubuntu-24.04" tools: python: "3.11" -# jobs: -# pre_build: -## - cat /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/pdb.py -# - cp /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/pdb.py /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/pdb.py.bak -# - | -# { head -n 1 /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/pdb.py.bak; -# echo "import sys"; -# echo "print(\"DEBUG: sys.path = \", sys.path)"; -# echo "import cmd"; -# echo "print(\"DEBUG: cmd.__file__=\", cmd.__file__)"; -# tail -n +2 /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/pdb.py.bak; } -# > /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/pdb.py -# - cp /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/doctest.py /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/doctest.py.bak -# - | -# { echo "import pdb"; -# echo "print(\"DEBUG: pdb.__file__=\", pdb.__file__)"; -# cat /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/doctest.py.bak; } -# > /home/docs/.asdf/installs/python/3.11.9/lib/python3.11/doctest.py -# - cp /home/docs/checkouts/readthedocs.org/user_builds/opengate-python/envs/fix_autodoc/lib/python3.11/site-packages/sphinx/ext/doctest.py /home/docs/checkouts/readthedocs.org/user_builds/opengate-python/envs/fix_autodoc/lib/python3.11/site-packages/sphinx/ext/doctest.py.bak -# - | -# { head -n 188 /home/docs/checkouts/readthedocs.org/user_builds/opengate-python/envs/fix_autodoc/lib/python3.11/site-packages/sphinx/ext/doctest.py.bak; -# echo "print(\"DEBUG: doctest.__file__=\", doctest.__file__)"; -# tail -n +189 /home/docs/checkouts/readthedocs.org/user_builds/opengate-python/envs/fix_autodoc/lib/python3.11/site-packages/sphinx/ext/doctest.py.bak; } -# > /home/docs/checkouts/readthedocs.org/user_builds/opengate-python/envs/fix_autodoc/lib/python3.11/site-packages/sphinx/ext/doctest.py + jobs: + pre_build: + - cd docs/source && sphinx-build -T -b html -d _build/doctrees -D language=en . $READTHEDOCS_OUTPUT/html # Build from the docs/ directory with Sphinx sphinx: diff --git a/MANIFEST.in b/MANIFEST.in index 967802d4b..7da24ba5b 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,5 +6,6 @@ recursive-include opengate/tests/src/gate/gate_test* * recursive-include opengate/tests/output_ref * recursive-include opengate/data *.txt recursive-include opengate/data *.xml +recursive-include opengate/data *.json include opengate/tests/HEAD include VERSION diff --git a/autodoctest.py b/autodoctest.py deleted file mode 100644 index 36d5f81c5..000000000 --- a/autodoctest.py +++ /dev/null @@ -1,3 +0,0 @@ -def test_func_for_autodoc(a): - """This is a test docstring.""" - print(a) diff --git a/core/opengate_core/opengate_core.cpp b/core/opengate_core/opengate_core.cpp index 6d12106fb..9d2f769d3 100644 --- a/core/opengate_core/opengate_core.cpp +++ b/core/opengate_core/opengate_core.cpp @@ -345,10 +345,12 @@ void init_GateSimulationStatisticsActor(py::module &); void init_GatePhaseSpaceActor(py::module &); -// void init_GateComptonSplittingActor(py::module &); - void init_GateOptrComptSplittingActor(py::module &m); +void init_GateLastVertexInteractionSplittingActor(py::module &m); + +void init_GateOptrComptPseudoTransportationActor(py::module &m); + void init_GateBOptrBremSplittingActor(py::module &m); void init_G4VBiasingOperator(py::module &m); @@ -379,6 +381,8 @@ void init_GateVDigiAttribute(py::module &m); void init_GateVSource(py::module &); +void init_GateLastVertexSource(py::module &); + void init_GateExceptionHandler(py::module &); void init_GateNTuple(py::module &); @@ -552,6 +556,7 @@ PYBIND11_MODULE(opengate_core, m) { init_GateImageNestedParameterisation(m); init_GateRepeatParameterisation(m); init_GateVSource(m); + init_GateLastVertexSource(m); init_GateSourceManager(m); init_GateGenericSource(m); init_GateTreatmentPlanPBSource(m); @@ -572,9 +577,10 @@ PYBIND11_MODULE(opengate_core, m) { init_GateLETActor(m); init_GateSimulationStatisticsActor(m); init_GatePhaseSpaceActor(m); - // init_GateComptonSplittingActor(m); init_GateBOptrBremSplittingActor(m); + init_GateOptrComptPseudoTransportationActor(m); init_GateOptrComptSplittingActor(m); + init_GateLastVertexInteractionSplittingActor(m); init_GateHitsCollectionActor(m); init_GateMotionVolumeActor(m); init_GateVDigitizerWithOutputActor(m); diff --git a/core/opengate_core/opengate_lib/GateGenericSource.cpp b/core/opengate_core/opengate_lib/GateGenericSource.cpp index 88748a25d..74d7e743a 100644 --- a/core/opengate_core/opengate_lib/GateGenericSource.cpp +++ b/core/opengate_core/opengate_lib/GateGenericSource.cpp @@ -9,8 +9,16 @@ #include "G4IonTable.hh" #include "G4ParticleTable.hh" #include "G4RandomTools.hh" +#include "GateHelpers.h" #include "GateHelpersDict.h" +#include "fmt/color.h" +#include "fmt/core.h" #include +#include +#include +#include +#include +#include GateGenericSource::GateGenericSource() : GateVSource() { fInitGenericIon = false; @@ -143,13 +151,14 @@ double GateGenericSource::PrepareNextTime(double current_simulation_time) { if (fMaxN <= 0) { if (fEffectiveEventTime < fStartTime) return fStartTime; - if (fEffectiveEventTime >= fEndTime) + if (fEffectiveEventTime >= fEndTime){ return -1; - + } // get next time according to current fActivity double next_time = CalcNextTime(fEffectiveEventTime); - if (next_time >= fEndTime) + if (next_time >= fEndTime){ return -1; + } return next_time; } @@ -510,41 +519,105 @@ void GateGenericSource::InitializeEnergy(py::dict puser_info) { fInitialActivity = fActivity; } - if (ene_type == "spectrum_lines") { - ene->SetEnergyDisType("spectrum_lines"); - auto w = DictGetVecDouble(user_info, "spectrum_weight"); - auto e = DictGetVecDouble(user_info, "spectrum_energy"); - auto total = 0.0; - for (double ww : w) - total += ww; - // normalize to total - for (unsigned long i = 0; i < w.size(); i++) { - w[i] = w[i] / total; + if (ene_type == "spectrum_discrete") { // TODO rename + auto weights = DictGetVecDouble(user_info, "spectrum_weights"); + auto energies = DictGetVecDouble(user_info, "spectrum_energies"); + + if (weights.empty()) + Fatal("The weights for " + fName + " is zero length. Abort"); + if (energies.empty()) + Fatal("The energies for " + fName + " is zero length. Abort"); + if (weights.size() != energies.size()) { + auto const errorMessage = + fmt::format("For {}, the spectrum vectors weights and energies" + " must have the same size ({} โ‰  {})", + fName, weights.size(), energies.size()); + Fatal(errorMessage); } + // cumulated weights - for (unsigned long i = 1; i < w.size(); i++) { - w[i] += w[i - 1]; - } - ene->fEnergyCDF = e; - ene->fProbabilityCDF = w; - if (ene->fEnergyCDF.empty() || ene->fProbabilityCDF.empty()) { - std::ostringstream oss; - oss << "The spectrum lines for source " << fName - << " is zero length. Abort"; - Fatal(oss.str()); - } - if (ene->fEnergyCDF.size() != ene->fProbabilityCDF.size()) { - std::ostringstream oss; - oss << "The spectrum vector energy and proba for source " << fName - << " must have the same length, while there are " - << ene->fEnergyCDF.size() << " and " << ene->fProbabilityCDF.size(); - Fatal(oss.str()); + std::partial_sum(std::begin(weights), std::end(weights), + std::begin(weights)); + auto const weightsSum = weights.back(); + + // normalize weights to total + for (auto &weight : weights) + weight /= weightsSum; + + // ! important ! + // Modify the activity according to the total sum of weights because we + // normalize the weights + fActivity *= weightsSum; + fInitialActivity = fActivity; + + ene->SetEnergyDisType(ene_type); + ene->SetEmin(energies.front()); + ene->SetEmax(energies.back()); + ene->fEnergyCDF = energies; + ene->fProbabilityCDF = weights; + } + + if (ene_type == "spectrum_histogram") { + auto weights = DictGetVecDouble(user_info, "spectrum_weights"); + auto energy_bin_edges = + DictGetVecDouble(user_info, "spectrum_energy_bin_edges"); + auto interpolation = + DictGetStr(user_info, "spectrum_histogram_interpolation"); + + if (weights.empty()) + Fatal("The weights for " + fName + " is zero length. Abort"); + if (energy_bin_edges.empty()) + Fatal("The energy_bin_edges for " + fName + " is zero length. Abort"); + if ((weights.size() + 1) != energy_bin_edges.size()) { + auto const errorMessage = fmt::format( + "For {}, the spectrum vector energy_bin_edges must have exactly one" + " more element than the vector weights ({} โ‰  {} + 1)", + fName, energy_bin_edges.size(), weights.size()); + Fatal(errorMessage); } + + if (interpolation == "None" || interpolation == "none") { + double accumulatedWeights = 0; + for (std::size_t i = 0; i < weights.size(); ++i) { + auto const diffEnergy = energy_bin_edges[i + 1] - energy_bin_edges[i]; + accumulatedWeights += weights[i] * diffEnergy; + weights[i] = accumulatedWeights; + } + } else if (interpolation == "linear") { + double accumulatedWeights = 0; + for (std::size_t i = 0; i < weights.size(); i++) { + auto const diffEnergy = energy_bin_edges[i + 1] - energy_bin_edges[i]; + auto const diffWeight = weights[i + 1] - weights[i]; + accumulatedWeights += + diffEnergy * weights[i] - 0.5 * diffEnergy * diffWeight; + weights[i] = accumulatedWeights; + } + } else + Fatal("For " + fName + + ", invalid spectrum interpolation type: " + interpolation); + + auto const weightsSum = weights.back(); + + // normalize weights to total + for (auto &weight : weights) + weight /= weightsSum; + // ! important ! // Modify the activity according to the total sum of weights because we // normalize the weights - fActivity = fActivity * total; + fActivity *= weightsSum; fInitialActivity = fActivity; + + std::string interpolation_str = ""; + if (interpolation != "None" && interpolation != "none") + interpolation_str = + (interpolation != "none" ? ("_" + interpolation) : ""); + + ene->SetEnergyDisType(ene_type + interpolation_str); + ene->SetEmin(energy_bin_edges.front()); + ene->SetEmax(energy_bin_edges.back()); + ene->fEnergyCDF = energy_bin_edges; + ene->fProbabilityCDF = weights; } if (ene_type == "F18_analytic") { diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp new file mode 100644 index 000000000..3d4e345e0 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.cpp @@ -0,0 +1,707 @@ +// +// ******************************************************************** +// * 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 GateLastVertexInteractionSplittingActor.cc +/// \brief Implementation of the GateLastVertexInteractionSplittingActor class + +#include "GateHelpersDict.h" +#include "GateHelpersImage.h" + +#include "CLHEP/Units/SystemOfUnits.h" +#include "G4BiasingProcessInterface.hh" +#include "G4Gamma.hh" +#include "G4LogicalVolumeStore.hh" +#include "G4ParticleTable.hh" +#include "G4PhysicalVolumeStore.hh" +#include "G4Positron.hh" +#include "G4ProcessManager.hh" +#include "G4ProcessVector.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "GateLastVertexInteractionSplittingActor.h" +#include "GateLastVertexSplittingPostStepDoIt.h" +#include "GateOptnComptSplitting.h" +#include "GateLastVertexSource.h" +#include "G4RunManager.hh" +#include + + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateLastVertexInteractionSplittingActor:: + GateLastVertexInteractionSplittingActor(py::dict &user_info) + : GateVActor(user_info, false) { + + + +} + + +void GateLastVertexInteractionSplittingActor::InitializeUserInput(py::dict &user_info) { +GateVActor::InitializeUserInput(user_info); +fMotherVolumeName = DictGetStr(user_info, "attached_to"); +fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); +fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); +fAngularKill = DictGetBool(user_info, "angular_kill"); +fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); +fMaxTheta = DictGetDouble(user_info, "max_theta"); +fBatchSize = DictGetDouble(user_info, "batch_size"); +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +void GateLastVertexInteractionSplittingActor::print_tree(const tree& tr, tree::pre_order_iterator it, tree::pre_order_iterator end) + { + if(!tr.is_valid(it)) return; + int rootdepth=tr.depth(it); + std::cout << "-----" << std::endl; + while(it!=end) { + for(int i=0; iFindParticle(particleName); + G4ProcessManager *processManager = particleDefinition->GetProcessManager(); + G4ProcessVector *processList = processManager->GetProcessList(); + G4VProcess* nullProcess = nullptr; + for (size_t i = 0; i < processList->size(); ++i) { + auto process = (*processList)[i]; + if (process->GetProcessName() == pName) { + return process; + } + } + return nullProcess; + +} + +G4Track* GateLastVertexInteractionSplittingActor::CreateATrackFromContainer(LastVertexDataContainer theContainer){ + + auto *particle_table = G4ParticleTable::GetParticleTable(); + SimpleContainer container = theContainer.GetContainerToSplit(); + if (container.GetParticleNameToSplit() != "None"){ + G4ParticleDefinition *particleDefinition = particle_table->FindParticle(container.GetParticleNameToSplit()); + G4ThreeVector momentum = container.GetMomentum(); + G4double energy = container.GetEnergy(); + if (energy <0){ + energy = 0; + momentum = {0,0,0}; + } + G4int trackStatus = container.GetTrackStatus(); + G4ThreeVector position = container.GetVertexPosition(); + G4ThreeVector polarization = container.GetPolarization(); + G4DynamicParticle* dynamicParticle = new G4DynamicParticle(particleDefinition,momentum,energy); + G4double time = 0; + G4Track* aTrack = new G4Track(dynamicParticle,time, position); + aTrack->SetPolarization(polarization); + if (trackStatus == 0){ + aTrack->SetTrackStatus(fAlive); + } + if (trackStatus == 1){ + aTrack->SetTrackStatus(fStopButAlive); + } + if ((trackStatus == 2) || (trackStatus == 3)){ + aTrack->SetTrackStatus(fAlive); + } + aTrack->SetWeight(container.GetWeight()); + return aTrack; + } + + return nullptr; + + +} + + +G4Track *GateLastVertexInteractionSplittingActor::CreateComptonTrack(G4ParticleChangeForGamma *gammaProcess, G4Track track, G4double weight) { + + G4double energy = gammaProcess->GetProposedKineticEnergy(); + G4double globalTime = track.GetGlobalTime(); + G4ThreeVector polarization = gammaProcess->GetProposedPolarization(); + const G4ThreeVector momentum = gammaProcess->GetProposedMomentumDirection(); + const G4ThreeVector position = track.GetPosition(); + G4Track *newTrack = new G4Track(track); + + newTrack->SetKineticEnergy(energy); + newTrack->SetMomentumDirection(momentum); + newTrack->SetPosition(position); + newTrack->SetPolarization(polarization); + newTrack->SetWeight(weight); + return newTrack; +} + +void GateLastVertexInteractionSplittingActor::ComptonSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process, LastVertexDataContainer container, G4double batchSize) { + + //G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + GateGammaEmPostStepDoIt *emProcess = (GateGammaEmPostStepDoIt *)process; + for (int i = 0; i < batchSize; i++){ + G4VParticleChange *processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); + G4ParticleChangeForGamma *gammaProcessFinalState = (G4ParticleChangeForGamma *)processFinalState; + + G4ThreeVector momentum = gammaProcessFinalState->GetProposedMomentumDirection(); + + G4Track *newTrack = CreateComptonTrack(gammaProcessFinalState, *fTrackToSplit, fWeight); + + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(newTrack->GetMomentumDirection(),fVectorDirector) == false)){ + delete newTrack; + } + else{ + fStackManager->PushOneTrack(newTrack); + } + + + // Special case here, since we generate independently each particle, we will not attach an electron to exiting compton photon, but we will the secondaries. + + + if (processFinalState->GetNumberOfSecondaries()> 0){ + delete processFinalState->GetSecondary(0); + } + + + processFinalState->Clear(); + gammaProcessFinalState->Clear(); + } +} + + + + +G4Track GateLastVertexInteractionSplittingActor::eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process){ + //It seem's that the the along step method apply only to brem results to no deposited energy but a change in momentum direction according to the process + //Whereas the along step method applied to the ionisation well change the deposited energy but not the momentum. Then I apply both to have a correct + //momentum and deposited energy before the brem effect. + G4String particleName = track->GetDefinition()->GetParticleName(); + G4VProcess* eIoniProcess = GetProcessFromProcessName(particleName, "eIoni"); + G4VProcess* eBremProcess = GetProcessFromProcessName(particleName, "eBrem"); + G4VParticleChange* eIoniProcessAlongState = eIoniProcess->AlongStepDoIt(*track, *step); + G4VParticleChange* eBremProcessAlongState = eBremProcess->AlongStepDoIt(*track, *step); + G4ParticleChangeForLoss* eIoniProcessAlongStateForLoss = (G4ParticleChangeForLoss*) eIoniProcessAlongState; + G4ParticleChangeForLoss* eBremProcessAlongStateForLoss = (G4ParticleChangeForLoss*) eBremProcessAlongState; + G4double LossEnergy = eIoniProcessAlongStateForLoss->GetLocalEnergyDeposit(); + G4ThreeVector momentum = eBremProcessAlongStateForLoss->GetProposedMomentumDirection(); + G4ThreeVector polarization = eBremProcessAlongStateForLoss->GetProposedPolarization(); + G4Track aTrack = G4Track(*track); + + aTrack.SetKineticEnergy(track->GetKineticEnergy() - LossEnergy); + aTrack.SetMomentumDirection(momentum); + aTrack.SetPolarization(polarization); + eIoniProcessAlongState->Clear(); + eBremProcessAlongState->Clear(); + return aTrack; + +} + + +void GateLastVertexInteractionSplittingActor::SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer theContainer, G4double batchSize) { + SimpleContainer container = theContainer.GetContainerToSplit(); + G4String particleName = fTrackToSplit->GetParticleDefinition()->GetParticleName(); + //G4TrackVector *trackVector = CurrentStep->GetfSecondary(); + + G4VParticleChange *processFinalState = nullptr; + GateBremPostStepDoIt* bremProcess = nullptr; + GateGammaEmPostStepDoIt *emProcess = nullptr; + GateplusannihilAtRestDoIt *eplusAnnihilProcess = nullptr; + + if ((container.GetAnnihilationFlag() == "PostStep") && (fTrackToSplit->GetKineticEnergy() > 0)){ + emProcess = (GateGammaEmPostStepDoIt *)process; + } + if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { + eplusAnnihilProcess = (GateplusannihilAtRestDoIt *)process; + } + for (int j = 0; j < batchSize;j++){ + G4int NbOfSecondaries = 0; + + G4int count =0; + while (NbOfSecondaries == 0){ + if (process->GetProcessName() == "eBrem") { + G4Track aTrack = eBremProcessFinalState(fTrackToSplit,initStep,process); + bremProcess = (GateBremPostStepDoIt*) process; + processFinalState = bremProcess->GateBremPostStepDoIt::PostStepDoIt(aTrack, *initStep); + } + else { + if ((container.GetAnnihilationFlag() == "PostStep") && (fTrackToSplit->GetKineticEnergy() > 0)){ + processFinalState = emProcess->PostStepDoIt(*fTrackToSplit, *initStep); + } + if ((container.GetAnnihilationFlag() == "AtRest") || (fTrackToSplit->GetKineticEnergy() == 0)) { + processFinalState = eplusAnnihilProcess->GateplusannihilAtRestDoIt::AtRestDoIt(*fTrackToSplit,*initStep); + } + } + NbOfSecondaries = processFinalState->GetNumberOfSecondaries(); + if (NbOfSecondaries == 0){ + processFinalState->Clear(); + } + count ++; + //Security break, in case of infinite loop + if (count > 10000){ + G4ExceptionDescription ed; + ed << " infinite loop detected during the track creation for the " <GetProcessName() <<" process"<AbortEvent(); + break; + } + } + + G4int idx = 0; + G4bool IsPushBack =false; + for (int i=0; i < NbOfSecondaries; i++){ + G4Track *newTrack = processFinalState->GetSecondary(i); + G4ThreeVector momentum = newTrack->GetMomentumDirection(); + + if (!(isnan(momentum[0]))){ + if ((fAngularKill) && (DoesParticleEmittedInSolidAngle(momentum,fVectorDirector) == false)){ + delete newTrack; + } + else if (IsPushBack == true){ + delete newTrack; + } + else { + newTrack->SetWeight(fWeight); + newTrack->SetCreatorProcess(process); + //trackVector->emplace_back(newTrack); + fStackManager->PushOneTrack(newTrack); + //delete newTrack; + IsPushBack=true; + + } + } + else { + delete newTrack; + } + } + processFinalState->Clear(); + } +} + + +void GateLastVertexInteractionSplittingActor::CreateNewParticleAtTheLastVertex(G4Step* initStep,G4Step *step,LastVertexDataContainer theContainer, G4double batchSize) { + // We retrieve the process associated to the process name to split and we + // split according the process. Since for compton scattering, the gamma is not + // a secondary particles, this one need to have his own splitting function. + + G4String processName = fProcessNameToSplit; + G4int nbOfTrackAlreadyInStack = fStackManager->GetNTotalTrack(); + if ((fProcessToSplit == 0) || (fProcessToSplit == nullptr)){ + SimpleContainer container = theContainer.GetContainerToSplit(); + fProcessToSplit = GetProcessFromProcessName(container.GetParticleNameToSplit(),processName); + } + + if (processName == "compt") { + ComptonSplitting(initStep,step, fProcessToSplit, theContainer, batchSize); + } + + else if((processName != "msc") && (processName != "conv")){ + SecondariesSplitting(initStep, step, fProcessToSplit, theContainer,batchSize); + } + fNumberOfTrackToSimulate = fStackManager->GetNTotalTrack() - nbOfTrackAlreadyInStack; + fNbOfBatchForExitingParticle ++; + if (fNbOfBatchForExitingParticle >500){ + fStackManager->clear(); + } + //stackManager->clear(); + +} + + +void GateLastVertexInteractionSplittingActor::CreateListOfbiasedVolume(G4LogicalVolume *volume) { + G4int nbOfDaughters = volume->GetNoDaughters(); + if (nbOfDaughters > 0) { + for (int i = 0; i < nbOfDaughters; i++) { + G4String LogicalVolumeName = volume->GetDaughter(i)->GetLogicalVolume()->GetName(); + G4LogicalVolume *logicalDaughtersVolume = volume->GetDaughter(i)->GetLogicalVolume(); + if (!(std::find(fListOfBiasedVolume.begin(),fListOfBiasedVolume.end(),LogicalVolumeName) != fListOfBiasedVolume.end())) + fListOfBiasedVolume.push_back(volume->GetDaughter(i)->GetLogicalVolume()->GetName()); + CreateListOfbiasedVolume(logicalDaughtersVolume); + } + } +} + + +void GateLastVertexInteractionSplittingActor::FillOfDataTree(G4Step*step){ + + G4String processName = "None"; + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) + processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + + G4String creatorProcessName = "None"; + if (step->GetTrack()->GetCreatorProcess() != 0) + creatorProcessName =step->GetTrack()->GetCreatorProcess()->GetProcessName(); + + if ((step->GetTrack()->GetParticleDefinition()->GetParticleName() == "e+") && + ((step->GetTrack()->GetTrackStatus() == 1) || + (step->GetTrack()->GetTrackStatus() == 2))) { + processName = "annihil"; + } + + G4String annihilFlag = "None"; + if (processName == "annihil"){ + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0){ + if (processName == step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ + annihilFlag = "PostStep"; + } + else if (processName != step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName()){ + annihilFlag ="AtRest"; + } + } + } + + + + if (fIsFirstStep){ + LastVertexDataContainer newContainer = LastVertexDataContainer(); + newContainer.SetTrackID(step->GetTrack()->GetTrackID()); + newContainer.SetParticleName(step->GetTrack()->GetDefinition()->GetParticleName()); + newContainer.SetCreationProcessName(creatorProcessName); + + + + if (fTree.empty()){ + fTree.set_head(newContainer); + } + + for (auto it = fTree.begin_post(); it != fTree.end_post(); ++it){ + LastVertexDataContainer container = *it; + G4int trackID = container.GetTrackID(); + + if (step->GetTrack()->GetParentID() == trackID){ + newContainer = container.ContainerFromParentInformation(step); + fTree.append_child(it,newContainer); + break; + + } + } + + for (auto it = fTree.begin_post(); it != fTree.end_post(); ++it){ + LastVertexDataContainer container = *it; + G4int trackID = container.GetTrackID(); + if (step->GetTrack()->GetTrackID() == trackID){ + fIterator = it; + break; + } + } + } + + + + LastVertexDataContainer* container = &(*fIterator); + G4int trackID = container->GetTrackID(); + if ((processName != "Transportation") &&(processName !="None") && (processName !="Rayl")){ + if (step->GetTrack()->GetTrackID() == trackID){ + G4ThreeVector position = step->GetTrack()->GetPosition(); + G4ThreeVector prePosition = step->GetPreStepPoint()->GetPosition(); + G4ThreeVector momentum; + if ((processName == "annihil")) + momentum = step->GetPostStepPoint()->GetMomentumDirection(); + else{ + momentum = step->GetPreStepPoint()->GetMomentumDirection(); + } + G4ThreeVector polarization = step->GetPreStepPoint()->GetPolarization(); + G4String particleName = step->GetTrack()->GetDefinition()->GetParticleName(); + G4double energy = step->GetPreStepPoint()->GetKineticEnergy(); + G4double weight = step->GetTrack()->GetWeight(); + G4int trackStatus = step->GetTrack()->GetTrackStatus(); + G4int nbOfSecondaries = step->GetfSecondary()->size(); + G4double stepLength = step->GetStepLength(); + if (((processName == "annihil"))){ + energy -= (step->GetTotalEnergyDeposit()); + } + SimpleContainer containerToSplit = SimpleContainer(processName,energy, momentum, position,polarization,particleName,weight,trackStatus,nbOfSecondaries,annihilFlag,stepLength,prePosition); + container->SetContainerToSplit(containerToSplit); + container->PushListOfSplittingParameters(containerToSplit); + + } + } + + +} + + + +G4bool GateLastVertexInteractionSplittingActor::IsParticleExitTheBiasedVolume(G4Step*step){ + + + if ((step->GetPostStepPoint()->GetStepStatus() == 1)) { + G4String logicalVolumeNamePostStep = "None"; + if (step->GetPostStepPoint()->GetPhysicalVolume() != 0) + logicalVolumeNamePostStep = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + + if (std::find(fListOfVolumeAncestor.begin(), fListOfVolumeAncestor.end(), logicalVolumeNamePostStep) != fListOfVolumeAncestor.end()){ + return true; + } + /* + else if (std::find(fListOfBiasedVolume.begin(), fListOfBiasedVolume.end(), logicalVolumeNamePostStep) != fListOfBiasedVolume.end()) { + return false; + } + */ + } + if (step->GetPostStepPoint()->GetStepStatus() == 0) + return true; + return false; +} + + + +G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesAProcess(G4Step* step){ + G4String processName = "None"; + G4String particleName = step->GetTrack()->GetParticleDefinition()->GetParticleName(); + if (step->GetPostStepPoint()->GetProcessDefinedStep() != 0) + processName = step->GetPostStepPoint()->GetProcessDefinedStep()->GetProcessName(); + + if (std::find(fListOfProcessesAccordingParticles[particleName].begin(), fListOfProcessesAccordingParticles[particleName].end(), processName) != fListOfProcessesAccordingParticles[particleName].end()){ + return true; + } + return false; + + +} + +G4bool GateLastVertexInteractionSplittingActor::IsTheParticleUndergoesALossEnergyProcess(G4Step* step){ + if (step->GetPostStepPoint()->GetKineticEnergy() - step->GetPreStepPoint()->GetKineticEnergy() != 0) + return true; + return false; + + +} + + + +void GateLastVertexInteractionSplittingActor::BeginOfRunAction( + const G4Run *run) { + + fListOfProcessesAccordingParticles["gamma"] = {"compt","phot","conv"}; + fListOfProcessesAccordingParticles["e-"] = {"eBrem","eIoni","msc"}; + fListOfProcessesAccordingParticles["e+"] = {"eBrem","eIoni","msc","annihil"}; + + std::cout<GetVolume(fMotherVolumeName); + fListOfBiasedVolume.push_back(biasingVolume->GetName()); + CreateListOfbiasedVolume(biasingVolume); + + auto* source = fSourceManager->FindSourceByName("source_vertex"); + fVertexSource = (GateLastVertexSource* ) source; + + fCosMaxTheta = std::cos(fMaxTheta); + fStackManager = G4EventManager::GetEventManager()->GetStackManager(); + + if (fRotationVectorDirector) { + G4VPhysicalVolume *physBiasingVolume = + G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume->GetObjectRotationValue(); + fVectorDirector = rot * fVectorDirector; + } +} + +void GateLastVertexInteractionSplittingActor::BeginOfEventAction( + const G4Event *event) { + fEventID = event->GetEventID(); + fIsAnnihilAlreadySplit = false; + fNbOfBatchForExitingParticle = 0; + if (fEventID%50000 == 0) + std::cout<<"event ID : "<FindSourceByName(fActiveSource); + GateLastVertexSource *vertexSource = (GateLastVertexSource*) source; + fContainer = vertexSource->GetLastVertexContainer(); + fProcessNameToSplit = vertexSource->GetProcessToSplit(); + if (fProcessToSplit !=0){ + fProcessToSplit = nullptr; + } + if (fTrackToSplit !=0){ + delete fTrackToSplit; + fTrackToSplit = nullptr; + } + fTrackToSplit = CreateATrackFromContainer(fContainer); + if (fTrackToSplit != 0) + fWeight = fTrackToSplit->GetWeight()/fSplittingFactor; + } + + +} + +void GateLastVertexInteractionSplittingActor::PreUserTrackingAction( + const G4Track *track) { + fToSplit =true; + fIsFirstStep = true; + +} + +void GateLastVertexInteractionSplittingActor::SteppingAction(G4Step *step) { + + + if (fActiveSource != "source_vertex"){ + FillOfDataTree(step); + + if (IsParticleExitTheBiasedVolume(step)){ + if ((fAngularKill == false) || ((fAngularKill == true) && (DoesParticleEmittedInSolidAngle(step->GetTrack()->GetMomentumDirection(),fVectorDirector) == true))){ + fListOfContainer.push_back((*fIterator)); + } + + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + + + } + + + + if (fOnlyTree == false){ + if (fActiveSource == "source_vertex"){ + + if (fIsFirstStep){ + fTrackID = step->GetTrack()->GetTrackID(); + fEkin = step->GetPostStepPoint()->GetKineticEnergy(); + } + else{ + if ((fTrackID == step->GetTrack()->GetTrackID()) && (fEkin != step->GetPreStepPoint()->GetKineticEnergy())){ + fToSplit =false; + } + else{ + fEkin = step->GetPostStepPoint()->GetKineticEnergy(); + } + + } + if (fToSplit) { + G4String creatorProcessName = "None"; + if (step->GetTrack()->GetCreatorProcess() != 0) + creatorProcessName =step->GetTrack()->GetCreatorProcess()->GetProcessName(); + if (((step->GetTrack()->GetParentID() == 0)&& (step->GetTrack()->GetTrackID() == 1))|| ((creatorProcessName == "annihil") && (step->GetTrack()->GetParentID() == 1))){ + + if ((fProcessNameToSplit != "annihil") || ((fProcessNameToSplit == "annihil")&& (fIsAnnihilAlreadySplit ==false))){ + + //FIXME : list of process which are not splitable yet + if ((fProcessNameToSplit != "msc") && (fProcessNameToSplit != "conv") && (fProcessNameToSplit != "eIoni")) { + fCopyInitStep = new G4Step(*step); + if (fProcessNameToSplit == "eBrem"){ + fCopyInitStep->SetStepLength(fContainer.GetContainerToSplit().GetStepLength()); + fCopyInitStep->GetPreStepPoint()->SetKineticEnergy(fContainer.GetContainerToSplit().GetEnergy()); + + } + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer, fBatchSize); + } + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + + if (fProcessNameToSplit == "annihil"){ + fIsAnnihilAlreadySplit = true; + } + } + + + else if ((fProcessNameToSplit == "annihil")&& (fIsAnnihilAlreadySplit == true)){ + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + } + + } + + else { + if (fIsFirstStep){ + fNumberOfTrackToSimulate --; + if (fKilledBecauseOfProcess == false){ + fSplitCounter += 1; + } + else { + fKilledBecauseOfProcess = false; + } + + if (fSplitCounter > fSplittingFactor){ + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + fStackManager->clear(); + } + } + + if (IsTheParticleUndergoesALossEnergyProcess(step)){ + step->GetTrack()->SetTrackStatus(fKillTrackAndSecondaries); + fKilledBecauseOfProcess = true; + } + + if (fIsFirstStep){ + if (fSplitCounter <= fSplittingFactor){ + if (fNumberOfTrackToSimulate == 0){ + CreateNewParticleAtTheLastVertex(fCopyInitStep,step,fContainer,(fSplittingFactor - fSplitCounter +1)/fSplittingFactor * fBatchSize); + } + } + } + } + } + } + } + + fIsFirstStep = false; + + + + +} + + +void GateLastVertexInteractionSplittingActor::EndOfEventAction( + const G4Event* event) { + + if (fActiveSource != "source_vertex"){ + + //print_tree(fTree,fTree.begin(),fTree.end()); + fVertexSource->SetNumberOfEventToSimulate(fListOfContainer.size()); + fVertexSource->SetNumberOfGeneratedEvent(0); + fVertexSource->SetListOfVertexToSimulate(fListOfContainer); + fTree.clear(); + fListOfContainer.clear(); + } + + if (fOnlyTree == false){ + + auto* source = fSourceManager->FindSourceByName("source_vertex"); + GateLastVertexSource* vertexSource = (GateLastVertexSource*) source; + if (vertexSource->GetNumberOfGeneratedEvent() < vertexSource->GetNumberOfEventToSimulate()){ + fSourceManager->SetActiveSourcebyName("source_vertex"); + } + fActiveSource = fSourceManager->GetActiveSourceName(); + } + + } + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h new file mode 100644 index 000000000..dfe0ee850 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexInteractionSplittingActor.h @@ -0,0 +1,137 @@ +// +// ******************************************************************** +// * 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 GateLastVertexInteractionSplittingActor.h +/// \brief Definition of the GateLastVertexInteractionSplittingActor class +#ifndef GateLastVertexInteractionSplittingActor_h +#define GateLastVertexInteractionSplittingActor_h 1 + +#include "G4ParticleChangeForGamma.hh" +#include "G4VEnergyLossProcess.hh" +#include "GateVActor.h" +#include +#include +#include "GateLastVertexSplittingDataContainer.h" +#include "tree.hh" +#include "tree_util.hh" +#include +#include "GateLastVertexSource.h" +#include "CLHEP/Vector/ThreeVector.h" +#include "G4StackManager.hh" +using CLHEP::Hep3Vector; + +namespace py = pybind11; + +class GateLastVertexInteractionSplittingActor : public GateVActor { +public: + GateLastVertexInteractionSplittingActor(py::dict &user_info); + virtual ~GateLastVertexInteractionSplittingActor() {} + + G4double fSplittingFactor; + G4bool fAngularKill; + G4bool fRotationVectorDirector; + G4ThreeVector fVectorDirector; + G4double fMaxTheta; + G4double fCosMaxTheta; + G4int fTrackIDOfSplittedTrack = 0; + G4int fParentID = -1; + G4int fEventID; + G4int fEventIDOfSplittedTrack; + G4int fEventIDOfInitialSplittedTrack; + G4int fTrackID; + G4double fEkin; + G4int fTrackIDOfInitialSplittedTrack = 0; + G4int ftmpTrackID; + G4bool fIsFirstStep = true; + G4bool fSuspendForAnnihil = false; + G4double fWeightOfEnteringParticle = 0; + G4double fSplitCounter = 0; + G4bool fToSplit = true; + G4String fActiveSource = "None"; + G4bool fIsAnnihilAlreadySplit =false; + G4int fCounter; + G4bool fKilledBecauseOfProcess = false; + G4bool fFirstSplittedPart = true; + G4bool fOnlyTree = false; + G4double fWeight; + G4double fBatchSize; + G4int fNumberOfTrackToSimulate = 0; + G4int fNbOfBatchForExitingParticle=0; + G4int fTracksCounts=0; + GateLastVertexSource* fVertexSource = nullptr; + tree fTree; + tree::post_order_iterator fIterator; + std::vector fListOfContainer; + G4StackManager* fStackManager = nullptr; + + + + G4Track *fTrackToSplit = nullptr; + G4Step* fCopyInitStep = nullptr; + G4String fProcessNameToSplit; + G4VProcess* fProcessToSplit; + LastVertexDataContainer fContainer; + + std::vector fTracksToPostpone; + std::map> fListOfProcessesAccordingParticles; + std::map fDataMap; + + + + std::vector fListOfVolumeAncestor; + std::vector fListOfBiasedVolume; + + std::vector fListOfProcesses = {"compt", "annihil", "eBrem", "conv", + "phot"}; + + void InitializeUserInput(py::dict &user_info) override; + virtual void SteppingAction(G4Step *) override; + virtual void BeginOfEventAction(const G4Event *) override; + virtual void EndOfEventAction(const G4Event *) override; + virtual void BeginOfRunAction(const G4Run *run) override; + virtual void PreUserTrackingAction(const G4Track *track) override; + + // Pure splitting functions + G4bool DoesParticleEmittedInSolidAngle(G4ThreeVector dir, G4ThreeVector vectorDirector); + G4Track *CreateComptonTrack(G4ParticleChangeForGamma *, G4Track, G4double); + void ComptonSplitting(G4Step* initStep,G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container, G4double batchSize); + void SecondariesSplitting(G4Step* initStep, G4Step *CurrentStep,G4VProcess *process,LastVertexDataContainer container, G4double batchSize); + + void CreateNewParticleAtTheLastVertex(G4Step*init,G4Step *current, LastVertexDataContainer, G4double batchSize); + G4Track* CreateATrackFromContainer(LastVertexDataContainer container); + G4bool IsTheParticleUndergoesAProcess(G4Step* step); + G4bool IsTheParticleUndergoesALossEnergyProcess(G4Step* step); + G4VProcess* GetProcessFromProcessName(G4String particleName, G4String pName); + G4Track eBremProcessFinalState(G4Track* track, G4Step* step,G4VProcess *process); + + + void FillOfDataTree(G4Step *step); + G4bool IsParticleExitTheBiasedVolume(G4Step*step); + void CreateListOfbiasedVolume(G4LogicalVolume *volume); + void print_tree(const tree& tr, tree::pre_order_iterator it, tree::pre_order_iterator end); +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateLastVertexSource.cpp b/core/opengate_core/opengate_lib/GateLastVertexSource.cpp new file mode 100644 index 000000000..b903d90c8 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSource.cpp @@ -0,0 +1,112 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include "GateLastVertexSource.h" +#include "G4ParticleTable.hh" +#include "GateHelpersDict.h" +#include + +GateLastVertexSource::GateLastVertexSource() : GateVSource() { +} + +GateLastVertexSource::~GateLastVertexSource() { +} + +void GateLastVertexSource::InitializeUserInfo(py::dict &user_info) { + GateVSource::InitializeUserInfo(user_info); + // get user info about activity or nb of events + fN = DictGetInt(user_info, "n"); + } + +double GateLastVertexSource::PrepareNextTime(double current_simulation_time) { + + /* + // If all N events have been generated, we stop (negative time) + if (fNumberOfGeneratedEvents >= fN){ + std::cout << "LV: "<< -1<<" "<< fNumberOfGeneratedEvents <<" "<< fN<= fN) + return -1; + + return fStartTime + 1; +} + +void GateLastVertexSource::PrepareNextRun() { + // The following compute the global transformation from + // the local volume (mother) to the world + GateVSource::PrepareNextRun(); + + // The global transformation to apply for the current RUN is known in : + // fGlobalTranslation & fGlobalRotation + + // init the number of generated events (here, for each run) + fNumberOfGeneratedEvents = 0; + +} + +void GateLastVertexSource::GenerateOnePrimary(G4Event* event, double current_simulation_time, G4int idx ){ + + if (fNumberOfGeneratedEvents >= fN){ + auto *particle_table = G4ParticleTable::GetParticleTable(); + auto *fParticleDefinition = particle_table->FindParticle("geantino"); + auto *particle = new G4PrimaryParticle(fParticleDefinition); + particle->SetKineticEnergy(0); + particle->SetMomentumDirection({1,0,0}); + particle->SetWeight(1); + auto *vertex = new G4PrimaryVertex({0,0,0}, current_simulation_time); + vertex->SetPrimary(particle); + event->AddPrimaryVertex(vertex); + } + else { + + SimpleContainer containerToSplit = fListOfContainer[idx].GetContainerToSplit(); + G4double energy = containerToSplit.GetEnergy(); + if (energy < 0){ + energy = 0; + } + fContainer = fListOfContainer[idx]; + G4ThreeVector position = containerToSplit.GetVertexPosition(); + G4ThreeVector momentum = containerToSplit.GetMomentum(); + G4String particleName = containerToSplit.GetParticleNameToSplit(); + G4double weight =containerToSplit.GetWeight(); + fProcessToSplit = containerToSplit.GetProcessNameToSplit(); + + auto &l = fThreadLocalData.Get(); + auto *particle_table = G4ParticleTable::GetParticleTable(); + auto *fParticleDefinition = particle_table->FindParticle(particleName); + auto *particle = new G4PrimaryParticle(fParticleDefinition); + particle->SetKineticEnergy(energy); + particle->SetMomentumDirection(momentum); + particle->SetWeight(weight); + auto *vertex = new G4PrimaryVertex(position, current_simulation_time); + vertex->SetPrimary(particle); + event->AddPrimaryVertex(vertex); + } + +} + + +void GateLastVertexSource::GeneratePrimaries(G4Event *event, + double current_simulation_time) { + + GenerateOnePrimary(event,current_simulation_time,fNumberOfGeneratedEvents); + fNumberOfGeneratedEvents++; + if (fNumberOfGeneratedEvents == fListOfContainer.size()){ + fListOfContainer.clear(); + } +} diff --git a/core/opengate_core/opengate_lib/GateLastVertexSource.h b/core/opengate_core/opengate_lib/GateLastVertexSource.h new file mode 100644 index 000000000..dd23c124d --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSource.h @@ -0,0 +1,85 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#ifndef GateLastVertexSource_h +#define GateLastVertexSource_h + +#include "GateAcceptanceAngleTesterManager.h" +#include "GateSingleParticleSource.h" +#include "GateVSource.h" +#include "GateLastVertexSplittingDataContainer.h" +#include + +namespace py = pybind11; + +/* + This is NOT a real source type but a template to help writing your own source + type. Copy-paste this file with a different name ("MyNewSource.hh") and start + building. You also need to copy : GateLastVertexSource.hh GateLastVertexSource.cpp + pyGateLastVertexSource.cpp + And add the source declaration in opengate_core.cpp + */ + +class GateLastVertexSource : public GateVSource { + +public: + GateLastVertexSource(); + + ~GateLastVertexSource() override; + + void InitializeUserInfo(py::dict &user_info) override; + + double PrepareNextTime(double current_simulation_time) override; + + void PrepareNextRun() override; + + void GeneratePrimaries(G4Event *event, double time) override; + + + void GenerateOnePrimary(G4Event *event, double time,G4int idx); + + + void SetListOfVertexToSimulate(std::vector list){ + fListOfContainer = list; + } + + void SetNumberOfGeneratedEvent(G4int nbEvent){ + fNumberOfGeneratedEvents = nbEvent; + } + + void SetNumberOfEventToSimulate(G4int N){ + fN = N; + } + + G4int GetNumberOfEventToSimulate(){ + return fN; + } + + G4int GetNumberOfGeneratedEvent(){ + return fNumberOfGeneratedEvents; + } + + G4String GetProcessToSplit(){ + return fProcessToSplit; + } + + LastVertexDataContainer GetLastVertexContainer(){ + return fContainer; + } + + +protected: + G4int fNumberOfGeneratedEvents = 0; + G4int fN = 0; + double fFloatValue; + std::vector fVectorValue; + std::vector fListOfContainer; + G4String fProcessToSplit = "None"; + LastVertexDataContainer fContainer; +}; + +#endif // GateLastVertexSource_h diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h new file mode 100644 index 000000000..474d6cc84 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingDataContainer.h @@ -0,0 +1,165 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +/// +#ifndef LastVertexDataContainer_h +#define LastVertexDataContainer_h + + +#include +#include "G4VEnergyLossProcess.hh" +#include "G4Track.hh" +#include "G4VEmProcess.hh" +#include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "G4PhysicalConstants.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4Gamma.hh" +#include "G4Electron.hh" +#include "G4Positron.hh" +#include "G4eeToTwoGammaModel.hh" +#include "G4EmBiasingManager.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilationEntanglementClipBoard.hh" +#include "G4EmParameters.hh" +#include "G4PhysicsModelCatalog.hh" +#include "GateLastVertexSplittingSimpleContainer.h" + + +class LastVertexDataContainer{ + +public : + + +LastVertexDataContainer(){} + +~LastVertexDataContainer(){} + + + +void SetTrackID(G4int trackID ){ + fTrackID = trackID; +} + +G4int GetTrackID(){ + return fTrackID; +} + + +void SetParticleName(G4String name){ + fParticleName = name; +} + +G4String GetParticleName(){ + return fParticleName; +} + + +void SetCreationProcessName(G4String creationProcessName){ + fCreationProcessName = creationProcessName; +} + +G4String GetCreationProcessName(){ + return fCreationProcessName; +} + + +void SetContainerToSplit(SimpleContainer container){ + fContainerToSplit = container; +} + + +SimpleContainer GetContainerToSplit(){ + return fContainerToSplit; +} + + + +void PushListOfSplittingParameters(SimpleContainer container){ + fVectorOfContainerToSplit.emplace_back(container); +} + + + + +LastVertexDataContainer ContainerFromParentInformation(G4Step* step){ + LastVertexDataContainer aContainer = LastVertexDataContainer(); + + aContainer.fTrackID = step->GetTrack()->GetTrackID(); + aContainer.fParticleName = step->GetTrack()->GetDefinition()->GetParticleName(); + if (this->fContainerToSplit.GetProcessNameToSplit() != "None"){ + if (this->fVectorOfContainerToSplit.size() !=0){ + G4ThreeVector vertexPosition = step->GetTrack()->GetVertexPosition(); + for (int i =0;ifVectorOfContainerToSplit.size();i++){ + if (vertexPosition == this->fVectorOfContainerToSplit[i].GetVertexPosition()){ + SimpleContainer tmpContainer = this->fVectorOfContainerToSplit[i]; + //std::cout<<"1 "<fContainerToSplit; + //std::cout<<"2 "< fVectorOfContainerToSplit; + +}; + +#endif + + + + + + \ No newline at end of file diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h new file mode 100644 index 000000000..38a9dc2ea --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingPostStepDoIt.h @@ -0,0 +1,116 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +/// +#ifndef GateLastVertexSplittingPostStepDoIt_h +#define GateLastVertexSplittingPostStepDoIt_h + + +#include "G4VEnergyLossProcess.hh" +#include "G4VEmProcess.hh" +#include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "G4PhysicalConstants.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4Gamma.hh" +#include "G4Electron.hh" +#include "G4Positron.hh" +#include "G4eeToTwoGammaModel.hh" +#include "G4EmBiasingManager.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilationEntanglementClipBoard.hh" +#include "G4EmParameters.hh" +#include "G4PhysicsModelCatalog.hh" +#include + + + + +class GateBremPostStepDoIt : public G4VEnergyLossProcess { +public : + +GateBremPostStepDoIt(); + +~ GateBremPostStepDoIt(); + +virtual G4VParticleChange * PostStepDoIt (const G4Track & track, const G4Step & step) override +{ + const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); + currentCouple = couple; + G4VParticleChange* particleChange = G4VEnergyLossProcess::PostStepDoIt(track,step); + return particleChange; +} + +virtual G4VParticleChange * AlongStepDoIt (const G4Track & track, const G4Step & step) override +{ + G4VParticleChange* particleChange = G4VEnergyLossProcess::AlongStepDoIt(track,step); + return particleChange; +} + + +}; + + +class GateGammaEmPostStepDoIt : public G4VEmProcess { +public : + +GateGammaEmPostStepDoIt(); + +~ GateGammaEmPostStepDoIt(); + +virtual G4VParticleChange * PostStepDoIt(const G4Track & track, const G4Step & step) override +{ + const G4MaterialCutsCouple* couple = step.GetPreStepPoint()->GetMaterialCutsCouple(); + currentCouple = couple; + G4VParticleChange* particleChange = G4VEmProcess::PostStepDoIt(track,step); + return particleChange; +} + + +}; + +class GateplusannihilAtRestDoIt : public G4eplusAnnihilation { +public : + +GateplusannihilAtRestDoIt(); +~ GateplusannihilAtRestDoIt(); + +virtual G4VParticleChange* AtRestDoIt(const G4Track& track, + const G4Step& step) override +// Performs the e+ e- annihilation when both particles are assumed at rest. + { + G4Track copyTrack = G4Track(track); + copyTrack.SetStep(&step); + G4VParticleChange* particleChange = G4eplusAnnihilation::AtRestDoIt(copyTrack,step); + return particleChange; + } +}; +#endif + + + + + + \ No newline at end of file diff --git a/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h b/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h new file mode 100644 index 000000000..e3069bf2c --- /dev/null +++ b/core/opengate_core/opengate_lib/GateLastVertexSplittingSimpleContainer.h @@ -0,0 +1,217 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +/// +#ifndef SimpleContainer_h +#define SimpleContainer_h + + +#include +#include "G4VEnergyLossProcess.hh" +#include "G4Track.hh" +#include "G4VEmProcess.hh" +#include "G4VParticleChange.hh" +#include "G4eplusAnnihilation.hh" +#include "G4PhysicalConstants.hh" +#include "G4MaterialCutsCouple.hh" +#include "G4Gamma.hh" +#include "G4Electron.hh" +#include "G4Positron.hh" +#include "G4eeToTwoGammaModel.hh" +#include "G4EmBiasingManager.hh" +#include "G4EntanglementAuxInfo.hh" +#include "G4eplusAnnihilationEntanglementClipBoard.hh" +#include "G4EmParameters.hh" +#include "G4PhysicsModelCatalog.hh" + + +class SimpleContainer{ + +public : +SimpleContainer(G4String processName,G4double energy,G4ThreeVector momentum, G4ThreeVector position,G4ThreeVector polarization,G4String name,G4double weight,G4int trackStatus,G4int nbSec,G4String flag,G4double length, G4ThreeVector prePos){ + + fProcessNameToSplit = processName; + fEnergyToSplit = energy; + fMomentumToSplit = momentum; + fPositionToSplit = position; + fPolarizationToSplit = polarization; + fParticleNameToSplit = name; + fWeightToSplit = weight; + fTrackStatusToSplit = trackStatus; + fNumberOfSecondariesToSplit = nbSec; + fAnnihilProcessFlag = flag; + fStepLength = length; + fPrePosition = prePos; + +} + + +SimpleContainer(){} + +~SimpleContainer(){} + +void SetProcessNameToSplit(G4String processName){ + fProcessNameToSplit = processName; +} + +G4String GetProcessNameToSplit(){ + return fProcessNameToSplit; +} + +void SetEnergy(G4double energy){ + fEnergyToSplit =energy; +} + +G4double GetEnergy(){ + return fEnergyToSplit; +} + + +void SetWeight(G4double weight){ + fWeightToSplit =weight; +} + +G4double GetWeight(){ + return fWeightToSplit; +} + +void SetPolarization(G4ThreeVector polarization){ + fPolarizationToSplit = polarization; +} + +G4ThreeVector GetPolarization(){ + return fPolarizationToSplit; +} + +void SetMomentum(G4ThreeVector momentum){ + fMomentumToSplit =momentum; +} + +G4ThreeVector GetMomentum(){ + return fMomentumToSplit; +} + +void SetVertexPosition(G4ThreeVector position){ + fPositionToSplit = position; +} + +G4ThreeVector GetVertexPosition(){ + return fPositionToSplit; +} + +void SetParticleNameToSplit(G4String name){ + fParticleNameToSplit = name; +} + +G4String GetParticleNameToSplit(){ + return fParticleNameToSplit; +} + + +void SetTrackStatus(G4int trackStatus){ + fTrackStatusToSplit = trackStatus; +} + + +G4int GetTrackStatus(){ + return fTrackStatusToSplit; +} + + +void SetNbOfSecondaries(G4int nbSec){ + fNumberOfSecondariesToSplit = nbSec; +} + +G4int GetNbOfSecondaries(){ + return fNumberOfSecondariesToSplit; +} + +void SetAnnihilationFlag(G4String flag){ + fAnnihilProcessFlag = flag; +} + +G4String GetAnnihilationFlag(){ + return fAnnihilProcessFlag; +} + +void SetStepLength(G4double length){ + fStepLength = length; +} + +G4double GetStepLength(){ + return fStepLength; +} + + + +void SetPrePositionToSplit(G4ThreeVector prePos){ + fPrePosition = prePos; +} + +G4ThreeVector GetPrePositionToSplit(){ + return fPrePosition; +} + + + + +void DumpInfoToSplit(){ + std::cout<<"Particle name of the particle to split: "<GetWrappedProcess()->GetProcessName()]; + + if (fRussianRouletteForWeights) { + G4int nbOfOrderOfMagnitude = std::log10(fInitialWeight / fMinWeight); + + if ((fProposedWeight < fMinWeight) || + ((fProposedWeight < 0.1 * fInitialWeight) && + (fNbOfRussianRoulette == nbOfOrderOfMagnitude - 1))) { + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + fNbOfRussianRoulette = 0; + return &fParticleChange; + } + + if ((fProposedWeight < 0.1 * fInitialWeight) && (fCountProcess == 4)) { + G4double probability = G4UniformRand(); + for (int i = 2; i <= nbOfOrderOfMagnitude; i++) { + G4double RRprobability = 1 / std::pow(10, i); + if (fProposedWeight * 1 / std::pow(10, fNbOfRussianRoulette) < + 10 * RRprobability * fMinWeight) { + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + fNbOfRussianRoulette = 0; + return &fParticleChange; + } + if ((fProposedWeight >= RRprobability * fInitialWeight) && + (fProposedWeight < 10 * RRprobability * fInitialWeight)) { + if (probability > 10 * RRprobability) { + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + fNbOfRussianRoulette = 0; + return &fParticleChange; + } else { + fProposedWeight = fProposedWeight / (10 * RRprobability); + fNbOfRussianRoulette = fNbOfRussianRoulette + i - 1; + } + } + } + } + } else { + if (fProposedWeight < fMinWeight) { + fParticleChange.ProposeTrackStatus(G4TrackStatus::fStopAndKill); + return &fParticleChange; + } + } + fParticleChange.ProposeWeight(fProposedWeight); + fOperationComplete = true; + return &fParticleChange; +} + +void GateOptnForceFreeFlight ::AlongMoveBy( + const G4BiasingProcessInterface *callingProcess, const G4Step *, + G4double weightChange) + +{ + G4String processName = callingProcess->GetWrappedProcess()->GetProcessName(); + if (processName != "Rayl"){ + fWeightChange[processName] = + weightChange; + } + else { + fWeightChange[processName] = 1; + } +} diff --git a/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h new file mode 100644 index 000000000..398f27851 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnForceFreeFlight.h @@ -0,0 +1,122 @@ +// +// ******************************************************************** +// * 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. * +// ******************************************************************** +// +// +// +//--------------------------------------------------------------- +// +// GateOptnForceFreeFlight +// +// Class Description: +// A G4VBiasingOperation physics-based biasing operation. +// If forces the physics process to not act on the track. +// In this implementation (meant for the ForceCollision +// operator) the free flight is done under zero weight for +// the track, and the action is meant to accumulate the weight +// change for making this uninteracting flight, +// cumulatedWeightChange. +// When the track reaches the current volume boundary, its +// weight is restored with value : +// initialWeight * cumulatedWeightChange +// +//--------------------------------------------------------------- +// Initial version Nov. 2013 M. Verderi +#ifndef GateOptnForceFreeFlight_h +#define GateOptnForceFreeFlight_h 1 + +#include "G4ForceCondition.hh" +#include "G4ParticleChange.hh" // -- ยงยง should add a dedicated "weight change only" particle change +#include "G4VBiasingOperation.hh" +class G4ILawForceFreeFlight; + +class GateOptnForceFreeFlight : public G4VBiasingOperation { +public: + // -- Constructor : + GateOptnForceFreeFlight(G4String name); + // -- destructor: + virtual ~GateOptnForceFreeFlight(); + +public: + // -- Methods from G4VBiasingOperation interface: + // ------------------------------------------- + // -- Used: + virtual const G4VBiasingInteractionLaw * + ProvideOccurenceBiasingInteractionLaw(const G4BiasingProcessInterface *, + G4ForceCondition &); + virtual void AlongMoveBy(const G4BiasingProcessInterface *, const G4Step *, + G4double); + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); + // -- Unused: + virtual G4double DistanceToApplyOperation(const G4Track *, G4double, + G4ForceCondition *) { + return DBL_MAX; + } + virtual G4VParticleChange *GenerateBiasingFinalState(const G4Track *, + const G4Step *) { + return 0; + } + +public: + // -- Additional methods, specific to this class: + // ---------------------------------------------- + // -- return concrete type of interaction law: + G4ILawForceFreeFlight *GetForceFreeFlightLaw() { + return fForceFreeFlightInteractionLaw; + } + // -- initialization for weight: + // void ResetInitialTrackWeight(G4double w) {fInitialTrackWeight = w; + // fCumulatedWeightChange = 1.0;} + + void SetMinWeight(G4double w) { fMinWeight = w; } + void SetInitialWeight(G4double w) { fInitialWeight = w; } + G4double GetTrackWeight() { return fProposedWeight; } + void SetTrackWeight(G4double w) { fProposedWeight = w; } + void SetRussianRouletteProbability(G4double p) { + fRussianRouletteProbability = p; + } + void SetCountProcess(G4int N) { fCountProcess = N; } + void SetSurvivedToRR(G4bool b) { fSurvivedToRR = b; } + void SetRussianRouletteForWeights(G4bool rr) { + fRussianRouletteForWeights = rr; + } + G4bool GetSurvivedToRR() { return fSurvivedToRR; } + G4bool OperationComplete() const { return fOperationComplete; } + +private: + G4ILawForceFreeFlight *fForceFreeFlightInteractionLaw; + std::map fWeightChange; + G4bool fRussianRouletteForWeights; + G4double fMinWeight, fRussianRouletteProbability, fInitialWeight; + G4ParticleChange fParticleChange; + G4bool fOperationComplete; + G4double fProposedWeight; + G4int fCountProcess; + G4bool fSurvivedToRR; + G4int fNbOfRussianRoulette; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp new file mode 100644 index 000000000..cf30c3a7d --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.cpp @@ -0,0 +1,97 @@ +// +// ******************************************************************** +// * 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 GateOptnPairProdSplitting.cc +/// \brief Implementation of the GateOptnPairProdSplitting class + +#include "GateOptnPairProdSplitting.h" +#include "G4BiasingProcessInterface.hh" + +#include "G4ComptonScattering.hh" +#include "G4DynamicParticle.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" +#include "G4GammaConversion.hh" +#include "G4ParticleChange.hh" +#include "G4ParticleChangeForGamma.hh" +#include "G4ParticleChangeForLoss.hh" +#include "G4PhotoElectricEffect.hh" +#include "G4ProcessType.hh" +#include "G4RayleighScattering.hh" +#include "G4SystemOfUnits.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VEmProcess.hh" +#include + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnPairProdSplitting::GateOptnPairProdSplitting(G4String name) + : GateOptnVGenericSplitting(name), fParticleChange() {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnPairProdSplitting::~GateOptnPairProdSplitting() {} + +G4VParticleChange *GateOptnPairProdSplitting::ApplyFinalStateBiasing( + const G4BiasingProcessInterface *callingProcess, const G4Track *track, + const G4Step *step, G4bool &) { + + G4int splittingFactor = ceil(fSplittingFactor); + G4double survivalProbabilitySplitting = + 1 - (splittingFactor - fSplittingFactor) / splittingFactor; + G4double particleWeight = 0; + + G4VParticleChange *processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + + if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) + return processFinalState; + TrackInitializationGamma(&fParticleChange, processFinalState, track, + fSplittingFactor); + + processFinalState->Clear(); + + G4int nCalls = 1; + while (nCalls <= splittingFactor) { + G4double splittingProbability = G4UniformRand(); + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { + particleWeight = track->GetWeight() / fSplittingFactor; + G4VParticleChange *processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if (processFinalState->GetNumberOfSecondaries() >= 1) { + for (int i = 0; i < processFinalState->GetNumberOfSecondaries(); i++) { + G4Track *SecondaryTrack = processFinalState->GetSecondary(i); + SecondaryTrack->SetWeight(particleWeight); + fParticleChange.AddSecondary(SecondaryTrack); + } + } + } + nCalls++; + } + return &fParticleChange; +} diff --git a/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h new file mode 100644 index 000000000..ef4128a81 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnPairProdSplitting.h @@ -0,0 +1,52 @@ +// +// ******************************************************************** +// * 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 GateOptnPairProdSplitting.h +/// \brief Definition of the GateOptnPairProdSplitting class +// + +#ifndef GateOptnPairProdSplitting_h +#define GateOptnPairProdSplitting_h 1 + +#include "G4ParticleChange.hh" +#include "GateOptnVGenericSplitting.h" + +class GateOptnPairProdSplitting : public GateOptnVGenericSplitting { +public: + // -- Constructor : + GateOptnPairProdSplitting(G4String name); + + // -- destructor: + virtual ~GateOptnPairProdSplitting(); + + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); + + G4ParticleChange fParticleChange; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp new file mode 100644 index 000000000..bbfe8acff --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.cpp @@ -0,0 +1,165 @@ +// +// ******************************************************************** +// * 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 GateOptnScatteredGammaSplitting.cc +/// \brief Implementation of the GateOptnScatteredGammaSplitting class + +#include "GateOptnScatteredGammaSplitting.h" +#include "G4BiasingProcessInterface.hh" + +#include "G4ComptonScattering.hh" +#include "G4DynamicParticle.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" +#include "G4GammaConversion.hh" +#include "G4ParticleChange.hh" +#include "G4ParticleChangeForGamma.hh" +#include "G4PhotoElectricEffect.hh" +#include "G4ProcessType.hh" +#include "G4RayleighScattering.hh" +#include "G4SystemOfUnits.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VEmProcess.hh" +#include "GateOptnVGenericSplitting.h" +#include + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnScatteredGammaSplitting::GateOptnScatteredGammaSplitting(G4String name) + : GateOptnVGenericSplitting(name), fParticleChange() {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnScatteredGammaSplitting::~GateOptnScatteredGammaSplitting() {} + +G4VParticleChange *GateOptnScatteredGammaSplitting::ApplyFinalStateBiasing( + const G4BiasingProcessInterface *callingProcess, const G4Track *track, + const G4Step *step, G4bool &) { + + // Here we generate for the first the compton process, given that this + // function (ApplyFinalStateBiasing) is called when there is a compton + // interaction Then the interaction location of the compton process will + // always be the same + + const G4ThreeVector position = step->GetPostStepPoint()->GetPosition(); + G4int splittingFactor = ceil(fSplittingFactor); + G4double survivalProbabilitySplitting = + 1 - (splittingFactor - fSplittingFactor) / splittingFactor; + G4double gammaWeight = 0; + + G4VParticleChange *processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + // In case we don't want to split (a bit faster) i.e no biaising or no + // splitting low weights particles. + + if (fSplittingFactor == 1 && fRussianRouletteForAngle == false) + return processFinalState; + + TrackInitializationGamma(&fParticleChange, processFinalState, track, + fSplittingFactor); + processFinalState->Clear(); + + // There is here the biasing process : + // Since G4VParticleChange class does not allow to retrieve scattered gamma + // information, we need to cast the type G4ParticleChangeForGamma to the + // G4VParticleChange object. We then call the process (biasWrapper(compt)) + // fSplittingFactor times (Here, the difference with the other version of + // splitting is the primary particle will be killed and its weight does not + // count) to generate, at last, fSplittingFactor gamma according to the + // compton interaction process. If the gamma track is ok regarding the + // russian roulette algorithm (no russian roulette + //, or within the acceptance angle, or not killed by the RR process), we add + // it to the primary track. + // If an electron is generated (above the range cut), we also generate it. + // A tremendous advantage is there is no need to use by ourself Klein-Nishina + // formula or other. So, if the physics list used takes into account the + // doppler broadening or other fine effects, this will be also taken into + // account by the MC simulation. PS : The first gamma is then the primary + // particle, but all the other splitted particle (electron of course AND + // gamma) must be considered as secondary particles, even though generated + // gamma will not be cut here by the applied cut. + + G4int nCalls = 1; + while (nCalls <= splittingFactor) { + gammaWeight = track->GetWeight() / fSplittingFactor; + G4VParticleChange *processGammaSplittedFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + G4ParticleChangeForGamma *castedProcessGammaSplittedFinalState = + (G4ParticleChangeForGamma *)processGammaSplittedFinalState; + const G4ThreeVector momentum = + castedProcessGammaSplittedFinalState->GetProposedMomentumDirection(); + G4double energy = + castedProcessGammaSplittedFinalState->GetProposedKineticEnergy(); + G4double splittingProbability = G4UniformRand(); + + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { + if (fRussianRouletteForAngle == true) { + G4double weightToApply = RussianRouletteForAngleSurvival( + castedProcessGammaSplittedFinalState + ->GetProposedMomentumDirection(), + fVectorDirector, fMaxTheta, fSplittingFactor); + if (weightToApply != 0) { + gammaWeight = gammaWeight * weightToApply; + G4Track *gammaTrack = new G4Track(*track); + gammaTrack->SetWeight(gammaWeight); + gammaTrack->SetKineticEnergy(energy); + gammaTrack->SetMomentumDirection(momentum); + gammaTrack->SetPosition(position); + fParticleChange.AddSecondary(gammaTrack); + if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { + G4Track *electronTrack = + processGammaSplittedFinalState->GetSecondary(0); + electronTrack->SetWeight(gammaWeight); + fParticleChange.AddSecondary(electronTrack); + } + } + } + + else { + G4Track *gammaTrack = new G4Track(*track); + gammaTrack->SetWeight(gammaWeight); + gammaTrack->SetKineticEnergy(energy); + gammaTrack->SetMomentumDirection(momentum); + gammaTrack->SetPosition(position); + fParticleChange.AddSecondary(gammaTrack); + if (processGammaSplittedFinalState->GetNumberOfSecondaries() == 1) { + G4Track *electronTrack = + processGammaSplittedFinalState->GetSecondary(0); + electronTrack->SetWeight(gammaWeight); + fParticleChange.AddSecondary(electronTrack); + } + } + } + nCalls++; + processGammaSplittedFinalState->Clear(); + castedProcessGammaSplittedFinalState->Clear(); + } + return &fParticleChange; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h new file mode 100644 index 000000000..ff0a6b623 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnScatteredGammaSplitting.h @@ -0,0 +1,53 @@ +// +// ******************************************************************** +// * 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 GateOptnScatteredGammaSplitting.h +/// \brief Definition of the GateOptnScatteredGammaSplitting class +// + +#ifndef GateOptnScatteredGammaSplitting_h +#define GateOptnScatteredGammaSplitting_h 1 + +#include "G4ParticleChange.hh" +#include "G4VBiasingOperation.hh" +#include "GateOptnVGenericSplitting.h" + +class GateOptnScatteredGammaSplitting : public GateOptnVGenericSplitting { +public: + // -- Constructor : + GateOptnScatteredGammaSplitting(G4String name); + + // -- destructor: + virtual ~GateOptnScatteredGammaSplitting(); + + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); + + G4ParticleChange fParticleChange; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp new file mode 100644 index 000000000..ba894cf04 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.cpp @@ -0,0 +1,105 @@ +// +// ******************************************************************** +// * 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 GateOptnVGenericSplitting.cc +/// \brief Implementation of the GateOptnVGenericSplitting class + +#include "GateOptnVGenericSplitting.h" +#include "G4BiasingProcessInterface.hh" + +#include "G4ComptonScattering.hh" +#include "G4DynamicParticle.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" +#include "G4GammaConversion.hh" +#include "G4ParticleChange.hh" +#include "G4ParticleChangeForGamma.hh" +#include "G4ParticleChangeForLoss.hh" +#include "G4PhotoElectricEffect.hh" +#include "G4ProcessType.hh" +#include "G4RayleighScattering.hh" +#include "G4SystemOfUnits.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VEmProcess.hh" +#include + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnVGenericSplitting:: + GateOptnVGenericSplitting(G4String name) + : G4VBiasingOperation(name), fParticleChange() {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptnVGenericSplitting:: + ~GateOptnVGenericSplitting() {} + + +void GateOptnVGenericSplitting::TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split) { + + G4ParticleChangeForLoss* processFinalStateForLoss =( G4ParticleChangeForLoss* ) processFinalState ; + particleChange->Initialize(*track); + particleChange->ProposeTrackStatus(processFinalStateForLoss->GetTrackStatus() ); + particleChange->ProposeEnergy(processFinalStateForLoss->GetProposedKineticEnergy() ); + particleChange->ProposeMomentumDirection(processFinalStateForLoss->GetProposedMomentumDirection()); + particleChange->SetNumberOfSecondaries(fSplittingFactor); + particleChange->SetSecondaryWeightByProcess(true); + processFinalStateForLoss->Clear(); +} + +void GateOptnVGenericSplitting::TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split) { + G4ParticleChangeForGamma* processFinalStateForGamma = (G4ParticleChangeForGamma *)processFinalState; + particleChange->Initialize(*track); + particleChange->ProposeTrackStatus(processFinalStateForGamma->GetTrackStatus() ); + particleChange->ProposeEnergy(processFinalStateForGamma->GetProposedKineticEnergy() ); + particleChange->ProposeMomentumDirection(processFinalStateForGamma->GetProposedMomentumDirection() ); + particleChange->SetNumberOfSecondaries(fSplittingFactor); + particleChange->SetSecondaryWeightByProcess(true); + processFinalStateForGamma->Clear(); + + +} + +G4double GateOptnVGenericSplitting::RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split){ +G4double cosTheta =vectorDirector * dir; +G4double theta = std::acos(cosTheta); +G4double weightToApply = 1; +if (theta > maxTheta){ + G4double probability = G4UniformRand(); + if (probability <= 1 / split) { + weightToApply = split; + } + else{ + weightToApply = 0; + } +} +return weightToApply; + +} + + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h new file mode 100644 index 000000000..d7cebd78a --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptnVGenericSplitting.h @@ -0,0 +1,122 @@ +// +// ******************************************************************** +// * 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 GateOptnVGenericSplitting.h +/// \brief Definition of the GateOptnVGenericSplitting class +// + +#ifndef GateOptnVGenericSplitting_h +#define GateOptnVGenericSplitting_h 1 + +#include "G4ParticleChange.hh" +#include "G4VBiasingOperation.hh" + +class GateOptnVGenericSplitting : public G4VBiasingOperation { +public: + // -- Constructor : + GateOptnVGenericSplitting(G4String name); + + // -- destructor: + virtual ~GateOptnVGenericSplitting(); + +public: + // ---------------------------------------------- + // -- Methods from G4VBiasingOperation interface: + // ---------------------------------------------- + // -- Unused: + virtual const G4VBiasingInteractionLaw * + ProvideOccurenceBiasingInteractionLaw(const G4BiasingProcessInterface *, + G4ForceCondition &) { + return 0; + } + + // --Used: + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &) { + return 0; + }; + + // -- Unsued: + virtual G4double DistanceToApplyOperation(const G4Track *, G4double, + G4ForceCondition *) { + return DBL_MAX; + } + virtual G4VParticleChange *GenerateBiasingFinalState(const G4Track *, + const G4Step *) { + return 0; + } + + +// ---------------------------------------------- +// -- Methods for the generic splitting +// ---------------------------------------------- + +void TrackInitializationChargedParticle(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); +void TrackInitializationGamma(G4ParticleChange* particleChange,G4VParticleChange* processFinalState, const G4Track* track,G4double split); +static G4double RussianRouletteForAngleSurvival(G4ThreeVector dir,G4ThreeVector vectorDirector,G4double maxTheta,G4double split); + + +public: + // ---------------------------------------------- + // -- Additional methods, specific to this class: + // ---------------------------------------------- + // -- Splitting factor: + void SetSplittingFactor(G4double splittingFactor) { + fSplittingFactor = splittingFactor; + } + G4double GetSplittingFactor() const { return fSplittingFactor; } + + void SetRussianRouletteForAngle(G4bool russianRoulette) { + fRussianRouletteForAngle = russianRoulette; + } + + void SetVectorDirector(G4ThreeVector vectorDirector) { + fVectorDirector = vectorDirector; + } + + void SetRotationMatrix(G4RotationMatrix rot) { fRot = rot; } + + G4ThreeVector GetVectorDirector() const { return fVectorDirector; } + + void SetMaxTheta(G4double maxTheta) { fMaxTheta = maxTheta; } + + G4double GetMaxTheta() const { return fMaxTheta; } + + G4VParticleChange *GetParticleChange() { + G4VParticleChange *particleChange = &fParticleChange; + return particleChange; + } + + G4double fSplittingFactor; + G4ParticleChange fParticleChange; + G4bool fRussianRouletteForAngle; + G4ThreeVector fVectorDirector; + G4double fMaxTheta; + G4RotationMatrix fRot; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp b/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp new file mode 100644 index 000000000..ce9ff2324 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptneBremSplitting.cpp @@ -0,0 +1,112 @@ +// +// ******************************************************************** +// * 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 GateOptneBremSplitting.cc +/// \brief Implementation of the GateOptneBremSplitting class + +#include "GateOptneBremSplitting.h" +#include "G4BiasingProcessInterface.hh" + +#include "G4ComptonScattering.hh" +#include "G4DynamicParticle.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" +#include "G4GammaConversion.hh" +#include "G4ParticleChange.hh" +#include "G4ParticleChangeForGamma.hh" +#include "G4ParticleChangeForLoss.hh" +#include "G4PhotoElectricEffect.hh" +#include "G4ProcessType.hh" +#include "G4RayleighScattering.hh" +#include "G4SystemOfUnits.hh" +#include "G4TrackStatus.hh" +#include "G4TrackingManager.hh" +#include "G4VEmProcess.hh" +#include "GateOptnVGenericSplitting.h" +#include + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptneBremSplitting::GateOptneBremSplitting(G4String name) + : GateOptnVGenericSplitting(name), fParticleChange() {} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptneBremSplitting::~GateOptneBremSplitting() {} + +G4VParticleChange *GateOptneBremSplitting::ApplyFinalStateBiasing( + const G4BiasingProcessInterface *callingProcess, const G4Track *track, + const G4Step *step, G4bool &) { + + G4int splittingFactor = ceil(fSplittingFactor); + G4double survivalProbabilitySplitting = + 1 - (splittingFactor - fSplittingFactor) / splittingFactor; + G4VParticleChange *processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if (fSplittingFactor == 1) + return processFinalState; + if (processFinalState->GetNumberOfSecondaries() == 0) + return processFinalState; + + TrackInitializationChargedParticle(&fParticleChange, processFinalState, track, + fSplittingFactor); + + processFinalState->Clear(); + + G4int nCalls = 1; + while (nCalls <= fSplittingFactor) { + G4double splittingProbability = G4UniformRand(); + if (splittingProbability <= survivalProbabilitySplitting || + survivalProbabilitySplitting == 1) { + processFinalState = + callingProcess->GetWrappedProcess()->PostStepDoIt(*track, *step); + if (processFinalState->GetNumberOfSecondaries() >= 1) { + for (int i = 0; i < processFinalState->GetNumberOfSecondaries(); i++) { + G4double gammaWeight = track->GetWeight() / fSplittingFactor; + G4Track *gammaTrack = processFinalState->GetSecondary(i); + if (fRussianRouletteForAngle == true) { + G4double weightToApply = RussianRouletteForAngleSurvival( + gammaTrack->GetMomentumDirection(), fVectorDirector, fMaxTheta, + fSplittingFactor); + if (weightToApply != 0) { + gammaWeight = gammaWeight * weightToApply; + gammaTrack->SetWeight(gammaWeight); + fParticleChange.AddSecondary(gammaTrack); + } + } else { + gammaTrack->SetWeight(gammaWeight); + fParticleChange.AddSecondary(gammaTrack); + } + } + } + processFinalState->Clear(); + } + nCalls++; + } + return &fParticleChange; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptneBremSplitting.h b/core/opengate_core/opengate_lib/GateOptneBremSplitting.h new file mode 100644 index 000000000..47319e536 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptneBremSplitting.h @@ -0,0 +1,54 @@ +// +// ******************************************************************** +// * 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 GateOptneBremSplitting.h +/// \brief Definition of the GateOptneBremSplitting class +// + +#ifndef GateOptneBremSplitting_h +#define GateOptneBremSplitting_h 1 + +#include "G4ParticleChange.hh" +#include "G4VBiasingOperation.hh" +#include "GateOptnVGenericSplitting.h" + +class GateOptneBremSplitting : public GateOptnVGenericSplitting { +public: + // -- Constructor : + GateOptneBremSplitting(G4String name); + + // -- destructor: + virtual ~GateOptneBremSplitting(); + +public: + virtual G4VParticleChange * + ApplyFinalStateBiasing(const G4BiasingProcessInterface *, const G4Track *, + const G4Step *, G4bool &); + + G4ParticleChange fParticleChange; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp new file mode 100644 index 000000000..13c5b3cfb --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.cpp @@ -0,0 +1,319 @@ +// +// ******************************************************************** +// * 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 GateOptrComptPseudoTransportationActor.cc +/// \brief Implementation of the GateOptrComptPseudoTransportationActor class + +#include "GateHelpersDict.h" +#include "GateHelpersImage.h" + +#include "CLHEP/Units/SystemOfUnits.h" +#include "G4BiasingProcessInterface.hh" +#include "G4Electron.hh" +#include "G4EmCalculator.hh" +#include "G4Exception.hh" +#include "G4Gamma.hh" +#include "G4LogicalVolumeStore.hh" +#include "G4ParticleTable.hh" +#include "G4PhysicalVolumeStore.hh" +#include "G4Positron.hh" +#include "G4ProcessManager.hh" +#include "G4ProcessVector.hh" +#include "G4RunManager.hh" +#include "G4TrackStatus.hh" +#include "G4UImanager.hh" +#include "G4UserTrackingAction.hh" +#include "G4VEmProcess.hh" +#include "G4eplusAnnihilation.hh" +#include "GateOptnPairProdSplitting.h" +#include "GateOptnScatteredGammaSplitting.h" +#include "GateOptneBremSplitting.h" +#include "GateOptrComptPseudoTransportationActor.h" +#include "G4UImanager.hh" +#include "G4eplusAnnihilation.hh" +#include "GateOptnVGenericSplitting.h" + + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +GateOptrComptPseudoTransportationActor::GateOptrComptPseudoTransportationActor( + py::dict &user_info) + : G4VBiasingOperator("ComptSplittingOperator"), + GateVActor(user_info, false) { + fMotherVolumeName = DictGetStr(user_info, "mother"); + fAttachToLogicalHolder = DictGetBool(user_info, "attach_to_logical_holder"); + fSplittingFactor = DictGetDouble(user_info, "splitting_factor"); + fRelativeMinWeightOfParticle = + DictGetDouble(user_info, "relative_min_weight_of_particle"); + // Since the russian roulette uses as a probability 1/splitting, we need to + // have a double, but the splitting factor provided by the user is logically + // an int, so we need to change the type. + fRotationVectorDirector = DictGetBool(user_info, "rotation_vector_director"); + fRussianRouletteForAngle = + DictGetBool(user_info, "russian_roulette_for_angle"); + fVectorDirector = DictGetG4ThreeVector(user_info, "vector_director"); + fRussianRouletteForWeights = + DictGetBool(user_info, "russian_roulette_for_weights"); + fMaxTheta = DictGetDouble(user_info, "max_theta"); + fFreeFlightOperation = new GateOptnForceFreeFlight("freeFlightOperation"); + fScatteredGammaSplittingOperation = + new GateOptnScatteredGammaSplitting("comptSplittingOperation"); + feBremSplittingOperation = + new GateOptneBremSplitting("eBremSplittingOperation"); + fPairProdSplittingOperation = + new GateOptnPairProdSplitting("PairProdSplittingOperation"); + fActions.insert("StartSimulationAction"); + fActions.insert("SteppingAction"); + fActions.insert("BeginOfEventAction"); + isSplitted = false; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +void GateOptrComptPseudoTransportationActor::AttachAllLogicalDaughtersVolumes( + G4LogicalVolume *volume) { + if (fAttachToLogicalHolder == false) { + if (volume->GetName() != G4LogicalVolumeStore::GetInstance() + ->GetVolume(fMotherVolumeName) + ->GetName()) { + AttachTo(volume); + } + } + if (fAttachToLogicalHolder == true) { + AttachTo(volume); + } + G4int nbOfDaughters = volume->GetNoDaughters(); + if (nbOfDaughters > 0) { + for (int i = 0; i < nbOfDaughters; i++) { + G4String LogicalVolumeName = + volume->GetDaughter(i)->GetLogicalVolume()->GetName(); + G4LogicalVolume *logicalDaughtersVolume = + volume->GetDaughter(i)->GetLogicalVolume(); + AttachAllLogicalDaughtersVolumes(logicalDaughtersVolume); + if (!(std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeName) != fNameOfBiasedLogicalVolume.end())) + fNameOfBiasedLogicalVolume.push_back( + volume->GetDaughter(i)->GetLogicalVolume()->GetName()); + } + } +} + +void GateOptrComptPseudoTransportationActor::StartSimulationAction() { + G4LogicalVolume *biasingVolume = + G4LogicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + + // Here we need to attach all the daughters and daughters of daughters (...) + // to the biasing operator. To do that, I use the function + // AttachAllLogicalDaughtersVolumes. + AttachAllLogicalDaughtersVolumes(biasingVolume); + + fScatteredGammaSplittingOperation->SetSplittingFactor(fSplittingFactor); + + fScatteredGammaSplittingOperation->SetMaxTheta(fMaxTheta); + fScatteredGammaSplittingOperation->SetRussianRouletteForAngle( + fRussianRouletteForAngle); + + feBremSplittingOperation->SetSplittingFactor(fSplittingFactor); + feBremSplittingOperation->SetMaxTheta(fMaxTheta); + feBremSplittingOperation->SetRussianRouletteForAngle( + fRussianRouletteForAngle); + + fPairProdSplittingOperation->SetSplittingFactor(fSplittingFactor); + + fFreeFlightOperation->SetRussianRouletteForWeights(fRussianRouletteForWeights); + + +} + +void GateOptrComptPseudoTransportationActor::StartRun() { + + // The way to behave of the russian roulette is the following : + // we provide a vector director and the theta angle acceptance, where theta = + // 0 is a vector colinear to the vector director Then if the track generated + // is on the acceptance angle, we add it to the primary track, and if it's not + // the case, we launch the russian roulette + if (fRotationVectorDirector) { + G4VPhysicalVolume *physBiasingVolume = + G4PhysicalVolumeStore::GetInstance()->GetVolume(fMotherVolumeName); + auto rot = physBiasingVolume->GetObjectRotationValue(); + fVectorDirector = rot * fVectorDirector; + fScatteredGammaSplittingOperation->SetRotationMatrix(rot); + feBremSplittingOperation->SetRotationMatrix(rot); + } + + fScatteredGammaSplittingOperation->SetVectorDirector(fVectorDirector); + feBremSplittingOperation->SetVectorDirector(fVectorDirector); +} + +void GateOptrComptPseudoTransportationActor::SteppingAction(G4Step *step) { + G4String creationProcessName = "None"; + if (step->GetTrack()->GetCreatorProcess() != 0){ + creationProcessName = step->GetTrack()->GetCreatorProcess()->GetProcessName(); + + } + +if ((fIsFirstStep) && (fRussianRouletteForAngle)){ + G4String LogicalVolumeNameOfCreation = step->GetTrack()->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ + if (creationProcessName == "biasWrapper(annihil)"){ + auto dir = step->GetPreStepPoint()->GetMomentumDirection(); + G4double w = GateOptnVGenericSplitting::RussianRouletteForAngleSurvival(dir,fVectorDirector,fMaxTheta,fSplittingFactor); + if (w == 0) + { + step->GetTrack()->SetTrackStatus(fStopAndKill); + } + else { + step->GetTrack()->SetWeight(step->GetTrack()->GetWeight() * w); + } + } + } +} + +if ((isSplitted == true) && (step->GetPostStepPoint()->GetStepStatus() != fWorldBoundary)) { + G4String LogicalVolumeName = step->GetPostStepPoint()->GetPhysicalVolume()->GetLogicalVolume()->GetName(); + if (!(std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeName ) !=fNameOfBiasedLogicalVolume.end()) + && (LogicalVolumeName != fMotherVolumeName)) { + step->GetTrack()->SetTrackStatus(fStopAndKill); + isSplitted = false; + } +} + +fIsFirstStep = false; +} + +void GateOptrComptPseudoTransportationActor::BeginOfEventAction( + const G4Event *event) { + fKillOthersParticles = false; + fPassedByABiasedVolume = false; + fEventID = event->GetEventID(); + fEventIDKineticEnergy = + event->GetPrimaryVertex(0)->GetPrimary(0)->GetKineticEnergy(); +} + +void GateOptrComptPseudoTransportationActor::StartTracking( + const G4Track *track) { + G4String creationProcessName = "None"; + fIsFirstStep = true; + if (track->GetCreatorProcess() != 0){ + creationProcessName = track->GetCreatorProcess()->GetProcessName(); + } + + + if (track->GetParticleDefinition()->GetParticleName() == "gamma"){ + G4String LogicalVolumeNameOfCreation = track->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), fNameOfBiasedLogicalVolume.end(),LogicalVolumeNameOfCreation ) !=fNameOfBiasedLogicalVolume.end()){ + + fInitialWeight = track->GetWeight(); + fFreeFlightOperation->SetInitialWeight(fInitialWeight); + } + } +} + +// For the following operation the idea is the following : +// All the potential photon processes are biased. If a particle undergoes a +// compton interaction, we splitted it (ComptonSplittingForTransportation +// operation) and the particle generated are pseudo-transported with the +// ForceFreeFLight operation +// Since the occurence Biaising operation is called at the beginning of each +// track, and propose a different way to track the particle +//(with modified physics), it here returns other thing than 0 if we want to +// pseudo-transport the particle, so if its creatorProcess is the modified +// compton interaction + +G4VBiasingOperation * +GateOptrComptPseudoTransportationActor::ProposeOccurenceBiasingOperation( + const G4Track *track, const G4BiasingProcessInterface *callingProcess) { + + if (track->GetParticleDefinition()->GetParticleName() == "gamma") { + G4String LogicalVolumeNameOfCreation = + track->GetLogicalVolumeAtVertex()->GetName(); + if (std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeNameOfCreation) != + fNameOfBiasedLogicalVolume.end()) { + fFreeFlightOperation->SetMinWeight(fInitialWeight / + fRelativeMinWeightOfParticle); + fFreeFlightOperation->SetTrackWeight(track->GetWeight()); + fFreeFlightOperation->SetCountProcess(0); + return fFreeFlightOperation; + } + } + return 0; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... + +// Here we call the final state biasing operation called if one of the biased +// interaction (all photon interaction here) occurs. +// That's why we need here to apply some conditions to just split the initial +// track. + +G4VBiasingOperation * +GateOptrComptPseudoTransportationActor::ProposeFinalStateBiasingOperation( + const G4Track *track, const G4BiasingProcessInterface *callingProcess) { + G4String particleName = track->GetParticleDefinition()->GetParticleName(); + + G4String LogicalVolumeNameOfCreation = + track->GetLogicalVolumeAtVertex()->GetName(); + G4String CreationProcessName = ""; + if (track->GetCreatorProcess() != 0) { + CreationProcessName = track->GetCreatorProcess()->GetProcessName(); + } + + if (track->GetParticleDefinition()->GetParticleName() == "gamma") { + if (std::find(fNameOfBiasedLogicalVolume.begin(), + fNameOfBiasedLogicalVolume.end(), + LogicalVolumeNameOfCreation) != + fNameOfBiasedLogicalVolume.end()) { + return callingProcess->GetCurrentOccurenceBiasingOperation(); + } + } + + if (((CreationProcessName != "biasWrapper(conv)") && + (CreationProcessName != "biasWrapper(compt)")) && + (callingProcess->GetWrappedProcess()->GetProcessName() == "eBrem")) { + return feBremSplittingOperation; + } + + if (!(std::find(fCreationProcessNameList.begin(), fCreationProcessNameList.end(),CreationProcessName) != fCreationProcessNameList.end())){ + if (callingProcess->GetWrappedProcess()->GetProcessName() == "compt"){ + + isSplitted = true; + return fScatteredGammaSplittingOperation; + } + if ((callingProcess->GetWrappedProcess()->GetProcessName() == "conv")) { + return fPairProdSplittingOperation; + } + } + return 0; +} + +void GateOptrComptPseudoTransportationActor::EndTracking() { + isSplitted = false; +} + +//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... diff --git a/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h new file mode 100644 index 000000000..629d31b69 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateOptrComptPseudoTransportationActor.h @@ -0,0 +1,137 @@ +// +// ******************************************************************** +// * 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 GateOptrComptPseudoTransportationActor.h +/// \brief Definition of the GateOptrComptPseudoTransportationActor class +#ifndef GateOptrComptPseudoTransportationActor_h +#define GateOptrComptPseudoTransportationActor_h 1 + +#include "G4EmCalculator.hh" +#include "G4VBiasingOperator.hh" +#include "GateOptnForceFreeFlight.h" +#include "GateOptnPairProdSplitting.h" +#include "GateOptneBremSplitting.h" + +#include "GateVActor.h" +#include +#include +namespace py = pybind11; + +class GateOptnScatteredGammaSplitting; + +class GateOptrComptPseudoTransportationActor : public G4VBiasingOperator, + public GateVActor { +public: + GateOptrComptPseudoTransportationActor(py::dict &user_info); + virtual ~GateOptrComptPseudoTransportationActor() {} + +public: + // ------------------------- + // Optional from base class: + // ------------------------- + // -- Call at run start: + // virtual void BeginOfRunAction(const G4Run *run); + + // virtual void SteppingAction(G4Step* step); + + // -- Call at each track starting: + // virtual void PreUserTrackingAction( const G4Track* track ); + + G4double fSplittingFactor; + G4double fInitialWeight; + G4double fRelativeMinWeightOfParticle; + G4double fWeightThreshold; + G4bool fBiasPrimaryOnly; + G4bool fBiasOnlyOnce; + G4int fNInteractions = 0; + G4bool fRussianRouletteForAngle; + G4bool fRussianRouletteForWeights; + G4bool fRotationVectorDirector; + G4ThreeVector fVectorDirector; + G4double fMaxTheta; + G4bool isSplitted; + G4int NbOfTrack = 0; + G4int NbOfProbe = 1; + G4double weight = 0; + G4bool fKillOthersParticles = false; + G4bool fUseProbes = false; + G4bool fSurvivedRR = false; + G4bool fAttachToLogicalHolder = true; + G4bool fPassedByABiasedVolume = false; + G4double fKineticEnergyAtTheEntrance; + G4int ftrackIDAtTheEntrance; + G4int fEventID; + G4double fEventIDKineticEnergy; + G4bool ftestbool= false; + G4bool fIsFirstStep = false; + const G4VProcess* fAnnihilation =nullptr; + + std::vector fNameOfBiasedLogicalVolume = {}; + std::vector v_EventID = {}; + std::vector fCreationProcessNameList = {"biasWrapper(compt)", "biasWrapper(eBrem)","biasWrapper(annihil)"}; + + // Unused but mandatory + + virtual void StartSimulationAction(); + virtual void StartRun(); + virtual void StartTracking(const G4Track *); + virtual void SteppingAction(G4Step *); + virtual void BeginOfEventAction(const G4Event *); + virtual void EndTracking(); + +protected: + // ----------------------------- + // -- Mandatory from base class: + // ----------------------------- + // -- Unused: + void AttachAllLogicalDaughtersVolumes(G4LogicalVolume *); + virtual G4VBiasingOperation *ProposeNonPhysicsBiasingOperation( + const G4Track * /* track */, + const G4BiasingProcessInterface * /* callingProcess */) { + return 0; + } + + // -- Used: + virtual G4VBiasingOperation *ProposeOccurenceBiasingOperation( + const G4Track * /* track */, + const G4BiasingProcessInterface * /* callingProcess */); + + virtual G4VBiasingOperation *ProposeFinalStateBiasingOperation( + const G4Track *track, const G4BiasingProcessInterface *callingProcess); + +private: + // -- Avoid compiler complaining for (wrong) method shadowing, + // -- this is because other virtual method with same name exists. + using G4VBiasingOperator::OperationApplied; + +private: + GateOptnForceFreeFlight *fFreeFlightOperation; + GateOptnScatteredGammaSplitting *fScatteredGammaSplittingOperation; + GateOptneBremSplitting *feBremSplittingOperation; + GateOptnPairProdSplitting *fPairProdSplittingOperation; +}; + +#endif diff --git a/core/opengate_core/opengate_lib/GateSPSEneDistribution.cpp b/core/opengate_core/opengate_lib/GateSPSEneDistribution.cpp index dcc864f01..648050ff6 100644 --- a/core/opengate_core/opengate_lib/GateSPSEneDistribution.cpp +++ b/core/opengate_core/opengate_lib/GateSPSEneDistribution.cpp @@ -8,7 +8,11 @@ #include "GateSPSEneDistribution.h" #include "G4UnitsTable.hh" #include "GateHelpers.h" +#include "fmt/color.h" +#include "fmt/core.h" #include +#include +#include // Parts copied from GateSPSEneDistribution.cc @@ -23,8 +27,12 @@ G4double GateSPSEneDistribution::VGenerateOne(G4ParticleDefinition *d) { GenerateFromCDF(); else if (GetEnergyDisType() == "range") GenerateRange(); - else if (GetEnergyDisType() == "spectrum_lines") + else if (GetEnergyDisType() == "spectrum_discrete") GenerateSpectrumLines(); + else if (GetEnergyDisType() == "spectrum_histogram") + GenerateSpectrumHistogram(); + else if (GetEnergyDisType() == "spectrum_histogram_linear") + GenerateSpectrumHistogramInterpolated(); else fParticleEnergy = G4SPSEneDistribution::GenerateOne(d); return fParticleEnergy; @@ -109,9 +117,46 @@ void GateSPSEneDistribution::GenerateRange() { } void GateSPSEneDistribution::GenerateSpectrumLines() { - auto x = G4UniformRand(); + auto const i = IndexForProbability(G4UniformRand()); + fParticleEnergy = fEnergyCDF[i]; +} + +void GateSPSEneDistribution::GenerateSpectrumHistogram() { + auto const i = IndexForProbability(G4UniformRand()); + fParticleEnergy = G4RandFlat::shoot(fEnergyCDF[i], fEnergyCDF[i + 1]); +} + +void GateSPSEneDistribution::GenerateSpectrumHistogramInterpolated() { + auto const i = IndexForProbability(G4UniformRand()); + + auto const a = (fEnergyCDF[i] + fEnergyCDF[i + 1]) / 2; + auto const b = (fEnergyCDF[i + 1] + fEnergyCDF[i + 2]) / 2; + auto const d = fProbabilityCDF[i + 1] - fProbabilityCDF[i]; + + if (std::abs(d) < std::numeric_limits::epsilon()) { + fParticleEnergy = G4RandFlat::shoot(a, b); + } else { + auto const alpha = d / (b - a); + auto const beta = fProbabilityCDF[i] - alpha * a; + auto const norm = .5 * alpha * (b * b - a * a) + beta * (b - a); + auto const p = G4UniformRand(); // p in ]0, 1[ + // [comment from GATE 9] inversion transform sampling + auto const sqrtDelta = std::sqrt((alpha * a + beta) * (alpha * a + beta) + + 2 * alpha * norm * p); + auto const x = (-beta + sqrtDelta) / alpha; + if ((x - a) * (x - b) <= 0) + fParticleEnergy = x; + else + fParticleEnergy = (-beta - sqrtDelta) / alpha; + } +} + +std::size_t GateSPSEneDistribution::IndexForProbability(double p) const { + // p in ]0, 1[ + // see + // https://geant4-forum.web.cern.ch/t/what-is-the-range-of-numbers-generated-by-g4uniformrand/5187 auto i = 0; - while (x >= (fProbabilityCDF[i])) + while (p >= (fProbabilityCDF[i])) // TODO -> std:: i++; - fParticleEnergy = fEnergyCDF[i]; + return i; } diff --git a/core/opengate_core/opengate_lib/GateSPSEneDistribution.h b/core/opengate_core/opengate_lib/GateSPSEneDistribution.h index fb4a02be1..59c565520 100644 --- a/core/opengate_core/opengate_lib/GateSPSEneDistribution.h +++ b/core/opengate_core/opengate_lib/GateSPSEneDistribution.h @@ -13,7 +13,7 @@ class GateSPSEneDistribution : public G4SPSEneDistribution { public: - virtual ~GateSPSEneDistribution() {} + virtual ~GateSPSEneDistribution() = default; void GenerateFromCDF(); @@ -27,6 +27,10 @@ class GateSPSEneDistribution : public G4SPSEneDistribution { void GenerateSpectrumLines(); + void GenerateSpectrumHistogram(); + + void GenerateSpectrumHistogramInterpolated(); + // Cannot inherit from GenerateOne virtual G4double VGenerateOne(G4ParticleDefinition *); @@ -34,6 +38,9 @@ class GateSPSEneDistribution : public G4SPSEneDistribution { std::vector fProbabilityCDF; std::vector fEnergyCDF; + +private: + std::size_t IndexForProbability(double p) const; }; #endif // GateSPSEneDistribution_h diff --git a/core/opengate_core/opengate_lib/GateSourceManager.cpp b/core/opengate_core/opengate_lib/GateSourceManager.cpp index d183567e9..d0772fe15 100644 --- a/core/opengate_core/opengate_lib/GateSourceManager.cpp +++ b/core/opengate_core/opengate_lib/GateSourceManager.cpp @@ -241,6 +241,9 @@ void GateSourceManager::PrepareRunToStart(int run_id) { : std::to_string(G4Threading::G4GetThreadId())); } + + + void GateSourceManager::PrepareNextSource() { auto &l = fThreadLocalData.Get(); l.fNextActiveSource = nullptr; @@ -255,6 +258,7 @@ void GateSourceManager::PrepareNextSource() { l.fNextSimulationTime = t; } } + // If no next time in the current interval, active source is NULL } diff --git a/core/opengate_core/opengate_lib/GateSourceManager.h b/core/opengate_core/opengate_lib/GateSourceManager.h index 893ced872..1b559a07b 100644 --- a/core/opengate_core/opengate_lib/GateSourceManager.h +++ b/core/opengate_core/opengate_lib/GateSourceManager.h @@ -64,6 +64,22 @@ class GateSourceManager : public G4VUserPrimaryGeneratorAction { // Return a source GateVSource *FindSourceByName(std::string name) const; + + + G4String GetActiveSourceName(){ + auto &l = fThreadLocalData.Get(); + if (l.fNextActiveSource !=0){ + G4String name = l.fNextActiveSource->fName; + return name; + } + return "None"; + } + + void SetActiveSourcebyName(G4String sourceName){ + auto &l = fThreadLocalData.Get(); + auto* source = FindSourceByName(sourceName); + l.fNextActiveSource = source; + } // [available on py side] start the simulation, master thread only void StartMasterThread(); diff --git a/core/opengate_core/opengate_lib/GateVActor.cpp b/core/opengate_core/opengate_lib/GateVActor.cpp index 6ac2dfaf3..cfd6e7873 100644 --- a/core/opengate_core/opengate_lib/GateVActor.cpp +++ b/core/opengate_core/opengate_lib/GateVActor.cpp @@ -80,8 +80,8 @@ bool GateVActor::GetWriteToDisk(std::string outputName) { } catch (std::out_of_range &e) { std::ostringstream msg; msg << "(GetWriteToDisk) No actor output with the name " << outputName - << " exists."; - msg << fMotherVolumeName << " " << GetName(); + << " exists in actor " << GetName() << " attached to " + << fMotherVolumeName << "."; Fatal(msg.str()); } return ""; // to avoid warning diff --git a/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp new file mode 100644 index 000000000..4a6a8c130 --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateLastVertexInteractionSplittingActor.cpp @@ -0,0 +1,21 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ +#include + +namespace py = pybind11; +#include "GateLastVertexInteractionSplittingActor.h" + +void init_GateLastVertexInteractionSplittingActor(py::module &m) { + + py::class_>( + m, "GateLastVertexInteractionSplittingActor") + .def_readwrite( + "fListOfVolumeAncestor", + &GateLastVertexInteractionSplittingActor::fListOfVolumeAncestor) + .def(py::init()); +} diff --git a/core/opengate_core/opengate_lib/pyGateLastVertexSource.cpp b/core/opengate_core/opengate_lib/pyGateLastVertexSource.cpp new file mode 100644 index 000000000..0cb59f146 --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateLastVertexSource.cpp @@ -0,0 +1,22 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include + +namespace py = pybind11; + +#include "GateLastVertexSource.h" + +void init_GateLastVertexSource(py::module &m) { + + py::class_(m, "GateLastVertexSource") + .def(py::init()) + .def("InitializeUserInfo", &GateLastVertexSource::InitializeUserInfo) + // If needed: add your own class functions that will be accessible from + // python side. + ; +} diff --git a/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp b/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp new file mode 100644 index 000000000..a5cb86c35 --- /dev/null +++ b/core/opengate_core/opengate_lib/pyGateOptrComptPseudoTransportationActor.cpp @@ -0,0 +1,20 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ +#include + +namespace py = pybind11; +#include "G4VBiasingOperator.hh" +#include "GateOptrComptPseudoTransportationActor.h" + +void init_GateOptrComptPseudoTransportationActor(py::module &m) { + + py::class_< + GateOptrComptPseudoTransportationActor, G4VBiasingOperator, GateVActor, + std::unique_ptr>( + m, "GateOptrComptPseudoTransportationActor") + .def(py::init()); +} diff --git a/core/opengate_core/opengate_lib/tree.hh b/core/opengate_core/opengate_lib/tree.hh new file mode 100644 index 000000000..4f78d5b8a --- /dev/null +++ b/core/opengate_core/opengate_lib/tree.hh @@ -0,0 +1,3412 @@ + +// STL-like templated tree class. +// +// Copyright (C) 2001-2024 Kasper Peeters +// Distributed under the GNU General Public License version 3. +// +// Special permission to use tree.hh under the conditions of a +// different license can be requested from the author. + +/** \mainpage tree.hh + \author Kasper Peeters + \version 3.20 + \date 2024-04-12 + \see http://github.com/kpeeters/tree.hh/ + + The tree.hh library for C++ provides an STL-like container class + for n-ary trees, templated over the data stored at the + nodes. Various types of iterators are provided (post-order, + pre-order, and others). Where possible the access methods are + compatible with the STL or alternative algorithms are + available. +*/ + + +#ifndef tree_hh_ +#define tree_hh_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// A node in the tree, combining links to other nodes as well as the actual data. +template +class tree_node_ { // size: 5*4=20 bytes (on 32 bit arch), can be reduced by 8. + public: + tree_node_(); + tree_node_(const T&); + tree_node_(T&&); + + tree_node_ *parent; + tree_node_ *first_child, *last_child; + tree_node_ *prev_sibling, *next_sibling; + T data; +}; + +template +tree_node_::tree_node_() + : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0) + { + } + +template +tree_node_::tree_node_(const T& val) + : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), data(val) + { + } + +template +tree_node_::tree_node_(T&& val) + : parent(0), first_child(0), last_child(0), prev_sibling(0), next_sibling(0), data(val) + { + } + +// Throw an exception with a stacktrace. + +//template +//void throw_with_trace(const E& e) +// { +// throw boost::enable_error_info(e) +// << traced(boost::stacktrace::stacktrace()); +// } + +class navigation_error : public std::logic_error { + public: + navigation_error(const std::string& s) : std::logic_error(s) + { +// assert(1==0); +// std::ostringstream str; +// std::cerr << boost::stacktrace::stacktrace() << std::endl; +// str << boost::stacktrace::stacktrace(); +// stacktrace=str.str(); + } + +// virtual const char *what() const noexcept override +// { +// return (std::logic_error::what()+std::string("; ")+stacktrace).c_str(); +// } +// +// std::string stacktrace; +}; + +template > > +class tree { + protected: + typedef tree_node_ tree_node; + public: + /// Value of the data stored at a node. + typedef T value_type; + + class iterator_base; + class pre_order_iterator; + class post_order_iterator; + class sibling_iterator; + class leaf_iterator; + + tree(); // empty constructor + tree(const T&); // constructor setting given element as head + tree(const iterator_base&); + tree(const tree&); // copy constructor + tree(tree&&); // move constructor + ~tree(); + tree& operator=(const tree&); // copy assignment + tree& operator=(tree&&); // move assignment + + /// Base class for iterators, only pointers stored, no traversal logic. +#ifdef __SGI_STL_PORT + class iterator_base : public stlport::bidirectional_iterator { +#else + class iterator_base { +#endif + public: + typedef T value_type; + typedef T* pointer; + typedef T& reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + iterator_base(); + iterator_base(tree_node *); + + T& operator*() const; + T* operator->() const; + + /// When called, the next increment/decrement skips children of this node. + void skip_children(); + void skip_children(bool skip); + /// Number of children of the node pointed to by the iterator. + unsigned int number_of_children() const; + + sibling_iterator begin() const; + sibling_iterator end() const; + + tree_node *node; + protected: + bool skip_current_children_; + }; + + /// Depth-first iterator, first accessing the node, then its children. + class pre_order_iterator : public iterator_base { + public: + pre_order_iterator(); + pre_order_iterator(tree_node *); + pre_order_iterator(const iterator_base&); + pre_order_iterator(const sibling_iterator&); + + bool operator==(const pre_order_iterator&) const; + bool operator!=(const pre_order_iterator&) const; + pre_order_iterator& operator++(); + pre_order_iterator& operator--(); + pre_order_iterator operator++(int); + pre_order_iterator operator--(int); + pre_order_iterator& operator+=(unsigned int); + pre_order_iterator& operator-=(unsigned int); + + pre_order_iterator& next_skip_children(); + }; + + /// Depth-first iterator, first accessing the children, then the node itself. + class post_order_iterator : public iterator_base { + public: + post_order_iterator(); + post_order_iterator(tree_node *); + post_order_iterator(const iterator_base&); + post_order_iterator(const sibling_iterator&); + + bool operator==(const post_order_iterator&) const; + bool operator!=(const post_order_iterator&) const; + post_order_iterator& operator++(); + post_order_iterator& operator--(); + post_order_iterator operator++(int); + post_order_iterator operator--(int); + post_order_iterator& operator+=(unsigned int); + post_order_iterator& operator-=(unsigned int); + + /// Set iterator to the first child as deep as possible down the tree. + void descend_all(); + }; + + /// Breadth-first iterator, using a queue + class breadth_first_queued_iterator : public iterator_base { + public: + breadth_first_queued_iterator(); + breadth_first_queued_iterator(tree_node *); + breadth_first_queued_iterator(const iterator_base&); + + bool operator==(const breadth_first_queued_iterator&) const; + bool operator!=(const breadth_first_queued_iterator&) const; + breadth_first_queued_iterator& operator++(); + breadth_first_queued_iterator operator++(int); + breadth_first_queued_iterator& operator+=(unsigned int); + + private: + std::queue traversal_queue; + }; + + /// The default iterator types throughout the tree class. + typedef pre_order_iterator iterator; + typedef breadth_first_queued_iterator breadth_first_iterator; + + /// Iterator which traverses only the nodes at a given depth from the root. + class fixed_depth_iterator : public iterator_base { + public: + fixed_depth_iterator(); + fixed_depth_iterator(tree_node *); + fixed_depth_iterator(const iterator_base&); + fixed_depth_iterator(const sibling_iterator&); + fixed_depth_iterator(const fixed_depth_iterator&); + + void swap(fixed_depth_iterator&, fixed_depth_iterator&); + fixed_depth_iterator& operator=(fixed_depth_iterator); + + bool operator==(const fixed_depth_iterator&) const; + bool operator!=(const fixed_depth_iterator&) const; + fixed_depth_iterator& operator++(); + fixed_depth_iterator& operator--(); + fixed_depth_iterator operator++(int); + fixed_depth_iterator operator--(int); + fixed_depth_iterator& operator+=(unsigned int); + fixed_depth_iterator& operator-=(unsigned int); + + tree_node *top_node; + }; + + /// Iterator which traverses only the nodes which are siblings of each other. + class sibling_iterator : public iterator_base { + public: + sibling_iterator(); + sibling_iterator(tree_node *); + sibling_iterator(const sibling_iterator&); + sibling_iterator(const iterator_base&); + + void swap(sibling_iterator&, sibling_iterator&); + sibling_iterator& operator=(sibling_iterator); + + bool operator==(const sibling_iterator&) const; + bool operator!=(const sibling_iterator&) const; + sibling_iterator& operator++(); + sibling_iterator& operator--(); + sibling_iterator operator++(int); + sibling_iterator operator--(int); + sibling_iterator& operator+=(unsigned int); + sibling_iterator& operator-=(unsigned int); + + tree_node *range_first() const; + tree_node *range_last() const; + tree_node *parent_; + private: + void set_parent_(); + }; + + /// Iterator which traverses only the leaves. + class leaf_iterator : public iterator_base { + public: + leaf_iterator(); + leaf_iterator(tree_node *, tree_node *top=0); + leaf_iterator(const sibling_iterator&); + leaf_iterator(const iterator_base&); + + bool operator==(const leaf_iterator&) const; + bool operator!=(const leaf_iterator&) const; + leaf_iterator& operator++(); + leaf_iterator& operator--(); + leaf_iterator operator++(int); + leaf_iterator operator--(int); + leaf_iterator& operator+=(unsigned int); + leaf_iterator& operator-=(unsigned int); + private: + tree_node *top_node; + }; + + /// Return iterator to the beginning of the tree. + inline pre_order_iterator begin() const; + /// Return iterator to the end of the tree. + inline pre_order_iterator end() const; + /// Return post-order iterator to the beginning of the tree. + post_order_iterator begin_post() const; + /// Return post-order end iterator of the tree. + post_order_iterator end_post() const; + /// Return fixed-depth iterator to the first node at a given depth from the given iterator. + /// If 'walk_back=true', a depth=0 iterator will be taken from the beginning of the sibling + /// range, not the current node. + fixed_depth_iterator begin_fixed(const iterator_base&, unsigned int, bool walk_back=true) const; + /// Return fixed-depth end iterator. + fixed_depth_iterator end_fixed(const iterator_base&, unsigned int) const; + /// Return breadth-first iterator to the first node at a given depth. + breadth_first_queued_iterator begin_breadth_first() const; + /// Return breadth-first end iterator. + breadth_first_queued_iterator end_breadth_first() const; + /// Return sibling iterator to the first child of given node. + static sibling_iterator begin(const iterator_base&); + /// Return sibling end iterator for children of given node. + static sibling_iterator end(const iterator_base&); + /// Return leaf iterator to the first leaf of the tree. + leaf_iterator begin_leaf() const; + /// Return leaf end iterator for entire tree. + leaf_iterator end_leaf() const; + /// Return leaf iterator to the first leaf of the subtree at the given node. + leaf_iterator begin_leaf(const iterator_base& top) const; + /// Return leaf end iterator for the subtree at the given node. + leaf_iterator end_leaf(const iterator_base& top) const; + + typedef std::vector path_t; + /// Return a path (to be taken from the 'top' node) corresponding to a node in the tree. + /// The first integer in path_t is the number of steps you need to go 'right' in the sibling + /// chain (so 0 if we go straight to the children). + path_t path_from_iterator(const iterator_base& iter, const iterator_base& top) const; + /// Return an iterator given a path from the 'top' node. + iterator iterator_from_path(const path_t&, const iterator_base& top) const; + + /// Return iterator to the parent of a node. Throws a `navigation_error` if the node + /// does not have a parent. + template static iter parent(iter); + /// Return iterator to the previous sibling of a node. + template static iter previous_sibling(iter); + /// Return iterator to the next sibling of a node. + template static iter next_sibling(iter); + /// Return iterator to the next node at a given depth. + template iter next_at_same_depth(iter) const; + + /// Erase all nodes of the tree. + void clear(); + /// Erase element at position pointed to by iterator, return incremented iterator. + template iter erase(iter); + /// Erase all children of the node pointed to by iterator. + void erase_children(const iterator_base&); + /// Erase all siblings to the right of the iterator. + void erase_right_siblings(const iterator_base&); + /// Erase all siblings to the left of the iterator. + void erase_left_siblings(const iterator_base&); + + /// Insert empty node as last/first child of node pointed to by position. + template iter append_child(iter position); + template iter prepend_child(iter position); + /// Insert node as last/first child of node pointed to by position. + template iter append_child(iter position, const T& x); + template iter append_child(iter position, T&& x); + template iter prepend_child(iter position, const T& x); + template iter prepend_child(iter position, T&& x); + /// Append the node (plus its children) at other_position as last/first child of position. + template iter append_child(iter position, iter other_position); + template iter prepend_child(iter position, iter other_position); + /// Append the nodes in the from-to range (plus their children) as last/first children of position. + template iter append_children(iter position, sibling_iterator from, sibling_iterator to); + template iter prepend_children(iter position, sibling_iterator from, sibling_iterator to); + + /// Short-hand to insert topmost node in otherwise empty tree. + pre_order_iterator set_head(const T& x); + pre_order_iterator set_head(T&& x); + /// Insert node as previous sibling of node pointed to by position. + template iter insert(iter position, const T& x); + template iter insert(iter position, T&& x); + /// Specialisation of previous member. + sibling_iterator insert(sibling_iterator position, const T& x); + sibling_iterator insert(sibling_iterator position, T&& x); + /// Insert node (with children) pointed to by subtree as previous sibling of node pointed to by position. + /// Does not change the subtree itself (use move_in or move_in_below for that). + template iter insert_subtree(iter position, const iterator_base& subtree); + /// Insert node as next sibling of node pointed to by position. + template iter insert_after(iter position, const T& x); + template iter insert_after(iter position, T&& x); + /// Insert node (with children) pointed to by subtree as next sibling of node pointed to by position. + template iter insert_subtree_after(iter position, const iterator_base& subtree); + + /// Replace node at 'position' with other node (keeping same children); 'position' becomes invalid. + template iter replace(iter position, const T& x); + /// Replace node at 'position' with subtree starting at 'from' (do not erase subtree at 'from'); see above. + template iter replace(iter position, const iterator_base& from); + /// Replace string of siblings (plus their children) with copy of a new string (with children); see above + sibling_iterator replace(sibling_iterator orig_begin, sibling_iterator orig_end, + sibling_iterator new_begin, sibling_iterator new_end); + + /// Move all children of node at 'position' to be siblings, returns position. + template iter flatten(iter position); + /// Move nodes in range to be children of 'position'. + template iter reparent(iter position, sibling_iterator begin, sibling_iterator end); + /// Move all child nodes of 'from' to be children of 'position'. + template iter reparent(iter position, iter from); + + /// Replace node with a new node, making the old node (plus subtree) a child of the new node. + template iter wrap(iter position, const T& x); + /// Replace the range of sibling nodes (plus subtrees), making these children of the new node. + template iter wrap(iter from, iter to, const T& x); + + /// Move 'source' node (plus its children) to become the next sibling of 'target'. + template iter move_after(iter target, iter source); + /// Move 'source' node (plus its children) to become the previous sibling of 'target'. + template iter move_before(iter target, iter source); + sibling_iterator move_before(sibling_iterator target, sibling_iterator source); + /// Move 'source' node (plus its children) to become the node at 'target' (erasing the node at 'target'). + template iter move_ontop(iter target, iter source); + + /// Extract the subtree starting at the indicated node, removing it from the original tree. + tree move_out(iterator); + /// Inverse of take_out: inserts the given tree as previous sibling of indicated node by a + /// move operation, that is, the given tree becomes empty. Returns iterator to the top node. + template iter move_in(iter, tree&); + /// As above, but now make the tree the last child of the indicated node. + template iter move_in_below(iter, tree&); + /// As above, but now make the tree the nth child of the indicated node (if possible). + template iter move_in_as_nth_child(iter, size_t, tree&); + + /// Merge with other tree, creating new branches and leaves only if they are not already present. + void merge(sibling_iterator, sibling_iterator, sibling_iterator, sibling_iterator, + bool duplicate_leaves=false); + /// As above, but using two trees with a single top node at the 'to' and 'from' positions. + void merge(iterator to, iterator from, bool duplicate_leaves); + /// Sort (std::sort only moves values of nodes, this one moves children as well). + void sort(sibling_iterator from, sibling_iterator to, bool deep=false); + template + void sort(sibling_iterator from, sibling_iterator to, StrictWeakOrdering comp, bool deep=false); + /// Compare two ranges of nodes (compares nodes as well as tree structure). + template + bool equal(const iter& one, const iter& two, const iter& three) const; + template + bool equal(const iter& one, const iter& two, const iter& three, BinaryPredicate) const; + template + bool equal_subtree(const iter& one, const iter& two) const; + template + bool equal_subtree(const iter& one, const iter& two, BinaryPredicate) const; + /// Extract a new tree formed by the range of siblings plus all their children. + tree subtree(sibling_iterator from, sibling_iterator to) const; + void subtree(tree&, sibling_iterator from, sibling_iterator to) const; + /// Exchange the node (plus subtree) with its sibling node (do nothing if no sibling present). + void swap(sibling_iterator it); + /// Exchange two nodes (plus subtrees). The iterators will remain valid and keep + /// pointing to the same nodes, which now sit at different locations in the tree. + void swap(iterator, iterator); + + /// Count the total number of nodes. + size_t size() const; + /// Count the total number of nodes below the indicated node (plus one). + size_t size(const iterator_base&) const; + /// Check if tree is empty. + bool empty() const; + /// Compute the depth to the root or to a fixed other iterator. + static int depth(const iterator_base&); + static int depth(const iterator_base&, const iterator_base&); + /// Compute the depth to the root, counting all levels for which predicate returns true. + template + static int depth(const iterator_base&, Predicate p); + /// Compute the depth distance between two nodes, counting all levels for which predicate returns true. + template + static int distance(const iterator_base& top, const iterator_base& bottom, Predicate p); + /// Determine the maximal depth of the tree. An empty tree has max_depth=-1. + int max_depth() const; + /// Determine the maximal depth of the tree with top node at the given position. + int max_depth(const iterator_base&) const; + /// Count the number of children of node at position. + static unsigned int number_of_children(const iterator_base&); + /// Count the number of siblings (left and right) of node at iterator. Total nodes at this level is +1. + unsigned int number_of_siblings(const iterator_base&) const; + /// Determine whether node at position is in the subtrees with indicated top node. + bool is_in_subtree(const iterator_base& position, const iterator_base& top) const; + /// Determine whether node at position is in the subtrees with root in the range. + bool is_in_subtree(const iterator_base& position, const iterator_base& begin, + const iterator_base& end) const; + /// Determine whether the iterator is an 'end' iterator and thus not actually pointing to a node. + bool is_valid(const iterator_base&) const; + /// Determine whether the iterator is one of the 'head' nodes at the top level, i.e. has no parent. + static bool is_head(const iterator_base&); + /// Find the lowest common ancestor of two nodes, that is, the deepest node such that + /// both nodes are descendants of it. + iterator lowest_common_ancestor(const iterator_base&, const iterator_base &) const; + + /// Determine the index of a node in the range of siblings to which it belongs. + unsigned int index(sibling_iterator it) const; + /// Inverse of 'index': return the n-th child of the node at position. + static sibling_iterator child(const iterator_base& position, unsigned int); + /// Return iterator to the sibling indicated by index + sibling_iterator sibling(const iterator_base& position, unsigned int) const; + + /// For debugging only: verify internal consistency by inspecting all pointers in the tree + /// (which will also trigger a valgrind error in case something got corrupted). + void debug_verify_consistency() const; + + /// Comparator class for iterators (compares pointer values; why doesn't this work automatically?) + class iterator_base_less { + public: + bool operator()(const typename tree::iterator_base& one, + const typename tree::iterator_base& two) const + { + return one.node < two.node; + } + }; + tree_node *head, *feet; // head/feet are always dummy; if an iterator points to them it is invalid + private: + tree_node_allocator alloc_; + void head_initialise_(); + void copy_(const tree& other); + + /// Comparator class for two nodes of a tree (used for sorting and searching). + template + class compare_nodes { + public: + compare_nodes(StrictWeakOrdering comp) : comp_(comp) {} + + bool operator()(const tree_node *a, const tree_node *b) const + { + return comp_(a->data, b->data); + } + private: + StrictWeakOrdering comp_; + }; + }; + +//template +//class iterator_base_less { +// public: +// bool operator()(const typename tree::iterator_base& one, +// const typename tree::iterator_base& two) const +// { +// txtout << "operatorclass<" << one.node < two.node << std::endl; +// return one.node < two.node; +// } +//}; + +// template +// bool operator<(const typename tree::iterator& one, +// const typename tree::iterator& two) +// { +// txtout << "operator< " << one.node < two.node << std::endl; +// if(one.node < two.node) return true; +// return false; +// } +// +// template +// bool operator==(const typename tree::iterator& one, +// const typename tree::iterator& two) +// { +// txtout << "operator== " << one.node == two.node << std::endl; +// if(one.node == two.node) return true; +// return false; +// } +// +// template +// bool operator>(const typename tree::iterator_base& one, +// const typename tree::iterator_base& two) +// { +// txtout << "operator> " << one.node < two.node << std::endl; +// if(one.node > two.node) return true; +// return false; +// } + + + +// Tree + +template +tree::tree() + { + head_initialise_(); + } + +template +tree::tree(const T& x) + { + head_initialise_(); + set_head(x); + } + +template +tree::tree(tree&& x) + { + head_initialise_(); + if(x.head->next_sibling!=x.feet) { // move tree if non-empty only + head->next_sibling=x.head->next_sibling; + feet->prev_sibling=x.feet->prev_sibling; + x.head->next_sibling->prev_sibling=head; + x.feet->prev_sibling->next_sibling=feet; + x.head->next_sibling=x.feet; + x.feet->prev_sibling=x.head; + } + } + +template +tree::tree(const iterator_base& other) + { + head_initialise_(); + set_head((*other)); + replace(begin(), other); + } + +template +tree::~tree() + { + clear(); + std::allocator_traits::destroy(alloc_, head); + std::allocator_traits::destroy(alloc_, feet); + std::allocator_traits::deallocate(alloc_, head, 1); + std::allocator_traits::deallocate(alloc_, feet, 1); + } + +template +void tree::head_initialise_() + { + head = std::allocator_traits::allocate(alloc_, 1, 0); + feet = std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, head, tree_node_()); + std::allocator_traits::construct(alloc_, feet, tree_node_()); + + head->parent=0; + head->first_child=0; + head->last_child=0; + head->prev_sibling=0; //head; + head->next_sibling=feet; //head; + + feet->parent=0; + feet->first_child=0; + feet->last_child=0; + feet->prev_sibling=head; + feet->next_sibling=0; + } + +template +tree& tree::operator=(const tree& other) + { + if(this != &other) + copy_(other); + return *this; + } + +template +tree& tree::operator=(tree&& x) + { + if(this != &x) { + clear(); // clear any existing data. + + head->next_sibling=x.head->next_sibling; + feet->prev_sibling=x.feet->prev_sibling; + x.head->next_sibling->prev_sibling=head; + x.feet->prev_sibling->next_sibling=feet; + x.head->next_sibling=x.feet; + x.feet->prev_sibling=x.head; + } + return *this; + } + +template +tree::tree(const tree& other) + { + head_initialise_(); + copy_(other); + } + +template +void tree::copy_(const tree& other) + { + clear(); + pre_order_iterator it=other.begin(), to=begin(); + while(it!=other.end()) { + to=insert(to, (*it)); + it.skip_children(); + ++it; + } + to=begin(); + it=other.begin(); + while(it!=other.end()) { + to=replace(to, it); + to.skip_children(); + it.skip_children(); + ++to; + ++it; + } + } + +template +void tree::clear() + { + if(head) + while(head->next_sibling!=feet) + erase(pre_order_iterator(head->next_sibling)); + } + +template +void tree::erase_children(const iterator_base& it) + { +// std::cout << "erase_children " << it.node << std::endl; + if(it.node==0) return; + + tree_node *cur=it.node->first_child; + tree_node *prev=0; + + while(cur!=0) { + prev=cur; + cur=cur->next_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->first_child=0; + it.node->last_child=0; +// std::cout << "exit" << std::endl; + } + +template +void tree::erase_right_siblings(const iterator_base& it) + { + if(it.node==0) return; + + tree_node *cur=it.node->next_sibling; + tree_node *prev=0; + + while(cur!=0) { + prev=cur; + cur=cur->next_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->next_sibling=0; + if(it.node->parent!=0) + it.node->parent->last_child=it.node; + } + +template +void tree::erase_left_siblings(const iterator_base& it) + { + if(it.node==0) return; + + tree_node *cur=it.node->prev_sibling; + tree_node *prev=0; + + while(cur!=0) { + prev=cur; + cur=cur->prev_sibling; + erase_children(pre_order_iterator(prev)); + std::allocator_traits::destroy(alloc_, prev); + std::allocator_traits::deallocate(alloc_, prev, 1); + } + it.node->prev_sibling=0; + if(it.node->parent!=0) + it.node->parent->first_child=it.node; + } + +template +template +iter tree::erase(iter it) + { + tree_node *cur=it.node; + assert(cur!=head); + iter ret=it; + ret.skip_children(); + ++ret; + erase_children(it); + if(cur->prev_sibling==0) { + cur->parent->first_child=cur->next_sibling; + } + else { + cur->prev_sibling->next_sibling=cur->next_sibling; + } + if(cur->next_sibling==0) { + cur->parent->last_child=cur->prev_sibling; + } + else { + cur->next_sibling->prev_sibling=cur->prev_sibling; + } + + std::allocator_traits::destroy(alloc_, cur); + std::allocator_traits::deallocate(alloc_, cur, 1); + return ret; + } + +template +typename tree::pre_order_iterator tree::begin() const + { + return pre_order_iterator(head->next_sibling); + } + +template +typename tree::pre_order_iterator tree::end() const + { + return pre_order_iterator(feet); + } + +template +typename tree::breadth_first_queued_iterator tree::begin_breadth_first() const + { + return breadth_first_queued_iterator(head->next_sibling); + } + +template +typename tree::breadth_first_queued_iterator tree::end_breadth_first() const + { + return breadth_first_queued_iterator(); + } + +template +typename tree::post_order_iterator tree::begin_post() const + { + tree_node *tmp=head->next_sibling; + if(tmp!=feet) { + while(tmp->first_child) + tmp=tmp->first_child; + } + return post_order_iterator(tmp); + } + +template +typename tree::post_order_iterator tree::end_post() const + { + return post_order_iterator(feet); + } + +template +typename tree::fixed_depth_iterator tree::begin_fixed(const iterator_base& pos, unsigned int dp, bool walk_back) const + { + typename tree::fixed_depth_iterator ret; + ret.top_node=pos.node; + + tree_node *tmp=pos.node; + unsigned int curdepth=0; + while(curdepthfirst_child==0) { + if(tmp->next_sibling==0) { + // try to walk up and then right again + do { + if(tmp==ret.top_node) + throw std::range_error("tree: begin_fixed out of range"); + tmp=tmp->parent; + if(tmp==0) + throw std::range_error("tree: begin_fixed out of range"); + --curdepth; + } while(tmp->next_sibling==0); + } + tmp=tmp->next_sibling; + } + tmp=tmp->first_child; + ++curdepth; + } + + // Now walk back to the first sibling in this range. + if(walk_back) + while(tmp->prev_sibling!=0) + tmp=tmp->prev_sibling; + + ret.node=tmp; + return ret; + } + +template +typename tree::fixed_depth_iterator tree::end_fixed(const iterator_base& pos, unsigned int dp) const + { + assert(1==0); // FIXME: not correct yet: use is_valid() as a temporary workaround + tree_node *tmp=pos.node; + unsigned int curdepth=1; + while(curdepthfirst_child==0) { + tmp=tmp->next_sibling; + if(tmp==0) + throw std::range_error("tree: end_fixed out of range"); + } + tmp=tmp->first_child; + ++curdepth; + } + return tmp; + } + +template +typename tree::sibling_iterator tree::begin(const iterator_base& pos) + { + assert(pos.node!=0); + if(pos.node->first_child==0) { + return end(pos); + } + return pos.node->first_child; + } + +template +typename tree::sibling_iterator tree::end(const iterator_base& pos) + { + sibling_iterator ret(0); + ret.parent_=pos.node; + return ret; + } + +template +typename tree::leaf_iterator tree::begin_leaf() const + { + tree_node *tmp=head->next_sibling; + if(tmp!=feet) { + while(tmp->first_child) + tmp=tmp->first_child; + } + return leaf_iterator(tmp); + } + +template +typename tree::leaf_iterator tree::end_leaf() const + { + return leaf_iterator(feet); + } + +template +typename tree::path_t tree::path_from_iterator(const iterator_base& iter, const iterator_base& top) const + { + path_t path; + tree_node *walk=iter.node; + + do { + if(path.size()>0) + walk=walk->parent; + int num=0; + while(walk!=top.node && walk->prev_sibling!=0 && walk->prev_sibling!=head) { + ++num; + walk=walk->prev_sibling; + } + path.push_back(num); + } + while(walk->parent!=0 && walk!=top.node); + + std::reverse(path.begin(), path.end()); + return path; + } + +template +typename tree::iterator tree::iterator_from_path(const path_t& path, const iterator_base& top) const + { + iterator it=top; + tree_node *walk=it.node; + + for(size_t step=0; step0) + walk=walk->first_child; + if(walk==0) + throw std::range_error("tree::iterator_from_path: no more nodes at step "+std::to_string(step)); + + for(int i=0; inext_sibling; + if(walk==0) + throw std::range_error("tree::iterator_from_path: out of siblings at step "+std::to_string(step)); + } + } + it.node=walk; + return it; + } + +template +typename tree::leaf_iterator tree::begin_leaf(const iterator_base& top) const + { + tree_node *tmp=top.node; + while(tmp->first_child) + tmp=tmp->first_child; + return leaf_iterator(tmp, top.node); + } + +template +typename tree::leaf_iterator tree::end_leaf(const iterator_base& top) const + { + return leaf_iterator(top.node, top.node); + } + +template +template +iter tree::parent(iter position) + { + if(position.node==0) + throw navigation_error("tree: attempt to navigate from null iterator."); + + if(position.node->parent==0) + throw navigation_error("tree: attempt to navigate up past head node."); + + return iter(position.node->parent); + } + +template +template +iter tree::previous_sibling(iter position) + { + assert(position.node!=0); + iter ret(position); + ret.node=position.node->prev_sibling; + return ret; + } + +template +template +iter tree::next_sibling(iter position) + { + assert(position.node!=0); + iter ret(position); + ret.node=position.node->next_sibling; + return ret; + } + +template +template +iter tree::next_at_same_depth(iter position) const + { + // We make use of a temporary fixed_depth iterator to implement this. + + typename tree::fixed_depth_iterator tmp(position.node); + + ++tmp; + return iter(tmp); + + // assert(position.node!=0); + // iter ret(position); + // + // if(position.node->next_sibling) { + // ret.node=position.node->next_sibling; + // } + // else { + // int relative_depth=0; + // upper: + // do { + // ret.node=ret.node->parent; + // if(ret.node==0) return ret; + // --relative_depth; + // } while(ret.node->next_sibling==0); + // lower: + // ret.node=ret.node->next_sibling; + // while(ret.node->first_child==0) { + // if(ret.node->next_sibling==0) + // goto upper; + // ret.node=ret.node->next_sibling; + // if(ret.node==0) return ret; + // } + // while(relative_depth<0 && ret.node->first_child!=0) { + // ret.node=ret.node->first_child; + // ++relative_depth; + // } + // if(relative_depth<0) { + // if(ret.node->next_sibling==0) goto upper; + // else goto lower; + // } + // } + // return ret; + } + +template +template +iter tree::append_child(iter position) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, tree_node_()); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->last_child!=0) { + position.node->last_child->next_sibling=tmp; + } + else { + position.node->first_child=tmp; + } + tmp->prev_sibling=position.node->last_child; + position.node->last_child=tmp; + tmp->next_sibling=0; + return tmp; + } + +template +template +iter tree::prepend_child(iter position) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, tree_node_()); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->first_child!=0) { + position.node->first_child->prev_sibling=tmp; + } + else { + position.node->last_child=tmp; + } + tmp->next_sibling=position.node->first_child; + position.node->prev_child=tmp; + tmp->prev_sibling=0; + return tmp; + } + +template +template +iter tree::append_child(iter position, const T& x) + { + // If your program fails here you probably used 'append_child' to add the top + // node to an empty tree. From version 1.45 the top element should be added + // using 'insert'. See the documentation for further information, and sorry about + // the API change. + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->last_child!=0) { + position.node->last_child->next_sibling=tmp; + } + else { + position.node->first_child=tmp; + } + tmp->prev_sibling=position.node->last_child; + position.node->last_child=tmp; + tmp->next_sibling=0; + return tmp; + } + +template +template +iter tree::append_child(iter position, T&& x) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); // Here is where the move semantics kick in + std::swap(tmp->data, x); + + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->last_child!=0) { + position.node->last_child->next_sibling=tmp; + } + else { + position.node->first_child=tmp; + } + tmp->prev_sibling=position.node->last_child; + position.node->last_child=tmp; + tmp->next_sibling=0; + return tmp; + } + +template +template +iter tree::prepend_child(iter position, const T& x) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->first_child!=0) { + position.node->first_child->prev_sibling=tmp; + } + else { + position.node->last_child=tmp; + } + tmp->next_sibling=position.node->first_child; + position.node->first_child=tmp; + tmp->prev_sibling=0; + return tmp; + } + +template +template +iter tree::prepend_child(iter position, T&& x) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); + + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node; + if(position.node->first_child!=0) { + position.node->first_child->prev_sibling=tmp; + } + else { + position.node->last_child=tmp; + } + tmp->next_sibling=position.node->first_child; + position.node->first_child=tmp; + tmp->prev_sibling=0; + return tmp; + } + +template +template +iter tree::append_child(iter position, iter other) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + sibling_iterator aargh=append_child(position, value_type()); + return replace(aargh, other); + } + +template +template +iter tree::prepend_child(iter position, iter other) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + sibling_iterator aargh=prepend_child(position, value_type()); + return replace(aargh, other); + } + +template +template +iter tree::append_children(iter position, sibling_iterator from, sibling_iterator to) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + iter ret=from; + + while(from!=to) { + insert_subtree(position.end(), from); + ++from; + } + return ret; + } + +template +template +iter tree::prepend_children(iter position, sibling_iterator from, sibling_iterator to) + { + assert(position.node!=head); + assert(position.node!=feet); + assert(position.node); + + if(from==to) return from; // should return end of tree? + + iter ret; + do { + --to; + ret=insert_subtree(position.begin(), to); + } + while(to!=from); + + return ret; + } + +template +typename tree::pre_order_iterator tree::set_head(const T& x) + { + assert(head->next_sibling==feet); + return insert(iterator(feet), x); + } + +template +typename tree::pre_order_iterator tree::set_head(T&& x) + { + assert(head->next_sibling==feet); + return insert(iterator(feet), x); + } + +template +template +iter tree::insert(iter position, const T& x) + { + if(position.node==0) { + position.node=feet; // Backward compatibility: when calling insert on a null node, + // insert before the feet. + } + assert(position.node!=head); // Cannot insert before head. + + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->next_sibling=position.node; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + + if(tmp->prev_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +template +iter tree::insert(iter position, T&& x) + { + if(position.node==0) { + position.node=feet; // Backward compatibility: when calling insert on a null node, + // insert before the feet. + } + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // Move semantics + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->next_sibling=position.node; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + + if(tmp->prev_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +typename tree::sibling_iterator tree::insert(sibling_iterator position, const T& x) + { + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->next_sibling=position.node; + if(position.node==0) { // iterator points to end of a subtree + tmp->parent=position.parent_; + tmp->prev_sibling=position.range_last(); + tmp->parent->last_child=tmp; + } + else { + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + } + + if(tmp->prev_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +typename tree::sibling_iterator tree::insert(sibling_iterator position, T&& x) + { + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // Move semantics + + tmp->first_child=0; + tmp->last_child=0; + + tmp->next_sibling=position.node; + if(position.node==0) { // iterator points to end of a subtree + tmp->parent=position.parent_; + tmp->prev_sibling=position.range_last(); + tmp->parent->last_child=tmp; + } + else { + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node->prev_sibling; + position.node->prev_sibling=tmp; + } + + if(tmp->prev_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->first_child=tmp; + } + else + tmp->prev_sibling->next_sibling=tmp; + return tmp; + } + +template +template +iter tree::insert_after(iter position, const T& x) + { + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, x); + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node; + tmp->next_sibling=position.node->next_sibling; + position.node->next_sibling=tmp; + + if(tmp->next_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->last_child=tmp; + } + else { + tmp->next_sibling->prev_sibling=tmp; + } + return tmp; + } + +template +template +iter tree::insert_after(iter position, T&& x) + { + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp); + std::swap(tmp->data, x); // move semantics + tmp->first_child=0; + tmp->last_child=0; + + tmp->parent=position.node->parent; + tmp->prev_sibling=position.node; + tmp->next_sibling=position.node->next_sibling; + position.node->next_sibling=tmp; + + if(tmp->next_sibling==0) { + if(tmp->parent) // when inserting nodes at the head, there is no parent + tmp->parent->last_child=tmp; + } + else { + tmp->next_sibling->prev_sibling=tmp; + } + return tmp; + } + +template +template +iter tree::insert_subtree(iter position, const iterator_base& subtree) + { + // insert dummy + iter it=insert(position, value_type()); + // replace dummy with subtree + return replace(it, subtree); + } + +template +template +iter tree::insert_subtree_after(iter position, const iterator_base& subtree) + { + // insert dummy + iter it=insert_after(position, value_type()); + // replace dummy with subtree + return replace(it, subtree); + } + +// template +// template +// iter tree::insert_subtree(sibling_iterator position, iter subtree) +// { +// // insert dummy +// iter it(insert(position, value_type())); +// // replace dummy with subtree +// return replace(it, subtree); +// } + +template +template +iter tree::replace(iter position, const T& x) + { +// kp::destructor(&position.node->data); +// kp::constructor(&position.node->data, x); + position.node->data=x; +// alloc_.destroy(position.node); +// alloc_.construct(position.node, x); + return position; + } + +template +template +iter tree::replace(iter position, const iterator_base& from) + { + assert(position.node!=head); + tree_node *current_from=from.node; + tree_node *start_from=from.node; + tree_node *current_to =position.node; + + // replace the node at position with head of the replacement tree at from +// std::cout << "warning!" << position.node << std::endl; + erase_children(position); +// std::cout << "no warning!" << std::endl; + tree_node *tmp=std::allocator_traits::allocate(alloc_, 1, 0); + std::allocator_traits::construct(alloc_, tmp, (*from)); + tmp->first_child=0; + tmp->last_child=0; + if(current_to->prev_sibling==0) { + if(current_to->parent!=0) + current_to->parent->first_child=tmp; + } + else { + current_to->prev_sibling->next_sibling=tmp; + } + tmp->prev_sibling=current_to->prev_sibling; + if(current_to->next_sibling==0) { + if(current_to->parent!=0) + current_to->parent->last_child=tmp; + } + else { + current_to->next_sibling->prev_sibling=tmp; + } + tmp->next_sibling=current_to->next_sibling; + tmp->parent=current_to->parent; +// kp::destructor(¤t_to->data); + std::allocator_traits::destroy(alloc_, current_to); + std::allocator_traits::deallocate(alloc_, current_to, 1); + current_to=tmp; + + // only at this stage can we fix 'last' + tree_node *last=from.node->next_sibling; + + pre_order_iterator toit=tmp; + // copy all children + do { + assert(current_from!=0); + if(current_from->first_child != 0) { + current_from=current_from->first_child; + toit=append_child(toit, current_from->data); + } + else { + while(current_from->next_sibling==0 && current_from!=start_from) { + current_from=current_from->parent; + toit=parent(toit); + assert(current_from!=0); + } + current_from=current_from->next_sibling; + if(current_from!=last) { + toit=append_child(parent(toit), current_from->data); + } + } + } + while(current_from!=last); + + return current_to; + } + +template +typename tree::sibling_iterator tree::replace( + sibling_iterator orig_begin, + sibling_iterator orig_end, + sibling_iterator new_begin, + sibling_iterator new_end) + { + tree_node *orig_first=orig_begin.node; + tree_node *new_first=new_begin.node; + tree_node *orig_last=orig_first; + while((++orig_begin)!=orig_end) + orig_last=orig_last->next_sibling; + tree_node *new_last=new_first; + while((++new_begin)!=new_end) + new_last=new_last->next_sibling; + + // insert all siblings in new_first..new_last before orig_first + bool first=true; + pre_order_iterator ret; + while(1==1) { + pre_order_iterator tt=insert_subtree(pre_order_iterator(orig_first), pre_order_iterator(new_first)); + if(first) { + ret=tt; + first=false; + } + if(new_first==new_last) + break; + new_first=new_first->next_sibling; + } + + // erase old range of siblings + bool last=false; + tree_node *next=orig_first; + while(1==1) { + if(next==orig_last) + last=true; + next=next->next_sibling; + erase((pre_order_iterator)orig_first); + if(last) + break; + orig_first=next; + } + return ret; + } + +template +template +iter tree::flatten(iter position) + { + if(position.node->first_child==0) + return position; + + tree_node *tmp=position.node->first_child; + while(tmp) { + tmp->parent=position.node->parent; + tmp=tmp->next_sibling; + } + if(position.node->next_sibling) { + position.node->last_child->next_sibling=position.node->next_sibling; + position.node->next_sibling->prev_sibling=position.node->last_child; + } + else { + position.node->parent->last_child=position.node->last_child; + } + position.node->next_sibling=position.node->first_child; + position.node->next_sibling->prev_sibling=position.node; + position.node->first_child=0; + position.node->last_child=0; + + return position; + } + + +template +template +iter tree::reparent(iter position, sibling_iterator begin, sibling_iterator end) + { + tree_node *first=begin.node; + tree_node *last=first; + + assert(first!=position.node); + + if(begin==end) return begin; + // determine last node + while((++begin)!=end) { + last=last->next_sibling; + } + // move subtree + if(first->prev_sibling==0) { + first->parent->first_child=last->next_sibling; + } + else { + first->prev_sibling->next_sibling=last->next_sibling; + } + if(last->next_sibling==0) { + last->parent->last_child=first->prev_sibling; + } + else { + last->next_sibling->prev_sibling=first->prev_sibling; + } + if(position.node->first_child==0) { + position.node->first_child=first; + position.node->last_child=last; + first->prev_sibling=0; + } + else { + position.node->last_child->next_sibling=first; + first->prev_sibling=position.node->last_child; + position.node->last_child=last; + } + last->next_sibling=0; + + tree_node *pos=first; + for(;;) { + pos->parent=position.node; + if(pos==last) break; + pos=pos->next_sibling; + } + + return first; + } + +template +template iter tree::reparent(iter position, iter from) + { + if(from.node->first_child==0) return position; + return reparent(position, from.node->first_child, end(from)); + } + +template +template iter tree::wrap(iter position, const T& x) + { + assert(position.node!=0); + sibling_iterator fr=position, to=position; + ++to; + iter ret = insert(position, x); + reparent(ret, fr, to); + return ret; + } + +template +template iter tree::wrap(iter from, iter to, const T& x) + { + assert(from.node!=0); + iter ret = insert(from, x); + reparent(ret, from, to); + return ret; + } + +template +template iter tree::move_after(iter target, iter source) + { + tree_node *dst=target.node; + tree_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + if(dst->next_sibling) + if(dst->next_sibling==src) // already in the right spot + return source; + + // take src out of the tree + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + // connect it to the new point + if(dst->next_sibling!=0) dst->next_sibling->prev_sibling=src; + else dst->parent->last_child=src; + src->next_sibling=dst->next_sibling; + dst->next_sibling=src; + src->prev_sibling=dst; + src->parent=dst->parent; + return src; + } + +template +template iter tree::move_before(iter target, iter source) + { + tree_node *dst=target.node; + tree_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + if(dst->prev_sibling) + if(dst->prev_sibling==src) // already in the right spot + return source; + + // take src out of the tree + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + // connect it to the new point + if(dst->prev_sibling!=0) dst->prev_sibling->next_sibling=src; + else dst->parent->first_child=src; + src->prev_sibling=dst->prev_sibling; + dst->prev_sibling=src; + src->next_sibling=dst; + src->parent=dst->parent; + return src; + } + +// specialisation for sibling_iterators +template +typename tree::sibling_iterator tree::move_before(sibling_iterator target, + sibling_iterator source) + { + tree_node *dst=target.node; + tree_node *src=source.node; + tree_node *dst_prev_sibling; + if(dst==0) { // must then be an end iterator + dst_prev_sibling=target.parent_->last_child; + assert(dst_prev_sibling); + } + else dst_prev_sibling=dst->prev_sibling; + assert(src); + + if(dst==src) return source; + if(dst_prev_sibling) + if(dst_prev_sibling==src) // already in the right spot + return source; + + // take src out of the tree + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else src->parent->first_child=src->next_sibling; + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else src->parent->last_child=src->prev_sibling; + + // connect it to the new point + if(dst_prev_sibling!=0) dst_prev_sibling->next_sibling=src; + else target.parent_->first_child=src; + src->prev_sibling=dst_prev_sibling; + if(dst) { + dst->prev_sibling=src; + src->parent=dst->parent; + } + else { + src->parent=dst_prev_sibling->parent; + } + src->next_sibling=dst; + return src; + } + +template +template iter tree::move_ontop(iter target, iter source) + { + tree_node *dst=target.node; + tree_node *src=source.node; + assert(dst); + assert(src); + + if(dst==src) return source; + +// if(dst==src->prev_sibling) { +// +// } + + // remember connection points + tree_node *b_prev_sibling=dst->prev_sibling; + tree_node *b_next_sibling=dst->next_sibling; + tree_node *b_parent=dst->parent; + + // remove target + erase(target); + + // take src out of the tree + if(src->prev_sibling!=0) src->prev_sibling->next_sibling=src->next_sibling; + else { + assert(src->parent!=0); + src->parent->first_child=src->next_sibling; + } + if(src->next_sibling!=0) src->next_sibling->prev_sibling=src->prev_sibling; + else { + assert(src->parent!=0); + src->parent->last_child=src->prev_sibling; + } + + // connect it to the new point + if(b_prev_sibling!=0) b_prev_sibling->next_sibling=src; + else { + assert(b_parent!=0); + b_parent->first_child=src; + } + if(b_next_sibling!=0) b_next_sibling->prev_sibling=src; + else { + assert(b_parent!=0); + b_parent->last_child=src; + } + src->prev_sibling=b_prev_sibling; + src->next_sibling=b_next_sibling; + src->parent=b_parent; + return src; + } + + +template +tree tree::move_out(iterator source) + { + tree ret; + + // Move source node into the 'ret' tree. + ret.head->next_sibling = source.node; + ret.feet->prev_sibling = source.node; + + // Close the links in the current tree. + if(source.node->prev_sibling!=0) + source.node->prev_sibling->next_sibling = source.node->next_sibling; + + if(source.node->next_sibling!=0) + source.node->next_sibling->prev_sibling = source.node->prev_sibling; + + // If the moved-out node was a first or last child of + // the parent, adjust those links. + if(source.node->parent->first_child==source.node) { + if(source.node->next_sibling!=0) + source.node->parent->first_child=source.node->next_sibling; + else + source.node->parent->first_child=0; + } + if(source.node->parent->last_child==source.node) { + if(source.node->prev_sibling!=0) + source.node->parent->last_child=source.node->prev_sibling; + else + source.node->parent->last_child=0; + } + source.node->parent=0; + + // Fix source prev/next links. + source.node->prev_sibling = ret.head; + source.node->next_sibling = ret.feet; + + return ret; // A good compiler will move this, not copy. + } + +template +template iter tree::move_in(iter loc, tree& other) + { + if(other.head->next_sibling==other.feet) return loc; // other tree is empty + + tree_node *other_first_head = other.head->next_sibling; + tree_node *other_last_head = other.feet->prev_sibling; + + sibling_iterator prev(loc); + --prev; + + prev.node->next_sibling = other_first_head; + loc.node->prev_sibling = other_last_head; + other_first_head->prev_sibling = prev.node; + other_last_head->next_sibling = loc.node; + + // Adjust parent pointers. + tree_node *walk=other_first_head; + while(true) { + walk->parent=loc.node->parent; + if(walk==other_last_head) + break; + walk=walk->next_sibling; + } + + // Close other tree. + other.head->next_sibling=other.feet; + other.feet->prev_sibling=other.head; + + return other_first_head; + } + +template +template iter tree::move_in_below(iter loc, tree& other) + { + if(other.head->next_sibling==other.feet) return loc; // other tree is empty + + auto n = other.number_of_children(loc); + return move_in_as_nth_child(loc, n, other); + } + +template +template iter tree::move_in_as_nth_child(iter loc, size_t n, tree& other) + { + if(other.head->next_sibling==other.feet) return loc; // other tree is empty + + tree_node *other_first_head = other.head->next_sibling; + tree_node *other_last_head = other.feet->prev_sibling; + + if(n==0) { + if(loc.node->first_child==0) { + loc.node->first_child=other_first_head; + loc.node->last_child=other_last_head; + other_last_head->next_sibling=0; + other_first_head->prev_sibling=0; + } + else { + loc.node->first_child->prev_sibling=other_last_head; + other_last_head->next_sibling=loc.node->first_child; + loc.node->first_child=other_first_head; + other_first_head->prev_sibling=0; + } + } + else { + --n; + tree_node *walk = loc.node->first_child; + while(true) { + if(walk==0) + throw std::range_error("tree: move_in_as_nth_child position out of range"); + if(n==0) + break; + --n; + walk = walk->next_sibling; + } + if(walk->next_sibling==0) + loc.node->last_child=other_last_head; + else + walk->next_sibling->prev_sibling=other_last_head; + other_last_head->next_sibling=walk->next_sibling; + walk->next_sibling=other_first_head; + other_first_head->prev_sibling=walk; + } + + // Adjust parent pointers. + tree_node *walk=other_first_head; + while(true) { + walk->parent=loc.node; + if(walk==other_last_head) + break; + walk=walk->next_sibling; + } + + // Close other tree. + other.head->next_sibling=other.feet; + other.feet->prev_sibling=other.head; + + return other_first_head; + } + + +template +void tree::merge(sibling_iterator to1, sibling_iterator to2, + sibling_iterator from1, sibling_iterator from2, + bool duplicate_leaves) + { + sibling_iterator fnd; + while(from1!=from2) { + if((fnd=std::find(to1, to2, (*from1))) != to2) { // element found + if(from1.begin()==from1.end()) { // full depth reached + if(duplicate_leaves) + append_child(parent(to1), (*from1)); + } + else { // descend further + merge(fnd.begin(), fnd.end(), from1.begin(), from1.end(), duplicate_leaves); + } + } + else { // element missing + insert_subtree(to2, from1); + } + ++from1; + } + } + +template +void tree::merge(iterator to, iterator from, bool duplicate_leaves) + { + sibling_iterator to1(to); + sibling_iterator to2=to1; + ++to2; + sibling_iterator from1(from); + sibling_iterator from2=from1; + ++from2; + + merge(to1, to2, from1, from2, duplicate_leaves); + } + + +template +void tree::sort(sibling_iterator from, sibling_iterator to, bool deep) + { + std::less comp; + sort(from, to, comp, deep); + } + +template +template +void tree::sort(sibling_iterator from, sibling_iterator to, + StrictWeakOrdering comp, bool deep) + { + if(from==to) return; + // make list of sorted nodes + // CHECK: if multiset stores equivalent nodes in the order in which they + // are inserted, then this routine should be called 'stable_sort'. + std::multiset > nodes(comp); + sibling_iterator it=from, it2=to; + while(it != to) { + nodes.insert(it.node); + ++it; + } + // reassemble + --it2; + + // prev and next are the nodes before and after the sorted range + tree_node *prev=from.node->prev_sibling; + tree_node *next=it2.node->next_sibling; + typename std::multiset >::iterator nit=nodes.begin(), eit=nodes.end(); + if(prev==0) { + if((*nit)->parent!=0) // to catch "sorting the head" situations, when there is no parent + (*nit)->parent->first_child=(*nit); + } + else prev->next_sibling=(*nit); + + --eit; + while(nit!=eit) { + (*nit)->prev_sibling=prev; + if(prev) + prev->next_sibling=(*nit); + prev=(*nit); + ++nit; + } + // prev now points to the last-but-one node in the sorted range + if(prev) + prev->next_sibling=(*eit); + + // eit points to the last node in the sorted range. + (*eit)->next_sibling=next; + (*eit)->prev_sibling=prev; // missed in the loop above + if(next==0) { + if((*eit)->parent!=0) // to catch "sorting the head" situations, when there is no parent + (*eit)->parent->last_child=(*eit); + } + else next->prev_sibling=(*eit); + + if(deep) { // sort the children of each node too + sibling_iterator bcs(*nodes.begin()); + sibling_iterator ecs(*eit); + ++ecs; + while(bcs!=ecs) { + sort(begin(bcs), end(bcs), comp, deep); + ++bcs; + } + } + } + +template +template +bool tree::equal(const iter& one_, const iter& two, const iter& three_) const + { + std::equal_to comp; + return equal(one_, two, three_, comp); + } + +template +template +bool tree::equal_subtree(const iter& one_, const iter& two_) const + { + std::equal_to comp; + return equal_subtree(one_, two_, comp); + } + +template +template +bool tree::equal(const iter& one_, const iter& two, const iter& three_, BinaryPredicate fun) const + { + pre_order_iterator one(one_), three(three_); + +// if(one==two && is_valid(three) && three.number_of_children()!=0) +// return false; + while(one!=two && is_valid(three)) { + if(!fun(*one,*three)) + return false; + if(one.number_of_children()!=three.number_of_children()) + return false; + ++one; + ++three; + } + return true; + } + +template +template +bool tree::equal_subtree(const iter& one_, const iter& two_, BinaryPredicate fun) const + { + pre_order_iterator one(one_), two(two_); + + if(!fun(*one,*two)) return false; + if(number_of_children(one)!=number_of_children(two)) return false; + return equal(begin(one),end(one),begin(two),fun); + } + +template +tree tree::subtree(sibling_iterator from, sibling_iterator to) const + { + assert(from!=to); // if from==to, the range is empty, hence no tree to return. + + tree tmp; + tmp.set_head(value_type()); + tmp.replace(tmp.begin(), tmp.end(), from, to); + return tmp; + } + +template +void tree::subtree(tree& tmp, sibling_iterator from, sibling_iterator to) const + { + assert(from!=to); // if from==to, the range is empty, hence no tree to return. + + tmp.set_head(value_type()); + tmp.replace(tmp.begin(), tmp.end(), from, to); + } + +template +size_t tree::size() const + { + size_t i=0; + pre_order_iterator it=begin(), eit=end(); + while(it!=eit) { + ++i; + ++it; + } + return i; + } + +template +size_t tree::size(const iterator_base& top) const + { + size_t i=0; + pre_order_iterator it=top, eit=top; + eit.skip_children(); + ++eit; + while(it!=eit) { + ++i; + ++it; + } + return i; + } + +template +bool tree::empty() const + { + pre_order_iterator it=begin(), eit=end(); + return (it==eit); + } + +template +int tree::depth(const iterator_base& it) + { + tree_node* pos=it.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0) { + pos=pos->parent; + ++ret; + } + return ret; + } + +template +int tree::depth(const iterator_base& it, const iterator_base& root) + { + tree_node* pos=it.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0 && pos!=root.node) { + pos=pos->parent; + ++ret; + } + return ret; + } + +template +template +int tree::depth(const iterator_base& it, Predicate p) + { + tree_node* pos=it.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0) { + pos=pos->parent; + if(p(pos)) + ++ret; + } + return ret; + } + +template +template +int tree::distance(const iterator_base& top, const iterator_base& bottom, Predicate p) + { + tree_node* pos=bottom.node; + assert(pos!=0); + int ret=0; + while(pos->parent!=0 && pos!=top.node) { + pos=pos->parent; + if(p(pos)) + ++ret; + } + return ret; + } + +template +int tree::max_depth() const + { + int maxd=-1; + for(tree_node *it = head->next_sibling; it!=feet; it=it->next_sibling) + maxd=std::max(maxd, max_depth(it)); + + return maxd; + } + + +template +int tree::max_depth(const iterator_base& pos) const + { + tree_node *tmp=pos.node; + + if(tmp==0 || tmp==head || tmp==feet) return -1; + + int curdepth=0, maxdepth=0; + while(true) { // try to walk the bottom of the tree + while(tmp->first_child==0) { + if(tmp==pos.node) return maxdepth; + if(tmp->next_sibling==0) { + // try to walk up and then right again + do { + tmp=tmp->parent; + if(tmp==0) return maxdepth; + --curdepth; + } + while(tmp->next_sibling==0); + } + if(tmp==pos.node) return maxdepth; + tmp=tmp->next_sibling; + } + tmp=tmp->first_child; + ++curdepth; + maxdepth=std::max(curdepth, maxdepth); + } + } + +template +unsigned int tree::number_of_children(const iterator_base& it) + { + tree_node *pos=it.node->first_child; + if(pos==0) return 0; + + unsigned int ret=1; +// while(pos!=it.node->last_child) { +// ++ret; +// pos=pos->next_sibling; +// } + while((pos=pos->next_sibling)) + ++ret; + return ret; + } + +template +unsigned int tree::number_of_siblings(const iterator_base& it) const + { + tree_node *pos=it.node; + unsigned int ret=0; + // count forward + while(pos->next_sibling && + pos->next_sibling!=head && + pos->next_sibling!=feet) { + ++ret; + pos=pos->next_sibling; + } + // count backward + pos=it.node; + while(pos->prev_sibling && + pos->prev_sibling!=head && + pos->prev_sibling!=feet) { + ++ret; + pos=pos->prev_sibling; + } + + return ret; + } + +template +void tree::swap(sibling_iterator it) + { + tree_node *nxt=it.node->next_sibling; + if(nxt) { + if(it.node->prev_sibling) + it.node->prev_sibling->next_sibling=nxt; + else + it.node->parent->first_child=nxt; + nxt->prev_sibling=it.node->prev_sibling; + tree_node *nxtnxt=nxt->next_sibling; + if(nxtnxt) + nxtnxt->prev_sibling=it.node; + else + it.node->parent->last_child=it.node; + nxt->next_sibling=it.node; + it.node->prev_sibling=nxt; + it.node->next_sibling=nxtnxt; + } + } + +template +void tree::swap(iterator one, iterator two) + { + // if one and two are adjacent siblings, use the sibling swap + if(one.node->next_sibling==two.node) swap(one); + else if(two.node->next_sibling==one.node) swap(two); + else { + tree_node *nxt1=one.node->next_sibling; + tree_node *nxt2=two.node->next_sibling; + tree_node *pre1=one.node->prev_sibling; + tree_node *pre2=two.node->prev_sibling; + tree_node *par1=one.node->parent; + tree_node *par2=two.node->parent; + + // reconnect + one.node->parent=par2; + one.node->next_sibling=nxt2; + if(nxt2) nxt2->prev_sibling=one.node; + else par2->last_child=one.node; + one.node->prev_sibling=pre2; + if(pre2) pre2->next_sibling=one.node; + else par2->first_child=one.node; + + two.node->parent=par1; + two.node->next_sibling=nxt1; + if(nxt1) nxt1->prev_sibling=two.node; + else par1->last_child=two.node; + two.node->prev_sibling=pre1; + if(pre1) pre1->next_sibling=two.node; + else par1->first_child=two.node; + } + } + +// template +// tree::iterator tree::find_subtree( +// sibling_iterator subfrom, sibling_iterator subto, iterator from, iterator to, +// BinaryPredicate fun) const +// { +// assert(1==0); // this routine is not finished yet. +// while(from!=to) { +// if(fun(*subfrom, *from)) { +// +// } +// } +// return to; +// } + +template +bool tree::is_in_subtree(const iterator_base& it, const iterator_base& top) const + { + sibling_iterator first=top; + sibling_iterator last=first; + ++last; + return is_in_subtree(it, first, last); + } + +template +bool tree::is_in_subtree(const iterator_base& it, const iterator_base& begin, + const iterator_base& end) const + { + // FIXME: this should be optimised. + pre_order_iterator tmp=begin; + while(tmp!=end) { + if(tmp==it) return true; + ++tmp; + } + return false; + } + +template +bool tree::is_valid(const iterator_base& it) const + { + if(it.node==0 || it.node==feet || it.node==head) return false; + else return true; + } + +template +bool tree::is_head(const iterator_base& it) + { + if(it.node->parent==0) return true; + return false; + } + +template +typename tree::iterator tree::lowest_common_ancestor( + const iterator_base& one, const iterator_base& two) const + { + std::set parents; + + // Walk up from 'one' storing all parents. + iterator walk=one; + do { + walk=parent(walk); + parents.insert(walk); + } + while( walk.node->parent ); + + // Walk up from 'two' until we encounter a node in parents. + walk=two; + do { + walk=parent(walk); + if(parents.find(walk) != parents.end()) break; + } + while( walk.node->parent ); + + return walk; + } + +template +unsigned int tree::index(sibling_iterator it) const + { + unsigned int ind=0; + if(it.node->parent==0) { + while(it.node->prev_sibling!=head) { + it.node=it.node->prev_sibling; + ++ind; + } + } + else { + while(it.node->prev_sibling!=0) { + it.node=it.node->prev_sibling; + ++ind; + } + } + return ind; + } + +template +typename tree::sibling_iterator tree::sibling(const iterator_base& it, unsigned int num) const + { + tree_node *tmp; + if(it.node->parent==0) { + tmp=head->next_sibling; + while(num) { + tmp = tmp->next_sibling; + --num; + } + } + else { + tmp=it.node->parent->first_child; + while(num) { + assert(tmp!=0); + tmp = tmp->next_sibling; + --num; + } + } + return tmp; + } + +template +void tree::debug_verify_consistency() const + { + iterator it=begin(); + while(it!=end()) { + // std::cerr << *it << " (" << it.node << ")" << std::endl; + if(it.node->parent!=0) { + if(it.node->prev_sibling==0) + assert(it.node->parent->first_child==it.node); + else + assert(it.node->prev_sibling->next_sibling==it.node); + if(it.node->next_sibling==0) + assert(it.node->parent->last_child==it.node); + else + assert(it.node->next_sibling->prev_sibling==it.node); + } + ++it; + } + } + +template +typename tree::sibling_iterator tree::child(const iterator_base& it, unsigned int num) + { + tree_node *tmp=it.node->first_child; + while(num--) { + assert(tmp!=0); + tmp=tmp->next_sibling; + } + return tmp; + } + + + + +// Iterator base + +template +tree::iterator_base::iterator_base() + : node(0), skip_current_children_(false) + { + } + +template +tree::iterator_base::iterator_base(tree_node *tn) + : node(tn), skip_current_children_(false) + { + } + +template +T& tree::iterator_base::operator*() const + { + return node->data; + } + +template +T* tree::iterator_base::operator->() const + { + return &(node->data); + } + +template +bool tree::post_order_iterator::operator!=(const post_order_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::post_order_iterator::operator==(const post_order_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool tree::pre_order_iterator::operator!=(const pre_order_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::pre_order_iterator::operator==(const pre_order_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool tree::sibling_iterator::operator!=(const sibling_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::sibling_iterator::operator==(const sibling_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +bool tree::leaf_iterator::operator!=(const leaf_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::leaf_iterator::operator==(const leaf_iterator& other) const + { + if(other.node==this->node && other.top_node==this->top_node) return true; + else return false; + } + +template +typename tree::sibling_iterator tree::iterator_base::begin() const + { + if(node->first_child==0) + return end(); + + sibling_iterator ret(node->first_child); + ret.parent_=this->node; + return ret; + } + +template +typename tree::sibling_iterator tree::iterator_base::end() const + { + sibling_iterator ret(0); + ret.parent_=node; + return ret; + } + +template +void tree::iterator_base::skip_children() + { + skip_current_children_=true; + } + +template +void tree::iterator_base::skip_children(bool skip) + { + skip_current_children_=skip; + } + +template +unsigned int tree::iterator_base::number_of_children() const + { + tree_node *pos=node->first_child; + if(pos==0) return 0; + + unsigned int ret=1; + while(pos!=node->last_child) { + ++ret; + pos=pos->next_sibling; + } + return ret; + } + + + +// Pre-order iterator + +template +tree::pre_order_iterator::pre_order_iterator() + : iterator_base(0) + { + } + +template +tree::pre_order_iterator::pre_order_iterator(tree_node *tn) + : iterator_base(tn) + { + } + +template +tree::pre_order_iterator::pre_order_iterator(const iterator_base &other) + : iterator_base(other.node) + { + } + +template +tree::pre_order_iterator::pre_order_iterator(const sibling_iterator& other) + : iterator_base(other.node) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + this->skip_children(); + ++(*this); + } + } + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::operator++() + { + assert(this->node!=0); + if(!this->skip_current_children_ && this->node->first_child != 0) { + this->node=this->node->first_child; + } + else { + this->skip_current_children_=false; + while(this->node->next_sibling==0) { + this->node=this->node->parent; + if(this->node==0) + return *this; + } + this->node=this->node->next_sibling; + } + return *this; + } + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::operator--() + { + assert(this->node!=0); + if(this->node->prev_sibling) { + this->node=this->node->prev_sibling; + while(this->node->last_child) + this->node=this->node->last_child; + } + else { + this->node=this->node->parent; + if(this->node==0) + return *this; + } + return *this; +} + +template +typename tree::pre_order_iterator tree::pre_order_iterator::operator++(int) + { + pre_order_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::next_skip_children() + { + (*this).skip_children(); + (*this)++; + return *this; + } + +template +typename tree::pre_order_iterator tree::pre_order_iterator::operator--(int) +{ + pre_order_iterator copy = *this; + --(*this); + return copy; +} + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename tree::pre_order_iterator& tree::pre_order_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + + + +// Post-order iterator + +template +tree::post_order_iterator::post_order_iterator() + : iterator_base(0) + { + } + +template +tree::post_order_iterator::post_order_iterator(tree_node *tn) + : iterator_base(tn) + { + } + +template +tree::post_order_iterator::post_order_iterator(const iterator_base &other) + : iterator_base(other.node) + { + } + +template +tree::post_order_iterator::post_order_iterator(const sibling_iterator& other) + : iterator_base(other.node) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + this->skip_children(); + ++(*this); + } + } + +template +typename tree::post_order_iterator& tree::post_order_iterator::operator++() + { + assert(this->node!=0); + if(this->node->next_sibling==0) { + this->node=this->node->parent; + this->skip_current_children_=false; + } + else { + this->node=this->node->next_sibling; + if(this->skip_current_children_) { + this->skip_current_children_=false; + } + else { + while(this->node->first_child) + this->node=this->node->first_child; + } + } + return *this; + } + +template +typename tree::post_order_iterator& tree::post_order_iterator::operator--() + { + assert(this->node!=0); + if(this->skip_current_children_ || this->node->last_child==0) { + this->skip_current_children_=false; + while(this->node->prev_sibling==0) + this->node=this->node->parent; + this->node=this->node->prev_sibling; + } + else { + this->node=this->node->last_child; + } + return *this; + } + +template +typename tree::post_order_iterator tree::post_order_iterator::operator++(int) + { + post_order_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::post_order_iterator tree::post_order_iterator::operator--(int) + { + post_order_iterator copy = *this; + --(*this); + return copy; + } + + +template +typename tree::post_order_iterator& tree::post_order_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename tree::post_order_iterator& tree::post_order_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +template +void tree::post_order_iterator::descend_all() + { + assert(this->node!=0); + while(this->node->first_child) + this->node=this->node->first_child; + } + + +// Breadth-first iterator + +template +tree::breadth_first_queued_iterator::breadth_first_queued_iterator() + : iterator_base() + { + } + +template +tree::breadth_first_queued_iterator::breadth_first_queued_iterator(tree_node *tn) + : iterator_base(tn) + { + traversal_queue.push(tn); + } + +template +tree::breadth_first_queued_iterator::breadth_first_queued_iterator(const iterator_base& other) + : iterator_base(other.node) + { + traversal_queue.push(other.node); + } + +template +bool tree::breadth_first_queued_iterator::operator!=(const breadth_first_queued_iterator& other) const + { + if(other.node!=this->node) return true; + else return false; + } + +template +bool tree::breadth_first_queued_iterator::operator==(const breadth_first_queued_iterator& other) const + { + if(other.node==this->node) return true; + else return false; + } + +template +typename tree::breadth_first_queued_iterator& tree::breadth_first_queued_iterator::operator++() + { + assert(this->node!=0); + + // Add child nodes and pop current node + sibling_iterator sib=this->begin(); + while(sib!=this->end()) { + traversal_queue.push(sib.node); + ++sib; + } + traversal_queue.pop(); + if(traversal_queue.size()>0) + this->node=traversal_queue.front(); + else + this->node=0; + return (*this); + } + +template +typename tree::breadth_first_queued_iterator tree::breadth_first_queued_iterator::operator++(int) + { + breadth_first_queued_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::breadth_first_queued_iterator& tree::breadth_first_queued_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + + + +// Fixed depth iterator + +template +tree::fixed_depth_iterator::fixed_depth_iterator() + : iterator_base() + { + } + +template +tree::fixed_depth_iterator::fixed_depth_iterator(tree_node *tn) + : iterator_base(tn), top_node(0) + { + } + +template +tree::fixed_depth_iterator::fixed_depth_iterator(const iterator_base& other) + : iterator_base(other.node), top_node(0) + { + } + +template +tree::fixed_depth_iterator::fixed_depth_iterator(const sibling_iterator& other) + : iterator_base(other.node), top_node(0) + { + } + +template +tree::fixed_depth_iterator::fixed_depth_iterator(const fixed_depth_iterator& other) + : iterator_base(other.node), top_node(other.top_node) + { + } + +template +void tree::fixed_depth_iterator::swap(fixed_depth_iterator& first, fixed_depth_iterator& second) + { + std::swap(first.node, second.node); + std::swap(first.skip_current_children_, second.skip_current_children_); + std::swap(first.top_node, second.top_node); + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator=(fixed_depth_iterator other) + { + swap(*this, other); + return *this; + } + +template +bool tree::fixed_depth_iterator::operator==(const fixed_depth_iterator& other) const + { + if(other.node==this->node && other.top_node==top_node) return true; + else return false; + } + +template +bool tree::fixed_depth_iterator::operator!=(const fixed_depth_iterator& other) const + { + if(other.node!=this->node || other.top_node!=top_node) return true; + else return false; + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator++() + { + assert(this->node!=0); + + if(this->node->next_sibling) { + this->node=this->node->next_sibling; + } + else { + int relative_depth=0; + upper: + do { + if(this->node==this->top_node) { + this->node=0; // FIXME: return a proper fixed_depth end iterator once implemented + return *this; + } + this->node=this->node->parent; + if(this->node==0) return *this; + --relative_depth; + } while(this->node->next_sibling==0); + lower: + this->node=this->node->next_sibling; + while(this->node->first_child==0) { + if(this->node->next_sibling==0) + goto upper; + this->node=this->node->next_sibling; + if(this->node==0) return *this; + } + while(relative_depth<0 && this->node->first_child!=0) { + this->node=this->node->first_child; + ++relative_depth; + } + if(relative_depth<0) { + if(this->node->next_sibling==0) goto upper; + else goto lower; + } + } + return *this; + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator--() + { + assert(this->node!=0); + + if(this->node->prev_sibling) { + this->node=this->node->prev_sibling; + } + else { + int relative_depth=0; + upper: + do { + if(this->node==this->top_node) { + this->node=0; + return *this; + } + this->node=this->node->parent; + if(this->node==0) return *this; + --relative_depth; + } while(this->node->prev_sibling==0); + lower: + this->node=this->node->prev_sibling; + while(this->node->last_child==0) { + if(this->node->prev_sibling==0) + goto upper; + this->node=this->node->prev_sibling; + if(this->node==0) return *this; + } + while(relative_depth<0 && this->node->last_child!=0) { + this->node=this->node->last_child; + ++relative_depth; + } + if(relative_depth<0) { + if(this->node->prev_sibling==0) goto upper; + else goto lower; + } + } + return *this; + +// +// +// assert(this->node!=0); +// if(this->node->prev_sibling!=0) { +// this->node=this->node->prev_sibling; +// assert(this->node!=0); +// if(this->node->parent==0 && this->node->prev_sibling==0) // head element +// this->node=0; +// } +// else { +// tree_node *par=this->node->parent; +// do { +// par=par->prev_sibling; +// if(par==0) { // FIXME: need to keep track of this! +// this->node=0; +// return *this; +// } +// } while(par->last_child==0); +// this->node=par->last_child; +// } +// return *this; + } + +template +typename tree::fixed_depth_iterator tree::fixed_depth_iterator::operator++(int) + { + fixed_depth_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::fixed_depth_iterator tree::fixed_depth_iterator::operator--(int) + { + fixed_depth_iterator copy = *this; + --(*this); + return copy; + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --(num); + } + return (*this); + } + +template +typename tree::fixed_depth_iterator& tree::fixed_depth_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --(num); + } + return *this; + } + + +// Sibling iterator + +template +tree::sibling_iterator::sibling_iterator() + : iterator_base() + { + set_parent_(); + } + +template +tree::sibling_iterator::sibling_iterator(tree_node *tn) + : iterator_base(tn) + { + set_parent_(); + } + +template +tree::sibling_iterator::sibling_iterator(const iterator_base& other) + : iterator_base(other.node) + { + set_parent_(); + } + +template +tree::sibling_iterator::sibling_iterator(const sibling_iterator& other) + : iterator_base(other), parent_(other.parent_) + { + } + +template +void tree::sibling_iterator::swap(sibling_iterator& first, sibling_iterator& second) + { + std::swap(first.node, second.node); + std::swap(first.skip_current_children_, second.skip_current_children_); + std::swap(first.parent_, second.parent_); + } + + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator=(sibling_iterator other) + { + swap(*this, other); + return *this; + } + +template +void tree::sibling_iterator::set_parent_() + { + parent_=0; + if(this->node==0) return; + if(this->node->parent!=0) + parent_=this->node->parent; + } + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator++() + { + if(this->node) + this->node=this->node->next_sibling; + return *this; + } + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator--() + { + if(this->node) this->node=this->node->prev_sibling; + else { + assert(parent_); + this->node=parent_->last_child; + } + return *this; +} + +template +typename tree::sibling_iterator tree::sibling_iterator::operator++(int) + { + sibling_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::sibling_iterator tree::sibling_iterator::operator--(int) + { + sibling_iterator copy = *this; + --(*this); + return copy; + } + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename tree::sibling_iterator& tree::sibling_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +template +typename tree::tree_node *tree::sibling_iterator::range_first() const + { + tree_node *tmp=parent_->first_child; + return tmp; + } + +template +typename tree::tree_node *tree::sibling_iterator::range_last() const + { + return parent_->last_child; + } + +// Leaf iterator + +template +tree::leaf_iterator::leaf_iterator() + : iterator_base(0), top_node(0) + { + } + +template +tree::leaf_iterator::leaf_iterator(tree_node *tn, tree_node *top) + : iterator_base(tn), top_node(top) + { + } + +template +tree::leaf_iterator::leaf_iterator(const iterator_base &other) + : iterator_base(other.node), top_node(0) + { + } + +template +tree::leaf_iterator::leaf_iterator(const sibling_iterator& other) + : iterator_base(other.node), top_node(0) + { + if(this->node==0) { + if(other.range_last()!=0) + this->node=other.range_last(); + else + this->node=other.parent_; + ++(*this); + } + } + +template +typename tree::leaf_iterator& tree::leaf_iterator::operator++() + { + assert(this->node!=0); + if(this->node->first_child!=0) { // current node is no longer leaf (children got added) + while(this->node->first_child) + this->node=this->node->first_child; + } + else { + while(this->node->next_sibling==0) { + if (this->node->parent==0) return *this; + this->node=this->node->parent; + if (top_node != 0 && this->node==top_node) return *this; + } + this->node=this->node->next_sibling; + while(this->node->first_child) + this->node=this->node->first_child; + } + return *this; + } + +template +typename tree::leaf_iterator& tree::leaf_iterator::operator--() + { + assert(this->node!=0); + while (this->node->prev_sibling==0) { + if (this->node->parent==0) return *this; + this->node=this->node->parent; + if (top_node !=0 && this->node==top_node) return *this; + } + this->node=this->node->prev_sibling; + while(this->node->last_child) + this->node=this->node->last_child; + return *this; + } + +template +typename tree::leaf_iterator tree::leaf_iterator::operator++(int) + { + leaf_iterator copy = *this; + ++(*this); + return copy; + } + +template +typename tree::leaf_iterator tree::leaf_iterator::operator--(int) + { + leaf_iterator copy = *this; + --(*this); + return copy; + } + + +template +typename tree::leaf_iterator& tree::leaf_iterator::operator+=(unsigned int num) + { + while(num>0) { + ++(*this); + --num; + } + return (*this); + } + +template +typename tree::leaf_iterator& tree::leaf_iterator::operator-=(unsigned int num) + { + while(num>0) { + --(*this); + --num; + } + return (*this); + } + +#endif + +// Local variables: +// tab-width: 3 +// End: diff --git a/core/opengate_core/opengate_lib/tree_util.hh b/core/opengate_core/opengate_lib/tree_util.hh new file mode 100644 index 000000000..bc69594f0 --- /dev/null +++ b/core/opengate_core/opengate_lib/tree_util.hh @@ -0,0 +1,92 @@ +/* + + A collection of miscellaneous utilities that operate on the templated + tree.hh class. + + + Copyright (C) 2001-2009 Kasper Peeters + + (At the moment this only contains a printing utility, thanks to Linda + Buisman ) + + This program is free software: you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#ifndef tree_util_hh_ +#define tree_util_hh_ + +#include +#include "tree.hh" + +namespace kptree { + +template +void print_tree_bracketed(const tree& t, std::ostream& str=std::cout); + +template +void print_subtree_bracketed(const tree& t, typename tree::iterator iRoot, + std::ostream& str=std::cout); + + + +// Iterate over all roots (the head) and print each one on a new line +// by calling printSingleRoot. + +template +void print_tree_bracketed(const tree& t, std::ostream& str) + { + int headCount = t.number_of_siblings(t.begin()); + int headNum = 0; + for(typename tree::sibling_iterator iRoots = t.begin(); iRoots != t.end(); ++iRoots, ++headNum) { + print_subtree_bracketed(t,iRoots,str); + if (headNum != headCount) { + str << std::endl; + } + } + } + + +// Print everything under this root in a flat, bracketed structure. + +template +void print_subtree_bracketed(const tree& t, typename tree::iterator iRoot, std::ostream& str) + { + if(t.empty()) return; + if (t.number_of_children(iRoot) == 0) { + str << *iRoot; + } + else { + // parent + str << *iRoot; + str << "("; + // child1, ..., childn + int siblingCount = t.number_of_siblings(t.begin(iRoot)); + int siblingNum; + typename tree::sibling_iterator iChildren; + for (iChildren = t.begin(iRoot), siblingNum = 0; iChildren != t.end(iRoot); ++iChildren, ++siblingNum) { + // recursively print child + print_subtree_bracketed(t,iChildren,str); + // comma after every child except the last one + if (siblingNum != siblingCount ) { + str << ", "; + } + } + str << ")"; + } + } + +} + +#endif diff --git a/docs/requirements.txt b/docs/requirements.txt index 152249441..5d23b8347 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -23,3 +23,7 @@ jsonpickle pandas requests PyYAML + +requests>=2.32.2 # not directly required, pinned by Snyk to avoid a vulnerability +urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability +zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability \ No newline at end of file diff --git a/docs/run_rtd.sh b/docs/run_rtd.sh index bf4212f3f..00061021c 100755 --- a/docs/run_rtd.sh +++ b/docs/run_rtd.sh @@ -16,4 +16,5 @@ python -m pip install --upgrade --no-cache-dir sphinx python -m pip install --exists-action=w --no-cache-dir -r docs/requirements.txt mkdir docs/output cd docs/source -python -m sphinx -T -b html -d _build/doctrees -D language=en . ../output/html \ No newline at end of file +sphinx-build -T -b html -d _build/doctrees -D language=en . ../output/html +python -m sphinx -T -b html -d _build/doctrees -D language=en . ../output/html diff --git a/docs/source/conf.py b/docs/source/conf.py index d861e8a33..56adc54fc 100755 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,7 +23,7 @@ # sys.path.append(str(Path(__file__).resolve().parents[1])) # print("DEBUG: sys.path = ", sys.path) -# sys.path.append(str(Path("..", "..").resolve())) +sys.path.append(str(Path("..", "..").resolve())) autodoc_mock_imports = [ "opengate_core", # "colored", @@ -131,7 +131,7 @@ # # html_theme = 'sphinx_rtd_theme' html_theme = "pydata_sphinx_theme" -html_logo = "_static/gate_logo.png" +# html_logo = "_static/gate_logo.png" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/source/developer_guide/developer_guide_installation.rst b/docs/source/developer_guide/developer_guide_installation.rst index 4873075d3..e13a5ff13 100644 --- a/docs/source/developer_guide/developer_guide_installation.rst +++ b/docs/source/developer_guide/developer_guide_installation.rst @@ -20,15 +20,15 @@ Virtual environment ------------------- :warning: It is highly, highly, *highly* recommended to create a python -environment prior to the installation, for example with -`venv `__. + environment prior to the installation, for example with + `venv `__. :warning: If you use -`conda `__ -instead to create your environment, be sure to instruct conda to install -python when creating your environment. You do so by adding โ€˜pythonโ€™ -after the new environment name. Optionally, you can select a specific -python version by adding โ€˜=3.XXโ€™. + `conda `__ + instead to create your environment, be sure to instruct conda to install + python when creating your environment. You do so by adding โ€˜pythonโ€™ + after the new environment name. Optionally, you can select a specific + python version by adding โ€˜=3.XXโ€™. Example: You can create a new conda environment with Python 3.10 installed in it via: @@ -49,8 +49,8 @@ First, clone the unique repository that contains both packages: git clone --recurse-submodules https://github.com/OpenGATE/opengate :warning: When you update, the data for the tests must also be updated, -use : ``git submodule update --init --recursive``. This also update the -included subpackages (pybind11, etc). + use : ``git submodule update --init --recursive``. This also update the + included subpackages (pybind11, etc). The subpackage ``opengate_core`` depends on the ITK and Geant4 libraries. Therefore, you first need to download and compile both @@ -62,10 +62,10 @@ STEP 1 - Geant4 and Qt ---------------------- :warning: When using conda, be sure to activate your environment before -compiling Geant4. The reason is that conda comes with its own compiler -and you will likely have mismatched libraries, e.g.ย lib c++, if not all -installation steps involving compilaton are performed in the same conda -environment. + compiling Geant4. The reason is that conda comes with its own compiler + and you will likely have mismatched libraries, e.g.ย lib c++, if not all + installation steps involving compilaton are performed in the same conda + environment. Installing QT is optional. Currently, QT visualisation is not working on all architectures. @@ -139,6 +139,12 @@ The pip install will run cmake, compile the sources and create the module. If you are curious you can have a look the compilation folder in the ``build/`` folder. +With Windows, change the `:` with `;` (https://cmake.org/cmake/help/latest/envvar/CMAKE_PREFIX_PATH.html) + +.. code:: bash + + set CMAKE_PREFIX_PATH=/geant4.11-build/;/itk-build/:${CMAKE_PREFIX_PATH} + STEP 4 - ``opengate`` module (python) ------------------------------------- diff --git a/docs/source/developer_guide/index.rst b/docs/source/developer_guide/index.rst index 25c01eed8..1aa5a3fc6 100644 --- a/docs/source/developer_guide/index.rst +++ b/docs/source/developer_guide/index.rst @@ -1,24 +1,21 @@ Developer guide =============== -.. code:: {toctree} - +.. toctree:: :caption: The Basics :maxdepth: 2 developer_guide_installation developer_guide_contribute -.. code:: {toctree} - +.. toctree:: :caption: Get coding :maxdepth: 2 developer_guide_code_structure developer_guide_how_to_implement -.. code:: {toctree} - +.. toctree:: :caption: Good to know :maxdepth: 2 diff --git a/docs/source/figures/example_lut_davis_model.png b/docs/source/figures/example_lut_davis_model.png new file mode 100644 index 0000000000000000000000000000000000000000..ddd68a01f423d1ece44d89f508778dfc6a219f45 GIT binary patch literal 246838 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

|3t`F| zoEQD@hrl<>T~MJ~m$Gk<6phVC3y=cGskyUecaNA?#Z{U+aeDKCCixLmzUjdFat6RJ|sSll6 z+x$y$JT~!3&9iOZx-1w?%h$W{z>xI8c55+vuW5#8B{&nXG&6Dp5AZx0$|%~C)ARG` zS@)g}sshvn$10Y^@b(>PIhL4WoQk7Jbrl`y++u|-NTE~2%UO^V6ILvh5XY9uHuW(A zt+K{Ve?b>3bFqNk!7z!X>!ZTx05MaU~h1CdU#=(d5QcftVt}pi-k-0OmTk@x+QYhu{u#W%dMwh1U++xG%Rp_)no* zFOERIL9G9E|S$!(OFj8Orsh+s-xqzLysL-)^N!Jz_8=B4?ByF(0sDQNxi+4pQ%o%+Q zolZ>_Hf~?m-+}u_U>1oQe=4xLF0<0tU;*nU_O1gv>qy-;@@FxZi~#&v8^G349h=C+ zExeIYn*oe_K1!CNm>hWy3~oUM1`-i4gur>FK;Xs5U#KxM6II#r21iYgg5p`bfO|D$ z)D1;VrDVa-3w>r;;}T%2p)GNkOH3Qp_3wHwY}pKG&jZ zjyN<7j!eYUQ)D09kOp4Q#k1xZzHtR>%R(n%;Ad{jM|{Ic&!e|b<^IIy)BOfwUZ^Mm z6j?GJ2up}rx|l4H1f8Cr5}eYjx%!y%A#qL9f9mj6cB^sBAH!^l)MhkURw?yYBubGa zGvpB*TfpqjnWsyo&VDzd8a|La=<;X2c2$>sNz%nK_fOB<-=CU%Dj)`BPbv}8+K1!mlJ&Zm%1cIEC1xkd6ET2`U-4}*m>LO8wY#n(YF2pa_`l2-_S+$;vk7Eua zV(2IV$Oz!iDj{fAck59jJ=+TH@mj?p~V~MVAxP>L0;G1 z^F7~Q1`J#f)PBedKi4WMVM9~QPop0Dp;h~3BR4%L7(IM5C63MRIIsW$k6Av(wEZ5pJtOxpPvtxuuC0Z9V0h7SMMdVTD4?Xbm}j( zEtkfbZ=qg{YJIPZMyZus#v;qdW}SeCZQ3>LYFOWnfWZgk_4;PwX7bxxsaPWNejWb! z1_mzB<^7z&r-!v#63Dcy44M%8s$@`?$m&+1x{-$H;jbi>E1QXSB&FjXidbfkWf}-4 zFhrvHlDV)>)rhO2wWew;Sh2I3PctE99%a=iH1uo_%^D*Tc~ZM&8@Xf`YLnB!OG}4V zQn}D%7tFC`ur&sXPmmX#IEKUolU1p;Tgp0;EXqvE&ECT@wvz8)wOO>8c=Fg(;$KnK zKo}0EQ&g$NOX|yHn~F~X1J;qOwU#}#Za7-v%8L8uWei>v!kzC05CX?q%IXp(j)g@< zc(*#m@o;%1#*%k#)&uUT?Fw|W0*G)@lA(nad-wq+6MPKkfkb;jdq$#qL8<1|l)=y( z&I@XrE5Vh)PGr|-N`(Y0o)Kd*N(2=u>W;Fc17bXHLv2X zYwC$=)MS|U(=BNk%yv$)I1fk+N+4AbKyScoIONACMFNf`tx+ z(xgZF;CGA-qzj%FeNpKDB>D9kI4|U_$O&$=(l#I#U>1ab&wiar>$ps0fG(cw#0Ec& zCM1vC+n*?>08i%S67Y6rOv4EW3@WsyQwAjNy?j422I%Zp0rdkCh)v4uQF~z7B z^X=o7-h+rwo%g2`H7hN5GvU&fg${Y!Q} zY~{T8Mp7R`3oha`LB$$knpn<8ZHbPvO@=v0ilr<(*-s~rEP1}5s&HR6lqUpbC*PZC zmIzaj4@`hisp=RSt}5h$J}QEZFsyL5f4(!!4BcVWSw-*JbW}5FvI>k&OQ4=_>`KfCw!jnIZRyKl4E?{OA5b%62Ww2US7P zC4Ki}M(3OQ&(z&8`Q|f`^qZODDCGOwm4GqFW+YB~<%eA(R#2pTk7wP=>o)cc5TE*74Bf@XqJOeFbHC3^L z_zIYR`*78oeVKLNfANQlP8h#)i<8%5$Hw};sPrxk2JN~fn{r4FGXLe1^4abut~{Wr zMg6-2(~+qWOd}LAtdL(|8tvJUa7ioDSM;{U)!JWle(~X@FGz_z2m}%)etfG> zu(Aqf{dnY8q3yJ7SyH4jn^F(^8Mr4T7$Crh0dYL!{)UNt*S^_*=egmqMS#^;Qk~@7 z=(A*Y_;UQi({U-zf1Sg>Cb%l=GKGWGG>XeLoYik(bBMo9j$53Sb3T|Kzzv;%u^Tzj zITJ{yOb?C5cl`M5mT9vW>nxWO5sgi9IAe*+}ZHs;4Qb(IE`a&YRTa z@7NpbPBpYNW;P%4YT*MW^6ra!u{#A6Di;pOH8l1HrFw&USa`6Q%HK;rP?Zf~I)(;^ zk7Zh8f}UWJmx$B|6O#>{y?4&Rn7Ym5f&qif&nqoZVPWK+ya&di33?_FlU~u<8dI?e z=P?JBy=hy@JeU3B>9vZ8|94~;C4|f9E99`Bw-!3+x=5;h*K$*NnpLU^LV_TCjcaVBlpnF-8XhIQJWDM;$SP7b>SRwdW>_ys^3sHyx0``-j#+&xojW?~t z5N0_nB5I$U@0fZDf2hrd;-#@_-rmR&FJF2G>e7-I$1@M=rmRpOWNSS#)Jt;fk}W5b zY4pNbu~il4A}+SMO+|QhZdr9I7E7t7%C)gpPX>P|yl>Yvx9U0a>w&OK`%^|yh-vy` zfv~i6hR$Qx#05Bqy846hfn8X1l%?`<+?c>jd4`cmN7RdV=IIioC$D=@(+N-~+q&j? zL9to~(uG6{O&Hr*y^duSC#uI#H!82ZFuk*d5o3&d&=_vs>Az{aqz7siO`jD8KvV_CP(bW>mL-eVqe;V$4IuDKOlF%pW&CTj3^7{7ZjM5G`6m#Ejk&& zY=TxENrNd;1eaMl9Swy)o-(FA;EjC!rEta(-sVT11(+?*OMPmQQ zFT-wln<_FMiVqEp@}Ot>jGN-g%6`&7LZfCx&~Wyho_c`u z^j!q791me%1m?PeB7;YMw{{QLbX;21P$K32RMV10La9RM1_jNb#W~iaWnrp7QhRbw zmPpm&UiU9vJe55PJKT?l;;SmuThybUhbo%SXa+gNl2K#Em0yk{=B4o zedM;l$zqnT@TqfxV zfu_}9oB2Dz7lP>hhB7%8`gn(@`z(Y^lW>G!+)&fov1+`Y>TwuVf;K8+R8X>*L%R}m z*~h~pdCpn%K&+ zXnqZu%bwgmZSGokf9ksr{aJc9zODV@{sskXv>C`QnTghqOJQs0m5hfaN5TkWv(9=}_+_L+bWPflTTZQM-ha-ZBWuNm zU)o7MK#4jmr;#G9ZD~9TWYCXUmN2oKO`>*w4Z|j_(R3$q$tbiWHD?{Q3_H4F287+*jGv;u_it+1&-i9!N zkx!(#5WDp_zce`=LRG;Y5I?m&0xnz(@gD|!&QgVdi{AEpDaijE`I%>J6y)|Rslyng zr-ScP8CEIU-!0)+$ud{U7Qsx^J081EIaxNH^aBXr+jyE}>M^u5u_O^Gg*XL21d`hK~4W@kEmQrfjxfK*DVLrb@ zJql&*Nb6V3O9r)L9%Dv$oO}e6>-ygw^bmplRK07lvAJV%AU%r=cS8|oY|3m#kOE6^ z@um1D{ee3q9=GPsmFkHewy3Ox#YIe8X<3QR&=QXZOu72h)X?(M@8hFHhM=ZFs=0)TG~_LEIy~2THQcc&*7BZ z$k(T>+iseGX@f@~xHNjri9#TtCuIEWdF~EmY;tN1SS*YW(#lM>@{UyAFk5btrr^9& zxLQUnP8@9031`r*6-kbVx(3^`k>0eDe>^ET(K!+pBC{RvODSkVy45#xD0aUT6R@z* zT2SIRd-}`0;02CUL5EOser&x1VfS0ATT}P<7mPU_HU3cmO6Id-fi@^CU-f7NXXMR0GgW~Ka1y?ui6*y*#81{$ME68?3EO! zT~Kz|YoiqC|06KcF;3NX+LrL}Wx44xd$dO@55n+b)iZvt$(VbvXg2o#%N#Sa>=Q?S z1oIxv>SJ{?@Bm6Ro+el0b)XOGPe>@7C7A`b!Pk}_62m|eTzHw}7WF~wIiznQOzpw@ zQ>=@UE$DU2!GYB7jlYW*`H#by(rlmNzCoNmQ6}5^qY&tn4hfV(y)W5>brmuqEb)#| z{GHv9&H?aaJX-gX;KW)k&v2Q;nGs;JaNf^u9djmaQf#_q7KMITGS_J79l9~$9}A!L$znzxx;8ruDEdk^&0jWCiU(==`;uKf z-=Ax183_E}j0n8X3aYHPe{ZA!y3gM#)ORwCptT%R)?QT`F~gKO1S?^Ux`7mnY4r24^MAJ4>w@TF(cy8hv|hl)E}D>cW^zweGD+lhS!1=Hc2qH9rL~zkYDV!Zfc_B0jPNF|rLHOM z`Mbx8NR+++gBSapdJwNNg|-ZG?M`avPP!#yBgN|!g4Vn# zRUH52^T=3_W`N6FV$t5(Ofa-0*{%doM-2P%Ont2QyQFbX2BUN$hqmrnpTb8i+W@%| z{1WTB-nL{^4w>B;HbyiabLI!y;zqc`X!_k(pH zut_q0%Cl{CbwS~>XT?3#&?rmI`TTf}}TpvK@)G?#ZqD}};#+Z2Xn0Q0z zeSBREf}cZdUTi>9kB^}aY@-2zGg8e~zsw9kei{!n^_puN5>=EdS_l0a^>EhHMDzfA z_znM30e*_kZ0rR^Qo+0-5DffXt_03{#FB04a_H)@TpnCa7Hv z_h8eyq-qvgshqX6H`YT{YQa=vCj8dAx|Y_etYqW5s)NNmD#Z=x_*LK4o}kv^jsQ+4 zovFH^vM#CT#D&K{mvDytU>%WZyuZ(@<-R`CyrP4}HbzB5&Y|I>siQNJ)ll@5A?h+4 zemi9__aTk>v-*HKDILnug$y3*v~!x^(3Vv}#r|NOfY1f6v%I19cXDBz+g9n6lTuJ} zGuDNHWi++%XQp%T@U%h{5U#s!&OE8|YH8UVs+z{1syMhPxHDqcj2E0~xuwjXqPHzT_p1{Obk7*&Z(4Z(8tH0!fT!J#y&$j7Y^g)kX$a z(i7RhhH*d*EV68GpBXt5fhx>RS~}wJzDX4=JVG{5;@_=@C8|?vH)9xUq0@n4YTwVB zerc3i@5dqN*R!aPRzfToTg*dZfg%XzBYN-Mna$V7P0RND&NSiI81B`L{b8GXou~#{ zE}y`#4k+r^E22RhO)=uX<7%CIx{;_pYiDZEE+R%d3SO0~E{CCWf>{r zyETmVtY7I;Eof3m(x&th=ZxKa7~dJI*X}%XT}~yu9E-wfbs2EDOItvS3a}wW4%;J5 z<(h--v@`4e%_RB$OX7Uo)6-E*0nhtDZ~hbsD5%f{44+XiF!rVqH4jVFA4Xv)+j=-c z0jUE*ZNa7!TePIqQn3NFi^XCutDxb?QO_i85I_)>M{=y{5;7|TC4E%QEQiT>hC zx;}0JggmHAtN=Nxma%pSoxou@uD-T4GS`~YVKlaJ(0T;fFoOAJMpvQA53{ri^<`R! zcpuTe$_Afr%P}AXS1b)jqR_89o<~Flsry{1=0UVsC+4gCTuvl65RD*7905?3<^(Mj-Lh`>N1OGs@XRl!Vx26l(RC!-?0Noy8$b^^Ps+Sb=J8mLv+Cfplxo!_?#ii-8)ZF zE1Tt1CmQLPfkGBimQ$&1Ue?H3sw}U3@JZ%eYAQwsnyr+(AQOxA!`}>D1TUy8C)1RX z_~}B$DZ5FBNcX;Q)uG+H3JVk_F>G13coDd0GM)Z;-+fdf#FWh*K%}2no!emqRgcPN z%M4j}-OILqOHc`B2yDe${6S3q4RkyvtPX^D;V|&z^hXeC0@ByVO=21r!g*lVwu6zn z?}@Z#GQS_D_hoyC^B&rVhn9vu8XI*v1XOu;;b;bi78X}ja(7j4&nAB^)8{tVeE8^lM<~;@^J_A5Hd%2QCR|r zx9E5AHQJJQgqb>Q9D8p_Y}hZPds%7g6zU$K%?KwMf7n#x`{1WXvTZKfY6N9#Py-yF z_)6u&U2Xr-n@x6KOLNXT3i*bLO*uU$YAP`pZD0jSgpoNVHESraHnci&GC2Yn!EhS2 zo7wAQ5zFMX!%u9EQ6Nv|e+X_PqZk~!R{oiB3cCQQdo&J5AJ9@R$54sBP%5|{bKlf7 zov_TnOnORDrP87lYPt^1OoHLcxg>vhWm}dCbXC(?Dkgl$Dd7 zDeuu>UW{~z)J}n=M8V0zo-$IEi;$oQ4Oq!iLDT1Il*Brkt8%d$@{2 zHNTig)EI|(mCK`r4B2Ql?AJX6gsadgb%htnuZQE+Mc*h|Z zX2LIJDQEA9^La9E(kaNuODkuTfi}{UN%}wxzDD!ZZsrNMk@q@Xk-m#=_o)XYEtV+=ja= zi7YqXYv{4}Zv{HDQev2U|2-a7+sND-*l$HYfz^Vx)m$D$6n&dYvm3}TlD}K$n2!H~ z3I+6*mE64@L!Cy@*IZztui6Z5xOMIq&?yvS8of#<9tN2Z9G3zUNu)%U29t}Kj%&~W zT6U2#2m?>^Hm9ENL*vNoxz&(>j1bJ6i)$J<2%*GIr(4)eCQ5;7veqKSEQ4953wPYh z@6r;oQC@O9?KY(8HH zTc=hCnA{7#xzV{2Fz6A*Wt#(^jXFxf?H2E;FqlihUsEP-5Th(#17$BL9M2vtkH>bz zAr3Su{U?B}*I7FwTl?_&jo8-6$>N9LX6njlI{nDjGFi$m= zF3pZ@h3n(}CVnr6Y}USHbzH7CFCzt_Gu@_)MNh;Z+=I*qYk};JT+{^N-i3c)sGEQ| zwXME(GlE`MB=AL8f)t%go1Kffv+DYbuO(;=MZ$~RDa6Sy5;tnAUMz(YSKrLXjK9X6 zbdT9Kw&=j6#ex2{zG+!B7w2fRvg~eU(R3&t@zcXKS*G8v~#0yxiCL0YhFQ zoD2Qt@ON`hNLbi~usH3(K_O6#JeG`8`ocrArc~yIlkGL+4c#FuEdRhNG*~nv9A81nGKN2QveI zS{s@izBR`Hhvt8j{4EUP(NU($e6^1EA*LxS6Y&DG=>iczt*WaHwXJyQDN!G6l_evnjlnZv3XwS92p4;DboNX{L(gX^r3x7`Pr3%MoQ8+ zPQ+7LECJ^AwAHAJ975Ejy4>B7#|=vi#yyz>)lE)UH+E#!iOwBD(bMumKJ!%Q)7Ro5 z9xiN@i|}0ZQC0gg#+I>W|5tNfbSmXZ7SQlkuW0@bn|8|+E<$dc^xL<;nY?(uzZj9V2M zv10zcsbo#iwyHutNpXLT^2WYDo&BzV?OvlFi%7lRcL;UkJcJ+$!fpO1f~bH4tdJdb1- z8m@NRse=l}DOpV6tZ%@ABo%C=#Csri-lBIicE4Jmrb*kp=u`g=b&YR&SnHmtLOenr zJ9OQJ(^+dFEh+ZV$~^4Bq*kXJB=(z-*4K}%%BZ7*ki`W3PP4hf9}||2sH|MJW)~?y zN^`g^*-xW+^#Oj6N%>vTjCm7^1o#b_Sb=(#)73h&tDeQ>YE8(HQE@WgE1-;a$x1$# zZ^EPRQx9%v&n52@@Aa<_V`SkT7XqT-7t_yM+z(gU>~-7jENL8{2usG%-azoS-|Nn{ z=V8o$Utf=cKdcgQ1ZKOvej0iYTGfEI!E=4EX?Z=~Ve^};WZPu?E$%4#f6{Tqq``^x z6p4z|-joir*tn(aw{=cCK*hqw9>3alJ9183uPW^<&u}DKj9Lp47N$Jgw^Me-WcVj? z4=9ZF)0{VA`Sr`+@^}f8M_WU*Hv85Exy3;vEDECWprHn5*H@# z%tUPy4JXpMVnin%Hlgj9d0WnSSCZyUxv%njcv*$R*ll^&3Y2G&W*vXDC1zF`sS2fK zYqA}#ofi328+<^fSzYN?ud>l!pHWb`$yL}-A8NC%t2HsO4z90&cY6J1)tcJHjm10l zap7z~;ZRb&#kcDomgfnnrDAPnVg??30#;>83%Gh?QFG=eEoY&$pyP7K9hAj#L5I}o z?3J6pq*sY-){d4n@XU&v>vEPVSa_&ZDXW+Hh7<*Hn0_t*X^_CW|; z+)yD&Wem0=xdSX+G3n9He2WTV6(4>wP8C#Yx_r9(4VxE*8&X9+G`GXCfanfFIuUhr zdysu^>zXhoyR>U{q^E;l6+LPU*MOvIc4-azZXhmoOxbo&_^zZ-JGUs@)@@O9u&_E6 zc(-E;v7JonxY9*6&YvmKOL|o1<1T3Tq497;UiutVEwKO2H(XC6WgO=hR7mjNztGmd z-iXCNjOE=>CAV*I%s{i;j{o|_ztKVn!PH=>=rB)yhqBBO&?2a^FYFL;B7L!={%e424G_o|w~NeA@Q1@A4|)!o6QpkdfhLUn_fRf=)6qz~VFX--zd&5#4%mjAcsWfT= z$vvSj-mKxq7T6pVXU#h4i_S;OAUW>5kj57EPEb;;K$r7`*u+()R&JSCEMwoy16ty0 z6|A<=#DS5fb-vNk| zsmTu(oNcgJN2}H)K-b_EtKu1Z3Uyc|b(`)?t_iu&m|r_gVXEg9Elf41*f4U1FjAAO zp8W3!ZO879zu|UcBL_q0x#l?gM8fgQR4RvCSlZAynT$-aBVQX!x-;u-9wx3@x|i=T ze57r>3ijmy!gHkdQ}A30XMIm|Do0gd;2jrsPrux|T?k)rbgIR4`n~~=zmU{KkyOqz z0F}Km|C_dfo{sH25t)pZxIa%OXzicToMZ0bPunMne{wx6g#$SBRMnk1ScZi>v;E$4 zu#`^=6O6d{W+TRlkU6K_O5B=PaQ|#jG+oN&f=I#>lPPm?MG8F%L9}k)W+EQj8fe@B zNjS~(yV)O0!5D1mso9Q3WA~eFXYXIXF^2?LWc@AdS{!Lz?Bw3dayJgiR-7NR0H!7S za_*2s@;7XDTVV@NW+AT;IfB9YQ}W3rBq+moqxpxy_>vr#Hp8OZun)V zOCS9dNrr5p)n?&25Z}MIL*MU9gVUtEFS0d#A9{n2#Wc^C4U34e!>Bc<2zPCmc6~XQ zUPWHL3RS33k>n{emJB#}c=-SRaNBK7k4~pGTEoCE#li1V2Mz&YaeaNfOz_+4rt4M! zsP`_EFo`SY+`MUedb-|h3d8?xh;VUxdwTbKXV>Xwo7>yl`|XHQaAG2f@_l_w zV6-SsVDcLvYNIOf4XpM3bL;*2zIAz=;apl<8_V(dJF)vUvwPQ4=NA)B=%23F<$nKm z7*}M4llQrOy58YJ`R?n#@S=64$KcjouP2|Km(RsNgxSdvUx_6U&MpuDSY&`iab(1Z ziElPpNeCvcMLfICcGc^`Rj}{zRPc!L-`#h$-iE7`Kw4XyRk>yAKr+WjtudljjmR+T zy1JI!Fe@gV;YU8@MJRL0Ksc)pHk6#RoE3S_^C-A1e70P#Q8=#}X^3zLN_mf(6xMya>otwg-S`ceW0MpcEJQbF*&6?o!l}0^kxPHgop`Gjokn zT7snPU=)@kJWsZXuwGL|>}--{C^LmCTkqCCGP0L@FD3^~9*DQv!_s?S!q7 z&xb9la`fJ%2RMKb1E>lC;F9D==U>%E@z8!aqBu{>o#U3=y8Dn3TIvpF%5#gD1cf9t z0c02An)-kNSmlJ6X=cyvs;Uq%N@3GV|1_2q5(bO%)lbZ6&1%cus&SO>Eo&$(Nk-$D zPr@xAGq22@wF85+#X-VOdE;ka6gkt|^+3v08HVostpR_1`aFz=Y`u<_3`ljU?ivLF<`Pc}@m_@>n$FdCo3g_(% zmMp6lJ(^J;2O%CBAM{dVXuc2HBX`}E<{+;s@376x!I3qXIPvGmNinG_Jzv?Wec_$e zXBO<-8ah{{0B;YAZ*EIV%9D+ky4DnR6*^p3pgOPIG&?sM@6f#?qY>k9dqPpQw(2qt zAjO-iQI8e(0hrE)c5dJX*`ppd`?V;ve3z0k!Y5+8^if$16_l%>yq%kaNlx?aSBGH~QHokgEW5FRdUj|?A z!q@$az<>S;C7iTJS4As|8eTh_>~scMI)Q~#eKpDT+Y|nD@NhbPNs>=}6Iy;uehGa& zi8GQW9F=n1#_NX0?j*ikQMszxZKzo~1%p?v4fV8G^&By7{)7yR1#-SU)n<~GJDqE4 zVx8L^ZB=RB1uh(d`)1TvrS%G)jmrs8Nx;E^Q5AL-NK|%e)rx9cTUT20 zxBvbOJW?B3qxPg`nA!Y^M2T_IPvw)TSY#UZ9a%mh;9$-|nZz++0IwLrNFfvo=maDi zEhJoI)_-X+j)JBnm=sLnut#+nB{HOwB%RG=ol6%A(b2~qGfTz>IPly$1T;w`kYJiu zK%wJSl?GWEel_7Mp^6#jG*<5Nt?D$kQH+esxiroIAQNH!?Tc8(3j&A;2oEWBzM;MX3JsdYid4Fd9(rj*;fPIMk0&w#&w~*E z4h8?2yxi$FOJq_3QV$%oF z|JwV?-}||9`w-*c8*Da}%FMztm`bn1<$i@V2k0{X`&r}n$YcsFx9b@afKxGabxroU z?MY+wzmMX(>j%Bs?aNc}yHM%=_i<^v+nb}yFlG|RKlT}$z(3a>w+E@0ooT+mOA8jV z9gcdZonuEKn0lzzFgl@UG8Lw!9-H!)4drTbL>pkQsH=-s7G-U}dZl`$s5_q@_m1#?{7c|cLhVJZ`<71#~#5gCii?KXGjB5+oJaY{v)Y38+ zKAv$z++D-v^!#1CL1kN7m-v!Q;oFH{}Z zw2%BDatPod4`Tmv=u6_075rM))|#r+hE-tio_NLmEgzf(-~|fSt}G1pwOds?xzVo8 zhSLuMr{z2sOIECocxTvA?33|J*#DEmREI7#BWD;7ApZbdWk!d6)$%-vtXy%B82QMy zgE9az6Usjl!yFvgBjQ>BS$hfVI~2r@xBXSOu>c>Z+fx-B7446j$TQPWnjLkxc zGAlq~vWHS%OLejF#|3crhY7$m(%^UJAWho;P)xV?DBgwKIll{_0STCxG-4EwLWw@$ zFbj5TvHzGxZcSOcH~*qSXq`(X0o4{8X&b18&AUK451!Sk9FmdcG-n?e&?inm^WtpJ z8$l8QQ_>6;lD>yakNvr-zHU#K)6w5dvuIM!B)XSC>IHu=fb^IYH1L^KV7=b}vTY*j zkrCiK5teeK3w3a)+gsCn{rX^_`w_Vl(D@178-=|v^b~lTD;BPxX-@Nt6p&E`9MhDb z{=U#kjL{72bS1G-bMu2i98c#&bp=z_r)3pu<2EB5Ho5WbgBI=5oleP9>DuaA*L-Y# zsI1xv+3F)h#@bU$>I#56K?{J*H80_e7^fkfeYA|&n<`gl<8XH2NMgkSw^>$inYkqP zkED4s$QTLe6PRv&N(Z2U8W8xEV->22v93M<#EE8H8ltzR~> z0{k}cWpT9r6}?-)g@Y*YO?Jn+qNd#wrjGRY}>Yt$#~E6 zTkHMruI{RH?>*h`-XHDF6&=wRrIy!kX>4Y}PY=uvBkiwb_XOnEDf<(WQp1m|FkO9h z1Jn|>GAj0%;VD!%8CSIBSwfuRve!Dwpdg##CIE$CL+cKx;SEi|=$Ui%WN1-(-7U}P ziz@^X3|QSX9Z;c&dOB^#qCHO?Rs4W?XDog=5={E>T1|_<-gM=cIJay6&PO_A+(TS4 zXTh%T-evlCGRDElmUmtZS(oNw%ucNTAsM>wpD0xME-Yfe1AbyLSio%}r#e#Zu?}=9 z<91y#Wo*t)Iw69SCCjin!HQ5R_HOLw1sN?*pSQa*=kS};d#<2&X19+yV+lnoxDC|Y zQaqKRmwS+^x>UXd6({jlecnS8t?G3qG=n zioB5T3qA3wkiVn9e)@ACS0Z^cn?FL($;3in4PxqeI}ztw{uJH})4V6F-vB_CHl~sc5He3!=8{{AUYXeskIb_6gag+Ml4duXs^(j(NnAP8DcOq zGt*O{`bVNz$@|)Cn-mY>UjFMrzDS8$sT}Q(TGd}eo|t|6ar%~8-w)5J9RA4jze!TX zZReqgghyW6Zv@K9$`>njzo)&ASjkXef{-@+zCJeGwtc+MwYT@8Ik$dDjhYF*X%cuG z#Je4`b8rl$c0G_2c;5&C?3TR13z-0t4q-%u9T)Y03smT^7G;r%SCOGjispJ;JPHTF%7kKi9aSNRan>1d8ENQ<4 zfghpCM~TXA|D~CLaus2DE2!-&rZx#07>9YZb_ePEwfAu{S6YV4Y{rF*O;Rf7Xk&p- zTZv&fg#dJ9P)5hAdRIru1CpO@mlhEPKjdhcO=TML*9JqzSW+})eMc<$nv=ZBs4(Q& zx&~u$^^RWWFs?@Qr2X6zY>JbE_!CKIL4i1mlaIrxBhj4)>Cm9s$gcneg#%w-S50IY27efr)i3MwiD~Si}L~ zU-UEaR471fnGR&#Xks0ZZyIz>)1YrfWEbSAr&%Nn)W=ne-^Q(seGRXBi37pIyq+EJhh4DuI>68mm{<->XzF59HI_?qYIC80a zs_=RuWF*uyp@S9kJlr3JP6(W(ux6iSe_L>ge%wuJ6>7mnceVUE3*vE^YU%#S?!*sC za)2e7=Jq2*8HbqLvKi~?=x7BTau$QVAL>ipspIs&-2>p>KU&ij?;{2nbt&2>K2zngWGNHR^iaEqx828QQfOswN-lnR!HaT^SDANyo+gP8%0?=oQbeJ^d>27O z%S^y5mlP#F2s&kbJ!OB@RMY7*qYxaM{RQ=FOJTBtfRlGOk#()3oBi6Uc8pMMtV-Zfar+TA`9QgP1s@R7pj*E8fI- z_ot$zHi;x74*qS(ynat7QCn=})``kFBq_ykPND!N)I<(R*V5bZm4X2hQW84!vh8r< zxg`CUwa_e!3QVHVHqq~1RxFl*r3tB$XzVGmzubgaJD&p_DW=Ey1qvt>(y;UzeS5_f zzhx{WyEgB>IfKcZQ;S8z*ygsSo3x2iK~p@Cc`O>ZxFX}{+fqD88md9(R+C{&AkowGjK_T8qUFUC+iOabD3 zDB=C|tlBFXTEP^mAwJ5^P>^lhbvd=@MYEN}`DQ2g(RVr@0kbBPMk+io`FLPA^Fa|*vLCTbgTI$^*h*9n_louA6>I!v5RyVi+x0Mx zZD44?Q?(>`c6oA!-erE?;-w63hXRjXPE{fbf}OjaQobpNb*oC3Z#H&V=civ8hvoZt z&`kMRK$gu{h5LJBb8C$7^8kPA68kXakzzSfA*a8(b_mirxm|5~m_@hn=?&^Hu*6T` z^KOg2(!8v|$T&o|)Kl^(Qm4Hv-b;<~$NZSTwy4Ci@}FRl=L&E4{0n}Nt0{cLDYC<6 zQTtOedNaye?)!9JIO;94!A*F<<1xfOHo~A-!HMe|J%7QcE_LuL_4z#=oBo40bdF=j zEBV+yWH7w(qlY%;6#5F0WgSc$0M1 zvJA!Vq=f(Yv-a+{_Ww#Eola}qweM5=e|2{MF!dJ&LG1kM>ZrC|=iv6|<#xT*GDD8v zrw4)81v7!)yMtPlmWyPk;A36vZK&UK=*4DxD(`hiVj`uy$Y=wK2Sj+5)`$xw4f}KV?@p_ECdx$ zB_B699!iOci!;k6YS4vLRHQx_@DY?MQ0cF8)M-Th2h$$r0YZmQia$-ip;9JjD6Bg$fb;DQNJ&bE}6sb}>Eq6fKnq2_Rf zHSMBW+SLvcqKu8%ug2eB6=wxw5x1+i+LC3O!0vfRwu2BRwxZk;rEdrb5Q;@xjv^9W z1iUfwug^l89x?{?EG=o+^!@9zMrc3dp-OD`h7PpQz`u3DG?%L97t|5A2a@hG=*1EYp);86Cc_y z?cD!V`W4>LvEJ}TI^GyFmzlM!8BThfE%R`_*sQ5sqR4ou6zI&~R^rVS(^SU>agO2H=(wk_*(FUgW0)ZXKZx8*UP{Xl zygeUWIl+>}<*}5EeDia1dXQX9lQ{S6n7dadNR8Z3TlzY1`GsP?NWQqP!9^8?_w1GT zJb8Zeqy5Z@N-3BY90}a>zRK>1_ur>1JUW5@$6_i|Af23vvfj#G2NBSNh9Owvlk8>$ zrJ1QYRLeZIRnZczMJ!`5kAER*xbO6Sun5LEtrH8f6^&nmH9yFDXPlb4_UAwTBb(PFl3UR-=Nyr&08KF}-+$)dkge zczCb}-0r7bJ$+{a59?V%B0&{HaP*!#F+yh;BLZx*sj`ApR`f%>GAvhwh+f)+`Tj}- zgi+-jF1R?Qn@Qme2hgA}=!oo_EH*ybLoMg)ic513Q^OX*QuQoNsyr#B@&v@|W%e;y)uPe@$EZWnH znuQe&Ix&XSel}U>&CQ2b#IGAfzJCkKe6J^k3yX`l3(E4hcjJuro7cYoqwlh~90Kuq zUH_-#0Enjg+_*s#Z+JJHGH2&@nDChKbB_^8_~xzq>&<%eZ?fOVvfm-_91%e*KscHL z9G+qj65l(p4`Vd<9VRS^nL^gBe(9!#l_Sf!zHUlFgBRpQ$ zDByY^NYP@mY?mlDpXJJ>?mGccK-D-1!L{81cE+M4CMKdsd+-q)4}_yiP}A;oYS0;C zK}HlH#SXXG@Ai>r+Ie|;mi_tDe|+x%C{n--q|bzul$40BuTjceW5ASfvDKL=PT&zl znd_bKpFB00$?0^nBc$iP3xR`+E11-L{u{C@DdAU_Lx}r1k-(z7Ss5l&uRrsY#}~8` zm-PrkZ^?kaz*NOM2^kGC6Tr~-)86h=QCZ8iuv!#( zkUMYqh`FYn>40l<1zm#RvP3ckPx>u&jv}7v6_Lr{O!5%qZ1fIU$DH~s!ZW?RI|G}7S0$XX$fU0pQN3`_?_lMlh#f!>I+=3R6ny7@8FS$j%*UPj3G; z{H;xLuz@7j*C5=LVfv{x%4`l#iZpNm?W=MnRb?V=ihVsGS4*b+n_PzB6)FuGLqL(F z0xcXa^!H+o8qcxtLD;ex!u>2#-=Ryqg4I$3PfNno4n)fF+RZvB8W|@g$2_vYFdGb= z!QB2e6x-PZg_<$uuG_nF(3Ar)(?C>;iJ}2A6baM~uTZtDO~Ev$oLgBeKI?KF!+gk< zIr?rOHJ<4Mo83_CJo<%!sw>Qs>}EJM*_nWg%Pq18^ z?nK^FJAGmK8T1RvKWL}7h%5_CN7bZ#lN@JmeLATdKSp4Qz#9*7v2MOQ^>Rx1SYQit zNtCyHI)I`Cl%pxep|K@)s)|#?q7p+ax}pU|7{X;pH+dUdl13D?RsJnP;f5qu+|&r$ zfq+1Y2_zw^GI8hkjbdLVUr6TfHcYXu(5Dz9Nf^&})>PVKzNybToLq0DwyA)Hj8tBH zc#b^D7%SyuAl^sw*c*mQ$6~yJ;;cSsBjxY#@r|le$@%j5GQHl~Lva+5T8@NPAVjFI zAbv`p3c^UdrXQ%0K?2% zm{u}4s{8?uIBh>4O&k96Tk3*|?KrJdLRPdeWART_L;tI$FQu#(-_}FNoey6kpR1Qt zKR=7T6)Wtx9Ru^w6Yk8?PZd-jsW%ZG?w2w zRA@6U+2!TbU5`yQw-c<>H{tT$V8q~Vn=Y6wo36OSG5ALFC9;i;jS5R8^0KnKPj8aN zN)HbY_P2YY0PWWci)3qVzEhHEyEn)r20DJ98JB&fp&4gcc^b8-mQ*4FItLIFfp2T+;t{r_Efb92(I>mL8V zatCNx&zapXxL0k*1OPYr>wOP%waE$-QQ(>AKNDT4eC|=(anAm#S+3|j+kY5sDvP^+ zDw{W4tPnt5pYPLOSuNYXxa0Lq@cRiI(~AzeHWw2gd;;0K1E;=SwN(V9s+WJ?@dNZB zgv(^gEf9Y4jFfA-%IQwudBfrOsyhwR*C|MvnM(Rboq z(cQe1&hdQ|$JRG*;)jOhD=LSfrsRYMIzH3MKOHT_b!Zt1OWTuq=g6j;OfW8~K|Y%d z3G8bn*ykI)etznQcB8KD*}c|Zd|)VuEeH1wLUK*GudJ}WNm zQyUW25oMEIJ#|A=7$&L54Ckmnv#?V^cxSJ&OauLFqr*%fQRa%+-yA1?PEZj_|C-Lf!Hx-$`&L`F(q|KH6}|#0GkE0X!pKrB&vYuP z6}}>rl_xTTP)F9CREA#ArZHZw&nohY1;Y-IRVa18JJ1CpsAUjBjPJ!fDHul%M9yISv*z?12{ZL%i||A+ zM?xA6uTMz{2P#CGdV<(<;);BsE#qBqmQ`G=S~$`gS@-{D443L^7Y1S$!~oxGSN8$C z)F59#M>|%?B~osBYb0ENkX|%?Ps$W|WO;adkE$6&MUu?9q_65fScv${^W~~{+i8OW zcBlwpmZ~WQHb;r!URc}%_+<0B$ddlzi%D`k=~e%mH7}T5f#csG3t%!|NE64V*XXST zQ9MWp2Lsk~RJs%48my&Phj%X2BQW9lt(D$gkd4?1QVcip;?6;F&t`Pis_tHL8zYD% z&Y)ok2k5CToc>vuxLtG<ef*sh1DALevdkPVlIpKk)<&hIb@R$YSj6pVoMiZQU05 zu75rmNPH_l75X7YGOViq}X{5&s2qfewDDY5Oz&^qr4V&V#}#n7ZJ{+I5~;J0*FV+ zxihkg1>oy|ahxe>PK^_>NJZ6w?S(2$?ZAS!^Y;Kp%?!hd#5+U4kFjddlmXncHfYd> zwM9Wag4Kp>q=H)u&Nj;#Jgx7)gp}Q)5D;7CZ;O{bHDSZFnk^m*V;<~1v8a}0EZmA> zEi8rM;%SciS%oBhUu7-9X!5Hv=GWqTMqt9R@d?>xfUPy1cTDfm1ceGU^tvyC=!R?+ zHT`7Dg4UVzA~+u5n^7tDO|&DCYnGE~QZ4>b%<1_`mR`VF8QnDfO-@RGa1WEH!L8rJ z2LU$w@U)fRX>ZDbV{@?@P-cO(kEvyhYRU?cGFCt(X0D=CL@HWp+tj>`lQy6-&UTAK&x-mfTvG z>h5<8;;spK=Cq^(^<;u>;w0YNc&|~f;h`!r2=eFL=$*|m$LuNhxLK9nyRkD_QgLVN zUlrTXhm0wlb1eDu$=ORI=Rmqw(zYrR*Bm=>pKo+`mmO_L#f`k3tE03REAH{K$9=%S zjEeOzu`V4b@v_lQtG*XcrjaP0%4~D{fIrzUrWe4?AaH$gUeYBnb|fnu=GoKfr=L(= zZP0A@ON@wXcwO&>7}*?J6bZJupj_x?<#9X~5pL@^=S^t)*QMT}Q}KIr$nOsLb~p)R zSYucY=_B=Dj+c8L>}2z$X>vzp5s=j@Bje`|Z7N51q!^L$)5p{pkv|I+5eWDsfbbzk zWF5e^p@f6)@9$%U^yZ6%u5@?+{(}yZr<)gVa4=BdsZ-~_U}488>bknJe%F{;$e_fPuQ* z9NxdIhL%|z+}&7P)L*{ofJvmz>&ceG zZYv5Y@CQ*3ATii<0e+G=sbX_0tHO$ki2pnm*Vvfqyj#;KoA+v%w1pghOz25y`G_u6 zoO`MYLc;csRmwsQjBp5fBJb&Qc(+O@ndEBxq6~~eqEE>;)Xe+L&go{(9G0^33kCau z(Fgs0UsdaEMsFN zA^eM1a+)yLEV9=~vO7{f&C~^niU{1X)(?yPWG;DtXZ4Km27WUyT7Os30%8;h?ZoBkIDLbtT_BZ{YSD2af+hKT zxxbKT?;??fYx%lI*3nLGOdqi926?E$_*~S!H>t71MaTkv`rvoij9!)z;QvriiS-Miss_yOlz)Y*Ks1BK}~ zQkop6R#y9f`z}yh8M}l0^7V8!B|i0glxSD{3>keR)^hp#k|W!bc2Wm^*`F4OXj|+(LeALS0;wjp=)V zc5zmV|FU3n#3j*h0B7NGeQnso^TV=$_Xf% z(aD;wu+EB^!FH6&2@Y0EuuMUJVb6kbttW4aw@N$*+5lTQ>tpw4x?H?3s_(@(zBA8Y z*zCQ{3#P8>-F<=_f{~=wDF>-Q1d=p(@@_*4^8Mqz^;6W*G9`hko}(e&D1Kh&;c17+ zqxkGQ!!zdXTR$R8Rk3~C3fBX82+%|ws66w@4rYXmg7Zd^;5by zT83wuE;(!o_O$H~Ik5K4-e|`B#+MVErVBHVI zcmV~iTX^~YDn;skP-y78Wn6tiO|-SRyR8Y>Xq@vcKbTIX9XZ%~rf$!X%b-su#;$v! z@mQaTCks@rgNKrDs2b{#*BvCt#A;>MBOy=npe5Obe9vb&ww`y%+I?5VmFY&71>56U z|0TX(BJyy}vQB<|jGmw5PEAnDOi@?Jw5NuH3H?~!Xp{djah$15@9S_zA`G2jL_Ah! zh(-pTY$!pA_c$%5zQx$R<&>q2|Ly&)`vX)OgA)nijW%shcpB^1$6BmLog=~5bc=7M zJKaIb;iW<5_z14&!ar~K%!qNHN8gCpHoxwgJg+*(go|4tpT10}yvA>goWn0pQ7d zFX1~V5aao#Q!6MSa8*|e*{dIC%pxf*4Hr=`v$-iNEiGL<@%ia{G?@uCVTwTi6kkB~ zlO#Uk>iw?k_U+?8%f=`~cBi20&P{6(OK1 z{ReM2Y&QTtP{-@C>3zNX1NYa*nfiZFXK`_njf3OP%R46r`1@tCkv7$=qDgA`0;=~4AL4hT5mF5&Bame{CPNiXPIlWZt$dS z*4Ba?v}c=B&ec#97?(gzD(hw+#-6Sw7L8W=`Gryk1uatbZXN*43Z}Seyy}x@<$gT58H{@Hsz?v0K?_+0`Dh~t7 zFDpkG05&1UzBXvGF~t;+aU^X*M0OcSIp!+{QYwI1`BAoWu-O zY=B`L<0>fDI&fj19sX<$wE|{!YbdWcL%yBD!rO~j(hFBZ^eExEs$WuCLbNh;rl_{P zJU|Y$QMYB}E_R5<`o)PBn>vf<<=^XUoD06qCBg6MTV` z)H35iKxHnz4ARll>DxUZUA_cQ&@SFey%SLiXA|ve5TaN9p` zI!(T)_Qj0kQ_9fE)tc@wq(?|7Fl4cgQbfV{<0M_n4D_@q6OJW~Wca-Vuo0Q@4^dxW zgB5t(PG}|5wo(UirPA6yzizAlX$_ zGJwt(M@*Pwxv2I;E&8bl3L!&BH@LTWBCz~$Z=Vki25j(tVE5mNKIw&BN%Kl0;KI<8 zaSVVeS%~s6-i9t5IO^RU$Ab3{%!+BW3p{8;Ku#|GuSL! zm(JqI_-;O^+xv?d;6$s9XS8O!o<|N z^MCTVnDQH}+XNfX($O*uFn&-ua$|n;d9SoZ3_&g8ZwV9lf-ddTJ-CM}XSGV38$Nyd zm(NQhy2(z}Ot}mQeuAEO0Y%jdKg1^IuCGlbtFxgUj{cgI+Gtec&|I%s9-B5vHlj`jIXJ?8#_~Cg8jaz@CdF&2Tp7T z5LdY^X{-G3<8!?|l@qq+TKfu{h=|GC8;6wR)OzMl9H_{c8^G}?xfOj?%D+5S#vDOS zr*@R0@GEM+LEVYVGtj^&>1B|jT3+|}k6r89*vL=h<|4HnC+9na~oh4lhCNI;Ok1bt4Vs2*_xty%xzAdyvm*ln~RKv z5|Jd_k7}3uYYV3E>v`2^CR+0L;}2?E;rK%(1NkQ~@?!x|0ubbbM=9x53rTxi5@@?u zRdHJsNZ%nKstiwgx419}LcoFn;(K=lo1;VxslkU!xPU6PYo);g$rKfJ(&Lh5n`i46 zID#-}D2fDbxqId+zPUtbIK$j?zkE%JEk#&>a5>S-zbEn5ncO`;{@Q4WMT51o!Yo{Q zCDc1*{0EPLOS=2(goA>+?4&?mwTa{MO>vD?6;WxrfoZvi+7_!Y!FHo>$ho+dO1DRp zDp0goWO&~v|C zEF#$&-~9w@a&5>VG=q|qvo0kTe1Zg-IY7Vtr^jS&zf=5|M-s@5otl!{a^L^{`I7sE zO$-i*|DT_q(g#el<`4X3TWm$8MkCN^pXXA$2H2N%lsy=Ln(^O7 z=zeu|%pF@TR~Hu+{%$avR{Hfz-pYzr82Ue_ABdjMKh?VzzGYoEUGnC)jV_?(p}|7P z%olZbe(?KvH<|IedF%L29b)VqLM|`zaR9{R;~uz3VIiHCH7Pxz(we)F!{!Jw?8A#G zw1z+m+Hpgz@w}PLjpqZJN9REEfzq1OJv1+9QDI)$)i@PefNZ?pHgrbeediMDicj2iZ$h0NO=ev<_jY*cP zx;{yQ1fTGfzdMP@tej}en)`lU5_QLPjtAxF3zdi4q&IcotWW1LhoA+<>~N!KMd&aM zr%d43N>z~?LvW#;4ylplru~JHwd_C>9Lm4Z#6Zsgcnmq=@i}z7f^DoHu##?hfZ6vY zJ3KaC^{xYlS5AX|M$6iGAdRHF=!8>@iIf9T_L9hQdyKZOpN^5Ph;>Hfx2m+6Gt&5! z)7G}Q5L!oFC}n7RwoYi&Y=k)J2{wJ)MLq7O%4@ZzawykuicFB?vOz3>TeVU$1dcF8MM(ZlS!+vTi{}vN2aS6O0?Px}a z)A-IxxlH9%Y$arIn%uvr@S7gqK8PU*P-4ENHKVSvyR5RA|#;qAlB&R|R@CWQsjE98rlqF6TQuE&IH3Zw+hPkXfVSQ!uXp$I#iB~hjO`1~zfI%u1biE}c8CJ|x zRXF}dB*Bkl8VCSLje^-$2Ob%cMl78CLuA^EUYFLh9`m6stFx3Smy{X&DMD!}Dq(IL zqNKe7pov)xOZ^!^;asdi6bj(Cg)>WNXM(41orttVg)?`5jtpTU7Dxz1qf-`YH6ErW z%-)eLP_tOZB(HK*d{gaE`-c+=CtX&*mxU?H0LfTS(+==HlNB3MSDCHY%5{LDVE7`s zI~qJB+Td(Li>;j1V1mi0XcbdXQkHt^1C_HKWb2y>2L~otzwrVXVQy*?FanHg#}llmf4KB&N1^ezCGC{@Ten0qwoodK*B=B&4)++GhM3SH zHzga<^jW2g=<9uj18{D3`5A=xIg~7TmwJ+AIqCA?q>Npt=x!q8&=Z75=IU7FCZnXE zpt=A z+|*0I6VYT2s*(ZDnI=vFJcradaB!0Fq6sG3+4qXpXz9K3`jEx;`qJ_m3^P*H@PJ zC3TEI`{$Q)%$L-b_AqegdeKNI4!VPe^^}CC)R7mW3~G5QlSpA9X!Adj_X9C5@BP~N z@RUxKR^>-_uC_)NK9zyUbf7f0o7SKeCGItg)?6dTEW-)p-;>p=HR_)UzxGStS97<9 zA1}w#1iM3Q)>Gz|IZr1Y2p9}f8Wob5l&Ie>We_u|u0hvRtyXFvPsWGe$`KicrPO(^ z0qv(APiq8Ur21lK;zwY+t>#;f(|JYzPZ0r6s8Ych{Z_4)R6?L27}jabhSdD(&SAG& zy7@0j^dAk?%LJd}b;aHFx>!?hw=JNrcD2WaP*I_#;p~6yeayQ71c@43T3GG2_;`4D zo-fP<#{szn6jNq;lZrNI2k@`)_3GbLxNOxKL5YZnBv1Qx=l&ORP0hN#f1xp%;4he^ z(r&hHGhFZAWg0Y$S1MO|vAuGF4;D@?QXw3|@)QK|YqI+47U=%fu_P&S4lPc?YCYPB z%JGpbx`vU^kx_X_%i+^Sj;Rond#u=MbC)cvY#enA<@+QCq zzyPBIbrNk0D-I~llQi1*iQZy3jL^>A>yEHONLxAD5_o8b(inxhnTM{Uw#5LlJ-r9S zf_*qnP*fO*L}*MG?zh;_a`u%mi+I@z+{66?e6De-cMmV6qtp-_W-0ZgZ5X*mNX|MR zUmHu(8>9VbwsfaiY6pWe1_Wsx@g9RXU`pKw5&oB?`fp_odyO&+juc!yvojv><$zup zaYls)@8;;_$nQWq@ppqoFPdOyJ$gD{5?!@|j-km>+VLA;2!gQ*Va;~{4)W*c1(zUt zLGnSeOBlk*QI1K5q#B1x6NPN3S843`wRnPa_>JC;l-JX$;}xjj@@e`8#6?Rpx1b?8QaS#*qe)&XOA5&gK1xH~W)qaV#t#FlGp zY^B|KVigC?8)>9%hq30sf|0Idw$emN`bO-538Gj+GD*VAAK^y}yXym;ncodKKm;g2 zn$xk*9OK%v7I_|O&>WE5xSn!F>?@$yqM=Q~<-;@hGyGm*d1hp!I`mUY7iosGn{c;L z7`SMPdGcBTw;CE~)ZJ{kVm9~Xwo*tV@VewmjTx2NhQ?H}g8Om%`*K&O7X2s! zO+xH@!79mON+y+b64|0M22V9-9+~b05!s4BGLJY4RjY(fugSXV`bYai6bxum_DS=y zX))hnQZzx06PdpBY2C_|uobrslMfsZ=!;NBU{E~nUaf8Gm{7D zBZg@42>a>xDD1?CH{Bfp%c%SFic|-*aB{i=*C>B{!wsxzkor9Qyf(I2;%$)27Y?4^ zW|Gt}hOX#OvRhMI&%82N`*f2n4qV~?rk+-o zzJflKp??l6QYh3!g(g^!(IiEI)DhJid9H}l4xNFT+%@i3L=u@LZf$Cm6v0!eN^zAb=22MW76m>PlDxsz&jP8h{B>|2%%fU{rPc z2KywlDJ48!S9(1UWAbko~=Pwey0R0*qn2q1^;xn@09;y&IvpYXA2WdMiy zR1}Y-RQ9g;zkaG8BQOs06C4R^BoxuuD3*aVe3MLVV`SdhnH(^ChFJ^#y4lHH$|YqQ69Y9vetnf}%2xtDYUiqM3owr4Yd$ z-j){#-Z64Bk8O9{J5M2QbEsH5(G{dWCz%#a<68EIJ?!^L7gG29Vf%z7P{TtCdoJg- zsV1!lkTHbIu2zy@IXLW8@J&WIhq_Lfr`=P5rAHMMW`Snr;|zu_)j)tyPOUh#R?su6 z7T>_58eJPgRH}%T^RW2mPG_v}-`jl~xKqebQBOKn;ezDCU5RlY`LQeV>zhm(h($+9 z#5^~aL0KDF9qo*1PDZ?=f9fTxb(P^Ye(yL5gDAf~r+t2=R$D>-TqP<-9CRPAr~G%bpYolUKIl%;y?e{{pD54`e?DTj{ zWfPJox{Trs$3Og&bv%z*)UhZ*r2_YT)m|3Z~4^{skRw%+EK{l0=8k!V`nUXrK7u~{ZYf1@o~59-Z@=aBJwuAf(M zxg`~FfP1YY*cRb$LsZlgZ%;APpHty*gnqu7rWqCi;Mu8{9-=>YRZ-Y5k3*g_NDI=0 zPc@mutSEWJ%LA_$!j1(aaEnmf1Q#W1;Y{VlwZR8J4$xredzT(?T;;ZZh)@q%-J-r1 z9jWfQ2K`$#!Hdc|)VQI8DDH8=#^iE?fILNjp97j{QCr_&Zbs4&-)%CeezV#jk%8O! zluU8y{19M}qwxZsl_T7-jDwMv736z^%1vixPCg-#S|#* zdFvx>&T?O0yK!=Q8cPGmIZsR2em>knrh{QhZg%38n9^>3UL{rB0?WJErIpUZKh&1O z4bIN&O0Atsb0|*AWkn-V4`$yqN%ruVp{8>7%87Q|0c+5YPikNu0G&a+I_ybIVDxCi zKSq{(*?R7Vh!CYFUSiU;g@h=pL~y*{m3uq-PE*^{bB46j%Uz>O4^kj<5O1v%^C7{K zW_h?!k_#|%J1Py^X$56yr?3x>H0}tL^qU7_6QG`Rn=@yLJmYtf*!PrNn0U7+1+6=^ zV-Ytu2lQycqv12$6iwh>Mxmn>ZDR?Bu#u^Kc4`>d>bCp$!hNa)j3E$1XlZD)R{3Xj z{U#wRbKoy=A%YeKt)J%+GGaR>SoDIg(pbhd{1aeLcllDabn5F2>Gbj@DVsG5;;nFb zEL}ZK#vLM0Q~zMfeAW`Pq2p-Ec+yB2B&u;k(wvLwOrF{e4;0Ts>^r>DwwkC14?$d* zt@5bcD-*I7_y0Ji@H2*1WsOto*?vgX*c@6X3h)A><4HR0V-L@Z{E2`43T|623MuwS z!{7?c!#9?+Wfmt9ANj-WXo&!d7l9|1r_TjRuGoi+;Niky?oXlWZPKq3NP^B`ytE4~ z)LjFxF9g-HdLrF^K{$UhgtoD0Xf(-(<8P3cjMZoNaf!=KHB&XEw0#M_G`n%dq1BGo z;^qt%()2Z}v;7O5dbh`Cfx&?ZOM75y#%mH5*fBrl6jUOt&Onu? zwkSy=4BJ=po&-X1;QCpiXq`jkRWng{YIVosZ{YDtoTmH;0!UzE#;(ExbaQQ3Iy3P4 z3?zR0`Vm^Nhm-1yc*=^7HS@QH%buc3kV(>yXV&$@5^w?$ni zbf&ZCIp>jirSN1N*~Ce3h_5MUyvy#z`2Wi)+SIwKK45lK5#06&J%FUIrcj= zJ}`*a#d{F!dE7%{6evB&rzM?#&WYEvOfr zh?yo<^^+iiArZufb5adYBKMHBvRlDs*rY*WgQqb;%mRy(+d%eg0;Xzr29s z3o6YbE9FT95JULp?-O;j4GB?Q!Qb76<8IQGDzy>_*$E^R3~1M3$OZi;0UFqN5~su! z{e!3G9}oT^*u2N|=Gq$d(qV`??g-c6O;&+>r?|!?JQ&CGcV9|ASj8bukBt1ZT!F!- z4PM)1)HrIZ!a_}M;dU|BHqVMKIK7tpRybqZ)0nsvq2&1p@CbHsPTkoUJU&I;1V0{H z%c>b1yWmkfB=YHI;5WVdd9+Bb&M>6XgGfUwSV!5<@Gf8C5$nLrmv|oMZ zHa4m_?ehL#unC#kQt%-ET$Bo$NuT5+5ehhB3G#eAmD zQytV^9TF)3ZS*Znh2|f@!M`ueG`gowXS~1D-)|q>5bp}V{$UFP?MEBB&ugyU;bcGm z#p|Qp{*y&&(!FnvKdr@1Fp`X%+}(Wc=l=!?9334=ZPu^S0rsgOHrW)$UvjdtlTbL& z>WRMI8tS9TumAcR%e{3zH{U!&85oZfjva!}8NaS9ZQQ$kbG@eClJAA~kBZ&?zP$SJ zjr(wBnYp+!qk2XZylec)tp2e>HpF}HUA!OhGMp!uxDTjBDanx_$eie z(qMFujPk7u8LfOqO{XX0LOA7E3yZA;>p{MjWnD_7yhHn!+7(_)|A`W5;Q==)9#P>D z&;x$mJ#O{7m^@`4A8)+h^@~t|B7vu|u5Oa?ic4?WNY|5V8hPQnmYP~Ncc@moEPn3a zE>xDfnvSoeo6Ax@L_sRIsbT!=XKT=P_2tS3S7?Pe6Dqb6 z;}`i^PNX@DyBl`%ppPkc5CsFVay2SfLtloj-#6f3ryT9Ao9=a->%LCxR>|^J@5A?^ z8!k-2>G(N=M>W@*r|j*Lf|LxKqq2Z)kvVC{diy~6gkyJyf7l@v+AoS{mMdxkA&O5fi4d{arr$^GS#9HB+|=ddmrCsf*t**m*lYyYEpex3mUS5nF{S#a&n~1dfeHdzkO=|<_Ib`^ zPG?WcISOv{9DeD|gW4-9%q-q{AFGV0A8AS0Dj@?}5af(Sfh&{XSOSRvHZXpKPTlCq zUBE3aDG^>QG;YBpdGwh?05*crV#QaQ?A|nGb_7jnOn>7gpud#?9U=Cs=RONVpFV@E&$4kWImaVL9zmZAHV1*zQPcMg*X(&!&|HKzGwXAc1PY%uvlaI`gW>)nOQdb>+@GfAMi7nt@4*NYpu`$6!EZO zLZ}XqeWFaO=fc{p(#|bB5y29$8xVUxERZ)g5Fb{1LOw#dYNfkPx9Z#3X!6hb?)SbB zldnZw%$NS|*RYz8Pe#@mFF%R=$urLt{#*?En(k&zO{?pNQ~y`7c1T?QH>OpA?_qZQ z-HM(62SGu;z8kgAqX7R#GOe%s(|j*K&WG#!lK=5{|B)@F5&DLwxgNbo+DlMV>gduy zmkQUcady?W@qGRODK~IXkr1XE510y1kvP~~i%&AIXSou&OI>lu8zp{u@v{upx-mVS zm}ek_f`^MM;BtI|OVPWGo0FK*pr&|yaphTtYrQ1hcwnO=FztvU|S*BDUbw?nPs)cjmRXkMwX&l;JLyPx}CN)F`FiFZqJETT_XW#%=JMdZ*{uykxZM5NDYJwx>PZI z4T<1TWfwOhqjWnN>WV|$vzRg#d9L&XIkm{S*cBY%vf&W|Lebiv;qQtR_{zF*_~5Vv zpsqtL0kM1t-Wz3y&otO%g|NQHd$z1EDi9H&=}4=xAntemjL5)t-Dt8!Yvn*t*F@T+ zqwElOLi_L5b}tK&Tc9_>yW?QRD?J~7|oiNZc~ zU&U5j{UslrJXjXADGZ;^4+RCI?MnI$?41WI*z z4`sE6Dm8lScFra)U@JDAPAA8U$MEp^Aa$CYmJJ=bp|8G$zFIF6)*RdFy~N!(hSZTr zuEcLKXUrk-X~ePLTi%5$d}hrR9xoo`YWx=WtTA>}hB;8)$GOBsmW?G0-#{S9I2pFp z`nVFmMVlK(_e~6`5%Z#?-8Opc4paj0S5<3#z2iI7)GC%AK}f;D@*$F56vGePE+c-7 z6NN(v33sAX%$v*HjNIXw{1a3ahb3bHU24R=BxxtfVnPzH)_HdIFe`eVw=!q=)rHrZ zb7N!o1{73Z!N*1jHHhDiU@|hx`S?xtRff17y+=){(cyH`Ww)`rx(&@02vvmNtB)d; zW_Ip?9aowlR~0H%;cz;AorG)gSYZ!BctKdmh9n~R#zfO^Coqz!3x}Fg514c%%wjK9 z5FXcKqfD8z4A%NMQ8<8*g4Oa2i}~A4jW__W;kqs*p5k+5%Ph;W8r`KR72RRAUgppK z_Zj}lFAUPNTVv<#E&S`RouO)b{J~dFFqN;coUbv^mS$Tzwx+Q+G~5=MMYC;NfV%)8 zeGoch5ryq9@29W7y=4NFYZ<549gVrPZaXN7f?;Sh&46gI{UYp5g;@LmiNw(s@tWzZ zJ~cVI5k~s7IBI>_t@r-K!a>s9atWL>ZcgT#akJMy3oz7|p;~vDUoJAxn|>(!-CF-< z@6b5cdTsr=hAf{Jw68DcVamj232$G9DxX2M#g%SqAZM6eZ)d3}{nk~b-974AglIZztn zpDlcjlpEuGVw7n!OPd=f;YMksL{<>QJd@Y6Pq3rb%SdFFachB_u_+Ffwqv5-2fTbF zA17{cCpr4X3|*X zjy1-znq|ZqW5Qfu%$nx0@?IjoMZeuaLW^)Ic8?`}8B>}Fp%V9FSiZsT>M)9w{3vmq zZ)V;{ml}?614mLJ@bT`Ol?8zb!V{H4OzR88{1^%fi++~j!ZsY?a?hNgsssr6eC!%N z5>xgfrHUn^)D@eL4p! zFOP@M`wgJFK|u{rZniuRYc}5va}ptfa}BoM3(ccM7=d0$7|zw3?7bs`pN%?&>XLl+ z>p085PI=`Rx#wj&&l6^>C- zD;U0sA_ZA}g;`^Us2|5wG@@P{q`;CEJF2_58NGfV3*8FZTf__IC<2zhq*d|p*;C$2 zUMn&iIzwBY#qP>pZpLn6i#p@h6z{ct!lJ&+)_M=N;_`#cDL~~&aUav2b3xzYb31Z3 z3_H66U7Dob4tnh#ZbYw#POT#h)dx9NI>w^0jC2cZ&1vYqjw&@g1x6#I__&NlOg!OY zNP}*>m-o{jv%kEXmvYZCZcTF~b`_8qzD3eY^O@B>Y^is0D>jZF2GKQTf@7sE+>YMn zXz?)nDnslq@8EL$E~Am}GZLNP$^0?)m3Q(%`bTs-t2~=Ofh$}tC9ackI#|&Q=rW2X zb?U-HLnrR0Y4=jxjg0el=DlVv1qpQ7oh0hp>2%VR)fxi9E7>PWxY1@uEEzej#zrxH zgQ03ay01}F92WIe{>kF&=u&0TSmm#~-$J!I`R8-b5%qLd^ff%wJi^IcAn_Clp$Ux? zful%Yt%I^M#DuxT)A?g0T#Ku*ai*Ugk^$IkL}yV#oBKwtgl0%obWA^7k3104LyDl>C-a9m!Jz=Q3Kc26 z(5^mc)dpxsNNmN&RXuKWFK{ojg6?b7)hNA%B#-C&i9DI+kN@9WWcCQsg(ZIL^}`e@ z4*%<$ml;{iv!gS?^y*E1@v&X}^!@?leIsS<_ceYyycS|dK`ntbA=8sM%s>1R_jz0o4e5k&c|+%_M;pw4N*~SZpEe< zsQ2(f{vaK8k`6b`U2B?Csp}jS!(5AxaWOK6Aq>)fjL&6HaI7$d9@?nBvU-SFbCpxc zt6YeU@^bzVX*Yq7AmW?NjHE|PLv%SQ?pU*&N!(#?rJpu8jw%#fJXZ7)*P|1hjbFtV z9*R`xw|kg1=D85N@j$ymknQ!Q{9_E%yILkApFivRHtlYTlf{F)m^(yYy@Qw+!4n>r z;`eyEaDZ#E2?lFj94!rz@UI~xIKty@WR@?iJQYlWLUK7i#)vsa#J704aGb$v7elok z=Jh4U%n4?6$#Cs4I_)ISI0vwmIyN>vPCFfLlEGRJHzO0gl{!sPDG~Rh_`>IA%Z_fP^yQ*91b~3!1y1=3GFjr%vd=$Tc z#9udkHoqhs9A3*k$-J@3fyy?1kcu);@8pBT`89wC%?zhEB6pijneyV;!e&WZrqj)k z)$-IteZ#)EpWY3A)|Cpg#uO^<18lJ68|YHUKp~=3K-{l*w7dd>z^>Z}g>5iI$d>hk zU>a=#^9EtnC?*8y!JbnXp@lV@_(nt)flmwB)XYMlw#cRsEdE+7Px{5bqc8s}RB}_; zrKOGAX5A6%7^e5=vm{>m6Lig9W7k8ueG!X(vly~x- z_P6M%cc91ugSB3!tqD}AvZ&AE38w{sto2>YkK+iJx)R3A6yQtAy~tQo_PmzkNcnKc zObc$rt}|!Ou&=U@qFUshHOjl`1(IHps#>6|Rw$`u`fCF$=!;xRTna}<*zR5w505>S zL1v6u7L28)Im<}&4)-Esv^yD=^<_NaGgKYqnfyt9ls?b6c@INsWSnkxREMdkRTQZZ z^DMr&^jT6)oDb7yNqA9qRCm+ir1&`X3FFp12I^bcS>4U?!T}_bRlUH1y@V!Jeq;K- zBkn~ht5v?)@h&^61N;xOzeK`~(NoW0D-Hrd!i{sZw2LWo5zSX|MV(i(PoeuNolb($ z$T%;qJ_!O=wE|ssn!2blYs`@F;z)w%J_;UlX5dc|cVql@_cw`oF)C_>h-cC6cF{wY zbn*hPFF(hz;x5XS8gu3%W!2`p?H@2u?_qCcI~gY(IF92ZQSpTv?jpNfj*n0mE>D&A zq9Ov2FO-mJSJv2OK{&sPH92MD+Z0IQA%q%4kikdwRTRZvD?NQRetIX)F-tUb@xe3C8s6pHY9ac37h+EaWoy~yOD93&-w_}%NoO@pug+H-Wr zOzzHA_|0#8#L4ZQv`5XbvDVG*5`+jm@Yj@c0qCgG#gRUSWg^2FjmB<6V*qY)G>U5U zQ?nZzKP8|T7-nav5GaAB6OQnW@YbWq*oR@qPh%a?dasl}!Elhl_Db*(&Cf5BFV`9B z%WNuGTQWjhD$2c??8aqnQrE)({Oc%EK@)qOFhGv zSDs-sGSBJcO$O>+VVvJ0Tb(wh%w_uP?ex_<=&{qBO^oop%z5@z21vS5rp;x>ta(n9 zcJnJsFFwGmZ-nS1a5+B4A8&n=sd$!5NoPlO8x!UIH6d?r(KSsOgWi&F! zm*N(e@5%XqTxX=ykd|8^4MxJTza!R&3lbuF3w|PU^y;tX4Qy4q~Q{ zSu+e*x3H+^Ska2jdDas?L$!WxN5=SS_qW+u+s4`0HL6O53$dHH4Nw$>F6Ad0Y|56vUJd-dXV^p0Z|BFMNo$`qZ)3 z4nekLkcvP-;-KIPAKedjCV~X;LHVBW!=+q?ic8>}=MhBO(9qdmIn1@_HEK%WKsjMe zgsJ3%I(zIMs+P^XF~z%?_k-+}!l9-**osS=n_{50gSZ#rYW!-zFR$-63A?Mi$!R&p z%+Y4wHu|^m_nbM$l95H?v(@e=>c^Nj7FgD^JePltMPreQDDy<|I0Lm_uEy?gE^&rA zW0r-GrEdfo1gNT2%4&sw?06Fw=l+nCfUCGH7=cNTkT7M=@Im@KQD5hu&HXYXkx7zX zoVVLfk<+uB%%8;aqx_f7H;|z}j4pkWUW^;D+jQAoY^nD!Va*_dG^Gn7%^Xi?O2R zcs75Sf>!27$!pw>O)_sR;VLfw?c5jWZ;&GbuEs`5dvW$w1~*o=n{ZvMjoZdRzHe*^ zgjDIZGu(=*%vmdJt#=TWI=jmQ`1tHA4R9qf%3GOp4A!?0aSaCRJ+xQTXu`l(>XcN6 z6}`mH>K1e#;zn{VP)7i(nr2g%d=VlwLrPh3Y_K5|~aEC|PB) zkviE(jkq1*g?l?ueI3IM+WJ;!mYVJ$g^MQyiqz=O2>LVInMoD-&+|7Z)Es{Q%O{9O zEL_K-Hx(gjsLbXn#7%?we32iH&GJi+?+9Yh%a%CvQid{#CV8VKOk5|(axR)-PeL5g z9AE1vxJENKyo#zmx^LJ2{V*o}$LYTtqYEr9FR=g6BHuJ_;|QCW zALaSN5mHWsQ>kkdz8?6^001BWNklJYs?wToJ)>yvT%@>^G7&P9^~uoA96l+jbB`Tg~O#`R`oKcl9z#C zqASNM953!d_f>|gy|}{RzqFrX(C(w%jdLeDg(Y<+%|(tEchPHSI8oR`+>P?}jvrD} zY8)x=<}1t35c4e7tmPiWXSAI6@WcCkJk@4LZJ1uWjR|uKEgZg%uu;)CR^H1>tVYf# z@Yfx0G}#vg1&KtH8qXJxGhr_9Ve(wKEnBD0?q*dhVv8E1)+{$7qj>lT-{+O$aY{;s z@25YarqtL{@8bEwF*K=i*P3D4m?PmuI9%DwC$URBSALwUkvp7;UMB9xI9%DsE$bfL zPC9VP@+>SV5Yk8W8`Ff>HwJD=7`Opi-mJHiEl}GbH>3AGNEDn~RjJakc>Q*6$OV84a8L75*YxZn}>c z0w&BcCd{$swcuM{`zag6v;x(2_cht~mWz@WM!rP`B?Pik1dMiL@e*)V{ z+Bh#mlb#Kfm4?l}@Ee&EeH1mg-($^0C{PGgq~hTV5fm!cV$(~&#^G%IqX3%_gvC^R zKZsO8;9z+!agL|*Px4Iuac;y$m^0>iI)8#Ykuk=sn~YgEnt-@P?^$bu@V^VO^T9ph zfo8t%a78_63kjO9feNm0IF)`sj6kB0*YbS5^DKEahbc{dX5}@ka7R!QWGzMr=8Yvj zOr6FNcCgDrn29sIFX|;YTsp{wcn~u6rCg9l;Y$2E=i(PwGIHFG-o_Ok0>PQ&Cww;h zGM`&{IZ%>(m8S{^xDp#>F!W8SC>8o^{Y*qAIjyfSVNDYAP0lASaWxhp<)wI{u$Nvt zgCYer#pbQdX)0WI=w322rIFV4wBhyKGtCS~`B6fd`Uc7b)t+I9@zRS*?bB9VE9C zijIlk@!~%Amxef-yv>r9NTa!eM-2^KtSz+p67ctM!I+k%A0ex5fHF^8h#u zr56$u7axJDfFd=JA!n^hr1U5Ym-o}>SZteuQVrcz=+7lNIoXepN;uYJ=$iq%Rx&Kb z%52Z3f?x&+2pxCRRSW%|$+7JgiHHVBgr`udd$h+QWTGa&{cptlQAIFuNU=6So;x>2rY+9azD!^ivwq?w?6+_Ny$`lXK=F{+G!2l# ztf^9pV7KrQnC4OV_|xLS&9I{oKhbcIp_75{Gd)pf?{GJ&s%!}M8prm&44+({e5gr9 zYrUH_z-Gsf%W7`~P(KY2|8ZFGhn;&^9S^&2GgBad_dXe=->757>wM;5nLoX7g+Kd; zcK-R7Pi*-7M&H7tLxBdgVuk&o~5W6b9M0|~N$x%K|-XZPAIGwnOBWymu`V2MU zpdsk9+v%~>%p0qGm<+bt$E;cU?M}YD{7jJWBRWl1EfMpq;Jw5D8jHMaLGTjEoRQ_t z^m#g+6#vuYmnbWBvRWBUD!h_Aj4lKFL|;h0mpRXrvBVsU^yGE&Y7HqM>P30Fbc_qJ zn`G52*CV5-Ql&0zJjLTu^cER6!@QnlPM^mSHtlX3J8OfSEbZfZWRekUg2R=aJY6_M zNv(4+Hi9QyK9_xxFRnaIPAl_CVuWMW1KhU8v4zc6ryCW6KD&zreU-!I9SqmDaw>U| zvRb3p?%+!+&tgiQWvzg%`Y3XQl2YcLITOS^Cje{fC#k%?!h7i;*FwX_TM^JnOQ=WI z+I4MFwJAqNI8@z1+>dcJc7?%OU=DRRa*K26(?}r0+Fai_EZ^kW{Ii@+ou#biX|m?6 z-|@Pi!8g4DylpV&_x1N0sD>7q8_K~3;9aynKts_B!GeHv#c2ZMhR;`XL0l*QeIrw@ zvGPJfvKo}5sFLK1e~Yv78MFp~ z1e56qec398C(%R@4W~8GX+uUu0RCF|N*Azv4M)0x&A3#kDL#r9;ip5#Y~i`Z??g3K!$o$ZNT# zSxU-DVXHRPFwejPid<{MHu|CELdr{0R!aCtQeKjRT42l?yRR+3yz~Wjmj}6+xW$!N z;7bzs;z;4MuRKKDj{vZ!XZcS1``C&TICpy%novo*ZS1Y=;%xFFny*mQiv0A-PqC*m z7@E{w;Bx#rRi%a_>dYq>c&cz5Tk)B&#&~)4IaFzItgx4D^=^D2xfPw{kG6h|y_KE3 zuzHldR$L7pH^-Yp)ikDYUQqaoyLa<`IUZva4Fl#QOqHr>Qu<78wqUZTe z=0`l2Kg_mTBZdtOdh9kJiIhr^MOy`52HQ-+hlrx{ zY+)bxDobXLiO4Kp-SYRSXdEi;q{nFw>JTy<1%U@ha~%#Ry|oA{jdgVmp-L%G(C~d9 zlptDAQLVCU&DvAn=kBv1lXe97EZgKW=>h)j+4J12=jkI!p=#3}iSc_s`!u%~m&umv=$gXo`}S5WfyK zC_;erL6vwy265cOcK3bBzxr2y%k)eZ!_e5eHN)^wH$y|+3=MU0`0zFc`a4?Y-oS~s zStTHZj;19Dl0wNfB1S%l5g+Bd*ElvKbT@kbQ5YLQ{jJ~nt>4+GqKARWhcU35vAd0H zzFp^U-nhzh$9J-4d)I~vLgVMue2$MUj`MRbA0T4s8`kq_b^z8l#743|4EVRoxS8qA z!y3Es{G+x20%w21ilZ9REsP|G* zD=ZrYw%T17QVpXP#L!V#)=J!sE^#e-m!HZV;feAt25TL3IcX-$Wo*U4kScS=3RC7X z%X*$(JHzw&0}R!A>2T7dyeQYBla$mdQQyRr#yZwB;1^c)5_clg+>Olfcxfm5E8B>A z5$;Cj>2cCzwGs$PkDXzw9k4`8Mv?rkOPs0~StrENiP2)iNNl6^E3c3=#)5 z7C9j~QrSb&kCW4ioGcxr*J?3hiE+&*o23R~%-|6-KQI>Z(V~kMX6I=XkMjlpZIIB|K_sh3~b0$nEF| zu2Mx6F21N@5`;Ev1G<2IFV2wLj;WMM3&{Nx5yr{UiX62x6=^`p3s-$ylMHmYU34$pBjeV=cGR z1II?^n+w7Ei!H#s^}AS47|C_lT8ojQVZ$DP?)$7H#Qng(0Ww?G__cdNS%eH6NE^2C zif*H~&!ZN;NMZI4o?W`{6=EGb7lGW+a8ARuPFrKKTcmX51 zsWWc)E#;|t9ZgCujK+;brH;2hsuWlK|#$0&_M*eXNTPd zC^*7i1MA^SDN+!?CXK_TL*(=VAUIMw!jis1o7>J%btuRLs^=-G6?*JWV!p+cHBZ0Y zN7_p>YtC^kew&Yzm-sk&feVSN%$U;@wLCp`FMBF`kV2wM6Gf^-{3zS&1I(E7%$T!W zOk88en5CxFC~5_gUW!+@KI$AJgr$GiKdG3Q5Lor=%9?akh}~QViC5$hZljzC}&3d8%-j*Rs!YxU`EN zr=7H$B<&^;DBOnj7&EG&C;9@1h9F@xlRARElbuZ+5&# zO|5gHu)jG+H-<+LCw+z^r5%CUV9Soy+8_-E(S7TkqT&e&sJM8j$Yy#3fzphX9y}2w z#f$hB?RJKoR-oHXvuc!B)K;mhHHun|HYXNDlW9y;qB)vFQNs1UsYQrzCr+XW7>~>` zZO(H$Hp$i4U0lWEsr*6qSGLnrOF}k>@B_s}4R;%T;Ro^GQ4lf!=K`=?mAf5_Y|RDc z&t7QjuS)2QN0?~K^Z$JNHsAPooY`EJr-!?^GMnSyym^*idTb}J?Czr@ZV@-vGKjZ~ z{^nfOnC~j8O1(YC;9z$+r>$X$&wu`LzW8$|IB|TK?(Q^;OL@Nio%6hZ>IUtZILD4` zzrS=L?i;_VmL!#U97T&07=;G*{`a-%Be2$bEch^7*n0o^ayG-^4>0aOj@|T+&lC$> zPckx-=isiM4WDa$eyFdVy6rN%RG>Y5f5y?xb_E`$RIESWkZmJ?yqSjdFqV0<&UvsM zHdg@Fm$T8&^`L#d+^xq(41DAF(Wwu8;sV>MoxGGif+`gb4@>^tAH7X~&zBkQ zZ-1Z-HmYMIz`oh{@vt&ZUz(tzOf$XZ4n`Qjy0_lTGx-BdS_|B{ohsy)}=bm@4MV*9i z@pH@1(B-7LYt1qqSt9O7$+$84>~;i#%kfc`jUq>jLnOQirquE9C~7s%Cq|gpSJ9-x zp>p8p3Iao_w7UsTrLJQ78sA7`s(fMs`hd#I*E#cAvGM~P*>{o z*eK9pHz_i{z9% z68OJ*z7}#F0$pmaJdk_N?l(G>JdIk zAW1O&_S^lq!e`EyWw17ghDKJ+lJVl?^&qRDDiwZa^%;h$16+xXP*;Yz6CI`3?%`JS z4moXQ-S-ugZTS}cb{~pV8HwBld<zXzePkL z*6MV_jHXTVlXb6PY#kx}oY>3!i zV8YW^63ny$K{MnSFePTRXppp$Y_^x8d)MDF4VkJe*Ly_b1o zfmw5!9n~R@mI4!qkCPWjcyV0eQBhhRkb%j{lf@_KvfGgmI(a5IUO3E^_#L*_w&4m% zU%i{W7H8U;WZW9(t6L@zQlLu%RSq&{jpBt4i)~(-HYZJ|onbsOgXx=e+a1gq%dF@t z^w$O`XnD$Ni3MYhFD!kAvRdVb=}!XX6v4JyKYJ>BnK9=nt3~FGC1P%rf$COn#723+ zd7P}4<8Qie&~3MIJ37YR@=mf^j?3|z+>VZ53mXM^Dfc81FUI!T7H&nya1=>FDR41y z1y}f}Qt(e_f1b1#N0Tb9@F^>G3R;;@lGnHqyTeH24rwRD_G%yZB9r{e!smE1^AZ2p zeu^HujgndKfIpO%IIp;>&s?I&Guu0P>~I>9S9z?`%bAHKe(&we{Mw1# zeEz^7702b);wlr_5_`MTB&>}Ayg(v_U~Z*E-_anspDzMqumvF10DXcK%158|cGtQ9^D zd%qqV{=~vTHe#L5ES88xExOy|tUuP5yKPGvNx-}x*wOt^IUZ#y^C%{|@v{MpKTSRB z%XyU3!A9kM8X&)!{F}XRE4bgN-o~}>eR!KAY02lyk0FqOhq(|O$_QR5*Z7zJ{!RY) zfBq7gRP>>{37fss`Z^!>kvH!7&Z(O`)v5Aic@N$7B&rnn2$qaIW9A(DDuZa!#1kGx zwTP|MS<%WA^g1u+gK&zsqBES2-r!>VDueX_w%h&eDfLrQt9+EY!S~wE5%Dda&mSPC zl{k~WO_$TgvxS3LzDZ6k@_za{%W9UsdKZ(%47)2^*;>!gU+>^ze3ZX$Kf}wp!)&j& z5%)|!mwkeYQfI+VM3<9c(p)0t#&|z{^**GN_7Xf@*iT+7Q&(L2?M~iHUcmHKUML)6Suasm z>Qoh%Hn)uf)ty|9-sVVU4^zfGmT!^Qs!V804A!@B(;8=0D>81)v8XSzs;xAW(g02H z1K?{#9BYnFVUq~EF<77^X*bQG@?kzsoI!|y=|=cKm4)anqQJe_W$wkp2W7C(M!h&u zFN&i&6x3XEjwq?6kbRYu)Dj9Rt6G*-ElbpoQCDhs!sGjy59qOb*<0DcU}Xz&FT$zx zCrlfY51OY4F#HHFtv=7r+5lH#cUaL^*;n4no#bpILd8No$r*l4qb{LoXVMfz%+&ZL1s)(Cllnwq9%e*$UdF zxbKnXPmN&Xy{^Ml8U|Lv#7*JCEeK+&`wHkT#1FrZv#^ay_H|~j`~wI<-_fs-di*xg zofnZZyf3Vv;XGQ`!0?05qXEh#feDN*MaW8qQE(Jc{k0tgMXr5wKCqxK;e|H7O+8D7 z4zS_7<+L2DS}wTggISk>LIcP!ftn&^vw&7RW?QjY)e1E1!y5^e6jY>8DXJx+UW`|> zFLA82hh;s7K;^BrcX330%|uBw`bM&-uhMRJ5s^AIQK#GK=5T2*qmfxEN|kZzF0XYdb~K zP2vb2)3-QM+RIy+AJJRyCFQ2L6}!oBbtgBXx0p7ki1<-v&1GiICEA@f@>+$Z&Ue{g z8m6EXct8CizHmu;F%+p`D{!QA6i0aMt!!t1c{>Y6mXDLyDQX3FR0o-`CK;&pbF{dR ztWn_2j!!sP+D>1+lWB9Ai;3GT=<_7K7@x^LgWK*>S8NhaniGX5_`A;UvtX zlXN{5_>6?qOGR_2C>7?+ESKUVs8V51d4Py#&}a9MaH1d@Z{&swglz5%t-Vf*vLYo7 z9IMD6-C?qBVR;tQZ3PCiX`~bg>9QQJGm|cIaJ~myt#fT)iMt<{_&?sh#6Nv{A3wcs z8&_wS`S2S`K&DOT-E;V7*+kOGR$pp61^iOp&ey;7%$6DSb|*uCuGQ1;f)hoxDy(ahTDU zQIPDf>|oAYMv;O=eU&}ceqLRDjPtR(Y;iIS)q40iew$m-!09sSr8%9r!nxQ55^jp0 z%|6fJ@-P#|60_DaE7~eg77o+qL@=en3%P@gN9H+|zCx#+Vt-{DJF2~0ir-_>nkVWT z?5Xw>_bdkL9h^?yWW<^#<0f$xk6B}xzw2CNccni71_EY{6*LK_lUMj==3U}`jIDMj z3X=b3=>_6mq|vf8CJtR{tg0nSYL$p*@J9LrvRVP~S=9=hC>^5LZljkX|`I%=>Qj2!nNAt)INodpk6m(+8AkF)aYRI*3WBPrSsrHDWEm!SGc zA8cKaHx;^yUSs{F5Y>p+J*>K1i!&bvYIj8lj6-mvhoD`h#dcrHaJN7*WsR@lrVvy& z`)UJaM)jbr42n?MTJLAxSfnbd2q7CjtoQY8RjE=ns`s_8fAB8|+dv%bCS`A`@>kI4<+bL){6eJ^&JN$>fDFhk=wSE+-P*ZAL zieF$`ZIE8OmyDZ4NWqo(Eh3(UD?AEXj!vhIMSYn`a{>_f!bg`nhwA%TFyk+i{1J|pc40`3KkfY+_Luk2<#Z7BERK|R zBN0qkQ+#&i8Q$o44@YrWG*+0j7Ma%<*;5(hvEnY0ZVXq1`!}-TpK^ptiMt#uc`O*Y zAWOBIqRmb4N%AUbH_6%LCAL)uc)YlGEpc80v~4ij)|B!!=SdMh)3TB`cLbVWmLh4; z_)_=?JcI}+6RG$pQpNFY-tIijOSz}%w$tJ5UKmM6CGN*LSce6F#m8bxF~$3_S#(X|h2d_- zvw42!jkA3D=rCV8vK>QHcwN$#jl>}Ipc zW)1)df*=VZhX>&0chlKF?t2dpXaJ;L9v_vOc=w+0JLjJ8{r%1_{ODur#=AVKnuZ-Q za?3sS001BWNklD^UdzUO0im@pS`DKQuxXVzHaq3k|_N<&kUc|DC#F5^Mpx3$7o{g6uXUfp%(jU|p` z2Z*=<#)319hNd`}>tR>E3x!9s-NZrFk0+ogN|c1dnb-&ti6u(D^aTj0AQFwcfmx)I z8NX?a?f{+R`kRMRS&UMeyTjL{og@1M$Anjm|8o(EBR^wrBeQ3w4!HL zfw%%4gMGOJ+zQ_2o;k59;VZXW3%J*=q1Ae8k-eytDL=0zU6g5Y0A2Y2)^JwyOI22# zkq{~=LPd8~k{?|m@zgiy%KQvMQ`CR_PcZcojsm6mKH0;TZ)WUBAAmQf9Xwf<2a2*@ zyYR2*H7`KXD1q{I-d%h!uM4Q~K$X6%=|iQ@H7_dps-H-$ej^1KUJ!vquMBO?m}yD_ z;TsJGR1jA)!}oaJzK zH+yqC84iyj$~RCyGXF_lO}@j!>4#{t8@L_xRb`r-Bnm}d7MV3?c%}Xg4rLFZge0JJ za$1QceVHYF#dn#jOcHJbhAT19N$D$iQm|-bd8hsYqoFY-0+Vbj_3-%eqZkM*Ty_?= zF&3KSYUBoCFUcw>ENLqg;^*irb&zUYrqQlLpfeo4!%SKIZBK3|dkZ_6H0L;3 zcZNq-PH-f%hmx=v3XhSOIr3VOfM@cBxli)-<{vN|zQxhZe&);++l$+HHF<`x8=~Hc z^2yX=gk9go>iYR;&DE!s_rBW8`gOU1pbP(Y+e0Bey*$BvpzJD_(eM-vb`$k>q`F20{*4#P zLHB&GQ6Vot5R<-An1mDKkGlUmt;Hth%q27hpP&0APc0q8Kw-EBo~(?IS7ro2C|PqU z=;A1crQ8&ra#;<3wz_uwn|bh*KvNZO@z9;BXSDA&E5QQwMak{tBHc@ML`ng_IRx8M z3A*>M@YzpqM`G}IA7170oo)R1(Vf1uTfh@L+xU}ru2QsJg2qQxRD=T01zl?8_z$YO zCqMh_eiDf=w{FdG_UtHM`MXo3(*=Zp&wl1OJ9}H!;=Breui_F(DAb0~%29DeSq0}l zfc-&)skQTAE6t2rth8Cae?fPU3fEH%-%at^=Z|bzN9A8l2)1|BGc-20X$LlI|DT%f z#|+%>{Z?u=4^r2IK7SCVWwYl>DZcym5RWtq%0YjTq9Pdpfrh7Cblu^h6Sw)=Yc@MN zlDzQLo(=2UimSXI!&jTW`_>?d{vrOfb(B4YUXEmYnKe>;wdqZE=X=?m-$uP1CE>=I zHx~GI!znuK77k^%(_3uuudorm;g3*gz>Y!_r|X7E+7WgZ+PD*%;)(P=x~vxBZivC~ z9R`E9Sc>Q9DYY}^jq`HdyEwwdMzBBMPk*t4fYKSrcQF|$k~Uom?g$NP8wcDSlu9=U zC>_MA9Y%v4XK${H(a;P9t%Oj5NpqUCw!+2mHHJbrxfPlq=0<3NmmRsB?$kT5-Zv=rV3zsCbsnbpF7GXr7sZm zqSXV8;L{k#OGx3DWRA7RVu$eJ^!(;`xxU`o#X}8+A8yII>v4ebBvNVwIW?9b? zbwV6Z@8`>{-{OhoJ?zVUlEKIrOM05o&@E<6Khk8Q-NZn?pEL2ZxMjDKAuouj0*nXm zk}`Z1i^{=VIiNx+f<*~eIBVvwfx$D&kFug?8CmB9UR3|%Cq*qFC|}MB-!-AaW!{+c z-L;kbU3*PiDPMiIZfUJqYT~2q*MfFMIkIZ`g(LkvKoek;)w(qolq|bmtO3RVX6H@n z9(fZPSwXL#^A%T=-=Ru7l>keg^6QmeIT9+MJPnkKrh+`0Il;HOE}_VgTyfB4S<>#o z`r~^&^HKrtwf(Gqx7u+N04_H}!)^cRD z6(U{?TUxAWD?Gk@oU`!@bd_338NT6bUdv+15|6Ds%-Q$_u0^h}E5C=MnO&5G&9a_G zL+3_#kZ(1;%!-~xA}MNlENNrQ5-UcE2D_1bIcjKsVJFWnKg`#fUM8(C^M(1((q3x7 z5gw=Gm)V!wN!*F>M)Cu01V=EHh9eZikqq~Ob0}PN55jJMeYqYQ>^k17yU15szRiNZ zKtUGiE%xFOC+fz0!(m~eaLLI6!*wIEJYH z>aMb)XZbL82}2po8#4^#yBH75&{ODSe|9Ht)}JQe85CuK@!$+UJM$tfR&vc;STje& z>el+EX6Jzz9^kGsKn9?I?`;F+j3;8_RZ;!1@jN)$AUMSX=2;uo=`#}{Tl zMab1bNtX2-7h=~zx$LmoF{C2onz%>|KnaQTe0RD&;TjU*d$~~#LWy$3*0LQ+bz%8{ zQZF<7mDnD-($HONH8EXPHY8d}BGlqhrY$^+?pgz>$g`m`NB*v+AX+kIh&5O|))xpQOu_J8;~ zzxb~|ULAk&5eY~2gJT#;q|mB+vO=tl+I$~Ob3c6ipm=aI>?p*)pgTwb6p9v8i#c}p zG*|O(m4p!Vb~W(&`!}&27em+9l-o@D{GcHIA1l?~2%tU)5Pwi9`2C)3)y7u&*0-O@ zm3Z&m7{9Sg_>l?g-(}ud0j1GlC2)nytdZh$d=Qj2Z?js3r>-f1x(k#MaPu|+U!Pl7PlkNh`3RHX6^+Z&FYHvYu6-88mst5IX8 zNgAHuq`~`qXWrj`XEJkU&g6OKy7%7QYgZr9J*k#=G`4eUvwSL+{KupN7uWAvZ-RX2 z_Qn*ud^(UilfgEk%AHz&v?}&1KFZDr4oi>w7J}9DgU%sPRlG}e4g;PY_8`i6C4z+S z5eLA)%o00^D%zqmbq)#>yY)PlChP>R$auy%Gp0(s;1GRq$d-J!qH*mxexZB;*>RL_ z%?H)L7Pxbkb~k2@X+{)MHjHfZ>HJ{~5fAp{J>~E;D}7aVTPC(|DydmKMMi;{$(PZw z9g#DU8{EsUh!XM!bv#Ue7WEyhefo=fb&eljc>!BHrIP~8%qzv-w?Gu#&Z`AtGqQnj z$CSL*jlW9Jv|pkHszU!({4H<8v2gy1-fB>1)kf4IX~{QizMD^N&18WTx1M&htbKJT z<6BNtvV3UPPO3`xbn3li$JZX5HbfTXSyOo%q7pB};p(dcNs!dC(MJ!YjYGwa3HVdvp{$)H z5*o&MG5d)PyEFGjzp_-NIf@k>ogc)57ztAYo)NfG$Pn)Op`8~KE07GC4iYz)$K@jy zl{IEbk|-$`#ZA3O2}&Sq;m~Odst470$N%I-Z{x}uN&yuQEXGN=QVRVS9bJt~qAWTs zXBRKIDKdCuV@HH0-?KVZ%Ri##+xmMy=f%%+VG!i`dz+_n4qs+^Xwe2LB&m>@EZstW z;kJ5A_4a*S*Vi5FhFtOB{Z1c6LHV_C{&6zfWj5N(Cm3%F>2H1#+QZ?nNAIs&T*?xR zkE1718HAIXsL*yAq9m%6iXa*nR7gJn-CY1V~-*T@M^vayp%ltkMnwN{~2 z?_R{83>S2cLiF^e2D$5s$k7uyym(-p5y!XbQPsO2zbAAJO>EUo52j8g_)>txg%^~> zDxW1M+UaXaZJb8u2nPlRf}gWwT0=o|Udcax>iuHMy@%R+QXkPoq!t>7OjCkB^W$ns z%ElVS!b#$E=3j1{+gW6?XP)WpAC+=Nby1NFVVqwq&^IaYjhp(#@sPs=Gcmj(6jrhl zLEz<2koTf@36bRa?ABQawZi2VA%#dxZLG%Qd-8PaS`N9MG|K={!GKE>oNB$F=&n93 zohA@H(d}#h$?aeHdo!c~e<2l{O55z(k7RBJQaa3|&xda@<+yJNKhcw$A|=o@AXC)$ z0nyao3Qv0C#K%-*q3PP@W3}WjeBh~!WBe=R#KmtN?vcKyacPC2yQ4m#S2@u=@IN&0 zswo(|-!tejT&G`^+RtHi&h{j>?G5d}e~Q0&74i@~c^N`P<5BYDwr|#N!xhpUA0UiK zb1JJi?X8Z@ca3FT#=j-f3(8UHY zf((~>H(L_E);xmN*Fr81iBpg5QKs%mDAl!?rg3;k&W_6HPTa{_3dD@=t?7zi-)BBVO`uXlt^AY@6Kt8;6&4F7mgv--6##1PFbK>Nbt$4uxQT}+zH~eK` zzvRu?Kp`PN)|BzmDsxddCVG$o&IBKIr*cynebUi)*ph;6EI)HxZ66^|hcT>Li7blp zK0nr2f^6e@6uencHTclL0Vi43yZjax|Jw&O;J8cx_dY58(Df9Rb?uwv~!a??d7cys$( zY$RJF5_H!9U|{fv6|`hU?aS;JTYMH>@?`uUeRr+1Kpe4Aj0trav!%Z{BF7By6+_r% zNY&1-(^OT~_y{O1tR2yb8b})Q0*ZCW(&K8wNhbL2lJQY8zzNyPK3^&#>(Z3OxKzV* zNk2a^kLhtpG=F_$;rUYSllbk52KL`z@TzZEn@2kaz7w7NFPgQ9rlsr^W81~I_m+kajI zjAEMhOy`Q_rDtj~C6*A}hQeXh+oFxTs4)W*60%kWXNfu0@~F&0-jW!q_fRRZG{|J*Vu2_b%H zmif?!I(LLCf32@%LD+^z(_FQLaWa4K-q~u^V>bHFnEFuWE7DGB&9AYcZIRPjTo$PX zT}xzaD^&8J23>aE@pyjCdQ)|5eJk+V5C#u01Kk>PxNPy!5?d%V$%@CmQ69qn*9E9` z^p&&r7Ag_-8l3!fgCl=Nl(IR22U2}8@51PO6xYJKtnF~BVix;n5E3#guVl+^dj9|v zY@%9tm@rBwh}5@@oPZ9*Gq%9JumxxKGP!ZMrR2|YVs=lN`Ti$3B)lsnHDL^RT#TsuZ?HcJ+C56W$%c>pjMJ0z=v_qpSSQc%wPmWLtWE_*iHymi$yI<(9A0*LB=oXL2aw(q?X+*7~QU zNd&zqp9+tlJPgk~hjYC4<>HE??7 z9758o8Z#sih!wHodCLbqHiQer80CeCiqi1zbowBMkh)n%`@s{M07w1_(0cri;;?aZq2J4#sm(KoN zBeG>^h{eS-y2v&4(gy?ky5&eK1D4d{Nnb4}Cy3O^;H`4NUe_);e~0+z&(w({*DyEm z0k+A$4e1n=)6$oPA6s4Gf4X*hl>F_i;VgV7D3Io>;O66BjkvvzHmO6rb0JWMtLF@x zy#BeXFx`t|w>Rx!sj6>Q-> zU*_KtH35%Pj?j-Dr=IecI#K@&QShFKPT^!N^#rL=-4FBz~=Dd@sO|v~QNOlTGcHU^J4Ma5MfgJ{w9P$-D4;h(R zddqm#6BO2o=*``7g8WSC#SM!w*IsTT+19%W9IP!=+g}%$NJrFC2}=Sz^^~vXeU{W6 zPHlX{YL?Wg2%x}!;mJ2ME@>_zE+@MZu!Df*paPWxrD{!4f5mm z_N{jG-8p7jW|AI0+k&&=GtDX3#8qY}GKx2y%g9=gqq4GjGpju=f^5V#d%~Aqt*#T$j)JYbX?lC64Kvm4P?}hQ3@I3FYz93Z-(YZh{E3^96%nd3fqw*|_7@FZT}Yp5WIh0k^=l z_Onx@1)q>o_{wPi8_B#Zs}}Dkzei$krle#Thi}LAI|IbLBfg1ZKg`Q_;cJ==^(*tG z$y8U1m*%`xEf?B9j39PQz$r8H`rg12wdw`O{UvE}qhM<+&@4ya(!DOC|E&m-&a|h~ z;b)znDGd0;Jp)39q((deySN9P<#m8@ytvK(gv7c!pn|^1!0#I&;oo=(b+;el{0agR zzkI`nY@tDj1t+$JK;tr`5Xv=`m%$24NoqstzV`N<)?~H0(;5m=sZA|oszA?`_>aTL zaVRG9CNmbbvtOF2(y#OXx~~X|Ivch8E1lC(Mbz_GgoFo657y+?%dbevw`eFXW--9g zKRuzDwBoRuwjOH~w|gvg3bBPmY!nyo&dWCkcL$uf(ZGEJM&p%&op-voYt*B9!p`&J zKYwu<$)jSk-}$GiTLaT0IU^vv_9f%n9l1UDXt2Y>jFeG$p#&VM@TvBr2h$(?-6YfE zs>M0lOcjV*gAx4Z=E1%}DnSL})~vu7m!8%C)WV*R*7 zGyrP?8*jt11_8;(z9ebY)*oAWwBS4+)rS~LIB^X$qbarjPE>qe{{gtidOy{`K9ab=|E$U9CTKk)!JtvT)&13OP4b?D z7kF!GQg!Jg<*HWl;&Rt98aHo#$F6IcP*U8hy)_Jca_q zL)0h-?J(Xel(b?{=yO@$!nh}`+7v18k;0hL15C7K4ZGv#20D9)xCPd#F2;}!#!t4P;~iMsu-^0Y%k z7QA01*^;kE$4*9Gv}Gg!sY-GYX-WHcR6W;lq5NF{v`kTIF&@=L)HzFQbFD2u z-hh<)>6f9WL+`|@=R5Ho9PiT(3@zB3)dJh`e3llzMVq!I&F%RZkcozWRL6y=*_7U4 zN`~dtyjR*9q7w8eSnI-E!sL5uD==~p@yaBbvOTVtK(}4sD*4}THgnjvMM0o$lM!(-GJ(o<9UqymKM+~r(-WW+jn)aFg19Y|%1_<%^4_yeAW zM~C`*($6WAFYTn_AZ|>s*T4|mEVx)^(mm*Cg(JB>qZp~_dN23x2ens1dVsOPXT64r z&rscuKRSZ}bDR1BdBqujaB5H8Al4yv?u?}nPY5r%(xEt3Tl)$-6@G18lo&hzfZXR+ zUmMU(^{$EgL3pZn3gJS%Ys=C2lvme9UCqD5c&S1rFCc&4ET(=c!eIj;&MGyDq?zn@ zs1jB>Yfcwl%2Y}pu3A}G*|9jadJZ3cVCb1{&_&c2Ma{&;g~*{gn?Rl+Oq z4vdBg+WpkAHr=LyEi!&FKb`fvhOP9Y3;M5VpAXI*$TaJYtAw^aw4Is}`gaevk@fe? za|cF92-Ox%Et$GdUnES;RoFdxX31P;ctmOkT9s9JT+1wRYsiqxaE>Y;8q1onU}8BM zq3cWKtY4w2e|95bIh|rTh{=Uk)Jop)zybuhRZH@846GnaSI+qnbHgDs-Dr+*iG zFSZ+f`$P>i3->LYqQ^-146;>_JhzX`>2T9yEAjU^X8{RASaiwW1iRkC>3Q=ROmlWiTVy!>e*P3S!?sz1m%X;muIBJ@QzOFiHJ#0b_ zJlyjWlzj<4>Y|MBLP6Wx49A4Dwu>J{K@AENMd7}t2%?$%l!*)*8wOQPsVV8jqp5D2~wW?za&W)hwRB@6IsXKemF_*ZS`R9@L^j%kMT zZJZ`+bA7$NI>NV105VENR*~3;M$>S)0|$jLWAn#C+hZ+jhkAd)v`gjmeevwPKf!g? zYxAb;?p0pDpH2F1vNVNSbxpqPU~}O~plABE2B%;wbEj{9B8sMNBAEcuz{udp%-jCA zg#K$8kZN32I>})Xx3bwa1Izw!J4cQKmiv`cuI92uU=r{qRlXNXUalySjb%(SLl%ms6_`!u z4{6=HzLFKw$swuI$&C19v)gul6;3KaMqU`jL4`T!A!eF}mhdx*MJ_=oFb11AM_#jI zcFzgMXtt&sxJJsFHiZ|4g=qV5tt3ISjAE0H&#Plf6a4kRhOPSHak$YeW!j%NvRROy zfOhY^;|GBoPaQQ(**QEuB8`w#n@N5pv#EcJGRq>{pqb8}s*7@OKZJY|v3jWVp-bAFtq zv6i{HqciJTgV$r62$1R{deJpciKx}f8#cnFD6ggH<@4-4Vm}T69+;+HDtdB;$%SV3 z{tP*+AQDi(VTkO_74yE=V0dzy*!#xiXP)T2vPs*!(F=^Oo=bPfZe_3Af)yUtYc%dL zslCsh$wIF!;B+ZRj1r9wHY;Q?;7t>#*``vPzDFDGbP_u?=g!p+v&1e_9K&|u6ATR< zsPKb&D?LM;gwl9wGN^+v41WXOSSD#rPduOfg9&WlaRIb>V{MffxX`EBAUEZ9KNmv8 z(KTSMImLQkohpe@9N|EgrS`kM1EO;(0|QgrUp3e+{Sh)NKe}|Z4aWU8*jnA0)qEFc z{XJ>ln(zG@I=|Xyy4xAQ8-ZmS)JEujonmo&)@Wf(PqMo7_GZxe=IgS?a zp;;&H(+>f>zRfH}_wrptG^LJlEo?7@mkP3{bkFMe>f{Oc9aR4=sNf=6^MBCSR85T} z>+cwe(I2+ty(ufR+y?FtLe)-Byq+%!f5%i(RfsE>F%V-*KK*2pb|1_PNAyPqT;HV4ThDdBh=z?XhO6(uz zK?NK=-R7oQyK-)v!Fj@6BD>W+a)X?Fg`1Ks1^Z@|NuR$z_T&Xn-nM_)HkFS^xn|L<;Z`Z-cTY32-s6OspqzG-Zx$=-+*I`xDJCK%mye zZKA3&%Befcz8by=f*T(P?FHlirUe?dr8DgV#SZRY2Y3Sp)PK`0=x976qI{0*i2uB= z=jZQ>I2vky8qI_Vco$U0^6Xgk@Nua2A9RwQd8gV$gN*1PNSfm-{{L3B7D&5}T=k|#@x<&=^`Nv-4&Q!P!Kk8YQK666e9h?4*(~#+iJXeyhKl8NeOiGQ z{0lPV);i(u-^cuoJ?)B)%r!W5D9CJ+N!Q?8o(0i?L97)~@iH9R8k{bi!ow6$p#!Kp z7ZuYTDu41y{0qHhqzhNyyr)?TZcSuWR$`iG;elsha>}i6IyTW12n(ciaV5unZ5U7; z`X82+K;vL*&S42C*yzAq5l`2*H-&E@tSO$F3DxGAl2QObCVp1dq#cr+Z)A5Y()xo# zCw<*$bdy9qh&{f}VER&%Y<=Oeb_+p(c>Cdz1~?#2w@_TN%2p9@XZ>QE<C|F`?V~5Ck=kxV1CnVe%Gk$3yY5=r%<>1cYaHR(EXT<@Cf6ysX@q) ztye@rL}H@JC*W>7!<%PfKA$Z?*NM?3ZosOJ3By&v`EA~ntpkqvovo}SfM}NFSOG*( z7)Crs(vH{e!OAxCWWl;qp|dO3tE^KQ z0A$LfxsuLCR3cyc@k)jcLGmCIyTBA2*2~mfTNTrdfqLxiM&q3^vvhgf##D*8pW2Y< zMXj9E(%E^QtFn=xkt)rk4-Hc=hUyt5Tk^*bI_ne~(dzgjH5M`jok zI!JU6nR zxK8G29GNlv@*4hy^BQn)?tT9?%T~E?1vUIU^cL|(#hxXkPqa-0{-?lgCVaE+*Z=%< zGWu&-{G^8Ea}Gxmp6HPiq1( zte@7WcWE#{4eaar)nc=?hqt;c}`5&l-(%dRk41;ff5ZNYAynv{Ko?<+Bva^%$= zmfS6+r-3y}{G1RH>6FDfYqH=gT*$Ht99C>H+UDa)j)UDJ(qA3&lQt&EZ(NT}d|n*l z$!rn~R%)f?Gkg7S1OxY`AB>sbbhqA8s)|lOUm}ZaWdou^EF*rxvSNLY8Xl?Mv=*pOS#*-;(l>Pj^nkMSp9x}i`g`Ypz7g*2$9;l%C=TxJ&W>865-kdX zNO?`g6e86MZ_bSH=@g89^NVxfRd}!i-m!GHB_gPb0N>p3K{3{z{9V0^eO5hkXwjfj zG*x;~tbmsva{%PPTruu~BV;EALiQPXY_ff?41 zYtsiXt#k4seO~#no@=gQsQ$%{fYQR?^CE|M*6N^2PiQBWjHEjir`Y7fs*XtZD!As4 zz{i#72i?~1i%<3Unz`q!y^my%hJg`QA_aDA653;Y?240`A*+BHF`sYx)Y!lp9&KsKjvMh_jtM6JL5ilhqYae%sKpeT>5YU>jWhrv3j=Q^E*8cB-|A66(Y zpSdWMP`0@u+148wI4hTKAELS)EUuOWs zyB{8Ik)sm*AsWs|bde9O&m4})Mv`qNz!swx)-NFn!7JI%Wt@&ur)0fs;kAF5yzXB9 zh~tgzvuMZ7fsX?F>Lfpmi+sRHtWR*|FC*Du z*G`ZkLAehD?+Yu=I@>oWjGf$?79V|`oaGnn{^UWVd?Q2l^6+%yD&n7*#40>CKkVJV z_9lGK5e})aJd9Oi*sCRwo9da-jo@bvjqV17BO6ssIa@60QPqJ>g2PPElQ)JX4$K*_ z4EZ|wKsc}J60VHCE20w6QrmQM-bg-B9q(dv3a)ek=`w-kq+i zlBaX{Ata?6!0L&qH}B0_k@?zf(LV>CEkCQ1ukDR~%wSYM=D6q%d$G#tA=4D(xY+A< zx(Nq!=EllYpPiOE4H!CIs;e5Y^fnVdhkMC3Z`{QM- z_=pLJv2=cY8wi;0Wx%C5m)|5disVial%3kjk2MfBXiedYmQI?%xgWWawl$~r0vp{7 zWT+9BcQl4Wbp->F_`CAG3wI#)ZiTsRhNkdds9MNJlNtm@+4LMHsop8v>3#UGt`Ee4}{d61o-; z9xU(&@Gy?KB|{qEY%aMa>t6oDD zOnO_#yH~&bzBnWt2>yIaGD0!28p@;L_}0Y5KmHWSS6M+RY%0))&gj|_lv1#Q!euRC z&CMKX&7gvU>}FRREw!s_UHkp$;@rBUBCg`Xpx8;(@=EejN%K{{B4#dPehU^BHn$k-RXI&zDy=@7q6; z3|D*f4WD}wI!RHU1KPC-4Nq!>0#5n}ubgK!Yy~3dOpy_!r38f1E{}4k_oDV?e8aSw z!fgbH(!0cFQJQH7!hwNXLMNYxRmbDvC;4_t+CLluu3NxJf^NUslL=zbM{Ot~IEb4g zAg-V>I=DN@%t}E7&UnsWuMLkdUa?_1ha($gBPr}Ml0KVhH%A^1s+wn7<$r<+E0@KG z3b(zzkGB9^9!25vH6Dc>yGwBt1w-wNC$$|THSp4iS*khsnspSy*Ywol2@2e8BK-%4KKwVQOncaD*2<*~!Vvvku@KoAjdY6sne`TkguEG@s*u%oCG+OyfdL0lp2Csy!Js~f} z2ngHXEn=oHKV-&D&=W~fue?pDfqJ2_)V32ZTk61#QfOT57?(ciOXRTD7YP%1S-y0u z7=PL+O~oX3OP16XCV-p-o%jG0`#Jq}FYEJ9Wh3Xmt`;9V@0YsHyfTAM3GNbwANddt(`_ z8H9`$waeyE&q+*>FV6HVI0eHH0T6_kH%A*etXMd{P^wM$FqXj5j8Gp5_kvqCdnT0$kgn6R8m z@!0wcT68wnn3{|igGjo_c#jt?yHLV}4(hTUtr&QZy6glW@%b)z$J#)FnTzky6VHG? z7BkV)zL~(+{J>JGmn=>uO3}*_bZY1;>=zpC4p*&d{;Igso7FjthEsSt_v&$Mp+5Z6 ze2&UN>@ISUgl0Id_oPo5Bb4MuYQl)~Ajl{3!@|{-PMxA|IgoB4SyvkK7X~)XzflnZ zYq+dF#&}L~xPpFBueopzs2^%d%uKg4pX)c;Ks&J}mMM5=@8ot+%qyWz1|s8ROW=#I zHkz9nEJz`vr?vY$sQ5;mH16SS0Bq>V#q%&>`i}s@mIduzHssywW91XnFm<6n@0CBY zCd48G7jr6km9MPEMRd*0yu@2j&WVwbi3xWRepQCVvUyV)b>Xvgla5D)kozPj9=Uo1rr;HA4xEB@h^w{n{`^rY zO+TDJ#+>TD&27H?W6)sJtp1*{ce&@1$y4UR_?r}Z(M4)chs)*3iE`XQ_`NG^Yzj>l z-1g+}&AOh2Ml@ENB0)Db9|$LI?aIqGVP%#`9LZw?8Fw}Jt!nk>1THG4Ol)oDnbjeY zy>P;sd5S+=@XZ3&+x&nZsEI#segx6OuIfXgbIrc8*1iM=j;OU>Ufv=P;h%j7> zoOp;Fl-=q%;MtqLz|(*K`icO!B#>|OI{i03@)Z2=RD|U&DU>Tbv~(yA*`I}lmz4(o zaxGo?XGL%^y_|{3fKeWCh~tK~zQx*V(jMQFItYy-6+DROIF4wof9U*z3$MqM)mRZ8 zB`L$GmWdm5BGTXVL9Jkr6SGJptuG;YCiQ_{kYNL>U_83b2BU8y#3d`U&`gE2yHw@% z{ojy0$y5<_%}Ea0?1S6~UwfllWNFVYmF+}#ju7}5t5qcrN`dj6TVY&C#_W21xaOKr zWrktkVQOwp@E3>C6~@jOnZcq5X9rA84a>vE!RaG7xr%bdh?SPy*?mA$9?;9U z>T&A2M#R-q&5SD;3!s_YQjKtTk>Zb~TBnC$jivEH+JrxpKPr=w`Tcn?NQ_B7dp6Y+ zU|t169YRUldKF|ATFh8d*7Ec@*M(v#?(eaceiSbnpRgNGbHc7=ToZP`s_QuhT-4W~ zYCS{lIv7s;`-0BW9wK_{Tx6Tz=dD;om;<}2*2tRwFepx$wnUbp(_`=S(pRQ0vSNlr zM;P|KEV<2>)oI;Sk?`lscxAA0K!(1fq%!5IW0q|;bHrJb zR~s&FFnZW6iRglBwM z6T6i|THj`61CYDzs@pMLp#VxPvz5O{a7@paAe45e!48M3VnzQCc|*peyf!F1@e0-8a7%+cq(@J2YGiSrx?e!f7(h_0Kjk;7%g%AytB6yJu{dgf?%rxT~4XD^#qB9E9BWYvR+osOnl)+CX8)0vSycvl0Ps;#vYz zqAOqntslFGrB!G8Tsg2Etrh0DWaw#V zyp6W7q;6ly;DpWcEkj3uaR+*bENzH?*g0E{I-*2LQIA|%Z<5KFJTmNtr)Eqb6R>QEb?ep7v*d)t~hh>AFzih z??QcNZ_1t7NsMn@ygf7aGBFEn`?B_`>#D94oe9qeD_y4~a||JF!q{A2MSm_$;T+N6 z5=cUr7iKcF{L?dQ3m<7gJrl>jjwg8F2+mWt+t2%wh`LT`5M|j!{?6@+C#^*K-T+}_ z)sHElmgOs0ex_#|ZRboePj%^An?Eh$a7A&JU(X-OkC8D`X@)7g4Z}0wz3>naRnlR- zeQ!5K*Py{mCB^cVXw@>@T<+LdZWhW2A__I5;F{Svr@IVADV)!@ZQPjCpT7Vym*r(M1BqKy4kh@;s%0Voi3*CZgw~g-V3wv6MSI zpxaBrH9x2S_HLQeHG)hx0by(6p?<9=;|?P z#1oFs3pmkZpUH~suGtn??@~AA+J%YVqdvlF%+QeZYXy?5B# z$FMK%jSq?mzQY+#;W5d`;MyxV3wCZ?Bbl12u4cm?JR%gKc#eF$EMM-zY0rtyJiJf; ztk6~vhTkTI_4ENQh(l-)%4Y85%8nlCRzjd^>#Iklb*i8OWv>N}fQnBp?VMT!!F@L7jERAo=`(}-fRRGk8;=VTHG?yT3ufU z#{R`H5-~h#V&LA;vH*kGT%=KK+WD~QijpcH{>**iKR$7QnBy99A=Y^tDGHn|*|jkV z47@F&rEg=*V|_SSMfh0x;J`6Y=5g1mkhJZ>Pq$c=Ye6?UO50Fw2PsD>KoPM#Eg3pj zt)tCqrsHawvpN)G4kInq_M$u}1t(`JV5$;J_&acUCGXey=BB1|$EDV~vINBTyW_nN znT$)e^e*4Ccnn!{8L1IO5e{kz5NFtoOK|q)2ge^+py7xbob^p26Us!pwm2`O&G+#RkVrCa5w9_ltqA?&QechL;{P_F6Q4`CECXvoZP5C91e1e!zk zsur3A*zI=;H0tg(t+mqGKJ zfj$xU-w(uSBxPQpL3T{+4x-Z0%wAe1Of!F;bZqEE;SIHj>bkf~z_KJD-TDjmSBCG9 z$kwG@1a+(4CJLfsTCRncONL&mOzo#$-CC*XJHVKxw@~>nKGSw{n@V39un*K0R?*Ga zF^F}n&PQE$m0z`Y13bj$K?aO>sBXwmM`XCM;+ZJpq>#@@8OY%8xq_@FK@^9NoHc~I zK_+kB?kI)|aqEp-$!(%V#?(-2+fg6nT0Hey!Z%fz1TeDoKBCW-ha41yi&Gd&JN2Z( zOaHIUP)Q%>$%Oqi*2Xo~t-RNI9awdAVv><5sT|>~Krg@j+h`5}>TxJw{deJGjpxS%;H9psH%3wEEE1ll_ z$6ub+?@9@Y#zL4H^03pA01W4O^n#rkValrkb`L8#=;9DHOYp;-lamQcsF3Ke$$2G0 zx{Cgl{Nvlul8S6*D*YZ`Sh?dsYcr9Jtdg?sE|eKWWT?|c+^?veSslyJLDA9I0wv0) z{|nWlbz@>{YNmJjl|a>|$clw+#`9Lhk9h_w$AhW>I>}bWoy7;+*xreNzkY07k6rgG z+MEUkZIe|-G2*?%tI$O3yH>^A)|8?YQFsfuY#VLPgO@|zkb2C*jM&(|cNK)dUniBc z8lGS*YP_-RAy<*odIUr#Q)_>tahCO?Ir4jOj{tI|sNklBvxkpk-paJxz9J;Kiv%QQ zV@a+kTTu+Lm!*y5OJ;{{E>awUlg{aA^?;iUZ6KWC@8d@zLYu^iM_WJje-ydPzIA`N z(;4YBDd;3Ur%h;9gMJR1E7zr^eiNdY=bXZ3LA|L#YOUc(j5&gq{x7_==x1Y&CMPgf z$A7^KmlF0ZsWq7^ajhsIG54PIMxn=&sYfiQAfm>Kz_yu}FE2$#RmC1`V$f0RX((>% z`0&xrr=`0UA$eb_8#t!D{A=hl(M`d>`g3p-{C-J7j6!rbf_k5U?#0rZ^2OPRbBv`C zc@aDAp(x}g;O$iO^Neo4O)*Pvce|81Lkynn$gOoGsCBk|F+;`Gm3qkH0oU;k^MO9@ z#47Wn0{0LHWh831;;Vg=3{uSBm7aohXHo2)qL( zb=rLoV733eZVjQlCUa4(b@%V|Sa?QDBjFVh-PjrZCE;)SN|G7UAQaHn)s(*jep2Yr z{dm{XCmFxGlKwaH>cuJ_Xt~p~)saK*esapSCw#c08#(9XnvsoFS(ZxCkm1Ygm$06< zOH)_1$+l+SZs_U;T$9Gvc?u~{rOq2Qb}cG@L}$TDyGY$Rj@W-peLfPv38WESeB15V za~NRV$(Q7-*%o!pL7|<_Z5o?8BnK{13BUNrf%UiHFQ}OUFLc3qDYx*pEwn$T8AM(B z{~H!3`_ugUE1eg4a;vV!=wG%+py=pxo#z5YSW@Nrtu-XQbIi&v>OZWKw&hu0tKV^$ z*$<%Kc#t){%WpL1w8NS}ClyWr2K?{v8m1h=PC6Qi2NTkC!A3W`Y8rWa_WDuhdxwi4 z$Z4vBQTw0cenJ7MVN7M{wb_bRZGb_O)wR|)oz+Q1RhD|p(<}ysa^!u3=vmUTvvYJC zI`M~vVqCvFFXaKIyr7Z_qgo1XBDPgL6X%e=0VgvNJqW`cvX;<3w+4a2`|ApDBQGpr zV$PiP5|IlGtM`Oegz9Yt1K~!q85_nr3EFG3+Na?UMsYh7=Q8|7{Grr8XqdP`;IW#+EetWo-a0xQpEU0 z15Z4Px2Xc%-uEP8LM6*4b=r9~`~UKm9Ji3eOm*jMhf$|+NfMw=$lF0&j33n_P%_CM zCx{Ddng|D7^xN7d1-L_r?=Em3^-KLozt(vf2ROr5l3C@BB7Y`Cn?%M;UsF#XY43l47}{Bm=#o=r%5Ew#DKi$TZpsQj}u0^&OA z<_jB6?y=q-^n#1<$jOG6lREj86hN&wF~mpRq_pNWS|O<7kDKopk^fLq!BcT%48(&9 zx2ZvZLl~whvpsJX(C6mi*SofwQ}Un=j^@?8X{mP6SiA=+&;b-|tixN0_+1S+EzOES z)VNTWkE^T=C!HjHm(@=%DS`i{;p`gPf1xY2Op!GQJt>DqLa??p7Cl-c%Ci2+Y zW`r=Gr|>vt7~!;y`W3mL;c`GqV@zDG9A8RK|K>A(wlQ08KjDS-zr{ImFKk8q2&w;c zmc}3--f|vjotl+6|F*_D#~yc9)u-V)mT&IEU7+D>(eeSgF2Yj=s3-p=@S78EE7a^G40nNjUISAj2op) z>1IGcsS-#)_V`xHZT7D~_vmf64|a7;>&u?+wf;w5$b*ZXVwL=vyHUSSR)g>Z6>6B9 zPgvI^tmze)+x^d$)jHm}vE%7rcd41qW-Kl~f1^|<5k&?X$4>ujOw4?gLLk8#j0G=5 zL&Z<~lAaHX6AE@La-PsM;uttKV_nhP}LbtxQEY9541V8(igjbd)!nE?~(LM@q*CIYGX>`7AwF8v$4M3^!}Lz1p5* z3PnklnF+2?Zdu^4)6Q@G&#&-@|HF5QM}rUT!=Ju$mv6KLd132OzTf^DbAe?{rIXQ$ zJd-&^&@spw1y;>1md#CitX}R!CTXiS^F;OpP1Pt8Nx(Jg#!TL(xUKVS$tttKRcztO zFvDV*kqt#1)_gFLr&hC`(< zF%q8PXU*@hq38L%jvsKOILMl@$x>jEimcLEIe?}#l1`GezQx_hUB*NAv4q8ZV1}x! z;NmbHoaL9+UgEXXM?982Mq8zcThS?=$($i!M|eMei;A>~I3X@1F5!qOmMju*BM1T% zv^*QeI%l(|cy8;ZtD6hS_fXg<0qe#(n|hkz z(l7_CJ_4@Ehl!7fyD45sKgOB-5bwutFd81G*J@)VJVr&7>-zPEt7EDly`=*jF7@+z z)4RNy@>DC{OWYvn23QEr`=@h3jueL&3r+A^>TSNf`7%MN^G4H0oXsD_lAgQSEj`1# z$xrx=`R@{S!+_#1+TKE;_6^OEV%0zos*iNw=9qP@tUM*fH! zk#!!E{Vb>jUURSD$|#v8hjC|(m@`SUZ02!J&=l^hdqQbs90iaN2@)bfT*bMhKH^*Q zEK#ET(D^f>BEfn2BxCL<)9OCY$uIB(Pcxw=SY?$v?#D7|cL?j<53L{Swz6-?!_$O0KM%&K{Asq4gL6W`RngMrDi zT4F*?)eV(FdShJqk%^UO(BUYDNi|840?&)*eOIV8%(1O+?jk5z7We1p>tpc6^XInb z$6g$}*#4s$J8}2itbubHRBMFT(f26NbIsYqhxx^&O90%SxX+iLd7MSH%oyW5uRTvP znWS{Om8olQkzZfJRSr>0CtNXU$wrxO-Qvv?BXn;iJa1n$->Y>})b^UWYtH*!gF@A9 zRfLcplU}w61Pos|ryjsNjcadDPcvWr@>zcL`C9%mrF@jxe*;_Hn3E$M@J`VM-fU;kyWOHi`c^9 ztLx8lx^T!hJguP=5A`&zADXN2eEJl(qLWmG!@slo5+$v|YpIJk(juqjc`bF3pc7!I zbbxQHKf}%FI1A<~2dpl}!xI>;f_vvjB_=5jiSsEaD%+C^#Tby!KuI=@JLK-dZ4;*imcjE5#UTpA>+6^OV&Qq>fi63hkj94-zafuFRzN!W?fVReIW z`6POsA#H%cQa4R@jCYzo=34Y7!=+)$qJ*vt?nFjd2+Z?R`dPlX@igxzuk(|ZzoH~d zNF{kX^9&N5w3cNtutZxW!I|8U?_opm?TzQ@vRb^c5m@7Qy8f7TV+A4LQ28*rG8hfr zse9fqlu68q(PMRz*9thgOI2FfHQiRNj zh`ak(_ty_s^8mKd;HhQ@)3vLeHDV?Bq2sp$E=kt|@=9*IudML`j#7LQ|IqXGZn7P4 zO2Q|xi)b!rLK1R9lw_VgF9EkAciGYl2&ITSVK!ZpFQ%X4FI#?2U%7{ZRwCqtxf#7q zQL~uQ^2`P&2)IFdEboNQP=cRyen2{C)0>8p0l6TwXB|d;seP@jkEhQu5t<=sN4Oci z!niSBX-DJRMO&@5G1#SlR@0~vnQ_6|ZxLPneu;tWD+yw`D;L1}YN zoTrU0guBQeB>x{?mfz&0e3Co;~V@E-^=;0Z3Zn!tt6dOD$&okvtFzZh9f%^sr7~~huPw5f8EU9@u zuYH4%G|`nwM1(O3@+0RDIWM1O)V;$y?yJ0{eVsT73=AF-=eVnG@s9g5`o$44D#ICZ zmJ9B?e8_tUG!i5U3X{WPm=od{f93w1h>BtglR+^E3f^RxF-3+{UurrQrY z3bBWAYGa1|B)y?R^FoGC^}nX?7sFpKG;pDQi_9JO;oB5E9<$s>%sk`&z}{< z|JzEn_c|^3EI|BW?fkdV^?z6$`=y&*+T!-i1ll+Lgv$X>6`&%jteA^f!Xja(cq#o9 zCkls%I-!O={6U&avO-a_ST;Ag6u-mq!a*L-9;LmSLib&!{pJw-_Kj)&k3YM}Z+!15 z{UvUTkoG2Wm+iJs9HPBwWME(Cp zd1yu2bXqN3iH-0tdp-am7%cYl?A9}MR8#bn+qe~(;)D2g-c5eYs=127p~>#xXmNng zr5_>cgt!_Vr?b+`ikaqr@A&~|a!0ut8DT!Sz^1;6LJ@Mpo>8Kblw^T>;kz6v5A(g% zZ*j2HNn53fzH%1}<|>QkEV|O^EBD|ChYR5;w)71y#cwhhSmu6coRr-})Q+(bSm4|1 zU*t=hPjWqapS9>12dos9u(^`Bf+dP1?G_4JmMRs7OUD>09VDygc&>P!nZPnVRwutm zeZ;NEUB0mSJSPf6*ur5dxXc$epQWl*c{_2L<-jW(&G!>^5>)*0H5rL^;NZ?o9LRcL0aGNKyH0hJ_z0g4KMeqx+ZwrDd_SxKIMU5!w<5w zTSaC&v6t5X9M?PjZ;Vn}bEjD69fSv@!RtFAwsqNsXDY&ElSx~#O01-I20w48+!3Do ziX#Pvz3sNqkvn;eIzu5mBUzz5opwiTgO=JiuLFA@yd%JLLaYQ9DT)GNCxFDWf5>SW z&&RX!z}H-0irNvorZnD7Tt!zV%VwIA%n_AMC|nYD5<>+jYI%yXz(i=AuoGb>I8C5x z(Nm2OQi`x;ps6g4D{ZYU7vq<4sWK9}&qf0?7>AW;v{#K$kid zs{EPyBfc$vh0E?I^oU+kqKOT)zT*U{{zdoLD>XDu%hOD$Y2I<);;1-|K=7Yw{}2O% zHP-mC`{$(9Dh@X7bn%A!bG~Q%9oEz;t)h=p;t_h~0iKXg^S1jA_tZ_kqfqMvTn z#mjtw5@nXu0w1~;FbVQy?Q3+2F4}1Gyul!tacBLLp>3KEi9-YkaKU|#C*)H!iKGw4 zK?sS3#R3bQ6sHJ^ATA1li;^|JFOB`+of|jygOHxT_4>Z#`jbllzE>*{zVl>{A*cxg z#17rK2j^Oy)2-9tB7{W9?F_bZm}Z)&iW3u2TqzK)L_-mbwbFO`bIjd*m(0>kea)z7 zE=D!vorDP&>3UwDl-Tvn#rBL=_4`^9fl{6TMQH+M>ImD}Aw)zdTvrhY7<}~ceSY-g z%WQ7u(KLzXd%63afAx2_%k(FjsufZf`)eq~j&9oT`ZS69r{!t9-z)#8VQlyta0eL) z>KK|JlPePn82hH(iz#RZq@>BkZ)|f#6YQwRWrlY*eIG394iixaAI{o-9pqT60}yEXtLwnj!g1q@-joE zKEAT?1W6}aci*T`6s5)0_&CevI_GkS>8PZLI6-Rcz0cz8U{+i_b(^WsGDTTtF|f|2 zk)bRtqHctMYjUXE&4F?|Z>ByW;21<4laf}(5f$31t#nvzRHTc-A!dh(Iw3kLDPC=U zpO_tHEHuJ&aFU|*g0KvfhltxTTB<2*;ZoE}teC5W+z?CV0)Nr+Q=04~&u%@zWN40p zmgiXUI9V;v<@hant!}=%`cU3NA1cTB6hHq%2B&Dg6QgH^9%DU!x#%ytwr=8NEoS)xxTs zV8d7;Vh6by9VO``$;lkHtfDJ}wn{q-dC&c0uXT_mbB>}`=&W{Al2yX4$&$ISLw#!dxL7b$K0;MinDK!beqbi*<6+m;Fg-14F%z7qr)``y zCBwT3c^U|8MkSF-6^G?L!Ex=9YTMaDW=;SAAOJ~3K~y_St-Rgfy|KQ%=&a?bT{Qq* z^NEhL{mk;~lQr-zwhM89d`KfyaZpvf;3$fQx$@xUD$u3p?M2{6Uah5<0tZD;ri$UH z9pyB(Mc_#*^8raVsL`3N;wn$SUG7Rp?YZLt{aRU~F-TKG;kA5*>*?nASqD%E#zP|v zSi`hcni(kf@WYm$lGQVW+z?5-nN?#6*H@X62nI@Bq^d`m3$8K|nq;VS041s%DITJ& zlHx}69^>H&S}U!zRa?ktIjm}h{?b7TdXctjGq=Ncm^Wv4eE(Fn8IvGuMur@j9es1S zG{~{SVU_}GY#LdjMv{@lG;^$xRhx8cLu`l@X7q6qv~XM=L!p>(?{U-ph!)YsW%mOT zdJ`q-U{ggH1{b(UhE*&|yz9P#PLK)~-C~H#&UljEB6F%_yjEnnbu^$X7P1OXMG zL$ve0`!oK)o+Kn9^owC)B)O%okfNO=3EE{Bx7=$4WPl9WI$5h~g$-7@p>Ciz2!C7@ z8)BXR*Zn?c)ER^@aD_m*4hzh&;jWVvTU-;@@DyTvTd_UbTa+atu^`!u7v*_&d5PAh zCNv@HmNZJJy4Rwbz(F7pE-bQ0(oGN|+yI1qDf>P+rv_w&khltFm$y#@ONlHA(ZpH# zC`u`$@&LG}-XjRbn(009ELO2hF}s9NFx#<4OFl-dr27G%YAlMc`ruYst~5>;202>j<9cj@ z#lQxUQV0!+rB%5Rz0bOyVYqyNCo)I<082Ggcnj+@J7%ukveQ_9FY5cV10_(;WKX1N-RFd)V z6l0;g=;)lyKSJD&v1G0>SUNySR!CN3l(h;ofkhUBi?mzqbXMAVB!85dz#^8c^264j zlhroqs`T*f_0MxHI?9HTK_D0l&oLXE=Ys@f^*m<^hj}4=j)>!h;a)egj6|l{(6_h} zyG};i;9Be^T~-%IN=F$i^)VY*VoS@C*0Vg8JI%dtgwgOQ$!dz0YBM*ZBRrKk$Lmd> zP>~gu%}v@XDJDWQWVIX{W`=?+A(db@Foh#*-cEkVU}*rUG_uAzucqFl*XrYbXbMA_ z*rLkS_+?yN0+k>oQ3mBjiHtZgqSY;QWs<6-STPosypWl^#*`q*VSD zazeOBLT-pgP_P>!#Ox@Rvt!6Nh5>{a;J2>j)iOOuI2OlK*y81RVA2?U$^p%Z8ckY5_Pttqc-Z$D?n)w zvs~iknl#aqqSp*ao$Y4ST|z41f%qCgZ~QhjMx~K%4Y(g1_1dFcR?Jn3vcQ&}!IEXa zk93X}57T6N!H*O!Rblg5>J0>v{?Y(dVKEk(KvR;plJDY0U=Hw=^_MwW7~+-I57-Q3 zXf3xg8(hE?0S*@ra;z}SlDULLqpHd%;RQfC%aa&dn77=YQC4}X$^rp5qPMvfx$CQN zX$*-+Kxqui)3k{;PRTQT=)TV@&N7e5&oL~Hktd5KDxB5Mkyj-?a^FRwW2-9dqLZMA zu);D=h^HA-x47e8;W_O~#EG-P7G>4TOZg67l%Hosu5g`e+;x*AMKjB4fjCKK8KbCj zJg&XO-bR4Gy9mbyhwWjG`T35Xz%$Vb6d#6*-U>K)!vKOvw(C{58vKX=tF@+wEC=q5}Y zSGWv|Gu%}-=oiC0E??k7_iYY}A;#Phnnj9_-HRNN$Lf1emMtt*W{XXrJ-VS4I&f%ebR@%xqMxQas2Lem6N3N$I}e;c=;{TP$Ip4HlLcVdOj%_V+4 z)Jj+03z%^!aT`lkv82U2i4VCRZQ{ADM~T{DhKju)JP!B$#5I-!%d}Qo87{qu0K#^V zs1w>T)(m;fBxfNrlTc>5Q10U8{tH2oCF(ouDi<-=_s~J;i3>A9`yPkoq zBOEe%o{F#;iA*yST42sxpvUTA)m){&+>0YzQq?#v4i}QwnGMcjs2~A1!?D5uEA}c1 zm(EHTK_|?jxkOnyHZ9EXejoX!pNPSZ8+g-1XL zZ0Yi|=C|0=bM%+{(UfE~JkGp1jf+D|rG??*L1K>QVaLWnDuJsU=7RGWD&QSI=T2ZM zgZaP`Yet$S(+d{WZM8E{?xCO+c_IBAJyr*+W}1*20g7~pS(^x49X)&3bT)W~-S4W9WtUe|}9)Q|3 zHgJSX)J-s49A?d2=aKwrPUnv@5t?TtEQvcwx+~orDGYKYc9XflG?(HZ)&@RGz+`ZI zU(YQygsFgQ(JLt7lC(Xks4YAl`Q6@4fQN$lTDii{94{*`;Eb@Sh%#9%&7$FA`(A5y zuOI<8L_is&wJgF1{;_J3L&bxPhHg<2m7T!4VdHDO_uUX<`x|;qN?mOPt^SX)ASUqN6D)#ZgT~RPD-?J)xAoSNHM?=pST|};f`Sv#1<~=EHSG_*<_hk(M?n| z(M=y=5n zH3_m10!KLXIvp&i6&z9F1^EJJ#W@1PL}{LX8Q}*N+V7BGn1TzBOmXktp91j1x8CAE z{^jow2?jv9z9Du^YVY78u&D69^B!69JRu$bYM=p?n4;)iFSRbCt>L4axzBfz_nuvA<5Q*i z50hqtXQ2B2)hTSpMc3p5Y4@dJk2dYq=Eib%=i4jYUitS^W!aA@8JSw;H~z_why*kY zL&q>QOx^n%Fm-}KgHXUA6f_70bizCC4T1sP`yDh02aN6X0fQgEeHS@&ncweygOD5H zXz?)X#wKAWNME@NSGYLBHf9OIoZNn+y%;Om1A{AOCN0{@4HNn+O4KTpZ)sr?2wkmKhF~ zyLdfy2}dZptXAd&%Pg5|M4d20rCv_u5Ai|b7K&-kPz%POnp z21+PS<%c+v8)hcB%=g=W#-cgLnz4xKhDnPxewth&ESgv|))_7idoCoE7bGg;#_6`Y zkO+cq2!TzL9V3}Lj;=HkPLzU{XCbi0>HIK&gQqi3Q<6mtrO{St z=3uFtHFFEY)d>VmrbF{ggs14Sx=7e@P>|K~oGc9TYU(|n%sj&LnMVk^0ZtbNd132O zu1ClCFn*cEz!DOOI}u#qT<#QO;W#7BS8(MfmdxS!hDwvc`*rC@TeXvyH=pHJbP|O_ zMOF#AA$qJ%in7El-!J`%!f_H#oDY(pkk(cKMR%o#ZmWyC;ZbsWW@pe8CF((K0Afy@ z!=+(N*Px)47zvNkSMFuWT%{sC!^^M}roGz1Twrz^%-6@ygTPw=2|Gp9jSzLB%mii_ zDh{#~SZ2xaJc}%wOP~Z@RyR2bmCE*d(NN(I za8n}!@S~0+@nm{6LpcPjhIQhWgXt<(Vmh5!8{yX0ICgCCHDGP~@&ngzxNPWvMyxLt zSh`>)p)r_tFl^|_I@DBU>k@LG9_iOpqrI*%;WHgoe1Pu16Vl69V*q4B9?eyBW=d$T zcaG-NXjHq(_Ww!+j^AKinGBYOmX_Vm*8qo#LkZ5jnZ_|MB+bQFa{HnddJex3|=LwW9VNg+gHmNGv2F zu39KcvKCvmBrol@yQkgLr)Szd(`Ux+b3AQ#PupXk>1p?JY`3?uEo&^<+HFY`MUf&% z0wgwKN1;%tec#@`W@g0vk?*~FfB=y8Iqt+CsCq9mGBPS7;=AAd?!8zlM1eFK#W{XJ zIhscSP3#~nqMQ)#=Z3mKQmkPKhmeR-(kT>*L-GmM^b$fCOz4X!Jch+S`o%CqVuU8q z#5?)|d7WlJL@85dUQYrVsZAF294Qh^sGF?oB?@HFC^95z6m87tTl|LiIwkVNh|?vy zIHOK8CiZeoU*Z*BMr+Noo+nI*sAynSuW(F0!~5ljXc8@aU%kX7bq*5)iNTN@!VwNa z37TnTuh>T;an9*;TvnGkBoAZOafZ5*>-?^=_mv|oIrA5Y0=ctyWLBhBm;V@P4p zNhf)c=ahPloXF8k3;)RYC)`pu_>%u8EV05NaTrrr#E7FsAg8QJ@O0H64lK+*`Z=#J za!S3(kD5OjXxfzo8DyN12$T*c<$YDv0zUh}5AvVB_$8E5T)T6JzkK;+e(qx*11$oh z6oJq_p7yxRHP%>XSPas~01-meQrpz67x&8Hg0AbDfeSvlQjy>K{GX#L*FJUR2tzx1 zKug+08_O*6mU^2VqMiM6A7K%y%xz61*22)?XIWdw;g?s4=53VFL14HHbo<+2I=JR8 zOmr7HQU&4~Pl7Nc>uU*)j}BtnF`AlsNT!$Y+-em(aW}c5HT5zieUB?n)CNTcMn&Cw zSi9yv0Qx}^n)|6n-i`2m&^Zrd-D`~d{Va)xQTXqL%D?v*?rGwy-@o-kZ9c4H_dAAr zTlm_`*T@yhG&Y3ZGyD7?Ny@#SJ?y)C&%57!A2v=8G8&YxvHdLnenjB;1|Rf#h`TYg7zIoa3Rk+g!sSqAHxWO?ORaD7y~sGtZZn6od$5&- zMDkqXDRM@Es}1AKhnMLucJXZTWKhO_cbb>qoZ$cdlT#c&(8ms$<(SdND{_)+(Fx*y zBh#U2&c_u}3-;vp&|Yrgc6bIMKx&&6dyDVb30mD4j&>N!4bo95uH*@yx8v7HnFYeW z!`}QL^Uf-_BJ&6>XmR6M7*OE`>KDpK)1zpiF?FCfEK84qC@~veVK%giFMLYECF+Mc znLNZoXq6k0+gyt(ZinYswic1vz!N?;8ppRmLtnXwH+`)nD za3IG(;tR#a*aW8<&k*+-7%UF5WUtfaHd8Q)xWXf4W*Co5lduxpj?6M#?4h^Rjz)2% z;Wlr^FA%gQu$6(WCFkN7DatZ&uaTaV#uti`DAoP~Wm&GVRhF_@wpUqjW=R`KcIQV) z8%e&|`XZhv<4G5xB??7@7pAw^MIyRE%x~hO8y{e#(97@iY?3xoccI$R!XDPG4YsTx zt)!_<8ogM}P?Or=YV;~fC`?Q&<=|+Whzhfvfq5JERr`A{qi<=OkO~v^BS;J`H(W+l z(?nMG3`vmbrcjY$RF@i!MhT758YQ452d45V3;%9bu8uXnXaBv+u&|Us5~5%hm}@d< z%PK;yreI2m%$-|O96=_E?QWTbA$*}#KsgB5gEb7r7TJm zbdDmq+V~Yj7Kvn+IKpmmfNs%^fn<_v2m}YkQJ#~}^9}V?R`oJXqK#F(NS_$wQ^wD- zu2)Ht#>U~h{x^BVIKhX+^R&qw`1qXGZ}Nu!G6=z}njk6~7-X1k*~gMzWLi%!qi^wy z{3tdK*YrEMl=1N?QeZ(ZQqVcRq5ceyGNbYl2E`~#dIn3_M2PZX{!INod&EJGbApTN0_~z57mo}2JkQJLImR(g>(kuOH`vQwOkv{VF|Nnyp@$~X zTziK#nzD8|tIrV^4V)Awu?W%tnwabt2l=jgkuhV85P=8Lz+_I(Fe(o-B6f33oFFT5 z_(JiFdhJ#n$M@`I@9UB z_YafV+_x4|O<`^jL2*$YbqA8(Cm>G()#Fd6gW}`Pjdi2UNeWf0bruVYwtkF~Q zIFud6)Z3he23!<3on{irsA6snY__>nW8MwU2Z37rqC#kWX9+&@4yv4YgUrk z&=LtFMZqZYz2>)Q@FNTqI@z7;r@z!ef3cIanWrdA%sXq$gqHa1>PHzWbkXd^Dv&4W z$uqkI|I3?KdG+ihKhdR0IB5>#MmdxnA)-QTnkkeB(lX72R#;8g_K3S2x^?F0o`=0fZyQ=wTp-441+ZDfrcQ{fpDii@#JtlL|-!bJ(620unu zxtDggos^Mc)7oUj+sSjAj}!78CL#-5YPdbVr-lp493i)74g?e?4T)4}*^ zgTdl3j&|rMcd}uv;wXm(KTg8jq%2FdO=ZEEWx<)PbV;!6tk7HRVbmMpdUTwS8=@#n z43xSUD)bUoA#z5ZxzH?&p;hL>%d9#p)kmrU?1Z^V(a2YTv%nXMSDQ}Nf(_AuKD5QiUwPUIg&lf*~Yhs z7JY0#V7>ZN<;t40UA0s~4Y^k{!jvniC@pUr2;ZAp?k=oR`B{l=CcPSQP%?w{EL^St zX$6L)rolI<1eU68#jg)F@Ly#K1p#RnF8dYMGLQhJB0(y-maN89frp@KBm!WFq1OBaChHZpA1D(M24*t}1^z&Z6AMY31~!SHwV<=Q#l z_UCC99r!33L^FAvqDu}UG1$Kra*mYAYn58_LYbyo4T&xT&-{qiGhvBh5FAT6}LQ;VY!n-9H*Uj{>1-p91$njD^K9#^9}Vi5;{q%XyGyO7-!Wvu5g(#_Ast* zQliAL7_Q6-fku%d$2onLPSL?`c4G>o^1cOs9~&D}n=I&gZt8JH#V#D-a9d5Vp;zb> zoh-9VyJ+XKdWTzjoHo%`dx=AY_-plb6h3j$h^&x1pz(#zw7!jlLr6qe&{KTa_%SRT z&Z##T5QALM=O|NRLocFHe9HKF_KH!I@VMho@>%0^>}Hgrbh+wZ;-bF55e{QwBb7$W zT2jX#MPl7t(g23$|NN)_lvgiYAd}CtnM(1wU-=cj@>{>bP)`>Ly~Qm(i7z~!7azhB zfv0%8o9E; ziLP4PwE@^(Uvy;wkcL6i?w!cZD?x@G6bOmq1p8?^wS=z@cr2UE=-8V+ak4p*U-Hg2~VVSu<17txIM?E4@ z{PJGSFI`I$AD$%c#yOB1s%gmE+{W731H~@N(nX^w$ug%Judre-Vrz@;atG&PSJ<1| zMQ_3%PGpD z#LmJnQ;}(IbBC}Bq`ln1Kq=6^pGfb;6&^Pub6jp1CrOq~E5oCiF^Wc+ zjG1S&FvPQoM~M3oq?Tx*$;dqCV-pM%JLxZV5LFH>eiPRtGwd$(^Jr#_1!tYNV%OQS z60DocEZeI*pLh;Sn_Lgw##TYbf);Cx?{vOHlj~q9lPZF0^qWZ=Sv=t~Q0!;ZN>QlD zKvX3eS^eK<%}(%Q%V}D@R(7~Mu(ZjY@FE9tJDCX2@>e^)Nm1rmwpWR$D5i4og-gNS zL=#kI1Q5E;tD&L6kMX1HA0nc{OokVDvE>!I${jqKJ;ck+Z?I&~(c-p~F;bLe0RTru ziF=J4&h29~Kfs2SW~kW5q3ljxZ@k2-O>ePkkFhT|jG+yRqQFbdKL9i(StM{F2hcX| zH__n7Nmq*NSUSYs{2s1Fu8}n}I4Vq|*G%5X@qor%T%{C>v6ldPya7-gSQB+zlYRlp25 z+NRlSWy4wl1zshM+*Gq*D5;5-wgI*wAa4|Tr{OX#E{z3UVb?^Zsw36?yVky{u+Opr ztWzO_vJ`Y?yt~+I!8VZIetYVMM)O0@zuFKKPU$Q8fuSIR`&BEURx6!qr~@5$x1lTN znHWS0KB8h6C;^Q{T`|6b-UzL0^IHK?5v7+=R#;})USqCFv3o5T<7x#~r6H-4cZCYt zgg78ha8aKkr&G0!UJ1n->-3A=Eb1w)>vI&yusxa@n-Isu)2y+I(u&($;bX>6;Nb8L z|4S_BTX-lI^(;lwjEKGXy39Nitm%2y^gIosnFujbIt5r{$n$&tf2C2hlF;jz+Qh@< zE%geQ_1R$Tg&?Oh?3Vj!CrG>4Avzdf027-*In1J3WL+=vU%cPo5&1aByyTlID`V}iPjWEk+bR?Ha4STlstJ> z^a{Ez$C8haEo^?<`wd<&K834YN|ab(6%U^)>N4Z{CQYK95_ztxD|FI}S<$5rhyi}W z_#AKer`RQS(Ifhil{aUd4fgAU0jZ`vzNh}0Z>zsTV$dqOh>K?aH{;(>q{uhaS4pT% znnerEq6rh5J~2q2=mQ}L>j;mCM|oYp!BusI1M&bCkzlS?-a##bKocbd1{$d(K9b?y z9{$5G{tN!uZ~hjZ?=!Wy$j5&1f92o(($AAQvcP~I;tBZ#wy>#XWvQ~ocg>$VD_DRQ z5-mJ5ZXl(~rTO(g{5*g7^{>~iIXpVbul^6e0Cl@%wIaCy3`};45qd>8FRRzMuHNBs z<8fTAXrq@@3b z{`dP`|85u?K%~>@A3~LXo50cPjUL!xz*ma@>DT{;|M9aYIeBF8p$~Syh}(ML`^o?L zPx{`e(V{XAc_i`?dXETqX6#m5F8k<*)w<|KNj{87y^iCp?EM zJeHkJWMEnqsW_Aal9$2@NoR8fiYh+n+ z*4dZaiALZFm%(B;yYu~6%BbW=-`}zV%lx46G{eOKO46gN)Jois0fN3#2ZO~fR3#A8(gBcFLUAQ}huKh&!9nBm zcKiZ4Ba5v<_`)M=q=T#`%E72GXw`1)&+lV2Kg5CDPUh@YGDd+zxe+4D;e2eI1~1HT zv5O0_2__;lMEnr#ZVOTi1d`+Fy|lVTnN2ZwyO=4)t?!p+ug}wl~*BDoW+0Zgmp&5EhJ*--*jO9n@F1IrsTHspr24zv8 zuQW(W`kYK3p~;Q2=_~g!k{`y_CbP~8Q{gH4 zOMPf0Tjmx^&H~#T3k(#8*^}SJTd@la6$cnC4DoXFDK@M%1Yjz&qOsRBdMzB!90fEt zqZ4eIo8)D#mR548G|Z;CNz{)a5G+-JcZI1jw1J69gV#V$xtAN^8-b+fo;hx4gKAY@ zQ(GwE2ico+Al<3<2eHjI3$d*$Z%|RXN_|X+CUK>QFThsd8X(j*V^;&LGN{Cgu4As) z_8RDWeyKuEzb>U>6$CtJ<*H}G>l?4%_^OhdYNHyk{NTB&hJxarYuE}Juz$HZrtQ`4Lh-^G^-!w&#HaF@Hjer$vY*mVz=?GX$B%lnNV z<$yTCH~p_LrLV9ab{Vpda46HqtiFSZWL+=g2^UY7I3!QfL<^VHTjWVoAj_Z_BT0gc z-a_M5yaWbi0vS~Y{T!7~@w9xFMj9!JGGFw+Kv;xmr;AZB%2)lrWLWIvoPGmW7kS2b z0TT;Z(V9yH1_mBJB}$xCrwHM&q8G`qNra}#y){x;@7S)5Z7I zci1ft5z!8h8&A?e12@!l)>&mzP2l5`Bh4!8qb(TBoHY0KrTU$6HD3DgxWin(~Vu{^i3>ym}j7nV7_WPjGOzUZ~B-kbP5EctWy3|V zzX|P86lJn1#~I#cLS5&_&Ck)RyAcRVxeQA;PqQ|66O>;8(ShVKh{f3^7J|pFfcna_ z3k1S4>Hq0Za&}^oANj}=mF@)h55Tuav7955UdH!JfI=&ckQJO(GYCZO$|}}SLeSWF zf@ow9NCT}^1=zngxy!q0-rtKR{z3P+-%&m9?>!wP1QqJ~Ivbgq-{gtI0}s9K-CPpG zy{*hHCm-k)c#vf0etoY#*SiJY*8hG#@PBWc_j`ti#SOT^IQ*2sE4rNAY^BQ?4cAKy4coBht!l%h= zroY(3Gl@rOP$4X3(No%it%6q}XB1emx5yaCY$7QID(sf*rXC&w#{ zbIvHR)3XZ!l= zDYU^vWClZGnpHc=H(FmOZDg^u z$<62#Ar&HFB{`7YMT;9}Dl|vN$S{^4B57thlpWz}^bQ+VkY9W}JWohl3}!p&Dz~v_ zCAbq_qP^TqvlpYY+=4>!?Upyl7+DVH$2gffK*lU^J~mF?C~`J-jwK_=scLSFEN$ZpkCc&Q)|nyZH4#x^w#+Sj;o=E_LU1`Y!L&08K$F)(PpOwSw}q3bVIPR?IjXslJUrG;$ECKw~Z?eSIEjhX5$HuThTj|L>Z|K%F<;r zbcZ{kDKxMzcaR-!JGaA=^p*xG3%62WWP56OQqx`Pq$|`F7!~1ZQbq!)A);agg{}gS zU}y`8V9r?~>c{CVb#t-dB4IxYK-$Pqk|pYEg4PqxzVK1_?FyIXY=&l-6q!?%#=Ah5FncpWe3krLYg zG~_p+wW3VE#wOnnVAlY>*k-4z0QxS_Ts>cJFFk_F^?LjId)pIrpGi$n`5kH%RwdiT zoD30kU^6O7IR=ACciVejZ(yjP2#u1J^`WB2F*TG$FfN9wv?~Kpu&Fos08&A%zNYxg zAQ)0BEPtEfs}?(p(L|P1O%fvn#KjH_ZBiy!|MH{)5(rWBzMm)Llg#KTzTy9WOzRsAh`sESk8nnPk4}0S5Tl&c z-^Zmyl1*;t%Oq8b9BDktWK@P$*~$s|1TCV4!}1tHYno}JAhJ9rP7)RonrQl=RE1^A z#EJ7~{(mJx1HEDw&&n4#r%n?V5gwCI@@@aC%;^cTI?FNRNqQM1PAil84g!l?`Z^_@ z=Z1fs7>z_|AV-=l5}23>ESB^F1EL?Z+{!BBI5NcR{>xat%^FLb)vuG+SsWamF+Re) zn&P~^%yZ%+bcjyQ>2vHC`#B~~)FgckB2KsLp~{(N$+DuCS@Bm15u$|4hs4t~$cDhc z){BG)aa^8YPR%f*XXz5%XrV}x;uZBO8Jz(%1yNvAud_)9kI69{;m}S8Euw`AqNGF# zfk9s9*e%BRj`|M6Vwe*g#}N()7?6W3vdYuqIb2=huk_bY%I6vJ6wRWQsEF{Wag2BT zcQ~uha8w^7gpCvi8V>`B(%bM=>%jE#@%iUJ@e_Dqy2qYW?h|}dx3V{FMpP=nBTO8p5#o?ZFXQ=HgsjSNw%5O^1i4C* zKv30I$WQ?1wSO0Azn^~n-s7R9^?gX*_wxCJ+Nz$b^?!$Skkzw>dzzV?+k8(}%)=gj zxVMFiHy3~C{CAVeycffudY^|`L-zyi?`7a0HhvO8f2oIMXO)j{K1W}%ov0tKrSpUO zc~f7gpz?{(gq6b%x0$!%*GQTvTFWt(oONzQr)l$AX>wycn>bF?4`FJ9CNGACdH1*h z4Ss~3g)n(pq#(-kh+>D8FuMxk^^3^@1D?R4zb(TUKtlJ3=W_HnEYRAx$ zXfBK|AZ_NbRG?oUD0HwGTIJQ|H+Zx03=&C`*TR;W!WSi;Og_f7=xri?g#JLNy1F?X7d$_ zhKmxK8<9x_2GebGtlBGNjVyDac{K@!_rKXz(K>%?z(LpQ0f1beG$3 zltaPjq08+ApdgEEn(K6xd)ctoIG%X~OPSc(VW`l}x|L)sKY)ScdUTe!*Fa~vjf|1U z)CPyMBfQ-5Iw>Pjdk>qv9c)?&I^Av(<_25lGFc<^%}IoHLI`5z-;%Lvv^pd-MC*og3nO>>4%} zp73b)nwWRyi1;C{HQb`T+{&yoL#`s@Y4vt+By)tY@36DbOTtVu6<#7^6iAv$hKl{% z2~W}PcJlnz)69mJc&+hu%CbbMBIDVffz`b%U~WpX$cnLu24Y?#yNV-Ri(IAIYvGd{ zFYr#obuPrt6HzgeR+^-_c{i9Qkc}8r0O&pQ!BEsyV%(lFo$df@)-t{>1%O){3>SyV z8Cm9>DU{yM0#lzWz1l=I$hMZjMcyr4fo<@8Z!m5>OI~ZQ?z`$Q^{)GBHAk=VtU^?o z@!;Okk|ONt+BNlSLtqdnG$tAkp&>+|b9XD2PCZ*${m}%{YNN);SNmv+2s`C|3MxaF z=;x|>ivk%Wf;g=_B|l1-Fk2*8)AL+WXFw~2fHFB&m?E!}ly!-)3nh1fO-j7q_!Lbv z5hIQ%Z2qP3ukrD?!43AZk4ZgFmIMYCgL0H9bpsQNeex*ldWAO8M~)=TbkHoC853ii z*Qd$o1V`ml_;`HD|9|NaeU!CFnk}+qaB%p%`XhQ7VodHKOsH=B1O;8-E%h22pQG|g zZtHO_sIv$R4vI%v(9OcEI>Q0^G?UySMk5Es zVZN`v%WkoUAu-IT+>J&vuE&|q-65|t+~gW9vV%o6MU1Av_|ZO%vXwfX2f|qe$otQaXVkU~1o1Z*WszqC*T}VKE?rHA|y#@ko=RNi<<$F(P)+ zB3gM(zeYv}y8OrG6EqN`Mi5EOjGh4jBVq)rBBu!y&x!@TK#OSQdHG?YB1*p=U{X)= zj=o5T?xb6G^NRi+CwZD)(Swp6y{eaz(45zAk=6s;fCzRDvU#L}RJ zSxez6gg`4zkUBB&)1^gbS}%n+B!F9ra61#1{ZFQ*U~*!58E>NsTV%Xul>R=&>W3z zFLJeLm1^3^>Kxa&cnCzT289%oPI{1Nd;*Vw)EYY!p?B|NNMVv#m_VpfP%z3cK*_s3 z0(I+?BEU)QNA{DDCXG!NT4;P7WUkcr=vo{*4slH?y@2+;yVjp-|F;*n;MpvznUK>N ztQlG&>K31e$)N9t`R)hC?|1IQu;{&Q-g`~;T#-tp^!+s550aHUEW^ruzpHEnZ(o|` zkH2-DKmWfz`@r_<+qk!#>gV5neVpI+0s*T7=HnYD2~~?3)i%a^@1gO8k1rIy(42{1Wyx7*$zH?O zHp})3Tjn}VUMv6m{6C`A4T3m=6pq35n##+d>jOD7CFwF1T4p>lM}rFCD4UUd4;^kZ zO2F;#JWI|xli@kQAnrD>JKxWqd_N`zhL)tvJTJAp&6=H{r`S$elvxNZ@&3eP?8^<~ zsH(kq_hL|cI-$84p5;bl8V8%6QahuC9&)nCnZ~PRjUa!k(2|+(BD2m6gT-NX7JIo6 zyGqK~;zZ_A+RJUUm)p47aGP_n^B7t(mfy|1vqV@$7|rjb(Tnm#dOuM=gg{n251O*{ zSa3G@%8pkUDfF>3-^-owB8`5OL)j7LLaQu>*6Av>F`Dm1YRQ_N~GDeP`Vkg%cuCryXb0~L^ zCsQYQD}IeFGr^)WjfY}y{s3$CDy!BKyYsslE)FskUgRSi?_+On5K~BQM;7>I>#MZ6 zJ7{&|oQ++^7an_ZW2`u9d}{q!dW$=_)G!$ogi$8nX+1@Y+epgDa3^w`9d0|(N*Q;B zro-*V);9gc9#-vj=AAhnPamVzZANOrva`-R(W?kFd-8ioSV@+gMUJH&W!>83dh{w< zXe@2xD4Tt`{alS+!xJw1^84v31y%1{;StpiHzSjn%0b~%ltnCU;!2n9QU{l#S4o>m zOl_fs!o#c0pWe~{Vb#Es$s>$MXK;nfkFR}zj8WiEJHN(?vy4lL;o>Me$}L=pT_rCA zqr#|2)09xy%0VD#P*HZ2JDGE)*;N>2B66F&%vJ&?C68nuC2i!ew8cdDCax^g>UI!O z5$2tlN>MR^5`Jytx(hJtZ6@2&4rNiUK$8lKjbLBlAk+3Vd6_1pBa9SAnTSkK76qtm zp7n*3DpP-Zud6cVg1f-765A;xb=`T@YwOv&yTE$=L)85rFxzz-vevtaciWBpOpWMsEG3C z{_kVs(83NnWjA-!1O;*w$k0X))A}mDcIgm9#6=Ug^`(j)THxc+FLu!++PJRHA}~2D zALp<*&S7zcn22#fUF5WSjTA|edW{@O_RC}RieVw!roA?V`!7za*WgJG>?i$$cY@6)FnP4KY=A| zK=YRW7T;Gd(ja0ugy<6k09@0Txvj4O@R)p>A+ZY&mka7`7S%M58Slr?2G{gE>=6eD zi3mjs+*I#yKpe*u)qO@27mYkF9>d1L5Ju&_+J2!uJkII!xVW4YCozP9)Djn$uc$Bc z3Hiy2K@q%AzV^AKFR`qbNs%HZVmv3FBPJRGE3{%1Xgrh<+}4xK>KW2Hg{#YiM3^{@ zJk68X(gG}~NTwiohcmjsOX@|05}Y)ipq(}nxgx*!)i3kAU;YY-bmrdK{dYP{NAhof z?dSOCCw``4sCx(zc&O?%?$x8pSPQ6l1}a4%WnGHyDiJ_cWv=myS*9<)Nn-vcT6<{Z z_8L-?`moKuS0;1s^G`E{$ZB_zwfxe-~rrer?>Z9}hAb0K&?q*^OgVQzlifscddRl(Z%* zi+Iw*6+Snj(=0ga6l9Jo(QD+5Jgr_EPbN=rB6WZZ@tbU!335h>nCBDHR(;ErNw+=8 zin7d>mF8^xCXR1aiWl_|R#o;PIQHfT*^?XOfIY&k$Q)OrliZ0caw|GRo7-F|hIA8! z%SSezz!!?^(LlcQR{T0^c9O%HUD(>h(iRQAgA$OF1w0w(emAWoH=@(5*y|K!o~h6s z-e?rK(r3lq!mSiY8Ox7yGWiH2_8{}26|zQ-=Qp21Xu%G*iMh}!w#z%?w4OL|&G-5Wm6g$Rsg8PIsw;BiT_bW$@jWGfYON=q>d!SnT9>WR9pGX3<$? zDm=%ky-AzfM$*V|Gcr?s+gNbcX>uFLm|5ap69=>V$r~jOWOos(#0y9yF)za2&@khX zX)eUhqqOEo<`5(KLFPhBoQ2RT_{e|8TyqIW3E0%Q4I+=xtYt>G$L<|bclD-u!; z%l0}geiJDpL(0msZm;4Bmj*wMMzcG=i?E6!wZYBkRlOvS=@nHk0IJ21|vw<4go)z>yrt9%4E?OTr9NKsI@;w76}|h39y! z`5aqjf~art#g1>0GBT{&YaGfR=34X`li_h@!WMR|>Z+!%G=vb6O>3QxZhQz^S-caw z!KStK0|A-<03ZNKL_t)JD?K)?4ScqXHz_o|rA`)|jY=?Zuw`sot850-jdr(-Mz4j5 z$o1Mr=+}YUs#e?4A&#bx@lNa#1(~U!ZjDU?w<6<|bg5D?)MLxs#1mx#&2j*OE2q~g zkKX0-u5G^CSi8C&)a`e5hW)lNQMYv54e0Ba&kFFb2H_cj#S!#JLRdF&n8&%I-zHDG zZoS(EpG5(cbD3WKBMY`!{NiVQRJi-*?9G53)qn#W% z92^G4AZ`G|BDE~7GWm1K}sK3F-Rr{Y?KBQ6A6ro-HeDK#?>wIpa$}j!w&+<<`{z?A)JKy08-~BE(XJ+`@9)^_oDqz#SlH$(g zRXCw**{ifxRCVArz*}{{RgcQT;8)pMwqF2ISzA=aD=9QcCrVR$7s-_=R9PV_FN{`I zsf@xGV!HyekO(v=%~mSS(W856>(4#51lu>3ICdNz^GRoxQC=~4ihC9~Ed)Y|y9@>@ zDPLGQaZgzQligqQIMtS2&c} zMTgs5TcD@H%Os5~zEmta>*UNlmTz)6J4)0G@x9i!*)TU5E9~US&nc-^mI^E?SHq8w(W|~8pLpa)`Sjl_c;x_Vh@+cwgFzqapHS#2_EIG44Te*=N zktyPS1M7Bzp+Yw&Qu}Ci8*#M7mX+a3bc+4CK|0FKRAmk;_69GvoTJf;Gg$2ARMQ2< zBe&2fn!RS$t<@SOIanNFu-MH|v6po#!LiI9VqT;=q9_5k!gE|{xXrVPqg-v6tYoo~ zOox^$(kJjyEII2;hZZrl!K$^2MAGavGaH)56)qj+b~?)KB+WDyVfu=l6lIaqjc2fx zO?SDMO>>KZQXjSoa>)nXl6gF&IYeHzKM^IiOpDUjKkS6e0;7q+~!Kd z6-uJW4z~keD3Zn|O87X+p|jk_k~7C=);@);91H~g#ZJD_c8Y>5a3?&05{hQGjloh- zIW}RY*f4|q@BeuDGbGI{v(7SW&KB1rHz-TRK&g-E&>foHR_2`jCfg)tIVf{aX3mZcwZ2OHX;uQW);NU>>d0G0ga zB9N{474U3ofvqFBB9B4`X&<%V5%Cx+dWChSD^A}^B0VigMxnV-k=Cf%{)*V{UOlH( z1K9Pfdlk^vFIQCwO#KR0Pqf$nUIX}S-@DESUa4Lx2YFHvXuwBchyV->aR7nAtiBSoA)$#j_J}7qt6ro;8Hpf710H2eVdCi=z7EpT z*+e-kpJYzWFf2#7t*;X&j)!7aT|)?ieewy`Szt*|)xc?xokZeQf=Oj1h6p4FJ7WE=;={NDU&oOy|F1k6b-{71&O{?r6t5X~mj}sxnb$yi#DSE^JAz|~p z_z2haHU7~1e|XM#fgh2drbvm?>i;M2&0^%p);qu7cOoM9tgL-u?HjAO6_+Nv+1>2r z_Hy?{BUv++1P$OBV~lwi`^CaQ@{2HlVHhL85B30_84F`WGmvI2Bs8MCcJRrjwQujDMs?L(4i7I6>Y?0RMjH=^=iR{&z5=B<^61_4&i11#; z%a|=PCc`W0D})L2sd<;LsBhuafqT$Cd@80IDAzo;w)OaCFkICa8BinS$?=i@V**Fk z@E6i*{Q94VUl&b`(!?9qHyM_Gf^db8T85aqW40J7RB*M+U2~IL`V#HZ!#CC6!y&}H z%<}IOKjlsTHO^MYg5XW{@&>^VH6kkT$S^RwxXdS?-(>RMJgW=a) zUV?*fch?zQ`yT6aw<)HV@U_OmV7nH*=|)1n6|A~R5NXYbLIirg{wg1>Wcdfb`6jzq zDF4r|J($n# z_O$%|KaqnpKqzF>-I`!}F}(-epT(>nyzlOQb1Ft-GRn+S<}u*@t32$}p7Tl1_^f=N zcCRm6jd(^$2x*(77vn*Ek##Ra-Y#N{CTEqYDwmM4IiEX5gBQaUpM~%mOYsew+!SwT zo~NtYf)>NQ*dhRj%e_Rj!?(9y=3ab}yRii(ibKTwFrN5ioE%fJWwLew7nh-OH;2pJ zM06On+c+LQz!#s*7?cQ|wPv>MJa^-B7%{xI{TzLjb~ZxW+=xHqR^l#3;AV0j*v74)Ru+4z~plpZ;cH%SQ$W!FdLm?ymXi*w~-UY z5k^ZrEJil@QOl=vSKFA0EMXg)@zQt@@su9U=8w?nwop|y{_UXx_oCa}i%l{eox&F% zS8CYC#ub;l@w+^XO;MF9uGH|v1%XB{h2St$8YJuFi0Cj4UXr!&3U}kvlvIWB(jaL& z!)$bxwpttA)m}n6Oo%XVWnLs?9Bw71`6zXfoSoN5 zR)NX*Eqw7wd5yGqZ7hcuiTe$-yB*9%9-_sk$!#R!N12N)QdT8~%0ndmB)1Z`sj6Ty zJX{?CgpOJV(~)@&m;2GcjpQ|QA)k~^F%_A_*1}47jfi$=&<$klbP(aRP8kaj_rv5Y zi<%wCa5{B`l3n)Q$0z28@k#6fcgqAOD!x!}gTcQFMX2kvTXk8rJA95dfiq<=zy`ow zVaSFpk~N=*2UUWxN|{G1c*x_Rd~a|*FhUdJQ2gTBU_mbbf7~?UiFQV&{!rkZds4*(1$(k)h zgmF2|Eq#gQ&^k*_9!lFxnjK;!sZgqqD?S(X&q>fs)s$(MLp0IMs$QYfbn*@P4$HDk z-sBjO!<9h`Uk{9?yzsEcJZ>dluM2ILcCe0-G%pDRmlVOd5$#Y)4NSErY2QAuF z+A89h+r4K-Y*^j-SZ5D5~1>^WYSb%FoD zzoS|8GQtpHEfhiwA2g&T=!3aY}% z!Y~m(=xig#;Y4A8d$9$sCmzyOYi2I8N>#aZ)S5Y37@*OM?=9QH6;%ziwvEOYA5T0o&L(-QK&RWnSF$g2 zJ#m+iB|KM{;An9OS81-t9&$Z#2g^7Vtt$6pcj>Km(_8JO(QP2&hd5Rq#23x|*gT8j zResU*S)dr&^xnv2~0X+TBiOBa2*WxQ1l{Q=_TaG!uoBw3V9qB=tF<8LadW(iVf|ZaQl%TuokQ zCA7?ksgD^f4>4XkOiV{4Av(Cw7;LDsne=2Qkp;cF|sIrO|7kztYL&hMWAfxlNuKr zE)p^k&gGt?s%qSd-)1eeM#0KqFcejRl-JB~WtjEQI$O>Lx;{}kQaWC*rFsu+xvIvF zohGg$oXel4Diu~kfi|=gBFh#?LBP*DZ0Brfln zl7vl&Bc)?ZMW-l9VXw8^h~LE2Z*GarQ8~{;bCaUU?lqyw*yLla%aIUqX=aB_OdXVq z0d2{i1j{ZI@u(rf9$2-|fqbMuPhPXwpcvfNfo=JdsImit?&C|uUmSb)?|&H&anNi= z1Uyp*+!9PoJWB|9iUwLJm<%=Jv8$_IqQqtW9wGr4zhd6Um%#i7Ep?~0AaA7uc}#Qw zP#RP_Ra2m9g2|4h!BtfRL&y(iE=g&_rA%2D5HYw^NVCdk=9k1JSe4v1>wATdNdpn0 zq-70l+&Z(G#1;qNxI{@{W090*LS~za&f}OWH4*Y=krobtGGxp;jnYc9bW-PlIisFq zTW@enU*VhfcbGSGv`ITJtCtB&n77o|cxa}mQQ;&1Cp<8RIjde~i3Q4(IVGpLWp41Y zdXojHtu(%K$qj%ZI$rFZ{p6Cd^6o3T5LmZ60z_f5L*9=BS(` z!v>==MxXQsw&3;JX@I=R^QrzABOcoLxW*+WF?RL6UsNyhxB9PH)bk9gQI4tOG?L;~ z^(ya}cL)<@PzHIRA5f#l?^^$uaT&)E=dtmpTY9))!dy4kh!dxkRwm6PaY=AWPGRrG z3JLDpNrYF`Yk0U=*i=l8|HAr>Jtxzra>eH0QqZI0>$1T;eT6gXJa4GCiAj9#blH@- zWo~jpon%5Lh!NW(@x>U*R)v%1B(uztGg+RO*Kvq)U+$14hakiy%5SUxItV}}!j@j= zp_!&%hGt%*H%yD&M|9W`PK{&f_CXlnVMB$66 zuK@ULk>Mj>@s-A}@|A!7O@8ga|2?w#0_D8RkN>B;{M8@*BN}31H0s%=%a6S_^LU5y zr1uRV5==r=l~r?<_1Wv_YB4Y<6pa>zGIa-C46&{bV%?osw`TU_arfu?2y2;u>M%mU zau}RYgoacn#nKj)at<)4x}23ku&fx-NPhs>W%m)j+}!0?u{=451)l}dpLUP^p9k~( ze_sxAUync7-N>DV^iyXa2lISd;fH!#m|jdjG0oFn%#+H#AH+Wmj33;8Rz-dmXn&U9 zKvlW)Ryvp{jPT=@U(!))W}-M$zj08R9!8hA8J{L>9BQIDpF7FXQou%N3`MKLbYz(Z zKhA16&3b)=_3IHL6~cNb&Ci=Y;bid$-IX>HI!ZdULprqc#OFD!8VPqNm!!nzK&9F3?pyL`8Yz?IKr` zHxPs!YloVYS&Xc3EiptyN0^VyQnGS%Rr_f0QuJ5)0m#@ns^YUjmg(p+F+alj{4vhu zkC3y9TuI(#q|{4SttIdu5CP5*L}*=MC7hI)uy%NJ`&It7 zulWbGm|;U!ih8zj91VuY%y;EGFwpI|Ax#A0|J z%Qy^F27`d##$l*3N`Ix7ZF>hJh6_8-u^8T@v(`w9o1|csQ1yw7WfW1JAfn@>oecAl z6;2dR@_ge=(N~1)y!%}1wjUjIrFlZXR78b*6-0PfWsL~Ud5s9YR3xYBoDIVpu?DO1- z&#>uaXz^OP5xt&1C|e6d0p>s*mJ~sN~3;sN*@n1$T?PK$I z$8#mYih>8!!mt*17~PI=jB{L*kEyVOWdiqG5vVDHZ2|_5{qV$+xjYt zW{NTej;J&A%P{ZzKcglEvSy7#YLFJ`Vp(R15TQhgBXW|67bWYZ5lQm8dYb|zM&&RC zljleJuXw}yI!!b&W2TumGyH}APdwB&a0v09ewT~-eZFRWmvytoA-d^P{ml3?teX`w zY~YwE1^q7Hw7$z38K6;844NUX>Z_cP2D;@CpXpC1QDRia>68wZSs`sQG}A(>bQ2{> zkrE%8pYgH&1+j1k&(~gOfPSXUG>*h5Q6fo0ot-ok$n$gk4tMn({=W6w%rnoFdB}uJ zVCwe$i!9MDUA&@R=TrSDm-Izmkyl7bidNIgf>|I(j-n~@s(OuPno$yn0qlbR-Ky3m zZJamf`MG|V1PPj?i3ynql+X@TMT5{NL14!qU=@g|2kWWl_~hy zQ`Q-jv9LsFkzi~wMsO{cPyLHLH1|2qF;1&fd`rH|8k=0wpI}Ro9tP;4izaDg$QmYY zV)ZGEJo(woBVQ3Q3WcVI%K{6;NRXpIV$9;d`KRCHU;fdbqqX6)$w~gjzx_Y>pa0o^ zi)G24Bl+I^=CRrSqg(Bfeg|j#@+fH#952|@D#D?WGgt`A_dWydVvNSt2C()dZUHje z1zMUL0zhByupR@}^}bK)_wX@w(;OibN}`m)^L#w7gw|f2S+C<6f1wC7CvQAW#wPHobQqeTa4_IL$hxZKUAv(5e3JpI)}?AUqUZ@dOjH-~VD=nyV0 zvyo+76by7RnGZAts6^87lSjT;V8=vG_Q3jSm_>q22A|r_GB*b(G<9 zA2A(f$KE04N2y7L;c_ocUIPWINI^=}#3QV2K1^NV)t$2p(8+pujgD%HsEM)ZWLOJ3 z%tq!oSvbaOsFf`z!|!Z-ho~Rs$1R^STJGa|d=lGO>{vP8OZ^htIDDSCPE{4Ds4_)W zAf&^*lz)LHx0yq=HcG0(RCJcRu?OgsOIejDsS*_lOg$nd%6RDrJ9dtm)X1wM5e+46 zxDmffO;zhqD%d7tcc@v0t%PAU{2y_Asjmja_gM2&#p7JaKF43Qy~}E76%DkwoeY(G zS&Xa@F(K~8XQ?Weth0@UO}pE{ww2OtxxFn^KuodTO z>Ne-?H#jNhxS~I1g*gV)QS!1w$>jE)y+Il|qRw(dUu4q+L7R$XP~s4!fx|Mvw3(zp zX73thAj>A7_ixf6HvuvNu`2YsHC*j+S>DB`S`T}s z_wu&tfI+JObU1|YOqrBwql zvT7DckR(QeIqtJ)W;iczGo~i!mjTvU<$eE0bV?8B)pi-F+)Oq40>l0d!d6^*|Zo+bHhGdv7dP$OG z!OU|^j@RpMiA(wt^Jbp&>Lo^H6vsGBn;F*4Iy)xIteGV)30{_$h)bLftAiE2#ASU6 zM?##GQ~W^xB^AoNt==Xok7ASThkEe+`_sy{Y03ZNK zL_t)jnkt`|Px;tBK47)f`*Av!>BRfpT$aDC z^n>4@=KQ)pt^G8c?q`{UJn05swxlA)FxcJ1!djM^>k|pvkKOM}^VoOK!JcOF#VXsm z5-p97mav|7{ga;QNzc4LpM%#uDczH4f1|Jn(wr@faXUW8P^ps!KLG?1 zjZ?8D8oeZMZa-J=bc0G=w5r@lOz}eQ7-OYA%2pN6xZI1)laEyR&CPER(II*(9VB!V zOpxF0x=s2(rHe*4j(yM`a9_vfqnVDb5b;Bt%N-+dR5FZ~2S__PDyjws5`Khlr7w`R zbG+013CBu@`Fi?g5;}q}n)e#7Q?N>$%O9bm)88bPnsS(i;daK<`MduhVjc`9U zLseCY>L~M(Wjb=LOcX~E6xZYTSqZIBv?_esaFevN&5pIjRzsSXa?ev!KEJd6J#to& zvZ^vx8swekPxw!5KgKeOEhocjXoKa@9H$DW*$i#s;?m}}^G5bOEpCdRG=IcQbehwJ zGrW~~k(8g{dg37)PL^}|2|^~!rH0QasvO&P1Xl%8eS_rzLOMduT4CGXB;hA`5Syc{ zihIt(dp5uVuDF1rxspJcfcxMd!&{Mb<)Vl&k_h)qcvV%uIBF zg0;g!c$(%&D}rLj+Qu>tQ61s8xBmg@&<*+_3_=Nu>=(oujNoZt;eClU7=}qAzjZTqA4JdkoB)nd5o& z7Tb1|)9N{fWrS8~=g<5<#*rwms`LEZ{{b!)s+3tZa~Q1xhJD}GEyJ|XSvMhrIF0zY zR4HOiAU9c+DifSzgDuweY#STfe$Rf-+^*T4qWjxY* zCM$u{SHQm6Jkm5*^>a!DZ4A3sIgeq0`q;R4=t-cxo^F2^xqD32>x7s`>|MwNrfXHI zICU|1e_vX3z@k5Fg7H!ZC#qvy<0EQR$VeLP8*CLKMhc%Ar{w~ln~x|FOrVP7aj9X9 z!2-e(<3;r?mdzZO_4{nd0?I(%&r-4~c)G$>`IwN2vTPPODn~i0PS8OY%VwFY`Z5aP zCG`pyc%2zD#Wj5i8=E*yV$XY80PMgrxVWjO>01yi6*kuf0?BV5FvsfM2Rva!-NR4#wvg8|L^q5Aci2I zQIzPu`QZN9Cf0SfofEfYT;}0RTBHmS@v&c zUwt+NSj6m+VvkQa4VusNWlqT{nxq-qSe%yUcwfKAdG#Wf^+g(Ki4!BnEAlECm1fPXlQrA?x&Cu3EMBo*qlH!yq_B_Wfz+pIkS;nC4lm`)(Pm zKlp<`_{04t_uu?Uvx6t0|EJybu>u$fE5#4ryT^sILo}yiUr76_+W7!{|D9XBer}ky z=EP&^pT@R7%cSPZ&K-QVgYV{Pc^tg&litxrroek2-r;*aLFW-RA@0QH=&7|4(P5Uu zTTI7R5QK@sFkQ7)THOX(+!P@b;$D1?s1DQYrN}sWZYO4$x8~_6H*mB%!f>UB4!4=G zc2E*HVc5o|*>7Muyve=T98nV{w%MvuB&8tqFIQnb1`*`20zY1WR=CpI#sJi z$T%D=405F0&rstt|cGTB_cg?cB$@M z8`!uB5(K8IrSQe@Ft$v=DzOqulXZ$Tcv0q}tF*bTKoHJ61DJ$#7KFNgif##+U8=zRX#~wW;K*%HM~kxM=+x3thLfr>m+O<^i?{^I5~Q& zour){8{stbkrjOL$=Nw(Bhy$2oo*MK&K6ZwtpoO#xUlmaMXN$sJ4{CxNcs(&E*$4# z>L#|au#JVQTrM}G%fd(&$uQY958(p<--rRm3t1er* z6q(1zr)-sREsqg3L8H}*Yk7=?CTI?Kb0>6<%dtxolVBW&Hmi#{HAP-!QI5lF*4MdU zeT9V8#B8d-WO#-Z{~krj(oR3ydX0n^;*6YU#!PX`Tw>MCqtV2rfd~nFJY1?&Oo>j_ zN00Q7W|NQgdsL;u5j8=Q1Z%8wLY<>bjZWz!M~0l)e2jf|sZgOnj~b*xjS2NU8bg+i zJw_dY3dMRPPd5PS1jpND01K-g%@ee^w77<^e96yXI=xSTpo3PXnc}v-g=1{GR42P(h?EgR5~WF6xNq(vqDT^mEQo@H#A#FQv{~(R zN*CR#mn-@jD`pjsDml_5Xk^|@vtrhmH23+;T;?Kx7GI;lQBA^D~S3^U{Vya z9bP$oia&kl9jdNNxmxA3$-Dge8*dN^h3dTdzBP@&hRP zvYH@jay?E)@ZTUPEM?=^Aw0i~HjrqTAQWl@tbLP!-TL@NEcrCEnS;ll<>UCPo_|&u z0Ic8t{onsX4s1^|{Wy5jCl&sSg$Z7uNbmz*zO_VKTLXvt+rDt!lO_jG0>UB3;)m}~ za%8B3;og=nl;vs9^Q#9v#6@xa97;BN!i#-gXz zN^iBD`_V-Tc7>!L!_QcMwi%j3gqn~tD!9hD~I@M=6PPo9cQfAM~B-; zqn9A%#fj+%Wvj~76Sj?QRoer9OHp?c7Ps;fW6d8=)O?b!W=Mi&28FR35h!=84xt_RB(Jt}r^jq{*d$=2)VmUm|T4&22n}YqdNvndMk&xt8a77T;V=fcuro#!r^c9pW;Y_DizXZg)ucj(Gye(iVnB`n9^0}_gb-o`zaCLlVbRJVqAJBI zGl&kf&UHQ0Fd2-Q+by%~ zOY>+Pt^YpMooWN7-LgXTFiM&=wD>3;lqKRT;(nC4Y9wRU5zz=C6hg)1SvCvQq)Lq< zn8y_%cAwpdpfIFmgH=6)FD{}LwnP|IM|obo%#@kr+v+#?E$bfwHmB4XI;EQsF$QFi zb=Gk4m{89#BI9&Q54X$>Hd*6@nxI{}2}y#S*+Ed$C{m`(xH?H(k}R1Sd^`@TlXOZq z8)l6GHH;$;@9UqkWafFrdJ{ph#0<7X0E?6~ku%%;M1P-cHc&V;(?L|?+%s3%W`kqu zc@E17`elGtX`>=ley0D5H5N#-%0qLT9vML+B&C67Y2v=V&&T=$R#;#{on~C>%&tWF z5B|UAxSVF*EYK=#7z{1a5`b+{q)GFx{slR9I4q+~GtICJ2jFsFz3}k3qCY1?n%}a1 zhm-0QbIfvGU!zM7(V|)cOd)|vi-$+WRCwQfz)$s$S!0zJX(dk{50BT>R~S^o)Wl_2 zhUt}F8dVbs5?BPml0Gw6=vDo6OK$)g4_4B8#S|pCr?~id)Tr^1{)nIHci3WsGF1xX z$eA5{@%fJW9;eh9o>wn0rUJ8>MrkA|3HCeI2JK1q)pyxj)xroi77fzC2xA;mN9j~u z^(%LyxCS6JMuZ-Exh}WZCQYYwqQu%Oe@H7DOp0dJ%wZYD#^$=Y%uoCuv8l5B$p2eh zT*hRSQ*wf=?C?GN*J+aGV7er`@1g!F(2`0q)ZNVwKl~754C|Q;tD9SV>%z7j zKYyHw6NfQ+w}06;o!G}H>^Gs2eNH}>?O=xz_}UPO453uAj*rYU8Skf&v`@o}U-tc3 z`Ti>ReIjswk}b}YW)#n|N&n&&3_{Zj846{O7bg0?)GX#{Y`#*$$5-dElyL4u?_+76 z^a7stLcVNyb^Tua{NPJ9|y z85;d0aUI1n7Hw`LaX-fC!WbtDqeOKCg<$P#G=?DB=wy5z$JnI2I5o>K?IcZwvv6b8ja z;RG#SE9dg3$=Z2*(Zu~Y4`L6wotR=Fvdqo+9p7*WS5QlLVfyj5a7UgT}(TWqpPnsv^q z*GWh-SvJrXd=|OFVq}#OYl0){3^l3I%K$@inA_ZB(JZlURw%GTr|Kt0f)uS}$r4v7 zYE;m8G)osRTVJP9wQ^Jck}cLLlcQ=XL?lLBQe?~q3Lz>D#7VGi)^H@s^Xhe6YRsB@ zxJKiNWtnIb#5xNNaOmQ4~g5t0ZS%rbAL*kSW2Ef0)nO61708Q6XD zh!qbyiQT>;aOM;PVhQciPstQ1l4VFvVMI|ON0KI@s);T&z^u7V!la0jq@;>LCto+O ziIZSkGPqRsKr=itn?ZIR*<|njPwYhG@vmPH4NR;&w)8T-czYq# zeIOxCR85%;Sqe;y1cu}o329-+Y}9R9*<)5UAj+XZTJR|28^}e$4!00?1#PMb&?HTC ztHaEhDf(oHv|giA^>9?3phSTkb{JM8q)BtbT%$yhHq}Kl&3w)JCJi)j#av>ORR&~) zR@x~^8AqbLtlq$vz+6m=a9w{ynLJ3qZcmw8G)gOpdfj_jeT6gX9IvU@>5+b>%oG~U z3+h#xq?!AAk`g&8Reqs=&a3Km zq69Yd2U}pF{@WHQal_o;rn$kCnPSFFvqP2zGslbS0&TSOf;!Kb8fQ=ja8xLWrdt1Z zu--pv5PLK2ee(nZq7bnHkSe?O)GCNJyN7CpT{Bq35JJ&FGYiaNFf`Htb!~VBlvIhV z*~SlB9tV89@ZL*kakNFs_bqh^~Din)Pv8_D?xryuY7ZsmSF!f#TKV z+#D^b6we(w@~HhDcs}+Y-@h;W2!Q>QrY~SE!7>Ici)gYLuTr9v+oDwSxU#UZm*ua0 z?=}A5fA~j%GK?|%`lm<0d>62bgg{vYhJg-<9sHRnw$+7Ybz<3#NL?#_e+Tzv81Bmy zXTI#3{rk^Snt9rJcnahGqyjvRX+OCC)PmR7snR=TZroqz>lY3`mCgQDW+eA!H(5*P zdGm#lU+KCh*`DuD_hrEUS6PL4)>6sSK>d2Uz`Gya;>hSCbCETcLz`?lIcB5FY&lz$ ztSX*p&J;!&u5>X_?!d;TV!6DMJw;!&oy!fAJcuq*v0NfLOxW0a71)=6xhq>$9>f+| zifpnPPBRl-;YQ*C4`U0g*I9dCY06fWW5pp(7e;CH6D)_fSPyS8Q5d1W(t)*qd2?UK zsKszUzQ|;JmY5&qeteGU=n|vlK3>^5iN@fGj{uv_4)@~oT#WvO# z-;)R!gJUe(YmMaX5=-GNhRcUIT<)c>)=p=wg`R2~%i#^y!kc*F(cmWuX&ZyjrPOV1 z#~)I(%5=Id+=$;NY(iw5ES~thn44g@(#24vi=-c?s$7aznTx3#{G#zAj+e$cRUBcU z(uHFz9Ahz=nC4-0iH{qvvKroECA`VDz0HfcQ+zx93JD$KZhV@>@Ena^6JzB-0q90z ziuW2XG8FoG z8+m*CWg7fASCY3FtoBg|SLt)cutHHL!*@ye5n95%^gCnxn)P3DL4AdFGI+Kj7pbxw zStlHCq~98@2cp>>7!-LjWb8DKihw9~$kHZVyls7xc{9VTnWRLSHaSF=EmG3JQ8huB z7%MChCWgW1p1DJlG}9toys5s%FU|Wbm?^$t{W|C5WoGIsAjj47WY{K7oFO&F%jyjl z%sdn7dH$jGPx!j}4j%BbdXuE2m@<>xF;@r^rb${Tn<6V_nJIIVF6pIP^)V*LST$>` zGq>l68ZdGh44Rr0$(uBmgy@t3nxzwssR!P9eA{nRyKfxa`wJN2N7a8fP1bBtWE*2V zb|lMEEJw)mIU;A#SS;v!)Wqc_^(K94gn1s~;i3s_cenLAHK_yx>b`uf`uBhpbWpoa zqU^F{PXXxr%Kq5CJY&BOupdQe1Pj4e!O0G5m@o-yylNnOY2>4&8|vQC9gcvXr+H=eo@{;*H&C=Wj7(IV5d1i0ZX&Aa>w?NV1;fuNPqyY=UR$ImKoAs zcA0KIXI^2q*^5Y!B^Jq(qs46Fm^?{X;Lp;zvZU*|e9vz^VnPZ#m;(>#lqeNcpa@wq zT<1o~`ivioWkzSYuYFAFB$GP9ZF`HGdX4+q$9CDl0dt5h>7tVkTr4$G%V*4I=#X~& z_)6(+28C-#bnALW2yLc~8}3RFTMAw)vnGg?3! zF;a(6i&BF~4N!{`#qm`j#^;^VmHlt5f%uOH=O1SYaVr*l94>rR8e8Mu815s${c*~h zj{~$1OT5%RrLbPZ7IQ^@^uYjMdZ8PiV?OfUCpnVvL`)K#yEMpGKYRFN!#?i*Q5CxN z)M0A_JSvZmYJf+X{+U@w@`Lw!=-k^+Uu1-H)fcHN*3zD9rlC;B7Z#pjSEh}a8zH1Y zX8p@Nh>Wu*(@smS79&DOwvpOmoYBxMy^#^-{mWDrD@dCxAH?s_8yRIJG(*bCl6G=r zoE&jC#H!1q@(Uh-aPo{%inGTlx zs|-aZd86hXrUH{}&u$}cTo(K*?8tOdTdZNxze2niV^5}&vCtehV*N}77Rfj{q8g$x zU&rm}1FRTk0*efWCm9Y;^Fh@WMnl8+G)&Iqd2ab6hmyOP^CwvFuQ2Cd=1y#goD`^Z z&rw%w;OxrNyu9!%pI&+rzlP|I3{mCA*_qwTia)_baE5|o7!Ho}LA;0Q zzyubTCI2$}^e`j4A|Sa6sVU@+RYA@Sz=rVxUJWy63?O56yx0GI#twCFO5tw&Ip6_>rEUSs-=NT z_FX0!D9OjpvQLh%SGsw|e2P^P<3T#C39sgMR5rJb0>$x|R%lE>b7 zl^49N>wZ}*6D@l}^=v!`iJ}w{aZpvXBzZp6yGp7xTbK$B^vlN~fj6V0ZX37zC+=M|Q9 zg&(_r%>-i{Fvl?BaNHc{usOntu8<~0zuu?Abh1zObBPNK>0S1k!zFgpAtrHt!TCB- zsp7W1!bN=tBOz8v@S%P{kpkdQO$`x=a^9X}LFYN{JjE{QW{DM2lA=~>i4wyvAsVHH zYN;bcn6QLNk*0z;A&GLwUM0y2gE~aD)U(VoC(LPvbcm2dIAV_SlKCu0%u(vyYHT)z z)gt}cOSd^nTGL$79)4tBBP1aXm_ryG3KS_&#D@C--@m?A@(6e-fA>6ZOG?VRP1IYOf}5+UL#$_iYx7kJ)z zfe7Jp7u~V}ErIb8c=!6ZtOMba?6z(QzfmFE>*Kn98m@-&agiy>r>T(*yu0K1A`l3+? z5b$&A-~oPc{#_F3G=*Z3_io(e)fYcaIJCujPbrQyg;ZI8R*tk>C$iRl-p%=I*ZJ3f z@CSr~ej01*2nEBa1EFXI!HR0aLCdAdS?15)mlZ}6~`Vve26`JyODM)4{>ycH(Yq&Hk!FNf|Rq+R1V+ zGD519r7k+h! zxFK3{4fIspWHLBTXSR*bY!@|!IJLz}8uK+6H6(m##)2~}`BoSXPkN4DaM_>QOMhgD z8UG9m{skKIbu{H$=*n~e&>J0OXS$PyLN$GnVfLiAF&G|aJUB+u$P@_Ve6+hUrkY51|nk&g@)Oa-p!J;LUl3DoyuO0XOHn@;t(C#M#44#2=B*kFdUj< zN4}kzSdyvmJP-6P?XsO)_5+IKsWWZ(@RQdpuQ*?24}1AD`^Pj$11+?1(_W;XJ}Rl^ zgn5?w(h>MA?cuPTq)Mt8(g!4Uj+7R#Sei^LX7bD&R#>LTzDq!Y1PQXLo)^aF%qv{gON?qS zK_UbsNQekYU15beES4BG)Jh|5Y-f=z5JH5xr`H(M0UD&8xYSTompoaL ztk5i-jA$R9GM{BqCvdUskeyTz<30N}cl9cHGGsJCtu#?7^-Smhc}?Rdz_ZR5$&zJ3 z7l=zGcWn;|o&VT5A@B2spyLWgbj>|_h%m>^i}dRqc5{GX`WUsnYsc1JAM#VJjU;7( zkVGhGo(xHhuA76j;wfx3nJyY_3%BT@s9D5#W5+3(o#~KWOzIc~O@CC-r!-C2G8o5e z$~76@^0Re4H7q4yz7hCuSQEDFX>bK9G{zCb!uo} zo@t&puW;O)Vp^x@q?1n5$$R#F{P-ExL3YU=3Ka0;@O}4t#KbdE_lwT2uuVD?1}cODxhvGne#T1fg4w(kyL+2@{k6 z@7TAnn&zze90$!I&e?N>2{F$sUo>B4S(mw{*Ek>t_@4c9iWK-+^9x+n9@?aX7%_?z z8PtK2H(0ODx}=LXI(g0hCEex#7wr2qN(&K*(7`sUr3yi4qn#?LLPRKPk?VScRb8P@ z>ba}87`68~C{OULbCxL4wLZdH|MC7MSS6`xW|-kE`y(odQ>4Iv_K}DL_)P9;cHt?% z)=}qe0;I&8m;0t}>~ofj_5y<8gq$dOiq*Bg3>z@bCi=L%Vy&BQ-u%nP2+Gf7%WhJj49UxAHDveIPf15 z{&7|r|6AjI9NS;M{(mtC=>eXS(nT~tWz^63%re!L;fDhLk1G^FbA1IMj7=}Iv!m{@ z??0?e4=eM=d~AL1Vc{P3{=@3&Q9HJB7>hC#n&w1eFLi}V0J>9MEcz0Rg=X24Zl@qc zdLv`xq)5CN<5qM46=lJfU^=jXiW0DXo?m`~hI|z{Q{Y1U4y(Qt)ozUXY!!ai2i(SE z`m#eI&m-pm)x`=9C3kYM@-|m0`#dv>g$m~UD@+F#SoNhD3(XR;0Zx=`vAlVd16&17Jnw3FqDm3`C}D+yRXuDINc4$@QEM@P1vj$9J~>#Xe`RM&@PPy;-> ze5B;rbMrOpU*=}4zr@&ks7KVW63T#a*`8_TNOC6yL!qV=t7S4U&-torEcur>oZQXTs{2$1 z8fZ?plPeS`ipyYlkVC0%_Q(;wRPi-xD{9w3Q&uzNb&x~S#SYodPxvWCb@35o!j2KA zfjl|BY<_`*a)1l=0ypdpUeoW9(s}Nq7;(svqLMlce%`Y`<)A!)BMu$1oh(^y*sJtu z4+Twg#=OEIIZB!gf1}^!uz7+m*+Y>$^)!=|1Qk-pX>*o>7Pzg~c)`5HUA@f(`xAQFs0*6>i~$7 z$7PjO{;T_^7#t3olibuB9ONLw+D`**6mc2lE-Nh3&OXwTW`+R@THu!UuuHlbv3Chd zm>Ov!#WGflTf6Z3JRmR^gIEEg>5`%p9_Y=PEVt<4CHV?TR+!ewwZ*+`(|w2RWxqMe zMSYWI?Ig((gL-RCZod3k6lGo~S!Nz9S&w_No`WM7!3d1lzBLOorM)_absOxCc<)$K z8l|Q0<<0ZPDN=Ons3J+0Jf z108JRupA~#m~kDWnHCzQk^8ofaUCTtRlICIhoTJW5S0>VM2C3ao@YjO(P6eR%QVX@ zQJ}!AF5n}=AvwxbeUBl%$5Bpl*c|48eZT{~&nfd1$K(X(^*oitsiU42(@Mro5++Ks zbdhA0AGrU8cG=0W-e#v9Vy_$kWB5h$bwVYk)fJa99j8Joc|xA#roF+4j!-Stbh3?? z%*%Y!{T=?1{37EzhEE(0agaClr%dZK544|Ca*DjX34|*6@o`j+@Sc5_B^GF=m3f)N z70ZZ@uv>QH6JP1{$x@)epbnAHRi>C?Ugv3-c7DbARh;sfjRHCFiQkibFZV-a&FV>6 z5nDI(yaiogK^Hk{j^G!6sqb#wo$BT_zTCM>tozz2^u{%Qjepv@=9huy`k3(;-_7G- zeRY;KXF?J2P6x{2OZ*mA*T<`1G3t#mi+D`Fcy)+)CSTWiI$hgDqqbxYv7}6k<)x=i z@z4LepX1;E>30Bl?VWcx(Y>F4_H$pwAlNOLUfXEJqNroodMf7(BEalE>l4Kezv#Q&_A-k!ClRd%-G{D z5uqXyq`D%+%zSbUxIZlYM~T5atjv%5ZELuP<>}+}03O#cClY(8D^!%&F;6zVU|cdL z$5?QdYZZOmiwx6|Z6f7l*2GrYat)kaKD=gr%2DGTw7JU53#XV0EO0L}LNb=&P--W! zV#sqDt?PADlp_7%35G(G)D@~YnA}dl`gn2aBo88!T&%oHQF8cg2uBSyg-X6W_Y48+ z#~=)Zr>HE(h`M2JM~9gR&LV1P&o!_s(@H@7YZHV|9S)?sxE~&4AUsLR$+17(iQoD* zu_01I@e%7C%~_O!6tSY@rNC@pf$`uJgYhAbCyr2GsOHSd5uRTu2kIRPV_FKicukHRwzmy1!8UmM^cA5nB2|5 z)OLC!qf7-B=*l*;C(}{#LaHqLl3a^DAnPx%=v&5DaM+#QMt}f$$@7(k=lP3MdB-V59!Xrk;7?;{b*lhh)uCQdkBOBXTTwm-sE%Y({cp7MQ$8JVM6Y7vAa zN#3*PxokgVm+a;$7r1OMkS9ZcFvrXpX4La$m?FhA(>!TTvEVL(vFzd%rkG?w=RENU zl2l4H1@0C=IInL3mKNDYleADp4TsDj{-XF@c1t&t zI?5f}gT?S^^I81(2;e71E!$)lBl>_PriqGoSlvhm3p!7nDhd=hD#vIqS%!#9HGTr@ zl0AINJkNiz-^0OhM{khT99QkT{EYJ|Y0^Ano*_=0K|4f>6kl?_%9KvAQ}%FAZ{wis zkbP7TBSVgAiPKIytGbV29b}(5gbzP6I#DuBC|tMia9Mv$h)U{B3nO};qvkAmaug}h zL_4d@Q!Fv77xl*!DPVvIVMIMuZXW>(tb14$z(<%OIhs~10f}I3&fCC>=d=h_ijpL0 zrWvpguw*f%DJn!1RhJ+^di6Rd%ri{t7$f??^DDT>7{FX(s*vIaa$FAA;$vz0<^LUG)Z{q?O3+XmL%0IoXtG(cXmxT