Using bulk data access to an output database

If you need to access all the data in a field from an output database, you can use the bulkDataBlocks method of the FieldOutput object to read the data in bulk form. The bulkDataBlocks method returns a reference to a sequence of FieldBulkData objects, each of which contains the entire output for a class of nodes or elements, blocked together into an array.

The data method of the FieldBulkData object returns an array of data corresponding to the output for the entire class of elements or nodes. The length and width methods of theFieldBulkData object return the number of output locations and the number of components at each output location, respectively. For example,


    odb_FieldOutput& disp = lastFrame.fieldOutputs()["U"];
    const odb_SequenceFieldBulkData& seqDispBulkData = 
        disp.bulkDataBlocks();
    int numDispBlocks = seqDispBulkData.size();
    for (int iblock=0; iblock<numDispBlocks; iblock++) {
        const odb_FieldBulkData& bulkData = 
            seqDispBulkData[iblock];
        int numNodes = bulkData.length();
        int numComp = bulkData.width();
        float* data = bulkData.data();
        int* nodeLabels = bulkData.nodeLabels();
        for (int node=0,pos=0; node<numNodes; node++) {
            int nodeLabel = nodeLabels[node];
            cout << "Node = " << nodeLabel;
	    cout << " U = ";
	    for (int comp=0;comp<numComp;comp++)
	      cout << data[pos++] << "  ";   
	    cout << endl;
        }
    }
 

The numberOfElements method returns the number of elements in a block. When you are accessing the results for elements, the numberOfElements method is useful in determining the number of output locations per element. For example, when you are accessing element data at integration points, you may need to determine the number of integration points per element. You can determine the number of integration points per element by dividing the length of the block, which is the total number of output locations, by the number of elements in the block. For example,


    odb_FieldOutput& stress = lastFrame.fieldOutputs()["S"];
    const odb_SequenceFieldBulkData& seqStressBulkData = 
        stress.bulkDataBlocks(); 
    int numStressBlocks = seqStressBulkData.size();
    for (int jblock=0; jblock<numStressBlocks; jblock++) {
        const odb_FieldBulkData& bulkData = 
            seqStressBulkData[jblock];
        int numValues = bulkData.length();
        int numComp = bulkData.width();
        float* data = bulkData.data();
        int nElems = bulkData.numberOfElements();
        int numIP = numValues/nElems;
        int* elementLabels = bulkData.elementLabels();
        int* integrationPoints = bulkData.integrationPoints();      
        const odb_SectionPoint& myBulkSectionPoint = 
            bulkData.sectionPoint();
	int sectPoint = myBulkSectionPoint.number();
	if (sectPoint)
	  cout << "Section Point: " << sectPoint << endl;
	cout << "Base Element type: " 
        << bulkData.baseElementType().CStr() << endl;
        for (int elem = 0, ipPosition=0, dataPosition=0; 
        elem<numValues; elem+=numIP) {
            cout << "El label: " << elementLabels[elem] << endl;
            for (int ip = 0; ip<numIP; ip++) {                
	        cout << "Int. Point: " 
                << integrationPoints[ipPosition++] << endl;
                cout << "S = "; 
                for (int comp = 0; comp<numComp; comp++)
                    cout << " " << data[dataPosition++] << " ";
                cout << endl;
            }
        }
    }
 

For more information, see FieldBulkData object.

The bulkDataBlocks method is an alternative to the values method of a FieldOutput object, described in Reading field output data. The values method of a FieldOutput object returns a reference to a sequence of FieldValue objects that contain data. Each FieldValue object in the sequence provides data for a unique location in the model.

Performance can be increased with the bulk data interface because the field data stored in a bulk data block are made available in a single array of floating point numbers. If you access the same data in nonbulk form, you must loop over a sequence of FieldValue objects and then access the data for each location separately. Traversing an array can prove to be significantly faster than traversing a sequence of objects and extracting data stored within the objects. As a result, accessing the data in an output database using the bulk data interface can be significantly faster than the nonbulk form.

If you do not need to access large amounts of data, you may get better performance with the nonbulk access method. This is especially true if the number of output locations you are accessing is smaller than the number of elements in a class. Similarly, the nonbulk access method may be faster if the number of nodes you are accessing is smaller than the number of nodes in an instance. The nonbulk access method is also better suited for random access to an output database, where successive output locations to be accessed may lie in completely different blocks.