diff --git a/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h b/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h index e59db09..3418b04 100644 --- a/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h +++ b/Include/nmNum/nmCalculation/nmCalculationAutoFitPSO.h @@ -166,6 +166,7 @@ private: // ===== 求解器相关 ===== QVector > runSolver(); QVector> runSolverDll(); + bool runFinalFullSolver(); QVector> runSolverExe(); // ===== 数据处理 ===== diff --git a/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h b/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h index 1de9f71..3efc33f 100644 --- a/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h +++ b/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h @@ -2,6 +2,8 @@ #define NMCALCULATIONDLLPEBISOLVERTASK_H #include +#include +#include #include #include #include @@ -22,6 +24,13 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread { // 返回 false 时调用方会丢弃本次结果, 防止复用上一粒子留下的旧曲线. bool wasSuccessful() const; + // 自动拟合粒子评价只提取目标井曲线,不写回共享数据和网格压力场。 + // 井名为空时保持原有完整结果保存模式。 + void setAutoFitTargetWell(const QString& wellName); + QVector > getAutoFitResultPressure() const; + QVector > getAutoFitResultLogLog() const; + QVector > getAutoFitResultSemiLog() const; + private: bool execute(); @@ -37,6 +46,10 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread { QString m_sPostprocessingDir; // run() 在线程内保存 execute() 结果, 等待线程结束的调用方只读取该状态. bool m_lastRunSucceeded; + QString m_autoFitTargetWellName; + QVector > m_autoFitResultPressure; + QVector > m_autoFitResultLogLog; + QVector > m_autoFitResultSemiLog; private slots: //void slotTaskUpdateProgress(); diff --git a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp index d0deda2..936d9fc 100644 --- a/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationAutoFitPSO.cpp @@ -721,8 +721,8 @@ void nmCalculationAutoFitPSO::setTargetLogLogData(const QVector void nmCalculationAutoFitPSO::stopFitting() { // 用户点击停止时走这里。停止策略是“请求式停止”: - // 先置 m_shouldStop,让主循环/求解器等待逻辑自然退出;短时间内还在评价时再重置计数。 - // 这样可以减少 DLL 任务被硬中断导致的数据状态残留。 + // 只置 m_shouldStop,让主循环/求解器等待逻辑退出并自行维护任务计数。 + // 不在这里清理临时目录或强制清零计数,避免与正在返回的 DLL 任务竞争。 if(m_simulationMode && m_simulationTimer) { m_simulationTimer->stop(); } @@ -736,25 +736,28 @@ void nmCalculationAutoFitPSO::stopFitting() m_shouldStop = true; - // 等待当前评估完成,缩短超时时间 + if(m_simulationMode) { + m_isRunning = false; + closeTraceFile(); + cleanupTemporaryDirectory(); + emit logMessageGenerated(tr("PSO simulation stop request processed")); + return; + } + + // 给当前评价一个短暂的自然退出时间。若仍在运行, + // runSolverDll() 会在下一个等待周期检查 m_shouldStop 并结束任务。 int waitCount = 0; - while(m_evaluationInProgress > 0 && waitCount < 30) { // 减少等待时间 + 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; + emit logMessageGenerated(tr("Waiting for current solver evaluation to stop...")); } - // 确保运行标志被清除 - m_isRunning = false; - emit logMessageGenerated(tr("PSO optimization stop request processed")); DEBUG_OUT("Stop request processed"); } else { @@ -762,8 +765,6 @@ void nmCalculationAutoFitPSO::stopFitting() emit logMessageGenerated(tr("Stop request received but optimization is not running")); } - closeTraceFile(); - cleanupTemporaryDirectory(); } bool nmCalculationAutoFitPSO::isRunning() const @@ -2903,6 +2904,15 @@ bool nmCalculationAutoFitPSO::startAutoFitting() emit logMessageGenerated(tr("Target data validation passed (%1 data points)").arg(m_targetLogLogData[0].size())); + if(m_targetWellName.isEmpty()) { + m_lastError = "Target well name is empty"; + emit logMessageGenerated(tr("ERROR: Target well name is empty")); + return false; + } + + emit logMessageGenerated(tr("Particle evaluation mode: solve all wells, retain target well '%1' only") + .arg(m_targetWellName)); + // 使用保存的初始值进行精英保护。resetOptimizer() 会清空部分运行状态, // 所以先把用户当前模型参数缓存下来,后面再恢复用于初始解评价和粒子初始化。 QVector savedInitialValues = m_initialValues; @@ -3286,13 +3296,37 @@ bool nmCalculationAutoFitPSO::startAutoFitting() return false; } - m_isRunning = false; + bool finalFullSolverSucceeded = true; + bool finalFullSolverExecuted = false; // 应用最终参数 if(!m_globalBestPosition.isEmpty()) { try { emit logMessageGenerated(tr("Applying optimized parameters to model...")); applyParametersToDataManager(m_globalBestPosition); + + if(m_shouldStop) { + // 手动停止优先保持快速返回,仅写回已确认的最优参数。 + emit logMessageGenerated(tr("Final full-field calculation skipped after user stop")); + } else { + // 粒子阶段只保留目标井临时曲线。正常结束后用最优参数完整计算一次, + // 将全部井曲线和网格压力场写回项目,该次不计入 PSO 粒子评价数。 + 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 { + emit logMessageGenerated(tr("ERROR: Final full-field calculation failed")); + m_lastError = tr("Optimized parameters were found, but the final full-field calculation failed"); + } + } + saveOptimizationResult(); // 输出最终优化结果 @@ -3311,16 +3345,24 @@ bool nmCalculationAutoFitPSO::startAutoFitting() emit logMessageGenerated(finalParams); - emit logMessageGenerated(tr("Parameters applied successfully to data manager")); + if(finalFullSolverExecuted && finalFullSolverSucceeded) { + emit logMessageGenerated(tr("Parameters and full-field results applied successfully to data manager")); + } else if(!finalFullSolverExecuted) { + emit logMessageGenerated(tr("Optimized parameters applied to data manager")); + } } catch(const std::exception& e) { + finalFullSolverSucceeded = false; 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(...) { + finalFullSolverSucceeded = false; emit logMessageGenerated(tr("ERROR: Unknown error applying final parameters")); m_lastError = "Failed to apply final parameters due to unknown error"; } } + m_isRunning = false; + // 判断系统确定最终结果 bool success; QString message; @@ -3363,6 +3405,11 @@ bool nmCalculationAutoFitPSO::startAutoFitting() emit logMessageGenerated(tr("=== PSO OPTIMIZATION - UNKNOWN END ===")); } + if(!finalFullSolverSucceeded) { + success = false; + message = m_lastError; + } + emitRunSummary(success, finalReason); // 先发送最终进度更新,确保进度条达到100% @@ -3961,7 +4008,7 @@ double nmCalculationAutoFitPSO::evaluateFitness(const QVector& parameter // 1. 校验粒子参数是否在用户设置的上下界和基本物理范围内; // 2. 将参数写入 DataManager 的储层/目标井对象; // 3. 调用真实数值求解器,生成模拟结果; - // 4. 从目标井读取模拟后的 result log-log 曲线; + // 4. 从本次求解任务读取目标井 result log-log 曲线; // 5. 与目标 history log-log 曲线计算误差,误差越小代表拟合越好。 // // 返回 1e10 表示该粒子评价失败或结果不可用。PSO 会把它当成很差的解。 @@ -4122,24 +4169,11 @@ double nmCalculationAutoFitPSO::evaluateFitness(const QVector& parameter return 1e10; } - // 5. 获取 LogLog 数据。runSolver() 会更新 DataManager 中目标井的计算结果, - // 这里再从目标井读取 resultLogLogData 作为模拟曲线。 - QVector> resultLogLogData; + // 5. 获取 LogLog 数据。runSolverDll() 直接从求解任务复制目标井曲线, + // 不再依赖 DataManager 中可能被其它井或上一粒子改写的共享结果。 + QVector> resultLogLogData = m_lastEvaluatedLogLogData; 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)); @@ -5145,7 +5179,7 @@ QVector> nmCalculationAutoFitPSO::runSolverDll() { // DLL 求解器路径。 // 这个函数负责把当前 DataManager 中的项目状态交给底层数值求解器, - // 并让目标井生成 result log-log 数据。evaluateFitness() 后续会从目标井读取该结果。 + // 数值求解仍包含全部计算井,以保留井间干扰;后处理只提取目标井曲线。 // // 如果这里失败,通常需要优先检查:HX_NWTM.dll、license、网格/井数据是否完整、 // 目标井是否存在,以及 DataManager 中刚写入的参数是否导致求解器异常。 @@ -5157,6 +5191,7 @@ QVector> nmCalculationAutoFitPSO::runSolverDll() } ++m_evaluationInProgress; + m_lastEvaluatedLogLogData.clear(); QVector> result; nmCalculationDllPebiSolverTask* dllTask = nullptr; @@ -5164,6 +5199,15 @@ QVector> nmCalculationAutoFitPSO::runSolverDll() DEBUG_OUT("Creating DLL solver task"); dllTask = new nmCalculationDllPebiSolverTask(m_tempDirectory); + if(m_targetWellName.isEmpty()) { + DEBUG_OUT("Target well name is empty - target-only solver cannot start"); + delete dllTask; + --m_evaluationInProgress; + return result; + } + + dllTask->setAutoFitTargetWell(m_targetWellName); + if(m_shouldStop) { DEBUG_OUT("Should stop - cleaning up and returning empty result"); delete dllTask; @@ -5223,22 +5267,9 @@ QVector> nmCalculationAutoFitPSO::runSolverDll() return result; } - // 验证结果数据是否已更新。DLL 任务会把结果写回 DataManager 中的目标井对象。 - nmDataAnalyzeManager* dataManager = nmDataAnalyzeManager::getCurrentInstance(); - //QVector 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; - } - - // 验证结果数据。evaluateFitness() 最终用的是 logLogResult, - // 但这里返回 pressureResult 给 validateSolverResult() 做基本求解成功判断。 - QVector> pressureResult = pTargetWell->getResultPressure(); - QVector> logLogResult = pTargetWell->getResultLogLog(); + // 任务结束后复制其局部结果,删除任务前不再持有任务内部引用。 + QVector> pressureResult = dllTask->getAutoFitResultPressure(); + QVector> logLogResult = dllTask->getAutoFitResultLogLog(); DEBUG_OUT(QString("DLL result verification - Pressure arrays: %1, LogLog arrays: %2") .arg(pressureResult.size()).arg(logLogResult.size())); @@ -5257,8 +5288,12 @@ QVector> nmCalculationAutoFitPSO::runSolverDll() } // 数据有效性检查 - if(pressureResult.size() >= 2 && pressureResult[0].size() > 0 && pressureResult[1].size() > 0) { + if(pressureResult.size() >= 2 + && pressureResult[0].size() > 0 + && pressureResult[1].size() > 0 + && validateLogLogData(logLogResult)) { result = pressureResult; + m_lastEvaluatedLogLogData = logLogResult; DEBUG_OUT(QString("Got DLL solver result: %1 points").arg(result[0].size())); m_consecutiveFailures = 0; @@ -5326,6 +5361,58 @@ QVector> nmCalculationAutoFitPSO::runSolverDll() return result; } +bool nmCalculationAutoFitPSO::runFinalFullSolver() +{ + // 不设置目标井名,任务按原完整模式保存全部井和网格结果。 + if(m_evaluationInProgress > 0) { + DEBUG_OUT("Cannot start final full-field solver while another evaluation is running"); + return false; + } + + ++m_evaluationInProgress; + nmCalculationDllPebiSolverTask dllTask(m_tempDirectory); + dllTask.start(); + + const int maxWait = 3600000; + const int checkInterval = 50; + QTime waitTimer; + waitTimer.start(); + + while(waitTimer.elapsed() < maxWait) { + if(dllTask.wait(checkInterval)) { + break; + } + + // 最终完整计算可能持续较长时间,此处需处理停止按钮事件。 + QApplication::processEvents(QEventLoop::AllEvents, checkInterval); + + if(!dllTask.isRunning()) { + 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()) { + DEBUG_OUT("Final full-field solver timeout, terminating task"); + dllTask.terminate(); + dllTask.wait(2000); + --m_evaluationInProgress; + return false; + } + + dllTask.wait(); + const bool succeeded = dllTask.wasSuccessful(); + --m_evaluationInProgress; + return succeeded; +} + //QVector> nmCalculationAutoFitPSO::runSolverExe() //{ // DEBUG_OUT("SOLVER EXE START"); diff --git a/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp b/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp index e8db3a5..9f87c0b 100644 --- a/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp @@ -227,6 +227,26 @@ bool nmCalculationDllPebiSolverTask::wasSuccessful() const return m_lastRunSucceeded; } +void nmCalculationDllPebiSolverTask::setAutoFitTargetWell(const QString& wellName) +{ + m_autoFitTargetWellName = wellName; +} + +QVector > nmCalculationDllPebiSolverTask::getAutoFitResultPressure() const +{ + return m_autoFitResultPressure; +} + +QVector > nmCalculationDllPebiSolverTask::getAutoFitResultLogLog() const +{ + return m_autoFitResultLogLog; +} + +QVector > nmCalculationDllPebiSolverTask::getAutoFitResultSemiLog() const +{ + return m_autoFitResultSemiLog; +} + bool nmCalculationDllPebiSolverTask::execute() { return this->execPebiMode(); @@ -582,6 +602,15 @@ bool nmCalculationDllPebiSolverTask::savePebiModeResult( int modelType) { nmDataAnalyzeManager* pDataInstance = nmDataAnalyzeManager::getCurrentInstance(); + if(pDataInstance == nullptr) { + return false; + } + + const bool autoFitTargetOnly = !m_autoFitTargetWellName.isEmpty(); + bool autoFitTargetFound = false; + m_autoFitResultPressure.clear(); + m_autoFitResultLogLog.clear(); + m_autoFitResultSemiLog.clear(); QVector> vvecPressure; QVector> vvecLogLog; @@ -599,16 +628,22 @@ bool nmCalculationDllPebiSolverTask::savePebiModeResult( } // 获取参与求解的井的顺序 - QVector> vecWellsOrder = nmDataAnalyzeManager::getCurrentInstance()->getCalculationWells(); + QVector> vecWellsOrder = pDataInstance->getCalculationWells(); - // 清空井名和二维位置的映射 - pDataInstance->clearWellLocations(); + // 粒子评价不修改全局井位置,完整求解仍按原流程重建映射. + if(!autoFitTargetOnly) { + pDataInstance->clearWellLocations(); + } // 遍历每口井,处理其数据 for(int wellIdx = 0; wellIdx < vecWellsOrder.size(); ++wellIdx) { NM_WELL_MODEL eWellType = vecWellsOrder[wellIdx].first; // 获取井的类型 QString sWellName = vecWellsOrder[wellIdx].second; // 获取井的名称 + if(autoFitTargetOnly && sWellName != m_autoFitTargetWellName) { + continue; + } + // 跳过裂缝(或未知井类型) if(eWellType == NM_WELL_MODEL::Unknow_Well) { continue; @@ -760,17 +795,34 @@ bool nmCalculationDllPebiSolverTask::savePebiModeResult( } } - // 将计算结果保存到对应的井数据里 - // 压力 - pWellData->setResultPressure(vvecPressure); - // 双对数 - pWellData->setResultLogLog(vvecLogLog); - // 半对数 - pWellData->setResultSemiLog(vvecSemiLog); - - // 存储当前井名称和二维位置到映射 - QPointF ptWellCoords(pWellData->getX().getValue().toDouble(), pWellData->getY().getValue().toDouble()); - pDataInstance->addWellLocation(sWellName, ptWellCoords); + if(autoFitTargetOnly) { + // 粒子评价结果保存在任务对象内,避免反复改写 DataManager. + m_autoFitResultPressure = vvecPressure; + m_autoFitResultLogLog = vvecLogLog; + m_autoFitResultSemiLog = vvecSemiLog; + autoFitTargetFound = true; + break; + } else { + // 完整模式保持原行为:写入全部井曲线和井位置. + pWellData->setResultPressure(vvecPressure); + pWellData->setResultLogLog(vvecLogLog); + pWellData->setResultSemiLog(vvecSemiLog); + + QPointF ptWellCoords(pWellData->getX().getValue().toDouble(), pWellData->getY().getValue().toDouble()); + pDataInstance->addWellLocation(sWellName, ptWellCoords); + } + } + + if(autoFitTargetOnly) { + const bool pressureValid = autoFitTargetFound + && m_autoFitResultPressure.size() >= 2 + && !m_autoFitResultPressure[0].isEmpty() + && m_autoFitResultPressure[0].size() == m_autoFitResultPressure[1].size(); + const bool logLogValid = m_autoFitResultLogLog.size() >= 3 + && !m_autoFitResultLogLog[0].isEmpty() + && m_autoFitResultLogLog[0].size() == m_autoFitResultLogLog[1].size() + && m_autoFitResultLogLog[0].size() == m_autoFitResultLogLog[2].size(); + return pressureValid && logLogValid; } // 计算有效单元数量