fix(numerical): 修复无流量观察井出现在成果下拉框

- 求解完成时冻结成果可选井列表
- 保存并恢复成果可选井编码,保证重载后下拉框一致
- 保留观察井内部计算结果,仅从成果井选择界面过滤
feature/grid-solver-workflow-20260827
lh 2 weeks ago
parent 1798c851a7
commit 82d2ace443

@ -5,6 +5,7 @@
#include "nmDefines.h"
#include <QString>
#include <QStringList>
#include <QVector>
/**
@ -129,10 +130,17 @@ public:
/** @brief 返回最后一次成功求解所使用的 DLL 井槽位顺序。 */
QVector<nmSolverWellRef> getResultSolverWellOrder() const;
/** @brief 返回最后一次成功求解时冻结的结果下拉框井编码。 */
QStringList getResultSelectableWellCodes() const;
/** @brief 从成果文件恢复结果存在状态、当前性和独立井顺序。 */
void restoreResultState(const QVector<nmSolverWellRef>& vecOrder,
bool bAvailable,
bool bCurrent);
/** @brief 从成果文件同时恢复冻结的结果下拉框井编码。 */
void restoreResultState(const QVector<nmSolverWellRef>& vecOrder,
const QStringList& listSelectableWellCodes,
bool bAvailable,
bool bCurrent);
/** @brief 选井或几何输入改变后,使当前网格和结果失效。 */
void invalidateGrid();
@ -173,6 +181,8 @@ private:
void normalizeIncludedWells();
/** @brief 保证当前结果井属于最后结果顺序,否则回退到第一口真实井。 */
void normalizeCurrentResultWellCode();
/** @brief 按本次主井和已启用主动井冻结结果下拉框井编码。 */
void freezeResultSelectableWellCodes();
private:
QString m_sPrimaryWellCode; ///< 主分析井的项目唯一 WellCode。
@ -184,6 +194,7 @@ private:
QString m_sCurrentResultWellCode; ///< 当前结果查看井,仅属于展示状态。
QVector<nmSolverWellRef> m_vecSolverWellOrder; ///< 网格确定的 DLL 数组下标映射。
QVector<nmSolverWellRef> m_vecResultSolverWellOrder; ///< 最后结果使用的独立槽位映射。
QStringList m_listResultSelectableWellCodes; ///< 最后结果允许在下拉框中显示的井编码。
quint64 m_nGridInputRevision; ///< 几何、选井等网格输入的最新版本。
quint64 m_nBuiltGridRevision; ///< 当前缓存网格对应版本0 表示无有效网格。

