# include " nmCalculationPebiGrid.h "
# include <Windows.h>
# include <QDebug>
# include <QMap>
# include <QMutex>
# include <QMutexLocker>
# include <QThread>
# include <QVector>
# include <cmath>
# include <limits.h>
# include <QSet>
# include <QHash>
# include "nmCalculationUtils.h"
# include "nmPebiGridDiagnostics.h"
# include "zxLogInstance.h"
# include "nmDataAnalyzeManager.h"
# include "nmDataWellBase.h"
# include "nmDataVerticalWell.h"
# include "nmDataVerticalFracturedWell.h"
# include "nmDataHorizontalFracturedWell.h"
# include "nmDataReservoir.h"
# include "nmDataAttribute.h"
# include "nmDataRegion.h"
# include "nmDataRegionMark.h"
# include "nmDataOutline.h"
# include "nmDataFracture.h"
# include "nmDataFault.h"
# include "nmDataBinaryTools.h"
# include "nmDataPvtParaForPebi.h"
# include "nmDataTimeStepSetting.h"
# include "nmPebiCellMapping.h"
# include <vtkPoints.h>
# include <vtkCellArray.h>
# include <vtkMath.h>
# include <vtkType.h>
# include <vtkUnsignedCharArray.h>
namespace {
const int CONST_PVT_POINT_COUNT = 200 ;
typedef void ( * HX_NWTM_GRID_Func ) (
HX_NWTM_GRID_OUTPUT1 & ,
HX_NWTM_GRID_OUTPUT2 & ,
const HX_NWTM_GRID_INPUT & ,
std : : string ) ;
__declspec ( noinline ) void invokePebiGridDll (
HX_NWTM_GRID_Func pfnGenerateGrid ,
const nmPebiGridInputSnapshot & oSnapshot ,
nmPebiGridResult & oResult )
{
// 单独调用网格 DLL, 避免字符串参数的构造、析构与异常捕获放在同一函数中导致编译错误。
pfnGenerateGrid ( oResult . m_oGridOutput1 , oResult . m_oGridOutput2 ,
oSnapshot . m_oGridInput , oSnapshot . m_sLicensePath . toStdString ( ) ) ;
}
bool invokePebiGridDllGuarded ( HX_NWTM_GRID_Func pfnGenerateGrid ,
const nmPebiGridInputSnapshot & oSnapshot ,
nmPebiGridResult & oResult )
{
// 仅拦截已复现的访问违例;普通 C++ 异常继续交给外层 catch, 堆损坏不在此恢复。
__try {
invokePebiGridDll ( pfnGenerateGrid , oSnapshot , oResult ) ;
} __except ( GetExceptionCode ( ) = = EXCEPTION_ACCESS_VIOLATION
? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
return false ;
}
return true ;
}
// 该锁只保护 PEBI 网格单例缓存; HX_NWTM.dll 的所有入口由独立进程级锁保护。
// 保留递归锁以兼容可能在持锁网格入口中调用缓存查询的旧代码路径。
QMutex s_oPebiGridMutex ( QMutex : : Recursive ) ;
bool isCancellationRequested ( const QAtomicInt * pCancelRequested )
{
return pCancelRequested ! = NULL & &
static_cast < int > ( * pCancelRequested ) ! = 0 ;
}
class nmInterruptibleMutexLocker
{
public :
nmInterruptibleMutexLocker ( )
: m_pMutex ( NULL ) ,
m_bLocked ( false )
{
}
~ nmInterruptibleMutexLocker ( )
{
unlock ( ) ;
}
bool lock ( QMutex * pMutex , const QAtomicInt * pCancelRequested )
{
if ( pMutex = = NULL | | m_bLocked ) {
return false ;
}
if ( pCancelRequested = = NULL ) {
pMutex - > lock ( ) ;
} else {
// 等锁阶段不能无限阻塞;锁顺序仍保持“网格缓存锁 -> DLL 全局锁”。
while ( ! pMutex - > tryLock ( 100 ) ) {
if ( isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
}
}
m_pMutex = pMutex ;
m_bLocked = true ;
return true ;
}
bool tryLock ( QMutex * pMutex )
{
if ( pMutex = = NULL | | m_bLocked | | ! pMutex - > tryLock ( ) ) {
return false ;
}
m_pMutex = pMutex ;
m_bLocked = true ;
return true ;
}
void unlock ( )
{
if ( m_bLocked & & m_pMutex ! = NULL ) {
m_pMutex - > unlock ( ) ;
m_bLocked = false ;
m_pMutex = NULL ;
}
}
private :
QMutex * m_pMutex ;
bool m_bLocked ;
} ;
std : : vector < double > buildConstantPvtVector ( double value )
{
return std : : vector < double > ( CONST_PVT_POINT_COUNT , value ) ;
}
std : : vector < double > buildConstantPvtPressure ( )
{
std : : vector < double > pressure ( CONST_PVT_POINT_COUNT , 0.0 ) ;
for ( int i = 0 ; i < CONST_PVT_POINT_COUNT ; + + i ) {
pressure [ i ] = i + 1.0 ;
}
return pressure ;
}
void fillScenePvtByModel ( nmDataBinaryTools : : NM_PEBI_SCENE & scene ,
NM_SOLVER_MODEL_TYPE modelType ,
nmDataPvtParaForPebi * pvt ,
nmDataReservoir * reservoir )
{
switch ( modelType ) {
case SMT_Oil_ConstPvt :
scene . PVT . p = buildConstantPvtPressure ( ) ;
if ( reservoir ! = nullptr ) {
scene . PVT . Bo = buildConstantPvtVector ( reservoir - > getBo ( ) . getValue ( ) . toDouble ( ) ) ;
scene . PVT . miuo = buildConstantPvtVector ( reservoir - > getMiuo ( ) . getValue ( ) . toDouble ( ) ) ;
}
break ;
case SMT_Oil_VariablePvt :
if ( pvt ! = nullptr ) {
scene . PVT . p = pvt - > getPressure ( ) . toStdVector ( ) ;
scene . PVT . Bo = pvt - > getBo ( ) . toStdVector ( ) ;
scene . PVT . Co = pvt - > getCo ( ) . toStdVector ( ) ;
scene . PVT . miuo = pvt - > getMiuo ( ) . toStdVector ( ) ;
}
break ;
case SMT_Water_ConstPvt :
scene . PVT . p = buildConstantPvtPressure ( ) ;
if ( reservoir ! = nullptr ) {
scene . PVT . Bw = buildConstantPvtVector ( reservoir - > getBw ( ) . getValue ( ) . toDouble ( ) ) ;
scene . PVT . miuw = buildConstantPvtVector ( reservoir - > getMiuw ( ) . getValue ( ) . toDouble ( ) ) ;
}
break ;
case SMT_Water_VariablePvt :
if ( pvt ! = nullptr ) {
scene . PVT . p = pvt - > getPressure ( ) . toStdVector ( ) ;
scene . PVT . Bw = pvt - > getBw ( ) . toStdVector ( ) ;
scene . PVT . Cw = pvt - > getCw ( ) . toStdVector ( ) ;
scene . PVT . miuw = pvt - > getMiuw ( ) . toStdVector ( ) ;
}
break ;
case SMT_Gas_VariablePvt :
case SMT_Gas_PseudoPressure :
if ( pvt ! = nullptr ) {
scene . PVT . p = pvt - > getPressure ( ) . toStdVector ( ) ;
scene . PVT . Bg = pvt - > getBg ( ) . toStdVector ( ) ;
scene . PVT . Cg = pvt - > getCg ( ) . toStdVector ( ) ;
scene . PVT . miug = pvt - > getMiug ( ) . toStdVector ( ) ;
}
break ;
case SMT_Oil_Water_TwoPhase :
if ( pvt ! = nullptr ) {
scene . PVT . p = pvt - > getPressure ( ) . toStdVector ( ) ;
scene . PVT . Bo = pvt - > getBo ( ) . toStdVector ( ) ;
scene . PVT . miuo = pvt - > getMiuo ( ) . toStdVector ( ) ;
scene . PVT . Bw = pvt - > getBw ( ) . toStdVector ( ) ;
scene . PVT . miuw = pvt - > getMiuw ( ) . toStdVector ( ) ;
scene . PVT . So = pvt - > getSo ( ) . toStdVector ( ) ;
scene . PVT . Kro = pvt - > getKro ( ) . toStdVector ( ) ;
scene . PVT . Krw = pvt - > getKrw ( ) . toStdVector ( ) ;
}
break ;
default :
break ;
}
}
void fillScenePseudoPressureTable ( nmDataBinaryTools : : NM_PEBI_SCENE & scene ,
NM_SOLVER_MODEL_TYPE modelType ,
nmDataAnalyzeManager * pDataManager )
{
// 第一步:只有 T5 需要把当前井的转换表随场景交给离线 Runner。
if ( modelType ! = SMT_Gas_VariablePvt ) {
return ;
}
// 第二步:使用本次网格任务显式绑定的数据管理器,禁止从其他窗口取数。
if ( pDataManager = = nullptr
| | ! pDataManager - > getPebiPseudoPressureTable ( scene . PVT . pseudoPressureP ,
scene . PVT . pseudoPressurePs ) ) {
qWarning ( ) < < " Gas pseudo-pressure table is unavailable; T5 Runner data cannot be generated from this scene. " ;
return ;
}
}
// 把当前所选流量记录整理成求解器的公共段时长和三相数组。
// 单相记录缺少的相只补同轴零值;多相记录的三相数值不做业务修正。
bool captureWellFlowSchedule (
const nmWellGaugeInputData & oGaugeInput ,
nmPebiWellInputSnapshot & oWellInput )
{
oWellInput . m_vecFlowDurations . clear ( ) ;
oWellInput . m_vecOilRates . clear ( ) ;
oWellInput . m_vecGasRates . clear ( ) ;
oWellInput . m_vecWaterRates . clear ( ) ;
oWellInput . m_eFlowSchedulePhase = PHASE_UNKNOWN ;
oWellInput . m_bUseOilRate = false ;
oWellInput . m_bUseGasRate = false ;
oWellInput . m_bUseWaterRate = false ;
if ( ! oGaugeInput . hasAlignedFlowSchedule ( ) ) {
return false ;
}
oWellInput . m_eFlowSchedulePhase = oGaugeInput . eFlowSchedulePhase ;
oWellInput . m_vecFlowDurations = oGaugeInput . vecFlowDurations ;
oWellInput . m_vecOilRates = oGaugeInput . vecOilRates ;
oWellInput . m_vecGasRates = oGaugeInput . vecGasRates ;
oWellInput . m_vecWaterRates = oGaugeInput . vecWaterRates ;
oWellInput . m_bUseOilRate = oGaugeInput . bUseOilRate ;
oWellInput . m_bUseGasRate = oGaugeInput . bUseGasRate ;
oWellInput . m_bUseWaterRate = oGaugeInput . bUseWaterRate ;
return true ;
}
}
# ifdef Q_OS_WIN
# include <windows.h>
# define DEBUG_OUT(msg) OutputDebugStringA(QString("[Mesh] %1\n").arg(msg).toLocal8Bit().data())
# endif
nmCalculationPebiGrid * nmCalculationPebiGrid : : m_instance = nullptr ;
nmCalculationPebiGrid * nmCalculationPebiGrid : : getInstance ( )
{
if ( m_instance = = nullptr ) {
m_instance = new nmCalculationPebiGrid ( ) ;
}
return m_instance ;
}
nmCalculationPebiGrid : : nmCalculationPebiGrid ( )
: m_nCachedGridInputRevision ( 0 ) ,
m_pDataManager ( nullptr )
{
// 默认值来自HX_NWTM_GRID_INPUT构造函数
m_dGridControl = p0 . GridControl ;
// 清空原有PEBI网格输出数据
p1 . TRI_cell . p . clear ( ) ;
p1 . TRI_cell . pindex . clear ( ) ;
p1 . TRI_cell . isplot . clear ( ) ;
// 清空 PEBI_cell
p1 . PEBI_cell . p . clear ( ) ;
p1 . PEBI_cell . pindex . clear ( ) ;
p1 . PEBI_cell . isplot . clear ( ) ;
}
nmCalculationPebiGrid : : ~ nmCalculationPebiGrid ( )
{
}
void nmCalculationPebiGrid : : setGridControl (
double dGridControl ,
nmDataAnalyzeManager * pDataManager )
{
QMutexLocker oLocker ( & s_oPebiGridMutex ) ;
// 第一步: GridControl 必须为正数,非法值保持原有设置。
if ( dGridControl < = 0.0 ) {
return ;
}
// 第二步:优先写入所属分析方案,由方案负责版本递增和网格失效。
if ( pDataManager ! = nullptr ) {
pDataManager - > setPebiGridControl ( dGridControl ) ;
}
// 第三步:同步单例缓存仅用于兼容仍直接读取该成员的内部网格输入构造。
m_dGridControl = dGridControl ;
}
void nmCalculationPebiGrid : : clearGridData (
const nmDataAnalyzeManager * pDataManager )
{
QMutexLocker oLocker ( & s_oPebiGridMutex ) ;
// 关闭某个网格窗口时只允许清理它自己的缓存,不能影响另一个成果窗口。
if ( pDataManager ! = nullptr & & m_pDataManager ! = pDataManager ) {
return ;
}
p0 = HX_NWTM_GRID_INPUT ( ) ;
p1 = HX_NWTM_GRID_OUTPUT1 ( ) ;
p2 = HX_NWTM_GRID_OUTPUT2 ( ) ;
m_dGridControl = p0 . GridControl ;
m_nCachedGridInputRevision = 0 ;
m_pDataManager = nullptr ;
}
bool nmCalculationPebiGrid : : copyCurrentGridFor (
const nmDataAnalyzeManager * pDataManager ,
quint64 nGridInputRevision ,
HX_NWTM_GRID_OUTPUT1 & oGridOutput1 ,
HX_NWTM_GRID_OUTPUT2 & oGridOutput2 ,
const QAtomicInt * pCancelRequested ) const
{
nmInterruptibleMutexLocker oGridLocker ;
const bool bLocked = pCancelRequested = = NULL
? oGridLocker . tryLock ( & s_oPebiGridMutex )
: oGridLocker . lock ( & s_oPebiGridMutex , pCancelRequested ) ;
if ( ! bLocked ) {
return false ;
}
// 后台只比较捕获时的所有权和版本值,不能再读取 DataManager 内部状态。
if ( pDataManager = = nullptr | |
m_pDataManager ! = pDataManager | |
m_nCachedGridInputRevision ! = nGridInputRevision | |
p1 . PEBI_cell . p . empty ( ) ) {
return false ;
}
// 一次性复制两份 DLL 输出,保证求解期间使用同一版本的网格快照。
oGridOutput1 = p1 ;
if ( isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
oGridOutput2 = p2 ;
return true ;
}
bool nmCalculationPebiGrid : : isCurrentGridAvailableFor (
const nmDataAnalyzeManager * pDataManager ,
quint64 nGridInputRevision ) const
{
// 该接口在主线程只做常量时间校验,不复制大型网格输出;锁忙时交给后台重建。
if ( ! s_oPebiGridMutex . tryLock ( ) ) {
return false ;
}
const bool bAvailable = pDataManager ! = nullptr & &
m_pDataManager = = pDataManager & &
pDataManager - > isPebiGridValid ( ) & &
m_nCachedGridInputRevision = = nGridInputRevision & &
! p1 . PEBI_cell . p . empty ( ) ;
s_oPebiGridMutex . unlock ( ) ;
return bAvailable ;
}
void nmCalculationPebiGrid : : logInputParameters ( const HX_NWTM_GRID_INPUT & input )
{
QString logMsg = " Input Parameters: \n " ;
logMsg + = QString ( " GridControl: %1 \n " ) . arg ( input . GridControl ) ;
logMsg + = QString ( " D: %1 \n " ) . arg ( input . D ) ;
// 记录边界信息
logMsg + = QString ( " Boundary count: %1 \n " ) . arg ( input . Boundary . size ( ) ) ;
for ( size_t i = 0 ; i < input . Boundary . size ( ) ; + + i ) {
const auto & line = input . Boundary [ i ] ;
if ( line . size ( ) > = 4 ) {
logMsg + = QString ( " Boundary %1: (%2, %3) -> (%4, %5) \n " )
. arg ( i ) . arg ( line [ 0 ] ) . arg ( line [ 1 ] ) . arg ( line [ 2 ] ) . arg ( line [ 3 ] ) ;
}
}
// 记录井信息
logMsg + = QString ( " Vertical Wells count: %1 \n " ) . arg ( input . VerticalWell . size ( ) ) ;
for ( size_t i = 0 ; i < input . VerticalWell . size ( ) ; + + i ) {
const auto & well = input . VerticalWell [ i ] ;
if ( well . size ( ) > = 3 ) {
logMsg + = QString ( " Well %1: (%2, %3), radius: %4 \n " )
. arg ( i ) . arg ( well [ 0 ] ) . arg ( well [ 1 ] ) . arg ( well [ 2 ] ) ;
}
}
// 记录垂直裂缝井以及裂缝信息
logMsg + = QString ( " Fracture Vertical Wells And Fractures count: %1 \n " ) . arg ( input . FractureVerticalWell . size ( ) ) ;
for ( size_t i = 0 ; i < input . FractureVerticalWell . size ( ) ; + + i ) {
const auto & frac = input . FractureVerticalWell [ i ] ;
if ( frac . size ( ) > = 6 ) {
logMsg + = QString ( " Fracture %1: (%2, %3) -> (%4, %5), width: %6, FC: %7 \n " )
. arg ( i ) . arg ( frac [ 0 ] ) . arg ( frac [ 1 ] ) . arg ( frac [ 2 ] ) . arg ( frac [ 3 ] ) . arg ( frac [ 4 ] ) . arg ( frac [ 5 ] ) ;
} else if ( frac . size ( ) > = 5 ) {
logMsg + = QString ( " Fracture %1: (%2, %3) -> (%4, %5), width: %6 \n " )
. arg ( i ) . arg ( frac [ 0 ] ) . arg ( frac [ 1 ] ) . arg ( frac [ 2 ] ) . arg ( frac [ 3 ] ) . arg ( frac [ 4 ] ) ;
}
}
// 记录多段压裂水平井信息
logMsg + = QString ( " Multistage Fractured Horizontal Wells count: %1 \n " ) . arg ( input . MultistageFracturedHorizontalWell . size ( ) ) ;
for ( size_t i = 0 ; i < input . MultistageFracturedHorizontalWell . size ( ) ; + + i ) {
const auto & horizontalWell = input . MultistageFracturedHorizontalWell [ i ] ;
logMsg + = QString ( " Horizontal Fractured Well %1 (Fractures count: %2): \n " ) . arg ( i ) . arg ( horizontalWell . size ( ) ) ;
for ( size_t j = 0 ; j < horizontalWell . size ( ) ; + + j ) {
const auto & frac = horizontalWell [ j ] ;
if ( frac . size ( ) > = 6 ) {
logMsg + = QString ( " Fracture %1.%2: (%3, %4) -> (%5, %6), width: %7, FC: %8 \n " )
. arg ( i ) . arg ( j ) . arg ( frac [ 0 ] ) . arg ( frac [ 1 ] ) . arg ( frac [ 2 ] ) . arg ( frac [ 3 ] ) . arg ( frac [ 4 ] ) . arg ( frac [ 5 ] ) ;
} else if ( frac . size ( ) > = 5 ) {
logMsg + = QString ( " Fracture %1.%2: (%3, %4) -> (%5, %6), width: %7 \n " )
. arg ( i ) . arg ( j ) . arg ( frac [ 0 ] ) . arg ( frac [ 1 ] ) . arg ( frac [ 2 ] ) . arg ( frac [ 3 ] ) . arg ( frac [ 4 ] ) ;
}
}
}
// 记录断层信息 (新增详细输出)
logMsg + = QString ( " Faults count: %1 \n " ) . arg ( input . Fault . size ( ) ) ;
for ( size_t i = 0 ; i < input . Fault . size ( ) ; + + i ) {
const auto & faultSegment = input . Fault [ i ] ;
if ( faultSegment . size ( ) > = 4 ) { // 断层段至少有起点和终点
logMsg + = QString ( " Fault Segment %1: (%2, %3) -> (%4, %5) \n " )
. arg ( i ) . arg ( faultSegment [ 0 ] ) . arg ( faultSegment [ 1 ] ) . arg ( faultSegment [ 2 ] ) . arg ( faultSegment [ 3 ] ) ;
}
}
qDebug ( ) < < logMsg ;
}
void nmCalculationPebiGrid : : logCurrentState ( )
{
QString stateMsg = " Current Output P1 State: \n " ;
// 记录一些关键状态信息
stateMsg + = QString ( " PEBI_cell points count: %1 \n " ) . arg ( p1 . PEBI_cell . p . size ( ) ) ;
stateMsg + = QString ( " PEBI_cell pindex count: %1 \n " ) . arg ( p1 . PEBI_cell . pindex . size ( ) ) ;
stateMsg + = QString ( " PEBI_cell isplot count: %1 \n " ) . arg ( p1 . PEBI_cell . isplot . size ( ) ) ;
qDebug ( ) < < stateMsg ;
}
bool nmCalculationPebiGrid : : meshGenPebiBoundary (
nmDataAnalyzeManager * pDataManager ,
HX_NWTM_GRID_INPUT & inputObj )
{
// 1、从数据中心获取边界数据
nmDataOutline * pOutlineData = pDataManager ! = nullptr
? pDataManager - > getOutlineData ( ) : nullptr ;
if ( pOutlineData = = nullptr ) {
return false ;
}
inputObj . Boundary . clear ( ) ;
// 获取边界的点
QVector < QPointF > vecOutlinePoints = pOutlineData - > getOutlinePoints ( ) ;
// 顺时针进行构建
for ( int i = 0 ; i < vecOutlinePoints . size ( ) ; i + + ) {
QPointF & startP = vecOutlinePoints [ i ] ;
QPointF endP ;
if ( i = = vecOutlinePoints . size ( ) - 1 ) {
endP = vecOutlinePoints . first ( ) ;
} else {
endP = vecOutlinePoints [ i + 1 ] ;
}
dVec1 line ( 4 ) ;
//line[0] = (int)startP.x();
//line[1] = (int)startP.y();
//line[2] = (int)endP.x();
//line[3] = (int)endP.y();
line [ 0 ] = qRound ( startP . x ( ) ) ; // 四舍五入
line [ 1 ] = qRound ( startP . y ( ) ) ; // 四舍五入
line [ 2 ] = qRound ( endP . x ( ) ) ; // 四舍五入
line [ 3 ] = qRound ( endP . y ( ) ) ; // 四舍五入
inputObj . Boundary . push_back ( line ) ;
}
return true ;
}
bool nmCalculationPebiGrid : : meshGenPebiWells (
nmDataAnalyzeManager * pDataManager ,
HX_NWTM_GRID_INPUT & inputObj ,
QVector < nmSolverWellRef > & vecSolverWellOrder ,
const QSet < QString > & setEffectiveWellCodes )
{
// 从数据中心获取井数据
//QVector<nmDataWellBase*> vecDataWells = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList();
//if (vecDataWells.size() == 0) {
// return true;
//}
if ( pDataManager = = nullptr ) {
return false ;
}
inputObj . VerticalWell . clear ( ) ;
inputObj . FractureVerticalWell . clear ( ) ;
inputObj . MultistageFracturedHorizontalWell . clear ( ) ;
// 水平井初始化
inputObj . HorizontalWell . clear ( ) ;
// 斜井
inputObj . InclinedWell . clear ( ) ;
// 第一步:求解器顺序只写入局部快照,后台成功前不修改分析方案。
vecSolverWellOrder . clear ( ) ;
// 获取直井数据
QVector < nmDataVerticalWell * > verticalWells = pDataManager - > getVerticalWellData ( ) ;
// 获取垂直裂缝井数据
QVector < nmDataVerticalFracturedWell * > vFracturedWells = pDataManager - > getVerticalFracturedWellData ( ) ;
// 获取多段压裂水平井数据
QVector < nmDataHorizontalFracturedWell * > hFracturedWells = pDataManager - > getHorizontalFracturedWellData ( ) ;
// 处理直井数据
foreach ( nmDataVerticalWell * pVerticalWell , verticalWells ) {
if ( pVerticalWell ! = nullptr & &
setEffectiveWellCodes . contains ( pVerticalWell - > getWellCode ( ) ) ) {
dVec1 well ( 3 ) ;
double dX = pVerticalWell - > getX ( ) . getValue ( ) . toDouble ( ) ;
double dY = pVerticalWell - > getY ( ) . getValue ( ) . toDouble ( ) ;
well [ 0 ] = dX ;
well [ 1 ] = dY ;
well [ 2 ] = pVerticalWell - > getRadius ( ) . getValue ( ) . toDouble ( ) ;
inputObj . VerticalWell . push_back ( well ) ;
// 求解器顺序只保存 WellCode, 井名不参与数组映射。
vecSolverWellOrder . append ( nmSolverWellRef (
- 1 ,
NM_WELL_MODEL : : Vertical_Well ,
pVerticalWell - > getWellCode ( ) ) ) ;
}
}
// 处理垂直裂缝井数据
foreach ( nmDataVerticalFracturedWell * pVerticalFracturedWell , vFracturedWells ) {
if ( pVerticalFracturedWell ! = nullptr & &
setEffectiveWellCodes . contains ( pVerticalFracturedWell - > getWellCode ( ) ) ) {
QVector < QPointF > vFracPoints = pVerticalFracturedWell - > getFracs ( ) ;
if ( vFracPoints . size ( ) = = 2 ) {
dVec1 crack ( 6 ) ;
crack [ 0 ] = vFracPoints [ 0 ] . x ( ) ;
crack [ 1 ] = vFracPoints [ 0 ] . y ( ) ;
crack [ 2 ] = vFracPoints [ 1 ] . x ( ) ;
crack [ 3 ] = vFracPoints [ 1 ] . y ( ) ;
// 裂缝宽度直接取井对象中保存的面板参数。
crack [ 4 ] = pVerticalFracturedWell - > getWidth ( ) . getValue ( ) . toDouble ( ) ;
// FC直接取井对象中保存的裂缝导流能力, 0表示无限导流。
crack [ 5 ] = pVerticalFracturedWell - > getDfc ( ) . getValue ( ) . toDouble ( ) ;
inputObj . FractureVerticalWell . push_back ( crack ) ;
vecSolverWellOrder . append ( nmSolverWellRef (
- 1 ,
NM_WELL_MODEL : : Vertical_Fractured_Well ,
pVerticalFracturedWell - > getWellCode ( ) ) ) ;
}
}
}
// 处理多段压裂水平井数据
foreach ( nmDataHorizontalFracturedWell * pHorizontalFracturedWell , hFracturedWells ) {
if ( pHorizontalFracturedWell ! = nullptr & &
setEffectiveWellCodes . contains ( pHorizontalFracturedWell - > getWellCode ( ) ) ) {
QVector < QPair < QPointF , QPointF > > vvecFracPoints = pHorizontalFracturedWell - > getFracs ( ) ;
std : : vector < std : : vector < double > > vMultistageFracturedHorizontalWell ;
for ( int j = 0 ; j < vvecFracPoints . size ( ) ; + + j ) {
dVec1 crack ( 6 ) ;
QPointF & vStartPoint = vvecFracPoints [ j ] . first ; // 起始点
QPointF & vEndPoint = vvecFracPoints [ j ] . second ; // 终止点
crack [ 0 ] = vStartPoint . x ( ) ;
crack [ 1 ] = vStartPoint . y ( ) ;
crack [ 2 ] = vEndPoint . x ( ) ;
crack [ 3 ] = vEndPoint . y ( ) ;
// 裂缝宽度直接取井对象中保存的面板参数。
crack [ 4 ] = pHorizontalFracturedWell - > getWidth ( ) . getValue ( ) . toDouble ( ) ;
// FC直接取井对象中保存的裂缝导流能力, 0表示无限导流。
crack [ 5 ] = pHorizontalFracturedWell - > getDfc ( ) . getValue ( ) . toDouble ( ) ;
vMultistageFracturedHorizontalWell . push_back ( crack ) ;
}
inputObj . MultistageFracturedHorizontalWell . push_back ( vMultistageFracturedHorizontalWell ) ;
vecSolverWellOrder . append ( nmSolverWellRef (
- 1 ,
NM_WELL_MODEL : : Horizontal_Fractured_Well ,
pHorizontalFracturedWell - > getWellCode ( ) ) ) ;
}
}
return true ;
// // 分类处理
// for (int i = 0; i < vecDataWells.size(); i++) {
// nmDataWellBase* pWellData = vecDataWells[i];
// if (pWellData != nullptr) {
//m_vecWellOrder.append(true);
// // 垂直裂缝井
// nmDataVerticalFracturedWell* pVerticalFracturedWell = dynamic_cast<nmDataVerticalFracturedWell*>(pWellData);
// if (pVerticalFracturedWell != nullptr) {
// dVec1 crack(5);
// QVector<QPointF> vFracPoints = pVerticalFracturedWell->getFracs();
// if (vFracPoints.size() == 2) {
// crack[0] = (int)vFracPoints[0].x();
// crack[1] = (int)vFracPoints[0].y();
// crack[2] = (int)vFracPoints[1].x();
// crack[3] = (int)vFracPoints[1].y();
// // todo, 裂缝的宽度
// crack[4] = 1;
// //crack[4] = QString::number(0.050000000000000000000000000000000000000000, 'f', 2).toDouble();
// // 确保裂缝宽度保留两位小数
// //crack[4] = std::round(0.05 * 100) / 100;
// //crack[4] = static_cast<double>(static_cast<int>(0.06 * 100)) / 100;
// //crack[4] = qRound(0.06 * 100) / 100.0;
// qDebug() << crack[0] << crack[1] << crack[2] << crack[3] << crack[4];
// inputObj.FractureVerticalWell.push_back(crack);
// /*dVec1 c(5);
// c[0] = 200; c[1] = 200; c[2] = 400; c[3] = 200; c[4] = 0.05;
// inputObj.FractureVerticalWell.push_back(c);*/
// }
// continue;
// }
// // 多段压裂水平井
// nmDataHorizontalFracturedWell* pHorizontalFracturedWell = dynamic_cast<nmDataHorizontalFracturedWell*>(pWellData);
// if (pHorizontalFracturedWell != nullptr) {
// QVector<QPair<QPointF, QPointF>> vvecFracPoints = pHorizontalFracturedWell->getFracs();
// std::vector<std::vector<double>> vMultistageFracturedHorizontalWell;
// // 遍历 vvecFracPoints, 每个 QPair 包含一个裂缝的起始点和终止点
// for (int j = 0; j < vvecFracPoints.size(); ++j) {
// dVec1 crack(5);
// QPointF& vStartPoint = vvecFracPoints[j].first; // 起始点
// QPointF& vEndPoint = vvecFracPoints[j].second; // 终止点
// crack[0] = (int)vStartPoint.x();
// crack[1] = (int)vStartPoint.y();
// crack[2] = (int)vEndPoint.x();
// crack[3] = (int)vEndPoint.y();
// // todo, 裂缝的宽度
// crack[4] = 1;
// vMultistageFracturedHorizontalWell.push_back(crack);
// }
// inputObj.MultistageFracturedHorizontalWell.push_back(vMultistageFracturedHorizontalWell);
// continue;
// }
// // 直井
// nmDataVerticalWell* pVertialWell = dynamic_cast<nmDataVerticalWell*>(pWellData);
// if (pVertialWell != nullptr) {
// dVec1 well(3);
// double dX = pVertialWell->getX().getValue().toDouble();
// double dY = pVertialWell->getY().getValue().toDouble();
// well[0] = dX;
// well[1] = dY;
// well[2] = pVertialWell->getRadius().getValue().toDouble();
// inputObj.VerticalWell.push_back(well);
// continue;
// }
// }
// }
//return true;
}
bool nmCalculationPebiGrid : : meshGenPebiFault (
nmDataAnalyzeManager * pDataManager ,
HX_NWTM_GRID_INPUT & inputObj )
{
// 从数据中心获取断层数据
QVector < nmDataFault * > vecDataFault = pDataManager ! = nullptr
? pDataManager - > getFaultDataList ( ) : QVector < nmDataFault * > ( ) ;
inputObj . Fault . clear ( ) ;
// 断层,没有宽度
for ( int i = 0 ; i < vecDataFault . size ( ) ; i + + ) {
nmDataFault * pFault = vecDataFault [ i ] ;
QVector < QPointF > vecPlts = pFault - > getFaultPoints ( ) ;
// 对每个断层进行处理
// 每个断层包括 多段
if ( vecPlts . size ( ) > 0 ) {
for ( int j = 0 ; j < vecPlts . size ( ) - 1 ; j + + ) {
//dVec1 frac(5);
dVec1 frac ( 4 ) ;
QPointF & vStartPoint = vecPlts [ j ] ;
QPointF & vEndPoint = vecPlts [ j + 1 ] ;
// 起始点、终止点,没有宽度
frac [ 0 ] = vStartPoint . x ( ) ;
frac [ 1 ] = vStartPoint . y ( ) ;
frac [ 2 ] = vEndPoint . x ( ) ;
frac [ 3 ] = vEndPoint . y ( ) ;
inputObj . Fault . push_back ( frac ) ;
}
}
}
return true ;
}
bool nmCalculationPebiGrid : : meshGenPebiCrack (
nmDataAnalyzeManager * pDataManager ,
HX_NWTM_GRID_INPUT & inputObj ,
QVector < nmSolverWellRef > & vecSolverWellOrder )
{
// 从数据中心获取裂缝几何数据
QVector < nmDataFracture * > vecDataFracture = pDataManager ! = nullptr
? pDataManager - > getFractureDataList ( ) : QVector < nmDataFracture * > ( ) ;
// 裂缝,将所有裂缝当一个 裂缝直井来处理
for ( int i = 0 ; i < vecDataFracture . size ( ) ; i + + ) {
nmDataFracture * pFrac = vecDataFracture [ i ] ;
QVector < QPointF > vecPlts = pFrac - > getFracturePoints ( ) ;
// 对每个裂缝进行处理
// 每个裂缝包括 多段
if ( vecPlts . size ( ) > 0 ) {
for ( int j = 0 ; j < vecPlts . size ( ) - 1 ; j + + ) {
dVec1 crack ( 6 ) ;
QPointF & vStartPoint = vecPlts [ j ] ;
QPointF & vEndPoint = vecPlts [ j + 1 ] ;
// 起始点、终止点,没有宽度
crack [ 0 ] = vStartPoint . x ( ) ;
crack [ 1 ] = vStartPoint . y ( ) ;
crack [ 2 ] = vEndPoint . x ( ) ;
crack [ 3 ] = vEndPoint . y ( ) ;
// todo, 裂缝的宽度
crack [ 4 ] = 1 ;
// 手动画裂缝目前只支持无限导流, FC固定为0。
crack [ 5 ] = 0.0 ;
qDebug ( ) < < crack [ 0 ] < < crack [ 1 ] < < crack [ 2 ] < < crack [ 3 ] < < crack [ 4 ] < < crack [ 5 ] ;
int iIndex = inputObj . VerticalWell . size ( ) + inputObj . FractureVerticalWell . size ( ) ;
inputObj . FractureVerticalWell . push_back ( crack ) ;
//int iIndex = inputObj.VerticalWell.size() + inputObj.FractureVerticalWell.size();
// 添加到自定义井顺序数组
vecSolverWellOrder . insert (
iIndex ,
nmSolverWellRef ( iIndex ,
NM_WELL_MODEL : : Unknow_Well ,
QString ( ) ,
NM_SolverEntry_ManualFracture ) ) ;
}
}
}
return true ;
}
void nmCalculationPebiGrid : : genPebiVTK ( const HX_NWTM_GRID_OUTPUT1 & P1 , QString vtkDir )
{
// 1. 提取所有点数据
QVector < QPair < double , double > > vPoints ;
for ( size_t i = 0 ; i < P1 . PEBI_cell . p . size ( ) ; + + i ) {
vPoints . append ( QPair < double , double > ( P1 . PEBI_cell . p [ i ] . x , P1 . PEBI_cell . p [ i ] . y ) ) ;
}
// 2. 预处理单元数据 - 只处理isplot为1的单元
QVector < QVector < int > > vCells ;
QVector < int > vCellTypes ; // 存储每个单元的类型
int totalCellDataSize = 0 ;
for ( size_t i = 0 ; i < P1 . PEBI_cell . pindex . size ( ) ; + + i ) {
// 只处理isplot为1的单元
if ( i > = P1 . PEBI_cell . isplot . size ( ) | | P1 . PEBI_cell . isplot [ i ] ! = 1 ) {
continue ;
}
const std : : vector < int > & vecIndices = P1 . PEBI_cell . pindex [ i ] ;
if ( vecIndices . empty ( ) ) continue ;
// 确定实际点数(检查首尾是否相同)
int numPoints = vecIndices . size ( ) ;
if ( numPoints > 1 & & vecIndices [ 0 ] = = vecIndices [ numPoints - 1 ] ) {
numPoints - - ;
}
// 跳过无效单元( 点数小于2)
if ( numPoints < 2 ) continue ;
// 确定单元类型
int cellType = 0 ;
if ( numPoints = = 2 ) { // 线
cellType = 3 ; // VTK_LINE
} else if ( numPoints = = 3 ) { // 三角形
cellType = 5 ; // VTK_TRIANGLE
} else if ( numPoints = = 4 ) { // 四边形
cellType = 9 ; // VTK_QUAD
} else { // 多边形
cellType = 7 ; // VTK_POLYGON
}
// 存储单元数据
QVector < int > cellData ;
cellData . append ( numPoints ) ;
for ( int j = 0 ; j < numPoints ; + + j ) {
cellData . append ( vecIndices [ j ] ) ;
}
vCells . append ( cellData ) ;
vCellTypes . append ( cellType ) ;
totalCellDataSize + = ( numPoints + 1 ) ; // +1 for the numPoints entry
}
// 3. 构建VTK文件内容
QStringList vtkContents ;
vtkContents . append ( " # vtk DataFile Version 4.0 " ) ;
vtkContents . append ( " Unstructured Grid " ) ;
vtkContents . append ( " ASCII " ) ;
vtkContents . append ( " DATASET UNSTRUCTURED_GRID " ) ;
// 写入点数据
vtkContents . append ( QString ( " POINTS %1 float " ) . arg ( vPoints . size ( ) ) ) ;
for ( int i = 0 ; i < vPoints . size ( ) ; + + i ) {
vtkContents . append ( QString ( " %1 %2 0.0 " ) . arg ( vPoints [ i ] . first ) . arg ( vPoints [ i ] . second ) ) ;
}
// 写入单元数据
vtkContents . append ( QString ( " \n CELLS %1 %2 " ) . arg ( vCells . size ( ) ) . arg ( totalCellDataSize ) ) ;
for ( int i = 0 ; i < vCells . size ( ) ; + + i ) {
QString line ;
const QVector < int > & cellData = vCells [ i ] ;
for ( int j = 0 ; j < cellData . size ( ) ; + + j ) {
line + = QString : : number ( cellData [ j ] ) ;
if ( j < cellData . size ( ) - 1 ) {
line + = " " ;
}
}
vtkContents . append ( line ) ;
}
// 写入单元类型
vtkContents . append ( QString ( " \n CELL_TYPES %1 " ) . arg ( vCellTypes . size ( ) ) ) ;
for ( int i = 0 ; i < vCellTypes . size ( ) ; + + i ) {
vtkContents . append ( QString : : number ( vCellTypes [ i ] ) ) ;
}
// 写入文件
nmCalculationUtils : : writeFile ( vtkContents , vtkDir + " /pebi.vtk " ) ;
}
HX_NWTM_GRID_OUTPUT1 nmCalculationPebiGrid : : getGridOutput1 ( )
{
QMutexLocker oLocker ( & s_oPebiGridMutex ) ;
return p1 ;
}
HX_NWTM_GRID_OUTPUT2 nmCalculationPebiGrid : : getGridOutput2 ( )
{
QMutexLocker oLocker ( & s_oPebiGridMutex ) ;
return p2 ;
}
vtkSmartPointer < vtkUnstructuredGrid > nmCalculationPebiGrid : : createPebiUnstructuredGrid (
const HX_NWTM_GRID_OUTPUT1 & P1 ,
const QAtomicInt * pCancelRequested )
{
const size_t nPointCount = P1 . PEBI_cell . p . size ( ) ;
const size_t nCellDefinitionCount = P1 . PEBI_cell . pindex . size ( ) ;
const size_t nOriginalCellCount = P1 . PEBI_cell . isplot . size ( ) ;
if ( nPointCount = = 0 | | nPointCount > static_cast < size_t > ( INT_MAX ) | |
nOriginalCellCount = = 0 | |
nOriginalCellCount > static_cast < size_t > ( INT_MAX ) | |
nCellDefinitionCount > static_cast < size_t > ( INT_MAX ) ) {
qWarning ( ) < < " PEBI grid output dimensions are invalid. " ;
return nullptr ;
}
if ( nCellDefinitionCount ! = nOriginalCellCount ) {
qWarning ( ) < < " PEBI grid output cell counts differ; "
< < " unmatched cells will be ignored: "
< < nCellDefinitionCount < < nOriginalCellCount ;
}
// 第一遍验证 DLL 输出、规范化单元并收集点,不向 VTK 对象提交半成品几何。
QSet < int > setUniquePointIndices ;
QVector < int > vecVisibleOriginalCellIds ;
QMap < int , QVector < int > > mapCellPointIds ;
int nSkippedCellCount = 0 ;
for ( size_t i = 0 ; i < P1 . PEBI_cell . pindex . size ( ) ; + + i ) {
if ( ( i % 256 ) = = 0 & & isCancellationRequested ( pCancelRequested ) ) {
return nullptr ;
}
const std : : vector < int > & vecIndices = P1 . PEBI_cell . pindex [ i ] ;
if ( vecIndices . size ( ) > static_cast < size_t > ( INT_MAX ) ) {
qWarning ( ) < < " PEBI cell point count exceeds VTK capacity: " < < i ;
return nullptr ;
}
if ( i > = nOriginalCellCount | | P1 . PEBI_cell . isplot [ i ] ! = 1 ) {
continue ;
}
if ( vecIndices . empty ( ) ) {
+ + nSkippedCellCount ;
continue ;
}
QVector < int > vecNormalizedPointIds ;
vecNormalizedPointIds . reserve ( static_cast < int > ( vecIndices . size ( ) ) ) ;
for ( int j = 0 ; j < static_cast < int > ( vecIndices . size ( ) ) ; + + j ) {
const int nOriginalPointId = vecIndices [ j ] ;
if ( nOriginalPointId < 0 | |
static_cast < size_t > ( nOriginalPointId ) > = nPointCount ) {
qWarning ( ) < < " PEBI cell references an invalid point: "
< < i < < nOriginalPointId ;
return nullptr ;
}
const double dX = P1 . PEBI_cell . p [ nOriginalPointId ] . x ;
const double dY = P1 . PEBI_cell . p [ nOriginalPointId ] . y ;
if ( ! vtkMath : : IsFinite ( dX ) | | ! vtkMath : : IsFinite ( dY ) ) {
qWarning ( ) < < " PEBI cell references a non-finite point: " < < i ;
return nullptr ;
}
if ( vecNormalizedPointIds . isEmpty ( ) | |
vecNormalizedPointIds . last ( ) ! = nOriginalPointId ) {
vecNormalizedPointIds . append ( nOriginalPointId ) ;
}
}
if ( vecNormalizedPointIds . size ( ) > 1 & &
vecNormalizedPointIds . first ( ) = = vecNormalizedPointIds . last ( ) ) {
vecNormalizedPointIds . resize (
vecNormalizedPointIds . size ( ) - 1 ) ;
}
if ( vecNormalizedPointIds . size ( ) < 2 ) {
+ + nSkippedCellCount ;
continue ;
}
QSet < int > setCellPointIndices ;
bool bHasRepeatedInteriorPoint = false ;
for ( int j = 0 ; j < vecNormalizedPointIds . size ( ) ; + + j ) {
const int nOriginalPointId = vecNormalizedPointIds [ j ] ;
if ( setCellPointIndices . contains ( nOriginalPointId ) ) {
bHasRepeatedInteriorPoint = true ;
break ;
}
setCellPointIndices . insert ( nOriginalPointId ) ;
}
if ( bHasRepeatedInteriorPoint ) {
+ + nSkippedCellCount ;
continue ;
}
foreach ( int nOriginalPointId , setCellPointIndices ) {
setUniquePointIndices . insert ( nOriginalPointId ) ;
}
const int nOriginalCellId = static_cast < int > ( i ) ;
vecVisibleOriginalCellIds . append ( nOriginalCellId ) ;
mapCellPointIds . insert ( nOriginalCellId , vecNormalizedPointIds ) ;
}
const int nVisibleCellCount = vecVisibleOriginalCellIds . size ( ) ;
if ( nVisibleCellCount < = 0 | | setUniquePointIndices . isEmpty ( ) ) {
qWarning ( ) < < " PEBI grid output contains no visible cells. " ;
return nullptr ;
}
if ( nSkippedCellCount > 0 ) {
qWarning ( ) < < " Skipped degenerate PEBI cells: " < < nSkippedCellCount ;
}
if ( isCancellationRequested ( pCancelRequested ) ) {
return nullptr ;
}
vtkSmartPointer < vtkUnstructuredGrid > pUnstructuredGrid =
vtkSmartPointer < vtkUnstructuredGrid > : : New ( ) ;
vtkSmartPointer < vtkPoints > pPoints =
vtkSmartPointer < vtkPoints > : : New ( ) ;
QMap < int , vtkIdType > mapOriginalToVtkPointId ;
vtkIdType currentVtkPointId = 0 ;
pPoints - > SetNumberOfPoints (
static_cast < vtkIdType > ( setUniquePointIndices . size ( ) ) ) ;
int nProcessedPointCount = 0 ;
foreach ( int nOriginalIdx , setUniquePointIndices ) {
if ( ( nProcessedPointCount % 256 ) = = 0 & &
isCancellationRequested ( pCancelRequested ) ) {
return nullptr ;
}
pPoints - > SetPoint ( currentVtkPointId ,
P1 . PEBI_cell . p [ nOriginalIdx ] . x ,
P1 . PEBI_cell . p [ nOriginalIdx ] . y ,
0.0 ) ;
mapOriginalToVtkPointId . insert (
nOriginalIdx , currentVtkPointId ) ;
+ + currentVtkPointId ;
+ + nProcessedPointCount ;
}
pUnstructuredGrid - > SetPoints ( pPoints ) ;
pUnstructuredGrid - > Allocate (
static_cast < vtkIdType > ( nVisibleCellCount ) ) ;
// 第二遍只提交已经验证过的可见单元,并同步构造 VTK -> 原始单元映射。
QVector < vtkIdType > vecVtkToOriginal ;
vecVtkToOriginal . reserve ( nVisibleCellCount ) ;
for ( int nCellIndex = 0 ;
nCellIndex < vecVisibleOriginalCellIds . size ( ) ; + + nCellIndex ) {
if ( ( nCellIndex % 256 ) = = 0 & &
isCancellationRequested ( pCancelRequested ) ) {
return nullptr ;
}
const int nOriginalCellId =
vecVisibleOriginalCellIds [ nCellIndex ] ;
const QVector < int > & vecPointIds =
mapCellPointIds [ nOriginalCellId ] ;
const int nPointsInCell = vecPointIds . size ( ) ;
int vtkCellType = 0 ;
if ( nPointsInCell = = 2 ) {
vtkCellType = VTK_LINE ;
} else if ( nPointsInCell = = 3 ) {
vtkCellType = VTK_TRIANGLE ;
} else if ( nPointsInCell = = 4 ) {
vtkCellType = VTK_QUAD ;
} else {
vtkCellType = VTK_POLYGON ;
}
QVector < vtkIdType > vecVtkPointIds ( nPointsInCell ) ;
for ( int j = 0 ; j < nPointsInCell ; + + j ) {
QMap < int , vtkIdType > : : const_iterator oPointIt =
mapOriginalToVtkPointId . constFind ( vecPointIds [ j ] ) ;
if ( oPointIt = = mapOriginalToVtkPointId . constEnd ( ) ) {
qWarning ( ) < < " Validated PEBI point is missing from the map: "
< < nOriginalCellId < < vecPointIds [ j ] ;
return nullptr ;
}
vecVtkPointIds [ j ] = oPointIt . value ( ) ;
}
const vtkIdType nExpectedVtkCellId =
static_cast < vtkIdType > ( vecVtkToOriginal . size ( ) ) ;
const vtkIdType nInsertedVtkCellId =
pUnstructuredGrid - > InsertNextCell (
vtkCellType ,
static_cast < vtkIdType > ( nPointsInCell ) ,
vecVtkPointIds . data ( ) ) ;
if ( nInsertedVtkCellId ! = nExpectedVtkCellId ) {
qWarning ( ) < < " PEBI VTK cell insertion order is invalid. " ;
return nullptr ;
}
vecVtkToOriginal . append (
static_cast < vtkIdType > ( nOriginalCellId ) ) ;
}
if ( isCancellationRequested ( pCancelRequested ) ) {
return nullptr ;
}
if ( vecVtkToOriginal . size ( ) ! = nVisibleCellCount | |
pUnstructuredGrid - > GetNumberOfCells ( ) ! =
static_cast < vtkIdType > ( vecVtkToOriginal . size ( ) ) ) {
qWarning ( ) < < " PEBI visible cell mapping is incomplete. " ;
return nullptr ;
}
QString sMappingError ;
if ( ! nmPebiCellMapping : : attach (
pUnstructuredGrid ,
vecVtkToOriginal ,
static_cast < vtkIdType > ( nOriginalCellCount ) ,
& sMappingError ) ) {
qWarning ( ) < < " Unable to attach PEBI cell mapping: "
< < sMappingError ;
return nullptr ;
}
return pUnstructuredGrid ;
}
bool nmCalculationPebiGrid : : beginManualInputSnapshot (
nmDataAnalyzeManager * pDataManager ,
nmPebiGridInputSnapshot & oSnapshot )
{
const nmPebiGridInputSnapshot oEmptySnapshot ;
oSnapshot = oEmptySnapshot ;
if ( pDataManager = = nullptr | |
QThread : : currentThread ( ) ! = pDataManager - > thread ( ) ) {
qWarning ( ) < < " Manual PEBI input must be captured on the DataManager thread. " ;
return false ;
}
oSnapshot . m_bRequireSolverInput = true ;
const nmDataNumericalAnalysisCase * pAnalysisCase =
pDataManager - > getNumericalAnalysisCase ( ) ;
if ( pAnalysisCase = = nullptr ) {
return false ;
}
oSnapshot . m_nGridInputRevision =
pAnalysisCase - > getGridInputRevision ( ) ;
oSnapshot . m_oGridInput = HX_NWTM_GRID_INPUT ( ) ;
// HX_NWTM_GRID_INPUT构造函数带有演示井, 分批追加真实井前必须清空,
// 避免求解前建网混入额外井,并保证网格井槽位与求解井顺序一致。
oSnapshot . m_oGridInput . VerticalWell . clear ( ) ;
oSnapshot . m_oGridInput . FractureVerticalWell . clear ( ) ;
oSnapshot . m_oGridInput . MultistageFracturedHorizontalWell . clear ( ) ;
oSnapshot . m_oGridInput . HorizontalWell . clear ( ) ;
oSnapshot . m_oGridInput . InclinedWell . clear ( ) ;
oSnapshot . m_oGridInput . GridControl =
pDataManager - > getPebiGridControl ( ) ;
// 边界只捕获一次;后续井批次只追加值,不重复访问已经完成的对象。
return meshGenPebiBoundary ( pDataManager , oSnapshot . m_oGridInput ) ;
}
bool nmCalculationPebiGrid : : appendManualWellInputSnapshot (
nmDataWellBase * pWellData ,
int nWellMode ,
nmPebiGridInputSnapshot & oSnapshot )
{
if ( pWellData = = nullptr | | pWellData - > getWellCode ( ) . isEmpty ( ) | |
oSnapshot . m_bCaptured ) {
return false ;
}
nmSolverWellRef oWellRef ;
nmDataHorizontalFracturedWell * pHorizontalFracturedWell =
dynamic_cast < nmDataHorizontalFracturedWell * > ( pWellData ) ;
nmDataVerticalFracturedWell * pVerticalFracturedWell =
dynamic_cast < nmDataVerticalFracturedWell * > ( pWellData ) ;
nmDataVerticalWell * pVerticalWell =
dynamic_cast < nmDataVerticalWell * > ( pWellData ) ;
if ( pHorizontalFracturedWell ! = nullptr ) {
const QVector < QPair < QPointF , QPointF > > vecFracPoints =
pHorizontalFracturedWell - > getFracs ( ) ;
std : : vector < std : : vector < double > > vecFractures ;
vecFractures . reserve ( vecFracPoints . size ( ) ) ;
for ( int nIndex = 0 ; nIndex < vecFracPoints . size ( ) ; + + nIndex ) {
dVec1 oCrack ( 6 ) ;
oCrack [ 0 ] = vecFracPoints [ nIndex ] . first . x ( ) ;
oCrack [ 1 ] = vecFracPoints [ nIndex ] . first . y ( ) ;
oCrack [ 2 ] = vecFracPoints [ nIndex ] . second . x ( ) ;
oCrack [ 3 ] = vecFracPoints [ nIndex ] . second . y ( ) ;
oCrack [ 4 ] = pHorizontalFracturedWell - > getWidth ( )
. getValue ( ) . toDouble ( ) ;
oCrack [ 5 ] = pHorizontalFracturedWell - > getDfc ( )
. getValue ( ) . toDouble ( ) ;
vecFractures . push_back ( oCrack ) ;
}
oSnapshot . m_oGridInput . MultistageFracturedHorizontalWell
. push_back ( vecFractures ) ;
oWellRef = nmSolverWellRef (
- 1 ,
NM_WELL_MODEL : : Horizontal_Fractured_Well ,
pWellData - > getWellCode ( ) ) ;
} else if ( pVerticalFracturedWell ! = nullptr ) {
const QVector < QPointF > vecFracPoints =
pVerticalFracturedWell - > getFracs ( ) ;
if ( vecFracPoints . size ( ) ! = 2 ) {
return false ;
}
dVec1 oCrack ( 6 ) ;
oCrack [ 0 ] = vecFracPoints [ 0 ] . x ( ) ;
oCrack [ 1 ] = vecFracPoints [ 0 ] . y ( ) ;
oCrack [ 2 ] = vecFracPoints [ 1 ] . x ( ) ;
oCrack [ 3 ] = vecFracPoints [ 1 ] . y ( ) ;
oCrack [ 4 ] = pVerticalFracturedWell - > getWidth ( )
. getValue ( ) . toDouble ( ) ;
oCrack [ 5 ] = pVerticalFracturedWell - > getDfc ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oGridInput . FractureVerticalWell . push_back ( oCrack ) ;
oWellRef = nmSolverWellRef (
- 1 ,
NM_WELL_MODEL : : Vertical_Fractured_Well ,
pWellData - > getWellCode ( ) ) ;
} else if ( pVerticalWell ! = nullptr ) {
dVec1 oWell ( 3 ) ;
oWell [ 0 ] = pVerticalWell - > getX ( ) . getValue ( ) . toDouble ( ) ;
oWell [ 1 ] = pVerticalWell - > getY ( ) . getValue ( ) . toDouble ( ) ;
oWell [ 2 ] = pVerticalWell - > getRadius ( ) . getValue ( ) . toDouble ( ) ;
oSnapshot . m_oGridInput . VerticalWell . push_back ( oWell ) ;
oWellRef = nmSolverWellRef (
- 1 ,
NM_WELL_MODEL : : Vertical_Well ,
pWellData - > getWellCode ( ) ) ;
} else {
return false ;
}
nmPebiWellInputSnapshot oWellInput ;
oWellInput . m_bRealWell = true ;
oWellInput . m_sWellCode = pWellData - > getWellCode ( ) ;
oWellInput . m_sWellName = pWellData - > getWellName ( ) ;
oWellInput . m_eWellCategory = pWellData - > getWellCategory ( ) ;
nmWellGaugeInputData oGaugeInput ;
pWellData - > buildGaugeInputData ( oGaugeInput ) ;
oWellInput . m_sGaugeInputSha1 =
nmDataWellBase : : calculateGaugeInputSha1 ( oGaugeInput ) ;
oWellInput . m_nFlowSectionIndex = oGaugeInput . nFlowSectionIndex ;
oWellInput . m_oLocation = QPointF (
pWellData - > getX ( ) . getValue ( ) . toDouble ( ) ,
pWellData - > getY ( ) . getValue ( ) . toDouble ( ) ) ;
oWellInput . m_dWellboreStorage =
pWellData - > getWellboreStorage ( ) . getValue ( ) . toDouble ( ) ;
oWellInput . m_dSkin = pWellData - > getPerforationCount ( ) > 0
? pWellData - > getPerforation ( 0 ) - > getSkin ( ) . getValue ( ) . toDouble ( )
: 0.0 ;
oWellInput . m_bRateControlled =
nWellMode = = static_cast < int > ( NM_CaseWell_RateControlled ) ;
if ( oWellInput . m_sGaugeInputSha1 . isEmpty ( ) | |
! nmIsValidWellCategory ( oWellInput . m_eWellCategory ) | |
( oWellInput . m_bRateControlled & &
( ! captureWellFlowSchedule ( oGaugeInput , oWellInput ) | |
oWellInput . m_nFlowSectionIndex < 1 | |
oWellInput . m_nFlowSectionIndex >
oWellInput . m_vecFlowDurations . size ( ) ) ) ) {
return false ;
}
oSnapshot . m_vecSolverWellOrder . append ( oWellRef ) ;
oSnapshot . m_vecWellInputs . append ( oWellInput ) ;
return true ;
}
bool nmCalculationPebiGrid : : finishManualInputSnapshot (
nmDataAnalyzeManager * pDataManager ,
const QSet < QString > & setEffectiveWellCodes ,
nmPebiGridInputSnapshot & oSnapshot )
{
if ( pDataManager = = nullptr | | setEffectiveWellCodes . isEmpty ( ) | |
QThread : : currentThread ( ) ! = pDataManager - > thread ( ) | |
oSnapshot . m_vecSolverWellOrder . size ( ) ! =
oSnapshot . m_vecWellInputs . size ( ) ) {
return false ;
}
const nmDataNumericalAnalysisCase * pAnalysisCase =
pDataManager - > getNumericalAnalysisCase ( ) ;
if ( pAnalysisCase = = nullptr | |
pAnalysisCase - > getGridInputRevision ( ) ! =
oSnapshot . m_nGridInputRevision ) {
return false ;
}
QHash < QString , nmPebiWellInputSnapshot > mapWellInputs ;
for ( int nIndex = 0 ; nIndex < oSnapshot . m_vecWellInputs . size ( ) ; + + nIndex ) {
const nmPebiWellInputSnapshot & oWellInput =
oSnapshot . m_vecWellInputs [ nIndex ] ;
if ( oWellInput . m_sWellCode . isEmpty ( ) | |
mapWellInputs . contains ( oWellInput . m_sWellCode ) ) {
return false ;
}
mapWellInputs . insert ( oWellInput . m_sWellCode , oWellInput ) ;
}
if ( ! meshGenPebiFault ( pDataManager , oSnapshot . m_oGridInput ) | |
! meshGenPebiCrack ( pDataManager ,
oSnapshot . m_oGridInput ,
oSnapshot . m_vecSolverWellOrder ) ) {
return false ;
}
QVector < nmPebiWellInputSnapshot > vecOrderedWellInputs ;
vecOrderedWellInputs . reserve ( oSnapshot . m_vecSolverWellOrder . size ( ) ) ;
QSet < QString > setOrderedWellCodes ;
for ( int nIndex = 0 ;
nIndex < oSnapshot . m_vecSolverWellOrder . size ( ) ;
+ + nIndex ) {
nmSolverWellRef & oWellRef = oSnapshot . m_vecSolverWellOrder [ nIndex ] ;
oWellRef . m_nSolverIndex = nIndex ;
if ( oWellRef . m_eEntryKind = = NM_SolverEntry_ManualFracture ) {
vecOrderedWellInputs . append ( nmPebiWellInputSnapshot ( ) ) ;
continue ;
}
if ( oWellRef . m_eEntryKind ! = NM_SolverEntry_Well | |
! setEffectiveWellCodes . contains ( oWellRef . m_sWellCode ) | |
setOrderedWellCodes . contains ( oWellRef . m_sWellCode ) | |
! mapWellInputs . contains ( oWellRef . m_sWellCode ) ) {
return false ;
}
setOrderedWellCodes . insert ( oWellRef . m_sWellCode ) ;
vecOrderedWellInputs . append ( mapWellInputs . value ( oWellRef . m_sWellCode ) ) ;
}
if ( setOrderedWellCodes ! = setEffectiveWellCodes ) {
return false ;
}
oSnapshot . m_vecWellInputs = vecOrderedWellInputs ;
const nmDataBinaryTools : : NM_PEBI_SCENE oEmptyScene ;
oSnapshot . m_oScene = oEmptyScene ;
const NM_SOLVER_MODEL_TYPE eSolverModelType =
pDataManager - > getSolverModelType ( ) ;
oSnapshot . m_oScene . solverType =
static_cast < int > ( eSolverModelType ) ;
nmDataReservoir * pReservoirData =
pDataManager - > getReservoirData ( ) ;
nmDataPvtParaForPebi * pPvtData =
pDataManager - > getPebiPvtPara ( ) ;
fillScenePvtByModel ( oSnapshot . m_oScene ,
eSolverModelType ,
pPvtData ,
pReservoirData ) ;
fillScenePseudoPressureTable ( oSnapshot . m_oScene ,
eSolverModelType ,
pDataManager ) ;
if ( pReservoirData ! = nullptr ) {
oSnapshot . m_oScene . Base . Pi = pReservoirData - > getInitialPressure ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Cti = pReservoirData - > getCt ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Cf = pReservoirData - > getCf ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Soi = pReservoirData - > getSoi ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Sgi = pReservoirData - > getSgi ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Swi = pReservoirData - > getSwi ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . k_ref =
nmCalculationUtils : : milliDarcyToDarcy (
pReservoirData - > getPermeability ( )
. getValue ( ) . toDouble ( ) ) ;
oSnapshot . m_oScene . Base . phi_ref = pReservoirData - > getPorosity ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . h_ref = pReservoirData - > getThickness ( )
. getValue ( ) . toDouble ( ) ;
}
nmDataTimeStepSetting * pTimeStepSetting =
pDataManager - > getTimeStep ( ) ;
if ( pTimeStepSetting ! = nullptr ) {
oSnapshot . m_oScene . Base . d =
pTimeStepSetting - > getTimeGrowthExponent ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . dt_Min =
pTimeStepSetting - > getMinDeltaTAttribute ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . dt_Max =
pTimeStepSetting - > getMaxDeltaTAttribute ( )
. getValue ( ) . toDouble ( ) ;
}
oSnapshot . m_sLicensePath = pDataManager - > getLicensePath ( ) ;
oSnapshot . m_bCaptured = true ;
oSnapshot . m_bValid = false ;
return true ;
}
bool nmCalculationPebiGrid : : captureInputSnapshot (
nmDataAnalyzeManager * pDataManager ,
nmPebiGridInputSnapshot & oSnapshot ,
bool bDeferPreparation ,
bool bRequireSolverInput )
{
// 使用具名常量触发复制赋值,兼容 Qt 4.8 配套的 VS2010 运行库 ABI。
const nmPebiGridInputSnapshot oEmptySnapshot ;
oSnapshot = oEmptySnapshot ;
if ( pDataManager = = nullptr ) {
return false ;
}
oSnapshot . m_bRequireSolverInput = bRequireSolverInput ;
// DataManager 及井对象都由所属线程维护。快照只允许在该线程创建,后台任务
// 随后只读取复制出的 STL/Qt 值类型,不能再次访问这些可变对象。
if ( QThread : : currentThread ( ) ! = pDataManager - > thread ( ) ) {
qWarning ( ) < < " PEBI input snapshot must be captured on the DataManager thread. " ;
return false ;
}
const nmDataNumericalAnalysisCase * pAnalysisCase =
pDataManager - > getNumericalAnalysisCase ( ) ;
if ( pAnalysisCase = = nullptr ) {
return false ;
}
// 第一步:先记录版本并复制网格控制参数。调用方必须在启动后台线程前完成本函数。
oSnapshot . m_nGridInputRevision =
pAnalysisCase - > getGridInputRevision ( ) ;
oSnapshot . m_oGridInput = HX_NWTM_GRID_INPUT ( ) ;
oSnapshot . m_oGridInput . GridControl =
pDataManager - > getPebiGridControl ( ) ;
// 第二步:有效井集合只计算一次,后续几何、角色和结果井快照共同复用。
const QVector < nmCalculationWellRef > vecEffectiveWells =
pDataManager - > getEffectiveCalculationWells ( ) ;
QSet < QString > setEffectiveWellCodes ;
QHash < QString , int > mapWellModes ;
for ( int nIndex = 0 ; nIndex < vecEffectiveWells . size ( ) ; + + nIndex ) {
setEffectiveWellCodes . insert ( vecEffectiveWells [ nIndex ] . m_sWellCode ) ;
mapWellModes . insert ( vecEffectiveWells [ nIndex ] . m_sWellCode ,
static_cast < int > ( vecEffectiveWells [ nIndex ] . m_eMode ) ) ;
}
// 第三步:分别记录输入捕获失败的步骤,避免界面只得到一个 false。
if ( ! meshGenPebiBoundary ( pDataManager , oSnapshot . m_oGridInput ) ) {
oSnapshot . m_sCaptureFailure = nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Unable to capture the grid boundary. Check that the analysis has a boundary. " ) ) ;
return false ;
}
if ( ! meshGenPebiWells ( pDataManager ,
oSnapshot . m_oGridInput ,
oSnapshot . m_vecSolverWellOrder ,
setEffectiveWellCodes ) ) {
oSnapshot . m_sCaptureFailure = nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Unable to capture well geometry. Check participating well types and fracture endpoints. " ) ) ;
return false ;
}
if ( ! meshGenPebiFault ( pDataManager , oSnapshot . m_oGridInput ) | |
! meshGenPebiCrack ( pDataManager ,
oSnapshot . m_oGridInput ,
oSnapshot . m_vecSolverWellOrder ) ) {
oSnapshot . m_sCaptureFailure = nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Unable to capture fault/fracture geometry. " ) ) ;
return false ;
}
for ( int nIndex = 0 ;
nIndex < oSnapshot . m_vecSolverWellOrder . size ( ) ;
+ + nIndex ) {
oSnapshot . m_vecSolverWellOrder [ nIndex ] . m_nSolverIndex = nIndex ;
}
// 第四步:真实井必须与本次有效计算井一一对应;手工裂缝只占槽位。
QSet < QString > setOrderedWellCodes ;
bool bSolverOrderValid = ! setEffectiveWellCodes . isEmpty ( ) ;
for ( int nIndex = 0 ;
bSolverOrderValid & &
nIndex < oSnapshot . m_vecSolverWellOrder . size ( ) ;
+ + nIndex ) {
const nmSolverWellRef & oWellRef =
oSnapshot . m_vecSolverWellOrder [ nIndex ] ;
if ( oWellRef . m_eEntryKind = = NM_SolverEntry_ManualFracture ) {
continue ;
}
bSolverOrderValid =
oWellRef . m_eEntryKind = = NM_SolverEntry_Well & &
setEffectiveWellCodes . contains ( oWellRef . m_sWellCode ) & &
! setOrderedWellCodes . contains ( oWellRef . m_sWellCode ) ;
if ( bSolverOrderValid ) {
setOrderedWellCodes . insert ( oWellRef . m_sWellCode ) ;
}
}
bSolverOrderValid = bSolverOrderValid & &
setOrderedWellCodes = = setEffectiveWellCodes ;
if ( ! bSolverOrderValid ) {
qWarning ( ) < < " PEBI solver order does not match effective wells. " ;
oSnapshot . m_sCaptureFailure = nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Participating wells do not match the grid well list. Check the primary well and included wells. " ) ) ;
return false ;
}
// 独立建网到此已经取得 DLL 实际消费的全部几何输入和井槽位顺序。
// 不再读取流量、流动段、PVT、储层或时间步, 避免建网依赖求解数据状态。
if ( ! oSnapshot . m_bRequireSolverInput ) {
oSnapshot . m_sLicensePath = pDataManager - > getLicensePath ( ) ;
oSnapshot . m_bCaptured = true ;
return bDeferPreparation
? true : prepareInputSnapshot ( oSnapshot ) ;
}
// 第五步:建立 WellCode 索引并一次复制每口井,避免五十口井时反复线性查找。
QHash < QString , nmDataWellBase * > mapWellsByCode ;
const QVector < nmDataWellBase * > vecAllWells =
pDataManager - > getWellDataList ( ) ;
for ( int nIndex = 0 ; nIndex < vecAllWells . size ( ) ; + + nIndex ) {
nmDataWellBase * pWellData = vecAllWells [ nIndex ] ;
if ( pWellData ! = nullptr & & ! pWellData - > getWellCode ( ) . isEmpty ( ) ) {
mapWellsByCode . insert ( pWellData - > getWellCode ( ) , pWellData ) ;
}
}
oSnapshot . m_vecWellInputs . clear ( ) ;
oSnapshot . m_vecWellInputs . reserve (
oSnapshot . m_vecSolverWellOrder . size ( ) ) ;
for ( int nIndex = 0 ;
nIndex < oSnapshot . m_vecSolverWellOrder . size ( ) ;
+ + nIndex ) {
const nmSolverWellRef & oWellRef =
oSnapshot . m_vecSolverWellOrder [ nIndex ] ;
nmPebiWellInputSnapshot oWellInput ;
oWellInput . m_sWellCode = oWellRef . m_sWellCode ;
if ( oWellRef . m_eEntryKind = = NM_SolverEntry_ManualFracture ) {
oSnapshot . m_vecWellInputs . append ( oWellInput ) ;
continue ;
}
nmDataWellBase * pWellData =
mapWellsByCode . value ( oWellRef . m_sWellCode , nullptr ) ;
if ( pWellData = = nullptr | | ! mapWellModes . contains ( oWellRef . m_sWellCode ) ) {
qWarning ( ) < < " Solver well code is missing from Map: "
< < oWellRef . m_sWellCode ;
return false ;
}
oWellInput . m_bRealWell = true ;
oWellInput . m_sWellName = pWellData - > getWellName ( ) ;
oWellInput . m_eWellCategory = pWellData - > getWellCategory ( ) ;
nmWellGaugeInputData oGaugeInput ;
pWellData - > buildGaugeInputData ( oGaugeInput ) ;
oWellInput . m_sGaugeInputSha1 =
nmDataWellBase : : calculateGaugeInputSha1 ( oGaugeInput ) ;
oWellInput . m_nFlowSectionIndex = oGaugeInput . nFlowSectionIndex ;
oWellInput . m_oLocation = QPointF (
pWellData - > getX ( ) . getValue ( ) . toDouble ( ) ,
pWellData - > getY ( ) . getValue ( ) . toDouble ( ) ) ;
oWellInput . m_dWellboreStorage =
pWellData - > getWellboreStorage ( ) . getValue ( ) . toDouble ( ) ;
oWellInput . m_dSkin = pWellData - > getPerforationCount ( ) > 0
? pWellData - > getPerforation ( 0 ) - > getSkin ( )
. getValue ( ) . toDouble ( )
: 0.0 ;
oWellInput . m_bRateControlled =
mapWellModes . value ( oWellRef . m_sWellCode ) = =
static_cast < int > ( NM_CaseWell_RateControlled ) ;
if ( oWellInput . m_sGaugeInputSha1 . isEmpty ( ) | |
( oSnapshot . m_bRequireSolverInput & &
! nmIsValidWellCategory ( oWellInput . m_eWellCategory ) ) ) {
qWarning ( ) < < " Solver well has an invalid category: "
< < oWellRef . m_sWellCode ;
return false ;
}
if ( oSnapshot . m_bRequireSolverInput & &
oWellInput . m_bRateControlled & &
( ! captureWellFlowSchedule ( oGaugeInput , oWellInput ) | |
oWellInput . m_nFlowSectionIndex < 1 | |
oWellInput . m_nFlowSectionIndex >
oWellInput . m_vecFlowDurations . size ( ) ) ) {
qWarning ( ) < < " Rate-controlled well has no valid rate schedule: "
< < oWellRef . m_sWellCode ;
return false ;
}
oSnapshot . m_vecWellInputs . append ( oWellInput ) ;
}
// 第六步: PVT、储层和时间步仍在主线程复制为值; 较重的井制度和场景数组
// 组装可由手工求解任务延后到后台执行。
const nmDataBinaryTools : : NM_PEBI_SCENE oEmptyScene ;
oSnapshot . m_oScene = oEmptyScene ;
const NM_SOLVER_MODEL_TYPE eSolverModelType =
pDataManager - > getSolverModelType ( ) ;
oSnapshot . m_oScene . solverType =
static_cast < int > ( eSolverModelType ) ;
nmDataReservoir * pReservoirData =
pDataManager - > getReservoirData ( ) ;
nmDataPvtParaForPebi * pPvtData =
pDataManager - > getPebiPvtPara ( ) ;
fillScenePvtByModel ( oSnapshot . m_oScene ,
eSolverModelType ,
pPvtData ,
pReservoirData ) ;
fillScenePseudoPressureTable ( oSnapshot . m_oScene ,
eSolverModelType ,
pDataManager ) ;
if ( pReservoirData ! = nullptr ) {
oSnapshot . m_oScene . Base . Pi = pReservoirData - > getInitialPressure ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Cti = pReservoirData - > getCt ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Cf = pReservoirData - > getCf ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Soi = pReservoirData - > getSoi ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Sgi = pReservoirData - > getSgi ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . Swi = pReservoirData - > getSwi ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . k_ref =
nmCalculationUtils : : milliDarcyToDarcy (
pReservoirData - > getPermeability ( )
. getValue ( ) . toDouble ( ) ) ;
oSnapshot . m_oScene . Base . phi_ref = pReservoirData - > getPorosity ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . h_ref = pReservoirData - > getThickness ( )
. getValue ( ) . toDouble ( ) ;
}
nmDataTimeStepSetting * pTimeStepSetting =
pDataManager - > getTimeStep ( ) ;
if ( pTimeStepSetting ! = nullptr ) {
oSnapshot . m_oScene . Base . d =
pTimeStepSetting - > getTimeGrowthExponent ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . dt_Min =
pTimeStepSetting - > getMinDeltaTAttribute ( )
. getValue ( ) . toDouble ( ) ;
oSnapshot . m_oScene . Base . dt_Max =
pTimeStepSetting - > getMaxDeltaTAttribute ( )
. getValue ( ) . toDouble ( ) ;
}
oSnapshot . m_sLicensePath = pDataManager - > getLicensePath ( ) ;
oSnapshot . m_bCaptured = true ;
return bDeferPreparation
? true
: prepareInputSnapshot ( oSnapshot ) ;
}
bool nmCalculationPebiGrid : : prepareInputSnapshot (
nmPebiGridInputSnapshot & oSnapshot ,
const QAtomicInt * pCancelRequested )
{
oSnapshot . m_bValid = false ;
if ( ! oSnapshot . m_bCaptured | |
isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
// 独立建网只消费几何输入,不因流量记录或求解参数不可用而失败。
if ( ! oSnapshot . m_bRequireSolverInput ) {
oSnapshot . m_bValid = true ;
return true ;
}
if ( oSnapshot . m_vecWellInputs . size ( ) ! =
oSnapshot . m_vecSolverWellOrder . size ( ) ) {
return false ;
}
return buildPebiScene ( oSnapshot , pCancelRequested ) ;
}
bool nmCalculationPebiGrid : : buildPebiScene (
nmPebiGridInputSnapshot & oSnapshot ,
const QAtomicInt * pCancelRequested )
{
const HX_NWTM_GRID_INPUT & oGridInput = oSnapshot . m_oGridInput ;
const QVector < nmSolverWellRef > & vecSolverWellOrder =
oSnapshot . m_vecSolverWellOrder ;
const QVector < nmPebiWellInputSnapshot > & vecWellInputs =
oSnapshot . m_vecWellInputs ;
nmDataBinaryTools : : NM_PEBI_SCENE & oScene = oSnapshot . m_oScene ;
// 第一步: 复制网格基础数据。PVT、储层和时间步已在主线程值化。
oScene . version = 1 ;
oScene . D = oGridInput . D ;
oScene . GridControl = oGridInput . GridControl ;
oScene . Boundary = oGridInput . Boundary ;
oScene . VerticalWell = oGridInput . VerticalWell ;
oScene . HorizontalWell = oGridInput . HorizontalWell ;
oScene . FractureVerticalWell = oGridInput . FractureVerticalWell ;
oScene . MultistageFracturedHorizontalWell =
oGridInput . MultistageFracturedHorizontalWell ;
oScene . InclinedWell = oGridInput . InclinedWell ;
oScene . Fault = oGridInput . Fault ;
oScene . wellType . clear ( ) ;
oScene . wellName . clear ( ) ;
oScene . wellType . reserve ( vecSolverWellOrder . size ( ) ) ;
oScene . wellName . reserve ( vecSolverWellOrder . size ( ) ) ;
for ( int nIndex = 0 ; nIndex < vecSolverWellOrder . size ( ) ; + + nIndex ) {
if ( isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
const nmSolverWellRef & oWellRef = vecSolverWellOrder [ nIndex ] ;
oScene . wellType . push_back ( static_cast < int > ( oWellRef . m_eWellType ) ) ;
oScene . wellName . push_back ( vecWellInputs [ nIndex ] . m_sWellName ) ;
}
// 第二步:把冻结的公共时间轴及油、气、水流量原样写入求解器输入。
const NM_SOLVER_MODEL_TYPE eSolverModelType =
static_cast < NM_SOLVER_MODEL_TYPE > ( oScene . solverType ) ;
if ( ! nmIsSupportedPebiSolverModel ( eSolverModelType ) ) {
qWarning ( ) < < " PEBI solver model is not supported: "
< < static_cast < int > ( eSolverModelType ) ;
return false ;
}
oScene . Rate . t . resize ( vecSolverWellOrder . size ( ) ) ;
oScene . Rate . qo . resize ( vecSolverWellOrder . size ( ) ) ;
oScene . Rate . qg . resize ( vecSolverWellOrder . size ( ) ) ;
oScene . Rate . qw . resize ( vecSolverWellOrder . size ( ) ) ;
for ( int nWellIndex = 0 ;
nWellIndex < vecSolverWellOrder . size ( ) ;
+ + nWellIndex ) {
const nmSolverWellRef & oWellRef =
vecSolverWellOrder [ nWellIndex ] ;
const nmPebiWellInputSnapshot & oWellInput =
vecWellInputs [ nWellIndex ] ;
if ( isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
// 手工裂缝和观察井保留外层槽位,但不提供源汇项。
if ( oWellRef . m_eEntryKind = = NM_SolverEntry_ManualFracture | |
! oWellInput . m_bRateControlled ) {
continue ;
}
// 多相记录已经明确提供 qo/qg/qw, 井别不再用于裁剪或拦截流量相。
// 此处只检查求解器数组能否按公共段时长逐项组装。
if ( oWellInput . m_vecFlowDurations . isEmpty ( ) | |
oWellInput . m_vecOilRates . size ( ) ! =
oWellInput . m_vecFlowDurations . size ( ) | |
oWellInput . m_vecGasRates . size ( ) ! =
oWellInput . m_vecFlowDurations . size ( ) | |
oWellInput . m_vecWaterRates . size ( ) ! =
oWellInput . m_vecFlowDurations . size ( ) | |
oWellInput . m_nFlowSectionIndex < 1 | |
oWellInput . m_nFlowSectionIndex >
oWellInput . m_vecFlowDurations . size ( ) ) {
qWarning ( ) < < " Rate-controlled well has inconsistent rate schedule arrays: "
< < oWellInput . m_sWellCode ;
return false ;
}
std : : vector < double > vecTime ;
std : : vector < double > vecOil ;
std : : vector < double > vecGas ;
std : : vector < double > vecWater ;
vecTime . reserve ( oWellInput . m_vecFlowDurations . size ( ) ) ;
vecOil . reserve ( oWellInput . m_vecFlowDurations . size ( ) ) ;
vecGas . reserve ( oWellInput . m_vecFlowDurations . size ( ) ) ;
vecWater . reserve ( oWellInput . m_vecFlowDurations . size ( ) ) ;
for ( int nPointIndex = 0 ;
nPointIndex < oWellInput . m_vecFlowDurations . size ( ) ;
+ + nPointIndex ) {
if ( ( nPointIndex % 256 ) = = 0 & &
isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
vecTime . push_back ( oWellInput . m_vecFlowDurations [ nPointIndex ] ) ;
vecOil . push_back ( oWellInput . m_vecOilRates [ nPointIndex ] ) ;
vecGas . push_back ( oWellInput . m_vecGasRates [ nPointIndex ] ) ;
vecWater . push_back ( oWellInput . m_vecWaterRates [ nPointIndex ] ) ;
}
oScene . Rate . t [ nWellIndex ] = vecTime ;
oScene . Rate . qo [ nWellIndex ] = vecOil ;
oScene . Rate . qg [ nWellIndex ] = vecGas ;
oScene . Rate . qw [ nWellIndex ] = vecWater ;
}
// 第三步:复制井筒储集、表皮和流量段索引。
oScene . CS . C . resize ( vecSolverWellOrder . size ( ) ) ;
oScene . CS . S . resize ( vecSolverWellOrder . size ( ) ) ;
oScene . wellFlowSectionIndex . resize ( vecSolverWellOrder . size ( ) ) ;
for ( int nWellIndex = 0 ;
nWellIndex < vecSolverWellOrder . size ( ) ;
+ + nWellIndex ) {
const nmSolverWellRef & oWellRef =
vecSolverWellOrder [ nWellIndex ] ;
const nmPebiWellInputSnapshot & oWellInput =
vecWellInputs [ nWellIndex ] ;
if ( isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
if ( oWellRef . m_eEntryKind = = NM_SolverEntry_ManualFracture ) {
oScene . CS . C [ nWellIndex ] = 0.0 ;
oScene . CS . S [ nWellIndex ] = 0.0 ;
oScene . wellFlowSectionIndex [ nWellIndex ] = 1 ;
continue ;
}
oScene . CS . C [ nWellIndex ] = oWellInput . m_dWellboreStorage ;
oScene . CS . S [ nWellIndex ] = oWellInput . m_dSkin ;
// 观察井没有流量段时该字段不参与物理计算,仍写入合法的一基占位值。
oScene . wellFlowSectionIndex [ nWellIndex ] =
oWellInput . m_nFlowSectionIndex > = 1
? oWellInput . m_nFlowSectionIndex : 1 ;
}
oSnapshot . m_bValid = ! isCancellationRequested ( pCancelRequested ) ;
return oSnapshot . m_bValid ;
}
void nmCalculationPebiGrid : : recordSnapshotFailure (
nmPebiGridResult & oResult ,
const QString & reason )
{
oResult . m_bSucceeded = false ;
oResult . m_sFailureReason = reason ;
// 使用框架日志接口,由框架处理跨线程显示及保存;每次失败只写一条简短提示。
zxLogInstance : : getInstance ( ) - > writeLogW (
nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Grid generation failed: %1 " ) ) . arg ( reason . simplified ( ) ) ) ;
}
bool nmCalculationPebiGrid : : calculateSnapshot (
const nmPebiGridInputSnapshot & oSnapshot ,
nmPebiGridResult & oResult ,
bool bCreateUnstructuredGrid ,
const QAtomicInt * pCancelRequested )
{
const nmPebiGridResult oEmptyResult ;
oResult = oEmptyResult ;
nmInterruptibleMutexLocker oGridLocker ;
if ( ! oGridLocker . lock ( & s_oPebiGridMutex , pCancelRequested ) ) {
return false ;
}
if ( isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
if ( ! oSnapshot . m_bValid ) {
recordSnapshotFailure ( oResult , oSnapshot . m_sCaptureFailure . isEmpty ( )
? nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Grid input capture/preparation failed. Check the boundary and participating wells. " ) )
: oSnapshot . m_sCaptureFailure ) ;
return false ;
}
const QString inputError = nmPebiGridDiagnostics : : validate ( oSnapshot . m_oGridInput ) ;
if ( ! inputError . isEmpty ( ) ) {
recordSnapshotFailure ( oResult , inputError ) ;
return false ;
}
// 建网、模型求解和 Kriging 共用 DLL 全局状态,加载至读取结果期间必须串行。
nmInterruptibleMutexLocker oDllLocker ;
if ( ! oDllLocker . lock ( nmCalculationUtils : : getHxNwtmDllMutex ( ) ,
pCancelRequested ) ) {
return false ;
}
HMODULE hGridModule = LoadLibrary ( L " HX_NWTM.dll " ) ;
if ( hGridModule = = nullptr ) {
const DWORD error = GetLastError ( ) ;
recordSnapshotFailure ( oResult ,
nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Unable to load HX_NWTM.dll (Windows error %1). Check the DLL and its runtime dependencies. " ) ) . arg ( error ) ) ;
return false ;
}
HX_NWTM_GRID_Func pfnGenerateGrid =
reinterpret_cast < HX_NWTM_GRID_Func > (
GetProcAddress ( hGridModule , " HX_NWTM_GRID " ) ) ;
if ( pfnGenerateGrid = = nullptr ) {
const DWORD error = GetLastError ( ) ;
recordSnapshotFailure ( oResult ,
nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " HX_NWTM_GRID is missing from the DLL (Windows error %1). Check the library version. " ) ) . arg ( error ) ) ;
FreeLibrary ( hGridModule ) ;
return false ;
}
try {
if ( isCancellationRequested ( pCancelRequested ) ) {
FreeLibrary ( hGridModule ) ;
return false ;
}
// 第二步: DLL 输出先落在局部结果,失败时不清空单例缓存和已有成果。
if ( ! invokePebiGridDllGuarded ( pfnGenerateGrid , oSnapshot , oResult ) ) {
// 本轮结果不再用于显示或求解,沿用现有失败通知保留旧成果。
// 用户取消不是输入失败,不生成误导性的“半径过小”等排查提示。
if ( ! isCancellationRequested ( pCancelRequested ) )
recordSnapshotFailure ( oResult ,
nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " The grid algorithm encountered an access violation (0xC0000005). " ) )
+ " \n " + nmPebiGridDiagnostics : : failureHint ( ) ) ;
nmCalculationUtils : : cleanupPebiGridDebugFiles ( ) ;
FreeLibrary ( hGridModule ) ;
hGridModule = nullptr ;
return false ;
}
nmCalculationUtils : : cleanupPebiGridDebugFiles ( ) ;
// 后续 VTK 构造只读取本次局部输出,无需继续占用进程级 DLL 锁。
FreeLibrary ( hGridModule ) ;
hGridModule = nullptr ;
oDllLocker . unlock ( ) ;
// DLL 本身没有取消入口;若执行期间收到停止请求,返回后立即丢弃局部输出。
if ( isCancellationRequested ( pCancelRequested ) ) {
return false ;
}
if ( oResult . m_oGridOutput1 . PEBI_cell . p . empty ( ) | |
oResult . m_oGridOutput1 . PEBI_cell . pindex . empty ( ) | |
oResult . m_oGridOutput2 . Area . empty ( ) ) {
// 自动拟合跳过 VTK 转换时也必须检查空输出,不能把空网格当成功。
recordSnapshotFailure ( oResult ,
nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " The grid algorithm returned empty grid data. Check the license and geometry inputs. " ) )
+ " \n " + nmPebiGridDiagnostics : : failureHint ( ) ) ;
return false ;
}
// 自动拟合只需要 DLL 数组,可跳过 VTK 构造;网格任务必须生成完整 VTK。
if ( bCreateUnstructuredGrid ) {
oResult . m_pUnstructuredGrid =
createPebiUnstructuredGrid (
oResult . m_oGridOutput1 ,
pCancelRequested ) ;
if ( oResult . m_pUnstructuredGrid = = nullptr | |
oResult . m_pUnstructuredGrid - > GetNumberOfCells ( ) < = 0 ) {
if ( ! isCancellationRequested ( pCancelRequested ) )
recordSnapshotFailure ( oResult ,
nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " The generated grid cannot be displayed: invalid points, cell indices or degenerate cells. " ) )
+ " \n " + nmPebiGridDiagnostics : : failureHint ( ) ) ;
return false ;
}
}
oResult . m_bSucceeded = ! isCancellationRequested ( pCancelRequested ) ;
} catch ( const std : : exception & e ) {
nmCalculationUtils : : cleanupPebiGridDebugFiles ( ) ;
if ( ! isCancellationRequested ( pCancelRequested ) )
recordSnapshotFailure ( oResult ,
nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Grid calculation raised an exception: %1 " ) ) . arg ( QString : : fromLocal8Bit ( e . what ( ) ) )
+ " \n " + nmPebiGridDiagnostics : : failureHint ( ) ) ;
if ( hGridModule ! = nullptr ) {
FreeLibrary ( hGridModule ) ;
}
return false ;
} catch ( . . . ) {
nmCalculationUtils : : cleanupPebiGridDebugFiles ( ) ;
if ( ! isCancellationRequested ( pCancelRequested ) )
recordSnapshotFailure ( oResult ,
nmPebiGridDiagnostics : : text ( QT_TRANSLATE_NOOP ( " nmPebiGridDiagnostics " , " Grid calculation raised an unknown exception. " ) )
+ " \n " + nmPebiGridDiagnostics : : failureHint ( ) ) ;
if ( hGridModule ! = nullptr ) {
FreeLibrary ( hGridModule ) ;
}
return false ;
}
return true ;
}
void nmCalculationPebiGrid : : commitOutputCache (
nmDataAnalyzeManager * pDataManager ,
const nmPebiGridInputSnapshot & oSnapshot ,
const nmPebiGridResult & oResult )
{
p0 = oSnapshot . m_oGridInput ;
p1 = oResult . m_oGridOutput1 ;
p2 = oResult . m_oGridOutput2 ;
m_dGridControl = oSnapshot . m_oGridInput . GridControl ;
m_nCachedGridInputRevision = oSnapshot . m_nGridInputRevision ;
m_pDataManager = pDataManager ;
}
bool nmCalculationPebiGrid : : commitSnapshotResult (
nmDataAnalyzeManager * pDataManager ,
const nmPebiGridInputSnapshot & oSnapshot ,
const nmPebiGridResult & oResult )
{
QMutexLocker oLocker ( & s_oPebiGridMutex ) ;
if ( pDataManager = = nullptr | |
! oSnapshot . m_bValid | |
! oResult . m_bSucceeded | |
oResult . m_pUnstructuredGrid = = nullptr ) {
return false ;
}
// 第一步: DataManager 在主线程内按版本整体替换井顺序、有效状态和 VTK 网格。
if ( ! pDataManager - > commitPebiGridResult (
oSnapshot . m_nGridInputRevision ,
oSnapshot . m_vecSolverWellOrder ,
oResult . m_pUnstructuredGrid ) ) {
return false ;
}
// 第二步:只有成果提交成功后才更新进程级 DLL 缓存,旧任务不能覆盖新网格。
commitOutputCache ( pDataManager , oSnapshot , oResult ) ;
return true ;
}