|
|
|
@ -96,7 +96,6 @@ 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;
|
|
|
|
@ -255,10 +254,10 @@ double calculateSensitivityPressureRmse(const QVector<QVector<double> >& measure
|
|
|
|
? std::sqrt(squaredError / count) : -1.0;
|
|
|
|
? std::sqrt(squaredError / count) : -1.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool savedPseudoPressure(const nmDataBinaryTools::NM_PEBI_SCENE& scene,
|
|
|
|
bool inputPseudoPressure(const nmDataBinaryTools::NM_PEBI_SCENE& scene,
|
|
|
|
double pressure, double& result)
|
|
|
|
double pressure, double& result)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 气井分析使用求解时的拟压力表,避免当前编辑改写全局转换器配置。
|
|
|
|
// 使用本次任务捕获的拟压力表,后台插值不访问全局转换器。
|
|
|
|
const std::vector<double>& p = scene.PVT.pseudoPressureP;
|
|
|
|
const std::vector<double>& p = scene.PVT.pseudoPressureP;
|
|
|
|
const std::vector<double>& ps = scene.PVT.pseudoPressurePs;
|
|
|
|
const std::vector<double>& ps = scene.PVT.pseudoPressurePs;
|
|
|
|
if(p.size() < 2 || p.size() != ps.size() || !isFiniteSolverNumber(pressure)) return false;
|
|
|
|
if(p.size() < 2 || p.size() != ps.size() || !isFiniteSolverNumber(pressure)) return false;
|
|
|
|
@ -449,7 +448,6 @@ 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();
|
|
|
|
@ -702,8 +700,7 @@ 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
|
|
|
|
@ -734,9 +731,7 @@ nmCalculationDllPebiSolverTask::nmCalculationDllPebiSolverTask(
|
|
|
|
m_bManagerUseActive = true;
|
|
|
|
m_bManagerUseActive = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(bUseSolvedInput) {
|
|
|
|
if(bDeferManualSnapshot && sAutoFitTargetWellName.isEmpty()) {
|
|
|
|
m_bInputSnapshotValid = restoreSolvedInput();
|
|
|
|
|
|
|
|
} else if(bDeferManualSnapshot && sAutoFitTargetWellName.isEmpty()) {
|
|
|
|
|
|
|
|
// 手工求解由界面事件循环分批冻结值输入;start() 必须等状态释放后再调用。
|
|
|
|
// 手工求解由界面事件循环分批冻结值输入;start() 必须等状态释放后再调用。
|
|
|
|
m_pManualCaptureState = new nmPebiManualCaptureState();
|
|
|
|
m_pManualCaptureState = new nmPebiManualCaptureState();
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
@ -799,8 +794,8 @@ nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivit
|
|
|
|
? tr("Solver input is unavailable.") : m_sFailureReason;
|
|
|
|
? tr("Solver input is unavailable.") : m_sFailureReason;
|
|
|
|
return nullptr;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!m_pInputSnapshot->m_oGridResult.m_bSucceeded ||
|
|
|
|
if(!m_pInputSnapshot->m_oGridResult.m_bSucceeded &&
|
|
|
|
m_pInputSnapshot->m_bRequiresGridCalculation) {
|
|
|
|
!m_pInputSnapshot->m_bRequiresGridCalculation) {
|
|
|
|
if(errorMessage) *errorMessage = tr("Generate a valid base model before running sensitivity.");
|
|
|
|
if(errorMessage) *errorMessage = tr("Generate a valid base model before running sensitivity.");
|
|
|
|
return nullptr;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -992,9 +987,8 @@ 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) {
|
|
|
|
const QSharedPointer<const nmPebiResultSnapshot> solved = m_pDataManager->getPebiResultSnapshot();
|
|
|
|
// 拟合参数基准值读取当前模型,不使用设置窗口中可编辑的拟合初值。
|
|
|
|
// 拟合参数的蓝色基准点来自原模型,不使用设置窗口中可编辑的拟合初值。
|
|
|
|
if(improveSettings) {
|
|
|
|
if(improveSettings && !solved.isNull()) {
|
|
|
|
|
|
|
|
const nmDataAutomaticFitting& fit = improveSettings->parameters;
|
|
|
|
const nmDataAutomaticFitting& fit = improveSettings->parameters;
|
|
|
|
const bool selected[] = { fit.getPermeabilitySelected(), fit.getSkinSelected(),
|
|
|
|
const bool selected[] = { fit.getPermeabilitySelected(), fit.getSkinSelected(),
|
|
|
|
fit.getWellboreStorageSelected(), fit.getPorositySelected(),
|
|
|
|
fit.getWellboreStorageSelected(), fit.getPorositySelected(),
|
|
|
|
@ -1002,28 +996,43 @@ nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivit
|
|
|
|
const char* names[] = { "Permeability", "Skin", "Wellbore storage", "Porosity",
|
|
|
|
const char* names[] = { "Permeability", "Skin", "Wellbore storage", "Porosity",
|
|
|
|
"Fracture conductivity", "Fracture half length" };
|
|
|
|
"Fracture conductivity", "Fracture half length" };
|
|
|
|
const char* units[] = { "mD", "", "m^3/MPa", "", "mD.m", "m" };
|
|
|
|
const char* units[] = { "mD", "", "m^3/MPa", "", "mD.m", "m" };
|
|
|
|
for(int i = 0; i < solved->getWellCount(); ++i) {
|
|
|
|
nmDataWellBase* well = m_pDataManager->findWellByName(improveSettings->targetWell);
|
|
|
|
const nmPebiResultWellSnapshot* well = solved->getWellAt(i);
|
|
|
|
if(well) {
|
|
|
|
if(well->m_sWellName != improveSettings->targetWell) continue;
|
|
|
|
// 启动前补齐拟合目标井的历史双对数,后台各组共用这份目标数据。
|
|
|
|
double halfLength = std::numeric_limits<double>::quiet_NaN();
|
|
|
|
QVector<nmPebiWellInputSnapshot>& wells = task->m_pInputSnapshot->m_oGridInput.m_vecWellInputs;
|
|
|
|
if(selected[5]) getSolvedFractureHalfLength(solved->getSensitivityInput(), well->m_sWellCode, halfLength);
|
|
|
|
for(int i = 0; i < wells.size(); ++i) {
|
|
|
|
|
|
|
|
if(wells[i].m_bRealWell && wells[i].m_sWellCode == well->getWellCode()) {
|
|
|
|
|
|
|
|
wells[i].m_vecHistoryLogLog = well->getHistoryLogLog();
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
double conductivity = 0.0, halfLength = 0.0;
|
|
|
|
|
|
|
|
if(nmDataVerticalFracturedWell* fractured = dynamic_cast<nmDataVerticalFracturedWell*>(well)) {
|
|
|
|
|
|
|
|
conductivity = fractured->getDfc().getValue().toDouble();
|
|
|
|
|
|
|
|
halfLength = fractured->getFractureHalfLength().getValue().toDouble();
|
|
|
|
|
|
|
|
} else if(nmDataHorizontalFracturedWell* fractured = dynamic_cast<nmDataHorizontalFracturedWell*>(well)) {
|
|
|
|
|
|
|
|
conductivity = fractured->getDfc().getValue().toDouble();
|
|
|
|
|
|
|
|
halfLength = fractured->getFractureHalfLength().getValue().toDouble();
|
|
|
|
|
|
|
|
}
|
|
|
|
const double values[] = { m_pInputSnapshot->m_oStaticPropertyInput.m_dReferencePermeabilityMilliDarcy,
|
|
|
|
const double values[] = { m_pInputSnapshot->m_oStaticPropertyInput.m_dReferencePermeabilityMilliDarcy,
|
|
|
|
well->m_dSkin, well->m_dWellboreStorage,
|
|
|
|
well->getPerforationCount() > 0 ? well->getPerforation(0)->getSkin().getValue().toDouble() : 0.0,
|
|
|
|
m_pInputSnapshot->m_oStaticPropertyInput.m_dReferencePorosity, well->m_dDfc, halfLength };
|
|
|
|
well->getWellboreStorage().getValue().toDouble(),
|
|
|
|
for(int j = 0; j < 6; ++j) if(selected[j])
|
|
|
|
m_pInputSnapshot->m_oStaticPropertyInput.m_dReferencePorosity, conductivity, halfLength };
|
|
|
|
|
|
|
|
for(int j = 0; j < 6; ++j) if(selected[j]) {
|
|
|
|
task->m_pSensitivityResult->m_fitParameters.append(nmDataAttribute(
|
|
|
|
task->m_pSensitivityResult->m_fitParameters.append(nmDataAttribute(
|
|
|
|
QCoreApplication::translate("nmWxAutomaticFitting", names[j]), values[j], units[j]));
|
|
|
|
QCoreApplication::translate("nmWxAutomaticFitting", names[j]), values[j], units[j]));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(int i = 0; !solved.isNull() && i < solved->getWellCount(); ++i) {
|
|
|
|
nmDataWellBase* well = m_pDataManager->findWellByCode(task->m_sAutoFitTargetWellCode);
|
|
|
|
const nmPebiResultWellSnapshot* well = solved->getWellAt(i);
|
|
|
|
if(well) {
|
|
|
|
if(well->m_sWellCode != task->m_sAutoFitTargetWellCode) continue;
|
|
|
|
// 第三、四种方法按分析主井的实测压力统计误差,与拟合目标井分别取值。
|
|
|
|
// 历史和基准曲线必须与计算输入属于同一次求解。
|
|
|
|
const int target = task->m_pInputSnapshot->m_oSensitivityBaseline.m_nTarget;
|
|
|
|
task->m_pSensitivityResult->m_sWellName = well->m_sWellName;
|
|
|
|
task->m_pInputSnapshot->m_oGridInput.m_vecWellInputs[target].m_vecHistoryPressure = well->getHistoryPressure();
|
|
|
|
task->m_pSensitivityResult->m_vecHistoryLogLog = well->m_oCurves.m_vecHistoryLogLog;
|
|
|
|
task->m_pSensitivityResult->m_sWellName = well->getWellName();
|
|
|
|
task->m_pSensitivityResult->m_vecBaseLogLog = well->m_oCurves.m_vecResultLogLog;
|
|
|
|
task->m_pSensitivityResult->m_vecHistoryLogLog = well->getHistoryLogLog();
|
|
|
|
break;
|
|
|
|
// 对比曲线沿用已有求解结果,分析输入则取当前参数。
|
|
|
|
|
|
|
|
task->m_pSensitivityResult->m_vecBaseLogLog = well->getResultLogLog();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return task.take();
|
|
|
|
return task.take();
|
|
|
|
@ -1059,37 +1068,6 @@ double nmCalculationDllPebiSolverTask::calculatePressureRmse(
|
|
|
|
return calculateSensitivityPressureRmse(measured, simulated, nullptr);
|
|
|
|
return calculateSensitivityPressureRmse(measured, simulated, nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool nmCalculationDllPebiSolverTask::getSolvedFractureHalfLength(
|
|
|
|
|
|
|
|
const QSharedPointer<const nmPebiSolverInputSnapshot>& input,
|
|
|
|
|
|
|
|
const QString& wellCode, double& halfLength)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 各类井的几何数组与同类型槽位顺序一致,水平压裂井的各条裂缝共用半长。
|
|
|
|
|
|
|
|
if(input.isNull()) return false;
|
|
|
|
|
|
|
|
const nmPebiGridInputSnapshot& grid = input->m_oGridInput;
|
|
|
|
|
|
|
|
size_t vertical = 0, horizontal = 0;
|
|
|
|
|
|
|
|
for(int i = 0; i < grid.m_vecSolverWellOrder.size(); ++i) {
|
|
|
|
|
|
|
|
const nmSolverWellRef& well = grid.m_vecSolverWellOrder[i];
|
|
|
|
|
|
|
|
const std::vector<double>* fracture = nullptr;
|
|
|
|
|
|
|
|
if(well.m_eWellType == Vertical_Fractured_Well) {
|
|
|
|
|
|
|
|
if(vertical < grid.m_oGridInput.FractureVerticalWell.size())
|
|
|
|
|
|
|
|
fracture = &grid.m_oGridInput.FractureVerticalWell[vertical];
|
|
|
|
|
|
|
|
++vertical;
|
|
|
|
|
|
|
|
} else if(well.m_eWellType == Horizontal_Fractured_Well) {
|
|
|
|
|
|
|
|
if(horizontal < grid.m_oGridInput.MultistageFracturedHorizontalWell.size() &&
|
|
|
|
|
|
|
|
!grid.m_oGridInput.MultistageFracturedHorizontalWell[horizontal].empty())
|
|
|
|
|
|
|
|
fracture = &grid.m_oGridInput.MultistageFracturedHorizontalWell[horizontal].front();
|
|
|
|
|
|
|
|
++horizontal;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(well.m_sWellCode != wellCode) continue;
|
|
|
|
|
|
|
|
if(!fracture || fracture->size() < 4) return false;
|
|
|
|
|
|
|
|
const double dx = (*fracture)[2] - (*fracture)[0];
|
|
|
|
|
|
|
|
const double dy = (*fracture)[3] - (*fracture)[1];
|
|
|
|
|
|
|
|
halfLength = std::sqrt(dx * dx + dy * dy) * 0.5;
|
|
|
|
|
|
|
|
return qIsFinite(halfLength) && halfLength > 0.0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool nmCalculationDllPebiSolverTask::improveSensitivityRun(QVector<double>& fittedParameters)
|
|
|
|
bool nmCalculationDllPebiSolverTask::improveSensitivityRun(QVector<double>& fittedParameters)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 采样值已经写入本任务;候选只覆盖选中的拟合参数,不触碰管理器和正式结果。
|
|
|
|
// 采样值已经写入本任务;候选只覆盖选中的拟合参数,不触碰管理器和正式结果。
|
|
|
|
@ -1102,7 +1080,7 @@ bool nmCalculationDllPebiSolverTask::improveSensitivityRun(QVector<double>& fitt
|
|
|
|
if(grid.m_vecWellInputs[i].m_bRealWell &&
|
|
|
|
if(grid.m_vecWellInputs[i].m_bRealWell &&
|
|
|
|
grid.m_vecWellInputs[i].m_sWellName == settings.targetWell) { target = i; break; }
|
|
|
|
grid.m_vecWellInputs[i].m_sWellName == settings.targetWell) { target = i; break; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(target < 0) return fail(tr("Target well is unavailable in the solved model."));
|
|
|
|
if(target < 0) return fail(tr("The target well is unavailable."));
|
|
|
|
const bool selected[] = { fit.getPermeabilitySelected(), fit.getSkinSelected(),
|
|
|
|
const bool selected[] = { fit.getPermeabilitySelected(), fit.getSkinSelected(),
|
|
|
|
fit.getWellboreStorageSelected(), fit.getPorositySelected(),
|
|
|
|
fit.getWellboreStorageSelected(), fit.getPorositySelected(),
|
|
|
|
fit.getFractureConductivitySelected(), fit.getFractureHalfLengthSelected() };
|
|
|
|
fit.getFractureConductivitySelected(), fit.getFractureHalfLengthSelected() };
|
|
|
|
@ -1176,7 +1154,8 @@ bool nmCalculationDllPebiSolverTask::improveSensitivityRun(QVector<double>& fitt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(selected[4]) grid.m_vecWellInputs[target].m_dDfc = values[4];
|
|
|
|
if(selected[4]) grid.m_vecWellInputs[target].m_dDfc = values[4];
|
|
|
|
m_pInputSnapshot->m_bRequiresGridCalculation = !fractures.empty();
|
|
|
|
m_pInputSnapshot->m_bRequiresGridCalculation =
|
|
|
|
|
|
|
|
m_pInputSnapshot->m_bRequiresGridCalculation || !fractures.empty();
|
|
|
|
m_sFailureReason.clear();
|
|
|
|
m_sFailureReason.clear();
|
|
|
|
calculateSingleRun();
|
|
|
|
calculateSingleRun();
|
|
|
|
return m_lastRunSucceeded ? m_autoFitResultLogLog : QVector<QVector<double> >();
|
|
|
|
return m_lastRunSucceeded ? m_autoFitResultLogLog : QVector<QVector<double> >();
|
|
|
|
@ -1244,7 +1223,7 @@ bool nmCalculationDllPebiSolverTask::executeSensitivityBatch()
|
|
|
|
result.m_vecLogLog = m_autoFitResultLogLog;
|
|
|
|
result.m_vecLogLog = m_autoFitResultLogLog;
|
|
|
|
result.m_vecSemiLog = m_autoFitResultSemiLog;
|
|
|
|
result.m_vecSemiLog = m_autoFitResultSemiLog;
|
|
|
|
if(m_pSensitivityResult->m_calcType == nmDataSensitive::CALC_MONTE_CARLO) {
|
|
|
|
if(m_pSensitivityResult->m_calcType == nmDataSensitive::CALC_MONTE_CARLO) {
|
|
|
|
// 使用求解时冻结的目标井实测压力,不读取界面后来修改的数据。
|
|
|
|
// 使用本批任务开始时读取的实测压力,计算过程中不访问界面数据。
|
|
|
|
const int target = m_pInputSnapshot->m_oSensitivityBaseline.m_nTarget;
|
|
|
|
const int target = m_pInputSnapshot->m_oSensitivityBaseline.m_nTarget;
|
|
|
|
result.m_dPressureRmse = calculateSensitivityPressureRmse(
|
|
|
|
result.m_dPressureRmse = calculateSensitivityPressureRmse(
|
|
|
|
m_pInputSnapshot->m_oGridInput.m_vecWellInputs[target].m_vecHistoryPressure,
|
|
|
|
m_pInputSnapshot->m_oGridInput.m_vecWellInputs[target].m_vecHistoryPressure,
|
|
|
|
@ -1617,20 +1596,6 @@ 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)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -1935,6 +1900,8 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 重开项目或当前几何改变时首次重建,后续采样复用本批网格。
|
|
|
|
|
|
|
|
m_pInputSnapshot->m_bRequiresGridCalculation = false;
|
|
|
|
if(bCreateUnstructuredGrid) {
|
|
|
|
if(bCreateUnstructuredGrid) {
|
|
|
|
if(oGridResult.m_pUnstructuredGrid == nullptr ||
|
|
|
|
if(oGridResult.m_pUnstructuredGrid == nullptr ||
|
|
|
|
oGridResult.m_pUnstructuredGrid->GetNumberOfCells() <= 0) {
|
|
|
|
oGridResult.m_pUnstructuredGrid->GetNumberOfCells() <= 0) {
|
|
|
|
@ -2154,13 +2121,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));
|
|
|
|
const bool useSavedPseudoPressure = usePseudoPressure &&
|
|
|
|
const bool useInputPseudoPressure = usePseudoPressure &&
|
|
|
|
m_pInputSnapshot->m_oSensitivityBaseline.m_nTarget >= 0;
|
|
|
|
m_pInputSnapshot->m_oSensitivityBaseline.m_nTarget >= 0;
|
|
|
|
CalPseudoPressure calPseudoPressure = usePseudoPressure && !useSavedPseudoPressure
|
|
|
|
CalPseudoPressure calPseudoPressure = usePseudoPressure && !useInputPseudoPressure
|
|
|
|
? getPseudoPressureConverter()
|
|
|
|
? getPseudoPressureConverter()
|
|
|
|
: nullptr;
|
|
|
|
: nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
if(usePseudoPressure && !useSavedPseudoPressure && calPseudoPressure == nullptr) {
|
|
|
|
if(usePseudoPressure && !useInputPseudoPressure && 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."));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -2298,9 +2265,9 @@ bool nmCalculationDllPebiSolverTask::buildPebiModeResult(
|
|
|
|
double pressureForLog = p1.pw[wellIdx][i];
|
|
|
|
double pressureForLog = p1.pw[wellIdx][i];
|
|
|
|
if(usePseudoPressure
|
|
|
|
if(usePseudoPressure
|
|
|
|
&& (!isFiniteSolverNumber(pressureForLog)
|
|
|
|
&& (!isFiniteSolverNumber(pressureForLog)
|
|
|
|
// 敏感性任务只查求解时保存的表,不能读取已被编辑或其他窗口改写的全局配置。
|
|
|
|
// 后台任务使用本次输入中的转换表,避免其他窗口改写全局配置。
|
|
|
|
|| !(useSavedPseudoPressure
|
|
|
|
|| !(useInputPseudoPressure
|
|
|
|
? savedPseudoPressure(
|
|
|
|
? inputPseudoPressure(
|
|
|
|
m_pInputSnapshot->m_oGridInput.m_oScene, pressureForLog, pressureForLog)
|
|
|
|
m_pInputSnapshot->m_oGridInput.m_oScene, pressureForLog, pressureForLog)
|
|
|
|
: calPseudoPressure(pressureForLog, pressureForLog, -1))
|
|
|
|
: calPseudoPressure(pressureForLog, pressureForLog, -1))
|
|
|
|
|| !isFiniteSolverNumber(pressureForLog))) {
|
|
|
|
|| !isFiniteSolverNumber(pressureForLog))) {
|
|
|
|
@ -2551,17 +2518,6 @@ 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;
|
|
|
|
|