Scoring Meshes

Scoring Meshes#

The user can specify scoring meshes to obtain quantities on the defined grid. In Geant4 this is achieved using a set of UI commands. In this Julia interface this functionality has been encapsulated in a number of data structures. The function to create a scoring mesh is G4JLScoringMesh and receive as arguments the the type and dimensions of the mesh, the position, the rotation, the number of bins in each dimension, and the quantities to accumulate with eventually some filter conditions.

using Geant4
using Geant4.SystemOfUnits
using CairoMakie

Let’s define a simple detector (from the previous notebook on sensitive detectors)

struct SimpleDetector <: G4JLDetector ; end

#---Materials----------------------------------------------------------------------------------
nist = G4NistManager!Instance()
m_air = FindOrBuildMaterial(nist, "G4_AIR")
m_bgo = FindOrBuildMaterial(nist, "G4_BGO")

#---Volumes------------------------------------------------------------------------------------
worldS = G4Box("world", 1m, 1m, 1m)
worldLV = G4LogicalVolume(worldS, m_air, "World")
worldPV = G4PVPlacement(nothing, G4ThreeVector(), worldLV, "World", nothing, false, 0, false)

crystalS = G4Box("world", 5cm, 5cm, 10cm)
crystalLV = G4LogicalVolume(crystalS, m_bgo, "Crystal")
crystalPV = G4PVPlacement(nothing, G4ThreeVector(), crystalLV, "Crystal", worldLV, false, 0, false)

#---define getConstructor
Geant4.getConstructor(::SimpleDetector)::Function = (::SimpleDetector) -> worldPV

We create a scoring mesh of the same size as the crystal and with bins 20, 20 and 40 in z direction to collect energy deposit, number of steps produced by gammas, electrons and positrons.

sc1 = G4JLScoringMesh("boxMesh_1",
                      BoxMesh(5cm,5cm,20cm),
                      bins = (20, 20, 40),
                      quantities = [ energyDeposit("eDep")
                                     nOfStep("nOfStepGamma", filters=[ParticleFilter("gammafilter", "gamma")])
                                     nOfStep("nOfStepEMinus", filters=[ParticleFilter("eMinusFilter", "e-")])
                                     nOfStep("nOfStepEPlus", filters=[ParticleFilter("ePlusFilter", "e+")])
                                   ]
                      );

Let’s initialize now the application

particlegun = G4JLGunGenerator(particle = "e-", 
                               energy = 3GeV, 
                               direction = G4ThreeVector(0,0,1), 
                               position = G4ThreeVector(0,0,-1m))

app = G4JLApplication(;detector = SimpleDetector(),                  # detector with parameters
                       generator = particlegun,                      # primary particle generator
                       nthreads = 4,                                 # number of threads (MT)
                       physics_type = FTFP_BERT,                     # what physics list to instantiate
                       scorers = [sc1]                               # list of scorers 
                      );
**************************************************************
 Geant4 version Name: geant4-11-03-patch-02 [MT]   (25-April-2025)
  << in Multi-threaded mode >> 
                       Copyright : Geant4 Collaboration
                      References : NIM A 506 (2003), 250-303
                                 : IEEE-TNS 53 (2006), 270-278
                                 : NIM A 835 (2016), 186-225
                             WWW : http://geant4.org/
**************************************************************
configure(app)
initialize(app)
--- G4CoupledTransportation is used 
.... G4ScoringMessenger::MeshBinCommand - G4ScoringBox
G4ScoringManager has 1 scoring meshes.
G4ScoringBox : boxMesh_1 --- Shape: Box mesh
 Size (x, y, z): (5, 5, 20) [cm]
 # of segments: (20, 20, 40)
 displacement: (0, 0, 0) [cm]
 registered primitve scorers : 
   0  eDep
   1  nOfStepGamma     with  gammafilter
   2  nOfStepEMinus     with  eMinusFilter
   3  nOfStepEPlus     with  ePlusFilter

run for 1000 events

beamOn(app,1000)
G4WT1 > .... G4ScoringMessenger::MeshBinCommand - G4ScoringBox
G4WT2 > .... G4ScoringMessenger::MeshBinCommand - G4ScoringBox
G4WT3 > .... G4ScoringMessenger::MeshBinCommand - G4ScoringBox
G4WT0 > .... G4ScoringMessenger::MeshBinCommand - G4ScoringBox
G4WT1 > G4ScoringManager has 1 scoring meshes.
G4WT1 > G4ScoringBox : boxMesh_1 --- Shape: Box mesh
G4WT2 > G4ScoringManager has 1 scoring meshes.
G4WT1 >  Size (x, y, z): (5, 5, 20) [cm]
G4WT2 > G4ScoringBox : boxMesh_1 --- Shape: Box mesh
G4WT1 >  # of segments: (20, 20, 40)
G4WT2 >  Size (x, y, z): (5, 5, 20) [cm]
G4WT1 >  displacement: (0, 0, 0) [cm]
G4WT2 >  # of segments: (20, 20, 40)
G4WT1 >  registered primitve scorers : 
G4WT1 >    0  eDep
G4WT1 >    1  nOfStepGamma     with  gammafilter
G4WT1 >    2  nOfStepEMinus     with  eMinusFilter
G4WT0 > G4ScoringManager has 1 scoring meshes.
G4WT2 >  displacement: (0, 0, 0) [cm]
G4WT2 >  registered primitve scorers : 
G4WT2 >    0  eDep
G4WT2 >    1  nOfStepGamma     with  gammafilter
G4WT1 >    3  nOfStepEPlus     with  ePlusFilter
G4WT2 >    2  nOfStepEMinus     with  eMinusFilter
G4WT2 >    3  nOfStepEPlus     with  ePlusFilter
G4WT3 > G4ScoringManager has 1 scoring meshes.
G4WT3 > G4ScoringBox : boxMesh_1 --- Shape: Box mesh
G4WT0 > G4ScoringBox : boxMesh_1 --- Shape: Box mesh
G4WT3 >  Size (x, y, z): (5, 5, 20) [cm]
G4WT3 >  # of segments: (20, 20, 40)
G4WT3 >  displacement: (0, 0, 0) [cm]
G4WT0 >  Size (x, y, z): (5, 5, 20) [cm]
G4WT0 >  # of segments: (20, 20, 40)
G4WT0 >  displacement: (0, 0, 0) [cm]
G4WT3 >  registered primitve scorers : 
G4WT3 >    0  eDep
G4WT0 >  registered primitve scorers : 
G4WT3 >    1  nOfStepGamma     with  gammafilter
G4WT0 >    0  eDep
G4WT3 >    2  nOfStepEMinus     with  eMinusFilter
G4WT0 >    1  nOfStepGamma     with  gammafilter
G4WT3 >    3  nOfStepEPlus     with  ePlusFilter
G4WT0 >    2  nOfStepEMinus     with  eMinusFilter
G4WT0 >    3  nOfStepEPlus     with  ePlusFilter

