Belle II Software development
CDCTrigger3DFitterModule.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8#include "trg/cdc/modules/fitting/CDCTrigger3DFitterModule.h"
9
10#include <framework/datastore/RelationVector.h>
11#include <cdc/dataobjects/WireID.h>
12#include <cdc/geometry/CDCGeometryPar.h>
13
14using namespace std;
15using namespace Belle2;
16
17//this line registers the module with the framework and actually makes it available
18//in steering files or the the module list (basf2 -m).
19REG_MODULE(CDCTrigger3DFitter);
20
22{
24 "The 3D fitter module of the CDC trigger.\n"
25 "1. Selects stereo hits around a given 2D track by Hough voting\n"
26 "2. Performs a linear fit in the s-z plane (s: 2D arclength).\n"
27 );
29 addParam("CDCHitCollectionName", m_CDCHitCollectionName,
30 "Name of the input StoreArray of CDCHits.",
31 string("CDCHits4Trg"));
32 addParam("TSHitCollectionName", m_TSHitCollectionName,
33 "Name of the input StoreArray of CDCTriggerSegmentHits.",
34 string(""));
35 addParam("EventTimeName", m_EventTimeName,
36 "Name of the event time object.",
37 string(""));
38 addParam("inputCollectionName", m_inputCollectionName,
39 "Name of the StoreArray holding the input tracks from the 2D finder.",
40 string("TRGCDC2DFinderTracks"));
41 addParam("outputCollectionName", m_outputCollectionName,
42 "Name of the StoreArray holding the 3D output tracks.",
43 string("TRGCDC3DFitterTracks"));
44 addParam("minVoteCounts", m_minVoteCount,
45 "Minimal number of votes in Hough voting.",
46 8);
47}
48
49void
51{
52 // register DataStore elements
57 // register relations
61
62
63 // get CDC geometry constants
64
65 // load parameters from database
67
68 m_xtCurve.resize(m_nStereoLayer, vector<double>(m_maxDriftTime));
69 m_nWire.resize(m_nStereoLayer, 0);
70 m_rWire.resize(m_nStereoLayer, 0);
71 m_phiBW.resize(m_nStereoLayer, vector<double>(cdc.getMaxNumberOfCellsPerLayer()));
72 m_zBW.resize(m_nStereoLayer, 0);
73 m_stereoAngle.resize(m_nStereoLayer, 0);
74
75 for (int iLayer = 0; iLayer < m_nStereoLayer; ++iLayer) {
76 // stereo layer ID (0--19) -> continuous layer ID (0--55)
77 int iStereoSuperLayer = iLayer / m_nLayerInSuperLayer;
78 int iLayerInSL = iLayer % m_nLayerInSuperLayer;
79 int iCLayer = 8 + 12 * iStereoSuperLayer + iLayerInSL;
80
81 // get x-t curve
82 for (int iTick = 0; iTick < (int)m_xtCurve[iLayer].size(); ++iTick) {
83 double t = iTick * 2 * cdc.getTdcBinWidth();
84 // take the average of left & right
85 double driftLength_left = cdc.getDriftLength(t, iCLayer, 0);
86 double driftLength_right = cdc.getDriftLength(t, iCLayer, 1);
87 m_xtCurve[iLayer][iTick] = (driftLength_left + driftLength_right) / 2;
88 }
89
90 // get the number of wires
91 m_nWire[iLayer] = cdc.nWiresInLayer(iCLayer);
92
93 // get the wire radius
94 m_rWire[iLayer] = cdc.senseWireR(iCLayer);
95
96 // get phi coordinate of the backward endpoint of the wire
97 for (int iWire = 0; iWire < m_nWire[iLayer]; iWire++) {
98 const B2Vector3D& wirePosBW = cdc.wireBackwardPosition(iCLayer, iWire);
99 m_phiBW[iLayer][iWire] = wirePosBW.Phi();
100 }
101
102 // get z coordinate of the of the backward endpoint of the wire
103 // get stereo angle
104 const B2Vector3D& wirePosB = cdc.wireBackwardPosition(iCLayer, 0);
105 const B2Vector3D& wirePosF = cdc.wireForwardPosition(iCLayer, 0);
106 const B2Vector3D wireDirection = wirePosF - wirePosB;
107 m_zBW[iLayer] = wirePosB.Z();
108 m_stereoAngle[iLayer] = wireDirection.Theta();
109 if (wirePosF.Phi() < wirePosB.Phi())m_stereoAngle[iLayer] *= -1;
110 }
111}
112
113void
115{
116 for (int iTrack = 0; iTrack < m_tracks2D.getEntries(); ++iTrack) {
117 // get 2D variable
118 double phi0 = m_tracks2D[iTrack]->getPhi0(); // (rad)
119 double omega = m_tracks2D[iTrack]->getOmega(); // curvature (cm^-1)
120
121 vector<vector<CDCHit*>> preselectedHits = preselector(phi0, omega);
122
123 vector<vector<double>> sCand = sConverter(preselectedHits, omega);
124 vector<vector<double>> zCand = zConverter(preselectedHits, phi0, omega);
125
126 HoughVoter voter = HoughVoter();
127 voter.vote(sCand, zCand);
128 auto [z0Voter, cotVoter, voteCount] = voter.findPeak();
129
130 // skip if vote count is not enough
131 if (voteCount < m_minVoteCount) {
132 B2DEBUG(100, "vote count is lower than threshold (in 3D Fitter)");
133 continue;
134 }
135
136 auto [s, z] = selector(sCand, zCand, z0Voter, cotVoter);
137
138 auto [z0, cot] = fitter(s, z);
139
140 // check if fit results are valid
141 if (isnan(z0) || isnan(cot)) {
142 B2DEBUG(100, "3D fit failed");
143 continue;
144 }
145
146 // save track
147 CDCTriggerTrack* fittedTrack =
148 m_tracks3D.appendNew(m_tracks2D[iTrack]->getPhi0(), m_tracks2D[iTrack]->getOmega(),
149 m_tracks2D[iTrack]->getChi2D(),
150 z0, cot, 0);
151 // make relation to 2D track
152 m_tracks2D[iTrack]->addRelationTo(fittedTrack);
153 } // end iTrack loop
154}
155
156// major functions
157
158vector<vector<CDCHit*>>
159 CDCTrigger3DFitterModule::preselector(double phi0, double omega)
160{
161 vector<vector<CDCHit*>> preselectedHits(m_nStereoLayer, vector<CDCHit*>());
162
163 vector<int> iWireBeginList = getIWireBegin(phi0, omega);
164
165 // judge if each hit is in the preselection range
166 for (int iTS = 0; iTS < m_TSHits.getEntries(); iTS++) {
168 if (TS->getISuperLayer() % 2 == 0)continue; // skip axial TS
169
171
172 vector<int> iHitInEachLayer = select5Cells(TS);
173
174 for (int iHit : iHitInEachLayer) {
175 if (iHit == -1)continue;
176
177 int iLayer = getIStereoLayer(hits[iHit]->getICLayer());
178 int iWire = hits[iHit]->getIWire();
179
180 if (iWireBeginList[iLayer] == -1) continue;
181
182 if (iWireBeginList[iLayer] + 10 - 1 < m_nWire[iLayer]) { // preselection range doesn't cross over iWire = 0
183 if (iWireBeginList[iLayer] <= iWire && iWire < iWireBeginList[iLayer] + 10) {
184 preselectedHits[iLayer].push_back(hits[iHit]);
185 }
186 } else { // preselection range crosses over iWire = 0
187 if (iWireBeginList[iLayer] <= iWire || iWire < (iWireBeginList[iLayer] + 10) % m_nWire[iLayer]) {
188 preselectedHits[iLayer].push_back(hits[iHit]);
189 }
190 }
191 }
192 }
193 return preselectedHits;
194}
195
196vector<vector<double>>
197 CDCTrigger3DFitterModule::sConverter(const vector<vector<CDCHit*>>& preselectedHits, double omega)
198{
199 vector<vector<double>> s(m_nStereoLayer, vector<double>());
200
201 for (int iLayer = 0; iLayer < m_nStereoLayer; iLayer++) {
202 for (int iHit = 0; iHit < (int)preselectedHits[iLayer].size(); iHit++) {
203 for (int lr = 1; lr <= 2; lr++) {
204 double _s = 2 / omega * asin(1.0 / 2 * m_rWire[iLayer] * omega);
205 s[iLayer].push_back(_s);
206 }
207 }
208 }
209 return s;
210}
211
212vector<vector<double>>
213 CDCTrigger3DFitterModule::zConverter(const vector<vector<CDCHit*>>& preselectedHits, double phi0, double omega)
214{
215 vector<vector<double>> z(m_nStereoLayer, vector<double>());
216
217 for (int iLayer = 0; iLayer < m_nStereoLayer; iLayer++) {
218 for (CDCHit* hit : preselectedHits[iLayer]) {
219 double driftLength = getDriftLength(*hit);
220
221 double phiCross = calPhiCross(m_rWire[iLayer], phi0, omega);
222 for (int lr = 1; lr <= 2; lr++) {
223 double phiHit;
224 if (lr == 1) { // right
225 phiHit = phiCross + driftLength / m_rWire[iLayer];
226 } else if (lr == 2) { // left
227 phiHit = phiCross - driftLength / m_rWire[iLayer];
228 }
229 double deltaPhi = normalizeAngle(phiHit - m_phiBW[iLayer][hit->getIWire()]);
230 double _z = m_zBW[iLayer] + m_rWire[iLayer] * deltaPhi / tan(m_stereoAngle[iLayer]);
231 z[iLayer].push_back(_z);
232 }
233 }
234 }
235 return z;
236}
237
238CDCTrigger3DFitterModule::HoughVoter::HoughVoter()
239{
240 votingCell.resize(nCellZ0, vector<int>(nCellZ0, 0));
241}
242
243void
244CDCTrigger3DFitterModule::HoughVoter::vote(const vector<vector<double>>& sCand, const vector<vector<double>>& zCand)
245{
246 // iCot=0(minCot) ... iCot=m(maxCot)
247 // iZ0=0(maxZ0)
248 // ...
249 // iZ0=n(minZ0)
250 for (int iLayer = 0; iLayer < m_nStereoLayer; iLayer++) {
251 for (int iHit = 0; iHit < (int)sCand[iLayer].size(); iHit++) {
252 double s = sCand[iLayer][iHit];
253 double z = zCand[iLayer][iHit];
254
255 for (int iCot = 0; iCot < nCellCot; iCot++) {
256 int iZ0Shift = floor((z - (-cellWidthZ0 / 2)) / cellWidthZ0);
257
258 double cotLower = getCotCellValue(iCot - 0.5);
259 double z0Lower = -s * cotLower;
260 int iZ0Lower = digitizeZ0(z0Lower) - iZ0Shift;
261
262 double cotUpper = getCotCellValue(iCot + 0.5);
263 double z0Upper = -s * cotUpper;
264 int iZ0Upper = digitizeZ0(z0Upper) - iZ0Shift;
265
266 for (int iZ0 = max(iZ0Lower, 0); iZ0 < min(iZ0Upper + 1, nCellZ0); iZ0++) {
267 votingCell[iZ0][iCot] |= (1 << iLayer);
268 }
269 }
270 }
271 }
272}
273
274tuple<double, double, int>
275CDCTrigger3DFitterModule::HoughVoter::findPeak()
276{
277 int maxCount = 0;
278 double z0, cot;
279 for (int iZ0 = 0; iZ0 < nCellZ0; iZ0++) {
280 for (int iCot = nCellCot - 1; iCot >= 0; iCot--) {
281 int count = __builtin_popcount(votingCell[iZ0][iCot]);
282 if (maxCount <= count) {
283 maxCount = count;
284 z0 = getZ0CellValue(iZ0);
285 cot = getCotCellValue(iCot);
286 }
287 }
288 }
289 return make_tuple(z0, cot, maxCount);
290}
291
292pair<vector<double>, vector<double>>
293 CDCTrigger3DFitterModule::selector(const vector<vector<double>>& sCand, const vector<vector<double>>& zCand, double z0, double cot)
294{
295 vector<double> sSelected(m_nStereoLayer, -1); // s is always positive, so -1 means no hit
296 vector<double> zSelected(m_nStereoLayer, -1);
297
298 for (int iLayer = 0; iLayer < m_nStereoLayer; iLayer++) {
299 double minDeltaZ = m_maxZSelection;
300 for (int iHit = 0; iHit < (int)sCand[iLayer].size(); iHit++) {
301 double zVoter = sCand[iLayer][iHit] * cot + z0;
302 double deltaZ = abs(zCand[iLayer][iHit] - zVoter);
303 if (deltaZ < minDeltaZ) {
304 minDeltaZ = deltaZ;
305 sSelected[iLayer] = sCand[iLayer][iHit];
306 zSelected[iLayer] = zCand[iLayer][iHit];
307 }
308 }
309 }
310
311 return make_pair(sSelected, zSelected);
312}
313
314pair<double, double>
315CDCTrigger3DFitterModule::fitter(const vector<double>& s, const vector<double>& z)
316{
317 // sigma parameter(=wire resolution) is set as 0 to reduce the calculation in firmware
318 double nHit = 0, sum_s = 0, sum_z = 0, sum_sz = 0, sum_ss = 0;
319 for (int iLayer = 0; iLayer < m_nStereoLayer; iLayer++) {
320 // skip no hit layer
321 if (s[iLayer] < 0)continue;
322
323 nHit += 1;
324 sum_s += s[iLayer];
325 sum_z += z[iLayer];
326 sum_sz += s[iLayer] * z[iLayer];
327 sum_ss += s[iLayer] * s[iLayer];
328 }
329
330 double denominator = nHit * sum_ss - sum_s * sum_s;
331 double z0 = (-sum_s * sum_sz + sum_ss * sum_z) / denominator;
332 double cot = (nHit * sum_sz - sum_s * sum_z) / denominator;
333 return make_pair(z0, cot);
334}
335
336
337// minor functions
338
339int
341{
342 int iStereoSuperLayer = (iContinuousLayer - 8) / 12;
343 int iLayerInSL = iContinuousLayer - 8 - 12 * iStereoSuperLayer;
344 return iStereoSuperLayer * m_nLayerInSuperLayer + iLayerInSL;
345}
346
347double
349{
350 return atan2(sin(angle), cos(angle));
351}
352
353double
354CDCTrigger3DFitterModule::calPhiCross(double r, double phi0, double omega)
355{
356 // Based on Sara's thesis P84 eq.(6.4)
357 // https://docs.belle2.org/record/823/
358 double phiCross = phi0 - asin(1.0 / 2 * r * omega);
359 return normalizeAngle(phiCross);
360}
361
362vector<int>
364{
365 vector<int> iWireBeginList(m_nStereoLayer, -1);
366
367 for (int iLayer = 0; iLayer < m_nStereoLayer; iLayer++) {
368 int iStereoSuperLayer = iLayer / m_nLayerInSuperLayer;
369 int iLayerInSL = iLayer % m_nLayerInSuperLayer;
370
371 // Skip if track and hit don't intersect
372 if (abs(1.0 / 2 * m_rWire[iLayer] * omega) > 1) continue;
373
374 double phiCross = calPhiCross(m_rWire[iLayer], phi0, omega);
375 if (phiCross < 0) phiCross += 2 * M_PI; // phiCross is in [0, 2pi]
376 // convert phi -> wire ID
377 double iWireCross = phiCross / (2 * M_PI) * m_nWire[iLayer];
378 if (iLayerInSL % 2 == 1) iWireCross -= 0.5; // odd layer is shifted by the half cell
379
380 // iWireBegin <= iWireCross < iWireEnd
381 int iWireBegin;
382 if (iStereoSuperLayer == 0 || iStereoSuperLayer == 2) { // phiFW > phiBW
383 int iWireEnd;
384 iWireEnd = (int)(ceil(iWireCross)) % m_nWire[iLayer];
385 iWireBegin = (iWireEnd - 10 + m_nWire[iLayer]) % m_nWire[iLayer];
386 } else { // phiBW > phiFW
387 iWireBegin = (int)(ceil(iWireCross)) % m_nWire[iLayer];
388 }
389 iWireBeginList[iLayer] = iWireBegin;
390 }
391 return iWireBeginList;
392}
393
394vector<int>
396{
397 int priorityILayer = WireID(TS->getID()).getICLayer();
398 int priorityIWire = TS->getIWire();
400
401 // fill iHit of each cell
402 vector<int> iHitInEachCell(m_nCellInTS, -1);
403 for (int iHit = 0; iHit < (int)hits.size(); iHit++) {
404 CDCHit hit = *hits[iHit];
405
406 // get iTSCell
407 // 10 9 8
408 // 7 6
409 // 5
410 // 4 3
411 // 2 1 0
412
413 int iLayer = getIStereoLayer(hit.getICLayer());
414
415 // get relative position of the wire to the priority wire
416 int diffILayer = hit.getICLayer() - priorityILayer;
417 int diffIWire = hit.getIWire() - priorityIWire;
418 if (diffIWire < -2) diffIWire += m_nWire[iLayer];
419 if (diffIWire > 2) diffIWire -= m_nWire[iLayer];
420
421 // 2nd priority case
422 if (TS->getPriorityPosition() == 1) { // upper right
423 diffILayer += 1;
424 diffIWire -= 1;
425 } else if (TS->getPriorityPosition() == 2) { // upper left
426 diffILayer += 1;
427 }
428
429 int iTSCell = m_cellIDInTS[make_pair(diffILayer, diffIWire)];
430 iHitInEachCell[iTSCell] = iHit;
431 }
432
433 // select
434 vector<int> iHitInEachLayer(m_nLayerInSuperLayer, -1);
435 vector<vector<int>> selectOrder{{1, 0, 2}, {3, 4}, {5}, {7, 6}, {9, 10, 8}};
436 for (int iLayerInSL = 0; iLayerInSL < m_nLayerInSuperLayer; iLayerInSL++) {
437 for (int iCell : selectOrder[iLayerInSL]) {
438 if (iHitInEachCell[iCell] == -1) continue;
439 iHitInEachLayer[iLayerInSL] = iHitInEachCell[iCell];
440 break;
441 }
442 }
443
444 return iHitInEachLayer;
445}
446
447double
449{
450 ;
452
453 double driftTime = 0;
454 if (m_eventTime->hasBinnedEventT0(Const::CDC)) {
455 double T0 = m_eventTime->getBinnedEventT0(Const::CDC); // ns
456 T0 = T0 / 2; // TRGCLK
457 int hitTime = (int)floor((cdc.getT0(WireID(hit.getID())) / cdc.getTdcBinWidth() - hit.getTDCCount() + 0.5) / 2); // TRGCLK
458 // reference: CDCTriggerTSFModule.cc:431
459 driftTime = hitTime - T0;
460 }
461 if (driftTime < 0)driftTime = 0;
462 if (driftTime >= m_maxDriftTime)driftTime = m_maxDriftTime - 1;
463
464 int iLayer = getIStereoLayer(hit.getICLayer());
465 return m_xtCurve[iLayer][driftTime];
466}
DataType Phi() const
The azimuth angle.
Definition: B2Vector3.h:151
DataType Z() const
access variable Z (= .at(2) without boundary check)
Definition: B2Vector3.h:435
DataType Theta() const
The polar angle.
Definition: B2Vector3.h:153
bool hasBinnedEventT0(const Const::EDetector detector) const
Check if one of the detectors in the given set has a binned t0 estimation.
int getBinnedEventT0(const Const::EDetector detector) const
Return the stored binned event t0 for the given detector or 0 if nothing stored.
Class containing the result of the unpacker in raw data and the result of the digitizer in simulation...
Definition: CDCHit.h:40
std::string m_EventTimeName
name of the event time StoreObjPtr
std::pair< double, double > fitter(const std::vector< double > &s, const std::vector< double > &z)
Performance linear fitting with the selected s and z.
StoreArray< CDCTriggerTrack > m_tracks2D
list of 2D input tracks
std::vector< std::vector< double > > m_xtCurve
CDC geometry constants.
std::vector< std::vector< CDCHit * > > preselector(double phi0, double omega)
Select 10 wires which crosses with the given 2D track.
std::string m_CDCHitCollectionName
Name of the StoreArray containing the input CDChits.
virtual void initialize() override
Initialize the module and register DataStore arrays.
CDCTrigger3DFitterModule()
Constructor, for setting module description and parameters.
virtual void event() override
Run the 3D fitter for an event.
std::string m_outputCollectionName
Name of the StoreArray containing the resulting 3D tracks.
std::vector< std::vector< double > > zConverter(const std::vector< std::vector< CDCHit * > > &preselectedHits, double phi0, double omega)
Calculate z at the hit position.
StoreArray< CDCTriggerSegmentHit > m_TSHits
list of track segment hits
double getDriftLength(const CDCHit hit)
Get drift length.
double calPhiCross(double r, double phi0, double omega)
Calculate phi coordinate of the crossing point of the given radius and the given 2D track.
std::string m_inputCollectionName
Name of the StoreArray containing the input tracks from the 2D fitter.
double normalizeAngle(double angle)
Convert the given angle(rad) to the range [-pi, pi].
int getIStereoLayer(int iContinuousLayer)
Convert continuous layer ID (0–55) -> stereo layer ID (0–19).
std::string m_TSHitCollectionName
Name of the StoreArray containing the input track segment hits.
StoreArray< CDCTriggerTrack > m_tracks3D
list of 3D output tracks
std::vector< int > select5Cells(const CDCTriggerSegmentHit *TS)
1 cell is selected in each layer to reduce LUT comsumption.
int m_minVoteCount
Minimal number of votes in Hough voting.
StoreObjPtr< BinnedEventT0 > m_eventTime
StoreObjPtr containing the event time.
std::vector< std::vector< double > > sConverter(const std::vector< std::vector< CDCHit * > > &preselectedHits, double omega)
Calculate s(arc length of the 2D track) at the hit position.
std::pair< std::vector< double >, std::vector< double > > selector(const std::vector< std::vector< double > > &sCand, const std::vector< std::vector< double > > &zCand, double z0, double cot)
Select the nearest hits from the voter result.
std::vector< int > getIWireBegin(double phi0, double omega)
Get the beginning wire ID of the preselection range(10 wires) for each layer.
Combination of several CDCHits to a track segment hit for the trigger.
unsigned short getPriorityPosition() const
get position of the priority cell within the track segment (0: no hit, 3: 1st priority,...
unsigned short getIWire() const
get wire number of priority wire within layer.
unsigned short getID() const
get the encoded wire number of the priority wire.
unsigned short getISuperLayer() const
get super layer number.
Track created by the CDC trigger.
The Class for CDC Geometry Parameters.
static CDCGeometryPar & Instance(const CDCGeometry *=nullptr)
Static method to get a reference to the CDCGeometryPar instance.
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
Class for type safe access to objects that are referred to in relations.
RelationVector< TO > getRelationsTo(const std::string &name="", const std::string &namedRelation="") const
Get the relations that point from this object to another store array.
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
bool requireRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, const std::string &namedRelation="") const
Produce error if no relation from this array to 'toArray' has been registered.
Definition: StoreArray.h:155
T * appendNew()
Construct a new T object at the end of the array.
Definition: StoreArray.h:246
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:216
bool registerRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut, const std::string &namedRelation="") const
Register a relation to the given StoreArray.
Definition: StoreArray.h:140
Class to identify a wire inside the CDC.
Definition: WireID.h:34
unsigned short getICLayer() const
Getter for continuous layer numbering.
Definition: WireID.cc:24
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.
STL namespace.