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

458 lines
15 KiB
C++

#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->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());
// 第二步:保留输入历史和已有计算结果,切换井型不应丢失曲线数据。
pTarget->setPressurePoints(pSource->getPressurePoints());
pTarget->setFlowPoints(pSource->getFlowPoints());
pTarget->setIndexF(pSource->getIndexF());
pTarget->setHistoryPressure(pSource->getHistoryPressure());
pTarget->setHistoryLogLog(pSource->getHistoryLogLog());
pTarget->setHistorySemiLog(pSource->getHistorySemiLog());
// 第三步:保留图元显示属性和跨井型开关。
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();
}
}