cee::ug::DataReader

class DataReader : public RefCountedObject

The DataReader is an easy and efficient way to get data into the UnstructGridModel, leveraging the data management support of UnstructGridModel through the DataSourceReader.

The DataReader is responsible for providing data to the DataSourceReader data source. After implementing the DataReader for your file format, you can just plug this custom reader into a DataSourceReader, set this as the data source for you UnstructGridModel. Then you can open an analysis, and use the DataSourceDirectory information to populate your UI. Then specify what to visualize in the ModelSpec as well as on cutting planes, isosurfaces, isovolumes and particle traces. UnstructGridModel will use the derived DataReader to load the geometry and results whenever needed. You only have to specify what to show, and the UnstructGridModel will do the data management.

To create a reader, you have to implement the following methods:

  • isSupportedFileFormat(): Return true if the file provided is supported by your reader. This can be used by your app to auto detect the file format and use the right reader for the given file.

  • open(): Open the file and prepare to answer the other methods in this class. Do not read all the data!

  • close(): Close any open files. Prepare for a new open().

  • geometryCount(): Return the number of geometries you are going to provide (usually 1).

  • initializeDirectory(): Update the given DataSourceDirectory with all the metadata for your analysis. Here you provide information about all parts, data states and results.

  • readGeometry(): Read the specified geometry. This will only be called once unless you override hasNewGeometryForEachState() and return true for the given geometry.

Example:

class SingleTriangleReader : public cee::ug::DataReader
{
public:
    bool isSupportedFileFormat(const cee::Str& filename) { return true; }
    bool open(const cee::Str& filename, Error* error)    { return true; }
    void close()                                        { }
    size_t geometryCount() const                        { return 1; }

    bool initializeDirectory(cee::ug::DataSourceDirectory* directory, Error* error)
    {
        directory->setPartInfo(0, cee::ug::PartInfo(1, "First part"));
        directory->setStateInfo(cee::ug::StateInfo(1, "First state", 0.0));
        directory->setResultInfo(cee::ug::ResultInfo(SCALAR, 10, "Per node result", cee::ug::PER_NODE));

        return true;
    }

    bool readGeometry(int stateId, size_t geometryIndex, DataGeometry* geometry, Error* error)
    {
        cee::PtrRef<cee::ug::DataPart> part = new cee::ug::DataPart(1);
        cee::PtrRef<cee::ug::DataElements> elements = new cee::ug::DataElements(false, 0);
        cee::PtrRef<cee::ug::DataNodes> nodes = new cee::ug::DataNodes(false);
        part->setElements(elements.get());
        part->setNodes(nodes.get());
        nodes->resize(3);
        nodes->setNode(0, cee::Vec3d(0,0,0));
        nodes->setNode(1, cee::Vec3d(1,0,0));
        nodes->setNode(2, cee::Vec3d(1,1,0));

        std::vector<unsigned int> elementIndices;
        for (unsigned int i = 0; i < 3; ++i) elementIndices.push_back(i);
        elements->addElement(cee::ug::Element::TRIANGLES, elementIndices);

        geometry->addPart(part.get());

        return true;
    }

    bool readScalarResult(int stateId, size_t geometryIndex, int resultId, DataResultScalar* scalarResult, Error* error)
    {
        std::vector<double> values;
        for (size_t i = 0; i < 3; ++i) values.push_back(static_cast<double>(i));

        cee::PtrRef<cee::ug::DataPartScalar> part = new cee::ug::DataPartScalar;
        part->setValues(values);
        scalarResult->addPart(part.get());

        return true;
    }
};

cee::PtrRef<SingleTriangleReader> reader = new SingleTriangleReader;
cee::PtrRef<cee::ug::DataSourceReader> dataSource = new cee::ug::DataSourceReader(1, reader.get());

cee::PtrRef<cee::ug::UnstructGridModel> model = new cee::ug::UnstructGridModel;
model->setDataSource(dataSource.get());

dataSource->open("MyFile.xyz");

std::vector<cee::ug::StateInfo>  stateInfos  = dataSource->directory()->stateInfos();
model->modelSpec().setStateId(stateInfos[0].id());
std::vector<cee::ug::ResultInfo> scalarInfo = dataSource->directory()->scalarResultInfos();
model->modelSpec().setFringesResultId(scalarInfo[0].id());

