import numpy as np
from scipy import stats
import tensorflow as tf
import tensorflow_probability as tfp
import flamedisx as fd
export, __all__ = fd.exporter()
o = tf.newaxis
[docs]@export
class MakeS1Photoelectrons(fd.Block):
dimensions = ('photons_detected', 'photoelectrons_detected')
extra_dimensions = ()
model_functions = ('double_pe_fraction',)
double_pe_fraction = 0.219
[docs] def _compute(self, data_tensor, ptensor,
photons_detected, photoelectrons_detected):
p_dpe = self.gimme('double_pe_fraction',
data_tensor=data_tensor, ptensor=ptensor)[:, o, o]
# Double-pe emission only creates additional photoelectrons.
# Invalid values will get assigned p=0 later.
extra_pe = photoelectrons_detected - photons_detected
invalid = extra_pe < 0
# Negative arguments would mess up tfp's Binomial
extra_pe = tf.where(invalid,
tf.zeros_like(extra_pe),
extra_pe)
# (N_pe - N_photons) distributed as Binom(N_photons, p=pdpe)
result = tfp.distributions.Binomial(
total_count=photons_detected,
probs=tf.cast(p_dpe, dtype=fd.float_type())
).prob(extra_pe)
# Set probability of extra_pe < 0 cases to 0
return tf.where(invalid,
tf.zeros_like(photoelectrons_detected),
result)
[docs] def _simulate(self, d):
d['photoelectrons_detected'] = stats.binom.rvs(
n=d['photons_detected'],
p=self.gimme_numpy('double_pe_fraction')) + d['photons_detected']
[docs] def _annotate(self, d):
# TODO: this assumes the spread from the double PE effect is subdominant
dpe_fraction = self.gimme_numpy('double_pe_fraction')
for suffix, intify in (('min', np.floor),
('max', np.ceil),
('mle', lambda x: x)):
d['photons_detected_' + suffix] = \
intify(d['photoelectrons_detected_' + suffix].values
/ (1 + dpe_fraction))