src/modules/DepositionGeant4/GeneratorActionG4.cpp
Implements the particle generator. More…
Functions
Name | |
---|---|
void | parse_macro_file_and_prepare_commands(const std::string & file_name, std::vector< std::string > & cmd_list) Parse the given file and save the UI macros in the given vector. |
void | apply_GPS_UI_commands_from_file(const std::string & file_name) Apply GPS UI command from a file. File is only read once and commands are buffered for later calls. |
Detailed Description
Implements the particle generator.
Copyright: Copyright (c) 2017-2024 CERN and the Allpix Squared authors. This software is distributed under the terms of the MIT License, copied verbatim in the file “LICENSE.md”. In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an Intergovernmental Organization or submit itself to any jurisdiction. SPDX-License-Identifier: MIT
Remark: Based on code from John Idarraga
Functions Documentation
function parse_macro_file_and_prepare_commands
static void parse_macro_file_and_prepare_commands(
const std::string & file_name,
std::vector< std::string > & cmd_list
)
Parse the given file and save the UI macros in the given vector.
function apply_GPS_UI_commands_from_file
static void apply_GPS_UI_commands_from_file(
const std::string & file_name
)
Apply GPS UI command from a file. File is only read once and commands are buffered for later calls.
Source code
#include "GeneratorActionG4.hpp"
#include <limits>
#include <memory>
#include <regex>
#include <G4Event.hh>
#include <G4IonTable.hh>
#include <G4ParticleDefinition.hh>
#include <G4ParticleTable.hh>
#include <G4RunManager.hh>
#include <G4UImanager.hh>
#include <Math/Vector2D.h>
#include <core/module/exceptions.h>
#include "core/config/exceptions.h"
#include "core/utils/log.h"
#include "tools/ROOT.h"
#include "tools/geant4/geant4.h"
using namespace allpix;
static void parse_macro_file_and_prepare_commands(const std::string& file_name, std::vector<std::string>& cmd_list) {
std::ifstream file(file_name);
std::string line;
LOG(TRACE) << "Parsing macro file " << file_name;
while(std::getline(file, line)) {
// Check for the "/gps/" pattern in the line:
if(!line.empty()) {
if(line.rfind("/gps/number", 0) == 0) {
throw ModuleError(
"The number of particles must be defined in the main configuration file, not in the macro.");
} else if(line.rfind("/gps/", 0) == 0 || line.at(0) == '#') {
cmd_list.push_back(line);
} else {
LOG(WARNING) << "Ignoring Geant4 macro command: \"" + line + "\" - not related to particle source.";
}
}
}
}
static void apply_GPS_UI_commands_from_file(const std::string& file_name) {
// Commands read from the file and ready to be applied
static std::vector<std::string> ui_commands;
// Get the UI commander
G4UImanager* UI = G4UImanager::GetUIpointer();
// Parse the macro file only once
if(ui_commands.empty()) {
parse_macro_file_and_prepare_commands(file_name, ui_commands);
}
// Apply UI macros
for(auto& cmd : ui_commands) {
LOG(DEBUG) << "Applying Geant4 macro command: \"" << cmd << "\"";
UI->ApplyCommand(cmd);
}
}
// Define radioactive isotopes:
std::map<std::string, std::tuple<int, int, int, double>> GeneratorActionG4::isotopes_ = {
{"fe55", std::make_tuple(26, 55, 0, 0.)},
{"am241", std::make_tuple(95, 241, 0, 0.)},
{"sr90", std::make_tuple(38, 90, 0, 0.)},
{"co60", std::make_tuple(27, 60, 0, 0.)},
{"cs137", std::make_tuple(55, 137, 0, 0.)},
};
GeneratorActionG4::GeneratorActionG4(const Configuration& config)
: particle_source_(std::make_unique<G4GeneralParticleSource>()), config_(config) {
// Set verbosity of source to off
particle_source_->SetVerbosity(0);
// Get source specific parameters
auto source_type = config_.get<SourceType>("source_type");
if(source_type == SourceType::MACRO) {
LOG(INFO) << "Using user macro for particle source.";
// Get the macro file and apply its commands
auto file_name = config.getPath("file_name", true);
apply_GPS_UI_commands_from_file(file_name);
} else {
// Get the source and set the centre coordinate of the source
auto* single_source = particle_source_->GetCurrentSource();
single_source->GetPosDist()->SetCentreCoords(config_.get<G4ThreeVector>("source_position"));
// Set position and direction parameters according to shape
if(source_type == SourceType::BEAM) {
// Align the -z axis of the system with the direction vector
auto direction = config_.get<G4ThreeVector>("beam_direction");
if(fabs(direction.mag() - 1.0) > std::numeric_limits<double>::epsilon()) {
LOG(WARNING) << "Momentum direction is not a unit vector: magnitude is ignored";
}
auto min_element = std::min(std::abs(direction.x()), std::min(std::abs(direction.y()), std::abs(direction.z())));
G4ThreeVector angref1;
if(min_element == std::abs(direction.x())) {
angref1 = direction.cross({1, 0, 0});
} else if(min_element == std::abs(direction.y())) {
angref1 = direction.cross({0, 1, 0});
} else if(min_element == std::abs((direction.z()))) {
angref1 = direction.cross({0, 0, 1});
}
G4ThreeVector angref2 = angref1.cross(direction);
// Set position parameters
single_source->GetPosDist()->SetPosDisType("Beam");
// Get beam_size parameter(s) from config file
ROOT::Math::XYVector beam_size{};
try {
beam_size = config_.get<ROOT::Math::XYVector>("beam_size", {0, 0});
} catch(InvalidKeyError&) {
const auto size = config_.get<double>("beam_size", 0);
beam_size = {size, size};
}
auto beam_shape = config_.get<BeamShape>("beam_shape", BeamShape::CIRCLE);
if(config_.get<bool>("flat_beam", false)) {
// Note: G4 definition of rectangle swaps x and y wrt our global coordinate system
single_source->GetPosDist()->SetPosDisType("Plane");
if(beam_shape == BeamShape::RECTANGLE) {
single_source->GetPosDist()->SetPosDisShape("Rectangle");
single_source->GetPosDist()->SetHalfX((beam_size.y()) / 2);
single_source->GetPosDist()->SetHalfY((beam_size.x()) / 2);
} else if(beam_shape == BeamShape::CIRCLE) {
single_source->GetPosDist()->SetPosDisShape("Circle");
single_source->GetPosDist()->SetRadius(beam_size.x());
} else if(beam_shape == BeamShape::ELLIPSE) {
single_source->GetPosDist()->SetPosDisShape("Ellipse");
single_source->GetPosDist()->SetHalfX((beam_size.y()) / 2);
single_source->GetPosDist()->SetHalfY((beam_size.x()) / 2);
} else {
throw InvalidValueError(config_, "beam_shape", "Cannot use this beam shape with flat beams");
}
} else {
if(beam_shape == BeamShape::CIRCLE) {
single_source->GetPosDist()->SetBeamSigmaInR(beam_size.x());
} else if(beam_shape == BeamShape::ELLIPSE || beam_shape == BeamShape::RECTANGLE) {
single_source->GetPosDist()->SetBeamSigmaInX((beam_size.y()) / 2);
single_source->GetPosDist()->SetBeamSigmaInY((beam_size.x()) / 2);
} else {
throw InvalidValueError(config_, "beam_shape", "This beam shape can only be used with flat beams");
}
}
single_source->GetPosDist()->SetPosRot1(angref1);
single_source->GetPosDist()->SetPosRot2(angref2);
if(config_.count({"beam_divergence", "focus_point"}) > 1) {
throw InvalidCombinationError(config_,
{"beam_divergence", "focus_point"},
"Beam divergence and beam focus point are mutually exclusive!");
}
if(config_.has("focus_point")) {
// Set beam to focus
single_source->GetAngDist()->SetAngDistType("focused");
auto focus_point = config_.get<G4ThreeVector>("focus_point");
single_source->GetAngDist()->SetFocusPoint(focus_point);
} else {
// Set angle distribution parameters
// NOTE beam2d will always fire in the -z direction of the system
single_source->GetAngDist()->SetAngDistType("beam2d");
single_source->GetAngDist()->DefineAngRefAxes("angref1", angref1);
single_source->GetAngDist()->DefineAngRefAxes("angref2", angref2);
auto divergence = config_.get<G4TwoVector>("beam_divergence", G4TwoVector(0., 0.));
single_source->GetAngDist()->SetBeamSigmaInAngX(divergence.x());
single_source->GetAngDist()->SetBeamSigmaInAngY(divergence.y());
}
} else if(source_type == SourceType::SPHERE) {
// Set position parameters
single_source->GetPosDist()->SetPosDisType("Surface");
single_source->GetPosDist()->SetPosDisShape("Sphere");
// Set angle distribution parameters
single_source->GetPosDist()->SetRadius(config_.get<double>("sphere_radius"));
if(config_.has("sphere_focus_point")) {
single_source->GetAngDist()->SetAngDistType("focused");
single_source->GetAngDist()->SetFocusPoint(config_.get<G4ThreeVector>("sphere_focus_point"));
} else {
single_source->GetAngDist()->SetAngDistType("cos");
}
} else if(source_type == SourceType::SQUARE) {
// Set position parameters
single_source->GetPosDist()->SetPosDisType("Plane");
single_source->GetPosDist()->SetPosDisShape("Square");
single_source->GetPosDist()->SetHalfX(config_.get<double>("square_side") / 2);
single_source->GetPosDist()->SetHalfY(config_.get<double>("square_side") / 2);
// Set angle distribution parameters
single_source->GetAngDist()->SetAngDistType("iso");
single_source->GetAngDist()->SetMaxTheta(config_.get<double>("square_angle", ROOT::Math::Pi()) / 2);
} else if(source_type == SourceType::POINT) {
// Set position parameters
single_source->GetPosDist()->SetPosDisType("Point");
// Set angle distribution parameters
single_source->GetAngDist()->SetAngDistType("iso");
}
// Find Geant4 particle
auto* pdg_table = G4ParticleTable::GetParticleTable();
particle_type_ = allpix::transform(config_.get<std::string>("particle_type", ""), ::tolower);
auto particle_code = config_.get<int>("particle_code", 0);
G4ParticleDefinition* particle = nullptr;
if(!particle_type_.empty() && particle_code != 0) {
if(pdg_table->FindParticle(particle_type_) == pdg_table->FindParticle(particle_code)) {
LOG(WARNING) << "particle_type and particle_code given. Continuing because they match.";
particle = pdg_table->FindParticle(particle_code);
if(particle == nullptr) {
throw InvalidValueError(config_, "particle_code", "particle code does not exist.");
}
} else {
throw InvalidValueError(config_,
"particle_type",
"Given particle_type does not match particle_code. Please remove one of them.");
}
} else if(particle_type_.empty() && particle_code == 0) {
throw InvalidValueError(config_, "particle_code", "Please set particle_code or particle_type.");
} else if(particle_code != 0) {
particle = pdg_table->FindParticle(particle_code);
if(particle == nullptr) {
throw InvalidValueError(config_, "particle_code", "particle code does not exist.");
}
} else if(isotopes_.find(particle_type_) != isotopes_.end() || particle_type_.substr(0, 3) == "ion") {
// In the case we are using a multithreaded version of Geant4, Ion tables may not be ready to use now
// so we do use them later
initialize_ion_as_particle_ = true;
} else {
particle = pdg_table->FindParticle(particle_type_);
if(particle == nullptr) {
throw InvalidValueError(config_, "particle_type", "particle type does not exist.");
}
}
if(particle != nullptr) {
LOG(DEBUG) << "Using particle " << particle->GetParticleName() << " (ID " << particle->GetPDGEncoding() << ").";
// Set global parameters of the source
single_source->SetNumberOfParticles(1);
single_source->SetParticleDefinition(particle);
// Set the primary track's start time in for the current event to zero:
single_source->SetParticleTime(0.0);
}
// Set energy parameters
single_source->GetEneDist()->SetEnergyDisType("Gauss");
single_source->GetEneDist()->SetMonoEnergy(config_.get<double>("source_energy"));
single_source->GetEneDist()->SetBeamSigmaInE(config_.get<double>("source_energy_spread", 0.));
}
}
void GeneratorActionG4::GeneratePrimaries(G4Event* event) {
// G4IonTable is only ready after initialization, so we need to pick the particle here and assign it to the source:
if(initialize_ion_as_particle_) {
auto* single_source = particle_source_->GetCurrentSource();
G4ParticleDefinition* particle = nullptr;
if(isotopes_.find(particle_type_) != isotopes_.end()) {
auto isotope = isotopes_[particle_type_];
// Set radioactive isotope:
particle = G4IonTable::GetIonTable()->GetIon(std::get<0>(isotope), std::get<1>(isotope), std::get<3>(isotope));
// Force the radioactive isotope to decay immediately:
particle->SetPDGLifeTime(0.);
single_source->SetParticleCharge(std::get<2>(isotope));
// Warn about non-zero source energy:
if(config_.get<double>("source_energy") > 0) {
LOG_ONCE(WARNING)
<< "A radioactive isotope is used as particle source, but the source energy is not set to zero.";
}
} else if(particle_type_.substr(0, 3) == "ion") {
// Parse particle type as ion with components /Z/A/Q/E/D
std::smatch ion;
if(std::regex_match(particle_type_,
ion,
std::regex("ion/([0-9]+)/([0-9]+)/([-+]?[0-9]+)/([0-9.]+(?:[a-zA-Z]+)?)/(true|false)")) &&
ion.ready()) {
particle = G4IonTable::GetIonTable()->GetIon(
allpix::from_string<int>(ion[1]), allpix::from_string<int>(ion[2]), allpix::from_string<double>(ion[4]));
if(allpix::from_string<bool>(ion[5])) {
particle->SetPDGLifeTime(0.);
}
single_source->SetParticleCharge(allpix::from_string<int>(ion[3]));
} else if(std::regex_match(
particle_type_, ion, std::regex("ion/([0-9]+)/([0-9]+)/([-+]?[0-9]+)/([0-9.]+(?:[a-zA-Z]+)?)")) &&
ion.ready()) {
// Parse old declaration with /Z/A/Q/E
particle = G4IonTable::GetIonTable()->GetIon(
allpix::from_string<int>(ion[1]), allpix::from_string<int>(ion[2]), allpix::from_string<double>(ion[4]));
single_source->SetParticleCharge(allpix::from_string<int>(ion[3]));
LOG(WARNING) << "Using \"ion/Z/A/Q/E\" is deprecated and superseded by \"ion/Z/A/Q/E/D\".";
} else {
throw InvalidValueError(config_, "particle_type", "cannot parse parameters for ion.");
}
}
if(particle != nullptr) {
// Set global parameters of the source
single_source->SetNumberOfParticles(1);
single_source->SetParticleDefinition(particle);
// Set the primary track's start time in for the current event to zero:
single_source->SetParticleTime(0.0);
// mark the initialization done
initialize_ion_as_particle_ = false;
LOG(DEBUG) << "Using ion " << particle->GetParticleName() << " (ID " << particle->GetPDGEncoding() << ") with "
<< Units::display(particle->GetPDGLifeTime(), {"s", "ns"}) << " lifetime.";
} else {
throw InvalidValueError(config_, "particle_type", "failed to fetch or create ion.");
}
}
particle_source_->GeneratePrimaryVertex(event);
}
GeneratorActionInitializationMaster::GeneratorActionInitializationMaster(const Configuration& config)
: particle_source_(std::make_unique<G4GeneralParticleSource>()) {
// Set verbosity of source to off
particle_source_->SetVerbosity(0);
// Get source specific parameters
auto source_type = config.get<GeneratorActionG4::SourceType>("source_type");
if(source_type == GeneratorActionG4::SourceType::MACRO) {
LOG(INFO) << "Using user macro for particle source.";
// Get the macro file and apply its commands
auto file_name = config.getPath("file_name", true);
apply_GPS_UI_commands_from_file(file_name);
}
}
Updated on 2024-12-13 at 08:31:37 +0000