|
|
|
@ -92,6 +92,7 @@ struct nmPebiSolverInputSnapshot
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nmPebiGridInputSnapshot m_oGridInput; ///< 网格、场景、井顺序和授权路径值快照。
|
|
|
|
nmPebiGridInputSnapshot m_oGridInput; ///< 网格、场景、井顺序和授权路径值快照。
|
|
|
|
|
|
|
|
QString m_sPrimaryWellCode; ///< 当次成功模型对应的分析主井。
|
|
|
|
nmPebiGridResult m_oGridResult; ///< 已有网格副本或后台新生成的局部网格。
|
|
|
|
nmPebiGridResult m_oGridResult; ///< 已有网格副本或后台新生成的局部网格。
|
|
|
|
/** @brief 在 DataManager 线程冻结的静态属性参考值、插值数据和授权路径。 */
|
|
|
|
/** @brief 在 DataManager 线程冻结的静态属性参考值、插值数据和授权路径。 */
|
|
|
|
nmPebiStaticPropertyCalculationInput m_oStaticPropertyInput;
|
|
|
|
nmPebiStaticPropertyCalculationInput m_oStaticPropertyInput;
|
|
|
|
@ -210,6 +211,28 @@ bool isFiniteSolverNumber(double value)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool savedPseudoPressure(const nmDataBinaryTools::NM_PEBI_SCENE& scene,
|
|
|
|
|
|
|
|
double pressure, double& result)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 气井分析使用求解时的拟压力表,避免当前编辑改写全局转换器配置。
|
|
|
|
|
|
|
|
const std::vector<double>& p = scene.PVT.pseudoPressureP;
|
|
|
|
|
|
|
|
const std::vector<double>& ps = scene.PVT.pseudoPressurePs;
|
|
|
|
|
|
|
|
if(p.size() < 2 || p.size() != ps.size() || !isFiniteSolverNumber(pressure)) return false;
|
|
|
|
|
|
|
|
const bool ascending = p[1] > p[0];
|
|
|
|
|
|
|
|
size_t first = 0, last = p.size();
|
|
|
|
|
|
|
|
while(first < last) {
|
|
|
|
|
|
|
|
const size_t mid = first + (last - first) / 2;
|
|
|
|
|
|
|
|
if(ascending ? p[mid] < pressure : p[mid] > pressure) first = mid + 1;
|
|
|
|
|
|
|
|
else last = mid;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const size_t right = (std::max)(size_t(1), (std::min)(first, p.size() - 1));
|
|
|
|
|
|
|
|
const double span = p[right] - p[right - 1];
|
|
|
|
|
|
|
|
if(span == 0.0) return false;
|
|
|
|
|
|
|
|
// 与现有离线计算一致,区间内线性插值,超出范围沿端点外推。
|
|
|
|
|
|
|
|
result = ps[right - 1] + (pressure - p[right - 1]) / span * (ps[right] - ps[right - 1]);
|
|
|
|
|
|
|
|
return isFiniteSolverNumber(result);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 旧的双对数曲线 DLL 仍是单相接口,只在结果后处理时取输入捕获阶段
|
|
|
|
// 旧的双对数曲线 DLL 仍是单相接口,只在结果后处理时取输入捕获阶段
|
|
|
|
// 冻结的制度参考相。PEBI 主求解仍在场景中接收油、气、水三相全部流量。
|
|
|
|
// 冻结的制度参考相。PEBI 主求解仍在场景中接收油、气、水三相全部流量。
|
|
|
|
const QVector<double>* referenceRatesOf(
|
|
|
|
const QVector<double>* referenceRatesOf(
|
|
|
|
@ -382,6 +405,7 @@ bool captureResultParameters(
|
|
|
|
|
|
|
|
|
|
|
|
nmPebiResultPvtParameters& oPvt =
|
|
|
|
nmPebiResultPvtParameters& oPvt =
|
|
|
|
oSnapshot.m_oResultPvtParameters;
|
|
|
|
oSnapshot.m_oResultPvtParameters;
|
|
|
|
|
|
|
|
oSnapshot.m_sPrimaryWellCode = pDataManager->getPrimaryWellCode();
|
|
|
|
oPvt.m_dConstantBo = pReservoir->getBo().getValue().toDouble();
|
|
|
|
oPvt.m_dConstantBo = pReservoir->getBo().getValue().toDouble();
|
|
|
|
oPvt.m_dConstantMiuo = pReservoir->getMiuo().getValue().toDouble();
|
|
|
|
oPvt.m_dConstantMiuo = pReservoir->getMiuo().getValue().toDouble();
|
|
|
|
oPvt.m_dConstantBg = pReservoir->getBg().getValue().toDouble();
|
|
|
|
oPvt.m_dConstantBg = pReservoir->getBg().getValue().toDouble();
|
|
|
|
@ -634,7 +658,8 @@ nmCalculationDllPebiSolverTask::nmCalculationDllPebiSolverTask(
|
|
|
|
nmDataAnalyzeManager* pDataManager,
|
|
|
|
nmDataAnalyzeManager* pDataManager,
|
|
|
|
const QString& sAutoFitTargetWellName,
|
|
|
|
const QString& sAutoFitTargetWellName,
|
|
|
|
QObject *parent,
|
|
|
|
QObject *parent,
|
|
|
|
bool bDeferManualSnapshot):
|
|
|
|
bool bDeferManualSnapshot,
|
|
|
|
|
|
|
|
bool bUseSolvedInput):
|
|
|
|
QThread(parent),
|
|
|
|
QThread(parent),
|
|
|
|
m_sPostprocessingDir(sPostprocessingDir),
|
|
|
|
m_sPostprocessingDir(sPostprocessingDir),
|
|
|
|
m_pDataManager(pDataManager != nullptr
|
|
|
|
m_pDataManager(pDataManager != nullptr
|
|
|
|
@ -665,7 +690,9 @@ nmCalculationDllPebiSolverTask::nmCalculationDllPebiSolverTask(
|
|
|
|
m_bManagerUseActive = true;
|
|
|
|
m_bManagerUseActive = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(bDeferManualSnapshot && sAutoFitTargetWellName.isEmpty()) {
|
|
|
|
if(bUseSolvedInput) {
|
|
|
|
|
|
|
|
m_bInputSnapshotValid = restoreSolvedInput();
|
|
|
|
|
|
|
|
} else if(bDeferManualSnapshot && sAutoFitTargetWellName.isEmpty()) {
|
|
|
|
// 手工求解由界面事件循环分批冻结值输入;start() 必须等状态释放后再调用。
|
|
|
|
// 手工求解由界面事件循环分批冻结值输入;start() 必须等状态释放后再调用。
|
|
|
|
m_pManualCaptureState = new nmPebiManualCaptureState();
|
|
|
|
m_pManualCaptureState = new nmPebiManualCaptureState();
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
@ -916,12 +943,15 @@ nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivit
|
|
|
|
task->m_pSensitivityResult->m_variables = frozenSampling.getVariables();
|
|
|
|
task->m_pSensitivityResult->m_variables = frozenSampling.getVariables();
|
|
|
|
task->m_pSensitivityResult->m_sWellCode = task->m_sAutoFitTargetWellCode;
|
|
|
|
task->m_pSensitivityResult->m_sWellCode = task->m_sAutoFitTargetWellCode;
|
|
|
|
if(m_pDataManager != nullptr) {
|
|
|
|
if(m_pDataManager != nullptr) {
|
|
|
|
nmDataWellBase* well = m_pDataManager->findWellByCode(task->m_sAutoFitTargetWellCode);
|
|
|
|
const QSharedPointer<const nmPebiResultSnapshot> solved = m_pDataManager->getPebiResultSnapshot();
|
|
|
|
if(well != nullptr) {
|
|
|
|
for(int i = 0; !solved.isNull() && i < solved->getWellCount(); ++i) {
|
|
|
|
task->m_pSensitivityResult->m_sWellName = well->getWellName();
|
|
|
|
const nmPebiResultWellSnapshot* well = solved->getWellAt(i);
|
|
|
|
task->m_pSensitivityResult->m_vecHistoryLogLog = well->getHistoryLogLog();
|
|
|
|
if(well->m_sWellCode != task->m_sAutoFitTargetWellCode) continue;
|
|
|
|
// 复用原模型已有数值解作为对比基准,不增加一次求解或跟随后续结果变化。
|
|
|
|
// 历史和基准曲线必须与计算输入属于同一次求解。
|
|
|
|
task->m_pSensitivityResult->m_vecBaseLogLog = well->getResultLogLog();
|
|
|
|
task->m_pSensitivityResult->m_sWellName = well->m_sWellName;
|
|
|
|
|
|
|
|
task->m_pSensitivityResult->m_vecHistoryLogLog = well->m_oCurves.m_vecHistoryLogLog;
|
|
|
|
|
|
|
|
task->m_pSensitivityResult->m_vecBaseLogLog = well->m_oCurves.m_vecResultLogLog;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return task.take();
|
|
|
|
return task.take();
|
|
|
|
@ -1346,6 +1376,20 @@ void nmCalculationDllPebiSolverTask::releaseDataManagerUse()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool nmCalculationDllPebiSolverTask::restoreSolvedInput()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 复制最后成功求解的输入,不再从当前编辑模型重新捕获。
|
|
|
|
|
|
|
|
if(!m_pDataManager || QThread::currentThread() != m_pDataManager->thread()) return false;
|
|
|
|
|
|
|
|
const QSharedPointer<const nmPebiResultSnapshot> solved = m_pDataManager->getPebiResultSnapshot();
|
|
|
|
|
|
|
|
if(solved.isNull() || solved->getSensitivityInput().isNull())
|
|
|
|
|
|
|
|
return fail(tr("Regenerate the model before running sensitivity."));
|
|
|
|
|
|
|
|
*m_pInputSnapshot = *solved->getSensitivityInput();
|
|
|
|
|
|
|
|
m_nGridInputRevision = solved->getGridInputRevision();
|
|
|
|
|
|
|
|
m_nResultInputRevision = solved->getResultInputRevision();
|
|
|
|
|
|
|
|
m_sAutoFitTargetWellCode = m_pInputSnapshot->m_sPrimaryWellCode;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmCalculationDllPebiSolverTask::captureInputSnapshot(
|
|
|
|
bool nmCalculationDllPebiSolverTask::captureInputSnapshot(
|
|
|
|
const QString& sAutoFitTargetWellName)
|
|
|
|
const QString& sAutoFitTargetWellName)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -1869,11 +1913,13 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
|
|
|
|
// p1.pw 是求解器返回的原始井底压力, 后续仍按 MPa 保存到 vvecPressure.
|
|
|
|
// p1.pw 是求解器返回的原始井底压力, 后续仍按 MPa 保存到 vvecPressure.
|
|
|
|
// 只有气单相变化 PVT 模型的双对数和半对数计算需要改用拟压力.
|
|
|
|
// 只有气单相变化 PVT 模型的双对数和半对数计算需要改用拟压力.
|
|
|
|
const bool usePseudoPressure = (modelType == static_cast<int>(SMT_Gas_VariablePvt));
|
|
|
|
const bool usePseudoPressure = (modelType == static_cast<int>(SMT_Gas_VariablePvt));
|
|
|
|
CalPseudoPressure calPseudoPressure = usePseudoPressure
|
|
|
|
const bool useSavedPseudoPressure = usePseudoPressure &&
|
|
|
|
|
|
|
|
m_pInputSnapshot->m_oSensitivityBaseline.m_nTarget >= 0;
|
|
|
|
|
|
|
|
CalPseudoPressure calPseudoPressure = usePseudoPressure && !useSavedPseudoPressure
|
|
|
|
? getPseudoPressureConverter()
|
|
|
|
? getPseudoPressureConverter()
|
|
|
|
: nullptr;
|
|
|
|
: nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
if(usePseudoPressure && calPseudoPressure == nullptr) {
|
|
|
|
if(usePseudoPressure && !useSavedPseudoPressure && calPseudoPressure == nullptr) {
|
|
|
|
qWarning() << "Failed to load the gas pseudo-pressure converter.";
|
|
|
|
qWarning() << "Failed to load the gas pseudo-pressure converter.";
|
|
|
|
return fail(tr("Failed to load the gas pseudo-pressure converter."));
|
|
|
|
return fail(tr("Failed to load the gas pseudo-pressure converter."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -2011,8 +2057,11 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
|
|
|
|
double pressureForLog = p1.pw[wellIdx][i];
|
|
|
|
double pressureForLog = p1.pw[wellIdx][i];
|
|
|
|
if(usePseudoPressure
|
|
|
|
if(usePseudoPressure
|
|
|
|
&& (!isFiniteSolverNumber(pressureForLog)
|
|
|
|
&& (!isFiniteSolverNumber(pressureForLog)
|
|
|
|
// -1 与主界面调用一致, 表示使用 configPsAbouts() 配置的当前模式.
|
|
|
|
// 敏感性任务只查求解时保存的表,不能读取已被编辑或其他窗口改写的全局配置。
|
|
|
|
|| !calPseudoPressure(pressureForLog, pressureForLog, -1)
|
|
|
|
|| !(useSavedPseudoPressure
|
|
|
|
|
|
|
|
? savedPseudoPressure(
|
|
|
|
|
|
|
|
m_pInputSnapshot->m_oGridInput.m_oScene, pressureForLog, pressureForLog)
|
|
|
|
|
|
|
|
: calPseudoPressure(pressureForLog, pressureForLog, -1))
|
|
|
|
|| !isFiniteSolverNumber(pressureForLog))) {
|
|
|
|
|| !isFiniteSolverNumber(pressureForLog))) {
|
|
|
|
qWarning() << "Failed to convert gas pressure to pseudo-pressure:"
|
|
|
|
qWarning() << "Failed to convert gas pressure to pseudo-pressure:"
|
|
|
|
<< p1.pw[wellIdx][i]
|
|
|
|
<< p1.pw[wellIdx][i]
|
|
|
|
@ -2261,6 +2310,17 @@ bool nmCalculationDllPebiSolverTask::buildPebiResultSnapshotCandidate(
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
nmPebiResultSnapshotBuilder oBuilder;
|
|
|
|
nmPebiResultSnapshotBuilder oBuilder;
|
|
|
|
|
|
|
|
// 复用现有输入结构,在转移井制度和曲线前留一份只读副本,随成功结果发布。
|
|
|
|
|
|
|
|
QSharedPointer<nmPebiSolverInputSnapshot> sensitivityInput(
|
|
|
|
|
|
|
|
new nmPebiSolverInputSnapshot(*m_pInputSnapshot));
|
|
|
|
|
|
|
|
sensitivityInput->m_bAutoFitTargetOnly = true;
|
|
|
|
|
|
|
|
sensitivityInput->m_bRequiresGridCalculation = false;
|
|
|
|
|
|
|
|
sensitivityInput->m_bGridResultNeedsCommit = false;
|
|
|
|
|
|
|
|
// 分析只需要 DLL 网格值,不保留正式结果的 VTK 可写引用。
|
|
|
|
|
|
|
|
sensitivityInput->m_pBaseGrid = NULL;
|
|
|
|
|
|
|
|
sensitivityInput->m_pSourceBaseGrid = NULL;
|
|
|
|
|
|
|
|
sensitivityInput->m_oGridResult.m_pUnstructuredGrid = NULL;
|
|
|
|
|
|
|
|
if(!oBuilder.setSensitivityInput(sensitivityInput, m_pInputSnapshot->m_sPrimaryWellCode)) return false;
|
|
|
|
if(!oBuilder.setInputRevisions(m_nGridInputRevision,
|
|
|
|
if(!oBuilder.setInputRevisions(m_nGridInputRevision,
|
|
|
|
m_nResultInputRevision)) {
|
|
|
|
m_nResultInputRevision)) {
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
|