/*
* Copyright (C) 2018 Université Clermont Auvergne, CNRS/IN2P3, LPC
* Author: Valentin NIESS (niess@in2p3.fr)
*
* A Geant4 particle source of atmospheric muons using GOUPIL
*
* 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 "IGoupilSource.hh"
#include "IGoupilSettings.hh"
#include "IGoupilUserAction.hh"
#include "goupil.h"
#include "goupil-eole.h"
#include "goupil-gull.h"
#include "goupil-primal.h"
#include "goupil-primate.h"
#include "goupil-turtle.h"
/* Geant4 includes */
#include "G4Event.hh"
#include "G4ExceptionHandler.hh"
#include "G4Field.hh"
#include "G4FieldManager.hh"
#include "G4Material.hh"
#include "G4MuonMinus.hh"
#include "G4MuonPlus.hh"
#include "G4TransportationManager.hh"
#include "Randomize.hh"
#include "G4PVPlacement.hh"
/* STD lib includes */
#include
#include
/* Global data and settings */
static struct {
/* Reference count of the number of sources instances */
int references;
/* The Geant4 geometry layer */
struct goupil_geometry_layer * g4layer;
/* The Geant4 frame transform */
struct goupil_coordinates_frame g4frame;
} gGlobals = {0, NULL, {{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0}}};
/* Error handler for GOUPIL */
static void handle_goupil(const char * message)
{
G4ExceptionDescription description;
description << G4endl << message << G4endl;
G4Exception("IGoupilSource", "A GOUPIL library error occurred",
FatalException, description);
}
/* Encapsulation of the Geant4 random engine */
static double random_uniform01(struct goupil_random *)
{
return G4UniformRand();
}
static int random_initialise_sampler(struct goupil_sampler_module * module)
{
struct goupil_random * random = (struct goupil_random *)module;
random->uniform01 = &random_uniform01;
return EXIT_SUCCESS;
}
static const char * goupil_signature_random(void)
{
return "G4UniformRand";
}
static int random_register(void)
{
return goupil_random_register(
&goupil_signature_random, NULL, &random_initialise_sampler);
}
/* Map for translating a Geant4 volume to a GOUPIL medium. TODO: optimize
* this, if relevant */
typedef std::map VolumeProxy;
/* History data of the geometry stepping */
struct g4_volume_history {
struct goupil_medium * medium[2];
VolumeProxy proxy;
};
/* Geant4 specific data for GOUPIL media */
struct medium_data {
G4VPhysicalVolume * physical;
const G4Field * field;
};
/* Update the map of Geant4 to GOUPIL volumes & the stepping history */
static struct goupil_medium * UpdateVolumes(
struct g4_volume_history * history, G4VPhysicalVolume * physical)
{
/* Check the history */
if ((G4VPhysicalVolume *)history->medium[0] != NULL) {
struct medium_data * data = (struct medium_data *)
goupil_medium_data(history->medium[0]);
if (data->physical == physical)
return history->medium[0];
}
if ((G4VPhysicalVolume *)history->medium[1] != NULL) {
struct medium_data * data = (struct medium_data *)
goupil_medium_data(history->medium[1]);
if (data->physical == physical) {
struct goupil_medium * m = history->medium[1];
history->medium[1] = history->medium[0];
history->medium[0] = m;
return m;
}
}
/* Look down the tree for the physical volume */
struct goupil_medium *& medium = history->proxy[physical];
if (medium == NULL) {
/* Fetch the medium data */
const char * name = physical->GetName().c_str();
G4LogicalVolume * logical = physical->GetLogicalVolume();
G4Material * material = logical->GetMaterial();
const char * material_name = material->GetName().c_str();
double density = material->GetDensity() / CLHEP::kg * CLHEP::m3;
/* Allocate the medium */
goupil_medium_create(&medium, name, material_name, density,
sizeof (struct medium_data));
/* Link the Geant4 physical volume */
struct medium_data * data = (struct medium_data *)
goupil_medium_data(medium);
data->physical = physical;
/* Link any electromagnetic field */
G4FieldManager * mgr = logical->GetFieldManager();
if (mgr == NULL) {
mgr = G4TransportationManager::
GetTransportationManager()->GetFieldManager();
}
if (mgr != NULL)
data->field = mgr->GetDetectorField();
else
data->field = NULL;
}
/* Update the history */
history->medium[1] = history->medium[0];
history->medium[0] = medium;
return medium;
}
/* Encapsulation of the Geant4 geometry navigator, for GOUPIL */
static double geometry_get_medium(struct goupil_geometry * geometry,
const double position[3], const double direction[3],
struct goupil_medium ** medium)
{
G4ThreeVector r(position[0] * CLHEP::m, position[1] * CLHEP::m,
position[2] * CLHEP::m);
G4ThreeVector u(direction[0], direction[1], direction[2]);
G4Navigator * navigator =
G4TransportationManager::GetTransportationManager()
->GetNavigatorForTracking();
/* Fetch the physical volume */
if (medium != NULL) {
navigator->ResetStackAndState();
G4VPhysicalVolume * physical =
navigator->LocateGlobalPointAndSetup(r, &u);
if (physical == NULL) {
*medium = NULL;
return 0.;
}
/* Fetch or register the volume if new */
*medium = UpdateVolumes((struct g4_volume_history *)
geometry->module.data, physical);
}
/* Compute the step length */
G4double safety = 0.;
return navigator->ComputeStep(r, u, kInfinity, safety) / CLHEP::m;
}
/* Encapsulation of the Geant4 magnetic field, for GOUPIL */
static double geometry_get_magnet(struct goupil_geometry *,
struct goupil_medium * medium, const double * position,
const double * direction, double * magnet)
{
/* Unpack the Geant4 field */
struct medium_data * data = (struct medium_data *)
goupil_medium_data(medium);
if ((data == NULL) || (data->field == NULL)) return -1.;
/* Use the Geant4 field getter in order to compute the magnetic field */
double point[4] = { position[0] * CLHEP::m,
position[1] * CLHEP::m, position[2] * CLHEP::m, 0. };
double value0[6], value1[6];
data->field->GetFieldValue(point, value0);
/* Fill in the magnetic field components and update the position */
const double ds = 1. * CLHEP::mm;
int i;
for (i = 0; i < 3; i++) {
magnet[i] = value0[i] / CLHEP::tesla;
point[i] += ds * direction[i];
}
/* Compute the magnetic field gradient */
data->field->GetFieldValue(point, value1);
double dB = 0., B = 0.;
for (i = 0; i < 3; i++) {
dB += fabs(value0[i] - value1[i]);
const double tmp = 0.5 * (value0[i] + value1[i]);
B += tmp * tmp;
}
if ((B > 0.) && (dB > 0.)) {
B = sqrt(B);
return 1E-02 * ds * B / (dB * CLHEP::m);
} else {
/* TODO: Geometry size? */
return 1.;
}
}
static const char * goupil_signature_geometry(void)
{
return "geometry";
}
/* Clean the sampler */
static void geometry_clean(struct goupil_sampler_module * module)
{
delete (struct g4_volume_history *)module->data;
module->data = NULL;
}
/* Initialise the geometry data for a new sampler, i.e. a thread local map
* of G4PhysicalVolume's and stepping history.
*/
static int geometry_initialise_sampler(struct goupil_sampler_module * module)
{
module->clean = &geometry_clean;
struct g4_volume_history * history = new struct g4_volume_history;
history->medium[1] = history->medium[0] = NULL;
module->data = history;
struct goupil_geometry * geometry = (struct goupil_geometry *)module;
geometry->get_medium = &geometry_get_medium;
geometry->get_magnet = &geometry_get_magnet;
return EXIT_SUCCESS;
}
static int geometry_register(void)
{
return goupil_geometry_register(&goupil_signature_geometry, NULL);
}
static G4double to_mm(G4double value)
{
return round(value * CLHEP::m / CLHEP::mm * 1E+03) * 1E-03;
}
static G4double to_MeV(G4double value)
{
return round(value * CLHEP::GeV / CLHEP::MeV * 1E+03) * 1E-03;
}
/* Map for translating GOUPIL outer volumes to Geant4 ones */
typedef std::map OuterVolume;
/* Stepping history for recording */
struct SteppingHistory {
int verbose;
std::ostream * stream;
IGoupilState * ancester;
G4int index;
struct goupil_geometry * geometry;
G4double kinetic_energy;
G4double distance;
OuterVolume outer;
};
/* Map a GOUPIL event to a name string */
static const char * event_to_str(enum goupil_event event)
{
if (event & GOUPIL_EVENT_START)
return "Initialisation";
else if (event & GOUPIL_EVENT_LIMIT_KINETIC)
return "KineticLimit";
else if (event & GOUPIL_EVENT_MEDIUM)
return "Transportation";
else if (event & GOUPIL_EVENT_VERTEX_BREMSSTRAHLUNG)
return "Bremsstrahlung";
else if (event & GOUPIL_EVENT_VERTEX_PAIR_CREATION)
return "PairCreation";
else if (event & GOUPIL_EVENT_VERTEX_PHOTONUCLEAR)
return "Photonuclear";
else if (event & GOUPIL_EVENT_VERTEX_DELTA_RAY)
return "DeltaRay";
else if (event & GOUPIL_EVENT_VERTEX_COULOMB)
return "CoulombScattering";
else
return "Ionisation";
}
static void recorder_dump(struct goupil_recorder * recorder,
struct goupil_recorder_frame * frame)
{
SteppingHistory * history = (SteppingHistory *)recorder->module.data;
if (frame->event & GOUPIL_EVENT_START) {
*history->stream << G4endl;
*history->stream << std::string(105, '*') << G4endl;
*history->stream << "* IGoupilSource Information:" << G4endl;
*history->stream << std::string(105, '*') << G4endl;
}
IGoupilSettings * settings = IGoupilSettings::GetInstance();
if (settings->GetVerboseLevel() < 2)
return;
if (frame->geometry != history->geometry) {
/* Dump a header. TODO: skip if same than previous */
const goupil_geometry_layer * layer =
goupil_geometry_get_layer(frame->geometry);
const char * tag[4];
if (layer == gGlobals.g4layer) {
tag[0] = "X(mm)";
tag[1] = "Y(mm)";
tag[2] = "Z(mm)";
tag[3] = "Volume";
} else {
tag[0] = "Lat(deg)";
tag[1] = "Lon(deg)";
tag[2] = "H(m)";
tag[3] = "Medium";
}
*history->stream << G4endl
<< std::setw( 5) << "Step#" << " "
<< std::setw( 8) << tag[0] << " "
<< std::setw( 8) << tag[1] << " "
<< std::setw( 8) << tag[2] << " "
<< std::setw( 9) << "KinE(MeV)" << " "
<< std::setw( 8) << "dE(MeV)" << " "
<< std::setw( 8) << "StepLeng" << " "
<< std::setw( 9) << "TrackLeng" << " "
<< std::setw(11) << tag[3] << " "
<< std::setw( 8) << "ProcName" << G4endl;
}
/* Dump the step summary */
char line[120], * cursor = line;
cursor += sprintf(cursor, "%5d ", history->index);
if (frame->geometry != NULL) {
const goupil_coordinates_frame * f =
goupil_geometry_get_frame(frame->geometry);
goupil_coordinates_cartesian * r = frame->position;
goupil_sampler_coordinates_transform(
recorder->module.sampler, f, r, r);
cursor += sprintf(cursor, "%8g %8g %8g ", to_mm(r->x),
to_mm(r->y), to_mm(r->z));
} else {
goupil_coordinates_geographic * r =
(goupil_coordinates_geographic *)frame->position;
goupil_sampler_coordinates_as(recorder->module.sampler,
GOUPIL_COORDINATES_GEOGRAPHIC, r, r);
const double dlat = r->latitude -
settings->GetEarthLocationLatitude() / CLHEP::deg;
const double dlon = r->longitude -
settings->GetEarthLocationLongitude() / CLHEP::deg;
cursor += sprintf(cursor, "%8.1e %8.1e %8.3f ", dlat, dlon,
r->altitude);
}
G4double dE = to_MeV(frame->kinetic_energy - history->kinetic_energy);
G4double ds = to_mm(frame->distance - history->distance);
const char * medium_name;
if (frame->medium == NULL)
medium_name = "OutOfWorld";
else
medium_name = goupil_medium_get_name(frame->medium);
cursor += sprintf(cursor, "%9g %8g %8g %9g %11s %8s",
to_MeV(frame->kinetic_energy), dE, ds,
to_mm(frame->distance), medium_name,
event_to_str(frame->event));
*history->stream << line << G4endl;
}
static void recorder_copy_state(struct goupil_recorder * recorder,
struct goupil_recorder_frame * frame, IGoupilState * state,
bool geographic)
{
goupil_coordinates_cartesian * r = frame->position;
if (geographic) {
GOUPIL_COORDINATES_GEOGRAPHIC_CREATE(rgeo, 0, 0, 0);
goupil_sampler_coordinates_as(recorder->module.sampler,
GOUPIL_COORDINATES_GEOGRAPHIC, r, rgeo);
const double dz = rgeo->altitude;
goupil_sampler_coordinates_as(recorder->module.sampler,
GOUPIL_COORDINATES_GEODETIC, rgeo, rgeo);
state->SetLatitude(rgeo->latitude * CLHEP::deg);
state->SetLongitude(rgeo->longitude * CLHEP::deg);
state->SetAltitude(rgeo->altitude * CLHEP::m);
state->SetTopographyLevel((rgeo->altitude - dz) * CLHEP::m);
}
goupil_sampler_coordinates_transform(
recorder->module.sampler, &gGlobals.g4frame, r, r);
state->SetPosition(
G4ThreeVector(r->x, r->y, r->z) * CLHEP::m);
goupil_coordinates_cartesian * u = frame->direction;
goupil_sampler_coordinates_transform(
recorder->module.sampler, &gGlobals.g4frame, u, u);
state->SetMomentumDirection(
G4ThreeVector(u->x, u->y, u->z));
state->SetKineticEnergy(
frame->kinetic_energy * CLHEP::GeV);
}
static void recorder_record(struct goupil_recorder * recorder,
struct goupil_recorder_frame * frame)
{
SteppingHistory * history = (SteppingHistory *)recorder->module.data;
if (frame->event < 0) {
recorder_copy_state(recorder, frame, history->ancester, false);
/* TODO: print a summary */
return;
}
IGoupilUserAction * userAction =
(IGoupilUserAction *)goupil_sampler_data_get(recorder->module.sampler);
if ((!history->verbose) && (!userAction)) {
return;
} else if (frame->event & GOUPIL_EVENT_START) {
/* Initialise the history */
history->index = 0;
history->geometry = NULL;
history->geometry++; /* Initialise to an unreachable address */
history->kinetic_energy = frame->kinetic_energy;
history->distance = frame->distance;
}
if (history->verbose) {
recorder_dump(recorder, frame);
}
if (userAction) {
IGoupilState state;
recorder_copy_state(recorder, frame, &state, true);
G4VPhysicalVolume * physical;
double field[6] = { 0., 0., 0., 0., 0., 0. };
if (frame->medium) {
if (frame->geometry != NULL) {
struct medium_data * data =
(struct medium_data *)goupil_medium_data(frame->medium);
physical = data->physical;
const G4ThreeVector & position = state.GetPosition();
const double r[3] = { position.x() / CLHEP::m,
position.y() / CLHEP::m, position.z() / CLHEP::m };
const G4ThreeVector & direction = state.GetMomentumDirection();
const double u[3] = { direction.x(), direction.y(), direction.z() };
geometry_get_magnet(
frame->geometry, frame->medium, r, u, field + 3);
} else {
physical = history->outer[frame->medium];
if (physical == NULL) {
/* Create a volume wrapper */
static G4LogicalVolume logical(0, 0, "(nil)");
physical = new G4PVPlacement(0, G4ThreeVector(0, 0, 0), &logical,
goupil_medium_get_name(frame->medium), 0, false, 0);
history->outer[frame->medium] = physical;
}
}
} else {
physical = NULL;
}
userAction->SteppingAction(history->index, frame->event,
physical, &state, field);
}
/* Update the history */
history->index++;
history->geometry = frame->geometry;
history->kinetic_energy = frame->kinetic_energy;
history->distance = frame->distance;
}
static void clean_recorder(struct goupil_sampler_module * module)
{
SteppingHistory * history = (SteppingHistory *)module->data;
if (history->stream != &G4cout) {
std::ofstream * ofs =
static_cast(history->stream);
ofs->close();
delete history->stream;
}
delete (SteppingHistory *)history;
}
static int recorder_initialise_sampler(struct goupil_sampler_module * module)
{
SteppingHistory * history = new SteppingHistory;
history->verbose = IGoupilSettings::GetVerboseLevel();
const G4String& file = IGoupilSettings::GetVerboseFile();
/* TODO: handle multiple threads, e.g. rename the file */
if (file.size()) {
std::ofstream * ofs = new std::ofstream;
ofs->open(file);
history->stream = ofs;
} else {
history->stream = &G4cout;
}
module->data = history;
module->clean = &clean_recorder;
struct goupil_recorder * recorder = (struct goupil_recorder *)module;
recorder->record = &recorder_record;
return EXIT_SUCCESS;
}
static const char * goupil_signature_recorder(void)
{
return "recorder";
}
static int recorder_register(void)
{
return goupil_recorder_register(&goupil_signature_recorder, NULL,
&recorder_initialise_sampler);
}
static G4String GetGoupilDataDir()
{
const char * path = getenv("GOUPIL_DATA_DIR");
if (path == NULL)
path = GOUPIL_DATA_DIR;
return path;
}
static void InitialiseSampler(
struct goupil_sampler ** sampler, IGoupilUserAction * userAction, const std::string& samplerMode)
{
if (*sampler != NULL)
return;
/* Check the configuration */
IGoupilSettings * settings = IGoupilSettings::GetInstance();
if (!settings->CheckEarthLocation()) {
G4ExceptionDescription description;
description << G4endl
<< "==> Earth location has not been set <=="
<< G4endl;
G4Exception("IGoupilSource", "Invalid configuration",
FatalException, description);
}
/* Initialise GOUPIL : TODO: put a barrier here */
if (gGlobals.references == 0) {
/* Initialise the GOUPIL library */
G4String data_dir = GetGoupilDataDir();
goupil_error_handler_set(&handle_goupil);
G4String mdf_path = data_dir + "/materials/materials.xml";
G4String dedx_path = data_dir + "/materials/dedx";
G4String cfg_path = data_dir + "/.g4goupil.cfg";
char * argv[] = {
(char *)mdf_path.c_str(),
(char *)dedx_path.c_str(),
(char *)cfg_path.c_str() };
goupil_initialise(
NULL, NULL, sizeof(argv) / sizeof(*argv), argv);
/* Configure GOUPIL to use the Geant4 pseudo-random engine */
random_register();
/* Configure the atmosphere model */
goupil_eole_register();
/* Configure the geomagnetic field */
G4String geomagnet_path = data_dir + "/geomagnet/" +
settings->GetGeomagnetModel() + ".COF";
goupil_gull_register(geomagnet_path.c_str(),
settings->GetGeomagnetDateDay(),
settings->GetGeomagnetDateMonth(),
settings->GetGeomagnetDateYear());
/* Configure the topography */
goupil_turtle_register(NULL, NULL); /* TODO: multithreading */
goupil_turtle_set_bottom(
settings->GetTopographyBottom() / CLHEP::m);
const IGoupilTopography & topography =
settings->GetTopography();
const G4String & reference = settings->GetTopographyReference();
bool referenceSet = false;
for (IGoupilTopography::const_iterator i = topography.begin();
i != topography.end(); i++) {
struct goupil_medium * layer;
G4String name = i->GetName();
goupil_turtle_layer_create(&layer, name.c_str(),
i->GetMaterial().c_str(),
i->GetDensity() / CLHEP::kg * CLHEP::m3);
IGoupilTopographyData data = i->GetData();
for (IGoupilTopographyData::const_iterator j =
data.begin(); j != data.end(); j++) {
struct goupil_turtle_data * datum;
const char * path = j->first.size() ?
j->first.c_str() : NULL;
goupil_turtle_data_create(&datum, path);
goupil_turtle_layer_link(
layer, datum, j->second / CLHEP::m);
}
if (reference == name) {
goupil_turtle_set_reference(layer);
referenceSet = true;
}
}
if (reference.empty()) {
goupil_turtle_set_reference(NULL);
} else if (!referenceSet) {
G4ExceptionDescription description;
description << G4endl << "==> Unknown reference layer `"
<< reference << "' <==" << G4endl;
G4Exception("IGoupilSource",
"A GOUPIL configuration error occurred",
FatalException, description);
}
/* Configure the primary flux model */
const G4String & model = settings->GetPrimaryModel();
if (strncmp(model.c_str(), "table:", 6) == 0) {
std::string filename = model.substr(6);
if (filename[0] != '/') {
G4String dirname = data_dir + "/primary/";
filename = dirname + filename;
}
goupil_primate_register(filename.c_str());
const double height = *goupil_primary_get_altitude();
settings->SetPrimaryHeight(height * CLHEP::m);
} else {
goupil_primal_register();
goupil_primal_model_set(model);
goupil_primal_altitude_set(
settings->GetPrimaryHeight() / CLHEP::m);
}
/* Configure the recorder */
G4int verboseLevel = settings->GetVerboseLevel();
recorder_register();
if ((verboseLevel >= 3) || userAction)
goupil_recorder_period_set(1);
else
goupil_recorder_period_set(0);
/* Configure the interface with the Geant4 geometry */
geometry_register();
GOUPIL_COORDINATES_GEOGRAPHIC_CREATE(origin,
settings->GetEarthLocationLatitude() / CLHEP::deg,
settings->GetEarthLocationLongitude() / CLHEP::deg,
settings->GetEarthLocationAltitude() / CLHEP::m);
GOUPIL_COORDINATES_EULER_CREATE(orientation,
settings->GetWorldOrientationAlpha() / CLHEP::deg,
settings->GetWorldOrientationBeta() / CLHEP::deg,
settings->GetWorldOrientationGamma() / CLHEP::deg);
goupil_geometry_layer_create(&gGlobals.g4layer, 0,
"Geant4", &goupil_signature_geometry, NULL,
&geometry_initialise_sampler, origin, orientation);
/* Configure the sampling mode */
goupil_sampler_mode_set(samplerMode.c_str());
}
/* Create the GOUPIL sampler */
goupil_sampler_create(sampler);
goupil_sampler_data_set(*sampler, userAction);
/* Set some remaining globals, requiring the sampler */
if (gGlobals.references++ == 0) {
const struct goupil_coordinates_frame * frame =
goupil_sampler_get_frame(*sampler, gGlobals.g4layer);
memcpy(&gGlobals.g4frame, frame, sizeof(gGlobals.g4frame));
}
}
/* Instanciate a new GOUPIL generator */
IGoupilSource::IGoupilSource() : G4SingleParticleSource(), fSampler(NULL),
fSamplerMode("DETAILED"), fUserAction(NULL)
{
/* Instanciate the muons, in case the Physics didn't do so */
G4MuonPlus::Definition();
G4MuonMinus::Definition();
/* Initialise the particle source */
G4SingleParticleSource::SetParticleDefinition(
G4MuonMinus::Definition());
}
/* Properly clean a GOUPIL generator */
IGoupilSource::~IGoupilSource()
{
goupil_sampler_destroy(&fSampler);
if (--gGlobals.references == 0) {
goupil_geometry_layer_destroy(&gGlobals.g4layer);
goupil_finalise();
}
}
/* Generate a muon final state and backward sample its atmospheric flux */
void IGoupilSource::GeneratePrimaryVertex(G4Event * event)
{
InitialiseSampler(&fSampler, fUserAction, fSamplerMode);
IGoupilSettings * settings = IGoupilSettings::GetInstance();
double weight;
if (settings->GetChargeRandomisation()) {
/* Generate the charge */
weight = 2.;
if (G4UniformRand() < 0.5)
G4SingleParticleSource::SetParticleDefinition(
G4MuonMinus::Definition());
else
G4SingleParticleSource::SetParticleDefinition(
G4MuonPlus::Definition());
} else {
weight = 1.;
}
/* Generate the final state(s) */
G4SingleParticleSource::GeneratePrimaryVertex(event);
/* Loop over the generated state(s) */
const G4int iVertex = event->GetNumberOfPrimaryVertex() - 1;
G4PrimaryVertex * vertex = event->GetPrimaryVertex(iVertex);
G4ThreeVector r = vertex->GetPosition() / CLHEP::m;
GOUPIL_COORDINATES_CARTESIAN_CREATE_POINT(
position, &gGlobals.g4frame, r[0], r[1], r[2]);
const G4int nprim = vertex->GetNumberOfParticle();
if ((G4int)fAncester.size() < nprim)
fAncester.resize(nprim);
int q = (int)this->GetParticleDefinition()->GetPDGCharge();
for (G4int i = 0; i < nprim; i++) {
/* Forward the ancester container to the recorder */
struct goupil_recorder * recorder =
goupil_sampler_get_recorder(fSampler);
SteppingHistory * history =
(SteppingHistory *)recorder->module.data;
history->ancester = &fAncester[i];
/* Transport the final state backward */
G4PrimaryParticle * primary = vertex->GetPrimary(i);
const double kinetic_energy =
primary->GetKineticEnergy() / CLHEP::GeV;
G4ThreeVector u = primary->GetMomentumDirection();
GOUPIL_COORDINATES_CARTESIAN_CREATE_VECTOR(direction,
&gGlobals.g4frame, u[0], u[1], u[2]);
double flux;
goupil_sampler_run(
fSampler, q, kinetic_energy, position, direction, &flux);
/* Update the MC weight */
weight *= primary->GetWeight();
primary->SetWeight(weight * flux);
}
}
/*
* Overload the base setter in order to check that the primary particle is
* indeed a mu+ or mu-
*/
void IGoupilSource::SetParticleDefinition(G4ParticleDefinition * aDefinition)
{
if ((aDefinition == G4MuonMinus::Definition()) ||
(aDefinition == G4MuonPlus::Definition())) {
G4SingleParticleSource::SetParticleDefinition(aDefinition);
} else {
G4Exception("IGoupilSource::SetParticleDefinition",
"Invalid primary", FatalException,
"\tOnly mu- or mu+ primaries are supported\n");
}
}
void IGoupilSource::SetSamplerMode(const std::string& mode) {
fSamplerMode = mode;
}