feat(nmNum): 按求解模型优化储层参数编辑

- 根据求解器模型动态显示 PVT、压缩系数和饱和度参数
- 隐藏储层类型和 KxKy,清理无效的单值参数定义
- 补充油水饱和度之和校验及关联错误提示
- 优化对话框初始高度和参数分组名称
- 更新中英文参数 XML 及中文翻译资源
feature/ReservoirPropertiesDlg-Refactoring-20260813
lh 2 days ago
parent 9aa97ec161
commit dbe5ac328f

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

@ -6793,6 +6793,7 @@ Average pressure in contour: %2 MPa</source>
<message><source>Object display</source><translation></translation></message>
<message><source>Display:</source><translation></translation></message>
<message><source>No editable objects in this category.</source><translation></translation></message>
<message><source>Initial oil and water saturations must sum to 1.</source><translation> 1</translation></message>
</context>
<context>
<name>nmWxReservoirPropertiesEditor</name>
@ -6829,9 +6830,12 @@ Average pressure in contour: %2 MPa</source>
<message><source>Position and wellbore</source><translation></translation></message>
<message><source>Wellbore storage</source><translation></translation></message>
<message><source>Wellbore storage and fracture</source><translation></translation></message>
<message><source>Basic properties</source><translation></translation></message>
<message><source>Basic and medium properties</source><translation></translation></message>
<message><source>Fluid parameters</source><translation></translation></message>
<message><source>Compression and saturation</source><translation></translation></message>
<message><source>Oil phase parameters</source><translation></translation></message>
<message><source>Water phase parameters</source><translation></translation></message>
<message><source>Compressibility</source><translation></translation></message>
<message><source>Compressibility and saturation</source><translation></translation></message>
<message><source>Reservoir medium parameters</source><translation></translation></message>
<message><source>Geometry</source><translation></translation></message>
<message><source>Flow properties</source><translation></translation></message>

@ -21,10 +21,12 @@
<ParaItem Name="phi" Alias="孔隙度" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="6" Desc="porosity" />
<ParaItem Name="Bo" Alias="油体积系数" Unit="" Default="1.2" Max="10" Min="1e-06" Digit="6" Desc="oil formation volume factor" />
<ParaItem Name="Miuo" Alias="油相黏度" Unit="mPa.s" Default="0.5" Max="10000" Min="1e-06" Digit="6" Desc="oil viscosity" />
<ParaItem Name="KxKy" Alias="水平渗透率比" Unit="" Default="1" Max="1000" Min="1e-06" Digit="6" Desc="horizontal permeability ratio" />
<ParaItem Name="Bw" Alias="水体积系数" Unit="" Default="1" Max="10" Min="1e-06" Digit="6" Desc="water formation volume factor" />
<ParaItem Name="Miuw" Alias="水相黏度" Unit="mPa.s" Default="1" Max="10000" Min="1e-06" Digit="6" Desc="water viscosity" />
<ParaItem Name="Cti" Alias="综合压缩系数" Unit="1/MPa" Default="0.001" Max="10" Min="1e-30" Digit="10" Scientific="1" Desc="total compressibility" />
<ParaItem Name="Cf" Alias="岩石压缩系数" Unit="1/MPa" Default="0.0001" Max="10" Min="1e-30" Digit="10" Scientific="1" Desc="rock compressibility" />
<ParaItem Name="Soi" Alias="初始含油饱和度" Unit="" Default="0.8" Max="1" Min="0" Digit="6" Desc="initial oil saturation" />
<ParaItem Name="Sgi" Alias="初始含气饱和度" Unit="" Default="0" Max="1" Min="0" Digit="6" Desc="initial gas saturation" />
<ParaItem Name="Swi" Alias="初始含水饱和度" Unit="" Default="0.2" Max="1" Min="0" Digit="6" Desc="initial water saturation" />
</ParaGroup>

