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.
261 lines
8.8 KiB
C++
261 lines
8.8 KiB
C++
#include "nmPebiGridViewDataBuilder.h"
|
|
|
|
#include "nmPebiGridViewData.h"
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
#include "nmDataHorizontalFracturedWell.h"
|
|
#include "nmDataNumericalAnalysisCase.h"
|
|
#include "nmDataVerticalFracturedWell.h"
|
|
#include "nmDataWellBase.h"
|
|
|
|
#include <QCoreApplication>
|
|
#include <QSet>
|
|
#include <QThread>
|
|
|
|
#include <cmath>
|
|
|
|
#include <vtkCellData.h>
|
|
#include <vtkDataArray.h>
|
|
#include <vtkMath.h>
|
|
#include <vtkUnstructuredGrid.h>
|
|
|
|
namespace {
|
|
|
|
QString builderText(const char* pText)
|
|
{
|
|
return QCoreApplication::translate("nmPebiGridViewDataBuilder", pText);
|
|
}
|
|
|
|
bool isBuilderGuiThread()
|
|
{
|
|
QCoreApplication* pApplication = QCoreApplication::instance();
|
|
return pApplication == NULL ||
|
|
pApplication->thread() == QThread::currentThread();
|
|
}
|
|
|
|
bool isFinitePoint(const QPointF& oPoint)
|
|
{
|
|
return vtkMath::IsFinite(oPoint.x()) &&
|
|
vtkMath::IsFinite(oPoint.y());
|
|
}
|
|
|
|
bool appendWellData(nmDataWellBase* pWell,
|
|
nmPebiGridWellViewData* pWellData,
|
|
QString* pError)
|
|
{
|
|
if(pWell == NULL || pWellData == NULL) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("The well data is incomplete.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
pWellData->m_sWellCode = pWell->getWellCode().trimmed();
|
|
pWellData->m_sWellName = pWell->getWellName();
|
|
pWellData->m_eWellType = pWell->getWellType();
|
|
pWellData->m_oPosition = QPointF(
|
|
pWell->getX().getValue().toDouble(),
|
|
pWell->getY().getValue().toDouble());
|
|
|
|
if(pWellData->m_sWellCode.isEmpty()) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("A well has no stable code.");
|
|
}
|
|
return false;
|
|
}
|
|
if(!isFinitePoint(pWellData->m_oPosition)) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("A well contains an invalid position.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nmDataHorizontalFracturedWell* pHorizontalWell =
|
|
dynamic_cast<nmDataHorizontalFracturedWell*>(pWell);
|
|
nmDataVerticalFracturedWell* pVerticalFracturedWell =
|
|
dynamic_cast<nmDataVerticalFracturedWell*>(pWell);
|
|
|
|
if(pHorizontalWell != NULL) {
|
|
const double dLength = pHorizontalWell->getWellLength()
|
|
.getValue().toDouble();
|
|
const double dAngle = pHorizontalWell->getDrainAngle()
|
|
.getValue().toDouble();
|
|
if(!vtkMath::IsFinite(dLength) || dLength < 0.0 ||
|
|
!vtkMath::IsFinite(dAngle)) {
|
|
if(pError != NULL) {
|
|
*pError = builderText(
|
|
"A horizontal well contains invalid geometry.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const double dRadians = dAngle * 3.14159265358979323846 / 180.0;
|
|
const QPointF oEndPoint(
|
|
pWellData->m_oPosition.x() + dLength * std::cos(dRadians),
|
|
pWellData->m_oPosition.y() + dLength * std::sin(dRadians));
|
|
if(!isFinitePoint(oEndPoint)) {
|
|
if(pError != NULL) {
|
|
*pError = builderText(
|
|
"A horizontal well contains invalid geometry.");
|
|
}
|
|
return false;
|
|
}
|
|
pWellData->m_vecWellSegments.append(
|
|
QLineF(pWellData->m_oPosition, oEndPoint));
|
|
|
|
const QVector<QPair<QPointF, QPointF> > vecFractures =
|
|
pHorizontalWell->getFracs();
|
|
for(int nIndex = 0; nIndex < vecFractures.size(); ++nIndex) {
|
|
const QPointF& oStart = vecFractures[nIndex].first;
|
|
const QPointF& oEnd = vecFractures[nIndex].second;
|
|
if(!isFinitePoint(oStart) || !isFinitePoint(oEnd)) {
|
|
if(pError != NULL) {
|
|
*pError = builderText(
|
|
"A well contains invalid fracture geometry.");
|
|
}
|
|
return false;
|
|
}
|
|
pWellData->m_vecFractureSegments.append(QLineF(oStart, oEnd));
|
|
}
|
|
} else if(pVerticalFracturedWell != NULL) {
|
|
const QVector<QPointF> vecFractures =
|
|
pVerticalFracturedWell->getFracs();
|
|
if(vecFractures.size() != 2 ||
|
|
!isFinitePoint(vecFractures[0]) ||
|
|
!isFinitePoint(vecFractures[1])) {
|
|
if(pError != NULL) {
|
|
*pError = builderText(
|
|
"A well contains invalid fracture geometry.");
|
|
}
|
|
return false;
|
|
}
|
|
pWellData->m_vecFractureSegments.append(
|
|
QLineF(vecFractures[0], vecFractures[1]));
|
|
} else if(pWellData->m_eWellType != Vertical_Well) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("The well type is not supported by this view.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
bool nmPebiGridViewDataBuilder::build(
|
|
nmDataAnalyzeManager* pDataManager,
|
|
nmPebiGridViewData* pViewData,
|
|
QString* pError)
|
|
{
|
|
Q_ASSERT(isBuilderGuiThread());
|
|
if(pError != NULL) {
|
|
pError->clear();
|
|
}
|
|
if(!isBuilderGuiThread()) {
|
|
if(pError != NULL) {
|
|
*pError = builderText(
|
|
"Grid preview data must be built in the GUI thread.");
|
|
}
|
|
return false;
|
|
}
|
|
if(pDataManager == NULL || pViewData == NULL) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("The grid data source is unavailable.");
|
|
}
|
|
return false;
|
|
}
|
|
if(pDataManager->thread() != QThread::currentThread()) {
|
|
if(pError != NULL) {
|
|
*pError = builderText(
|
|
"Grid preview data must be built in the GUI thread.");
|
|
}
|
|
return false;
|
|
}
|
|
if(!pDataManager->isPebiGridValid()) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("The PEBI grid has not been committed.");
|
|
}
|
|
return false;
|
|
}
|
|
if(pDataManager->getGridType() != NM_Grid_PEBI) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("The committed grid is not a PEBI grid.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
vtkSmartPointer<vtkUnstructuredGrid> pGrid =
|
|
pDataManager->getUnstructuredGrid();
|
|
if(pGrid == NULL || pGrid->GetNumberOfPoints() <= 0 ||
|
|
pGrid->GetNumberOfCells() <= 0) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("The committed PEBI grid is empty.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const nmDataNumericalAnalysisCase* pAnalysisCase =
|
|
pDataManager->getNumericalAnalysisCase();
|
|
if(pAnalysisCase == NULL) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("The grid version is unavailable.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 先在局部对象中收集完整快照,任何井数据错误都不会覆盖调用方旧数据。
|
|
nmPebiGridViewData oCandidate;
|
|
oCandidate.m_pGrid = pGrid;
|
|
oCandidate.m_eGridType = NM_Grid_PEBI;
|
|
oCandidate.m_nGridInputRevision =
|
|
pAnalysisCase->getGridInputRevision();
|
|
oCandidate.m_nValidCellCount = pGrid->GetNumberOfCells();
|
|
|
|
// Material 只认可 CellData 中与 VTK 单元一一对应的数组。
|
|
vtkCellData* pCellData = pGrid->GetCellData();
|
|
vtkDataArray* pMaterialArray = pCellData != NULL
|
|
? pCellData->GetArray("Material") : NULL;
|
|
oCandidate.m_bHasMaterial = pMaterialArray != NULL &&
|
|
pMaterialArray->GetNumberOfTuples() == pGrid->GetNumberOfCells();
|
|
|
|
// 井显示必须与本次已提交网格使用的求解器槽位一致,不能遍历 Map 中
|
|
// 未参与建网的井。手工裂缝槽位只维持 DLL 下标,不对应真实井 Actor。
|
|
const QVector<nmSolverWellRef> vecSolverWellOrder =
|
|
pDataManager->getSolverWellOrder();
|
|
QSet<QString> setWellCodes;
|
|
oCandidate.m_vecWells.reserve(vecSolverWellOrder.size());
|
|
for(int nIndex = 0; nIndex < vecSolverWellOrder.size(); ++nIndex) {
|
|
const nmSolverWellRef& oWellRef = vecSolverWellOrder[nIndex];
|
|
if(oWellRef.m_eEntryKind != NM_SolverEntry_Well) {
|
|
continue;
|
|
}
|
|
|
|
const QString sWellCode = oWellRef.m_sWellCode.trimmed();
|
|
nmDataWellBase* pWell = pDataManager->findWellByCode(sWellCode);
|
|
if(sWellCode.isEmpty() || pWell == NULL) {
|
|
if(pError != NULL) {
|
|
*pError = builderText(
|
|
"The committed grid references a missing well.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
nmPebiGridWellViewData oWellData;
|
|
if(!appendWellData(pWell, &oWellData, pError)) {
|
|
return false;
|
|
}
|
|
if(setWellCodes.contains(oWellData.m_sWellCode)) {
|
|
if(pError != NULL) {
|
|
*pError = builderText("The grid contains duplicate well codes.");
|
|
}
|
|
return false;
|
|
}
|
|
setWellCodes.insert(oWellData.m_sWellCode);
|
|
oCandidate.m_vecWells.append(oWellData);
|
|
}
|
|
|
|
*pViewData = oCandidate;
|
|
return true;
|
|
}
|