|
|
|
|
@ -3,14 +3,116 @@
|
|
|
|
|
#include "nmWxAutomaticfittingStart.h"
|
|
|
|
|
#include "nmWxParameterProperty.h"
|
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
|
|
#include "iSubWndFitting.h"
|
|
|
|
|
#include "mGui/mGuiAnal/iAnalRun.h"
|
|
|
|
|
#include "iGuiPlot.h"
|
|
|
|
|
#include "ZxObjCurve.h"
|
|
|
|
|
#include "mAlgDefines.h"
|
|
|
|
|
|
|
|
|
|
#pragma comment(lib, "mGuiAnal.lib")
|
|
|
|
|
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
#include <QHeaderView>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <float.h>
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#define DEBUG_UI(msg) OutputDebugStringA(QString("[UI] %1\n").arg(msg).toLocal8Bit().data())
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
bool nmAutoFitUiIsFinite(double value)
|
|
|
|
|
{
|
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
return _finite(value) != 0;
|
|
|
|
|
#else
|
|
|
|
|
return std::isfinite(value);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 主界面气井双对数中的源曲线和导数曲线已经采用拟压力量纲.
|
|
|
|
|
// 自动拟合直接读取这两条显示曲线, 避免井对象中的原始压力历史数据与结果曲线量纲不一致.
|
|
|
|
|
// 两条曲线可能因无效点过滤而长度不同, 因此按时间坐标匹配共同有效点.
|
|
|
|
|
bool getMainHistoryLogLog(iSubWndFitting* fitting,
|
|
|
|
|
QVector<QVector<double> >& data,
|
|
|
|
|
QString& errorMessage)
|
|
|
|
|
{
|
|
|
|
|
data.clear();
|
|
|
|
|
if(!fitting) {
|
|
|
|
|
errorMessage = QObject::tr("The current fitting window is unavailable.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QWidget* widget = fitting->getFitSubRstWxOf(FSRT_DoubleLog, false, &errorMessage);
|
|
|
|
|
iGuiPlot* plot = qobject_cast<iGuiPlot*>(widget);
|
|
|
|
|
if(!plot) {
|
|
|
|
|
errorMessage = QObject::tr("The main double-log plot is unavailable.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ZxObjCurve* sourceCurve = plot->getCurve(s_Souce_Curve);
|
|
|
|
|
ZxObjCurve* derivativeCurve = plot->getCurve(s_Deriv_Curve);
|
|
|
|
|
if(!sourceCurve || !derivativeCurve) {
|
|
|
|
|
errorMessage = QObject::tr("The main double-log history curves are unavailable.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVector<QPointF> sourcePoints = sourceCurve->getAllValues();
|
|
|
|
|
QVector<QPointF> derivativePoints = derivativeCurve->getAllValues();
|
|
|
|
|
|
|
|
|
|
data.resize(3);
|
|
|
|
|
int sourceIndex = 0;
|
|
|
|
|
int derivativeIndex = 0;
|
|
|
|
|
while(sourceIndex < sourcePoints.size()
|
|
|
|
|
&& derivativeIndex < derivativePoints.size()) {
|
|
|
|
|
const QPointF sourcePoint = sourcePoints[sourceIndex];
|
|
|
|
|
const QPointF derivativePoint = derivativePoints[derivativeIndex];
|
|
|
|
|
const double sourceTime = sourcePoint.x();
|
|
|
|
|
const double derivativeTime = derivativePoint.x();
|
|
|
|
|
|
|
|
|
|
if(!nmAutoFitUiIsFinite(sourceTime) || sourceTime <= 0.0) {
|
|
|
|
|
++sourceIndex;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if(!nmAutoFitUiIsFinite(derivativeTime) || derivativeTime <= 0.0) {
|
|
|
|
|
++derivativeIndex;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const double timeTolerance = qMax(1.0,
|
|
|
|
|
qMax(qAbs(sourceTime), qAbs(derivativeTime))) * 1e-8;
|
|
|
|
|
if(qAbs(sourceTime - derivativeTime) <= timeTolerance) {
|
|
|
|
|
const double source = sourcePoint.y();
|
|
|
|
|
const double derivative = derivativePoint.y();
|
|
|
|
|
if(nmAutoFitUiIsFinite(source)
|
|
|
|
|
&& nmAutoFitUiIsFinite(derivative)
|
|
|
|
|
&& source > 0.0 && derivative > 0.0) {
|
|
|
|
|
data[0].append(sourceTime);
|
|
|
|
|
data[1].append(source);
|
|
|
|
|
data[2].append(derivative);
|
|
|
|
|
}
|
|
|
|
|
++sourceIndex;
|
|
|
|
|
++derivativeIndex;
|
|
|
|
|
} else if(sourceTime < derivativeTime) {
|
|
|
|
|
++sourceIndex;
|
|
|
|
|
} else {
|
|
|
|
|
++derivativeIndex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(data[0].size() < 5) {
|
|
|
|
|
data.clear();
|
|
|
|
|
errorMessage = QObject::tr("The main double-log history curves have too few valid points.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置某一行参数是否显示。隐藏时同步取消勾选并禁用,避免隐藏参数参与自动拟合。
|
|
|
|
|
void nmWxAutomaticFitting::setParameterRowVisible(QTableWidget* table, int row, bool visible)
|
|
|
|
|
{
|
|
|
|
|
@ -556,16 +658,54 @@ void nmWxAutomaticFitting::onAccept()
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nmDataAnalyzeManager* pManager = nmDataAnalyzeManager::getCurrentInstance();
|
|
|
|
|
if(!pManager) {
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Data manager is unavailable!"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从数据管理器获取最新的井对象
|
|
|
|
|
nmDataWellBase* pTargetWell = nmDataAnalyzeManager::getCurrentInstance()->findWellByName(selectedWellName);
|
|
|
|
|
nmDataWellBase* pTargetWell = pManager->findWellByName(selectedWellName);
|
|
|
|
|
|
|
|
|
|
if(!pTargetWell) {
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Target well not found!"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取目标双对数历史数据
|
|
|
|
|
QVector<QVector<double> > targetLogLogData = pTargetWell->getHistoryLogLog();
|
|
|
|
|
QVector<QVector<double> > targetLogLogData;
|
|
|
|
|
const bool usePseudoPressure = (pManager->getSolverModelType() == SMT_Gas_VariablePvt);
|
|
|
|
|
if(usePseudoPressure) {
|
|
|
|
|
// 目标曲线来自当前主界面, 必须确认界面显示的井就是用户选择的拟合井.
|
|
|
|
|
nmDataWellBase* pCurrentWell = pManager->getCurWellData();
|
|
|
|
|
if(!pCurrentWell || pCurrentWell->getWellName() != selectedWellName) {
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"),
|
|
|
|
|
tr("Please display the selected gas well in the main result view before fitting."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iSubWndFitting* pFitting = nmDataAnalyzeManager::getCurrentFitting();
|
|
|
|
|
iAnalRun* pAnalRun = pFitting ? pFitting->getAnalRun() : nullptr;
|
|
|
|
|
// 自动拟合不依赖手动求解流程, 启动 PSO 前需要单独初始化同一拟压力转换器.
|
|
|
|
|
// 传入拟合窗口自己的页面数据, 保证目标曲线和每个粒子的结果使用同一 PVT 与模式.
|
|
|
|
|
if(!pAnalRun
|
|
|
|
|
|| !pAnalRun->configPsAbouts(true,
|
|
|
|
|
pFitting->getModelOption(),
|
|
|
|
|
false,
|
|
|
|
|
pFitting->getAllWxPtr())) {
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"),
|
|
|
|
|
tr("Gas pseudo-pressure data is unavailable or invalid."));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString curveError;
|
|
|
|
|
if(!getMainHistoryLogLog(pFitting, targetLogLogData, curveError)) {
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), curveError);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 油井和水井不使用拟压力, 继续读取井对象中原有的双对数历史数据.
|
|
|
|
|
targetLogLogData = pTargetWell->getHistoryLogLog();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(targetLogLogData.isEmpty() || targetLogLogData.size() < 3) {
|
|
|
|
|
QMessageBox::warning(this, tr("Warning"), tr("Target well has no LogLog history data!"));
|
|
|
|
|
@ -864,6 +1004,8 @@ void nmWxAutomaticFitting::startAutoFitting(const QVector<QVector<double>>& targ
|
|
|
|
|
|
|
|
|
|
m_progressMonitor = new nmWxAutomaticfittingStart(this);
|
|
|
|
|
m_progressMonitor->setAutoFitter(m_autoFitterPSO);
|
|
|
|
|
m_progressMonitor->setPseudoPressureMode(
|
|
|
|
|
nmDataAnalyzeManager::getCurrentInstance()->getSolverModelType() == SMT_Gas_VariablePvt);
|
|
|
|
|
m_progressMonitor->setTargetLogLogData(targetData);
|
|
|
|
|
|
|
|
|
|
connect(m_autoFitterPSO, SIGNAL(fittingFinished(bool, QString)),
|
|
|
|
|
|