diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm index b3e2db2..00d929e 100644 Binary files a/Bin/Config/Lang/cn/nmNum_cn.qm and b/Bin/Config/Lang/cn/nmNum_cn.qm differ 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); } }