You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmData/nmDataAnalyzeManager.cpp

6160 lines
210 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmDataAnalyzeManager.h"
#include "nmAttrRegistry.h"
#include "nmDataAnalyzeContext.h"
#include "nmDataAnalyzeContextProvider.h"
#include "nmDataPlotContext.h"
#include "nmDataPlotContextProvider.h"
#include "nmDataWellBase.h"
#include "nmDataVerticalWell.h"
#include "nmDataVerticalFracturedWell.h"
#include "nmDataHorizontalFracturedWell.h"
#include "ZxDataWell.h"
#include "ZxBaseUtil.h"
#include <QSet>
#include "zxLogInstance.h"
#include "nmDataReservoir.h"
#include "nmDataRegionMark.h"
#include "nmDataOutline.h"
#include "nmDataRegion.h"
#include "nmDataFracture.h"
#include "nmDataFault.h"
#include "nmDataMeasuringScale.h"
#include "nmDataMeasure.h"
#include "nmDataAxis.h"
#include "nmDataGeoRef.h"
#include "nmDataDiagnostic.h"
#include "nmDataForecast.h"
#include "nmDataAutomaticFitting.h"
#include "nmDataSensitive.h"
#include "nmDataPvtParaForPebi.h"
#include "ZxDataWell.h"
#include "ZxDataGaugeP.h"
#include "ZxDataGaugeF.h"
#include "zxSysUtils.h"
#include "ZxDataProject.h"
#include "nmDataMixedResults.h"
#include "nmDataLayer.h"
#include "nmDataUtils.h"
#include "mAlgDefines.h"
#include "nmDataJsonTools.h" // JSON工具类
#include "nmTranslationManager.h"
#include "iAnalRun.h"
#include "ZxDataAnalRun.h"
#include <Windows.h>
#include <iostream>
#include <vector>
#include "singlePhaseSolver.h"
#include <vtkUnstructuredGridReader.h>
#include <vtkXMLUnstructuredGridReader.h>
#include <vtkUnstructuredGridWriter.h>
#include <vtkXMLUnstructuredGridWriter.h>
#include <vtkNew.h>
#include "nmDataTimeStepSetting.h"
#include <QByteArray>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
// 井基参数名 → 数据成员访问器的映射描述
struct WellParaDesc {
const char* sName; // 参数基名,如 "W_X"
NM_WELL_MODEL eWellType; // Unknow_Well 表示公用参数
nmDataAttribute* (*getAttr)(nmDataWellBase&); // 访问器函数指针
};
static nmDataAttribute* getParaX(nmDataWellBase& w) { return &w.getX(); }
static nmDataAttribute* getParaY(nmDataWellBase& w) { return &w.getY(); }
static nmDataAttribute* getParaRw(nmDataWellBase& w) { return &w.getRadius(); }
static nmDataAttribute* getParaC(nmDataWellBase& w) { return &w.getWellboreStorage(); }
static nmDataAttribute* getParaSkin(nmDataWellBase& w)
{
return w.getPerforationCount() > 0 ? &w.getPerforation(0)->getSkin() : NULL;
}
static nmDataAttribute* getParaWellLength(nmDataWellBase& w) { return &w.getWellLength(); }
static nmDataAttribute* getParaHfDrainAngle(nmDataWellBase& w) { return &static_cast<nmDataHorizontalFracturedWell&>(w).getDrainAngle(); }
static nmDataAttribute* getParaHfNumberOfFractures(nmDataWellBase& w) { return &static_cast<nmDataHorizontalFracturedWell&>(w).getNumberOfFractures(); }
static nmDataAttribute* getParaVfFractureHalfLength(nmDataWellBase& w) { return &static_cast<nmDataVerticalFracturedWell&>(w).getFractureHalfLength(); }
static nmDataAttribute* getParaVfFractureAngle(nmDataWellBase& w) { return &static_cast<nmDataVerticalFracturedWell&>(w).getFractureAngle(); }
static nmDataAttribute* getParaVfDfc(nmDataWellBase& w) { return &static_cast<nmDataVerticalFracturedWell&>(w).getDfc(); }
static nmDataAttribute* getParaHfFractureHalfLength(nmDataWellBase& w) { return &static_cast<nmDataHorizontalFracturedWell&>(w).getFractureHalfLength(); }
static nmDataAttribute* getParaHfFractureAngle(nmDataWellBase& w) { return &static_cast<nmDataHorizontalFracturedWell&>(w).getFractureAngle(); }
static nmDataAttribute* getParaHfDfc(nmDataWellBase& w) { return &static_cast<nmDataHorizontalFracturedWell&>(w).getDfc(); }
static const WellParaDesc WELL_PARA_DESCS[] = {
{"W_X", NM_WELL_MODEL::Unknow_Well, &getParaX},
{"W_Y", NM_WELL_MODEL::Unknow_Well, &getParaY},
{"W_Rw", NM_WELL_MODEL::Unknow_Well, &getParaRw},
{"W_C", NM_WELL_MODEL::Unknow_Well, &getParaC},
{"W_Skin", NM_WELL_MODEL::Unknow_Well, &getParaSkin},
{"W_FractureHalfLength", NM_WELL_MODEL::Vertical_Fractured_Well, &getParaVfFractureHalfLength},
{"W_FractureAngle", NM_WELL_MODEL::Vertical_Fractured_Well, &getParaVfFractureAngle},
{"W_Dfc", NM_WELL_MODEL::Vertical_Fractured_Well, &getParaVfDfc},
{"W_WellLength", NM_WELL_MODEL::Horizontal_Fractured_Well, &getParaWellLength},
{"W_DrainAngle", NM_WELL_MODEL::Horizontal_Fractured_Well, &getParaHfDrainAngle},
{"W_NumberOfFractures", NM_WELL_MODEL::Horizontal_Fractured_Well, &getParaHfNumberOfFractures},
{"W_FractureHalfLength", NM_WELL_MODEL::Horizontal_Fractured_Well, &getParaHfFractureHalfLength},
{"W_FractureAngle", NM_WELL_MODEL::Horizontal_Fractured_Well, &getParaHfFractureAngle},
{"W_Dfc", NM_WELL_MODEL::Horizontal_Fractured_Well, &getParaHfDfc},
};
static const int WELL_PARA_DESC_COUNT = sizeof(WELL_PARA_DESCS) / sizeof(WELL_PARA_DESCS[0]);
static bool isWellParaOf(const WellParaDesc& desc, NM_WELL_MODEL eWellType)
{
return desc.eWellType == NM_WELL_MODEL::Unknow_Well || desc.eWellType == eWellType;
}
// 当前 PEBI 网格只支持这三类数值井。Map 中其他类型的图元不能进入求解井集合。
static bool isSupportedNumericalWell(const nmDataWellBase* pWellData)
{
if(pWellData == nullptr || pWellData->getWellCode().isEmpty()) {
return false;
}
const NM_WELL_MODEL eWellType = pWellData->getWellType();
return eWellType == NM_WELL_MODEL::Vertical_Well ||
eWellType == NM_WELL_MODEL::Vertical_Fractured_Well ||
eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well;
}
static bool isWellParaAttrName(const QString& name)
{
for (int i = 0; i < WELL_PARA_DESC_COUNT; ++i) {
if (name.endsWith(QString("_") + WELL_PARA_DESCS[i].sName)) {
return true;
}
}
return false;
}
// 参数名必须与 XML 定义和参数面板拼接的基础编号完全一致。
struct GeometryParaDesc {
const char* sName;
nmDataAttribute* (*getFractureAttr)(nmDataFracture&);
nmDataAttribute* (*getFaultAttr)(nmDataFault&);
nmDataAttribute* (*getRegionAttr)(nmDataRegion&);
nmDataAttribute* (*getRegionMarkAttr)(nmDataRegionMark&);
};
static nmDataAttribute* getFractureStartX(nmDataFracture& data) { return &data.getStartX(); }
static nmDataAttribute* getFractureStartY(nmDataFracture& data) { return &data.getStartY(); }
static nmDataAttribute* getFractureEndX(nmDataFracture& data) { return &data.getEndX(); }
static nmDataAttribute* getFractureEndY(nmDataFracture& data) { return &data.getEndY(); }
static nmDataAttribute* getFractureFc(nmDataFracture& data) { return &data.getFractureDfc(); }
static nmDataAttribute* getFaultStartX(nmDataFault& data) { return &data.getStartX(); }
static nmDataAttribute* getFaultStartY(nmDataFault& data) { return &data.getStartY(); }
static nmDataAttribute* getFaultEndX(nmDataFault& data) { return &data.getEndX(); }
static nmDataAttribute* getFaultEndY(nmDataFault& data) { return &data.getEndY(); }
static nmDataAttribute* getRegionLeakage(nmDataRegion& data) { return &data.getRegionLeakage(); }
static nmDataAttribute* getRegionMarkComW(nmDataRegionMark& data) { return &data.getComW(); }
static nmDataAttribute* getRegionMarkComKr(nmDataRegionMark& data) { return &data.getComKr(); }
static nmDataAttribute* getRegionMarkNetToGross(nmDataRegionMark& data) { return &data.getNetToGross(); }
static const GeometryParaDesc GEOMETRY_PARA_DESCS[] = {
{"F_X0", &getFractureStartX, NULL, NULL, NULL},
{"F_Y0", &getFractureStartY, NULL, NULL, NULL},
{"F_X1", &getFractureEndX, NULL, NULL, NULL},
{"F_Y1", &getFractureEndY, NULL, NULL, NULL},
{"F_FC", &getFractureFc, NULL, NULL, NULL},
{"FT_X0", NULL, &getFaultStartX, NULL, NULL},
{"FT_Y0", NULL, &getFaultStartY, NULL, NULL},
{"FT_X1", NULL, &getFaultEndX, NULL, NULL},
{"FT_Y1", NULL, &getFaultEndY, NULL, NULL},
{"R_Leakage", NULL, NULL, &getRegionLeakage, NULL},
{"RM_ComW", NULL, NULL, NULL, &getRegionMarkComW},
{"RM_ComKr", NULL, NULL, NULL, &getRegionMarkComKr},
{"RM_NetToGross", NULL, NULL, NULL, &getRegionMarkNetToGross},
};
static const int GEOMETRY_PARA_DESC_COUNT = sizeof(GEOMETRY_PARA_DESCS) / sizeof(GEOMETRY_PARA_DESCS[0]);
static const char* OUTLINE_PARA_NAMES[] = {
"BR_XMin", "BR_YMin", "BR_XMax", "BR_YMax",
"BC_CenterX", "BC_CenterY", "BC_Radius",
"BP_X", "BP_Y"
};
static const int OUTLINE_PARA_NAME_COUNT = sizeof(OUTLINE_PARA_NAMES) / sizeof(OUTLINE_PARA_NAMES[0]);
static bool isGeometryParaAttrName(const QString& name)
{
for (int i = 0; i < GEOMETRY_PARA_DESC_COUNT; ++i) {
if (name.endsWith(QString("_") + GEOMETRY_PARA_DESCS[i].sName)) {
return true;
}
}
for (int i = 0; i < OUTLINE_PARA_NAME_COUNT; ++i) {
if (name.endsWith(QString("_") + OUTLINE_PARA_NAMES[i])) {
return true;
}
}
return false;
}
namespace {
// 清空指定目录下的所有旧文件和子目录,但保留目录本身
bool clearDirectoryContents(const QString& dirPath)
{
if(dirPath.trimmed().isEmpty()) {
return false;
}
QDir dir(dirPath);
if(dir.isRoot()) {
qDebug() << QString("Refuse to clear root directory: %1").arg(dirPath);
return false;
}
if(!dir.exists()) {
return true;
}
QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden | QDir::System);
for(int i = 0; i < entries.size(); ++i) {
const QFileInfo& entry = entries.at(i);
bool bRemoved = false;
if(entry.isDir() && !entry.isSymLink()) {
bRemoved = clearDirectoryContents(entry.absoluteFilePath()) && dir.rmdir(entry.fileName());
} else {
bRemoved = QFile::remove(entry.absoluteFilePath());
}
if(!bRemoved) {
qDebug() << QString("Failed to remove old result item: %1").arg(entry.absoluteFilePath());
return false;
}
}
return true;
}
// 多条PVT曲线都可能带压力横坐标只保存第一次读到的有效横坐标
void setPebiPressureIfEmpty(nmDataPvtParaForPebi* pPvtPara, const QVector<double>& vecX)
{
if(pPvtPara != nullptr && pPvtPara->getPressure().isEmpty() && !vecX.isEmpty()) {
pPvtPara->setPressure(vecX);
}
}
// 从Diffusion右侧结果表中提取指定列数据
bool extractDiffusionColumn(const VVecDouble& vvec, int nColumn, QVector<double>& vecValues)
{
vecValues.clear();
if(vvec.isEmpty() || nColumn < 0) {
return false;
}
if(vvec.size() > nColumn && vvec[nColumn].size() > 4 && vvec[nColumn].size() > vvec.size()) {
vecValues = vvec[nColumn];
return !vecValues.isEmpty();
}
// Diffusion结果表通常按行保存[自变量, 结果1, 结果2]
for(int i = 0; i < vvec.size(); ++i) {
if(vvec[i].size() > nColumn) {
vecValues.append(vvec[i][nColumn]);
}
}
if(!vecValues.isEmpty()) {
return true;
}
// 兼容少量按列缓存的数据
if(vvec.size() > nColumn) {
vecValues = vvec[nColumn];
}
return !vecValues.isEmpty();
}
// 根据饱和度坐标计算互补饱和度例如油水相渗中由Sw得到So
QVector<double> complementSaturation(const QVector<double>& vecSaturation)
{
QVector<double> vecResult;
vecResult.reserve(vecSaturation.size());
for(int i = 0; i < vecSaturation.size(); ++i) {
vecResult.append(1.0 - vecSaturation[i]);
}
return vecResult;
}
// 将数组顺序反转用于把Diffusion中按Sw递增的数据整理为按So递增的数据
QVector<double> reversedVector(const QVector<double>& vecValues)
{
QVector<double> vecResult;
vecResult.reserve(vecValues.size());
for(int i = vecValues.size(); i > 0; --i) {
vecResult.append(vecValues[i - 1]);
}
return vecResult;
}
// 将油水两相Diffusion相渗结果映射到PEBI求解器需要的饱和度和相对渗透率字段
void applyDiffusionKkToPebiPvt(nmDataPvtParaForPebi* pPvtPara, const VVecDouble& vvecKK)
{
if(vvecKK.isEmpty()) {
return;
}
QVector<double> vecS;
QVector<double> vecKr1;
QVector<double> vecKr2;
if(!extractDiffusionColumn(vvecKK, 0, vecS)) {
return;
}
extractDiffusionColumn(vvecKK, 1, vecKr1);
extractDiffusionColumn(vvecKK, 2, vecKr2);
// 油水第一列为SwSo按1-Sw生成后续列为Kro/Krw
// PEBI默认相渗以So为横坐标Diffusion表按Sw递增保存这里整体转为So递增方向
pPvtPara->setSo(reversedVector(complementSaturation(vecS)));
if(!vecKr1.isEmpty()) {
pPvtPara->setKro(reversedVector(vecKr1));
}
if(!vecKr2.isEmpty()) {
pPvtPara->setKrw(reversedVector(vecKr2));
}
}
/// @brief 尝试获取某相态的3条PVT曲线B/C/Miu全部成功则写入pebiPvtPara
/// @param pCtx 上下文提供者用于读取PVT结果页
/// @param pSubWnd 当前流动段分析窗口
/// @param pftContext getPvtRstOf需要的相态参数
/// @param sBName 体积系数参数名("Bo"/"Bg"/"Bw"
/// @param sCName 压缩系数参数名("Co"/"Cg"/"Cw"
/// @param sMiuName 粘度参数名("Miuo"/"Miug"/"Miuw"
/// @param pPvt 输出的PEBI PVT参数对象
/// @param vecPressure 压力横坐标(首次成功时写入,后续复用)
/// @return 三条曲线全部获取成功返回true否则返回false且不写入pPvt
bool tryFetchPhasePvtCurves(
nmDataAnalyzeContextProvider* pCtx,
iSubWndFitting* pSubWnd,
PvtFluidType pftContext,
const QString& sBName,
const QString& sCName,
const QString& sMiuName,
nmDataPvtParaForPebi* pPvt,
QVector<double>& vecPressure)
{
// 分别获取体积系数、压缩系数、粘度三条曲线
QVector<double> vecB, vecC, vecMiu, vecX;
bool bB = pCtx->getPvtRstOf(pSubWnd, pftContext, sBName, vecX, vecB);
bool bC = pCtx->getPvtRstOf(pSubWnd, pftContext, sCName, vecX, vecC);
bool bMiu = pCtx->getPvtRstOf(pSubWnd, pftContext, sMiuName, vecX, vecMiu);
// 任一曲线缺失则视为该相态PVT数据不完整不写入
if(!(bB && bC && bMiu)) {
return false;
}
// 首次成功时保存压力横坐标
if(vecPressure.isEmpty() && !vecX.isEmpty()) {
vecPressure = vecX;
}
// 按相态名称写入对应的setter油气水各自独立字段
if(sBName == "Bo") pPvt->setBo(vecB);
else if(sBName == "Bg") pPvt->setBg(vecB);
else if(sBName == "Bw") pPvt->setBw(vecB);
if(sCName == "Co") pPvt->setCo(vecC);
else if(sCName == "Cg") pPvt->setCg(vecC);
else if(sCName == "Cw") pPvt->setCw(vecC);
if(sMiuName == "Miuo") pPvt->setMiuo(vecMiu);
else if(sMiuName == "Miug") pPvt->setMiug(vecMiu);
else if(sMiuName == "Miuw") pPvt->setMiuw(vecMiu);
return true;
}
/// @brief 设置油藏属性的简化辅助,消除 tempAttr=getX(); tempAttr.setValue(); setX(tempAttr) 三行重复模式
/// @param attr 油藏属性引用
/// @param dValue 属性值
void setReservoirAttr(nmDataAttribute& attr, double dValue)
{
attr.setValue(dValue);
}
/// @brief 设置油藏属性的简化辅助QString版本
void setReservoirAttr(nmDataAttribute& attr, const QString& sValue)
{
attr.setValue(sValue);
}
/// @brief 从界面读取PVT单值参数
/// @param pCtx 上下文提供者
/// @param pSubWnd 当前流动段分析窗口
/// @param eType 相态类型
/// @return 参数名到值的映射
QMap<QString, double> readPvtSingleValues(
nmDataAnalyzeContextProvider* pCtx,
iSubWndFitting* pSubWnd,
PvtFluidType eType)
{
// 按相态构建需要读取的参数列表
QStringList listParas;
switch(eType) {
case WFT_Oil: listParas << "Bo" << "Miuo"; break;
case WFT_Gas: listParas << "Bg" << "Miug"; break;
case WFT_Water: listParas << "Bw" << "Miuw"; break;
case WFT_Oil_Water: listParas << "Bo" << "Miuo" << "Bw" << "Miuw"; break;
default: break;
}
// 综合压缩系数Ct不区分模式统一读取
listParas << "Ct";
// 调用接口读取参数值
QMap<QString, double> mapPvtValues;
if(!listParas.isEmpty()) {
pCtx->getPvtParaValues(pSubWnd, listParas, mapPvtValues);
}
return mapPvtValues;
}
/// @brief 按相态填充油藏PVT字段和压缩系数
/// @param pRes 油藏数据对象
/// @param eType 相态类型
/// @param mapPvtValues 从界面读取的PVT单值参数
/// @param dCf 岩石压缩系数(来自分层数据)
void populateReservoirByPhase(
nmDataReservoir* pRes,
PvtFluidType eType,
const QMap<QString, double>& mapPvtValues,
double dCf)
{
// 按相态设置多相流类型和对应的B/Miu字段
switch(eType) {
case WFT_Oil:
pRes->setPhaseType(PHASE_Oil);
setReservoirAttr(pRes->getBo(), mapPvtValues.value("Bo", 1.5));
setReservoirAttr(pRes->getMiuo(), mapPvtValues.value("Miuo", 1.0));
break;
case WFT_Gas:
pRes->setPhaseType(PHASE_Gas);
setReservoirAttr(pRes->getBg(), mapPvtValues.value("Bg", 1.0));
setReservoirAttr(pRes->getMiug(), mapPvtValues.value("Miug", 1.0));
break;
case WFT_Water:
pRes->setPhaseType(PHASE_Water);
setReservoirAttr(pRes->getBw(), mapPvtValues.value("Bw", 1.0));
setReservoirAttr(pRes->getMiuw(), mapPvtValues.value("Miuw", 1.0));
break;
case WFT_Oil_Water:
pRes->setPhaseType(PHASE_Oil_Water);
setReservoirAttr(pRes->getBo(), mapPvtValues.value("Bo", 1.5));
setReservoirAttr(pRes->getMiuo(), mapPvtValues.value("Miuo", 1.0));
setReservoirAttr(pRes->getBw(), mapPvtValues.value("Bw", 1.0));
setReservoirAttr(pRes->getMiuw(), mapPvtValues.value("Miuw", 1.0));
break;
default:
pRes->setPhaseType(PHASE_UNKNOWN);
break;
}
// 压缩系数Ct来自PVT参数界面Cf来自分层数据不区分模式统一设置
setReservoirAttr(pRes->getCt(), mapPvtValues.value("Ct", 0.1));
setReservoirAttr(pRes->getCf(), dCf);
}
/// @brief 创建默认分层
/// @param dThickness 储层厚度
/// @param vecLayers 分层数据列表(输出)
void createDefaultLayer(double dThickness, QVector<nmDataLayer*>& vecLayers)
{
// 清空现有分层数据
qDeleteAll(vecLayers);
vecLayers.clear();
// 创建一个默认分层
nmDataLayer* pDefaultLayer = new nmDataLayer();
pDefaultLayer->setTop(6000.0); // 默认顶深
pDefaultLayer->setThickness(dThickness); // 使用从界面获取的厚度值
pDefaultLayer->setBottom(6000.0 + dThickness); // 计算底深
pDefaultLayer->setIsChecked(false); // 默认未选中
pDefaultLayer->setColor(QColor(0, 255, 0)); // 设置默认颜色(绿色)
// 将默认分层添加到分层列表
vecLayers.append(pDefaultLayer);
}
}
ZX_DEFINE_DYNAMIC(DataAnalyzeManager, nmDataAnalyzeManager)
nmDataAnalyzeManager::nmDataAnalyzeManager(): ZxDataObjectBin(0)
{
m_pOwnerFitting = nullptr;
m_nBackgroundUseCount = 0;
m_reservoirData = nullptr;
m_outlineData = nullptr;
m_pMeasuringScaleData = nullptr;
m_pebiPvtPara = nullptr;
m_pMixedResults = nullptr;
m_pLayerData = nullptr;
m_pCurDataWell = nullptr;
m_pMeasureData = nullptr;
m_axisData = nullptr;
m_pNmGuiPlot = nullptr;
m_pGeoRefData = nullptr;
m_pTimeStep = nullptr;
//m_pPerCloData = nullptr;
m_pAutomaticFittingData = nullptr;
m_pDiagnosticData = nullptr;
//m_pSkinVsRateData = nullptr;
//m_pFlowSegmentData = nullptr;
m_pForecastData = nullptr;
m_pSensitiveData = nullptr;
// 属性注册表
m_pAttrRegistry = new nmAttrRegistry();
m_backgroundImageInfo.bIsVisible = false;
m_dScalarRangeP[0] = 0.0;
m_dScalarRangeP[1] = 0.0;
// 初始化混合参数数据(Temp)
//m_pMixedResults = new nmDataMixedResults;
// 初始化储层数据
m_pLayerData = new nmDataLayer;
m_eGridType = NM_Grid_Type::NM_Grid_PEBI; //默认网格类型为PEBI
m_eSolverModelType = NM_SOLVER_MODEL_TYPE::SMT_Oil_ConstPvt;
m_nPebiSolverType = PebiSolverCpuAccelerated;
m_nPebiOmpThreads = 4;
m_nPebiIluReuseSteps = 5;
this->initDefaultDisplaySettings();
// 初始化中英文翻译映射
nmTranslationManager::initTranslations();
m_bIsLoadData = false;
// 获取完整路径
//QString appPath = QCoreApplication::applicationFilePath();
//qDebug() << "完整应用路径:" << appPath;
// 许可证路径
//m_licensePath = appPath + "/../../3rd/Pebi/license/HXNWTM_license.dat";
// 获取完整路径
QString appPath = QCoreApplication::applicationFilePath();
// 获取应用程序所在的目录
QString appDir = QFileInfo(appPath).absolutePath();
// 定义从应用程序目录到许可证目录的相对路径
QString relativeJump = "/../Res/license/HXNWTM_license.dat";
// 将目录和相对跳转路径拼接
m_licensePath = appDir + relativeJump;
}
// 初始化静态成员
nmDataAnalyzeManager::~nmDataAnalyzeManager()
{
// 第一步:任何窗口销毁顺序下,都必须先等后台网格/求解任务停止访问。
// wait() 会暂时释放互斥量,因此任务仍能调用 endBackgroundUse() 正常退出。
{
QMutexLocker oLocker(&m_oBackgroundUseMutex);
while(m_nBackgroundUseCount > 0) {
m_oNoBackgroundUseCondition.wait(&m_oBackgroundUseMutex);
}
}
// 图元只引用数据数据统一由DataManager释放。
// 关闭成果或流动段分析后,清理该分析窗口对应的全部数据。
// 这两个成员是借用引用或容器内对象的别名不单独delete。
m_pCurDataWell = nullptr;
m_pNmGuiPlot = nullptr;
// 先释放容器中的数据对象,再释放其余独占的单对象数据。
qDeleteAll(m_vWellData);
m_vWellData.clear();
qDeleteAll(m_vFaultData);
m_vFaultData.clear();
qDeleteAll(m_vFractureData);
m_vFractureData.clear();
qDeleteAll(m_vRegionData);
m_vRegionData.clear();
qDeleteAll(m_vRegionMarkData);
m_vRegionMarkData.clear();
qDeleteAll(m_vecLayers);
m_vecLayers.clear();
delete m_outlineData;
m_outlineData = nullptr;
delete m_axisData;
m_axisData = nullptr;
delete m_pAutomaticFittingData;
m_pAutomaticFittingData = nullptr;
delete m_reservoirData;
m_reservoirData = nullptr;
delete m_pMeasuringScaleData;
m_pMeasuringScaleData = nullptr;
delete m_pMeasureData;
m_pMeasureData = nullptr;
delete m_pGeoRefData;
m_pGeoRefData = nullptr;
delete m_pForecastData;
m_pForecastData = nullptr;
delete m_pSensitiveData;
m_pSensitiveData = nullptr;
delete m_pDiagnosticData;
m_pDiagnosticData = nullptr;
delete m_pebiPvtPara;
m_pebiPvtPara = nullptr;
delete m_pMixedResults;
m_pMixedResults = nullptr;
delete m_pLayerData;
m_pLayerData = nullptr;
delete m_pTimeStep;
m_pTimeStep = nullptr;
// 最后 delete 属性注册表确保数据对象先销毁nmAttrRegistry 能接收 destroyed 信号清理映射)
delete m_pAttrRegistry;
m_pAttrRegistry = nullptr;
}
QMap<iSubWndFitting*, nmDataAnalyzeManager*> nmDataAnalyzeManager::s_mapDataAnalManager;
iSubWndFitting* nmDataAnalyzeManager::s_pCurSubWndFitting = nullptr;
nmDataAnalyzeManager* nmDataAnalyzeManager::getInstanceByFitting(iSubWndFitting* pSubWndF)
{
if(s_mapDataAnalManager.contains(pSubWndF)) {
return s_mapDataAnalManager[pSubWndF];
}
nmDataAnalyzeManager* pInstance = new nmDataAnalyzeManager();
// DataManager 与创建它的成果窗口一一绑定。后续后台任务读取 PVT、拟压力等
// 上下文时必须使用该窗口,不能随界面当前页签切换到另一份成果。
pInstance->m_pOwnerFitting = pSubWndF;
s_mapDataAnalManager[pSubWndF] = pInstance;
return pInstance;
}
void nmDataAnalyzeManager::removeInstanceByFitting(iSubWndFitting* pSubWndF)
{
if(pSubWndF == nullptr) {
return;
}
// take()先解除窗口和manager的映射避免析构期间再次找到待释放对象。
nmDataAnalyzeManager* pInstance = s_mapDataAnalManager.take(pSubWndF);
if(s_pCurSubWndFitting == pSubWndF) {
s_pCurSubWndFitting = nullptr;
}
delete pInstance;
}
nmDataAnalyzeManager* nmDataAnalyzeManager::getCurrentInstance()
{
if(s_mapDataAnalManager.contains(s_pCurSubWndFitting)) {
return s_mapDataAnalManager[s_pCurSubWndFitting];
} else {
return nullptr;
}
}
void nmDataAnalyzeManager::beginBackgroundUse()
{
QMutexLocker oLocker(&m_oBackgroundUseMutex);
++m_nBackgroundUseCount;
}
void nmDataAnalyzeManager::endBackgroundUse()
{
QMutexLocker oLocker(&m_oBackgroundUseMutex);
if(m_nBackgroundUseCount <= 0) {
Q_ASSERT(false);
return;
}
--m_nBackgroundUseCount;
if(m_nBackgroundUseCount == 0) {
m_oNoBackgroundUseCondition.wakeAll();
}
}
nmDataWellBase *nmDataAnalyzeManager::createWell(NM_WELL_MODEL eWellType)
{
// 根据当前Fitting窗口来获取对应的井相关数据
iSubWndFitting* pSubWndFitting = nmDataAnalyzeManager::getCurrentFitting();
nmDataAnalyzeContextProvider* pContextProvider = nmDataAnalyzeContext::provider();
Q_ASSERT(nullptr != pSubWndFitting);
Q_ASSERT(nullptr != pContextProvider);
nmDataWellBase* pWellData = nullptr;
// 新建一口井,默认直井
if(eWellType == NM_WELL_MODEL::Vertical_Well) {
// 直接使用子类指针创建对象
nmDataVerticalWell* verticalWell = new nmDataVerticalWell;
pWellData = static_cast<nmDataWellBase*>(verticalWell);
} else if(eWellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
// 直接使用子类指针创建对象
nmDataVerticalFracturedWell* vFracturedWell = new nmDataVerticalFracturedWell;
pWellData = static_cast<nmDataWellBase*>(vFracturedWell);
} else if(eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
// 直接使用子类指针创建对象
nmDataHorizontalFracturedWell* hFracturedWell = new nmDataHorizontalFracturedWell;
pWellData = static_cast<nmDataWellBase*>(hFracturedWell);
}
// 默认井才可以选择当前流动段,其余井默认选择最后一段
if(pWellData == nullptr) {
return nullptr;
}
if(m_vWellData.count() == 0) {
int nIndexF = -1;
// 第一口井就选择界面上的流动段索引
// 流动段索引由窗口层上下文提供,数据层不直接访问窗口对象
if(pSubWndFitting != nullptr && pContextProvider != nullptr) {
if(pContextProvider->getCurrentSegmentIndex(pSubWndFitting, nIndexF)) {
// 设置当前流动段索引
pWellData->setIndexF(nIndexF);
}
}
}
// Map 中增加真实井会改变井约束点和求解器井槽位,旧网格必须立即失效。
m_vWellData.append(pWellData);
m_oNumericalAnalysisCase.invalidateGrid();
emit dataChanged();
return pWellData;
}
// 实现 changeWellType 函数
nmDataWellBase* nmDataAnalyzeManager::changeWellType(nmDataWellBase* pOldWellData, NM_WELL_MODEL eTargetWellType)
{
if(pOldWellData == nullptr || !m_vWellData.contains(pOldWellData)) {
return nullptr;
}
if(pOldWellData->getWellType() == eTargetWellType) {
return pOldWellData;
}
// 第一步:先在管理器之外构造目标井,失败时旧井及其分析方案关联保持不变。
nmDataWellBase* pNewWellData = nullptr;
if(eTargetWellType == NM_WELL_MODEL::Vertical_Well) {
pNewWellData = new nmDataVerticalWell;
} else if(eTargetWellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
pNewWellData = new nmDataVerticalFracturedWell;
} else if(eTargetWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
pNewWellData = new nmDataHorizontalFracturedWell;
}
if(pNewWellData == nullptr) {
return nullptr;
}
// 第二步:复制井编码、名称、坐标、产量、压力及公共井参数,再恢复目标井型。
// 基类赋值会复制旧井型,因此必须在赋值后显式写回目标井型。
*pNewWellData = *pOldWellData;
pNewWellData->setWellType(eTargetWellType);
// 第三步:裂缝井依据复制后的井位和目标井默认裂缝参数重建几何。
nmDataVerticalFracturedWell* pVerticalFracturedWell =
dynamic_cast<nmDataVerticalFracturedWell*>(pNewWellData);
if(pVerticalFracturedWell != nullptr) {
pVerticalFracturedWell->setFracs();
}
nmDataHorizontalFracturedWell* pHorizontalFracturedWell =
dynamic_cast<nmDataHorizontalFracturedWell*>(pNewWellData);
if(pHorizontalFracturedWell != nullptr) {
pHorizontalFracturedWell->setFracs();
}
// 第四步原位提交替换WellCode 不变,因此主井、包含井和结果井关联自然保留。
if(!replaceWellData(pOldWellData, pNewWellData)) {
delete pNewWellData;
return nullptr;
}
return pNewWellData;
}
// 实现 removeWell 函数
bool nmDataAnalyzeManager::removeWell(nmDataWellBase* pWellData)
{
// 第一步:只有当前管理器真正拥有的井才允许删除,非法指针不能先污染分析方案。
if(pWellData == nullptr || !m_vWellData.contains(pWellData)) {
return false;
}
const QString removedWellName = pWellData->getWellName();
const QString removedWellCode = pWellData->getWellCode();
const bool isCurrentWell = (m_pCurDataWell == pWellData);
// 第二步Map 中删除任意真实井都会改变网格井集合。即使它是自动参与的
// 无产量观察井,也必须让旧网格、旧求解器顺序和旧结果一起失效。
m_oNumericalAnalysisCase.invalidateGrid();
// 第三步:删除井对象前,先从“包含其他井”中移除对应 WellCode。
QVector<nmCalculationWellRef> vecIncludedWells =
m_oNumericalAnalysisCase.getIncludedWells();
for(int nIndex = vecIncludedWells.size() - 1; nIndex >= 0; --nIndex) {
if(vecIncludedWells[nIndex].m_sWellCode == removedWellCode) {
vecIncludedWells.remove(nIndex);
}
}
m_oNumericalAnalysisCase.setIncludedWells(vecIncludedWells);
// 第四步:主分析井被删除时清空主井;新的主井必须由上层业务明确指定。
if(m_oNumericalAnalysisCase.getPrimaryWellCode() == removedWellCode) {
m_oNumericalAnalysisCase.setPrimaryWellCode(QString());
}
if(m_oNumericalAnalysisCase.getCurrentResultWellCode() == removedWellCode) {
m_oNumericalAnalysisCase.setCurrentResultWellCode(QString());
}
// 如果删的是当前井,切换到其他有效井;如果没有,则清空当前井指针。
if(isCurrentWell) {
m_pCurDataWell = nullptr;
for(int i = 0; i < m_vWellData.size(); ++i) {
nmDataWellBase* pCandidate = m_vWellData[i];
if(pCandidate != nullptr && pCandidate != pWellData) {
m_pCurDataWell = pCandidate;
break;
}
}
}
QString removedCode = pWellData->getWellCode();
QString removedName = pWellData->getWellName();
// 先从 registry 清理该井的所有属性,避免野指针
if (m_pAttrRegistry) {
for (int i = 0; i < WELL_PARA_DESC_COUNT; ++i) {
if (!isWellParaOf(WELL_PARA_DESCS[i], pWellData->getWellType())) {
continue;
}
QString name = removedCode + "_" + WELL_PARA_DESCS[i].sName;
m_pAttrRegistry->unregAttr(name);
}
}
// 倒序移除全部匹配项,兼容旧逻辑可能遗留的重复指针。
for (int i = m_vWellData.size() - 1; i >= 0; --i) {
if (m_vWellData[i] == pWellData) {
m_vWellData.remove(i);
}
}
delete pWellData;
pWellData = nullptr;
emit sigWellRemoved(removedCode, removedName);
emit dataChanged();
return true;
}
void nmDataAnalyzeManager::removeWellDataAndPlot(nmDataWellBase* pWellData)
{
// 参数校验
if(pWellData == nullptr) {
return;
}
// 公共删井入口中不允许删除当前井,避免活动井引用失效
if(pWellData == getCurWellData()) {
return;
}
nmDataPlotContextProvider* pPlotContextProvider = nmDataPlotContext::provider();
if (m_pNmGuiPlot != nullptr && pPlotContextProvider != nullptr)
{
// 移除所有井图元
pPlotContextProvider->removeWellPlotByData(m_pNmGuiPlot, pWellData);
removeWell(pWellData);
}
}
void nmDataAnalyzeManager::clearAllWellData()
{
typedef QPair<QString, QString> WellIdentity;
QList<WellIdentity> removedWells;
// 遍历并删除所有井对象,兼容旧逻辑可能遗留的重复指针。
QSet<nmDataWellBase*> deletedWells;
foreach(nmDataWellBase* pWell, m_vWellData) {
if(pWell != nullptr && !deletedWells.contains(pWell)) {
deletedWells.insert(pWell);
removedWells.append(qMakePair(pWell->getWellCode(), pWell->getWellName()));
delete pWell;
}
}
// 清空数组
m_vWellData.clear();
m_pCurDataWell = nullptr;
m_oNumericalAnalysisCase.clear();
// 清理 registry 中所有井相关属性
if (m_pAttrRegistry) {
foreach (const QString& name, m_pAttrRegistry->registeredNames()) {
if (isWellParaAttrName(name)) {
m_pAttrRegistry->unregAttr(name);
}
}
}
foreach (const WellIdentity& well, removedWells) {
emit sigWellRemoved(well.first, well.second);
}
emit dataChanged();
}
QVector<nmDataWellBase*> nmDataAnalyzeManager::getWellDataList() const
{
return m_vWellData;
}
//QVector<nmDataWellBase> nmDataAnalyzeManager::getWellDataListCopy() const
//{
// QVector<nmDataWellBase> result;
// result.reserve(m_vWellData.size());
//
// foreach(const nmDataWellBase* well, m_vWellData) {
// if(well) {
// result.append(*well); // 调用拷贝构造函数
// }
// }
//
// return result;
//}
//void nmDataAnalyzeManager::updateWellData(const QVector<nmDataWellBase>& newData)
//{
//
// // 确保数量一致
// if (newData.size() != m_vWellData.size()) {
// return;
// }
//
// for (int i = 0; i < newData.size(); ++i) {
// nmDataWellBase* existingWell = m_vWellData[i];
// const nmDataWellBase& newWell = newData[i];
//
// if (existingWell && existingWell->getWellName() == newWell.getWellName()) {
// // 根据类型更新数据
// if (auto existingVFractured = dynamic_cast<nmDataVerticalFracturedWell*>(existingWell)) {
// if (auto newVFractured = dynamic_cast<const nmDataVerticalFracturedWell*>(&newWell)) {
// *existingVFractured = *newVFractured;
// }
// }
// else if (auto existingHFractured = dynamic_cast<nmDataHorizontalFracturedWell*>(existingWell)) {
// if (auto newHFractured = dynamic_cast<const nmDataHorizontalFracturedWell*>(&newWell)) {
// *existingHFractured = *newHFractured;
// }
// } else if (auto existingVertical = dynamic_cast<nmDataVerticalWell*>(existingWell)) {
// if (auto newVertical = dynamic_cast<const nmDataVerticalWell*>(&newWell)) {
// *existingVertical = *newVertical; // 调用赋值运算符
// }
// }
// }
// }
//
//}
void nmDataAnalyzeManager::updateVerticalWells(const QVector<nmDataVerticalWell>& wells)
{
foreach(const auto& newWell, wells) {
foreach(auto* existingWell, m_vWellData) {
if(auto * vWell = dynamic_cast<nmDataVerticalWell * >(existingWell)) {
if(vWell->getWellName() == newWell.getWellName()) {
*vWell = newWell; // 调用赋值运算符
break;
}
}
}
}
}
void nmDataAnalyzeManager::updateVerticalFracturedWells(const QVector<nmDataVerticalFracturedWell>& wells)
{
foreach(const auto& newWell, wells) {
foreach(auto* existingWell, m_vWellData) {
if(auto * vWell = dynamic_cast<nmDataVerticalFracturedWell * >(existingWell)) {
if(vWell->getWellName() == newWell.getWellName()) {
*vWell = newWell; // 调用赋值运算符
break;
}
}
}
}
}
void nmDataAnalyzeManager::updateHorizontalFracturedWells(const QVector<nmDataHorizontalFracturedWell>& wells)
{
foreach(const auto& newWell, wells) {
foreach(auto* existingWell, m_vWellData) {
if(auto * vWell = dynamic_cast<nmDataHorizontalFracturedWell * >(existingWell)) {
if(vWell->getWellName() == newWell.getWellName()) {
*vWell = newWell; // 调用赋值运算符
break;
}
}
}
}
}
// 获取所有直井数据
QVector<nmDataVerticalWell*> nmDataAnalyzeManager::getVerticalWellData() const
{
QVector<nmDataVerticalWell*> verticalWells;
foreach(nmDataWellBase* well, m_vWellData) {
nmDataVerticalWell* vWell = dynamic_cast<nmDataVerticalWell*>(well);
if(vWell && !dynamic_cast<nmDataVerticalFracturedWell * >(well)) {
// 确保不是垂直裂缝井
verticalWells.append(vWell);
}
}
return verticalWells;
}
// 获取所有垂直裂缝井数据
QVector<nmDataVerticalFracturedWell*> nmDataAnalyzeManager::getVerticalFracturedWellData() const
{
QVector<nmDataVerticalFracturedWell*> vFracturedWells;
foreach(nmDataWellBase* well, m_vWellData) {
nmDataVerticalFracturedWell* vFracturedWell = dynamic_cast<nmDataVerticalFracturedWell*>(well);
if(vFracturedWell) {
vFracturedWells.append(vFracturedWell);
}
}
return vFracturedWells;
}
// 获取所有多段压裂水平井数据
QVector<nmDataHorizontalFracturedWell*> nmDataAnalyzeManager::getHorizontalFracturedWellData() const
{
QVector<nmDataHorizontalFracturedWell*> hFracturedWells;
foreach(nmDataWellBase* well, m_vWellData) {
nmDataHorizontalFracturedWell* hFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell*>(well);
if(hFracturedWell) {
hFracturedWells.append(hFracturedWell);
}
}
return hFracturedWells;
}
nmDataWellBase* nmDataAnalyzeManager::findWellByName(QString wellName) const
{
foreach(nmDataWellBase* pWell, m_vWellData) {
if(pWell && pWell->getWellName() == wellName) {
return pWell; // 找到匹配的井,返回指针
}
}
return nullptr; // 未找到匹配的井,返回 nullptr
}
nmDataWellBase* nmDataAnalyzeManager::findWellByCode(
const QString& sWellCode) const
{
if(sWellCode.isEmpty()) {
return nullptr;
}
foreach(nmDataWellBase* pWell, m_vWellData) {
if(pWell != nullptr && pWell->getWellCode() == sWellCode) {
return pWell;
}
}
return nullptr;
}
void nmDataAnalyzeManager::initCurWellData()
{
// 获取当前默认井数据
ZxDataWell* pWellData = zxCurWell;
if(pWellData == nullptr) {
return;
}
// 判断是哪一种井类型,初始化对应的参数
QString wellClass = pWellData->getWellClassEn();
// 获取当前井的压力、流量数据
ZxDataObjectList m_listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP);
ZxDataObjectList m_listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF);
ZxDataGaugeP* pGaugeP = nullptr;
ZxDataGaugeF* pGaugeF = nullptr;
// 遍历压力数据列表
for(int i = 0; i < m_listGaugeP.size(); ++i) {
if(pGaugeP = dynamic_cast<ZxDataGaugeP * >(m_listGaugeP[i])) { // 拿到第一条压力数据
break;
}
}
// 遍历流量数据列表
for(int i = 0; i < m_listGaugeF.size(); ++i) {
if(pGaugeF = dynamic_cast<ZxDataGaugeF * >(m_listGaugeF[i])) { // 拿到第一条流量数据
break;
}
}
// 获取的压力、流量数据
QVector<QPointF> vecPtsP, vecPtsF;
// 临时存储xy坐标
VecDouble vecX, vecY;
if(pGaugeP != nullptr) {
// 获取压力数据
if(pGaugeP->getDataVecXY(vecX, vecY)) {
int pointCount = vecX.size();
if(pointCount > vecY.size()) {
pointCount = vecY.size();
}
for(int i = 0; i < pointCount; ++i) {
QPointF pt(vecX[i], vecY[i]);
vecPtsP.append(pt);
}
}
}
if(pGaugeF != nullptr) {
vecX.clear();
vecY.clear();
// 获取流量数据
if(pGaugeF->getDataVecXY(vecX, vecY)) {
int pointCount = vecX.size();
if(pointCount > vecY.size()) {
pointCount = vecY.size();
}
for(int i = 0; i < pointCount; ++i) {
QPointF pt(vecX[i], vecY[i]);
vecPtsF.append(pt);
}
}
}
if(ZxBaseUtil::isSameStr(wellClass, "VerticalWell")) {
// 初始化直井默认参数
nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Vertical_Well);
nmDataVerticalWell* pVerticalWell = dynamic_cast<nmDataVerticalWell*>(pWell);
if(pVerticalWell == nullptr) {
return;
}
pVerticalWell->setWellName(pWellData->getName());
pVerticalWell->setWellCode(pWellData->getCode());
nmDataAttribute tempAttr = pVerticalWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pVerticalWell->setX(tempAttr);
tempAttr = pVerticalWell->getY();
tempAttr.setValue(pWellData->getLocationY());
pVerticalWell->setY(tempAttr);
tempAttr = pVerticalWell->getRadius();
tempAttr.setValue(pWellData->getWellRadius());
pVerticalWell->setRadius(tempAttr);
// 设置井的压力数据、流量数据
pVerticalWell->setPressurePoints(vecPtsP);
pVerticalWell->setFlowPoints(vecPtsF);
QVector<QVector<double>> vvecHistoryPressureData; //压力历史数据
QVector<QVector<double>> vvecHistoryLogData; // 历史双对数曲线数据
QVector<QVector<double>> vvecHistorySemiLogData; // 历史半对数曲线数据
this->calculationLogData(pVerticalWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
// 使用setter方法存储历史数据到井对象中
pVerticalWell->setHistoryPressure(vvecHistoryPressureData);
pVerticalWell->setHistoryLogLog(vvecHistoryLogData);
pVerticalWell->setHistorySemiLog(vvecHistorySemiLogData);
// 设置为当前查看的井
this->setCurWellData(pVerticalWell);
} else if(ZxBaseUtil::isSameStr(wellClass, "VerticalFracturedWell")) {
// 初始化垂直裂缝井默认参数
nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Vertical_Fractured_Well);
nmDataVerticalFracturedWell* pVFracturedWell = dynamic_cast<nmDataVerticalFracturedWell*>(pWell);
if(pVFracturedWell == nullptr) {
return;
}
pVFracturedWell->setWellName(pWellData->getName());
pVFracturedWell->setWellCode(pWellData->getCode());
nmDataAttribute tempAttr = pVFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pVFracturedWell->setX(tempAttr);
tempAttr = pVFracturedWell->getY();
tempAttr.setValue(pWellData->getLocationY());
pVFracturedWell->setY(tempAttr);
tempAttr = pVFracturedWell->getRadius();
tempAttr.setValue(pWellData->getWellRadius());
pVFracturedWell->setRadius(tempAttr);
// 设置井的压力数据、流量数据
pVFracturedWell->setPressurePoints(vecPtsP);
pVFracturedWell->setFlowPoints(vecPtsF);
QVector<QVector<double>> vvecHistoryPressureData; //压力历史数据
QVector<QVector<double>> vvecHistoryLogData; // 历史双对数曲线数据
QVector<QVector<double>> vvecHistorySemiLogData; // 历史半对数曲线数据
this->calculationLogData(pVFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
// 使用setter方法存储历史数据到井对象中
pVFracturedWell->setHistoryPressure(vvecHistoryPressureData);
pVFracturedWell->setHistoryLogLog(vvecHistoryLogData);
pVFracturedWell->setHistorySemiLog(vvecHistorySemiLogData);
// 更新裂缝位置信息
pVFracturedWell->setFracs();
// 设置为当前查看的井
this->setCurWellData(pVFracturedWell);
} else if(ZxBaseUtil::isSameStr(wellClass, "HorizontalMultiFracturedWell")) {
// 初始化多段压裂水平井默认参数
nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well);
nmDataHorizontalFracturedWell* pHFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell*>(pWell);
if(pHFracturedWell == nullptr) {
return;
}
pHFracturedWell->setWellName(pWellData->getName());
pHFracturedWell->setWellCode(pWellData->getCode());
nmDataAttribute tempAttr = pHFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pHFracturedWell->setX(tempAttr);
tempAttr = pHFracturedWell->getY();
tempAttr.setValue(pWellData->getLocationY());
pHFracturedWell->setY(tempAttr);
tempAttr = pHFracturedWell->getRadius();
tempAttr.setValue(pWellData->getWellRadius());
pHFracturedWell->setRadius(tempAttr);
// 设置井的压力数据、流量数据
pHFracturedWell->setPressurePoints(vecPtsP);
pHFracturedWell->setFlowPoints(vecPtsF);
QVector<QVector<double>> vvecHistoryPressureData; //压力历史数据
QVector<QVector<double>> vvecHistoryLogData; // 历史双对数曲线数据
QVector<QVector<double>> vvecHistorySemiLogData; // 历史半对数曲线数据
this->calculationLogData(pHFracturedWell, vvecHistoryPressureData, vvecHistoryLogData, vvecHistorySemiLogData);
//使用setter方法存储历史数据到井对象中
pHFracturedWell->setHistoryPressure(vvecHistoryPressureData);
pHFracturedWell->setHistoryLogLog(vvecHistoryLogData);
pHFracturedWell->setHistorySemiLog(vvecHistorySemiLogData);
// 计算裂缝数据
pHFracturedWell->setFracs();
// 设置为当前查看的井
this->setCurWellData(pHFracturedWell);
}
// 当前流动段井是数值分析入口井。这里显式初始化主井和结果井,
// 后续切换结果下拉框时不再改变主井身份。
if(m_pCurDataWell != nullptr && !m_pCurDataWell->getWellCode().isEmpty()) {
const QString sPrimaryWellCode = m_pCurDataWell->getWellCode();
m_oNumericalAnalysisCase.setPrimaryWellCode(sPrimaryWellCode);
m_oNumericalAnalysisCase.setPrimaryWellMode(
m_pCurDataWell->getFlowPoints().size() >= 2
? NM_CaseWell_RateControlled
: NM_CaseWell_Observation);
m_oNumericalAnalysisCase.setCurrentResultWellCode(sPrimaryWellCode);
}
}
void nmDataAnalyzeManager::calculationLogData(
nmDataWellBase* pWellData,
QVector<QVector<double>>& vvecHistoryData,
QVector<QVector<double>>& vvecLogPreData,
QVector<QVector<double>>& vvecSemiLogPreData)
{
// 清空输出参数
vvecHistoryData.clear();
vvecLogPreData.clear();
vvecSemiLogPreData.clear();
if(pWellData == nullptr) {
return;
}
// 初始化二维数组结构
vvecHistoryData.resize(2); // [0]=x, [1]=y
vvecLogPreData.resize(3); // [0]=x, [1]=y, [2]=z
vvecSemiLogPreData.resize(2); // [0]=x, [1]=pointData[0]
// 准备压力数据
QVector<QPointF> vecPressure = pWellData->getPressurePoints();
std::vector<Point> wellPressureData;
// 填充 wellPressureData 和 vvecHistoryData
foreach(const QPointF& qpoint, vecPressure) {
// wellPressureData
Point pt;
pt.x = qpoint.x();
pt.y = qpoint.y();
pt.z = 0.0;
wellPressureData.push_back(pt);
// vvecHistoryData
vvecHistoryData[0].append(qpoint.x()); // x
vvecHistoryData[1].append(qpoint.y()); // y
}
// Prepare flow-rate segment data.
QVector<QPointF> vecTimeQ = pWellData->getFlowPoints();
if(vecPressure.isEmpty() || vecTimeQ.size() < 2) {
return;
}
int nTimeNumQ = vecTimeQ.size() - 1;
if (nTimeNumQ <= 0) {
// 没有流量段数据,无法计算双对数/半对数曲线
return;
}
std::vector<double> timeQ(nTimeNumQ);
std::vector<double> q(nTimeNumQ);
for(int i = 0; i < nTimeNumQ; ++i) {
timeQ[i] = vecTimeQ[i + 1].x();
q[i] = vecTimeQ[i + 1].y();
}
// 调用 DLL 计算双对数曲线
std::vector<Point> logPre;
int iSectionFlowIndex = pWellData->getIndexF();
HMODULE hMod_solver = LoadLibrary(L"singlePhaseSolverDll.dll");
if(hMod_solver) {
typedef bool (*PreLog)(const std::vector<Point>&, const int&, double*, double*, int, std::vector<Point>&);
PreLog preLogFun = (PreLog)GetProcAddress(hMod_solver, "logLogPre");
if(nullptr == preLogFun) {
FreeLibrary(hMod_solver);
std::cout << "preLogFun failed!\n";
return;
}
bool bCalculated = preLogFun(wellPressureData, iSectionFlowIndex,
timeQ.data(), q.data(), nTimeNumQ, logPre);
if(!bCalculated || logPre.empty()) {
FreeLibrary(hMod_solver);
return;
}
// The solver's final point is not part of the plotted result.
for(std::vector<Point>::size_type i = 0; i + 1 < logPre.size(); ++i) {
//logFile << logPre[i].x << "\t" << logPre[i].y << "\t" << logPre[i].z << "\t" << std::endl;
vvecLogPreData[0].append(logPre[i].x); // x
vvecLogPreData[1].append(logPre[i].y); // y
vvecLogPreData[2].append(logPre[i].z); // z
}
// 填充半对数曲线数据 (x, pointData[0])
foreach(const auto& point, logPre) {
vvecSemiLogPreData[0].append(point.x); // x
double y_value = point.pointData.empty() ? 0.0 : point.pointData[0];
vvecSemiLogPreData[1].append(y_value); // pointData[0] 或默认值
}
FreeLibrary(hMod_solver);
}
}
nmDataWellBase* nmDataAnalyzeManager::appendWellData(ZxDataWell* pWellData)
{
if(pWellData == nullptr || pWellData->getCode().isEmpty()) {
return nullptr;
}
// 第一步:同一 WellCode 只创建一个数值井;重复选择时直接返回已有对象。
nmDataWellBase* pExistingWell = findWellByCode(pWellData->getCode());
if(pExistingWell != nullptr) {
return pExistingWell;
}
// 判断是哪一种井类型,初始化对应的参数
QString sWellClass = pWellData->getWellClassEn();
// 获取当前井的压力、流量数据
ZxDataObjectList m_listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP);
ZxDataObjectList m_listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF);
ZxDataGaugeP* pGaugeP = nullptr;
ZxDataGaugeF* pGaugeF = nullptr;
// 遍历压力数据列表
for(int i = 0; i < m_listGaugeP.size(); ++i) {
if(pGaugeP = dynamic_cast<ZxDataGaugeP * >(m_listGaugeP[i])) { // 拿到第一条压力数据
break;
}
}
// 遍历流量数据列表
for(int i = 0; i < m_listGaugeF.size(); ++i) {
if(pGaugeF = dynamic_cast<ZxDataGaugeF * >(m_listGaugeF[i])) { // 拿到第一条流量数据
break;
}
}
// 获取的压力、流量数据
QVector<QPointF> vecPtsP, vecPtsF;
// 临时存储xy坐标
VecDouble vecX, vecY;
if(pGaugeP != nullptr) {
// 获取压力数据
if(pGaugeP->getDataVecXY(vecX, vecY)) {
int pointCount = vecX.size();
if(pointCount > vecY.size()) {
pointCount = vecY.size();
}
for(int i = 0; i < pointCount; ++i) {
QPointF pt(vecX[i], vecY[i]);
vecPtsP.append(pt);
}
}
}
if(pGaugeF != nullptr) {
vecX.clear();
vecY.clear();
// 获取流量数据
if(pGaugeF->getDataVecXY(vecX, vecY)) {
int pointCount = vecX.size();
if(pointCount > vecY.size()) {
pointCount = vecY.size();
}
for(int i = 0; i < pointCount; ++i) {
QPointF pt(vecX[i], vecY[i]);
vecPtsF.append(pt);
}
}
}
int nIndexF; //当前井的流动段索引
// 更新当前井的流动段的索引,默认为最后一段
nIndexF = vecPtsF.count() - 1;
nmDataWellBase* pAddedWell = nullptr;
if(ZxBaseUtil::isSameStr(sWellClass, "VerticalWell")) {
// 初始化直井默认参数
nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Vertical_Well);
nmDataVerticalWell* pVerticalWell = dynamic_cast<nmDataVerticalWell*>(pWell);
if(pVerticalWell == nullptr) {
return nullptr;
}
pAddedWell = pVerticalWell;
pVerticalWell->setWellName(pWellData->getName());
pVerticalWell->setWellCode(pWellData->getCode());
nmDataAttribute tempAttr = pVerticalWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pVerticalWell->setX(tempAttr);
tempAttr = pVerticalWell->getY();
tempAttr.setValue(pWellData->getLocationY());
pVerticalWell->setY(tempAttr);
tempAttr = pVerticalWell->getRadius();
tempAttr.setValue(pWellData->getWellRadius());
pVerticalWell->setRadius(tempAttr);
// 设置井的压力数据、流量数据
pVerticalWell->setPressurePoints(vecPtsP);
pVerticalWell->setFlowPoints(vecPtsF);
// 设置流量段索引
pVerticalWell->setIndexF(nIndexF);
} else if(ZxBaseUtil::isSameStr(sWellClass, "VerticalFracturedWell")) {
// 初始化垂直裂缝井默认参数
nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Vertical_Fractured_Well);
nmDataVerticalFracturedWell* pVFracturedWell = dynamic_cast<nmDataVerticalFracturedWell*>(pWell);
if(pVFracturedWell == nullptr) {
return nullptr;
}
pAddedWell = pVFracturedWell;
pVFracturedWell->setWellName(pWellData->getName());
pVFracturedWell->setWellCode(pWellData->getCode());
nmDataAttribute tempAttr = pVFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pVFracturedWell->setX(tempAttr);
tempAttr = pVFracturedWell->getY();
tempAttr.setValue(pWellData->getLocationY());
pVFracturedWell->setY(tempAttr);
tempAttr = pVFracturedWell->getRadius();
tempAttr.setValue(pWellData->getWellRadius());
pVFracturedWell->setRadius(tempAttr);
// 设置井的压力数据、流量数据
pVFracturedWell->setPressurePoints(vecPtsP);
pVFracturedWell->setFlowPoints(vecPtsF);
// 更新裂缝位置信息
pVFracturedWell->setFracs();
// 设置流量段索引
pVFracturedWell->setIndexF(nIndexF);
} else if(ZxBaseUtil::isSameStr(sWellClass, "HorizontalMultiFracturedWell")) {
// 初始化多段压裂水平井默认参数
nmDataWellBase* pWell = this->createWell(NM_WELL_MODEL::Horizontal_Fractured_Well);
nmDataHorizontalFracturedWell* pHFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell*>(pWell);
if(pHFracturedWell == nullptr) {
return nullptr;
}
pAddedWell = pHFracturedWell;
pHFracturedWell->setWellName(pWellData->getName());
pHFracturedWell->setWellCode(pWellData->getCode());
nmDataAttribute tempAttr = pHFracturedWell->getX();
tempAttr.setValue(pWellData->getLocationX());
pHFracturedWell->setX(tempAttr);
tempAttr = pHFracturedWell->getY();
tempAttr.setValue(pWellData->getLocationY());
pHFracturedWell->setY(tempAttr);
tempAttr = pHFracturedWell->getRadius();
tempAttr.setValue(pWellData->getWellRadius());
pHFracturedWell->setRadius(tempAttr);
// 设置井的压力数据、流量数据
pHFracturedWell->setPressurePoints(vecPtsP);
pHFracturedWell->setFlowPoints(vecPtsF);
// 计算裂缝数据
pHFracturedWell->setFracs();
// 设置流量段索引
pHFracturedWell->setIndexF(nIndexF);
}
// 第二步:不支持的项目井型不进入 Map也不发送伪造的新增井通知。
if(pAddedWell == nullptr) {
return nullptr;
}
// 第三步:井数据完整建立后,再通知 Map 和参数面板新增井分组。
{
syncWellAttrs();
QString code = pWellData->getCode();
QString name = pWellData->getName();
QStringList paras;
for (int i = 0; i < WELL_PARA_DESC_COUNT; ++i) {
if (isWellParaOf(WELL_PARA_DESCS[i], pAddedWell->getWellType())) {
paras << WELL_PARA_DESCS[i].sName;
}
}
emit sigWellAdded(code, name, paras);
}
return pAddedWell;
}
void nmDataAnalyzeManager::appendNmWellData(nmDataWellBase* pWellData)
{
if (pWellData == nullptr)
return;
bool bWellAdded = false;
if (!m_vWellData.contains(pWellData))
{
m_vWellData.append(pWellData);
bWellAdded = true;
}
// 外部先构造、再加入 Map 的井没有经过 createWell(),需要在这里统一
// 使旧网格失效。已由 createWell() 登记的井不重复增加输入版本号。
if(bWellAdded)
{
m_oNumericalAnalysisCase.invalidateGrid();
}
// 通知面板新增井分组
{
syncWellAttrs();
QString code = pWellData->getWellCode();
QString name = pWellData->getWellName();
QStringList paras;
for (int i = 0; i < WELL_PARA_DESC_COUNT; ++i) {
if (isWellParaOf(WELL_PARA_DESCS[i], pWellData->getWellType())) {
paras << WELL_PARA_DESCS[i].sName;
}
}
emit sigWellAdded(code, name, paras);
}
}
bool nmDataAnalyzeManager::replaceWellData(nmDataWellBase* pOldWell,
nmDataWellBase* pNewWell)
{
// 先完成所有无副作用的前置校验,失败时保持双方所有权不变。
if (pOldWell == nullptr || pNewWell == nullptr ||
pOldWell == pNewWell || m_vWellData.contains(pNewWell) ||
pOldWell->getWellCode().isEmpty() ||
pOldWell->getWellCode() != pNewWell->getWellCode())
{
return false;
}
int nOldIndex = m_vWellData.indexOf(pOldWell);
if (nOldIndex < 0)
{
return false;
}
// 删除旧对象前保存观察者通知所需信息,后续不得再解引用旧指针。
QString sOldCode = pOldWell->getWellCode();
QString sOldName = pOldWell->getWellName();
bool bWasCurrentWell = m_pCurDataWell == pOldWell;
// 第一步:在原位置替换全部旧指针;同一 WellCode 的分析方案关联无需迁移。
for (int nIndex = m_vWellData.size() - 1; nIndex >= 0; --nIndex)
{
if (m_vWellData[nIndex] == pOldWell)
{
m_vWellData.remove(nIndex);
}
}
m_vWellData.insert(qMin(nOldIndex, m_vWellData.size()), pNewWell);
// 井型改变会改变 PEBI 几何输入,旧求解器顺序和网格必须整体失效。
m_oNumericalAnalysisCase.invalidateGrid();
if (bWasCurrentWell)
{
m_pCurDataWell = pNewWell;
}
// 第二步:先让注册表指向新对象,再销毁旧对象并统一通知观察者。
syncWellAttrs();
delete pOldWell;
QStringList listParameters;
for (int nIndex = 0; nIndex < WELL_PARA_DESC_COUNT; ++nIndex)
{
if (isWellParaOf(WELL_PARA_DESCS[nIndex],
pNewWell->getWellType()))
{
listParameters << WELL_PARA_DESCS[nIndex].sName;
}
}
// 沿用既有移除和添加信号,保证工程树及属性页无需感知替换细节。
emit sigWellRemoved(sOldCode, sOldName);
emit sigWellAdded(pNewWell->getWellCode(),
pNewWell->getWellName(),
listParameters);
emit dataChanged();
return true;
}
/// @brief 创建油藏数据对象从界面读取PVT单值和分层数据构建reservoir对象
void nmDataAnalyzeManager::createReservoir()
{
// 1. 获取上下文
iSubWndFitting* pSubWndFitting = nmDataAnalyzeManager::getCurrentFitting();
nmDataAnalyzeContextProvider* pCtx = nmDataAnalyzeContext::provider();
Q_ASSERT(nullptr != pSubWndFitting);
Q_ASSERT(nullptr != pCtx);
PvtFluidType eType = WFT_Null;
pCtx->getBasicPft(pSubWndFitting, eType);
// 2. 读取PVT单值参数
QMap<QString, double> mapPvtValues = readPvtSingleValues(pCtx, pSubWndFitting, eType);
// 3. 读取分层数据,提取储层基础参数
VVecVariant vvecLayerData;
pCtx->getBasicDataLayers(pSubWndFitting, vvecLayerData);
double dThickness = 10;
double dPorosity = 0.5;
double dCf = 1.0;
double dInitialPre = 30.0;
if(!vvecLayerData.isEmpty() && vvecLayerData[0].size() >= 5) {
dThickness = vvecLayerData[0][1].toDouble();
dPorosity = vvecLayerData[0][2].toDouble();
dCf = vvecLayerData[0][3].toDouble();
dInitialPre = vvecLayerData[0][4].toDouble();
}
// 4. 构建油藏对象
if(m_reservoirData != nullptr) {
delete m_reservoirData;
m_reservoirData = nullptr;
}
m_reservoirData = new nmDataReservoir;
// 注册储层属性到 nmAttrRegistryname 对齐 XML ParaItem Name
m_pAttrRegistry->clear();
m_pAttrRegistry->regAttr("h", &m_reservoirData->getThickness());
m_pAttrRegistry->regAttr("Pi", &m_reservoirData->getInitialPressure());
m_pAttrRegistry->regAttr("K", &m_reservoirData->getPermeability());
m_pAttrRegistry->regAttr("phi", &m_reservoirData->getPorosity());
m_pAttrRegistry->regAttr("Cti", &m_reservoirData->getCt());
m_pAttrRegistry->regAttr("Cf", &m_reservoirData->getCf());
m_pAttrRegistry->regAttr("Soi", &m_reservoirData->getSoi());
m_pAttrRegistry->regAttr("Swi", &m_reservoirData->getSwi());
// 按相态填充PVT字段和压缩系数
populateReservoirByPhase(m_reservoirData, eType, mapPvtValues, dCf);
// 设置基础属性
setReservoirAttr(m_reservoirData->getInitialPressure(), dInitialPre);
setReservoirAttr(m_reservoirData->getReservoirType(), QString("Homogeneous"));
setReservoirAttr(m_reservoirData->getThickness(), dThickness);
setReservoirAttr(m_reservoirData->getPorosity(), dPorosity);
// 5. 创建默认分层
createDefaultLayer(dThickness, m_vecLayers);
}
nmDataAxis* nmDataAnalyzeManager::getAxisData() const
{
return m_axisData;
}
void nmDataAnalyzeManager::setAxisData(nmDataAxis* pAxisData)
{
if(pAxisData != nullptr) {
m_axisData = pAxisData;
}
}
nmDataReservoir* nmDataAnalyzeManager::getReservoirData() const
{
return m_reservoirData;
}
nmAttrRegistry* nmDataAnalyzeManager::getAttrRegistry() const
{
return m_pAttrRegistry;
}
void nmDataAnalyzeManager::syncWellAttrs()
{
if (m_pAttrRegistry == NULL) return;
QSet<QString> activeAttrNames;
QSet<QString> activeWellCodes;
foreach (nmDataWellBase* pWell, m_vWellData)
{
if (pWell == nullptr) continue;
QString code = pWell->getWellCode();
if (code.isEmpty() || activeWellCodes.contains(code)) continue;
activeWellCodes.insert(code);
for (int i = 0; i < WELL_PARA_DESC_COUNT; ++i)
{
if (!isWellParaOf(WELL_PARA_DESCS[i], pWell->getWellType())) {
continue;
}
nmDataAttribute* pAttr = WELL_PARA_DESCS[i].getAttr(*pWell);
if (pAttr != NULL)
{
QString name = code + "_" + WELL_PARA_DESCS[i].sName;
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, pAttr);
}
}
}
// 精确清理已经不存在的井参数注册,兼容 wellCode 自身包含下划线。
foreach (const QString& name, m_pAttrRegistry->registeredNames())
{
if (isWellParaAttrName(name) && !activeAttrNames.contains(name))
{
m_pAttrRegistry->unregAttr(name);
}
}
}
void nmDataAnalyzeManager::syncGeometryAttrs()
{
if (m_pAttrRegistry == NULL) return;
// 沿用井参数注册方式,以“对象编码 + XML 参数名”建立双向绑定。
QSet<QString> activeAttrNames;
int fractureIndex = 0;
foreach (nmDataFracture* pFracture, m_vFractureData)
{
// DFN 裂缝由专用功能批量管理,不作为独立裂缝显示在参数面板中。
if (pFracture == nullptr || pFracture->getFractureType().getValue().toString() == "DFN") {
continue;
}
QString code = QString("FRAC%1").arg(++fractureIndex, 4, 10, QChar('0'));
for (int i = 0; i < GEOMETRY_PARA_DESC_COUNT; ++i)
{
if (GEOMETRY_PARA_DESCS[i].getFractureAttr == NULL) continue;
nmDataAttribute* pAttr = GEOMETRY_PARA_DESCS[i].getFractureAttr(*pFracture);
if (pAttr != NULL)
{
QString name = code + "_" + GEOMETRY_PARA_DESCS[i].sName;
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, pAttr);
}
}
}
for (int faultIndex = 0; faultIndex < m_vFaultData.size(); ++faultIndex)
{
nmDataFault* pFault = m_vFaultData[faultIndex];
if (pFault == nullptr) continue;
QString code = QString("FAULT%1").arg(faultIndex + 1, 4, 10, QChar('0'));
for (int i = 0; i < GEOMETRY_PARA_DESC_COUNT; ++i)
{
if (GEOMETRY_PARA_DESCS[i].getFaultAttr == NULL) continue;
nmDataAttribute* pAttr = GEOMETRY_PARA_DESCS[i].getFaultAttr(*pFault);
if (pAttr != NULL)
{
QString name = code + "_" + GEOMETRY_PARA_DESCS[i].sName;
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, pAttr);
}
}
}
for (int regionIndex = 0; regionIndex < m_vRegionData.size(); ++regionIndex)
{
nmDataRegion* pRegion = m_vRegionData[regionIndex];
if (pRegion == nullptr) continue;
QString code = QString("REGION%1").arg(regionIndex + 1, 4, 10, QChar('0'));
for (int i = 0; i < GEOMETRY_PARA_DESC_COUNT; ++i)
{
if (GEOMETRY_PARA_DESCS[i].getRegionAttr == NULL) continue;
nmDataAttribute* pAttr = GEOMETRY_PARA_DESCS[i].getRegionAttr(*pRegion);
if (pAttr != NULL)
{
QString name = code + "_" + GEOMETRY_PARA_DESCS[i].sName;
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, pAttr);
}
}
}
for (int markIndex = 0; markIndex < m_vRegionMarkData.size(); ++markIndex)
{
nmDataRegionMark* pMark = m_vRegionMarkData[markIndex];
if (pMark == nullptr) continue;
QString code = QString("RMARK%1").arg(markIndex + 1, 4, 10, QChar('0'));
for (int i = 0; i < GEOMETRY_PARA_DESC_COUNT; ++i)
{
if (GEOMETRY_PARA_DESCS[i].getRegionMarkAttr == NULL) continue;
nmDataAttribute* pAttr = GEOMETRY_PARA_DESCS[i].getRegionMarkAttr(*pMark);
if (pAttr != NULL)
{
QString name = code + "_" + GEOMETRY_PARA_DESCS[i].sName;
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, pAttr);
}
}
}
if (m_outlineData != nullptr)
{
if (m_outlineData->getOutlineType() == NM_Rect_Outline_Type)
{
const QString code = "BOUNDARY";
const QStringList names = QStringList() << "BR_XMin" << "BR_YMin" << "BR_XMax" << "BR_YMax";
nmDataAttribute* attrs[] = {
&m_outlineData->getLeftAttribute(),
&m_outlineData->getBottomAttribute(),
&m_outlineData->getRightAttribute(),
&m_outlineData->getTopAttribute()
};
for (int i = 0; i < names.count(); ++i) {
const QString name = code + "_" + names[i];
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, attrs[i]);
}
}
else if (m_outlineData->getOutlineType() == NM_Round_Outline_Type)
{
const QString code = "BOUNDARY";
const QStringList names = QStringList() << "BC_CenterX" << "BC_CenterY" << "BC_Radius";
nmDataAttribute* attrs[] = {
&m_outlineData->getCenterXAttribute(),
&m_outlineData->getCenterYAttribute(),
&m_outlineData->getRadiusAttribute()
};
for (int i = 0; i < names.count(); ++i) {
const QString name = code + "_" + names[i];
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, attrs[i]);
}
}
else if (m_outlineData->getOutlineType() == NM_Polygon_Outline_Type)
{
// 多边形顶点数量动态变化,每个 Point 使用独立对象编码注册 X/Y。
for (int pointIndex = 0; pointIndex < m_outlineData->getOutlinePointCount(); ++pointIndex)
{
const QString code = QString("BVERT%1").arg(pointIndex + 1, 4, 10, QChar('0'));
nmDataAttribute* pX = m_outlineData->getPointXAttribute(pointIndex);
nmDataAttribute* pY = m_outlineData->getPointYAttribute(pointIndex);
if (pX != nullptr)
{
const QString name = code + "_BP_X";
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, pX);
}
if (pY != nullptr)
{
const QString name = code + "_BP_Y";
activeAttrNames.insert(name);
m_pAttrRegistry->regAttr(name, pY);
}
}
}
}
// 精确清理已删除对象、已删除顶点和旧边界类型留下的注册项。
foreach (const QString& name, m_pAttrRegistry->registeredNames())
{
if (isGeometryParaAttrName(name) && !activeAttrNames.contains(name))
{
m_pAttrRegistry->unregAttr(name);
}
}
}
nmDataReservoir nmDataAnalyzeManager::getReservoirDataCopy() const
{
if(m_reservoirData) {
return *m_reservoirData; // 调用拷贝构造函数
}
return nmDataReservoir(); // 返回默认构造对象
}
void nmDataAnalyzeManager::updateReservoirData(const nmDataReservoir& newData)
{
if(m_reservoirData) {
*m_reservoirData = newData; // 调用赋值运算符
if (m_pAttrRegistry) {
m_pAttrRegistry->refreshAll();
}
}
}
nmDataGeoRef* nmDataAnalyzeManager::createGeoRefData()
{
if(m_pGeoRefData != nullptr) {
delete m_pGeoRefData;
m_pGeoRefData = nullptr;
}
m_pGeoRefData = new nmDataGeoRef;
return m_pGeoRefData;
}
nmDataGeoRef* nmDataAnalyzeManager::getGeoRefData() const
{
return m_pGeoRefData;
}
nmDataGeoRef nmDataAnalyzeManager::getGeoRefDataCopy() const
{
if(m_pGeoRefData) {
return *m_pGeoRefData; // 调用拷贝构造函数
}
return nmDataGeoRef(); // 返回默认构造对象
}
void nmDataAnalyzeManager::updateGeoRefData(const nmDataGeoRef& newData)
{
if(m_pGeoRefData == nullptr) {
m_pGeoRefData = new nmDataGeoRef;
}
*m_pGeoRefData = newData; // 调用赋值运算符
}
nmDataForecast* nmDataAnalyzeManager::createForecastData()
{
if(m_pForecastData != nullptr) {
delete m_pForecastData;
m_pForecastData = nullptr;
}
m_pForecastData = new nmDataForecast;
return m_pForecastData;
}
nmDataForecast* nmDataAnalyzeManager::getForecastData() const
{
return m_pForecastData;
}
nmDataForecast nmDataAnalyzeManager::getForecastDataCopy() const
{
if(m_pForecastData) {
return *m_pForecastData; // 调用拷贝构造函数
}
return nmDataForecast(); // 返回默认构造对象
}
void nmDataAnalyzeManager::updateForecastData(const nmDataForecast& newData)
{
if(m_pForecastData == nullptr) {
m_pForecastData = new nmDataForecast;
}
*m_pForecastData = newData; // 调用赋值运算符
}
nmDataSensitive* nmDataAnalyzeManager::createSensitiveData()
{
// 如果已有数据,先删除再创建
if (m_pSensitiveData != nullptr) {
delete m_pSensitiveData;
m_pSensitiveData = nullptr;
}
// 创建一个新的 nmDataSensitive 对象
m_pSensitiveData = new nmDataSensitive();
// 初始化 Calculation Type
m_pSensitiveData->setCalculationType(nmDataSensitive::CALC_DETERMINISTIC);
// 设置总模型数为 0后面可以通过 setTotalModelCount 修改
m_pSensitiveData->setTotalModelCount(0);
// 创建变量列表
QList<nmDataSensitive::VariableSampling> vars;
// 定义一个通用的添加变量的辅助函数
auto addVar = [&](const QString& group,
const QString& name,
double model, double min, double max,
const QString& unit)
{
nmDataSensitive::VariableSampling vs;
vs.setVarGroup(group); // 设置变量组
vs.setVarName(name); // 设置变量名
vs.setEnabled(false); // 默认不勾选
vs.setMode(nmDataSensitive::VariableSampling::MODE_AUTOMATIC); // 自动模式
vs.setLog(false); // 默认不选 Log
vs.setNumber(5); // 默认 Number 为 5
// 设置 modelValue、minValue、maxValue 和单位
nmDataAttribute& modelValue = vs.getModelValue();
modelValue.setName(name + " Model");
modelValue.setUnit(unit);
modelValue.setValue(model);
nmDataAttribute& minValue = vs.getMinValue();
minValue.setName(name + " Min");
minValue.setUnit(unit);
minValue.setValue(min);
nmDataAttribute& maxValue = vs.getMaxValue();
maxValue.setName(name + " Max");
maxValue.setUnit(unit);
maxValue.setValue(max);
vars.append(vs); // 将变量添加到变量列表中
};
// 初始化变量组和每个变量
// === Tested Well ===
addVar("Tested Well", "Zw", 15.0, 7.5, 30.0, "ft");
addVar("Tested Well", "Hw", 10.0, 5.0, 20.0, "ft");
addVar("Tested Well", "Lw", 12.0, 6.0, 18.0, "ft");
addVar("Tested Well", "Skin", 1.0, 0.5, 1.5, "dimensionless");
addVar("Tested Well", "C", 0.3, 0.1, 1.0, "dimensionless");
// === Reservoir ===
addVar("Reservoir", "Pi", 1000.0, 800.0, 1200.0, "psi");
addVar("Reservoir", "k", 150.0, 100.0, 200.0, "mD");
addVar("Reservoir", "h", 50.0, 30.0, 70.0, "ft");
addVar("Reservoir", "φ", 0.2, 0.1, 0.4, "dimensionless");
addVar("Reservoir", "ntg", 0.9, 0.7, 1.0, "dimensionless");
addVar("Reservoir", "kz/kr", 1.0, 0.5, 1.5, "dimensionless");
// === Pvt ===
addVar("Pvt", "Total compressibility", 5e-6, 2e-6, 8e-6, "1/psi");
// 将变量列表设置到敏感性数据对象中
m_pSensitiveData->setVariables(vars);
// 返回已初始化的敏感性数据对象
return m_pSensitiveData;
}
nmDataSensitive* nmDataAnalyzeManager::getSensitiveData() const
{
return m_pSensitiveData;
}
nmDataSensitive nmDataAnalyzeManager::getSensitiveDataCopy() const
{
if(m_pSensitiveData) {
return *m_pSensitiveData; // 调用拷贝构造函数
}
return nmDataSensitive(); // 返回默认构造对象
}
void nmDataAnalyzeManager::updateSensitiveData(const nmDataSensitive& newData)
{
if(m_pSensitiveData == nullptr) {
m_pSensitiveData = new nmDataSensitive();
}
*m_pSensitiveData = newData; // 调用赋值运算符
}
nmDataDiagnostic* nmDataAnalyzeManager::getDiagnosticData() const {
return m_pDiagnosticData;
}
bool nmDataAnalyzeManager::resetFromDiagnostic()
{
// 获取当前井和储层数据
nmDataWellBase* pCurrentWell = getCurWellData();
nmDataReservoir* pReservoirData = getReservoirData();
if(!pCurrentWell || !pReservoirData) {
return false;
}
// 获取"双对数"线性数据
QVector<QVector<double>> rawData = pCurrentWell->getHistoryLogLog();
if(rawData.isEmpty() || rawData.size() != 3) {
return false;
}
try {
// 创建诊断对象并使用修正后的数据
if(!m_pDiagnosticData) {
m_pDiagnosticData = new nmDataDiagnostic();
}
m_pDiagnosticData->resetFromDiagnostic(rawData, pCurrentWell, pReservoirData);
// 应用诊断结果到相应参数
applyDiagnosticResults(m_pDiagnosticData, pCurrentWell, pReservoirData);
return true;
}
catch(...) {
return false;
}
}
bool nmDataAnalyzeManager::resetFromAnalytical()
{
// 获取当前井和储层数据
nmDataWellBase* pCurrentWell = getCurWellData();
nmDataReservoir* pReservoirData = getReservoirData();
if(!pCurrentWell || !pReservoirData) {
return false;
}
try {
// 重置储层参数为默认值
pReservoirData->resetToDefaults();
// 重置井参数为默认值
pCurrentWell->resetToDefaults();
return true;
}
catch(...) {
return false;
}
}
// 应用诊断结果
void nmDataAnalyzeManager::applyDiagnosticResults(nmDataDiagnostic* diagnostic, nmDataWellBase* wellData, nmDataReservoir* reservoirData)
{
if(!diagnostic || !wellData || !reservoirData) return;
// 获取诊断结果
double diagnosticPerm = diagnostic->getDiagnosticPermeability().getValue().toDouble();
double diagnosticStorage = diagnostic->getDiagnosticWellboreStorage().getValue().toDouble();
double diagnosticTrans = diagnostic->getDiagnosticTransmissibility().getValue().toDouble();
double diagnosticSkin = diagnostic->getDiagnosticSkin().getValue().toDouble();
// 应用渗透率到储层数据
nmDataAttribute& reservoirPerm = reservoirData->getPermeability();
reservoirPerm.setValue(diagnosticPerm);
// 应用井筒储存到井数据
nmDataAttribute& wellStorage = wellData->getWellboreStorage();
wellStorage.setValue(diagnosticStorage);
// 应用导流能力到储层数据
nmDataAttribute& reservoirTrans = reservoirData->getTransmissibility();
reservoirTrans.setValue(diagnosticTrans);
// 应用皮损系数到井的第一段射孔
if(wellData->getPerforationCount() > 0) {
nmDataPerforation* firstPerforation = wellData->getPerforation(0);
if(firstPerforation) {
firstPerforation->getSkin().setValue(diagnosticSkin);
}
}
}
nmDataAutomaticFitting* nmDataAnalyzeManager::createAutomaticFittingData()
{
if(m_pAutomaticFittingData != nullptr) {
delete m_pAutomaticFittingData;
m_pAutomaticFittingData = nullptr;
}
m_pAutomaticFittingData = new nmDataAutomaticFitting;
return m_pAutomaticFittingData;
}
nmDataAutomaticFitting* nmDataAnalyzeManager::getAutomaticFittingData() const
{
return m_pAutomaticFittingData;
}
nmDataAutomaticFitting nmDataAnalyzeManager::getAutomaticFittingDataCopy() const
{
if(m_pAutomaticFittingData) {
return *m_pAutomaticFittingData; // 调用拷贝构造函数
}
return nmDataAutomaticFitting(); // 返回默认构造对象
}
void nmDataAnalyzeManager::updateAutomaticFittingData(const nmDataAutomaticFitting& newData)
{
if(m_pAutomaticFittingData == nullptr) {
m_pAutomaticFittingData = new nmDataAutomaticFitting;
}
*m_pAutomaticFittingData = newData; // 调用赋值运算符
}
/// @brief 从PVT结果页读取曲线数组确定求解器模型类型
/// 按相态获取PVT数组参数获取不到则回退到常数PVT模型Gas除外弹警告
void nmDataAnalyzeManager::initPvtParaFromSubFit()
{
// 1. 获取上下文
iSubWndFitting* pSubWndFitting = nmDataAnalyzeManager::getCurrentFitting();
nmDataAnalyzeContextProvider* pCtx = nmDataAnalyzeContext::provider();
Q_ASSERT(nullptr != pSubWndFitting);
Q_ASSERT(nullptr != pCtx);
if(nullptr == pSubWndFitting || nullptr == pCtx) {
return;
}
PvtFluidType eType = WFT_Null;
pCtx->getBasicPft(pSubWndFitting, eType);
// 2. 清理旧的PVT参数对象新建空对象
if(m_pebiPvtPara != nullptr) {
delete m_pebiPvtPara;
m_pebiPvtPara = nullptr;
}
m_pebiPvtPara = new nmDataPvtParaForPebi;
// 压力横坐标,首次成功读取时赋值
QVector<double> vecPressure;
// 3. 按相态分支获取PVT曲线并确定求解器类型
switch(eType) {
case WFT_Oil:
// 油相获取到变量PVT曲线则升级模型否则保持常数PVT
if(tryFetchPhasePvtCurves(pCtx, pSubWndFitting, WFT_Oil,
"Bo", "Co", "Miuo", m_pebiPvtPara, vecPressure)) {
setSolverModelType(SMT_Oil_VariablePvt);
} else {
setSolverModelType(SMT_Oil_ConstPvt);
}
break;
case WFT_Water:
// 水相获取到变量PVT曲线则升级模型否则保持常数PVT
if(tryFetchPhasePvtCurves(pCtx, pSubWndFitting, WFT_Water,
"Bw", "Cw", "Miuw", m_pebiPvtPara, vecPressure)) {
setSolverModelType(SMT_Water_VariablePvt);
} else {
setSolverModelType(SMT_Water_ConstPvt);
}
break;
case WFT_Gas:
// 气相必须获取到全部PVT曲线否则弹警告并返回
setSolverModelType(SMT_Gas_VariablePvt);
if(!tryFetchPhasePvtCurves(pCtx, pSubWndFitting, WFT_Gas,
"Bg", "Cg", "Miug", m_pebiPvtPara, vecPressure)) {
QMessageBox::warning(nullptr, tr("Warning"), tr("Please select all gas PVT parameters."));
return;
}
break;
case WFT_Oil_Water: {
// 油水两相油、水PVT曲线必须全部获取否则返回
setSolverModelType(SMT_Oil_Water_TwoPhase);
bool bOilOk = tryFetchPhasePvtCurves(pCtx, pSubWndFitting, WFT_Oil_Water,
"Bo", "Co", "Miuo", m_pebiPvtPara, vecPressure);
bool bWaterOk = tryFetchPhasePvtCurves(pCtx, pSubWndFitting, WFT_Oil_Water,
"Bw", "Cw", "Miuw", m_pebiPvtPara, vecPressure);
if(!(bOilOk && bWaterOk)) {
return;
}
// Diffusion相渗数据来自Diffusion页面
VVecDouble vvecDiffusionKK;
if(pCtx->getDiffusionRstOf(pSubWndFitting, DSO_KK, vvecDiffusionKK)) {
applyDiffusionKkToPebiPvt(m_pebiPvtPara, vvecDiffusionKK);
}
break;
}
default:
break;
}
// 4. 统一设置压力横坐标
setPebiPressureIfEmpty(m_pebiPvtPara, vecPressure);
}
nmDataRegionMark *nmDataAnalyzeManager::createRegionMark()
{
nmDataRegionMark* regionMarkData = new nmDataRegionMark;
m_vRegionMarkData.append(regionMarkData);
return regionMarkData;
}
QVector<nmDataRegionMark*> nmDataAnalyzeManager::getRegionMarkDataList() const
{
return m_vRegionMarkData;
}
bool nmDataAnalyzeManager::removeRegionMarkData(nmDataRegionMark * pData)
{
if(pData) {
// 遍历 m_vRegionMarkData 数组,找到并移除对应的对象
for(int i = 0; i < m_vRegionMarkData.size(); ++i) {
if(m_vRegionMarkData[i] == pData) {
delete m_vRegionMarkData[i]; // 释放内存
m_vRegionMarkData.remove(i);
notifyGeometryListChanged();
return true; // 移除成功,返回 true
}
}
}
return false; // 未找到或移除失败,返回 false
}
QVector<nmDataRegionMark> nmDataAnalyzeManager::getRegionMarkDataListCopy() const
{
QVector<nmDataRegionMark> result;
result.reserve(m_vRegionMarkData.size());
foreach(const nmDataRegionMark* regionMark, m_vRegionMarkData) {
if(regionMark) {
result.append(*regionMark); // 调用拷贝构造函数
}
}
return result;
}
void nmDataAnalyzeManager::updateRegionMarkData(const QVector<nmDataRegionMark>& newData)
{
// 确保数量一致
if(newData.size() != m_vRegionMarkData.size()) {
return;
}
for(int i = 0; i < newData.size(); ++i) {
if(m_vRegionMarkData[i]) {
// 只更新内容,不改变指针
*m_vRegionMarkData[i] = newData[i]; // 调用赋值运算符
m_vRegionMarkData[i]->notifyDataChanged();
}
}
// 批量赋值不触发属性信号,完成后统一刷新参数面板。
if (m_pAttrRegistry) {
m_pAttrRegistry->refreshAll();
}
}
nmDataOutline *nmDataAnalyzeManager::createOutline()
{
if(m_outlineData != nullptr) {
delete m_outlineData;
m_outlineData = nullptr;
}
m_outlineData = new nmDataOutline;
return m_outlineData;
}
nmDataOutline* nmDataAnalyzeManager::getOutlineData()
{
return m_outlineData;
}
bool nmDataAnalyzeManager::removeOutlineData()
{
if(m_outlineData) {
delete m_outlineData; // 删除边界数据对象
m_outlineData = nullptr;
notifyGeometryListChanged();
return true; // 移除成功,返回 true
}
return false; // 未找到或移除失败,返回 false
}
nmDataOutline nmDataAnalyzeManager::getOutlineDataCopy() const
{
if(m_outlineData) {
return *m_outlineData; // 调用拷贝构造函数
}
nmDataOutline obj;
return obj; // 返回默认构造对象
}
void nmDataAnalyzeManager::updateOutlineData(const nmDataOutline & newData)
{
if(m_outlineData) {
*m_outlineData = newData; // 调用赋值运算符
}
}
nmDataRegion *nmDataAnalyzeManager::createRegion()
{
nmDataRegion* regionData = new nmDataRegion;
m_vRegionData.append(regionData);
return regionData;
}
QVector<nmDataRegion*> nmDataAnalyzeManager::getRegionDataList() const
{
return m_vRegionData;
}
bool nmDataAnalyzeManager::removeRegionData(nmDataRegion * pData)
{
if(pData) {
// 遍历 m_vRegionData 数组,找到并移除对应的对象
for(int i = 0; i < m_vRegionData.size(); ++i) {
if(m_vRegionData[i] == pData) {
delete m_vRegionData[i]; // 释放内存
m_vRegionData.remove(i);
notifyGeometryListChanged();
return true; // 移除成功,返回 true
}
}
}
return false; // 未找到或移除失败,返回 false
}
QVector<nmDataRegion> nmDataAnalyzeManager::getRegionDataListCopy() const
{
QVector<nmDataRegion> result;
result.reserve(m_vRegionData.size());
foreach(const nmDataRegion* region, m_vRegionData) {
if(region) {
result.append(*region); // 调用拷贝构造函数
}
}
return result;
}
void nmDataAnalyzeManager::updateRegionData(const QVector<nmDataRegion>& newData)
{
// 确保数量一致
if(newData.size() != m_vRegionData.size()) {
return;
}
for(int i = 0; i < newData.size(); ++i) {
if(m_vRegionData[i]) {
// 只更新内容,不改变指针
*m_vRegionData[i] = newData[i]; // 调用赋值运算符
m_vRegionData[i]->notifyDataChanged();
}
}
// 批量赋值不触发属性信号,完成后统一刷新参数面板。
if (m_pAttrRegistry) {
m_pAttrRegistry->refreshAll();
}
}
nmDataFracture *nmDataAnalyzeManager::createFracture()
{
nmDataFracture* fractureData = new nmDataFracture;
m_vFractureData.append(fractureData);
return fractureData;
}
QVector<nmDataFracture*> nmDataAnalyzeManager::getFractureDataList() const
{
return m_vFractureData;
}
QVector<nmDataFracture*> nmDataAnalyzeManager::getDFNFractureDataList() const
{
QVector<nmDataFracture*> vecDFNs;
// 遍历 m_vFractureData 数组,找到并移除对应的对象
for(int i = 0; i < m_vFractureData.size(); ++i) {
if(m_vFractureData[i]->getFractureType().getValue() == tr("DFN")) {
vecDFNs.append(m_vFractureData[i]);
}
}
return vecDFNs;
}
bool nmDataAnalyzeManager::removeFractureData(nmDataFracture * pData)
{
if(pData) {
// 遍历 m_vFractureData 数组,找到并移除对应的对象
for(int i = 0; i < m_vFractureData.size(); ++i) {
if(m_vFractureData[i] == pData) {
const bool bNotifyGeometryList =
m_vFractureData[i]->getFractureType().getValue().toString() != "DFN";
delete m_vFractureData[i]; // 释放内存
m_vFractureData.remove(i);
if (bNotifyGeometryList) {
notifyGeometryListChanged();
} else {
notifyDataChanged();
}
return true; // 移除成功,返回 true
}
}
}
return false; // 未找到或移除失败,返回 false
}
QVector<nmDataFracture> nmDataAnalyzeManager::getFractureDataListCopy() const
{
QVector<nmDataFracture> result;
result.reserve(m_vFractureData.size());
foreach(const nmDataFracture* fracture, m_vFractureData) {
if(fracture) {
result.append(*fracture); // 调用拷贝构造函数
}
}
return result;
}
void nmDataAnalyzeManager::updateFractureData(const QVector<nmDataFracture>& newData)
{
// 确保数量一致
if(newData.size() != m_vFractureData.size()) {
return;
}
for(int i = 0; i < newData.size(); ++i) {
if(m_vFractureData[i]) {
// 只更新内容,不改变指针
*m_vFractureData[i] = newData[i]; // 调用赋值运算符
m_vFractureData[i]->notifyDataChanged();
}
}
// 批量赋值不触发属性信号,完成后统一刷新参数面板。
if (m_pAttrRegistry) {
m_pAttrRegistry->refreshAll();
}
}
nmDataFault *nmDataAnalyzeManager::createFault()
{
nmDataFault* faultData = new nmDataFault;
m_vFaultData.append(faultData);
return faultData;
}
QVector<nmDataFault*> nmDataAnalyzeManager::getFaultDataList() const
{
return m_vFaultData;
}
bool nmDataAnalyzeManager::removeFaultData(nmDataFault * pData)
{
if(pData) {
// 遍历 m_vFaultData 数组,找到并移除对应的对象
for(int i = 0; i < m_vFaultData.size(); ++i) {
if(m_vFaultData[i] == pData) {
delete m_vFaultData[i]; // 释放内存
m_vFaultData.remove(i);
notifyGeometryListChanged();
return true; // 移除成功,返回 true
}
}
}
return false; // 未找到或移除失败,返回 false
}
QVector<nmDataFault> nmDataAnalyzeManager::getFaultDataListCopy() const
{
QVector<nmDataFault> result;
result.reserve(m_vFaultData.size());
foreach(const nmDataFault* fault, m_vFaultData) {
if(fault) {
result.append(*fault); // 调用拷贝构造函数
}
}
return result;
}
void nmDataAnalyzeManager::updateFaultData(const QVector<nmDataFault>& newData)
{
// 确保数量一致
if(newData.size() != m_vFaultData.size()) {
return;
}
for(int i = 0; i < newData.size(); ++i) {
if(m_vFaultData[i]) {
// 只更新内容,不改变指针
*m_vFaultData[i] = newData[i]; // 调用赋值运算符
m_vFaultData[i]->notifyDataChanged();
}
}
// 批量赋值不触发属性信号,完成后统一刷新参数面板。
if (m_pAttrRegistry) {
m_pAttrRegistry->refreshAll();
}
}
nmGuiPlot* nmDataAnalyzeManager::getPlot() const
{
return m_pNmGuiPlot;
}
void nmDataAnalyzeManager::setPlot(nmGuiPlot * plot)
{
m_pNmGuiPlot = plot;
}
void nmDataAnalyzeManager::updateWellPlotByDataManager()
{
nmDataPlotContextProvider* pPlotContextProvider = nmDataPlotContext::provider();
if (m_pNmGuiPlot != nullptr && pPlotContextProvider != nullptr)
{
pPlotContextProvider->updateWellPlots(m_pNmGuiPlot, this);
}
}
QVector<QPointF> nmDataAnalyzeManager::getValueAllConnectablePoints()
{
QVector<QPointF> vecAllPoints;
// 遍历所有断层数据
foreach(nmDataFault* fault, m_vFaultData) {
if(fault) {
QVector<QPointF> faultPoints = fault->getFaultPoints();
foreach(const QPointF& point, faultPoints) {
vecAllPoints.append(point);
}
}
}
// 遍历所有裂缝数据
foreach(nmDataFracture* fracture, m_vFractureData) {
if(fracture) {
QVector<QPointF> fracturePoints = fracture->getFracturePoints();
foreach(const QPointF& point, fracturePoints) {
vecAllPoints.append(point);
}
}
}
// 遍历所有复合区数据
foreach(nmDataRegion* region, m_vRegionData) {
if(region) {
QVector<QPointF> regionPoints = region->getVecPts();
foreach(const QPointF& point, regionPoints) {
vecAllPoints.append(point);
}
}
}
// 处理边界数据
if(m_outlineData) {
QVector<QPointF> outlinePoints = m_outlineData->getOutlinePoints();
// 圆形边界,特殊处理
if(m_outlineData->getOutlineType() == NM_Round_Outline_Type) {
outlinePoints.remove(0); // 圆心
outlinePoints.remove(0); // 半径
}
foreach(const QPointF& point, outlinePoints) {
vecAllPoints.append(point);
}
}
// 垂直裂缝井的裂缝两个端点
// 遍历所有井数据
foreach(nmDataWellBase* well, m_vWellData) {
if(well == nullptr) {
continue;
}
if(well->getWellType() == NM_WELL_MODEL::Vertical_Fractured_Well) {
nmDataVerticalFracturedWell* pVFwell = dynamic_cast<nmDataVerticalFracturedWell*>(well);
if(pVFwell == nullptr) {
continue;
}
QVector<QPointF> fracsPoints = pVFwell->getFracs();
foreach(const QPointF& point, fracsPoints) {
vecAllPoints.append(point);
}
}
}
return vecAllPoints;
}
QVector<QPointF> nmDataAnalyzeManager::getPosAllConnectablePoints()
{
// 将绘图坐标系转为qt坐标系
QVector<QPointF> vecValues = this->getValueAllConnectablePoints();
QVector<QPointF> vecPos;
Q_ASSERT(m_pNmGuiPlot != nullptr);
nmDataPlotContextProvider* pPlotContextProvider = nmDataPlotContext::provider();
if(m_pNmGuiPlot == nullptr || pPlotContextProvider == nullptr) {
return vecPos;
}
pPlotContextProvider->getPosForValue(m_pNmGuiPlot, vecValues, vecPos);
// TODO:没有m_pNmGuiPlot的情况下怎么办也就是加载的时候
return vecPos;
}
QVector<QPointF> nmDataAnalyzeManager::removePosByObject(QObject * obj)
{
// 获取所有可连接的点(屏幕坐标)
QVector<QPointF> vecAllPoints = this->getPosAllConnectablePoints();
// 创建一个临时容器用于存储要移除的点
QVector<QPointF> pointsToRemove;
// 检查传入的对象类型并收集相关点
if(nmDataFault * fault = dynamic_cast<nmDataFault * >(obj)) {
QVector<QPointF> faultPoints = fault->getFaultPoints();
foreach(const QPointF& point, faultPoints) {
pointsToRemove.append(point);
}
} else if(nmDataFracture * fracture = dynamic_cast<nmDataFracture * >(obj)) {
QVector<QPointF> fracturePoints = fracture->getFracturePoints();
foreach(const QPointF& point, fracturePoints) {
pointsToRemove.append(point);
}
} else if(nmDataRegion * region = dynamic_cast<nmDataRegion * >(obj)) {
QVector<QPointF> regionPoints = region->getVecPts();
foreach(const QPointF& point, regionPoints) {
pointsToRemove.append(point);
}
} else if(nmDataVerticalFracturedWell * well = dynamic_cast<nmDataVerticalFracturedWell * >(obj)) {
QVector<QPointF> fracsPoints = well->getFracs();
foreach(const QPointF& point, fracsPoints) {
pointsToRemove.append(point);
}
} else if(m_outlineData == obj) {
QVector<QPointF> outlinePoints = m_outlineData->getOutlinePoints();
if(m_outlineData->getOutlineType() == NM_Round_Outline_Type) {
outlinePoints.remove(0); // 圆心
outlinePoints.remove(0); // 半径
}
foreach(const QPointF& point, outlinePoints) {
pointsToRemove.append(point);
}
}
// change to Pos
QVector<QPointF> vecPosRemove;
nmDataPlotContextProvider* pPlotContextProvider = nmDataPlotContext::provider();
if(m_pNmGuiPlot && pPlotContextProvider) {
pPlotContextProvider->getPosForValue(m_pNmGuiPlot, pointsToRemove, vecPosRemove);
}
// 移除与特定对象相关的点
QVector<QPointF>::iterator itRemove;
for(itRemove = vecPosRemove.begin(); itRemove != vecPosRemove.end(); ++itRemove) {
QPointF pointToRemove = *itRemove;
for(int i = 0; i < vecAllPoints.size(); ++i) {
if(vecAllPoints[i] == pointToRemove) {
vecAllPoints.remove(i);
break; // 只移除第一个匹配的点
}
}
}
return vecAllPoints;
}
nmDataMeasuringScale *nmDataAnalyzeManager::getMeasuringScaleData()
{
if(m_pMeasuringScaleData == nullptr) {
m_pMeasuringScaleData = new nmDataMeasuringScale;
}
return m_pMeasuringScaleData;
}
void nmDataAnalyzeManager::setMeasuringScaleData(nmDataMeasuringScale * pMeasuringScaleData)
{
if(m_pMeasuringScaleData != nullptr) {
delete m_pMeasuringScaleData;
m_pMeasuringScaleData = nullptr;
}
m_pMeasuringScaleData = pMeasuringScaleData;
}
nmDataMeasure *nmDataAnalyzeManager::getMeasureData()
{
if(m_pMeasureData == nullptr) {
m_pMeasureData = new nmDataMeasure;
}
return m_pMeasureData;
}
void nmDataAnalyzeManager::setMeasureData(nmDataMeasure * pMeasureData)
{
if(m_pMeasureData != nullptr) {
delete m_pMeasureData;
m_pMeasureData = nullptr;
}
m_pMeasureData = pMeasureData;
}
bool nmDataAnalyzeManager::removeMeasureData()
{
if(m_pMeasureData) {
delete m_pMeasureData;
m_pMeasureData = nullptr;
return true; // 移除成功,返回 true
}
return false; // 未找到或移除失败,返回 false
}
NM_Grid_Type nmDataAnalyzeManager::getGridType()
{
return m_eGridType;
}
void nmDataAnalyzeManager::setGridType(NM_Grid_Type newGridType)
{
if(m_eGridType == newGridType) {
return;
}
m_eGridType = newGridType;
m_oNumericalAnalysisCase.invalidateGrid();
}
NM_SOLVER_MODEL_TYPE nmDataAnalyzeManager::getSolverModelType() const
{
return m_eSolverModelType;
}
void nmDataAnalyzeManager::setSolverModelType(NM_SOLVER_MODEL_TYPE newSolverModelType)
{
if(m_eSolverModelType == newSolverModelType) {
return;
}
m_eSolverModelType = newSolverModelType;
m_oNumericalAnalysisCase.invalidateResults();
}
int nmDataAnalyzeManager::getPebiSolverType() const
{
return m_nPebiSolverType;
}
void nmDataAnalyzeManager::setPebiSolverType(int nSolverType)
{
if((nSolverType == PebiSolverCpuAccelerated ||
nSolverType == PebiSolverOriginal) &&
m_nPebiSolverType != nSolverType) {
m_nPebiSolverType = nSolverType;
m_oNumericalAnalysisCase.invalidateResults();
}
}
int nmDataAnalyzeManager::getPebiOmpThreads() const
{
return m_nPebiOmpThreads;
}
void nmDataAnalyzeManager::setPebiOmpThreads(int nOmpThreads)
{
if(nOmpThreads == 1 || nOmpThreads == 2 || nOmpThreads == 4
|| nOmpThreads == 8 || nOmpThreads == 16) {
if(m_nPebiOmpThreads != nOmpThreads) {
m_nPebiOmpThreads = nOmpThreads;
m_oNumericalAnalysisCase.invalidateResults();
}
}
}
int nmDataAnalyzeManager::getPebiIluReuseSteps() const
{
return m_nPebiIluReuseSteps;
}
void nmDataAnalyzeManager::setPebiIluReuseSteps(int nIluReuseSteps)
{
if(nIluReuseSteps >= 1 && nIluReuseSteps <= 100 &&
m_nPebiIluReuseSteps != nIluReuseSteps) {
m_nPebiIluReuseSteps = nIluReuseSteps;
m_oNumericalAnalysisCase.invalidateResults();
}
}
// 获取Pebi网格求解数据接口
// 获取压力历史数据
QVector<QVector<double>> nmDataAnalyzeManager::getPebiSolverHistoryDataByName(const QString & wellName)
{
QVector<QVector<double>> vvecHisotryData;
QVector<double> vX;
QVector<double> vY;
// 验证井名有效性
if(wellName.isEmpty()) {
return vvecHisotryData;
}
// 从结果文件中读取数据
QString sHistoryDataFilePath = ZxBaseUtil::getCurWellDirOf("Nm/Solver") + "output/Pebi/" + wellName + "/Pressure.txt";
if(!QFile::exists(sHistoryDataFilePath)) {
return vvecHisotryData;
}
QStringList sContent = nmDataUtils::readNmDataFile(sHistoryDataFilePath);
QRegExp regex("\\s+");
for(int i = 0; i < sContent.size(); i++) {
QString sData = sContent[i].trimmed();
if(sData.isEmpty()) continue;
QStringList sXY = sData.split(regex, QString::SkipEmptyParts);
if(sXY.size() == 2) {
bool okX, okY;
double x = sXY[0].toDouble(&okX);
double y = sXY[1].toDouble(&okY);
if(okX && okY) {
vX.append(x);
vY.append(y);
}
}
}
vvecHisotryData.append(vX);
vvecHisotryData.append(vY);
return vvecHisotryData;
}
QVector<QVector<double>> nmDataAnalyzeManager::getPebiSolverLogPreDataByName(const QString & wellName)
{
QVector<QVector<double>> vvecLogPreData;
QVector<double> vX;
QVector<double> vY;
QVector<double> vZ;
// 验证井名有效性
if(wellName.isEmpty()) {
return vvecLogPreData;
}
// 从结果文件中读取数据
QString sLogPreDataFilePath = ZxBaseUtil::getCurWellDirOf("Nm/Solver") + "output/Pebi/" + wellName + "/Loglog.txt";
if(!QFile::exists(sLogPreDataFilePath)) {
return vvecLogPreData;
}
QStringList sContent = nmDataUtils::readNmDataFile(sLogPreDataFilePath);
QRegExp regex("\\s+");
for(int i = 0; i < sContent.size() - 1; i++) {
QString sData = sContent[i].trimmed();
if(sData.isEmpty()) continue;
QStringList sXYZ = sData.split(regex, QString::SkipEmptyParts);
if(sXYZ.size() == 3) {
bool okX, okY, okZ;
double x = sXYZ[0].toDouble(&okX);
double y = sXYZ[1].toDouble(&okY);
double z = sXYZ[2].toDouble(&okZ);
if(okX && okY && okZ) {
vX.append(x);
vY.append(y);
vZ.append(z);
}
}
}
vvecLogPreData.append(vX);
vvecLogPreData.append(vY);
vvecLogPreData.append(vZ);
return vvecLogPreData;
}
QVector<QVector<double>> nmDataAnalyzeManager::getPebiSolverSemiLogPreDataByName(const QString & wellName)
{
QVector<QVector<double>> vvecSemiLogPreData;
QVector<double> vX;
QVector<double> vY;
// 验证井名有效性
if(wellName.isEmpty()) {
return vvecSemiLogPreData;
}
// 从结果文件中读取数据
QString sSemiLogPreDataFilePath = ZxBaseUtil::getCurWellDirOf("Nm/Solver") + "output/Pebi/" + wellName + "/SemiLog.txt";
if(!QFile::exists(sSemiLogPreDataFilePath)) {
return vvecSemiLogPreData;
}
QStringList sContent = nmDataUtils::readNmDataFile(sSemiLogPreDataFilePath);
QRegExp regex("\\s+");
for(int i = 0; i < sContent.size(); i++) {
QString sData = sContent[i].trimmed();
if(sData.isEmpty()) continue;
QStringList sXY = sData.split(regex, QString::SkipEmptyParts);
if(sXY.size() == 2) {
bool okX, okY;
double x = sXY[0].toDouble(&okX);
double y = sXY[1].toDouble(&okY);
if(okX && okY) {
vX.append(x);
vY.append(y);
}
}
}
vvecSemiLogPreData.append(vX);
vvecSemiLogPreData.append(vY);
return vvecSemiLogPreData;
}
nmDataPvtParaForPebi* nmDataAnalyzeManager::getPebiPvtPara()
{
return m_pebiPvtPara;
}
bool nmDataAnalyzeManager::getPebiPseudoPressureTable(std::vector<double>& pressure,
std::vector<double>& pseudoPressure)
{
pressure.clear();
pseudoPressure.clear();
nmDataAnalyzeContextProvider* context = nmDataAnalyzeContext::provider();
// 使用本 DataManager 创建时绑定的成果窗口;切换当前页签不能改变数据来源。
iSubWndFitting* fitting = m_pOwnerFitting;
VVecDouble pseudoResult;
if(context == nullptr
|| fitting == nullptr
|| !context->getPseuRstOf(fitting, pseudoResult)
|| pseudoResult.size() < 2
|| pseudoResult[0].size() < 2
|| pseudoResult[0].size() != pseudoResult[1].size()) {
return false;
}
pressure = pseudoResult[0].toStdVector();
pseudoPressure = pseudoResult[1].toStdVector();
return true;
}
nmDataMixedResults* nmDataAnalyzeManager::getMixedResults()
{
return m_pMixedResults;
}
nmDataMixedResults* nmDataAnalyzeManager::createMixedResult()
{
if(m_pMixedResults != nullptr) {
delete m_pMixedResults;
m_pMixedResults = nullptr;
}
m_pMixedResults = new nmDataMixedResults;
return m_pMixedResults;
}
nmDataLayer* nmDataAnalyzeManager::getLayerData()
{
// 1. 删除 m_vecLayers 中当前存储的所有 nmDataLayer 对象,释放内存
qDeleteAll(m_vecLayers);
// 2. 清空 m_vecLayers 自身,移除所有指针
m_vecLayers.clear();
return m_pLayerData;
}
void nmDataAnalyzeManager::setLayers(QVector<nmDataLayer*> vecLayers)
{
m_vecLayers = vecLayers;
// 几何分层会改变储层离散输入,旧网格和旧结果不再有效。
m_oNumericalAnalysisCase.invalidateGrid();
emit dataChanged();
}
QVector<nmDataLayer*> nmDataAnalyzeManager::getLayers()
{
return m_vecLayers;
}
double nmDataAnalyzeManager::getMinLayerTop()
{
// 在计算前初始化边界值
double m_dMinLayerTop = DBL_MAX;
// 复制新数据并计算边界
foreach(const nmDataLayer* layer , m_vecLayers) {
if(layer) {
// 更新缓存的最小顶深度
m_dMinLayerTop = qMin(m_dMinLayerTop, layer->getTop());
}
}
// 如果 m_vecLayers 为空,可以设置默认值
if(m_vecLayers.isEmpty()) {
m_dMinLayerTop = 0.0;
}
return m_dMinLayerTop;
}
// 新增的实现:返回存储的最大底深度
double nmDataAnalyzeManager::getMaxLayerBottom()
{
// 在计算前初始化边界值
double m_dMaxLayerBottom = DBL_MIN;
// 复制新数据并计算边界
foreach(const nmDataLayer* layer , m_vecLayers) {
if(layer) {
// 更新缓存的最大顶深度
m_dMaxLayerBottom = qMax(m_dMaxLayerBottom, layer->getBottom());
}
}
// 如果 m_vecLayers 为空,可以设置默认值
if(m_vecLayers.isEmpty()) {
m_dMaxLayerBottom = 0.0;
}
return m_dMaxLayerBottom;
}
void nmDataAnalyzeManager::appendCalculationWell(const QPair<NM_WELL_MODEL, QString>& well)
{
// 旧接口只作为过渡适配层:输入的显示井名在这里立即转换成 WellCode。
if(well.first == NM_WELL_MODEL::Unknow_Well) {
appendSolverWell(nmSolverWellRef(-1,
well.first,
QString(),
NM_SolverEntry_ManualFracture));
return;
}
nmDataWellBase* pWellData = findWellByName(well.second);
if(pWellData == nullptr || pWellData->getWellCode().isEmpty()) {
return;
}
appendSolverWell(nmSolverWellRef(-1,
well.first,
pWellData->getWellCode(),
NM_SolverEntry_Well));
}
void nmDataAnalyzeManager::insertCalculationWell(int index, const QPair<NM_WELL_MODEL, QString>& well)
{
if(well.first == NM_WELL_MODEL::Unknow_Well) {
insertSolverWell(index,
nmSolverWellRef(index,
well.first,
QString(),
NM_SolverEntry_ManualFracture));
return;
}
nmDataWellBase* pWellData = findWellByName(well.second);
if(pWellData == nullptr || pWellData->getWellCode().isEmpty()) {
return;
}
insertSolverWell(index,
nmSolverWellRef(index,
well.first,
pWellData->getWellCode(),
NM_SolverEntry_Well));
}
bool nmDataAnalyzeManager::removeCalculationWell(int index)
{
return m_oNumericalAnalysisCase.removeSolverWell(index);
}
void nmDataAnalyzeManager::clearCalculationWells()
{
clearSolverWellOrder();
}
QVector<QPair<NM_WELL_MODEL, QString>> nmDataAnalyzeManager::getCalculationWells() const
{
QVector<QPair<NM_WELL_MODEL, QString> > vecLegacyOrder;
QVector<nmSolverWellRef> vecSolverOrder = getSolverWellOrder();
for(int nIndex = 0; nIndex < vecSolverOrder.size(); ++nIndex) {
const nmSolverWellRef& oWellRef = vecSolverOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_ManualFracture) {
vecLegacyOrder.append(qMakePair(NM_WELL_MODEL::Unknow_Well,
QString()));
continue;
}
nmDataWellBase* pWellData = findWellByCode(oWellRef.m_sWellCode);
if(pWellData != nullptr) {
vecLegacyOrder.append(qMakePair(oWellRef.m_eWellType,
pWellData->getWellName()));
}
}
return vecLegacyOrder;
}
bool nmDataAnalyzeManager::isContainsWellName(const QString & wellName) const
{
nmDataWellBase* pWellData = findWellByName(wellName);
return pWellData != nullptr &&
isWellSelectedForCalculation(pWellData->getWellCode());
}
nmDataNumericalAnalysisCase* nmDataAnalyzeManager::getNumericalAnalysisCase()
{
return &m_oNumericalAnalysisCase;
}
const nmDataNumericalAnalysisCase*
nmDataAnalyzeManager::getNumericalAnalysisCase() const
{
return &m_oNumericalAnalysisCase;
}
void nmDataAnalyzeManager::setPrimaryWellCode(const QString& sWellCode)
{
if(!sWellCode.isEmpty() && findWellByCode(sWellCode) == nullptr) {
return;
}
const quint64 nOldRevision =
m_oNumericalAnalysisCase.getGridInputRevision();
m_oNumericalAnalysisCase.setPrimaryWellCode(sWellCode);
if(m_oNumericalAnalysisCase.getCurrentResultWellCode().isEmpty()) {
m_oNumericalAnalysisCase.setCurrentResultWellCode(sWellCode);
}
if(m_oNumericalAnalysisCase.getGridInputRevision() != nOldRevision) {
emit dataChanged();
}
}
QString nmDataAnalyzeManager::getPrimaryWellCode() const
{
return m_oNumericalAnalysisCase.getPrimaryWellCode();
}
void nmDataAnalyzeManager::setIncludeOtherWells(bool bInclude)
{
const bool bOldInclude = m_oNumericalAnalysisCase.getIncludeOtherWells();
m_oNumericalAnalysisCase.setIncludeOtherWells(bInclude);
if(bOldInclude != m_oNumericalAnalysisCase.getIncludeOtherWells()) {
// 开关只影响手工选择的有产量干扰井。无产量观察井始终有效,
// 因此当前结果井只有在新有效集合中确实不存在时才回退到主井。
const QString sCurrentResultWellCode = getCurrentResultWellCode();
if(!sCurrentResultWellCode.isEmpty() &&
!isWellAvailableForResult(sCurrentResultWellCode)) {
m_oNumericalAnalysisCase.setCurrentResultWellCode(
getPrimaryWellCode());
}
emit dataChanged();
}
}
bool nmDataAnalyzeManager::getIncludeOtherWells() const
{
return m_oNumericalAnalysisCase.getIncludeOtherWells();
}
void nmDataAnalyzeManager::setPebiGridControl(double dGridControl)
{
m_oNumericalAnalysisCase.setPebiGridControl(dGridControl);
}
double nmDataAnalyzeManager::getPebiGridControl() const
{
return m_oNumericalAnalysisCase.getPebiGridControl();
}
void nmDataAnalyzeManager::setIncludedCalculationWells(
const QVector<nmCalculationWellRef>& vecWells)
{
const QVector<nmCalculationWellRef> vecOldWells =
m_oNumericalAnalysisCase.getIncludedWells();
QVector<nmCalculationWellRef> vecValidWells;
// 第一步Include Other Wells 只保存其他有产量的生产/注入井。
// 无产量井由 Map 自动作为观察井加入,不能混入手工包含列表。
for(int nIndex = 0; nIndex < vecWells.size(); ++nIndex) {
const nmCalculationWellRef& oWellRef = vecWells[nIndex];
nmDataWellBase* pWellData = findWellByCode(oWellRef.m_sWellCode);
if(isSupportedNumericalWell(pWellData) &&
oWellRef.m_sWellCode != getPrimaryWellCode() &&
pWellData->getFlowPoints().size() >= 2) {
vecValidWells.append(nmCalculationWellRef(
oWellRef.m_sWellCode,
NM_CaseWell_RateControlled));
}
}
// 第二步:分析方案内部负责按 WellCode 去重,并根据井集合变化使网格失效。
m_oNumericalAnalysisCase.setIncludedWells(vecValidWells);
const QVector<nmCalculationWellRef> vecNewWells =
m_oNumericalAnalysisCase.getIncludedWells();
bool bChanged = vecOldWells.size() != vecNewWells.size();
for(int nIndex = 0;
!bChanged && nIndex < vecNewWells.size();
++nIndex) {
bChanged = vecOldWells[nIndex].m_sWellCode !=
vecNewWells[nIndex].m_sWellCode ||
vecOldWells[nIndex].m_eMode !=
vecNewWells[nIndex].m_eMode;
}
if(bChanged) {
// 第三步:取消当前正在查看的主动井后,结果井回退到主井;自动观察井
// 不在 IncludedWells 中,但仍属于有效计算集合,不能被错误回退。
const QString sCurrentResultWellCode = getCurrentResultWellCode();
if(!sCurrentResultWellCode.isEmpty() &&
!isWellAvailableForResult(sCurrentResultWellCode)) {
m_oNumericalAnalysisCase.setCurrentResultWellCode(
getPrimaryWellCode());
}
emit dataChanged();
}
}
QVector<nmCalculationWellRef>
nmDataAnalyzeManager::getIncludedCalculationWells() const
{
return m_oNumericalAnalysisCase.getIncludedWells();
}
QVector<nmCalculationWellRef>
nmDataAnalyzeManager::getEffectiveCalculationWells() const
{
QVector<nmCalculationWellRef> vecEffectiveWells;
QSet<QString> setEffectiveWellCodes;
// 第一步:主分析井始终排在第一位。它的主动/观察角色由当前方案保存,
// 不受 Include Other Wells 开关控制。
const QString sPrimaryWellCode = getPrimaryWellCode();
nmDataWellBase* pPrimaryWell = findWellByCode(sPrimaryWellCode);
if(isSupportedNumericalWell(pPrimaryWell)) {
vecEffectiveWells.append(nmCalculationWellRef(
sPrimaryWellCode,
m_oNumericalAnalysisCase.getPrimaryWellMode()));
setEffectiveWellCodes.insert(sPrimaryWellCode);
}
// 第二步:手工包含列表只提供“哪些有产量井被选中”。使用集合查询,
// 最终井顺序仍跟随 Map保证网格、求解器和结果下拉框顺序稳定。
QSet<QString> setIncludedRateWellCodes;
if(getIncludeOtherWells()) {
const QVector<nmCalculationWellRef> vecIncludedWells =
m_oNumericalAnalysisCase.getIncludedWells();
for(int nIndex = 0; nIndex < vecIncludedWells.size(); ++nIndex) {
setIncludedRateWellCodes.insert(
vecIncludedWells[nIndex].m_sWellCode);
}
}
// 第三步:遍历 Map 中的其他真实井。无产量井无条件作为观察井加入;
// 有产量井只有打开 Include Other Wells 且被勾选后才作为主动井加入。
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWellData = m_vWellData[nIndex];
if(!isSupportedNumericalWell(pWellData) ||
setEffectiveWellCodes.contains(pWellData->getWellCode())) {
continue;
}
const bool bHasValidRate = pWellData->getFlowPoints().size() >= 2;
if(!bHasValidRate) {
vecEffectiveWells.append(nmCalculationWellRef(
pWellData->getWellCode(),
NM_CaseWell_Observation));
setEffectiveWellCodes.insert(pWellData->getWellCode());
} else if(setIncludedRateWellCodes.contains(
pWellData->getWellCode())) {
vecEffectiveWells.append(nmCalculationWellRef(
pWellData->getWellCode(),
NM_CaseWell_RateControlled));
setEffectiveWellCodes.insert(pWellData->getWellCode());
}
}
return vecEffectiveWells;
}
NM_CASE_WELL_MODE nmDataAnalyzeManager::getCalculationWellMode(
const QString& sWellCode) const
{
// 求解角色必须从当前有效井集合读取,不能只查看历史 IncludedWells。
// 这样原有主动井清空产量后会立即转成自动观察井,不会继续索要产量制度。
const QVector<nmCalculationWellRef> vecEffectiveWells =
getEffectiveCalculationWells();
for(int nIndex = 0; nIndex < vecEffectiveWells.size(); ++nIndex) {
if(vecEffectiveWells[nIndex].m_sWellCode == sWellCode) {
return vecEffectiveWells[nIndex].m_eMode;
}
}
return NM_CaseWell_Observation;
}
bool nmDataAnalyzeManager::isWellSelectedForCalculation(
const QString& sWellCode) const
{
QVector<nmCalculationWellRef> vecEffectiveWells =
getEffectiveCalculationWells();
for(int nIndex = 0; nIndex < vecEffectiveWells.size(); ++nIndex) {
if(vecEffectiveWells[nIndex].m_sWellCode == sWellCode) {
return true;
}
}
return false;
}
bool nmDataAnalyzeManager::isWellAvailableForResult(
const QString& sWellCode) const
{
nmDataWellBase* pWellData = findWellByCode(sWellCode);
if(pWellData == nullptr) {
return false;
}
// 第一步:主分析井是结果界面的固定入口,即使当前处于关井段也必须保留。
if(sWellCode == getPrimaryWellCode()) {
return true;
}
// 第二步:其他井只有同时具备有效产量并以主动井角色参与当前求解时,
// 才能出现在“结果井”列表。所有无流量观察井一律只在内部保存计算结果。
if(pWellData->getFlowPoints().size() < 2) {
return false;
}
const QVector<nmCalculationWellRef> vecEffectiveWells =
getEffectiveCalculationWells();
for(int nIndex = 0; nIndex < vecEffectiveWells.size(); ++nIndex) {
if(vecEffectiveWells[nIndex].m_sWellCode == sWellCode &&
vecEffectiveWells[nIndex].m_eMode == NM_CaseWell_RateControlled) {
return true;
}
}
return false;
}
void nmDataAnalyzeManager::clearSolverWellOrder()
{
m_oNumericalAnalysisCase.clearSolverWellOrder();
}
void nmDataAnalyzeManager::appendSolverWell(const nmSolverWellRef& oWellRef)
{
m_oNumericalAnalysisCase.appendSolverWell(oWellRef);
}
void nmDataAnalyzeManager::insertSolverWell(
int nIndex,
const nmSolverWellRef& oWellRef)
{
m_oNumericalAnalysisCase.insertSolverWell(nIndex, oWellRef);
}
QVector<nmSolverWellRef> nmDataAnalyzeManager::getSolverWellOrder() const
{
return m_oNumericalAnalysisCase.getSolverWellOrder();
}
void nmDataAnalyzeManager::setSolverWellOrder(
const QVector<nmSolverWellRef>& vecSolverWellOrder)
{
m_oNumericalAnalysisCase.setSolverWellOrder(vecSolverWellOrder);
}
bool nmDataAnalyzeManager::commitPebiGridResult(
quint64 nGridInputRevision,
const QVector<nmSolverWellRef>& vecSolverWellOrder,
vtkSmartPointer<vtkUnstructuredGrid> pGrid)
{
// 第一步:空井顺序或空网格都不是可求解成果,不能破坏当前有效网格。
if(vecSolverWellOrder.isEmpty() || pGrid == nullptr ||
pGrid->GetNumberOfCells() <= 0) {
return false;
}
// 第二步:先检查后台任务捕获的输入版本。该函数只在主线程调用,检查和
// 后续写入之间不会处理界面事件,因此可以作为一次不可分割的成果提交。
if(m_oNumericalAnalysisCase.getGridInputRevision() !=
nGridInputRevision) {
return false;
}
// 第三步:整体替换 DLL 槽位顺序,再登记同一输入版本并替换 VTK 网格。
// setSolverWellOrder() 会统一重编号,避免逐井追加期间暴露半成品顺序。
m_oNumericalAnalysisCase.setSolverWellOrder(vecSolverWellOrder);
if(!m_oNumericalAnalysisCase.markGridBuiltIfCurrent(
nGridInputRevision)) {
return false;
}
m_pVtkUnstructuredGrid = pGrid;
return true;
}
void nmDataAnalyzeManager::markPebiGridBuilt()
{
m_oNumericalAnalysisCase.markGridBuilt();
}
bool nmDataAnalyzeManager::markPebiGridBuiltIfCurrent(
quint64 nGridInputRevision)
{
return m_oNumericalAnalysisCase.markGridBuiltIfCurrent(
nGridInputRevision);
}
void nmDataAnalyzeManager::invalidatePebiGrid()
{
m_oNumericalAnalysisCase.invalidateGrid();
}
void nmDataAnalyzeManager::invalidatePebiResults()
{
m_oNumericalAnalysisCase.invalidateResults();
}
void nmDataAnalyzeManager::discardPebiResults()
{
m_oNumericalAnalysisCase.discardResults();
}
bool nmDataAnalyzeManager::isPebiGridValid() const
{
return m_oNumericalAnalysisCase.isGridValid();
}
void nmDataAnalyzeManager::setCurrentResultWellCode(
const QString& sWellCode)
{
const QString sValidatedWellCode = sWellCode.isEmpty()
? getPrimaryWellCode() : sWellCode;
if(!sValidatedWellCode.isEmpty() &&
!isWellAvailableForResult(sValidatedWellCode)) {
return;
}
m_oNumericalAnalysisCase.setCurrentResultWellCode(sValidatedWellCode);
}
QString nmDataAnalyzeManager::getCurrentResultWellCode() const
{
return m_oNumericalAnalysisCase.getCurrentResultWellCode();
}
nmDataWellBase* nmDataAnalyzeManager::getCurrentResultWellData() const
{
return findWellByCode(getCurrentResultWellCode());
}
// 设置当前查看井
void nmDataAnalyzeManager::setCurWellData(nmDataWellBase * wellData)
{
m_pCurDataWell = wellData;
}
// 获取当前查看井
nmDataWellBase* nmDataAnalyzeManager::getCurWellData()
{
return m_pCurDataWell;
}
void nmDataAnalyzeManager::notifyDataChanged()
{
// 现有属性编辑链路尚未细分“几何变化”和“仅求解输入变化”,
// 为保证正确性先采用保守策略:任一计算数据变化都使网格失效。
m_oNumericalAnalysisCase.invalidateGrid();
// 当前结果井清空产量后会变成观察井,应立即退出结果井列表并回到主井。
const QString sCurrentResultWellCode = getCurrentResultWellCode();
if(!sCurrentResultWellCode.isEmpty() &&
!isWellAvailableForResult(sCurrentResultWellCode)) {
m_oNumericalAnalysisCase.setCurrentResultWellCode(
getPrimaryWellCode());
}
emit dataChanged();
}
void nmDataAnalyzeManager::notifyGeometryListChanged()
{
m_oNumericalAnalysisCase.invalidateGrid();
emit sigGeometryListChanged();
emit dataChanged();
}
void nmDataAnalyzeManager::notifyParameterObjectNameChanged()
{
emit sigGeometryListChanged();
}
void nmDataAnalyzeManager::setDisplaySettings(const QVector<DisplaySetting>& displaySettings)
{
m_vecDisplaySettings = displaySettings;
}
QVector<DisplaySetting> nmDataAnalyzeManager::getDisplaySettings() const
{
return m_vecDisplaySettings;
}
bool nmDataAnalyzeManager::updateCategoryDisplaySetting(const QString & categoryName, const CategoryDisplayInfo & info)
{
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == categoryName) {
m_vecDisplaySettings[i].value = info;
return true;
}
}
return false;
}
void nmDataAnalyzeManager::initDefaultDisplaySettings()
{
m_vecDisplaySettings.clear();
// Contour
DisplaySetting contourSetting;
contourSetting.key = tr("Contour");
contourSetting.value.isRootChecked = true; // 默认选中
m_vecDisplaySettings.append(contourSetting);
// Faults
DisplaySetting faultsSetting;
faultsSetting.key = tr("Faults");
faultsSetting.value.isRootChecked = true;
m_vecDisplaySettings.append(faultsSetting);
// Images
DisplaySetting imagesSetting;
imagesSetting.key = tr("Images");
imagesSetting.value.isRootChecked = true;
m_vecDisplaySettings.append(imagesSetting);
// Wells
DisplaySetting wellsSetting;
wellsSetting.key = tr("Wells");
wellsSetting.value.isRootChecked = true;
m_vecDisplaySettings.append(wellsSetting);
// Fractures
DisplaySetting fracturesSetting;
fracturesSetting.key = tr("Fractures");
fracturesSetting.value.isRootChecked = true;
m_vecDisplaySettings.append(fracturesSetting);
// Limits
DisplaySetting limitsSetting;
limitsSetting.key = tr("Regions");
limitsSetting.value.isRootChecked = true;
m_vecDisplaySettings.append(limitsSetting);
// RegionMarks
DisplaySetting regionsSetting;
regionsSetting.key = tr("RegionMarks");
regionsSetting.value.isRootChecked = true;
m_vecDisplaySettings.append(regionsSetting);
}
CategoryDisplayInfo* nmDataAnalyzeManager::findCategoryDisplayInfo(const QString & categoryName)
{
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == categoryName) {
return &m_vecDisplaySettings[i].value;
}
}
return nullptr;
}
bool nmDataAnalyzeManager::appendDisplaySetting(const DisplaySetting & displaySetting)
{
// 检查是否已存在相同key的设置
foreach(const auto& setting, m_vecDisplaySettings) {
if(setting.key == displaySetting.key) {
return false; // 已存在相同key添加失败
}
}
// 添加新的显示设置
m_vecDisplaySettings.append(displaySetting);
return true;
}
bool nmDataAnalyzeManager::removeDisplaySetting(const QString & categoryName)
{
// 遍历查找并删除指定key的设置
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == categoryName) {
m_vecDisplaySettings.remove(i);
return true; // 删除成功
}
}
return false; // 未找到指定key的设置删除失败
}
bool nmDataAnalyzeManager::addChildItemsToCategory(const QString & parentCategory,
const QStringList & childNames,
bool defaultChecked)
{
// 查找父类别
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == parentCategory) {
// 为每个子项名称创建ChildCategoryDisplayInfo并添加到父类别
for(int j = 0; j < childNames.size(); ++j) {
ChildCategoryDisplayInfo childInfo;
childInfo.name = childNames.at(j);
childInfo.isChecked = defaultChecked;
childInfo.isEnabled = m_vecDisplaySettings[i].value.isRootChecked; // 子项启用状态取决于父项
m_vecDisplaySettings[i].value.childItems.append(childInfo);
}
return true;
}
}
return false;
}
void nmDataAnalyzeManager::refreshChildItemsDisplay()
{
// 步骤1: 清空所有父节点的子节点
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
m_vecDisplaySettings[i].value.childItems.clear();
}
// 更新边界和位图
CategoryDisplayInfo outlineInfo, imageInfo;
outlineInfo.isRootChecked = m_outlineData->getPlotVisible();
imageInfo.isRootChecked = this->m_backgroundImageInfo.bIsVisible;
this->updateCategoryDisplaySetting(tr("Contour"), outlineInfo);
this->updateCategoryDisplaySetting(tr("Images"), imageInfo);
// 步骤2: 重新添加子节点,根据不同数据容器中的对象名称生成子节点
// 为 "Wells" 分类添加子节点
addWellChildItemsToCategory(tr("Wells"));
// 为 "Faults" 分类添加子节点
addFaultChildItemsToCategory(tr("Faults"));
// 为 "Fractures" 分类添加子节点
addFractureChildItemsToCategory(tr("Fractures"));
// 为 "Regions" 分类添加子节点
addRegionChildItemsToCategory(tr("Regions"));
// 为 "RegionMarks" 分类添加子节点
addRegionMarkChildItemsToCategory(tr("RegionMarks"));
}
// 为Wells分类添加子节点
void nmDataAnalyzeManager::addWellChildItemsToCategory(const QString & parentCategory)
{
// 查找父类别
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == parentCategory) {
// 为每个井对象创建子节点
for(int j = 0; j < m_vWellData.size(); ++j) {
if(m_vWellData[j]) {
ChildCategoryDisplayInfo childInfo;
childInfo.name = m_vWellData[j]->getWellName();
childInfo.isChecked = m_vWellData[j]->getPlotVisible();
childInfo.isEnabled = m_vecDisplaySettings[i].value.isRootChecked;
m_vecDisplaySettings[i].value.childItems.append(childInfo);
}
}
return;
}
}
}
// 为Faults分类添加子节点
void nmDataAnalyzeManager::addFaultChildItemsToCategory(const QString & parentCategory)
{
// 查找父类别
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == parentCategory) {
// 为每个断层对象创建子节点
for(int j = 0; j < m_vFaultData.size(); ++j) {
if(m_vFaultData[j]) {
ChildCategoryDisplayInfo childInfo;
childInfo.name = m_vFaultData[j]->getFaultName();
childInfo.isChecked = m_vFaultData[j]->getPlotVisible();
childInfo.isEnabled = m_vecDisplaySettings[i].value.isRootChecked;
m_vecDisplaySettings[i].value.childItems.append(childInfo);
}
}
return;
}
}
}
// 为Fractures分类添加子节点
void nmDataAnalyzeManager::addFractureChildItemsToCategory(const QString & parentCategory)
{
// 查找父类别
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == parentCategory) {
// 为每个裂缝对象创建子节点
for(int j = 0; j < m_vFractureData.size(); ++j) {
if(m_vFractureData[j]) {
ChildCategoryDisplayInfo childInfo;
childInfo.name = m_vFractureData[j]->getFractureName();
childInfo.isChecked = m_vFractureData[j]->getPlotVisible();
childInfo.isEnabled = m_vecDisplaySettings[i].value.isRootChecked;
m_vecDisplaySettings[i].value.childItems.append(childInfo);
}
}
return;
}
}
}
// 为Regions分类添加子节点
void nmDataAnalyzeManager::addRegionChildItemsToCategory(const QString & parentCategory)
{
// 查找父类别
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == parentCategory) {
// 为每个复合区对象创建子节点
for(int j = 0; j < m_vRegionData.size(); ++j) {
if(m_vRegionData[j]) {
ChildCategoryDisplayInfo childInfo;
childInfo.name = m_vRegionData[j]->getRegoinName();
childInfo.isChecked = m_vRegionData[j]->getPlotVisible();
childInfo.isEnabled = m_vecDisplaySettings[i].value.isRootChecked;
m_vecDisplaySettings[i].value.childItems.append(childInfo);
}
}
return;
}
}
}
// 为RegionMarks分类添加子节点
void nmDataAnalyzeManager::addRegionMarkChildItemsToCategory(const QString & parentCategory)
{
// 查找父类别
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
if(m_vecDisplaySettings[i].key == parentCategory) {
// 为每个区域标记对象创建子节点
for(int j = 0; j < m_vRegionMarkData.size(); ++j) {
if(m_vRegionMarkData[j]) {
ChildCategoryDisplayInfo childInfo;
childInfo.name = m_vRegionMarkData[j]->getRegionMarkName();
childInfo.isChecked = m_vRegionMarkData[j]->getPlotVisible();
childInfo.isEnabled = m_vecDisplaySettings[i].value.isRootChecked;
m_vecDisplaySettings[i].value.childItems.append(childInfo);
}
}
return;
}
}
}
void nmDataAnalyzeManager::setAllCategoriesRootVisibility(bool bIsVisible)
{
for(int i = 0; i < m_vecDisplaySettings.size(); ++i) {
m_vecDisplaySettings[i].value.isRootChecked = bIsVisible;
}
}
void nmDataAnalyzeManager::setBackgroundImageInfo(const BackgroundImageInfo & info)
{
m_backgroundImageInfo = info;
}
const BackgroundImageInfo& nmDataAnalyzeManager::getBackgroundImageInfo() const
{
return m_backgroundImageInfo;
}
QVector<nmPropertyInterpolationDataSet> nmDataAnalyzeManager::getPropertyInterpolationDataSets() const
{
return m_vecPropertyInterpolationDataSets;
}
void nmDataAnalyzeManager::setPropertyInterpolationDataSets(
const QVector<nmPropertyInterpolationDataSet>& dataSets)
{
m_vecPropertyInterpolationDataSets = dataSets;
// 属性插值只覆盖求解器单元属性,不改变二维 PEBI 拓扑。
m_oNumericalAnalysisCase.invalidateResults();
}
// 从 JSON 文件读取数据到 C++ 对象
bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
{
rapidjson::Document doc;
// 调用 nmDataJsonTools 读取 JSON 文件到 Document
if(!nmDataJsonTools::ReadDomFromFile(filePath, doc)) {
qDebug() << "Error: Failed to read DOM from file:" << filePath;
return false;
}
// 新数据模型不兼容旧项目。明确校验版本,避免按井名或对象顺序猜测关联关系。
const int nSupportedProjectVersion = 2;
if(!doc.HasMember("NumericalProjectVersion") ||
!doc["NumericalProjectVersion"].IsInt() ||
doc["NumericalProjectVersion"].GetInt() != nSupportedProjectVersion) {
qWarning() << "Unsupported numerical project version:" << filePath;
return false;
}
QString sSavedPrimaryWellCode;
QString sSavedCurrentResultWellCode;
NM_CASE_WELL_MODE eSavedPrimaryWellMode = NM_CaseWell_Observation;
bool bSavedIncludeOtherWells = false;
double dSavedPebiGridControl = 150.0;
QVector<nmCalculationWellRef> vecSavedIncludedWells;
QVector<nmSolverWellRef> vecSavedSolverOrder;
// 第一步:版本 2 必须完整保存分析方案,缺字段时不再按旧项目规则猜测。
if(!doc.HasMember("NumericalAnalysisCase") ||
!doc["NumericalAnalysisCase"].IsObject() ||
!doc.HasMember("Wells") ||
!doc["Wells"].IsArray()) {
qWarning() << "Numerical project has no complete analysis case:" << filePath;
return false;
}
const rapidjson::Value& oCaseJson = doc["NumericalAnalysisCase"];
if(!oCaseJson.HasMember("PrimaryWellCode") ||
!oCaseJson["PrimaryWellCode"].IsString() ||
!oCaseJson.HasMember("PrimaryWellMode") ||
!oCaseJson["PrimaryWellMode"].IsInt() ||
!oCaseJson.HasMember("IncludeOtherWells") ||
!oCaseJson["IncludeOtherWells"].IsBool() ||
!oCaseJson.HasMember("PebiGridControl") ||
!oCaseJson["PebiGridControl"].IsNumber() ||
!oCaseJson.HasMember("CurrentResultWellCode") ||
!oCaseJson["CurrentResultWellCode"].IsString() ||
!oCaseJson.HasMember("IncludedWells") ||
!oCaseJson["IncludedWells"].IsArray() ||
!oCaseJson.HasMember("SolverWellOrder") ||
!oCaseJson["SolverWellOrder"].IsArray()) {
qWarning() << "Numerical analysis case contains invalid fields:" << filePath;
return false;
}
sSavedPrimaryWellCode = QString::fromUtf8(
oCaseJson["PrimaryWellCode"].GetString());
sSavedCurrentResultWellCode = QString::fromUtf8(
oCaseJson["CurrentResultWellCode"].GetString());
eSavedPrimaryWellMode = static_cast<NM_CASE_WELL_MODE>(
oCaseJson["PrimaryWellMode"].GetInt());
bSavedIncludeOtherWells = oCaseJson["IncludeOtherWells"].GetBool();
dSavedPebiGridControl = oCaseJson["PebiGridControl"].GetDouble();
const bool bPrimaryModeValid =
eSavedPrimaryWellMode == NM_CaseWell_RateControlled ||
eSavedPrimaryWellMode == NM_CaseWell_Observation;
if(sSavedPrimaryWellCode.isEmpty() ||
sSavedCurrentResultWellCode.isEmpty() ||
!bPrimaryModeValid ||
!qIsFinite(dSavedPebiGridControl) ||
dSavedPebiGridControl <= 0.0) {
qWarning() << "Numerical analysis case contains invalid base values:" << filePath;
return false;
}
// 第二步:先从 JSON 井数组建立唯一 WellCode 到井型的索引,尚不修改当前内存数据。
QMap<QString, NM_WELL_MODEL> mapSavedWellTypes;
const rapidjson::Value& vecWellsJson = doc["Wells"];
for(rapidjson::SizeType nIndex = 0; nIndex < vecWellsJson.Size(); ++nIndex) {
const rapidjson::Value& oWellJson = vecWellsJson[nIndex];
if(!oWellJson.IsObject() ||
!oWellJson.HasMember("WellCode") ||
!oWellJson["WellCode"].IsString() ||
!oWellJson.HasMember("WellType") ||
!oWellJson["WellType"].IsInt()) {
qWarning() << "Numerical project contains an invalid well entry:" << filePath;
return false;
}
const QString sWellCode = QString::fromUtf8(
oWellJson["WellCode"].GetString());
const NM_WELL_MODEL eWellType = static_cast<NM_WELL_MODEL>(
oWellJson["WellType"].GetInt());
const bool bSupportedWellType =
eWellType == NM_WELL_MODEL::Vertical_Well ||
eWellType == NM_WELL_MODEL::Vertical_Fractured_Well ||
eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well;
if(sWellCode.isEmpty() || !bSupportedWellType ||
mapSavedWellTypes.contains(sWellCode)) {
qWarning() << "Numerical project contains an empty, duplicate or unsupported WellCode:"
<< sWellCode;
return false;
}
mapSavedWellTypes.insert(sWellCode, eWellType);
}
if(!mapSavedWellTypes.contains(sSavedPrimaryWellCode)) {
qWarning() << "Primary WellCode is absent from Wells:" << sSavedPrimaryWellCode;
return false;
}
// 第三步:校验包含井,禁止空编码、重复编码、主井重复和非法角色。
QSet<QString> setEffectiveWellCodes;
setEffectiveWellCodes.insert(sSavedPrimaryWellCode);
QSet<QString> setIncludedWellCodes;
const rapidjson::Value& vecIncludedJson = oCaseJson["IncludedWells"];
for(rapidjson::SizeType nIndex = 0;
nIndex < vecIncludedJson.Size();
++nIndex) {
const rapidjson::Value& oWellJson = vecIncludedJson[nIndex];
if(!oWellJson.IsObject() ||
!oWellJson.HasMember("WellCode") ||
!oWellJson["WellCode"].IsString() ||
!oWellJson.HasMember("Mode") ||
!oWellJson["Mode"].IsInt()) {
qWarning() << "IncludedWells contains an invalid entry:" << filePath;
return false;
}
const QString sWellCode = QString::fromUtf8(
oWellJson["WellCode"].GetString());
const NM_CASE_WELL_MODE eMode = static_cast<NM_CASE_WELL_MODE>(
oWellJson["Mode"].GetInt());
// Include Other Wells 只保存主动生产/注入井;无产量观察井由 Map
// 自动推导,不允许以观察角色混入这个持久化列表。
const bool bModeValid = eMode == NM_CaseWell_RateControlled;
if(sWellCode == sSavedPrimaryWellCode ||
!mapSavedWellTypes.contains(sWellCode) ||
setIncludedWellCodes.contains(sWellCode) ||
!bModeValid) {
qWarning() << "IncludedWells contains an invalid WellCode or role:"
<< sWellCode;
return false;
}
setIncludedWellCodes.insert(sWellCode);
if(bSavedIncludeOtherWells) {
setEffectiveWellCodes.insert(sWellCode);
}
vecSavedIncludedWells.append(nmCalculationWellRef(sWellCode, eMode));
}
if(!setEffectiveWellCodes.contains(sSavedCurrentResultWellCode)) {
qWarning() << "Current result WellCode is not effective:"
<< sSavedCurrentResultWellCode;
return false;
}
// 第四步:此时井的历史流量尚未恢复,无法判断一口 Map 井最终属于
// 主动井还是自动观察井。因此这里只校验真实井存在、井型一致且不重复;
// 历史数据加载完成后,再与最终有效计算井集合做严格一致性校验。
QSet<QString> setOrderedWellCodes;
const rapidjson::Value& vecOrderJson = oCaseJson["SolverWellOrder"];
for(rapidjson::SizeType nIndex = 0;
nIndex < vecOrderJson.Size();
++nIndex) {
const rapidjson::Value& oOrderJson = vecOrderJson[nIndex];
if(!oOrderJson.IsObject() ||
!oOrderJson.HasMember("WellCode") ||
!oOrderJson["WellCode"].IsString() ||
!oOrderJson.HasMember("WellType") ||
!oOrderJson["WellType"].IsInt() ||
!oOrderJson.HasMember("EntryKind") ||
!oOrderJson["EntryKind"].IsInt()) {
qWarning() << "SolverWellOrder contains an invalid entry:" << filePath;
return false;
}
const QString sWellCode = QString::fromUtf8(
oOrderJson["WellCode"].GetString());
const NM_WELL_MODEL eWellType = static_cast<NM_WELL_MODEL>(
oOrderJson["WellType"].GetInt());
const NM_SOLVER_ENTRY_KIND eEntryKind =
static_cast<NM_SOLVER_ENTRY_KIND>(
oOrderJson["EntryKind"].GetInt());
if(eEntryKind == NM_SolverEntry_Well) {
if(!mapSavedWellTypes.contains(sWellCode) ||
setOrderedWellCodes.contains(sWellCode) ||
mapSavedWellTypes.value(sWellCode, NM_WELL_MODEL::Unknow_Well) != eWellType) {
qWarning() << "SolverWellOrder contains an invalid real well:"
<< sWellCode;
return false;
}
setOrderedWellCodes.insert(sWellCode);
} else if(eEntryKind == NM_SolverEntry_ManualFracture) {
if(!sWellCode.isEmpty() || eWellType != NM_WELL_MODEL::Unknow_Well) {
qWarning() << "Manual fracture solver entry is malformed:" << filePath;
return false;
}
} else {
qWarning() << "SolverWellOrder contains an unknown entry kind:" << filePath;
return false;
}
vecSavedSolverOrder.append(nmSolverWellRef(
static_cast<int>(nIndex), eWellType, sWellCode, eEntryKind));
}
if(!vecSavedSolverOrder.isEmpty()) {
// 主井和已启用的显式包含井无需依赖历史数据即可确定,必须已经在顺序中。
// 顺序中的其他 Map 井可能是稍后才能识别的无产量观察井,当前不能拒绝。
QSet<QString>::const_iterator oEffectiveIt = setEffectiveWellCodes.constBegin();
for(; oEffectiveIt != setEffectiveWellCodes.constEnd(); ++oEffectiveIt) {
if(!setOrderedWellCodes.contains(*oEffectiveIt)) {
qWarning() << "SolverWellOrder does not contain a configured well:"
<< *oEffectiveIt;
return false;
}
}
}
// 清空现有数据,确保从头加载新数据
foreach(nmDataFracture* pFractureData, m_vFractureData) {
delete pFractureData; // 释放堆上分配的 nmDataFracture 对象
}
m_vFractureData.clear();
foreach(nmDataFault* pFaultData, m_vFaultData) {
delete pFaultData; // 释放堆上分配的 nmDataFault 对象
}
m_vFaultData.clear();
foreach(nmDataRegion* pRegionData, m_vRegionData) {
delete pRegionData; // 释放堆上分配的 nmDataRegion 对象
}
m_vRegionData.clear();
foreach(nmDataRegionMark* pRegionMarkData, m_vRegionMarkData) {
delete pRegionMarkData; // 释放堆上分配的 nmDataRegionMark 对象
}
m_vRegionMarkData.clear();
foreach(nmDataLayer* pLayerData, m_vecLayers) {
delete pLayerData; // 释放堆上分配的 nmDataLayer 对象
}
m_vecLayers.clear();
m_vecPropertyInterpolationDataSets.clear();
if(m_reservoirData) {
delete m_reservoirData;
m_reservoirData = nullptr;
}
if(m_axisData) {
delete m_axisData;
m_axisData = nullptr;
}
if(m_outlineData) {
delete m_outlineData;
m_outlineData = nullptr;
}
if(m_pMixedResults) {
delete m_pMixedResults;
m_pMixedResults = nullptr;
}
if(m_pGeoRefData) {
delete m_pGeoRefData;
m_pGeoRefData = nullptr;
}
if(m_pTimeStep) {
delete m_pTimeStep;
m_pTimeStep = nullptr;
}
if(m_pSensitiveData) {
delete m_pSensitiveData;
m_pSensitiveData = nullptr;
}
//if(m_pPerCloData) {
// delete m_pPerCloData;
// m_pPerCloData = nullptr;
//}
if(m_pAutomaticFittingData) {
delete m_pAutomaticFittingData;
m_pAutomaticFittingData = nullptr;
}
//if(m_pebiPvtPara) {
// delete m_pebiPvtPara;
// m_pebiPvtPara = nullptr;
//}
foreach(nmDataWellBase* pWellData, m_vWellData) {
delete pWellData; // 释放堆上分配的 nmDataWellBase 对象
}
m_vWellData.clear();
m_oNumericalAnalysisCase.clear();
// 解析 "Fractures" 数组
if(doc.HasMember("Fractures") && doc["Fractures"].IsArray()) {
const rapidjson::Value& fracturesJson = doc["Fractures"];
for(rapidjson::SizeType i = 0; i < fracturesJson.Size(); ++i) {
// 动态创建 nmDataFracture 对象,并获取其指针
nmDataFracture* fracture = new nmDataFracture();
fracture->FromJsonValue(fracturesJson[i]); // 调用 nmDataFracture 自身的反序列化方法
m_vFractureData.append(fracture); // 将指针添加到 QVector
}
}
// 解析 "Faults" 数组
if(doc.HasMember("Faults") && doc["Faults"].IsArray()) {
const rapidjson::Value& faultsJson = doc["Faults"];
for(rapidjson::SizeType i = 0; i < faultsJson.Size(); ++i) {
// 动态创建 nmDataFault 对象,并获取其指针
nmDataFault* fault = new nmDataFault();
fault->FromJsonValue(faultsJson[i]); // 调用 nmDataFault 自身的反序列化方法
m_vFaultData.append(fault); // 将指针添加到 QVector
}
}
// 解析 "Regions" 数组
if(doc.HasMember("Regions") && doc["Regions"].IsArray()) {
const rapidjson::Value& regionsJson = doc["Regions"];
for(rapidjson::SizeType i = 0; i < regionsJson.Size(); ++i) {
// 动态创建 nmDataRegion 对象,并获取其指针
nmDataRegion* region = new nmDataRegion(); // 传入this作为父对象
region->FromJsonValue(regionsJson[i]); // 调用 nmDataRegion 自身的反序列化方法
m_vRegionData.append(region); // 将指针添加到 QVector
}
}
// 解析 "RegionMarks" 数组
if(doc.HasMember("RegionMarks") && doc["RegionMarks"].IsArray()) {
const rapidjson::Value& regionMarksJson = doc["RegionMarks"];
for(rapidjson::SizeType i = 0; i < regionMarksJson.Size(); ++i) {
// 动态创建 nmDataRegionMark 对象,并获取其指针
nmDataRegionMark* regionMark = new nmDataRegionMark(); // 传入this作为父对象
regionMark->FromJsonValue(regionMarksJson[i]); // 调用 nmDataRegion 自身的反序列化方法
m_vRegionMarkData.append(regionMark); // 将指针添加到 QVector
}
}
// 解析 "Layers" 数组
if(doc.HasMember("Layers") && doc["Layers"].IsArray()) {
const rapidjson::Value& layersJson = doc["Layers"];
for(rapidjson::SizeType i = 0; i < layersJson.Size(); ++i) {
// 动态创建 nmDataRegion 对象,并获取其指针
nmDataLayer* layer = new nmDataLayer(); // 传入this作为父对象
layer->FromJsonValue(layersJson[i]); // 调用 nmDataLayer 自身的反序列化方法
m_vecLayers.append(layer); // 将指针添加到 QVector
}
}
/* 读取属性插值数据组 */
if(doc.HasMember("PropertyInterpolationDataSets") &&
doc["PropertyInterpolationDataSets"].IsArray()) {
const rapidjson::Value& dataSetsJson = doc["PropertyInterpolationDataSets"];
for(rapidjson::SizeType i = 0; i < dataSetsJson.Size(); ++i) {
const rapidjson::Value& dataSetJson = dataSetsJson[i];
if(!dataSetJson.IsObject()) {
continue;
}
nmPropertyInterpolationDataSet dataSet;
if(dataSetJson.HasMember("Name") && dataSetJson["Name"].IsString()) {
dataSet.name = QString::fromUtf8(dataSetJson["Name"].GetString());
}
if(dataSetJson.HasMember("Property") && dataSetJson["Property"].IsString()) {
dataSet.property = QString::fromUtf8(dataSetJson["Property"].GetString());
}
if(dataSet.property == "h") {
dataSet.valueDisplayUnit = "m";
}
else if(dataSet.property == "phi") {
dataSet.valueDisplayUnit.clear();
}
if(dataSetJson.HasMember("XDisplayUnit") &&
dataSetJson["XDisplayUnit"].IsString()) {
dataSet.xDisplayUnit = QString::fromUtf8(
dataSetJson["XDisplayUnit"].GetString());
}
if(dataSetJson.HasMember("YDisplayUnit") &&
dataSetJson["YDisplayUnit"].IsString()) {
dataSet.yDisplayUnit = QString::fromUtf8(
dataSetJson["YDisplayUnit"].GetString());
}
if(dataSetJson.HasMember("ValueDisplayUnit") &&
dataSetJson["ValueDisplayUnit"].IsString()) {
dataSet.valueDisplayUnit = QString::fromUtf8(
dataSetJson["ValueDisplayUnit"].GetString());
}
if(dataSetJson.HasMember("RangeDisplayUnit") &&
dataSetJson["RangeDisplayUnit"].IsString()) {
dataSet.rangeDisplayUnit = QString::fromUtf8(
dataSetJson["RangeDisplayUnit"].GetString());
}
if(dataSetJson.HasMember("UseForCalculation") &&
dataSetJson["UseForCalculation"].IsBool()) {
dataSet.useForCalculation = dataSetJson["UseForCalculation"].GetBool();
}
if(dataSetJson.HasMember("ShowPoints") && dataSetJson["ShowPoints"].IsBool()) {
dataSet.showPoints = dataSetJson["ShowPoints"].GetBool();
}
if(dataSetJson.HasMember("ShowLabels") && dataSetJson["ShowLabels"].IsBool()) {
dataSet.showLabels = dataSetJson["ShowLabels"].GetBool();
}
if(dataSetJson.HasMember("Nugget") && dataSetJson["Nugget"].IsNumber()) {
dataSet.nugget = dataSetJson["Nugget"].GetDouble();
}
if(dataSetJson.HasMember("Sill") && dataSetJson["Sill"].IsNumber()) {
dataSet.sill = dataSetJson["Sill"].GetDouble();
}
if(dataSetJson.HasMember("Range") && dataSetJson["Range"].IsNumber()) {
dataSet.range = dataSetJson["Range"].GetDouble();
}
if(dataSetJson.HasMember("Model") && dataSetJson["Model"].IsInt()) {
dataSet.model = dataSetJson["Model"].GetInt();
}
if(dataSetJson.HasMember("Points") && dataSetJson["Points"].IsArray()) {
const rapidjson::Value& pointsJson = dataSetJson["Points"];
for(rapidjson::SizeType pointIndex = 0; pointIndex < pointsJson.Size(); ++pointIndex) {
const rapidjson::Value& pointJson = pointsJson[pointIndex];
if(!pointJson.IsObject() ||
!pointJson.HasMember("X") || !pointJson["X"].IsNumber() ||
!pointJson.HasMember("Y") || !pointJson["Y"].IsNumber() ||
!pointJson.HasMember("Value") || !pointJson["Value"].IsNumber()) {
continue;
}
dataSet.points.append(nmPropertyInterpolationPointData(
pointJson["X"].GetDouble(),
pointJson["Y"].GetDouble(),
pointJson["Value"].GetDouble()));
}
}
m_vecPropertyInterpolationDataSets.append(dataSet);
}
}
/* 解析 "Reservoir" 对象 */
if(doc.HasMember("Reservoir") && doc["Reservoir"].IsObject()) {
m_reservoirData = new nmDataReservoir;
m_reservoirData->FromJsonValue(doc["Reservoir"]);
// 注册储层属性到 nmAttrRegistry与 createReservoir 保持一致)
if (m_pAttrRegistry) {
m_pAttrRegistry->clear();
m_pAttrRegistry->regAttr("h", &m_reservoirData->getThickness());
m_pAttrRegistry->regAttr("Pi", &m_reservoirData->getInitialPressure());
m_pAttrRegistry->regAttr("K", &m_reservoirData->getPermeability());
m_pAttrRegistry->regAttr("phi", &m_reservoirData->getPorosity());
m_pAttrRegistry->regAttr("Cti", &m_reservoirData->getCt());
m_pAttrRegistry->regAttr("Cf", &m_reservoirData->getCf());
m_pAttrRegistry->regAttr("Soi", &m_reservoirData->getSoi());
m_pAttrRegistry->regAttr("Swi", &m_reservoirData->getSwi());
}
}
// 解析 "Axis" 对象
if(doc.HasMember("Axis") && doc["Axis"].IsObject()) {
m_axisData = new nmDataAxis;
m_axisData->FromJsonValue(doc["Axis"]);
}
// 解析 "Outline" 对象
if(doc.HasMember("Outline") && doc["Outline"].IsObject()) {
m_outlineData = new nmDataOutline;
m_outlineData->FromJsonValue(doc["Outline"]);
}
// 解析 "GeoRef" 对象
if(doc.HasMember("GeoReference") && doc["GeoReference"].IsObject()) {
m_pGeoRefData = new nmDataGeoRef;
m_pGeoRefData->FromJsonValue(doc["GeoReference"]);
}
// 解析 "AutomaticFitting" 对象
if(doc.HasMember("AutomaticFitting") && doc["AutomaticFitting"].IsObject()) {
m_pAutomaticFittingData = new nmDataAutomaticFitting;
m_pAutomaticFittingData->FromJsonValue(doc["AutomaticFitting"]);
}
// 解析 "Sensitive" 对象
if(doc.HasMember("Sensitive") && doc["Sensitive"].IsObject()) {
m_pSensitiveData = new nmDataSensitive;
m_pSensitiveData->FromJsonValue(doc["Sensitive"]);
}
// 解析 "PVT" 对象
//if(doc.HasMember("PVT") && doc["PVT"].IsObject()) {
// m_pebiPvtPara = new nmDataPvtParaForPebi;
// m_pebiPvtPara->FromJsonValue(doc["PVT"]);
//}
// 解析 "MixResult" 对象
if(doc.HasMember("MixResult") && doc["MixResult"].IsObject()) {
m_pMixedResults = new nmDataMixedResults;
m_pMixedResults->FromJsonValue(doc["MixResult"]);
}
// 解析 "Wells" 数组
if(doc.HasMember("Wells") && doc["Wells"].IsArray()) {
const rapidjson::Value& wellsJson = doc["Wells"];
for(rapidjson::SizeType i = 0; i < wellsJson.Size(); ++i) {
//// 动态创建 nmDataFault 对象,并获取其指针
//nmDataWellBase* well = new nmDataWellBase(); // 传入this作为父对象
//well->FromJsonValue(wellsJson[i]); // 调用 nmDataFault 自身的反序列化方法
//m_vWellData.append(well); // 将指针添加到 QVector
// 获取当前井的JSON对象
const rapidjson::Value& wellItemJson = wellsJson[i];
// 临时指针,用于指向新创建的井对象
nmDataWellBase* pWell = nullptr;
// 井类型
NM_WELL_MODEL eWellType = NM_WELL_MODEL::Unknow_Well;
// 1. 读取 wellType
if(wellItemJson.HasMember("WellType") && wellItemJson["WellType"].IsInt()) {
eWellType = static_cast<NM_WELL_MODEL>(wellItemJson["WellType"].GetInt());
}
// 2. 根据 eWellType 动态创建不同的井类型实例
switch(eWellType) {
case NM_WELL_MODEL::Vertical_Well:
pWell = new nmDataVerticalWell;
break;
case NM_WELL_MODEL::Vertical_Fractured_Well:
pWell = new nmDataVerticalFracturedWell;
break;
case NM_WELL_MODEL::Horizontal_Fractured_Well:
pWell = new nmDataHorizontalFracturedWell;
break;
// TODO: 添加其他具体的井类型
default:
break;
}
// 3. 调用具体井类型的 FromJsonValue 方法进行反序列化
if(pWell) { // 确保 well 对象已成功创建
pWell->FromJsonValue(wellItemJson); // 调用其自身的反序列化方法
m_vWellData.append(pWell); // 将指针添加到 QVector
// TODO将最后一口井设置为当前井(临时)
//this->setCurWellData(pWell);
}
}
}
// 反序列化函数没有返回值,因此必须再次核对实际创建的井目录。
// JSON 预校验目录与内存目录必须在 WellCode、井型和数量上完全一致。
QMap<QString, NM_WELL_MODEL> mapLoadedWellTypes;
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWellData = m_vWellData[nIndex];
if(pWellData == nullptr || pWellData->getWellCode().isEmpty() ||
mapLoadedWellTypes.contains(pWellData->getWellCode())) {
qWarning() << "Loaded numerical project contains an invalid WellCode:";
return false;
}
mapLoadedWellTypes.insert(pWellData->getWellCode(),
pWellData->getWellType());
}
if(mapLoadedWellTypes != mapSavedWellTypes) {
qWarning() << "Loaded well directory differs from validated JSON:"
<< filePath;
return false;
}
// 井对象全部加载完成后,先按 WellCode 原样恢复已经通过结构校验的分析方案。
// 这里不能调用会检查流量点的公开 setter因为历史流量要到 loadNmResult()
// 的二进制恢复阶段才可用;业务有效性在历史数据恢复后统一确认。
nmDataWellBase* pPrimaryWell = findWellByCode(sSavedPrimaryWellCode);
if(pPrimaryWell == nullptr) {
qWarning() << "PrimaryWellCode is missing from numerical project:"
<< sSavedPrimaryWellCode;
return false;
}
m_pCurDataWell = pPrimaryWell;
m_oNumericalAnalysisCase.setPrimaryWellCode(sSavedPrimaryWellCode);
m_oNumericalAnalysisCase.setPrimaryWellMode(eSavedPrimaryWellMode);
m_oNumericalAnalysisCase.setPebiGridControl(dSavedPebiGridControl);
m_oNumericalAnalysisCase.setIncludedWells(vecSavedIncludedWells);
m_oNumericalAnalysisCase.setIncludeOtherWells(bSavedIncludeOtherWells);
m_oNumericalAnalysisCase.setSolverWellOrder(vecSavedSolverOrder);
m_oNumericalAnalysisCase.setCurrentResultWellCode(
sSavedCurrentResultWellCode.isEmpty()
? sSavedPrimaryWellCode : sSavedCurrentResultWellCode);
// 解析 时间步 对象
if(doc.HasMember("TimeStep") && doc["TimeStep"].IsObject()) {
m_pTimeStep = new nmDataTimeStepSetting;
m_pTimeStep->FromJsonValue(doc["TimeStep"]);
}
// 恢复当前求解器模型类型
if(doc.HasMember("SolverModelType") && doc["SolverModelType"].IsInt()) {
m_eSolverModelType = static_cast<NM_SOLVER_MODEL_TYPE>(doc["SolverModelType"].GetInt());
}
if(doc.HasMember("PebiSolverType") && doc["PebiSolverType"].IsInt()) {
setPebiSolverType(doc["PebiSolverType"].GetInt());
}
if(doc.HasMember("PebiOmpThreads") && doc["PebiOmpThreads"].IsInt()) {
setPebiOmpThreads(doc["PebiOmpThreads"].GetInt());
}
if(doc.HasMember("PebiIluReuseSteps") && doc["PebiIluReuseSteps"].IsInt()) {
setPebiIluReuseSteps(doc["PebiIluReuseSteps"].GetInt());
}
return true;
}
// 将 C++ 对象数据写入 JSON 文件
bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath)
{
// 第一步:保存前校验 Map 井目录,持久化身份只允许非空且唯一的 WellCode。
QSet<QString> setWellCodes;
QMap<QString, NM_WELL_MODEL> mapWellTypes;
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWellData = m_vWellData[nIndex];
if(pWellData == nullptr || pWellData->getWellCode().isEmpty() ||
setWellCodes.contains(pWellData->getWellCode())) {
qWarning() << "Cannot save numerical project: empty or duplicate WellCode.";
return false;
}
setWellCodes.insert(pWellData->getWellCode());
mapWellTypes.insert(pWellData->getWellCode(), pWellData->getWellType());
}
const QString sPrimaryWellCode = getPrimaryWellCode();
const QString sCurrentResultWellCode = getCurrentResultWellCode();
if(!setWellCodes.contains(sPrimaryWellCode) ||
!qIsFinite(getPebiGridControl()) ||
getPebiGridControl() <= 0.0) {
qWarning() << "Cannot save numerical project: invalid primary well or GridControl.";
return false;
}
// 第二步:校验有效计算井、当前结果井和求解器顺序使用同一组 WellCode。
QSet<QString> setEffectiveWellCodes;
QVector<nmCalculationWellRef> vecEffectiveWells =
getEffectiveCalculationWells();
for(int nIndex = 0; nIndex < vecEffectiveWells.size(); ++nIndex) {
const nmCalculationWellRef& oWellRef = vecEffectiveWells[nIndex];
if(!setWellCodes.contains(oWellRef.m_sWellCode) ||
setEffectiveWellCodes.contains(oWellRef.m_sWellCode)) {
qWarning() << "Cannot save numerical project: invalid effective WellCode:"
<< oWellRef.m_sWellCode;
return false;
}
setEffectiveWellCodes.insert(oWellRef.m_sWellCode);
}
if(!setEffectiveWellCodes.contains(sCurrentResultWellCode)) {
qWarning() << "Cannot save numerical project: result WellCode is not effective:"
<< sCurrentResultWellCode;
return false;
}
QSet<QString> setOrderedWellCodes;
QVector<nmSolverWellRef> vecValidatedSolverOrder = getSolverWellOrder();
// 求解器顺序只属于有效网格。网格已经失效时保存空顺序,
// 防止旧顺序与未保存的旧网格组成一个自相矛盾的项目。
if(!isPebiGridValid()) {
vecValidatedSolverOrder.clear();
}
for(int nIndex = 0; nIndex < vecValidatedSolverOrder.size(); ++nIndex) {
const nmSolverWellRef& oWellRef = vecValidatedSolverOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
if(!setEffectiveWellCodes.contains(oWellRef.m_sWellCode) ||
setOrderedWellCodes.contains(oWellRef.m_sWellCode) ||
mapWellTypes.value(oWellRef.m_sWellCode,
NM_WELL_MODEL::Unknow_Well) != oWellRef.m_eWellType) {
qWarning() << "Cannot save numerical project: invalid solver WellCode:"
<< oWellRef.m_sWellCode;
return false;
}
setOrderedWellCodes.insert(oWellRef.m_sWellCode);
} else if(oWellRef.m_eEntryKind == NM_SolverEntry_ManualFracture) {
if(!oWellRef.m_sWellCode.isEmpty() ||
oWellRef.m_eWellType != NM_WELL_MODEL::Unknow_Well) {
qWarning() << "Cannot save numerical project: malformed manual fracture entry.";
return false;
}
} else {
qWarning() << "Cannot save numerical project: unknown solver entry kind.";
return false;
}
}
if(!vecValidatedSolverOrder.isEmpty() &&
setOrderedWellCodes != setEffectiveWellCodes) {
qWarning() << "Cannot save numerical project: solver order is incomplete.";
return false;
}
// 第三步:基础身份校验通过后再构造 JSON避免生成无法重新加载的半成品文件。
rapidjson::Document doc;
doc.SetObject(); // 根节点是对象
rapidjson::Document::AllocatorType& allocator = doc.GetAllocator(); // 获取内存分配器
// 新的数据模型从版本2开始只使用 WellCode 建立跨模块关联。
doc.AddMember("NumericalProjectVersion", 2, allocator);
// 构建 "Fractures" 数组的 JSON Value
rapidjson::Value fracturesJsonArray(rapidjson::kArrayType);
// 遍历存储 nmDataFracture 指针的 QVector并解引用指针来调用 ToJsonValue 方法
foreach(nmDataFracture* pFractureData, m_vFractureData) {
if(pFractureData) { // 检查指针是否有效
fracturesJsonArray.PushBack(pFractureData->ToJsonValue(allocator), allocator);
}
}
doc.AddMember("Fractures", fracturesJsonArray, allocator);
// 构建 "Faults" 数组的 JSON Value
rapidjson::Value faultsJsonArray(rapidjson::kArrayType);
// 遍历存储 nmDataFault 指针的 QVector并解引用指针来调用 ToJsonValue 方法
foreach(nmDataFault* pFaultData, m_vFaultData) {
if(pFaultData) { // 检查指针是否有效
faultsJsonArray.PushBack(pFaultData->ToJsonValue(allocator), allocator);
}
}
doc.AddMember("Faults", faultsJsonArray, allocator);
// 构建 "Regions" 数组的 JSON Value
rapidjson::Value regionsJsonArray(rapidjson::kArrayType);
// 遍历存储 nmDataRegion 指针的 QVector并解引用指针来调用 ToJsonValue 方法
foreach(nmDataRegion* pRegionData, m_vRegionData) {
if(pRegionData) { // 检查指针是否有效
regionsJsonArray.PushBack(pRegionData->ToJsonValue(allocator), allocator);
}
}
doc.AddMember("Regions", regionsJsonArray, allocator);
// 构建 "RegionMarks" 数组的 JSON Value
rapidjson::Value regionMarksJsonArray(rapidjson::kArrayType);
// 遍历存储 nmDataRegionMark 指针的 QVector并解引用指针来调用 ToJsonValue 方法
foreach(nmDataRegionMark* pRegionMarkData, m_vRegionMarkData) {
if(pRegionMarkData) { // 检查指针是否有效
regionMarksJsonArray.PushBack(pRegionMarkData->ToJsonValue(allocator), allocator);
}
}
doc.AddMember("RegionMarks", regionMarksJsonArray, allocator);
// 构建 "Layers" 数组的 JSON Value
rapidjson::Value layersJsonArray(rapidjson::kArrayType);
// 遍历存储 nmDataLayer 指针的 QVector并解引用指针来调用 ToJsonValue 方法
foreach(nmDataLayer* pLayerData, m_vecLayers) {
if(pLayerData) { // 检查指针是否有效
layersJsonArray.PushBack(pLayerData->ToJsonValue(allocator), allocator);
}
}
doc.AddMember("Layers", layersJsonArray, allocator);
/* 保存属性插值数据组数值统一使用m、D等求解器基准单位 */
rapidjson::Value interpolationDataSetsJson(rapidjson::kArrayType);
for(int dataSetIndex = 0;
dataSetIndex < m_vecPropertyInterpolationDataSets.size();
++dataSetIndex) {
const nmPropertyInterpolationDataSet& dataSet =
m_vecPropertyInterpolationDataSets[dataSetIndex];
rapidjson::Value dataSetJson(rapidjson::kObjectType);
QByteArray nameUtf8 = dataSet.name.toUtf8();
QByteArray propertyUtf8 = dataSet.property.toUtf8();
dataSetJson.AddMember("Name",
rapidjson::Value(nameUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("Property",
rapidjson::Value(propertyUtf8.constData(), allocator).Move(), allocator);
QByteArray xDisplayUnitUtf8 = dataSet.xDisplayUnit.toUtf8();
QByteArray yDisplayUnitUtf8 = dataSet.yDisplayUnit.toUtf8();
QByteArray valueDisplayUnitUtf8 = dataSet.valueDisplayUnit.toUtf8();
QByteArray rangeDisplayUnitUtf8 = dataSet.rangeDisplayUnit.toUtf8();
dataSetJson.AddMember("XDisplayUnit",
rapidjson::Value(xDisplayUnitUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("YDisplayUnit",
rapidjson::Value(yDisplayUnitUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("ValueDisplayUnit",
rapidjson::Value(valueDisplayUnitUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("RangeDisplayUnit",
rapidjson::Value(rangeDisplayUnitUtf8.constData(), allocator).Move(), allocator);
dataSetJson.AddMember("UseForCalculation", dataSet.useForCalculation, allocator);
dataSetJson.AddMember("ShowPoints", dataSet.showPoints, allocator);
dataSetJson.AddMember("ShowLabels", dataSet.showLabels, allocator);
dataSetJson.AddMember("Nugget", dataSet.nugget, allocator);
dataSetJson.AddMember("Sill", dataSet.sill, allocator);
dataSetJson.AddMember("Range", dataSet.range, allocator);
dataSetJson.AddMember("Model", dataSet.model, allocator);
rapidjson::Value pointsJson(rapidjson::kArrayType);
for(int pointIndex = 0; pointIndex < dataSet.points.size(); ++pointIndex) {
const nmPropertyInterpolationPointData& point = dataSet.points[pointIndex];
rapidjson::Value pointJson(rapidjson::kObjectType);
pointJson.AddMember("X", point.x, allocator);
pointJson.AddMember("Y", point.y, allocator);
pointJson.AddMember("Value", point.value, allocator);
pointsJson.PushBack(pointJson, allocator);
}
dataSetJson.AddMember("Points", pointsJson, allocator);
interpolationDataSetsJson.PushBack(dataSetJson, allocator);
}
doc.AddMember("PropertyInterpolationDataSets", interpolationDataSetsJson, allocator);
// 序列化 "Reservoir"
if(m_reservoirData) {
rapidjson::Value reservoirJson(rapidjson::kObjectType);
reservoirJson = m_reservoirData->ToJsonValue(allocator);
doc.AddMember("Reservoir", reservoirJson, allocator);
}
// 序列化 "Axis"
if(m_axisData) {
rapidjson::Value axisJson(rapidjson::kObjectType);
axisJson = m_axisData->ToJsonValue(allocator);
doc.AddMember("Axis", axisJson, allocator);
}
// 序列化 "Outline"
if(m_outlineData) {
rapidjson::Value outlineJson(rapidjson::kObjectType);
outlineJson = m_outlineData->ToJsonValue(allocator);
doc.AddMember("Outline", outlineJson, allocator);
}
// 序列化 "Reservoir"
if(m_pGeoRefData) {
rapidjson::Value geoRefJson(rapidjson::kObjectType);
geoRefJson = m_pGeoRefData->ToJsonValue(allocator);
doc.AddMember("GeoReference", geoRefJson, allocator);
}
//// 序列化 "PerforationClosing"
//if(m_pPerCloData) {
// rapidjson::Value perCloJson(rapidjson::kObjectType);
// perCloJson = m_pPerCloData->ToJsonValue(allocator);
// doc.AddMember("PerforationClosing", perCloJson, allocator);
//}
//// 序列化 "SkinVsRate"
//if(m_pSkinVsRateData) {
// rapidjson::Value skinJson(rapidjson::kObjectType);
// skinJson = m_pSkinVsRateData->ToJsonValue(allocator);
// doc.AddMember("SkinVsRate", skinJson, allocator);
//}
// 序列化 "AutomaticFitting"
if(m_pAutomaticFittingData) {
rapidjson::Value autofitJson(rapidjson::kObjectType);
autofitJson = m_pAutomaticFittingData->ToJsonValue(allocator);
doc.AddMember("AutomaticFitting", autofitJson, allocator);
}
// 序列化 "PVT"
//if(m_pebiPvtPara) {
// rapidjson::Value pvtJson(rapidjson::kObjectType);
// pvtJson = m_pebiPvtPara->ToJsonValue(allocator);
// doc.AddMember("PVT", pvtJson, allocator);
//}
// 序列化 "MixResult"
if(m_pMixedResults) {
rapidjson::Value mixResultsJson(rapidjson::kObjectType);
mixResultsJson = m_pMixedResults->ToJsonValue(allocator);
doc.AddMember("MixResult", mixResultsJson, allocator);
}
// 序列化 "Sensitive"
if(m_pSensitiveData) {
rapidjson::Value sensitiveJson(rapidjson::kObjectType);
sensitiveJson = m_pSensitiveData->ToJsonValue(allocator);
doc.AddMember("Sensitive", sensitiveJson, allocator);
}
// 构建 "Wells" 数组的 JSON Value
rapidjson::Value wellsJsonArray(rapidjson::kArrayType);
// 遍历存储 nmDataWellBase 指针的 QVector并解引用指针来调用 ToJsonValue 方法
foreach(nmDataWellBase* pWellData, m_vWellData) {
if(nmDataVerticalFracturedWell * pVerticalFracturedWell = dynamic_cast<nmDataVerticalFracturedWell * >(pWellData)) { // 检查指针是否有效
wellsJsonArray.PushBack(pVerticalFracturedWell->ToJsonValue(allocator), allocator);
} else if(nmDataHorizontalFracturedWell * pHorizontalFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell * >(pWellData)) { // 检查指针是否有效
wellsJsonArray.PushBack(pHorizontalFracturedWell->ToJsonValue(allocator), allocator);
} else if(nmDataHorizontalWell * pHorizontalWell = dynamic_cast<nmDataHorizontalWell * >(pWellData)) { // 检查指针是否有效
wellsJsonArray.PushBack(pHorizontalWell->ToJsonValue(allocator), allocator);
} else if(nmDataVerticalWell * pVerticalWell = dynamic_cast<nmDataVerticalWell * >(pWellData)) { // 检查指针是否有效
wellsJsonArray.PushBack(pVerticalWell->ToJsonValue(allocator), allocator);
}
}
doc.AddMember("Wells", wellsJsonArray, allocator);
// 数值分析方案独立保存:井名称不参与身份、顺序和结果映射。
rapidjson::Value oCaseJson(rapidjson::kObjectType);
QByteArray baPrimaryWellCode = getPrimaryWellCode().toUtf8();
oCaseJson.AddMember("PrimaryWellCode",
rapidjson::Value(baPrimaryWellCode.constData(), allocator).Move(),
allocator);
oCaseJson.AddMember("PrimaryWellMode",
static_cast<int>(m_oNumericalAnalysisCase.getPrimaryWellMode()),
allocator);
oCaseJson.AddMember("IncludeOtherWells",
m_oNumericalAnalysisCase.getIncludeOtherWells(),
allocator);
oCaseJson.AddMember("PebiGridControl",
m_oNumericalAnalysisCase.getPebiGridControl(),
allocator);
QByteArray baCurrentResultWellCode = getCurrentResultWellCode().toUtf8();
oCaseJson.AddMember("CurrentResultWellCode",
rapidjson::Value(baCurrentResultWellCode.constData(), allocator).Move(),
allocator);
rapidjson::Value vecIncludedJson(rapidjson::kArrayType);
QVector<nmCalculationWellRef> vecIncludedWells =
getIncludedCalculationWells();
for(int nIndex = 0; nIndex < vecIncludedWells.size(); ++nIndex) {
const nmCalculationWellRef& oWellRef = vecIncludedWells[nIndex];
rapidjson::Value oWellJson(rapidjson::kObjectType);
QByteArray baWellCode = oWellRef.m_sWellCode.toUtf8();
oWellJson.AddMember("WellCode",
rapidjson::Value(baWellCode.constData(), allocator).Move(),
allocator);
oWellJson.AddMember("Mode", static_cast<int>(oWellRef.m_eMode), allocator);
vecIncludedJson.PushBack(oWellJson, allocator);
}
oCaseJson.AddMember("IncludedWells", vecIncludedJson, allocator);
rapidjson::Value vecSolverOrderJson(rapidjson::kArrayType);
QVector<nmSolverWellRef> vecSolverOrder = vecValidatedSolverOrder;
for(int nIndex = 0; nIndex < vecSolverOrder.size(); ++nIndex) {
const nmSolverWellRef& oWellRef = vecSolverOrder[nIndex];
rapidjson::Value oOrderJson(rapidjson::kObjectType);
QByteArray baWellCode = oWellRef.m_sWellCode.toUtf8();
oOrderJson.AddMember("WellCode",
rapidjson::Value(baWellCode.constData(), allocator).Move(),
allocator);
oOrderJson.AddMember("WellType",
static_cast<int>(oWellRef.m_eWellType), allocator);
oOrderJson.AddMember("EntryKind",
static_cast<int>(oWellRef.m_eEntryKind), allocator);
vecSolverOrderJson.PushBack(oOrderJson, allocator);
}
oCaseJson.AddMember("SolverWellOrder", vecSolverOrderJson, allocator);
doc.AddMember("NumericalAnalysisCase", oCaseJson, allocator);
// 序列化 时间步数据
if(m_pTimeStep) {
rapidjson::Value timeStepJson(rapidjson::kObjectType);
timeStepJson = m_pTimeStep->ToJsonValue(allocator);
doc.AddMember("TimeStep", timeStepJson, allocator);
}
// 保存当前求解器模型类型
doc.AddMember("SolverModelType", static_cast<int>(m_eSolverModelType), allocator);
doc.AddMember("PebiSolverType", m_nPebiSolverType, allocator);
doc.AddMember("PebiOmpThreads", m_nPebiOmpThreads, allocator);
doc.AddMember("PebiIluReuseSteps", m_nPebiIluReuseSteps, allocator);
// 将最终构建好的 Document 写入文件
if(!nmDataJsonTools::WriteDomToFile(doc, filePath)) {
qDebug() << "Error: Failed to write DOM to file:" << filePath;
return false;
}
return true;
}
bool nmDataAnalyzeManager::saveNmResult(QString sRstCode, iSubWndFitting* pSubWndF, bool bClearRoot)
{
Q_ASSERT(nullptr != pSubWndF);
// 通过窗口层上下文获取保存目录
nmDataAnalyzeContextProvider* pContextProvider = nmDataAnalyzeContext::provider();
Q_ASSERT(nullptr != pContextProvider);
QString sDir;
if(pContextProvider == nullptr || !pContextProvider->getSaveResultDir(pSubWndF, sRstCode, sDir)) {
return false;
}
// bClearRoot为true时清空整个成果目录清理已删除窗口的残留数据。
// 仅在遍历保存的第一个窗口传入true后续窗口传false避免误删其他窗口数据。
if(bClearRoot) {
QString sResultRootPath = QFileInfo(sDir).dir().absolutePath();
if(!clearDirectoryContents(sResultRootPath)) {
return false;
}
}
QString sResultPath = sDir + "/Results";
if(!this->ensureDirectoryExists(sResultPath)) {
return false;
}
// 将数据写入Json文件
if(!this->WriteProjectData(sResultPath + "/Numerical_Parameters.json")) {
return false;
}
// --- 保存所有井的历史数据到独立目录 /WellHistory ---
QString sWellHistoryDataPath = sDir + "/WellHistory";
if(!this->ensureDirectoryExists(sWellHistoryDataPath)) {
return false;
}
QVector<nmDataWellBase*> vecAllWells = this->getWellDataList(); // 获取所有井
// 遍历所有井,保存历史数据
for(int wellIdx = 0; wellIdx < vecAllWells.size(); ++wellIdx) {
nmDataWellBase* pWellData = vecAllWells[wellIdx];
if(pWellData) {
if(!this->saveWellHistoryData(sWellHistoryDataPath, pWellData)) {
return false;
}
}
}
// 第一步:准备网格、场结果和单井结果的固定路径。
QString sGridPath = sDir + "/Grid";
QString sResultGridFilePath = sGridPath + "/ResultGrid.vtu";
QString sResultWellLocationPath =
sGridPath + "/ResultWellLocations.bin";
QString sTimeStepDataDirPath = sDir + "/TimeStepData";
QString sTimeStepDataFilePath =
sTimeStepDataDirPath + "/PressureTimeSteps.bin";
QString sWellRstDataPath = sDir + "/WellRst";
// 第二步:无有效网格时不保存内存中残留的 VTK 对象和结果,并清理同目录旧文件。
if(!isPebiGridValid()) {
QFile::remove(sGridPath + "/CurrentGrid.vtu");
QFile::remove(sResultGridFilePath);
QFile::remove(sResultWellLocationPath);
QFile::remove(sTimeStepDataFilePath);
if(QDir(sWellRstDataPath).exists() &&
!clearDirectoryContents(sWellRstDataPath)) {
return false;
}
return true;
}
// 方案标记为有效网格时,必须同时持有可写出的 VTK 网格。
if(!m_pVtkUnstructuredGrid) {
qWarning() << "Cannot save a valid PEBI grid: VTK grid is missing.";
return false;
}
if(!this->ensureDirectoryExists(sGridPath)) {
return false;
}
// 第三步:保存与 SolverWellOrder 同版本的实时网格。
if(!this->writeUnstructuredGridToFile(
m_pVtkUnstructuredGrid, sGridPath + "/CurrentGrid.vtu")) {
return false;
}
// 第四步:网格有效但结果无效时只保存网格,并删除上一次求解的残留文件。
if(!m_oNumericalAnalysisCase.areResultsValid()) {
QFile::remove(sResultGridFilePath);
QFile::remove(sResultWellLocationPath);
QFile::remove(sTimeStepDataFilePath);
if(QDir(sWellRstDataPath).exists() &&
!clearDirectoryContents(sWellRstDataPath)) {
return false;
}
return true;
}
// 结果标记有效时,基础网格和时间步压力必须同时完整存在。
if(!m_pResultBaseGrid || m_mapTimeStepDataP.isEmpty()) {
qWarning() << "Cannot save valid PEBI results: result grid or time steps are missing.";
return false;
}
// 第五步:保存场图基础网格、井位置和全部时间步压力。
if(!this->writeUnstructuredGridToFile(
m_pResultBaseGrid, sResultGridFilePath)) {
return false;
}
if(!this->saveWellLocations(sResultWellLocationPath)) {
return false;
}
if(!this->ensureDirectoryExists(sTimeStepDataDirPath)) {
return false;
}
if(!writeTimeStepDataMapToBinaryFile(sTimeStepDataFilePath)) {
qDebug() << QString("Failed to save all time step data map to binary file: %1").arg(sTimeStepDataFilePath);
return false;
}
// 第六步:先清空目标目录,再按 WellCode 保存每口真实计算井的结果。
if(!this->ensureDirectoryExists(sWellRstDataPath)) {
return false;
}
if(!clearDirectoryContents(sWellRstDataPath)) {
return false;
}
QVector<nmSolverWellRef> vecWells = this->getSolverWellOrder();
// 遍历每口井,保存里面计算出来的数据
for(int wellIdx = 0; wellIdx < vecWells.size(); ++wellIdx) {
const nmSolverWellRef& oWellRef = vecWells[wellIdx];
if(oWellRef.m_eEntryKind == NM_SolverEntry_ManualFracture) {
continue;
}
nmDataWellBase* pWellData =
this->findWellByCode(oWellRef.m_sWellCode);
if(pWellData) {
if(!this->saveWellCalRstData(sWellRstDataPath, pWellData)) {
return false;
}
}
}
return true;
}
bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
{
// 将Json文件内容读取出来
QString sResultPath = sLoadAnalDir + "/Results/Numerical_Parameters.json";
if(!this->ReadProjectData(sResultPath)) {
return false;
}
// 第一步:先检查分析方案、网格文件和结果文件是否构成完整的一组。
QString sCurrentGridPath = sLoadAnalDir + "/Grid/CurrentGrid.vtu";
QString sResultGridPath = sLoadAnalDir + "/Grid/ResultGrid.vtu";
QString sResultWellLocationPath =
sLoadAnalDir + "/Grid/ResultWellLocations.bin";
QString sTimeStepDataFilePath =
sLoadAnalDir + "/TimeStepData/PressureTimeSteps.bin";
QString sWellRstDataPath = sLoadAnalDir + "/WellRst";
const bool bHasCurrentGridFile = QFile::exists(sCurrentGridPath);
const bool bHasResultGridFile = QFile::exists(sResultGridPath);
const bool bHasResultWellLocationFile =
QFile::exists(sResultWellLocationPath);
const bool bHasTimeStepDataFile = QFile::exists(sTimeStepDataFilePath);
QStringList listResultFilters;
listResultFilters << "*_Results.bin";
const bool bHasAnyWellResultFile = !QDir(sWellRstDataPath).entryList(
listResultFilters, QDir::Files).isEmpty();
const bool bHasSolverWellOrder = !getSolverWellOrder().isEmpty();
const bool bHasAnyResultFile = bHasResultGridFile ||
bHasResultWellLocationFile || bHasTimeStepDataFile ||
bHasAnyWellResultFile;
const bool bHasCompleteResultFiles = bHasResultGridFile &&
bHasResultWellLocationFile && bHasTimeStepDataFile;
if(bHasCurrentGridFile != bHasSolverWellOrder) {
qWarning() << "CurrentGrid.vtu and SolverWellOrder are inconsistent:"
<< sLoadAnalDir;
return false;
}
if(bHasAnyResultFile &&
(!bHasCurrentGridFile || !bHasCompleteResultFiles)) {
qWarning() << "PEBI result files are incomplete:" << sLoadAnalDir;
return false;
}
// 第二步:读取当前网格;只有文件和求解器顺序同时存在时才标记有效。
if(bHasCurrentGridFile &&
!this->readUnstructuredGridFromFile(m_pVtkUnstructuredGrid,
sCurrentGridPath)) {
return false;
}
if(bHasCurrentGridFile) {
if(getSolverWellOrder().isEmpty()) {
qWarning() << "Loaded grid has no SolverWellOrder:" << sCurrentGridPath;
return false;
}
m_oNumericalAnalysisCase.markGridBuilt();
}
// 第三步:读取完整求解结果;网格项目允许尚未执行过求解。
if(bHasResultGridFile &&
!this->readUnstructuredGridFromFile(m_pResultBaseGrid,
sResultGridPath)) {
return false;
}
if(bHasResultWellLocationFile &&
!this->loadWellLocations(sResultWellLocationPath)) {
return false;
}
if(bHasTimeStepDataFile &&
!readTimeStepDataMapFromBinaryFile(sTimeStepDataFilePath)) {
qWarning() << QString("Failed to load all time step data map from binary file: %1").arg(sTimeStepDataFilePath);
return false;
}
if(bHasCompleteResultFiles && m_mapTimeStepDataP.isEmpty()) {
qWarning() << "PEBI result contains no pressure time steps:"
<< sLoadAnalDir;
return false;
}
// 第四步:按 Map 井目录完整恢复历史数据。
// 版本 2 保存时会为每口井写入历史文件,因此缺失或损坏都表示项目不完整。
QString sWellHistoryDataPath = sLoadAnalDir + "/WellHistory";
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWellData = m_vWellData[nIndex];
if(!this->loadWellHistoryData(sWellHistoryDataPath, pWellData)) {
qWarning() << "Failed to load well history for WellCode:"
<< (pWellData == nullptr
? QString()
: pWellData->getWellCode());
return false;
}
}
// 第五步:有完整场结果时,必须同时具备每口真实求解井的结果。
// SolverWellOrder 是 DLL 返回槽位与井对象之间的唯一映射;手工裂缝只占槽位,
// 不对应 Map 中的井对象,也没有独立的结果文件。
if(bHasCompleteResultFiles) {
QVector<nmSolverWellRef> vecSolverOrder = getSolverWellOrder();
for(int nIndex = 0; nIndex < vecSolverOrder.size(); ++nIndex) {
const nmSolverWellRef& oWellRef = vecSolverOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_ManualFracture) {
continue;
}
nmDataWellBase* pWellData = findWellByCode(oWellRef.m_sWellCode);
if(pWellData == nullptr ||
!this->loadWellCalRstData(sWellRstDataPath, pWellData)) {
qWarning() << "Failed to load calculation result for WellCode:"
<< oWellRef.m_sWellCode;
return false;
}
}
}
// 第六步:二进制数据完整后,再生成各井用于曲线显示的压力、流量点。
this->loadWellPreAndFlow();
// 第七步:历史数据恢复后再执行依赖流量的业务校验。
// IncludedWells 即使当前总开关关闭,也只能保存有有效产量制度的井。
QVector<nmCalculationWellRef> vecIncludedWells =
getIncludedCalculationWells();
for(int nIndex = 0; nIndex < vecIncludedWells.size(); ++nIndex) {
const nmCalculationWellRef& oWellRef = vecIncludedWells[nIndex];
nmDataWellBase* pWellData = findWellByCode(oWellRef.m_sWellCode);
if(!isSupportedNumericalWell(pWellData) ||
pWellData->getFlowPoints().size() < 2) {
qWarning() << "Failed to restore included rate well:"
<< oWellRef.m_sWellCode;
return false;
}
}
// 主动井必须能够恢复有效产量制度;自动观察井允许没有流量点。
QVector<nmCalculationWellRef> vecEffectiveWells = getEffectiveCalculationWells();
QSet<QString> setEffectiveWellCodes;
for(int nIndex = 0; nIndex < vecEffectiveWells.size(); ++nIndex) {
const nmCalculationWellRef& oWellRef = vecEffectiveWells[nIndex];
nmDataWellBase* pWellData = findWellByCode(oWellRef.m_sWellCode);
if(pWellData == nullptr ||
(oWellRef.m_eMode == NM_CaseWell_RateControlled &&
pWellData->getFlowPoints().size() < 2)) {
qWarning() << "Failed to restore rate schedule for WellCode:"
<< oWellRef.m_sWellCode;
return false;
}
setEffectiveWellCodes.insert(oWellRef.m_sWellCode);
}
// 保存的求解器槽位允许夹有手工裂缝,但其中全部真实井必须与最终有效井
// 完全一致。该校验放在这里,才能正确识别 Map 中自动加入的无产量观察井。
QVector<nmSolverWellRef> vecLoadedSolverOrder = getSolverWellOrder();
if(!vecLoadedSolverOrder.isEmpty()) {
QSet<QString> setOrderedWellCodes;
for(int nIndex = 0; nIndex < vecLoadedSolverOrder.size(); ++nIndex) {
const nmSolverWellRef& oWellRef = vecLoadedSolverOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
setOrderedWellCodes.insert(oWellRef.m_sWellCode);
}
}
if(setOrderedWellCodes != setEffectiveWellCodes) {
qWarning() << "Loaded SolverWellOrder differs from effective wells:"
<< sLoadAnalDir;
return false;
}
}
// 结果井属于显示状态,但也必须在流量恢复后满足当前结果井规则。
if(!isWellAvailableForResult(getCurrentResultWellCode())) {
qWarning() << "Loaded result WellCode is not available:"
<< getCurrentResultWellCode();
return false;
}
if(bHasCurrentGridFile && bHasCompleteResultFiles) {
m_oNumericalAnalysisCase.markResultsAvailable();
}
if (m_pVtkUnstructuredGrid != nullptr)
m_bIsLoadData = true;
return true;
}
bool nmDataAnalyzeManager::ensureDirectoryExists(const QString & dirPath)
{
QDir dir(dirPath);
if(!dir.exists()) {
qDebug() << QString("Directory does not exist, attempting to create: %1").arg(dirPath);
if(!dir.mkpath(dirPath)) {
qDebug() << QString("Failed to create directory: %1").arg(dirPath);
return false;
}
qDebug() << QString("Directory created successfully: %1").arg(dirPath);
} else {
qDebug() << QString("Directory already exists: %1").arg(dirPath);
}
return true;
}
void nmDataAnalyzeManager::loadWellPreAndFlow()
{
// 获取当前工区里所有的ZxDataWell井
if(zxCurProject == nullptr) {
return;
}
ZxDataObjectList wellList = zxCurProject->getChildren(iDataModelType::sTypeWell);
foreach(nmDataWellBase* pWell, m_vWellData) {
if(pWell == nullptr) {
continue;
}
QString sWellCode = pWell->getWellCode();
ZxDataWell* pWellData = nullptr;
for(int i = 0; i < wellList.size(); i++) {
ZxDataWell* pCandidateWell = dynamic_cast<ZxDataWell*>(wellList[i]);
if(pCandidateWell != nullptr &&
pCandidateWell->getCode() == sWellCode) {
pWellData = pCandidateWell;
break;
}
}
// 拿到了井的数据
if(pWellData) {
// 获取当前井的压力、流量数据
ZxDataObjectList m_listGaugeP = pWellData->getChildren(iDataModelType::sTypeDataGaugeP);
ZxDataObjectList m_listGaugeF = pWellData->getChildren(iDataModelType::sTypeDataGaugeF);
ZxDataGaugeP* pGaugeP = nullptr;
ZxDataGaugeF* pGaugeF = nullptr;
// 遍历压力数据列表
for(int i = 0; i < m_listGaugeP.size(); ++i) {
if(pGaugeP = dynamic_cast<ZxDataGaugeP * >(m_listGaugeP[i])) { // 拿到第一条压力数据
break;
}
}
// 遍历流量数据列表
for(int i = 0; i < m_listGaugeF.size(); ++i) {
if(pGaugeF = dynamic_cast<ZxDataGaugeF * >(m_listGaugeF[i])) { // 拿到第一条流量数据
break;
}
}
// 获取的压力、流量数据
QVector<QPointF> vecPtsP, vecPtsF;
// 临时存储xy坐标
VecDouble vecX, vecY;
if(pGaugeP != nullptr) {
// 获取压力数据
if(pGaugeP->getDataVecXY(vecX, vecY)) {
int pointCount = vecX.size();
if(pointCount > vecY.size()) {
pointCount = vecY.size();
}
for(int i = 0; i < pointCount; ++i) {
QPointF pt(vecX[i], vecY[i]);
vecPtsP.append(pt);
}
}
}
if(pGaugeF != nullptr) {
vecX.clear();
vecY.clear();
// 获取流量数据
if(pGaugeF->getDataVecXY(vecX, vecY)) {
int pointCount = vecX.size();
if(pointCount > vecY.size()) {
pointCount = vecY.size();
}
for(int i = 0; i < pointCount; ++i) {
QPointF pt(vecX[i], vecY[i]);
vecPtsF.append(pt);
}
}
}
// 设置井的压力数据、流量数据
pWell->setPressurePoints(vecPtsP);
pWell->setFlowPoints(vecPtsF);
}
}
}
vtkSmartPointer<vtkUnstructuredGrid> nmDataAnalyzeManager::getUnstructuredGrid() const
{
return m_pVtkUnstructuredGrid;
}
void nmDataAnalyzeManager::setUnstructuredGrid(vtkSmartPointer<vtkUnstructuredGrid> grid)
{
m_pVtkUnstructuredGrid = grid;
}
void nmDataAnalyzeManager::clearUnstructuredGrid()
{
// 释放当前分析持有的实时VTK网格对象关闭网格窗口后不再保留旧网格。
m_pVtkUnstructuredGrid = nullptr;
}
vtkSmartPointer<vtkUnstructuredGrid> nmDataAnalyzeManager::getUnstructuredGridCopy() const
{
vtkSmartPointer<vtkUnstructuredGrid> pCopyGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();
if(m_pVtkUnstructuredGrid) {
// 执行深拷贝:复制网格的所有几何和拓扑信息
pCopyGrid->DeepCopy(m_pVtkUnstructuredGrid);
}
return pCopyGrid;
}
vtkSmartPointer<vtkUnstructuredGrid> nmDataAnalyzeManager::getResultBaseGrid() const
{
return m_pResultBaseGrid;
}
void nmDataAnalyzeManager::setResultBaseGrid(vtkSmartPointer<vtkUnstructuredGrid> grid)
{
m_pResultBaseGrid = grid;
}
vtkSmartPointer<vtkUnstructuredGrid> nmDataAnalyzeManager::getResultBaseGridCopy() const
{
vtkSmartPointer<vtkUnstructuredGrid> pCopyGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();
if(m_pResultBaseGrid) {
// 执行深拷贝:复制网格的所有几何和拓扑信息
pCopyGrid->DeepCopy(m_pResultBaseGrid);
}
return pCopyGrid;
}
bool nmDataAnalyzeManager::writeUnstructuredGridToFile(vtkSmartPointer<vtkUnstructuredGrid> grid, const QString& filePath)
{
// 检查是否存在有效的网格数据
if(!grid) {
return false;
}
// 根据文件扩展名决定使用哪种格式
if(filePath.endsWith(".vtu", Qt::CaseInsensitive)) {
// 使用XML格式推荐格式
vtkNew<vtkXMLUnstructuredGridWriter> writer;
writer->SetInputData(grid); // 设置输入数据
writer->SetFileName(filePath.toStdString().c_str()); // 设置输出文件名
writer->SetDataModeToBinary(); // 设置为二进制模式也可以用SetDataModeToAscii()设为文本模式
writer->Write(); // 执行写入操作
} else if(filePath.endsWith(".vtk", Qt::CaseInsensitive)) {
// 使用传统的VTK格式
vtkNew<vtkUnstructuredGridWriter> writer;
writer->SetInputData(grid); // 设置输入数据
writer->SetFileName(filePath.toStdString().c_str()); // 设置输出文件名
writer->SetFileTypeToBinary(); // 设置为二进制模式也可以用SetFileTypeToASCII()设为文本模式
writer->Write(); // 执行写入操作
} else {
return false; // 不支持的文件扩展名
}
return true; // 写入成功
}
bool nmDataAnalyzeManager::readUnstructuredGridFromFile(vtkSmartPointer<vtkUnstructuredGrid>& grid, const QString& filePath)
{
// 检查文件是否存在
if(!QFile::exists(filePath)) {
return false;
}
// 根据文件扩展名选择不同的读取器
if(filePath.endsWith(".vtu", Qt::CaseInsensitive)) {
// 使用XML格式读取器
vtkNew<vtkXMLUnstructuredGridReader> reader;
reader->SetFileName(filePath.toStdString().c_str()); // 设置输入文件名
reader->Update(); // 执行读取操作
grid = reader->GetOutput(); // 获取读取的数据
} else if(filePath.endsWith(".vtk", Qt::CaseInsensitive)) {
// 使用传统VTK格式读取器
vtkNew<vtkUnstructuredGridReader> reader;
reader->SetFileName(filePath.toStdString().c_str()); // 设置输入文件名
reader->Update(); // 执行读取操作
grid = reader->GetOutput(); // 获取读取的数据
} else {
return false; // 不支持的文件扩展名
}
// 检查是否成功读取了有效的网格数据
if(grid && grid->GetNumberOfPoints() > 0) {
return true;
}
return false; // 读取失败
}
void nmDataAnalyzeManager::addTimeStep(double time, vtkSmartPointer<vtkDoubleArray> data)
{
if(!data || data->GetNumberOfTuples() == 0) return;
m_mapTimeStepDataP.insert(time, data);
}
void nmDataAnalyzeManager::clearTimeSteps()
{
m_mapTimeStepDataP.clear();
m_dScalarRangeP[0] = DBL_MAX;
m_dScalarRangeP[1] = -DBL_MAX;
}
vtkSmartPointer<vtkDoubleArray> nmDataAnalyzeManager::getTimeStepData(double time) const
{
if(m_mapTimeStepDataP.contains(time)) {
return m_mapTimeStepDataP.value(time);
}
return nullptr;
}
QList<double> nmDataAnalyzeManager::getTimeStepKeys()
{
return m_mapTimeStepDataP.keys();
}
bool nmDataAnalyzeManager::isTimeStepDataEmpty()
{
return m_mapTimeStepDataP.isEmpty();
}
/**
* @brief 辅助函数将整个时间步数据QMap保存到单个二进制文件。
* 文件中依次存储QMap大小、每个时间键、每个vtkDoubleArray的大小、每个vtkDoubleArray的数据。
* @param filePath 文件保存的完整路径。
* @return 写入成功返回true否则返回false。
*/
bool nmDataAnalyzeManager::writeTimeStepDataMapToBinaryFile(const QString& filePath)
{
QFile file(filePath);
// 使用 Truncate 模式确保如果文件已存在,会被清空并覆盖
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qDebug() << QString("Failed to open file for writing (TimeStepDataMap): %1").arg(filePath);
return false;
}
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_8); // 当前项目Qt版本
// 1. 写入 QMap 的元素数量
out << (qint32)m_mapTimeStepDataP.size();
// 2. 遍历 QMap依次写入每个时间键和对应的 vtkDoubleArray 数据
for(auto it = m_mapTimeStepDataP.constBegin(); it != m_mapTimeStepDataP.constEnd(); ++it) {
double time = it.key();
vtkSmartPointer<vtkDoubleArray> data = it.value();
if(!qIsFinite(time) || !data || data->GetNumberOfTuples() <= 0) {
file.close();
QFile::remove(filePath);
return false;
}
// 写入时间键
out << time;
// 写入 vtkDoubleArray 的元素数量
qint64 numberOfTuples = data ? data->GetNumberOfTuples() : 0;
out << numberOfTuples;
// 写入 vtkDoubleArray 的数据
if(data && numberOfTuples > 0) {
for(qint64 i = 0; i < numberOfTuples; ++i) {
out << data->GetValue(i);
}
}
}
const bool bWriteOk = out.status() == QDataStream::Ok && file.flush();
file.close();
if(!bWriteOk) {
QFile::remove(filePath);
}
return bWriteOk;
}
/**
* @brief 辅助函数从单个二进制文件加载整个时间步数据QMap。
* @param filePath 文件所在的完整路径。
* @return 加载成功返回true否则返回false。
*/
bool nmDataAnalyzeManager::readTimeStepDataMapFromBinaryFile(const QString& filePath)
{
QFile file(filePath);
if(!file.open(QIODevice::ReadOnly)) {
qDebug() << QString("Failed to open file for reading (TimeStepDataMap): %1").arg(filePath);
return false;
}
QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_8); // 必须与写入时的版本一致
// 1. 读取 QMap 的元素数量
qint32 mapSize;
in >> mapSize;
const qint64 nFileSize = file.size();
if(in.status() != QDataStream::Ok || mapSize <= 0 ||
mapSize > nFileSize / static_cast<qint64>(sizeof(double) + sizeof(qint64))) {
qDebug() << QString("Invalid map size read from binary file: %1").arg(filePath);
file.close();
return false;
}
QMap<double, vtkSmartPointer<vtkDoubleArray> > mapLoadedTimeSteps;
double dLoadedMinP = DBL_MAX;
double dLoadedMaxP = -DBL_MAX;
// 2. 循环读取每个时间键和对应的 vtkDoubleArray 数据
for(qint32 k = 0; k < mapSize; ++k) {
double time;
in >> time; // 读取时间键
qint64 numberOfTuples;
in >> numberOfTuples; // 读取 vtkDoubleArray 的元素数量
// 第一步:每一帧必须有有限时间、非空压力数组,且声明长度不能超过文件容量。
if(in.status() != QDataStream::Ok || !qIsFinite(time) ||
mapLoadedTimeSteps.contains(time) || numberOfTuples <= 0 ||
numberOfTuples > nFileSize / static_cast<qint64>(sizeof(double))) {
file.close();
return false;
}
vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
data->SetNumberOfComponents(1); // 假设是单分量数组
data->SetNumberOfTuples(numberOfTuples); // 设置数组大小
// 设置数组名称
data->SetName("p");
if(numberOfTuples > 0) {
double currentMin = DBL_MAX;
double currentMax = -DBL_MAX;
for(qint64 i = 0; i < numberOfTuples; ++i) {
double value;
in >> value;
if(in.status() != QDataStream::Ok || !qIsFinite(value)) {
file.close();
return false;
}
data->SetValue(i, value);
// 更新当前时间步的范围
currentMin = qMin(currentMin, value);
currentMax = qMax(currentMax, value);
}
// 更新全局范围
dLoadedMinP = qMin(dLoadedMinP, currentMin);
dLoadedMaxP = qMax(dLoadedMaxP, currentMax);
}
mapLoadedTimeSteps.insert(time, data);
}
// 第二步:完整消费文件后再一次性提交,损坏文件不会留下部分场结果。
if(in.status() != QDataStream::Ok || !file.atEnd() ||
mapLoadedTimeSteps.size() != mapSize) {
file.close();
return false;
}
m_mapTimeStepDataP = mapLoadedTimeSteps;
m_dScalarRangeP[0] = dLoadedMinP;
m_dScalarRangeP[1] = dLoadedMaxP;
file.close();
return true;
}
bool nmDataAnalyzeManager::saveWellHistoryData(QString sDir, nmDataWellBase* pWellData)
{
Q_ASSERT(nullptr != pWellData);
if(nullptr == pWellData) {
return false;
}
// 文件名使用 WellCode井改名不会造成历史数据丢失。
QString sWellCode = pWellData->getWellCode();
if(sWellCode.isEmpty()) {
return false;
}
QString filePath = sDir + "/" + sWellCode + "_History.bin";
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly)) {
return false;
}
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_8);
// 只写入历史数据
out << pWellData->getHistoryPressure();
out << pWellData->getHistoryLogLog();
out << pWellData->getHistorySemiLog();
const bool bWriteOk = out.status() == QDataStream::Ok && file.flush();
file.close();
if(!bWriteOk) {
QFile::remove(filePath);
}
return bWriteOk;
}
bool nmDataAnalyzeManager::loadWellHistoryData(QString sDir, nmDataWellBase* pWellData)
{
Q_ASSERT(nullptr != pWellData);
if(nullptr == pWellData) {
return false;
}
QString sWellCode = pWellData->getWellCode();
if(sWellCode.isEmpty()) {
return false;
}
QString filePath = sDir + "/" + sWellCode + "_History.bin";
QFile file(filePath);
if(!file.open(QIODevice::ReadOnly)) {
return false;
}
QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_8);
// 只需要读取历史数据
QVector<QVector<double>> historyPressureData;
QVector<QVector<double>> historyLoglogData;
QVector<QVector<double>> historySemiLogData;
// 只读取历史数据
in >> historyPressureData;
in >> historyLoglogData;
in >> historySemiLogData;
// 第一步:先验证整个数据流,再修改井对象,避免截断文件写入半成品数据。
if(in.status() != QDataStream::Ok || !file.atEnd()) {
file.close();
return false;
}
// 第二步:文件完整时一次性回填三类历史曲线。
pWellData->setHistoryPressure(historyPressureData);
pWellData->setHistoryLogLog(historyLoglogData);
pWellData->setHistorySemiLog(historySemiLogData);
file.close();
return true;
}
bool nmDataAnalyzeManager::saveWellCalRstData(QString sDir, nmDataWellBase* pWellData)
{
Q_ASSERT(nullptr != pWellData);
if(nullptr == pWellData) {
return false;
}
// 构建文件路径,为每口井的结果创建一个独立文件
QString sWellCode = pWellData->getWellCode();
if(sWellCode.isEmpty()) {
return false;
}
QString filePath = sDir + "/" + sWellCode + "_Results.bin";
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly)) {
return false;
}
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_8); // 设置数据流版本,保证未来兼容性
// 写入数据
// QDataStream 可以直接序列化 QVector<QVector<double>>
out << pWellData->getResultPressure();
out << pWellData->getResultLogLog();
out << pWellData->getResultSemiLog();
const bool bWriteOk = out.status() == QDataStream::Ok && file.flush();
file.close();
if(!bWriteOk) {
QFile::remove(filePath);
}
return bWriteOk;
}
bool nmDataAnalyzeManager::loadWellCalRstData(QString sDir, nmDataWellBase* pWellData)
{
Q_ASSERT(nullptr != pWellData);
if(nullptr == pWellData) {
return false;
}
QString sWellCode = pWellData->getWellCode();
if(sWellCode.isEmpty()) {
return false;
}
QString filePath = sDir + "/" + sWellCode + "_Results.bin";
QFile file(filePath);
if(!file.open(QIODevice::ReadOnly)) {
return false;
}
QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_8); // 确保与写入时版本一致
// 读取数据到 pWellData 的成员变量
QVector<QVector<double>> pressureData;
QVector<QVector<double>> loglogData;
QVector<QVector<double>> semiLogData;
in >> pressureData;
in >> loglogData;
in >> semiLogData;
// 第一步:先验证整个数据流,再修改井对象,避免损坏文件生成部分结果。
if(in.status() != QDataStream::Ok || !file.atEnd()) {
file.close();
return false;
}
// 第二步:主动井可包含三类曲线;观察井正常情况下只有压力数据。
pWellData->setResultPressure(pressureData);
pWellData->setResultLogLog(loglogData);
pWellData->setResultSemiLog(semiLogData);
//// 存储当前井名称和二维位置到映射
//QPointF ptWellCoords(pWellData->getX().getValue().toDouble(), pWellData->getY().getValue().toDouble());
//addWellLocation(sWellName, ptWellCoords);
file.close();
return true;
}
void nmDataAnalyzeManager::getScalarRangeP(double range[2]) const
{
range[0] = m_dScalarRangeP[0];
range[1] = m_dScalarRangeP[1];
}
void nmDataAnalyzeManager::setScalarRangeP(double min, double max)
{
m_dScalarRangeP[0] = min;
m_dScalarRangeP[1] = max;
}
void nmDataAnalyzeManager::addWellLocation(const QString& sWellCode, const QPointF& location)
{
m_mapWellLocations.insert(sWellCode, location);
}
QPointF nmDataAnalyzeManager::getWellLocation(const QString& sWellCode) const
{
return m_mapWellLocations.value(sWellCode, QPointF());
}
bool nmDataAnalyzeManager::removeWellLocation(const QString& sWellCode)
{
return m_mapWellLocations.remove(sWellCode) > 0;
}
void nmDataAnalyzeManager::clearWellLocations()
{
m_mapWellLocations.clear();
}
QMap<QString, QPointF> nmDataAnalyzeManager::getAllWellLocations() const
{
return m_mapWellLocations;
}
bool nmDataAnalyzeManager::saveWellLocations(const QString& filePath)
{
// 第一步:结果位置必须逐口覆盖求解器中的真实井,手工裂缝不对应 Map 井。
QSet<QString> setExpectedWellCodes;
QVector<nmSolverWellRef> vecSolverOrder = getSolverWellOrder();
for(int nIndex = 0; nIndex < vecSolverOrder.size(); ++nIndex) {
const nmSolverWellRef& oWellRef = vecSolverOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
if(oWellRef.m_sWellCode.isEmpty() ||
setExpectedWellCodes.contains(oWellRef.m_sWellCode)) {
return false;
}
setExpectedWellCodes.insert(oWellRef.m_sWellCode);
}
}
QSet<QString> setStoredWellCodes;
QMap<QString, QPointF>::const_iterator itLocation =
m_mapWellLocations.constBegin();
for(; itLocation != m_mapWellLocations.constEnd(); ++itLocation) {
if(itLocation.key().isEmpty() ||
!qIsFinite(itLocation.value().x()) ||
!qIsFinite(itLocation.value().y())) {
return false;
}
setStoredWellCodes.insert(itLocation.key());
}
if(setExpectedWellCodes.isEmpty() ||
setStoredWellCodes != setExpectedWellCodes) {
return false;
}
// 第二步:身份和坐标完整后再写文件,写入失败时删除半成品。
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly)) {
return false;
}
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_8);
qDebug() << "Map size before writting:" << m_mapWellLocations.size();
out << m_mapWellLocations;
const bool bWriteOk = out.status() == QDataStream::Ok && file.flush();
file.close();
if(!bWriteOk) {
QFile::remove(filePath);
}
return bWriteOk;
}
bool nmDataAnalyzeManager::loadWellLocations(const QString& filePath)
{
QFile file(filePath);
if(!file.open(QIODevice::ReadOnly)) {
return false;
}
QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_8);
// 第一步:先读入局部对象,损坏文件不能覆盖当前内存中的有效位置。
QMap<QString, QPointF> mapLoadedWellLocations;
in >> mapLoadedWellLocations;
if(in.status() != QDataStream::Ok || !file.atEnd()) {
file.close();
return false;
}
// 第二步:加载结果必须与 SolverWellOrder 中的真实井 WellCode 完整对应。
QSet<QString> setExpectedWellCodes;
QVector<nmSolverWellRef> vecSolverOrder = getSolverWellOrder();
for(int nIndex = 0; nIndex < vecSolverOrder.size(); ++nIndex) {
const nmSolverWellRef& oWellRef = vecSolverOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
if(oWellRef.m_sWellCode.isEmpty() ||
setExpectedWellCodes.contains(oWellRef.m_sWellCode)) {
file.close();
return false;
}
setExpectedWellCodes.insert(oWellRef.m_sWellCode);
}
}
QSet<QString> setLoadedWellCodes;
QMap<QString, QPointF>::const_iterator itLocation =
mapLoadedWellLocations.constBegin();
for(; itLocation != mapLoadedWellLocations.constEnd(); ++itLocation) {
if(itLocation.key().isEmpty() ||
!qIsFinite(itLocation.value().x()) ||
!qIsFinite(itLocation.value().y())) {
file.close();
return false;
}
setLoadedWellCodes.insert(itLocation.key());
}
if(setExpectedWellCodes.isEmpty() ||
setLoadedWellCodes != setExpectedWellCodes) {
file.close();
return false;
}
// 第三步:全部校验通过后一次性提交。
m_mapWellLocations = mapLoadedWellLocations;
qDebug() << "Map size after reading:" << m_mapWellLocations.size();
file.close();
return true;
}
//void nmDataAnalyzeManager::createTimeStep()
//{
// if (m_pTimeStep != nullptr)
// {
// delete m_pTimeStep;
// m_pTimeStep = nullptr;
// }
//
// // 获取当前井下流量的时间范围
// QVector<QPointF> vecFlowPoints;
// if (m_pCurDataWell){
// vecFlowPoints = m_pCurDataWell->getFlowPoints();
// }
//
// // 计算起始时间和终止时间
// // 检查坐标数组是否为空,以防止访问越界
// if (!vecFlowPoints.isEmpty()) {
// // 起始时间就是第一个点的横坐标
// double dStartTime = vecFlowPoints.first().x();
//
// // 终止时间是所有点的横坐标之和
// double dEndTime = 0.0;
// foreach (const QPointF& point, vecFlowPoints) {
// dEndTime += point.x();
// }
//
// m_pTimeStep = new nmDataTimeStepSetting(dStartTime,dEndTime);
// }
//}
nmDataTimeStepSetting* nmDataAnalyzeManager::createTimeStep()
{
if (m_pTimeStep != nullptr)
{
delete m_pTimeStep;
m_pTimeStep = nullptr;
}
m_pTimeStep = new nmDataTimeStepSetting;
return m_pTimeStep;
}
nmDataTimeStepSetting* nmDataAnalyzeManager::getTimeStep()
{
return m_pTimeStep;
}
// 获取许可证路径
void nmDataAnalyzeManager::setLicensePath(const QString& licensePath)
{
m_licensePath = licensePath;
}
QString nmDataAnalyzeManager::getLicensePath() const
{
return m_licensePath;
}