You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Src/nmNum/nmCalculation/nmCalculationPebiGrid.cpp

1087 lines
39 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "nmCalculationPebiGrid.h"
#include <Windows.h>
#include <QApplication>
#include <QDebug>
#include <QDir>
#include <cmath>
#include <QSet>
#include "nmCalculationUtils.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 <vtkPoints.h>
#include <vtkCellArray.h>
#include <vtkType.h>
#include <vtkUnsignedCharArray.h>
#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()
{
// 默认值来自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()
{
}
bool nmCalculationPebiGrid::meshGenPebi()
{
if(this->generateOutputPara() == false) {
return false;
}
// 根据生成的p1生成VTK格式的非结构化网格
vtkSmartPointer<vtkUnstructuredGrid> pGrid = this->createPebiUnstructuredGrid(p1);
Q_ASSERT(nullptr != pGrid);
// 更新当期分析下的非结构化网格对象
nmDataAnalyzeManager* pCurDataManager = nmDataAnalyzeManager::getCurrentInstance();
Q_ASSERT(nullptr != pCurDataManager);
pCurDataManager->setUnstructuredGrid(pGrid);
return true;
}
void nmCalculationPebiGrid::setGridControl(double gridControl)
{
// GridControl必须为正数非法值保持原有设置
if(gridControl > 0.0) {
m_dGridControl = gridControl;
}
}
void nmCalculationPebiGrid::clearGridData()
{
// 关闭网格窗口后清空PEBI单例缓存避免后续求解误用旧网格输入输出。
p0 = HX_NWTM_GRID_INPUT();
p1 = HX_NWTM_GRID_OUTPUT1();
p2 = HX_NWTM_GRID_OUTPUT2();
m_dGridControl = p0.GridControl;
}
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() >= 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() >= 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(HX_NWTM_GRID_INPUT& inputObj)
{
// 1、从数据中心获取边界数据
nmDataOutline* pOutlineData = nmDataAnalyzeManager::getCurrentInstance()->getOutlineData();
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(HX_NWTM_GRID_INPUT& inputObj)
{
// 从数据中心获取井数据
//QVector<nmDataWellBase*> vecDataWells = nmDataAnalyzeManager::getCurrentInstance()->getWellDataList();
//if (vecDataWells.size() == 0) {
// return true;
//}
nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getCurrentInstance();
inputObj.VerticalWell.clear();
inputObj.FractureVerticalWell.clear();
inputObj.MultistageFracturedHorizontalWell.clear();
// 水平井初始化
inputObj.HorizontalWell.clear();
// 斜井
inputObj.InclinedWell.clear();
// 清空自定义井顺序
pDataManager->clearCalculationWells();
// 获取直井数据
QVector<nmDataVerticalWell*> verticalWells = pDataManager->getVerticalWellData();
// 获取垂直裂缝井数据
QVector<nmDataVerticalFracturedWell*> vFracturedWells = pDataManager->getVerticalFracturedWellData();
// 获取多段压裂水平井数据
QVector<nmDataHorizontalFracturedWell*> hFracturedWells = pDataManager->getHorizontalFracturedWellData();
// 处理直井数据
foreach(nmDataVerticalWell* pVerticalWell, verticalWells) {
if(pVerticalWell != nullptr) {
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);
// 添加到自定义井顺序数组
pDataManager->appendCalculationWell(QPair<NM_WELL_MODEL, QString>(NM_WELL_MODEL::Vertical_Well, pVerticalWell->getWellName()));
}
}
// 处理垂直裂缝井数据
foreach(nmDataVerticalFracturedWell* pVerticalFracturedWell, vFracturedWells) {
if(pVerticalFracturedWell != nullptr) {
QVector<QPointF> vFracPoints = pVerticalFracturedWell->getFracs();
if(vFracPoints.size() == 2) {
dVec1 crack(5);
crack[0] = vFracPoints[0].x();
crack[1] = vFracPoints[0].y();
crack[2] = vFracPoints[1].x();
crack[3] = vFracPoints[1].y();
crack[4] = 1; // 裂缝宽度
inputObj.FractureVerticalWell.push_back(crack);
// 添加到自定义井顺序数组
pDataManager->appendCalculationWell(QPair<NM_WELL_MODEL, QString>(NM_WELL_MODEL::Vertical_Fractured_Well, pVerticalFracturedWell->getWellName()));
}
}
}
// 处理多段压裂水平井数据
foreach(nmDataHorizontalFracturedWell* pHorizontalFracturedWell, hFracturedWells) {
if(pHorizontalFracturedWell != nullptr) {
QVector<QPair<QPointF, QPointF>> vvecFracPoints = pHorizontalFracturedWell->getFracs();
std::vector<std::vector<double>> vMultistageFracturedHorizontalWell;
for(int j = 0; j < vvecFracPoints.size(); ++j) {
dVec1 crack(5);
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] = 2; // 裂缝宽度
vMultistageFracturedHorizontalWell.push_back(crack);
}
inputObj.MultistageFracturedHorizontalWell.push_back(vMultistageFracturedHorizontalWell);
// 添加到自定义井顺序数组
pDataManager->appendCalculationWell(QPair<NM_WELL_MODEL, QString>(NM_WELL_MODEL::Horizontal_Fractured_Well, pHorizontalFracturedWell->getWellName()));
}
}
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(HX_NWTM_GRID_INPUT & inputObj)
{
// 从数据中心获取断层数据
QVector<nmDataFault*> vecDataFault = nmDataAnalyzeManager::getCurrentInstance()->getFaultDataList();
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(HX_NWTM_GRID_INPUT &inputObj)
{
// 从数据中心获取裂缝几何数据
QVector<nmDataFracture*> vecDataFracture = nmDataAnalyzeManager::getCurrentInstance()->getFractureDataList();
// 裂缝,将所有裂缝当一个 裂缝直井来处理
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(5);
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;
qDebug() << crack[0] << crack[1] << crack[2] << crack[3] << crack[4];
int iIndex = inputObj.VerticalWell.size() + inputObj.FractureVerticalWell.size();
inputObj.FractureVerticalWell.push_back(crack);
//int iIndex = inputObj.VerticalWell.size() + inputObj.FractureVerticalWell.size();
// 添加到自定义井顺序数组
nmDataAnalyzeManager::getCurrentInstance()->insertCalculationWell(iIndex, QPair<NM_WELL_MODEL, QString>(NM_WELL_MODEL::Unknow_Well, ""));
}
}
}
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("\nCELLS %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("\nCELL_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()
{
return p1;
}
HX_NWTM_GRID_OUTPUT2 nmCalculationPebiGrid::getGridOutput2()
{
return p2;
}
vtkSmartPointer<vtkUnstructuredGrid> nmCalculationPebiGrid::createPebiUnstructuredGrid(const HX_NWTM_GRID_OUTPUT1& P1)
{
vtkSmartPointer<vtkUnstructuredGrid> pUnstructuredGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();
vtkSmartPointer<vtkPoints> pPoints = vtkSmartPointer<vtkPoints>::New();
// 用于存储所有有效单元引用的唯一点索引
QSet<int> setUniquePointIndices;
// 1. 预处理单元数据,确定哪些单元是有效的,并收集这些单元引用的所有唯一点索引
// 这一步先不向 vtkPoints 添加点,而是收集需要添加的点的索引。
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 nPointsInCell = vecIndices.size();
if(nPointsInCell > 1 && vecIndices[0] == vecIndices[nPointsInCell - 1]) {
nPointsInCell--;
}
if(nPointsInCell < 2) continue; // 过滤掉无效单元点数小于2
// 将此有效单元引用的所有点索引添加到 setUniquePointIndices 集合中
for(int j = 0; j < nPointsInCell; ++j) {
setUniquePointIndices.insert(vecIndices[j]);
}
}
// 创建一个映射表,将原始点索引映射到 VTK 中的新点索引
QMap<int, vtkIdType> mapOriginalToVtkPointId;
vtkIdType currentVtkPointId = 0;
// 2. 根据收集到的唯一点索引,将这些点添加到 vtkPoints
pPoints->SetNumberOfPoints(setUniquePointIndices.size());
// 遍历 setUniquePointIndices
foreach(int nOriginalIdx, setUniquePointIndices) {
if(nOriginalIdx >= 0 && nOriginalIdx < P1.PEBI_cell.p.size()) {
pPoints->SetPoint(currentVtkPointId, P1.PEBI_cell.p[nOriginalIdx].x, P1.PEBI_cell.p[nOriginalIdx].y, 0.0);
mapOriginalToVtkPointId[nOriginalIdx] = currentVtkPointId;
currentVtkPointId++;
} else {
// 处理异常情况:如果 setUniquePointIndices 中包含了无效的原始点索引
qDebug() << "Warning: Invalid original point index" << nOriginalIdx << "found in setUniquePointIndices.";
}
}
pUnstructuredGrid->SetPoints(pPoints);
// 3. 再次遍历单元数据,这次是根据新的 VTK 点索引来插入单元
for(size_t i = 0; i < P1.PEBI_cell.pindex.size(); ++i) {
// 再次检查 isplot 标志,确保只处理有效单元
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 nPointsInCell = vecIndices.size();
if(nPointsInCell > 1 && vecIndices[0] == vecIndices[nPointsInCell - 1]) {
nPointsInCell--;
}
if(nPointsInCell < 2) continue; // 过滤掉无效单元
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;
}
// 使用映射表将原始索引转换为新的 VTK 索引
vtkIdType* pts = new vtkIdType[nPointsInCell];
for(int j = 0; j < nPointsInCell; ++j) {
if(mapOriginalToVtkPointId.contains(vecIndices[j])) { // 确保点在映射表中
pts[j] = mapOriginalToVtkPointId[vecIndices[j]];
} else {
qDebug() << "Error: Point" << vecIndices[j] << "for cell" << i << "not found in map. This should not happen!";
// 可以选择跳过此单元或进行其他错误处理
delete[] pts;
return nullptr;
}
}
pUnstructuredGrid->InsertNextCell(vtkCellType, nPointsInCell, pts);
delete[] pts;
}
return pUnstructuredGrid;
}
//bool nmCalculationPebiGrid::generateOutputPara() {
// HMODULE dll = LoadLibrary(L"HX_NWTM.dll");
//
// if(!dll) {
// DWORD error = GetLastError();
// printf("Failed to load DLL. Error code: %lu\n", error);
// return false;
// }
//
// // 定义函数指针类型
// typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&, HX_NWTM_GRID_OUTPUT2&, const HX_NWTM_GRID_INPUT&, std::string);
// //typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&, HX_NWTM_GRID_OUTPUT2&, const HX_NWTM_GRID_INPUT&, std::string);
//
// // 获取函数地址
// HX_NWTM_GRID_Func HX_NWTM_GRID = (HX_NWTM_GRID_Func)GetProcAddress(dll, "HX_NWTM_GRID");
//
// if(!HX_NWTM_GRID) {
// DWORD error = GetLastError();
// printf("Failed to resolve function. Error code: %lu\n", error);
// FreeLibrary(dll);
// return false;
// }
//
// try {
// // 执行网格生成逻辑
// this->meshGenPebiBoundary(p0);
// this->meshGenPebiWells(p0);
// this->meshGenPebiFault(p0);
// this->meshGenPebiCrack(p0);
//
// nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getCurrentInstance();
// QString licensePath = pDataManager->getLicensePath();
// std::string licensePathStd = licensePath.toStdString();
//
// // 调用DLL函数
// HX_NWTM_GRID(p1, p2, p0, licensePathStd);
//
// } catch(const std::exception& e) {
// zxLogInstance::getInstance()->writeLogF(QString("C++ Exception: %1").arg(e.what()));
// logCurrentState();
// // 记录输入参数信息
// logInputParameters(p0);
// // 确保只释放一次
// FreeLibrary(dll);
// return false;
// } catch(...) {
// // 结构化异常,需要修改项目属性,项目属性 → C/C++ → Code Generation → Enable C++ Exceptions → 选择 Yes with SEH Exceptions (/EHa)
// zxLogInstance::getInstance()->writeLogF("SEH Exception Occurred");
// logCurrentState();
// // 记录输入参数信息
// logInputParameters(p0);
// // 确保只释放一次
// FreeLibrary(dll);
// return false;
// }
//
// // 确保只释放一次
// FreeLibrary(dll);
// return true;
//}
bool nmCalculationPebiGrid::generateOutputPara()
{
HMODULE dll = LoadLibrary(L"HX_NWTM.dll");
// 定义函数指针类型
typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&, HX_NWTM_GRID_OUTPUT2&, const HX_NWTM_GRID_INPUT&, std::string);
// ===== 调用网格生成器 =====
typedef void (*HX_NWTM_GRID_Func)(HX_NWTM_GRID_OUTPUT1&, HX_NWTM_GRID_OUTPUT2&, const HX_NWTM_GRID_INPUT&, std::string);
HX_NWTM_GRID_Func HX_NWTM_GRID = (HX_NWTM_GRID_Func)GetProcAddress(dll, "HX_NWTM_GRID");
if(!HX_NWTM_GRID) {
DWORD error = GetLastError();
printf("Failed to resolve function. Error code: %lu\n", error);
FreeLibrary(dll);
return false;
}
try {
// PEBI生成器是单例输入输出结构会保留上一次状态重新划分前先清空避免拖动井后复用旧网格数据
p0 = HX_NWTM_GRID_INPUT();
p1 = HX_NWTM_GRID_OUTPUT1();
p2 = HX_NWTM_GRID_OUTPUT2();
// PEBI网格划分输入参数GridControl来自界面
p0.GridControl = m_dGridControl;
// 执行网格生成逻辑
this->meshGenPebiBoundary(p0);
this->meshGenPebiWells(p0);
this->meshGenPebiFault(p0);
this->meshGenPebiCrack(p0);
// ===== 导出 scene.bin =====
{
nmDataBinaryTools::NM_PEBI_SCENE scene;
nmDataAnalyzeManager* dm = nmDataAnalyzeManager::getCurrentInstance();
// 1. 网格基础数据
scene.version = 1;
scene.D = p0.D;
scene.GridControl = p0.GridControl;
scene.Boundary = p0.Boundary;
scene.VerticalWell = p0.VerticalWell;
scene.HorizontalWell = p0.HorizontalWell;
scene.FractureVerticalWell = p0.FractureVerticalWell;
scene.MultistageFracturedHorizontalWell = p0.MultistageFracturedHorizontalWell;
scene.InclinedWell = p0.InclinedWell;
scene.Fault = p0.Fault;
// 2. 井顺序信息
QVector<QPair<NM_WELL_MODEL, QString>> order = dm->getCalculationWells();
scene.wellType.clear();
scene.wellName.clear();
scene.wellType.reserve(order.size());
scene.wellName.reserve(order.size());
for(int i = 0; i < order.size(); ++i) {
scene.wellType.push_back((int)order[i].first);
scene.wellName.push_back(order[i].second);
}
// ===== 3. 求解器参数 =====
// 3.1 求解器类型
nmDataReservoir* pReservoirData = dm->getReservoirData();
if(pReservoirData) {
if(pReservoirData->getPhaseType() == PHASE_Oil) {
scene.solverType = 2; // 油单相变化PVT
} else if(pReservoirData->getPhaseType() == PHASE_Gas) {
scene.solverType = 5; // 气单相变化PVT
} else {
scene.solverType = 1; // 默认油单相
}
}
// 3.2 Rate流量数据
scene.Rate.t.resize(order.size());
scene.Rate.qo.resize(order.size());
scene.Rate.qg.resize(order.size());
scene.Rate.qw.resize(order.size());
for(int wellIdx = 0; wellIdx < order.size(); ++wellIdx) {
NM_WELL_MODEL eWellType = order[wellIdx].first;
QString sWellName = order[wellIdx].second;
// 跳过裂缝
if(eWellType == NM_WELL_MODEL::Unknow_Well) {
scene.Rate.t[wellIdx].clear();
scene.Rate.qo[wellIdx].clear();
scene.Rate.qg[wellIdx].clear();
scene.Rate.qw[wellIdx].clear();
continue;
}
// 从井数据中提取流量曲线
nmDataWellBase* pWellData = dm->findWellByName(sWellName);
if(pWellData) {
QVector<QPointF> vecTimeQ = pWellData->getFlowPoints();
// 移除第一个点0,0
if(!vecTimeQ.isEmpty() && vecTimeQ[0].x() == 0 && vecTimeQ[0].y() == 0) {
vecTimeQ.remove(0);
}
// 提取时间和流量
std::vector<double> timeData;
std::vector<double> rateData;
for(size_t i = 0; i < vecTimeQ.size(); ++i) {
const QPointF& pt = vecTimeQ[i];
timeData.push_back(pt.x());
rateData.push_back(pt.y());
}
scene.Rate.t[wellIdx] = timeData;
// TODO: 根据井类型分配到qo/qg/qw
// 目前简化处理都分配到qo
scene.Rate.qo[wellIdx] = rateData;
scene.Rate.qg[wellIdx] = rateData; // 气井用这个
scene.Rate.qw[wellIdx] = rateData; // 水井用这个
}
}
// 3.3 CS井筒参数
scene.CS.C.resize(order.size());
scene.CS.S.resize(order.size());
for(int wellIdx = 0; wellIdx < order.size(); ++wellIdx) {
NM_WELL_MODEL eWellType = order[wellIdx].first;
QString sWellName = order[wellIdx].second;
if(eWellType == NM_WELL_MODEL::Unknow_Well) {
scene.CS.C[wellIdx] = 0.0;
scene.CS.S[wellIdx] = 0.0;
continue;
}
nmDataWellBase* pWellData = dm->findWellByName(sWellName);
if(pWellData) {
scene.CS.C[wellIdx] = pWellData->getWellboreStorage().getValue().toDouble();
// 获取第一段射孔的表皮系数
if(pWellData->getPerforationCount() > 0) {
scene.CS.S[wellIdx] = pWellData->getPerforation(0)->getSkin().getValue().toDouble();
} else {
scene.CS.S[wellIdx] = 0.0;
}
}
}
// 3.4 井流量段索引
scene.wellFlowSectionIndex.resize(order.size());
for(int wellIdx = 0; wellIdx < order.size(); ++wellIdx) {
NM_WELL_MODEL eWellType = order[wellIdx].first;
QString sWellName = order[wellIdx].second;
if(eWellType == NM_WELL_MODEL::Unknow_Well) {
scene.wellFlowSectionIndex[wellIdx] = 1; // 默认值
continue;
}
nmDataWellBase* pWellData = dm->findWellByName(sWellName);
if(pWellData) {
scene.wellFlowSectionIndex[wellIdx] = pWellData->getIndexF();
} else {
scene.wellFlowSectionIndex[wellIdx] = 1; // 默认值
}
}
// 3.5 PVT数据
nmDataPvtParaForPebi* pvt = dm->getPebiPvtPara();
if(pvt) {
scene.PVT.p = pvt->getPressure().toStdVector();
scene.PVT.Rso = pvt->getRso().toStdVector();
scene.PVT.pb = pvt->getPb().getValue().toDouble();
scene.PVT.Bo = pvt->getBo().toStdVector();
scene.PVT.Co = pvt->getCo().toStdVector();
scene.PVT.miuo = pvt->getMiuo().toStdVector();
scene.PVT.rouo = pvt->getRouo().toStdVector();
scene.PVT.Rv = pvt->getRv().toStdVector();
scene.PVT.Bg = pvt->getBg().toStdVector();
scene.PVT.Cg = pvt->getCg().toStdVector();
scene.PVT.miug = pvt->getMiug().toStdVector();
scene.PVT.roug = pvt->getRoug().toStdVector();
scene.PVT.Z = pvt->getZ().toStdVector();
scene.PVT.Rsw = pvt->getRsw().toStdVector();
scene.PVT.Bw = pvt->getBw().toStdVector();
scene.PVT.Cw = pvt->getCw().toStdVector();
scene.PVT.miuw = pvt->getMiuw().toStdVector();
scene.PVT.rouw = pvt->getRouw().toStdVector();
scene.PVT.V = pvt->getV().toStdVector();
scene.PVT.k_kinitial = pvt->getKKinitial().toStdVector();
scene.PVT.Cf_Cfinitial = pvt->getCfCfinitial().toStdVector();
scene.PVT.So = pvt->getSo().toStdVector();
scene.PVT.Kro = pvt->getKro().toStdVector();
scene.PVT.Sg = pvt->getSg().toStdVector();
scene.PVT.Krg = pvt->getKrg().toStdVector();
scene.PVT.Sw = pvt->getSw().toStdVector();
scene.PVT.Krw = pvt->getKrw().toStdVector();
}
// 3.6 Base储层参数
if(pReservoirData) {
scene.Base.Pi = pReservoirData->getInitialPressure().getValue().toDouble();
scene.Base.Cti = pReservoirData->getCt().getValue().toDouble();
scene.Base.Cf = pReservoirData->getCf().getValue().toDouble();
scene.Base.Soi = pReservoirData->getSoi().getValue().toDouble();
scene.Base.Sgi = pReservoirData->getSgi().getValue().toDouble();
scene.Base.Swi = pReservoirData->getSwi().getValue().toDouble();
// 保存参考值用于Python采样
scene.Base.k_ref = pReservoirData->getPermeability().getValue().toDouble();
scene.Base.phi_ref = pReservoirData->getPorosity().getValue().toDouble();
scene.Base.h_ref = pReservoirData->getThickness().getValue().toDouble();
}
// 3.7 时间步长设置
nmDataTimeStepSetting* pTimeStepSetting = dm->getTimeStep();
if(pTimeStepSetting) {
scene.Base.d = pTimeStepSetting->getTimeGrowthExponent().getValue().toDouble();
scene.Base.dt_Min = pTimeStepSetting->getMinDeltaTAttribute().getValue().toDouble();
scene.Base.dt_Max = pTimeStepSetting->getMaxDeltaTAttribute().getValue().toDouble();
}
// 4. 保存到文件
QDir appDir(QCoreApplication::applicationDirPath());
//QDir dataDir(appDir.filePath("../../3rd/Data"));
QDir dataDir(appDir.filePath("../../ML/nmWTAI-ML/data/temp"));
if(!dataDir.exists()) {
dataDir.mkpath("."); // 确保 Data 目录存在
}
QString outPath = dataDir.filePath("scene.bin");
bool ok = nmDataBinaryTools::savePebiSceneBin(outPath, scene);
if(!ok) {
zxLogInstance::getInstance()->writeLogF("savePebiSceneBin failed: " + nmDataBinaryTools::getLastError());
FreeLibrary(dll);
return false;
} else {
zxLogInstance::getInstance()->writeLogF("scene.bin exported: " + outPath);
}
}
// ===== 导出所有井loglog + rate + pressure 到 temp 目录 =====
{
nmDataAnalyzeManager* dm = nmDataAnalyzeManager::getCurrentInstance();
if(dm) {
QDir appDir(QCoreApplication::applicationDirPath());
QDir tempDir(appDir.filePath("../../ML/nmWTAI-ML/data/temp"));
if(!tempDir.exists()) {
tempDir.mkpath(".");
}
QString outDir = tempDir.absolutePath();
int okCount = 0;
int failCount = 0;
auto exportOne = [&](nmDataWellBase * w) {
if(!w) return;
bool ok1 = w->exportHistoryLogLogToCsv(outDir);
bool ok2 = w->exportHistoryRateToCsv(outDir);
bool ok3 = w->exportHistoryPressureToCsv(outDir);
if(ok1 && ok2 && ok3) okCount++;
else failCount++;
};
// 1) 直井
QVector<nmDataVerticalWell*> verticalWells = dm->getVerticalWellData();
for(int i = 0; i < verticalWells.size(); ++i) {
exportOne(verticalWells[i]);
}
// 2) 垂直裂缝井
QVector<nmDataVerticalFracturedWell*> vfWells = dm->getVerticalFracturedWellData();
for(int i = 0; i < vfWells.size(); ++i) {
exportOne(vfWells[i]);
}
// 3) 多段压裂水平井
QVector<nmDataHorizontalFracturedWell*> hfWells = dm->getHorizontalFracturedWellData();
for(int i = 0; i < hfWells.size(); ++i) {
exportOne(hfWells[i]);
}
zxLogInstance::getInstance()->writeLogF(
QString("History exported to temp. ok=%1, fail=%2, dir=%3")
.arg(okCount).arg(failCount).arg(outDir)
);
}
}
nmDataAnalyzeManager* pDataManager = nmDataAnalyzeManager::getCurrentInstance();
QString licensePath = pDataManager->getLicensePath();
std::string licensePathStd = licensePath.toStdString();
HX_NWTM_GRID(p1, p2, p0, licensePathStd);
} catch(const std::exception& e) {
zxLogInstance::getInstance()->writeLogF(QString("C++ Exception: %1").arg(e.what()));
logCurrentState();
logInputParameters(p0);
FreeLibrary(dll);
return false;
} catch(...) {
zxLogInstance::getInstance()->writeLogF("SEH Exception Occurred");
logCurrentState();
logInputParameters(p0);
FreeLibrary(dll);
return false;
}
FreeLibrary(dll);
return true;
}