创建 CAE 插件——了解点状网格模型 API(第 1 部分)

CAE 插件的主要目的是导出网格模型。为此,插件必须将 Pointwise 表示的网格数据转换为插件所针对的求解器支持的格式。要正确进行此转换,您必须对点状网格模型 (PWGM) 有透彻的了解。

对于这篇文章,我将重点关注非结构化 PWGM。结构化 PWGM 将在以后的帖子中介绍。虽然结构化 PWGM 和非结构化 PWGM 之间有很多共同点,但也存在一些根本性差异,因此两篇文章比一篇文章更好地介绍了它们。

网格手柄

在这篇文章中,我将提到句柄。句柄是一种无需使用指针即可唯一标识任何 PWGM 实体或元素的紧凑方式。对于那些熟悉 C/C++ 的人来说,Handle 可以被认为是 SDK 的 C++ this指针。非结构化 PWGM 支持五种句柄类型:

  • PWGM_HGRIDMODEL

  • PWGM_HBLOCK

  • PWGM_HDOMAIN

  • PWGM_HVERTEX

  • PWGM_HELEMENT

每种 Handle 类型都有一个或多个 SDK 函数,用于查询 Handle 代表的实体。SDK 使用函数命名方案,其中名称的前缀反映了被查询的实体句柄。例如,所有以 a 开头的函数都将PwDomaPWGM_HDOMAIN作为它们的第一个参数(域的this指针)。

使用 Handles 还可以减少 SDK 函数所需的参数数量。它们的使用还允许在您的插件代码中进行更简单的函数调用。例如,如果没有句柄,块元素函数将需要三个参数来唯一标识给定的 PWGM 块元素:

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
elementFunction(model, blkIndex, elementIndex, otherArgsHere);
[/源代码]

但是,使用句柄,函数调用变为:

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
elementFunction(hElement, otherArgsHere);
[/源代码]

您还可以使用各种 SDK 宏从句柄中提取信息。有关详细信息,请参阅CAE 插件 SDK 文档的模块/PWGM-API 不透明数据处理类型部分。

网格层次结构

非结构化 PWGM 以层次结构排列。这个层次结构的根是网格模型本身。在导出时,网格模型的句柄runtimeWrite()使用参数传递给插件的函数 PWGM_HGRIDMODEL model

[sourcecode language=”cpp” gutter=”true” toolbar=”false” wraplines=”false”]
PWP_BOOL runtimeWrite(CAEP_RTITEM *pRti, PWGM_HGRIDMODEL model, const CAEP_WRITEINFO *pWriteInfo);
// model 和 pWriteInfo 也可以使用 pRti->model 和 pRti->pWriteInfo
[/sourcecode]

非结构化 PWGM 分为两个不同但相关的视图。第一个视图提供对网格模型的以单元格为中心的访问。第二个视图提供以面为中心的网格模型访问。

当前 1.0 R3 版本的插件 SDK 中不提供以人脸为中心的视图 API。但是,编码已完成并正在测试中。我希望在接下来的几周内发布它

更新:以人脸为中心的视图 API 现在可用于 Pointwise v17.1 + Plugin SDK v1.0 R4!

在以细胞为中心的视图中,网格模型包括

  • 一组块,每个块都有一组单元格元素和一个条件。

  • 一组域,每个域都有一组面元素和一个条件。

  • 一组顶点。

在以面为中心的视图中,网格模型包括

  • 所有块单元格元素的一个数组。

  • 所有单元面元素的一个数组,包括位于网格边界上的面和网格内部的面。

  • 一组顶点。

重要的是要注意,以单元为中心的视图和以面为中心的视图都使用相同的顶点数组。如上所述,顶点是将两种观点联系在一起的共同关系。完整的网格模型层次结构如下图所示。

创建 CAE 插件——了解点状网格模型 API(第 1 部分)的图1

网格模型层次结构

网格维数

对于注意到上面的讨论没有提到 2D 网格与 3D 网格的人来说,这是一颗金星。这是因为 PWGM 使访问网格数据(大部分)独立于网格的维度。

与 Pointwise 本身一样,PWGM 支持 2D 和 3D 网格。但是,有一个关键区别。PWGM 独立于其 2D 或 3D 维度为网格数据提供相同的接口。PWGM 的一致性是使用五个通用实体实现的;块、域、元素、顶点和条件。具体来说,从 PWGM 的角度来看:

  • 一个是块元素的集合,所有元素都被分配了一个共同的体积条件。每个块都由一个唯一的整数索引标识。索引范围从 0 到模型中的块数 (Nb) 减去 1 (0 … Nb-1)。

  • 域是域元素的集合,所有域元素都分配了一个公共边界条件每个域都由一个唯一的整数索引标识。指数范围从 0 到模型中的域数 (Nd) 减去 1 (0 … Nd-1)。

  • 顶点是由唯一整数索引标识的单个 XYZ 坐标。索引范围从 0 到模型中的顶点数 (Nv) 减去 1 (0 … Nv-1)。

  • 元素是顶点索引的有序集合索引顺序定义了元素的连接性(请参阅模块/单元连接性)。

  • 条件是一组两个用户定义的属性和两个求解器定义的属性

