#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 #include #include #include #include #include #include #include #include #include #include #include namespace { // PVT数组读到时才覆盖,读不到时保留pch.h中的默认参数 void assignPvtVectorIfNotEmpty(dVec1& target, const QVector& source) { if(!source.isEmpty()) { target = dVec1(source.toStdVector()); } } 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]); } /// @brief 将常数PVT单值扩展为200元素数组(PEBI求解器要求数组输入) /// @param dValue 常数PVT值 /// @param nSize 数组大小,默认200 /// @return 包含nSize个dValue元素的dVec1数组 dVec1 expandConstPvt(double dValue, int nSize = 200) { return dVec1(nSize, dValue); } /// @brief 生成线性占位压力数组(常数PVT无曲线数据时使用) /// @param nSize 数组大小,默认200 /// @return [1, 2, ..., nSize] 的 dVec1 数组 dVec1 generateDummyPressure(int nSize = 200) { dVec1 vecP(nSize, 0); for(int i = 0; i < nSize; ++i) { vecP[i] = (i + 1.0); } return vecP; } /// @brief 根据求解器模型类型填充PVT输入数据 /// 常数PVT模型从 reservoir 读单值并扩展为200元素数组 /// 变化PVT模型从 pebiPvtPara 读曲线数组 /// @param input PEBI求解器输入结构 /// @param eModelType 求解器模型类型 /// @param pPvt PEBI PVT参数对象(变化PVT时提供曲线数组) /// @param pRes 油藏数据对象(常数PVT时提供单值) void fillPvtInputByModel(HX_NWTM_MODEL_INPUT& input, NM_SOLVER_MODEL_TYPE eModelType, nmDataPvtParaForPebi* pPvt, nmDataReservoir* pRes) { switch(eModelType) { case SMT_Oil_ConstPvt: // 油相常数PVT:不依赖pPvt,生成占位压力数组 + 单值扩展为200元素数组 input.PVT.p = generateDummyPressure(); if(pRes != nullptr) { input.PVT.Bo = expandConstPvt(pRes->getBo().getValue().toDouble()); input.PVT.miuo = expandConstPvt(pRes->getMiuo().getValue().toDouble()); } break; case SMT_Oil_VariablePvt: // 油相变化PVT:从 pebiPvtPara 读曲线数组 if(pPvt != nullptr) { assignPvtVectorIfNotEmpty(input.PVT.p, pPvt->getPressure()); assignPvtVectorIfNotEmpty(input.PVT.Bo, pPvt->getBo()); assignPvtVectorIfNotEmpty(input.PVT.Co, pPvt->getCo()); assignPvtVectorIfNotEmpty(input.PVT.miuo, pPvt->getMiuo()); } break; case SMT_Water_ConstPvt: // 水相常数PVT:不依赖pPvt,生成占位压力数组 + 单值扩展为200元素数组 input.PVT.p = generateDummyPressure(); if(pRes != nullptr) { input.PVT.Bw = expandConstPvt(pRes->getBw().getValue().toDouble()); input.PVT.miuw = expandConstPvt(pRes->getMiuw().getValue().toDouble()); } break; case SMT_Water_VariablePvt: // 水相变化PVT:从 pebiPvtPara 读曲线数组 if(pPvt != nullptr) { assignPvtVectorIfNotEmpty(input.PVT.p, pPvt->getPressure()); assignPvtVectorIfNotEmpty(input.PVT.Bw, pPvt->getBw()); assignPvtVectorIfNotEmpty(input.PVT.Cw, pPvt->getCw()); assignPvtVectorIfNotEmpty(input.PVT.miuw, pPvt->getMiuw()); } break; case SMT_Gas_VariablePvt: case SMT_Gas_PseudoPressure: // 气相变化PVT/拟压力:从 pebiPvtPara 读曲线数组 if(pPvt != nullptr) { assignPvtVectorIfNotEmpty(input.PVT.p, pPvt->getPressure()); assignPvtVectorIfNotEmpty(input.PVT.Bg, pPvt->getBg()); assignPvtVectorIfNotEmpty(input.PVT.Cg, pPvt->getCg()); assignPvtVectorIfNotEmpty(input.PVT.miug, pPvt->getMiug()); } break; case SMT_Oil_Water_TwoPhase: // 油水两相:从 pebiPvtPara 读油+水曲线数组 + 相渗数据 if(pPvt != nullptr) { assignPvtVectorIfNotEmpty(input.PVT.p, pPvt->getPressure()); assignPvtVectorIfNotEmpty(input.PVT.Bo, pPvt->getBo()); assignPvtVectorIfNotEmpty(input.PVT.miuo, pPvt->getMiuo()); assignPvtVectorIfNotEmpty(input.PVT.Bw, pPvt->getBw()); assignPvtVectorIfNotEmpty(input.PVT.miuw, pPvt->getMiuw()); // 相渗与饱和度数据(PEBI油水两相不需要PVT.Sw数组,初始含水饱和度通过Base.Swi传递) assignPvtVectorIfNotEmpty(input.PVT.So, pPvt->getSo()); assignPvtVectorIfNotEmpty(input.PVT.Kro, pPvt->getKro()); assignPvtVectorIfNotEmpty(input.PVT.Krw, pPvt->getKrw()); } break; default: break; } } } 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() { 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 || pDataInstance->getCalculationWells().isEmpty()) { // 网格输出存在但井顺序为空时,重新生成p2并同步当前DataManager的计算井顺序 if(!pGridInstance->generateOutputPara()) { FreeLibrary(dll); return false; } } // 参数定义,使用默认参数 HX_NWTM_MODEL_OUTPUT p1; HX_NWTM_MODEL_INPUT p0(pGridInstance->getGridOutput2()); nmDataReservoir* pReservoirData = pDataInstance->getReservoirData(); // 根据PVT参数获取结果选择求解器模型类型 p0.T = static_cast(pDataInstance->getSolverModelType()); // 获取PEBI网格存入井的顺序 QVector> 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()); // 遍历井数据 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(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 vecTimeQ = pWellData->getFlowPoints(); // 分离时间(x)和流量(y)数据 QVector timeDataF; QVector 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(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 vecTimeQ = pWellData->getFlowPoints(); // 分离时间(x)和流量(y)数据 QVector timeDataF; QVector 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(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 vecTimeQ = pWellData->getFlowPoints(); // 分离时间(x)和流量(y)数据 QVector timeDataF; QVector 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()); } } // PVT数据:根据求解器模型类型填充PVT输入数据 nmDataPvtParaForPebi* pebiPvtPara = pDataInstance->getPebiPvtPara(); fillPvtInputByModel(p0, static_cast(p0.T), pebiPvtPara, pReservoirData); // 基础数据(储层参数?) 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(); } 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; } // 保存计算结果 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 HX_logderivative(const std::vector& x, const std::vector& y, const int& n) { // 功能: 对数导数函数 // 作者: 何辉 // 日期: 2024.07.16 // 单位: 西安华线石油科技有限公司(西安石油大学) std::vector 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> vvecPressure; QVector> vvecLogLog; QVector> vvecSemiLog; // 获取参与求解的井的顺序 QVector> 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 currentWellTime; QVector 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 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 vecTimeQ = pWellData->getFlowPoints(); int nTimeNumQ = vecTimeQ.size() - 1; // 移除第一个 0 点 std::vector timeQ(nTimeNumQ); std::vector q(nTimeNumQ); for(int i = 0; i < nTimeNumQ; ++i) { timeQ[i] = vecTimeQ[i + 1].x(); q[i] = vecTimeQ[i + 1].y(); } // 调用外部 DLL 计算双对数曲线 std::vector logPreResultFromDll; // 存储 DLL 的计算结果 int iSectionFlowIndex = pWellData->getIndexF(); HMODULE hMod_solver = LoadLibrary(L"singlePhaseSolverDll.dll"); if(hMod_solver) { typedef bool (*PreLog)(const std::vector&, const int&, double*, double*, int, std::vector&); 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 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 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 pressureData = vtkSmartPointer::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> vvecPressure; // QVector> vvecLogLog; // QVector> vvecSemiLog; // // // 获取参与求解的井的顺序 // QVector> 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 currentWellTime; // QVector 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 t_vec; // 时间 t (x) // std::vector 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 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 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; }