Plugin How-To – Obtaining Face Counts

Using the Pointwise Grid Model from within a plugin can be a little confusing. This leads to customers calling our support team with a plugin question that starts with the phrase “How do I…?

While many of these inquiries can be answered by pointing the customer to the appropriate pages in the Pointwise CAE Plugin SDK documentation, some inquiries require a more in-depth explanation with code examples.

I plan to periodically post these interesting inquiries along with their answers. These posts should provide valuable insight into writing a plugin and using the Pointwise Grid Model.

Today’s Plugin How-To deals with obtaining grid model face counts.

Q: How do I determine the total number of internal faces in an unstructured grid?

You will need to do a little grid processing first to calculate the number of internal faces.

First, you must calculate the total number of face usages by all cells from all blocks in the grid. Let’s call this value usageCount. For example, a hex cell has 6 face usages and a pyramid cell has 5 face usages. The total face usage count represents the sum of all boundary faces and all interior faces. However, as you may have already noticed, interior faces are shared between adjacent cells. This results in the double counting of interior faces! That is okay. We will adjust for that later.

Next, you must calculate the total number of grid boundary faces. Let’s call this value bndryCount. This is done by stepping through the grid model domains and summing the total element counts. The individual tri or quad face counts are not needed here. Just the total face count is needed.

With usageCount and bndryCount, we can compute the number of internal faces as:

internalCount = (usageCount – bndryCount) / 2

Subtracting the number of boundary faces from the total face usage count leaves us with only the number of double counted internal faces. Dividing this by 2 gives us the number of distinct interior faces. It is important to note that for this calculation, connection faces are considered to be interior faces. A connection face is a face shared between two cells with different volume conditions.

Example Code Using the C API

[code language=”cpp” toolbar=”false” wraplines=”false” collapse=”true” title=”show example”]
static PWP_UINT32
calcInternalFaceCount(CAEP_RTITEM *pRti)
{
PWGM_ELEMCOUNTS cnts;
// Count block cell face usage counts
PWP_UINT32 usageCount = 0;
PWP_UINT32 ndx = 0;
PWGM_HBLOCK hBlk = PwModEnumBlocks(pRti->model, ndx);
while (PWGM_HBLOCK_ISVALID(hBlk)) {
PwBlkElementCount(hBlk, &cnts);
// process elements according to the export’s
// dimensionality.
if (CAEPU_RT_DIM_2D(pRti)) {
// Run through all 2D elems and sum their
// respective edge counts
usageCount += PWGM_ECNT_Quad(cnts) * 4 +
PWGM_ECNT_Tri(cnts) * 3;
}
else {
// Run through all 3D elems and sum their
// respective face counts
usageCount += PWGM_ECNT_Tet(cnts) * 4 +
PWGM_ECNT_Pyramid(cnts) * 5 +
PWGM_ECNT_Wedge(cnts) * 5 +
PWGM_ECNT_Hex(cnts) * 6;
}
hBlk = PwModEnumBlocks(pRti->model, ++ndx);
}

// Count boundary faces
PWP_UINT32 bndryCount = 0;
ndx = 0;
PWGM_HDOMAIN hDom = PwModEnumDomains(pRti->model, ndx);
while (PWGM_HBLOCK_ISVALID(hDom)) {
PwDomElementCount(hDom, &cnts);
// process elements according to the export dimension
if (CAEPU_RT_DIM_2D(pRti)) {
// Sum the 2D boundary edge counts
bndryCount += PWGM_ECNT_Bar(cnts);
}
else {
// Sum the 3D boundary face counts
bndryCount += PWGM_ECNT_Quad(cnts) +
PWGM_ECNT_Tri(cnts);
}
hDom = PwModEnumDomains(pRti->model, ++ndx);
}

// Compute number of internal faces
PWP_UINT32 internalCount = (usageCount – bndryCount) / 2;

char msg[128];
caeuSendInfoMsg(pRti, "CALC FACE COUNTS:", 0);

sprintf(msg, "boundary: %lu", (unsigned long)bndryCount);
caeuSendInfoMsg(pRti, msg, 0);

sprintf(msg, "interior: %lu",
(unsigned long)internalCount);
caeuSendInfoMsg(pRti, msg, 0);

sprintf(msg, "total: %lu",
(unsigned long)bndryCount + internalCount);
caeuSendInfoMsg(pRti, msg, 0);

return internalCount;
}

