refactor(nmNum): 清理PEBI旧结果状态并统一使用结果快照

- 删除 AnalysisCase 中旧结果状态和结果井字段
- 删除 Manager 中旧结果网格、压力帧及井位置载荷
- 求解任务统一提交完整结果快照
- 井结果 getter 改为仅从快照弱引用读取
- 增加快照提交内存异常保护
feature/pebi-result-snapshot-20260831
lh 2 weeks ago
parent 50c2ab93f4
commit 3cf01e046b

@ -5,7 +5,6 @@
#include <QString>
#include <QVector>
#include <QMap>
#include <QPointF>
#include <QAtomicInt>
#include <QSharedPointer>
#include <iostream>
@ -19,7 +18,6 @@
class nmDataAnalyzeManager;
class nmPebiResultSnapshot;
class vtkDoubleArray;
class vtkUnstructuredGrid;
struct nmPebiSolverInputSnapshot;
struct nmPebiManualCaptureState;
@ -116,7 +114,6 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
QVector<QVector<double> > m_vecPressure; ///< 时间和井底压力。
QVector<QVector<double> > m_vecLogLog; ///< 时间、压差及导数。
QVector<QVector<double> > m_vecSemiLog; ///< 时间和半对数压力。
QPointF m_oLocation; ///< 结果网格中的井坐标。
};
QString m_sPostprocessingDir; ///< 本次求解使用的后处理输出目录。
@ -143,9 +140,6 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
QVector<QVector<double> > m_autoFitResultSemiLog; ///< 目标井半对数结果。
QVector<nmPebiWellResultSnapshot> m_vecPendingWellResults; ///< 完整求解的井结果快照。
QMap<double, vtkSmartPointer<vtkDoubleArray> > m_mapPendingTimeSteps; ///< 场压力时间步快照。
QMap<QString, QPointF> m_mapPendingWellLocations; ///< 旧 UI 兼容层使用的井编码位置映射。
/** @brief 发布前为求解基础网格,候选封闭后改为旧 UI 独占的结构网格。 */
vtkSmartPointer<vtkUnstructuredGrid> m_pPendingResultGrid;
/** @brief 工作线程已经完整校验、等待主线程发布的可写候选引用。 */
QSharedPointer<nmPebiResultSnapshot> m_pPendingResultSnapshot;
double m_dPendingScalarMin; ///< 全部场压力的最小值。

