@@ -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
@@ -127,7 +148,7 @@ std::pair<uint64_t, uint64_t> findTSRange(TCanvas* qcQuality, bool selectBad = t
127148 // Medium: 2.5
128149 // Bad: 1.5
129150 // Null: 0.5
130- auto* gr = static_cast < TGraph * > (qcQuality -> GetListOfPrimitives () -> FindObject ( "Graph" ) );
151+ auto* gr = getQualityTrend (qcQuality );
131152 double xp , yp ;
132153 std ::pair < uint64_t , uint64_t > range {std ::numeric_limits < uint64_t > ::max (), 0 };
133154 for (int ip = 0 ; ip < gr -> GetN (); ++ ip ) {
@@ -144,6 +165,32 @@ std::pair<uint64_t, uint64_t> findTSRange(TCanvas* qcQuality, bool selectBad = t
144165 return range ;
145166}
146167
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+
147194/// @brief Find bad channels from the occupancy histograms
148195/// @param hits Occupancy histogram
149196/// @param infos Mapping
@@ -186,72 +233,61 @@ std::vector<o2::mid::ColumnData> getRejectList(std::vector<o2::mid::ColumnData>
186233 return badChannels ;
187234}
188235
189- /// @brief Gets the run duration with a safety marging
190- /// @param ccdbApi CCDB api
191- /// @param marging margin in milliseconds
192- /// @return Pair with the timestamps of start-margin and end+margin for the run
193- std ::pair < int64_t , int64_t > getRunDuration (const o2 ::ccdb ::CcdbApi & ccdbApi , int runNumber , int64_t margin = 120000 )
194- {
195- auto runRange = o2 ::ccdb ::BasicCCDBManager ::getRunDuration (ccdbApi , runNumber );
196- runRange .first -= margin ; // Subtract margin
197- runRange .second += margin ; // Add margin
198- return runRange ;
199- }
200-
201236/// @brief Builds the reject list for the selected timestamp
202- /// @param timestamp Timestamp for query
237+ /// @param md MD structure
203238/// @param qcdbApi QCDB api
204239/// @param ccdbApi CCDB api
205240/// @param outCCDBApi api of the CCDB where the reject list will be uploaded
206241/// @return Reject list
207- 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 )
208243{
209- std ::map < std ::string , std ::string > metadata ;
210244 RejectListStruct rl ;
211- 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 ));
212252 if (!qcQuality ) {
213- std ::cerr << "Cannot find QC quality for " << tsToString (timestamp ) << std ::endl ;
253+ std ::cerr << "Cannot find QC quality for " << tsToString (md . start ) << std ::endl ;
214254 return rl ;
215255 }
256+
216257 // Find the first and last timestamp where the quality was bad (if any)
217258 auto badTSRange = findTSRange (qcQuality );
218259 if (badTSRange .second == 0 ) {
219260 std ::cout << "All good" << std ::endl ;
220261 return rl ;
221262 }
263+
264+ // Find the first and last timestamp where the quality flag was set
265+ auto qualityTSRange = getFirstLast (qcQuality );
222266 // Search for the last timestamp for which the run quality was good
223267 auto goodTSRange = findTSRange (qcQuality , false );
224- // Query the CCDB to see to which run the timestamp corresponds
225- auto oldestTSInQCQuality = (goodTSRange .first == 0 ) ? badTSRange .first : goodTSRange .first ;
226- auto grpecs = * ccdbApi .retrieveFromTFileAny < o2 ::parameters ::GRPECSObject > ("GLO/Config/GRPECS" , metadata , getTSMS (oldestTSInQCQuality ));
227- if (!grpecs .isDetReadOut (o2 ::detectors ::DetID ::MID )) {
228- std ::cout << "Error: we are probably reading a parallel run" << std ::endl ;
229- grpecs .print ();
230- return rl ;
231- }
232- if (grpecs .getRunType () != o2 ::parameters ::GRPECS ::PHYSICS ) {
233- std ::cout << "This is not a physics run: skip" << std ::endl ;
234- grpecs .print ();
235- return rl ;
236- }
237268
238- 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 );
239272
240273 // Search for hits histogram in the period where the QC quality was bad
241- auto tsVector = findObjectsTSInPeriod (badTSRange .first , badTSRange .second , qcdbApi , "qc/MID/MO/QcTaskMIDDigits/Hits" );
242- if (tsVector .empty ()) {
274+ auto mdVector = findObjectsMDInPeriod (badTSRange .first , badTSRange .second , qcdbApi , "qc/MID/MO/QcTaskMIDDigits/Hits" );
275+ if (mdVector .empty ()) {
243276 std ::cerr << "Cannot find hits in period " << tsToString (badTSRange .first ) << " - " << tsToString (badTSRange .second ) << std ::endl ;
244277 return {};
245278 }
246- // Focus on the first object found
247- 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 ));
248284 o2 ::mid ::GlobalMapper gm ;
249285 auto infos = gm .buildStripsInfo ();
250286 auto badChannels = findBadChannels (occupancy , infos );
251- 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 ));
252288 rl .rejectList = getRejectList (badChannels , badChannelsCCDB );
253289 if (rl .rejectList .empty ()) {
254- 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 ;
255291 return rl ;
256292 }
257293
@@ -260,21 +296,15 @@ RejectListStruct build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbA
260296 for (auto& col : rl .rejectList ) {
261297 std ::cout << col << std ::endl ;
262298 }
263- std ::cout << "Run number: " << grpecs .getRun () << std ::endl ;
264- std ::cout << "SOR - EOR: " << timeRangeToString (grpecs .getTimeStart (), grpecs .getTimeEnd ()) << std ::endl ;
299+ std ::cout << "Run number: " << md .runNumber << std ::endl ;
265300 std ::cout << "SOT - EOT: " << timeRangeToString (runRange .first , runRange .second ) << std ::endl ;
266301 std ::cout << "Good: " << timeRangeToString (goodTSRange .first , goodTSRange .second ) << std ::endl ;
267302 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 ;
268304
269305 // Set the start of the reject list to the last timestamp in which the occupancy was ok
270306 rl .start = goodTSRange .second ;
271- if (goodTSRange .first == 0 ) {
272- // If the quality was bad for the full run, set the start of the reject list to the SOR
273- std ::cout << "CAVEAT: no good TS found. Will use SOT instead" << std ::endl ;
274- rl .start = runRange .first ;
275- }
276- // Set the end of the reject list to the end of run
277- rl .end = runRange .second ;
307+ rl .end = badTSRange .second ;
278308 return rl ;
279309}
280310
@@ -301,8 +331,8 @@ RejectListStruct load_from_json(const o2::ccdb::CcdbApi& ccdbApi, const char* fi
301331 std ::cerr << "Problem parsing " << filename << std ::endl ;
302332 return rl ;
303333 }
304- auto startRange = getRunDuration (ccdbApi , doc ["startRun" ].GetInt ());
305- 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 ());
306336 rl .start = startRange .first ;
307337 rl .end = endRange .second ;
308338 std ::cout << "Manual RL validity: " << timeRangeToString (rl .start , rl .end ) << std ::endl ;
@@ -397,9 +427,9 @@ void build_rejectlist(long start, long end, const char* qcdbUrl = "http://ali-qc
397427 o2 ::ccdb ::CcdbApi ccdbApi ;
398428 ccdbApi .init (ccdbUrl );
399429 std ::vector < RejectListStruct > rls ;
400- auto objectsTS = findObjectsTSInPeriod (start , end , qcdbApi , sPathQCQuality .c_str ());
401- for (auto ts : objectsTS ) {
402- 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 );
403433 if (rl .start != rl .end ) {
404434 rls .emplace_back (rl );
405435 }
0 commit comments