MCParticle vs MCtrack energies

Hi,
For my purpose i need to retrieve the true interactions positions of gamma rays interacting with a silicon detector as well as the deposited energies. For this reason i was playing around with MCTrack and MCParticle. The simulation consists in 2 point sources, 9 cm away from the detector surface.
Although i though MCTrack was enough for this purpose i thought of having a look anyway at MCParticle but i found some discrepancies and unexpected behaviors and i am not sure if those arise from errors in the allpix functions or from my understanding of them.

Using a python script i was getting data from MCTrack and MCParticle as

        for mc_track in br_mc_track:
        #here we record all the interactions the event had with the detector togheter with the tracks generated
        data_array[1] = mc_track.getStartPoint().x()
        data_array[2] = mc_track.getStartPoint().y()
        data_array[3] = mc_track.getStartPoint().z()
        data_array[4] = mc_track.getGlobalStartTime()
        data_array[5] = mc_track.getKineticEnergyInitial()
        data_array[6] = mc_track.getCreationProcessName()
        data_array[7] = mc_track.getParticleID()
        data_array[8] = hex(ROOT.addressof(mc_track))
        data_array[9] = hex(ROOT.addressof(mc_track.getParent()))
        data_array[10] = mc_track.getOriginatingVolumeName()

        # Create a dictionary from the array and column names
        new_row = dict(zip(df.columns, data_array))
        print(iev)

        # Convert the dictionary to a DataFrame and append it
        df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)

    for mc_particle in br_mc_particle:
        data_array[1] = mc_particle.getGlobalStartPoint().x()
        data_array[2] = mc_particle.getGlobalStartPoint().y()
        data_array[3] = mc_particle.getGlobalStartPoint().z()
        data_array[4] = mc_particle.getGlobalTime()
        data_array[5] = mc_particle.getKineticEnergyStart()
        data_array[6] = f"mc_particle, is it primary? {mc_particle.isPrimary()}. Connected track: {hex(ROOT.addressof(mc_particle.getTrack()))}"
        data_array[7] = mc_particle.getParticleID()
        data_array[8] = hex(ROOT.addressof(mc_particle))
        data_array[9] = hex(ROOT.addressof(mc_particle.getParent()))
        data_array[10] = "parent particle: {hex(ROOT.addressof(mc_particle.getPrimary()))}"

        new_row = dict(zip(df.columns, data_array))
        print(iev)

        df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)

respectively where iev is the event ID.

An example of results is shown below

We can see that for event IDs 104,113,123 everything looks fine, the first row(s) in each event are from MCTrack, and the initial kinetic energy (init_kin) is 511 keV as set in the .conf file for the source. the same is shown in the MCParticle row(s), with the difference that the initial coordinates are in proximity with the detector surface (which is at z=0) since MCParticle start saving informations of the particle once this enters the detector.

But then when we start to have interactions in the active area of the detector as for event 124, in the MCParticle rows (distinguishable from having mc_particle under the column process_name), we see that the starting kinetic energy of the primary particle is not 511 kev as expected but lower like 482keV.
If we look at the deposited energy from the first compton interaction by summing the initial kinetic energy of the secondary particles related to the interaction we notice that 482keV should rather be the energy of the primary particle after the interaction (so after having deposited some of its energy since 511-28.36=482.64keV).

Similarly happens for all other cases in which there is an interaction in the detector, where the init_kin from MCParticle is lower than 511kev and very similar to the final energy of the primary particle after having interacted.

On a last note if i calculate the deposited energy in the compton interaction looking at MCTrack for event 124, i would sum 28.5keV+0.1keV=28.6keV, while from MCParticle= 28.36keV+0.0keV =28.36keV. Similarly for all other interaction cases the results seem to be slightly different. Could this be because MCParticle looks at the true deposited energy while MCTrack has the true deposited energy + other effects present in the physics model such as doppler broadening? (i am using FTFP_BERT_PEN)

I also looked at getTotalDepositedCharge() for MCParticle and from that divide by 2 and multiply by 3.64eV and i do get similar results as from the other two ways mentioned before to measure the deposited energy although not exactly the same and i am not sure what is the more accurate/best way among those 3.

Bests

Luca

TLDR: if i want to know the deposited energy by a photon having a compton scattering or photoabsorption in the detector should i look at MCTrack initial kinetic energies of the secondaries, MCParticle initial kinetic energies of the secondaries, or MCParticle deposited charge of primary and secondaries? (or is it all the same?)

Hi @luca_tero