@ -3541,19 +3541,11 @@ bool nmDataAnalyzeManager::isWellAvailableForResult(
return false;
}
// 已有结果使用求解完成时冻结的井顺序。当前参数、选井或网格变化
// 不能把最后一套结果中的井从结果界面移除
// 已有结果使用求解完成时冻结的可显示井列表。当前参数、选井或网格变化
// 不能改变最后一套结果的下拉框内容,自动加入的无流量观察井也不会进入界面
if(m_oNumericalAnalysisCase.hasResults()) {
const QVector<nmSolverWellRef> vecResultOrder =
m_oNumericalAnalysisCase.getResultSolverWellOrder();
for(int nIndex = 0; nIndex < vecResultOrder.size(); ++nIndex) {
const nmSolverWellRef& oWellRef = vecResultOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well &&
oWellRef.m_sWellCode == sWellCode) {
return true;
}
}
return false;
return m_oNumericalAnalysisCase
.getResultSelectableWellCodes().contains(sWellCode);
}
// 第一步:主分析井是结果界面的固定入口,即使当前处于关井段也必须保留。
@ -3562,7 +3554,7 @@ bool nmDataAnalyzeManager::isWellAvailableForResult(
}
// 第二步:其他井只有同时具备有效产量并以主动井角色参与当前求解时,
// 才能出现在“结果井”列表。所有无流量观察井一律只在内部保存计算结果。
// 才能出现在“结果井”列表。主井以外的无流量观察井只在内部保存计算结果。
if(pWellData->getFlowPoints().size() < 2) {
return false;
}
@ -4109,6 +4101,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
QVector<nmCalculationWellRef> vecSavedIncludedWells;
QVector<nmSolverWellRef> vecSavedSolverOrder;
QVector<nmSolverWellRef> vecSavedResultSolverOrder;
QStringList listSavedResultSelectableWellCodes;
// 第一步:版本 2 必须完整保存分析方案,缺字段时不再按旧项目规则猜测。
if(!doc.HasMember("NumericalAnalysisCase") ||
@ -4310,14 +4303,17 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
const bool bHasAnyResultStateField =
oCaseJson.HasMember("ResultsAvailable") ||
oCaseJson.HasMember("ResultsCurrent") ||
oCaseJson.HasMember("ResultSolverWellOrder");
oCaseJson.HasMember("ResultSolverWellOrder") ||
oCaseJson.HasMember("ResultSelectableWellCodes");
const bool bHasCompleteResultState =
oCaseJson.HasMember("ResultsAvailable") &&
oCaseJson["ResultsAvailable"].IsBool() &&
oCaseJson.HasMember("ResultsCurrent") &&
oCaseJson["ResultsCurrent"].IsBool() &&
oCaseJson.HasMember("ResultSolverWellOrder") &&
oCaseJson["ResultSolverWellOrder"].IsArray();
oCaseJson["ResultSolverWellOrder"].IsArray() &&
(!oCaseJson.HasMember("ResultSelectableWellCodes") ||
oCaseJson["ResultSelectableWellCodes"].IsArray());
if(bHasAnyResultStateField && !bHasCompleteResultState) {
qWarning() << "Numerical result state contains invalid fields:"
<< filePath;
@ -4393,6 +4389,34 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
sWellCode, eEntryKind));
}
const bool bHasSelectableWellCodes =
oCaseJson.HasMember("ResultSelectableWellCodes");
if(bHasSelectableWellCodes) {
QSet<QString> setSelectableWellCodes;
const rapidjson::Value& vecSelectableWellCodesJson =
oCaseJson["ResultSelectableWellCodes"];
for(rapidjson::SizeType nIndex = 0;
nIndex < vecSelectableWellCodesJson.Size();
++nIndex) {
if(!vecSelectableWellCodesJson[nIndex].IsString()) {
qWarning() << "ResultSelectableWellCodes contains an invalid entry:"
<< filePath;
return false;
}
const QString sWellCode = QString::fromUtf8(
vecSelectableWellCodesJson[nIndex].GetString());
if(!setResultWellCodes.contains(sWellCode) ||
setSelectableWellCodes.contains(sWellCode)) {
qWarning() << "ResultSelectableWellCodes contains an invalid WellCode:"
<< sWellCode;
return false;
}
setSelectableWellCodes.insert(sWellCode);
listSavedResultSelectableWellCodes.append(sWellCode);
}
}
if(bSavedResultsAvailable &&
(vecSavedResultSolverOrder.isEmpty() ||
!setResultWellCodes.contains(sSavedCurrentResultWellCode))) {
@ -4400,6 +4424,17 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
<< filePath;
return false;
}
if(bHasSelectableWellCodes &&
((bSavedResultsAvailable &&
(listSavedResultSelectableWellCodes.isEmpty() ||
!listSavedResultSelectableWellCodes.contains(
sSavedCurrentResultWellCode))) ||
(!bSavedResultsAvailable &&
!listSavedResultSelectableWellCodes.isEmpty()))) {
qWarning() << "ResultSelectableWellCodes is inconsistent with result state:"
<< filePath;
return false;
}
} else {
vecSavedResultSolverOrder = vecSavedSolverOrder;
}
@ -4798,13 +4833,14 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
m_oNumericalAnalysisCase.setIncludedWells(vecSavedIncludedWells);
m_oNumericalAnalysisCase.setIncludeOtherWells(bSavedIncludeOtherWells);
m_oNumericalAnalysisCase.setSolverWellOrder(vecSavedSolverOrder);
m_oNumericalAnalysisCase.setCurrentResultWellCode(
sSavedCurrentResultWellCode.isEmpty()
? sSavedPrimaryWellCode : sSavedCurrentResultWellCode);
m_oNumericalAnalysisCase.restoreResultState(
vecSavedResultSolverOrder,
listSavedResultSelectableWellCodes,
bSavedResultsAvailable,
bSavedResultsCurrent);
m_oNumericalAnalysisCase.setCurrentResultWellCode(
sSavedCurrentResultWellCode.isEmpty()
? sSavedPrimaryWellCode : sSavedCurrentResultWellCode);
// 解析 时间步 对象
@ -4944,9 +4980,30 @@ bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath)
return false;
}
}
QStringList listValidatedResultSelectableWellCodes;
if(bHasResults) {
listValidatedResultSelectableWellCodes =
m_oNumericalAnalysisCase.getResultSelectableWellCodes();
}
QSet<QString> setResultSelectableWellCodes;
for(int nIndex = 0;
nIndex < listValidatedResultSelectableWellCodes.size();
++nIndex) {
const QString sWellCode =
listValidatedResultSelectableWellCodes[nIndex];
if(!setResultWellCodes.contains(sWellCode) ||
setResultSelectableWellCodes.contains(sWellCode)) {
qWarning() << "Cannot save numerical project: invalid selectable result WellCode:"
<< sWellCode;
return false;
}
setResultSelectableWellCodes.insert(sWellCode);
}
if(bHasResults &&
(vecValidatedResultOrder.isEmpty() ||
!setResultWellCodes.contains(sCurrentResultWellCode))) {
listValidatedResultSelectableWellCodes.isEmpty() ||
!setResultSelectableWellCodes.contains(sCurrentResultWellCode))) {
qWarning() << "Cannot save numerical project: result order is incomplete.";
return false;
}
@ -5237,6 +5294,20 @@ bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath)
}
oCaseJson.AddMember("ResultSolverWellOrder",
vecResultOrderJson, allocator);
rapidjson::Value vecResultSelectableWellCodesJson(
rapidjson::kArrayType);
for(int nIndex = 0;
nIndex < listValidatedResultSelectableWellCodes.size();
++nIndex) {
QByteArray baWellCode =
listValidatedResultSelectableWellCodes[nIndex].toUtf8();
vecResultSelectableWellCodesJson.PushBack(
rapidjson::Value(baWellCode.constData(), allocator).Move(),
allocator);
}
oCaseJson.AddMember("ResultSelectableWellCodes",
vecResultSelectableWellCodesJson, allocator);
doc.AddMember("NumericalAnalysisCase", oCaseJson, allocator);
// 序列化 时间步数据
@ -5423,6 +5494,8 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
m_oNumericalAnalysisCase.hasResults();
const QVector<nmSolverWellRef> vecResultSolverOrder =
getResultSolverWellOrder();
const QStringList listResultSelectableWellCodes =
m_oNumericalAnalysisCase.getResultSelectableWellCodes();
// 第一步:先检查分析方案、网格文件和结果文件是否构成完整的一组。
QString sCurrentGridPath = sLoadAnalDir + "/Grid/CurrentGrid.vtu";
@ -5597,6 +5670,8 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
m_oNumericalAnalysisCase.restoreResultState(
bHasCompleteResultFiles
? vecResultSolverOrder : QVector<nmSolverWellRef>(),
bHasCompleteResultFiles
? listResultSelectableWellCodes : QStringList(),
bHasCompleteResultFiles,
bHasCompleteResultFiles && bHasCurrentGridFile &&
bSavedResultsCurrent);

