|
|
|
@ -1,6 +1,7 @@
|
|
|
|
#include "nmCalculationDllPebiSolverTask.h"
|
|
|
|
#include "nmCalculationDllPebiSolverTask.h"
|
|
|
|
#include "zxLogInstance.h"
|
|
|
|
#include "zxLogInstance.h"
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
|
|
|
|
|
#include "nmDataSensitive.h"
|
|
|
|
#include "nmDataWellBase.h"
|
|
|
|
#include "nmDataWellBase.h"
|
|
|
|
#include "nmDataVerticalWell.h"
|
|
|
|
#include "nmDataVerticalWell.h"
|
|
|
|
#include "nmDataVerticalFracturedWell.h"
|
|
|
|
#include "nmDataVerticalFracturedWell.h"
|
|
|
|
@ -26,6 +27,7 @@
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QHash>
|
|
|
|
#include <QHash>
|
|
|
|
#include <QMutexLocker>
|
|
|
|
#include <QMutexLocker>
|
|
|
|
|
|
|
|
#include <QScopedPointer>
|
|
|
|
#include <QSet>
|
|
|
|
#include <QSet>
|
|
|
|
#include <QUuid>
|
|
|
|
#include <QUuid>
|
|
|
|
#include <iostream>
|
|
|
|
#include <iostream>
|
|
|
|
@ -46,10 +48,35 @@
|
|
|
|
#include <vtkUnstructuredGrid.h>
|
|
|
|
#include <vtkUnstructuredGrid.h>
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @brief 一次 PEBI 求解任务独占的完整不可变输入。
|
|
|
|
* @brief PEBI 求解任务独占的完整值输入,敏感性任务仅更新采样标量。
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
struct nmPebiSolverInputSnapshot
|
|
|
|
struct nmPebiSolverInputSnapshot
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
// 只冻结会被采样覆盖的标量,不另存一份大型网格或完整模型。
|
|
|
|
|
|
|
|
struct SensitivityBaseline
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
SensitivityBaseline()
|
|
|
|
|
|
|
|
: m_nTarget(-1), m_dSceneSkin(0.0), m_dSceneStorage(0.0),
|
|
|
|
|
|
|
|
m_dWellSkin(0.0), m_dWellStorage(0.0), m_dPressure(0.0),
|
|
|
|
|
|
|
|
m_dScenePermeability(0.0), m_dSceneThickness(0.0),
|
|
|
|
|
|
|
|
m_dScenePorosity(0.0), m_dReferencePermeability(0.0),
|
|
|
|
|
|
|
|
m_dReferenceThickness(0.0), m_dReferencePorosity(0.0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int m_nTarget; ///< 非负值表示已初始化的独立敏感性任务。
|
|
|
|
|
|
|
|
double m_dSceneSkin;
|
|
|
|
|
|
|
|
double m_dSceneStorage;
|
|
|
|
|
|
|
|
double m_dWellSkin;
|
|
|
|
|
|
|
|
double m_dWellStorage;
|
|
|
|
|
|
|
|
double m_dPressure;
|
|
|
|
|
|
|
|
double m_dScenePermeability;
|
|
|
|
|
|
|
|
double m_dSceneThickness;
|
|
|
|
|
|
|
|
double m_dScenePorosity;
|
|
|
|
|
|
|
|
double m_dReferencePermeability;
|
|
|
|
|
|
|
|
double m_dReferenceThickness;
|
|
|
|
|
|
|
|
double m_dReferencePorosity;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
nmPebiSolverInputSnapshot()
|
|
|
|
nmPebiSolverInputSnapshot()
|
|
|
|
: m_nSolverType(nmDataAnalyzeManager::PebiSolverOriginal),
|
|
|
|
: m_nSolverType(nmDataAnalyzeManager::PebiSolverOriginal),
|
|
|
|
m_nOmpThreads(1),
|
|
|
|
m_nOmpThreads(1),
|
|
|
|
@ -66,6 +93,7 @@ struct nmPebiSolverInputSnapshot
|
|
|
|
nmPebiGridResult m_oGridResult; ///< 已有网格副本或后台新生成的局部网格。
|
|
|
|
nmPebiGridResult m_oGridResult; ///< 已有网格副本或后台新生成的局部网格。
|
|
|
|
/** @brief 在 DataManager 线程冻结的静态属性参考值、插值数据和授权路径。 */
|
|
|
|
/** @brief 在 DataManager 线程冻结的静态属性参考值、插值数据和授权路径。 */
|
|
|
|
nmPebiStaticPropertyCalculationInput m_oStaticPropertyInput;
|
|
|
|
nmPebiStaticPropertyCalculationInput m_oStaticPropertyInput;
|
|
|
|
|
|
|
|
SensitivityBaseline m_oSensitivityBaseline; ///< 首次复制时冻结,后续更新只读。
|
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> m_pBaseGrid; ///< 完整成果使用的基础 VTK 网格。
|
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> m_pBaseGrid; ///< 完整成果使用的基础 VTK 网格。
|
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> m_pSourceBaseGrid; ///< 主线程捕获引用,后台只读并深拷贝。
|
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> m_pSourceBaseGrid; ///< 主线程捕获引用,后台只读并深拷贝。
|
|
|
|
int m_nSolverType; ///< PEBI 求解器实现类型。
|
|
|
|
int m_nSolverType; ///< PEBI 求解器实现类型。
|
|
|
|
@ -668,6 +696,167 @@ nmCalculationDllPebiSolverTask::~nmCalculationDllPebiSolverTask()
|
|
|
|
m_pInputSnapshot = nullptr;
|
|
|
|
m_pInputSnapshot = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nmCalculationDllPebiSolverTask::nmCalculationDllPebiSolverTask(
|
|
|
|
|
|
|
|
const nmCalculationDllPebiSolverTask& source, QObject* parent)
|
|
|
|
|
|
|
|
: QThread(parent), m_pDataManager(nullptr),
|
|
|
|
|
|
|
|
m_pInputSnapshot(new nmPebiSolverInputSnapshot(*source.m_pInputSnapshot)),
|
|
|
|
|
|
|
|
m_pManualCaptureState(nullptr), m_bManagerUseActive(false),
|
|
|
|
|
|
|
|
m_bInputSnapshotValid(source.m_bInputSnapshotValid),
|
|
|
|
|
|
|
|
m_nGridInputRevision(source.m_nGridInputRevision),
|
|
|
|
|
|
|
|
m_nResultInputRevision(source.m_nResultInputRevision),
|
|
|
|
|
|
|
|
m_lastRunSucceeded(false), m_nSolveTimeMs(-1), m_nPebiCount(-1),
|
|
|
|
|
|
|
|
m_sAutoFitTargetWellCode(source.m_sAutoFitTargetWellCode),
|
|
|
|
|
|
|
|
m_dPendingScalarMin(0.0), m_dPendingScalarMax(0.0),
|
|
|
|
|
|
|
|
m_bPendingFullResultReady(false), m_nCancelRequested(0), m_nWasCancelled(0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 采样任务只持有自己的值快照;源任务和分析窗口可独立释放。
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nmCalculationDllPebiSolverTask* nmCalculationDllPebiSolverTask::createSensitivityRun(
|
|
|
|
|
|
|
|
const nmDataSensitive& sampling, quint64 runIndex, QString* errorMessage) const
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 从未运行的目标井快照复制输入,禁止采样过程回写原始模型。
|
|
|
|
|
|
|
|
if(errorMessage) errorMessage->clear();
|
|
|
|
|
|
|
|
if(isRunning() || !m_bInputSnapshotValid || !m_pInputSnapshot ||
|
|
|
|
|
|
|
|
!m_pInputSnapshot->m_bAutoFitTargetOnly ||
|
|
|
|
|
|
|
|
m_pInputSnapshot->m_oSensitivityBaseline.m_nTarget >= 0) {
|
|
|
|
|
|
|
|
if(errorMessage) *errorMessage = m_sFailureReason.isEmpty()
|
|
|
|
|
|
|
|
? tr("Solver input is unavailable.") : m_sFailureReason;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!m_pInputSnapshot->m_oGridResult.m_bSucceeded ||
|
|
|
|
|
|
|
|
m_pInputSnapshot->m_bRequiresGridCalculation) {
|
|
|
|
|
|
|
|
if(errorMessage) *errorMessage = tr("Generate a valid base model before running sensitivity.");
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QScopedPointer<nmCalculationDllPebiSolverTask> task(
|
|
|
|
|
|
|
|
new nmCalculationDllPebiSolverTask(*this, nullptr));
|
|
|
|
|
|
|
|
nmPebiGridInputSnapshot& grid = task->m_pInputSnapshot->m_oGridInput;
|
|
|
|
|
|
|
|
nmPebiStaticPropertyCalculationInput& properties = task->m_pInputSnapshot->m_oStaticPropertyInput;
|
|
|
|
|
|
|
|
int target = -1;
|
|
|
|
|
|
|
|
for(int i = 0; i < grid.m_vecSolverWellOrder.size() && i < grid.m_vecWellInputs.size(); ++i) {
|
|
|
|
|
|
|
|
if(grid.m_vecSolverWellOrder[i].m_sWellCode == m_sAutoFitTargetWellCode &&
|
|
|
|
|
|
|
|
grid.m_vecWellInputs[i].m_bRealWell) { target = i; break; }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(target < 0 || target >= int(grid.m_oScene.CS.C.size()) ||
|
|
|
|
|
|
|
|
target >= int(grid.m_oScene.CS.S.size())) {
|
|
|
|
|
|
|
|
if(errorMessage) *errorMessage = tr("Solver input is unavailable.");
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 首次创建时保存真实输入基准,后续切换采样参数也不会继承上一组的值。
|
|
|
|
|
|
|
|
nmPebiSolverInputSnapshot::SensitivityBaseline& baseline =
|
|
|
|
|
|
|
|
task->m_pInputSnapshot->m_oSensitivityBaseline;
|
|
|
|
|
|
|
|
baseline.m_nTarget = target;
|
|
|
|
|
|
|
|
baseline.m_dSceneSkin = grid.m_oScene.CS.S[target];
|
|
|
|
|
|
|
|
baseline.m_dSceneStorage = grid.m_oScene.CS.C[target];
|
|
|
|
|
|
|
|
baseline.m_dWellSkin = grid.m_vecWellInputs[target].m_dSkin;
|
|
|
|
|
|
|
|
baseline.m_dWellStorage = grid.m_vecWellInputs[target].m_dWellboreStorage;
|
|
|
|
|
|
|
|
baseline.m_dPressure = grid.m_oScene.Base.Pi;
|
|
|
|
|
|
|
|
baseline.m_dScenePermeability = grid.m_oScene.Base.k_ref;
|
|
|
|
|
|
|
|
baseline.m_dSceneThickness = grid.m_oScene.Base.h_ref;
|
|
|
|
|
|
|
|
baseline.m_dScenePorosity = grid.m_oScene.Base.phi_ref;
|
|
|
|
|
|
|
|
baseline.m_dReferencePermeability = properties.m_dReferencePermeabilityMilliDarcy;
|
|
|
|
|
|
|
|
baseline.m_dReferenceThickness = properties.m_dReferenceThicknessMeters;
|
|
|
|
|
|
|
|
baseline.m_dReferencePorosity = properties.m_dReferencePorosity;
|
|
|
|
|
|
|
|
if(!task->updateSensitivityRun(sampling, runIndex, errorMessage)) return nullptr;
|
|
|
|
|
|
|
|
return task.take();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool nmCalculationDllPebiSolverTask::updateSensitivityRun(
|
|
|
|
|
|
|
|
const nmDataSensitive& sampling, quint64 runIndex, QString* errorMessage)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if(errorMessage) errorMessage->clear();
|
|
|
|
|
|
|
|
// 更新必须发生在两次求解之间,不能用于原始任务或实时管理器输入。
|
|
|
|
|
|
|
|
if(isRunning() || m_pDataManager != nullptr || !m_pInputSnapshot ||
|
|
|
|
|
|
|
|
!m_pInputSnapshot->m_bAutoFitTargetOnly ||
|
|
|
|
|
|
|
|
m_pInputSnapshot->m_oSensitivityBaseline.m_nTarget < 0) {
|
|
|
|
|
|
|
|
if(errorMessage) *errorMessage = tr("Sensitivity task is unavailable or still running.");
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 即使本组配置无效,也不能将上次的成功状态或曲线当成本组结果。
|
|
|
|
|
|
|
|
m_bInputSnapshotValid = false;
|
|
|
|
|
|
|
|
discardPendingFullResult();
|
|
|
|
|
|
|
|
m_lastRunSucceeded = false;
|
|
|
|
|
|
|
|
m_sFailureReason.clear();
|
|
|
|
|
|
|
|
m_nSolveTimeMs = -1;
|
|
|
|
|
|
|
|
m_nPebiCount = -1;
|
|
|
|
|
|
|
|
m_nCancelRequested.fetchAndStoreOrdered(0);
|
|
|
|
|
|
|
|
m_nWasCancelled.fetchAndStoreOrdered(0);
|
|
|
|
|
|
|
|
QVector<double> values;
|
|
|
|
|
|
|
|
if(!sampling.usesBaseUnits() || !sampling.getRunValues(runIndex, values)) {
|
|
|
|
|
|
|
|
fail(tr("Invalid sensitivity sampling configuration."));
|
|
|
|
|
|
|
|
if(errorMessage) *errorMessage = m_sFailureReason;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 在小型标量副本上恢复基准并校验,全部通过后一次写入,避免半更新输入。
|
|
|
|
|
|
|
|
nmPebiSolverInputSnapshot::SensitivityBaseline parameters =
|
|
|
|
|
|
|
|
m_pInputSnapshot->m_oSensitivityBaseline;
|
|
|
|
|
|
|
|
nmPebiGridInputSnapshot& grid = m_pInputSnapshot->m_oGridInput;
|
|
|
|
|
|
|
|
nmPebiStaticPropertyCalculationInput& properties = m_pInputSnapshot->m_oStaticPropertyInput;
|
|
|
|
|
|
|
|
const QList<nmDataSensitive::VariableSampling>& variables = sampling.getVariables();
|
|
|
|
|
|
|
|
bool valid = values.size() == variables.size();
|
|
|
|
|
|
|
|
for(int i = 0; valid && i < variables.size(); ++i) {
|
|
|
|
|
|
|
|
const nmDataSensitive::VariableSampling& variable = variables[i];
|
|
|
|
|
|
|
|
if(!variable.m_enabled) continue;
|
|
|
|
|
|
|
|
const QString& name = variable.m_varName;
|
|
|
|
|
|
|
|
const double value = values[i];
|
|
|
|
|
|
|
|
valid = isFiniteSolverNumber(value);
|
|
|
|
|
|
|
|
if(!valid) break;
|
|
|
|
|
|
|
|
// 井筒参数按目标井槽位写入,储层参数同步到场景和静态属性计算输入。
|
|
|
|
|
|
|
|
if(name == "Skin") {
|
|
|
|
|
|
|
|
valid = value >= 0.0;
|
|
|
|
|
|
|
|
parameters.m_dSceneSkin = value;
|
|
|
|
|
|
|
|
parameters.m_dWellSkin = value;
|
|
|
|
|
|
|
|
} else if(name == "C") {
|
|
|
|
|
|
|
|
valid = value >= 0.0;
|
|
|
|
|
|
|
|
parameters.m_dSceneStorage = value;
|
|
|
|
|
|
|
|
parameters.m_dWellStorage = value;
|
|
|
|
|
|
|
|
} else if(name == "Pi") {
|
|
|
|
|
|
|
|
valid = value > 0.0;
|
|
|
|
|
|
|
|
parameters.m_dPressure = value;
|
|
|
|
|
|
|
|
} else if(name == "k" || name == "h" || name == QString(QChar(0x03c6))) {
|
|
|
|
|
|
|
|
valid = value > 0.0 && (name != QString(QChar(0x03c6)) || value <= 1.0);
|
|
|
|
|
|
|
|
const QString property = name == QString(QChar(0x03c6)) ? "phi" : name;
|
|
|
|
|
|
|
|
// 非均质插值会覆盖参考值,不能将这种运行误报为参数已变化。
|
|
|
|
|
|
|
|
for(int field = 0; valid && field < properties.m_vecDataSets.size(); ++field) {
|
|
|
|
|
|
|
|
const nmPropertyInterpolationDataSet& dataSet = properties.m_vecDataSets[field];
|
|
|
|
|
|
|
|
if(dataSet.useForCalculation && dataSet.property == property) valid = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(name == "k") {
|
|
|
|
|
|
|
|
parameters.m_dReferencePermeability = value;
|
|
|
|
|
|
|
|
parameters.m_dScenePermeability = nmCalculationUtils::milliDarcyToDarcy(value);
|
|
|
|
|
|
|
|
} else if(name == "h") {
|
|
|
|
|
|
|
|
parameters.m_dReferenceThickness = value;
|
|
|
|
|
|
|
|
parameters.m_dSceneThickness = value;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
parameters.m_dReferencePorosity = value;
|
|
|
|
|
|
|
|
parameters.m_dScenePorosity = value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else valid = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!valid) {
|
|
|
|
|
|
|
|
fail(tr("The sampled parameters are invalid or use a non-uniform field."));
|
|
|
|
|
|
|
|
if(errorMessage) *errorMessage = m_sFailureReason;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const int target = parameters.m_nTarget;
|
|
|
|
|
|
|
|
grid.m_oScene.CS.S[target] = parameters.m_dSceneSkin;
|
|
|
|
|
|
|
|
grid.m_oScene.CS.C[target] = parameters.m_dSceneStorage;
|
|
|
|
|
|
|
|
grid.m_vecWellInputs[target].m_dSkin = parameters.m_dWellSkin;
|
|
|
|
|
|
|
|
grid.m_vecWellInputs[target].m_dWellboreStorage = parameters.m_dWellStorage;
|
|
|
|
|
|
|
|
grid.m_oScene.Base.Pi = parameters.m_dPressure;
|
|
|
|
|
|
|
|
grid.m_oScene.Base.k_ref = parameters.m_dScenePermeability;
|
|
|
|
|
|
|
|
grid.m_oScene.Base.h_ref = parameters.m_dSceneThickness;
|
|
|
|
|
|
|
|
grid.m_oScene.Base.phi_ref = parameters.m_dScenePorosity;
|
|
|
|
|
|
|
|
properties.m_dReferencePermeabilityMilliDarcy = parameters.m_dReferencePermeability;
|
|
|
|
|
|
|
|
properties.m_dReferenceThicknessMeters = parameters.m_dReferenceThickness;
|
|
|
|
|
|
|
|
properties.m_dReferencePorosity = parameters.m_dReferencePorosity;
|
|
|
|
|
|
|
|
m_bInputSnapshotValid = true;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void nmCalculationDllPebiSolverTask::run()
|
|
|
|
void nmCalculationDllPebiSolverTask::run()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_nSolveTimeMs = -1;
|
|
|
|
m_nSolveTimeMs = -1;
|
|
|
|
|