diff --git a/include/RMGHardware.hh b/include/RMGHardware.hh index 61051744..81d5ebc7 100644 --- a/include/RMGHardware.hh +++ b/include/RMGHardware.hh @@ -141,6 +141,9 @@ class RMGHardware : public G4VUserDetectorConstruction { /** @brief Get the instance of the world volume, after @ref Construct had been called once. */ [[nodiscard]] const G4VPhysicalVolume* GetDefinedWorldVolume() const { return fWorld; } + /** @brief Get the instance of the world volume, after @ref Construct had been called once. */ + G4VPhysicalVolume* GetDefinedWorldVolume() { return fWorld; } + /** @brief Set the maximum step size. * * @details This is used as a @c G4UserLimit to limit step sizes to being no larger than the diff --git a/include/RMGUserInit.hh b/include/RMGUserInit.hh index 93a187a6..f6c44ee0 100644 --- a/include/RMGUserInit.hh +++ b/include/RMGUserInit.hh @@ -33,6 +33,7 @@ #include "RMGTrackOutputScheme.hh" #include "RMGVGenerator.hh" #include "RMGVOutputScheme.hh" +#include "RMGVolumeDistanceStacker.hh" /** * @brief User initialization class. @@ -155,6 +156,7 @@ class RMGUserInit { AddOptionalOutputScheme("ParticleFilter"); AddOptionalOutputScheme("Track"); AddOptionalOutputScheme("GeometryCheck"); + AddOptionalOutputScheme("VolumeStacker"); } /** diff --git a/include/RMGVolumeDistanceStacker.hh b/include/RMGVolumeDistanceStacker.hh new file mode 100644 index 00000000..b2775e83 --- /dev/null +++ b/include/RMGVolumeDistanceStacker.hh @@ -0,0 +1,56 @@ +// Copyright (C) 2025 Manuel Huber +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser 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 Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +#ifndef _RMG_VOLUME_DISTANCE_STACKER_HH_ +#define _RMG_VOLUME_DISTANCE_STACKER_HH_ + +#include +#include +#include + +#include "G4GenericMessenger.hh" + +#include "RMGVOutputScheme.hh" + +/** @brief Special scheme to stack electron/positron tracks created in a specific volume. */ +class RMGVolumeDistanceStacker : public RMGVOutputScheme { + + public: + + RMGVolumeDistanceStacker(); + + /** @brief Wraps @c G4UserStackingAction::StackingActionClassify + * @details This is used to classify all e-/e+ tracks as @c fWaiting if the conditions are met. + */ + std::optional StackingActionClassify(const G4Track*, int) override; + + /** @brief Set the minimum distance to any other volume for this track to be stacked. */ + void SetVolumeSafety(double safety) { fVolumeSafety = safety; } + + /** @brief Set the volume name in which to stack e-/e+ tracks. */ + void SetVolumeName(std::string volume) { fVolumeName = volume; } + + private: + + std::unique_ptr fMessenger; + void DefineCommands(); + + double fVolumeSafety = -1; + std::string fVolumeName; +}; + +#endif + +// vim: tabstop=2 shiftwidth=2 expandtab diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 911a7df9..faf6350f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,6 +49,7 @@ set(PROJECT_PUBLIC_HEADERS ${_root}/include/RMGVGenerator.hh ${_root}/include/RMGVOutputScheme.hh ${_root}/include/RMGVVertexGenerator.hh + ${_root}/include/RMGVolumeDistanceStacker.hh ${_root}/include/RMGVertexOutputScheme.hh ${_root}/include/RMGWorkerInitialization.hh) @@ -91,7 +92,8 @@ set(PROJECT_SOURCES ${_root}/src/RMGUserAction.cc ${_root}/src/RMGVertexConfinement.cc ${_root}/src/RMGVertexFromFile.cc - ${_root}/src/RMGVertexOutputScheme.cc) + ${_root}/src/RMGVertexOutputScheme.cc + ${_root}/src/RMGVolumeDistanceStacker.cc) # Write RMGConfig.hh # no need to install, it is included in the header list above diff --git a/src/RMGOutputTools.cc b/src/RMGOutputTools.cc index c5a7bb50..d57abb76 100644 --- a/src/RMGOutputTools.cc +++ b/src/RMGOutputTools.cc @@ -21,6 +21,7 @@ #include "G4Electron.hh" #include "G4Gamma.hh" #include "G4LogicalVolume.hh" +#include "G4MultiUnion.hh" #include "G4TransportationManager.hh" #include "G4VSolid.hh" @@ -423,8 +424,15 @@ double RMGOutputTools::distance_to_surface(const G4VPhysicalVolume* pv, const G4 sample_tf.Invert(); const auto sample_point = sample_tf.TransformPoint(position); const auto sample_solid = sample_physical->GetLogicalVolume()->GetSolid(); + + // MultiUnions have a flag to calculate accurate safeties, instead of just using the outer bounding box. + auto mu = dynamic_cast(sample_solid); + if (mu != nullptr) mu->SetAccurateSafety(true); + const double sample_dist = sample_solid->DistanceToIn(sample_point); if (sample_dist < dist) { dist = sample_dist; } + + if (mu != nullptr) mu->SetAccurateSafety(false); } return dist; } diff --git a/src/RMGVolumeDistanceStacker.cc b/src/RMGVolumeDistanceStacker.cc new file mode 100644 index 00000000..7115e850 --- /dev/null +++ b/src/RMGVolumeDistanceStacker.cc @@ -0,0 +1,81 @@ +// Copyright (C) 2025 Manuel Huber +// +// This program is free software: you can redistribute it and/or modify it under +// the terms of the GNU Lesser 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 Lesser General Public License for more +// details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see . + +#include "RMGVolumeDistanceStacker.hh" + +#include "G4Electron.hh" +#include "G4Positron.hh" + +#include "RMGHardware.hh" +#include "RMGManager.hh" +#include "RMGOutputTools.hh" + +namespace u = CLHEP; + +RMGVolumeDistanceStacker::RMGVolumeDistanceStacker() { this->DefineCommands(); } + +std::optional RMGVolumeDistanceStacker::StackingActionClassify( + const G4Track* aTrack, + int stage +) { + // we are only interested in stacking tracks into stage 1 after stage 0 finished. + if (stage != 0) return std::nullopt; + + // do not touch the initial track of an event. + if (aTrack->GetTrackID() == 0) return std::nullopt; + + // stop if not configured. + if (fVolumeName.empty() || fVolumeSafety < 0) return std::nullopt; + + // only defer electron/positron tracks. + if (aTrack->GetDefinition() != G4Electron::Definition() && + aTrack->GetDefinition() != G4Positron::Definition()) + return std::nullopt; + + // note: aTrack->GetLogicalVolumeAtVertex() and aTrack->GetVertexPosition() might not be correctly + // set at this point - do not trust it. + + // only defer tracks in the specified volume. + const auto vol_name = aTrack->GetVolume()->GetLogicalVolume()->GetName(); + if (vol_name != fVolumeName) return std::nullopt; + + // only defer tracks that have a minimum distance to other volumes. + auto distance = RMGOutputTools::distance_to_surface(aTrack->GetVolume(), aTrack->GetPosition()); + if (distance < fVolumeSafety) return std::nullopt; + + return fWaiting; +} + + +void RMGVolumeDistanceStacker::DefineCommands() { + + fMessenger = std::make_unique( + this, + "/RMG/Output/VolumeStacker/", + "Commands for controlling stacking tracks in the bulk of a volume." + ); + + fMessenger->DeclareMethodWithUnit("VolumeSafety", "cm", &RMGVolumeDistanceStacker::SetVolumeSafety) + .SetGuidance("Set the minimum distance to any other volume for this track to be stacked.") + .SetParameterName("safety", false) + .SetStates(G4State_Idle); + + fMessenger->DeclareMethod("VolumeName", &RMGVolumeDistanceStacker::SetVolumeName) + .SetGuidance("Set the volume name in which to stack e-/e+ tracks.") + .SetParameterName("volume", false) + .SetStates(G4State_Idle); +} + +// vim: tabstop=2 shiftwidth=2 expandtab