From 45ee68645b23547d17e8bf78c08779413fcbc447 Mon Sep 17 00:00:00 2001 From: lh <2334563547@qq.com> Date: Tue, 8 Sep 2026 17:20:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(nmCalculation):=20=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E6=95=B0=E5=80=BC=E6=B1=82=E8=A7=A3=E5=A4=B1=E8=B4=A5=E5=8E=9F?= =?UTF-8?q?=E5=9B=A0=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 记录输入捕获、网格生成、DLL 调用及结果处理等失败原因 - 将任务侧失败原因传递至求解完成提示 - 区分求解失败、用户取消和输入变化导致的结果丢弃 - 补充中文翻译并更新语言包 --- Bin/Config/Lang/cn/nmNum_cn.qm | Bin 137047 -> 141636 bytes Bin/Config/Lang/cn/nmNum_cn.ts | 47 +++++- .../nmCalculationDllPebiSolverTask.h | 5 + Include/nmNum/nmSubWnd/nmSubWndMain.h | 7 +- .../nmCalculationDllPebiSolverTask.cpp | 152 +++++++++++------- Src/nmNum/nmSubWnd/nmSubWndMain.cpp | 37 ++++- 6 files changed, 179 insertions(+), 69 deletions(-) diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index b3e2db2781c84ff8f3e3f7e94f0dffa4a24f8318..00d929e63486d964d3011e48e75424ae0fa6ac5c 100644 GIT binary patch delta 16036 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 G!2baqVVl|j delta 12968 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 diff --git a/Bin/Config/Lang/cn/nmNum_cn.ts b/Bin/Config/Lang/cn/nmNum_cn.ts index aeffc34..c5ae027 100644 --- a/Bin/Config/Lang/cn/nmNum_cn.ts +++ b/Bin/Config/Lang/cn/nmNum_cn.ts @@ -2846,10 +2846,8 @@ Reason: %1 The solver currently supports oil, gas, and water single-phase models and the oil-water two-phase model only. 当前求解器仅支持油、气、水单相模型和油水两相模型。 - - solver failed! - 求解失败! - + Solver failed求解失败 + No specific failure reason is available.未获取到具体失败原因。 Progress: 求解进度: @@ -5230,6 +5228,10 @@ Please check your input coordinates. Segment %1第 %1 段 No eligible wells没有可配置的其他井 Select at least one production record first.请先选择至少一个产量相。 + + Help + 帮助 + OK 确定 @@ -7145,6 +7147,43 @@ Average pressure in contour: %2 MPa Capturing solver settings... 正在捕获求解设置... + Insufficient memory to prepare solver input.准备求解输入时内存不足。 + Insufficient memory to process solver results.处理求解结果时内存不足。 + Solver input is unavailable.当前求解输入不可用。 + Failed to capture solver input.无法获取求解输入。 + The well configuration is invalid.井配置无效。 + No valid wells are available for solving.没有可用于求解的有效井。 + Failed to capture well data.无法获取井数据。 + Failed to finalize solver input.无法完成求解输入准备。 + Failed to capture solver settings.无法获取求解设置。 + The target well is unavailable.目标井不可用。 + Solver input snapshot is invalid.求解输入快照无效。 + Failed to prepare solver input.求解输入准备失败。 + The prepared result grid is invalid.准备的结果网格无效。 + Grid generation failed.网格生成失败。 + The generated grid is invalid.生成的网格数据无效。 + Property interpolation failed.属性插值失败。 + Failed to load HX_NWTM.dll.无法加载 HX_NWTM.dll。 + The solver interface is incomplete.求解器接口不完整。 + An exception occurred while calling the solver.调用求解器时发生异常。 + The solver result data is invalid.求解器结果数据无效。 + Failed to load the gas pseudo-pressure converter.无法加载气体拟压力转换器。 + The solver returned incomplete result data.求解器返回的结果数据不完整。 + The solver returned invalid time data.求解器返回的时间数据无效。 + The solver returned incomplete well pressure data.求解器返回的井压力数据不完整。 + The solver returned invalid well pressure data.求解器返回的井压力数据无效。 + Failed to convert gas pressure to pseudo-pressure.气体压力转换为拟压力失败。 + The pressure curve interface is incomplete.压力曲线接口不完整。 + Failed to calculate pressure curves.压力曲线计算失败。 + Failed to load singlePhaseSolverDll.dll.无法加载 singlePhaseSolverDll.dll。 + The calculated pressure curves are invalid.计算得到的压力曲线无效。 + The solver returned incomplete field pressure data.求解器返回的场压力数据不完整。 + The solver returned invalid field pressure data.求解器返回的场压力数据无效。 + Failed to build the result snapshot.求解结果快照构建失败。 + Insufficient memory to build the result snapshot.构建求解结果快照时内存不足。 + Failed to commit the solver results.求解结果提交失败。 + Failed to commit the generated grid.生成的网格提交失败。 + The generated grid is no longer valid.生成的网格已失效。 nmParameterField diff --git a/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h b/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h index 73ebc70..309d7e9 100644 --- a/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h +++ b/Include/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.h @@ -49,6 +49,8 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread { int getSolveTimeMs() const; /** @brief 获取本次求解的PEBI网格数量。 */ int getPebiCount() const; + /** @brief 返回当前代码已识别的失败原因;成功或主动停止时为空。 */ + QString getFailureReason() const; /** @brief 请求协作式停止;DLL 正在执行时会在返回后丢弃结果。 */ void requestCancel(); /** @brief 返回本次任务是否因手工停止而结束。 */ @@ -91,6 +93,8 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread { bool isCancelRequested() const; /** @brief 仅为手工求解报告真实执行阶段。 */ void reportStage(const QString& sStage, int nCurrent, int nTotal); + /** @brief 记录首个已识别的失败原因并统一返回 false。 */ + bool fail(const QString& sFailureReason); /** @brief 构造 DLL 输入、执行求解并生成仍然有效的局部结果快照。 */ bool execPebiMode(); @@ -134,6 +138,7 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread { int m_nSolveTimeMs; /** @brief 本次求解的PEBI网格数量。 */ int m_nPebiCount; + QString m_sFailureReason; ///< 当前代码已识别的首个失败原因。 QString m_sAutoFitTargetWellCode; ///< 自动拟合目标井 WellCode;空值表示保存完整结果。 QVector > m_autoFitResultPressure; ///< 目标井原始压力结果。 QVector > m_autoFitResultLogLog; ///< 目标井双对数结果。 diff --git a/Include/nmNum/nmSubWnd/nmSubWndMain.h b/Include/nmNum/nmSubWnd/nmSubWndMain.h index 305868b..ce26e00 100644 --- a/Include/nmNum/nmSubWnd/nmSubWndMain.h +++ b/Include/nmNum/nmSubWnd/nmSubWndMain.h @@ -137,7 +137,8 @@ class NM_SUB_WND_EXPORT nmSubWndMain : public iSubWndBaseFit { virtual bool slotSaveAll(); // 求解完成 - bool on_calculationFinished(NM_Calculation_Result); + bool on_calculationFinished(NM_Calculation_Result, + const QString& sFailureReason); // DLL线程只返回成功/失败,这里转换回原来的计算结果处理流程。 void on_solverTaskFinished(bool isSuccessed); /** @brief 完成回调退出后隐藏求解进度框。 */ @@ -254,7 +255,8 @@ class NM_SUB_WND_EXPORT nmSubWndMain : public iSubWndBaseFit { void scheduleSolverResultCompletion(bool bSucceeded, int nPebiCount, int nSolveTimeMs, - bool bInputChanged = false); + bool bInputChanged, + const QString& sFailureReason); // 半对数曲线数据 bool inithalfLog(QVector> & vvecLogPreData, QVector& vecDescs,bool isPreLoadData); // 双对数曲线数据 @@ -309,6 +311,7 @@ private: bool m_bSolverResultCompletionPending; ///< 等待进度框隐藏后处理求解结果。 bool m_bPendingSolverSucceeded; ///< 待处理求解结果是否成功。 bool m_bPendingSolverInputChanged; ///< 输入变化导致本次旧结果被丢弃。 + QString m_sPendingSolverFailureReason; ///< 当前代码已识别的待显示失败原因。 int m_nPendingSolverPebiCount; ///< 待显示的网格数量。 int m_nPendingSolverTimeMs; ///< 待显示的求解耗时。 bool m_bSolverStartPending; ///< 进度框已显示,输入正分批捕获或任务等待启动。 diff --git a/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp b/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp index bbcfa11..4b27c7c 100644 --- a/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp +++ b/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp @@ -651,6 +651,7 @@ nmCalculationDllPebiSolverTask::nmCalculationDllPebiSolverTask( } catch(const std::bad_alloc&) { // 构造阶段内存不足时保留旧成果,任务保持无效并由既有完成流程报错。 qWarning() << "Cannot allocate PEBI solver input snapshot."; + fail(tr("Insufficient memory to prepare solver input.")); delete m_pManualCaptureState; m_pManualCaptureState = nullptr; delete m_pInputSnapshot; @@ -683,6 +684,7 @@ void nmCalculationDllPebiSolverTask::run() } catch(const std::bad_alloc&) { // VTK 部分分配接口没有返回值,统一在任务边界拒绝候选并保留旧快照。 qWarning() << "Cannot allocate PEBI solver result snapshot."; + fail(tr("Insufficient memory to process solver results.")); discardPendingFullResult(); bSucceeded = false; } @@ -732,7 +734,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( if(m_pManualCaptureState == nullptr || m_pInputSnapshot == nullptr || m_pDataManager == nullptr || isRunning() || QThread::currentThread() != m_pDataManager->thread()) { - return false; + return fail(tr("Solver input is unavailable.")); } if(isCancelRequested()) { m_nWasCancelled.fetchAndStoreOrdered(1); @@ -745,7 +747,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( nmDataNumericalAnalysisCase* pAnalysisCase = m_pDataManager->getNumericalAnalysisCase(); if(pGridService == nullptr || pAnalysisCase == nullptr) { - return false; + return fail(tr("Solver input is unavailable.")); } if(pState->m_ePhase != @@ -764,7 +766,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( if(!pGridService->beginManualInputSnapshot( m_pDataManager, m_pInputSnapshot->m_oGridInput)) { - return false; + return fail(tr("Failed to capture solver input.")); } const QVector vecEffectiveWells = @@ -774,7 +776,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( if(oWellRef.m_sWellCode.isEmpty() || pState->m_setEffectiveWellCodes.contains( oWellRef.m_sWellCode)) { - return false; + return fail(tr("The well configuration is invalid.")); } pState->m_setEffectiveWellCodes.insert(oWellRef.m_sWellCode); pState->m_mapWellModes.insert( @@ -782,7 +784,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( static_cast(oWellRef.m_eMode)); } if(pState->m_setEffectiveWellCodes.isEmpty()) { - return false; + return fail(tr("No valid wells are available for solving.")); } QSet setOrderedWellCodes; @@ -829,7 +831,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( if(setOrderedWellCodes != pState->m_setEffectiveWellCodes || pState->m_vecOrderedWells.size() != pState->m_setEffectiveWellCodes.size()) { - return false; + return fail(tr("The well configuration is invalid.")); } pState->m_ePhase = nmPebiManualCaptureState::CapturePhase_Wells; @@ -856,7 +858,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( pWellData, pState->m_mapWellModes.value(pWellData->getWellCode()), m_pInputSnapshot->m_oGridInput)) { - return false; + return fail(tr("Failed to capture well data.")); } if(m_pInputSnapshot->m_oGridInput.m_vecWellInputs.isEmpty() || !captureResultWellMetadata( @@ -864,7 +866,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( isDisplayResultWell(pAnalysisCase, pWellData->getWellCode()), m_pInputSnapshot->m_oGridInput.m_vecWellInputs.last())) { - return false; + return fail(tr("Failed to capture well data.")); } ++pState->m_nNextWellIndex; } @@ -887,7 +889,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( m_pDataManager, pState->m_setEffectiveWellCodes, m_pInputSnapshot->m_oGridInput)) { - return false; + return fail(tr("Failed to finalize solver input.")); } pState->m_ePhase = nmPebiManualCaptureState::CapturePhase_SolverSettings; @@ -907,7 +909,7 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep( m_pDataManager->getPebiIluReuseSteps(); if(!captureResultParameters(m_pDataManager, *m_pInputSnapshot)) { - return false; + return fail(tr("Failed to capture solver settings.")); } pState->m_ePhase = nmPebiManualCaptureState::CapturePhase_GridCache; @@ -953,6 +955,14 @@ void nmCalculationDllPebiSolverTask::reportStage( } } +bool nmCalculationDllPebiSolverTask::fail(const QString& sFailureReason) +{ + if(!isCancelRequested() && m_sFailureReason.isEmpty()) { + m_sFailureReason = sFailureReason; + } + return false; +} + void nmCalculationDllPebiSolverTask::releaseDataManagerUse() { if(m_bManagerUseActive && m_pDataManager != nullptr) { @@ -967,13 +977,13 @@ bool nmCalculationDllPebiSolverTask::captureInputSnapshot( if(m_pDataManager == nullptr || m_pInputSnapshot == nullptr || QThread::currentThread() != m_pDataManager->thread()) { qWarning() << "PEBI solver input must be captured on the DataManager thread."; - return false; + return fail(tr("Solver input is unavailable.")); } nmDataNumericalAnalysisCase* pAnalysisCase = m_pDataManager->getNumericalAnalysisCase(); if(pAnalysisCase == nullptr) { - return false; + return fail(tr("Solver input is unavailable.")); } // 第一步:冻结几何和求解输入版本。提交阶段会再次核对,期间发生任何编辑 @@ -992,7 +1002,7 @@ bool nmCalculationDllPebiSolverTask::captureInputSnapshot( if(pTargetWell == nullptr || pTargetWell->getWellCode().isEmpty()) { qWarning() << "Auto-fit target well is unavailable:" << sAutoFitTargetWellName; - return false; + return fail(tr("The target well is unavailable.")); } m_sAutoFitTargetWellCode = pTargetWell->getWellCode(); } else { @@ -1010,14 +1020,14 @@ bool nmCalculationDllPebiSolverTask::captureInputSnapshot( !m_pInputSnapshot->m_bAutoFitTargetOnly) || m_pInputSnapshot->m_oGridInput.m_nGridInputRevision != m_nGridInputRevision) { - return false; + return fail(tr("Failed to capture solver input.")); } const QVector& vecWellOrder = m_pInputSnapshot->m_oGridInput.m_vecSolverWellOrder; if(m_pInputSnapshot->m_oGridInput.m_vecWellInputs.size() != vecWellOrder.size()) { - return false; + return fail(tr("Failed to capture well data.")); } // 自动拟合候选保持原来的轻量输入;只有最终完整场求解冻结正式结果元数据。 @@ -1044,7 +1054,7 @@ bool nmCalculationDllPebiSolverTask::captureInputSnapshot( isDisplayResultWell(pAnalysisCase, oWellRef.m_sWellCode), m_pInputSnapshot->m_oGridInput.m_vecWellInputs[nIndex])) { - return false; + return fail(tr("Failed to capture well data.")); } } } @@ -1061,7 +1071,7 @@ bool nmCalculationDllPebiSolverTask::captureInputSnapshot( m_pDataManager->getPebiIluReuseSteps(); if(!m_pInputSnapshot->m_bAutoFitTargetOnly && !captureResultParameters(m_pDataManager, *m_pInputSnapshot)) { - return false; + return fail(tr("Failed to capture solver settings.")); } // 第四步:主线程仅检查缓存并捕获基础网格的智能指针。生成后的基础网格 @@ -1106,8 +1116,10 @@ bool nmCalculationDllPebiSolverTask::captureInputSnapshot( bool nmCalculationDllPebiSolverTask::prepareManualInput() { - if(m_pInputSnapshot == nullptr || - m_pInputSnapshot->m_bAutoFitTargetOnly) { + if(m_pInputSnapshot == nullptr) { + return fail(tr("Solver input snapshot is invalid.")); + } + if(m_pInputSnapshot->m_bAutoFitTargetOnly) { return true; } @@ -1119,7 +1131,9 @@ bool nmCalculationDllPebiSolverTask::prepareManualInput() m_pInputSnapshot->m_oGridInput, &m_nCancelRequested) || isCancelRequested()) { - return false; + return isCancelRequested() + ? false + : fail(tr("Failed to prepare solver input.")); } reportStage(tr("Checking grid cache..."), 0, 0); @@ -1149,7 +1163,7 @@ bool nmCalculationDllPebiSolverTask::prepareManualInput() return false; } if(m_pInputSnapshot->m_pBaseGrid->GetNumberOfCells() <= 0) { - return false; + return fail(tr("The prepared result grid is invalid.")); } } else if(isCancelRequested()) { return false; @@ -1177,6 +1191,11 @@ int nmCalculationDllPebiSolverTask::getPebiCount() const return m_nPebiCount; } +QString nmCalculationDllPebiSolverTask::getFailureReason() const +{ + return m_sFailureReason; +} + QVector > nmCalculationDllPebiSolverTask::getAutoFitResultPressure() const { return m_autoFitResultPressure; @@ -1205,7 +1224,9 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() { if(!m_bInputSnapshotValid || m_pInputSnapshot == nullptr || isCancelRequested()) { - return false; + return isCancelRequested() + ? false + : fail(tr("Solver input snapshot is invalid.")); } const QAtomicInt* pCancelRequested = @@ -1229,12 +1250,17 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() const bool bCreateUnstructuredGrid = !m_pInputSnapshot->m_bAutoFitTargetOnly; reportStage(tr("Generating grid..."), 0, 0); - if(!nmCalculationPebiGrid::getInstance()->calculateSnapshot( + nmCalculationPebiGrid* pGridService = + nmCalculationPebiGrid::getInstance(); + if(pGridService == nullptr || + !pGridService->calculateSnapshot( m_pInputSnapshot->m_oGridInput, oGridResult, bCreateUnstructuredGrid, pCancelRequested)) { - return false; + return isCancelRequested() + ? false + : fail(tr("Grid generation failed.")); } if(isCancelRequested()) { @@ -1244,7 +1270,7 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() if(bCreateUnstructuredGrid) { if(oGridResult.m_pUnstructuredGrid == nullptr || oGridResult.m_pUnstructuredGrid->GetNumberOfCells() <= 0) { - return false; + return fail(tr("The generated grid is invalid.")); } m_pInputSnapshot->m_pBaseGrid = @@ -1262,7 +1288,9 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() if(!oGridResult.m_bSucceeded || oGridResult.m_oGridOutput1.PEBI_cell.p.empty() || isCancelRequested()) { - return false; + return isCancelRequested() + ? false + : fail(tr("The generated grid is invalid.")); } // 第三步:由场景值快照重建完整模型输入。属性插值可以耗时,但它只读取任务 @@ -1283,7 +1311,7 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() .arg(sInterpolationError); qWarning() << sLogMessage; zxLogInstance::getInstance()->writeLogF(sLogMessage); - return false; + return fail(tr("Property interpolation failed.")); } if(isCancelRequested()) { @@ -1301,7 +1329,7 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() if(hModelModule == nullptr) { qWarning() << "Failed to load HX_NWTM.dll. Error code:" << GetLastError(); - return false; + return fail(tr("Failed to load HX_NWTM.dll.")); } typedef void (*HX_NWTM_MODEL_Fun)( @@ -1332,7 +1360,7 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() pfnGetSolveTime == nullptr) { qWarning() << "Failed to resolve PEBI solver configuration interface."; FreeLibrary(hModelModule); - return false; + return fail(tr("The solver interface is incomplete.")); } HX_NWTM_MODEL_OUTPUT oModelOutput; @@ -1356,12 +1384,12 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() .arg(e.what()); logHX_NWTM_MODEL_INPUT_Simplified(oModelInput); FreeLibrary(hModelModule); - return false; + return fail(tr("An exception occurred while calling the solver.")); } catch(...) { qWarning() << "SEH Exception Occurred during HX_NWTM_MODEL call"; logHX_NWTM_MODEL_INPUT_Simplified(oModelInput); FreeLibrary(hModelModule); - return false; + return fail(tr("An exception occurred while calling the solver.")); } // 后处理只读取本次局部输出,无需继续占用进程级 DLL 锁。 @@ -1378,6 +1406,9 @@ bool nmCalculationDllPebiSolverTask::execPebiMode() oModelOutput, oModelInput.T, oGridResult.m_oGridOutput1); + if(!bSucceeded && !isCancelRequested() && m_sFailureReason.isEmpty()) { + fail(tr("The solver result data is invalid.")); + } return bSucceeded; } @@ -1408,7 +1439,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( const HX_NWTM_GRID_OUTPUT1& oGridOutput) { if(m_pInputSnapshot == nullptr) { - return false; + return fail(tr("The solver result data is invalid.")); } const bool autoFitTargetOnly = @@ -1430,7 +1461,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( if(usePseudoPressure && calPseudoPressure == nullptr) { qWarning() << "Failed to load the gas pseudo-pressure converter."; - return false; + return fail(tr("Failed to load the gas pseudo-pressure converter.")); } // 井顺序与后处理输入都来自任务启动时的同一份快照。 @@ -1444,7 +1475,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( if(vecWellsOrder.isEmpty() || vecWellInputs.size() != vecWellsOrder.size() || p1.t.empty()) { - return false; + return fail(tr("The solver returned incomplete result data.")); } for(size_t nTimeIndex = 0; nTimeIndex < p1.t.size(); ++nTimeIndex) { if((nTimeIndex % 256) == 0 && isCancelRequested()) { @@ -1453,7 +1484,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( if(!isFiniteSolverNumber(p1.t[nTimeIndex])) { qWarning() << "PEBI returned a non-finite time at index:" << static_cast(nTimeIndex); - return false; + return fail(tr("The solver returned invalid time data.")); } } for(int nIndex = 0; nIndex < vecWellsOrder.size(); ++nIndex) { @@ -1471,7 +1502,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( vecWellInputs[nIndex].m_sWellCode != oWellRef.m_sWellCode) { qWarning() << "Incomplete well pressure returned for WellCode:" << oWellRef.m_sWellCode; - return false; + return fail(tr("The solver returned incomplete well pressure data.")); } for(size_t nTimeIndex = 0; nTimeIndex < p1.pw[nIndex].size(); @@ -1483,7 +1514,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( qWarning() << "PEBI returned a non-finite pressure for WellCode:" << oWellRef.m_sWellCode << "time index:" << static_cast(nTimeIndex); - return false; + return fail(tr("The solver returned invalid well pressure data.")); } } } @@ -1573,7 +1604,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( qWarning() << "Failed to convert gas pressure to pseudo-pressure:" << p1.pw[wellIdx][i] << "well code:" << oWellRef.m_sWellCode; - return false; + return fail(tr("Failed to convert gas pressure to pseudo-pressure.")); } pt.y = pressureForLog; @@ -1619,7 +1650,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( if(nullptr == preLogFun) { FreeLibrary(hMod_solver); std::cout << "preLogFun failed!\n"; - return false; + return fail(tr("The pressure curve interface is incomplete.")); } // 气井传入拟压力序列, 油井和水井仍传入原始压力序列. @@ -1631,7 +1662,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( nTimeNumQ, logPreResultFromDll)) { FreeLibrary(hMod_solver); - return false; + return fail(tr("Failed to calculate pressure curves.")); } if(isCancelRequested()) { @@ -1694,7 +1725,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( FreeLibrary(hMod_solver); } else { qWarning() << "Failed to load singlePhaseSolverDll.dll."; - return false; + return fail(tr("Failed to load singlePhaseSolverDll.dll.")); } } @@ -1730,13 +1761,15 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( && !m_autoFitResultLogLog[0].isEmpty() && m_autoFitResultLogLog[0].size() == m_autoFitResultLogLog[1].size() && m_autoFitResultLogLog[0].size() == m_autoFitResultLogLog[2].size(); - return pressureValid && logLogValid; + return pressureValid && logLogValid + ? true + : fail(tr("The calculated pressure curves are invalid.")); } // 第四步:完整求解还必须具有与时间轴、网格绘图单元严格对应的场压力。 if(p1.p.size() != p1.t.size() || oGridOutput.PEBI_cell.isplot.empty()) { - return false; + return fail(tr("The solver returned incomplete field pressure data.")); } // 计算有效单元数量 @@ -1752,7 +1785,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( } if(actualPlotCellsCount == 0) { - return false; + return fail(tr("The generated grid is invalid.")); } for(size_t nTimeIndex = 0; nTimeIndex < p1.p.size(); ++nTimeIndex) { if(isCancelRequested()) { @@ -1761,7 +1794,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( if(p1.p[nTimeIndex].size() < oGridOutput.PEBI_cell.isplot.size()) { qWarning() << "Incomplete field pressure returned for time index:" << static_cast(nTimeIndex); - return false; + return fail(tr("The solver returned incomplete field pressure data.")); } for(size_t nCellIndex = 0; nCellIndex < oGridOutput.PEBI_cell.isplot.size(); @@ -1774,7 +1807,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( qWarning() << "PEBI returned a non-finite field pressure at time/cell:" << static_cast(nTimeIndex) << static_cast(nCellIndex); - return false; + return fail(tr("The solver returned invalid field pressure data.")); } } } @@ -1841,14 +1874,14 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( // QMap 使用时间作为唯一键;重复时间会覆盖旧帧,因此必须检查数量一致。 if(m_mapPendingTimeSteps.size() != static_cast(p1.t.size())) { - return false; + return fail(tr("The solver returned invalid time data.")); } // 第六步:基础网格在任务创建或局部建网阶段已经完成深拷贝。后台结果构造 // 不再读取 DataManager 中可能被网格窗口替换的 VTK 指针。 if(m_pInputSnapshot->m_pBaseGrid == nullptr || m_pInputSnapshot->m_pBaseGrid->GetNumberOfCells() <= 0) { - return false; + return fail(tr("The prepared result grid is invalid.")); } int nExpectedWellResultCount = 0; @@ -1858,7 +1891,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( } } if(m_vecPendingWellResults.size() != nExpectedWellResultCount) { - return false; + return fail(tr("The solver returned incomplete well pressure data.")); } m_dPendingScalarMin = dMinP; @@ -1866,6 +1899,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult( m_bPendingFullResultReady = buildPebiResultSnapshotCandidate(); if(!m_bPendingFullResultReady) { // 候选结构或内存校验失败时立即释放大结果,旧快照保持原状。 + fail(tr("Failed to build the result snapshot.")); discardPendingFullResult(); } return m_bPendingFullResultReady; @@ -2037,6 +2071,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate() return true; } catch(const std::bad_alloc&) { qWarning() << "Cannot allocate PEBI result snapshot candidate."; + fail(tr("Insufficient memory to build the result snapshot.")); m_pPendingResultSnapshot.clear(); return false; } @@ -2056,13 +2091,16 @@ bool nmCalculationDllPebiSolverTask::commitResult( pDataManager != m_pDataManager || QThread::currentThread() != pDataManager->thread()) { discardPendingFullResult(); - return false; + return fail(tr("Failed to commit the solver results.")); } nmDataNumericalAnalysisCase* pAnalysisCase = pDataManager->getNumericalAnalysisCase(); - if(pAnalysisCase == nullptr || - pAnalysisCase->getGridInputRevision() != m_nGridInputRevision || + if(pAnalysisCase == nullptr) { + discardPendingFullResult(); + return fail(tr("Failed to commit the solver results.")); + } + if(pAnalysisCase->getGridInputRevision() != m_nGridInputRevision || pAnalysisCase->getResultInputRevision() != m_nResultInputRevision) { discardPendingFullResult(); return false; @@ -2071,16 +2109,19 @@ bool nmCalculationDllPebiSolverTask::commitResult( // 第二步:任务后台生成了新网格时,先在当前主线程按同一输入版本提交。 // 已有网格路径则再次确认其仍然有效。两条路径都不允许旧任务覆盖新编辑。 if(m_pInputSnapshot->m_bGridResultNeedsCommit) { - if(!nmCalculationPebiGrid::getInstance()->commitSnapshotResult( + nmCalculationPebiGrid* pGridService = + nmCalculationPebiGrid::getInstance(); + if(pGridService == nullptr || + !pGridService->commitSnapshotResult( pDataManager, m_pInputSnapshot->m_oGridInput, m_pInputSnapshot->m_oGridResult)) { discardPendingFullResult(); - return false; + return fail(tr("Failed to commit the generated grid.")); } } else if(!pAnalysisCase->isGridValid()) { discardPendingFullResult(); - return false; + return fail(tr("The generated grid is no longer valid.")); } // 第三步:Manager 在唯一发布点校验 UUID、槽位和版本后替换规范快照。 @@ -2090,6 +2131,7 @@ bool nmCalculationDllPebiSolverTask::commitResult( &sError); if(!bCommitted) { qWarning() << "Cannot commit PEBI result snapshot:" << sError; + fail(tr("Failed to commit the solver results.")); } // 成功和失败都不允许任务继续持有候选可写别名;旧快照只由 Manager 决定。 diff --git a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp index 3603ee5..b81b67c 100644 --- a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp +++ b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp @@ -1764,6 +1764,7 @@ void nmSubWndMain::continueSolverInputCapture() const bool bCancelled = pTask->wasCancelled(); const bool bInputChanged = pTask->hasInputChanged(m_pSolverDataManager.data()); + const QString sFailureReason = pTask->getFailureReason(); m_pSolverTask = nullptr; if(pTask == s_pRunningSolverTask) { s_pRunningSolverTask = nullptr; @@ -1783,7 +1784,11 @@ void nmSubWndMain::continueSolverInputCapture() m_pSolverDataManager = nullptr; m_pSolverFitting = nullptr; } else { - scheduleSolverResultCompletion(false, -1, -1, bInputChanged); + scheduleSolverResultCompletion(false, + -1, + -1, + bInputChanged, + sFailureReason); } m_bSolverCancelRequested = false; return; @@ -2377,13 +2382,18 @@ void nmSubWndMain::geoLayering() } } -bool nmSubWndMain::on_calculationFinished(NM_Calculation_Result result) +bool nmSubWndMain::on_calculationFinished( + NM_Calculation_Result result, + const QString& sFailureReason) { if(result == NM_Calculation_Result_Success) { return this->mergeAnaResultToFitting(); } else { // 如果失败,进度条其实没必要拉满了,直接提示错误 - QMessageBox::warning(this, tr("solver error"), tr("solver failed!")); + const QString sMessage = sFailureReason.isEmpty() + ? tr("No specific failure reason is available.") + : sFailureReason; + QMessageBox::warning(this, tr("Solver failed"), sMessage); return false; } } @@ -2392,12 +2402,14 @@ void nmSubWndMain::scheduleSolverResultCompletion( bool bSucceeded, int nPebiCount, int nSolveTimeMs, - bool bInputChanged) + bool bInputChanged, + const QString& sFailureReason) { s_pPendingSolverWindow = this; m_bSolverResultCompletionPending = true; m_bPendingSolverSucceeded = bSucceeded; m_bPendingSolverInputChanged = bInputChanged; + m_sPendingSolverFailureReason = sFailureReason; m_nPendingSolverPebiCount = nPebiCount; m_nPendingSolverTimeMs = nSolveTimeMs; @@ -2421,11 +2433,13 @@ void nmSubWndMain::completePendingSolverResult() const bool bSucceeded = m_bPendingSolverSucceeded; const bool bInputChanged = m_bPendingSolverInputChanged; + const QString sFailureReason = m_sPendingSolverFailureReason; const int nPebiCount = m_nPendingSolverPebiCount; const int nSolveTimeMs = m_nPendingSolverTimeMs; m_bSolverResultCompletionPending = false; m_bPendingSolverSucceeded = false; m_bPendingSolverInputChanged = false; + m_sPendingSolverFailureReason.clear(); m_nPendingSolverPebiCount = -1; m_nPendingSolverTimeMs = -1; @@ -2437,9 +2451,11 @@ void nmSubWndMain::completePendingSolverResult() tr("The input changed while the solver was preparing or running. " "The outdated result was discarded.")); } else { - bResultWindowReady = on_calculationFinished(bSucceeded - ? NM_Calculation_Result_Success - : NM_Calculation_Result_Fail); + bResultWindowReady = on_calculationFinished( + bSucceeded + ? NM_Calculation_Result_Success + : NM_Calculation_Result_Fail, + sFailureReason); } // mergeAnaResultToFitting() 已使用完启动时捕获的上下文。 @@ -2496,6 +2512,7 @@ void nmSubWndMain::on_solverTaskFinished(bool isSuccessed) int nPebiCount = pTask != nullptr ? pTask->getPebiCount() : -1; int nSolveTimeMs = pTask != nullptr ? pTask->getSolveTimeMs() : -1; const bool bCancelled = pTask->wasCancelled(); + QString sFailureReason = pTask->getFailureReason(); bool bInputChanged = false; // 后台线程只生成局部结果快照。回到主线程后先验证所属 DataManager 和 // 输入版本,再整体替换旧成果;输入变化与真正的求解失败分别提示。 @@ -2508,6 +2525,9 @@ void nmSubWndMain::on_solverTaskFinished(bool isSuccessed) pTask->hasInputChanged(m_pSolverDataManager.data()); isSuccessed = pTask->commitResult(m_pSolverDataManager.data()); bInputChanged = !isSuccessed && bInputChanged; + if(!isSuccessed) { + sFailureReason = pTask->getFailureReason(); + } } else if(bCancelled) { isSuccessed = false; } @@ -2530,7 +2550,8 @@ void nmSubWndMain::on_solverTaskFinished(bool isSuccessed) scheduleSolverResultCompletion(isSuccessed, nPebiCount, nSolveTimeMs, - bInputChanged); + bInputChanged, + sFailureReason); } }