插件使用相同的函数调用访问此实体层次结构。2D 和 3D 之间的区别仅在元素中发挥作用。例如,3D 块的元素是六边形、棱柱、金字塔和四边形。3D 边界域的元素是三边形和四边形。同样,二维块的元素是三边形和四边形。二维边界域的元素是条(线)。下表总结了 PWGM 网格实体、它们的元素和相应的逐点网格实体之间的关系。

创建 CAE 插件——了解点状网格模型 API(第 1 部分)的图2

按维度划分的网格模型元素类型和 Pointwise 实体类型

在将您在 Pointwise 中创建的网格传递给插件之前,共享相同边界条件的 Pointwise 边界实体(3D 域或 2D 连接器)被合并到一个 PWGM 域中。因此,PWGM 域的数量可能与点边界实体的数量不同,具体取决于点边界条件应用于网格的方式。

相反,共享共同体积条件的逐点体积(3D 中的块或 2D 中的域)不会合并。这意味着 PWGM 块的数量始终等于 Pointwise 体积实体的数量。下图说明了 2D Pointwise 网格如何出现在以单元为中心的视图的插件中。

创建 CAE 插件——了解点状网格模型 API(第 1 部分)的图3

边界合并如何在将点状网格传递给插件(以单元为中心的视图)之前更改点状网格。

在以面为中心的视图中,边界和体积条件的合并没有意义,因为块和域仅作为单元和面元素访问。下图说明了相同的 2D Pointwise 网格如何出现在以面为中心的视图的插件中。

创建 CAE 插件——了解点状网格模型 API(第 1 部分)的图4

在以面为中心的视图中,Pointwise 网格如何呈现给插件。

枚举实体

PWGM 提供对所有网格实体及其相应元素的枚举、随机访问,但以面为中心的视图中的面除外。使用此方案,为每个实体或实体元素提供一个 SDK 功能以确定网格模型中存在的项目数。然后将计数与其他相关的枚举 SDK 函数一起使用,以逐一遍历项目层次结构。例如,网格模型块的数量是使用该PwModBlockCount()函数确定的。有了这个计数,您就可以使用该函数枚举块PwModEnumBlocks()。PWGM 枚举函数返回请求项的句柄。顶部的模型级函数名称以PwMod字首。这些函数都将网格模型句柄作为其输入,并向枚举的网格模型实体返回计数或句柄。非结构化 PWGM 目前支持的顶级模型级功能是

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_UINT32 PwModBlockCount (PWGM_HGRIDMODEL 模型);
PWGM_HBLOCK PwModEnumBlocks(PWGM_HGRIDMODEL 模型,PWP_UINT32 ndx);
PWP_UINT32 PwModDomainCount(PWGM_HGRIDMODEL 模型);
PWGM_HDOMAIN PwModEnumDomains(PWGM_HGRIDMODEL 模型,PWP_UINT32 ndx);
PWP_UINT32 PwModVertexCount(PWGM_HGRIDMODEL 模型);
PWGM_HVERTEX PwModEnumVertices(PWGM_HGRIDMODEL 模型,PWP_UINT32 ndx);
[/源代码]

枚举块或域的元素以类似的方式完成。首先,您使用适当的块或域句柄(使用上述函数获得)获得实体的元素计数。然后,使用计数,逐个枚举元素,获得每个元素的句柄。元素的句柄用于获取元素的数据。PWGM 目前支持的 Block 和 Domain Element 功能是

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_UINT32 PwBlkElementCount (PWGM_HBLOCK block, PWGM_ELEMCOUNTS *pCounts);
PWGM_HELEMENT PwBlkEnumElements(PWGM_HBLOCK 块,PWP_UINT32 ndx);
PWP_UINT32 PwDomElementCount(PWGM_HDOMAIN 域,PWGM_ELEMCOUNTS *pCounts);
PWGM_HELEMENT PwDomEnumElements(PWGM_HDOMAIN 域,PWP_UINT32 ndx);
[/源代码]

