diff --git a/Bin/Config/Lang/cn/nmNum_cn.qm b/Bin/Config/Lang/cn/nmNum_cn.qm
index 5cb22c9a..398b4c45 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 93aefd4a..f1afb9c1 100644
--- a/Bin/Config/Lang/cn/nmNum_cn.ts
+++ b/Bin/Config/Lang/cn/nmNum_cn.ts
@@ -7130,6 +7130,12 @@ Average pressure in contour: %2 MPa
Well information井信息
Well number井号
Well name井名
+ Well category井别
+ Oil well油井
+ Gas well气井
+ Water well水井
+ Unknown未知
+ Well model井型
Well type井型
Project-level unique well identifier项目级唯一井标识
Vertical well直井
diff --git a/Include/nmNum/nmCalculation/nmCalculationPebiGrid.h b/Include/nmNum/nmCalculation/nmCalculationPebiGrid.h
index f784c750..3a19c921 100644
--- a/Include/nmNum/nmCalculation/nmCalculationPebiGrid.h
+++ b/Include/nmNum/nmCalculation/nmCalculationPebiGrid.h
@@ -27,7 +27,8 @@ class nmDataWellBase;
struct nmPebiWellInputSnapshot
{
nmPebiWellInputSnapshot()
- : m_nFlowSectionIndex(1),
+ : m_eWellCategory(NM_WellCategory_Unknown),
+ m_nFlowSectionIndex(1),
m_dRadius(0.0),
m_dWellboreStorage(0.0),
m_dSkin(0.0),
@@ -44,6 +45,7 @@ struct nmPebiWellInputSnapshot
QString m_sWellName; ///< 捕获时井名称。
QString m_sWellInstanceId; ///< 完整场结果使用的稳定井 UUID。
QVector m_vecFlowPoints; ///< 求解使用的流量制度。
+ NM_WELL_CATEGORY m_eWellCategory; ///< 求解时冻结的油井、气井或水井井别。
/** @brief 求解时冻结的历史压力曲线。 */
QVector > m_vecHistoryPressure;
/** @brief 求解时冻结的历史双对数曲线。 */
@@ -51,7 +53,7 @@ struct nmPebiWellInputSnapshot
/** @brief 求解时冻结的历史半对数曲线。 */
QVector > m_vecHistorySemiLog;
QPointF m_oLocation; ///< 求解时井平面位置。
- int m_nFlowSectionIndex; ///< 当前流动段下标。
+ int m_nFlowSectionIndex; ///< 当前流动段一基索引。
double m_dRadius; ///< 求解时井筒半径。
double m_dWellboreStorage; ///< 求解时井筒储集系数。
double m_dSkin; ///< 求解时表皮系数。
diff --git a/Include/nmNum/nmData/nmDataAnalyzeManager.h b/Include/nmNum/nmData/nmDataAnalyzeManager.h
index 502ecb64..1bf892a2 100644
--- a/Include/nmNum/nmData/nmDataAnalyzeManager.h
+++ b/Include/nmNum/nmData/nmDataAnalyzeManager.h
@@ -121,6 +121,37 @@ enum NM_SOLVER_MODEL_TYPE {
SMT_Oil_Gas_Water_ThreePhase = 10
};
+/** @brief 判断求解模型是否包含指定工程井别对应的流量相。 */
+inline bool nmSolverModelSupportsWellCategory(
+ NM_SOLVER_MODEL_TYPE eSolverModelType,
+ NM_WELL_CATEGORY eWellCategory)
+{
+ switch(eSolverModelType) {
+ case SMT_Oil_ConstPvt:
+ case SMT_Oil_VariablePvt:
+ return eWellCategory == NM_WellCategory_Oil;
+ case SMT_Water_ConstPvt:
+ case SMT_Water_VariablePvt:
+ return eWellCategory == NM_WellCategory_Water;
+ case SMT_Gas_VariablePvt:
+ case SMT_Gas_PseudoPressure:
+ return eWellCategory == NM_WellCategory_Gas;
+ case SMT_Oil_Gas_TwoPhase:
+ return eWellCategory == NM_WellCategory_Oil ||
+ eWellCategory == NM_WellCategory_Gas;
+ case SMT_Oil_Water_TwoPhase:
+ return eWellCategory == NM_WellCategory_Oil ||
+ eWellCategory == NM_WellCategory_Water;
+ case SMT_Gas_Water_TwoPhase:
+ return eWellCategory == NM_WellCategory_Gas ||
+ eWellCategory == NM_WellCategory_Water;
+ case SMT_Oil_Gas_Water_ThreePhase:
+ return nmIsValidWellCategory(eWellCategory);
+ default:
+ return false;
+ }
+}
+
class nmDataWellBase;
class ZxDataWell;
class nmDataReservoir;
@@ -704,7 +735,8 @@ public:
}
// 加载井数组里对应的压力、流量数据,这个是在井里没有这些数据的情况下调用
- void loadWellPreAndFlow();
+ /** @brief 按保存井别重新读取工程井压力和流量,任一井映射失败时返回 false。 */
+ bool loadWellPreAndFlow();
// 设置当前分析下的VTK网格对象
void setUnstructuredGrid(vtkSmartPointer grid);
diff --git a/Include/nmNum/nmData/nmDataWellBase.h b/Include/nmNum/nmData/nmDataWellBase.h
index eccbc6fd..ccfc297e 100644
--- a/Include/nmNum/nmData/nmDataWellBase.h
+++ b/Include/nmNum/nmData/nmDataWellBase.h
@@ -61,9 +61,14 @@ public:
void setWellCode(const QString& code);
QString getWellCode() const;
+ /** @brief 保存工程井的油井、气井或水井井别;该值不由数值井编辑器修改。 */
+ void setWellCategory(NM_WELL_CATEGORY eWellCategory);
+ /** @brief 返回工程井井别,用于选择流量大字段及求解器相别数组。 */
+ NM_WELL_CATEGORY getWellCategory() const;
+
/** @brief 返回井对象的稳定实例标识,与可编辑井编码相互独立。 */
QString getWellInstanceId() const;
- /** @brief v3 加载或井型替换时恢复已有实例标识。 */
+ /** @brief 项目加载或井型替换时恢复已有实例标识。 */
bool restoreWellInstanceId(const QString& sWellInstanceId);
/** @brief 为新建井生成不带花括号的 UUID。 */
static QString createWellInstanceId();
@@ -133,6 +138,12 @@ public:
QVector getFlowPoints() const;
// 设置流量曲线点数据
void setFlowPoints(const QVector& points);
+ /** @brief 返回去除可选 (0,0) 占位点后的真实流动段。 */
+ QVector getFlowSegmentPoints() const;
+ /** @brief 返回真实流动段数量,不把首个 (0,0) 占位点计入。 */
+ int getFlowSegmentCount() const;
+ /** @brief 校验当前流动段索引是否位于一基的真实流动段范围内。 */
+ bool hasValidFlowSectionIndex() const;
int getIndexF() const;
void setIndexF(const int newIndex);
@@ -237,6 +248,7 @@ protected:
QString m_sWellInstanceId;
QString m_wellName; // 井名
QString m_wellCode; // 井编码(项目级唯一标识,来自ZxDataWell::getCode())
+ NM_WELL_CATEGORY m_eWellCategory; // 工程井井别:油井、气井或水井
// Location---位置
nmDataAttribute m_inputWellHead; //井口坐标输入
@@ -265,7 +277,7 @@ protected:
nmDataAttribute m_bottomholeMD; // 井底测量深度
QVector m_vecPtsP; // 压力曲线点数据
- QVector m_vecPtsF; // 流量曲线点数据
+ QVector m_vecPtsF; // 流量曲线点数据,允许首项为 (0,0) 占位点
int m_nIndexF; // 当前流量段的索引
diff --git a/Include/nmNum/nmData/nmDefines.h b/Include/nmNum/nmData/nmDefines.h
index 7aa4e66e..53230289 100644
--- a/Include/nmNum/nmData/nmDefines.h
+++ b/Include/nmNum/nmData/nmDefines.h
@@ -56,6 +56,22 @@ enum NM_PHASE_TYPE {
PHASE_UNKNOWN, // 其它
PHASE_Oil_Water // 油水
};
+
+// 工程井井别。数值模块只保存工程井井别的只读副本,流量相别由该值唯一派生。
+// Oil/Gas/Water 的数值与 ZxDataWell::getWellTypeIndex() 及流量大字段索引保持一致。
+enum NM_WELL_CATEGORY {
+ NM_WellCategory_Unknown = -1,
+ NM_WellCategory_Oil = 0,
+ NM_WellCategory_Gas = 1,
+ NM_WellCategory_Water = 2
+};
+
+inline bool nmIsValidWellCategory(NM_WELL_CATEGORY eWellCategory)
+{
+ return eWellCategory == NM_WellCategory_Oil ||
+ eWellCategory == NM_WellCategory_Gas ||
+ eWellCategory == NM_WellCategory_Water;
+}
// 结构树节点类型
enum NM_TREE_TYPE {
TREE_OilReservoir, // 油藏节点
diff --git a/Include/nmNum/nmData/nmPebiResultSnapshot.h b/Include/nmNum/nmData/nmPebiResultSnapshot.h
index b7080923..05f61433 100644
--- a/Include/nmNum/nmData/nmPebiResultSnapshot.h
+++ b/Include/nmNum/nmData/nmPebiResultSnapshot.h
@@ -35,6 +35,8 @@ struct NM_DATA_EXPORT nmPebiResultSolverSlot
QString m_sWellCode;
/** @brief 求解时冻结的井模型类型。 */
NM_WELL_MODEL m_eWellType;
+ /** @brief 求解时冻结的油井、气井或水井井别。 */
+ NM_WELL_CATEGORY m_eWellCategory;
};
/** @brief 一口结果井的历史曲线和计算曲线。 */
@@ -70,6 +72,8 @@ struct NM_DATA_EXPORT nmPebiResultWellSnapshot
QString m_sWellName;
/** @brief 求解时冻结的井模型类型。 */
NM_WELL_MODEL m_eWellType;
+ /** @brief 求解时冻结的油井、气井或水井井别。 */
+ NM_WELL_CATEGORY m_eWellCategory;
/** @brief 求解时冻结的井角色。 */
NM_CASE_WELL_MODE m_eWellMode;
/** @brief 求解时冻结的井平面位置。 */
diff --git a/Include/nmNum/nmData/nmPebiResultSnapshotBuilder.h b/Include/nmNum/nmData/nmPebiResultSnapshotBuilder.h
index 6ea13c61..67317f27 100644
--- a/Include/nmNum/nmData/nmPebiResultSnapshotBuilder.h
+++ b/Include/nmNum/nmData/nmPebiResultSnapshotBuilder.h
@@ -21,7 +21,7 @@ public:
/** @brief 销毁尚未被 takeCandidate() 转交的候选。 */
~nmPebiResultSnapshotBuilder();
- /** @brief v3 加载时恢复已保存的快照 UUID;求解候选可使用自动生成值。 */
+ /** @brief v4 加载时恢复已保存的快照 UUID;求解候选可使用自动生成值。 */
bool setSnapshotId(const QString& sSnapshotId);
/** @brief 设置候选创建时对应的网格输入版本和求解输入版本。 */
bool setInputRevisions(quint64 nGridInputRevision,
diff --git a/Include/nmNum/nmData/nmPebiResultSnapshotSerializer.h b/Include/nmNum/nmData/nmPebiResultSnapshotSerializer.h
index 77f687b8..bf2018fd 100644
--- a/Include/nmNum/nmData/nmPebiResultSnapshotSerializer.h
+++ b/Include/nmNum/nmData/nmPebiResultSnapshotSerializer.h
@@ -10,7 +10,7 @@
class nmPebiResultSnapshot;
/**
- * @brief 负责 v3 快照三文件载荷及 Snapshot.json 的流式保存和加载。
+ * @brief 负责 v4 快照三文件载荷及 Snapshot.json 的流式保存和加载。
*
* 序列化器是快照的受限友元,只在保存时读取内部 VTK 对象;加载仍通过
* nmPebiResultSnapshotBuilder 构造和封闭候选。
diff --git a/Include/nmNum/nmSubWxs/nmWxIncludeOtherWells.h b/Include/nmNum/nmSubWxs/nmWxIncludeOtherWells.h
index e3bfdb0a..0efc62fc 100644
--- a/Include/nmNum/nmSubWxs/nmWxIncludeOtherWells.h
+++ b/Include/nmNum/nmSubWxs/nmWxIncludeOtherWells.h
@@ -23,6 +23,7 @@ struct WellDataRow {
bool m_bIsIncluded; ///< 是否被用户勾选为其他主动井。
bool m_bIsPrimary; ///< 主井防御标记;主井正常情况下不会进入本表。
bool m_bCanCalculate; ///< 当前井型和几何参数是否可进入 PEBI 模型。
+ NM_WELL_CATEGORY m_eWellCategory; ///< 工程井井别,决定哪一列显示流量。
NM_CASE_WELL_MODE m_eMode; ///< 本列表固定保存有产量主动井角色。
QString m_sOilProd; ///< 油产量列的摘要文本。
QString m_sGasProd; ///< 气产量列的摘要文本。
@@ -34,6 +35,7 @@ struct WellDataRow {
: m_bIsIncluded(false),
m_bIsPrimary(false),
m_bCanCalculate(false),
+ m_eWellCategory(NM_WellCategory_Unknown),
m_eMode(NM_CaseWell_RateControlled)
{
}
@@ -89,7 +91,7 @@ private:
QHBoxLayout* m_pButtonLayout; ///< 底部操作按钮布局。
QVBoxLayout* m_pMainLayout; ///< 对话框主布局。
- QVector m_wellData; ///< 与表格行一一对应的候选井数据。
+ QVector m_vecWellData; ///< 与表格行一一对应的候选井数据。
};
#endif // NMWXINCLUDEOTHERWELLS_H
diff --git a/Include/nmNum/nmSubWxs/nmWxWellEditor.h b/Include/nmNum/nmSubWxs/nmWxWellEditor.h
index fe0a7afb..af1262a8 100644
--- a/Include/nmNum/nmSubWxs/nmWxWellEditor.h
+++ b/Include/nmNum/nmSubWxs/nmWxWellEditor.h
@@ -173,6 +173,7 @@ private:
nmWellEditorController* m_pWellEditorController; ///< 轨迹交互控制器。
QLineEdit* m_pWellCodeLineEdit; ///< 只读项目级井号。
QLineEdit* m_pWellNameLineEdit; ///< 井名输入框。
+ QLineEdit* m_pWellCategoryLineEdit; ///< 只读工程井井别。
QComboBox* m_pWellTypeComboBox; ///< 井型选择器。
QCheckBox* m_pTimeDependentCheckBox; ///< 时间变表皮开关。
QToolButton* m_pTimeDependentButton; ///< 时间变表皮详情入口。
diff --git a/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp b/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp
index f70eae99..0bd52880 100644
--- a/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp
+++ b/Src/nmNum/nmCalculation/nmCalculationDllPebiSolverTask.cpp
@@ -195,6 +195,8 @@ bool captureResultWellMetadata(
{
if(pWellData == NULL || !oWellInput.m_bRealWell ||
pWellData->getWellCode() != oWellInput.m_sWellCode ||
+ pWellData->getWellCategory() != oWellInput.m_eWellCategory ||
+ !nmIsValidWellCategory(oWellInput.m_eWellCategory) ||
pWellData->getWellInstanceId().isEmpty() ||
QUuid(pWellData->getWellInstanceId()).isNull()) {
return false;
@@ -213,7 +215,7 @@ bool captureResultWellMetadata(
// 无产量井仍作为观察井保留在求解槽位和快照元数据中,但不进入结果下拉框。
oWellInput.m_bDisplayResultWell = bDisplayResultWell &&
oWellInput.m_bRateControlled &&
- oWellInput.m_vecFlowPoints.size() >= 2;
+ !oWellInput.m_vecFlowPoints.isEmpty();
nmDataVerticalFracturedWell* pVerticalFracturedWell =
dynamic_cast(pWellData);
@@ -1512,7 +1514,10 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
const bool bRateControlled =
oWellInput.m_bRateControlled &&
- oWellInput.m_vecFlowPoints.size() >= 2;
+ !oWellInput.m_vecFlowPoints.isEmpty() &&
+ oWellInput.m_nFlowSectionIndex >= 1 &&
+ oWellInput.m_nFlowSectionIndex <=
+ oWellInput.m_vecFlowPoints.size();
vvecLogLog.clear();
vvecSemiLog.clear();
@@ -1549,14 +1554,14 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
// 准备流量段数据
const QVector& vecTimeQ =
oWellInput.m_vecFlowPoints;
- // 防御空流量数据,避免 -1 转成 std::vector 的巨大无符号长度。
- int nTimeNumQ = qMax(0, vecTimeQ.size() - 1); // 移除第一个 0 点
+ // 输入快照已经移除可选 (0,0) 占位点,此处逐段传给曲线 DLL。
+ const int nTimeNumQ = vecTimeQ.size();
std::vector timeQ(nTimeNumQ);
std::vector q(nTimeNumQ);
for(int i = 0; i < nTimeNumQ; ++i) {
- timeQ[i] = vecTimeQ[i + 1].x();
- q[i] = vecTimeQ[i + 1].y();
+ timeQ[i] = vecTimeQ[i].x();
+ q[i] = vecTimeQ[i].y();
}
// 调用外部 DLL 计算双对数曲线
@@ -1913,6 +1918,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
oSlot.m_eEntryKind = oWellRef.m_eEntryKind;
oSlot.m_sWellCode = oWellRef.m_sWellCode;
oSlot.m_eWellType = oWellRef.m_eWellType;
+ oSlot.m_eWellCategory = oWellInput.m_eWellCategory;
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
oSlot.m_sWellInstanceId = oWellInput.m_sWellInstanceId;
}
@@ -1941,6 +1947,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
oWell.m_sWellCode = oWellInput.m_sWellCode;
oWell.m_sWellName = oWellInput.m_sWellName;
oWell.m_eWellType = oWellRef.m_eWellType;
+ oWell.m_eWellCategory = oWellInput.m_eWellCategory;
oWell.m_eWellMode = oWellInput.m_bRateControlled
? NM_CaseWell_RateControlled
: NM_CaseWell_Observation;
diff --git a/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp b/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp
index 6552c1da..added2b4 100644
--- a/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp
+++ b/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp
@@ -1103,7 +1103,8 @@ bool nmCalculationPebiGrid::appendManualWellInputSnapshot(
oWellInput.m_bRealWell = true;
oWellInput.m_sWellCode = pWellData->getWellCode();
oWellInput.m_sWellName = pWellData->getWellName();
- oWellInput.m_vecFlowPoints = pWellData->getFlowPoints();
+ oWellInput.m_eWellCategory = pWellData->getWellCategory();
+ oWellInput.m_vecFlowPoints = pWellData->getFlowSegmentPoints();
oWellInput.m_nFlowSectionIndex = pWellData->getIndexF();
oWellInput.m_oLocation = QPointF(
pWellData->getX().getValue().toDouble(),
@@ -1115,8 +1116,10 @@ bool nmCalculationPebiGrid::appendManualWellInputSnapshot(
: 0.0;
oWellInput.m_bRateControlled =
nWellMode == static_cast(NM_CaseWell_RateControlled);
- if(oWellInput.m_bRateControlled &&
- oWellInput.m_vecFlowPoints.size() < 2) {
+ if(!nmIsValidWellCategory(oWellInput.m_eWellCategory) ||
+ (oWellInput.m_bRateControlled &&
+ (oWellInput.m_vecFlowPoints.isEmpty() ||
+ !pWellData->hasValidFlowSectionIndex()))) {
return false;
}
@@ -1378,7 +1381,8 @@ bool nmCalculationPebiGrid::captureInputSnapshot(
oWellInput.m_bRealWell = true;
oWellInput.m_sWellName = pWellData->getWellName();
- oWellInput.m_vecFlowPoints = pWellData->getFlowPoints();
+ oWellInput.m_eWellCategory = pWellData->getWellCategory();
+ oWellInput.m_vecFlowPoints = pWellData->getFlowSegmentPoints();
oWellInput.m_nFlowSectionIndex = pWellData->getIndexF();
oWellInput.m_oLocation = QPointF(
pWellData->getX().getValue().toDouble(),
@@ -1392,8 +1396,14 @@ bool nmCalculationPebiGrid::captureInputSnapshot(
oWellInput.m_bRateControlled =
mapWellModes.value(oWellRef.m_sWellCode) ==
static_cast(NM_CaseWell_RateControlled);
+ if(!nmIsValidWellCategory(oWellInput.m_eWellCategory)) {
+ qWarning() << "Solver well has an invalid category:"
+ << oWellRef.m_sWellCode;
+ return false;
+ }
if(oWellInput.m_bRateControlled &&
- oWellInput.m_vecFlowPoints.size() < 2) {
+ (oWellInput.m_vecFlowPoints.isEmpty() ||
+ !pWellData->hasValidFlowSectionIndex())) {
qWarning() << "Rate-controlled well has no valid rate schedule:"
<< oWellRef.m_sWellCode;
return false;
@@ -1519,7 +1529,7 @@ bool nmCalculationPebiGrid::buildPebiScene(
oScene.wellName.push_back(vecWellInputs[nIndex].m_sWellName);
}
- // 第二步:按求解器模型填充每个槽位的产量制度。
+ // 第二步:按每口井冻结的井别填充产量制度;求解模型只负责相容性校验。
const NM_SOLVER_MODEL_TYPE eSolverModelType =
static_cast(oScene.solverType);
oScene.Rate.t.resize(vecSolverWellOrder.size());
@@ -1545,26 +1555,31 @@ bool nmCalculationPebiGrid::buildPebiScene(
continue;
}
- QVector vecTimeQ = oWellInput.m_vecFlowPoints;
- if(!vecTimeQ.isEmpty() &&
- vecTimeQ[0].x() == 0.0 &&
- vecTimeQ[0].y() == 0.0) {
- vecTimeQ.remove(0);
+ if(!nmIsValidWellCategory(oWellInput.m_eWellCategory) ||
+ !nmSolverModelSupportsWellCategory(
+ eSolverModelType, oWellInput.m_eWellCategory) ||
+ oWellInput.m_vecFlowPoints.isEmpty() ||
+ oWellInput.m_nFlowSectionIndex < 1 ||
+ oWellInput.m_nFlowSectionIndex >
+ oWellInput.m_vecFlowPoints.size()) {
+ qWarning() << "Rate-controlled well input is incompatible with solver model:"
+ << oWellInput.m_sWellCode;
+ return false;
}
std::vector vecTime;
std::vector vecRate;
- vecTime.reserve(vecTimeQ.size());
- vecRate.reserve(vecTimeQ.size());
+ vecTime.reserve(oWellInput.m_vecFlowPoints.size());
+ vecRate.reserve(oWellInput.m_vecFlowPoints.size());
for(int nPointIndex = 0;
- nPointIndex < vecTimeQ.size();
+ nPointIndex < oWellInput.m_vecFlowPoints.size();
++nPointIndex) {
if((nPointIndex % 256) == 0 &&
isCancellationRequested(pCancelRequested)) {
return false;
}
- vecTime.push_back(vecTimeQ[nPointIndex].x());
- vecRate.push_back(vecTimeQ[nPointIndex].y());
+ vecTime.push_back(oWellInput.m_vecFlowPoints[nPointIndex].x());
+ vecRate.push_back(oWellInput.m_vecFlowPoints[nPointIndex].y());
}
oScene.Rate.t[nWellIndex] = vecTime;
@@ -1572,32 +1587,15 @@ bool nmCalculationPebiGrid::buildPebiScene(
oScene.Rate.qg[nWellIndex].assign(vecRate.size(), 0.0);
oScene.Rate.qw[nWellIndex].assign(vecRate.size(), 0.0);
- if(eSolverModelType == SMT_Gas_VariablePvt ||
- eSolverModelType == SMT_Gas_PseudoPressure) {
+ if(oWellInput.m_eWellCategory == NM_WellCategory_Gas) {
oScene.Rate.qg[nWellIndex] = vecRate;
- } else if(eSolverModelType == SMT_Water_ConstPvt ||
- eSolverModelType == SMT_Water_VariablePvt) {
+ } else if(oWellInput.m_eWellCategory == NM_WellCategory_Water) {
oScene.Rate.qw[nWellIndex] = vecRate;
} else {
oScene.Rate.qo[nWellIndex] = vecRate;
}
}
- // 油水两相当前按定产油量处理,气、水产量数组保持等长零值。
- if(eSolverModelType == SMT_Oil_Water_TwoPhase) {
- for(size_t nWellIndex = 0;
- nWellIndex < oScene.Rate.t.size();
- ++nWellIndex) {
- if(isCancellationRequested(pCancelRequested)) {
- return false;
- }
- oScene.Rate.qg[nWellIndex].assign(
- oScene.Rate.t[nWellIndex].size(), 0.0);
- oScene.Rate.qw[nWellIndex].assign(
- oScene.Rate.t[nWellIndex].size(), 0.0);
- }
- }
-
// 第三步:复制井筒储集、表皮和流量段索引。
oScene.CS.C.resize(vecSolverWellOrder.size());
oScene.CS.S.resize(vecSolverWellOrder.size());
@@ -1622,8 +1620,10 @@ bool nmCalculationPebiGrid::buildPebiScene(
oScene.CS.C[nWellIndex] = oWellInput.m_dWellboreStorage;
oScene.CS.S[nWellIndex] = oWellInput.m_dSkin;
+ // 观察井没有流量段时该字段不参与物理计算,仍写入合法的一基占位值。
oScene.wellFlowSectionIndex[nWellIndex] =
- oWellInput.m_nFlowSectionIndex;
+ oWellInput.m_nFlowSectionIndex >= 1
+ ? oWellInput.m_nFlowSectionIndex : 1;
}
oSnapshot.m_bValid = !isCancellationRequested(pCancelRequested);
diff --git a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp
index 7ffbb79e..079a4648 100644
--- a/Src/nmNum/nmData/nmDataAnalyzeManager.cpp
+++ b/Src/nmNum/nmData/nmDataAnalyzeManager.cpp
@@ -39,6 +39,7 @@
#include "nmDataPvtParaForPebi.h"
#include "ZxDataWell.h"
+#include "ZxDataGaugeBase.h"
#include "ZxDataGaugeP.h"
#include "ZxDataGaugeF.h"
#include "zxSysUtils.h"
@@ -124,7 +125,8 @@ static bool isWellParaOf(const WellParaDesc& desc, NM_WELL_MODEL eWellType)
// 当前 PEBI 网格只支持这三类数值井。Map 中其他类型的图元不能进入求解井集合。
static bool isSupportedNumericalWell(const nmDataWellBase* pWellData)
{
- if(pWellData == nullptr || pWellData->getWellCode().isEmpty()) {
+ if(pWellData == nullptr || pWellData->getWellCode().isEmpty() ||
+ !nmIsValidWellCategory(pWellData->getWellCategory())) {
return false;
}
@@ -134,6 +136,85 @@ static bool isSupportedNumericalWell(const nmDataWellBase* pWellData)
eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well;
}
+// 将工程井井别转换为数值模块的稳定枚举。枚举值同时就是流量大字段索引。
+static NM_WELL_CATEGORY frameworkWellCategory(const ZxDataWell* pWellData)
+{
+ if(pWellData == nullptr) {
+ return NM_WellCategory_Unknown;
+ }
+
+ const NM_WELL_CATEGORY eWellCategory =
+ static_cast(pWellData->getWellTypeIndex());
+ return nmIsValidWellCategory(eWellCategory)
+ ? eWellCategory : NM_WellCategory_Unknown;
+}
+
+// 从一条测量数据中复制二维点。流量调用方必须显式传入油、气、水井别索引。
+static bool readGaugePoints(ZxDataGaugeBase* pGaugeData,
+ int nObjectIndex,
+ QVector& vecPoints)
+{
+ vecPoints.clear();
+ if(pGaugeData == nullptr) {
+ return false;
+ }
+
+ VecDouble vecX;
+ VecDouble vecY;
+ if(!pGaugeData->getDataVecXY(vecX, vecY, nObjectIndex)) {
+ return false;
+ }
+
+ const int nPointCount = qMin(
+ static_cast(vecX.size()),
+ static_cast(vecY.size()));
+ vecPoints.reserve(nPointCount);
+ for(int nIndex = 0; nIndex < nPointCount; ++nIndex) {
+ vecPoints.append(QPointF(vecX[nIndex], vecY[nIndex]));
+ }
+ return true;
+}
+
+// 读取工程井的第一条压力和流量数据;井别是流量相别的唯一事实来源。
+static bool readFrameworkWellCurves(
+ ZxDataWell* pWellData,
+ QVector& vecPressurePoints,
+ QVector& vecFlowPoints,
+ NM_WELL_CATEGORY& eWellCategory)
+{
+ vecPressurePoints.clear();
+ vecFlowPoints.clear();
+ eWellCategory = frameworkWellCategory(pWellData);
+ if(pWellData == nullptr || !nmIsValidWellCategory(eWellCategory)) {
+ return false;
+ }
+
+ ZxDataGaugeP* pGaugeP = nullptr;
+ const ZxDataObjectList listGaugeP =
+ pWellData->getChildren(iDataModelType::sTypeDataGaugeP);
+ for(int nIndex = 0; nIndex < listGaugeP.size(); ++nIndex) {
+ pGaugeP = dynamic_cast(listGaugeP[nIndex]);
+ if(pGaugeP != nullptr) {
+ break;
+ }
+ }
+
+ ZxDataGaugeF* pGaugeF = nullptr;
+ const ZxDataObjectList listGaugeF =
+ pWellData->getChildren(iDataModelType::sTypeDataGaugeF);
+ for(int nIndex = 0; nIndex < listGaugeF.size(); ++nIndex) {
+ pGaugeF = dynamic_cast(listGaugeF[nIndex]);
+ if(pGaugeF != nullptr) {
+ break;
+ }
+ }
+
+ // 压力数据仍读取二维主对象;流量必须使用井别对应的 0/1/2 大字段索引。
+ readGaugePoints(pGaugeP, 0, vecPressurePoints);
+ readGaugePoints(pGaugeF, static_cast(eWellCategory), vecFlowPoints);
+ return true;
+}
+
static bool isWellParaAttrName(const QString& name)
{
for (int i = 0; i < WELL_PARA_DESC_COUNT; ++i) {
@@ -1363,61 +1444,14 @@ void nmDataAnalyzeManager::initCurWellData()
// 判断是哪一种井类型,初始化对应的参数
QString wellClass = pWellData->getWellClassEn();
- // 获取当前井的压力、流量数据
- ZxDataObjectList m_listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP);
- ZxDataObjectList m_listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF);
-
- ZxDataGaugeP* pGaugeP = nullptr;
- ZxDataGaugeF* pGaugeF = nullptr;
-
- // 遍历压力数据列表
- for(int i = 0; i < m_listGaugeP.size(); ++i) {
- if(pGaugeP = dynamic_cast(m_listGaugeP[i])) { // 拿到第一条压力数据
- break;
- }
- }
-
- // 遍历流量数据列表
- for(int i = 0; i < m_listGaugeF.size(); ++i) {
- if(pGaugeF = dynamic_cast(m_listGaugeF[i])) { // 拿到第一条流量数据
- break;
- }
- }
-
- // 获取的压力、流量数据
- QVector vecPtsP, vecPtsF;
- // 临时存储x,y坐标
- VecDouble vecX, vecY;
-
- if(pGaugeP != nullptr) {
- // 获取压力数据
- if(pGaugeP->getDataVecXY(vecX, vecY)) {
- int pointCount = vecX.size();
- if(pointCount > vecY.size()) {
- pointCount = vecY.size();
- }
- for(int i = 0; i < pointCount; ++i) {
- QPointF pt(vecX[i], vecY[i]);
- vecPtsP.append(pt);
- }
- }
- }
-
- if(pGaugeF != nullptr) {
- vecX.clear();
- vecY.clear();
-
- // 获取流量数据
- if(pGaugeF->getDataVecXY(vecX, vecY)) {
- int pointCount = vecX.size();
- if(pointCount > vecY.size()) {
- pointCount = vecY.size();
- }
- for(int i = 0; i < pointCount; ++i) {
- QPointF pt(vecX[i], vecY[i]);
- vecPtsF.append(pt);
- }
- }
+ QVector vecPtsP;
+ QVector vecPtsF;
+ NM_WELL_CATEGORY eWellCategory = NM_WellCategory_Unknown;
+ if(!readFrameworkWellCurves(pWellData, vecPtsP, vecPtsF,
+ eWellCategory)) {
+ qWarning() << "Cannot import numerical well with invalid category:"
+ << pWellData->getCode();
+ return;
}
if(ZxBaseUtil::isSameStr(wellClass, "VerticalWell")) {
@@ -1430,6 +1464,7 @@ void nmDataAnalyzeManager::initCurWellData()
pVerticalWell->setWellName(pWellData->getName());
pVerticalWell->setWellCode(pWellData->getCode());
+ pVerticalWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = pVerticalWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pVerticalWell->setX(tempAttr);
@@ -1468,6 +1503,7 @@ void nmDataAnalyzeManager::initCurWellData()
pVFracturedWell->setWellName(pWellData->getName());
pVFracturedWell->setWellCode(pWellData->getCode());
+ pVFracturedWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = pVFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pVFracturedWell->setX(tempAttr);
@@ -1509,6 +1545,7 @@ void nmDataAnalyzeManager::initCurWellData()
pHFracturedWell->setWellName(pWellData->getName());
pHFracturedWell->setWellCode(pWellData->getCode());
+ pHFracturedWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = pHFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pHFracturedWell->setX(tempAttr);
@@ -1549,7 +1586,7 @@ void nmDataAnalyzeManager::initCurWellData()
const QString sPrimaryWellCode = m_pCurDataWell->getWellCode();
m_oNumericalAnalysisCase.setPrimaryWellCode(sPrimaryWellCode);
m_oNumericalAnalysisCase.setPrimaryWellMode(
- m_pCurDataWell->getFlowPoints().size() >= 2
+ m_pCurDataWell->getFlowSegmentCount() > 0
? NM_CaseWell_RateControlled
: NM_CaseWell_Observation);
}
@@ -1593,23 +1630,20 @@ void nmDataAnalyzeManager::calculationLogData(
vvecHistoryData[1].append(qpoint.y()); // y
}
- // Prepare flow-rate segment data.
- QVector vecTimeQ = pWellData->getFlowPoints();
- if(vecPressure.isEmpty() || vecTimeQ.size() < 2) {
+ // 流动段索引采用一基;这里只使用去除可选 (0,0) 占位点后的真实段。
+ const QVector vecTimeQ = pWellData->getFlowSegmentPoints();
+ if(vecPressure.isEmpty() || vecTimeQ.isEmpty() ||
+ !pWellData->hasValidFlowSectionIndex()) {
return;
}
- int nTimeNumQ = vecTimeQ.size() - 1;
- if (nTimeNumQ <= 0) {
- // 没有流量段数据,无法计算双对数/半对数曲线
- return;
- }
+ const int nTimeNumQ = vecTimeQ.size();
std::vector timeQ(nTimeNumQ);
std::vector q(nTimeNumQ);
for(int i = 0; i < nTimeNumQ; ++i) {
- timeQ[i] = vecTimeQ[i + 1].x();
- q[i] = vecTimeQ[i + 1].y();
+ timeQ[i] = vecTimeQ[i].x();
+ q[i] = vecTimeQ[i].y();
}
// 调用 DLL 计算双对数曲线
@@ -1666,71 +1700,18 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
return pExistingWell;
}
- // 判断是哪一种井类型,初始化对应的参数
- QString sWellClass = pWellData->getWellClassEn();
-
- // 获取当前井的压力、流量数据
- ZxDataObjectList m_listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP);
- ZxDataObjectList m_listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF);
-
- ZxDataGaugeP* pGaugeP = nullptr;
- ZxDataGaugeF* pGaugeF = nullptr;
-
- // 遍历压力数据列表
- for(int i = 0; i < m_listGaugeP.size(); ++i) {
- if(pGaugeP = dynamic_cast(m_listGaugeP[i])) { // 拿到第一条压力数据
- break;
- }
- }
-
- // 遍历流量数据列表
- for(int i = 0; i < m_listGaugeF.size(); ++i) {
- if(pGaugeF = dynamic_cast(m_listGaugeF[i])) { // 拿到第一条流量数据
- break;
- }
- }
-
- // 获取的压力、流量数据
- QVector vecPtsP, vecPtsF;
- // 临时存储x,y坐标
- VecDouble vecX, vecY;
-
- if(pGaugeP != nullptr) {
- // 获取压力数据
- if(pGaugeP->getDataVecXY(vecX, vecY)) {
- int pointCount = vecX.size();
- if(pointCount > vecY.size()) {
- pointCount = vecY.size();
- }
- for(int i = 0; i < pointCount; ++i) {
- QPointF pt(vecX[i], vecY[i]);
- vecPtsP.append(pt);
- }
- }
- }
-
- if(pGaugeF != nullptr) {
- vecX.clear();
- vecY.clear();
-
- // 获取流量数据
- if(pGaugeF->getDataVecXY(vecX, vecY)) {
- int pointCount = vecX.size();
- if(pointCount > vecY.size()) {
- pointCount = vecY.size();
- }
- for(int i = 0; i < pointCount; ++i) {
- QPointF pt(vecX[i], vecY[i]);
- vecPtsF.append(pt);
- }
- }
+ // 井别决定流量大字段;压力仍读取第一条压力数据的主对象。
+ const QString sWellClass = pWellData->getWellClassEn();
+ QVector vecPtsP;
+ QVector vecPtsF;
+ NM_WELL_CATEGORY eWellCategory = NM_WellCategory_Unknown;
+ if(!readFrameworkWellCurves(pWellData, vecPtsP, vecPtsF,
+ eWellCategory)) {
+ qWarning() << "Cannot append numerical well with invalid category:"
+ << pWellData->getCode();
+ return nullptr;
}
- int nIndexF; //当前井的流动段索引
-
- // 更新当前井的流动段的索引,默认为最后一段
- nIndexF = vecPtsF.count() - 1;
-
nmDataWellBase* pAddedWell = nullptr;
if(ZxBaseUtil::isSameStr(sWellClass, "VerticalWell")) {
// 初始化直井默认参数
@@ -1742,7 +1723,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
pAddedWell = pVerticalWell;
pVerticalWell->setWellName(pWellData->getName());
- pVerticalWell->setWellCode(pWellData->getCode());
+ pVerticalWell->setWellCode(pWellData->getCode());
+ pVerticalWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = pVerticalWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pVerticalWell->setX(tempAttr);
@@ -1757,8 +1739,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
pVerticalWell->setPressurePoints(vecPtsP);
pVerticalWell->setFlowPoints(vecPtsF);
- // 设置流量段索引
- pVerticalWell->setIndexF(nIndexF);
+ // 其他井默认选择最后一个真实流动段,索引保持一基。
+ pVerticalWell->setIndexF(pVerticalWell->getFlowSegmentCount());
} else if(ZxBaseUtil::isSameStr(sWellClass, "VerticalFracturedWell")) {
// 初始化垂直裂缝井默认参数
@@ -1770,7 +1752,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
pAddedWell = pVFracturedWell;
pVFracturedWell->setWellName(pWellData->getName());
- pVFracturedWell->setWellCode(pWellData->getCode());
+ pVFracturedWell->setWellCode(pWellData->getCode());
+ pVFracturedWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = pVFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pVFracturedWell->setX(tempAttr);
@@ -1787,8 +1770,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
// 更新裂缝位置信息
pVFracturedWell->setFracs();
- // 设置流量段索引
- pVFracturedWell->setIndexF(nIndexF);
+ // 其他井默认选择最后一个真实流动段,索引保持一基。
+ pVFracturedWell->setIndexF(pVFracturedWell->getFlowSegmentCount());
} else if(ZxBaseUtil::isSameStr(sWellClass, "HorizontalMultiFracturedWell")) {
// 初始化多段压裂水平井默认参数
@@ -1800,7 +1783,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
pAddedWell = pHFracturedWell;
pHFracturedWell->setWellName(pWellData->getName());
- pHFracturedWell->setWellCode(pWellData->getCode());
+ pHFracturedWell->setWellCode(pWellData->getCode());
+ pHFracturedWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = pHFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pHFracturedWell->setX(tempAttr);
@@ -1818,8 +1802,8 @@ nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
// 计算裂缝数据
pHFracturedWell->setFracs();
- // 设置流量段索引
- pHFracturedWell->setIndexF(nIndexF);
+ // 其他井默认选择最后一个真实流动段,索引保持一基。
+ pHFracturedWell->setIndexF(pHFracturedWell->getFlowSegmentCount());
}
// 第二步:不支持的项目井型不进入 Map,也不发送伪造的新增井通知。
@@ -3655,7 +3639,8 @@ void nmDataAnalyzeManager::setIncludedCalculationWells(
nmDataWellBase* pWellData = findWellByCode(oWellRef.m_sWellCode);
if(isSupportedNumericalWell(pWellData) &&
oWellRef.m_sWellCode != getPrimaryWellCode() &&
- pWellData->getFlowPoints().size() >= 2) {
+ nmIsValidWellCategory(pWellData->getWellCategory()) &&
+ pWellData->getFlowSegmentCount() > 0) {
vecValidWells.append(nmCalculationWellRef(
oWellRef.m_sWellCode,
NM_CaseWell_RateControlled));
@@ -3725,7 +3710,9 @@ nmDataAnalyzeManager::getEffectiveCalculationWells() const
continue;
}
- const bool bHasValidRate = pWellData->getFlowPoints().size() >= 2;
+ const bool bHasValidRate =
+ nmIsValidWellCategory(pWellData->getWellCategory()) &&
+ pWellData->getFlowSegmentCount() > 0;
if(!bHasValidRate) {
vecEffectiveWells.append(nmCalculationWellRef(
pWellData->getWellCode(),
@@ -3998,6 +3985,7 @@ bool nmDataAnalyzeManager::commitPebiResultSnapshot(
findWellByCode(pWell->m_sWellCode);
if(pWell == NULL || pLiveWell == NULL ||
pLiveWell->getWellInstanceId() != pWell->m_sWellInstanceId ||
+ pLiveWell->getWellCategory() != pWell->m_eWellCategory ||
setWellInstanceIds.contains(pWell->m_sWellInstanceId))
{
sError = "PEBI result well identity is stale.";
@@ -4470,7 +4458,8 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
// 新数据模型不兼容旧项目。明确校验版本,避免按井名或对象顺序猜测关联关系。
- const int nSupportedProjectVersion = 3;
+ const int nSupportedProjectVersion =
+ nmNumericalResultPersistence::projectVersion();
if(!doc.HasMember("NumericalProjectVersion") ||
!doc["NumericalProjectVersion"].IsInt() ||
doc["NumericalProjectVersion"].GetInt() != nSupportedProjectVersion) {
@@ -4483,7 +4472,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
quint64 nSavedResultInputRevision = 0;
if(!doc.HasMember("InputRevisions") ||
!doc["InputRevisions"].IsObject()) {
- qWarning() << "Numerical v3 project has no input revisions:" << filePath;
+ qWarning() << "Numerical v4 project has no input revisions:" << filePath;
return false;
}
const rapidjson::Value& oRevisionJson = doc["InputRevisions"];
@@ -4493,7 +4482,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
!oRevisionJson["BuiltGridRevision"].IsUint64() ||
!oRevisionJson.HasMember("ResultInputRevision") ||
!oRevisionJson["ResultInputRevision"].IsUint64()) {
- qWarning() << "Numerical v3 project contains invalid input revisions:"
+ qWarning() << "Numerical v4 project contains invalid input revisions:"
<< filePath;
return false;
}
@@ -4506,7 +4495,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
if(nSavedGridInputRevision == 0 || nSavedResultInputRevision == 0 ||
(nSavedBuiltGridRevision != 0 &&
nSavedBuiltGridRevision != nSavedGridInputRevision)) {
- qWarning() << "Numerical v3 project input revision values are invalid:"
+ qWarning() << "Numerical v4 project input revision values are invalid:"
<< filePath;
return false;
}
@@ -4518,7 +4507,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
QVector vecSavedIncludedWells;
QVector vecSavedSolverOrder;
- // 第一步:v3 必须完整保存当前分析方案,缺字段时不按旧项目规则猜测。
+ // 第一步:v4 必须完整保存当前分析方案,缺字段时不按旧项目规则猜测。
if(!doc.HasMember("NumericalAnalysisCase") ||
!doc["NumericalAnalysisCase"].IsObject() ||
!doc.HasMember("Wells") ||
@@ -4574,7 +4563,9 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
!oWellJson.HasMember("WellInstanceId") ||
!oWellJson["WellInstanceId"].IsString() ||
!oWellJson.HasMember("WellType") ||
- !oWellJson["WellType"].IsInt()) {
+ !oWellJson["WellType"].IsInt() ||
+ !oWellJson.HasMember("WellCategory") ||
+ !oWellJson["WellCategory"].IsInt()) {
qWarning() << "Numerical project contains an invalid well entry:" << filePath;
return false;
}
@@ -4588,11 +4579,15 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
oWellInstanceUuid.toString().remove('{').remove('}');
const NM_WELL_MODEL eWellType = static_cast(
oWellJson["WellType"].GetInt());
+ const NM_WELL_CATEGORY eWellCategory =
+ static_cast(
+ oWellJson["WellCategory"].GetInt());
const bool bSupportedWellType =
eWellType == NM_WELL_MODEL::Vertical_Well ||
eWellType == NM_WELL_MODEL::Vertical_Fractured_Well ||
eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well;
if(sWellCode.isEmpty() || !bSupportedWellType ||
+ !nmIsValidWellCategory(eWellCategory) ||
mapSavedWellTypes.contains(sWellCode) ||
oWellInstanceUuid.isNull() ||
sNormalizedWellInstanceId != sWellInstanceId ||
@@ -4713,7 +4708,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
}
}
- // 前置校验已完成,从这里开始替换当前输入。旧快照要到全部 v3 载荷
+ // 前置校验已完成,从这里开始替换当前输入。旧快照要到全部当前版本载荷
// 成功后才替换,加载失败或内存不足时仍保留上一套已发布快照。
// 清空现有数据,确保从头加载新数据
foreach(nmDataFracture* pFractureData, m_vFractureData) {
@@ -5134,7 +5129,7 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
nSavedGridInputRevision,
nSavedBuiltGridRevision,
nSavedResultInputRevision)) {
- qWarning() << "Failed to restore numerical v3 input revisions:"
+ qWarning() << "Failed to restore numerical v4 input revisions:"
<< filePath;
return false;
}
@@ -5148,7 +5143,7 @@ bool nmDataAnalyzeManager::WriteProjectData(
const nmNumericalWindowPayloadReferences* pPayloadReferences)
{
if(pPayloadReferences == NULL) {
- qWarning() << "Numerical v3 project requires explicit payload references.";
+ qWarning() << "Numerical v4 project requires explicit payload references.";
return false;
}
// 第一步:保存前校验 Map 井目录,持久化身份只允许非空且唯一的 WellCode。
@@ -5163,6 +5158,7 @@ bool nmDataAnalyzeManager::WriteProjectData(
const QString sNormalizedWellInstanceId =
oWellInstanceUuid.toString().remove('{').remove('}');
if(pWellData == nullptr || pWellData->getWellCode().isEmpty() ||
+ !nmIsValidWellCategory(pWellData->getWellCategory()) ||
setWellCodes.contains(pWellData->getWellCode()) ||
oWellInstanceUuid.isNull() ||
sNormalizedWellInstanceId != sWellInstanceId ||
@@ -5238,7 +5234,8 @@ bool nmDataAnalyzeManager::WriteProjectData(
doc.SetObject(); // 根节点是对象
rapidjson::Document::AllocatorType& allocator = doc.GetAllocator(); // 获取内存分配器
- doc.AddMember("NumericalProjectVersion", 3, allocator);
+ doc.AddMember("NumericalProjectVersion",
+ nmNumericalResultPersistence::projectVersion(), allocator);
rapidjson::Value oRevisionJson(rapidjson::kObjectType);
oRevisionJson.AddMember("GridInputRevision",
m_oNumericalAnalysisCase.getGridInputRevision(), allocator);
@@ -5879,18 +5876,40 @@ bool nmDataAnalyzeManager::loadNmResultV3InPlace(
pWell->setHistorySemiLog(oHistory.m_vecSemiLog);
}
if(!mapLoadedHistories.isEmpty()) {
- qWarning() << "Numerical v3 project contains unreferenced well histories.";
+ qWarning() << "Numerical v4 project contains unreferenced well histories.";
return false;
}
- loadWellPreAndFlow();
+ if(!loadWellPreAndFlow()) {
+ qWarning() << "Failed to reload numerical well pressure/rate data.";
+ return false;
+ }
+ // 结果快照保存的是求解时井别;加载时必须与当前工程井保持一致。
+ if(!pLoadedCandidate.isNull()) {
+ for(int nIndex = 0;
+ nIndex < pLoadedCandidate->getWellCount(); ++nIndex) {
+ const nmPebiResultWellSnapshot* pSavedWell =
+ pLoadedCandidate->getWellAt(nIndex);
+ nmDataWellBase* pLiveWell = pSavedWell == nullptr
+ ? nullptr : findWellByCode(pSavedWell->m_sWellCode);
+ if(pSavedWell == nullptr || pLiveWell == nullptr ||
+ pLiveWell->getWellInstanceId() !=
+ pSavedWell->m_sWellInstanceId ||
+ pLiveWell->getWellCategory() !=
+ pSavedWell->m_eWellCategory) {
+ qWarning() << "Loaded PEBI result well category is stale.";
+ return false;
+ }
+ }
+ }
const QVector vecIncludedWells =
getIncludedCalculationWells();
for(int nIndex = 0; nIndex < vecIncludedWells.size(); ++nIndex) {
nmDataWellBase* pWell = findWellByCode(
vecIncludedWells[nIndex].m_sWellCode);
if(!isSupportedNumericalWell(pWell) ||
- pWell->getFlowPoints().size() < 2) {
+ !nmIsValidWellCategory(pWell->getWellCategory()) ||
+ pWell->getFlowSegmentCount() <= 0) {
qWarning() << "Failed to restore included numerical well:"
<< vecIncludedWells[nIndex].m_sWellCode;
return false;
@@ -6026,98 +6045,51 @@ bool nmDataAnalyzeManager::ensureDirectoryExists(const QString & dirPath)
return true;
}
-void nmDataAnalyzeManager::loadWellPreAndFlow()
+bool nmDataAnalyzeManager::loadWellPreAndFlow()
{
- // 获取当前工区里所有的ZxDataWell井
+ // 当前项目文件只保存井别和井身份,压力/流量仍从工程井节点重新读取。
if(zxCurProject == nullptr) {
- return;
+ return false;
}
- ZxDataObjectList wellList = zxCurProject->getChildren(iDataModelType::sTypeWell);
-
- foreach(nmDataWellBase* pWell, m_vWellData) {
- if(pWell == nullptr) {
+ const ZxDataObjectList listWells =
+ zxCurProject->getChildren(iDataModelType::sTypeWell);
+ QHash mapFrameworkWells;
+ for(int nIndex = 0; nIndex < listWells.size(); ++nIndex) {
+ ZxDataWell* pFrameworkWell =
+ dynamic_cast(listWells[nIndex]);
+ if(pFrameworkWell == nullptr ||
+ pFrameworkWell->getCode().isEmpty() ||
+ mapFrameworkWells.contains(pFrameworkWell->getCode())) {
continue;
}
+ mapFrameworkWells.insert(pFrameworkWell->getCode(), pFrameworkWell);
+ }
- QString sWellCode = pWell->getWellCode();
-
- ZxDataWell* pWellData = nullptr;
-
- for(int i = 0; i < wellList.size(); i++) {
- ZxDataWell* pCandidateWell = dynamic_cast(wellList[i]);
-
- if(pCandidateWell != nullptr &&
- pCandidateWell->getCode() == sWellCode) {
- pWellData = pCandidateWell;
- break;
- }
+ foreach(nmDataWellBase* pWell, m_vWellData) {
+ if(pWell == nullptr || pWell->getWellCode().isEmpty()) {
+ return false;
}
- // 拿到了井的数据
- if(pWellData) {
- // 获取当前井的压力、流量数据
- ZxDataObjectList m_listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP);
- ZxDataObjectList m_listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF);
-
- ZxDataGaugeP* pGaugeP = nullptr;
- ZxDataGaugeF* pGaugeF = nullptr;
-
- // 遍历压力数据列表
- for(int i = 0; i < m_listGaugeP.size(); ++i) {
- if(pGaugeP = dynamic_cast(m_listGaugeP[i])) { // 拿到第一条压力数据
- break;
- }
- }
-
- // 遍历流量数据列表
- for(int i = 0; i < m_listGaugeF.size(); ++i) {
- if(pGaugeF = dynamic_cast(m_listGaugeF[i])) { // 拿到第一条流量数据
- break;
- }
- }
-
- // 获取的压力、流量数据
- QVector vecPtsP, vecPtsF;
- // 临时存储x,y坐标
- VecDouble vecX, vecY;
-
- if(pGaugeP != nullptr) {
- // 获取压力数据
- if(pGaugeP->getDataVecXY(vecX, vecY)) {
- int pointCount = vecX.size();
- if(pointCount > vecY.size()) {
- pointCount = vecY.size();
- }
- for(int i = 0; i < pointCount; ++i) {
- QPointF pt(vecX[i], vecY[i]);
- vecPtsP.append(pt);
- }
- }
- }
-
- if(pGaugeF != nullptr) {
- vecX.clear();
- vecY.clear();
-
- // 获取流量数据
- if(pGaugeF->getDataVecXY(vecX, vecY)) {
- int pointCount = vecX.size();
- if(pointCount > vecY.size()) {
- pointCount = vecY.size();
- }
- for(int i = 0; i < pointCount; ++i) {
- QPointF pt(vecX[i], vecY[i]);
- vecPtsF.append(pt);
- }
- }
- }
-
- // 设置井的压力数据、流量数据
- pWell->setPressurePoints(vecPtsP);
- pWell->setFlowPoints(vecPtsF);
+ ZxDataWell* pFrameworkWell =
+ mapFrameworkWells.value(pWell->getWellCode(), nullptr);
+ QVector vecPressurePoints;
+ QVector vecFlowPoints;
+ NM_WELL_CATEGORY eFrameworkCategory = NM_WellCategory_Unknown;
+ if(!readFrameworkWellCurves(pFrameworkWell,
+ vecPressurePoints,
+ vecFlowPoints,
+ eFrameworkCategory) ||
+ pWell->getWellCategory() != eFrameworkCategory) {
+ qWarning() << "Numerical well category does not match framework well:"
+ << pWell->getWellCode();
+ return false;
}
+
+ pWell->setPressurePoints(vecPressurePoints);
+ pWell->setFlowPoints(vecFlowPoints);
}
+ return true;
}
vtkSmartPointer nmDataAnalyzeManager::getUnstructuredGrid() const
diff --git a/Src/nmNum/nmData/nmDataWellBase.cpp b/Src/nmNum/nmData/nmDataWellBase.cpp
index eb0d77a4..9bbc63b6 100644
--- a/Src/nmNum/nmData/nmDataWellBase.cpp
+++ b/Src/nmNum/nmData/nmDataWellBase.cpp
@@ -12,7 +12,8 @@
#include
nmDataWellBase::nmDataWellBase()
- : m_nIndexF(0)
+ : m_eWellCategory(NM_WellCategory_Unknown)
+ , m_nIndexF(0)
, m_bPlotVisible(true)
, m_bTimeDependentSkin(false)
, m_eWellType(Vertical_Well)
@@ -111,6 +112,7 @@ nmDataWellBase& nmDataWellBase::operator=(const nmDataWellBase& other)
m_sWellInstanceId = other.m_sWellInstanceId;
m_wellName = other.m_wellName;
m_wellCode = other.m_wellCode;
+ m_eWellCategory = other.m_eWellCategory;
m_x = other.m_x;
m_y = other.m_y;
m_radius = other.m_radius;
@@ -190,6 +192,9 @@ rapidjson::Value nmDataWellBase::ToJsonValue(rapidjson::Document::AllocatorType&
// 序列化井类型
wellObject.AddMember("WellType", rapidjson::Value(static_cast(m_eWellType)), allocator);
+ // 井别决定流量大字段的油、气、水索引,v4 项目必须显式保存。
+ wellObject.AddMember("WellCategory",
+ rapidjson::Value(static_cast(m_eWellCategory)), allocator);
// 序列化 nmDataAttribute 类型的成员
// 调用 nmDataAttribute 自身的 ToJsonValue 方法进行递归序列化
@@ -256,7 +261,7 @@ void nmDataWellBase::FromJsonValue(const rapidjson::Value& jsonValue)
m_wellCode = QString::fromUtf8(jsonValue["WellCode"].GetString());
}
- // v3 已在 Manager 中完成格式校验,这里恢复求解时保存的稳定井身份。
+ // 项目格式已在 Manager 中完成校验,这里恢复求解时保存的稳定井身份。
if(jsonValue.HasMember("WellInstanceId") &&
jsonValue["WellInstanceId"].IsString()) {
restoreWellInstanceId(QString::fromUtf8(
@@ -268,6 +273,13 @@ void nmDataWellBase::FromJsonValue(const rapidjson::Value& jsonValue)
m_eWellType = static_cast(jsonValue["WellType"].GetInt());
}
+ // v4 不兼容旧项目,缺失或非法井别由 Manager 的严格结构校验直接拒绝。
+ if(jsonValue.HasMember("WellCategory") &&
+ jsonValue["WellCategory"].IsInt()) {
+ m_eWellCategory = static_cast(
+ jsonValue["WellCategory"].GetInt());
+ }
+
// 反序列化 nmDataAttribute 类型的成员
// 调用 nmDataAttribute 自身的 FromJsonValue 方法进行递归反序列化
if(jsonValue.HasMember("X") && jsonValue["X"].IsObject()) {
@@ -495,6 +507,17 @@ QString nmDataWellBase::getWellCode() const
return m_wellCode;
}
+void nmDataWellBase::setWellCategory(NM_WELL_CATEGORY eWellCategory)
+{
+ m_eWellCategory = nmIsValidWellCategory(eWellCategory)
+ ? eWellCategory : NM_WellCategory_Unknown;
+}
+
+NM_WELL_CATEGORY nmDataWellBase::getWellCategory() const
+{
+ return m_eWellCategory;
+}
+
QString nmDataWellBase::getWellInstanceId() const
{
return m_sWellInstanceId;
@@ -668,6 +691,33 @@ void nmDataWellBase::setFlowPoints(const QVector& points)
m_vecPtsF = points;
}
+QVector nmDataWellBase::getFlowSegmentPoints() const
+{
+ QVector vecSegments = m_vecPtsF;
+ // 工程流量数据通常以 (0,0) 作为非业务占位点;真实数据没有占位点时不得误删首段。
+ if(!vecSegments.isEmpty() &&
+ vecSegments.first().x() == 0.0 &&
+ vecSegments.first().y() == 0.0) {
+ vecSegments.remove(0);
+ }
+ return vecSegments;
+}
+
+int nmDataWellBase::getFlowSegmentCount() const
+{
+ if(m_vecPtsF.isEmpty()) {
+ return 0;
+ }
+ return m_vecPtsF.first().x() == 0.0 &&
+ m_vecPtsF.first().y() == 0.0
+ ? m_vecPtsF.size() - 1 : m_vecPtsF.size();
+}
+
+bool nmDataWellBase::hasValidFlowSectionIndex() const
+{
+ return m_nIndexF >= 1 && m_nIndexF <= getFlowSegmentCount();
+}
+
int nmDataWellBase::getIndexF() const
{
return m_nIndexF;
@@ -1433,29 +1483,18 @@ bool nmDataWellBase::exportHistoryLogLogToCsv(const QString& outDir)
bool nmDataWellBase::exportHistoryRateToCsv(const QString& outDir)
{
- // 1) 获取流量点 (t, q)
- QVector vecTimeQ = this->getFlowPoints();
+ // 1) 获取真实流量段 (t, q),不把可选 (0,0) 占位点写入文件。
+ const QVector vecTimeQ = this->getFlowSegmentPoints();
- if(vecTimeQ.size() < 2) {
+ if(vecTimeQ.isEmpty() || !hasValidFlowSectionIndex() ||
+ !nmIsValidWellCategory(m_eWellCategory)) {
return false;
}
- // 2) 移除第一个点 (0,0)
- if(!vecTimeQ.isEmpty()
- && vecTimeQ[0].x() == 0.0
- && vecTimeQ[0].y() == 0.0)
- {
- vecTimeQ.remove(0);
- }
-
- if(vecTimeQ.size() < 2) {
- return false;
- }
-
- // 3) sectionIndex(流动段索引)
+ // 2) sectionIndex(流动段一基索引)
int sectionIndex = this->getIndexF();
- // 4) 输出文件名
+ // 3) 输出文件名
QString wellName = this->getWellName();
if(wellName.isEmpty()) wellName = "UnknownWell";
@@ -1468,10 +1507,13 @@ bool nmDataWellBase::exportHistoryRateToCsv(const QString& outDir)
QTextStream out(&file);
- // 5) 写表头
- out << "t,qo,sectionIndex\n";
+ // 4) 表头与井别保持一致,便于离线排查实际进入求解器的相别。
+ const char* pRateColumn = m_eWellCategory == NM_WellCategory_Gas
+ ? "qg" : (m_eWellCategory == NM_WellCategory_Water
+ ? "qw" : "qo");
+ out << "t," << pRateColumn << ",sectionIndex\n";
- // 6) 写入每个点
+ // 5) 写入每个点
for(int i = 0; i < vecTimeQ.size(); ++i)
{
double ti = vecTimeQ[i].x();
diff --git a/Src/nmNum/nmData/nmNumericalResultPersistence.cpp b/Src/nmNum/nmData/nmNumericalResultPersistence.cpp
index 306e6650..29e9fc3d 100644
--- a/Src/nmNum/nmData/nmNumericalResultPersistence.cpp
+++ b/Src/nmNum/nmData/nmNumericalResultPersistence.cpp
@@ -204,7 +204,8 @@ nmNumericalWindowPayloadReferences::nmNumericalWindowPayloadReferences()
int nmNumericalResultPersistence::projectVersion()
{
- return 3;
+ // v4 强制保存井别,旧版本不再按默认油相解释流量字段。
+ return 4;
}
quint64 nmNumericalResultPersistence::maximumBinaryPayloadBytes()
diff --git a/Src/nmNum/nmData/nmPebiResultSnapshot.cpp b/Src/nmNum/nmData/nmPebiResultSnapshot.cpp
index af22721a..623a2dda 100644
--- a/Src/nmNum/nmData/nmPebiResultSnapshot.cpp
+++ b/Src/nmNum/nmData/nmPebiResultSnapshot.cpp
@@ -10,12 +10,14 @@
nmPebiResultSolverSlot::nmPebiResultSolverSlot()
: m_nSolverIndex(-1),
m_eEntryKind(NM_SolverEntry_Well),
- m_eWellType(Unknow_Well)
+ m_eWellType(Unknow_Well),
+ m_eWellCategory(NM_WellCategory_Unknown)
{
}
nmPebiResultWellSnapshot::nmPebiResultWellSnapshot()
: m_eWellType(Unknow_Well),
+ m_eWellCategory(NM_WellCategory_Unknown),
m_eWellMode(NM_CaseWell_Observation),
m_bHasPerforation(false),
m_bHasSkin(false),
diff --git a/Src/nmNum/nmData/nmPebiResultSnapshotBuilder.cpp b/Src/nmNum/nmData/nmPebiResultSnapshotBuilder.cpp
index d29c3df4..822b3506 100644
--- a/Src/nmNum/nmData/nmPebiResultSnapshotBuilder.cpp
+++ b/Src/nmNum/nmData/nmPebiResultSnapshotBuilder.cpp
@@ -523,6 +523,7 @@ bool nmPebiResultSnapshotBuilder::takeWell(
qSwap(oTarget.m_sWellCode, oWell.m_sWellCode);
qSwap(oTarget.m_sWellName, oWell.m_sWellName);
oTarget.m_eWellType = oWell.m_eWellType;
+ oTarget.m_eWellCategory = oWell.m_eWellCategory;
oTarget.m_eWellMode = oWell.m_eWellMode;
oTarget.m_oLocation = oWell.m_oLocation;
oTarget.m_bHasPerforation = oWell.m_bHasPerforation;
@@ -547,6 +548,7 @@ bool nmPebiResultSnapshotBuilder::takeWell(
// 清除剩余值,保证调用方不再保留候选井的可写语义。
oWell.m_eWellType = Unknow_Well;
+ oWell.m_eWellCategory = NM_WellCategory_Unknown;
oWell.m_eWellMode = NM_CaseWell_Observation;
oWell.m_oLocation = QPointF();
oWell.m_bHasPerforation = false;
@@ -720,6 +722,7 @@ bool nmPebiResultSnapshotBuilder::validate(QString* pError)
if(!isValidInstanceId(oWell.m_sWellInstanceId) ||
oWell.m_sWellCode.isEmpty() || oWell.m_sWellName.isEmpty() ||
oWell.m_eWellType == Unknow_Well ||
+ !nmIsValidWellCategory(oWell.m_eWellCategory) ||
(oWell.m_eWellMode != NM_CaseWell_RateControlled &&
oWell.m_eWellMode != NM_CaseWell_Observation) ||
!isFiniteValue(oWell.m_oLocation.x()) ||
@@ -759,7 +762,8 @@ bool nmPebiResultSnapshotBuilder::validate(QString* pError)
}
if(oSlot.m_eEntryKind == NM_SolverEntry_ManualFracture)
{
- if(!oSlot.m_sWellInstanceId.isEmpty())
+ if(!oSlot.m_sWellInstanceId.isEmpty() ||
+ oSlot.m_eWellCategory != NM_WellCategory_Unknown)
{
return fail("Manual fracture slot has a well identity.", pError);
}
@@ -770,6 +774,8 @@ bool nmPebiResultSnapshotBuilder::validate(QString* pError)
m_pCandidate->findWell(oSlot.m_sWellInstanceId);
if(pWell == NULL || pWell->m_sWellCode != oSlot.m_sWellCode ||
pWell->m_eWellType != oSlot.m_eWellType ||
+ pWell->m_eWellCategory != oSlot.m_eWellCategory ||
+ !nmIsValidWellCategory(oSlot.m_eWellCategory) ||
setSlotWellIds.contains(oSlot.m_sWellInstanceId))
{
return fail("PEBI solver slot well mapping is invalid.", pError);
diff --git a/Src/nmNum/nmData/nmPebiResultSnapshotSerializer.cpp b/Src/nmNum/nmData/nmPebiResultSnapshotSerializer.cpp
index d84b56e1..27163ed6 100644
--- a/Src/nmNum/nmData/nmPebiResultSnapshotSerializer.cpp
+++ b/Src/nmNum/nmData/nmPebiResultSnapshotSerializer.cpp
@@ -528,7 +528,7 @@ bool parseSnapshotJson(
if(!oDocument.IsObject() ||
!oDocument.HasMember("SnapshotFormatVersion") ||
!oDocument["SnapshotFormatVersion"].IsInt() ||
- oDocument["SnapshotFormatVersion"].GetInt() != 1 ||
+ oDocument["SnapshotFormatVersion"].GetInt() != 2 ||
!oDocument.HasMember("ResultCellCount") ||
!oDocument["ResultCellCount"].IsUint64() ||
!oDocument.HasMember("PressureFrameCount") ||
@@ -692,7 +692,8 @@ bool nmPebiResultSnapshotSerializer::save(
oDocument.SetObject();
rapidjson::Document::AllocatorType& oAllocator =
oDocument.GetAllocator();
- oDocument.AddMember("SnapshotFormatVersion", 1, oAllocator);
+ // v2 增加井别元数据;压力帧和井曲线二进制格式保持不变。
+ oDocument.AddMember("SnapshotFormatVersion", 2, oAllocator);
oDocument.AddMember("SnapshotId",
toJsonString(pSnapshot->m_sSnapshotId, oAllocator), oAllocator);
oDocument.AddMember("GridInputRevision",
@@ -726,6 +727,8 @@ bool nmPebiResultSnapshotSerializer::save(
oJson.AddMember("WellCode",
toJsonString(oSlot.m_sWellCode, oAllocator), oAllocator);
oJson.AddMember("WellType", static_cast(oSlot.m_eWellType), oAllocator);
+ oJson.AddMember("WellCategory", static_cast(
+ oSlot.m_eWellCategory), oAllocator);
oSlots.PushBack(oJson, oAllocator);
}
oDocument.AddMember("SolverSlots", oSlots, oAllocator);
@@ -742,6 +745,8 @@ bool nmPebiResultSnapshotSerializer::save(
oJson.AddMember("WellName", toJsonString(
oWell.m_sWellName, oAllocator), oAllocator);
oJson.AddMember("WellType", static_cast(oWell.m_eWellType), oAllocator);
+ oJson.AddMember("WellCategory", static_cast(
+ oWell.m_eWellCategory), oAllocator);
oJson.AddMember("WellMode", static_cast(oWell.m_eWellMode), oAllocator);
oJson.AddMember("X", oWell.m_oLocation.x(), oAllocator);
oJson.AddMember("Y", oWell.m_oLocation.y(), oAllocator);
@@ -979,9 +984,11 @@ bool nmPebiResultSnapshotSerializer::load(
nmPebiResultSolverSlot oSlot;
int nEntryKind = 0;
int nWellType = 0;
+ int nWellCategory = 0;
if(!readRequiredInt(oJson, "SolverIndex", oSlot.m_nSolverIndex) ||
!readRequiredInt(oJson, "EntryKind", nEntryKind) ||
!readRequiredInt(oJson, "WellType", nWellType) ||
+ !readRequiredInt(oJson, "WellCategory", nWellCategory) ||
!oJson.HasMember("WellInstanceId") ||
!oJson["WellInstanceId"].IsString() ||
!oJson.HasMember("WellCode") || !oJson["WellCode"].IsString())
@@ -990,6 +997,8 @@ bool nmPebiResultSnapshotSerializer::load(
}
oSlot.m_eEntryKind = static_cast(nEntryKind);
oSlot.m_eWellType = static_cast(nWellType);
+ oSlot.m_eWellCategory = static_cast(
+ nWellCategory);
oSlot.m_sWellInstanceId = QString::fromUtf8(
oJson["WellInstanceId"].GetString());
oSlot.m_sWellCode = QString::fromUtf8(oJson["WellCode"].GetString());
@@ -1011,6 +1020,7 @@ bool nmPebiResultSnapshotSerializer::load(
const rapidjson::Value& oJson = oWells[nIndex];
nmPebiResultWellSnapshot oWell;
int nWellType = 0;
+ int nWellCategory = 0;
int nWellMode = 0;
if(!oJson.IsObject() ||
!oJson.HasMember("WellInstanceId") ||
@@ -1018,6 +1028,7 @@ bool nmPebiResultSnapshotSerializer::load(
!oJson.HasMember("WellCode") || !oJson["WellCode"].IsString() ||
!oJson.HasMember("WellName") || !oJson["WellName"].IsString() ||
!readRequiredInt(oJson, "WellType", nWellType) ||
+ !readRequiredInt(oJson, "WellCategory", nWellCategory) ||
!readRequiredInt(oJson, "WellMode", nWellMode) ||
!readRequiredDouble(oJson, "X", oWell.m_oLocation.rx()) ||
!readRequiredDouble(oJson, "Y", oWell.m_oLocation.ry()) ||
@@ -1036,6 +1047,8 @@ bool nmPebiResultSnapshotSerializer::load(
oWell.m_sWellCode = QString::fromUtf8(oJson["WellCode"].GetString());
oWell.m_sWellName = QString::fromUtf8(oJson["WellName"].GetString());
oWell.m_eWellType = static_cast(nWellType);
+ oWell.m_eWellCategory = static_cast(
+ nWellCategory);
oWell.m_eWellMode = static_cast(nWellMode);
if(setJsonWellIds.contains(oWell.m_sWellInstanceId) ||
!mapCurves.contains(oWell.m_sWellInstanceId))
diff --git a/Src/nmNum/nmPlot/nmGuiPlot.cpp b/Src/nmNum/nmPlot/nmGuiPlot.cpp
index 61134298..77df26ca 100644
--- a/Src/nmNum/nmPlot/nmGuiPlot.cpp
+++ b/Src/nmNum/nmPlot/nmGuiPlot.cpp
@@ -1,4 +1,4 @@
-#include "ZxBaseUtil.h"
+#include "ZxBaseUtil.h"
#include "zxLogInstance.h"
#include
@@ -254,6 +254,14 @@ void nmGuiPlot::initDefultGeoObj()
// 判断是哪一种井类型,初始化对应的参数
QString wellClass = pWellData->getWellClassEn();
+ const NM_WELL_CATEGORY eWellCategory =
+ static_cast(
+ pWellData->getWellTypeIndex());
+ if(!nmIsValidWellCategory(eWellCategory)) {
+ this->setPlotsByDataManger(
+ nmDataAnalyzeManager::getCurrentInstance());
+ return;
+ }
// 获取当前井的压力、流量数据
ZxDataObjectList m_listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP);
@@ -285,7 +293,7 @@ void nmGuiPlot::initDefultGeoObj()
if(pGaugeP != nullptr) {
// 获取压力数据
- if(pGaugeP->getDataVecXY(vecX, vecY)) {
+ if(pGaugeP->getDataVecXY(vecX, vecY, 0)) {
for(int i = 0; i < vecX.size(); ++i) {
if(i < vecY.size()) {
QPointF pt(vecX[i], vecY[i]);
@@ -300,7 +308,9 @@ void nmGuiPlot::initDefultGeoObj()
vecY.clear();
// 获取流量数据
- if(pGaugeF->getDataVecXY(vecX, vecY)) {
+ // 井别是流量大字段索引,单相井也不能固定读取默认的油相字段。
+ if(pGaugeF->getDataVecXY(
+ vecX, vecY, static_cast(eWellCategory))) {
for(int i = 0; i < vecX.size(); ++i) {
if(i < vecY.size()) {
QPointF pt(vecX[i], vecY[i]);
@@ -327,6 +337,7 @@ void nmGuiPlot::initDefultGeoObj()
m_VerticalWell->setWellName(pWellData->getName());
m_VerticalWell->setWellCode(pWellData->getCode());
+ m_VerticalWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = m_VerticalWell->getX();
tempAttr.setValue(pWellData->getLocationX());
m_VerticalWell->setX(tempAttr);
@@ -364,6 +375,7 @@ void nmGuiPlot::initDefultGeoObj()
m_VFracturedWell->setWellName(pWellData->getName());
m_VFracturedWell->setWellCode(pWellData->getCode());
+ m_VFracturedWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = m_VFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
m_VFracturedWell->setX(tempAttr);
@@ -404,6 +416,7 @@ void nmGuiPlot::initDefultGeoObj()
m_HFracturedWell->setWellName(pWellData->getName());
m_HFracturedWell->setWellCode(pWellData->getCode());
+ m_HFracturedWell->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = m_HFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
m_HFracturedWell->setX(tempAttr);
diff --git a/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp b/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp
index 14988bd4..60354984 100644
--- a/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp
+++ b/Src/nmNum/nmPlot/nmPlotGraphicBinder.cpp
@@ -1747,6 +1747,12 @@ bool nmPlotGraphicBinder::ensureSelectedWellData(nmObjPointWell* pGraphic,
return false;
}
+ const NM_WELL_CATEGORY eWellCategory =
+ static_cast(pWellData->getWellTypeIndex());
+ if(!nmIsValidWellCategory(eWellCategory)) {
+ return false;
+ }
+
pData = createWellForGraphic(pGraphic, wellType);
if (pData == nullptr) {
return false;
@@ -1755,6 +1761,7 @@ bool nmPlotGraphicBinder::ensureSelectedWellData(nmObjPointWell* pGraphic,
// 重新勾选井时在这里补建nmDataWell,并将工程树中的基础属性同步进去。
pData->setWellName(pWellData->getName());
pData->setWellCode(pWellData->getCode());
+ pData->setWellCategory(eWellCategory);
nmDataAttribute tempAttr = pData->getX();
tempAttr.setValue(pWellData->getLocationX());
pData->setX(tempAttr);
@@ -1785,7 +1792,7 @@ bool nmPlotGraphicBinder::ensureSelectedWellData(nmObjPointWell* pGraphic,
QVector pressurePoints, flowPoints;
VecDouble vecX, vecY;
- if(pGaugeP != nullptr && pGaugeP->getDataVecXY(vecX, vecY)) {
+ if(pGaugeP != nullptr && pGaugeP->getDataVecXY(vecX, vecY, 0)) {
for(int i = 0; i < vecX.size(); ++i) {
if(i < vecY.size()) {
pressurePoints.append(QPointF(vecX[i], vecY[i]));
@@ -1795,7 +1802,10 @@ bool nmPlotGraphicBinder::ensureSelectedWellData(nmObjPointWell* pGraphic,
vecX.clear();
vecY.clear();
- if(pGaugeF != nullptr && pGaugeF->getDataVecXY(vecX, vecY)) {
+ // 流量大字段的 0/1/2 分别对应油井、气井和水井,禁止使用默认索引猜测。
+ if(pGaugeF != nullptr &&
+ pGaugeF->getDataVecXY(vecX, vecY,
+ static_cast(eWellCategory))) {
for(int i = 0; i < vecX.size(); ++i) {
if(i < vecY.size()) {
flowPoints.append(QPointF(vecX[i], vecY[i]));
@@ -1803,17 +1813,21 @@ bool nmPlotGraphicBinder::ensureSelectedWellData(nmObjPointWell* pGraphic,
}
}
- int indexF = -2;
+ int nFlowSectionIndex = -2;
if(pWellData != zxCurWell) {
- // 非当前查看井默认使用最后一段流量段。
- indexF = flowPoints.count() - 1;
+ // 非当前查看井在设置流量点后选择最后一个真实段,索引保持一基。
+ nFlowSectionIndex = flowPoints.size();
+ if(!flowPoints.isEmpty() && flowPoints.first().x() == 0.0 &&
+ flowPoints.first().y() == 0.0) {
+ --nFlowSectionIndex;
+ }
}
pData->setPressurePoints(pressurePoints);
pData->setFlowPoints(flowPoints);
- if(indexF >= -1) {
- pData->setIndexF(indexF);
+ if(nFlowSectionIndex >= -1) {
+ pData->setIndexF(nFlowSectionIndex);
}
if (wellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
diff --git a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp
index e0e3a25f..f1b04b84 100644
--- a/Src/nmNum/nmSubWnd/nmSubWndMain.cpp
+++ b/Src/nmNum/nmSubWnd/nmSubWndMain.cpp
@@ -1611,7 +1611,9 @@ void nmSubWndMain::solveAndAnalyze()
}
bHasRateControlledWell = true;
- if(pWellData->getFlowPoints().size() < 2) {
+ if(!nmIsValidWellCategory(pWellData->getWellCategory()) ||
+ pWellData->getFlowSegmentCount() <= 0 ||
+ !pWellData->hasValidFlowSectionIndex()) {
QMessageBox::warning(
this,
tr("solver error"),
@@ -1619,6 +1621,16 @@ void nmSubWndMain::solveAndAnalyze()
.arg(pWellData->getWellName()));
return;
}
+ if(!nmSolverModelSupportsWellCategory(
+ pDataManager->getSolverModelType(),
+ pWellData->getWellCategory())) {
+ QMessageBox::warning(
+ this,
+ tr("solver error"),
+ tr("Well '%1' category is not supported by the current solver model.")
+ .arg(pWellData->getWellName()));
+ return;
+ }
}
if(!bHasRateControlledWell) {
QMessageBox::warning(
diff --git a/Src/nmNum/nmSubWxs/nmWellEditorSession.cpp b/Src/nmNum/nmSubWxs/nmWellEditorSession.cpp
index 7f4158e1..ffb1d761 100644
--- a/Src/nmNum/nmSubWxs/nmWellEditorSession.cpp
+++ b/Src/nmNum/nmSubWxs/nmWellEditorSession.cpp
@@ -354,6 +354,8 @@ bool nmWellEditorSession::copySharedWellData(
}
pTarget->setWellName(pSource->getWellName());
pTarget->setWellCode(pSource->getWellCode());
+ // 井型切换只改变几何模型,工程井井别和流量相别必须保持不变。
+ pTarget->setWellCategory(pSource->getWellCategory());
pTarget->setX(pSource->getX());
pTarget->setY(pSource->getY());
pTarget->setRadius(pSource->getRadius());
diff --git a/Src/nmNum/nmSubWxs/nmWxEditWellPlot.cpp b/Src/nmNum/nmSubWxs/nmWxEditWellPlot.cpp
index d8fcbeb8..c6116831 100644
--- a/Src/nmNum/nmSubWxs/nmWxEditWellPlot.cpp
+++ b/Src/nmNum/nmSubWxs/nmWxEditWellPlot.cpp
@@ -1,4 +1,4 @@
-#include "nmWxEditWellPlot.h"
+#include "nmWxEditWellPlot.h"
#include "nmWxNumericalDesign.h"
#include "nmWxParameterProperty.h"
@@ -787,6 +787,7 @@ void nmWxEditWellPlot::onWellTypeChanged(int index)
// 历史数据
pNewWellData->setPressurePoints(pOldWellData->getPressurePoints());
pNewWellData->setFlowPoints(pOldWellData->getFlowPoints());
+ pNewWellData->setWellCategory(pOldWellData->getWellCategory());
pNewWellData->setIndexF(pOldWellData->getIndexF());
// 结果数据
diff --git a/Src/nmNum/nmSubWxs/nmWxIncludeOtherWells.cpp b/Src/nmNum/nmSubWxs/nmWxIncludeOtherWells.cpp
index ce2491bb..c121296a 100644
--- a/Src/nmNum/nmSubWxs/nmWxIncludeOtherWells.cpp
+++ b/Src/nmNum/nmSubWxs/nmWxIncludeOtherWells.cpp
@@ -93,7 +93,7 @@ void nmWxIncludeOtherWells::setupTable()
QString sPrimaryWellCode = m_pDataManager != nullptr
? m_pDataManager->getPrimaryWellCode() : QString();
- int displayRow = 0;
+ int nDisplayRow = 0;
// 第一步:候选来源是 Map,而不是整个项目井库。该对话框只管理
// 其他有产量的生产/注入井;主井和无产量观察井都不在这里显示。
for(int i = 0; i < vecMapWells.size(); i++) {
@@ -103,40 +103,49 @@ void nmWxIncludeOtherWells::setupTable()
}
const bool bIsPrimary = pWell->getWellCode() == sPrimaryWellCode;
- const bool bHasValidRate = pWell->getFlowPoints().size() >= 2;
+ const bool bHasValidRate =
+ nmIsValidWellCategory(pWell->getWellCategory()) &&
+ pWell->getFlowSegmentCount() > 0;
const bool bHasValidPressure = pWell->getPressurePoints().size() >= 2;
if(bIsPrimary || !bHasValidRate) {
continue;
}
- WellDataRow wellRow;
- wellRow.m_sWellCode = pWell->getWellCode();
- wellRow.m_sName = pWell->getWellName();
- wellRow.m_bIsPrimary = bIsPrimary;
+ WellDataRow oWellRow;
+ oWellRow.m_sWellCode = pWell->getWellCode();
+ oWellRow.m_sName = pWell->getWellName();
+ oWellRow.m_bIsPrimary = bIsPrimary;
+ oWellRow.m_eWellCategory = pWell->getWellCategory();
// 第二步:读取这口有产量干扰井上次确认的包含状态。
- wellRow.m_bCanCalculate = true;
- wellRow.m_bIsIncluded =
+ oWellRow.m_bCanCalculate = true;
+ oWellRow.m_bIsIncluded =
m_pDataManager != nullptr &&
m_pDataManager->getNumericalAnalysisCase()->isIncludedWell(
- wellRow.m_sWellCode);
-
- // 第三步:IncludedWells 中的候选固定是有产量主动井。
- wellRow.m_sPressure = bHasValidPressure ? "History" : "None";
- wellRow.m_sOilProd = "Rate";
- wellRow.m_eMode = NM_CaseWell_RateControlled;
-
- // 气相和水相暂时设为None
- wellRow.m_sGasProd = "None";
- wellRow.m_sWaterProd = "None";
+ oWellRow.m_sWellCode);
+
+ // 第三步:井别唯一决定产量列;不再把所有井都显示为油量。
+ oWellRow.m_sPressure = bHasValidPressure
+ ? tr("History") : tr("None");
+ oWellRow.m_sOilProd = tr("None");
+ oWellRow.m_sGasProd = tr("None");
+ oWellRow.m_sWaterProd = tr("None");
+ if(oWellRow.m_eWellCategory == NM_WellCategory_Oil) {
+ oWellRow.m_sOilProd = tr("Rate");
+ } else if(oWellRow.m_eWellCategory == NM_WellCategory_Gas) {
+ oWellRow.m_sGasProd = tr("Rate");
+ } else if(oWellRow.m_eWellCategory == NM_WellCategory_Water) {
+ oWellRow.m_sWaterProd = tr("Rate");
+ }
+ oWellRow.m_eMode = NM_CaseWell_RateControlled;
// 添加到数据列表和表格
- m_wellData.append(wellRow);
- addWellDataToTable(wellRow, displayRow);
- displayRow++;
+ m_vecWellData.append(oWellRow);
+ addWellDataToTable(oWellRow, nDisplayRow);
+ ++nDisplayRow;
}
- m_pTableWidget->setRowCount(displayRow); // 调整实际显示的行数
+ m_pTableWidget->setRowCount(nDisplayRow); // 调整实际显示的行数
m_pTableWidget->horizontalHeader()->setStretchLastSection(true);
m_pTableWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch);
}
@@ -147,6 +156,7 @@ void nmWxIncludeOtherWells::addWellDataToTable(const WellDataRow& data, int row)
QTableWidgetItem* nameItem = new QTableWidgetItem(data.m_sName);
nameItem->setFlags(nameItem->flags() & ~Qt::ItemIsEditable);
nameItem->setData(Qt::UserRole, data.m_sWellCode); // 表格内部始终携带 WellCode
+ nameItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
m_pTableWidget->setItem(row, 0, nameItem);
// Included列 - 使用自定义复选框组件
@@ -180,6 +190,7 @@ void nmWxIncludeOtherWells::addWellDataToTable(const WellDataRow& data, int row)
QTableWidgetItem* item = new QTableWidgetItem(listOtherData[col-2]);
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
item->setData(Qt::UserRole, row);
+ item->setTextAlignment(Qt::AlignCenter);
m_pTableWidget->setItem(row, col, item);
}
}
@@ -192,18 +203,18 @@ void nmWxIncludeOtherWells::onCheckBoxStateChanged(int state)
}
const int nRow = pCheckBox->property("row").toInt();
- if(nRow < 0 || nRow >= m_wellData.size()) {
+ if(nRow < 0 || nRow >= m_vecWellData.size()) {
return;
}
// 主井正常不会进入本表;保留该判断作为防御,避免未来改动误取消主井。
- if(m_wellData[nRow].m_bIsPrimary) {
- m_wellData[nRow].m_bIsIncluded = true;
+ if(m_vecWellData[nRow].m_bIsPrimary) {
+ m_vecWellData[nRow].m_bIsIncluded = true;
return;
}
// 其他候选井只记录选择状态,绝不在该对话框内创建或删除 Map 井。
- m_wellData[nRow].m_bIsIncluded = (state == Qt::Checked);
+ m_vecWellData[nRow].m_bIsIncluded = (state == Qt::Checked);
m_pTableWidget->selectRow(nRow);
}
@@ -213,7 +224,7 @@ void nmWxIncludeOtherWells::onItemClicked(QTableWidgetItem* item)
QVector nmWxIncludeOtherWells::getWellData() const
{
- return m_wellData;
+ return m_vecWellData;
}
void nmWxIncludeOtherWells::setupButtons()
diff --git a/Src/nmNum/nmSubWxs/nmWxNumericalDesign.cpp b/Src/nmNum/nmSubWxs/nmWxNumericalDesign.cpp
index a70dc840..3e781e46 100644
--- a/Src/nmNum/nmSubWxs/nmWxNumericalDesign.cpp
+++ b/Src/nmNum/nmSubWxs/nmWxNumericalDesign.cpp
@@ -1098,7 +1098,8 @@ void nmWxNumericalDesign::onOptionsIconClicked()
continue;
}
- if(pWellData->getFlowPoints().size() < 2) {
+ if(!nmIsValidWellCategory(pWellData->getWellCategory()) ||
+ pWellData->getFlowSegmentCount() <= 0) {
// 对话框打开后若产量数据发生变化,提交时仍要阻止它混入包含列表。
continue;
}
@@ -1214,7 +1215,9 @@ void nmWxNumericalDesign::updateIncludeOtherWellsAvailability()
continue;
}
- const bool bHasRateTarget = pWellData->getFlowPoints().size() >= 2;
+ const bool bHasRateTarget =
+ nmIsValidWellCategory(pWellData->getWellCategory()) &&
+ pWellData->getFlowSegmentCount() > 0;
if(bHasRateTarget) {
bHasEligibleOtherWell = true;
break;
diff --git a/Src/nmNum/nmSubWxs/nmWxWellEditor.cpp b/Src/nmNum/nmSubWxs/nmWxWellEditor.cpp
index 03a9929a..aab9c1f9 100644
--- a/Src/nmNum/nmSubWxs/nmWxWellEditor.cpp
+++ b/Src/nmNum/nmSubWxs/nmWxWellEditor.cpp
@@ -51,6 +51,7 @@ nmWxWellEditor::nmWxWellEditor(const nmWellEditContext& oContext,
m_pWellEditorController(nullptr),
m_pWellCodeLineEdit(nullptr),
m_pWellNameLineEdit(nullptr),
+ m_pWellCategoryLineEdit(nullptr),
m_pWellTypeComboBox(nullptr),
m_pTimeDependentCheckBox(nullptr),
m_pTimeDependentButton(nullptr),
@@ -105,6 +106,22 @@ bool nmWxWellEditor::initialize(QString& sError)
// 第一步:同步井信息和时间变表皮控件。
m_pWellCodeLineEdit->setText(pWell->getWellCode());
m_pWellNameLineEdit->setText(pWell->getWellName());
+ QString sWellCategory;
+ switch(pWell->getWellCategory()) {
+ case NM_WellCategory_Oil:
+ sWellCategory = tr("Oil well");
+ break;
+ case NM_WellCategory_Gas:
+ sWellCategory = tr("Gas well");
+ break;
+ case NM_WellCategory_Water:
+ sWellCategory = tr("Water well");
+ break;
+ default:
+ sWellCategory = tr("Unknown");
+ break;
+ }
+ m_pWellCategoryLineEdit->setText(sWellCategory);
int nWellTypeIndex = m_pWellTypeComboBox->findData(
static_cast(pWell->getWellType()));
m_bChangingWellType = true;
@@ -645,25 +662,31 @@ QWidget* nmWxWellEditor::createWellInformationWidget(QWidget* pParent)
QLabel* pCodeLabel = new QLabel(tr("Well number"), pInformationGroup);
QLabel* pNameLabel = new QLabel(tr("Well name"), pInformationGroup);
- QLabel* pTypeLabel = new QLabel(tr("Well type"), pInformationGroup);
+ QLabel* pCategoryLabel = new QLabel(
+ tr("Well category"), pInformationGroup);
+ QLabel* pTypeLabel = new QLabel(tr("Well model"), pInformationGroup);
QLabel* pTimeDependentLabel = new QLabel(
tr("Time-dependent skin"), pInformationGroup);
pCodeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
pNameLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ pCategoryLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
pTypeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
pTimeDependentLabel->setAlignment(
Qt::AlignRight | Qt::AlignVCenter);
m_pWellCodeLineEdit = new QLineEdit(pInformationGroup);
m_pWellNameLineEdit = new QLineEdit(pInformationGroup);
+ m_pWellCategoryLineEdit = new QLineEdit(pInformationGroup);
m_pWellTypeComboBox = new QComboBox(pInformationGroup);
m_pWellCodeLineEdit->setReadOnly(true);
+ m_pWellCategoryLineEdit->setReadOnly(true);
m_pWellCodeLineEdit->setToolTip(
tr("Project-level unique well identifier"));
QList listEditors;
listEditors << m_pWellCodeLineEdit
<< m_pWellNameLineEdit
+ << m_pWellCategoryLineEdit
<< m_pWellTypeComboBox;
for (int nIndex = 0; nIndex < listEditors.size(); ++nIndex)
{
@@ -701,10 +724,12 @@ QWidget* nmWxWellEditor::createWellInformationWidget(QWidget* pParent)
pInformationLayout->addWidget(m_pWellCodeLineEdit, 0, 1);
pInformationLayout->addWidget(pNameLabel, 1, 0);
pInformationLayout->addWidget(m_pWellNameLineEdit, 1, 1);
- pInformationLayout->addWidget(pTypeLabel, 2, 0);
- pInformationLayout->addWidget(m_pWellTypeComboBox, 2, 1);
- pInformationLayout->addWidget(pTimeDependentLabel, 3, 0);
- pInformationLayout->addWidget(pTimeDependentEditor, 3, 1);
+ pInformationLayout->addWidget(pCategoryLabel, 2, 0);
+ pInformationLayout->addWidget(m_pWellCategoryLineEdit, 2, 1);
+ pInformationLayout->addWidget(pTypeLabel, 3, 0);
+ pInformationLayout->addWidget(m_pWellTypeComboBox, 3, 1);
+ pInformationLayout->addWidget(pTimeDependentLabel, 4, 0);
+ pInformationLayout->addWidget(pTimeDependentEditor, 4, 1);
connect(m_pTimeDependentCheckBox, SIGNAL(toggled(bool)),
this, SLOT(slotTimeDependentToggled(bool)));