model->updateVisualization();

Public Functions

DataReader()
virtual bool isSupportedFileFormat(const Str &filename) = 0

Returns true if the given file is supported by the reader.

Note! This method must be overridden by all derived readers.

virtual bool open(const Str &filename, Error *error) = 0

Opens the given file and prepare to answer the initializeDirectory() method and then readGeometry() etc.

Returns true if the file was successfully opened the reader is ready to provide data. Please note that you should usually not read all the data in this method, only check that the file is present and maybe do some initial reading.

Note! This method must be overridden by all derived readers.

virtual void close() = 0

Supposed to close any of your open file(s) and prepare to open a new file.

Note! This method must be overridden by all derived readers.

virtual size_t geometryCount() const = 0

Returns the number of geometries provided by this data reader.

The normal case will be one geometry

Note! This method must be overridden by all derived readers.

virtual bool initializeDirectory(DataSourceDirectory *directory, Error *error) = 0

Updates the directory with all the data provided by this reader.

You must add info about all states, parts, scalars, vectors, displacements and transformation results. The reader will only be asked about data that is in the directory.

Note! This method must be overridden by all derived readers.

Example:

bool initializeDirectory(cee::ug::DataSourceDirectory* directory, Error* error)
{
    directory->setPartInfo(0, cee::ug::PartInfo(1, "First part"));
    directory->setStateInfo(cee::ug::StateInfo(1, "First state", 0.0));
    directory->setResultInfo(cee::ug::ResultInfo(cee::ug::SCALAR, 10, "Per node result", cee::ug::PER_NODE));
    directory->setResultInfo(cee::ug::ResultInfo(cee::ug::VECTOR, 10, "Per node result", cee::ug::PER_NODE));
    directory->setResultInfo(cee::ug::ResultInfo(cee::ug::DISPLACEMENT,10, "Displacement result", cee::ug::PER_NODE));
    directory->setTransformationResult(true);

    return true;
}

virtual bool hasNewGeometryForEachState(size_t globalGeometryIndex) const

Supposed to return true if each state has a separate/different geometry.

The default implementation returns false.

Use this method to specify that one (or more) of your geometries change for each state. This is usually the case if the model is remeshed between each time step.

If only the nodes change but not the topology of the geometry, you should use displacements results as this is much more efficient than re-specifying the whole geometry.

See also

readGeometry

virtual bool readGeometry(int stateId, size_t geometryIndex, DataGeometry *geometry, Error *error) = 0

Reads the geometry from the analysis and fill in the passed geometry.

Use this method to provide the geometry (with all parts) for the given geometry index. If you have overridden the hasNewGeometryForEachState() method and returned true, you will get a call to this method for each new state that is loaded. If not, you will only once get a call to this method.

Note! This method must be overridden by all derived readers.

Advanced example: Two states, two geometries, one remeshed and one constant.

