Skip to content

Commit 404b414

Browse files
dstoccopillot
authored andcommitted
Add possibility to specify a manual reject list for MID
1 parent 37a1f62 commit 404b414

File tree

2 files changed

+212
-43
lines changed

2 files changed

+212
-43
lines changed

Detectors/MUON/MID/Calibration/macros/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,53 @@ root -l
6060
.x build_rejectlist.C+(1716436103391,1721272208000,"localhost:8083")
6161
```
6262

63+
### Add custom bad channels
64+
65+
The macro `build_rejectlist.C` scans the QCDB and the CCDB in search of issues.
66+
However, the QCDB flag is based on local boards with empty signals.
67+
It can happen that a local board is problematic, but not completely dead and, therefore, it is not correctly spotted by the macro.
68+
It is therefore important to have a way to add the issues by hand.
69+
This can be done with a json file in the form:
70+
71+
```json
72+
{
73+
"startRun": 557251,
74+
"endRun": 557926,
75+
"rejectList": [
76+
{
77+
"deId": 4,
78+
"columnId": 2,
79+
"patterns": [
80+
"0x0",
81+
"0xFFFF",
82+
"0x0",
83+
"0x0",
84+
"0x0"
85+
]
86+
},
87+
{
88+
"deId": 13,
89+
"columnId": 2,
90+
"patterns": [
91+
"0x0",
92+
"0xFFFF",
93+
"0x0",
94+
"0x0",
95+
"0x0"
96+
]
97+
}
98+
]
99+
}
100+
```
101+
102+
The path to the file is then given to the macro with:
103+
104+
```shell
105+
.x build_rejectlist.C+(1726299038000,1727386238000,"http://localhost:8083","http://alice-ccdb.cern.ch","http://localhost:8080","rejectlist.json")
106+
```
107+
108+
The macro will then merge the manual reject list from the file with the reject list that it finds by scanning the QCDB and CCDB.
109+
63110
## Running the local CCDB
64111

65112
The local CCDB server can be easily built through alibuild.

Detectors/MUON/MID/Calibration/macros/build_rejectlist.C

Lines changed: 165 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <vector>
2222
#include <limits>
2323
#include <algorithm>
24+
#include "rapidjson/document.h"
25+
#include "rapidjson/istreamwrapper.h"
2426
#include "TCanvas.h"
2527
#include "TH1.h"
2628
#include "TGraph.h"
@@ -29,15 +31,25 @@
2931
#include "DataFormatsParameters/GRPECSObject.h"
3032
#include "DetectorsCommonDataFormats/DetID.h"
3133
#include "DataFormatsMID/ColumnData.h"
34+
#include "MIDBase/ColumnDataHandler.h"
3235
#include "MIDGlobalMapping/ExtendedMappingInfo.h"
3336
#include "MIDGlobalMapping/GlobalMapper.h"
3437
#include "MIDFiltering/ChannelMasksHandler.h"
38+
39+
// ...
3540
#if !defined(__CLING__) || defined(__ROOTCLING__)
3641
#include "CCDB/BasicCCDBManager.h"
3742
#endif
3843

3944
static const std::string sPathQCQuality = "qc/MID/MO/MIDQuality/Trends/global/MIDQuality/MIDQuality";
4045

46+
/// @brief Reject list object
47+
struct RejectListStruct {
48+
long start = 0; /// Start validity
49+
long end = 0; /// End validity
50+
std::vector<o2::mid::ColumnData> rejectList{}; /// Bad channels
51+
};
52+
4153
/// @brief Get timestamp in milliseconds
4254
/// @param timestamp Input timestamp (in s or ms)
4355
/// @return Timestamp in ms
@@ -174,25 +186,38 @@ std::vector<o2::mid::ColumnData> getRejectList(std::vector<o2::mid::ColumnData>
174186
return badChannels;
175187
}
176188

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+
177201
/// @brief Builds the reject list for the selected timestamp
178202
/// @param timestamp Timestamp for query
179203
/// @param qcdbApi QCDB api
180204
/// @param ccdbApi CCDB api
181205
/// @param outCCDBApi api of the CCDB where the reject list will be uploaded
182206
/// @return Reject list
183-
std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbApi, const o2::ccdb::CcdbApi& ccdbApi, const o2::ccdb::CcdbApi& outCCDBApi)
207+
RejectListStruct build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbApi, const o2::ccdb::CcdbApi& ccdbApi)
184208
{
185209
std::map<std::string, std::string> metadata;
210+
RejectListStruct rl;
186211
auto* qcQuality = qcdbApi.retrieveFromTFileAny<TCanvas>(sPathQCQuality, metadata, getTSMS(timestamp));
187212
if (!qcQuality) {
188213
std::cerr << "Cannot find QC quality for " << tsToString(timestamp) << std::endl;
189-
return {};
214+
return rl;
190215
}
191216
// Find the first and last timestamp where the quality was bad (if any)
192217
auto badTSRange = findTSRange(qcQuality);
193218
if (badTSRange.second == 0) {
194219
std::cout << "All good" << std::endl;
195-
return {};
220+
return rl;
196221
}
197222
// Search for the last timestamp for which the run quality was good
198223
auto goodTSRange = findTSRange(qcQuality, false);
@@ -202,18 +227,15 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
202227
if (!grpecs.isDetReadOut(o2::detectors::DetID::MID)) {
203228
std::cout << "Error: we are probably reading a parallel run" << std::endl;
204229
grpecs.print();
205-
return {};
230+
return rl;
206231
}
207232
if (grpecs.getRunType() != o2::parameters::GRPECS::PHYSICS) {
208233
std::cout << "This is not a physics run: skip" << std::endl;
209234
grpecs.print();
210-
return {};
235+
return rl;
211236
}
212237

213-
auto runRange = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, grpecs.getRun());
214-
long margin = 120000; // Add a two minutes safety margin
215-
runRange.first -= margin; // Subtract margin
216-
runRange.second += margin; // Add margin
238+
auto runRange = getRunDuration(ccdbApi, grpecs.getRun());
217239

218240
// Search for hits histogram in the period where the QC quality was bad
219241
auto tsVector = findObjectsTSInPeriod(badTSRange.first, badTSRange.second, qcdbApi, "qc/MID/MO/QcTaskMIDDigits/Hits");
@@ -227,15 +249,15 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
227249
auto infos = gm.buildStripsInfo();
228250
auto badChannels = findBadChannels(occupancy, infos);
229251
auto badChannelsCCDB = *ccdbApi.retrieveFromTFileAny<std::vector<o2::mid::ColumnData>>("MID/Calib/BadChannels", metadata, getTSMS(timestamp));
230-
auto rejectList = getRejectList(badChannels, badChannelsCCDB);
231-
if (rejectList.empty()) {
252+
rl.rejectList = getRejectList(badChannels, badChannelsCCDB);
253+
if (rl.rejectList.empty()) {
232254
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;
233-
return {};
255+
return rl;
234256
}
235257

236258
// Print some useful information
237259
std::cout << "Reject list:" << std::endl;
238-
for (auto& col : rejectList) {
260+
for (auto& col : rl.rejectList) {
239261
std::cout << col << std::endl;
240262
}
241263
std::cout << "Run number: " << grpecs.getRun() << std::endl;
@@ -245,41 +267,120 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
245267
std::cout << "Bad: " << timeRangeToString(badTSRange.first, badTSRange.second) << std::endl;
246268

247269
// Set the start of the reject list to the last timestamp in which the occupancy was ok
248-
auto startRL = goodTSRange.second;
270+
rl.start = goodTSRange.second;
249271
if (goodTSRange.first == 0) {
250272
// If the quality was bad for the full run, set the start of the reject list to the SOR
251273
std::cout << "CAVEAT: no good TS found. Will use SOT instead" << std::endl;
252-
startRL = runRange.first;
274+
rl.start = runRange.first;
253275
}
254276
// Set the end of the reject list to the end of run
255-
auto endRL = runRange.second;
256-
// Ask if you want to upload the object to the CCDB
257-
std::cout << "Upload reject list with validity: " << startRL << " - " << endRL << " to " << outCCDBApi.getURL() << "? [y/n]" << std::endl;
258-
std::string answer;
259-
std::cin >> answer;
260-
if (answer == "y") {
261-
std::cout << "Storing RejectList valid from " << startRL << " to " << endRL << std::endl;
262-
outCCDBApi.storeAsTFileAny(&rejectList, "MID/Calib/RejectList", metadata, startRL, endRL);
277+
rl.end = runRange.second;
278+
return rl;
279+
}
280+
281+
/// @brief Loads the reject list from a json file
282+
/// @param ccdbApi CCDB api
283+
/// @param filename json filename
284+
/// @return Reject list structure
285+
RejectListStruct load_from_json(const o2::ccdb::CcdbApi& ccdbApi, const char* filename = "rejectlist.json")
286+
{
287+
// Open the JSON file
288+
std::cout << "Reading reject list from file " << filename << std::endl;
289+
RejectListStruct rl;
290+
std::ifstream inFile(filename);
291+
if (!inFile.is_open()) {
292+
std::cerr << "Could not open the file!" << std::endl;
293+
return rl;
263294
}
264-
return rejectList;
295+
296+
// Create an IStreamWrapper for file input stream
297+
rapidjson::IStreamWrapper isw(inFile);
298+
299+
rapidjson::Document doc;
300+
if (doc.ParseStream(isw).HasParseError()) {
301+
std::cerr << "Problem parsing " << filename << std::endl;
302+
return rl;
303+
}
304+
auto startRange = getRunDuration(ccdbApi, doc["startRun"].GetInt());
305+
auto endRange = getRunDuration(ccdbApi, doc["endRun"].GetInt());
306+
rl.start = startRange.first;
307+
rl.end = endRange.second;
308+
std::cout << "Manual RL validity: " << timeRangeToString(rl.start, rl.end) << std::endl;
309+
auto rlArray = doc["rejectList"].GetArray();
310+
for (auto& ar : rlArray) {
311+
o2::mid::ColumnData col;
312+
col.deId = ar["deId"].GetInt();
313+
col.columnId = ar["columnId"].GetInt();
314+
auto patterns = ar["patterns"].GetArray();
315+
for (size_t iar = 0; iar < 5; ++iar) {
316+
col.patterns[iar] = std::strtol(patterns[iar].GetString(), NULL, 16);
317+
}
318+
rl.rejectList.emplace_back(col);
319+
std::cout << col << std::endl;
320+
}
321+
return rl;
265322
}
266323

267-
/// @brief Builds the reject list for the selected timestamp
268-
/// @param timestamp Timestamp for query
269-
/// @param qcdbUrl QCDB URL
270-
/// @param ccdbUrl CCDB URL
271-
/// @param outCCDBUrl URL of the CCDB where the reject list will be uploaded
272-
/// @return Reject list
273-
std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const char* qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083", const char* ccdbUrl = "http://alice-ccdb.cern.ch", const char* outCCDBUrl = "http://localhost:8080")
324+
/// @brief Merges the manual and automatic reject lists
325+
/// @param manualRL Manual reject list from json file
326+
/// @param rls Reject list from QCDB and CCDB
327+
/// @return Merged reject list
328+
std::vector<RejectListStruct> merge_rejectlists(const RejectListStruct& manualRL, const std::vector<RejectListStruct>& rls)
274329
{
275-
// Get the QC quality object for the selected timestamp
276-
o2::ccdb::CcdbApi qcdbApi;
277-
qcdbApi.init(qcdbUrl);
278-
o2::ccdb::CcdbApi ccdbApi;
279-
ccdbApi.init(ccdbUrl);
280-
o2::ccdb::CcdbApi outCCDBApi;
281-
outCCDBApi.init(outCCDBUrl);
282-
return build_rejectlist(timestamp, qcdbApi, ccdbApi, outCCDBApi);
330+
std::vector<RejectListStruct> merged;
331+
if (rls.empty()) {
332+
merged.emplace_back(manualRL);
333+
return merged;
334+
}
335+
o2::mid::ColumnDataHandler ch;
336+
RejectListStruct tmpRL;
337+
long lastEnd = manualRL.start;
338+
for (auto& rl : rls) {
339+
std::cout << "Checking rl with validity: " << timeRangeToString(rl.start, rl.end) << std::endl;
340+
if (rl.start >= manualRL.start && rl.end <= manualRL.end) {
341+
// The period is included in the validity of the manual reject list
342+
if (rl.start > lastEnd) {
343+
// Fill holes between periods
344+
tmpRL = manualRL;
345+
tmpRL.start = lastEnd;
346+
tmpRL.end = rl.start;
347+
merged.emplace_back(tmpRL);
348+
std::cout << "Adding manual RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl;
349+
}
350+
lastEnd = rl.end;
351+
352+
// merge
353+
ch.clear();
354+
ch.merge(rl.rejectList);
355+
ch.merge(manualRL.rejectList);
356+
tmpRL = rl;
357+
tmpRL.rejectList = ch.getMerged();
358+
std::sort(tmpRL.rejectList.begin(), tmpRL.rejectList.end(), [](const o2::mid::ColumnData& col1, const o2::mid::ColumnData& col2) { return o2::mid::getColumnDataUniqueId(col1.deId, col1.columnId) < o2::mid::getColumnDataUniqueId(col2.deId, col2.columnId); });
359+
merged.emplace_back(tmpRL);
360+
std::cout << "Merging RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl;
361+
// std::cout << "Before: " << std::endl;
362+
// for (auto& col : rl.rejectList) {
363+
// std::cout << col << std::endl;
364+
// }
365+
// std::cout << "After: " << std::endl;
366+
// for (auto& col : tmpRL.rejectList) {
367+
// std::cout << col << std::endl;
368+
// }
369+
} else {
370+
if (rl.start > manualRL.end && lastEnd < manualRL.end) {
371+
// Close manual period
372+
tmpRL = manualRL;
373+
tmpRL.start = lastEnd;
374+
merged.emplace_back(tmpRL);
375+
std::cout << "Adding manual RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl;
376+
lastEnd = manualRL.end;
377+
}
378+
// Add current reject list as it is
379+
merged.emplace_back(rl);
380+
std::cout << "Adding RL with validity: " << timeRangeToString(rl.start, rl.end) << std::endl;
381+
}
382+
}
383+
return merged;
283384
}
284385

285386
/// @brief Builds the reject list in a time range
@@ -288,17 +389,38 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const char* qc
288389
/// @param qcdbUrl QCDB URL
289390
/// @param ccdbUrl CCDB URL
290391
/// @param outCCDBUrl URL of the CCDB where the reject lists will be uploaded
291-
void build_rejectlist(long start, long end, const char* qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083", const char* ccdbUrl = "http://alice-ccdb.cern.ch", const char* outCCDBUrl = "http://localhost:8080")
392+
void build_rejectlist(long start, long end, const char* qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083", const char* ccdbUrl = "http://alice-ccdb.cern.ch", const char* outCCDBUrl = "http://localhost:8080", const char* json_rejectlist = "")
292393
{
293394
// Query the MID QC quality objects
294395
o2::ccdb::CcdbApi qcdbApi;
295396
qcdbApi.init(qcdbUrl);
296397
o2::ccdb::CcdbApi ccdbApi;
297398
ccdbApi.init(ccdbUrl);
298-
o2::ccdb::CcdbApi outCCDBApi;
299-
outCCDBApi.init(outCCDBUrl);
399+
std::vector<RejectListStruct> rls;
300400
auto objectsTS = findObjectsTSInPeriod(start, end, qcdbApi, sPathQCQuality.c_str());
301401
for (auto ts : objectsTS) {
302-
build_rejectlist(ts, qcdbApi, ccdbApi, outCCDBApi);
402+
auto rl = build_rejectlist(ts, qcdbApi, ccdbApi);
403+
if (rl.start != rl.end) {
404+
rls.emplace_back(rl);
405+
}
406+
}
407+
408+
if (!std::string(json_rejectlist).empty()) {
409+
auto rlManual = load_from_json(ccdbApi, json_rejectlist);
410+
rls = merge_rejectlists(rlManual, rls);
411+
}
412+
413+
o2::ccdb::CcdbApi outCCDBApi;
414+
outCCDBApi.init(outCCDBUrl);
415+
std::map<std::string, std::string> metadata;
416+
for (auto& rl : rls) {
417+
// Ask if you want to upload the object to the CCDB
418+
std::cout << "Upload reject list with validity: " << rl.start << " - " << rl.end << " to " << outCCDBApi.getURL() << "? [y/n]" << std::endl;
419+
std::string answer;
420+
std::cin >> answer;
421+
if (answer == "y") {
422+
std::cout << "Storing RejectList valid from " << rl.start << " to " << rl.end << std::endl;
423+
outCCDBApi.storeAsTFileAny(&rl.rejectList, "MID/Calib/RejectList", metadata, rl.start, rl.end);
424+
}
303425
}
304426
}

0 commit comments

Comments
 (0)