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/nmCalculation/nmCalculationDllPebiSolverT...

1187 lines
47 KiB
C++

#include "nmCalculationDllPebiSolverTask.h"
#include "singlePhaseSolver.h"
#include "zxLogInstance.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataWellBase.h"
#include "nmDataVerticalWell.h"
#include "nmDataVerticalFracturedWell.h"
#include "nmDataHorizontalFracturedWell.h"
#include "nmDataReservoir.h"
#include "nmDataAttribute.h"
#include "nmDataRegion.h"
#include "nmDataRegionMark.h"
#include "nmDataOutline.h"
#include "nmDataFracture.h"
#include "nmDataFault.h"
#include "nmDataTimeStepSetting.h"
#include "nmCalculationPebiGrid.h"
#include "nmDataAnalyzeManager.h"
#include "nmDataPvtParaForPebi.h"
#include <QDebug>
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include <algorithm>
#include <stdexcept>
#include <cmath>
#include <float.h>
#include <string>
#include <vtkSmartPointer.h>
#include <vtkDoubleArray.h>
namespace {
// PVT数组读到时才覆盖读不到时保留pch.h中的默认参数
void assignPvtVectorIfNotEmpty(dVec1& target, const QVector<double>& source)
{
if(!source.isEmpty()) {
target = dVec1(source.toStdVector());
}
}
// 将内部储层相态转换为求解器T类型
int pebiModelTypeFromPhase(NM_PHASE_TYPE phaseType)
{
switch(phaseType) {
case PHASE_Water:
return 4;
case PHASE_Gas:
return 5;
case PHASE_Oil_Water:
return 8;
case PHASE_Oil:
default:
return 1;
}
}
bool isFiniteSolverNumber(double value)
{
#ifdef _MSC_VER
return _finite(value) != 0;
#else
return std::isfinite(value);
#endif
}
bool isReasonableLogLogValue(double value)
{
const double maxReasonableAbsValue = 1.0e12;
return isFiniteSolverNumber(value)
&& value >= -maxReasonableAbsValue
&& value <= maxReasonableAbsValue;
}
bool isValidLogLogPoint(const Point& pt)
{
return isReasonableLogLogValue(pt.x)
&& isReasonableLogLogValue(pt.y)
&& isReasonableLogLogValue(pt.z)
&& pt.x > 0.0
&& pt.z >= DBL_EPSILON;
}
bool isValidSemiLogPoint(const Point& pt)
{
return isReasonableLogLogValue(pt.x)
&& pt.x > 0.0
&& !pt.pointData.empty()
&& isReasonableLogLogValue(pt.pointData[0]);
}
}
nmCalculationDllPebiSolverTask::nmCalculationDllPebiSolverTask(
QString sPostprocessingDir,
QObject *parent):
QThread(parent),
m_sPostprocessingDir(sPostprocessingDir)
{
}
nmCalculationDllPebiSolverTask::~nmCalculationDllPebiSolverTask()
{
if(isRunning()) {
terminate();
wait(3000);
}
}
void nmCalculationDllPebiSolverTask::run()
{
bool result = this->execute();
emit sig_calculateDone(result);
}
bool nmCalculationDllPebiSolverTask::execute()
{
return this->execPebiMode();
}
bool nmCalculationDllPebiSolverTask::execPebiMode()
{
//emit sigResSolverProgressUpdated(1);
nmDataAnalyzeManager* pDataInstance = nmDataAnalyzeManager::getCurrentInstance();
nmCalculationPebiGrid* pGridInstance = nmCalculationPebiGrid::getInstance();
if(pDataInstance == nullptr || pGridInstance == nullptr) {
return false;
}
// 载入Pebi动态库
HMODULE dll = LoadLibrary(L"HX_NWTM.dll");
if(dll) {
// 定义函数指针类型
typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&, HX_NWTM_GRID_OUTPUT2&, const HX_NWTM_GRID_INPUT&, std::string);
typedef void (*HX_NWTM_MODEL_Fun)(HX_NWTM_MODEL_OUTPUT&, const HX_NWTM_MODEL_INPUT&, std::string);
// 获取函数地址
HX_NWTM_MODEL_Fun HX_NWTM_MODEL = (HX_NWTM_MODEL_Fun)GetProcAddress(dll, "HX_NWTM_MODEL");
if(HX_NWTM_MODEL) {
if(pGridInstance->getGridOutput1().PEBI_cell.p.size() <= 0) {
// 先生成需要的输入参数p2
pGridInstance->generateOutputPara();
}
// 参数定义,使用默认参数
HX_NWTM_MODEL_OUTPUT p1;
HX_NWTM_MODEL_INPUT p0(pGridInstance->getGridOutput2());
nmDataReservoir* pReservoirData = pDataInstance->getReservoirData();
NM_PHASE_TYPE phaseType = pReservoirData != nullptr ? pReservoirData->getPhaseType() : PHASE_Oil;
// 根据界面相态选择求解器模型类型
p0.T = pebiModelTypeFromPhase(phaseType);
// 获取PEBI网格存入井的顺序
QVector<QPair<NM_WELL_MODEL, QString>> vecWellsOrder = pDataInstance->getCalculationWells();
p0.CS.C.resize(vecWellsOrder.size());
p0.CS.S.resize(vecWellsOrder.size());
p0.Rate.t.resize(vecWellsOrder.size());
p0.Rate.qo.resize(vecWellsOrder.size());
p0.Rate.qw.resize(vecWellsOrder.size());
p0.Rate.qg.resize(vecWellsOrder.size());
//emit sigResSolverProgressUpdated(2);
// 遍历井数据
for(int i = 0; i < vecWellsOrder.size(); ++i) {
NM_WELL_MODEL wellType = vecWellsOrder[i].first; // 获取井的类型
QString wellName = vecWellsOrder[i].second; // 获取井的名称
// 跳过裂缝
if(wellType == NM_WELL_MODEL::Unknow_Well) {
// 清空裂缝的数据,因为头文件内部有五口井的数据,会传递给裂缝
p0.CS.C[i] = 0;
p0.CS.S[i] = 0;
p0.Rate.t[i].clear();
p0.Rate.qo[i].clear();
p0.Rate.qw[i].clear();
p0.Rate.qg[i].clear();
continue;
}
// 处理直井
if(wellType == NM_WELL_MODEL::Vertical_Well) {
nmDataVerticalWell* pWellData = dynamic_cast<nmDataVerticalWell*>(pDataInstance->findWellByName(wellName));
if(pWellData == nullptr) {
return false;
}
// 井筒储集系数
p0.CS.C[i] = pWellData->getWellboreStorage().getValue().toDouble();
// 表皮系数
//p0.CS.S[i] = pWellData->getSkin().getValue().toDouble();
// 获取第一段射孔的表皮系数
p0.CS.S[i] = pWellData->getPerforation(0)->getSkin().getValue().toDouble();
// 流量数据
QVector<QPointF> vecTimeQ = pWellData->getFlowPoints();
// 分离时间(x)和流量(y)数据
QVector<double> timeDataF;
QVector<double> rateData;
foreach(const QPointF& point, vecTimeQ) {
timeDataF.append(point.x()); // 时间数据
rateData.append(point.y()); // 流量数据
}
// 删除 timeDataF 中的第一个元素
if(!timeDataF.isEmpty()) {
timeDataF.remove(0);
}
// 删除 rateData 中的第一个元素
if(!rateData.isEmpty()) {
rateData.remove(0);
}
//timeDataF.append(2000); // 时间数据
//rateData.append(2000); // 流量数据
// TODO:根据井类型来分类,现在将油气水三个流量设置为一致
// 油井 - 填充到 qo
dVec1& timeValues = p0.Rate.t[i];
timeValues.resize(timeDataF.size());
std::copy(timeDataF.begin(), timeDataF.end(), timeValues.begin());
p0.Rate.qo[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qo[i].begin());
// 气井 - 填充到 qg
p0.Rate.qg[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qg[i].begin());
// 水井 - 填充到 qw
p0.Rate.qw[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qw[i].begin());
}
else if(wellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
nmDataVerticalFracturedWell* pWellData = dynamic_cast<nmDataVerticalFracturedWell*>(pDataInstance->findWellByName(wellName));
if(pWellData == nullptr) {
return false;
}
// 井筒储集系数
p0.CS.C[i] = pWellData->getWellboreStorage().getValue().toDouble();
// 表皮系数
//p0.CS.S[i] = pWellData->getSkin().getValue().toDouble();
// 获取第一段射孔的表皮系数
p0.CS.S[i] = pWellData->getPerforation(0)->getSkin().getValue().toDouble();
// 流量数据
QVector<QPointF> vecTimeQ = pWellData->getFlowPoints();
// 分离时间(x)和流量(y)数据
QVector<double> timeDataF;
QVector<double> rateData;
foreach(const QPointF& point, vecTimeQ) {
timeDataF.append(point.x()); // 时间数据
rateData.append(point.y()); // 流量数据
}
// 删除 timeDataF 中的第一个元素
if(!timeDataF.isEmpty()) {
timeDataF.remove(0);
}
// 删除 rateData 中的第一个元素
if(!rateData.isEmpty()) {
rateData.remove(0);
}
//timeDataF.append(2000); // 时间数据
//rateData.append(2000); // 流量数据
// TODO:根据井类型来分类,现在将油气水三个流量设置为一致
// 油井 - 填充到 qo
dVec1& timeValues = p0.Rate.t[i];
timeValues.resize(timeDataF.size());
std::copy(timeDataF.begin(), timeDataF.end(), timeValues.begin());
p0.Rate.qo[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qo[i].begin());
// 气井 - 填充到 qg
p0.Rate.qg[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qg[i].begin());
// 水井 - 填充到 qw
p0.Rate.qw[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qw[i].begin());
}
else if(wellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
nmDataHorizontalFracturedWell* pWellData = dynamic_cast<nmDataHorizontalFracturedWell*>(pDataInstance->findWellByName(wellName));
if(pWellData == nullptr) {
return false;
}
// 井筒储集系数
p0.CS.C[i] = pWellData->getWellboreStorage().getValue().toDouble();
// 表皮系数
//p0.CS.S[i] = pWellData->getSkin().getValue().toDouble();
// 获取第一段射孔的表皮系数
p0.CS.S[i] = pWellData->getPerforation(0)->getSkin().getValue().toDouble();
// 流量数据
QVector<QPointF> vecTimeQ = pWellData->getFlowPoints();
// 分离时间(x)和流量(y)数据
QVector<double> timeDataF;
QVector<double> rateData;
foreach(const QPointF& point, vecTimeQ) {
timeDataF.append(point.x()); // 时间数据
rateData.append(point.y()); // 流量数据
}
// 删除 timeDataF 中的第一个元素
if(!timeDataF.isEmpty()) {
timeDataF.remove(0);
}
// 删除 rateData 中的第一个元素
if(!rateData.isEmpty()) {
rateData.remove(0);
}
//timeDataF.append(2000); // 时间数据
//rateData.append(2000); // 流量数据
// TODO:根据井类型来分类,现在将油气水三个流量设置为一致
// 油井 - 填充到 qo
dVec1& timeValues = p0.Rate.t[i];
timeValues.resize(timeDataF.size());
std::copy(timeDataF.begin(), timeDataF.end(), timeValues.begin());
p0.Rate.qo[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qo[i].begin());
// 气井 - 填充到 qg
p0.Rate.qg[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qg[i].begin());
// 水井 - 填充到 qw
p0.Rate.qw[i].resize(rateData.size());
std::copy(rateData.begin(), rateData.end(), p0.Rate.qw[i].begin());
}
}
//emit sigResSolverProgressUpdated(3);
// PVT数据
nmDataPvtParaForPebi* pebiPvtPara = pDataInstance->getPebiPvtPara();
if(pebiPvtPara != nullptr) {
// p0构造时已有默认PVT数据这里只用读到的参数覆盖
// 压力与饱和压力
assignPvtVectorIfNotEmpty(p0.PVT.p, pebiPvtPara->getPressure());
if(pebiPvtPara->getPb().getValue().isValid()) {
p0.PVT.pb = pebiPvtPara->getPb().getValue().toDouble();
}
// 读取PVT数据 - 油相参数
assignPvtVectorIfNotEmpty(p0.PVT.Rso, pebiPvtPara->getRso());
assignPvtVectorIfNotEmpty(p0.PVT.Bo, pebiPvtPara->getBo());
assignPvtVectorIfNotEmpty(p0.PVT.Co, pebiPvtPara->getCo());
assignPvtVectorIfNotEmpty(p0.PVT.miuo, pebiPvtPara->getMiuo());
assignPvtVectorIfNotEmpty(p0.PVT.rouo, pebiPvtPara->getRouo());
// 读取PVT数据 - 气相参数
assignPvtVectorIfNotEmpty(p0.PVT.Rv, pebiPvtPara->getRv());
assignPvtVectorIfNotEmpty(p0.PVT.Bg, pebiPvtPara->getBg());
assignPvtVectorIfNotEmpty(p0.PVT.Cg, pebiPvtPara->getCg());
assignPvtVectorIfNotEmpty(p0.PVT.miug, pebiPvtPara->getMiug());
assignPvtVectorIfNotEmpty(p0.PVT.roug, pebiPvtPara->getRoug());
assignPvtVectorIfNotEmpty(p0.PVT.Z, pebiPvtPara->getZ());
// 读取PVT数据 - 水相参数
assignPvtVectorIfNotEmpty(p0.PVT.Rsw, pebiPvtPara->getRsw());
assignPvtVectorIfNotEmpty(p0.PVT.Bw, pebiPvtPara->getBw());
assignPvtVectorIfNotEmpty(p0.PVT.Cw, pebiPvtPara->getCw());
assignPvtVectorIfNotEmpty(p0.PVT.miuw, pebiPvtPara->getMiuw());
assignPvtVectorIfNotEmpty(p0.PVT.rouw, pebiPvtPara->getRouw());
// 读取PVT数据 - 其他参数
assignPvtVectorIfNotEmpty(p0.PVT.V, pebiPvtPara->getV());
assignPvtVectorIfNotEmpty(p0.PVT.k_kinitial, pebiPvtPara->getKKinitial());
assignPvtVectorIfNotEmpty(p0.PVT.Cf_Cfinitial, pebiPvtPara->getCfCfinitial());
// 读取PVT数据 - 饱和度与相对渗透率
assignPvtVectorIfNotEmpty(p0.PVT.So, pebiPvtPara->getSo());
assignPvtVectorIfNotEmpty(p0.PVT.Kro, pebiPvtPara->getKro());
assignPvtVectorIfNotEmpty(p0.PVT.Sg, pebiPvtPara->getSg());
assignPvtVectorIfNotEmpty(p0.PVT.Krg, pebiPvtPara->getKrg());
assignPvtVectorIfNotEmpty(p0.PVT.Sw, pebiPvtPara->getSw());
assignPvtVectorIfNotEmpty(p0.PVT.Krw, pebiPvtPara->getKrw());
}
//emit sigResSolverProgressUpdated(4);
// 基础数据(储层参数?)
if(pReservoirData != nullptr) {
p0.Base.Pi = pReservoirData->getInitialPressure().getValue().toDouble(); // 初始压力
p0.Base.Cti = pReservoirData->getCt().getValue().toDouble(); // 综合压缩系数
p0.Base.Cf = pReservoirData->getCf().getValue().toDouble(); // 岩石压缩系数
p0.Base.Soi = pReservoirData->getSoi().getValue().toDouble(); // 初始含油饱和度
p0.Base.Sgi = pReservoirData->getSgi().getValue().toDouble(); // 初始含气饱和度
p0.Base.Swi = pReservoirData->getSwi().getValue().toDouble(); // 初始含水饱和度
// 根据网格单元数设置每个单元的参数
size_t cellCount = pGridInstance->getGridOutput2().Trinodexy.size();
p0.Base.k = dVec1(cellCount, pReservoirData->getPermeability().getValue().toDouble());
p0.Base.phi = dVec1(cellCount, pReservoirData->getPorosity().getValue().toDouble());
p0.Base.h = dVec1(cellCount, pReservoirData->getThickness().getValue().toDouble());
}
// 获取时间步长
nmDataTimeStepSetting* pTimeStepSetting = pDataInstance->getTimeStep();
if(pTimeStepSetting) {
p0.Base.d = pTimeStepSetting->getTimeGrowthExponent().getValue().toDouble();
p0.Base.dt_Min = pTimeStepSetting->getMinDeltaTAttribute().getValue().toDouble();
p0.Base.dt_Max = pTimeStepSetting->getMaxDeltaTAttribute().getValue().toDouble();
}
//emit sigResSolverProgressUpdated(5);
// 模拟进度条更新
// 在调用 HX_NWTM_MODEL 前启动一个线程或 QFutureWatcher 模拟进度
//QFuture<void> future = QtConcurrent::run([this]() {
// for(int i = 6; i <= 95; ++i) {
// QThread::msleep(500); // 模拟耗时
// if (!isRunning()){
// return;
// }
// emit sigResSolverProgressUpdated(i);
// }
//});
try {
nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getCurrentInstance();
QString licensePath = pDataManager->getLicensePath();
std::string licensePathStd = licensePath.toStdString();
HX_NWTM_MODEL(p1, p0, licensePathStd);
} catch(const std::exception& e) {
// 捕获 C++ 标准异常
qDebug() << QString("C++ Exception during HX_NWTM_MODEL call: %1").arg(e.what());
// 输出到日志窗口
logHX_NWTM_MODEL_INPUT_Simplified(p0);
// 生成一个带有时间戳的文件名
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
QString logFileName = QString("AAA_HX_NWTM_MODEL_INPUT_error_%1.txt").arg(timestamp);
QString appDirPath = QCoreApplication::applicationDirPath();
QString logFilePath = QDir(appDirPath).filePath(logFileName);
// 将 p0 的内容写入文件
this->saveHX_NWTM_MODEL_INPUT_ToTxt(p0, logFilePath);
FreeLibrary(dll); // 异常发生,释放 DLL
return false;
} catch(...) {
// 捕获结构化异常 (SEH)
qDebug() << "SEH Exception Occurred during HX_NWTM_MODEL call";
// 输出到日志窗口
logHX_NWTM_MODEL_INPUT_Simplified(p0);
// 生成一个带有时间戳的文件名
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");
QString logFileName = QString("AAA_HX_NWTM_MODEL_INPUT_error_%1.txt").arg(timestamp);
QString appDirPath = QCoreApplication::applicationDirPath();
QString logFilePath = QDir(appDirPath).filePath(logFileName);
// 将 p0 的内容写入文件
this->saveHX_NWTM_MODEL_INPUT_ToTxt(p0, logFilePath);
FreeLibrary(dll); // 异常发生,释放 DLL
return false;
}
// 求解完成后停止定时器并直接设置进度到%
//emit sigResSolverProgressUpdated(97);
// 保存计算结果
bool success = this->savePebiModeResult(p1);
FreeLibrary(dll); // 卸载 DLL
return success;
} else {
DWORD error = GetLastError();
printf("Failed to resolve HX_NWTM_GRID function. Error code: %lu\n", error);
}
FreeLibrary(dll); // 卸载 DLL
} else {
DWORD error = GetLastError();
printf("Failed to load DLL. Error code: %lu\n", error);
}
return false;
}
std::vector<double> HX_logderivative(const std::vector<double>& x, const std::vector<double>& y, const int& n)
{
// 功能: 对数导数函数
// 作者: 何辉
// 日期: 2024.07.16
// 单位: 西安华线石油科技有限公司(西安石油大学)
std::vector<double> d; d.resize(n - 1);
d[0] = 0.5 * (y[1] - y[0]) / (x[1] - x[0]) * (x[1] + x[0]);
d[0] = max(d[0], DBL_EPSILON);
for (int i = 1; i < n - 2; ++i)
{
d[i] = (y[i - 1] * (x[i] - x[i + 1]) / ((x[i - 1] - x[i]) * (x[i - 1] - x[i + 1])) +
y[i] * (2 * x[i] - x[i - 1] - x[i + 1]) / ((x[i] - x[i - 1]) * (x[i] - x[i + 1])) +
y[i + 1] * (x[i] - x[i - 1]) / ((x[i - 1] - x[i + 1]) * (x[i] - x[i + 1]))) * x[i];
d[i] = max(d[i], DBL_EPSILON);
}
d[n - 2] = 0.5 * (y[n - 2] - y[n - 3]) / (x[n - 2] - x[n - 3]) * (x[n - 2] + x[n - 3]);
d[n - 2] = max(d[n - 2], DBL_EPSILON);
return d;
}
bool nmCalculationDllPebiSolverTask::savePebiModeResult(HX_NWTM_MODEL_OUTPUT& p1)
{
nmDataAnalyzeManager* pDataInstance = nmDataAnalyzeManager::getCurrentInstance();
QVector<QVector<double>> vvecPressure;
QVector<QVector<double>> vvecLogLog;
QVector<QVector<double>> vvecSemiLog;
// 获取参与求解的井的顺序
QVector<QPair<NM_WELL_MODEL, QString>> vecWellsOrder = nmDataAnalyzeManager::getCurrentInstance()->getCalculationWells();
// 清空井名和二维位置的映射
pDataInstance->clearWellLocations();
// 遍历每口井,处理其数据
for(int wellIdx = 0; wellIdx < vecWellsOrder.size(); ++wellIdx) {
NM_WELL_MODEL eWellType = vecWellsOrder[wellIdx].first; // 获取井的类型
QString sWellName = vecWellsOrder[wellIdx].second; // 获取井的名称
// 跳过裂缝(或未知井类型)
if(eWellType == NM_WELL_MODEL::Unknow_Well) {
continue;
}
// 3.1 填充井底压力数据到局部变量
QVector<double> currentWellTime;
QVector<double> currentWellPressure;
// 确保 p1.pw[wellIdx] 存在且大小与 p1.t 匹配
if(wellIdx < p1.pw.size()) {
for(size_t i = 0; i < p1.pw[wellIdx].size(); ++i) {
if(i < p1.t.size()) { // 确保时间数据也存在
currentWellTime.append(p1.t[i]);
currentWellPressure.append(p1.pw[wellIdx][i]);
}
}
}
vvecPressure.clear(); // 清空上次循环的数据,为当前井准备
vvecPressure.append(currentWellTime);
vvecPressure.append(currentWellPressure);
// 3.2 计算双对数和半对数曲线数据并存储到局部变量
nmDataWellBase* pWellData = nmDataAnalyzeManager::getCurrentInstance()->findWellByName(sWellName);
if(pWellData == nullptr) {
continue;
}
{
// 准备压力数据 (用于传递给外部 DLL)
std::vector<Point> wellPressureDataForDll;
if(wellIdx < p1.pw.size()) {
for(size_t i = 0; i < p1.pw[wellIdx].size(); ++i) {
Point pt;
if(i < p1.t.size()) {
pt.x = p1.t[i];
} else {
pt.x = 0.0; // 或者进行错误处理
}
pt.y = p1.pw[wellIdx][i];
pt.z = 0.0; // 假设这里的 z 是 0
wellPressureDataForDll.push_back(pt);
}
}
// 准备流量段数据
QVector<QPointF> vecTimeQ = pWellData->getFlowPoints();
int nTimeNumQ = vecTimeQ.size() - 1; // 移除第一个 0 点
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> logPreResultFromDll; // 存储 DLL 的计算结果
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 false;
}
preLogFun(wellPressureDataForDll, iSectionFlowIndex, timeQ.data(), q.data(), nTimeNumQ, logPreResultFromDll);
// 填充双对数曲线数据到局部变量
QVector<double> logX, logY, logZ;
// 检查结果是否为空,并跳过第一个点
if (logPreResultFromDll.size() > 1) {
// 从索引 1 开始遍历,跳过索引 0 的第一个点
for (size_t i = 1; i < logPreResultFromDll.size(); ++i) {
const auto& pt = logPreResultFromDll[i];
if(!isValidLogLogPoint(pt)) {
continue;
}
logX.append(pt.x);
logY.append(pt.y);
logZ.append(pt.z);
}
}
vvecLogLog.clear(); // 清空上次循环的数据
vvecLogLog.append(logX);
vvecLogLog.append(logY);
vvecLogLog.append(logZ);
// 填充半对数曲线数据到局部变量
QVector<double> semiLogX, semiLogY;
// 检查结果是否为空,并跳过第一个点
if (logPreResultFromDll.size() > 1) {
// 半对数曲线也应该同步跳过第一个点
for (size_t i = 1; i < logPreResultFromDll.size(); ++i) {
const auto& pt = logPreResultFromDll[i];
if(!isValidSemiLogPoint(pt)) {
continue;
}
semiLogX.append(pt.x);
semiLogY.append(pt.pointData[0]);
}
}
vvecSemiLog.clear(); // 清空上次循环的数据
vvecSemiLog.append(semiLogX);
vvecSemiLog.append(semiLogY);
FreeLibrary(hMod_solver);
}
}
// 将计算结果保存到对应的井数据里
// 压力
pWellData->setResultPressure(vvecPressure);
// 双对数
pWellData->setResultLogLog(vvecLogLog);
// 半对数
pWellData->setResultSemiLog(vvecSemiLog);
// 存储当前井名称和二维位置到映射
QPointF ptWellCoords(pWellData->getX().getValue().toDouble(), pWellData->getY().getValue().toDouble());
pDataInstance->addWellLocation(sWellName, ptWellCoords);
}
// 计算有效单元数量
nmCalculationPebiGrid* pGridInstance = nmCalculationPebiGrid::getInstance();
HX_NWTM_GRID_OUTPUT1 P1_grid = pGridInstance->getGridOutput1();
size_t actualPlotCellsCount = 0;
for(size_t i = 0; i < P1_grid.PEBI_cell.isplot.size(); ++i) {
if(P1_grid.PEBI_cell.isplot[i] == 1) {
actualPlotCellsCount++;
}
}
// 在保存时间步数据前,先计算标量范围
double dMinP = DBL_MAX;
double dMaxP = -DBL_MAX;
// 4. 为每个时间步保存压力场到 nmDataAnalyzeManager 的 QMap 中
for(size_t timeIdx = 0; timeIdx < p1.p.size(); ++timeIdx) {
// 获取当前时间步的时间值
double currentTime = p1.t[timeIdx];
// 创建 vtkDoubleArray 来存储当前时间步的压力数据
vtkSmartPointer<vtkDoubleArray> pressureData = vtkSmartPointer<vtkDoubleArray>::New();
pressureData->SetName("p"); // 设置标量数据的名称
// 直接使用外面计算好的 actualPlotCellsCount
pressureData->SetNumberOfValues(actualPlotCellsCount);
size_t destIdx = 0; // 目标数组的索引
double dCurrentTimeMin = DBL_MAX; // 当前时间步的最小值
double dCurrentTimeMax = -DBL_MAX; // 当前时间步的最大值
// 遍历原始数据,并根据 isplot 填充到 pressureData
for(size_t i = 0; i < P1_grid.PEBI_cell.isplot.size(); ++i) {
if(P1_grid.PEBI_cell.isplot[i] == 1) { // 只有当 isplot 为1时才考虑这个单元格
if(timeIdx < p1.p.size() && i < p1.p[timeIdx].size()) {
pressureData->SetValue(destIdx, p1.p[timeIdx][i]);
// 更新当前时间步的范围
dCurrentTimeMin = qMin(dCurrentTimeMin, p1.p[timeIdx][i]);
dCurrentTimeMax = qMax(dCurrentTimeMax, p1.p[timeIdx][i]);
} else {
// 处理数据不匹配的情况,例如设置一个默认值或报错
qWarning() << "Data mismatch: timeIdx or cell index out of bounds for time step" << timeIdx << " cell " << i;
pressureData->SetValue(destIdx, 0.0); // 设置一个默认值
}
destIdx++;
}
}
// 更新全局范围
dMinP = qMin(dMinP, dCurrentTimeMin);
dMaxP = qMax(dMaxP, dCurrentTimeMax);
// 将时间与对应的压力数据存储到 nmDataAnalyzeManager
pDataInstance->addTimeStep(currentTime, pressureData);
}
// 保存全局范围到管理器
pDataInstance->setScalarRangeP(dMinP, dMaxP);
return true;
}
//bool nmCalculationDllPebiSolverTask::savePebiModeResult(HX_NWTM_MODEL_OUTPUT& p1)
//{
// nmDataAnalyzeManager* pDataInstance = nmDataAnalyzeManager::getCurrentInstance();
//
// QVector<QVector<double>> vvecPressure;
// QVector<QVector<double>> vvecLogLog;
// QVector<QVector<double>> vvecSemiLog;
//
// // 获取参与求解的井的顺序
// QVector<QPair<NM_WELL_MODEL, QString>> vecWellsOrder = nmDataAnalyzeManager::getCurrentInstance()->getCalculationWells();
//
// // 清空井名和二维位置的映射
// pDataInstance->clearWellLocations();
//
// // 遍历每口井,处理其数据
// for(int wellIdx = 0; wellIdx < vecWellsOrder.size(); ++wellIdx) {
// NM_WELL_MODEL eWellType = vecWellsOrder[wellIdx].first; // 获取井的类型
// QString sWellName = vecWellsOrder[wellIdx].second; // 获取井的名称
//
// // 跳过裂缝(或未知井类型)
// if(eWellType == NM_WELL_MODEL::Unknow_Well) {
// continue;
// }
//
// // 3.1 填充井底压力数据到局部变量
// QVector<double> currentWellTime;
// QVector<double> currentWellPressure; // P_wf(t)
//
// // 确保 p1.pw[wellIdx] 存在且大小与 p1.t 匹配
// if(wellIdx < p1.pw.size()) {
// for(size_t i = 0; i < p1.pw[wellIdx].size(); ++i) {
// if(i < p1.t.size()) { // 确保时间数据也存在
// currentWellTime.append(p1.t[i]);
// currentWellPressure.append(p1.pw[wellIdx][i]);
// }
// }
// }
//
// vvecPressure.clear(); // 清空上次循环的数据,为当前井准备
// vvecPressure.append(currentWellTime);
// vvecPressure.append(currentWellPressure);
//
// // 3.2 计算双对数和半对数曲线数据并存储到局部变量
// nmDataWellBase* pWellData = nmDataAnalyzeManager::getCurrentInstance()->findWellByName(sWellName);
//
// if(pWellData) {
//
// // --- START: 替换为 HX_logderivative 计算双对数数据 ---
//
// // 1. 获取初始压力 Pi用于计算压力降 DeltaP
// double initialPressure = nmDataAnalyzeManager::getCurrentInstance()->getReservoirData()->getInitialPressure().getValue().toDouble();
//
// // 为了演示,如果无法获取 Pi我们将使用数组中第一个压力点作为初始压力 Pi
// //if (!currentWellPressure.isEmpty()) {
// // // 通常第一个压力点 Pwf(t=0) 就是初始压力 Pi
// // initialPressure = currentWellPressure.first();
// //}
//
// // 2. 准备 t (x) 和 DeltaP (y) 的 std::vector
// std::vector<double> t_vec; // 时间 t (x)
// std::vector<double> deltaP_vec; // 压力降 DeltaP = Pi - Pwf(t) (y)
// int N = currentWellTime.size();
//
// for (int i = 0; i < N; ++i) {
// t_vec.push_back(currentWellTime[i]);
// // 计算压力降
// deltaP_vec.push_back(initialPressure - currentWellPressure[i]);
// }
//
// // 3. 调用 HX_logderivative 计算对数导数 (P')
// std::vector<double> logDerivative_vec;
//
// if (N > 2) { // 确保有足够的数据点 (N-1 个导数点)
// logDerivative_vec = HX_logderivative(t_vec, deltaP_vec, N);
// } else {
// // 数据点不足,无法计算导数
// logDerivative_vec.clear();
// }
//
// // 4. 组装双对数曲线数据 (t, DeltaP, P'),严格遵循绘图规则
// QVector<double> logX, logY, logZ; // logX: t_plot, logY: DeltaP_plot, logZ: P'
// size_t M = logDerivative_vec.size(); // 导数点数量 M = N - 1
//
// if (M >= 1) {
//
// // --- 4.1 第一个点 (索引 i=0) ---
// // 导数时间: t_plot = 0.5 * (x[1] + x[0])
// logX.append(0.5 * (t_vec[1] + t_vec[0]));
// // 压力降 (近似): DeltaP[0] 和 DeltaP[1] 的平均值
// logY.append((deltaP_vec[0] + deltaP_vec[1]) / 2.0);
// // 导数: d[0]
// logZ.append(logDerivative_vec[0]);
//
//
// // --- 4.2 中间点 (索引 i=1 到 N-3) ---
// // 在 logDerivative_vec 中,这些点对应索引 i=1 到 M-1 (即 N-3)
// for (size_t i = 1; i < (size_t)N - 2; ++i) {
// // 导数时间: t_plot = x[i]
// logX.append(t_vec[i]);
// // 压力降: 匹配原始数据 DeltaP[i]
// logY.append(deltaP_vec[i]);
// // 导数: d[i]
// logZ.append(logDerivative_vec[i]);
// }
//
// // --- 4.3 最后一个点 (索引 i=N-2) ---
// if (M >= 2) { // 确保至少有 2 个导数点
// size_t lastIdx = M - 1; // 对应 logDerivative_vec[N-2]
// // 导数时间: t_plot = 0.5 * (x[n-2] + x[n-3])
// // 对应 t_vec[N-2] 和 t_vec[N-3]
// logX.append(0.5 * (t_vec[N - 2] + t_vec[N - 3]));
//
// // 压力降 (近似): DeltaP[N-2] 和 DeltaP[N-3] 的平均值
// logY.append((deltaP_vec[N - 2] + deltaP_vec[N - 3]) / 2.0);
//
// // 导数: d[N-2]
// logZ.append(logDerivative_vec[lastIdx]);
// }
// }
//
// // 将组装好的数据存储到 vvecLogLog
// vvecLogLog.clear(); // 清空上次循环的数据
// vvecLogLog.append(logX); // 时间 (t_plot)
// vvecLogLog.append(logY); // 压力降 (DeltaP_plot)
// vvecLogLog.append(logZ); // 对数导数 (P')
//
//
// // 5. 组装半对数曲线数据
// // 半对数图 Y 轴是井底压力 Pwf(t)X 轴是时间 t
// // vvecSemiLog 存储 [t, Pwf(t)],使用原始数据即可
// vvecSemiLog.clear();
// vvecSemiLog.append(currentWellTime); // X轴: 时间 t
// vvecSemiLog.append(currentWellPressure); // Y轴: 井底压力 Pwf(t)
//
// // --- END: 替换为 HX_logderivative 计算双对数数据 ---
//
// } // end if(pWellData)
//
// // 将计算结果保存到对应的井数据里
// // 压力
// pWellData->setResultPressure(vvecPressure);
// // 双对数
// pWellData->setResultLogLog(vvecLogLog);
// // 半对数
// pWellData->setResultSemiLog(vvecSemiLog);
//
// // 存储当前井名称和二维位置到映射
// QPointF ptWellCoords(pWellData->getX().getValue().toDouble(), pWellData->getY().getValue().toDouble());
// pDataInstance->addWellLocation(sWellName, ptWellCoords);
// }
// 主日志函数:打印 HX_NWTM_MODEL_INPUT 的内容
void nmCalculationDllPebiSolverTask::logHX_NWTM_MODEL_INPUT_Simplified(const HX_NWTM_MODEL_INPUT& p0)
{
// 1. Basic Parameters (T)
qDebug() << QString("Parameter T: %1").arg(p0.T);
// 2. GRID Data
qDebug() << "\n--- GRID Data ---";
qDebug() << QString("GRID.Trinodexy: Capacity %1").arg(p0.GRID.Trinodexy.size());
qDebug() << QString("GRID.Area: Capacity %1").arg(p0.GRID.Area.size());
qDebug() << QString("GRID.D: Capacity %1").arg(p0.GRID.D.size());
// 2.1 GRID.ZhiJingNeiBianJie
qDebug() << "\n -- GRID.ZhiJingNeiBianJie --";
qDebug() << QString(" n: %1").arg(p0.GRID.ZhiJingNeiBianJie.n);
qDebug() << QString(" XiLinw: Capacity %1").arg(p0.GRID.ZhiJingNeiBianJie.XiLinw.size());
qDebug() << QString(" lw: Capacity %1").arg(p0.GRID.ZhiJingNeiBianJie.lw.size());
qDebug() << QString(" dw: Capacity %1").arg(p0.GRID.ZhiJingNeiBianJie.dw.size());
qDebug() << QString(" rw: Capacity %1").arg(p0.GRID.ZhiJingNeiBianJie.rw.size());
qDebug() << QString(" inwell: Capacity %1").arg(p0.GRID.ZhiJingNeiBianJie.inwell.size());
// 2.2 GRID.LieFengJingNeiBianJie
qDebug() << "\n -- GRID.LieFengJingNeiBianJie --";
qDebug() << QString(" n: %1").arg(p0.GRID.LieFengJingNeiBianJie.n);
qDebug() << QString(" XiLinf: Capacity %1").arg(p0.GRID.LieFengJingNeiBianJie.XiLinf.size());
qDebug() << QString(" lf: Capacity %1").arg(p0.GRID.LieFengJingNeiBianJie.lf.size());
qDebug() << QString(" df: Capacity %1").arg(p0.GRID.LieFengJingNeiBianJie.df.size());
qDebug() << QString(" xf: Capacity %1").arg(p0.GRID.LieFengJingNeiBianJie.xf.size());
qDebug() << QString(" infra: Capacity %1").arg(p0.GRID.LieFengJingNeiBianJie.infra.size());
// 2.3 GRID.DuoJiYaLieShuiPingJingNeiBianJie
qDebug() << "\n -- GRID.DuoJiYaLieShuiPingJingNeiBianJie --";
qDebug() << QString(" n: %1").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.n);
qDebug() << QString(" XiLinh: Capacity %1").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.XiLinh.size());
qDebug() << QString(" lh: Capacity %1").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.lh.size());
qDebug() << QString(" dh: Capacity %1").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.dh.size());
qDebug() << QString(" dsxf: Capacity %1").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.dsxf.size());
qDebug() << QString(" inhor: Capacity %1").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.inhor.size());
// 2.4 GRID.WaiBianJie
qDebug() << "\n -- GRID.WaiBianJie --";
qDebug() << QString(" n: %1").arg(p0.GRID.WaiBianJie.n);
qDebug() << QString(" WaiBianh: Capacity %1").arg(p0.GRID.WaiBianJie.WaiBianh.size());
qDebug() << QString(" WaiBianl: Capacity %1").arg(p0.GRID.WaiBianJie.WaiBianl.size());
qDebug() << QString(" WaiBiand: Capacity %1").arg(p0.GRID.WaiBianJie.WaiBiand.size());
// 2.5 GRID.NeiBuDuanCeng
qDebug() << "\n -- GRID.NeiBuDuanCeng --";
qDebug() << QString(" n: %1").arg(p0.GRID.NeiBuDuanCeng.n);
qDebug() << QString(" faultb1: Capacity %1").arg(p0.GRID.NeiBuDuanCeng.faultb1.size());
qDebug() << QString(" faultb2: Capacity %1").arg(p0.GRID.NeiBuDuanCeng.faultb2.size());
qDebug() << QString(" faultl1: Capacity %1").arg(p0.GRID.NeiBuDuanCeng.faultl1.size());
qDebug() << QString(" faultd1: Capacity %1").arg(p0.GRID.NeiBuDuanCeng.faultd1.size());
// 2.6 GRID.YuChuLiJuZhen
qDebug() << "\n -- GRID.YuChuLiJuZhen --";
qDebug() << QString(" ia: Capacity %1").arg(p0.GRID.YuChuLiJuZhen.ia.size());
qDebug() << QString(" ja: Capacity %1").arg(p0.GRID.YuChuLiJuZhen.ja.size());
qDebug() << QString(" nzeros: Capacity %1").arg(p0.GRID.YuChuLiJuZhen.nzeros.size());
qDebug() << QString(" numk: %1").arg(p0.GRID.YuChuLiJuZhen.numk);
// 3. Rate Data
qDebug() << "\n--- Rate Data ---";
qDebug() << QString("Rate.t: Contains %1 data sets").arg(p0.Rate.t.size());
qDebug() << QString("Rate.qo: Contains %1 data sets").arg(p0.Rate.qo.size());
qDebug() << QString("Rate.qg: Contains %1 data sets").arg(p0.Rate.qg.size());
qDebug() << QString("Rate.qw: Contains %1 data sets").arg(p0.Rate.qw.size());
// Optional: Print capacity of each inner vector, e.g.:
// for (int i = 0; i < p0.Rate.t.size(); ++i) {
// zxLogInstance::getInstance()->writeLogF(QString(" Rate.t[%1] Capacity: %2").arg(i).arg(p0.Rate.t[i].size()));
// }
// 4. Pressure Data
qDebug() << "\n--- Pressure Data ---";
qDebug() << QString("Pressure.t: Contains %1 data sets").arg(p0.Pressure.t.size());
qDebug() << QString("Pressure.p: Contains %1 data sets").arg(p0.Pressure.p.size());
// 5. CS Wellbore Storage and Skin Data
qDebug() << "\n--- CS Data ---";
qDebug() << QString("CS.C: Capacity %1").arg(p0.CS.C.size());
qDebug() << QString("CS.S: Capacity %1").arg(p0.CS.S.size());
// 6. PVT Fluid Property Data
qDebug() << "\n--- PVT Data ---";
qDebug() << QString("PVT.p: Capacity %1").arg(p0.PVT.p.size());
qDebug() << QString("PVT.Rso: Capacity %1").arg(p0.PVT.Rso.size());
qDebug() << QString("PVT.Bo: Capacity %1").arg(p0.PVT.Bo.size());
qDebug() << QString("PVT.Co: Capacity %1").arg(p0.PVT.Co.size());
qDebug() << QString("PVT.miuo: Capacity %1").arg(p0.PVT.miuo.size());
qDebug() << QString("PVT.rouo: Capacity %1").arg(p0.PVT.rouo.size());
qDebug() << QString("PVT.Rv: Capacity %1").arg(p0.PVT.Rv.size());
qDebug() << QString("PVT.Bg: Capacity %1").arg(p0.PVT.Bg.size());
qDebug() << QString("PVT.Cg: Capacity %1").arg(p0.PVT.Cg.size());
qDebug() << QString("PVT.miug: Capacity %1").arg(p0.PVT.miug.size());
qDebug() << QString("PVT.roug: Capacity %1").arg(p0.PVT.roug.size());
qDebug() << QString("PVT.Z: Capacity %1").arg(p0.PVT.Z.size());
qDebug() << QString("PVT.Rsw: Capacity %1").arg(p0.PVT.Rsw.size());
qDebug() << QString("PVT.Bw: Capacity %1").arg(p0.PVT.Bw.size());
qDebug() << QString("PVT.Cw: Capacity %1").arg(p0.PVT.Cw.size());
qDebug() << QString("PVT.miuw: Capacity %1").arg(p0.PVT.miuw.size());
qDebug() << QString("PVT.rouw: Capacity %1").arg(p0.PVT.rouw.size());
qDebug() << QString("PVT.V: Capacity %1").arg(p0.PVT.V.size());
qDebug() << QString("PVT.k_kinitial: Capacity %1").arg(p0.PVT.k_kinitial.size());
qDebug() << QString("PVT.Cf_Cfinitial: Capacity %1").arg(p0.PVT.Cf_Cfinitial.size());
qDebug() << QString("PVT.So: Capacity %1").arg(p0.PVT.So.size());
qDebug() << QString("PVT.Kro: Capacity %1").arg(p0.PVT.Kro.size());
qDebug() << QString("PVT.Sg: Capacity %1").arg(p0.PVT.Sg.size());
qDebug() << QString("PVT.Krg: Capacity %1").arg(p0.PVT.Krg.size());
qDebug() << QString("PVT.Sw: Capacity %1").arg(p0.PVT.Sw.size());
qDebug() << QString("PVT.Krw: Capacity %1").arg(p0.PVT.Krw.size());
// 7. Base Data
qDebug() << "\n--- Base Data ---";
qDebug() << QString("Base.Pi: %1").arg(p0.Base.Pi);
qDebug() << QString("Base.Cti: %1").arg(p0.Base.Cti);
qDebug() << QString("Base.Cf: %1").arg(p0.Base.Cf);
qDebug() << QString("Base.Soi: %1").arg(p0.Base.Soi);
qDebug() << QString("Base.Sgi: %1").arg(p0.Base.Sgi);
qDebug() << QString("Base.Swi: %1").arg(p0.Base.Swi);
qDebug() << QString("Base.k: Capacity %1").arg(p0.Base.k.size());
qDebug() << QString("Base.phi: Capacity %1").arg(p0.Base.phi.size());
qDebug() << QString("Base.h: Capacity %1").arg(p0.Base.h.size());
qDebug() << "\n--- End of HX_NWTM_MODEL_INPUT Content ---";
}
bool nmCalculationDllPebiSolverTask::saveHX_NWTM_MODEL_INPUT_ToTxt(const HX_NWTM_MODEL_INPUT& p0, const QString& filePath)
{
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
// Log an error if the file cannot be opened
qDebug() << QString("ERROR: Could not open file for writing: %1").arg(filePath);
return false;
}
QTextStream out(&file);
out << "--- Start Printing HX_NWTM_MODEL_INPUT Content ---\n";
// 1. Basic Parameters (T)
out << QString("Parameter T: %1\n").arg(p0.T);
// 2. GRID Data
out << "\n--- GRID Data ---\n";
out << QString("GRID.Trinodexy: Capacity %1\n").arg(p0.GRID.Trinodexy.size());
out << QString("GRID.Area: Capacity %1\n").arg(p0.GRID.Area.size());
out << QString("GRID.D: Capacity %1\n").arg(p0.GRID.D.size());
// 2.1 GRID.ZhiJingNeiBianJie
out << "\n -- GRID.ZhiJingNeiBianJie --\n";
out << QString(" n: %1\n").arg(p0.GRID.ZhiJingNeiBianJie.n);
out << QString(" XiLinw: Capacity %1\n").arg(p0.GRID.ZhiJingNeiBianJie.XiLinw.size());
out << QString(" lw: Capacity %1\n").arg(p0.GRID.ZhiJingNeiBianJie.lw.size());
out << QString(" dw: Capacity %1\n").arg(p0.GRID.ZhiJingNeiBianJie.dw.size());
out << QString(" rw: Capacity %1\n").arg(p0.GRID.ZhiJingNeiBianJie.rw.size());
out << QString(" inwell: Capacity %1\n").arg(p0.GRID.ZhiJingNeiBianJie.inwell.size());
// 2.2 GRID.LieFengJingNeiBianJie
out << "\n -- GRID.LieFengJingNeiBianJie --\n";
out << QString(" n: %1\n").arg(p0.GRID.LieFengJingNeiBianJie.n);
out << QString(" XiLinf: Capacity %1\n").arg(p0.GRID.LieFengJingNeiBianJie.XiLinf.size());
out << QString(" lf: Capacity %1\n").arg(p0.GRID.LieFengJingNeiBianJie.lf.size());
out << QString(" df: Capacity %1\n").arg(p0.GRID.LieFengJingNeiBianJie.df.size());
out << QString(" xf: Capacity %1\n").arg(p0.GRID.LieFengJingNeiBianJie.xf.size());
out << QString(" infra: Capacity %1\n").arg(p0.GRID.LieFengJingNeiBianJie.infra.size());
// 2.3 GRID.DuoJiYaLieShuiPingJingNeiBianJie
out << "\n -- GRID.DuoJiYaLieShuiPingJingNeiBianJie --\n";
out << QString(" n: %1\n").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.n);
out << QString(" XiLinh: Capacity %1\n").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.XiLinh.size());
out << QString(" lh: Capacity %1\n").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.lh.size());
out << QString(" dh: Capacity %1\n").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.dh.size());
out << QString(" dsxf: Capacity %1\n").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.dsxf.size());
out << QString(" inhor: Capacity %1\n").arg(p0.GRID.DuoJiYaLieShuiPingJingNeiBianJie.inhor.size());
// 2.4 GRID.WaiBianJie
out << "\n -- GRID.WaiBianJie --\n";
out << QString(" n: %1\n").arg(p0.GRID.WaiBianJie.n);
out << QString(" WaiBianh: Capacity %1\n").arg(p0.GRID.WaiBianJie.WaiBianh.size());
out << QString(" WaiBianl: Capacity %1\n").arg(p0.GRID.WaiBianJie.WaiBianl.size());
out << QString(" WaiBiand: Capacity %1\n").arg(p0.GRID.WaiBianJie.WaiBiand.size());
// 2.5 GRID.NeiBuDuanCeng
out << "\n -- GRID.NeiBuDuanCeng --\n";
out << QString(" n: %1\n").arg(p0.GRID.NeiBuDuanCeng.n);
out << QString(" faultb1: Capacity %1\n").arg(p0.GRID.NeiBuDuanCeng.faultb1.size());
out << QString(" faultb2: Capacity %1\n").arg(p0.GRID.NeiBuDuanCeng.faultb2.size());
out << QString(" faultl1: Capacity %1\n").arg(p0.GRID.NeiBuDuanCeng.faultl1.size());
out << QString(" faultd1: Capacity %1\n").arg(p0.GRID.NeiBuDuanCeng.faultd1.size());
// 2.6 GRID.YuChuLiJuZhen
out << "\n -- GRID.YuChuLiJuZhen --\n";
out << QString(" ia: Capacity %1\n").arg(p0.GRID.YuChuLiJuZhen.ia.size());
out << QString(" ja: Capacity %1\n").arg(p0.GRID.YuChuLiJuZhen.ja.size());
out << QString(" nzeros: Capacity %1\n").arg(p0.GRID.YuChuLiJuZhen.nzeros.size());
out << QString(" numk: %1\n").arg(p0.GRID.YuChuLiJuZhen.numk);
// 3. Rate Data
out << "\n--- Rate Data ---\n";
out << QString("Rate.t: Contains %1 data sets\n").arg(p0.Rate.t.size());
out << QString("Rate.qo: Contains %1 data sets\n").arg(p0.Rate.qo.size());
out << QString("Rate.qg: Contains %1 data sets\n").arg(p0.Rate.qg.size());
out << QString("Rate.qw: Contains %1 data sets\n").arg(p0.Rate.qw.size());
// Optional: Print capacity of each inner vector, e.g.:
// for (int i = 0; i < p0.Rate.t.size(); ++i) {
// out << QString(" Rate.t[%1] Capacity: %2\n").arg(i).arg(p0.Rate.t[i].size());
// }
// 4. Pressure Data
out << "\n--- Pressure Data ---\n";
out << QString("Pressure.t: Contains %1 data sets\n").arg(p0.Pressure.t.size());
out << QString("Pressure.p: Contains %1 data sets\n").arg(p0.Pressure.p.size());
// 5. CS Wellbore Storage and Skin Data
out << "\n--- CS Data ---\n";
out << QString("CS.C: Capacity %1\n").arg(p0.CS.C.size());
out << QString("CS.S: Capacity %1\n").arg(p0.CS.S.size());
// 6. PVT Fluid Property Data
out << "\n--- PVT Data ---\n";
out << QString("PVT.p: Capacity %1\n").arg(p0.PVT.p.size());
out << QString("PVT.Rso: Capacity %1\n").arg(p0.PVT.Rso.size());
out << QString("PVT.Bo: Capacity %1\n").arg(p0.PVT.Bo.size());
out << QString("PVT.Co: Capacity %1\n").arg(p0.PVT.Co.size());
out << QString("PVT.miuo: Capacity %1\n").arg(p0.PVT.miuo.size());
out << QString("PVT.rouo: Capacity %1\n").arg(p0.PVT.rouo.size());
out << QString("PVT.Rv: Capacity %1\n").arg(p0.PVT.Rv.size());
out << QString("PVT.Bg: Capacity %1\n").arg(p0.PVT.Bg.size());
out << QString("PVT.Cg: Capacity %1\n").arg(p0.PVT.Cg.size());
out << QString("PVT.miug: Capacity %1\n").arg(p0.PVT.miug.size());
out << QString("PVT.roug: Capacity %1\n").arg(p0.PVT.roug.size());
out << QString("PVT.Z: Capacity %1\n").arg(p0.PVT.Z.size());
out << QString("PVT.Rsw: Capacity %1\n").arg(p0.PVT.Rsw.size());
out << QString("PVT.Bw: Capacity %1\n").arg(p0.PVT.Bw.size());
out << QString("PVT.Cw: Capacity %1\n").arg(p0.PVT.Cw.size());
out << QString("PVT.miuw: Capacity %1\n").arg(p0.PVT.miuw.size());
out << QString("PVT.rouw: Capacity %1\n").arg(p0.PVT.rouw.size());
out << QString("PVT.V: Capacity %1\n").arg(p0.PVT.V.size());
out << QString("PVT.k_kinitial: Capacity %1\n").arg(p0.PVT.k_kinitial.size());
out << QString("PVT.Cf_Cfinitial: Capacity %1\n").arg(p0.PVT.Cf_Cfinitial.size());
out << QString("PVT.So: Capacity %1\n").arg(p0.PVT.So.size());
out << QString("PVT.Kro: Capacity %1\n").arg(p0.PVT.Kro.size());
out << QString("PVT.Sg: Capacity %1\n").arg(p0.PVT.Sg.size());
out << QString("PVT.Krg: Capacity %1\n").arg(p0.PVT.Krg.size());
out << QString("PVT.Sw: Capacity %1\n").arg(p0.PVT.Sw.size());
out << QString("PVT.Krw: Capacity %1\n").arg(p0.PVT.Krw.size());
// 7. Base Data
out << "\n--- Base Data ---\n";
out << QString("Base.Pi: %1\n").arg(p0.Base.Pi);
out << QString("Base.Cti: %1\n").arg(p0.Base.Cti);
out << QString("Base.Cf: %1\n").arg(p0.Base.Cf);
out << QString("Base.Soi: %1\n").arg(p0.Base.Soi);
out << QString("Base.Sgi: %1\n").arg(p0.Base.Sgi);
out << QString("Base.Swi: %1\n").arg(p0.Base.Swi);
out << QString("Base.k: Capacity %1\n").arg(p0.Base.k.size());
out << QString("Base.phi: Capacity %1\n").arg(p0.Base.phi.size());
out << QString("Base.h: Capacity %1\n").arg(p0.Base.h.size());
out << QString("Base.d: Capacity %1\n").arg(p0.Base.d);
out << QString("Base.dt_Max: Capacity %1\n").arg(p0.Base.dt_Max);
out << QString("Base.dt_Min: Capacity %1\n").arg(p0.Base.dt_Min);
out << "\n--- End of HX_NWTM_MODEL_INPUT Content ---\n";
file.close();
qDebug() << QString("Successfully saved HX_NWTM_MODEL_INPUT content to: %1").arg(filePath);
return true;
}