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

555 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 "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;
}
QStringList nmDataNumericalAnalysisCase::getResultSelectableWellCodes() const
{
return m_listResultSelectableWellCodes;
}
void nmDataNumericalAnalysisCase::restoreResultState(
const QVector<nmSolverWellRef>& vecOrder,
bool bAvailable,
bool bCurrent)
{
restoreResultState(vecOrder, QStringList(), bAvailable, bCurrent);
}
void nmDataNumericalAnalysisCase::restoreResultState(
const QVector<nmSolverWellRef>& vecOrder,
const QStringList& listSelectableWellCodes,
bool bAvailable,
bool bCurrent)
{
m_vecResultSolverWellOrder = vecOrder;
QSet<QString> setResultWellCodes;
for(int nIndex = 0;
nIndex < m_vecResultSolverWellOrder.size();
++nIndex) {
m_vecResultSolverWellOrder[nIndex].m_nSolverIndex = nIndex;
const nmSolverWellRef& oWellRef = m_vecResultSolverWellOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
setResultWellCodes.insert(oWellRef.m_sWellCode);
}
}
m_listResultSelectableWellCodes.clear();
for(int nIndex = 0; nIndex < listSelectableWellCodes.size(); ++nIndex) {
const QString sWellCode = listSelectableWellCodes[nIndex];
if(setResultWellCodes.contains(sWellCode) &&
!m_listResultSelectableWellCodes.contains(sWellCode)) {
m_listResultSelectableWellCodes.append(sWellCode);
}
}
m_bResultsAvailable = bAvailable &&
!m_vecResultSolverWellOrder.isEmpty();
m_bResultsValid = m_bResultsAvailable && bCurrent;
if(m_bResultsAvailable) {
// 旧成果没有独立可显示井字段时,按保存的主井和包含井保守恢复。
if(m_listResultSelectableWellCodes.isEmpty()) {
freezeResultSelectableWellCodes();
}
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;
freezeResultSelectableWellCodes();
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;
freezeResultSelectableWellCodes();
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_listResultSelectableWellCodes.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()
{
if(m_listResultSelectableWellCodes.contains(
m_sCurrentResultWellCode)) {
return;
}
m_sCurrentResultWellCode = m_listResultSelectableWellCodes.isEmpty()
? QString() : m_listResultSelectableWellCodes.first();
}
void nmDataNumericalAnalysisCase::freezeResultSelectableWellCodes()
{
QSet<QString> setSelectableWellCodes;
if(!m_sPrimaryWellCode.isEmpty()) {
setSelectableWellCodes.insert(m_sPrimaryWellCode);
}
if(m_bIncludeOtherWells) {
for(int nIndex = 0; nIndex < m_vecIncludedWells.size(); ++nIndex) {
setSelectableWellCodes.insert(
m_vecIncludedWells[nIndex].m_sWellCode);
}
}
m_listResultSelectableWellCodes.clear();
for(int nIndex = 0;
nIndex < m_vecResultSolverWellOrder.size();
++nIndex) {
const nmSolverWellRef& oWellRef =
m_vecResultSolverWellOrder[nIndex];
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well &&
setSelectableWellCodes.contains(oWellRef.m_sWellCode) &&
!m_listResultSelectableWellCodes.contains(oWellRef.m_sWellCode)) {
m_listResultSelectableWellCodes.append(oWellRef.m_sWellCode);
}
}
}