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

1690 lines
50 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 "nmPebiResultSnapshot.h"
#include <QDir>
#include <QByteArray>
#include <QFile>
#include <QTextStream>
#include <QCoreApplication>
#include <QDateTime>
#include <QUuid>
nmDataWellBase::nmDataWellBase()
: m_eWellCategory(NM_WellCategory_Unknown)
, m_nIndexF(0)
, m_bPlotVisible(true)
, m_bTimeDependentSkin(false)
, m_eWellType(Vertical_Well)
, m_dLastWellLength(0.0)
, m_pReservoir(nullptr)
{
m_sWellInstanceId = createWellInstanceId();
nmDataAnalyzeManager* pDataManager =
nmDataAnalyzeManager::getCurrentInstance();
m_pReservoir = pDataManager == nullptr
? nullptr : pDataManager->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");
// 候选加载阶段尚未发布储层对象,井长随后会由 JSON 完整恢复。
const QVariant oInitialWellLength = m_pReservoir == nullptr
? QVariant(0.0) : m_pReservoir->getThickness().getValue();
m_wellLength = nmDataAttribute("Well length", oInitialWellLength, "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;
// 图元外观属性默认值(与 nmObjPointWell::init 中的 m_oDot 默认值一致)
m_nDotStyle = 3; // DTS_Circle
m_nDotColorR = 0; // QColor(0, 255, 0)
m_nDotColorG = 255;
m_nDotColorB = 0;
m_dDotRadius = 1.5;
m_bDotFilling = true;
m_bShowSubObjs = 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) {
// 工作副本不能继承 Manager 维护的结果绑定;提交替换后统一重绑。
m_pPebiResultSnapshot.clear();
m_sWellInstanceId = other.m_sWellInstanceId;
m_wellName = other.m_wellName;
m_wellCode = other.m_wellCode;
m_eWellCategory = other.m_eWellCategory;
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_nDotStyle = other.m_nDotStyle;
m_nDotColorR = other.m_nDotColorR;
m_nDotColorG = other.m_nDotColorG;
m_nDotColorB = other.m_nDotColorB;
m_dDotRadius = other.m_dDotRadius;
m_bDotFilling = other.m_bDotFilling;
m_bShowSubObjs = other.m_bShowSubObjs;
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_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("WellCode", rapidjson::Value(m_wellCode.toStdString().c_str(), allocator).Move(), allocator);
// UUID 是实时井、井历史和结果快照之间唯一允许的关联键。
const QByteArray baWellInstanceId = m_sWellInstanceId.toUtf8();
wellObject.AddMember("WellInstanceId",
rapidjson::Value(baWellInstanceId.constData(),
static_cast<rapidjson::SizeType>(baWellInstanceId.size()),
allocator).Move(), allocator);
// 序列化井类型
wellObject.AddMember("WellType", rapidjson::Value(static_cast<int>(m_eWellType)), allocator);
// 井别决定流量大字段的油、气、水索引v4 项目必须显式保存。
wellObject.AddMember("WellCategory",
rapidjson::Value(static_cast<int>(m_eWellCategory)), 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);
// 序列化图元外观属性
wellObject.AddMember("DotStyle", m_nDotStyle, allocator);
wellObject.AddMember("DotColorR", m_nDotColorR, allocator);
wellObject.AddMember("DotColorG", m_nDotColorG, allocator);
wellObject.AddMember("DotColorB", m_nDotColorB, allocator);
wellObject.AddMember("DotRadius", m_dDotRadius, allocator);
wellObject.AddMember("DotFilling", m_bDotFilling, allocator);
wellObject.AddMember("ShowSubObjs", m_bShowSubObjs, 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());
}
// 反序列化井编码
if(jsonValue.HasMember("WellCode") && jsonValue["WellCode"].IsString()) {
m_wellCode = QString::fromUtf8(jsonValue["WellCode"].GetString());
}
// 项目格式已在 Manager 中完成校验,这里恢复求解时保存的稳定井身份。
if(jsonValue.HasMember("WellInstanceId") &&
jsonValue["WellInstanceId"].IsString()) {
restoreWellInstanceId(QString::fromUtf8(
jsonValue["WellInstanceId"].GetString()));
}
// 反序列化井类型 (m_eWellType)
if(jsonValue.HasMember("WellType") && jsonValue["WellType"].IsInt()) {
m_eWellType = static_cast<NM_WELL_MODEL>(jsonValue["WellType"].GetInt());
}
// v4 不兼容旧项目,缺失或非法井别由 Manager 的严格结构校验直接拒绝。
if(jsonValue.HasMember("WellCategory") &&
jsonValue["WellCategory"].IsInt()) {
m_eWellCategory = static_cast<NM_WELL_CATEGORY>(
jsonValue["WellCategory"].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();
}
// 反序列化图元外观属性
if(jsonValue.HasMember("DotStyle") && jsonValue["DotStyle"].IsInt()) {
m_nDotStyle = jsonValue["DotStyle"].GetInt();
}
if(jsonValue.HasMember("DotColorR") && jsonValue["DotColorR"].IsInt()) {
m_nDotColorR = jsonValue["DotColorR"].GetInt();
}
if(jsonValue.HasMember("DotColorG") && jsonValue["DotColorG"].IsInt()) {
m_nDotColorG = jsonValue["DotColorG"].GetInt();
}
if(jsonValue.HasMember("DotColorB") && jsonValue["DotColorB"].IsInt()) {
m_nDotColorB = jsonValue["DotColorB"].GetInt();
}
if(jsonValue.HasMember("DotRadius") && jsonValue["DotRadius"].IsDouble()) {
m_dDotRadius = jsonValue["DotRadius"].GetDouble();
}
if(jsonValue.HasMember("DotFilling") && jsonValue["DotFilling"].IsBool()) {
m_bDotFilling = jsonValue["DotFilling"].GetBool();
}
if(jsonValue.HasMember("ShowSubObjs") && jsonValue["ShowSubObjs"].IsBool()) {
m_bShowSubObjs = jsonValue["ShowSubObjs"].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)
{
nmDataPerforation* pBasePerforation = getPerforation(0);
if (pBasePerforation != nullptr) {
wellHeadMd = pBasePerforation->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::setWellCode(const QString& code)
{
m_wellCode = code;
}
QString nmDataWellBase::getWellCode() const
{
return m_wellCode;
}
void nmDataWellBase::setWellCategory(NM_WELL_CATEGORY eWellCategory)
{
m_eWellCategory = nmIsValidWellCategory(eWellCategory)
? eWellCategory : NM_WellCategory_Unknown;
}
NM_WELL_CATEGORY nmDataWellBase::getWellCategory() const
{
return m_eWellCategory;
}
QString nmDataWellBase::getWellInstanceId() const
{
return m_sWellInstanceId;
}
bool nmDataWellBase::restoreWellInstanceId(
const QString& sWellInstanceId)
{
QUuid oUuid(sWellInstanceId);
if(sWellInstanceId.isEmpty() || oUuid.isNull()) {
return false;
}
m_sWellInstanceId = oUuid.toString().remove('{').remove('}');
// UUID 改变后原弱引用不再具有关联意义,等待 Manager 重新绑定。
m_pPebiResultSnapshot.clear();
return true;
}
QString nmDataWellBase::createWellInstanceId()
{
// UUID 只表示井对象身份;删除后新建的同名或同编码井必须获得新值。
return QUuid::createUuid().toString().remove('{').remove('}');
}
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;
}
QVector<QPointF> nmDataWellBase::getFlowSegmentPoints() const
{
QVector<QPointF> vecSegments = m_vecPtsF;
// 工程流量数据通常以 (0,0) 作为非业务占位点;真实数据没有占位点时不得误删首段。
if(!vecSegments.isEmpty() &&
vecSegments.first().x() == 0.0 &&
vecSegments.first().y() == 0.0) {
vecSegments.remove(0);
}
return vecSegments;
}
int nmDataWellBase::getFlowSegmentCount() const
{
if(m_vecPtsF.isEmpty()) {
return 0;
}
return m_vecPtsF.first().x() == 0.0 &&
m_vecPtsF.first().y() == 0.0
? m_vecPtsF.size() - 1 : m_vecPtsF.size();
}
bool nmDataWellBase::hasValidFlowSectionIndex() const
{
return m_nIndexF >= 1 && m_nIndexF <= getFlowSegmentCount();
}
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()
{
QSharedPointer<const nmPebiResultSnapshot> pSnapshot =
m_pPebiResultSnapshot.toStrongRef();
if(!pSnapshot.isNull()) {
const nmPebiResultWellSnapshot* pWell =
pSnapshot->findWell(m_sWellInstanceId);
if(pWell != NULL) {
return pWell->m_oCurves.m_vecResultPressure;
}
}
return QVector<QVector<double> >();
}
QVector<QVector<double>> nmDataWellBase::getResultLogLog()
{
QSharedPointer<const nmPebiResultSnapshot> pSnapshot =
m_pPebiResultSnapshot.toStrongRef();
if(!pSnapshot.isNull()) {
const nmPebiResultWellSnapshot* pWell =
pSnapshot->findWell(m_sWellInstanceId);
if(pWell != NULL) {
return pWell->m_oCurves.m_vecResultLogLog;
}
}
return QVector<QVector<double> >();
}
QVector<QVector<double>> nmDataWellBase::getResultSemiLog()
{
QSharedPointer<const nmPebiResultSnapshot> pSnapshot =
m_pPebiResultSnapshot.toStrongRef();
if(!pSnapshot.isNull()) {
const nmPebiResultWellSnapshot* pWell =
pSnapshot->findWell(m_sWellInstanceId);
if(pWell != NULL) {
return pWell->m_oCurves.m_vecResultSemiLog;
}
}
return QVector<QVector<double> >();
}
void nmDataWellBase::bindPebiResultSnapshot(
const QWeakPointer<const nmPebiResultSnapshot>& pSnapshot)
{
m_pPebiResultSnapshot = pSnapshot;
}
// 射孔管理相关方法
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::applyWellboreEdit(
double dBottomholeMd,
double dWellLength,
const QVector<nmDataPerforation>& vecPerforations)
{
// 第一步:批量写入只接受完整快照,水平压裂井还必须保留基准射孔。
if (vecPerforations.size() != m_vecPerforations.size() ||
(m_eWellType == Horizontal_Fractured_Well &&
vecPerforations.isEmpty())) {
return false;
}
for (int nIndex = 0; nIndex < m_vecPerforations.size(); ++nIndex) {
if (m_vecPerforations[nIndex] == nullptr) {
return false;
}
}
// 第二步:先同步井长基准,使原井长槽自然跳过隐式缩放。
bool bSignalsWereBlocked = blockSignals(true);
m_bottomholeMD.setValue(dBottomholeMd);
m_dLastWellLength = dWellLength;
m_wellLength.setValue(dWellLength);
for (int nIndex = 0; nIndex < vecPerforations.size(); ++nIndex) {
*m_vecPerforations[nIndex] = vecPerforations[nIndex];
}
blockSignals(bSignalsWereBlocked);
// 第三步:完整数据落地后只广播一次稳定状态。
if (!bSignalsWereBlocked) {
emit sigWellDataChanged();
}
return true;
}
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),不把可选 (0,0) 占位点写入文件。
const QVector<QPointF> vecTimeQ = this->getFlowSegmentPoints();
if(vecTimeQ.isEmpty() || !hasValidFlowSectionIndex() ||
!nmIsValidWellCategory(m_eWellCategory)) {
return false;
}
// 2) sectionIndex流动段一基索引
int sectionIndex = this->getIndexF();
// 3) 输出文件名
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);
// 4) 表头与井别保持一致,便于离线排查实际进入求解器的相别。
const char* pRateColumn = m_eWellCategory == NM_WellCategory_Gas
? "qg" : (m_eWellCategory == NM_WellCategory_Water
? "qw" : "qo");
out << "t," << pRateColumn << ",sectionIndex\n";
// 5) 写入每个点
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;
}