@@ -50,6 +50,17 @@ struct RejectListStruct {
5050 std ::vector < o2 ::mid ::ColumnData > rejectList {}; /// Bad channels
5151};
5252
53+ /// @brief Useful metadata
54+ struct MDStruct {
55+ long start = 0 ; /// Start validity
56+ long end = 0 ; /// End validity
57+ int runNumber = 0 ; /// Run number
58+ std ::string runType ; /// Run Type
59+
60+ bool operator < (const MDStruct & other ) const { return start < other .start ; }
61+ bool operator == (const MDStruct & other ) const { return start == other .start ; }
62+ };
63+
5364/// @brief Get timestamp in milliseconds
5465/// @param timestamp Input timestamp (in s or ms)
5566/// @return Timestamp in ms
@@ -96,23 +107,33 @@ std::string timeRangeToString(long start, long end)
96107/// @param end Query objects created not after
97108/// @param api CDB api
98109/// @param path CDB path
99- /// @return Vector of start validity of each object sorted in ascending way
100- std ::vector < long > findObjectsTSInPeriod (long start , long end , const o2 ::ccdb ::CcdbApi & api , const char * path )
110+ /// @return Vector of metadata in ascending order
111+ std ::vector < MDStruct > findObjectsMDInPeriod (long start , long end , const o2 ::ccdb ::CcdbApi & api , const char * path )
101112{
102- std ::vector < long > ts ;
103- auto out = api .list (path , false, "text/plain" , getTSMS (end ), getTSMS (start ));
104- std ::stringstream ss (out );
105- std ::string token ;
106- while (ss >> token ) {
107- if (token .find ("Validity" ) != std ::string ::npos ) {
108- ss >> token ;
109- ts .emplace_back (std ::atol (token .c_str ()));
110- }
113+ std ::vector < MDStruct > mds ;
114+ auto out = api .list (path , false, "application/json" , getTSMS (end ), getTSMS (start ));
115+ rapidjson ::Document doc ;
116+ doc .Parse (out .c_str ());
117+ for (auto& obj : doc ["objects" ].GetArray ()) {
118+ MDStruct md ;
119+ md .start = obj ["validFrom" ].GetInt64 ();
120+ md .end = obj ["validUntil" ].GetInt64 ();
121+ md .runNumber = std ::atoi (obj ["RunNumber" ].GetString ());
122+ md .runType = obj ["RunType" ].GetString ();
123+ mds .emplace_back (md );
111124 }
112- ts .erase (std ::unique (ts .begin (), ts .end ()), ts .end ());
125+ mds .erase (std ::unique (mds .begin (), mds .end ()), mds .end ());
113126 // Sort timestamps in ascending order
114- std ::sort (ts .begin (), ts .end ());
115- return ts ;
127+ std ::sort (mds .begin (), mds .end ());
128+ return mds ;
129+ }
130+
131+ /// @brief Gets the quality trend graph from the quality canvas
132+ /// @param qcQuality MID QC quality canvas
133+ /// @return Quality trend graph
134+ TGraph * getQualityTrend (const TCanvas * qcQuality )
135+ {
136+ return static_cast < TGraph * > (qcQuality -> GetListOfPrimitives ()-> FindObject ("Graph" ));
116137}
117138
118139/// @brief Find the first and last time when the quality was good or bad
@@ -121,7 +142,13 @@ std::vector<long> findObjectsTSInPeriod(long start, long end, const o2::ccdb::Cc
121142/// @return Pair with first and last time
122143std ::pair < uint64_t , uint64_t > findTSRange (TCanvas * qcQuality , bool selectBad = true)
123144{
124- auto* gr = static_cast < TGraph * > (qcQuality -> GetListOfPrimitives ()-> FindObject ("Graph" ));
145+ // Gets the plot with the quality flags
146+ // The flag values are:
147+ // Good: 3.5
148+ // Medium: 2.5
149+ // Bad: 1.5
150+ // Null: 0.5
151+ auto* gr = getQualityTrend (qcQuality );
125152 double xp , yp ;
126153 std ::pair < uint64_t , uint64_t > range {std ::numeric_limits < uint64_t > ::max (), 0 };
127154 for (int ip = 0 ; ip < gr -> GetN (); ++ ip ) {
@@ -138,6 +165,32 @@ std::pair<uint64_t, uint64_t> findTSRange(TCanvas* qcQuality, bool selectBad = t
138165 return range ;
139166}
140167
168+ /// @brief Gets the first and last timestamp in the quality
169+ /// @param qcQuality MID QC quality canvas
170+ /// @return Pair with the first and last timestamp in the quality trend
171+ std ::pair < uint64_t , uint64_t > getFirstLast (const TCanvas * qcQuality )
172+ {
173+ auto* gr = getQualityTrend (qcQuality );
174+ double xp1 , xp2 , yp ;
175+ gr -> GetPoint (0 , xp1 , yp );
176+ gr -> GetPoint (gr -> GetN () - 1 , xp2 , yp );
177+ return {static_cast < uint64_t > (xp1 * 1000 ), static_cast < uint64_t > (xp2 * 1000 )};
178+ }
179+
180+ /// @brief Update the selected range of timestamp
181+ /// @param selectedTSRange Reference to the selected range to be modified
182+ /// @param qcTSRange Range of the MID quality trend
183+ /// @param runRange Run range
184+ void updateRange (std ::pair < uint64_t , uint64_t > & selectedTSRange , const std ::pair < uint64_t , uint64_t > qcTSRange , const std ::pair < uint64_t , uint64_t > runRange )
185+ {
186+ if (selectedTSRange .first == qcTSRange .first ) {
187+ selectedTSRange .first = runRange .first ;
188+ }
189+ if (selectedTSRange .second == qcTSRange .second ) {
190+ selectedTSRange .second = runRange .second ;
191+ }
192+ }
193+
141194/// @brief Find bad channels from the occupancy histograms
142195/// @param hits Occupancy histogram
143196/// @param infos Mapping
@@ -180,72 +233,61 @@ std::vector<o2::mid::ColumnData> getRejectList(std::vector<o2::mid::ColumnData>
180233 return badChannels ;
181234}
182235
183- /// @brief Gets the run duration with a safety marging
184- /// @param ccdbApi CCDB api
185- /// @param marging margin in milliseconds
186- /// @return Pair with the timestamps of start-margin and end+margin for the run
187- std ::pair < int64_t , int64_t > getRunDuration (const o2 ::ccdb ::CcdbApi & ccdbApi , int runNumber , int64_t margin = 120000 )
188- {
189- auto runRange = o2 ::ccdb ::BasicCCDBManager ::getRunDuration (ccdbApi , runNumber );
190- runRange .first -= margin ; // Subtract margin
191- runRange .second += margin ; // Add margin
192- return runRange ;
193- }
194-
195236/// @brief Builds the reject list for the selected timestamp
196- /// @param timestamp Timestamp for query
237+ /// @param md MD structure
197238/// @param qcdbApi QCDB api
198239/// @param ccdbApi CCDB api
199240/// @param outCCDBApi api of the CCDB where the reject list will be uploaded
200241/// @return Reject list
201- RejectListStruct build_rejectlist (long timestamp , const o2 ::ccdb ::CcdbApi & qcdbApi , const o2 ::ccdb ::CcdbApi & ccdbApi )
242+ RejectListStruct build_rejectlist (const MDStruct & md , const o2 ::ccdb ::CcdbApi & qcdbApi , const o2 ::ccdb ::CcdbApi & ccdbApi )
202243{
203- std ::map < std ::string , std ::string > metadata ;
204244 RejectListStruct rl ;
205- auto* qcQuality = qcdbApi .retrieveFromTFileAny < TCanvas > (sPathQCQuality , metadata , getTSMS (timestamp ));
245+ if (md .runType != "PHYSICS" ) {
246+ std ::cout << "Run " << md .runNumber << " is of type " << md .runType << ": skip" << std ::endl ;
247+ return rl ;
248+ }
249+
250+ std ::map < std ::string , std ::string > metadata ;
251+ auto* qcQuality = qcdbApi .retrieveFromTFileAny < TCanvas > (sPathQCQuality , metadata , getTSMS (md .start ));
206252 if (!qcQuality ) {
207- std ::cerr << "Cannot find QC quality for " << tsToString (timestamp ) << std ::endl ;
253+ std ::cerr << "Cannot find QC quality for " << tsToString (md . start ) << std ::endl ;
208254 return rl ;
209255 }
256+
210257 // Find the first and last timestamp where the quality was bad (if any)
211258 auto badTSRange = findTSRange (qcQuality );
212259 if (badTSRange .second == 0 ) {
213260 std ::cout << "All good" << std ::endl ;
214261 return rl ;
215262 }
263+
264+ // Find the first and last timestamp where the quality flag was set
265+ auto qualityTSRange = getFirstLast (qcQuality );
216266 // Search for the last timestamp for which the run quality was good
217267 auto goodTSRange = findTSRange (qcQuality , false );
218- // Query the CCDB to see to which run the timestamp corresponds
219- auto oldestTSInQCQuality = (goodTSRange .first == 0 ) ? badTSRange .first : goodTSRange .first ;
220- auto grpecs = * ccdbApi .retrieveFromTFileAny < o2 ::parameters ::GRPECSObject > ("GLO/Config/GRPECS" , metadata , getTSMS (oldestTSInQCQuality ));
221- if (!grpecs .isDetReadOut (o2 ::detectors ::DetID ::MID )) {
222- std ::cout << "Error: we are probably reading a parallel run" << std ::endl ;
223- grpecs .print ();
224- return rl ;
225- }
226- if (grpecs .getRunType () != o2 ::parameters ::GRPECS ::PHYSICS ) {
227- std ::cout << "This is not a physics run: skip" << std ::endl ;
228- grpecs .print ();
229- return rl ;
230- }
231268
232- auto runRange = getRunDuration (ccdbApi , grpecs .getRun ());
269+ auto runRange = o2 ::ccdb ::BasicCCDBManager ::getRunDuration (ccdbApi , md .runNumber );
270+ updateRange (badTSRange , qualityTSRange , runRange );
271+ updateRange (goodTSRange , qualityTSRange , runRange );
233272
234273 // Search for hits histogram in the period where the QC quality was bad
235- auto tsVector = findObjectsTSInPeriod (badTSRange .first , badTSRange .second , qcdbApi , "qc/MID/MO/QcTaskMIDDigits/Hits" );
236- if (tsVector .empty ()) {
274+ auto mdVector = findObjectsMDInPeriod (badTSRange .first , badTSRange .second , qcdbApi , "qc/MID/MO/QcTaskMIDDigits/Hits" );
275+ if (mdVector .empty ()) {
237276 std ::cerr << "Cannot find hits in period " << tsToString (badTSRange .first ) << " - " << tsToString (badTSRange .second ) << std ::endl ;
238277 return {};
239278 }
240- // Focus on the first object found
241- TH1 * occupancy = qcdbApi .retrieveFromTFileAny < TH1F > ("qc/MID/MO/QcTaskMIDDigits/Hits" , metadata , getTSMS (tsVector .front ()));
279+ // Focus on the last object found
280+ // We chose the last instead of the first because it might happen that
281+ // we lose additional boards before the EOR
282+ // If we build the reject list for the first object, we would therefore miss some boards
283+ TH1 * occupancy = qcdbApi .retrieveFromTFileAny < TH1F > ("qc/MID/MO/QcTaskMIDDigits/Hits" , metadata , getTSMS (mdVector .back ().start ));
242284 o2 ::mid ::GlobalMapper gm ;
243285 auto infos = gm .buildStripsInfo ();
244286 auto badChannels = findBadChannels (occupancy , infos );
245- auto badChannelsCCDB = * ccdbApi .retrieveFromTFileAny < std ::vector < o2 ::mid ::ColumnData >>("MID/Calib/BadChannels" , metadata , getTSMS (timestamp ));
287+ auto badChannelsCCDB = * ccdbApi .retrieveFromTFileAny < std ::vector < o2 ::mid ::ColumnData >>("MID/Calib/BadChannels" , metadata , getTSMS (md . start ));
246288 rl .rejectList = getRejectList (badChannels , badChannelsCCDB );
247289 if (rl .rejectList .empty ()) {
248- std ::cout << "Warning: reject list was empty. It probably means that an entire board is already masked in calibration for run " << grpecs . getRun () << std ::endl ;
290+ std ::cout << "Warning: reject list was empty. It probably means that an entire board is already masked in calibration for run " << md . runNumber << std ::endl ;
249291 return rl ;
250292 }
251293
@@ -254,21 +296,15 @@ RejectListStruct build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbA
254296 for (auto& col : rl .rejectList ) {
255297 std ::cout << col << std ::endl ;
256298 }
257- std ::cout << "Run number: " << grpecs .getRun () << std ::endl ;
258- std ::cout << "SOR - EOR: " << timeRangeToString (grpecs .getTimeStart (), grpecs .getTimeEnd ()) << std ::endl ;
299+ std ::cout << "Run number: " << md .runNumber << std ::endl ;
259300 std ::cout << "SOT - EOT: " << timeRangeToString (runRange .first , runRange .second ) << std ::endl ;
260301 std ::cout << "Good: " << timeRangeToString (goodTSRange .first , goodTSRange .second ) << std ::endl ;
261302 std ::cout << "Bad: " << timeRangeToString (badTSRange .first , badTSRange .second ) << std ::endl ;
303+ std ::cout << "Fraction bad: " << static_cast < double > (badTSRange .second - badTSRange .first ) / static_cast < double > (runRange .second - runRange .first ) << std ::endl ;
262304
263305 // Set the start of the reject list to the last timestamp in which the occupancy was ok
264306 rl .start = goodTSRange .second ;
265- if (goodTSRange .first == 0 ) {
266- // If the quality was bad for the full run, set the start of the reject list to the SOR
267- std ::cout << "CAVEAT: no good TS found. Will use SOT instead" << std ::endl ;
268- rl .start = runRange .first ;
269- }
270- // Set the end of the reject list to the end of run
271- rl .end = runRange .second ;
307+ rl .end = badTSRange .second ;
272308 return rl ;
273309}
274310
@@ -295,8 +331,8 @@ RejectListStruct load_from_json(const o2::ccdb::CcdbApi& ccdbApi, const char* fi
295331 std ::cerr << "Problem parsing " << filename << std ::endl ;
296332 return rl ;
297333 }
298- auto startRange = getRunDuration (ccdbApi , doc ["startRun" ].GetInt ());
299- auto endRange = getRunDuration (ccdbApi , doc ["endRun" ].GetInt ());
334+ auto startRange = o2 :: ccdb :: BasicCCDBManager :: getRunDuration (ccdbApi , doc ["startRun" ].GetInt ());
335+ auto endRange = o2 :: ccdb :: BasicCCDBManager :: getRunDuration (ccdbApi , doc ["endRun" ].GetInt ());
300336 rl .start = startRange .first ;
301337 rl .end = endRange .second ;
302338 std ::cout << "Manual RL validity: " << timeRangeToString (rl .start , rl .end ) << std ::endl ;
@@ -391,9 +427,9 @@ void build_rejectlist(long start, long end, const char* qcdbUrl = "http://ali-qc
391427 o2 ::ccdb ::CcdbApi ccdbApi ;
392428 ccdbApi .init (ccdbUrl );
393429 std ::vector < RejectListStruct > rls ;
394- auto objectsTS = findObjectsTSInPeriod (start , end , qcdbApi , sPathQCQuality .c_str ());
395- for (auto ts : objectsTS ) {
396- auto rl = build_rejectlist (ts , qcdbApi , ccdbApi );
430+ auto objectsMD = findObjectsMDInPeriod (start , end , qcdbApi , sPathQCQuality .c_str ());
431+ for (auto md : objectsMD ) {
432+ auto rl = build_rejectlist (md , qcdbApi , ccdbApi );
397433 if (rl .start != rl .end ) {
398434 rls .emplace_back (rl );
399435 }
0 commit comments