使用两个函数获取元素的数据:

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_BOOL PwElemDataMod (PWGM_HELEMENT element, PWGM_ELEMDATA *pData);
PWP_BOOL PwEnumElemDataMod(PWGM_HELEMENT 元素,PWGM_ENUMELEMDATA *pData);
[/源代码]

除了元素之外,每个块和域都有一个关联的条件。条件表示块的体积条件或域的边界条件。使用下面列出的函数获取条件数据:

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_BOOL PwBlkCondition (PWGM_HBLOCK block, PWGM_CONDDATA *pCondData);
PWP_BOOL PwDomCondition(PWGM_HDOMAIN 域,PWGM_CONDDATA *pCondData);
[/源代码]

网格模型的 XYZ 顶点数据是使用下面列出的函数获得的。特定导出格式的细节将决定使用哪些功能。有关详细信息,请参阅插件文档。

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_BOOL PwVertDataMod (PWGM_HVERTEX vertex, PWGM_VERTDATA *pVertData);
PWP_BOOL PwVertIndexMod (PWGM_HVERTEX 顶点, PWP_UINT32 *pIndex);
PWP_BOOL PwVertXyzVal (PWGM_HVERTEX 顶点, PWGM_ENUM_XYZ 其中, PWGM_XYZVAL *pVal);
[/源代码]

流媒体实体

PWGM 提供对以人脸为中心的视图的人脸的“流式”访问。从某种意义上说,它们是流式传输的,一次可以让一个插件使用这些面孔。一旦插件完成了面部元素,它就无法再次访问面部元素,除非从头开始重新启动流(除非您在插件中制作面部数据的本地副本)。

我们决定在 PWGM 中使用人脸流,以最大限度地减少导出过程中消耗的额外内存量。Pointwise 没有明确存储网格面信息。因此,为了提供以面部为中心的视图,PWGM 需要处理以细胞为中心的数据并将其转换为以面部为中心的表示。为枚举、随机访问存储完整的、以面部为中心的视图可以使以细胞为中心的视图已经需要的 RAM 量增加一倍以上。而且,正如我们的许多客户已经知道的那样,大型网格模型可能会超出许多现代计算机的 RAM 限制。添加非流式面部元素数据可能会超过计算机可用的 RAM,并且根本无法导出一些大网格!

插件通过调用 PwModStreamFaces()SDK 函数启动人脸流会话。此函数接受一个PWGM_ENUM_FACEORDER值、三个回调函数和一个用户数据指针。

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_BOOL PwModStreamFaces(PWGM_HGRIDMODEL model, PWGM_ENUM_FACEORDER order,
PWGM_BEGINSTREAMCB beginCB, PWGM_FACESTREAMCB faceCB,
PWGM_ENDSTREAMCB endCB, void *userData);
[/源代码]

可选的用户数据指针可以设置为 null 或者它可以用于将插件特定的运行时信息传递给流会话的所有阶段。用户数据指针被传递给三个回调中的每一个。

面部顺序值控制面部在流式传输期间的分组方式。下面列出了支持的订单。

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
typedef enum PWGM_ENUM_FCEORDER_e {
PWGM_FACEORDER_DONTCARE, // 任意顺序的所有单元面
PWGM_FACEORDER_BOUNDARYFIRST, // 边界内的所有单元面,内部顺序
PWGM_FACEORDER_INTERIORFIRST, // 所有单元内部面,边界顺序
PWGM_FACEORDER_BOUNDARYONLY, // 只有边界单元面
PWGM_FCEORDER_INTERIORONLY, // 只有内部单元面
PWGM_FCEORDER_BCGROUPSFIRST, // BoundaryFirst 按 BCs 分组
PWGM_FACEORDER_BCGROUPSLAST, // InteriorFirst 按 BCs
分组 PWGM_FACEORDER_BCGROUPSONLY, //
Boundary_UM_WORDER
}
[/源代码]

使用三个回调函数将面孔流式传输到插件。beginCB()PWGM 在流会话开始时调用一次回调。此回调接收将流式传输的边界、内部和总面数。

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_BOOL beginCB(PWGM_BEGINSTREAM_DATA *data);

typedef struct PWGM_BEGINSTREAM_DATA_t {
PWGM_ENUM_FACEORDER 命令;//请求的单元格面流序列顺序。
PWP_UINT32 totalNumFaces;// 模型中的总面数(= #Boundary + #Interior)。
PWP_UINT32 numBoundaryFaces;// # totalNumFaces 中的面位于模型的边界上。
PWP_UINT32 numInteriorFaces;// # totalNumFaces 中位于模型内部的面。
PWGM_ELEMCOUNTS 计数;// 模型的总元素数。
PWGM_HGRIDMODEL 模型;// 网格模型句柄
void *userData; // 指针传递给 PwModStreamFaces(…, void *userData)
}
PWGM_BEGINSTREAM_DATA;
[/源代码]

