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

1476 lines
42 KiB
C++

#include "nmDataWellBase.h"
#include "nmDataReservoir.h"
#include "nmDataAnalyzeManager.h"
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QCoreApplication>
#include <QDateTime>
nmDataWellBase::nmDataWellBase()
: m_nIndexF(0)
, m_bPlotVisible(true)
, m_bTimeDependentSkin(false)
, m_eWellType(Vertical_Well)
, m_dLastWellLength(0.0)
, m_pReservoir(nullptr)
{
m_pReservoir = nmDataAnalyzeManager::getCurrentInstance()->getReservoirData();
m_wellName = "";
m_x = nmDataAttribute("X", 0.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
m_y = nmDataAttribute("Y", 0.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
m_radius = nmDataAttribute("Radius", 0.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
m_drillFloorElevation = nmDataAttribute("Drill floor elevation", 0.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
m_zw = nmDataAttribute("Zw", 22.5, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
m_wellLength = nmDataAttribute("Well length", m_pReservoir->getThickness().getValue(), "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
m_rateDependentSkin = nmDataAttribute("Rate dependent skin", false, "");
m_dSdQ = nmDataAttribute("dS/dQ", 0.0, "1/B/D", UNIT_TYPE_FLOW_RATE_RECIPROCAL, QStringList(), QStringList() << "1/B/D" << "1/MMm^3/D" << "1/Mcf/D" << "1/Mm^3/D" << "1/Mm^3/hr"
<< "1/U.K. gal/hr" << "1/U.K. gal/min" << "1/U.S. gal/hr" << "1/U.S. gal/min" << "1/cf/D" << "1/cf/s" << "1/cm^3/sec" << "1/l/min" << "1/m^3/D" << "1/m^3/hr" << "1/m^3/min" << "1/m^3/sec");
m_wellboreModel = nmDataAttribute("Wellbore model", "Constant", "", UNIT_TYPE_DIMENSIONLESS, QStringList() << "None" << "Constant" << "Changing hegeman" << "Changing fair"
<< "Changing spivey packer" << "Changing spivey fissures", QStringList());
m_wellboreStorage = nmDataAttribute("Wellbore storage", 0.01, "m^3/MPa", UNIT_TYPE_COMPRESSIBILITY, QStringList(), QStringList() << "bbl/psi" << "m^3/bar" << "m^3/kPa" << "m^3/Pa" << "m^3.cm^2/kg" << "m^2" << "m^3/MPa");
m_bottomholeMD = nmDataAttribute("Bottomhole MD", 6000.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
// 初始化新添加的成员
m_inputWellHead = nmDataAttribute("Input well head", "", "");
m_wellHeadX = nmDataAttribute("Well head X", 0.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
m_wellHeadY = nmDataAttribute("Well head Y", 0.0, "m", UNIT_TYPE_LENGTH, QStringList(), QStringList() << "m" << "cm" << "mm" << "in" << "0.1 in" << "ft" << "mile" << "km");
m_finalWellboreStorage = nmDataAttribute("Final wellbore storage", 0.01, "bbl/psi", UNIT_TYPE_COMPRESSIBILITY, QStringList(), QStringList() << "bbl/psi" << "m^3/bar" << "m^3/kPa" << "m^3/Pa" << "m^3.cm^2/kg" << "m^2" << "m^3/MPa");
m_cInitialCFinal = nmDataAttribute("C[initial]/C[final]", 10.0, "");
m_dtChangingStorage = nmDataAttribute("Dt changing storage", 1.0, "hr", UNIT_TYPE_TIME, QStringList(), QStringList() << "ms" << "sec" << "min" << "hr" << "day" << "Week" << "Month" << "Year");
m_leakSkin = nmDataAttribute("Leak Skin", 0.0, "");
// 图元可见性默认为true
m_bPlotVisible = true;
m_bTimeDependentSkin = false;
// 添加一段射孔,默认与井身长度相同 (现在创建对象并添加指针)
nmDataPerforation* defaultPerforation = new nmDataPerforation();
defaultPerforation->getMdStart().setValue(m_bottomholeMD.getValue().toDouble());
defaultPerforation->getMdEnd().setValue(m_bottomholeMD.getValue().toDouble() + m_wellLength.getValue().toDouble());
m_vecPerforations.append(defaultPerforation); // 添加指针
// 默认与当前井身长度一致
m_dLastWellLength = m_wellLength.getValue().toDouble();
this->connectAttributeSignals();
}
void nmDataWellBase::resetToDefaults()
{
m_wellboreStorage.setValue(0.01);
// 重置第一段射孔的皮损系数
if(getPerforationCount() > 0) {
nmDataPerforation* firstPerforation = getPerforation(0);
if(firstPerforation) {
firstPerforation->getSkin().setValue(0.0); // 皮损系数
}
}
}
nmDataWellBase::nmDataWellBase(const nmDataWellBase& other)
{
*this = other; // 使用赋值运算符实现
}
nmDataWellBase::~nmDataWellBase()
{
// 释放所有射孔段对象
qDeleteAll(m_vecPerforations);
m_vecPerforations.clear();
}
nmDataWellBase& nmDataWellBase::operator=(const nmDataWellBase& other)
{
if(this != &other) {
m_wellName = other.m_wellName;
m_x = other.m_x;
m_y = other.m_y;
m_radius = other.m_radius;
m_drillFloorElevation = other.m_drillFloorElevation;
m_zw = other.m_zw;
m_wellLength = other.m_wellLength;
m_rateDependentSkin = other.m_rateDependentSkin;
m_dSdQ = other.m_dSdQ;
m_wellboreModel = other.m_wellboreModel;
m_wellboreStorage = other.m_wellboreStorage;
m_bottomholeMD = other.m_bottomholeMD;
m_vecPtsP = other.m_vecPtsP;
m_vecPtsF = other.m_vecPtsF;
m_nIndexF = other.m_nIndexF;
m_finalWellboreStorage = other.m_finalWellboreStorage;
m_inputWellHead = other.m_inputWellHead;
m_wellHeadX = other.m_wellHeadX;
m_wellHeadY = other.m_wellHeadY;
m_cInitialCFinal = other.m_cInitialCFinal;
m_dtChangingStorage = other.m_dtChangingStorage;
m_leakSkin = other.m_leakSkin;
m_bPlotVisible = other.m_bPlotVisible;
m_bTimeDependentSkin = other.m_bTimeDependentSkin;
m_eWellType = other.m_eWellType;
// 先释放当前的射孔段对象
qDeleteAll(m_vecPerforations);
m_vecPerforations.clear();
// 深拷贝新的射孔段对象
foreach(nmDataPerforation* perf, other.m_vecPerforations) {
if(perf) {
m_vecPerforations.append(new nmDataPerforation(*perf));
}
}
// 复制历史数据
m_vvecHsyPressure = other.m_vvecHsyPressure;
m_vvecHsyLogLog = other.m_vvecHsyLogLog;
m_vvecHsySemiLog = other.m_vvecHsySemiLog;
// 复制计算结果数据
m_vvecRstPressure = other.m_vvecRstPressure;
m_vvecRstLogLog = other.m_vvecRstLogLog;
m_vvecRstSemiLog = other.m_vvecRstSemiLog;
// 默认与当前井身长度一致
m_dLastWellLength = m_wellLength.getValue().toDouble();
}
return *this;
}
// 序列化 nmDataWellBase 为 RapidJSON Value
rapidjson::Value nmDataWellBase::ToJsonValue(rapidjson::Document::AllocatorType& allocator) const
{
// 创建一个 RapidJSON 对象类型的值
rapidjson::Value wellObject(rapidjson::kObjectType);
// 序列化名称
wellObject.AddMember("WellName", rapidjson::Value(m_wellName.toStdString().c_str(), allocator).Move(), allocator);
// 序列化井类型
wellObject.AddMember("WellType", rapidjson::Value(static_cast<int>(m_eWellType)), allocator);
// 序列化 nmDataAttribute 类型的成员
// 调用 nmDataAttribute 自身的 ToJsonValue 方法进行递归序列化
wellObject.AddMember("X", m_x.ToJsonValue(allocator), allocator);
wellObject.AddMember("Y", m_y.ToJsonValue(allocator), allocator);
wellObject.AddMember("Radius", m_radius.ToJsonValue(allocator), allocator);
wellObject.AddMember("DrillFloorElevation", m_drillFloorElevation.ToJsonValue(allocator), allocator);
wellObject.AddMember("Zw", m_zw.ToJsonValue(allocator), allocator);
wellObject.AddMember("WellLength", m_wellLength.ToJsonValue(allocator), allocator);
wellObject.AddMember("RateDependentSkin", m_rateDependentSkin.ToJsonValue(allocator), allocator);
wellObject.AddMember("dSdQ", m_dSdQ.ToJsonValue(allocator), allocator);
wellObject.AddMember("WellboreModel", m_wellboreModel.ToJsonValue(allocator), allocator);
wellObject.AddMember("WellboreStorage", m_wellboreStorage.ToJsonValue(allocator), allocator);
wellObject.AddMember("BottomholeMD", m_bottomholeMD.ToJsonValue(allocator), allocator);
wellObject.AddMember("InputWellHead", m_inputWellHead.ToJsonValue(allocator), allocator);
wellObject.AddMember("WellHeadX", m_wellHeadX.ToJsonValue(allocator), allocator);
wellObject.AddMember("WellHeadY", m_wellHeadY.ToJsonValue(allocator), allocator);
wellObject.AddMember("FinalWellboreStorage", m_finalWellboreStorage.ToJsonValue(allocator), allocator);
wellObject.AddMember("CInitialCFinal", m_cInitialCFinal.ToJsonValue(allocator), allocator);
wellObject.AddMember("DtChangingStorage", m_dtChangingStorage.ToJsonValue(allocator), allocator);
wellObject.AddMember("LeakSkin", m_leakSkin.ToJsonValue(allocator), allocator);
// 序列化时间变表皮状态
wellObject.AddMember("TimeDependentSkin", m_bTimeDependentSkin, allocator);
// 序列化图元可见性 (m_bPlotVisible)
wellObject.AddMember("PlotVisible", m_bPlotVisible, allocator);
// 序列化井的流动段索引 (m_nIndexF)
wellObject.AddMember("IndexFlow", m_nIndexF, allocator);
// 序列化射孔段集合 (现在处理指针)
rapidjson::Value perforationsArray(rapidjson::kArrayType);
foreach(const nmDataPerforation* perfPtr, m_vecPerforations) {
if(perfPtr) { // 检查指针是否有效
perforationsArray.PushBack(perfPtr->ToJsonValue(allocator), allocator);
}
}
wellObject.AddMember("Perforations", perforationsArray, allocator);
return wellObject; // 返回序列化后的 RapidJSON Value
}
// 从 RapidJSON Value 反序列化数据到 nmDataWellBase
void nmDataWellBase::FromJsonValue(const rapidjson::Value& jsonValue)
{
// 反序列化名称
if(jsonValue.HasMember("WellName") && jsonValue["WellName"].IsString()) {
m_wellName = QString::fromUtf8(jsonValue["WellName"].GetString());
}
// 反序列化井类型 (m_eWellType)
if(jsonValue.HasMember("WellType") && jsonValue["WellType"].IsInt()) {
m_eWellType = static_cast<NM_WELL_MODEL>(jsonValue["WellType"].GetInt());
}
// 反序列化 nmDataAttribute 类型的成员
// 调用 nmDataAttribute 自身的 FromJsonValue 方法进行递归反序列化
if(jsonValue.HasMember("X") && jsonValue["X"].IsObject()) {
m_x.FromJsonValue(jsonValue["X"]);
}
if(jsonValue.HasMember("Y") && jsonValue["Y"].IsObject()) {
m_y.FromJsonValue(jsonValue["Y"]);
}
if(jsonValue.HasMember("Radius") && jsonValue["Radius"].IsObject()) {
m_radius.FromJsonValue(jsonValue["Radius"]);
}
if(jsonValue.HasMember("DrillFloorElevation") && jsonValue["DrillFloorElevation"].IsObject()) {
m_drillFloorElevation.FromJsonValue(jsonValue["DrillFloorElevation"]);
}
if(jsonValue.HasMember("Zw") && jsonValue["Zw"].IsObject()) {
m_zw.FromJsonValue(jsonValue["Zw"]);
}
if(jsonValue.HasMember("WellLength") && jsonValue["WellLength"].IsObject()) {
m_wellLength.FromJsonValue(jsonValue["WellLength"]);
}
if(jsonValue.HasMember("RateDependentSkin") && jsonValue["RateDependentSkin"].IsObject()) {
m_rateDependentSkin.FromJsonValue(jsonValue["RateDependentSkin"]);
}
if(jsonValue.HasMember("dSdQ") && jsonValue["dSdQ"].IsObject()) {
m_dSdQ.FromJsonValue(jsonValue["dSdQ"]);
}
if(jsonValue.HasMember("WellboreModel") && jsonValue["WellboreModel"].IsObject()) {
m_wellboreModel.FromJsonValue(jsonValue["WellboreModel"]);
}
if(jsonValue.HasMember("WellboreStorage") && jsonValue["WellboreStorage"].IsObject()) {
m_wellboreStorage.FromJsonValue(jsonValue["WellboreStorage"]);
}
if(jsonValue.HasMember("BottomholeMD") && jsonValue["BottomholeMD"].IsObject()) {
m_bottomholeMD.FromJsonValue(jsonValue["BottomholeMD"]);
}
if(jsonValue.HasMember("InputWellHead") && jsonValue["InputWellHead"].IsObject()) {
m_inputWellHead.FromJsonValue(jsonValue["InputWellHead"]);
}
if(jsonValue.HasMember("WellHeadX") && jsonValue["WellHeadX"].IsObject()) {
m_wellHeadX.FromJsonValue(jsonValue["WellHeadX"]);
}
if(jsonValue.HasMember("WellHeadY") && jsonValue["WellHeadY"].IsObject()) {
m_wellHeadY.FromJsonValue(jsonValue["WellHeadY"]);
}
if(jsonValue.HasMember("FinalWellboreStorage") && jsonValue["FinalWellboreStorage"].IsObject()) {
m_finalWellboreStorage.FromJsonValue(jsonValue["FinalWellboreStorage"]);
}
if(jsonValue.HasMember("CInitialCFinal") && jsonValue["CInitialCFinal"].IsObject()) {
m_cInitialCFinal.FromJsonValue(jsonValue["CInitialCFinal"]);
}
if(jsonValue.HasMember("DtChangingStorage") && jsonValue["DtChangingStorage"].IsObject()) {
m_dtChangingStorage.FromJsonValue(jsonValue["DtChangingStorage"]);
}
if(jsonValue.HasMember("LeakSkin") && jsonValue["LeakSkin"].IsObject()) {
m_leakSkin.FromJsonValue(jsonValue["LeakSkin"]);
}
// 反序列化时间变表皮状态
if(jsonValue.HasMember("TimeDependentSkin") && jsonValue["TimeDependentSkin"].IsBool()) {
m_bTimeDependentSkin = jsonValue["TimeDependentSkin"].GetBool();
}
// 反序列化图元可见性 (m_bPlotVisible)
if(jsonValue.HasMember("PlotVisible") && jsonValue["PlotVisible"].IsBool()) {
m_bPlotVisible = jsonValue["PlotVisible"].GetBool();
}
// 反序列化井的流动段索引 (m_nIndexF)
if(jsonValue.HasMember("IndexFlow") && jsonValue["IndexFlow"].IsInt()) {
m_nIndexF = jsonValue["IndexFlow"].GetInt();
}
// 反序列化射孔段集合 (现在处理指针)
if(jsonValue.HasMember("Perforations") && jsonValue["Perforations"].IsArray()) {
// 清空并释放现有射孔段对象
qDeleteAll(m_vecPerforations);
m_vecPerforations.clear();
const rapidjson::Value& perforationsArray = jsonValue["Perforations"];
for(rapidjson::SizeType i = 0; i < perforationsArray.Size(); ++i) {
if(perforationsArray[i].IsObject()) {
nmDataPerforation* newPerforation = new nmDataPerforation(); // 创建新对象
newPerforation->FromJsonValue(perforationsArray[i]);
m_vecPerforations.append(newPerforation); // 添加指针
}
}
}
// 默认与当前井身长度一致
m_dLastWellLength = m_wellLength.getValue().toDouble();
}
void nmDataWellBase::connectAttributeSignals() {
// 通用属性
connect(&m_inputWellHead, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_wellHeadX, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_wellHeadY, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_x, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_y, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_drillFloorElevation, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_radius, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_zw, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_wellLength, SIGNAL(sigValueChanged()), this, SLOT(slotWellLengthChanged()));
connect(&m_rateDependentSkin, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_dSdQ, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_wellboreModel, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_wellboreStorage, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_finalWellboreStorage, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_cInitialCFinal, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_dtChangingStorage, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_leakSkin, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
connect(&m_bottomholeMD, SIGNAL(sigValueChanged()), this, SIGNAL(sigWellDataChanged()));
}
void nmDataWellBase::notifyParameterChanged()
{
emit sigParameterChanged();
}
void nmDataWellBase::slotWellLengthChanged()
{
double oldLength = m_dLastWellLength;
double newLength = m_wellLength.getValue().toDouble();
// 避免除以零和不必要的计算
if (qFuzzyIsNull(oldLength) || oldLength <= 0 || qFuzzyCompare(oldLength, newLength)) {
return;
}
// 获取井口深度
double wellHeadMd = this->getBottomholeMD().getValue().toDouble();
// 对于多段压裂水平井类型井口MD是第一段射孔的起点
if (m_eWellType == Horizontal_Fractured_Well)
{
wellHeadMd = getPerforations()[0]->getMdStart().getValue().toDouble();
}
// 遍历所有射孔段并按比例更新其MD数据
foreach(nmDataPerforation* pPerfData, m_vecPerforations) {
if(pPerfData) {
double originalPerforationMdStart = pPerfData->getMdStart().getValue().toDouble();
double originalPerforationMdEnd = pPerfData->getMdEnd().getValue().toDouble();
// 计算射孔在旧井筒上的相对位置
double relativePerforationStart = (originalPerforationMdStart - wellHeadMd) / oldLength;
double relativePerforationEnd = (originalPerforationMdEnd - wellHeadMd) / oldLength;
// 根据新的井筒长度计算新的绝对MD值
double newPerforationMdStart = wellHeadMd + (relativePerforationStart * newLength);
double newPerforationMdEnd = wellHeadMd + (relativePerforationEnd * newLength);
pPerfData->getMdStart().setValue(newPerforationMdStart);
pPerfData->getMdEnd().setValue(newPerforationMdEnd);
}
}
// 更新历史井身
m_dLastWellLength = newLength;
emit sigWellDataChanged();
}
// Getters and Setters
void nmDataWellBase::setWellName(const QString& name)
{
m_wellName = name;
}
QString nmDataWellBase::getWellName() const
{
return m_wellName;
}
void nmDataWellBase::setX(const nmDataAttribute& attr)
{
m_x = attr;
emit sigWellDataChanged();
}
nmDataAttribute& nmDataWellBase::getX()
{
return m_x;
}
void nmDataWellBase::setY(const nmDataAttribute& attr)
{
m_y = attr;
emit sigWellDataChanged();
}
nmDataAttribute& nmDataWellBase::getY()
{
return m_y;
}
void nmDataWellBase::setRadius(const nmDataAttribute& attr)
{
m_radius = attr;
}
nmDataAttribute& nmDataWellBase::getRadius()
{
return m_radius;
}
void nmDataWellBase::setDrillFloorElevation(const nmDataAttribute& attr)
{
m_drillFloorElevation = attr;
}
nmDataAttribute& nmDataWellBase::getDrillFloorElevation()
{
return m_drillFloorElevation;
}
void nmDataWellBase::setZw(const nmDataAttribute& attr)
{
m_zw = attr;
}
nmDataAttribute& nmDataWellBase::getZw()
{
return m_zw;
}
void nmDataWellBase::setWellLength(const nmDataAttribute& attr)
{
m_wellLength = attr;
emit sigWellDataChanged();
}
nmDataAttribute& nmDataWellBase::getWellLength()
{
return m_wellLength;
}
void nmDataWellBase::setRateDependentSkin(const nmDataAttribute& attr)
{
m_rateDependentSkin = attr;
}
nmDataAttribute& nmDataWellBase::getRateDependentSkin()
{
return m_rateDependentSkin;
}
void nmDataWellBase::setdSdQ(const nmDataAttribute& attr)
{
m_dSdQ = attr;
}
nmDataAttribute& nmDataWellBase::getdSdQ()
{
return m_dSdQ;
}
void nmDataWellBase::setWellboreModel(const nmDataAttribute& attr)
{
m_wellboreModel = attr;
}
nmDataAttribute& nmDataWellBase::getWellboreModel()
{
return m_wellboreModel;
}
void nmDataWellBase::setWellboreStorage(const nmDataAttribute& attr)
{
m_wellboreStorage = attr;
}
nmDataAttribute& nmDataWellBase::getWellboreStorage()
{
return m_wellboreStorage;
}
void nmDataWellBase::setBottomholeMD(const nmDataAttribute& attr)
{
m_bottomholeMD = attr;
emit sigWellDataChanged();
}
nmDataAttribute& nmDataWellBase::getBottomholeMD()
{
return m_bottomholeMD;
}
bool nmDataWellBase::isTimeDependentSkin() const
{
return m_bTimeDependentSkin;
}
void nmDataWellBase::setTimeDependentSkin(bool enabled)
{
m_bTimeDependentSkin = enabled;
}
// 获取压力曲线点数据
QVector<QPointF> nmDataWellBase::getPressurePoints() const
{
return m_vecPtsP;
}
// 设置压力曲线点数据
void nmDataWellBase::setPressurePoints(const QVector<QPointF>& points)
{
m_vecPtsP = points;
}
// 获取流量曲线点数据
QVector<QPointF> nmDataWellBase::getFlowPoints() const
{
return m_vecPtsF;
}
// 设置流量曲线点数据
void nmDataWellBase::setFlowPoints(const QVector<QPointF>& points)
{
m_vecPtsF = points;
}
int nmDataWellBase::getIndexF() const
{
return m_nIndexF;
}
void nmDataWellBase::setIndexF(const int newIndex)
{
m_nIndexF = newIndex;
}
// Location---位置
void nmDataWellBase::setInputWellHead(const nmDataAttribute& attr)
{
m_inputWellHead = attr;
}
nmDataAttribute& nmDataWellBase::getInputWellHead()
{
return m_inputWellHead;
}
void nmDataWellBase::setWellHeadX(const nmDataAttribute& attr)
{
m_wellHeadX = attr;
}
nmDataAttribute& nmDataWellBase::getWellHeadX()
{
return m_wellHeadX;
}
void nmDataWellBase::setWellHeadY(const nmDataAttribute& attr)
{
m_wellHeadY = attr;
}
nmDataAttribute& nmDataWellBase::getWellHeadY()
{
return m_wellHeadY;
}
// Wellbore---井储
void nmDataWellBase::setCInitialCFinal(const nmDataAttribute& attr)
{
m_cInitialCFinal = attr;
}
nmDataAttribute& nmDataWellBase::getCInitialCFinal()
{
return m_cInitialCFinal;
}
void nmDataWellBase::setDtChangingStorage(const nmDataAttribute& attr)
{
m_dtChangingStorage = attr;
}
nmDataAttribute& nmDataWellBase::getDtChangingStorage()
{
return m_dtChangingStorage;
}
void nmDataWellBase::setLeakSkin(const nmDataAttribute& attr)
{
m_leakSkin = attr;
}
nmDataAttribute& nmDataWellBase::getLeakSkin()
{
return m_leakSkin;
}
void nmDataWellBase::setFinalWellboreStorage(const nmDataAttribute& attr)
{
m_finalWellboreStorage = attr;
}
nmDataAttribute& nmDataWellBase::getFinalWellboreStorage()
{
return m_finalWellboreStorage;
}
bool nmDataWellBase::getPlotVisible() const
{
return m_bPlotVisible;
}
void nmDataWellBase::setPlotVisible(const bool newState)
{
m_bPlotVisible = newState;
}
void nmDataWellBase::setWellType(NM_WELL_MODEL newWellType)
{
m_eWellType = newWellType;
}
NM_WELL_MODEL nmDataWellBase::getWellType() const
{
return m_eWellType;
}
// 历史数据相关方法
QVector<QVector<double>> nmDataWellBase::getHistoryPressure() {
return m_vvecHsyPressure;
}
void nmDataWellBase::setHistoryPressure(QVector<QVector<double>> pressureData) {
m_vvecHsyPressure = pressureData;
}
QVector<QVector<double>> nmDataWellBase::getHistoryLogLog() {
return m_vvecHsyLogLog;
}
void nmDataWellBase::setHistoryLogLog(QVector<QVector<double>> loglogData) {
m_vvecHsyLogLog = loglogData;
}
QVector<QVector<double>> nmDataWellBase::getHistorySemiLog() {
return m_vvecHsySemiLog;
}
void nmDataWellBase::setHistorySemiLog(QVector<QVector<double>> semiLogData) {
m_vvecHsySemiLog = semiLogData;
}
// 计算结果相关方法
QVector<QVector<double>> nmDataWellBase::getResultPressure()
{
return m_vvecRstPressure;
}
void nmDataWellBase::setResultPressure(QVector<QVector<double>> pressureData)
{
m_vvecRstPressure = pressureData;
}
QVector<QVector<double>> nmDataWellBase::getResultLogLog()
{
return m_vvecRstLogLog;
}
void nmDataWellBase::setResultLogLog(QVector<QVector<double>> loglogData)
{
m_vvecRstLogLog = loglogData;
}
QVector<QVector<double>> nmDataWellBase::getResultSemiLog()
{
return m_vvecRstSemiLog;
}
void nmDataWellBase::setResultSemiLog(QVector<QVector<double>> semiLogData)
{
m_vvecRstSemiLog = semiLogData;
}
// 射孔管理相关方法
void nmDataWellBase::addPerforation(nmDataPerforation* perforation)
{
if(perforation) { // 确保传入的指针非空
m_vecPerforations.append(perforation);
emit sigWellDataChanged();
}
}
void nmDataWellBase::removePerforation(int index)
{
if(index >= 0 && index < m_vecPerforations.size()) {
delete m_vecPerforations.at(index); // 释放内存
m_vecPerforations.remove(index); // 从 QVector 中移除指针
emit sigWellDataChanged();
}
}
nmDataPerforation* nmDataWellBase::getPerforation(int index)
{
if(index >= 0 && index < m_vecPerforations.size()) {
return m_vecPerforations.at(index);
}
return nullptr;
}
nmDataPerforation nmDataWellBase::getPerforationCopy(int index)
{
if (index >= 0 && index < m_vecPerforations.size() && m_vecPerforations[index] != nullptr) {
return *m_vecPerforations[index]; // 调用拷贝构造函数
}
return nmDataPerforation(); // 返回默认构造对象
}
void nmDataWellBase::updatePerforation(int index, const nmDataPerforation& newData)
{
if (index >= 0 && index < m_vecPerforations.size() && m_vecPerforations[index] != nullptr) {
*m_vecPerforations[index] = newData; // 调用赋值运算符
emit sigWellDataChanged(); // 发出数据变化信号
}
}
int nmDataWellBase::getPerforationCount()
{
return m_vecPerforations.size();
}
QVector<nmDataPerforation*>& nmDataWellBase::getPerforations()
{
return m_vecPerforations;
}
void nmDataWellBase::clearPerforations()
{
foreach (nmDataPerforation* perf , m_vecPerforations)
{
delete perf;
}
m_vecPerforations.clear();
}
bool nmDataWellBase::isBasePerforation()
{
if(m_vecPerforations.size() > 1 || m_vecPerforations.size() <= 0 ) {
return false;
}
// 检查第一个射孔段数据是不是基础射孔
nmDataPerforation* pPerf = m_vecPerforations[0];
if (pPerf == nullptr)
return false;
// 名称检查
if (pPerf->getName().getValue().toString() != "Perforation#1")
return false;
// 长度检查
double bottomholeMd = this->getBottomholeMD().getValue().toDouble();
double mdStart = pPerf->getMdStart().getValue().toDouble();
double mdEnd = pPerf->getMdEnd().getValue().toDouble();
double expectedMdEnd = bottomholeMd + this->getWellLength().getValue().toDouble();
if (qAbs(mdStart - bottomholeMd) > 1e-9)
return false;
if (qAbs(mdEnd - expectedMdEnd) > 1e-9)
return false;
// 表皮系数检查
if(qAbs(pPerf->getSkin().getValue().toDouble()) > 1e-9)
return false;
return true;
}
// 井级别的流量和时间查询方法
double nmDataWellBase::getTotalTimeRange() const
{
QVector<QPointF> allFlowPoints = getFlowPoints();
double totalTime = 0.0;
for (int i = 0; i < allFlowPoints.size(); ++i) {
totalTime += allFlowPoints[i].x();
}
return totalTime;
}
double nmDataWellBase::getFlowRateAtTime(double time) const
{
QVector<QPointF> allFlowPoints = getFlowPoints();
double currentTime = 0.0;
// 遍历所有流量点,找到包含指定时间的阶梯段
for (int i = 0; i < allFlowPoints.size(); ++i) {
const QPointF& point = allFlowPoints.at(i);
double stepStartTime = currentTime;
double stepEndTime = currentTime + point.x();
// 如果时间在这个阶梯段内
if (time >= stepStartTime && time < stepEndTime) {
return point.y(); // 返回该阶梯段的流量值
}
currentTime = stepEndTime;
// 如果时间超出了所有阶梯段,返回最后一个阶梯段的流量值
if (time >= stepEndTime && i == allFlowPoints.size() - 1) {
return point.y();
}
}
// 如果没有找到合适的阶梯段可能是时间为0或负数返回第一个阶梯段的流量值
if (!allFlowPoints.isEmpty()) {
return allFlowPoints.first().y();
}
return 100.0; // 默认值
}
QVector<double> nmDataWellBase::getAllFlowStepBoundaries() const
{
QVector<double> allBoundaries;
QVector<QPointF> allFlowPoints = getFlowPoints();
double currentTime = 0.0;
// 添加起始时间
allBoundaries.append(currentTime);
// 遍历所有流量点,收集所有阶梯边界时间
for (int i = 0; i < allFlowPoints.size(); ++i) {
const QPointF& point = allFlowPoints.at(i);
currentTime += point.x();
allBoundaries.append(currentTime);
}
return allBoundaries;
}
QVector<QPointF> nmDataWellBase::getFlowStepsInTimeRange(double startTime, double endTime) const
{
QVector<QPointF> stepsInRange;
QVector<QPointF> allFlowPoints = getFlowPoints();
double currentTime = 0.0;
for (int i = 0; i < allFlowPoints.size(); ++i) {
const QPointF& point = allFlowPoints.at(i);
double stepStartTime = currentTime;
double stepEndTime = currentTime + point.x();
// 检查这个阶梯段是否与指定时间范围有重叠
if (stepEndTime > startTime && stepStartTime < endTime) {
// 计算在范围内的部分
double overlapStart = qMax(stepStartTime, startTime);
double overlapEnd = qMin(stepEndTime, endTime);
if (overlapEnd > overlapStart) {
stepsInRange.append(QPointF(overlapEnd - overlapStart, point.y()));
}
}
currentTime = stepEndTime;
if (currentTime >= endTime) {
break; // 超出范围,停止
}
}
return stepsInRange;
}
double nmDataWellBase::findFlowStepBoundaryAtTime(double timePoint) const
{
QVector<QPointF> allFlowPoints = getFlowPoints();
double currentTime = 0.0;
// 遍历所有流量点找到包含timePoint的阶梯段
for (int i = 0; i < allFlowPoints.count(); ++i) {
const QPointF& point = allFlowPoints.at(i);
double stepStartTime = currentTime;
double stepEndTime = currentTime + point.x();
// 如果timePoint在这个阶梯段内
if (timePoint >= stepStartTime && timePoint < stepEndTime) {
return stepEndTime; // 返回该阶梯段的结束时间作为分割边界
}
currentTime = stepEndTime;
}
return -1.0; // 没有找到合适的阶梯边界
}
double nmDataWellBase::findSmartFlowStepBoundary(double targetTime) const
{
QVector<QPointF> allFlowPoints = getFlowPoints();
double currentTime = 0.0;
// 遍历所有流量点找到包含targetTime的阶梯段
for (int i = 0; i < allFlowPoints.count(); ++i) {
const QPointF& point = allFlowPoints.at(i);
double stepStartTime = currentTime;
double stepEndTime = currentTime + point.x();
// 如果targetTime在这个阶梯段内
if (targetTime >= stepStartTime && targetTime <= stepEndTime) {
double stepMidTime = (stepStartTime + stepEndTime) / 2.0;
// 根据在阶梯段的前半部分还是后半部分决定吸附位置
if (targetTime <= stepMidTime) {
// 在前半部分,吸附到阶梯段开始
return stepStartTime;
} else {
// 在后半部分,吸附到阶梯段结束
return stepEndTime;
}
}
currentTime = stepEndTime;
}
// 如果没有找到对应的阶梯段使用已有的findNearestFlowStepBoundary函数
return findNearestFlowStepBoundary(targetTime);
}
double nmDataWellBase::findNearestFlowStepBoundary(double targetTime) const
{
QVector<double> boundaries = getAllFlowStepBoundaries();
if (boundaries.isEmpty()) {
return targetTime; // 如果没有边界,返回原时间
}
double nearestBoundary = boundaries[0];
double minDistance = qAbs(targetTime - boundaries[0]);
for (int i = 1; i < boundaries.size(); ++i) {
double distance = qAbs(targetTime - boundaries[i]);
if (distance < minDistance) {
minDistance = distance;
nearestBoundary = boundaries[i];
}
}
return nearestBoundary;
}
bool nmDataWellBase::isTimeInFlowStepFirstHalf(double time) const
{
QVector<QPointF> allFlowPoints = getFlowPoints();
double currentTime = 0.0;
// 遍历所有流量点找到包含time的阶梯段
for (int i = 0; i < allFlowPoints.count(); ++i) {
const QPointF& point = allFlowPoints.at(i);
double stepStartTime = currentTime;
double stepEndTime = currentTime + point.x();
// 如果time在这个阶梯段内
if (time >= stepStartTime && time <= stepEndTime) {
double stepMidTime = (stepStartTime + stepEndTime) / 2.0;
return time <= stepMidTime;
}
currentTime = stepEndTime;
}
return false;
}
// 射孔段相关的查询方法
QVector<double> nmDataWellBase::getFlowStepBoundariesInSegment(int segmentIndex, int perforationIndex) const
{
QVector<double> boundaries;
if (perforationIndex < 0 || perforationIndex >= m_vecPerforations.size()) {
return boundaries;
}
nmDataPerforation* perforation = m_vecPerforations[perforationIndex];
if (!perforation) {
return boundaries;
}
const QVector<FlowSegmentData>& segments = perforation->getFlowSegments();
if (segmentIndex < 0 || segmentIndex >= segments.size()) {
return boundaries;
}
const FlowSegmentData& segment = segments[segmentIndex];
double segmentStartTime = segment.segmentStart.getValue().toDouble();
double segmentEndTime = segment.segmentEnd.getValue().toDouble();
QVector<QPointF> allFlowPoints = getFlowPoints();
double currentTime = 0.0;
// 遍历所有流量点,找到在该段范围内的所有阶梯边界
for (int i = 0; i < allFlowPoints.size(); ++i) {
const QPointF& point = allFlowPoints.at(i);
double stepEndTime = currentTime + point.x();
// 如果这个阶梯段与目标段有重叠
if (stepEndTime > segmentStartTime && currentTime < segmentEndTime) {
// 添加在段范围内的边界点
if (stepEndTime <= segmentEndTime) {
boundaries.append(stepEndTime);
}
}
currentTime = stepEndTime;
// 如果已经超出段的结束时间,停止遍历
if (currentTime >= segmentEndTime) {
break;
}
}
// 确保最后一个边界不超过段的结束时间
if (!boundaries.isEmpty() && boundaries.last() > segmentEndTime) {
boundaries[boundaries.size() - 1] = segmentEndTime;
}
// 如果没有找到任何边界,添加段的结束时间
if (boundaries.isEmpty()) {
boundaries.append(segmentEndTime);
}
return boundaries;
}
QVector<double> nmDataWellBase::getAllBoundariesInSegment(int segmentIndex, int perforationIndex) const
{
QVector<double> allBoundaries;
if (perforationIndex < 0 || perforationIndex >= m_vecPerforations.size()) {
return allBoundaries;
}
nmDataPerforation* perforation = m_vecPerforations[perforationIndex];
if (!perforation) {
return allBoundaries;
}
const QVector<FlowSegmentData>& segments = perforation->getFlowSegments();
if (segmentIndex < 0 || segmentIndex >= segments.size()) {
return allBoundaries;
}
const FlowSegmentData& segment = segments[segmentIndex];
double segmentStartTime = segment.segmentStart.getValue().toDouble();
double segmentEndTime = segment.segmentEnd.getValue().toDouble();
// 获取流量阶梯边界
QVector<double> flowBoundaries = getFlowStepBoundariesInSegment(segmentIndex, perforationIndex);
// 添加所有边界
allBoundaries.append(segmentStartTime);
for (int i = 0; i < flowBoundaries.size(); ++i) {
double boundary = flowBoundaries[i];
if (boundary > segmentStartTime && boundary <= segmentEndTime) {
allBoundaries.append(boundary);
}
}
// 排序并去重
qSort(allBoundaries);
for (int i = allBoundaries.size() - 1; i > 0; --i) {
if (qAbs(allBoundaries[i] - allBoundaries[i - 1]) < 1e-6) {
allBoundaries.remove(i);
}
}
return allBoundaries;
}
int nmDataWellBase::countFlowStepsInSegment(int segmentIndex, int perforationIndex) const
{
if (perforationIndex < 0 || perforationIndex >= m_vecPerforations.size()) {
return 0;
}
nmDataPerforation* perforation = m_vecPerforations[perforationIndex];
if (!perforation) {
return 0;
}
const QVector<FlowSegmentData>& segments = perforation->getFlowSegments();
if (segmentIndex < 0 || segmentIndex >= segments.size()) {
return 0;
}
const FlowSegmentData& segment = segments[segmentIndex];
double startTime = segment.segmentStart.getValue().toDouble();
double endTime = segment.segmentEnd.getValue().toDouble();
QVector<QPointF> stepsInRange = getFlowStepsInTimeRange(startTime, endTime);
return stepsInRange.size();
}
bool nmDataWellBase::canFlowSegmentBeSplit(int segmentIndex, int perforationIndex) const
{
return countFlowStepsInSegment(segmentIndex, perforationIndex) > 1;
}
double nmDataWellBase::findNearestFlowStepBoundaryBeforeSegment(int segmentIndex, int perforationIndex) const
{
if (perforationIndex < 0 || perforationIndex >= m_vecPerforations.size()) {
return -1.0;
}
nmDataPerforation* perforation = m_vecPerforations[perforationIndex];
if (!perforation) {
return -1.0;
}
const QVector<FlowSegmentData>& segments = perforation->getFlowSegments();
if (segmentIndex <= 0 || segmentIndex >= segments.size()) {
return -1.0; // 第一个段或无效索引,无法插入
}
// 获取当前段的开始时间
double currentSegmentStartTime = segments[segmentIndex].segmentStart.getValue().toDouble();
// 获取前一个段的开始和结束时间
double prevSegmentStartTime = segments[segmentIndex - 1].segmentStart.getValue().toDouble();
double prevSegmentEndTime = segments[segmentIndex - 1].segmentEnd.getValue().toDouble();
// 在前一个段内查找所有流量阶梯边界
QVector<double> boundaries = getFlowStepBoundariesInSegment(segmentIndex - 1, perforationIndex);
if (boundaries.isEmpty()) {
return -1.0;
}
// 找到距离当前段开始时间最近的边界(但要小于当前段的开始时间,且不等于前一个段的结束时间)
double nearestBoundary = -1.0;
double minDistance = 1e10;
for (int i = 0; i < boundaries.size(); ++i) {
double boundary = boundaries[i];
// 边界必须在前一个段内部,且小于当前段的开始时间,且不等于前一个段的结束时间
if (boundary > prevSegmentStartTime &&
boundary < currentSegmentStartTime &&
qAbs(boundary - prevSegmentEndTime) > 1e-6) { // 不等于前一个段的结束时间
double distance = currentSegmentStartTime - boundary;
if (distance < minDistance) {
minDistance = distance;
nearestBoundary = boundary;
}
}
}
return nearestBoundary;
}
bool nmDataWellBase::exportHistoryLogLogToCsv(const QString& outDir)
{
// 1) 取历史数据:期望 hist[0]=t, hist[1]=p, hist[2]=deriv
QVector<QVector<double> > hist = this->getHistoryLogLog();
if(hist.size() < 2) {
return false;
}
const QVector<double>& t = hist[0];
const QVector<double>& p = hist[1];
QVector<double> deriv;
bool hasDeriv = false;
if(hist.size() >= 3) {
deriv = hist[2];
hasDeriv = true;
}
// 2) 长度检查
int n = t.size();
if(n <= 0) return false;
if(p.size() != n) return false;
if(hasDeriv && deriv.size() != n) {
// 导数长度不一致就当没有导数
hasDeriv = false;
}
// 3) 输出文件名
QString wellName = this->getWellName(); // 你已有方法
if(wellName.isEmpty()) wellName = "UnknownWell";
// 文件直接放 temp 目录,不创建子目录
QString filePath = QDir(outDir).filePath(wellName + "_loglog.csv");
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
QTextStream out(&file);
// 4) 写表头
if(hasDeriv) {
out << "t,p,deriv\n";
} else {
out << "t,p\n";
}
// 5) 正确写入:按索引 i 写 t[i], p[i], deriv[i]
for(int i = 0; i < n; ++i) {
// 过滤非法数据
double ti = t[i];
double pi = p[i];
if(ti <= 0) continue;
if(pi <= 0) continue;
out << QString::number(ti, 'g', 12) << ",";
out << QString::number(pi, 'g', 12);
if(hasDeriv) {
double di = deriv[i];
if(di <= 0) di = 1e-12; // 你后续 log10 时需要正数
out << "," << QString::number(di, 'g', 12);
}
out << "\n";
}
file.close();
return true;
}
bool nmDataWellBase::exportHistoryRateToCsv(const QString& outDir)
{
// 1) 获取流量点 (t, q)
QVector<QPointF> vecTimeQ = this->getFlowPoints();
if(vecTimeQ.size() < 2) {
return false;
}
// 2) 移除第一个点 (0,0)
if(!vecTimeQ.isEmpty()
&& vecTimeQ[0].x() == 0.0
&& vecTimeQ[0].y() == 0.0)
{
vecTimeQ.remove(0);
}
if(vecTimeQ.size() < 2) {
return false;
}
// 3) sectionIndex流动段索引
int sectionIndex = this->getIndexF();
// 4) 输出文件名
QString wellName = this->getWellName();
if(wellName.isEmpty()) wellName = "UnknownWell";
QString filePath = QDir(outDir).filePath(wellName + "_rate.csv");
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
QTextStream out(&file);
// 5) 写表头
out << "t,qo,sectionIndex\n";
// 6) 写入每个点
for(int i = 0; i < vecTimeQ.size(); ++i)
{
double ti = vecTimeQ[i].x();
double qi = vecTimeQ[i].y();
if(ti <= 0) continue;
if(qi < 0) qi = 0; // 流量允许关井
out << QString::number(ti, 'g', 12) << ",";
out << QString::number(qi, 'g', 12) << ",";
out << sectionIndex << "\n";
}
file.close();
return true;
}
bool nmDataWellBase::exportHistoryPressureToCsv(const QString& outDir)
{
// 1) 取历史压力数据:期望 hist[0]=t, hist[1]=p
QVector<QVector<double> > hist = this->getHistoryPressure();
if(hist.size() < 2) {
return false;
}
const QVector<double>& t = hist[0];
const QVector<double>& p = hist[1];
// 2) 长度检查
int n = t.size();
if(n <= 0) return false;
if(p.size() != n) return false;
// 3) 输出文件名
QString wellName = this->getWellName();
if(wellName.isEmpty()) wellName = "UnknownWell";
// 文件直接放 temp 目录,不创建子目录
QString filePath = QDir(outDir).filePath(wellName + "_pressure.csv");
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
QTextStream out(&file);
out.setRealNumberPrecision(12); // 设置精度
// 4) 写表头
out << "t,p\n";
// 5) 写入数据
for(int i = 0; i < n; ++i) {
double ti = t[i];
double pi = p[i];
// 过滤非法数据
if(ti <= 0) continue;
if(pi <= 0) continue; // 假设压力为正
out << ti << "," << pi << "\n";
}
file.close();
return true;
}
bool nmDataWellBase::exportResultLogLogToCsv(const QString& outDir)
{
QVector<QVector<double> > result = this->getResultLogLog();
if(result.size() < 2) {
return false;
}
const QVector<double>& t = result[0];
const QVector<double>& p = result[1];
QVector<double> deriv;
bool hasDeriv = false;
if(result.size() >= 3) {
deriv = result[2];
hasDeriv = true;
}
int n = t.size();
if(n <= 0) return false;
if(p.size() != n) return false;
if(hasDeriv && deriv.size() != n) {
hasDeriv = false;
}
QString wellName = this->getWellName();
if(wellName.isEmpty()) wellName = "UnknownWell";
QString filePath = QDir(outDir).filePath(wellName + "_result_loglog.csv");
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
QTextStream out(&file);
if(hasDeriv) {
out << "t,p,deriv\n";
} else {
out << "t,p\n";
}
for(int i = 0; i < n; ++i) {
double ti = t[i];
double pi = p[i];
if(ti <= 0) continue;
if(pi <= 0) continue;
out << QString::number(ti, 'g', 12) << ",";
out << QString::number(pi, 'g', 12);
if(hasDeriv) {
double di = deriv[i];
if(di <= 0) di = 1e-12;
out << "," << QString::number(di, 'g', 12);
}
out << "\n";
}
file.close();
return true;
}
bool nmDataWellBase::exportResultPressureToCsv(const QString& outDir)
{
QVector<QVector<double> > result = this->getResultPressure();
if(result.size() < 2) {
return false;
}
const QVector<double>& t = result[0];
const QVector<double>& p = result[1];
int n = t.size();
if(n <= 0) return false;
if(p.size() != n) return false;
QString wellName = this->getWellName();
if(wellName.isEmpty()) wellName = "UnknownWell";
QString filePath = QDir(outDir).filePath(wellName + "_result_pressure.csv");
QFile file(filePath);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return false;
}
QTextStream out(&file);
out.setRealNumberPrecision(12);
out << "t,p\n";
for(int i = 0; i < n; ++i) {
double ti = t[i];
double pi = p[i];
if(ti <= 0) continue;
if(pi <= 0) continue;
out << ti << "," << pi << "\n";
}
file.close();
return true;
}