BmnRoot
Loading...
Searching...
No Matches
BmnSsdModule.cxx
Go to the documentation of this file.
1
7#include "BmnSsdModule.h"
8
9#include <cassert>
10#include <cmath>
11#include "TClonesArray.h"
12#include "TGeoManager.h"
13#include "TRandom.h"
14#include "TString.h"
15#include "FairLogger.h"
16#include "FairRunAna.h"
17#include "BmnMatch.h"
18#include "BmnSsdAddress.h"
19#include "BmnSsdCluster.h"
20#include "BmnSsdDigi.h"
21#include "BmnSsdDigitize.h"
22#include "BmnSsdSensorDssd.h"
23#include "BmnSsdSetup.h"
24
25using namespace std;
26
27
28// ----- Default constructor -------------------------------------------
29BmnSsdModule::BmnSsdModule(UInt_t address, TGeoPhysicalNode* node,
30 BmnSsdElement* mother) :
31 BmnSsdElement(address, kSsdModule, node, mother),
32 fNofChannels(2048),
33 fDynRange(0.),
34 fThreshold(0.),
35 fNofAdcChannels(0),
36 fTimeResolution(0),
37 fDeadTime(0.),
38 fNoise(0.),
39 fZeroNoiseRate(0.),
40 fNoiseRate(0.),
41 fIsSet(kFALSE),
42 fDeadChannels(),
43 fNoiseCharge(nullptr),
44 fAnalogBuffer(),
45 fClusters()
46{
47}
48// -------------------------------------------------------------------------
49
50
51
52// --- Destructor --------------------------------------------------------
54
55 // --- Clean analog buffer
56 for (auto chanIt = fAnalogBuffer.begin(); chanIt != fAnalogBuffer.end();
57 chanIt++) {
58 for ( auto sigIt = (chanIt->second).begin(); sigIt != (chanIt->second).end();
59 sigIt++) {
60 delete (*sigIt);
61 }
62 }
63}
64// -------------------------------------------------------------------------
65
66
67
68// ----- Convert ADC channel to analogue charge (channel mean) ---------
69Double_t BmnSsdModule::AdcToCharge(UShort_t adcChannel) {
70 return fThreshold + fDynRange / Double_t(fNofAdcChannels) *
71 ( Double_t(adcChannel) + 0.5 );
72}
73// -------------------------------------------------------------------------
74
75
76
77// ----- Add a signal to the buffer ------------------------------------
78void BmnSsdModule::AddSignal(UShort_t channel, Double_t time,
79 Double_t charge, Int_t index, Int_t entry,
80 Int_t file) {
81
82 // --- Check channel number
83 assert( channel < fNofChannels );
84
85 // --- Debug
86 LOG(debug3) << GetName() << ": Receiving signal " << charge
87 << " in channel " << channel << " at time "
88 << time << " s";
89
90 // --- Discard charge if the channel is dead
91 if ( fDeadChannels.count(channel) ) {
92 LOG(debug) << GetName() << ": discarding signal in dead channel "
93 << channel;
94 return;
95 }
96
97 // --- If the channel is not yet active: create a new set and insert
98 // --- new signal into it.
99 if ( fAnalogBuffer.find(channel) == fAnalogBuffer.end() ) {
100 BmnSsdSignal* signal = new BmnSsdSignal(time, charge,
101 index, entry, file);
102 fAnalogBuffer[channel].insert(signal);
103 LOG(debug4) << GetName() << ": Activating channel " << channel;
104 return;
105 } //? Channel not yet active
106
107 // --- The channel is active: there are already signals in.
108 // --- Loop over all signals in the channels and compare their time.
109 //TODO: Loop over all signals is not needed, since they are time-ordered.
110 Bool_t isMerged = kFALSE;
111 sigset::iterator it;
112 for (it = fAnalogBuffer[channel].begin();
113 it != fAnalogBuffer[channel].end(); it++) {
114
115 // Time between new and old signal smaller than dead time: merge signals
116 if ( TMath::Abs( (*it)->GetTime() - time ) < fDeadTime ) {
117
118 // Current implementation of merging signals:
119 // Add charges, keep first signal time
120 // TODO: Check with SSD electronics people on more realistic behaviour.
121 LOG(debug4) << GetName() << ": channel " << channel
122 << ", new signal at t = " << time
123 << " ns is merged with present signal at t = "
124 << (*it)->GetTime() << " ns";
125 (*it)->SetTime( TMath::Min( (*it)->GetTime(), time) );
126 (*it)->AddLink(charge, index, entry, file);
127 isMerged = kTRUE; // mark new signal as merged
128 LOG(debug4) << " New signal: time " << (*it)->GetTime()
129 << ", charge " << (*it)->GetCharge()
130 << ", number of links " << (*it)->GetMatch().GetNofLinks();
131 break; // Merging should be necessary only for one buffer signal
132
133 } //? Time difference smaller than dead time
134
135 } // Loop over signals in buffer for this channel
136
137 // --- If signal was merged: no further action
138 if ( isMerged ) return;
139
140 // --- Arriving here, the signal did not interfere with existing ones.
141 // --- So, it is added to the analog buffer.
142 BmnSsdSignal* signal = new BmnSsdSignal(time, charge,
143 index, entry, file);
144 fAnalogBuffer[channel].insert(signal);
145 LOG(debug4) << GetName() << ": Adding signal at t = " << time
146 << " ns, charge " << charge << " in channel " << channel;
147
148}
149// -------------------------------------------------------------------------
150
151
152
153// ----- Status of analogue buffer -------------------------------------
154void BmnSsdModule::BufferStatus(Int_t& nofSignals,
155 Double_t& timeFirst,
156 Double_t& timeLast) {
157
158
159 Int_t nSignals = 0;
160 Double_t tFirst = -1.;
161 Double_t tLast = -1.;
162 Double_t tSignal = -1.;
163
164 // --- Loop over active channels
165 for ( auto chanIt = fAnalogBuffer.begin();
166 chanIt != fAnalogBuffer.end(); chanIt++) {
167
168 // --- Loop over signals in channel
169 for ( auto sigIt = (chanIt->second).begin();
170 sigIt != (chanIt->second).end(); sigIt++) {
171
172 tSignal = (*sigIt)->GetTime();
173 nSignals++;
174 tFirst = tFirst < 0. ? tSignal : TMath::Min(tFirst, tSignal);
175 tLast = TMath::Max(tLast, tSignal);
176
177 } // signals in channel
178
179 } // channels in module
180
181 nofSignals = nSignals;
182 timeFirst = tFirst;
183 timeLast = tLast;
184
185}
186// -------------------------------------------------------------------------
187
188
189
190// ----- Convert analog charge to ADC channel number -------------------
191Int_t BmnSsdModule::ChargeToAdc(Double_t charge) {
192 if ( charge < fThreshold ) return -1;
193 Int_t adc = Int_t ( (charge - fThreshold) * Double_t(fNofAdcChannels)
194 / fDynRange );
195 return ( adc < fNofAdcChannels ? adc : fNofChannels - 1 );
196}
197// -------------------------------------------------------------------------
198
199
200
201// ----- Digitise an analogue charge signal ----------------------------
202void BmnSsdModule::Digitize(UShort_t channel, BmnSsdSignal* signal) {
203
204 // --- Check channel number
205 assert ( channel < fNofChannels );
206
207 // --- No action if charge is below threshold
208 Double_t charge = signal->GetCharge();
209 if ( charge < fThreshold ) return;
210
211 // --- Digitise charge
212 // --- Prescription according to the information on the SSD-XYTER
213 // --- by C. Schmidt.
214 UShort_t adc = 0;
215 if ( charge > fDynRange ) adc = fNofAdcChannels - 1;
216 else adc = UShort_t( (charge - fThreshold) / fDynRange
217 * Double_t(fNofAdcChannels) );
218
219 // --- Digitise time
220 Double_t deltaT = gRandom->Gaus(0., fTimeResolution);
221 Long64_t dTime = Long64_t(round(signal->GetTime() + deltaT));
222
223 // --- Send the message to the digitiser task
224 LOG(debug4) << GetName() << ": charge " << signal->GetCharge()
225 << ", dyn. range " << fDynRange << ", threshold "
226 << fThreshold << ", # ADC channels "
227 << fNofAdcChannels;
228 LOG(debug3) << GetName() << ": Sending message. Channel " << channel
229 << ", time " << dTime << ", adc " << adc;
231 if ( digitiser ) digitiser->CreateDigi(fAddress, channel, dTime, adc,
232 signal->GetMatch());
233
234 // --- If no digitiser task is present (debug mode): create a digi and
235 // --- add it to the digi buffer.
236 else {
237 LOG(fatal) << GetName() << ": no digitiser task present!";
238 }
239 return;
240}
241// -------------------------------------------------------------------------
242
243
244
245// ----- Find hits -----------------------------------------------------
246Int_t BmnSsdModule::FindHits(TClonesArray* hitArray, BmnEvent* event) {
247
248 // --- Call FindHits method in each daughter sensor
249 Int_t nHits = 0;
250 for (Int_t iSensor = 0; iSensor < GetNofDaughters(); iSensor++) {
251 BmnSsdSensor* sensor = dynamic_cast<BmnSsdSensor*>(GetDaughter(iSensor));
252 nHits += sensor->FindHits(fClusters, hitArray, event, fDeadTime);
253 }
254
255 LOG(debug2) << GetName() << ": Clusters " << fClusters.size()
256 << ", sensors " << GetNofDaughters() << ", hits "
257 << nHits;
258 return nHits;
259}
260// -------------------------------------------------------------------------
261
262
263
264// ----- Generate noise ------------------------------------------------
265Int_t BmnSsdModule::GenerateNoise(Double_t t1, Double_t t2) {
266
267 assert( t2 > t1 );
268
269 // --- Mean number of digis in [t1, t2]
270 Double_t nNoiseMean = fNoiseRate * fNofChannels * ( t2 - t1 );
271
272 // --- Sample number of noise digis
273 Int_t nNoise = gRandom->Poisson(nNoiseMean);
274
275 // --- Create noise digis
276 for (Int_t iNoise = 0; iNoise < nNoise; iNoise++) {
277
278 // --- Random channel number, time and charge
279 Int_t channel = Int_t(gRandom->Uniform(Double_t(fNofChannels)));
280 Double_t time = gRandom->Uniform(t1, t2);
281 Double_t charge = fNoiseCharge->GetRandom();
282
283 // --- Insert a signal object (without link index, entry and file)
284 // --- into the analogue buffer.
285 AddSignal(channel, time, charge, -1, -1, -1);
286
287 } //# noise digis
288
289
290 return nNoise;
291}
292// -------------------------------------------------------------------------
293
294
295
296// ----- Get the unique address from the sensor name (static) ----------
298
299 Bool_t isValid = kTRUE;
300 if ( name.Length() != 16 ) isValid = kFALSE;
301 if ( isValid) {
302 if ( ! name.BeginsWith("SSD") ) isValid = kFALSE;
303 if ( name[4] != 'U' ) isValid = kFALSE;
304 if ( name[8] != 'L' ) isValid = kFALSE;
305 if ( name[13] != 'M' ) isValid = kFALSE;
306 }
307 if ( ! isValid ) {
308 LOG(fatal) << "GetAddressFromName: Not a valid module name "
309 << name;
310 return 0;
311 }
312
313 Int_t unit = 10 * ( name[5] - '0') + name[6] - '0' - 1;
314 Int_t ladder = 10 * ( name[9] - '0') + name[10] - '0' - 1;
315 Int_t hLadder = ( name[11] == 'U' ? 0 : 1);
316 Int_t module = 10 * ( name[14] - '0') + name[15] - '0' - 1;
317
318 return BmnSsdAddress::GetAddress(unit, ladder, hLadder, module);
319}
320// -------------------------------------------------------------------------
321
322
323
324// ----- Initialise the analogue buffer ---------------------------------
326
327 for (UShort_t channel = 0; channel < fNofChannels; channel++) {
328 multiset<BmnSsdSignal*, BmnSsdSignal::Before> mset;
329 fAnalogBuffer[channel] = mset;
330 } // channel loop
331
332}
333// -------------------------------------------------------------------------
334
335
336
337// ----- Initialise daughters from geometry ----------------------------
338void BmnSsdModule::InitDaughters() {
339
340 // --- Catch absence of TGeoManager
341 assert( gGeoManager );
342
343 // --- Catch physical node not being set
344 assert ( fNode);
345
346 TGeoNode* moduleNode = fNode->GetNode(); // This node
347 TString modulePath = fNode->GetName(); // Full path to this node
348
349 for (Int_t iNode = 0; iNode < moduleNode->GetNdaughters(); iNode++) {
350
351 // Check name of daughter node for level name
352 TString daughterName = moduleNode->GetDaughter(iNode)->GetName();
353 if ( daughterName.Contains("Sensor", TString::kIgnoreCase) ) {
354
355 // Create physical node
356 TString daughterPath = modulePath + "/" + daughterName;
357 TGeoPhysicalNode* sensorNode = new TGeoPhysicalNode(daughterPath.Data());
358
359 // Get or create element from setup and add it as daughter
364 sensorNode);
365 sensor->SetMother(this);
366 fDaughters.push_back(sensor);
367
368 } //? name of daughter node contains "sensor"
369
370 } //# daughter nodes
371
372}
373// -------------------------------------------------------------------------
374
375
376
377// ----- Process the analogue buffer -----------------------------------
378Int_t BmnSsdModule::ProcessAnalogBuffer(Double_t readoutTime) {
379
380 // --- Counter
381 Int_t nDigis = 0;
382
383 // --- Time limit up to which signals are digitised and sent to DAQ.
384 // --- Up to that limit, it is guaranteed that future signals do not
385 // --- interfere with the buffered ones. The readoutTime is the time
386 // --- of the last processed SsdPoint. All coming points will be later
387 // --- in time. So, the time limit is defined by this time minus
388 // --- 5 times the time resolution (maximal deviation of signal time
389 // --- from SsdPoint time) minus the dead time, within which
390 // --- interference of signals can happen.
391 Double_t timeLimit = readoutTime - 5. * fTimeResolution - fDeadTime;
392
393 // --- Iterate over active channels
394 map<UShort_t, sigset>::iterator chanIt;
395 for (chanIt = fAnalogBuffer.begin();
396 chanIt != fAnalogBuffer.end(); chanIt++) {
397
398 // --- Digitise all signals up to the specified time limit
399 sigset::iterator sigIt = (chanIt->second).begin();
400 sigset::iterator oldIt = sigIt;
401 while ( sigIt != (chanIt->second).end() ) {
402
403 // --- Exit loop if signal time is larger than time limit
404 // --- N.b.: Readout time < 0 means digitise everything
405 if ( readoutTime >= 0. && (*sigIt)->GetTime() > timeLimit ) break;
406
407 // --- Digitise signal
408 Digitize( chanIt->first, (*sigIt) );
409 nDigis++;
410
411 // --- Increment iterator before it becomes invalid
412 oldIt = sigIt;
413 sigIt++;
414
415 // --- Delete digitised signal
416 delete (*oldIt);
417 (chanIt->second).erase(oldIt);
418
419 } // Iterate over signals in channel
420 } // Iterate over channels
421
422 return nDigis;
423}
424// -------------------------------------------------------------------------
425
426
427
428// ----- Set the module parameters -------------------------------------
429void BmnSsdModule::SetParameters(Double_t dynRange, Double_t threshold,
430 Int_t nAdc, Double_t timeResolution,
431 Double_t deadTime, Double_t noise,
432 Double_t zeroNoiseRate,
433 Double_t fracDeadChannels) {
434
435 // Assert validity of parameters
436 assert( dynRange > 0. );
437 assert( threshold > 0. );
438 assert( nAdc > 0 );
439 assert( timeResolution > 0. );
440 assert( deadTime >= 0. );
441 assert( noise >= 0. );
442 assert( zeroNoiseRate >= 0. );
443 assert( fracDeadChannels >= 0. && fracDeadChannels <= 1.);
444
445 // Set number of channels, which depends on the connected sensor.
446 // For sensor DssdStereo, it is 2 * number of strips
447 assert(GetDaughter(0)); // check whether a sensor is attached
448 TString dType = GetDaughter(0)->GetTitle();
449 if ( dType.BeginsWith("Dssd") ) {
450 BmnSsdSensorDssd* sensor =
451 dynamic_cast<BmnSsdSensorDssd*>(GetDaughter(0));
452 Int_t nStripsF = sensor->GetNofStrips(0); // front side
453 Int_t nStripsB = sensor->GetNofStrips(1); // back side
454 fNofChannels = 2 * TMath::Max(nStripsF, nStripsB);
455 }
456 else {
457 LOG(fatal) << GetName() << ": No sensor connected!";
458 return;
459 }
460
461 // Set other parameters
462 fDynRange = dynRange;
463 fThreshold = threshold;
464 fNofAdcChannels = nAdc;
465 fTimeResolution = timeResolution;
466 fDeadTime = deadTime;
467 fNoise = noise;
468 fZeroNoiseRate = zeroNoiseRate;
469
470 // Calculate noise rate and prepare function for noise sampling
471 fNoiseRate = 0.5 * fZeroNoiseRate
472 * TMath::Exp( -0.5 * fThreshold * fThreshold / (fNoise * fNoise) );
473 fNoiseCharge = new TF1("Noise Charge", "TMath::Gaus(x, [0], [1])",
474 threshold, 10. * noise);
475 fNoiseCharge->SetParameters(0., noise);
476
477 // Determine dead channels, if necessary
478 if ( fracDeadChannels > 0.) SetDeadChannels(fracDeadChannels);
479
480 // Initialise the analogue buffer
482
483 // Mark the module initialised
484 fIsSet = kTRUE;
485}
486// -------------------------------------------------------------------------
487
488
489
490// ----- Create list of dead channels ----------------------------------
491Int_t BmnSsdModule::SetDeadChannels(Double_t percentage) {
492
493 Double_t fraction = percentage;
494
495 // --- Catch illegal percentage values
496 if ( percentage < 0. ) {
497 LOG(WARNING) << GetName() << ": illegal percentage of dead channels "
498 << percentage << ", is set to 0.";
499 fraction = 0.;
500 }
501 if ( percentage > 100. ) {
502 LOG(WARNING) << GetName() << ": illegal percentage of dead channels "
503 << percentage << ", is set to 100.";
504 fraction = 100.;
505 }
506
507 // --- Re-set dead channel list
508 fDeadChannels.clear();
509
510 // --- Number of dead channels
511 UInt_t nOfDeadChannels = fraction * fNofChannels / 100;
512
513 // --- Case percentage < 50: randomise inactive channels
514 // --- N.b.: catches also zero fraction (nOfDeadChannels = 0)
515 // --- N.b.: set::insert has no effect if element is already present
516 if ( nOfDeadChannels < (fNofChannels / 2) ) {
517 while ( fDeadChannels.size() < nOfDeadChannels )
518 fDeadChannels.insert( Int_t( gRandom->Uniform(fNofChannels) ) );
519 }
520
521 // --- Case percentage > 50: randomise active channels
522 // --- N.b.: catches also unity fraction (nOfDeadChannels = fNofChannels)
523 // --- N.b.: set::erase has no effect if element is not present
524 else {
525 for (Int_t channel = 0; channel < fNofChannels; channel++)
526 fDeadChannels.insert(channel);
527 while ( fDeadChannels.size() > nOfDeadChannels )
528 fDeadChannels.erase( Int_t ( gRandom->Uniform(fNofChannels) ) );
529 }
530
531 return fDeadChannels.size();
532}
533// -------------------------------------------------------------------------
534
535
536
537// ----- String output -------------------------------------------------
539 stringstream ss;
540 ss << "Module " << GetName() << ": dynRange " << fDynRange
541 << "e, thresh. " << fThreshold << "e, nAdc " << fNofAdcChannels
542 << ", time res. " << fTimeResolution << "ns, dead time "
543 << fDeadTime << "ns, noise " << fNoise << "e, zero noise rate "
544 << fZeroNoiseRate << "/ns, dead chan. " << fDeadChannels.size()
545 << " / " << fNofChannels;
546 return ss.str();
547}
548// -------------------------------------------------------------------------
@ kSsdSensor
@ kSsdModule
Data class for SSD clusters.
Class characterising one event by a collection of links (indices) to data objects,...
Definition BmnEvent.h:26
Task class for simulating the detector response of the SSD.
void CreateDigi(Int_t address, UShort_t channel, Long64_t time, UShort_t adc, const BmnMatch &match)
Class representing an element of the SSD setup.
TGeoPhysicalNode * fNode
Pointer to geometry.
std::vector< BmnSsdElement * > fDaughters
Array of daughters.
Int_t GetNofDaughters() const
Int_t fAddress
Unique element address.
BmnSsdElement * GetDaughter(Int_t index) const
void SetMother(BmnSsdElement *mother)
Int_t ProcessAnalogBuffer(Double_t readoutTime)
void AddSignal(UShort_t channel, Double_t time, Double_t charge, Int_t index=0, Int_t entry=0, Int_t file=0)
std::string ToString() const
Int_t ChargeToAdc(Double_t charge)
void InitAnalogBuffer()
static Int_t GetAddressFromName(TString name)
Get the address from the module name (static)
Int_t GenerateNoise(Double_t t1, Double_t t2)
Generate noise.
virtual ~BmnSsdModule()
void SetParameters(Double_t dynRange, Double_t threshold, Int_t nAdc, Double_t timeResolution, Double_t deadTime, Double_t noise, Double_t zeroNoiseRate, Double_t fracDeadChannels=0.)
BmnSsdModule(UInt_t address=0, TGeoPhysicalNode *node=nullptr, BmnSsdElement *mother=nullptr)
Int_t FindHits(TClonesArray *hitArray, BmnEvent *event=NULL)
void BufferStatus(Int_t &nofSignals, Double_t &timeFirst, Double_t &timeLast)
Double_t AdcToCharge(UShort_t adcChannel)
Class describing double-sided silicon strip sensors.
virtual Int_t GetNofStrips(Int_t side) const =0
Number of strips on front and back side.
Class representing an instance of a sensor in the BMN-SSD.
virtual Int_t FindHits(std::vector< BmnSsdCluster * > &clusters, TClonesArray *hitArray, BmnEvent *event, Double_t dTime)=0
static BmnSsdSetup * Instance()
BmnSsdDigitize * GetDigitizer() const
Definition BmnSsdSetup.h:68
BmnSsdSensor * AssignSensor(Int_t address, TGeoPhysicalNode *node=nullptr)
Data class for an analog signal in the SSD.
const BmnMatch & GetMatch() const
Double_t GetTime() const
Double_t GetCharge() const
Int_t SetElementId(Int_t address, Int_t level, UInt_t newId)
Set the index of an element, leaving the other element levels untouched.
Int_t GetAddress(UInt_t unit=0, UInt_t ladder=0, UInt_t halfladder=0, UInt_t module=0, UInt_t sensor=0, UInt_t side=0, UInt_t version=kCurrentVersion)
Construct address.
STL namespace.