|
|
|
|
@ -77,6 +77,7 @@
|
|
|
|
|
#include <QFile>
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QUuid>
|
|
|
|
|
|
|
|
|
|
// 井基参数名 → 数据成员访问器的映射描述
|
|
|
|
|
struct WellParaDesc {
|
|
|
|
|
@ -4742,12 +4743,15 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
|
|
|
|
|
|
|
|
|
|
// 第二步:先从 JSON 井数组建立唯一 WellCode 到井型的索引,尚不修改当前内存数据。
|
|
|
|
|
QMap<QString, NM_WELL_MODEL> mapSavedWellTypes;
|
|
|
|
|
QSet<QString> setSavedWellInstanceIds;
|
|
|
|
|
const rapidjson::Value& vecWellsJson = doc["Wells"];
|
|
|
|
|
for(rapidjson::SizeType nIndex = 0; nIndex < vecWellsJson.Size(); ++nIndex) {
|
|
|
|
|
const rapidjson::Value& oWellJson = vecWellsJson[nIndex];
|
|
|
|
|
if(!oWellJson.IsObject() ||
|
|
|
|
|
!oWellJson.HasMember("WellCode") ||
|
|
|
|
|
!oWellJson["WellCode"].IsString() ||
|
|
|
|
|
!oWellJson.HasMember("WellInstanceId") ||
|
|
|
|
|
!oWellJson["WellInstanceId"].IsString() ||
|
|
|
|
|
!oWellJson.HasMember("WellType") ||
|
|
|
|
|
!oWellJson["WellType"].IsInt()) {
|
|
|
|
|
qWarning() << "Numerical project contains an invalid well entry:" << filePath;
|
|
|
|
|
@ -4756,6 +4760,11 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
|
|
|
|
|
|
|
|
|
|
const QString sWellCode = QString::fromUtf8(
|
|
|
|
|
oWellJson["WellCode"].GetString());
|
|
|
|
|
const QString sWellInstanceId = QString::fromUtf8(
|
|
|
|
|
oWellJson["WellInstanceId"].GetString());
|
|
|
|
|
const QUuid oWellInstanceUuid(sWellInstanceId);
|
|
|
|
|
const QString sNormalizedWellInstanceId =
|
|
|
|
|
oWellInstanceUuid.toString().remove('{').remove('}');
|
|
|
|
|
const NM_WELL_MODEL eWellType = static_cast<NM_WELL_MODEL>(
|
|
|
|
|
oWellJson["WellType"].GetInt());
|
|
|
|
|
const bool bSupportedWellType =
|
|
|
|
|
@ -4763,12 +4772,16 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
|
|
|
|
|
eWellType == NM_WELL_MODEL::Vertical_Fractured_Well ||
|
|
|
|
|
eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well;
|
|
|
|
|
if(sWellCode.isEmpty() || !bSupportedWellType ||
|
|
|
|
|
mapSavedWellTypes.contains(sWellCode)) {
|
|
|
|
|
qWarning() << "Numerical project contains an empty, duplicate or unsupported WellCode:"
|
|
|
|
|
<< sWellCode;
|
|
|
|
|
mapSavedWellTypes.contains(sWellCode) ||
|
|
|
|
|
oWellInstanceUuid.isNull() ||
|
|
|
|
|
sNormalizedWellInstanceId != sWellInstanceId ||
|
|
|
|
|
setSavedWellInstanceIds.contains(sWellInstanceId)) {
|
|
|
|
|
qWarning() << "Numerical project contains an invalid well identity:"
|
|
|
|
|
<< sWellCode << sWellInstanceId;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mapSavedWellTypes.insert(sWellCode, eWellType);
|
|
|
|
|
setSavedWellInstanceIds.insert(sWellInstanceId);
|
|
|
|
|
}
|
|
|
|
|
if(!mapSavedWellTypes.contains(sSavedPrimaryWellCode)) {
|
|
|
|
|
qWarning() << "Primary WellCode is absent from Wells:" << sSavedPrimaryWellCode;
|
|
|
|
|
@ -5391,17 +5404,24 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
|
|
|
|
|
// 反序列化函数没有返回值,因此必须再次核对实际创建的井目录。
|
|
|
|
|
// JSON 预校验目录与内存目录必须在 WellCode、井型和数量上完全一致。
|
|
|
|
|
QMap<QString, NM_WELL_MODEL> mapLoadedWellTypes;
|
|
|
|
|
QSet<QString> setLoadedWellInstanceIds;
|
|
|
|
|
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
|
|
|
|
|
nmDataWellBase* pWellData = m_vWellData[nIndex];
|
|
|
|
|
if(pWellData == nullptr || pWellData->getWellCode().isEmpty() ||
|
|
|
|
|
mapLoadedWellTypes.contains(pWellData->getWellCode())) {
|
|
|
|
|
mapLoadedWellTypes.contains(pWellData->getWellCode()) ||
|
|
|
|
|
!setSavedWellInstanceIds.contains(
|
|
|
|
|
pWellData->getWellInstanceId()) ||
|
|
|
|
|
setLoadedWellInstanceIds.contains(
|
|
|
|
|
pWellData->getWellInstanceId())) {
|
|
|
|
|
qWarning() << "Loaded numerical project contains an invalid WellCode:";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
mapLoadedWellTypes.insert(pWellData->getWellCode(),
|
|
|
|
|
pWellData->getWellType());
|
|
|
|
|
setLoadedWellInstanceIds.insert(pWellData->getWellInstanceId());
|
|
|
|
|
}
|
|
|
|
|
if(mapLoadedWellTypes != mapSavedWellTypes) {
|
|
|
|
|
if(mapLoadedWellTypes != mapSavedWellTypes ||
|
|
|
|
|
setLoadedWellInstanceIds != setSavedWellInstanceIds) {
|
|
|
|
|
qWarning() << "Loaded well directory differs from validated JSON:"
|
|
|
|
|
<< filePath;
|
|
|
|
|
return false;
|
|
|
|
|
@ -5475,15 +5495,25 @@ bool nmDataAnalyzeManager::WriteProjectData(
|
|
|
|
|
}
|
|
|
|
|
// 第一步:保存前校验 Map 井目录,持久化身份只允许非空且唯一的 WellCode。
|
|
|
|
|
QSet<QString> setWellCodes;
|
|
|
|
|
QSet<QString> setWellInstanceIds;
|
|
|
|
|
QMap<QString, NM_WELL_MODEL> mapWellTypes;
|
|
|
|
|
for(int nIndex = 0; nIndex < m_vWellData.size(); ++nIndex) {
|
|
|
|
|
nmDataWellBase* pWellData = m_vWellData[nIndex];
|
|
|
|
|
const QString sWellInstanceId = pWellData == nullptr
|
|
|
|
|
? QString() : pWellData->getWellInstanceId();
|
|
|
|
|
const QUuid oWellInstanceUuid(sWellInstanceId);
|
|
|
|
|
const QString sNormalizedWellInstanceId =
|
|
|
|
|
oWellInstanceUuid.toString().remove('{').remove('}');
|
|
|
|
|
if(pWellData == nullptr || pWellData->getWellCode().isEmpty() ||
|
|
|
|
|
setWellCodes.contains(pWellData->getWellCode())) {
|
|
|
|
|
qWarning() << "Cannot save numerical project: empty or duplicate WellCode.";
|
|
|
|
|
setWellCodes.contains(pWellData->getWellCode()) ||
|
|
|
|
|
oWellInstanceUuid.isNull() ||
|
|
|
|
|
sNormalizedWellInstanceId != sWellInstanceId ||
|
|
|
|
|
setWellInstanceIds.contains(sWellInstanceId)) {
|
|
|
|
|
qWarning() << "Cannot save numerical project: invalid well identity.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
setWellCodes.insert(pWellData->getWellCode());
|
|
|
|
|
setWellInstanceIds.insert(sWellInstanceId);
|
|
|
|
|
mapWellTypes.insert(pWellData->getWellCode(), pWellData->getWellType());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -6319,6 +6349,39 @@ bool nmDataAnalyzeManager::saveNmResult(QString sRstCode, iSubWndFitting* pSubWn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
// v3 先在独立候选对象上完成全部解析和业务校验。
|
|
|
|
|
// 任一步失败时只析构候选,当前窗口已经发布的数据保持不变。
|
|
|
|
|
nmDataAnalyzeManager oLoadedManager;
|
|
|
|
|
oLoadedManager.m_pOwnerFitting = m_pOwnerFitting;
|
|
|
|
|
oLoadedManager.initPvtParaFromSubFit();
|
|
|
|
|
if(!oLoadedManager.loadNmResultV3InPlace(sLoadAnalDir)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 井构造时不能依赖尚未发布的候选 Manager,完整读取后统一绑定储层。
|
|
|
|
|
for(int nIndex = 0; nIndex < oLoadedManager.m_vWellData.size(); ++nIndex) {
|
|
|
|
|
nmDataWellBase* pWell = oLoadedManager.m_vWellData[nIndex];
|
|
|
|
|
if(pWell != NULL) {
|
|
|
|
|
pWell->m_pReservoir = oLoadedManager.m_reservoirData;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
swapLoadedProjectState(oLoadedManager);
|
|
|
|
|
rebuildLoadedAttributeRegistry();
|
|
|
|
|
rebindPebiResultSnapshotToWells();
|
|
|
|
|
emit sigPebiResultSnapshotChanged();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch(const std::bad_alloc&) {
|
|
|
|
|
qWarning() << "Not enough memory to stage numerical v3 result.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool nmDataAnalyzeManager::loadNmResultV3InPlace(
|
|
|
|
|
const QString& sLoadAnalDir)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
// 外部框架仍传入旧的“成果根/窗口标识”逻辑路径;v3 只从根清单解析真实代次。
|
|
|
|
|
@ -6714,6 +6777,78 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nmDataAnalyzeManager::swapLoadedProjectState(
|
|
|
|
|
nmDataAnalyzeManager& oLoadedManager)
|
|
|
|
|
{
|
|
|
|
|
// 这里只交换项目数据所有权。窗口、Plot、后台任务和同步原语仍属于当前 Manager。
|
|
|
|
|
qSwap(m_vWellData, oLoadedManager.m_vWellData);
|
|
|
|
|
qSwap(m_vFaultData, oLoadedManager.m_vFaultData);
|
|
|
|
|
qSwap(m_vFractureData, oLoadedManager.m_vFractureData);
|
|
|
|
|
qSwap(m_vRegionData, oLoadedManager.m_vRegionData);
|
|
|
|
|
qSwap(m_vRegionMarkData, oLoadedManager.m_vRegionMarkData);
|
|
|
|
|
qSwap(m_vecLayers, oLoadedManager.m_vecLayers);
|
|
|
|
|
qSwap(m_vecPropertyInterpolationDataSets,
|
|
|
|
|
oLoadedManager.m_vecPropertyInterpolationDataSets);
|
|
|
|
|
|
|
|
|
|
qSwap(m_outlineData, oLoadedManager.m_outlineData);
|
|
|
|
|
qSwap(m_axisData, oLoadedManager.m_axisData);
|
|
|
|
|
qSwap(m_pAutomaticFittingData,
|
|
|
|
|
oLoadedManager.m_pAutomaticFittingData);
|
|
|
|
|
qSwap(m_reservoirData, oLoadedManager.m_reservoirData);
|
|
|
|
|
qSwap(m_pGeoRefData, oLoadedManager.m_pGeoRefData);
|
|
|
|
|
qSwap(m_pSensitiveData, oLoadedManager.m_pSensitiveData);
|
|
|
|
|
qSwap(m_pebiPvtPara, oLoadedManager.m_pebiPvtPara);
|
|
|
|
|
qSwap(m_pMixedResults, oLoadedManager.m_pMixedResults);
|
|
|
|
|
qSwap(m_pTimeStep, oLoadedManager.m_pTimeStep);
|
|
|
|
|
qSwap(m_pCurDataWell, oLoadedManager.m_pCurDataWell);
|
|
|
|
|
|
|
|
|
|
qSwap(m_eSolverModelType, oLoadedManager.m_eSolverModelType);
|
|
|
|
|
qSwap(m_nPebiSolverType, oLoadedManager.m_nPebiSolverType);
|
|
|
|
|
qSwap(m_nPebiOmpThreads, oLoadedManager.m_nPebiOmpThreads);
|
|
|
|
|
qSwap(m_nPebiIluReuseSteps, oLoadedManager.m_nPebiIluReuseSteps);
|
|
|
|
|
qSwap(m_oNumericalAnalysisCase,
|
|
|
|
|
oLoadedManager.m_oNumericalAnalysisCase);
|
|
|
|
|
qSwap(m_pPebiResultSnapshot,
|
|
|
|
|
oLoadedManager.m_pPebiResultSnapshot);
|
|
|
|
|
qSwap(m_sCurrentResultWellInstanceId,
|
|
|
|
|
oLoadedManager.m_sCurrentResultWellInstanceId);
|
|
|
|
|
|
|
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> pGrid =
|
|
|
|
|
m_pVtkUnstructuredGrid;
|
|
|
|
|
m_pVtkUnstructuredGrid = oLoadedManager.m_pVtkUnstructuredGrid;
|
|
|
|
|
oLoadedManager.m_pVtkUnstructuredGrid = pGrid;
|
|
|
|
|
pGrid = m_pResultBaseGrid;
|
|
|
|
|
m_pResultBaseGrid = oLoadedManager.m_pResultBaseGrid;
|
|
|
|
|
oLoadedManager.m_pResultBaseGrid = pGrid;
|
|
|
|
|
qSwap(m_mapTimeStepDataP, oLoadedManager.m_mapTimeStepDataP);
|
|
|
|
|
qSwap(m_mapWellLocations, oLoadedManager.m_mapWellLocations);
|
|
|
|
|
qSwap(m_dScalarRangeP[0], oLoadedManager.m_dScalarRangeP[0]);
|
|
|
|
|
qSwap(m_dScalarRangeP[1], oLoadedManager.m_dScalarRangeP[1]);
|
|
|
|
|
qSwap(m_bIsLoadData, oLoadedManager.m_bIsLoadData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nmDataAnalyzeManager::rebuildLoadedAttributeRegistry()
|
|
|
|
|
{
|
|
|
|
|
if(m_pAttrRegistry == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册表对象本身不替换,避免破坏已经建立的 UI 信号连接。
|
|
|
|
|
m_pAttrRegistry->clear();
|
|
|
|
|
if(m_reservoirData != NULL) {
|
|
|
|
|
m_pAttrRegistry->regAttr("h", &m_reservoirData->getThickness());
|
|
|
|
|
m_pAttrRegistry->regAttr("Pi", &m_reservoirData->getInitialPressure());
|
|
|
|
|
m_pAttrRegistry->regAttr("K", &m_reservoirData->getPermeability());
|
|
|
|
|
m_pAttrRegistry->regAttr("phi", &m_reservoirData->getPorosity());
|
|
|
|
|
m_pAttrRegistry->regAttr("Cti", &m_reservoirData->getCt());
|
|
|
|
|
m_pAttrRegistry->regAttr("Cf", &m_reservoirData->getCf());
|
|
|
|
|
m_pAttrRegistry->regAttr("Soi", &m_reservoirData->getSoi());
|
|
|
|
|
m_pAttrRegistry->regAttr("Swi", &m_reservoirData->getSwi());
|
|
|
|
|
}
|
|
|
|
|
syncWellAttrs();
|
|
|
|
|
syncGeometryAttrs();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool nmDataAnalyzeManager::ensureDirectoryExists(const QString & dirPath)
|
|
|
|
|
{
|
|
|
|
|
QDir dir(dirPath);
|
|
|
|
|
|