6. Examples

This chapter contains example CEETRON Mesh programs demonstrating the use of the mesh generation modules. The source code for all examples resides in CeetronSAM/src/sam/vis/exam.

6.1. Example 41, Generate a Surface Mapped Mesh

This example illustrates mapped mesh generation using the MapMesh module. A simple beam with a length of 10. and a height of 1. is to be modeled with 8 node Serendipity quadrilateral elements in 2D. The beam geometry is input as 4 points forming a simple linear quadrilateral patch. A node association is defined along edge 4 of the patch so that the nodes generated along the edge may be identified, for example, to apply a clamped boundary restraint. The value chosen for the association, in this case 100, is arbitrary. A node association of 200 is assigned to point 2. This association could be possibly used to apply a concentrated load.

The msh_MapMeshSetPatchParami() function is used to specify that a 8 by 2 grid of quadrilateral elements is to be generated. The msh_MapMeshSetParami() function is used to specify parabolic elements. The nodes and finite elements are actually generated by calling msh_MapMeshGenerate(). The generated nodes and elements are entered into the Connect object which is passed in the argument list. Lastly, the generated nodes and elements are printed.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[4][3] = {{0., 0., 0.}, {10., 0., 0.}, {10., 1., 0.}, {0., 1., 0.}};

/*----------------------------------------------------------------------
                      Generate a Surface Mapped Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_MapMesh* mapmesh;
    vis_Connect* connect;

    Vint i;
    Vint pix[4];
    Vint numnp, numel;
    Vdouble x[3];
    Vint aid, aid1;
    Vint nix, ix[8];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create MapMesh object */
    mapmesh = msh_MapMeshBegin();
    msh_MapMeshDef(mapmesh, 4, 1);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define points */
    for (i = 0; i < 4; i++) {
        msh_MapMeshSetPoint(mapmesh, i + 1, coords[i]);
    }
    /* define patch connectivity */
    pix[0] = 1;
    pix[1] = 2;
    pix[2] = 3;
    pix[3] = 4;
    msh_MapMeshSetPatch(mapmesh, 1, VIS_SHAPEQUAD, 2, 0, 0, pix);

    /* set assoc of 100 at edge 4 */
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 1, SYS_EDGE, 4, 100);

    /* set assoc of 200 at point 2 */
    msh_MapMeshSetPointAssoc(mapmesh, VIS_MISCID1, 2, 200);

    /* set mesh parameters */
    msh_MapMeshSetParami(mapmesh, VIS_MESH_MAXI, 3);
    msh_MapMeshSetPatchParami(mapmesh, 1, VIS_MESH_SHAPE, VIS_SHAPEQUAD);
    msh_MapMeshSetPatchParami(mapmesh, 1, MAPMESH_NUMELEMI, 8);
    msh_MapMeshSetPatchParami(mapmesh, 1, MAPMESH_NUMELEMJ, 2);

    /* generate */
    msh_MapMeshGenerate(mapmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        vis_ConnectNodeAssoc(connect, VIS_MISCID1, 1, &i, &aid1);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d, aid1= %d\n", i, x[0], x[1], x[2], aid, aid1);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5], ix[6], ix[7]);
    }
    /* end objects */
    vis_ConnectEnd(connect);
    msh_MapMeshEnd(mapmesh);
    return 0;
}

6.2. Example 41a, Generate a Multiple Patch Mapped Mesh

This example illustrates mapped mesh generation of quadrilateral shell elements using the MapMesh module with multiple patches. A shell structure with a circular section connected to a straight section is extruded in the z direction. The circular and straight sections are each modelled with a separate patch. The circular section at z=0. in the x-y plane is modelled with points 1 through 4 which describe a cubic approximation to the circle. Point 1 is located at the origin. The straight section connects point 4 on the circular section to point 5 which marks the other endpoint of the straight section. Points 6 through 10 describe the cross section at z=50. The first patch describes the circular shell, the second patch describes the straight shell. The two patches share points 4 and 9 where they are connected. Separate node associations are defined at the generated nodes on the z=0. and z=50. cross sections for boundary condition application or any other use. A 4 by 20 element mesh is generated for the first patch and a 10 by 20 element mesh for the second patch. The number of elements generated along the common J direction on each patch must be the same for all the generated nodes to be shared correctly between the two patches. The nodes and finite elements are generated by calling msh_MapMeshGenerate() and are entered into the Connect object which is passed in the argument list. Lastly, the generated nodes and elements are printed.

#include <stdio.h>
#include <math.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/*----------------------------------------------------------------------
                      Generate a Multiple Patch Mapped Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_MapMesh* mapmesh;
    vis_Connect* connect;

    Vdouble radius, angle, length, span, radian;
    Vdouble coords[10][3];
    Vint i;
    Vint pix[8];
    Vint numnp, numel;
    Vdouble x[3];
    Vint aid, aid1, pid;
    Vint nix, ix[4];

    radius = 3.;
    angle = 60.;
    length = 10.;
    span = 50.;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create MapMesh object */
    mapmesh = msh_MapMeshBegin();
    msh_MapMeshDef(mapmesh, 10, 2);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectDef(connect, 0, 0);

    /* compute points */
    /* cross section radius, compute 4 points */
    for (i = 0; i < 4; i++) {
        radian = i * angle / 3. * (3.14159 / 180.);
        coords[i][0] = radius - radius * cos(radian);
        coords[i][1] = radius * sin(radian);
        coords[i][2] = 0.;
    }
    /* cross section straight length, 1 additional point */
    coords[4][0] = coords[3][0] + length * sin(radian);
    coords[4][1] = coords[3][1] + length * cos(radian);
    coords[4][2] = 0.;
    /* span, 5 more points offset in z from first 5 points */
    for (i = 0; i < 5; i++) {
        coords[i + 5][0] = coords[i][0];
        coords[i + 5][1] = coords[i][1];
        coords[i + 5][2] = coords[i][2] + span;
    }
    /* define points */
    for (i = 0; i < 10; i++) {
        msh_MapMeshSetPoint(mapmesh, i + 1, coords[i]);
    }
    /* define patch point connectivity */
    /* patch 1 */
    pix[0] = 1;
    pix[1] = 2;
    pix[2] = 3;
    pix[3] = 4;
    pix[4] = 6;
    pix[5] = 7;
    pix[6] = 8;
    pix[7] = 9;
    msh_MapMeshSetPatch(mapmesh, 1, VIS_SHAPEQUAD, 4, 2, 0, pix);
    /* patch 2 */
    pix[0] = 4;
    pix[1] = 5;
    pix[2] = 9;
    pix[3] = 10;
    msh_MapMeshSetPatch(mapmesh, 2, VIS_SHAPEQUAD, 2, 2, 0, pix);

    /* set assoc of 1 at z=0. edge */
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 1, SYS_EDGE, 1, 1);
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 2, SYS_EDGE, 1, 1);

    /* set assoc of 2 at z=span edge */
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 1, SYS_EDGE, 3, 2);
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 2, SYS_EDGE, 3, 2);

    /* set property ids as element associations */
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_PROPID, 1, SYS_ELEM, 0, 10);
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_PROPID, 2, SYS_ELEM, 0, 20);

    /* set mesh parameters */
    msh_MapMeshSetParami(mapmesh, VIS_MESH_MAXI, 2);
    /* patch 1 */
    msh_MapMeshSetPatchParami(mapmesh, 1, VIS_MESH_SHAPE, VIS_SHAPEQUAD);
    msh_MapMeshSetPatchParami(mapmesh, 1, MAPMESH_NUMELEMI, 4);
    msh_MapMeshSetPatchParami(mapmesh, 1, MAPMESH_NUMELEMJ, 20);
    /* patch 2 */
    msh_MapMeshSetPatchParami(mapmesh, 2, VIS_MESH_SHAPE, VIS_SHAPEQUAD);
    msh_MapMeshSetPatchParami(mapmesh, 2, MAPMESH_NUMELEMI, 10);
    msh_MapMeshSetPatchParami(mapmesh, 2, MAPMESH_NUMELEMJ, 20);

    /* generate */
    msh_MapMeshGenerate(mapmesh, connect);

    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam41a.bdf");

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        vis_ConnectNodeAssoc(connect, VIS_MISCID1, 1, &i, &aid1);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d, aid1= %d\n", i, x[0], x[1], x[2], aid, aid1);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        vis_ConnectElemAssoc(connect, VIS_PROPID, 1, &i, &pid);
        printf("id= %d  ix= %d %d %d %d,  pid= %d\n", i, ix[0], ix[1], ix[2], ix[3], pid);
    }

    /* end objects */
    vis_ConnectEnd(connect);
    msh_MapMeshEnd(mapmesh);
    return 0;
}

6.3. Example 42, Generate an Extruded Mesh

This example illustrates extruded mesh generation using the ExtMesh module. A cross section of 2 parabolic Serendipity triangles lying in the x-z plane is extruded about the z axis through 45 degrees using a cylindrical coordinate system. The resulting mesh contains 15 node parabolic Serendipity wedge elements.

The cross section geometry is input as 9 nodes in the x-z global coordinate plane connected by 2 parabolic triangular elements. The node coordinates are defined using msh_ExtMeshSetNode() and are always expressed in the global coordinate system. A CoordSys object is used to define a cylindrical coordinate system in which the points defining the extrusion path are defined. The two path endpoints are defined using msh_ExtMeshSetPoint(). The point coordinates are entered in the cylindrical coordinate system.

The msh_ExtMeshSetPathParami() function is used to specify that 4 planes of elements are to be extruded. Since the cross section contains 2 elements, the total number of elements generated will be 8. The function msh_ExtMeshGenerate() generates the nodes and elements. During the mesh generation process, the input node coordinates are transformed to the cylindrical coordinate system defined by CoordSys. All nodes are then extruded along the specified path in the cylindrical coordinate system. The generated nodes are then transformed back to the global coordinate system before they are entered into the Connect object which is passed in the argument list. Lastly, the generated nodes and elements are printed.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble xnode[9][3] = {{2., 0., 0.}, {3., 0., 0.}, {4., 0., 0.}, {2., 0., 1.}, {3., 0., 1.},
                              {4., 0., 1.}, {2., 0., 2.}, {3., 0., 2.}, {4., 0., 2.}};

static Vint ixtri[2][6] = {{1, 3, 7, 2, 5, 4}, {3, 9, 7, 6, 8, 5}};

static Vdouble origin[3] = {0., 0., 0.};
static Vdouble triad[3][3] = {{1., 0., 0.}, {0., 1., 0.}, {0., 0., 1.}};

/*----------------------------------------------------------------------
                      Generate an Extruded Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_ExtMesh* extmesh;
    vis_Connect* connect;

    Vint i;
    Vdouble xp[3];
    Vint pix[2];
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[15];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create ExtMesh object */
    extmesh = msh_ExtMeshBegin();
    msh_ExtMeshDef(extmesh, 9, 2);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectDef(connect, 0, 0);

    /* set coordinate system transformation */
    msh_ExtMeshSetOriginTriad(extmesh, SYS_CYLINDRICAL, origin, triad);

    /* define nodes */
    for (i = 0; i < 9; i++) {
        msh_ExtMeshSetNode(extmesh, i + 1, xnode[i]);
    }
    /* define parabolic triangle connectivity */
    for (i = 0; i < 2; i++) {
        msh_ExtMeshSetElem(extmesh, i + 1, VIS_SHAPETRI, 3, 0, ixtri[i]);
    }
    /* define path end points */
    xp[0] = 0.;
    xp[1] = 0.;
    xp[2] = 0.;
    msh_ExtMeshSetPoint(extmesh, 1, xp);
    xp[1] = 45.;
    msh_ExtMeshSetPoint(extmesh, 2, xp);
    pix[0] = 1;
    pix[1] = 2;
    msh_ExtMeshSetPath(extmesh, 2, pix);

    /* set mesh parameters */
    msh_ExtMeshSetPathParami(extmesh, 1, VIS_MESH_NUMELEMENTS, 4);

    /* generate */
    msh_ExtMeshGenerate(extmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        printf("id= %d  x= %e, y= %e, z= %e\n", i, x[0], x[1], x[2]);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5]);
        printf("           %d %d %d %d %d %d\n", ix[6], ix[7], ix[8], ix[9], ix[10], ix[11]);
        printf("           %d %d %d\n", ix[12], ix[13], ix[14]);
    }
    /* write out NASTRAN bulk data file */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam42.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_ExtMeshEnd(extmesh);
    return 0;
}

6.4. Example 42a, Generate an Extruded Mesh along Normals

This example illustrates extruded mesh generation along node normals using the ExtMesh module. This type of extrusion meshing is useful in creating boundary layer meshes. The original surface mesh to be extruded consists of 10 triangles which form 5 sides of a unit cube. The extrusion directions are defined for each node. The extrusion directions are normalized and scaled to the desired thickness of the boundary layer mesh, .25 in this case. The function msh_ExtMeshSetNodeNormal() is used to input the extrusion directions for each node.

The msh_ExtMeshSetParami() function is used to activate extrusion along node normals, specify the number of layers (4), flag the fact that the normals are in the opposite direction to the triangle connectivity sense and enable the assignment of the VIS_GEOFACE association to the element faces and nodes generated on the initial and final faces of the extruded mesh. Since the normals are opposite to the connectivity sense, the connectivity of the resulting wedge elements must be reversed to yield a positive volume.

The function msh_ExtMeshGenerate() generates the nodes and elements. Lastly, the generated nodes and elements are printed.

#include <stdio.h>
#include <math.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/*----------------------------------------------------------------------
                      Generate a Multiple Patch Mapped Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_MapMesh* mapmesh;
    vis_Connect* connect;

    Vdouble radius, angle, length, span, radian;
    Vdouble coords[10][3];
    Vint i;
    Vint pix[8];
    Vint numnp, numel;
    Vdouble x[3];
    Vint aid, aid1, pid;
    Vint nix, ix[4];

    radius = 3.;
    angle = 60.;
    length = 10.;
    span = 50.;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create MapMesh object */
    mapmesh = msh_MapMeshBegin();
    msh_MapMeshDef(mapmesh, 10, 2);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectDef(connect, 0, 0);

    /* compute points */
    /* cross section radius, compute 4 points */
    for (i = 0; i < 4; i++) {
        radian = i * angle / 3. * (3.14159 / 180.);
        coords[i][0] = radius - radius * cos(radian);
        coords[i][1] = radius * sin(radian);
        coords[i][2] = 0.;
    }
    /* cross section straight length, 1 additional point */
    coords[4][0] = coords[3][0] + length * sin(radian);
    coords[4][1] = coords[3][1] + length * cos(radian);
    coords[4][2] = 0.;
    /* span, 5 more points offset in z from first 5 points */
    for (i = 0; i < 5; i++) {
        coords[i + 5][0] = coords[i][0];
        coords[i + 5][1] = coords[i][1];
        coords[i + 5][2] = coords[i][2] + span;
    }
    /* define points */
    for (i = 0; i < 10; i++) {
        msh_MapMeshSetPoint(mapmesh, i + 1, coords[i]);
    }
    /* define patch point connectivity */
    /* patch 1 */
    pix[0] = 1;
    pix[1] = 2;
    pix[2] = 3;
    pix[3] = 4;
    pix[4] = 6;
    pix[5] = 7;
    pix[6] = 8;
    pix[7] = 9;
    msh_MapMeshSetPatch(mapmesh, 1, VIS_SHAPEQUAD, 4, 2, 0, pix);
    /* patch 2 */
    pix[0] = 4;
    pix[1] = 5;
    pix[2] = 9;
    pix[3] = 10;
    msh_MapMeshSetPatch(mapmesh, 2, VIS_SHAPEQUAD, 2, 2, 0, pix);

    /* set assoc of 1 at z=0. edge */
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 1, SYS_EDGE, 1, 1);
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 2, SYS_EDGE, 1, 1);

    /* set assoc of 2 at z=span edge */
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 1, SYS_EDGE, 3, 2);
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_MISCID, 2, SYS_EDGE, 3, 2);

    /* set property ids as element associations */
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_PROPID, 1, SYS_ELEM, 0, 10);
    msh_MapMeshSetPatchAssoc(mapmesh, VIS_PROPID, 2, SYS_ELEM, 0, 20);

    /* set mesh parameters */
    msh_MapMeshSetParami(mapmesh, VIS_MESH_MAXI, 2);
    /* patch 1 */
    msh_MapMeshSetPatchParami(mapmesh, 1, VIS_MESH_SHAPE, VIS_SHAPEQUAD);
    msh_MapMeshSetPatchParami(mapmesh, 1, MAPMESH_NUMELEMI, 4);
    msh_MapMeshSetPatchParami(mapmesh, 1, MAPMESH_NUMELEMJ, 20);
    /* patch 2 */
    msh_MapMeshSetPatchParami(mapmesh, 2, VIS_MESH_SHAPE, VIS_SHAPEQUAD);
    msh_MapMeshSetPatchParami(mapmesh, 2, MAPMESH_NUMELEMI, 10);
    msh_MapMeshSetPatchParami(mapmesh, 2, MAPMESH_NUMELEMJ, 20);

    /* generate */
    msh_MapMeshGenerate(mapmesh, connect);

    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam41a.bdf");

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        vis_ConnectNodeAssoc(connect, VIS_MISCID1, 1, &i, &aid1);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d, aid1= %d\n", i, x[0], x[1], x[2], aid, aid1);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        vis_ConnectElemAssoc(connect, VIS_PROPID, 1, &i, &pid);
        printf("id= %d  ix= %d %d %d %d,  pid= %d\n", i, ix[0], ix[1], ix[2], ix[3], pid);
    }

    /* end objects */
    vis_ConnectEnd(connect);
    msh_MapMeshEnd(mapmesh);
    return 0;
}

6.5. Example 42b, Generate a TetMesh with an Extruded Boundary Layer

This example illustrates generating a boundary layer mesh from a surface geometry and filling the interior with tetrahedra. The process outlined in this example is typical of a complete mesh generation sequence for meshing the interior of a surface mesh in which a boundary layer mesh is required. The SurfMesh, ExtMesh and TetMesh objects are using in succession to generate the complete mesh.

A simple unit cube defined by 12 boundary triangles defines the surface geometry. These triangles are entered into the SurfMesh object for generating the initial surface mesh of triangles. The resulting triangles are entered into the ExtMesh object. The normals of the surface triangle mesh are averaged at each node and scaled by the desired boundary layer length of .25. Since the boundary layer mesh is extruded in the opposite direction of the sense of the boundary triangle connectivity, the parameter EXTMESH_REVERSE is set to create positive volume boundary layer elements. The extrusion distance is large enough that the node vectors begin to “intersect” resulting in inverted elements. The parameter EXTMESH_LOCALCHECK is enabled to check for inversion and recede the extrusion distance sufficiently to avoid inversion. The parameter EXTMESH_FACEMARK is enabled to mark all nodes and element face on the final extrusion layer with the association VIS_GEOFACE. All nodes and element faces on the initial extrusion layer are marked with a value of 1 and with a 2 on the final extrusion layer. This will facilitate passing the triangles on the final extrusion layer to the TetMesh module for the final tetrahedral meshing of the interior.

Finally, using the VIS_GEOFACE association, the nodes and triangles on the final extrusion layer are detected and passed to the TetMesh module. The VIS_MESH_INWARD parameter must be set since the sense of the triangle on the final extrusion layer will point to the inside of the geometry.

#include <stdio.h>
#include <math.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a simple unit cube */
static Vdouble xc[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                           {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};

static Vint tefl[12][3] = {{1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 1}, {0, 1, 1},
                           {0, 1, 1}, {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {1, 1, 0}};

/* surface triangle node normals */
static Vdouble norms[12][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}}, {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
         Cross product between two vectors
----------------------------------------------------------------------*/
static void
cross(Vdouble a[3], Vdouble b[3], Vdouble c[3])
{
    c[0] = a[1] * b[2] - a[2] * b[1];
    c[1] = a[2] * b[0] - a[0] * b[2];
    c[2] = a[0] * b[1] - a[1] * b[0];
}

/*----------------------------------------------------------------------
         Difference between two vectors
----------------------------------------------------------------------*/
static void
diff(Vdouble a[3], Vdouble b[3], Vdouble c[3])
{
    c[0] = a[0] - b[0];
    c[1] = a[1] - b[1];
    c[2] = a[2] - b[2];
}

/*----------------------------------------------------------------------
         Generate Extruded Boundary Layer with Interior TetMesh
----------------------------------------------------------------------*/
int
main()
{
    vis_IdTran* idtran;
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    msh_ExtMesh* extmesh;
    vis_Connect *connectsurf, *connectext, *connecttet, *connectall;
    vis_Group *ngroup, *fgroup;
    Vdouble(*normal)[3], x[3][3], u[3], v[3], w[3], mag;
    Vint i, j, nix, ix[10], numnpsurf, numelsurf, numnpext, numelext;
    Vint jx[3], numnp, numel, no, aid, num;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* perform surface meshing */
    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 8, 12);

    /* define points */
    for (i = 0; i < 8; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
    }
    /* set mesh parameters */
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, 0.2);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }

    /* write generated surf mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam42b_srf.bdf");

    /* perform extrusion meshing */
    /* compute averaged nodal normals */
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numelsurf);
    vis_ConnectNumber(connectsurf, SYS_NODE, &numnpsurf);
    normal = (Vdouble(*)[3])malloc(3 * numnpsurf * sizeof(Vdouble));
    for (i = 0; i < numnpsurf; ++i) {
        normal[i][0] = normal[i][1] = normal[i][2] = 0.;
    }
    for (i = 0; i < numelsurf; ++i) {
        vis_ConnectElemNode(connectsurf, i + 1, &nix, ix);
        vis_ConnectCoordsdv(connectsurf, nix, ix, x);
        diff(x[1], x[0], u);
        diff(x[2], x[0], v);
        cross(u, v, w);
        mag = sqrt(w[0] * w[0] + w[1] * w[1] + w[2] * w[2]);
        w[0] /= mag;
        w[1] /= mag;
        w[2] /= mag;
        for (j = 0; j < 3; ++j) {
            normal[ix[j] - 1][0] += w[0];
            normal[ix[j] - 1][1] += w[1];
            normal[ix[j] - 1][2] += w[2];
        }
    }
    /* set normal length to layer size of 0.25 */
    for (i = 0; i < numnpsurf; ++i) {
        mag = sqrt(normal[i][0] * normal[i][0] + normal[i][1] * normal[i][1] + normal[i][2] * normal[i][2]);
        mag = 0.25 / mag;
        normal[i][0] *= -mag;
        normal[i][1] *= -mag;
        normal[i][2] *= -mag;
    }

    /* create ExtMesh object */
    extmesh = msh_ExtMeshBegin();
    msh_ExtMeshDef(extmesh, numnpsurf, numelsurf);

    /* enter generated surface mesh */
    for (i = 1; i <= numnpsurf; ++i) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, &u);
        msh_ExtMeshSetNode(extmesh, i, u);
        msh_ExtMeshSetNodeNormal(extmesh, i, normal[i - 1]);
    }
    for (i = 1; i <= numelsurf; ++i) {
        vis_ConnectElemNode(connectsurf, i, &nix, ix);
        msh_ExtMeshSetElem(extmesh, i, SYS_SHAPETRI, 2, 0, ix);
    }
    /* set mesh parameters */
    msh_ExtMeshSetParami(extmesh, EXTMESH_NORMAL, SYS_ON);
    msh_ExtMeshSetParamd(extmesh, VIS_MESH_GROWTHRATE, 2.);
    msh_ExtMeshSetParami(extmesh, EXTMESH_LOCALRECEDE, SYS_ON);
    msh_ExtMeshSetParami(extmesh, EXTMESH_REVERSE, SYS_ON);
    msh_ExtMeshSetNormalPath(extmesh, 4, 1., SYS_OFF);
    /* mark faces on original layer with value of 1
       and faces on final layer with value of 2 */
    msh_ExtMeshSetParami(extmesh, EXTMESH_FACEMARK, VIS_GEOFACE);

    /* create Connect object and generate */
    connectext = vis_ConnectBegin();
    vis_ConnectPre(connectext, SYS_DOUBLE);
    vis_ConnectDef(connectext, 0, 0);
    msh_ExtMeshGenerate(extmesh, connectext);
    if (msh_ExtMeshError(extmesh)) {
        printf("extrusion mesh generation error\n");
        exit(1);
    }
    else {
        printf("extrusion mesh generation complete\n");
    }
    /* write generated ext mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectext, SYS_NASTRAN_BULKDATA, "exam42b_ext.bdf");

    /* perform tet meshing of the remaining interior */
    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numnpsurf, numelsurf, 2);
    msh_TetMeshSetParami(tetmesh, VIS_MESH_INWARD, SYS_ON);

    /* fill TetMesh object with generated points on surface */
    vis_ConnectNumber(connectext, SYS_ELEM, &numelext);
    vis_ConnectNumber(connectext, SYS_NODE, &numnpext);
    numnp = 0;
    idtran = vis_IdTranBegin();
    /* find nodes on surface */
    /* first generate face group */
    fgroup = vis_GroupBegin();
    vis_GroupDef(fgroup, numelext, SYS_ELEM, SYS_FACE);
    vis_ConnectSetGroupParami(connectext, CONNECT_ASSOCTYPE, VIS_GEOFACE);
    vis_ConnectSetGroupParami(connectext, CONNECT_ASSOCID, 2);
    vis_ConnectKernel(connectext, 0);
    vis_ConnectFaceGroup(connectext, CONNECT_ASSOC, NULL, fgroup);
    /* second generate unique node group from face group */
    ngroup = vis_GroupBegin();
    vis_GroupDef(ngroup, numnpext, SYS_NODE, SYS_NONE);
    vis_ConnectNodeGroup(connectext, CONNECT_UNIQUE, fgroup, ngroup);
    /* set nodes */
    for (i = 1; i <= numnpext; i++) {
        if (!vis_GroupNode(ngroup, i))
            continue;
        numnp++;
        vis_IdTranSetId(idtran, numnp, i);
        vis_ConnectCoordsdv(connectext, 1, &i, &u);
        msh_TetMeshSetNode(tetmesh, numnp, u);
    }
    vis_GroupEnd(ngroup);
    vis_GroupEnd(fgroup);

    /* fill TetMesh object with generated faces on surface */
    numel = 0;
    for (i = 1; i <= numelext; i++) {
        vis_ConnectElemNum(connectext, SYS_FACE, i, &num);
        for (no = 1; no <= num; no++) {
            vis_ConnectElemEntAssoc(connectext, VIS_GEOFACE, SYS_FACE, i, no, &aid);
            if (aid != 2)
                continue;
            vis_ConnectElemCon(connectext, SYS_FACE, i, no, &nix, ix);
            vis_ConnectCoordsdv(connectext, nix, ix, x);
            vis_IdTranIndices(idtran, nix, ix, jx);
            numel++;
            msh_TetMeshSetTri(tetmesh, numel, jx);
        }
    }
    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    /* write generated tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam42b_tet.bdf");

    /* merge extruded and tet meshes */
    connectall = vis_ConnectBegin();
    vis_ConnectPre(connectall, SYS_DOUBLE);
    vis_ConnectAppend(connectall, connectext);
    vis_ConnectAppend(connectall, connecttet);
    vis_ConnectSetParamf(connectall, CONNECT_TOLERANCE, 0.0);
    vis_ConnectMerge(connectall, NULL);
    vis_ConnectWrite(connectall, SYS_NASTRAN_BULKDATA, "exam42b_all.bdf");

    /* end objects */
    free(normal);
    vis_IdTranEnd(idtran);
    vis_ConnectEnd(connectall);
    vis_ConnectEnd(connectext);
    vis_ConnectEnd(connectsurf);
    vis_ConnectEnd(connecttet);
    msh_ExtMeshEnd(extmesh);
    msh_SurfMeshEnd(surfmesh);
    msh_TetMeshEnd(tetmesh);
    return 0;
}

6.6. Example 42c, Generate an Extruded Mesh with Interface Layers

This example illustrates generating a normal-extruded mesh where the requested extrusion distances would lead to collisions between elements. These collisions can be avoided by enabling a global recession algorithm using the integer parameter EXTMESH_GLOBALRECEDE.

The example also illustrates the usage of 3 paths in the extrusion direction, each with its own fraction of the normal distance and number of layers. Interface elements are requested at the end of each extrusion path.

The parameter EXTMESH_FACEMARK is enabled to mark all element faces at the boundaries of each extrusion path.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble x[16][3] = {{0., 1., 1.}, {1., 1., 1.}, {2., 0., 1.}, {3., 0., 1.}, {4., 1., 1.}, {5., 1., 1.},
                           {0., 1., 0.}, {1., 1., 0.}, {2., 0., 0.}, {3., 0., 0.}, {4., 1., 0.}, {5., 1., 0.},
                           {0., 2., 0.}, {5., 2., 0.}, {0., 2., 1.}, {5., 2., 1.}};

static Vdouble xn[16][3] = {{0., 1., 0.},  {1., 1., 0.},  {1., 1., 0.},  {-1., 1., 0.}, {-1., 1., 0.}, {0., 1., 0.},
                            {0., 1., 0.},  {1., 1., 0.},  {1., 1., 0.},  {-1., 1., 0.}, {-1., 1., 0.}, {0., 1., 0.},
                            {0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}};

static Vint pix[6][4] = {{1, 2, 8, 7}, {2, 3, 9, 8}, {3, 4, 10, 9}, {4, 5, 11, 10}, {5, 6, 12, 11}, {13, 14, 16, 15}};

/*----------------------------------------------------------------------
                      Generate an Extruded Mesh with Interface Layers
----------------------------------------------------------------------*/
int
main()
{
    msh_ExtMesh* extmesh;
    vis_Connect* connect;
    Vint numnp, numel, i, j, nix, ix[8];
    Vdouble y[3];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create ExtMesh object */
    extmesh = msh_ExtMeshBegin();
    msh_ExtMeshDef(extmesh, 16, 6);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < 16; i++) {
        msh_ExtMeshSetNode(extmesh, i + 1, x[i]);
        msh_ExtMeshSetNodeNormal(extmesh, i + 1, xn[i]);
    }
    /* define linear quadrilateral connectivity */
    for (i = 0; i < 6; i++) {
        msh_ExtMeshSetElem(extmesh, i + 1, VIS_SHAPEQUAD, 2, 0, pix[i]);
    }
    /* set mesh parameters */
    msh_ExtMeshSetParami(extmesh, EXTMESH_NORMAL, SYS_ON);
    msh_ExtMeshSetParami(extmesh, EXTMESH_GLOBALRECEDE, SYS_ON);
    msh_ExtMeshSetParami(extmesh, EXTMESH_FACEMARK, VIS_GEOFACE);
    /* 3 sets of layers with an interface */
    msh_ExtMeshSetNormalPath(extmesh, 4, 0.25, SYS_ON);
    msh_ExtMeshSetNormalPath(extmesh, 5, 0.5, SYS_ON);
    msh_ExtMeshSetNormalPath(extmesh, 4, 0.25, SYS_ON);

    /* generate */
    msh_ExtMeshGenerate(extmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, &y);
        printf("id= %d  x= %e, y= %e, z= %e\n", i, y[0], y[1], y[2]);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix=", i);
        for (j = 0; j < nix; j++) {
            printf(" %d", ix[j]);
        }
        printf("\n");
    }
    /* write out NASTRAN bulk data file */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam42c.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_ExtMeshEnd(extmesh);
    return 0;
}

6.7. Example 43, Generate a 2D Planar Tri Mesh

This example illustrates generating a triangle mesh in a unit square with a rectangular hole, an embedded line loop and a short fixed interior edge. The input geometry consists of 12 nodes and 16 boundary lines. The number of input nodes and boundary lines are specified by calling msh_TriMeshDef(). Each node and boundary line are input using msh_TriMeshSetNode() and msh_TriMeshSetLine() respectively. Note that the embedded line loop (line segments 9 thru 14 connecting points 5, 6, 11 and 12) are defined by six back-to-back line segments. The hard edge (line segments 15 and 16) is specified using back-to-back line segments and disabling splitting during mesh generation using msh_TriMeshSetLineStat(). The default mesh sizing is specified by msh_TriMeshSetParamd(), and local mesh size at node 1 is specified using msh_TriMeshSetNodeSizing(). A possible addition to the local mesh sizing specification would be to compute the length of the hard edge and set that length as the local mesh size at the end nodes of the hard edge. This would tend to create equilateral triangles about the hard edge which would gradually increase in size (depending upon the growth rate) to the default mesh size.

Two varieties of line associations are set. The edge associations will be propagated into the final mesh as element edge entity associations on the generated triangle edges lying on the line. All nodes generated along the line will be given node associations. The line element associations will be propagated into the final mesh as element associations on all triangles bounded by the line and any other input lines.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

#define MAX_NODE 12
#define MAX_LINE 16

/* a square with a rectangular hole and fixed edge */
static Vdouble coords[MAX_NODE][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},  {.3, .3, 0.},   {.7, .3, 0.},
                                      {.3, .7, 0.}, {.7, .7, 0.}, {.8, .8, 0.}, {.91, .9, 0.}, {.15, .15, 0.}, {.85, .15, 0.}};

/* boundary lines */
static Vint lines[MAX_LINE][2] = {{1, 2},
                                  {2, 4},
                                  {4, 3},
                                  {3, 1},
                                  {5, 7},
                                  {7, 8},
                                  {8, 6},
                                  {6, 5},
                                  {11, 5},
                                  {5, 11},
                                  {12, 6},
                                  {6, 12},
                                  {11, 12},
                                  {12, 11},
                                  /* interior edge */
                                  {9, 10},
                                  {10, 9}};

/*----------------------------------------------------------------------
                      Generate a 2D Planar Tri Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_TriMesh* trimesh;
    vis_Connect* connect;

    Vint i, k;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[6];
    Vint aid, num, aids[3];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, MAX_NODE, MAX_LINE, 2);

    /* create connect object to receive mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < MAX_NODE; i++) {
        msh_TriMeshSetNode(trimesh, i + 1, coords[i]);
    }
    /* define lines */
    for (i = 0; i < MAX_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* set line 16 as an undivided edge */
    msh_TriMeshSetLineStat(trimesh, 16, 0);

    /* tag the first line segment */
    msh_TriMeshSetLineAssoc(trimesh, VIS_MISCID, 1, SYS_EDGE, 1001);
    /* elements inside region with boundary edge 10 or 13 */
    msh_TriMeshSetLineAssoc(trimesh, VIS_MISCID1, 10, SYS_ELEM, 2000);
    msh_TriMeshSetLineAssoc(trimesh, VIS_MISCID1, 10, SYS_ELEM, 2002);
    msh_TriMeshSetLineAssoc(trimesh, VIS_MISCID1, 13, SYS_ELEM, 2001);
    /* tag the first node */
    msh_TriMeshSetNodeAssoc(trimesh, VIS_MISCID2, 1, 1002);

    /* generate quadratic elements */
    msh_TriMeshSetParami(trimesh, VIS_MESH_MAXI, 3);
    msh_TriMeshSetParami(trimesh, TRIMESH_SMOOTH, 1);

    /* set mesh size */
    msh_TriMeshSetParamd(trimesh, VIS_MESH_EDGELENGTH, .1);

    /* refine about node 1 */
    msh_TriMeshSetNodeSizing(trimesh, 1, .02);

    msh_TriMeshWrite(trimesh, SYS_ASCII, "exam43.tri");
    /* generate */
    msh_TriMeshGenerate(trimesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    /* check for nodes generated on tagged line segment */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        printf("id= %d  x= %13.10f, y= %13.10f, z= %13.10f\n", i, x[0], x[1], x[2]);
        vis_ConnectNodeAssoc(connect, VIS_MISCID2, 1, &i, &aid);
        if (aid) {
            printf("   VIS_MISCID2= %d\n", aid);
        }
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5]);
        for (k = 1; k <= 3; k++) {
            vis_ConnectElemEntAssoc(connect, VIS_MISCID, SYS_EDGE, i, k, &aid);
            if (aid) {
                printf(" edge %d,   VIS_MISCID= %d\n", k, aid);
            }
        }
        vis_ConnectAllElemAssoc(connect, VIS_MISCID1, i, &num, aids);
        for (k = 0; k < num; k++) {
            if (aids[k]) {
                printf(" elem,   VIS_MISCID1= %d\n", aids[k]);
            }
        }
    }
    /* write out NASTRAN bulk data file */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TriMeshEnd(trimesh);
    return 0;
}

6.8. Example 43a, Generate a 2D Planar Tri Mesh with Parabolic Lines

This example illustrates generating a triangle mesh in a unit square with a circular hole. The input boundary segments are parabolic. Note that in the output mesh all segment endpoint node numbers are retained. In general the midside node numbers will be changed.

#include <stdio.h>
#include <math.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

#define SQUARE_NODE 8
#define SQUARE_LINE 4

/* outside square */
static Vdouble coords[SQUARE_NODE][3] = {{0., 0., 0.}, {1., 0., 0.}, {1., 1., 0.}, {0., 1., 0.},
                                         {.5, 0., 0.}, {1., .5, 0.}, {.5, 1., 0.}, {0., .5, 0.}};

/* square boundary lines */
static Vint lines[SQUARE_LINE][3] = {{1, 5, 2}, {2, 6, 3}, {3, 7, 4}, {4, 8, 1}};

/*----------------------------------------------------------------------
                      Generate a 2D Planar Tri Mesh with Parabolic Lines
----------------------------------------------------------------------*/
int
main()
{
    msh_TriMesh* trimesh;
    vis_Connect* connect;

    Vint i;
    Vint numpts, numlns, lix[3], numcirclelines;
    Vint numnp, numel;
    Vdouble ang, rad, xc[3], x[3];
    Vint nix, ix[6];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    numcirclelines = 8;
    numpts = SQUARE_NODE + 2 * numcirclelines;
    numlns = SQUARE_LINE + numcirclelines;
    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, numpts, numlns, 3);

    /* create connect object to receive mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < SQUARE_NODE; i++) {
        msh_TriMeshSetNode(trimesh, i + 1, coords[i]);
    }
    /* generate circle nodes */
    rad = .25;
    ang = 0.;
    xc[0] = .5;
    xc[1] = .5;
    xc[2] = 0.;
    for (i = 0; i < 2 * numcirclelines; i++) {
        x[0] = rad * cos(ang) + xc[0];
        x[1] = rad * sin(ang) + xc[1];
        x[2] = 0. + xc[2];
        ang += 3.14159 / numcirclelines;
        msh_TriMeshSetNode(trimesh, SQUARE_NODE + i + 1, x);
    }
    /* define lines */
    for (i = 0; i < SQUARE_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* generate circle lines */
    for (i = 0; i < numcirclelines; i++) {
        if (i == numcirclelines - 1) {
            lix[0] = SQUARE_NODE + 1;
        }
        else {
            lix[0] = SQUARE_NODE + 2 * i + 3;
        }
        lix[1] = SQUARE_NODE + 2 * i + 2;
        lix[2] = SQUARE_NODE + 2 * i + 1;
        msh_TriMeshSetLine(trimesh, SQUARE_LINE + i + 1, lix);
    }
    /* generate quadratic elements */
    msh_TriMeshSetParami(trimesh, VIS_MESH_MAXI, 3);
    msh_TriMeshSetParami(trimesh, TRIMESH_SMOOTH, 1);

    /* set mesh size */
    msh_TriMeshSetParamd(trimesh, VIS_MESH_EDGELENGTH, .05);

    /* write debugging file */
    msh_TriMeshWrite(trimesh, SYS_ASCII, "exam43a.tri");
    /* generate */
    msh_TriMeshGenerate(trimesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        printf("id= %d  x= %f, y= %f, z= %f\n", i, x[0], x[1], x[2]);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5]);
    }
    /* write out NASTRAN bulk data file */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43a.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TriMeshEnd(trimesh);
    return 0;
}

6.9. Example 43b, Generate a 2D Planar Tri Mesh with Sizing Function

This example is an extension of Example 43 to illustrate using user-defined isotropic and anisotropic sizing functions to affect the sizing of the generated triangular mesh. Any sizing function set by the user will override any other sizing specifications set using functions such as msh_TriMeshSetParamd() or msh_TriMeshSetNodeSizing(). In addition, any anisotropic sizing function set by the user takes precedence over an isotropic sizing function which may also have been set. In this example the anisotropic sizing function, s_aniso, is set using the function msh_TriMeshSetFunction(). The anisotropic sizing function returns three orthogonal vectors, each vector is scaled to the desired size in that direction. The generated triangle aspect ratios will be 4 and inclined at an angle of 45 degrees.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

#define MAX_NODE 8
#define MAX_LINE 8

/* a square with a rectangular hole */
static Vdouble coords[MAX_NODE][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                                      {.2, .2, 0.}, {.8, .2, 0.}, {.2, .8, 0.}, {.8, .8, 0.}};

static Vint lines[MAX_LINE][2] = {{1, 2}, {2, 4}, {4, 3}, {3, 1}, {5, 7}, {7, 8}, {8, 6}, {6, 5}};

/* isotropic size function */
static void
s_iso(msh_TriMesh* trimesh, Vobject* usrobj, Vdouble x[3], Vdouble* s)
{
    printf("s_iso called\n");
    /* linear variation in y direction */
    *s = .05 + .05 * x[1];
    printf("x= %e %e %e, s= %e\n", x[0], x[1], x[2], *s);
}
/* anisotropic size function */
static void
s_aniso(msh_TriMesh* trimesh, Vobject* usrobj, Vdouble x[3], Vdouble s[3][3])
{
    Vdouble s1, s2, s3;
    printf("s_aniso called\n");
    /* 4 to 1 aspect ratio at 45 degree angle */
    s1 = .025;
    s[0][0] = s1 * .707;
    s[0][1] = s1 * .707;
    s[0][2] = 0.;

    s2 = .1;
    s[1][0] = s2 * -.707;
    s[1][1] = s2 * .707;
    s[1][2] = 0.;
    /* set the out of plane direction size to unity */
    s3 = 1.;
    s[2][0] = 0.;
    s[2][1] = 0.;
    s[2][2] = s3 * 1.;
    printf("x= %e %e %e, s1= %e, s2= %e\n", x[0], x[1], x[2], s1, s2);
}

/*----------------------------------------------------------------------
                      Generate a 2D Planar Tri Mesh with Sizing Function
----------------------------------------------------------------------*/
int
main()
{
    msh_TriMesh* trimesh;
    vis_Connect* connect;

    Vint i, k;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[6];
    Vint aid;
    Vint anisoflag;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, MAX_NODE, MAX_LINE, 2);

    /* create connect object to receive mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < MAX_NODE; i++) {
        msh_TriMeshSetNode(trimesh, i + 1, coords[i]);
    }
    /* define lines */
    for (i = 0; i < MAX_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* tag the first line segment */
    msh_TriMeshSetLineAssoc(trimesh, VIS_MISCID, 1, SYS_EDGE, 1001);

    /* generate quadratic elements */
    msh_TriMeshSetParami(trimesh, VIS_MESH_MAXI, 3);
    msh_TriMeshSetParami(trimesh, TRIMESH_SMOOTH, VIS_ON);

    /* use zero for isotropic sizing, one for anisotropic */
    anisoflag = 1;
    /* set sizing function */
    if (anisoflag == 0) {
        msh_TriMeshSetFunction(trimesh, TRIMESH_FUN_SIZING, (Vfunc*)s_iso, NULL);
    }
    else {
        msh_TriMeshSetFunction(trimesh, TRIMESH_FUN_ASIZING, (Vfunc*)s_aniso, NULL);
    }

    msh_TriMeshWrite(trimesh, SYS_ASCII, "exam43b.tri");
    /* generate */
    msh_TriMeshGenerate(trimesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
    }
    /* print element information */
    /* check for edges generated on tagged line segment */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5]);
        for (k = 1; k <= 3; k++) {
            vis_ConnectElemEntAssoc(connect, VIS_MISCID, SYS_EDGE, i, k, &aid);
            if (aid) {
                printf(" edge %d,   VIS_MISCID= %d\n", k, aid);
            }
        }
    }
    /* write out NASTRAN bulk data file */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43b.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TriMeshEnd(trimesh);
    return 0;
}
../_images/vistools-exam43b.gif

Figure 28-43b, Generated Anisotropic Triangle Mesh

6.10. Example 43c, Generate a 2D Planar Quad Mesh

This example illustrates using TriMesh to generate a planar triangulation which can then be used as the surface geometry for a subsequent mesh generated using SurfMesh.

The planar geometry consists of a unit square with a fixed interior edge. The desired goal of TriMesh is to generate a triangulation of the input geometry without generating any additional nodes on the boundary or interior. Do this by calling msh_TriMeshSetParami() and disable the parameters VIS_MESH_BOUNDREFINE and VIS_MESH_INTERREFINE. It is useful to know which edges of the generated triangles lie on input boundary lines. Use msh_TriMeshSetParami() and set line flag association to VIS_MISCID1. This will enable generation of the element association consisting of bitwise flags of triangle edges which lie on input boundary lines. The triangle mesh is generated using msh_TriMeshGenerate(). The generated triangle mesh is illustrated in the first figure below. Note that the number of nodes in the output Connect object, numnp is equal to the number of nodes (MAX_NODE) input to TriMesh. These nodes are set in SurfMesh using msh_SurfMeshSetPoint(). The triangles are set using msh_SurfMeshSetTri(). The edge flags are queried using vis_ConnectElemAssoc() with association type VIS_MISCID1. The edge flags are parsed bit by bit and the corresponding triangle edges are marked as preserved. Use msh_SurfMeshSetParami() and msh_SurfMeshSetParamd() to set meshing parameters. In this case linear quadrilateral elements are generated with a mesh size of .1. Note that the preserved edges appear in the final mesh. The generated quadrilateral mesh is illustrated in the second figure below.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

#define MAX_NODE 6
#define MAX_LINE 6

/* a square with a fixed interior edge */
static Vdouble coords[MAX_NODE][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.}, {.2, .3, 0.}, {.7, .8, 0.}};

/* boundary lines */
static Vint lines[MAX_LINE][2] = {{1, 2},
                                  {2, 4},
                                  {4, 3},
                                  {3, 1},
                                  /* interior edge */
                                  {5, 6},
                                  {6, 5}};

/*----------------------------------------------------------------------
                      Generate a 2D Planar Quad Surface Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_TriMesh* trimesh;
    vis_Connect *connect, *connectsurf;
    msh_SurfMesh* surfmesh;

    Vint i, j;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[6], efl[3];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, MAX_NODE, MAX_LINE, 2);

    /* create connect object to receive mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < MAX_NODE; i++) {
        msh_TriMeshSetNode(trimesh, i + 1, coords[i]);
    }
    /* define lines */
    for (i = 0; i < MAX_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* generate boundary triangulation only */
    msh_TriMeshSetParami(trimesh, VIS_MESH_BOUNDREFINE, VIS_OFF);
    msh_TriMeshSetParami(trimesh, VIS_MESH_INTERREFINE, VIS_OFF);
    /* element associations with line flags */
    msh_TriMeshSetParami(trimesh, TRIMESH_LINEFLAG, VIS_MISCID1);

    /* generate */
    msh_TriMeshWrite(trimesh, SYS_ASCII, "exam43c.tri");
    msh_TriMeshGenerate(trimesh, connect);
    /* delete */
    msh_TriMeshEnd(trimesh);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("TriMesh, numnp= %d, numel= %d\n", numnp, numel);

    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numnp, numel);

    /* create connect object to receive surf mesh */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* load nodes and preserve them */
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        msh_SurfMeshSetPoint(surfmesh, i, x, 1);
    }
    /* load elements */
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        for (j = 0; j < 3; j++) {
            vis_ConnectElemEntAssoc(connect, VIS_MISCID1, SYS_EDGE, i, j + 1, &efl[j]);
        }
        msh_SurfMeshSetTri(surfmesh, i, ix, efl);
    }
    /* delete */
    vis_ConnectEnd(connect);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_SHAPE, VIS_SHAPEQUAD);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .1);

    /* generate */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam43c.srf");
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    /* print generated nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numnp);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numel);
    printf("SurfMesh, numnp= %d, numel= %d\n", numnp, numel);

    /* write out NASTRAN bulk data file */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam43c.bdf");

    /* end objects */
    vis_ConnectEnd(connectsurf);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}
../_images/vistools-exam43c1.gif

Figure 28-43c1, Generated Triangle Mesh

../_images/vistools-exam43c2.gif

Figure 28-43c2, Generated Quadrilateral Mesh

6.11. Example 43d, Refine a 2D Planar Tri Mesh

This example illustrates using TriMesh to generate and subsequently refine a planar triangulation. The original mesh is generated with an edge length of .2. The refined mesh sets the edge length to .1 for the elements with any generated node in the upper right corner of the original mesh.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

#define MAX_NODE 8
#define MAX_LINE 8

/* a square with a rectangular hole */
static Vdouble coords[MAX_NODE][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                                      {.2, .2, 0.}, {.8, .2, 0.}, {.2, .8, 0.}, {.8, .8, 0.}};

static Vint lines[MAX_LINE][2] = {{1, 2}, {2, 4}, {4, 3}, {3, 1}, {5, 7}, {7, 8}, {8, 6}, {6, 5}};

/*----------------------------------------------------------------------
                      Refine a 2D Planar Tri Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_TriMesh* trimesh;
    vis_Connect* connect;
    vis_State* state;

    Vint i;
    Vint numnp, numel;
    Vdouble x[3], v;
    Vint nix, ix[6];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, MAX_NODE, MAX_LINE, 2);

    /* create connect object to receive mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < MAX_NODE; i++) {
        msh_TriMeshSetNode(trimesh, i + 1, coords[i]);
    }
    /* define lines */
    for (i = 0; i < MAX_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* generate quadratic elements */
    msh_TriMeshSetParami(trimesh, VIS_MESH_MAXI, 3);
    msh_TriMeshSetParami(trimesh, TRIMESH_SMOOTH, VIS_ON);
    msh_TriMeshSetParamd(trimesh, VIS_MESH_EDGELENGTH, .2);

    /* generate initial mesh */
    msh_TriMeshGenerate(trimesh, connect);
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("Generated original mesh\n");
    printf("numnp= %d\n", numnp);
    printf("numel= %d\n", numel);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43d.bdf");

    /* create State object with new element edge lengths */
    state = vis_StateBegin();
    vis_StateDef(state, numel, SYS_ELEM, SYS_NONE, VIS_SCALAR);
    /* refine elements in upper right corner */
    for (i = 1; i <= numel; ++i) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        vis_ConnectCoordsdv(connect, 1, &ix[0], (Vdouble(*)[3])x);
        if (x[0] >= .8 && x[1] >= .8) {
            v = .1;
            vis_StateSetDatadv(state, i, &v);
        }
    }
    /* clear Connect object */
    vis_ConnectDef(connect, 0, 0);
    /* generate refined mesh */
    msh_TriMeshRefine(trimesh, state, connect);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43d.ref.bdf");

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("Generated refined mesh\n");
    printf("numnp= %d\n", numnp);
    printf("numel= %d\n", numel);

    /* print node information */
    /* check for nodes generated on tagged line segment */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5]);
    }

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TriMeshEnd(trimesh);
    vis_StateEnd(state);
    return 0;
}

6.12. Example 43e, Refine a 2D Planar Tri Mesh as an Orphan Mesh

This example is a modification of Example 43d using TriMesh to generate a mesh but then for purposes of illustration, the original mesh is erased using TriMeshDef and the original mesh is reentered as an orphan mesh for subsequent refinement. The original mesh is generated with an edge length of .2. The refined mesh sets the edge length to .1 for the elements with any generated node in the upper right corner of the original mesh.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

#define MAX_NODE 8
#define MAX_LINE 8

/* a square with a rectangular hole */
static Vdouble coords[MAX_NODE][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                                      {.2, .2, 0.}, {.8, .2, 0.}, {.2, .8, 0.}, {.8, .8, 0.}};

static Vint lines[MAX_LINE][2] = {{1, 2}, {2, 4}, {4, 3}, {3, 1}, {5, 7}, {7, 8}, {8, 6}, {6, 5}};

/*----------------------------------------------------------------------
                      Refine a 2D Planar Tri Mesh as on Orphan Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_TriMesh* trimesh;
    vis_Connect* connect;
    vis_State* state;

    Vint i;
    Vint numnp, numel;
    Vdouble x[3], v;
    Vint nix, ix[6];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, MAX_NODE, MAX_LINE, 2);

    /* create connect object to receive mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < MAX_NODE; i++) {
        msh_TriMeshSetNode(trimesh, i + 1, coords[i]);
    }
    /* define lines */
    for (i = 0; i < MAX_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* generate quadratic elements */
    msh_TriMeshSetParami(trimesh, VIS_MESH_MAXI, 3);
    msh_TriMeshSetParami(trimesh, TRIMESH_SMOOTH, VIS_ON);
    msh_TriMeshSetParamd(trimesh, VIS_MESH_EDGELENGTH, .2);

    /* generate initial mesh */
    msh_TriMeshGenerate(trimesh, connect);
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("Generated original mesh\n");
    printf("numnp= %d\n", numnp);
    printf("numel= %d\n", numel);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43e.bdf");

    /* redefine trimesh to setup as taking orphan mesh */
    msh_TriMeshDef(trimesh, numnp, MAX_LINE, 2);
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, &x);
        msh_TriMeshSetNode(trimesh, i, x);
    }
    /* define lines */
    for (i = 0; i < MAX_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* set trimesh as background mesh */
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        msh_TriMeshSetElem(trimesh, i, SYS_SHAPETRI, 3, ix);
    }
    /* create State object with new element edge lengths */
    state = vis_StateBegin();
    vis_StateDef(state, numel, SYS_ELEM, SYS_NONE, VIS_SCALAR);
    /* refine elements in upper right corner */
    for (i = 1; i <= numel; ++i) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        vis_ConnectCoordsdv(connect, 1, &ix[0], (Vdouble(*)[3])x);
        if (x[0] >= .8 && x[1] >= .8) {
            v = .1;
            vis_StateSetDatadv(state, i, &v);
        }
    }
    /* clear Connect object */
    vis_ConnectDef(connect, 0, 0);
    /* generate refined mesh */
    msh_TriMeshRefine(trimesh, state, connect);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43e_ref.bdf");

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("Generated refined mesh\n");
    printf("numnp= %d\n", numnp);
    printf("numel= %d\n", numel);

    /* print node information */
    /* check for nodes generated on tagged line segment */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5]);
    }

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TriMeshEnd(trimesh);
    vis_StateEnd(state);
    return 0;
}

6.13. Example 43f, Generate a 2D Planar Tri Mesh with Sizing

This example illustrates using node, line and region sizing to control mesh size. The domain consists of two side-by-side unit squares. An overall target edge length is set to .2. An unconnected point is placed in the center of the left region (region 1) and a size of .01 is specified there using msh_TriMeshSetNodeSizing(). A line sizing of .05 is placed on the left edge of region 1 using msh_TriMeshSetLineSizing() using an entity type of SYS_EDGE. A region sizing of .05 is defined for region 2 using the left edge of region 2. The function msh_TriMeshSetLineSizing() with an entity type of SYS_ELEM is used to set the region sizing. Note that the size for a region is set to the minimum of all region (SYS_ELEM) sizes set on lines which bound the region. The default growth rate of 1.5 is used to transition between regions of various sizes.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

#define MAX_NODE 7
#define MAX_LINE 8

/* 2 x 1 rectangle divided in the middle into two regions */
static Vdouble coords[MAX_NODE][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                                      {2., 0., 0.}, {2., 1., 0.}, {.5, .5, 0.}};

/* boundary lines of two regions */
static Vint lines[MAX_LINE][2] = {{1, 2}, {2, 4}, {4, 3}, {3, 1}, {2, 5}, {5, 6}, {6, 4}, {4, 2}};

/*----------------------------------------------------------------------
                      Generate a 2D Planar Tri Mesh with Sizing
----------------------------------------------------------------------*/
int
main()
{
    msh_TriMesh* trimesh;
    vis_Connect* connect;
    Vint i;
    Vint numnp, numel;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, MAX_NODE, MAX_LINE, 2);

    /* create connect object to receive mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < MAX_NODE; i++) {
        msh_TriMeshSetNode(trimesh, i + 1, coords[i]);
    }
    /* define lines */
    for (i = 0; i < MAX_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* tag the two regions */
    msh_TriMeshSetLineAssoc(trimesh, VIS_GEOBODY, 1, SYS_ELEM, 1);
    msh_TriMeshSetLineAssoc(trimesh, VIS_GEOBODY, 5, SYS_ELEM, 2);

    /* generate quadratic elements */
    msh_TriMeshSetParami(trimesh, VIS_MESH_MAXI, 3);
    msh_TriMeshSetParami(trimesh, TRIMESH_SMOOTH, 1);
    /* allow unconnected node */
    msh_TriMeshSetParami(trimesh, TRIMESH_UNCONNECT, SYS_ON);

    /* set mesh size */
    msh_TriMeshSetParamd(trimesh, VIS_MESH_EDGELENGTH, .2);

    /* refine about node 7 in region 1 */
    msh_TriMeshSetNodeSizing(trimesh, 7, .01);

    /* refine edge 4 in region 1 */
    msh_TriMeshSetLineSizing(trimesh, 4, SYS_EDGE, .05);
    /* refine region 2 */
    msh_TriMeshSetLineSizing(trimesh, 5, SYS_ELEM, .05);

    msh_TriMeshWrite(trimesh, SYS_ASCII, "exam43f.tri");
    /* generate */
    msh_TriMeshGenerate(trimesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* write out NASTRAN bulk data file */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43f.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TriMeshEnd(trimesh);
    return 0;
}

6.14. Example 43g, Generate a 2D Planar Tri Mesh with Interior Sizing

This example is an alteration of Example 43f and illustrates specifying interior sizing. A boundary depth size is specified using msh_TriMeshSetLineDepthSizing(). This function allows a depth and size to be set for each boundary line. This feature is useful for generating isotropic boundary layers. In addition, a user specified element size is defined with a simple geometry shape in the interior of the domain using msh_TriMeshSetGeomSizing(). In this case a .05 by .15 by .25 ellipsoid. Note that only the x and y extents of the geometry are used for the sizing region.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"
#define MAX_NODE 6
#define MAX_LINE 8

/* 2 x 1 rectangle divided in the middle into two regions */
static Vdouble coords[MAX_NODE][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.}, {2., 0., 0.}, {2., 1., 0.}};

/* boundary lines of two regions */
static Vint lines[MAX_LINE][2] = {{1, 2}, {2, 4}, {4, 3}, {3, 1}, {2, 5}, {5, 6}, {6, 4}, {4, 2}};

/*----------------------------------------------------------------------
                      Generate a 2D Planar Tri Mesh with Interior Sizing
----------------------------------------------------------------------*/
int
main()
{
    msh_TriMesh* trimesh;
    vis_Connect* connect;
    Vint i;
    Vint numnp, numel;
    Vdouble xo[3], xa[3], xb[3], d[3];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, MAX_NODE, MAX_LINE, 2);

    /* create connect object to receive mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < MAX_NODE; i++) {
        msh_TriMeshSetNode(trimesh, i + 1, coords[i]);
    }
    /* define lines */
    for (i = 0; i < MAX_LINE; i++) {
        msh_TriMeshSetLine(trimesh, i + 1, lines[i]);
    }
    /* define line depth sizing in region 2 */
    for (i = 4; i < MAX_LINE; i++) {
        msh_TriMeshSetLineDepthSizing(trimesh, i + 1, .1, .02);
    }
    /* tag the two regions */
    msh_TriMeshSetLineAssoc(trimesh, VIS_GEOBODY, 1, SYS_ELEM, 1);
    msh_TriMeshSetLineAssoc(trimesh, VIS_GEOBODY, 5, SYS_ELEM, 2);

    /* generate quadratic elements */
    msh_TriMeshSetParami(trimesh, VIS_MESH_MAXI, 3);
    msh_TriMeshSetParami(trimesh, TRIMESH_SMOOTH, 1);

    /* set mesh size */
    msh_TriMeshSetParamd(trimesh, VIS_MESH_EDGELENGTH, .2);

    /* interior ellipsoidal shape for sizing */
    /* positioned in center of cube */
    xo[0] = .5;
    xo[1] = .5;
    xo[2] = 0.;
    /* oriented along x axis and at 45 degrees in y-z plane */
    xa[0] = 1.0;
    xa[1] = .5;
    xa[2] = .5;
    xb[0] = .5;
    xb[1] = 1.0;
    xb[2] = .5;
    /* lengths of ellipsoid along each axis */
    d[0] = .05;
    d[1] = .15;
    d[2] = .25;
    msh_TriMeshSetGeomSizing(trimesh, VIS_MESH_GEOMSPHERE, xo, xa, xb, d, 0.02);

    msh_TriMeshWrite(trimesh, SYS_ASCII, "exam43g.tri");
    /* generate */
    msh_TriMeshGenerate(trimesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* write out NASTRAN bulk data file */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam43g.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TriMeshEnd(trimesh);
    return 0;
}
../_images/vistools-exam43g.gif

Figure 28-48f, Generated Planar Tri Mesh with Interor Sizing

6.15. Example 44, Generate a 3D Volume Tet Mesh

This example illustrates generating a simple tetrahedral mesh in the interior of a cube using the TetMesh module. The input surface geometry consists of the 8 corner nodes of the cube and 12 triangular surface facets, 2 per face of the cube. The connectivity of the surface triangles is such that the normal to the triangles, determined by the right hand rule, points into the volume to be meshed. The number of input nodes and triangles are specified by calling msh_TetMeshDef(), then each node and triangle is input using msh_TetMeshSetNode() and msh_TetMeshSetTri() respectively.

The nodes and finite elements are actually generated by calling msh_TetMeshGenerate(). In this simple case no internal nodes are generated. The generated nodes and elements are entered into the Connect object which is passed in the argument list. Lastly, the generated nodes and elements are printed.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                               {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};

/*----------------------------------------------------------------------
                      Generate a 3D Volume Tet Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_TetMesh* tetmesh;
    vis_Connect* connect;

    Vint i, j;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[4];
    Vint num, aid, aids[10];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create tetmesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, 8, 12, 2);

    /* create connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < 8; i++) {
        msh_TetMeshSetNode(tetmesh, i + 1, coords[i]);
    }
    /* define tris */
    for (i = 0; i < 12; i++) {
        msh_TetMeshSetTri(tetmesh, i + 1, tris[i]);
    }
    /* define a node association on node 2 */
    msh_TetMeshSetNodeAssoc(tetmesh, VIS_GEOVERT, 2, 1000);
    /* define an element edge association on tri 4, edge 2 */
    msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOEDGE, 4, SYS_EDGE, 2, 10);
    /* define an element face association on tri 3 */
    msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOFACE, 3, SYS_FACE, 1, 100);
    /* define an element association on tri 3 */
    msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOBODY, 3, SYS_ELEM, 0, 200);
    msh_TetMeshSetTriAssoc(tetmesh, VIS_PROPID, 3, SYS_ELEM, 0, 1);
    /* set edgelength */
    msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, 1.);

    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam44.tet");
    /* generate */
    msh_TetMeshGenerate(tetmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        printf("id= %d  x= %f, y= %f, z= %f\n", i, x[0], x[1], x[2]);
    }
    /* node associations */
    for (i = 1; i <= numnp; i++) {
        vis_ConnectAllNodeAssoc(connect, VIS_GEOVERT, i, &num, aids);
        for (j = 0; j < num; j++) {
            if (aids[j] == 0)
                continue;
            printf("id= %d  aid[%d]= %d\n", i, j, aids[j]);
        }
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3]);
    }
    /* element entity associations */
    for (i = 1; i <= numel; i++) {
        /* tet edges */
        for (j = 1; j <= 6; j++) {
            vis_ConnectElemEntAssoc(connect, VIS_GEOEDGE, SYS_EDGE, i, j, &aid);
            if (aid == 0)
                continue;
            printf("id= %d edge= %d, aid= %d\n", i, j, aid);
        }
        /* tet faces */
        for (j = 1; j <= 4; j++) {
            vis_ConnectElemEntAssoc(connect, VIS_GEOFACE, SYS_FACE, i, j, &aid);
            if (aid == 0)
                continue;
            printf("id= %d face= %d, aid= %d\n", i, j, aid);
        }
    }
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam44.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TetMeshEnd(tetmesh);
    return 0;
}

6.16. Example 44a, Generate a 3D Volume Tet Mesh with Interior Features

This example is an extension of Example 44 with the addition of an interior void, baffle face, floating face, floating edge and node. By default, a node point which been introduced in the interior which is not connected to any surface triangle, interior face or edge is reported as an error. The parameter TETMESH_UNCONNECT must be enabled to allow unconnected nodes to be inserted in the final mesh.

A monitor function has been installed to follow the progress of the meshing process. The function msh_TetMeshGetInteger() is called with type VIS_MESH_PROGRESS within the monitor function to query and print the current state of the meshing process.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* fixed node points */
static Vdouble coords[25][3] = {{0., 0., 0.},
                                {5., 0., 0.},
                                {0., 3., 0.},
                                {5., 3., 0.},
                                {0., 0., 3.},
                                {5., 0., 3.},
                                {0., 3., 3.},
                                {5., 3., 3.},
                                /* unit cube void */
                                {1., 1., 1.},
                                {2., 1., 1.},
                                {1., 2., 1.},
                                {2., 2., 1.},
                                {1., 1., 2.},
                                {2., 1., 2.},
                                {1., 2., 2.},
                                {2., 2., 2.},
                                /* interior baffle face */
                                {3., 1., 1.},
                                {3., 1., 2.},
                                /* interior edge */
                                {4., 2., 1.},
                                {4., 2., 2.},
                                /* interior point */
                                {3., 2., 1.},
                                /* interior floating face */
                                {3.5, 1., 1.},
                                {3.5, 1., 2.},
                                {4.5, 1., 1.},
                                {4.5, 1., 2.}};

/* surface triangles */
static Vint tris[32][3] = {{1, 5, 3},
                           {3, 5, 7},
                           {4, 6, 2},
                           {8, 6, 4},
                           {1, 2, 5},
                           {5, 2, 6},
                           {7, 4, 3},
                           {8, 4, 7},
                           {1, 4, 2},
                           {4, 1, 3},
                           {5, 8, 7},
                           {5, 6, 8},
                           /* unit cube void */
                           {9, 11, 13},
                           {11, 15, 13},
                           {12, 10, 14},
                           {16, 12, 14},
                           {9, 13, 10},
                           {13, 14, 10},
                           {15, 11, 12},
                           {16, 15, 12},
                           {9, 10, 12},
                           {12, 11, 9},
                           {13, 15, 16},
                           {13, 16, 14},
                           /* interior double backed triangles */
                           {14, 18, 10},
                           {14, 10, 18},
                           {10, 18, 17},
                           {10, 17, 18},
                           /* interior double backed floating triangles */
                           {22, 25, 24},
                           {22, 24, 25},
                           {23, 22, 25},
                           {23, 25, 22}};

/* interior edge */
static Vint inedge[2] = {19, 20};

/*----------------------------------------------------------------------
                      monitor function
----------------------------------------------------------------------*/
static void
monitor(msh_TetMesh* tetmesh, Vobject* usrobj)
{
    int iparam[4];

    printf("monitor called\n");
    /* query and print progress */
    msh_TetMeshGetInteger(tetmesh, VIS_MESH_PROGRESS, iparam);
    printf("phase=   %d\n", iparam[0]);
    printf("numnp=   %d\n", iparam[1]);
    printf("numel=   %d\n", iparam[2]);
    printf("percent= %d\n", iparam[3]);
}

/*----------------------------------------------------------------------
                      Generate a 3D Volume Tet Mesh with Interior Features
----------------------------------------------------------------------*/
int
main()
{
    msh_TetMesh* tetmesh;
    vis_Connect* connect;

    Vint i;
    Vint numnode, numtris;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[4];

    /* number of nodes */
    numnode = 25;
    /* number of triangles */
    numtris = 32;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create tetmesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numnode, numtris, 2);

    /* create connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < numnode; i++) {
        msh_TetMeshSetNode(tetmesh, i + 1, coords[i]);
    }
    /* define tris */
    for (i = 0; i < numtris; i++) {
        msh_TetMeshSetTri(tetmesh, i + 1, tris[i]);
    }
    /* define interior edge */
    msh_TetMeshSetEdge(tetmesh, 1, inedge);

    /* allow unconnected nodes */
    msh_TetMeshSetParami(tetmesh, TETMESH_UNCONNECT, VIS_ON);

    /* set monitor function */
    msh_TetMeshSetFunction(tetmesh, TETMESH_FUN_MONITOR, (Vfunc*)monitor, NULL);

    /* generate */
    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam44a.tet");
    msh_TetMeshGenerate(tetmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        printf("id= %d  x= %f, y= %f, z= %f\n", i, x[0], x[1], x[2]);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3]);
    }
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam44a.bdf");
    /* end objects */
    vis_ConnectEnd(connect);
    msh_TetMeshEnd(tetmesh);
    return 0;
}

6.17. Example 44b, Generate a 3D Volume Tet Mesh of a Simple Cube

This example is similar to Example 44. However, it eliminates all geometric complexities, meshes a simple cube, and labels vertices, edges, and faces that can be later retrieved on the generated mesh.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                               {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};

/*----------------------------------------------------------------------
                      Generate a 3D Volume Tet Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_TetMesh* tetmesh;
    vis_Connect* connect;
    vis_State* state;

    Vint i, j;
    Vint numnp, numel;
    Vdouble x[3], v;
    Vint nix, ix[4];
    Vint num, aid, aids[10];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create tetmesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, 8, 12, 2);

    /* create connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < 8; i++) {
        msh_TetMeshSetNode(tetmesh, i + 1, coords[i]);
    }
    /* define tris */
    for (i = 0; i < 12; i++) {
        msh_TetMeshSetTri(tetmesh, i + 1, tris[i]);
    }
    /* define a node association on node 2 */
    msh_TetMeshSetNodeAssoc(tetmesh, VIS_GEOVERT, 2, 1000);
    /* define an element edge association on tri 4, edge 2 */
    msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOEDGE, 4, SYS_EDGE, 2, 10);
    /* define an element face association on tri 3 */
    msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOFACE, 3, SYS_FACE, 1, 100);

    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam44b.tet");
    /* generate */
    msh_TetMeshGenerate(tetmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        printf("id= %d  x= %f, y= %f, z= %f\n", i, x[0], x[1], x[2]);
    }
    /* node associations */
    for (i = 1; i <= numnp; i++) {
        vis_ConnectAllNodeAssoc(connect, VIS_GEOVERT, i, &num, aids);
        for (j = 0; j < num; j++) {
            if (aids[j] == 0)
                continue;
            printf("id= %d  aid[%d]= %d\n", i, j, aids[j]);
        }
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d %d\n", i, ix[0], ix[1], ix[2], ix[3]);
    }
    /* element entity associations */
    for (i = 1; i <= numel; i++) {
        /* tet edges */
        for (j = 1; j <= 6; j++) {
            vis_ConnectElemEntAssoc(connect, VIS_GEOEDGE, SYS_EDGE, i, j, &aid);
            if (aid == 0)
                continue;
            printf("id= %d edge= %d, aid= %d\n", i, j, aid);
        }
        /* tet faces */
        for (j = 1; j <= 4; j++) {
            vis_ConnectElemEntAssoc(connect, VIS_GEOFACE, SYS_FACE, i, j, &aid);
            if (aid == 0)
                continue;
            printf("id= %d face= %d, aid= %d\n", i, j, aid);
        }
    }
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam44b.bdf");

    state = vis_StateBegin();
    vis_StateDef(state, numel, SYS_ELEM, SYS_NONE, VIS_SCALAR);
    for (i = 1; i <= numel; ++i) {
        v = .5;
        vis_StateSetDatadv(state, i, &v);
    }
    vis_StateWrite(state, NULL, SYS_ASCII, "exam44b.tet.sta1");
    vis_ConnectDef(connect, 0, 0);
    msh_TetMeshRefine(tetmesh, state, connect);
    vis_StateEnd(state);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam44b.ref.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_TetMeshEnd(tetmesh);
    return 0;
}

6.18. Example 44c, Refine a 3D Volume Tet Mesh as an Orphan Mesh

This example extends Example 44b. Once an original tetrahedron mesh is generated its origin is erased by re-Defing the TetMesh object. The original mesh is specified using msh_TetMeshSetElem(). All edges in the cube are assigned a VIS_GEOEDGE association to ensure that they are preserved during mesh refinement.

A uniform new mesh size of 0.25 is employed throughout the volume, as this constant value is set in the State object required for the refinement process.

#include <stdio.h>
#include <math.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                               {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};

static Vint eassoc[12][3] = {{1, 0, 2},  {0, 3, 4},  {0, 5, 6}, {7, 0, 8}, {9, 0, 0}, {0, 0, 10},
                             {0, 11, 0}, {0, 0, 12}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

static Vint maxi = 2;

/*----------------------------------------------------------------------
      Generate a 3D Volume Tet Mesh and Refine it as an Orphan Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_TetMesh* tetmesh;
    vis_Connect *connect1, *connect2;
    vis_State* state;

    Vint i, j;
    Vint numnp, numel;
    Vdouble x[3], size;
    Vint nix, ix[4];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create tetmesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, 8, 12, maxi);

    /* create connect object */
    connect1 = vis_ConnectBegin();
    vis_ConnectPre(connect1, SYS_DOUBLE);
    vis_ConnectDef(connect1, 0, 0);

    /* define nodes */
    for (i = 0; i < 8; i++) {
        msh_TetMeshSetNode(tetmesh, i + 1, coords[i]);
        msh_TetMeshSetNodeAssoc(tetmesh, VIS_GEOVERT, i + 1, i + 1);
    }
    /* define tris */
    for (i = 0; i < 12; i++) {
        msh_TetMeshSetTri(tetmesh, i + 1, tris[i]);
        for (j = 0; j < 3; ++j) {
            if (eassoc[i][j]) {
                msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOEDGE, i + 1, SYS_EDGE, j + 1, eassoc[i][j]);
            }
        }
    }
    /* generate */
    msh_TetMeshGenerate(tetmesh, connect1);
    vis_ConnectNumber(connect1, SYS_NODE, &numnp);
    vis_ConnectNumber(connect1, SYS_ELEM, &numel);
    printf("Generated original mesh\n");
    printf("numnp= %d\n", numnp);
    printf("numel= %d\n", numel);
    vis_ConnectWrite(connect1, SYS_NASTRAN_BULKDATA, "exam44c_orig.bdf");

    /* redefine tetmesh to setup as taking orphan mesh */
    msh_TetMeshDef(tetmesh, numnp, 12, maxi);

    /* define nodes */
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect1, 1, &i, &x);
        msh_TetMeshSetNode(tetmesh, i, x);
        msh_TetMeshSetNodeAssoc(tetmesh, VIS_GEOVERT, i, i);
    }
    /* define tris */
    for (i = 0; i < 12; i++) {
        msh_TetMeshSetTri(tetmesh, i + 1, tris[i]);
        for (j = 0; j < 3; ++j) {
            if (eassoc[i][j]) {
                msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOEDGE, i + 1, SYS_EDGE, j + 1, eassoc[i][j]);
            }
        }
    }
    /* set tetmesh as background mesh */
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect1, i, &nix, ix);
        msh_TetMeshSetElem(tetmesh, i, SYS_SHAPETET, maxi, ix);
    }
    /* create state object with new sizes */
    state = vis_StateBegin();
    vis_StatePre(state, SYS_DOUBLE);
    vis_StateDef(state, numel, SYS_ELEM, SYS_NONE, VIS_SCALAR);
    size = 0.25;
    for (i = 1; i <= numel; i++) {
        vis_StateSetDatadv(state, i, &size);
    }
    /* refine mesh */
    connect2 = vis_ConnectBegin();
    vis_ConnectPre(connect2, SYS_DOUBLE);
    vis_ConnectDef(connect2, 0, 0);
    msh_TetMeshRefine(tetmesh, state, connect2);
    vis_ConnectNumber(connect2, SYS_NODE, &numnp);
    vis_ConnectNumber(connect2, SYS_ELEM, &numel);
    printf("Generated refined mesh\n");
    printf("numnp= %d\n", numnp);
    printf("numel= %d\n", numel);
    vis_ConnectWrite(connect2, SYS_NASTRAN_BULKDATA, "exam44c_ref.bdf");

    /* end objects */
    vis_ConnectEnd(connect1);
    vis_ConnectEnd(connect2);
    msh_TetMeshEnd(tetmesh);
    vis_StateEnd(state);
    return 0;
}

6.19. Example 44d, Successively Refine a 3D Linear Tet Mesh

Unlike Example 44c, this examples assumes that the initial tetrahedron mesh was generated by the same TetMesh object to be used for mesh refinement. As a result, there is no need to re-enter the boundary data or to specify the original mesh with msh_TetMeshSetElem().

The example also illustrates the ability of the msh_TetMeshRefine() method to be called several times. For simplicity, the uniform mesh size is halved from one refinement step to another.

#include <stdio.h>
#include <math.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                               {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};

static Vint maxi = 2;

static Vint eassoc[12][3] = {{1, 0, 2},  {0, 3, 4},  {0, 5, 6}, {7, 0, 8}, {9, 0, 0}, {0, 0, 10},
                             {0, 11, 0}, {0, 0, 12}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

/*----------------------------------------------------------------------
    Generate a Linear 3D Volume Tet Mesh and Perform Successive Refinements
----------------------------------------------------------------------*/
int
main()
{
    msh_TetMesh* tetmesh;
    vis_Connect *connect1, *connect2, *connecttmp;
    vis_State* state;
    vis_GridFun* gf;

    Vint i, j, iref;
    Vint numel;
    Vdouble size;
    Vchar buffer[80];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create tetmesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, 8, 12, maxi);

    /* create connect object */
    connect1 = vis_ConnectBegin();
    vis_ConnectPre(connect1, SYS_DOUBLE);
    vis_ConnectDef(connect1, 0, 0);

    /* define nodes */
    for (i = 0; i < 8; i++) {
        msh_TetMeshSetNode(tetmesh, i + 1, coords[i]);
        /* define a node association on every vertex */
        msh_TetMeshSetNodeAssoc(tetmesh, VIS_GEOVERT, i + 1, i + 1);
    }
    /* define tris */
    for (i = 0; i < 12; i++) {
        msh_TetMeshSetTri(tetmesh, i + 1, tris[i]);
        for (j = 0; j < 3; ++j) {
            if (eassoc[i][j]) {
                msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOEDGE, i + 1, SYS_EDGE, j + 1, eassoc[i][j]);
            }
        }
    }
    /* define an element edge association on tri 4, edge 2 */

    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam44.tet");
    /* generate */
    msh_TetMeshGenerate(tetmesh, connect1);
    vis_ConnectNumber(connect1, SYS_ELEM, &numel);
    printf("Generated original mesh\n");
    vis_ConnectWrite(connect1, SYS_NASTRAN_BULKDATA, "exam44d_orig.bdf");

    /* create state object with new sizes and refine */
    gf = vis_GridFunBegin();
    state = vis_StateBegin();
    vis_StateSetObject(state, VIS_GRIDFUN, (Vobject*)gf);

    connect2 = vis_ConnectBegin();
    vis_ConnectPre(connect2, SYS_DOUBLE);
    size = 1.;
    for (iref = 0; iref < 3; ++iref) {
        size *= 0.5;
        vis_ConnectGridFun(connect1, gf);
        vis_StatePre(state, SYS_DOUBLE);
        vis_StateDef(state, numel, SYS_ELEM, SYS_NONE, VIS_SCALAR);
        for (i = 1; i <= numel; i++) {
            vis_StateSetDatadv(state, i, &size);
        }
        /* refine mesh */
        vis_ConnectDef(connect2, 0, 0);
        msh_TetMeshRefine(tetmesh, state, connect2);
        printf("Generated refined mesh %d\n", iref + 1);
        sprintf(buffer, "exam44d_ref%d.bdf", iref + 1);
        vis_ConnectWrite(connect2, SYS_NASTRAN_BULKDATA, buffer);
        vis_ConnectNumber(connect2, SYS_ELEM, &numel);

        /* swap connect objects */
        connecttmp = connect1;
        connect1 = connect2;
        connect2 = connecttmp;
    }

    /* end objects */
    vis_ConnectEnd(connect1);
    vis_ConnectEnd(connect2);
    msh_TetMeshEnd(tetmesh);
    vis_StateEnd(state);
    vis_GridFunEnd(gf);
    return 0;
}

6.20. Example 44e, Successively Refine a 3D Parabolic Tet Mesh

This example mimics Example 44d. However, it assumes that the mesh is parabolic.

#include <stdio.h>
#include <math.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[26][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.}, {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.},
                                {1., 1., 1.}, {.5, 0., 0.}, {1., .5, 0.}, {.5, 1., 0.}, {0., .5, 0.}, {.5, 0., 1.}, {1., .5, 1.},
                                {.5, 1., 1.}, {0., .5, 1.}, {0., 0., .5}, {1., 0., .5}, {0., 1., .5}, {1., 1., .5}, {.5, .5, 0.},
                                {.5, .5, 1.}, {0., .5, .5}, {1., .5, .5}, {.5, 0., .5}, {.5, 1., .5}};

static Vint tris[12][6] = {{1, 5, 3, 17, 23, 12}, {3, 5, 7, 23, 16, 19}, {4, 6, 2, 24, 18, 10}, {8, 6, 4, 14, 24, 20},
                           {1, 2, 5, 9, 25, 17},  {5, 2, 6, 25, 18, 13}, {7, 4, 3, 26, 11, 19}, {8, 4, 7, 20, 26, 15},
                           {1, 4, 2, 21, 10, 9},  {4, 1, 3, 21, 12, 11}, {5, 8, 7, 22, 15, 16}, {5, 6, 8, 13, 14, 22}};

static Vint maxi = 3;

static Vint eassoc[12][3] = {{1, 0, 2},  {0, 3, 4},  {0, 5, 6}, {7, 0, 8}, {9, 0, 0}, {0, 0, 10},
                             {0, 11, 0}, {0, 0, 12}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

/*----------------------------------------------------------------------
    Generate a Quadratic 3D Volume Tet Mesh and Perform Successive Refinements
----------------------------------------------------------------------*/
int
main()
{
    msh_TetMesh* tetmesh;
    vis_Connect *connect1, *connect2, *connecttmp;
    vis_State* state;
    vis_GridFun* gf;

    Vint i, j, iref;
    Vint numel;
    Vdouble size;
    Vchar buffer[80];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create tetmesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, 26, 12, maxi);

    /* create connect object */
    connect1 = vis_ConnectBegin();
    vis_ConnectPre(connect1, SYS_DOUBLE);
    vis_ConnectDef(connect1, 0, 0);

    /* define nodes */
    for (i = 0; i < 26; i++) {
        msh_TetMeshSetNode(tetmesh, i + 1, coords[i]);
        /* define a node association on every corner vertex */
        if (i < 8) {
            msh_TetMeshSetNodeAssoc(tetmesh, VIS_GEOVERT, i + 1, i + 1);
        }
    }
    /* define tris */
    for (i = 0; i < 12; i++) {
        msh_TetMeshSetTri(tetmesh, i + 1, tris[i]);
        for (j = 0; j < 3; ++j) {
            if (eassoc[i][j]) {
                msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOEDGE, i + 1, SYS_EDGE, j + 1, eassoc[i][j]);
            }
        }
    }
    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam44e.tet");
    /* generate */
    msh_TetMeshGenerate(tetmesh, connect1);
    vis_ConnectNumber(connect1, SYS_ELEM, &numel);
    printf("Generated original mesh\n");
    vis_ConnectWrite(connect1, SYS_NASTRAN_BULKDATA, "exam44e_orig.bdf");

    /* create state object with new sizes and refine */
    gf = vis_GridFunBegin();
    state = vis_StateBegin();
    vis_StateSetObject(state, VIS_GRIDFUN, (Vobject*)gf);

    connect2 = vis_ConnectBegin();
    vis_ConnectPre(connect2, SYS_DOUBLE);
    size = 1.;
    for (iref = 0; iref < 3; ++iref) {
        size *= 0.5;
        vis_ConnectGridFun(connect1, gf);
        vis_StatePre(state, SYS_DOUBLE);
        vis_StateDef(state, numel, SYS_ELEM, SYS_NONE, VIS_SCALAR);
        for (i = 1; i <= numel; i++) {
            vis_StateSetDatadv(state, i, &size);
        }
        /* refine mesh */
        vis_ConnectDef(connect2, 0, 0);
        msh_TetMeshRefine(tetmesh, state, connect2);
        printf("Generated refined mesh %d\n", iref + 1);
        sprintf(buffer, "exam44e_ref%d.bdf", iref + 1);
        vis_ConnectWrite(connect2, SYS_NASTRAN_BULKDATA, buffer);
        vis_ConnectNumber(connect2, SYS_ELEM, &numel);

        /* swap connect objects */
        connecttmp = connect1;
        connect1 = connect2;
        connect2 = connecttmp;
    }

    /* end objects */
    vis_ConnectEnd(connect1);
    vis_ConnectEnd(connect2);
    msh_TetMeshEnd(tetmesh);
    vis_StateEnd(state);
    vis_GridFunEnd(gf);
    return 0;
}

6.21. Example 45, Generate Surface Mesh and 3D Volume Tet Mesh

This example illustrates generating tetrahedral meshes from a simple geometry. The geometry is a straight forward unit cube but it illustrates the complete process of generating a surface triangulation with a SurfMesh object using the geometry and associated geometry tags, passing this boundary triangulation to a TetMesh object and generating a final tetrahedral mesh with all the generated nodes and tetrahedra associated to the original geometry features.

The geometry points are defined and assigned the VIS_GEOVERT point association type. The value of the association is set to the point id. The nodes in the surface mesh generated by SurfMesh and the nodes in the final volume mesh generated by TetMesh which lie on these geometry points will have the VIS_GEOVERT node association type set to the original defined point id value.

The triangle edges are assigned VIS_GEOEDGE element entity association values set to the edge ids in the range 1-12. The triangle faces are assigned the VIS_GEOFACE element entity association values set to the face ids in the range 1-6. Finally the triangles are assigned VIS_GEOBODY element association values of 1, since there is only a single body. All tetrahedra edges lying on geometry edges will be given VIS_GEOEDGE element entity associations, all tetrahedra faces lying on geometry faces will be given VIS_GEOFACE element entity associations, and all tetrahedra will be given VIS_GEOBODY element associations.

The parameter, maxi is set to 3 to generate parabolic elements throughout. Setting the parameter to 2 will generate linear elements. The final output of the TetMesh object which is placed in a Connect object is both printed and written to a NASTRAN bulk data file.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"
/*
                 3----3-----4
                /.         /|
               / 4        / 2
             12  .      11  |
             /   1....1./...2
            7----7-----8   /
            |  9       |  10
            8 .        6 /
            |.         |/
            5----5-----6

           faces are ordered 1(-z), 2(+z) 3(-y) 4(+y) 5(-x) 6(+x)

           y
           |
           +-- x
          /
         z
*/

/* a simple unit cube */
static Vdouble xc[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                           {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};
static Vint tefl[12][3] = {{1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 1}, {0, 1, 1},
                           {0, 1, 1}, {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {1, 1, 0}};
/* edge associations */
static Vint ted[12][3] = {{9, 0, 4},  {0, 8, 12}, {0, 10, 2}, {1, 0, 11}, {1, 0, 9}, {0, 10, 5},
                          {0, 3, 12}, {11, 0, 7}, {0, 2, 1},  {0, 4, 3},  {0, 7, 8}, {5, 6, 0}};
/* face associations */
static Vint tfa[12] = {5, 5, 6, 6, 3, 3, 4, 4, 1, 1, 2, 2};

/* triangle normals */
static Vdouble norms[12][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}}, {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
                      Generate a Surface Mesh and Tet Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vis_Connect *connectsurf, *connecttet;

    Vint i, j, k;
    Vint maxi;
    Vint numsurfpnts, numsurftris;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[10];
    Vint num, aid, aids[10];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 8, 12);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* generate parabolic elements */
    maxi = 3;
    /* define points */
    for (i = 0; i < 8; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
        /* set VIS_GEOVERT association to the point id */
        msh_SurfMeshSetPointAssoc(surfmesh, VIS_GEOVERT, i + 1, i + 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
        /* set VIS_GEOEDGE and VIS_GEOFACE */
        for (j = 0; j < 3; j++) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOEDGE, i + 1, SYS_EDGE, j + 1, ted[i][j]);
        }
        msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOFACE, i + 1, SYS_FACE, 0, tfa[i]);
        /* set VIS_GEOBODY to 1, only a single body */
        msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, i + 1, SYS_ELEM, 0, 1);
    }

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, maxi);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .5);

    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam45.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* write generated tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam45_srf.bdf");

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts, numsurftris, maxi);

    /* Fill TetMesh object with generated points on surface */
    msh_TetMeshConnect(tetmesh, connectsurf);

    /* set mesh parameters */
    msh_TetMeshSetParami(tetmesh, VIS_MESH_MAXI, maxi);
    msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, .5);

    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
    vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of tets = %d\n", numel);

    /* write generated tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam45_tet.bdf");

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connecttet, 1, &i, (Vdouble(*)[3])x);
        printf("id= %d  x= %e, y= %e, z= %e\n", i, x[0], x[1], x[2]);
    }
    /* node associations */
    for (i = 1; i <= numnp; i++) {
        vis_ConnectAllNodeAssoc(connecttet, VIS_GEOVERT, i, &num, aids);
        for (j = 0; j < num; j++) {
            if (aids[j] == 0)
                continue;
            printf("id= %d, VIS_GEOVERT[%d]= %d\n", i, j, aids[j]);
        }
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connecttet, i, &nix, ix);
        printf("id= %d  ix=", i);
        for (j = 0; j < nix; j++) {
            printf(" %d", ix[j]);
        }
        printf("\n");
    }
    /* element associations */
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemAssoc(connecttet, VIS_GEOBODY, 1, &i, &aid);
        if (aid == 0)
            continue;
        printf("id= %d, VIS_GEOBODY= %d\n", i, aid);
    }
    /* element entity associations */
    for (i = 1; i <= numel; i++) {
        /* tet edges */
        for (j = 1; j <= 6; j++) {
            vis_ConnectAllElemEntAssoc(connecttet, VIS_GEOEDGE, SYS_EDGE, i, j, &num, aids);
            for (k = 0; k < num; k++) {
                if (aids[k] == 0)
                    continue;
                printf("id= %d edge= %d, VIS_GEOEDGE[%d]= %d\n", i, j, k, aids[k]);
            }
        }
        /* tet faces */
        for (j = 1; j <= 4; j++) {
            vis_ConnectElemEntAssoc(connecttet, VIS_GEOFACE, SYS_FACE, i, j, &aid);
            if (aid == 0)
                continue;
            printf("id= %d face= %d, VIS_GEOFACE= %d\n", i, j, aid);
        }
    }
    /* end objects */
    vis_ConnectEnd(connectsurf);
    vis_ConnectEnd(connecttet);
    msh_SurfMeshEnd(surfmesh);
    msh_TetMeshEnd(tetmesh);
    return 0;
}

6.22. Example 45a, Generate Surface and 3D Volume Tet Mesh with Sizing

This example illustrates generating a tetrahedral mesh from a non-manifold, rectilinear solid containing an internal face. Node based and region based sizing is applied. A default edge length of .2 is applied for the surface mesh. In region 1, an unconnected node is inserted in the center to define a size of .02 using msh_TetMeshSetNodeSizing(). In region 2 a region size of .05 is applied using msh_TetMeshSetTriSizing().

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a cube with interior surface */
/* point coordinates */
static Vdouble xc[12][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.}, {0., 0., 1.}, {1., 0., 1.},
                            {0., 1., 1.}, {1., 1., 1.}, {0., 0., 2.}, {1., 0., 2.}, {0., 1., 2.}, {1., 1., 2.}};

/* triangle connectivity */
static Vint tris[22][3] = {{1, 5, 3}, {3, 5, 7},  {4, 6, 2},  {8, 6, 4},   {1, 2, 5},   {5, 2, 6},  {7, 4, 3},  {8, 4, 7},
                           {1, 4, 2}, {4, 1, 3},  {5, 8, 7},  {5, 6, 8},   {5, 9, 7},   {7, 9, 11}, {8, 10, 6}, {12, 10, 8},
                           {5, 6, 9}, {9, 6, 10}, {11, 8, 7}, {12, 8, 11}, {9, 12, 11}, {9, 10, 12}};
/* triangle right and left element associations */
static Vint ta[22][2] = {
/* outer boundary and non-manifold internal surface */
{1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 0}, {1, 2},
{1, 2}, {2, 0}, {2, 0}, {2, 0}, {2, 0}, {2, 0}, {2, 0}, {2, 0}, {2, 0}, {2, 0}, {2, 0}};

/* triangle edge flags */
static Vint tefl[22][3] = {{1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1},
                           {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {1, 1, 0}, {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1},
                           {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {0, 1, 1}, {1, 1, 0}};

/* triangle normals */
static Vdouble norms[22][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}}, {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},

                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
                      Generate Surface and 3D Volume Tet Mesh with Sizing
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vis_Connect *connectsurf, *connecttet;

    Vint i;
    Vint numsurfpnts, numsurftris;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[3], aid;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 12, 22);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* define points */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 22; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
        msh_SurfMeshSetTriBack(surfmesh, i + 1, ta[i][0], ta[i][1]);
        /* set property associations */
        if (ta[i][0]) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, i + 1, SYS_ELEM, 0, ta[i][0]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, 0, ta[i][0]);
        }
        if (ta[i][1]) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, i + 1, SYS_ELEM, -1, ta[i][1]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, -1, ta[i][1]);
        }
    }
    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_INTSURFBACK, VIS_ON);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .2);

    /* write out file encapsulating meshing specifications */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam45a.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    msh_SurfMeshEnd(surfmesh);

    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* write generated surf mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam45a-srf.bdf");

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts + 1, numsurftris, 2);

    /* Fill TetMesh object with generated points on surface */
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, (Vdouble(*)[3])x);
        msh_TetMeshSetNode(tetmesh, i, x);
    }

    /* Fill TetMesh object with generated triangles */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemNode(connectsurf, i, &nix, ix);
        msh_TetMeshSetTri(tetmesh, i, ix);
        vis_ConnectElemAssoc(connectsurf, VIS_PROPID, 1, &i, &aid);
        if (aid) {
            msh_TetMeshSetTriAssoc(tetmesh, VIS_PROPID, i, SYS_ELEM, 0, aid);
        }
        vis_ConnectElemAssoc(connectsurf, VIS_GEOBODY, 1, &i, &aid);
        if (aid) {
            msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOBODY, i, SYS_ELEM, 0, aid);
        }
    }
    /* set sizing */
    msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, .2);
    /* add sizing at unconnected node in center of region 1 */
    x[0] = .5;
    x[1] = .5;
    x[2] = .5;
    msh_TetMeshSetNode(tetmesh, numsurfpnts + 1, x);
    msh_TetMeshSetNodeSizing(tetmesh, numsurfpnts + 1, .02);
    msh_TetMeshSetParami(tetmesh, TETMESH_UNCONNECT, SYS_ON);
    /* set region sizing */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemAssoc(connectsurf, VIS_GEOBODY, 1, &i, &aid);
        if (aid == 2) {
            msh_TetMeshSetTriSizing(tetmesh, i, .05);
        }
    }

    vis_ConnectEnd(connectsurf);

    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    /* write out file encapsulating meshing specifications */
    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam45a.tet");
    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    msh_TetMeshEnd(tetmesh);
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
    vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);

    /* write generated tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam45a-tet.bdf");

    vis_ConnectEnd(connecttet);
    return 0;
}

6.23. Example 46, Generate 3D Curve Mesh Using CurvMesh

This example illustrates generating parabolic line elements along a curve defined by 4 input line segments. The input curve consists of a straight line segment .75 long along the positive y-axis followed by a 90 degree curve of radius .25 followed by a straight line segment 1.75 long along the positive x-axis. The number of input points and the number and order of input line segments are specified by calling msh_CurvMeshDef(), then each point and line is input using msh_CurvMeshSetPoint() and msh_CurvMeshSetLine() respectively. The input point numbers 2 and 4 are marked as preserved. This ensures that a node is generated at this location in the output mesh. All free points (those points connected to only one input line segment) are automatically preserved. The function msh_CurvMeshSetLineTang() sets the line edgepoint tangent vectors. These tangents are used to construct a cubic geometry along the line.

A line association is defined along input line 1. As a result all nodes generated along input line 1 will be given the the specified association. In a similar manner, a point association is defined at point 5. The node generated under the point will be given the specified association.

The density of the generated mesh and the order of line elements to generate are specified using msh_CurvMeshSetParamd() and msh_CurvMeshSetParami(). In this case a target edge length of .4 is specified with no line element spanning more than 30. degrees. The size of the generated line elements is the minimum of these two size criteria.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble xc[5][3] = {{0., 0., 0.}, {0., .75, 0.}, {.0732233, .9267767, 0.}, {.25, 1., 0.}, {2., 1., 0.}};

static Vint pc[5] = {0, 1, 0, 1, 0};

static Vint lix[4][2] = {{1, 2}, {2, 3}, {3, 4}, {4, 5}};
static Vdouble vex[4][2][3] = {
{{0., 1., 0.}, {0., 1., 0.}}, {{0., 1., 0.}, {.707, .707, 0.}}, {{.707, .707, 0.}, {1., 0., 0.}}, {{1., 0., 0.}, {1., 0., 0.}}};

/*----------------------------------------------------------------------
                      Generate a Boundary Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_CurvMesh* curvmesh;
    vis_Connect* connect;

    Vint i;
    Vint numnp, numel;
    Vdouble x[3];
    Vint aid, aid1;
    Vint nix, ix[3];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create CurvMesh object */
    curvmesh = msh_CurvMeshBegin();
    msh_CurvMeshDef(curvmesh, 5, 4);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectDef(connect, 0, 0);

    /* define points */
    for (i = 0; i < 5; i++) {
        msh_CurvMeshSetPoint(curvmesh, i + 1, xc[i], pc[i]);
    }
    /* define parabolic line segment connectivity */
    for (i = 0; i < 4; i++) {
        msh_CurvMeshSetLine(curvmesh, i + 1, lix[i]);
        msh_CurvMeshSetLineTang(curvmesh, i + 1, vex[i]);
    }
    /* set assoc of 100 at line 1 */
    msh_CurvMeshSetLineAssoc(curvmesh, VIS_MISCID, 1, 100);

    /* set assoc of 200 at point 5 */
    msh_CurvMeshSetPointAssoc(curvmesh, VIS_MISCID1, 5, 200);

    /* set mesh parameters */
    msh_CurvMeshSetParami(curvmesh, VIS_MESH_MAXI, 3);
    msh_CurvMeshSetParamd(curvmesh, VIS_MESH_SPANANGLE, 30.);
    msh_CurvMeshSetParamd(curvmesh, VIS_MESH_EDGELENGTH, .4);

    /* generate */
    msh_CurvMeshGenerate(curvmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    vis_ConnectWrite(connect, SYS_SDRC_UNIVERSAL, "exam46.unv");

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        vis_ConnectNodeAssoc(connect, VIS_MISCID1, 1, &i, &aid1);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d, aid1= %d\n", i, x[0], x[1], x[2], aid, aid1);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d\n", i, ix[0], ix[1], ix[2]);
    }
    /* end objects */
    vis_ConnectEnd(connect);
    msh_CurvMeshEnd(curvmesh);
    return 0;
}

6.24. Example 47, Generate 3D Surface Mesh Using SurfMesh

This example illustrates generating 3 node triangular elements on a curved surface defined by 12 points and 10 triangular facets. The input surface represents a simple folded plate. This geometry illustrates the ability of the SurfMesh module to automatically refine the generated mesh in areas of high curvature and transition the element size to a target size in flat areas.

The number of input points and the number and order of input triangles are specified by calling msh_SurfMeshDef(), then each point and triangle is input using msh_SurfMeshSetPoint() and msh_SurfMeshSetTri() respectively. In general new points are placed on the surface defined by the triangular facets and any defined point may be moved unless it is explicitly preserved. Note that the four corner input points are marked as preserved in the function msh_SurfMeshSetPoint() so that they do not move.

The density of the generated mesh and the order of elements to generate are specified using msh_SurfMeshSetParamd() and msh_SurfMeshSetParami(). The dimensions of the plate are approximately 1. by 2. and it has been folded in the middle through 90 degrees with a radius of curvature of .1 . A target edge length of .2 is specified with no element spanning more than 30. degrees. The size of the generated elements is the minimum of these two size criteria. A minimum edge length of .02 is specified. This ensures that no unnecessarily small triangles are produced. The call msh_SurfMeshGenerate() generates nodes and triangular elements and enters them into the Connect object.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble xc[12][3] = {
{0.000e+00, 0.000e+00, 0.000e+00}, {0.000e+00, 9.000e-01, 0.000e+00}, {1.333e-02, 9.500e-01, 0.000e+00},
{5.000e-02, 9.866e-01, 0.000e+00}, {1.000e-01, 1.000e+00, 0.000e+00}, {1.000e+00, 1.000e+00, 0.000e+00},
{0.000e+00, 0.000e+00, 1.000e+00}, {0.000e+00, 9.000e-01, 1.000e+00}, {1.333e-02, 9.500e-01, 1.000e+00},
{5.000e-02, 9.866e-01, 1.000e+00}, {1.000e-01, 1.000e+00, 1.000e+00}, {1.000e+00, 1.000e+00, 1.000e+00}};

static Vint pc[12] = {1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1};

static Vint tix[10][3] = {{1, 2, 7},  {2, 8, 7},  {2, 3, 8},   {3, 9, 8},  {3, 4, 9},
                          {4, 10, 9}, {4, 5, 10}, {5, 11, 10}, {5, 6, 11}, {6, 12, 11}};

static Vint efl[3] = {0, 0, 0};

/*----------------------------------------------------------------------
                      Generate a Surface Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh = NULL;
    vis_Connect* connect = NULL;

    Vint i = 0;
    Vint numnp = 0;
    Vint numel = 0;
    Vdouble x[3] = {0};
    Vint nix = 0;
    Vint ix[3] = {0};

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 12, 10);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define points */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], pc[i]);
    }
    /* define triangle connectivity */
    for (i = 0; i < 10; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tix[i], efl);
    }

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 30.);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .02);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .2);

    /* write out file encapsulating meshing specifications */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam47.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        printf("id= %d  x= %f, y= %f, z= %f\n", i, x[0], x[1], x[2]);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        printf("id= %d  ix= %d %d %d\n", i, ix[0], ix[1], ix[2]);
    }
    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "example47_SurfMeshGenerate.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}

6.25. Example 47 [Parallel], Generate 3D Surface Mesh Using Parallel SurfMesh

This example illustrates generating surface triangle elements in a complex geometry. The input file is a .srf file read by the SurfMesh

The target is to perform this in parallel. To define the size of the thread pool to be used, please use msh_SurfMeshSetNumThreads(). To generate and execute please use msh_SurfMeshGenerate().

The algorithm for the parallel strategy consists of the following steps:

Step 1: Gather all elements that are not complying with the surfmesh parameters. Step 2: Call ParMetis to generate a given number of partitions. Step 3: Per Partition, call surfMesh blocking the boundary edges. Step 4: Per partition, identify two element groups. Compliant elements and Non-Compliant elements. Step 5: Use the non-compliant elements as input for the next iteration.

In this example, the algorithm will:

ITERATION 1 Split the domain into 16 partitions, we consume a threadpool using 4 threads to work dynamically. We generated 16 partition meshes. Those meshes will have compliant and non-compliant elements.

ITERATION 2 Taking only as input the non-compliant elements, we partition that element group to generate newly 16 partitions. We block the new partition-boundaries and we generate surface meshes. From that second iteration, we split two element groups: the compliant and non-compliant elements for next iteration.

ITERATION 3 Finally, we take the the non-compliant elements group from the previous iteration. Since this is the last iteration, we call surfMesh in sequential.

ITERATION 1,2,3 + FINAL MERGE After 3 Iterations, we gather the element group of compliant elements of each iteration and we obtain a perfect puzzle!

The partition strategy can be changed using msh_SurfMeshSetPartitionStrategy(). Refer to that method for further details.

The call msh_SurfMesh_EXPERIMENTAL_Generate() generates nodes and triangular elements and enters them into the Connect object.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"
#include <vector>
#include <string>

/*----------------------------------------------------------------------
        Generate a Surface Mesh in Parallel
----------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    std::string filename = argv[1]; // Expecting "PATH_to/walek.srf";
    constexpr Vint threadCount = 4;
    constexpr Vdouble targetEdgeLength = 0.6;
    std::vector<Vint> partitionStrategy = {16, 16};

    /* create Connect object */
    vis_Connect* connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* create SurfMesh object */
    msh_SurfMesh* surfmesh = msh_SurfMeshBegin();

    msh_SurfMeshRead(surfmesh, SYS_ASCII, filename.c_str());
    Vint errorFlag = msh_SurfMeshError(surfmesh);
    if (errorFlag) {
        printf("A problem occurred during reading of input file\n");
        exit(errorFlag);
    }

    /* set mesh parameters */
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, targetEdgeLength);

    /* Define the parallel strategy. Set the number of partitions per iteration of the algorithm.*/
    msh_SurfMeshSetPartitionStrategy(surfmesh, static_cast<Vint>(partitionStrategy.size()), partitionStrategy.data());

    /* Define the number of threads to use during the parallel computation and call Experimental Generate */
    msh_SurfMeshSetNumThreads(surfmesh, threadCount);
    msh_SurfMeshGenerate(surfmesh, connect);

    errorFlag = msh_SurfMeshError(surfmesh);

    if (errorFlag) {
        printf("A problem occurred during execution of parallel surfmesher\n");
    }

    /*Optionally, print some recorded data available in a dictionary report*/
    vsy_DblDict* keyValueDict = nullptr;
    msh_SurfMeshGetExecutionReport(surfmesh, &keyValueDict);
    // vsy_DblDictPrint(keyValueDict);

    /* print output nodes and elements */
    Vint nodeCount = 0;
    Vint elementCount = 0;
    vis_ConnectNumber(connect, SYS_NODE, &nodeCount);
    vis_ConnectNumber(connect, SYS_ELEM, &elementCount);
    printf("nodeCount= %d, elementCount= %d\n", nodeCount, elementCount);

    // EXPECTED output
    /*
     * Percentage of Compliant Elements [%], v= 98.278854
     * Percentage of Non-Compliant Elements [%], v= 1.721146
     * Total Surfmesher Time [s], v= 58.630623
     * Iteration 3 Time[s], v= 14.753183
     * Iteration 2 Time[s], v= 4.554247
     * Iteration 1 Time[s], v= 38.485378
     * nodeCount= 923153, elementCount= 1846298
     */

    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "example47_SurfMeshParallel.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}
../_images/vistools_exam47parallel.png

6.26. Example 47a, Generate 3D Surface Mesh with Associations

This example is an extension of Example 47 in which node and element associations are assigned to the input points, triangles and triangle edges to be propagated into the final mesh. Triangle vertex normals are also assigned. In addition, alterations are made to the meshing parameters to output 6 node parabolic triangles rather than 3 node linear triangles. Node association types VIS_MISCID and VIS_MISCID1 are used in this example, however any association type may be assigned. Node associations are assigned to points 1 and 7 using msh_SurfMeshSetPointAssoc(). Besides specifying the association, this function also marks the node to be preserved. After the triangles are defined, node associations are defined on triangle edges and faces. The triangle edge association on triangle 2 edge 1 creates a preserved edge. This preserved edge along with the “free” edges of triangle 1 and 2 (free edges are always preserved) bounds triangles 1 and 2. This means that node and element associations can be safely applied to the set of triangles 1 and 2 with predictable results. The end points of the preserved edge (triangle 1 edge 2) are automatically preserved as “topological” points. Any point connected to 1 preserved edge or 3 or more preserved edges is a topologically preserved point. The VIS_PROPID association is used to propagate property ids 1 and 2 to the two sets of triangles bounded by preserved edges.

The function msh_SurfMeshSetParami() is used to indicate that parabolic triangles are to be output. The mesh density and spanning angle parameters are doubled from those used in Example 47 where linear elements were generated. The call msh_SurfMeshGenerate() generates nodes and triangular elements and enters them into the Connect object. The resulting mesh is printed and also written to file exam47a.bdf in NASTRAN bulk data file format. Note that vis/exam/exam30ddev.cpp may be used to display the generated mesh saved in the file.

In the figure below, triangle and point numbers are displayed along with triangle vertex normals in orange. Preserved edges are black, free edges are red and interior, unpreserved edges are in white.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* point coordinates */
static Vdouble xc[12][3] = {
{0.000e+00, 0.000e+00, 0.000e+00}, {0.000e+00, 9.000e-01, 0.000e+00}, {1.333e-02, 9.500e-01, 0.000e+00},
{5.000e-02, 9.866e-01, 0.000e+00}, {1.000e-01, 1.000e+00, 0.000e+00}, {1.000e+00, 1.000e+00, 0.000e+00},
{0.000e+00, 0.000e+00, 1.000e+00}, {0.000e+00, 9.000e-01, 1.000e+00}, {1.333e-02, 9.500e-01, 1.000e+00},
{5.000e-02, 9.866e-01, 1.000e+00}, {1.000e-01, 1.000e+00, 1.000e+00}, {1.000e+00, 1.000e+00, 1.000e+00}};
/* preserved point flags */
static Vint pc[12] = {1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1};
/* triangle connectivity */
static Vint tix[10][3] = {{1, 2, 7},  {2, 8, 7},  {2, 3, 8},   {3, 9, 8},  {3, 4, 9},
                          {4, 10, 9}, {4, 5, 10}, {5, 11, 10}, {5, 6, 11}, {6, 12, 11}};
/* triangle preserved edge flags */
static Vint efl[10][3] = {{0, 0, 0}, {1, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0},
                          {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
/* triangle node normals */
static Vdouble tvn[10][3][3] = {{{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},

                                {{1., 0., 0.}, {.867, -.500, 0.}, {1., 0., 0.}},
                                {{.867, -.500, 0.}, {.867, -.500, 0.}, {1., 0., 0.}},

                                {{.867, -.500, 0.}, {.500, -.867, 0.}, {.867, -.500, 0.}},
                                {{.500, -.867, 0.}, {.500, -.867, 0.}, {.867, -.500, 0.}},

                                {{.500, -.867, 0.}, {0., -1., 0.}, {.500, -.867, 0.}},
                                {{0., -1., 0.}, {0., -1., 0.}, {.500, -.867, 0.}},

                                {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}};

/* triangle associations */
static Vint tpid[10] = {1, 1, 2, 2, 2, 2, 2, 2, 2, 2};

/*----------------------------------------------------------------------
                      Generate a 3D Surface Mesh with Associations
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    vis_Connect* connect;

    Vint i;
    Vint numnp, numel;
    Vdouble x[3];
    Vint aid, aid1, pid;
    Vint nix, ix[6];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 12, 10);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define points */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], pc[i]);
    }
    /* set node associations at points 1 and 7 */
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID1, 1, 1011);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 7, 1001);

    /* define triangle connectivity, normals and property id */
    for (i = 0; i < 10; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tix[i], efl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, tvn[i]);
        msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, 0, tpid[i]);
    }
    /* set node associations at triangle edge */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 2, SYS_EDGE, 1, 1002);

    /* set node associations on triangles */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 1, SYS_FACE, 0, 1003);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 2, SYS_FACE, 0, 1003);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 60.);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .04);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .4);

    /* generate */
    msh_SurfMeshGenerate(surfmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        vis_ConnectNodeAssoc(connect, VIS_MISCID1, 1, &i, &aid1);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d, aid1= %d\n", i, x[0], x[1], x[2], aid, aid1);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        vis_ConnectElemAssoc(connect, VIS_PROPID, 1, &i, &pid);
        printf("id= %d  ix= %d %d %d %d %d %d, pid= %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5], pid);
    }
    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam47a.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}
../_images/vistools-exam47a.gif

Figure 28-47a, Input Geometry Tesselation

6.27. Example 47b, Generate 3D Surface Mesh with Normals and Associations

This example is an extension of Example 47a in which triangle node normals are used to improve the surface description supplied to the SurfMesh module. In addition the triangle node normals are used in conjunction with the Connect module to identify edges to be preserved due to discontinuities in the surface normal and curvature. The input geometry is changed from previous examples to add a short flat extension which forms a distinct surface normal discontinuity with the rest of the geometry. Two points and two additional triangles define this additional geometry. All geometry and normals are entered into a Connect object. Feature edges due to discontinuous surface normal are extracted. This operation will flag the edge shared by triangles 10 and 11. Edges due to curvature change are then extracted. This operation will flag the edge shared by triangles 2 and 3 and the edge shared by triangles 8 and 9.

An option is activated to compute normals at the nodes of the final triangle mesh. Use msh_SurfMeshSetParami() with parameter VIS_MESH_COMPUTENORMAL to enable this option.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* point coordinates */
static Vdouble xc[14][3] = {
{0.000e+00, 0.000e+00, 0.000e+00}, {0.000e+00, 9.000e-01, 0.000e+00}, {1.333e-02, 9.500e-01, 0.000e+00},
{5.000e-02, 9.867e-01, 0.000e+00}, {1.000e-01, 1.000e+00, 0.000e+00}, {1.000e+00, 1.000e+00, 0.000e+00},
{0.000e+00, 0.000e+00, 1.000e+00}, {0.000e+00, 9.000e-01, 1.000e+00}, {1.333e-02, 9.500e-01, 1.000e+00},
{5.000e-02, 9.867e-01, 1.000e+00}, {1.000e-01, 1.000e+00, 1.000e+00}, {1.000e+00, 1.000e+00, 1.000e+00},
{1.500e+00, 9.000e-01, 0.000e+00}, {1.500e+00, 9.000e-01, 1.000e+00}};

/* point preservation flags */
static Vint pc[14] = {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1};

/* triangle connectivity */
static Vint tix[12][3] = {{1, 2, 7},  {2, 8, 7},   {2, 3, 8},  {3, 9, 8},   {3, 4, 9},   {4, 10, 9},
                          {4, 5, 10}, {5, 11, 10}, {5, 6, 11}, {6, 12, 11}, {6, 13, 12}, {13, 14, 12}};

/* triangle node normals */
static Vdouble tvn[12][3][3] = {{{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},

                                {{1., 0., 0.}, {.867, -.500, 0.}, {1., 0., 0.}},
                                {{.867, -.500, 0.}, {.867, -.500, 0.}, {1., 0., 0.}},

                                {{.867, -.500, 0.}, {.500, -.867, 0.}, {.867, -.500, 0.}},
                                {{.500, -.867, 0.}, {.500, -.867, 0.}, {.867, -.500, 0.}},

                                {{.500, -.867, 0.}, {0., -1., 0.}, {.500, -.867, 0.}},
                                {{0., -1., 0.}, {0., -1., 0.}, {.500, -.867, 0.}},

                                {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},

                                {{-.196, -.981, 0.}, {-.196, -.981, 0.}, {-.196, -.981, 0.}},
                                {{-.196, -.981, 0.}, {-.196, -.981, 0.}, {-.196, -.981, 0.}}};

/* triangle property ids */
static Vint tpid[12] = {1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3};

/*----------------------------------------------------------------------
                      Generate a 3D Surface Mesh with Normals and Associations
----------------------------------------------------------------------*/
int
main()
{
    vis_Connect* connectgeo;
    vis_Group *groupface, *groupedge;
    msh_SurfMesh* surfmesh;
    vis_Connect* connect;

    Vint i, k;
    Vint numpnts, numtris;
    Vint flags;
    Vint efl[3];
    Vint numnp, numel;
    Vdouble x[3], ve[6][3];
    Vint aid, aid1, pid;
    Vint nix, ix[6];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    numpnts = 14;
    numtris = 12;
    /* enter geometry */
    connectgeo = vis_ConnectBegin();
    vis_ConnectPre(connectgeo, SYS_DOUBLE);
    vis_ConnectDef(connectgeo, numpnts, numtris);

    /* enter points */
    for (i = 0; i < numpnts; i++) {
        vis_ConnectSetCoordsdv(connectgeo, i + 1, xc[i]);
    }
    /* enter triangles */
    for (i = 0; i < numtris; i++) {
        vis_ConnectSetTopology(connectgeo, i + 1, VIS_SHAPETRI, 2, 0, 0);
        vis_ConnectSetElemNode(connectgeo, i + 1, tix[i]);
        vis_ConnectSetElemNormdv(connectgeo, i + 1, 1, tvn[i]);
    }
    vis_ConnectKernel(connectgeo, 0);

    /* determine feature edges */
    /* group of faces */
    groupface = vis_GroupBegin();
    vis_GroupDef(groupface, numtris, SYS_ELEM, SYS_FACE);
    vis_ConnectFaceGroup(connectgeo, CONNECT_FREE, NULL, groupface);

    /* extract feature edges and nodes at 10. degrees */
    vis_ConnectSetGroupParamf(connectgeo, CONNECT_FEATUREANGLE, 10.);
    vis_ConnectSetGroupParami(connectgeo, CONNECT_FEATURESENSE, VIS_ON);

    /* curvature changes greater than unity */
    vis_ConnectSetGroupParamf(connectgeo, CONNECT_CURVATURECHANGE, 1.);

    /* group of feature edges */
    groupedge = vis_GroupBegin();
    vis_GroupDef(groupedge, numtris, SYS_ELEM, SYS_EDGE);
    /* surface discontinuity */
    vis_ConnectEdgeGroup(connectgeo, CONNECT_FEATURE, groupface, groupedge);
    /* curvature discontinuity */
    vis_ConnectEdgeGroup(connectgeo, CONNECT_CURVATURE, groupface, groupedge);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numpnts, numtris);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);
    /* define points */
    for (i = 0; i < numpnts; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], pc[i]);
    }
    /* set node associations at points */
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID1, 1, 1011);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 7, 1001);

    /* define triangle connectivity and property id */
    for (i = 0; i < numtris; i++) {
        for (k = 1; k <= 3; k++) {
            vis_GroupGetEntFlag(groupedge, i + 1, k, &flags);
            if (flags) {
                efl[k - 1] = 1;
            }
            else {
                efl[k - 1] = 0;
            }
        }
        msh_SurfMeshSetTri(surfmesh, i + 1, tix[i], efl);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, tvn[i]);
        msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, 0, tpid[i]);
    }
    /* set node associations at triangle edge */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 2, SYS_EDGE, 1, 1002);

    /* set node associations on triangles */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 1, SYS_FACE, 0, 1003);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 2, SYS_FACE, 0, 1003);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_COMPUTENORMAL, 1);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 60.);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .04);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .4);

    /* generate */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam47b.srf");
    msh_SurfMeshGenerate(surfmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        vis_ConnectNodeAssoc(connect, VIS_MISCID1, 1, &i, &aid1);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d, aid1= %d\n", i, x[0], x[1], x[2], aid, aid1);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        /* element connectivity */
        vis_ConnectElemAssoc(connect, VIS_PROPID, 1, &i, &pid);
        printf("id= %d  ix= %d %d %d %d %d %d, pid= %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5], pid);
        /* element normals */
        vis_ConnectElemNormdv(connect, i, 1, ve);
        for (k = 0; k < nix; k++) {
            printf("        n= %e %e %e\n", ve[k][0], ve[k][1], ve[k][2]);
        }
    }
    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam47b.bdf");

    /* end objects */
    vis_ConnectEnd(connectgeo);
    vis_GroupEnd(groupface);
    vis_GroupEnd(groupedge);
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}

6.28. Example 47c, Generate a Non-Manifold 3D Surface Mesh with Sizing

This example illustrates adding a non-manifold branch to Example 47, (region 4) and defining element sizes on a specified face and edge in region 3. Note that non-manifold geometries must be enabled using msh_SurfMeshSetParami() with parameter SURFMESH_NONMANIFOLD enabled. The entity based sizing is specified using msh_SurfMeshSetTriSizing().

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* point coordinates */
static Vdouble xc[16][3] = {
{0.000e+00, 0.000e+00, 0.000e+00}, {0.000e+00, 9.000e-01, 0.000e+00}, {1.333e-02, 9.500e-01, 0.000e+00},
{5.000e-02, 9.867e-01, 0.000e+00}, {1.000e-01, 1.000e+00, 0.000e+00}, {1.000e+00, 1.000e+00, 0.000e+00},
{0.000e+00, 0.000e+00, 1.000e+00}, {0.000e+00, 9.000e-01, 1.000e+00}, {1.333e-02, 9.500e-01, 1.000e+00},
{5.000e-02, 9.867e-01, 1.000e+00}, {1.000e-01, 1.000e+00, 1.000e+00}, {1.000e+00, 1.000e+00, 1.000e+00},
{1.500e+00, 9.000e-01, 0.000e+00}, {1.500e+00, 9.000e-01, 1.000e+00}, {1.000e+00, 0.000e+00, 0.000e+00},
{1.000e+00, 0.000e+00, 1.000e+00}};

/* point preservation flags */
static Vint pc[16] = {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1};

/* triangle connectivity */
static Vint tix[14][3] = {{1, 2, 7},   {2, 8, 7},  {2, 3, 8},   {3, 9, 8},   {3, 4, 9},    {4, 10, 9},  {4, 5, 10},
                          {5, 11, 10}, {5, 6, 11}, {6, 12, 11}, {6, 13, 12}, {13, 14, 12}, {12, 15, 6}, {16, 15, 12}};

/* triangle node normals */
static Vdouble tvn[14][3][3] = {{{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},

                                {{1., 0., 0.}, {.867, -.500, 0.}, {1., 0., 0.}},
                                {{.867, -.500, 0.}, {.867, -.500, 0.}, {1., 0., 0.}},

                                {{.867, -.500, 0.}, {.500, -.867, 0.}, {.867, -.500, 0.}},
                                {{.500, -.867, 0.}, {.500, -.867, 0.}, {.867, -.500, 0.}},

                                {{.500, -.867, 0.}, {0., -1., 0.}, {.500, -.867, 0.}},
                                {{0., -1., 0.}, {0., -1., 0.}, {.500, -.867, 0.}},

                                {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},

                                {{-.196, -.981, 0.}, {-.196, -.981, 0.}, {-.196, -.981, 0.}},
                                {{-.196, -.981, 0.}, {-.196, -.981, 0.}, {-.196, -.981, 0.}},

                                {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}}};

/* triangle property ids */
static Vint tpid[14] = {1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4};

/*----------------------------------------------------------------------
                      Generate a Non-Manifold 3D Surface Mesh with Sizing
----------------------------------------------------------------------*/
int
main()
{
    vis_Connect* connectgeo;
    vis_Group *groupface, *groupedge;
    msh_SurfMesh* surfmesh;
    vis_Connect* connect;

    Vint i, k;
    Vint numpnts, numtris;
    Vint flags;
    Vint efl[3];
    Vint numnp, numel;
    Vdouble x[3], ve[6][3];
    Vint aid, aid1, pid;
    Vint nix, ix[6];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    numpnts = 16;
    numtris = 14;
    /* enter geometry */
    connectgeo = vis_ConnectBegin();
    vis_ConnectPre(connectgeo, SYS_DOUBLE);
    vis_ConnectDef(connectgeo, numpnts, numtris);

    /* enter points */
    for (i = 0; i < numpnts; i++) {
        vis_ConnectSetCoordsdv(connectgeo, i + 1, xc[i]);
    }
    /* enter triangles */
    for (i = 0; i < numtris; i++) {
        vis_ConnectSetTopology(connectgeo, i + 1, VIS_SHAPETRI, 2, 0, 0);
        vis_ConnectSetElemNode(connectgeo, i + 1, tix[i]);
        vis_ConnectSetElemNormdv(connectgeo, i + 1, 1, tvn[i]);
    }
    vis_ConnectKernel(connectgeo, 0);

    /* determine feature edges */
    /* group of faces */
    groupface = vis_GroupBegin();
    vis_GroupDef(groupface, numtris, SYS_ELEM, SYS_FACE);
    vis_ConnectFaceGroup(connectgeo, CONNECT_FREE, NULL, groupface);

    /* extract feature edges and nodes at 10. degrees */
    vis_ConnectSetGroupParamf(connectgeo, CONNECT_FEATUREANGLE, 10.);
    vis_ConnectSetGroupParami(connectgeo, CONNECT_FEATURESENSE, VIS_ON);

    /* curvature changes greater than unity */
    vis_ConnectSetGroupParamf(connectgeo, CONNECT_CURVATURECHANGE, 1.);

    /* group of feature edges */
    groupedge = vis_GroupBegin();
    vis_GroupDef(groupedge, numtris, SYS_ELEM, SYS_EDGE);
    /* surface discontinuity */
    vis_ConnectEdgeGroup(connectgeo, CONNECT_FEATURE, groupface, groupedge);
    /* curvature discontinuity */
    vis_ConnectEdgeGroup(connectgeo, CONNECT_CURVATURE, groupface, groupedge);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numpnts, numtris);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);
    /* define points */
    for (i = 0; i < numpnts; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], pc[i]);
    }
    /* set node associations at points */
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID1, 1, 1011);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 7, 1001);

    /* define triangle connectivity and property id */
    for (i = 0; i < numtris; i++) {
        for (k = 1; k <= 3; k++) {
            vis_GroupGetEntFlag(groupedge, i + 1, k, &flags);
            if (flags) {
                efl[k - 1] = 1;
            }
            else {
                efl[k - 1] = 0;
            }
        }
        msh_SurfMeshSetTri(surfmesh, i + 1, tix[i], efl);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, tvn[i]);
        msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, 0, tpid[i]);
        /* specific sizing for region 3 */
        if (tpid[i] == 3) {
            msh_SurfMeshSetTriSizing(surfmesh, i + 1, SYS_FACE, 0, .05);
        }
        if ((i + 1) == 12) {
            msh_SurfMeshSetTriSizing(surfmesh, i + 1, SYS_EDGE, 1, .0125);
        }
    }
    /* set node associations at triangle edge */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 2, SYS_EDGE, 1, 1002);

    /* set node associations on triangles */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOFACE, 1, SYS_FACE, 0, 1003);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOFACE, 2, SYS_FACE, 0, 1003);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_COMPUTENORMAL, 1);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 60.);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .04);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .4);

    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam47c.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        vis_ConnectNodeAssoc(connect, VIS_MISCID1, 1, &i, &aid1);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d, aid1= %d\n", i, x[0], x[1], x[2], aid, aid1);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        /* element connectivity */
        vis_ConnectElemAssoc(connect, VIS_PROPID, 1, &i, &pid);
        printf("id= %d  ix= %d %d %d %d %d %d, pid= %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5], pid);
        /* element normals */
        vis_ConnectElemNormdv(connect, i, 1, ve);
        for (k = 0; k < nix; k++) {
            printf("        n= %e %e %e\n", ve[k][0], ve[k][1], ve[k][2]);
        }
    }
    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam47c.bdf");

    /* end objects */
    vis_ConnectEnd(connectgeo);
    vis_GroupEnd(groupface);
    vis_GroupEnd(groupedge);
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}

6.29. Example 47d, Generate a 3D Surface Mesh with Trimming Loop

This example illustrates adding trimming loop to Example 47. The trimming loop is defined by introducing four unconnected points, 17 through 20, which form a square. The function msh_SurfMeshSetEdge() is used in a general way to define edges which are meant to be recovered in the final mesh. If the recovered edges form a closed loop, an option exists to trim (delete) any elements which occur in the interior of the loop.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* point coordinates */
static Vdouble xc[20][3] = {{0.000e+00, 0.000e+00, 0.000e+00},
                            {0.000e+00, 9.000e-01, 0.000e+00},
                            {1.333e-02, 9.500e-01, 0.000e+00},
                            {5.000e-02, 9.867e-01, 0.000e+00},
                            {1.000e-01, 1.000e+00, 0.000e+00},
                            {1.000e+00, 1.000e+00, 0.000e+00},
                            {0.000e+00, 0.000e+00, 1.000e+00},
                            {0.000e+00, 9.000e-01, 1.000e+00},
                            {1.333e-02, 9.500e-01, 1.000e+00},
                            {5.000e-02, 9.867e-01, 1.000e+00},
                            {1.000e-01, 1.000e+00, 1.000e+00},
                            {1.000e+00, 1.000e+00, 1.000e+00},
                            {1.500e+00, 9.000e-01, 0.000e+00},
                            {1.500e+00, 9.000e-01, 1.000e+00},
                            {1.000e+00, 0.000e+00, 0.000e+00},
                            {1.000e+00, 0.000e+00, 1.000e+00},
                            {1.0, .33, .66},
                            {1.0, .33, .33},
                            {1.0, .66, .33},
                            {1.0, .66, .66}};

/* point preservation flags */
static Vint pc[20] = {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};

/* triangle connectivity */
static Vint tix[14][3] = {{1, 2, 7},   {2, 8, 7},  {2, 3, 8},   {3, 9, 8},   {3, 4, 9},    {4, 10, 9},  {4, 5, 10},
                          {5, 11, 10}, {5, 6, 11}, {6, 12, 11}, {6, 13, 12}, {13, 14, 12}, {12, 15, 6}, {16, 15, 12}};

/* triangle node normals */
static Vdouble tvn[14][3][3] = {{{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},

                                {{1., 0., 0.}, {.867, -.500, 0.}, {1., 0., 0.}},
                                {{.867, -.500, 0.}, {.867, -.500, 0.}, {1., 0., 0.}},

                                {{.867, -.500, 0.}, {.500, -.867, 0.}, {.867, -.500, 0.}},
                                {{.500, -.867, 0.}, {.500, -.867, 0.}, {.867, -.500, 0.}},

                                {{.500, -.867, 0.}, {0., -1., 0.}, {.500, -.867, 0.}},
                                {{0., -1., 0.}, {0., -1., 0.}, {.500, -.867, 0.}},

                                {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},

                                {{-.196, -.981, 0.}, {-.196, -.981, 0.}, {-.196, -.981, 0.}},
                                {{-.196, -.981, 0.}, {-.196, -.981, 0.}, {-.196, -.981, 0.}},

                                {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}}};

/* triangle property ids */
static Vint tpid[14] = {1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4};

/*----------------------------------------------------------------------
                      Generate a 3D Surface Mesh with Trimming Loop
----------------------------------------------------------------------*/
int
main()
{
    vis_Connect* connectgeo;
    vis_Group *groupface, *groupedge;
    msh_SurfMesh* surfmesh;
    vis_Connect* connect;

    Vint i, k;
    Vint numpnts, numtris;
    Vint flags;
    Vint efl[3];
    Vint numnp, numel;
    Vdouble x[3], ve[6][3];
    Vint aid, aid1, pid;
    Vint nix, ix[6];
    Vint eix[5];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    numpnts = 20;
    numtris = 14;
    /* enter geometry */
    connectgeo = vis_ConnectBegin();
    vis_ConnectPre(connectgeo, SYS_DOUBLE);
    vis_ConnectDef(connectgeo, numpnts, numtris);

    /* enter points */
    for (i = 0; i < numpnts; i++) {
        vis_ConnectSetCoordsdv(connectgeo, i + 1, xc[i]);
    }
    /* enter triangles */
    for (i = 0; i < numtris; i++) {
        vis_ConnectSetTopology(connectgeo, i + 1, VIS_SHAPETRI, 2, 0, 0);
        vis_ConnectSetElemNode(connectgeo, i + 1, tix[i]);
        vis_ConnectSetElemNormdv(connectgeo, i + 1, 1, tvn[i]);
    }
    vis_ConnectKernel(connectgeo, 0);

    /* determine feature edges */
    /* group of faces */
    groupface = vis_GroupBegin();
    vis_GroupDef(groupface, numtris, SYS_ELEM, SYS_FACE);
    vis_ConnectFaceGroup(connectgeo, CONNECT_FREE, NULL, groupface);

    /* extract feature edges and nodes at 10. degrees */
    vis_ConnectSetGroupParamf(connectgeo, CONNECT_FEATUREANGLE, 10.);
    vis_ConnectSetGroupParami(connectgeo, CONNECT_FEATURESENSE, VIS_ON);

    /* curvature changes greater than unity */
    vis_ConnectSetGroupParamf(connectgeo, CONNECT_CURVATURECHANGE, 1.);

    /* group of feature edges */
    groupedge = vis_GroupBegin();
    vis_GroupDef(groupedge, numtris, SYS_ELEM, SYS_EDGE);
    /* surface discontinuity */
    vis_ConnectEdgeGroup(connectgeo, CONNECT_FEATURE, groupface, groupedge);
    /* curvature discontinuity */
    vis_ConnectEdgeGroup(connectgeo, CONNECT_CURVATURE, groupface, groupedge);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numpnts, numtris);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);
    /* define points */
    for (i = 0; i < numpnts; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], pc[i]);
    }
    /* set node associations at points */
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID1, 1, 1011);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 7, 1001);

    /* define triangle connectivity and property id */
    for (i = 0; i < numtris; i++) {
        for (k = 1; k <= 3; k++) {
            vis_GroupGetEntFlag(groupedge, i + 1, k, &flags);
            if (flags) {
                efl[k - 1] = 1;
            }
            else {
                efl[k - 1] = 0;
            }
        }
        msh_SurfMeshSetTri(surfmesh, i + 1, tix[i], efl);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, tvn[i]);
        msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, 0, tpid[i]);
        /* specific sizing for region 3 */
        if (tpid[i] == 3) {
            msh_SurfMeshSetTriSizing(surfmesh, i + 1, SYS_FACE, 0, .05);
        }
        if ((i + 1) == 12) {
            msh_SurfMeshSetTriSizing(surfmesh, i + 1, SYS_EDGE, 1, .0125);
        }
    }
    /* set trimming loop */
    eix[0] = 20;
    eix[1] = 19;
    eix[2] = 18;
    eix[3] = 17;
    eix[4] = 20;
    msh_SurfMeshSetEdge(surfmesh, VIS_ON, 5, eix, VIS_OFF, NULL);

    /* set node associations at triangle edge */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 2, SYS_EDGE, 1, 1002);

    /* set node associations on triangles */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 1, SYS_FACE, 0, 1003);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID, 2, SYS_FACE, 0, 1003);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_COMPUTENORMAL, 1);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 60.);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .04);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .4);

    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam47d.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        vis_ConnectNodeAssoc(connect, VIS_MISCID1, 1, &i, &aid1);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d, aid1= %d\n", i, x[0], x[1], x[2], aid, aid1);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        /* element connectivity */
        vis_ConnectElemAssoc(connect, VIS_PROPID, 1, &i, &pid);
        printf("id= %d  ix= %d %d %d %d %d %d, pid= %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5], pid);
        /* element normals */
        vis_ConnectElemNormdv(connect, i, 1, ve);
        for (k = 0; k < nix; k++) {
            printf("        n= %e %e %e\n", ve[k][0], ve[k][1], ve[k][2]);
        }
    }
    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam47d.bdf");

    /* end objects */
    vis_ConnectEnd(connectgeo);
    vis_GroupEnd(groupface);
    vis_GroupEnd(groupedge);
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}
../_images/vistools-exam47d.gif

Figure 28-47d, Surface Mesh with Trimming Loop

6.30. Example 48, Generate a 3D Volume Tet Mesh from an STL File

This example illustrates generating a tetrahedral mesh from a surface facetization characteristic of STL file data. Generating a volume mesh from an arbitrary surface facetization requires retriangulating the surface using the SurfMesh module and directing the generated surface triangles as input to the TetMesh module for tetrahedralization.

The STL file is read using the convenience function vis_ConnectRead() in the Connect object. The initial triangles are unconnected so vis_ConnectMerge() is called to merge coincident nodes together. Feature edges and nodes are identified using standard Connect object functions. These feature edges and nodes will be flagged to be “preserved” in the surface meshing phase. The target edge length is set to be about 5 percent of the model extent. The surface mesh is generated using msh_SurfMeshGenerate(). The resulting surface mesh is input to the TetMesh module and the tetrahedral mesh is generated using msh_TetMeshGenerate(). The final mesh is output to a SDRC Universal file using the convenience function vis_ConnectWrite().

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/*----------------------------------------------------------------------
                      Generate a 3D Volume Tet Mesh from an STL or OBJ File
----------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    Vint i, k;
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vis_Connect *connect, *connectsurf, *connecttet;
    vis_Group *groupface, *groupedge, *groupnode;

    Vint numpnts, numtris;
    Vdouble x[3];
    Vint nix, tri[3], edgeflags[3];
    Vint numsurfpnts, numsurftris;
    Vint numtetpnts, numtets;
    Vint flags, flagk;
    Vdouble edgelen;
    Vfloat bbox[2][3];
    Vdouble d[3], fl;
    Vchar inpath[256], outpath[256];
    Vint filetype;

    /* check for proper number of arguments */
    if (argc < 2) {
        fprintf(stderr, "Usage: %s inputfile outputfile\n", argv[0]);
        fprintf(stderr, " inputfile is blank, 'angle.stl' is assumed\n");
        strcpy(inpath, "angle.stl");
    }
    else {
        strcpy(inpath, argv[1]);
    }
    if (argc < 3) {
        fprintf(stderr, "Usage: %s inputfile outputfile\n", argv[0]);
        fprintf(stderr, " outputfile is blank, 'exam48.unv' is assumed\n");
        strcpy(outpath, "exam48.unv");
    }
    else {
        strcpy(outpath, argv[2]);
    }
    /* detect file type */
    if (strstr(inpath, ".stl") != NULL || strstr(inpath, ".STL") != NULL) {
        filetype = SYS_FILE_STL;
    }
    else if (strstr(inpath, ".obj") != NULL) {
        filetype = SYS_FILE_OBJ;
    }
    else {
        fprintf(stderr, "Unrecognized file type, file: %s\n", inpath);
        exit(0);
    }

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* instance Connect object to hold STL or OBJ data */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    vis_ConnectSetParamf(connect, CONNECT_TOLERANCE, 0.);
    vis_ConnectRead(connect, filetype, inpath);
    if (filetype == SYS_FILE_STL) {
        printf("Read STL file complete\n");
    }
    else {
        printf("Read OBJ file complete\n");
    }
    /* determine model extent for element sizing */
    vis_ConnectExtent(connect, NULL, bbox);
    d[0] = bbox[1][0] - bbox[0][0];
    d[1] = bbox[1][1] - bbox[0][1];
    d[2] = bbox[1][2] - bbox[0][2];
    fl = sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);

    vis_ConnectNumber(connect, SYS_NODE, &numpnts);
    vis_ConnectNumber(connect, SYS_ELEM, &numtris);

    /* determine feature edges, etc. */
    vis_ConnectKernel(connect, 0);

    /* group of faces */
    groupface = vis_GroupBegin();
    vis_GroupDef(groupface, numtris, SYS_ELEM, SYS_FACE);
    vis_ConnectFaceGroup(connect, CONNECT_FREE, NULL, groupface);

    /* extract feature edges and nodes at 45. degrees */
    vis_ConnectSetGroupParamf(connect, CONNECT_FEATUREANGLE, 45.);
    vis_ConnectSetGroupParamf(connect, CONNECT_FEATUREANGLE, 60.);
    vis_ConnectSetGroupParami(connect, CONNECT_FEATURESENSE, VIS_ON);

    /* group of feature edges */
    groupedge = vis_GroupBegin();
    vis_GroupDef(groupedge, numtris, SYS_ELEM, SYS_EDGE);
    vis_ConnectEdgeGroup(connect, CONNECT_FEATURE, groupface, groupedge);

    /* group of feature nodes */
    groupnode = vis_GroupBegin();
    vis_GroupDef(groupnode, numpnts, SYS_NODE, SYS_NONE);
    vis_ConnectNodeGroup(connect, CONNECT_FEATURE, groupedge, groupnode);

    /* instance surf mesher */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numpnts, numtris);

    /* define input points with feature points preserved */
    for (i = 1; i <= numpnts; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_GroupGetIndex(groupnode, i, &flags);
        msh_SurfMeshSetPoint(surfmesh, i, x, flags);
    }
    /* define input triangles with feature edges preserved */
    for (i = 1; i <= numtris; i++) {
        vis_ConnectElemNode(connect, i, &nix, tri);
        vis_GroupGetIndex(groupedge, i, &flags);
        for (k = 1; k <= 3; k++) {
            vis_GroupGetEntFlag(groupedge, i, k, &flagk);
            if (flagk) {
                edgeflags[k - 1] = 1;
            }
            else {
                edgeflags[k - 1] = 0;
            }
        }
        msh_SurfMeshSetTri(surfmesh, i, tri, edgeflags);
    }
    /* instance Connect object to hold generated surface tris */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* set target edge length at 5 percent of model extent */
    edgelen = .05 * fl;
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, edgelen);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .1 * edgelen);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_INTSURFBACK, VIS_ON);

    /* set span angles, etc. */
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 30.);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);

    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam48.srf");
    /* generate quality surface mesh */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surface mesh generation error\n");
    }
    else {
        printf("surface mesh generation complete\n");
    }
    /* find out the number of generated nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);
    vis_ConnectWrite(connectsurf, SYS_SDRC_UNIVERSAL, "exam48srf.unv");

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts, numsurftris, 2);

    /* Fill TetMesh object with generated points on surface */
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, (Vdouble(*)[3])x);
        msh_TetMeshSetNode(tetmesh, i, x);
    }
    /* Fill TetMesh object with generated triangles */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemNode(connectsurf, i, &nix, tri);
        msh_TetMeshSetTri(tetmesh, i, tri);
    }

    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, edgelen);
    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
    }
    else {
        printf("tet mesh generation complete\n");
    }

    /* write out to Universal file */
    vis_ConnectWrite(connecttet, SYS_SDRC_UNIVERSAL, outpath);
    printf("Write Universal file complete\n");

    /* find out the number of generated nodes and elements */
    vis_ConnectNumber(connecttet, SYS_NODE, &numtetpnts);
    vis_ConnectNumber(connecttet, SYS_ELEM, &numtets);
    printf(" Number of nodes= %d\n", numtetpnts);
    printf(" Number of tets = %d\n", numtets);

    /* delete objects */
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    vis_ConnectEnd(connectsurf);
    vis_ConnectEnd(connecttet);
    msh_TetMeshEnd(tetmesh);
    vis_GroupEnd(groupface);
    vis_GroupEnd(groupedge);
    vis_GroupEnd(groupnode);
    return 0;
}

6.31. Example 48a, Generate an Anisotropic Surface Mesh and Tet Mesh

This example illustrates generating either an isotropic or anisotropic tetrahedral mesh of a simple unit cube. The surface mesh is first constructed using the SurfMesh module. The subsequent surface triangulation is used as input to the TetMesh module for tetrahedral mesh generation. The isotropic or anisotropic sizing is specified using a callback sizing function s_iso and s_aniso respectively. These functions are registered in the SurfMesh module using the function msh_SurfMeshSetFunction(). The sotropic sizing function grades the mesh from a size of .1 at x = 0. to a size of .3 at x = 1. The anisotropic sizing function enforces a uniform 4 to 1 aspect ratio at a 45 degree angle in the x,y plane.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a simple unit cube */
static Vdouble xc[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                           {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};
static Vint tefl[12][3] = {{1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 1}, {0, 1, 1},
                           {0, 1, 1}, {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {1, 1, 0}};

static Vdouble norms[12][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}}, {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/* isotropic size function */
static void
s_iso(msh_SurfMesh* surfmesh, Vobject* usrobj, Vdouble x[3], Vdouble* s)
{
    /* linear size gradation in x direction */
    *s = .02 + .2 * x[0];
}

/* anisotropic size function */
static void
s_aniso(msh_SurfMesh* surfmesh, Vobject* usrobj, Vdouble x[3], Vdouble s[3][3])
{
    Vdouble s1, s2, s3;
    /* 4 to 1 aspect ratio at 45 degree angle in x,y */
    s1 = .025;
    s[0][0] = s1 * .707;
    s[0][1] = s1 * .707;
    s[0][2] = 0.;

    s2 = .1;
    s[1][0] = s2 * -.707;
    s[1][1] = s2 * .707;
    s[1][2] = 0.;
    /* z */
    s3 = .2;
    s[2][0] = 0.;
    s[2][1] = 0.;
    s[2][2] = s3 * 1.;
}

/*----------------------------------------------------------------------
                      Generate an Anisotropic Surface Mesh and Tet Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vis_Connect *connectsurf, *connecttet;

    Vint i;
    Vint numsurfpnts, numsurftris;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[3];
    Vint iso;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 8, 12);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* define points */
    for (i = 0; i < 8; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
    }

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);

    /* set iso to 1 for isotropic, 0 for anisotropic */
    iso = 1;
    /* set isotropic sizing function */
    if (iso) {
        msh_SurfMeshSetFunction(surfmesh, SYS_FUNCTION_SIZING, (Vfunc*)s_iso, NULL);
        /* set anisotropic sizing function */
    }
    else {
        msh_SurfMeshSetFunction(surfmesh, SYS_FUNCTION_ASIZING, (Vfunc*)s_aniso, NULL);
    }
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* write generated surf mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam48a_srf.bdf");

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts, numsurftris, 2);

    /* Fill TetMesh object with generated points on surface */
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, (Vdouble(*)[3])x);
        msh_TetMeshSetNode(tetmesh, i, x);
    }
    /* Fill TetMesh object with generated triangles */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemNode(connectsurf, i, &nix, ix);
        msh_TetMeshSetTri(tetmesh, i, ix);
    }

    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    /* set isotropic sizing function */
    if (iso) {
        msh_TetMeshSetFunction(tetmesh, SYS_FUNCTION_SIZING, (Vfunc*)s_iso, NULL);
        /* set anisotropic sizing function */
    }
    else {
        msh_TetMeshSetFunction(tetmesh, SYS_FUNCTION_ASIZING, (Vfunc*)s_aniso, NULL);
    }
    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
    vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);

    /* write generated tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam48a_tet.bdf");

    /* end objects */
    vis_ConnectEnd(connectsurf);
    vis_ConnectEnd(connecttet);
    msh_SurfMeshEnd(surfmesh);
    msh_TetMeshEnd(tetmesh);
    return 0;
}

6.32. Example 48b, Generate a Surface Mesh and Tet Mesh, Non-manifold Solid

This example illustrates generating a tetrahedral mesh of a rectilinear solid containing an internal face, a void and an inclusion. The internal surface at z=1 divides the rectilinear solid in two. The first solid region from 0. <= z <= 1. contains a small void, the second solid region from 1. <= z <= 2. contains a small inclusion.

The SurfMesh module is used to perform the initial triangulation of the surfaces representing the solid. Non-manifold input support must be enabled in the SurfMesh module using the SURFMESH_NONMANIFOLD parameter in the function msh_SurfMeshSetParami(). Since the resulting triangulation is to be input to the TetMesh module for subsequent solid meshing, all internal boundary triangles must be “double backed”. This feature is enabled using the SURFMESH_INTSURFBACK parameter. In this case the SurfMesh module will double back the internal face which divides the solid as well as the faces enclosing the inclusion.

Element associations are assigned to all triangles on a face bordering the first region so that the VIS_PROPID association value of 1 will be assigned to all generated tetrahedra enclosed by the face and all other boundary faces. A VIS_PROPID association value of 2 is assigned to all triangles on a face bordering the second region. This will result in all generated tetrahedra within this region to be assigned this value. Note however that the tetrahedra generated within the inclusion are not considered to be in the second region. Finally, a VIS_PROPID of 3 is specified for the inclusion.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a cube with interior surface, a hole and an inclusion */
static Vdouble xc[28][3] = {{0., 0., 0.},
                            {1., 0., 0.},
                            {0., 1., 0.},
                            {1., 1., 0.},
                            {0., 0., 1.},
                            {1., 0., 1.},
                            {0., 1., 1.},
                            {1., 1., 1.},
                            {0., 0., 2.},
                            {1., 0., 2.},
                            {0., 1., 2.},
                            {1., 1., 2.},
                            /* hole */
                            {.1, .1, .1},
                            {.2, .1, .1},
                            {.1, .2, .1},
                            {.2, .2, .1},
                            {.1, .1, .2},
                            {.2, .1, .2},
                            {.1, .2, .2},
                            {.2, .2, .2},
                            /* inclusion */
                            {.1, .1, 1.1},
                            {.2, .1, 1.1},
                            {.1, .2, 1.1},
                            {.2, .2, 1.1},
                            {.1, .1, 1.2},
                            {.2, .1, 1.2},
                            {.1, .2, 1.2},
                            {.2, .2, 1.2}};

static Vint tris[46][3] = {{1, 5, 3},
                           {3, 5, 7},
                           {4, 6, 2},
                           {8, 6, 4},
                           {1, 2, 5},
                           {5, 2, 6},
                           {7, 4, 3},
                           {8, 4, 7},
                           {1, 4, 2},
                           {4, 1, 3},
                           {5, 8, 7},
                           {5, 6, 8},
                           {5, 9, 7},
                           {7, 9, 11},
                           {8, 10, 6},
                           {12, 10, 8},
                           {5, 6, 9},
                           {9, 6, 10},
                           {11, 8, 7},
                           {12, 8, 11},
                           {9, 12, 11},
                           {9, 10, 12},
                           /* hole */
                           {13, 15, 17},
                           {15, 19, 17},
                           {16, 14, 18},
                           {20, 16, 18},
                           {13, 17, 14},
                           {17, 18, 14},
                           {19, 15, 16},
                           {20, 19, 16},
                           {13, 14, 16},
                           {16, 15, 13},
                           {17, 19, 20},
                           {17, 20, 18},
                           /* inclusion */
                           {21, 25, 23},
                           {23, 25, 27},
                           {24, 26, 22},
                           {28, 26, 24},
                           {21, 22, 25},
                           {25, 22, 26},
                           {27, 24, 23},
                           {28, 24, 27},
                           {21, 24, 22},
                           {24, 21, 23},
                           {25, 28, 27},
                           {25, 26, 28}};

static Vint tefl[46][3] = {{1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 1, 0},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {1, 1, 0},
                           /* hole */
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 0, 1},
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 1, 0},
                           {0, 1, 1},
                           /* inclusion */
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 1, 0}};

static Vdouble norms[46][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},

                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  /* hole */
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  /* inclusion */
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
                      Generate a Surface Mesh and Tet Mesh, Non-manifold Solid
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vis_Connect *connectsurf, *connecttet;

    Vint i;
    Vint numsurfpnts, numsurftris;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[3], aid;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 28, 46);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* define points */
    for (i = 0; i < 28; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 46; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
    }
    /* set property association for each body */
    /* set all elements on a face bordering body */
    /* body 1 */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 1, SYS_ELEM, 0, 1);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 2, SYS_ELEM, 0, 1);
    /* body 2 */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 21, SYS_ELEM, 0, 2);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 22, SYS_ELEM, 0, 2);
    /* body 3, inclusion */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 45, SYS_ELEM, 0, 3);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 46, SYS_ELEM, 0, 3);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_INTSURFBACK, VIS_ON);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, 1.);

    /* write out file encapsulating meshing specifications */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam48b.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    msh_SurfMeshEnd(surfmesh);

    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* write generated surf mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam48b-srf.bdf");

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts, numsurftris, 2);

    /* Fill TetMesh object with generated points on surface */
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, (Vdouble(*)[3])x);
        msh_TetMeshSetNode(tetmesh, i, x);
    }
    /* Fill TetMesh object with generated triangles */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemNode(connectsurf, i, &nix, ix);
        msh_TetMeshSetTri(tetmesh, i, ix);
        vis_ConnectElemAssoc(connectsurf, VIS_PROPID, 1, &i, &aid);
        if (aid) {
            msh_TetMeshSetTriAssoc(tetmesh, VIS_PROPID, i, SYS_ELEM, 0, aid);
        }
    }
    vis_ConnectEnd(connectsurf);

    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    /* write out file encapsulating meshing specifications */
    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam48b.tet");

    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    msh_TetMeshEnd(tetmesh);
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
    vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);

    /* write generated tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam48b-tet.bdf");

    vis_ConnectEnd(connecttet);
    return 0;
}

6.33. Example 48c, Refine a non-Manifold 3D Volume Tet Mesh

This example builds on Example 48b. Once the first tetrahedron mesh is generated as in Example 38b, a State object with sizes is set for each element. Sizes are set to .25 for each element, then msh_TetMeshRefine() is called.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/vis/vis.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a cube with interior surface, a hole and an inclusion */
static Vdouble xc[28][3] = {{0., 0., 0.},
                            {1., 0., 0.},
                            {0., 1., 0.},
                            {1., 1., 0.},
                            {0., 0., 1.},
                            {1., 0., 1.},
                            {0., 1., 1.},
                            {1., 1., 1.},
                            {0., 0., 2.},
                            {1., 0., 2.},
                            {0., 1., 2.},
                            {1., 1., 2.},
                            /* hole */
                            {.1, .1, .1},
                            {.2, .1, .1},
                            {.1, .2, .1},
                            {.2, .2, .1},
                            {.1, .1, .2},
                            {.2, .1, .2},
                            {.1, .2, .2},
                            {.2, .2, .2},
                            /* inclusion */
                            {.1, .1, 1.1},
                            {.2, .1, 1.1},
                            {.1, .2, 1.1},
                            {.2, .2, 1.1},
                            {.1, .1, 1.2},
                            {.2, .1, 1.2},
                            {.1, .2, 1.2},
                            {.2, .2, 1.2}};

static Vint tris[46][3] = {{1, 5, 3},
                           {3, 5, 7},
                           {4, 6, 2},
                           {8, 6, 4},
                           {1, 2, 5},
                           {5, 2, 6},
                           {7, 4, 3},
                           {8, 4, 7},
                           {1, 4, 2},
                           {4, 1, 3},
                           {5, 8, 7},
                           {5, 6, 8},
                           {5, 9, 7},
                           {7, 9, 11},
                           {8, 10, 6},
                           {12, 10, 8},
                           {5, 6, 9},
                           {9, 6, 10},
                           {11, 8, 7},
                           {12, 8, 11},
                           {9, 12, 11},
                           {9, 10, 12},
                           /* hole */
                           {13, 15, 17},
                           {15, 19, 17},
                           {16, 14, 18},
                           {20, 16, 18},
                           {13, 17, 14},
                           {17, 18, 14},
                           {19, 15, 16},
                           {20, 19, 16},
                           {13, 14, 16},
                           {16, 15, 13},
                           {17, 19, 20},
                           {17, 20, 18},
                           /* inclusion */
                           {21, 25, 23},
                           {23, 25, 27},
                           {24, 26, 22},
                           {28, 26, 24},
                           {21, 22, 25},
                           {25, 22, 26},
                           {27, 24, 23},
                           {28, 24, 27},
                           {21, 24, 22},
                           {24, 21, 23},
                           {25, 28, 27},
                           {25, 26, 28}};

static Vint tefl[46][3] = {{1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 1, 0},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {1, 1, 0},
                           /* hole */
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 0, 1},
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 1, 0},
                           {0, 1, 1},
                           /* inclusion */
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 1, 0}};

static Vdouble norms[46][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},

                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  /* hole */
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  /* inclusion */
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
                      Generate a Surface Mesh and Tet Mesh, Non-manifold Solid
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vis_Connect *connectsurf, *connecttet, *connectref;
    vis_State* state;
    vis_GridFun* gf;

    Vint i, j;
    Vint numsurfpnts, numsurftris;
    Vint numnp, numel;
    Vdouble x[3], size;
    Vint nix, ix[3], aid;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 28, 46);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* define points */
    for (i = 0; i < 28; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 46; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        for (j = 0; j < 3; j++) {
            if (tefl[i][j]) {
                msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOEDGE, i + 1, SYS_EDGE, j + 1, 1);
            }
        }
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
    }
    /* set property association for each body */
    /* set all elements on a face bordering body */
    /* body 1 */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 1, SYS_ELEM, 0, 1);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 2, SYS_ELEM, 0, 1);
    /* body 2 */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 21, SYS_ELEM, 0, 2);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 22, SYS_ELEM, 0, 2);
    /* body 3, inclusion */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 45, SYS_ELEM, 0, 3);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, 46, SYS_ELEM, 0, 3);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_INTSURFBACK, VIS_ON);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, 1.);

    /* write out file encapsulating meshing specifications */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam48c.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    msh_SurfMeshEnd(surfmesh);

    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* write generated surf mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam48c-srf.bdf");

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts, numsurftris, 2);

    /* Fill TetMesh object with generated points on surface */
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, (Vdouble(*)[3])x);
        msh_TetMeshSetNode(tetmesh, i, x);
    }
    /* Fill TetMesh object with generated triangles */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemNode(connectsurf, i, &nix, ix);
        msh_TetMeshSetTri(tetmesh, i, ix);
        vis_ConnectElemAssoc(connectsurf, VIS_PROPID, 1, &i, &aid);
        if (aid) {
            msh_TetMeshSetTriAssoc(tetmesh, VIS_PROPID, i, SYS_ELEM, 0, aid);
        }
        /* flag preserved edge with a VIS_GEOEDGE association */
        for (j = 1; j <= 3; j++) {
            vis_ConnectElemEntAssoc(connectsurf, VIS_GEOEDGE, SYS_EDGE, i, j, &aid);
            if (aid) {
                msh_TetMeshSetTriAssoc(tetmesh, VIS_PROPID, i, SYS_EDGE, j, aid);
            }
        }
    }
    vis_ConnectEnd(connectsurf);

    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    /* write out file encapsulating meshing specifications */
    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam48c.tet");
    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
    vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);

    /* write generated tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam48c-tet.bdf");

    /* refine mesh */
    state = vis_StateBegin();
    gf = vis_GridFunBegin();
    vis_ConnectGridFun(connecttet, gf);
    vis_StateSetObject(state, VIS_GRIDFUN, (Vobject*)gf);
    vis_StateDef(state, numel, SYS_ELEM, SYS_NONE, VIS_SCALAR);
    for (i = 1; i <= numel; i++) {
        size = .125;
        vis_StateSetDatadv(state, i, &size);
    }
    /* refine tetmesh */
    connectref = vis_ConnectBegin();
    vis_ConnectPre(connectref, SYS_DOUBLE);
    msh_TetMeshRefine(tetmesh, state, connectref);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh refinement error\n");
        exit(1);
    }
    else {
        printf("tet mesh refinement complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectref, SYS_NODE, &numnp);
    vis_ConnectNumber(connectref, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);
    /* write refined tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectref, SYS_NASTRAN_BULKDATA, "exam48c-ref.bdf");

    msh_TetMeshEnd(tetmesh);
    vis_ConnectEnd(connecttet);
    vis_ConnectEnd(connectref);
    vis_StateEnd(state);
    vis_GridFunEnd(gf);
    return 0;
}

6.34. Example 48d, Generate a Tet Mesh, Non-manifold Solid

This example is an alteration of Example 48b and illustrates defining explicit material regions which are independent of the boundary triangle orientations. By default a void must have all bounding triangles pointing into the void region. By using the msh_SurfMeshSetTriBack() function material and void regions may be explicitly defined for the regions bounded by a triangle. The function msh_SurfMeshSetTriAssoc() also allows element associations to be defined on the front and back sides of a boundary triangle.

In this example, the hole which existed in Example 48b due to the orientation of the bounding triangles is now defined as a material region using msh_SurfMeshSetTriBack().

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a cube with interior surface, and two inclusions */
/* point coordinates */
static Vdouble xc[28][3] = {{0., 0., 0.},
                            {1., 0., 0.},
                            {0., 1., 0.},
                            {1., 1., 0.},
                            {0., 0., 1.},
                            {1., 0., 1.},
                            {0., 1., 1.},
                            {1., 1., 1.},
                            {0., 0., 2.},
                            {1., 0., 2.},
                            {0., 1., 2.},
                            {1., 1., 2.},
                            /* inclusion 1 */
                            {.1, .1, .1},
                            {.2, .1, .1},
                            {.1, .2, .1},
                            {.2, .2, .1},
                            {.1, .1, .2},
                            {.2, .1, .2},
                            {.1, .2, .2},
                            {.2, .2, .2},
                            /* inclusion 2 */
                            {.1, .1, 1.1},
                            {.2, .1, 1.1},
                            {.1, .2, 1.1},
                            {.2, .2, 1.1},
                            {.1, .1, 1.2},
                            {.2, .1, 1.2},
                            {.1, .2, 1.2},
                            {.2, .2, 1.2}};

/* triangle connectivity */
static Vint tris[46][3] = {{1, 5, 3},
                           {3, 5, 7},
                           {4, 6, 2},
                           {8, 6, 4},
                           {1, 2, 5},
                           {5, 2, 6},
                           {7, 4, 3},
                           {8, 4, 7},
                           {1, 4, 2},
                           {4, 1, 3},
                           {5, 8, 7},
                           {5, 6, 8},
                           {5, 9, 7},
                           {7, 9, 11},
                           {8, 10, 6},
                           {12, 10, 8},
                           {5, 6, 9},
                           {9, 6, 10},
                           {11, 8, 7},
                           {12, 8, 11},
                           {9, 12, 11},
                           {9, 10, 12},
                           /* inclusion 1 */
                           {13, 15, 17},
                           {15, 19, 17},
                           {16, 14, 18},
                           {20, 16, 18},
                           {13, 17, 14},
                           {17, 18, 14},
                           {19, 15, 16},
                           {20, 19, 16},
                           {13, 14, 16},
                           {16, 15, 13},
                           {17, 19, 20},
                           {17, 20, 18},
                           /* inclusion 2 */
                           {21, 25, 23},
                           {23, 25, 27},
                           {24, 26, 22},
                           {28, 26, 24},
                           {21, 22, 25},
                           {25, 22, 26},
                           {27, 24, 23},
                           {28, 24, 27},
                           {21, 24, 22},
                           {24, 21, 23},
                           {25, 28, 27},
                           {25, 26, 28}};

/* triangle right and left element associations */
static Vint ta[46][2] = {
/* outer boundary and non-manifold internal surface */
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 2},
{1, 2},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
/* inclusion 1, faces point into inclusion */
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
/* inclusion 2, faces point out of inclusion */
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2}};

/* triangle edge flags */
static Vint tefl[46][3] = {{1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 1, 0},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {1, 1, 0},
                           /* inclusion 1 */
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 0, 1},
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 1, 0},
                           {0, 1, 1},
                           /* inclusion 2 */
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 1, 0}};

/* triangle normals */
static Vdouble norms[46][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},

                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  /* inclusion 1 */
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  /* inclusion 2 */
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
                      Generate a Tet Mesh, Non-manifold Solid
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vis_Connect *connectsurf, *connecttet;

    Vint i;
    Vint numsurfpnts, numsurftris;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[3], aid;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 28, 46);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* define points */
    for (i = 0; i < 28; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 46; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
        msh_SurfMeshSetTriBack(surfmesh, i + 1, ta[i][0], ta[i][1]);
        /* set property associations */
        if (ta[i][0]) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, i + 1, SYS_ELEM, 0, ta[i][0]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, 0, ta[i][0]);
        }
        if (ta[i][1]) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, i + 1, SYS_ELEM, -1, ta[i][1]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, -1, ta[i][1]);
        }
    }
    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_INTSURFBACK, VIS_ON);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, 1.);

    /* write out file encapsulating meshing specifications */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam48d.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    msh_SurfMeshEnd(surfmesh);

    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* write generated surf mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam48d-srf.bdf");

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts, numsurftris, 2);

    /* Fill TetMesh object with generated points on surface */
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, (Vdouble(*)[3])x);
        msh_TetMeshSetNode(tetmesh, i, x);
    }
    /* Fill TetMesh object with generated triangles */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemNode(connectsurf, i, &nix, ix);
        msh_TetMeshSetTri(tetmesh, i, ix);
        vis_ConnectElemAssoc(connectsurf, VIS_PROPID, 1, &i, &aid);
        if (aid) {
            msh_TetMeshSetTriAssoc(tetmesh, VIS_PROPID, i, SYS_ELEM, 0, aid);
        }
        vis_ConnectElemAssoc(connectsurf, VIS_GEOBODY, 1, &i, &aid);
        if (aid) {
            msh_TetMeshSetTriAssoc(tetmesh, VIS_GEOBODY, i, SYS_ELEM, 0, aid);
        }
    }
    vis_ConnectEnd(connectsurf);

    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    /* write out file encapsulating meshing specifications */
    msh_TetMeshWrite(tetmesh, SYS_ASCII, "exam48d.tet");
    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    msh_TetMeshEnd(tetmesh);
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
    vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);

    /* write generated tet mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam48d-tet.bdf");

    vis_ConnectEnd(connecttet);
    return 0;
}

6.35. Example 48e, Generate a Hybrid Boundary and Volumetric Mesh

This example is an alteration of Example 48d and illustrates using VolMesh to designate different material regions to be volumetrically filled or output as a boundary triangulation. The function msh_VolMeshSetFaceFill() is used to specify the fill type for each region. The regions have been tagged as an element association using the VIS_GEOBODY association type. Regions 1 and 2 are filled with tetrahedra and regions 3 and 4 are represented by the bounding triangles. The output mesh will contain the generated tetrahedra in regions 1 and 2 and the bounding triangles of regions 3 and 4.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a cube with interior surface, and two inclusions */
/* point coordinates */
static Vdouble xc[28][3] = {{0., 0., 0.},
                            {1., 0., 0.},
                            {0., 1., 0.},
                            {1., 1., 0.},
                            {0., 0., 1.},
                            {1., 0., 1.},
                            {0., 1., 1.},
                            {1., 1., 1.},
                            {0., 0., 2.},
                            {1., 0., 2.},
                            {0., 1., 2.},
                            {1., 1., 2.},
                            /* inclusion 1 */
                            {.1, .1, .1},
                            {.2, .1, .1},
                            {.1, .2, .1},
                            {.2, .2, .1},
                            {.1, .1, .2},
                            {.2, .1, .2},
                            {.1, .2, .2},
                            {.2, .2, .2},
                            /* inclusion 2 */
                            {.1, .1, 1.1},
                            {.2, .1, 1.1},
                            {.1, .2, 1.1},
                            {.2, .2, 1.1},
                            {.1, .1, 1.2},
                            {.2, .1, 1.2},
                            {.1, .2, 1.2},
                            {.2, .2, 1.2}};

/* triangle connectivity */
static Vint tris[46][3] = {{1, 5, 3},
                           {3, 5, 7},
                           {4, 6, 2},
                           {8, 6, 4},
                           {1, 2, 5},
                           {5, 2, 6},
                           {7, 4, 3},
                           {8, 4, 7},
                           {1, 4, 2},
                           {4, 1, 3},
                           {5, 8, 7},
                           {5, 6, 8},
                           {5, 9, 7},
                           {7, 9, 11},
                           {8, 10, 6},
                           {12, 10, 8},
                           {5, 6, 9},
                           {9, 6, 10},
                           {11, 8, 7},
                           {12, 8, 11},
                           {9, 12, 11},
                           {9, 10, 12},
                           /* inclusion 1 */
                           {13, 15, 17},
                           {15, 19, 17},
                           {16, 14, 18},
                           {20, 16, 18},
                           {13, 17, 14},
                           {17, 18, 14},
                           {19, 15, 16},
                           {20, 19, 16},
                           {13, 14, 16},
                           {16, 15, 13},
                           {17, 19, 20},
                           {17, 20, 18},
                           /* inclusion 2 */
                           {21, 25, 23},
                           {23, 25, 27},
                           {24, 26, 22},
                           {28, 26, 24},
                           {21, 22, 25},
                           {25, 22, 26},
                           {27, 24, 23},
                           {28, 24, 27},
                           {21, 24, 22},
                           {24, 21, 23},
                           {25, 28, 27},
                           {25, 26, 28}};

/* triangle right and left element associations */
static Vint ta[46][2] = {
/* outer boundary and non-manifold internal surface */
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 0},
{1, 2},
{1, 2},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
{2, 0},
/* inclusion 1, faces point into inclusion */
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
{1, 3},
/* inclusion 2, faces point out of inclusion */
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2},
{4, 2}};

/* triangle edge flags */
static Vint tefl[46][3] = {{1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 1, 0},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {1, 1, 0},
                           /* inclusion 1 */
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 0, 1},
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 0, 1},
                           {1, 1, 0},
                           {1, 1, 0},
                           {1, 1, 0},
                           {0, 1, 1},
                           /* inclusion 2 */
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 0, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {0, 1, 1},
                           {1, 1, 0}};

/* triangle normals */
static Vdouble norms[46][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},

                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  /* inclusion 1 */
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  /* inclusion 2 */
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
                   Generate a Hybrid Boundary and Volumetric Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_VolMesh* volmesh;
    vis_Connect *connectsurf, *connectvol;

    Vint i;
    Vint numsurfpnts, numsurftris;
    Vint numnp, numel;
    Vint aid;
    Vdouble edgelength;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 28, 46);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* define points */
    for (i = 0; i < 28; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 46; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
        msh_SurfMeshSetTriBack(surfmesh, i + 1, ta[i][0], ta[i][1]);
        /* set property associations */
        if (ta[i][0]) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, 0, ta[i][0]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, i + 1, SYS_ELEM, 0, ta[i][0]);
        }
        if (ta[i][1]) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, i + 1, SYS_ELEM, -1, ta[i][1]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, i + 1, SYS_ELEM, -1, ta[i][1]);
        }
    }
    /* set mesh parameters */
    edgelength = .5;
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_INTSURFBACK, VIS_ON);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, edgelength);

    /* write out file encapsulating meshing specifications */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam48e.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    msh_SurfMeshEnd(surfmesh);

    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* write generated surf mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam48e-srf.bdf");

    /* create VolMesh object */
    volmesh = msh_VolMeshBegin();
    msh_VolMeshDef(volmesh, numsurfpnts, numsurftris);

    /* Fill VolMesh object with generated surface mesh */
    msh_VolMeshConnect(volmesh, connectsurf);

    /* Define fill regions */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemAssoc(connectsurf, VIS_GEOBODY, 1, &i, &aid);
        /* tet fill for regions 1 and 2 */
        if (aid == 1 || aid == 2) {
            msh_VolMeshSetFaceFill(volmesh, i, VOLMESH_FILL_TET);
            /* boundary output for regions 3 and 4 */
        }
        else {
            msh_VolMeshSetFaceFill(volmesh, i, VOLMESH_FILL_BOUNDARY);
        }
    }
    msh_VolMeshSetParamd(volmesh, VIS_MESH_EDGELENGTH, edgelength);
    vis_ConnectEnd(connectsurf);

    /* create Connect object to hold generated vol mesh */
    connectvol = vis_ConnectBegin();
    vis_ConnectPre(connectvol, SYS_DOUBLE);
    vis_ConnectDef(connectvol, 0, 0);

    /* write out file encapsulating meshing specifications */
    msh_VolMeshWrite(volmesh, SYS_ASCII, "exam48e.vol");
    /* generate mesh */
    msh_VolMeshGenerate(volmesh, connectvol);
    if (msh_VolMeshError(volmesh)) {
        printf("hybrid mesh generation error\n");
        exit(1);
    }
    else {
        printf("hybrid mesh generation complete\n");
    }
    msh_VolMeshEnd(volmesh);
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectvol, SYS_NODE, &numnp);
    vis_ConnectNumber(connectvol, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);

    /* write generated hybrid mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectvol, SYS_NASTRAN_BULKDATA, "exam48e-vol.bdf");

    vis_ConnectEnd(connectvol);
    return 0;
}

6.36. Example 48f, Generate a Surface Mesh and Pyr/Tet Mesh Interior Sizing

This example is an alteration of Example 48a and illustrates specifying interior sizing. A boundary depth size is specified using msh_VolMeshSetFaceDepthSizing(). This function allows a depth and size to be set for each boundary face. The specified size should be close to the size of the associated boundary face. This feature is useful for generating isotropic boundary layers. In addition, a user specified element size is defined with a simple geometry shape in the interior of the domain using msh_VolMeshSetGeomSizing(). In this case a .05 by .15 by .25 ellipsoid. A quadrilateral surface mesh is generated so that a pyramidal transition layer will be generated by VolMesh.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/system.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a simple unit cube */
static Vdouble xc[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                           {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};
static Vint tefl[12][3] = {{1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 1}, {0, 1, 1},
                           {0, 1, 1}, {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {1, 1, 0}};

static Vdouble norms[12][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}}, {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
                      Generate a Surface Mesh and Pyr/Tet Mesh Interior Sizing
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_VolMesh* volmesh;
    vis_Connect *connectsurf, *connectvol;

    Vint i;
    Vint numsurfpnts, numsurftris;
    Vint numnp, numel;
    Vdouble x[3];
    Vint nix, ix[4];
    Vdouble isize, bsize;
    Vdouble xo[3], xa[3], xb[3], d[3];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 8, 12);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* define points */
    for (i = 0; i < 8; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
    }
    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 2);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_SHAPE, VIS_SHAPEQUAD);
    /* boundary size */
    bsize = .05;
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, bsize);
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* create VolMesh object */
    volmesh = msh_VolMeshBegin();
    msh_VolMeshDef(volmesh, numsurfpnts, numsurftris);
    /* interior ellipsoidal shape for sizing */
    /* positioned in center of cube */
    xo[0] = .5;
    xo[1] = .5;
    xo[2] = .5;
    /* oriented along x axis and at 45 degrees in y-z plane */
    xa[0] = 1.0;
    xa[1] = .5;
    xa[2] = .5;
    xb[0] = .5;
    xb[1] = 1.0;
    xb[2] = 1.0;
    /* lengths of ellipsoid along each axis */
    d[0] = .10;
    d[1] = .20;
    d[2] = .30;
    msh_VolMeshSetGeomSizing(volmesh, VIS_MESH_GEOMSPHERE, xo, xa, xb, d, 0.02);

    /* interior sizing */
    isize = .2;
    msh_VolMeshSetParamd(volmesh, VIS_MESH_EDGELENGTH, isize);

    /* Fill VolMesh object with generated points on surface */
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, (Vdouble(*)[3])x);
        msh_VolMeshSetNode(volmesh, i, x);
    }
    /* Fill VolMesh object with generated triangles */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemNode(connectsurf, i, &nix, ix);
        msh_VolMeshSetFace(volmesh, i, VIS_SHAPEQUAD, 0, 0, ix);
    }
    /* set boundary depth and size */
    for (i = 1; i <= numsurftris; i++) {
        msh_VolMeshSetFaceDepthSizing(volmesh, i, 0.1, bsize);
    }
    /* create Connect object to hold generated pyr/tet mesh */
    connectvol = vis_ConnectBegin();
    vis_ConnectPre(connectvol, SYS_DOUBLE);
    vis_ConnectDef(connectvol, 0, 0);

    msh_VolMeshWrite(volmesh, SYS_ASCII, "exam48f.vol");
    /* generate mesh */
    msh_VolMeshGenerate(volmesh, connectvol);
    if (msh_VolMeshError(volmesh)) {
        printf("Vol mesh generation error\n");
        exit(1);
    }
    else {
        printf("Vol mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectvol, SYS_NODE, &numnp);
    vis_ConnectNumber(connectvol, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);

    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectvol, SYS_NASTRAN_BULKDATA, "exam48f.bdf");

    /* end objects */
    vis_ConnectEnd(connectsurf);
    vis_ConnectEnd(connectvol);
    msh_SurfMeshEnd(surfmesh);
    msh_VolMeshEnd(volmesh);
    return 0;
}
../_images/vistools-exam48f.gif

Figure 28-48f, Generated Pyr/Tet Mesh with Interor Sizing

6.37. Example 48g, Generate a Surface Mesh, Curve Mesh and Tet Mesh

This example is an alteration of Example 48a and illustrates embedding a set of edges in the interior of the volume. The boundary triangles are generated using SurfMesh and the interior edges are generated using CurvMesh. The Connect object containing the generated edge mesh is appended and merged to the Connect object generated by SurfMesh. It is possible for the generated edges to connect to the boundary nodes on the surface mesh. The combined Connect object is entered into TetMesh as input using msh_TetMeshConnect().

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* a unit cube and interior edge endpoints */
static Vdouble xc[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                           {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};
static Vint tefl[12][3] = {{1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 1}, {0, 1, 1},
                           {0, 1, 1}, {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {1, 1, 0}};

static Vdouble norms[12][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},
                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},
                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}}, {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

static Vdouble xe[2][3] = {{0.25, 0.25, 0.25}, {0.75, 0.75, 0.25}};

static Vint lin[2] = {1, 2};

static Vdouble tangs[2][3] = {{1., 0., 0.}, {0., 1., 0.}};

/*----------------------------------------------------------------------
                      Generate a Surface Mesh, Curve Mesh and Tet Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_CurvMesh* curvmesh;
    msh_TetMesh* tetmesh;
    vis_Connect *connectsurf, *connectcurv, *connecttet;

    Vint i;
    Vint numsurfpnts, numsurftris;
    Vint numcurvpnts, numcurvlins;
    Vint numnp, numel;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 8, 12);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* define points */
    for (i = 0; i < 8; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
    }
    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .1);

    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* generate interior curve mesh */
    /* create CurvMesh object */
    curvmesh = msh_CurvMeshBegin();
    msh_CurvMeshDef(curvmesh, 2, 1);

    /* create Connect object */
    connectcurv = vis_ConnectBegin();
    vis_ConnectPre(connectcurv, SYS_DOUBLE);
    vis_ConnectDef(connectcurv, 0, 0);

    /* define points */
    for (i = 0; i < 2; i++) {
        msh_CurvMeshSetPoint(curvmesh, i + 1, xe[i], 1);
    }
    /* define line connectivity */
    msh_CurvMeshSetLine(curvmesh, 1, lin);
    msh_CurvMeshSetLineTang(curvmesh, 1, tangs);
    /* set mesh parameters */
    msh_CurvMeshSetParami(curvmesh, VIS_MESH_MAXI, 3);
    msh_CurvMeshSetParamd(curvmesh, VIS_MESH_EDGELENGTH, .005);

    /* generate */
    msh_CurvMeshGenerate(curvmesh, connectcurv);
    if (msh_CurvMeshError(curvmesh)) {
        printf("curv mesh generation error\n");
        exit(1);
    }
    else {
        printf("curv mesh generation complete\n");
    }
    /* print number of curve nodes and elements */
    vis_ConnectNumber(connectcurv, SYS_NODE, &numcurvpnts);
    vis_ConnectNumber(connectcurv, SYS_ELEM, &numcurvlins);
    printf(" Number of nodes= %d\n", numcurvpnts);
    printf(" Number of lines= %d\n", numcurvlins);

    /* add curve mesh to surface mesh */
    vis_ConnectAppend(connectsurf, connectcurv);
    vis_ConnectMerge(connectsurf, NULL);

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts, numsurftris, 2);
    /* fill with boundary elements */
    msh_TetMeshConnect(tetmesh, connectsurf);
    msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, .1);

    /* create Connect object to hold generated tet mesh */
    connecttet = vis_ConnectBegin();
    vis_ConnectPre(connecttet, SYS_DOUBLE);
    vis_ConnectDef(connecttet, 0, 0);

    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connecttet);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
    vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);
    vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam48g.bdf");

    /* end objects */
    vis_ConnectEnd(connectsurf);
    vis_ConnectEnd(connectcurv);
    vis_ConnectEnd(connecttet);
    msh_SurfMeshEnd(surfmesh);
    msh_CurvMeshEnd(curvmesh);
    msh_TetMeshEnd(tetmesh);
    return 0;
}

6.38. Example 49, Generate 3D Volume Tet Model and Write to .vdm File

This example illustrates the use of CEETRON Mesh mesh generation and global modules and VdmTools data management modules to build a complete finite element description of a a cantilever beam and write it to a VdmTools native format (.vdm) file.

The surface geometry of the cantilever beam is entered into the SurfMesh module as a set of points, connecting triangles and preserved edges. Two sets of triangle “face” associations are defined at the ends of the beam to identify generated nodes to apply restraints and loading conditions. The nodes for restraints are identified by node association VIS_MISCID1 and nodes for loading are identified by node association VIS_MISCID2. The surface mesh is generated and input to the TetMesh module for tetrahedralization. The surface mesh is not altered by the tet meshing process. All surface triangles will be preserved as faces of tetrahedra and all surface nodes and their numbering are unaltered. Once the tetrahedral mesh is complete, a Model object hierarchy is generated to hold the generated mesh and all required material properties, restraints, loading and solution properties. The Model object is a container for other objects which provide a complete description of a finite element model. The Connect object containing the node coordinates and element connectivity is registered in the Model object. In this example material properties, element properties, restraints cases and load cases are registered in Model as HashTable objects of MProp, EProp, RCase and LCase objects respectively. Two solution property objects, SProp, are created to control the solution for each of the two load cases and are entered into a List object which is subsequently registered in the Model object.

One the Model object is fully populated with it’s component objects the vis_ModelWrite() function is used to write the model to a file in NASTRAN Bulk Data Format. Then the VdmTools modules NatLib, DataFun and LMan, are used to export the model to a native .vdm file. Finally the model hierarchy is deleted.

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/vdm/vdm.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

/* points defining corners of cantilever beam */
static Vdouble pnts[8][3] = {{0., 0., 0.},  {1., 0., 0.},  {0., 1., 0.},  {1., 1., 0.},
                             {0., 0., 10.}, {1., 0., 10.}, {0., 1., 10.}, {1., 1., 10.}};

/* triangles defining surface of cantilever beam */
static Vint tris[12][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3}, {8, 4, 7}, {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}};

/* triangles edges to be preserved */
static Vint efls[12][3] = {{1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 0}, {0, 0, 1},
                           {0, 1, 0}, {0, 0, 1}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

/*----------------------------------------------------------------------
                      Generate 3D Volume Tet Model and Write to .vdm File
----------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    Vint i, k;
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vis_Connect* connectsurf;
    vis_Group *groupface, *groupnode;

    vis_Model* model;
    vis_Connect* connect;
    vis_GridFun* gridfun;
    vis_MProp* mprop;
    vis_EProp* eprop;
    vis_SProp* sprop;
    vis_RCase* rcase;
    vis_LCase* lcase;
    vsy_HashTable *mphash, *ephash, *rchash, *lchash;
    vsy_List* splist;

    vdm_DataFun* datafun;
    vdm_NatLib* natlib;
    vdm_LMan* lman;

    Vint numpnts, numtris;
    Vdouble x[3];
    Vint nix, tri[6];
    Vint numsurfpnts, numsurftris;
    Vint numtetpnts, numtets;
    Vint flag, flagno, maxi;
    Vdouble edgelen;
    Vchar outpath[256];
    Vint no;
    Vint aid;
    Vdouble v[10][3], temp;

    /* check for proper number of arguments */
    if (argc < 2) {
        fprintf(stderr, "Usage: %s outputfile\n", argv[0]);
        fprintf(stderr, " outputfile is blank, exam49vdm.vdm is assumed\n");
        strcpy(outpath, "exam49vdm.vdm");
    }
    else {
        strcpy(outpath, argv[1]);
    }

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* element order */
    maxi = 3;

    numpnts = 8;
    numtris = 12;
    /* instance surf mesher */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numpnts, numtris);

    /* define input points */
    for (i = 1; i <= numpnts; i++) {
        msh_SurfMeshSetPoint(surfmesh, i, pnts[i - 1], 0);
    }

    /* define input triangles and preserved edge flags */
    for (i = 1; i <= numtris; i++) {
        msh_SurfMeshSetTri(surfmesh, i, tris[i - 1], efls[i - 1]);
    }
    /* define node associations at z=0. for restraints */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID1, 9, SYS_FACE, 0, 1);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID1, 10, SYS_FACE, 0, 1);

    /* define node associations at z=10. for loads */
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID2, 11, SYS_FACE, 0, 1);
    msh_SurfMeshSetTriAssoc(surfmesh, VIS_MISCID2, 12, SYS_FACE, 0, 1);

    /* instance Connect object to hold generated surface tris */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* set target edge length */
    edgelen = .50;
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, edgelen);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .2 * edgelen);

    /* set span angles, etc. */
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 30.);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINANGLE, 20.);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, maxi);

    /* generate quality surface mesh */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surface mesh generation error\n");
        exit(1);
    }
    else {
        printf("surface mesh generation complete\n");
    }
    /* find out the number of generated nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfpnts);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurftris);
    printf(" Number of nodes= %d\n", numsurfpnts);
    printf(" Number of tris = %d\n", numsurftris);

    /* create TetMesh object */
    tetmesh = msh_TetMeshBegin();
    msh_TetMeshDef(tetmesh, numsurfpnts, numsurftris, maxi);

    /* fill TetMesh object with generated points on surface */
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectCoordsdv(connectsurf, 1, &i, (Vdouble(*)[3])x);
        msh_TetMeshSetNode(tetmesh, i, x);
    }
    /* fill TetMesh object with generated triangles */
    for (i = 1; i <= numsurftris; i++) {
        vis_ConnectElemNode(connectsurf, i, &nix, tri);
        msh_TetMeshSetTri(tetmesh, i, tri);
    }
    /* create Connect object to hold generated tet mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, edgelen);

    /* generate mesh */
    msh_TetMeshGenerate(tetmesh, connect);
    if (msh_TetMeshError(tetmesh)) {
        printf("tet mesh generation error\n");
        exit(1);
    }
    else {
        printf("tet mesh generation complete\n");
    }
    /* find out the number of generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numtetpnts);
    vis_ConnectNumber(connect, SYS_ELEM, &numtets);
    printf(" Number of nodes= %d\n", numtetpnts);
    printf(" Number of tets = %d\n", numtets);

    vis_ConnectKernel(connect, 0);

    /* set property identifier 1 for each element */
    for (i = 1; i <= numtets; i++) {
        vis_ConnectSetElemAssoc(connect, VIS_PROPID, i, 1);
    }
    /* instance a GridFun object */
    gridfun = vis_GridFunBegin();
    vis_ConnectGridFun(connect, gridfun);

    /* create Model object hierarchy */
    model = vis_ModelBegin();

    /* material and element property hash tables */
    mphash = vsy_HashTableBegin();
    ephash = vsy_HashTableBegin();

    /* restraint and load case hash tables */
    rchash = vsy_HashTableBegin();
    lchash = vsy_HashTableBegin();

    /* solution property list */
    splist = vsy_ListBegin();

    /* isotropic material 1 */
    mprop = vis_MPropBegin();
    vis_MPropDef(mprop, SYS_MAT_ISOTROPIC);
    vis_MPropSetValued(mprop, MPROP_E, 1.e+7);
    vis_MPropSetValued(mprop, MPROP_NU, .3);
    vis_MPropSetValued(mprop, MPROP_DENSITY, 0.0000133);
    vis_MPropSetValued(mprop, MPROP_A, 0.000254);
    vis_MPropSetValued(mprop, MPROP_TREF, 70.0);
    vsy_HashTableInsert(mphash, 1, mprop);

    /* solid property 1 */
    eprop = vis_EPropBegin();
    vis_EPropDef(eprop, VIS_ELEM_SOLID);
    vis_EPropSetValuei(eprop, EPROP_MID, 1);
    vsy_HashTableInsert(ephash, 1, eprop);

    /* restraint case 1 */
    rcase = vis_RCaseBegin();
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectNodeAssoc(connectsurf, VIS_MISCID1, 1, &i, &aid);
        if (aid) {
            vis_RCaseSetSPC(rcase, i, SYS_DOF_TX, RCASE_FIXED, NULL, 0);
            vis_RCaseSetSPC(rcase, i, SYS_DOF_TY, RCASE_FIXED, NULL, 0);
            vis_RCaseSetSPC(rcase, i, SYS_DOF_TZ, RCASE_FIXED, NULL, 0);
        }
    }
    vsy_HashTableInsert(rchash, 1, rcase);

    /* load node associations to query for element faces */
    groupnode = vis_GroupBegin();
    vis_GroupDef(groupnode, numtetpnts, SYS_NODE, SYS_NONE);
    for (i = 1; i <= numsurfpnts; i++) {
        vis_ConnectNodeAssoc(connectsurf, VIS_MISCID2, 1, &i, &aid);
        vis_GroupSetIndex(groupnode, i, aid);
    }
    /* build face group to query for element faces */
    groupface = vis_GroupBegin();
    vis_GroupDef(groupface, numtets, SYS_ELEM, SYS_FACE);
    vis_ConnectFaceGroup(connect, CONNECT_CONTAINED, groupnode, groupface);

    /* load case 1, distributed load */
    lcase = vis_LCaseBegin();
    vis_LCaseSetObject(lcase, VIS_GRIDFUN, gridfun);
    for (k = 0; k < 10; k++) {
        v[k][0] = 0.;
        v[k][1] = 1.;
        v[k][2] = 0.;
    }
    for (i = 1; i <= numtets; i++) {
        vis_GroupGetIndex(groupface, i, &flag);
        if (flag == 0)
            continue;
        for (no = 1; no <= 6; no++) {
            vis_GroupGetEntFlag(groupface, i, no, &flagno);
            if (flagno) {
                vis_LCaseSetDistdv(lcase, SYS_FACE, i, no, LCASE_TRAC, (Vdouble*)v);
            }
        }
    }
    vsy_HashTableInsert(lchash, 1, lcase);

    /* load case 2, thermal load, soak temperature of 1000. */
    lcase = vis_LCaseBegin();
    temp = 1000.;
    for (i = 1; i <= numtetpnts; i++) {
        vis_LCaseSetConcdv(lcase, i, LCASE_TEMP, &temp);
    }
    vsy_HashTableInsert(lchash, 2, lcase);

    /* solution step 1 */
    sprop = vis_SPropBegin();
    vis_SPropDef(sprop, SYS_SOL_STATIC);
    vis_SPropSetValuec(sprop, SPROP_TITLE, (Vchar*)"Example 49");
    vis_SPropSetValuei(sprop, SPROP_ANALYSIS, SYS_ANALYSIS_STRUCTURAL);
    vis_SPropSetValuei(sprop, SPROP_RCASE, 1);
    vis_SPropSetValued(sprop, SPROP_RCASE_FACTOR, 1.);
    vis_SPropSetValuei(sprop, SPROP_LCASE_NUM, 1);
    vis_SPropSetValuei(sprop, SPROP_LCASE, 1);
    vis_SPropSetValued(sprop, SPROP_LCASE_FACTOR, 1.);
    vsy_ListInsert(splist, 1, sprop);

    /* solution step 2 */
    sprop = vis_SPropBegin();
    vis_SPropDef(sprop, SYS_SOL_STATIC);
    vis_SPropSetValuei(sprop, SPROP_ANALYSIS, SYS_ANALYSIS_STRUCTURAL);
    vis_SPropSetValuei(sprop, SPROP_RCASE, 1);
    vis_SPropSetValued(sprop, SPROP_RCASE_FACTOR, 1.);
    vis_SPropSetValuei(sprop, SPROP_LCASE_NUM, 1);
    vis_SPropSetValuei(sprop, SPROP_LCASE, 2);
    vis_SPropSetValued(sprop, SPROP_LCASE_FACTOR, 1.);
    vis_SPropSetValuei(sprop, SPROP_THERMALSTRAIN, SYS_ON);
    vsy_ListInsert(splist, 2, sprop);
    /* register Connect in Model */
    vis_ModelSetObject(model, VIS_CONNECT, connect);

    /* register property hashtables and lists in Model */
    vis_ModelSetHashTable(model, VIS_MPROP, mphash);
    vis_ModelSetHashTable(model, VIS_EPROP, ephash);
    vis_ModelSetList(model, VIS_SPROP, splist);

    /* register case hashtables in Model */
    vis_ModelSetHashTable(model, VIS_RCASE, rchash);
    vis_ModelSetHashTable(model, VIS_LCASE, lchash);
    printf("model definition complete\n");

    /* write NASTRAN Bulk Data File */
    vis_ModelWrite(model, SYS_NASTRAN_BULKDATA, (Vchar*)"exam49vdm.bdf");
    printf("model written to NASTRAN Bulk Data file %s\n", "exam49vdm.bdf");
    vis_ModelWrite(model, SYS_ABAQUS_INPUT, (Vchar*)"exam49vdm.inp");
    printf("model written to ABAQUS Input Data file %s\n", "exam49vdm.inp");

    /* save model to .vdm */
    natlib = vdm_NatLibBegin();
    datafun = vdm_DataFunBegin();
    lman = vdm_LManBegin();
    vdm_NatLibDataFun(natlib, datafun);
    vdm_DataFunSetConvention(datafun, VDM_CONVENTION_DOUBLE);
    vdm_DataFunSetStatus(datafun, VDM_STATUS_NEW);
    vdm_DataFunOpen(datafun, 0, outpath, VDM_NATIVE);
    vdm_LManSetObject(lman, VDM_DATAFUN, datafun);
    vdm_LManSaveModel(lman, model);
    vdm_DataFunClose(datafun);
    vdm_LManEnd(lman);
    vdm_DataFunEnd(datafun);
    vdm_NatLibEnd(natlib);
    printf("model saved on native .vdm output file %s\n", outpath);

    /* delete objects */
    msh_SurfMeshEnd(surfmesh);
    vis_ConnectEnd(connectsurf);
    msh_TetMeshEnd(tetmesh);
    vis_GridFunEnd(gridfun);
    vis_GroupEnd(groupface);
    vis_GroupEnd(groupnode);

    /* delete model heirarchy */
    vis_ConnectEnd(connect);
    vsy_HashTableForEach(ephash, (void (*)(Vobject*))vis_EPropEnd);
    vsy_HashTableEnd(ephash);
    vsy_HashTableForEach(mphash, (void (*)(Vobject*))vis_MPropEnd);
    vsy_HashTableEnd(mphash);
    vsy_ListForEach(splist, (void (*)(Vobject*))vis_SPropEnd);
    vsy_ListEnd(splist);
    vsy_HashTableForEach(rchash, (void (*)(Vobject*))vis_RCaseEnd);
    vsy_HashTableEnd(rchash);
    vsy_HashTableForEach(lchash, (void (*)(Vobject*))vis_LCaseEnd);
    vsy_HashTableEnd(lchash);
    vis_ModelEnd(model);
    return 0;
}

6.39. Example 50, Generate a 3D Volume Tet Mesh with No Interior Points

This example illustrates the use of CEETRON Mesh mesh generation and Connect modules to generate a tetrahedral mesh with no interior points. The basic idea is to use the TetMesh module with interior point refinement disabled. The TetMesh module is not guaranteed to generate interior points given an arbitrary surface triangulation. If any interior points are generated, then all tetrahedra connected to the interior points are examined and if any of these tetrahedra have a triangular face exposed on the surface, then the longest edge of the exposed triangle is split. The Connect object is used to perform all adjacency queries, element edge length computations and edge splitting operations on the surface triangulation. The refined surface mesh is then used as input for another attempt by the TetMesh module to generate a tetrahedral mesh with no interior points.

The original input triangulation is a prism defined by 8 triangles. This prism may not be meshed with no interior points without refining the surface triangulation. The example iterates until the TetMesh module succeeds in generating a tetrahedral mesh with no addition points generated.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[6][3] = {{0., 0., 0.}, {2., 0., 0.}, {1., 1., 0.}, {0., 0., 1.}, {2., 0., 1.}, {1., 1., 1.}};

static Vint tris[8][3] = {{1, 3, 2}, {4, 5, 6}, {1, 2, 4}, {2, 5, 4}, {2, 3, 5}, {5, 3, 6}, {6, 3, 1}, {6, 1, 4}};

void
loadsurface(vis_Connect* connectsrf);
void
readsurface(vis_Connect* connectsrf, Vchar* path);
int
generatetetmesh(vis_Connect* connectsrf, vis_Connect* connecttet);
void
refinesurface(vis_Connect* connectsrf, vis_Connect* connecttet);

/*----------------------------------------------------------------------
                      Generate a 3D Volume Tet Mesh with No Interior Points
----------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    vis_Connect* connectsrf;
    vis_Connect* connecttet;
    Vint numnod, numtri;
    Vint numnp, numel;
    Vint istat, imesh, ifail;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create connect object of surface */
    connectsrf = vis_ConnectBegin();
    vis_ConnectPre(connectsrf, SYS_DOUBLE);
    vis_ConnectDef(connectsrf, 0, 0);
    /* load surface from static arrays */
    if (argc < 2) {
        loadsurface(connectsrf);
        /* read surface from TetMeshWrite file */
    }
    else {
        readsurface(connectsrf, argv[1]);
    }
    /* convert to dynamic mode */
    vis_ConnectMode(connectsrf, CONNECT_DYNAMIC);
    /* create tet mesh given surface triangles */
    /* enter refinement loop, try 3 times */
    for (imesh = 0; imesh < 100; imesh++) {
        printf("iteration= %d\n", imesh);
        vis_ConnectNumber(connectsrf, SYS_NODE, &numnod);
        vis_ConnectNumber(connectsrf, SYS_ELEM, &numtri);
        printf("numnod= %d, numtri= %d\n", numnod, numtri);

        connecttet = vis_ConnectBegin();
        vis_ConnectPre(connecttet, SYS_DOUBLE);
        vis_ConnectDef(connecttet, 0, 0);
        /* generate tet mesh */
        istat = generatetetmesh(connectsrf, connecttet);
        if (istat) {
            ifail = 0;
            printf("TetMesh generation failure\n");
            break;
        }
        vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
        vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
        printf("numnp= %d, numel= %d\n", numnp, numel);
        /* check for no interior points */
        if (numnp == numnod) {
            ifail = 0;
            printf("no interior points generated\n");
            vis_ConnectWrite(connecttet, SYS_NASTRAN_BULKDATA, "exam50.bdf");
            break;
        }
        printf("number of interior points= %d \n", numnp - numnod);
        ifail = 1;
        /* refine surface mesh */
        refinesurface(connectsrf, connecttet);
        vis_ConnectEnd(connecttet);
    }
    /* end objects */
    vis_ConnectEnd(connectsrf);
    if (ifail == 0) {
        vis_ConnectEnd(connecttet);
    }
    return 0;
}

void
loadsurface(vis_Connect* connectsrf)
{
    Vint i;

    printf("load surface from static arrays\n");
    /* set nodes */
    for (i = 0; i < 6; i++) {
        vis_ConnectSetCoordsdv(connectsrf, i + 1, coords[i]);
    }
    /* set triangles */
    for (i = 0; i < 8; i++) {
        vis_ConnectSetTopology(connectsrf, i + 1, VIS_SHAPETRI, 2, 0, 0);
        vis_ConnectSetElemNode(connectsrf, i + 1, tris[i]);
    }
}

void
readsurface(vis_Connect* connectsrf, Vchar* path)
{
    msh_TetMesh* tetmesh;
    vis_Connect* connect;

    printf("read surface from file: %s\n", path);
    /* create tetmesh object to import TetMeshWrite file */
    tetmesh = msh_TetMeshBegin();
    /* read file */
    if (strstr(path, ".tet") != NULL) {
        msh_TetMeshRead(tetmesh, SYS_ASCII, path);
    }
    else if (strstr(path, ".btet") != NULL) {
        msh_TetMeshRead(tetmesh, SYS_BINARY, path);
    }
    /* get internal connect object */
    msh_TetMeshGetConnect(tetmesh, &connect);
    /* copy it */
    vis_ConnectCopy(connectsrf, connect);
    /* destroy tetmesh object */
    msh_TetMeshEnd(tetmesh);
}

int
generatetetmesh(vis_Connect* connectsrf, vis_Connect* connecttet)
{
    Vint i;
    msh_TetMesh* tetmesh;
    Vint numnod, numtri;
    Vdouble x[3];
    Vint nix, ix[3];
    Vint ierr;

    /* create tetmesh object */
    tetmesh = msh_TetMeshBegin();
    vis_ConnectNumber(connectsrf, SYS_NODE, &numnod);
    vis_ConnectNumber(connectsrf, SYS_ELEM, &numtri);
    msh_TetMeshDef(tetmesh, numnod, numtri, 2);
    /* define nodes */
    for (i = 1; i <= numnod; i++) {
        vis_ConnectCoordsdv(connectsrf, 1, &i, (Vdouble(*)[3])x);
        msh_TetMeshSetNode(tetmesh, i, x);
    }
    /* define tris */
    for (i = 1; i <= numtri; i++) {
        vis_ConnectElemNode(connectsrf, i, &nix, ix);
        msh_TetMeshSetTri(tetmesh, i, ix);
    }
    /* try for no interior points */
    msh_TetMeshSetParami(tetmesh, VIS_MESH_INTERREFINE, VIS_OFF);
    /* generate */
    msh_TetMeshGenerate(tetmesh, connecttet);
    ierr = msh_TetMeshError(tetmesh);

    msh_TetMeshEnd(tetmesh);
    return ierr;
}

void
refinesurface(vis_Connect* connectsrf, vis_Connect* connecttet)
{
    Vint i, j, k;
    Vint numtrinod;
    Vint numtetnod;
    Vint nix, ix[4];
    Vint nixa, ixa[1000];
    Vint nint, next, kext[3];
    Vint nixext, ixext;
    Vint nixed, ixed[3];
    Vdouble xa[2][3], xc[3];
    Vint ntrinod;
    Vdouble s, smax;
    Vint kmax = 0;

    vis_ConnectNumber(connectsrf, SYS_NODE, &numtrinod);
    vis_ConnectNumber(connecttet, SYS_NODE, &numtetnod);
    vis_ConnectKernel(connecttet, 0);

    ntrinod = numtrinod;
    /* loop through interior nodes, get connected tets */
    for (i = numtrinod + 1; i <= numtetnod; i++) {
        vis_ConnectNodeElem(connecttet, i, &nixa, ixa);
        /* loop through adjacent tets, get exterior nodes */
        for (j = 0; j < nixa; j++) {
            vis_ConnectElemNode(connecttet, ixa[j], &nix, ix);
            nint = 0;
            next = 0;
            for (k = 0; k < nix; k++) {
                if (ix[k] <= numtrinod) {
                    kext[next] = ix[k];
                    next += 1;
                }
                else {
                    nint += 1;
                }
            }
            /* if more than one interior point, skip */
            if (nint != 1)
                continue;
            vis_ConnectNodeAdj(connectsrf, 3, kext, &nixext, &ixext);
            /* if not exactly one surface triangle, skip */
            if (nixext != 1)
                continue;
            /* split longest edge */
            smax = 0.;
            for (k = 1; k <= 3; k++) {
                vis_ConnectSizeElemdv(connectsrf, SYS_EDGE, ixext, k, &s);
                if (s >= smax) {
                    smax = s;
                    kmax = k;
                }
            }
            /* generate point at center of edge */
            vis_ConnectElemCon(connectsrf, SYS_EDGE, ixext, kmax, &nixed, ixed);
            vis_ConnectCoordsdv(connectsrf, nixed, ixed, xa);
            xc[0] = (xa[0][0] + xa[1][0]) / 2.;
            xc[1] = (xa[0][1] + xa[1][1]) / 2.;
            xc[2] = (xa[0][2] + xa[1][2]) / 2.;
            ntrinod += 1;
            vis_ConnectSetCoordsdv(connectsrf, ntrinod, xc);
            vis_ConnectSplitElem(connectsrf, SYS_EDGE, ixext, kmax, ntrinod);
            printf("split elem= %d, edge= %d, node= %d\n", ixext, kmax, ntrinod);
            break;
        }
    }
}

6.40. Example 51, Tesselate and Mesh a Bounded Region in a Plane

This example illustrates the use of the tesselation method in the Connect module, vis_ConnectTess(). This method takes as input a two-dimensional region bounded by overlapping arcs and straight segments, tesselates the arcs into straight segments, then breaks the resulting segments at their points of intersection. The resulting segments inherit the associations of the input arcs and segments and can be used as input to the TriMesh module to generate a triangulation of the original region.

The example starts with a set of overlapping two-dimensional regions defined by straight segments. Each segment is assigned a region with a VIS_PROPID element association. vis_ConnectTess() is then invoked to generate a Connect object with non-intersecting segments. This is used, in turn, as the boundary for a two-dimensional tesselation of the interior of the boundary using TriMesh. No interior points are generated by TriMesh.

The initial triangulation generated with TriMesh is then used as input for a quality triangulation using SurfMesh. The final triangulation is written as a NATRAN bulk data file. However, in order to illustrate the multiple element associations inherited by overlapping regions, these associations are sent to the output console.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble x[20][3] = {{1., 0., 0.}, {4., 0., 0.}, {0., 3., 0.}, {1., 3., 0.}, {4., 3., 0.}, {5., 3., 0.}, {2., 4., 0.},
                           {4., 4., 0.}, {1., 5., 0.}, {3., 5., 0.}, {2., 6., 0.}, {4., 6., 0.}, {1., 7., 0.}, {3., 7., 0.},
                           {0., 8., 0.}, {5., 8., 0.}, {2., 1., 0.}, {3., 1., 0.}, {2., 2., 0.}, {3., 2., 0.}};

static Vint ix[20][2] = {{1, 2},   {2, 5},   {5, 4},   {4, 1},

                         {3, 6},   {6, 16},  {16, 15}, {15, 3},

                         {9, 10},  {10, 14}, {14, 13}, {13, 9},

                         {7, 8},   {8, 12},  {12, 11}, {11, 7},

                         {17, 19}, {19, 20}, {20, 18}, {18, 17}};

/*----------------------------------------------------------------------
                      Tesselate and Mesh a Bounded Region in a Plane
----------------------------------------------------------------------*/
int
main()
{
    vis_Connect *connect, *tess;
    msh_TriMesh* trimesh;
    msh_SurfMesh* surfmesh;
    Vint n, numnp, numel, nix, jx[3], aid[20], num, k, efls[3], i;
    Vdouble xd[3];

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* define original geometry */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    for (n = 1; n <= 20; n++) {
        vis_ConnectSetCoordsdv(connect, n, x[n - 1]);
    }
    for (n = 1; n <= 20; n++) {
        vis_ConnectSetTopology(connect, n, SYS_SHAPELINE, 2, 0, 0);
        vis_ConnectSetElemNode(connect, n, ix[n - 1]);
        vis_ConnectSetElemAssoc(connect, VIS_PROPID, n, (n - 1) / 4 + 1);
    }
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam51-bound.bdf");

    /* tesselate geometry */
    tess = vis_ConnectBegin();
    vis_ConnectPre(tess, SYS_DOUBLE);
    vis_ConnectTess(tess, connect);
    vis_ConnectEnd(connect);
    vis_ConnectWrite(tess, SYS_NASTRAN_BULKDATA, "exam51-tess.bdf");

    vis_ConnectNumber(tess, SYS_NODE, &numnp);
    vis_ConnectNumber(tess, SYS_ELEM, &numel);

    /* create trimesh object */
    trimesh = msh_TriMeshBegin();
    msh_TriMeshDef(trimesh, numnp, numel, 2);
    msh_TriMeshSetParamd(trimesh, VIS_MESH_EDGELENGTH, 10.);

    /* define trimesh nodes */
    for (n = 1; n <= numnp; n++) {
        vis_ConnectCoordsdv(tess, 1, &n, (Vdouble(*)[3])xd);
        msh_TriMeshSetNode(trimesh, n, xd);
    }
    /* define trimesh lines */
    for (n = 1; n <= numel; n++) {
        vis_ConnectElemNode(tess, n, &nix, jx);
        msh_TriMeshSetLine(trimesh, n, jx);
        vis_ConnectAllElemAssoc(tess, VIS_PROPID, n, &num, aid);
        for (k = 0; k < num; k++) {
            msh_TriMeshSetLineAssoc(trimesh, VIS_PROPID, n, SYS_ELEM, aid[k]);
            msh_TriMeshSetLineAssoc(trimesh, VIS_PROPID, n, SYS_EDGE, aid[k]);
        }
    }
    vis_ConnectEnd(tess);

    connect = vis_ConnectBegin();
    msh_TriMeshWrite(trimesh, SYS_ASCII, "exam51.tri");
    msh_TriMeshGenerate(trimesh, connect);
    msh_TriMeshEnd(trimesh);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam51-tri.bdf");

    /* use output from TriMesh as input to SurfMesh */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numnp, numel);

    for (n = 1; n <= numnp; n++) {
        vis_ConnectCoordsdv(connect, 1, &n, (Vdouble(*)[3])xd);
        msh_SurfMeshSetPoint(surfmesh, n, xd, 1);
    }

    for (n = 1; n <= numel; n++) {
        vis_ConnectElemNode(connect, n, &nix, jx);
        efls[0] = efls[1] = efls[2] = 0;
        for (i = 1; i <= 3; i++) {
            vis_ConnectAllElemEntAssoc(connect, VIS_PROPID, SYS_EDGE, n, i, &num, aid);
            if (num) {
                if (aid[0]) {
                    efls[i - 1] = 1;
                }
            }
        }
        msh_SurfMeshSetTri(surfmesh, n, jx, efls);
        for (i = 1; i <= 3; i++) {
            vis_ConnectAllElemEntAssoc(connect, VIS_PROPID, SYS_EDGE, n, i, &num, aid);
            if (num) {
                for (k = 0; k < num; k++) {
                    msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, n, SYS_EDGE, i, aid[k]);
                }
            }
        }

        vis_ConnectAllElemAssoc(connect, VIS_PROPID, n, &num, aid);
        if (num) {
            for (k = 0; k < num; k++) {
                msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, n, SYS_ELEM, 1, aid[k]);
                msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, n, SYS_FACE, 1, aid[k]);
            }
        }
    }
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, 0.5);
    vis_ConnectEnd(connect);

    connect = vis_ConnectBegin();
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam51.srf");
    msh_SurfMeshGenerate(surfmesh, connect);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam51-srf.bdf");

    /* print result */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("Nodes:\n");
    for (n = 1; n <= numnp; n++) {
        vis_ConnectCoordsdv(connect, 1, &n, (Vdouble(*)[3])xd);
        printf("node= %d, x= %e, y= %e\n", n, xd[0], xd[1]);
    }

    printf("Elements:\n");
    for (n = 1; n <= numel; n++) {
        vis_ConnectElemNode(connect, n, &nix, jx);
        printf("elem= %d, nodes= %d %d %d, assoc= ", n, jx[0], jx[1], jx[2]);
        vis_ConnectAllElemAssoc(connect, VIS_PROPID, n, &num, aid);
        for (k = 0; k < num; k++) {
            printf("%d ", aid[k]);
        }
        printf("\n");
    }
    /* end objects */
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}

6.41. Example 52acis, Illustrate ACIS Interface to SurfMesh and TetMesh

This example illustrates accessing geometry from the ACIS toolkit and loading it into a SurfMesh object for surface meshing. The subsequent surface mesh is then transferred to a TetMesh object for tetrahedral volume meshing.

#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include <float.h>

#include "spatial_license.h"
#include "license.hxx"
#include "acis.hxx"
#include "api.hxx"
#include "ga_api.hxx"
#include "at_name.hxx"
#include "at_int.hxx"
#include "kernapi.hxx"
#include "ckoutcom.hxx"
#include "transf.hxx"
#include "transfrm.hxx"
#include "geometry.hxx"
#include "curve.hxx"
#include "curdef.hxx"
#include "body.hxx"
#include "vertex.hxx"
#include "point.hxx"
#include "coedge.hxx"
#include "edge.hxx"
#include "loop.hxx"

/* GHF
#define VKI_PROJECT_WIRES
*/
#define VKI_PROJECT_CONICS
#ifdef VKI_PROJECT_CONICS
#include "cone.hxx"
#include "sphere.hxx"
#include "torus.hxx"
#include "plane.hxx"
#include "getowner.hxx"
#endif

#include "cstrapi.hxx"
#include "af_api.hxx"
#include "LinkedMeshManager.hxx"
#include "idx_mesh.hxx"
#include "stlmmg.hxx"
#include "gmeshmg.hxx"
#include "cface.hxx"
#include "at_fcf.hxx"
#include "cell2d.hxx"
#include "cshell.hxx"
#include "intrapi.hxx"
#include "ptlist.hxx"
#include "license.hxx"
#include "spa_unlock_result.hxx"

#ifdef OLDLICENSE
extern void
unlock_spatial_products_6831();
#endif

/* SurfMesh geometry projection callback function */
void
project_surfmesh2acis(msh_SurfMesh* surfmesh, Vobject* object, Vint enttype, Vint entaid, Vdouble uh[], Vdouble xh[3],
                      Vdouble ug[], Vdouble xg[3])
{
    SPAposition pxh(xh[0], xh[1], xh[2]);
    SPAposition pxg;
    double distance;
    param_info ent_info;
    ENTITY* ent;
    outcome res;

    res = api_get_entity_from_id(--entaid, ent);
    check_outcome(res);

    res = api_entity_point_distance(ent, pxh, pxg, distance, ent_info);
    check_outcome(res);
    if (enttype == SYS_EDGE) {
        ug[0] = ent_info.t();
    }
    else if (enttype == SYS_FACE) {
        ug[0] = ent_info.uv().u;
        ug[1] = ent_info.uv().v;
    }
    xg[0] = pxg.x();
    xg[1] = pxg.y();
    xg[2] = pxg.z();
}

/* ACIS initialization function */
static void
init_acis()
{
#ifdef OLDLICENSE
    unlock_spatial_products_6831();
#endif
    check_outcome(api_start_modeller(0));
    check_outcome(api_initialize_faceter());
}

/* ACIS termination function */
static void
term_acis()
{
    api_terminate_faceter();
    api_stop_modeller();
}

static void
associate_to_cshell(CFACE* cface, tag_id_type* id)
{
    if (cface == NULL)
        return;
    if (cface->owner()->identity() == CELL2D_TYPE)
        return;

    CSHELL* cshell = (CSHELL*)cface->owner();
    CELL* cell = (CELL*)cshell->owner();
    api_get_entity_id(reinterpret_cast<ENTITY*>(cell), *id);
    *id += 1;
}

static logical
calculate_edge_tangents(EDGE* edge, SPAposition pos, Vdouble tpar, Vdouble tv[3])
{
    SPAunit_vector tang;
    SPAvector tan_vec;

    if (tpar == DBL_MAX) {
        if (same_point(pos, edge->start_pos(), SPAresabs)) {
            tpar = edge->start_param();
        }
        else if (same_point(pos, edge->end_pos(), SPAresabs)) {
            tpar = edge->end_param();
        }
        if (tpar == DBL_MAX) {
            tv[0] = 0.;
            tv[1] = 0.;
            tv[2] = 0.;
            return (0);
        }
    }
    tan_vec = edge->geometry()->equation().eval_deriv(tpar, TRUE, TRUE);
    tang = normalise(tan_vec);
    tv[0] = tang.x();
    tv[1] = tang.y();
    tv[2] = tang.z();
    return (1);
}

/* ACIS mesh manager */
class VKI_MESH_MANAGER: public GLOBAL_MESH_MANAGER {
  private:
    msh_SurfMesh* surfmesh;
    Vint tix[3];            /* triangle connectivity */
    Vdouble norms[3][3];    /* triangle node normals */
    Vdouble tangs[3][3];    /* triangle node tangents */
    Vint efl[3];            /* triangle edge flags */
    Vint reversed[3];       /* coedge direction */
    tag_id_type edge_id[3]; /* edge entity tags for triangle */
    tag_id_type face_id;    /* face entity tag for triangle */
    Vint conicid;           /* conic id */
    Vint hasconic;          /* current face has conic */
    Vint nodeind;           /* current defined point */
    Vint elemind;           /* current defined triangle */
    Vint ibody;             /* current body */
    Vint rfaceflag;
    Vint lfaceflag;
    tag_id_type rfaceid; /* back cell */
    tag_id_type lfaceid; /* front cell */

  public:
    VKI_MESH_MANAGER(msh_SurfMesh* surfMesh):
        surfmesh(surfMesh), nodeind(0), elemind(0), face_id(0), conicid(0), hasconic(0), rfaceflag(0), lfaceflag(0), rfaceid(0),
        lfaceid(0), ibody(0)
    {
    }

    virtual logical
    need_global_indexed_polygons(void)
    {
        return TRUE;
    }

    virtual logical
    need_precount_of_global_indexed_polygons(void)
    {
        return TRUE;
    }

    virtual logical
    need_coedge_pointers_on_polyedges()
    {
        return TRUE;
    }

    virtual logical
    need_indexed_polynode_with_data()
    {
        return TRUE;
    }

    virtual logical
    need_edge_indices()
    {
        return TRUE;
    }

    virtual void*
    null_node_id(void)
    {
        return (void*)-1;
    }

    virtual void*
    announce_global_node(int inode, VERTEX* ver, const SPAposition& Xi)
    {
        Vdouble x[3];
        tag_id_type id;
        /* set point coordinates */
        x[0] = Xi.x();
        x[1] = Xi.y();
        x[2] = Xi.z();
        nodeind = inode + 1;
        msh_SurfMeshSetPoint(surfmesh, nodeind, x, 1);
        /* recover and set vertex geometry tag */
        api_get_entity_id((ENTITY*)ver, id);
        /* offset by one to ensure a non-zero tag */
        id += 1;
        msh_SurfMeshSetPointAssoc(surfmesh, VIS_GEOVERT, nodeind, id);
        return ((void*)inode);
    }

    virtual void*
    announce_global_node(int inode, EDGE* mod_edge, const SPAposition& Xi, double t)
    {
        Vdouble x[3];
        /* set point coordinates */
        x[0] = Xi.x();
        x[1] = Xi.y();
        x[2] = Xi.z();
        nodeind = inode + 1;
        msh_SurfMeshSetPoint(surfmesh, nodeind, x, 0);
        return ((void*)inode);
    }

    virtual void*
    announce_global_node(int inode, FACE* mod_face, const SPAposition& Xi, const SPApar_pos& uv)
    {
        Vdouble x[3];
        /* set point coordinates */
        x[0] = Xi.x();
        x[1] = Xi.y();
        x[2] = Xi.z();
        nodeind = inode + 1;
        msh_SurfMeshSetPoint(surfmesh, nodeind, x, 0);
        return ((void*)inode);
    }

    virtual void
    announce_polygon_model_face(ENTITY* ent)
    {
        FACE* face = (FACE*)ent;
        Vint sense;
        Vdouble xo[3], ax[3], dt[3], radius, mag, angle;
        /* set geometry face tag for oncoming polygons */
        api_get_entity_id((ENTITY*)ent, face_id);
        /* offset by one to ensure a non-zero tag */
        face_id += 1;
        hasconic = 0;
        logical face_sense = (face->sense() == REVERSED);
        if (face_sense) {
            sense = -1;
        }
        else {
            sense = 1;
        }
        /* conic sections */
#ifdef VKI_PROJECT_CONICS
        {
            /* GHF */
            printf("face_id= %d\n", face_id);
            printf("sense= %d\n", sense);
            SPAtransf T = SPAtransf();
            T = get_owner_transf(face);

            if (is_conical_face(face)) {
                cone const& the_cone = (cone const&)(face->geometry()->equation());
                SPAposition the_cone_apex = the_cone.get_apex();
                ellipse ell = the_cone.base;
                SPAposition center = ell.centre;
                SPAunit_vector nor = ell.normal;
                SPAvector axis = ell.major_axis;
                Vdouble radius_ratio = ell.radius_ratio;
                Vdouble sine_angle = the_cone.sine_angle;
                /* GHF
                 */
                printf("conical face\n");
                printf("center= %e %e %e\n", center.x(), center.y(), center.z());
                printf("nor= %e %e %e\n", nor.x(), nor.y(), nor.z());
                printf("axis= %e %e %e\n", axis.x(), axis.y(), axis.z());
                printf("radius_ratio= %e\n", radius_ratio);
                printf("sine_angle= %e\n", sine_angle);
                if (radius_ratio == 1.) {
                    conicid += 1;
                    hasconic = 1;
                    xo[0] = center.x();
                    xo[1] = center.y();
                    xo[2] = center.z();
                    ax[0] = nor.x();
                    ax[1] = nor.y();
                    ax[2] = nor.z();
                    dt[0] = axis.x();
                    dt[1] = axis.y();
                    dt[2] = axis.z();
                    radius = sqrt(dt[0] * dt[0] + dt[1] * dt[1] + dt[2] * dt[2]);
                    dt[0] = dt[0] / radius;
                    dt[1] = dt[1] / radius;
                    dt[2] = dt[2] / radius;
                    if (sine_angle == 0.) {
                        msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_CYLINDER, sense, xo, ax, dt, radius, 0.);
                    }
                    else {
                        angle = fabs(sine_angle);
                        msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_CONE, sense, xo, ax, dt, angle, 0.);
                    }
                }
            }
            else if (is_toroidal_face(face)) {
                torus const& tor = (torus const&)(face->geometry()->equation());
                SPAposition center = tor.centre;
                SPAunit_vector normal = tor.normal;
                SPAunit_vector ref = tor.uv_oridir;
                double major_radius = tor.major_radius;
                double minor_radius = tor.minor_radius;
                /* GHF
                 */
                printf("toroidal face\n");
                conicid += 1;
                hasconic = 1;
                xo[0] = center.x();
                xo[1] = center.y();
                xo[2] = center.z();
                ax[0] = normal.x();
                ax[1] = normal.y();
                ax[2] = normal.z();
                dt[0] = ref.x();
                dt[1] = ref.y();
                dt[2] = ref.z();
                major_radius = fabs(major_radius);
                minor_radius = fabs(minor_radius);
                msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_TORUS, sense, xo, ax, dt, minor_radius, major_radius);
            }
            else if (is_spherical_face(face)) {
                sphere* sph = (sphere*)(face->geometry()->trans_surface(T, face_sense));
                SPAposition center = sph->centre;
                SPAunit_vector normal = sph->pole_dir;
                SPAunit_vector ref = sph->uv_oridir;
                radius = sph->radius;
                /* GHF
                 */
                printf("spherical face\n");
                printf("center= %e %e %e\n", center.x(), center.y(), center.z());
                printf("normal= %e %e %e\n", normal.x(), normal.y(), normal.z());
                printf("radius= %e\n", radius);
                conicid += 1;
                hasconic = 1;
                xo[0] = center.x();
                xo[1] = center.y();
                xo[2] = center.z();
                ax[0] = normal.x();
                ax[1] = normal.y();
                ax[2] = normal.z();
                dt[0] = ref.x();
                dt[1] = ref.y();
                dt[2] = ref.z();
                radius = fabs(radius);
                msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_SPHERE, sense, xo, ax, dt, radius, 0.);
            }
            else if (is_planar_face(face)) {
                const plane& p = (const plane&)(face->geometry()->equation());
                SPAposition pos = p.root_point;
                SPAunit_vector nor = p.normal;
                SPAvector ref = p.u_deriv;
                conicid += 1;
                hasconic = 1;
                xo[0] = pos.x();
                xo[1] = pos.y();
                xo[2] = pos.z();
                ax[0] = nor.x();
                ax[1] = nor.y();
                ax[2] = nor.z();
                dt[0] = ref.x();
                dt[1] = ref.y();
                dt[2] = ref.z();
                mag = sqrt(dt[0] * dt[0] + dt[1] * dt[1] + dt[2] * dt[2]);
                dt[0] = dt[0] / mag;
                dt[1] = dt[1] / mag;
                dt[2] = dt[2] / mag;
                msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_PLANE, sense, xo, ax, dt, 0., 0.);
                /* GHF
                 */
                printf("planar face\n");
                printf("pos= %e %e %e\n", pos.x(), pos.y(), pos.z());
                printf("nor= %e %e %e\n", nor.x(), nor.y(), nor.z());
            }
        }
#endif
        /* determine material presense relative to face */
        rfaceflag = 1;
        lfaceflag = 0;
        if (face->sides() == DOUBLE_SIDED) {
            lfaceflag = 1;
        }

        ATTRIB_FACECFACE* att = (ATTRIB_FACECFACE*)find_attrib(face, ATTRIB_CT_TYPE, ATTRIB_FACECFACE_TYPE);
        rfaceid = lfaceid = 0;
        /* check for cellular topology information */
        if (att != NULL) {
            associate_to_cshell(att->back_cface(), &rfaceid);
            associate_to_cshell(att->front_cface(), &lfaceid);
        }
    }

    virtual void
    announce_indexed_polynode(ENTITY* ent, int ipoly, int i, void* idptr, const double& edge_tpar, const SPApar_pos& uv,
                              const SPAposition& pos, const SPAunit_vector& uvec)
    {
        EDGE* edge;
        /* entity is a COEDGE pointer */
        /* triangle edge flagged if lie on geometry edge */
        efl[i] = (ent != NULL) ? 1 : 0;
        /* set triangle normals */
        norms[i][0] = uvec.x();
        norms[i][1] = uvec.y();
        norms[i][2] = uvec.z();
        /* set ith triangle point connection */
        tix[i] = (long)idptr + 1;
        if (i == 0) {
            edge_id[0] = edge_id[1] = edge_id[2] = 0;
            reversed[0] = reversed[1] = reversed[2] = 0;
        }
        if (ent == NULL) {
            return;
        }
        /* store the coedge sense */
        reversed[i] = ((COEDGE*)ent)->sense() != FORWARD;
        /* set edge entity tag and tangent */
        edge = ((COEDGE*)(ent))->edge();
        if (edge && edge->geometry()) {
            api_get_entity_id((ENTITY*)edge, edge_id[i]);
            /* offset by one to ensure a non-zero tag */
            edge_id[i] += 1;
            calculate_edge_tangents(edge, pos, edge_tpar, tangs[i]);
        }
    }

    virtual void
    announce_counts(int numel, int numnp, int npolynode)
    {
        if (numnp == 0 || numel == 0)
            return;
        msh_SurfMeshDef(surfmesh, numnp, numel);
        ibody += 1;
        conicid = 0;
    }

    virtual void
    begin_mesh_output(ENTITY* faceted_entity, ENTITY* refine_entity, ENTITY* output_entity)
    {
    }

    virtual void*
    announce_indexed_node(int inode, const SPApar_pos& param, const SPAposition& Xi, const SPAunit_vector& normal)
    {
        Vdouble x[3];

        x[0] = Xi.x();
        x[1] = Xi.y();
        x[2] = Xi.z();
        nodeind = inode + 1;
        msh_SurfMeshSetPoint(surfmesh, nodeind, x, 0);
        return ((void*)inode);
    }

    virtual void
    end_mesh_output(ENTITY* faceted_entity, ENTITY* refine_entity, ENTITY* output_entity)
    {
    }

    virtual void
    start_indexed_polygon(int ipoly, int npolynode, int ishare)
    {
    }

    virtual void
    announce_indexed_polynode(int ipoly, int i, void* pnode)
    {
    }

    virtual void
    end_indexed_polygon(int iPoly)
    {
        int i, j, i1;
        Vdouble tv[2][3], t1;
        Vdouble x0[3], x1[3], dx[3], fl, elen;
        Vint pflag, oknorm, oktang;
        EDGE* edge;
        SPAposition ci, xi;
        double d;
        param_info ent_info;

        elemind = iPoly + 1;
        msh_SurfMeshSetTri(surfmesh, elemind, tix, efl);
        /* check for zero normal */
        /* we round to 32 bit precision to get rid of noise
           in the normal values which is inconsistent between
           successive mesh manager runs on an unchanged model */
        oknorm = 1;
        for (i = 0; i < 3; i++) {
            norms[i][0] = (float)norms[i][0];
            norms[i][1] = (float)norms[i][1];
            norms[i][2] = (float)norms[i][2];
            fl = norms[i][0] * norms[i][0] + norms[i][1] * norms[i][1] + norms[i][2] * norms[i][2];
            if (fl == 0.) {
                oknorm = 0;
                break;
            }
        }
        if (oknorm) {
            msh_SurfMeshSetTriNorm(surfmesh, elemind, norms);
        }
        /* tag for nodes generated on geometry edge */
        /* compute edge tangents along triangle edge */
        for (i = 0; i < 3; i++) {
            if (edge_id[i]) {
                msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOEDGE, elemind, SYS_EDGE, i + 1, edge_id[i]);
                msh_SurfMeshGetPoint(surfmesh, tix[i], x0, &pflag);
                tv[0][0] = tangs[i][0];
                tv[0][1] = tangs[i][1];
                tv[0][2] = tangs[i][2];
                i1 = (i + 1) % 3;
                msh_SurfMeshGetPoint(surfmesh, tix[i1], x1, &pflag);
                api_get_entity_from_id(edge_id[i] - 1, (ENTITY*&)edge);
                xi.set_x(x1[0]);
                xi.set_y(x1[1]);
                xi.set_z(x1[2]);
                api_entity_point_distance((ENTITY*)edge, xi, ci, d, ent_info);
                if (ent_info.entity_type() == ent_is_vertex) {
                    t1 = reversed[i] ? edge->start_param() : edge->end_param();
                    if (edge->sense() == REVERSED) {
                        t1 = -t1;
                    }
                }
                else {
                    t1 = ent_info.t();
                }
                calculate_edge_tangents(edge, xi, t1, tv[1]);
                /* align tangents with element connectivity */
                dx[0] = x1[0] - x0[0];
                dx[1] = x1[1] - x0[1];
                dx[2] = x1[2] - x0[2];
                elen = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]);
                if (elen != 0.) {
                    dx[0] /= elen;
                    dx[1] /= elen;
                    dx[2] /= elen;
                }
                oktang = 1;
                for (j = 0; j < 2; j++) {
                    fl = dx[0] * tv[j][0] + dx[1] * tv[j][1] + dx[2] * tv[j][2];
                    if (fl < 0.) {
                        tv[j][0] *= -1.;
                        tv[j][1] *= -1.;
                        tv[j][2] *= -1.;
                    }
                    if (fabs(fl) < .5) {
                        oktang = 0;
                        break;
                    }
                }
                if (oktang) {
                    msh_SurfMeshSetTriTang(surfmesh, elemind, i + 1, tv);
                }
            }
        }
        /* tag for nodes and elements generated on geometry face */
        if (face_id) {
            msh_SurfMeshSetTriBack(surfmesh, elemind, rfaceflag, lfaceflag);
            if (rfaceid) {
                msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, elemind, SYS_ELEM, 0, rfaceid);
                /* add VIS_PROPID for NASTRAN bulk data file export */
                msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, elemind, SYS_ELEM, 0, rfaceid);
            }
            if (lfaceid) {
                msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, elemind, SYS_ELEM, -1, lfaceid);
                /* add VIS_PROPID for NASTRAN bulk data file export */
                msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, elemind, SYS_ELEM, -1, lfaceid);
            }
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOFACE, elemind, SYS_FACE, 0, face_id);
            if (hasconic) {
                msh_SurfMeshSetTriConic(surfmesh, elemind, conicid);
            }
        }
    }
};

static void
process_wires(ENTITY_LIST* wires, msh_CurvMesh* curvmesh)
{
    bool again;
    Vint numnp, numel, ix[2], index;
    Vdouble x[3];
    vsy_IntHash* nodeih;
    tag_id_type id, start_tag, end_tag;

    nodeih = vsy_IntHashBegin();
    numnp = numel = 0;
    if (wires->count()) {
        WIRE* wire = (WIRE*)wires->first();
        while (wire) {
            if (wire->cont() == ALL_INSIDE) {
                ENTITY_LIST edges;
                check_outcome(api_get_edges(wire, edges));
                if (edges.count()) {
                    EDGE* edge = (EDGE*)edges.first();
                    while (edge) {
                        api_get_entity_id((ENTITY*)edge, id);
                        id += 1;
                        VERTEX* start = edge->start();
                        VERTEX* end = edge->end();
                        /* get entity tags and offset by 1 */
                        api_get_entity_id((ENTITY*)start, start_tag);
                        start_tag += 1;
                        api_get_entity_id((ENTITY*)end, end_tag);
                        end_tag += 1;

                        AF_POINT* de0;
                        AF_POINT* deN;
                        AF_POINT* de;
                        if (AF_POINT::find(edge, edge->sense(), de0, deN)) {
                            Vdouble ts[2][3];
                            de = de0;
                            again = true;
                            do {
                                double t = de->get_parameter();
                                SPAposition p = de->get_position();
                                x[0] = p.x();
                                x[1] = p.y();
                                x[2] = p.z();
                                SPAvector tan_vec = edge->geometry()->equation().eval_deriv(t, TRUE, TRUE);
                                SPAunit_vector tang = normalise(tan_vec);
                                if (de == de0) {
                                    ts[0][0] = tang.x();
                                    ts[0][1] = tang.y();
                                    ts[0][2] = tang.z();
                                    vsy_IntHashLookup(nodeih, start_tag, &index);
                                    if (index == 0) {
                                        index = ++numnp;
                                        vsy_IntHashInsert(nodeih, start_tag, index);
                                        msh_CurvMeshSetPoint(curvmesh, index, x, 1);
                                        msh_CurvMeshSetPointAssoc(curvmesh, VIS_GEOVERT, index, start_tag);
                                    }
                                    ix[0] = index;
                                }
                                else if (de == deN) {
                                    vsy_IntHashLookup(nodeih, end_tag, &index);
                                    if (index == 0) {
                                        index = ++numnp;
                                        vsy_IntHashInsert(nodeih, end_tag, index);
                                        msh_CurvMeshSetPoint(curvmesh, index, x, 1);
                                        msh_CurvMeshSetPointAssoc(curvmesh, VIS_GEOVERT, index, end_tag);
                                    }
                                    again = false;
                                    ix[1] = index;
                                    ++numel;
                                    msh_CurvMeshSetLine(curvmesh, numel, ix);
                                    ts[1][0] = tang.x();
                                    ts[1][1] = tang.y();
                                    ts[1][2] = tang.z();
                                    msh_CurvMeshSetLineTang(curvmesh, numel, ts);
                                    msh_CurvMeshSetLineAssoc(curvmesh, VIS_GEOEDGE, numel, id);
                                    /* save information for next point */
                                    ix[0] = ix[1];
                                    ts[0][0] = ts[1][0];
                                    ts[0][1] = ts[1][1];
                                    ts[0][2] = ts[1][2];
                                }
                                else {
                                    index = ++numnp;
                                    msh_CurvMeshSetPoint(curvmesh, index, x, 1);
                                    ix[1] = index;
                                    ++numel;
                                    msh_CurvMeshSetLine(curvmesh, numel, ix);
                                    ts[1][0] = tang.x();
                                    ts[1][1] = tang.y();
                                    ts[1][2] = tang.z();
                                    msh_CurvMeshSetLineTang(curvmesh, numel, ts);
                                    msh_CurvMeshSetLineAssoc(curvmesh, VIS_GEOEDGE, numel, id);
                                    /* save information for next point */
                                    ix[0] = ix[1];
                                    ts[0][0] = ts[1][0];
                                    ts[0][1] = ts[1][1];
                                    ts[0][2] = ts[1][2];
                                }
                                de = de->next(0);
                            } while (again);
                        }
                        edge = (EDGE*)edges.next();
                    }
                }
            }
            wire = (WIRE*)wires->next();
        }
    }
    vsy_IntHashEnd(nodeih);
}

static void
count_wire_ents(ENTITY_LIST* wires, Vint* numnodes, Vint* numsegs)
{
    bool again;
    Vint numnp, numel, index;
    vsy_IntHash* nodeih;
    tag_id_type start_tag, end_tag;

    *numnodes = *numsegs = 0;
    numnp = numel = 0;
    nodeih = vsy_IntHashBegin();
    if (wires->count()) {
        WIRE* wire = (WIRE*)wires->first();
        while (wire) {
            if (wire->cont() == ALL_INSIDE) {
                ENTITY_LIST edges;
                check_outcome(api_get_edges(wire, edges));
                if (edges.count()) {
                    EDGE* edge = (EDGE*)edges.first();
                    while (edge) {
                        VERTEX* start = edge->start();
                        VERTEX* end = edge->end();
                        /* get entity tags and offset by 1 */
                        api_get_entity_id((ENTITY*)start, start_tag);
                        start_tag += 1;
                        api_get_entity_id((ENTITY*)end, end_tag);
                        end_tag += 1;

                        AF_POINT* de0;
                        AF_POINT* deN;
                        AF_POINT* de;
                        if (AF_POINT::find(edge, edge->sense(), de0, deN)) {
                            de = de0;
                            again = true;
                            do {
                                if (de == de0) {
                                    vsy_IntHashLookup(nodeih, start_tag, &index);
                                    if (index == 0) {
                                        index = ++numnp;
                                        vsy_IntHashInsert(nodeih, start_tag, index);
                                    }
                                }
                                else if (de == deN) {
                                    vsy_IntHashLookup(nodeih, end_tag, &index);
                                    if (index == 0) {
                                        index = ++numnp;
                                        vsy_IntHashInsert(nodeih, end_tag, index);
                                    }
                                    again = false;
                                    ++numel;
                                }
                                else {
                                    ++numnp;
                                    ++numel;
                                }
                                de = de->next(0);
                            } while (again);
                        }
                        edge = (EDGE*)edges.next();
                    }
                }
            }
            wire = (WIRE*)wires->next();
        }
    }
    vsy_IntHashEnd(nodeih);
    *numnodes = numnp;
    *numsegs = numel;
}

/*----------------------------------------------------------------------
                      Illustrate ACIS Interface to CurvMesh, SurfMesh and TetMesh
----------------------------------------------------------------------*/
int
main(int argc, char* argv[])
{
    ENTITY_LIST ents;
    ENTITY* ent;
    vis_Connect* connect;
    vis_Connect* connectsrf;
    vis_Connect* connecttet;
    vis_Connect* connectcrv;
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    msh_CurvMesh* curvmesh;
    outcome res;
    Vint maxi;
    Vint numpnts, numtris;
    Vint i;
    Vint numnp, numel;
    Vint pflag;
    Vint nfree;
    Vdouble diaglen, edgelen;
    Vdouble minext[3], maxext[3];
    Vdouble x[3];
    FILE* fsat;
    REFINEMENT* ref;
    int enttype;
    Vint ibody;
    Vint ierr;
    Vchar filnam[256];
    Vint tetmeshoption;
    Vint numwirepnts, numwiresegs;

    if (argc < 2) {
        printf("Usage: %s acis_sat_file\n", argv[0]);
        return 0;
    }
    /*
     * function to call in order to unlock the spatial products.
     * SPATIAL_LICENSE is defined in spatial_license.h and points to the license key string.
     */
    spa_unlock_result out = spa_unlock_products(SPATIAL_LICENSE);
    if (out.get_state() != SPA_UNLOCK_PASS && out.get_state() != SPA_UNLOCK_PASS_WARN) {
        printf("%s\n", out.get_message_text());
        return 0;
    }
    /* initialize ACIS */
    init_acis();
    /* open ACIS sat file */
    fsat = acis_fopen(argv[1], "r");
    if (fsat == NULL) {
        printf("Unable to open sat file %s\n", argv[1]);
        return -1;
    }
    /* GHF */
    printf("acis_fopen\n");
    /* restore the entities within */
    res = api_restore_entity_list(fsat, TRUE, ents);
    check_outcome(res);
    /* GHF */
    printf("api_restore_entity_list\n");
    /* ACIS sat file */
    acis_fclose(fsat);
    /* GHF */
    printf("acis_fclose\n");
    /* create a refinement object */
    res = api_create_refinement(ref);
    /* GHF */
    printf("A api_create_refinement\n");
    check_outcome(res);
    /* GHF */
    printf("B api_create_refinement\n");
    /* a normal tolerance of 15 degrees */
    API_BEGIN
    ref->set_normal_tol(15);
    API_END

    EXCEPTION_BEGIN
    MESH_MANAGER* old_MM = NULL;
    MESH_MANAGER* MM = NULL;

    EXCEPTION_TRY

    /* instance surface mesher and tet mesher objects */
    surfmesh = msh_SurfMeshBegin();
    tetmesh = msh_TetMeshBegin();
    /* instance Connect object to hold resultant mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);

    check_outcome(api_get_mesh_manager(old_MM));

    /* create and install VKI mesh manager */
    MM = ACIS_NEW VKI_MESH_MANAGER(surfmesh);
    res = api_set_mesh_manager(MM);
    check_outcome(res);
    /* mesh each body separately */
    ibody = 0;
    for (ents.init(); (ent = ents.next());) {
        ibody += 1;
#ifdef VKI_PROJECT_WIRES
        ENTITY_LIST wires;
#endif
        /* use no-op block to rollback body transformation */
        API_NOP_BEGIN
        api_change_body_trans((BODY*)ent, NULL);
        enttype = ent->identity();
        res = api_set_entity_refinement(ent, ref, FALSE);
        check_outcome(res);
        /* facet entity */
        res = api_facet_entity(ent);
        check_outcome(res);

        /* wire entity */
#ifdef VKI_PROJECT_WIRES
        res = api_get_wires(ent, wires);
        check_outcome(res);
        count_wire_ents(&wires, &numwirepnts, &numwiresegs);
        if (numwirepnts && numwiresegs) {
            curvmesh = msh_CurvMeshBegin();
            msh_CurvMeshDef(curvmesh, numwirepnts, numwiresegs);
            process_wires(&wires, curvmesh);
        }
#endif
        API_NOP_END
        check_outcome(result);

        /* find extent of object to set a reasonable element size */
        msh_SurfMeshInq(surfmesh, &numpnts, &numtris);
        if (numpnts == 0 || numtris == 0)
            continue;
        printf(" Tesselation number of points= %d\n", numpnts);
        printf(" Tesselation number of tris=   %d\n", numtris);
        for (i = 1; i <= numpnts; i++) {
            msh_SurfMeshGetPoint(surfmesh, i, x, &pflag);
            if (i == 1) {
                minext[0] = maxext[0] = x[0];
                minext[1] = maxext[1] = x[1];
                minext[2] = maxext[2] = x[2];
            }
            else {
                if (x[0] < minext[0])
                    minext[0] = x[0];
                if (x[1] < minext[1])
                    minext[1] = x[1];
                if (x[2] < minext[2])
                    minext[2] = x[2];
                if (x[0] > maxext[0])
                    maxext[0] = x[0];
                if (x[1] > maxext[1])
                    maxext[1] = x[1];
                if (x[2] > maxext[2])
                    maxext[2] = x[2];
            }
        }
        diaglen = sqrt((maxext[0] - minext[0]) * (maxext[0] - minext[0]) + (maxext[1] - minext[1]) * (maxext[1] - minext[1]) +
                       (maxext[2] - minext[2]) * (maxext[2] - minext[2]));
        /* set element size to 10% of diagonal of extent box */
        edgelen = diaglen / 10.;
        msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, edgelen);
        maxi = 3;

        /* generate CurvMesh */
#ifdef VKI_PROJECT_WIRES
        if (numwirepnts && numwiresegs) {
            msh_CurvMeshSetParami(curvmesh, VIS_MESH_MAXI, maxi);
            msh_CurvMeshSetParamd(curvmesh, VIS_MESH_EDGELENGTH, edgelen);
            connectcrv = vis_ConnectBegin();
            vis_ConnectDef(connectcrv, 0, 0);
            vis_ConnectPre(connectcrv, SYS_DOUBLE);
            /* optional write of CurvMesh contents for QA
  sprintf(filnam,"exam52acis%d.crv",ibody);
  msh_CurvMeshWrite (curvmesh,SYS_ASCII,filnam);
            */
            msh_CurvMeshGenerate(curvmesh, connectcrv);
            msh_CurvMeshEnd(curvmesh);

            /* vertices and elements are now those in the generated mesh */
            vis_ConnectNumber(connectcrv, SYS_NODE, &numwirepnts);
            vis_ConnectNumber(connectcrv, SYS_ELEM, &numwiresegs);
            /* optional write of mesh
  sprintf(filnam,"model-crv%d.unv",ibody);
  vis_ConnectWrite (connectcrv, SYS_SDRC_UNIVERSAL,filnam);
            */
        }
#endif

        msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .01 * edgelen);
        msh_SurfMeshSetParamd(surfmesh, VIS_MESH_GROWTHRATE, 2.);
        msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 30.);
        /* generate parabolic triangles */
        msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, maxi);
        /* enable non-manifold geometries */
        msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, SYS_ON);
        /* double backed triangulation for internal surfaces */
        msh_SurfMeshSetParami(surfmesh, SURFMESH_INTSURFBACK, SYS_ON);

        connectsrf = vis_ConnectBegin();
        vis_ConnectDef(connectsrf, 0, 0);
        vis_ConnectPre(connectsrf, SYS_DOUBLE);
        /* set exact geometry projection callback
msh_SurfMeshSetFunction (surfmesh,SURFMESH_FUN_GEOPROJ,
                    (Vfunc*)project_surfmesh2acis,NULL);
        */
        /* optional write of SurfMesh contents for QA
         */
        sprintf(filnam, "exam52acis%d.srf", ibody);
        msh_SurfMeshWrite(surfmesh, SYS_ASCII, filnam);
        msh_SurfMeshGenerate(surfmesh, connectsrf);
        ierr = msh_SurfMeshError(surfmesh);
        if (ierr) {
            printf("Error generating surface mesh, body= %d\n", ibody);
            sprintf(filnam, "model-%d.srf", ibody);
            msh_SurfMeshWrite(surfmesh, SYS_ASCII, filnam);
            continue;
        }
        /* add curve mesh to surface mesh */
#ifdef VKI_PROJECT_WIRES
        if (numwirepnts && numwiresegs) {
            vis_ConnectAppend(connectsrf, connectcrv);
            vis_ConnectMerge(connectsrf, NULL);
            vis_ConnectEnd(connectcrv);
        }
#endif
        printf("SurfMesh body= %d complete\n", ibody);
        vis_ConnectNumber(connectsrf, SYS_NODE, &numnp);
        vis_ConnectNumber(connectsrf, SYS_ELEM, &numel);
        printf(" Surface number of nodes= %d\n", numnp);
        printf(" Surface number of elems= %d\n", numel);
        /* optional write of surface mesh
sprintf(filnam,"model-srf%d.bdf",ibody);
vis_ConnectWrite (connectsrf,SYS_NASTRAN_BULKDATA,filnam);
        */

        tetmeshoption = 0;
        /* test for no free edges */
        msh_SurfMeshGetInteger(surfmesh, SURFMESH_NUMFREEEDGE, &nfree);
        if (nfree) {
            tetmeshoption = 0;
        }
        if (tetmeshoption) {
            connecttet = vis_ConnectBegin();
            vis_ConnectPre(connecttet, SYS_DOUBLE);

            /* add surface mesh information to tetmesh */
            msh_TetMeshConnect(tetmesh, connectsrf);

            msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, edgelen);
            msh_TetMeshSetParamd(tetmesh, VIS_MESH_GROWTHRATE, 2.);
            /* optional write of TetMesh contents for QA
  sprintf(filnam,"model_%d.btet",ibody);
  msh_TetMeshWrite (tetmesh,SYS_BINARY,filnam);
            */
            msh_TetMeshGenerate(tetmesh, connecttet);
            ierr = msh_TetMeshError(tetmesh);
            if (ierr) {
                printf("Error generating tet mesh, volume= %d\n", ibody);
                sprintf(filnam, "model_%d.btet", ibody);
                msh_TetMeshWrite(tetmesh, SYS_BINARY, filnam);
                vis_ConnectEnd(connecttet);
                continue;
            }
            printf("TetMesh body= %d complete\n", ibody);
            vis_ConnectNumber(connecttet, SYS_NODE, &numnp);
            vis_ConnectNumber(connecttet, SYS_ELEM, &numel);
            printf(" Volume number of nodes= %d\n", numnp);
            printf(" Volume number of elems= %d\n", numel);
            /* optional write of volume mesh
  sprintf(filnam,"model-tet%d.bdf",ibody);
  vis_ConnectWrite (connecttet,SYS_NASTRAN_BULKDATA,filnam);
            */
            vis_ConnectAppend(connect, connecttet);
            vis_ConnectEnd(connecttet);
        }
        else {
            vis_ConnectAppend(connect, connectsrf);
        }
    }
    /* report total number of generate nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("total number of nodes= %d\n", numnp);
    printf("total number of elems= %d\n", numel);
    /* optional write of final mesh */
    if (numnp && numel) {
        vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "model-tet.bdf");
        vis_ConnectWrite(connect, SYS_SDRC_UNIVERSAL, "model-tet.unv");
    }
    /* clean up */
    msh_SurfMeshEnd(surfmesh);
    msh_TetMeshEnd(tetmesh);
    vis_ConnectEnd(connect);

    ref->lose();

    EXCEPTION_CATCH_TRUE
    api_set_mesh_manager(old_MM);
    ACIS_DELETE MM;
    EXCEPTION_END
    /* final cleanup */
    res = api_del_entity_list(ents);
    check_outcome(res);
    term_acis();
    return 0;
}

6.42. Example 52capri, Illustrate CAPRI Interface to SurfMesh and TetMesh

This example illustrates accessing geometry from the CAPRI toolkit and loading it into a SurfMesh object for surface meshing. The subsequent surface mesh is then transferred to a TetMesh object for tetrahedral volume meshing.

#include "sam/base/base.h"
#include "sam/vis/vis.h"
#include "sam/msh/msh.h"
#include "capri.h"

/* SurfMesh geometry projection callback function */
void
project_surfmesh2capri(msh_SurfMesh* surfmesh, Vobject* object, Vint enttype, Vint entaid, Vdouble uh[], Vdouble xh[3],
                       Vdouble ug[], Vdouble xg[3])
{
    Vint* ivol = (Vint*)object;
    Vint status;
    Vdouble uv[4];

    uv[0] = uv[1] = uv[2] = uv[3] = 1.e+20;
    if (enttype == SYS_EDGE) {
        status = gi_qNearestOnEdge(*ivol, entaid, xh, uv, xg);
    }
    else if (enttype == SYS_FACE) {
        status = gi_qNearestOnFace(*ivol, entaid, xh, uv, xg);
    }
    if (status) {
        xg[0] = xh[0];
        xg[1] = xh[1];
        xg[2] = xh[2];
    }
}
/* utility function to transform a point */
static void
transform(Vdouble tm[3][4], Vdouble point[], Vdouble out[])
{
    out[0] = tm[0][0] * point[0] + tm[0][1] * point[1] + tm[0][2] * point[2] + tm[0][3];
    out[1] = tm[1][0] * point[0] + tm[1][1] * point[1] + tm[1][2] * point[2] + tm[1][3];
    out[2] = tm[2][0] * point[0] + tm[2][1] * point[1] + tm[2][2] * point[2] + tm[2][3];
}

/* load CAPRI tesselation of a volume into SurfMesh */
static void
loadvolume(Vint ivol, msh_SurfMesh* surfmesh)
{
    vsy_IntDict* intdict;
    vis_IdTran* idtran;
    Vint status, i, j, k;
    Vint no, aid;
    Vchar *name, buf[256];
    Vint nnode, nedge, nface, nbound, n;
    Vint iface;
    Vdouble tm[3][4], *points, *uv, x[3], vn[3][3];
    Vint tlen, *tris, *tric, plen, *ptype, *pindex;
    Vint numnp, numel, ix[3], jx[3], efl[3], base, npts;
    Vint iszero;
    Vint nedgept, n1, n2, nodes[2];
    Vdouble *edgept, *edget, trange[2], tang[2][3], curv;

    status = gi_dGetVolume(ivol, &nnode, &nedge, &nface, &nbound, &name);
    if (status) {
        printf("Error reading volume information for volume= %d\n", ivol);
        return;
    }
    printf("Loaded volume= %s\n", name);

    status = gi_iGetDisplace(ivol, (double*)tm);
    if (status) {
        printf("Error reading Displace for volume= %d\n", ivol);
        return;
    }

    intdict = vsy_IntDictBegin();
    idtran = vis_IdTranBegin();
    /* count total number of tesselation points and triangles */
    numnp = numel = npts = 0;
    for (iface = 1; iface <= nface; ++iface) {
        status = gi_dTesselFace(ivol, iface, &tlen, &tris, &tric, &plen, &points, &ptype, &pindex, &uv);
        if (status) {
            printf("Error reading TesselFace for volume= %d, face= %d\n", ivol, iface);
            return;
        }
        /* loop through points on tesselated face */
        for (i = 0; i < plen; ++i) {
            /* encode edge flag and point */
            sprintf(buf, "%d %d", ptype[i], pindex[i]);
            /* if node in interior */
            if (ptype[i] == -1) {
                numnp += 1;
                vsy_IntDictInsert(intdict, buf, numnp);
                n = numnp;
                /* node not in interior */
            }
            else {
                vsy_IntDictLookup(intdict, buf, &n);
                if (n == 0) {
                    numnp += 1;
                    vsy_IntDictInsert(intdict, buf, numnp);
                    n = numnp;
                }
            }
            npts += 1;
            vis_IdTranSetId(idtran, npts, n);
        }
        numel += tlen;
    }
    vsy_IntDictEnd(intdict);

    msh_SurfMeshDef(surfmesh, numnp, numel);
    npts = base = numel = 0;
    for (iface = 1; iface <= nface; ++iface) {
        status = gi_dTesselFace(ivol, iface, &tlen, &tris, &tric, &plen, &points, &ptype, &pindex, &uv);
        if (status) {
            printf("Error reading TesselFace for volume= %d, face= %d\n", ivol, iface);
            return;
        }
        /* loop through points on tesselated face */
        for (i = 0; i < plen; ++i) {
            npts += 1;
            vis_IdTranGetId(idtran, npts, &n);
            transform(tm, &points[3 * i], x);
            /* if node in interior */
            if (ptype[i] == -1) {
                msh_SurfMeshSetPoint(surfmesh, n, x, 0);
                /* if node not in interior, ie on edge/vertex */
            }
            else {
                /* node on geometry vertex */
                if (ptype[i] == 0) {
                    msh_SurfMeshSetPoint(surfmesh, n, x, 1);
                    msh_SurfMeshSetPointAssoc(surfmesh, VIS_GEOVERT, n, pindex[i]);
                    /* node on geometry edge, not on vertex */
                }
                else {
                    msh_SurfMeshSetPoint(surfmesh, n, x, 0);
                }
            }
        }
        /* loop through triangles on tesselated face */
        for (i = 0; i < tlen; ++i) {
            numel += 1;
            ix[0] = tris[3 * i] + base;
            ix[1] = tris[3 * i + 1] + base;
            ix[2] = tris[3 * i + 2] + base;
            vis_IdTranGetIds(idtran, 3, ix, jx);
            /* flag triangle edges on geometry edges */
            for (j = 0; j < 3; ++j) {
                k = (j + 1) % 3;
                efl[k] = 0;
                if (tric[3 * i + j] < 0) {
                    efl[k] = 1;
                }
            }
            msh_SurfMeshSetTri(surfmesh, numel, jx, efl);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOFACE, numel, SYS_FACE, 1, iface);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, numel, SYS_ELEM, 1, ivol);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, numel, SYS_ELEM, 1, ivol);
            iszero = 0;
            /* loop over triangle nodes */
            for (j = 0; j < 3; ++j) {
                if (tric[3 * i + j] < 0) {
                    /* edge number opposing node */
                    no = (j + 1) % 3 + 1;
                    /* edge association is geometry edge number */
                    aid = -tric[3 * i + j];
                    msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOEDGE, numel, SYS_EDGE, no, aid);
                    /* set triangle edge tangents */
                    status = gi_dTesselEdge(ivol, aid, &nedgept, &edgept, &edget);
                    n1 = tris[3 * i + (j + 1) % 3] - 1;
                    n2 = tris[3 * i + (j + 2) % 3] - 1;
                    ix[0] = ptype[n1];
                    ix[1] = ptype[n2];

                    /* point on edge information not available */
                    gi_dGetEdge(ivol, aid, trange, nodes);
                    if (ix[0] == 0) {
                        if (pindex[n1] == nodes[0]) {
                            ix[0] = 1;
                        }
                        else {
                            ix[0] = nedgept;
                        }
                    }
                    if (ix[1] == 0) {
                        if (pindex[n2] == nodes[0]) {
                            ix[1] = 1;
                        }
                        else {
                            ix[1] = nedgept;
                        }
                    }

                    --ix[0];
                    --ix[1];
                    status = gi_cCurvOfEdge(ivol, aid, edget[ix[0]], tang[0], &curv);
                    status = gi_cCurvOfEdge(ivol, aid, edget[ix[1]], tang[1], &curv);
                    if (edget[ix[1]] - edget[ix[0]] < 0.) {
                        tang[0][0] = -tang[0][0];
                        tang[0][1] = -tang[0][1];
                        tang[0][2] = -tang[0][2];
                        tang[1][0] = -tang[1][0];
                        tang[1][1] = -tang[1][1];
                        tang[1][2] = -tang[1][2];
                    }
                    msh_SurfMeshSetTriTang(surfmesh, numel, no, tang);
                }
                /* evaluate normal at node, check for zeros */
                status = gi_qNormalToFace(ivol, iface, &uv[2 * tris[3 * i + j] - 2], x, vn[j]);
                if (vn[j][0] == 0. && vn[j][1] == 0. && vn[j][2] == 0.) {
                    iszero = 1;
                }
            }
            /* set triangle node normals */
            if (!iszero) {
                msh_SurfMeshSetTriNorm(surfmesh, numel, vn);
            }
        }
        base += plen;
    }
    vis_IdTranEnd(idtran);
}

/*----------------------------------------------------------------------
                      Illustrate CAPRI Interface to SurfMesh and TetMesh
----------------------------------------------------------------------*/
int
main(int argc, char* argv[])
{
    Vint i;
    vis_Connect *connectsrf, *connecttet, *connect;
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    Vint imodel, status, nvol, ivol, icode;
    Vint ierr;
    Vint numpnts, numtris;
    Vint numnp, numel;
    Vchar buf[256];
    Vdouble extent[2][3], dmax, d, edgelen;

    if (argc < 3) {
        printf("Usage: %s modeller model\n", argv[0]);
        return 1;
    }

    status = gi_uStart();
    if (status) {
        printf("Unable to start CAPRI\n");
        return 1;
    }
    printf("CAPRI started\n");

    gi_putenv("CAPRItess=On");
    imodel = gi_uLoadModel(NULL, argv[1], argv[2]);
    if (imodel == 0) {
        printf("Unable to open %s model %s\n", argv[1], argv[2]);
        return 1;
    }
    printf("CAPRI model loaded\n");

    /* get number of volumes */
    nvol = gi_uNumVolumes();

    /* get maximum extent of all volumes */
    dmax = 0.;
    for (ivol = 1; ivol <= nvol; ++ivol) {
        icode = gi_dBox(ivol, (double*)extent);
        for (i = 0; i < 3; ++i) {
            d = extent[1][i] - extent[0][i];
            if (d > dmax)
                dmax = d;
        }
    }
    edgelen = dmax / 20.;
    /* instance surface mesher and tet mesher */
    surfmesh = msh_SurfMeshBegin();
    tetmesh = msh_TetMeshBegin();
    /* instance Connect object to hold resultant mesh */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    /* mesh each volume separately */
    for (ivol = 1; ivol <= nvol; ++ivol) {
        connectsrf = vis_ConnectBegin();
        vis_ConnectPre(connectsrf, SYS_DOUBLE);
        msh_SurfMeshSetFunction(surfmesh, SURFMESH_FUN_GEOPROJ, (Vfunc*)project_surfmesh2capri, (Vobject*)&ivol);

        /* load the CAPRI geometry tesselation into SurfMesh */
        loadvolume(ivol, surfmesh);
        msh_SurfMeshInq(surfmesh, &numpnts, &numtris);
        if (numpnts == 0 || numtris == 0)
            continue;
        /* compute extent to set reasonable mesh size */
        msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, edgelen);
        msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 45.);
        /* generate parabolic triangles */
        msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);
        msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam52capri.srf");
        msh_SurfMeshGenerate(surfmesh, connectsrf);
        ierr = msh_SurfMeshError(surfmesh);
        if (ierr) {
            printf("Error generating surface mesh, volume= %d\n", ivol);
            sprintf(buf, "model_%d.srf", ivol);
            msh_SurfMeshWrite(surfmesh, SYS_ASCII, buf);
            vis_ConnectEnd(connectsrf);
            continue;
        }
        printf("SurfMesh volume= %d complete\n", ivol);

        icode = gi_dVolumeType(ivol);
        /* volume is solid, generate tet mesh */
        if (icode == 0) {
            connecttet = vis_ConnectBegin();
            vis_ConnectPre(connecttet, SYS_DOUBLE);
            msh_TetMeshConnect(tetmesh, connectsrf);
            msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, edgelen);
            msh_TetMeshGenerate(tetmesh, connecttet);
            ierr = msh_TetMeshError(tetmesh);
            if (ierr) {
                printf("Error generating tet mesh, volume= %d\n", ivol);
                sprintf(buf, "model_%d.tet", ivol);
                msh_TetMeshWrite(tetmesh, SYS_ASCII, buf);
                vis_ConnectEnd(connecttet);
                continue;
            }
            printf("TetMesh volume= %d complete\n", ivol);
            vis_ConnectAppend(connect, connecttet);
            vis_ConnectEnd(connecttet);
            /* volume is shell */
        }
        else {
            vis_ConnectAppend(connect, connectsrf);
        }
        vis_ConnectEnd(connectsrf);
    }
    /* report total number of generate nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("total number of nodes= %d\n", numnp);
    printf("total number of elems= %d\n", numel);
    if (numnp && numel) {
        vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam52capri.bdf");
    }
    /* clean up */
    msh_SurfMeshEnd(surfmesh);
    msh_TetMeshEnd(tetmesh);
    vis_ConnectEnd(connect);
    status = gi_uRelModel(imodel);
    status = gi_uStop(0);
    return 0;
}

6.43. Example 52pk, Illustrate Parasolid Interface to SurfMesh and TetMesh

This example illustrates accessing geometry from the Parasolid toolkit and loading it into a SurfMesh object for surface meshing. The subsequent surface mesh is then transferred to a TetMesh object for tetrahedral volume meshing.

/***********************************************************************
 *                                                                      *
 *               Copyright (C) 2011, Visual Kinematics, Inc.            *
 *                                                                      *
 *  These coded inttructions, statements and computer programs contain  *
 *  unpublished proprietary information of Visual Kinematics, Inc.,     *
 *  and are protected by Federal copyright law.  They may not be        *
 *  disclosed to third parties or copied or duplicated in any form,     *
 *  in whole or in part, without the prior written consent of           *
 *  Visual Kinematics, Inc.                                             *
 *                                                                      *
 ***********************************************************************/
/*
   file       :  exam52pk.cxx
   description:  Parasolid PK file library object
   author     :  Arthur Muller
   date       :  May 5, 2011
   discussion :
*/

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "sam/base/base.h"
#include "sam/base/vututil.h"
#include "sam/vis/vis.h"
#include "sam/msh/msh.h"
/*
 * The three files below are part of the
 * Parasolid installation
 */
#include "parasolid_kernel.h"
#include "frustrum_ifails.h"
#include "frustrum_tokens.h"

/*
 * The frustrum functions below are required by
 * the Parasolid engine. We use the example frustrum
 * functions provided with the Parasolid distribution
 */
#include "frustrum.c"
/*
 * The static variables below
 * are required by the Parasolid engine
 */
static PK_SESSION_frustrum_t frustrum;
static PK_ERROR_frustrum_t errorFrustrum;
static PK_SESSION_start_o_t options;

/*
 * The error handler and memory allocation functions
 * are required by the Parasolid engine
 */
extern PK_ERROR_code_t
parasolid_ErrorHandler(PK_ERROR_sf_t* error)
{
    printf("PK error: %s returned %s\n", error->function, error->code_token);
    return error->code;
}

extern void
parasolid_ReturnMemory(int* nbytes, char** memory, int* ifail)
{
    vut_freeMem(*memory);
    *ifail = FR_no_errors;
}

extern void
parasolid_GetMemory(int* nbytes, char** memory, int* ifail)
{
    *ifail = FR_no_errors;
    *memory = (Vchar*)vut_mallocMem(*nbytes);
    if (*memory == NULL) {
        *ifail = FR_memory_full;
    }
}

static void
parasolid_Init()
{
    /* initialize frustrum with default values */
    errorFrustrum.handler_fn = parasolid_ErrorHandler;
    PK_SESSION_frustrum_o_m(frustrum);

    frustrum.fstart = FSTART;
    frustrum.fstop = FSTOP;
    frustrum.ffoprd = FFOPRD;
    frustrum.ffopwr = FFOPWR;
    frustrum.ffwrit = FFWRIT;
    frustrum.ffread = FFREAD;
    frustrum.ffclos = FFCLOS;
    frustrum.fabort = FABORT;
    frustrum.fmallo = parasolid_GetMemory;
    frustrum.fmfree = parasolid_ReturnMemory;

    /* register frustrum */
    PK_SESSION_register_frustrum(&frustrum);

    /* start the modeller */
    PK_SESSION_start_o_m(options);
    PK_SESSION_start(&options);
}

static void
parasolid_Term()
{
    PK_SESSION_stop();
}

char*
strremove(char* str, const char* sub)
{
    char *p, *q, *r;
    if (*sub && (q = r = strstr(str, sub)) != NULL) {
        size_t len = strlen(sub);
        while ((r = strstr(p = r + len, sub)) != NULL) {
            memmove(q, p, r - p);
            q += r - p;
        }
        memmove(q, p, strlen(p) + 1);
    }
    return str;
}

static Vint writesurfmesh = 1;

/*----------------------------------------------------------------------
          Illustrate Parasolid x_t/x_b interface to SurfMesh
----------------------------------------------------------------------*/
int
main(int argc, char* argv[])
{
    /* DevTools interface definitions */
    msh_SurfMesh* surfmesh;
    msh_TetMesh* tetmesh;
    vsy_IntHash* nodeih;
    vsy_IntHash* vertexih;
    vis_Connect *connectsrf, *connect, *connecttet;
    vsy_IntHash *bodylih, *bodyrih, *conicih;
    vsy_IntHash* finedgeih;
    vsy_IntHash* pointvertexih;
    vsy_IntVec* faceorientiv;
    Vint nparts, geobody, geobodyrb, geobodylb;
    Vint ii, j, m, finID, finIndex, point, normalIndex, facetID, geoface;
    Vint geoedge[3], geovert[3];
    Vint length, ix[3], index;
    Vint conicid, numconic, isconic;
    Vdouble *xl, *xn, vertex[3][3], normals[3][3];
    Vdouble xo[3], axis[3], ref[3], cr, cc;
    Vint numel, numnp, eflags[3], sense;
    Vchar buffer[256];
    Vdouble minext[3], maxext[3], diaglen, edgelen;
    int n_vertices, n_regions, n_shells, isvoid, n_faces;
    double t, tang[2][3], etang[3];
    Vint ierr, tetmeshoption, nfree;
    Vchar filnam[256];

    /* Parasolid interface definitions */
    PK_PART_t* parts;
    PK_TOPOL_fctab_facet_fin_s* facet_fin;
    PK_TOPOL_fctab_fin_fin_s* fin_fin;
    PK_TOPOL_fctab_fin_data_s* fin_data;
    PK_TOPOL_fctab_data_point_s* data_point_idx;
    PK_TOPOL_fctab_data_normal_s* data_normal_idx;
    PK_TOPOL_fctab_point_vec_s* point_vec;
    PK_TOPOL_fctab_normal_vec_s* normal_vec;
    PK_TOPOL_fctab_facet_face_s* facet_face;
    PK_TOPOL_fctab_fin_edge_s* fin_edge;
    PK_TOPOL_fctab_point_topol_s* point_topol;
    PK_VERTEX_t* vertices;
    PK_REGION_t* regions;
    PK_SHELL_t* shells;
    PK_FACE_t* faces;
    PK_LOGICAL_t *orients, orientf;
    PK_ERROR_t err = PK_ERROR_no_errors;
    PK_PART_receive_o_t receive_opts;
    PK_CLASS_t eclass, sclass;
    PK_BODY_type_t bodyType;
    PK_TOPOL_facet_2_r_t tables;
    PK_TOPOL_facet_2_o_t facetOptions;
    PK_GEOM_range_vector_o_t options;
    PK_range_result_t range_result;
    PK_range_1_r_t range;
    PK_LOGICAL_t is_solid;
    PK_SURF_t surf;
    PK_CURVE_t curve;
    PK_VECTOR_t pt, tg;
    char filename[80];

    if (argc < 2) {
        fprintf(stderr, "Usage: %s parasolid_x_t_file\n", argv[0]);
        return 0;
    }

    nparts = 0;
    parts = NULL;
    numnp = numel = 0;

    /* open Parasolid file */
    PK_PART_receive_o_m(receive_opts);
    if (strstr(argv[1], ".x_t") || strstr(argv[1], ".xmt_txt")) {
        receive_opts.transmit_format = PK_transmit_format_text_c;
    }
    else if (strstr(argv[1], ".x_b") || strstr(argv[1], ".xmt_bin")) {
        receive_opts.transmit_format = PK_transmit_format_binary_c;
    }
    else {
        fprintf(stderr, "Unknown file extension\n");
        return 1;
    }
    /* initialize Parasolid engine */
    parasolid_Init();
    /* load Parasolid parts */
    strcpy(&filename[0], argv[1]);
#ifndef _WIN32
    // In unix, frustrum.c implements FFOPRD which always expects a part with name
    // NAME.xmt_txt in disk (AKA PS_LONG_NAME) but it must be passed without the extension.
    // As such, it is necessary to remove the ".xmt_txt" from the filename
    strremove(&filename[0], ".xmt_txt");
    printf("loading file %s.xmt_txt\n", filename);
#else
    strremove(&filename[0], ".x_t");
    printf("loading file %s.x_t\n", filename);
#endif
    err = PK_PART_receive(filename, &receive_opts, &nparts, &parts);
    if (err != PK_ERROR_no_errors) {
        fprintf(stderr, "PK_PART_receive error= %d\n", err);
        return 1;
    }
    /* set default Parasolid tesselation options */
    PK_TOPOL_facet_2_o_m(facetOptions);
    /* set default Parasolid point projection options */
    PK_GEOM_range_vector_o_m(options);

    /* initialize DevTools objects */
    surfmesh = msh_SurfMeshBegin();
    tetmesh = msh_TetMeshBegin();
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vertexih = vsy_IntHashBegin();

    /* initialize tesselation choice options */
    facetOptions.choice.facet_fin = true;
    facetOptions.choice.fin_data = true;
    facetOptions.choice.data_point_idx = true;
    facetOptions.choice.data_normal_idx = true;
    facetOptions.choice.point_vec = true;
    facetOptions.choice.normal_vec = true;
    facetOptions.choice.facet_face = true;
    facetOptions.choice.fin_edge = true;
    facetOptions.choice.point_topol = true;
    facetOptions.choice.facet_fin = PK_LOGICAL_true;
    facetOptions.choice.fin_fin = PK_LOGICAL_true;
    facetOptions.choice.fin_data = PK_LOGICAL_true;
    facetOptions.choice.data_point_idx = PK_LOGICAL_true;
    facetOptions.choice.data_normal_idx = PK_LOGICAL_true;
    facetOptions.choice.point_vec = PK_LOGICAL_true;
    facetOptions.choice.normal_vec = PK_LOGICAL_true;
    facetOptions.choice.facet_face = PK_LOGICAL_true;
    facetOptions.choice.fin_edge = PK_LOGICAL_true;
    facetOptions.choice.point_topol = PK_LOGICAL_true;

    /* initialize tesselation control options */
    facetOptions.control.shape = PK_facet_shape_convex_c;
    facetOptions.control.match = PK_facet_match_topol_c;
    /*
     * The option below is used to minimize the number
     * of edge tesselation point that are not on the edge
     */
    facetOptions.control.quality = PK_facet_quality_improved_c;

    /* loop over all parts */
    for (int i = 0; i < nparts; i++) {
        PK_ENTITY_ask_class(parts[i], &eclass);
        if (eclass == PK_CLASS_body) {
            /* initialize IntHash for body to the left of face */
            bodylih = vsy_IntHashBegin();
            bodyrih = vsy_IntHashBegin();
            conicih = vsy_IntHashBegin();
            faceorientiv = vsy_IntVecBegin();
            numconic = 0;

            /* check for solid, sheet, or non-manifold body */
            PK_BODY_ask_type(parts[i], &bodyType);
            if (bodyType == PK_BODY_type_sheet_c || bodyType == PK_BODY_type_solid_c || bodyType == PK_BODY_type_general_c) {
                if (bodyType == PK_BODY_type_general_c) {
                    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, SYS_ON);
                }
                else {
                    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, SYS_OFF);
                }

                /* create region->face map */
                PK_BODY_ask_regions(parts[i], &n_regions, &regions);
                for (int ir = 0; ir < n_regions; ir++) {
                    /* skip non-solid regions */
                    PK_REGION_is_solid(regions[ir], &is_solid);
                    if (is_solid == false) {
                        continue;
                    }

                    PK_REGION_ask_shells(regions[ir], &n_shells, &shells);
                    /*
                     * The "isvoid" flag checks whether any face orientation
                     * points outwardly to the body. If all faces point
                     * inwardly SurfMeshGenerate will still succeed.  However,
                     * this problem needs to be accounted for when passing
                     * the surface mesh to TetMesh.
                     */
                    isvoid = 1;
                    for (int is = 0; is < n_shells; is++) {
                        PK_SHELL_ask_oriented_faces(shells[is], &n_faces, &faces, &orients);
                        for (int ifa = 0; ifa < n_faces; ifa++) {
                            if (!orients[ifa]) {
                                vsy_IntHashInsert(bodylih, faces[ifa], regions[ir]);
                                isvoid = 0;
                            }
                            else {
                                vsy_IntHashInsert(bodyrih, faces[ifa], regions[ir]);
                            }
                            PK_FACE_ask_oriented_surf(faces[ifa], &surf, &orientf);
                            PK_ENTITY_ask_class(surf, &sclass);
                            isconic = 0;
                            if (sclass == PK_CLASS_plane) {
                                isconic = sclass;
                            }
                            else if (sclass == PK_CLASS_cyl) {
                                isconic = sclass;
                            }
                            else if (sclass == PK_CLASS_cone) {
                                isconic = sclass;
                            }
                            else if (sclass == PK_CLASS_sphere) {
                                isconic = sclass;
                            }
                            else if (sclass == PK_CLASS_torus) {
                                isconic = sclass;
                            }
                            if (isconic) {
                                vsy_IntHashLookup(conicih, surf, &conicid);
                                if (conicid == 0) {
                                    numconic += 1;
#ifdef MESH_DEBUG
                                    printf("conicid= %d, orientf= %d\n", numconic, orientf);
#endif
                                    vsy_IntHashInsert(conicih, surf, numconic);
                                    vsy_IntVecSet(faceorientiv, numconic, orientf);
                                }
                            }
                        }
                        if (n_faces) {
                            PK_MEMORY_free(faces);
                            PK_MEMORY_free(orients);
                        }
                    }
                    if (n_shells) {
                        PK_MEMORY_free(shells);
                    }
                }
                if (n_regions) {
                    PK_MEMORY_free(regions);
                }

                /* assign part number to vertices */
                numnp = numel = 0;
                PK_BODY_ask_vertices(parts[i], &n_vertices, &vertices);
                for (j = 0; j < n_vertices; j++) {
                    vsy_IntHashInsert(vertexih, vertices[j], parts[i]);
                }
                if (n_vertices) {
                    PK_MEMORY_free(vertices);
                }
                /* tesselate part */
                err = PK_TOPOL_facet_2(1, &parts[i], NULL, &facetOptions, &tables);
                if (err != 0) {
                    fprintf(stderr, "PK_TOPOL_facet_2 error= %d\n", err);
                    return 1;
                }

                /* gather tables */
                for (j = 0; j < tables.number_of_tables; j++) {
                    if (tables.tables[j].fctab == PK_TOPOL_fctab_facet_fin_c) {
                        facet_fin = tables.tables[j].table.facet_fin;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_fin_fin_c) {
                        fin_fin = tables.tables[j].table.fin_fin;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_fin_data_c) {
                        fin_data = tables.tables[j].table.fin_data;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_data_point_c) {
                        data_point_idx = tables.tables[j].table.data_point_idx;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_data_normal_c) {
                        data_normal_idx = tables.tables[j].table.data_normal_idx;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_point_vec_c) {
                        point_vec = tables.tables[j].table.point_vec;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_normal_vec_c) {
                        normal_vec = tables.tables[j].table.normal_vec;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_facet_face_c) {
                        facet_face = tables.tables[j].table.facet_face;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_fin_edge_c) {
                        fin_edge = tables.tables[j].table.fin_edge;
                    }
                    else if (tables.tables[j].fctab == PK_TOPOL_fctab_point_topol_c) {
                        point_topol = tables.tables[j].table.point_topol;
                    }
                }
                /* initialize SurfMesh */
                msh_SurfMeshDef(surfmesh, point_vec->length, facet_fin->length / 3);

                /* set conic sections */
                vsy_IntHashInitIter(conicih);
                while (vsy_IntHashNextIter(conicih, &surf, &conicid), conicid) {
                    vsy_IntVecGet(faceorientiv, conicid, &sense);
                    if (sense == 0)
                        sense = -1;
#ifdef MESH_DEBUG
                    printf("conicid= %d\n", conicid);
#endif
                    PK_ENTITY_ask_class(surf, &sclass);
                    if (sclass == PK_CLASS_plane) {
                        PK_PLANE_sf_t plane_sf;
#ifdef MESH_DEBUG
                        printf("PK_CLASS_plane\n");
#endif
                        PK_PLANE_ask(surf, &plane_sf);
                        xo[0] = plane_sf.basis_set.location.coord[0];
                        xo[1] = plane_sf.basis_set.location.coord[1];
                        xo[2] = plane_sf.basis_set.location.coord[2];
                        axis[0] = plane_sf.basis_set.axis.coord[0];
                        axis[1] = plane_sf.basis_set.axis.coord[1];
                        axis[2] = plane_sf.basis_set.axis.coord[2];
                        ref[0] = plane_sf.basis_set.ref_direction.coord[0];
                        ref[1] = plane_sf.basis_set.ref_direction.coord[1];
                        ref[2] = plane_sf.basis_set.ref_direction.coord[2];
                        msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_PLANE, sense, xo, axis, ref, 0., 0.);
                    }
                    else if (sclass == PK_CLASS_cyl) {
                        PK_CYL_sf_t cyl_sf;
#ifdef MESH_DEBUG
                        printf("PK_CLASS_cyl\n");
#endif
                        PK_CYL_ask(surf, &cyl_sf);
                        xo[0] = cyl_sf.basis_set.location.coord[0];
                        xo[1] = cyl_sf.basis_set.location.coord[1];
                        xo[2] = cyl_sf.basis_set.location.coord[2];
                        axis[0] = cyl_sf.basis_set.axis.coord[0];
                        axis[1] = cyl_sf.basis_set.axis.coord[1];
                        axis[2] = cyl_sf.basis_set.axis.coord[2];
                        ref[0] = cyl_sf.basis_set.ref_direction.coord[0];
                        ref[1] = cyl_sf.basis_set.ref_direction.coord[1];
                        ref[2] = cyl_sf.basis_set.ref_direction.coord[2];
                        cr = cyl_sf.radius;
                        msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_CYLINDER, sense, xo, axis, ref, cr, 0.);
                    }
                    else if (sclass == PK_CLASS_cone) {
                        PK_CONE_sf_t cone_sf;
#ifdef MESH_DEBUG
                        printf("PK_CLASS_cone\n");
#endif
                        PK_CONE_ask(surf, &cone_sf);
                        xo[0] = cone_sf.basis_set.location.coord[0];
                        xo[1] = cone_sf.basis_set.location.coord[1];
                        xo[2] = cone_sf.basis_set.location.coord[2];
                        axis[0] = cone_sf.basis_set.axis.coord[0];
                        axis[1] = cone_sf.basis_set.axis.coord[1];
                        axis[2] = cone_sf.basis_set.axis.coord[2];
                        ref[0] = cone_sf.basis_set.ref_direction.coord[0];
                        ref[1] = cone_sf.basis_set.ref_direction.coord[1];
                        ref[2] = cone_sf.basis_set.ref_direction.coord[2];
                        cr = cone_sf.semi_angle;
                        msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_CONE, sense, xo, axis, ref, cr, 0.);
                    }
                    else if (sclass == PK_CLASS_sphere) {
                        PK_SPHERE_sf_t sphere_sf;
#ifdef MESH_DEBUG
                        printf("PK_CLASS_sphere\n");
#endif
                        PK_SPHERE_ask(surf, &sphere_sf);
                        xo[0] = sphere_sf.basis_set.location.coord[0];
                        xo[1] = sphere_sf.basis_set.location.coord[1];
                        xo[2] = sphere_sf.basis_set.location.coord[2];
                        axis[0] = sphere_sf.basis_set.axis.coord[0];
                        axis[1] = sphere_sf.basis_set.axis.coord[1];
                        axis[2] = sphere_sf.basis_set.axis.coord[2];
                        ref[0] = sphere_sf.basis_set.ref_direction.coord[0];
                        ref[1] = sphere_sf.basis_set.ref_direction.coord[1];
                        ref[2] = sphere_sf.basis_set.ref_direction.coord[2];
                        cr = sphere_sf.radius;
                        msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_SPHERE, sense, xo, axis, ref, cr, 0.);
                    }
                    else if (sclass == PK_CLASS_torus) {
                        PK_TORUS_sf_t torus_sf;
#ifdef MESH_DEBUG
                        printf("PK_CLASS_torus\n");
#endif
                        PK_TORUS_ask(surf, &torus_sf);
                        xo[0] = torus_sf.basis_set.location.coord[0];
                        xo[1] = torus_sf.basis_set.location.coord[1];
                        xo[2] = torus_sf.basis_set.location.coord[2];
                        axis[0] = torus_sf.basis_set.axis.coord[0];
                        axis[1] = torus_sf.basis_set.axis.coord[1];
                        axis[2] = torus_sf.basis_set.axis.coord[2];
                        ref[0] = torus_sf.basis_set.ref_direction.coord[0];
                        ref[1] = torus_sf.basis_set.ref_direction.coord[1];
                        ref[2] = torus_sf.basis_set.ref_direction.coord[2];
                        cr = torus_sf.minor_radius;
                        cc = torus_sf.major_radius;
                        msh_SurfMeshSetConic(surfmesh, conicid, SURFMESH_CONIC_TORUS, sense, xo, axis, ref, cr, cc);
#ifdef MESH_DEBUG
                        printf("xo= %e %e %e\n", xo[0], xo[1], xo[2]);
                        printf("axis= %e %e %e\n", axis[0], axis[1], axis[2]);
                        printf("ref= %e %e %e\n", ref[0], ref[1], ref[2]);
                        printf("minor_radius= %e\n", torus_sf.minor_radius);
                        printf("major_radius= %e\n", torus_sf.major_radius);
#endif
                    }
                }
                /* map fin->edge information */
                numnp = numel = 0;
                finedgeih = vsy_IntHashBegin();
                for (ii = 0; ii < fin_edge->length; ii++) {
                    vsy_IntHashInsert(finedgeih, fin_edge->data[ii].fin, fin_edge->data[ii].edge);
                }
                /* map point->vertex information */
                pointvertexih = vsy_IntHashBegin();
                for (ii = 0; ii < point_topol->length; ii++) {
                    vsy_IntHashInsert(pointvertexih, point_topol->data[ii].point + 1, point_topol->data[ii].topol);
                }
                nodeih = vsy_IntHashBegin();

                /* loop over all fins */
                length = facet_fin->length;
                j = 0;
                for (ii = 0; ii < length; ii++) {
                    finID = facet_fin->data[ii].fin;
                    finIndex = fin_data->data[finID];
                    point = data_point_idx->point[finIndex];
                    xl = point_vec->vec[point].coord;
                    vertex[j][0] = xl[0];
                    vertex[j][1] = xl[1];
                    vertex[j][2] = xl[2];
                    normalIndex = data_normal_idx->normal[finIndex];
                    xn = normal_vec->vec[normalIndex].coord;
                    facetID = facet_fin->data[ii].facet;
                    geoface = facet_face->face[facetID];
                    PK_FACE_ask_surf(geoface, &surf);
                    vsy_IntHashLookup(conicih, surf, &conicid);
                    vsy_IntHashLookup(finedgeih, finID, &geoedge[(j + 2) % 3]);
                    vsy_IntHashLookup(nodeih, point + 1, &index);

                    /* check for new node */
                    if (index == 0) {
                        index = ++numnp;

                        /* update extent */
                        if (index == 1) {
                            minext[0] = maxext[0] = vertex[j][0];
                            minext[1] = maxext[1] = vertex[j][1];
                            minext[2] = maxext[2] = vertex[j][2];
                        }
                        else {
                            if (vertex[j][0] < minext[0])
                                minext[0] = vertex[j][0];
                            if (vertex[j][1] < minext[1])
                                minext[1] = vertex[j][1];
                            if (vertex[j][2] < minext[2])
                                minext[2] = vertex[j][2];
                            if (vertex[j][0] > maxext[0])
                                maxext[0] = vertex[j][0];
                            if (vertex[j][1] > maxext[1])
                                maxext[1] = vertex[j][1];
                            if (vertex[j][2] > maxext[2])
                                maxext[2] = vertex[j][2];
                        }
                        vsy_IntHashInsert(nodeih, point + 1, index);

                        /*
                         * Check whether node is on edge/vertex, or interior
                         * to a surface.
                         */
                        vsy_IntHashLookup(pointvertexih, point + 1, &geovert[j]);
                        vsy_IntHashLookup(vertexih, geovert[j], &geobody);
                        if (geobody) {
                            msh_SurfMeshSetPoint(surfmesh, index, vertex[j], 1);
                            msh_SurfMeshSetPointAssoc(surfmesh, VIS_GEOVERT, index, geovert[j]);
                        }
                        else {
                            msh_SurfMeshSetPoint(surfmesh, index, vertex[j], 0);
                        }
                    }
                    normals[j][0] = xn[0];
                    normals[j][1] = xn[1];
                    normals[j][2] = xn[2];
                    ix[j] = index;
                    ++j;
                    /* every 3 fins => completed triangle */
                    if (j == 3) {
                        ++numel;

                        /* gather edge information */
                        for (m = 1; m <= 3; m++) {
                            eflags[m - 1] = 0;
                            if (geoedge[m - 1]) {
                                eflags[m - 1] = 1;
                            }
                        }
                        /* insert triangle, its associations, and normals */
                        msh_SurfMeshSetTri(surfmesh, numel, ix, eflags);
                        msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOFACE, numel, SYS_FACE, 0, geoface);
                        vsy_IntHashLookup(bodylih, geoface, &geobodyrb);
                        vsy_IntHashLookup(bodyrih, geoface, &geobodylb);
                        msh_SurfMeshSetTriBack(surfmesh, numel, geobodyrb, geobodylb);
                        if (geobodyrb) {
                            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, numel, SYS_ELEM, 0, geobodyrb);
                            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, numel, SYS_ELEM, 0, geobodyrb);
                        }
                        if (geobodylb) {
                            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, numel, SYS_ELEM, -1, geobodylb);
                            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, numel, SYS_ELEM, -1, geobodylb);
                        }
                        msh_SurfMeshSetTriNorm(surfmesh, numel, normals);
                        if (conicid) {
                            msh_SurfMeshSetTriConic(surfmesh, numel, conicid);
                        }
                        /* compute edge tangents for geometry edges */
                        for (m = 1; m <= 3; m++) {
                            if (geoedge[m - 1]) {
                                /* assign edge association */
                                msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOEDGE, numel, SYS_EDGE, m, geoedge[m - 1]);

                                /* compute parameter "t" on curve */
                                etang[0] = vertex[m % 3][0] - vertex[m - 1][0];
                                etang[1] = vertex[m % 3][1] - vertex[m - 1][1];
                                etang[2] = vertex[m % 3][2] - vertex[m - 1][2];
                                PK_EDGE_ask_curve(geoedge[m - 1], &curve);

                                /* first point on edge */
                                pt.coord[0] = vertex[m - 1][0];
                                pt.coord[1] = vertex[m - 1][1];
                                pt.coord[2] = vertex[m - 1][2];
                                err = PK_CURVE_parameterise_vector(curve, pt, &t);

                                /*
                                 * An error indicates that the point, although
                                 * supposedly on the edge, was placed away from the
                                 * during the tesselation. We then try to find
                                 * a point on the edge that is as close as possible
                                 * to the tesselation point
                                 */
                                if (err != 0) {
                                    err = PK_GEOM_range_vector(curve, pt, &options, &range_result, &range);
                                    if (err) {
                                        printf("Unable to project point to curve, err= %d\n", err);
                                        continue;
                                    }
                                    t = range.end.parameters[0];
                                }
                                PK_CURVE_eval_with_tangent(curve, t, 0, &pt, &tg);

                                /* make sure tangent is aligned with edge direction */
                                tang[0][0] = tg.coord[0];
                                tang[0][1] = tg.coord[1];
                                tang[0][2] = tg.coord[2];
                                if (tang[0][0] * etang[0] + tang[0][1] * etang[1] + tang[0][2] * etang[2] < 0.) {
                                    tang[0][0] = -tang[0][0];
                                    tang[0][1] = -tang[0][1];
                                    tang[0][2] = -tang[0][2];
                                }

                                /* likewise, second point on edge */
                                pt.coord[0] = vertex[m % 3][0];
                                pt.coord[1] = vertex[m % 3][1];
                                pt.coord[2] = vertex[m % 3][2];
                                err = PK_CURVE_parameterise_vector(curve, pt, &t);
                                if (err != 0) {
                                    err = PK_GEOM_range_vector(curve, pt, &options, &range_result, &range);
                                    if (err) {
                                        printf("Unable to project point to curve, err= %d\n", err);
                                        continue;
                                    }
                                    t = range.end.parameters[0];
                                }

                                PK_CURVE_eval_with_tangent(curve, t, 0, &pt, &tg);
                                tang[1][0] = tg.coord[0];
                                tang[1][1] = tg.coord[1];
                                tang[1][2] = tg.coord[2];
                                if (tang[1][0] * etang[0] + tang[1][1] * etang[1] + tang[1][2] * etang[2] < 0.) {
                                    tang[1][0] = -tang[1][0];
                                    tang[1][1] = -tang[1][1];
                                    tang[1][2] = -tang[1][2];
                                }

                                /* set tangents */
                                msh_SurfMeshSetTriTang(surfmesh, numel, m, tang);
                            }
                        }
                        j = 0;
                    }
                }
                vsy_IntHashEnd(nodeih);
                vsy_IntHashEnd(finedgeih);
                vsy_IntHashEnd(conicih);
                vsy_IntHashEnd(pointvertexih);
                vsy_IntVecEnd(faceorientiv);

                /* compute extent diagonal lenth for edge length */
                diaglen =
                sqrt((maxext[0] - minext[0]) * (maxext[0] - minext[0]) + (maxext[1] - minext[1]) * (maxext[1] - minext[1]) +
                     (maxext[2] - minext[2]) * (maxext[2] - minext[2]));
                edgelen = diaglen / 20.;
                msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, edgelen);
                msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 45.);

                /* generate parabolic triangles */
                msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);

                /* save SurfMesh file */
                if (writesurfmesh) {
                    sprintf(buffer, "exam52pk_%d.srf", parts[i]);
                    msh_SurfMeshWrite(surfmesh, SYS_ASCII, buffer);
                }

                /* generate surface mesh and write result */
                connectsrf = vis_ConnectBegin();
                vis_ConnectPre(connectsrf, SYS_DOUBLE);
                msh_SurfMeshGenerate(surfmesh, connectsrf);
                ierr = msh_SurfMeshError(surfmesh);
                if (ierr) {
                    printf("Error generating surface mesh, part= %d\n", parts[i]);
                    sprintf(filnam, "model-%d.srf", parts[i]);
                    msh_SurfMeshWrite(surfmesh, SYS_ASCII, filnam);
                    vis_ConnectEnd(connectsrf);
                    continue;
                }
                printf("SurfMesh part= %d complete\n", parts[i]);
                vis_ConnectNumber(connectsrf, SYS_NODE, &numnp);
                vis_ConnectNumber(connectsrf, SYS_ELEM, &numel);
                printf("Surface number of nodes= %d\n", numnp);
                printf("Surface number of elems= %d\n", numel);

                tetmeshoption = 1;
                /* test for no free edges */
                msh_SurfMeshGetInteger(surfmesh, SURFMESH_NUMFREEEDGE, &nfree);
                if (nfree) {
                    tetmeshoption = 0;
                }
                if (tetmeshoption) {
                    connecttet = vis_ConnectBegin();
                    vis_ConnectPre(connecttet, SYS_DOUBLE);
                    msh_TetMeshConnect(tetmesh, connectsrf);
                    msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, edgelen);
                    msh_TetMeshSetParamd(tetmesh, VIS_MESH_GROWTHRATE, 2.);
                    /* optional write of TetMesh contents for QA
          msh_TetMeshWrite (tetmesh,SYS_BINARY,"model1.btet");
                    */
                    msh_TetMeshGenerate(tetmesh, connecttet);
                    ierr = msh_TetMeshError(tetmesh);
                    if (ierr) {
                        printf("Error generating tet mesh, part= %d\n", parts[i]);
                        sprintf(filnam, "part_%d.btet", parts[i]);
                        msh_TetMeshWrite(tetmesh, SYS_BINARY, filnam);
                        vis_ConnectEnd(connecttet);
                        vsy_IntHashEnd(bodylih);
                        vsy_IntHashEnd(bodyrih);
                        continue;
                    }
                    printf("TetMesh part= %d complete\n", parts[i]);
                    vis_ConnectAppend(connect, connecttet);
                    vis_ConnectEnd(connecttet);
                }
                else {
                    vis_ConnectAppend(connect, connectsrf);
                }
                vis_ConnectEnd(connectsrf);
            }
            vsy_IntHashEnd(bodylih);
            vsy_IntHashEnd(bodyrih);
        }
    }
    /* report total number of generate nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("total number of nodes= %d\n", numnp);
    printf("total number of elems= %d\n", numel);
    /* optional write of final mesh */
    if (numnp && numel) {
        vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "model.bdf");
    }
    msh_SurfMeshEnd(surfmesh);
    msh_TetMeshEnd(tetmesh);
    vsy_IntHashEnd(vertexih);
    vis_ConnectEnd(connect);
    PK_MEMORY_free(parts);

    /* stop Parasolid engine */
    parasolid_Term();
}

6.44. Example 52occ, Illustrate OpenCASCADE Interface to SurfMesh and TetMesh

This example illustrates accessing geometry from the OpenCASCADE toolkit and loading it into a SurfMesh object for surface meshing. The subsequent surface mesh is then transferred to a TetMesh object for tetrahedral volume meshing.

#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/vis/visshar.h"
#include "sam/base/system.h"
#include "sam/vis/exam/exam52occ.hxx"
#include <new>
#include <stdio.h>

static Vdouble rstol[3][2] = {
//{1.e-1,1.e-1}, {0.9,1.e-1}, {1.e-1,0.9} };
{ONETHIRD, ONETHIRD},
{ONETHIRD, ONETHIRD},
{ONETHIRD, ONETHIRD}};
static Vdouble rschord[3][2] = {{0.5, 0.5}, {0., 0.5}, {0.5, 0.}};
static Vdouble rsorig[3][2] = {{0., 0.}, {1., 0.}, {0., 1.}};

/*----------------------------------------------------------------------
                      Illustrate OpenCASCADE Interface SurfMesh
----------------------------------------------------------------------*/
static void
exam52occ_nodeid(vsy_ADTree* adtree, vis_Connect* connect, Vdouble coord[3], Vint isvert, Vint* nvert, Vint* id)
{
    Vint key, numnp, minkey;
    Vdouble dist, mindist, x[3];
    /* see if point already exists */
    vsy_ADTreeRefPointInit(adtree, coord);
    minkey = 0;
    while (vsy_ADTreeRefPointNext(adtree, &key), key) {
        vis_ConnectCoordsdv(connect, 1, &key, &x);
        x[0] -= coord[0];
        x[1] -= coord[1];
        x[2] -= coord[2];
        dist = x[0] * x[0] + x[1] * x[1] + x[2] * x[2];
        if (minkey == 0) {
            mindist = dist;
            minkey = key;
        }
        else {
            if (dist < mindist) {
                mindist = dist;
                minkey = key;
            }
        }
    }
    if (minkey > 0) {
        *id = minkey;
        return;
    }
    /* add point */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    ++numnp;
    vis_ConnectSetCoordsdv(connect, numnp, coord);
    if (isvert) {
        vis_ConnectSetNodeAssoc(connect, VIS_GEOVERT, numnp, ++(*nvert));
    }
    vsy_ADTreeInsertPoint(adtree, numnp, coord);
    *id = numnp;
}

static void
exam52_surfnormal(Handle(Geom_Surface) geomsurf, Standard_Real u, Standard_Real v, Vdouble enorm[3], Vint* flag)
{
    Vint iter;

    *flag = 0;
    for (iter = 0; iter < 5; ++iter) {
        try {
            GeomLProp_SLProps aProp(geomsurf, u, v, 1, Precision::Angular());
            gp_Dir normal = aProp.Normal();
            normal.Coord(enorm[0], enorm[1], enorm[2]);
            *flag = 1;
            return;
        }
        catch (Standard_Failure) {
            u += 1.e-6;
            v += 1.e-6;
        }
    }
}

static void
exam52_edgetang(Handle(Geom_Curve) geomcurve, Standard_Real uv, Vdouble tang[3], Vint* flag)
{
    Vint iter;

    *flag = 0;
    for (iter = 0; iter < 5; ++iter) {
        try {
            GeomLProp_CLProps aProp(geomcurve, uv, 1, Precision::Angular());
            gp_Dir tangent;
            aProp.Tangent(tangent);
            tangent.Coord(tang[0], tang[1], tang[2]);
            *flag = 1;
            return;
        }
        catch (Standard_Failure) {
            uv += 1.e-6;
        }
    }
}

static void
exam52occ_configADTree(TopoDS_Shape& myShape, vsy_ADTree* adtree, Vdouble* diaglength)
{
    Vdouble dx, dy, dz, tol;
    Standard_Real xmin[3], xmax[3];

    /* Get bounding box for shape */
    Bnd_Box B;
    BRepBndLib::Add(myShape, B);
    B.Get(xmin[0], xmin[1], xmin[2], xmax[0], xmax[1], xmax[2]);

    /* Create ADTree with this bounding box */
    dx = xmax[0] - xmin[0];
    dy = xmax[1] - xmin[1];
    dz = xmax[2] - xmin[2];

    /* Set diagonal length of extent box */
    *diaglength = sqrt(dx * dx + dy * dy + dz * dz);

    xmin[0] -= 0.05 * dx;
    xmax[0] += 0.05 * dx;
    xmin[1] -= 0.05 * dy;
    xmax[1] += 0.05 * dy;
    xmin[2] -= 0.05 * dz;
    xmax[2] += 0.05 * dz;
    vsy_ADTreeDef(adtree, ADTREE_POINT, 100, xmin, xmax);
    tol = 1.1 * sqrt(dx * dx + dy * dy + dz * dz) * 1.e-8;
    vsy_ADTreeSetParamd(adtree, ADTREE_TOLERANCE, tol);
}

static void
exam52occ_addVertex(TopoDS_Shape& myShape, vsy_ADTree* adtree, vis_Connect* connect, Vint* nvert)
{
    Standard_Real x, y, z;
    TopExp_Explorer ex;
    Vdouble coord[3];
    Vint id;

    /* insert all vertices; check for repeated */
    for (ex.Init(myShape, TopAbs_VERTEX); ex.More(); ex.Next()) {
        TopoDS_Vertex vertex = TopoDS::Vertex(ex.Current());
        gp_Pnt p = BRep_Tool::Pnt(vertex);
        p.Coord(x, y, z);
        coord[0] = x;
        coord[1] = y;
        coord[2] = z;

        /* get node id */
        exam52occ_nodeid(adtree, connect, coord, 1, nvert, &id);
    }
}

static void
exam52occ_sort3(Vint ix[3])
{
    Vint imin, imax, kx[3], i;

    imin = (ix[0] < ix[1]) ? ((ix[0] < ix[2] ? ix[0] : ix[2])) : ((ix[1] < ix[2] ? ix[1] : ix[2]));
    imax = (ix[0] < ix[1]) ? ((ix[1] < ix[2] ? ix[2] : ix[1])) : ((ix[0] < ix[2] ? ix[2] : ix[0]));
    kx[0] = kx[1] = kx[2] = 0;
    for (i = 0; i < 3; ++i) {
        if (imin == ix[i]) {
            kx[i] = 1;
        }
        if (imax == ix[i]) {
            kx[i] = 1;
        }
    }
    for (i = 0; i < 3; ++i) {
        if (kx[i] == 0) {
            ix[1] = ix[i];
            break;
        }
    }
    ix[0] = imin;
    ix[2] = imax;
}

int
main(int argc, char* argv[])
{
    TopoDS_Shape myShape;
    vis_Connect* connect;
    Vint n, j, k, rev;
    Vint numnp, numel, nix, ix[3], index, ntri, npts, ixn[3];
    Vdouble coord[3];
    Vchar brepname[SYS_MAXPATHCHAR];
    Vchar srfname[SYS_MAXPATHCHAR];
    Vint nvert = 0;
    Standard_Real x, y, z;
    vsy_ADTree* adtree;
    BRep_Builder b;
    Vint shape, maxi, maxj, maxk, key;
    Vdouble xyz[3], enorm[3][3];
    vsy_IntVHash* ivhenum;
    vsy_IntHash *ihnode, *ihedge;
    vsy_VHashTable* vhtang;
    Vint numedge, numface, in0, in1;
    Vdouble edgelength = 0.;
    Vdouble diaglength, chordheight;
    Vint iquad = 0, itet = 0;
    Vint ierr;
    msh_SurfMesh* surfmesh;
    vis_Connect* connectsrf;
    msh_TetMesh* tetmesh;
    vis_Connect* connecttet;
    vsy_IntVHash* ivhall;

    /* gather all parameters */
    strcpy(srfname, "exam52occ.srf");
    for (int iarg = 1; iarg < argc; ++iarg) {
        if (strcmp(argv[iarg], "-quad") == 0) {
            iquad = 1;
        }
        else if (strcmp(argv[iarg], "-srfname") == 0) {
            ++iarg;
            strcpy(srfname, argv[iarg]);
        }
        else if (strcmp(argv[iarg], "-tet") == 0) {
            itet = 1;
        }
        else if (strcmp(argv[iarg], "-el") == 0) {
            ++iarg;
            sscanf(argv[iarg], "%le", &edgelength);
        }
    }
    strcpy(brepname, argv[argc - 1]);

    /* read brep file */
    printf("reading file= %s\n", brepname);
    BRepTools::Read(myShape, brepname, b);
    if (myShape.IsNull()) {
        printf("Unable to read brep file= %s\n", brepname);
        return 1;
    }

    /* create Connect object to store geometry tesselation information */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    numnp = numel = nvert = 0;

    /* create shape with missing additional edges */
    TopoDS_Compound toadd;
    ihedge = vsy_IntHashBegin();
    ivhenum = vsy_IntVHashBegin();
    vhtang = vsy_VHashTableBegin();
    vsy_IntVHashDef(ivhenum, 2, 100);
    vsy_VHashTableDef(vhtang, 2, 100);

    /* create ADTree to generate unique points */
    adtree = vsy_ADTreeBegin();
    exam52occ_configADTree(myShape, adtree, &diaglength);
    /* set meshing edgelength, tesselation chordheight */
    if (edgelength == 0.) {
        edgelength = .05 * diaglength;
    }
    chordheight = .001 * diaglength;

    /* tesseleate geometry */
    BRepTools::Clean(myShape);
    BRepMesh_IncrementalMesh(myShape, chordheight);

    /* insert all vertices; check for repeated */
    exam52occ_addVertex(myShape, adtree, connect, &nvert);

    /* extract face tesselation */
    TopTools_IndexedMapOfShape faceMap;
    TopExp::MapShapes(myShape, TopAbs_FACE, faceMap);
    ivhall = vsy_IntVHashBegin();
    vsy_IntVHashDef(ivhall, 3, faceMap.Extent());

    numedge = 0;
    numface = 0;
    for (int j = 1; j <= faceMap.Extent(); ++j) {
        const TopoDS_Face& face = TopoDS::Face(faceMap(j));
        numface += 1;

        /* Extract geometric surface from face */
        TopLoc_Location surflocation;
        Handle(Geom_Surface) geomsurf = BRep_Tool::Surface(face, surflocation);

        /* Map nodes on this face */
        vsy_IntVHash* ivhfreeedges;
        ivhfreeedges = vsy_IntVHashBegin();
        vsy_IntVHashDef(ivhfreeedges, 2, 100);

        TopLoc_Location location;
        Handle_Poly_Triangulation triangulation = BRep_Tool::Triangulation(face, location);
        if (triangulation.IsNull())
            continue;

        const TColgp_Array1OfPnt& nodes = triangulation->Nodes();
        const Poly_Array1OfTriangle& triangles = triangulation->Triangles();
        const TColgp_Array1OfPnt2d& uvnodes = triangulation->UVNodes();

        /* store all nodes first */
        Vint minnode = nodes.Lower();
        Vint maxnode = nodes.Upper();
        Vint sizenode = maxnode - minnode + 1;
        Vint* nodemap = (Vint*)malloc(sizenode * sizeof(Vint));
        for (int i = minnode; i <= maxnode; i++) {
            const gp_Pnt& p1 = nodes(i);
            p1.Coord(x, y, z);
            location.Transformation().Transforms(x, y, z);
            coord[0] = x;
            coord[1] = y;
            coord[2] = z;
            exam52occ_nodeid(adtree, connect, coord, 0, &nvert, &index);
            nodemap[i - minnode] = index;
        }

        /* Iterate over the triangles and their nodes */
        Vint firstflagged = 0;
        for (int i = triangles.Lower(); i <= triangles.Upper(); ++i) {
            const Poly_Triangle& triangle = triangles(i);

            triangle.Get(ixn[0], ixn[1], ixn[2]);
            /* first loop through nodes */
            for (k = 0; k < 3; k++) {
                ix[k] = nodemap[ixn[k] - minnode];
            }
            ++numel;
            /* check for degenerate triangle */
            if (ix[0] == ix[1] || ix[0] == ix[2] || ix[1] == ix[2]) {
                vis_ConnectSetTopology(connect, numel, SYS_SHAPEPOINT, 1, 0, 0);
                vis_ConnectSetElemNode(connect, numel, ix);
                continue;
            }
            vis_ConnectSetTopology(connect, numel, SYS_SHAPETRI, 0, 0, 0);
            vis_ConnectSetElemNode(connect, numel, ix);
            vis_ConnectSetElemAssoc(connect, VIS_GEOFACE, numel, j);
            if (face.Orientation() == TopAbs_REVERSED) {
                vis_ConnectSetElemAssoc(connect, VIS_MISCID, numel, 1);
            }

            /* second loop through nodes for normals */
            Vint flag, normflag = 0;
            Standard_Real uvo[3][2], uvtol[3][2];
            for (k = 0; k < 3; k++) {
                const gp_Pnt2d& uv = uvnodes(triangle(k + 1));
                uv.Coord(uvo[k][0], uvo[k][1]);
            }
            /* check for geovert */
            Vint vertid[3];
            vis_ConnectNodeAssoc(connect, VIS_GEOVERT, 3, ix, vertid);
#ifdef OLDWAY
            Vdouble h[3];
            for (k = 0; k < 3; k++) {
                if (vertid[k]) {
                    vis_shape2DGendv(1, SYS_SHAPETRI, 0, 0, rstol[k], h, NULL);
                    uvtol[k][0] = h[0] * uvo[0][0] + h[1] * uvo[1][0] + h[2] * uvo[2][0];
                    uvtol[k][1] = h[0] * uvo[0][1] + h[1] * uvo[1][1] + h[2] * uvo[2][1];
                }
                else {
                    MOVE2(uvo[k], uvtol[k]);
                }
            }
#endif

            Vdouble enormal[3], xtri[3][3], v1[3], v2[3];
            vis_ConnectCoordsdv(connect, 3, ix, xtri);
            DIFF3(xtri[1], xtri[0], v1);
            DIFF3(xtri[2], xtri[0], v2);
            CROSS3(v1, v2, enormal);
            UNIT3(enormal);
            for (k = 0; k < 3; k++) {
                const gp_Pnt2d& uv = uvnodes(triangle(k + 1));
                if (vertid[k]) {
                    Vdouble uvv[2], h[3];
                    for (int l = 0; l < 10; ++l) {
                        uvv[0] = rsorig[k][0] + (rschord[k][0] - rsorig[k][0]) * 0.1 * l;
                        uvv[1] = rsorig[k][0] + (rschord[k][0] - rsorig[k][0]) * 0.1 * l;
                        vis_shape2DGendv(1, SYS_SHAPETRI, 0, 0, uvv, h, NULL);
                        uvtol[k][0] = h[0] * uvo[0][0] + h[1] * uvo[1][0] + h[2] * uvo[2][0];
                        uvtol[k][1] = h[0] * uvo[0][1] + h[1] * uvo[1][1] + h[2] * uvo[2][1];
                        exam52_surfnormal(geomsurf, uvtol[k][0], uvtol[k][1], enorm[k], &flag);
                        if (flag == 1) {
                            ++normflag;
                            gp_Vec vec = gp_Vec(enorm[k][0], enorm[k][1], enorm[k][2]);
                            gp_Vec nvec = surflocation.Transformation().GetRotation().Multiply(vec);
                            nvec.Coord(enorm[k][0], enorm[k][1], enorm[k][2]);
                        }
                        if (DOT3(enorm[k], enormal) > 0.8)
                            break;
                    }
                }
                else {
                    exam52_surfnormal(geomsurf, uvo[k][0], uvo[k][1], enorm[k], &flag);
                    if (flag == 1) {
                        ++normflag;
                        gp_Vec vec = gp_Vec(enorm[k][0], enorm[k][1], enorm[k][2]);
                        gp_Vec nvec = surflocation.Transformation().GetRotation().Multiply(vec);
                        nvec.Coord(enorm[k][0], enorm[k][1], enorm[k][2]);
                    }
                }
            }
            vis_ConnectSetElemNormdv(connect, numel, 1, enorm);

            /* mark edges */
            Vint jx[2], key;
            for (k = 0; k < 3; k++) {
                jx[0] = ix[k] > ix[(k + 1) % 3] ? ix[(k + 1) % 3] : ix[k];
                jx[1] = ix[k] > ix[(k + 1) % 3] ? ix[k] : ix[(k + 1) % 3];
                vsy_IntVHashLookup(ivhfreeedges, jx, &key);
                ++key;
                vsy_IntVHashInsert(ivhfreeedges, jx, key);
            }

            /* sort nodes min to max and store unique connectivity */
            if (firstflagged == 0) {
                exam52occ_sort3(ix);
                vsy_IntVHashInsert(ivhall, ix, numel);
                firstflagged = 1;
            }
        }

        Vint en = 1;
        TopExp_Explorer ExpW;
        for (ExpW.Init(face, TopAbs_EDGE); ExpW.More(); ExpW.Next()) {
            ++en;

            TopoDS_Shape shapw = ExpW.Current();
            TopoDS_Edge edge = TopoDS::Edge(shapw);

            TopLoc_Location edgelocation;
            Standard_Real cfirst, clast;
            Handle(Geom_Curve) geomcurve = BRep_Tool::Curve(edge, edgelocation, cfirst, clast);
            if (geomcurve.IsNull())
                continue;

            TopLoc_Location tlocation;
            Handle(Poly_PolygonOnTriangulation) edgeMesh;
            edgeMesh = BRep_Tool::PolygonOnTriangulation(edge, triangulation, tlocation);

            if (edgeMesh.IsNull())
                continue;

            Handle(TColStd_HArray1OfReal) unodes = edgeMesh->Parameters();
            const TColStd_Array1OfInteger& enodes = edgeMesh->Nodes();
            Vint* indices = (Vint*)malloc(edgeMesh->NbNodes() * sizeof(Vint));
            Vdouble* tang = (Vdouble*)malloc(3 * edgeMesh->NbNodes() * sizeof(Vdouble));
            Vint flag;
            for (k = 1; k <= edgeMesh->NbNodes(); ++k) {
                indices[k - 1] = nodemap[enodes(k) - 1];

                /* get tangent */
                const Standard_Real u = unodes->Value(k);
                exam52_edgetang(geomcurve, u, tang + 3 * (k - 1), &flag);
                if (flag == 1) {
                    gp_Vec vec = gp_Vec(tang[3 * (k - 1)], tang[3 * (k - 1) + 1], tang[3 * (k - 1) + 2]);
                    gp_Vec nvec = edgelocation.Transformation().GetRotation().Multiply(vec);
                    nvec.Coord(tang[3 * (k - 1)], tang[3 * (k - 1) + 1], tang[3 * (k - 1) + 2]);
                }
                else {
                    tang[3 * (k - 1)] = 2.;
                }
            }
            for (k = 1; k <= edgeMesh->NbNodes() - 1; ++k) {
                Vdouble* atang;
                Vint jx[2];

                ix[0] = indices[k - 1];
                ix[1] = indices[k];

                /* update free edges */
                jx[0] = ix[0] > ix[1] ? ix[1] : ix[0];
                jx[1] = ix[0] > ix[1] ? ix[0] : ix[1];
                vsy_IntVHashInsert(ivhfreeedges, jx, -1);

                if (k == 1) {
                    vsy_IntVHashLookup(ivhenum, ix, &key);
                    if (key == 0 || key == -1) {
                        key = ++numedge;
                    }
                }

                vsy_IntVHashLookup(ivhenum, ix, &n);
                if (n <= 0) {
                    /* check reversed edge */
                    vsy_IntVHashLookup(ivhenum, jx, &n);
                    if (n <= 0) {
                        if (tang[3 * (k - 1)] != 2. && tang[3 * k] != 2.) {
                            vsy_IntVHashInsert(ivhenum, ix, key);
                            atang = (Vdouble*)malloc(6 * sizeof(Vdouble));
                            vut_CopyD(tang + 3 * (k - 1), atang, 6);
                            vsy_VHashTableInsert(vhtang, ix, atang);
                        }
                    }
                }
            }
            free(indices);
            free(tang);
        }
        free(nodemap);

        /* add remaining free edges */
        vsy_IntVHashInitIter(ivhfreeedges);
        while (vsy_IntVHashNextIter(ivhfreeedges, ix, &key), key) {
            if (key == 1) {
                vsy_IntVHashLookup(ivhenum, ix, &k);
                if (k == 0) {
                    Vint nx[2];

                    nx[0] = ix[1];
                    nx[1] = ix[0];
                    vsy_IntVHashLookup(ivhenum, nx, &k);
                    if (k == 0) {
                        vsy_IntVHashInsert(ivhenum, ix, -1);
                    }
                }
            }
        }
        vsy_IntVHashEnd(ivhfreeedges);
    }

    /* determine double-backed surfaces */
    Vint* ibody = (Vint*)malloc(2 * numel * sizeof(Vint));
    vut_ZeroI(ibody, 2 * numel);
    Vint nbody = 0;
    TopExp_Explorer ExpS;
    for (ExpS.Init(myShape, TopAbs_SOLID); ExpS.More(); ExpS.Next()) {
        TopoDS_Shape shapw = ExpS.Current();
        TopoDS_Solid solid = TopoDS::Solid(shapw);
        ++nbody;

        TopExp_Explorer ExpF;
        for (ExpF.Init(solid, TopAbs_FACE); ExpF.More(); ExpF.Next()) {
            TopoDS_Shape shapz = ExpF.Current();
            TopoDS_Face face = TopoDS::Face(shapz);

            TopLoc_Location location;
            Handle_Poly_Triangulation triangulation = BRep_Tool::Triangulation(face, location);
            if (triangulation.IsNull())
                continue;

            const TColgp_Array1OfPnt& nodes = triangulation->Nodes();
            const Poly_Array1OfTriangle& triangles = triangulation->Triangles();

            /* Iterate over the triangles and their nodes */
            Vint firstflagged = 0;
            for (int i = triangles.Lower(); i <= triangles.Upper(); ++i) {
                const Poly_Triangle& triangle = triangles(i);

                if (firstflagged == 0) {
                    /* extract first element index */
                    for (k = 0; k < 3; k++) {
                        const gp_Pnt& p1 = nodes(triangle(k + 1));
                        p1.Coord(x, y, z);
                        location.Transformation().Transforms(x, y, z);
                        coord[0] = x;
                        coord[1] = y;
                        coord[2] = z;
                        exam52occ_nodeid(adtree, connect, coord, 0, &nvert, &ix[k]);
                    }
                    if (ix[0] == ix[1] || ix[0] == ix[2] || ix[1] == ix[2]) {
                        continue;
                    }
                    firstflagged = 1;
                    exam52occ_sort3(ix);
                    vsy_IntVHashLookup(ivhall, ix, &key);
                }
                else {
                    /* increment element index */
                    ++key;
                }
                vis_ConnectElemAssoc(connect, VIS_MISCID, 1, &key, &rev);
                if (face.Orientation() == TopAbs_REVERSED) {
                    if (rev) {
                        ibody[2 * (key - 1)] = nbody;
                    }
                    else {
                        ibody[2 * (key - 1) + 1] = nbody;
                    }
                }
                else {
                    if (rev) {
                        ibody[2 * (key - 1) + 1] = nbody;
                    }
                    else {
                        ibody[2 * (key - 1)] = nbody;
                    }
                }
            }
        }
    }
    vsy_IntVHashEnd(ivhall);

    /* create SurfMesh */
    surfmesh = msh_SurfMeshBegin();
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    ntri = npts = 0;
    ihnode = vsy_IntHashBegin();
    for (n = 1; n <= numel; ++n) {
        vis_ConnectTopology(connect, n, &shape, &maxi, &maxj, &maxk);
        if (shape != SYS_SHAPETRI)
            continue;
        ++ntri;
        vis_ConnectElemNode(connect, n, &nix, ix);
        for (j = 0; j < 3; ++j) {
            vsy_IntHashLookup(ihnode, ix[j], &key);
            if (key == 0) {
                ++npts;
                vsy_IntHashInsert(ihnode, ix[j], npts);
            }
        }
    }

    if (npts == 0 || ntri == 0)
        goto labelcleanup;

    msh_SurfMeshDef(surfmesh, npts, ntri);
    /* load points */
    for (n = 1; n <= numnp; ++n) {
        vsy_IntHashLookup(ihnode, n, &j);
        if (j == 0)
            continue;

        vis_ConnectCoordsdv(connect, 1, &n, &xyz);
        vis_ConnectNodeAssoc(connect, VIS_GEOVERT, 1, &n, &nvert);
        if (nvert) {
            msh_SurfMeshSetPoint(surfmesh, j, xyz, 1);
            msh_SurfMeshSetPointAssoc(surfmesh, VIS_GEOVERT, j, nvert);
        }
        else {
            msh_SurfMeshSetPoint(surfmesh, j, xyz, 0);
        }
    }
    /* load triangles */
    ntri = 0;
    static Vint irev[3] = {2, 1, 0};
    for (n = 1; n <= numel; ++n) {
        Vint tefl[3] = {0, 0, 0};
        Vint teflag = 0;
        Vint tesens[3] = {0, 0, 0};
        Vint jx[3], aid, enormflag;
        Vdouble* etang[3] = {NULL, NULL, NULL};

        vis_ConnectTopology(connect, n, &shape, &maxi, &maxj, &maxk);
        if (shape != SYS_SHAPETRI)
            continue;
        ++ntri;
        vis_ConnectElemNode(connect, n, &nix, ix);
        vis_ConnectElemAssoc(connect, VIS_GEOFACE, 1, &n, &aid);
        vis_ConnectElemAssoc(connect, VIS_MISCID, 1, &n, &rev);
        /* loop over triangle edges */
        for (k = 0; k < 3; k++) {
            if (rev == 0) {
                in0 = k;
                in1 = (k + 1) % 3;
            }
            else {
                in1 = irev[k];
                in0 = (irev[k] + 1) % 3;
            }
            jx[0] = ix[in0];
            jx[1] = ix[in1];
            vsy_IntVHashLookup(ivhenum, jx, &teflag);
            if (teflag == 0) {
                jx[0] = ix[in1];
                jx[1] = ix[in0];
                vsy_IntVHashLookup(ivhenum, jx, &teflag);
                if (teflag) {
                    tesens[k] = 1;
                    tefl[k] = 1;
                }
            }
            else {
                tefl[k] = 1;
            }
            if (tefl[k]) {
                if (teflag > 0) {
                    msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOEDGE, ntri, SYS_EDGE, k + 1, teflag);
                }
                vsy_VHashTableLookup(vhtang, jx, (Vobject**)&etang[k]);
            }
        }
        if (rev == 0) {
            vsy_IntHashLookup(ihnode, ix[0], &jx[0]);
            vsy_IntHashLookup(ihnode, ix[1], &jx[1]);
            vsy_IntHashLookup(ihnode, ix[2], &jx[2]);
        }
        else {
            vsy_IntHashLookup(ihnode, ix[0], &jx[0]);
            vsy_IntHashLookup(ihnode, ix[1], &jx[2]);
            vsy_IntHashLookup(ihnode, ix[2], &jx[1]);
        }
        msh_SurfMeshSetTri(surfmesh, ntri, jx, tefl);
        if (ibody[2 * (n - 1)] > 0 && ibody[2 * (n - 1) + 1] > 0) {
            msh_SurfMeshSetTriBack(surfmesh, ntri, 1, 1);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, ntri, SYS_ELEM, 0, ibody[2 * (n - 1)]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, ntri, SYS_ELEM, -1, ibody[2 * (n - 1) + 1]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, ntri, SYS_ELEM, 0, ibody[2 * (n - 1)]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, ntri, SYS_ELEM, -1, ibody[2 * (n - 1) + 1]);
        }
        else if (ibody[2 * (n - 1)] > 0) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOBODY, ntri, SYS_ELEM, 0, ibody[2 * (n - 1)]);
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_PROPID, ntri, SYS_ELEM, 0, ibody[2 * (n - 1)]);
        }
        /* set triangle node normals */
        vis_ConnectIsElemNorm(connect, n, 1, &enormflag);
        if (enormflag) {
            vis_ConnectElemNormdv(connect, n, 1, enorm);
            if (rev) {
                Vdouble tmp[3];
                tmp[0] = enorm[1][0];
                tmp[1] = enorm[1][1];
                tmp[2] = enorm[1][2];
                enorm[1][0] = enorm[2][0];
                enorm[1][1] = enorm[2][1];
                enorm[1][2] = enorm[2][2];
                enorm[2][0] = tmp[0];
                enorm[2][1] = tmp[1];
                enorm[2][2] = tmp[2];
                for (k = 0; k < 3; k++) {
                    enorm[0][k] = -enorm[0][k];
                    enorm[1][k] = -enorm[1][k];
                    enorm[2][k] = -enorm[2][k];
                }
            }
            msh_SurfMeshSetTriNorm(surfmesh, ntri, enorm);
        }
        msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOFACE, ntri, SYS_FACE, 0, aid);
        /* set triangle edge tangents */
        for (k = 0; k < 3; k++) {
            if (tefl[k] == 0 || etang[k] == NULL)
                continue;
            if (tesens[k] == 0) {
                msh_SurfMeshSetTriTang(surfmesh, ntri, k + 1, (Vdouble(*)[3])etang[k]);
            }
            else {
                Vdouble copy[6];
                for (int i = 0; i < 3; ++i) {
                    copy[i] = -etang[k][i + 3];
                    copy[i + 3] = -etang[k][i];
                }
                msh_SurfMeshSetTriTang(surfmesh, ntri, k + 1, (Vdouble(*)[3])copy);
            }
        }
    }
    /* end objects */
labelcleanup:
    free(ibody);
    vsy_VHashTableForEach(vhtang, (Vfunc1*)free);
    vsy_VHashTableEnd(vhtang);
    vsy_IntHashEnd(ihnode);
    vsy_IntHashEnd(ihedge);
    vsy_IntVHashEnd(ivhenum);

    vis_ConnectEnd(connect);
    vsy_ADTreeEnd(adtree);
    printf("Geometry read complete\n");
    printf(" Number of geometry edges=  %d\n", numedge);
    printf(" Number of geometry faces=  %d\n", numface);
    printf(" Number of geometry bodies= %d\n", nbody);
    printf(" Number of tesselation points= %d\n", npts);
    printf(" Number of tesselation tris=   %d\n", ntri);
    if (npts == 0 || ntri == 0) {
        msh_SurfMeshEnd(surfmesh);
        return 1;
    }

    if (iquad) {
        msh_SurfMeshSetParami(surfmesh, VIS_MESH_SHAPE, VIS_SHAPEQUAD);
    }
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, edgelength);
    printf("edgelength= %e\n", edgelength);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, 1);
    /* optional write of SurfMesh contents for QA */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, srfname);
    connectsrf = vis_ConnectBegin();
    msh_SurfMeshGenerate(surfmesh, connectsrf);
    ierr = msh_SurfMeshError(surfmesh);
    if (ierr) {
        printf("Error generating surface mesh\n");
    }
    else {
        Vint numnpsrf, numelsrf;
        printf("SurfMesh complete\n");
        vis_ConnectNumber(connectsrf, SYS_NODE, &numnpsrf);
        vis_ConnectNumber(connectsrf, SYS_ELEM, &numelsrf);
        printf(" Surface number of nodes= %d\n", numnpsrf);
        printf(" Surface number of elems= %d\n", numelsrf);
        /* optional write of SurfMesh mesh
vis_ConnectWrite (connectsrf,SYS_NASTRAN_BULKDATA,"exam52occ-srf.bdf");
        */
        /* perform tet mesh */
        if (nbody)
            itet = 1;
        if (itet) {
            connecttet = vis_ConnectBegin();
            tetmesh = msh_TetMeshBegin();
            msh_TetMeshConnect(tetmesh, connectsrf);
            msh_TetMeshSetParamd(tetmesh, VIS_MESH_EDGELENGTH, edgelength);
            /* optional write of TetMesh contents for QA
  msh_TetMeshWrite (tetmesh,SYS_ASCII,"exam52occ.tet");
            */
            msh_TetMeshGenerate(tetmesh, connecttet);
            if (ierr) {
                printf("Error generating tet mesh\n");
            }
            else {
                Vint numnptet, numeltet;
                printf("TetMesh complete\n");
                vis_ConnectNumber(connecttet, SYS_NODE, &numnptet);
                vis_ConnectNumber(connecttet, SYS_ELEM, &numeltet);
                printf(" Tet mesh number of nodes= %d\n", numnptet);
                printf(" Tet mesh number of elems= %d\n", numeltet);
                /* optional write of TetMesh mesh
         vis_ConnectWrite (connecttet,SYS_NASTRAN_BULKDATA,
                           "exam52occ-tet.bdf");
                */
            }
            vis_ConnectEnd(connecttet);
            msh_TetMeshEnd(tetmesh);
        }
    }
    msh_SurfMeshEnd(surfmesh);
    vis_ConnectEnd(connectsrf);

    return 0;
}

6.45. Example 60, Generate a 3D Surface Mesh with User Nodes

This example illustrates the process of defining unconnected user nodes which are meant to appear in the final surface mesh. The geometry is a flat plate with a circular hole. In order to achieve a good approximation to the circular hole geometry, triangle edge vertex tangents are defined using msh_SurfMeshSetTriTang(). A total of five unconnected points are defined to be inserted into the final mesh. Four of the points are on the perimeter of the circular hole, the fifth point is inserted on the plate along the global x-axis. The function msh_SurfMeshSetTriHint() is used to specify the topological entity that the unnconnected points are to be projected to. Points which are given a SYS_EDGE hint will be constrained to be projected to a preserved edge connected to a specified preserved edge hint. The search is bounded by preserved nodes. Likewise points which are given a SYS_FACE hint will be constrained to be projected to a triangle face connected to the specified triangle face hint. The search is bounded by preserved edges. Point associations are assigned to the unconnected points so that they can be identified in the final mesh.

The edges around the circular hole are given a special preserved edge flag of 2 which will purge the edge of any points which are not preserved. Note that interior midside nodes of some of the generated triangles connected to the circular hole have been adjusted to avoid local element inversions at or near the edge of the hole.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble xc[21][3] = {{5.000e-01, 0.000e+00, 0.000e+00},
                            {4.330e-01, 2.500e-01, 0.000e+00},
                            {2.500e-01, 4.330e-01, 0.000e+00},
                            {0.000e+00, 5.000e-01, 0.000e+00},
                            {-2.500e-01, 4.330e-01, 0.000e+00},
                            {-4.330e-01, 2.500e-01, 0.000e+00},
                            {-5.000e-01, 0.000e+00, 0.000e+00},
                            {-4.330e-01, -2.500e-01, 0.000e+00},
                            {-2.500e-01, -4.330e-01, 0.000e+00},
                            {0.000e+00, -5.000e-01, 0.000e+00},
                            {2.500e-01, -4.330e-01, 0.000e+00},
                            {4.330e-01, -2.500e-01, 0.000e+00},
                            {1., 1., 0.},
                            {-1., 1., 0.},
                            {-1., -1., 0.},
                            {1., -1., 0.},
                            {3.536e-01, 3.536e-01, 0.000e+00},
                            {-3.536e-01, 3.536e-01, 0.000e+00},
                            {-3.536e-01, -3.536e-01, 0.000e+00},
                            {3.536e-01, -3.536e-01, 0.000e+00},
                            {7.500e-01, 0.000e+00, 0.000e+00}};

static Vint pc[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1};

static Vdouble tng[12][3] = {{0.000, -1.000, 0.000}, {0.500, -0.866, 0.000}, {0.866, -0.500, 0.000},  {1.000, 0.000, 0.000},
                             {0.866, 0.500, 0.000},  {0.500, 0.866, 0.000},  {0.000, 1.000, 0.000},   {-0.500, 0.866, 0.000},
                             {-0.866, 0.500, 0.000}, {-1.000, 0.000, 0.000}, {-0.866, -0.500, 0.000}, {-0.500, -0.866, 0.000}};

static Vdouble tvn[3][3] = {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}};

static Vint tix[16][3] = {{1, 13, 2},  {2, 13, 3},  {3, 13, 4},  {4, 14, 5},   {5, 14, 6},   {6, 14, 7},
                          {7, 15, 8},  {8, 15, 9},  {9, 15, 10}, {10, 16, 11}, {11, 16, 12}, {12, 16, 1},
                          {1, 16, 13}, {4, 13, 14}, {7, 14, 15}, {10, 15, 16}};

static Vint efl[16][3] = {{0, 0, 2}, {0, 0, 2}, {0, 0, 2}, {0, 0, 2}, {0, 0, 2}, {0, 0, 2}, {0, 0, 2}, {0, 0, 2},
                          {0, 0, 2}, {0, 0, 2}, {0, 0, 2}, {0, 0, 2}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

/*----------------------------------------------------------------------
                      Generate a 3D Surface Mesh with User Nodes
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    vis_Connect* connect;

    Vint i, k;
    Vint numtris, numpnts;
    Vint numnp, numel;
    Vdouble x[3];
    Vdouble sn[2][3];
    Vint aid, pid;
    Vint nix, ix[6];
    Vint i1, i2;
    Vint numnegjac, qualflag;

    numpnts = 21;
    numtris = 16;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numpnts, numtris);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define points */
    for (i = 0; i < numpnts; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], pc[i]);
    }

    /* define triangle connectivity */
    for (i = 0; i < numtris; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tix[i], efl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, tvn);
    }
    /* define triangle side tangents for
       triangle edges in circle */
    for (i = 0; i < 12; i++) {
        i1 = tix[i][2];
        i2 = tix[i][0];
        for (k = 0; k < 3; k++) {
            sn[0][k] = tng[i1 - 1][k];
            sn[1][k] = tng[i2 - 1][k];
        }
        msh_SurfMeshSetTriTang(surfmesh, i + 1, 3, sn);
    }
    /* set unconnected point hints for points 17-20 */
    msh_SurfMeshSetTriHint(surfmesh, 2, SYS_EDGE, 3, 17);
    msh_SurfMeshSetTriHint(surfmesh, 5, SYS_EDGE, 3, 18);
    msh_SurfMeshSetTriHint(surfmesh, 8, SYS_EDGE, 3, 19);
    msh_SurfMeshSetTriHint(surfmesh, 11, SYS_EDGE, 3, 20);

    /* set unconnected point hint for point 21 */
    msh_SurfMeshSetTriHint(surfmesh, 12, SYS_FACE, 0, 21);

    /* set point associations */
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 17, 1017);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 18, 1018);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 19, 1019);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 20, 1020);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 21, 1021);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 60.);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .04);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .4);

    /* generate */
    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam60.srf");
    msh_SurfMeshGenerate(surfmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    /* query for negative jacobians */
    msh_SurfMeshGetInteger(surfmesh, SURFMESH_NUMNEGJAC, &numnegjac);
    if (numnegjac) {
        printf(" Number of negative jacobians= %d\n", numnegjac);
        for (i = 1; i <= numel; i++) {
            vis_ConnectElemAssoc(connect, VIS_QUALFLAG, 1, &i, &qualflag);
            if (qualflag != 0) {
                printf(" element= %d\n", i);
            }
        }
    }
    /* print node information */
    printf("Node information\n");
    for (i = 1; i <= numnp; i++) {
        vis_ConnectCoordsdv(connect, 1, &i, (Vdouble(*)[3])x);
        vis_ConnectNodeAssoc(connect, VIS_MISCID, 1, &i, &aid);
        printf("id= %d  x= %f, y= %f, z= %f,  aid= %d\n", i, x[0], x[1], x[2], aid);
    }
    /* print element information */
    printf("Element information\n");
    for (i = 1; i <= numel; i++) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        vis_ConnectElemAssoc(connect, VIS_PROPID, 1, &i, &pid);
        printf("id= %d  ix= %d %d %d %d %d %d, pid= %d\n", i, ix[0], ix[1], ix[2], ix[3], ix[4], ix[5], pid);
    }
    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam60.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}

6.46. Example 60a, Refine a 3D Surface Mesh

This example is an extension of Example 60 to illustrate refinement of a surface mesh. The original mesh is generated with an edge length of .3. The refined mesh sets the edge length to .1 for the elements with any generated node in the upper right corner of the original mesh. A State object is used to set new sizes for each element. Any element size which is not set in the State is assumed to be unchanged.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble xc[21][3] = {{5.000e-01, 0.000e+00, 0.000e+00},
                            {4.330e-01, 2.500e-01, 0.000e+00},
                            {2.500e-01, 4.330e-01, 0.000e+00},
                            {0.000e+00, 5.000e-01, 0.000e+00},
                            {-2.500e-01, 4.330e-01, 0.000e+00},
                            {-4.330e-01, 2.500e-01, 0.000e+00},
                            {-5.000e-01, 0.000e+00, 0.000e+00},
                            {-4.330e-01, -2.500e-01, 0.000e+00},
                            {-2.500e-01, -4.330e-01, 0.000e+00},
                            {0.000e+00, -5.000e-01, 0.000e+00},
                            {2.500e-01, -4.330e-01, 0.000e+00},
                            {4.330e-01, -2.500e-01, 0.000e+00},
                            {1., 1., 0.},
                            {-1., 1., 0.},
                            {-1., -1., 0.},
                            {1., -1., 0.},
                            {3.536e-01, 3.536e-01, 0.000e+00},
                            {-3.536e-01, 3.536e-01, 0.000e+00},
                            {-3.536e-01, -3.536e-01, 0.000e+00},
                            {3.536e-01, -3.536e-01, 0.000e+00},
                            {7.500e-01, 0.000e+00, 0.000e+00}};

static Vint pc[21] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1};

static Vdouble tng[12][3] = {{0.000, -1.000, 0.000}, {0.500, -0.866, 0.000}, {0.866, -0.500, 0.000},  {1.000, 0.000, 0.000},
                             {0.866, 0.500, 0.000},  {0.500, 0.866, 0.000},  {0.000, 1.000, 0.000},   {-0.500, 0.866, 0.000},
                             {-0.866, 0.500, 0.000}, {-1.000, 0.000, 0.000}, {-0.866, -0.500, 0.000}, {-0.500, -0.866, 0.000}};

static Vdouble tvn[3][3] = {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}};

static Vint tix[16][3] = {{1, 13, 2},  {2, 13, 3},  {3, 13, 4},  {4, 14, 5},   {5, 14, 6},   {6, 14, 7},
                          {7, 15, 8},  {8, 15, 9},  {9, 15, 10}, {10, 16, 11}, {11, 16, 12}, {12, 16, 1},
                          {1, 16, 13}, {4, 13, 14}, {7, 14, 15}, {10, 15, 16}};

static Vint efl[16][3] = {{0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1},
                          {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

/*----------------------------------------------------------------------
                      Refine a 3D Surface Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    vis_Connect* connect;
    vis_State* state;

    Vint i, k;
    Vint numtris, numpnts;
    Vint numnp, numel;
    Vdouble x[3];
    Vdouble sn[2][3];
    Vdouble v;
    Vint nix, *ix = NULL;
    Vint i1, i2;
    Vint maxElementNodes = 0;

    numpnts = 21;
    numtris = 16;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, numpnts, numtris);

    /* create Connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define points */
    for (i = 0; i < numpnts; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], pc[i]);
    }

    /* define triangle connectivity */
    for (i = 0; i < numtris; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tix[i], efl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, tvn);
    }
    /* define triangle side tangents for
       triangle edges in circle */
    for (i = 0; i < 12; i++) {
        i1 = tix[i][2];
        i2 = tix[i][0];
        for (k = 0; k < 3; k++) {
            sn[0][k] = tng[i1 - 1][k];
            sn[1][k] = tng[i2 - 1][k];
        }
        msh_SurfMeshSetTriTang(surfmesh, i + 1, 3, sn);
    }
    /* set unconnected point hints for points 17-20 */
    msh_SurfMeshSetTriHint(surfmesh, 2, SYS_EDGE, 3, 17);
    msh_SurfMeshSetTriHint(surfmesh, 5, SYS_EDGE, 3, 18);
    msh_SurfMeshSetTriHint(surfmesh, 8, SYS_EDGE, 3, 19);
    msh_SurfMeshSetTriHint(surfmesh, 11, SYS_EDGE, 3, 20);

    /* set unconnected point hint for point 21 */
    msh_SurfMeshSetTriHint(surfmesh, 12, SYS_FACE, 0, 21);

    /* set point associations */
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 17, 1017);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 18, 1018);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 19, 1019);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 20, 1020);
    msh_SurfMeshSetPointAssoc(surfmesh, VIS_MISCID, 21, 1021);

    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, 3);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_SPANANGLE, 45.);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_MINEDGELENGTH, .04);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, .3);

    msh_SurfMeshSetParami(surfmesh, VIS_MESH_SHAPE, SYS_SHAPEQUAD);

    /* generate */
    msh_SurfMeshGenerate(surfmesh, connect);
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("Original mesh complete\n");
    printf("numnp= %d, numel= %d\n", numnp, numel);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam60a-orig.bdf");

    state = vis_StateBegin();
    vis_StateDef(state, numel, SYS_ELEM, SYS_NONE, VIS_SCALAR);
    /* refine elements in upper right corner */
    maxElementNodes = 0;
    vis_ConnectMaxElemNode(connect, &maxElementNodes);
    ix = (Vint*)vut_MemoryMalloc(maxElementNodes * sizeof(Vint));
    for (i = 1; i <= numel; ++i) {
        vis_ConnectElemNode(connect, i, &nix, ix);
        vis_ConnectCoordsdv(connect, 1, &ix[0], (Vdouble(*)[3])x);
        if (x[0] >= .6 && x[1] >= .6) {
            v = .1;
            vis_StateSetDatadv(state, i, &v);
        }
    }
    vut_MemoryFree(ix);
    /* clear Connect object */
    vis_ConnectDef(connect, 0, 0);
    msh_SurfMeshRefine(surfmesh, state, connect);
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("Refined mesh complete\n");
    printf("numnp= %d, numel= %d\n", numnp, numel);
    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam60a-ref.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    vis_StateEnd(state);
    msh_SurfMeshEnd(surfmesh);
    return 0;
}

6.47. Example 61, Generate a Mixed Volumetric and Boundary Mesh

This example illustrates using VolMesh to generate a mixed volumetric and boundary mesh from a set of input boundary triangles. The input triangulation is a simple unit cube with an interior diagonal face which is defined by double-backed triangles. The interior face divides the cube into two regions, the first of which is to be filled with tetrahedra, the second is meant to be unfilled. The resulting mesh will consist of the generated tetrahedra for the first region followed by the triangles which bound the second region. The function msh_VolMeshSetFaceFill() is used to define the fill type for a face. All faces which bound a region to be volumetrically filled must have a volumetric fill type specified.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                               {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint tris[16][3] = {{1, 5, 3}, {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6}, {7, 4, 3}, {8, 4, 7},
                           {1, 4, 2}, {4, 1, 3}, {5, 8, 7}, {5, 6, 8}, {5, 2, 4}, {5, 4, 7}, {5, 4, 2}, {5, 7, 4}};
/* fill regions */
static Vint rtris[16] = {1, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 2, 1, 1, 2, 2};

/*----------------------------------------------------------------------
                      Generate a 3D Volume Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_VolMesh* volmesh;
    vis_Connect* connect;

    Vint i;
    Vint numnp, numel;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create volmesh object */
    volmesh = msh_VolMeshBegin();
    msh_VolMeshDef(volmesh, 8, 16);

    /* create connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < 8; i++) {
        msh_VolMeshSetNode(volmesh, i + 1, coords[i]);
    }
    /* define tris */
    for (i = 0; i < 16; i++) {
        msh_VolMeshSetFace(volmesh, i + 1, VIS_SHAPETRI, 2, 0, tris[i]);
        /* define region fill types */
        if (rtris[i] == 1) {
            msh_VolMeshSetFaceFill(volmesh, i + 1, VOLMESH_FILL_TET);
        }
        else if (rtris[i] == 2) {
            msh_VolMeshSetFaceFill(volmesh, i + 1, VOLMESH_FILL_BOUNDARY);
        }
    }
    /* define a node association on node 2 */
    msh_VolMeshSetNodeAssoc(volmesh, VIS_GEOVERT, 2, 1000);
    /* define an element edge association on tri 4, edge 2 */
    msh_VolMeshSetFaceAssoc(volmesh, VIS_GEOEDGE, 4, SYS_EDGE, 2, 10);
    /* define an element face association on tri 3 */
    msh_VolMeshSetFaceAssoc(volmesh, VIS_GEOFACE, 3, SYS_FACE, 1, 100);
    /* define an element association on tri 3 */
    msh_VolMeshSetFaceAssoc(volmesh, VIS_GEOBODY, 3, SYS_ELEM, 0, 200);

    /* generate */
    msh_VolMeshGenerate(volmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam61.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_VolMeshEnd(volmesh);
    return 0;
}

6.48. Example 61a, Generate a Mixed Shape 3D Volume Mesh

This example is a modification of Example 61 in which one of the boundary faces of the reqion to be tetrahedralized is now a quadrilateral. The tetrahedralized region will contain a pyramid to conformally transition from the quadrilateral face to the interior tetrahedra. In this simple case, a single tetrahedron and pyramid are generated. This example also illustrates installing a monitor function.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[8][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.},
                               {0., 0., 1.}, {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}};

static Vint faces[15][4] = {{1, 5, 3},    {3, 5, 7}, {4, 6, 2}, {8, 6, 4}, {1, 2, 5}, {5, 2, 6}, {7, 4, 3}, {8, 4, 7},
                            {1, 3, 4, 2}, {5, 8, 7}, {5, 6, 8}, {5, 2, 4}, {5, 4, 7}, {5, 4, 2}, {5, 7, 4}};

static Vint shapefaces[15] = {SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPETRI,  SYS_SHAPETRI,
                              SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPEQUAD, SYS_SHAPETRI,
                              SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPETRI,  SYS_SHAPETRI};

static Vint rfaces[15] = {1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2, 2};

/*----------------------------------------------------------------------
                      monitor function
----------------------------------------------------------------------*/
static void
monitor(msh_VolMesh* volmesh, Vobject* usrobj)
{
    int iparam[4];

    printf("monitor called\n");
    /* query and print progress */
    msh_VolMeshGetInteger(volmesh, VIS_MESH_PROGRESS, iparam);
    printf("phase=   %d\n", iparam[0]);
    printf("numnp=   %d\n", iparam[1]);
    printf("numel=   %d\n", iparam[2]);
    printf("percent= %d\n", iparam[3]);
}

/*----------------------------------------------------------------------
                      Generate a Mixed Shape 3D Volume Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_VolMesh* volmesh;
    vis_Connect* connect;

    Vint i;
    Vint numnp, numel;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create volmesh object */
    volmesh = msh_VolMeshBegin();
    msh_VolMeshDef(volmesh, 8, 15);

    /* create connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < 8; i++) {
        msh_VolMeshSetNode(volmesh, i + 1, coords[i]);
    }
    /* define faces */
    for (i = 0; i < 15; i++) {
        msh_VolMeshSetFace(volmesh, i + 1, shapefaces[i], 2, 0, faces[i]);
        if (rfaces[i] == 1) {
            msh_VolMeshSetFaceFill(volmesh, i + 1, VOLMESH_FILL_TET);
        }
        else if (rfaces[i] == 2) {
            msh_VolMeshSetFaceFill(volmesh, i + 1, VOLMESH_FILL_BOUNDARY);
        }
    }
    /* define a node association on node 2 */
    msh_VolMeshSetNodeAssoc(volmesh, VIS_GEOVERT, 2, 1000);
    /* define an element edge association on tri 4, edge 2 */
    msh_VolMeshSetFaceAssoc(volmesh, VIS_GEOEDGE, 4, SYS_EDGE, 2, 10);
    /* define an element face association on tri 3 */
    msh_VolMeshSetFaceAssoc(volmesh, VIS_GEOFACE, 3, SYS_FACE, 1, 100);
    /* define an element association on tri 3 */
    msh_VolMeshSetFaceAssoc(volmesh, VIS_GEOBODY, 3, SYS_ELEM, 0, 200);
    msh_VolMeshSetFaceAssoc(volmesh, VIS_PROPID, 3, SYS_ELEM, 0, 1);

    msh_VolMeshWrite(volmesh, SYS_ASCII, "exam61a.vol");

    /* set monitor function */
    msh_VolMeshSetFunction(volmesh, VOLMESH_FUN_MONITOR, (Vfunc*)monitor, NULL);
    /* generate */
    msh_VolMeshGenerate(volmesh, connect);

    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam61a.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_VolMeshEnd(volmesh);
    return 0;
}

6.49. Example 61b, Generate a Mixed Order 3D Volume Mesh

This example tetrahedralizes a unit cube. The input faces are mixed shape (triangles and a quadrilateral) and mixed order (two of the triangles share a parabolic edge). The interior is to be filled with parabolic tetrahedra. Pyramids are generated adjacent to input quadrilaterals and all interior edges will have midside nodes placed. The boundary nodes and face topology are unchanged in the final mesh. All elements will therefore be of mixed order. The maxi parameter will be 3 ie parabolic and the maxj parameter will reflect the missing midside nodes on the respective edge. The default fill region is VOLMESH_FILL_TET.

The macro MAXJ_SETMISSINGMIDSIDE is used to set missing midside node flags in the input maxj topology parameter.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"

static Vdouble coords[9][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.}, {0., 0., 1.},
                               {1., 0., 1.}, {0., 1., 1.}, {1., 1., 1.}, {0., .5, 1.}};

static Vint faces[11][4] = {{1, 5, 3}, {3, 5, 7, 9}, {4, 6, 2},    {8, 6, 4},    {1, 2, 5}, {5, 2, 6},
                            {7, 4, 3}, {8, 4, 7},    {1, 3, 4, 2}, {5, 8, 7, 9}, {5, 6, 8}};

static Vint shapefaces[11] = {SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPETRI,  SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPETRI,
                              SYS_SHAPETRI, SYS_SHAPETRI, SYS_SHAPEQUAD, SYS_SHAPETRI, SYS_SHAPETRI};

#define MAXJ_SETMISSINGMIDSIDE(a, b) (a) += (1 << (16 + (b)))

/*----------------------------------------------------------------------
                      Generate a Mixed Order 3D Volume Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_VolMesh* volmesh;
    vis_Connect* connect;

    Vint i;
    Vint maxi, maxj;
    Vint numnp, numel;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create volmesh object */
    volmesh = msh_VolMeshBegin();
    msh_VolMeshDef(volmesh, 9, 11);

    /* create connect object */
    connect = vis_ConnectBegin();
    vis_ConnectPre(connect, SYS_DOUBLE);
    vis_ConnectDef(connect, 0, 0);

    /* define nodes */
    for (i = 0; i < 9; i++) {
        msh_VolMeshSetNode(volmesh, i + 1, coords[i]);
    }
    /* define faces */
    for (i = 0; i < 11; i++) {
        maxi = 2;
        maxj = 0;
        /* mixed order triangles */
        /* maxi is 3 and add bits to maxj for missing midside */
        if (i + 1 == 2) {
            maxi = 3;
            MAXJ_SETMISSINGMIDSIDE(maxj, 0);
            MAXJ_SETMISSINGMIDSIDE(maxj, 2);
        }
        if (i + 1 == 10) {
            maxi = 3;
            MAXJ_SETMISSINGMIDSIDE(maxj, 0);
            MAXJ_SETMISSINGMIDSIDE(maxj, 1);
        }
        msh_VolMeshSetFace(volmesh, i + 1, shapefaces[i], maxi, maxj, faces[i]);
    }
    /* set target edge length */
    msh_VolMeshSetParamd(volmesh, VIS_MESH_EDGELENGTH, .1);
    /* generate parabolic tets/pyramids in the interior */
    msh_VolMeshSetParami(volmesh, VIS_MESH_MAXI, 3);

    msh_VolMeshWrite(volmesh, SYS_ASCII, "exam61b.vol");
    /* generate */
    msh_VolMeshGenerate(volmesh, connect);
    /* print generated nodes and elements */
    vis_ConnectNumber(connect, SYS_NODE, &numnp);
    vis_ConnectNumber(connect, SYS_ELEM, &numel);
    printf("numnp= %d, numel= %d\n", numnp, numel);

    vis_ConnectWrite(connect, SYS_NASTRAN_BULKDATA, "exam61b.bdf");

    /* end objects */
    vis_ConnectEnd(connect);
    msh_VolMeshEnd(volmesh);
    return 0;
}

6.50. Example 62, Generate a Quad Surface Mesh and Tet Volume Mesh

This example illustrates generating a quadrilateral mesh of linear elements on the surfaces of a non-manifold solid and subsequently filling the interior with parabolic tetrahedra. Pyramids are generated adjacent to input quadrilaterals and all interior edges will have midside nodes placed. The boundary nodes and face topology are unchanged in the final mesh. The default fill region is VOLMESH_FILL_TET.

#include <stdio.h>
#include "sam/base/base.h"
#include "sam/msh/msh.h"
#include "sam/base/license.h"
#include "sam/CEETRON_SAM_license.h"
/*
                 3----3-----4
                /.         /|
               / 4        / 2
             12  .      11  |
             /   1....1./...2
            7----7-----8   /
           /|  9      /|  10
          / 8 .      / 6 /
         /  |.      /  |/
        /   5....5./...6
      11---------12   /
       |  .      |   /
       | .       |  /
       |.        | /
       9---------10

           node numbers 1 through 12 shown on corners
           edge numbers 1 through 12 shown on edge midsides
           edges 13 through 18 not shown
           faces are 1(-z), 2(+z) 3(-y) 4(+y) 5(-x) 6(+x)
           faces 7 through 12 not shown

           y
           |
           +-- x
          /
         z
*/

/* a 1 by 1 by 2 cube */
static Vdouble xc[12][3] = {{0., 0., 0.}, {1., 0., 0.}, {0., 1., 0.}, {1., 1., 0.}, {0., 0., 1.}, {1., 0., 1.},
                            {0., 1., 1.}, {1., 1., 1.}, {0., 0., 2.}, {1., 0., 2.}, {0., 1., 2.}, {1., 1., 2.}};

/* triangle connectivity */
static Vint tris[22][3] = {{1, 5, 3},  {3, 5, 7},   {4, 6, 2},   {8, 6, 4},   {1, 2, 5}, {5, 2, 6},
                           {7, 4, 3},  {8, 4, 7},   {1, 4, 2},   {4, 1, 3},   {5, 8, 7}, {5, 6, 8},

                           {5, 9, 7},  {7, 9, 11},  {8, 10, 6},  {12, 10, 8}, {5, 6, 9}, {9, 6, 10},
                           {11, 8, 7}, {12, 8, 11}, {9, 12, 11}, {9, 10, 12}};

/* triangle preserved edge flags */
static Vint tefl[22][3] = {{1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 1}, {0, 1, 1},
                           {0, 1, 1}, {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {0, 1, 1}, {1, 1, 0},

                           {1, 0, 1}, {0, 1, 1}, {0, 1, 1}, {1, 0, 1}, {1, 0, 1}, {0, 1, 1},
                           {0, 1, 1}, {1, 0, 1}, {0, 1, 1}, {1, 1, 0}};
/* edge associations */
static Vint ted[22][3] = {{9, 0, 4},  {0, 8, 12}, {0, 10, 2}, {1, 0, 11}, {1, 0, 9}, {0, 10, 5},
                          {0, 3, 12}, {11, 0, 7}, {0, 2, 1},  {0, 4, 3},  {0, 7, 8}, {5, 6, 0},

                          {9, 0, 4},  {0, 8, 12}, {0, 10, 2}, {1, 0, 11}, {1, 0, 9}, {0, 10, 5},
                          {0, 3, 12}, {11, 0, 7}, {0, 7, 8},  {5, 6, 0}};
/* face associations */
static Vint tfa[22] = {5, 5, 6, 6, 3, 3, 4, 4, 1, 1, 2, 2, 11, 11, 9, 9, 10, 10, 7, 7, 8, 8};

/* triangle normals */
/* triangles 13-24, same */
static Vdouble norms[22][3][3] = {{{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},

                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},

                                  {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}}, {{0., 0., -1.}, {0., 0., -1.}, {0., 0., -1.}},
                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},

                                  {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}}, {{-1., 0., 0.}, {-1., 0., 0.}, {-1., 0., 0.}},
                                  {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},    {{1., 0., 0.}, {1., 0., 0.}, {1., 0., 0.}},

                                  {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}}, {{0., -1., 0.}, {0., -1., 0.}, {0., -1., 0.}},
                                  {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},    {{0., 1., 0.}, {0., 1., 0.}, {0., 1., 0.}},

                                  {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}},    {{0., 0., 1.}, {0., 0., 1.}, {0., 0., 1.}}};

/*----------------------------------------------------------------------
                      Generate a Quad Surface Mesh and Tet Volume Mesh
----------------------------------------------------------------------*/
int
main()
{
    msh_SurfMesh* surfmesh;
    msh_VolMesh* volmesh;
    vis_Connect *connectsurf, *connectvol;

    Vint i, j;
    Vint shape, maxi;
    Vint numsurfnode, numsurfface;
    Vint numnp, numel;
    Vint aid;
    Vdouble edgelen;

    vsy_LicenseValidate(CEETRON_SAM_LICENSE);

    /* create SurfMesh object */
    surfmesh = msh_SurfMeshBegin();
    msh_SurfMeshDef(surfmesh, 12, 22);

    /* create Connect object */
    connectsurf = vis_ConnectBegin();
    vis_ConnectPre(connectsurf, SYS_DOUBLE);
    vis_ConnectDef(connectsurf, 0, 0);

    /* generate linear quad elements on surface */
    maxi = 2;
    shape = VIS_SHAPEQUAD;
    edgelen = .3;
    /* define points */
    for (i = 0; i < 12; i++) {
        msh_SurfMeshSetPoint(surfmesh, i + 1, xc[i], 1);
        /* set VIS_GEOVERT association to the point id */
        msh_SurfMeshSetPointAssoc(surfmesh, VIS_GEOVERT, i + 1, i + 1);
    }
    /* define triangle connectivity */
    for (i = 0; i < 22; i++) {
        msh_SurfMeshSetTri(surfmesh, i + 1, tris[i], tefl[i]);
        msh_SurfMeshSetTriNorm(surfmesh, i + 1, norms[i]);
        for (j = 0; j < 3; j++) {
            msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOEDGE, i + 1, SYS_EDGE, j + 1, ted[i][j]);
        }
        msh_SurfMeshSetTriAssoc(surfmesh, VIS_GEOFACE, i + 1, SYS_FACE, 0, tfa[i]);
    }
    /* set mesh parameters */
    msh_SurfMeshSetParami(surfmesh, SURFMESH_NONMANIFOLD, VIS_ON);
    msh_SurfMeshSetParami(surfmesh, SURFMESH_INTSURFBACK, VIS_ON);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_MAXI, maxi);
    msh_SurfMeshSetParami(surfmesh, VIS_MESH_SHAPE, shape);
    msh_SurfMeshSetParamd(surfmesh, VIS_MESH_EDGELENGTH, edgelen);

    msh_SurfMeshWrite(surfmesh, SYS_ASCII, "exam62.srf");
    /* generate */
    msh_SurfMeshGenerate(surfmesh, connectsurf);
    if (msh_SurfMeshError(surfmesh)) {
        printf("surf mesh generation error\n");
        exit(1);
    }
    else {
        printf("surf mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectsurf, SYS_NODE, &numsurfnode);
    vis_ConnectNumber(connectsurf, SYS_ELEM, &numsurfface);
    printf(" Number of nodes= %d\n", numsurfnode);
    printf(" Number of faces= %d\n", numsurfface);

    /* write generated surf mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectsurf, SYS_NASTRAN_BULKDATA, "exam62_srf.bdf");

    /* create VolMesh object */
    volmesh = msh_VolMeshBegin();
    msh_VolMeshDef(volmesh, numsurfnode, numsurfface);

    /* Fill VolMesh object with generated surface mesh */
    msh_VolMeshConnect(volmesh, connectsurf);

    /* Define fill regions */
    for (i = 1; i <= numsurfface; i++) {
        vis_ConnectElemAssoc(connectsurf, VIS_GEOBODY, 1, &i, &aid);
        if (aid == 1) {
            msh_VolMeshSetFaceFill(volmesh, i, VOLMESH_FILL_TET);
        }
        else if (aid == 2) {
            msh_VolMeshSetFaceFill(volmesh, i, VOLMESH_FILL_BOUNDARY);
        }
    }
    /* set mesh parameters */
    /* generate parabolic tets */
    msh_VolMeshSetParami(volmesh, VIS_MESH_MAXI, 3);
    msh_VolMeshSetParamd(volmesh, VIS_MESH_EDGELENGTH, edgelen);

    /* create Connect object to hold generated vol mesh */
    connectvol = vis_ConnectBegin();
    vis_ConnectPre(connectvol, SYS_DOUBLE);
    vis_ConnectDef(connectvol, 0, 0);

    msh_VolMeshWrite(volmesh, SYS_ASCII, "exam62.vol");
    /* generate mesh */
    msh_VolMeshGenerate(volmesh, connectvol);
    if (msh_VolMeshError(volmesh)) {
        printf("vol mesh generation error\n");
        exit(1);
    }
    else {
        printf("vol mesh generation complete\n");
    }
    /* print number of surface nodes and elements */
    vis_ConnectNumber(connectvol, SYS_NODE, &numnp);
    vis_ConnectNumber(connectvol, SYS_ELEM, &numel);
    printf(" Number of nodes= %d\n", numnp);
    printf(" Number of elems= %d\n", numel);

    /* write generated mesh in NASTRAN bulk data format */
    vis_ConnectWrite(connectvol, SYS_NASTRAN_BULKDATA, "exam62_vol.bdf");

    /* end objects */
    vis_ConnectEnd(connectsurf);
    vis_ConnectEnd(connectvol);
    msh_SurfMeshEnd(surfmesh);
    msh_VolMeshEnd(volmesh);
    return 0;
}