GmshCommon - A .NET/CLI wrapper for Gmsh

Hello again,

Due to some work that I am doing, I have been working on a Gmsh .NET/CLI wrapper to allow me to use Gmsh within a .NET program directly.

It is not feature-complete yet, by any means, but it does expose most of the core functionality for loading entities, meshing, and extracting mesh data.

I have been using it alongside PrePoMax through the PrePoMax API and it works pretty seamlessly, avoiding the need to write meshes to a file and subsequently loading them in PrePoMax.

It is available here.

1 Like

Dear Tom,

I think this is great coincidence. My next big goal is to add support for Gmsh to PrePoMax to enable structured 2D and 3D meshing. At least for simple geometries in 3D. I was planning to use .geo files so I will take a look at your code shortly (vacation).

Have you any experience creating hexahedral meshes?

2 Likes

Great!

I can share some code for going from GmshCommon to adding nodes and elements in PrePoMax without exporting/importing :slight_smile:

Have you any experience creating hexahedral meshes?

No… I’ve been sticking to tetrahedra in my work :stuck_out_tongue: Would be interesting to see how good Gmsh is at this…

That woud be great it woud help a lot.

Code to get node data…

// Get all nodes
var nodes = new Dictionary<int, double[]>;

IntPtr[] nodeTagsIntPtr;
double[] coords;
Gmsh.Mesh.GetNodes(out nodeTagsIntPtr, out coords, 3, -1, true, false);

for (int i = 0; i < nodeTagsIntPtr.Length; ++i)
{
     var nodeTag = (int)nodeTagsIntPtr[i];
     var nodeCoord = new double[] { coords[i * 3], coords[i * 3 + 1], coords[i * 3 + 2] };
 
     nodes[nodeTag] = nodeCoord;
}

… and element data…

var elements = new Dictionary<int, int[]>();
var elementTypes = new Dictionary<int, int>();
int dimension = 3;

int[] elementTypesTemp;
IntPtr[][] elementTagsIntPtr;
IntPtr[][] elementNodeTags;
Gmsh.Mesh.GetElements(out elementTypesTemp, out elementTagsIntPtr, out elementNodeTags, dimension , -1);

// This reshuffling is required due to the way Gmsh 
// organizes the returned element data
for (int i = 0; i < elementTypesTemp.Length; ++i)
{
    var elementType = elementTypesTemp[i];

    int dim, order, numNodes, numPrimaryNodes;
    string elementName;
    double[] localNodeCoords;

    elementName = GmshCommon.Gmsh.Mesh.GetElementProperties(elementType, out dim, out order, out numNodes, out localNodeCoords, out numPrimaryNodes);

    for (int j = 0; j < elementTagsIntPtr[i].Length; ++j)
    {
        var elementTag = (int)elementTagsIntPtr[i][j];
        elementTypes[elementTag] = elementType;
        var elementNodes = new int[numNodes];

        for (int k = 0; k < numNodes; ++k)
        {
            elementNodes[k] = (int)elementNodeTags[i][j * numNodes + k];
        }

        elements[elementTag] = elementNodes;
    }
}

Then, to make nodes in PrePoMax…

foreach (var entry in nodes)
{
    model.Mesh.Nodes.Add(entry .Key, new FeNode(entry .Key, entry .Value));
}

…and elements…

var partId = 1;
foreach (var entry in elements)
{
    // Element types from http://gmsh.info/doc/texinfo/gmsh.html#MSH-file-format
    switch (elementTypes[entry.Key])
    {
        case (4): // 4-node tetrahedron. 
            model.Mesh.Elements.Add(entry.Key, new LinearTetraElement(entry.Key, partId , entry.Value));
            break;
        case (11): // 10-node second order tetrahedron
            model.Mesh.Elements.Add(entry.Key, new ParabolicTetraElement(entry.Key, partId , entry.Value));
            break;
        case (...): // other element types...
            model.Mesh.Elements.Add(entry.Key, new ...);
            break;
        default:
            break;
    }
}

Then there is more fun stuff to do with defining physical groups, making a compound mesh (fragmenting with the OCC kernel), and more :slight_smile:

1 Like

This part can be very important since without it you get unnecessary 1D and 2D elements in the exported input file containing a 3D mesh.

This part can be very important since without it you get unnecessary 1D and 2D elements in the exported input file containing a 3D mesh.

The cool things is that with this way, you are in control of exactly what you transfer over to PrePoMax, since Gmsh isn’t exporting any .inp files. So you can decide how to construct the FE model.

That said, assigning and manipulating physical groups are already supported in GmshCommon :wink: This is so that we can create element- or node-sets from Gmsh entities - very useful for multi-part input geometries.

