diff --git a/ana/toa/toa_efficiency.py b/ana/toa/toa_efficiency.py index 7984f8d63..f644e5c27 100644 --- a/ana/toa/toa_efficiency.py +++ b/ana/toa/toa_efficiency.py @@ -20,6 +20,7 @@ description='takes a csv from tasks.toa_vref_scan and outputs a plot of TOA efficiency per channel against TOA VREF values' ) parser.add_argument('-f', required = True, help='csv file containing scan from tasks.toa_vref_scan') +parser.add_argument('-r', '--roc', type = int, choices = [0, 1, 2, 3, 4, 5], required = True, help = 'ROC index to filter data (e.g., 0, 1, 2, 3, 4, 5)') args = parser.parse_args() if not os.path.isfile(args.f): @@ -28,8 +29,24 @@ if not args.f.lower().endswith('csv'): print(args.f + ' is not a csv file') sys.exit() + data, head = read_pflib_csv(args.f) +data.columns = data.columns.str.strip() + +if 'ROC' not in data.columns: + print("Error: 'ROC' column missing from CSV header. Please make sure you are using the updated C++ code.") + sys.exit() + +data = data[data['ROC'] == args.roc].copy() + +if data.empty: + print(f"Error: No scan data found for ROC {args.roc} in '{args.f}'. Was this ROC active during the run?") + sys.exit() + +data = data.sort_values(by='TOA_VREF').reset_index(drop = True) +print(f"Extracted {len(data)} voltage points for ROC {args.roc}") + """ Example plot of how TOA_VREF plot should look. I credit the toa_vref_analysis.py script from the TileboardQC Repository for HGCAL Tileboard Quality Control for this @@ -50,34 +67,28 @@ the signal-to-noise ratio. Select the point 'o' on the graph. """ +vref_axis = data['TOA_VREF'].values + channel_lists = [] for chan in range(72): - channel_lists.append([]) - -unique_toa_vrefs = data['TOA_VREF'].unique() - -for toa_vref in unique_toa_vrefs: - filtered_data = data[data['TOA_VREF'] == toa_vref] - for chan in range(72): - non_zero = filtered_data[filtered_data[str(chan)] != 0] - chan_triggers = len(non_zero) - chan_toa_efficiency = chan_triggers / len(filtered_data) - channel_lists[chan].append(chan_toa_efficiency) + channel_lists.append(data[str(chan)].values) # Plotting the results +input_stem = Path(args.f).stem plt.figure(figsize=(10, 6)) for chan in range(72): - plt.plot(channel_lists[chan], + plt.plot(vref_axis, channel_lists[chan], marker = 'o', label=f'Ch. {chan}', linestyle='none') -plt.title('TOA Efficiency vs TOA VREF per channel') +plt.title(f'TOA Efficiency vs TOA VREF per channel for ROC: {args.roc}') plt.xlabel('TOA VREF') plt.ylabel('TOA Efficiency') plt.tight_layout() -plt.savefig('toa_efficiency_plot.png') -plt.show() -print("Plot saved as 'toa_efficiency_plot.png'") +plot_filename = f"toa_efficiency_plot_{input_stem}_roc{args.roc}.png" +plt.savefig(plot_filename) +plt.close() +print(f"Plot saved as '{plot_filename}'") # Printing the values of highest non-zero TOA_VREF per link link0 = np.array(channel_lists[:36]).T @@ -91,16 +102,24 @@ if not all(x == 0 for x in link1[i]): link1_count.append(i) -print("for link 0, the highest non-zero toa_vref is " + str(link0_count[-1]) + ", " \ -"so set TOA_VREF to ", link0_count[-1] + 10) -print("for link 1, the highest non-zero toa_vref is " + str(link1_count[-1]) + ", " \ -"so set TOA_VREF to ", link1_count[-1] + 10) +if not link0_count or not link1_count: + print("Error: Efficiency data contains only zeros for one or both links.") + sys.exit() + +max_vref_l0 = data.loc[link0_count[-1], "TOA_VREF"] +max_vref_l1 = data.loc[link1_count[-1], "TOA_VREF"] + +optimal_vref_l0 = max_vref_l0 + 10 +optimal_vref_l1 = max_vref_l1 + 10 + +print(f"for link 0, the highest non-zero toa_vref is {max_vref_l0} so set TOA_VREF to {optimal_vref_l0}") +print(f"for link 1, the highest non-zero toa_vref is {max_vref_l1} so set TOA_VREF to {optimal_vref_l1}") #Outputting the values to a yaml file df_max = pd.DataFrame({ 'link': [0, 1], - 'max_toa': [link0_count[-1] + 10, link1_count[-1] + 10] + 'max_toa': [optimal_vref_l0, optimal_vref_l1] } ) @@ -112,7 +131,8 @@ 'TOA_VREF': int(row['max_toa']) } -with open("output.yaml", "w") as f: +yaml_filename = f"output_{input_stem}_roc{args.roc}.yaml" +with open(yaml_filename, "w") as f: yaml.dump(yaml_data, f, sort_keys=False) -print("Output saved to 'output.yaml'") \ No newline at end of file +print(f"Output saved to '{yaml_filename}'") diff --git a/app/tool/algorithm/get_toa_efficiencies.cxx b/app/tool/algorithm/get_toa_efficiencies.cxx index d472a52b7..d84d7aa52 100644 --- a/app/tool/algorithm/get_toa_efficiencies.cxx +++ b/app/tool/algorithm/get_toa_efficiencies.cxx @@ -1,25 +1,64 @@ #include "get_toa_efficiencies.h" +#include "pflib/logging/Logging.h" #include "pflib/utility/efficiency.h" namespace pflib::algorithm { std::array get_toa_efficiencies( + int i_roc, const pflib::packing::SingleECONDRocErxMapping& mapping, const std::vector& data) { + static auto the_log_{::pflib::logging::get("get_toa_efficiencies")}; + std::array efficiencies; + /// reserve a vector of the appropriate size to avoid repeating allocation /// time for all 72 channels + + if (data.empty()) { + pflib_log(warn) << "[DEBUG TOA] data packet vector is EMPTY for ROC " + << i_roc; + efficiencies.fill(0.0); + return efficiencies; + } + std::vector toas(data.size()); for (int ch{0}; ch < 72; ch++) { // TODO: 348 - int i_link = (ch / 36); - int i_ch = ch % 36; + + auto [i_erx, i_ch] = mapping.toErxChannel(i_roc, ch); + + if (i_erx < 0 || i_ch < 0) { + pflib_log(error) + << "[DEBUG MAP] Sanity Failure: Negative mapping values detected " + << "ROC " << i_roc << " Ch " << ch + << " resolved to eRx = " << (int)i_erx << ", eCh = " << (int)i_ch; + } + for (std::size_t i{0}; i < toas.size(); i++) { - toas[i] = data[i].samples[data[i].i_soi].channel(i_link, i_ch).toa(); + toas[i] = data[i].soi().channel(i_erx, i_ch).toa(); } + /// we assume that the data provided is not empty otherwise the efficiency /// calculation is meaningless + + if (ch == 0 || ch == 36) { + pflib_log(trace) << "[DEBUG TOA] ROC " << i_roc << " Ch " << ch + << " (eRx: " << (int)i_erx << ", eCh: " << (int)i_ch + << ")" + << " first 3 raw TOAs: [" << toas[0] << ", " + << (toas.size() > 1 ? std::to_string(toas[1]) : "N/A") + << ", " + << (toas.size() > 2 ? std::to_string(toas[2]) : "N/A") + << "]"; + } + efficiencies[ch] = pflib::utility::efficiency(toas); + + if (efficiencies[ch] > 0.0) { + pflib_log(debug) << "[DEBUG TOA] Found non-zero efficiency on ROC " + << i_roc << " Ch " << ch << " = " << efficiencies[ch]; + } } return efficiencies; } diff --git a/app/tool/algorithm/get_toa_efficiencies.h b/app/tool/algorithm/get_toa_efficiencies.h index 1b7b21a0a..02933494d 100644 --- a/app/tool/algorithm/get_toa_efficiencies.h +++ b/app/tool/algorithm/get_toa_efficiencies.h @@ -15,6 +15,7 @@ namespace pflib::algorithm { // templated to match any event packet type std::array get_toa_efficiencies( + int i_roc, const pflib::packing::SingleECONDRocErxMapping& mapping, const std::vector& data); } // namespace pflib::algorithm diff --git a/app/tool/algorithm/toa_vref_scan.cxx b/app/tool/algorithm/toa_vref_scan.cxx index 5fff9c53c..fb5627955 100644 --- a/app/tool/algorithm/toa_vref_scan.cxx +++ b/app/tool/algorithm/toa_vref_scan.cxx @@ -1,74 +1,201 @@ #include "toa_vref_scan.h" +#include +#include +#include +#include +#include + #include "../daq_run.h" #include "../tasks/toa_vref_scan.h" #include "get_toa_efficiencies.h" #include "pflib/utility/efficiency.h" #include "pflib/utility/string_format.h" +#include "trim_toa_scan.h" namespace pflib::algorithm { -std::map> toa_vref_scan( - Target* tgt, ROC roc) { +std::map>> +toa_vref_scan(Target* tgt, bool scan_all, bool write_csv, + const std::string& csv_filepath) { static auto the_log_{::pflib::logging::get("toa_vref_scan")}; - /// do a run of 100 samples per toa_vref to measure the TOA - /// efficiency when looking at pedestal data - static const std::size_t n_events = 100; tgt->setup_run(1, Target::DaqFormat::ECOND_SW_HEADERS, 1); - std::array target; - std::array, 2> final_effs; - // TODO 348 - DecodeAndBuffer buffer{n_events, 2}; + std::map> target; // i_roc int for each variable + std::map, 2>> final_effs; + std::map>> + settings; + + for (int i_roc : tgt->roc_ids()) { + target[i_roc][0] = -1; + target[i_roc][1] = -1; + } + + size_t total_links_to_find = tgt->roc_ids().size() * 2; + size_t links_found_count = 0; + + DecodeAndBuffer buffer{n_events, tgt->nrocs() * 2}; + + // create a .csv file to save efficiency and vref data for analysis + std::ofstream csv_file; + + if (write_csv) { + std::string final_path = csv_filepath; + + if (final_path.empty()) { + auto now = std::chrono::system_clock::now(); + auto in_time_t = std::chrono::system_clock::to_time_t(now); + std::stringstream ss; + ss << "toa_vref_scan_data_" + << std::put_time(std::localtime(&in_time_t), "%Y%m%d_%H%M%S") + << ".csv"; + final_path = ss.str(); + } - // loop over runs, from toa_vref = 0 to = 255 - for (int toa_vref{0}; toa_vref < 256; toa_vref++) { + csv_file.open(final_path); + if (!csv_file) { + pflib_log(error) << "Failed to open CSV file for writing: " << final_path; + write_csv = false; + } else { + pflib_log(info) << "Saving scan data to file: " << final_path; + csv_file << "TOA_VREF, ROC"; + for (int chan = 0; chan < 72; ++chan) { + csv_file << "," << chan; + } + csv_file << "\n"; + } + } + + // loop down from 255 to 0 + for (int toa_vref = 255; toa_vref >= 0; toa_vref--) { pflib_log(info) << "testing toa_vref = " << toa_vref; - auto test_handle = roc.testParameters() - .add("REFERENCEVOLTAGE_0", "TOA_VREF", toa_vref) - .add("REFERENCEVOLTAGE_1", "TOA_VREF", toa_vref) - .apply(); + std::map> parameters; + parameters["REFERENCEVOLTAGE_0"]["TOA_VREF"] = toa_vref; + parameters["REFERENCEVOLTAGE_1"]["TOA_VREF"] = toa_vref; + auto test_params = tgt->tempApplyAllROCs(parameters); + usleep(10); + daq_run(tgt, "PEDESTAL", buffer, n_events, 100); + pflib_log(trace) << "finished toa_vref = " << toa_vref << ", getting efficiencies"; - auto efficiencies = get_toa_efficiencies(buffer.get_buffer()); - pflib_log(trace) - << "got channel efficiencies, getting max efficiency per link"; - for (int i_link{0}; i_link < 2; i_link++) { - auto start = efficiencies.begin() + - 36 * i_link; // start at 0 for link 0, 36 for link 1 - auto end = start + 36; - final_effs[i_link][toa_vref] = *std::max_element(start, end); + + for (int i_roc : tgt->roc_ids()) { + const auto& mapping = tgt->getRocErxMapping(); + + auto efficiencies = + get_toa_efficiencies(i_roc, mapping, buffer.get_buffer()); + pflib_log(trace) << "got channel efficiencies for ROC " << i_roc + << ", getting max efficiency per link"; + + // Save raw channel efficiencies to .csv file + if (write_csv) { + csv_file << toa_vref << "," << i_roc; + for (double eff : efficiencies) { + csv_file << "," << eff; + } + csv_file << "\n"; + } + + for (int i_link{0}; i_link < 2; i_link++) { + auto start = efficiencies.begin() + 36 * i_link; + auto end = start + 36; + + double max_eff = *std::max_element(start, end); + + final_effs[i_roc][i_link][toa_vref] = max_eff; + + if (toa_vref % 32 == 0 || max_eff > 0.0) { + pflib_log(trace) << "[DEBUG SCAN] VREF " << toa_vref << " | ROC " + << i_roc << " Link " << i_link + << " | Max Efficiency: " << max_eff; + } + + // iterating from 255 down, find the first non-zero efficiency for each + // link in each roc + if (!scan_all) { + if (target[i_roc][i_link] == -1 && max_eff > 0.0) { + int calculated_vref = toa_vref + 10; + target[i_roc][i_link] = + (calculated_vref > 255) ? 255 : calculated_vref; + links_found_count++; + pflib_log(info) + << "[DEBUG SEARCH] Success. Found threshold edge at VREF " + << toa_vref << " (Setting target to " << target[i_roc][i_link] + << ")"; + } + } + } + } + + if (!scan_all && links_found_count == total_links_to_find) { + pflib_log(info) + << "All links found their threshold early. Breaking VREF loop at " + << toa_vref; + break; } - pflib_log(trace) << "got link efficiencies"; } pflib_log(info) << "sample collections done, deducing settings"; + // get the max toa_vref with non-zero efficiency? Iterate through the array - // from bottom up. - for (int i_link{0}; i_link < 2; i_link++) { - int highest_non_zero_eff = -1; // just a placeholder in case it's not found - for (int toa_vref = final_effs[0].size() - 1; toa_vref >= 0; toa_vref--) { - if (final_effs[i_link][toa_vref] > 0.0) { - highest_non_zero_eff = - toa_vref + 10; // need to add 10 since we don't want to overlap - // with highest pedestals! - break; // should break from link 0 into link 1 + // from top down. + + if (scan_all) { + for (int i_roc : tgt->roc_ids()) { + for (int i_link{0}; i_link < 2; i_link++) { + int highest_non_zero_eff = + -1; // just a placeholder in case it's not found + for (int toa_vref = final_effs[i_roc][i_link].size() - 1; toa_vref >= 0; + toa_vref--) { + if (toa_vref == (int)final_effs[i_roc][i_link].size() - 1) { + pflib_log(trace) + << "[DEBUG SEARCH] Starting backwards search for ROC " << i_roc + << " Link " << i_link << ". Initial val at max VREF: " + << final_effs[i_roc][i_link][toa_vref]; + } + + if (final_effs[i_roc][i_link][toa_vref] > 0.0) { + highest_non_zero_eff = + toa_vref + 10; // need to add 10 since we don't want to + // overlap with highest pedestals! + pflib_log(info) + << "[DEBUG SEARCH] Success. Found threshold edge at VREF " + << toa_vref << " (Setting target to " << highest_non_zero_eff + << ")"; + break; // should break from link 0 into link 1 + } + } + + if (highest_non_zero_eff < 0) { + pflib_log(warn) << "ROC " << i_roc << " link " << i_link + << ": no non-zero TOA efficiency found, skipping"; + continue; + } + + if (highest_non_zero_eff > 255) { + pflib_log(warn) << "ROC " << i_roc << " link " << i_link + << ": deduced TOA_VREF " << highest_non_zero_eff + << " out of range, clamping to 255"; + highest_non_zero_eff = 255; + } + + target[i_roc][i_link] = highest_non_zero_eff; // store value } + // toa_vref is a global parameter (1 value per link) } - target[i_link] = highest_non_zero_eff; // store value } - // toa_vref is a global parameter (1 value per link) - std::map> settings; - for (int i_link{0}; i_link < 2; i_link++) { - std::string page{ - pflib::utility::string_format("REFERENCEVOLTAGE_%d", i_link)}; - settings[page]["TOA_VREF"] = target[i_link]; + for (int i_roc : tgt->roc_ids()) { + for (int i_link{0}; i_link < 2; i_link++) { + std::string page{ + pflib::utility::string_format("REFERENCEVOLTAGE_%d", i_link)}; + settings[i_roc][page]["TOA_VREF"] = target[i_roc][i_link]; + } } return settings; diff --git a/app/tool/algorithm/toa_vref_scan.h b/app/tool/algorithm/toa_vref_scan.h index 633af3a56..1c98c34df 100644 --- a/app/tool/algorithm/toa_vref_scan.h +++ b/app/tool/algorithm/toa_vref_scan.h @@ -15,7 +15,8 @@ namespace pflib::algorithm { * * @note Only functional for single-ROC targets */ -std::map> toa_vref_scan( - Target* tgt, ROC roc); +std::map>> +toa_vref_scan(Target* tgt, bool scan_all = false, bool write_csv = false, + const std::string& csv_filepath = ""); } // namespace pflib::algorithm diff --git a/app/tool/algorithm/trim_toa_scan.cxx b/app/tool/algorithm/trim_toa_scan.cxx index 84cc4fa01..548e4ecf0 100644 --- a/app/tool/algorithm/trim_toa_scan.cxx +++ b/app/tool/algorithm/trim_toa_scan.cxx @@ -65,7 +65,8 @@ std::tuple siegel_regression( namespace pflib::algorithm { std::map> trim_toa_scan( - Target* tgt, ROC roc) { + Target* tgt, ROC roc, + int i_roc) { // added int i_roc index for efficiencies static auto the_log_{::pflib::logging::get("trim_toa_scan")}; /** @@ -95,6 +96,8 @@ std::map> trim_toa_scan( // loop over trim_toa, from trim_toa = 0 to 32 by skipping 4 // loop over calib, from calib = 0 to 800 by skipping over 4 + auto mapping = tgt->getRocErxMapping(); + for (int trim_toa{0}; trim_toa < 32; trim_toa += 4) { pflib_log(info) << "testing trim_toa = " << trim_toa; auto trim_toa_test_builder = roc.testParameters(); @@ -117,7 +120,9 @@ std::map> trim_toa_scan( pflib_log(trace) << "finished trim_toa = " << trim_toa << ", and calib = " << calib << ", getting efficiencies"; - auto efficiencies = get_toa_efficiencies(buffer.get_buffer()); + auto efficiencies = get_toa_efficiencies( + i_roc, mapping, + buffer.get_buffer()); // added i_roc index for efficiencies pflib_log(trace) << "got channel efficiencies, storing now"; for (int ch{0}; ch < 72; ch++) { // need to divide by 4 because index is value/4 from final_data diff --git a/app/tool/algorithm/trim_toa_scan.h b/app/tool/algorithm/trim_toa_scan.h index b5cc75700..7ab2f68f3 100644 --- a/app/tool/algorithm/trim_toa_scan.h +++ b/app/tool/algorithm/trim_toa_scan.h @@ -16,6 +16,6 @@ namespace pflib::algorithm { * @note Only functional for single-ROC targets */ std::map> trim_toa_scan( - Target* tgt, ROC roc); + Target* tgt, ROC roc, int i_roc); } // namespace pflib::algorithm diff --git a/app/tool/tasks/toa_vref_scan.cxx b/app/tool/tasks/toa_vref_scan.cxx index 86667b0bc..8240d6019 100644 --- a/app/tool/tasks/toa_vref_scan.cxx +++ b/app/tool/tasks/toa_vref_scan.cxx @@ -7,15 +7,31 @@ #include "../algorithm/toa_vref_scan.h" void toa_vref_scan(Target* tgt) { - auto roc{tgt->roc(pftool::state.iroc)}; - auto settings = pflib::algorithm::toa_vref_scan(tgt, roc); + bool scan_all = pftool::readline_bool("Scan all VREF values?", false); + + bool write_csv = + pftool::readline_bool("Save raw scan data to a CSV file?", true); + std::string csv_path = ""; + if (write_csv) { + csv_path = pftool::readline_path("toa-vref-scan-data", ".csv"); + } + + auto settings = + pflib::algorithm::toa_vref_scan(tgt, scan_all, write_csv, csv_path); + YAML::Emitter out; out << YAML::BeginMap; - for (const auto& page : settings) { - out << YAML::Key << page.first; + + for (const auto& [i_roc, page_map] : settings) { + out << YAML::Key << i_roc; out << YAML::Value << YAML::BeginMap; - for (const auto& param : page.second) { - out << YAML::Key << param.first << YAML::Value << param.second; + for (const auto& page : page_map) { + out << YAML::Key << page.first; + out << YAML::Value << YAML::BeginMap; + for (const auto& param : page.second) { + out << YAML::Key << param.first << YAML::Value << param.second; + } + out << YAML::EndMap; } out << YAML::EndMap; } @@ -25,14 +41,15 @@ void toa_vref_scan(Target* tgt) { std::cout << out.c_str() << std::endl; } - if (pftool::readline_bool("Apply settings to the chip? ", true)) { - roc.applyParameters(settings); + if (pftool::readline_bool("Apply settings to the chips? ", true)) { + for (const auto& [i_roc, page_map] : settings) { + tgt->roc(i_roc).applyParameters(page_map); + } } if (pftool::readline_bool("Save settings to a file? ", false)) { - std::string fname = pftool::readline_path( - "toa-vref-scan-" + std::to_string(pftool::state.iroc) + "-settings", - ".yaml"); + std::string fname = + pftool::readline_path("toa-vref-scan-settings", ".yaml"); std::ofstream f{fname}; if (not f.is_open()) { diff --git a/app/tool/tasks/trim_toa_scan.cxx b/app/tool/tasks/trim_toa_scan.cxx index e78bc849d..45be07b21 100644 --- a/app/tool/tasks/trim_toa_scan.cxx +++ b/app/tool/tasks/trim_toa_scan.cxx @@ -8,7 +8,7 @@ void trim_toa_scan(Target* tgt) { auto roc{tgt->roc(pftool::state.iroc)}; - auto settings = pflib::algorithm::trim_toa_scan(tgt, roc); + auto settings = pflib::algorithm::trim_toa_scan(tgt, roc, pftool::state.iroc); YAML::Emitter out; out << YAML::BeginMap; for (const auto& page : settings) {