|
|
#include "nmDataNumericalAnalysisCase.h"
|
|
|
|
|
|
#include <QSet>
|
|
|
#include <QtGlobal>
|
|
|
|
|
|
namespace
|
|
|
{
|
|
|
// 外部枚举值只允许落到当前方案支持的两种井角色,其他值保守按观察井处理。
|
|
|
NM_CASE_WELL_MODE normalizedWellMode(NM_CASE_WELL_MODE eMode)
|
|
|
{
|
|
|
return eMode == NM_CaseWell_RateControlled
|
|
|
? NM_CaseWell_RateControlled : NM_CaseWell_Observation;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ==================== 轻量井引用 ====================
|
|
|
|
|
|
nmCalculationWellRef::nmCalculationWellRef()
|
|
|
: m_eMode(NM_CaseWell_Observation)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
nmCalculationWellRef::nmCalculationWellRef(const QString& sWellCode,
|
|
|
NM_CASE_WELL_MODE eMode)
|
|
|
: m_sWellCode(sWellCode),
|
|
|
m_eMode(eMode)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
nmSolverWellRef::nmSolverWellRef()
|
|
|
: m_nSolverIndex(-1),
|
|
|
m_eWellType(NM_WELL_MODEL::Unknow_Well),
|
|
|
m_eEntryKind(NM_SolverEntry_Well)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
nmSolverWellRef::nmSolverWellRef(int nSolverIndex,
|
|
|
NM_WELL_MODEL eWellType,
|
|
|
const QString& sWellCode,
|
|
|
NM_SOLVER_ENTRY_KIND eEntryKind)
|
|
|
: m_nSolverIndex(nSolverIndex),
|
|
|
m_eWellType(eWellType),
|
|
|
m_sWellCode(sWellCode),
|
|
|
m_eEntryKind(eEntryKind)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
nmDataNumericalAnalysisCase::nmDataNumericalAnalysisCase()
|
|
|
{
|
|
|
clear();
|
|
|
}
|
|
|
|
|
|
// ==================== 主分析井 ====================
|
|
|
|
|
|
QString nmDataNumericalAnalysisCase::getPrimaryWellCode() const
|
|
|
{
|
|
|
return m_sPrimaryWellCode;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::setPrimaryWellCode(const QString& sWellCode)
|
|
|
{
|
|
|
if(m_sPrimaryWellCode == sWellCode) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_sPrimaryWellCode = sWellCode;
|
|
|
normalizeIncludedWells();
|
|
|
invalidateGrid();
|
|
|
}
|
|
|
|
|
|
NM_CASE_WELL_MODE nmDataNumericalAnalysisCase::getPrimaryWellMode() const
|
|
|
{
|
|
|
return m_ePrimaryWellMode;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::setPrimaryWellMode(NM_CASE_WELL_MODE eMode)
|
|
|
{
|
|
|
eMode = normalizedWellMode(eMode);
|
|
|
if(m_ePrimaryWellMode == eMode) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_ePrimaryWellMode = eMode;
|
|
|
invalidateResults();
|
|
|
}
|
|
|
|
|
|
// ==================== 包含其他有产量井 ====================
|
|
|
|
|
|
bool nmDataNumericalAnalysisCase::getIncludeOtherWells() const
|
|
|
{
|
|
|
return m_bIncludeOtherWells;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::setIncludeOtherWells(bool bInclude)
|
|
|
{
|
|
|
if(m_bIncludeOtherWells == bInclude) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_bIncludeOtherWells = bInclude;
|
|
|
|
|
|
// 只有确实选过有产量干扰井时,总开关才会改变网格中的井集合。
|
|
|
// Map 中的无产量观察井不经过本开关,由数据管理层始终加入计算。
|
|
|
// 空选择下切换开关只是保存用户设置,不应触发一次无意义的网格重建。
|
|
|
if(!m_vecIncludedWells.isEmpty()) {
|
|
|
invalidateGrid();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
double nmDataNumericalAnalysisCase::getPebiGridControl() const
|
|
|
{
|
|
|
return m_dPebiGridControl;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::setPebiGridControl(double dGridControl)
|
|
|
{
|
|
|
// 第一步:网格控制值必须为正数,非法输入不能污染当前有效方案。
|
|
|
if(dGridControl <= 0.0) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 第二步:相同数值不重复增加输入版本,避免界面刷新引发无意义重划分。
|
|
|
if(qFuzzyCompare(m_dPebiGridControl, dGridControl)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 第三步:网格密度属于几何离散输入,变化后网格和结果必须同时失效。
|
|
|
m_dPebiGridControl = dGridControl;
|
|
|
invalidateGrid();
|
|
|
}
|
|
|
|
|
|
// 包含井列表只保存用户显式选择;Map 中无产量井由数据管理器动态补入有效集合。
|
|
|
QVector<nmCalculationWellRef> nmDataNumericalAnalysisCase::getIncludedWells() const
|
|
|
{
|
|
|
return m_vecIncludedWells;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::setIncludedWells(
|
|
|
const QVector<nmCalculationWellRef>& vecWells)
|
|
|
{
|
|
|
// 第一步:保存旧选择,并对新的有产量干扰井做去空和去重。
|
|
|
QVector<nmCalculationWellRef> vecOldWells = m_vecIncludedWells;
|
|
|
m_vecIncludedWells = vecWells;
|
|
|
normalizeIncludedWells();
|
|
|
|
|
|
// 第二步:井在对话框中的显示顺序不影响 PEBI 几何,只比较 WellCode 集合。
|
|
|
QSet<QString> setOldWellCodes;
|
|
|
QSet<QString> setNewWellCodes;
|
|
|
for(int nIndex = 0; nIndex < vecOldWells.size(); ++nIndex) {
|
|
|
setOldWellCodes.insert(vecOldWells[nIndex].m_sWellCode);
|
|
|
}
|
|
|
for(int nIndex = 0; nIndex < m_vecIncludedWells.size(); ++nIndex) {
|
|
|
setNewWellCodes.insert(m_vecIncludedWells[nIndex].m_sWellCode);
|
|
|
}
|
|
|
|
|
|
const bool bWellSetChanged = setOldWellCodes != setNewWellCodes;
|
|
|
|
|
|
// 第三步:总开关关闭时,包含井尚未进入有效计算集合,选择变化只需保存。
|
|
|
if(!m_bIncludeOtherWells) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 第四步:IncludedWells 中的井固定为主动井,增删井会改变网格。
|
|
|
if(bWellSetChanged) {
|
|
|
invalidateGrid();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool nmDataNumericalAnalysisCase::isIncludedWell(const QString& sWellCode) const
|
|
|
{
|
|
|
for(int nIndex = 0; nIndex < m_vecIncludedWells.size(); ++nIndex) {
|
|
|
if(m_vecIncludedWells[nIndex].m_sWellCode == sWellCode) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
NM_CASE_WELL_MODE nmDataNumericalAnalysisCase::getWellMode(
|
|
|
const QString& sWellCode) const
|
|
|
{
|
|
|
if(sWellCode == m_sPrimaryWellCode) {
|
|
|
return m_ePrimaryWellMode;
|
|
|
}
|
|
|
|
|
|
for(int nIndex = 0; nIndex < m_vecIncludedWells.size(); ++nIndex) {
|
|
|
if(m_vecIncludedWells[nIndex].m_sWellCode == sWellCode) {
|
|
|
return m_vecIncludedWells[nIndex].m_eMode;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return NM_CaseWell_Observation;
|
|
|
}
|
|
|
|
|
|
QVector<nmCalculationWellRef> nmDataNumericalAnalysisCase::getEffectiveWells() const
|
|
|
{
|
|
|
QVector<nmCalculationWellRef> vecResult;
|
|
|
|
|
|
// 第一步:主分析井始终参与当前数值分析,并固定排在第一位。
|
|
|
if(!m_sPrimaryWellCode.isEmpty()) {
|
|
|
vecResult.append(nmCalculationWellRef(m_sPrimaryWellCode,
|
|
|
m_ePrimaryWellMode));
|
|
|
}
|
|
|
|
|
|
// 第二步:这里只返回方案显式保存的井。数据管理层还会把 Map 中所有
|
|
|
// 无产量井自动追加为观察井,它们与 Include Other Wells 完全无关。
|
|
|
if(m_bIncludeOtherWells) {
|
|
|
vecResult += m_vecIncludedWells;
|
|
|
}
|
|
|
|
|
|
return vecResult;
|
|
|
}
|
|
|
|
|
|
// ==================== DLL 求解器井槽位顺序 ====================
|
|
|
|
|
|
QVector<nmSolverWellRef> nmDataNumericalAnalysisCase::getSolverWellOrder() const
|
|
|
{
|
|
|
return m_vecSolverWellOrder;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::clearSolverWellOrder()
|
|
|
{
|
|
|
m_vecSolverWellOrder.clear();
|
|
|
m_nBuiltGridRevision = 0;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::appendSolverWell(
|
|
|
const nmSolverWellRef& oWellRef)
|
|
|
{
|
|
|
nmSolverWellRef oIndexedRef = oWellRef;
|
|
|
oIndexedRef.m_nSolverIndex = m_vecSolverWellOrder.size();
|
|
|
m_vecSolverWellOrder.append(oIndexedRef);
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::insertSolverWell(
|
|
|
int nIndex,
|
|
|
const nmSolverWellRef& oWellRef)
|
|
|
{
|
|
|
if(nIndex < 0 || nIndex > m_vecSolverWellOrder.size()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_vecSolverWellOrder.insert(nIndex, oWellRef);
|
|
|
for(int nOrderIndex = 0;
|
|
|
nOrderIndex < m_vecSolverWellOrder.size();
|
|
|
++nOrderIndex) {
|
|
|
m_vecSolverWellOrder[nOrderIndex].m_nSolverIndex = nOrderIndex;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
bool nmDataNumericalAnalysisCase::removeSolverWell(int nIndex)
|
|
|
{
|
|
|
if(nIndex < 0 || nIndex >= m_vecSolverWellOrder.size()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
m_vecSolverWellOrder.remove(nIndex);
|
|
|
for(int nOrderIndex = 0;
|
|
|
nOrderIndex < m_vecSolverWellOrder.size();
|
|
|
++nOrderIndex) {
|
|
|
m_vecSolverWellOrder[nOrderIndex].m_nSolverIndex = nOrderIndex;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::setSolverWellOrder(
|
|
|
const QVector<nmSolverWellRef>& vecOrder)
|
|
|
{
|
|
|
m_vecSolverWellOrder = vecOrder;
|
|
|
for(int nIndex = 0; nIndex < m_vecSolverWellOrder.size(); ++nIndex) {
|
|
|
m_vecSolverWellOrder[nIndex].m_nSolverIndex = nIndex;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// ==================== 网格与结果版本管理 ====================
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::invalidateGrid()
|
|
|
{
|
|
|
++m_nGridInputRevision;
|
|
|
m_nBuiltGridRevision = 0;
|
|
|
m_vecSolverWellOrder.clear();
|
|
|
invalidateResults();
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::invalidateResults()
|
|
|
{
|
|
|
// 历史结果由 Manager 快照持有。这里仅增加当前求解输入版本,
|
|
|
// 快照是否仍为当前结果由双方版本比较自然得出。
|
|
|
++m_nResultInputRevision;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::markGridBuilt()
|
|
|
{
|
|
|
m_nBuiltGridRevision = m_nGridInputRevision;
|
|
|
}
|
|
|
|
|
|
bool nmDataNumericalAnalysisCase::markGridBuiltIfCurrent(
|
|
|
quint64 nGridInputRevision)
|
|
|
{
|
|
|
// 第一步:后台划分期间几何输入发生变化时,当前输出立即作废。
|
|
|
if(nGridInputRevision != m_nGridInputRevision ||
|
|
|
m_vecSolverWellOrder.isEmpty()) {
|
|
|
m_nBuiltGridRevision = 0;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 第二步:输入版本未变化时,才把求解器顺序和网格登记为同一版本。
|
|
|
markGridBuilt();
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
bool nmDataNumericalAnalysisCase::isGridValid() const
|
|
|
{
|
|
|
return m_nBuiltGridRevision != 0 &&
|
|
|
m_nBuiltGridRevision == m_nGridInputRevision &&
|
|
|
!m_vecSolverWellOrder.isEmpty();
|
|
|
}
|
|
|
|
|
|
quint64 nmDataNumericalAnalysisCase::getGridInputRevision() const
|
|
|
{
|
|
|
return m_nGridInputRevision;
|
|
|
}
|
|
|
|
|
|
quint64 nmDataNumericalAnalysisCase::getBuiltGridRevision() const
|
|
|
{
|
|
|
return m_nBuiltGridRevision;
|
|
|
}
|
|
|
|
|
|
quint64 nmDataNumericalAnalysisCase::getResultInputRevision() const
|
|
|
{
|
|
|
return m_nResultInputRevision;
|
|
|
}
|
|
|
|
|
|
bool nmDataNumericalAnalysisCase::restoreInputRevisions(
|
|
|
quint64 nGridInputRevision,
|
|
|
quint64 nBuiltGridRevision,
|
|
|
quint64 nResultInputRevision)
|
|
|
{
|
|
|
if(nGridInputRevision == 0 || nResultInputRevision == 0 ||
|
|
|
(nBuiltGridRevision != 0 &&
|
|
|
nBuiltGridRevision != nGridInputRevision))
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// v3 保存的是输入代次本身,加载期间 setter 产生的临时递增不能带入结果判断。
|
|
|
m_nGridInputRevision = nGridInputRevision;
|
|
|
m_nBuiltGridRevision = nBuiltGridRevision;
|
|
|
m_nResultInputRevision = nResultInputRevision;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
// 项目重新初始化时恢复完整默认状态,不保留上一成果的井关联或有效标记。
|
|
|
void nmDataNumericalAnalysisCase::clear()
|
|
|
{
|
|
|
m_sPrimaryWellCode.clear();
|
|
|
m_ePrimaryWellMode = NM_CaseWell_Observation;
|
|
|
m_bIncludeOtherWells = false;
|
|
|
m_dPebiGridControl = 150.0;
|
|
|
m_vecIncludedWells.clear();
|
|
|
m_vecSolverWellOrder.clear();
|
|
|
m_nGridInputRevision = 1;
|
|
|
m_nBuiltGridRevision = 0;
|
|
|
m_nResultInputRevision = 1;
|
|
|
}
|
|
|
|
|
|
void nmDataNumericalAnalysisCase::normalizeIncludedWells()
|
|
|
{
|
|
|
QVector<nmCalculationWellRef> vecNormalized;
|
|
|
QSet<QString> setSeenWellCodes;
|
|
|
|
|
|
for(int nIndex = 0; nIndex < m_vecIncludedWells.size(); ++nIndex) {
|
|
|
const nmCalculationWellRef& oWellRef = m_vecIncludedWells[nIndex];
|
|
|
if(oWellRef.m_sWellCode.isEmpty() ||
|
|
|
oWellRef.m_sWellCode == m_sPrimaryWellCode ||
|
|
|
setSeenWellCodes.contains(oWellRef.m_sWellCode)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
setSeenWellCodes.insert(oWellRef.m_sWellCode);
|
|
|
vecNormalized.append(nmCalculationWellRef(
|
|
|
oWellRef.m_sWellCode,
|
|
|
NM_CaseWell_RateControlled));
|
|
|
}
|
|
|
|
|
|
m_vecIncludedWells = vecNormalized;
|
|
|
}
|