#include "FITKOpenFOAMMeshReader.h" #include #include #include #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(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 indexStrs = strs.at(1).split(' ', Qt::SkipEmptyParts); if (indexStrs.size() != strs.at(0).toInt()) return false; QList 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 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> points) { auto faceNum = points.size(); // 假定第一个面为底面 QList 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 leftFace{}; QList rightFace{}; QList frontFace{}; QList 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 face) { // OpenFOAM的face节点是按右手螺旋定则排序的 auto polygonCell = new Interface::FITKElementPolygon; polygonCell->setNodeID(face); return polygonCell; } Interface::FITKAbstractElement* FITKOpenFOAMMeshReader::setupCell(int eleIndex, QList faces) { Interface::FITKAbstractElement* cell{}; // 单元包含面的个数 auto facesNum = faces.size(); // 包含每个面的点索引的面数组 QVector> points{}; // 包含每个面的点数的面数组 QVector 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& 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; } }