@ -22,10 +22,12 @@
<ParaItem Name="phi" Alias="Porosity" Unit="" Default="0.1" Max="1" Min="1e-05" Digit="4" Desc="porosity" />
<ParaItem Name="Bo" Alias="Oil Formation Volume Factor" Unit="" Default="1.2" Max="10" Min="1e-06" Digit="6" Desc="oil formation volume factor" />
<ParaItem Name="Miuo" Alias="Oil Viscosity" Unit="mPa.s" Default="0.5" Max="10000" Min="1e-06" Digit="6" Desc="oil viscosity" />
<ParaItem Name="KxKy" Alias="Horizontal Permeability Ratio" Unit="" Default="1" Max="1000" Min="1e-06" Digit="6" Desc="horizontal permeability ratio" />
<ParaItem Name="Bw" Alias="Water Formation Volume Factor" Unit="" Default="1" Max="10" Min="1e-06" Digit="6" Desc="water formation volume factor" />
<ParaItem Name="Miuw" Alias="Water Viscosity" Unit="mPa.s" Default="1" Max="10000" Min="1e-06" Digit="6" Desc="water viscosity" />
<ParaItem Name="Cti" Alias="Total Compressibility" Unit="1/MPa" Default="0.001" Max="10" Min="1e-30" Digit="10" Scientific="1" Desc="total compressibility" />
<ParaItem Name="Cf" Alias="Rock Compressibility" Unit="1/MPa" Default="0.0001" Max="10" Min="1e-30" Digit="10" Scientific="1" Desc="rock compressibility" />
<ParaItem Name="Soi" Alias="Initial Oil Saturation" Unit="" Default="0.8" Max="1" Min="0" Digit="6" Desc="initial oil saturation" />
<ParaItem Name="Sgi" Alias="Initial Gas Saturation" Unit="" Default="0" Max="1" Min="0" Digit="6" Desc="initial gas saturation" />
<ParaItem Name="Swi" Alias="Initial Water Saturation" Unit="" Default="0.2" Max="1" Min="0" Digit="6" Desc="initial water saturation" />
</ParaGroup>

@ -117,6 +117,9 @@ public:
/** @brief 返回当前枚举稳定code。 */
QString enumCode() const;
/** @brief 设置页面级关联校验错误;空文字表示清除关联错误。 */
void setExternalValidationError(const QString& sError);
/** @brief 校验当前输入,并更新参数范围提示。 */
bool validate();
@ -159,6 +162,7 @@ private:
QComboBox* m_pUnitComboBox; ///< 可换算单位下拉框。
QLabel* m_pUnitLabel; ///< 无单位字段的空白对齐占位。
QColor m_clrNormalText; ///< 输入框默认文字颜色。
QString m_sExternalValidationError; ///< 页面级关联校验错误文字。
bool m_bVisible; ///< 参数行是否参与编辑。
bool m_bUpdating; ///< 程序写值时阻止业务信号。
};

@ -22,6 +22,7 @@ struct NM_SUB_WXS_EXPORT nmReservoirPropertiesSnapshot
/** @brief 构造空快照。 */
nmReservoirPropertiesSnapshot();
int m_nSolverModelType; ///< 打开对话框时的求解器模型类型数值。
nmDataReservoir m_oReservoir; ///< 储层参数副本。
QVector<nmDataVerticalWell> m_vecVerticalWells; ///< 直井参数副本。
QVector<nmDataVerticalFracturedWell> m_vecVerticalFracturedWells; ///< 垂直压裂井参数副本。

