Hi @knakkali
I had a look at your example and I found the reason for the difference. It is however, not a bug in the code per say, but a logical result of how we do the simulation.
First, have a look at the output of the PulseTransfer
for a single event, once in the corner of the sensor and once in the center:
With Trapping
Pixel (0,0)
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (0,0) has charge of -821e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (0,1) has charge of 37e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (1,0) has charge of 37e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (1,1) has charge of 21e with 200 ancestors
(I) (E: 1) [R:PulseTransfer:detector1] Total charge induced on all pixels: -726e
Pixel (200,96)
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (199,95) has charge of 21e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (199,96) has charge of 36e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (199,97) has charge of 21e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (200,95) has charge of 37e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (200,96) has charge of -821e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (200,97) has charge of 37e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (201,95) has charge of 22e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (201,96) has charge of 37e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (201,97) has charge of 21e with 200 ancestors
(I) (E: 1) [R:PulseTransfer:detector1] Total charge induced on all pixels: -588e
Analysis
If you compare the central pixel, both see exactly the same charge, i.e. -821e. Neighbors see a non-zero charge being induced because of the imbalance between electrons and holes introduced by the trapping. I.e. if the hole gets trapped on its way to the backside, this induced charge does not go back to zero as it does when disabling trapping (see below).
However, for the corner pixel, there are only three neighbors (top, right, top-right) while for the central pixel there are eight neighboring pixels. Hence, if you simply calculate the sum of them all, you will get a lower total induced charge for the central pixel - only because you also count the neighbor pixels with a wrong-polarity induced current. Something that you probably don’t want to do…
Without Trapping
You can see the situation quite clearly when disabling trapping (just comment out the trapping_model
parameter):
Pixel (0,0)
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (0,0) has charge of -999e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (0,1) has charge of 1e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (1,0) has charge of 1e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (1,1) has charge of 0e with 200 ancestors
(I) (E: 1) [R:PulseTransfer:detector1] Total charge induced on all pixels: -997e
Pixel (200,96)
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (199,95) has charge of 0e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (199,96) has charge of 1e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (199,97) has charge of 0e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (200,95) has charge of 1e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (200,96) has charge of -999e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (200,97) has charge of 1e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (201,95) has charge of 0e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (201,96) has charge of 1e with 200 ancestors
(D) (E: 1) [R:PulseTransfer:detector1] Pixel (201,97) has charge of 0e with 200 ancestors
(I) (E: 1) [R:PulseTransfer:detector1] Total charge induced on all pixels: -995e
Analysis
Here you not only see that we induce the full deposited 1000e, but also that the accumulated (final) charge induced in the neighbor pixels sums to zero (or 1e because of numerical precision).
Where To Go From Here
By simply only summing the PixelCharge
contributions with the correct sign (and ignoring the others) I get a curve (red) that is very very close to the weighted CCE approach you have presented above (blue):
I would argue that this is a physically logical thing to do because your front-end will not detect these pulses. Alternatively, you could analyze the PixelHit
object instead of the PixelCharge
because there these neighbors have been suppressed, being below the threshold.
An alternative that would also save you computing time is to use distance = 0
and only calculate charge induced in the central pixel, and none of the neighbors. With the pitch and weighting field you have, you don’t expect a significant contribution from induction in neighbors anyway (small inter-pixel capacitance).
Our Homework
Currently, in the DefaultDigitizer
we simply std::abs()
the charge we get from the PixelCharge
object - which in this case obviously is problematic. Not really problematic in your case, because 40e will certainly not cross your threshold, but still this is an improper treatment of the input data and we likely have to add a parameter to define the threshold polarity. Tracked here: DefaultDigitizer: Threshold Polarity (#273) · Issues · Allpix Squared / Allpix Squared · GitLab
With this,
happy simulation!
Simon