I would again say that it depends on what you’d like to do with these numbers. Would you like to know how much …

  • energy the primary lost?
  • energy the secondary particle had?
  • energy was deposited?
  • charge was deposited?

It’s different steps of the simulation, so of course there are minor differences. The difference between lost and secondary/deposited energy could be correlated with e.g. ionisation energy. The deposited charge again is somewhat randomised and undergoes rounding, see here.

Thus, I believe it is really up to you and your research goal which observable to choose. One thing that I would like to clarify, however:

  • MCTrack is an object recording a particles (regardless of being primary/secondary) properties through the full setup.
  • MCParticle is an object recording a traversal of an MCTrack through an individual sensitive volume. This is the reason why it starts just at the surface of your sensor and can already have a different initial energy than the MCTrack, because it’s the energy the particle had when entering the sensor.

Cheers
Paul

Hi

Thank you for the clarification. My interest is knowing the true energy deposited in the detector (which later on in a more realistic setting i will change and calculate it from the charge deposited)

Regarding MCParticle starting kinetic energy i understand the concept you mean but from my simulations i found that whenever the primary photon interacts with the active volume of the detector (and not before since there is nothing else before it, not even passive volumes and i set all in vacuum), then the kinetic initial energy from MCParticle for the primary photon at the point of entrance of the detector is lower than the energy of the monochromatic source (511 keV). This happen on all such cases.

On the contrary for all cases in which the primary photon does not interact with the detector the MCParticle kinetic initial energy signaled at the entrance of the detector is 511 keV.

But i’ll check better for that in case i made some mistakes in the conf or in the python code to read the ROOT file.

Bests

Luca

Hi @luca_tero ,

I do understand the issue, yes. I could imagine that Geant4 only emits a new “step” when the actual interaction is taking place, such that then at the beginning of this new step the energy was already decreased. I know that we distinguish photons from other particles when it comes to the position of the deposit, maybe we also have to adapt this part.

Would you mind providing us with your configurations, such that we can reproduce and investigate the issue?

Thanks!
Paul

Hi

Sure thing, here is the main conf file i use:

[Allpix]
log_level = "INFO"
log_format = "DEFAULT"
detectors_file = "detector.conf"
number_of_events = 10000

[GeometryBuilderGeant4]
world_material = "vacuum"


[DepositionGeant4]
record_all_tracks= true
physics_list = FTFP_BERT_PEN


source_type = "macro"
file_name = "Source_test.mac"


number_of_particles = 1
max_step_length = 0.5um
range_cut = 3.0um

#[ElectricFieldReader]
[Ignore]
model = "linear"
bias_voltage = -100V

#[GenericPropagation]
[Ignore]
temperature = 293K
charge_per_step = 100

#[SimpleTransfer]
[Ignore]
max_depth_distance = 5um

#[DefaultDigitizer]
[Ignore]
threshold = 600e

#[DetectorHistogrammer]


[VisualizationGeant4]
mode = "gui"
accumulate = true
display_trajectories = true
draw_hits = true

[ROOTObjectWriter]
file_name = "ROOT_test_2source.root"
#include = "MCParticle" "MCTrack" "DepositedCharge"


where source_test.mac is

# Point Source 1
/gps/source/intensity 5.
/gps/particle gamma
/gps/pos/type Point
/gps/pos/centre 0.0 0.0 100.0 mm
/gps/ene/type Mono
/gps/ene/mono 511 keV
/gps/ang/type iso


# Point Source 2
/gps/source/add 10.
/gps/particle gamma
/gps/pos/type Point
/gps/pos/centre 50.0 0.0 100.0 mm
/gps/ene/type Mono
/gps/ene/mono 511 keV
/gps/ang/type iso



and detector.conf is of a custom type placed in position and orientation 0 0 0 as follow

type = "monolithic"
geometry = "pixel"

number_of_pixels = 2000 2000
pixel_size = 50um 250um

sensor_thickness = 2cm #just to play around for now and have enough thickness to detect many events

chip_thickness = 100 um

to read the data i then used a custom made python script using the root and allpix libraries, i can provide that too if needed (but nothing fancier than what is already in the allpix example using mc_particle.getKineticEnergyStart() and similar).

also to confirm my understanding, if one would be interested in the ground truth energy deposited in the detector in an interaction then the MCTrack information on the energies would be a good way, while if one wants to look at the energy that the detector will “detect” from that interaction i should look at the deposited charge (for example with MCParticle getTotalDepositedChage), correct?

Bests