@ -2,6 +2,7 @@
#include "nmReservoirPropertiesDataSource.h"
#include "nmAttrRegistry.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataAttribute.h"
#include "nmDataPerforation.h"
@ -31,9 +32,12 @@ static const char* const s_pCatalogTranslationMarkers[] =
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Position and wellbore"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Wellbore storage"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Wellbore storage and fracture"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Basic properties"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Basic and medium properties"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Fluid parameters"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Compression and saturation"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Oil phase parameters"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Water phase parameters"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Compressibility"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Compressibility and saturation"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Reservoir parameters"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Vertical well"),
QT_TRANSLATE_NOOP("nmWxReservoirPropertiesEditor", "Fractured vertical well"),
@ -162,6 +166,141 @@ nmReservoirParameterGroupInfo parameterGroup(
return oGroup;
}
/** @brief 返回当前求解器模型对应的流体参数分组标题。 */
const char* solverPvtGroupTitle(
int nSolverModelType,
NM_PHASE_TYPE ePhaseType)
{
switch (nSolverModelType)
{
case SMT_Oil_ConstPvt:
return "Oil phase parameters";
case SMT_Water_ConstPvt:
return "Water phase parameters";
default:
if (nSolverModelType == 0 && ePhaseType == PHASE_Oil)
{
return "Oil phase parameters";
}
if (nSolverModelType == 0 && ePhaseType == PHASE_Water)
{
return "Water phase parameters";
}
return "Fluid parameters";
}
}
/** @brief 按求解器模型向流体分组添加实际使用的PVT单值参数。 */
void appendSolverPvtFields(
nmReservoirParameterGroupInfo& oGroup,
int nSolverModelType,
NM_PHASE_TYPE ePhaseType)
{
switch (nSolverModelType)
{
case SMT_Oil_ConstPvt:
oGroup.m_listFields
<< numericField("Miuo", "Miuo")
<< unitlessNumericField("Bo", "Bo");
break;
case SMT_Water_ConstPvt:
oGroup.m_listFields
<< numericField("Miuw", "Miuw")
<< unitlessNumericField("Bw", "Bw");
break;
default:
// 自定义数据源未提供模型类型时,按单相相态保留兼容入口。
if (nSolverModelType == 0 &&
(ePhaseType == PHASE_Oil ||
ePhaseType == PHASE_UNKNOWN))
{
oGroup.m_listFields
<< numericField("Miuo", "Miuo")
<< unitlessNumericField("Bo", "Bo");
}
else if (nSolverModelType == 0 &&
ePhaseType == PHASE_Water)
{
oGroup.m_listFields
<< numericField("Miuw", "Miuw")
<< unitlessNumericField("Bw", "Bw");
}
break;
}
}
/** @brief 判断当前模型是否需要编辑初始含水饱和度。 */
bool solverUsesInitialWaterSaturation(
int nSolverModelType,
NM_PHASE_TYPE ePhaseType)
{
return nSolverModelType == SMT_Oil_Water_TwoPhase ||
(nSolverModelType == 0 &&
ePhaseType == PHASE_Oil_Water);
}
/** @brief 判断当前模型是否需要编辑初始含油饱和度。 */
bool solverUsesInitialOilSaturation(
int nSolverModelType,
NM_PHASE_TYPE ePhaseType)
{
return nSolverModelType == SMT_Oil_Water_TwoPhase ||
(nSolverModelType == 0 &&
ePhaseType == PHASE_Oil_Water);
}
/** @brief 判断当前模型是否需要编辑初始含气饱和度。 */
bool solverUsesInitialGasSaturation(
int nSolverModelType,
NM_PHASE_TYPE ePhaseType)
{
return nSolverModelType == SMT_Gas_VariablePvt ||
nSolverModelType == SMT_Gas_PseudoPressure ||
(nSolverModelType == 0 &&
ePhaseType == PHASE_Gas);
}
/** @brief 按求解器模型添加实际使用的压缩系数和饱和度参数。 */
void appendSolverCompressionFields(
nmReservoirParameterGroupInfo& oGroup,
int nSolverModelType,
NM_PHASE_TYPE ePhaseType)
{
switch (nSolverModelType)
{
case SMT_Oil_ConstPvt:
case SMT_Water_ConstPvt:
oGroup.m_listFields.append(numericField("Cti", "Cti"));
break;
default:
if (nSolverModelType == 0 &&
(ePhaseType == PHASE_Oil ||
ePhaseType == PHASE_Water ||
ePhaseType == PHASE_UNKNOWN))
{
oGroup.m_listFields.append(numericField("Cti", "Cti"));
}
else
{
oGroup.m_listFields.append(numericField("Cf", "Cf"));
}
break;
}
if (solverUsesInitialOilSaturation(nSolverModelType, ePhaseType))
{
oGroup.m_listFields.append(numericField("Soi", "Soi"));
}
if (solverUsesInitialWaterSaturation(nSolverModelType, ePhaseType))
{
oGroup.m_listFields.append(numericField("Swi", "Swi"));
}
if (solverUsesInitialGasSaturation(nSolverModelType, ePhaseType))
{
oGroup.m_listFields.append(numericField("Sgi", "Sgi"));
}
}
/** @brief 为参数ID添加对象编码前缀。 */
QString prefixed(const QString& sObjectCode, const char* pParameterId)
{
@ -306,10 +445,12 @@ void nmReservoirParameterCatalog::registerSnapshot(
pRegistry->regAttr("Cti", &oSnapshot.m_oReservoir.getCt());
pRegistry->regAttr("Cf", &oSnapshot.m_oReservoir.getCf());
pRegistry->regAttr("Soi", &oSnapshot.m_oReservoir.getSoi());
pRegistry->regAttr("Sgi", &oSnapshot.m_oReservoir.getSgi());
pRegistry->regAttr("Swi", &oSnapshot.m_oReservoir.getSwi());
pRegistry->regAttr("Bo", &oSnapshot.m_oReservoir.getBo());
pRegistry->regAttr("Miuo", &oSnapshot.m_oReservoir.getMiuo());
pRegistry->regAttr("KxKy", &oSnapshot.m_oReservoir.getKxKy());
pRegistry->regAttr("Bw", &oSnapshot.m_oReservoir.getBw());
pRegistry->regAttr("Miuw", &oSnapshot.m_oReservoir.getMiuw());
// 第二步:注册求解器支持的三类井及其压裂参数。
for (int nIndex = 0;
@ -437,8 +578,11 @@ nmReservoirParameterCatalog::createObjectInfos(
oInfo.m_sObjectCode = "RESERVOIR";
oInfo.m_sDisplayName = editorText("Reservoir parameters");
NM_PHASE_TYPE ePhaseType =
oSnapshot.m_oReservoir.getPhaseType();
nmReservoirParameterGroupInfo oBasic =
parameterGroup("Basic properties", 0, 0, 1);
parameterGroup("Basic and medium properties", 0, 0, 1);
oBasic.m_nRowSpan = 2;
oBasic.m_bValueRightAligned = true;
oBasic.m_listFields
@ -448,21 +592,37 @@ nmReservoirParameterCatalog::createObjectInfos(
<< numericField("phi", "phi");
nmReservoirParameterGroupInfo oFluid =
parameterGroup("Fluid parameters", 0, 1, 1);
oFluid.m_listFields
<< reservoirModelField("ReservoirType")
<< numericField("Miuo", "Miuo")
<< unitlessNumericField("Bo", "Bo")
<< numericField("KxKy", "KxKy");
parameterGroup(solverPvtGroupTitle(
oSnapshot.m_nSolverModelType,
ePhaseType),
0, 1, 1);
appendSolverPvtFields(oFluid,
oSnapshot.m_nSolverModelType,
ePhaseType);
bool bUsesSaturation = solverUsesInitialWaterSaturation(
oSnapshot.m_nSolverModelType, ePhaseType) ||
solverUsesInitialGasSaturation(
oSnapshot.m_nSolverModelType, ePhaseType);
nmReservoirParameterGroupInfo oCompression =
parameterGroup("Compression and saturation", 1, 1, 1);
oCompression.m_listFields
<< numericField("Cti", "Cti")
<< numericField("Cf", "Cf")
<< numericField("Soi", "Soi")
<< numericField("Swi", "Swi");
oInfo.m_listGroups << oBasic << oFluid << oCompression;
parameterGroup(bUsesSaturation
? "Compressibility and saturation"
: "Compressibility",
oFluid.m_listFields.isEmpty() ? 0 : 1,
1, 1);
if (oFluid.m_listFields.isEmpty())
{
oCompression.m_nRowSpan = 2;
}
appendSolverCompressionFields(oCompression,
oSnapshot.m_nSolverModelType,
ePhaseType);
oInfo.m_listGroups.append(oBasic);
if (!oFluid.m_listFields.isEmpty())
{
oInfo.m_listGroups.append(oFluid);
}
oInfo.m_listGroups.append(oCompression);
listInfos.append(oInfo);
return listInfos;
}

@ -66,6 +66,7 @@ nmReservoirParameterField::nmReservoirParameterField(
m_pUnitComboBox(nullptr),
m_pUnitLabel(nullptr),
m_clrNormalText(),
m_sExternalValidationError(),
m_bVisible(true),
m_bUpdating(false)
{
@ -315,6 +316,13 @@ QString nmReservoirParameterField::enumCode() const
m_pValueComboBox->currentIndex()).toString();
}
void nmReservoirParameterField::setExternalValidationError(
const QString& sError)
{
m_sExternalValidationError = sError;
validate();
}
bool nmReservoirParameterField::validate()
{
if (!m_bVisible || isEnum())
@ -345,6 +353,7 @@ bool nmReservoirParameterField::validate()
bValid = dValue == static_cast<int>(dValue);
}
}
bValid = bValid && m_sExternalValidationError.isEmpty();
updateValidationAppearance(bValid);
return bValid;
}
@ -461,6 +470,14 @@ void nmReservoirParameterField::updateValidationAppearance(bool bValid)
}
sHelp += sRange;
}
if (!m_sExternalValidationError.isEmpty())
{
if (!sHelp.isEmpty())
{
sHelp += "\n";
}
sHelp += m_sExternalValidationError;
}
m_pLineEdit->setToolTip(validationToolTip(sHelp, bValid));
m_pLineEdit->setWhatsThis(sHelp);
if (m_pLabel != nullptr)

