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/nmDataNumericalAnalysisCase...

395 lines
11 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 "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;
}
// 主井在创建数值分析时固定。项目重新加载会先调用 clear(),因此加载恢复
// 仍可从空状态初始化,但运行期间不能通过公共接口清空或换成另一口井。
if(!m_sPrimaryWellCode.isEmpty() || sWellCode.isEmpty()) {
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;
}