refactor(nmNum): 优化 LM 拟合日志并展示误差改善率

feature/AutoFit-Optimize-20260914
lvjunjie 3 weeks ago
parent ed9cc46ac5
commit 1237b44a4c

Binary file not shown.

@ -664,6 +664,172 @@ Reason: %1</source>
</context> </context>
<context> <context>
<name>nmCalculationAutoFitLM</name> <name>nmCalculationAutoFitLM</name>
<message>
<source>[Fitting completed]</source>
<translation>【拟合完成】</translation>
</message>
<message>
<source>[Fitting ended]</source>
<translation>【拟合结束】</translation>
</message>
<message>
<source>[Initial parameters]</source>
<translation>【初始参数】</translation>
</message>
<message>
<source>[Stage 1: Height alignment]
Adjust permeability only; accept by height error.</source>
<translation>【第一阶段:高度预调】
只调整渗透率,按高度误差判断是否接受。</translation>
</message>
<message>
<source>[Stage 2: Joint LM - Shape]
Adjust selected parameters; accept by shape error.</source>
<translation>【第二阶段:联合 LM · 形状】
调整勾选参数,按形状误差判断是否接受。</translation>
</message>
<message>
<source>[Stage 2: Joint LM - Total error]
Adjust selected parameters; accept by total error.</source>
<translation>【第二阶段:联合 LM · 总误差】
调整勾选参数,按总误差判断是否接受。</translation>
</message>
<message>
<source>%1 | %2 | %3 | Improvement: %4%</source>
<translation>%1|%2|%3|改善:%4%</translation>
</message>
<message>
<source>%1 | Solver failed
No valid error was returned; keep the last accepted result and reduce the step.</source>
<translation>%1|求解失败
未返回有效误差,保留上次已接受结果并缩小步长。</translation>
</message>
<message>
<source>
Objective error did not decrease; keep the last accepted result and reduce the step.</source>
<translation>
目标误差未减小,保留上次已接受结果并缩小步长。</translation>
</message>
<message>
<source>========== LM fitting started ==========</source>
<translation>========== LM 拟合开始 ==========</translation>
</message>
<message>
<source>Target well: %1</source>
<translation>目标井:%1</translation>
</message>
<message>
<source>Target error: %1</source>
<translation>目标误差:%1</translation>
</message>
<message>
<source>Initial total error: %1</source>
<translation>初始总误差:%1</translation>
</message>
<message>
<source>Permeability</source>
<translation>渗透率</translation>
</message>
<message>
<source>Skin</source>
<translation>表皮系数</translation>
</message>
<message>
<source>Wellbore storage</source>
<translation>井筒储集系数</translation>
</message>
<message>
<source>Porosity</source>
<translation>孔隙度</translation>
</message>
<message>
<source>Fracture conductivity</source>
<translation>裂缝导流能力</translation>
</message>
<message>
<source>Fracture half length</source>
<translation>裂缝半长</translation>
</message>
<message>
<source>m^3/MPa</source>
<translation>m³/MPa</translation>
</message>
<message>
<source>mD.m</source>
<translation>mD·m</translation>
</message>
<message>
<source>Trial %1 | %2 | Height error: %3 -&gt; %4</source>
<translation>试算 %1|%2|高度误差:%3 → %4</translation>
</message>
<message>
<source>Trial %1 | Solver failed; keep the last accepted result and reduce the step.</source>
<translation>试算 %1|求解失败,保留上次已接受结果并缩小步长。</translation>
</message>
<message>
<source>Height error did not decrease; keep the last accepted result and reduce the step.</source>
<translation>高度误差未减小,保留上次已接受结果并缩小步长。</translation>
</message>
<message>
<source>Pressure and derivative height directions conflict; keep the last accepted result and reduce the step.</source>
<translation>压力与导数的高度偏移方向冲突,保留上次已接受结果并缩小步长。</translation>
</message>
<message>
<source>Height alignment ended: %1</source>
<translation>高度预调结束:%1</translation>
</message>
<message>
<source>Height alignment skipped: permeability is not selected.</source>
<translation>跳过高度预调:未勾选渗透率。</translation>
</message>
<message>
<source>Calculating sensitivity...</source>
<translation>正在计算灵敏度……</translation>
</message>
<message>
<source>Sensitivity trial</source>
<translation>灵敏度试算</translation>
</message>
<message>
<source>Iteration %1</source>
<translation>迭代 %1</translation>
</message>
<message>
<source>Shape error: %1 -&gt; %2</source>
<translation>形状误差:%1 → %2</translation>
</message>
<message>
<source>Total error: %1 -&gt; %2</source>
<translation>总误差:%1 → %2</translation>
</message>
<message>
<source>Shape phase ended: %1</source>
<translation>形状部分结束:%1</translation>
</message>
<message>
<source>Total-error phase ended: %1</source>
<translation>总误差部分结束:%1</translation>
</message>
<message>
<source>Iteration %1 | No feasible descent step; keep the last accepted result.</source>
<translation>迭代 %1|没有可行的下降步,保留上次已接受结果。</translation>
</message>
<message>
<source>Iteration or evaluation budget reached</source>
<translation>达到本阶段迭代次数或试算次数上限</translation>
</message>
<message>
<source>Final total error did not improve; restoring the initial result.</source>
<translation>最终结果无效或总误差变大,恢复初始结果。</translation>
</message>
<message>
<source>Initial solution failed; trying the parameter-range midpoint.</source>
<translation>初始求解失败,将尝试参数范围中点。</translation>
</message>
<message>
<source>Solver calls: %1</source>
<translation>共调用求解器:%1 次</translation>
</message>
<message> <message>
<source>Failed to refresh the sampling layer from the current curve.</source> <source>Failed to refresh the sampling layer from the current curve.</source>
<translation>无法根据当前曲线刷新采样层级。</translation> <translation>无法根据当前曲线刷新采样层级。</translation>
@ -4395,6 +4561,10 @@ Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured We
</context> </context>
<context> <context>
<name>nmWxAutomaticfittingStart</name> <name>nmWxAutomaticfittingStart</name>
<message>
<source>Total time: %1 min %2 s</source>
<translation>总耗时:%1 分 %2 秒</translation>
</message>
<message> <message>
<source>Auto Fitting Progress</source> <source>Auto Fitting Progress</source>
<translation>自动拟合进程</translation> <translation>自动拟合进程</translation>

@ -7,6 +7,7 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QFile> #include <QFile>
#include <QAtomicInt>
#include <limits> #include <limits>
#include "nmCalculation_global.h" #include "nmCalculation_global.h"
@ -112,6 +113,7 @@ public:
bool isRunning() const; bool isRunning() const;
int getCurrentIteration() const; int getCurrentIteration() const;
int getTotalEvaluations() const; int getTotalEvaluations() const;
int getSolverCallCount() const;
void resetOptimizer(); void resetOptimizer();
void setTargetWellName(const QString& wellName); void setTargetWellName(const QString& wellName);
@ -126,6 +128,10 @@ signals:
void finalizingStarted(); void finalizingStarted();
void logMessageGenerated(const QString& message); void logMessageGenerated(const QString& message);
private slots:
// 由求解线程直接通知,仅累加原子计数,不操作界面。
void recordSolverCall();
private: private:
bool initializeTemporaryDirectory(); bool initializeTemporaryDirectory();
void cleanupTemporaryDirectory(); void cleanupTemporaryDirectory();
@ -172,6 +178,8 @@ private:
const AutoFitObjectiveBreakdownLM* objectiveBreakdown = nullptr); const AutoFitObjectiveBreakdownLM* objectiveBreakdown = nullptr);
QVector<double> buildTraceParameterVector(const QVector<double>& selectedParameters) const; QVector<double> buildTraceParameterVector(const QVector<double>& selectedParameters) const;
void emitRunSummary(bool success, StopReasonLM finalReason); void emitRunSummary(bool success, StopReasonLM finalReason);
void emitTrialLog(const QString& title, const AutoFitObjectiveBreakdownLM& before,
const AutoFitObjectiveBreakdownLM& after, bool valid, bool accepted, bool shapeStage);
bool validateParameters(const QVector<double>& parameters) const; bool validateParameters(const QVector<double>& parameters) const;
bool validateLogLogData(const QVector<QVector<double> >& logLogData) const; bool validateLogLogData(const QVector<QVector<double> >& logLogData) const;
@ -212,6 +220,7 @@ private:
int m_maxIterations; int m_maxIterations;
double m_targetError; double m_targetError;
int m_totalEvaluations; int m_totalEvaluations;
QAtomicInt m_solverCallCount;
int m_successfulEvaluations; int m_successfulEvaluations;
volatile int m_evaluationInProgress; volatile int m_evaluationInProgress;

