Tutorial: My First VTFx

../../../_images/tut_vtfxwriter.png

This tutorial shows how to write your own VTFx file from scratch.

This tutorial describes how to create a minimal VTFx file containing one database with a single cube, one state (i.e. time step) and one case.

Create and configure the File instance

Create the file instance and specify the output filename and the file settings for this file. To keep it simple, just specify an application name and vendor name. See FileSettings for other available settings.

    // Create the VTFx file
    // -------------------------------------------------------------------------
    cee::PtrRef<cee::vtfx::File> file = new cee::vtfx::File;

    // Create VTFx file settings struct
    cee::vtfx::FileSettings fileSettings;
    fileSettings.applicationName = "VTFx: VTFxMinimal";
    fileSettings.vendorName = "My Company AS";

    // Let the API create the VTFx file 
    const cee::Str fileName = "ExampleMinimal.vtfx";
    if (!file->create(fileName, fileSettings))
    {
        return EXIT_FAILURE;
    }

Create database and case

The database collects all data blocks (NodeBlock, ElementBlock, GeometryBlock, …) for a file. The case represents one configuration of the visualization of a VTFx database. Several cases can use the same database but with a different set of properties describing the actual visualization.

A cee::vtfx::File can have several databases and cases, but for this simple example we will only create one database (with id = 1) and one case (with id = 1) referencing the database. The database and case take the file instance as a parameter upon construction.

    // Create a single database (could have many)
    cee::PtrRef<cee::vtfx::Database> database = new cee::vtfx::Database(file.get(), "Only Database", 1, cee::vtfx::Database::SIMULATION_TYPE_STRUCTURAL, cee::vtfx::Database::SOLUTION_TYPE_TRANSIENT, "Database description"); // database id 1

    // Create a single case (could have many)
    cee::PtrRef<cee::vtfx::Case> singleCase = new cee::vtfx::Case(file.get(), "Only Case", 1, 1); // case id 1 using database with id 1

Add nodes

The node block is a block that stores node coordinates. Node ids can also be specified, but for this simple tutorial, we will only be using node indexes.

Create the node block (with id = 1) with withNodeIds set to false.

This part is a cube so we add eight nodes. The node coordinates can either be added as an array of cee::Vec3f or as an interleaved float array (x,y,z,x,y,z,x,y,z,…).

When done, we write this block to the database instance.

    // Create the node block
    cee::PtrRef<cee::vtfx::NodeBlock> nodeBlock = new cee::vtfx::NodeBlock(1, false); // node block id 1, not using node ids

    // Set the node data (Note: The VTFx component expects interleaved node coordinates (xyzxyzxyz...))
    const float NODES_PART[] =
    {
        0.0f, 0.0f, 0.0f,
        1.0f, 0.0f, 0.0f,
        1.0f, 1.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f,
        1.0f, 0.0f, 1.0f,
        1.0f, 1.0f, 1.0f,
        0.0f, 1.0f, 1.0f
    };

    std::vector<float> nodesPart(NODES_PART, NODES_PART + sizeof(NODES_PART) / sizeof(NODES_PART[0]));
    if (!nodeBlock->setNodes(nodesPart))
    {
        return EXIT_FAILURE;
    }

Add elements

An element block describes the connectivity table for the elements. The nodes in the table can be referred to by index or id.

First we create an element block (with id = 1). Since we created the node block above without using ids, we will not reference nodes by ids so we set referNodesByIds in the constructor to false. The node block used to define the elements is specified by setNodeBlockId(1) which is the node block id.

This tutorial only has one hexahedron element. An element is described by the nodes that define it. For a single hexahedron element, it takes eight nodes. (See ElementBlock::ElementType or a full list of supported element types.)

    // Create the element block
    cee::PtrRef<cee::vtfx::ElementBlock> elementBlock = new cee::vtfx::ElementBlock(1, false, false); // element block id 1, not using element ids, referring nodes by index
    elementBlock->setNodeBlockId(1); // Use node block with id = 1

    // Set the element data
    const int CONNECTS_PART[] =
    {
        0, 1, 2, 3, 4, 5, 6, 7
    };

    std::vector<int> elementNodes(CONNECTS_PART, CONNECTS_PART + sizeof(CONNECTS_PART) / sizeof(CONNECTS_PART[0]));
    if (!elementBlock->addElements(cee::vtfx::ElementBlock::HEXAHEDRONS, elementNodes))
    {
        return EXIT_FAILURE;
    }

    // Write the element block
    if (!database->writeBlock(elementBlock.get()))
    {
        return EXIT_FAILURE;
    }

Add geometry

The geometry block bundles all elements contained in this geometry.

First, create the geometry block. Each database can - and must - have only one geometry block. In the GeometryBlock constructor, set number of geometries per state to 1. In most cases, a single geometry containing all element blocks will fulfill your needs.

Add the elements to the geometry block by using the element block ids. For this file, there’s only one element block (with id = 1), itself containing a single element. Specify an id (for instance, id = 1) for the part that uses this element block. A part can only contain one element block.

You also need to add meta data about the geometry using a geometry info block. Create the geometry info block and set the geometries per state to 1 (same as for the GeometryBlock above). Add a meta data for a part specifying part id used in the geometry block and a part name.

    // Create geometry block
    cee::PtrRef<cee::vtfx::GeometryBlock> geoBlock = new cee::vtfx::GeometryBlock(1); // Only one geometry per state

    size_t geoIndex = 0;
    int partId = 1;

    // Set to use element block with id 1
    if (!geoBlock->addElementBlock(geoIndex, 1, partId)) // Element block with id = 1
    {
        return EXIT_FAILURE;
    }

    // Write the geometry block
    if (!database->writeBlock(geoBlock.get()))
    {
        return EXIT_FAILURE;
    }

    // Create geometry info block
    cee::PtrRef<cee::vtfx::GeometryInfoBlock> infoBlock = new cee::vtfx::GeometryInfoBlock(1); // Only one geometry per state
    infoBlock->addPartInfo(geoIndex, partId, cee::Str("Only part"));

    // Write the geometry info block
    if (!database->writeBlock(infoBlock.get()))
    {
        return EXIT_FAILURE;
    }

Add a state

Create a StateInfoBlock containing the meta data of states. There must be only one state info block in each database.

Add meta data for single state with id = 1 and give it a suitable name, reference value and define the reference value type.

    // Create state info block
    cee::PtrRef<cee::vtfx::StateInfoBlock> stepInfo = new cee::vtfx::StateInfoBlock;
    if (!stepInfo->addStateInfo(1, "Only step", 42.0f, cee::vtfx::StateInfoBlock::TIME))
    {
        return EXIT_FAILURE;
    }

    // Write the state info block
    if (!database->writeBlock(stepInfo.get()))
    {
        return EXIT_FAILURE;
    }

Finally, close the file and it will be written to disk.

    // Finally close the file
    if (!file->close())
    {
        return EXIT_FAILURE;
    }

If any error occurred when writing the file, a log message should have been created. Check the log for further information on what went wrong. See the Logging page for more information on how to set up logging.

See the complete source code here:

Minimal VTFx File