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

504 lines
14 KiB
C++

#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;
}
const QString sOldPrimaryWellCode = m_sPrimaryWellCode;
m_sPrimaryWellCode = sWellCode;
normalizeIncludedWells();
if(!m_bResultsAvailable &&
(m_sCurrentResultWellCode.isEmpty() ||
m_sCurrentResultWellCode == sOldPrimaryWellCode)) {
m_sCurrentResultWellCode = m_sPrimaryWellCode;
}
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;
}
// ==================== 结果查看状态 ====================
QString nmDataNumericalAnalysisCase::getCurrentResultWellCode() const
{
return m_sCurrentResultWellCode;
}
void nmDataNumericalAnalysisCase::setCurrentResultWellCode(
const QString& sWellCode)
{
// 切换结果井只是界面查看状态,不应触发网格或求解结果失效。
m_sCurrentResultWellCode = sWellCode;
}
// ==================== DLL 求解器井槽位顺序 ====================
QVector<nmSolverWellRef> nmDataNumericalAnalysisCase::getSolverWellOrder() const
{
return m_vecSolverWellOrder;
}
void nmDataNumericalAnalysisCase::clearSolverWellOrder()
{
m_vecSolverWellOrder.clear();
m_nBuiltGridRevision = 0;
m_bResultsValid = false;
}
void nmDataNumericalAnalysisCase::appendSolverWell(
const nmSolverWellRef& oWellRef)
{
nmSolverWellRef oIndexedRef = oWellRef;
oIndexedRef.m_nSolverIndex = m_vecSolverWellOrder.size();
m_vecSolverWellOrder.append(oIndexedRef);
m_bResultsValid = false;
}
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;
}
m_bResultsValid = false;
}
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;
}
m_bResultsValid = false;
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;
}
m_bResultsValid = false;
}
QVector<nmSolverWellRef>
nmDataNumericalAnalysisCase::getResultSolverWellOrder() const
{
return m_vecResultSolverWellOrder;
}
void nmDataNumericalAnalysisCase::restoreResultState(
const QVector<nmSolverWellRef>& vecOrder,
bool bAvailable,
bool bCurrent)
{
m_vecResultSolverWellOrder = vecOrder;
for(int nIndex = 0;
nIndex < m_vecResultSolverWellOrder.size();
++nIndex) {
m_vecResultSolverWellOrder[nIndex].m_nSolverIndex = nIndex;
}
m_bResultsAvailable = bAvailable &&
!m_vecResultSolverWellOrder.isEmpty();
m_bResultsValid = m_bResultsAvailable && bCurrent;
if(m_bResultsAvailable) {
normalizeCurrentResultWellCode();
}
}
// ==================== 网格与结果版本管理 ====================
void nmDataNumericalAnalysisCase::invalidateGrid()
{
++m_nGridInputRevision;
m_nBuiltGridRevision = 0;
m_vecSolverWellOrder.clear();
invalidateResults();
}
void nmDataNumericalAnalysisCase::invalidateResults()
{
++m_nResultInputRevision;
m_bResultsValid = false;
}
void nmDataNumericalAnalysisCase::markGridBuilt()
{
m_nBuiltGridRevision = m_nGridInputRevision;
m_bResultsValid = false;
}
bool nmDataNumericalAnalysisCase::markGridBuiltIfCurrent(
quint64 nGridInputRevision)
{
// 第一步:后台划分期间几何输入发生变化时,当前输出立即作废。
if(nGridInputRevision != m_nGridInputRevision ||
m_vecSolverWellOrder.isEmpty()) {
m_nBuiltGridRevision = 0;
m_bResultsValid = false;
return false;
}
// 第二步:输入版本未变化时,才把求解器顺序和网格登记为同一版本。
markGridBuilt();
return true;
}
void nmDataNumericalAnalysisCase::discardResults()
{
// 重新求解本身不是输入修改,只清除有效标记,不增加结果输入版本号。
m_bResultsValid = false;
}
void nmDataNumericalAnalysisCase::markResultsAvailable()
{
if(!isGridValid()) {
return;
}
m_vecResultSolverWellOrder = m_vecSolverWellOrder;
m_bResultsAvailable = true;
m_bResultsValid = true;
normalizeCurrentResultWellCode();
}
bool nmDataNumericalAnalysisCase::markResultsAvailableIfCurrent(
quint64 nGridInputRevision,
quint64 nResultInputRevision)
{
// 第一步:任务使用的网格版本和求解输入版本必须仍是当前版本。
const bool bInputCurrent =
nGridInputRevision == m_nGridInputRevision &&
nResultInputRevision == m_nResultInputRevision;
// 第二步:只有输入未变化且网格仍有效时,才允许旧线程提交结果。
// 校验失败不能破坏此前已经存在的结果。
if(!bInputCurrent || !isGridValid()) {
return false;
}
m_vecResultSolverWellOrder = m_vecSolverWellOrder;
m_bResultsAvailable = true;
m_bResultsValid = true;
normalizeCurrentResultWellCode();
return true;
}
bool nmDataNumericalAnalysisCase::isGridValid() const
{
return m_nBuiltGridRevision != 0 &&
m_nBuiltGridRevision == m_nGridInputRevision &&
!m_vecSolverWellOrder.isEmpty();
}
bool nmDataNumericalAnalysisCase::areResultsValid() const
{
return areResultsCurrent() && isGridValid();
}
bool nmDataNumericalAnalysisCase::hasResults() const
{
return m_bResultsAvailable &&
!m_vecResultSolverWellOrder.isEmpty();
}
bool nmDataNumericalAnalysisCase::areResultsCurrent() const
{
return hasResults() && m_bResultsValid;
}
quint64 nmDataNumericalAnalysisCase::getGridInputRevision() const
{
return m_nGridInputRevision;
}
quint64 nmDataNumericalAnalysisCase::getBuiltGridRevision() const
{
return m_nBuiltGridRevision;
}
quint64 nmDataNumericalAnalysisCase::getResultInputRevision() const
{
return m_nResultInputRevision;
}
// 项目重新初始化时恢复完整默认状态,不保留上一成果的井关联或有效标记。
void nmDataNumericalAnalysisCase::clear()
{
m_sPrimaryWellCode.clear();
m_ePrimaryWellMode = NM_CaseWell_Observation;
m_bIncludeOtherWells = false;
m_dPebiGridControl = 150.0;
m_vecIncludedWells.clear();
m_sCurrentResultWellCode.clear();
m_vecSolverWellOrder.clear();
m_vecResultSolverWellOrder.clear();
m_nGridInputRevision = 1;
m_nBuiltGridRevision = 0;
m_nResultInputRevision = 1;
m_bResultsAvailable = false;
m_bResultsValid = false;
}
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;
}
void nmDataNumericalAnalysisCase::normalizeCurrentResultWellCode()
{
QString sFirstResultWellCode;
for(int nIndex = 0;
nIndex < m_vecResultSolverWellOrder.size();
++nIndex) {
const nmSolverWellRef& oWellRef =
m_vecResultSolverWellOrder[nIndex];
if(oWellRef.m_eEntryKind != NM_SolverEntry_Well) {
continue;
}
if(sFirstResultWellCode.isEmpty()) {
sFirstResultWellCode = oWellRef.m_sWellCode;
}
if(oWellRef.m_sWellCode == m_sCurrentResultWellCode) {
return;
}
}
m_sCurrentResultWellCode = sFirstResultWellCode;
}