#include "nmPebiResultSnapshotSerializer.h" #include "nmDataJsonTools.h" #include "nmPebiResultSnapshot.h" #include "nmPebiResultSnapshotBuilder.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 int g_nSnapshotFormatVersion = 4; 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(0)) / nLeft) { return false; } nProduct = nLeft * nRight; return true; } bool safeAdd(quint64 nLeft, quint64 nRight, quint64& nSum) { if(nRight > (~static_cast(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(qMin( static_cast(g_nStreamChunkBytes), nBytes - nWritten)); if(oFile.write(pData + static_cast(nWritten), nChunk) != nChunk) { return false; } nWritten += static_cast(nChunk); } return true; } bool readAll(QFile& oFile, char* pData, quint64 nBytes) { quint64 nRead = 0; while(nRead < nBytes) { const qint64 nChunk = static_cast(qMin( static_cast(g_nStreamChunkBytes), nBytes - nRead)); if(oFile.read(pData + static_cast(nRead), nChunk) != nChunk) { return false; } nRead += static_cast(nChunk); } return true; } template bool writePod(QFile& oFile, const T& oValue) { return writeAll(oFile, reinterpret_cast(&oValue), static_cast(sizeof(T))); } template bool readPod(QFile& oFile, T& oValue) { return readAll(oFile, reinterpret_cast(&oValue), static_cast(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(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() || oJson["Sha1"].GetStringLength() != 40u) { return false; } oReference.m_sRelativePath = QString::fromUtf8( oJson["Path"].GetString(), static_cast(oJson["Path"].GetStringLength())); oReference.m_nLength = oJson["Length"].GetUint64(); oReference.m_sSha1 = QString::fromLatin1( oJson["Sha1"].GetString(), static_cast(oJson["Sha1"].GetStringLength())); return oReference.isValid(); } void addDoubleVector(rapidjson::Value& oParent, const char* pName, const QVector& 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& vecValues) { if(!oParent.IsObject() || !oParent.HasMember(pName) || !oParent[pName].IsArray() || oParent[pName].Size() > static_cast(10000000)) { return false; } const rapidjson::Value& oArray = oParent[pName]; QVector vecLoaded; vecLoaded.reserve(static_cast(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 >& vecCurve) { const quint32 nSeriesCount = static_cast(vecCurve.size()); if(!writePod(oFile, nSeriesCount)) { return false; } for(int nSeries = 0; nSeries < vecCurve.size(); ++nSeries) { const QVector& vecValues = vecCurve[nSeries]; const quint64 nPointCount = static_cast(vecValues.size()); quint64 nBytes = 0; if(!safeMultiply(nPointCount, sizeof(double), nBytes) || !writePod(oFile, nPointCount) || (nBytes > 0 && !writeAll(oFile, reinterpret_cast(vecValues.constData()), nBytes))) { return false; } } return true; } bool readCurve(QFile& oFile, QVector >* pCurve, quint64& nTotalPoints) { quint32 nSeriesCount = 0; if(!readPod(oFile, nSeriesCount) || nSeriesCount > g_nMaximumCurveSeries) { return false; } QVector > vecLoaded; if(pCurve != NULL) { vecLoaded.resize(static_cast(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(oFile.bytesAvailable()) || nPointCount > static_cast(INT_MAX)) { return false; } nTotalPoints += nPointCount; if(pCurve == NULL) { if(!oFile.seek(oFile.pos() + static_cast(nBytes))) { return false; } } else { QVector& vecValues = vecLoaded[static_cast(nSeries)]; vecValues.resize(static_cast(nPointCount)); if(nBytes > 0 && !readAll(oFile, reinterpret_cast(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(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(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(nFrameBytes)); } else { vtkSmartPointer pPressure = vtkSmartPointer::New(); pPressure->SetName("p"); pPressure->SetNumberOfComponents(1); pPressure->SetNumberOfTuples(static_cast(nCellCount)); double* pValues = pPressure->GetPointer(0); bOk = pValues != NULL && pPressure->GetNumberOfTuples() == static_cast(nCellCount) && readAll(oFile, reinterpret_cast(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* pCurves, QString* pError) { const QFileInfo oInfo(sFilePath); if(!oInfo.isFile() || oInfo.size() < 0 || static_cast(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 mapLoaded; QSet 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(oFile.bytesAvailable()); QByteArray baUuid; if(bOk) { baUuid.resize(static_cast(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(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() != g_nSnapshotFormatVersion || !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.compare( oPressureReference.m_sRelativePath, Qt::CaseInsensitive) == 0 || oGridReference.m_sRelativePath.compare( oCurvesReference.m_sRelativePath, Qt::CaseInsensitive) == 0 || oPressureReference.m_sRelativePath.compare( oCurvesReference.m_sRelativePath, Qt::CaseInsensitive) == 0) { 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( oSnapshot.m_pResultGrid->GetNumberOfCells()); const quint32 nFrameCount = static_cast( 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(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(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(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& 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(); // v4 在井目录中增加求解时冻结的完整流量制度,不兼容旧快照。 oDocument.AddMember("SnapshotFormatVersion", g_nSnapshotFormatVersion, 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(pSnapshot->m_pResultGrid->GetNumberOfCells()), oAllocator); oDocument.AddMember("PressureFrameCount", static_cast(pSnapshot->m_vecPressureFrames.size()), oAllocator); oDocument.AddMember("WellCount", static_cast(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(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(oSlot.m_eWellType), oAllocator); oJson.AddMember("WellCategory", static_cast( 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(oWell.m_eWellType), oAllocator); oJson.AddMember("WellCategory", static_cast( oWell.m_eWellCategory), oAllocator); oJson.AddMember("WellMode", static_cast(oWell.m_eWellMode), oAllocator); oJson.AddMember("GaugeInputSha1", toJsonString( oWell.m_sGaugeInputSha1, oAllocator), oAllocator); oJson.AddMember("FlowSchedulePhase", static_cast( oWell.m_eFlowSchedulePhase), oAllocator); oJson.AddMember("FlowSectionIndex", oWell.m_nFlowSectionIndex, oAllocator); oJson.AddMember("UseOilRate", oWell.m_bUseOilRate, oAllocator); oJson.AddMember("UseGasRate", oWell.m_bUseGasRate, oAllocator); oJson.AddMember("UseWaterRate", oWell.m_bUseWaterRate, oAllocator); addDoubleVector(oJson, "FlowDurations", oWell.m_vecFlowDurations, oAllocator); addDoubleVector(oJson, "OilRates", oWell.m_vecOilRates, oAllocator); addDoubleVector(oJson, "GasRates", oWell.m_vecGasRates, oAllocator); addDoubleVector(oJson, "WaterRates", oWell.m_vecWaterRates, 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::getPayloadRelativePaths( const QString& sWindowDirectory, const nmNumericalFileReference& oSnapshotJsonReference, QStringList& listRelativePaths, QString* pError) { listRelativePaths.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; } listRelativePaths << oGridReference.m_sRelativePath << oPressureReference.m_sRelativePath << oCurvesReference.m_sRelativePath; return true; } bool nmPebiResultSnapshotSerializer::load( const QString& sWindowDirectory, const nmNumericalFileReference& oSnapshotJsonReference, QSharedPointer& 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 pGrid; QMap mapCurves; if(!nmNumericalResultPersistence::readGrid(sGridPath, pGrid, pError) || static_cast(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(nEntryKind); oSlot.m_eWellType = static_cast(nWellType); oSlot.m_eWellCategory = static_cast( 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 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; int nFlowSchedulePhase = 0; if(!oJson.IsObject() || !oJson.HasMember("WellInstanceId") || !oJson["WellInstanceId"].IsString() || !oJson.HasMember("WellCode") || !oJson["WellCode"].IsString() || !oJson.HasMember("WellName") || !oJson["WellName"].IsString() || !oJson.HasMember("GaugeInputSha1") || !oJson["GaugeInputSha1"].IsString() || oJson["GaugeInputSha1"].GetStringLength() != 40u || !readRequiredInt(oJson, "WellType", nWellType) || !readRequiredInt(oJson, "WellCategory", nWellCategory) || !readRequiredInt(oJson, "WellMode", nWellMode) || !readRequiredInt(oJson, "FlowSchedulePhase", nFlowSchedulePhase) || !readRequiredInt(oJson, "FlowSectionIndex", oWell.m_nFlowSectionIndex) || !readRequiredBool(oJson, "UseOilRate", oWell.m_bUseOilRate) || !readRequiredBool(oJson, "UseGasRate", oWell.m_bUseGasRate) || !readRequiredBool(oJson, "UseWaterRate", oWell.m_bUseWaterRate) || !readDoubleVector(oJson, "FlowDurations", oWell.m_vecFlowDurations) || !readDoubleVector(oJson, "OilRates", oWell.m_vecOilRates) || !readDoubleVector(oJson, "GasRates", oWell.m_vecGasRates) || !readDoubleVector(oJson, "WaterRates", oWell.m_vecWaterRates) || !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_sGaugeInputSha1 = QString::fromLatin1( oJson["GaugeInputSha1"].GetString(), static_cast( oJson["GaugeInputSha1"].GetStringLength())); oWell.m_eWellType = static_cast(nWellType); oWell.m_eWellCategory = static_cast( nWellCategory); oWell.m_eWellMode = static_cast(nWellMode); oWell.m_eFlowSchedulePhase = static_cast( nFlowSchedulePhase); 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."); } }