fix(nmNum): 修复数值面板初始化和多段压裂水平井适配

- 补充数值面板初始化失败提示和空数据保护
- 统一使用 HorizontalMultiFracturedWell 井型
- 防止求解结果为空时发生越界
- 优化 Release 下左侧面板高度分配
- 同步中文翻译
feature/Merge-Framework-20260721
lh 1 week ago
parent d7fa5676db
commit 4deee62207

Binary file not shown.

@ -16,6 +16,18 @@
&apos;%1&apos;</source> &apos;%1&apos;</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Numerical Panel Initialization Failed</source>
<translation></translation>
</message>
<message>
<source>The numerical panels could not be opened.
Reason: %1</source>
<translation>
%1</translation>
</message>
</context> </context>
<context> <context>
<name>iWxBase</name> <name>iWxBase</name>
@ -2841,6 +2853,50 @@
Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured Wells.</source> Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured Wells.</source>
<translation>%1 </translation> <translation>%1 </translation>
</message> </message>
<message>
<source>The current window is not a flow-segment analysis window.</source>
<translation></translation>
</message>
<message>
<source>The numerical panel containers have not been created.</source>
<translation></translation>
</message>
<message>
<source>No current well is available. Please select a well and try again.</source>
<translation></translation>
</message>
<message>
<source>The current well type &apos;%1&apos; is not supported. Supported types are Vertical, Vertical Fractured, and Horizontal Multi-Fractured Wells.</source>
<translation>%1</translation>
</message>
<message>
<source>The numerical data manager could not be created.</source>
<translation></translation>
</message>
<message>
<source>The numerical well data could not be initialized for well &apos;%1&apos; (type: %2). Please check the well type and its pressure, flow-rate, and flow-segment data.</source>
<translation>%1%2</translation>
</message>
<message>
<source>The analytical rectangular-boundary data is incomplete. Missing parameters: %1.</source>
<translation>%1</translation>
</message>
<message>
<source>The rectangular-boundary data could not be created.</source>
<translation></translation>
</message>
<message>
<source>The circular-boundary data could not be created.</source>
<translation></translation>
</message>
<message>
<source>The default numerical boundary could not be created.</source>
<translation></translation>
</message>
<message>
<source>The numerical panel widgets could not be attached to their containers.</source>
<translation></translation>
</message>
</context> </context>
<context> <context>
<name>nmSwitchOutlineConfirmationDlg</name> <name>nmSwitchOutlineConfirmationDlg</name>

