How-to¶
Create detector geometry model¶
Модель детектора состоит из описания геометрии и материалов.
Задание материалов¶
Все материалы, учавствующие в моделировании, описаны в файле geometry/media.geo.
Формат файла описан здесь.
Создание геометрии¶
На данный момент симуляции возможны с двумя форматами геометрий: root и gdml.
Пример создания геометрии с помощью макроса будет приведен ниже. Все макросы, создающие геометрию, должны быть расположены в директроии /macro/geo/
и быть названы по шаблону create_det_geo_v1.C
. Файл с результирующей геометрие должен находиться в geometry
и называться по шаблону det.v1.geo.root.
В примере будет рассмотрен простой нейтронный детектор из 4 волокон квадратного сечения, расположенных вдоль пучка.
Инициализируаем глобальное смещение и поворот детектроа.
void create_det_geo_v1()
{
// Create a global translation
Float_t global_X = 0.;
Float_t global_Y = 0.;
Float_t global_Z = 0.;
//Create gloabal Rotation
TGeoRotation *fGlobalRotation = new TGeoRotation();
fGlobalRotation->RotateX(0.);
fGlobalRotation->RotateY(0.);
fGlobalRotation->RotateZ(0.);
// Create a zero rotation
TGeoRotation *fZeroRotation = new TGeoRotation();
fZeroRotation->RotateX(0.);
fZeroRotation->RotateY(0.);
fZeroRotation->RotateZ(0.);
Цепляем глобальные менеджер геометрии и читаем файл материалов.
TGeoManager* gGeoMan = NULL;
// ------- Load media from media file -----------------------------------
FairGeoLoader* geoLoad = new FairGeoLoader("TGeo","FairGeoLoader");
FairGeoInterface* geoFace = geoLoad->getGeoInterface();
TString geoPath = gSystem->Getenv("VMCWORKDIR");
TString medFile = geoPath + "/geometry/media.geo";
geoFace->setMediaFile(medFile);
geoFace->readMedia();
gGeoMan = gGeoManager;
// --------------------------------------------------------------------------
Задаемся путем где будет лежать файл результирующей геометрии.
// ------- Geometry file name (output) ----------------------------------
TString geoFileName = geoPath + "/geometry/det.v1.geo.root";
// --------------------------------------------------------------------------
Некоторый костыль. Превращение объекта материала FairRoot в объект материала Root.
// ----------------- Get and create the required media -----------------
FairGeoMedia* geoMedia = geoFace->getMedia();
FairGeoBuilder* geoBuild = geoLoad->getGeoBuilder();
FairGeoMedium* mBC408 = geoMedia->getMedium("BC408");
if ( ! mBC408 ) Fatal("Main", "FairMedium BC408 not found");
geoBuild->createMedium(mBC408);
TGeoMedium* pMed37 = gGeoMan->GetMedium("BC408");
if ( ! pMed37 ) Fatal("Main", "Medium BC408 not found");
FairGeoMedium* vacuum = geoMedia->getMedium("vacuum");
if ( ! vacuum ) Fatal("Main", "FairMedium vacuum not found");
geoBuild->createMedium(vacuum);
TGeoMedium* pMed0 = gGeoMan->GetMedium("vacuum");
if ( ! pMed0 ) Fatal("Main", "Medium vacuum not found");
// --------------------------------------------------------------------------
Создаем объемы геометрии. Верхний объем всегда Assembly и называется TOP. Объем детектра тоже Assembly.
//------------------------- VOLUMES -----------------------------------------
// -------------- Create geometry and top volume -------------------------
gGeoMan = (TGeoManager*)gROOT->FindObject("FAIRGeom");
gGeoMan->SetName("DETgeom");
TGeoVolume* top = new TGeoVolumeAssembly("TOP");
gGeoMan->SetTopVolume(top);
TGeoVolume* det = new TGeoVolumeAssembly("det");
// --------------------------------------------------------------------------
//------------------ BC408 fiber -----------------------------------------
Double_t fiber_X = 0.6; //cm
Double_t fiber_Y = 0.6; //cm
Double_t fiber_Z = 100.; //cm
fiber_X /= 2.;
fiber_Y /= 2.;
fiber_Z /= 2.;
TGeoVolume *fiber = gGeoManager->MakeBox("fiber", pMed37, fiber_X, fiber_Y, fiber_Z);
Создаем структуру. Указываем как объёмы вложены друг в друга.
//------------------ STRUCTURE -----------------------------------------
//------------------ Add fibers to det -----------------------------
Int_t fibers_in_det_X_Nb = 2;
Int_t fibers_in_det_Y_Nb = 2;
Double_t det_X = fiber_X * fibers_in_det_X_Nb;
Double_t det_Y = fiber_Y * fibers_in_det_Y_Nb;
Double_t det_Z = fiber_Z;
Int_t i_fiber = 0;
for (Int_t i_Y_fiber = 0; i_Y_fiber < fibers_in_det_Y_Nb; i_Y_fiber++){
for (Int_t i_X_fiber = 0; i_X_fiber < fibers_in_det_X_Nb; i_X_fiber++){
Double_t fiber_in_det_X_trans = det_X - fiber_X*2*(i_X_fiber)-fiber_X;
Double_t fiber_in_det_Y_trans = det_Y - fiber_Y*2*(i_Y_fiber)-fiber_Y;
Double_t fiber_in_det_Z_trans = 0.;
det->AddNode( fiber, i_fiber, new TGeoCombiTrans(fiber_in_det_X_trans,
fiber_in_det_Y_trans,
fiber_in_det_Z_trans,
fZeroRotation));
i_fiber++;
}
}
top->AddNode(det, 1, new TGeoCombiTrans(global_X,global_Y,global_Z,fGlobalRotation));
Проверяем ошибки в геометрии. Записываем ее в файл.
// --------------- Finish -----------------------------------------------
gGeoMan->CloseGeometry();
gGeoMan->CheckOverlaps(0.001);
gGeoMan->PrintOverlaps();
gGeoMan->Test();
TFile* geoFile = new TFile(geoFileName, "RECREATE");
top->Write();
geoFile->Close();
// --------------------------------------------------------------------------
}
Create detector class library¶
Для каждого детектора создается его библиотека классов, в которую входят:
- Классы симуляций детектора, унаследованные от
ERDetector
;- Классы диджитизации, реконструкции и анализа, унаследованные от
FairTask
;- Классы, описывающие множества параметров детектора, унаследованные от
FairParGenericSet
;- Классы данных детектора, унаследованные напрямую от TObject или от
FairMCPoint
,FairHit
, если это поинт или хит соотвественно.
Классы каждой библиотеки хранятся в директории первого уровня:
cd ~/expertroot
mkdir det
cd det
Сборка библиотеки описывается файлом CMakeLists.txt, имеющим следующую структуру:
# Create a library called "libDet" which includes the source files given in
# the array .
# The extension is already found. Any number of sources could be listed here.
set(INCLUDE_DIRECTORIES
${BASE_INCLUDE_DIRECTORIES}
${ROOT_INCLUDE_DIR}
${Boost_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/det
${CMAKE_SOURCE_DIR}/base
)
include_directories( ${INCLUDE_DIRECTORIES})
set(LINK_DIRECTORIES
${BASE_LINK_DIRECTORIES}
${FAIRROOT_LIBRARY_DIR}
)
link_directories( ${LINK_DIRECTORIES})
set(SRCS
ERDet.cxx
ERDetPoint.cxx
)
# fill list of header files from list of source files
# by exchanging the file extension
CHANGE_FILE_EXTENSION(*.cxx *.h HEADERS "${SRCS}")
Set(LINKDEF ERDetLinkDef.h)
Set(LIBRARY_NAME Det)
Set(DEPENDENCIES ERBase ERData Base Core Geom)
GENERATE_LIBRARY()
В переменную INCLUDE_DIRECTORIES
следует добавить все директории с необходимыми файлами включений. Директории внешних пакетов: ${BASE_INCLUDE_DIRECTORIES}, ${ROOT_INCLUDE_DIR}, ${Boost_INCLUDE_DIRS}
стоит добавлять всегда. В переменную LINK_DIRECTORIES сдедует добавить директории библиотек. Переменные ${BASE_LINK_DIRECTORIES}, ${FAIRROOT_LIBRARY_DIR}
стоит добавлять всегда. В переменной SRCS
следует перечислить все *.cxx
исходные файлы библиотеки. В переменной LINKDEF
служебный файл для интерпретатора cling
. В переменной DEPENDENCIES
указать какие именно библиотеки необходимо подключить. Функция GENERATE_LIBRARY()
автоматически сформирует библиотеку добавив операции записи в root файл объектам данным, и добавив необходимые объекты - словари, с помощью которых интерпретатор вызывает нужные методы классов из библиотек.
Служебный файл ERDetLinkDef.h
будет иметь следующую структуру:
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ class ERDet;
#pragma link C++ class ERDetPoint+;
Если мы хотим указать, что объект данного класса будет писаться в root файл, необходимо добавить +
после названия класса.
Минимальный набор файлов библиотеки классов детектора для симуляции:
CMakeLists.txt
ERDetLinkDef.h
ERDet.h
ERDet.cxx
ERDetPoint.h
ERDetPoint.cxx
Чтобы добавить библиотеку в процесс общей сборки expertroot нужно добавить строку:
add_subdirectory (det)
в файл ~/expertroot/CMakeLists.txt
туда где вызываюстя подобные команды (на данный момент после add_subdirectory (beamtime)
).
При создании новых классов не забывайте вносить их в CMakeLists.txt
и в LinkDef.h
файлы.
Create detector simulation¶
Для создания симуляции детектора необходимо:
- Создать геометрию детектора
- Создать директорию и служебные файлы CMakeLists.txt и LinkDef.h для организации библиотеки классов детектора
- Создать класс симуляции, определяющий правила по которым информация из транспорта будет записыавться в поинты
- Создать класс поинта
Класс детектора для симуляции¶
Процедура симуляции детектора заключается в записи данных из процедуры транспорта частиц через чувствительные (Sensitive) объёмы детектора в выходной файл root файл симуляции. Главным атрибутом этих данных является потеря энергии частици (energy loss - eloss), так как она является источником для дальнейшего моделирования цифрового отклика детектора. Традиционно запись информации происходит в коллекцию объектов, называемых поинтами (Point). Поинт - прямолинейный отрезок трека в активном объёме. Поинт создается набором шагов транспорта от рождения или входа трек в чувствительный объём до выхода или конца трека. На запись поинтов в файл обычно устанавливают порог на eloss.
Для создания симуляции детектора необходимо создать класс - наследник класса ERDetector
и написать реализации методов:
- ProcessHits - вызывается на каждом шаге транспорт внутри активного объёма детектора. В данном методе заложена процедура записи данных из объекта Virtual Monte Carlo (TVirtualMC - https://root.cern.ch/doc/master/classTVirtualMC.html) в коллекции выходных данных (обычно поинтов, но возможно и другое).
- BeginEvent - вызывается в начале события
- EndOfEvent - вызывается в конце события
- Register - для связывания объектов и веток во выходном файле. Вызывается при run->Init().
- GetCollection - возвращает коллекцию поинтов
- Print - для вывода информации о событии
- Reset - для обнуления всех коллекций и объектов между событиями
- CopyClones - Copies the hit collection with a given track index offset
- Initialize - инициализация объекта. Вызывается при run->Init().
- CheckIfSensitive - установка чувствительных объёмов в геометрии. Вызывается при run->Init(). Рекурсивно опрашивает для всех объёмов в геометрии.
Типовая структура ERDet.h файла класса выглядит так:
// -------------------------------------------------------------------------
// ----- ERDet header file -----
// ----- Created data by developer name -----
// -------------------------------------------------------------------------
#ifndef ERDet_H
#define ERDet_H
Указываем имя разработчика и дату начала разработки. Также используем стандартную конструкцию, чтобы избежать многократного включения в другие файлы.
#include "ERDetector.h"
#include "ERDetPoint.h"
#include "TLorentzVector.h"
#include "TClonesArray.h"
class ERDet : public ERDetector
{
Объявляем класс ERDet
, унаследованный от ERDetector
. Объявляем класс поинта; его реализация будет показана далее.
public:
/** Default constructor **/
ERDet();
/** Standard constructor.
*@param name ERDet ERDet name
*@param active sensitivity flag
*@param verbose Verbosity level. 1 - only standart logs, 2 - Print points after each event, 3 - GEANT Step information
**/
ERDet(const char* name, Bool_t active, Int_t verbose);
/** Destructor **/
virtual ~ERDet();
/** Virtual method ProcessHits
**
** Defines the action to be taken when a step is inside the
** active volume. Creates a ERDetPoint and adds it to the
** collection.
*@param vol Pointer to the active volume
**/
virtual Bool_t ProcessHits(FairVolume* vol = 0);
/** Virtual method BeginEvent
**
**/
virtual void BeginEvent();
/** Virtual method EndOfEvent
**
** If verbosity level is set, print point collection at the
** end of the event.
**/
virtual void EndOfEvent();
/** Virtual method Register
**
** Registers the point collection in the ROOT manager.
**/
virtual void Register();
/** Accessor to the point collection **/
virtual TClonesArray* GetCollection(Int_t iColl) const;
/** Virtual method Print
**
** Screen output of hit collection.
**/
virtual void Print(Option_t *option="") const;
/** Virtual method Reset
**
** Clears the point collection
**/
virtual void Reset();
/** Virtual method CopyClones
**
** Copies the hit collection with a given track index offset
*@param cl1 Origin
*@param cl2 Target
*@param offset Index offset
**/
virtual void CopyClones(TClonesArray* cl1, TClonesArray* cl2,
Int_t offset);
/** Virtaul method Initialize
**
** Initialize ERDet data
**/
virtual void Initialize();
/** Virtaul method CheckIfSensitive
**Check whether a volume is sensitive.
** @param(name) Volume name
** @value kTRUE if volume is sensitive, else kFALSE
**
** The decision is based on the volume name.
**/
virtual Bool_t CheckIfSensitive(std::string name);
Объявляем публичные методы, описанные выше.
private:
TClonesArray* fDetPoints; //! The point collection
Int_t fEventID; //! event index
Int_t fTrackID; //! track index
Int_t fMot0TrackID; //! mother track index
Int_t fPID; //! particle PDG
TLorentzVector fPosIn, fPosOut; //! position
TLorentzVector fMomIn, fMomOut; //! momentum
Double32_t fTime; //! time
Double32_t fLength; //! length
Double32_t fELoss; //! energy loss
private:
/** Private method AddPoint
**
** Adds a ERDetPoint to the Point Collection
**/
ERDetPoint* AddPoint(Int_t eventID, Int_t trackID,
Int_t mot0trackID,
Int_t pid,
TVector3 posIn,
TVector3 pos_out, TVector3 momIn,
TVector3 momOut, Double_t time,
Double_t length, Double_t eLoss);
ClassDef(ERDet,1);
};
#endif
Объявляем fDetPoints
- коллекцию поинтов, метод для добавления поинтов в коллекцию. Объявляем набор переменнных, хранящих текущее состояние поинта внутри активного объема детектора. Используем мароподстановку ClassDef(ERDet,1); для добавления функциональности Root объекта.
Типовые реализации методов приведены далее. Их необходимо добавить в файл ERDet.cxx.
#include "ERDet.h"
#include "TVirtualMC.h"
#include "TParticle.h"
// ----- Default constructor -------------------------------------------
ERDet::ERDet() :
ERDetector("ERDet", kTRUE),
fDetPoints(NULL)
{
fDetPoints = new TClonesArray("ERDetPoint");
//Это нужно сделать для того, чтобы геометрия в симуляции автоматом писалась в файл runtime db
flGeoPar = new TList();
flGeoPar->SetName( GetName());
fVerboseLevel = 1;
}
// -------------------------------------------------------------------------
// ----- Standard constructor ------------------------------------------
ERDet::ERDet(const char* name, Bool_t active, Int_t verbose)
: ERDetector(name, active,1),
fDetPoints(NULL)
{
fDetPoints = new TClonesArray("ERDetPoint");
//Это нужно сделать для того, чтобы геометрия в симуляции автоматом писалась в файл runtime db
flGeoPar = new TList();
flGeoPar->SetName( GetName());
fVerboseLevel = verbose;
}
// -------------------------------------------------------------------------
Реализуем конструкторы класса. Конструкцией : FairDetector(...)
передаем параметры конструктор FairDetector
от которого отнаследован ERDetector
.
Важное требование FairRoot
и Root
- все указатели должны быть инициализированы в констукторе объекта. Поэтому в список инициализации добавлено fDetPoints(NULL)
. Необходимо также инициализировать список геометрических параметров, объявленный в FairDetector
: flGeoPar = new TList();flGeoPar->SetName( GetName());
.
// -------------------------------------------------------------------------
ERDet::~ERDet() {
if (fDetPoints) {
fDetPoints->Delete();
delete fDetPoints;
}
}
В деструкторе очищаем коллекцию поинтов (вызовется деструктор поинта для каждого) и удаляем саму коллекцию.
void ERDet::Initialize()
{
FairDetector::Initialize();
}
В инициализации просто вызываем метод инициализации из FairDetector
.
Остальные методы кроме ProcessHits приведены без комментариев.
void ERDet::BeginEvent() {
}
// -------------------------------------------------------------------------
void ERDet::EndOfEvent() {
if (fVerboseLevel > 0)
Print();
Reset();
}
// -------------------------------------------------------------------------
void ERDet::Register() {
FairRootManager* ioman = FairRootManager::Instance();
if (!ioman)
Fatal("Init", "IO manager is not set");
ioman->Register("DetPoint","Det", fDetPoints, kTRUE);
}
// ----------------------------------------------------------------------------
TClonesArray* ERDet::GetCollection(Int_t iColl) const {
if (iColl == 0)
return fDetPoints;
else
return NULL;
}
// ----------------------------------------------------------------------------
// ----- Public method Print ----------------------------------------------
void ERDet::Print(Option_t *option) const
{
for (Int_t iPoint = 0; iPoint < fDetPoints->GetEntriesFast(); iPoint++){
ERDetPoint* point = (ERDetPoint*)fDetPoints->At(iPoint);
point->Print();
}
}
// ----------------------------------------------------------------------------
void ERDet::Reset() {
fDetPoints->Clear();
}
// ----------------------------------------------------------------------------
// ----- Public method CopyClones -----------------------------------------
void ERDet::CopyClones(TClonesArray* cl1, TClonesArray* cl2, Int_t offset) {
Int_t nEntries = cl1->GetEntriesFast();
std::cout << "Det: " << nEntries << " entries to add" << std::endl;
TClonesArray& clref = *cl2;
ERDetPoint* oldpoint = NULL;
for (Int_t i=0; i<nEntries; i++) {
oldpoint = (ERDetPoint*) cl1->At(i);
Int_t index = oldpoint->GetTrackID() + offset;
oldpoint->SetTrackID(index);
new (clref[cl2->GetEntriesFast()]) ERDetPoint(*oldpoint);
}
std::cout << "Det: " << cl2->GetEntriesFast() << " merged entries" << std::endl;
}
// ----------------------------------------------------------------------------
ERDetPoint* ERDet::AddPoint(Int_t eventID, Int_t trackID,
Int_t mot0trackID,
Int_t pid,
TVector3 posIn,
TVector3 posOut, TVector3 momIn,
TVector3 momOut, Double_t time,
Double_t length, Double_t eLoss){
TClonesArray& clref = *fDetPoints;
Int_t size = clref.GetEntriesFast();
return new(clref[size]) ERDetPoint(eventID, trackID, mot0trackID,pid,posIn,posOut,
momIn,momOut,time,length,eLoss);
}
// ----------------------------------------------------------------------------
Bool_t ERDet::CheckIfSensitive(std::string name)
{
TString volName = name;
if(volName.Contains("fiber")) {
return kTRUE;
}
return kFALSE;
}
// ----------------------------------------------------------------------------
ClassImp(ERDet)
Макроподстановка ClassImp(ERDet)
необходима для добавления реализации служебных методов.
Метод ProcessHits¶
Bool_t ERDet::ProcessHits(FairVolume* vol) {
Инициализируем переменные, описывающие текущее состояние поинтов, информацией из объекта gMC
.
if ( gMC->IsTrackEntering() ) { // Return true if this is the first step of the track in the current volume
fELoss = 0.;
fEventID = gMC->CurrentEvent();
gMC->TrackPosition(fPosIn);
gMC->TrackMomentum(fMomIn);
fTrackID = gMC->GetStack()->GetCurrentTrackNumber();
fTime = gMC->TrackTime() * 1.0e09; // Return the current time of flight of the track being transported
fLength = gMC->TrackLength(); // Return the length of the current track from its origin (in cm)
fMot0TrackID = gMC->GetStack()->GetCurrentTrack()->GetMother(0);
fPID = gMC->TrackPid();
}
Условие начала поинта. Поинт начинается при первом шаге в чувствительном объёме. Инициализируем переменные, которые можно инициализировать в начале поинта.
fELoss += gMC->Edep(); // GeV //Return the energy lost in the current step
Инкрементация eLoss
поинта происходит на каждом шаге от его начала до окончания.
if (gMC->IsTrackExiting() || //Return true if this is the last step of the track in the current volume
gMC->IsTrackStop() || //Return true if the track energy has fallen below the threshold
gMC->IsTrackDisappeared())
{
gMC->TrackPosition(fPosOut);
gMC->TrackMomentum(fMomOut);
Условие окончания поинта. Инициализируем переменные окончания поинта.
if (fELoss > 0.){
AddPoint( fEventID, fTrackID, fMot0TrackID, fPID,
TVector3(fPosIn.X(), fPosIn.Y(), fPosIn.Z()),
TVector3(fPosOut.X(), fPosOut.Y(), fPosOut.Z()),
TVector3(fMomIn.Px(), fMomIn.Py(), fMomIn.Pz()),
TVector3(fMomOut.Px(), fMomOut.Py(), fMomOut.Pz()),
fTime, fLength, fELoss);
}
}
return kTRUE;
}
Условие записи поинта в выходной файл. По умолчанию необходимо, чтобы на одном из шагов был Eloss у частицы.
Note
Каждое из условий: начала поинта, окончания и записи в файл можно менять. Также можно менять структуру данных поинта. Это будет продемонстрировано далее.
Как работать с процессами в ProcessHits¶
ProcessHits вызывается при каждом шаге в чувствительном объёме. На каждом шаге обновляется структура gMC, в которой в том числе записан список произошедших процессов. Вывести информацию о процессах в консоль можно, к примеру следующим образом.
TArrayI processesID;
gMC->StepProcesses(processesID);
std::cerr << gMC->TrackPid() << " " << gMC->Edep() << " " ;
for (Int_t i = 0;i<processesID.GetSize();i++){
std::cerr << TMCProcessName[processesID[i]] << " ";
}
std::cerr << std::endl;
В массив целых чисел processesID
будет записан список идентификаторов процессов. С помощью структуры данных TMCProcessName
идетификаторы будут преобразованы к строковым выражениям.
В результате можно будет наблюдать таблиицу из PDG частицы, Edep на данном шаге и списка произошедших процессов.
Пример неупругого взаимодействия нейтрона в сцинциляторе
2112 0 No active process
2112 0 Hadronic inelastic Primary particle emission Primary particle emission
2212 0 No active process
2212 0.0015359 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
2212 0.000867295 Energy loss Multiple scattering Energy threshold Primary particle emission Primary particle emission
1000020040 0 No active process
1000020040 0.001234 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
1000020040 0.00136297 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
1000020040 0.00146174 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
1000020040 0.000321879 Energy loss Multiple scattering Energy threshold Primary particle emission Primary particle emission
1000010020 0 No active process
1000010020 0.00177477 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
1000010020 0.00185553 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
1000010020 0.00193789 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
1000010020 0.00202412 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
1000010020 0 No active process
1000010020 0.00175385 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
2212 0 No active process
2212 0.0159552 Energy loss Multiple scattering Transportation Primary particle emission Primary particle emission
2112 0 No active process
2112 0 Transportation Primary particle emission Primary particle emission
2112 0 No active process
2112 0 Hadronic elastic Primary particle emission Primary particle emission
2112 0.000594615 Energy threshold Primary particle emission Primary particle emission
2112 0 No active process
2112 0 Transportation Primary particle emission Primary particle emission
2112 0 No active process
2112 0 Transportation Primary particle emission Primary particle emission
2212 0 No active process
2212 0.00136197 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
2212 0.00141336 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
2212 0.00132469 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
2212 0.00143044 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
2212 0.00132046 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
2212 0.00025315 Energy loss Multiple scattering Energy threshold Primary particle emission Primary particle emission
11 0 No active process
11 0.00245072 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
11 0.00121991 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
11 0.00146832 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
11 0.000707945 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
11 0.000584947 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
11 0.000423729 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
11 0.000434988 Energy loss Multiple scattering Energy loss Primary particle emission Primary particle emission
11 4.95559e-05 Energy loss Multiple scattering Transportation Primary particle emission Primary particle emission
Как реализовать закон Биркса в ProcessHits¶
Для детекторов на базе сцинтилляторов из симуляции необходим не только eloss
, но еще и световыход - light yield
. Световыход зависит от плотности ионизации и может изменяться по длине поинта. Поэтому данное вычисление нельзя перенести, например, в диджитизацию. Далее будет изложена реализация закона Биркса, взятая из Geant4.
// Set constants for Birk's Law implentation
static const Double_t dP = 1.032 ;
static const Double_t BirkC1 = 0.013/dP;
static const Double_t BirkC2 = 9.6e-6/(dP * dP);
static Double_t lightYield;
Необходимо ввести ряд констант и переменную, в которой будет храниться суммарный световыход поинта, в начале метода ProcessHits
.
if ( gMC->IsTrackEntering() ) { // Return true if this is the first step of the track in the current volume
...
lightYield = 0.;
...
}
Данную переменную надо обнулить в начале поинта.
// Correction for all charge states
if (gMC->TrackCharge()!=0) { // Return the charge of the track currently transported
Double_t BirkC1Mod = 0;
// Apply correction for higher charge states
if (TMath::Abs(gMC->TrackCharge())>=2)
BirkC1Mod=BirkC1*7.2/12.6;
else
BirkC1Mod=BirkC1;
if (gMC->TrackStep()>0)
{
Double_t dedxcm=gMC->Edep()*1000./gMC->TrackStep(); //[MeV/cm]
curLightYield=gMC->Edep()*1000./(1.+BirkC1Mod*dedxcm+BirkC2*dedxcm*dedxcm); //[MeV]
curLightYield /= 1000.; //[GeV]
lightYield+=curLightYield;
}
}
Реализация закона. Первое условие - работаем с заряженной частицей. Второе условие - величина шага больше нуля (чтобы не делить на ноль). Таким образом, в случае, если энерговыделение произошло на шаге длинной равной нулю, а такое может быть, к примеру, когда происходит родить вторичную ниже порога на рождение.
AddPoint( eventID, trackID, mot0TrackID, mass,
TVector3(posIn.X(), posIn.Y(), posIn.Z()),
TVector3(posOut.X(), posOut.Y(), posOut.Z()),
TVector3(momIn.Px(), momIn.Py(), momIn.Pz()),
TVector3(momOut.Px(), momOut.Py(), momOut.Pz()),
time, length, eLoss, lightYield);
Для записи в файл, данный атрибут необходимо добавить в класс поинта, конструктор поинта, и метод AddPoint
.
Класс Point¶
Типовой класс поинт выглядит следующим образом:
ERDetPoint.h:
// -------------------------------------------------------------------------
// ----- ERDetPoint header file -----
// ----- Created data developerName -----
// -------------------------------------------------------------------------
/** ERDetPoint.h
**/
#ifndef ERDetPoint_H
#define ERDetPoint_H
#include "TObject.h"
#include "TVector3.h"
#include "FairMCPoint.h"
class ERDetPoint : public FairMCPoint
{
public:
/** Default constructor **/
ERDetPoint();
/** Constructor with arguments
*@param EventID Index of Event
*@param trackID Index of MCTrack
*@param mot0trackID Index of Mother MCTrack
*@param pid particle ID
*@param posIn Ccoordinates at entrance of point [cm]
*@param posOut Coordinates at exit of point [cm]
*@param momIn Momentum of track at entrance [GeV]
*@param momOut Momentum of track at exit [GeV]
*@param tof Time since event start [ns]
*@param length Track length since creation [cm]
*@param eLoss Energy deposit [KeV]
**/
ERDetPoint(Int_t eventID, Int_t trackID,
Int_t mot0trackID,
Int_t pid,
TVector3 posIn,
TVector3 posOut, TVector3 momIn, TVector3 momOut,
Double_t tof, Double_t length, Double_t eLoss);
/** Copy constructor **/
ERDetPoint(const ERDetPoint&);
/** Destructor **/
virtual ~ERDetPoint();
ERDetPoint& operator=(const ERDetPoint&) { return *this; }
/** Accessors **/
Int_t GetEventID() const { return fEventID; }
Int_t GetMot0TrackID() const { return fMot0TrackID; }
Double_t GetXIn() const { return fX; }
Double_t GetYIn() const { return fY; }
Double_t GetZIn() const { return fZ; }
Double_t GetXOut() const { return fX_out; }
Double_t GetYOut() const { return fY_out; }
Double_t GetZOut() const { return fZ_out; }
Double_t GetPxOut() const { return fPx_out; }
Double_t GetPyOut() const { return fPy_out; }
Double_t GetPzOut() const { return fPz_out; }
Int_t GetPID() const { return fPid; }
void PositionIn(TVector3& pos) { pos.SetXYZ(fX, fY, fZ); }
void PositionOut(TVector3& pos) { pos.SetXYZ(fX_out,fY_out,fZ_out); }
void MomentumOut(TVector3& mom) { mom.SetXYZ(fPx_out,fPy_out,fPz_out); }
Int_t StilbenNr() const {return fStilbenNr;}
Float_t LightYield() const {return fLightYield;}
/** Point coordinates at given z from linear extrapolation **/
Double_t GetX(Double_t z) const;
Double_t GetY(Double_t z) const;
/** Check for distance between in and out **/
Bool_t IsUsable() const;
/** Output to screen **/
virtual void Print(const Option_t* opt = 0) const;
protected:
Int_t fEventID;
Int_t fMot0TrackID;
Int_t fPid;
Double32_t fX_out, fY_out, fZ_out;
Double32_t fPx_out, fPy_out, fPz_out;
Int_t fStilbenNr;
Float_t fLightYield;
ClassDef(ERDetPoint,1)
};
#endif
ERDetPoint.cxx:
// -------------------------------------------------------------------------
// ----- ERDetPoint source file -----
// -------------------------------------------------------------------------
#include "ERDetPoint.h"
#include <iostream>
using namespace std;
// ----- Default constructor -------------------------------------------
ERDetPoint::ERDetPoint()
: FairMCPoint(),
fX_out(0.), fY_out(0.), fZ_out(0.),
fPx_out(0.), fPy_out(0.), fPz_out(0.),
fStilbenNr(-1)
{
}
// -------------------------------------------------------------------------
ERDetPoint::ERDetPoint(Int_t eventID, Int_t trackID,
Int_t mot0trackID,
Int_t pid,
TVector3 posIn,
TVector3 posOut, TVector3 momIn, TVector3 momOut,
Double_t tof, Double_t length, Double_t eLoss)
: FairMCPoint(trackID, -1., posIn, momIn, tof, length, eLoss),
fEventID(eventID),
fPid(pid),
fX_out(posOut.X()), fY_out(posOut.Y()), fZ_out(posOut.Z()),
fPx_out(momOut.X()), fPy_out(momOut.Y()), fPz_out(momOut.Z())
{
}
// -------------------------------------------------------------------------
ERDetPoint::ERDetPoint(const ERDetPoint& right)
: FairMCPoint(right),
fPid(right.fPid),
fX_out(right.fX_out), fY_out(right.fY_out), fZ_out(right.fZ_out),
fPx_out(right.fPx_out), fPy_out(right.fPy_out), fPz_out(right.fPz_out)
{
}
// -------------------------------------------------------------------------
ERDetPoint::~ERDetPoint()
{
}
// -------------------------------------------------------------------------
void ERDetPoint::Print(const Option_t* opt /* = 0*/) const
{
cout << "-I- ERDetPoint: track " << fTrackID << " mother track = " << fMot0TrackID << endl;
cout << " particle ID " << fPid << endl;
cout << " Position (" << fX << ", " << fY << ", " << fZ << ") cm" << endl;
cout << " Momentum (" << fPx << ", " << fPy << ", " << fPz << ") GeV" << endl;
cout << " Time " << fTime << " ns, Length " << fLength << " cm" << endl;
cout << " Energy loss " << fELoss << " keV "<< endl;
}
// -------------------------------------------------------------------------
// ----- Point x coordinate from linear extrapolation ------------------
Double_t ERDetPoint::GetX(Double_t z) const
{
if ( (fZ_out-z)*(fZ-z) >= 0. ) return (fX_out+fX)/2.;
Double_t dz = fZ_out - fZ;
return ( fX + (z-fZ) / dz * (fX_out-fX) );
}
// -------------------------------------------------------------------------
// ----- Point y coordinate from linear extrapolation ------------------
Double_t ERDetPoint::GetY(Double_t z) const
{
if ( (fZ_out-z)*(fZ-z) >= 0. ) return (fY_out+fY)/2.;
Double_t dz = fZ_out - fZ;
// if ( TMath::Abs(dz) < 1.e-3 ) return (fY_out+fY)/2.;
return ( fY + (z-fZ) / dz * (fY_out-fY) );
}
// -------------------------------------------------------------------------
// ----- Public method IsUsable ----------------------------------------
Bool_t ERDetPoint::IsUsable() const
{
Double_t dz = fZ_out - fZ;
if ( TMath::Abs(dz) < 1.e-4 ) return kFALSE;
return kTRUE;
}
// -------------------------------------------------------------------------
ClassImp(ERDetPoint)
Класс MCHeader¶
Макрос симуляции¶
Стандартный макрос симуляции детектора должен находится в папке ~/expertroot/macro/det
и называться sim.C
.
sim.C:
void sim(Int_t nEvents = 1000){
//---------------------Files-----------------------------------------------
TString outFile= "sim.root";
TString parFile= "par.root";
// ------------------------------------------------------------------------
// ----- Timer --------------------------------------------------------
TStopwatch timer;
timer.Start();
// ------------------------------------------------------------------------
Создаем менеджер симуляции и указываем какой библиотекой будет осуществляться транспорт:
// ----- Create simulation run ----------------------------------------
FairRunSim* run = new FairRunSim();
/** Select transport engine
* TGeant3
* TGeant4
**/
run->SetName("TGeant4"); // Transport engine
run->SetOutputFile(outFile.Data()); // Output file
// ------------------------------------------------------------------------
Создаем базу данных параметров
// ----- Runtime database ---------------------------------------------
FairRuntimeDb* rtdb = run->GetRuntimeDb();
// ------------------------------------------------------------------------
Добавляем файл с материалами
// ----- Create media -------------------------------------------------
run->SetMaterials("media.geo"); // Materials
// ------------------------------------------------------------------------
Добавляем пассивный объем пещеры и активный детектор.
// ----- Create detectors ----------------------------------------------
FairModule* cave= new ERCave("CAVE");
cave->SetGeometryFileName("cave.geo");
run->AddModule(cave);
// Det definition
/* Select verbosity level
* 0 - only standard logs
* 1 - Print points after each event
*/
Int_t verbose = 0;
ERDet* det= new ERDet("ERDet", kTRUE,verbose);
det->SetGeometryFileName("det.v1.geo.root");
run->AddModule(det);
// ------------------------------------------------------------------------
Инициализируем класс генератора событий:
// ----- Create PrimaryGenerator --------------------------------------
FairPrimaryGenerator* primGen = new FairPrimaryGenerator();
Int_t pdgId = 2112; // neutron beam
Double32_t theta1 = 0.; // polar angle distribution
Double32_t theta2 = 7.;
Double32_t kin_energy = .500; //GeV
Double_t mass = TDatabasePDG::Instance()->GetParticle(pdgId)->Mass();
Double32_t momentum = TMath::Sqrt(kin_energy*kin_energy + 2.*kin_energy*mass); //GeV
FairBoxGenerator* boxGen = new FairBoxGenerator(pdgId, 1);
boxGen->SetThetaRange(theta1, theta1);
boxGen->SetPRange(momentum, momentum);
boxGen->SetPhiRange(90, 90);
boxGen->SetBoxXYZ(0.,0,0.6,0.6,0.);
primGen->AddGenerator(boxGen);
run->SetGenerator(primGen);
// ------------------------------------------------------------------------
Сохранение траекторий для event display и уровень подробности логов.
//-------Set visualisation flag to true------------------------------------
run->SetStoreTraj(kTRUE);
//-------Set LOG verbosity -----------------------------------------------
FairLogger::GetLogger()->SetLogVerbosityLevel("LOW");
Запуск процедуры инициализации менеджера и инициализации базы данных параметров
// ----- Initialize simulation run ------------------------------------
run->Init();
Int_t nSteps = -15000;
// ----- Runtime database ---------------------------------------------
Bool_t kParameterMerged = kTRUE;
FairParRootFileIo* parOut = new FairParRootFileIo(kParameterMerged);
parOut->open(parFile.Data());
rtdb->setOutput(parOut);
rtdb->saveOutput();
rtdb->print();
// ---------------------------------------------------------
Запуск на исполнение
// ----- Run simulation ------------------------------------------------
run->Run(nEvents);
// ----- Finish -------------------------------------------------------
timer.Stop();
Double_t rtime = timer.RealTime();
Double_t ctime = timer.CpuTime();
cout << endl << endl;
cout << "Macro finished succesfully." << endl;
cout << "Output file is sim.root" << endl;
cout << "Parameter file is par.root" << endl;
cout << "Real time " << rtime << " s, CPU time " << ctime
<< "s" << endl << endl;
}