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
Recordmight 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) 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)
#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) 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)
#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) 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)
#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) 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)
#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) 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)
#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) 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)
#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) 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)
#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
Runversion: the version of the application used to create the
Runuser: 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"}};
}