.. ## Copyright (c) Lawrence Livermore National Security, LLC and other .. ## Axom Project Contributors. See top-level LICENSE and COPYRIGHT .. ## files for dates and other details. .. ## .. ## SPDX-License-Identifier: (BSD-3-Clause) .. _tutorial-label: ======== Tutorial ======== This short tutorial walks you through the basic usage of the Sina library. For more in-depth details, see the documentation for the individual classes, such as Record, Relationship, and Document. .. contents:: Tutorial Contents :depth: 2 ------------------------------ Creating Documents and Records ------------------------------ The basic working units in Sina are the Document, Record, and Relationship. A Document is a collection of Records and Relationships. A Record contains information about a particular entity, such as the run of an application, or a description of a uncertainty quantification (UQ) study. A Relationship describes how two records relate to each user (e.g. UQ studies contain runs). This first example shows how to create a record: .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin create record] :end-before: //! [end create record] The record has an ID "some_record_id", which is unique to the enclosing document (it will be replaced by a global ID upon ingestion). The only required field for records is their type, which is "my_record_type" in this case. Once created, a record can be added to a Document. We can create Runs. Runs are special types of records. They have the required fields of application ("My Sim Code"), version ("1.2.3"), and user ("jdoe"). The type is automatically set to "run". .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin create run] :end-before: //! [end create run] ----------- Adding Data ----------- Once we have a Record, we can add different types of data to it. Any Datum object that is added will end up in the "data" section of the record in the output file. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin adding data] :end-before: //! [end adding data] ----------------- Adding Curve Sets ----------------- While you can add data items that are vectors of numbers, sometimes you want to express relationships between them. For example, you may want to express the fact that a timeplot captures the fact that there is an independent variable (e.g. "time"), and possibly multiple dependent variables (e.g. "temperature" and "energy"). .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin curve sets] :end-before: //! [end curve sets] ------------ Adding Files ------------ It is also useful to add to a record a set of files that it relates to. For example your application generated some external data, or you want to point to a license file. Conversely, at times it may be necessary to remove a file from the record's file list. For example if the file was deleted or renamed. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin file add_and_remove] :end-before: //! [end file add_and_remove] ----------------------------- Relationships Between Records ----------------------------- Relationships between objects can be captured via the Relationship class. This relates two records via a user-defined predicate. In the example below, a new relashionship is created between two records: a UQ study and a run. The study is said to "contain" the run. As a best practice, predicates should be active verbs, such as "contains" in "the study contains the run", rather than "is part of", as in "the run is part of the study". .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin relationships] :end-before: //! [end relationships] --------------------- Library-Specific Data --------------------- Oftentimes, simulation codes are composed of multiple libraries. If those offer a capability to collect data in a Sina document, you can leverage that to expose this additional data in your records. For example, suppose you are using libraries named ``foo`` and ``bar``. library ``foo`` defines ``foo_collectData()`` like this: .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin library data foo] :end-before: //! [end library data foo] Library ``bar`` defines ``bar_gatherData()`` like this: .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin library data bar] :end-before: //! [end library data bar] In your host application, you can define sections for ``foo`` and ``bar`` to add their own data. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin library data host] :end-before: //! [end library data host] In the example above, once the record is ingested into a Sina datastore, users will be able to search for "temperature" (value = 450), "foo/temperature" (value = 500), and "bar/temperature" (value = 400). ---------------- Input and Output ---------------- Once you have a document, it is easy to save it to a file. To save to a JSON, we run the saveDocument() with the optional argument Protocol set to JSON or set as nothing. Alternatively if you wish to save the document to an HDF5 file: Configure axom for HDF5 support then you can set saveDocument()'s optional Protocol parameter to HDF5. After executing the below, you will output a file named "my_output.json" and a file named "my_output.hdf5", both of which you can ingest into a Sina datastore. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin io write] :end-before: //! [end io write] If needed, you can also load a document from a file. This can be useful, for example, if you wrote a document when writing a restart and you want to continue from where you left off. To load from a JSON file simply run loadDocument() with the optional argument Protocol set to JSON or set as nothing. If you've configured for, and wish to load from an HDF5 simply set the Protocol to HDF5. Note that due to HDF5's handling of '/' as indicators for nested structures, parent nodes will have '/' changed to the ``slashSubstitute`` variable located in ``axom/sina/core/Document.hpp`` as an HDF5 with saveDocument(). loadDocument() will restore them to normal: .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin io read] :end-before: //! [end io read] --------------------------------- Non-Conforming, User-Defined Data --------------------------------- While the Sina format is capable of expressing and indexing lots of different types of data, there may be special cases it doesn't cover well. If you want to add extra data to a record but don't care if it doesn't get indexed, you can add it to the "user_defined" section of records (or libraries of a record). This is a JSON object that will be ignored by Sina for processing purposes, but will be brought back with your record if you retrieve it from a database. Sina uses `Conduit `_ to convert to and from JSON. The user-defined section is exposed as a `Conduit Node `_. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp :start-after: //! [begin user defined] :end-before: //! [end user defined]