14 #ifndef ICARUSCODE_PMT_ALGORITHMS_PMTSIMULATIONALG_H
15 #define ICARUSCODE_PMT_ALGORITHMS_PMTSIMULATIONALG_H
37 #include "messagefacility/MessageLogger/MessageLogger.h"
38 #include "fhiclcpp/types/Atom.h"
39 #include "fhiclcpp/types/OptionalAtom.h"
40 #include "fhiclcpp/types/Sequence.h"
41 #include "fhiclcpp/types/Table.h"
44 #include "CLHEP/Random/RandEngine.h"
60 namespace icarus::opdet {
63 using namespace util::quantities::electromagnetism_literals;
64 using namespace util::quantities::electronics_literals;
81 template <
typename SampleType>
113 {
return create(opChannel, bufferRange); }
400 double multiplicationStageGain(
unsigned int i = 1)
const;
406 unsigned int nDynodes()
const {
return voltageDistribution.size(); }
411 void setVoltageDistribution(std::vector<double>&& Rs);
433 unsigned int pulseSubsamples = 1U;
435 unsigned int ADCbits = 14U;
457 CLHEP::HepRandomEngine* randomEngine =
nullptr;
460 CLHEP::HepRandomEngine* gainRandomEngine =
nullptr;
463 CLHEP::HepRandomEngine* darkNoiseRandomEngine =
nullptr;
466 CLHEP::HepRandomEngine* elecNoiseRandomEngine =
nullptr;
469 bool trackSelectedPhotons =
false;
476 std::size_t
pretrigSize()
const {
return pretrigFraction * readoutWindowSize; }
477 std::size_t
posttrigSize()
const {
return readoutWindowSize - pretrigSize(); }
482 {
return {
ADCcount{ 0 }, maxADC() }; }
508 std::tuple<std::vector<raw::OpDetWaveform>, std::optional<sim::SimPhotons>>
513 template <
typename Stream>
514 void printConfiguration(
Stream&& out, std::string indent =
"")
const;
520 using OpDetWaveformMaker_t
544 : fNSubsamples(static_cast<double>(nSubsamples)) {}
547 std::tuple<tick, SubsampleIndex_t> operator() (
double const tick_d)
const;
554 template <
typename Rand>
558 double const fReferenceGain = 0.0;
563 : fRandomGain(
std::move(randomGain))
564 , fReferenceGain(refGain)
568 double operator() (
double const n);
573 auto makeGainFluctuator()
const;
612 std::optional<sim::SimPhotons>& photons_used
669 std::vector<raw::OpDetWaveform> CreateFixedSizeOpDetWaveforms
687 template <
typename Combine>
709 void AddPhotoelectrons(
744 std::vector<optical_tick> FindTriggers(
Waveform_t const& wvfm)
const;
765 std::vector<optical_tick> CreateBeamGateTriggers()
const;
768 bool KicksPhotoelectron()
const;
771 std::pair<ADCcount, ADCcount> saturationRange()
const;
776 (
Waveform_t& waveform, std::pair<ADCcount, ADCcount>
const& range)
const;
779 void ApplySaturation(
Waveform_t& waveform)
const;
803 fhicl::Atom<double> DynodeK {
805 Comment(
"exponent to the voltage in multiplication gain expression"),
808 fhicl::Sequence<double> VoltageDistribution {
809 Name(
"VoltageDistribution"),
810 Comment(
"voltage distribution (relative resistor value)"),
811 { 17.4, 3.4, 5.0, 3.33, 1.67, 1.0, 1.2, 1.5, 2.2, 3.0, 2.4 }
814 fhicl::Atom<double> Gain {
816 Comment(
"average total gain (from one photoelectron to full signal)"),
831 fhicl::Atom<microseconds> ReadoutEnablePeriod {
832 Name(
"ReadoutEnablePeriod"),
833 Comment(
"Time for which PMT readout is enabled [us]")
837 Name(
"ReadoutWindowSize"),
839 (
"Duration of a single PMT readout acquisition window [samples]")
842 fhicl::Atom<unsigned int> ADCBits {
844 Comment(
"number of bits of the Analog-to-Digital Converter"),
847 fhicl::Atom<float> Baseline {
849 Comment(
"Waveform baseline (may be fractional) [ADC]")
852 fhicl::Atom<int> PulsePolarity {
853 Name(
"PulsePolarity"),
854 Comment(
"Pulse polarity: 1 for positive, -1 for negative")
857 fhicl::Atom<double> PreTrigFraction {
858 Name(
"PreTrigFraction"),
859 Comment(
"fraction of the readout window located earlier than the readout trigger")
866 fhicl::OptionalAtom<float> Saturation {
868 Comment(
"photomultiplier saturation (as number of photoelectrons)")
870 fhicl::Atom<double> QE {
872 Comment(
"total photoelectron quantum efficiency")
875 fhicl::Table<PMTspecConfig> PMTspecs {
877 Comment(
"collection of PMT characteristics"),
879 fhicl::Atom<bool> FluctuateGain {
880 Name(
"FluctuateGain"),
881 Comment(
"include gain fluctuation in the photoelectron response"),
888 fhicl::Atom<unsigned int> PulseSubsamples {
889 Name(
"PulseSubsamples"),
891 (
"split each tick by this many subsamples to increase PMT timing simulation"),
899 Name(
"DarkNoiseRate"),
900 Comment(
"Frequency of \"spontaneous\" emission of a dark noise photoelectron [Hz]")
907 fhicl::Atom<double> AmpNoise {
909 Comment(
"RMS of the electronics noise fluctuations [ADC counts]")
912 fhicl::Atom<bool> FastElectronicsNoise {
913 Name(
"FastElectronicsNoise"),
915 (
"use an approximate and faster random generator for electronics noise"),
922 fhicl::Atom<float> ThresholdADC {
923 Name(
"ThresholdADC"),
924 Comment(
"Threshold for self-triggered readout [ADC counts]")
927 fhicl::Atom<bool> CreateBeamGateTriggers {
928 Name(
"CreateBeamGateTriggers"),
929 Comment(
"Whether to create unbiased readout trigger at beam spill")
932 fhicl::Atom<microseconds> BeamGateTriggerRepPeriod {
933 Name(
"BeamGateTriggerRepPeriod"),
934 Comment(
"Repetition period for beam gate generated readout triggers [us]")
937 fhicl::Atom<std::size_t> BeamGateTriggerNReps {
938 Name(
"BeamGateTriggerNReps"),
939 Comment(
"Number of beam gate readout triggers to generate")
942 fhicl::Atom<microseconds> TriggerOffsetPMT {
943 Name(
"TriggerOffsetPMT"),
944 Comment(
"Time when readout begins, relative to readout trigger [us]")
969 std::unique_ptr<PMTsimulationAlg> operator()(
973 CLHEP::HepRandomEngine& mainRandomEngine,
974 CLHEP::HepRandomEngine& darkNoiseRandomEngine,
975 CLHEP::HepRandomEngine& elecNoiseRandomEngine,
976 bool trackSelectedPhotons =
false
999 CLHEP::HepRandomEngine& mainRandomEngine,
1000 CLHEP::HepRandomEngine& darkNoiseRandomEngine,
1001 CLHEP::HepRandomEngine& elecNoiseRandomEngine,
1002 bool trackSelectedPhotons =
false
1018 template <
typename SampleType>
1024 : fWaveform(waveform)
1025 , fPMTstartTime(PMTstartTime)
1026 , fSamplingPeriod(samplingPeriod)
1031 template <
typename SampleType>
1035 std::size_t
const start
1036 = std::min(std::size_t(bufferRange.first.value()), fWaveform.size());
1037 std::size_t
const end
1038 = std::min(std::size_t(bufferRange.second.value()), fWaveform.size());
1039 assert(start <= end);
1042 raw::TimeStamp_t const timeStamp { fPMTstartTime + start * fSamplingPeriod };
1049 fWaveform.begin() + start,
1050 fWaveform.begin() +
end,
1051 std::back_inserter(outputWaveform),
1052 [](
auto sample){
return sample.value(); }
1055 return outputWaveform;
1062 template <
typename Stream>
1066 using namespace util::quantities::electronics_literals;
1069 << indent <<
"ADC bits: " << fParams.ADCbits
1070 <<
" (" << fParams.ADCrange().first <<
" -- " << fParams.ADCrange().second
1072 <<
'\n' << indent <<
"Baseline: " << fParams.baseline
1073 <<
'\n' << indent <<
"ReadoutWindowSize: " << fParams.readoutWindowSize <<
" ticks"
1074 <<
'\n' << indent <<
"PreTrigFraction: " << fParams.pretrigFraction
1075 <<
'\n' << indent <<
"ThresholdADC: " << fParams.thresholdADC
1076 <<
'\n' << indent <<
"Saturation: " << fParams.saturation <<
" p.e."
1077 <<
'\n' << indent <<
"doGainFluctuations: "
1078 << std::boolalpha << fParams.doGainFluctuations
1079 <<
'\n' << indent <<
"PulsePolarity: " << ((fParams.pulsePolarity == 1)?
"positive":
"negative") <<
" (=" << fParams.pulsePolarity <<
")"
1080 <<
'\n' << indent <<
"Sampling: " << fSampling;
1081 if (fParams.pulseSubsamples > 1U)
1082 out <<
" (subsampling: x" << fParams.pulseSubsamples <<
")";
1084 <<
'\n' << indent <<
"Samples/waveform: " << fNsamples <<
" ticks"
1085 <<
'\n' << indent <<
"Gain at first stage: " << fParams.PMTspecs.firstStageGain()
1088 out <<
'\n' << indent <<
"Electronics noise: ";
1089 if (fParams.ampNoise > 0_ADCf) {
1090 out << fParams.ampNoise <<
" RMS ("
1091 << (fParams.useFastElectronicsNoise?
"faster":
"slower") <<
" algorithm)";
1095 if (fParams.createBeamGateTriggers) {
1096 out <<
'\n' << indent <<
"Create " << fParams.beamGateTriggerNReps
1097 <<
" beam gate triggers, one every " << fParams.beamGateTriggerRepPeriod <<
".";
1099 else out <<
'\n' << indent <<
"Do not create beam gate triggers.";
1101 out <<
'\n' << indent <<
"... and more.";
1103 out <<
'\n' << indent <<
"Template photoelectron waveform settings:"
1105 wsp.dump(std::forward<Stream>(out), indent +
" ");
1107 out <<
'\n' << indent <<
"Track used photons: "
1108 << std::boolalpha << fParams.trackSelectedPhotons
1116 #endif // ICARUSCODE_PMT_ALGORITHMS_PMTSIMULATIONALG_H
PMTspecs_t PMTspecs
PMT specifications.
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
size_t beamGateTriggerNReps
Number of beamgate trigger reps to produce.
Dimensioned variables representing frequency quantities.
unsigned int nDynodes() const
Number of dynodes in the PMTs.
temporary see SBN DocDB from CERN test stand measurement ReadoutWindowSize
float pretrigFraction
Fraction of window size to be before "trigger".
double QEbase
Uncorrected PMT quantum efficiency.
value_t
the JSON type enumeration
GainFluctuator(double const refGain, Rand &&randomGain)
double dynodeK
Gain from stage with voltage dV is proportional to dV^K.
Fast approximate Gaussian random translator.
microsecond_as<> microsecond
Type of time stored in microseconds, in double precision.
PulseFunction_t::ADCcount ADCcount
Functor to convert tick point into a tick number and a subsample index.
DiscretePhotoelectronPulse::Subsample_t PulseSampling_t
Type of sampled pulse shape: sequence of samples, one per tick.
gsl::index SubsampleIndex_t
Type of index of subsample.
bool useFastElectronicsNoise
Whether to use fast generator for electronics noise.
std::size_t pretrigSize() const
util::quantities::intervals::microseconds time_interval
bool createBeamGateTriggers
Option to create unbiased readout around beam spill.
double TimeStamp_t
us since 1970, based on TimeService
Dimensioned variables representing electromagnetic quantities.
picocoulomb_as<> picocoulomb
Type of charge stored in picocoulomb, in double precision.
DiscretePhotoelectronPulse wsp
ADCcount thresholdADC
ADC Threshold for self-triggered readout.
Precomputed digitized shape of a given function.
Abstract interface of shape of a pulse from one photoelectron.
std::size_t fNsamples
Samples per waveform.
OpDetWaveformMaker_t::WaveformData_t Waveform_t
Type internally used for storing waveforms.
tick_as< double > tick_d
Tick number, represented by double.
std::size_t posttrigSize() const
SinglePhotonResponseFunc_t const * pulseFunction
Single photon response function.
PMTsimulationAlg::ConfigurationParameters_t fBaseConfig
Part of the configuration learned from configuration files.
ADCcount::value_t WaveformValue_t
Numeric type in waveforms.
size_t readoutWindowSize
ReadoutWindowSize in samples.
Simulation objects for optical detectors.
PhotoelectronPulseFunction< nanoseconds > PulseFunction_t
Type of shape (times are in nanoseconds).
void printConfiguration(Stream &&out, std::string indent="") const
Prints the configuration into the specified output stream.
double gain
Total typical gain of a PMT.
Utilities to read and write quantities in FHiCL configuration.
A value measured in the specified unit.
Class for a function with precomputed values.
microseconds readoutEnablePeriod
Time (us) for which pmt readout is enabled.
megahertz_as<> megahertz
Type of frequency stored in megahertz, in double precision.
auto end(FixedBins< T, C > const &) noexcept
Algorithm class for the full simulation of PMT channels.
DiscretePhotoelectronPulse::PulseFunction_t SinglePhotonResponseFunc_t
Type for single photon response shape function: nanosecond -> ADC counts.
tick_as<> tick
Tick number, represented by std::ptrdiff_t.
Interface for a function describing a pulse from a photoelectron.
BEGIN_PROLOG vertical distance to the surface Name
TimeToTickAndSubtickConverter(unsigned int nSubsamples)
microseconds triggerOffsetPMT
Time relative to trigger when PMT readout starts TODO make it a trigger_time point.
An interval (duration, length, distance) between two quantity points.
std::optional< Rand > fRandomGain
Random gain extractor (optional).
detinfo::timescales::optical_tick optical_tick
Sampling of a photoelectron pulse.
ConfigurationParameters_t fParams
Complete algorithm configuration.
std::vector< double > voltageDistribution
Dimensioned variables related to electronics.
Collection of photons which recorded on one channel.
timescale_traits< OpticalTimeCategory >::tick_t optical_tick
Compact representation of photons on a channel.
void(PMTsimulationAlg::*)(Waveform_t &) const NoiseAdderFunc_t
Type of member function to add electronics noise.
std::pair< ADCcount, ADCcount > ADCrange() const
Contains all timing reference information for the detector.
int pulsePolarity
Pulse polarity (=1 for positive, =-1 for negative)
megahertz fSampling
Wave sampling frequency [MHz].
temporary see SBN DocDB DarkNoiseRate
double fQE
PMT quantum efficiency.
NoiseAdderFunc_t const fNoiseAdder
Single photon pulse (sampled).
Dimensioned variables representing space or time quantities.
Data types for detinfo::DetectorTimings.
nanosecond_as<> nanosecond
Type of time stored in nanoseconds, in double precision.
SampledFunction_t::SubsampleData_t Subsample_t
Type of subsample data (a sampling of the full range).
Type holding all configuration parameters for this algorithm.
double const fNSubsamples
Number of subsamples.
DiscretePhotoelectronPulse::SubsampleIndex_t SubsampleIndex_t
microseconds beamGateTriggerRepPeriod
Repetition Period (us) for BeamGateTriggers TODO make this a time_interval.
double firstStageGain() const
Returns the gain from the first stage of PMT multiplication.
hertz_as<> hertz
Type of frequency stored in hertz, in double precision.
timescale_traits< ElectronicsTimeCategory >::time_point_t electronics_time
A point in time on the electronics time scale.
static util::FastAndPoorGauss< 32768U, float > const fFastGauss
Returns a new PMTsimulationAlg with an updated configuration.
Main algorithm FHiCL configuration.