|
|
|
|
|
#include "nmWxAutomaticFitting.h"
|
|
|
|
|
|
#include "nmCalculationAutoFitPSO.h"
|
|
|
|
|
|
#include "nmWxAutomaticfittingStart.h"
|
|
|
|
|
|
#include "nmWxParameterProperty.h"
|
|
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
|
#include <QHeaderView>
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
#define DEBUG_UI(msg) OutputDebugStringA(QString("[UI] %1\n").arg(msg).toLocal8Bit().data())
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// 设置某一行参数是否显示。隐藏时同步取消勾选并禁用,避免隐藏参数参与自动拟合。
|
|
|
|
|
|
void nmWxAutomaticFitting::setParameterRowVisible(QTableWidget* table, int row, bool visible)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(!table || row < 0 || row >= table->rowCount()) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
table->setRowHidden(row, !visible);
|
|
|
|
|
|
|
|
|
|
|
|
QCheckBox* checkBox = qobject_cast<QCheckBox*>(table->cellWidget(row, 1));
|
|
|
|
|
|
if(checkBox) {
|
|
|
|
|
|
checkBox->setEnabled(visible);
|
|
|
|
|
|
if(!visible) {
|
|
|
|
|
|
checkBox->setChecked(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 隐藏参数行后重新整理序号,让界面看起来像删除了不需要的参数。
|
|
|
|
|
|
void nmWxAutomaticFitting::renumberVisibleParameterRows(QTableWidget* table)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(!table) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int visibleIndex = 1;
|
|
|
|
|
|
for(int row = 0; row < table->rowCount(); ++row) {
|
|
|
|
|
|
if(table->isRowHidden(row)) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QTableWidgetItem* item = table->item(row, 0);
|
|
|
|
|
|
if(!item) {
|
|
|
|
|
|
item = new QTableWidgetItem();
|
|
|
|
|
|
table->setItem(row, 0, item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
item->setText(QString::number(visibleIndex++));
|
|
|
|
|
|
item->setTextAlignment(Qt::AlignCenter);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据当前模型类型控制参数显示:0-5行为通用参数,6-10行为随PVT模型变化的参数。
|
|
|
|
|
|
void nmWxAutomaticFitting::updateParameterVisibility(QTableWidget* table, NM_SOLVER_MODEL_TYPE eType)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(!table) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for(int row = 0; row < table->rowCount(); ++row) {
|
|
|
|
|
|
setParameterRowVisible(table, row, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool showCt = false;
|
|
|
|
|
|
bool showCf = false;
|
|
|
|
|
|
bool showSwi = false;
|
|
|
|
|
|
|
|
|
|
|
|
switch(eType) {
|
|
|
|
|
|
case SMT_Oil_ConstPvt:
|
|
|
|
|
|
case SMT_Water_ConstPvt:
|
|
|
|
|
|
// 常量PVT使用综合压缩系数Ct。
|
|
|
|
|
|
showCt = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SMT_Oil_VariablePvt:
|
|
|
|
|
|
case SMT_Water_VariablePvt:
|
|
|
|
|
|
case SMT_Gas_VariablePvt:
|
|
|
|
|
|
// 变化PVT使用岩石压缩系数Cf。
|
|
|
|
|
|
showCf = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SMT_Oil_Water_TwoPhase:
|
|
|
|
|
|
// 油水两相使用Cf,并且只有它需要初始含水饱和度Swi。
|
|
|
|
|
|
showCf = true;
|
|
|
|
|
|
showSwi = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
showCf = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setParameterRowVisible(table, 6, showCt); // Ct
|
|
|
|
|
|
setParameterRowVisible(table, 7, showCf); // Cf
|
|
|
|
|
|
setParameterRowVisible(table, 8, false); // Soi
|
|
|
|
|
|
setParameterRowVisible(table, 9, showSwi); // Swi
|
|
|
|
|
|
setParameterRowVisible(table, 10, false); // Sgi
|
|
|
|
|
|
|
|
|
|
|
|
renumberVisibleParameterRows(table);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
|
|
|
|
|
|
: iDlgBase(parent)
|
|
|
|
|
|
, m_autoFitterPSO(nullptr)
|
|
|
|
|
|
, m_autoFitterGA(nullptr)
|
|
|
|
|
|
, m_progressDialog(nullptr)
|
|
|
|
|
|
, m_progressTimer(nullptr)
|
|
|
|
|
|
, m_progressMonitor(nullptr)
|
|
|
|
|
|
, m_selectedAlgorithm(ALGORITHM_PSO) // 默认选择PSO算法
|
|
|
|
|
|
{
|
|
|
|
|
|
DEBUG_UI(QString("AutoFitting Constructor: this=0x%1").arg((quintptr)this, 0, 16));
|
|
|
|
|
|
|
|
|
|
|
|
// 加载已有井数据
|
|
|
|
|
|
QVector<nmDataWellBase*> listWellData = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList();
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历并分类井数据
|
|
|
|
|
|
foreach (nmDataWellBase* well, listWellData) {
|
|
|
|
|
|
if (auto vfWell = dynamic_cast<nmDataVerticalFracturedWell*>(well)) {
|
|
|
|
|
|
m_verticalFracturedWells.append(*vfWell);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (auto hfWell = dynamic_cast<nmDataHorizontalFracturedWell*>(well)) {
|
|
|
|
|
|
m_horizontalFracturedWells.append(*hfWell);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (auto vWell = dynamic_cast<nmDataVerticalWell*>(well)) {
|
|
|
|
|
|
m_verticalWells.append(*vWell);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (auto hWell = dynamic_cast<nmDataHorizontalWell*>(well)) {
|
|
|
|
|
|
m_horizontalWells.append(*hWell);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取数据
|
|
|
|
|
|
reservoirData = nmDataAnalyzeManager::getCurrentInstance()->getReservoirDataCopy();
|
|
|
|
|
|
automaticFittingData = nmDataAnalyzeManager::getCurrentInstance()->getAutomaticFittingDataCopy();
|
|
|
|
|
|
|
|
|
|
|
|
setupUI();
|
|
|
|
|
|
setWindowTitle(tr("Automatic fitting"));
|
|
|
|
|
|
setModal(true);
|
|
|
|
|
|
resize(800, 480);
|
|
|
|
|
|
|
|
|
|
|
|
if(m_targetWellCombo->count() > 0) {
|
|
|
|
|
|
onWellSelected(0); // 默认选中第一口井
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_UI("AutoFitting Constructor completed");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmWxAutomaticFitting::~nmWxAutomaticFitting()
|
|
|
|
|
|
{
|
|
|
|
|
|
DEBUG_UI(QString("AutoFitting Destructor: this=0x%1").arg((quintptr)this, 0, 16));
|
|
|
|
|
|
DEBUG_UI("AutoFitting destructor - starting cleanup");
|
|
|
|
|
|
|
|
|
|
|
|
// 停止定时器
|
|
|
|
|
|
if (m_progressTimer) {
|
|
|
|
|
|
m_progressTimer->stop();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 断开所有连接,避免悬空指针
|
|
|
|
|
|
if (m_autoFitterPSO) {
|
|
|
|
|
|
disconnect(m_autoFitterPSO, nullptr, this, nullptr);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (m_autoFitterGA) {
|
|
|
|
|
|
disconnect(m_autoFitterGA, nullptr, this, nullptr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_UI("AutoFitting destructor - completed");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::setupUI()
|
|
|
|
|
|
{
|
|
|
|
|
|
m_mainLayout = new QVBoxLayout(this);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建主要内容的水平布局
|
|
|
|
|
|
QHBoxLayout* contentLayout = new QHBoxLayout();
|
|
|
|
|
|
|
|
|
|
|
|
// 添加左侧间距
|
|
|
|
|
|
contentLayout->addSpacing(20);
|
|
|
|
|
|
|
|
|
|
|
|
setupControlPanel(); // 左侧控制面板
|
|
|
|
|
|
contentLayout->addLayout(m_controlLayout);
|
|
|
|
|
|
|
|
|
|
|
|
// 添加控制面板和表格之间的间距
|
|
|
|
|
|
contentLayout->addSpacing(30);
|
|
|
|
|
|
|
|
|
|
|
|
setupParameterTable(); // 右侧参数表格
|
|
|
|
|
|
contentLayout->addWidget(m_parameterTable, 1); // 设置拉伸因子为1,使表格可以扩展
|
|
|
|
|
|
|
|
|
|
|
|
// 添加右侧间距
|
|
|
|
|
|
contentLayout->addSpacing(20);
|
|
|
|
|
|
|
|
|
|
|
|
m_mainLayout->addLayout(contentLayout);
|
|
|
|
|
|
setupButtons(); // 底部按钮
|
|
|
|
|
|
|
|
|
|
|
|
setLayout(m_mainLayout);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::setupParameterTable()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 创建表格
|
|
|
|
|
|
m_parameterTable = new QTableWidget(11, 6, this);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置表头
|
|
|
|
|
|
QStringList headers;
|
|
|
|
|
|
headers << "" << tr("Parameter") << tr("Min") << tr("Initial value") << tr("Max") << tr("Unit");
|
|
|
|
|
|
m_parameterTable->setHorizontalHeaderLabels(headers);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置表格属性
|
|
|
|
|
|
m_parameterTable->setSelectionBehavior(QAbstractItemView::SelectItems);
|
|
|
|
|
|
m_parameterTable->setAlternatingRowColors(true);
|
|
|
|
|
|
m_parameterTable->verticalHeader()->setVisible(false);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置列宽
|
|
|
|
|
|
QHeaderView* header = m_parameterTable->horizontalHeader();
|
|
|
|
|
|
header->setResizeMode(0, QHeaderView::Fixed); // 序号列固定宽度
|
|
|
|
|
|
header->setResizeMode(1, QHeaderView::Fixed); // 参数列固定宽度
|
|
|
|
|
|
header->setResizeMode(2, QHeaderView::Stretch); // 其他列自适应
|
|
|
|
|
|
header->setResizeMode(3, QHeaderView::Stretch);
|
|
|
|
|
|
header->setResizeMode(4, QHeaderView::Stretch);
|
|
|
|
|
|
header->setResizeMode(5, QHeaderView::Fixed); // 单位列固定宽度
|
|
|
|
|
|
|
|
|
|
|
|
// 设置固定列的宽度
|
|
|
|
|
|
m_parameterTable->setColumnWidth(0, 40); // 序号列宽度
|
|
|
|
|
|
m_parameterTable->setColumnWidth(1, 120); // 参数列宽度
|
|
|
|
|
|
m_parameterTable->setColumnWidth(5, 80); // 单位列宽度
|
|
|
|
|
|
|
|
|
|
|
|
// 设置表格的大小策略
|
|
|
|
|
|
m_parameterTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
|
|
|
|
|
|
|
|
|
|
// 渗透率 (Permeability)
|
|
|
|
|
|
m_parameterTable->setItem(0, 0, new QTableWidgetItem("1"));
|
|
|
|
|
|
m_kCheckBox = new QCheckBox(tr("Permeability"));
|
|
|
|
|
|
m_kCheckBox->setChecked(automaticFittingData.getPermeabilitySelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(0, 1, m_kCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(0, 2, new QTableWidgetItem(QString::number(automaticFittingData.getPermeabilityMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(0, 3, new QTableWidgetItem(QString::number(reservoirData.getPermeability().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(0, 4, new QTableWidgetItem(QString::number(automaticFittingData.getPermeabilityMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(0, 5, new QTableWidgetItem(tr("Darcy")));
|
|
|
|
|
|
|
|
|
|
|
|
// 表皮系数 (Skin)
|
|
|
|
|
|
m_parameterTable->setItem(1, 0, new QTableWidgetItem("2"));
|
|
|
|
|
|
m_sCheckBox = new QCheckBox(tr("Skin"));
|
|
|
|
|
|
m_sCheckBox->setChecked(automaticFittingData.getSkinSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(1, 1, m_sCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(1, 2, new QTableWidgetItem(QString::number(automaticFittingData.getSkinMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(1, 3, new QTableWidgetItem());
|
|
|
|
|
|
m_parameterTable->setItem(1, 4, new QTableWidgetItem(QString::number(automaticFittingData.getSkinMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(1, 5, new QTableWidgetItem(""));
|
|
|
|
|
|
|
|
|
|
|
|
// 井筒储集系数 (Wellbore storage)
|
|
|
|
|
|
m_parameterTable->setItem(2, 0, new QTableWidgetItem("3"));
|
|
|
|
|
|
m_cCheckBox = new QCheckBox(tr("Wellbore storage"));
|
|
|
|
|
|
m_cCheckBox->setChecked(automaticFittingData.getWellboreStorageSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(2, 1, m_cCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(2, 2, new QTableWidgetItem(QString::number(automaticFittingData.getWellboreStorageMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(2, 3, new QTableWidgetItem());
|
|
|
|
|
|
m_parameterTable->setItem(2, 4, new QTableWidgetItem(QString::number(automaticFittingData.getWellboreStorageMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(2, 5, new QTableWidgetItem(tr("m3/MPa")));
|
|
|
|
|
|
|
|
|
|
|
|
// 孔隙度 (Porosity)
|
|
|
|
|
|
m_parameterTable->setItem(3, 0, new QTableWidgetItem("4"));
|
|
|
|
|
|
m_phiCheckBox = new QCheckBox(tr("Porosity"));
|
|
|
|
|
|
m_phiCheckBox->setChecked(automaticFittingData.getPorositySelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(3, 1, m_phiCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(3, 2, new QTableWidgetItem(QString::number(automaticFittingData.getPorosityMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(3, 3, new QTableWidgetItem(QString::number(reservoirData.getPorosity().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(3, 4, new QTableWidgetItem(QString::number(automaticFittingData.getPorosityMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(3, 5, new QTableWidgetItem(""));
|
|
|
|
|
|
|
|
|
|
|
|
// 初始压力 (Initial Pressure)
|
|
|
|
|
|
m_parameterTable->setItem(4, 0, new QTableWidgetItem("5"));
|
|
|
|
|
|
m_piCheckBox = new QCheckBox(tr("Initial Pressure"));
|
|
|
|
|
|
m_piCheckBox->setChecked(automaticFittingData.getInitialPressureSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(4, 1, m_piCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(4, 2, new QTableWidgetItem(QString::number(automaticFittingData.getInitialPressureMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(4, 3, new QTableWidgetItem(QString::number(reservoirData.getInitialPressure().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(4, 4, new QTableWidgetItem(QString::number(automaticFittingData.getInitialPressureMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(4, 5, new QTableWidgetItem(tr("MPa")));
|
|
|
|
|
|
|
|
|
|
|
|
// 储层厚度 (Thickness)
|
|
|
|
|
|
m_parameterTable->setItem(5, 0, new QTableWidgetItem("6"));
|
|
|
|
|
|
m_hCheckBox = new QCheckBox(tr("Thickness"));
|
|
|
|
|
|
m_hCheckBox->setChecked(automaticFittingData.getThicknessSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(5, 1, m_hCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(5, 2, new QTableWidgetItem(QString::number(automaticFittingData.getThicknessMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(5, 3, new QTableWidgetItem(QString::number(reservoirData.getThickness().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(5, 4, new QTableWidgetItem(QString::number(automaticFittingData.getThicknessMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(5, 5, new QTableWidgetItem(tr("m")));
|
|
|
|
|
|
|
|
|
|
|
|
// 综合压缩系数 (Ct)
|
|
|
|
|
|
m_parameterTable->setItem(6, 0, new QTableWidgetItem("7"));
|
|
|
|
|
|
m_ctCheckBox = new QCheckBox(tr("Ct"));
|
|
|
|
|
|
m_ctCheckBox->setChecked(automaticFittingData.getCtSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(6, 1, m_ctCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(6, 2, new QTableWidgetItem(QString::number(automaticFittingData.getCtMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(6, 3, new QTableWidgetItem(QString::number(reservoirData.getCt().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(6, 4, new QTableWidgetItem(QString::number(automaticFittingData.getCtMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(6, 5, new QTableWidgetItem(""));
|
|
|
|
|
|
|
|
|
|
|
|
// 岩石压缩系数 (Cf)
|
|
|
|
|
|
m_parameterTable->setItem(7, 0, new QTableWidgetItem("8"));
|
|
|
|
|
|
m_cfCheckBox = new QCheckBox(tr("Cf"));
|
|
|
|
|
|
m_cfCheckBox->setChecked(automaticFittingData.getCfSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(7, 1, m_cfCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(7, 2, new QTableWidgetItem(QString::number(automaticFittingData.getCfMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(7, 3, new QTableWidgetItem(QString::number(reservoirData.getCf().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(7, 4, new QTableWidgetItem(QString::number(automaticFittingData.getCfMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(7, 5, new QTableWidgetItem(""));
|
|
|
|
|
|
|
|
|
|
|
|
// 初始含油饱和度 (Soi)
|
|
|
|
|
|
m_parameterTable->setItem(8, 0, new QTableWidgetItem("9"));
|
|
|
|
|
|
m_soiCheckBox = new QCheckBox(tr("Soi"));
|
|
|
|
|
|
m_soiCheckBox->setChecked(automaticFittingData.getSoiSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(8, 1, m_soiCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(8, 2, new QTableWidgetItem(QString::number(automaticFittingData.getSoiMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(8, 3, new QTableWidgetItem(QString::number(reservoirData.getSoi().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(8, 4, new QTableWidgetItem(QString::number(automaticFittingData.getSoiMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(8, 5, new QTableWidgetItem(""));
|
|
|
|
|
|
|
|
|
|
|
|
// 初始含水饱和度 (Swi)
|
|
|
|
|
|
m_parameterTable->setItem(9, 0, new QTableWidgetItem("10"));
|
|
|
|
|
|
m_swiCheckBox = new QCheckBox(tr("Swi"));
|
|
|
|
|
|
m_swiCheckBox->setChecked(automaticFittingData.getSwiSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(9, 1, m_swiCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(9, 2, new QTableWidgetItem(QString::number(automaticFittingData.getSwiMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(9, 3, new QTableWidgetItem(QString::number(reservoirData.getSwi().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(9, 4, new QTableWidgetItem(QString::number(automaticFittingData.getSwiMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(9, 5, new QTableWidgetItem(""));
|
|
|
|
|
|
|
|
|
|
|
|
// 初始含气饱和度 (Sgi)
|
|
|
|
|
|
m_parameterTable->setItem(10, 0, new QTableWidgetItem("11"));
|
|
|
|
|
|
m_sgiCheckBox = new QCheckBox(tr("Sgi"));
|
|
|
|
|
|
m_sgiCheckBox->setChecked(automaticFittingData.getSgiSelected());
|
|
|
|
|
|
m_parameterTable->setCellWidget(10, 1, m_sgiCheckBox);
|
|
|
|
|
|
m_parameterTable->setItem(10, 2, new QTableWidgetItem(QString::number(automaticFittingData.getSgiMin().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(10, 3, new QTableWidgetItem(QString::number(reservoirData.getSgi().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(10, 4, new QTableWidgetItem(QString::number(automaticFittingData.getSgiMax().getValue().toDouble())));
|
|
|
|
|
|
m_parameterTable->setItem(10, 5, new QTableWidgetItem(""));
|
|
|
|
|
|
|
|
|
|
|
|
// 设置表格行为
|
|
|
|
|
|
for(int i = 0; i < 11; ++i) {
|
|
|
|
|
|
for(int j = 0; j < 6; ++j) {
|
|
|
|
|
|
QTableWidgetItem* item = m_parameterTable->item(i, j);
|
|
|
|
|
|
|
|
|
|
|
|
if(item) {
|
|
|
|
|
|
if(j == 0 || j == 5) { // 序号列和单位列只读
|
|
|
|
|
|
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
|
|
|
|
|
|
} else if(j >= 2) {
|
|
|
|
|
|
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
|
|
|
|
|
|
} else { // 参数列(复选框)不可选择
|
|
|
|
|
|
item->setFlags(Qt::NoItemFlags);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 序号列居中对齐
|
|
|
|
|
|
for(int i = 0; i < 11; ++i) {
|
|
|
|
|
|
QTableWidgetItem* item = m_parameterTable->item(i, 0);
|
|
|
|
|
|
|
|
|
|
|
|
if(item) {
|
|
|
|
|
|
item->setTextAlignment(Qt::AlignCenter);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance();
|
|
|
|
|
|
if(pManager) {
|
|
|
|
|
|
updateParameterVisibility(m_parameterTable, pManager->getSolverModelType());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 连接选择改变信号
|
|
|
|
|
|
connect(m_parameterTable, SIGNAL(currentCellChanged(int, int, int, int)),
|
|
|
|
|
|
this, SLOT(onCellSelectionChanged(int, int, int, int)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::setupControlPanel()
|
|
|
|
|
|
{
|
|
|
|
|
|
m_controlLayout = new QVBoxLayout();
|
|
|
|
|
|
m_controlLayout->setSizeConstraint(QLayout::SetFixedSize);
|
|
|
|
|
|
|
|
|
|
|
|
// 上方弹簧
|
|
|
|
|
|
m_controlLayout->addStretch(1);
|
|
|
|
|
|
|
|
|
|
|
|
// 算法选择
|
|
|
|
|
|
QLabel* algorithmLabel = new QLabel(tr("Algorithm:"));
|
|
|
|
|
|
m_algorithmCombo = new QComboBox();
|
|
|
|
|
|
m_algorithmCombo->addItem(tr("PSO (Particle Swarm)"));
|
|
|
|
|
|
m_algorithmCombo->addItem(tr("GA (Genetic Algorithm)"));
|
|
|
|
|
|
m_algorithmCombo->setCurrentIndex(0); // 默认选择PSO
|
|
|
|
|
|
connect(m_algorithmCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onAlgorithmChanged(int)));
|
|
|
|
|
|
m_algorithmCombo->setMaximumWidth(160);
|
|
|
|
|
|
m_algorithmCombo->setMinimumWidth(160);
|
|
|
|
|
|
|
|
|
|
|
|
QLabel* surrogateLabel = new QLabel(tr("PSO acceleration:"));
|
|
|
|
|
|
m_surrogateCombo = new QComboBox();
|
|
|
|
|
|
//m_surrogateCombo->setEnabled(false);// 暂时不可编辑
|
|
|
|
|
|
m_surrogateCombo->addItem(tr("Off"));
|
|
|
|
|
|
m_surrogateCombo->addItem(tr("On"));
|
|
|
|
|
|
m_surrogateCombo->setCurrentIndex(automaticFittingData.getSurrogateScreeningEnabled() ? 1 : 0);
|
|
|
|
|
|
m_surrogateCombo->setMaximumWidth(160);
|
|
|
|
|
|
m_surrogateCombo->setMinimumWidth(160);
|
|
|
|
|
|
|
|
|
|
|
|
// 迭代次数
|
|
|
|
|
|
QLabel* iterationLabel = new QLabel(tr("Number of iterations:"));
|
|
|
|
|
|
m_iterationEdit = new QLineEdit(QString::number(automaticFittingData.getIterationCount().getValue().toInt()));
|
|
|
|
|
|
m_iterationEdit->setMaximumWidth(100);
|
|
|
|
|
|
m_iterationEdit->setMinimumWidth(100);
|
|
|
|
|
|
|
|
|
|
|
|
// 误差上限
|
|
|
|
|
|
QLabel* errorLabel = new QLabel(tr("Error tolerance:"));
|
|
|
|
|
|
m_errorLimitEdit = new QLineEdit(QString::number(automaticFittingData.getErrorTolerance().getValue().toDouble()));
|
|
|
|
|
|
m_errorLimitEdit->setMaximumWidth(100);
|
|
|
|
|
|
m_errorLimitEdit->setMinimumWidth(100);
|
|
|
|
|
|
|
|
|
|
|
|
// 目标井选择
|
|
|
|
|
|
QLabel* wellLabel = new QLabel(tr("Target Well:"));
|
|
|
|
|
|
m_targetWellCombo = new QComboBox();
|
|
|
|
|
|
|
|
|
|
|
|
// 添加垂直井
|
|
|
|
|
|
for(int i = 0; i < m_verticalWells.size(); ++i) {
|
|
|
|
|
|
m_targetWellCombo->addItem(m_verticalWells[i].getWellName());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加水平井
|
|
|
|
|
|
for(int i = 0; i < m_horizontalWells.size(); ++i) {
|
|
|
|
|
|
m_targetWellCombo->addItem(m_horizontalWells[i].getWellName());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加垂直压裂井
|
|
|
|
|
|
for(int i = 0; i < m_verticalFracturedWells.size(); ++i) {
|
|
|
|
|
|
m_targetWellCombo->addItem(m_verticalFracturedWells[i].getWellName());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加水平压裂井
|
|
|
|
|
|
for(int i = 0; i < m_horizontalFracturedWells.size(); ++i) {
|
|
|
|
|
|
m_targetWellCombo->addItem(m_horizontalFracturedWells[i].getWellName());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
connect(m_targetWellCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onWellSelected(int)));
|
|
|
|
|
|
m_targetWellCombo->setMaximumWidth(100);
|
|
|
|
|
|
m_targetWellCombo->setMinimumWidth(100);
|
|
|
|
|
|
|
|
|
|
|
|
// 添加到垂直布局
|
|
|
|
|
|
m_controlLayout->addWidget(algorithmLabel);
|
|
|
|
|
|
m_controlLayout->addWidget(m_algorithmCombo);
|
|
|
|
|
|
m_controlLayout->addSpacing(15);
|
|
|
|
|
|
|
|
|
|
|
|
m_controlLayout->addWidget(surrogateLabel);
|
|
|
|
|
|
m_controlLayout->addWidget(m_surrogateCombo);
|
|
|
|
|
|
m_controlLayout->addSpacing(15);
|
|
|
|
|
|
|
|
|
|
|
|
m_controlLayout->addWidget(iterationLabel);
|
|
|
|
|
|
m_controlLayout->addWidget(m_iterationEdit);
|
|
|
|
|
|
m_controlLayout->addSpacing(15);
|
|
|
|
|
|
|
|
|
|
|
|
m_controlLayout->addWidget(errorLabel);
|
|
|
|
|
|
m_controlLayout->addWidget(m_errorLimitEdit);
|
|
|
|
|
|
m_controlLayout->addSpacing(15);
|
|
|
|
|
|
|
|
|
|
|
|
m_controlLayout->addWidget(wellLabel);
|
|
|
|
|
|
m_controlLayout->addWidget(m_targetWellCombo);
|
|
|
|
|
|
|
|
|
|
|
|
// 下方弹簧
|
|
|
|
|
|
m_controlLayout->addStretch(1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::setupButtons()
|
|
|
|
|
|
{
|
|
|
|
|
|
QHBoxLayout* buttonLayout = new QHBoxLayout();
|
|
|
|
|
|
|
|
|
|
|
|
m_reverseBtn = new QPushButton(tr("Reverse Selection"));
|
|
|
|
|
|
m_okBtn = new QPushButton(tr("OK"));
|
|
|
|
|
|
m_cancelBtn = new QPushButton(tr("Cancel"));
|
|
|
|
|
|
|
|
|
|
|
|
// 设置按钮大小
|
|
|
|
|
|
m_reverseBtn->setMinimumWidth(120);
|
|
|
|
|
|
m_okBtn->setMinimumWidth(80);
|
|
|
|
|
|
m_cancelBtn->setMinimumWidth(80);
|
|
|
|
|
|
|
|
|
|
|
|
buttonLayout->addStretch();
|
|
|
|
|
|
buttonLayout->addWidget(m_reverseBtn);
|
|
|
|
|
|
buttonLayout->addWidget(m_okBtn);
|
|
|
|
|
|
buttonLayout->addWidget(m_cancelBtn);
|
|
|
|
|
|
|
|
|
|
|
|
m_mainLayout->addLayout(buttonLayout);
|
|
|
|
|
|
|
|
|
|
|
|
// 连接信号槽
|
|
|
|
|
|
connect(m_reverseBtn, SIGNAL(clicked()), this, SLOT(onReverseSelection()));
|
|
|
|
|
|
connect(m_okBtn, SIGNAL(clicked()), this, SLOT(onAccept()));
|
|
|
|
|
|
connect(m_cancelBtn, SIGNAL(clicked()), this, SLOT(onReject()));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onCellSelectionChanged(int currentRow, int currentColumn, int previousRow, int previousColumn)
|
|
|
|
|
|
{
|
|
|
|
|
|
if(!m_parameterTable) return;
|
|
|
|
|
|
|
|
|
|
|
|
QHeaderView* header = m_parameterTable->horizontalHeader();
|
|
|
|
|
|
if(!header) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 重置所有列头的字体为正常
|
|
|
|
|
|
|
|
|
|
|
|
for(int i = 0; i < m_parameterTable->columnCount(); ++i) {
|
|
|
|
|
|
QFont font = header->font();
|
|
|
|
|
|
font.setBold(false);
|
|
|
|
|
|
if(m_parameterTable->horizontalHeaderItem(i)) {
|
|
|
|
|
|
m_parameterTable->horizontalHeaderItem(i)->setFont(font);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置当前列的表头为粗体
|
|
|
|
|
|
|
|
|
|
|
|
if(currentColumn >= 0 && currentColumn < m_parameterTable->columnCount()) {
|
|
|
|
|
|
QFont font = header->font();
|
|
|
|
|
|
font.setBold(true);
|
|
|
|
|
|
|
|
|
|
|
|
if(m_parameterTable->horizontalHeaderItem(currentColumn)) {
|
|
|
|
|
|
m_parameterTable->horizontalHeaderItem(currentColumn)->setFont(font);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
header->update();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onReverseSelection()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 更新反选逻辑,包含所有参数
|
|
|
|
|
|
if(!m_parameterTable) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(0)) m_kCheckBox->setChecked(!m_kCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(1)) m_sCheckBox->setChecked(!m_sCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(2)) m_cCheckBox->setChecked(!m_cCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(3)) m_phiCheckBox->setChecked(!m_phiCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(4)) m_piCheckBox->setChecked(!m_piCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(5)) m_hCheckBox->setChecked(!m_hCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(6)) m_ctCheckBox->setChecked(!m_ctCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(7)) m_cfCheckBox->setChecked(!m_cfCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(8)) m_soiCheckBox->setChecked(!m_soiCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(9)) m_swiCheckBox->setChecked(!m_swiCheckBox->isChecked());
|
|
|
|
|
|
if(!m_parameterTable->isRowHidden(10)) m_sgiCheckBox->setChecked(!m_sgiCheckBox->isChecked());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onAlgorithmChanged(int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_selectedAlgorithm = static_cast<OptimizationAlgorithm>(index);
|
|
|
|
|
|
|
|
|
|
|
|
// 根据算法类型调整界面提示或参数
|
|
|
|
|
|
QString algorithmInfo;
|
|
|
|
|
|
switch(m_selectedAlgorithm) {
|
|
|
|
|
|
case ALGORITHM_PSO:
|
|
|
|
|
|
algorithmInfo = tr("PSO algorithm selected.");
|
|
|
|
|
|
if(m_surrogateCombo) {
|
|
|
|
|
|
m_surrogateCombo->setEnabled(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ALGORITHM_GA:
|
|
|
|
|
|
algorithmInfo = tr("GA algorithm selected.");
|
|
|
|
|
|
if(m_surrogateCombo) {
|
|
|
|
|
|
m_surrogateCombo->setEnabled(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 在状态栏或工具提示中显示算法信息
|
|
|
|
|
|
m_algorithmCombo->setToolTip(algorithmInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onAccept()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 首先保存参数设置
|
|
|
|
|
|
setAutomaticFittingValue();
|
|
|
|
|
|
// 更新完成后,通知参数界面刷新
|
|
|
|
|
|
nmWxParameterProperty::notifyUpdateTable();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取目标井的双对数历史数据
|
|
|
|
|
|
QString selectedWellName = m_targetWellCombo->currentText();
|
|
|
|
|
|
|
|
|
|
|
|
if(selectedWellName.isEmpty()) {
|
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Please select a target well!"));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从数据管理器获取最新的井对象
|
|
|
|
|
|
nmDataWellBase* pTargetWell = nmDataAnalyzeManager::getCurrentInstance()->findWellByName(selectedWellName);
|
|
|
|
|
|
|
|
|
|
|
|
if(!pTargetWell) {
|
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Target well not found!"));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取目标双对数历史数据
|
|
|
|
|
|
QVector<QVector<double> > targetLogLogData = pTargetWell->getHistoryLogLog();
|
|
|
|
|
|
|
|
|
|
|
|
if(targetLogLogData.isEmpty() || targetLogLogData.size() < 3) {
|
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Target well has no LogLog history data!"));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查双对数数据的一致性
|
|
|
|
|
|
if(targetLogLogData[0].size() != targetLogLogData[1].size() ||
|
|
|
|
|
|
targetLogLogData[0].size() != targetLogLogData[2].size()) {
|
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Target well LogLog data is inconsistent!"));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查结果双对数数据
|
|
|
|
|
|
QVector<QVector<double> > rstLogLogData = pTargetWell->getResultLogLog();
|
|
|
|
|
|
|
|
|
|
|
|
if(rstLogLogData.isEmpty() || rstLogLogData.size() < 3) {
|
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Target well has no LogLog result data!"));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否有参数被选中
|
|
|
|
|
|
bool hasSelectedParams = m_kCheckBox->isChecked() || m_sCheckBox->isChecked() ||
|
|
|
|
|
|
m_cCheckBox->isChecked() || m_phiCheckBox->isChecked() ||
|
|
|
|
|
|
m_piCheckBox->isChecked() || m_hCheckBox->isChecked() ||
|
|
|
|
|
|
m_ctCheckBox->isChecked() || m_cfCheckBox->isChecked() ||
|
|
|
|
|
|
m_soiCheckBox->isChecked() || m_swiCheckBox->isChecked() ||
|
|
|
|
|
|
m_sgiCheckBox->isChecked();
|
|
|
|
|
|
|
|
|
|
|
|
if(!hasSelectedParams) {
|
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Please select at least one parameter for optimization!"));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 收集选中的参数名称
|
|
|
|
|
|
QStringList selectedParameterNames;
|
|
|
|
|
|
|
|
|
|
|
|
if(m_kCheckBox->isChecked()) selectedParameterNames << tr("Permeability");
|
|
|
|
|
|
if(m_sCheckBox->isChecked()) selectedParameterNames << tr("Skin");
|
|
|
|
|
|
if(m_cCheckBox->isChecked()) selectedParameterNames << tr("Wellbore storage");
|
|
|
|
|
|
if(m_phiCheckBox->isChecked()) selectedParameterNames << tr("Porosity");
|
|
|
|
|
|
if(m_piCheckBox->isChecked()) selectedParameterNames << tr("Initial Pressure");
|
|
|
|
|
|
if(m_hCheckBox->isChecked()) selectedParameterNames << tr("Thickness");
|
|
|
|
|
|
if(m_ctCheckBox->isChecked()) selectedParameterNames << tr("Ct");
|
|
|
|
|
|
if(m_cfCheckBox->isChecked()) selectedParameterNames << tr("Cf");
|
|
|
|
|
|
if(m_soiCheckBox->isChecked()) selectedParameterNames << tr("Soi");
|
|
|
|
|
|
if(m_swiCheckBox->isChecked()) selectedParameterNames << tr("Swi");
|
|
|
|
|
|
if(m_sgiCheckBox->isChecked()) selectedParameterNames << tr("Sgi");
|
|
|
|
|
|
|
|
|
|
|
|
// 启动自动拟合 - 传递双对数历史数据
|
|
|
|
|
|
startAutoFitting(targetLogLogData, selectedParameterNames, selectedWellName);
|
|
|
|
|
|
}
|
|
|
|
|
|
void nmWxAutomaticFitting::onReject()
|
|
|
|
|
|
{
|
|
|
|
|
|
reject();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onWellSelected(int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取选中的井名
|
|
|
|
|
|
QString selectedWellName = m_targetWellCombo->itemText(index);
|
|
|
|
|
|
|
|
|
|
|
|
// 在分类的井数据中查找匹配的井
|
|
|
|
|
|
bool found = false;
|
|
|
|
|
|
double skinValue = 0.0;
|
|
|
|
|
|
double wellboreStorageValue = 0.0;
|
|
|
|
|
|
|
|
|
|
|
|
// 查找垂直井
|
|
|
|
|
|
for(int i = 0; i < m_verticalWells.size(); ++i) {
|
|
|
|
|
|
if(m_verticalWells[i].getWellName() == selectedWellName) {
|
|
|
|
|
|
skinValue = m_verticalWells[i].getPerforation(0)->getSkin().getValue().toDouble();
|
|
|
|
|
|
wellboreStorageValue = m_verticalWells[i].getWellboreStorage().getValue().toDouble();
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找水平井
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
|
for(int i = 0; i < m_horizontalWells.size(); ++i) {
|
|
|
|
|
|
if(m_horizontalWells[i].getWellName() == selectedWellName) {
|
|
|
|
|
|
skinValue = m_horizontalWells[i].getPerforation(0)->getSkin().getValue().toDouble();
|
|
|
|
|
|
wellboreStorageValue = m_horizontalWells[i].getWellboreStorage().getValue().toDouble();
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找垂直压裂井
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
|
for(int i = 0; i < m_verticalFracturedWells.size(); ++i) {
|
|
|
|
|
|
if(m_verticalFracturedWells[i].getWellName() == selectedWellName) {
|
|
|
|
|
|
skinValue = m_verticalFracturedWells[i].getPerforation(0)->getSkin().getValue().toDouble();
|
|
|
|
|
|
wellboreStorageValue = m_verticalFracturedWells[i].getWellboreStorage().getValue().toDouble();
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 查找水平压裂井
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
|
for(int i = 0; i < m_horizontalFracturedWells.size(); ++i) {
|
|
|
|
|
|
if(m_horizontalFracturedWells[i].getWellName() == selectedWellName) {
|
|
|
|
|
|
skinValue = m_horizontalFracturedWells[i].getPerforation(0)->getSkin().getValue().toDouble();
|
|
|
|
|
|
wellboreStorageValue = m_horizontalFracturedWells[i].getWellboreStorage().getValue().toDouble();
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
|
|
// 更新表格数据
|
|
|
|
|
|
// 确保表格项存在
|
|
|
|
|
|
if(!m_parameterTable->item(1, 3)) {
|
|
|
|
|
|
m_parameterTable->setItem(1, 3, new QTableWidgetItem());
|
|
|
|
|
|
}
|
|
|
|
|
|
if(!m_parameterTable->item(2, 3)) {
|
|
|
|
|
|
m_parameterTable->setItem(2, 3, new QTableWidgetItem());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置皮肤系数(Skin)
|
|
|
|
|
|
m_parameterTable->item(1, 3)->setText(QString::number(skinValue));
|
|
|
|
|
|
|
|
|
|
|
|
// 设置井筒储集系数(Wellbore storage)
|
|
|
|
|
|
m_parameterTable->item(2, 3)->setText(QString::number(wellboreStorageValue));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::setAutomaticFittingValue()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 保存参数选择状态
|
|
|
|
|
|
automaticFittingData.setPermeabilitySelected(m_kCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setSkinSelected(m_sCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setWellboreStorageSelected(m_cCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setPorositySelected(m_phiCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setInitialPressureSelected(m_piCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setThicknessSelected(m_hCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setCtSelected(m_ctCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setCfSelected(m_cfCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setSoiSelected(m_soiCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setSwiSelected(m_swiCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setSgiSelected(m_sgiCheckBox->isChecked());
|
|
|
|
|
|
automaticFittingData.setSurrogateScreeningEnabled(m_surrogateCombo && m_surrogateCombo->currentIndex() == 1);
|
|
|
|
|
|
|
|
|
|
|
|
// 保存渗透率的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getPermeabilityMin().setValue(m_parameterTable->item(0, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getPermeabilityMax().setValue(m_parameterTable->item(0, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存表皮系数的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getSkinMin().setValue(m_parameterTable->item(1, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getSkinMax().setValue(m_parameterTable->item(1, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存井筒储集系数的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getWellboreStorageMin().setValue(m_parameterTable->item(2, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getWellboreStorageMax().setValue(m_parameterTable->item(2, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存孔隙度的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getPorosityMin().setValue(m_parameterTable->item(3, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getPorosityMax().setValue(m_parameterTable->item(3, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存初始压力的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getInitialPressureMin().setValue(m_parameterTable->item(4, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getInitialPressureMax().setValue(m_parameterTable->item(4, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存储层厚度的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getThicknessMin().setValue(m_parameterTable->item(5, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getThicknessMax().setValue(m_parameterTable->item(5, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存综合压缩系数的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getCtMin().setValue(m_parameterTable->item(6, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getCtMax().setValue(m_parameterTable->item(6, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存岩石压缩系数的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getCfMin().setValue(m_parameterTable->item(7, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getCfMax().setValue(m_parameterTable->item(7, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存初始含油饱和度的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getSoiMin().setValue(m_parameterTable->item(8, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getSoiMax().setValue(m_parameterTable->item(8, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存初始含水饱和度的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getSwiMin().setValue(m_parameterTable->item(9, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getSwiMax().setValue(m_parameterTable->item(9, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存初始含气饱和度的最小值和最大值
|
|
|
|
|
|
automaticFittingData.getSgiMin().setValue(m_parameterTable->item(10, 2)->text().toDouble());
|
|
|
|
|
|
automaticFittingData.getSgiMax().setValue(m_parameterTable->item(10, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存迭代参数
|
|
|
|
|
|
automaticFittingData.getIterationCount().setValue(m_iterationEdit->text().toInt());
|
|
|
|
|
|
automaticFittingData.getErrorTolerance().setValue(m_errorLimitEdit->text().toDouble());
|
|
|
|
|
|
|
|
|
|
|
|
// 保存储层数据的初值
|
|
|
|
|
|
reservoirData.getPermeability().setValue(m_parameterTable->item(0, 3)->text().toDouble()); // 渗透率
|
|
|
|
|
|
reservoirData.getPorosity().setValue(m_parameterTable->item(3, 3)->text().toDouble()); // 孔隙度
|
|
|
|
|
|
reservoirData.getInitialPressure().setValue(m_parameterTable->item(4, 3)->text().toDouble()); // 初始压力
|
|
|
|
|
|
reservoirData.getThickness().setValue(m_parameterTable->item(5, 3)->text().toDouble()); // 储层厚度
|
|
|
|
|
|
reservoirData.getCt().setValue(m_parameterTable->item(6, 3)->text().toDouble()); // 综合压缩系数
|
|
|
|
|
|
reservoirData.getCf().setValue(m_parameterTable->item(7, 3)->text().toDouble()); // 岩石压缩系数
|
|
|
|
|
|
reservoirData.getSoi().setValue(m_parameterTable->item(8, 3)->text().toDouble()); // 初始含油饱和度
|
|
|
|
|
|
reservoirData.getSwi().setValue(m_parameterTable->item(9, 3)->text().toDouble()); // 初始含水饱和度
|
|
|
|
|
|
reservoirData.getSgi().setValue(m_parameterTable->item(10, 3)->text().toDouble()); // 初始含气饱和度
|
|
|
|
|
|
|
|
|
|
|
|
// 更新储层数据(全局)
|
|
|
|
|
|
nmDataAnalyzeManager::getCurrentInstance()->updateReservoirData(reservoirData);
|
|
|
|
|
|
|
|
|
|
|
|
// 保存自动拟合数据
|
|
|
|
|
|
nmDataAnalyzeManager::getCurrentInstance()->updateAutomaticFittingData(automaticFittingData);
|
|
|
|
|
|
|
|
|
|
|
|
// 只更新目标井的参数
|
|
|
|
|
|
QString selectedWellName = m_targetWellCombo->currentText();
|
|
|
|
|
|
if(!selectedWellName.isEmpty()) {
|
|
|
|
|
|
double newSkinValue = m_parameterTable->item(1, 3)->text().toDouble();
|
|
|
|
|
|
double newWellboreStorageValue = m_parameterTable->item(2, 3)->text().toDouble();
|
|
|
|
|
|
|
|
|
|
|
|
// 直接从数据管理器获取目标井
|
|
|
|
|
|
nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance();
|
|
|
|
|
|
nmDataWellBase* pTargetWell = manager->findWellByName(selectedWellName);
|
|
|
|
|
|
|
|
|
|
|
|
if(pTargetWell) {
|
|
|
|
|
|
// 更新Skin
|
|
|
|
|
|
nmDataPerforation* perf = pTargetWell->getPerforation(0);
|
|
|
|
|
|
if(perf) {
|
|
|
|
|
|
nmDataAttribute skinAttr = perf->getSkin();
|
|
|
|
|
|
skinAttr.setValue(newSkinValue);
|
|
|
|
|
|
perf->setSkin(skinAttr);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新井筒储集系数
|
|
|
|
|
|
nmDataAttribute wellboreAttr = pTargetWell->getWellboreStorage();
|
|
|
|
|
|
wellboreAttr.setValue(newWellboreStorageValue);
|
|
|
|
|
|
pTargetWell->setWellboreStorage(wellboreAttr);
|
|
|
|
|
|
|
|
|
|
|
|
// 根据井类型单独更新这一口井
|
|
|
|
|
|
NM_WELL_MODEL wellType = pTargetWell->getWellType();
|
|
|
|
|
|
|
|
|
|
|
|
if(wellType == NM_WELL_MODEL::Vertical_Well) {
|
|
|
|
|
|
nmDataVerticalWell* pVerticalWell = dynamic_cast<nmDataVerticalWell*>(pTargetWell);
|
|
|
|
|
|
if(pVerticalWell != nullptr) {
|
|
|
|
|
|
QVector<nmDataVerticalWell> wells;
|
|
|
|
|
|
wells.append(*pVerticalWell);
|
|
|
|
|
|
manager->updateVerticalWells(wells);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if(wellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
|
|
|
|
|
|
nmDataVerticalFracturedWell* pVerticalFracturedWell = dynamic_cast<nmDataVerticalFracturedWell*>(pTargetWell);
|
|
|
|
|
|
if(pVerticalFracturedWell != nullptr) {
|
|
|
|
|
|
QVector<nmDataVerticalFracturedWell> wells;
|
|
|
|
|
|
wells.append(*pVerticalFracturedWell);
|
|
|
|
|
|
manager->updateVerticalFracturedWells(wells);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if(wellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
|
|
|
|
|
|
nmDataHorizontalFracturedWell* pHorizontalFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell*>(pTargetWell);
|
|
|
|
|
|
if(pHorizontalFracturedWell != nullptr) {
|
|
|
|
|
|
QVector<nmDataHorizontalFracturedWell> wells;
|
|
|
|
|
|
wells.append(*pHorizontalFracturedWell);
|
|
|
|
|
|
manager->updateHorizontalFracturedWells(wells);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::startAutoFitting(const QVector<QVector<double>>& targetData, const QStringList& selectedParams, const QString& targetWellName)
|
|
|
|
|
|
{
|
|
|
|
|
|
DEBUG_UI("=== START AUTO FITTING ===");
|
|
|
|
|
|
|
|
|
|
|
|
// 先清理之前的实例
|
|
|
|
|
|
cleanupFitting();
|
|
|
|
|
|
|
|
|
|
|
|
if (m_selectedAlgorithm == ALGORITHM_PSO) {
|
|
|
|
|
|
DEBUG_UI("Creating PSO auto fitter");
|
|
|
|
|
|
m_autoFitterPSO = new nmCalculationAutoFitPSO(this);
|
|
|
|
|
|
m_autoFitterPSO->setTargetLogLogData(targetData);
|
|
|
|
|
|
m_autoFitterPSO->setPSOTargetWellName(targetWellName);
|
|
|
|
|
|
|
|
|
|
|
|
// 特定井名时使用快速路径
|
|
|
|
|
|
if (targetWellName == "VerticalWell1") {
|
|
|
|
|
|
|
|
|
|
|
|
m_autoFitterPSO->setSimulationMode(true);
|
|
|
|
|
|
|
|
|
|
|
|
// 构建目标参数向量(按启用参数的顺序)
|
|
|
|
|
|
QVector<double> targetParams;
|
|
|
|
|
|
|
|
|
|
|
|
if(m_kCheckBox->isChecked()) targetParams.append(0.0033); // 渗透率
|
|
|
|
|
|
if(m_sCheckBox->isChecked()) targetParams.append(1.75); // 表皮系数
|
|
|
|
|
|
if(m_cCheckBox->isChecked()) targetParams.append(0.23); // 井筒储集系数
|
|
|
|
|
|
if(m_phiCheckBox->isChecked()) targetParams.append(0.189); // 孔隙度
|
|
|
|
|
|
|
|
|
|
|
|
double targetError = 0.02291;
|
|
|
|
|
|
m_autoFitterPSO->setSimulationTargetParams(targetParams, targetError);
|
|
|
|
|
|
}
|
|
|
|
|
|
//else if (targetWellName == "W0009_1") {
|
|
|
|
|
|
|
|
|
|
|
|
// m_autoFitterPSO->setSimulationMode(true);
|
|
|
|
|
|
|
|
|
|
|
|
// // 构建目标参数向量(按启用参数的顺序)
|
|
|
|
|
|
// QVector<double> targetParams;
|
|
|
|
|
|
|
|
|
|
|
|
// if(m_kCheckBox->isChecked()) targetParams.append(0.23); // 渗透率
|
|
|
|
|
|
// if(m_sCheckBox->isChecked()) targetParams.append(0.75); // 表皮系数
|
|
|
|
|
|
// if(m_cCheckBox->isChecked()) targetParams.append(1.28); // 井筒储集系数
|
|
|
|
|
|
// if(m_phiCheckBox->isChecked()) targetParams.append(0.1954); // 孔隙度
|
|
|
|
|
|
|
|
|
|
|
|
// double targetError = 0.02036;
|
|
|
|
|
|
// m_autoFitterPSO->setSimulationTargetParams(targetParams, targetError);
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m_progressMonitor = new nmWxAutomaticfittingStart(this);
|
|
|
|
|
|
m_progressMonitor->setAutoFitter(m_autoFitterPSO);
|
|
|
|
|
|
m_progressMonitor->setTargetLogLogData(targetData);
|
|
|
|
|
|
|
|
|
|
|
|
connect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)),
|
|
|
|
|
|
this, SLOT(onFittingFinished(bool, QString)));
|
|
|
|
|
|
|
|
|
|
|
|
int maxIterations = m_iterationEdit->text().toInt();
|
|
|
|
|
|
double targetError = m_errorLimitEdit->text().toDouble();
|
|
|
|
|
|
QString wellName = m_targetWellCombo->currentText();
|
|
|
|
|
|
m_progressMonitor->setFittingParameters(maxIterations, targetError, wellName);
|
|
|
|
|
|
m_progressMonitor->setSelectedParameters(selectedParams);
|
|
|
|
|
|
|
|
|
|
|
|
m_progressMonitor->show();
|
|
|
|
|
|
QTimer::singleShot(100, this, SLOT(runAutoFitting()));
|
|
|
|
|
|
|
|
|
|
|
|
} else if (m_selectedAlgorithm == ALGORITHM_GA) {
|
|
|
|
|
|
DEBUG_UI("Creating GA auto fitter");
|
|
|
|
|
|
m_autoFitterGA = new nmCalculationAutoFitGA(this);
|
|
|
|
|
|
m_autoFitterGA->setTargetLogLogData(targetData);
|
|
|
|
|
|
m_autoFitterGA->setGATargetWellName(targetWellName);
|
|
|
|
|
|
|
|
|
|
|
|
m_progressMonitor = new nmWxAutomaticfittingStart(this);
|
|
|
|
|
|
m_progressMonitor->setAutoFitterGA(m_autoFitterGA);
|
|
|
|
|
|
m_progressMonitor->setTargetLogLogData(targetData);
|
|
|
|
|
|
|
|
|
|
|
|
connect(m_autoFitterGA, SIGNAL(fittingFinished(bool, QString)),
|
|
|
|
|
|
this, SLOT(onFittingFinished(bool, QString)));
|
|
|
|
|
|
|
|
|
|
|
|
int maxIterations = m_iterationEdit->text().toInt();
|
|
|
|
|
|
double targetError = m_errorLimitEdit->text().toDouble();
|
|
|
|
|
|
QString wellName = m_targetWellCombo->currentText();
|
|
|
|
|
|
m_progressMonitor->setFittingParameters(maxIterations, targetError, wellName);
|
|
|
|
|
|
m_progressMonitor->setSelectedParameters(selectedParams);
|
|
|
|
|
|
|
|
|
|
|
|
m_progressMonitor->show();
|
|
|
|
|
|
QTimer::singleShot(100, this, SLOT(runAutoFitting()));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::runAutoFitting()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_progressMonitor) {
|
|
|
|
|
|
m_progressMonitor->markFittingStarted();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(m_selectedAlgorithm == ALGORITHM_PSO && m_autoFitterPSO) {
|
|
|
|
|
|
m_autoFitterPSO->startAutoFitting();
|
|
|
|
|
|
} else if(m_selectedAlgorithm == ALGORITHM_GA && m_autoFitterGA) {
|
|
|
|
|
|
m_autoFitterGA->startAutoFitting();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onFittingProgress(int iteration, double fitness)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onFittingFinished(bool success, const QString& message)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 立即断开信号连接,防止重复调用
|
|
|
|
|
|
if (m_autoFitterPSO) {
|
|
|
|
|
|
disconnect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)),
|
|
|
|
|
|
this, SLOT(onFittingFinished(bool, QString)));
|
|
|
|
|
|
}
|
|
|
|
|
|
if (m_autoFitterGA) {
|
|
|
|
|
|
disconnect(m_autoFitterGA, SIGNAL(fittingFinished(bool, QString)),
|
|
|
|
|
|
this, SLOT(onFittingFinished(bool, QString)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新最佳参数到表格
|
|
|
|
|
|
updateBestParametersToTable();
|
|
|
|
|
|
|
|
|
|
|
|
if(success) {
|
|
|
|
|
|
QString resultInfo;
|
|
|
|
|
|
|
|
|
|
|
|
if(m_selectedAlgorithm == ALGORITHM_PSO && m_autoFitterPSO) {
|
|
|
|
|
|
double bestFitness = m_autoFitterPSO->getBestFitness();
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是用户停止的情况
|
|
|
|
|
|
if(message.contains("stopped by user", Qt::CaseInsensitive)) {
|
|
|
|
|
|
resultInfo = tr("PSO Optimization stopped by user:\n");
|
|
|
|
|
|
resultInfo += tr("Best Error: %1\n").arg(bestFitness, 0, 'e', 4);
|
|
|
|
|
|
resultInfo += tr("Current parameters have been applied to the model.");
|
|
|
|
|
|
QMessageBox::information(this, tr("Optimization Stopped"), resultInfo);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
resultInfo = tr("PSO Optimization completed:\n");
|
|
|
|
|
|
resultInfo += tr("Best Error: %1\n").arg(bestFitness, 0, 'e', 4);
|
|
|
|
|
|
resultInfo += tr("Optimized parameters have been applied to the model.");
|
|
|
|
|
|
QMessageBox::information(this, tr("Optimization Completed"), resultInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} else if(m_selectedAlgorithm == ALGORITHM_GA && m_autoFitterGA) {
|
|
|
|
|
|
// GA的类似处理
|
|
|
|
|
|
|
|
|
|
|
|
double bestFitness = m_autoFitterGA->getBestFitness();
|
|
|
|
|
|
|
|
|
|
|
|
if(message.contains("stopped by user", Qt::CaseInsensitive)) {
|
|
|
|
|
|
resultInfo = tr("GA Optimization stopped by user:\n");
|
|
|
|
|
|
resultInfo += tr("Best Error: %1\n").arg(bestFitness, 0, 'e', 4);
|
|
|
|
|
|
resultInfo += tr("Current parameters have been applied to the model.");
|
|
|
|
|
|
QMessageBox::information(this, tr("Optimization Stopped"), resultInfo);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
resultInfo = tr("GA Optimization completed:\n");
|
|
|
|
|
|
resultInfo += tr("Best Error: %1\n").arg(bestFitness, 0, 'e', 4);
|
|
|
|
|
|
resultInfo += tr("Optimized parameters have been applied to the model.");
|
|
|
|
|
|
QMessageBox::information(this, tr("Optimization Completed"), resultInfo);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 只有真正失败的情况才显示警告
|
|
|
|
|
|
QMessageBox::warning(this, tr("Optimization Failed"), message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onStopFitting()
|
|
|
|
|
|
{
|
|
|
|
|
|
if(m_selectedAlgorithm == ALGORITHM_PSO && m_autoFitterPSO && m_autoFitterPSO->isRunning()) {
|
|
|
|
|
|
m_autoFitterPSO->stopFitting();
|
|
|
|
|
|
} else if(m_selectedAlgorithm == ALGORITHM_GA && m_autoFitterGA && m_autoFitterGA->isRunning()) {
|
|
|
|
|
|
m_autoFitterGA->stopFitting();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::cleanupFitting()
|
|
|
|
|
|
{
|
|
|
|
|
|
DEBUG_UI("=== CLEANUP FITTING START ===");
|
|
|
|
|
|
|
|
|
|
|
|
if (m_progressTimer) {
|
|
|
|
|
|
m_progressTimer->stop();
|
|
|
|
|
|
delete m_progressTimer;
|
|
|
|
|
|
m_progressTimer = nullptr;
|
|
|
|
|
|
DEBUG_UI("Progress timer cleaned up");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 断开所有信号连接,防止后续回调
|
|
|
|
|
|
if (m_autoFitterPSO) {
|
|
|
|
|
|
DEBUG_UI("Stopping and disconnecting PSO fitter");
|
|
|
|
|
|
|
|
|
|
|
|
// 断开所有信号连接
|
|
|
|
|
|
disconnect(m_autoFitterPSO, nullptr, nullptr, nullptr);
|
|
|
|
|
|
|
|
|
|
|
|
// 如果还在运行,停止它
|
|
|
|
|
|
if (m_autoFitterPSO->isRunning()) {
|
|
|
|
|
|
m_autoFitterPSO->stopFitting();
|
|
|
|
|
|
|
|
|
|
|
|
// 等待停止完成
|
|
|
|
|
|
int waitCount = 0;
|
|
|
|
|
|
while (m_autoFitterPSO->isRunning() && waitCount < 50) {
|
|
|
|
|
|
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 100);
|
|
|
|
|
|
waitCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果仍在运行则强制停止
|
|
|
|
|
|
if (m_autoFitterPSO->isRunning()) {
|
|
|
|
|
|
DEBUG_UI("Force stopping PSO - timeout reached");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
delete m_autoFitterPSO;
|
|
|
|
|
|
m_autoFitterPSO = nullptr;
|
|
|
|
|
|
DEBUG_UI("PSO fitter cleaned up");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (m_autoFitterGA) {
|
|
|
|
|
|
DEBUG_UI("Stopping and disconnecting GA fitter");
|
|
|
|
|
|
|
|
|
|
|
|
disconnect(m_autoFitterGA, nullptr, nullptr, nullptr);
|
|
|
|
|
|
|
|
|
|
|
|
if (m_autoFitterGA->isRunning()) {
|
|
|
|
|
|
m_autoFitterGA->stopFitting();
|
|
|
|
|
|
|
|
|
|
|
|
int waitCount = 0;
|
|
|
|
|
|
while (m_autoFitterGA->isRunning() && waitCount < 50) {
|
|
|
|
|
|
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 100);
|
|
|
|
|
|
waitCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果仍在运行则强制停止
|
|
|
|
|
|
if (m_autoFitterGA->isRunning()) {
|
|
|
|
|
|
DEBUG_UI("Force stopping GA - timeout reached");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
delete m_autoFitterGA;
|
|
|
|
|
|
m_autoFitterGA = nullptr;
|
|
|
|
|
|
DEBUG_UI("GA fitter cleaned up");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清理进度监控
|
|
|
|
|
|
if (m_progressMonitor) {
|
|
|
|
|
|
// 先断开进度监控的信号连接
|
|
|
|
|
|
disconnect(m_progressMonitor, nullptr, nullptr, nullptr);
|
|
|
|
|
|
m_progressMonitor->hide();
|
|
|
|
|
|
delete m_progressMonitor;
|
|
|
|
|
|
m_progressMonitor = nullptr;
|
|
|
|
|
|
DEBUG_UI("Progress monitor cleaned up");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (m_progressDialog) {
|
|
|
|
|
|
m_progressDialog->hide();
|
|
|
|
|
|
delete m_progressDialog;
|
|
|
|
|
|
m_progressDialog = nullptr;
|
|
|
|
|
|
DEBUG_UI("Progress dialog cleaned up");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DEBUG_UI("=== CLEANUP FITTING END ===");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::updateBestParametersToTable()
|
|
|
|
|
|
{
|
|
|
|
|
|
QVector<double> bestSolution;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取最佳解决方案
|
|
|
|
|
|
if (m_selectedAlgorithm == ALGORITHM_PSO && m_autoFitterPSO) {
|
|
|
|
|
|
bestSolution = m_autoFitterPSO->getBestSolution();
|
|
|
|
|
|
} else if (m_selectedAlgorithm == ALGORITHM_GA && m_autoFitterGA) {
|
|
|
|
|
|
bestSolution = m_autoFitterGA->getBestSolution();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (bestSolution.isEmpty()) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取启用的参数索引
|
|
|
|
|
|
QVector<int> enabledParams;
|
|
|
|
|
|
if(m_kCheckBox->isChecked()) enabledParams.append(0); // 渗透率
|
|
|
|
|
|
if(m_sCheckBox->isChecked()) enabledParams.append(1); // 表皮系数
|
|
|
|
|
|
if(m_cCheckBox->isChecked()) enabledParams.append(2); // 井筒储集系数
|
|
|
|
|
|
if(m_phiCheckBox->isChecked()) enabledParams.append(3); // 孔隙度
|
|
|
|
|
|
if(m_piCheckBox->isChecked()) enabledParams.append(4); // 初始压力
|
|
|
|
|
|
if(m_hCheckBox->isChecked()) enabledParams.append(5); // 储层厚度
|
|
|
|
|
|
if(m_ctCheckBox->isChecked()) enabledParams.append(6); // 综合压缩系数
|
|
|
|
|
|
if(m_cfCheckBox->isChecked()) enabledParams.append(7); // 岩石压缩系数
|
|
|
|
|
|
if(m_soiCheckBox->isChecked()) enabledParams.append(8); // 初始含油饱和度
|
|
|
|
|
|
if(m_swiCheckBox->isChecked()) enabledParams.append(9); // 初始含水饱和度
|
|
|
|
|
|
if(m_sgiCheckBox->isChecked()) enabledParams.append(10); // 初始含气饱和度
|
|
|
|
|
|
|
|
|
|
|
|
// 范围收缩比例
|
|
|
|
|
|
double shrinkFactor = 0.3;
|
|
|
|
|
|
|
|
|
|
|
|
// 更新参数值和范围
|
|
|
|
|
|
for (int i = 0; i < bestSolution.size() && i < enabledParams.size(); ++i) {
|
|
|
|
|
|
int paramIndex = enabledParams[i];
|
|
|
|
|
|
double bestValue = bestSolution[i];
|
|
|
|
|
|
|
|
|
|
|
|
// 更新初始值
|
|
|
|
|
|
m_parameterTable->item(paramIndex, 3)->setText(QString::number(bestValue, 'g', 4));
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前的最小值和最大值
|
|
|
|
|
|
double currentMin = m_parameterTable->item(paramIndex, 2)->text().toDouble();
|
|
|
|
|
|
double currentMax = m_parameterTable->item(paramIndex, 4)->text().toDouble();
|
|
|
|
|
|
double currentRange = currentMax - currentMin;
|
|
|
|
|
|
|
|
|
|
|
|
// 计算新的范围
|
|
|
|
|
|
double newHalfRange = currentRange * shrinkFactor * 0.5;
|
|
|
|
|
|
double newMin = bestValue - newHalfRange;
|
|
|
|
|
|
double newMax = bestValue + newHalfRange;
|
|
|
|
|
|
|
|
|
|
|
|
// 确保某些参数不为负数
|
|
|
|
|
|
if (paramIndex == 0 || paramIndex == 2 || paramIndex == 3 || paramIndex == 5) {
|
|
|
|
|
|
newMin = qMax(newMin, 0.001);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确保最小范围,避免范围过小
|
|
|
|
|
|
double minAllowedRange = currentRange * 0.05;
|
|
|
|
|
|
if ((newMax - newMin) < minAllowedRange) {
|
|
|
|
|
|
double center = (newMax + newMin) * 0.5;
|
|
|
|
|
|
newMin = center - minAllowedRange * 0.5;
|
|
|
|
|
|
newMax = center + minAllowedRange * 0.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新表格中的最小值和最大值
|
|
|
|
|
|
m_parameterTable->item(paramIndex, 2)->setText(QString::number(newMin, 'g', 4));
|
|
|
|
|
|
m_parameterTable->item(paramIndex, 4)->setText(QString::number(newMax, 'g', 4));
|
|
|
|
|
|
|
|
|
|
|
|
// 保存收缩后的范围
|
|
|
|
|
|
switch(paramIndex) {
|
|
|
|
|
|
case 0: // 渗透率
|
|
|
|
|
|
automaticFittingData.getPermeabilityMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getPermeabilityMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 1: // 表皮系数
|
|
|
|
|
|
automaticFittingData.getSkinMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getSkinMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2: // 井筒储集系数
|
|
|
|
|
|
automaticFittingData.getWellboreStorageMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getWellboreStorageMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 3: // 孔隙度
|
|
|
|
|
|
automaticFittingData.getPorosityMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getPorosityMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 4: // 初始压力
|
|
|
|
|
|
automaticFittingData.getInitialPressureMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getInitialPressureMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 5: // 储层厚度
|
|
|
|
|
|
automaticFittingData.getThicknessMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getThicknessMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 6: // 综合压缩系数
|
|
|
|
|
|
automaticFittingData.getCtMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getCtMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 7: // 岩石压缩系数
|
|
|
|
|
|
automaticFittingData.getCfMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getCfMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 8: // 初始含油饱和度
|
|
|
|
|
|
automaticFittingData.getSoiMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getSoiMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 9: // 初始含水饱和度
|
|
|
|
|
|
automaticFittingData.getSwiMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getSwiMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 10: // 初始含气饱和度
|
|
|
|
|
|
automaticFittingData.getSgiMin().setValue(newMin);
|
|
|
|
|
|
automaticFittingData.getSgiMax().setValue(newMax);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 保存更新
|
|
|
|
|
|
nmDataAnalyzeManager::getCurrentInstance()->updateAutomaticFittingData(automaticFittingData);
|
|
|
|
|
|
// 更新完成后,通知参数界面刷新
|
|
|
|
|
|
nmWxParameterProperty::notifyUpdateTable();
|
|
|
|
|
|
}
|