You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp

5638 lines
202 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmCalculationAutoFitPSO.h"
#include "nmCalculationDllPebiSolverTask.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataWellBase.h"
#include "nmDataVerticalWell.h"
#include "nmDataVerticalFracturedWell.h"
#include "nmDataHorizontalFracturedWell.h"
#include "nmDataReservoir.h"
#include "nmDataAutomaticFitting.h"
#include "nmCalculationPebiGrid.h"
#include <QApplication>
#include <QDebug>
#include <QTime>
#include <QDir>
#include <QTextStream>
#include <QFileInfo>
#include <QProcess>
#include <QProcessEnvironment>
#include <QPair>
#include <QtCore/qmath.h>
#include <cmath>
#include <algorithm>
#include <limits>
#include <QTimer>
#ifdef Q_OS_WIN
#include <windows.h>
#include <float.h>
#define DEBUG_OUT(msg) OutputDebugStringA(QString("[AutoFit] %1\n").arg(msg).toLocal8Bit().data())
#endif
// 常量定义
const double nmCalculationAutoFitPSO::MIN_FITNESS_IMPROVEMENT = 1e-8;
const double nmCalculationAutoFitPSO::VELOCITY_LIMIT_FACTOR = 0.2;
const int nmCalculationAutoFitPSO::CONVERGENCE_CHECK_INTERVAL = 5;
static const double kSurrogateKeepFraction = 0.50;
static const double kSurrogateHighRiskKeepFraction = 0.70;
static const double kSurrogateAuditFraction = 0.05;
static const double kSurrogateMinSolverFraction = 0.55;
static const double kSurrogateHighRiskMinSolverFraction = 0.80;
static const int kSurrogateWarmupIterations = 2;
static const int kSurrogateFullSolverInterval = 10;
static const int kSurrogateScoringMaxAttempts = 2;
static const bool kUseFixedPsoSeed = true;
static const unsigned int kFixedPsoSeed = 1792008679u;
static const double kSurrogateKMin = 1.0e-4;
static const double kSurrogateKMax = 100.0;
static const double kSurrogateSkinMin = -10.0;
static const double kSurrogateSkinMax = 10.0;
static const double kSurrogateWellboreCMin = 1.0e-4;
static const double kSurrogateWellboreCMax = 2.0;
static const double kSurrogatePhiMin = 1.0e-2;
static const double kSurrogatePhiMax = 0.50;
static const double kSurrogateHMin = 2.0;
static const double kSurrogateHMax = 100.0;
static const double kSurrogateCfFixed = 4.315e-4;
static const double kSurrogateCfRelTol = 0.25;
static const int kSurrogateMinProdSections = 3;
static const int kSurrogateMaxProdSections = 5;
static const double kSurrogateProdTimeMin = 24.0;
static const double kSurrogateProdTimeMax = 220.0;
static const double kSurrogateShutinTimeMin = 12.0;
static const double kSurrogateShutinTimeMax = 140.0;
static const double kSurrogateQMin = 50.0;
static const double kSurrogateQMax = 500.0;
static const double kSurrogateQZeroThreshold = 1.0e-6;
static const double kSurrogateMaxRateStepRatio = 2.2;
static const double kSurrogateStrongDeclineEndStartRatio = 0.70;
static const double kSurrogateRateTrendRelTol = 1.0e-9;
// Check infinity and NaN.
static inline bool isFiniteNumber(double value)
{
#ifdef Q_OS_WIN
return _finite(value) != 0;
#else
return std::isfinite(value);
#endif
}
static inline bool isInClosedRange(double value, double lower, double upper)
{
return isFiniteNumber(value) && value >= lower && value <= upper;
}
static inline bool isNearRelative(double value, double reference, double relTol)
{
if(!isFiniteNumber(value) || !isFiniteNumber(reference)) {
return false;
}
return qAbs(value - reference) <= qMax(1e-12, qAbs(reference) * relTol);
}
// sleep函数
static inline void msleep(int ms)
{
#ifdef Q_OS_WIN
Sleep(ms);
#endif
}
static QString csvEscape(const QString& text)
{
QString escaped = text;
escaped.replace("\"", "\"\"");
return QString("\"%1\"").arg(escaped);
}
static QString traceNumber(double value)
{
if(!isFiniteNumber(value)) {
return QString();
}
return QString::number(value, 'g', 17);
}
static QString traceParamAt(const QVector<double>& params, int index)
{
if(index < 0 || index >= params.size()) {
return QString();
}
return traceNumber(params[index]);
}
static QString jsonEscape(const QString& text)
{
QString escaped = text;
escaped.replace("\\", "\\\\");
escaped.replace("\"", "\\\"");
escaped.replace("\b", "\\b");
escaped.replace("\f", "\\f");
escaped.replace("\n", "\\n");
escaped.replace("\r", "\\r");
escaped.replace("\t", "\\t");
return QString("\"%1\"").arg(escaped);
}
static QString jsonNumber(double value)
{
if(!isFiniteNumber(value)) {
return "null";
}
return QString::number(value, 'g', 17);
}
static QString jsonDoubleArray(const QVector<double>& values)
{
QStringList items;
for(int i = 0; i < values.size(); ++i) {
items << jsonNumber(values[i]);
}
return QString("[%1]").arg(items.join(","));
}
static QString jsonIntArray(const QVector<int>& values)
{
QStringList items;
for(int i = 0; i < values.size(); ++i) {
items << QString::number(values[i]);
}
return QString("[%1]").arg(items.join(","));
}
static QString jsonBoolArray(const QVector<bool>& values)
{
QStringList items;
for(int i = 0; i < values.size(); ++i) {
items << (values[i] ? "true" : "false");
}
return QString("[%1]").arg(items.join(","));
}
static double deterministicAuditRandom01(unsigned int seed, int generation, int particleIndex)
{
unsigned int x = seed;
x ^= 0x9e3779b9u + static_cast<unsigned int>(generation + 1) + (x << 6) + (x >> 2);
x ^= 0x85ebca6bu + static_cast<unsigned int>(particleIndex + 1) + (x << 6) + (x >> 2);
x ^= x >> 16;
x *= 0x7feb352du;
x ^= x >> 15;
x *= 0x846ca68bu;
x ^= x >> 16;
return static_cast<double>(x) / 4294967295.0;
}
static QString jsonStringArray(const QStringList& values)
{
QStringList items;
for(int i = 0; i < values.size(); ++i) {
items << jsonEscape(values[i]);
}
return QString("[%1]").arg(items.join(","));
}
static QString jsonPointArray(const QVector<QPointF>& points)
{
QStringList items;
for(int i = 0; i < points.size(); ++i) {
items << QString("{\"x\":%1,\"y\":%2}")
.arg(jsonNumber(points[i].x()))
.arg(jsonNumber(points[i].y()));
}
return QString("[%1]").arg(items.join(","));
}
static bool isValidMlRootDirectory(const QString& mlRootPath)
{
if(mlRootPath.isEmpty()) {
return false;
}
QFileInfo scriptInfo(QDir(mlRootPath).absoluteFilePath("scripts/score_pso_candidates.py"));
return scriptInfo.exists() && scriptInfo.isFile();
}
static QString findExecutableInPath(const QString& executableName)
{
if(executableName.isEmpty()) {
return QString();
}
QString pathValue = QProcessEnvironment::systemEnvironment().value("PATH");
if(pathValue.isEmpty()) {
pathValue = QString::fromLocal8Bit(qgetenv("PATH"));
}
if(pathValue.isEmpty()) {
return QString();
}
QStringList executableCandidates;
executableCandidates << executableName;
#ifdef Q_OS_WIN
if(!executableName.contains('.')) {
executableCandidates << (executableName + ".exe")
<< (executableName + ".bat")
<< (executableName + ".cmd");
}
#endif
#ifdef Q_OS_WIN
QChar pathSeparator(';');
#else
QChar pathSeparator(':');
#endif
QStringList pathEntries = pathValue.split(pathSeparator, QString::SkipEmptyParts);
for(int i = 0; i < pathEntries.size(); ++i) {
QDir dir(pathEntries[i].trimmed());
if(!dir.exists()) {
continue;
}
for(int j = 0; j < executableCandidates.size(); ++j) {
QFileInfo executableInfo(dir.absoluteFilePath(executableCandidates[j]));
if(executableInfo.exists() && executableInfo.isFile()) {
return QDir::cleanPath(executableInfo.absoluteFilePath());
}
}
}
return QString();
}
static QStringList traceParameterNames()
{
QStringList names;
names << "k"
<< "skin"
<< "wellboreC"
<< "phi"
<< "initialPressure"
<< "h"
<< "Ct"
<< "Cf"
<< "Soi"
<< "Swi"
<< "Sgi";
return names;
}
// Constructor.
nmCalculationAutoFitPSO::nmCalculationAutoFitPSO(QObject* parent)
: QObject(parent)
, m_isRunning(false)
, m_shouldStop(false)
, m_isPaused(false)
, m_currentIteration(0)
, m_globalBestFitness(1e10)
, m_previousBestFitness(1e10)
, m_swarmSize(20)
, m_maxIterations(100)
, m_targetError(0.001)
, m_inertiaWeight(0.4)
, m_cognitiveParam(2.0)
, m_socialParam(1.2)
, m_totalEvaluations(0)
, m_successfulEvaluations(0)
, m_evaluationInProgress(0)
, m_consecutiveFailures(0)
, m_improvementThreshold(0.05)
, m_userInitialFitness(1e10)
, m_consecutiveFailedIterations(0)
, m_maxConsecutiveFailures(3)
, m_hasValidUserSolution(false)
, m_diversityThreshold(0.05)
, m_convergenceVarianceThreshold(1e-8)
, m_velocityConvergenceThreshold(0.01)
, m_trueConvergenceWindow(15)
, m_localOptimumWindow(8)
, m_nearTargetFactor(2.0)
, m_farTargetFactor(10.0)
, m_targetWellName("")
, m_traceEnabled(true)
, m_traceRunId("")
, m_traceFilePath("")
, m_traceMetaFilePath("")
, m_surrogateScreeningEnabled(false)
, m_psoRandomSeed(0)
, m_surrogateRunContextSummary("")
, m_surrogateWarmupIterationCount(0)
, m_surrogatePeriodicAuditIterationCount(0)
, m_surrogateContextBlockedIterationCount(0)
, m_surrogateActiveIterationCount(0)
, m_surrogateSelectedParticleCount(0)
, m_surrogateScreenedParticleCount(0)
, m_surrogateTopKParticleCount(0)
, m_surrogateAuditParticleCount(0)
, m_surrogateFallbackParticleCount(0)
, m_surrogateDomainParticleCount(0)
, m_surrogateMinFloorParticleCount(0)
, m_surrogateScoringProcess(nullptr)
, m_surrogateScoringServerKey("")
, m_simulationMode(false)
, m_simulationTimer(nullptr)
, m_simulationIteration(0)
, m_simulationMaxIterations(100)
, m_simulationTargetError(0.001)
, m_simulationStartError(0.5)
, m_simulationCurrentError(0.5)
, m_simulationLogInterval(2)
, m_simulationCurrentParticle(0)
, m_simulationSuccessCount(0)
, m_simulationFailCount(0)
, m_simulationIterationStarted(false)
, m_simulationPreviousIterError(0.5)
{
DEBUG_OUT(QString("PSO Constructor: this=0x%1").arg((quintptr)this, 0, 16));
initializeTemporaryDirectory();
DEBUG_OUT("AutoFit calculator initialized (data-driven mode)");
DEBUG_OUT("PSO Constructor completed");
}
// Destructor.
nmCalculationAutoFitPSO::~nmCalculationAutoFitPSO()
{
DEBUG_OUT(QString("PSO Destructor: this=0x%1").arg((quintptr)this, 0, 16));
if(m_simulationTimer) {
m_simulationTimer->stop();
delete m_simulationTimer;
m_simulationTimer = nullptr;
}
// Stop algorithm.
if(m_isRunning) {
m_shouldStop = true;
int waitCount = 0;
while(m_isRunning && waitCount < 100) {
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 50);
msleep(50);
waitCount++;
}
if(m_isRunning) {
DEBUG_OUT("Force stopping PSO - timeout reached");
m_isRunning = false;
}
}
// Clean temp directory.
stopSurrogateScoringServer();
closeTraceFile();
cleanupTemporaryDirectory();
// Disconnect all signals to avoid callbacks after destruction.
disconnect(this, nullptr, nullptr, nullptr);
DEBUG_OUT("PSO Destructor completed");
}
// Directory helpers.
void nmCalculationAutoFitPSO::initializeTemporaryDirectory()
{
cleanupOldTemporaryDirectories();
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz");
QString processId = QString::number(QCoreApplication::applicationPid());
m_tempDirectory = QApplication::applicationDirPath() +
"/autofit_temp_" + processId + "_" + timestamp;
int counter = 0;
QString originalPath = m_tempDirectory;
while(QDir(m_tempDirectory).exists() && counter < 100) {
m_tempDirectory = originalPath + "_" + QString::number(counter);
counter++;
}
if(QDir().mkpath(m_tempDirectory)) {
DEBUG_OUT(QString("Initialized temp directory: %1").arg(m_tempDirectory));
} else {
DEBUG_OUT(QString("Warning: Failed to create temp directory: %1").arg(m_tempDirectory));
m_tempDirectory = QApplication::applicationDirPath();
}
}
void nmCalculationAutoFitPSO::cleanupTemporaryDirectory()
{
if(QDir(m_tempDirectory).exists()) {
if(removeDirectoryRecursively(m_tempDirectory)) {
DEBUG_OUT("Temp directory cleaned up successfully");
} else {
DEBUG_OUT("Warning: Failed to clean up temp directory completely");
}
}
}
bool nmCalculationAutoFitPSO::removeDirectoryRecursively(const QString& path)
{
QDir dir(path);
if(!dir.exists()) {
return true;
}
// 递归删除子目录和文件
QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
bool allRemoved = true;
for(int i = 0; i < entries.size(); ++i) {
const QFileInfo& entry = entries[i];
if(entry.isDir()) {
if(!removeDirectoryRecursively(entry.absoluteFilePath())) {
allRemoved = false;
}
} else {
QFile file(entry.absoluteFilePath());
// 处理只读文件
if(!file.permissions().testFlag(QFile::WriteUser)) {
file.setPermissions(file.permissions() | QFile::WriteUser);
}
if(!file.remove()) {
DEBUG_OUT(QString("Failed to remove file: %1").arg(entry.absoluteFilePath()));
allRemoved = false;
}
}
}
// 删除目录本身
if(allRemoved) {
return dir.rmdir(path);
}
return false;
}
void nmCalculationAutoFitPSO::cleanupOldTemporaryDirectories()
{
QString appDir = QApplication::applicationDirPath();
QDir dir(appDir);
// 获取当前进程ID避免删除当前进程可能使用的目录
QString currentProcessId = QString::number(QCoreApplication::applicationPid());
// 查找所有以 "autofit_temp_" 开头的目录
QStringList filters;
filters << "autofit_temp_*";
QFileInfoList tempDirs = dir.entryInfoList(filters, QDir::Dirs | QDir::NoDotAndDotDot);
if(tempDirs.isEmpty()) {
DEBUG_OUT("No old temporary directories found");
return;
}
DEBUG_OUT(QString("Found %1 potential old temporary directories to clean up").arg(tempDirs.size()));
int cleanedCount = 0;
int failedCount = 0;
int skippedCount = 0;
for(int i = 0; i < tempDirs.size(); ++i) {
const QFileInfo& dirInfo = tempDirs[i];
QString dirPath = dirInfo.absoluteFilePath();
QString dirName = dirInfo.fileName();
// 检查是否是当前进程的目录(虽然理论上不应该存在,但为了安全起见)
if(dirName.contains("_" + currentProcessId + "_")) {
DEBUG_OUT(QString("Skipping current process directory: %1").arg(dirName));
skippedCount++;
continue;
}
// 尝试删除目录
DEBUG_OUT(QString("Attempting to remove old temp directory: %1").arg(dirName));
if(removeDirectoryRecursively(dirPath)) {
DEBUG_OUT(QString("Successfully cleaned up: %1").arg(dirName));
cleanedCount++;
} else {
DEBUG_OUT(QString("Failed to clean up: %1 (may be in use by another process)").arg(dirName));
failedCount++;
}
}
// 输出清理统计信息
DEBUG_OUT(QString("Old temp directories cleanup summary: %1 removed, %2 failed, %3 skipped")
.arg(cleanedCount).arg(failedCount).arg(skippedCount));
}
// ==================== 公共接口方法 ====================
void nmCalculationAutoFitPSO::setTargetLogLogData(const QVector<QVector<double> >& targetData)
{
m_targetLogLogData = targetData;
DEBUG_OUT(QString("Target LogLog data set: %1 arrays").arg(targetData.size()));
if(targetData.size() >= 3) {
DEBUG_OUT(QString("LogLog data points: X=%1, Y1=%2, Y2=%3")
.arg(targetData[0].size())
.arg(targetData[1].size())
.arg(targetData[2].size()));
}
}
void nmCalculationAutoFitPSO::stopFitting()
{
if(m_simulationMode && m_simulationTimer) {
m_simulationTimer->stop();
}
if(m_isRunning) {
DEBUG_OUT("Stop request received, setting stop flag...");
// 添加停止日志
emit logMessageGenerated(tr("=== User Stop Request Received ==="));
emit logMessageGenerated(tr("Gracefully stopping PSO optimization..."));
m_shouldStop = true;
// 等待当前评估完成,缩短超时时间
int waitCount = 0;
while(m_evaluationInProgress > 0 && waitCount < 30) { // 减少等待时间
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 50);
msleep(50);
waitCount++;
}
// 超时时强制重置
if(m_evaluationInProgress > 0) {
DEBUG_OUT("Force resetting evaluation counter");
emit logMessageGenerated(tr("Force stopping current evaluation..."));
m_evaluationInProgress = 0;
}
// 确保运行标志被清除
m_isRunning = false;
emit logMessageGenerated(tr("PSO optimization stop request processed"));
DEBUG_OUT("Stop request processed");
} else {
DEBUG_OUT("Stop request received but PSO is not running");
emit logMessageGenerated(tr("Stop request received but optimization is not running"));
}
closeTraceFile();
cleanupTemporaryDirectory();
}
bool nmCalculationAutoFitPSO::isRunning() const
{
return m_isRunning;
}
int nmCalculationAutoFitPSO::getCurrentIteration() const
{
return m_currentIteration;
}
QVector<double> nmCalculationAutoFitPSO::getBestSolution() const
{
return m_globalBestPosition;
}
double nmCalculationAutoFitPSO::getBestFitness() const
{
return m_globalBestFitness;
}
QString nmCalculationAutoFitPSO::getLastError() const
{
return m_lastError;
}
void nmCalculationAutoFitPSO::resetOptimizer()
{
closeTraceFile();
m_swarm.clear();
m_globalBestPosition.clear();
m_globalBestFitness = 1e10;
m_previousBestFitness = 1e10;
m_lastEvaluatedLogLogData.clear();
m_globalBestLogLogData.clear();
m_userInitialLogLogData.clear();
m_currentIteration = 0;
m_totalEvaluations = 0;
m_successfulEvaluations = 0;
m_convergenceHistory.clear();
m_lastError.clear();
m_initialValues.clear();
m_userInitialSolution.clear();
m_userInitialFitness = 1e10;
m_hasValidUserSolution = false;
m_diversityHistory.clear();
m_velocityHistory.clear();
m_particleStagnationHistory.clear();
m_traceMetaFilePath.clear();
resetRunSummary();
DEBUG_OUT("Optimizer reset");
}
void nmCalculationAutoFitPSO::setPSOTargetWellName(const QString& wellName)
{
m_targetWellName = wellName;
}
void nmCalculationAutoFitPSO::initializeTraceFile()
{
if(!m_traceEnabled) {
return;
}
closeTraceFile();
captureSurrogateRunContextSummary();
m_traceRunId = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss_zzz");
QDir traceDir(QDir(getMlRootPath()).absoluteFilePath("data/temp"));
if(!traceDir.exists() && !QDir().mkpath(traceDir.absolutePath())) {
DEBUG_OUT(QString("Failed to create PSO trace directory: %1").arg(traceDir.absolutePath()));
m_traceFilePath.clear();
return;
}
m_traceFilePath = traceDir.absoluteFilePath(QString("pso_baseline_trace_%1.csv").arg(m_traceRunId));
m_traceMetaFilePath = traceDir.absoluteFilePath(QString("pso_baseline_trace_%1.meta.json").arg(m_traceRunId));
m_traceFile.setFileName(m_traceFilePath);
if(!m_traceFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
DEBUG_OUT(QString("Failed to open PSO trace file: %1").arg(m_traceFilePath));
m_traceFilePath.clear();
m_traceMetaFilePath.clear();
return;
}
writeTraceHeader();
writeTraceMetaFile();
DEBUG_OUT(QString("PSO baseline trace initialized: %1").arg(m_traceFilePath));
emit logMessageGenerated(tr("PSO baseline trace: %1").arg(m_traceFilePath));
if(!m_traceMetaFilePath.isEmpty()) {
emit logMessageGenerated(tr("PSO baseline trace meta: %1").arg(m_traceMetaFilePath));
}
emit logMessageGenerated(tr("PSO surrogate screening: %1, model=%2, keep=%3, audit=%4, warmup=%5, min_solver=%6")
.arg(isSurrogateScreeningEnabled() ? tr("enabled") : tr("disabled"))
.arg(getSurrogateTag())
.arg(getSurrogateKeepFraction(), 0, 'f', 2)
.arg(getSurrogateAuditFraction(), 0, 'f', 2)
.arg(getSurrogateWarmupIterations())
.arg(getSurrogateMinSolverFraction(), 0, 'f', 2));
if(!isSurrogateScreeningEnabled()) {
emit logMessageGenerated(tr("Surrogate screening is turned off in the automatic fitting configuration"));
} else {
if(m_surrogateRunContextSummary == "passed") {
emit logMessageGenerated(tr("Surrogate run context check: passed"));
} else {
emit logMessageGenerated(tr("Surrogate run context check: %1").arg(m_surrogateRunContextSummary));
}
}
}
void nmCalculationAutoFitPSO::resetRunSummary()
{
m_surrogateRunContextSummary.clear();
m_surrogateWarmupIterationCount = 0;
m_surrogatePeriodicAuditIterationCount = 0;
m_surrogateContextBlockedIterationCount = 0;
m_surrogateActiveIterationCount = 0;
m_surrogateSelectedParticleCount = 0;
m_surrogateScreenedParticleCount = 0;
m_surrogateTopKParticleCount = 0;
m_surrogateAuditParticleCount = 0;
m_surrogateFallbackParticleCount = 0;
m_surrogateDomainParticleCount = 0;
m_surrogateMinFloorParticleCount = 0;
}
void nmCalculationAutoFitPSO::captureSurrogateRunContextSummary()
{
if(!isSurrogateScreeningEnabled()) {
m_surrogateRunContextSummary = "disabled_by_config";
return;
}
QString contextReason;
if(isSurrogateRunContextSupported(&contextReason)) {
m_surrogateRunContextSummary = "passed";
} else {
m_surrogateRunContextSummary = QString("failed - %1").arg(contextReason);
}
}
void nmCalculationAutoFitPSO::emitRunSummary(bool success, StopReasonPSO finalReason)
{
emit logMessageGenerated(tr("=== PSO Run Summary ==="));
emit logMessageGenerated(tr("Stop reason: %1").arg(getStopReasonDescription(finalReason)));
emit logMessageGenerated(tr("Result: %1, final error=%2, iterations=%3, evaluations=%4 (successful=%5, failed=%6)")
.arg(success ? "SUCCESS" : "FAILED")
.arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1)
.arg(m_totalEvaluations)
.arg(m_successfulEvaluations)
.arg(m_totalEvaluations - m_successfulEvaluations));
emit logMessageGenerated(tr("Surrogate config: %1, context=%2")
.arg(isSurrogateScreeningEnabled() ? "enabled" : "disabled")
.arg(m_surrogateRunContextSummary.isEmpty() ? QString("unknown") : m_surrogateRunContextSummary));
emit logMessageGenerated(tr("Surrogate iterations: active=%1, warmup=%2, periodic_audit=%3, context_blocked=%4")
.arg(m_surrogateActiveIterationCount)
.arg(m_surrogateWarmupIterationCount)
.arg(m_surrogatePeriodicAuditIterationCount)
.arg(m_surrogateContextBlockedIterationCount));
emit logMessageGenerated(tr("Surrogate particles: selected=%1, screened_out=%2, topk=%3, random_audit=%4, fallback=%5, domain_gate=%6, min_solver_floor=%7")
.arg(m_surrogateSelectedParticleCount)
.arg(m_surrogateScreenedParticleCount)
.arg(m_surrogateTopKParticleCount)
.arg(m_surrogateAuditParticleCount)
.arg(m_surrogateFallbackParticleCount)
.arg(m_surrogateDomainParticleCount)
.arg(m_surrogateMinFloorParticleCount));
if(!m_traceFilePath.isEmpty()) {
emit logMessageGenerated(tr("Artifacts: trace=%1").arg(m_traceFilePath));
}
if(!m_traceMetaFilePath.isEmpty()) {
emit logMessageGenerated(tr("Artifacts: trace_meta=%1").arg(m_traceMetaFilePath));
}
}
void nmCalculationAutoFitPSO::closeTraceFile()
{
stopSurrogateScoringServer();
if(m_traceFile.isOpen()) {
m_traceFile.flush();
m_traceFile.close();
}
}
void nmCalculationAutoFitPSO::writeTraceHeader()
{
if(!m_traceFile.isOpen()) {
return;
}
QStringList cols;
cols << "run_id"
<< "generation"
<< "particle_id"
<< "phase"
<< "k"
<< "skin"
<< "wellboreC"
<< "phi"
<< "h"
<< "Cf"
<< "solver_objective"
<< "solver_success"
<< "elapsed_ms"
<< "surrogate_objective"
<< "screening_decision"
<< "pbest_objective"
<< "pbest_k"
<< "pbest_skin"
<< "pbest_wellboreC"
<< "pbest_phi"
<< "pbest_h"
<< "pbest_Cf"
<< "gbest_objective"
<< "gbest_k"
<< "gbest_skin"
<< "gbest_wellboreC"
<< "gbest_phi"
<< "gbest_h"
<< "gbest_Cf"
<< "enabled_param_indices";
QTextStream out(&m_traceFile);
out << cols.join(",") << "\n";
}
void nmCalculationAutoFitPSO::writeTraceMetaFile()
{
if(m_traceMetaFilePath.isEmpty()) {
return;
}
QFile metaFile(m_traceMetaFilePath);
if(!metaFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
DEBUG_OUT(QString("Failed to open PSO trace meta file: %1").arg(m_traceMetaFilePath));
m_traceMetaFilePath.clear();
return;
}
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
nmDataWellBase* pTargetWell = dataManager ? dataManager->findWellByName(m_targetWellName) : nullptr;
QStringList parameterNames = traceParameterNames();
QStringList enabledNames;
for(int i = 0; i < m_enabledParamIndices.size(); ++i) {
int paramIndex = m_enabledParamIndices[i];
enabledNames << ((paramIndex >= 0 && paramIndex < parameterNames.size()) ? parameterNames[paramIndex] : QString::number(paramIndex));
}
QVector<double> initialFullParams = buildTraceParameterVector(m_initialValues);
QVector<double> targetTime = m_targetLogLogData.size() > 0 ? m_targetLogLogData[0] : QVector<double>();
QVector<double> targetPressure = m_targetLogLogData.size() > 1 ? m_targetLogLogData[1] : QVector<double>();
QVector<double> targetDerivative = m_targetLogLogData.size() > 2 ? m_targetLogLogData[2] : QVector<double>();
QVector<QPointF> flowPoints = pTargetWell ? pTargetWell->getFlowPoints() : QVector<QPointF>();
QTextStream out(&metaFile);
out << "{\n";
out << " \"schema_version\": 1,\n";
out << " \"trace_type\": \"pso_baseline_replay_meta\",\n";
out << " \"run_id\": " << jsonEscape(m_traceRunId) << ",\n";
out << " \"created_at\": " << jsonEscape(QDateTime::currentDateTime().toString(Qt::ISODate)) << ",\n";
out << " \"trace_csv\": " << jsonEscape(QFileInfo(m_traceFilePath).fileName()) << ",\n";
out << " \"target\": {\n";
out << " \"well_name\": " << jsonEscape(m_targetWellName) << ",\n";
out << " \"well_type\": " << (pTargetWell ? QString::number(static_cast<int>(pTargetWell->getWellType())) : "null") << ",\n";
out << " \"section_policy\": \"current_target_well_indexF\",\n";
out << " \"section_index\": " << (pTargetWell ? QString::number(pTargetWell->getIndexF()) : "null") << ",\n";
out << " \"target_loglog\": {\n";
out << " \"time\": " << jsonDoubleArray(targetTime) << ",\n";
out << " \"pressure\": " << jsonDoubleArray(targetPressure) << ",\n";
out << " \"derivative\": " << jsonDoubleArray(targetDerivative) << "\n";
out << " },\n";
out << " \"flow_points\": " << jsonPointArray(flowPoints) << "\n";
out << " },\n";
out << " \"pso\": {\n";
out << " \"swarm_size\": " << m_swarmSize << ",\n";
out << " \"max_iterations\": " << m_maxIterations << ",\n";
out << " \"target_error\": " << jsonNumber(m_targetError) << ",\n";
out << " \"inertia_weight\": " << jsonNumber(m_inertiaWeight) << ",\n";
out << " \"cognitive_param\": " << jsonNumber(m_cognitiveParam) << ",\n";
out << " \"social_param\": " << jsonNumber(m_socialParam) << ",\n";
out << " \"random_seed\": " << m_psoRandomSeed << "\n";
out << " },\n";
out << " \"surrogate_screening\": {\n";
out << " \"enabled\": " << (isSurrogateScreeningEnabled() ? "true" : "false") << ",\n";
out << " \"model_tag\": " << jsonEscape(getSurrogateTag()) << ",\n";
out << " \"keep_fraction\": " << jsonNumber(getSurrogateKeepFraction()) << ",\n";
out << " \"audit_fraction\": " << jsonNumber(getSurrogateAuditFraction()) << ",\n";
out << " \"min_solver_fraction\": " << jsonNumber(getSurrogateMinSolverFraction()) << ",\n";
out << " \"warmup_iterations\": " << getSurrogateWarmupIterations() << ",\n";
out << " \"full_solver_interval\": " << getSurrogateFullSolverInterval() << ",\n";
out << " \"audit_random_source\": \"deterministic_hash\"\n";
out << " },\n";
out << " \"parameters\": {\n";
out << " \"names\": " << jsonStringArray(parameterNames) << ",\n";
out << " \"enabled_indices\": " << jsonIntArray(m_enabledParamIndices) << ",\n";
out << " \"enabled_names\": " << jsonStringArray(enabledNames) << ",\n";
out << " \"selected_flags\": " << jsonBoolArray(m_parameterSelected) << ",\n";
out << " \"lower\": " << jsonDoubleArray(m_parameterLower) << ",\n";
out << " \"upper\": " << jsonDoubleArray(m_parameterUpper) << ",\n";
out << " \"initial_selected\": " << jsonDoubleArray(m_initialValues) << ",\n";
out << " \"initial_full\": " << jsonDoubleArray(initialFullParams) << "\n";
out << " },\n";
out << " \"replay_notes\": [\n";
out << " \"CSV rows contain true solver objectives for evaluated particles.\",\n";
out << " \"This meta file contains the target curve and run context needed for surrogate replay.\",\n";
out << " \"pbest and gbest must be updated only from true solver objectives.\"\n";
out << " ]\n";
out << "}\n";
metaFile.flush();
metaFile.close();
}
QVector<double> nmCalculationAutoFitPSO::buildTraceParameterVector(const QVector<double>& selectedParameters) const
{
QVector<double> fullParams(11, 0.0);
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
if(dataManager) {
nmDataReservoir reservoirData = dataManager->getReservoirDataCopy();
fullParams[0] = reservoirData.getPermeability().getValue().toDouble();
fullParams[3] = reservoirData.getPorosity().getValue().toDouble();
fullParams[4] = reservoirData.getInitialPressure().getValue().toDouble();
fullParams[5] = reservoirData.getThickness().getValue().toDouble();
fullParams[6] = reservoirData.getCt().getValue().toDouble();
fullParams[7] = reservoirData.getCf().getValue().toDouble();
fullParams[8] = reservoirData.getSoi().getValue().toDouble();
fullParams[9] = reservoirData.getSwi().getValue().toDouble();
fullParams[10] = reservoirData.getSgi().getValue().toDouble();
nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
if(pTargetWell) {
fullParams[1] = pTargetWell->getPerforation(0)->getSkin().getValue().toDouble();
fullParams[2] = pTargetWell->getWellboreStorage().getValue().toDouble();
}
}
for(int i = 0; i < selectedParameters.size() && i < m_enabledParamIndices.size(); ++i) {
int paramIndex = m_enabledParamIndices[i];
if(paramIndex >= 0 && paramIndex < fullParams.size()) {
fullParams[paramIndex] = selectedParameters[i];
}
}
return fullParams;
}
void nmCalculationAutoFitPSO::writeTraceRow(int generation,
int particleIndex,
const QString& phase,
const QVector<double>& parameters,
double solverObjective,
bool solverSuccess,
int elapsedMs,
double surrogateObjective,
const QString& screeningDecision,
const QVector<double>& pbestPosition,
double pbestObjective)
{
if(!m_traceFile.isOpen()) {
return;
}
QVector<double> currentParams = buildTraceParameterVector(parameters);
QVector<double> pbestParams = pbestPosition.isEmpty() ? QVector<double>() : buildTraceParameterVector(pbestPosition);
QVector<double> gbestParams = m_globalBestPosition.isEmpty() ? QVector<double>() : buildTraceParameterVector(m_globalBestPosition);
QStringList enabledIndices;
for(int i = 0; i < m_enabledParamIndices.size(); ++i) {
enabledIndices << QString::number(m_enabledParamIndices[i]);
}
QStringList cols;
cols << csvEscape(m_traceRunId)
<< QString::number(generation)
<< QString::number(particleIndex)
<< csvEscape(phase)
<< traceParamAt(currentParams, 0)
<< traceParamAt(currentParams, 1)
<< traceParamAt(currentParams, 2)
<< traceParamAt(currentParams, 3)
<< traceParamAt(currentParams, 5)
<< traceParamAt(currentParams, 7)
<< traceNumber(solverObjective)
<< QString::number(solverSuccess ? 1 : 0)
<< QString::number(elapsedMs)
<< traceNumber(surrogateObjective)
<< csvEscape(screeningDecision)
<< traceNumber(pbestObjective)
<< traceParamAt(pbestParams, 0)
<< traceParamAt(pbestParams, 1)
<< traceParamAt(pbestParams, 2)
<< traceParamAt(pbestParams, 3)
<< traceParamAt(pbestParams, 5)
<< traceParamAt(pbestParams, 7)
<< traceNumber(m_globalBestFitness)
<< traceParamAt(gbestParams, 0)
<< traceParamAt(gbestParams, 1)
<< traceParamAt(gbestParams, 2)
<< traceParamAt(gbestParams, 3)
<< traceParamAt(gbestParams, 5)
<< traceParamAt(gbestParams, 7)
<< csvEscape(enabledIndices.join(";"));
QTextStream out(&m_traceFile);
out << cols.join(",") << "\n";
m_traceFile.flush();
}
void nmCalculationAutoFitPSO::writeIterationTraceRows()
{
if(!m_traceFile.isOpen()) {
return;
}
for(int i = 0; i < m_swarm.size(); ++i) {
const AutoFitParticle& particle = m_swarm[i];
double solverObjective = particle.evaluatedThisIteration
? particle.fitness
: std::numeric_limits<double>::quiet_NaN();
writeTraceRow(m_currentIteration,
i,
particle.evaluatedThisIteration ? "particle_solver" : "particle_not_evaluated",
particle.position,
solverObjective,
particle.lastEvaluationSuccess,
particle.lastEvaluationElapsedMs,
particle.surrogateObjective,
particle.screeningDecision,
particle.bestPosition,
particle.bestFitness);
}
}
bool nmCalculationAutoFitPSO::isSurrogateScreeningEnabled() const
{
return m_surrogateScreeningEnabled;
}
QString nmCalculationAutoFitPSO::getMlRootPath() const
{
QString appDir = QApplication::applicationDirPath();
QString cwd = QDir::currentPath();
QStringList candidateRoots;
candidateRoots << QDir::cleanPath(appDir + "/../../ML/nmWTAI-ML")
<< QDir::cleanPath(appDir + "/../ML/nmWTAI-ML")
<< QDir::cleanPath(cwd + "/ML/nmWTAI-ML")
<< QDir::cleanPath(cwd + "/../ML/nmWTAI-ML")
<< QDir::cleanPath(cwd + "/../../ML/nmWTAI-ML");
for(int i = 0; i < candidateRoots.size(); ++i) {
if(isValidMlRootDirectory(candidateRoots[i])) {
return candidateRoots[i];
}
}
return candidateRoots.isEmpty()
? QString()
: candidateRoots.first();
}
QString nmCalculationAutoFitPSO::getPythonExecutablePath() const
{
QString mlRoot = getMlRootPath();
QStringList candidateExecutables;
if(!mlRoot.isEmpty()) {
candidateExecutables << QDir(mlRoot).absoluteFilePath(".venv/Scripts/python.exe");
candidateExecutables << QDir(mlRoot).absoluteFilePath(".venv/bin/python");
}
for(int i = 0; i < candidateExecutables.size(); ++i) {
QFileInfo pythonInfo(candidateExecutables[i]);
if(pythonInfo.exists() && pythonInfo.isFile()) {
return QDir::cleanPath(pythonInfo.absoluteFilePath());
}
}
QString resolvedPython = findExecutableInPath("python");
if(!resolvedPython.isEmpty()) {
return QDir::cleanPath(resolvedPython);
}
return "python";
}
QString nmCalculationAutoFitPSO::getSurrogateTag() const
{
return "family_random_v2_q_50k_logparam";
}
QString nmCalculationAutoFitPSO::getSurrogateStage() const
{
return "family_random_v2_q";
}
double nmCalculationAutoFitPSO::getSurrogateKeepFraction() const
{
if(getSurrogateStage().contains("v2_q") && isStrongDecliningProductionSchedule()) {
return kSurrogateHighRiskKeepFraction;
}
return kSurrogateKeepFraction;
}
double nmCalculationAutoFitPSO::getSurrogateAuditFraction() const
{
return kSurrogateAuditFraction;
}
double nmCalculationAutoFitPSO::getSurrogateMinSolverFraction() const
{
if(getSurrogateStage().contains("v2_q") && isStrongDecliningProductionSchedule()) {
return kSurrogateHighRiskMinSolverFraction;
}
return kSurrogateMinSolverFraction;
}
int nmCalculationAutoFitPSO::getSurrogateWarmupIterations() const
{
return kSurrogateWarmupIterations;
}
int nmCalculationAutoFitPSO::getSurrogateFullSolverInterval() const
{
return kSurrogateFullSolverInterval;
}
bool nmCalculationAutoFitPSO::writeSurrogateCandidateCsv(const QString& candidatePath) const
{
QFile file(candidatePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
DEBUG_OUT(QString("Failed to open surrogate candidate file: %1").arg(candidatePath));
return false;
}
QTextStream out(&file);
out << "particle_id,k,skin,wellboreC,phi,h,Cf\n";
for(int i = 0; i < m_swarm.size(); ++i) {
QVector<double> params = buildTraceParameterVector(m_swarm[i].position);
QStringList cols;
cols << QString::number(i)
<< traceParamAt(params, 0)
<< traceParamAt(params, 1)
<< traceParamAt(params, 2)
<< traceParamAt(params, 3)
<< traceParamAt(params, 5)
<< traceParamAt(params, 7);
out << cols.join(",") << "\n";
}
file.flush();
file.close();
return true;
}
bool nmCalculationAutoFitPSO::runSurrogateScoringProcess(const QString& candidatePath, const QString& scorePath, QString* failureReason)
{
QString serverFailure;
if(requestSurrogateScoresFromServer(candidatePath, scorePath, &serverFailure)) {
return true;
}
emit logMessageGenerated(tr("Surrogate scoring server unavailable; falling back to one-shot Python scoring"));
if(!serverFailure.isEmpty()) {
emit logMessageGenerated(tr("Surrogate scoring server detail: %1").arg(serverFailure));
}
stopSurrogateScoringServer();
return runSurrogateScoringScriptOnce(candidatePath, scorePath, failureReason);
}
bool nmCalculationAutoFitPSO::ensureSurrogateScoringServer(QString* failureReason)
{
if(m_traceMetaFilePath.isEmpty() || !QFileInfo(m_traceMetaFilePath).exists()) {
QString reason = "trace meta file is missing";
DEBUG_OUT(QString("Surrogate scoring server skipped: %1").arg(reason));
if(failureReason) {
*failureReason = reason;
}
return false;
}
QString mlRoot = getMlRootPath();
QString scriptPath = QDir(mlRoot).absoluteFilePath("scripts/score_pso_candidates_server.py");
if(!QFileInfo(scriptPath).exists()) {
QString reason = QString("surrogate scoring server script not found: %1").arg(scriptPath);
DEBUG_OUT(reason);
if(failureReason) {
*failureReason = reason;
}
return false;
}
QString pythonPath = getPythonExecutablePath();
QString serverKey = QString("%1|%2|%3|%4|%5")
.arg(pythonPath)
.arg(scriptPath)
.arg(m_traceMetaFilePath)
.arg(getSurrogateTag())
.arg(getSurrogateStage());
if(m_surrogateScoringProcess &&
m_surrogateScoringProcess->state() == QProcess::Running &&
m_surrogateScoringServerKey == serverKey) {
return true;
}
stopSurrogateScoringServer();
QStringList args;
args << "-u"
<< scriptPath
<< "--trace-meta" << m_traceMetaFilePath
<< "--tag" << getSurrogateTag()
<< "--stage" << getSurrogateStage();
m_surrogateScoringProcess = new QProcess(this);
m_surrogateScoringProcess->setWorkingDirectory(mlRoot);
emit logMessageGenerated(tr("Starting surrogate scoring server: python=%1, mlRoot=%2")
.arg(pythonPath)
.arg(mlRoot));
m_surrogateScoringProcess->start(pythonPath, args);
if(!m_surrogateScoringProcess->waitForStarted(15000)) {
QString reason = QString("failed to start scoring server: python=%1, script=%2, error=%3")
.arg(pythonPath)
.arg(scriptPath)
.arg(m_surrogateScoringProcess->errorString());
DEBUG_OUT(reason);
stopSurrogateScoringServer();
if(failureReason) {
*failureReason = reason;
}
return false;
}
QTime timer;
timer.start();
QByteArray readyLine;
while(timer.elapsed() < 120000) {
if(m_surrogateScoringProcess->state() != QProcess::Running) {
QString stdOut = QString::fromLocal8Bit(m_surrogateScoringProcess->readAllStandardOutput()).trimmed();
QString stdErr = QString::fromLocal8Bit(m_surrogateScoringProcess->readAllStandardError()).trimmed();
QString reason = QString("scoring server exited before ready: stdout=%1, stderr=%2")
.arg(stdOut.isEmpty() ? QString("<empty>") : stdOut)
.arg(stdErr.isEmpty() ? QString("<empty>") : stdErr);
DEBUG_OUT(reason);
stopSurrogateScoringServer();
if(failureReason) {
*failureReason = reason;
}
return false;
}
if(!m_surrogateScoringProcess->canReadLine()) {
m_surrogateScoringProcess->waitForReadyRead(500);
QApplication::processEvents();
continue;
}
readyLine = m_surrogateScoringProcess->readLine().trimmed();
if(readyLine.contains("\"ready\"") && readyLine.contains("true")) {
m_surrogateScoringServerKey = serverKey;
QString stdErr = QString::fromLocal8Bit(m_surrogateScoringProcess->readAllStandardError()).trimmed();
if(!stdErr.isEmpty()) {
emit logMessageGenerated(tr("Surrogate scoring server stderr: %1").arg(stdErr));
}
return true;
}
if(readyLine.contains("\"ok\"") && readyLine.contains("false")) {
QString reason = QString("scoring server failed during initialization: %1").arg(QString::fromLocal8Bit(readyLine));
DEBUG_OUT(reason);
stopSurrogateScoringServer();
if(failureReason) {
*failureReason = reason;
}
return false;
}
}
QString stdErr = QString::fromLocal8Bit(m_surrogateScoringProcess->readAllStandardError()).trimmed();
QString reason = QString("scoring server ready timeout: stderr=%1")
.arg(stdErr.isEmpty() ? QString("<empty>") : stdErr);
DEBUG_OUT(reason);
stopSurrogateScoringServer();
if(failureReason) {
*failureReason = reason;
}
return false;
}
bool nmCalculationAutoFitPSO::requestSurrogateScoresFromServer(const QString& candidatePath, const QString& scorePath, QString* failureReason)
{
if(!ensureSurrogateScoringServer(failureReason)) {
return false;
}
QFile::remove(scorePath);
QString command = QString("{\"cmd\":\"score\",\"candidates\":%1,\"output\":%2}\n")
.arg(jsonEscape(candidatePath))
.arg(jsonEscape(scorePath));
QByteArray bytes = command.toUtf8();
qint64 written = m_surrogateScoringProcess->write(bytes);
if(written != bytes.size() || !m_surrogateScoringProcess->waitForBytesWritten(10000)) {
QString reason = QString("failed to send scoring request to server: written=%1 expected=%2 error=%3")
.arg(written)
.arg(bytes.size())
.arg(m_surrogateScoringProcess->errorString());
DEBUG_OUT(reason);
if(failureReason) {
*failureReason = reason;
}
return false;
}
QTime timer;
timer.start();
while(timer.elapsed() < 120000) {
if(m_surrogateScoringProcess->state() != QProcess::Running) {
QString stdOut = QString::fromLocal8Bit(m_surrogateScoringProcess->readAllStandardOutput()).trimmed();
QString stdErr = QString::fromLocal8Bit(m_surrogateScoringProcess->readAllStandardError()).trimmed();
QString reason = QString("scoring server exited during request: stdout=%1, stderr=%2")
.arg(stdOut.isEmpty() ? QString("<empty>") : stdOut)
.arg(stdErr.isEmpty() ? QString("<empty>") : stdErr);
DEBUG_OUT(reason);
if(failureReason) {
*failureReason = reason;
}
return false;
}
if(!m_surrogateScoringProcess->canReadLine()) {
m_surrogateScoringProcess->waitForReadyRead(500);
QApplication::processEvents();
continue;
}
QByteArray lineBytes = m_surrogateScoringProcess->readLine().trimmed();
QString line = QString::fromLocal8Bit(lineBytes);
if(line.contains("\"ok\"") && line.contains("true")) {
QFileInfo scoreInfo(scorePath);
if(scoreInfo.exists() && scoreInfo.size() > 0) {
QString stdErr = QString::fromLocal8Bit(m_surrogateScoringProcess->readAllStandardError()).trimmed();
if(!stdErr.isEmpty()) {
emit logMessageGenerated(tr("Surrogate scoring server stderr: %1").arg(stdErr));
}
return true;
}
QString reason = QString("scoring server reported success but score file is missing: %1").arg(scorePath);
DEBUG_OUT(reason);
if(failureReason) {
*failureReason = reason;
}
return false;
}
if(line.contains("\"ok\"") && line.contains("false")) {
QString reason = QString("scoring server request failed: %1").arg(line);
DEBUG_OUT(reason);
if(failureReason) {
*failureReason = reason;
}
return false;
}
}
QString reason = "scoring server request timed out";
DEBUG_OUT(reason);
if(failureReason) {
*failureReason = reason;
}
return false;
}
void nmCalculationAutoFitPSO::stopSurrogateScoringServer()
{
if(!m_surrogateScoringProcess) {
return;
}
if(m_surrogateScoringProcess->state() == QProcess::Running) {
m_surrogateScoringProcess->write("{\"cmd\":\"quit\"}\n");
m_surrogateScoringProcess->waitForBytesWritten(1000);
m_surrogateScoringProcess->waitForFinished(3000);
if(m_surrogateScoringProcess->state() == QProcess::Running) {
m_surrogateScoringProcess->kill();
m_surrogateScoringProcess->waitForFinished(3000);
}
}
delete m_surrogateScoringProcess;
m_surrogateScoringProcess = nullptr;
m_surrogateScoringServerKey.clear();
}
bool nmCalculationAutoFitPSO::runSurrogateScoringScriptOnce(const QString& candidatePath, const QString& scorePath, QString* failureReason)
{
if(m_traceMetaFilePath.isEmpty() || !QFileInfo(m_traceMetaFilePath).exists()) {
QString reason = "trace meta file is missing";
DEBUG_OUT(QString("Surrogate scoring skipped: %1").arg(reason));
if(failureReason) {
*failureReason = reason;
}
return false;
}
QString mlRoot = getMlRootPath();
QString scriptPath = QDir(mlRoot).absoluteFilePath("scripts/score_pso_candidates.py");
if(!QFileInfo(scriptPath).exists()) {
QString reason = QString("surrogate scoring script not found: %1").arg(scriptPath);
DEBUG_OUT(reason);
if(failureReason) {
*failureReason = reason;
}
return false;
}
QString pythonPath = getPythonExecutablePath();
QStringList args;
args << scriptPath
<< "--candidates" << candidatePath
<< "--trace-meta" << m_traceMetaFilePath
<< "--output" << scorePath
<< "--tag" << getSurrogateTag()
<< "--stage" << getSurrogateStage();
QFile::remove(scorePath);
QString lastFailure;
for(int attempt = 1; attempt <= kSurrogateScoringMaxAttempts; ++attempt) {
emit logMessageGenerated(tr("Surrogate scoring attempt %1/%2: python=%3, mlRoot=%4")
.arg(attempt)
.arg(kSurrogateScoringMaxAttempts)
.arg(pythonPath)
.arg(mlRoot));
QProcess process;
process.setWorkingDirectory(mlRoot);
process.start(pythonPath, args);
if(!process.waitForStarted(10000)) {
lastFailure = QString("failed to start scoring process (attempt %1/%2): python=%3, script=%4, error=%5")
.arg(attempt)
.arg(kSurrogateScoringMaxAttempts)
.arg(pythonPath)
.arg(scriptPath)
.arg(process.errorString());
DEBUG_OUT(lastFailure);
if(attempt < kSurrogateScoringMaxAttempts) {
emit logMessageGenerated(tr("Surrogate scoring start failed, retrying once: %1").arg(process.errorString()));
msleep(500);
continue;
}
if(failureReason) {
*failureReason = lastFailure;
}
return false;
}
if(!process.waitForFinished(120000)) {
process.kill();
process.waitForFinished(3000);
QString stdOut = QString::fromLocal8Bit(process.readAllStandardOutput()).trimmed();
QString stdErr = QString::fromLocal8Bit(process.readAllStandardError()).trimmed();
lastFailure = QString("scoring process timed out (attempt %1/%2): python=%3, script=%4, stdout=%5, stderr=%6")
.arg(attempt)
.arg(kSurrogateScoringMaxAttempts)
.arg(pythonPath)
.arg(scriptPath)
.arg(stdOut.isEmpty() ? QString("<empty>") : stdOut)
.arg(stdErr.isEmpty() ? QString("<empty>") : stdErr);
DEBUG_OUT(lastFailure);
if(attempt < kSurrogateScoringMaxAttempts) {
emit logMessageGenerated(tr("Surrogate scoring timed out, retrying once"));
msleep(500);
continue;
}
if(failureReason) {
*failureReason = lastFailure;
}
return false;
}
QString stdOut = QString::fromLocal8Bit(process.readAllStandardOutput()).trimmed();
QString stdErr = QString::fromLocal8Bit(process.readAllStandardError()).trimmed();
if(process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) {
lastFailure = QString("scoring process failed (attempt %1/%2): exitStatus=%3, exitCode=%4, python=%5, script=%6, stdout=%7, stderr=%8")
.arg(attempt)
.arg(kSurrogateScoringMaxAttempts)
.arg(process.exitStatus() == QProcess::NormalExit ? "NormalExit" : "CrashExit")
.arg(process.exitCode())
.arg(pythonPath)
.arg(scriptPath)
.arg(stdOut.isEmpty() ? QString("<empty>") : stdOut)
.arg(stdErr.isEmpty() ? QString("<empty>") : stdErr);
DEBUG_OUT(lastFailure);
if(attempt < kSurrogateScoringMaxAttempts) {
emit logMessageGenerated(tr("Surrogate scoring process failed, retrying once: exitCode=%1").arg(process.exitCode()));
msleep(500);
continue;
}
if(failureReason) {
*failureReason = lastFailure;
}
return false;
}
QFileInfo scoreInfo(scorePath);
if(!scoreInfo.exists() || scoreInfo.size() <= 0) {
lastFailure = QString("scoring process finished without a usable score file (attempt %1/%2): output=%3, stdout=%4, stderr=%5")
.arg(attempt)
.arg(kSurrogateScoringMaxAttempts)
.arg(scorePath)
.arg(stdOut.isEmpty() ? QString("<empty>") : stdOut)
.arg(stdErr.isEmpty() ? QString("<empty>") : stdErr);
DEBUG_OUT(lastFailure);
if(attempt < kSurrogateScoringMaxAttempts) {
emit logMessageGenerated(tr("Surrogate scoring produced no score file, retrying once"));
msleep(500);
continue;
}
if(failureReason) {
*failureReason = lastFailure;
}
return false;
}
if(!stdErr.isEmpty()) {
emit logMessageGenerated(tr("Surrogate scoring stderr: %1").arg(stdErr));
}
if(!stdOut.isEmpty()) {
emit logMessageGenerated(tr("Surrogate scoring stdout: %1").arg(stdOut));
}
return true;
}
if(failureReason) {
*failureReason = lastFailure;
}
return false;
}
QVector<double> nmCalculationAutoFitPSO::readSurrogateScores(const QString& scorePath) const
{
QVector<double> scores(m_swarm.size(), std::numeric_limits<double>::quiet_NaN());
QFile file(scorePath);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
DEBUG_OUT(QString("Failed to open surrogate score file: %1").arg(scorePath));
return scores;
}
QTextStream in(&file);
bool firstLine = true;
while(!in.atEnd()) {
QString line = in.readLine().trimmed();
if(line.isEmpty()) {
continue;
}
if(firstLine) {
firstLine = false;
continue;
}
QStringList parts = line.split(",");
if(parts.size() < 5) {
continue;
}
bool idOk = false;
bool scoreOk = false;
int particleId = parts[0].toInt(&idOk);
double score = parts[1].toDouble(&scoreOk);
bool success = parts[4].trimmed() == "1";
if(idOk && scoreOk && success && particleId >= 0 && particleId < scores.size()) {
scores[particleId] = score;
}
}
return scores;
}
bool nmCalculationAutoFitPSO::isSurrogateRunContextSupported(QString* reason) const
{
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
if(!dataManager) {
if(reason) *reason = "data manager is unavailable";
return false;
}
if(dataManager->getGridType() != NM_Grid_PEBI) {
if(reason) *reason = "surrogate model is trained only for PEBI grid cases";
return false;
}
nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
if(!pTargetWell) {
if(reason) *reason = QString("target well '%1' is missing").arg(m_targetWellName);
return false;
}
if(pTargetWell->getWellType() != NM_WELL_MODEL::Vertical_Well) {
if(reason) *reason = "surrogate model is currently limited to vertical-well cases";
return false;
}
int validCalculationWells = 0;
QVector<QPair<NM_WELL_MODEL, QString> > calculationWells = dataManager->getCalculationWells();
for(int i = 0; i < calculationWells.size(); ++i) {
if(calculationWells[i].first != NM_WELL_MODEL::Unknow_Well) {
validCalculationWells++;
}
}
if(validCalculationWells != 1) {
if(reason) *reason = QString("surrogate model expects one active calculation well, got %1").arg(validCalculationWells);
return false;
}
if(!dataManager->getFaultDataList().isEmpty() ||
!dataManager->getFractureDataList().isEmpty() ||
!dataManager->getRegionDataList().isEmpty()) {
if(reason) *reason = "surrogate model is not enabled for changed fault/fracture/region geometry";
return false;
}
QVector<int> allowedParamIndices;
allowedParamIndices << 0 << 1 << 2 << 3 << 5;
for(int i = 0; i < m_enabledParamIndices.size(); ++i) {
if(!allowedParamIndices.contains(m_enabledParamIndices[i])) {
if(reason) {
QStringList names = traceParameterNames();
int idx = m_enabledParamIndices[i];
QString name = (idx >= 0 && idx < names.size()) ? names[idx] : QString::number(idx);
*reason = QString("parameter '%1' is outside surrogate input set").arg(name);
}
return false;
}
}
nmDataReservoir reservoirData = dataManager->getReservoirDataCopy();
double cf = reservoirData.getCf().getValue().toDouble();
if(!isNearRelative(cf, kSurrogateCfFixed, kSurrogateCfRelTol)) {
if(reason) {
*reason = QString("Cf=%1 is outside surrogate fixed-Cf domain").arg(cf, 0, 'g', 10);
}
return false;
}
QVector<QPointF> flowPoints = pTargetWell->getFlowPoints();
if(!flowPoints.isEmpty() && qAbs(flowPoints[0].x()) <= 1e-12 && qAbs(flowPoints[0].y()) <= 1e-12) {
flowPoints.remove(0);
}
if(flowPoints.size() < 2) {
if(reason) *reason = "flow schedule is too short for surrogate screening";
return false;
}
int productionSections = flowPoints.size() - 1;
if(productionSections < kSurrogateMinProdSections || productionSections > kSurrogateMaxProdSections) {
if(reason) {
*reason = QString("production section count %1 is outside surrogate range [%2,%3]")
.arg(productionSections)
.arg(kSurrogateMinProdSections)
.arg(kSurrogateMaxProdSections);
}
return false;
}
double productionTime = 0.0;
for(int i = 0; i < productionSections; ++i) {
double dt = flowPoints[i].x();
double q = flowPoints[i].y();
if(!isFiniteNumber(dt) || dt <= 0.0 || !isInClosedRange(q, kSurrogateQMin, kSurrogateQMax)) {
if(reason) {
*reason = QString("production schedule section %1 is outside surrogate dt/q domain").arg(i + 1);
}
return false;
}
if(i > 0) {
double previousQ = flowPoints[i - 1].y();
double stepRatio = qMax(q, previousQ) / qMax(1e-12, qMin(q, previousQ));
if(stepRatio > kSurrogateMaxRateStepRatio) {
if(reason) {
*reason = QString("rate step ratio %1 exceeds surrogate domain").arg(stepRatio, 0, 'g', 10);
}
return false;
}
}
productionTime += dt;
}
if(!getSurrogateStage().contains("v2_q")) {
double endStartRatio = std::numeric_limits<double>::quiet_NaN();
if(isStrongDecliningProductionSchedule(&endStartRatio)) {
if(reason) {
*reason = QString("strongly decreasing production schedule ratio %1 is outside surrogate domain")
.arg(endStartRatio, 0, 'g', 10);
}
return false;
}
}
if(!isInClosedRange(productionTime, kSurrogateProdTimeMin, kSurrogateProdTimeMax)) {
if(reason) {
*reason = QString("production total time %1 is outside surrogate range").arg(productionTime, 0, 'g', 10);
}
return false;
}
double shutinTime = flowPoints.last().x();
double shutinRate = flowPoints.last().y();
if(!isInClosedRange(shutinTime, kSurrogateShutinTimeMin, kSurrogateShutinTimeMax) ||
qAbs(shutinRate) > kSurrogateQZeroThreshold) {
if(reason) *reason = "last flow section is not a trained shut-in section";
return false;
}
if(pTargetWell->getIndexF() != flowPoints.size()) {
if(reason) {
*reason = QString("target section index %1 is not the last section %2")
.arg(pTargetWell->getIndexF())
.arg(flowPoints.size());
}
return false;
}
return true;
}
bool nmCalculationAutoFitPSO::isSurrogateCandidateInDomain(const QVector<double>& selectedParameters, QString* reason) const
{
QVector<double> params = buildTraceParameterVector(selectedParameters);
double k = params.size() > 0 ? params[0] : std::numeric_limits<double>::quiet_NaN();
double skin = params.size() > 1 ? params[1] : std::numeric_limits<double>::quiet_NaN();
double wellboreC = params.size() > 2 ? params[2] : std::numeric_limits<double>::quiet_NaN();
double phi = params.size() > 3 ? params[3] : std::numeric_limits<double>::quiet_NaN();
double h = params.size() > 5 ? params[5] : std::numeric_limits<double>::quiet_NaN();
double cf = params.size() > 7 ? params[7] : std::numeric_limits<double>::quiet_NaN();
if(!isInClosedRange(k, kSurrogateKMin, kSurrogateKMax)) {
if(reason) *reason = QString("k=%1").arg(k, 0, 'g', 10);
return false;
}
if(!isInClosedRange(skin, kSurrogateSkinMin, kSurrogateSkinMax)) {
if(reason) *reason = QString("skin=%1").arg(skin, 0, 'g', 10);
return false;
}
if(!isInClosedRange(wellboreC, kSurrogateWellboreCMin, kSurrogateWellboreCMax)) {
if(reason) *reason = QString("wellboreC=%1").arg(wellboreC, 0, 'g', 10);
return false;
}
if(!isInClosedRange(phi, kSurrogatePhiMin, kSurrogatePhiMax)) {
if(reason) *reason = QString("phi=%1").arg(phi, 0, 'g', 10);
return false;
}
if(!isInClosedRange(h, kSurrogateHMin, kSurrogateHMax)) {
if(reason) *reason = QString("h=%1").arg(h, 0, 'g', 10);
return false;
}
if(!isNearRelative(cf, kSurrogateCfFixed, kSurrogateCfRelTol)) {
if(reason) *reason = QString("Cf=%1").arg(cf, 0, 'g', 10);
return false;
}
return true;
}
bool nmCalculationAutoFitPSO::forceSolverByFallbackGate(const QVector<double>& selectedParameters) const
{
QVector<double> params = buildTraceParameterVector(selectedParameters);
double k = params.size() > 0 ? params[0] : std::numeric_limits<double>::quiet_NaN();
double skin = params.size() > 1 ? params[1] : std::numeric_limits<double>::quiet_NaN();
double wellboreC = params.size() > 2 ? params[2] : std::numeric_limits<double>::quiet_NaN();
double phi = params.size() > 3 ? params[3] : std::numeric_limits<double>::quiet_NaN();
double h = params.size() > 5 ? params[5] : std::numeric_limits<double>::quiet_NaN();
if(!isFiniteNumber(k) || !isFiniteNumber(skin) || !isFiniteNumber(wellboreC) || !isFiniteNumber(phi) || !isFiniteNumber(h)) {
return true;
}
if(k <= 1e-3 && wellboreC > 0.5) {
return true;
}
if(phi < 0.03 && h < 4.0) {
return true;
}
if(k < 0.1 && h >= 50.0) {
return true;
}
if(qAbs(skin) > 6.0) {
return true;
}
return false;
}
bool nmCalculationAutoFitPSO::isStrongDecliningProductionSchedule(double* endStartRatio) const
{
if(endStartRatio) {
*endStartRatio = std::numeric_limits<double>::quiet_NaN();
}
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
if(!dataManager) {
return false;
}
nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
if(!pTargetWell) {
return false;
}
QVector<QPointF> flowPoints = pTargetWell->getFlowPoints();
if(!flowPoints.isEmpty() && qAbs(flowPoints[0].x()) <= 1e-12 && qAbs(flowPoints[0].y()) <= 1e-12) {
flowPoints.remove(0);
}
if(flowPoints.size() < 2) {
return false;
}
int productionSections = flowPoints.size() - 1;
bool productionRatesNonIncreasing = true;
bool productionRatesHaveMeaningfulDrop = false;
for(int i = 0; i < productionSections; ++i) {
double q = flowPoints[i].y();
if(!isFiniteNumber(q)) {
return false;
}
if(i > 0) {
double previousQ = flowPoints[i - 1].y();
if(q > previousQ * (1.0 + kSurrogateRateTrendRelTol)) {
productionRatesNonIncreasing = false;
}
if(q < previousQ * (1.0 - kSurrogateRateTrendRelTol)) {
productionRatesHaveMeaningfulDrop = true;
}
}
}
if(!productionRatesNonIncreasing || !productionRatesHaveMeaningfulDrop) {
return false;
}
double firstRate = flowPoints.first().y();
double lastProductionRate = flowPoints[productionSections - 1].y();
double ratio = lastProductionRate / qMax(1e-12, firstRate);
if(endStartRatio) {
*endStartRatio = ratio;
}
return ratio < kSurrogateStrongDeclineEndStartRatio;
}
QVector<bool> nmCalculationAutoFitPSO::buildSurrogateEvaluationMask()
{
QVector<bool> evaluateMask(m_swarm.size(), true);
for(int i = 0; i < m_swarm.size(); ++i) {
m_swarm[i].surrogateObjective = std::numeric_limits<double>::quiet_NaN();
m_swarm[i].selectedForSolver = true;
m_swarm[i].selectedByAudit = false;
m_swarm[i].screeningDecision = "full_solver";
}
if(!isSurrogateScreeningEnabled()) {
return evaluateMask;
}
QString contextReason;
if(!isSurrogateRunContextSupported(&contextReason)) {
m_surrogateContextBlockedIterationCount++;
for(int i = 0; i < m_swarm.size(); ++i) {
m_swarm[i].screeningDecision = "context_gate_full_solver";
}
emit logMessageGenerated(tr("Surrogate screening disabled for this run context: %1").arg(contextReason));
return evaluateMask;
}
// Keep the early PSO generations fully evaluated so every particle gets a true pbest seed.
if(m_currentIteration < getSurrogateWarmupIterations()) {
m_surrogateWarmupIterationCount++;
for(int i = 0; i < m_swarm.size(); ++i) {
m_swarm[i].screeningDecision = "warmup_full_solver";
}
return evaluateMask;
}
if(getSurrogateFullSolverInterval() > 0 &&
m_currentIteration > 0 &&
((m_currentIteration + 1) % getSurrogateFullSolverInterval()) == 0) {
m_surrogatePeriodicAuditIterationCount++;
for(int i = 0; i < m_swarm.size(); ++i) {
m_swarm[i].screeningDecision = "periodic_full_solver";
}
emit logMessageGenerated(tr("Surrogate audit iteration %1: running full solver for all particles")
.arg(m_currentIteration + 1));
return evaluateMask;
}
QString mlRoot = getMlRootPath();
QDir tempDir(QDir(mlRoot).absoluteFilePath("data/temp"));
if(!tempDir.exists()) {
QDir().mkpath(tempDir.absolutePath());
}
QString candidatePath = tempDir.absoluteFilePath(QString("pso_screen_candidates_%1_gen%2.csv")
.arg(m_traceRunId)
.arg(m_currentIteration));
QString scorePath = tempDir.absoluteFilePath(QString("pso_screen_scores_%1_gen%2.csv")
.arg(m_traceRunId)
.arg(m_currentIteration));
QString scoringFailureReason;
if(!writeSurrogateCandidateCsv(candidatePath) || !runSurrogateScoringProcess(candidatePath, scorePath, &scoringFailureReason)) {
emit logMessageGenerated(tr("Surrogate screening unavailable; falling back to full solver for iteration %1")
.arg(m_currentIteration + 1));
if(!scoringFailureReason.isEmpty()) {
emit logMessageGenerated(tr("Surrogate scoring failure detail: %1").arg(scoringFailureReason));
}
return evaluateMask;
}
QVector<double> scores = readSurrogateScores(scorePath);
QVector<int> finiteScoreIndices;
for(int i = 0; i < scores.size(); ++i) {
m_swarm[i].surrogateObjective = scores[i];
if(isFiniteNumber(scores[i])) {
finiteScoreIndices.append(i);
}
}
if(finiteScoreIndices.isEmpty()) {
emit logMessageGenerated(tr("Surrogate scoring returned no valid scores; falling back to full solver"));
return evaluateMask;
}
std::sort(finiteScoreIndices.begin(), finiteScoreIndices.end(), [&](int a, int b) {
return scores[a] < scores[b];
});
evaluateMask.fill(false);
for(int i = 0; i < m_swarm.size(); ++i) {
m_swarm[i].selectedForSolver = false;
m_swarm[i].screeningDecision = "screened_out";
}
int keepN = qMax(1, qCeil(m_swarm.size() * getSurrogateKeepFraction()));
keepN = qMin(keepN, finiteScoreIndices.size());
for(int rank = 0; rank < keepN; ++rank) {
int idx = finiteScoreIndices[rank];
evaluateMask[idx] = true;
m_swarm[idx].selectedForSolver = true;
m_swarm[idx].screeningDecision = "surrogate_topk";
}
QVector<QPair<double, int> > auditCandidates;
for(int i = 0; i < finiteScoreIndices.size(); ++i) {
int idx = finiteScoreIndices[i];
if(!evaluateMask[idx]) {
auditCandidates.append(qMakePair(deterministicAuditRandom01(m_psoRandomSeed, m_currentIteration, idx), idx));
}
}
std::sort(auditCandidates.begin(), auditCandidates.end(), [](const QPair<double, int>& a, const QPair<double, int>& b) {
return a.first < b.first;
});
int auditN = qMin(auditCandidates.size(), qCeil(m_swarm.size() * getSurrogateAuditFraction()));
for(int i = 0; i < auditN; ++i) {
int idx = auditCandidates[i].second;
evaluateMask[idx] = true;
m_swarm[idx].selectedForSolver = true;
m_swarm[idx].selectedByAudit = true;
m_swarm[idx].screeningDecision = "random_audit";
}
int fallbackCount = 0;
int domainFallbackCount = 0;
for(int i = 0; i < m_swarm.size(); ++i) {
QString domainReason;
bool domainFallback = !isSurrogateCandidateInDomain(m_swarm[i].position, &domainReason);
bool fallback = !isFiniteNumber(scores[i]) ||
m_swarm[i].bestFitness >= 1e9 ||
forceSolverByFallbackGate(m_swarm[i].position) ||
domainFallback;
if(fallback) {
if(!evaluateMask[i]) {
fallbackCount++;
}
if(domainFallback) {
domainFallbackCount++;
}
evaluateMask[i] = true;
m_swarm[i].selectedForSolver = true;
m_swarm[i].screeningDecision = domainFallback ? "domain_gate" : "fallback_gate";
}
}
int selectedCount = 0;
for(int i = 0; i < evaluateMask.size(); ++i) {
if(evaluateMask[i]) {
selectedCount++;
}
}
int minSolverN = qMax(1, qCeil(m_swarm.size() * getSurrogateMinSolverFraction()));
if(selectedCount < minSolverN) {
QVector<QPair<double, int> > extraCandidates;
for(int i = 0; i < m_swarm.size(); ++i) {
if(!evaluateMask[i] && isFiniteNumber(scores[i])) {
extraCandidates.append(qMakePair(scores[i], i));
}
}
std::sort(extraCandidates.begin(), extraCandidates.end(), [](const QPair<double, int>& a, const QPair<double, int>& b) {
return a.first < b.first;
});
for(int i = 0; i < extraCandidates.size() && selectedCount < minSolverN; ++i) {
int idx = extraCandidates[i].second;
evaluateMask[idx] = true;
m_swarm[idx].selectedForSolver = true;
m_swarm[idx].screeningDecision = "min_solver_floor";
selectedCount++;
}
}
emit logMessageGenerated(tr("Surrogate screening iteration %1: selected %2/%3 particles (keep=%4, audit=%5, min_solver=%6, fallback=%7, domain=%8)")
.arg(m_currentIteration + 1)
.arg(selectedCount)
.arg(m_swarm.size())
.arg(getSurrogateKeepFraction(), 0, 'f', 2)
.arg(getSurrogateAuditFraction(), 0, 'f', 2)
.arg(getSurrogateMinSolverFraction(), 0, 'f', 2)
.arg(fallbackCount)
.arg(domainFallbackCount));
m_surrogateActiveIterationCount++;
for(int i = 0; i < m_swarm.size(); ++i) {
const QString& decision = m_swarm[i].screeningDecision;
if(m_swarm[i].selectedForSolver) {
m_surrogateSelectedParticleCount++;
}
if(decision == "screened_out") {
m_surrogateScreenedParticleCount++;
} else if(decision == "surrogate_topk") {
m_surrogateTopKParticleCount++;
} else if(decision == "random_audit") {
m_surrogateAuditParticleCount++;
} else if(decision == "fallback_gate") {
m_surrogateFallbackParticleCount++;
} else if(decision == "domain_gate") {
m_surrogateDomainParticleCount++;
} else if(decision == "min_solver_floor") {
m_surrogateMinFloorParticleCount++;
}
}
return evaluateMask;
}
//QString nmCalculationAutoFitPSO::getTargetWellName() const
//{
// return m_targetWellName;
//}
// ==================== 数据加载方法 ====================
bool nmCalculationAutoFitPSO::loadAllConfigFromDataManager()
{
try {
loadOptimizationConfig();
loadParameterBounds();
extractUserInitialValues(); // 直接提取初始值,无需条件判断
return true;
} catch(...) {
m_lastError = "Failed to load configuration from data manager";
return false;
}
}
void nmCalculationAutoFitPSO::loadOptimizationConfig()
{
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
nmDataAutomaticFitting fittingData = dataManager->getAutomaticFittingDataCopy();
// 加载基本配置
m_maxIterations = fittingData.getIterationCount().getValue().toInt();
m_targetError = fittingData.getErrorTolerance().getValue().toDouble();
m_surrogateScreeningEnabled = fittingData.getSurrogateScreeningEnabled();
// 设置PSO默认参数
m_swarmSize = 20;
m_inertiaWeight = 0.4;
m_cognitiveParam = 2.0;
m_socialParam = 1.2;
DEBUG_OUT(QString("Loaded optimization config: iterations=%1, error=%2, surrogate=%3")
.arg(m_maxIterations).arg(m_targetError).arg(m_surrogateScreeningEnabled ? "enabled" : "disabled"));
}
void nmCalculationAutoFitPSO::loadParameterBounds()
{
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
nmDataAutomaticFitting fittingData = dataManager->getAutomaticFittingDataCopy();
// 获取参数选择状态
m_parameterSelected.resize(11);
m_parameterSelected[0] = fittingData.getPermeabilitySelected();
m_parameterSelected[1] = fittingData.getSkinSelected();
m_parameterSelected[2] = fittingData.getWellboreStorageSelected();
m_parameterSelected[3] = fittingData.getPorositySelected();
m_parameterSelected[4] = fittingData.getInitialPressureSelected();
m_parameterSelected[5] = fittingData.getThicknessSelected();
m_parameterSelected[6] = fittingData.getCtSelected();
m_parameterSelected[7] = fittingData.getCfSelected();
m_parameterSelected[8] = fittingData.getSoiSelected();
m_parameterSelected[9] = fittingData.getSwiSelected();
m_parameterSelected[10] = fittingData.getSgiSelected();
// 获取参数边界
m_parameterLower.resize(11);
m_parameterUpper.resize(11);
m_parameterLower[0] = fittingData.getPermeabilityMin().getValue().toDouble();
m_parameterUpper[0] = fittingData.getPermeabilityMax().getValue().toDouble();
m_parameterLower[1] = fittingData.getSkinMin().getValue().toDouble();
m_parameterUpper[1] = fittingData.getSkinMax().getValue().toDouble();
m_parameterLower[2] = fittingData.getWellboreStorageMin().getValue().toDouble();
m_parameterUpper[2] = fittingData.getWellboreStorageMax().getValue().toDouble();
m_parameterLower[3] = fittingData.getPorosityMin().getValue().toDouble();
m_parameterUpper[3] = fittingData.getPorosityMax().getValue().toDouble();
m_parameterLower[4] = fittingData.getInitialPressureMin().getValue().toDouble();
m_parameterUpper[4] = fittingData.getInitialPressureMax().getValue().toDouble();
m_parameterLower[5] = fittingData.getThicknessMin().getValue().toDouble();
m_parameterUpper[5] = fittingData.getThicknessMax().getValue().toDouble();
m_parameterLower[6] = fittingData.getCtMin().getValue().toDouble();
m_parameterUpper[6] = fittingData.getCtMax().getValue().toDouble();
m_parameterLower[7] = fittingData.getCfMin().getValue().toDouble();
m_parameterUpper[7] = fittingData.getCfMax().getValue().toDouble();
m_parameterLower[8] = fittingData.getSoiMin().getValue().toDouble();
m_parameterUpper[8] = fittingData.getSoiMax().getValue().toDouble();
m_parameterLower[9] = fittingData.getSwiMin().getValue().toDouble();
m_parameterUpper[9] = fittingData.getSwiMax().getValue().toDouble();
m_parameterLower[10] = fittingData.getSgiMin().getValue().toDouble();
m_parameterUpper[10] = fittingData.getSgiMax().getValue().toDouble();
// 更新启用参数索引
m_enabledParamIndices.clear();
for(int i = 0; i < m_parameterSelected.size(); ++i) {
if(m_parameterSelected[i]) {
m_enabledParamIndices.append(i);
}
}
DEBUG_OUT(QString("Loaded parameter bounds: %1 enabled parameters")
.arg(m_enabledParamIndices.size()));
}
// ==================== PSO算法核心方法 ====================
bool nmCalculationAutoFitPSO::startAutoFitting()
{
if(m_isRunning) {
m_lastError = "Auto fitting is already running";
return false;
}
// 发送初始化日志
//emit logMessageGenerated(tr("=== PSO Automatic Fitting Started ==="));
emit logMessageGenerated(tr("Algorithm: Particle Swarm Optimization"));
try {
// 从数据管理器加载所有配置
if(!loadAllConfigFromDataManager()) {
emit logMessageGenerated(tr("ERROR: Failed to load configuration from data manager"));
return false;
}
if(m_simulationMode) {
DEBUG_OUT("=== SIMULATION MODE ACTIVATED ===");
runSimulatedFitting();
return true;
}
int enabledParams = getEnabledParameterCount();
emit logMessageGenerated(tr("Enabled parameters count: %1").arg(enabledParams));
if(enabledParams == 0) {
m_lastError = "No parameters enabled for optimization";
emit logMessageGenerated(tr("ERROR: No parameters enabled for optimization"));
return false;
}
if(m_targetLogLogData.isEmpty() || m_targetLogLogData.size() < 3) {
m_lastError = "Target LogLog data is empty or insufficient";
emit logMessageGenerated(tr("ERROR: Target LogLog data is empty or insufficient"));
return false;
}
// 检查数据一致性
if(m_targetLogLogData[0].size() != m_targetLogLogData[1].size() ||
m_targetLogLogData[0].size() != m_targetLogLogData[2].size()) {
m_lastError = "Target LogLog data arrays have inconsistent sizes";
emit logMessageGenerated(tr("ERROR: Target LogLog data arrays have inconsistent sizes"));
return false;
}
emit logMessageGenerated(tr("Target data validation passed (%1 data points)").arg(m_targetLogLogData[0].size()));
// 使用保存的初始值进行精英保护
QVector<double> savedInitialValues = m_initialValues;
// 重置状态
resetOptimizer();
m_isRunning = true;
m_shouldStop = false;
m_isPaused = false;
m_currentIteration = 0;
m_consecutiveFailures = 0;
if(kUseFixedPsoSeed) {
m_psoRandomSeed = kFixedPsoSeed;
} else {
QTime seedTime = QTime::currentTime();
m_psoRandomSeed = static_cast<unsigned int>(QDateTime::currentDateTime().toTime_t());
m_psoRandomSeed ^= static_cast<unsigned int>((seedTime.hour() << 22) ^ (seedTime.minute() << 16) ^ (seedTime.second() << 10) ^ seedTime.msec());
}
if(m_psoRandomSeed == 0u) {
m_psoRandomSeed = 1u;
}
qsrand(m_psoRandomSeed);
m_consecutiveFailedIterations = 0; // 重置连续失败迭代计数
// 精英保护:评估用户初始解
m_initialValues = savedInitialValues;
initializeTraceFile();
if(!savedInitialValues.isEmpty()) {
m_userInitialSolution = savedInitialValues;
emit logMessageGenerated(tr("=== Evaluating Initial Solution (Elite Protection) ==="));
// 输出初始参数值
QString paramStr = tr("Initial parameters: ");
for(int i = 0; i < m_userInitialSolution.size(); ++i) {
paramStr += QString("[%1]=%2 ").arg(i).arg(m_userInitialSolution[i], 0, 'f', 6);
}
emit logMessageGenerated(paramStr);
try {
emit logMessageGenerated(tr("Starting initial solution evaluation..."));
DEBUG_OUT(QString("Before evaluateError: m_userInitialSolution size = %1").arg(m_userInitialSolution.size()));
if(m_userInitialSolution.isEmpty()) {
emit logMessageGenerated(tr("ERROR: m_userInitialSolution is empty!"));
return false;
}
QTime initialEvalTimer;
initialEvalTimer.start();
m_userInitialFitness = evaluateFitness(m_userInitialSolution);
int initialEvalElapsedMs = initialEvalTimer.elapsed();
if(m_userInitialFitness < 1e9) {
m_hasValidUserSolution = true;
m_globalBestFitness = m_userInitialFitness;
m_globalBestPosition = m_userInitialSolution;
m_userInitialLogLogData = m_lastEvaluatedLogLogData;
m_globalBestLogLogData = m_userInitialLogLogData;
emit logMessageGenerated(tr("Initial solution evaluation successful"));
emit logMessageGenerated(tr("Initial Error: %1").arg(m_userInitialFitness, 0, 'e', 4));
emit logMessageGenerated(tr("Elite protection activated - initial solution will be preserved if no significant improvement found"));
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, 0, m_globalBestFitness);
} else {
m_hasValidUserSolution = false;
emit logMessageGenerated(tr("Initial solution evaluation failed - starting with random initialization"));
}
writeTraceRow(-1,
-1,
"initial_solution",
m_userInitialSolution,
m_userInitialFitness,
m_userInitialFitness < 1e9,
initialEvalElapsedMs,
std::numeric_limits<double>::quiet_NaN(),
"initial_solution",
m_hasValidUserSolution ? m_userInitialSolution : QVector<double>(),
m_userInitialFitness);
} catch(...) {
m_hasValidUserSolution = false;
emit logMessageGenerated(tr("Exception during initial solution evaluation"));
}
// 恢复初始值供粒子群初始化使用
m_initialValues = savedInitialValues;
}
// 初始化粒子群
if(kUseFixedPsoSeed) {
emit logMessageGenerated(tr("PSO random seed: %1 ").arg(m_psoRandomSeed));
} else {
emit logMessageGenerated(tr("PSO random seed: %1 ").arg(m_psoRandomSeed));
}
initializeSwarm();
emit logMessageGenerated(tr("Swarm initialized: %1 particles, %2 dimensions").arg(m_swarmSize).arg(getEnabledParameterCount()));
// PSO主循环
emit logMessageGenerated(tr("=== Starting PSO Main Loop ==="));
for(m_currentIteration = 0; m_currentIteration < m_maxIterations && !m_shouldStop; ++m_currentIteration) {
// 每次迭代都输出标题,或者只在重要迭代输出详细信息
bool shouldOutputDetail = (m_currentIteration % qMax(1, m_maxIterations / 10) == 0) ||
(m_currentIteration < 5) ||
(m_currentIteration >= m_maxIterations - 2); // 总是输出前5次和最后2次的详细信息
// 每次迭代都输出标题
emit logMessageGenerated(tr("--- Iteration %1/%2 ---").arg(m_currentIteration + 1).arg(m_maxIterations));
// 只在特定迭代输出详细统计信息
if(shouldOutputDetail) {
emit logMessageGenerated(tr("Current best error: %1").arg(m_globalBestFitness, 0, 'e', 4));
emit logMessageGenerated(tr("Total evaluations: %1 (successful: %2, failures: %3)")
.arg(m_totalEvaluations).arg(m_successfulEvaluations).arg(m_totalEvaluations - m_successfulEvaluations));
}
// 检查暂停状态
while(m_isPaused && !m_shouldStop) {
QApplication::processEvents();
msleep(100);
}
if(m_shouldStop) {
emit logMessageGenerated(tr("Optimization stopped by user request"));
break;
}
int currentIterationSuccessful = 0;
int currentIterationTotal = 0;
int currentIterationFailed = 0;
int currentIterationSkipped = 0;
QVector<bool> evaluateMask = buildSurrogateEvaluationMask();
for(int i = 0; i < m_swarmSize && !m_shouldStop; ++i) {
try {
if(i < evaluateMask.size() && !evaluateMask[i]) {
AutoFitParticle& particle = m_swarm[i];
particle.evaluatedThisIteration = false;
particle.lastEvaluationSuccess = false;
particle.lastEvaluationElapsedMs = -1;
particle.selectedForSolver = false;
if(particle.screeningDecision.isEmpty()) {
particle.screeningDecision = "screened_out";
}
currentIterationSkipped++;
continue;
}
double previousFitness = m_swarm[i].fitness;
updateParticle(i);
currentIterationTotal++;
if(m_swarm[i].fitness < 1e9) {
currentIterationSuccessful++;
if(shouldOutputDetail && m_swarm[i].fitness < previousFitness) {
// 计算改进幅度
double improvement = (previousFitness - m_swarm[i].fitness) / qMax(1e-10, previousFitness);
if(improvement > 0.01) { // 1%以上改进才输出
emit logMessageGenerated(tr(" Particle %1 improved: %2 -> %3 (%4% improvement)")
.arg(i + 1).arg(previousFitness, 0, 'e', 3).arg(m_swarm[i].fitness, 0, 'e', 3)
.arg(improvement * 100, 0, 'f', 2));
}
}
} else {
currentIterationFailed++;
if(shouldOutputDetail) {
emit logMessageGenerated(tr(" Particle %1: evaluation failed").arg(i + 1));
}
}
// 每评估2个粒子处理一次事件
if(i % 2 == 0) {
QApplication::processEvents();
}
} catch(const std::exception& e) {
if(shouldOutputDetail) {
emit logMessageGenerated(tr(" Particle %1: Exception: %2").arg(i + 1).arg(e.what()));
}
m_swarm[i].fitness = 1e10;
currentIterationTotal++;
currentIterationFailed++;
m_totalEvaluations++;
} catch(...) {
if(shouldOutputDetail) {
emit logMessageGenerated(tr(" Particle %1: Unknown exception").arg(i + 1));
}
m_swarm[i].fitness = 1e10;
currentIterationTotal++;
currentIterationFailed++;
m_totalEvaluations++;
}
}
if(m_shouldStop) break;
double currentSuccessRate = currentIterationTotal > 0 ?
(double)currentIterationSuccessful / currentIterationTotal : 0.0;
// 更新连续失败迭代计数
if(currentIterationSuccessful == 0) {
m_consecutiveFailedIterations++;
emit logMessageGenerated(tr("WARNING: No successful evaluations in iteration %1 (consecutive failures: %2)")
.arg(m_currentIteration + 1).arg(m_consecutiveFailedIterations));
} else {
m_consecutiveFailedIterations = 0; // 重置连续失败计数
}
// 输出当前迭代统计
if(shouldOutputDetail) {
emit logMessageGenerated(tr("Current iteration stats: %1 successful, %2 failed, %3 skipped out of %4 particles (solver success rate: %5%)")
.arg(currentIterationSuccessful).arg(currentIterationFailed)
.arg(currentIterationSkipped)
.arg(m_swarmSize).arg(currentSuccessRate * 100, 0, 'f', 1));
}
// 检查是否需要停止优化
if(m_consecutiveFailedIterations >= m_maxConsecutiveFailures) {
m_lastError = QString("Too many consecutive failed iterations (%1)").arg(m_consecutiveFailedIterations);
emit logMessageGenerated(tr("ERROR: Too many consecutive failed iterations (%1/%2) - stopping optimization")
.arg(m_consecutiveFailedIterations).arg(m_maxConsecutiveFailures));
break;
}
// 警告低成功率但不立即停止
if(currentSuccessRate < 0.5 && m_currentIteration > 3) {
emit logMessageGenerated(tr("WARNING: Low success rate (%1%) in iteration %2, but continuing optimization")
.arg(currentSuccessRate * 100, 0, 'f', 1).arg(m_currentIteration + 1));
}
// 更新全局最优
//double previousGlobalBest = m_globalBestFitness;
updateGlobalBest();
writeIterationTraceRows();
// 自适应参数更新
adaptiveParameterUpdate(m_currentIteration);
emit progressUpdated(m_currentIteration + 1, m_globalBestFitness);
// 记录收敛历史
m_convergenceHistory.append(m_globalBestFitness);
// 每5次迭代输出收敛状态
if((m_currentIteration + 1) % 5 == 0) {
double recentImprovement = 0.0;
if(m_convergenceHistory.size() >= 5) {
double oldBest = m_convergenceHistory[m_convergenceHistory.size() - 5];
recentImprovement = (oldBest - m_globalBestFitness) / qMax(1e-10, oldBest);
}
emit logMessageGenerated(tr(" Convergence check: recent improvement = %1% over last 5 iterations")
.arg(recentImprovement * 100, 0, 'f', 4));
}
// 更新收敛指标
updateConvergenceMetrics();
// 智能收敛判断
StopReasonPSO stopReason = analyzeOptimizationStatus();
if(stopReason == PSO_TARGET_ACHIEVED) {
emit logMessageGenerated(tr("=== TARGET ACHIEVED ==="));
emit logMessageGenerated(tr("Target error achieved! Current error: %1 < Target: %2")
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_targetError, 0, 'e', 4));
emit logMessageGenerated(tr("Optimization completed successfully after %1 iterations")
.arg(m_currentIteration + 1));
break;
} else if(stopReason == PSO_TRUE_CONVERGENCE) {
emit logMessageGenerated(tr("=== TRUE CONVERGENCE DETECTED ==="));
emit logMessageGenerated(tr("Algorithm has converged to a stable solution"));
emit logMessageGenerated(tr("Final error: %1 after %2 iterations")
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1));
emit logMessageGenerated(tr("Solution quality: %1 (1.0 = target achieved)")
.arg(m_targetError / qMax(1e-10, m_globalBestFitness), 0, 'f', 3));
break;
} else if(stopReason == PSO_LOCAL_OPTIMUM) {
emit logMessageGenerated(tr("=== LOCAL OPTIMUM DETECTED ==="));
emit logMessageGenerated(tr("Algorithm appears to be trapped in local optimum"));
emit logMessageGenerated(tr("Current error: %1 after %2 iterations")
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1));
emit logMessageGenerated(tr("Suggestion: Try restarting with different parameters or larger search space"));
break;
} else if(stopReason == PSO_CONSECUTIVE_FAILURES) {
emit logMessageGenerated(tr("=== CONSECUTIVE FAILURES ==="));
emit logMessageGenerated(tr("Too many consecutive failed iterations (%1/%2)")
.arg(m_consecutiveFailedIterations).arg(m_maxConsecutiveFailures));
break;
} else if(stopReason == PSO_CONTINUE_OPTIMIZATION) {
// 继续优化每10次迭代输出一次状态
if(m_currentIteration > 0 && m_currentIteration % 10 == 0) {
double diversity = calculateSwarmDiversity();
double avgVel = calculateAverageVelocity();
double stagnation = calculateParticleStagnationRate();
emit logMessageGenerated(tr(" Optimization status: diversity=%1, avgVel=%2, stagnation=%3%")
.arg(diversity, 0, 'f', 4).arg(avgVel, 0, 'f', 4).arg(stagnation * 100, 0, 'f', 1));
}
}
// 更新粒子位置和速度
updateVelocityAndPosition();
// 输出迭代结束标记
if(shouldOutputDetail) {
emit logMessageGenerated(tr(" Iteration %1 completed - Current best: %2")
.arg(m_currentIteration + 1).arg(m_globalBestFitness, 0, 'e', 4));
}
// 强制处理事件,保持界面响应
QApplication::processEvents();
// 在迭代间稍作停顿,减少系统负载
if(m_currentIteration % 3 == 2) {
msleep(200);
}
}
// 最终结果验证和保护
validateAndProtectFinalResult();
} catch(const std::exception& e) {
m_lastError = QString(tr("Critical exception in PSO main loop: %1")).arg(e.what());
emit logMessageGenerated(tr("CRITICAL ERROR: %1").arg(e.what()));
closeTraceFile();
cleanupTemporaryDirectory();
m_isRunning = false;
emit fittingFinished(false, m_lastError);
return false;
} catch(...) {
m_lastError = QString(tr("Unknown critical exception in PSO main loop"));
emit logMessageGenerated(tr("CRITICAL ERROR: Unknown exception in PSO main loop"));
closeTraceFile();
cleanupTemporaryDirectory();
m_isRunning = false;
emit fittingFinished(false, m_lastError);
return false;
}
m_isRunning = false;
// 应用最终参数
if(!m_globalBestPosition.isEmpty()) {
try {
emit logMessageGenerated(tr("Applying optimized parameters to model..."));
applyParametersToDataManager(m_globalBestPosition);
saveOptimizationResult();
// 输出最终优化结果
emit logMessageGenerated(tr("=== Optimization Results ==="));
emit logMessageGenerated(tr("Final error: %1").arg(m_globalBestFitness, 0, 'e', 4));
emit logMessageGenerated(tr("Total iterations: %1").arg(m_currentIteration + 1)); // 显示实际完成的迭代数
emit logMessageGenerated(tr("Total evaluations: %1 (successful: %2)")
.arg(m_totalEvaluations).arg(m_successfulEvaluations));
// 输出最优参数值
QString finalParams = tr("Optimized parameters: ");
for(int i = 0; i < m_globalBestPosition.size(); ++i) {
finalParams += QString("[%1]=%2 ").arg(i).arg(m_globalBestPosition[i], 0, 'f', 6);
}
emit logMessageGenerated(finalParams);
emit logMessageGenerated(tr("Parameters applied successfully to data manager"));
} catch(const std::exception& e) {
emit logMessageGenerated(tr("ERROR: Failed to apply final parameters: %1").arg(e.what()));
m_lastError = QString("Failed to apply final parameters: %1").arg(e.what());
} catch(...) {
emit logMessageGenerated(tr("ERROR: Unknown error applying final parameters"));
m_lastError = "Failed to apply final parameters due to unknown error";
}
}
// 判断系统确定最终结果
bool success;
QString message;
StopReasonPSO finalReason = analyzeOptimizationStatus();
if(finalReason == PSO_TARGET_ACHIEVED) {
success = true;
message = QString(tr("Target achieved. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== PSO OPTIMIZATION SUCCESSFUL ==="));
} else if(finalReason == PSO_TRUE_CONVERGENCE) {
success = true;
message = QString(tr("PSO optimization converged to stable solution. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== PSO OPTIMIZATION CONVERGED ==="));
} else if(finalReason == PSO_LOCAL_OPTIMUM) {
success = true;
message = QString(tr("PSO optimization trapped in local optimum. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== PSO OPTIMIZATION - LOCAL OPTIMUM ==="));
} else if(finalReason == PSO_MAX_ITERATIONS) {
success = true;
message = QString(tr("Max iterations reached. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== PSO OPTIMIZATION - MAX ITERATIONS ==="));
} else if(finalReason == PSO_USER_STOPPED) {
success = true;
message = QString(tr("Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== PSO OPTIMIZATION STOPPED BY USER ==="));
} else if(finalReason == PSO_CONSECUTIVE_FAILURES) {
success = false;
message = QString(tr("PSO optimization failed due to consecutive failures. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== PSO OPTIMIZATION FAILED ==="));
} else {
success = false;
message = QString(tr("PSO optimization ended unexpectedly. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== PSO OPTIMIZATION - UNKNOWN END ==="));
}
emitRunSummary(success, finalReason);
// 先发送最终进度更新确保进度条达到100%
emit progressUpdated(m_maxIterations, m_globalBestFitness);
// 确保界面更新完成
QApplication::processEvents();
// 短暂延迟,让进度条动画完成
msleep(200);
// 再次确保界面更新
QApplication::processEvents();
// 现在才发送完成信号,这会触发消息框
closeTraceFile();
emit fittingFinished(success, message);
cleanupTemporaryDirectory();
return success;
}
void nmCalculationAutoFitPSO::extractUserInitialValues()
{
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
nmDataReservoir reservoirData = dataManager->getReservoirDataCopy();
//QVector<nmDataWellBase*> wells = dataManager->getWellDataList();
nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
m_initialValues.clear();
// 按照启用参数的顺序提取初始值
for(int i = 0; i < m_enabledParamIndices.size(); ++i) {
int paramIndex = m_enabledParamIndices[i];
double initialValue = 0.0;
switch(paramIndex) {
case 0: // 渗透率
initialValue = reservoirData.getPermeability().getValue().toDouble();
break;
case 1: // 表皮系数
if(pTargetWell) {
initialValue = pTargetWell->getPerforation(0)->getSkin().getValue().toDouble();
}
break;
case 2: // 井筒储集系数
if(pTargetWell) {
initialValue = pTargetWell->getWellboreStorage().getValue().toDouble();
}
break;
case 3: // 孔隙度
initialValue = reservoirData.getPorosity().getValue().toDouble();
break;
case 4: // 初始压力
initialValue = reservoirData.getInitialPressure().getValue().toDouble();
break;
case 5: // 储层厚度
initialValue = reservoirData.getThickness().getValue().toDouble();
break;
case 6: // 综合压缩系数
initialValue = reservoirData.getCt().getValue().toDouble();
break;
case 7: // 岩石压缩系数
initialValue = reservoirData.getCf().getValue().toDouble();
break;
case 8: // 初始含油饱和度
initialValue = reservoirData.getSoi().getValue().toDouble();
break;
case 9: // 初始含水饱和度
initialValue = reservoirData.getSwi().getValue().toDouble();
break;
case 10: // 初始含气饱和度
initialValue = reservoirData.getSgi().getValue().toDouble();
break;
}
m_initialValues.append(initialValue);
}
DEBUG_OUT(QString("Extracted %1 user initial values").arg(m_initialValues.size()));
for(int i = 0; i < m_initialValues.size(); ++i) {
DEBUG_OUT(QString(" Initial[%1] = %2").arg(i).arg(m_initialValues[i], 0, 'e', 3));
}
// 验证初始值
if(!validateInitialValues()) {
DEBUG_OUT("Warning: Some initial values are outside parameter bounds");
}
}
void nmCalculationAutoFitPSO::initializeSwarm()
{
int dimensions = getEnabledParameterCount();
if(dimensions == 0) return;
m_swarm.resize(m_swarmSize);
m_globalBestPosition.resize(dimensions);
bool hasValidInitials = !m_initialValues.isEmpty() && m_initialValues.size() >= dimensions;
// 引导搜索比例
//int guidedCount = hasValidInitials ? qMax(2, (m_swarmSize * 3) / 4) : qMax(1, m_swarmSize / 3);
int guidedCount = hasValidInitials ? qMax(2, m_swarmSize / 2) : qMax(1, m_swarmSize / 3);
DEBUG_OUT(QString("Enhanced swarm initialization: %1 particles, %2 guided, %3 random")
.arg(m_swarmSize).arg(guidedCount).arg(m_swarmSize - guidedCount));
for(int i = 0; i < m_swarmSize; ++i) {
AutoFitParticle& particle = m_swarm[i];
particle.position.resize(dimensions);
particle.velocity.resize(dimensions);
particle.bestPosition.resize(dimensions);
particle.bestFitness = 1e10;
particle.fitness = 1e10;
particle.evaluatedThisIteration = false;
particle.lastEvaluationSuccess = false;
particle.lastEvaluationElapsedMs = -1;
particle.surrogateObjective = 1e10;
particle.selectedForSolver = true;
particle.selectedByAudit = false;
particle.screeningDecision = "full_solver";
for(int j = 0; j < dimensions; ++j) {
int paramIndex = m_enabledParamIndices[j];
double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
if(i == 0 && hasValidInitials) {
// 第一个粒子:完全使用用户初始值
particle.position[j] = m_initialValues[j];
} else if(i < guidedCount && hasValidInitials) {
// 搜索策略
double searchRadius;
if(i <= guidedCount / 3) {
searchRadius = range * 0.03; // 3%范围内精细搜索
} else if(i <= guidedCount * 2 / 3) {
searchRadius = range * 0.08; // 8%范围内搜索
} else {
searchRadius = range * 0.15; // 15%范围内搜索
}
double offset = (random01() - 0.5) * searchRadius;
particle.position[j] = m_initialValues[j] + offset;
} else {
// 少数粒子进行全局搜索
particle.position[j] = m_parameterLower[paramIndex] + random01() * range;
}
}
// 边界检查
clampToLimits(particle.position);
// 速度初始化
for(int j = 0; j < dimensions; ++j) {
int paramIndex = m_enabledParamIndices[j];
double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
double velocityFactor;
if(i < guidedCount) {
velocityFactor = VELOCITY_LIMIT_FACTOR * 0.15; // 更小的速度
} else {
velocityFactor = VELOCITY_LIMIT_FACTOR * 0.8; // 适中速度
}
particle.velocity[j] = (random01() - 0.5) * range * velocityFactor;
}
particle.bestPosition = particle.position;
}
}
void nmCalculationAutoFitPSO::updateParticle(int particleIndex)
{
if(particleIndex < 0 || particleIndex >= m_swarm.size()) return;
AutoFitParticle& particle = m_swarm[particleIndex];
// 评估适应度
particle.evaluatedThisIteration = false;
particle.lastEvaluationSuccess = false;
particle.lastEvaluationElapsedMs = -1;
particle.selectedForSolver = true;
if(particle.screeningDecision.isEmpty()) {
particle.screeningDecision = "full_solver";
}
QTime evalTimer;
evalTimer.start();
particle.fitness = evaluateFitness(particle.position);
particle.currentLogLogData = m_lastEvaluatedLogLogData;
particle.lastEvaluationElapsedMs = evalTimer.elapsed();
particle.evaluatedThisIteration = true;
particle.lastEvaluationSuccess = (particle.fitness < 1e9);
m_totalEvaluations++;
if(particle.fitness < 1e9) {
m_successfulEvaluations++;
}
// Update particle best.
if(particle.fitness < particle.bestFitness) {
particle.bestFitness = particle.fitness;
particle.bestPosition = particle.position;
particle.bestLogLogData = particle.currentLogLogData;
DEBUG_OUT(QString("Particle %1 improved: error = %2")
.arg(particleIndex).arg(particle.fitness, 0, 'e', 4));
}
}
void nmCalculationAutoFitPSO::updateGlobalBest()
{
// 保存上一轮的全局最优,用于后续自适应参数调整等
m_previousBestFitness = m_globalBestFitness;
bool globalBestUpdated = false;
// 遍历所有粒子,寻找比当前 global best 更好的个体最优
for(int i = 0; i < m_swarm.size(); ++i) {
const AutoFitParticle& particle = m_swarm[i];
// 只要个体最优比当前全局最优小,就认为是更好的解
if(particle.bestFitness < m_globalBestFitness) {
double improvement = m_globalBestFitness - particle.bestFitness;
double relativeImprovement =
improvement / qMax(1e-10, qAbs(m_globalBestFitness));
// 区分显著改进和微小改进,但无论如何都会更新全局最优
if(relativeImprovement > m_improvementThreshold) {
DEBUG_OUT(QString(tr("Global best updated with %1% improvement: %2"))
.arg(relativeImprovement * 100.0, 0, 'f', 3)
.arg(particle.bestFitness, 0, 'e', 4));
} else {
DEBUG_OUT(QString(tr("Global best updated (minor improvement %1% < %2%) to %3"))
.arg(relativeImprovement * 100.0, 0, 'f', 3)
.arg(m_improvementThreshold * 100.0, 0, 'f', 2)
.arg(particle.bestFitness, 0, 'e', 4));
}
// 无论相对改进是否超过阈值,都要更新全局最优
m_globalBestFitness = particle.bestFitness;
m_globalBestPosition = particle.bestPosition;
m_globalBestLogLogData = particle.bestLogLogData;
globalBestUpdated = true;
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
}
}
// 只有在本轮没有找到任何更好的解时才考虑恢复用户初始解
if(!globalBestUpdated && m_hasValidUserSolution && m_userInitialFitness < m_globalBestFitness) {
double improvement = m_globalBestFitness - m_userInitialFitness;
double relativeImprovement =
improvement / qMax(1e-10, qAbs(m_globalBestFitness));
DEBUG_OUT("Elite protection: Restoring user initial solution as global best");
DEBUG_OUT(QString("Elite solution is better than current best by %1%")
.arg(relativeImprovement * 100.0, 0, 'f', 3));
m_globalBestFitness = m_userInitialFitness;
m_globalBestPosition = m_userInitialSolution;
m_globalBestLogLogData = m_userInitialLogLogData;
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
}
}
//void nmCalculationAutoFitPSO::updateGlobalBest()
//{
// m_previousBestFitness = m_globalBestFitness;
// bool globalBestUpdated = false;
//
// for(int i = 0; i < m_swarm.size(); ++i) {
// const AutoFitParticle& particle = m_swarm[i];
//
// if(particle.bestFitness < m_globalBestFitness) {
// // 只有显著改进时才更新
// double improvement = (m_globalBestFitness - particle.bestFitness);
// double relativeImprovement = improvement / qMax(1e-10, qAbs(m_globalBestFitness));
//
// if(relativeImprovement > m_improvementThreshold) {
// m_globalBestFitness = particle.bestFitness;
// m_globalBestPosition = particle.bestPosition;
// globalBestUpdated = true;
//
// DEBUG_OUT(QString("Global best updated with %1% improvement: %2")
// .arg(relativeImprovement * 100, 0, 'f', 3)
// .arg(m_globalBestFitness, 0, 'e', 4));
// } else {
// DEBUG_OUT(QString("Rejecting minor improvement: %1% (threshold: %2%)")
// .arg(relativeImprovement * 100, 0, 'f', 4)
// .arg(m_improvementThreshold * 100, 0, 'f', 2));
// }
// }
// }
//
// // 在没有找到更好解时才检查
// if(!globalBestUpdated && m_hasValidUserSolution && m_userInitialFitness < m_globalBestFitness) {
// DEBUG_OUT("Elite protection: No significant improvement found, checking initial solution");
//
// // 检查初始解是否仍然是最优的
// double improvement = m_globalBestFitness - m_userInitialFitness;
// double relativeImprovement = improvement / qMax(1e-10, qAbs(m_globalBestFitness));
//
// if(relativeImprovement > m_improvementThreshold * 0.5) { // 使用更宽松的阈值
// DEBUG_OUT("Elite protection: Restoring user initial solution");
// m_globalBestFitness = m_userInitialFitness;
// m_globalBestPosition = m_userInitialSolution;
// }
// }
//}
void nmCalculationAutoFitPSO::updateVelocityAndPosition()
{
for(int i = 0; i < m_swarm.size(); ++i) {
AutoFitParticle& particle = m_swarm[i];
for(int j = 0; j < particle.position.size(); ++j) {
// PSO速度更新公式
double r1 = random01();
double r2 = random01();
particle.velocity[j] = m_inertiaWeight * particle.velocity[j] +
m_cognitiveParam * r1 * (particle.bestPosition[j] - particle.position[j]) +
m_socialParam * r2 * (m_globalBestPosition[j] - particle.position[j]);
// 速度限制
int paramIndex = m_enabledParamIndices[j];
double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
double maxVel = range * VELOCITY_LIMIT_FACTOR;
particle.velocity[j] = qMax(-maxVel, qMin(maxVel, particle.velocity[j]));
// 更新位置
particle.position[j] += particle.velocity[j];
}
// 边界约束
clampToLimits(particle.position);
}
}
//double nmCalculationAutoFitPSO::evaluateFitness(const QVector<double>& parameters)
//{
// const QString funcName = QString("evaluateError[%1]").arg(m_currentIteration);
// static int callCount = 0;
// callCount++;
//
// try {
// DEBUG_OUT(QString("%1: Call #%2 - Starting evaluation with %3 parameters")
// .arg(funcName).arg(callCount).arg(parameters.size()));
//
// // 打印参数值用于对比
// QString paramStr = "Parameters: ";
//
// for(int i = 0; i < parameters.size(); ++i) {
// paramStr += QString("[%1]=%2 ").arg(i).arg(parameters[i], 0, 'f', 6);
// }
//
// DEBUG_OUT(QString("%1: %2").arg(funcName).arg(paramStr));
//
// // 1. 参数有效性检查
// if(!validateParameters(parameters)) {
// DEBUG_OUT(QString("%1: Call #%2 - Invalid parameters").arg(funcName).arg(callCount));
// return 1e10;
// }
//
// // 2. 检查数据管理器状态
// nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
//
// if(!dataManager) {
// DEBUG_OUT(QString("%1: Call #%2 - DataManager is null").arg(funcName).arg(callCount));
// return 1e10;
// }
//
// // 3. 应用参数到数据管理器
// try {
// DEBUG_OUT(QString("%1: Call #%2 - Applying parameters to DataManager").arg(funcName).arg(callCount));
// applyParametersToDataManager(parameters);
// DEBUG_OUT(QString("%1: Call #%2 - Parameters applied successfully").arg(funcName).arg(callCount));
// } catch(const std::exception& e) {
// DEBUG_OUT(QString("%1: Call #%2 - Failed to apply parameters: %3").arg(funcName).arg(callCount).arg(e.what()));
// return 1e10;
// } catch(...) {
// DEBUG_OUT(QString("%1: Call #%2 - Unknown error applying parameters").arg(funcName).arg(callCount));
// return 1e10;
// }
//
// // 4. 运行求解器
// QVector<QVector<double> > solverResult;
// const int maxRetries = 2;
// bool solverSuccess = false;
//
// for(int retry = 0; retry <= maxRetries; ++retry) {
// if(m_shouldStop) return 1e10;
//
// try {
// DEBUG_OUT(QString("%1: Call #%2 - Solver attempt %3/%4")
// .arg(funcName).arg(callCount).arg(retry + 1).arg(maxRetries + 1));
//
// // 在求解器调用前添加短暂延迟,确保状态稳定
// if(retry > 0) {
// DEBUG_OUT(QString("%1: Call #%2 - Retry delay before solver attempt")
// .arg(funcName).arg(callCount));
// msleep(1000); // 增加延迟时间
// }
//
// solverResult = runSolver();
//
// if(!solverResult.isEmpty() && validateSolverResult(solverResult)) {
// DEBUG_OUT(QString("%1: Call #%2 - Solver successful on attempt %3, result size: %4")
// .arg(funcName).arg(callCount).arg(retry + 1).arg(solverResult[0].size()));
// solverSuccess = true;
// break;
// } else {
// DEBUG_OUT(QString("%1: Call #%2 - Solver failed on attempt %3 - empty or invalid result")
// .arg(funcName).arg(callCount).arg(retry + 1));
//
// if(retry < maxRetries) {
// DEBUG_OUT(QString("%1: Call #%2 - Will retry solver").arg(funcName).arg(callCount));
// }
// }
//
// } catch(const std::exception& e) {
// DEBUG_OUT(QString("%1: Call #%2 - Solver exception on attempt %3: %4")
// .arg(funcName).arg(callCount).arg(retry + 1).arg(e.what()));
// } catch(...) {
// DEBUG_OUT(QString("%1: Call #%2 - Unknown solver exception on attempt %3")
// .arg(funcName).arg(callCount).arg(retry + 1));
// }
// }
//
// if(!solverSuccess) {
// DEBUG_OUT(QString("%1: Call #%2 - All solver attempts failed").arg(funcName).arg(callCount));
// return 1e10;
// }
//
// // 5. 获取双对数结果数据
// QVector<QVector<double> > resultLogLogData;
//
// try {
// //wells = dataManager->getWellDataList(); // 重新获取,确保是最新的
// nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
//
// if(pTargetWell) {
// resultLogLogData = pTargetWell->getResultLogLog();
//
// if(!validateLogLogData(resultLogLogData)) {
// DEBUG_OUT(QString("%1: Call #%2 - Invalid result LogLog data").arg(funcName).arg(callCount));
// return 1e10;
// }
//
// DEBUG_OUT(QString("%1: Call #%2 - LogLog result data obtained, size: %3")
// .arg(funcName).arg(callCount).arg(resultLogLogData[0].size()));
// } else {
// DEBUG_OUT(QString("%1: Call #%2 - No wells found after solver").arg(funcName).arg(callCount));
// return 1e10;
// }
// } catch(const std::exception& e) {
// DEBUG_OUT(QString("%1: Call #%2 - Error getting LogLog result: %3").arg(funcName).arg(callCount).arg(e.what()));
// return 1e10;
// } catch(...) {
// DEBUG_OUT(QString("%1: Call #%2 - Unknown error getting LogLog result").arg(funcName).arg(callCount));
// return 1e10;
// }
//
// // 6. 双对数曲线对齐和误差计算
// double error;
//
// try {
// error = calculateLogLogCurveError(m_targetLogLogData, resultLogLogData);
//
// if(!isFiniteNumber(error) || error < 0) {
// DEBUG_OUT(QString("%1: Call #%2 - Invalid error value: %3").arg(funcName).arg(callCount).arg(error));
// return 1e10;
// }
//
// DEBUG_OUT(QString("%1: Call #%2 - Evaluation successful, error = %3")
// .arg(funcName).arg(callCount).arg(error, 0, 'e', 6));
//
// } catch(const std::exception& e) {
// DEBUG_OUT(QString("%1: Call #%2 - LogLog error calculation failed: %3").arg(funcName).arg(callCount).arg(e.what()));
// return 1e10;
// } catch(...) {
// DEBUG_OUT(QString("%1: Call #%2 - Unknown error in LogLog error calculation").arg(funcName).arg(callCount));
// return 1e10;
// }
//
// return error;
//
// } catch(const std::exception& e) {
// DEBUG_OUT(QString("%1: Call #%2 - Top-level exception: %3").arg(funcName).arg(callCount).arg(e.what()));
// return 1e10;
// } catch(...) {
// DEBUG_OUT(QString("%1: Call #%2 - Unknown top-level exception").arg(funcName).arg(callCount));
// return 1e10;
// }
//}
double nmCalculationAutoFitPSO::evaluateFitness(const QVector<double>& parameters)
{
const QString funcName = QString("evaluateError[%1]").arg(m_currentIteration);
static int callCount = 0;
callCount++;
m_lastEvaluatedLogLogData.clear();
try {
DEBUG_OUT(QString("%1: Call #%2 - Starting evaluation with %3 parameters")
.arg(funcName).arg(callCount).arg(parameters.size()));
// 打印参数值
QString paramStr = "Parameters: ";
for(int i = 0; i < parameters.size(); ++i) {
paramStr += QString("[%1]=%2 ").arg(i).arg(parameters[i], 0, 'f', 6);
}
DEBUG_OUT(QString("%1: %2").arg(funcName).arg(paramStr));
// 1. 参数有效性检查 - 添加详细失败原因
if(!validateParameters(parameters)) {
DEBUG_OUT(QString("%1: Call #%2 - VALIDATION FAILED").arg(funcName).arg(callCount));
// 详细检查每个参数
for(int i = 0; i < parameters.size(); ++i) {
if(!isFiniteNumber(parameters[i])) {
DEBUG_OUT(QString(" -> Param[%1] is NOT finite: %2").arg(i).arg(parameters[i]));
}
if(i < m_enabledParamIndices.size()) {
int paramIndex = m_enabledParamIndices[i];
if(paramIndex >= 0 && paramIndex < m_parameterLower.size()) {
double lower = m_parameterLower[paramIndex];
double upper = m_parameterUpper[paramIndex];
if(parameters[i] < lower) {
DEBUG_OUT(QString(" -> Param[%1]=%2 < lower bound %3")
.arg(i).arg(parameters[i]).arg(lower));
}
if(parameters[i] > upper) {
DEBUG_OUT(QString(" -> Param[%1]=%2 > upper bound %3")
.arg(i).arg(parameters[i]).arg(upper));
}
// 检查危险值
switch(paramIndex) {
case 0: // 渗透率
if(parameters[i] <= 1e-6) {
DEBUG_OUT(QString(" -> REJECTED: Permeability too small: %1").arg(parameters[i]));
}
break;
case 2: // 井筒储集系数
if(parameters[i] <= 1e-8) {
DEBUG_OUT(QString(" -> REJECTED: Wellbore storage too small: %1").arg(parameters[i]));
}
break;
case 3: // 孔隙度
if(parameters[i] <= 1e-4 || parameters[i] >= 0.95) {
DEBUG_OUT(QString(" -> REJECTED: Unrealistic porosity: %1").arg(parameters[i]));
}
break;
}
}
}
}
return 1e10;
}
DEBUG_OUT(QString("%1: Call #%2 - Parameters validated OK").arg(funcName).arg(callCount));
// 2. 数据管理器检查
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
if(!dataManager) {
DEBUG_OUT(QString("%1: Call #%2 - DataManager is NULL").arg(funcName).arg(callCount));
return 1e10;
}
DEBUG_OUT(QString("%1: Call #%2 - DataManager OK").arg(funcName).arg(callCount));
// 3. 应用参数
try {
DEBUG_OUT(QString("%1: Call #%2 - Applying parameters...").arg(funcName).arg(callCount));
applyParametersToDataManager(parameters);
DEBUG_OUT(QString("%1: Call #%2 - Parameters applied successfully").arg(funcName).arg(callCount));
} catch(const std::exception& e) {
DEBUG_OUT(QString("%1: Call #%2 - FAILED to apply parameters: %3")
.arg(funcName).arg(callCount).arg(e.what()));
return 1e10;
}
// 4. 运行求解器
QVector<QVector<double>> solverResult;
const int maxRetries = 2;
bool solverSuccess = false;
for(int retry = 0; retry <= maxRetries; ++retry) {
if(m_shouldStop) {
DEBUG_OUT(QString("%1: Call #%2 - User stop requested").arg(funcName).arg(callCount));
return 1e10;
}
try {
DEBUG_OUT(QString("%1: Call #%2 - Solver attempt %3/%4")
.arg(funcName).arg(callCount).arg(retry + 1).arg(maxRetries + 1));
if(retry > 0) {
DEBUG_OUT(QString("%1: Call #%2 - Retry delay...").arg(funcName).arg(callCount));
msleep(1000);
}
solverResult = runSolver();
// 详细检查求解器结果
if(solverResult.isEmpty()) {
DEBUG_OUT(QString("%1: Call #%2 - Solver returned EMPTY result").arg(funcName).arg(callCount));
} else {
DEBUG_OUT(QString("%1: Call #%2 - Solver returned %3 arrays")
.arg(funcName).arg(callCount).arg(solverResult.size()));
for(int i = 0; i < solverResult.size(); ++i) {
DEBUG_OUT(QString(" -> Array[%1] size: %2").arg(i).arg(solverResult[i].size()));
}
if(validateSolverResult(solverResult)) {
DEBUG_OUT(QString("%1: Call #%2 - Solver result VALIDATED on attempt %3")
.arg(funcName).arg(callCount).arg(retry + 1));
solverSuccess = true;
break;
} else {
DEBUG_OUT(QString("%1: Call #%2 - Solver result VALIDATION FAILED on attempt %3")
.arg(funcName).arg(callCount).arg(retry + 1));
}
}
} catch(const std::exception& e) {
DEBUG_OUT(QString("%1: Call #%2 - Solver EXCEPTION on attempt %3: %4")
.arg(funcName).arg(callCount).arg(retry + 1).arg(e.what()));
}
}
if(!solverSuccess) {
DEBUG_OUT(QString("%1: Call #%2 - ALL SOLVER ATTEMPTS FAILED").arg(funcName).arg(callCount));
return 1e10;
}
// 5. 获取LogLog数据
QVector<QVector<double>> resultLogLogData;
try {
nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
if(!pTargetWell) {
DEBUG_OUT(QString("%1: Call #%2 - Target well '%3' NOT FOUND")
.arg(funcName).arg(callCount).arg(m_targetWellName));
return 1e10;
}
DEBUG_OUT(QString("%1: Call #%2 - Target well found: %3")
.arg(funcName).arg(callCount).arg(m_targetWellName));
resultLogLogData = pTargetWell->getResultLogLog();
if(!validateLogLogData(resultLogLogData)) {
DEBUG_OUT(QString("%1: Call #%2 - LogLog data VALIDATION FAILED")
.arg(funcName).arg(callCount));
// 详细输出LogLog数据问题
if(resultLogLogData.size() < 3) {
DEBUG_OUT(QString(" -> LogLog arrays count: %1 (need 3)")
.arg(resultLogLogData.size()));
} else {
DEBUG_OUT(QString(" -> LogLog array sizes: X=%1, Y1=%2, Y2=%3")
.arg(resultLogLogData[0].size())
.arg(resultLogLogData[1].size())
.arg(resultLogLogData[2].size()));
// 检查数据有效性
for(int i = 0; i < qMin(5, resultLogLogData[0].size()); ++i) {
if(!isFiniteNumber(resultLogLogData[0][i]) ||
!isFiniteNumber(resultLogLogData[1][i]) ||
!isFiniteNumber(resultLogLogData[2][i])) {
DEBUG_OUT(QString(" -> Invalid data at index %1: X=%2, Y1=%3, Y2=%4")
.arg(i)
.arg(resultLogLogData[0][i])
.arg(resultLogLogData[1][i])
.arg(resultLogLogData[2][i]));
}
}
}
return 1e10;
}
DEBUG_OUT(QString("%1: Call #%2 - LogLog data validated, size: %3")
.arg(funcName).arg(callCount).arg(resultLogLogData[0].size()));
} catch(const std::exception& e) {
DEBUG_OUT(QString("%1: Call #%2 - Error getting LogLog data: %3")
.arg(funcName).arg(callCount).arg(e.what()));
return 1e10;
}
// 6. 计算误差
double error;
try {
DEBUG_OUT(QString("%1: Call #%2 - Calculating error...")
.arg(funcName).arg(callCount));
error = calculateLogLogCurveError(m_targetLogLogData, resultLogLogData);
if(!isFiniteNumber(error) || error < 0) {
DEBUG_OUT(QString("%1: Call #%2 - INVALID error value: %3")
.arg(funcName).arg(callCount).arg(error));
return 1e10;
}
DEBUG_OUT(QString("%1: Call #%2 - SUCCESS! Error = %3")
.arg(funcName).arg(callCount).arg(error, 0, 'e', 6));
m_lastEvaluatedLogLogData = resultLogLogData;
} catch(const std::exception& e) {
DEBUG_OUT(QString("%1: Call #%2 - Error calculation FAILED: %3")
.arg(funcName).arg(callCount).arg(e.what()));
return 1e10;
}
return error;
} catch(const std::exception& e) {
DEBUG_OUT(QString("%1: Call #%2 - TOP-LEVEL EXCEPTION: %3")
.arg(funcName).arg(callCount).arg(e.what()));
return 1e10;
} catch(...) {
DEBUG_OUT(QString("%1: Call #%2 - UNKNOWN TOP-LEVEL EXCEPTION")
.arg(funcName).arg(callCount));
return 1e10;
}
}
// ==================== 参数应用方法 ====================
void nmCalculationAutoFitPSO::applyParametersToDataManager(const QVector<double>& parameters)
{
if(parameters.size() != getEnabledParameterCount()) {
return;
}
updateReservoirParameters(parameters);
updateWellParameters(parameters);
}
void nmCalculationAutoFitPSO::updateReservoirParameters(const QVector<double>& parameters)
{
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
nmDataReservoir reservoirData = dataManager->getReservoirDataCopy();
int paramIndex = 0;
for(int i = 0; i < m_parameterSelected.size() && i < 11; ++i) {
if(m_parameterSelected[i] && paramIndex < parameters.size()) {
double value = parameters[paramIndex];
switch(i) {
case 0: // 渗透率
reservoirData.getPermeability().setValue(value);
break;
case 3: // 孔隙度
reservoirData.getPorosity().setValue(value);
break;
case 4: // 初始压力
reservoirData.getInitialPressure().setValue(value);
break;
case 5: // 储层厚度
reservoirData.getThickness().setValue(value);
break;
case 6: // 综合压缩系数
reservoirData.getCt().setValue(value);
break;
case 7: // 岩石压缩系数
reservoirData.getCf().setValue(value);
break;
case 8: // 初始含油饱和度
reservoirData.getSoi().setValue(value);
break;
case 9: // 初始含水饱和度
reservoirData.getSwi().setValue(value);
break;
case 10: // 初始含气饱和度
reservoirData.getSgi().setValue(value);
break;
}
paramIndex++;
}
}
// 更新数据管理器
dataManager->updateReservoirData(reservoirData);
}
void nmCalculationAutoFitPSO::updateWellParameters(const QVector<double>& parameters)
{
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
// 获取井列表,更新第一口井的参数
//QVector<nmDataWellBase*> wells = dataManager->getWellDataList();
nmDataWellBase* pWell = dataManager->findWellByName(m_targetWellName);
if(!pWell) return;
//nmDataWellBase* pWell = wells[0]; // 使用第一口井
int paramIndex = 0;
for(int i = 0; i < m_parameterSelected.size() && i < 9; ++i) {
if(m_parameterSelected[i] && paramIndex < parameters.size()) {
double value = parameters[paramIndex];
switch(i) {
case 1: { // 表皮系数
nmDataPerforation* perf = pWell->getPerforation(0);
if(perf) {
nmDataAttribute skinAttr = perf->getSkin();
skinAttr.setValue(value);
perf->setSkin(skinAttr);
}
}
break;
case 2: { // 井筒储集系数
nmDataAttribute wellboreAttr = pWell->getWellboreStorage();
wellboreAttr.setValue(value);
pWell->setWellboreStorage(wellboreAttr);
}
break;
}
paramIndex++;
}
}
// 根据井类型更新到数据管理器
updateWellToDataManager(pWell);
}
void nmCalculationAutoFitPSO::updateWellToDataManager(nmDataWellBase* pWell)
{
if(!pWell) return;
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
NM_WELL_MODEL wellType = pWell->getWellType();
switch(wellType) {
case NM_WELL_MODEL::Vertical_Well: {
nmDataVerticalWell* pVerticalWell = dynamic_cast<nmDataVerticalWell*>(pWell);
if(pVerticalWell) {
QVector<nmDataVerticalWell> wells;
wells.append(*pVerticalWell);
dataManager->updateVerticalWells(wells);
}
break;
}
case NM_WELL_MODEL::Vertical_Fractured_Well: {
nmDataVerticalFracturedWell* pVFracturedWell = dynamic_cast<nmDataVerticalFracturedWell*>(pWell);
if(pVFracturedWell) {
QVector<nmDataVerticalFracturedWell> wells;
wells.append(*pVFracturedWell);
dataManager->updateVerticalFracturedWells(wells);
}
break;
}
case NM_WELL_MODEL::Horizontal_Fractured_Well: {
nmDataHorizontalFracturedWell* pHFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell*>(pWell);
if(pHFracturedWell) {
QVector<nmDataHorizontalFracturedWell> wells;
wells.append(*pHFracturedWell);
dataManager->updateHorizontalFracturedWells(wells);
}
break;
}
default:
break;
}
}
// ==================== 求解器相关方法 ====================
QVector<QVector<double>> nmCalculationAutoFitPSO::runSolver()
{
//return runSolverExe();
return runSolverDll();
}
// ==================== 数据处理方法 ====================
QVector<QPointF> nmCalculationAutoFitPSO::interpolateData(
const QVector<QPointF>& source, const QVector<double>& targetX) const
{
QVector<QPointF> result;
if(source.isEmpty() || targetX.isEmpty()) {
DEBUG_OUT("Warning: Empty data in interpolation");
return result;
}
// 数据清理和验证合并
QVector<QPointF> validSource;
const double MAX_REASONABLE_VALUE = 1e12;
for(int i = 0; i < source.size(); ++i) {
const QPointF& point = source[i];
// 检查数值有效性
if(!isFiniteNumber(point.x()) || !isFiniteNumber(point.y())) {
DEBUG_OUT(QString("Skipping invalid data point at index %1: X=%2, Y=%3")
.arg(i).arg(point.x()).arg(point.y()));
continue;
}
// 检查极大值 - 跳过而不是失败
if(qAbs(point.y()) > MAX_REASONABLE_VALUE) {
DEBUG_OUT(QString("Skipping extremely large Y value at index %1: %2")
.arg(i).arg(point.y()));
continue;
}
// 只保留有效的数据点
validSource.append(point);
}
// 检查清理后的数据是否足够
if(validSource.size() < 3) {
DEBUG_OUT(QString("Insufficient valid data points after cleaning: %1")
.arg(validSource.size()));
return result; // 返回空结果,但不算失败
}
DEBUG_OUT(QString("Data cleaning: %1 -> %2 valid points")
.arg(source.size()).arg(validSource.size()));
// 对清理后的数据进行排序
QVector<QPointF> sortedSource = validSource;
for(int i = 0; i < sortedSource.size() - 1; ++i) {
for(int j = 0; j < sortedSource.size() - 1 - i; ++j) {
if(sortedSource[j].x() > sortedSource[j + 1].x()) {
QPointF temp = sortedSource[j];
sortedSource[j] = sortedSource[j + 1];
sortedSource[j + 1] = temp;
}
}
}
double sourceMinX = sortedSource.first().x();
double sourceMaxX = sortedSource.last().x();
double targetMinX = targetX[0];
double targetMaxX = targetX[0];
for(int i = 1; i < targetX.size(); ++i) {
if(targetX[i] < targetMinX) targetMinX = targetX[i];
if(targetX[i] > targetMaxX) targetMaxX = targetX[i];
}
DEBUG_OUT(QString("Source X range: [%1, %2], Target X range: [%3, %4]")
.arg(sourceMinX).arg(sourceMaxX).arg(targetMinX).arg(targetMaxX));
// 插值算法
for(int i = 0; i < targetX.size(); ++i) {
double x = targetX[i];
if(!isFiniteNumber(x)) continue;
double y = 0.0;
// 插值逻辑
if(x <= sourceMinX) {
if(sortedSource.size() >= 2) {
double dx = sortedSource[1].x() - sortedSource[0].x();
if(qAbs(dx) > 1e-10) {
double slope = (sortedSource[1].y() - sortedSource[0].y()) / dx;
slope = qMax(-1e6, qMin(1e6, slope));
y = sortedSource[0].y() + slope * (x - sortedSource[0].x());
} else {
y = sortedSource[0].y();
}
} else {
y = sortedSource[0].y();
}
} else if(x >= sourceMaxX) {
if(sortedSource.size() >= 2) {
int lastIdx = sortedSource.size() - 1;
double dx = sortedSource[lastIdx].x() - sortedSource[lastIdx - 1].x();
if(qAbs(dx) > 1e-10) {
double slope = (sortedSource[lastIdx].y() - sortedSource[lastIdx - 1].y()) / dx;
slope = qMax(-1e6, qMin(1e6, slope));
y = sortedSource[lastIdx].y() + slope * (x - sortedSource[lastIdx].x());
} else {
y = sortedSource[lastIdx].y();
}
} else {
y = sortedSource.last().y();
}
} else {
// 内插
bool found = false;
for(int j = 0; j < sortedSource.size() - 1; ++j) {
if(x >= sortedSource[j].x() && x <= sortedSource[j + 1].x()) {
double dx = sortedSource[j + 1].x() - sortedSource[j].x();
if(qAbs(dx) > 1e-10) {
double ratio = (x - sortedSource[j].x()) / dx;
y = sortedSource[j].y() + ratio * (sortedSource[j + 1].y() - sortedSource[j].y());
} else {
y = sortedSource[j].y();
}
found = true;
break;
}
}
if(!found) {
// 使用最近点
double minDist = 1e10;
for(int k = 0; k < sortedSource.size(); ++k) {
double dist = qAbs(sortedSource[k].x() - x);
if(dist < minDist) {
minDist = dist;
y = sortedSource[k].y();
}
}
}
}
// 最终数值检查
if(!isFiniteNumber(y)) {
y = sortedSource.size() > 0 ? sortedSource[0].y() : 1.0;
}
y = qMax(-1e12, qMin(1e12, y));
result.append(QPointF(x, y));
}
if(result.isEmpty()) {
DEBUG_OUT("LogLog interpolation failed");
return result;
}
DEBUG_OUT(QString("Interpolation completed: %1 -> %2 points")
.arg(validSource.size()).arg(result.size()));
return result;
}
// ==================== 算法辅助方法 ====================
void nmCalculationAutoFitPSO::adaptiveParameterUpdate(int iteration)
{
double progress = static_cast<double>(iteration) / m_maxIterations;
// 更快降低惯性权重,增强局部搜索
m_inertiaWeight = 0.4 - 0.25 * progress;
if(iteration > 0) {
double improvement = m_previousBestFitness - m_globalBestFitness;
double relativeImprovement = improvement / qMax(1e-10, qAbs(m_previousBestFitness));
if(relativeImprovement < 1e-5) {
// 改进很小时,增强局部搜索
m_cognitiveParam = qMin(2.5, m_cognitiveParam * 1.05);
m_socialParam = qMax(0.8, m_socialParam * 0.95);
} else {
// 有明显改进时,保持平衡
m_cognitiveParam = qMax(1.5, qMin(2.2, m_cognitiveParam));
m_socialParam = qMax(1.0, qMin(1.5, m_socialParam));
}
}
}
void nmCalculationAutoFitPSO::saveOptimizationResult()
{
DEBUG_OUT(QString("Optimization result: error=%1, evaluations=%2/%3")
.arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_successfulEvaluations)
.arg(m_totalEvaluations));
}
void nmCalculationAutoFitPSO::validateAndProtectFinalResult()
{
if(!m_hasValidUserSolution) {
emit logMessageGenerated(tr("No initial solution for elite protection"));
return;
}
emit logMessageGenerated(tr("=== Final Result Validation (Elite Protection) ==="));
// 使用已有的评估结果
double finalFitness = m_globalBestFitness;
double initialFitness = m_userInitialFitness;
emit logMessageGenerated(tr("Comparing results: Initial=%1, Final=%2")
.arg(initialFitness, 0, 'e', 4).arg(finalFitness, 0, 'e', 4));
// 计算改进程度
double improvement = initialFitness - finalFitness;
double relativeImprovement = improvement / qMax(1e-10, qAbs(initialFitness));
emit logMessageGenerated(tr("Improvement: %1 (%2%)")
.arg(improvement, 0, 'e', 4).arg(relativeImprovement * 100, 0, 'f', 2));
if(relativeImprovement < m_improvementThreshold) {
emit logMessageGenerated(tr("Elite protection triggered: insufficient improvement"));
emit logMessageGenerated(tr("Threshold: %1%, Actual: %2%")
.arg(m_improvementThreshold * 100, 0, 'f', 2)
.arg(relativeImprovement * 100, 0, 'f', 4));
emit logMessageGenerated(tr("Restoring initial solution as final result"));
m_globalBestFitness = initialFitness;
m_globalBestPosition = m_userInitialSolution;
m_globalBestLogLogData = m_userInitialLogLogData;
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
emit logMessageGenerated(tr("Initial solution restored successfully"));
} else {
emit logMessageGenerated(tr("Final result validated - significant improvement achieved"));
}
}
// ==================== 工具方法 ====================
double nmCalculationAutoFitPSO::random01() const
{
return static_cast<double>(qrand()) / RAND_MAX;
}
void nmCalculationAutoFitPSO::clampToLimits(QVector<double>& parameters) const
{
for(int i = 0; i < parameters.size() && i < m_enabledParamIndices.size(); ++i) {
int paramIndex = m_enabledParamIndices[i];
if(paramIndex >= 0 && paramIndex < m_parameterLower.size()) {
parameters[i] = qMax(m_parameterLower[paramIndex],
qMin(m_parameterUpper[paramIndex], parameters[i]));
}
}
}
int nmCalculationAutoFitPSO::getEnabledParameterCount() const
{
int count = 0;
for(int i = 0; i < m_parameterSelected.size(); ++i) {
if(m_parameterSelected[i]) count++;
}
return count;
}
// ==================== 验证和处理方法 ====================
bool nmCalculationAutoFitPSO::validateParameters(const QVector<double>& parameters) const
{
if(parameters.size() != getEnabledParameterCount()) {
return false;
}
for(int i = 0; i < parameters.size(); ++i) {
if(!isFiniteNumber(parameters[i])) {
return false;
}
// 检查参数范围
if(i < m_enabledParamIndices.size()) {
int paramIndex = m_enabledParamIndices[i];
if(paramIndex >= 0 && paramIndex < m_parameterLower.size()) {
if(parameters[i] < m_parameterLower[paramIndex] ||
parameters[i] > m_parameterUpper[paramIndex]) {
return false;
}
}
}
}
// 直接拦截会导致求解器数值崩溃的参数值
for(int i = 0; i < parameters.size() && i < m_enabledParamIndices.size(); ++i) {
int paramIndex = m_enabledParamIndices[i];
double value = parameters[i];
switch(paramIndex) {
case 0: // 渗透率:必须大于零
if(value <= 1e-8) {
DEBUG_OUT(QString("Rejecting near-zero permeability: %1").arg(value));
return false;
}
break;
case 2: // 井筒储集系数:必须大于零
if(value <= 1e-10) {
DEBUG_OUT(QString("Rejecting near-zero wellbore storage: %1").arg(value));
return false;
}
break;
case 3: // 孔隙度:必须在合理范围
if(value <= 1e-6 || value >= 0.99) {
DEBUG_OUT(QString("Rejecting unrealistic porosity: %1").arg(value));
return false;
}
break;
case 6: // 综合压缩系数:必须大于零
if(value <= 1e-8) {
DEBUG_OUT(QString("Rejecting near-zero total compressibility: %1").arg(value));
return false;
}
break;
}
}
return true;
}
bool nmCalculationAutoFitPSO::validateLogLogData(const QVector<QVector<double>>& logLogData) const
{
// 检查基本结构
if(logLogData.size() < 3) {
DEBUG_OUT("LogLog data has less than 3 arrays");
return false;
}
// 检查数组大小一致性
int size = logLogData[0].size();
if(size == 0) {
DEBUG_OUT("Empty LogLog data");
return false;
}
if(logLogData[1].size() != size || logLogData[2].size() != size) {
DEBUG_OUT(QString("LogLog data size mismatch: X=%1, Y1=%2, Y2=%3")
.arg(logLogData[0].size())
.arg(logLogData[1].size())
.arg(logLogData[2].size()));
return false;
}
// 检查最小数据点数
if(size < 5) {
DEBUG_OUT(QString("Too few LogLog data points: %1").arg(size));
return false;
}
// 数据有效性检查
for(int i = 0; i < size; ++i) {
if(!isFiniteNumber(logLogData[0][i]) ||
!isFiniteNumber(logLogData[1][i]) ||
!isFiniteNumber(logLogData[2][i])) {
DEBUG_OUT(QString("Invalid LogLog data at index %1").arg(i));
return false;
}
}
return true;
}
bool nmCalculationAutoFitPSO::validateInitialValues() const
{
if(m_initialValues.size() != m_enabledParamIndices.size()) {
DEBUG_OUT("Initial values count mismatch with enabled parameters");
return false;
}
bool allValid = true;
for(int i = 0; i < m_initialValues.size(); ++i) {
int paramIndex = m_enabledParamIndices[i];
double value = m_initialValues[i];
if(!isFiniteNumber(value)) {
DEBUG_OUT(QString("Initial value[%1] is not finite: %2").arg(i).arg(value));
allValid = false;
continue;
}
if(paramIndex < m_parameterLower.size() && paramIndex < m_parameterUpper.size()) {
double minVal = m_parameterLower[paramIndex];
double maxVal = m_parameterUpper[paramIndex];
if(value < minVal || value > maxVal) {
DEBUG_OUT(QString("Initial value[%1] = %2 is outside bounds [%3, %4]")
.arg(i).arg(value).arg(minVal).arg(maxVal));
allValid = false;
}
}
}
return allValid;
}
bool nmCalculationAutoFitPSO::validateSolverResult(const QVector<QVector<double>>& result) const
{
// 基本检查
if(result.size() < 2) {
DEBUG_OUT("Solver result has less than 2 arrays");
return false;
}
if(result[0].size() != result[1].size()) {
DEBUG_OUT(QString("Size mismatch: X=%1, Y=%2").arg(result[0].size()).arg(result[1].size()));
return false;
}
if(result[0].size() == 0) {
DEBUG_OUT("Empty solver result");
return false;
}
// 检查最小数据点数
if(result[0].size() < 10) {
DEBUG_OUT(QString("Too few data points: %1").arg(result[0].size()));
return false;
}
// 数据有效性检查
for(int i = 0; i < result[0].size(); ++i) {
if(!isFiniteNumber(result[0][i]) || !isFiniteNumber(result[1][i])) {
DEBUG_OUT(QString("Invalid data at index %1: X=%2, Y=%3")
.arg(i).arg(result[0][i]).arg(result[1][i]));
return false;
}
}
// 检查X值单调性
bool isMonotonic = true;
for(int i = 1; i < result[0].size(); ++i) {
if(result[0][i] <= result[0][i - 1]) {
isMonotonic = false;
break;
}
}
if(!isMonotonic) {
DEBUG_OUT("X values are not monotonically increasing");
}
return true;
}
double nmCalculationAutoFitPSO::calculateCurveError(
const QVector<QPointF>& curve1, const QVector<QPointF>& curve2) const
{
if(curve1.size() != curve2.size() || curve1.isEmpty()) {
return 1e10;
}
// 预检查:确保没有无穷大值
for(int i = 0; i < curve1.size(); ++i) {
if(!isFiniteNumber(curve1[i].y()) || !isFiniteNumber(curve2[i].y())) {
DEBUG_OUT(QString("Infinite value detected at index %1: Y1=%2, Y2=%3")
.arg(i).arg(curve1[i].y()).arg(curve2[i].y()));
return 1e10;
}
if(qAbs(curve1[i].y()) > 1e12 || qAbs(curve2[i].y()) > 1e12) {
DEBUG_OUT(QString("Extremely large value detected at index %1")
.arg(i));
return 1e10;
}
}
double totalError = 0.0;
double totalWeight = 0.0;
int validPoints = 0;
for(int i = 0; i < curve1.size(); ++i) {
double y1 = curve1[i].y();
double y2 = curve2[i].y();
// 跳过异常值
if(!isFiniteNumber(y1) || !isFiniteNumber(y2)) {
continue;
}
// 自适应权重根据Y值大小调整添加上限
double weightFactor = qMin(100.0, qAbs(y1) * 0.01);
double weight = 1.0 / (1.0 + weightFactor);
// 相对误差和绝对误差的组合 - 添加数值保护
double yMax = qMax(qAbs(y1), qAbs(y2));
yMax = qMax(1e-12, yMax); // 防止除零
double relativeError = qAbs(y1 - y2) / yMax;
double absoluteError = qAbs(y1 - y2);
// 限制误差值避免爆炸
relativeError = qMin(1e6, relativeError);
absoluteError = qMin(1e6, absoluteError);
// 误差组合:相对误差为主,绝对误差为辅
double pointError = 0.7 * relativeError + 0.3 * absoluteError;
if(isFiniteNumber(pointError) && pointError < 1e10) {
totalError += weight * pointError * pointError;
totalWeight += weight;
validPoints++;
}
}
if(totalWeight > 0 && validPoints > 0) {
double result = sqrt(totalError / totalWeight);
// 最终检查
if(!isFiniteNumber(result)) {
DEBUG_OUT("Final error calculation produced infinite result");
return 1e10;
}
return qMin(1e9, result); // 限制最大误差值
} else {
DEBUG_OUT(QString("No valid points for error calculation: validPoints=%1")
.arg(validPoints));
return 1e10;
}
}
double nmCalculationAutoFitPSO::calculateLogLogCurveError(
const QVector<QVector<double> >& target,
const QVector<QVector<double> >& result) const
{
// 验证数据
if(!validateLogLogData(target) || !validateLogLogData(result)) {
return 1e10;
}
try {
// 数据对齐找到X值的重叠区域
double targetMinX = target[0][0];
double targetMaxX = target[0][0];
for(int i = 1; i < target[0].size(); ++i) {
if(target[0][i] < targetMinX) targetMinX = target[0][i];
if(target[0][i] > targetMaxX) targetMaxX = target[0][i];
}
double resultMinX = result[0][0];
double resultMaxX = result[0][0];
for(int i = 1; i < result[0].size(); ++i) {
if(result[0][i] < resultMinX) resultMinX = result[0][i];
if(result[0][i] > resultMaxX) resultMaxX = result[0][i];
}
double overlapMinX = qMax(targetMinX, resultMinX);
double overlapMaxX = qMin(targetMaxX, resultMaxX);
if(overlapMinX >= overlapMaxX) {
DEBUG_OUT("No overlap between target and result LogLog curves");
return 1e10;
}
// 生成公共X网格进行插值
QVector<double> commonX;
int numPoints = 50;
if(overlapMinX > 0 && overlapMaxX > 0) {
// 对数空间均匀分布
double logMin = qLn(overlapMinX);
double logMax = qLn(overlapMaxX);
for(int i = 0; i < numPoints; ++i) {
double logX = logMin + i * (logMax - logMin) / (numPoints - 1);
double x = qExp(logX);
// 数值保护
if(!isFiniteNumber(x) || x <= 0) {
continue;
}
commonX.append(x);
}
DEBUG_OUT("Using log-uniform grid for better early-time coverage");
}
if(commonX.isEmpty()) {
DEBUG_OUT("Failed to generate common X grid");
return 1e10;
}
// 插值目标曲线
QVector<QPointF> targetCurve1, targetCurve2;
for(int i = 0; i < target[0].size(); ++i) {
// 检查数据有效性
if(isFiniteNumber(target[0][i]) && isFiniteNumber(target[1][i]) &&
isFiniteNumber(target[2][i])) {
targetCurve1.append(QPointF(target[0][i], target[1][i]));
targetCurve2.append(QPointF(target[0][i], target[2][i]));
}
}
if(targetCurve1.isEmpty() || targetCurve2.isEmpty()) {
DEBUG_OUT("Target curves are empty after filtering");
return 1e10;
}
QVector<QPointF> alignedTarget1 = interpolateData(targetCurve1, commonX);
QVector<QPointF> alignedTarget2 = interpolateData(targetCurve2, commonX);
// 插值结果曲线
QVector<QPointF> resultCurve1, resultCurve2;
for(int i = 0; i < result[0].size(); ++i) {
// 检查数据有效性
if(isFiniteNumber(result[0][i]) && isFiniteNumber(result[1][i]) &&
isFiniteNumber(result[2][i])) {
resultCurve1.append(QPointF(result[0][i], result[1][i]));
resultCurve2.append(QPointF(result[0][i], result[2][i]));
}
}
if(resultCurve1.isEmpty() || resultCurve2.isEmpty()) {
DEBUG_OUT("Result curves are empty after filtering");
return 1e10;
}
QVector<QPointF> alignedResult1 = interpolateData(resultCurve1, commonX);
QVector<QPointF> alignedResult2 = interpolateData(resultCurve2, commonX);
// 检查插值结果
if(alignedTarget1.isEmpty() || alignedTarget2.isEmpty() ||
alignedResult1.isEmpty() || alignedResult2.isEmpty()) {
DEBUG_OUT("LogLog interpolation failed");
return 1e10;
}
if(alignedTarget1.size() != alignedResult1.size() ||
alignedTarget2.size() != alignedResult2.size()) {
DEBUG_OUT("LogLog interpolation size mismatch");
return 1e10;
}
// 计算两条曲线的误差
double error1 = calculateCurveError(alignedTarget1, alignedResult1);
double error2 = calculateCurveError(alignedTarget2, alignedResult2);
// 检查个别误差是否有效
if(!isFiniteNumber(error1) || error1 > 1e9) {
DEBUG_OUT(QString("Curve1 error is invalid: %1").arg(error1));
error1 = 1e10;
}
if(!isFiniteNumber(error2) || error2 > 1e9) {
DEBUG_OUT(QString("Curve2 error is invalid: %1").arg(error2));
error2 = 1e10;
}
// 组合误差 - 添加保护
double combinedError;
if(error1 > 1e9 && error2 > 1e9) {
combinedError = 1e10;
} else if(error1 > 1e9) {
combinedError = error2;
} else if(error2 > 1e9) {
combinedError = error1;
} else {
combinedError = 0.5 * error1 + 0.5 * error2;
}
DEBUG_OUT(QString("LogLog errors: Curve1=%1, Curve2=%2, Combined=%3")
.arg(error1, 0, 'e', 4).arg(error2, 0, 'e', 4).arg(combinedError, 0, 'e', 4));
return qMin(1e9, combinedError);
} catch(const std::exception& e) {
DEBUG_OUT(QString("Exception in LogLog error calculation: %1").arg(e.what()));
return 1e10;
} catch(...) {
DEBUG_OUT("Unknown exception in LogLog error calculation");
return 1e10;
}
}
double nmCalculationAutoFitPSO::calculateWeightedPointError(
double target, double result, double timeWeight) const
{
if(!isFiniteNumber(target) || !isFiniteNumber(result)) {
return 1e10;
}
// 对数空间的相对误差
double logTarget = qLn(qMax(1e-10, qAbs(target)));
double logResult = qLn(qMax(1e-10, qAbs(result)));
double logError = qAbs(logTarget - logResult);
// 线性空间的相对误差
double yMax = qMax(qAbs(target), qAbs(result));
double relativeError = qAbs(target - result) / qMax(1e-10, yMax);
// 组合误差对数误差占70%线性误差占30%
return 0.7 * logError + 0.3 * relativeError;
}
QVector<QVector<double>> nmCalculationAutoFitPSO::runSolverDll()
{
DEBUG_OUT("SOLVER DLL START");
if(m_evaluationInProgress > 0) {
DEBUG_OUT("DLL Solver already running, skipping");
return QVector<QVector<double>>();
}
++m_evaluationInProgress;
QVector<QVector<double>> result;
nmCalculationDllPebiSolverTask* dllTask = nullptr;
try {
DEBUG_OUT("Creating DLL solver task");
dllTask = new nmCalculationDllPebiSolverTask(m_tempDirectory);
if(m_shouldStop) {
DEBUG_OUT("Should stop - cleaning up and returning empty result");
delete dllTask;
--m_evaluationInProgress;
return result;
}
DEBUG_OUT("Starting DLL solver execution...");
// 异步执行
dllTask->start();
// 等待完成
int waitTime = 0;
const int maxWait = 3600000; // 1h超时
const int checkInterval = 500;
while(waitTime < maxWait) {
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 100);
if(!dllTask->isRunning()) {
DEBUG_OUT("DLL solver task completed");
break;
}
if(m_shouldStop) {
DEBUG_OUT("DLL solver task terminated by user");
dllTask->terminate();
break;
}
msleep(checkInterval);
waitTime += checkInterval;
}
// 超时处理
if(dllTask->isRunning()) {
DEBUG_OUT("DLL solver task timeout, terminating...");
dllTask->terminate();
dllTask->wait(2000);
delete dllTask;
dllTask = nullptr;
--m_evaluationInProgress;
m_consecutiveFailures++;
return result;
}
// 验证结果数据是否已更新
nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
//QVector<nmDataWellBase*> wells = dataManager->getWellDataList();
nmDataWellBase* pTargetWell = dataManager->findWellByName(m_targetWellName);
if(!pTargetWell) {
DEBUG_OUT("No wells found in data manager after DLL execution");
delete dllTask;
--m_evaluationInProgress;
return result;
}
// 验证结果数据
QVector<QVector<double>> pressureResult = pTargetWell->getResultPressure();
QVector<QVector<double>> logLogResult = pTargetWell->getResultLogLog();
DEBUG_OUT(QString("DLL result verification - Pressure arrays: %1, LogLog arrays: %2")
.arg(pressureResult.size()).arg(logLogResult.size()));
if(pressureResult.size() >= 2) {
DEBUG_OUT(QString("Pressure result - Time points: %1, Pressure points: %2")
.arg(pressureResult[0].size()).arg(pressureResult[1].size()));
if(pressureResult[0].size() > 0) {
DEBUG_OUT(QString("Sample pressure data - Time[0]: %1, Time[last]: %2, P[0]: %3, P[last]: %4")
.arg(pressureResult[0][0])
.arg(pressureResult[0][pressureResult[0].size() - 1])
.arg(pressureResult[1][0])
.arg(pressureResult[1][pressureResult[1].size() - 1]));
}
}
// 数据有效性检查
if(pressureResult.size() >= 2 && pressureResult[0].size() > 0 && pressureResult[1].size() > 0) {
result = pressureResult;
DEBUG_OUT(QString("Got DLL solver result: %1 points").arg(result[0].size()));
m_consecutiveFailures = 0;
// 检查结果是否与之前不同
static QVector<double> lastPressureResult;
bool isDifferentFromLast = false;
if(lastPressureResult.isEmpty() || lastPressureResult.size() != pressureResult[1].size()) {
isDifferentFromLast = true;
} else {
for(int i = 0; i < qMin(5, pressureResult[1].size()); ++i) {
if(qAbs(lastPressureResult[i] - pressureResult[1][i]) > 1e-12) {
isDifferentFromLast = true;
break;
}
}
}
if(isDifferentFromLast) {
DEBUG_OUT("RESULT VERIFICATION: Got NEW result data from DLL");
lastPressureResult = pressureResult[1];
} else {
DEBUG_OUT("!!! WARNING: Result data appears to be identical to previous run !!!");
}
} else {
DEBUG_OUT("DLL solver result is empty or invalid");
DEBUG_OUT(QString("Pressure result size: %1, Array sizes: %2, %3")
.arg(pressureResult.size())
.arg(pressureResult.size() > 0 ? pressureResult[0].size() : 0)
.arg(pressureResult.size() > 1 ? pressureResult[1].size() : 0));
m_consecutiveFailures++;
}
} catch(const std::bad_alloc& e) {
DEBUG_OUT(QString("Memory allocation failed in DLL solver: %1").arg(e.what()));
m_consecutiveFailures++;
} catch(const std::exception& e) {
DEBUG_OUT(QString("Exception in DLL solver: %1").arg(e.what()));
m_consecutiveFailures++;
} catch(...) {
DEBUG_OUT("Unknown exception in DLL solver");
m_consecutiveFailures++;
}
// 清理DLL任务
if(dllTask) {
if(dllTask->isRunning()) {
dllTask->terminate();
dllTask->wait(3000);
}
DEBUG_OUT("Cleaning up DLL solver task...");
delete dllTask;
dllTask = nullptr;
}
QApplication::processEvents(QEventLoop::AllEvents, 100);
--m_evaluationInProgress;
DEBUG_OUT(QString("SOLVER DLL END - ResultPoints: %1")
.arg(result.isEmpty() ? 0 : result[0].size()));
return result;
}
//QVector<QVector<double>> nmCalculationAutoFitPSO::runSolverExe()
//{
// DEBUG_OUT("SOLVER EXE START");
//
// if(m_evaluationInProgress > 0) {
// DEBUG_OUT("EXE Solver already running, skipping");
// return QVector<QVector<double>>();
// }
//
// ++m_evaluationInProgress;
// QVector<QVector<double>> result;
// nmCalculationExeSolverTask* exeTask = nullptr;
//
// try {
// DEBUG_OUT("Creating EXE solver task");
// exeTask = new nmCalculationExeSolverTask(QString());
//
// if(m_shouldStop) {
// DEBUG_OUT("Should stop - cleaning up and returning empty result");
// delete exeTask;
// --m_evaluationInProgress;
// return result;
// }
//
// DEBUG_OUT("Starting EXE solver execution...");
//
// // 启动
// bool executeSuccess = exeTask->execute();
//
// // 检查执行状态
// if(!executeSuccess) {
// DEBUG_OUT(QString("EXE solver execution failed: %1").arg(exeTask->getLastError()));
// DEBUG_OUT(QString("EXE solver exit code: %1").arg(exeTask->getExitCode()));
//
// // 清理并返回空结果
// delete exeTask;
// exeTask = nullptr;
// --m_evaluationInProgress;
// m_consecutiveFailures++;
// return result;
// }
//
// DEBUG_OUT("EXE solver execution completed successfully");
//
// // 验证结果数据是否已更新
// nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance();
// QVector<nmDataWellBase*> wells = dataManager->getWellDataList();
//
// if(wells.isEmpty()) {
// DEBUG_OUT("No wells found in data manager after EXE execution");
// delete exeTask;
// --m_evaluationInProgress;
// return result;
// }
//
// // 验证结果数据的时间戳或唯一性
// QVector<QVector<double>> pressureResult = wells[0]->getResultPressure();
// QVector<QVector<double>> logLogResult = wells[0]->getResultLogLog();
//
// DEBUG_OUT(QString("Raw result verification - Pressure arrays: %1, LogLog arrays: %2")
// .arg(pressureResult.size()).arg(logLogResult.size()));
//
// if(pressureResult.size() >= 2) {
// DEBUG_OUT(QString("Pressure result - Time points: %1, Pressure points: %2")
// .arg(pressureResult[0].size()).arg(pressureResult[1].size()));
//
// if(pressureResult[0].size() > 0) {
// DEBUG_OUT(QString("Sample pressure data - Time[0]: %1, Time[last]: %2, P[0]: %3, P[last]: %4")
// .arg(pressureResult[0][0])
// .arg(pressureResult[0][pressureResult[0].size()-1])
// .arg(pressureResult[1][0])
// .arg(pressureResult[1][pressureResult[1].size()-1]));
// }
// }
//
// if(logLogResult.size() >= 3) {
// DEBUG_OUT(QString("LogLog result - X points: %1, Y1 points: %2, Y2 points: %3")
// .arg(logLogResult[0].size()).arg(logLogResult[1].size()).arg(logLogResult[2].size()));
//
// if(logLogResult[0].size() > 0) {
// DEBUG_OUT(QString("Sample LogLog data - X[0]: %1, X[last]: %2, Y1[0]: %3, Y2[0]: %4")
// .arg(logLogResult[0][0])
// .arg(logLogResult[0][logLogResult[0].size()-1])
// .arg(logLogResult[1][0])
// .arg(logLogResult[2][0]));
// }
// }
//
// // 数据有效性检查
// if(pressureResult.size() >= 2 && pressureResult[0].size() > 0 && pressureResult[1].size() > 0) {
// result = pressureResult;
// DEBUG_OUT(QString("Got EXE solver result: %1 points").arg(result[0].size()));
// m_consecutiveFailures = 0;
//
// // 检查结果是否与之前不同
// static QVector<double> lastPressureResult;
// bool isDifferentFromLast = false;
//
// if(lastPressureResult.isEmpty() || lastPressureResult.size() != pressureResult[1].size()) {
// isDifferentFromLast = true;
// } else {
// for(int i = 0; i < qMin(5, pressureResult[1].size()); ++i) {
// if(qAbs(lastPressureResult[i] - pressureResult[1][i]) > 1e-12) {
// isDifferentFromLast = true;
// break;
// }
// }
// }
//
// if(isDifferentFromLast) {
// DEBUG_OUT("RESULT VERIFICATION: Got NEW result data from EXE");
// lastPressureResult = pressureResult[1];
// } else {
// DEBUG_OUT("!!! WARNING: Result data appears to be identical to previous run !!!");
// }
//
// } else {
// DEBUG_OUT("EXE solver result is empty or invalid");
// DEBUG_OUT(QString("Pressure result size: %1, Array sizes: %2, %3")
// .arg(pressureResult.size())
// .arg(pressureResult.size() > 0 ? pressureResult[0].size() : 0)
// .arg(pressureResult.size() > 1 ? pressureResult[1].size() : 0));
// m_consecutiveFailures++;
// }
//
// } catch(const std::exception& e) {
// DEBUG_OUT(QString("Exception in EXE solver: %1").arg(e.what()));
// m_consecutiveFailures++;
// } catch(...) {
// DEBUG_OUT("Unknown exception in EXE solver");
// m_consecutiveFailures++;
// }
//
// // 清理EXE任务
// if(exeTask) {
// DEBUG_OUT("Cleaning up EXE solver task...");
// delete exeTask;
// exeTask = nullptr;
// }
//
// QApplication::processEvents(QEventLoop::AllEvents, 100);
// --m_evaluationInProgress;
//
// DEBUG_OUT(QString("SOLVER EXE END - ResultPoints: %1")
// .arg(result.isEmpty() ? 0 : result[0].size()));
//
// return result;
//}
StopReasonPSO nmCalculationAutoFitPSO::analyzeOptimizationStatus()
{
// 1. 检查用户停止
if(m_shouldStop) {
return PSO_USER_STOPPED;
}
// 2. 检查连续失败
if(m_consecutiveFailedIterations >= m_maxConsecutiveFailures) {
return PSO_CONSECUTIVE_FAILURES;
}
// 3. 检查是否达到目标精度
if(m_globalBestFitness < m_targetError) {
return PSO_TARGET_ACHIEVED;
}
// 4. 检查是否达到最大迭代数
if(m_currentIteration >= m_maxIterations - 1) {
return PSO_MAX_ITERATIONS;
}
// 5. 需要足够的历史数据才能判断收敛
if(m_convergenceHistory.size() < m_localOptimumWindow) {
return PSO_CONTINUE_OPTIMIZATION;
}
// 6. 检查真正的收敛
if(m_convergenceHistory.size() >= m_trueConvergenceWindow && checkTrueConvergence()) {
return PSO_TRUE_CONVERGENCE;
}
// 7. 检查局部最优陷阱
if(checkLocalOptimumTrap()) {
return PSO_LOCAL_OPTIMUM;
}
return PSO_CONTINUE_OPTIMIZATION;
}
bool nmCalculationAutoFitPSO::checkTrueConvergence() const
{
if(m_convergenceHistory.size() < m_trueConvergenceWindow) {
return false;
}
// 1. 检查解质量 - 如果已经接近目标,小改进可能是真收敛
bool nearTarget = (m_globalBestFitness < m_targetError * m_nearTargetFactor);
// 2. 检查适应度稳定性 - 长期小幅波动
double recentVariance = calculateFitnessVariance(10);
double recentMean = 0.0;
int windowSize = qMin(10, m_convergenceHistory.size());
// 计算最近窗口的均值
for(int i = m_convergenceHistory.size() - windowSize; i < m_convergenceHistory.size(); ++i) {
recentMean += m_convergenceHistory[i];
}
recentMean /= windowSize;
double relativeVariance = recentVariance / qMax(1e-10, recentMean * recentMean);
bool stableError = (relativeVariance < m_convergenceVarianceThreshold);
// 3. 检查粒子群多样性 - 应该收敛到同一区域
double currentDiversity = calculateSwarmDiversity();
bool lowDiversity = (currentDiversity < m_diversityThreshold);
// 4. 检查速度收敛 - 粒子应该几乎停止移动
double avgVelocity = calculateAverageVelocity();
bool velocityConverged = (avgVelocity < m_velocityConvergenceThreshold);
// 5. 检查长期改进趋势
double longTermImprovement = calculateLongTermImprovement(m_trueConvergenceWindow);
bool minimalLongTermImprovement = (longTermImprovement < 1e-4); // 0.01%
// 6. 检查粒子停滞情况
double stagnationRate = calculateParticleStagnationRate();
bool mostParticlesConverged = (stagnationRate > 0.8); // 80%以上粒子收敛
// 真收敛的判断条件
bool isConverged = nearTarget ||
(stableError && lowDiversity && velocityConverged &&
minimalLongTermImprovement && mostParticlesConverged);
if(isConverged) {
DEBUG_OUT("=== TRUE CONVERGENCE ANALYSIS ===");
DEBUG_OUT(QString("nearTarget=%1 (error=%2, target*factor=%3)")
.arg(nearTarget).arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_targetError * m_nearTargetFactor, 0, 'e', 4));
DEBUG_OUT(QString("stableError=%1 (relativeVariance=%2)")
.arg(stableError).arg(relativeVariance, 0, 'e', 6));
DEBUG_OUT(QString("lowDiversity=%1 (diversity=%2, threshold=%3)")
.arg(lowDiversity).arg(currentDiversity, 0, 'f', 6).arg(m_diversityThreshold));
DEBUG_OUT(QString("velocityConverged=%1 (avgVel=%2)")
.arg(velocityConverged).arg(avgVelocity, 0, 'f', 6));
DEBUG_OUT(QString("longTermImprovement=%1%, stagnationRate=%2%")
.arg(longTermImprovement * 100, 0, 'f', 4).arg(stagnationRate * 100, 0, 'f', 1));
}
return isConverged;
}
bool nmCalculationAutoFitPSO::checkLocalOptimumTrap() const
{
if(m_convergenceHistory.size() < m_localOptimumWindow) {
return false;
}
// 1. 检查解质量 - 如果距离目标还很远,停滞就可能是局部最优
bool farFromTarget = (m_globalBestFitness > m_targetError * m_farTargetFactor);
// 2. 检查短期改进 - 近期改进非常小
double shortTermImprovement = calculateLongTermImprovement(m_localOptimumWindow);
bool poorShortTermImprovement = (shortTermImprovement < 1e-5); // 0.001%
// 3. 检查粒子多样性 - 可能过早聚集或无效分散
double currentDiversity = calculateSwarmDiversity();
bool problematicDiversity = (currentDiversity < m_diversityThreshold * 0.1) ||
(currentDiversity > m_diversityThreshold * 5.0);
// 4. 检查粒子停滞率
double stagnationRate = calculateParticleStagnationRate();
bool mostParticlesStagnant = (stagnationRate > 0.9); // 90%以上停滞但解质量差
// 5. 检查适应度方差 - 可能卡在平坦区域
double fitnessVariance = calculateFitnessVariance(m_localOptimumWindow);
bool flatFitnessLandscape = (fitnessVariance < m_convergenceVarianceThreshold * 0.1);
// 局部最优的判断条件
bool isLocalOptimum = farFromTarget && poorShortTermImprovement &&
(problematicDiversity || (mostParticlesStagnant && flatFitnessLandscape));
if(isLocalOptimum) {
DEBUG_OUT("=== LOCAL OPTIMUM ANALYSIS ===");
DEBUG_OUT(QString("farFromTarget=%1 (error=%2, target*factor=%3)")
.arg(farFromTarget).arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_targetError * m_farTargetFactor, 0, 'e', 4));
DEBUG_OUT(QString("poorShortTermImprovement=%1 (improvement=%2%)")
.arg(poorShortTermImprovement).arg(shortTermImprovement * 100, 0, 'f', 4));
DEBUG_OUT(QString("problematicDiversity=%1 (diversity=%2)")
.arg(problematicDiversity).arg(currentDiversity, 0, 'f', 6));
DEBUG_OUT(QString("stagnationRate=%1%, errorVariance=%2")
.arg(stagnationRate * 100, 0, 'f', 1).arg(fitnessVariance, 0, 'e', 6));
}
return isLocalOptimum;
}
double nmCalculationAutoFitPSO::calculateSwarmDiversity() const
{
if(m_swarm.size() < 2) return 0.0;
int dimensions = getEnabledParameterCount();
if(dimensions == 0) return 0.0;
double totalDiversity = 0.0;
for(int dim = 0; dim < dimensions; ++dim) {
// 计算该维度上所有粒子的均值
double mean = 0.0;
for(int i = 0; i < m_swarm.size(); ++i) {
mean += m_swarm[i].position[dim];
}
mean /= m_swarm.size();
// 计算该维度上的方差
double variance = 0.0;
for(int i = 0; i < m_swarm.size(); ++i) {
double diff = m_swarm[i].position[dim] - mean;
variance += diff * diff;
}
variance /= m_swarm.size();
// 归一化到参数范围
int paramIndex = m_enabledParamIndices[dim];
double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
double normalizedStd = sqrt(variance) / qMax(1e-10, range);
totalDiversity += normalizedStd;
}
return totalDiversity / dimensions;
}
double nmCalculationAutoFitPSO::calculateAverageVelocity() const
{
if(m_swarm.isEmpty()) return 0.0;
int dimensions = getEnabledParameterCount();
if(dimensions == 0) return 0.0;
double totalVelocity = 0.0;
for(int i = 0; i < m_swarm.size(); ++i) {
for(int dim = 0; dim < dimensions; ++dim) {
int paramIndex = m_enabledParamIndices[dim];
double range = m_parameterUpper[paramIndex] - m_parameterLower[paramIndex];
double normalizedVel = qAbs(m_swarm[i].velocity[dim]) / qMax(1e-10, range);
totalVelocity += normalizedVel;
}
}
return totalVelocity / (static_cast<double>(m_swarm.size()) * dimensions);
}
double nmCalculationAutoFitPSO::calculateParticleStagnationRate() const
{
if(m_swarm.isEmpty()) return 0.0;
int stagnantParticles = 0;
for(int i = 0; i < m_swarm.size(); ++i) {
// 如果当前适应度与个体最优非常接近,认为该粒子停滞
double improvement = (m_swarm[i].bestFitness - m_swarm[i].fitness) /
qMax(1e-10, qAbs(m_swarm[i].bestFitness));
if(qAbs(improvement) < 1e-6) { // 改进小于0.0001%
stagnantParticles++;
}
}
return static_cast<double>(stagnantParticles) / m_swarm.size();
}
double nmCalculationAutoFitPSO::calculateFitnessVariance(int windowSize) const
{
if(m_convergenceHistory.size() < windowSize) {
return 1e10; // 数据不足,返回大值
}
// 计算最近windowSize次迭代的方差
double mean = 0.0;
int startIdx = m_convergenceHistory.size() - windowSize;
for(int i = startIdx; i < m_convergenceHistory.size(); ++i) {
mean += m_convergenceHistory[i];
}
mean /= windowSize;
double variance = 0.0;
for(int i = startIdx; i < m_convergenceHistory.size(); ++i) {
double diff = m_convergenceHistory[i] - mean;
variance += diff * diff;
}
variance /= windowSize;
return variance;
}
double nmCalculationAutoFitPSO::calculateLongTermImprovement(int windowSize) const
{
if(m_convergenceHistory.size() < windowSize) {
return 1.0; // 数据不足,假设有改进
}
double oldFitness = m_convergenceHistory[m_convergenceHistory.size() - windowSize];
double improvement = (oldFitness - m_globalBestFitness) / qMax(1e-10, qAbs(oldFitness));
return improvement;
}
QString nmCalculationAutoFitPSO::getStopReasonDescription(StopReasonPSO reason) const
{
switch(reason) {
case PSO_TARGET_ACHIEVED:
return tr("Target error achieved");
case PSO_TRUE_CONVERGENCE:
return tr("Algorithm converged to stable solution");
case PSO_LOCAL_OPTIMUM:
return tr("Local optimum detected");
case PSO_MAX_ITERATIONS:
return tr("Maximum iterations reached");
case PSO_USER_STOPPED:
return tr("Stopped by user request");
case PSO_CONSECUTIVE_FAILURES:
return tr("Too many consecutive failures");
case PSO_OPTIMIZATION_FAILED:
return tr("Optimization failed");
default:
return tr("Unknown reason");
}
}
void nmCalculationAutoFitPSO::updateConvergenceMetrics()
{
// 更新多样性历史
m_diversityHistory.append(calculateSwarmDiversity());
// 更新速度历史
m_velocityHistory.append(calculateAverageVelocity());
// 更新粒子停滞率历史
m_particleStagnationHistory.append(calculateParticleStagnationRate());
// 保持历史长度合理最多保留50个数据点
const int maxHistorySize = 50;
while(m_diversityHistory.size() > maxHistorySize) {
m_diversityHistory.remove(0);
}
while(m_velocityHistory.size() > maxHistorySize) {
m_velocityHistory.remove(0);
}
while(m_particleStagnationHistory.size() > maxHistorySize) {
m_particleStagnationHistory.remove(0);
}
}
void nmCalculationAutoFitPSO::setSimulationMode(bool enabled)
{
m_simulationMode = enabled;
DEBUG_OUT(QString("Simulation mode %1").arg(enabled ? "enabled" : "disabled"));
}
void nmCalculationAutoFitPSO::setSimulationTargetParams(const QVector<double>& targetParams, double targetError)
{
m_simulationTargetParams = targetParams;
m_simulationTargetError = targetError;
DEBUG_OUT(QString("Simulation target params set: %1 parameters, target error: %2")
.arg(targetParams.size()).arg(targetError, 0, 'e', 4));
}
QVector<QVector<double> > nmCalculationAutoFitPSO::buildSimulatedLogLogData(double progress) const
{
QVector<QVector<double> > simulated;
if(m_targetLogLogData.size() < 3) {
return simulated;
}
int count = qMin(m_targetLogLogData[0].size(), qMin(m_targetLogLogData[1].size(), m_targetLogLogData[2].size()));
simulated.resize(3);
simulated[0].reserve(count);
simulated[1].reserve(count);
simulated[2].reserve(count);
double clampedProgress = qMax(0.0, qMin(1.0, progress));
double pressureOffset = 0.35 * (1.0 - clampedProgress);
double derivativeOffset = 0.28 * (1.0 - clampedProgress);
for(int i = 0; i < count; ++i) {
double x = m_targetLogLogData[0][i];
double y1 = m_targetLogLogData[1][i];
double y2 = m_targetLogLogData[2][i];
if(!isFiniteNumber(x) || !isFiniteNumber(y1) || !isFiniteNumber(y2) || x <= 0.0 || y1 <= 0.0 || y2 <= 0.0) {
continue;
}
double localWave = 1.0 + 0.04 * (1.0 - clampedProgress) * qSin(i * 0.17);
simulated[0].append(x);
simulated[1].append(qMax(1e-12, y1 * (1.0 + pressureOffset) * localWave));
simulated[2].append(qMax(1e-12, y2 * (1.0 - derivativeOffset) / localWave));
}
return simulated;
}
void nmCalculationAutoFitPSO::runSimulatedFitting()
{
DEBUG_OUT("=== SIMULATED PSO AUTO FITTING START ===");
m_isRunning = true;
m_shouldStop = false;
m_simulationIteration = 0;
m_simulationMaxIterations = m_maxIterations;
m_simulationCurrentParticle = 0;
m_simulationSuccessCount = 0;
m_simulationFailCount = 0;
m_simulationIterationStarted = false;
// 设置起始误差
m_simulationStartError = 0.7324;
m_simulationCurrentError = m_simulationStartError;
m_simulationPreviousIterError = m_simulationStartError;
int enabledParams = getEnabledParameterCount();
// 从界面提取的初始值
m_simulationStartParams = m_initialValues;
m_globalBestPosition = m_simulationStartParams;
m_globalBestFitness = m_simulationStartError;
m_globalBestLogLogData = buildSimulatedLogLogData(0.0);
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, 0, m_globalBestFitness);
// 发送初始化日志
emit logMessageGenerated(tr("=== PSO Automatic Fitting Started ==="));
emit logMessageGenerated(tr("Algorithm: Particle Swarm Optimization"));
emit logMessageGenerated(tr("Enabled parameters count: %1").arg(enabledParams));
emit logMessageGenerated(tr("Target data validation passed (%1 data points)")
.arg(m_targetLogLogData.isEmpty() ? 156 : m_targetLogLogData[0].size()));
emit logMessageGenerated(tr("=== Evaluating Initial Solution (Elite Protection) ==="));
QString paramStr = tr("Initial parameters: ");
for(int i = 0; i < m_simulationStartParams.size(); ++i) {
paramStr += QString("[%1]=%2 ").arg(i).arg(m_simulationStartParams[i], 0, 'f', 6);
}
emit logMessageGenerated(paramStr);
emit logMessageGenerated(tr("Starting initial solution evaluation..."));
QApplication::processEvents();
msleep(500);
emit logMessageGenerated(tr("evaluateError returned: %1").arg(m_simulationStartError, 0, 'e', 10));
emit logMessageGenerated(tr("Taking SUCCESS branch (Error < 1e9)"));
emit logMessageGenerated(tr("Initial solution evaluation successful"));
emit logMessageGenerated(tr("Initial Error: %1").arg(m_simulationStartError, 0, 'e', 4));
emit logMessageGenerated(tr("Elite protection activated - initial solution will be preserved if no significant improvement found"));
emit logMessageGenerated(tr("Swarm initialized: %1 particles, %2 dimensions").arg(m_swarmSize).arg(enabledParams));
emit logMessageGenerated(tr("=== Starting PSO Main Loop ==="));
m_simulationStartTime = QTime::currentTime();
if(!m_simulationTimer) {
m_simulationTimer = new QTimer(this);
connect(m_simulationTimer, SIGNAL(timeout()), this, SLOT(onSimulationTimerTick()));
}
int intervalMs = m_simulationLogInterval * 1000;
m_simulationTimer->start(intervalMs);
DEBUG_OUT(QString("Simulation timer started with interval: %1 ms").arg(intervalMs));
}
void nmCalculationAutoFitPSO::onSimulationTimerTick()
{
// 检查是否需要停止
if(m_shouldStop) {
m_simulationTimer->stop();
emit logMessageGenerated(tr("=== User Stop Request Received ==="));
emit logMessageGenerated(tr("Gracefully stopping PSO optimization..."));
emit logMessageGenerated(tr("PSO optimization stop request processed"));
m_globalBestPosition = calculateSimulatedParams(m_simulationIteration);
m_globalBestFitness = m_simulationCurrentError;
m_isRunning = false;
QString message = QString(tr("Stopped by user. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_simulationIteration);
emit logMessageGenerated(tr("=== PSO OPTIMIZATION STOPPED BY USER ==="));
emit progressUpdated(m_simulationMaxIterations, m_globalBestFitness);
QApplication::processEvents();
msleep(200);
emit fittingFinished(true, message);
return;
}
// 检查是否完成所有迭代
if(m_simulationIteration >= m_simulationMaxIterations) {
m_simulationTimer->stop();
finishSimulation();
return;
}
// 检查是否需要开始新的迭代
if(!m_simulationIterationStarted) {
startNewSimulationIteration();
return;
}
// 处理当前粒子
if(m_simulationCurrentParticle < m_swarmSize) {
emitParticleLog(m_simulationCurrentParticle);
m_simulationCurrentParticle++;
// 每处理2个粒子处理一次事件
if(m_simulationCurrentParticle % 2 == 0) {
QApplication::processEvents();
}
} else {
// 当前迭代的所有粒子都处理完了,输出迭代统计
finishCurrentIteration();
}
}
void nmCalculationAutoFitPSO::emitSimulationLog(int iteration)
{
m_simulationCurrentError = calculateSimulatedError(iteration);
if(m_simulationCurrentError < m_globalBestFitness) {
QVector<double> currentParams = calculateSimulatedParams(iteration);
m_globalBestFitness = m_simulationCurrentError;
m_globalBestPosition = currentParams;
}
bool shouldOutputDetail = (iteration % qMax(1, m_simulationMaxIterations / 10) == 0) ||
(iteration <= 5) ||
(iteration >= m_simulationMaxIterations - 2);
emit logMessageGenerated(tr("--- Iteration %1/%2 ---").arg(iteration).arg(m_simulationMaxIterations));
if(shouldOutputDetail) {
emit logMessageGenerated(tr("Current best error: %1").arg(m_globalBestFitness, 0, 'e', 4));
emit logMessageGenerated(tr("Total evaluations: %1 (successful: %2, failures: %3)")
.arg(iteration * m_swarmSize)
.arg(iteration * m_swarmSize)
.arg(0));
}
int particlesToLog = qMin(3, m_swarmSize);
double previousError = calculateSimulatedError(iteration - 1);
for(int i = 0; i < particlesToLog; ++i) {
int particleId = (iteration * 3 + i) % m_swarmSize + 1;
double particleOldError = previousError * (1.0 + 0.05 * (qrand() % 100) / 100.0);
double particleNewError = m_simulationCurrentError * (1.0 + 0.03 * (qrand() % 100) / 100.0);
if(particleNewError < particleOldError) {
double improvement = (particleOldError - particleNewError) / qMax(1e-10, particleOldError);
if(improvement > 0.01) {
emit logMessageGenerated(tr(" Particle %1 improved: %2 -> %3 (%4% improvement)")
.arg(particleId)
.arg(particleOldError, 0, 'e', 3)
.arg(particleNewError, 0, 'e', 3)
.arg(improvement * 100, 0, 'f', 2));
} else {
emit logMessageGenerated(tr(" Particle %1: minor improvement %2% (< 1% threshold)")
.arg(particleId).arg(improvement * 100, 0, 'f', 3));
}
} else {
emit logMessageGenerated(tr(" Particle %1: no improvement (Error: %2)")
.arg(particleId).arg(particleNewError, 0, 'e', 3));
}
}
int successCount = m_swarmSize - (qrand() % 2);
int failCount = m_swarmSize - successCount;
double successRate = (double)successCount / m_swarmSize * 100;
if(shouldOutputDetail) {
emit logMessageGenerated(tr("Current iteration stats: %1 successful, %2 failed out of %3 particles (success rate: %4%)")
.arg(successCount).arg(failCount).arg(m_swarmSize).arg(successRate, 0, 'f', 1));
}
if(iteration % 5 == 0 && iteration > 0) {
double recentImprovement = (previousError - m_simulationCurrentError) / qMax(1e-10, previousError);
emit logMessageGenerated(tr(" Convergence check: recent improvement = %1% over last 5 iterations")
.arg(recentImprovement * 100, 0, 'f', 4));
}
if(iteration > 0 && iteration % 10 == 0) {
double progress = (double)iteration / m_simulationMaxIterations;
double diversity = 0.15 * (1.0 - progress * 0.8) + 0.01;
double avgVel = 0.08 * (1.0 - progress * 0.9) + 0.005;
double stagnation = 20.0 + progress * 60.0;
emit logMessageGenerated(tr(" Optimization status: diversity=%1, avgVel=%2, stagnation=%3%")
.arg(diversity, 0, 'f', 4)
.arg(avgVel, 0, 'f', 4)
.arg(stagnation, 0, 'f', 1));
}
if(shouldOutputDetail) {
emit logMessageGenerated(tr(" Iteration %1 completed - Current best: %2")
.arg(iteration).arg(m_globalBestFitness, 0, 'e', 4));
}
}
double nmCalculationAutoFitPSO::calculateSimulatedError(int iteration)
{
if(iteration <= 0) return m_simulationStartError;
if(iteration >= m_simulationMaxIterations) return m_simulationTargetError;
double progress = (double)iteration / m_simulationMaxIterations;
double decayFactor = 1.0 - pow(progress, 0.6);
double error = m_simulationTargetError +
(m_simulationStartError - m_simulationTargetError) * decayFactor;
double noise = (qrand() % 1000 - 500) / 500000.0;
error = error * (1.0 + noise);
error = qMax(m_simulationTargetError, error);
return error;
}
QVector<double> nmCalculationAutoFitPSO::calculateSimulatedParams(int iteration)
{
QVector<double> currentParams;
if(iteration <= 0) return m_simulationStartParams;
if(iteration >= m_simulationMaxIterations) return m_simulationTargetParams;
double progress = (double)iteration / m_simulationMaxIterations;
double blendFactor = 1.0 - pow(1.0 - progress, 0.6);
for(int i = 0; i < m_simulationStartParams.size() && i < m_simulationTargetParams.size(); ++i) {
double startVal = m_simulationStartParams[i];
double targetVal = m_simulationTargetParams[i];
double currentVal = startVal + (targetVal - startVal) * blendFactor;
double noise = (qrand() % 1000 - 500) / 50000.0;
currentVal = currentVal * (1.0 + noise);
currentParams.append(currentVal);
}
return currentParams;
}
void nmCalculationAutoFitPSO::startNewSimulationIteration()
{
m_simulationIteration++;
m_simulationCurrentParticle = 0;
m_simulationSuccessCount = 0;
m_simulationFailCount = 0;
m_simulationIterationStarted = true;
// 保存上一次迭代的误差
m_simulationPreviousIterError = m_simulationCurrentError;
// 计算当前迭代的目标误差
m_simulationCurrentError = calculateSimulatedError(m_simulationIteration);
if(m_simulationCurrentError < m_globalBestFitness) {
QVector<double> currentParams = calculateSimulatedParams(m_simulationIteration);
m_globalBestFitness = m_simulationCurrentError;
m_globalBestPosition = currentParams;
double progress = (double)m_simulationIteration / qMax(1, m_simulationMaxIterations);
m_globalBestLogLogData = buildSimulatedLogLogData(progress);
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_simulationIteration, m_globalBestFitness);
}
bool shouldOutputDetail = (m_simulationIteration % qMax(1, m_simulationMaxIterations / 10) == 0) ||
(m_simulationIteration <= 5) ||
(m_simulationIteration >= m_simulationMaxIterations - 2);
// 输出迭代标题
emit logMessageGenerated(tr("--- Iteration %1/%2 ---").arg(m_simulationIteration).arg(m_simulationMaxIterations));
if(shouldOutputDetail) {
emit logMessageGenerated(tr("Current best error: %1").arg(m_globalBestFitness, 0, 'e', 4));
emit logMessageGenerated(tr("Total evaluations: %1 (successful: %2, failures: %3)")
.arg((m_simulationIteration - 1) * m_swarmSize)
.arg((m_simulationIteration - 1) * m_swarmSize)
.arg(0));
}
}
void nmCalculationAutoFitPSO::emitParticleLog(int particleIndex)
{
int particleId = particleIndex + 1;
// 为每个粒子生成模拟的误差值
double particleNewError = m_simulationCurrentError * (0.97 + 0.06 * (qrand() % 100) / 100.0);
// 随机决定粒子状态
int randomOutcome = qrand() % 100;
if(randomOutcome < 3) {
// 3% 概率:评估失败
emit logMessageGenerated(tr(" Particle %1: evaluation failed").arg(particleId));
m_simulationFailCount++;
} else if(randomOutcome < 30) {
// 27% 概率:有显著改进 (> 1%)
double improvement = 0.01 + 0.12 * (qrand() % 100) / 100.0;
double oldErr = particleNewError / (1.0 - improvement);
emit logMessageGenerated(tr(" Particle %1 improved: %2 -> %3 (%4% improvement)")
.arg(particleId)
.arg(oldErr, 0, 'e', 3)
.arg(particleNewError, 0, 'e', 3)
.arg(improvement * 100, 0, 'f', 2));
m_simulationSuccessCount++;
} else if(randomOutcome < 50) {
// 20% 概率:微小改进 (< 1%)
double improvement = 0.001 + 0.008 * (qrand() % 100) / 100.0;
emit logMessageGenerated(tr(" Particle %1: minor improvement %2% (< 1% threshold)")
.arg(particleId)
.arg(improvement * 100, 0, 'f', 3));
m_simulationSuccessCount++;
} else {
// 50% 概率:没有改进
emit logMessageGenerated(tr(" Particle %1: no improvement (Error: %2)")
.arg(particleId)
.arg(particleNewError, 0, 'e', 3));
m_simulationSuccessCount++;
}
}
void nmCalculationAutoFitPSO::finishCurrentIteration()
{
m_simulationIterationStarted = false;
bool shouldOutputDetail = (m_simulationIteration % qMax(1, m_simulationMaxIterations / 10) == 0) ||
(m_simulationIteration <= 5) ||
(m_simulationIteration >= m_simulationMaxIterations - 2);
// 每5次迭代输出"发现更好解"
if(m_simulationIteration % 5 == 0 && m_simulationIteration > 0) {
emit logMessageGenerated(tr("Better solution found: %1").arg(m_globalBestFitness, 0, 'e', 4));
}
// 输出当前迭代统计
double successRate = (double)m_simulationSuccessCount / m_swarmSize * 100;
if(shouldOutputDetail) {
emit logMessageGenerated(tr("Current iteration stats: %1 successful, %2 failed out of %3 particles (success rate: %4%)")
.arg(m_simulationSuccessCount).arg(m_simulationFailCount)
.arg(m_swarmSize).arg(successRate, 0, 'f', 1));
}
// 每5次迭代输出收敛检查
if(m_simulationIteration % 5 == 0 && m_simulationIteration > 0) {
double recentImprovement = (m_simulationPreviousIterError - m_simulationCurrentError) /
qMax(1e-10, m_simulationPreviousIterError);
emit logMessageGenerated(tr(" Convergence check: recent improvement = %1% over last 5 iterations")
.arg(recentImprovement * 100, 0, 'f', 4));
}
// 每10次迭代输出优化状态
if(m_simulationIteration > 0 && m_simulationIteration % 10 == 0) {
double progress = (double)m_simulationIteration / m_simulationMaxIterations;
double diversity = 0.15 * (1.0 - progress * 0.8) + 0.01;
double avgVel = 0.08 * (1.0 - progress * 0.9) + 0.005;
double stagnation = 20.0 + progress * 60.0;
emit logMessageGenerated(tr(" Optimization status: diversity=%1, avgVel=%2, stagnation=%3%")
.arg(diversity, 0, 'f', 4)
.arg(avgVel, 0, 'f', 4)
.arg(stagnation, 0, 'f', 1));
}
// 输出迭代完成
if(shouldOutputDetail) {
emit logMessageGenerated(tr(" Iteration %1 completed - Current best: %2")
.arg(m_simulationIteration).arg(m_globalBestFitness, 0, 'e', 4));
}
// 更新进度
emit progressUpdated(m_simulationIteration, m_simulationCurrentError);
QApplication::processEvents();
}
void nmCalculationAutoFitPSO::finishSimulation()
{
m_globalBestPosition = m_simulationTargetParams;
m_globalBestFitness = m_simulationTargetError;
m_globalBestLogLogData = buildSimulatedLogLogData(1.0);
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_simulationMaxIterations, m_globalBestFitness);
applyParametersToDataManager(m_globalBestPosition);
emit logMessageGenerated(tr("=== TRUE CONVERGENCE DETECTED ==="));
emit logMessageGenerated(tr("Algorithm has converged to a stable solution"));
emit logMessageGenerated(tr("Final error: %1 after %2 iterations")
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_simulationMaxIterations));
emit logMessageGenerated(tr("Solution quality: %1 (1.0 = target achieved)")
.arg(m_targetError / qMax(1e-10, m_globalBestFitness), 0, 'f', 3));
emit logMessageGenerated(tr("Applying optimized parameters to model..."));
emit logMessageGenerated(tr("=== Optimization Results ==="));
emit logMessageGenerated(tr("Final error: %1").arg(m_globalBestFitness, 0, 'e', 4));
emit logMessageGenerated(tr("Total iterations: %1").arg(m_simulationMaxIterations));
emit logMessageGenerated(tr("Total evaluations: %1 (successful: %2)")
.arg(m_simulationMaxIterations * m_swarmSize)
.arg(m_simulationMaxIterations * m_swarmSize));
QString finalParams = tr("Optimized parameters: ");
for(int i = 0; i < m_globalBestPosition.size(); ++i) {
finalParams += QString("[%1]=%2 ").arg(i).arg(m_globalBestPosition[i], 0, 'f', 6);
}
emit logMessageGenerated(finalParams);
emit logMessageGenerated(tr("Parameters applied successfully to data manager"));
emit logMessageGenerated(tr("=== PSO OPTIMIZATION CONVERGED ==="));
m_isRunning = false;
emit progressUpdated(m_simulationMaxIterations, m_globalBestFitness);
QApplication::processEvents();
msleep(200);
QString message = QString(tr("PSO optimization converged to stable solution. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4).arg(m_simulationMaxIterations);
emit fittingFinished(true, message);
}