@ -960,7 +960,7 @@ void nmDataAnalyzeManager::initCurWellData()
// 设置为当前查看的井 // 设置为当前查看的井
this->setCurWellData(pVFracturedWell); this->setCurWellData(pVFracturedWell);
} else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalFracturedWell")) { } else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalMultiFracturedWell")) {
// 初始化多段压裂水平井默认参数 // 初始化多段压裂水平井默认参数
nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well); nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well);
@ -1041,8 +1041,12 @@ void nmDataAnalyzeManager::calculationLogData(
vvecHistoryData[1].append(qpoint.y()); // y vvecHistoryData[1].append(qpoint.y()); // y
} }
// 准备流量段数据 // Prepare flow-rate segment data.
QVector<QPointF> vecTimeQ = pWellData->getFlowPoints(); QVector<QPointF> vecTimeQ = pWellData->getFlowPoints();
if(vecPressure.isEmpty() || vecTimeQ.size() < 2) {
return;
}
int nTimeNumQ = vecTimeQ.size() - 1; int nTimeNumQ = vecTimeQ.size() - 1;
std::vector<double> timeQ(nTimeNumQ); std::vector<double> timeQ(nTimeNumQ);
std::vector<double> q(nTimeNumQ); std::vector<double> q(nTimeNumQ);
@ -1068,10 +1072,15 @@ void nmDataAnalyzeManager::calculationLogData(
return; return;
} }
preLogFun(wellPressureData, iSectionFlowIndex, timeQ.data(), q.data(), nTimeNumQ, logPre); bool bCalculated = preLogFun(wellPressureData, iSectionFlowIndex,
timeQ.data(), q.data(), nTimeNumQ, logPre);
if(!bCalculated || logPre.empty()) {
FreeLibrary(hMod_solver);
return;
}
// 不添加最后一个元素 // The solver's final point is not part of the plotted result.
for(uint i = 0; i < logPre.size() - 1; i++) { for(std::vector<Point>::size_type i = 0; i + 1 < logPre.size(); ++i) {
//logFile << logPre[i].x << "\t" << logPre[i].y << "\t" << logPre[i].z << "\t" << std::endl; //logFile << logPre[i].x << "\t" << logPre[i].y << "\t" << logPre[i].z << "\t" << std::endl;
vvecLogPreData[0].append(logPre[i].x); // x vvecLogPreData[0].append(logPre[i].x); // x
vvecLogPreData[1].append(logPre[i].y); // y vvecLogPreData[1].append(logPre[i].y); // y
@ -1213,7 +1222,7 @@ void nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
pVFracturedWell->setFracs(); pVFracturedWell->setFracs();
// 设置流量段索引 // 设置流量段索引
pVFracturedWell->setIndexF(nIndexF); pVFracturedWell->setIndexF(nIndexF);
} else if(ZxBaseUtil::isSameStr(sWellClass, "HorizontalFracturedWell")) { } else if(ZxBaseUtil::isSameStr(sWellClass, "HorizontalMultiFracturedWell")) {
// 初始化多段压裂水平井默认参数 // 初始化多段压裂水平井默认参数
nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well); nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well);

@ -369,7 +369,7 @@ void nmGuiPlot::initDefultGeoObj()
// 设置为当前查看的井 // 设置为当前查看的井
nmDataAnalyzeManager::getCurrentInstance()->setCurWellData(m_VFracturedWell); nmDataAnalyzeManager::getCurrentInstance()->setCurWellData(m_VFracturedWell);
} else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalFracturedWell")) { } else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalMultiFracturedWell")) {
// 初始化多段压裂水平井默认参数 // 初始化多段压裂水平井默认参数
nmDataWellBase* pWell = nmDataAnalyzeManager::getCurrentInstance()->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well); nmDataWellBase* pWell = nmDataAnalyzeManager::getCurrentInstance()->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well);

@ -324,7 +324,7 @@ void nmObjPointWell::afterCreated()
} else if(ZxBaseUtil::isSameStr(wellClass, "VerticalFracturedWell")) { } else if(ZxBaseUtil::isSameStr(wellClass, "VerticalFracturedWell")) {
// 切换当前井类型 // 切换当前井类型
m_enumWellType = NM_WELL_MODEL::Vertical_Fractured_Well; m_enumWellType = NM_WELL_MODEL::Vertical_Fractured_Well;
} else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalFracturedWell")) { } else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalMultiFracturedWell")) {
// 切换当前井类型 // 切换当前井类型
m_enumWellType = NM_WELL_MODEL::Horizontal_Fractured_Well; m_enumWellType = NM_WELL_MODEL::Horizontal_Fractured_Well;
} else { } else {
@ -485,7 +485,7 @@ void nmObjPointWell::editWell()
} else if(m_enumWellType == NM_WELL_MODEL::Vertical_Fractured_Well) { } else if(m_enumWellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
sWellClass = "VerticalFracturedWell"; sWellClass = "VerticalFracturedWell";
} else if(m_enumWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) { } else if(m_enumWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
sWellClass = "HorizontalFracturedWell"; sWellClass = "HorizontalMultiFracturedWell";
} }
m_pWellData->setWellClassCn(sWellClass); m_pWellData->setWellClassCn(sWellClass);
} }
@ -627,7 +627,7 @@ void nmObjPointWell::paintBack(QPainter* painter, const ZxPaintParam& param)
painter->restore(); painter->restore();
} else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalFracturedWell") && m_pNmWellData != nullptr) { } else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalMultiFracturedWell") && m_pNmWellData != nullptr) {
Q_ASSERT(NULL != m_pAxisX); Q_ASSERT(NULL != m_pAxisX);
Q_ASSERT(NULL != m_pAxisY); Q_ASSERT(NULL != m_pAxisY);
@ -763,7 +763,7 @@ void nmObjPointWell::setNmWellData(nmDataWellBase* wellData)
m_enumWellType = NM_WELL_MODEL::Vertical_Well; m_enumWellType = NM_WELL_MODEL::Vertical_Well;
} else if(ZxBaseUtil::isSameStr(wellClass, "VerticalFracturedWell")) { } else if(ZxBaseUtil::isSameStr(wellClass, "VerticalFracturedWell")) {
m_enumWellType = NM_WELL_MODEL::Vertical_Fractured_Well; m_enumWellType = NM_WELL_MODEL::Vertical_Fractured_Well;
} else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalFracturedWell")) { } else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalMultiFracturedWell")) {
m_enumWellType = NM_WELL_MODEL::Horizontal_Fractured_Well; m_enumWellType = NM_WELL_MODEL::Horizontal_Fractured_Well;
} }
} }

