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

1189 lines
48 KiB
C++

#include "nmPebiResultSnapshotSerializer.h"
#include "nmDataJsonTools.h"
#include "nmPebiResultSnapshot.h"
#include "nmPebiResultSnapshotBuilder.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QMap>
#include <QSet>
#include <QtGlobal>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <new>
#include <vtkCellData.h>
#include <vtkDoubleArray.h>
#include <vtkUnstructuredGrid.h>
#include <rapidjson/document.h>
namespace {
const char g_aPressureMagic[8] = { 'N', 'M', 'P', 'R', 'E', 'S', '3', 0 };
const char g_aCurvesMagic[8] = { 'N', 'M', 'C', 'U', 'R', 'V', '3', 0 };
const quint32 g_nBinaryFormatVersion = 1;
const quint32 g_nEndianMarker = 0x01020304u;
const quint64 g_nMaximumCellCount = 50000000ull;
const quint32 g_nMaximumFrameCount = 10000u;
const quint32 g_nMaximumWellCount = 10000u;
const quint32 g_nMaximumCurveSeries = 64u;
const quint64 g_nMaximumCurvePoints = 50000000ull;
const quint32 g_nMaximumUuidBytes = 128u;
const quint64 g_nMaximumSnapshotJsonBytes = 64ull * 1024ull * 1024ull;
const qint64 g_nStreamChunkBytes = 4 * 1024 * 1024;
bool setError(QString* pError, const QString& sError)
{
if(pError != NULL)
{
*pError = sError;
}
return false;
}
bool isFiniteValue(double dValue)
{
#if defined(_MSC_VER)
return _finite(dValue) != 0;
#else
return qIsFinite(dValue);
#endif
}
bool safeMultiply(quint64 nLeft, quint64 nRight, quint64& nProduct)
{
if(nLeft != 0 && nRight > (~static_cast<quint64>(0)) / nLeft)
{
return false;
}
nProduct = nLeft * nRight;
return true;
}
bool safeAdd(quint64 nLeft, quint64 nRight, quint64& nSum)
{
if(nRight > (~static_cast<quint64>(0)) - nLeft)
{
return false;
}
nSum = nLeft + nRight;
return true;
}
bool writeAll(QFile& oFile, const char* pData, quint64 nBytes)
{
quint64 nWritten = 0;
while(nWritten < nBytes)
{
const qint64 nChunk = static_cast<qint64>(qMin(
static_cast<quint64>(g_nStreamChunkBytes),
nBytes - nWritten));
if(oFile.write(pData + static_cast<size_t>(nWritten), nChunk) != nChunk)
{
return false;
}
nWritten += static_cast<quint64>(nChunk);
}
return true;
}
bool readAll(QFile& oFile, char* pData, quint64 nBytes)
{
quint64 nRead = 0;
while(nRead < nBytes)
{
const qint64 nChunk = static_cast<qint64>(qMin(
static_cast<quint64>(g_nStreamChunkBytes),
nBytes - nRead));
if(oFile.read(pData + static_cast<size_t>(nRead), nChunk) != nChunk)
{
return false;
}
nRead += static_cast<quint64>(nChunk);
}
return true;
}
template<typename T>
bool writePod(QFile& oFile, const T& oValue)
{
return writeAll(oFile, reinterpret_cast<const char*>(&oValue),
static_cast<quint64>(sizeof(T)));
}
template<typename T>
bool readPod(QFile& oFile, T& oValue)
{
return readAll(oFile, reinterpret_cast<char*>(&oValue),
static_cast<quint64>(sizeof(T)));
}
bool hasMagic(const char* pActual, const char* pExpected)
{
for(int nIndex = 0; nIndex < 8; ++nIndex)
{
if(pActual[nIndex] != pExpected[nIndex])
{
return false;
}
}
return true;
}
rapidjson::Value toJsonString(const QString& sValue,
rapidjson::Document::AllocatorType& oAllocator)
{
const QByteArray baValue = sValue.toUtf8();
return rapidjson::Value(baValue.constData(),
static_cast<rapidjson::SizeType>(baValue.size()),
oAllocator);
}
void addFileReference(rapidjson::Value& oParent,
const char* pName,
const nmNumericalFileReference& oReference,
rapidjson::Document::AllocatorType& oAllocator)
{
rapidjson::Value oJson(rapidjson::kObjectType);
oJson.AddMember("Path", toJsonString(oReference.m_sRelativePath,
oAllocator), oAllocator);
oJson.AddMember("Length", oReference.m_nLength, oAllocator);
oJson.AddMember("Sha1", toJsonString(oReference.m_sSha1,
oAllocator), oAllocator);
oParent.AddMember(rapidjson::Value(pName, oAllocator).Move(),
oJson, oAllocator);
}
bool readFileReference(const rapidjson::Value& oParent,
const char* pName,
nmNumericalFileReference& oReference)
{
if(!oParent.IsObject() || !oParent.HasMember(pName) ||
!oParent[pName].IsObject())
{
return false;
}
const rapidjson::Value& oJson = oParent[pName];
if(!oJson.HasMember("Path") || !oJson["Path"].IsString() ||
!oJson.HasMember("Length") || !oJson["Length"].IsUint64() ||
!oJson.HasMember("Sha1") || !oJson["Sha1"].IsString())
{
return false;
}
oReference.m_sRelativePath = QString::fromUtf8(oJson["Path"].GetString());
oReference.m_nLength = oJson["Length"].GetUint64();
oReference.m_sSha1 = QString::fromLatin1(oJson["Sha1"].GetString());
return oReference.isValid();
}
void addDoubleVector(rapidjson::Value& oParent,
const char* pName,
const QVector<double>& vecValues,
rapidjson::Document::AllocatorType& oAllocator)
{
rapidjson::Value oArray(rapidjson::kArrayType);
for(int nIndex = 0; nIndex < vecValues.size(); ++nIndex)
{
oArray.PushBack(vecValues[nIndex], oAllocator);
}
oParent.AddMember(rapidjson::Value(pName, oAllocator).Move(),
oArray, oAllocator);
}
bool readDoubleVector(const rapidjson::Value& oParent,
const char* pName,
QVector<double>& vecValues)
{
if(!oParent.IsObject() || !oParent.HasMember(pName) ||
!oParent[pName].IsArray() ||
oParent[pName].Size() > static_cast<rapidjson::SizeType>(10000000))
{
return false;
}
const rapidjson::Value& oArray = oParent[pName];
QVector<double> vecLoaded;
vecLoaded.reserve(static_cast<int>(oArray.Size()));
for(rapidjson::SizeType nIndex = 0; nIndex < oArray.Size(); ++nIndex)
{
if(!oArray[nIndex].IsNumber() ||
!isFiniteValue(oArray[nIndex].GetDouble()))
{
return false;
}
vecLoaded.append(oArray[nIndex].GetDouble());
}
vecValues.swap(vecLoaded);
return true;
}
bool writeCurve(QFile& oFile, const QVector<QVector<double> >& vecCurve)
{
const quint32 nSeriesCount = static_cast<quint32>(vecCurve.size());
if(!writePod(oFile, nSeriesCount))
{
return false;
}
for(int nSeries = 0; nSeries < vecCurve.size(); ++nSeries)
{
const QVector<double>& vecValues = vecCurve[nSeries];
const quint64 nPointCount = static_cast<quint64>(vecValues.size());
quint64 nBytes = 0;
if(!safeMultiply(nPointCount, sizeof(double), nBytes) ||
!writePod(oFile, nPointCount) ||
(nBytes > 0 && !writeAll(oFile,
reinterpret_cast<const char*>(vecValues.constData()), nBytes)))
{
return false;
}
}
return true;
}
bool readCurve(QFile& oFile,
QVector<QVector<double> >* pCurve,
quint64& nTotalPoints)
{
quint32 nSeriesCount = 0;
if(!readPod(oFile, nSeriesCount) || nSeriesCount > g_nMaximumCurveSeries)
{
return false;
}
QVector<QVector<double> > vecLoaded;
if(pCurve != NULL)
{
vecLoaded.resize(static_cast<int>(nSeriesCount));
}
for(quint32 nSeries = 0; nSeries < nSeriesCount; ++nSeries)
{
quint64 nPointCount = 0;
quint64 nBytes = 0;
if(!readPod(oFile, nPointCount) ||
nPointCount > g_nMaximumCurvePoints ||
nTotalPoints > g_nMaximumCurvePoints - nPointCount ||
!safeMultiply(nPointCount, sizeof(double), nBytes) ||
nBytes > static_cast<quint64>(oFile.bytesAvailable()) ||
nPointCount > static_cast<quint64>(INT_MAX))
{
return false;
}
nTotalPoints += nPointCount;
if(pCurve == NULL)
{
if(!oFile.seek(oFile.pos() + static_cast<qint64>(nBytes)))
{
return false;
}
}
else
{
QVector<double>& vecValues = vecLoaded[static_cast<int>(nSeries)];
vecValues.resize(static_cast<int>(nPointCount));
if(nBytes > 0 && !readAll(oFile,
reinterpret_cast<char*>(vecValues.data()), nBytes))
{
return false;
}
}
}
if(pCurve != NULL)
{
pCurve->swap(vecLoaded);
}
return true;
}
bool readPressureFrames(const QString& sFilePath,
quint64 nExpectedCellCount,
quint32 nExpectedFrameCount,
nmPebiResultSnapshotBuilder* pBuilder,
QString* pError)
{
const QFileInfo oInfo(sFilePath);
if(!oInfo.isFile() || oInfo.size() < 0 ||
static_cast<quint64>(oInfo.size()) >
nmNumericalResultPersistence::maximumBinaryPayloadBytes())
{
return setError(pError, "PEBI pressure payload is missing or too large.");
}
QFile oFile(sFilePath);
if(!oFile.open(QIODevice::ReadOnly))
{
return setError(pError, "Cannot open PEBI pressure frame file.");
}
char aMagic[8] = { 0 };
quint32 nVersion = 0;
quint32 nEndian = 0;
quint64 nCellCount = 0;
quint32 nFrameCount = 0;
quint64 nFrameBytes = 0;
quint64 nPerFrameBytes = 0;
quint64 nPayloadBytes = 0;
quint64 nExpectedBytes = 0;
const quint64 nHeaderBytes = 8 + sizeof(quint32) + sizeof(quint32) +
sizeof(quint64) + sizeof(quint32);
bool bOk = readAll(oFile, aMagic, 8) &&
hasMagic(aMagic, g_aPressureMagic) &&
readPod(oFile, nVersion) && nVersion == g_nBinaryFormatVersion &&
readPod(oFile, nEndian) && nEndian == g_nEndianMarker &&
readPod(oFile, nCellCount) && nCellCount > 0 &&
nCellCount <= g_nMaximumCellCount &&
readPod(oFile, nFrameCount) && nFrameCount > 0 &&
nFrameCount <= g_nMaximumFrameCount &&
nCellCount == nExpectedCellCount &&
nFrameCount == nExpectedFrameCount &&
safeMultiply(nCellCount, sizeof(double), nFrameBytes) &&
safeAdd(sizeof(double), nFrameBytes, nPerFrameBytes) &&
safeMultiply(nPerFrameBytes, nFrameCount, nPayloadBytes) &&
safeAdd(nHeaderBytes, nPayloadBytes, nExpectedBytes) &&
nExpectedBytes == static_cast<quint64>(oInfo.size());
double dPreviousTime = 0.0;
for(quint32 nFrame = 0; bOk && nFrame < nFrameCount; ++nFrame)
{
double dTime = 0.0;
bOk = readPod(oFile, dTime) && isFiniteValue(dTime) &&
(nFrame == 0 || dTime > dPreviousTime);
if(!bOk)
{
break;
}
if(pBuilder == NULL)
{
bOk = oFile.seek(oFile.pos() + static_cast<qint64>(nFrameBytes));
}
else
{
vtkSmartPointer<vtkDoubleArray> pPressure =
vtkSmartPointer<vtkDoubleArray>::New();
pPressure->SetName("p");
pPressure->SetNumberOfComponents(1);
pPressure->SetNumberOfTuples(static_cast<vtkIdType>(nCellCount));
double* pValues = pPressure->GetPointer(0);
bOk = pValues != NULL &&
pPressure->GetNumberOfTuples() ==
static_cast<vtkIdType>(nCellCount) &&
readAll(oFile, reinterpret_cast<char*>(pValues),
nFrameBytes);
for(quint64 nCell = 0; bOk && nCell < nCellCount; ++nCell)
{
bOk = isFiniteValue(pValues[nCell]);
}
bOk = bOk && pBuilder->addPressureFrame(dTime, pPressure) &&
pPressure == NULL;
}
dPreviousTime = dTime;
}
bOk = bOk && oFile.atEnd();
oFile.close();
return bOk ? true : setError(pError, "PEBI pressure frame file is damaged.");
}
bool readWellCurves(const QString& sFilePath,
quint32 nExpectedWellCount,
QMap<QString, nmPebiResultWellCurves>* pCurves,
QString* pError)
{
const QFileInfo oInfo(sFilePath);
if(!oInfo.isFile() || oInfo.size() < 0 ||
static_cast<quint64>(oInfo.size()) >
nmNumericalResultPersistence::maximumBinaryPayloadBytes())
{
return setError(pError, "PEBI well curve payload is missing or too large.");
}
QFile oFile(sFilePath);
if(!oFile.open(QIODevice::ReadOnly))
{
return setError(pError, "Cannot open PEBI well curve file.");
}
char aMagic[8] = { 0 };
quint32 nVersion = 0;
quint32 nEndian = 0;
quint32 nWellCount = 0;
QMap<QString, nmPebiResultWellCurves> mapLoaded;
QSet<QString> setWellIds;
quint64 nTotalPoints = 0;
bool bOk = readAll(oFile, aMagic, 8) &&
hasMagic(aMagic, g_aCurvesMagic) &&
readPod(oFile, nVersion) && nVersion == g_nBinaryFormatVersion &&
readPod(oFile, nEndian) && nEndian == g_nEndianMarker &&
readPod(oFile, nWellCount) && nWellCount <= g_nMaximumWellCount &&
nWellCount == nExpectedWellCount;
for(quint32 nWell = 0; bOk && nWell < nWellCount; ++nWell)
{
quint32 nUuidBytes = 0;
bOk = readPod(oFile, nUuidBytes) && nUuidBytes > 0 &&
nUuidBytes <= g_nMaximumUuidBytes &&
nUuidBytes <= static_cast<quint32>(oFile.bytesAvailable());
QByteArray baUuid;
if(bOk)
{
baUuid.resize(static_cast<int>(nUuidBytes));
bOk = readAll(oFile, baUuid.data(), nUuidBytes);
}
const QString sUuid = QString::fromUtf8(baUuid);
bOk = bOk && !sUuid.isEmpty() && !setWellIds.contains(sUuid);
nmPebiResultWellCurves oCurves;
bOk = bOk && readCurve(oFile,
pCurves == NULL ? NULL : &oCurves.m_vecHistoryPressure,
nTotalPoints) &&
readCurve(oFile,
pCurves == NULL ? NULL : &oCurves.m_vecHistoryLogLog,
nTotalPoints) &&
readCurve(oFile,
pCurves == NULL ? NULL : &oCurves.m_vecHistorySemiLog,
nTotalPoints) &&
readCurve(oFile,
pCurves == NULL ? NULL : &oCurves.m_vecResultPressure,
nTotalPoints) &&
readCurve(oFile,
pCurves == NULL ? NULL : &oCurves.m_vecResultLogLog,
nTotalPoints) &&
readCurve(oFile,
pCurves == NULL ? NULL : &oCurves.m_vecResultSemiLog,
nTotalPoints);
if(bOk)
{
setWellIds.insert(sUuid);
if(pCurves != NULL)
{
mapLoaded.insert(sUuid, oCurves);
}
}
}
bOk = bOk && oFile.atEnd() && setWellIds.size() == nExpectedWellCount;
oFile.close();
if(!bOk)
{
return setError(pError, "PEBI well curve file is damaged.");
}
if(pCurves != NULL)
{
pCurves->swap(mapLoaded);
}
return true;
}
bool readRequiredInt(const rapidjson::Value& oParent,
const char* pName,
int& nValue)
{
if(!oParent.IsObject() || !oParent.HasMember(pName) ||
!oParent[pName].IsInt())
{
return false;
}
nValue = oParent[pName].GetInt();
return true;
}
bool readRequiredDouble(const rapidjson::Value& oParent,
const char* pName,
double& dValue)
{
if(!oParent.IsObject() || !oParent.HasMember(pName) ||
!oParent[pName].IsNumber() ||
!isFiniteValue(oParent[pName].GetDouble()))
{
return false;
}
dValue = oParent[pName].GetDouble();
return true;
}
bool readRequiredBool(const rapidjson::Value& oParent,
const char* pName,
bool& bValue)
{
if(!oParent.IsObject() || !oParent.HasMember(pName) ||
!oParent[pName].IsBool())
{
return false;
}
bValue = oParent[pName].GetBool();
return true;
}
bool parseSnapshotJson(
const QString& sSnapshotJsonPath,
rapidjson::Document& oDocument,
nmNumericalFileReference& oGridReference,
nmNumericalFileReference& oPressureReference,
nmNumericalFileReference& oCurvesReference,
quint64& nCellCount,
quint32& nFrameCount,
quint32& nWellCount,
QString* pError)
{
const QFileInfo oInfo(sSnapshotJsonPath);
if(!oInfo.isFile() || oInfo.size() <= 0 ||
static_cast<quint64>(oInfo.size()) > g_nMaximumSnapshotJsonBytes ||
!nmDataJsonTools::ReadDomFromFile(sSnapshotJsonPath, oDocument))
{
return setError(pError, "Snapshot.json is missing, too large or invalid.");
}
if(!oDocument.IsObject() ||
!oDocument.HasMember("SnapshotFormatVersion") ||
!oDocument["SnapshotFormatVersion"].IsInt() ||
oDocument["SnapshotFormatVersion"].GetInt() != 2 ||
!oDocument.HasMember("ResultCellCount") ||
!oDocument["ResultCellCount"].IsUint64() ||
!oDocument.HasMember("PressureFrameCount") ||
!oDocument["PressureFrameCount"].IsUint() ||
!oDocument.HasMember("WellCount") ||
!oDocument["WellCount"].IsUint() ||
!readFileReference(oDocument, "ResultGrid", oGridReference) ||
!readFileReference(oDocument, "PressureFrames", oPressureReference) ||
!readFileReference(oDocument, "WellCurves", oCurvesReference))
{
return setError(pError, "Snapshot.json header or file references are invalid.");
}
nCellCount = oDocument["ResultCellCount"].GetUint64();
nFrameCount = oDocument["PressureFrameCount"].GetUint();
nWellCount = oDocument["WellCount"].GetUint();
if(nCellCount == 0 || nCellCount > g_nMaximumCellCount ||
nFrameCount == 0 || nFrameCount > g_nMaximumFrameCount ||
nWellCount > g_nMaximumWellCount ||
oGridReference.m_sRelativePath == oPressureReference.m_sRelativePath ||
oGridReference.m_sRelativePath == oCurvesReference.m_sRelativePath ||
oPressureReference.m_sRelativePath == oCurvesReference.m_sRelativePath)
{
return setError(pError, "Snapshot.json declares invalid or duplicate payloads.");
}
return true;
}
}
bool nmPebiResultSnapshotSerializer::writePressureFrames(
const nmPebiResultSnapshot& oSnapshot,
const QString& sFilePath,
QString* pError)
{
QFile oFile(sFilePath);
if(!oFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
return setError(pError, "Cannot create PEBI pressure frame file.");
}
const quint64 nCellCount = static_cast<quint64>(
oSnapshot.m_pResultGrid->GetNumberOfCells());
const quint32 nFrameCount = static_cast<quint32>(
oSnapshot.m_vecPressureFrames.size());
bool bOk = writeAll(oFile, g_aPressureMagic, 8) &&
writePod(oFile, g_nBinaryFormatVersion) &&
writePod(oFile, g_nEndianMarker) &&
writePod(oFile, nCellCount) && writePod(oFile, nFrameCount);
quint64 nFrameBytes = 0;
bOk = bOk && safeMultiply(nCellCount, sizeof(double), nFrameBytes);
for(int nIndex = 0; bOk && nIndex < oSnapshot.m_vecPressureFrames.size();
++nIndex)
{
const nmPebiResultSnapshot::PressureFrame& oFrame =
oSnapshot.m_vecPressureFrames[nIndex];
double* pValues = oFrame.m_pPressure == NULL ? NULL :
oFrame.m_pPressure->GetPointer(0);
bOk = pValues != NULL && writePod(oFile, oFrame.m_dTime) &&
writeAll(oFile, reinterpret_cast<const char*>(pValues),
nFrameBytes);
}
bOk = bOk && oFile.flush();
oFile.close();
if(!bOk)
{
QFile::remove(sFilePath);
return setError(pError, "Cannot write complete PEBI pressure frame file.");
}
return true;
}
bool nmPebiResultSnapshotSerializer::writeWellCurves(
const nmPebiResultSnapshot& oSnapshot,
const QString& sFilePath,
QString* pError)
{
QFile oFile(sFilePath);
if(!oFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
return setError(pError, "Cannot create PEBI well curve file.");
}
const quint32 nWellCount = static_cast<quint32>(oSnapshot.m_vecWells.size());
bool bOk = writeAll(oFile, g_aCurvesMagic, 8) &&
writePod(oFile, g_nBinaryFormatVersion) &&
writePod(oFile, g_nEndianMarker) && writePod(oFile, nWellCount);
for(int nIndex = 0; bOk && nIndex < oSnapshot.m_vecWells.size(); ++nIndex)
{
const nmPebiResultWellSnapshot& oWell = oSnapshot.m_vecWells[nIndex];
const QByteArray baUuid = oWell.m_sWellInstanceId.toUtf8();
const quint32 nUuidBytes = static_cast<quint32>(baUuid.size());
bOk = nUuidBytes > 0 && nUuidBytes <= g_nMaximumUuidBytes &&
writePod(oFile, nUuidBytes) &&
writeAll(oFile, baUuid.constData(), nUuidBytes) &&
writeCurve(oFile, oWell.m_oCurves.m_vecHistoryPressure) &&
writeCurve(oFile, oWell.m_oCurves.m_vecHistoryLogLog) &&
writeCurve(oFile, oWell.m_oCurves.m_vecHistorySemiLog) &&
writeCurve(oFile, oWell.m_oCurves.m_vecResultPressure) &&
writeCurve(oFile, oWell.m_oCurves.m_vecResultLogLog) &&
writeCurve(oFile, oWell.m_oCurves.m_vecResultSemiLog);
}
bOk = bOk && oFile.flush();
oFile.close();
if(!bOk)
{
QFile::remove(sFilePath);
return setError(pError, "Cannot write complete PEBI well curve file.");
}
return true;
}
bool nmPebiResultSnapshotSerializer::save(
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot,
const QString& sWindowDirectory,
nmNumericalFileReference& oSnapshotJsonReference,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
if(pSnapshot.isNull() || !pSnapshot->m_bComplete ||
pSnapshot->m_pResultGrid == NULL)
{
return setError(pError, "Cannot save an incomplete PEBI result snapshot.");
}
try
{
const QString sSnapshotDirectory = QDir(sWindowDirectory).filePath("Snapshot");
if(!QDir().mkpath(sSnapshotDirectory))
{
return setError(pError, "Cannot create PEBI snapshot directory.");
}
const QString sGridPath = QDir(sWindowDirectory).filePath(
"Snapshot/ResultGrid.vtu");
const QString sPressurePath = QDir(sWindowDirectory).filePath(
"Snapshot/PressureFrames.bin");
const QString sCurvesPath = QDir(sWindowDirectory).filePath(
"Snapshot/WellCurves.bin");
const QString sJsonPath = QDir(sWindowDirectory).filePath(
"Snapshot/Snapshot.json");
nmNumericalFileReference oGridReference;
nmNumericalFileReference oPressureReference;
nmNumericalFileReference oCurvesReference;
if(!nmNumericalResultPersistence::writeGrid(
pSnapshot->m_pResultGrid, sGridPath, pError) ||
!nmNumericalResultPersistence::buildFileReference(
sGridPath, "Snapshot/ResultGrid.vtu", oGridReference) ||
!writePressureFrames(*pSnapshot, sPressurePath, pError) ||
!nmNumericalResultPersistence::buildFileReference(
sPressurePath, "Snapshot/PressureFrames.bin",
oPressureReference) ||
!writeWellCurves(*pSnapshot, sCurvesPath, pError) ||
!nmNumericalResultPersistence::buildFileReference(
sCurvesPath, "Snapshot/WellCurves.bin", oCurvesReference))
{
return false;
}
rapidjson::Document oDocument;
oDocument.SetObject();
rapidjson::Document::AllocatorType& oAllocator =
oDocument.GetAllocator();
// v2 增加井别元数据;压力帧和井曲线二进制格式保持不变。
oDocument.AddMember("SnapshotFormatVersion", 2, oAllocator);
oDocument.AddMember("SnapshotId",
toJsonString(pSnapshot->m_sSnapshotId, oAllocator), oAllocator);
oDocument.AddMember("GridInputRevision",
pSnapshot->m_nGridInputRevision, oAllocator);
oDocument.AddMember("ResultInputRevision",
pSnapshot->m_nResultInputRevision, oAllocator);
oDocument.AddMember("ResultCellCount",
static_cast<quint64>(pSnapshot->m_pResultGrid->GetNumberOfCells()),
oAllocator);
oDocument.AddMember("PressureFrameCount",
static_cast<unsigned>(pSnapshot->m_vecPressureFrames.size()),
oAllocator);
oDocument.AddMember("WellCount",
static_cast<unsigned>(pSnapshot->m_vecWells.size()), oAllocator);
oDocument.AddMember("ScalarMinimum", pSnapshot->m_dScalarMin, oAllocator);
oDocument.AddMember("ScalarMaximum", pSnapshot->m_dScalarMax, oAllocator);
addFileReference(oDocument, "ResultGrid", oGridReference, oAllocator);
addFileReference(oDocument, "PressureFrames", oPressureReference, oAllocator);
addFileReference(oDocument, "WellCurves", oCurvesReference, oAllocator);
rapidjson::Value oSlots(rapidjson::kArrayType);
for(int nIndex = 0; nIndex < pSnapshot->m_vecSolverSlots.size(); ++nIndex)
{
const nmPebiResultSolverSlot& oSlot =
pSnapshot->m_vecSolverSlots[nIndex];
rapidjson::Value oJson(rapidjson::kObjectType);
oJson.AddMember("SolverIndex", oSlot.m_nSolverIndex, oAllocator);
oJson.AddMember("EntryKind", static_cast<int>(oSlot.m_eEntryKind), oAllocator);
oJson.AddMember("WellInstanceId",
toJsonString(oSlot.m_sWellInstanceId, oAllocator), oAllocator);
oJson.AddMember("WellCode",
toJsonString(oSlot.m_sWellCode, oAllocator), oAllocator);
oJson.AddMember("WellType", static_cast<int>(oSlot.m_eWellType), oAllocator);
oJson.AddMember("WellCategory", static_cast<int>(
oSlot.m_eWellCategory), oAllocator);
oSlots.PushBack(oJson, oAllocator);
}
oDocument.AddMember("SolverSlots", oSlots, oAllocator);
rapidjson::Value oWells(rapidjson::kArrayType);
for(int nIndex = 0; nIndex < pSnapshot->m_vecWells.size(); ++nIndex)
{
const nmPebiResultWellSnapshot& oWell = pSnapshot->m_vecWells[nIndex];
rapidjson::Value oJson(rapidjson::kObjectType);
oJson.AddMember("WellInstanceId", toJsonString(
oWell.m_sWellInstanceId, oAllocator), oAllocator);
oJson.AddMember("WellCode", toJsonString(
oWell.m_sWellCode, oAllocator), oAllocator);
oJson.AddMember("WellName", toJsonString(
oWell.m_sWellName, oAllocator), oAllocator);
oJson.AddMember("WellType", static_cast<int>(oWell.m_eWellType), oAllocator);
oJson.AddMember("WellCategory", static_cast<int>(
oWell.m_eWellCategory), oAllocator);
oJson.AddMember("WellMode", static_cast<int>(oWell.m_eWellMode), oAllocator);
oJson.AddMember("X", oWell.m_oLocation.x(), oAllocator);
oJson.AddMember("Y", oWell.m_oLocation.y(), oAllocator);
oJson.AddMember("HasPerforation", oWell.m_bHasPerforation, oAllocator);
oJson.AddMember("HasSkin", oWell.m_bHasSkin, oAllocator);
oJson.AddMember("HasDfc", oWell.m_bHasDfc, oAllocator);
oJson.AddMember("Radius", oWell.m_dRadius, oAllocator);
oJson.AddMember("WellboreStorage", oWell.m_dWellboreStorage, oAllocator);
oJson.AddMember("Skin", oWell.m_dSkin, oAllocator);
oJson.AddMember("Dfc", oWell.m_dDfc, oAllocator);
oWells.PushBack(oJson, oAllocator);
}
oDocument.AddMember("Wells", oWells, oAllocator);
rapidjson::Value oDisplayWells(rapidjson::kArrayType);
for(int nIndex = 0;
nIndex < pSnapshot->m_listDisplayWellInstanceIds.size(); ++nIndex)
{
oDisplayWells.PushBack(toJsonString(
pSnapshot->m_listDisplayWellInstanceIds[nIndex], oAllocator),
oAllocator);
}
oDocument.AddMember("DisplayWellInstanceIds", oDisplayWells, oAllocator);
const nmPebiResultReservoirParameters& oReservoir =
pSnapshot->m_oReservoirParameters;
rapidjson::Value oReservoirJson(rapidjson::kObjectType);
oReservoirJson.AddMember("Pi", oReservoir.m_dInitialPressure, oAllocator);
oReservoirJson.AddMember("K", oReservoir.m_dPermeability, oAllocator);
oReservoirJson.AddMember("h", oReservoir.m_dThickness, oAllocator);
oReservoirJson.AddMember("phi", oReservoir.m_dPorosity, oAllocator);
oReservoirJson.AddMember("Cti", oReservoir.m_dTotalCompressibility, oAllocator);
oReservoirJson.AddMember("Cf", oReservoir.m_dRockCompressibility, oAllocator);
oReservoirJson.AddMember("Soi", oReservoir.m_dOilSaturation, oAllocator);
oReservoirJson.AddMember("Sgi", oReservoir.m_dGasSaturation, oAllocator);
oReservoirJson.AddMember("Swi", oReservoir.m_dWaterSaturation, oAllocator);
oDocument.AddMember("Reservoir", oReservoirJson, oAllocator);
const nmPebiResultPvtParameters& oPvt = pSnapshot->m_oPvtParameters;
rapidjson::Value oPvtJson(rapidjson::kObjectType);
oPvtJson.AddMember("HasBubblePoint", oPvt.m_bHasBubblePoint, oAllocator);
oPvtJson.AddMember("BubblePoint", oPvt.m_dBubblePoint, oAllocator);
oPvtJson.AddMember("ConstantBo", oPvt.m_dConstantBo, oAllocator);
oPvtJson.AddMember("ConstantMiuo", oPvt.m_dConstantMiuo, oAllocator);
oPvtJson.AddMember("ConstantBg", oPvt.m_dConstantBg, oAllocator);
oPvtJson.AddMember("ConstantMiug", oPvt.m_dConstantMiug, oAllocator);
oPvtJson.AddMember("ConstantBw", oPvt.m_dConstantBw, oAllocator);
oPvtJson.AddMember("ConstantMiuw", oPvt.m_dConstantMiuw, oAllocator);
#define NM_ADD_PVT_VECTOR(Name) addDoubleVector(oPvtJson, #Name, oPvt.m_vec##Name, oAllocator)
NM_ADD_PVT_VECTOR(Pressure); NM_ADD_PVT_VECTOR(Rso);
NM_ADD_PVT_VECTOR(Bo); NM_ADD_PVT_VECTOR(Co);
NM_ADD_PVT_VECTOR(Miuo); NM_ADD_PVT_VECTOR(Rouo);
NM_ADD_PVT_VECTOR(Rv); NM_ADD_PVT_VECTOR(Bg);
NM_ADD_PVT_VECTOR(Cg); NM_ADD_PVT_VECTOR(Miug);
NM_ADD_PVT_VECTOR(Roug); NM_ADD_PVT_VECTOR(Z);
NM_ADD_PVT_VECTOR(Rsw); NM_ADD_PVT_VECTOR(Bw);
NM_ADD_PVT_VECTOR(Cw); NM_ADD_PVT_VECTOR(Miuw);
NM_ADD_PVT_VECTOR(Rouw); NM_ADD_PVT_VECTOR(V);
NM_ADD_PVT_VECTOR(KkInitial); NM_ADD_PVT_VECTOR(CfCfInitial);
NM_ADD_PVT_VECTOR(So); NM_ADD_PVT_VECTOR(Kro);
NM_ADD_PVT_VECTOR(Sg); NM_ADD_PVT_VECTOR(Krg);
NM_ADD_PVT_VECTOR(Sw); NM_ADD_PVT_VECTOR(Krw);
#undef NM_ADD_PVT_VECTOR
oDocument.AddMember("Pvt", oPvtJson, oAllocator);
const nmPebiResultSolverSettings& oSettings =
pSnapshot->m_oSolverSettings;
rapidjson::Value oSettingsJson(rapidjson::kObjectType);
oSettingsJson.AddMember("SolverModelType", oSettings.m_nSolverModelType, oAllocator);
oSettingsJson.AddMember("PebiSolverType", oSettings.m_nPebiSolverType, oAllocator);
oSettingsJson.AddMember("OmpThreads", oSettings.m_nOmpThreads, oAllocator);
oSettingsJson.AddMember("IluReuseSteps", oSettings.m_nIluReuseSteps, oAllocator);
oSettingsJson.AddMember("GridControl", oSettings.m_dGridControl, oAllocator);
oSettingsJson.AddMember("HasTimeStepSettings", oSettings.m_bHasTimeStepSettings, oAllocator);
oSettingsJson.AddMember("TimeGrowthExponent", oSettings.m_dTimeGrowthExponent, oAllocator);
oSettingsJson.AddMember("MinDeltaT", oSettings.m_dMinDeltaT, oAllocator);
oSettingsJson.AddMember("MaxDeltaT", oSettings.m_dMaxDeltaT, oAllocator);
oDocument.AddMember("SolverSettings", oSettingsJson, oAllocator);
if(!nmDataJsonTools::WriteDomToFile(oDocument, sJsonPath) ||
!nmNumericalResultPersistence::buildFileReference(
sJsonPath, "Snapshot/Snapshot.json",
oSnapshotJsonReference) ||
!validate(sWindowDirectory, oSnapshotJsonReference, pError))
{
return false;
}
return true;
}
catch(const std::bad_alloc&)
{
return setError(pError, "Not enough memory to save PEBI result snapshot.");
}
}
bool nmPebiResultSnapshotSerializer::validate(
const QString& sWindowDirectory,
const nmNumericalFileReference& oSnapshotJsonReference,
QString* pError)
{
if(pError != NULL)
{
pError->clear();
}
QString sJsonPath;
if(!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oSnapshotJsonReference,
&sJsonPath, pError))
{
return false;
}
rapidjson::Document oDocument;
nmNumericalFileReference oGridReference;
nmNumericalFileReference oPressureReference;
nmNumericalFileReference oCurvesReference;
quint64 nCellCount = 0;
quint32 nFrameCount = 0;
quint32 nWellCount = 0;
if(!parseSnapshotJson(sJsonPath, oDocument, oGridReference,
oPressureReference, oCurvesReference, nCellCount,
nFrameCount, nWellCount, pError))
{
return false;
}
QString sGridPath;
QString sPressurePath;
QString sCurvesPath;
if(!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oGridReference, &sGridPath, pError) ||
!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oPressureReference, &sPressurePath, pError) ||
!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oCurvesReference, &sCurvesPath, pError) ||
!readPressureFrames(sPressurePath, nCellCount, nFrameCount,
NULL, pError) ||
!readWellCurves(sCurvesPath, nWellCount, NULL, pError))
{
return false;
}
return true;
}
bool nmPebiResultSnapshotSerializer::load(
const QString& sWindowDirectory,
const nmNumericalFileReference& oSnapshotJsonReference,
QSharedPointer<nmPebiResultSnapshot>& pCandidate,
QString* pError)
{
pCandidate.clear();
if(pError != NULL)
{
pError->clear();
}
try
{
QString sJsonPath;
if(!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oSnapshotJsonReference,
&sJsonPath, pError))
{
return false;
}
rapidjson::Document oDocument;
nmNumericalFileReference oGridReference;
nmNumericalFileReference oPressureReference;
nmNumericalFileReference oCurvesReference;
quint64 nCellCount = 0;
quint32 nFrameCount = 0;
quint32 nWellCount = 0;
if(!parseSnapshotJson(sJsonPath, oDocument, oGridReference,
oPressureReference, oCurvesReference, nCellCount,
nFrameCount, nWellCount, pError))
{
return false;
}
QString sGridPath;
QString sPressurePath;
QString sCurvesPath;
if(!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oGridReference, &sGridPath, pError) ||
!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oPressureReference, &sPressurePath, pError) ||
!nmNumericalResultPersistence::validateFileReference(
sWindowDirectory, oCurvesReference, &sCurvesPath, pError))
{
return false;
}
vtkSmartPointer<vtkUnstructuredGrid> pGrid;
QMap<QString, nmPebiResultWellCurves> mapCurves;
if(!nmNumericalResultPersistence::readGrid(sGridPath, pGrid, pError) ||
static_cast<quint64>(pGrid->GetNumberOfCells()) != nCellCount ||
!readWellCurves(sCurvesPath, nWellCount, &mapCurves, pError))
{
return setError(pError, pError != NULL && !pError->isEmpty()
? *pError : "PEBI snapshot grid or curves are invalid.");
}
nmPebiResultSnapshotBuilder oBuilder;
if(!oDocument.HasMember("SnapshotId") ||
!oDocument["SnapshotId"].IsString() ||
!oDocument.HasMember("GridInputRevision") ||
!oDocument["GridInputRevision"].IsUint64() ||
!oDocument.HasMember("ResultInputRevision") ||
!oDocument["ResultInputRevision"].IsUint64() ||
!oBuilder.setSnapshotId(QString::fromUtf8(
oDocument["SnapshotId"].GetString())) ||
!oBuilder.setInputRevisions(
oDocument["GridInputRevision"].GetUint64(),
oDocument["ResultInputRevision"].GetUint64()) ||
!oBuilder.takeResultGrid(pGrid) || pGrid != NULL ||
!readPressureFrames(sPressurePath, nCellCount, nFrameCount,
&oBuilder, pError))
{
return setError(pError, pError != NULL && !pError->isEmpty()
? *pError : "PEBI snapshot identity or pressure frames are invalid.");
}
double dScalarMinimum = 0.0;
double dScalarMaximum = 0.0;
if(!readRequiredDouble(oDocument, "ScalarMinimum", dScalarMinimum) ||
!readRequiredDouble(oDocument, "ScalarMaximum", dScalarMaximum) ||
!oBuilder.setScalarRange(dScalarMinimum, dScalarMaximum) ||
!oDocument.HasMember("SolverSlots") ||
!oDocument["SolverSlots"].IsArray() ||
oDocument["SolverSlots"].Size() > g_nMaximumWellCount + 100000u)
{
return setError(pError, "PEBI snapshot scalar range or solver slots are invalid.");
}
const rapidjson::Value& oSlots = oDocument["SolverSlots"];
for(rapidjson::SizeType nIndex = 0; nIndex < oSlots.Size(); ++nIndex)
{
const rapidjson::Value& oJson = oSlots[nIndex];
nmPebiResultSolverSlot oSlot;
int nEntryKind = 0;
int nWellType = 0;
int nWellCategory = 0;
if(!readRequiredInt(oJson, "SolverIndex", oSlot.m_nSolverIndex) ||
!readRequiredInt(oJson, "EntryKind", nEntryKind) ||
!readRequiredInt(oJson, "WellType", nWellType) ||
!readRequiredInt(oJson, "WellCategory", nWellCategory) ||
!oJson.HasMember("WellInstanceId") ||
!oJson["WellInstanceId"].IsString() ||
!oJson.HasMember("WellCode") || !oJson["WellCode"].IsString())
{
return setError(pError, "PEBI snapshot solver slot is invalid.");
}
oSlot.m_eEntryKind = static_cast<NM_SOLVER_ENTRY_KIND>(nEntryKind);
oSlot.m_eWellType = static_cast<NM_WELL_MODEL>(nWellType);
oSlot.m_eWellCategory = static_cast<NM_WELL_CATEGORY>(
nWellCategory);
oSlot.m_sWellInstanceId = QString::fromUtf8(
oJson["WellInstanceId"].GetString());
oSlot.m_sWellCode = QString::fromUtf8(oJson["WellCode"].GetString());
if(!oBuilder.addSolverSlot(oSlot))
{
return setError(pError, "Cannot restore PEBI snapshot solver slot.");
}
}
if(!oDocument.HasMember("Wells") || !oDocument["Wells"].IsArray() ||
oDocument["Wells"].Size() != nWellCount)
{
return setError(pError, "PEBI snapshot well directory is invalid.");
}
const rapidjson::Value& oWells = oDocument["Wells"];
QSet<QString> setJsonWellIds;
for(rapidjson::SizeType nIndex = 0; nIndex < oWells.Size(); ++nIndex)
{
const rapidjson::Value& oJson = oWells[nIndex];
nmPebiResultWellSnapshot oWell;
int nWellType = 0;
int nWellCategory = 0;
int nWellMode = 0;
if(!oJson.IsObject() ||
!oJson.HasMember("WellInstanceId") ||
!oJson["WellInstanceId"].IsString() ||
!oJson.HasMember("WellCode") || !oJson["WellCode"].IsString() ||
!oJson.HasMember("WellName") || !oJson["WellName"].IsString() ||
!readRequiredInt(oJson, "WellType", nWellType) ||
!readRequiredInt(oJson, "WellCategory", nWellCategory) ||
!readRequiredInt(oJson, "WellMode", nWellMode) ||
!readRequiredDouble(oJson, "X", oWell.m_oLocation.rx()) ||
!readRequiredDouble(oJson, "Y", oWell.m_oLocation.ry()) ||
!readRequiredBool(oJson, "HasPerforation", oWell.m_bHasPerforation) ||
!readRequiredBool(oJson, "HasSkin", oWell.m_bHasSkin) ||
!readRequiredBool(oJson, "HasDfc", oWell.m_bHasDfc) ||
!readRequiredDouble(oJson, "Radius", oWell.m_dRadius) ||
!readRequiredDouble(oJson, "WellboreStorage", oWell.m_dWellboreStorage) ||
!readRequiredDouble(oJson, "Skin", oWell.m_dSkin) ||
!readRequiredDouble(oJson, "Dfc", oWell.m_dDfc))
{
return setError(pError, "PEBI snapshot well metadata are invalid.");
}
oWell.m_sWellInstanceId = QString::fromUtf8(
oJson["WellInstanceId"].GetString());
oWell.m_sWellCode = QString::fromUtf8(oJson["WellCode"].GetString());
oWell.m_sWellName = QString::fromUtf8(oJson["WellName"].GetString());
oWell.m_eWellType = static_cast<NM_WELL_MODEL>(nWellType);
oWell.m_eWellCategory = static_cast<NM_WELL_CATEGORY>(
nWellCategory);
oWell.m_eWellMode = static_cast<NM_CASE_WELL_MODE>(nWellMode);
if(setJsonWellIds.contains(oWell.m_sWellInstanceId) ||
!mapCurves.contains(oWell.m_sWellInstanceId))
{
return setError(pError, "PEBI snapshot well curve mapping is invalid.");
}
setJsonWellIds.insert(oWell.m_sWellInstanceId);
oWell.m_oCurves = mapCurves.take(oWell.m_sWellInstanceId);
if(!oBuilder.takeWell(oWell))
{
return setError(pError, "Cannot restore PEBI snapshot well.");
}
}
if(!mapCurves.isEmpty())
{
return setError(pError, "PEBI well curve file contains unreferenced wells.");
}
if(!oDocument.HasMember("DisplayWellInstanceIds") ||
!oDocument["DisplayWellInstanceIds"].IsArray())
{
return setError(pError, "PEBI display well list is missing.");
}
QStringList listDisplayWellIds;
const rapidjson::Value& oDisplay = oDocument["DisplayWellInstanceIds"];
for(rapidjson::SizeType nIndex = 0; nIndex < oDisplay.Size(); ++nIndex)
{
if(!oDisplay[nIndex].IsString())
{
return setError(pError, "PEBI display well list is invalid.");
}
listDisplayWellIds.append(QString::fromUtf8(
oDisplay[nIndex].GetString()));
}
if(!oBuilder.setDisplayWellInstanceIds(listDisplayWellIds))
{
return setError(pError, "Cannot restore PEBI display well list.");
}
if(!oDocument.HasMember("Reservoir") ||
!oDocument["Reservoir"].IsObject())
{
return setError(pError, "PEBI reservoir snapshot is missing.");
}
const rapidjson::Value& oReservoirJson = oDocument["Reservoir"];
nmPebiResultReservoirParameters oReservoir;
if(!readRequiredDouble(oReservoirJson, "Pi", oReservoir.m_dInitialPressure) ||
!readRequiredDouble(oReservoirJson, "K", oReservoir.m_dPermeability) ||
!readRequiredDouble(oReservoirJson, "h", oReservoir.m_dThickness) ||
!readRequiredDouble(oReservoirJson, "phi", oReservoir.m_dPorosity) ||
!readRequiredDouble(oReservoirJson, "Cti", oReservoir.m_dTotalCompressibility) ||
!readRequiredDouble(oReservoirJson, "Cf", oReservoir.m_dRockCompressibility) ||
!readRequiredDouble(oReservoirJson, "Soi", oReservoir.m_dOilSaturation) ||
!readRequiredDouble(oReservoirJson, "Sgi", oReservoir.m_dGasSaturation) ||
!readRequiredDouble(oReservoirJson, "Swi", oReservoir.m_dWaterSaturation) ||
!oBuilder.setReservoirParameters(oReservoir))
{
return setError(pError, "PEBI reservoir snapshot is invalid.");
}
if(!oDocument.HasMember("Pvt") || !oDocument["Pvt"].IsObject())
{
return setError(pError, "PEBI PVT snapshot is missing.");
}
const rapidjson::Value& oPvtJson = oDocument["Pvt"];
nmPebiResultPvtParameters oPvt;
if(!readRequiredBool(oPvtJson, "HasBubblePoint", oPvt.m_bHasBubblePoint) ||
!readRequiredDouble(oPvtJson, "BubblePoint", oPvt.m_dBubblePoint) ||
!readRequiredDouble(oPvtJson, "ConstantBo", oPvt.m_dConstantBo) ||
!readRequiredDouble(oPvtJson, "ConstantMiuo", oPvt.m_dConstantMiuo) ||
!readRequiredDouble(oPvtJson, "ConstantBg", oPvt.m_dConstantBg) ||
!readRequiredDouble(oPvtJson, "ConstantMiug", oPvt.m_dConstantMiug) ||
!readRequiredDouble(oPvtJson, "ConstantBw", oPvt.m_dConstantBw) ||
!readRequiredDouble(oPvtJson, "ConstantMiuw", oPvt.m_dConstantMiuw))
{
return setError(pError, "PEBI PVT constants are invalid.");
}
#define NM_READ_PVT_VECTOR(Name) readDoubleVector(oPvtJson, #Name, oPvt.m_vec##Name)
if(!NM_READ_PVT_VECTOR(Pressure) || !NM_READ_PVT_VECTOR(Rso) ||
!NM_READ_PVT_VECTOR(Bo) || !NM_READ_PVT_VECTOR(Co) ||
!NM_READ_PVT_VECTOR(Miuo) || !NM_READ_PVT_VECTOR(Rouo) ||
!NM_READ_PVT_VECTOR(Rv) || !NM_READ_PVT_VECTOR(Bg) ||
!NM_READ_PVT_VECTOR(Cg) || !NM_READ_PVT_VECTOR(Miug) ||
!NM_READ_PVT_VECTOR(Roug) || !NM_READ_PVT_VECTOR(Z) ||
!NM_READ_PVT_VECTOR(Rsw) || !NM_READ_PVT_VECTOR(Bw) ||
!NM_READ_PVT_VECTOR(Cw) || !NM_READ_PVT_VECTOR(Miuw) ||
!NM_READ_PVT_VECTOR(Rouw) || !NM_READ_PVT_VECTOR(V) ||
!NM_READ_PVT_VECTOR(KkInitial) || !NM_READ_PVT_VECTOR(CfCfInitial) ||
!NM_READ_PVT_VECTOR(So) || !NM_READ_PVT_VECTOR(Kro) ||
!NM_READ_PVT_VECTOR(Sg) || !NM_READ_PVT_VECTOR(Krg) ||
!NM_READ_PVT_VECTOR(Sw) || !NM_READ_PVT_VECTOR(Krw) ||
!oBuilder.setPvtParameters(oPvt))
{
#undef NM_READ_PVT_VECTOR
return setError(pError, "PEBI PVT arrays are invalid.");
}
#undef NM_READ_PVT_VECTOR
if(!oDocument.HasMember("SolverSettings") ||
!oDocument["SolverSettings"].IsObject())
{
return setError(pError, "PEBI solver settings are missing.");
}
const rapidjson::Value& oSettingsJson = oDocument["SolverSettings"];
nmPebiResultSolverSettings oSettings;
if(!readRequiredInt(oSettingsJson, "SolverModelType", oSettings.m_nSolverModelType) ||
!readRequiredInt(oSettingsJson, "PebiSolverType", oSettings.m_nPebiSolverType) ||
!readRequiredInt(oSettingsJson, "OmpThreads", oSettings.m_nOmpThreads) ||
!readRequiredInt(oSettingsJson, "IluReuseSteps", oSettings.m_nIluReuseSteps) ||
!readRequiredDouble(oSettingsJson, "GridControl", oSettings.m_dGridControl) ||
!readRequiredBool(oSettingsJson, "HasTimeStepSettings", oSettings.m_bHasTimeStepSettings) ||
!readRequiredDouble(oSettingsJson, "TimeGrowthExponent", oSettings.m_dTimeGrowthExponent) ||
!readRequiredDouble(oSettingsJson, "MinDeltaT", oSettings.m_dMinDeltaT) ||
!readRequiredDouble(oSettingsJson, "MaxDeltaT", oSettings.m_dMaxDeltaT) ||
!oBuilder.setSolverSettings(oSettings))
{
return setError(pError, "PEBI solver settings are invalid.");
}
QString sBuilderError;
if(!oBuilder.finalize(&sBuilderError))
{
return setError(pError, sBuilderError);
}
pCandidate = oBuilder.takeCandidate();
if(pCandidate.isNull())
{
return setError(pError, "Cannot take the loaded PEBI snapshot candidate.");
}
return true;
}
catch(const std::bad_alloc&)
{
pCandidate.clear();
return setError(pError, "Not enough memory to load PEBI result snapshot.");
}
}