From 27256f9c591563a057dde1fa9312c6391f66c5b7 Mon Sep 17 00:00:00 2001 From: lvjunjie Date: Fri, 4 Sep 2026 16:19:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(numerical):=20=E4=BF=AE=E5=A4=8D=E5=81=9C?= =?UTF-8?q?=E6=AD=A2=E6=8B=9F=E5=90=88=E5=90=8E=E7=BB=93=E6=9E=9C=E6=9C=AA?= =?UTF-8?q?=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LM 和 PSO 停止优化后保留当前最优参数并执行最终完整求解 - 最终求解成功后提交正式结果快照并刷新主界面 - 增加最终结果生成状态,避免重复停止及未提交快照时误报成功 --- .../nmCalculation/nmCalculationAutoFitLM.h | 3 + .../nmCalculation/nmCalculationAutoFitPSO.h | 3 + .../nmSubWxs/nmWxAutomaticFittingStart.h | 2 + .../nmCalculation/nmCalculationAutoFitLM.cpp | 60 +++++++++---------- .../nmCalculation/nmCalculationAutoFitPSO.cpp | 20 ++++++- .../nmSubWxs/nmWxAutomaticFittingStart.cpp | 26 ++++++++ 6 files changed, 82 insertions(+), 32 deletions(-) diff --git a/Include/nmNum/nmCalculation/nmCalculationAutoFitLM.h b/Include/nmNum/nmCalculation/nmCalculationAutoFitLM.h index 74eac3a1..8473904e 100644 --- a/Include/nmNum/nmCalculation/nmCalculationAutoFitLM.h +++ b/Include/nmNum/nmCalculation/nmCalculationAutoFitLM.h @@ -90,6 +90,8 @@ signals: QVector > bestData, int iteration, double fitness); + /** @brief 优化迭代结束,开始用当前最优参数生成正式结果。 */ + void finalizingStarted(); void logMessageGenerated(const QString& message); private: @@ -149,6 +151,7 @@ private: private: bool m_isRunning; bool m_shouldStop; + bool m_isFinalizing; int m_currentIteration; QString m_lastError; diff --git a/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h b/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h index 337f78ae..f4e9b9b5 100644 --- a/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h +++ b/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h @@ -119,6 +119,8 @@ signals: QVector > bestData, int iteration, double fitness); + /** @brief 优化迭代结束,开始用当前最优参数生成正式结果。 */ + void finalizingStarted(); signals: @@ -274,6 +276,7 @@ private: // ===== 运行状态 ===== bool m_isRunning; // 当前是否有一次自动拟合正在运行。 bool m_shouldStop; // 用户停止标志;主循环和求解器等待循环会定期检查它。 + bool m_isFinalizing; // 正在用最优参数生成正式结果,此阶段不再接受停止请求。 bool m_isPaused; // 预留暂停标志;主循环中有暂停等待逻辑。 int m_currentIteration; // 当前 PSO 迭代序号,从 0 开始。 QString m_lastError; // 最近一次失败原因,供 UI 展示或日志排查。 diff --git a/Include/nmNum/nmSubWxs/nmWxAutomaticFittingStart.h b/Include/nmNum/nmSubWxs/nmWxAutomaticFittingStart.h index 45aa7457..0697b6f1 100644 --- a/Include/nmNum/nmSubWxs/nmWxAutomaticFittingStart.h +++ b/Include/nmNum/nmSubWxs/nmWxAutomaticFittingStart.h @@ -93,6 +93,7 @@ public: public slots: void onFittingProgress(int iteration, double fitness); void onFittingFinished(bool success, const QString& message); + void onFinalizingStarted(); void onStopButtonClicked(); void onLogMessageReceived(const QString& message); void onBestCurveUpdated(QVector > targetData, @@ -163,6 +164,7 @@ private: QString m_wellName; QStringList m_selectedParameters; bool m_isFinished; + bool m_isFinalizing; double m_bestFitnessEver; QDateTime m_startTime; diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp index ee17e8eb..8422c13c 100644 --- a/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitLM.cpp @@ -470,6 +470,7 @@ nmCalculationAutoFitLM::nmCalculationAutoFitLM(QObject* parent) : QObject(parent) , m_isRunning(false) , m_shouldStop(false) + , m_isFinalizing(false) , m_currentIteration(0) , m_globalBestFitness(1e10) , m_maxIterations(100) @@ -568,6 +569,12 @@ void nmCalculationAutoFitLM::setTargetLogLogData(const QVector > void nmCalculationAutoFitLM::stopFitting() { + if(m_isFinalizing) { + emit logMessageGenerated( + tr("The final full-field calculation is already running")); + return; + } + // 用户点击停止时只设置请求标志,让 LM 主循环和求解器等待逻辑自然退出。 if(m_isRunning) { emit logMessageGenerated(tr("=== User Stop Request Received ===")); @@ -659,6 +666,7 @@ void nmCalculationAutoFitLM::resetOptimizer() m_userInitialFitness = 1e10; m_hasValidUserSolution = false; m_traceMetaFilePath.clear(); + m_isFinalizing = false; DEBUG_OUT("LM optimizer reset"); } @@ -1132,6 +1140,7 @@ bool nmCalculationAutoFitLM::startAutoFitting() } m_isRunning = true; m_shouldStop = false; + m_isFinalizing = false; m_currentIteration = 0; m_consecutiveFailures = 0; m_initialValues = savedInitialValues; @@ -1213,7 +1222,8 @@ bool nmCalculationAutoFitLM::startAutoFitting() &m_globalBestObjectiveBreakdown); } - if(m_globalBestFitness < m_targetError) { + if(finalReason != LM_USER_STOPPED && + m_globalBestFitness < m_targetError) { finalReason = LM_TARGET_ACHIEVED; } } catch(const std::exception& e) { @@ -1221,6 +1231,7 @@ bool nmCalculationAutoFitLM::startAutoFitting() emit logMessageGenerated(tr("CRITICAL ERROR: %1").arg(e.what())); closeTraceFile(); cleanupTemporaryDirectory(); + m_isFinalizing = false; m_isRunning = false; emit fittingFinished(false, m_lastError); return false; @@ -1229,16 +1240,21 @@ bool nmCalculationAutoFitLM::startAutoFitting() emit logMessageGenerated(tr("CRITICAL ERROR: Unknown exception in automatic fitting")); closeTraceFile(); cleanupTemporaryDirectory(); + m_isFinalizing = false; m_isRunning = false; emit fittingFinished(false, m_lastError); return false; } - bool finalFullSolverSucceeded = true; + // 只有最终完整求解实际执行并提交快照后才能置为成功。 + bool finalFullSolverSucceeded = false; bool finalFullSolverExecuted = false; if(!m_globalBestPosition.isEmpty()) { try { + // Stop 只结束优化迭代;从这里开始必须用当前最优参数生成并发布正式快照。 + m_isFinalizing = true; + emit finalizingStarted(); emit logMessageGenerated(tr("Applying optimized parameters to model...")); applyParametersToDataManager(m_globalBestPosition); @@ -1255,29 +1271,19 @@ bool nmCalculationAutoFitLM::startAutoFitting() dataManager->invalidatePebiGrid(); } - if(m_shouldStop) { + emit logMessageGenerated( + tr("Running final full-field calculation with optimized parameters...")); + finalFullSolverExecuted = true; + finalFullSolverSucceeded = runFinalFullSolver(); + + if(finalFullSolverSucceeded) { emit logMessageGenerated( - tr("Final full-field calculation skipped after user stop")); + tr("Final full-field calculation completed successfully")); } else { + m_lastError = + tr("Optimized parameters were found, but the final full-field calculation failed"); emit logMessageGenerated( - tr("Running final full-field calculation with optimized parameters...")); - finalFullSolverExecuted = true; - finalFullSolverSucceeded = runFinalFullSolver(); - - if(finalFullSolverSucceeded) { - emit logMessageGenerated( - tr("Final full-field calculation completed successfully")); - } else if(m_shouldStop) { - finalFullSolverExecuted = false; - finalFullSolverSucceeded = true; - emit logMessageGenerated( - tr("Final full-field calculation stopped by user")); - } else { - m_lastError = - tr("Optimized parameters were found, but the final full-field calculation failed"); - emit logMessageGenerated( - tr("ERROR: Final full-field calculation failed")); - } + tr("ERROR: Final full-field calculation failed")); } saveOptimizationResult(); @@ -1317,6 +1323,7 @@ bool nmCalculationAutoFitLM::startAutoFitting() } } + m_isFinalizing = false; m_isRunning = false; bool success = false; QString message; @@ -1349,7 +1356,7 @@ bool nmCalculationAutoFitLM::startAutoFitting() emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING - MAX ITERATIONS ===")); } else if(finalReason == LM_USER_STOPPED) { success = true; - message = QString(tr("Best error: %1, Iterations: %2")) + message = QString(tr("Stopped by user. Best error: %1, Iterations: %2")) .arg(m_globalBestFitness, 0, 'e', 4) .arg(m_currentIteration + 1); emit logMessageGenerated(tr("=== LM AUTOMATIC FITTING STOPPED BY USER ===")); @@ -4138,13 +4145,6 @@ bool nmCalculationAutoFitLM::runFinalFullSolver() break; } - if(m_shouldStop) { - DEBUG_OUT("Final full-field solver terminated by user"); - dllTask.terminate(); - dllTask.wait(2000); - --m_evaluationInProgress; - return false; - } } if(dllTask.isRunning()) { diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp index c08c2f5e..95badd35 100644 --- a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp @@ -447,6 +447,7 @@ nmCalculationAutoFitPSO::nmCalculationAutoFitPSO(QObject* parent) : QObject(parent) , m_isRunning(false) , m_shouldStop(false) + , m_isFinalizing(false) , m_isPaused(false) , m_currentIteration(0) , m_globalBestFitness(1e10) @@ -612,6 +613,12 @@ void nmCalculationAutoFitPSO::setTargetLogLogData(const QVector void nmCalculationAutoFitPSO::stopFitting() { + if(m_isFinalizing) { + emit logMessageGenerated( + tr("The final full-field calculation is already running")); + return; + } + // 用户点击停止时走这里。停止策略是“请求式停止”: // 先置 m_shouldStop,让主循环/求解器等待逻辑自然退出;短时间内还在评价时再重置计数。 // 这样可以减少 DLL 任务被硬中断导致的数据状态残留。 @@ -718,6 +725,7 @@ void nmCalculationAutoFitPSO::resetOptimizer() m_particleStagnationHistory.clear(); m_traceMetaFilePath.clear(); m_surrogateContextFilePath.clear(); + m_isFinalizing = false; resetRunSummary(); DEBUG_OUT("Optimizer reset"); @@ -2834,6 +2842,7 @@ bool nmCalculationAutoFitPSO::startAutoFitting() } m_isRunning = true; m_shouldStop = false; + m_isFinalizing = false; m_isPaused = false; m_currentIteration = 0; m_consecutiveFailures = 0; @@ -3199,6 +3208,7 @@ bool nmCalculationAutoFitPSO::startAutoFitting() emit logMessageGenerated(tr("CRITICAL ERROR: %1").arg(e.what())); closeTraceFile(); cleanupTemporaryDirectory(); + m_isFinalizing = false; m_isRunning = false; emit fittingFinished(false, m_lastError); return false; @@ -3207,17 +3217,20 @@ bool nmCalculationAutoFitPSO::startAutoFitting() emit logMessageGenerated(tr("CRITICAL ERROR: Unknown exception in PSO main loop")); closeTraceFile(); cleanupTemporaryDirectory(); + m_isFinalizing = false; m_isRunning = false; emit fittingFinished(false, m_lastError); return false; } - m_isRunning = false; bool finalFullSolverSucceeded = false; // 应用最终参数 if(!m_globalBestPosition.isEmpty()) { try { + // Stop 只结束优化迭代;从这里开始必须用当前最优参数生成并发布正式快照。 + m_isFinalizing = true; + emit finalizingStarted(); emit logMessageGenerated(tr("Applying optimized parameters to model...")); applyParametersToDataManager(m_globalBestPosition); @@ -3267,6 +3280,9 @@ bool nmCalculationAutoFitPSO::startAutoFitting() } } + m_isFinalizing = false; + m_isRunning = false; + // 判断系统确定最终结果 bool success; QString message; @@ -3294,7 +3310,7 @@ bool nmCalculationAutoFitPSO::startAutoFitting() emit logMessageGenerated(tr("=== PSO OPTIMIZATION - MAX ITERATIONS ===")); } else if(finalReason == PSO_USER_STOPPED) { success = true; - message = QString(tr("Best error: %1, Iterations: %2")) + message = QString(tr("Stopped by user. 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) { diff --git a/Src/nmNum/nmSubWxs/nmWxAutomaticFittingStart.cpp b/Src/nmNum/nmSubWxs/nmWxAutomaticFittingStart.cpp index edd6ccd4..4a508097 100644 --- a/Src/nmNum/nmSubWxs/nmWxAutomaticFittingStart.cpp +++ b/Src/nmNum/nmSubWxs/nmWxAutomaticFittingStart.cpp @@ -389,6 +389,7 @@ nmWxAutomaticfittingStart::nmWxAutomaticfittingStart(QWidget *parent) , m_targetError(0.001) , m_wellName("") , m_isFinished(false) + , m_isFinalizing(false) , m_bestFitnessEver(1e10) , m_startTime() { @@ -592,6 +593,8 @@ void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitPSO* autoFitte this, SLOT(onFittingProgress(int, double)));//更新进度条、当前迭代数、当前误差 connect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)), this, SLOT(onFittingFinished(bool, QString)));//显示结束状态 + connect(m_autoFitterPSO, SIGNAL(finalizingStarted()), + this, SLOT(onFinalizingStarted())); connect(m_autoFitterPSO, SIGNAL(logMessageGenerated(QString)), this, SLOT(onLogMessageReceived(QString)));//把 PSO 内部日志显示到窗口 connect(m_autoFitterPSO, SIGNAL(bestCurveUpdated(QVector >,QVector >,int,double)), @@ -618,6 +621,8 @@ void nmWxAutomaticfittingStart::setAutoFitter(nmCalculationAutoFitLM* autoFitter this, SLOT(onFittingProgress(int, double))); connect(m_autoFitterLM, SIGNAL(fittingFinished(bool, QString)), this, SLOT(onFittingFinished(bool, QString))); + connect(m_autoFitterLM, SIGNAL(finalizingStarted()), + this, SLOT(onFinalizingStarted())); connect(m_autoFitterLM, SIGNAL(logMessageGenerated(QString)), this, SLOT(onLogMessageReceived(QString))); connect(m_autoFitterLM, SIGNAL(bestCurveUpdated(QVector >,QVector >,int,double)), @@ -714,6 +719,7 @@ void nmWxAutomaticfittingStart::onFittingProgress(int iteration, double fitness) void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& message) { m_isFinished = true; + m_isFinalizing = false; const QString algorithmName = m_algorithmName; @@ -801,6 +807,15 @@ void nmWxAutomaticfittingStart::onFittingFinished(bool success, const QString& m } +void nmWxAutomaticfittingStart::onFinalizingStarted() +{ + // 最终完整求解负责生成正式快照,此阶段不能再次停止或关闭进度窗口。 + m_isFinalizing = true; + stopButton->setEnabled(false); + stopButton->setText(tr("Finalizing...")); + addLogMessage(tr("Generating the final result with the current best parameters...")); +} + void nmWxAutomaticfittingStart::onStopButtonClicked() { const bool isRunning = (m_autoFitterPSO && m_autoFitterPSO->isRunning()) || @@ -814,6 +829,9 @@ void nmWxAutomaticfittingStart::onStopButtonClicked() QMessageBox::No); if (ret == QMessageBox::Yes) { + // 先锁定按钮,等优化循环退出后由 finalizingStarted() 切换到最终求解状态。 + stopButton->setEnabled(false); + stopButton->setText(tr("Stopping...")); if (m_autoFitterPSO) { m_autoFitterPSO->stopFitting(); } else if (m_autoFitterLM) { @@ -885,6 +903,14 @@ void nmWxAutomaticfittingStart::closeEvent(QCloseEvent *event) const bool isRunning = (m_autoFitterPSO && m_autoFitterPSO->isRunning()) || (m_autoFitterLM && m_autoFitterLM->isRunning()); const QString algorithmName = m_algorithmName; + if(m_isFinalizing && !m_isFinished) { + QMessageBox::information( + this, + tr("Finalizing"), + tr("The final result is being generated. Please wait for the calculation to finish.")); + event->ignore(); + return; + } if (isRunning && !m_isFinished) { int ret = QMessageBox::question(this, tr("Confirm Close"),