|
|
|
|
@ -119,7 +119,7 @@ void nmWxAutomaticFitting::updateParameterVisibility(QTableWidget* table, NM_SOL
|
|
|
|
|
setParameterRowVisible(table, row, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setParameterRowVisible(table, 4, eType == SMT_Oil_Water_TwoPhase); // Swi
|
|
|
|
|
setParameterRowVisible(table, 4, false); // 原含水饱和度索引保留,不再参与拟合。
|
|
|
|
|
|
|
|
|
|
// Dfc 只属于垂直压裂井和多段压裂水平井。普通井隐藏并取消勾选,
|
|
|
|
|
// 防止切换目标井后不可见的裂缝参数仍进入拟合参数向量。
|
|
|
|
|
@ -140,16 +140,16 @@ void nmWxAutomaticFitting::updateParameterVisibility(QTableWidget* table, NM_SOL
|
|
|
|
|
renumberVisibleParameterRows(table);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取参数的系统物理边界,并为 Swi 叠加当前储层饱和度约束。
|
|
|
|
|
// 获取可拟合参数的系统物理边界,索引 4 不再作为拟合参数。
|
|
|
|
|
bool nmWxAutomaticFitting::getPhysicalParameterRange(int parameterIndex,
|
|
|
|
|
double& minValue, double& maxValue)
|
|
|
|
|
{
|
|
|
|
|
static const char* parameterNames[] = {
|
|
|
|
|
"Result_K", "Result_W_Skin", "Result_W_C", "Result_phi",
|
|
|
|
|
"Result_Swi", "Result_W_Dfc",
|
|
|
|
|
"", "Result_W_Dfc",
|
|
|
|
|
"W_FractureHalfLength"
|
|
|
|
|
};
|
|
|
|
|
if(parameterIndex < 0 || parameterIndex >= 7) {
|
|
|
|
|
if(parameterIndex < 0 || parameterIndex >= 7 || parameterIndex == 4) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -157,20 +157,12 @@ bool nmWxAutomaticFitting::getPhysicalParameterRange(int parameterIndex,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(parameterIndex == 4) {
|
|
|
|
|
double soi = reservoirData.getSoi().getValue().toDouble();
|
|
|
|
|
double sgi = reservoirData.getSgi().getValue().toDouble();
|
|
|
|
|
if(nmAutoFitUiIsFinite(soi) && nmAutoFitUiIsFinite(sgi)) {
|
|
|
|
|
if(soi < 0.0 || sgi < 0.0 || soi + sgi > 1.0) {
|
|
|
|
|
// Soi+Sgi 超过 1 时没有可行的 Swi,固定到物理下限,避免继续搜索非法区间。
|
|
|
|
|
minValue = 0.0;
|
|
|
|
|
maxValue = 0.0;
|
|
|
|
|
} else {
|
|
|
|
|
maxValue = qMin(maxValue, 1.0 - soi - sgi);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 自动拟合的表皮搜索下界固定为 0,自动范围和手工输入校验使用同一边界。
|
|
|
|
|
if(parameterIndex == 1) {
|
|
|
|
|
minValue = 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -214,10 +206,6 @@ void nmWxAutomaticFitting::setParameterRange(int parameterIndex,
|
|
|
|
|
automaticFittingData.getPorosityMin().setValue(minValue);
|
|
|
|
|
automaticFittingData.getPorosityMax().setValue(maxValue);
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
automaticFittingData.getSwiMin().setValue(minValue);
|
|
|
|
|
automaticFittingData.getSwiMax().setValue(maxValue);
|
|
|
|
|
break;
|
|
|
|
|
case 5:
|
|
|
|
|
automaticFittingData.getFractureConductivityMin().setValue(minValue);
|
|
|
|
|
automaticFittingData.getFractureConductivityMax().setValue(maxValue);
|
|
|
|
|
@ -233,8 +221,8 @@ void nmWxAutomaticFitting::setParameterRange(int parameterIndex,
|
|
|
|
|
m_updatingParameterRanges = wasUpdatingRanges;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据当前初值生成建议搜索范围,并始终截断在系统物理边界内。skin 使用
|
|
|
|
|
// 加减固定宽度,其余正值参数使用倍率范围;该规则在首次加载和拟合完成后复用。
|
|
|
|
|
// 根据当前初值生成建议搜索范围,并始终截断在拟合边界内。skin 下界为 0,
|
|
|
|
|
// 上界按初值加固定宽度,其余正值参数使用倍率范围;每次打开窗口和拟合完成后复用。
|
|
|
|
|
void nmWxAutomaticFitting::updateRangeForParameter(int parameterIndex,
|
|
|
|
|
double centerValue)
|
|
|
|
|
{
|
|
|
|
|
@ -271,19 +259,14 @@ void nmWxAutomaticFitting::updateRangeForParameter(int parameterIndex,
|
|
|
|
|
double newMax = physicalMax;
|
|
|
|
|
if(parameterIndex == 1) {
|
|
|
|
|
const double skinHalfRange = 10.0;
|
|
|
|
|
newMin = qMax(physicalMin, reference - skinHalfRange);
|
|
|
|
|
newMin = physicalMin;
|
|
|
|
|
newMax = qMin(physicalMax, reference + skinHalfRange);
|
|
|
|
|
} else if(reference > 0.0
|
|
|
|
|
&& !(parameterIndex == 4 && centerValue <= 0.0)
|
|
|
|
|
&& !(parameterIndex == 5 && centerValue <= 0.0)) {
|
|
|
|
|
const double lowerFactor = 0.1;
|
|
|
|
|
const double upperFactor = 10.0;
|
|
|
|
|
newMin = qMax(physicalMin, reference * lowerFactor);
|
|
|
|
|
newMax = qMin(physicalMax, reference * upperFactor);
|
|
|
|
|
} else if(parameterIndex == 4) {
|
|
|
|
|
// 没有可靠 Swi 初值时,不把搜索范围压缩到零附近。
|
|
|
|
|
newMin = physicalMin;
|
|
|
|
|
newMax = physicalMax;
|
|
|
|
|
} else if(parameterIndex == 5) {
|
|
|
|
|
// Dfc=0 表示无限导流,不存在以零为中心的连续倍率范围。
|
|
|
|
|
newMin = physicalMin;
|
|
|
|
|
@ -301,7 +284,7 @@ void nmWxAutomaticFitting::updateRangeForParameter(int parameterIndex,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 首次进入自动范围模式时,按当前表格中的初值为所有参数建立建议范围。
|
|
|
|
|
// 按当前表格中的初值为所有参数建立建议范围,每次打开窗口和拟合完成后共用。
|
|
|
|
|
void nmWxAutomaticFitting::initializeSuggestedParameterRanges()
|
|
|
|
|
{
|
|
|
|
|
if(!m_parameterTable) {
|
|
|
|
|
@ -328,57 +311,6 @@ void nmWxAutomaticFitting::initializeSuggestedParameterRanges()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 校正已保存的范围:保留物理边界内的用户区间,无交集时按当前初值生成兜底区间。
|
|
|
|
|
void nmWxAutomaticFitting::normalizeSavedParameterRanges()
|
|
|
|
|
{
|
|
|
|
|
if(!m_parameterTable) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(int parameterIndex = 0; parameterIndex < 7; ++parameterIndex) {
|
|
|
|
|
QTableWidgetItem* minItem = m_parameterTable->item(parameterIndex, 2);
|
|
|
|
|
QTableWidgetItem* maxItem = m_parameterTable->item(parameterIndex, 4);
|
|
|
|
|
QTableWidgetItem* initialItem = m_parameterTable->item(parameterIndex, 3);
|
|
|
|
|
if(!minItem || !maxItem || !initialItem) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double physicalMin = 0.0;
|
|
|
|
|
double physicalMax = 0.0;
|
|
|
|
|
if(!getPhysicalParameterRange(parameterIndex, physicalMin, physicalMax)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
bool savedMinOk = false;
|
|
|
|
|
bool savedMaxOk = false;
|
|
|
|
|
const double savedMin = minItem->text().toDouble(&savedMinOk);
|
|
|
|
|
const double savedMax = maxItem->text().toDouble(&savedMaxOk);
|
|
|
|
|
// 旧项目没有 Dfc 字段时,nmDataAttribute 会表现为 0~0。Dfc=0 在求解器中
|
|
|
|
|
// 表示无限导流,不能作为连续拟合区间,因此按当前压裂井初值重新建范围。
|
|
|
|
|
const bool savedRangeValid = savedMinOk && savedMaxOk
|
|
|
|
|
&& nmAutoFitUiIsFinite(savedMin) && nmAutoFitUiIsFinite(savedMax)
|
|
|
|
|
&& savedMax >= savedMin
|
|
|
|
|
&& !(parameterIndex == 5 && savedMax <= 1.0e-10);
|
|
|
|
|
|
|
|
|
|
if(savedRangeValid && physicalMax >= physicalMin) {
|
|
|
|
|
const double clippedMin = qMax(savedMin, physicalMin);
|
|
|
|
|
const double clippedMax = qMin(savedMax, physicalMax);
|
|
|
|
|
if(clippedMax >= clippedMin) {
|
|
|
|
|
setParameterRange(parameterIndex, clippedMin, clippedMax);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 已保存范围无效或与物理边界无交集时,按数据对象初值重新生成。
|
|
|
|
|
bool initialOk = false;
|
|
|
|
|
const double initialValue = initialItem->text().toDouble(&initialOk);
|
|
|
|
|
if(initialOk && nmAutoFitUiIsFinite(initialValue)) {
|
|
|
|
|
updateRangeForParameter(parameterIndex, initialValue);
|
|
|
|
|
} else if(physicalMax >= physicalMin) {
|
|
|
|
|
setParameterRange(parameterIndex, physicalMin, physicalMax);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 校验当前表格中的参数范围;parameterIndex 为 -1 时检查所有可见参数行。
|
|
|
|
|
bool nmWxAutomaticFitting::validateParameterTable(QString& errorMessage, int parameterIndex)
|
|
|
|
|
{
|
|
|
|
|
@ -393,7 +325,7 @@ bool nmWxAutomaticFitting::validateParameterTable(QString& errorMessage, int par
|
|
|
|
|
|
|
|
|
|
static const char* parameterNames[] = {
|
|
|
|
|
"Permeability", "Skin", "Wellbore storage", "Porosity",
|
|
|
|
|
"Swi", "Fracture conductivity",
|
|
|
|
|
"", "Fracture conductivity",
|
|
|
|
|
"Fracture half length"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@ -484,30 +416,10 @@ nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取数据
|
|
|
|
|
nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance();
|
|
|
|
|
reservoirData = pManager->getReservoirDataCopy();
|
|
|
|
|
automaticFittingData = pManager->getAutomaticFittingDataCopy();
|
|
|
|
|
const bool hasSavedFittingData = pManager && pManager->getAutomaticFittingData() != nullptr;
|
|
|
|
|
// 自动范围始终开启;用户在表格中修改上下限后,itemChanged 会临时切换为手工范围。
|
|
|
|
|
m_autoParameterRanges = true;
|
|
|
|
|
|
|
|
|
|
@ -519,11 +431,8 @@ nmWxAutomaticFitting::nmWxAutomaticFitting(QWidget *parent)
|
|
|
|
|
if(m_targetWellCombo->count() > 0) {
|
|
|
|
|
onWellSelected(0); // 默认选中第一口井
|
|
|
|
|
}
|
|
|
|
|
if(!hasSavedFittingData) {
|
|
|
|
|
initializeSuggestedParameterRanges();
|
|
|
|
|
} else {
|
|
|
|
|
normalizeSavedParameterRanges();
|
|
|
|
|
}
|
|
|
|
|
// 每次打开都按当前模型参数生成范围,不沿用上次的上下限。
|
|
|
|
|
initializeSuggestedParameterRanges();
|
|
|
|
|
m_updatingParameterRanges = false;
|
|
|
|
|
|
|
|
|
|
DEBUG_UI("AutoFitting Constructor completed");
|
|
|
|
|
@ -650,15 +559,7 @@ void nmWxAutomaticFitting::setupParameterTable()
|
|
|
|
|
m_parameterTable->setItem(3, 4, new QTableWidgetItem(QString::number(automaticFittingData.getPorosityMax().getValue().toDouble())));
|
|
|
|
|
m_parameterTable->setItem(3, 5, new QTableWidgetItem(""));
|
|
|
|
|
|
|
|
|
|
// 初始含水饱和度 (Swi)
|
|
|
|
|
m_parameterTable->setItem(4, 0, new QTableWidgetItem("5"));
|
|
|
|
|
m_swiCheckBox = new QCheckBox(tr("Swi"));
|
|
|
|
|
m_swiCheckBox->setChecked(automaticFittingData.getSwiSelected());
|
|
|
|
|
m_parameterTable->setCellWidget(4, 1, m_swiCheckBox);
|
|
|
|
|
m_parameterTable->setItem(4, 2, new QTableWidgetItem(QString::number(automaticFittingData.getSwiMin().getValue().toDouble())));
|
|
|
|
|
m_parameterTable->setItem(4, 3, new QTableWidgetItem(QString::number(reservoirData.getSwi().getValue().toDouble())));
|
|
|
|
|
m_parameterTable->setItem(4, 4, new QTableWidgetItem(QString::number(automaticFittingData.getSwiMax().getValue().toDouble())));
|
|
|
|
|
m_parameterTable->setItem(4, 5, new QTableWidgetItem(""));
|
|
|
|
|
// 索引 4 留空,保持裂缝参数与优化器的现有索引一致。
|
|
|
|
|
|
|
|
|
|
// 裂缝导流能力 (Dfc)。该行只对压裂井显示,初值在 onWellSelected() 中
|
|
|
|
|
// 从当前目标井读取,其他裂缝几何参数保持固定,不进入自动拟合。
|
|
|
|
|
@ -891,7 +792,6 @@ void nmWxAutomaticFitting::onReverseSelection()
|
|
|
|
|
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_swiCheckBox->setChecked(!m_swiCheckBox->isChecked());
|
|
|
|
|
if(!m_parameterTable->isRowHidden(5)) m_dfcCheckBox->setChecked(!m_dfcCheckBox->isChecked());
|
|
|
|
|
if(!m_parameterTable->isRowHidden(6)) m_fractureHalfLengthCheckBox->setChecked(!m_fractureHalfLengthCheckBox->isChecked());
|
|
|
|
|
}
|
|
|
|
|
@ -902,7 +802,7 @@ void nmWxAutomaticFitting::onParameterTableItemChanged(QTableWidgetItem* item)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用户改动范围后,切井和拟合结果不再自动覆盖这组手工范围。
|
|
|
|
|
// 手工范围用于本轮拟合,切井时保留;拟合完成后再按最新参数自动生成范围。
|
|
|
|
|
if(item->column() == 2 || item->column() == 4) {
|
|
|
|
|
m_autoParameterRanges = false;
|
|
|
|
|
}
|
|
|
|
|
@ -1001,7 +901,7 @@ void nmWxAutomaticFitting::onAccept()
|
|
|
|
|
// 检查是否有参数被选中
|
|
|
|
|
bool hasSelectedParams = m_kCheckBox->isChecked() || m_sCheckBox->isChecked() ||
|
|
|
|
|
m_cCheckBox->isChecked() || m_phiCheckBox->isChecked() ||
|
|
|
|
|
m_swiCheckBox->isChecked() || m_dfcCheckBox->isChecked() ||
|
|
|
|
|
m_dfcCheckBox->isChecked() ||
|
|
|
|
|
m_fractureHalfLengthCheckBox->isChecked();
|
|
|
|
|
|
|
|
|
|
if(!hasSelectedParams) {
|
|
|
|
|
@ -1016,7 +916,6 @@ void nmWxAutomaticFitting::onAccept()
|
|
|
|
|
if(m_sCheckBox->isChecked()) selectedParameterNames << tr("Skin");
|
|
|
|
|
if(m_cCheckBox->isChecked()) selectedParameterNames << tr("Wellbore storage");
|
|
|
|
|
if(m_phiCheckBox->isChecked()) selectedParameterNames << tr("Porosity");
|
|
|
|
|
if(m_swiCheckBox->isChecked()) selectedParameterNames << tr("Swi");
|
|
|
|
|
if(m_dfcCheckBox->isChecked()) selectedParameterNames << tr("Fracture conductivity");
|
|
|
|
|
if(m_fractureHalfLengthCheckBox->isChecked()) selectedParameterNames << tr("Fracture half length");
|
|
|
|
|
|
|
|
|
|
@ -1030,105 +929,55 @@ void nmWxAutomaticFitting::onReject()
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::onWellSelected(int index)
|
|
|
|
|
{
|
|
|
|
|
// 获取选中的井名
|
|
|
|
|
QString selectedWellName = m_targetWellCombo->itemText(index);
|
|
|
|
|
|
|
|
|
|
// 在分类的井数据中查找匹配的井
|
|
|
|
|
bool found = false;
|
|
|
|
|
bool fracturedWell = false;
|
|
|
|
|
double skinValue = 0.0;
|
|
|
|
|
double wellboreStorageValue = 0.0;
|
|
|
|
|
double fractureConductivityValue = 0.0;
|
|
|
|
|
double fractureHalfLengthValue = 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;
|
|
|
|
|
}
|
|
|
|
|
// 每次都读取模型实际保留的井参数,避免拟合结束后切井又恢复到打开窗口时的旧副本。
|
|
|
|
|
nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance();
|
|
|
|
|
if(!manager) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查找水平井
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nmDataWellBase* targetWell = manager->findWellByName(m_targetWellCombo->itemText(index));
|
|
|
|
|
if(!targetWell) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查找垂直压裂井
|
|
|
|
|
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();
|
|
|
|
|
fractureConductivityValue = m_verticalFracturedWells[i].getDfc().getValue().toDouble();
|
|
|
|
|
fractureHalfLengthValue = m_verticalFracturedWells[i].getFractureHalfLength().getValue().toDouble();
|
|
|
|
|
fracturedWell = true;
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const bool wasUpdatingRanges = m_updatingParameterRanges;
|
|
|
|
|
m_updatingParameterRanges = true;
|
|
|
|
|
updateParameterVisibility(m_parameterTable, manager->getSolverModelType());
|
|
|
|
|
|
|
|
|
|
// 查找水平压裂井
|
|
|
|
|
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();
|
|
|
|
|
fractureConductivityValue = m_horizontalFracturedWells[i].getDfc().getValue().toDouble();
|
|
|
|
|
fractureHalfLengthValue = m_horizontalFracturedWells[i].getFractureHalfLength().getValue().toDouble();
|
|
|
|
|
fracturedWell = true;
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
nmDataPerforation* perforation = targetWell->getPerforation(0);
|
|
|
|
|
if(perforation) {
|
|
|
|
|
const double skin = perforation->getSkin().getValue().toDouble();
|
|
|
|
|
m_parameterTable->item(1, 3)->setText(QString::number(skin, 'g', 10));
|
|
|
|
|
if(m_autoParameterRanges) {
|
|
|
|
|
updateRangeForParameter(1, skin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const double storage = targetWell->getWellboreStorage().getValue().toDouble();
|
|
|
|
|
m_parameterTable->item(2, 3)->setText(QString::number(storage, 'g', 10));
|
|
|
|
|
if(m_autoParameterRanges) {
|
|
|
|
|
updateRangeForParameter(2, storage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
|
// 先按目标井类型刷新可见行,再写入当前井的井级初值。
|
|
|
|
|
nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance();
|
|
|
|
|
if(manager) {
|
|
|
|
|
updateParameterVisibility(m_parameterTable, manager->getSolverModelType());
|
|
|
|
|
}
|
|
|
|
|
// 更新表格数据
|
|
|
|
|
// 确保表格项存在
|
|
|
|
|
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));
|
|
|
|
|
if(fracturedWell && m_parameterTable->item(5, 3)) {
|
|
|
|
|
m_parameterTable->item(5, 3)->setText(QString::number(fractureConductivityValue));
|
|
|
|
|
}
|
|
|
|
|
if(fracturedWell && m_parameterTable->item(6, 3)) {
|
|
|
|
|
m_parameterTable->item(6, 3)->setText(QString::number(fractureHalfLengthValue));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool fracturedWell = false;
|
|
|
|
|
double conductivity = 0.0;
|
|
|
|
|
double halfLength = 0.0;
|
|
|
|
|
if(nmDataVerticalFracturedWell* well = dynamic_cast<nmDataVerticalFracturedWell*>(targetWell)) {
|
|
|
|
|
conductivity = well->getDfc().getValue().toDouble();
|
|
|
|
|
halfLength = well->getFractureHalfLength().getValue().toDouble();
|
|
|
|
|
fracturedWell = true;
|
|
|
|
|
} else if(nmDataHorizontalFracturedWell* well = dynamic_cast<nmDataHorizontalFracturedWell*>(targetWell)) {
|
|
|
|
|
conductivity = well->getDfc().getValue().toDouble();
|
|
|
|
|
halfLength = well->getFractureHalfLength().getValue().toDouble();
|
|
|
|
|
fracturedWell = true;
|
|
|
|
|
}
|
|
|
|
|
if(fracturedWell) {
|
|
|
|
|
m_parameterTable->item(5, 3)->setText(QString::number(conductivity, 'g', 10));
|
|
|
|
|
m_parameterTable->item(6, 3)->setText(QString::number(halfLength, 'g', 10));
|
|
|
|
|
if(m_autoParameterRanges) {
|
|
|
|
|
updateRangeForParameter(1, skinValue);
|
|
|
|
|
updateRangeForParameter(2, wellboreStorageValue);
|
|
|
|
|
if(fracturedWell) {
|
|
|
|
|
updateRangeForParameter(5, fractureConductivityValue);
|
|
|
|
|
updateRangeForParameter(6, fractureHalfLengthValue);
|
|
|
|
|
}
|
|
|
|
|
updateRangeForParameter(5, conductivity);
|
|
|
|
|
updateRangeForParameter(6, halfLength);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_updatingParameterRanges = wasUpdatingRanges;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::setAutomaticFittingValue()
|
|
|
|
|
@ -1138,7 +987,6 @@ void nmWxAutomaticFitting::setAutomaticFittingValue()
|
|
|
|
|
automaticFittingData.setSkinSelected(m_sCheckBox->isChecked());
|
|
|
|
|
automaticFittingData.setWellboreStorageSelected(m_cCheckBox->isChecked());
|
|
|
|
|
automaticFittingData.setPorositySelected(m_phiCheckBox->isChecked());
|
|
|
|
|
automaticFittingData.setSwiSelected(m_swiCheckBox->isChecked());
|
|
|
|
|
automaticFittingData.setFractureConductivitySelected(m_dfcCheckBox->isChecked());
|
|
|
|
|
automaticFittingData.setFractureHalfLengthSelected(m_fractureHalfLengthCheckBox->isChecked());
|
|
|
|
|
automaticFittingData.setSurrogateScreeningEnabled(m_surrogateCombo && m_surrogateCombo->currentIndex() == 1);
|
|
|
|
|
@ -1159,10 +1007,6 @@ void nmWxAutomaticFitting::setAutomaticFittingValue()
|
|
|
|
|
automaticFittingData.getPorosityMin().setValue(m_parameterTable->item(3, 2)->text().toDouble());
|
|
|
|
|
automaticFittingData.getPorosityMax().setValue(m_parameterTable->item(3, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
// 保存初始含水饱和度的最小值和最大值
|
|
|
|
|
automaticFittingData.getSwiMin().setValue(m_parameterTable->item(4, 2)->text().toDouble());
|
|
|
|
|
automaticFittingData.getSwiMax().setValue(m_parameterTable->item(4, 4)->text().toDouble());
|
|
|
|
|
|
|
|
|
|
// 保存裂缝导流能力的最小值和最大值
|
|
|
|
|
automaticFittingData.getFractureConductivityMin().setValue(m_parameterTable->item(5, 2)->text().toDouble());
|
|
|
|
|
automaticFittingData.getFractureConductivityMax().setValue(m_parameterTable->item(5, 4)->text().toDouble());
|
|
|
|
|
@ -1175,10 +1019,9 @@ void nmWxAutomaticFitting::setAutomaticFittingValue()
|
|
|
|
|
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.getSwi().setValue(m_parameterTable->item(4, 3)->text().toDouble()); // 初始含水饱和度
|
|
|
|
|
|
|
|
|
|
// 更新储层数据(全局)
|
|
|
|
|
nmDataAnalyzeManager::getCurrentInstance()->updateReservoirData(reservoirData);
|
|
|
|
|
@ -1361,10 +1204,9 @@ void nmWxAutomaticFitting::onFittingFinished(bool success, const QString& messag
|
|
|
|
|
if(manager && manager->getAttrRegistry()) {
|
|
|
|
|
manager->getAttrRegistry()->refreshAll();
|
|
|
|
|
}
|
|
|
|
|
// 失败状态不代表参数未写回;初值与范围始终以模型实际保留的数据为准。
|
|
|
|
|
refreshParametersFromModel();
|
|
|
|
|
if(success) {
|
|
|
|
|
// 只有成功拟合的结果才用于生成下一轮范围,失败结果不污染当前配置。
|
|
|
|
|
updateBestParametersToTable();
|
|
|
|
|
|
|
|
|
|
QString resultInfo;
|
|
|
|
|
|
|
|
|
|
if(m_autoFitterPSO || m_autoFitterLM) {
|
|
|
|
|
@ -1479,47 +1321,30 @@ void nmWxAutomaticFitting::cleanupFitting()
|
|
|
|
|
DEBUG_UI("=== CLEANUP FITTING END ===");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nmWxAutomaticFitting::updateBestParametersToTable()
|
|
|
|
|
void nmWxAutomaticFitting::refreshParametersFromModel()
|
|
|
|
|
{
|
|
|
|
|
QVector<double> bestSolution;
|
|
|
|
|
|
|
|
|
|
// 获取最佳解决方案
|
|
|
|
|
if (m_autoFitterPSO) {
|
|
|
|
|
bestSolution = m_autoFitterPSO->getBestSolution();
|
|
|
|
|
} else if(m_autoFitterLM) {
|
|
|
|
|
bestSolution = m_autoFitterLM->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_swiCheckBox->isChecked()) enabledParams.append(4); // 初始含水饱和度
|
|
|
|
|
if(m_dfcCheckBox->isChecked()) enabledParams.append(5); // 裂缝导流能力
|
|
|
|
|
if(m_fractureHalfLengthCheckBox->isChecked()) enabledParams.append(6); // 裂缝半长
|
|
|
|
|
|
|
|
|
|
// 更新参数值和范围
|
|
|
|
|
for (int i = 0; i < bestSolution.size() && i < enabledParams.size(); ++i) {
|
|
|
|
|
int paramIndex = enabledParams[i];
|
|
|
|
|
double bestValue = bestSolution[i];
|
|
|
|
|
if(!nmAutoFitUiIsFinite(bestValue)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新初始值
|
|
|
|
|
m_parameterTable->item(paramIndex, 3)->setText(QString::number(bestValue, 'g', 4));
|
|
|
|
|
// 自动范围模式下,以拟合结果为中心复用首次建范围的规则;手工范围由用户保留。
|
|
|
|
|
if(m_autoParameterRanges) {
|
|
|
|
|
updateRangeForParameter(paramIndex, bestValue);
|
|
|
|
|
}
|
|
|
|
|
nmDataAnalyzeManager* manager = nmDataAnalyzeManager::getCurrentInstance();
|
|
|
|
|
if(!manager) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存更新
|
|
|
|
|
nmDataAnalyzeManager::getCurrentInstance()->updateAutomaticFittingData(automaticFittingData);
|
|
|
|
|
// 更新完成后,通知参数界面刷新
|
|
|
|
|
// 同步储层副本,避免下一次保存设置时把未拟合的储层属性覆盖成旧值。
|
|
|
|
|
reservoirData = manager->getReservoirDataCopy();
|
|
|
|
|
const bool wasUpdatingRanges = m_updatingParameterRanges;
|
|
|
|
|
m_updatingParameterRanges = true;
|
|
|
|
|
m_parameterTable->item(0, 3)->setText(QString::number(
|
|
|
|
|
reservoirData.getPermeability().getValue().toDouble(), 'g', 10));
|
|
|
|
|
m_parameterTable->item(3, 3)->setText(QString::number(
|
|
|
|
|
reservoirData.getPorosity().getValue().toDouble(), 'g', 10));
|
|
|
|
|
|
|
|
|
|
// 成功或失败都从当前目标井读取已落地的参数,不采用优化器尚未写回的最佳候选。
|
|
|
|
|
// 先刷新全部初值,再统一重建范围,避免即时校验读到新旧值混合的表格。
|
|
|
|
|
m_autoParameterRanges = false;
|
|
|
|
|
onWellSelected(m_targetWellCombo->currentIndex());
|
|
|
|
|
m_autoParameterRanges = true;
|
|
|
|
|
initializeSuggestedParameterRanges();
|
|
|
|
|
m_updatingParameterRanges = wasUpdatingRanges;
|
|
|
|
|
|
|
|
|
|
manager->updateAutomaticFittingData(automaticFittingData);
|
|
|
|
|
nmWxParameterProperty::notifyUpdateTable();
|
|
|
|
|
}
|
|
|
|
|
|