19 #include "G4SDManager.hh"
20 #include "G4SystemOfUnits.hh"
21 #include "G4UIcmdWithAString.hh"
22 #include "G4UnitsTable.hh"
33 :
GGSUserAction(), _messenger(this,
"/GGS/userActions/hitsAction/",
"Commands for hits action"), _outFileBase(
""),
34 _outFile(nullptr), _outTree(nullptr), _outTreeName(
""), _privateMessenger(this) {
37 _messenger.DeclareProperty(
"fileBase", _outFileBase)
38 .SetGuidance(
"Sets the base name for ROOT output file")
39 .SetGuidance(
" Can be with or without extension (.root will be used automatically)")
40 .SetStates(G4State_PreInit, G4State_Idle);
41 _messenger.DeclareProperty(
"treeName", _outTreeName)
42 .SetGuidance(
"Set the name of the TTree object in the ROOT output file.")
43 .SetStates(G4State_PreInit, G4State_Idle);
46 .SetGuidance(
"Class name of output integrated hits")
47 .SetStates(G4State_PreInit, G4State_Idle);
49 .SetGuidance(
"Class name of output particle hits")
50 .SetStates(G4State_PreInit, G4State_Idle);
52 .SetGuidance(
"Class name of output position hits")
53 .SetStates(G4State_PreInit, G4State_Idle);
61 for (
auto &handleElem : _outputHandles) {
62 OutputHandle &handle = handleElem.second;
63 if (handle.partHitArray) {
64 handle.partHitArray->SetOwner(
false);
66 if (handle.posHitArray) {
67 handle.posHitArray->SetOwner(
false);
75 const std::string routineName(
"GGSHitsAction::BeginOfRunAction");
81 if (!_outFile || (_outFile && _outFile->IsZombie())) {
82 GGSCOUT(ERROR) <<
"Cannot open ROOT file " << _outFileBase <<
"for run" << run->GetRunID() <<
GGSENDL;
89 if (_outTreeName ==
"") {
91 _outTree->SetTitle(TString(_outTree->GetTitle()) +
"Hits ");
93 _outTree =
new TTree(_outTreeName,
"GGS hits");
96 G4LogicalVolumeStore *logicalVS = G4LogicalVolumeStore::GetInstance();
100 std::vector<G4VSensitiveDetector *> processedSDs;
101 for (
unsigned int i = 0; i < logicalVS->size(); i++) {
102 G4LogicalVolume *logicalV = logicalVS->at(i);
105 std::vector<G4VSensitiveDetector *> sDets(0);
106 G4VSensitiveDetector *sd = logicalV->GetSensitiveDetector();
116 for (
auto &sdPtr : sDets) {
117 if (std::find(processedSDs.begin(), processedSDs.end(), sdPtr) != processedSDs.end()) {
120 auto sdName = sdPtr->GetName();
121 auto ggssdNamePos = sdName.find(
".GGSIntHitSD", 0);
122 if (ggssdNamePos != std::string::npos) {
124 if (intHitSD && intHitSD->isActive()) {
125 std::string sdNameReduced = sdName;
126 sdNameReduced.erase(ggssdNamePos, 12);
128 std::unordered_map<std::string, OutputHandle>::iterator handleIter = _outputHandles.find(sdName);
129 if (handleIter == _outputHandles.end()) {
132 auto insertResult = _outputHandles.insert(std::pair<std::string, OutputHandle>{sdName, OutputHandle()});
133 if (insertResult.second) {
134 handleIter = insertResult.first;
139 auto &newHandle = handleIter->second;
140 newHandle.intHitArray = std::unique_ptr<TClonesArray>(
new TClonesArray(
141 newHandle.intHitClassName.c_str()));
143 _outTree->Branch((sdNameReduced +
"-GGSIntHits").data(),
"TClonesArray", newHandle.intHitArray.get(), 64000);
147 auto thresholdIter = std::find_if(
148 _thresholds.begin(), _thresholds.end(), [&sdNameReduced](
const ThresholdStruct &threshold) {
149 return (threshold.hitType == 0) && (sdNameReduced == threshold.detectorName);
151 if (thresholdIter != _thresholds.end()) {
152 newHandle.intHitThreshold = thresholdIter->value;
156 newHandle.partHitArray = std::unique_ptr<TObjArray>(
new TObjArray());
159 newHandle.partHitArray->SetOwner(
true);
160 _outTree->Branch((sdNameReduced +
"-GGSPartHits").data(),
"TObjArray", newHandle.partHitArray.get(), 64000);
161 newHandle.partHitCAService =
164 auto thresholdIter = std::find_if(
165 _thresholds.begin(), _thresholds.end(), [&sdNameReduced](
const ThresholdStruct &threshold) {
166 return (threshold.hitType == 1) && (sdNameReduced == threshold.detectorName);
168 if (thresholdIter != _thresholds.end()) {
169 newHandle.partHitThreshold = thresholdIter->value;
173 newHandle.posHitArray = std::unique_ptr<TObjArray>(
new TObjArray());
174 newHandle.posHitArray->SetOwner(
true);
175 _outTree->Branch((sdNameReduced +
"-GGSPosHits").data(),
"TObjArray", newHandle.posHitArray.get(), 64000);
176 newHandle.posHitCAService =
179 auto thresholdIter = std::find_if(
180 _thresholds.begin(), _thresholds.end(), [&sdNameReduced](
const ThresholdStruct &threshold) {
181 return (threshold.hitType == 2) && (sdNameReduced == threshold.detectorName);
183 if (thresholdIter != _thresholds.end()) {
184 newHandle.posHitThreshold = thresholdIter->value;
190 G4Threading::G4GetThreadId() <= 0) {
191 unsigned int nBins = intHitSD->
GetTimeBins().size();
192 TArrayF timeBins(nBins);
193 for (
unsigned int iBin = 0; iBin < nBins; iBin++)
195 _outFile->WriteObject(&timeBins, (sdNameReduced +
"-GGSIntHits-TimeBins").data());
198 std::vector<float> detThresholds{(float)(newHandle.intHitThreshold / GeV),
199 (float)(newHandle.partHitThreshold / GeV),
200 (float)(newHandle.posHitThreshold / GeV)};
201 _outFile->WriteObject(&detThresholds, (sdNameReduced +
"-GGSIntHits-Thresholds").data());
207 processedSDs.push_back(sdPtr);
223 if (_outTreeName !=
"") {
241 _GiveBackAllArrays();
242 for (
auto &handleElem : _outputHandles) {
243 if (handleElem.second.posHitCAService) {
244 handleElem.second.posHitCAService->DeleteAll();
246 if (handleElem.second.partHitCAService) {
247 handleElem.second.partHitCAService->DeleteAll();
257 _GiveBackAllArrays();
259 for (
auto &handleElem : _outputHandles) {
260 OutputHandle &handle = handleElem.second;
261 if (handle.partHitArray) {
267 handle.partHitArray->SetOwner(
false);
269 handle.partHitArray->Clear();
272 handle.partHitArray->SetOwner(
true);
275 if (handle.posHitArray) {
276 handle.posHitArray->SetOwner(
false);
277 handle.posHitArray->Clear();
278 handle.posHitArray->SetOwner(
true);
292 G4SDManager *SDman = G4SDManager::GetSDMpointer();
295 G4HCofThisEvent *evHitColl =
event->GetHCofThisEvent();
298 for (
auto &handleElem : _outputHandles) {
300 const std::string &detectorName = handleElem.first;
301 OutputHandle &handle = handleElem.second;
303 G4int hitsCollID = SDman->GetCollectionID(detectorName);
306 handle.intHitArray->Clear();
307 _Convert(*((
GGSIntHitsCollection *)(evHitColl->GetHC(hitsCollID))), *(handle.intHitArray), handle, detectorName);
310 for (
int iIntHit = 0; iIntHit < handle.intHitArray->GetEntries(); ++iIntHit) {
312 if (tIntHit->_partHits) {
313 handle.partHitArray->AddAtAndExpand(tIntHit->_partHits, handle.partHitArray->GetEntries());
314 if (tIntHit->_partHits->GetEntries() > 0 && ((
GGSTPartHitBase *)(tIntHit->_partHits->At(0)))->_posHits) {
315 static TClonesArray *partHits =
nullptr;
316 static Int_t nPartHits = 0;
317 partHits = tIntHit->_partHits;
318 nPartHits = partHits->GetEntries();
319 for (Int_t iPartHit = 0; iPartHit < nPartHits; iPartHit++) {
321 partHit->_posHitIndex = handle.posHitArray->GetEntries();
322 handle.posHitArray->AddAtAndExpand(partHit->_posHits, handle.posHitArray->GetEntries());
332 if (_outTreeName !=
"") {
340 void GGSHitsAction::_Convert(
const GGSPosHitsCollection &posHits, TClonesArray &tPosHits, OutputHandle &handle) {
342 if (tPosHits.GetEntries() != 0) {
343 throw std::runtime_error(
"GGSHitsAction::_Convert: dirty TClonesArray for position hits");
345 for (
size_t iHit = 0, currTPosHit = 0; iHit < posHits.GetSize(); ++iHit) {
346 auto &posHit = *((
GGSPosHit *)(posHits.GetHit(iHit)));
347 if (posHit.GetEnergyDeposit() > handle.posHitThreshold) {
349 tPosHit.
eDep = posHit.GetEnergyDeposit() / GeV;
350 tPosHit.UserConversion(posHit);
359 void GGSHitsAction::_Convert(
const GGSPartHitsCollection &partHits, TClonesArray &tPartHits, OutputHandle &handle) {
361 if (tPartHits.GetEntries() != 0) {
362 throw std::runtime_error(
"GGSHitsAction::_Convert: dirty TClonesArray for particle hits");
364 for (
size_t iHit = 0, currTPartHit = 0; iHit < partHits.GetSize(); ++iHit) {
365 auto &partHit = *((
GGSPartHit *)(partHits.GetHit(iHit)));
367 auto &tPartHit = *((
GGSTPartHitBase *)(tPartHits.ConstructedAt(currTPartHit,
"C")));
370 if (!(tPartHit._posHits)) {
371 tPartHit._posHits = handle.posHitCAService->ProvideArray(
372 posHitColl->GetSize());
376 throw std::logic_error(
377 "GGSHitsAction::_Convert: particle hit already has position hits, this should not happen.");
379 _Convert(*posHitColl, *(tPartHit._posHits), handle);
382 if (partHit.GetEnergyDeposit() > handle.partHitThreshold || (posHitColl && tPartHit._posHits->GetEntries() != 0)) {
383 tPartHit.eDep = partHit.GetEnergyDeposit() / GeV;
384 tPartHit.UserConversion(partHit);
388 if (tPartHit._posHits) {
389 handle.posHitCAService->TakeBackArray(tPartHit._posHits);
391 tPartHits.RemoveLast();
398 void GGSHitsAction::_Convert(
const GGSIntHitsCollection &intHits, TClonesArray &tIntHits, OutputHandle &handle,
399 const std::string &detectorName) {
401 if (tIntHits.GetEntries() != 0) {
402 throw std::runtime_error(
"GGSHitsAction::_Convert: dirty TClonesArray for particle hits");
404 for (
size_t iHit = 0, currTIntHit = 0; iHit < intHits.GetSize(); ++iHit) {
405 auto &intHit = *((
GGSIntHit *)(intHits.GetHit(iHit)));
407 auto &tIntHit = *((
GGSTIntHitBase *)(tIntHits.ConstructedAt(currTIntHit,
"C")));
409 if (!tIntHit._partHits) {
410 tIntHit._partHits = handle.partHitCAService->ProvideArray(
411 partHitColl->GetSize());
415 throw std::logic_error(
416 "GGSHitsAction::_Convert: integrated hit already has particle hits, this should not happen.");
418 _Convert(*partHitColl, *(tIntHit._partHits), handle);
422 auto totEDep = intHit.GetTotalEnergyDep();
423 if (totEDep > handle.intHitThreshold || (partHitColl && tIntHit._partHits->GetEntries() != 0)) {
424 tIntHit.eDep = totEDep / GeV;
425 if (tIntHit.eDepTimeBin.GetSize() != (int)(intHit.GetEnergyDep().size()))
426 tIntHit.eDepTimeBin.Set(intHit.GetEnergyDep().size());
427 for (
unsigned int i = 0; i < intHit.GetEnergyDep().size(); i++) {
428 tIntHit.eDepTimeBin[i] = intHit.GetEnergyDep()[i] / GeV;
430 tIntHit.UserConversion(intHit);
434 intHit.GetAbsTranslation(), intHit.GetID());
438 if (tIntHit._partHits) {
439 handle.partHitCAService->TakeBackArray(tIntHit._partHits);
443 tIntHits.RemoveLast();
451 const std::string routineName(
"[GGSHitsAction::SetOutputIntHitClass] ");
454 TClass *intHitClass = TClass::GetClass(className.c_str());
456 throw std::runtime_error(routineName +
"Dictionary for class " + className +
" not found");
460 detectorName.insert(detectorName.find_first_of(
'.'),
".GGSIntHitSD");
461 auto handleIter = _outputHandles.find(detectorName);
462 if (handleIter == _outputHandles.end()) {
463 handleIter = _outputHandles.insert(std::pair<std::string, OutputHandle>{detectorName, OutputHandle()}).first;
467 handleIter->second.intHitClassName = className;
473 const std::string routineName(
"[GGSHitsAction::SetOutputPartHitClass] ");
476 TClass *partHitClass = TClass::GetClass(className.c_str());
478 throw std::runtime_error(routineName +
"Dictionary for class " + className +
" not found");
482 detectorName.insert(detectorName.find_first_of(
'.'),
".GGSIntHitSD");
483 auto handleIter = _outputHandles.find(detectorName);
484 if (handleIter == _outputHandles.end()) {
485 handleIter = _outputHandles.insert(std::pair<std::string, OutputHandle>{detectorName, OutputHandle()}).first;
489 handleIter->second.partHitClassName = className;
495 const std::string routineName(
"[GGSHitsAction::SetOutputPosHitClass] ");
498 TClass *posHitClass = TClass::GetClass(className.c_str());
500 throw std::runtime_error(routineName +
"Dictionary for class " + className +
" not found");
504 detectorName.insert(detectorName.find_first_of(
'.'),
".GGSIntHitSD");
505 auto handleIter = _outputHandles.find(detectorName);
506 if (handleIter == _outputHandles.end()) {
507 handleIter = _outputHandles.insert(std::pair<std::string, OutputHandle>{detectorName, OutputHandle()}).first;
511 handleIter->second.posHitClassName = className;
517 const std::string &valueStr,
const std::string &unit) {
519 const std::string routineName(
"GGSHitsAction::SetHitThreshold");
522 if (hitType ==
"integrated") {
524 }
else if (hitType ==
"particle") {
526 }
else if (hitType ==
"position") {
529 throw std::runtime_error(routineName +
": Unknown hit type " + hitType +
" for detector " + detectorName);
534 value = std::stof(valueStr);
535 }
catch (std::invalid_argument &exc) {
536 GGSCOUT(ERROR) <<
"Invalid threshold for " << hitType <<
" hits of detector " << detectorName <<
GGSENDL;
538 }
catch (std::out_of_range &exc) {
539 GGSCOUT(ERROR) <<
"Invalid value for threshold of " << hitType <<
" hits of detector " << detectorName <<
GGSENDL;
543 auto &unitsTable = G4UnitDefinition::GetUnitsTable();
544 auto unitCatIter = std::find_if(unitsTable.begin(), unitsTable.end(),
545 [](
const G4UnitsCategory *unitCat) {
return (unitCat->GetName() ==
"Energy"); });
546 if (unitCatIter == unitsTable.end()) {
547 throw std::runtime_error(routineName +
": energy unit category not found");
550 auto &unitsList = (*unitCatIter)->GetUnitsList();
551 auto unitIter = std::find_if(unitsList.begin(), unitsList.end(), [&unit](
const G4UnitDefinition *unitDef) {
552 return unit == unitDef->GetSymbol().c_str();
554 if (unitIter == unitsList.end()) {
555 throw std::runtime_error(routineName +
": unit " + unit +
" not found");
558 auto thresholdIter = std::find_if(
559 _thresholds.begin(), _thresholds.end(), [&detectorName, hitTypeNo](
const ThresholdStruct &threshold) {
560 return (threshold.hitType == hitTypeNo) && (threshold.detectorName == detectorName);
562 if (thresholdIter == _thresholds.end()) {
563 _thresholds.push_back(ThresholdStruct{detectorName, hitTypeNo, -1.});
564 thresholdIter = _thresholds.end() - 1;
568 dynamic_cast<GGSIntHitSD *
>(G4SDManager::GetSDMpointer()->FindSensitiveDetector(detectorName +
".GGSIntHitSD"));
570 throw std::invalid_argument(routineName +
": detector " + detectorName +
" not found");
575 thresholdIter->value = value * (*unitIter)->GetValue();
576 sd->SetIntHitThreshold(thresholdIter->value);
579 thresholdIter->value = value * (*unitIter)->GetValue();
580 sd->SetPartHitThreshold(thresholdIter->value);
583 thresholdIter->value = value * (*unitIter)->GetValue();
584 sd->SetPosHitThreshold(thresholdIter->value);
591 GGSHitsAction::GGSHitsActionPrivateMessenger::GGSHitsActionPrivateMessenger(
GGSHitsAction *hitsAction)
592 : _hitsAction(hitsAction) {
593 _setThreshCmd = std::make_unique<G4UIcmdWithAString>(
"/GGS/userActions/hitsAction/setHitThreshold",
this);
594 _setThreshCmd->SetGuidance(
"Set energy deposit threshold for hit output");
595 _setThreshCmd->AvailableForStates(G4State_PreInit, G4State_Idle);
600 void GGSHitsAction::GGSHitsActionPrivateMessenger::SetNewValue(G4UIcommand *command, G4String newValue) {
601 const std::string routineName(
"GGSHitsAction::GGSHitsActionPrivateMessenger::SetNewValue");
602 if (command == _setThreshCmd.get()) {
604 if (tokens.size() != 4) {
605 throw std::runtime_error(routineName +
": setHitTreshold expects 4 parameters, but " +
606 std::to_string(tokens.size()) +
" were given.");
609 _hitsAction->SetHitThreshold(tokens[0], tokens[1], tokens[2], tokens[3]);
615 void GGSHitsAction::_GiveBackAllArrays() {
616 for (
auto &handleElem : _outputHandles) {
617 OutputHandle &handle = handleElem.second;
618 int nIntHits = handle.intHitArray->GetEntries();
619 for (
int iIntHit = 0; iIntHit < nIntHits; iIntHit++) {
621 if (intHit->_partHits) {
623 for (
int iPartHit = 0; iPartHit < nPartHits; iPartHit++) {
625 if (partHit->_posHits) {
626 handle.posHitCAService->TakeBackArray(partHit->_posHits);
629 handle.partHitCAService->TakeBackArray(intHit->_partHits);
G4THitsCollection< GGSPosHit > GGSPosHitsCollection
Alias for G4 template hits collection for GGSPosHit.
OutputMode GetOutputMode()
Gets the ouptut mode.
Sensitive detector class for integrated hits.
A service which manages a pool of reusable TClonesArray.
void EndOfRunAction(const G4Run *run)
Actions executed at end of run.
const std::vector< G4VSensitiveDetector * > & GetListOfSensitiveDetectors()
Retrieves a list of sensitive detectors collected into this object.
int StoreVolume(TFile *tFilePtr, const std::string &detector, const G4VPhysicalVolume *volume, const G4ThreeVector &position, int id)
Set persistence for the specified volume.
void SetHitThreshold(const std::string &detectorName, const std::string &hitType, const std::string &valueStr, const std::string &unit)
Sets a lower energy deposit threshold for hits.
TFile * GetFileForThisRun(const std::filesystem::path &baseName, const G4Run *run)
Opens a file for a given run and returns a pointer to it.
GGSTPartHitBase * GetPartHit(unsigned int iHit)
Get the specified particle hit.
GGSHitsAction()
Constructor.
~GGSHitsAction()
Destructor.
void CloseFileForThisRun(const std::filesystem::path &baseName, const G4Run *run)
Closes the ROOT output file.
void EndOfEventAction(const G4Event *event)
Actions executed at end of event.
Tokens Tokenize(const std::string &str, char delimiter= ' ')
Extracts words from a string.
bool GetPosHitsStorage()
Return current status of particle hits storage (i.e. persistence).
Definition of GGS Particle Hit.
Float_t eDep
Total deposited energy.
Definition of GGS Position Hit.
Base class for storing G4 particle hits.
A multiple sensitive detector.
void BeginOfEventAction(const G4Event *event)
Actions executed at beginning of event.
const std::vector< G4double > & GetTimeBins()
Time bins getter.
void BeginOfRunAction(const G4Run *run)
Actions executed at beginning of run.
bool GetPartHitsStorage()
Return current status of particle hits storage (i.e. persistence).
G4THitsCollection< GGSIntHit > GGSIntHitsCollection
Alias for G4 template hits collection for GGSIntHit.
void SetOutputPartHitClass(std::string detectorName, const std::string &className)
Sets the name of the particle hit class to be used for output.
TTree * GetDefaultTree(TFile *file)
Gets the default tree for this file.
An action which saves hits in sensitive volumes on ROOT file.
static GGSRootFileService & GetInstance()
Get reference to GGSRootFileService unique instance.
Mother class for user actions in GGS.
Definition of GGS Integrated Hit.
G4THitsCollection< GGSPartHit > GGSPartHitsCollection
Alias for G4 template hits collection for GGSPartHit.
void SetOutputPosHitClass(std::string detectorName, const std::string &className)
Sets the name of the position hit class to be used for output.
#define RegisterUA(uaClassName)
Macro for registration of user actions classes.
Base class for storing G4 position hits.
void SetOutputIntHitClass(std::string detectorName, const std::string &className)
Sets the name of the integrated hit class to be used for output of a given detector.
Int_t GetNPartHits()
Gets the number of particle hits.
Base class for storing G4 integrated hits.