@ -157,6 +157,8 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
//void slotTaskUpdateProgress(); //void slotTaskUpdateProgress();
signals: signals:
/** @brief 即将实际调用数值求解器,供 LM 统计调用次数。 */
void solverInvoked();
void sig_calculateDone(bool isSuccessed); void sig_calculateDone(bool isSuccessed);
/** @brief 报告当前真实阶段;nTotal<=0 表示 DLL 等不可测阶段。 */ /** @brief 报告当前真实阶段;nTotal<=0 表示 DLL 等不可测阶段。 */
void sigStageChanged(QString sStage, int nCurrent, int nTotal); void sigStageChanged(QString sStage, int nCurrent, int nTotal);

@ -695,6 +695,7 @@ nmCalculationAutoFitLM::nmCalculationAutoFitLM(QObject* parent)
, m_maxIterations(100) , m_maxIterations(100)
, m_targetError(0.001) , m_targetError(0.001)
, m_totalEvaluations(0) , m_totalEvaluations(0)
, m_solverCallCount(0)
, m_successfulEvaluations(0) , m_successfulEvaluations(0)
, m_evaluationInProgress(0) , m_evaluationInProgress(0)
, m_consecutiveFailures(0) , m_consecutiveFailures(0)
@ -791,15 +792,15 @@ void nmCalculationAutoFitLM::setTargetLogLogData(const QVector<QVector<double> >
void nmCalculationAutoFitLM::stopFitting() void nmCalculationAutoFitLM::stopFitting()
{ {
if(m_isFinalizing) { if(m_isFinalizing) {
emit logMessageGenerated( DEBUG_OUT(
tr("The final full-field calculation is already running")); tr("The final full-field calculation is already running"));
return; return;
} }
// 用户点击停止时只设置请求标志,让 LM 主循环和求解器等待逻辑自然退出。 // 用户点击停止时只设置请求标志,让 LM 主循环和求解器等待逻辑自然退出。
if(m_isRunning) { if(m_isRunning) {
emit logMessageGenerated(tr("=== User Stop Request Received ===")); DEBUG_OUT(tr("=== User Stop Request Received ==="));
emit logMessageGenerated(tr("Gracefully stopping LM automatic fitting...")); DEBUG_OUT(tr("Gracefully stopping LM automatic fitting..."));
m_shouldStop = true; m_shouldStop = true;
// 此槽由求解等待循环派发,必须立即返回,外层循环才能转发取消请求。 // 此槽由求解等待循环派发,必须立即返回,外层循环才能转发取消请求。
@ -807,9 +808,9 @@ void nmCalculationAutoFitLM::stopFitting()
emit logMessageGenerated(tr("Waiting for current solver evaluation to stop...")); emit logMessageGenerated(tr("Waiting for current solver evaluation to stop..."));
} }
emit logMessageGenerated(tr("LM automatic fitting stop request processed")); DEBUG_OUT(tr("LM automatic fitting stop request processed"));
} else { } else {
emit logMessageGenerated(tr("Stop request received but optimization is not running")); DEBUG_OUT(tr("Stop request received but optimization is not running"));
} }
} }
@ -827,10 +828,22 @@ int nmCalculationAutoFitLM::getCurrentIteration() const
int nmCalculationAutoFitLM::getTotalEvaluations() const int nmCalculationAutoFitLM::getTotalEvaluations() const
{ {
// 返回本轮拟合实际调用真实求解器的总次数,供完成日志展示。 // 返回评价次数,用于迭代预算;真实求解器调用次数另行统计。
return m_totalEvaluations; return m_totalEvaluations;
} }
int nmCalculationAutoFitLM::getSolverCallCount() const
{
// 包含失败调用、重试和最终完整求解,不包含未进入求解器的评价。
return m_solverCallCount;
}
void nmCalculationAutoFitLM::recordSolverCall()
{
// 信号来自求解线程,使用原子累加避免跨线程读写竞争。
m_solverCallCount.fetchAndAddOrdered(1);
}
QVector<double> nmCalculationAutoFitLM::getBestSolution() const QVector<double> nmCalculationAutoFitLM::getBestSolution() const
{ {
// 返回紧凑的“启用参数向量”,顺序与 m_enabledParamIndices 一致。 // 返回紧凑的“启用参数向量”,顺序与 m_enabledParamIndices 一致。
@ -873,6 +886,7 @@ void nmCalculationAutoFitLM::resetOptimizer()
m_comparisonTimeMax = 0.0; m_comparisonTimeMax = 0.0;
m_currentIteration = 0; m_currentIteration = 0;
m_totalEvaluations = 0; m_totalEvaluations = 0;
m_solverCallCount = 0;
m_successfulEvaluations = 0; m_successfulEvaluations = 0;
m_lastError.clear(); m_lastError.clear();
m_initialValues.clear(); m_initialValues.clear();
@ -931,9 +945,9 @@ void nmCalculationAutoFitLM::initializeTraceFile()
writeTraceHeader(); writeTraceHeader();
writeTraceMetaFile(); writeTraceMetaFile();
emit logMessageGenerated(tr("LM fitting trace: %1").arg(m_traceFilePath)); DEBUG_OUT(tr("LM fitting trace: %1").arg(m_traceFilePath));
if(!m_traceMetaFilePath.isEmpty()) { if(!m_traceMetaFilePath.isEmpty()) {
emit logMessageGenerated( DEBUG_OUT(
tr("LM fitting trace metadata: %1").arg(m_traceMetaFilePath)); tr("LM fitting trace metadata: %1").arg(m_traceMetaFilePath));
} }
} }
@ -1172,26 +1186,48 @@ void nmCalculationAutoFitLM::writeTraceRow(
m_traceFile.flush(); m_traceFile.flush();
} }
void nmCalculationAutoFitLM::emitRunSummary(bool success, StopReasonLM finalReason) // 窗口统一显示普通小数;失败评价不展示惩罚值或非有限数。
static QString lmLogError(double value)
{ {
// 汇总仅描述 LM 迭代和真实求解器评价。 return isFiniteNumber(value) && value < 1.0e9
emit logMessageGenerated(tr("=== LM Run Summary ===")); ? QString::number(value, 'f', 6) : QString("--");
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 ? tr("SUCCESS") : tr("FAILED"))
.arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1)
.arg(m_totalEvaluations)
.arg(m_successfulEvaluations)
.arg(m_totalEvaluations - m_successfulEvaluations));
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 nmCalculationAutoFitLM::emitRunSummary(bool success, StopReasonLM finalReason)
{
// 最终只汇总总误差,耗时由进程窗口在收到结束信号后追加。
emit logMessageGenerated(success ? tr("[Fitting completed]") : tr("[Fitting ended]"));
emit logMessageGenerated(tr("Stop reason: %1").arg(success
? getStopReasonDescription(finalReason)
: (m_lastError.isEmpty() ? getStopReasonDescription(finalReason) : m_lastError)));
emit logMessageGenerated(tr("Total error: %1 -> %2")
.arg(lmLogError(m_userInitialFitness)).arg(lmLogError(m_globalBestFitness)));
emit logMessageGenerated(tr("Solver calls: %1").arg(getSolverCallCount()));
}
void nmCalculationAutoFitLM::emitTrialLog(const QString& title,
const AutoFitObjectiveBreakdownLM& before, const AutoFitObjectiveBreakdownLM& after,
bool valid, bool accepted, bool shapeStage)
{
// 只显示当前阶段目标;改善率相对上次已接受的误差计算,变差时为负值。
if(!valid) {
emit logMessageGenerated(tr("%1 | Solver failed\nNo valid error was returned; keep the last accepted result and reduce the step.").arg(title));
return;
} }
const QString status = accepted ? tr("accepted") : tr("rejected");
const double beforeError = shapeStage ? before.shapeLoss : before.total;
const double afterError = shapeStage ? after.shapeLoss : after.total;
const QString objective = (shapeStage ? tr("Shape error: %1 -> %2")
: tr("Total error: %1 -> %2"))
.arg(lmLogError(beforeError)).arg(lmLogError(afterError));
// 起点为零时相对改善率无定义,避免除零。
const QString improvement = beforeError > 0.0
? QString::number((beforeError - afterError) / beforeError * 100.0, 'f', 2)
: QString("--");
QString message = tr("%1 | %2 | %3 | Improvement: %4%")
.arg(title).arg(status).arg(objective).arg(improvement);
if(!accepted) message += tr("\n Objective error did not decrease; keep the last accepted result and reduce the step.");
emit logMessageGenerated(message);
} }
QVector<double> nmCalculationAutoFitLM::buildTraceParameterVector(const QVector<double>& selectedParameters) const QVector<double> nmCalculationAutoFitLM::buildTraceParameterVector(const QVector<double>& selectedParameters) const
@ -1353,9 +1389,9 @@ bool nmCalculationAutoFitLM::startAutoFitting()
return false; return false;
} }
emit logMessageGenerated(tr("Algorithm: LM")); DEBUG_OUT(tr("Algorithm: LM"));
const int enabledParams = getEnabledParameterCount(); const int enabledParams = getEnabledParameterCount();
emit logMessageGenerated(tr("Enabled parameters count: %1").arg(enabledParams)); DEBUG_OUT(tr("Enabled parameters count: %1").arg(enabledParams));
if(enabledParams == 0) { if(enabledParams == 0) {
m_lastError = tr("No parameters enabled for optimization"); m_lastError = tr("No parameters enabled for optimization");
@ -1382,7 +1418,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
return false; return false;
} }
emit logMessageGenerated( DEBUG_OUT(
tr("Target data validation passed (%1 data points)") tr("Target data validation passed (%1 data points)")
.arg(m_targetLogLogData[0].size())); .arg(m_targetLogLogData[0].size()));
@ -1403,21 +1439,29 @@ bool nmCalculationAutoFitLM::startAutoFitting()
m_consecutiveFailures = 0; m_consecutiveFailures = 0;
m_initialValues = savedInitialValues; m_initialValues = savedInitialValues;
initializeTraceFile(); initializeTraceFile();
// 开头只展示一次初始参数和总误差,不展示形状误差。
emit logMessageGenerated(tr("========== LM fitting started =========="));
emit logMessageGenerated(tr("Target well: %1").arg(m_targetWellName));
emit logMessageGenerated(tr("Target error: %1").arg(lmLogError(m_targetError)));
// 先用真实求解器评价用户当前模型,供最终精英保护使用。 // 先用真实求解器评价用户当前模型,供最终精英保护使用。
if(!savedInitialValues.isEmpty()) { if(!savedInitialValues.isEmpty()) {
m_userInitialSolution = savedInitialValues; m_userInitialSolution = savedInitialValues;
emit logMessageGenerated(tr("=== Evaluating Initial Solution (Elite Protection) ===")); DEBUG_OUT(tr("=== Evaluating Initial Solution (Elite Protection) ==="));
QString paramStr = tr("Initial parameters: "); const char* names[] = { QT_TR_NOOP("Permeability"), QT_TR_NOOP("Skin"),
QT_TR_NOOP("Wellbore storage"), QT_TR_NOOP("Porosity"), "",
QT_TR_NOOP("Fracture conductivity"), QT_TR_NOOP("Fracture half length") };
const char* units[] = { "mD", "", "m^3/MPa", "", "", "mD.m", "m" };
emit logMessageGenerated(tr("[Initial parameters]"));
for(int i = 0; i < m_userInitialSolution.size(); ++i) { for(int i = 0; i < m_userInitialSolution.size(); ++i) {
paramStr += QString("[%1]=%2 ") const int index = m_enabledParamIndices[i];
.arg(i).arg(m_userInitialSolution[i], 0, 'f', 6); emit logMessageGenerated(QString("%1: %2 %3").arg(tr(names[index]))
.arg(m_userInitialSolution[i], 0, 'g', 10).arg(tr(units[index])));
} }
emit logMessageGenerated(paramStr);
try { try {
emit logMessageGenerated( DEBUG_OUT(
tr("Starting initial solution evaluation...")); tr("Starting initial solution evaluation..."));
QTime initialEvalTimer; QTime initialEvalTimer;
initialEvalTimer.start(); initialEvalTimer.start();
@ -1434,16 +1478,15 @@ bool nmCalculationAutoFitLM::startAutoFitting()
m_globalBestLogLogData = m_userInitialLogLogData; m_globalBestLogLogData = m_userInitialLogLogData;
m_userInitialObjectiveBreakdown = m_lastObjectiveBreakdown; m_userInitialObjectiveBreakdown = m_lastObjectiveBreakdown;
m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown; m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
emit logMessageGenerated(tr("Initial solution evaluation successful")); DEBUG_OUT(tr("Initial solution evaluation successful"));
emit logMessageGenerated( emit logMessageGenerated(tr("Initial total error: %1").arg(lmLogError(m_userInitialFitness)));
tr("Initial Error: %1").arg(m_userInitialFitness, 0, 'e', 4));
emit bestCurveUpdated(m_targetLogLogData, emit bestCurveUpdated(m_targetLogLogData,
m_globalBestLogLogData, m_globalBestLogLogData,
0, 0,
m_globalBestFitness); m_globalBestFitness);
} else { } else {
m_hasValidUserSolution = false; m_hasValidUserSolution = false;
emit logMessageGenerated(tr("Initial solution evaluation failed")); if(!m_shouldStop) emit logMessageGenerated(tr("Initial solution failed; trying the parameter-range midpoint."));
} }
writeTraceRow(-1, writeTraceRow(-1,
@ -1458,7 +1501,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
? &m_userInitialObjectiveBreakdown : nullptr); ? &m_userInitialObjectiveBreakdown : nullptr);
} catch(...) { } catch(...) {
m_hasValidUserSolution = false; m_hasValidUserSolution = false;
emit logMessageGenerated(tr("Exception during initial solution evaluation")); if(!m_shouldStop) emit logMessageGenerated(tr("Initial solution failed; trying the parameter-range midpoint."));
} }
m_initialValues = savedInitialValues; m_initialValues = savedInitialValues;
@ -1491,6 +1534,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
cleanupTemporaryDirectory(); cleanupTemporaryDirectory();
m_isFinalizing = false; m_isFinalizing = false;
m_isRunning = false; m_isRunning = false;
emitRunSummary(false, LM_OPTIMIZATION_FAILED);
emit fittingFinished(false, m_lastError); emit fittingFinished(false, m_lastError);
return false; return false;
} catch(...) { } catch(...) {
@ -1500,6 +1544,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
cleanupTemporaryDirectory(); cleanupTemporaryDirectory();
m_isFinalizing = false; m_isFinalizing = false;
m_isRunning = false; m_isRunning = false;
emitRunSummary(false, LM_OPTIMIZATION_FAILED);
emit fittingFinished(false, m_lastError); emit fittingFinished(false, m_lastError);
return false; return false;
} }
@ -1513,7 +1558,7 @@ bool nmCalculationAutoFitLM::startAutoFitting()
// Stop 只结束优化迭代;从这里开始必须用当前最优参数生成并发布正式快照。 // Stop 只结束优化迭代;从这里开始必须用当前最优参数生成并发布正式快照。
m_isFinalizing = true; m_isFinalizing = true;
emit finalizingStarted(); emit finalizingStarted();
emit logMessageGenerated(tr("Applying optimized parameters to model...")); DEBUG_OUT(tr("Applying optimized parameters to model..."));
applyParametersToDataManager(m_globalBestPosition); applyParametersToDataManager(m_globalBestPosition);
// 裂缝参数会改变网格输入;标记失效后,最终求解任务会基于新快照重建网格。 // 裂缝参数会改变网格输入;标记失效后,最终求解任务会基于新快照重建网格。
@ -1529,28 +1574,28 @@ bool nmCalculationAutoFitLM::startAutoFitting()
dataManager->invalidatePebiGrid(); dataManager->invalidatePebiGrid();
} }
emit logMessageGenerated( DEBUG_OUT(
tr("Running final full-field calculation with optimized parameters...")); tr("Running final full-field calculation with optimized parameters..."));
finalFullSolverExecuted = true; finalFullSolverExecuted = true;
finalFullSolverSucceeded = runFinalFullSolver(); finalFullSolverSucceeded = runFinalFullSolver();
if(finalFullSolverSucceeded) { if(finalFullSolverSucceeded) {
emit logMessageGenerated( DEBUG_OUT(
tr("Final full-field calculation completed successfully")); tr("Final full-field calculation completed successfully"));
} else { } else {
m_lastError = m_lastError =
tr("Optimized parameters were found, but the final full-field calculation failed"); tr("Optimized parameters were found, but the final full-field calculation failed");
emit logMessageGenerated( DEBUG_OUT(
tr("ERROR: Final full-field calculation failed")); tr("ERROR: Final full-field calculation failed"));
} }
saveOptimizationResult(); saveOptimizationResult();
emit logMessageGenerated(tr("=== Optimization Results ===")); DEBUG_OUT(tr("=== Optimization Results ==="));
emit logMessageGenerated( DEBUG_OUT(
tr("Final error: %1").arg(m_globalBestFitness, 0, 'e', 4)); tr("Final error: %1").arg(m_globalBestFitness, 0, 'e', 4));
emit logMessageGenerated( DEBUG_OUT(
tr("Total iterations: %1").arg(m_currentIteration + 1)); tr("Total iterations: %1").arg(m_currentIteration + 1));
emit logMessageGenerated( DEBUG_OUT(
tr("Total evaluations: %1 (successful: %2)") tr("Total evaluations: %1 (successful: %2)")
.arg(m_totalEvaluations).arg(m_successfulEvaluations)); .arg(m_totalEvaluations).arg(m_successfulEvaluations));
@ -1559,24 +1604,24 @@ bool nmCalculationAutoFitLM::startAutoFitting()
finalParams += QString("[%1]=%2 ") finalParams += QString("[%1]=%2 ")
.arg(i).arg(m_globalBestPosition[i], 0, 'f', 6); .arg(i).arg(m_globalBestPosition[i], 0, 'f', 6);
} }
emit logMessageGenerated(finalParams); DEBUG_OUT(finalParams);
if(finalFullSolverExecuted && finalFullSolverSucceeded) { if(finalFullSolverExecuted && finalFullSolverSucceeded) {
emit logMessageGenerated( DEBUG_OUT(
tr("Parameters and full-field results applied successfully to data manager")); tr("Parameters and full-field results applied successfully to data manager"));
} else if(!finalFullSolverExecuted) { } else if(!finalFullSolverExecuted) {
emit logMessageGenerated( DEBUG_OUT(
tr("Optimized parameters applied to data manager")); tr("Optimized parameters applied to data manager"));
} }
} catch(const std::exception& e) { } catch(const std::exception& e) {
finalFullSolverSucceeded = false; finalFullSolverSucceeded = false;
m_lastError = tr("Failed to apply final parameters: %1").arg(e.what()); m_lastError = tr("Failed to apply final parameters: %1").arg(e.what());
emit logMessageGenerated( DEBUG_OUT(
tr("ERROR: Failed to apply final parameters: %1").arg(e.what())); tr("ERROR: Failed to apply final parameters: %1").arg(e.what()));
} catch(...) { } catch(...) {
finalFullSolverSucceeded = false; finalFullSolverSucceeded = false;
m_lastError = tr("Failed to apply final parameters due to unknown error"); m_lastError = tr("Failed to apply final parameters due to unknown error");
emit logMessageGenerated( DEBUG_OUT(
tr("ERROR: Unknown error applying final parameters")); tr("ERROR: Unknown error applying final parameters"));
} }
} }
@ -1591,45 +1636,45 @@ bool nmCalculationAutoFitLM::startAutoFitting()
message = QString(tr("Target achieved. Best error: %1, Iterations: %2")) message = QString(tr("Target achieved. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4) .arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1); .arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING SUCCESSFUL ===")); DEBUG_OUT(tr("=== LM AUTOMATIC FITTING SUCCESSFUL ==="));
} else if(finalReason == LM_TRUE_CONVERGENCE) { } else if(finalReason == LM_TRUE_CONVERGENCE) {
success = true; success = true;
message = QString( message = QString(
tr("Automatic fitting converged to a stable solution. Best error: %1, Iterations: %2")) tr("Automatic fitting converged to a stable solution. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4) .arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1); .arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING CONVERGED ===")); DEBUG_OUT(tr("=== LM AUTOMATIC FITTING CONVERGED ==="));
} else if(finalReason == LM_LOCAL_OPTIMUM) { } else if(finalReason == LM_LOCAL_OPTIMUM) {
success = true; success = true;
message = QString( message = QString(
tr("Automatic fitting reached a local optimum. Best error: %1, Iterations: %2")) tr("Automatic fitting reached a local optimum. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4) .arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1); .arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING - LOCAL OPTIMUM ===")); DEBUG_OUT(tr("=== LM AUTOMATIC FITTING - LOCAL OPTIMUM ==="));
} else if(finalReason == LM_MAX_ITERATIONS) { } else if(finalReason == LM_MAX_ITERATIONS) {
success = true; success = true;
message = QString(tr("Total-stage budget reached. Best error: %1, Cumulative iterations: %2")) message = QString(tr("Total-stage budget reached. Best error: %1, Cumulative iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4) .arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1); .arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING - MAX ITERATIONS ===")); DEBUG_OUT(tr("=== LM AUTOMATIC FITTING - MAX ITERATIONS ==="));
} else if(finalReason == LM_USER_STOPPED) { } else if(finalReason == LM_USER_STOPPED) {
success = true; success = true;
message = QString(tr("Stopped by user. Best error: %1, Iterations: %2")) message = QString(tr("Stopped by user. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4) .arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1); .arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING STOPPED BY USER ===")); DEBUG_OUT(tr("=== LM AUTOMATIC FITTING STOPPED BY USER ==="));
} else if(finalReason == LM_CONSECUTIVE_FAILURES) { } else if(finalReason == LM_CONSECUTIVE_FAILURES) {
message = QString( message = QString(
tr("Automatic fitting failed due to consecutive failures. Best error: %1, Iterations: %2")) tr("Automatic fitting failed due to consecutive failures. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4) .arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1); .arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING FAILED ===")); DEBUG_OUT(tr("=== LM AUTOMATIC FITTING FAILED ==="));
} else { } else {
message = QString( message = QString(
tr("Automatic fitting ended unexpectedly. Best error: %1, Iterations: %2")) tr("Automatic fitting ended unexpectedly. Best error: %1, Iterations: %2"))
.arg(m_globalBestFitness, 0, 'e', 4) .arg(m_globalBestFitness, 0, 'e', 4)
.arg(m_currentIteration + 1); .arg(m_currentIteration + 1);
emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING - UNKNOWN END ===")); DEBUG_OUT(tr("=== LM AUTOMATIC FITTING - UNKNOWN END ==="));
} }
if(!finalFullSolverSucceeded) { if(!finalFullSolverSucceeded) {
@ -1918,8 +1963,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
restoreEvaluationState(current); restoreEvaluationState(current);
emit progressUpdated(-1, current.fitness); emit progressUpdated(-1, current.fitness);
emit logMessageGenerated(tr("=== Starting LM Main Loop ===")); DEBUG_OUT(tr("=== Starting LM Main Loop ==="));
emit logMessageGenerated( DEBUG_OUT(
tr("LM starting point error: %1; evaluation budget per LM phase: %2") tr("LM starting point error: %1; evaluation budget per LM phase: %2")
.arg(current.fitness, 0, 'e', 4) .arg(current.fitness, 0, 'e', 4)
.arg(phaseEvaluationBudget)); .arg(phaseEvaluationBudget));
@ -1931,8 +1976,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
int heightStagnation = 0; int heightStagnation = 0;
double heightScale = 1.0; double heightScale = 1.0;
double heightSlope = -1.0; double heightSlope = -1.0;
int heightTrials = 0;
if(permeabilityColumn >= 0) { if(permeabilityColumn >= 0) {
emit logMessageGenerated(tr("LM stage 1: align curve height using permeability only; no fixed evaluation limit, effective improvement threshold 10%.")); emit logMessageGenerated(tr("[Stage 1: Height alignment]\nAdjust permeability only; accept by height error."));
// 不限制试算次数,由高度对齐、改善停滞及可行步长决定何时结束。 // 不限制试算次数,由高度对齐、改善停滞及可行步长决定何时结束。
while(processPauseAndStop()) { while(processPauseAndStop()) {
const double bias = current.breakdown.verticalCommonBias; const double bias = current.breakdown.verticalCommonBias;
@ -1941,6 +1987,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
if(k <= 0.0 || m_parameterLower[0] <= 0.0 || m_parameterUpper[0] <= m_parameterLower[0]) break; if(k <= 0.0 || m_parameterLower[0] <= 0.0 || m_parameterUpper[0] <= m_parameterLower[0]) break;
const double logRange = qLn(m_parameterUpper[0]) - qLn(m_parameterLower[0]); const double logRange = qLn(m_parameterUpper[0]) - qLn(m_parameterLower[0]);
const double change = qBound(-0.30 * logRange, -bias / heightSlope * heightScale, 0.30 * logRange); const double change = qBound(-0.30 * logRange, -bias / heightSlope * heightScale, 0.30 * logRange);
const AutoFitObjectiveBreakdownLM previous = current.breakdown;
++heightTrials;
TrustRegionEvaluation candidate; TrustRegionEvaluation candidate;
candidate.parameters = current.parameters; candidate.parameters = current.parameters;
candidate.parameters[permeabilityColumn] = qBound(m_parameterLower[0], k * qExp(change), m_parameterUpper[0]); candidate.parameters[permeabilityColumn] = qBound(m_parameterLower[0], k * qExp(change), m_parameterUpper[0]);
@ -1970,10 +2018,16 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
heightBaseline = current.breakdown.verticalLoss; heightBaseline = current.breakdown.verticalLoss;
heightStagnation = 0; heightStagnation = 0;
} else ++heightStagnation; } else ++heightStagnation;
emit logMessageGenerated(tr("Permeability alignment: k=%1, height error=%2, shape error=%3, result=%4") if(candidate.valid) {
.arg(candidate.parameters[permeabilityColumn], 0, 'g', 6) emit logMessageGenerated(tr("Trial %1 | %2 | Height error: %3 -> %4")
.arg(candidate.breakdown.verticalLoss, 0, 'e', 4).arg(candidate.breakdown.shapeLoss, 0, 'e', 4) .arg(heightTrials).arg(accepted ? tr("accepted") : tr("rejected"))
.arg(accepted ? tr("accepted") : tr("rejected"))); .arg(lmLogError(previous.verticalLoss)).arg(lmLogError(candidate.breakdown.verticalLoss)));
if(!accepted) emit logMessageGenerated(candidate.breakdown.verticalReliable
? tr("Height error did not decrease; keep the last accepted result and reduce the step.")
: tr("Pressure and derivative height directions conflict; keep the last accepted result and reduce the step."));
} else {
emit logMessageGenerated(tr("Trial %1 | Solver failed; keep the last accepted result and reduce the step.").arg(heightTrials));
}
} }
QString heightReason = tr("no feasible permeability step or step too small"); QString heightReason = tr("no feasible permeability step or step too small");
if(m_shouldStop) heightReason = tr("stopped by user"); if(m_shouldStop) heightReason = tr("stopped by user");
@ -1982,9 +2036,10 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
else if(heightStagnation >= 2) heightReason = tr("2 consecutive steps without effective height improvement"); else if(heightStagnation >= 2) heightReason = tr("2 consecutive steps without effective height improvement");
else if(current.parameters[permeabilityColumn] <= m_parameterLower[0] || else if(current.parameters[permeabilityColumn] <= m_parameterLower[0] ||
current.parameters[permeabilityColumn] >= m_parameterUpper[0]) heightReason = tr("permeability reached its bound"); current.parameters[permeabilityColumn] >= m_parameterUpper[0]) heightReason = tr("permeability reached its bound");
emit logMessageGenerated(tr("Permeability alignment ended: %1").arg(heightReason)); emit logMessageGenerated(tr("Height alignment ended: %1").arg(heightReason));
} }
if(permeabilityColumn < 0) emit logMessageGenerated(tr("Height alignment skipped: permeability is not selected."));
if(m_shouldStop) return LM_USER_STOPPED; if(m_shouldStop) return LM_USER_STOPPED;
// 原逐参数第二阶段已移除;同一个联合 LM 先匹配整体形状,再匹配总误差。 // 原逐参数第二阶段已移除;同一个联合 LM 先匹配整体形状,再匹配总误差。
@ -2002,7 +2057,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
restoreEvaluationState(current); restoreEvaluationState(current);
}; };
double effectiveImprovementBaseline = stageError(current); double effectiveImprovementBaseline = stageError(current);
emit logMessageGenerated(tr("LM sampling: %1 points (%2 intervals per log-time decade).") DEBUG_OUT(tr("LM sampling: %1 points (%2 intervals per log-time decade).")
.arg(current.breakdown.residualVector.size() / 2).arg(kAutoFitIntervalsPerDecade)); .arg(current.breakdown.residualVector.size() / 2).arg(kAutoFitIntervalsPerDecade));
auto registerEffectiveImprovement = [&](double fitness) -> bool { auto registerEffectiveImprovement = [&](double fitness) -> bool {
@ -2048,7 +2103,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
return false; return false;
}; };
emit logMessageGenerated( DEBUG_OUT(
tr("LM effective improvement threshold: max(%1, %2% of baseline error); " tr("LM effective improvement threshold: max(%1, %2% of baseline error); "
"%3 consecutive ineffective steps trigger convergence confirmation") "%3 consecutive ineffective steps trigger convergence confirmation")
.arg(effectiveAbsoluteImprovement, 0, 'e', 2) .arg(effectiveAbsoluteImprovement, 0, 'e', 2)
@ -2212,9 +2267,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
0, 0,
shapeStage ? "shape_accepted_cached_probe" : "total_accepted_cached_probe", shapeStage ? "shape_accepted_cached_probe" : "total_accepted_cached_probe",
&current.breakdown); &current.breakdown);
emit logMessageGenerated( emitTrialLog(tr("Sensitivity trial"), base.breakdown, current.breakdown, true, true, shapeStage);
tr("Sensitivity probe accepted: current objective error=%1")
.arg(stageError(current), 0, 'e', 4));
} else { } else {
restoreEvaluationState(current); restoreEvaluationState(current);
} }
@ -2258,9 +2311,9 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
: QT_TR_NOOP("full sensitivity at total-stage entry"); : QT_TR_NOOP("full sensitivity at total-stage entry");
emit progressUpdated(0, current.fitness); emit progressUpdated(0, current.fitness);
emit logMessageGenerated(shapeStage emit logMessageGenerated(shapeStage
? tr("Joint LM shape phase: adjust all selected parameters by full shape error.") ? tr("[Stage 2: Joint LM - Shape]\nAdjust selected parameters; accept by shape error.")
: tr("Joint LM total phase: adjust all selected parameters by total error.")); : tr("[Stage 2: Joint LM - Total error]\nAdjust selected parameters; accept by total error."));
emit logMessageGenerated(tr("LM phase budget: %1 iterations, %2 evaluations.") DEBUG_OUT(tr("LM phase budget: %1 iterations, %2 evaluations.")
.arg(m_maxIterations).arg(phaseEvaluationBudget)); .arg(m_maxIterations).arg(phaseEvaluationBudget));
writeTraceRow(completedIterations, -1, "stage_switch", current.parameters, current.fitness, writeTraceRow(completedIterations, -1, "stage_switch", current.parameters, current.fitness,
true, 0, shapeStage ? "height_to_shape_lm" : "shape_to_total", &current.breakdown); true, 0, shapeStage ? "height_to_shape_lm" : "shape_to_total", &current.breakdown);
@ -2276,7 +2329,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
break; break;
} }
if(rebuildRequested) { if(rebuildRequested) {
emit logMessageGenerated(tr("Rebuilding sensitivity model: %1").arg(tr(rebuildReason))); emit logMessageGenerated(tr("Calculating sensitivity..."));
writeTraceRow(m_currentIteration, -1, "sensitivity_rebuild", current.parameters, writeTraceRow(m_currentIteration, -1, "sensitivity_rebuild", current.parameters,
current.fitness, true, 0, rebuildReason, &current.breakdown); current.fitness, true, 0, rebuildReason, &current.breakdown);
if(!rebuildSensitivity()) { if(!rebuildSensitivity()) {
@ -2333,6 +2386,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
// 当前阶段没有可行下降步时,收缩半径并进入原有重建/收敛处理。 // 当前阶段没有可行下降步时,收缩半径并进入原有重建/收敛处理。
if(selectedColumns.isEmpty()) { if(selectedColumns.isEmpty()) {
emit logMessageGenerated(tr("Iteration %1 | No feasible descent step; keep the last accepted result.").arg(phaseIterations));
if(trustRadius <= minimumTrustRadius * 1.01 && if(trustRadius <= minimumTrustRadius * 1.01 &&
modelRebuiltAtMinimumRadius) { modelRebuiltAtMinimumRadius) {
stopReason = LM_LOCAL_OPTIMUM; stopReason = LM_LOCAL_OPTIMUM;
@ -2379,6 +2433,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
} }
if(!candidate.valid) { if(!candidate.valid) {
emitTrialLog(tr("Iteration %1").arg(phaseIterations), current.breakdown, candidate.breakdown,
false, false, shapeStage);
// 求解失败的候选不能改变 current。先完整恢复上一个已接受参数和 // 求解失败的候选不能改变 current。先完整恢复上一个已接受参数和
// 对应误差快照,再缩小信赖域;连续失败达到上限才终止整个拟合。 // 对应误差快照,再缩小信赖域;连续失败达到上限才终止整个拟合。
++consecutiveSolverFailures; ++consecutiveSolverFailures;
@ -2493,14 +2549,8 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
: "rejected_" + selectionName, : "rejected_" + selectionName,
&candidate.breakdown); &candidate.breakdown);
QString componentDisplayName = shapeStage ? tr("shape deviation") : tr("total error"); emitTrialLog(tr("Iteration %1").arg(phaseIterations), oldBreakdown, candidate.breakdown,
emit logMessageGenerated( true, accepted, shapeStage);
tr("Iteration %1: focus=%2, parameters=%3, error=%4, result=%5")
.arg(iteration + 1)
.arg(componentDisplayName)
.arg(selectedColumns.size())
.arg(stageError(candidate), 0, 'e', 4)
.arg(accepted ? tr("accepted") : tr("rejected")));
emit progressUpdated(phaseIterations, m_globalBestFitness); emit progressUpdated(phaseIterations, m_globalBestFitness);
const bool effectiveImprovement = registerEffectiveImprovement(stageError(current)); const bool effectiveImprovement = registerEffectiveImprovement(stageError(current));
@ -2528,9 +2578,11 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
break; break;
// 形状达标、确认停滞或用完额度后,保留当前参数进入总误差段。 // 形状达标、确认停滞或用完额度后,保留当前参数进入总误差段。
if(shapeStage) { if(shapeStage) {
emit logMessageGenerated(tr("Shape LM phase ended: %1").arg(getStopReasonDescription(stopReason))); emit logMessageGenerated(tr("Shape phase ended: %1").arg(getStopReasonDescription(stopReason)));
writeTraceRow(m_currentIteration, -1, "lm_phase_end", current.parameters, current.fitness, writeTraceRow(m_currentIteration, -1, "lm_phase_end", current.parameters, current.fitness,
true, 0, QString("shape_stop_reason_%1").arg(static_cast<int>(stopReason)), &current.breakdown); true, 0, QString("shape_stop_reason_%1").arg(static_cast<int>(stopReason)), &current.breakdown);
} else {
emit logMessageGenerated(tr("Total-error phase ended: %1").arg(getStopReasonDescription(stopReason)));
} }
} }
@ -2538,7 +2590,7 @@ StopReasonLM nmCalculationAutoFitLM::runTrustRegionFitting()
m_currentIteration = completedIterations - 1; m_currentIteration = completedIterations - 1;
} }
restoreEvaluationState(current); restoreEvaluationState(current);
emit logMessageGenerated(tr("Adaptive fitting counts: %1 shape LM iterations, %2 total LM iterations, %3 total evaluations.") DEBUG_OUT(tr("Adaptive fitting counts: %1 shape LM iterations, %2 total LM iterations, %3 total evaluations.")
.arg(shapeIterations).arg(totalIterations).arg(m_totalEvaluations)); .arg(shapeIterations).arg(totalIterations).arg(m_totalEvaluations));
if(m_shouldStop) { if(m_shouldStop) {
@ -3001,17 +3053,17 @@ void nmCalculationAutoFitLM::validateAndProtectFinalResult()
// 最终精英保护只阻止无效结果或真正变差的结果。任何真实误差下降都应保留, // 最终精英保护只阻止无效结果或真正变差的结果。任何真实误差下降都应保留,
// 不能再用固定百分比门槛把已经找到的更优解恢复成初始值。 // 不能再用固定百分比门槛把已经找到的更优解恢复成初始值。
if(!m_hasValidUserSolution) { if(!m_hasValidUserSolution) {
emit logMessageGenerated(tr("No initial solution for elite protection")); DEBUG_OUT(tr("No initial solution for elite protection"));
return; return;
} }
emit logMessageGenerated(tr("=== Final Result Validation (Elite Protection) ===")); DEBUG_OUT(tr("=== Final Result Validation (Elite Protection) ==="));
// 使用已有的评估结果 // 使用已有的评估结果
double finalFitness = m_globalBestFitness; double finalFitness = m_globalBestFitness;
double initialFitness = m_userInitialFitness; double initialFitness = m_userInitialFitness;
emit logMessageGenerated(tr("Comparing results: Initial=%1, Final=%2") DEBUG_OUT(tr("Comparing results: Initial=%1, Final=%2")
.arg(initialFitness, 0, 'e', 4).arg(finalFitness, 0, 'e', 4)); .arg(initialFitness, 0, 'e', 4).arg(finalFitness, 0, 'e', 4));
bool finalValid = isFiniteNumber(finalFitness) && bool finalValid = isFiniteNumber(finalFitness) &&
@ -3024,15 +3076,15 @@ void nmCalculationAutoFitLM::validateAndProtectFinalResult()
double improvement = initialFitness - finalFitness; double improvement = initialFitness - finalFitness;
double relativeImprovement = double relativeImprovement =
improvement / qMax(1.0e-10, qAbs(initialFitness)); improvement / qMax(1.0e-10, qAbs(initialFitness));
emit logMessageGenerated(tr("Improvement: %1 (%2%)") DEBUG_OUT(tr("Improvement: %1 (%2%)")
.arg(improvement, 0, 'e', 4) .arg(improvement, 0, 'e', 4)
.arg(relativeImprovement * 100, 0, 'f', 2)); .arg(relativeImprovement * 100, 0, 'f', 2));
} }
if(!finalValid || finalFitness > initialFitness) { if(!finalValid || finalFitness > initialFitness) {
emit logMessageGenerated( DEBUG_OUT(
tr("Elite protection triggered: final result is invalid or worse than initial")); tr("Elite protection triggered: final result is invalid or worse than initial"));
emit logMessageGenerated(tr("Restoring initial solution as final result")); emit logMessageGenerated(tr("Final total error did not improve; restoring the initial result."));
m_globalBestFitness = initialFitness; m_globalBestFitness = initialFitness;
m_globalBestPosition = m_userInitialSolution; m_globalBestPosition = m_userInitialSolution;
@ -3040,9 +3092,9 @@ void nmCalculationAutoFitLM::validateAndProtectFinalResult()
m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown; m_globalBestObjectiveBreakdown = m_userInitialObjectiveBreakdown;
emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness); emit bestCurveUpdated(m_targetLogLogData, m_globalBestLogLogData, m_currentIteration + 1, m_globalBestFitness);
emit logMessageGenerated(tr("Initial solution restored successfully")); DEBUG_OUT(tr("Initial solution restored successfully"));
} else { } else {
emit logMessageGenerated( DEBUG_OUT(
tr("Final result validated - solution is not worse than initial")); tr("Final result validated - solution is not worse than initial"));
} }
} }
@ -4129,6 +4181,7 @@ QVector<QVector<double>> nmCalculationAutoFitLM::runSolverDll()
DEBUG_OUT("Starting DLL solver execution..."); DEBUG_OUT("Starting DLL solver execution...");
// 异步执行 DLL 任务。循环等待期间持续 processEvents,保证界面不会完全卡死。 // 异步执行 DLL 任务。循环等待期间持续 processEvents,保证界面不会完全卡死。
connect(dllTask, SIGNAL(solverInvoked()), this, SLOT(recordSolverCall()), Qt::DirectConnection);
dllTask->start(); dllTask->start();
// 等待完成。最大等待 1 小时;用户停止使用任务已有的协作取消接口。 // 等待完成。最大等待 1 小时;用户停止使用任务已有的协作取消接口。
@ -4296,6 +4349,7 @@ bool nmCalculationAutoFitLM::runFinalFullSolver()
++m_evaluationInProgress; ++m_evaluationInProgress;
nmCalculationDllPebiSolverTask dllTask(m_tempDirectory, dataManager); nmCalculationDllPebiSolverTask dllTask(m_tempDirectory, dataManager);
connect(&dllTask, SIGNAL(solverInvoked()), this, SLOT(recordSolverCall()), Qt::DirectConnection);
dllTask.start(); dllTask.start();
const int maxWait = 3600000; const int maxWait = 3600000;
@ -4347,7 +4401,7 @@ QString nmCalculationAutoFitLM::getStopReasonDescription(StopReasonLM reason) co
return tr("Local optimum detected"); return tr("Local optimum detected");
case LM_MAX_ITERATIONS: case LM_MAX_ITERATIONS:
return tr("Total-stage iteration or evaluation budget reached"); return tr("Iteration or evaluation budget reached");
case LM_USER_STOPPED: case LM_USER_STOPPED:
return tr("Stopped by user request"); return tr("Stopped by user request");