当插件从 返回时beginCB(),PWGM 开始处理以单元格为中心的数据。面完成后,它们将传递给faceCB()回调。传递给此回调的信息包括面类型(内部或边界)、面顶点和面任一侧的块单元格元素。

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_BOOL faceCB(PWGM_FACESTREAM_DATA *data);

typedef struct PWGM_FACESTREAM_DATA_t {
PWGM_HGRIDMODEL 模型;// 网格模型句柄
PWP_UINT32 面;// 人脸在模型索引空间中的索引
PWGM_ELEMDATA elemData; // 人脸的元素数据。
PWGM_ENUM_FACETYPE 类型;// PWGM_FACETYPE_XXX 类型之一。
PWGM_FACEREF_DATA 所有者;// 拥有面的块元素。
PWGM_FACEREF_DATA 邻居;// 面另一侧的块元素。// 如果类型为 PWGM_FACETYPE_BOUNDARY,则此值
未定义。
无效*用户数据;// 指针传递给 PwModStreamFaces(…, void *userData)
}
PWGM_FACESTREAM_DATA;
[/源代码]

最后一张脸传递给 后faceCB(),PWGM 对回调进行最后一次调用endCB()

[sourcecode language=”cpp” gutter=”true” toolbar=”false”]
PWP_BOOL endCB(PWGM_ENDSTREAM_DATA *data);

typedef struct PWGM_ENDSTREAM_DATA_t {
PWGM_HGRIDMODEL 模型;// 网格模型句柄
PWP_BOOL ok; // PWP_TRUE 如果流式传输成功完成
void *userData; // 指针传递给 PwModStreamFaces(…, void *userData)
}
PWGM_ENDSTREAM_DATA;
[/源代码]

以下示例 C++ 代码显示了正在使用的流式 API。

[sourcecode language=”cpp” collapse=”true”]
/*************************************** **/
PWP_UINT32
beginCB(PWGM_BEGINSTREAM_DATA *data)
{
// 将用户数据指针转换为 CAEP_RTITEM*
CAEP_RTITEM *pRti = (CAEP_RTITEM*)data->userData;

// 在这里进行初始化

// 设置开始进度步数
 return caeuProgressBeginStep(pRti, data->totalNumFaces);
}

/******************************************/
PWP_UINT32
faceCB(PWGM_FACESTREAM_DATA *data)
{
//将用户数据指针转换为 CAEP_RTITEM*
CAEP_RTITEM *pRti = (CAEP_RTITEM*)data->userData;

// 处理这张脸
if (PWGM_FACETYPE_INTERIOR == data->type) {
// 做点什么
}
else if (PWGM_FACETYPE_BOUNDARY == data->type) {
// 做点别的
 }

// 递增进度
return caeuProgressIncr(pRti);
}

/******************************************/
PWP_UINT32
endCB(PWGM_ENDSTREAM_DATA *data)
{
//将用户数据指针转换为 CAEP_RTITEM*
CAEP_RTITEM *pRti = (CAEP_RTITEM*)data->userData;

// 在这里执行最后的清理

// 结束进度步骤
return caeuProgressEndStep(pRti);
}

/******************************************/
PWP_BOOL
runtimeWrite(CAEP_RTITEM *pRti,PWGM_HGRIDMODEL 模型,
const CAEP_WRITEINFO * /*pWriteInfo*/)
{
PWP_BOOL ret = PWP_FALSE;
if (caeuProgressInit(pRti, 2) && writePointsFile(*pRti)) {
// 在此示例中,将 pRti 作为 PwModStreamFaces(…, void *userData) 参数传递。
// 但是,可以使用任何特定于实现的值。
ret = PwModStreamFaces(模型,PWGM_FACEORDER_BCGROUPSLAST,beginCB,faceCB,endCB,pRti);
}
caeuProgressEnd(pRti, ret);
返还;
}
[/源代码]

了解 Pointwise CAE 插件 SDK 中可用的非结构化网格模型后,您应该能够创建一个插件,将您选择的求解器添加到 Pointwise。与往常一样,如果您有任何其他问题,请随时联系 Pointwise 支持。我们随时为您提供帮助!

我将在下一篇文章中介绍结构化网格模型。

文章来源:pointwise博客
原文:https://blog.pointwise.com/2012/05/23/creating-a-cae-plugin-understanding-the-pointwise-grid-model-api-part-1/

默认 最新
当前暂无评论,小编等你评论哦!
点赞 评论 收藏
关注