Basic/B2a Example
In this example is based on the Geant4 basic/B2 example of the Geant4 distribution. It is a simple example that shows how to interact with the Geant4 classes.
You can also download this example as a Jupyter notebook and a plain Julia source file.
Table of contents
- Loading the necessary Julia modules
- Define Detector Parameters structure
- Define Tracker Hit
- Define Sensitive Detector
- Define End Event Action
- Particle Gun initialization
- Define Physics List
- Create the Application
- Configure, Initialize and Run
- Run the simulation
Loading the necessary Julia modules
Load the Geant4
and Geant4.SystemOfUnits
modules. We will also use the Printf
and GeometryBasics
modules to format the output and handle the geometry.ß
using Geant4
using Geant4.SystemOfUnits
using Printf, GeometryBasics
Define Detector Parameters structure
The B2aDetector
structure is defined with the default detector parameters. We include the B2aDetector.jl file not to clutter the example.
include(joinpath(@__DIR__, "B2aDetector.jl"))
Define Tracker Hit
We will extract the hits from the tracker and store them in a TrackerHit
structure that ius custom defined
struct TrackerHit
trackID::Int32
chamberNb::Int32
edep::Float64
pos::Point3{Float64}
end
Providing a custom Base.show
method to print the hits in a nice way
function Base.show(io::IO, hit::TrackerHit)
(;trackID, chamberNb, edep, pos) = hit
@printf(io, "\ntrackID: %3d chamberNb: %2d Edep: %.3f MeV Position: (%3f, %3f, %3f)", trackID, chamberNb, edep/MeV, pos...)
end
Define Sensitive Detector
First we define the data structure that will hold the hits. In this case we will store the hits in a vector of TrackerHit
structures.
struct B2aSDData <: G4JLSDData
trackerHits::Vector{TrackerHit}
B2aSDData() = new([])
end
Then we define the methods that will be called by the Geant4 simulation _initialize
(to initialize), _endOfEvent
(at the end of the event) and _processHits
(called when a hit is detected in that sensitive detector)
# Initialize method
function _initialize(::G4HCofThisEvent, data::B2aSDData)::Nothing
empty!(data.trackerHits)
return
end
# End of event method
function _endOfEvent(::G4HCofThisEvent, data::B2aSDData)::Nothing
return
end
# Process Hit method
function _processHits(step::G4Step, ::G4TouchableHistory, data::B2aSDData)::Bool
edep = step |> GetTotalEnergyDeposit
edep < 0. && return false
pos = step |> GetPostStepPoint |> GetPosition
push!(data.trackerHits, TrackerHit(step |> GetTrack |> GetTrackID,
step |> GetPreStepPoint |> GetTouchable |> GetCopyNumber,
edep,
Point3{Float64}(x(pos),y(pos),z(pos))))
return true
end
_processHits (generic function with 1 method)
Finally, create the sensitive detector instance of the type G4JLSensitiveDetector
with the name Chamber_SD
and the associated data structure B2aSDData
. The three previously defined methods are passed as arguments to the constructor.
chamber_SD = G4JLSensitiveDetector("Chamber_SD", B2aSDData(); ## SD name an associated data are mandatory
processhits_method=_processHits, ## process hist method (also mandatory)
initialize_method=_initialize, ## intialize method
endofevent_method=_endOfEvent) ## end of event method
Geant4.G4JLProtoSD{Main.var"##230".B2aSDData}("Chamber_SD", Main.var"##230".B2aSDData(Main.var"##230".TrackerHit[]), Main.var"##230"._processHits, Main.var"##230"._initialize, Main.var"##230"._endOfEvent)
Define End Event Action
This user function will be called at the end of each event. It will print the number of hits in the tracker for each event.
function endeventaction(evt::G4Event, app::G4JLApplication)
hits = getSDdata(app, "Chamber_SD").trackerHits
eventID = evt |> GetEventID
if eventID < 10 || eventID % 1000 == 0
G4JL_println("Event: $eventID with $(length(hits)) hits stored in this event")
end
return
end
endeventaction (generic function with 1 method)
Particle Gun initialization
particlegun = G4JLGunGenerator(particle = "proton",
energy = 3GeV,
direction = G4ThreeVector(0,0,1),
position = G4ThreeVector(0,0,-2940.0))
Geant4.G4JLGunGenerator("ParticleGun", Geant4.G4JLParticleGunData(nothing, "proton", G4ThreeVector(0.0,0.0,1.0), G4ThreeVector(0.0,0.0,-2940.0), 3000.0), Geant4.var"#init#19"(), Geant4.var"#gen#20"(), Geant4.G4JLGeneratorAction[])
Define Physics List
We define a new physics list that is based on the FTFP_BERT
physics list and adds a G4StepLimiterPhysics
to limit the step length of the particles.
struct B2PhysicsList <: G4VUserPhysicsList
function B2PhysicsList(verbose)
pl = FTFP_BERT(verbose)
lp = G4StepLimiterPhysics()
SetApplyToAll(lp, true) ## Apply to all particles
RegisterPhysics(pl, move!(lp)) ## Register to the physics list
return pl ## We need to return the physics list instance
end
end
Create the Application
We put all together in the G4JLApplication
structure. We define the detector, the primary generator, the number of threads, the physics list, the end event action and the sensitive detectors.
app = G4JLApplication(;detector = B2aDetector(nChambers=5), ## detector with parameters
generator = particlegun, ## primary particle generator
nthreads = 0, ## # of threads (0 = no MT)
physics_type = B2PhysicsList, ## what physics list to instantiate
endeventaction_method = endeventaction, ## end event action
sdetectors = ["Chamber_LV+" => chamber_SD] ## mapping of LVs to SDs (+ means multiple LVs with same name)
)
Geant4.G4JLApplication{Main.var"##230".B2aDetector, Geant4.G4JLNoData}(Geant4.G4RunManagerAllocated(Ptr{Nothing} @0x00000000025cd730), Main.var"##230".B2aDetector(5, true, 800.0, 200.0, 50.0, 2940.0), [Geant4.G4JLNoData()], Geant4.G4JLGunGenerator("ParticleGun", Geant4.G4JLParticleGunData(nothing, "proton", G4ThreeVector(0.0,0.0,1.0), G4ThreeVector(0.0,0.0,-2940.0), 3000.0), Geant4.var"#init#19"(), Geant4.var"#gen#20"(), Geant4.G4JLGeneratorAction[]), nothing, nothing, 0, 0, Geant4.G4RunManager, Geant4.G4JLDetectorConstruction, Main.var"##230".B2PhysicsList, Geant4.G4JLRunAction, Geant4.G4JLEventAction, Geant4.G4JLTrackingAction, Geant4.G4JLSteppingAction, nothing, nothing, nothing, nothing, nothing, nothing, Main.var"##230".endeventaction, nothing, nothing, Dict{String, Geant4.G4JLProtoSD}("Chamber_LV+" => Geant4.G4JLProtoSD{Main.var"##230".B2aSDData}("Chamber_SD", Main.var"##230".B2aSDData(Main.var"##230".TrackerHit[]), Main.var"##230"._processHits, Main.var"##230"._initialize, Main.var"##230"._endOfEvent)), Dict{String, Vector{Geant4.G4JLSensitiveDetector}}(), Geant4.G4JLScoringMesh[], nothing, nothing)
Configure, Initialize and Run
configure(app)
initialize(app)
Checking overlaps for volume Target:0 (G4Tubs) ... OK!
Checking overlaps for volume Tracker:0 (G4Tubs) ... OK!
Checking overlaps for volume Chamber_PV:1 (G4Tubs) ... OK!
Checking overlaps for volume Chamber_PV:2 (G4Tubs) ... OK!
Checking overlaps for volume Chamber_PV:3 (G4Tubs) ... OK!
Checking overlaps for volume Chamber_PV:4 (G4Tubs) ... OK!
Checking overlaps for volume Chamber_PV:5 (G4Tubs) ... OK!
Run the simulation
# ui`/tracking/verbose 1`
beamOn(app,100)
Event: 0 with 130 hits stored in this event
Event: 1 with 1155 hits stored in this event
Event: 2 with 84 hits stored in this event
Event: 3 with 79 hits stored in this event
Event: 4 with 134 hits stored in this event
Event: 5 with 206 hits stored in this event
Event: 6 with 64 hits stored in this event
Event: 7 with 10110 hits stored in this event
Event: 8 with 27 hits stored in this event
Event: 9 with 202 hits stored in this event
This page was generated using Literate.jl.