Skip to content

Commit 2f7204d

Browse files
dstoccoalcaliva
authored andcommitted
Add possibility to specify a manual reject list for MID
1 parent 2e9fae4 commit 2f7204d

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
@@ -168,25 +180,38 @@ std::vector<o2::mid::ColumnData> getRejectList(std::vector<o2::mid::ColumnData>
168180
return badChannels;
169181
}
170182

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+
171195
/// @brief Builds the reject list for the selected timestamp
172196
/// @param timestamp Timestamp for query
173197
/// @param qcdbApi QCDB api
174198
/// @param ccdbApi CCDB api
175199
/// @param outCCDBApi api of the CCDB where the reject list will be uploaded
176200
/// @return Reject list
177-
std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbApi, const o2::ccdb::CcdbApi& ccdbApi, const o2::ccdb::CcdbApi& outCCDBApi)
201+
RejectListStruct build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbApi, const o2::ccdb::CcdbApi& ccdbApi)
178202
{
179203
std::map<std::string, std::string> metadata;
204+
RejectListStruct rl;
180205
auto* qcQuality = qcdbApi.retrieveFromTFileAny<TCanvas>(sPathQCQuality, metadata, getTSMS(timestamp));
181206
if (!qcQuality) {
182207
std::cerr << "Cannot find QC quality for " << tsToString(timestamp) << std::endl;
183-
return {};
208+
return rl;
184209
}
185210
// Find the first and last timestamp where the quality was bad (if any)
186211
auto badTSRange = findTSRange(qcQuality);
187212
if (badTSRange.second == 0) {
188213
std::cout << "All good" << std::endl;
189-
return {};
214+
return rl;
190215
}
191216
// Search for the last timestamp for which the run quality was good
192217
auto goodTSRange = findTSRange(qcQuality, false);
@@ -196,18 +221,15 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
196221
if (!grpecs.isDetReadOut(o2::detectors::DetID::MID)) {
197222
std::cout << "Error: we are probably reading a parallel run" << std::endl;
198223
grpecs.print();
199-
return {};
224+
return rl;
200225
}
201226
if (grpecs.getRunType() != o2::parameters::GRPECS::PHYSICS) {
202227
std::cout << "This is not a physics run: skip" << std::endl;
203228
grpecs.print();
204-
return {};
229+
return rl;
205230
}
206231

207-
auto runRange = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, grpecs.getRun());
208-
long margin = 120000; // Add a two minutes safety margin
209-
runRange.first -= margin; // Subtract margin
210-
runRange.second += margin; // Add margin
232+
auto runRange = getRunDuration(ccdbApi, grpecs.getRun());
211233

