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