Полный рабочий пример на Geant4 с визуализацией

Полный рабочий пример на Geant4 с визуализацией

Рассмотрим полностью рабочий пример на Geant4 с визуализацией, который «хоть что-то делает». Этот проект является более сложным, чем первый пример на Geant4, однако этот пример позволяет полноценно провести моделирование установки, записать спектр в файл, угловое распределение при попадании в детектор. Вто же время, предыдущий пример существенно более прост, в том числе и для понимания основных частей проекта на Джанте.

Запуск примера

Для запуска проекта в консоли смотрите сюда. Если же вы хотите все сделать "по человечески", с возможностью отлаживать программу, тогда вам нужно посмотреть статью об интеграции QtCreator и Geant4.

Схема установки примера

И начнем мы разбирать этот пример со схемы установки.

Схема уставки проекта на Geant4
Схема моделируемой установки, P — источник частиц, протоны, 1 — мишень, 2 — чувствительная область, детектор.

У нас есть некий источник начальных частиц, из которого вылетают протоны. Обозначено через вектор P на рисунке. Далее частицы попадают на мишень 1, которая представляет собой некую пластинку. Первичные частицы взаимодействуют с мишенью и вместе с вторичными частицами попадают на детектор 2.

В детекторе у нас размещена чувствительная область, в которой мы и регистрируем частицы, узнаем их энергию и направление полета. Так как далее они нам не нужны, то все их "убиваем", т.е. уничтожаем. И после моделирование выводим спектр протонов и их угловое распределение.

Исходный код проекта

Основной файл проекта example2.cpp

В нем мы подключаем физику, геометрию системы и интерфейс пользователя и многое другое.

// Подключаем заголовочные файлы
#include "G4RunManager.hh" // RunManager, класс из ядра Geant4,
//должен быть включен обязательно
#include "G4UImanager.hh" // Менеджер взаимодействия с пользователем
#include "ExG4DetectorConstruction01.hh" // Конструкция и структура детектора,
//должна определяться пользователем
#include "FTFP_BERT.hh" // Подключение физического листа из Geant4, где определяется
// используемые физические процессы и частицы
#include "ExG4ActionInitialization01.hh" // Пользовательский класс
//для задания начального источника частиц
#ifdef G4UI_USE //Если используется интерфейс пользователя то включаем визуализацию
#include "G4VisExecutive.hh"//Визуализация
#include "G4UIExecutive.hh"//Выбор соответствующего интерфейса пользователя
#endif

int main(int argc,char** argv)
{
// Создание класса G4RunManager, он контролирует выполнение программы и
// управляет событиями (запуском начальных частиц) при запуске проекта
G4RunManager* runManager = new G4RunManager;
// Установка обязательных инициализирующих классов
// Создание геометрии, материала детектора и мишени
// т.е. моделируемой установки
runManager->SetUserInitialization(new ExG4DetectorConstruction01);
// Создание физического листа - набора моделируемых частиц и физических процессов
// которые используются в данном моделировании.
// Используется готовый физический лист из Geant4
runManager->SetUserInitialization(new FTFP_BERT);
// Объявление начальных частиц (параметры пучка) и
// подключение прочих классов, используемых
// для получения данных о частицах в процессе моделирования
runManager->SetUserInitialization(new ExG4ActionInitialization01);
// Инициализация ядра Geant4
runManager->Initialize();
// Объявления менеджера визуализации
G4VisManager* visManager = new G4VisExecutive;
// Инициализация менеджера визуализации
visManager->Initialize();
// Получение указателя на менеджера взаимодействия с пользователем
// нужен, что бы можно было отправлять команды в проект
G4UImanager* UImanager = G4UImanager::GetUIpointer();
// Проверяем или были переданы через командную сроку параметры
if ( argc == 1 ) {//Если через командную строку ничего не передавалось
// То устанавливаем  интерактивный режим
#ifdef G4UI_USE
G4UIExecutive* ui = new G4UIExecutive(argc, argv);//Создание интерфейса пользователя
UImanager->ApplyCommand("/control/execute vis.mac");
//Параметры отображения берем из заранее подготовленного
// файла vis.mac
ui->SessionStart();//Запуск интерфейса пользователя
delete ui;//Удаление интерфейса пользователя
#endif
}
else {
// Если были переданы параметры, по включаем пакетный режим
G4String command = "/control/execute ";//Записываем в строковую переменную
// команду выполнить
G4String fileName = argv[1];//Имя файла из командной строки при запуске проекта
// Мы считаем, что первым параметром было передано имя файла с командами для запуска
// проекта в пакетном режиме
UImanager->ApplyCommand(command+fileName);//Выполнение команды
}
// Окончание работы, вызов деструктора (удаление) G4RunManager
delete runManager;
return 0;
}

