Hall A ROOT/C++ Analyzer (podd)
Loading...
Searching...
No Matches
THaCodaData.cxx
Go to the documentation of this file.
1
2//
3// THaCodaData
4// Abstract class of CODA data.
5//
6// THaCodaData is an abstract class of CODA data. Derived
7// classes will be typically either a CODA file (a disk
8// file) or a connection to the ET system. Public methods
9// allow to open (i.e. set up), read, write, etc.
10//
11// author Robert Michaels (rom@jlab.org)
12//
14
15#include "THaCodaData.h"
16#include "Helper.h"
17#include "TMath.h"
18#include "evio.h"
19#include <cassert>
20#include <iostream>
21#include <algorithm>
22
23using namespace std;
24
25namespace Decoder {
26
27//_____________________________________________________________________________
29 : handle{0}
30 , verbose{1}
31 , fIsGood{true}
32{}
33
34//_____________________________________________________________________________
36{
37 // Get CODA version from current data source
38 int32_t EvioVersion = 0;
39 int status = evIoctl(handle, (char*)"v", &EvioVersion);
40 fIsGood = (status == S_SUCCESS);
41 if( status != S_SUCCESS ) {
42 staterr("ioctl",status);
43 codaClose();
44 return -1;
45 }
46 //TODO debug message
47 //cout << "Evio file EvioVersion = "<< EvioVersion << endl;
48 return (EvioVersion < 4) ? 2 : 3;
49}
50
51//_____________________________________________________________________________
52void THaCodaData::staterr(const char* tried_to, Int_t status) const
53{
54 // staterr gives the non-expert user a reasonable clue
55 // of what the status returns from evio mean.
56 // Note: severe errors can cause job to exit(0)
57 // and the user has to pay attention to why.
58 if (status == S_SUCCESS) return; // everything is fine.
59 if (status == EOF) {
60 if (verbose > 0)
61 cout << endl << "Normal end of file " << filename << " encountered"
62 << endl;
63 return;
64 }
65 cerr << Form("THaCodaFile: ERROR while trying to %s %s: ",
66 tried_to, filename.Data());
67 switch( static_cast<UInt_t>(status) ) {
68 case S_EVFILE_TRUNC :
69 cerr << "Truncated event on file read. Evbuffer size is too small. "
70 << endl;
71 break;
72 case S_EVFILE_BADBLOCK :
73 cerr << "Bad block number encountered " << endl;
74 break;
75 case S_EVFILE_BADHANDLE :
76 cerr << "Bad handle (file/stream not open) " << endl;
77 break;
78 case S_EVFILE_ALLOCFAIL :
79 cerr << "Failed to allocate event I/O" << endl;
80 break;
81 case S_EVFILE_BADFILE :
82 cerr << "File format error" << endl;
83 break;
84 case S_EVFILE_UNKOPTION :
85 case S_EVFILE_BADARG :
86 case S_EVFILE_BADMODE :
87 cerr << "Unknown function argument or option" << endl;
88 break;
89 case S_EVFILE_UNXPTDEOF :
90 cerr << "Unexpected end of file while reading event" << endl;
91 break;
92 case S_EVFILE_BADSIZEREQ :
93 cerr << "Invalid buffer size request to evIoct" << endl;
94 break;
95#if defined(S_EVFILE_BADHEADER)
96 case S_EVFILE_BADHEADER :
97 cerr << "Invalid bank/segment header data" << endl;
98 break;
99#endif
100 default:
101 cerr << "Unknown error " << hex << status << dec << endl;
102 break;
103 }
104}
105
106//_____________________________________________________________________________
108{
109 // Convert EVIO return codes to THaCodaData codes
110
111 switch( static_cast<UInt_t>(evio_retcode) ) {
112
113 case S_SUCCESS:
114 return CODA_OK;
115
116 case static_cast<UInt_t>(EOF):
117 return CODA_EOF;
118
119 case S_EVFILE_UNXPTDEOF:
120 case S_EVFILE_TRUNC:
121#if defined(S_EVFILE_BADHEADER)
122 case S_EVFILE_BADHEADER:
123#endif
124 return CODA_ERROR;
125
126 case S_EVFILE_BADBLOCK:
127 case S_EVFILE_BADHANDLE:
128 case S_EVFILE_ALLOCFAIL:
129 case S_EVFILE_BADFILE:
130 case S_EVFILE_BADSIZEREQ:
131 return CODA_FATAL;
132
133 // The following indicate a programming error and so should be trapped
134 case S_EVFILE_UNKOPTION:
135 case S_EVFILE_BADARG:
136 case S_EVFILE_BADMODE:
137 assert( false );
138 return CODA_FATAL;
139
140 default:
141 return CODA_ERROR;
142 }
143}
144
145//=============================================================================
146static constexpr UInt_t kMaxBufSize = kMaxUInt / 16; // 1 GiB sanity size limit
147static constexpr UInt_t kInitBufSize = 1024; // 4 kiB initial size
148
149// Dynamic event buffer class
151 fBuffer(std::max(std::min(initial_size, kMaxBufSize), kInitBufSize)),
152 fMaxSaved(11), // make this odd for faster calculation of median
153 fNevents(0),
154 fUpdateInterval(1000),
155 fChanged(false),
156 fDidGrow(false)
157{
158 fSaved.reserve(fMaxSaved);
159}
160
161//_____________________________________________________________________________
162// Record fMaxSaved largest event sizes
164{
165 UInt_t evtsize = fBuffer[0] + 1;
166
167 if( fNevents < fMaxSaved || fSaved.empty() ) {
168 fSaved.push_back(evtsize);
169 fChanged = true;
170 } else {
171 auto it = min_element(ALL(fSaved));
172 assert(it != fSaved.end());
173 if( evtsize > *it ) {
174 fChanged = true;
175 if( fSaved.size() < fMaxSaved )
176 fSaved.push_back(evtsize);
177 else
178 *it = evtsize;
179 }
180 }
181
182 ++fNevents;
183 fDidGrow = false;
184}
185
186//_____________________________________________________________________________
187// Calculate median of values in vector 'vec'. Partially reorders 'vec'
188template<typename T, typename Alloc>
189static T Median( vector<T, Alloc>& vec )
190{
191 if( vec.empty() ) {
192 return T{};
193 }
194 auto n = vec.size() / 2;
195 nth_element( vec.begin(), vec.begin()+n, vec.end() );
196 auto med = vec[n];
197 if( !(vec.size() & 1) ) {
198 // even number of elements
199 auto med2 = *max_element( vec.begin(), vec.begin()+n );
200 assert(med2 <= med);
201 med = (med2 + med) / 2;
202 }
203 return med;
204}
205
206//_____________________________________________________________________________
207// Evaluate whether event buffer is too large based on event size history.
208// Shrink if necessary.
210{
211 // Detect outliers in the group of largest event sizes. To arrive at a
212 // robust estimate, use median absolute deviation (MAD), not r.m.s., as the
213 // measure of dispersion. See, for example, https://arxiv.org/abs/1910.00229.
214
215 // Find median of saved event sizes.
216 UInt_t median = Median(fSaved);
217
218 // Calculate absolute deviations from median: devs[i] = |x[i]-median|
220 transform(ALL(fSaved), devs.begin(), [median]( UInt_t elem ) -> UInt_t {
221 return std::abs((Long64_t)elem - (Long64_t)median);
222 });
223
224 // Calculate MAD and scale by the conventional normalization factor
225 UInt_t MAD = TMath::Nint(1.4826 * Median(devs));
226
227 // Take largest non-outlier event size as estimate of the needed buffer size
228 UInt_t max_regular_event_size = median;
229 if( MAD > 0 ) {
230 constexpr UInt_t lambda = 3; // outlier boundary in units of MADs
231 auto n = fSaved.size()/2;
232 sort(fSaved.begin()+n, fSaved.end());
233 for( auto rt = fSaved.rbegin(); rt != fSaved.rbegin()+n; ++rt ) {
234 UInt_t elem = *rt;
235 if( elem <= median + lambda * MAD ) {
236 max_regular_event_size = elem;
237 break;
238 }
239 }
240 }
241 // Provision 20% headroom
242 size_t newsize = std::min(UInt_t(1.2 * max_regular_event_size), kMaxBufSize);
243 fBuffer.resize(newsize);
244
245 // Shrink buffer if it could offer substantial memory savings
246 if( fBuffer.capacity() > 2 * newsize )
247 fBuffer.shrink_to_fit();
248
249 fChanged = false;
250}
251
252//_____________________________________________________________________________
253// Grow event buffer.
254// If newsize == 0 (default), use heuristics to guess a new size.
255// If newsize > size(), grow buffer to 'newsize'. Otherwise leave buffer as is.
256//
257// Returns false if buffer is already at maximum size.
259{
260 if( size() == kMaxBufSize )
261 return false;
262
263 if( newsize == 0 ) {
264 UInt_t maybe_newsize = 0;
265 if( !fDidGrow && fNevents > fUpdateInterval ) {
266 // If we have accumulated history data, try a modest increase first
267 maybe_newsize = 2 * Median(fSaved);
268 }
269 if( maybe_newsize > size() ) {
270 newsize = maybe_newsize;
271 } else if( fNevents < fUpdateInterval/10 ) {
272 // Grow the buffer rapidly at the beginning of the analysis.
273 // If we overshoot, updateSize() will shrink it again later on.
274 newsize = 4 * size();
275 } else {
276 // Fallback for all other cases: Double the buffer size whenever it
277 // is found too small and try again.
278 newsize = 2 * size();
279 }
280 } else if( newsize <= size() ) {
281 return true;
282 }
283 if( newsize > kMaxBufSize)
284 newsize = kMaxBufSize;
285
286 fBuffer.resize(newsize);
287 fDidGrow = true;
288
289 return true;
290}
291
292//_____________________________________________________________________________
293// Reset to starting values
295{
296 fBuffer.clear();
297 fBuffer.shrink_to_fit();
298 fSaved.clear();
299 fChanged = false;
300 fDidGrow = false;
301}
302
303} // namespace Decoder
304
int Int_t
unsigned int UInt_t
std::string fBuffer
bool Bool_t
const UInt_t kMaxUInt
#define CODA_ERROR
Definition THaCodaData.h:30
#define CODA_FATAL
Definition THaCodaData.h:31
#define CODA_EOF
Definition THaCodaData.h:29
#define CODA_OK
Definition THaCodaData.h:28
char * Form(const char *fmt,...)
VectorUIntNI fBuffer
Definition THaCodaData.h:53
VectorUInt fSaved
Definition THaCodaData.h:54
EvtBuffer(UInt_t initial_size=0)
UInt_t size() const
Definition THaCodaData.h:49
Bool_t grow(UInt_t newsize=0)
void staterr(const char *tried_to, Int_t status) const
virtual Int_t codaClose()=0
static Int_t ReturnCode(Int_t evio_retcode)
virtual Int_t getCodaVersion()
const char * Data() const
const Int_t n
static constexpr UInt_t kMaxBufSize
static T Median(vector< T, Alloc > &vec)
static constexpr UInt_t kInitBufSize
std::vector< UInt_t, default_init_allocator< UInt_t > > VectorUIntNI
Definition CustomAlloc.h:38
double T(double x)
double min(double x, double y)
double max(double x, double y)
Int_t Nint(T x)
bool verbose
STL namespace.
ClassImp(TPyArg)