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++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "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;
}