// Message window output:
// Info: (0) CALC FACE COUNTS:
// Info: (0) boundary: 13800
// Info: (0) interior: 830050
// Info: (0) total: 843850
[/code]

Example Code Using the C++ API

[code language=”cpp” toolbar=”false” wraplines=”false” collapse=”true” title=”show example”]
void YourPlugin::calcFaceCounts()
{
PWGM_ELEMCOUNTS cnts;
// Count block cell face usage counts
PWP_UINT32 usageCount = 0;
CaeUnsBlock blk(model_);
while (blk.isValid()) {
blk.elementCount(&cnts);
// process elements according to the export dimension.
if (isDimension2D()) {
// Run through all 2D elems and sum their
// respective edge counts
usageCount += PWGM_ECNT_Quad(cnts) * 4 +
PWGM_ECNT_Tri(cnts) * 3;
}
else {
// Run through all 3D elems and sum their
// respective face counts
usageCount += PWGM_ECNT_Tet(cnts) * 4 +
PWGM_ECNT_Pyramid(cnts) * 5 +
PWGM_ECNT_Wedge(cnts) * 5 +
PWGM_ECNT_Hex(cnts) * 6;
}
++blk;
}
// Count boundary faces
PWP_UINT32 bndryCount = 0;
CaeUnsPatch patch(model_);
while (patch.isValid()) {
patch.elementCount(&cnts);
// process elements according to the export dimension.
if (isDimension2D()) {
// Sum the 2D boundary edge counts
bndryCount += PWGM_ECNT_Bar(cnts);
}
else {
// Sum the 3D boundary face counts
bndryCount += PWGM_ECNT_Quad(cnts) +
PWGM_ECNT_Tri(cnts);
}
++patch;
}
// Compute number of internal faces
PWP_UINT32 internalCount = (usageCount – bndryCount) / 2;
char msg[128];
sendInfoMsg("CALC FACE COUNTS:");

sprintf(msg, "boundary: %lu", (unsigned long)bndryCount);
sendInfoMsg(msg);

sprintf(msg, "interior: %lu",
(unsigned long)internalCount);
sendInfoMsg(msg);

sprintf(msg, "total: %lu",
(unsigned long)bndryCount + internalCount);
sendInfoMsg(msg);
}

// Message window output:
// Info: (0) CALC FACE COUNTS:
// Info: (0) boundary: 13800
// Info: (0) interior: 830050
// Info: (0) total: 843850
[/code]

Q: I see that face count information is available to the face streaming callbacks. Can I use face streaming to obtain the face counts?

The short answer is yes.

However, face streaming can be a time- and memory-intensive operation. Face streaming should only be used if needed. Fortunately, there is a little face streaming trick you can use to obtain the face counts without incurring the usual overhead.

Basically, you should invoke the face streamer as usual providing the appropriate callbacks. Then, you would capture the face count numbers in the beginStream() callback. However, the beginStream() callback should return 0 (zero) to halt streaming! This approach is acceptable because the processing performed by the face streamer prior to calling the beginStream() callback is relatively light. The vast majority of the face streaming overhead is avoided by returning 0.

Example Code Using the C API

[code language=”cpp” toolbar=”false” wraplines=”false” collapse=”true” title=”show example”]
struct FaceCounts {
// Capture these values
PWP_UINT32 totalNumFaces_;
PWP_UINT32 numBoundaryFaces_;
PWP_UINT32 numInteriorFaces_;
};

static PWP_UINT32 beginCB(PWGM_BEGINSTREAM_DATA *data)
{
FaceCounts *cnts = (FaceCounts*)data->userData;
// capture face counts
cnts->totalNumFaces_ = data->totalNumFaces;
cnts->numBoundaryFaces_ = data->numBoundaryFaces;
cnts->numInteriorFaces_ = data->numInteriorFaces;
return 0; // 0 forces streaming to end
}

