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

790 lines
25 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 "nmPebiResultSnapshotBuilder.h"
#include <QSet>
#include <QUuid>
#include <QtGlobal>
#include <new>
#include <float.h>
#include <math.h>
#if defined(_MSC_VER)
#else
#include <cmath>
#endif
#include <vtkCellData.h>
#include <vtkDoubleArray.h>
#include <vtkUnstructuredGrid.h>
namespace {
// Builder 只校验内存模型的完整性;持久化文件的数量上限和字节上限
// 由 v3 加载器在分配 VTK/Qt 容器之前单独检查。
bool isFiniteValue(double dValue)
{
#if defined(_MSC_VER)
return _finite(dValue) != 0;
#else
return std::isfinite(dValue);
#endif
}
bool isValidInstanceId(const QString& sInstanceId)
{
return !sInstanceId.isEmpty() && !QUuid(sInstanceId).isNull();
}
bool isFiniteVector(const QVector<double>& vecValues)
{
for(int nIndex = 0; nIndex < vecValues.size(); ++nIndex)
{
if(!isFiniteValue(vecValues[nIndex]))
{
return false;
}
}
return true;
}
bool isStrictlyIncreasing(const QVector<double>& vecValues)
{
for(int nIndex = 1; nIndex < vecValues.size(); ++nIndex)
{
if(vecValues[nIndex] <= vecValues[nIndex - 1])
{
return false;
}
}
return true;
}
bool isRequiredSeriesValid(const QVector<double>& vecAxis,
const QVector<double>& vecValues)
{
return vecAxis.size() >= 2 &&
vecValues.size() == vecAxis.size();
}
bool isOptionalSeriesValid(const QVector<double>& vecAxis,
const QVector<double>& vecValues)
{
return vecValues.isEmpty() ||
(!vecAxis.isEmpty() && vecValues.size() == vecAxis.size());
}
bool isCurveValid(const QVector<QVector<double> >& vecCurve,
int nMinimumSeries,
bool bRequired)
{
if(vecCurve.isEmpty())
{
return !bRequired;
}
if(vecCurve.size() < nMinimumSeries || vecCurve[0].isEmpty())
{
return false;
}
const int nPointCount = vecCurve[0].size();
for(int nSeries = 0; nSeries < vecCurve.size(); ++nSeries)
{
if(vecCurve[nSeries].size() != nPointCount ||
!isFiniteVector(vecCurve[nSeries]))
{
return false;
}
}
return true;
}
bool areWellCurvesValid(const nmPebiResultWellCurves& oCurves)
{
return isCurveValid(oCurves.m_vecHistoryPressure, 2, false) &&
isCurveValid(oCurves.m_vecHistoryLogLog, 3, false) &&
isCurveValid(oCurves.m_vecHistorySemiLog, 2, false) &&
isCurveValid(oCurves.m_vecResultPressure, 2, true) &&
isCurveValid(oCurves.m_vecResultLogLog, 3, false) &&
isCurveValid(oCurves.m_vecResultSemiLog, 2, false);
}
bool areReservoirParametersFinite(
const nmPebiResultReservoirParameters& oParameters)
{
return isFiniteValue(oParameters.m_dInitialPressure) &&
isFiniteValue(oParameters.m_dPermeability) &&
isFiniteValue(oParameters.m_dThickness) &&
isFiniteValue(oParameters.m_dPorosity) &&
isFiniteValue(oParameters.m_dTotalCompressibility) &&
isFiniteValue(oParameters.m_dRockCompressibility) &&
isFiniteValue(oParameters.m_dOilSaturation) &&
isFiniteValue(oParameters.m_dGasSaturation) &&
isFiniteValue(oParameters.m_dWaterSaturation);
}
bool arePvtParametersFinite(const nmPebiResultPvtParameters& oParameters)
{
if((oParameters.m_bHasBubblePoint &&
!isFiniteValue(oParameters.m_dBubblePoint)) ||
!isFiniteValue(oParameters.m_dConstantBo) ||
!isFiniteValue(oParameters.m_dConstantMiuo) ||
!isFiniteValue(oParameters.m_dConstantBg) ||
!isFiniteValue(oParameters.m_dConstantMiug) ||
!isFiniteValue(oParameters.m_dConstantBw) ||
!isFiniteValue(oParameters.m_dConstantMiuw))
{
return false;
}
return isFiniteVector(oParameters.m_vecPressure) &&
isFiniteVector(oParameters.m_vecRso) &&
isFiniteVector(oParameters.m_vecBo) &&
isFiniteVector(oParameters.m_vecCo) &&
isFiniteVector(oParameters.m_vecMiuo) &&
isFiniteVector(oParameters.m_vecRouo) &&
isFiniteVector(oParameters.m_vecRv) &&
isFiniteVector(oParameters.m_vecBg) &&
isFiniteVector(oParameters.m_vecCg) &&
isFiniteVector(oParameters.m_vecMiug) &&
isFiniteVector(oParameters.m_vecRoug) &&
isFiniteVector(oParameters.m_vecZ) &&
isFiniteVector(oParameters.m_vecRsw) &&
isFiniteVector(oParameters.m_vecBw) &&
isFiniteVector(oParameters.m_vecCw) &&
isFiniteVector(oParameters.m_vecMiuw) &&
isFiniteVector(oParameters.m_vecRouw) &&
isFiniteVector(oParameters.m_vecV) &&
isFiniteVector(oParameters.m_vecKkInitial) &&
isFiniteVector(oParameters.m_vecCfCfInitial) &&
isFiniteVector(oParameters.m_vecSo) &&
isFiniteVector(oParameters.m_vecKro) &&
isFiniteVector(oParameters.m_vecSg) &&
isFiniteVector(oParameters.m_vecKrg) &&
isFiniteVector(oParameters.m_vecSw) &&
isFiniteVector(oParameters.m_vecKrw);
}
bool areOptionalPressureSeriesValid(
const nmPebiResultPvtParameters& oParameters)
{
const QVector<double>& vecPressure = oParameters.m_vecPressure;
return isOptionalSeriesValid(vecPressure, oParameters.m_vecRso) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecBo) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecCo) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecMiuo) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecRouo) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecRv) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecBg) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecCg) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecMiug) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecRoug) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecZ) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecRsw) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecBw) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecCw) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecMiuw) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecRouw) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecV) &&
isOptionalSeriesValid(vecPressure, oParameters.m_vecKkInitial) &&
isOptionalSeriesValid(vecPressure,
oParameters.m_vecCfCfInitial);
}
bool areOptionalSaturationSeriesValid(
const nmPebiResultPvtParameters& oParameters,
int nSolverModelType)
{
if(!oParameters.m_vecSo.isEmpty() &&
!isStrictlyIncreasing(oParameters.m_vecSo))
{
return false;
}
if(!oParameters.m_vecSg.isEmpty() &&
!isStrictlyIncreasing(oParameters.m_vecSg))
{
return false;
}
if(!oParameters.m_vecSw.isEmpty() &&
!isStrictlyIncreasing(oParameters.m_vecSw))
{
return false;
}
if(oParameters.m_vecSo.isEmpty() != oParameters.m_vecKro.isEmpty() ||
(!oParameters.m_vecSo.isEmpty() &&
oParameters.m_vecSo.size() != oParameters.m_vecKro.size()) ||
oParameters.m_vecSg.isEmpty() != oParameters.m_vecKrg.isEmpty() ||
(!oParameters.m_vecSg.isEmpty() &&
oParameters.m_vecSg.size() != oParameters.m_vecKrg.size()))
{
return false;
}
// 当前油水两相求解器以 So 为横坐标,同时保存 Kro 和 Krw。
if(nSolverModelType == 8)
{
return isRequiredSeriesValid(oParameters.m_vecSo,
oParameters.m_vecKro) &&
isRequiredSeriesValid(oParameters.m_vecSo,
oParameters.m_vecKrw);
}
if(oParameters.m_vecKrw.isEmpty())
{
return oParameters.m_vecSw.isEmpty();
}
if(!oParameters.m_vecSw.isEmpty() &&
oParameters.m_vecKrw.size() != oParameters.m_vecSw.size())
{
return false;
}
if(!oParameters.m_vecSo.isEmpty() &&
oParameters.m_vecKrw.size() != oParameters.m_vecSo.size())
{
return false;
}
return !oParameters.m_vecSw.isEmpty() ||
!oParameters.m_vecSo.isEmpty();
}
bool arePvtParametersStructurallyValid(
const nmPebiResultPvtParameters& oParameters,
int nSolverModelType)
{
const QVector<double>& vecPressure = oParameters.m_vecPressure;
if((!vecPressure.isEmpty() && !isStrictlyIncreasing(vecPressure)) ||
!areOptionalPressureSeriesValid(oParameters) ||
!areOptionalSaturationSeriesValid(oParameters, nSolverModelType))
{
return false;
}
switch(nSolverModelType)
{
case 1: // 常量油 PVT
case 3: // 常量水 PVT
return true;
case 2: // 变化油 PVT
return isRequiredSeriesValid(vecPressure, oParameters.m_vecBo) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecCo) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecMiuo);
case 4: // 变化水 PVT
return isRequiredSeriesValid(vecPressure, oParameters.m_vecBw) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecCw) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecMiuw);
case 5: // 变化气 PVT
case 6: // 气拟压力模型
return isRequiredSeriesValid(vecPressure, oParameters.m_vecBg) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecCg) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecMiug);
case 8: // 油水两相
return isRequiredSeriesValid(vecPressure, oParameters.m_vecBo) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecMiuo) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecBw) &&
isRequiredSeriesValid(vecPressure, oParameters.m_vecMiuw);
default:
return false;
}
}
bool areSolverSettingsValid(const nmPebiResultSolverSettings& oSettings)
{
const bool bSupportedModel =
oSettings.m_nSolverModelType == 1 ||
oSettings.m_nSolverModelType == 2 ||
oSettings.m_nSolverModelType == 3 ||
oSettings.m_nSolverModelType == 4 ||
oSettings.m_nSolverModelType == 5 ||
oSettings.m_nSolverModelType == 6 ||
oSettings.m_nSolverModelType == 8;
const bool bSupportedSolver =
oSettings.m_nPebiSolverType == 1 ||
oSettings.m_nPebiSolverType == 2;
if(!bSupportedModel || !bSupportedSolver ||
oSettings.m_nOmpThreads <= 0 ||
oSettings.m_nIluReuseSteps <= 0 ||
!isFiniteValue(oSettings.m_dGridControl) ||
oSettings.m_dGridControl <= 0.0)
{
return false;
}
return !oSettings.m_bHasTimeStepSettings ||
(isFiniteValue(oSettings.m_dTimeGrowthExponent) &&
isFiniteValue(oSettings.m_dMinDeltaT) &&
isFiniteValue(oSettings.m_dMaxDeltaT) &&
oSettings.m_dTimeGrowthExponent > 0.0 &&
oSettings.m_dMinDeltaT > 0.0 &&
oSettings.m_dMaxDeltaT >= oSettings.m_dMinDeltaT);
}
}
nmPebiResultSnapshotBuilder::nmPebiResultSnapshotBuilder()
: m_bFinalized(false),
m_bFailed(false),
m_bHasScalarRange(false),
m_bHasReservoirParameters(false),
m_bHasPvtParameters(false),
m_bHasSolverSettings(false)
{
try
{
m_pCandidate = QSharedPointer<nmPebiResultSnapshot>(
new nmPebiResultSnapshot());
}
catch(const std::bad_alloc&)
{
m_bFailed = true;
m_sLastError = "Cannot allocate PEBI result snapshot.";
}
}
nmPebiResultSnapshotBuilder::~nmPebiResultSnapshotBuilder()
{
m_pCandidate.clear();
}
bool nmPebiResultSnapshotBuilder::canWrite()
{
if(m_pCandidate.isNull() || m_bFinalized || m_bFailed)
{
if(m_sLastError.isEmpty())
{
m_sLastError = "PEBI result snapshot builder is not writable.";
}
return false;
}
return true;
}
bool nmPebiResultSnapshotBuilder::fail(
const QString& sError,
QString* pError)
{
m_bFailed = true;
m_sLastError = sError;
if(pError != NULL)
{
*pError = sError;
}
return false;
}
bool nmPebiResultSnapshotBuilder::setSnapshotId(
const QString& sSnapshotId)
{
if(!canWrite() || !isValidInstanceId(sSnapshotId))
{
return fail("Invalid PEBI result snapshot identity.");
}
m_pCandidate->m_sSnapshotId = QUuid(sSnapshotId).toString()
.remove('{').remove('}');
return true;
}
bool nmPebiResultSnapshotBuilder::setInputRevisions(
quint64 nGridInputRevision,
quint64 nResultInputRevision)
{
if(!canWrite() || nGridInputRevision == 0 || nResultInputRevision == 0)
{
return fail("Invalid PEBI result input revisions.");
}
m_pCandidate->m_nGridInputRevision = nGridInputRevision;
m_pCandidate->m_nResultInputRevision = nResultInputRevision;
return true;
}
bool nmPebiResultSnapshotBuilder::takeResultGrid(
vtkSmartPointer<vtkUnstructuredGrid>& pResultGrid)
{
if(!canWrite() || pResultGrid == NULL ||
pResultGrid->GetNumberOfCells() <= 0)
{
return fail("Invalid PEBI result grid.");
}
// 通过交换所有权避免大网格 DeepCopy成功后调用方不能再修改源网格。
m_pCandidate->m_pResultGrid = pResultGrid;
pResultGrid = NULL;
return true;
}
bool nmPebiResultSnapshotBuilder::addPressureFrame(
double dTime,
vtkSmartPointer<vtkDoubleArray>& pPressure)
{
if(!canWrite() || !isFiniteValue(dTime) || pPressure == NULL ||
pPressure->GetNumberOfComponents() != 1 ||
pPressure->GetNumberOfTuples() <= 0)
{
return fail("Invalid PEBI pressure frame.");
}
if(!m_pCandidate->m_vecPressureFrames.isEmpty() &&
dTime <= m_pCandidate->m_vecPressureFrames.last().m_dTime)
{
return fail("PEBI pressure frame times must be strictly increasing.");
}
try
{
// 压力帧同样直接转交,发布路径不会产生第二份完整时间步数据。
nmPebiResultSnapshot::PressureFrame oFrame;
oFrame.m_dTime = dTime;
pPressure->SetName("p");
oFrame.m_pPressure = pPressure;
m_pCandidate->m_vecPressureFrames.append(oFrame);
pPressure = NULL;
}
catch(const std::bad_alloc&)
{
return fail("Cannot allocate PEBI pressure frame list.");
}
return true;
}
bool nmPebiResultSnapshotBuilder::setScalarRange(
double dMinimum,
double dMaximum)
{
if(!canWrite() || !isFiniteValue(dMinimum) ||
!isFiniteValue(dMaximum) ||
dMinimum > dMaximum)
{
return fail("Invalid PEBI scalar range.");
}
m_pCandidate->m_dScalarMin = dMinimum;
m_pCandidate->m_dScalarMax = dMaximum;
m_bHasScalarRange = true;
return true;
}
bool nmPebiResultSnapshotBuilder::addSolverSlot(
const nmPebiResultSolverSlot& oSlot)
{
if(!canWrite())
{
return false;
}
try
{
m_pCandidate->m_vecSolverSlots.append(oSlot);
}
catch(const std::bad_alloc&)
{
return fail("Cannot allocate PEBI solver slot list.");
}
return true;
}
bool nmPebiResultSnapshotBuilder::addWell(
const nmPebiResultWellSnapshot& oWell)
{
if(!canWrite())
{
return false;
}
try
{
m_pCandidate->m_vecWells.append(oWell);
}
catch(const std::bad_alloc&)
{
return fail("Cannot allocate PEBI result well list.");
}
return true;
}
bool nmPebiResultSnapshotBuilder::setDisplayWellInstanceIds(
const QStringList& listWellInstanceIds)
{
if(!canWrite())
{
return false;
}
try
{
m_pCandidate->m_listDisplayWellInstanceIds = listWellInstanceIds;
}
catch(const std::bad_alloc&)
{
return fail("Cannot allocate PEBI display well list.");
}
return true;
}
bool nmPebiResultSnapshotBuilder::setReservoirParameters(
const nmPebiResultReservoirParameters& oParameters)
{
if(!canWrite())
{
return false;
}
m_pCandidate->m_oReservoirParameters = oParameters;
m_bHasReservoirParameters = true;
return true;
}
bool nmPebiResultSnapshotBuilder::setPvtParameters(
const nmPebiResultPvtParameters& oParameters)
{
if(!canWrite())
{
return false;
}
try
{
m_pCandidate->m_oPvtParameters = oParameters;
}
catch(const std::bad_alloc&)
{
return fail("Cannot allocate PEBI PVT snapshot.");
}
m_bHasPvtParameters = true;
return true;
}
bool nmPebiResultSnapshotBuilder::setSolverSettings(
const nmPebiResultSolverSettings& oSettings)
{
if(!canWrite())
{
return false;
}
m_pCandidate->m_oSolverSettings = oSettings;
m_bHasSolverSettings = true;
return true;
}
bool nmPebiResultSnapshotBuilder::validate(QString* pError)
{
// 第一部分校验快照身份、输入版本和所有必填结果载荷。
if(!isValidInstanceId(m_pCandidate->m_sSnapshotId))
{
return fail("PEBI result snapshot identity is invalid.", pError);
}
if(m_pCandidate->m_nGridInputRevision == 0 ||
m_pCandidate->m_nResultInputRevision == 0)
{
return fail("PEBI result revisions are missing.", pError);
}
if(m_pCandidate->m_pResultGrid == NULL ||
m_pCandidate->m_pResultGrid->GetNumberOfCells() <= 0)
{
return fail("PEBI result grid is missing.", pError);
}
if(m_pCandidate->m_vecPressureFrames.isEmpty())
{
return fail("PEBI pressure frames are missing.", pError);
}
if(!m_bHasScalarRange || !m_bHasReservoirParameters ||
!m_bHasPvtParameters || !m_bHasSolverSettings)
{
return fail("PEBI result parameters are incomplete.", pError);
}
if(!areReservoirParametersFinite(
m_pCandidate->m_oReservoirParameters) ||
!areSolverSettingsValid(m_pCandidate->m_oSolverSettings) ||
!arePvtParametersFinite(m_pCandidate->m_oPvtParameters) ||
!arePvtParametersStructurallyValid(
m_pCandidate->m_oPvtParameters,
m_pCandidate->m_oSolverSettings.m_nSolverModelType))
{
return fail("PEBI result parameters are invalid.", pError);
}
// 每帧必须与基础网格一一对应;同时从实际压力重新计算标量范围,
// 防止保存或求解端传入与场数据不一致的显示范围。
const vtkIdType nCellCount =
m_pCandidate->m_pResultGrid->GetNumberOfCells();
double dPreviousTime = 0.0;
double dActualMinimum = DBL_MAX;
double dActualMaximum = -DBL_MAX;
for(int nIndex = 0;
nIndex < m_pCandidate->m_vecPressureFrames.size(); ++nIndex)
{
const nmPebiResultSnapshot::PressureFrame& oFrame =
m_pCandidate->m_vecPressureFrames[nIndex];
if(!isFiniteValue(oFrame.m_dTime) ||
(nIndex > 0 && oFrame.m_dTime <= dPreviousTime) ||
oFrame.m_pPressure == NULL ||
oFrame.m_pPressure->GetNumberOfComponents() != 1 ||
oFrame.m_pPressure->GetNumberOfTuples() != nCellCount)
{
return fail("PEBI pressure frame dimensions are invalid.", pError);
}
for(vtkIdType nCellIndex = 0; nCellIndex < nCellCount;
++nCellIndex)
{
const double dValue = oFrame.m_pPressure->GetValue(nCellIndex);
if(!isFiniteValue(dValue))
{
return fail("PEBI pressure frame contains invalid values.",
pError);
}
if(dValue < dActualMinimum)
{
dActualMinimum = dValue;
}
if(dValue > dActualMaximum)
{
dActualMaximum = dValue;
}
}
dPreviousTime = oFrame.m_dTime;
}
const double dRangeScale = qMax(1.0,
qMax(fabs(dActualMinimum), fabs(dActualMaximum)));
const double dRangeTolerance = dRangeScale * 1.0e-10;
if(fabs(m_pCandidate->m_dScalarMin - dActualMinimum) >
dRangeTolerance ||
fabs(m_pCandidate->m_dScalarMax - dActualMaximum) >
dRangeTolerance)
{
return fail("PEBI scalar range does not match pressure frames.",
pError);
}
// UUID 是结果关联主键,井编码只作为求解时冻结元数据参与一致性校验。
QSet<QString> setWellIds;
QSet<QString> setWellCodes;
for(int nIndex = 0; nIndex < m_pCandidate->m_vecWells.size(); ++nIndex)
{
const nmPebiResultWellSnapshot& oWell =
m_pCandidate->m_vecWells[nIndex];
if(!isValidInstanceId(oWell.m_sWellInstanceId) ||
oWell.m_sWellCode.isEmpty() || oWell.m_sWellName.isEmpty() ||
oWell.m_eWellType == Unknow_Well ||
(oWell.m_eWellMode != NM_CaseWell_RateControlled &&
oWell.m_eWellMode != NM_CaseWell_Observation) ||
!isFiniteValue(oWell.m_oLocation.x()) ||
!isFiniteValue(oWell.m_oLocation.y()) ||
!isFiniteValue(oWell.m_dRadius) ||
!isFiniteValue(oWell.m_dWellboreStorage) ||
(oWell.m_bHasSkin && !isFiniteValue(oWell.m_dSkin)) ||
(oWell.m_bHasDfc && !isFiniteValue(oWell.m_dDfc)) ||
setWellIds.contains(oWell.m_sWellInstanceId) ||
setWellCodes.contains(oWell.m_sWellCode) ||
!areWellCurvesValid(oWell.m_oCurves))
{
return fail("PEBI result well data are invalid.", pError);
}
setWellIds.insert(oWell.m_sWellInstanceId);
setWellCodes.insert(oWell.m_sWellCode);
}
// 求解器槽位必须连续且覆盖全部真实井;手工裂缝槽位不允许伪装成井。
QSet<QString> setSlotWellIds;
int nRealWellSlotCount = 0;
for(int nIndex = 0; nIndex < m_pCandidate->m_vecSolverSlots.size();
++nIndex)
{
const nmPebiResultSolverSlot& oSlot =
m_pCandidate->m_vecSolverSlots[nIndex];
if(oSlot.m_nSolverIndex != nIndex ||
(oSlot.m_eEntryKind != NM_SolverEntry_Well &&
oSlot.m_eEntryKind != NM_SolverEntry_ManualFracture))
{
return fail("PEBI solver slot order is invalid.", pError);
}
if(oSlot.m_eEntryKind == NM_SolverEntry_ManualFracture)
{
if(!oSlot.m_sWellInstanceId.isEmpty())
{
return fail("Manual fracture slot has a well identity.", pError);
}
continue;
}
const nmPebiResultWellSnapshot* pWell =
m_pCandidate->findWell(oSlot.m_sWellInstanceId);
if(pWell == NULL || pWell->m_sWellCode != oSlot.m_sWellCode ||
pWell->m_eWellType != oSlot.m_eWellType ||
setSlotWellIds.contains(oSlot.m_sWellInstanceId))
{
return fail("PEBI solver slot well mapping is invalid.", pError);
}
setSlotWellIds.insert(oSlot.m_sWellInstanceId);
++nRealWellSlotCount;
}
if(nRealWellSlotCount != m_pCandidate->m_vecWells.size())
{
return fail("PEBI result well count does not match solver slots.", pError);
}
// 可显示列表是完整井集合的有序子集,允许为空以支持仅显示场图。
QSet<QString> setDisplayWellIds;
for(int nIndex = 0;
nIndex < m_pCandidate->m_listDisplayWellInstanceIds.size(); ++nIndex)
{
const QString& sWellInstanceId =
m_pCandidate->m_listDisplayWellInstanceIds[nIndex];
if(!setWellIds.contains(sWellInstanceId) ||
setDisplayWellIds.contains(sWellInstanceId))
{
return fail("PEBI display well list is invalid.", 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)
{
return fail("PEBI result grid still owns pressure scalars.", pError);
}
return true;
}
bool nmPebiResultSnapshotBuilder::finalize(QString* pError)
{
if(!canWrite())
{
if(pError != NULL)
{
*pError = m_sLastError;
}
return false;
}
try
{
if(!validate(pError))
{
return false;
}
// 只有全部校验通过后才设置完整标志Manager 不接受其他候选。
m_pCandidate->m_bComplete = true;
m_bFinalized = true;
return true;
}
catch(const std::bad_alloc&)
{
return fail("Cannot finalize PEBI result snapshot.", pError);
}
}
QSharedPointer<nmPebiResultSnapshot>
nmPebiResultSnapshotBuilder::takeCandidate()
{
if(!m_bFinalized || m_bFailed || m_pCandidate.isNull() ||
!m_pCandidate->isComplete())
{
return QSharedPointer<nmPebiResultSnapshot>();
}
// take 后 Builder 不再持有可写别名,后续生命周期交给提交方管理。
QSharedPointer<nmPebiResultSnapshot> pCandidate = m_pCandidate;
m_pCandidate.clear();
return pCandidate;
}
QString nmPebiResultSnapshotBuilder::getLastError() const
{
return m_sLastError;
}