@ -20,6 +20,7 @@
#include <QPointF>
#include <QPair>
#include <QString>
#include <QStringList>
#include <QList>
#include <QMutex>
#include <QSharedPointer>
@ -28,7 +29,6 @@
#include <vtkUnstructuredGrid.h>
#include <vtkSmartPointer.h>
#include <vtkDoubleArray.h>
// 子类别显示信息结构
struct ChildCategoryDisplayInfo {
@ -487,9 +487,6 @@ public:
NM_CASE_WELL_MODE getCalculationWellMode(const QString& sWellCode) const;
/** @brief 判断井是否属于当前真实求解井集合。 */
bool isWellSelectedForCalculation(const QString& sWellCode) const;
/** @brief 判断井是否允许出现在结果井下拉框。 */
bool isWellAvailableForResult(const QString& sWellCode) const;
/** @brief 清空网格生成器写入的 DLL 井槽位顺序。 */
void clearSolverWellOrder();
/** @brief 向 DLL 井槽位顺序末尾追加一个真实井或裂缝条目。 */
@ -498,8 +495,6 @@ public:
void insertSolverWell(int nIndex, const nmSolverWellRef& oWellRef);
/** @brief 返回网格确定的 DLL 井槽位顺序,与包含井选择相互独立。 */
QVector<nmSolverWellRef> getSolverWellOrder() const;
/** @brief 返回最后一次成功求解使用的 DLL 井槽位顺序。 */
QVector<nmSolverWellRef> getResultSolverWellOrder() const;
/** @brief 整体替换 DLL 井槽位顺序,并由分析方案统一重编号。 */
void setSolverWellOrder(const QVector<nmSolverWellRef>& vecSolverWellOrder);
/**
@ -524,14 +519,12 @@ public:
void invalidatePebiGrid();
/** @brief 产量、PVT 等变化后,仅使 PEBI 结果失效。 */
void invalidatePebiResults();
/** @brief 丢弃旧结果有效标记,不把本次重新求解视为输入变化。 */
void discardPebiResults();
/** @brief 判断当前 PEBI 网格是否与最新输入一致。 */
bool isPebiGridValid() const;
/** @brief 判断是否存在一套可保存和查看的 PEBI 结果。 */
bool hasPebiResults() const;
/** @brief 返回并行接入的最后成功结果快照,不改变旧结果流程。 */
/** @brief 返回最后一次成功求解发布的完整只读结果快照。 */
QSharedPointer<const nmPebiResultSnapshot>
getPebiResultSnapshot() const;
/** @brief 是否已发布完整结果快照。 */
@ -550,27 +543,9 @@ public:
bool commitPebiResultSnapshot(
QSharedPointer<nmPebiResultSnapshot>& pCandidate,
QString* pError = NULL);
/**
* @brief UI
* @note
*/
bool commitPebiResultSnapshot(
QSharedPointer<nmPebiResultSnapshot>& pCandidate,
QMap<double, vtkSmartPointer<vtkDoubleArray> >&
mapLegacyTimeSteps,
vtkSmartPointer<vtkUnstructuredGrid>& pLegacyResultGrid,
QMap<QString, QPointF>& mapLegacyWellLocations,
QString* pError = NULL);
/** @brief 清除并行快照及其结果井查看状态。 */
/** @brief 清除最后结果快照及其结果井查看状态。 */
void clearPebiResultSnapshot();
/** @brief 设置结果查看井;不改变主分析井和参数编辑井。 */
void setCurrentResultWellCode(const QString& sWellCode);
/** @brief 返回结果查看井 WellCode。 */
QString getCurrentResultWellCode() const;
/** @brief 返回结果查看井对象,井不存在时返回空指针。 */
nmDataWellBase* getCurrentResultWellData() const;
// 设置当前查看井
void setCurWellData(nmDataWellBase* wellData);
// 获取当前查看井
@ -686,14 +661,6 @@ public:
const QString& sMainJsonPath,
QString* pError = NULL);
// 保存数值成果到本地
// @param sRstCode 当前成果ID
// @param pSubWndF 当前分析窗体
// @param bClearRoot 是否清空成果目录仅在遍历保存的第一个窗口时传true
// 清理旧窗口残留后续窗口传false避免误删其他窗口数据
// @return 保存成功返回 true否则返回 false
bool saveNmResult(QString sRstCode, iSubWndFitting* pSubWndF, bool bClearRoot = false);
// 加载本地成果到内存中
// @param sLoadAnalDir 加载的分析目录
// @return 保存成功返回 true否则返回 false
@ -723,68 +690,6 @@ public:
vtkSmartPointer<vtkUnstructuredGrid> getUnstructuredGrid() const;
// 清理当前分析下的VTK网格对象
void clearUnstructuredGrid();
// 获取当前分析下的VTK网格对象的深拷贝对象一般复制给m_pResultBaseGrid使用深拷贝防止场图污染网格
vtkSmartPointer<vtkUnstructuredGrid> getUnstructuredGridCopy() const;
// 设置当前分析下的结果基础网格
void setResultBaseGrid(vtkSmartPointer<vtkUnstructuredGrid> grid);
// 获取当前分析下的结果基础网格
vtkSmartPointer<vtkUnstructuredGrid> getResultBaseGrid() const;
// 获取当前分析下的结果基础网格的深拷贝对象,一般作为场图渲染的基础网格
vtkSmartPointer<vtkUnstructuredGrid> getResultBaseGridCopy() const;
// 将非结构化网格数据写入文件
bool writeUnstructuredGridToFile(vtkSmartPointer<vtkUnstructuredGrid> grid, const QString& filePath);
// 从文件读取非结构化网格数据
bool readUnstructuredGridFromFile(vtkSmartPointer<vtkUnstructuredGrid>& grid, const QString& filePath);
// 添加时间步数据
void addTimeStep(double time, vtkSmartPointer<vtkDoubleArray> data);
/** @brief 清空上一轮求解遗留的全部时间步压力场。 */
void clearTimeSteps();
// 获取时间步数据
vtkSmartPointer<vtkDoubleArray> getTimeStepData(double time) const;
// 获取时间步数据 QMap 的所有键(时间点)
QList<double> getTimeStepKeys();
// 判断当前是否存储了时间步数据
bool isTimeStepDataEmpty();
// 保存整个时间步数据QMap到单个二进制文件
bool writeTimeStepDataMapToBinaryFile(const QString& filePath);
// 从单个二进制文件加载整个时间步数据QMap
bool readTimeStepDataMapFromBinaryFile(const QString& filePath);
// 保存井的历史数据(所有井都要调用)
bool saveWellHistoryData(QString sDir, nmDataWellBase* pWellData);
// 加载井的历史数据(所有井都要调用)
bool loadWellHistoryData(QString sDir, nmDataWellBase* pWellData);
// 保存参与计算井的结果到本地,压力、双对数、半对数
bool saveWellCalRstData(QString sDir, nmDataWellBase* pWellData);
// 加载井的计算结果到内存
bool loadWellCalRstData(QString sDir, nmDataWellBase* pWellData);
// 获取 / 设置 压力标量值的范围
void getScalarRangeP(double range[2]) const;
void setScalarRangeP(double min, double max);
/** @brief 按 WellCode 保存本次真实求解井的二维坐标。 */
void addWellLocation(const QString& sWellCode, const QPointF& location);
/** @brief 返回指定 WellCode 的求解井坐标,不存在时返回默认 QPointF。 */
QPointF getWellLocation(const QString& sWellCode) const;
/** @brief 删除指定 WellCode 的求解井坐标,实际删除时返回 true。 */
bool removeWellLocation(const QString& sWellCode);
/** @brief 清空全部求解井坐标。 */
void clearWellLocations();
/** @brief 返回全部 WellCode 到求解井坐标的映射副本。 */
QMap<QString, QPointF> getAllWellLocations() const;
// 保存/加载当前场图中的井位置相关信息
bool saveWellLocations(const QString& filePath);
bool loadWellLocations(const QString& filePath);
// 创建时间步数据
//void createTimeStep();
nmDataTimeStepSetting* createTimeStep();
@ -918,10 +823,10 @@ public:
// 孔射封堵
//nmDataPerforationClosing* m_pPerCloData;
// 当前数值分析方案:保存选井、井角色、结果井和网格产生的求解器顺序
// 当前数值分析方案:保存选井、井角色、当前网格槽位和三个输入版本
nmDataNumericalAnalysisCase m_oNumericalAnalysisCase;
/** @brief 最后一次成功结果;第二阶段与旧结果载荷并行存在。 */
/** @brief 最后一次成功求解发布的完整只读结果。 */
QSharedPointer<const nmPebiResultSnapshot> m_pPebiResultSnapshot;
/** @brief 快照查看状态中当前选择的结果井 UUID。 */
QString m_sCurrentResultWellInstanceId;
@ -941,18 +846,6 @@ public:
// VTK非结构化网格对象用于展示网格划分窗体
vtkSmartPointer<vtkUnstructuredGrid> m_pVtkUnstructuredGrid;
// VTK非结构化网格对象用于结果场图里的基础网格
vtkSmartPointer<vtkUnstructuredGrid> m_pResultBaseGrid;
// 时间步压力数据
QMap<double, vtkSmartPointer<vtkDoubleArray>> m_mapTimeStepDataP;
// 压力值范围 [min, max]
double m_dScalarRangeP[2];
// 存储 WellCode 及其二维位置坐标的映射,只包含本次真实求解井。
QMap<QString, QPointF> m_mapWellLocations;
// 时间步设置数据
nmDataTimeStepSetting* m_pTimeStep;