Задание геометрии детектора

Далее приведем заголовочный файл класса создания установки, детектора и объявления всех необходимых материалов ExG4DetectorConstruction01.hh

#ifndef ExG4DetectorConstruction01_h
#define ExG4DetectorConstruction01_h 1

#include "G4VUserDetectorConstruction.hh"
#include "globals.hh"

class G4VPhysicalVolume;
class G4LogicalVolume;

/// \brief The ExG4DetectorConstruction01 class Класс геометрии установки,
///  объявление материалов и детекторов

class ExG4DetectorConstruction01 : public G4VUserDetectorConstruction
{
  public:
    //Конструктор, вызывается при создании экземпляра класса
    //Обычно используется для задания начальных значений и значений по умолчанию
    //при создании геометрии и материалов
    ExG4DetectorConstruction01();
    //Деструктор, вызывается при удалении экземпляра класса
    //Обычно используется для освобождения памяти инициализированных массивов внутри класса
    virtual ~ExG4DetectorConstruction01();
    //Объявление и создание детекторов и среды
    virtual G4VPhysicalVolume* Construct();
    //Установка чувствительного объема. Когда частица в нем, тогда извлекается
    //вся информация о треке и параметрах частицы на каждом шаге моделирования
    //Не работает в многопоточном режиме
    virtual void ConstructSDandField();
  protected:
};
#endif

И заголовочные файлы класса чувствительной области детектора, где снимаются параметры частиц ExG4DetectorSD.hh

Однако следует помнить, что таким образом можно снимать параметры только для проекта не использующего распаралеливание.

#include<G4VSensitiveDetector.hh>
#ifndef ExG4DetectorSD_h
#define ExG4DetectorSD_h 1
   class G4Step;
   class G4TouchableHistory;
   /// Класс определения чувствительной области детектора
   class ExG4DetectorSD: public G4VSensitiveDetector
     {
     private:
       //Создадим гистограмму в которую запишем распределение
       //энергии протонов
       //Число бинов (интервалов в гистограмме)
       static const int NOBINS = 1000;
       //Максимальная энергия в гистограмме
       const double HIST_MAX;
       //Минимальная энергия в гистограмме
       const double HIST_MIN;
       //Объявляем саму гистограмму
       int histogram[NOBINS];
       //Постоим также угол, на который рассеялся протон
       int histogram_angle[NOBINS];
     public:
        //Контструктор, в нем обнуляем гистограммы
        ExG4DetectorSD(G4String name);
        //Декструктор, в нем выведем гистограммы в файл
        //Вывод данных в файл лучше делать здесь чем в ProcessHits, так как
        //вызов деструктора происходит в конце работы программы,
        //а если записывать в процессе моделирования, то значительное
        //время будет тратится на ожидание записи в файл. А это относительно
        //медленная процедура и занимает много времени и в результате
        //моделирование будет занимать больше времени чем нужно.
        ~ExG4DetectorSD();
        //Когда частица попадает в этот чувствительный объем, тогда на каждом
        //её шаге моделирования вызывается эта функция.
        //В ней мы получаем и собираем информацию о состоянии
        //частицы и ее треке
        G4bool ProcessHits(G4Step* step, G4TouchableHistory* history);
     };
#endif /* SENSITIVEDETECTOR */

А теперь файл реализации конструкции установки и детектора ExG4DetectorConstruction01.cpp

/// \file ExG4DetectorConstruction01.cpp
/// \brief Implementation of the ExG4DetectorConstruction01 class

#include "ExG4DetectorConstruction01.hh"
#include "ExG4DetectorSD.hh"
#include "G4RunManager.hh"
#include "G4SDManager.hh"
// Менеджер предопределенных материалов
#include "G4NistManager.hh"
#include "G4Box.hh"
#include "G4LogicalVolume.hh"
#include "G4PVPlacement.hh"
// Подключаем систему едениц измерения, что бы пользоваться еденицами cm, MeV.
#include "G4SystemOfUnits.hh"

// Конструктор класса объявления материалов и геометрии всей моделируемой системы
ExG4DetectorConstruction01::ExG4DetectorConstruction01()
: G4VUserDetectorConstruction()
{ }

// Деструктор
ExG4DetectorConstruction01::~ExG4DetectorConstruction01()
{ }

