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.
AppFlow/FITK_Component/FITKOFMeshIO/FITKOpenFOAMMeshReader.cpp

717 lines
22 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 "FITKOpenFOAMMeshReader.h"
#include <QFileInfo>
#include <QDir>
#include <QFile>
#include "FITK_Interface/FITKInterfaceMesh/FITKUnstructuredMeshVTK.h"
#include "FITK_Interface/FITKInterfaceModel/FITKElementHex.h"
#include "FITK_Interface/FITKInterfaceModel/FITKElementPolygon.h"
#include "FITK_Interface/FITKInterfaceMesh/FITKUnstructuredFluidMeshVTK.h"
#include "FITK_Kernel/FITKAppFramework/FITKMessage.h"
#include "FITK_Interface/FITKInterfaceModel/FITKElementQuad.h"
namespace IO {
FITKOpenFOAMMeshReader::FITKOpenFOAMMeshReader() = default;
FITKOpenFOAMMeshReader::~FITKOpenFOAMMeshReader() = default;
void FITKOpenFOAMMeshReader::run()
{
if (_fluidMesh == nullptr || _boundaryManager == nullptr) return;
read();
}
void FITKOpenFOAMMeshReader::consoleMessage(int level, const QString & str)
{
//判断消息等级
switch (level)
{
case 1:AppFrame::FITKMessageNormal(str); break;
case 2: AppFrame::FITKMessageWarning(str); break;
case 3:AppFrame::FITKMessageError(str); break;
default: AppFrame::FITKMessageError(str); break;
}
}
void FITKOpenFOAMMeshReader::setDataObject(Core::FITKAbstractDataObject * data)
{
auto mesh = dynamic_cast<Interface::FITKUnstructuredFluidMeshVTK*>(data);
if (mesh == nullptr) return;
_fluidMesh = mesh->getFieldMesh();
_boundaryManager = mesh->getBoundaryMeshManager();
}
bool FITKOpenFOAMMeshReader::read()
{
if (_fluidMesh == nullptr) return false;
if (_fileName.isEmpty()) return false;
QFileInfo fileInfo(_fileName + QDir::separator());
if (!fileInfo.isDir()) return false;
auto dir = fileInfo.dir();
auto dirname = dir.absolutePath();
if (!dir.exists()) return false;
if (!dir.exists("points") || !dir.exists("faces") || !dir.exists("owner") || !dir.exists("neighbour") || !dir.exists("boundary")) return false;
printf("Reading points...\n");
if (!readPoints(dir.filePath("points"))) return false;
printf("Reading faces...\n");
if (!readFaces(dir.filePath("faces"))) return false;
printf("Reading owner...\n");
if (!readOwner(dir.filePath("owner"))) return false;
printf("Reading neighbour...\n");
if (!readNeighbour(dir.filePath("neighbour"))) return false;
printf("Reading boundary...\n");
if (!readBoundary(dir.filePath("boundary"))) return false;
//printf("Setup 2D cells...\n");
//if (!setup2DCells()) return false;
printf("Setup boundarys...\n");
if (!setupBoundarys()) return false;
printf("Finish reading. Node: %d, Element: %d\n", _fluidMesh->getNodeCount(), _fluidMesh->getElementCount());
return true;
}
bool FITKOpenFOAMMeshReader::processLine(QString& line, bool inComment)
{
auto beginPos = line.indexOf("/*");
auto endPos = line.indexOf("*/");
auto singlePos = line.indexOf("//");
// 当前行为注释内容
if (inComment) {
// 注释尚未结束
if (endPos < 0) {
line.clear();
return true;
}
// 注释结束
else {
line = line.mid(endPos + 2);
return false;
}
}
else {
// 当前行包含注释且在行内结束
if (beginPos >= 0 && endPos >= 0) {
line = line.mid(0, beginPos) + line.mid(endPos);
return false;
}
// 当前行包含注释尚未在行内结束
else if (beginPos >= 0) {
line = line.mid(0, beginPos);
return true;
}
// 当前行包含当行注释
else if (singlePos >= 0) {
line = line.mid(0, singlePos);
return false;
}
else {
return false;
}
}
}
bool FITKOpenFOAMMeshReader::readFoamFileHeader(QFile& file, FoamFileHeader& header) {
QString line;
bool isInt{ false };
do
{
line = file.readLine().simplified();
// 读到整数表示异常
line.toInt(&isInt);
if (isInt) return false;
auto strs = line.split(' ', Qt::SkipEmptyParts);
if (strs.size() != 2) continue;
if (strs.at(0) == "format") {
header.format = strs.at(1).chopped(1);
}
else if (strs.at(0) == "class") {
header.clazz = strs.at(1).chopped(1);
}
else if (strs.at(0) == "location") {
header.location = strs.at(1).chopped(1);
}
else if (strs.at(0) == "object") {
header.object = strs.at(1).chopped(1);
}
} while (!line.startsWith("}"));
return true;
}
bool FITKOpenFOAMMeshReader::readPointsData(QFile& file) {
if (_fluidMesh == nullptr) return false;
QString line;
bool ok1, ok2, ok3;
double x, y, z;
int index = -1;
do
{
line = file.readLine().simplified();
if (line.isEmpty()) continue;
if (line == "(") continue;
if (line == ")") break;
// 移除括号
line = line.chopped(1).remove(0, 1);
auto strs = line.split(' ', Qt::SkipEmptyParts);
if (strs.size() != 3) continue;
x = strs.at(0).toDouble(&ok1);
y = strs.at(1).toDouble(&ok2);
z = strs.at(2).toDouble(&ok3);
if (ok1 && ok2 && ok3) {
_fluidMesh->addNode(++index, x, y, z);
}
else {
return false;
}
} while (!file.atEnd());
return true;
}
bool FITKOpenFOAMMeshReader::readPoints(const QString &points)
{
if (_fluidMesh == nullptr) return false;
QFile pointsFile(points);
if (!pointsFile.exists()) return false;
if (!pointsFile.open(QIODevice::ReadOnly | QIODevice::Text)) return false;
QString line;
bool inComment{ false };
bool isInt{ false };
while (!pointsFile.atEnd())
{
line = pointsFile.readLine();
inComment = processLine(line, inComment);
line = line.simplified();
if (line.isEmpty()) continue;
// 读取文件头信息
if (line.startsWith("FoamFile")) {
FoamFileHeader header;
if (!readFoamFileHeader(pointsFile, header)) return false;
// 只支持文本格式
if (header.format != "ascii") return false;
}
// 读取点数据
_pointsNum = line.toInt(&isInt);
if (isInt) {
if (!readPointsData(pointsFile)) return false;
}
}
pointsFile.close();
return true;
}
bool FITKOpenFOAMMeshReader::readFacesData(QFile& file) {
QString line{}, multiLine{};
bool ok;
int i = -1;
bool atDataArea{ false };
bool inBracket{ false };
do
{
line = file.readLine().simplified();
if (line.isEmpty()) continue;
// 第一个括号
if (line == "(" && !atDataArea) {
atDataArea = true;
continue;
}
else if (line == "(") {
inBracket = true;
}
else if (line == ")" && !inBracket) {
break;
}
// 单行数据
if (!inBracket && line.endsWith(")")) {
multiLine = line.chopped(1);
}
// 跨行数据
else if (!line.endsWith(")")) {
multiLine.append(" " + line);
continue;
}
else {
multiLine.append(" " + line.chopped(1));
inBracket = false;
}
auto strs = multiLine.split('(', Qt::SkipEmptyParts);
if (strs.size() != 2) continue;
QList<QString> indexStrs = strs.at(1).split(' ', Qt::SkipEmptyParts);
if (indexStrs.size() != strs.at(0).toInt()) return false;
QList<int> indexs{};
for (auto str : indexStrs)
{
auto val = str.toInt(&ok);
if (ok) indexs.append(val);
else return false;
}
multiLine.clear();
_faces[++i] = indexs;
} while (!file.atEnd());
return true;
}
bool FITKOpenFOAMMeshReader::readFaces(const QString &faces)
{
QFile facesFile(faces);
if (!facesFile.exists()) return false;
if (!facesFile.open(QIODevice::ReadOnly | QIODevice::Text)) return false;
QString line;
bool inComment{ false };
bool isInt{ false };
while (!facesFile.atEnd())
{
line = facesFile.readLine();
inComment = processLine(line, inComment);
line = line.simplified();
if (line.isEmpty()) continue;
// 读取文件头信息
if (line.startsWith("FoamFile")) {
FoamFileHeader header;
if (!readFoamFileHeader(facesFile, header)) return false;
// 只支持文本格式
if (header.format != "ascii") return false;
}
// 读取面数据
_facesNum = line.toInt(&isInt);
if (isInt) {
_faces.resize(_facesNum);
if (!readFacesData(facesFile)) return false;
}
}
facesFile.close();
return true;
}
bool FITKOpenFOAMMeshReader::readOwnerData(QFile& file) {
QString line;
bool ok;
int faceIndex = -1;
do
{
line = file.readLine().simplified();
if (line.isEmpty()) continue;
if (line == "(") continue;
if (line == ")") break;
auto ownerIndex = line.toInt(&ok);
if (ok) {
_owner[ownerIndex].append(++faceIndex);
}
else {
return false;
}
} while (!file.atEnd());
return true;
}
bool FITKOpenFOAMMeshReader::readOwner(const QString &owner)
{
QFile ownerFile(owner);
if (!ownerFile.exists()) return false;
if (!ownerFile.open(QIODevice::ReadOnly | QIODevice::Text)) return false;
QString line;
bool inComment{ false };
bool isInt{ false };
int ownerNum{};
while (!ownerFile.atEnd())
{
line = ownerFile.readLine();
inComment = processLine(line, inComment);
line = line.simplified();
if (line.isEmpty()) continue;
// 读取文件头信息
if (line.startsWith("FoamFile")) {
FoamFileHeader header;
if (!readFoamFileHeader(ownerFile, header)) return false;
// 只支持文本格式
if (header.format != "ascii") return false;
}
// 读取Owner数据
ownerNum = line.toInt(&isInt);
if (isInt) {
if (!readOwnerData(ownerFile)) return false;
}
}
ownerFile.close();
return true;
}
bool FITKOpenFOAMMeshReader::readNeighbourData(QFile& file)
{
QString line;
bool ok;
int faceIndex = -1;
do
{
line = file.readLine().simplified();
if (line.isEmpty()) continue;
if (line == "(") continue;
if (line == ")") break;
auto neighbourIndex = line.toInt(&ok);
if (ok) {
_neighbour[++faceIndex] = neighbourIndex;
_tempNeighbour[neighbourIndex].append(faceIndex);
}
else {
return false;
}
} while (!file.atEnd());
return true;
}
bool FITKOpenFOAMMeshReader::readNeighbour(const QString &neighbour)
{
QFile neighbourFile(neighbour);
if (!neighbourFile.exists()) return false;
if (!neighbourFile.open(QIODevice::ReadOnly | QIODevice::Text)) return false;
QString line;
bool inComment{ false };
bool isInt{ false };
int neighbourNum{};
while (!neighbourFile.atEnd())
{
line = neighbourFile.readLine();
inComment = processLine(line, inComment);
line = line.simplified();
if (line.isEmpty()) continue;
// 读取文件头信息
if (line.startsWith("FoamFile")) {
FoamFileHeader header;
if (!readFoamFileHeader(neighbourFile, header)) return false;
// 只支持文本格式
if (header.format != "ascii") return false;
}
// 读取Owner数据
neighbourNum = line.toInt(&isInt);
if (isInt) {
if (!readNeighbourData(neighbourFile)) return false;
}
}
neighbourFile.close();
return true;
}
bool FITKOpenFOAMMeshReader::readSingleBoundaryData(QFile & file, Boundary* boundary)
{
QString line;
do
{
line = file.readLine().simplified();
if (line.isEmpty()) continue;
if (line == "{") continue;
if (line == "}") break;
if (line.startsWith("type")) {
boundary->type = line.mid(4).simplified().chopped(1);
}
else if (line.startsWith("inGroups")) {
boundary->group = line.mid(8).simplified().chopped(1);
}
else if (line.startsWith("nFaces")) {
boundary->nFaces = line.mid(6).simplified().chopped(1).toInt();
}
else if (line.startsWith("startFace")) {
boundary->startFace = line.mid(9).simplified().chopped(1).toInt();
}
} while (!file.atEnd());
return true;
}
bool FITKOpenFOAMMeshReader::readBoundaryData(QFile & file)
{
QString line;
bool ok;
int faceIndex = -1;
Boundary boundary{};
do
{
line = file.readLine().simplified();
if (line.isEmpty()) continue;
if (line == "(") continue;
if (line == ")") break;
auto boundary = new Boundary;
boundary->name = line;
readSingleBoundaryData(file, boundary);
if (!boundary->isValid()) {
delete boundary;
boundary = nullptr;
break;
}
else {
_Boundarys.append(boundary);
}
} while (!file.atEnd());
return true;
}
bool FITKOpenFOAMMeshReader::readBoundary(const QString & boundary)
{
QFile boundaryFile(boundary);
if (!boundaryFile.exists()) return false;
if (!boundaryFile.open(QIODevice::ReadOnly | QIODevice::Text)) return false;
QString line;
bool inComment{ false };
bool isInt{ false };
int boundaryNum{};
while (!boundaryFile.atEnd())
{
line = boundaryFile.readLine();
inComment = processLine(line, inComment);
line = line.simplified();
if (line.isEmpty()) continue;
// 读取文件头信息
if (line.startsWith("FoamFile")) {
FoamFileHeader header;
if (!readFoamFileHeader(boundaryFile, header)) return false;
// 只支持文本格式
if (header.format != "ascii") return false;
}
// 读取boundary数据
boundaryNum = line.toInt(&isInt);
if (isInt) {
if (!readBoundaryData(boundaryFile)) return false;
}
}
boundaryFile.close();
return true;
}
bool FITKOpenFOAMMeshReader::getHex8IdsByFace(QList<int> face, int inIdPrev, int inIdNext, int& outIdPrev, int& outIdNext)
{
int idNextPos = face.indexOf(inIdNext);
int idPrevPos = face.indexOf(inIdPrev);
// id1必须在id2后面且必须相邻
if (idPrevPos == 0 && idNextPos == 1) {
outIdNext = face.at(3);
outIdPrev = face.at(2);
}
else if (idPrevPos == 1 && idNextPos == 2) {
outIdNext = face.at(0);
outIdPrev = face.at(3);
}
else if (idPrevPos == 2 && idNextPos == 3) {
outIdNext = face.at(1);
outIdPrev = face.at(0);
}
else if (idPrevPos == 3 && idNextPos == 0) {
outIdNext = face.at(2);
outIdPrev = face.at(1);
}
return true;
}
Interface::FITKElementHex8* FITKOpenFOAMMeshReader::setupHex8Cell(int eleIndex, QVector<QList<int>> points) {
auto faceNum = points.size();
// 假定第一个面为底面
QList<int> bottomFace = points.at(0);
if (bottomFace.size() != 4) return nullptr;
int id0 = bottomFace.at(0);
int id1 = bottomFace.at(1);
int id2 = bottomFace.at(2);
int id3 = bottomFace.at(3);
int id4{ -1 }, id5{ -1 }, id6{ -1 }, id7{ -1 };
QList<int> leftFace{};
QList<int> rightFace{};
QList<int> frontFace{};
QList<int> backFace{};
// 只有三个面不足以确定所有顶点,需要添加邻面
if (faceNum < 5) {
auto faces = _tempNeighbour[eleIndex];
for (auto faceIndex : faces) {
auto face = _faces[faceIndex];
if (face.size() != 4) return nullptr;
points.append({ face.at(3), face.at(2), face.at(1), face.at(0) });
}
}
faceNum = points.size();
for (int i = 1; i < faceNum; ++i) {
auto face = points.at(i);
if (face.contains(id0) && face.contains(id1)) {
leftFace = std::move(face);
}
else if (face.contains(id1) && face.contains(id2)) {
backFace = std::move(face);
}
else if (face.contains(id2) && face.contains(id3)) {
rightFace = std::move(face);
}
else if (face.contains(id3) && face.contains(id0)) {
frontFace = std::move(face);
}
}
// 分析面确定顶面的节点顺序
if (!leftFace.empty())
{
getHex8IdsByFace(leftFace, id1, id0, id4, id7);
}
if (!rightFace.empty())
{
getHex8IdsByFace(rightFace, id3, id2, id6, id5);
}
if ((id4 == -1 || id5 == -1) && !frontFace.empty())
{
getHex8IdsByFace(frontFace, id0, id3, id5, id4);
}
if ((id6 == -1 || id7 == -1) && !backFace.empty())
{
getHex8IdsByFace(backFace, id2, id1, id7, id6);
}
// 只有三个face的单元缺一个顶点需要从neighbour中读取
if (id4 == -1 || id5 == -1 || id6 == -1 || id7 == -1)
{
return nullptr;
}
// OpenFOAM的face节点是按右手螺旋定则排序的
auto hex8Cell = new Interface::FITKElementHex8;
hex8Cell->setEleID(eleIndex);
hex8Cell->setNodeID({ id0, id3, id2, id1, id4, id5, id6, id7 });
return hex8Cell;
}
Interface::FITKElementPolygon * FITKOpenFOAMMeshReader::setupPolygonCell(QList<int> face)
{
// OpenFOAM的face节点是按右手螺旋定则排序的
auto polygonCell = new Interface::FITKElementPolygon;
polygonCell->setNodeID(face);
return polygonCell;
}
Interface::FITKAbstractElement* FITKOpenFOAMMeshReader::setupCell(int eleIndex, QList<int> faces) {
Interface::FITKAbstractElement* cell{};
// 单元包含面的个数
auto facesNum = faces.size();
// 包含每个面的点索引的面数组
QVector<QList<int>> points{};
// 包含每个面的点数的面数组
QVector<int> pointNumOfFace{};
// 遍历面
for (auto fIndex : faces) {
auto pointsInFace = _faces[fIndex];
points.append(pointsInFace);
pointNumOfFace.append(pointsInFace.size());
}
// 面数组里面单个面中点数的最大值和最小值
int minPointNum = pointNumOfFace.at(0);
int maxPointNum = minPointNum;
for (int i = 1, n = 0; i < facesNum; ++i) {
n = pointNumOfFace.at(i);
if (minPointNum > n) minPointNum = n;
if (maxPointNum < n) maxPointNum = n;
}
// 六面体单元(最少有3个面最多有6个面)
if (minPointNum == 4 && maxPointNum == 4) {
cell = setupHex8Cell(eleIndex, points);
}
return cell;
}
bool FITKOpenFOAMMeshReader::setup3DCells() {
if (_fluidMesh == nullptr) return false;
auto eleNum = _owner.size();
for (int i = 0; i < eleNum; ++i)
{
auto cell = setupCell(i, _owner[i]);
if (cell == nullptr) {
return false;
}
_fluidMesh->appendElement(cell);
}
return true;
}
bool FITKOpenFOAMMeshReader::setup2DCells()
{
if (_fluidMesh == nullptr) return false;
auto eleNum = _faces.size();
for (int i = 0; i < eleNum; ++i)
{
auto cell = setupPolygonCell(_faces.at(i));
if (cell == nullptr) {
return false;
}
_fluidMesh->appendElement(cell);
}
return true;
}
bool FITKOpenFOAMMeshReader::setupBoundarys()
{
if (_boundaryManager == nullptr) return false;
for (auto boundary : _Boundarys) {
auto b = new Interface::FITKBoundaryMeshVTK(_fluidMesh);
//记录边界网格信息 libaojun@20240906
b->setUserData(Core::FITKUserData::FITKUserRole, boundary->startFace);
b->setUserData(Core::FITKUserData::FITKUserRole + 1, boundary->nFaces);
//********************
for (int i = 0; i < boundary->nFaces; ++i)
{
const QList<int>& face = _faces[boundary->startFace + i];
auto ele = new Interface::FITKElementPolygon;
ele->setNodeCount(face.size());
ele->setNodeID(face);
b->appendElement(ele);
}
b->setDataObjectName(boundary->name);
_boundaryManager->appendDataObj(b);
}
return true;
}
}