#include "nmDataNumericalAnalysisCase.h" #include #include 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 nmDataNumericalAnalysisCase::getIncludedWells() const { return m_vecIncludedWells; } void nmDataNumericalAnalysisCase::setIncludedWells( const QVector& vecWells) { // 第一步:保存旧选择,并对新的有产量干扰井做去空和去重。 QVector vecOldWells = m_vecIncludedWells; m_vecIncludedWells = vecWells; normalizeIncludedWells(); // 第二步:井在对话框中的显示顺序不影响 PEBI 几何,只比较 WellCode 集合。 QSet setOldWellCodes; QSet 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 nmDataNumericalAnalysisCase::getEffectiveWells() const { QVector 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 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& vecOrder) { m_vecSolverWellOrder = vecOrder; for(int nIndex = 0; nIndex < m_vecSolverWellOrder.size(); ++nIndex) { m_vecSolverWellOrder[nIndex].m_nSolverIndex = nIndex; } m_bResultsValid = false; } QVector nmDataNumericalAnalysisCase::getResultSolverWellOrder() const { return m_vecResultSolverWellOrder; } QStringList nmDataNumericalAnalysisCase::getResultSelectableWellCodes() const { return m_listResultSelectableWellCodes; } void nmDataNumericalAnalysisCase::restoreResultState( const QVector& vecOrder, bool bAvailable, bool bCurrent) { restoreResultState(vecOrder, QStringList(), bAvailable, bCurrent); } void nmDataNumericalAnalysisCase::restoreResultState( const QVector& vecOrder, const QStringList& listSelectableWellCodes, bool bAvailable, bool bCurrent) { m_vecResultSolverWellOrder = vecOrder; QSet 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 vecNormalized; QSet 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 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); } } }