10#include <dqm/analysis/modules/DQMHistAnalysisECLSummary.h>
13#include <ecl/geometry/ECLGeometryPar.h>
20#include "boost/format.hpp"
28 m_neighbours_obj(
"F", 0.1)
31 B2DEBUG(20,
"DQMHistAnalysisECLSummary: Constructor done.");
33 std::string(
"ECL:channels_info:"));
35 "Mask Cell IDs based on information from ECL PVs",
38 "The higher this parameter, the larger differences in occupancy are allowed for adjacent channels", 0.28);
40 "The higher this parameter, the larger differences in the number of hits with bad chi2 are allowed for adjacent channels",
42 addParam(
"onlyIfUpdated",
m_onlyIfUpdated,
"If true (default), update EPICS PVs only if histograms were updated.",
58 {
"dead",
"#splitline{dead}{channels}", 1, 1, 1e5},
59 {
"cold",
"#splitline{cold}{channels}", 1, 2, 1e5},
60 {
"hot",
"#splitline{hot}{channels}", 25, 50, 1e5},
61 {
"bad_chi2",
"#splitline{bad #chi^{2}}{channels}", 5, 10, 1e6},
62 {
"bad_fit",
"#splitline{fit incon-}{sistencies}", 5, 10, 0 },
68 for (
int crate_id = 1; crate_id <= ECL::ECL_CRATES; crate_id++) {
69 std::string pv_name = (boost::format(
"crate%02d:%s") % crate_id % alarm.name).str();
73 for (
auto& ecl_part : {
"All",
"FWDEndcap",
"Barrel",
"BWDEndcap"}) {
74 std::string pv_name = (boost::format(
"%s:%s") % ecl_part % alarm.name).str();
78 std::string mask_pv_name = (boost::format(
"mask:%s") % alarm.name).str();
86 TString title =
"#splitline{ECL errors monitoring}";
87 title +=
"{E - Error, W - Warning, L - Low statistics}";
88 title +=
";ECLCollector ID (same as Crate ID)";
90 ECL::ECL_CRATES, 1, ECL::ECL_CRATES + 1,
100 for (
int i = 1; i <= ECL::ECL_CRATES; i++) {
120 c_occupancy =
new TCanvas(
"ECL/c_cid_Thr5MeV_overlaid_analysis");
121 c_bad_chi2 =
new TCanvas(
"ECL/c_bad_quality_overlaid_analysis");
124 1, ECL::ECL_TOTAL_CHANNELS + 1);
131 1, ECL::ECL_TOTAL_CHANNELS + 1);
138 1, ECL::ECL_TOTAL_CHANNELS + 1);
145 1, ECL::ECL_TOTAL_CHANNELS + 1);
151 B2DEBUG(20,
"DQMHistAnalysisECLSummary: initialized.");
156 B2DEBUG(20,
"DQMHistAnalysisECLSummary: beginRun called.");
175 if (!h_total_events)
return;
186 const double HISTCOLOR_RED = 0.9;
187 const double HISTCOLOR_GREEN = 0.45;
188 const double HISTCOLOR_ORANGE = 0.65;
189 const double HISTCOLOR_GRAY = 0.00;
205 bool warn_flag =
false;
206 bool error_flag =
false;
208 for (
size_t alarm_idx = 0; alarm_idx < alarm_counts.size(); alarm_idx++) {
209 for (
size_t crate = 0; crate < alarm_counts[alarm_idx].size(); crate++) {
211 char label_text[2] = {0};
213 int warning_limit =
m_ecl_alarms[alarm_idx].warning_limit;
215 int alarms = alarm_counts[alarm_idx][crate];
221 color = HISTCOLOR_GRAY;
223 }
else if (alarms >= alarm_limit) {
224 color = HISTCOLOR_RED;
228 }
else if (alarms >= warning_limit) {
229 color = HISTCOLOR_ORANGE;
234 color = HISTCOLOR_GREEN;
237 if (label_text[0] ==
'E' || label_text[0] ==
'W') {
238 B2DEBUG(100,
"Non-zero (" << alarm_counts[alarm_idx][crate]
239 <<
") for alarm_idx, crate = " << alarm_idx <<
", " << crate);
242 if (label_text[0] != 0) {
243 auto text =
new TText((crate + 1.5), (alarm_idx + 0.5), label_text);
244 if (label_text[0] !=
'L') text->SetTextColor(kWhite);
245 text->SetTextSize(0.03);
246 text->SetTextAlign(22);
255 auto gstyle_title_h = gStyle->GetTitleH();
256 auto gstyle_title_x = gStyle->GetTitleX();
257 auto gstyle_title_y = gStyle->GetTitleY();
258 gStyle->SetTitleH(0.04);
259 gStyle->SetTitleX(0.60);
260 gStyle->SetTitleY(1.00);
276 "gStyle->SetPalette(kRainBow);"
277 "if (channels_summary) channels_summary->SetDrawOption(\"col\");");
280 "gStyle->SetPalette(kBird);");
297 gStyle->SetTitleH(gstyle_title_h);
298 gStyle->SetTitleX(gstyle_title_x);
299 gStyle->SetTitleY(gstyle_title_y);
304 B2DEBUG(20,
"DQMHistAnalysisECLSummary: endRun called");
312 B2DEBUG(20,
"terminate called");
324 if (alarm_info.name == name)
return {index, alarm_info};
327 B2FATAL(
"Could not get ECL alarm " + name);
339 std::string pv_name = (boost::format(
"crate%02d:%s") % crate_id % alarm.name).str();
341 double upper_warning_limit = 0;
342 double upper_alarm_limit = 0;
345 if (!accessed || upper_alarm_limit <= 0 || upper_warning_limit <= 0) {
346 B2WARNING(
"Failed to get alarm limits");
350 alarm.alarm_limit = upper_alarm_limit;
351 alarm.warning_limit = upper_warning_limit;
357 static std::map<std::string, dbr_sts_long_array> mask_info;
359 B2WARNING(
"Failed to get arrays of masked channels");
365 m_mask[alarm.name].clear();
367 int cell_id = mask_info[alarm.name].value[i];
370 if (cell_id == 0)
break;
371 m_mask[alarm.name].insert(cell_id);
379 std::string mask_pv_name = (boost::format(
"mask:%s") % alarm.name).str();
382 if (mask_chid ==
nullptr)
return false;
386 if (r != ECA_NORMAL)
return false;
392 auto r = ca_pend_io(5.0);
393 if (r != ECA_NORMAL)
return false;
400 std::vector< std::vector<int> > alarm_counts(
401 m_ecl_alarms.size(), std::vector<int>(ECL::ECL_CRATES));
405 TH1* h_fail_crateid =
findHist(
"ECL/fail_crateid",
false);
408 for (
int crate_id = 1; crate_id <= ECL::ECL_CRATES; crate_id++) {
409 int errors_count = 0;
410 if (h_fail_crateid) {
411 errors_count = h_fail_crateid->GetBinContent(crate_id);
416 alarm_counts[fit_alarm_index][crate_id - 1] = errors_count;
420 std::map<int, int> error_bitmasks;
425 error_bitmasks[cell_id] |= error_bitmask;
430 error_bitmasks[cell_id] |= error_bitmask;
435 if (!update_mirabelle) {
442 static std::vector<std::string> indices = {
"dead",
"cold",
"hot",
"bad_chi2"};
443 for (
auto& index_name : indices) {
445 int alarm_bit = 1 << alarm_index;
447 for (
auto& [cid, error_bitmask] : error_bitmasks) {
448 if ((error_bitmask & alarm_bit) == 0)
continue;
450 bool masked = (
m_mask[index_name].find(cid) !=
m_mask[index_name].end());
455 alarm_counts[alarm_index][crate_id - 1] += 1;
458 if (update_mirabelle)
continue;
463 if (index_name ==
"bad_chi2") {
466 }
else if (index_name ==
"dead" || index_name ==
"cold" || index_name ==
"hot") {
472 if (update_mirabelle)
continue;
475 if (index_name ==
"hot" || index_name ==
"bad_chi2") {
476 TCanvas* current_canvas;
479 TH1F* overlay_hist_green;
480 if (index_name ==
"hot") {
492 if (main_hist && main_hist->GetEntries() > 0) {
493 for (
auto& overlay : {overlay_hist, overlay_hist_green}) {
494 for (
int bin_id = 1; bin_id <= ECL::ECL_TOTAL_CHANNELS; bin_id++) {
495 if (overlay->GetBinContent(bin_id) == 0)
continue;
497 if (main_hist->GetBinContent(bin_id) == 0)
continue;
498 overlay->SetBinContent(bin_id, main_hist->GetBinContent(bin_id));
502 current_canvas->Clear();
503 current_canvas->cd();
504 main_hist->Draw(
"hist");
505 overlay_hist->Draw(
"hist;same");
506 overlay_hist_green->Draw(
"hist;same");
507 current_canvas->Modified();
508 current_canvas->Update();
509 current_canvas->Draw();
516 for (
size_t alarm_idx = 0; alarm_idx < alarm_counts.size(); alarm_idx++) {
518 std::map<std::string, int> total;
520 for (
size_t crate = 0; crate < alarm_counts[alarm_idx].size(); crate++) {
521 int crate_id = crate + 1;
522 int value = alarm_counts[alarm_idx][crate];
524 if (!update_mirabelle) {
525 std::string pv_name = (boost::format(
"crate%02d:%s") % crate_id % alarm.name).str();
529 total[
"All"] += value;
530 if (crate_id <= ECL::ECL_BARREL_CRATES) {
531 total[
"Barrel"] += value;
532 }
else if (crate_id <= ECL::ECL_BARREL_CRATES + ECL::ECL_FWD_CRATES) {
533 total[
"FWDEndcap"] += value;
535 total[
"BWDEndcap"] += value;
539 for (
auto& ecl_part : {
"All",
"FWDEndcap",
"Barrel",
"BWDEndcap"}) {
540 std::string pv_name = (boost::format(
"%s:%s") % ecl_part % alarm.name).str();
541 if (update_mirabelle) {
542 std::string var_name = pv_name;
543 std::replace(var_name.begin(), var_name.end(),
':',
'_');
544 B2DEBUG(100, var_name <<
" = " << total[ecl_part]);
557 static std::vector< std::vector<short> > neighbours(ECL::ECL_TOTAL_CHANNELS);
558 if (neighbours[0].size() == 0) {
559 for (
int cid0 = 0; cid0 < ECL::ECL_TOTAL_CHANNELS; cid0++) {
567 neighbours[cid0].erase(neighbours[cid0].begin());
574 max_deviation,
true);
581 static std::vector< std::vector<short> > neighbours(ECL::ECL_TOTAL_CHANNELS);
582 if (neighbours[0].size() == 0) {
583 for (
int cid_center = 1; cid_center <= ECL::ECL_TOTAL_CHANNELS; cid_center++) {
584 geom->Mapping(cid_center - 1);
585 const int theta_id_center = geom->GetThetaID();
586 int phi_id_center = geom->GetPhiID();
588 phi_id_center = phi_id_center * 144 / crystals_in_ring;
589 for (
int cid0 = 0; cid0 < 8736; cid0++) {
590 if (cid0 == cid_center - 1)
continue;
592 int theta_id = geom->GetThetaID();
593 int phi_id = geom->GetPhiID();
595 if (std::abs(theta_id - theta_id_center) <= 2 &&
596 std::abs(phi_id - phi_id_center) <= 2) {
597 neighbours[cid_center - 1].push_back(cid0);
606 max_deviation,
false);
610 TH1* hist,
double total_events,
611 const std::vector< std::vector<short> >& neighbours,
612 double max_deviation,
bool occupancy_histogram)
614 std::map<int, int> retval;
616 if (!hist)
return retval;
619 if (hist->Integral() <= 0)
return retval;
629 const auto& [chi2_index, chi2_alarm] =
getAlarmByName(
"bad_chi2");
631 double min_required_events;
633 if (occupancy_histogram) {
634 min_required_events = std::min({
635 dead_alarm.required_statistics,
636 cold_alarm.required_statistics,
637 hot_alarm.required_statistics,
640 min_required_events = chi2_alarm.required_statistics;
643 if (total_events < min_required_events)
return retval;
645 int dead_bit = 1 << dead_index;
646 int cold_bit = 1 << cold_index;
647 int hot_bit = 1 << hot_index;
648 int chi2_bit = 1 << chi2_index;
652 if (occupancy_histogram) {
654 bool not_normalized = (
findCanvas(
"ECL/c_cid_Thr5MeV_analysis") ==
nullptr);
655 if (total_events >= dead_alarm.required_statistics) {
656 double min_occupancy;
659 min_occupancy = 1e-6;
662 min_occupancy = 1e-4;
665 if (not_normalized) {
667 min_occupancy *= total_events;
669 for (
int cid = 1; cid <= ECL::ECL_TOTAL_CHANNELS; cid++) {
670 if (hist->GetBinContent(cid) > min_occupancy)
continue;
671 retval[cid] |= dead_bit;
674 if (total_events >= hot_alarm.required_statistics) {
676 double max_occupancy = 0.7;
677 if (not_normalized) {
679 max_occupancy *= total_events;
681 for (
int cid = 1; cid <= ECL::ECL_TOTAL_CHANNELS; cid++) {
682 if (hist->GetBinContent(cid) < max_occupancy)
continue;
683 retval[cid] |= hot_bit;
690 for (
int cid = 1; cid <= ECL::ECL_TOTAL_CHANNELS; cid++) {
691 double actual_value = hist->GetBinContent(cid);
693 std::vector<short> neighb = neighbours[cid - 1];
694 std::multiset<double> values_sorted;
695 for (
auto& neighbour_cid : neighb) {
696 values_sorted.insert(hist->GetBinContent(neighbour_cid));
699 double expected_value;
700 if (occupancy_histogram) {
702 expected_value = *std::next(values_sorted.begin(), 1 * neighb.size() / 4);
705 expected_value = *std::next(values_sorted.begin(), 35 * neighb.size() / 48);
707 double deviation = std::abs((actual_value - expected_value) /
710 if (!occupancy_histogram) {
712 double min_occupancy = 1.41e-5;
713 if (
findCanvas(
"ECL/c_bad_quality_analysis") ==
nullptr) {
715 min_occupancy *= total_events;
718 if (actual_value < min_occupancy)
continue;
721 if (deviation < max_deviation)
continue;
723 if (occupancy_histogram) {
724 if (actual_value < expected_value) retval[cid] |= cold_bit;
725 if (actual_value > 2 * expected_value) retval[cid] |= hot_bit;
727 if (actual_value > expected_value) retval[cid] |= chi2_bit;
736 static std::map<std::string, std::vector<TLine*> > lines;
737 std::string name = hist->GetName();
738 if (lines[name].empty()) {
739 int x_min = hist->GetXaxis()->GetXmin();
740 int x_max = hist->GetXaxis()->GetXmax();
741 int y_min = hist->GetYaxis()->GetXmin();
742 int y_max = hist->GetYaxis()->GetXmax();
743 for (
int x = x_min + 1; x < x_max; x++) {
744 auto l =
new TLine(x, 0, x, 5);
745 l->SetLineStyle(kDashed);
746 lines[name].push_back(l);
748 for (
int y = y_min + 1; y < y_max; y++) {
749 auto l =
new TLine(1, y, ECL::ECL_CRATES + 1, y);
750 l->SetLineStyle(kDashed);
751 lines[name].push_back(l);
754 for (
auto line : lines[name]) {
std::map< int, int > getSuspiciousChannels(TH1 *hist, double total_events, const std::vector< std::vector< short > > &neighbours, double max_deviation, bool occupancy_histogram)
Get outlier channels, ones with values (occupancy, errors, etc) that are much higher than the values ...
double m_total_events
Number of events with ECL data in the current run.
double m_maxDeviationForChi2
The higher this parameter, the larger differences in the number of hits with bad chi2 are allowed for...
TH1F * h_bad_occ_overlay
Overlay to indicate bad channels on occupancy histogram.
void initialize() override final
Initialize the module.
void updateAlarmConfig()
Set alarm limits in DQM based on EPICS PV limits.
std::vector< std::vector< int > > updateAlarmCounts(bool update_mirabelle=false)
Calculate and return number of problems per crate for all alarm categories.
std::vector< TText * > m_labels
Labels on the ECL alarms histogram.
ECL::ECLChannelMapper m_mapper
Object to map ECL crystal ID to ECL crate ID.
void drawGrid(TH2 *hist)
Draw grid with TLine primitives for the specified histogram.
TCanvas * c_channels_summary
TCanvas for ECL alarms regarding suspicious channels.
std::map< int, int > getChannelsWithChi2Problems()
static const int c_max_masked_channels
Size of an array with masked channels.
std::pair< int, ECLAlarmType > getAlarmByName(std::string name)
Returns index of the specific alarm type and detailed information.
TH2F * h_channels_summary
Summarized information about ECL channels.
double m_maxDeviationForOccupancy
The higher this parameter, the larger differences in occupancy are allowed for adjacent channels.
ECL::ECLNeighbours m_neighbours_obj
Object to get ECL crystal neighbours.
~DQMHistAnalysisECLSummaryModule()
Destructor.
TCanvas * c_occupancy
ECL occupancy histogram with highlighted suspicious channels.
TExec * m_ecl_style
Special object to specify style parameters.
std::string m_pvPrefix
Prefix to use for PVs registered by this module.
MonitoringObject * m_monObj
MiraBelle monitoring object.
void terminate() override final
Terminate.
void event() override final
Event processor.
TCanvas * c_bad_chi2
ECL bad chi2 histogram with highlighted suspicious channels.
TH1F * h_bad_chi2_overlay
Overlay to indicate bad channels on chi2 histogram.
void endRun() override final
Call when a run ends.
bool m_onlyIfUpdated
If true (default), update EPICS PVs only if there were changes in the histograms.
std::map< int, int > getChannelsWithOccupancyProblems()
TH1F * h_bad_chi2_overlay_green
Overlay to indicate masked bad channels on chi2 histogram.
void beginRun() override final
Call when a run begins.
DQMHistAnalysisECLSummaryModule()
< derived from DQMHistAnalysisModule class.
TExec * m_default_style
Special object to revert changes done by ecl_style.
TH1F * h_bad_occ_overlay_green
Overlay to indicate masked bad channels on occupancy histogram.
std::map< std::string, std::set< int > > m_mask
List of masked Cell IDs for each alarm type.
bool getMaskedChannels(std::map< std::string, dbr_sts_long_array > &mask_info)
Get the array of masked channels for each type of alarm case monitored by this module.
bool m_useChannelMask
If true, mask Cell IDs based on DQM:ECL:channels_info:{alarm_type} PV info.
std::vector< ECLAlarmType > m_ecl_alarms
Different alarms monitored in h_channels_summary.
The base class for the histogram analysis module.
TCanvas * findCanvas(TString cname)
Find canvas by name.
static MonitoringObject * getMonitoringObject(const std::string &name)
Get MonitoringObject with given name (new object is created if non-existing)
void colorizeCanvas(TCanvas *canvas, EStatus status)
Helper function for Canvas colorization.
static TH1 * findHist(const std::string &histname, bool onlyIfUpdated=false)
Get histogram from list (no other search).
@ c_ColorTooFew
Not enough entries/event to judge.
static const std::string & getRunType(void)
Get the Run Type.
void setEpicsPV(std::string keyname, double value)
Write value to a EPICS PV.
EStatus makeStatus(bool enough, bool warn_flag, bool error_flag)
Helper function to judge the status for coloring and EPICS.
int registerEpicsPV(std::string pvname, std::string keyname="")
EPICS related Functions.
chid getEpicsPVChID(std::string keyname)
Get EPICS PV Channel Id.
bool requestLimitsFromEpicsPVs(chid id, double &lowerAlarm, double &lowerWarn, double &upperWarn, double &upperAlarm)
Get Alarm Limits from EPICS PV.
bool initFromFile()
Initialize channel mapper using data stored in default location.
int getCrateID(int iCOPPERNode, int iFINESSE, bool pcie40=false)
Get crate number by given COPPER node number and FINESSE number.
The Class for ECL Geometry Parameters.
static ECLGeometryPar * Instance()
Static method to get a reference to the ECLGeometryPar instance.
const std::vector< short int > & getNeighbours(short int cid) const
Return the neighbours for a given cell ID.
short int getCrystalsPerRing(const short int thetaid) const
return number of crystals in a given theta ring
void setVariable(const std::string &var, float val, float upErr=-1., float dwErr=-1)
set value to float variable (new variable is made if not yet existing)
void addParam(const std::string &name, T ¶mVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Abstract base class for different kinds of events.