@ -5,7 +5,6 @@
#include "nmDefines.h"
#include <QString>
#include <QStringList>
#include <QVector>
/**
@ -67,7 +66,7 @@ struct NM_DATA_EXPORT nmSolverWellRef
};
/**
* @brief
* @brief
*
* Map
* Map
@ -110,11 +109,6 @@ public:
/** @brief 返回方案显式选择的井Map 观察井由数据管理层自动补充。 */
QVector<nmCalculationWellRef> getEffectiveWells() const;
/** @brief 返回界面当前查看结果的井编码。 */
QString getCurrentResultWellCode() const;
/** @brief 切换结果查看井;不改变主井、网格或已计算结果。 */
void setCurrentResultWellCode(const QString& sWellCode);
/** @brief 返回网格生成后确定的 DLL 井槽位顺序。 */
QVector<nmSolverWellRef> getSolverWellOrder() const;
/** @brief 清空求解器顺序,并将已建网格和结果标记为无效。 */
@ -128,20 +122,6 @@ public:
/** @brief 整体替换求解器顺序,并按容器顺序重新编号。 */
void setSolverWellOrder(const QVector<nmSolverWellRef>& vecOrder);
/** @brief 返回最后一次成功求解所使用的 DLL 井槽位顺序。 */
QVector<nmSolverWellRef> getResultSolverWellOrder() const;
/** @brief 返回最后一次成功求解时冻结的结果下拉框井编码。 */
QStringList getResultSelectableWellCodes() const;
/** @brief 从成果文件恢复结果存在状态、当前性和独立井顺序。 */
void restoreResultState(const QVector<nmSolverWellRef>& vecOrder,
bool bAvailable,
bool bCurrent);
/** @brief 从成果文件同时恢复冻结的结果下拉框井编码。 */
void restoreResultState(const QVector<nmSolverWellRef>& vecOrder,
const QStringList& listSelectableWellCodes,
bool bAvailable,
bool bCurrent);
/** @brief 选井或几何输入改变后,使当前网格和结果失效。 */
void invalidateGrid();
/** @brief 产量、PVT 等只影响求解的输入改变后,仅使结果失效。 */
@ -150,22 +130,8 @@ public:
void markGridBuilt();
/** @brief 仅当网格生成期间几何输入未变化时提交网格。 */
bool markGridBuiltIfCurrent(quint64 nGridInputRevision);
/** @brief 丢弃旧结果,但不改变输入版本号。 */
void discardResults();
/** @brief 求解成功并完成结果映射后记录结果有效。 */
void markResultsAvailable();
/** @brief 仅当任务启动后的网格和求解输入均未变化时提交结果。 */
bool markResultsAvailableIfCurrent(quint64 nGridInputRevision,
quint64 nResultInputRevision);
/** @brief 当前网格版本与几何输入一致且井顺序非空时返回 true。 */
bool isGridValid() const;
/** @brief 内存中存在一套可保存和查看的完整求解结果时返回 true。 */
bool hasResults() const;
/** @brief 已有结果仍对应当前输入时返回 true。 */
bool areResultsCurrent() const;
/** @brief 当前结果已提交且所依赖网格仍有效时返回 true。 */
bool areResultsValid() const;
/** @brief 返回最新几何/选井输入版本,供后台任务启动时捕获。 */
quint64 getGridInputRevision() const;
/** @brief 返回当前缓存网格对应的输入版本0 表示没有有效网格。 */
@ -186,11 +152,6 @@ public:
private:
/** @brief 规范化有产量包含井,移除空编码、主井和重复项。 */
void normalizeIncludedWells();
/** @brief 保证当前结果井属于最后结果顺序,否则回退到第一口真实井。 */
void normalizeCurrentResultWellCode();
/** @brief 按本次主井和已启用主动井冻结结果下拉框井编码。 */
void freezeResultSelectableWellCodes();
private:
QString m_sPrimaryWellCode; ///< 主分析井的项目唯一 WellCode。
NM_CASE_WELL_MODE m_ePrimaryWellMode; ///< 主井在本次方案中的源汇角色。
@ -198,16 +159,11 @@ private:
double m_dPebiGridControl; ///< 当前方案使用的 PEBI 网格疏密参数。
QVector<nmCalculationWellRef> m_vecIncludedWells; ///< 已选择的其他有产量井,不含自动观察井。
QString m_sCurrentResultWellCode; ///< 当前结果查看井,仅属于展示状态。
QVector<nmSolverWellRef> m_vecSolverWellOrder; ///< 网格确定的 DLL 数组下标映射。
QVector<nmSolverWellRef> m_vecResultSolverWellOrder; ///< 最后结果使用的独立槽位映射。
QStringList m_listResultSelectableWellCodes; ///< 最后结果允许在下拉框中显示的井编码。
quint64 m_nGridInputRevision; ///< 几何、选井等网格输入的最新版本。
quint64 m_nBuiltGridRevision; ///< 当前缓存网格对应版本0 表示无有效网格。
quint64 m_nResultInputRevision; ///< 产量、角色、PVT 等求解输入版本。
bool m_bResultsAvailable; ///< 是否存在一套可保存和查看的结果载荷。
bool m_bResultsValid; ///< 已有结果是否仍对应当前输入和网格。
};
#endif // NMDATANUMERICALANALYSISCASE_H

@ -168,20 +168,14 @@ public:
NM_WELL_MODEL getWellType() const;
// 计算结果相关方法
/** @brief 返回当前井 UUID 在最后结果快照中的压力曲线;未绑定时兼容旧成员。 */
/** @brief 返回当前井 UUID 在最后结果快照中的压力曲线;未绑定时返回空。 */
QVector<QVector<double>> getResultPressure();
/** @brief 第二阶段兼容接口,后续旧结果成员清理前仍允许写入。 */
void setResultPressure(QVector<QVector<double>> pressureData);
/** @brief 返回当前井 UUID 在最后结果快照中的双对数曲线;未绑定时兼容旧成员。 */
/** @brief 返回当前井 UUID 在最后结果快照中的双对数曲线;未绑定时返回空。 */
QVector<QVector<double>> getResultLogLog();
/** @brief 第二阶段兼容接口,后续旧结果成员清理前仍允许写入。 */
void setResultLogLog(QVector<QVector<double>> loglogData);
/** @brief 返回当前井 UUID 在最后结果快照中的半对数曲线;未绑定时兼容旧成员。 */
/** @brief 返回当前井 UUID 在最后结果快照中的半对数曲线;未绑定时返回空。 */
QVector<QVector<double>> getResultSemiLog();
/** @brief 第二阶段兼容接口,后续旧结果成员清理前仍允许写入。 */
void setResultSemiLog(QVector<QVector<double>> semiLogData);
// 时间变表皮状态的getter和setter
bool isTimeDependentSkin() const;
@ -297,11 +291,6 @@ protected:
QVector<QVector<double>> m_vvecHsyLogLog; // 历史双对数数据
QVector<QVector<double>> m_vvecHsySemiLog; // 历史半对数数据
// 计算结果数据
QVector<QVector<double>> m_vvecRstPressure; // 计算结果压力数据
QVector<QVector<double>> m_vvecRstLogLog; // 计算结果双对数数据
QVector<QVector<double>> m_vvecRstSemiLog; // 计算结果半对数数据
/** @brief 最后结果快照的弱引用,不延长历史结果生命周期。 */
QWeakPointer<const nmPebiResultSnapshot> m_pPebiResultSnapshot;

@ -1190,8 +1190,6 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
m_bPendingFullResultReady = false;
m_vecPendingWellResults.clear();
m_mapPendingTimeSteps.clear();
m_mapPendingWellLocations.clear();
m_pPendingResultGrid = nullptr;
m_pPendingResultSnapshot.clear();
m_autoFitResultPressure.clear();
m_autoFitResultLogLog.clear();
@ -1681,7 +1679,6 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
oWellResult.m_vecPressure = vvecPressure;
oWellResult.m_vecLogLog = vvecLogLog;
oWellResult.m_vecSemiLog = vvecSemiLog;
oWellResult.m_oLocation = oWellInput.m_oLocation;
m_vecPendingWellResults.append(oWellResult);
}
@ -1815,9 +1812,8 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
// 第六步:基础网格在任务创建或局部建网阶段已经完成深拷贝。后台结果构造
// 不再读取 DataManager 中可能被网格窗口替换的 VTK 指针。
m_pPendingResultGrid = m_pInputSnapshot->m_pBaseGrid;
if(m_pPendingResultGrid == nullptr ||
m_pPendingResultGrid->GetNumberOfCells() <= 0) {
if(m_pInputSnapshot->m_pBaseGrid == nullptr ||
m_pInputSnapshot->m_pBaseGrid->GetNumberOfCells() <= 0) {
return false;
}
@ -1846,8 +1842,6 @@ void nmCalculationDllPebiSolverTask::discardPendingFullResult()
m_pPendingResultSnapshot.clear();
m_vecPendingWellResults.clear();
m_mapPendingTimeSteps.clear();
m_mapPendingWellLocations.clear();
m_pPendingResultGrid = NULL;
m_bPendingFullResultReady = false;
// 候选可能与输入基础网格共享 VTK 对象,丢弃或发布后必须清除任务侧别名。
@ -1864,7 +1858,7 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
if(m_pInputSnapshot == NULL ||
m_pInputSnapshot->m_bAutoFitTargetOnly ||
!m_pInputSnapshot->m_bResultMetadataCaptured ||
m_pPendingResultGrid == NULL ||
m_pInputSnapshot->m_pBaseGrid == NULL ||
m_mapPendingTimeSteps.isEmpty()) {
return false;
}
@ -1876,11 +1870,9 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
return false;
}
// Builder 接管同一 VTK 对象的候选引用;任务暂留一份引用供第三阶段
// 旧 UI 镜像发布,主线程提交后会立即清除全部任务侧别名。
vtkSmartPointer<vtkUnstructuredGrid> pResultGrid =
m_pPendingResultGrid;
if(!oBuilder.takeResultGrid(pResultGrid)) {
// Builder 直接接管任务输入中的基础网格引用,不再为旧结果流程创建
// 第二套网格壳。输入快照残留的其他别名会在提交或丢弃边界统一清除。
if(!oBuilder.takeResultGrid(m_pInputSnapshot->m_pBaseGrid)) {
return false;
}
@ -1892,6 +1884,8 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
return false;
}
}
// 压力帧的规范引用已经进入 Builder立即释放任务侧映射降低发布前峰值。
m_mapPendingTimeSteps.clear();
if(!oBuilder.setScalarRange(m_dPendingScalarMin,
m_dPendingScalarMax)) {
return false;
@ -1906,7 +1900,6 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
}
QStringList listDisplayWellIds;
m_mapPendingWellLocations.clear();
int nResultWellIndex = 0;
for(int nSlotIndex = 0;
nSlotIndex < vecSolverOrder.size();
@ -1975,8 +1968,6 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
qSwap(oWell.m_oCurves.m_vecResultSemiLog,
oWellResult.m_vecSemiLog);
m_mapPendingWellLocations.insert(oWell.m_sWellCode,
oWell.m_oLocation);
if(oWellInput.m_bDisplayResultWell) {
listDisplayWellIds.append(oWell.m_sWellInstanceId);
}
@ -2002,26 +1993,10 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
return false;
}
// 第三阶段旧动画仍会在结果网格上切换活动压力标量,不能把快照
// 内部基础网格直接交给它。CopyStructure 只建立独立网格壳并共享
// 已验证为只读的点和单元拓扑,不复制大网格,也不会共享 CellData。
vtkSmartPointer<vtkUnstructuredGrid> pLegacyResultGrid =
vtkSmartPointer<vtkUnstructuredGrid>::New();
pLegacyResultGrid->CopyStructure(m_pPendingResultGrid);
pLegacyResultGrid->GetCellData()->Initialize();
if(pLegacyResultGrid->GetNumberOfCells() !=
m_pPendingResultGrid->GetNumberOfCells() ||
pLegacyResultGrid->GetCellData()->GetScalars() != NULL) {
return false;
}
m_pPendingResultSnapshot = oBuilder.takeCandidate();
if(m_pPendingResultSnapshot.isNull()) {
return false;
}
// 发布时 Manager 接收旧 UI 独占的网格壳;规范基础网格只留在快照中。
m_pPendingResultGrid = pLegacyResultGrid;
return true;
} catch(const std::bad_alloc&) {
qWarning() << "Cannot allocate PEBI result snapshot candidate.";
@ -2051,33 +2026,12 @@ bool nmCalculationDllPebiSolverTask::commitResult(
pDataManager->getNumericalAnalysisCase();
if(pAnalysisCase == nullptr ||
pAnalysisCase->getGridInputRevision() != m_nGridInputRevision ||
pAnalysisCase->getResultInputRevision() != m_nResultInputRevision ||
m_mapPendingTimeSteps.isEmpty() ||
m_pPendingResultGrid == nullptr ||
m_pPendingResultGrid->GetNumberOfCells() <= 0) {
pAnalysisCase->getResultInputRevision() != m_nResultInputRevision) {
discardPendingFullResult();
return false;
}
// 第二步:即使外部遗漏版本递增,同编码新井也不能接收旧任务结果。
QSet<QString> setWellIds;
for(int nIndex = 0;
nIndex < m_pPendingResultSnapshot->getWellCount();
++nIndex) {
const nmPebiResultWellSnapshot* pWell =
m_pPendingResultSnapshot->getWellAt(nIndex);
nmDataWellBase* pLiveWell = pWell == NULL ? NULL :
pDataManager->findWellByCode(pWell->m_sWellCode);
if(pWell == NULL || pLiveWell == NULL ||
pLiveWell->getWellInstanceId() != pWell->m_sWellInstanceId ||
setWellIds.contains(pWell->m_sWellInstanceId)) {
discardPendingFullResult();
return false;
}
setWellIds.insert(pWell->m_sWellInstanceId);
}
// 第三步:任务后台生成了新网格时,先在当前主线程按同一输入版本提交。
// 第二步:任务后台生成了新网格时,先在当前主线程按同一输入版本提交。
// 已有网格路径则再次确认其仍然有效。两条路径都不允许旧任务覆盖新编辑。
if(m_pInputSnapshot->m_bGridResultNeedsCommit) {
if(!nmCalculationPebiGrid::getInstance()->commitSnapshotResult(
@ -2092,13 +2046,10 @@ bool nmCalculationDllPebiSolverTask::commitResult(
return false;
}
// 第四步Manager 在一次发布点校验并替换规范快照及旧 UI 兼容引用
// 第三步Manager 在唯一发布点校验 UUID、槽位和版本后替换规范快照
QString sError;
const bool bCommitted = pDataManager->commitPebiResultSnapshot(
m_pPendingResultSnapshot,
m_mapPendingTimeSteps,
m_pPendingResultGrid,
m_mapPendingWellLocations,
&sError);
if(!bCommitted) {
qWarning() << "Cannot commit PEBI result snapshot:" << sError;
@ -2249,11 +2200,8 @@ bool nmCalculationDllPebiSolverTask::commitResult(
//
// // 将计算结果保存到对应的井数据里
// // 压力
// pWellData->setResultPressure(vvecPressure);
// // 双对数
// pWellData->setResultLogLog(vvecLogLog);
// // 半对数
// pWellData->setResultSemiLog(vvecSemiLog);
//
// // 存储当前井名称和二维位置到映射
// QPointF ptWellCoords(pWellData->getX().getValue().toDouble(), pWellData->getY().getValue().toDouble());

File diff suppressed because it is too large Load Diff

@ -63,14 +63,8 @@ void nmDataNumericalAnalysisCase::setPrimaryWellCode(const QString& 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();
}
@ -218,20 +212,6 @@ QVector<nmCalculationWellRef> nmDataNumericalAnalysisCase::getEffectiveWells() c
return vecResult;
}
// ==================== 结果查看状态 ====================
QString nmDataNumericalAnalysisCase::getCurrentResultWellCode() const
{
return m_sCurrentResultWellCode;
}
void nmDataNumericalAnalysisCase::setCurrentResultWellCode(
const QString& sWellCode)
{
// 切换结果井只是界面查看状态,不应触发网格或求解结果失效。
m_sCurrentResultWellCode = sWellCode;
}
// ==================== DLL 求解器井槽位顺序 ====================
QVector<nmSolverWellRef> nmDataNumericalAnalysisCase::getSolverWellOrder() const
@ -243,7 +223,6 @@ void nmDataNumericalAnalysisCase::clearSolverWellOrder()
{
m_vecSolverWellOrder.clear();
m_nBuiltGridRevision = 0;
m_bResultsValid = false;
}
void nmDataNumericalAnalysisCase::appendSolverWell(
@ -252,7 +231,6 @@ void nmDataNumericalAnalysisCase::appendSolverWell(
nmSolverWellRef oIndexedRef = oWellRef;
oIndexedRef.m_nSolverIndex = m_vecSolverWellOrder.size();
m_vecSolverWellOrder.append(oIndexedRef);
m_bResultsValid = false;
}
void nmDataNumericalAnalysisCase::insertSolverWell(
@ -269,7 +247,6 @@ void nmDataNumericalAnalysisCase::insertSolverWell(
++nOrderIndex) {
m_vecSolverWellOrder[nOrderIndex].m_nSolverIndex = nOrderIndex;
}
m_bResultsValid = false;
}
bool nmDataNumericalAnalysisCase::removeSolverWell(int nIndex)
@ -284,7 +261,6 @@ bool nmDataNumericalAnalysisCase::removeSolverWell(int nIndex)
++nOrderIndex) {
m_vecSolverWellOrder[nOrderIndex].m_nSolverIndex = nOrderIndex;
}
m_bResultsValid = false;
return true;
}
@ -295,65 +271,6 @@ void nmDataNumericalAnalysisCase::setSolverWellOrder(
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();
}
}
// ==================== 网格与结果版本管理 ====================
@ -368,14 +285,14 @@ void nmDataNumericalAnalysisCase::invalidateGrid()
void nmDataNumericalAnalysisCase::invalidateResults()
{
// 历史结果由 Manager 快照持有。这里仅增加当前求解输入版本,
// 快照是否仍为当前结果由双方版本比较自然得出。
++m_nResultInputRevision;
m_bResultsValid = false;
}
void nmDataNumericalAnalysisCase::markGridBuilt()
{
m_nBuiltGridRevision = m_nGridInputRevision;
m_bResultsValid = false;
}
bool nmDataNumericalAnalysisCase::markGridBuiltIfCurrent(
@ -385,7 +302,6 @@ bool nmDataNumericalAnalysisCase::markGridBuiltIfCurrent(
if(nGridInputRevision != m_nGridInputRevision ||
m_vecSolverWellOrder.isEmpty()) {
m_nBuiltGridRevision = 0;
m_bResultsValid = false;
return false;
}
@ -394,48 +310,6 @@ bool nmDataNumericalAnalysisCase::markGridBuiltIfCurrent(
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 &&
@ -443,22 +317,6 @@ bool nmDataNumericalAnalysisCase::isGridValid() const
!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;
@ -490,7 +348,6 @@ bool nmDataNumericalAnalysisCase::restoreInputRevisions(
m_nGridInputRevision = nGridInputRevision;
m_nBuiltGridRevision = nBuiltGridRevision;
m_nResultInputRevision = nResultInputRevision;
m_bResultsValid = false;
return true;
}
@ -502,15 +359,10 @@ void nmDataNumericalAnalysisCase::clear()
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()
@ -534,41 +386,3 @@ void nmDataNumericalAnalysisCase::normalizeIncludedWells()
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);
}
}
}

@ -162,11 +162,6 @@ nmDataWellBase& nmDataWellBase::operator=(const nmDataWellBase& other)
m_vvecHsyLogLog = other.m_vvecHsyLogLog;
m_vvecHsySemiLog = other.m_vvecHsySemiLog;
// 复制计算结果数据
m_vvecRstPressure = other.m_vvecRstPressure;
m_vvecRstLogLog = other.m_vvecRstLogLog;
m_vvecRstSemiLog = other.m_vvecRstSemiLog;
// 默认与当前井身长度一致
m_dLastWellLength = m_wellLength.getValue().toDouble();
}
@ -812,12 +807,7 @@ QVector<QVector<double>> nmDataWellBase::getResultPressure()
return pWell->m_oCurves.m_vecResultPressure;
}
}
return m_vvecRstPressure;
}
void nmDataWellBase::setResultPressure(QVector<QVector<double>> pressureData)
{
m_vvecRstPressure = pressureData;
return QVector<QVector<double> >();
}
QVector<QVector<double>> nmDataWellBase::getResultLogLog()
@ -831,12 +821,7 @@ QVector<QVector<double>> nmDataWellBase::getResultLogLog()
return pWell->m_oCurves.m_vecResultLogLog;
}
}
return m_vvecRstLogLog;
}
void nmDataWellBase::setResultLogLog(QVector<QVector<double>> loglogData)
{
m_vvecRstLogLog = loglogData;
return QVector<QVector<double> >();
}
QVector<QVector<double>> nmDataWellBase::getResultSemiLog()
@ -850,12 +835,7 @@ QVector<QVector<double>> nmDataWellBase::getResultSemiLog()
return pWell->m_oCurves.m_vecResultSemiLog;
}
}
return m_vvecRstSemiLog;
}
void nmDataWellBase::setResultSemiLog(QVector<QVector<double>> semiLogData)
{
m_vvecRstSemiLog = semiLogData;
return QVector<QVector<double> >();
}
void nmDataWellBase::bindPebiResultSnapshot(

@ -379,9 +379,6 @@ bool nmWellEditorSession::copySharedWellData(
pTarget->setHistoryPressure(pSource->getHistoryPressure());
pTarget->setHistoryLogLog(pSource->getHistoryLogLog());
pTarget->setHistorySemiLog(pSource->getHistorySemiLog());
pTarget->setResultPressure(pSource->getResultPressure());
pTarget->setResultLogLog(pSource->getResultLogLog());
pTarget->setResultSemiLog(pSource->getResultSemiLog());
// 第三步:保留图元显示属性和跨井型开关。
pTarget->setPlotVisible(pSource->getPlotVisible());

@ -688,9 +688,6 @@ void nmWxEditWellPlot::onWellTypeChanged(int index)
pNewWellData->setIndexF(pOldWellData->getIndexF());
// 结果数据
pNewWellData->setResultPressure(pOldWellData->getResultPressure());
pNewWellData->setResultLogLog(pOldWellData->getResultLogLog());
pNewWellData->setResultSemiLog(pOldWellData->getResultSemiLog());
}
// 对于裂缝类型的井,需要更新裂缝位置

Loading…
Cancel
Save