At some point I will try to document things better, but for now you can see what functions are available in the source code here.

possible by using extrusion based on surface with fully quad mesh (quasi-structured) or hex dominant mesh as an alternative.

both approach are semi and fully automatic i.e no partition is needed before.

I will add support for extruded mesh and for revolved mesh. But first, I will have to figure out how this works in Gmsh and how to incorporate it into PrePoMax.

1 Like

thanks for interesting in extrusion and revolwing of surfaces to generates hexahedral element. may spliting surfaces by line curently available in PrePoMax be extended to a body.

quasi-structured algorithm no need a surfaces to be partiton before as common user task in structured quads.

2023-03-07 14_52_52-Options - Mesh

then a base of quad results can be extruded by thickness definition and number of element layer.

some workflow still required a partition when the model has intersection, this video YouTube links is more detailed about the steps.

however, i did net yet to test for latest GMSH capabilities in normal extrusion of complex 3D surfaces or Nurbs.

1 Like

I am starting to test the capabilities of the Gmsh, and I also started to test the GmshCommon.dll. I managed to set it up and run a test model. Great (I was missing the x64 configuration at the beginning).

But I had no success in building the GmshCommon .dll from the source. I downloaded it in a separate folder, added the dependencies in the deps folder and tried compiling it using the solution file with Visual Studi 2017 and 2022, but I got the same error: unresolved external symbols. It seems the error occurs for all methods/functions of the class GmshCommon. Something is missing.

I know I am missing something basic, but I can not figure it out. @tsvilans any ideas?

Hmm… is it linking with the Gmsh libs from the SDK? It sounds like it cannot find the Gmsh dependencies… What is your deps folder structure?

My opinion is the same but I do not know why the Gmsh files would not be found. Did you apply any special settings when you prepared the project? And I am wondering which software you are using to build it and which version of it.

I created a folder Gmsh and used the instructions you provided. I downloaded the gmsh SDK and copied it into deps folder (I also checked the settings for the Include and Library directories and despite that they were pointing to the right path I added additional absolute paths to it).

Interesting. I will take a closer look after an imminent deadline this week… it must be something simple.
I’m using Visual Studio 2022, nothing really extraordinary. I will check the paths as well…

This is where it is looking for the libs:

So it looks the same as yours. What does your $(SolutionDir) variable resolve to?

The $(SolutionDir) is equal to C:\Pc\Programiranje\Gmsh\gmsh_common\src

If I change the Configuration Type to a Static library, it compiles it without errors.

Do you think this might cause problems if it finds the same libs twice? Or do you mean you used absolute paths instead of relative ones?

The only other thing I can think of if it is somehow related to the CLI, but I’m kind of in the dark there…

This is very weird… It needs to be a dynamic library, but I’m not sure why it would work as a static library…

Does this page look the same on your side?

The settings are the same.

I tried it on a second “clean” machine and I get the same error.

Can you compare the command line from C/C++ settings with your version:

/Yu"pch.h" /ifcOutput "x64\Debug\" /GS /W3 /Zc:wchar_t /I"C:\Programiranje\Gmsh\gmsh_common\src\..\deps\gmsh-4.11.1-Windows64-sdk\include" /Zi /O2 /Fd"x64\Debug\vc143.pdb" /Zc:inline /fp:precise /D "_DEBUG" /D "_WINDLL" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /clr /FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\mscorlib.dll" /FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Data.dll" /FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.dll" /FU"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Xml.dll" /MD /FC /Fa"x64\Debug\" /EHa /nologo /Fo"x64\Debug\" /Fp"x64\Debug\GmshCommon.pch" /diagnostics:column

And the command line from the Linker settings:

/OUT:"C:\Programiranje\Gmsh\gmsh_common\src\..\bin\GmshCommon.dll" /MANIFEST /NXCOMPAT /PDB:"C:\Programiranje\Gmsh\gmsh_common\src\..\bin\GmshCommon.pdb" /DYNAMICBASE "gmsh.lib" /FIXED:NO /DEBUG:FULL /DLL /MACHINE:X64 /PGD:"C:\Programiranje\Gmsh\gmsh_common\src\..\bin\GmshCommon.pgd" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /ManifestFile:"x64\Debug\GmshCommon.dll.intermediate.manifest" /LTCGOUT:"x64\Debug\GmshCommon.iobj" /ERRORREPORT:PROMPT /ILK:"x64\Debug\GmshCommon.ilk" /NOLOGO /LIBPATH:"C:\Programiranje\Gmsh\gmsh_common\src\..\deps\gmsh-4.11.1-Windows64-sdk\lib" /TLBID:1

This lines are from my second machine where the base folder is: C:\Programiranje\Gmsh\gmsh_common