@ -10,6 +10,7 @@
#include <QLabel>
#include <QSizePolicy>
#include <QVBoxLayout>
#include <QtGlobal>
nmReservoirParameterPage::nmReservoirParameterPage(
nmReservoirPropertiesCategory eCategory,
@ -375,6 +376,20 @@ bool nmReservoirParameterPage::commitPendingEdits()
bool nmReservoirParameterPage::isInputValid()
{
nmReservoirParameterField* pSoiField =
m_mapFields.value("Soi", nullptr);
nmReservoirParameterField* pSwiField =
m_mapFields.value("Swi", nullptr);
if (pSoiField != nullptr)
{
pSoiField->setExternalValidationError(QString());
}
if (pSwiField != nullptr)
{
pSwiField->setExternalValidationError(QString());
}
bool bAllValid = true;
for (int nFieldIndex = 0;
nFieldIndex < m_listFields.size();
++nFieldIndex)
@ -384,10 +399,32 @@ bool nmReservoirParameterPage::isInputValid()
if (pField != nullptr && pField->isFieldVisible() &&
!pField->validate())
{
return false;
bAllValid = false;
}
}
return true;
// 油水两相同时显示两个初始饱和度时两者之和必须为1。
if (pSoiField != nullptr && pSwiField != nullptr &&
pSoiField->isFieldVisible() &&
pSwiField->isFieldVisible())
{
bool bSoiValid = false;
bool bSwiValid = false;
QVariant oSoi = pSoiField->baseValue(bSoiValid);
QVariant oSwi = pSwiField->baseValue(bSwiValid);
const double dTolerance = 1.0e-6;
if (bSoiValid && bSwiValid &&
qAbs(oSoi.toDouble() + oSwi.toDouble() - 1.0) >
dTolerance)
{
QString sError = tr(
"Initial oil and water saturations must sum to 1.");
pSoiField->setExternalValidationError(sError);
pSwiField->setExternalValidationError(sError);
bAllValid = false;
}
}
return bAllValid;
}
void nmReservoirParameterPage::slotFieldValueEdited()