212234
// Search for hits histogram in the period where the QC quality was bad
213235
auto tsVector = findObjectsTSInPeriod(badTSRange.first, badTSRange.second, qcdbApi, "qc/MID/MO/QcTaskMIDDigits/Hits");
@@ -221,15 +243,15 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
221243
auto infos = gm.buildStripsInfo();
222244
auto badChannels = findBadChannels(occupancy, infos);
223245
auto badChannelsCCDB = *ccdbApi.retrieveFromTFileAny<std::vector<o2::mid::ColumnData>>("MID/Calib/BadChannels", metadata, getTSMS(timestamp));
224-
auto rejectList = getRejectList(badChannels, badChannelsCCDB);
225-
if (rejectList.empty()) {
246+
rl.rejectList = getRejectList(badChannels, badChannelsCCDB);
247+
if (rl.rejectList.empty()) {
226248
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;
227-
return {};
249+
return rl;
228250
}
229251

230252
// Print some useful information
231253
std::cout << "Reject list:" << std::endl;
232-
for (auto& col : rejectList) {
254+
for (auto& col : rl.rejectList) {
233255
std::cout << col << std::endl;
234256
}
235257
std::cout << "Run number: " << grpecs.getRun() << std::endl;
@@ -239,41 +261,120 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const o2::ccdb
239261
std::cout << "Bad: " << timeRangeToString(badTSRange.first, badTSRange.second) << std::endl;
240262

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

261-
/// @brief Builds the reject list for the selected timestamp
262-
/// @param timestamp Timestamp for query
263-
/// @param qcdbUrl QCDB URL
264-
/// @param ccdbUrl CCDB URL
265-
/// @param outCCDBUrl URL of the CCDB where the reject list will be uploaded
266-
/// @return Reject list
267-
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")
318+
/// @brief Merges the manual and automatic reject lists
319+
/// @param manualRL Manual reject list from json file
320+
/// @param rls Reject list from QCDB and CCDB
321+
/// @return Merged reject list
322+
std::vector<RejectListStruct> merge_rejectlists(const RejectListStruct& manualRL, const std::vector<RejectListStruct>& rls)
268323
{
269-
// Get the QC quality object for the selected timestamp
270-
o2::ccdb::CcdbApi qcdbApi;
271-
qcdbApi.init(qcdbUrl);
272-
o2::ccdb::CcdbApi ccdbApi;
273-
ccdbApi.init(ccdbUrl);
274-
o2::ccdb::CcdbApi outCCDBApi;
275-
outCCDBApi.init(outCCDBUrl);
276-
return build_rejectlist(timestamp, qcdbApi, ccdbApi, outCCDBApi);
324+
std::vector<RejectListStruct> merged;
325+
if (rls.empty()) {
326+
merged.emplace_back(manualRL);
327+
return merged;
328+
}
329+
o2::mid::ColumnDataHandler ch;
330+
RejectListStruct tmpRL;
331+
long lastEnd = manualRL.start;
332+
for (auto& rl : rls) {
333+
std::cout << "Checking rl with validity: " << timeRangeToString(rl.start, rl.end) << std::endl;
334+
if (rl.start >= manualRL.start && rl.end <= manualRL.end) {
335+
// The period is included in the validity of the manual reject list
336+
if (rl.start > lastEnd) {
337+
// Fill holes between periods
338+
tmpRL = manualRL;
339+
tmpRL.start = lastEnd;
340+
tmpRL.end = rl.start;
341+
merged.emplace_back(tmpRL);
342+
std::cout << "Adding manual RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl;
343+
}
344+
lastEnd = rl.end;
345+
346+
// merge
347+
ch.clear();
348+
ch.merge(rl.rejectList);
349+
ch.merge(manualRL.rejectList);
350+
tmpRL = rl;
351+
tmpRL.rejectList = ch.getMerged();
352+
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); });
353+
merged.emplace_back(tmpRL);
354+
std::cout << "Merging RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl;
355+
// std::cout << "Before: " << std::endl;
356+
// for (auto& col : rl.rejectList) {
357+
// std::cout << col << std::endl;
358+
// }
359+
// std::cout << "After: " << std::endl;
360+
// for (auto& col : tmpRL.rejectList) {
361+
// std::cout << col << std::endl;
362+
// }
363+
} else {
364+
if (rl.start > manualRL.end && lastEnd < manualRL.end) {
365+
// Close manual period
366+
tmpRL = manualRL;
367+
tmpRL.start = lastEnd;
368+
merged.emplace_back(tmpRL);
369+
std::cout << "Adding manual RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl;
370+
lastEnd = manualRL.end;
371+
}
372+
// Add current reject list as it is
373+
merged.emplace_back(rl);
374+
std::cout << "Adding RL with validity: " << timeRangeToString(rl.start, rl.end) << std::endl;
375+
}
376+
}
377+
return merged;
277378
}
278379

279380
/// @brief Builds the reject list iin a time range
@@ -282,17 +383,38 @@ std::vector<o2::mid::ColumnData> build_rejectlist(long timestamp, const char* qc
282383
/// @param qcdbUrl QCDB URL
283384
/// @param ccdbUrl CCDB URL
284385
/// @param outCCDBUrl URL of the CCDB where the reject lists will be uploaded
285-
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")
386+
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 = "")
286387
{
287388
// Query the MID QC quality objects
288389
o2::ccdb::CcdbApi qcdbApi;
289390
qcdbApi.init(qcdbUrl);
290391
o2::ccdb::CcdbApi ccdbApi;
291392
ccdbApi.init(ccdbUrl);
292-
o2::ccdb::CcdbApi outCCDBApi;
293-
outCCDBApi.init(outCCDBUrl);
393+
std::vector<RejectListStruct> rls;
294394
auto objectsTS = findObjectsTSInPeriod(start, end, qcdbApi, sPathQCQuality.c_str());
295395
for (auto ts : objectsTS) {
296-
build_rejectlist(ts, qcdbApi, ccdbApi, outCCDBApi);
396+
auto rl = build_rejectlist(ts, qcdbApi, ccdbApi);
397+
if (rl.start != rl.end) {
398+
rls.emplace_back(rl);
399+
}
400+
}
401+
402+
if (!std::string(json_rejectlist).empty()) {
403+
auto rlManual = load_from_json(ccdbApi, json_rejectlist);
404+
rls = merge_rejectlists(rlManual, rls);
405+
}
406+
407+
o2::ccdb::CcdbApi outCCDBApi;
408+
outCCDBApi.init(outCCDBUrl);
409+
std::map<std::string, std::string> metadata;
410+
for (auto& rl : rls) {
411+
// Ask if you want to upload the object to the CCDB
412+
std::cout << "Upload reject list with validity: " << rl.start << " - " << rl.end << " to " << outCCDBApi.getURL() << "? [y/n]" << std::endl;
413+
std::string answer;
414+
std::cin >> answer;
415+
if (answer == "y") {
416+
std::cout << "Storing RejectList valid from " << rl.start << " to " << rl.end << std::endl;
417+
outCCDBApi.storeAsTFileAny(&rl.rejectList, "MID/Calib/RejectList", metadata, rl.start, rl.end);
418+
}
297419
}
298420
}

0 commit comments

Comments
 (0)