IFC SDK and SDAI: Work with Nested Aggregate Attributes

Egor Vorobyov

January 22, 2021

IFC SDK supports the Standard Data Access Interface (SDAI) which provides a low-level API for manipulating data defined with the EXPRESS Schema. This article describes how to work with nested aggregate attributes.

For details about generally working with SDAI in IFC SDK, see this article.

Let’s read data from an aggregate attribute that contains an array of other aggregate attributes. Assume that an .ifc file contains the following application instance:

//#1277= IFCRATIONALBSPLINESURFACEWITHKNOTS(3,1, ((#1249,#1251), (#1253,#1255), (#1257,#1259), (#1261,#1263), (#1265,#1267), (#1269,#1271), (#1273,#1275)), .RULED_SURF.,.F.,.F.,.U.,(4,1,1,1,4),(2,2),(0.,0.25,0.5,0.75,1.),(0.,1.),.UNSPECIFIED., ((1.,1.),(1.,1.),(1.,1.),(1.,1.),(1.,1.),(1.,1.),(1.,1.)));

This instance contains two-dimensional aggregates (highlighted with bold text). Below you can see how to read the first aggregate item from each two-dimensional aggregate.

Get the appropriate parent instance with the _sdaiGetEntityById() function:

SdaiAppInstance applicationInstance = _sdaiGetEntityById(model, 1277);
TEST_ASSERT(applicationInstance != NULL);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

From the instance attribute named "controlpointslist", get an aggregate value with sdaiGetAttrBN:

SdaiAggr  aggregate2dControlPointsList = NULL;
TEST_ASSERT(sdaiGetAttrBN(applicationInstance, 
				"controlpointslist", 
				sdaiAGGR, 
				&aggregate2dControlPointsList) != NULL);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

Get a child aggregate from the aggregate that was extracted previously:

SdaiAggr  aggregateChild = NULL;
TEST_ASSERT(sdaiGetAggrByIndex(aggregate2dControlPointsList, 
 				      0, 
 				      sdaiAGGR, 
                                      &aggregateChild) != NULL);

TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

The extracted child aggregate should contain a list of application instances. We can use sdaiGetAggrByIndex to extract the child aggregate because we are working with an ordered aggregate list. Look at the application instance in the .ifc file and find a value of the first element #1249, #1251. We will compare these values with the values of the child aggregate:

SdaiAppInstance  childAggrAppInstnce = NULL;
TEST_ASSERT(sdaiGetAggrByIndex (aggregateChild, 
 					0, 
 					sdaiINSTANCE, 
 					&childAggrAppInstnce) != NULL);
TEST_ASSERT(_sdaiGetEntityId(childAggrAppInstnce) == 1249);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

TEST_ASSERT(sdaiGetAggrByIndex (aggregateChild, 
 					1,
 					sdaiINSTANCE, 
 					&childAggrAppInstnce) != NULL);
TEST_ASSERT(_sdaiGetEntityId(childAggrAppInstnce) == 1251);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

Let’s read another 2D aggregate with the attribute named "weightsdata". The sample instance declaration contains a check value for the extracted data: 1.,1. Use sdaiGetAttrBN to extract the attribute value from the application instance:

SdaiAggr  aggregate2dWeightsData = NULL;

TEST_ASSERT(sdaiGetAttrBN(applicationInstance, 
 				"weightsdata", 
 				sdaiAGGR, 
 				&aggregate2dWeightsData) != NULL);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

Then extract the 2D aggregate to aggregate2dWeightsData and extract a child aggregate from it:

TEST_ASSERT(sdaiGetAggrByIndex(aggregate2dWeightsData, 
 				      0,
   				      sdaiAGGR, 
                 	              &aggregateChild) != NULL);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

If there are no errors, aggregateChild should be the valid instance of the aggregate. Looking to the expression scheme, this aggregate should be a list of real values, so we can extract a real value from the child aggregate using sdaiGetAggrByIndex. For item validation, check the data from the sample .ifc file.

SdaiReal  childAggrReal = NULL;

TEST_ASSERT(sdaiGetAggrByIndex(aggregateChild, 0, sdaiREAL, &childAggrReal) != NULL);
TEST_ASSERT(OdEqual(childAggrReal, 1.));
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

TEST_ASSERT(sdaiGetAggrByIndex(aggregateChild, 1, sdaiREAL, &childAggrReal) != NULL);
TEST_ASSERT(OdEqual(childAggrReal, 1.));
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);