bool readGeometry(int stateId, size_t geometryIndex, DataGeometry* geometry, Error* /*error*/)
{
    cee::PtrRef<cee::ug::DataPart> part = new cee::ug::DataPart(1);
    cee::PtrRef<cee::ug::DataElements> elements = new cee::ug::DataElements(false, 0);
    cee::PtrRef<cee::ug::DataNodes> nodes = new cee::ug::DataNodes(false);
    part->setElements(elements.get());
    part->setNodes(nodes.get());

    if (geometryIndex == 0)
    {
        if (stateId == 1)
        {
            nodes->resize(3);
            nodes->setNode(0, cee::Vec3d(0,0,0));
            nodes->setNode(1, cee::Vec3d(1,0,0));
            nodes->setNode(2, cee::Vec3d(1,1,0));

            std::vector<unsigned int> elementIndices;
            for (unsigned int i = 0; i < 3; ++i) elementIndices.push_back(i);
            elements->addElement(cee::ug::Element::TRIANGLES, elementIndices);
        }
        else
        {
            nodes->resize(4);
            nodes->setNode(0, cee::Vec3d(0,0,0));
            nodes->setNode(1, cee::Vec3d(1,0,0));
            nodes->setNode(2, cee::Vec3d(1,1,0));
            nodes->setNode(3, cee::Vec3d(0,1,0));

            std::vector<unsigned int> elementIndices;
            for (unsigned int i = 0; i < 3; ++i) elementIndices.push_back(i);
            elements->addElement(cee::ug::Element::TRIANGLES, elementIndices);

            elementIndices[1] = 2;
            elementIndices[2] = 3;
            elements->addElement(cee::ug::Element::TRIANGLES, elementIndices);
        }
    }
    else
    {
        nodes->resize(4);
        nodes->setNode(0, cee::Vec3d(0,2,0));
        nodes->setNode(1, cee::Vec3d(1,2,0));
        nodes->setNode(2, cee::Vec3d(1,3,0));
        nodes->setNode(3, cee::Vec3d(1,2,0));

        std::vector<unsigned int> elementIndices;
        for (unsigned int i = 0; i < 4; ++i) elementIndices.push_back(i);
        elements->addElement(cee::ug::Element::QUADS, elementIndices);
    }

    geometry->addPart(part.get());

    return true;
}

virtual bool readScalarResult(int stateId, size_t geometryIndex, int resultId, DataResultScalar *scalarResult, Error *error)

Reads the specified scalar result from the analysis and populate the passed scalarResult.

Use this method to provide the specified scalar result for the given geometry and at the given state. The result mapping provided must match the one specified for this result in initializeDirectory() and the number of result values must match the corresponding geometry.

Advanced example matching the example in readGeometry() (Two states, two geometries, one remeshed and one constant):

bool readScalarResult(int stateId, size_t geometryIndex, int resultId, DataResultScalar* scalarResult, Error* /*error*/)
{
    if (resultId == 10)
    {
        std::vector<double> values;

        if (geometryIndex == 0)
        {
            if (stateId == 1)
            {
                for (size_t i = 0; i < 3; ++i) values.push_back(static_cast<double>(stateId*100 + i));
            }
            else
            {
                for (size_t i = 0; i < 4; ++i) values.push_back(static_cast<double>(stateId*200 + i));
            }
        }
        else
        {
            for (size_t i = 0; i < 4; ++i) values.push_back(static_cast<double>(stateId*300 + i));
        }

        cee::PtrRef<cee::ug::DataPartScalar> part = new cee::ug::DataPartScalar;
        part->setValues(values);
        scalarResult->addPart(part.get());

        return true;
    }

    return false;
}

virtual bool readVectorResult(int stateId, size_t geometryIndex, int resultId, DataResultVector *vectorResult, Error *error)

Reads the specified vector result from the analysis and populate the passed vectorResult.

Use this method to provide the specified vector result for the given geometry and at the given state. The result mapping provided must match the one specified for this result in initializeDirectory() and the number of result values must match the corresponding geometry.

virtual bool readDisplacementResult(int stateId, size_t geometryIndex, int resultId, DataResultDisplacement *displacementResult, Error *error)

Reads the specified displacement result from the analysis and populate the passed vectorResult.

Use this method to provide the specified displacement result for the given geometry and at the given state. The number of results values must match the number of nodes in the corresponding geometry.

Note! The values are the absolute node positions, not just the relative offset to the undisplaced node position.

virtual bool readSymmetricTensorResult(int stateId, size_t geometryIndex, int resultId, DataResultSymmetricTensor *symmetricTensorResult, Error *error)

Reads the specified symmetric tensor result from the analysis and populate the passed symmetricTensorResult.

Use this method to provide the specified symmetric tensor result for the given geometry and at the given state. The result mapping provided must match the one specified for this result in initializeDirectory() and the number of result values must match the corresponding geometry.

virtual bool readTransformationResult(int stateId, size_t geometryIndex, DataResultTransformation *transformationResult, Error *error)

Reads the specified transformation result from the analysis and populate the passed transformationResult.

Use this method to provide per part transformation matrices for the given geometry and state. The number of parts must match the corresponding geometry.

This method will be called if DataSourceDirectory::setTransformationResult(true) is done in initializeDirectory().