// Функция определения материалов и геометрии всей системы,
// должна возвращать физический объем - ссылку на экземпляр класса G4VPhysicalVolume
// Геометрию проектировать будем следующую: пучок протонов попадает на мишень
// вольфрамовый лист толщиной 1 мм, а за мишенью поставим детектор
// таких же размеров, он будет регистрировать что в него попало.
G4VPhysicalVolume* ExG4DetectorConstruction01::Construct()
{
    // Для простоты используем предопределенные в Geant4 материалы
    // Так объявляется менеджер, из которого можно извлечь
    // ранее предопределенные материалы
  G4NistManager* nist = G4NistManager::Instance();

  // Определяем размеры детектора
  G4double det_sizeXY = 25*cm, det_sizeZ = 0.15*cm;

  // Материал детектора, здесь выбираем вольфрам
  G4Material* det_mat = nist->FindOrBuildMaterial("G4_W");

  // Опция для включения/выключения проверки перекрытия объемов
  G4bool checkOverlaps = true;

  // World
  // Объем мира, самый большой объем, включающий остальные, аналог экспериментального
  // зала
  G4double world_sizeXY = 30*cm;//Размер по x и y здесь будут одинаковы - ширина и высота
  G4double world_sizeZ  = 20*cm;//Размер по z - толщина
  // Выбор материала для мира из предопределенных в Geant4, для зала берем воздух
  G4Material* world_mat = nist->FindOrBuildMaterial("G4_AIR");

  // Создание объема для мира установки (экспериментального зала),
  // определяется сама форма объема,
  // берем параллелепипед, это просто геометрическая фигура
  G4Box* solidWorld =
  new G4Box("World",                       //its name, название объема
       0.5*world_sizeXY, 0.5*world_sizeXY, 0.5*world_sizeZ);     //its size, его размеры
  // указываются половины размеров высоты, ширины и глубины

  // Логический объем, здесь подключается материал, из которого сделан объем
  G4LogicalVolume* logicWorld =
    new G4LogicalVolume(solidWorld,          //its solid, геометрический объем, объявлен выше
                        world_mat,           //its material, материал объема
                        "World");            //its name, название логического объема
  //совпадает с названием объема, но
  //для Geant4 это разные объекты
  //геометрический объем и логический объем

  //Физический объем, а теперь наш логический объем помещаем в "реальный" мир
  G4VPhysicalVolume* physWorld =
    new G4PVPlacement(0,                     //no rotation, нет вращения
                      G4ThreeVector(),       //at (0,0,0), расположение в центре (0,0,0)
                      logicWorld,            //its logical volume, логический объем этого физического
                      "World",               //its name, название физического объема
                      0,                     //its mother  volume, материнский объем, этот самый первый, поэтому 0
                      false,                 //no boolean operation, без логических (булевых) операций
                      0,                     //copy number, номер копии
                      checkOverlaps);        //overlaps checking, флаг проверки перекрытия объемов

  // Детектор, для него также используем параллелепипед
  G4Box* solidDet =
    new G4Box("Detector",                    //its name, имя
        0.5*det_sizeXY, 0.5*det_sizeXY, 0.5*det_sizeZ); //its size, размеры

  //Логический объем
  G4LogicalVolume* logicDet =
    new G4LogicalVolume(solidDet,            //its solid, объем
                        det_mat,             //its material, указываем материал детектора
                        "Detector");         //its name, его имя

  //Физический объем детектора
  new G4PVPlacement(0,                       //no rotation, так же без вращения
                    G4ThreeVector(0,0,5*cm), //at (0,0,5 см) положение центра детектора, он смещен на 5 см от центра объема World
                    logicDet,                //its logical volume, подключаем логический объем
                    "Detector",              //its name, имя физического объема
                    logicWorld,              //its mother  volume, родительский логический объем, помещаем в world!
                    false,                   //no boolean operation, без булевых операций
                    0,                       //copy number, номер копии
                    checkOverlaps);          //overlaps checking, флаг проверки перекрытия объемов

  // Для мишени, на которую будет падать пучек, возьмем геометрические размеры как
  // у детектора, параллелепипед - лист вольфрама.
  //Логический объем
  G4LogicalVolume* logicTar =
    new G4LogicalVolume(solidDet,            //its solid, объем
                        det_mat,             //its material, указываем материал мишени
                        "Target");         //its name, его имя

  //Физический объем мишени
  new G4PVPlacement(0,                       //no rotation, так же без вращения
                    G4ThreeVector(0,0,-5*cm),//at (0,0,-5 см) положение центра мишени в другую сторону от детектора, смещена на 5 см от центра объема World
                    logicTar,                //its logical volume, подключаем логический объем
                    "Target",                //its name, имя физического объема
                    logicWorld,              //its mother  volume, родительский логический объем!
                    false,                   //no boolean operation, без булевых операций
                    0,                       //copy number, номер копии
                    checkOverlaps);
  //Всегда возвращает физический объем
  return physWorld;
}

void ExG4DetectorConstruction01::ConstructSDandField()
{
  // Объявление чувствительной области детектора, в которой можно получить подробную
  // информацию о состоянии и движении частицы, не работает в многопоточной версии
  // Назовем чувствительную область DetectorSD
  G4String trackerChamberSDname = "DetectorSD";
  // Создаем экземпляр чувствительной области
  ExG4DetectorSD* aTrackerSD = new ExG4DetectorSD(trackerChamberSDname);
  // Передаем указатель менеджеру
  G4SDManager::GetSDMpointer()->AddNewDetector(aTrackerSD);
  // Добавляем чувствительный объем ко всем логическим областям с
  // именем Detector
  SetSensitiveDetector("Detector", aTrackerSD, true);
}

И файл реализации чувствительного объема ExG4DetectorSD.cpp

#include<G4Step.hh>
// Для работы с файлами
#include<fstream>
// Для работы с покотками ввода и вывода
#include<iostream>
// Подключаем систему едениц измерения, что бы пользоваться еденицами cm, MeV.
#include "G4SystemOfUnits.hh"
#include "G4ThreeVector.hh"
#include "ExG4DetectorSD.hh"
// Используем пространство имен std, что бы не писать много где std::
using namespace std;
// Конструктор чувствительной области, по умолчанию инициализируем нижнюю и верхнюю
// границы гистограммы в 0 и 50 МэВ
ExG4DetectorSD::ExG4DetectorSD(G4String name): G4VSensitiveDetector(name),
                             HIST_MAX(50*MeV),// Инициализация верхней границы
                             HIST_MIN(0 *MeV)// Инициализация нижней границы
{
  // Обнуляем гистограммы
  for(int i = 0; i<NOBINS; i++){
    histogram[i] = 0;
    histogram_angle[i] = 0;
  }
}
//Вызывается на каждом шаге моделирования частицы, когда она попадает в этот чувствительный объем
G4bool ExG4DetectorSD::ProcessHits(G4Step* step, G4TouchableHistory* history)
   {
    // Получаем кинетическую энергии частицы с предыдущего шага, т.е. начальную
    // кинетическую энегрию перед текущим шагом
    double energy = step->GetPreStepPoint()->GetKineticEnergy();
    // Вычисляем ширину бина (интерва) гистограммы
    double bin_width = (HIST_MAX - HIST_MIN) / NOBINS;
    // Если имя частицы протон (proton), тогда заполняем гистограммы
    if(step->GetTrack()->GetDefinition()->GetParticleName() == "proton" ){
         // Определяем индекс (номер) бина гистограммы энергии
         int index = int(floor((energy-HIST_MIN)/bin_width));
         // Добавляем +1 в соответствующий бин
         if(index >= 0 && index < NOBINS)
           histogram[index]++;
         // Далее заполняем гистограмму углового распределения
         // Получаем вектор направления частицы
         G4ThreeVector ang = step->GetPreStepPoint()->GetMomentumDirection();
         // Задаем единичный вектор в направлении оси OZ
         G4ThreeVector *centerVector = new G4ThreeVector(0, 0, 1);
         // Применяем фунцию класса G4ThreeVector - находим угол относительно
         // вектора centerVector
         double angle=ang.angle(*centerVector);
         // Определяем ширину бина в гистограмме углового распределения.
         // Так как у нас измеряются углы между векторами, то максимальный
         // угол равен пи 3.14, минимальный 0
         double bin_width_ang = (3.14) / NOBINS;
         // Получаем номер бина
         index = int(floor((angle)/bin_width_ang));
         // Заполняем гистограмму
         if(index >= 0 && index < NOBINS)
             histogram_angle[index]++;
}
     // Так как мы хотим только измерить параметры частиц после прохождения
     // мишени и не интересуемся дальнейшей их судьбой в детекторе, то их убиваем -
     // устанавливаем статус остановлено и уничтожено (fStopAndKill)
     step->GetTrack()->SetTrackStatus(fStopAndKill);
     return true;
   }
ExG4DetectorSD::~ExG4DetectorSD()
{
    // В деструкторе выводим гистограммы в файлы
    // Открываем файл (существующий файл полностью перезаписывается)
    std::ofstream file("spectrum.dat");
    // Вычисляем ширину бина
    double bin_width = (HIST_MAX - HIST_MIN) / NOBINS;
    // Выводим гистограмму
    for(int i = 0; i<NOBINS; i++)
    {
        // Вычисляем энергию
        double energy = i*bin_width + HIST_MIN;
        // Выводим в файл
        file << std::setw(15) << energy/MeV << " "
             << std::setw(15) << histogram[i] << std::endl;
    }
    // Закрываем файл
    file.close();
    // Открываем файл для вывода гистограммы углового распределения
    file.open("angle.dat");
    // Вычисляем ширину бина
    bin_width = (3.14) / NOBINS;
    // Выводим гистограмму
    for(int i = 0; i<NOBINS; i++)
    {
        // Вычисляем угол
        double angle = i*bin_width;
        // Выводим в файл
        file << std::setw(15) << angle << " "
             << std::setw(15) << histogram_angle[i] << std::endl;
    }
    // Закрываем файл
    file.close();
}

Классы инициализации проекта

Далее идут важные и обязательные файлы инициализации проекта ExG4ActionInitialization01.hh

/// \file ExG4ActionInitialization01.hh
/// \brief Definition of the ExG4ActionInitialization01 class

#ifndef ExG4ActionInitialization01_h
#define ExG4ActionInitialization01_h 1

#include "G4VUserActionInitialization.hh"

/// Обязательный класс, который должен быть объявлен в проекте Geant4
/// Имя класса может быть другим, и он должен наследоваться от
/// класса G4VUserActionInitialization

class ExG4ActionInitialization01 : public G4VUserActionInitialization
{
  public:
    ExG4ActionInitialization01();//Конструктор
    virtual ~ExG4ActionInitialization01();//Деструктор
    virtual void Build() const;//Создание источника первичных частиц

};

#endif

И файл класса задания начальных частиц ExG4PrimaryGeneratorAction01.hh

/// \file ExG4PrimaryGeneratorAction01.hh
/// \brief Definition of the ExG4PrimaryGeneratorAction01 class

#ifndef B1PrimaryGeneratorAction_h
#define B1PrimaryGeneratorAction_h 1

#include "G4VUserPrimaryGeneratorAction.hh"
#include "G4ParticleGun.hh"
#include "globals.hh"

class G4ParticleGun;
class G4Event;
class G4Box;

/// Класс определения источника первичных частиц
class ExG4PrimaryGeneratorAction01 : public G4VUserPrimaryGeneratorAction
{
public:
  ///
  /// \brief ExG4PrimaryGeneratorAction01 Конструктор
  ///
  ExG4PrimaryGeneratorAction01();
  ///
  /// \brief ~ExG4PrimaryGeneratorAction01 Деструктор
  ///
  virtual ~ExG4PrimaryGeneratorAction01();
  ///
  /// \brief GeneratePrimaries Метод из базового класса, задает параметры источника начальных частиц
  ///
  virtual void GeneratePrimaries(G4Event*);
  ///
  /// \brief GetParticleGun Метод для доступа к источнику частиц (пушке частиц ;) )
  /// \return Указатель на источник частиц
  ///
  const G4ParticleGun* GetParticleGun() const { return fParticleGun; }

private:
  G4ParticleGun*  fParticleGun; //указатель на источник частиц
  // Временная переменная объема
  G4Box* fEnvelopeBox;
};
#endif

Файл реализации инициализации ExG4ActionInitialization01.cpp

/// \file ExG4ActionInitialization01.cpp
/// \brief Implementation of the ExG4ActionInitialization01 class
#include "ExG4ActionInitialization01.hh"
#include "ExG4PrimaryGeneratorAction01.hh"//Подключаем обязательный класс
//в котором описывается источник начальных частиц

/// Обязательный класс, который должен быть объявлен в проекте Geant4
/// Имя класса может быть другим, и он должен наследоваться от
/// класса G4VUserActionInitialization

/// Конструктор
ExG4ActionInitialization01::ExG4ActionInitialization01()
 : G4VUserActionInitialization()
{}
//Деструктор, ничего не объявляли, поэтому оставим пустым
ExG4ActionInitialization01::~ExG4ActionInitialization01()
{}
//Создание источника первичных частиц
void ExG4ActionInitialization01::Build() const
{
SetUserAction(new ExG4PrimaryGeneratorAction01);//Задается источник первичных частиц
// через обязательный класс ExG4PrimaryGeneratorAction01
}

И файл реализации задания начального источника частиц ExG4PrimaryGeneratorAction01.cpp

/// \file ExG4PrimaryGeneratorAction01.cpp
/// \brief Implementation of the ExG4PrimaryGeneratorAction01 class

// Подключаем необходимые заголовочные файлы
#include "ExG4PrimaryGeneratorAction01.hh"
// Подключаем необходимы заголовочные файлы
#include "G4LogicalVolumeStore.hh"
#include "G4LogicalVolume.hh"
#include "G4Box.hh"
#include "G4RunManager.hh"
// Источник частиц
#include "G4ParticleGun.hh"
// Таблица указателей на данные частиц
#include "G4ParticleTable.hh"
// Данные частиц
#include "G4ParticleDefinition.hh"
// Подключаем систему едениц измерения, что бы пользоваться еденицами cm, MeV.
#include "G4SystemOfUnits.hh"
// Генератор случайных чисел
#include "Randomize.hh"

///
/// \brief ExG4PrimaryGeneratorAction01::ExG4PrimaryGeneratorAction01 Формирование начального пучка.
/// Класс, в котором описывается положение, тип, энергия, направление вылета
/// и распределение начальных частиц
///
ExG4PrimaryGeneratorAction01::ExG4PrimaryGeneratorAction01()
: G4VUserPrimaryGeneratorAction(),
  fParticleGun(0),
  fEnvelopeBox(0)
{
  // Данные, которые здесь задаются после могут быть изменены через команды в mac файле,
  // или в консольном режиме.
  // По умолчанию у источника частиц поставим 1 частицу
  G4int n_particle = 1;
  fParticleGun  = new G4ParticleGun(n_particle);

  // Получаем встроеную в Geant4 таблицу частиц
  G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
  G4String particleName;
  // Ищем частицу, в нашем случае протон
  G4ParticleDefinition* particle
    = particleTable->FindParticle(particleName="proton");
  // Устанавливаем полученную частицу в качестве испускаемого типа начальных частиц в источнике
  fParticleGun->SetParticleDefinition(particle);
  // Устанавливаем направление движение частицы по (x,y,z)
  // Здесь устанавливается направление вдоль оси Z
  fParticleGun->SetParticleMomentumDirection(G4ThreeVector(0.,0.,1.));
  // Установка начальной энергии испускаемых частиц, 50 МэВ
  fParticleGun->SetParticleEnergy(50*MeV);
}

/// Деструктор, удаляем созданный в конструкторе экземпляр класса источника G4ParticleGun
/// для очистки памяти
ExG4PrimaryGeneratorAction01::~ExG4PrimaryGeneratorAction01()
{
  // удаляем созданный в конструкторе экземпляр класса источника G4ParticleGun
  delete fParticleGun;
}

/// Эта функция вызывается в начале каждого события и генерирует начальные частицы.
/// Определенные здесь условия переписывают команды из файла mac и команды из
/// консольного режима.
void ExG4PrimaryGeneratorAction01::GeneratePrimaries(G4Event* anEvent)
{
  // Для избежания зависимости этого класса от класса DetectorConstruction,
  // мы получаем ссылку на объем детектора через класс G4LogicalVolumeStore

  G4double envSizeXY = 0;
  G4double envSizeZ = 0;
  // Проверяем или ссылка на fEnvelopeBox пустая
  if (!fEnvelopeBox)
  {
     // Если пустая, то получаем ссылку на объем детектора
    G4LogicalVolume* envLV
      = G4LogicalVolumeStore::GetInstance()->GetVolume("Detector");
    if ( envLV ) fEnvelopeBox = dynamic_cast<G4Box*>(envLV->GetSolid());
  }
  // Получаем размеры объема, предполагается что, стороны по x и y одинаковы
  if ( fEnvelopeBox ) {
    envSizeXY = fEnvelopeBox->GetXHalfLength()*2.;
    envSizeZ = fEnvelopeBox->GetZHalfLength()*2.;
  }
  else  {//Если ссылка на fEnvelopeBox пустая, выдаем предупреждение
    G4ExceptionDescription msg;
    msg << "Envelope volume of box shape not found.\n";
    msg << "Perhaps you have changed geometry.\n";
    msg << "The gun will be place at the center.";
    G4Exception("B1PrimaryGeneratorAction::GeneratePrimaries()",
     "MyCode0002",JustWarning,msg);
  }
  // Задаем координаты где будет источник начальных частиц
  G4double x0 = 0;
  G4double y0 = 0;
  // Устанавливаем в положение -10 см, 0 означает цент мирового объема
  G4double z0 = -0.5 * 20*cm;
  // Команда устанавливает позицию источника начальных частиц
  fParticleGun->SetParticlePosition(G4ThreeVector(x0,y0,z0));
  // Генерируем первичное событие
  fParticleGun->GeneratePrimaryVertex(anEvent);
}

