|
|
|
|
@ -3541,6 +3541,21 @@ bool nmDataAnalyzeManager::isWellAvailableForResult(
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 已有结果使用求解完成时冻结的井顺序。当前参数、选井或网格变化
|
|
|
|
|
// 不能把最后一套结果中的井从结果界面移除。
|
|
|
|
|
if(m_oNumericalAnalysisCase.hasResults()) {
|
|
|
|
|
const QVector<nmSolverWellRef> vecResultOrder =
|
|
|
|
|
m_oNumericalAnalysisCase.getResultSolverWellOrder();
|
|
|
|
|
for(int nIndex = 0; nIndex < vecResultOrder.size(); ++nIndex) {
|
|
|
|
|
const nmSolverWellRef& oWellRef = vecResultOrder[nIndex];
|
|
|
|
|
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well &&
|
|
|
|
|
oWellRef.m_sWellCode == sWellCode) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第一步:主分析井是结果界面的固定入口,即使当前处于关井段也必须保留。
|
|
|
|
|
if(sWellCode == getPrimaryWellCode()) {
|
|
|
|
|
return true;
|
|
|
|
|
@ -3586,6 +3601,12 @@ QVector<nmSolverWellRef> nmDataAnalyzeManager::getSolverWellOrder() const
|
|
|
|
|
return m_oNumericalAnalysisCase.getSolverWellOrder();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVector<nmSolverWellRef>
|
|
|
|
|
nmDataAnalyzeManager::getResultSolverWellOrder() const
|
|
|
|
|
{
|
|
|
|
|
return m_oNumericalAnalysisCase.getResultSolverWellOrder();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nmDataAnalyzeManager::setSolverWellOrder(
|
|
|
|
|
const QVector<nmSolverWellRef>& vecSolverWellOrder)
|
|
|
|
|
{
|
|
|
|
|
@ -3682,6 +3703,11 @@ bool nmDataAnalyzeManager::isPebiGridValid() const
|
|
|
|
|
return m_oNumericalAnalysisCase.isGridValid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool nmDataAnalyzeManager::hasPebiResults() const
|
|
|
|
|
{
|
|
|
|
|
return m_oNumericalAnalysisCase.hasResults();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void nmDataAnalyzeManager::setCurrentResultWellCode(
|
|
|
|
|
const QString& sWellCode)
|
|
|
|
|
{
|
|
|
|
|
@ -4078,8 +4104,11 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
|
|
|
|
|
NM_CASE_WELL_MODE eSavedPrimaryWellMode = NM_CaseWell_Observation;
|
|
|
|
|
bool bSavedIncludeOtherWells = false;
|
|
|
|
|
double dSavedPebiGridControl = 150.0;
|
|
|
|
|
bool bSavedResultsAvailable = false;
|
|
|
|
|
bool bSavedResultsCurrent = false;
|
|
|
|
|
QVector<nmCalculationWellRef> vecSavedIncludedWells;
|
|
|
|
|
QVector<nmSolverWellRef> vecSavedSolverOrder;
|
|
|
|
|
QVector<nmSolverWellRef> vecSavedResultSolverOrder;
|
|
|
|
|
|
|
|
|
|
// 第一步:版本 2 必须完整保存分析方案,缺字段时不再按旧项目规则猜测。
|
|
|
|
|
if(!doc.HasMember("NumericalAnalysisCase") ||
|
|
|
|
|
@ -4206,8 +4235,8 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
|
|
|
|
|
vecSavedIncludedWells.append(nmCalculationWellRef(sWellCode, eMode));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!setEffectiveWellCodes.contains(sSavedCurrentResultWellCode)) {
|
|
|
|
|
qWarning() << "Current result WellCode is not effective:"
|
|
|
|
|
if(!mapSavedWellTypes.contains(sSavedCurrentResultWellCode)) {
|
|
|
|
|
qWarning() << "Current result WellCode is absent from Wells:"
|
|
|
|
|
<< sSavedCurrentResultWellCode;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@ -4275,6 +4304,113 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第五步:新成果独立保存最后一次结果的井顺序和当前性。旧版本 2 文件
|
|
|
|
|
// 没有这些可选字段时,先以当前 SolverWellOrder 作为候选,最终由结果
|
|
|
|
|
// 二进制文件是否完整决定是否确实存在结果。
|
|
|
|
|
const bool bHasAnyResultStateField =
|
|
|
|
|
oCaseJson.HasMember("ResultsAvailable") ||
|
|
|
|
|
oCaseJson.HasMember("ResultsCurrent") ||
|
|
|
|
|
oCaseJson.HasMember("ResultSolverWellOrder");
|
|
|
|
|
const bool bHasCompleteResultState =
|
|
|
|
|
oCaseJson.HasMember("ResultsAvailable") &&
|
|
|
|
|
oCaseJson["ResultsAvailable"].IsBool() &&
|
|
|
|
|
oCaseJson.HasMember("ResultsCurrent") &&
|
|
|
|
|
oCaseJson["ResultsCurrent"].IsBool() &&
|
|
|
|
|
oCaseJson.HasMember("ResultSolverWellOrder") &&
|
|
|
|
|
oCaseJson["ResultSolverWellOrder"].IsArray();
|
|
|
|
|
if(bHasAnyResultStateField && !bHasCompleteResultState) {
|
|
|
|
|
qWarning() << "Numerical result state contains invalid fields:"
|
|
|
|
|
<< filePath;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bHasCompleteResultState) {
|
|
|
|
|
bSavedResultsAvailable =
|
|
|
|
|
oCaseJson["ResultsAvailable"].GetBool();
|
|
|
|
|
bSavedResultsCurrent = oCaseJson["ResultsCurrent"].GetBool();
|
|
|
|
|
if(bSavedResultsCurrent && !bSavedResultsAvailable) {
|
|
|
|
|
qWarning() << "Current result state has no available result:"
|
|
|
|
|
<< filePath;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QSet<QString> setResultWellCodes;
|
|
|
|
|
const rapidjson::Value& vecResultOrderJson =
|
|
|
|
|
oCaseJson["ResultSolverWellOrder"];
|
|
|
|
|
for(rapidjson::SizeType nIndex = 0;
|
|
|
|
|
nIndex < vecResultOrderJson.Size();
|
|
|
|
|
++nIndex) {
|
|
|
|
|
const rapidjson::Value& oOrderJson =
|
|
|
|
|
vecResultOrderJson[nIndex];
|
|
|
|
|
if(!oOrderJson.IsObject() ||
|
|
|
|
|
!oOrderJson.HasMember("WellCode") ||
|
|
|
|
|
!oOrderJson["WellCode"].IsString() ||
|
|
|
|
|
!oOrderJson.HasMember("WellType") ||
|
|
|
|
|
!oOrderJson["WellType"].IsInt() ||
|
|
|
|
|
!oOrderJson.HasMember("EntryKind") ||
|
|
|
|
|
!oOrderJson["EntryKind"].IsInt()) {
|
|
|
|
|
qWarning() << "ResultSolverWellOrder contains an invalid entry:"
|
|
|
|
|
<< filePath;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString sWellCode = QString::fromUtf8(
|
|
|
|
|
oOrderJson["WellCode"].GetString());
|
|
|
|
|
const NM_WELL_MODEL eWellType = static_cast<NM_WELL_MODEL>(
|
|
|
|
|
oOrderJson["WellType"].GetInt());
|
|
|
|
|
const NM_SOLVER_ENTRY_KIND eEntryKind =
|
|
|
|
|
static_cast<NM_SOLVER_ENTRY_KIND>(
|
|
|
|
|
oOrderJson["EntryKind"].GetInt());
|
|
|
|
|
|
|
|
|
|
if(eEntryKind == NM_SolverEntry_Well) {
|
|
|
|
|
const bool bSupportedResultWellType =
|
|
|
|
|
eWellType == NM_WELL_MODEL::Vertical_Well ||
|
|
|
|
|
eWellType == NM_WELL_MODEL::Vertical_Fractured_Well ||
|
|
|
|
|
eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well;
|
|
|
|
|
if(!mapSavedWellTypes.contains(sWellCode) ||
|
|
|
|
|
setResultWellCodes.contains(sWellCode) ||
|
|
|
|
|
!bSupportedResultWellType) {
|
|
|
|
|
qWarning() << "ResultSolverWellOrder contains an invalid real well:"
|
|
|
|
|
<< sWellCode;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
setResultWellCodes.insert(sWellCode);
|
|
|
|
|
} else if(eEntryKind == NM_SolverEntry_ManualFracture) {
|
|
|
|
|
if(!sWellCode.isEmpty() ||
|
|
|
|
|
eWellType != NM_WELL_MODEL::Unknow_Well) {
|
|
|
|
|
qWarning() << "Result manual fracture entry is malformed:"
|
|
|
|
|
<< filePath;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
qWarning() << "ResultSolverWellOrder contains an unknown entry kind:"
|
|
|
|
|
<< filePath;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vecSavedResultSolverOrder.append(nmSolverWellRef(
|
|
|
|
|
static_cast<int>(nIndex), eWellType,
|
|
|
|
|
sWellCode, eEntryKind));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bSavedResultsAvailable &&
|
|
|
|
|
(vecSavedResultSolverOrder.isEmpty() ||
|
|
|
|
|
!setResultWellCodes.contains(sSavedCurrentResultWellCode))) {
|
|
|
|
|
qWarning() << "Available result has no complete result well order:"
|
|
|
|
|
<< filePath;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
vecSavedResultSolverOrder = vecSavedSolverOrder;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!bSavedResultsAvailable &&
|
|
|
|
|
!setEffectiveWellCodes.contains(sSavedCurrentResultWellCode)) {
|
|
|
|
|
qWarning() << "Current result WellCode is not effective:"
|
|
|
|
|
<< sSavedCurrentResultWellCode;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 清空现有数据,确保从头加载新数据
|
|
|
|
|
foreach(nmDataFracture* pFractureData, m_vFractureData) {
|
|
|
|
|
delete pFractureData; // 释放堆上分配的 nmDataFracture 对象
|
|
|
|
|
@ -4662,6 +4798,10 @@ bool nmDataAnalyzeManager::ReadProjectData(const QString & filePath)
|
|
|
|
|
m_oNumericalAnalysisCase.setIncludedWells(vecSavedIncludedWells);
|
|
|
|
|
m_oNumericalAnalysisCase.setIncludeOtherWells(bSavedIncludeOtherWells);
|
|
|
|
|
m_oNumericalAnalysisCase.setSolverWellOrder(vecSavedSolverOrder);
|
|
|
|
|
m_oNumericalAnalysisCase.restoreResultState(
|
|
|
|
|
vecSavedResultSolverOrder,
|
|
|
|
|
bSavedResultsAvailable,
|
|
|
|
|
bSavedResultsCurrent);
|
|
|
|
|
m_oNumericalAnalysisCase.setCurrentResultWellCode(
|
|
|
|
|
sSavedCurrentResultWellCode.isEmpty()
|
|
|
|
|
? sSavedPrimaryWellCode : sSavedCurrentResultWellCode);
|
|
|
|
|
@ -4710,9 +4850,10 @@ bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath)
|
|
|
|
|
const QString sPrimaryWellCode = getPrimaryWellCode();
|
|
|
|
|
const QString sCurrentResultWellCode = getCurrentResultWellCode();
|
|
|
|
|
if(!setWellCodes.contains(sPrimaryWellCode) ||
|
|
|
|
|
!setWellCodes.contains(sCurrentResultWellCode) ||
|
|
|
|
|
!qIsFinite(getPebiGridControl()) ||
|
|
|
|
|
getPebiGridControl() <= 0.0) {
|
|
|
|
|
qWarning() << "Cannot save numerical project: invalid primary well or GridControl.";
|
|
|
|
|
qWarning() << "Cannot save numerical project: invalid primary/result well or GridControl.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -4730,12 +4871,6 @@ bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath)
|
|
|
|
|
}
|
|
|
|
|
setEffectiveWellCodes.insert(oWellRef.m_sWellCode);
|
|
|
|
|
}
|
|
|
|
|
if(!setEffectiveWellCodes.contains(sCurrentResultWellCode)) {
|
|
|
|
|
qWarning() << "Cannot save numerical project: result WellCode is not effective:"
|
|
|
|
|
<< sCurrentResultWellCode;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QSet<QString> setOrderedWellCodes;
|
|
|
|
|
QVector<nmSolverWellRef> vecValidatedSolverOrder = getSolverWellOrder();
|
|
|
|
|
// 求解器顺序只属于有效网格。网格已经失效时保存空顺序,
|
|
|
|
|
@ -4772,6 +4907,56 @@ bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 最后结果使用独立井顺序,不要求继续属于当前有效计算井,也不要求井型
|
|
|
|
|
// 仍与当前模型一致;但对应 WellCode 必须仍存在,才能保存单井曲线数据。
|
|
|
|
|
const bool bHasResults = m_oNumericalAnalysisCase.hasResults();
|
|
|
|
|
QVector<nmSolverWellRef> vecValidatedResultOrder;
|
|
|
|
|
if(bHasResults) {
|
|
|
|
|
vecValidatedResultOrder =
|
|
|
|
|
m_oNumericalAnalysisCase.getResultSolverWellOrder();
|
|
|
|
|
}
|
|
|
|
|
QSet<QString> setResultWellCodes;
|
|
|
|
|
for(int nIndex = 0;
|
|
|
|
|
nIndex < vecValidatedResultOrder.size();
|
|
|
|
|
++nIndex) {
|
|
|
|
|
const nmSolverWellRef& oWellRef = vecValidatedResultOrder[nIndex];
|
|
|
|
|
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
|
|
|
|
|
const bool bSupportedResultWellType =
|
|
|
|
|
oWellRef.m_eWellType == NM_WELL_MODEL::Vertical_Well ||
|
|
|
|
|
oWellRef.m_eWellType == NM_WELL_MODEL::Vertical_Fractured_Well ||
|
|
|
|
|
oWellRef.m_eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well;
|
|
|
|
|
if(!setWellCodes.contains(oWellRef.m_sWellCode) ||
|
|
|
|
|
setResultWellCodes.contains(oWellRef.m_sWellCode) ||
|
|
|
|
|
!bSupportedResultWellType) {
|
|
|
|
|
qWarning() << "Cannot save numerical project: invalid result WellCode:"
|
|
|
|
|
<< oWellRef.m_sWellCode;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
setResultWellCodes.insert(oWellRef.m_sWellCode);
|
|
|
|
|
} else if(oWellRef.m_eEntryKind == NM_SolverEntry_ManualFracture) {
|
|
|
|
|
if(!oWellRef.m_sWellCode.isEmpty() ||
|
|
|
|
|
oWellRef.m_eWellType != NM_WELL_MODEL::Unknow_Well) {
|
|
|
|
|
qWarning() << "Cannot save numerical project: malformed result fracture entry.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
qWarning() << "Cannot save numerical project: unknown result entry kind.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(bHasResults &&
|
|
|
|
|
(vecValidatedResultOrder.isEmpty() ||
|
|
|
|
|
!setResultWellCodes.contains(sCurrentResultWellCode))) {
|
|
|
|
|
qWarning() << "Cannot save numerical project: result order is incomplete.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!bHasResults &&
|
|
|
|
|
!setEffectiveWellCodes.contains(sCurrentResultWellCode)) {
|
|
|
|
|
qWarning() << "Cannot save numerical project: result WellCode is not effective:"
|
|
|
|
|
<< sCurrentResultWellCode;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第三步:基础身份校验通过后再构造 JSON,避免生成无法重新加载的半成品文件。
|
|
|
|
|
rapidjson::Document doc;
|
|
|
|
|
doc.SetObject(); // 根节点是对象
|
|
|
|
|
@ -5030,6 +5215,28 @@ bool nmDataAnalyzeManager::WriteProjectData(const QString & filePath)
|
|
|
|
|
vecSolverOrderJson.PushBack(oOrderJson, allocator);
|
|
|
|
|
}
|
|
|
|
|
oCaseJson.AddMember("SolverWellOrder", vecSolverOrderJson, allocator);
|
|
|
|
|
oCaseJson.AddMember("ResultsAvailable", bHasResults, allocator);
|
|
|
|
|
oCaseJson.AddMember("ResultsCurrent",
|
|
|
|
|
m_oNumericalAnalysisCase.areResultsCurrent(), allocator);
|
|
|
|
|
|
|
|
|
|
rapidjson::Value vecResultOrderJson(rapidjson::kArrayType);
|
|
|
|
|
for(int nIndex = 0;
|
|
|
|
|
nIndex < vecValidatedResultOrder.size();
|
|
|
|
|
++nIndex) {
|
|
|
|
|
const nmSolverWellRef& oWellRef = vecValidatedResultOrder[nIndex];
|
|
|
|
|
rapidjson::Value oOrderJson(rapidjson::kObjectType);
|
|
|
|
|
QByteArray baWellCode = oWellRef.m_sWellCode.toUtf8();
|
|
|
|
|
oOrderJson.AddMember("WellCode",
|
|
|
|
|
rapidjson::Value(baWellCode.constData(), allocator).Move(),
|
|
|
|
|
allocator);
|
|
|
|
|
oOrderJson.AddMember("WellType",
|
|
|
|
|
static_cast<int>(oWellRef.m_eWellType), allocator);
|
|
|
|
|
oOrderJson.AddMember("EntryKind",
|
|
|
|
|
static_cast<int>(oWellRef.m_eEntryKind), allocator);
|
|
|
|
|
vecResultOrderJson.PushBack(oOrderJson, allocator);
|
|
|
|
|
}
|
|
|
|
|
oCaseJson.AddMember("ResultSolverWellOrder",
|
|
|
|
|
vecResultOrderJson, allocator);
|
|
|
|
|
doc.AddMember("NumericalAnalysisCase", oCaseJson, allocator);
|
|
|
|
|
|
|
|
|
|
// 序列化 时间步数据
|
|
|
|
|
@ -5111,37 +5318,30 @@ bool nmDataAnalyzeManager::saveNmResult(QString sRstCode, iSubWndFitting* pSubWn
|
|
|
|
|
sTimeStepDataDirPath + "/PressureTimeSteps.bin";
|
|
|
|
|
QString sWellRstDataPath = sDir + "/WellRst";
|
|
|
|
|
|
|
|
|
|
// 第二步:无有效网格时不保存内存中残留的 VTK 对象和结果,并清理同目录旧文件。
|
|
|
|
|
if(!isPebiGridValid()) {
|
|
|
|
|
QFile::remove(sGridPath + "/CurrentGrid.vtu");
|
|
|
|
|
QFile::remove(sResultGridFilePath);
|
|
|
|
|
QFile::remove(sResultWellLocationPath);
|
|
|
|
|
QFile::remove(sTimeStepDataFilePath);
|
|
|
|
|
if(QDir(sWellRstDataPath).exists() &&
|
|
|
|
|
!clearDirectoryContents(sWellRstDataPath)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 方案标记为有效网格时,必须同时持有可写出的 VTK 网格。
|
|
|
|
|
if(!m_pVtkUnstructuredGrid) {
|
|
|
|
|
qWarning() << "Cannot save a valid PEBI grid: VTK grid is missing.";
|
|
|
|
|
// 第二步:当前网格和最后结果是两套独立载荷。当前网格失效时只删除
|
|
|
|
|
// CurrentGrid.vtu,不能连带删除已经成功生成的结果。
|
|
|
|
|
const bool bGridValid = isPebiGridValid();
|
|
|
|
|
const bool bHasResults = m_oNumericalAnalysisCase.hasResults();
|
|
|
|
|
if((bGridValid || bHasResults) &&
|
|
|
|
|
!this->ensureDirectoryExists(sGridPath)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!this->ensureDirectoryExists(sGridPath)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第三步:保存与 SolverWellOrder 同版本的实时网格。
|
|
|
|
|
if(!this->writeUnstructuredGridToFile(
|
|
|
|
|
m_pVtkUnstructuredGrid, sGridPath + "/CurrentGrid.vtu")) {
|
|
|
|
|
return false;
|
|
|
|
|
if(bGridValid) {
|
|
|
|
|
if(!m_pVtkUnstructuredGrid) {
|
|
|
|
|
qWarning() << "Cannot save a valid PEBI grid: VTK grid is missing.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!this->writeUnstructuredGridToFile(
|
|
|
|
|
m_pVtkUnstructuredGrid, sGridPath + "/CurrentGrid.vtu")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
QFile::remove(sGridPath + "/CurrentGrid.vtu");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第四步:网格有效但结果无效时只保存网格,并删除上一次求解的残留文件。
|
|
|
|
|
if(!m_oNumericalAnalysisCase.areResultsValid()) {
|
|
|
|
|
// 第三步:确实没有任何已生成结果时,才清理同目录中的旧结果文件。
|
|
|
|
|
if(!bHasResults) {
|
|
|
|
|
QFile::remove(sResultGridFilePath);
|
|
|
|
|
QFile::remove(sResultWellLocationPath);
|
|
|
|
|
QFile::remove(sTimeStepDataFilePath);
|
|
|
|
|
@ -5152,13 +5352,14 @@ bool nmDataAnalyzeManager::saveNmResult(QString sRstCode, iSubWndFitting* pSubWn
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 结果标记有效时,基础网格和时间步压力必须同时完整存在。
|
|
|
|
|
// 结果存在时,基础网格和时间步压力必须同时完整存在;是否仍对应当前
|
|
|
|
|
// 输入不影响保存和查看。
|
|
|
|
|
if(!m_pResultBaseGrid || m_mapTimeStepDataP.isEmpty()) {
|
|
|
|
|
qWarning() << "Cannot save valid PEBI results: result grid or time steps are missing.";
|
|
|
|
|
qWarning() << "Cannot save available PEBI results: result grid or time steps are missing.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第五步:保存场图基础网格、井位置和全部时间步压力。
|
|
|
|
|
// 第四步:保存场图基础网格、井位置和全部时间步压力。
|
|
|
|
|
if(!this->writeUnstructuredGridToFile(
|
|
|
|
|
m_pResultBaseGrid, sResultGridFilePath)) {
|
|
|
|
|
return false;
|
|
|
|
|
@ -5177,14 +5378,15 @@ bool nmDataAnalyzeManager::saveNmResult(QString sRstCode, iSubWndFitting* pSubWn
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第六步:先清空目标目录,再按 WellCode 保存每口真实计算井的结果。
|
|
|
|
|
// 第五步:先清空目标目录,再按最后结果的 WellCode 顺序保存单井结果。
|
|
|
|
|
if(!this->ensureDirectoryExists(sWellRstDataPath)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(!clearDirectoryContents(sWellRstDataPath)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
QVector<nmSolverWellRef> vecWells = this->getSolverWellOrder();
|
|
|
|
|
QVector<nmSolverWellRef> vecWells =
|
|
|
|
|
this->getResultSolverWellOrder();
|
|
|
|
|
|
|
|
|
|
// 遍历每口井,保存里面计算出来的数据
|
|
|
|
|
for(int wellIdx = 0; wellIdx < vecWells.size(); ++wellIdx) {
|
|
|
|
|
@ -5196,10 +5398,9 @@ bool nmDataAnalyzeManager::saveNmResult(QString sRstCode, iSubWndFitting* pSubWn
|
|
|
|
|
nmDataWellBase* pWellData =
|
|
|
|
|
this->findWellByCode(oWellRef.m_sWellCode);
|
|
|
|
|
|
|
|
|
|
if(pWellData) {
|
|
|
|
|
if(!this->saveWellCalRstData(sWellRstDataPath, pWellData)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(pWellData == nullptr ||
|
|
|
|
|
!this->saveWellCalRstData(sWellRstDataPath, pWellData)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -5214,6 +5415,15 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// JSON 中的当前性在读取当前网格前先保存下来;markGridBuilt() 会按运行时
|
|
|
|
|
// 语义把已有结果标为非当前,完整载荷读完后再恢复保存时的状态。
|
|
|
|
|
const bool bSavedResultsCurrent =
|
|
|
|
|
m_oNumericalAnalysisCase.areResultsCurrent();
|
|
|
|
|
const bool bDeclaredResultsAvailable =
|
|
|
|
|
m_oNumericalAnalysisCase.hasResults();
|
|
|
|
|
const QVector<nmSolverWellRef> vecResultSolverOrder =
|
|
|
|
|
getResultSolverWellOrder();
|
|
|
|
|
|
|
|
|
|
// 第一步:先检查分析方案、网格文件和结果文件是否构成完整的一组。
|
|
|
|
|
QString sCurrentGridPath = sLoadAnalDir + "/Grid/CurrentGrid.vtu";
|
|
|
|
|
QString sResultGridPath = sLoadAnalDir + "/Grid/ResultGrid.vtu";
|
|
|
|
|
@ -5233,6 +5443,7 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
|
|
|
|
|
const bool bHasAnyWellResultFile = !QDir(sWellRstDataPath).entryList(
|
|
|
|
|
listResultFilters, QDir::Files).isEmpty();
|
|
|
|
|
const bool bHasSolverWellOrder = !getSolverWellOrder().isEmpty();
|
|
|
|
|
const bool bHasResultSolverOrder = !vecResultSolverOrder.isEmpty();
|
|
|
|
|
const bool bHasAnyResultFile = bHasResultGridFile ||
|
|
|
|
|
bHasResultWellLocationFile || bHasTimeStepDataFile ||
|
|
|
|
|
bHasAnyWellResultFile;
|
|
|
|
|
@ -5245,10 +5456,15 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(bHasAnyResultFile &&
|
|
|
|
|
(!bHasCurrentGridFile || !bHasCompleteResultFiles)) {
|
|
|
|
|
(!bHasCompleteResultFiles || !bHasResultSolverOrder)) {
|
|
|
|
|
qWarning() << "PEBI result files are incomplete:" << sLoadAnalDir;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if(bDeclaredResultsAvailable && !bHasCompleteResultFiles) {
|
|
|
|
|
qWarning() << "Declared PEBI results are missing from disk:"
|
|
|
|
|
<< sLoadAnalDir;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第二步:读取当前网格;只有文件和求解器顺序同时存在时才标记有效。
|
|
|
|
|
if(bHasCurrentGridFile &&
|
|
|
|
|
@ -5264,7 +5480,8 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
|
|
|
|
|
m_oNumericalAnalysisCase.markGridBuilt();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第三步:读取完整求解结果;网格项目允许尚未执行过求解。
|
|
|
|
|
// 第三步:读取完整求解结果。结果使用自己的网格和井顺序,不要求
|
|
|
|
|
// CurrentGrid.vtu 仍然有效或仍然存在。
|
|
|
|
|
if(bHasResultGridFile &&
|
|
|
|
|
!this->readUnstructuredGridFromFile(m_pResultBaseGrid,
|
|
|
|
|
sResultGridPath)) {
|
|
|
|
|
@ -5302,12 +5519,13 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第五步:有完整场结果时,必须同时具备每口真实求解井的结果。
|
|
|
|
|
// SolverWellOrder 是 DLL 返回槽位与井对象之间的唯一映射;手工裂缝只占槽位,
|
|
|
|
|
// 不对应 Map 中的井对象,也没有独立的结果文件。
|
|
|
|
|
// ResultSolverWellOrder 是最后结果槽位与井对象之间的映射;手工裂缝只占
|
|
|
|
|
// 槽位,不对应 Map 中的井对象,也没有独立的结果文件。
|
|
|
|
|
if(bHasCompleteResultFiles) {
|
|
|
|
|
QVector<nmSolverWellRef> vecSolverOrder = getSolverWellOrder();
|
|
|
|
|
for(int nIndex = 0; nIndex < vecSolverOrder.size(); ++nIndex) {
|
|
|
|
|
const nmSolverWellRef& oWellRef = vecSolverOrder[nIndex];
|
|
|
|
|
for(int nIndex = 0;
|
|
|
|
|
nIndex < vecResultSolverOrder.size();
|
|
|
|
|
++nIndex) {
|
|
|
|
|
const nmSolverWellRef& oWellRef = vecResultSolverOrder[nIndex];
|
|
|
|
|
if(oWellRef.m_eEntryKind == NM_SolverEntry_ManualFracture) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
@ -5374,18 +5592,23 @@ bool nmDataAnalyzeManager::loadNmResult(QString sLoadAnalDir)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 结果井属于显示状态,但也必须在流量恢复后满足当前结果井规则。
|
|
|
|
|
// 二进制载荷完整后再登记结果存在。旧版本 2 项目没有独立结果状态时
|
|
|
|
|
// 也会在这里按实际文件恢复,当前输入状态则保守标为不一致。
|
|
|
|
|
m_oNumericalAnalysisCase.restoreResultState(
|
|
|
|
|
bHasCompleteResultFiles
|
|
|
|
|
? vecResultSolverOrder : QVector<nmSolverWellRef>(),
|
|
|
|
|
bHasCompleteResultFiles,
|
|
|
|
|
bHasCompleteResultFiles && bHasCurrentGridFile &&
|
|
|
|
|
bSavedResultsCurrent);
|
|
|
|
|
|
|
|
|
|
// 结果井属于显示状态;有历史结果时按结果井顺序校验,无结果时按当前方案。
|
|
|
|
|
if(!isWellAvailableForResult(getCurrentResultWellCode())) {
|
|
|
|
|
qWarning() << "Loaded result WellCode is not available:"
|
|
|
|
|
<< getCurrentResultWellCode();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bHasCurrentGridFile && bHasCompleteResultFiles) {
|
|
|
|
|
m_oNumericalAnalysisCase.markResultsAvailable();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_pVtkUnstructuredGrid != nullptr)
|
|
|
|
|
if (m_pVtkUnstructuredGrid != nullptr || m_pResultBaseGrid != nullptr)
|
|
|
|
|
m_bIsLoadData = true;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
@ -6017,9 +6240,10 @@ QMap<QString, QPointF> nmDataAnalyzeManager::getAllWellLocations() const
|
|
|
|
|
|
|
|
|
|
bool nmDataAnalyzeManager::saveWellLocations(const QString& filePath)
|
|
|
|
|
{
|
|
|
|
|
// 第一步:结果位置必须逐口覆盖求解器中的真实井,手工裂缝不对应 Map 井。
|
|
|
|
|
// 第一步:结果位置必须逐口覆盖最后结果中的真实井,手工裂缝不对应 Map 井。
|
|
|
|
|
QSet<QString> setExpectedWellCodes;
|
|
|
|
|
QVector<nmSolverWellRef> vecSolverOrder = getSolverWellOrder();
|
|
|
|
|
QVector<nmSolverWellRef> vecSolverOrder =
|
|
|
|
|
getResultSolverWellOrder();
|
|
|
|
|
for(int nIndex = 0; nIndex < vecSolverOrder.size(); ++nIndex) {
|
|
|
|
|
const nmSolverWellRef& oWellRef = vecSolverOrder[nIndex];
|
|
|
|
|
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
|
|
|
|
|
@ -6089,9 +6313,10 @@ bool nmDataAnalyzeManager::loadWellLocations(const QString& filePath)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第二步:加载结果必须与 SolverWellOrder 中的真实井 WellCode 完整对应。
|
|
|
|
|
// 第二步:加载结果必须与最后结果井顺序中的真实井 WellCode 完整对应。
|
|
|
|
|
QSet<QString> setExpectedWellCodes;
|
|
|
|
|
QVector<nmSolverWellRef> vecSolverOrder = getSolverWellOrder();
|
|
|
|
|
QVector<nmSolverWellRef> vecSolverOrder =
|
|
|
|
|
getResultSolverWellOrder();
|
|
|
|
|
for(int nIndex = 0; nIndex < vecSolverOrder.size(); ++nIndex) {
|
|
|
|
|
const nmSolverWellRef& oWellRef = vecSolverOrder[nIndex];
|
|
|
|
|
if(oWellRef.m_eEntryKind == NM_SolverEntry_Well) {
|
|
|
|
|
|