#include "nmWxAutomaticFitting.h" #include "nmCalculationAutoFitPSO.h" #include "nmWxAutomaticfittingStart.h" #include "nmWxParameterProperty.h" #include "nmDataAnalyzeManager.h" #include #include #ifdef Q_OS_WIN #include #define DEBUG_UI(msg) OutputDebugStringA(QString("[UI] %1\n").arg(msg).toLocal8Bit().data()) #endif nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent) : QDialog(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 listWellData = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList(); // 遍历并分类井数据 foreach (nmDataWellBase* well, listWellData) { if (auto vfWell = dynamic_cast(well)) { m_verticalFracturedWells.append(*vfWell); } else if (auto hfWell = dynamic_cast(well)) { m_horizontalFracturedWells.append(*hfWell); } else if (auto vWell = dynamic_cast(well)) { m_verticalWells.append(*vWell); } else if (auto hWell = dynamic_cast(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); } } // 连接选择改变信号 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() { // 更新反选逻辑,包含所有参数 m_kCheckBox->setChecked(!m_kCheckBox->isChecked()); m_sCheckBox->setChecked(!m_sCheckBox->isChecked()); m_cCheckBox->setChecked(!m_cCheckBox->isChecked()); m_phiCheckBox->setChecked(!m_phiCheckBox->isChecked()); m_piCheckBox->setChecked(!m_piCheckBox->isChecked()); m_hCheckBox->setChecked(!m_hCheckBox->isChecked()); m_ctCheckBox->setChecked(!m_ctCheckBox->isChecked()); m_cfCheckBox->setChecked(!m_cfCheckBox->isChecked()); m_soiCheckBox->setChecked(!m_soiCheckBox->isChecked()); m_swiCheckBox->setChecked(!m_swiCheckBox->isChecked()); m_sgiCheckBox->setChecked(!m_sgiCheckBox->isChecked()); } void nmWxAutomaticFitting::onAlgorithmChanged(int index) { m_selectedAlgorithm = static_cast(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 > 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 > 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(pTargetWell); if(pVerticalWell != nullptr) { QVector wells; wells.append(*pVerticalWell); manager->updateVerticalWells(wells); } } else if(wellType == NM_WELL_MODEL::Vertical_Fractured_Well) { nmDataVerticalFracturedWell* pVerticalFracturedWell = dynamic_cast(pTargetWell); if(pVerticalFracturedWell != nullptr) { QVector wells; wells.append(*pVerticalFracturedWell); manager->updateVerticalFracturedWells(wells); } } else if(wellType == NM_WELL_MODEL::Horizontal_Fractured_Well) { nmDataHorizontalFracturedWell* pHorizontalFracturedWell = dynamic_cast(pTargetWell); if(pHorizontalFracturedWell != nullptr) { QVector wells; wells.append(*pHorizontalFracturedWell); manager->updateHorizontalFracturedWells(wells); } } } } } void nmWxAutomaticFitting::startAutoFitting(const QVector>& 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 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 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 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 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(); }