Make файл

Так как изменилось имя проекта, то соответственно немного изменился CMakeLists.txt

# $Id: CMakeLists.txt 2023-01-19 Виктор Гавриловец $
#CMakeList.txt, с его помощью собирается проект Geant4
#Как правило здесь настраивать ничего не надо

# Запуск проекта, проверка необходимой версии Cmake
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
# Название проекта
project(example2)

# Поиск пакетов Geant4, включение всех доступных интерфейсов пользователя
# (Qt, GAG, tcsh, csh) и драйверов визуализации по умолчанию.
# Можно использовать переменную окружения WITH_GEANT4_UIVIS, установить в OFF (выключено)
# через командную строку или с помощью ccmake/cmake-gui для сборки проекта только в
# пакетном режимом
#
option(WITH_GEANT4_UIVIS "Build example with Geant4 UI and Vis drivers" ON)
if(WITH_GEANT4_UIVIS)
  find_package(Geant4 REQUIRED ui_all vis_all)
else()
  find_package(Geant4 REQUIRED)
endif()

# Подключение директорий из Geant4
include(${Geant4_USE_FILE})
# Подключение директорий проекта
include_directories(${PROJECT_SOURCE_DIR}/include)

# Расположение искодного кода и заголовочных файлов этого проекта
# Заголовочные файлы подключаются, значит что они будут показаны в
# IDE - среде разработки
file(GLOB sources ${PROJECT_SOURCE_DIR}/src/*.cpp)
file(GLOB headers ${PROJECT_SOURCE_DIR}/include/*.hh)

# Иногда возникает проблема, что не находит файлы проекта
# это потому, что у файлов исходников могут быть другие расширения, например

# file(GLOB sources ${PROJECT_SOURCE_DIR}/src/*.cc)
# file(GLOB headers ${PROJECT_SOURCE_DIR}/include/*.h)

# Добавление исполняемого файла и линковка его с библиотеками Geant4
#
add_executable(example2 example2.cpp ${sources} ${headers})
target_link_libraries(example2 ${Geant4_LIBRARIES})

# Копирование скриптов в директорию сборки - туда, где мы собираем проект.
# Таким образом мы сможем запустить проект в директории сборки - текущей рабочей
# директории, что нужно например, при отладке.
#
set(EXAMPLE1_SCRIPTS
  run1.mac
  vis.mac
  )

foreach(_script ${EXAMPLE1_SCRIPTS})
  configure_file(
    ${PROJECT_SOURCE_DIR}/${_script}
    ${PROJECT_BINARY_DIR}/${_script}
    COPYONLY
    )
endforeach()

# Для внутеренего использования в Geant4, но не имеет влияния если проект
# компилируется для отдельного использования
#
add_custom_target(ex1 DEPENDS example2)

# Установка исполняемого файла в 'bin' директорию по пути
# установки CMAKE_INSTALL_PREFIX
#
install(TARGETS example2 DESTINATION bin)

Визуализация и консольный запуск

Для визуализации используется следующий файлvis.mac

# Macro file for the visualization setting in the initialization phase 
# of the project example 2 when running in interactive mode
#

# Use these open statements to open selected visualization
#
# Use this open statement to create an OpenGL view:
/vis/open OGL 600x600-0+0
#
# Use this open statement to create an OpenInventor view:
#/vis/open OI
#
# Use this open statement to create a .prim file suitable for
# viewing in DAWN:
#/vis/open DAWNFILE
#
# Use this open statement to create a .heprep file suitable for
# viewing in HepRApp:
#/vis/open HepRepFile
#
# Use this open statement to create a .wrl file suitable for
# viewing in a VRML viewer:
#/vis/open VRML2FILE
#
# Disable auto refresh and quieten vis messages whilst scene and
# trajectories are established:
/vis/viewer/set/autoRefresh false
/vis/verbose errors
#
# Draw geometry:
/vis/drawVolume
#
# Specify view angle:
/vis/viewer/set/viewpointVector -1 0 0
/vis/viewer/set/lightsVector -1 0 0
#
# Specify style (surface, wireframe, auxiliary edges,...)
/vis/viewer/set/style wireframe
/vis/viewer/set/auxiliaryEdge true
/vis/viewer/set/lineSegmentsPerCircle 100
#
# Draw smooth trajectories at end of event, showing trajectory points
# as markers 2 pixels wide:
/vis/scene/add/trajectories smooth
/vis/modeling/trajectories/create/drawByCharge
/vis/modeling/trajectories/drawByCharge-0/default/setDrawStepPts true
/vis/modeling/trajectories/drawByCharge-0/default/setStepPtsSize 2
# (if too many tracks cause core dump => /tracking/storeTrajectory 0)
#
# Draw hits at end of event:
#/vis/scene/add/hits
#
# To draw only gammas:
#/vis/filtering/trajectories/create/particleFilter
#/vis/filtering/trajectories/particleFilter-0/add gamma
#
# To invert the above, drawing all particles except gammas,
# keep the above two lines but also add:
#/vis/filtering/trajectories/particleFilter-0/invert true
#
# Many other options are available with /vis/modeling and /vis/filtering.
# For example, to select colour by particle ID:
#/vis/modeling/trajectories/create/drawByParticleID
#/vis/modeling/trajectories/drawByParticleID-0/default/setDrawStepPts true
# To select or override default colours (note: e+ is blue by default):
#/vis/modeling/trajectories/list
#/vis/modeling/trajectories/drawByParticleID-0/set e+ yellow
#
# To superimpose all of the events from a given run:
/vis/scene/endOfEventAction accumulate
#
# Decorations
# Name
#/vis/set/textColour green
#/vis/set/textLayout right
#/vis/scene/add/text2D 0.9 -.9 24 ! ! exampleB1
# or, if your system does not support right-adjustment
#/vis/scene/add/text2D 0 -.9 24 ! ! exampleB1
#/vis/set/textLayout    # Revert to normal (left adjusted) layout
#/vis/set/textColour    # Revert to default text colour (blue)
#
# Axes, scale, etc.
/vis/scene/add/scale   # Simple scale line
/vis/scene/add/axes    # Simple axes: x=red, y=green, z=blue.
/vis/scene/add/eventID # Drawn at end of event
#/vis/scene/add/date    # Date stamp
#/vis/scene/add/logo2D  # Simple logo
#/vis/scene/add/logo    # 3D logo
#
# Frame
#/vis/set/colour red
#/vis/set/lineWidth 2
#/vis/scene/add/frame   # Simple frame around the view
/vis/set/colour        # Revert to default colour (white)
/vis/set/lineWidth     # Revert to default line width (1.)
#
# Attach text to one edge of Shape1, with a small, fixed offset
#/vis/scene/add/text 0 6 -4 cm 18 4 4 Shape1
# Attach text to one corner of Shape2, with a small, fixed offset
#/vis/scene/add/text 6 7 10 cm 18 4 4 Shape2
#
# To get nice view
# Make the "World" box invisible
/vis/geometry/set/visibility World 0 true
/vis/geometry/set/colour World 0 0 0 1 .3
# "Envelope" is transparent blue to represent water
/vis/geometry/set/colour Detector 0 0 1 1 .5
/vis/viewer/set/style surface
/vis/viewer/set/hiddenMarker true
/vis/viewer/set/viewpointThetaPhi 120 150
#
# Re-establish auto refreshing and verbosity:
/vis/viewer/set/autoRefresh true
/vis/verbose warnings
#
# For file-based drivers, use this to create an empty detector view:
#/vis/viewer/flush

А для программы с использованием файла mac - run1.mac

# Macro file for example 2
# 
# Can be run in batch, without graphic
# or interactively: Idle> /control/execute run1.mac
#
# Change the default number of workers (in multi-threading mode)
#/run/numberOfWorkers 4
#
# Initialize kernel
/run/initialize
#
/control/verbose 1
/run/verbose 1
/event/verbose 0
#
# gamma 6 MeV to the direction (0.,0.,1.)
#
#/gun/particle gamma
#/gun/energy 6 MeV
#
#/run/beamOn 5
#
# proton 210 MeV to the direction (0.,0.,1.)
# Установка типа частиц, протонов
/gun/particle proton
# Установка энергии частиц, 50 МэВ
/gun/energy 50 MeV
# Установка уровня
/tracking/verbose 0
# Запуск частиц, 10 тысяч
/run/beamOn 10000

В других статья будут подробней разбираться команды отрисовки и запуска примеров Geant4.

Весь представленный код примера находится здесь.