@ -1382,6 +1382,8 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
} }
reportStage(tr("Solving model..."), 0, 0); reportStage(tr("Solving model..."), 0, 0);
// 只统计进入数值求解器的调用,网格准备失败和缓存复用不计入。
emit solverInvoked();
pfnModel(oModelOutput, pfnModel(oModelOutput,
oModelInput, oModelInput,
m_pInputSnapshot->m_oGridInput.m_sLicensePath m_pInputSnapshot->m_oGridInput.m_sLicensePath

@ -612,6 +612,8 @@ void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitLM* autoFitter
m_autoFitterPSO = nullptr; m_autoFitterPSO = nullptr;
m_autoFitterLM = autoFitter; m_autoFitterLM = autoFitter;
m_algorithmName = "LM"; m_algorithmName = "LM";
// LM 日志由优化器统一组织,清除通用初始化提示。
logTextEdit->clear();
algorithmTypeValue->setText(m_algorithmName); algorithmTypeValue->setText(m_algorithmName);
algorithmTypeValue->setStyleSheet("QLabel { color: blue; font-weight: bold; }"); algorithmTypeValue->setStyleSheet("QLabel { color: blue; font-weight: bold; }");
@ -629,7 +631,6 @@ void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitLM* autoFitter
this, SLOT(onBestCurveUpdated(QVector<QVector<double> >,QVector<QVector<double> >,int,double))); this, SLOT(onBestCurveUpdated(QVector<QVector<double> >,QVector<QVector<double> >,int,double)));
stopButton->setEnabled(true); stopButton->setEnabled(true);
addLogMessage(tr("%1 auto fitting started").arg(m_algorithmName));
} }
} }
@ -646,7 +647,7 @@ void nmWxAutomaticfittingStart::setFittingParameters(int maxIterations, double t
progressBar->setRange(0, maxIterations); progressBar->setRange(0, maxIterations);
const QString algorithmName = m_algorithmName; const QString algorithmName = m_algorithmName;
addLogMessage(tr("%1 fitting parameters set: MaxIterations=%2, TargetAccuracy=%3, TargetWell=%4") if(!m_autoFitterLM) addLogMessage(tr("%1 fitting parameters set: MaxIterations=%2, TargetAccuracy=%3, TargetWell=%4")
.arg(algorithmName).arg(maxIterations).arg(formatScientific(targetError)).arg(wellName)); .arg(algorithmName).arg(maxIterations).arg(formatScientific(targetError)).arg(wellName));
} }
@ -657,7 +658,7 @@ void nmWxAutomaticfittingStart::markFittingStarted()
const QString algorithmName = m_algorithmName; const QString algorithmName = m_algorithmName;
QString timestamp = m_startTime.toString("yyyy-MM-dd hh:mm:ss"); QString timestamp = m_startTime.toString("yyyy-MM-dd hh:mm:ss");
addLogMessage(tr("=== %1 Fitting Session Started at %2 ===") if(!m_autoFitterLM) addLogMessage(tr("=== %1 Fitting Session Started at %2 ===")
.arg(algorithmName) .arg(algorithmName)
.arg(timestamp)); .arg(timestamp));
} }
@ -670,7 +671,7 @@ void nmWxAutomaticfittingStart::setSelectedParameters(const QStringList& paramet
updateParameterTable(); updateParameterTable();
const QString algorithmName = m_algorithmName; const QString algorithmName = m_algorithmName;
addLogMessage(tr("%1 selected parameters: %2").arg(algorithmName).arg(parameterNames.join(", "))); if(!m_autoFitterLM) addLogMessage(tr("%1 selected parameters: %2").arg(algorithmName).arg(parameterNames.join(", ")));
} }
void nmWxAutomaticfittingStart::setTargetLogLogData(const QVector<QVector<double> >& targetData) void nmWxAutomaticfittingStart::setTargetLogLogData(const QVector<QVector<double> >& targetData)
@ -710,7 +711,7 @@ void nmWxAutomaticfittingStart::onFittingProgress(int iteration, double fitness)
bestComfortValue->setText(formatScientific(fitness)); bestComfortValue->setText(formatScientific(fitness));
bestComfortValue->setStyleSheet("QLabel { color: green; font-weight: bold; }"); bestComfortValue->setStyleSheet("QLabel { color: green; font-weight: bold; }");
addLogMessage(tr("Better solution found: %1").arg(formatScientific(fitness))); if(!m_autoFitterLM) addLogMessage(tr("Better solution found: %1").arg(formatScientific(fitness)));
// 更新参数表格 // 更新参数表格
updateParameterTable(); updateParameterTable();
@ -732,7 +733,7 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
// 区分正常完成和用户停止 // 区分正常完成和用户停止
if(message.contains("stopped by user", Qt::CaseInsensitive)) { if(message.contains("stopped by user", Qt::CaseInsensitive)) {
addLogMessage(tr("%1 fitting stopped by user: %2").arg(algorithmName).arg(message)); if(!m_autoFitterLM) addLogMessage(tr("%1 fitting stopped by user: %2").arg(algorithmName).arg(message));
// 更新进度条显示停止状态 // 更新进度条显示停止状态
progressBar->setFormat(tr("Stopped: 100%")); progressBar->setFormat(tr("Stopped: 100%"));
@ -741,7 +742,7 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
"QProgressBar::chunk { background-color: #FF9800; }" // 橙色表示停止 "QProgressBar::chunk { background-color: #FF9800; }" // 橙色表示停止
); );
} else if(message.contains("target achieved", Qt::CaseInsensitive)) { } else if(message.contains("target achieved", Qt::CaseInsensitive)) {
addLogMessage(tr("%1 fitting completed successfully - target achieved: %2").arg(algorithmName).arg(message)); if(!m_autoFitterLM) addLogMessage(tr("%1 fitting completed successfully - target achieved: %2").arg(algorithmName).arg(message));
// 更新进度条显示成功状态 // 更新进度条显示成功状态
progressBar->setFormat(tr("Target Achieved: 100%")); progressBar->setFormat(tr("Target Achieved: 100%"));
@ -750,7 +751,7 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
"QProgressBar::chunk { background-color: #4CAF50; }" // 绿色表示成功 "QProgressBar::chunk { background-color: #4CAF50; }" // 绿色表示成功
); );
} else { } else {
addLogMessage(tr("%1 fitting completed successfully: %2").arg(algorithmName).arg(message)); if(!m_autoFitterLM) addLogMessage(tr("%1 fitting completed successfully: %2").arg(algorithmName).arg(message));
// 更新进度条显示完成状态 // 更新进度条显示完成状态
progressBar->setFormat(tr("Completed: 100%")); progressBar->setFormat(tr("Completed: 100%"));
@ -761,7 +762,7 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
} }
} else { } else {
// 真正的失败情况 // 真正的失败情况
addLogMessage(tr("%1 fitting failed: %2").arg(algorithmName).arg(message)); if(!m_autoFitterLM) addLogMessage(tr("%1 fitting failed: %2").arg(algorithmName).arg(message));
// 更新进度条显示失败状态 // 更新进度条显示失败状态
int currentProgress = progressBar->value(); int currentProgress = progressBar->value();
@ -786,7 +787,7 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
// 添加完成时间戳 // 添加完成时间戳
QDateTime endTime = QDateTime::currentDateTime(); QDateTime endTime = QDateTime::currentDateTime();
QString timestamp = endTime.toString("yyyy-MM-dd hh:mm:ss"); QString timestamp = endTime.toString("yyyy-MM-dd hh:mm:ss");
addLogMessage(tr("=== %1 Fitting Session Ended at %2 ===") if(!m_autoFitterLM) addLogMessage(tr("=== %1 Fitting Session Ended at %2 ===")
.arg(algorithmName).arg(timestamp)); .arg(algorithmName).arg(timestamp));
// 如果有开始时间,则计算总耗时 // 如果有开始时间,则计算总耗时
@ -800,12 +801,13 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m
? m_autoFitterLM->getTotalEvaluations() ? m_autoFitterLM->getTotalEvaluations()
: (m_autoFitterPSO ? m_autoFitterPSO->getTotalEvaluations() : 0); : (m_autoFitterPSO ? m_autoFitterPSO->getTotalEvaluations() : 0);
// 同时显示总秒数、分秒和本轮真实求解器调用次数。 // LM 的调用次数由优化器汇总,这里只在结束时显示一次总耗时。
if(m_autoFitterLM) {
addLogMessage(tr("Total time: %1 min %2 s").arg(minutes).arg(remainSec, 0, 'f', 1));
} else {
addLogMessage(tr("Total fitting time: %1 s (%2 min %3 s), total solver calls: %4") addLogMessage(tr("Total fitting time: %1 s (%2 min %3 s), total solver calls: %4")
.arg(seconds, 0, 'f', 2) .arg(seconds, 0, 'f', 2).arg(minutes).arg(remainSec, 0, 'f', 2).arg(totalEvaluations));
.arg(minutes) }
.arg(remainSec, 0, 'f', 2)
.arg(totalEvaluations));
} }
} }
@ -840,7 +842,7 @@ void nmWxAutomaticfittingStart::onStopButtonClicked()
} else if (m_autoFitterLM) { } else if (m_autoFitterLM) {
m_autoFitterLM->stopFitting(); m_autoFitterLM->stopFitting();
} }
addLogMessage(tr("User requested to stop %1 fitting").arg(algorithmName)); if(!m_autoFitterLM) addLogMessage(tr("User requested to stop %1 fitting").arg(algorithmName));
} }
} }
} }

Loading…
Cancel
Save