Documents¶
Sina Document
objects are a way to represent the top-level object of a
JSON file that conforms to the Sina schema. When serialized, these documents
can be ingested into a Sina database and used with the Sina tool.
Document
objects follow a very basic JSON layout consisting of two entries:
records
and relationships
. Each of these entries will store a list of their
respective objects. An example of an empty document is shown below:
{
"records": [],
"relationships": []
}
The records
list can contain Record
objects and their inheritying types,
such as Run
objects. The relationships
list can contain Relationship
objects. For more information on these objects, see Records
and Relationships.
Assembling Documents¶
Document
objects can be assembled programatically. To accomplish this:
Create a new instance of the
Document
classCreate a
Record
Add the instance of the
Record
with theadd
method
On the Sina C++ User Guide page, you can see an example of this
process. Below we will expand on this example to add a Relationship
:
// Copyright (c) 2017-2025, Lawrence Livermore National Security, LLC and
// other Axom Project Developers. See the top-level LICENSE file for details.
//
// SPDX-License-Identifier: (BSD-3-Clause)
#include "axom/config.hpp"
#include "axom/sina.hpp"
int main(void)
{
// Create a new document
axom::sina::Document document;
// Create a record of this specific study
// This study has an ID of "study1", which has to be unique to this file
axom::sina::ID studyID {"study1", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> study {new axom::sina::Record {studyID, "UQ study"}};
// Create a run of "My Sim Code" version "1.2.3", which was run by "jdoe".
// The run has an ID of "run1", which has to be unique to this file.
axom::sina::ID runID {"run1", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> run {
new axom::sina::Run {runID, "My Sim Code", "1.2.3", "jdoe"}};
// Create a relationship between the study and the run
// Here we're saying that the study contains the run
axom::sina::Relationship relationship {studyID, "contains", runID};
// Add the run, study record, and relationship to the document
document.add(std::move(run));
document.add(std::move(study));
document.add(relationship);
// Save the document directly to a file.
// since we gave saveDocument no optional protocol parameter, it will default to JSON
axom::sina::saveDocument(document, "MySinaData.json");
#ifdef AXOM_USE_HDF5
// We will also save a copy of the document as an HDF5 file
// which can be done by passing the protocol as HDF5
axom::sina::saveDocument(document, "MySinaData.hdf5", axom::sina::Protocol::HDF5);
#endif
}
After executing the above code, the resulting MySinaData.json
file will
look like so:
{
"records": [
{
"type": "run",
"local_id": "run1",
"application": "My Sim Code",
"version": "1.2.3",
"user": "jdoe"
},
{
"type": "UQ study",
"local_id": "study1"
}
],
"relationships": [
{
"predicate": "contains",
"local_subject": "study1",
"local_object": "run1"
}
]
}
Generating Documents From JSON¶
Alternatively to assembling Document
instances programatically, it is
also possible to generate Document
objects from existing JSON files
or JSON strings.
Using our same example from the previous section, if we instead had the
MySinaData.json
file prior to executing our code, we could generate
the document using Sina’s loadDocument()
function:
#include "axom/sina.hpp"
int main (void) {
axom::sina::Document myDocument = axom::sina::loadDocument("MySinaData.json");
}
Similarly, if we had JSON in string format we could also load an instance
of the Document
that way:
#include "axom/sina.hpp"
int main (void) {
std::string my_json = "{\"records\":[{\"type\":\"run\",\"id\":\"test\"}],\"relationships\":[]}";
axom::sina::Document myDocument = axom::sina::Document(my_json, axom::sina::createRecordLoaderWithAllKnownTypes());
std::cout << myDocument.toJson() << std::endl;
}
Generating Documents From HDF5¶
In addition to assembling Document
instances from existing JSON files, it
is possible to generate Document
objects from existing HDF5 files using
Conduit.
When Axom is configured with HDF5 support, Sina’s saveDocument()
and loadDocument()
functions support HDF5 assembly through the Protocol::HDF5 argument. The functions will
throw a runtime error with the list of available types in Axom configurations if Protocol::HDF5
is attempted without HDF5 support.
#include "axom/sina.hpp"
int main (void) {
axom::sina::Document myDocument = axom::sina::loadDocument("MySinaData.hdf5", axom::sina::Protocol::HDF5);
}
Obtaining Records & Relationships from Existing Documents¶
Sina provides an easy way to query for both Record
and Relationship
objects that are associated with a Document
. The getRecords()
and
getRelationships()
methods will handle this respectively.
Below is an example showcasing their usage:
// Copyright (c) 2017-2025, Lawrence Livermore National Security, LLC and
// other Axom Project Developers. See the top-level LICENSE file for details.
//
// SPDX-License-Identifier: (BSD-3-Clause)
#include "axom/sina.hpp"
#include "axom/slic.hpp"
int main(void)
{
// Initialize slic
axom::slic::initialize();
// Create a new document
axom::sina::Document document;
// Create a record of this specific study
// This study has an ID of "study1", which has to be unique to this file
axom::sina::ID studyID {"study1", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> study {new axom::sina::Record {studyID, "UQ study"}};
// Create a run of "My Sim Code" version "1.2.3", which was run by "jdoe".
// The run has an ID of "run1", which has to be unique to this file.
axom::sina::ID runID {"run1", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> run {
new axom::sina::Run {runID, "My Sim Code", "1.2.3", "jdoe"}};
// Create a relationship between the study and the run
// Here we're saying that the study contains the run
axom::sina::Relationship relationship {studyID, "contains", runID};
// Add the run, study record, and relationship to the document
document.add(std::move(run));
document.add(std::move(study));
document.add(relationship);
// Query for a list of records and relationships
auto &records = document.getRecords();
auto &relationships = document.getRelationships();
SLIC_ASSERT_MSG(records.size() == 2, "Unexpected number of records found.");
std::cout << "Number of Records: " << records.size() << std::endl;
SLIC_ASSERT_MSG(relationships.size() == 1, "Unexpected number of relationships found.");
std::cout << "Number of Relationships: " << relationships.size() << std::endl;
// Finalize slic
axom::slic::finalize();
}
Running this will show that both records and the one relationship were properly queried:
Number of Records: 2
Number of Relationships: 1