|
|
|
|
@ -22,6 +22,8 @@
|
|
|
|
|
|
|
|
|
|
#include "nmCalculationPebiGrid.h"
|
|
|
|
|
#include "nmCalculationUtils.h"
|
|
|
|
|
#include "nmCalculationAutoFitLM.h"
|
|
|
|
|
#include "nmDataAutomaticFitting.h"
|
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
|
|
#include "nmDataPvtParaForPebi.h"
|
|
|
|
|
|
|
|
|
|
@ -50,7 +52,7 @@
|
|
|
|
|
#include <vtkUnstructuredGrid.h>
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief PEBI 求解任务独占的完整值输入,敏感性任务仅更新采样标量。
|
|
|
|
|
* @brief PEBI 求解任务独占的完整值输入,敏感性采样和拟合只修改此副本。
|
|
|
|
|
*/
|
|
|
|
|
struct nmPebiSolverInputSnapshot
|
|
|
|
|
{
|
|
|
|
|
@ -948,7 +950,8 @@ bool nmCalculationDllPebiSolverTask::calculateSensitivityBatch(
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivityBatch(
|
|
|
|
|
const nmDataSensitive& sampling, QString* errorMessage) const
|
|
|
|
|
const nmDataSensitive& sampling, QString* errorMessage,
|
|
|
|
|
const nmSensitivityFitSettings* improveSettings) const
|
|
|
|
|
{
|
|
|
|
|
if(errorMessage) errorMessage->clear();
|
|
|
|
|
try {
|
|
|
|
|
@ -975,6 +978,9 @@ nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivit
|
|
|
|
|
if(errorMessage) *errorMessage = creationError;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
// 第四种的拟合配置按值冻结,整批样本共用确认过的初值和范围。
|
|
|
|
|
if(improveSettings) task->m_pSensitivityFitSettings =
|
|
|
|
|
QSharedPointer<const nmSensitivityFitSettings>(new nmSensitivityFitSettings(*improveSettings));
|
|
|
|
|
task->m_pSensitivitySampling = QSharedPointer<const nmDataSensitive>(
|
|
|
|
|
new nmDataSensitive(frozenSampling));
|
|
|
|
|
// 批次结果单独持有冻结配置与历史曲线,关闭参数窗口后仍可保存和显示。
|
|
|
|
|
@ -1059,6 +1065,116 @@ bool nmCalculationDllPebiSolverTask::getSolvedFractureHalfLength(
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool nmCalculationDllPebiSolverTask::improveSensitivityRun()
|
|
|
|
|
{
|
|
|
|
|
// 采样值已经写入本任务;候选只覆盖选中的拟合参数,不触碰管理器和正式结果。
|
|
|
|
|
const nmSensitivityFitSettings& settings = *m_pSensitivityFitSettings;
|
|
|
|
|
const nmDataAutomaticFitting& fit = settings.parameters;
|
|
|
|
|
nmPebiGridInputSnapshot& grid = m_pInputSnapshot->m_oGridInput;
|
|
|
|
|
nmPebiStaticPropertyCalculationInput& properties = m_pInputSnapshot->m_oStaticPropertyInput;
|
|
|
|
|
int target = -1;
|
|
|
|
|
for(int i = 0; i < grid.m_vecWellInputs.size(); ++i) {
|
|
|
|
|
if(grid.m_vecWellInputs[i].m_bRealWell &&
|
|
|
|
|
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."));
|
|
|
|
|
const bool selected[] = { fit.getPermeabilitySelected(), fit.getSkinSelected(),
|
|
|
|
|
fit.getWellboreStorageSelected(), fit.getPorositySelected(),
|
|
|
|
|
fit.getFractureConductivitySelected(), fit.getFractureHalfLengthSelected() };
|
|
|
|
|
for(int i = 0; i < properties.m_vecDataSets.size(); ++i) {
|
|
|
|
|
const nmPropertyInterpolationDataSet& field = properties.m_vecDataSets[i];
|
|
|
|
|
if(field.useForCalculation && ((selected[0] && field.property == "k") ||
|
|
|
|
|
(selected[3] && field.property == "phi")))
|
|
|
|
|
return fail(tr("The sampled parameters are invalid or use a non-uniform field."));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 井型数组与求解槽位顺序一致,保留原端点以免反复缩放造成几何累积偏差。
|
|
|
|
|
std::vector<std::vector<double>*> fractures;
|
|
|
|
|
std::vector<std::vector<double> > originalFractures;
|
|
|
|
|
if(selected[4] || selected[5]) {
|
|
|
|
|
size_t vertical = 0, horizontal = 0;
|
|
|
|
|
for(int i = 0; i <= target; ++i) {
|
|
|
|
|
const int type = grid.m_vecSolverWellOrder[i].m_eWellType;
|
|
|
|
|
if(type == Vertical_Fractured_Well) {
|
|
|
|
|
if(i == target) fractures.push_back(&grid.m_oGridInput.FractureVerticalWell[vertical]);
|
|
|
|
|
++vertical;
|
|
|
|
|
} else if(type == Horizontal_Fractured_Well) {
|
|
|
|
|
if(i == target) {
|
|
|
|
|
std::vector<std::vector<double> >& well =
|
|
|
|
|
grid.m_oGridInput.MultistageFracturedHorizontalWell[horizontal];
|
|
|
|
|
for(size_t j = 0; j < well.size(); ++j) fractures.push_back(&well[j]);
|
|
|
|
|
}
|
|
|
|
|
++horizontal;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(fractures.empty()) return fail(tr("Fracture parameters are unavailable for the target well."));
|
|
|
|
|
for(size_t i = 0; i < fractures.size(); ++i) originalFractures.push_back(*fractures[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString resultWellCode = m_sAutoFitTargetWellCode;
|
|
|
|
|
m_sAutoFitTargetWellCode = grid.m_vecWellInputs[target].m_sWellCode;
|
|
|
|
|
auto evaluate = [&](const QVector<double>& parameters) -> QVector<QVector<double> > {
|
|
|
|
|
if(isCancelRequested()) return QVector<QVector<double> >();
|
|
|
|
|
QVector<double> values = settings.initialValues;
|
|
|
|
|
int index = 0;
|
|
|
|
|
for(int i = 0; i < 6; ++i) if(selected[i]) values[i] = parameters[index++];
|
|
|
|
|
if(selected[0]) {
|
|
|
|
|
grid.m_oScene.Base.k_ref = nmCalculationUtils::milliDarcyToDarcy(values[0]);
|
|
|
|
|
properties.m_dReferencePermeabilityMilliDarcy = values[0];
|
|
|
|
|
}
|
|
|
|
|
if(selected[1]) {
|
|
|
|
|
grid.m_oScene.CS.S[target] = values[1];
|
|
|
|
|
grid.m_vecWellInputs[target].m_dSkin = values[1];
|
|
|
|
|
}
|
|
|
|
|
if(selected[2]) {
|
|
|
|
|
grid.m_oScene.CS.C[target] = values[2];
|
|
|
|
|
grid.m_vecWellInputs[target].m_dWellboreStorage = values[2];
|
|
|
|
|
}
|
|
|
|
|
if(selected[3]) {
|
|
|
|
|
grid.m_oScene.Base.phi_ref = values[3];
|
|
|
|
|
properties.m_dReferencePorosity = values[3];
|
|
|
|
|
}
|
|
|
|
|
for(size_t i = 0; i < fractures.size(); ++i) {
|
|
|
|
|
std::vector<double>& fracture = *fractures[i];
|
|
|
|
|
fracture = originalFractures[i];
|
|
|
|
|
if(selected[4]) fracture[5] = values[4];
|
|
|
|
|
if(selected[5]) {
|
|
|
|
|
const double dx = (fracture[2] - fracture[0]) * 0.5;
|
|
|
|
|
const double dy = (fracture[3] - fracture[1]) * 0.5;
|
|
|
|
|
const double centerX = (fracture[0] + fracture[2]) * 0.5;
|
|
|
|
|
const double centerY = (fracture[1] + fracture[3]) * 0.5;
|
|
|
|
|
const double scale = values[5] / std::sqrt(dx * dx + dy * dy);
|
|
|
|
|
fracture[0] = centerX - dx * scale;
|
|
|
|
|
fracture[1] = centerY - dy * scale;
|
|
|
|
|
fracture[2] = centerX + dx * scale;
|
|
|
|
|
fracture[3] = centerY + dy * scale;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(selected[4]) grid.m_vecWellInputs[target].m_dDfc = values[4];
|
|
|
|
|
m_pInputSnapshot->m_bRequiresGridCalculation = !fractures.empty();
|
|
|
|
|
m_sFailureReason.clear();
|
|
|
|
|
calculateSingleRun();
|
|
|
|
|
return m_lastRunSucceeded ? m_autoFitResultLogLog : QVector<QVector<double> >();
|
|
|
|
|
};
|
|
|
|
|
auto cancelled = [&]() -> bool { return isCancelRequested(); };
|
|
|
|
|
const bool verticalWell = grid.m_vecSolverWellOrder[target].m_eWellType == Vertical_Well;
|
|
|
|
|
// 敏感性改善仅使用 LM,直接复用其拟合流程。
|
|
|
|
|
nmCalculationAutoFitLM fitter;
|
|
|
|
|
fitter.setTargetLogLogData(grid.m_vecWellInputs[target].m_vecHistoryLogLog);
|
|
|
|
|
const bool succeeded = fitter.startSensitivityFitting(settings, evaluate, cancelled, verticalWell);
|
|
|
|
|
const QVector<double> best = fitter.getBestSolution();
|
|
|
|
|
const QString error = fitter.getLastError();
|
|
|
|
|
// 算法最后评价的候选不一定最优;恢复分析井后用最优参数求解并保留三条曲线。
|
|
|
|
|
m_sAutoFitTargetWellCode = resultWellCode;
|
|
|
|
|
if(isCancelRequested()) return false;
|
|
|
|
|
if(!succeeded) {
|
|
|
|
|
m_sFailureReason.clear();
|
|
|
|
|
return fail(error.isEmpty() ? tr("No valid fitting result was obtained.") : error);
|
|
|
|
|
}
|
|
|
|
|
evaluate(best);
|
|
|
|
|
return m_lastRunSucceeded;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool nmCalculationDllPebiSolverTask::executeSensitivityBatch()
|
|
|
|
|
{
|
|
|
|
|
m_pSensitivityResult->m_runs.clear();
|
|
|
|
|
@ -1069,9 +1185,15 @@ bool nmCalculationDllPebiSolverTask::executeSensitivityBatch()
|
|
|
|
|
progressTimer.start();
|
|
|
|
|
emit sigSensitivityProgress(0, count, 0);
|
|
|
|
|
try {
|
|
|
|
|
// 拟合还会改动其他井的参数、裂缝几何及网格,整批只保留一份初始输入供逐组恢复。
|
|
|
|
|
QScopedPointer<nmPebiSolverInputSnapshot> fitBaseline;
|
|
|
|
|
if(!m_pSensitivityFitSettings.isNull())
|
|
|
|
|
fitBaseline.reset(new nmPebiSolverInputSnapshot(*m_pInputSnapshot));
|
|
|
|
|
|
|
|
|
|
for(quint64 runIndex = 0; runIndex < count; ++runIndex) {
|
|
|
|
|
if(isCancelRequested()) return false;
|
|
|
|
|
// 先还原输入,再由 updateSensitivityRun 填入本组采样值;停止标记不随输入重置。
|
|
|
|
|
if(!fitBaseline.isNull() && runIndex > 0) *m_pInputSnapshot = *fitBaseline;
|
|
|
|
|
SensitivityRunResult result;
|
|
|
|
|
result.m_nRunIndex = runIndex;
|
|
|
|
|
// 确定性组合和 Monte-Carlo 样本均按预览顺序读取,后台不重新采样。
|
|
|
|
|
@ -1084,8 +1206,12 @@ bool nmCalculationDllPebiSolverTask::executeSensitivityBatch()
|
|
|
|
|
&result.m_sFailureReason);
|
|
|
|
|
}
|
|
|
|
|
if(inputReady) {
|
|
|
|
|
calculateSingleRun();
|
|
|
|
|
result.m_bSucceeded = m_lastRunSucceeded;
|
|
|
|
|
if(m_pSensitivityFitSettings.isNull()) {
|
|
|
|
|
calculateSingleRun();
|
|
|
|
|
result.m_bSucceeded = m_lastRunSucceeded;
|
|
|
|
|
} else {
|
|
|
|
|
result.m_bSucceeded = improveSensitivityRun();
|
|
|
|
|
}
|
|
|
|
|
if(result.m_bSucceeded) {
|
|
|
|
|
result.m_vecPressure = m_autoFitResultPressure;
|
|
|
|
|
result.m_vecLogLog = m_autoFitResultLogLog;
|
|
|
|
|
|