@ -304,22 +304,54 @@ nmDataNumericalAnalysisCase::getResultSolverWellOrder() const
return m_vecResultSolverWellOrder;
}
QStringList nmDataNumericalAnalysisCase::getResultSelectableWellCodes() const
{
return m_listResultSelectableWellCodes;
}
void nmDataNumericalAnalysisCase::restoreResultState(
const QVector<nmSolverWellRef>& vecOrder,
bool bAvailable,
bool bCurrent)
{
restoreResultState(vecOrder, QStringList(), bAvailable, bCurrent);
}
void nmDataNumericalAnalysisCase::restoreResultState(
const QVector<nmSolverWellRef>& vecOrder,
const QStringList& listSelectableWellCodes,
bool bAvailable,
bool bCurrent)
{
m_vecResultSolverWellOrder = vecOrder;
QSet<QString> setResultWellCodes;
for(int nIndex = 0;
nIndex < m_vecResultSolverWellOrder.size();
++nIndex) {
m_vecResultSolverWellOrder[nIndex].m_nSolverIndex = nIndex;
const nmSolverWellRef& oWellRef = m_vecResultSolverWellOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
setResultWellCodes.insert(oWellRef.m_sWellCode);
}
}
m_listResultSelectableWellCodes.clear();
for(int nIndex = 0; nIndex < listSelectableWellCodes.size(); ++nIndex) {
const QString sWellCode = listSelectableWellCodes[nIndex];
if(setResultWellCodes.contains(sWellCode) &&
!m_listResultSelectableWellCodes.contains(sWellCode)) {
m_listResultSelectableWellCodes.append(sWellCode);
}
}
m_bResultsAvailable = bAvailable &&
!m_vecResultSolverWellOrder.isEmpty();
m_bResultsValid = m_bResultsAvailable && bCurrent;
if(m_bResultsAvailable) {
// 旧成果没有独立可显示井字段时,按保存的主井和包含井保守恢复。
if(m_listResultSelectableWellCodes.isEmpty()) {
freezeResultSelectableWellCodes();
}
normalizeCurrentResultWellCode();
}
}
@ -375,6 +407,7 @@ void nmDataNumericalAnalysisCase::markResultsAvailable()
}
m_vecResultSolverWellOrder = m_vecSolverWellOrder;
freezeResultSelectableWellCodes();
m_bResultsAvailable = true;
m_bResultsValid = true;
normalizeCurrentResultWellCode();
@ -396,6 +429,7 @@ bool nmDataNumericalAnalysisCase::markResultsAvailableIfCurrent(
}
m_vecResultSolverWellOrder = m_vecSolverWellOrder;
freezeResultSelectableWellCodes();
m_bResultsAvailable = true;
m_bResultsValid = true;
normalizeCurrentResultWellCode();
@ -451,6 +485,7 @@ void nmDataNumericalAnalysisCase::clear()
m_sCurrentResultWellCode.clear();
m_vecSolverWellOrder.clear();
m_vecResultSolverWellOrder.clear();
m_listResultSelectableWellCodes.clear();
m_nGridInputRevision = 1;
m_nBuiltGridRevision = 0;
m_nResultInputRevision = 1;
@ -482,22 +517,38 @@ void nmDataNumericalAnalysisCase::normalizeIncludedWells()
void nmDataNumericalAnalysisCase::normalizeCurrentResultWellCode()
{
QString sFirstResultWellCode;
if(m_listResultSelectableWellCodes.contains(
m_sCurrentResultWellCode)) {
return;
}
m_sCurrentResultWellCode = m_listResultSelectableWellCodes.isEmpty()
? QString() : m_listResultSelectableWellCodes.first();
}
void nmDataNumericalAnalysisCase::freezeResultSelectableWellCodes()
{
QSet<QString> setSelectableWellCodes;
if(!m_sPrimaryWellCode.isEmpty()) {
setSelectableWellCodes.insert(m_sPrimaryWellCode);
}
if(m_bIncludeOtherWells) {
for(int nIndex = 0; nIndex < m_vecIncludedWells.size(); ++nIndex) {
setSelectableWellCodes.insert(
m_vecIncludedWells[nIndex].m_sWellCode);
}
}
m_listResultSelectableWellCodes.clear();
for(int nIndex = 0;
nIndex < m_vecResultSolverWellOrder.size();
++nIndex) {
const nmSolverWellRef& oWellRef =
m_vecResultSolverWellOrder[nIndex];
if(oWellRef.m_eEntryKind != NM_SolverEntry_Well) {
continue;
}
if(sFirstResultWellCode.isEmpty()) {
sFirstResultWellCode = oWellRef.m_sWellCode;
}
if(oWellRef.m_sWellCode == m_sCurrentResultWellCode) {
return;
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well &&
setSelectableWellCodes.contains(oWellRef.m_sWellCode) &&
!m_listResultSelectableWellCodes.contains(oWellRef.m_sWellCode)) {
m_listResultSelectableWellCodes.append(oWellRef.m_sWellCode);
}
}
m_sCurrentResultWellCode = sFirstResultWellCode;
}

@ -378,7 +378,7 @@ void nmWxNumericalDesign::fillResultWellCombo(const QString& sSelectedWellCode)
: pManager->getSolverWellOrder();
// 第一步:有结果时优先按最后结果顺序,否则按当前网格顺序收集结果井。
// 无流量观察井仍在求解器顺序中,但不能出现在本下拉框。
// 自动加入的无流量观察井仍在求解器顺序中,但不能出现在本下拉框。
for(int nOrderIndex = 0; nOrderIndex < vecSolverOrder.size(); ++nOrderIndex) {
const nmSolverWellRef& oWellRef = vecSolverOrder[nOrderIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well &&

Loading…
Cancel
Save