FastCAE使用gmsh进行网格划分,划分的时候直接启动一个新的gmsh进程,个人猜测这么设计是为了规避gmsh的GPL协议风险。
进行网格划分时,其大体运行如下图:
一、Python到gmshModule模块
GUI操作到Python这步不再分析,比较简单。执行的Python代码大概如下:
gmsher = Mesher.Gmsher()
gmsher.setDim(3)
gmsher.selectedAll()
gmsher.setElementType("Tet")
gmsher.setElementOrder(1)
gmsher.setMethod(1)
gmsher.setSizeFactor(1)
gmsher.setMinSize(0)
gmsher.setMaxSize(100)
gmsher.cleanGeo()
gmsher.startGenerationThread()
这些代码可以在控制台找到,如下图:
这样创建了一个gmsher对象,赋予网格划分的控制参数,最后调用startGenerationThread()方法,这个方法的源码在Mesher.py文件中。对于三维问题,会调用gmshModule模块的接口GenerateMesh3D()。gmshModule同样提供了针对二维、流体网格的划分接口,代码在GmshPy.h文件中,如下图所示。
二、启动gmsh线程,写出gmsh文件,生成网格
在GmshPy::GenerateMesh3D()函数中,只是准备网格划分的参数,最后会调用到GmshModule::generateSlot()函数中。
void GmshModule::generateSlot(GMshPara *para)
{
GmshThread *thread = iniGmshThread(para);
auto processBar = new ModuleBase::ProcessBar(_mainwindow, tr("Gmsh Working..."));
_threadManager->insertThread(processBar, thread); // 运行gmsh线程
}
// 初始化gmsh线程
GmshThread *GmshModule::iniGmshThread(GMshPara *para)
{
if (_threadManager->isRuning())
{
delete para;
ModuleBase::Message m(Common::Message::Error, QString("Gmsh is running !"));
emit printMessageToMessageWindow(m);
}
GmshThread *thread = new GmshThread(_mainwindow, _preWindow, this, para->_dim);
thread->setPara(para);
delete para;
return thread;
}
在GmshModule::generateSlot函数中,会初始化一个线程,并启动运行。
再打开GmshThread的run方法,大体流程就是合并几何对象,形成一个TopoDS_Compound对象,调用BRepTools::Write写出geometry.brep文件。写出gmsh控制文件,最后调用gmsh命令生成网格文件。
void GmshThread::run()
{
this->mergeGeometry(); // 写出几何文件
this->initGmshEnvoirment(); // 写出gmsh的控制文件
this->generate(); // 执行gmsh命令
}
void GmshThread::mergeGeometry()
{
QString exelPath = QCoreApplication::applicationDirPath();
const QString tempDir = exelPath + "/../temp/";
QDir dir(tempDir);
if (!dir.exists())
dir.mkpath(tempDir);
// 清理之前的临时文件
const QString meshfilename = exelPath + "/../temp/mesh.vtk";
if (QFile::exists(meshfilename))
QFile::remove(meshfilename);
const QString geofilename = exelPath + "/../temp/geometry.brep";
if (QFile::exists(geofilename))
QFile::remove(geofilename);
const QString gmshfilename = exelPath + "/../temp/gmsh.Geo";
if (QFile::exists(gmshfilename))
QFile::remove(gmshfilename);
const QString tempPath = tempDir + QString("geometry.brep");
if (_fluidMesh)
_fluidMeshProcess->mergeFluidField(_compounnd, _solidHash);
else if (_selectall)
mergeAllGeo(); // 将需要划分网格的组成一个compounnd对象
else if (_selectvisible)
mergeVisibleGeo();
else
mergeSelectGeo();
QByteArray arr = tempPath.toLatin1();
BRepTools::Write(*_compounnd, arr.data()); // 使用BRepTools写出geometry.brep文件
}
void GmshThread::initGmshEnvoirment()
{
QString exelPath = QCoreApplication::applicationDirPath();
const QString tempDir = exelPath + "/../temp/";
// const QString gmshDir = exelPath + "/gmsh/";
QFile::remove(tempDir + "gmsh.Geo");
_scriptWriter->setCompound(_compounnd);
setGmshScriptData();
if (_fluidMesh)
{
QList<int> curve = _fluidMeshProcess->getInerMember(1);
QList<int> surface = _fluidMeshProcess->getInerMember(2);
_scriptWriter->writeFluidMeshScript(tempDir, _solidHash, curve, surface);
}
else
_scriptWriter->writeGmshScript(tempDir); // 写出gmsh控制文件
}
void GmshThread::generate()
{
QString exelPath = QCoreApplication::applicationDirPath();
const QString tempDir = exelPath + "/../temp/";
// const QString gmshDir = exelPath + "/gmsh/";
QString gmshexe = exelPath + "/gmsh";
bool ok = false;
#ifdef Q_OS_WIN
ok = QFile::exists(gmshexe + ".exe");
#endif
#ifdef Q_OS_LINUX
ok = QFile::exists(gmshexe);
#endif
if (!ok)
{
QMessageBox::warning(_mainwindow, QString(tr("Warning")), QString(tr("Gmsh is not exist !")));
return;
}
// 调用gmsh生成网格
QString startProcess = QString("%1 %2 -format vtk -bin -o %3 -%4").arg(gmshexe).arg(tempDir + "gmsh.Geo").arg(tempDir + "mesh.vtk").arg(_dim);
if (gmshexe.contains(" "))
startProcess = QString("\"%1\"").arg(startProcess);
qDebug() << startProcess;
_process.start(startProcess);
}
最后调用的gmsh命令大体如下,生成mesh.vtk网格文件。
C:/workspace/FastCAE/build/Debug/gmsh C:/workspace/FastCAE/build/Debug/…/temp/gmsh.Geo -format vtk -bin -o C:/workspace/FastCAE/build/Debug/…/temp/mesh.vtk -3
三、读取网格文件
读取网格文件代码在GmshThread::readMesh函数中,注意执行这段代码时gmsh线程已经结束了。
void GmshThread::readMesh()
{
MeshData::MeshData *data = MeshData::MeshData::getInstance();
QString exelPath = QCoreApplication::applicationDirPath();
const QString fileName = exelPath + "/../temp/mesh.vtk";
QTextCodec *codec = QTextCodec::codecForName("GB18030");
QByteArray ba = codec->fromUnicode(fileName);
vtkSmartPointer<vtkDataSetReader> vtkReader = vtkSmartPointer<vtkDataSetReader>::New();
vtkReader->SetFileName(ba);
vtkReader->Update();
vtkDataSet *dataset = vtkReader->GetOutput();
if (dataset == nullptr)
return;
if (!_isSaveToKernal)
emit writeToSolveFileSig(dataset);
else
{
auto k = new MeshData::MeshKernal();
k->setName(QString("Mesh_%1").arg(k->getID()));
k->setMeshData(dataset);
data->appendMeshKernal(k);
if (!_fluidMesh)
setGmshSettingData(k);
emit _gmshModule->updateMeshTree();
emit _gmshModule->updateSetTree();
// emit _preWindow->updateMeshActorSig();
emit _gmshModule->updateActions();
emit updateMeshActor();
}
}
由于gmsh输出的是vtk文件,这里直接使用vtkDataSetReader读取的。