Now the scoring mesh variable sc1 holds the collected information

sum, sum2, entries = sc1.eDep;

Each of these variables (sum, sum2 and entries) is a 20×20×40 Array corresponding to the deposit energy. Let’s see a X-Y plane of it in the middle of the Z axis:

sum[:,:,20]
20×20 Matrix{Float64}:
 13.7256   14.9938    7.92104   13.8171   …   7.27156   9.63841  19.785
 12.5688   29.1449   14.7244    27.9177      13.7953   13.2012   15.1446
  9.0862   26.3659   18.3457    35.1564      17.8024   13.9003   15.4984
 12.3851   15.7975   41.7305    41.863       26.1884   19.0315   19.7014
 14.3283   31.9743   38.3296    34.5613      34.2372   26.2778   20.1119
 26.0273   36.8281   46.8928    63.5484   …  38.885    36.7931   43.1647
 31.7248   24.7029   34.6437    66.118       38.5972   26.8317   24.2865
 30.0158   42.4134   61.1366   115.708       71.6774   37.5478   34.0374
 31.9256   30.7449   59.6449   132.869       76.5851   65.04     36.357
 38.0952   28.9673   71.953    125.173       48.5948   55.2591   23.0177
 31.6647   45.7067   90.3182    95.1163   …  49.6353   53.9987   23.6632
 40.1828   45.9377   59.8547    69.8218      60.2548   38.2881   29.3659
 32.4741   28.0979   54.3396    94.8621      52.9933   61.4301   31.4744
 23.9163   44.4536   51.3471    90.3794      43.5916   42.0923   21.3182
 13.7058   26.7813   56.5094    65.2599      23.2429   14.2696   24.3575
 24.269    34.1277   34.0417    48.8177   …  26.33     43.6835   23.2543
 21.4099   13.3081   13.878     24.8339      25.716    19.2284   15.9123
 13.6347    7.63564  15.3051    30.4052      18.9042   17.8803   17.1869
 13.7882    9.50164  14.0989    18.6022      16.4974    6.60949   7.72048
  8.92128   6.3244   15.1491     7.80917      7.27676   5.53977   6.25419

A better way to to plot a heatmap with Makie.

img = heatmap(sum[:,:,20], color=:thermal, title="Edep (XY)")
display("image/png", img)
Invalid attributes color and title for plot type Heatmap{Tuple{Makie.EndPoints{Float32}, Makie.EndPoints{Float32}, Matrix{Float32}}}.

The available plot attributes for Heatmap{Tuple{Makie.EndPoints{Float32}, Makie.EndPoints{Float32}, Matrix{Float32}}} are:

alpha        depth_shift      inspector_hover  nan_color       transparency
clip_planes  fxaa             inspector_label  overdraw        visible     
colormap     highclip         interpolate      space                       
colorrange   inspectable      lowclip          ssao                        
colorscale   inspector_clear  model            transformation              



Stacktrace:
 [1] validate_attribute_keys(plot::Heatmap{Tuple{Makie.EndPoints{Float32}, Makie.EndPoints{Float32}, Matrix{Float32}}})
   @ Makie ~/.julia/packages/Makie/4JW9B/src/recipes.jl:931
 [2] push!(scene::Scene, plot::Plot)
   @ Makie ~/.julia/packages/Makie/4JW9B/src/scenes.jl:512
 [3] plot!
   @ ~/.julia/packages/Makie/4JW9B/src/interfaces.jl:212 [inlined]
 [4] plot!(ax::Axis, plot::Heatmap{Tuple{Makie.EndPoints{Float32}, Makie.EndPoints{Float32}, Matrix{Float32}}})
   @ Makie ~/.julia/packages/Makie/4JW9B/src/figureplotting.jl:431
 [5] plot!(fa::Makie.FigureAxis, plot::Heatmap{Tuple{Makie.EndPoints{Float32}, Makie.EndPoints{Float32}, Matrix{Float32}}})
   @ Makie ~/.julia/packages/Makie/4JW9B/src/figureplotting.jl:427
 [6] _create_plot(F::Function, attributes::Dict{Symbol, Any}, args::Matrix{Float64})
   @ Makie ~/.julia/packages/Makie/4JW9B/src/figureplotting.jl:330
 [7] #heatmap#47
   @ ~/.julia/packages/Makie/4JW9B/src/recipes.jl:517 [inlined]
 [8] top-level scope
   @ In[9]:1
img = heatmap(sum[:,10,:]', color=:thermal, title="Edep (XZ)")
display("image/png", img)