|
|
#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());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
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<int>(pDataInstance->getSolverModelType());
|
|
|
|
|
|
// 获取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());
|
|
|
|
|
|
// 遍历井数据
|
|
|
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());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// PVT数据:根据求解器模型类型填充PVT输入数据
|
|
|
nmDataPvtParaForPebi* pebiPvtPara = pDataInstance->getPebiPvtPara();
|
|
|
fillPvtInputByModel(p0, static_cast<NM_SOLVER_MODEL_TYPE>(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<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;
|
|
|
}
|