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

6196 lines
216 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 "nmPebiResultSnapshot.h"
#include "nmPebiResultSnapshotSerializer.h"
#include "nmNumericalSaveSession.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 <QThread>
#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 <new>
#include <vector>
#include "singlePhaseSolver.h"
#include "nmDataTimeStepSetting.h"
#include <QByteArray>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include <QUuid>
// 井基参数名 → 数据成员访问器的映射描述
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);
}
rapidjson::Value numericalJsonString(
const QString& sValue,
rapidjson::Document::AllocatorType& oAllocator)
{
const QByteArray baValue = sValue.toUtf8();
return rapidjson::Value(baValue.constData(),
static_cast<rapidjson::SizeType>(baValue.size()), oAllocator);
}
void addNumericalFileReference(
rapidjson::Value& oParent,
const char* pName,
const nmNumericalFileReference& oReference,
rapidjson::Document::AllocatorType& oAllocator)
{
rapidjson::Value oJson(rapidjson::kObjectType);
oJson.AddMember("Path", numericalJsonString(
oReference.m_sRelativePath, oAllocator), oAllocator);
oJson.AddMember("Length", oReference.m_nLength, oAllocator);
oJson.AddMember("Sha1", numericalJsonString(
oReference.m_sSha1, oAllocator), oAllocator);
oParent.AddMember(rapidjson::Value(pName, oAllocator).Move(),
oJson, oAllocator);
}
bool readNumericalFileReference(
const rapidjson::Value& oParent,
const char* pName,
nmNumericalFileReference& oReference)
{
if(!oParent.IsObject() || !oParent.HasMember(pName) ||
!oParent[pName].IsObject())
{
return false;
}
const rapidjson::Value& oJson = oParent[pName];
if(!oJson.HasMember("Path") || !oJson["Path"].IsString() ||
!oJson.HasMember("Length") || !oJson["Length"].IsUint64() ||
!oJson.HasMember("Sha1") || !oJson["Sha1"].IsString())
{
return false;
}
oReference.m_sRelativePath = QString::fromUtf8(oJson["Path"].GetString());
oReference.m_nLength = oJson["Length"].GetUint64();
oReference.m_sSha1 = QString::fromLatin1(oJson["Sha1"].GetString());
return oReference.isValid();
}
bool readV3WindowHeader(
const QString& sMainJsonPath,
nmNumericalWindowPayloadReferences& oReferences,
QString& sCurrentResultWellInstanceId,
quint64& nGridInputRevision,
quint64& nBuiltGridRevision,
quint64& nResultInputRevision,
QString* pError)
{
const QFileInfo oInfo(sMainJsonPath);
if(!oInfo.isFile() || oInfo.size() <= 0 ||
oInfo.size() > 64ll * 1024ll * 1024ll)
{
if(pError != NULL) *pError = "Numerical v3 main JSON is missing or too large.";
return false;
}
rapidjson::Document oDocument;
if(!nmDataJsonTools::ReadDomFromFile(sMainJsonPath, oDocument) ||
!oDocument.IsObject() ||
!oDocument.HasMember("NumericalProjectVersion") ||
!oDocument["NumericalProjectVersion"].IsInt())
{
if(pError != NULL) *pError = "Numerical project version is missing.";
return false;
}
const int nVersion = oDocument["NumericalProjectVersion"].GetInt();
if(nVersion != nmNumericalResultPersistence::projectVersion())
{
if(pError != NULL)
{
*pError = nVersion == 2
? "Numerical project version 2 is not supported."
: "Numerical project version is not supported.";
}
return false;
}
if(!oDocument.HasMember("InputRevisions") ||
!oDocument["InputRevisions"].IsObject() ||
!oDocument.HasMember("CurrentResultWellInstanceId") ||
!oDocument["CurrentResultWellInstanceId"].IsString() ||
!oDocument.HasMember("PayloadFiles") ||
!oDocument["PayloadFiles"].IsObject())
{
if(pError != NULL) *pError = "Numerical v3 main JSON header is incomplete.";
return false;
}
const rapidjson::Value& oRevisions = oDocument["InputRevisions"];
if(!oRevisions.HasMember("GridInputRevision") ||
!oRevisions["GridInputRevision"].IsUint64() ||
!oRevisions.HasMember("BuiltGridRevision") ||
!oRevisions["BuiltGridRevision"].IsUint64() ||
!oRevisions.HasMember("ResultInputRevision") ||
!oRevisions["ResultInputRevision"].IsUint64())
{
if(pError != NULL) *pError = "Numerical input revisions are invalid.";
return false;
}
nGridInputRevision = oRevisions["GridInputRevision"].GetUint64();
nBuiltGridRevision = oRevisions["BuiltGridRevision"].GetUint64();
nResultInputRevision = oRevisions["ResultInputRevision"].GetUint64();
if(nGridInputRevision == 0 || nResultInputRevision == 0 ||
(nBuiltGridRevision != 0 && nBuiltGridRevision != nGridInputRevision))
{
if(pError != NULL) *pError = "Numerical input revision values are invalid.";
return false;
}
sCurrentResultWellInstanceId = QString::fromUtf8(
oDocument["CurrentResultWellInstanceId"].GetString());
const rapidjson::Value& oPayload = oDocument["PayloadFiles"];
if(!oPayload.HasMember("CurrentGrid") ||
!oPayload.HasMember("WellHistories") ||
!oPayload["WellHistories"].IsArray() ||
oPayload["WellHistories"].Size() > 10000u ||
!oPayload.HasMember("Snapshot"))
{
if(pError != NULL) *pError = "Numerical payload reference list is invalid.";
return false;
}
QSet<QString> setPaths;
oReferences = nmNumericalWindowPayloadReferences();
if(!oPayload["CurrentGrid"].IsNull())
{
if(!readNumericalFileReference(oPayload, "CurrentGrid",
oReferences.m_oCurrentGrid))
{
if(pError != NULL) *pError = "Current grid reference is invalid.";
return false;
}
oReferences.m_bHasCurrentGrid = true;
setPaths.insert(oReferences.m_oCurrentGrid.m_sRelativePath);
}
const rapidjson::Value& oHistories = oPayload["WellHistories"];
for(rapidjson::SizeType nIndex = 0; nIndex < oHistories.Size(); ++nIndex)
{
const rapidjson::Value& oJson = oHistories[nIndex];
nmNumericalFileReference oReference;
if(!oJson.IsObject() ||
!oJson.HasMember("WellInstanceId") ||
!oJson["WellInstanceId"].IsString() ||
!readNumericalFileReference(oJson, "File", oReference))
{
if(pError != NULL) *pError = "Well history reference is invalid.";
return false;
}
const QString sWellInstanceId = QString::fromUtf8(
oJson["WellInstanceId"].GetString());
if(sWellInstanceId.isEmpty() ||
oReferences.m_mapWellHistories.contains(sWellInstanceId) ||
setPaths.contains(oReference.m_sRelativePath))
{
if(pError != NULL) *pError = "Well history references are duplicated.";
return false;
}
oReferences.m_mapWellHistories.insert(sWellInstanceId, oReference);
setPaths.insert(oReference.m_sRelativePath);
}
if(!oPayload["Snapshot"].IsNull())
{
if(!readNumericalFileReference(oPayload, "Snapshot",
oReferences.m_oSnapshot) ||
setPaths.contains(oReferences.m_oSnapshot.m_sRelativePath))
{
if(pError != NULL) *pError = "Snapshot reference is invalid or duplicated.";
return false;
}
oReferences.m_bHasSnapshot = true;
}
return true;
}
}
ZX_DEFINE_DYNAMIC(DataAnalyzeManager, nmDataAnalyzeManager)
nmDataAnalyzeManager::nmDataAnalyzeManager(): ZxDataObjectBin(0)
{
m_pOwnerFitting = nullptr;
m_nBackgroundUseCount = 0;
m_bPebiGridGenerationActive = false;
m_nPebiGridGenerationRevision = 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;
// 初始化混合参数数据(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;
}
bool nmDataAnalyzeManager::isPrimaryWell(
const nmDataWellBase* pWellData) const
{
if(pWellData == nullptr) {
return false;
}
const QString sPrimaryWellCode =
m_oNumericalAnalysisCase.getPrimaryWellCode();
return !sPrimaryWellCode.isEmpty() &&
pWellData->getWellCode() == sPrimaryWellCode;
}
bool nmDataAnalyzeManager::canRemoveWell(
const nmDataWellBase* pWellData) const
{
if(pWellData == nullptr || isPrimaryWell(pWellData)) {
return false;
}
// 不通过 WellCode 代替所有权判断,避免同编码外部对象误删管理器内的井。
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
if(m_vWellData[nIndex] == pWellData) {
return true;
}
}
return false;
}
bool nmDataAnalyzeManager::removeWell(nmDataWellBase* pWellData)
{
// 固定主井及非法指针必须在任何版本递增、图元解绑和对象删除前拒绝。
// 该检查是数据层最终兜底,不能只依赖 Map 界面的提前拦截。
if(!canRemoveWell(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(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(!canRemoveWell(pWellData)) {
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;
// 全量清空表示当前分析上下文被丢弃;先发出快照清除通知,
// 此时实时井对象仍然有效,直接连接的 UI 刷新不会遇到悬空井指针。
clearPebiResultSnapshot();
// 遍历并删除所有井对象,兼容旧逻辑可能遗留的重复指针。
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)
{
bool bWellUpdated = false;
foreach(const auto& newWell, wells) {
const QString sWellInstanceId = newWell.getWellInstanceId();
if(sWellInstanceId.isEmpty()) {
qWarning() << "Cannot update vertical well without UUID:"
<< newWell.getWellName();
continue;
}
foreach(auto* existingWell, m_vWellData) {
if(auto * vWell = dynamic_cast<nmDataVerticalWell * >(existingWell)) {
// UUID 是井实例的唯一身份,禁止同名新井覆盖旧井。
if(vWell->getWellInstanceId() == sWellInstanceId) {
*vWell = newWell; // 调用赋值运算符
bWellUpdated = true;
break;
}
}
}
}
// 井赋值不复制快照绑定,并会解除目标井的弱引用,回写后必须由 Manager 统一恢复。
if(bWellUpdated) {
rebindPebiResultSnapshotToWells();
}
}
void nmDataAnalyzeManager::updateVerticalFracturedWells(const QVector<nmDataVerticalFracturedWell>& wells)
{
bool bWellUpdated = false;
foreach(const auto& newWell, wells) {
const QString sWellInstanceId = newWell.getWellInstanceId();
if(sWellInstanceId.isEmpty()) {
qWarning() << "Cannot update vertical fractured well without UUID:"
<< newWell.getWellName();
continue;
}
foreach(auto* existingWell, m_vWellData) {
if(auto * vWell = dynamic_cast<nmDataVerticalFracturedWell * >(existingWell)) {
// UUID 是井实例的唯一身份,禁止同名新井覆盖旧井。
if(vWell->getWellInstanceId() == sWellInstanceId) {
*vWell = newWell; // 调用赋值运算符
bWellUpdated = true;
break;
}
}
}
}
// 井赋值不复制快照绑定,并会解除目标井的弱引用,回写后必须由 Manager 统一恢复。
if(bWellUpdated) {
rebindPebiResultSnapshotToWells();
}
}
void nmDataAnalyzeManager::updateHorizontalFracturedWells(const QVector<nmDataHorizontalFracturedWell>& wells)
{
bool bWellUpdated = false;
foreach(const auto& newWell, wells) {
const QString sWellInstanceId = newWell.getWellInstanceId();
if(sWellInstanceId.isEmpty()) {
qWarning() << "Cannot update horizontal fractured well without UUID:"
<< newWell.getWellName();
continue;
}
foreach(auto* existingWell, m_vWellData) {
if(auto * vWell = dynamic_cast<nmDataHorizontalFracturedWell * >(existingWell)) {
// UUID 是井实例的唯一身份,禁止同名新井覆盖旧井。
if(vWell->getWellInstanceId() == sWellInstanceId) {
*vWell = newWell; // 调用赋值运算符
bWellUpdated = true;
break;
}
}
}
}
// 井赋值不复制快照绑定,并会解除目标井的弱引用,回写后必须由 Manager 统一恢复。
if(bWellUpdated) {
rebindPebiResultSnapshotToWells();
}
}
// 获取所有直井数据
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;
}
nmDataWellBase* nmDataAnalyzeManager::findWellByInstanceId(
const QString& sWellInstanceId) const
{
if(sWellInstanceId.isEmpty()) {
return nullptr;
}
foreach(nmDataWellBase* pWell, m_vWellData) {
if(pWell != nullptr &&
pWell->getWellInstanceId() == sWellInstanceId) {
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_oNumericalAnalysisCase.getPrimaryWellCode().isEmpty() &&
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);
}
}
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() ||
pOldWell->getWellInstanceId().isEmpty() ||
pOldWell->getWellInstanceId() !=
pNewWell->getWellInstanceId())
{
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;
// 同 UUID 井型替换后,新对象必须重新获得最后结果的弱引用。
rebindPebiResultSnapshotToWells();
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.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()) {
// 开关只改变当前求解输入。最后结果下拉框继续使用快照冻结的 UUID 列表。
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) {
// 第三步:这里只通知当前输入变化,不修改独立快照中的结果井选择。
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;
}
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::beginPebiGridGeneration(
quint64 nGridInputRevision)
{
if(nGridInputRevision == 0) {
return;
}
// 该状态只协调同一主线程上的窗口流程;后台任务不得直接读写 DataManager。
m_bPebiGridGenerationActive = true;
m_nPebiGridGenerationRevision = nGridInputRevision;
}
void nmDataAnalyzeManager::endPebiGridGeneration(bool bSucceeded)
{
if(!m_bPebiGridGenerationActive) {
return;
}
const quint64 nGridInputRevision = m_nPebiGridGenerationRevision;
m_bPebiGridGenerationActive = false;
m_nPebiGridGenerationRevision = 0;
emit sigPebiGridGenerationFinished(nGridInputRevision, bSucceeded);
}
bool nmDataAnalyzeManager::isPebiGridGenerationActive() const
{
return m_bPebiGridGenerationActive;
}
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();
}
bool nmDataAnalyzeManager::isPebiGridValid() const
{
return m_oNumericalAnalysisCase.isGridValid();
}
bool nmDataAnalyzeManager::hasPebiResults() const
{
// 结果存在性只有一个事实来源Manager 是否持有完整只读快照。
return !m_pPebiResultSnapshot.isNull();
}
QSharedPointer<const nmPebiResultSnapshot>
nmDataAnalyzeManager::getPebiResultSnapshot() const
{
return m_pPebiResultSnapshot;
}
bool nmDataAnalyzeManager::hasPebiResultSnapshot() const
{
return !m_pPebiResultSnapshot.isNull();
}
bool nmDataAnalyzeManager::isPebiResultSnapshotCurrent() const
{
return !m_pPebiResultSnapshot.isNull() &&
m_pPebiResultSnapshot->getGridInputRevision() ==
m_oNumericalAnalysisCase.getGridInputRevision() &&
m_pPebiResultSnapshot->getResultInputRevision() ==
m_oNumericalAnalysisCase.getResultInputRevision();
}
QString nmDataAnalyzeManager::getCurrentResultWellInstanceId() const
{
return m_sCurrentResultWellInstanceId;
}
bool nmDataAnalyzeManager::setCurrentResultWellInstanceId(
const QString& sWellInstanceId)
{
// 结果井选择属于快照查看状态,不能反向修改当前分析主井。
if(m_pPebiResultSnapshot.isNull())
{
if(!sWellInstanceId.isEmpty())
{
return false;
}
m_sCurrentResultWellInstanceId.clear();
return true;
}
QString sValidatedWellInstanceId = sWellInstanceId;
if(sValidatedWellInstanceId.isEmpty())
{
const QStringList& listDisplayWellIds =
m_pPebiResultSnapshot->getDisplayWellInstanceIds();
sValidatedWellInstanceId = listDisplayWellIds.isEmpty()
? QString() : listDisplayWellIds.first();
}
if(!sValidatedWellInstanceId.isEmpty() &&
!m_pPebiResultSnapshot->isDisplayWell(sValidatedWellInstanceId))
{
return false;
}
if(m_sCurrentResultWellInstanceId == sValidatedWellInstanceId)
{
return true;
}
m_sCurrentResultWellInstanceId = sValidatedWellInstanceId;
emit sigPebiResultSnapshotChanged();
return true;
}
bool nmDataAnalyzeManager::commitPebiResultSnapshot(
QSharedPointer<nmPebiResultSnapshot>& pCandidate,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
QString sError;
QString sNextWellInstanceId;
QSharedPointer<const nmPebiResultSnapshot> pPublishedCandidate;
try
{
// 发布只能发生在 Manager 所属线程,避免工作线程直接替换 UI 正在读取的引用。
if(QThread::currentThread() != thread())
{
sError = "PEBI result snapshot must be committed on its owner thread.";
}
else if(pCandidate.isNull() || !pCandidate->isComplete())
{
sError = "PEBI result snapshot candidate is incomplete.";
}
else if(pCandidate->getGridInputRevision() !=
m_oNumericalAnalysisCase.getGridInputRevision() ||
pCandidate->getResultInputRevision() !=
m_oNumericalAnalysisCase.getResultInputRevision() ||
!m_oNumericalAnalysisCase.isGridValid())
{
sError = "PEBI result snapshot input revisions are stale.";
}
// 即使外部遗漏输入版本递增,也不允许已删除井或同编码新井接收旧任务结果。
QSet<QString> setWellInstanceIds;
for(int nIndex = 0;
sError.isEmpty() && nIndex < pCandidate->getWellCount();
++nIndex)
{
const nmPebiResultWellSnapshot* pWell =
pCandidate->getWellAt(nIndex);
nmDataWellBase* pLiveWell = pWell == NULL ? NULL :
findWellByCode(pWell->m_sWellCode);
if(pWell == NULL || pLiveWell == NULL ||
pLiveWell->getWellInstanceId() != pWell->m_sWellInstanceId ||
setWellInstanceIds.contains(pWell->m_sWellInstanceId))
{
sError = "PEBI result well identity is stale.";
break;
}
setWellInstanceIds.insert(pWell->m_sWellInstanceId);
}
// 当前网格登记的槽位必须与候选完全相同,防止相同版本下的非法外部改写。
const QVector<nmSolverWellRef> vecCurrentOrder =
m_oNumericalAnalysisCase.getSolverWellOrder();
if(sError.isEmpty() &&
vecCurrentOrder.size() != pCandidate->getSolverSlotCount())
{
sError = "PEBI solver slot order has changed.";
}
for(int nIndex = 0;
sError.isEmpty() && nIndex < vecCurrentOrder.size();
++nIndex)
{
const nmSolverWellRef& oCurrent = vecCurrentOrder[nIndex];
const nmPebiResultSolverSlot* pSaved =
pCandidate->getSolverSlotAt(nIndex);
if(pSaved == NULL ||
oCurrent.m_nSolverIndex != pSaved->m_nSolverIndex ||
oCurrent.m_eEntryKind != pSaved->m_eEntryKind ||
oCurrent.m_sWellCode != pSaved->m_sWellCode ||
oCurrent.m_eWellType != pSaved->m_eWellType)
{
sError = "PEBI solver slot order has changed.";
}
}
if(sError.isEmpty())
{
pPublishedCandidate =
QSharedPointer<const nmPebiResultSnapshot>(pCandidate);
// 新结果仍包含旧选择时保持用户上下文,否则退到第一口可显示井。
sNextWellInstanceId = m_sCurrentResultWellInstanceId;
if(!pPublishedCandidate->isDisplayWell(sNextWellInstanceId))
{
const QStringList& listDisplayWellIds =
pPublishedCandidate->getDisplayWellInstanceIds();
sNextWellInstanceId = listDisplayWellIds.isEmpty()
? QString() : listDisplayWellIds.first();
}
}
}
catch(const std::bad_alloc&)
{
// 校验期间内存不足时尚未触碰发布点,旧快照和旧结果井选择保持不变。
pCandidate.clear();
if(pError != NULL)
{
*pError = "Not enough memory to validate PEBI result snapshot.";
}
return false;
}
if(!sError.isEmpty())
{
// 提交失败也要消除可写别名,但旧快照和旧结果井选择保持不变。
pCandidate.clear();
if(pError != NULL)
{
*pError = sError;
}
return false;
}
// 发布点只替换只读引用;先前快照由仍在查看它的窗口延迟释放。
m_pPebiResultSnapshot = pPublishedCandidate;
m_sCurrentResultWellInstanceId = sNextWellInstanceId;
pCandidate.clear();
rebindPebiResultSnapshotToWells();
emit sigPebiResultSnapshotChanged();
return true;
}
void nmDataAnalyzeManager::rebindPebiResultSnapshotToWells()
{
QWeakPointer<const nmPebiResultSnapshot> pWeakSnapshot;
if(!m_pPebiResultSnapshot.isNull())
{
pWeakSnapshot = QWeakPointer<const nmPebiResultSnapshot>(
m_pPebiResultSnapshot);
}
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex)
{
nmDataWellBase* pWell = m_vWellData[nIndex];
if(pWell == NULL)
{
continue;
}
const bool bBelongsToSnapshot =
!m_pPebiResultSnapshot.isNull() &&
m_pPebiResultSnapshot->findWell(
pWell->getWellInstanceId()) != NULL;
pWell->bindPebiResultSnapshot(bBelongsToSnapshot
? pWeakSnapshot
: QWeakPointer<const nmPebiResultSnapshot>());
}
}
void nmDataAnalyzeManager::clearPebiResultSnapshot()
{
if(m_pPebiResultSnapshot.isNull() &&
m_sCurrentResultWellInstanceId.isEmpty())
{
return;
}
m_pPebiResultSnapshot.clear();
m_sCurrentResultWellInstanceId.clear();
rebindPebiResultSnapshotToWells();
emit sigPebiResultSnapshotChanged();
}
// 设置当前查看井
void nmDataAnalyzeManager::setCurWellData(nmDataWellBase * wellData)
{
m_pCurDataWell = wellData;
}
// 获取当前查看井
nmDataWellBase* nmDataAnalyzeManager::getCurWellData()
{
return m_pCurDataWell;
}
void nmDataAnalyzeManager::notifyDataChanged()
{
// 现有属性编辑链路尚未细分“几何变化”和“仅求解输入变化”,
// 为保证正确性先采用保守策略:任一计算数据变化都使网格失效。
m_oNumericalAnalysisCase.invalidateGrid();
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 = 3;
if(!doc.HasMember("NumericalProjectVersion") ||
!doc["NumericalProjectVersion"].IsInt() ||
doc["NumericalProjectVersion"].GetInt() != nSupportedProjectVersion) {
qWarning() << "Unsupported numerical project version:" << filePath;
return false;
}
quint64 nSavedGridInputRevision = 0;
quint64 nSavedBuiltGridRevision = 0;
quint64 nSavedResultInputRevision = 0;
if(!doc.HasMember("InputRevisions") ||
!doc["InputRevisions"].IsObject()) {
qWarning() << "Numerical v3 project has no input revisions:" << filePath;
return false;
}
const rapidjson::Value& oRevisionJson = doc["InputRevisions"];
if(!oRevisionJson.HasMember("GridInputRevision") ||
!oRevisionJson["GridInputRevision"].IsUint64() ||
!oRevisionJson.HasMember("BuiltGridRevision") ||
!oRevisionJson["BuiltGridRevision"].IsUint64() ||
!oRevisionJson.HasMember("ResultInputRevision") ||
!oRevisionJson["ResultInputRevision"].IsUint64()) {
qWarning() << "Numerical v3 project contains invalid input revisions:"
<< filePath;
return false;
}
nSavedGridInputRevision =
oRevisionJson["GridInputRevision"].GetUint64();
nSavedBuiltGridRevision =
oRevisionJson["BuiltGridRevision"].GetUint64();
nSavedResultInputRevision =
oRevisionJson["ResultInputRevision"].GetUint64();
if(nSavedGridInputRevision == 0 || nSavedResultInputRevision == 0 ||
(nSavedBuiltGridRevision != 0 &&
nSavedBuiltGridRevision != nSavedGridInputRevision)) {
qWarning() << "Numerical v3 project input revision values are invalid:"
<< filePath;
return false;
}
QString sSavedPrimaryWellCode;
NM_CASE_WELL_MODE eSavedPrimaryWellMode = NM_CaseWell_Observation;
bool bSavedIncludeOtherWells = false;
double dSavedPebiGridControl = 150.0;
QVector<nmCalculationWellRef> vecSavedIncludedWells;
QVector<nmSolverWellRef> vecSavedSolverOrder;
// 第一步v3 必须完整保存当前分析方案,缺字段时不按旧项目规则猜测。
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("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());
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() ||
!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;
QSet<QString> setSavedWellInstanceIds;
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("WellInstanceId") ||
!oWellJson["WellInstanceId"].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 QString sWellInstanceId = QString::fromUtf8(
oWellJson["WellInstanceId"].GetString());
const QUuid oWellInstanceUuid(sWellInstanceId);
const QString sNormalizedWellInstanceId =
oWellInstanceUuid.toString().remove('{').remove('}');
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) ||
oWellInstanceUuid.isNull() ||
sNormalizedWellInstanceId != sWellInstanceId ||
setSavedWellInstanceIds.contains(sWellInstanceId)) {
qWarning() << "Numerical project contains an invalid well identity:"
<< sWellCode << sWellInstanceId;
return false;
}
mapSavedWellTypes.insert(sWellCode, eWellType);
setSavedWellInstanceIds.insert(sWellInstanceId);
}
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));
}
// 第四步:此时井的历史流量尚未恢复,无法判断一口 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;
}
}
}
// 前置校验已完成,从这里开始替换当前输入。旧快照要到全部 v3 载荷
// 成功后才替换,加载失败或内存不足时仍保留上一套已发布快照。
// 清空现有数据,确保从头加载新数据
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;
QSet<QString> setLoadedWellInstanceIds;
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWellData = m_vWellData[nIndex];
if(pWellData == nullptr || pWellData->getWellCode().isEmpty() ||
mapLoadedWellTypes.contains(pWellData->getWellCode()) ||
!setSavedWellInstanceIds.contains(
pWellData->getWellInstanceId()) ||
setLoadedWellInstanceIds.contains(
pWellData->getWellInstanceId())) {
qWarning() << "Loaded numerical project contains an invalid WellCode:";
return false;
}
mapLoadedWellTypes.insert(pWellData->getWellCode(),
pWellData->getWellType());
setLoadedWellInstanceIds.insert(pWellData->getWellInstanceId());
}
if(mapLoadedWellTypes != mapSavedWellTypes ||
setLoadedWellInstanceIds != setSavedWellInstanceIds) {
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);
// 解析 时间步 对象
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());
}
if(!m_oNumericalAnalysisCase.restoreInputRevisions(
nSavedGridInputRevision,
nSavedBuiltGridRevision,
nSavedResultInputRevision)) {
qWarning() << "Failed to restore numerical v3 input revisions:"
<< filePath;
return false;
}
return true;
}
// 将 C++ 对象数据写入 JSON 文件
bool nmDataAnalyzeManager::WriteProjectData(
const QString & filePath,
const nmNumericalWindowPayloadReferences* pPayloadReferences)
{
if(pPayloadReferences == NULL) {
qWarning() << "Numerical v3 project requires explicit payload references.";
return false;
}
// 第一步:保存前校验 Map 井目录,持久化身份只允许非空且唯一的 WellCode。
QSet<QString> setWellCodes;
QSet<QString> setWellInstanceIds;
QMap<QString, NM_WELL_MODEL> mapWellTypes;
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWellData = m_vWellData[nIndex];
const QString sWellInstanceId = pWellData == nullptr
? QString() : pWellData->getWellInstanceId();
const QUuid oWellInstanceUuid(sWellInstanceId);
const QString sNormalizedWellInstanceId =
oWellInstanceUuid.toString().remove('{').remove('}');
if(pWellData == nullptr || pWellData->getWellCode().isEmpty() ||
setWellCodes.contains(pWellData->getWellCode()) ||
oWellInstanceUuid.isNull() ||
sNormalizedWellInstanceId != sWellInstanceId ||
setWellInstanceIds.contains(sWellInstanceId)) {
qWarning() << "Cannot save numerical project: invalid well identity.";
return false;
}
setWellCodes.insert(pWellData->getWellCode());
setWellInstanceIds.insert(sWellInstanceId);
mapWellTypes.insert(pWellData->getWellCode(), pWellData->getWellType());
}
const QString sPrimaryWellCode = getPrimaryWellCode();
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);
}
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(); // 获取内存分配器
doc.AddMember("NumericalProjectVersion", 3, allocator);
rapidjson::Value oRevisionJson(rapidjson::kObjectType);
oRevisionJson.AddMember("GridInputRevision",
m_oNumericalAnalysisCase.getGridInputRevision(), allocator);
oRevisionJson.AddMember("BuiltGridRevision",
m_oNumericalAnalysisCase.getBuiltGridRevision(), allocator);
oRevisionJson.AddMember("ResultInputRevision",
m_oNumericalAnalysisCase.getResultInputRevision(), allocator);
doc.AddMember("InputRevisions", oRevisionJson, allocator);
doc.AddMember("CurrentResultWellInstanceId", numericalJsonString(
m_sCurrentResultWellInstanceId, allocator), 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);
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);
// 主 JSON 只保存引用,不通过目录扫描推断网格、历史或快照载荷。
rapidjson::Value oPayloadJson(rapidjson::kObjectType);
if(pPayloadReferences->m_bHasCurrentGrid) {
addNumericalFileReference(oPayloadJson, "CurrentGrid",
pPayloadReferences->m_oCurrentGrid, allocator);
} else {
oPayloadJson.AddMember("CurrentGrid",
rapidjson::Value(rapidjson::kNullType).Move(), allocator);
}
rapidjson::Value oHistoryJson(rapidjson::kArrayType);
QMap<QString, nmNumericalFileReference>::const_iterator oHistoryIt =
pPayloadReferences->m_mapWellHistories.constBegin();
for(; oHistoryIt != pPayloadReferences->m_mapWellHistories.constEnd();
++oHistoryIt) {
rapidjson::Value oWellJson(rapidjson::kObjectType);
oWellJson.AddMember("WellInstanceId", numericalJsonString(
oHistoryIt.key(), allocator), allocator);
addNumericalFileReference(oWellJson, "File",
oHistoryIt.value(), allocator);
oHistoryJson.PushBack(oWellJson, allocator);
}
oPayloadJson.AddMember("WellHistories", oHistoryJson, allocator);
if(pPayloadReferences->m_bHasSnapshot) {
addNumericalFileReference(oPayloadJson, "Snapshot",
pPayloadReferences->m_oSnapshot, allocator);
} else {
oPayloadJson.AddMember("Snapshot",
rapidjson::Value(rapidjson::kNullType).Move(), allocator);
}
doc.AddMember("PayloadFiles", oPayloadJson, allocator);
// 将最终构建好的 Document 写入文件
if(!nmDataJsonTools::WriteDomToFile(doc, filePath)) {
qDebug() << "Error: Failed to write DOM to file:" << filePath;
return false;
}
return true;
}
bool nmDataAnalyzeManager::saveNmResultV3(
const QString& sWindowDirectory,
nmNumericalFileReference& oMainJsonReference,
QString* pError)
{
if(pError != NULL) {
pError->clear();
}
oMainJsonReference = nmNumericalFileReference();
try {
if(sWindowDirectory.isEmpty() ||
!ensureDirectoryExists(sWindowDirectory)) {
if(pError != NULL) *pError = "Cannot create numerical v3 window directory.";
return false;
}
nmNumericalWindowPayloadReferences oReferences;
if(isPebiGridValid()) {
if(m_pVtkUnstructuredGrid == NULL ||
!ensureDirectoryExists(QDir(sWindowDirectory).filePath("Grid"))) {
if(pError != NULL) *pError = "Current PEBI grid is unavailable.";
return false;
}
const QString sCurrentGridPath = QDir(sWindowDirectory).filePath(
"Grid/CurrentGrid.vtu");
if(!nmNumericalResultPersistence::writeGrid(
m_pVtkUnstructuredGrid, sCurrentGridPath, pError) ||
!nmNumericalResultPersistence::buildFileReference(
sCurrentGridPath, "Grid/CurrentGrid.vtu",
oReferences.m_oCurrentGrid)) {
return false;
}
oReferences.m_bHasCurrentGrid = true;
}
const QString sHistoryDirectory = QDir(sWindowDirectory).filePath(
"WellHistory");
if(!ensureDirectoryExists(sHistoryDirectory)) {
if(pError != NULL) *pError = "Cannot create v3 well history directory.";
return false;
}
QSet<QString> setWellInstanceIds;
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWell = m_vWellData[nIndex];
if(pWell == NULL || pWell->getWellInstanceId().isEmpty() ||
setWellInstanceIds.contains(pWell->getWellInstanceId())) {
if(pError != NULL) *pError = "Live well UUID directory is invalid.";
return false;
}
setWellInstanceIds.insert(pWell->getWellInstanceId());
nmNumericalWellHistoryData oHistory;
oHistory.m_vecPressure = pWell->getHistoryPressure();
oHistory.m_vecLogLog = pWell->getHistoryLogLog();
oHistory.m_vecSemiLog = pWell->getHistorySemiLog();
const QString sRelativePath = QString("WellHistory/%1.bin")
.arg(pWell->getWellInstanceId());
const QString sHistoryPath = QDir(sWindowDirectory).filePath(
sRelativePath);
nmNumericalFileReference oReference;
if(!nmNumericalResultPersistence::writeWellHistory(
sHistoryPath, oHistory, pError) ||
!nmNumericalResultPersistence::buildFileReference(
sHistoryPath, sRelativePath, oReference)) {
return false;
}
oReferences.m_mapWellHistories.insert(
pWell->getWellInstanceId(), oReference);
}
if(!m_pPebiResultSnapshot.isNull()) {
if(!nmPebiResultSnapshotSerializer::save(
m_pPebiResultSnapshot, sWindowDirectory,
oReferences.m_oSnapshot, pError)) {
return false;
}
oReferences.m_bHasSnapshot = true;
}
// 所有外部载荷成功后才写主 JSON使引用链不会指向半成品文件。
const QString sMainJsonPath = QDir(sWindowDirectory).filePath(
"Numerical_Parameters.json");
if(!WriteProjectData(sMainJsonPath, &oReferences) ||
!nmNumericalResultPersistence::buildFileReference(
sMainJsonPath, "Numerical_Parameters.json",
oMainJsonReference)) {
if(pError != NULL && pError->isEmpty())
*pError = "Cannot write numerical v3 main JSON.";
return false;
}
// 返回保存会话前沿主 JSON 的明确引用完整复核本窗口载荷。
return validateNmResultV3Window(
sWindowDirectory, sMainJsonPath, pError);
}
catch(const std::bad_alloc&) {
if(pError != NULL) *pError = "Not enough memory to save numerical v3 result.";
return false;
}
}
bool nmDataAnalyzeManager::validateNmResultV3Window(
const QString& sWindowDirectory,
const QString& sMainJsonPath,
QString* pError)
{
if(pError != NULL) {
pError->clear();
}
try {
const QString sExpectedDirectory = QDir::cleanPath(
QFileInfo(sWindowDirectory).absoluteFilePath());
const QFileInfo oMainInfo(sMainJsonPath);
if(!oMainInfo.isFile() ||
QDir::cleanPath(oMainInfo.absolutePath()).compare(
sExpectedDirectory, Qt::CaseInsensitive) != 0) {
if(pError != NULL)
*pError = "Numerical v3 main JSON leaves its window directory.";
return false;
}
nmNumericalWindowPayloadReferences oReferences;
QString sCurrentResultWellInstanceId;
quint64 nGridInputRevision = 0;
quint64 nBuiltGridRevision = 0;
quint64 nResultInputRevision = 0;
if(!readV3WindowHeader(sMainJsonPath, oReferences,
sCurrentResultWellInstanceId, nGridInputRevision,
nBuiltGridRevision, nResultInputRevision, pError)) {
return false;
}
Q_UNUSED(nGridInputRevision);
Q_UNUSED(nBuiltGridRevision);
Q_UNUSED(nResultInputRevision);
if(!oReferences.m_bHasSnapshot &&
!sCurrentResultWellInstanceId.isEmpty()) {
if(pError != NULL)
*pError = "Numerical v3 result well is selected without a snapshot.";
return false;
}
if(oReferences.m_bHasCurrentGrid &&
!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oReferences.m_oCurrentGrid,
NULL, pError)) {
return false;
}
QMap<QString, nmNumericalFileReference>::const_iterator oIt =
oReferences.m_mapWellHistories.constBegin();
for(; oIt != oReferences.m_mapWellHistories.constEnd(); ++oIt) {
QString sHistoryPath;
nmNumericalWellHistoryData oValidatedHistory;
if(!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oIt.value(), &sHistoryPath, pError) ||
!nmNumericalResultPersistence::readWellHistory(
sHistoryPath, oValidatedHistory, pError)) {
return false;
}
}
if(oReferences.m_bHasSnapshot &&
!nmPebiResultSnapshotSerializer::validate(
sWindowDirectory, oReferences.m_oSnapshot, pError)) {
return false;
}
return true;
}
catch(const std::bad_alloc&) {
if(pError != NULL)
*pError = "Not enough memory to validate numerical v3 result.";
return false;
}
}
bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
{
try {
// v3 先在独立候选对象上完成全部解析和业务校验。
// 任一步失败时只析构候选,当前窗口已经发布的数据保持不变。
nmDataAnalyzeManager oLoadedManager;
oLoadedManager.m_pOwnerFitting = m_pOwnerFitting;
oLoadedManager.initPvtParaFromSubFit();
if(!oLoadedManager.loadNmResultV3InPlace(sLoadAnalDir)) {
return false;
}
// 井构造时不能依赖尚未发布的候选 Manager完整读取后统一绑定储层。
for(int nIndex = 0; nIndex < oLoadedManager.m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWell = oLoadedManager.m_vWellData[nIndex];
if(pWell != NULL) {
pWell->m_pReservoir = oLoadedManager.m_reservoirData;
}
}
swapLoadedProjectState(oLoadedManager);
rebuildLoadedAttributeRegistry();
rebindPebiResultSnapshotToWells();
emit sigPebiResultSnapshotChanged();
return true;
}
catch(const std::bad_alloc&) {
qWarning() << "Not enough memory to stage numerical v3 result.";
return false;
}
}
bool nmDataAnalyzeManager::loadNmResultV3InPlace(
const QString& sLoadAnalDir)
{
try {
// 外部框架仍传入旧的“成果根/窗口标识”逻辑路径v3 只从根清单解析真实代次。
const QFileInfo oLogicalWindowInfo(QDir::cleanPath(sLoadAnalDir));
const QString sWindowId = oLogicalWindowInfo.fileName();
const QString sResultRootDirectory = oLogicalWindowInfo.dir().absolutePath();
const QString sManifestPath = QDir(sResultRootDirectory).filePath(
"NumericalSaveManifest.json");
if(!QFileInfo(sManifestPath).isFile()) {
const QString sLegacyJsonPath = QDir(sLoadAnalDir).filePath(
"Results/Numerical_Parameters.json");
if(QFileInfo(sLegacyJsonPath).isFile()) {
qWarning() << "Numerical project version 2 is not supported:"
<< sLegacyJsonPath;
} else {
qWarning() << "Numerical v3 root manifest is missing:"
<< sManifestPath;
}
return false;
}
QString sWindowDirectory;
QString sMainJsonPath;
QString sError;
if(!nmNumericalSaveSession::resolveWindowMainJson(
sResultRootDirectory, sWindowId, sWindowDirectory,
sMainJsonPath, NULL, &sError)) {
qWarning() << sError;
return false;
}
nmNumericalWindowPayloadReferences oReferences;
QString sSavedResultWellInstanceId;
quint64 nGridInputRevision = 0;
quint64 nBuiltGridRevision = 0;
quint64 nResultInputRevision = 0;
if(!readV3WindowHeader(sMainJsonPath, oReferences,
sSavedResultWellInstanceId, nGridInputRevision,
nBuiltGridRevision, nResultInputRevision, &sError)) {
qWarning() << sError << sMainJsonPath;
return false;
}
// 所有引用文件先完成哈希和结构读取,任何失败都不能替换已发布快照。
vtkSmartPointer<vtkUnstructuredGrid> pLoadedCurrentGrid;
if(oReferences.m_bHasCurrentGrid) {
QString sCurrentGridPath;
if(!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oReferences.m_oCurrentGrid,
&sCurrentGridPath, &sError) ||
!nmNumericalResultPersistence::readGrid(
sCurrentGridPath, pLoadedCurrentGrid, &sError)) {
qWarning() << sError;
return false;
}
}
QMap<QString, nmNumericalWellHistoryData> mapLoadedHistories;
QMap<QString, nmNumericalFileReference>::const_iterator oHistoryIt =
oReferences.m_mapWellHistories.constBegin();
for(; oHistoryIt != oReferences.m_mapWellHistories.constEnd();
++oHistoryIt) {
QString sHistoryPath;
nmNumericalWellHistoryData oHistory;
if(!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oHistoryIt.value(),
&sHistoryPath, &sError) ||
!nmNumericalResultPersistence::readWellHistory(
sHistoryPath, oHistory, &sError)) {
qWarning() << sError;
return false;
}
mapLoadedHistories.insert(oHistoryIt.key(), oHistory);
}
QSharedPointer<nmPebiResultSnapshot> pLoadedCandidate;
if(oReferences.m_bHasSnapshot &&
!nmPebiResultSnapshotSerializer::load(
sWindowDirectory, oReferences.m_oSnapshot,
pLoadedCandidate, &sError)) {
qWarning() << sError;
return false;
}
// 主 JSON 在外部载荷全部可用后才恢复当前输入v2 会在版本检查处直接拒绝。
if(!ReadProjectData(sMainJsonPath)) {
return false;
}
if(m_oNumericalAnalysisCase.getGridInputRevision() !=
nGridInputRevision ||
m_oNumericalAnalysisCase.getBuiltGridRevision() !=
nBuiltGridRevision ||
m_oNumericalAnalysisCase.getResultInputRevision() !=
nResultInputRevision ||
oReferences.m_bHasCurrentGrid !=
m_oNumericalAnalysisCase.isGridValid()) {
qWarning() << "Loaded numerical input revisions and current grid are inconsistent.";
return false;
}
// 实时井历史按 UUID 一一匹配;井名、井编码和数组顺序都不能替代身份。
if(mapLoadedHistories.size() != m_vWellData.size()) {
qWarning() << "Loaded numerical well history directory is incomplete.";
return false;
}
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
nmDataWellBase* pWell = m_vWellData[nIndex];
if(pWell == NULL ||
!mapLoadedHistories.contains(pWell->getWellInstanceId())) {
qWarning() << "Loaded numerical well UUID has no history payload.";
return false;
}
const nmNumericalWellHistoryData oHistory =
mapLoadedHistories.take(pWell->getWellInstanceId());
pWell->setHistoryPressure(oHistory.m_vecPressure);
pWell->setHistoryLogLog(oHistory.m_vecLogLog);
pWell->setHistorySemiLog(oHistory.m_vecSemiLog);
}
if(!mapLoadedHistories.isEmpty()) {
qWarning() << "Numerical v3 project contains unreferenced well histories.";
return false;
}
loadWellPreAndFlow();
const QVector<nmCalculationWellRef> vecIncludedWells =
getIncludedCalculationWells();
for(int nIndex = 0; nIndex < vecIncludedWells.size(); ++nIndex) {
nmDataWellBase* pWell = findWellByCode(
vecIncludedWells[nIndex].m_sWellCode);
if(!isSupportedNumericalWell(pWell) ||
pWell->getFlowPoints().size() < 2) {
qWarning() << "Failed to restore included numerical well:"
<< vecIncludedWells[nIndex].m_sWellCode;
return false;
}
}
if(!pLoadedCandidate.isNull() &&
!sSavedResultWellInstanceId.isEmpty() &&
!pLoadedCandidate->isDisplayWell(sSavedResultWellInstanceId)) {
qWarning() << "Saved result well UUID is absent from the PEBI snapshot.";
return false;
}
if(pLoadedCandidate.isNull() &&
!sSavedResultWellInstanceId.isEmpty()) {
qWarning() << "Numerical project selects a result well without a snapshot.";
return false;
}
// 所有可能失败的读取和映射已完成,再一次替换当前网格与规范快照。
m_pVtkUnstructuredGrid = pLoadedCurrentGrid;
if(pLoadedCandidate.isNull()) {
clearPebiResultSnapshot();
} else {
QSharedPointer<const nmPebiResultSnapshot> pPublished(
pLoadedCandidate);
QString sNextWellInstanceId = sSavedResultWellInstanceId;
if(sNextWellInstanceId.isEmpty()) {
const QStringList& listDisplayIds =
pPublished->getDisplayWellInstanceIds();
sNextWellInstanceId = listDisplayIds.isEmpty()
? QString() : listDisplayIds.first();
}
m_pPebiResultSnapshot = pPublished;
m_sCurrentResultWellInstanceId = sNextWellInstanceId;
pLoadedCandidate.clear();
rebindPebiResultSnapshotToWells();
emit sigPebiResultSnapshotChanged();
}
m_bIsLoadData = m_pVtkUnstructuredGrid != NULL ||
!m_pPebiResultSnapshot.isNull();
return true;
}
catch(const std::bad_alloc&) {
qWarning() << "Not enough memory to load numerical v3 result.";
return false;
}
}
void nmDataAnalyzeManager::swapLoadedProjectState(
nmDataAnalyzeManager& oLoadedManager)
{
// 这里只交换项目数据所有权。窗口、Plot、后台任务和同步原语仍属于当前 Manager。
qSwap(m_vWellData, oLoadedManager.m_vWellData);
qSwap(m_vFaultData, oLoadedManager.m_vFaultData);
qSwap(m_vFractureData, oLoadedManager.m_vFractureData);
qSwap(m_vRegionData, oLoadedManager.m_vRegionData);
qSwap(m_vRegionMarkData, oLoadedManager.m_vRegionMarkData);
qSwap(m_vecLayers, oLoadedManager.m_vecLayers);
qSwap(m_vecPropertyInterpolationDataSets,
oLoadedManager.m_vecPropertyInterpolationDataSets);
qSwap(m_outlineData, oLoadedManager.m_outlineData);
qSwap(m_axisData, oLoadedManager.m_axisData);
qSwap(m_pAutomaticFittingData,
oLoadedManager.m_pAutomaticFittingData);
qSwap(m_reservoirData, oLoadedManager.m_reservoirData);
qSwap(m_pGeoRefData, oLoadedManager.m_pGeoRefData);
qSwap(m_pSensitiveData, oLoadedManager.m_pSensitiveData);
qSwap(m_pebiPvtPara, oLoadedManager.m_pebiPvtPara);
qSwap(m_pMixedResults, oLoadedManager.m_pMixedResults);
qSwap(m_pTimeStep, oLoadedManager.m_pTimeStep);
qSwap(m_pCurDataWell, oLoadedManager.m_pCurDataWell);
qSwap(m_eSolverModelType, oLoadedManager.m_eSolverModelType);
qSwap(m_nPebiSolverType, oLoadedManager.m_nPebiSolverType);
qSwap(m_nPebiOmpThreads, oLoadedManager.m_nPebiOmpThreads);
qSwap(m_nPebiIluReuseSteps, oLoadedManager.m_nPebiIluReuseSteps);
qSwap(m_oNumericalAnalysisCase,
oLoadedManager.m_oNumericalAnalysisCase);
qSwap(m_pPebiResultSnapshot,
oLoadedManager.m_pPebiResultSnapshot);
qSwap(m_sCurrentResultWellInstanceId,
oLoadedManager.m_sCurrentResultWellInstanceId);
vtkSmartPointer<vtkUnstructuredGrid> pGrid =
m_pVtkUnstructuredGrid;
m_pVtkUnstructuredGrid = oLoadedManager.m_pVtkUnstructuredGrid;
oLoadedManager.m_pVtkUnstructuredGrid = pGrid;
qSwap(m_bIsLoadData, oLoadedManager.m_bIsLoadData);
}
void nmDataAnalyzeManager::rebuildLoadedAttributeRegistry()
{
if(m_pAttrRegistry == NULL) {
return;
}
// 注册表对象本身不替换,避免破坏已经建立的 UI 信号连接。
m_pAttrRegistry->clear();
if(m_reservoirData != NULL) {
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());
}
syncWellAttrs();
syncGeometryAttrs();
}
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;
}
//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;
}