优化多井自动拟合求解流程

feature/MultiWellAutoFit-20260805
lvjunjie 2 weeks ago
parent f87ffafad9
commit eb6ef39007

@ -166,6 +166,7 @@ private:
// ===== 求解器相关 =====
QVector<QVector<double> > runSolver();
QVector<QVector<double>> runSolverDll();
bool runFinalFullSolver();
QVector<QVector<double>> runSolverExe();
// ===== 数据处理 =====

@ -2,6 +2,8 @@
#define NMCALCULATIONDLLPEBISOLVERTASK_H
#include <QThread>
#include <QString>
#include <QVector>
#include <iostream>
#include <vector>
#include <Windows.h>
@ -22,6 +24,13 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
// 返回 false 时调用方会丢弃本次结果, 防止复用上一粒子留下的旧曲线.
bool wasSuccessful() const;
// 自动拟合粒子评价只提取目标井曲线,不写回共享数据和网格压力场。
// 井名为空时保持原有完整结果保存模式。
void setAutoFitTargetWell(const QString& wellName);
QVector<QVector<double> > getAutoFitResultPressure() const;
QVector<QVector<double> > getAutoFitResultLogLog() const;
QVector<QVector<double> > 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<QVector<double> > m_autoFitResultPressure;
QVector<QVector<double> > m_autoFitResultLogLog;
QVector<QVector<double> > m_autoFitResultSemiLog;
private slots:
//void slotTaskUpdateProgress();

@ -721,8 +721,8 @@ void nmCalculationAutoFitPSO::setTargetLogLogData(const QVector<QVector<double>
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<double> 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<double>& 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<double>& parameter
return 1e10;
}
// 5. 获取 LogLog 数据。runSolver() 会更新 DataManager 中目标井的计算结果
// 这里再从目标井读取 resultLogLogData 作为模拟曲线
QVector<QVector<double>> resultLogLogData;
// 5. 获取 LogLog 数据。runSolverDll() 直接从求解任务复制目标井曲线
// 不再依赖 DataManager 中可能被其它井或上一粒子改写的共享结果
QVector<QVector<double>> 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<QVector<double>> nmCalculationAutoFitPSO::runSolverDll()
{
// DLL 求解器路径。
// 这个函数负责把当前 DataManager 中的项目状态交给底层数值求解器,
// 并让目标井生成 result log-log 数据。evaluateFitness() 后续会从目标井读取该结果
// 数值求解仍包含全部计算井,以保留井间干扰;后处理只提取目标井曲线
//
// 如果这里失败通常需要优先检查HX_NWTM.dll、license、网格/井数据是否完整、
// 目标井是否存在,以及 DataManager 中刚写入的参数是否导致求解器异常。
@ -5157,6 +5191,7 @@ QVector<QVector<double>> nmCalculationAutoFitPSO::runSolverDll()
}
++m_evaluationInProgress;
m_lastEvaluatedLogLogData.clear();
QVector<QVector<double>> result;
nmCalculationDllPebiSolverTask* dllTask = nullptr;
@ -5164,6 +5199,15 @@ QVector<QVector<double>> 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<QVector<double>> nmCalculationAutoFitPSO::runSolverDll()
return result;
}
// 验证结果数据是否已更新。DLL 任务会把结果写回 DataManager 中的目标井对象。
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;
}
// 验证结果数据。evaluateFitness() 最终用的是 logLogResult
// 但这里返回 pressureResult 给 validateSolverResult() 做基本求解成功判断。
QVector<QVector<double>> pressureResult = pTargetWell->getResultPressure();
QVector<QVector<double>> logLogResult = pTargetWell->getResultLogLog();
// 任务结束后复制其局部结果,删除任务前不再持有任务内部引用。
QVector<QVector<double>> pressureResult = dllTask->getAutoFitResultPressure();
QVector<QVector<double>> 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<QVector<double>> 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<QVector<double>> 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<QVector<double>> nmCalculationAutoFitPSO::runSolverExe()
//{
// DEBUG_OUT("SOLVER EXE START");

@ -227,6 +227,26 @@ bool nmCalculationDllPebiSolverTask::wasSuccessful() const
return m_lastRunSucceeded;
}
void nmCalculationDllPebiSolverTask::setAutoFitTargetWell(const QString& wellName)
{
m_autoFitTargetWellName = wellName;
}
QVector<QVector<double> > nmCalculationDllPebiSolverTask::getAutoFitResultPressure() const
{
return m_autoFitResultPressure;
}
QVector<QVector<double> > nmCalculationDllPebiSolverTask::getAutoFitResultLogLog() const
{
return m_autoFitResultLogLog;
}
QVector<QVector<double> > 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<QVector<double>> vvecPressure;
QVector<QVector<double>> vvecLogLog;
@ -599,16 +628,22 @@ bool nmCalculationDllPebiSolverTask::savePebiModeResult(
}
// 获取参与求解的井的顺序
QVector<QPair<NM_WELL_MODEL, QString>> vecWellsOrder = nmDataAnalyzeManager::getCurrentInstance()->getCalculationWells();
QVector<QPair<NM_WELL_MODEL, QString>> 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;
}
// 计算有效单元数量

Loading…
Cancel
Save