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/nmSubWxs/nmWellEditorSession.cpp

470 lines
16 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 "nmWellEditorSession.h"
#include "nmDataHorizontalFracturedWell.h"
#include "nmDataPerforation.h"
#include "nmDataVerticalFracturedWell.h"
#include "nmDataVerticalWell.h"
#include <QCoreApplication>
#include <QtGlobal>
namespace
{
/* Qt 4.8的lupdate需要显式标记间接翻译字符串。 */
static const char* const s_pSessionTranslationMarkers[] =
{
QT_TRANSLATE_NOOP("nmWellEditorSession", "The original well data is unavailable."),
QT_TRANSLATE_NOOP("nmWellEditorSession", "The well type is not supported."),
QT_TRANSLATE_NOOP("nmWellEditorSession", "Failed to create the well working copy."),
QT_TRANSLATE_NOOP("nmWellEditorSession", "The well editing session is unavailable."),
QT_TRANSLATE_NOOP("nmWellEditorSession", "Failed to create the selected well type."),
QT_TRANSLATE_NOOP("nmWellEditorSession", "Failed to preserve the well identity.")
};
QString sessionText(const char* pText)
{
Q_UNUSED(s_pSessionTranslationMarkers);
return QCoreApplication::translate("nmWellEditorSession", pText);
}
bool isSupportedWellType(NM_WELL_MODEL eWellType)
{
// 首期编辑器只开放已完成字段目录和轨迹交互的三种井型。
return eWellType == Vertical_Well ||
eWellType == Vertical_Fractured_Well ||
eWellType == Horizontal_Fractured_Well;
}
bool isVerticalWellType(NM_WELL_MODEL eWellType)
{
return eWellType == Vertical_Well ||
eWellType == Vertical_Fractured_Well;
}
bool arePerforationTypesCompatible(NM_WELL_MODEL eSourceType,
NM_WELL_MODEL eTargetType)
{
// 直井和压裂直井的射孔MD语义一致多段压裂水平井使用独立水平段。
return isVerticalWellType(eSourceType) &&
isVerticalWellType(eTargetType);
}
void ensureRequiredPerforations(nmDataWellBase* pWell)
{
nmDataHorizontalFracturedWell* pHorizontal =
dynamic_cast<nmDataHorizontalFracturedWell*>(pWell);
if (pHorizontal != nullptr)
{
pHorizontal->ensureBasePerforation();
}
}
double wellboreTopMd(nmDataWellBase* pWell)
{
if (pWell == nullptr)
{
return 0.0;
}
// 水平井的有效井筒从第一段射孔起点开始其余井型使用井底MD。
if (dynamic_cast<nmDataHorizontalFracturedWell*>(pWell) != nullptr &&
pWell->getPerforationCount() > 0)
{
nmDataPerforation* pFirstPerforation = pWell->getPerforation(0);
if (pFirstPerforation != nullptr)
{
return pFirstPerforation->getMdStart().getValue().toDouble();
}
}
return pWell->getBottomholeMD().getValue().toDouble();
}
double mapPerforationMd(double dSourceMd,
double dSourceTop,
double dSourceLength,
double dTargetTop,
double dTargetLength)
{
// 异常旧数据无法按比例换算时只平移井筒起点,避免除零或产生非数值。
if (dSourceLength <= 0.0 || dTargetLength <= 0.0)
{
return dSourceMd + dTargetTop - dSourceTop;
}
double dRelativePosition =
(dSourceMd - dSourceTop) / dSourceLength;
return dTargetTop + dRelativePosition * dTargetLength;
}
}
nmWellEditorSession::nmWellEditorSession(
const nmWellEditContext& oContext)
: m_oContext(oContext),
m_mapWorkingWells(),
m_nCurrentWellType(static_cast<int>(Unknow_Well)),
m_bInitialized(false)
{
}
nmWellEditorSession::~nmWellEditorSession()
{
// 会话独占所有缓存已由takeModifiedWell()转移的对象不再位于映射中。
qDeleteAll(m_mapWorkingWells);
m_mapWorkingWells.clear();
m_oContext.m_pFrameworkWell = nullptr;
m_oContext.m_pOriginalWell = nullptr;
}
bool nmWellEditorSession::initialize(QString& sError)
{
if (m_oContext.m_pOriginalWell == nullptr)
{
sError = sessionText("The original well data is unavailable.");
return false;
}
NM_WELL_MODEL eWellType =
m_oContext.m_pOriginalWell->getWellType();
if (!isSupportedWellType(eWellType))
{
sError = sessionText("The well type is not supported.");
return false;
}
// 原始井只作为克隆源,后续界面和子窗口均操作独立工作副本。
nmDataWellBase* pWorkingWell =
m_oContext.m_pOriginalWell->clone();
if (pWorkingWell == nullptr)
{
sError = sessionText("Failed to create the well working copy.");
return false;
}
// 异常旧工程可能包含零射孔水平井,进入编辑会话前恢复业务不变量。
ensureRequiredPerforations(pWorkingWell);
m_nCurrentWellType = static_cast<int>(eWellType);
m_mapWorkingWells.insert(m_nCurrentWellType, pWorkingWell);
m_bInitialized = true;
sError.clear();
return true;
}
nmDataWellBase* nmWellEditorSession::currentWell() const
{
return m_mapWorkingWells.value(m_nCurrentWellType, nullptr);
}
NM_WELL_MODEL nmWellEditorSession::currentWellType() const
{
return static_cast<NM_WELL_MODEL>(m_nCurrentWellType);
}
bool nmWellEditorSession::switchWellType(NM_WELL_MODEL eWellType,
QString& sError)
{
if (!m_bInitialized)
{
sError = sessionText("The well editing session is unavailable.");
return false;
}
if (!isSupportedWellType(eWellType))
{
sError = sessionText("The well type is not supported.");
return false;
}
int nTargetType = static_cast<int>(eWellType);
if (nTargetType == m_nCurrentWellType)
{
sError.clear();
return true;
}
// 第一步:已有井型直接恢复缓存,避免往返切换丢失输入。
if (m_mapWorkingWells.contains(nTargetType))
{
m_nCurrentWellType = nTargetType;
sError.clear();
return true;
}
// 第二步:首次进入井型时只迁移共享数据,保留目标井型默认几何。
nmDataWellBase* pNewWell = createWell(eWellType);
if (pNewWell == nullptr)
{
sError = sessionText("Failed to create the selected well type.");
return false;
}
nmDataWellBase* pSharedSource = currentWell();
nmDataWellBase* pPerforationSource =
findCompatiblePerforationSource(eWellType);
if (!initializeWorkingWell(pSharedSource,
pPerforationSource,
pNewWell))
{
delete pNewWell;
sError = sessionText("Failed to preserve the well identity.");
return false;
}
m_mapWorkingWells.insert(nTargetType, pNewWell);
m_nCurrentWellType = nTargetType;
sError.clear();
return true;
}
void nmWellEditorSession::setWellName(const QString& sWellName)
{
// 井名属于所有井型共有信息,必须同步缓存以保证往返切换后一致。
QMap<int, nmDataWellBase*>::iterator oIt =
m_mapWorkingWells.begin();
for (; oIt != m_mapWorkingWells.end(); ++oIt)
{
if (oIt.value() != nullptr)
{
oIt.value()->setWellName(sWellName);
}
}
}
void nmWellEditorSession::setTimeDependentSkin(bool bEnabled)
{
// 详情数据属于射孔,只能与当前井型工作副本保持一致。
nmDataWellBase* pWell = currentWell();
if (pWell != nullptr)
{
pWell->setTimeDependentSkin(bEnabled);
}
}
QStringList nmWellEditorSession::otherWellNames() const
{
return m_oContext.m_listOtherWellNames;
}
nmDataWellBase* nmWellEditorSession::takeModifiedWell()
{
if (!m_bInitialized)
{
return nullptr;
}
// 第一步:从映射摘除最终副本,使会话不再拥有其所有权。
nmDataWellBase* pModifiedWell =
m_mapWorkingWells.take(m_nCurrentWellType);
// 第二步:销毁未选中的井型缓存,避免临时对象泄漏到提交层。
qDeleteAll(m_mapWorkingWells);
m_mapWorkingWells.clear();
m_bInitialized = false;
return pModifiedWell;
}
nmDataWellBase* nmWellEditorSession::createWell(
NM_WELL_MODEL eWellType) const
{
// 必须调用各派生类默认构造,目标井型的井长和裂缝默认值由此保留。
switch (eWellType)
{
case Vertical_Well:
return new nmDataVerticalWell();
case Vertical_Fractured_Well:
return new nmDataVerticalFracturedWell();
case Horizontal_Fractured_Well:
return new nmDataHorizontalFracturedWell();
default:
return nullptr;
}
}
nmDataWellBase* nmWellEditorSession::findCompatiblePerforationSource(
NM_WELL_MODEL eTargetType) const
{
// 第一步:优先使用当前工作副本,保持连续切换时的最新输入。
nmDataWellBase* pCurrentWell = currentWell();
if (pCurrentWell != nullptr &&
arePerforationTypesCompatible(
pCurrentWell->getWellType(), eTargetType))
{
return pCurrentWell;
}
// 第二步:切换经过水平井时,从已有垂直井缓存恢复兼容射孔来源。
QMap<int, nmDataWellBase*>::const_iterator oIt =
m_mapWorkingWells.constBegin();
for (; oIt != m_mapWorkingWells.constEnd(); ++oIt)
{
nmDataWellBase* pCandidate = oIt.value();
if (pCandidate != nullptr &&
arePerforationTypesCompatible(
pCandidate->getWellType(), eTargetType))
{
return pCandidate;
}
}
return nullptr;
}
bool nmWellEditorSession::initializeWorkingWell(
nmDataWellBase* pSharedSource,
nmDataWellBase* pPerforationSource,
nmDataWellBase* pTarget) const
{
if (pSharedSource == nullptr || pTarget == nullptr)
{
return false;
}
// 第一步:白名单复制跨井型仍具有相同含义的数据。
if (!copySharedWellData(pSharedSource, pTarget))
{
return false;
}
// 第二步:只在垂直井族内迁移射孔;跨井族保留目标构造默认值。
if (pPerforationSource != nullptr)
{
pTarget->setTimeDependentSkin(
pPerforationSource->isTimeDependentSkin());
migratePerforationData(pPerforationSource, pTarget);
}
ensureRequiredPerforations(pTarget);
// 第三步:目标对象完全初始化后统一重建派生几何。
rebuildDerivedGeometry(pTarget);
return true;
}
bool nmWellEditorSession::copySharedWellData(
nmDataWellBase* pSource,
nmDataWellBase* pTarget) const
{
if (pSource == nullptr || pTarget == nullptr)
{
return false;
}
// 只复制跨井型语义完全一致的字段,严禁使用基类整体赋值覆盖目标默认值。
// 第一步:复制井身份、位置和通用井筒配置。
if (!pTarget->restoreWellInstanceId(pSource->getWellInstanceId()))
{
return false;
}
pTarget->setWellName(pSource->getWellName());
pTarget->setWellCode(pSource->getWellCode());
// 井型切换只改变几何模型,工程井井别和流量相别必须保持不变。
pTarget->setWellCategory(pSource->getWellCategory());
pTarget->setX(pSource->getX());
pTarget->setY(pSource->getY());
pTarget->setRadius(pSource->getRadius());
pTarget->setDrillFloorElevation(pSource->getDrillFloorElevation());
pTarget->setZw(pSource->getZw());
pTarget->setRateDependentSkin(pSource->getRateDependentSkin());
pTarget->setdSdQ(pSource->getdSdQ());
pTarget->setWellboreModel(pSource->getWellboreModel());
pTarget->setWellboreStorage(pSource->getWellboreStorage());
pTarget->setFinalWellboreStorage(pSource->getFinalWellboreStorage());
pTarget->setInputWellHead(pSource->getInputWellHead());
pTarget->setWellHeadX(pSource->getWellHeadX());
pTarget->setWellHeadY(pSource->getWellHeadY());
pTarget->setCInitialCFinal(pSource->getCInitialCFinal());
pTarget->setDtChangingStorage(pSource->getDtChangingStorage());
pTarget->setLeakSkin(pSource->getLeakSkin());
// 第二步:保留输入历史和已有计算结果,切换井型不应丢失曲线数据。
// 必须整体拷贝记录列表和选中 Code不能用 setPressurePoints/setFlowPoints(phase)
// 逐点写入——目标对象是刚构造的新井,尚未选中任何记录,逐点写入会按阶段一的
// 兼容语义各自新建 1 条匿名占位记录,丢失全部真实计量记录和已选中的 Gauge Code。
pTarget->setPressureRecords(pSource->getPressureRecords());
pTarget->setFlowRecords(pSource->getFlowRecords());
pTarget->selectPressureGaugeCode(pSource->getSelectedPressureGaugeCode());
pTarget->selectFlowGaugeCode(pSource->getSelectedFlowGaugeCode());
// 井型切换只能改变几何模型,不能重置用户选择的分相产量。
pTarget->setUseOilRate(pSource->getUseOilRate());
pTarget->setUseGasRate(pSource->getUseGasRate());
pTarget->setUseWaterRate(pSource->getUseWaterRate());
pTarget->setIndexF(pSource->getIndexF());
pTarget->setHistoryData(pSource->getHistoryPressure(),
pSource->getHistoryLogLog(),
pSource->getHistorySemiLog(),
pSource->getHistoryGaugeInputSha1());
// 第三步:保留图元显示属性和跨井型开关。
pTarget->setPlotVisible(pSource->getPlotVisible());
pTarget->setDotStyle(pSource->getDotStyle());
pTarget->setDotColor(pSource->getDotColorR(),
pSource->getDotColorG(),
pSource->getDotColorB());
pTarget->setDotRadius(pSource->getDotRadius());
pTarget->setDotFilling(pSource->getDotFilling());
pTarget->setShowSubObjs(pSource->getShowSubObjs());
return true;
}
void nmWellEditorSession::migratePerforationData(
nmDataWellBase* pSource,
nmDataWellBase* pTarget) const
{
if (pSource == nullptr || pTarget == nullptr)
{
return;
}
// 第一步:清空目标默认射孔前保存两种井型各自的井筒坐标范围。
double dSourceTop = wellboreTopMd(pSource);
double dSourceLength = pSource->getWellLength().getValue().toDouble();
double dTargetTop = wellboreTopMd(pTarget);
double dTargetLength = pTarget->getWellLength().getValue().toDouble();
int nSourceCount = pSource->getPerforationCount();
pTarget->clearPerforations();
// 第二步:拷贝构造保留表皮、流动段、时间状态和图表位置等完整数据。
for (int nIndex = 0; nIndex < nSourceCount; ++nIndex)
{
nmDataPerforation* pSourcePerforation =
pSource->getPerforation(nIndex);
if (pSourcePerforation == nullptr)
{
continue;
}
nmDataPerforation* pTargetPerforation =
new nmDataPerforation(*pSourcePerforation);
double dTargetStart = mapPerforationMd(
pSourcePerforation->getMdStart().getValue().toDouble(),
dSourceTop,
dSourceLength,
dTargetTop,
dTargetLength);
double dTargetEnd = mapPerforationMd(
pSourcePerforation->getMdEnd().getValue().toDouble(),
dSourceTop,
dSourceLength,
dTargetTop,
dTargetLength);
pTargetPerforation->getMdStart().setValue(dTargetStart);
pTargetPerforation->getMdEnd().setValue(dTargetEnd);
pTarget->addPerforation(pTargetPerforation);
}
}
void nmWellEditorSession::rebuildDerivedGeometry(
nmDataWellBase* pWell) const
{
// 裂缝集合是参数派生数据,必须在共享字段迁移完成后统一重建。
if (nmDataVerticalFracturedWell* pVertical =
dynamic_cast<nmDataVerticalFracturedWell*>(pWell))
{
pVertical->setFracs();
}
if (nmDataHorizontalFracturedWell* pHorizontal =
dynamic_cast<nmDataHorizontalFracturedWell*>(pWell))
{
pHorizontal->setFracs();
}
}