EnergySpectrumAnalyer/src/DataProcessWorkPool.cpp

938 lines
41 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "DataProcessWorkPool.h"
#include "MeasureAnalysisProjectModel.h"
#include "GlobalDefine.h"
#include "csv.h"
#include <QDir>
#include <QThreadPool>
#include <algorithm>
#include <fstream>
#include <memory>
#include <queue>
#include <sstream>
#include <string>
#include <vector>
#include <QVariant>
#include <QFuture>
#include <QtConcurrent>
#include <QProcess>
#include <QProcessEnvironment>
#include "DataCalcProcess/MathModelDefine.h"
#include "DataCalcProcess/FindPeaksBySvd.h"
#include "DataCalcProcess/GaussPolyCoe.h"
#include "DataCalcProcess/NolinearLeastSquaresCurveFit.h"
#include "EnergyScaleDataModel.h"
#include "DataCalcProcess/CoincidenceSpectrumProcess.h"
#include <QDebug>
using namespace DataProcessWorkPool;
using namespace io;
void DataProcessTask::SetFinishedNotifier(QObject* finished_notifier, const char* finished_process, const QString& project_name)
{
this->_finished_notifier = finished_notifier;
this->_finished_notifier_process = finished_process;
this->_project_name = project_name;
}
const QString& DataProcessTask::GetProjectName() const
{
return this->_project_name;
}
const char* DataProcessTask::GetFinishedNotifierProcess() const
{
return this->_finished_notifier_process;
}
QObject* DataProcessTask::GetFinishedNotifier() const
{
return this->_finished_notifier;
}
bool DataProcessTask::IsValidSetWorkParameters() const
{
return !(this->_project_name.isEmpty());
}
void DataProcessTask::StartTask()
{
QThreadPool::globalInstance()->start(this);
}
void DataProcessTask::run()
{
if (!IsValidSetWorkParameters()) {
return;
}
bool task_ok = processTask();
if ((GetFinishedNotifier() != nullptr) && (GetFinishedNotifierProcess() != nullptr)) {
QMetaObject::invokeMethod(
_finished_notifier,
_finished_notifier_process,
Qt::QueuedConnection,
Q_ARG(bool, task_ok),
Q_ARG(QString, _project_name),
Q_ARG(QVariant, _task_result_data)
);
}
}
void DataProcessTask::updateTaskResultData(const QVariant &task_result_data)
{
this->_task_result_data = task_result_data;
}
void ParticleDataTask::SetAllChannelParticleDataFilename(const QString& all_channel_particle_data_filename)
{
this->_all_channel_particle_data_filename = all_channel_particle_data_filename;
}
const QString& ParticleDataTask::GetAllChannelParticleDataFilename() const
{
return this->_all_channel_particle_data_filename;
}
bool ParticleDataTask::IsValidSetWorkParameters() const
{
return (!GetAllChannelParticleDataFilename().isEmpty()) && DataProcessTask::IsValidSetWorkParameters();
}
bool ParticleDataTask::processTask()
{
return processEveryChannelParticleData();
}
void EveryChannelParticleCountDataTask::SetAllChannelCountResultDir(const QString& dir_path)
{
this->_all_ch_count_dir = dir_path;
}
const QString& EveryChannelParticleCountDataTask::GetAllChannelCountResultDir() const
{
return this->_all_ch_count_dir;
}
void EveryChannelParticleCountDataTask::SetEveryChannelCountResultDir(const QString& dir_path)
{
this->_every_ch_count_dir = dir_path;
}
const QString& EveryChannelParticleCountDataTask::GetEveryChannelCountResultDir() const
{
return this->_every_ch_count_dir;
}
bool EveryChannelParticleCountDataTask::IsValidSetWorkParameters() const
{
return (!GetAllChannelCountResultDir().isEmpty()) && (!GetEveryChannelCountResultDir().isEmpty()) && ParticleDataTask::IsValidSetWorkParameters();
}
bool EveryChannelParticleCountDataTask::processEveryChannelParticleData()
{
bool ret_ok = true;
const QString& all_ch_count_dir = GetAllChannelCountResultDir();
const QString& every_ch_count_dir = GetEveryChannelCountResultDir();
QDir all_ch_count_output_dir(all_ch_count_dir);
all_ch_count_output_dir.mkpath(all_ch_count_dir);
QDir every_ch_count_output_dir(every_ch_count_dir);
every_ch_count_output_dir.mkpath(every_ch_count_dir);
const QString& all_channel_particle_data_filename = GetAllChannelParticleDataFilename();
QMap<uint, QString> particle_count_filename_list;
try {
// 统计每个通道的粒子计数(相同板卡号通道号相同道址)
QMap<uint, QMap<uint, unsigned long long>> channel_address_counts; // 通道号 -> 地址 -> 计数
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
// 使用更灵活的方式处理CSV文件忽略额外列
io::CSVReader<
4,
io::trim_chars<' ', '\t'>,
io::double_quote_escape<',', '"'>,
io::throw_on_overflow,
io::empty_line_comment>
reader(QStrToSysPath(all_channel_particle_data_filename));
reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, address_str, time_str);
uint board_id;
uint channel_id;
uint address;
unsigned long long time;
while (reader.read_row(board_id, channel_id, address, time)) {
// 板卡和通道号计算,通道号 = 板卡号 * 4 + 通道号
int channel_num = (board_id) * 4 + (channel_id + 1);
// 统计每个通道的粒子计数
if (!channel_address_counts.contains(channel_num)) {
channel_address_counts[channel_num] = QMap<uint, unsigned long long>();
}
channel_address_counts[channel_num][address]++;
}
// 写入每个通道的粒子计数数据(优化:使用一次打开文件,批量写入)
QMap<uint, std::shared_ptr<std::ofstream>> channel_file_streams;
// 预创建所有通道的文件流
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) {
uint channel_num = channel_it.key();
QString count_data_filename = every_ch_count_output_dir.filePath(QStringLiteral(u"通道%1粒子计数.csv").arg(channel_num));
particle_count_filename_list.insert(channel_num, count_data_filename);
// 创建文件流
std::shared_ptr<std::ofstream> out(new std::ofstream(QStrToSysPath(count_data_filename)));
channel_file_streams[channel_num] = out;
*out << QString(QStringLiteral(u"道址")).toStdString() << "," << QString(QStringLiteral(u"计数")).toStdString() << std::endl;
}
// 批量写入数据
for (auto channel_it = channel_address_counts.begin(); channel_it != channel_address_counts.end(); ++channel_it) {
uint channel_num = channel_it.key();
const QMap<uint, unsigned long long>& address_counts = channel_it.value();
auto out_stream = channel_file_streams[channel_num];
for (auto address_it = address_counts.begin(); address_it != address_counts.end(); ++address_it) {
uint address = address_it.key();
unsigned long long count = address_it.value();
*out_stream << address << "," << count << std::endl;
}
}
channel_file_streams.clear();
} catch (const std::runtime_error& e) {
const QString& e_what = QString::fromLatin1(e.what());
QString error = QStringLiteral(u"处理%1发生运行时异常:%2").arg(all_channel_particle_data_filename).arg(e_what);
LOG_ERROR(error)
ret_ok = false;
} catch (const std::exception& e) {
const QString& e_what = QString::fromLatin1(e.what());
QString error = QStringLiteral(u"处理%1异常:%2").arg(all_channel_particle_data_filename).arg(e_what);
LOG_ERROR(error)
ret_ok = false;
} catch (...) {
QString error = QStringLiteral(u"处理%1未知异常.").arg(all_channel_particle_data_filename);
LOG_ERROR(error)
ret_ok = false;
}
const QString& project_name = GetProjectName();
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model == nullptr) {
ret_ok = false;
} else {
// 更新项目模型中的通道粒子计数数据文件名
for (auto it = particle_count_filename_list.begin(); it != particle_count_filename_list.end(); ++it) {
project_model->SetChannelAddressCountDataFilename(it.key(), it.value());
}
}
const QString& info = QStringLiteral(u"所有通道粒子计数处理完成.");
LOG_INFO(info);
return ret_ok;
}
////////////////////////////////////////////////////////////////////////////////////
void ParticleDataSortTask::SetSortedResultDir(const QString& sorted_result_dir)
{
this->_sorted_result_dir = sorted_result_dir;
}
const QString& ParticleDataSortTask::GetSortedResultDir() const
{
return this->_sorted_result_dir;
}
bool ParticleDataSortTask::IsValidSetWorkParameters() const
{
return (!GetSortedResultDir().isEmpty()) && ParticleDataTask::IsValidSetWorkParameters();
}
struct CsvRow {
uint board_id;
uint channel_id;
uint address;
unsigned long long time;
size_t chunk_index;
bool operator<(const CsvRow& other) const
{
return time > other.time;
}
};
std::vector<std::string> splitFile(const std::string& input_file, size_t chunk_size)
{
std::vector<std::string> chunks;
try {
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
io::CSVReader<
4,
io::trim_chars<' ', '\t'>,
io::double_quote_escape<',', '"'>,
io::throw_on_overflow,
io::empty_line_comment
> reader(input_file);
reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, address_str, time_str);
int chunk_index = 0;
while (true) {
std::vector<CsvRow> rows;
size_t current_size = 0;
uint board_id;
uint channel_id;
uint address;
unsigned long long time;
while (reader.read_row(board_id, channel_id, address, time)) {
CsvRow row;
row.board_id = board_id;
row.channel_id = channel_id;
row.address = address;
row.time = time;
// Estimate row size
current_size += std::to_string(board_id).size() + std::to_string(channel_id).size() + std::to_string(address).size() + std::to_string(time).size() + 4;
if (current_size > chunk_size && !rows.empty()) {
break;
}
rows.push_back(row);
}
if (rows.empty())
break;
std::sort(rows.begin(), rows.end(), [](const CsvRow& a, const CsvRow& b) {
return a.time < b.time;
});
std::string chunk_file = input_file + ".chunk" + std::to_string(chunk_index);
std::ofstream out_file(chunk_file);
for (const auto& row : rows) {
out_file << row.board_id << "," << row.channel_id << "," << row.address << "," << row.time << "\n";
}
out_file.close();
chunks.push_back(chunk_file);
chunk_index++;
}
} catch (const std::exception& e) {
throw(e);
}
return chunks;
}
void mergeChunks(const std::vector<std::string>& chunks, const std::string& output_file)
{
std::vector<std::unique_ptr<io::CSVReader<4>>> chunk_readers;
std::priority_queue<CsvRow> min_heap;
for (const auto& chunk : chunks) {
auto reader = std::make_unique<io::CSVReader<4>>(chunk);
chunk_readers.push_back(std::move(reader));
}
for (size_t i = 0; i < chunk_readers.size(); i++) {
uint board_id;
uint channel_id;
uint address;
unsigned long long time;
if (chunk_readers[i]->read_row(board_id, channel_id, address, time)) {
CsvRow row;
row.board_id = board_id;
row.channel_id = channel_id;
row.address = address;
row.time = time;
row.chunk_index = i;
min_heap.push(row);
}
}
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
std::ofstream outFile(output_file);
outFile << board_id_str << "," << channel_id_str << "," << address_str << "," << time_str << "\n";
while (!min_heap.empty()) {
CsvRow current = min_heap.top();
min_heap.pop();
outFile << current.board_id << "," << current.channel_id << "," << current.address << "," << current.time << "\n";
size_t chunk_index = current.chunk_index;
if (chunk_readers[chunk_index]) {
uint board_id;
uint channel_id;
uint address;
unsigned long long time;
if (chunk_readers[chunk_index]->read_row(board_id, channel_id, address, time)) {
CsvRow row;
row.board_id = board_id;
row.channel_id = channel_id;
row.address = address;
row.time = time;
row.chunk_index = chunk_index;
min_heap.push(row);
} else {
chunk_readers[chunk_index].reset();
}
}
}
outFile.close();
for (const auto& chunk : chunks) {
std::remove(chunk.c_str());
}
}
bool ParticleDataSortTask::processEveryChannelParticleData()
{
bool ret_ok = true;
const QString& sorted_result_dir = GetSortedResultDir();
QDir sorted_result_output_dir(sorted_result_dir);
sorted_result_output_dir.mkpath(sorted_result_dir);
const QString& all_channel_particle_data_filename = GetAllChannelParticleDataFilename();
QString sorted_output_filename = sorted_result_output_dir.filePath(QStringLiteral(u"粒子数据.csv"));
try {
const size_t CHUNK_SIZE = 100 * 1024 * 1024; // 100MB chunks
std::vector<std::string> chunks = splitFile(QStrToSysPath(all_channel_particle_data_filename), CHUNK_SIZE);
if (chunks.empty()) {
std::ifstream in_file(QStrToSysPath(all_channel_particle_data_filename));
std::ofstream out_file(QStrToSysPath(sorted_output_filename));
std::string line;
while (std::getline(in_file, line)) {
out_file << line << "\n";
}
in_file.close();
out_file.close();
} else {
mergeChunks(chunks, QStrToSysPath(sorted_output_filename));
}
} catch (const std::exception& e) {
const QString& e_what = QString::fromLatin1(e.what());
QString error = QString(QStringLiteral(u"处理%1异常:%2")).arg(all_channel_particle_data_filename).arg(e_what);
LOG_ERROR(error);
ret_ok = false;
} catch (...) {
QString error = QString(QStringLiteral(u"处理%1未知异常.")).arg(all_channel_particle_data_filename);
LOG_ERROR(error);
ret_ok = false;
}
this->updateTaskResultData(QVariant(sorted_output_filename));
return ret_ok;
}
bool ParticleDataSortByMinimysTask::processEveryChannelParticleData()
{
bool ret_ok = true;
const QString& sorted_result_dir = GetSortedResultDir();
QDir sorted_result_output_dir(sorted_result_dir);
sorted_result_output_dir.mkpath(sorted_result_dir);
const QString& data_filename = GetAllChannelParticleDataFilename();
QString output_filename = sorted_result_output_dir.filePath(QStringLiteral(u"粒子数据.csv"));
QString minimsys = QDir(qApp->applicationDirPath()).filePath(QString("minimsys"));
QString bash = QDir(minimsys).filePath(QString("bash"));
QString head = QDir(minimsys).filePath(QString("head"));
QString tail = QDir(minimsys).filePath(QString("tail"));
QString sort = QDir(minimsys).filePath(QString("sort"));
QString cmd = QString("(%1 -n 1 %4 && %2 -n +2 %4 | %3 -t ',' -k4 -n --parallel=8 -S 4G) > %5").arg(head).arg(tail).arg(sort).arg(data_filename).arg(output_filename);
QProcess proc;
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
env.clear();
env.insert("PATH", minimsys);
proc.setProcessEnvironment(env);
proc.setWorkingDirectory(minimsys);
proc.start(bash, { "-c", cmd });
proc.waitForFinished(-1);
if (proc.exitCode() != 0) {
QString process_error = QString(proc.readAllStandardError());
QString error = QStringLiteral(u"处理%1异常:%2").arg(data_filename).arg(process_error);
LOG_ERROR(error);
ret_ok = false;
}
this->updateTaskResultData(QVariant(output_filename));
return ret_ok;
}
bool CoincidenceEventAnalysisTask::processTask()
{
const QString& project_name = GetProjectName();
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model == nullptr) {
return false;
}
const QString& particle_data_filename = project_model->GetAllChannelParticleDataFilename();
if (particle_data_filename.isEmpty()) {
return false;
}
std::string event_id_str = QString(QStringLiteral(u"事件ID")).toStdString();
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
std::string addr_str = QString(QStringLiteral(u"道址")).toStdString();
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
try {
io::CSVReader<
4,
io::trim_chars<' ', '\t'>,
io::double_quote_escape<',', '"'>,
io::throw_on_overflow,
io::empty_line_comment>
reader(QStrToSysPath(particle_data_filename));
reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, addr_str, time_str);
using namespace CoincidenceSpectrum::F2t9Order;
std::vector<SpectrumData> spec_data;
SpectrumData spec_data_item;
while (reader.read_row(
spec_data_item.board_id,
spec_data_item.channel_id,
spec_data_item.energy,
spec_data_item.timestamp)) {
spec_data.push_back(spec_data_item);
}
std::vector<CoincidenceEvent> events;
if (!ProcessCoincidence(spec_data, events, project_model->GetConformTimeWin())) {
LOG_WARN(QStringLiteral(u"粒子符合数据处理异常!"));
}
const QString& coincidence_data_dir_name = QStringLiteral(u"粒子符合数据");
QDir project_dir(project_model->GetProjectDir());
project_dir.mkpath(coincidence_data_dir_name);
const QString& coincidence_data_dir_path = project_dir.filePath(coincidence_data_dir_name);
QMap<int, std::shared_ptr<std::ofstream> > coincidence_data_out_stream_map;
unsigned long long event_id = 0;
for (const CoincidenceEvent& event : events) {
++ event_id;
const QString& event_data_filename = QDir(coincidence_data_dir_path).filePath(QStringLiteral(u"[%1ns]%2个粒子符合事件[未刻度].csv").arg(project_model->GetConformTimeWin()).arg(event.coincidence_order));
if ( !coincidence_data_out_stream_map.contains(event.coincidence_order) ) {
std::shared_ptr<std::ofstream> out_stream(new std::ofstream(QStrToSysPath(event_data_filename)));
*out_stream << event_id_str << "," << board_id_str << "," << channel_id_str << "," << addr_str << "," << time_str << std::endl;
coincidence_data_out_stream_map[event.coincidence_order] = out_stream;
}
std::shared_ptr<std::ofstream> out_stream = coincidence_data_out_stream_map[event.coincidence_order];
for (const SpectrumData& data_item : event.events) {
*out_stream << event_id << "," << data_item.board_id << "," << data_item.channel_id << "," << data_item.energy << "," << data_item.timestamp << std::endl;
}
project_model->SetTimeWinConformParticleData(project_model->GetConformTimeWin(), event.coincidence_order, event_data_filename);
}
} catch (const std::exception& e) {
const QString& e_what = QString::fromUtf8(e.what());
LOG_WARN(QStringLiteral(u"粒子符合数据处理异常:%1").arg(e_what));
return false;
}
const QString& info = QStringLiteral(u"粒子符合数据处理完成.");
LOG_INFO(info);
return true;
}
void AutoFindPeaksTask::SetAnalysisType(AnalysisType analysis_type)
{
this->_analysis_type = analysis_type;
}
void AutoFindPeaksTask::SetDataFileList(const QMap<QString, QVariant> &data_files_set)
{
this->_data_files_set = data_files_set;
}
void AutoFindPeaksTask::SetResultDir(const QString &result_dir)
{
this->_result_dir = result_dir;
}
void AutoFindPeaksTask::SetFindPeakSetpWinWidth(int step_win_width)
{
this->_step_win_width = step_win_width;
}
bool AutoFindPeaksTask::IsValidSetWorkParameters() const
{
return (!this->_data_files_set.isEmpty()) && (!this->_result_dir.isEmpty()) && DataProcessTask::IsValidSetWorkParameters();
}
bool AutoFindPeaksTask::processTask()
{
QString result_filename = QDir(this->_result_dir).filePath(QStringLiteral(u"自动寻峰结果.csv"));
std::ofstream out_file(QStrToSysPath(result_filename));
std::string channel_str = QString(QStringLiteral(u"通道")).toStdString();
std::string addr_str = QString(QStringLiteral(u"峰位")).toStdString();
std::string left_addr_str = QString(QStringLiteral(u"左边界")).toStdString();
std::string lright_addr_str = QString(QStringLiteral(u"右边界")).toStdString();
std::string width_str = QString(QStringLiteral(u"峰宽")).toStdString();
std::string height_str = QString(QStringLiteral(u"峰高")).toStdString();
std::string fwhm_str = QString(QStringLiteral(u"FWHM")).toStdString();
std::string area_str = QString(QStringLiteral(u"峰面积")).toStdString();
out_file << channel_str << "," << addr_str << "," << left_addr_str << "," << lright_addr_str
<< "," << width_str << "," << height_str << "," << fwhm_str << "," << area_str << "\n";
QStringList ch_count_data_name = this->_data_files_set.keys();
std::sort(ch_count_data_name.begin(), ch_count_data_name.end(), [](const QString& a, const QString& b) {
int num_a = ExtractNumberFromString(a);
int num_b = ExtractNumberFromString(b);
return num_a < num_b;
});
for (const QString& ch_count_data_name : ch_count_data_name) {
if (ch_count_data_name.contains(ch_count_data_name)) {
const QString& ch_count_data_filename = this->_data_files_set.value(ch_count_data_name).toString();
std::string channel = ch_count_data_name.toStdString();
arma::mat data;
const std::string data_filename = QStrToSysPath(ch_count_data_filename);
if (!data.load(data_filename, arma::csv_ascii)) {
QString error = QString(QStringLiteral(u"%1自动寻峰数据加载异常!")).arg(ch_count_data_name);
LOG_WARN(error);
continue;
}
std::vector<FindPeaksBySvd::PeakInfo> peak_info_vec;
try {
peak_info_vec = FindPeaksBySvd().FindPeaks(data, this->_step_win_width);
} catch (const std::string& e) {
QString error = QString(QStringLiteral(u"%1自动寻峰异常:%2!")).arg(ch_count_data_name).arg(QString::fromStdString(e));
LOG_WARN(error);
continue;
}
if (!peak_info_vec.empty()) {
for(auto peak_info : peak_info_vec) {
int addr = peak_info.pos;
int left = peak_info.left;
int right = peak_info.left + peak_info.width;
int width = peak_info.width;
int height = peak_info.height;
int fwhm = peak_info.fwhm;
double area = peak_info.area;
out_file << channel << "," << addr << "," << left << "," << right << ","
<< width << "," << height << "," << fwhm << "," << area << "\n";
}
} else {
const QString& error = QStringLiteral(u"%1自动寻峰异常!").arg(ch_count_data_name);
LOG_WARN(error);
}
const QString& info = QStringLiteral(u"%1自动寻峰完成.").arg(ch_count_data_name);
LOG_INFO(info);
}
}
const QString& project_name = GetProjectName();
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model == nullptr) {
return false;
} else {
project_model->SetAnalysisCustomData(this->_analysis_type, QString("AutoFindPeaksResult"), result_filename);
}
const QString& info = QStringLiteral(u"自动寻峰完成.");
LOG_INFO(info);
return true;
}
void ChannelEnergyScaleFittingTask::SetData(const FitDataMap& channel_energy_scale_fit_data_map, const QMap<QString, int>& fit_degree_map)
{
this->_channel_energy_scale_fit_data_map = channel_energy_scale_fit_data_map;
this->_fit_degree_map = fit_degree_map;
}
void ChannelEnergyScaleFittingTask::SetResultDir(const QString &result_dir)
{
this->_result_dir = result_dir;
}
bool ChannelEnergyScaleFittingTask::IsValidSetWorkParameters() const
{
return (!this->_channel_energy_scale_fit_data_map.isEmpty()) && (!this->_fit_degree_map.isEmpty())&& (!this->_result_dir.isEmpty()) && DataProcessTask::IsValidSetWorkParameters();
}
bool ChannelEnergyScaleFittingTask::processTask()
{
QDir result_dir(this->_result_dir);
const QString& energy_scale_data_filename = result_dir.filePath(QStringLiteral(u"多通道能量刻度拟合结果.json"));
EnergyScaleDataModel energy_scale_data_model(energy_scale_data_filename);
for (const QString& channel : this->_channel_energy_scale_fit_data_map.keys()) {
const QMap<int, QList<double> >& energy_scale_fit_data = this->_channel_energy_scale_fit_data_map.value(channel);
if (energy_scale_fit_data.isEmpty()) {
continue;
}
int fit_degree = this->_fit_degree_map.value(channel);
if (fit_degree <= 0) {
continue;
}
std::vector<double> vec_x, vec_y1, vec_y2;
for (const int& addr : energy_scale_fit_data.keys()) {
vec_x.push_back(addr);
vec_y1.push_back(energy_scale_fit_data.value(addr)[0]);
vec_y2.push_back(energy_scale_fit_data.value(addr)[1]);
}
std::vector<double> energy_scale_fit_result_coeffs;
try {
energy_scale_fit_result_coeffs = GaussPolyCoe::PolynomialFit(vec_x, vec_y1, fit_degree);
} catch(const std::exception& e) {
QString error = QString(QStringLiteral(u"%1能量刻度多项式%2次拟合异常:%3!")).arg(channel).arg(fit_degree).arg(QString::fromStdString(e.what()));
LOG_WARN(error);
}
arma::vec fwhm_fit_result_coeffs;
try {
fwhm_fit_result_coeffs = NolinearLeastSquaresCurveFit::Lsqcurvefit(FwhmModel, vec_y1, vec_y2, { 1.0, 1.0 });
} catch(const std::exception& e) {
QString error = QString(QStringLiteral(u"%1分辨率拟合异常:%2!")).arg(channel).arg(QString::fromStdString(e.what()));
LOG_WARN(error);
}
const QString& info = QStringLiteral(u"%1能量刻度拟合完成.").arg(channel);
LOG_INFO(info);
std::vector<std::vector<double> > fit_result_data;
for (int i = 0; i < vec_x.size(); i++) {
int addr = vec_x[i];
double set_energy = vec_y1[i];
double fit_energy = GaussPolyCoe::Predict(energy_scale_fit_result_coeffs, addr);
double peak_fwhm = vec_y2[i];
double fit_fwhm = FwhmModel(addr, fwhm_fit_result_coeffs);
std::vector<double> fit_result_item_data;
fit_result_item_data.push_back(addr);
fit_result_item_data.push_back(set_energy);
fit_result_item_data.push_back(fit_energy);
fit_result_item_data.push_back(set_energy - fit_energy);
fit_result_item_data.push_back(peak_fwhm);
fit_result_item_data.push_back(fit_fwhm);
fit_result_item_data.push_back(peak_fwhm - fit_fwhm);
fit_result_data.push_back(fit_result_item_data);
}
energy_scale_data_model.SetEnergyFitDegree(channel, fit_degree);
energy_scale_data_model.SetEnergyFitResultCoeffs(channel, energy_scale_fit_result_coeffs);
energy_scale_data_model.SetFwhmFitResultCoeffs(channel, std::vector<double>(fwhm_fit_result_coeffs.begin(), fwhm_fit_result_coeffs.end()));
energy_scale_data_model.SetFitData(channel, fit_result_data);
}
if (!energy_scale_data_model.SaveData()) {
return false;
}
const QString& info = QStringLiteral(u"能量刻度拟合完成.");
LOG_INFO(info);
return true;
}
bool EnergyScaleParticleDataTask::processTask()
{
const QString& project_name = GetProjectName();
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model == nullptr) {
return false;
}
EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename());
if (!energy_scale_data_model.LoadData()) {
return false;
}
if (!energy_scale_data_model.IsValid()) {
return false;
}
const QString& all_channel_particle_data_filename = project_model->GetAllChannelParticleDataFilename();
if (all_channel_particle_data_filename.isEmpty()) {
return false;
}
const QString& energy_spectrum_filename = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"能谱数据.csv"));
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
std::string energy_str = QString(QStringLiteral(u"能量(KeV)")).toStdString();
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
std::ofstream out(QStrToSysPath(energy_spectrum_filename));
out << board_id_str << "," << channel_id_str << "," << energy_str << "," << time_str<< "\n" ;
try {
io::CSVReader<
4,
io::trim_chars<' ', '\t'>,
io::double_quote_escape<',', '"'>,
io::throw_on_overflow,
io::empty_line_comment>
reader(QStrToSysPath(all_channel_particle_data_filename));
reader.read_header(io::ignore_extra_column, board_id_str, channel_id_str, address_str, time_str);
uint board_id;
uint channel_id;
uint address;
unsigned long long time;
while (reader.read_row(board_id, channel_id, address, time)) {
int channel_num = (board_id) * 4 + (channel_id + 1);
const QString& channel_name = QStringLiteral(u"通道%1").arg(channel_num);
auto coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name);
if (!coeffs.empty()) {
double energy = GaussPolyCoe::Predict(coeffs, address);
out << board_id << "," << channel_id << "," << energy << "," << time << "\n";
}
}
out.close();
project_model->SetParticleEnergyDataFilename(energy_spectrum_filename);
} catch (const std::exception& e) {
out.close();
std::remove(QStrToSysPath(energy_spectrum_filename));
const QString& e_what = QString::fromStdString(e.what());
LOG_WARN(QStringLiteral(u"能谱数据处理异常:%1").arg(e_what));
return false;
}
const QString& info = QStringLiteral(u"能谱数据处理完成.");
LOG_INFO(info);
return true;
}
bool EnergyCountProcessTask::processTask()
{
const QString& project_name = GetProjectName();
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model == nullptr) {
return false;
}
EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename());
if (!energy_scale_data_model.LoadData()) {
return false;
}
if (!energy_scale_data_model.IsValid()) {
return false;
}
const QMap<uint, QString>& ch_addr_count_filename_list = project_model->GetChannelAddressCountDataFilenameList();
if (ch_addr_count_filename_list.isEmpty()) {
LOG_WARN(QStringLiteral(u"能量计数统计需要的通道道址计数文件异常!"));
return false;
}
const QString& out_path = QDir(project_model->GetProjectDir()).filePath(QStringLiteral(u"能量计数"));
if ( !QDir(out_path).mkpath(out_path) ) {
LOG_WARN(QStringLiteral(u"创建能量计数数据目录\"%1\"异常!").arg(out_path));
return false;
}
std::string address_str = QString(QStringLiteral(u"道址")).toStdString();
std::string energy_str = QString(QStringLiteral(u"能量(KeV)")).toStdString();
std::string count_str = QString(QStringLiteral(u"计数")).toStdString();
double bin_width = 0.1f;
std::map<double, unsigned long long> stat_map;
for (const uint& channel_num : ch_addr_count_filename_list.keys()) {
const QString& channel_name = QStringLiteral(u"通道%1").arg(channel_num);
const QString& data_filename = ch_addr_count_filename_list[channel_num];
const QString& ch_out_filename = QDir(out_path).filePath(channel_name + ".csv");
std::ofstream ch_out(QStrToSysPath(ch_out_filename));
ch_out << energy_str << "," << count_str << "\n" ;
try {
io::CSVReader<
2,
io::trim_chars<' ', '\t'>,
io::double_quote_escape<',', '"'>,
io::throw_on_overflow,
io::empty_line_comment>
reader(QStrToSysPath(data_filename));
reader.read_header(io::ignore_extra_column, address_str, count_str);
uint address;
unsigned long long count;
while (reader.read_row(address, count)) {
auto coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name);
if (!coeffs.empty()) {
double energy = GaussPolyCoe::Predict(coeffs, address);
ch_out << energy << "," << count << "\n";
// 计算属于哪个能量 bin
double bin_energy = floor(energy / bin_width) * bin_width;
// 统一保留 3 位
bin_energy = round(bin_energy * 1000) / 1000.0;
stat_map[bin_energy] += count;
}
}
ch_out.close();
project_model->SetChannelEnergyCountDataFilename(channel_num, ch_out_filename);
} catch (const std::exception& e) {
ch_out.close();
std::remove(QStrToSysPath(ch_out_filename));
const QString& e_what = QString::fromStdString(e.what());
LOG_WARN(QStringLiteral(u"%1能量计数异常:%2").arg(channel_name).arg(e_what));
}
}
const QString& out_filename = QDir(out_path).filePath(QStringLiteral(u"全通道.csv"));
std::ofstream out(QStrToSysPath(out_filename));
out << energy_str << "," << count_str << "\n" ;
for (const auto& [energy, count] : stat_map) {
out << energy << "," << count << "\n";
}
project_model->SetAllChannelEnergyTotalCountDataFilename(out_filename);
const QString& info = QStringLiteral(u"能量计数处理完成.");
LOG_INFO(info);
return true;
}
bool EnergyScaleCoincidenceDataTask::processTask()
{
const QString& project_name = GetProjectName();
MeasureAnalysisProjectModel* project_model = ProjectList::Instance()->GetProjectModel(project_name);
if (project_model == nullptr) {
return false;
}
EnergyScaleDataModel energy_scale_data_model(project_model->GetEnergyScaleFilename());
if (!energy_scale_data_model.LoadData()) {
return false;
}
if (!energy_scale_data_model.IsValid()) {
return false;
}
const QString& coincidence_data_dir_name = QStringLiteral(u"符合能谱数据");
QDir project_dir(project_model->GetProjectDir());
project_dir.mkpath(coincidence_data_dir_name);
const QString& coincidence_data_dir_path = project_dir.filePath(coincidence_data_dir_name);
std::string event_id_str = QString(QStringLiteral(u"事件ID")).toStdString();
std::string board_id_str = QString(QStringLiteral(u"板卡号")).toStdString();
std::string channel_id_str = QString(QStringLiteral(u"通道号")).toStdString();
std::string addr_str = QString(QStringLiteral(u"道址")).toStdString();
std::string energy_str = QString(QStringLiteral(u"能量(KeV)")).toStdString();
std::string time_str = QString(QStringLiteral(u"时间计数")).toStdString();
auto conformParticleDataEnergyScale = [&](int time_win, int coincidence_order, const QString& conform_particle_data_filename) -> bool {
try {
io::CSVReader<
5,
io::trim_chars<' ', '\t'>,
io::double_quote_escape<',', '"'>,
io::throw_on_overflow,
io::empty_line_comment>
reader(QStrToSysPath(conform_particle_data_filename));
reader.read_header(io::ignore_extra_column, event_id_str, board_id_str, channel_id_str, addr_str, time_str);
const QString& data_name = QStringLiteral(u"[%1ns]%2个粒子符合事件.csv").arg(time_win).arg(coincidence_order);
const QString& coincidence_energy_data_filename = QDir(coincidence_data_dir_path).filePath(data_name);
std::ofstream out_stream(QStrToSysPath(coincidence_energy_data_filename));
out_stream << event_id_str << "," << board_id_str << "," << channel_id_str << "," << energy_str << "," << time_str << "\n" ;
using namespace CoincidenceSpectrum::F2t9Order;
unsigned long long event_id;
SpectrumData data_item;
while (reader.read_row(
event_id,
data_item.board_id,
data_item.channel_id,
data_item.energy,
data_item.timestamp)) {
int channel_num = (data_item.board_id) * 4 + (data_item.channel_id + 1);
const QString& channel_name = QStringLiteral(u"通道%1").arg(channel_num);
auto coeffs = energy_scale_data_model.GetEnergyFitResultCoeffs(channel_name);
if (!coeffs.empty()) {
data_item.energy = GaussPolyCoe::Predict(coeffs, data_item.energy);
out_stream << event_id << "," << data_item.board_id << "," << data_item.channel_id << "," << data_item.energy << "," << data_item.timestamp << "\n" ;
} else {
LOG_WARN(QStringLiteral(u"符合能谱数据处理异常:%1能量刻度拟合参数为空!").arg(channel_name));
// out_stream.close();
// QFile::remove(coincidence_energy_data_filename);
// return false;
}
}
out_stream.close();
project_model->SetTimeWinConformEnergyData(time_win, coincidence_order, coincidence_energy_data_filename);
} catch (const std::exception& e) {
const QString& e_what = QString::fromStdString(e.what());
LOG_WARN(QStringLiteral(u"符合能谱数据处理异常:%1").arg(e_what));
return false;
}
return true;
};
const auto& conform_particle_data_filename_list = project_model->GetConformParticleDataFilenameList();
for (const auto& time_win : conform_particle_data_filename_list.keys()) {
const auto& time_win_event_data_filename_list = conform_particle_data_filename_list.value(time_win);
for (const auto& coincidence_order : time_win_event_data_filename_list.keys()) {
const QString& conform_particle_data_filename = time_win_event_data_filename_list.value(coincidence_order);
conformParticleDataEnergyScale(time_win, coincidence_order, conform_particle_data_filename);
}
}
const QString& info = QStringLiteral(u"符合能谱数据处理完成.");
LOG_INFO(info);
return true;
}