Hello!
Thank you for the repository access, I am finding Pyxel to be an incredibly useful tool and wanted to share a small change I have made while using it.
I have been attempting to use Pyxel to generate data that matches results I am getting off of a lab CMOS image sensor. The built in pipeline works exactly as needed, however the charge_generation.photoelectrons.simple_conversion model appears to be suppressing noise, changing the $Noise = \sqrt{Signal}$ shot noise relationship expected at that point in the detector.
The current modification I have made is to models.charge_generation.photoelectrons.simple_conversion (line 35):
detector_charge[slice(0, photon_rows), slice(0, photon_cols)] = (
ph.array * ch.qe * ch.eta
)
changing it to (in a fresh function):
detector_charge[slice(0, photon_rows), slice(0, photon_cols)] = (
ph.array * ch.qe * ch.eta
)
This changes the calculation from a linear multiplication to binomial sampling, which primarily changes the noise in the output pixel values. Considering the contributions to the output noise by the input noise ($Noise_{in}$) and the noise of the charge generation model ($Noise_{model}$) (I have taken the signal to be the mean pixel value):

$Noise_{out}^2 = (Noise_{in}.Gain_{model})^2 + Noise_{model}^2$

For linear multiplication:

$Noise_{out}^2 = (\sqrt{Signal_{in}}.QE)^2 + 0^2$

$Noise_{out}^2 = (\sqrt{Signal_{out}}.\sqrt{QE})^2$

$Noise_{out} = \sqrt{Signal_{out}} .\sqrt{QE}$

Which is less than expected (when QE<1), however for binomial sampling:

$Noise_{out}^2 = (\sqrt{Signal_{in}}.QE)^2 + Noise_{charge generation}^2$

$= Signal_{in}.QE^2 + np(1p)$ < the variance of the binomial distribution

( $QE$ is the probability of success, $p$, and $Signal_{in}$(in photons) is the number of trials, $n$, of the binomial distribution)

$= Signal_{in}.QE^2 + Signal_{in}.QE(1QE)$

$= Signal_{in}.QE^2 + Signal_{in}.QE  Signal_{in}.QE^2$

$= Signal_{in}.QE=$

$Noise_{out} = \sqrt{Signal_{in}.QE} = \sqrt{Signal_{out}}$

Which matches the expected signal to noise relationship. Making this change has resulted in excellent agreement between the Pyxel outputs and my lab data. For example the plot below includes three sets of PTC data, two generated by Pyxel, with this modification ('Binomial Charge Generation') and without ('Linear Charge Generation'), alongside PTC data points captured from the lab sensor ('Experimental Results'). The plot demonstrates the lower noise seen in the output data using the linear charge_generation model. Both simulation runs were made with the parametric_ptc.yaml example script with changes to the detector values to match the lab sensor, the simulation was completed with a QE of 60%.
I am aware that numpy.random.binomial will break when (ch.qe * ch.eta
) > 1, or when any elements of ph.array
are not ints. I have stopped the latter from crashing the program by using the numpy.random.default_rng.binomial function, but the change is less elegant.
Hi Brad,
You can add the new external library in ‘setup.cfg’ under section [options] / install_requires.
If this package cannot be easily installed with pip on all platforms (Windows, Mac and Linux) then you have to consider to add this package as an optional package (also in ‘setup.cfg’ under section [options.extras_require]).
jp.display_detector(detector, array=detector.image)
the error TypeError: display_detector() got an unexpected keyword argument 'array'
and I'm unsure how to fix this
Hi Brad,
There are several ways to debug a Python program (see https://towardsdatascience.com/ultimateguidetopythondebugging854dea731e1b or https://medium.com/techtofreedom/sixdebuggingtechniquesforpythonprogrammerscb25a4baaf4b).
However, it is easier to use a IDE software such as PyCharm with an embedded debugger (see https://www.jetbrains.com/help/pycharm/debuggingpythoncode.html).