static PWP_UINT32
streamInternalFaceCount(CAEP_RTITEM *pRti)
{
// Instatiate the face count data.
FaceCounts faceCounts;
// Stream faces through beginCB. It is okay to set the
// faceCB and endCB to null because they are never
// called. Pass faceCounts to streamer as user data.
PwModStreamFaces(pRti->model, PWGM_FACEORDER_DONTCARE,
beginCB, 0, 0, &faceCounts);
// After PwModStreamFaces() returns, faceCounts has been
// updated with the counts!
char msg[128];
caeuSendInfoMsg(pRti, "STREAM FACE COUNTS:", 0);

sprintf(msg, "boundary: %lu",
(unsigned long)faceCounts.numBoundaryFaces_);
caeuSendInfoMsg(pRti, msg, 0);

sprintf(msg, "interior: %lu",
(unsigned long)faceCounts.numInteriorFaces_);
caeuSendInfoMsg(pRti, msg, 0);

sprintf(msg, "total: %lu",
(unsigned long)faceCounts.totalNumFaces_);
caeuSendInfoMsg(pRti, msg, 0);

return faceCounts.numInteriorFaces_;
}

// Message window output:
// Info: (0) STREAM FACE COUNTS:
// Info: (0) boundary: 13800
// Info: (0) interior: 830050
// Info: (0) total: 843850
[/code]

Example Code Using the C++ API

[code language=”cpp” toolbar=”false” wraplines=”false” collapse=”true” title=”show example”]
// Define a stream handler to capture face counts
class FaceCounts : public CaeFaceStreamHandler {
public:
FaceCounts() :
totalNumFaces_(0),
numBoundaryFaces_(0),
numInteriorFaces_(0)
{
}

~FaceCounts()
{
}

PWP_UINT32 streamBegin(const PWGM_BEGINSTREAM_DATA &data)
{
// capture face counts
totalNumFaces_ = data.totalNumFaces;
numBoundaryFaces_ = data.numBoundaryFaces;
numInteriorFaces_ = data.numInteriorFaces;
return 0; // 0 forces streaming to end
}

PWP_UINT32 streamFace(const PWGM_FACESTREAM_DATA &)
{
// This virtual override is required. However, it
// will never get called because we return 0 in
// streamBegin()
return 0;
}

// Capture these values
PWP_UINT32 totalNumFaces_;
PWP_UINT32 numBoundaryFaces_;
PWP_UINT32 numInteriorFaces_;
};

// Obtain the face counts in your plugin
void YourPlugin::streamFaceCounts()
{
// Instantiate the face count stream handler
FaceCounts faceCounts;
// stream faces through the faceCounts handler
model_.streamFaces(PWGM_FACEORDER_DONTCARE, faceCounts);
// After streamFaces() returns, faceCounts has been
// updated with the counts!
char msg[128];
sendInfoMsg("STREAM FACE COUNTS:");

sprintf(msg, "boundary: %lu",
(unsigned long)faceCounts.numBoundaryFaces_);
sendInfoMsg(msg);

sprintf(msg, "interior: %lu",
(unsigned long)faceCounts.numInteriorFaces_);
sendInfoMsg(msg);

sprintf(msg, "total: %lu",
(unsigned long)faceCounts.totalNumFaces_);
sendInfoMsg(msg);
}

// Message window output:
// Info: (0) STREAM FACE COUNTS:
// Info: (0) boundary: 13800
// Info: (0) interior: 830050
// Info: (0) total: 843850
[/code]

Your Suggestions Welcome

I hope this information is useful. If you have any other ideas for a future Plugin How-To post, please leave a comment below or contact our support staff.

Happy coding!

Find out more about the Pointwise Plugin SDK by clicking on the link below.

Learn more about the Pointwise CAE Plugin SDK

About David Garlisch

Illini by birth, Texan by choice.
This entry was posted in Applications, Software and tagged , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply