feat(nmData): 冻结 PEBI 结果快照属性

- 冻结求解时的 k/phi/h 并按 OriginalCellId 映射结果属性
- 升级 V5 快照持久化及属性目录校验
- 支持通用属性帧切换和双缓冲渲染
- 加固基础网格映射与井曲线完整性验证
feature/nmNum-postprocessing-view-20260910
lh 3 weeks ago
parent 36f17ae321
commit e98267c9c3

@ -20,6 +20,7 @@ class nmPebiResultSnapshot;
class vtkDoubleArray;
struct nmPebiSolverInputSnapshot;
struct nmPebiManualCaptureState;
struct nmPebiStaticPropertyValues;
// 主窗口现在直接创建DLL求解线程任务,因此类需要导出,供nmSubWnd模块跨DLL使用。
class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
@ -101,9 +102,10 @@ class NMCALCULATION_EXPORT nmCalculationDllPebiSolverTask : public QThread {
/** @brief 按求解器井顺序组装压力、曲线和场结果快照,不修改 DataManager。 */
bool buildPebiModeResult(HX_NWTM_MODEL_OUTPUT& p1,
int nModelType,
const HX_NWTM_GRID_OUTPUT1& oGridOutput);
nmPebiStaticPropertyValues& oStaticProperties);
/** @brief 用任务局部输出构造并封闭完整结果候选,不访问实时数据对象。 */
bool buildPebiResultSnapshotCandidate();
bool buildPebiResultSnapshotCandidate(
nmPebiStaticPropertyValues& oStaticProperties);
/** @brief 输出精简版 DLL 输入日志,用于定位求解异常。 */
void logHX_NWTM_MODEL_INPUT_Simplified(const HX_NWTM_MODEL_INPUT& p0);

@ -3,6 +3,7 @@
#include "nmData_global.h"
#include "nmDataNumericalAnalysisCase.h"
#include "nmPebiStaticPropertyValues.h"
#include <QPointF>
#include <QSharedPointer>
@ -248,7 +249,7 @@ struct NM_DATA_EXPORT nmPebiResultSolverSettings
/**
* @brief 最后一次成功 PEBI 求解的完整只读结果。
*
* 普通调用方只能读取值类型,或要求快照把结构和压力绑定到调用方拥有的
* 普通调用方只能读取值类型,或要求快照把结构和属性绑定到调用方拥有的
* VTK 对象,不能取得快照内部 VTK 对象的可写指针。
*/
class NM_DATA_EXPORT nmPebiResultSnapshot
@ -284,6 +285,22 @@ public:
/** @brief 返回结果基础网格单元数。 */
vtkIdType getResultCellCount() const;
/** @brief 按属性目录顺序返回当前快照包含的正式属性 ID。 */
QStringList getAvailablePropertyIds() const;
/** @brief 判断快照是否包含指定正式属性。 */
bool hasProperty(const QString& sPropertyId) const;
/** @brief 返回属性帧数;未知属性返回 0,静态属性固定返回 1。 */
int getPropertyFrameCount(const QString& sPropertyId) const;
/**
* @brief 返回动态属性指定帧的时间。
* @note 静态属性、未知属性或越界帧返回 false,且不修改 dTime。
*/
bool getPropertyTimeAt(const QString& sPropertyId,
int nFrameIndex,
double& dTime) const;
/** @brief 返回指定属性的冻结显示范围。 */
bool getPropertyRange(const QString& sPropertyId,
double aRange[2]) const;
/** @brief 返回压力时间步数量。 */
int getPressureFrameCount() const;
/** @brief 返回指定压力帧的时间,越界时返回 0。 */
@ -318,22 +335,24 @@ private:
nmPebiResultSnapshot& operator=(const nmPebiResultSnapshot&);
/**
* @brief 只复制拓扑到适配器拥有的渲染网格,不复制快照的 CellData。
* @brief 复制拓扑和内部单元映射,不复制任何正式结果属性数组。
* @note VTK 7.1 的 CopyStructure 会共享点和单元拓扑,适配器不得修改它们。
*/
bool copyResultGridStructure(vtkUnstructuredGrid* pTargetGrid) const;
/**
* @brief 将快照压力帧临时共享绑定到适配器的独立渲染网格。
* @brief 将指定正式属性帧临时共享绑定到适配器的独立渲染网格。
* @note 目标网格会临时持有同一 VTK 数组引用,下游流程不得改写数组。
*/
bool bindPressureFrame(int nIndex,
bool bindPropertyFrame(const QString& sPropertyId,
int nFrameIndex,
vtkUnstructuredGrid* pTargetGrid) const;
/** @brief 将一帧压力复制到适配器复用的缓冲数组。 */
bool copyPressureFrame(int nIndex,
vtkDoubleArray* pTargetPressure) const;
/** @brief 将一帧正式属性复制到适配器提供的备用缓冲数组。 */
bool copyPropertyFrame(const QString& sPropertyId,
int nFrameIndex,
vtkDoubleArray* pTargetScalars) const;
private:
// 网格与压力分开保存,避免基础网格通过 CellData 暴露可变压力数组。
// 网格与结果属性分开保存,避免基础网格通过 CellData 暴露可变数组。
/** @brief 快照 UUID。 */
QString m_sSnapshotId;
/** @brief 生成结果时使用的网格输入版本。 */
@ -342,8 +361,18 @@ private:
quint64 m_nResultInputRevision;
/** @brief 是否已由 Builder 完整校验并封闭。 */
bool m_bComplete;
/** @brief 不含压力 p 数组和活动标量的结果基础网格。 */
/** @brief 只含几何和内部映射、不含正式结果标量的基础网格。 */
vtkSmartPointer<vtkUnstructuredGrid> m_pResultGrid;
/** @brief 按原始 PEBI 单元顺序保存的三项规范静态属性。 */
nmPebiStaticPropertyValues m_oStaticProperties;
/** @brief 按可见 VTK Cell 顺序缓存的渗透率。 */
vtkSmartPointer<vtkDoubleArray> m_pPermeability;
/** @brief 按可见 VTK Cell 顺序缓存的孔隙度。 */
vtkSmartPointer<vtkDoubleArray> m_pPorosity;
/** @brief 按可见 VTK Cell 顺序缓存的厚度。 */
vtkSmartPointer<vtkDoubleArray> m_pThickness;
/** @brief 三项静态属性的可见单元范围,顺序为 k/phi/h。 */
double m_aStaticRanges[3][2];
/** @brief 按时间严格递增保存的全部压力帧。 */
QVector<PressureFrame> m_vecPressureFrames;
/** @brief 全部压力帧中的全局最小值。 */

@ -29,6 +29,11 @@ public:
/** @brief 成功后接管网格且清空调用方智能指针,不执行 DeepCopy。 */
bool takeResultGrid(
vtkSmartPointer<vtkUnstructuredGrid>& pResultGrid);
/**
* @brief 接管原始顺序的三项静态属性并建立只读 VTK 可见单元缓存。
* @note 失败时调用方三个数组保持不变;成功后通过 qSwap 清空调用方。
*/
bool takeStaticProperties(nmPebiStaticPropertyValues& oValues);
/** @brief 成功后接管压力数组且清空调用方智能指针,不复制整帧数据。 */
bool addPressureFrame(
double dTime,
@ -66,7 +71,7 @@ private:
bool canWrite();
/** @brief 将 Builder 永久置为失败状态并输出错误信息。 */
bool fail(const QString& sError, QString* pError = NULL);
/** @brief 校验候选全部结构,并移除基础网格的压力标量。 */
/** @brief 校验候选全部结构,不修改已经接管的候选载荷。 */
bool validate(QString* pError);
private:
@ -79,12 +84,18 @@ private:
bool m_bFailed;
/** @brief 调用方是否显式设置过标量范围。 */
bool m_bHasScalarRange;
/** @brief 是否已经接管并建立完整静态属性缓存。 */
bool m_bHasStaticProperties;
/** @brief 调用方是否显式设置过储层参数。 */
bool m_bHasReservoirParameters;
/** @brief 调用方是否显式设置过 PVT 参数。 */
bool m_bHasPvtParameters;
/** @brief 调用方是否显式设置过求解设置。 */
bool m_bHasSolverSettings;
/** @brief 基础网格中 VTK Cell 到原始 PEBI 单元的冻结映射。 */
QVector<vtkIdType> m_vecVtkToOriginal;
/** @brief 本次候选的原始 PEBI 单元总数。 */
vtkIdType m_nOriginalCellCount;
/** @brief 最近一次失败的英文内部诊断文本。 */
QString m_sLastError;
};

@ -23,11 +23,18 @@ class vtkUnstructuredGrid;
class NM_DATA_EXPORT nmPebiResultSnapshotRenderAdapter
{
public:
/** @brief 压力帧与渲染网格之间的绑定方式。 */
/** @brief 正式属性数组与渲染网格之间的绑定方式。 */
enum PropertyBindingMode
{
SharedPropertyArray = 0, ///< 临时共享快照只读数组,不产生整帧复制。
CopiedPropertyArray = 1 ///< 复制到双缓冲,隔离不可信下游流程。
};
/** @brief 旧压力调用方使用的兼容枚举。 */
enum PressureBindingMode
{
SharedPressureFrame = 0, ///< 临时共享快照压力数组,不产生整帧复制。
CopiedPressureFrame = 1 ///< 复制到可复用缓冲,隔离不可信下游流程。
SharedPressureFrame = SharedPropertyArray,
CopiedPressureFrame = CopiedPropertyArray
};
/** @brief 创建尚未绑定快照的渲染适配器。 */
@ -36,26 +43,36 @@ public:
~nmPebiResultSnapshotRenderAdapter();
/**
* @brief 绑定一套完整快照并建立不含 CellData 的渲染网格结构。
* @brief 绑定快照并建立仅含内部单元映射的渲染网格结构。
* @param pSnapshot 调用期间保持只读的结果快照。
* @param eBindingMode 共享压力或单帧复制模式。
* @param eBindingMode 共享属性或单帧复制模式。
*/
bool initialize(
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot,
PropertyBindingMode eBindingMode);
/** @brief 使用旧压力枚举初始化,行为与通用绑定模式一致。 */
bool initialize(
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot,
PressureBindingMode eBindingMode);
/** @brief 解除输出、压力数组和快照引用,使适配器恢复为空状态。 */
/** @brief 解除输出、属性数组和快照引用,使适配器恢复为空状态。 */
void clear();
/** @brief 事务式发布指定属性帧,失败时保持原画面和状态不变。 */
bool setPropertyFrame(const QString& sPropertyId, int nFrameIndex);
/** @brief 将指定压力帧发布到当前 VTK 输出端口。 */
bool setPressureFrame(int nFrameIndex);
/** @brief 返回供 Mapper 或过滤器建立连接的 VTK 输出端口。 */
vtkAlgorithmOutput* getOutputPort() const;
/** @brief 返回当前已经绑定的压力帧下标,尚未切帧时为 -1。 */
int getCurrentFrameIndex() const;
/** @brief 返回当前已经绑定的属性 ID,尚未绑定时为空。 */
QString getCurrentPropertyId() const;
/** @brief 返回适配器渲染网格的单元数。 */
vtkIdType getCellCount() const;
/** @brief 返回当前是否使用零整帧复制的共享压力模式。 */
bool isUsingSharedPressure() const;
/** @brief 返回当前是否使用零整帧复制的共享属性模式。 */
bool isUsingSharedProperties() const;
private:
/** @brief 禁止复制包含 VTK 管道和快照引用的适配器。 */
@ -70,13 +87,17 @@ private:
QSharedPointer<const nmPebiResultSnapshot> m_pSnapshot;
/** @brief 适配器独占的渲染网格,结构可与快照基础网格共享。 */
vtkSmartPointer<vtkUnstructuredGrid> m_pRenderGrid;
/** @brief 复制模式复用的单帧压力缓冲。 */
vtkSmartPointer<vtkDoubleArray> m_pPressureBuffer;
/** @brief 复制模式当前绑定到 CellData 的活动缓冲。 */
vtkSmartPointer<vtkDoubleArray> m_pActiveBuffer;
/** @brief 复制模式在切换成功前独立填充和校验的备用缓冲。 */
vtkSmartPointer<vtkDoubleArray> m_pStandbyBuffer;
/** @brief 将独立渲染网格接入 VTK 管道的生产者。 */
vtkSmartPointer<vtkTrivialProducer> m_pProducer;
/** @brief 当前压力帧绑定方式。 */
PressureBindingMode m_eBindingMode;
/** @brief 当前已经绑定的压力帧下标。 */
/** @brief 当前属性数组绑定方式。 */
PropertyBindingMode m_eBindingMode;
/** @brief 当前已经绑定的属性 ID。 */
QString m_sCurrentPropertyId;
/** @brief 当前已经绑定的属性帧下标。 */
int m_nCurrentFrameIndex;
};

@ -11,7 +11,7 @@
class nmPebiResultSnapshot;
/**
* @brief 负责当前快照三文件载荷及 Snapshot.json 的流式保存和加载。
* @brief 负责当前快照四文件载荷及 Snapshot.json 的流式保存和加载。
*
* 序列化器是快照的受限友元,只在保存时读取内部 VTK 对象;加载仍通过
* nmPebiResultSnapshotBuilder 构造和封闭候选。
@ -26,13 +26,13 @@ public:
nmNumericalFileReference& oSnapshotJsonReference,
QString* pError = NULL);
/** @brief 校验 Snapshot.json 及其三条引用,不创建快照对象。 */
/** @brief 校验 Snapshot.json 及其四条引用,不发布快照对象。 */
static bool validate(
const QString& sWindowDirectory,
const nmNumericalFileReference& oSnapshotJsonReference,
QString* pError = NULL);
/** @brief 返回 Snapshot.json 中三个规范化载荷路径,供窗口级全局查重。 */
/** @brief 返回 Snapshot.json 中四个规范化载荷路径,供窗口级全局查重。 */
static bool getPayloadRelativePaths(
const QString& sWindowDirectory,
const nmNumericalFileReference& oSnapshotJsonReference,
@ -47,6 +47,11 @@ public:
QString* pError = NULL);
private:
/** @brief 写出原始 PEBI 单元顺序的三项规范静态属性。 */
static bool writeStaticProperties(
const nmPebiResultSnapshot& oSnapshot,
const QString& sFilePath,
QString* pError);
/** @brief 从快照内部数组流式写出全部压力帧。 */
static bool writePressureFrames(const nmPebiResultSnapshot& oSnapshot,
const QString& sFilePath,

@ -15,6 +15,7 @@
#include "nmDataFault.h"
#include "nmDataTimeStepSetting.h"
#include "nmPebiResultSnapshotBuilder.h"
#include "nmPebiCellMapping.h"
#include "nmPebiStaticPropertyCalculator.h"
#include "nmCalculationPebiGrid.h"
@ -1460,7 +1461,7 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
const bool bSucceeded = buildPebiModeResult(
oModelOutput,
oModelInput.T,
oGridResult.m_oGridOutput1);
oStaticProperties);
if(!bSucceeded && !isCancelRequested() && m_sFailureReason.isEmpty()) {
fail(tr("The solver result data is invalid."));
}
@ -1491,7 +1492,7 @@ std::vector<double> HX_logderivative(const std::vector<double>& x, const std::ve
bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
HX_NWTM_MODEL_OUTPUT& p1,
int modelType,
const HX_NWTM_GRID_OUTPUT1& oGridOutput)
nmPebiStaticPropertyValues& oStaticProperties)
{
if(m_pInputSnapshot == nullptr) {
return fail(tr("The solver result data is invalid."));
@ -1821,47 +1822,51 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
: fail(tr("The calculated pressure curves are invalid."));
}
// 第四步:完整求解还必须具有与时间轴、网格绘图单元严格对应的场压力。
// 第四步:完整求解只接受携带有效 OriginalCellId 的冻结基础网格。
// 场压力按同一映射过滤,不能再根据 isplot 顺序推测 VTK Cell 身份。
if(p1.p.size() != p1.t.size() ||
oGridOutput.PEBI_cell.isplot.empty()) {
m_pInputSnapshot->m_pBaseGrid == nullptr ||
m_pInputSnapshot->m_pBaseGrid->GetNumberOfCells() <= 0) {
return fail(tr("The solver returned incomplete field pressure data."));
}
// 计算有效单元数量
size_t actualPlotCellsCount = 0;
for(size_t i = 0; i < oGridOutput.PEBI_cell.isplot.size(); ++i) {
if((i % 256) == 0 && isCancelRequested()) {
return false;
}
if(oGridOutput.PEBI_cell.isplot[i] == 1) {
actualPlotCellsCount++;
}
}
if(actualPlotCellsCount == 0) {
QVector<vtkIdType> vecVtkToOriginal;
QVector<vtkIdType> vecOriginalToVtk;
QString sMappingError;
if(nmPebiCellMapping::read(m_pInputSnapshot->m_pBaseGrid,
&vecVtkToOriginal, &vecOriginalToVtk, &sMappingError) !=
nmPebiCellMapping::MappingStatus_Valid ||
vecVtkToOriginal.isEmpty() || vecOriginalToVtk.isEmpty() ||
vecVtkToOriginal.size() !=
m_pInputSnapshot->m_pBaseGrid->GetNumberOfCells()) {
qWarning() << "Invalid PEBI result cell mapping:" << sMappingError;
return fail(tr("The generated grid is invalid."));
}
const int nOriginalCellCount = vecOriginalToVtk.size();
const int nVisibleCellCount = vecVtkToOriginal.size();
for(size_t nTimeIndex = 0; nTimeIndex < p1.p.size(); ++nTimeIndex) {
if(isCancelRequested()) {
return false;
}
if(p1.p[nTimeIndex].size() < oGridOutput.PEBI_cell.isplot.size()) {
if(p1.p[nTimeIndex].size() !=
static_cast<size_t>(nOriginalCellCount)) {
qWarning() << "Incomplete field pressure returned for time index:"
<< static_cast<int>(nTimeIndex);
return fail(tr("The solver returned incomplete field pressure data."));
}
for(size_t nCellIndex = 0;
nCellIndex < oGridOutput.PEBI_cell.isplot.size();
++nCellIndex) {
if((nCellIndex % 256) == 0 && isCancelRequested()) {
for(int nVtkCellId = 0;
nVtkCellId < nVisibleCellCount; ++nVtkCellId) {
if((nVtkCellId % 256) == 0 && isCancelRequested()) {
return false;
}
if(oGridOutput.PEBI_cell.isplot[nCellIndex] == 1 &&
!isFiniteSolverNumber(p1.p[nTimeIndex][nCellIndex])) {
const vtkIdType nOriginalCellId =
vecVtkToOriginal[nVtkCellId];
if(nOriginalCellId < 0 ||
nOriginalCellId >= nOriginalCellCount ||
!isFiniteSolverNumber(p1.p[nTimeIndex][
static_cast<size_t>(nOriginalCellId)])) {
qWarning() << "PEBI returned a non-finite field pressure at time/cell:"
<< static_cast<int>(nTimeIndex)
<< static_cast<int>(nCellIndex);
<< static_cast<int>(nOriginalCellId);
return fail(tr("The solver returned invalid field pressure data."));
}
}
@ -1888,27 +1893,24 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
vtkSmartPointer<vtkDoubleArray> pressureData = vtkSmartPointer<vtkDoubleArray>::New();
pressureData->SetName("p"); // 设置标量数据的名称
// 直接使用外面计算好的 actualPlotCellsCount
pressureData->SetNumberOfValues(actualPlotCellsCount);
pressureData->SetNumberOfComponents(1);
pressureData->SetNumberOfValues(nVisibleCellCount);
size_t destIdx = 0; // 目标数组的索引
double dCurrentTimeMin = DBL_MAX; // 当前时间步的最小值
double dCurrentTimeMax = -DBL_MAX; // 当前时间步的最大值
// 遍历原始数据,并根据 isplot 填充到 pressureData
for(size_t i = 0; i < oGridOutput.PEBI_cell.isplot.size(); ++i) {
if((i % 256) == 0 && isCancelRequested()) {
for(int nVtkCellId = 0;
nVtkCellId < nVisibleCellCount; ++nVtkCellId) {
if((nVtkCellId % 256) == 0 && isCancelRequested()) {
return false;
}
if(oGridOutput.PEBI_cell.isplot[i] == 1) { // 只有当 isplot 为1时才考虑这个单元格
pressureData->SetValue(destIdx, p1.p[timeIdx][i]);
// 更新当前时间步的范围
dCurrentTimeMin = qMin(dCurrentTimeMin, p1.p[timeIdx][i]);
dCurrentTimeMax = qMax(dCurrentTimeMax, p1.p[timeIdx][i]);
destIdx++;
}
const vtkIdType nOriginalCellId =
vecVtkToOriginal[nVtkCellId];
const double dPressure = p1.p[timeIdx][
static_cast<size_t>(nOriginalCellId)];
pressureData->SetValue(nVtkCellId, dPressure);
dCurrentTimeMin = qMin(dCurrentTimeMin, dPressure);
dCurrentTimeMax = qMax(dCurrentTimeMax, dPressure);
}
// 更新全局范围
@ -1951,7 +1953,8 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
m_dPendingScalarMin = dMinP;
m_dPendingScalarMax = dMaxP;
m_bPendingFullResultReady = buildPebiResultSnapshotCandidate();
m_bPendingFullResultReady =
buildPebiResultSnapshotCandidate(oStaticProperties);
if(!m_bPendingFullResultReady) {
// 候选结构或内存校验失败时立即释放大结果,旧快照保持原状。
fail(tr("Failed to build the result snapshot."));
@ -1978,7 +1981,8 @@ void nmCalculationDllPebiSolverTask::discardPendingFullResult()
}
}
bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate(
nmPebiStaticPropertyValues& oStaticProperties)
{
if(m_pInputSnapshot == NULL ||
m_pInputSnapshot->m_bAutoFitTargetOnly ||
@ -2000,6 +2004,9 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate()
if(!oBuilder.takeResultGrid(m_pInputSnapshot->m_pBaseGrid)) {
return false;
}
if(!oBuilder.takeStaticProperties(oStaticProperties)) {
return false;
}
QMap<double, vtkSmartPointer<vtkDoubleArray> >::const_iterator
oTimeIt = m_mapPendingTimeSteps.constBegin();

@ -1,11 +1,49 @@
#include "nmPebiResultSnapshot.h"
#include "nmPebiCellMapping.h"
#include "nmPebiResultPropertyCatalog.h"
#include <vtkAbstractArray.h>
#include <vtkCellData.h>
#include <vtkDataArray.h>
#include <vtkDoubleArray.h>
#include <vtkFieldData.h>
#include <vtkUnstructuredGrid.h>
#include <QUuid>
namespace {
bool deepCopyNamedArray(vtkFieldData* pSource,
vtkFieldData* pTarget,
const char* pArrayName)
{
if(pSource == NULL || pTarget == NULL || pArrayName == NULL ||
pTarget->GetAbstractArray(pArrayName) != NULL)
{
return false;
}
vtkAbstractArray* pSourceArray =
pSource->GetAbstractArray(pArrayName);
if(pSourceArray == NULL)
{
return false;
}
vtkSmartPointer<vtkAbstractArray> pTargetArray;
pTargetArray.TakeReference(pSourceArray->NewInstance());
if(pTargetArray == NULL)
{
return false;
}
pTargetArray->DeepCopy(pSourceArray);
return pTarget->AddArray(pTargetArray) >= 0 &&
pTarget->GetAbstractArray(pArrayName) == pTargetArray.GetPointer();
}
}
// VTK 结构和压力操作仅供渲染适配器调用,普通结果读取接口不暴露可写对象。
nmPebiResultSolverSlot::nmPebiResultSolverSlot()
: m_nSolverIndex(-1),
@ -86,6 +124,11 @@ nmPebiResultSnapshot::nmPebiResultSnapshot()
m_dScalarMin(0.0),
m_dScalarMax(0.0)
{
for(int nPropertyIndex = 0; nPropertyIndex < 3; ++nPropertyIndex)
{
m_aStaticRanges[nPropertyIndex][0] = 0.0;
m_aStaticRanges[nPropertyIndex][1] = 0.0;
}
}
nmPebiResultSnapshot::~nmPebiResultSnapshot()
@ -171,6 +214,107 @@ vtkIdType nmPebiResultSnapshot::getResultCellCount() const
return m_pResultGrid != NULL ? m_pResultGrid->GetNumberOfCells() : 0;
}
QStringList nmPebiResultSnapshot::getAvailablePropertyIds() const
{
QStringList listPropertyIds;
if(!m_bComplete)
{
return listPropertyIds;
}
const QList<nmPebiResultPropertyDescriptor> listDescriptors =
nmPebiResultPropertyCatalog::descriptors();
for(int nIndex = 0; nIndex < listDescriptors.size(); ++nIndex)
{
if(hasProperty(listDescriptors[nIndex].m_sPropertyId))
{
listPropertyIds.append(listDescriptors[nIndex].m_sPropertyId);
}
}
return listPropertyIds;
}
bool nmPebiResultSnapshot::hasProperty(const QString& sPropertyId) const
{
if(!m_bComplete)
{
return false;
}
if(sPropertyId == QString::fromLatin1("permeability"))
{
return m_pPermeability != NULL;
}
if(sPropertyId == QString::fromLatin1("porosity"))
{
return m_pPorosity != NULL;
}
if(sPropertyId == QString::fromLatin1("thickness"))
{
return m_pThickness != NULL;
}
if(sPropertyId == QString::fromLatin1("pressure"))
{
return !m_vecPressureFrames.isEmpty();
}
return false;
}
int nmPebiResultSnapshot::getPropertyFrameCount(
const QString& sPropertyId) const
{
if(!hasProperty(sPropertyId))
{
return 0;
}
return sPropertyId == QString::fromLatin1("pressure")
? m_vecPressureFrames.size() : 1;
}
bool nmPebiResultSnapshot::getPropertyTimeAt(
const QString& sPropertyId,
int nFrameIndex,
double& dTime) const
{
if(sPropertyId != QString::fromLatin1("pressure") ||
!hasProperty(sPropertyId) || nFrameIndex < 0 ||
nFrameIndex >= m_vecPressureFrames.size())
{
return false;
}
dTime = m_vecPressureFrames[nFrameIndex].m_dTime;
return true;
}
bool nmPebiResultSnapshot::getPropertyRange(
const QString& sPropertyId,
double aRange[2]) const
{
if(aRange == NULL || !hasProperty(sPropertyId))
{
return false;
}
if(sPropertyId == QString::fromLatin1("permeability"))
{
aRange[0] = m_aStaticRanges[0][0];
aRange[1] = m_aStaticRanges[0][1];
return true;
}
if(sPropertyId == QString::fromLatin1("porosity"))
{
aRange[0] = m_aStaticRanges[1][0];
aRange[1] = m_aStaticRanges[1][1];
return true;
}
if(sPropertyId == QString::fromLatin1("thickness"))
{
aRange[0] = m_aStaticRanges[2][0];
aRange[1] = m_aStaticRanges[2][1];
return true;
}
aRange[0] = m_dScalarMin;
aRange[1] = m_dScalarMax;
return true;
}
int nmPebiResultSnapshot::getPressureFrameCount() const
{
return m_vecPressureFrames.size();
@ -187,13 +331,7 @@ double nmPebiResultSnapshot::getPressureTimeAt(int nIndex) const
bool nmPebiResultSnapshot::getScalarRange(double aRange[2]) const
{
if(aRange == NULL || !m_bComplete)
{
return false;
}
aRange[0] = m_dScalarMin;
aRange[1] = m_dScalarMax;
return true;
return getPropertyRange(QString::fromLatin1("pressure"), aRange);
}
const nmPebiResultReservoirParameters&
@ -222,61 +360,117 @@ bool nmPebiResultSnapshot::copyResultGridStructure(
}
// CopyStructure 在当前 VTK 7.1 中共享点和单元拓扑,不复制大网格。
// 随后清空目标 CellData,确保压力只能通过受控接口按帧挂载。
// 结果属性仍由受控接口按帧挂载;内部映射单独深拷贝,避免渲染端
// 修改数组时污染不可变快照。
pTargetGrid->Initialize();
pTargetGrid->CopyStructure(m_pResultGrid);
pTargetGrid->GetCellData()->Initialize();
return pTargetGrid->GetNumberOfCells() ==
m_pResultGrid->GetNumberOfCells();
pTargetGrid->GetFieldData()->Initialize();
if(pTargetGrid->GetNumberOfCells() !=
m_pResultGrid->GetNumberOfCells() ||
!deepCopyNamedArray(m_pResultGrid->GetCellData(),
pTargetGrid->GetCellData(),
nmPebiCellMapping::originalCellIdArrayName()) ||
!deepCopyNamedArray(m_pResultGrid->GetFieldData(),
pTargetGrid->GetFieldData(),
nmPebiCellMapping::originalCellCountArrayName()) ||
pTargetGrid->GetCellData()->GetScalars() != NULL)
{
pTargetGrid->Initialize();
return false;
}
return true;
}
bool nmPebiResultSnapshot::bindPressureFrame(
int nIndex,
bool nmPebiResultSnapshot::bindPropertyFrame(
const QString& sPropertyId,
int nFrameIndex,
vtkUnstructuredGrid* pTargetGrid) const
{
if(!m_bComplete || pTargetGrid == NULL || nIndex < 0 ||
nIndex >= m_vecPressureFrames.size() ||
if(!m_bComplete || pTargetGrid == NULL || nFrameIndex < 0 ||
nFrameIndex >= getPropertyFrameCount(sPropertyId) ||
pTargetGrid->GetNumberOfCells() != getResultCellCount())
{
return false;
}
vtkDoubleArray* pPressure =
m_vecPressureFrames[nIndex].m_pPressure.GetPointer();
if(pPressure == NULL ||
pPressure->GetNumberOfTuples() != getResultCellCount())
vtkDoubleArray* pScalars = NULL;
if(sPropertyId == QString::fromLatin1("permeability"))
{
pScalars = m_pPermeability.GetPointer();
}
else if(sPropertyId == QString::fromLatin1("porosity"))
{
pScalars = m_pPorosity.GetPointer();
}
else if(sPropertyId == QString::fromLatin1("thickness"))
{
pScalars = m_pThickness.GetPointer();
}
else if(sPropertyId == QString::fromLatin1("pressure"))
{
pScalars = m_vecPressureFrames[nFrameIndex].m_pPressure.GetPointer();
}
const QString sExpectedArrayName =
sPropertyId == QString::fromLatin1("pressure")
? QString::fromLatin1("p") : sPropertyId;
if(pScalars == NULL || pScalars->GetName() == NULL ||
QString::fromLatin1(pScalars->GetName()) != sExpectedArrayName ||
pScalars->GetNumberOfComponents() != 1 ||
pScalars->GetNumberOfTuples() != getResultCellCount())
{
return false;
}
// SetScalars 会增加 VTK 引用计数,压力的规范所有权仍属于快照。
// 渲染网格销毁或切换快照时应及时解除这个临时引用。
pTargetGrid->GetCellData()->SetScalars(pPressure);
// 共享模式只在完整校验后替换活动数组;事后校验失败时恢复旧引用。
vtkDataArray* pPreviousScalars =
pTargetGrid->GetCellData()->GetScalars();
pTargetGrid->GetCellData()->SetScalars(pScalars);
if(pTargetGrid->GetCellData()->GetScalars() != pScalars)
{
pTargetGrid->GetCellData()->SetScalars(pPreviousScalars);
return false;
}
pTargetGrid->Modified();
return pTargetGrid->GetCellData()->GetScalars() == pPressure;
return true;
}
bool nmPebiResultSnapshot::copyPressureFrame(
int nIndex,
vtkDoubleArray* pTargetPressure) const
bool nmPebiResultSnapshot::copyPropertyFrame(
const QString& sPropertyId,
int nFrameIndex,
vtkDoubleArray* pTargetScalars) const
{
if(!m_bComplete || pTargetPressure == NULL || nIndex < 0 ||
nIndex >= m_vecPressureFrames.size())
if(!m_bComplete || pTargetScalars == NULL || nFrameIndex < 0 ||
nFrameIndex >= getPropertyFrameCount(sPropertyId))
{
return false;
}
vtkDoubleArray* pSourcePressure =
m_vecPressureFrames[nIndex].m_pPressure.GetPointer();
if(pSourcePressure == NULL)
vtkDoubleArray* pSourceScalars = NULL;
if(sPropertyId == QString::fromLatin1("permeability"))
{
pSourceScalars = m_pPermeability.GetPointer();
}
else if(sPropertyId == QString::fromLatin1("porosity"))
{
pSourceScalars = m_pPorosity.GetPointer();
}
else if(sPropertyId == QString::fromLatin1("thickness"))
{
pSourceScalars = m_pThickness.GetPointer();
}
else if(sPropertyId == QString::fromLatin1("pressure"))
{
pSourceScalars =
m_vecPressureFrames[nFrameIndex].m_pPressure.GetPointer();
}
if(pSourceScalars == NULL)
{
return false;
}
// 回退路径只复制当前帧,调用方应在动画期间复用同一个缓冲数组。
pTargetPressure->DeepCopy(pSourcePressure);
pTargetPressure->SetName("p");
return pTargetPressure->GetNumberOfComponents() == 1 &&
pTargetPressure->GetNumberOfTuples() == getResultCellCount();
pTargetScalars->DeepCopy(pSourceScalars);
return pTargetScalars->GetNumberOfComponents() == 1 &&
pTargetScalars->GetNumberOfTuples() == getResultCellCount();
}

@ -1,5 +1,7 @@
#include "nmPebiResultSnapshotBuilder.h"
#include "nmPebiCellMapping.h"
#include <QSet>
#include <QUuid>
#include <QtGlobal>
@ -30,6 +32,22 @@ bool isFiniteValue(double dValue)
#endif
}
// 这四个名称由结果快照独占。基础网格若已携带同名数组,无法判断其
// 来源和语义,必须在接管所有权前拒绝,而不是在 finalize() 中静默删除。
bool hasReservedResultPropertyArray(vtkUnstructuredGrid* pGrid)
{
if(pGrid == NULL || pGrid->GetCellData() == NULL)
{
return false;
}
vtkCellData* pCellData = pGrid->GetCellData();
return pCellData->GetAbstractArray("p") != NULL ||
pCellData->GetAbstractArray("permeability") != NULL ||
pCellData->GetAbstractArray("porosity") != NULL ||
pCellData->GetAbstractArray("thickness") != NULL;
}
bool isValidInstanceId(const QString& sInstanceId)
{
return !sInstanceId.isEmpty() && !QUuid(sInstanceId).isNull();
@ -377,15 +395,111 @@ bool areSolverSettingsValid(const nmPebiResultSolverSettings& oSettings)
oSettings.m_dMaxDeltaT >= oSettings.m_dMinDeltaT);
}
bool buildStaticPropertyArray(
const QVector<double>& vecOriginalValues,
const QVector<vtkIdType>& vecVtkToOriginal,
const char* pArrayName,
vtkSmartPointer<vtkDoubleArray>& pArray,
double aRange[2])
{
if(pArrayName == NULL || vecOriginalValues.isEmpty() ||
vecVtkToOriginal.isEmpty() || aRange == NULL)
{
return false;
}
vtkSmartPointer<vtkDoubleArray> pCandidate =
vtkSmartPointer<vtkDoubleArray>::New();
if(pCandidate == NULL)
{
return false;
}
pCandidate->SetName(pArrayName);
pCandidate->SetNumberOfComponents(1);
pCandidate->SetNumberOfTuples(vecVtkToOriginal.size());
double dMinimum = DBL_MAX;
double dMaximum = -DBL_MAX;
for(int nVtkCellId = 0;
nVtkCellId < vecVtkToOriginal.size(); ++nVtkCellId)
{
const vtkIdType nOriginalCellId = vecVtkToOriginal[nVtkCellId];
if(nOriginalCellId < 0 ||
nOriginalCellId >= vecOriginalValues.size())
{
return false;
}
const double dValue =
vecOriginalValues[static_cast<int>(nOriginalCellId)];
if(!isFiniteValue(dValue))
{
return false;
}
pCandidate->SetValue(nVtkCellId, dValue);
dMinimum = qMin(dMinimum, dValue);
dMaximum = qMax(dMaximum, dValue);
}
if(pCandidate->GetNumberOfComponents() != 1 ||
pCandidate->GetNumberOfTuples() != vecVtkToOriginal.size() ||
dMinimum == DBL_MAX || dMaximum == -DBL_MAX)
{
return false;
}
pArray = pCandidate;
aRange[0] = dMinimum;
aRange[1] = dMaximum;
return true;
}
bool validateStaticPropertyArray(
const QVector<double>& vecOriginalValues,
const QVector<vtkIdType>& vecVtkToOriginal,
vtkDoubleArray* pArray,
const char* pArrayName,
const double aRange[2])
{
if(pArray == NULL || pArrayName == NULL || aRange == NULL ||
pArray->GetName() == NULL ||
QString::fromLatin1(pArray->GetName()) !=
QString::fromLatin1(pArrayName) ||
pArray->GetNumberOfComponents() != 1 ||
pArray->GetNumberOfTuples() != vecVtkToOriginal.size())
{
return false;
}
double dMinimum = DBL_MAX;
double dMaximum = -DBL_MAX;
for(int nVtkCellId = 0;
nVtkCellId < vecVtkToOriginal.size(); ++nVtkCellId)
{
const vtkIdType nOriginalCellId = vecVtkToOriginal[nVtkCellId];
if(nOriginalCellId < 0 ||
nOriginalCellId >= vecOriginalValues.size())
{
return false;
}
const double dExpected =
vecOriginalValues[static_cast<int>(nOriginalCellId)];
const double dActual = pArray->GetValue(nVtkCellId);
if(!isFiniteValue(dActual) || dActual != dExpected)
{
return false;
}
dMinimum = qMin(dMinimum, dActual);
dMaximum = qMax(dMaximum, dActual);
}
return dMinimum == aRange[0] && dMaximum == aRange[1];
}
}
nmPebiResultSnapshotBuilder::nmPebiResultSnapshotBuilder()
: m_bFinalized(false),
m_bFailed(false),
m_bHasScalarRange(false),
m_bHasStaticProperties(false),
m_bHasReservoirParameters(false),
m_bHasPvtParameters(false),
m_bHasSolverSettings(false)
m_bHasSolverSettings(false),
m_nOriginalCellCount(0)
{
try
{
@ -460,16 +574,93 @@ bool nmPebiResultSnapshotBuilder::takeResultGrid(
vtkSmartPointer<vtkUnstructuredGrid>& pResultGrid)
{
if(!canWrite() || pResultGrid == NULL ||
pResultGrid->GetNumberOfCells() <= 0)
pResultGrid->GetNumberOfCells() <= 0 ||
m_pCandidate->m_pResultGrid != NULL)
{
return fail("Invalid PEBI result grid.");
}
if(hasReservedResultPropertyArray(pResultGrid))
{
return fail("PEBI result grid contains reserved property arrays.");
}
QVector<vtkIdType> vecVtkToOriginal;
QVector<vtkIdType> vecOriginalToVtk;
QString sMappingError;
if(nmPebiCellMapping::read(pResultGrid,
&vecVtkToOriginal, &vecOriginalToVtk, &sMappingError) !=
nmPebiCellMapping::MappingStatus_Valid ||
vecVtkToOriginal.size() != pResultGrid->GetNumberOfCells() ||
vecOriginalToVtk.isEmpty())
{
return fail(sMappingError.isEmpty()
? QString::fromLatin1("Invalid PEBI result cell mapping.")
: sMappingError);
}
// 通过交换所有权避免大网格 DeepCopy;成功后调用方不能再修改源网格。
m_vecVtkToOriginal.swap(vecVtkToOriginal);
m_nOriginalCellCount = vecOriginalToVtk.size();
m_pCandidate->m_pResultGrid = pResultGrid;
pResultGrid = NULL;
return true;
}
bool nmPebiResultSnapshotBuilder::takeStaticProperties(
nmPebiStaticPropertyValues& oValues)
{
if(!canWrite() || m_pCandidate->m_pResultGrid == NULL ||
m_bHasStaticProperties || m_nOriginalCellCount <= 0 ||
!oValues.isValidForCellCount(
static_cast<int>(m_nOriginalCellCount)))
{
return fail("Invalid PEBI static property values.");
}
vtkSmartPointer<vtkDoubleArray> pPermeability;
vtkSmartPointer<vtkDoubleArray> pPorosity;
vtkSmartPointer<vtkDoubleArray> pThickness;
double aPermeabilityRange[2] = { 0.0, 0.0 };
double aPorosityRange[2] = { 0.0, 0.0 };
double aThicknessRange[2] = { 0.0, 0.0 };
if(!buildStaticPropertyArray(
oValues.m_vecPermeabilityMilliDarcy,
m_vecVtkToOriginal, "permeability",
pPermeability, aPermeabilityRange) ||
!buildStaticPropertyArray(
oValues.m_vecPorosity,
m_vecVtkToOriginal, "porosity",
pPorosity, aPorosityRange) ||
!buildStaticPropertyArray(
oValues.m_vecThicknessMeters,
m_vecVtkToOriginal, "thickness",
pThickness, aThicknessRange))
{
return fail("Cannot build PEBI static property render caches.");
}
// 到这里之后不再执行可能失败的操作,连续交换保证调用方只在成功时清空。
m_pCandidate->m_pPermeability = pPermeability;
m_pCandidate->m_pPorosity = pPorosity;
m_pCandidate->m_pThickness = pThickness;
for(int nRangeIndex = 0; nRangeIndex < 2; ++nRangeIndex)
{
m_pCandidate->m_aStaticRanges[0][nRangeIndex] =
aPermeabilityRange[nRangeIndex];
m_pCandidate->m_aStaticRanges[1][nRangeIndex] =
aPorosityRange[nRangeIndex];
m_pCandidate->m_aStaticRanges[2][nRangeIndex] =
aThicknessRange[nRangeIndex];
}
qSwap(m_pCandidate->m_oStaticProperties.
m_vecPermeabilityMilliDarcy,
oValues.m_vecPermeabilityMilliDarcy);
qSwap(m_pCandidate->m_oStaticProperties.m_vecPorosity,
oValues.m_vecPorosity);
qSwap(m_pCandidate->m_oStaticProperties.m_vecThicknessMeters,
oValues.m_vecThicknessMeters);
m_bHasStaticProperties = true;
return true;
}
bool nmPebiResultSnapshotBuilder::addPressureFrame(
double dTime,
vtkSmartPointer<vtkDoubleArray>& pPressure)
@ -711,6 +902,41 @@ bool nmPebiResultSnapshotBuilder::validate(QString* pError)
{
return fail("PEBI result grid is missing.", pError);
}
QVector<vtkIdType> vecVerifiedVtkToOriginal;
QVector<vtkIdType> vecVerifiedOriginalToVtk;
QString sMappingError;
if(nmPebiCellMapping::read(m_pCandidate->m_pResultGrid,
&vecVerifiedVtkToOriginal, &vecVerifiedOriginalToVtk,
&sMappingError) != nmPebiCellMapping::MappingStatus_Valid ||
vecVerifiedVtkToOriginal != m_vecVtkToOriginal ||
vecVerifiedOriginalToVtk.size() != m_nOriginalCellCount)
{
return fail(sMappingError.isEmpty()
? QString::fromLatin1(
"PEBI result cell mapping changed during construction.")
: sMappingError,
pError);
}
if(!m_bHasStaticProperties ||
!m_pCandidate->m_oStaticProperties.isValidForCellCount(
static_cast<int>(m_nOriginalCellCount)) ||
!validateStaticPropertyArray(
m_pCandidate->m_oStaticProperties.
m_vecPermeabilityMilliDarcy,
m_vecVtkToOriginal, m_pCandidate->m_pPermeability,
"permeability", m_pCandidate->m_aStaticRanges[0]) ||
!validateStaticPropertyArray(
m_pCandidate->m_oStaticProperties.m_vecPorosity,
m_vecVtkToOriginal, m_pCandidate->m_pPorosity,
"porosity", m_pCandidate->m_aStaticRanges[1]) ||
!validateStaticPropertyArray(
m_pCandidate->m_oStaticProperties.m_vecThicknessMeters,
m_vecVtkToOriginal, m_pCandidate->m_pThickness,
"thickness", m_pCandidate->m_aStaticRanges[2]))
{
return fail("PEBI static properties are incomplete or inconsistent.",
pError);
}
if(m_pCandidate->m_vecPressureFrames.isEmpty())
{
return fail("PEBI pressure frames are missing.", pError);
@ -746,6 +972,9 @@ bool nmPebiResultSnapshotBuilder::validate(QString* pError)
if(!isFiniteValue(oFrame.m_dTime) ||
(nIndex > 0 && oFrame.m_dTime <= dPreviousTime) ||
oFrame.m_pPressure == NULL ||
oFrame.m_pPressure->GetName() == NULL ||
QString::fromLatin1(oFrame.m_pPressure->GetName()) !=
QString::fromLatin1("p") ||
oFrame.m_pPressure->GetNumberOfComponents() != 1 ||
oFrame.m_pPressure->GetNumberOfTuples() != nCellCount)
{
@ -885,13 +1114,12 @@ bool nmPebiResultSnapshotBuilder::validate(QString* pError)
setDisplayWellIds.insert(sWellInstanceId);
}
// 发布前基础网格必须与压力帧解耦,渲染网格只能按帧受控绑定。
m_pCandidate->m_pResultGrid->GetCellData()->SetScalars(NULL);
m_pCandidate->m_pResultGrid->GetCellData()->RemoveArray("p");
if(m_pCandidate->m_pResultGrid->GetCellData()->GetScalars() != NULL ||
m_pCandidate->m_pResultGrid->GetCellData()->GetArray("p") != NULL)
// finalize() 只验证候选,不再修改基础网格。正式结果数组必须由快照
// 独立持有,其他模块写入的非保留数组和活动属性则保持原样。
if(hasReservedResultPropertyArray(m_pCandidate->m_pResultGrid))
{
return fail("PEBI result grid still owns pressure scalars.", pError);
return fail("PEBI result grid still owns result property scalars.",
pError);
}
return true;
}

@ -2,15 +2,59 @@
#include <vtkAlgorithmOutput.h>
#include <vtkCellData.h>
#include <vtkDataArray.h>
#include <vtkDoubleArray.h>
#include <vtkTrivialProducer.h>
#include <vtkUnstructuredGrid.h>
#include <QtGlobal>
#include <math.h>
#if !defined(_MSC_VER)
#include <cmath>
#endif
namespace {
bool isFiniteValue(double dValue)
{
#if defined(_MSC_VER)
return _finite(dValue) != 0;
#else
return std::isfinite(dValue);
#endif
}
bool isValidCopiedScalars(vtkDoubleArray* pScalars,
vtkIdType nExpectedCellCount,
const QString& sExpectedArrayName)
{
if(pScalars == NULL || pScalars->GetName() == NULL ||
QString::fromLatin1(pScalars->GetName()) != sExpectedArrayName ||
pScalars->GetNumberOfComponents() != 1 ||
pScalars->GetNumberOfTuples() != nExpectedCellCount)
{
return false;
}
for(vtkIdType nCellId = 0; nCellId < nExpectedCellCount; ++nCellId)
{
if(!isFiniteValue(pScalars->GetValue(nCellId)))
{
return false;
}
}
return true;
}
}
nmPebiResultSnapshotRenderAdapter::nmPebiResultSnapshotRenderAdapter()
: m_pRenderGrid(vtkSmartPointer<vtkUnstructuredGrid>::New()),
m_pPressureBuffer(vtkSmartPointer<vtkDoubleArray>::New()),
m_pActiveBuffer(vtkSmartPointer<vtkDoubleArray>::New()),
m_pStandbyBuffer(vtkSmartPointer<vtkDoubleArray>::New()),
m_pProducer(vtkSmartPointer<vtkTrivialProducer>::New()),
m_eBindingMode(SharedPressureFrame),
m_eBindingMode(SharedPropertyArray),
m_nCurrentFrameIndex(-1)
{
}
@ -22,12 +66,12 @@ nmPebiResultSnapshotRenderAdapter::~nmPebiResultSnapshotRenderAdapter()
bool nmPebiResultSnapshotRenderAdapter::initialize(
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot,
PressureBindingMode eBindingMode)
PropertyBindingMode eBindingMode)
{
clear();
if(pSnapshot.isNull() || !pSnapshot->isComplete() ||
(eBindingMode != SharedPressureFrame &&
eBindingMode != CopiedPressureFrame))
(eBindingMode != SharedPropertyArray &&
eBindingMode != CopiedPropertyArray))
{
return false;
}
@ -46,6 +90,20 @@ bool nmPebiResultSnapshotRenderAdapter::initialize(
return true;
}
bool nmPebiResultSnapshotRenderAdapter::initialize(
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot,
PressureBindingMode eBindingMode)
{
if(eBindingMode != SharedPressureFrame &&
eBindingMode != CopiedPressureFrame)
{
return false;
}
return initialize(pSnapshot,
eBindingMode == CopiedPressureFrame
? CopiedPropertyArray : SharedPropertyArray);
}
void nmPebiResultSnapshotRenderAdapter::clear()
{
// 先断开管道和 CellData,及时释放对旧快照压力数组的临时 VTK 引用。
@ -59,38 +117,62 @@ void nmPebiResultSnapshotRenderAdapter::clear()
m_pRenderGrid->GetCellData()->Initialize();
m_pRenderGrid->Initialize();
}
if(m_pPressureBuffer != NULL)
if(m_pActiveBuffer != NULL)
{
m_pPressureBuffer->Initialize();
m_pActiveBuffer->Initialize();
}
if(m_pStandbyBuffer != NULL)
{
m_pStandbyBuffer->Initialize();
}
m_pSnapshot.clear();
m_eBindingMode = SharedPressureFrame;
m_eBindingMode = SharedPropertyArray;
m_sCurrentPropertyId.clear();
m_nCurrentFrameIndex = -1;
}
bool nmPebiResultSnapshotRenderAdapter::setPressureFrame(int nFrameIndex)
bool nmPebiResultSnapshotRenderAdapter::setPropertyFrame(
const QString& sPropertyId,
int nFrameIndex)
{
if(m_pSnapshot.isNull() || m_pRenderGrid == NULL ||
nFrameIndex < 0 ||
nFrameIndex >= m_pSnapshot->getPressureFrameCount())
nFrameIndex < 0 || !m_pSnapshot->hasProperty(sPropertyId) ||
nFrameIndex >= m_pSnapshot->getPropertyFrameCount(sPropertyId))
{
return false;
}
bool bBound = false;
if(m_eBindingMode == SharedPressureFrame)
if(m_eBindingMode == SharedPropertyArray)
{
bBound = m_pSnapshot->bindPressureFrame(
nFrameIndex, m_pRenderGrid);
bBound = m_pSnapshot->bindPropertyFrame(
sPropertyId, nFrameIndex, m_pRenderGrid);
}
else
{
bBound = m_pSnapshot->copyPressureFrame(
nFrameIndex, m_pPressureBuffer);
// 活动缓冲在整个准备阶段保持不变;只有备用缓冲完整有效后才切换。
bBound = m_pSnapshot->copyPropertyFrame(
sPropertyId, nFrameIndex, m_pStandbyBuffer) &&
isValidCopiedScalars(
m_pStandbyBuffer, m_pSnapshot->getResultCellCount(),
sPropertyId == QString::fromLatin1("pressure")
? QString::fromLatin1("p") : sPropertyId);
if(bBound)
{
m_pRenderGrid->GetCellData()->SetScalars(m_pPressureBuffer);
m_pRenderGrid->Modified();
vtkDataArray* pPreviousScalars =
m_pRenderGrid->GetCellData()->GetScalars();
m_pRenderGrid->GetCellData()->SetScalars(m_pStandbyBuffer);
if(m_pRenderGrid->GetCellData()->GetScalars() !=
m_pStandbyBuffer.GetPointer())
{
m_pRenderGrid->GetCellData()->SetScalars(pPreviousScalars);
bBound = false;
}
else
{
qSwap(m_pActiveBuffer, m_pStandbyBuffer);
m_pRenderGrid->Modified();
}
}
}
@ -98,11 +180,17 @@ bool nmPebiResultSnapshotRenderAdapter::setPressureFrame(int nFrameIndex)
{
return false;
}
m_sCurrentPropertyId = sPropertyId;
m_nCurrentFrameIndex = nFrameIndex;
m_pProducer->Modified();
return true;
}
bool nmPebiResultSnapshotRenderAdapter::setPressureFrame(int nFrameIndex)
{
return setPropertyFrame(QString::fromLatin1("pressure"), nFrameIndex);
}
vtkAlgorithmOutput*
nmPebiResultSnapshotRenderAdapter::getOutputPort() const
{
@ -115,6 +203,11 @@ int nmPebiResultSnapshotRenderAdapter::getCurrentFrameIndex() const
return m_nCurrentFrameIndex;
}
QString nmPebiResultSnapshotRenderAdapter::getCurrentPropertyId() const
{
return m_sCurrentPropertyId;
}
vtkIdType nmPebiResultSnapshotRenderAdapter::getCellCount() const
{
return m_pRenderGrid == NULL ? 0 :
@ -122,7 +215,12 @@ vtkIdType nmPebiResultSnapshotRenderAdapter::getCellCount() const
}
bool nmPebiResultSnapshotRenderAdapter::isUsingSharedPressure() const
{
return isUsingSharedProperties();
}
bool nmPebiResultSnapshotRenderAdapter::isUsingSharedProperties() const
{
return !m_pSnapshot.isNull() &&
m_eBindingMode == SharedPressureFrame;
m_eBindingMode == SharedPropertyArray;
}

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save