@ -1,4 +1,4 @@
#include "zxLogInstance.h" #include "zxLogInstance.h"
#include "iRibbonXmlCmd.h" #include "iRibbonXmlCmd.h"
#include <QEvent> #include <QEvent>
#include <QMdiArea> #include <QMdiArea>
@ -71,6 +71,15 @@ bool hasPvtCurve(iSubWndFitting* pSubWndF, PvtFluidType eType, const QStringList
return false; return false;
} }
bool showNmDockInitializationError(const QString& detail)
{
QMessageBox::warning(nullptr,
QObject::tr("Numerical Panel Initialization Failed"),
QObject::tr("The numerical panels could not be opened.\n\nReason: %1")
.arg(detail));
return false;
}
} }
nmSubWndUtils* nmSubWndUtils::getInstance() nmSubWndUtils* nmSubWndUtils::getInstance()
@ -347,10 +356,11 @@ bool nmSubWndUtils::dealwithRibbonTab(iRibbonXmlTab* pTab, \
bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd) bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
{ {
iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd); iSubWndFitting* pSubWndF = dynamic_cast<iSubWndFitting*>(pSubWnd);
Q_ASSERT(nullptr != pSubWndF);
if(pSubWndF == nullptr) { if(pSubWndF == nullptr) {
return false; return showNmDockInitializationError(
tr("The current window is not a flow-segment analysis window."));
} }
Q_ASSERT(nullptr != pSubWndF);
// 如果当前分析没有对应相态的PVT相关参数提示并返回 // 如果当前分析没有对应相态的PVT相关参数提示并返回
//PvtFluidType eType = pSubWndF->getBasicPft(); //PvtFluidType eType = pSubWndF->getBasicPft();
@ -383,6 +393,10 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
iDockBaseWx* pWxDockNm1 = pSubWndF->getNmDockWx(0); iDockBaseWx* pWxDockNm1 = pSubWndF->getNmDockWx(0);
iDockBaseWx* pWxDockNm2 = pSubWndF->getNmDockWx(1); iDockBaseWx* pWxDockNm2 = pSubWndF->getNmDockWx(1);
if(pWxDockNm1 == nullptr || pWxDockNm2 == nullptr) {
return showNmDockInitializationError(
tr("The numerical panel containers have not been created."));
}
Q_ASSERT(nullptr != pWxDockNm1); Q_ASSERT(nullptr != pWxDockNm1);
Q_ASSERT(nullptr != pWxDockNm2); Q_ASSERT(nullptr != pWxDockNm2);
if (pWxDockNm1->widget() != nullptr && pWxDockNm2->widget() != nullptr) if (pWxDockNm1->widget() != nullptr && pWxDockNm2->widget() != nullptr)
@ -393,7 +407,8 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
// 获取当前井并检查类型是否支持 // 获取当前井并检查类型是否支持
ZxDataWell* pWellData = zxCurWell; ZxDataWell* pWellData = zxCurWell;
if(pWellData == nullptr) { if(pWellData == nullptr) {
return false; return showNmDockInitializationError(
tr("No current well is available. Please select a well and try again."));
} }
QString wellClass = pWellData->getWellClassEn(); QString wellClass = pWellData->getWellClassEn();
@ -403,21 +418,21 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
supportedTypes << "VerticalWell" << "VerticalFracturedWell" << "HorizontalMultiFracturedWell"; supportedTypes << "VerticalWell" << "VerticalFracturedWell" << "HorizontalMultiFracturedWell";
if (!supportedTypes.contains(wellClass)) { if (!supportedTypes.contains(wellClass)) {
QMessageBox::warning(nullptr, tr("Unsupported Well Type"), return showNmDockInitializationError(
tr("The current well type '%1' is not supported for numerical modeling.\n" tr("The current well type '%1' is not supported. Supported types are Vertical, "
"Supported types: Vertical, Vertical Fractured, and Horizontal Multi-Fractured Wells.") "Vertical Fractured, and Horizontal Multi-Fractured Wells.")
.arg(pWellData->getWellClassCn())); .arg(pWellData->getWellClassCn()));
return false;
} }
// 进入到这里,说明是新打开的流动段分析,所以需要动态创建数据管理中心 // 进入到这里,说明是新打开的流动段分析,所以需要动态创建数据管理中心
// 根据分析窗口创建唯一的数据管理对象 // 根据分析窗口创建唯一的数据管理对象
nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getInstanceByFitting(pSubWndF); nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getInstanceByFitting(pSubWndF);
Q_ASSERT(nullptr != pDataManager);
if(pDataManager == nullptr) { if(pDataManager == nullptr) {
return false; return showNmDockInitializationError(
tr("The numerical data manager could not be created."));
} }
Q_ASSERT(nullptr != pDataManager);
// 每个成果窗口只需要绑定一次外层MDI激活信号。 // 每个成果窗口只需要绑定一次外层MDI激活信号。
// 后续切换成果窗口时,通过激活事件同步对应的数据中心和左侧面板。 // 后续切换成果窗口时,通过激活事件同步对应的数据中心和左侧面板。
@ -445,10 +460,14 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
pDataManager->initCurWellData(); pDataManager->initCurWellData();
// 1.4 初始化边界数据 // 1.4 初始化边界数据
nmDataWellBase* pCurWell = pDataManager->getCurWellData(); nmDataWellBase* pCurWell = pDataManager->getCurWellData();
Q_ASSERT(nullptr != pCurWell);
if(pCurWell == nullptr) { if(pCurWell == nullptr) {
return false; return showNmDockInitializationError(
tr("The numerical well data could not be initialized for well '%1' (type: %2). "
"Please check the well type and its pressure, flow-rate, and flow-segment data.")
.arg(pWellData->getName())
.arg(wellClass));
} }
Q_ASSERT(nullptr != pCurWell);
double dX = pCurWell->getX().getValue().toDouble(); double dX = pCurWell->getX().getValue().toDouble();
double dY = pCurWell->getY().getValue().toDouble(); double dY = pCurWell->getY().getValue().toDouble();
@ -457,7 +476,6 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
QMap<QString, QVariant> mapAnaParas; QMap<QString, QVariant> mapAnaParas;
mapAnaParas.clear(); mapAnaParas.clear();
pSubWndF->getBrotherDockParas(mapAnaParas, false); pSubWndF->getBrotherDockParas(mapAnaParas, false);
Q_ASSERT(mapAnaParas.count() > 0);
// 解析解边界类型 // 解析解边界类型
m_Bdy_Type oBdyType = BT_None; m_Bdy_Type oBdyType = BT_None;
@ -480,15 +498,24 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
vecBdyDiss.clear(); vecBdyDiss.clear();
QStringList list; QStringList list;
list << "ne" << "se" << "we" << "ee"; list << "ne" << "se" << "we" << "ee";
QStringList missingKeys;
for(int i = 0; i < list.count(); i++) { for(int i = 0; i < list.count(); i++) {
QString s = list[i]; QString s = list[i];
if(mapAnaParas.contains(s)) { if(mapAnaParas.contains(s)) {
vecBdyDiss << mapAnaParas[s].toDouble(); vecBdyDiss << mapAnaParas[s].toDouble();
} else {
missingKeys << s;
} }
} }
if(!missingKeys.isEmpty()) {
return showNmDockInitializationError(
tr("The analytical rectangular-boundary data is incomplete. Missing parameters: %1.")
.arg(missingKeys.join(", ")));
}
double top_dist = vecBdyDiss[0]; double top_dist = vecBdyDiss[0];
double right_dist = vecBdyDiss[1]; double right_dist = vecBdyDiss[1];
double bottom_dist = vecBdyDiss[2]; double bottom_dist = vecBdyDiss[2];
@ -503,10 +530,11 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
vecBoundaryPoints << topLeft << topRight << bottomRight << bottomLeft; vecBoundaryPoints << topLeft << topRight << bottomRight << bottomLeft;
// 创建矩形边界数据对象 // 创建矩形边界数据对象
nmDataOutline* pOutlineData = pDataManager->createOutline(); nmDataOutline* pOutlineData = pDataManager->createOutline();
Q_ASSERT(pOutlineData);
if(pOutlineData == nullptr) { if(pOutlineData == nullptr) {
return false; return showNmDockInitializationError(
tr("The rectangular-boundary data could not be created."));
} }
Q_ASSERT(pOutlineData);
// 设置默认边界名称 // 设置默认边界名称
pOutlineData->setName(""); pOutlineData->setName("");
// 设置边界点 // 设置边界点
@ -527,10 +555,11 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
// 创建圆形边界数据对象 // 创建圆形边界数据对象
nmDataOutline* pOutlineData = pDataManager->createOutline(); nmDataOutline* pOutlineData = pDataManager->createOutline();
Q_ASSERT(pOutlineData);
if(pOutlineData == nullptr) { if(pOutlineData == nullptr) {
return false; return showNmDockInitializationError(
tr("The circular-boundary data could not be created."));
} }
Q_ASSERT(pOutlineData);
// 设置默认边界名称 // 设置默认边界名称
pOutlineData->setName(""); pOutlineData->setName("");
// 设置半径 // 设置半径
@ -560,10 +589,11 @@ bool nmSubWndUtils::fillNmDockWxs(iSubWnd* pSubWnd)
// 如果解析解这里没有设置边界,数值解设置默认边界 // 如果解析解这里没有设置边界,数值解设置默认边界
// 创建默认矩形边界数据对象 // 创建默认矩形边界数据对象
nmDataOutline* pDefaultOutlineData = pDataManager->createOutline(); nmDataOutline* pDefaultOutlineData = pDataManager->createOutline();
Q_ASSERT(pDefaultOutlineData);
if(pDefaultOutlineData == nullptr) { if(pDefaultOutlineData == nullptr) {
return false; return showNmDockInitializationError(
tr("The default numerical boundary could not be created."));
} }
Q_ASSERT(pDefaultOutlineData);
// 设置默认边界名称 // 设置默认边界名称
pDefaultOutlineData->setName(""); pDefaultOutlineData->setName("");
// 设置边界点 // 设置边界点

Loading…
Cancel
Save