Records¶
Sina Record
objects are used to represent the data to be stored for your
study. Some examples for the natural scope of Record
objects include things
like:
a single run of an application
a Maestro step
a cluster of runs that has some metadata attached to the cluster (this
Record
might have a “contains” Relationship for all the runs within it)
Each Record
must have an ID and a type that you define. Additionally, Each
Record
can have a list of File
objects and a list of Datum
objects.
Datums¶
Sina Datum
objects help track the value and (optionally) tags and/or units of a
value associated with a Record
. In the Sina schema, a Datum
always belongs
to a Record
or one of Record
’s inheriting types.
Some examples of potential Datum
values would be:
a scalar
a piece of metadata
an input parameter
The value of a Datum
may be a string, a double, an array of strings, or an array
of doubles.
Below showcases an example of creating an instance of Datum
with an array of
strings and adding it to a Record
:
// 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 the record
axom::sina::ID myID {"my_record", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> myRecord {new axom::sina::Record {myID, "my_type"}};
// Create the datum with an array of strings
std::vector<std::string> myTags {"input"};
axom::sina::Datum myDatum {myTags};
// Add the datum to the record
myRecord->add("my_scalar", std::move(myDatum));
// Compare the actual output to the expected JSON output, then print it to console
std::string actualJsonString = myRecord->toNode().to_json();
std::string expectedJsonString = R"(
{
"data":
{
"my_scalar":
{
"value":
[
"input"
]
}
},
"type": "my_type",
"local_id": "my_record"
})";
SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0,
"JSON output does not match expected structure.");
std::cout << actualJsonString << std::endl;
// Finalize slic
axom::slic::finalize();
}
Once executed, this code will output:
{
"data":
{
"my_scalar":
{
"value":
[
"input"
]
}
},
"type": "my_type",
"local_id": "my_record"
}
Checking Datum Type¶
It’s possible to check the type of a Datum
with the getType()
method. Types
are tracked in an enumeration called ValueType
. The enumeration is as follows:
0: string
1: scalar
2: array of strings
3: array of scalars
Below is an example of this in action:
// 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"
using ValueTypeUnderlying = typename std::underlying_type<axom::sina::ValueType>::type;
void printType(axom::sina::Datum datum, std::string datumName, const std::string& errMsg)
{
auto datumType = static_cast<ValueTypeUnderlying>(datum.getType());
SLIC_ASSERT_MSG(static_cast<bool>(std::is_same<decltype(datumType), ValueTypeUnderlying>::value),
errMsg);
AXOM_UNUSED_VAR(errMsg);
std::cout << datumName << " type: " << datumType << std::endl;
}
int main(void)
{
// Initialize slic
axom::slic::initialize();
// Define 3 different datums
axom::sina::Datum myDatum {12.34};
std::string value = "foobar";
axom::sina::Datum myOtherDatum {value};
std::vector<double> scalars = {1, 2, 20.0};
axom::sina::Datum myArrayDatum {scalars};
// Prints 1, corresponding to Scalar
printType(myDatum,
"myDatum",
"myDatumType did not match the expected type 'Scalar' (numerically "
"represented as 1).");
// Prints 0, corresponding to String
printType(myOtherDatum,
"myOtherDatum",
"myDatumType did not match the expected type 'String' (numerically "
"represented as 0).");
// Prints 3, corresponding to ScalarArray
printType(myArrayDatum,
"myArrayDatum",
"myArrayDatum did not match the expected type 'ScalarArray' "
"(numerically represented as 3).");
// Finalize slic
axom::slic::finalize();
}
Viewing Datum From an existing Record¶
Sometimes it’s necessary to obtain the current Datum
instances from an existing
Record
. To do this, you can utilize the Record
object’s getData
method.
This method will return an unordered map of Datum
instances.
Below is an example of this process:
// 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"
int main(void)
{
// Define 3 different datums
axom::sina::Datum myDatum {12.34};
std::string value = "foobar";
axom::sina::Datum myOtherDatum {value};
std::vector<double> scalars = {1, 2, 20.0};
axom::sina::Datum myArrayDatum {scalars};
// Create a record to store the datum
axom::sina::ID myID {"my_record", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> myRecord {new axom::sina::Record {myID, "my_type"}};
// Add the datum instances to the record
myRecord->add("datum1", std::move(myDatum));
myRecord->add("datum2", std::move(myOtherDatum));
myRecord->add("datum3", std::move(myArrayDatum));
// Query the datum from the record
auto& data = myRecord->getData();
// Print the keys and type of datum
for(const auto& pair : data)
{
std::cout << pair.first << " is type: "
<< static_cast<std::underlying_type<axom::sina::ValueType>::type>(pair.second.getType())
<< std::endl;
}
}
Executing this code will print out:
datum1 is type: 1
datum2 is type: 0
datum3 is type: 3
Which, we know from Checking Datum Type, signifies that datum1 is a scalar, datum2 is a string, and datum3 is an array of scalars.
Using this knowledge we can modify our code to show us the current datum values:
// 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();
// Define 3 different datums
double scalarValue = 12.34;
axom::sina::Datum myDatum {scalarValue};
std::string stringValue = "foobar";
axom::sina::Datum myOtherDatum {stringValue};
std::vector<double> scalarArrayValue = {1, 2, 20.0};
axom::sina::Datum myArrayDatum {scalarArrayValue};
// Create a record to store the datum
axom::sina::ID myID {"my_record", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> myRecord {new axom::sina::Record {myID, "my_type"}};
// Add the datum instances to the record
myRecord->add("datum1", std::move(myDatum));
myRecord->add("datum2", std::move(myOtherDatum));
myRecord->add("datum3", std::move(myArrayDatum));
// Query the datum
auto& data = myRecord->getData();
// Print the datum values
double datum1Val = data.at("datum1").getScalar();
SLIC_ASSERT_MSG(datum1Val == scalarValue, "Data stored in record at 'datum1' is unexpected.");
std::cout << "datum1: " << datum1Val << std::endl;
std::string datum2Val = data.at("datum2").getValue();
SLIC_ASSERT_MSG(datum2Val == stringValue, "Data stored in record at 'datum2' is unexpected.");
std::cout << "datum2: " << datum2Val << std::endl;
std::vector<double> datum3Val = data.at("datum3").getScalarArray();
SLIC_ASSERT_MSG(datum3Val == scalarArrayValue, "Data stored in record at 'datum3' is unexpected.");
std::cout << "datum3: ";
for(const auto& value : datum3Val)
{
std::cout << value << " ";
}
std::cout << std::endl;
// Finalize slic
axom::slic::finalize();
}
This will provide the following output:
datum1: 12.34
datum2: foobar
datum3: 1 2 20
Files¶
Sina File
objects help track the location (URI) and mimetype of a file on the
file system, plus any tags. In the Sina schema, a File
always belongs to a Record
or one of Record
’s inheriting types.
Every File
must have a URI, while mimetype and tags are optional.
Below is an example showcasing how to create a file and add it to a record:
// 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 2 different files
axom::sina::File myFile {"/path/to/file.png"};
myFile.setMimeType("image/png");
axom::sina::File myOtherFile {"/path/to/other/file.txt"};
myOtherFile.setTags({"these", "are", "tags"});
// Create a record to store the files
axom::sina::ID myID {"my_record", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> myRecord {new axom::sina::Record {myID, "my_type"}};
// Add the files to the record
myRecord->add(myFile);
myRecord->add(myOtherFile);
// Compare the actual output to the expected JSON output, then print it to console
std::string actualJsonString = myRecord->toNode().to_json();
std::string expectedJsonString = R"(
{
"type": "my_type",
"local_id": "my_record",
"files":
{
"/path/to/other/file.txt":
{
"tags":
[
"these",
"are",
"tags"
]
},
"/path/to/file.png":
{
"mimetype": "image/png"
}
}
})";
SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0,
"JSON output does not match expected structure.");
std::cout << actualJsonString << std::endl;
// Finalize slic
axom::slic::finalize();
}
This code will produce the following output:
{
"type": "my_type",
"local_id": "my_record",
"files":
{
"/path/to/other/file.txt":
{
"tags":
[
"these",
"are",
"tags"
]
},
"/path/to/file.png":
{
"mimetype": "image/png"
}
}
}
Similarly, files can be removed from a Record
with the remove()
method:
// 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 2 different files
axom::sina::File myFile {"/path/to/file.png"};
myFile.setMimeType("image/png");
axom::sina::File myOtherFile {"/path/to/other/file.txt"};
myOtherFile.setTags({"these", "are", "tags"});
// Create a record to store the files
axom::sina::ID myID {"my_record", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> myRecord {new axom::sina::Record {myID, "my_type"}};
// Add the files to the record
myRecord->add(myFile);
myRecord->add(myOtherFile);
// Remove a file from the record
myRecord->remove(myFile);
// Compare the actual output to the expected JSON output, then print it to console
std::string actualJsonString = myRecord->toNode().to_json();
std::string expectedJsonString = R"(
{
"type": "my_type",
"local_id": "my_record",
"files":
{
"/path/to/other/file.txt":
{
"tags":
[
"these",
"are",
"tags"
]
}
}
})";
SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0,
"JSON output does not match expected structure.");
std::cout << actualJsonString << std::endl;
// Finalize slic
axom::slic::finalize();
}
As we see from the output, the contents of myFile
are no longer in the
Record
instance:
{
"type": "my_type",
"local_id": "my_record",
"files":
{
"/path/to/other/file.txt":
{
"tags":
[
"these",
"are",
"tags"
]
}
}
}
Viewing Files From an Existing Record¶
Sometimes it’s necessary to view the current File
instances that are stored in
a Record
. This can be accomplished by using the Record
object’s getFiles()
method which returns an unordered map of File
instances.
Below is an expansion of the previous example where we query the Record
instance
for files:
// 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"
int main(void)
{
// Create 2 different files
axom::sina::File myFile {"/path/to/file.png"};
myFile.setMimeType("image/png");
axom::sina::File myOtherFile {"/path/to/other/file.txt"};
myOtherFile.setTags({"these", "are", "tags"});
// Create a record to store the files
axom::sina::ID myID {"my_record", axom::sina::IDType::Local};
std::unique_ptr<axom::sina::Record> myRecord {new axom::sina::Record {myID, "my_type"}};
// Add the files to the record
myRecord->add(myFile);
myRecord->add(myOtherFile);
// Query the record for files
auto& files = myRecord->getFiles();
for(const auto& file : files)
{
std::cout << "File with URI '" << file.getUri() << "' has mimetype '" << file.getMimeType()
<< "' and tags '";
for(const auto& tag : file.getTags())
{
std::cout << tag << " ";
}
std::cout << "'" << std::endl;
}
}
The above code will output:
File with URI '/path/to/file.png' has mimetype 'image/png' and tags ''
File with URI '/path/to/other/file.txt' has mimetype '' and tags 'these are tags '
Runs¶
Sina Run
objects are a subtype of Sina Record
objects corresponding to
a single run of an application as specified in the Sina schema. Similar to Record
objects, Run
objects also require an ID; however, they do not require a type as
it will automatically be set to “run”. In addition to IDs, there are a few other
required fields:
application: the application/code used to create the
Run
version: the version of the application used to create the
Run
user: the username of the person who ran the application that generated this
Run
The below code shows an example of how a Run
can be created:
#include "axom/sina.hpp"
int main(void) {
// Create the ID first
axom::sina::ID run1ID{"run1", axom::sina::IDType::Local};
// Create a run with:
// ID: run1ID
// application: "My Sim Code"
// version: "1.2.3"
// user: "jdoe"
std::unique_ptr<sina::Record> run1{new sina::Run{run1ID, "My Sim Code", "1.2.3", "jdoe"}};
}