Commit 8e19333c authored by Dave Moxey's avatar Dave Moxey
Browse files

Merge branch 'feature/geo-reader' into 'master'


See merge request !731
parents 7702eef3 0fd8d822
......@@ -3,7 +3,9 @@ Changelog
- Add feature to read basic 2D geo files as CAD (!731)
- Add periodic boundary condition meshing in 2D (!733)
- Adjust boundary layer thickness in corners in 2D (!739)
- Add non-O BL meshing in 2D (!757)
......@@ -152,6 +154,7 @@ v4.4.0
- 2D to 3D mesh extrusion module (!715)
- Add new two-dimensional mesher from NACA code or step file (!720)
- Add basic gmsh cad (.geo) reader to the meshing system (!731)
- Fix inverted boundary layer in 2D (!736)
- More sensible element sizing with boundary layers in 2D (!736)
- Change variable names in mcf file to make more sense (!736)
......@@ -867,10 +867,11 @@ In all cases the mesh generator needs two pieces of information and four
parameters. It firstly needs to know the CAD file with which to work. In the
example above this is listed as a 4 digit number, this is because the mesh
generator is equiped with a NACA wing generator. In all other cases this
parameter would be a STEP file. Secondly, what type of mesh to make, the options
are \inltt{EULER} and \inltt{BL} for 3D meshes and \inltt{2D} and \inltt{2DBL}
parameter would be the name of a CAD file (in either STEP or GEO format).
Secondly, what type of mesh to make, the options
are \inltt{EULER} and \inltt{BndLayer} for 3D meshes and \inltt{2D} and \inltt{2DBndLayer}
for 2D meshes. In the case of \inltt{EULER} the mesh will be made with only
tetrahedra. For \inltt{BL} the mesh generator will attempt to insert a single
tetrahedra. For \inltt{BndLayer} the mesh generator will attempt to insert a single
macro prism layer onto the geometry surface. This option requires additional
parameters. This is similar for the 2D scenarios. The automatic mesh
specification system requires three parameters to build the specification of a
......@@ -890,7 +891,7 @@ An example is shown.
<I PROPERTY="CADFile" VALUE="6412" />
<I PROPERTY="MeshType" VALUE="2DBL" />
<I PROPERTY="MeshType" VALUE="2DBndLayer" />
......@@ -900,10 +901,10 @@ An example is shown.
<P PARAM="Order" VALUE="4" />
<!-- Boundary layer !-->
<P PARAM="BLSurfs" VALUE="5,6" />
<P PARAM="BLThick" VALUE="0.03" />
<P PARAM="BLLayers" VALUE="4" />
<P PARAM="BLProg" VALUE="2.0" />
<P PARAM="BndLayerSurfaces" VALUE="5,6" />
<P PARAM="BndLayerThickness" VALUE="0.03" />
<P PARAM="BndLayerLayers" VALUE="4" />
<P PARAM="BndLayerProgression" VALUE="2.0" />
<!-- 2D Domain !-->
<P PARAM="Xmin" VALUE="-1.0" />
......@@ -917,7 +918,7 @@ An example is shown.
A list of the CAD surfaces which will have a prism generated on is described by
\inltt{BLSurfs} and the thickness of the boundary to aim for is \inltt{BLThick}.
\inltt{BndLayerSurfaces} and the thickness of the boundary to aim for is \inltt{BndLayerThickness}.
The mesh generator has been created with a range of error messages to aid in
debugging. If you encounter an error and the mesh generator fails, run \nm with
......@@ -925,6 +926,32 @@ the verbose \inltt{-v} flag and send the stdout with the .mcf and .step files
to \inltt{}. Without the feedback this functionality
cannot improve.
\subsubsection{GEO format}
Recent developments have been made to facilitate the generation of meshes from
simple 2D geometries. The GEO file format, used by Gmsh, is a popular option
that allows the user to script geometrical and meshing operations without the
need of a GUI. A simplified reader has been implemented in NekMesh for 2D geometries.
Although very basic this reader may be extended in the future to cover a wider
range of geometrical features.
For a full description of the GEO format the user should refer to Gmsh's
documentation. The following commands are currently supported:
\item \inltt{//} (comments)
\item \inltt{Point}
\item \inltt{Line}
\item \inltt{Spline}
\item \inltt{Line Loop}
\item \inltt{Plane Surface}
At the present time, NekMesh does not support the full scripting capabilities of the
GEO format. The used GEO files should be a straightforward succession of entity
creations (see list above). This should however allow for the creation of quite
a wide range of 2D geometries by transformation of arbitrary curves into generic
%%% Local Variables:
%%% mode: latex
......@@ -140,6 +140,21 @@ namespace Nektar
static bool GenerateUnOrderedVector(const char *const str, std::vector<unsigned int> &vec)
// Functors used to parse the sequence.
fctor6 functor6(&vec);
return parse(str,
// Begin grammar
uint_p[functor6] >> *(',' >> uint_p[functor6])
// End grammar
static bool GenerateOrderedStringVector(const char *const str, std::vector<std::string> &vec)
// Functors used to parse the sequence.
......@@ -351,6 +366,23 @@ namespace Nektar
struct fctor6
fctor6(std::vector<unsigned int> *vec):
void operator()(unsigned int n) const
std::vector<unsigned int> *m_vector;
......@@ -40,6 +40,9 @@
#include <NekMeshUtils/CADSystem/OCE/CADSystemOCE.h>
#include <NekMeshUtils/CADSystem/OCE/CADVertOCE.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
using namespace std;
namespace Nektar
......@@ -54,7 +57,16 @@ bool CADSystemOCE::LoadCAD()
if (m_naca.size() == 0)
// not a naca profile behave normally
//not a naca profile behave normally
//could be a geo
string ext = boost::filesystem::extension(m_name);
if (boost::iequals(ext, ".geo"))
shape = BuildGeo(m_name);
// Takes step file and makes OpenCascade shape
STEPControl_Reader reader;
reader = STEPControl_Reader();
......@@ -67,6 +79,7 @@ bool CADSystemOCE::LoadCAD()
return false;
shape = BuildNACA(m_name);
......@@ -388,8 +401,166 @@ TopoDS_Shape CADSystemOCE::BuildNACA(string naca)
ShapeFix_Face sf(face.Face());
return sf.Face();
TopoDS_Shape CADSystemOCE::BuildGeo(string geo)
ifstream f;;
map<int, string> points;
map<int, string> lines;
map<int, string> splines;
map<int, string> loops;
map<int, string> surfs;
string fline;
string flinetmp;
while (!f.eof())
getline(f, fline);
if (fline.size() == 0)
if (boost::starts_with(fline, "//"))
if (!boost::contains(fline, ";"))
flinetmp += fline;
fline = flinetmp + fline;
vector<string> tmp1, tmp2;
boost::split(tmp1, fline, boost::is_any_of("="));
boost::split(tmp2, tmp1[0], boost::is_any_of("("));
string type = tmp2[0];
boost::erase_all(tmp2[1], ")");
boost::erase_all(tmp2[1], " ");
int id = boost::lexical_cast<int>(tmp2[1]);
boost::erase_all(tmp1[1], " ");
boost::erase_all(tmp1[1], "{");
boost::erase_all(tmp1[1], "}");
boost::erase_all(tmp1[1], ";");
string var = tmp1[1];
if (boost::iequals(type, "Point"))
points[id] = var;
else if (boost::iequals(type, "Line"))
lines[id] = var;
else if (boost::iequals(type, "Spline"))
splines[id] = var;
else if (boost::iequals(type, "Line Loop"))
//line loops sometimes have negative entries for gmsh
//orientaton purposes
//we dont care so remove it
boost::erase_all(var, "-");
loops[id] = var;
else if (boost::iequals(type, "Plane Surface"))
surfs[id] = var;
cout << "not sure " << type << endl;
map<int, string>::iterator it;
// build points
map<int, gp_Pnt> cPoints;
for (it = points.begin(); it != points.end(); it++)
vector<NekDouble> data;
ParseUtils::GenerateUnOrderedVector(it->second.c_str(), data);
cPoints[it->first] =
gp_Pnt(data[0] * 1000.0, data[1] * 1000.0, data[2] * 1000.0);
// build edges
map<int, TopoDS_Edge> cEdges;
for (it = lines.begin(); it != lines.end(); it++)
vector<unsigned int> data;
ParseUtils::GenerateUnOrderedVector(it->second.c_str(), data);
BRepBuilderAPI_MakeEdge em(cPoints[data[0]], cPoints[data[1]]);
cEdges[it->first] = em.Edge();
for (it = splines.begin(); it != splines.end(); it++)
vector<unsigned int> data;
ParseUtils::GenerateUnOrderedVector(it->second.c_str(), data);
TColgp_Array1OfPnt pointArray(0, data.size() - 1);
for (int i = 0; i < data.size(); i++)
pointArray.SetValue(i, cPoints[data[i]]);
GeomAPI_PointsToBSpline spline(pointArray);
Handle(Geom_BSplineCurve) curve = spline.Curve();
BRepBuilderAPI_MakeEdge em(curve);
cEdges[it->first] = em.Edge();
// build wires
map<int, TopoDS_Wire> cWires;
for (it = loops.begin(); it != loops.end(); it++)
vector<unsigned int> data;
ParseUtils::GenerateUnOrderedVector(it->second.c_str(), data);
BRepBuilderAPI_MakeWire wm;
for (int i = 0; i < data.size(); i++)
cWires[it->first] = wm.Wire();
// make surface, at this point assuming its 2D (therefore only 1)
// also going to assume that the first loop in the list is the outer domain
ASSERTL0(surfs.size() == 1, "more than 1 surf");
it = surfs.begin();
vector<unsigned int> data;
ParseUtils::GenerateUnOrderedVector(it->second.c_str(), data);
BRepBuilderAPI_MakeFace face(cWires[data[0]], true);
for (int i = 1; i < data.size(); i++)
ASSERTL0(face.Error() == BRepBuilderAPI_FaceDone, "build geo failed");
ShapeFix_Face sf(face.Face());
return sf.Face();
......@@ -76,6 +76,7 @@ private:
void AddSurf(int i, TopoDS_Shape in);
TopoDS_Shape BuildNACA(std::string naca);
TopoDS_Shape BuildGeo(std::string geo);
/// OCC master object
TopoDS_Shape shape;
TopTools_IndexedMapOfShape mapOfVerts, mapOfEdges, mapOfFaces;
......@@ -150,6 +150,7 @@ IF(NEKTAR_USE_MESHGEN)
ADD_NEKTAR_TEST (MeshGen/sphere)
ADD_NEKTAR_TEST (MeshGen/2d-cad)
ADD_NEKTAR_TEST (MeshGen/2d-naca)
ADD_NEKTAR_TEST (MeshGen/t106step)
<?xml version="1.0" encoding="utf-8" ?>
<description>.geo reader test mesh</description>
<parameters>-m jac:list t106c.mcf geo.xml:xml:test</parameters>
<file description="Input File">t106c.mcf</file>
<file description="Input File 2">t106c.geo</file>
<metric type="regex" id="1">
<regex>^Total negative Jacobians: (\d+)</regex>
<field id="0">0</field>
This diff is collapsed.
<I PROPERTY="CADFile" VALUE="t106c.geo" />
<I PROPERTY="MeshType" VALUE="2D" />
<P PARAM="MinDelta" VALUE="0.0005" />
<P PARAM="MaxDelta" VALUE="0.1" />
<P PARAM="EPS" VALUE="0.01" />
<P PARAM="Order" VALUE="4" />
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment