|
|
#include "nmWxNumericalDesign.h"
|
|
|
|
|
|
#include <QScrollArea>
|
|
|
#include <QCoreApplication>
|
|
|
#include <QGroupBox>
|
|
|
#include <QCheckBox>
|
|
|
#include <QComboBox>
|
|
|
#include <QSpinBox>
|
|
|
#include <QRadioButton>
|
|
|
#include <QLineEdit>
|
|
|
#include <QVBoxLayout>
|
|
|
#include <QHBoxLayout>
|
|
|
#include <QGridLayout>
|
|
|
#include <QPushButton>
|
|
|
#include <QLabel>
|
|
|
#include <QMessageBox>
|
|
|
|
|
|
#include "iUnitGroup.h"
|
|
|
#include "iUnitWxBinder.h"
|
|
|
#include "mModuleDefines.h"
|
|
|
#include "nmWxIncludeOtherWells.h"
|
|
|
#include "nmWxTimeDependentSkin.h"
|
|
|
#include "nmWxAutomaticFitting.h"
|
|
|
#include "nmWxParameterProperty.h"
|
|
|
#include "nmWxForecast.h"
|
|
|
#include "nmWxSensitive.h"
|
|
|
|
|
|
#include "nmDataWellBase.h"
|
|
|
#include "nmDataReservoir.h"
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
#include "nmPebiResultSnapshot.h"
|
|
|
#include "nmDataTimeStepSetting.h"
|
|
|
#include "nmDataDiagnostic.h"
|
|
|
|
|
|
namespace
|
|
|
{
|
|
|
|
|
|
const char* const s_pTimeGrowthExponentId = "NM_TimeGrowthExponent";
|
|
|
const char* const s_pDtMinId = "NM_DtMin";
|
|
|
const char* const s_pDtMaxId = "NM_DtMax";
|
|
|
|
|
|
nmParameterFieldInfo createTimeStepFieldInfo(
|
|
|
const char* pParameterId,
|
|
|
bool bUnitEnabled)
|
|
|
{
|
|
|
nmParameterFieldInfo oFieldInfo;
|
|
|
oFieldInfo.m_sParameterId = QLatin1String(pParameterId);
|
|
|
oFieldInfo.m_sBaseParameterId = QLatin1String(pParameterId);
|
|
|
oFieldInfo.m_eEditorType = NM_PARAMETER_EDITOR_NUMBER;
|
|
|
oFieldInfo.m_bUnitEnabled = bUnitEnabled;
|
|
|
return oFieldInfo;
|
|
|
}
|
|
|
|
|
|
// 判断其他 Map 井是否具备进入 Include Other Wells 候选集的数值井型。
|
|
|
bool isSupportedAdditionalWell(nmDataWellBase* pWellData)
|
|
|
{
|
|
|
if(pWellData == nullptr || pWellData->getWellCode().isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
const NM_WELL_MODEL eWellType = pWellData->getWellType();
|
|
|
return eWellType == Vertical_Well ||
|
|
|
eWellType == Vertical_Fractured_Well ||
|
|
|
eWellType == Horizontal_Fractured_Well;
|
|
|
}
|
|
|
|
|
|
// 可配置性看框架记录本身,不能看当前是否已把三个相都设为“无”,
|
|
|
// 否则用户清空分相后将无法再次打开对话框恢复选择。
|
|
|
bool hasAvailableRateTarget(nmDataWellBase* pWellData)
|
|
|
{
|
|
|
if(pWellData == nullptr ||
|
|
|
!nmIsValidWellCategory(pWellData->getWellCategory())) {
|
|
|
return false;
|
|
|
}
|
|
|
return pWellData->hasAvailableFlowRate();
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
nmWxNumericalDesign* nmWxNumericalDesign::s_pCurrentInstance = nullptr;
|
|
|
|
|
|
static bool isNumericalDesignDebugOptionsVisible()
|
|
|
{
|
|
|
#ifdef QT_DEBUG
|
|
|
return true;
|
|
|
#endif
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 显示当前模型类型
|
|
|
static QString solverModelDisplayName(NM_SOLVER_MODEL_TYPE eType)
|
|
|
{
|
|
|
switch(eType) {
|
|
|
case SMT_Oil_ConstPvt:
|
|
|
return nmWxNumericalDesign::tr("Oil single-phase constant pvt");
|
|
|
case SMT_Oil_VariablePvt:
|
|
|
return nmWxNumericalDesign::tr("Oil single-phase variable pvt");
|
|
|
case SMT_Water_ConstPvt:
|
|
|
return nmWxNumericalDesign::tr("Water single-phase constant pvt");
|
|
|
case SMT_Water_VariablePvt:
|
|
|
return nmWxNumericalDesign::tr("Water single-phase variable pvt");
|
|
|
case SMT_Gas_VariablePvt:
|
|
|
return nmWxNumericalDesign::tr("Gas single-phase variable pvt");
|
|
|
case SMT_Oil_Water_TwoPhase:
|
|
|
return nmWxNumericalDesign::tr("Oil-water two-phase");
|
|
|
default:
|
|
|
return QString();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
nmWxNumericalDesign::nmWxNumericalDesign(
|
|
|
nmDataAnalyzeManager* pDataManager,
|
|
|
QWidget* pParent)
|
|
|
: iDlgBase(pParent),
|
|
|
m_pDataManager(pDataManager),
|
|
|
m_pTimeGrowthExponentField(nullptr),
|
|
|
m_pDtMinField(nullptr),
|
|
|
m_pDtMaxField(nullptr)
|
|
|
{
|
|
|
Q_ASSERT(m_pDataManager != nullptr);
|
|
|
|
|
|
// 设置当前实例为静态指针
|
|
|
s_pCurrentInstance = this;
|
|
|
m_pModelAndResultGroup = nullptr;
|
|
|
m_pResultWellCombo = nullptr;
|
|
|
m_pCurrentModelCombo = nullptr;
|
|
|
m_bFillingResultWellCombo = false;
|
|
|
m_bFillingCurrentModelCombo = false;
|
|
|
// 单位绑定器必须先切换到数值试井参数系列,再创建时间步字段。
|
|
|
setParaSrcTag(s_Nm_Serie);
|
|
|
|
|
|
// 初始化图标路径
|
|
|
m_sIconDir = QCoreApplication::applicationDirPath();
|
|
|
m_sIconDir = m_sIconDir.section('/', 0, -2); // 获取上一级目录
|
|
|
m_sIconDir += "/Res/Icon/";
|
|
|
|
|
|
// 设置窗口的尺寸策略为可扩展
|
|
|
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
|
|
|
|
// 创建滚动区域
|
|
|
QScrollArea* pScrollArea = new QScrollArea(this);
|
|
|
pScrollArea->setWidgetResizable(true);
|
|
|
|
|
|
QWidget* pScrollWidget = new QWidget();
|
|
|
pScrollWidget->setObjectName("nmNumericalDesignScrollWidget");
|
|
|
pScrollWidget->setStyleSheet("#nmNumericalDesignScrollWidget { background-color: white; }");
|
|
|
QVBoxLayout* pScrollLayout = new QVBoxLayout(pScrollWidget);
|
|
|
|
|
|
// 初始化所有UI组件
|
|
|
initMainOptionsGroup(); // 初始化主选项组
|
|
|
initModelAndResultGroup();
|
|
|
initOutputGroup(); // 初始化输出组 (新)
|
|
|
initAdvancedGroup(); // 初始化高级组 (新)
|
|
|
initTimeSteppingGroup(); // 初始化时间步进组
|
|
|
initNumericalSettingsGroup(); // 初始化数值设置组 (新)
|
|
|
initPebiSolverSettingsGroup(); // 初始化PEBI求解器设置组
|
|
|
initButton(); // 初始化生成按钮
|
|
|
|
|
|
// 将各个组添加到滚动区域的布局中
|
|
|
pScrollLayout->addWidget(m_pMainOptionsGroup);
|
|
|
pScrollLayout->addWidget(m_pModelAndResultGroup);
|
|
|
pScrollLayout->addWidget(m_pOutputGroup);
|
|
|
pScrollLayout->addWidget(m_pAdvancedGroup);
|
|
|
pScrollLayout->addWidget(m_pTimeSteppingGroup);
|
|
|
pScrollLayout->addWidget(m_pNumericalSettingsGroup); // 新的数值设置组
|
|
|
pScrollLayout->addWidget(m_pPebiSolverSettingsGroup);
|
|
|
pScrollLayout->addStretch(); // 添加拉伸器,使内容靠上
|
|
|
|
|
|
pScrollWidget->setLayout(pScrollLayout); // 设置滚动容器的布局
|
|
|
pScrollArea->setWidget(pScrollWidget); // 将滚动容器设置给滚动区域
|
|
|
|
|
|
// 设置主布局并添加滚动区域
|
|
|
m_pMainLayout = new QVBoxLayout(this); // 窗口的主布局
|
|
|
m_pMainLayout->addWidget(pScrollArea); // 将滚动区域添加到主布局
|
|
|
m_pMainLayout->addLayout(m_pGenerateLayout); // 将生成按钮布局添加到主布局底部
|
|
|
|
|
|
updateUiFromData(); // 更新时间步进组的组件值
|
|
|
|
|
|
// 设置信号槽连接
|
|
|
setupConnections();
|
|
|
|
|
|
//对于没有功能的组件,release下隐藏,debug下显示
|
|
|
bool bDebugOptionsVisible = isNumericalDesignDebugOptionsVisible();
|
|
|
m_pImposePiCheck->setVisible(bDebugOptionsVisible);
|
|
|
m_pShowAveragePressureCheck->setVisible(bDebugOptionsVisible);
|
|
|
m_pOutputGroup->setVisible(bDebugOptionsVisible);
|
|
|
m_pUseWellIntakeCheck->setVisible(bDebugOptionsVisible);
|
|
|
m_pRedefineKrPcCheck->setVisible(bDebugOptionsVisible);
|
|
|
m_pRedefineKrPcIconButton->setVisible(bDebugOptionsVisible);
|
|
|
m_pApplyUnconsolidationCheck->setVisible(bDebugOptionsVisible);
|
|
|
m_pApplyUnconsolidationIconButton->setVisible(bDebugOptionsVisible);
|
|
|
m_pUseDFNCheck->setVisible(bDebugOptionsVisible);
|
|
|
m_pNumericalSettingsGroup->setVisible(bDebugOptionsVisible);
|
|
|
m_pForecastBtn->setVisible(bDebugOptionsVisible);
|
|
|
m_pSensitiveBtn->setVisible(bDebugOptionsVisible);
|
|
|
}
|
|
|
|
|
|
nmWxNumericalDesign::~nmWxNumericalDesign()
|
|
|
{
|
|
|
// 所有子控件将由Qt的父子对象机制自动删除
|
|
|
|
|
|
// 如果当前实例是静态指针指向的实例,则清空静态指针
|
|
|
if(s_pCurrentInstance == this) {
|
|
|
s_pCurrentInstance = nullptr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool nmWxNumericalDesign::bindUnitField(
|
|
|
QLineEdit* pLineEdit,
|
|
|
QComboBox* pUnitComboBox,
|
|
|
const QString& sBaseParameterId)
|
|
|
{
|
|
|
return bindUnitPairWxs(pLineEdit,
|
|
|
pUnitComboBox,
|
|
|
sBaseParameterId,
|
|
|
false);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::setUnitBaseValue(
|
|
|
QLineEdit* pLineEdit,
|
|
|
double dBaseValue)
|
|
|
{
|
|
|
if(m_pUnitBinder != nullptr) {
|
|
|
m_pUnitBinder->setBaseValue(pLineEdit, dBaseValue);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
double nmWxNumericalDesign::unitBaseValue(QLineEdit* pLineEdit) const
|
|
|
{
|
|
|
return m_pUnitBinder != nullptr
|
|
|
? m_pUnitBinder->getBaseValue(pLineEdit) : 0.0;
|
|
|
}
|
|
|
|
|
|
bool nmWxNumericalDesign::isUnitInputValid(QLineEdit* pLineEdit)
|
|
|
{
|
|
|
return m_pUnitBinder != nullptr &&
|
|
|
m_pUnitBinder->isInputOK(pLineEdit, true, nullptr);
|
|
|
}
|
|
|
|
|
|
QString nmWxNumericalDesign::currentUnit(QLineEdit* pLineEdit) const
|
|
|
{
|
|
|
return m_pUnitBinder != nullptr
|
|
|
? m_pUnitBinder->getCurrentUnit(pLineEdit) : QString();
|
|
|
}
|
|
|
|
|
|
iUnitGroup* nmWxNumericalDesign::unitGroup(QLineEdit* pLineEdit) const
|
|
|
{
|
|
|
return m_pUnitBinder != nullptr
|
|
|
? m_pUnitBinder->getUnitGroup(pLineEdit) : nullptr;
|
|
|
}
|
|
|
|
|
|
// 激活当前成果面板,并刷新查看井结果下拉框
|
|
|
void nmWxNumericalDesign::activateCurrentInstance()
|
|
|
{
|
|
|
// 一个QMdiArea中可以同时打开多个成果窗口,每个成果都有自己的数值解面板。
|
|
|
// 已经打开过的成果再次被激活时不会重新构造面板,因此需要在切换时更新静态指针。
|
|
|
s_pCurrentInstance = this;
|
|
|
// 调用前已经切换到当前成果,此处统一刷新该Dock中的全部数据控件。
|
|
|
updateUiFromData();
|
|
|
}
|
|
|
|
|
|
// 外部刷新当前查看井下拉框
|
|
|
void nmWxNumericalDesign::notifyResultWellSelectorChanged(
|
|
|
nmDataAnalyzeManager* pDataManager,
|
|
|
const QString& sWellInstanceId)
|
|
|
{
|
|
|
// 静态活动面板可能已经切换到另一个成果,只刷新所属数据管理器一致的实例。
|
|
|
if(s_pCurrentInstance == nullptr ||
|
|
|
s_pCurrentInstance->m_pDataManager != pDataManager) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
s_pCurrentInstance->fillResultWellCombo(sWellInstanceId);
|
|
|
}
|
|
|
|
|
|
// 初始化模型与结果切换控件
|
|
|
void nmWxNumericalDesign::initModelAndResultGroup()
|
|
|
{
|
|
|
m_pModelAndResultGroup = new QGroupBox(tr("Model and result"), this);
|
|
|
QGridLayout* pGridLayout = new QGridLayout(m_pModelAndResultGroup);
|
|
|
pGridLayout->setContentsMargins(8, 15, 8, 10);
|
|
|
pGridLayout->setHorizontalSpacing(6);
|
|
|
pGridLayout->setVerticalSpacing(6);
|
|
|
|
|
|
QLabel* pCurrentModelLabel = new QLabel(
|
|
|
tr("Current model"), m_pModelAndResultGroup);
|
|
|
QLabel* pResultWellLabel = new QLabel(
|
|
|
tr("Result well"), m_pModelAndResultGroup);
|
|
|
pCurrentModelLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
pResultWellLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
|
|
|
m_pCurrentModelCombo = new QComboBox(m_pModelAndResultGroup);
|
|
|
m_pResultWellCombo = new QComboBox(m_pModelAndResultGroup);
|
|
|
QList<QComboBox*> listCombos;
|
|
|
listCombos << m_pCurrentModelCombo << m_pResultWellCombo;
|
|
|
for(int nIndex = 0; nIndex < listCombos.size(); ++nIndex) {
|
|
|
listCombos[nIndex]->setFixedHeight(nmParameterField::editorHeight());
|
|
|
listCombos[nIndex]->setMinimumWidth(
|
|
|
nmParameterField::editorMinimumWidth());
|
|
|
listCombos[nIndex]->setSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
|
}
|
|
|
|
|
|
// 按当前成果回填模型和结果井
|
|
|
updateCurrentModelDisplay();
|
|
|
fillResultWellCombo();
|
|
|
|
|
|
pGridLayout->addWidget(pCurrentModelLabel, 0, 0);
|
|
|
pGridLayout->addWidget(m_pCurrentModelCombo, 0, 1);
|
|
|
pGridLayout->addWidget(pResultWellLabel, 1, 0);
|
|
|
pGridLayout->addWidget(m_pResultWellCombo, 1, 1);
|
|
|
pGridLayout->setColumnStretch(0, 0);
|
|
|
pGridLayout->setColumnStretch(1, 1);
|
|
|
|
|
|
m_pModelAndResultGroup->setSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Preferred);
|
|
|
}
|
|
|
|
|
|
// 按当前成果对应的数据管理器刷新模型下拉框选中项
|
|
|
void nmWxNumericalDesign::updateCurrentModelDisplay()
|
|
|
{
|
|
|
if(m_pCurrentModelCombo == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_bFillingCurrentModelCombo = true;
|
|
|
m_pCurrentModelCombo->clear();
|
|
|
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
if(pManager == nullptr) {
|
|
|
m_pCurrentModelCombo->setEnabled(false);
|
|
|
m_bFillingCurrentModelCombo = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 根据储层相态过滤可选模型类型
|
|
|
nmDataReservoir* pReservoir = pManager->getReservoirData();
|
|
|
NM_PHASE_TYPE ePhase = (pReservoir != nullptr) ? pReservoir->getPhaseType() : PHASE_UNKNOWN;
|
|
|
NM_SOLVER_MODEL_TYPE eCurrentType = pManager->getSolverModelType();
|
|
|
|
|
|
switch(ePhase) {
|
|
|
case PHASE_Oil:
|
|
|
m_pCurrentModelCombo->addItem(solverModelDisplayName(SMT_Oil_ConstPvt), SMT_Oil_ConstPvt);
|
|
|
m_pCurrentModelCombo->addItem(solverModelDisplayName(SMT_Oil_VariablePvt), SMT_Oil_VariablePvt);
|
|
|
break;
|
|
|
case PHASE_Water:
|
|
|
m_pCurrentModelCombo->addItem(solverModelDisplayName(SMT_Water_ConstPvt), SMT_Water_ConstPvt);
|
|
|
m_pCurrentModelCombo->addItem(solverModelDisplayName(SMT_Water_VariablePvt), SMT_Water_VariablePvt);
|
|
|
break;
|
|
|
case PHASE_Gas:
|
|
|
m_pCurrentModelCombo->addItem(solverModelDisplayName(SMT_Gas_VariablePvt), SMT_Gas_VariablePvt);
|
|
|
break;
|
|
|
case PHASE_Oil_Water:
|
|
|
m_pCurrentModelCombo->addItem(solverModelDisplayName(SMT_Oil_Water_TwoPhase), SMT_Oil_Water_TwoPhase);
|
|
|
break;
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
int nIndex = m_pCurrentModelCombo->findData(eCurrentType);
|
|
|
if(nIndex >= 0) {
|
|
|
m_pCurrentModelCombo->setCurrentIndex(nIndex);
|
|
|
}
|
|
|
|
|
|
m_pCurrentModelCombo->setEnabled(true);
|
|
|
m_bFillingCurrentModelCombo = false;
|
|
|
}
|
|
|
|
|
|
// 按最后成功快照刷新结果井下拉框
|
|
|
void nmWxNumericalDesign::fillResultWellCombo(
|
|
|
const QString& sSelectedWellInstanceId)
|
|
|
{
|
|
|
if(m_pResultWellCombo == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
m_bFillingResultWellCombo = true;
|
|
|
m_pResultWellCombo->clear();
|
|
|
|
|
|
if(pManager == nullptr) {
|
|
|
m_pResultWellCombo->setEnabled(false);
|
|
|
m_bFillingResultWellCombo = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QSharedPointer<const nmPebiResultSnapshot> pSnapshot =
|
|
|
pManager->getPebiResultSnapshot();
|
|
|
if(pSnapshot.isNull()) {
|
|
|
m_pResultWellCombo->setEnabled(false);
|
|
|
m_bFillingResultWellCombo = false;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 名称和顺序始终来自求解时快照;实时 UUID 不存在时只增加删除标记。
|
|
|
const QStringList& listWellIds =
|
|
|
pSnapshot->getDisplayWellInstanceIds();
|
|
|
for(int nWellIndex = 0; nWellIndex < listWellIds.size(); ++nWellIndex) {
|
|
|
const QString& sWellInstanceId = listWellIds[nWellIndex];
|
|
|
const nmPebiResultWellSnapshot* pWell =
|
|
|
pSnapshot->findWell(sWellInstanceId);
|
|
|
if(pWell == nullptr) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
QString sDisplayName = pWell->m_sWellName;
|
|
|
if(pManager->findWellByInstanceId(sWellInstanceId) == nullptr) {
|
|
|
sDisplayName = tr("%1 (Deleted)").arg(sDisplayName);
|
|
|
}
|
|
|
m_pResultWellCombo->addItem(sDisplayName, sWellInstanceId);
|
|
|
}
|
|
|
|
|
|
QString sCurrentWellInstanceId = sSelectedWellInstanceId;
|
|
|
if(sCurrentWellInstanceId.isEmpty()) {
|
|
|
sCurrentWellInstanceId =
|
|
|
pManager->getCurrentResultWellInstanceId();
|
|
|
}
|
|
|
|
|
|
int nIndex = m_pResultWellCombo->findData(sCurrentWellInstanceId);
|
|
|
if(nIndex < 0 && m_pResultWellCombo->count() > 0) {
|
|
|
nIndex = 0;
|
|
|
}
|
|
|
|
|
|
if(nIndex >= 0) {
|
|
|
m_pResultWellCombo->setCurrentIndex(nIndex);
|
|
|
pManager->setCurrentResultWellInstanceId(
|
|
|
m_pResultWellCombo->itemData(nIndex).toString());
|
|
|
}
|
|
|
|
|
|
m_pResultWellCombo->setEnabled(m_pResultWellCombo->count() > 1);
|
|
|
m_bFillingResultWellCombo = false;
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::initMainOptionsGroup()
|
|
|
{
|
|
|
m_pMainOptionsGroup = new QGroupBox(tr("Main options"), this);
|
|
|
QGridLayout* pGridLayout = new QGridLayout(m_pMainOptionsGroup); // 使用 QGridLayout
|
|
|
pGridLayout->setContentsMargins(8, 15, 8, 10);
|
|
|
pGridLayout->setHorizontalSpacing(6);
|
|
|
pGridLayout->setVerticalSpacing(6);
|
|
|
|
|
|
// 第一行: Include other wells + Gear Icon (左侧), Reset from diagnostic (右侧)
|
|
|
m_pIncludeOtherWellsCheck = new QCheckBox(tr("Include other wells"), this);
|
|
|
if(m_pDataManager != nullptr) {
|
|
|
m_pIncludeOtherWellsCheck->setChecked(
|
|
|
m_pDataManager->getIncludeOtherWells());
|
|
|
}
|
|
|
m_pOptionsGearButton = new QPushButton(this);
|
|
|
m_pOptionsGearButton->setIcon(QIcon(m_sIconDir + "NmAnalDesign1.png"));
|
|
|
m_pOptionsGearButton->setIconSize(QSize(20, 20));
|
|
|
m_pOptionsGearButton->setFixedSize(22, 22);
|
|
|
m_pOptionsGearButton->setEnabled(m_pIncludeOtherWellsCheck->isChecked());
|
|
|
|
|
|
m_pResetDiagnosticButton = new QPushButton(tr("Reset from diagnostic"), this);
|
|
|
m_pResetDiagnosticButton->setFixedHeight(nmParameterField::editorHeight());
|
|
|
m_pResetDiagnosticButton->setSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
|
|
|
|
// 为左侧的复选框和齿轮创建一个QHBoxLayout,以便它们可以紧密排列
|
|
|
QHBoxLayout* pIncludeWellsLayout = new QHBoxLayout();
|
|
|
pIncludeWellsLayout->addWidget(m_pIncludeOtherWellsCheck);
|
|
|
pIncludeWellsLayout->addWidget(m_pOptionsGearButton);
|
|
|
pIncludeWellsLayout->addStretch(); // 将它们推到左侧
|
|
|
|
|
|
pGridLayout->addLayout(pIncludeWellsLayout, 0, 0, Qt::AlignLeft); // 第0行,第0列,左对齐
|
|
|
pGridLayout->addWidget(m_pResetDiagnosticButton, 0, 1);
|
|
|
|
|
|
// 第二行: Impose pi (左侧), Reset from analytical (右侧)
|
|
|
m_pImposePiCheck = new QCheckBox(tr("Impose pi"), this);
|
|
|
m_pImposePiCheck->setChecked(true); // 默认选中
|
|
|
|
|
|
m_pResetAnalyticalButton = new QPushButton(tr("Reset from analytical"), this);
|
|
|
m_pResetAnalyticalButton->setFixedHeight(nmParameterField::editorHeight());
|
|
|
m_pResetAnalyticalButton->setSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
|
|
|
|
pGridLayout->addWidget(m_pImposePiCheck, 1, 0, Qt::AlignLeft); // 第1行,第0列,左对齐
|
|
|
pGridLayout->addWidget(m_pResetAnalyticalButton, 1, 1);
|
|
|
|
|
|
// 第三行: Show average pressure (左侧), 右侧留空
|
|
|
m_pShowAveragePressureCheck = new QCheckBox(tr("Show average pressure"), this);
|
|
|
m_pShowAveragePressureCheck->setChecked(false); // 默认未选中
|
|
|
|
|
|
pGridLayout->addWidget(m_pShowAveragePressureCheck, 2, 0, Qt::AlignLeft); // 第2行,第0列,左对齐
|
|
|
pGridLayout->setColumnStretch(0, 0);
|
|
|
pGridLayout->setColumnStretch(1, 1);
|
|
|
|
|
|
m_pMainOptionsGroup->setLayout(pGridLayout); // 设置分组框布局
|
|
|
m_pMainOptionsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::initOutputGroup()
|
|
|
{
|
|
|
m_pOutputGroup = new QGroupBox(tr("Output"), this);
|
|
|
QGridLayout* pGridLayout = new QGridLayout(m_pOutputGroup);
|
|
|
|
|
|
// 设置网格的边距和间距,确保内容不会太贴近边缘
|
|
|
pGridLayout->setContentsMargins(10, 10, 10, 10); // 调整内边距
|
|
|
pGridLayout->setHorizontalSpacing(10); // 水平间距
|
|
|
pGridLayout->setVerticalSpacing(5); // 垂直间距
|
|
|
|
|
|
// 第一行: Output result fields (复选框) + Automatic (单选按钮)
|
|
|
m_pOutputResultFieldsCheck = new QCheckBox(tr("Output result fields"), this);
|
|
|
m_pOutputResultFieldsCheck->setChecked(true); // 默认选中
|
|
|
|
|
|
m_pOutputAutomaticRadio = new QRadioButton(tr("Automatic"), this);
|
|
|
m_pOutputAutomaticRadio->setChecked(true); // 默认选中Automatic
|
|
|
|
|
|
// 根据 m_pOutputResultFieldsCheck 的初始状态设置单选按钮的初始启用状态
|
|
|
bool isOutputChecked = m_pOutputResultFieldsCheck->isChecked();
|
|
|
m_pOutputAutomaticRadio->setEnabled(isOutputChecked); //
|
|
|
|
|
|
// 将 Output result fields 放在第0行,第0列
|
|
|
pGridLayout->addWidget(m_pOutputResultFieldsCheck, 0, 0, Qt::AlignLeft);
|
|
|
// 将 Automatic 放在第0行,第1列
|
|
|
pGridLayout->addWidget(m_pOutputAutomaticRadio, 0, 1, Qt::AlignLeft);
|
|
|
|
|
|
// 第二行: Manual (单选按钮) 和 齿轮图标,挨在一起,并且整体在右边
|
|
|
m_pOutputManualRadio = new QRadioButton(tr("Manual"), this);
|
|
|
m_pOutputManualIconButton = new QPushButton(this);
|
|
|
m_pOutputManualIconButton->setIcon(QIcon(m_sIconDir + "NmAnalDesign1.png")); // 确认图标路径正确
|
|
|
m_pOutputManualIconButton->setIconSize(QSize(20, 20));
|
|
|
m_pOutputManualIconButton->setFixedSize(22, 22);
|
|
|
// m_pOutputManualIconButton 的初始启用状态将由两个条件决定:
|
|
|
// 1. m_pOutputResultFieldsCheck 是否被勾选
|
|
|
// 2. m_pOutputManualRadio 是否被选中
|
|
|
// 这里先根据 m_pOutputResultFieldsCheck 设置一个初始值,后续连接会完善逻辑
|
|
|
m_pOutputManualIconButton->setEnabled(isOutputChecked && m_pOutputManualRadio->isChecked()); //
|
|
|
|
|
|
// 将 Manual 和 齿轮图标放在一个 QHBoxLayout 中,让它们挨在一起
|
|
|
QHBoxLayout* pManualAndIconButtonLayout = new QHBoxLayout();
|
|
|
pManualAndIconButtonLayout->addWidget(m_pOutputManualRadio);
|
|
|
pManualAndIconButtonLayout->addWidget(m_pOutputManualIconButton);
|
|
|
pManualAndIconButtonLayout->addStretch(); // 使它们紧靠在一起,并推到该布局的左侧
|
|
|
|
|
|
// 将包含 Manual 和图标的 QHBoxLayout 放在第1行,第1列,并右对齐
|
|
|
pGridLayout->addLayout(pManualAndIconButtonLayout, 1, 1, Qt::AlignRight);
|
|
|
|
|
|
// 第三行: Output well drainage results (复选框)
|
|
|
m_pOutputWellDrainageCheck = new QCheckBox(tr("Output well drainage results"), this);
|
|
|
m_pOutputWellDrainageCheck->setChecked(false); // 默认未选中
|
|
|
m_pOutputWellDrainageCheck->setEnabled(isOutputChecked); // 初始状态也受主复选框控制
|
|
|
|
|
|
// 跨越所有列(0到1,共2列),左对齐
|
|
|
pGridLayout->addWidget(m_pOutputWellDrainageCheck, 2, 0, 1, 2, Qt::AlignLeft);
|
|
|
|
|
|
// 设置列拉伸:
|
|
|
// 第0列 (Output result fields): 不拉伸,只占其内容宽度
|
|
|
pGridLayout->setColumnStretch(0, 0);
|
|
|
// 第1列 (Automatic / Manual & Icon group): 拉伸,以提供足够的空间并将右侧的 Manual & Icon group 推到右边
|
|
|
pGridLayout->setColumnStretch(1, 1);
|
|
|
|
|
|
m_pOutputGroup->setLayout(pGridLayout);
|
|
|
m_pOutputGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::initAdvancedGroup()
|
|
|
{
|
|
|
m_pAdvancedGroup = new QGroupBox(tr("Advanced"), this); // 创建高级分组框
|
|
|
QVBoxLayout* pMainVLayout = new QVBoxLayout(m_pAdvancedGroup); // 高级组的垂直布局
|
|
|
|
|
|
// 时间相关复选框和关联的时钟图标按钮
|
|
|
QHBoxLayout* pTimeDependentLayout = new QHBoxLayout(); // 为时间相关复选框和图标创建水平布局
|
|
|
m_pTimeDependentCheck = new QCheckBox(tr("Time dependent"), this); // "时间相关"复选框
|
|
|
m_pTimeDependentCheck->setChecked(false); // 默认未选中
|
|
|
|
|
|
m_pTimeDependentIconButton = new QPushButton(this); // 时钟图标按钮
|
|
|
m_pTimeDependentIconButton->setIcon(QIcon(m_sIconDir + "NmAnalDesign3.png")); // 使用时钟图标
|
|
|
m_pTimeDependentIconButton->setIconSize(QSize(20, 20));
|
|
|
m_pTimeDependentIconButton->setFixedSize(22, 22);
|
|
|
m_pTimeDependentIconButton->setEnabled(false); // 默认禁用,根据复选框状态启用
|
|
|
|
|
|
pTimeDependentLayout->addWidget(m_pTimeDependentCheck); // 添加复选框
|
|
|
pTimeDependentLayout->addWidget(m_pTimeDependentIconButton); // 添加图标按钮
|
|
|
pTimeDependentLayout->addStretch(); // 添加拉伸器,将它们推到左侧
|
|
|
|
|
|
pMainVLayout->addLayout(pTimeDependentLayout); // 将这个水平布局添加到主垂直布局
|
|
|
|
|
|
m_pUseWellIntakeCheck = new QCheckBox(tr("Use well intake"), this); // "使用井筒进水"复选框
|
|
|
m_pUseWellIntakeCheck->setChecked(false); // 默认未选中
|
|
|
pMainVLayout->addWidget(m_pUseWellIntakeCheck); // 添加到主垂直布局
|
|
|
|
|
|
QHBoxLayout* pRedefineKrPcLayout = new QHBoxLayout(); // "重新定义 KrPc"的水平布局
|
|
|
m_pRedefineKrPcCheck = new QCheckBox(tr("Redefine KrPc in hydraulic fractures"), this); // 复选框
|
|
|
m_pRedefineKrPcIconButton = new QPushButton(this); // 图标按钮
|
|
|
m_pRedefineKrPcIconButton->setIcon(QIcon(m_sIconDir + "KK.png")); // 根据图片,这个图标应该是曲线图
|
|
|
m_pRedefineKrPcIconButton->setIconSize(QSize(40, 40));
|
|
|
m_pRedefineKrPcIconButton->setFixedSize(42, 42);
|
|
|
m_pRedefineKrPcIconButton->setEnabled(false); // 默认禁用
|
|
|
pRedefineKrPcLayout->addWidget(m_pRedefineKrPcCheck);
|
|
|
pRedefineKrPcLayout->addWidget(m_pRedefineKrPcIconButton);
|
|
|
pRedefineKrPcLayout->addStretch(); // 添加拉伸器
|
|
|
pMainVLayout->addLayout(pRedefineKrPcLayout); // 添加到主垂直布局
|
|
|
|
|
|
QHBoxLayout* pApplyUnconsolidationLayout = new QHBoxLayout(); // "应用未固结"的水平布局
|
|
|
m_pApplyUnconsolidationCheck = new QCheckBox(tr("Apply unconsolidation in hydraulic fractures"), this); // 复选框
|
|
|
m_pApplyUnconsolidationIconButton = new QPushButton(this); // 图标按钮
|
|
|
m_pApplyUnconsolidationIconButton->setIcon(QIcon(m_sIconDir + "SS.png")); // 根据图片,这个图标应该是方块压缩
|
|
|
m_pApplyUnconsolidationIconButton->setIconSize(QSize(47, 42));
|
|
|
m_pApplyUnconsolidationIconButton->setFixedSize(49, 44);
|
|
|
m_pApplyUnconsolidationIconButton->setEnabled(false); // 默认禁用
|
|
|
pApplyUnconsolidationLayout->addWidget(m_pApplyUnconsolidationCheck);
|
|
|
pApplyUnconsolidationLayout->addWidget(m_pApplyUnconsolidationIconButton);
|
|
|
pApplyUnconsolidationLayout->addStretch(); // 添加拉伸器
|
|
|
pMainVLayout->addLayout(pApplyUnconsolidationLayout); // 添加到主垂直布局
|
|
|
|
|
|
m_pUseDFNCheck = new QCheckBox(tr("Use DFN"), this); // "使用 DFN"复选框
|
|
|
m_pUseDFNCheck->setChecked(false); // 默认未选中
|
|
|
pMainVLayout->addWidget(m_pUseDFNCheck); // 添加到主垂直布局
|
|
|
|
|
|
m_pAdvancedGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); // 设置尺寸策略
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::initTimeSteppingGroup()
|
|
|
{
|
|
|
m_pTimeSteppingGroup = new QGroupBox(tr("Time stepping"), this);
|
|
|
QGridLayout* pFieldLayout = new QGridLayout(m_pTimeSteppingGroup);
|
|
|
pFieldLayout->setContentsMargins(8, 15, 8, 10);
|
|
|
pFieldLayout->setHorizontalSpacing(6);
|
|
|
pFieldLayout->setVerticalSpacing(6);
|
|
|
pFieldLayout->setColumnStretch(0, 0);
|
|
|
pFieldLayout->setColumnStretch(1, 8);
|
|
|
pFieldLayout->setColumnStretch(2, 5);
|
|
|
pFieldLayout->setColumnMinimumWidth(
|
|
|
1, nmParameterField::editorMinimumWidth());
|
|
|
pFieldLayout->setColumnMinimumWidth(
|
|
|
2, nmParameterField::unitMinimumWidth());
|
|
|
|
|
|
// 第一步:按稳定参数ID创建名称、数值和单位三列字段。
|
|
|
m_pTimeGrowthExponentField = new nmParameterField(
|
|
|
createTimeStepFieldInfo(s_pTimeGrowthExponentId, false),
|
|
|
this,
|
|
|
m_pTimeSteppingGroup);
|
|
|
m_pDtMinField = new nmParameterField(
|
|
|
createTimeStepFieldInfo(s_pDtMinId, true),
|
|
|
this,
|
|
|
m_pTimeSteppingGroup);
|
|
|
m_pDtMaxField = new nmParameterField(
|
|
|
createTimeStepFieldInfo(s_pDtMaxId, true),
|
|
|
this,
|
|
|
m_pTimeSteppingGroup);
|
|
|
|
|
|
// 第二步:逐项接入NM参数定义和框架单位绑定器。
|
|
|
QString sError;
|
|
|
bool bInitialized =
|
|
|
m_pTimeGrowthExponentField->initialize(
|
|
|
pFieldLayout, 0, 0, sError) &&
|
|
|
m_pDtMinField->initialize(
|
|
|
pFieldLayout, 1, 0, sError) &&
|
|
|
m_pDtMaxField->initialize(
|
|
|
pFieldLayout, 2, 0, sError);
|
|
|
if(!bInitialized) {
|
|
|
m_pTimeSteppingGroup->setEnabled(false);
|
|
|
m_pTimeSteppingGroup->setToolTip(sError);
|
|
|
}
|
|
|
|
|
|
m_pTimeGrowthExponentField->setValueRightAligned(true);
|
|
|
m_pDtMinField->setValueRightAligned(true);
|
|
|
m_pDtMaxField->setValueRightAligned(true);
|
|
|
m_pTimeSteppingGroup->setSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Preferred);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::initNumericalSettingsGroup()
|
|
|
{
|
|
|
m_pNumericalSettingsGroup = new QGroupBox(tr("Numerical settings"), this); // 创建数值设置分组框
|
|
|
QVBoxLayout* pMainVLayout = new QVBoxLayout(m_pNumericalSettingsGroup); // 主垂直布局,用于放置子布局
|
|
|
|
|
|
// 第一行:Use automatic settings 单选按钮
|
|
|
// 直接添加到主垂直布局
|
|
|
m_pUseAutomaticSettingsRadio = new QRadioButton(tr("Use automatic settings"), this); // "使用自动设置"单选按钮
|
|
|
m_pUseAutomaticSettingsRadio->setChecked(true); // 默认选中"使用自动设置"
|
|
|
pMainVLayout->addWidget(m_pUseAutomaticSettingsRadio);
|
|
|
|
|
|
// 第二行:Use custom settings 单选按钮 + 齿轮图标
|
|
|
QHBoxLayout* pCustomSettingsHLayout = new QHBoxLayout(); // 为第二行创建一个水平布局
|
|
|
|
|
|
m_pUseCustomSettingsRadio = new QRadioButton(tr("Use custom settings"), this); // "使用自定义设置"单选按钮
|
|
|
m_pCustomSettingsIconButton = new QPushButton(this); // 自定义设置旁边的图标按钮
|
|
|
m_pCustomSettingsIconButton->setIcon(QIcon(m_sIconDir + "NmAnalDesign1.png")); // 占位符图标
|
|
|
m_pCustomSettingsIconButton->setIconSize(QSize(20, 20));
|
|
|
m_pCustomSettingsIconButton->setFixedSize(22, 22);
|
|
|
m_pCustomSettingsIconButton->setEnabled(false); // 默认禁用,当选择"使用自定义设置"时启用
|
|
|
|
|
|
// 这个值可以根据实际显示效果调整
|
|
|
pCustomSettingsHLayout->addWidget(m_pUseCustomSettingsRadio);
|
|
|
pCustomSettingsHLayout->addWidget(m_pCustomSettingsIconButton);
|
|
|
pCustomSettingsHLayout->addStretch(); // 添加拉伸器,确保按钮和图标靠左对齐
|
|
|
|
|
|
pMainVLayout->addLayout(pCustomSettingsHLayout); // 将第二行的水平布局添加到主垂直布局
|
|
|
|
|
|
m_pNumericalSettingsGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); // 设置尺寸策略
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::initPebiSolverSettingsGroup()
|
|
|
{
|
|
|
m_pPebiSolverSettingsGroup = new QGroupBox(tr("Solver settings"), this);
|
|
|
QGridLayout* pGridLayout = new QGridLayout(m_pPebiSolverSettingsGroup);
|
|
|
pGridLayout->setContentsMargins(8, 15, 8, 10);
|
|
|
pGridLayout->setHorizontalSpacing(6);
|
|
|
pGridLayout->setVerticalSpacing(6);
|
|
|
|
|
|
QLabel* pSolverTypeLabel = new QLabel(tr("Solver library"), this);
|
|
|
QLabel* pOmpThreadsLabel = new QLabel(tr("OpenMP threads"), this);
|
|
|
QLabel* pIluReuseStepsLabel = new QLabel(tr("ILU reuse steps"), this);
|
|
|
pSolverTypeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
pOmpThreadsLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
pIluReuseStepsLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
|
|
|
|
|
m_pPebiSolverTypeCombo = new QComboBox(this);
|
|
|
m_pPebiSolverTypeCombo->addItem(tr("CPU accelerated solver"),
|
|
|
nmDataAnalyzeManager::PebiSolverCpuAccelerated);
|
|
|
m_pPebiSolverTypeCombo->addItem(tr("Original solver"),
|
|
|
nmDataAnalyzeManager::PebiSolverOriginal);
|
|
|
|
|
|
m_pPebiOmpThreadsCombo = new QComboBox(this);
|
|
|
m_pPebiOmpThreadsCombo->addItem("1", 1);
|
|
|
m_pPebiOmpThreadsCombo->addItem("2", 2);
|
|
|
m_pPebiOmpThreadsCombo->addItem("4", 4);
|
|
|
m_pPebiOmpThreadsCombo->addItem("8", 8);
|
|
|
m_pPebiOmpThreadsCombo->addItem("16", 16);
|
|
|
|
|
|
m_pPebiIluReuseStepsSpin = new QSpinBox(this);
|
|
|
m_pPebiIluReuseStepsSpin->setRange(1, 100);
|
|
|
|
|
|
m_pPebiSolverTypeCombo->setFixedHeight(nmParameterField::editorHeight());
|
|
|
m_pPebiOmpThreadsCombo->setFixedHeight(nmParameterField::editorHeight());
|
|
|
m_pPebiIluReuseStepsSpin->setFixedHeight(nmParameterField::editorHeight());
|
|
|
m_pPebiSolverTypeCombo->setMinimumWidth(
|
|
|
nmParameterField::editorMinimumWidth());
|
|
|
m_pPebiOmpThreadsCombo->setMinimumWidth(
|
|
|
nmParameterField::editorMinimumWidth());
|
|
|
m_pPebiIluReuseStepsSpin->setMinimumWidth(
|
|
|
nmParameterField::editorMinimumWidth());
|
|
|
m_pPebiSolverTypeCombo->setSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
|
m_pPebiOmpThreadsCombo->setSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
|
m_pPebiIluReuseStepsSpin->setSizePolicy(
|
|
|
QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
|
|
|
|
pGridLayout->addWidget(pSolverTypeLabel, 0, 0);
|
|
|
pGridLayout->addWidget(m_pPebiSolverTypeCombo, 0, 1);
|
|
|
pGridLayout->addWidget(pOmpThreadsLabel, 1, 0);
|
|
|
pGridLayout->addWidget(m_pPebiOmpThreadsCombo, 1, 1);
|
|
|
pGridLayout->addWidget(pIluReuseStepsLabel, 2, 0);
|
|
|
pGridLayout->addWidget(m_pPebiIluReuseStepsSpin, 2, 1);
|
|
|
pGridLayout->setColumnStretch(0, 0);
|
|
|
pGridLayout->setColumnStretch(1, 1);
|
|
|
|
|
|
m_pPebiSolverSettingsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::updatePebiSolverSettingEnabled()
|
|
|
{
|
|
|
bool bAccelerationEnabled = m_pPebiSolverTypeCombo->itemData(
|
|
|
m_pPebiSolverTypeCombo->currentIndex()).toInt()
|
|
|
== nmDataAnalyzeManager::PebiSolverCpuAccelerated;
|
|
|
m_pPebiOmpThreadsCombo->setEnabled(bAccelerationEnabled);
|
|
|
m_pPebiIluReuseStepsSpin->setEnabled(bAccelerationEnabled);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::updatePebiSolverSettingsToData()
|
|
|
{
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
if(pManager == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
pManager->setPebiSolverType(m_pPebiSolverTypeCombo->itemData(
|
|
|
m_pPebiSolverTypeCombo->currentIndex()).toInt());
|
|
|
pManager->setPebiOmpThreads(m_pPebiOmpThreadsCombo->itemData(
|
|
|
m_pPebiOmpThreadsCombo->currentIndex()).toInt());
|
|
|
pManager->setPebiIluReuseSteps(m_pPebiIluReuseStepsSpin->value());
|
|
|
}
|
|
|
|
|
|
|
|
|
void nmWxNumericalDesign::initButton()
|
|
|
{
|
|
|
// 创建自动拟合按钮
|
|
|
m_pAutomaticfittingBtn = new QPushButton(tr("Automatic fitting"), this);
|
|
|
m_pAutomaticfittingBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
|
// 创建预测按钮
|
|
|
m_pForecastBtn = new QPushButton(tr("Forecast"), this);
|
|
|
m_pForecastBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
|
// 创建敏感性分析按钮
|
|
|
m_pSensitiveBtn = new QPushButton(tr("Sensitive"), this);
|
|
|
m_pSensitiveBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
|
|
// 创建右下角按钮
|
|
|
m_pGenerateButton = new QPushButton(tr("Generate"), this); // "生成"按钮
|
|
|
m_pGenerateButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // 固定尺寸
|
|
|
|
|
|
// 创建按钮布局
|
|
|
m_pGenerateLayout = new QHBoxLayout(); // 水平布局
|
|
|
m_pGenerateLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); // 左侧添加拉伸器
|
|
|
m_pGenerateLayout->addWidget(m_pAutomaticfittingBtn);
|
|
|
m_pGenerateLayout->addSpacing(10);
|
|
|
m_pGenerateLayout->addWidget(m_pForecastBtn);
|
|
|
m_pGenerateLayout->addSpacing(10);
|
|
|
m_pGenerateLayout->addWidget(m_pSensitiveBtn);
|
|
|
m_pGenerateLayout->addSpacing(10);
|
|
|
m_pGenerateLayout->addWidget(m_pGenerateButton);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::setupConnections()
|
|
|
{
|
|
|
// 连接生成按钮的点击信号到槽函数
|
|
|
connect(m_pGenerateButton, SIGNAL(clicked()), this, SLOT(onGenerateClicked()));
|
|
|
connect(m_pResultWellCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onResultWellChanged(int)));
|
|
|
connect(m_pCurrentModelCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentModelChanged(int)));
|
|
|
|
|
|
// 主选项组连接
|
|
|
// 连接齿轮图标按钮的槽函数
|
|
|
connect(m_pOptionsGearButton, SIGNAL(clicked()), this, SLOT(onOptionsIconClicked()));
|
|
|
connect(m_pAutomaticfittingBtn, SIGNAL(clicked()), this, SLOT(onAutomaticFittingClicked()));
|
|
|
connect(m_pForecastBtn, SIGNAL(clicked()), this, SLOT(onForecastClicked()));
|
|
|
connect(m_pSensitiveBtn, SIGNAL(clicked()), this, SLOT(onSensitiveClicked()));
|
|
|
// 更新齿轮图标按钮的启用状态
|
|
|
connect(m_pIncludeOtherWellsCheck, SIGNAL(toggled(bool)), m_pOptionsGearButton, SLOT(setEnabled(bool)));
|
|
|
connect(m_pIncludeOtherWellsCheck, SIGNAL(toggled(bool)),
|
|
|
this, SLOT(onIncludeOtherWellsToggled(bool)));
|
|
|
// Map井增删或产量、压力目标变化后,立即更新多井选项是否可用。
|
|
|
if(m_pDataManager != nullptr) {
|
|
|
connect(m_pDataManager, SIGNAL(dataChanged()),
|
|
|
this, SLOT(onWellCollectionChanged()), Qt::UniqueConnection);
|
|
|
// createWell() 会在井对象登记时先发送 dataChanged(),此时 WellCode、
|
|
|
// 坐标和历史流量可能尚未填充。sigWellAdded 表示井已完整建立,必须再次
|
|
|
// 刷新 Include Other Wells,才能正确识别刚加入 Map 的有产量井。
|
|
|
connect(m_pDataManager,
|
|
|
SIGNAL(sigWellAdded(QString,QString,QStringList)),
|
|
|
this, SLOT(onWellCollectionChanged()), Qt::UniqueConnection);
|
|
|
}
|
|
|
connect(m_pResetDiagnosticButton, SIGNAL(clicked()), this, SLOT(onResetFromDiagnosticClicked())); // "从诊断复位"按钮连接
|
|
|
connect(m_pResetAnalyticalButton, SIGNAL(clicked()), this, SLOT(onResetFromAnalyticalClicked())); // "从解析复位"按钮连接
|
|
|
|
|
|
// 输出组连接
|
|
|
// 新增连接:m_pOutputResultFieldsCheck 控制其他相关组件的启用/禁用
|
|
|
connect(m_pOutputResultFieldsCheck, SIGNAL(toggled(bool)), this, SLOT(onOutputResultFieldsToggled(bool))); //
|
|
|
connect(m_pOutputAutomaticRadio, SIGNAL(toggled(bool)), this, SLOT(onOutputResultFieldsAutomaticToggled(bool))); // "自动"单选按钮连接
|
|
|
connect(m_pOutputManualRadio, SIGNAL(toggled(bool)), this, SLOT(onOutputResultFieldsManualToggled(bool))); // "手动"单选按钮连接
|
|
|
connect(m_pOutputManualIconButton, SIGNAL(clicked()), this, SLOT(onManualOutputSettingsClicked())); // "手动"图标按钮连接
|
|
|
|
|
|
// 高级组连接
|
|
|
// 在 setupConnections() 中连接 pTimeDependentIconButton 的信号槽,或者在这里连接
|
|
|
connect(m_pTimeDependentCheck, SIGNAL(toggled(bool)), m_pTimeDependentIconButton, SLOT(setEnabled(bool)));
|
|
|
connect(m_pTimeDependentCheck, SIGNAL(toggled(bool)), this, SLOT(onTimeDependentCheck(bool)));
|
|
|
// 如果这个图标按钮有自己的点击逻辑,需要连接到槽函数
|
|
|
connect(m_pTimeDependentIconButton, SIGNAL(clicked()), this, SLOT(onTimeIconClicked()));
|
|
|
// "重新定义 KrPc"图标按钮的启用状态取决于复选框
|
|
|
connect(m_pRedefineKrPcCheck, SIGNAL(toggled(bool)), m_pRedefineKrPcIconButton, SLOT(setEnabled(bool)));
|
|
|
connect(m_pRedefineKrPcIconButton, SIGNAL(clicked()), this, SLOT(onRedefineKrPcIconClicked())); // 图标按钮点击连接
|
|
|
// "应用未固结"图标按钮的启用状态取决于复选框
|
|
|
connect(m_pApplyUnconsolidationCheck, SIGNAL(toggled(bool)), m_pApplyUnconsolidationIconButton, SLOT(setEnabled(bool)));
|
|
|
connect(m_pApplyUnconsolidationIconButton, SIGNAL(clicked()), this, SLOT(onApplyUnconsolidationIconClicked())); // 图标按钮点击连接
|
|
|
|
|
|
// 时间步字段输入过程中只刷新校验,编辑完成后再整体提交。
|
|
|
QList<nmParameterField*> listTimeStepFields;
|
|
|
listTimeStepFields << m_pTimeGrowthExponentField
|
|
|
<< m_pDtMinField
|
|
|
<< m_pDtMaxField;
|
|
|
for(int nIndex = 0; nIndex < listTimeStepFields.size(); ++nIndex) {
|
|
|
QLineEdit* pLineEdit = listTimeStepFields[nIndex] != nullptr
|
|
|
? listTimeStepFields[nIndex]->valueEditor() : nullptr;
|
|
|
if(pLineEdit == nullptr) {
|
|
|
continue;
|
|
|
}
|
|
|
connect(pLineEdit, SIGNAL(textChanged(QString)),
|
|
|
this, SLOT(onTimeStepValueChanged(QString)));
|
|
|
connect(pLineEdit, SIGNAL(editingFinished()),
|
|
|
this, SLOT(onTimeStepEditingFinished()));
|
|
|
}
|
|
|
// 数值设置组连接
|
|
|
// "自定义设置"图标按钮的启用状态取决于"使用自定义设置"单选按钮
|
|
|
connect(m_pUseCustomSettingsRadio, SIGNAL(toggled(bool)), m_pCustomSettingsIconButton, SLOT(setEnabled(bool)));
|
|
|
connect(m_pCustomSettingsIconButton, SIGNAL(clicked()), this, SLOT(onCustomNumericalSettingsClicked())); // 图标按钮点击连接
|
|
|
|
|
|
// PEBI求解器设置组连接
|
|
|
connect(m_pPebiSolverTypeCombo, SIGNAL(currentIndexChanged(int)),
|
|
|
this, SLOT(onPebiSolverTypeChanged(int)));
|
|
|
connect(m_pPebiOmpThreadsCombo, SIGNAL(currentIndexChanged(int)),
|
|
|
this, SLOT(onPebiSolverSettingsChanged()));
|
|
|
connect(m_pPebiIluReuseStepsSpin, SIGNAL(valueChanged(int)),
|
|
|
this, SLOT(onPebiSolverSettingsChanged()));
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onPebiSolverTypeChanged(int index)
|
|
|
{
|
|
|
if(index < 0) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
updatePebiSolverSettingEnabled();
|
|
|
updatePebiSolverSettingsToData();
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onPebiSolverSettingsChanged()
|
|
|
{
|
|
|
updatePebiSolverSettingsToData();
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onTimeStepValueChanged(const QString& sText)
|
|
|
{
|
|
|
Q_UNUSED(sText);
|
|
|
double dTimeGrowthExponent = 0.0;
|
|
|
double dDtMin = 0.0;
|
|
|
double dDtMax = 0.0;
|
|
|
QString sError;
|
|
|
collectTimeStepValues(dTimeGrowthExponent,
|
|
|
dDtMin,
|
|
|
dDtMax,
|
|
|
sError);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onTimeStepEditingFinished()
|
|
|
{
|
|
|
QString sError;
|
|
|
if(!updateDataFromUi(sError)) {
|
|
|
focusFirstInvalidTimeStepField();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onTimeDependentCheck(bool checked)
|
|
|
{
|
|
|
// 更新当前井的时间变表皮状态
|
|
|
nmDataWellBase* pCurrentWell = m_pDataManager != nullptr
|
|
|
? m_pDataManager->getCurWellData() : nullptr;
|
|
|
|
|
|
if(pCurrentWell) {
|
|
|
pCurrentWell->setTimeDependentSkin(checked);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 切换查看井后同步当前井,并刷新结果曲线
|
|
|
void nmWxNumericalDesign::onResultWellChanged(int index)
|
|
|
{
|
|
|
if(m_bFillingResultWellCombo || index < 0 || m_pResultWellCombo == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const QString sWellInstanceId =
|
|
|
m_pResultWellCombo->itemData(index).toString();
|
|
|
if(sWellInstanceId.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
if(pManager != nullptr) {
|
|
|
// 结果井切换只更新查看状态,不改变主分析井和参数编辑井。
|
|
|
pManager->setCurrentResultWellInstanceId(sWellInstanceId);
|
|
|
}
|
|
|
|
|
|
// 复用主窗口已有的井结果刷新逻辑
|
|
|
emit sigResultWellChanged(sWellInstanceId);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onIncludeOtherWellsToggled(bool bChecked)
|
|
|
{
|
|
|
if(m_pDataManager == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 总开关只控制有效计算井集合;已经勾选的井保留,重新打开时无需再次选择。
|
|
|
m_pDataManager->setIncludeOtherWells(bChecked);
|
|
|
fillResultWellCombo();
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onWellCollectionChanged()
|
|
|
{
|
|
|
// 这里只刷新多井入口,避免Map编辑时覆盖用户尚未提交的其他数值设置。
|
|
|
updateIncludeOtherWellsAvailability();
|
|
|
// 删除井不清除快照;重新填充后保留 UUID 选择并显示“已删除”状态。
|
|
|
fillResultWellCombo();
|
|
|
}
|
|
|
|
|
|
// 当前模型下拉框切换槽,将用户选择的模型类型同步到数据管理器
|
|
|
void nmWxNumericalDesign::onCurrentModelChanged(int index)
|
|
|
{
|
|
|
if(m_bFillingCurrentModelCombo || index < 0 || m_pCurrentModelCombo == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
NM_SOLVER_MODEL_TYPE eType = static_cast<NM_SOLVER_MODEL_TYPE>(
|
|
|
m_pCurrentModelCombo->itemData(index).toInt());
|
|
|
if(eType <= 0) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
if(pManager != nullptr) {
|
|
|
pManager->setSolverModelType(eType);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onGenerateClicked()
|
|
|
{
|
|
|
QString sError;
|
|
|
if(!updateDataFromUi(sError)) {
|
|
|
focusFirstInvalidTimeStepField();
|
|
|
QMessageBox::warning(this, tr("Invalid time step"), sError);
|
|
|
return;
|
|
|
}
|
|
|
emit sigGenerateClicked();
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onResetFromDiagnosticClicked()
|
|
|
{
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
if(!pManager) {
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("No data manager available."));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if(pManager->resetFromDiagnostic()) {
|
|
|
// 通知参数面板更新显示
|
|
|
nmWxParameterProperty::notifyUpdateTable();
|
|
|
} else {
|
|
|
QMessageBox::warning(this, tr("Warning"),
|
|
|
tr("Failed to reset from diagnostic. Please check if log-log data is available."));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onResetFromAnalyticalClicked()
|
|
|
{
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
if(!pManager) {
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("No data manager available."));
|
|
|
return;
|
|
|
}
|
|
|
if(pManager->resetFromAnalytical()) {
|
|
|
// 通知参数面板更新显示
|
|
|
nmWxParameterProperty::notifyUpdateTable();
|
|
|
} else {
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Failed to reset to analytical defaults."));
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onManualOutputSettingsClicked()
|
|
|
{
|
|
|
// 占位符:显示消息框
|
|
|
QMessageBox::information(this, tr("Output Settings"), tr("Manual output settings clicked."));
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onRedefineKrPcIconClicked()
|
|
|
{
|
|
|
// 占位符:显示消息框
|
|
|
QMessageBox::information(this, tr("Advanced Settings"), tr("Redefine KrPc settings clicked."));
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onApplyUnconsolidationIconClicked()
|
|
|
{
|
|
|
// 占位符:显示消息框
|
|
|
QMessageBox::information(this, tr("Advanced Settings"), tr("Apply unconsolidation settings clicked."));
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onCustomNumericalSettingsClicked()
|
|
|
{
|
|
|
// 占位符:显示消息框
|
|
|
QMessageBox::information(this, tr("Numerical Settings"), tr("Custom numerical settings clicked."));
|
|
|
}
|
|
|
|
|
|
// 针对高级组中的"时间相关"按钮的槽函数
|
|
|
void nmWxNumericalDesign::onTimeIconClicked()
|
|
|
{
|
|
|
nmWxTimeDependentSkin::showForCurrentWell();
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onOutputResultFieldsAutomaticToggled(bool checked)
|
|
|
{
|
|
|
// 当"自动"单选按钮被选中时,禁用手动输出设置按钮
|
|
|
if(checked) {
|
|
|
m_pOutputManualIconButton->setEnabled(false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onOutputResultFieldsManualToggled(bool checked)
|
|
|
{
|
|
|
// 当"手动"单选按钮被选中时,启用手动输出设置按钮
|
|
|
if(checked) {
|
|
|
m_pOutputManualIconButton->setEnabled(true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onOptionsIconClicked()
|
|
|
{
|
|
|
nmWxIncludeOtherWells dlg(m_pDataManager, this); // 创建"包含其他井"对话框实例
|
|
|
if(m_pDataManager == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if(dlg.exec() == QDialog::Accepted) {
|
|
|
QVector<WellDataRow> vecWellRows = dlg.getWellData();
|
|
|
QVector<nmCalculationWellRef> vecIncludedWells;
|
|
|
bool bIncludedMembershipChanged = false;
|
|
|
bool bIncludedCurveSelectionChanged = false;
|
|
|
|
|
|
// 第一步:先把每一行的压力、公共流量记录、分相开关和流动段写回。
|
|
|
// 这些状态属于井本身;未勾选井也保留配置,方便下次直接启用。
|
|
|
for(int nRowIndex = 0; nRowIndex < vecWellRows.size(); ++nRowIndex) {
|
|
|
const WellDataRow& oWellRow = vecWellRows[nRowIndex];
|
|
|
if(oWellRow.m_bIsPrimary) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
nmDataWellBase* pWellData =
|
|
|
m_pDataManager->findWellByCode(oWellRow.m_sWellCode);
|
|
|
if(pWellData == nullptr) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
const bool bWasIncluded =
|
|
|
m_pDataManager->getNumericalAnalysisCase()->isIncludedWell(
|
|
|
oWellRow.m_sWellCode);
|
|
|
const bool bWillBeIncluded = oWellRow.m_bIsIncluded &&
|
|
|
oWellRow.canCalculate();
|
|
|
bIncludedMembershipChanged = bIncludedMembershipChanged ||
|
|
|
bWasIncluded != bWillBeIncluded;
|
|
|
|
|
|
// 必须在写回前冻结差异;写回后井对象已无法提供旧的记录和分相状态。
|
|
|
const bool bCurveSelectionChanged =
|
|
|
pWellData->getSelectedPressureGaugeCode() !=
|
|
|
oWellRow.m_sPressureGaugeCode ||
|
|
|
pWellData->getSelectedFlowGaugeCode() !=
|
|
|
oWellRow.m_sFlowGaugeCode ||
|
|
|
pWellData->getUseOilRate() != oWellRow.m_bUseOilRate ||
|
|
|
pWellData->getUseGasRate() != oWellRow.m_bUseGasRate ||
|
|
|
pWellData->getUseWaterRate() != oWellRow.m_bUseWaterRate ||
|
|
|
pWellData->getIndexF() != oWellRow.m_nFlowSegmentIndex;
|
|
|
|
|
|
// 先切换公共流量记录,再写入分相开关和该记录的一基流动段索引。
|
|
|
// 这样 switchSelectedFlowGaugeCode() 可以先保存旧记录自己的段索引。
|
|
|
pWellData->selectPressureGaugeCode(
|
|
|
oWellRow.m_sPressureGaugeCode);
|
|
|
if(pWellData->getSelectedFlowGaugeCode() !=
|
|
|
oWellRow.m_sFlowGaugeCode) {
|
|
|
pWellData->switchSelectedFlowGaugeCode(
|
|
|
oWellRow.m_sFlowGaugeCode);
|
|
|
}
|
|
|
pWellData->setUseOilRate(oWellRow.m_bUseOilRate);
|
|
|
pWellData->setUseGasRate(oWellRow.m_bUseGasRate);
|
|
|
pWellData->setUseWaterRate(oWellRow.m_bUseWaterRate);
|
|
|
pWellData->setIndexF(oWellRow.m_nFlowSegmentIndex);
|
|
|
|
|
|
if(bCurveSelectionChanged) {
|
|
|
QVector<QVector<double> > vecHistoryPressure;
|
|
|
QVector<QVector<double> > vecHistoryLogLog;
|
|
|
QVector<QVector<double> > vecHistorySemiLog;
|
|
|
m_pDataManager->calculationLogData(pWellData,
|
|
|
vecHistoryPressure,
|
|
|
vecHistoryLogLog,
|
|
|
vecHistorySemiLog);
|
|
|
pWellData->setHistoryPressure(vecHistoryPressure);
|
|
|
pWellData->setHistoryLogLog(vecHistoryLogLog);
|
|
|
pWellData->setHistorySemiLog(vecHistorySemiLog);
|
|
|
if(bWasIncluded || bWillBeIncluded) {
|
|
|
bIncludedCurveSelectionChanged = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(bWillBeIncluded) {
|
|
|
vecIncludedWells.append(
|
|
|
nmCalculationWellRef(oWellRow.m_sWellCode,
|
|
|
NM_CaseWell_RateControlled));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 第二步:井集合改变会使网格和结果一起失效;仅更换曲线或分相时
|
|
|
// 保留现有网格,只使旧求解结果失效。
|
|
|
m_pDataManager->setIncludedCalculationWells(vecIncludedWells);
|
|
|
m_pDataManager->setIncludeOtherWells(
|
|
|
m_pIncludeOtherWellsCheck->isChecked());
|
|
|
if(bIncludedCurveSelectionChanged &&
|
|
|
!bIncludedMembershipChanged) {
|
|
|
m_pDataManager->invalidatePebiResults();
|
|
|
}
|
|
|
|
|
|
nmWxParameterProperty::notifyUpdateTable();
|
|
|
fillResultWellCombo();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onOutputResultFieldsToggled(bool checked)
|
|
|
{
|
|
|
// 根据主复选框的状态启用或禁用相关组件
|
|
|
m_pOutputAutomaticRadio->setEnabled(checked); //
|
|
|
m_pOutputManualRadio->setEnabled(checked); //
|
|
|
|
|
|
// 特别处理 m_pOutputManualIconButton:
|
|
|
// 它只有在 m_pOutputResultFieldsCheck 勾选 且 m_pOutputManualRadio 选中时才启用
|
|
|
if(checked && m_pOutputManualRadio->isChecked()) { //
|
|
|
m_pOutputManualIconButton->setEnabled(true); //
|
|
|
} else {
|
|
|
m_pOutputManualIconButton->setEnabled(false); //
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::updateUiFromData()
|
|
|
{
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
if(pManager == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 第一步:刷新与当前成果、模型和井相关的选择控件。
|
|
|
updateIncludeOtherWellsAvailability();
|
|
|
fillResultWellCombo();
|
|
|
updateCurrentModelDisplay();
|
|
|
updateTimeDependentSkinDisplay();
|
|
|
|
|
|
// 第二步:业务层始终保存小时基准值,单位绑定器负责转换显示。
|
|
|
nmDataTimeStepSetting* pTimeStepSetting = pManager->getTimeStep();
|
|
|
if(pTimeStepSetting != nullptr) {
|
|
|
m_pTimeGrowthExponentField->setBaseValue(
|
|
|
pTimeStepSetting->getTimeGrowthExponent().getValue());
|
|
|
m_pDtMinField->setBaseValue(
|
|
|
pTimeStepSetting->getMinDeltaTAttribute().getValue());
|
|
|
m_pDtMaxField->setBaseValue(
|
|
|
pTimeStepSetting->getMaxDeltaTAttribute().getValue());
|
|
|
}
|
|
|
|
|
|
// 第三步:回填PEBI设置时屏蔽即时提交信号。
|
|
|
m_pPebiSolverTypeCombo->blockSignals(true);
|
|
|
m_pPebiOmpThreadsCombo->blockSignals(true);
|
|
|
m_pPebiIluReuseStepsSpin->blockSignals(true);
|
|
|
|
|
|
int nSolverTypeIndex = m_pPebiSolverTypeCombo->findData(pManager->getPebiSolverType());
|
|
|
if(nSolverTypeIndex < 0) {
|
|
|
nSolverTypeIndex = m_pPebiSolverTypeCombo->findData(
|
|
|
nmDataAnalyzeManager::PebiSolverCpuAccelerated);
|
|
|
}
|
|
|
m_pPebiSolverTypeCombo->setCurrentIndex(nSolverTypeIndex);
|
|
|
|
|
|
int nOmpThreadsIndex = m_pPebiOmpThreadsCombo->findData(pManager->getPebiOmpThreads());
|
|
|
if(nOmpThreadsIndex < 0) {
|
|
|
nOmpThreadsIndex = m_pPebiOmpThreadsCombo->findData(4);
|
|
|
}
|
|
|
m_pPebiOmpThreadsCombo->setCurrentIndex(nOmpThreadsIndex);
|
|
|
m_pPebiIluReuseStepsSpin->setValue(pManager->getPebiIluReuseSteps());
|
|
|
|
|
|
m_pPebiSolverTypeCombo->blockSignals(false);
|
|
|
m_pPebiOmpThreadsCombo->blockSignals(false);
|
|
|
m_pPebiIluReuseStepsSpin->blockSignals(false);
|
|
|
updatePebiSolverSettingEnabled();
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::updateIncludeOtherWellsAvailability()
|
|
|
{
|
|
|
if(m_pDataManager == nullptr ||
|
|
|
m_pIncludeOtherWellsCheck == nullptr ||
|
|
|
m_pOptionsGearButton == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 第一步:只有参考井以外、已经加入 Map 且有有效产量制度的井才是
|
|
|
// Include Other Wells 候选。无产量观察井不控制这个开关。
|
|
|
bool bHasEligibleOtherWell = false;
|
|
|
const QString sPrimaryWellCode = m_pDataManager->getPrimaryWellCode();
|
|
|
QVector<nmDataWellBase*> vecMapWells = m_pDataManager->getWellDataList();
|
|
|
for(int nIndex = 0; nIndex < vecMapWells.size(); ++nIndex) {
|
|
|
nmDataWellBase* pWellData = vecMapWells[nIndex];
|
|
|
if(!isSupportedAdditionalWell(pWellData) ||
|
|
|
pWellData->getWellCode() == sPrimaryWellCode) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
const bool bHasRateTarget = hasAvailableRateTarget(pWellData);
|
|
|
if(bHasRateTarget) {
|
|
|
bHasEligibleOtherWell = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 第二步:没有有产量候选井时关闭该开关;自动观察井仍会继续参与网格和计算。
|
|
|
if(!bHasEligibleOtherWell && m_pDataManager->getIncludeOtherWells()) {
|
|
|
m_pDataManager->setIncludeOtherWells(false);
|
|
|
}
|
|
|
|
|
|
// 第三步:屏蔽界面信号后同步状态;齿轮只在总开关已打开时可用。
|
|
|
const bool bIncludeChecked =
|
|
|
bHasEligibleOtherWell && m_pDataManager->getIncludeOtherWells();
|
|
|
const bool bSignalsWereBlocked =
|
|
|
m_pIncludeOtherWellsCheck->blockSignals(true);
|
|
|
m_pIncludeOtherWellsCheck->setEnabled(bHasEligibleOtherWell);
|
|
|
m_pIncludeOtherWellsCheck->setChecked(bIncludeChecked);
|
|
|
m_pIncludeOtherWellsCheck->blockSignals(bSignalsWereBlocked);
|
|
|
m_pOptionsGearButton->setEnabled(
|
|
|
bHasEligibleOtherWell && bIncludeChecked);
|
|
|
}
|
|
|
|
|
|
|
|
|
bool nmWxNumericalDesign::updateDataFromUi(QString& sError)
|
|
|
{
|
|
|
nmDataAnalyzeManager* pManager = m_pDataManager;
|
|
|
if(pManager == nullptr) {
|
|
|
sError = tr("No data manager available.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
nmDataTimeStepSetting* pTimeStepSetting = pManager->getTimeStep();
|
|
|
if(pTimeStepSetting == nullptr) {
|
|
|
sError = tr("Time step settings are unavailable.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 第一步:先读取全部基准值,任何错误都不能产生部分写入。
|
|
|
double dTimeGrowthExponent = 0.0;
|
|
|
double dDtMin = 0.0;
|
|
|
double dDtMax = 0.0;
|
|
|
if(!collectTimeStepValues(dTimeGrowthExponent,
|
|
|
dDtMin,
|
|
|
dDtMax,
|
|
|
sError)) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 第二步:单位绑定器返回小时基准值,数据单位及持久化结构保持不变。
|
|
|
const double dOldTimeGrowthExponent =
|
|
|
pTimeStepSetting->getTimeGrowthExponent().getValue().toDouble();
|
|
|
const double dOldDtMin =
|
|
|
pTimeStepSetting->getMinDeltaTAttribute().getValue().toDouble();
|
|
|
const double dOldDtMax =
|
|
|
pTimeStepSetting->getMaxDeltaTAttribute().getValue().toDouble();
|
|
|
pTimeStepSetting->getTimeGrowthExponent().setValue(
|
|
|
dTimeGrowthExponent);
|
|
|
pTimeStepSetting->getMinDeltaTAttribute().setValue(dDtMin);
|
|
|
pTimeStepSetting->getMaxDeltaTAttribute().setValue(dDtMax);
|
|
|
|
|
|
// 第三步:时间离散改变时旧结果失效,但二维 PEBI 网格仍可直接复用。
|
|
|
if(!qFuzzyCompare(dOldTimeGrowthExponent, dTimeGrowthExponent) ||
|
|
|
!qFuzzyCompare(dOldDtMin, dDtMin) ||
|
|
|
!qFuzzyCompare(dOldDtMax, dDtMax)) {
|
|
|
pManager->invalidatePebiResults();
|
|
|
}
|
|
|
updatePebiSolverSettingsToData();
|
|
|
sError.clear();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmWxNumericalDesign::collectTimeStepValues(
|
|
|
double& dTimeGrowthExponent,
|
|
|
double& dDtMin,
|
|
|
double& dDtMax,
|
|
|
QString& sError)
|
|
|
{
|
|
|
if(m_pTimeGrowthExponentField == nullptr ||
|
|
|
m_pDtMinField == nullptr ||
|
|
|
m_pDtMaxField == nullptr) {
|
|
|
sError = tr("Time step fields are unavailable.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 第一步:清除上一次跨字段校验错误,再执行各字段格式和范围校验。
|
|
|
m_pTimeGrowthExponentField->setExternalValidationError(QString());
|
|
|
m_pDtMinField->setExternalValidationError(QString());
|
|
|
m_pDtMaxField->setExternalValidationError(QString());
|
|
|
bool bGrowthValid = false;
|
|
|
bool bDtMinValid = false;
|
|
|
bool bDtMaxValid = false;
|
|
|
QVariant oGrowthValue =
|
|
|
m_pTimeGrowthExponentField->baseValue(bGrowthValid);
|
|
|
QVariant oDtMinValue = m_pDtMinField->baseValue(bDtMinValid);
|
|
|
QVariant oDtMaxValue = m_pDtMaxField->baseValue(bDtMaxValid);
|
|
|
if(!bGrowthValid || !bDtMinValid || !bDtMaxValid ||
|
|
|
!oGrowthValue.isValid() ||
|
|
|
!oDtMinValue.isValid() ||
|
|
|
!oDtMaxValue.isValid()) {
|
|
|
sError = tr("Please correct the invalid time step values.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
dTimeGrowthExponent = oGrowthValue.toDouble();
|
|
|
dDtMin = oDtMinValue.toDouble();
|
|
|
dDtMax = oDtMaxValue.toDouble();
|
|
|
|
|
|
// 第二步:单字段合法后再校验最小、最大时间步的业务关系。
|
|
|
if(dDtMin >= dDtMax) {
|
|
|
sError = tr("Dt min must be less than Dt max.");
|
|
|
m_pDtMaxField->setExternalValidationError(sError);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
sError.clear();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::focusFirstInvalidTimeStepField()
|
|
|
{
|
|
|
QList<nmParameterField*> listFields;
|
|
|
listFields << m_pTimeGrowthExponentField
|
|
|
<< m_pDtMinField
|
|
|
<< m_pDtMaxField;
|
|
|
for(int nIndex = 0; nIndex < listFields.size(); ++nIndex) {
|
|
|
nmParameterField* pField = listFields[nIndex];
|
|
|
if(pField != nullptr && !pField->validate() &&
|
|
|
pField->valueEditor() != nullptr) {
|
|
|
pField->valueEditor()->setFocus();
|
|
|
pField->valueEditor()->selectAll();
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::updateTimeDependentSkinDisplay()
|
|
|
{
|
|
|
if(m_pTimeDependentCheck == nullptr ||
|
|
|
m_pTimeDependentIconButton == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
nmDataWellBase* pCurrentWell = m_pDataManager != nullptr
|
|
|
? m_pDataManager->getCurWellData() : nullptr;
|
|
|
bool bTimeDependent = pCurrentWell != nullptr &&
|
|
|
pCurrentWell->isTimeDependentSkin();
|
|
|
bool bSignalsWereBlocked = m_pTimeDependentCheck->blockSignals(true);
|
|
|
m_pTimeDependentCheck->setChecked(bTimeDependent);
|
|
|
m_pTimeDependentCheck->setEnabled(pCurrentWell != nullptr);
|
|
|
m_pTimeDependentCheck->blockSignals(bSignalsWereBlocked);
|
|
|
m_pTimeDependentIconButton->setEnabled(bTimeDependent);
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onAutomaticFittingClicked()
|
|
|
{
|
|
|
nmWxAutomaticFitting* dlg = new nmWxAutomaticFitting;
|
|
|
dlg->show();
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onForecastClicked()
|
|
|
{
|
|
|
nmWxForecast* dlg = new nmWxForecast();
|
|
|
dlg->show();
|
|
|
}
|
|
|
|
|
|
void nmWxNumericalDesign::onSensitiveClicked()
|
|
|
{
|
|
|
nmWxSensitive* dlg = new nmWxSensitive();
|
|
|
dlg->show();
|
|
|
}
|
|
|
|
|
|
// 实现通知方法
|
|
|
void nmWxNumericalDesign::notifyTimeDependentSkinChanged(const QString& wellName, bool checked)
|
|
|
{
|
|
|
// 检查当前是否有活动实例
|
|
|
if(!s_pCurrentInstance) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 使用活动Dock所属的数据管理器,避免多成果窗口间读取到其他成果的数据。
|
|
|
nmDataAnalyzeManager* pDataManager = s_pCurrentInstance->m_pDataManager;
|
|
|
nmDataWellBase* pCurrentWell = pDataManager != nullptr
|
|
|
? pDataManager->getCurWellData() : nullptr;
|
|
|
|
|
|
if(!pCurrentWell) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 检查是否是同一口井
|
|
|
if(pCurrentWell->getWellName() == wellName) {
|
|
|
// 阻塞信号,避免循环触发
|
|
|
s_pCurrentInstance->m_pTimeDependentCheck->blockSignals(true);
|
|
|
|
|
|
// 更新复选框状态
|
|
|
s_pCurrentInstance->m_pTimeDependentCheck->setChecked(checked);
|
|
|
|
|
|
// 恢复信号
|
|
|
s_pCurrentInstance->m_pTimeDependentCheck->blockSignals(false);
|
|
|
|
|
|
// 同时更新图标按钮的启用状态
|
|
|
s_pCurrentInstance->m_pTimeDependentIconButton->setEnabled(checked);
|
|
|
}
|
|
|
}
|