|
|
|
|
@ -15,6 +15,7 @@
|
|
|
|
|
#include "nmDataFault.h"
|
|
|
|
|
#include "nmDataTimeStepSetting.h"
|
|
|
|
|
#include "nmPebiResultSnapshotBuilder.h"
|
|
|
|
|
#include "nmPebiStaticPropertyCalculator.h"
|
|
|
|
|
|
|
|
|
|
#include "nmCalculationPebiGrid.h"
|
|
|
|
|
#include "nmCalculationUtils.h"
|
|
|
|
|
@ -35,6 +36,7 @@
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <float.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
#include <vtkSmartPointer.h>
|
|
|
|
|
@ -61,7 +63,8 @@ struct nmPebiSolverInputSnapshot
|
|
|
|
|
|
|
|
|
|
nmPebiGridInputSnapshot m_oGridInput; ///< 网格、场景、井顺序和授权路径值快照。
|
|
|
|
|
nmPebiGridResult m_oGridResult; ///< 已有网格副本或后台新生成的局部网格。
|
|
|
|
|
QVector<nmPropertyInterpolationDataSet> m_vecPropertyDataSets; ///< 属性插值值副本。
|
|
|
|
|
/** @brief 在 DataManager 线程冻结的静态属性参考值、插值数据和授权路径。 */
|
|
|
|
|
nmPebiStaticPropertyCalculationInput m_oStaticPropertyInput;
|
|
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> m_pBaseGrid; ///< 完整成果使用的基础 VTK 网格。
|
|
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> m_pSourceBaseGrid; ///< 主线程捕获引用,后台只读并深拷贝。
|
|
|
|
|
int m_nSolverType; ///< PEBI 求解器实现类型。
|
|
|
|
|
@ -258,10 +261,21 @@ bool captureResultWellMetadata(
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool captureResultParameters(
|
|
|
|
|
/**
|
|
|
|
|
* @brief 从同一个储层对象冻结静态属性输入及可选的正式结果储层参数。
|
|
|
|
|
*
|
|
|
|
|
* 自动拟合也必须捕获静态属性输入,但不需要正式结果元数据。只有全部字段都复制
|
|
|
|
|
* 成功后才设置 m_bCaptured,防止默认构造的零值被误认为有效业务输入。
|
|
|
|
|
*/
|
|
|
|
|
bool captureStaticPropertyInput(
|
|
|
|
|
nmDataAnalyzeManager* pDataManager,
|
|
|
|
|
nmPebiSolverInputSnapshot& oSnapshot)
|
|
|
|
|
nmPebiSolverInputSnapshot& oSnapshot,
|
|
|
|
|
bool bCaptureResultReservoir,
|
|
|
|
|
nmDataReservoir** ppReservoir)
|
|
|
|
|
{
|
|
|
|
|
if(ppReservoir != NULL) {
|
|
|
|
|
*ppReservoir = NULL;
|
|
|
|
|
}
|
|
|
|
|
if(pDataManager == NULL ||
|
|
|
|
|
QThread::currentThread() != pDataManager->thread()) {
|
|
|
|
|
return false;
|
|
|
|
|
@ -272,26 +286,61 @@ bool captureResultParameters(
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nmPebiResultReservoirParameters& oReservoir =
|
|
|
|
|
oSnapshot.m_oResultReservoirParameters;
|
|
|
|
|
oReservoir.m_dInitialPressure = pReservoir->getInitialPressure()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dPermeability = pReservoir->getPermeability()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dThickness = pReservoir->getThickness()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dPorosity = pReservoir->getPorosity()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dTotalCompressibility = pReservoir->getCt()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dRockCompressibility = pReservoir->getCf()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dOilSaturation = pReservoir->getSoi()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dGasSaturation = pReservoir->getSgi()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dWaterSaturation = pReservoir->getSwi()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
nmPebiStaticPropertyCalculationInput oStaticInput;
|
|
|
|
|
// 数据层保持显示和持久化基准单位;渗透率只在组装求解器 Base.k 时转成 D。
|
|
|
|
|
oStaticInput.m_dReferencePermeabilityMilliDarcy =
|
|
|
|
|
pReservoir->getPermeability().getValue().toDouble();
|
|
|
|
|
oStaticInput.m_dReferencePorosity =
|
|
|
|
|
pReservoir->getPorosity().getValue().toDouble();
|
|
|
|
|
oStaticInput.m_dReferenceThicknessMeters =
|
|
|
|
|
pReservoir->getThickness().getValue().toDouble();
|
|
|
|
|
oStaticInput.m_vecDataSets =
|
|
|
|
|
pDataManager->getPropertyInterpolationDataSets();
|
|
|
|
|
// 授权路径随网格输入最先冻结;Kriging、建网和模型求解必须复用同一份值,
|
|
|
|
|
// 不能在分阶段捕获期间从 DataManager 再读出另一个时刻的路径。
|
|
|
|
|
oStaticInput.m_sLicensePath =
|
|
|
|
|
oSnapshot.m_oGridInput.m_sLicensePath;
|
|
|
|
|
oStaticInput.m_bCaptured = true;
|
|
|
|
|
|
|
|
|
|
if(bCaptureResultReservoir) {
|
|
|
|
|
nmPebiResultReservoirParameters oReservoir;
|
|
|
|
|
oReservoir.m_dInitialPressure = pReservoir->getInitialPressure()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dPermeability =
|
|
|
|
|
oStaticInput.m_dReferencePermeabilityMilliDarcy;
|
|
|
|
|
oReservoir.m_dThickness =
|
|
|
|
|
oStaticInput.m_dReferenceThicknessMeters;
|
|
|
|
|
oReservoir.m_dPorosity = oStaticInput.m_dReferencePorosity;
|
|
|
|
|
oReservoir.m_dTotalCompressibility = pReservoir->getCt()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dRockCompressibility = pReservoir->getCf()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dOilSaturation = pReservoir->getSoi()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dGasSaturation = pReservoir->getSgi()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oReservoir.m_dWaterSaturation = pReservoir->getSwi()
|
|
|
|
|
.getValue().toDouble();
|
|
|
|
|
oSnapshot.m_oResultReservoirParameters = oReservoir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oSnapshot.m_oStaticPropertyInput = oStaticInput;
|
|
|
|
|
if(ppReservoir != NULL) {
|
|
|
|
|
*ppReservoir = pReservoir;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @brief 捕获完整结果专用的 PVT 和求解设置,不重复查询储层对象。 */
|
|
|
|
|
bool captureResultParameters(
|
|
|
|
|
nmDataAnalyzeManager* pDataManager,
|
|
|
|
|
nmDataReservoir* pReservoir,
|
|
|
|
|
nmPebiSolverInputSnapshot& oSnapshot)
|
|
|
|
|
{
|
|
|
|
|
if(pDataManager == NULL || pReservoir == NULL ||
|
|
|
|
|
QThread::currentThread() != pDataManager->thread()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nmPebiResultPvtParameters& oPvt =
|
|
|
|
|
oSnapshot.m_oResultPvtParameters;
|
|
|
|
|
@ -359,122 +408,54 @@ bool captureResultParameters(
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将启用的数据组插值到全部网格单元中心,并覆盖对应的求解器属性数组.
|
|
|
|
|
bool applyPropertyInterpolation(
|
|
|
|
|
HX_NWTM_MODEL_INPUT& modelInput,
|
|
|
|
|
const QVector<nmPropertyInterpolationDataSet>& dataSets,
|
|
|
|
|
const QString& licensePath,
|
|
|
|
|
QString& errorMessage,
|
|
|
|
|
/**
|
|
|
|
|
* @brief 校验 DLL 三个原始单元数组共享的结构索引域并提取属性计算位置。
|
|
|
|
|
*
|
|
|
|
|
* 数量一致只能证明三个数组可按共同下标访问。下标 i 是否表示同一个求解单元
|
|
|
|
|
* 由 DLL 接口契约保证,应用层不重新排序,也不把 Trinodexy[i] 宣称为几何质心。
|
|
|
|
|
*/
|
|
|
|
|
bool extractStaticPropertyLocations(
|
|
|
|
|
const HX_NWTM_GRID_OUTPUT1& oGridOutput1,
|
|
|
|
|
const HX_NWTM_GRID_OUTPUT2& oGridOutput2,
|
|
|
|
|
QVector<QPointF>& vecLocations,
|
|
|
|
|
QString& sErrorMessage,
|
|
|
|
|
const QAtomicInt* pCancelRequested)
|
|
|
|
|
{
|
|
|
|
|
bool hasEnabledDataSet = false;
|
|
|
|
|
for(int i = 0; i < dataSets.size(); ++i) {
|
|
|
|
|
if(dataSets[i].useForCalculation) {
|
|
|
|
|
hasEnabledDataSet = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!hasEnabledDataSet) {
|
|
|
|
|
return true;
|
|
|
|
|
vecLocations.clear();
|
|
|
|
|
sErrorMessage.clear();
|
|
|
|
|
|
|
|
|
|
const size_t nLocationCount = oGridOutput2.Trinodexy.size();
|
|
|
|
|
const size_t nIsPlotCount = oGridOutput1.PEBI_cell.isplot.size();
|
|
|
|
|
const size_t nPolygonIndexCount = oGridOutput1.PEBI_cell.pindex.size();
|
|
|
|
|
if(nLocationCount == 0 ||
|
|
|
|
|
nLocationCount != nIsPlotCount ||
|
|
|
|
|
nLocationCount != nPolygonIndexCount ||
|
|
|
|
|
nLocationCount > static_cast<size_t>(INT_MAX)) {
|
|
|
|
|
sErrorMessage = QLatin1String(
|
|
|
|
|
"PEBI property index arrays are empty or inconsistent.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVector<QPointF> targetPoints;
|
|
|
|
|
targetPoints.reserve(static_cast<int>(modelInput.GRID.Trinodexy.size()));
|
|
|
|
|
for(size_t cellIndex = 0;
|
|
|
|
|
cellIndex < modelInput.GRID.Trinodexy.size(); ++cellIndex) {
|
|
|
|
|
if((cellIndex % 256) == 0 &&
|
|
|
|
|
vecLocations.reserve(static_cast<int>(nLocationCount));
|
|
|
|
|
for(size_t nIndex = 0; nIndex < nLocationCount; ++nIndex) {
|
|
|
|
|
if((nIndex % 256) == 0 &&
|
|
|
|
|
isTaskCancellationRequested(pCancelRequested)) {
|
|
|
|
|
vecLocations.clear();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const dVec1& cellPosition = modelInput.GRID.Trinodexy[cellIndex];
|
|
|
|
|
if(cellPosition.size() < 2) {
|
|
|
|
|
errorMessage = QString("Grid cell %1 has no valid center coordinate.")
|
|
|
|
|
.arg(static_cast<qulonglong>(cellIndex));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
targetPoints.append(QPointF(cellPosition[0], cellPosition[1]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool kApplied = false;
|
|
|
|
|
bool phiApplied = false;
|
|
|
|
|
bool hApplied = false;
|
|
|
|
|
for(int dataSetIndex = 0; dataSetIndex < dataSets.size(); ++dataSetIndex) {
|
|
|
|
|
if(isTaskCancellationRequested(pCancelRequested)) {
|
|
|
|
|
// Trinodexy 提供属性计算位置;本阶段只读取 X/Y,不涉及 TRI_cell。
|
|
|
|
|
const dVec1& vecPosition = oGridOutput2.Trinodexy[nIndex];
|
|
|
|
|
if(vecPosition.size() < 2 ||
|
|
|
|
|
!isFiniteSolverNumber(vecPosition[0]) ||
|
|
|
|
|
!isFiniteSolverNumber(vecPosition[1])) {
|
|
|
|
|
sErrorMessage = QString::fromLatin1(
|
|
|
|
|
"PEBI property location %1 is invalid.")
|
|
|
|
|
.arg(static_cast<qulonglong>(nIndex));
|
|
|
|
|
vecLocations.clear();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const nmPropertyInterpolationDataSet& dataSet = dataSets[dataSetIndex];
|
|
|
|
|
if(!dataSet.useForCalculation) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dVec1* solverValues = NULL;
|
|
|
|
|
bool* propertyApplied = NULL;
|
|
|
|
|
if(dataSet.property == "k") {
|
|
|
|
|
solverValues = &modelInput.Base.k;
|
|
|
|
|
propertyApplied = &kApplied;
|
|
|
|
|
} else if(dataSet.property == "phi") {
|
|
|
|
|
solverValues = &modelInput.Base.phi;
|
|
|
|
|
propertyApplied = &phiApplied;
|
|
|
|
|
} else if(dataSet.property == "h") {
|
|
|
|
|
solverValues = &modelInput.Base.h;
|
|
|
|
|
propertyApplied = &hApplied;
|
|
|
|
|
} else {
|
|
|
|
|
errorMessage = QString("Dataset '%1' has an unknown property.")
|
|
|
|
|
.arg(dataSet.name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(*propertyApplied) {
|
|
|
|
|
errorMessage = QString(
|
|
|
|
|
"More than one dataset is enabled for property %1.")
|
|
|
|
|
.arg(dataSet.property);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVector<QPointF> measurementPoints;
|
|
|
|
|
QVector<double> measurementValues;
|
|
|
|
|
measurementPoints.reserve(dataSet.points.size());
|
|
|
|
|
measurementValues.reserve(dataSet.points.size());
|
|
|
|
|
for(int pointIndex = 0; pointIndex < dataSet.points.size(); ++pointIndex) {
|
|
|
|
|
const nmPropertyInterpolationPointData& point = dataSet.points[pointIndex];
|
|
|
|
|
measurementPoints.append(QPointF(point.x, point.y));
|
|
|
|
|
measurementValues.append(point.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVector<double> interpolationValues;
|
|
|
|
|
QString calculationError;
|
|
|
|
|
if(!nmCalculationUtils::calculateKriging(
|
|
|
|
|
targetPoints,
|
|
|
|
|
measurementPoints,
|
|
|
|
|
measurementValues,
|
|
|
|
|
dataSet.nugget,
|
|
|
|
|
dataSet.sill,
|
|
|
|
|
dataSet.range,
|
|
|
|
|
dataSet.model,
|
|
|
|
|
licensePath,
|
|
|
|
|
interpolationValues,
|
|
|
|
|
&calculationError,
|
|
|
|
|
pCancelRequested)) {
|
|
|
|
|
errorMessage = QString("Dataset '%1': %2")
|
|
|
|
|
.arg(dataSet.name)
|
|
|
|
|
.arg(calculationError);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
solverValues->resize(interpolationValues.size());
|
|
|
|
|
for(int valueIndex = 0; valueIndex < interpolationValues.size(); ++valueIndex) {
|
|
|
|
|
if((valueIndex % 256) == 0 &&
|
|
|
|
|
isTaskCancellationRequested(pCancelRequested)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 属性插值数据层以mD保存;渗透率在写入PEBI数组时转换为D。
|
|
|
|
|
(*solverValues)[valueIndex] = dataSet.property == "k"
|
|
|
|
|
? nmCalculationUtils::milliDarcyToDarcy(
|
|
|
|
|
interpolationValues[valueIndex])
|
|
|
|
|
: interpolationValues[valueIndex];
|
|
|
|
|
}
|
|
|
|
|
*propertyApplied = true;
|
|
|
|
|
vecLocations.append(QPointF(vecPosition[0], vecPosition[1]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
@ -485,7 +466,9 @@ bool applyPropertyInterpolation(
|
|
|
|
|
*/
|
|
|
|
|
bool buildModelInputFromSnapshot(
|
|
|
|
|
const nmPebiSolverInputSnapshot& oSnapshot,
|
|
|
|
|
const QVector<QPointF>& vecPropertyLocations,
|
|
|
|
|
HX_NWTM_MODEL_INPUT& oModelInput,
|
|
|
|
|
nmPebiStaticPropertyValues& oStaticProperties,
|
|
|
|
|
QString& sErrorMessage,
|
|
|
|
|
const QAtomicInt* pCancelRequested)
|
|
|
|
|
{
|
|
|
|
|
@ -535,7 +518,7 @@ bool buildModelInputFromSnapshot(
|
|
|
|
|
oModelInput.PVT.Sw = oScene.PVT.Sw;
|
|
|
|
|
oModelInput.PVT.Krw = oScene.PVT.Krw;
|
|
|
|
|
|
|
|
|
|
// 第三步:先用储层参考值填满全部网格单元,再按捕获的属性数据组覆盖。
|
|
|
|
|
// 第三步:恢复与静态属性无关的储层和时间步参数。
|
|
|
|
|
oModelInput.Base.Pi = oScene.Base.Pi;
|
|
|
|
|
oModelInput.Base.Cti = oScene.Base.Cti;
|
|
|
|
|
oModelInput.Base.Cf = oScene.Base.Cf;
|
|
|
|
|
@ -546,17 +529,45 @@ bool buildModelInputFromSnapshot(
|
|
|
|
|
oModelInput.Base.dt_Min = oScene.Base.dt_Min;
|
|
|
|
|
oModelInput.Base.dt_Max = oScene.Base.dt_Max;
|
|
|
|
|
|
|
|
|
|
const size_t nCellCount = oModelInput.GRID.Trinodexy.size();
|
|
|
|
|
oModelInput.Base.k = dVec1(nCellCount, oScene.Base.k_ref);
|
|
|
|
|
oModelInput.Base.phi = dVec1(nCellCount, oScene.Base.phi_ref);
|
|
|
|
|
oModelInput.Base.h = dVec1(nCellCount, oScene.Base.h_ref);
|
|
|
|
|
|
|
|
|
|
return applyPropertyInterpolation(
|
|
|
|
|
oModelInput,
|
|
|
|
|
oSnapshot.m_vecPropertyDataSets,
|
|
|
|
|
oSnapshot.m_oGridInput.m_sLicensePath,
|
|
|
|
|
sErrorMessage,
|
|
|
|
|
// 第四步:Calculator 只生成原始单元顺序的业务值,不接触 DLL 模型结构。
|
|
|
|
|
const nmPebiStaticPropertyCalculationStatus eStatus =
|
|
|
|
|
nmPebiStaticPropertyCalculator::calculate(
|
|
|
|
|
vecPropertyLocations,
|
|
|
|
|
oSnapshot.m_oStaticPropertyInput,
|
|
|
|
|
oStaticProperties,
|
|
|
|
|
&sErrorMessage,
|
|
|
|
|
pCancelRequested);
|
|
|
|
|
if(eStatus != NM_PebiStaticPropertyCalculation_Succeeded) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int nCellCount = vecPropertyLocations.size();
|
|
|
|
|
dVec1 vecPermeability(static_cast<size_t>(nCellCount));
|
|
|
|
|
dVec1 vecPorosity(static_cast<size_t>(nCellCount));
|
|
|
|
|
dVec1 vecThickness(static_cast<size_t>(nCellCount));
|
|
|
|
|
for(int nIndex = 0; nIndex < nCellCount; ++nIndex) {
|
|
|
|
|
if((nIndex % 256) == 0 &&
|
|
|
|
|
isTaskCancellationRequested(pCancelRequested)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 静态属性层统一保存 mD;求解器 Base.k 的唯一写入边界转换为 D。
|
|
|
|
|
vecPermeability[static_cast<size_t>(nIndex)] =
|
|
|
|
|
nmCalculationUtils::milliDarcyToDarcy(
|
|
|
|
|
oStaticProperties.m_vecPermeabilityMilliDarcy[nIndex]);
|
|
|
|
|
vecPorosity[static_cast<size_t>(nIndex)] =
|
|
|
|
|
oStaticProperties.m_vecPorosity[nIndex];
|
|
|
|
|
vecThickness[static_cast<size_t>(nIndex)] =
|
|
|
|
|
oStaticProperties.m_vecThicknessMeters[nIndex];
|
|
|
|
|
}
|
|
|
|
|
if(isTaskCancellationRequested(pCancelRequested)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 三个临时数组全部完成后再一起交换,失败和取消不会留下半套求解输入。
|
|
|
|
|
oModelInput.Base.k.swap(vecPermeability);
|
|
|
|
|
oModelInput.Base.phi.swap(vecPorosity);
|
|
|
|
|
oModelInput.Base.h.swap(vecThickness);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isReasonableLogLogValue(double value)
|
|
|
|
|
@ -688,6 +699,11 @@ void nmCalculationDllPebiSolverTask::run()
|
|
|
|
|
discardPendingFullResult();
|
|
|
|
|
bSucceeded = false;
|
|
|
|
|
}
|
|
|
|
|
if(!bSucceeded) {
|
|
|
|
|
// execute() 的所有普通失败也必须释放压力帧、网格别名和候选快照。
|
|
|
|
|
// 该清理函数允许在取消或异常分支再次调用。
|
|
|
|
|
discardPendingFullResult();
|
|
|
|
|
}
|
|
|
|
|
if(isCancelRequested()) {
|
|
|
|
|
// 停止只丢弃任务局部结果,主线程不会进入 commitResult()。
|
|
|
|
|
m_nWasCancelled.fetchAndStoreOrdered(1);
|
|
|
|
|
@ -899,15 +915,20 @@ bool nmCalculationDllPebiSolverTask::captureManualInputStep(
|
|
|
|
|
if(pState->m_ePhase ==
|
|
|
|
|
nmPebiManualCaptureState::CapturePhase_SolverSettings) {
|
|
|
|
|
reportStage(tr("Capturing solver settings..."), 0, 0);
|
|
|
|
|
m_pInputSnapshot->m_vecPropertyDataSets =
|
|
|
|
|
m_pDataManager->getPropertyInterpolationDataSets();
|
|
|
|
|
m_pInputSnapshot->m_nSolverType =
|
|
|
|
|
m_pDataManager->getPebiSolverType();
|
|
|
|
|
m_pInputSnapshot->m_nOmpThreads =
|
|
|
|
|
m_pDataManager->getPebiOmpThreads();
|
|
|
|
|
m_pInputSnapshot->m_nIluReuseSteps =
|
|
|
|
|
m_pDataManager->getPebiIluReuseSteps();
|
|
|
|
|
if(!captureResultParameters(m_pDataManager,
|
|
|
|
|
nmDataReservoir* pCapturedReservoir = NULL;
|
|
|
|
|
// 手工求解无条件冻结静态输入;正式结果参数复用同一次储层对象读取。
|
|
|
|
|
if(!captureStaticPropertyInput(m_pDataManager,
|
|
|
|
|
*m_pInputSnapshot,
|
|
|
|
|
true,
|
|
|
|
|
&pCapturedReservoir) ||
|
|
|
|
|
!captureResultParameters(m_pDataManager,
|
|
|
|
|
pCapturedReservoir,
|
|
|
|
|
*m_pInputSnapshot)) {
|
|
|
|
|
return fail(tr("Failed to capture solver settings."));
|
|
|
|
|
}
|
|
|
|
|
@ -1059,18 +1080,25 @@ bool nmCalculationDllPebiSolverTask::captureInputSnapshot(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第三步:复制属性插值及 DLL 求解配置。这些设置只影响模型求解结果,
|
|
|
|
|
// 不应在工作线程中再次从 DataManager 查询。
|
|
|
|
|
m_pInputSnapshot->m_vecPropertyDataSets =
|
|
|
|
|
m_pDataManager->getPropertyInterpolationDataSets();
|
|
|
|
|
// 第三步:冻结 DLL 求解配置和静态属性输入。静态输入必须在自动拟合分支
|
|
|
|
|
// 判断之外捕获,使每个粒子任务都读取候选参数写入后的最新储层参考值。
|
|
|
|
|
m_pInputSnapshot->m_nSolverType =
|
|
|
|
|
m_pDataManager->getPebiSolverType();
|
|
|
|
|
m_pInputSnapshot->m_nOmpThreads =
|
|
|
|
|
m_pDataManager->getPebiOmpThreads();
|
|
|
|
|
m_pInputSnapshot->m_nIluReuseSteps =
|
|
|
|
|
m_pDataManager->getPebiIluReuseSteps();
|
|
|
|
|
if(!m_pInputSnapshot->m_bAutoFitTargetOnly &&
|
|
|
|
|
!captureResultParameters(m_pDataManager, *m_pInputSnapshot)) {
|
|
|
|
|
nmDataReservoir* pCapturedReservoir = NULL;
|
|
|
|
|
const bool bCaptureFullResult =
|
|
|
|
|
!m_pInputSnapshot->m_bAutoFitTargetOnly;
|
|
|
|
|
if(!captureStaticPropertyInput(m_pDataManager,
|
|
|
|
|
*m_pInputSnapshot,
|
|
|
|
|
bCaptureFullResult,
|
|
|
|
|
&pCapturedReservoir) ||
|
|
|
|
|
(bCaptureFullResult &&
|
|
|
|
|
!captureResultParameters(m_pDataManager,
|
|
|
|
|
pCapturedReservoir,
|
|
|
|
|
*m_pInputSnapshot))) {
|
|
|
|
|
return fail(tr("Failed to capture solver settings."));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1293,24 +1321,50 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
|
|
|
|
|
: fail(tr("The generated grid is invalid."));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第三步:由场景值快照重建完整模型输入。属性插值可以耗时,但它只读取任务
|
|
|
|
|
// 自己的点集副本,因此放在工作线程不会阻塞或竞争界面数据。
|
|
|
|
|
// 第三步:先验证 DLL 原始单元数组的共同结构索引域。自动拟合不创建 VTK,
|
|
|
|
|
// 因此这项约束必须位于求解任务内,不能只依赖 VTK 转换路径。
|
|
|
|
|
QVector<QPointF> vecPropertyLocations;
|
|
|
|
|
QString sPreparationError;
|
|
|
|
|
if(!extractStaticPropertyLocations(oGridResult.m_oGridOutput1,
|
|
|
|
|
oGridResult.m_oGridOutput2,
|
|
|
|
|
vecPropertyLocations,
|
|
|
|
|
sPreparationError,
|
|
|
|
|
pCancelRequested)) {
|
|
|
|
|
if(isCancelRequested()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const QString sLogMessage =
|
|
|
|
|
QString::fromLatin1("Invalid PEBI property index domain: %1")
|
|
|
|
|
.arg(sPreparationError);
|
|
|
|
|
qWarning() << sLogMessage;
|
|
|
|
|
zxLogInstance::getInstance()->writeLogF(sLogMessage);
|
|
|
|
|
return fail(tr("The generated grid is invalid."));
|
|
|
|
|
}
|
|
|
|
|
if(isCancelRequested()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第四步:由任务值快照重建完整模型输入。静态属性计算只读取任务自己的
|
|
|
|
|
// 位置和测点副本,因此工作线程不会访问或竞争 DataManager 对象。
|
|
|
|
|
// 直接初始化可避免先构造临时模型输入、再复制完整 GRID 的额外内存峰值。
|
|
|
|
|
HX_NWTM_MODEL_INPUT oModelInput(oGridResult.m_oGridOutput2);
|
|
|
|
|
QString sInterpolationError;
|
|
|
|
|
nmPebiStaticPropertyValues oStaticProperties;
|
|
|
|
|
reportStage(tr("Preparing model input..."), 0, 0);
|
|
|
|
|
if(!buildModelInputFromSnapshot(*m_pInputSnapshot,
|
|
|
|
|
vecPropertyLocations,
|
|
|
|
|
oModelInput,
|
|
|
|
|
sInterpolationError,
|
|
|
|
|
oStaticProperties,
|
|
|
|
|
sPreparationError,
|
|
|
|
|
pCancelRequested)) {
|
|
|
|
|
if(isCancelRequested()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const QString sLogMessage =
|
|
|
|
|
QString("Property interpolation failed: %1")
|
|
|
|
|
.arg(sInterpolationError);
|
|
|
|
|
QString::fromLatin1("Static property preparation failed: %1")
|
|
|
|
|
.arg(sPreparationError);
|
|
|
|
|
qWarning() << sLogMessage;
|
|
|
|
|
zxLogInstance::getInstance()->writeLogF(sLogMessage);
|
|
|
|
|
// 沿用既有已翻译提示;详细原因已经写入日志,不扩展本阶段 UI 文案。
|
|
|
|
|
return fail(tr("Property interpolation failed."));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -1318,7 +1372,8 @@ bool nmCalculationDllPebiSolverTask::execPebiMode()
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 第四步:建网、模型求解和 Kriging 共用 DLL 全局状态,整个 DLL 调用必须串行。
|
|
|
|
|
// 第五步:模型求解与前面的建网、Kriging 共用 DLL 全局状态。Kriging 已在
|
|
|
|
|
// calculateKriging() 内独立持锁,这里只串行保护模型求解入口。
|
|
|
|
|
reportStage(tr("Waiting for solver..."), 0, 0);
|
|
|
|
|
nmSolverDllMutexLocker oDllLocker;
|
|
|
|
|
if(!oDllLocker.lock(nmCalculationUtils::getHxNwtmDllMutex(),
|
|
|
|
|
@ -1910,11 +1965,13 @@ void nmCalculationDllPebiSolverTask::discardPendingFullResult()
|
|
|
|
|
m_pPendingResultSnapshot.clear();
|
|
|
|
|
m_vecPendingWellResults.clear();
|
|
|
|
|
m_mapPendingTimeSteps.clear();
|
|
|
|
|
m_autoFitResultPressure.clear();
|
|
|
|
|
m_autoFitResultLogLog.clear();
|
|
|
|
|
m_autoFitResultSemiLog.clear();
|
|
|
|
|
m_bPendingFullResultReady = false;
|
|
|
|
|
|
|
|
|
|
// 候选可能与输入基础网格共享 VTK 对象,丢弃或发布后必须清除任务侧别名。
|
|
|
|
|
if(m_pInputSnapshot != NULL &&
|
|
|
|
|
!m_pInputSnapshot->m_bAutoFitTargetOnly) {
|
|
|
|
|
if(m_pInputSnapshot != NULL) {
|
|
|
|
|
m_pInputSnapshot->m_pBaseGrid = NULL;
|
|
|
|
|
m_pInputSnapshot->m_pSourceBaseGrid = NULL;
|
|
|
|
|
m_pInputSnapshot->m_oGridResult.m_pUnstructuredGrid = NULL;
|
|
|
|
|
|