@ -5,6 +5,7 @@
#include "nmWxParameterProperty.h"
nmReservoirPropertiesSnapshot::nmReservoirPropertiesSnapshot()
: m_nSolverModelType(0)
{
}
@ -34,7 +35,9 @@ bool nmAnalyzeManagerReservoirDataSource::loadSnapshot(
return false;
}
// 第二步:复制储层和几何对象,确保界面编辑不会触碰实时数据。
// 第二步:复制求解器模型、储层和几何对象,确保界面编辑不会触碰实时数据。
oSnapshot.m_nSolverModelType =
static_cast<int>(m_pDataManager->getSolverModelType());
oSnapshot.m_oReservoir = m_pDataManager->getReservoirDataCopy();
oSnapshot.m_vecRegionMarks = m_pDataManager->getRegionMarkDataListCopy();
oSnapshot.m_vecFaults = m_pDataManager->getFaultDataListCopy();

@ -48,7 +48,7 @@ nmWxReservoirPropertiesEditor::nmWxReservoirPropertiesEditor(
setWindowTitle(tr("Reservoir properties settings"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
// 首次打开使用建议尺寸,后续最小宽高均由页面内容和布局自动决定。
resize(880, 520);
resize(880, 480);
}
nmWxReservoirPropertiesEditor::~nmWxReservoirPropertiesEditor()

Loading…
Cancel
Save