From 89413c64ad3bddd1f6dda9f9145465f2c561d4fd Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 24 Apr 2024 13:51:24 -0700 Subject: [PATCH 01/60] establish directory structure and copy/paste headers/sources --- src/axom/sina/include/sina/AdiakWriter.hpp | 46 ++++ src/axom/sina/include/sina/ConduitUtil.hpp | 109 ++++++++ src/axom/sina/include/sina/CppBridge.hpp | 33 +++ src/axom/sina/include/sina/Curve.hpp | 114 +++++++++ src/axom/sina/include/sina/CurveSet.hpp | 103 ++++++++ src/axom/sina/include/sina/DataHolder.hpp | 164 ++++++++++++ src/axom/sina/include/sina/Datum.hpp | 190 ++++++++++++++ src/axom/sina/include/sina/Document.hpp | 185 ++++++++++++++ src/axom/sina/include/sina/File.hpp | 100 ++++++++ src/axom/sina/include/sina/ID.hpp | 137 ++++++++++ src/axom/sina/include/sina/Record.hpp | 212 ++++++++++++++++ src/axom/sina/include/sina/Relationship.hpp | 130 ++++++++++ src/axom/sina/include/sina/Run.hpp | 91 +++++++ src/axom/sina/include/sina/sina.hpp | 17 ++ src/axom/sina/src/AdiakWriter.cpp | 262 ++++++++++++++++++++ src/axom/sina/src/ConduitUtil.cpp | 140 +++++++++++ src/axom/sina/src/Curve.cpp | 57 +++++ src/axom/sina/src/CurveSet.cpp | 97 ++++++++ src/axom/sina/src/DataHolder.cpp | 128 ++++++++++ src/axom/sina/src/Datum.cpp | 167 +++++++++++++ src/axom/sina/src/Document.cpp | 135 ++++++++++ src/axom/sina/src/File.cpp | 63 +++++ src/axom/sina/src/ID.cpp | 55 ++++ src/axom/sina/src/Record.cpp | 105 ++++++++ src/axom/sina/src/Relationship.cpp | 37 +++ src/axom/sina/src/Run.cpp | 45 ++++ 26 files changed, 2922 insertions(+) create mode 100644 src/axom/sina/include/sina/AdiakWriter.hpp create mode 100644 src/axom/sina/include/sina/ConduitUtil.hpp create mode 100644 src/axom/sina/include/sina/CppBridge.hpp create mode 100644 src/axom/sina/include/sina/Curve.hpp create mode 100644 src/axom/sina/include/sina/CurveSet.hpp create mode 100644 src/axom/sina/include/sina/DataHolder.hpp create mode 100644 src/axom/sina/include/sina/Datum.hpp create mode 100644 src/axom/sina/include/sina/Document.hpp create mode 100644 src/axom/sina/include/sina/File.hpp create mode 100644 src/axom/sina/include/sina/ID.hpp create mode 100644 src/axom/sina/include/sina/Record.hpp create mode 100644 src/axom/sina/include/sina/Relationship.hpp create mode 100644 src/axom/sina/include/sina/Run.hpp create mode 100644 src/axom/sina/include/sina/sina.hpp create mode 100644 src/axom/sina/src/AdiakWriter.cpp create mode 100644 src/axom/sina/src/ConduitUtil.cpp create mode 100644 src/axom/sina/src/Curve.cpp create mode 100644 src/axom/sina/src/CurveSet.cpp create mode 100644 src/axom/sina/src/DataHolder.cpp create mode 100644 src/axom/sina/src/Datum.cpp create mode 100644 src/axom/sina/src/Document.cpp create mode 100644 src/axom/sina/src/File.cpp create mode 100644 src/axom/sina/src/ID.cpp create mode 100644 src/axom/sina/src/Record.cpp create mode 100644 src/axom/sina/src/Relationship.cpp create mode 100644 src/axom/sina/src/Run.cpp diff --git a/src/axom/sina/include/sina/AdiakWriter.hpp b/src/axom/sina/include/sina/AdiakWriter.hpp new file mode 100644 index 0000000000..b3d2ceaec8 --- /dev/null +++ b/src/axom/sina/include/sina/AdiakWriter.hpp @@ -0,0 +1,46 @@ +#ifndef SINA_ADIAK_HPP +#define SINA_ADIAK_HPP + +/// @file + +#include "sina/config.hpp" +#if SINA_BUILD_ADIAK_BINDINGS + +#include +#include + +#include "sina/ConduitUtil.hpp" +#include "sina/Record.hpp" +#include "sina/Run.hpp" + +extern "C" { +#include "adiak_tool.h" +} + +namespace sina { + +/** + * The callback function to pass to Adiak in order to write collected data to a Sina Record. + * + * To register this with Adiak, you should call adiak_register_cb, passing in + * a pointer to the record as the last value. Your code should look something + * like this: + * + * \code + * Record record{ID{"my_id", IDType::Local}, "my_record_type"}; + * adiak_register_cb(1, adiak_category_all, adiakSinaCallback, 0, &record); + * \endcode + * + * Note that not everything that Sina can capture an be captured through the + * Adiak API. For example, there is currently no support in Adiak to capture + * anything like a CurveSet. As a result, to do that, you must hold on to + * the Record object passed here as the opaque value and manipulate it directly. + **/ +void adiakSinaCallback(const char *name, adiak_category_t category, const char *subcategory, adiak_value_t *value, adiak_datatype_t *t, void *opaque_value); + +} + +#endif // SINA_BUILD_ADIAK_BINDINGS + +#endif // SINA_ADIAK_HPP + diff --git a/src/axom/sina/include/sina/ConduitUtil.hpp b/src/axom/sina/include/sina/ConduitUtil.hpp new file mode 100644 index 0000000000..faf79c4625 --- /dev/null +++ b/src/axom/sina/include/sina/ConduitUtil.hpp @@ -0,0 +1,109 @@ +#ifndef SINA_JSONUTIL_HPP +#define SINA_JSONUTIL_HPP + +/** + * @file + * + * This file contains utility methods to make working with conduit + * easier. The functions here also include error reporting with user-friendly + * messages. + */ + +#include +#include + +#include "conduit.hpp" + +namespace sina { + +/** + * Get a required field from a conduit Node. + * + * @param fieldName the name of the field to get + * @param parent the parent object from which to get the field + * @param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * @return the requested field as a Node + * @throws std::invalid_argument if the field does not exist + */ +conduit::Node const &getRequiredField(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType); + +/** + * Get the value of a required field from a conduit Node. The field value + * must be a string. + * + * @param fieldName the name of the field to get + * @param parent the parent object from which to get the field + * @param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * @return the value of the requested field + * @throws std::invalid_argument if the field does not exist or is not a string + */ +std::string getRequiredString(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType); + +/** + * Get the value of a required field from a conduit Node. The field value + * must be a double. + * + * @param fieldName the name of the field to get + * @param parent the parent object from which to get the field + * @param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * @return the value of the requested field + * @throws std::invalid_argument if the field does not exist or is not a double + */ +double getRequiredDouble(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType); + +/** + * Get the value of an optional field from a conduit Node. The field value + * must be a string if it is present. + * + * @param fieldName the name of the field to get + * @param parent the parent object from which to get the field + * @param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * @return the value of the requested field, or an empty string if it + * does not exist + * @throws std::invalid_argument if the field exists but is not a string + */ +std::string getOptionalString(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType); + +/** + * Convert the given node to a vector of doubles. + * + * @param node the node to convert + * @param name the name of the node, used in error reporting + * @return the node as a list of doubles + * @throws std::invalid_argument if the node is not a list of doubles + */ +std::vector toDoubleVector(conduit::Node const &node, + std::string const &name); + +/** + * Convert the given node to a vector of strings. + * + * @param node the node to convert + * @param name the name of the node, used in error reporting + * @return the node as a list of strings + * @throws std::invalid_argument if the node is not a list of strings + */ +std::vector toStringVector(conduit::Node const &node, + std::string const &name); + +/** + * Add a vector of strings to a Node. This operation's not natively + * part of Conduit. + * + * @param parent the node to add the strings to + * @param child_name the name of the child (aka the name of the field) + * @param string_values the data values for the field + */ +void addStringsToNode(conduit::Node &parent, const std::string &child_name, + std::vector const &string_values); +} + +#endif //SINA_JSONUTIL_HPP diff --git a/src/axom/sina/include/sina/CppBridge.hpp b/src/axom/sina/include/sina/CppBridge.hpp new file mode 100644 index 0000000000..4322ffc4ef --- /dev/null +++ b/src/axom/sina/include/sina/CppBridge.hpp @@ -0,0 +1,33 @@ +#ifndef SINA_CPPBRIDGE_HPP +#define SINA_CPPBRIDGE_HPP + +/** + * @file + * + * This file contains functions which can go away once this library is + * migrated to C++14 or later. + */ + +#include +#include + +namespace sina { namespace internal { + +/** + * Make a unique pointer pointing to a new object. + * + * Delete when switch to C++14 in favor of std::make_unique. + * + * @tparam T the type of the object + * @tparam ParamTypes the types of the parameters + * @param params the parameters to the constructor + * @return a unique_ptr to the newly-constructed object + */ +template +std::unique_ptr make_unique(ParamTypes &&... params) { + return std::unique_ptr(new T(std::forward(params)...)); +}; + +}} // namespaces + +#endif //SINA_CPPBRIDGE_HPP diff --git a/src/axom/sina/include/sina/Curve.hpp b/src/axom/sina/include/sina/Curve.hpp new file mode 100644 index 0000000000..71cd5024ba --- /dev/null +++ b/src/axom/sina/include/sina/Curve.hpp @@ -0,0 +1,114 @@ +#ifndef SINA_CURVE_HPP +#define SINA_CURVE_HPP + +/** + * @file + * + * Contains the definition of the Curve class. + */ + +#include +#include + +#include "conduit.hpp" + +namespace sina { + +/** + * A Curve represents a 1-dimensional curve inside a \see CurveSet. + */ +class Curve { +public: + /** + * Create a Curve with the given name and values + * + * @param name the name of the curve + * @param values the curve's values + */ + Curve(std::string name, std::vector values); + + /** + * Create a Curve with the given name and values + * + * @param name the name of the curve + * @param values the curve's values + * @param numValues the number of values. + */ + Curve(std::string name, double const *values, std::size_t numValues); + + /** + * Create a Curve by deserializing a conduit node. + * + * @param name the name of the curve + * @param curveAsNode the serialized version of a curve + */ + Curve(std::string name, conduit::Node const &curveAsNode); + + /** + * Get the curve's name. + * + * @return the curve's name + */ + std::string const &getName() const { + return name; + } + + /** + * Get the values of the curve. + * + * @return the curve's values + */ + std::vector const &getValues() const { + return values; + } + + /** + * Set the units of the values. + * + * @param units the value's units + */ + void setUnits(std::string units); + + /** + * Get the units of the values. + * + * @return the value's units + */ + std::string const &getUnits() const { + return units; + } + + /** + * Set the tags for this curve. + * + * @param tags the curve's tags + */ + void setTags(std::vector tags); + + /** + * Get the tags for this curve. + * + * @return the curve's tags + */ + std::vector const &getTags() const { + return tags; + } + + /** + * Convert this curve to a Conduit node. + * + * @return a Conduit representation of this curve + */ + conduit::Node toNode() const; + +private: + std::string name; + std::vector values; + std::string units; + std::vector tags; +}; + +} + + +#endif //SINA_CURVE_HPP diff --git a/src/axom/sina/include/sina/CurveSet.hpp b/src/axom/sina/include/sina/CurveSet.hpp new file mode 100644 index 0000000000..33831c3946 --- /dev/null +++ b/src/axom/sina/include/sina/CurveSet.hpp @@ -0,0 +1,103 @@ +#ifndef SINA_CURVESET_HPP +#define SINA_CURVESET_HPP + +/** + * @file + * + * Contains the definition of the CurveSet class. + */ + +#include +#include + +#include "conduit.hpp" + +#include "sina/Curve.hpp" + +namespace sina { + +/** + * A CurveSet represents an entry in a record's "curve_set". + * + * A CurveSet consist of a set of independent and dependent curves. Each curve + * is a list of numbers along with optional units and tags. + * + * \see Record + * \see Curve + */ +class CurveSet { +public: + using CurveMap = std::unordered_map; + + /** + * Create a CurveSet with the given name + * + * @param name the name of the CurveSet + */ + explicit CurveSet(std::string name); + + /** + * Create a CurveSet from the given Conduit node. + * + * @param name the name of the CurveSet + * @param node the Conduit node representing the CurveSet + */ + CurveSet(std::string name, conduit::Node const &node); + + /** + * Get the name of the this CurveSet. + * + * @return the curve set's name + */ + std::string const & getName() const { + return name; + } + + /** + * Add an independent curve. + * + * @param curve the curve to add + */ + void addIndependentCurve(Curve curve); + + /** + * Add a dependent curve. + * + * @param curve the curve to add + */ + void addDependentCurve(Curve curve); + + /** + * Get a map of all the independent curves. + * + * @return a map of all the independent curves + */ + CurveMap const &getIndependentCurves() const { + return independentCurves; + } + + /** + * Get a map of all the dependent curves. + * + * @return a map of all the dependent curves + */ + CurveMap const &getDependentCurves() const { + return dependentCurves; + } + + /** + * Convert his CurveSet to a Conduit node. + * + * @return the Node representation of this CurveSet + */ + conduit::Node toNode() const; + +private: + std::string name; + CurveMap independentCurves; + CurveMap dependentCurves; +}; + +} + +#endif //SINA_CURVESET_HPP diff --git a/src/axom/sina/include/sina/DataHolder.hpp b/src/axom/sina/include/sina/DataHolder.hpp new file mode 100644 index 0000000000..9a4cd71f48 --- /dev/null +++ b/src/axom/sina/include/sina/DataHolder.hpp @@ -0,0 +1,164 @@ +#ifndef SINA_DATAHOLDER_HPP +#define SINA_DATAHOLDER_HPP + +/** + * @file + * + * Contains the definition of the DataHolder class. + */ + +#include +#include +#include + +#include "conduit.hpp" + +#include "sina/Datum.hpp" +#include "sina/CurveSet.hpp" + +namespace sina { + +/** + * A DataHolder is a basic container for certain types of information. + * + * DataHolders contain curves, libraries, and data (\see Datum), and represent + * all the information a library can have associated with it. Records expand + * on DataHolders to contain additional info. + * + * \see Record + * \see LibraryData + */ +class DataHolder { +public: + using DatumMap = std::unordered_map; + using CurveSetMap = std::unordered_map; + using LibraryDataMap = std::unordered_map>; + + /** + * Construct an empty DataHolder. + */ + DataHolder() = default; + + virtual ~DataHolder() = default; + + DataHolder(DataHolder const &) = delete; + + DataHolder &operator=(DataHolder const &) = delete; + + /** + * Construct a DataHolder from its conduit Node representation. + * + * @param asNode the DataHolder as a Node + */ + explicit DataHolder(conduit::Node const &asNode); + + /** + * Get the DataHolder's data. + * + * @return the DataHolder's data + */ + DatumMap const &getData() const noexcept { + return data; + } + + /** + * Add a Datum to this DataHolder. + * + * @param name the key for the Datum to add + * @param datum the Datum to add + */ + void add(std::string name, Datum datum); + + /** + * Add a CurveSet to this DataHolder. + * + * @param curveSet the CurveSet to add + */ + void add(CurveSet curveSet); + + /** + * Get the curve sets associated with this DataHolder. + * + * @return the dataholder's curve sets + */ + CurveSetMap const &getCurveSets() const noexcept { + return curveSets; + } + + /** + * Add a new library to this DataHolder. + * + * If you try to add a library with a name that already exists, the old + * library will be replaced. + * + * @return a pointer to a new DataHolder for a library + * of the given name. + */ + std::shared_ptr addLibraryData(std::string const &name); + + std::shared_ptr addLibraryData(std::string const &name, conduit::Node existingLibraryData); + + /** + * Get all library data associated with this DataHolder. + * + * @return the dataholder's library data + */ + LibraryDataMap const &getLibraryData() const noexcept { + return libraryData; + } + + /** + * Get a specific library associated with this DataHolder. + * + * @return the dataholder's library data + */ + std::shared_ptr getLibraryData(std::string const &libraryName) { + return libraryData.at(libraryName); + } + std::shared_ptr const getLibraryData(std::string const &libraryName) const { + return libraryData.at(libraryName); + } + + /** + * Get the user-defined content of the object. + * + * @return the user-defined content + */ + conduit::Node const &getUserDefinedContent() const noexcept { + return userDefined; + } + + /** + * Get the user-defined content of the object. + * + * @return the user-defined content + */ + conduit::Node &getUserDefinedContent() noexcept { + return userDefined; + } + + /** + * Set the user-defined content of the object. + * + * @param userDefined the user-defined content. Must be an object (key/value pairs) + */ + void setUserDefinedContent(conduit::Node userDefined); + + /** + * Convert this DataHolder to its conduit Node representation. + * + * @return the Node representation of this DataHolder. + */ + virtual conduit::Node toNode() const; + + +private: + CurveSetMap curveSets; + DatumMap data; + LibraryDataMap libraryData; + conduit::Node userDefined; +}; + +} + +#endif //SINA_DATAHOLDER_HPP diff --git a/src/axom/sina/include/sina/Datum.hpp b/src/axom/sina/include/sina/Datum.hpp new file mode 100644 index 0000000000..85550058ed --- /dev/null +++ b/src/axom/sina/include/sina/Datum.hpp @@ -0,0 +1,190 @@ +#ifndef SINA_DATUM_HPP +#define SINA_DATUM_HPP + +/// @file + +#include +#include + +#include "sina/ConduitUtil.hpp" +#include "conduit.hpp" + +namespace sina { + +/** + * Represents whether a Datum is a String, Scalar (double), array of Strings, + * or array of Scalars. + */ +enum class ValueType { + String, + Scalar, + StringArray, + ScalarArray +}; + +/** + * A Datum tracks the value and (optionally) tags and/or units of a + * value associated with a Record, e.g. a scalar, a piece of metadata, + * or an input parameter. In the Sina schema, a Datum always + * belongs to a Record or one of Record's inheriting types. + * + * Every Datum must have a value; units and tags are optional. + * + * The value of a Datum may be a string, a double, an array of strings, + * or an array of doubles. + * + * \code + * sina::Datum myDatum{12.34}; + * std::string value = "foobar"; + * sina::Datum myOtherDatum{value}; + * std::vector scalars = {1, 2, 20.0}; + * sina::Datum myArrayDatum{scalars}; + * //prints 1, corresponding to Scalar + * std::cout << static_cast::type>(myDatum.getType()) << std::endl; + * //prints 0, corresponding to String + * std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; + * //prints 3, corresponding to ScalarArray + * std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; + * myRecord->add(myDatum); + * myOtherDatum.setUnits("km/s"); + * myRecord->add(myOtherDatum); + * std::vector tags = {"input", "core"}; + * myArrayDatum.setTags(tags); + * myRecord->add(myArrayDatum); + * \endcode + */ +class Datum { +public: + /** + * Construct a new Datum. + * + * @param value the string value of the datum + */ + Datum(std::string value); + + /** + * Construct a new Datum. + * + * @param value the double value of the datum + */ + Datum(double value); + + /** + * Construct a new Datum. + * + * @param value the string array value of the datum + */ + Datum(std::vector value); + + /** + * Construct a new Datum. + * + * @param value the scalar array value of the datum + */ + Datum(std::vector value); + + /** + * Construct a Datum from its Node representation. + * + * @param asNode the Datum as conduit Node + */ + explicit Datum(conduit::Node const &asNode); + + /** + * Get the string value of the Datum. + * + * @return the string value + */ + std::string const &getValue() const noexcept { + return stringValue; + } + + /** + * Get the scalar value of the Datum. + * + * @return the scalar value + */ + double const &getScalar() const noexcept { + return scalarValue; + } + + /** + * Get the string array value of the Datum. + * + * @return the string vector value + */ + std::vector const &getStringArray() const noexcept { + return stringArrayValue; + } + + /** + * Get the scalar array value of the Datum. + * + * @return the scalar vector value + */ + std::vector const &getScalarArray() const noexcept { + return scalarArrayValue; + } + + /** + * Get the tags of the Datum + * + * @return the tags of the value + */ + std::vector const &getTags() const noexcept { + return tags; + } + + /** + * Set the tags of the Datum + * + * @param tags the tags of the value + */ + void setTags(std::vector tags); + + /** + * Get the units of the Datum + * + * @return the units of the value + */ + std::string const &getUnits() const noexcept { + return units; + } + + /** + * Set the units of the Datum + * + * @param units the units of the value + */ + void setUnits(std::string units); + + + /** + * Get the type of the Datum + * + * @return the type of the value + */ + ValueType getType() const noexcept { + return type; + } + + /** + * Convert this Datum to its conduit Node representation. + * + * @return the Node representation of this Datum. + */ + conduit::Node toNode() const; + +private: + std::string stringValue; + double scalarValue; + std::vector stringArrayValue; + std::vector scalarArrayValue; + std::string units; + std::vector tags; + ValueType type; +}; + +} + +#endif //SINA_DATUM_HPP diff --git a/src/axom/sina/include/sina/Document.hpp b/src/axom/sina/include/sina/Document.hpp new file mode 100644 index 0000000000..104579b6c8 --- /dev/null +++ b/src/axom/sina/include/sina/Document.hpp @@ -0,0 +1,185 @@ +#ifndef SINA_DOCUMENT_HPP +#define SINA_DOCUMENT_HPP + +/// @file + +#include +#include + +#include "conduit.hpp" + +#include "sina/Record.hpp" +#include "sina/Relationship.hpp" + +namespace sina { + +/** + * A Document represents the top-level object of a JSON file conforming to the + * Sina schema. When serialized, these documents can be ingested into a + * Sina database and used with the Sina tool. + * + * Documents contain at most two objects: a list of Records and a list of Relationships. A simple, empty document: + * \code{.json} + * { + * "records": [], + * "relationships": [] + * } + * \endcode + * + * The "records" list can contain Record objects and their inheriting types, such as Run (for a full list, please see + * the inheritance diagram in the Record documentation). The "relationships" list can contain Relationship objects. + * + * Documents can be assembled programatically and/or generated from existing JSON. An example of an assembled + * Document is provided on the main page. To load a Document from an existing JSON file: + * \code + * sina::Document myDocument = sina::loadDocument("path/to/infile.json"); + * \endcode + * + * To generate a Document from a JSON string and vice versa: + * \code + * std::string my_json = "{\"records\":[{\"type\":\"run\",\"id\":\"test\"}],\"relationships\":[]}"; + * sina::Document myDocument = sina::Document(my_json, sina::createRecordLoaderWithAllKnownTypes()); + * std::cout << myDocument.toJson() << std::endl;); + * \endcode + * + * You can add further entries to the Document using add(): + * \code + * std::unique_ptr myRun{new sina::Run{someID, "My Sim Code", "1.2.3", "jdoe"}}; + * sina::Relationship myRelationship{someID, "comes before", someOtherID}; + * myDocument.add(myRun); + * myDocument.add(myRelationship); + * \endcode + * + * You can also export your Document to file: + * \code + * sina::saveDocument(myDocument, "path/to/outfile.json") + * \endcode + * + */ +class Document { +public: + using RecordList = std::vector>; + using RelationshipList = std::vector; + + Document() = default; + + // Since we hold pointers to polymorphic objects, we can't support + // copying or assignment + Document(Document const &) = delete; + + Document &operator=(Document const &) = delete; + + Document(Document &&) = default; + + Document &operator=(Document &&) = default; + + /** + * Create a Document from its Conduit Node representation + * + * @param asNode the Document as a Node + * @param recordLoader an RecordLoader to use to load the different + * types of records which may be in the document + */ + Document(conduit::Node const &asNode, RecordLoader const &recordLoader); + + /** + * Create a Document from a JSON string representation + * + * @param asJson the Document as a JSON string + * @param recordLoader an RecordLoader to use to load the different + * types of records which may be in the document + */ + Document(std::string const &asJson, RecordLoader const &recordLoader); + + /** + * Add the given record to this document. + * + * @param record the record to add + */ + void add(std::unique_ptr record); + + /** + * Get the list of records currently in this document. + * + * @return the list of records + */ + RecordList const &getRecords() const noexcept { + return records; + } + + /** + * Add a relationship to this document + * + * @param relationship the relationship to add + */ + void add(Relationship relationship); + + /** + * Get the list of relationships in this document. + * + * @return the list of relationships + */ + RelationshipList const &getRelationships() const noexcept { + return relationships; + } + + + /** + * Convert this document to a conduit Node. + * + * @return the contents of the document as a Node + */ + conduit::Node toNode() const; + + /** + * Convert this document to a JSON string. + * + * @return the contents of the document as a JSON string + */ + std::string toJson(conduit::index_t indent=0, conduit::index_t depth=0, + const std::string &pad="", const std::string &eoe="") const; + +private: + /** + * Constructor helper method, extracts info from a conduit Node. + */ + void createFromNode(conduit::Node const &asNode, + RecordLoader const &recordLoader); + RecordList records; + RelationshipList relationships; +}; + +/** + * Save the given Document to the specified location. If the given file exists, + * it will be overwritten. + * + * @param document the Document to save + * @param fileName the location to which to save the file + * @throws std::ios::failure if there are any IO errors + */ +void saveDocument(Document const &document, std::string const &fileName); + +/** + * Load a document from the given path. Only records which this library + * knows about will be able to be loaded. + * + * @param path the file system path from which to load the document + * @return the loaded Document + */ +Document loadDocument(std::string const &path); + +/** + * Load a document from the given path. + * + * @param path the file system path from which to load the document + * @param recordLoader the RecordLoader to use to load the different types + * of records + * @return the loaded Document + */ +Document loadDocument(std::string const &path, + RecordLoader const &recordLoader); + +} + + +#endif //SINA_DOCUMENT_HPP diff --git a/src/axom/sina/include/sina/File.hpp b/src/axom/sina/include/sina/File.hpp new file mode 100644 index 0000000000..ae061dd6d8 --- /dev/null +++ b/src/axom/sina/include/sina/File.hpp @@ -0,0 +1,100 @@ +#ifndef SINA_FILE_HPP +#define SINA_FILE_HPP + +/// @file + +#include +#include + +#include "conduit.hpp" + +namespace sina { +/** + * A File tracks 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. + * + * \code + * sina::File myFile{"/path/to/file.png"}; + * myFile.setMimeType("image/png"); + * sina::File myOtherFile{"/path/to/other/file.txt"}; + * myOtherFile.setTags({"these","are","tags"}); + * myRecord->add(myFile); + * myRecord->add(myOtherFile); + * \endcode + */ +class File { +public: + /** + * Construct a new File. + * + * @param uri the location of the file + */ + explicit File(std::string uri); + + /** + * Construct a new File. + * + * @param uri the uri for a file + * @param asNode the Node representation of the file's additional info + */ + File(std::string uri, conduit::Node const &asNode); + + /** + * Get the File's URI. + * + * @return the URI + */ + std::string const &getUri() const noexcept { + return uri; + } + + /** + * Get the File's MIME type. + * + * @return the MIME type + */ + std::string const &getMimeType() const noexcept { + return mimeType; + } + + /** + * Get the File's tags. + * + * @return the tags + */ + std::vector const &getTags() const noexcept { + return tags; + } + + /** + * Set the File's MIME type. + * + * @param mimeType the MIME type + */ + void setMimeType(std::string mimeType); + + /** + * Set the File's tags. + * + * @param tags the File's tags + */ + void setTags(std::vector tags); + + /** + * Convert this File to its conduit Node representation. + * + * @return the File in its Node representation + */ + conduit::Node toNode() const; + +private: + std::string uri; + std::string mimeType; + std::vector tags; +}; + +} + +#endif //SINA_FILE_HPP diff --git a/src/axom/sina/include/sina/ID.hpp b/src/axom/sina/include/sina/ID.hpp new file mode 100644 index 0000000000..abcae6f936 --- /dev/null +++ b/src/axom/sina/include/sina/ID.hpp @@ -0,0 +1,137 @@ +#ifndef SINA_ID_HPP +#define SINA_ID_HPP + +/** + * @file + * + * The Sina schema allows records to have either a local ID or a global ID. + * When a global ID is specified, that will be used in the database. When a + * local ID is specified, an ID will be automatically generated when inserting + * the record into the database. + */ + +#include + +#include "conduit.hpp" + +namespace sina { + +/** + * Represents whether an ID is local or global. + */ +enum class IDType { + Local, + Global +}; + +/** + * An ID is used to represent the ID of an record. This class holds both the + * actual ID and whether it is a local or global ID. + */ +class ID { +public: + /** + * Create a new ID. + * @param id the actual value of the ID + * @param type whether the ID is local or global + */ + ID(std::string id, IDType type); + + /** + * Get the value of the ID. + * + * @return the actual ID + */ + std::string const &getId() const noexcept { + return id; + } + + /** + * Get the type of the ID. + * + * @return whether the ID is local or global + */ + IDType getType() const noexcept { + return type; + } + +private: + std::string id; + IDType type; +}; + +namespace internal { + +/** + * IDField instances are used to describe a pair of ID fields in a schema + * object which correspond to global and local names for the field. For + * example, the "id" and "local_id" fields in records, or the + * "subject"/"local_subject" and "object"/"local_object" pairs in + * relationships. + */ +class IDField { +public: + /** + * Construct a new IDField. + * + * @param value the value of the ID + * @param localName the name of the local variant of the field + * @param globalName the name of the global variant of the field + */ + IDField(ID value, std::string localName, std::string globalName); + + /** + * Construct an IDField by looking for its values in a conduit Node. + * + * @param parentObject the conduit Node containing the ID field + * @param localName the local name of the field + * @param globalName the global name of the field + */ + IDField(conduit::Node const &parentObject, std::string localName, + std::string globalName); + + /** + * Get the value of this field. + * + * @return the ID describing the field's value + */ + ID const &getID() const noexcept { + return value; + } + + /** + * Get the name to use for this field when the ID is local. + * + * @return the name of the local ID field + */ + std::string const &getLocalName() const noexcept { + return localName; + } + + /** + * Get the name to use for this field when the ID is global. + * + * @return the name of the global ID field + */ + std::string const &getGlobalName() const noexcept { + return globalName; + } + + /** + * Add this field to the given Node. + * + * @param object the Node to which to add the field + */ + void addTo(conduit::Node &object) const; + +private: + ID value; + std::string localName; + std::string globalName; +}; + +} // namespace internal + +} // namespace sina + +#endif //SINA_ID_HPP diff --git a/src/axom/sina/include/sina/Record.hpp b/src/axom/sina/include/sina/Record.hpp new file mode 100644 index 0000000000..0e16efc7b6 --- /dev/null +++ b/src/axom/sina/include/sina/Record.hpp @@ -0,0 +1,212 @@ +#ifndef SINA_RECORD_HPP +#define SINA_RECORD_HPP + +/// @file + +#include +#include +#include +#include +#include +#include + +#include "conduit.hpp" + +#include "sina/ID.hpp" +#include "sina/DataHolder.hpp" +#include "sina/CurveSet.hpp" +#include "sina/Datum.hpp" +#include "sina/File.hpp" + +namespace sina { + +/** + * FileEqualByURI is used to store files in a Record. + */ +struct FileEqualByURI { + bool operator()(const File &file1, const File &file2) const { + return file1.getUri() == file2.getUri(); + } +}; + +/** + * FileHashByURI is used to store files in a Record. Files are stored according + * to the hash of their URI. + */ +struct FileHashByURI { + size_t operator()(const File &file) const { + return std::hash()(file.getUri()); + } +}; + +/** + * The Record class represents an entry in a Document's Record list. Records represent the data to be stored + * (as opposed to the relationships between data)--natural scopes for Records include things like a single run + * of an application, an msub job, 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), etc. + * + * Each Record must have a type and an id. Each Record can also have a list of + * File objects and a map of Datum objects. + * + * \code + * sina::ID myID{"my_record", sina::IDType::Local}; + * std::unique_ptr myRecord{new sina::Record{myID, "my_type"}}; + * std::vector myTags{"input"}; + * sina::Datum myDatum{12, myTags}; + * myRecord->add("my_scalar",std::move(myDatum)); + * std::cout << myRecord->toNode().to_json() << std::endl; + * \endcode + * + * The output would be: + * \code{.json} + * {"local_id":"my_record","type":"my_type","data":{"my_scalar":{"tags":["input"],"value":12.0}}} + * \endcode + */ +class Record : public DataHolder { +public: + using FileSet = std::unordered_set; + + /** + * Construct a new Record. + * + * @param id the ID of the record + * @param type the type of the record + */ + Record(ID id, std::string type); + + /** + * Construct a Record from its conduit Node representation. + * + * @param asNode the Record as a Node + */ + explicit Record(conduit::Node const &asNode); + + Record(Record const &) = delete; + + Record &operator=(Record const &) = delete; + + /** + * Get the Record's ID. + * + * @return the ID + */ + ID const &getId() const noexcept { + return id.getID(); + } + + /** + * Get the Record's type. + * + * @return the Record's type + */ + std::string const &getType() const noexcept { + return type; + } + + /** + * Remove a File from this record. + * + * @param file the File to remove + */ + void remove(File const& file); + + + + using DataHolder::add; + /** + * Add a File to this record. + * + * @param file the File to add + */ + void add(File file); + + + /** + * Get the files associated with this record. + * + * @return the record's files + */ + FileSet const &getFiles() const noexcept { + return files; + } + + /** + * Convert this record to its conduit Node representation. + * + * @return the Node representation of this record. + */ + conduit::Node toNode() const override; + + /** + * Add another record to this one as library data. + * + * Useful for libraries that can run in standalone mode; the host + * simply calls this method on the record the library produces. + * Merges file lists. + */ + void addRecordAsLibraryData(Record const &childRecord, std::string const &name); + +private: + internal::IDField id; + std::string type; + FileSet files; +}; + + +/** + * A RecordLoader is used to convert conduit::Node instances which represent + * Sina Records into instances of their corresponding sina::Record + * subclasses. For convenience, a RecordLoader capable of handling Records of all known + * types can be created using createRecordLoaderWithAllKnownTypes: + * + * \code + * sina::Document myDocument = sina::Document(jObj, sina::createRecordLoaderWithAllKnownTypes()); + * \endcode + */ +class RecordLoader { +public: + /** + * A TypeLoader is a function which converts records of a specific type + * to their corresponding sub classes. + */ + using TypeLoader = std::function( + conduit::Node const &)>; + + /** + * Add a function for loading records of the specified type. + * + * @param type the type of records this function can load + * @param loader the function which can load the records + */ + void addTypeLoader(std::string const &type, TypeLoader loader); + + /** + * Load a sina::Record from its conduit Node representation. + * + * @param recordAsNode the Record as a Node + * @return the Record + */ + std::unique_ptr load(conduit::Node const &recordAsNode) const; + + /** + * Check whether this loader can load records of the given type. + * + * @param type the type of the records to check + * @return whether records of the given type can be loaded + */ + bool canLoad(std::string const &type) const; + +private: + std::unordered_map typeLoaders; +}; + +/** + * Create a RecordLoader which can load records of all known types. + * + * @return the newly-created loader + */ +RecordLoader createRecordLoaderWithAllKnownTypes(); + +} + +#endif //SINA_RECORD_HPP diff --git a/src/axom/sina/include/sina/Relationship.hpp b/src/axom/sina/include/sina/Relationship.hpp new file mode 100644 index 0000000000..799ffe6b8e --- /dev/null +++ b/src/axom/sina/include/sina/Relationship.hpp @@ -0,0 +1,130 @@ +#ifndef SINA_RELATIONSHIP_HPP +#define SINA_RELATIONSHIP_HPP + +/** + * @file + * + * Contains the definition of the Relationship class. + */ + +#include + +#include "conduit.hpp" + +#include "sina/ID.hpp" + +namespace sina { + +/** + * A Relationship consists of three parts: a subject, an object, and a predicate. It describes + * a relationship between two Records (and/or Record inheritors, e.g. Run). + * The subject and object must be IDs referring to valid records, while the predicate may be + * any string. + * + * In describing the connection between objects, a Relationship is read as + * " ". For example, in the relationship + * "Alice knows Bob", "Alice" is the subject, "knows" is the predicate, and + * "Bob" is the object. For further examples: + * + * - Task_22 contains Run_1024 + * - msub_1_1 describes out_j_1_1 + * - Carlos sends an email to Dani + * - local_task_12 runs before local_run_14 + * + * Note that Relationships are described in the active voice. **Avoiding the passive voice + * in predicates is recommended**, as this keeps the "direction" of the relationship constant. + * An example of a passively-voiced Relationship is "Dani is emailed by Carlos". Instead, + * this should be phrased as "Carlos emails Dani". + * + * If assembling Relationships programatically, it may be useful to reference the + * ID documentation. + * + * \code + * sina::ID task22{"Task_22", sina::IDType::Global}; + * sina::ID run1024{"Run_1024", sina::IDType::Global}; + * sina::Relationship myRelationship{task22, "contains", run1024}; + * std::cout << myRelationship.toNode().to_json() << std::endl; + * \endcode + * + * This would output: + * \code{.json} + * {"object":"Run_1024","predicate":"contains","subject":"Task_22"} + * \endcode + * + * As with any other Sina ID, the subject or object may be either local (uniquely refer to one object + * in a Sina file) or global (uniquely refer to one object in a database). Local IDs are replaced with + * global ones upon ingestion; all Relationships referring to that Local ID (as well as the Record possessing + * that ID) will be updated to use the same global ID. + * + * \code + * sina::ID myLocalID{"my_local_run", sina::IDType::Local}; + * std::unique_ptr myRun{new sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; + * sina::Relationship myRelationship{task22, "contains", myLocalID}; + * \endcode + * + * In the above code, "my_local_run" would be replaced by a global ID on ingestion. If this new global ID was, + * for example, "5Aed-BCds-23G1", then "my_local_run" would automatically be replaced by "5Aed-BCds-23G1" in both + * the Record and Relationship entries. + */ +class Relationship { +public: + /** + * Create a new relationship. + * + * @param subject the subject of the relationship + * @param predicate the predicate describing the relationship from the + * subject to the object + * @param object the object of the relationship + */ + Relationship(ID subject, std::string predicate, ID object); + + /** + * Create a Relationship object from its representation as a conduit Node. + * + * @param asNode the relationship as a Node + */ + explicit Relationship(conduit::Node const &asNode); + + /** + * Get the subject. + * + * @return the subject + */ + ID const &getSubject() const noexcept { + return subject.getID(); + } + + /** + * Get the object. + * + * @return the object + */ + ID const &getObject() const noexcept { + return object.getID(); + } + + /** + * Get the predicate. + * + * @return the predicate + */ + std::string const &getPredicate() const noexcept { + return predicate; + } + + /** + * Convert this Relationship to its Node representation. + * + * @return this relationship as a conduit Node + */ + conduit::Node toNode() const; + +private: + internal::IDField subject; + internal::IDField object; + std::string predicate; +}; + +} + +#endif //SINA_RELATIONSHIP_HPP diff --git a/src/axom/sina/include/sina/Run.hpp b/src/axom/sina/include/sina/Run.hpp new file mode 100644 index 0000000000..718758901e --- /dev/null +++ b/src/axom/sina/include/sina/Run.hpp @@ -0,0 +1,91 @@ +#ifndef SINA_RUN_HPP +#define SINA_RUN_HPP + +/// @file + +#include "sina/Record.hpp" + +namespace sina { + +/** + * A Run is a subtype of Record corresponding to a single run of an application, as + * specified in the Sina schema. A Run has a few additional fields required in addition + * to the id required by a Record (type is automatically set to "run"): + * + * - 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 + * + * To create a Run: + * \code + * sina::ID run1ID{"run1", sina::IDType::Local}; + * std::unique_ptr run1{new sina::Run{run1ID, "My Sim Code", "1.2.3", "jdoe"}}; + * \endcode + * + */ +class Run : public Record { +public: + /** + * Create a new Run. + * + * @param id the run's ID + * @param application the application that was run + * @param version (optional) the version of the application + * @param user (optional) the user who executed the run + */ + Run(ID id, std::string application, std::string version = "", std::string user = ""); + + /** + * Create a Run from its representation as a conduit Node + * + * @param asNode the run as a Node + */ + explicit Run(conduit::Node const &asNode); + + /** + * Get the application that was run. + * + * @return the application's name + */ + std::string const &getApplication() const { + return application; + } + + /** + * Get the version of the application that was run. + * + * @return the application's version + */ + std::string const &getVersion() const { + return version; + } + + /** + * Get the name of the user who ran the application. + * + * @return the user's name + */ + std::string const &getUser() const { + return user; + } + + conduit::Node toNode() const override; + +private: + std::string application; + std::string version; + std::string user; +}; + +/** + * Add a type loader to the given RecordLoader for loading Run instances. + * + * @param loader the RecordLoader to which to add the function for loading + * Run instances. + */ +void addRunLoader(RecordLoader &loader); + +} + + +#endif //SINA_RUN_HPP diff --git a/src/axom/sina/include/sina/sina.hpp b/src/axom/sina/include/sina/sina.hpp new file mode 100644 index 0000000000..bf95700703 --- /dev/null +++ b/src/axom/sina/include/sina/sina.hpp @@ -0,0 +1,17 @@ +/** + * @file + * + * This file contains all headers end users need to use sina-cpp. + */ + +#ifndef SINA_HPP +#define SINA_HPP + +#include "sina/Document.hpp" +#include "sina/ID.hpp" +#include "sina/Record.hpp" +#include "sina/Run.hpp" +#include "sina/Datum.hpp" +#include "sina/config.hpp" + +#endif // SINA_HPP diff --git a/src/axom/sina/src/AdiakWriter.cpp b/src/axom/sina/src/AdiakWriter.cpp new file mode 100644 index 0000000000..58718ec0f4 --- /dev/null +++ b/src/axom/sina/src/AdiakWriter.cpp @@ -0,0 +1,262 @@ +/// @file + +#include "sina/AdiakWriter.hpp" + +#if AXOM_BUILD_SINA_ADIAK_BINDINGS + +#include +#include +#include +#include +#include + +extern "C" { +#include "adiak_tool.h" +} + +#include "sina/CppBridge.hpp" +#include "sina/ConduitUtil.hpp" +#include "sina/Record.hpp" +#include "sina/Datum.hpp" +#include "sina/Document.hpp" + + +namespace sina { +namespace { + +/** +* Adiak has a much wider array of supported types than Sina. We will convert +* Adiak types to ones Sina understands; SinaType holds the possibilities. +**/ +enum SinaType {sina_scalar, sina_string, sina_list, sina_file, sina_unknown}; + +/** +* Add a sina::Datum object to a Record. These are the sina equivalent +* of an Adiak datapoint. Since we track slightly different info, this function +* harvests what it can and hands it off to the Record. +**/ +template +void addDatum(const std::string &name, T sina_safe_val, const std::vector &tags, sina::Record *record){ + sina::Datum datum{sina_safe_val}; + datum.setTags(std::move(tags)); + record->add(name, datum); +} + +/** +* Add a sina::File object to our current Record. Adiak stores paths, +* which are essentially the same as Sina's idea of storing files. +**/ +void addFile(const std::string &name, const std::string &uri, sina::Record *record){ + // We don't care about type here, there's only one adiak type that acts as a file + sina::File file{uri}; + file.setTags(std::vector{name}); + record->add(std::move(file)); +} + +/** +* Given an Adiak type, return its corresponding Sina type. +**/ +SinaType findSinaType(adiak_datatype_t *t){ + switch (t->dtype){ + case adiak_long: + case adiak_ulong: + case adiak_int: + case adiak_uint: + case adiak_double: + case adiak_timeval: + return sina_scalar; + case adiak_date: + case adiak_version: + case adiak_string: + case adiak_catstring: + return sina_string; + case adiak_path: + return sina_file; + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + return sina_list; + case adiak_type_unset: + return sina_unknown; + default: + return sina_unknown; + } +} + +/** +* Several Adiak types become what Sina views as a "scalar" (a double). +* Manage the conversions from various Adiak types to the final double +* representation +**/ +double toScalar(adiak_value_t *val, adiak_datatype_t *adiak_type){ + switch (adiak_type->dtype){ + case adiak_long: + case adiak_ulong: + return static_cast(val->v_long); + case adiak_int: + case adiak_uint: + return static_cast(val->v_int); + case adiak_double: + return val->v_double; + case adiak_timeval: { + struct timeval *tval = static_cast(val->v_ptr); + return static_cast(tval->tv_sec) + + (static_cast(tval->tv_usec) / 1000000.0); + } + // None of the rest of these should ever be reachable, so special error message + case adiak_date: + case adiak_version: + case adiak_string: + case adiak_catstring: + case adiak_path: + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + case adiak_type_unset: { + std::string msg("Logic error, contact maintainer: Adiak-to-Sina double converter given "); + char *s = adiak_type_to_string(adiak_type, 1); + msg += s; + free(s); + throw std::runtime_error(msg); + } + default: + throw std::runtime_error("Adiak-to-Sina double converter given something not convertible to double"); + } +} + +/** +* Some Adiak types become what Sina views as a string. +* Manage the conversions from various Adiak types to said string. +**/ +std::string toString(adiak_value_t *val, adiak_datatype_t *adiak_type){ + switch (adiak_type->dtype){ + case adiak_date: { + char datestr[512]; + signed long seconds_since_epoch = static_cast(val->v_long); + struct tm *loc = localtime(&seconds_since_epoch); + strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z", loc); + return static_cast(datestr); + } + case adiak_catstring: + case adiak_version: + case adiak_string: + case adiak_path: + return std::string(static_cast(val->v_ptr)); + case adiak_long: + case adiak_ulong: + case adiak_int: + case adiak_uint: + case adiak_double: + case adiak_timeval: + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + case adiak_type_unset:{ + std::string msg("Logic error, contact maintainer: Adiak-to-Sina string converter given "); + char *s = adiak_type_to_string(adiak_type, 1); + msg += s; + free(s); + throw std::runtime_error(msg); + } + default: + throw std::runtime_error("Adiak-to-Sina string converter given something not convertible to string"); + } +} + +/** +* Some Adiak types become a list of some form. Sina, being concerned +* with queries and visualization, only handles lists that are all scalars +* or all strings. Manage conversions from various Adiak list types that +* contain scalars to a simple list (vector) of scalars. +**/ +std::vector toScalarList(adiak_value_t *subvals, adiak_datatype_t *t){ + std::vector sina_safe_list; + for (int i = 0; i < t->num_elements; i++) { + sina_safe_list.emplace_back(toScalar(subvals+i, t->subtype[0])); + } + return sina_safe_list; +} + + +/** +* Partner method to toScalarList, invoked when the children of an adiak list +* type are strings (according to Sina). +**/ +std::vector toStringList(adiak_value_t *subvals, adiak_datatype_t *t){ + std::vector sina_safe_list; + for (int i = 0; i < t->num_elements; i++) { + sina_safe_list.emplace_back(toString(subvals+i, t->subtype[0])); + } + return sina_safe_list; +} + +} + +void adiakSinaCallback(const char *name, adiak_category_t, const char *subcategory, adiak_value_t *val, adiak_datatype_t *adiak_type, void *void_record) +{ + const SinaType sina_type = findSinaType(adiak_type); + sina::Record *record = static_cast(void_record); + std::vector tags; + if(subcategory && subcategory[0]!='\0'){ + tags.emplace_back(subcategory); + } + switch (sina_type) { + case sina_unknown: + // If we don't know what it is, we can't store it, so as above... + throw std::runtime_error("Unknown Adiak type cannot be added to Sina record."); + case sina_scalar: { + char * s = adiak_type_to_string(adiak_type, 1); + tags.emplace_back(s); + free(s); + addDatum(name, toScalar(val, adiak_type), tags, record); + break; + } + case sina_string: { + char * s = adiak_type_to_string(adiak_type, 1); + tags.emplace_back(s); + free(s); + addDatum(name, toString(val, adiak_type), tags, record); + break; + } + case sina_file: + addFile(name, toString(val, adiak_type), record); + break; + case sina_list: { + // Sina doesn't really know/care the difference between list, tuple, set + // Further simplification: everything has to be the same type + // Even further simplification: nothing nested. In the future, depth>1 lists + // should be sent to user_defined + adiak_value_t *subvals = static_cast(val->v_ptr); + SinaType list_type = findSinaType(adiak_type->subtype[0]); + char * s = adiak_type_to_string(adiak_type->subtype[0], 1); + tags.emplace_back(s); + free(s); + switch (list_type) { + case sina_string: + addDatum(name, toStringList(subvals, adiak_type), tags, record); + break; + // Weird case wherein we're given a list of filenames, which we can somewhat manage + case sina_file: + for (int i=0; i < adiak_type->num_elements; i++) { + addFile(name, toString(subvals+i, adiak_type->subtype[0]), record); + } + break; + case sina_scalar: + addDatum(name, toScalarList(subvals, adiak_type), tags, record); + break; + case sina_unknown: + throw std::runtime_error("Type must not be unknown for list entries to be added to a Sina record"); + case sina_list: + throw std::runtime_error("Lists must not be nested for list entries to be added to a Sina record"); + default: + throw std::runtime_error("Type must be set for list entries to be added to a Sina record"); + } + } + } +} +} + +#endif // AXOM_BUILD_SINA_ADIAK_BINDINGS diff --git a/src/axom/sina/src/ConduitUtil.cpp b/src/axom/sina/src/ConduitUtil.cpp new file mode 100644 index 0000000000..648c16ed1c --- /dev/null +++ b/src/axom/sina/src/ConduitUtil.cpp @@ -0,0 +1,140 @@ +#include "sina/ConduitUtil.hpp" + +#include +#include +#include +#include +#include + +#include "conduit.hpp" + +namespace sina { + +namespace { +/** + * Get the given field as string. If it is not a string, an exception is + * thrown with a user-friendly message. + * + * @param field the value of the field + * @param fieldName the name of the field + * @param parentType the name of the parent which contained the field + * @return the avlue of the field + * @throws std::invalid_argument if the field is not a string + */ +std::string getExpectedString(conduit::Node const &field, + std::string const &fieldName, + std::string const &parentType) { + if (!field.dtype().is_string()) { + std::ostringstream message; + message << "The field '" << fieldName + << "' for objects of type '" << parentType + << "' must be a string, was '" + << field.dtype().name() << "'"; + throw std::invalid_argument(message.str()); + } + return field.as_string(); +} +} + +conduit::Node const &getRequiredField(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType) { + if (!parent.has_child(fieldName)) { + std::ostringstream message; + message << "The field '" << fieldName + << "' is required for objects of type '" << parentType << "'"; + throw std::invalid_argument(message.str()); + } + return parent.child(fieldName); +} + +std::string getRequiredString(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType) { + conduit::Node const &field = getRequiredField(fieldName, parent, parentType); + return getExpectedString(field, fieldName, parentType); +} + +std::string getOptionalString(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType) { + if (!parent.has_child(fieldName) || parent.child(fieldName).dtype().is_empty()) { + return ""; + } + return getExpectedString(parent.child(fieldName), fieldName, parentType); +} + +double getRequiredDouble(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType) { + auto &ref = getRequiredField(fieldName, parent, parentType); + if (!ref.dtype().is_number()) { + std::ostringstream message; + message << "The field '" << fieldName + << "' for objects of type '" << parentType + << "' must be a double"; + throw std::invalid_argument(message.str()); + } + return ref.as_double(); +} + +void addStringsToNode(conduit::Node &parent, std::string const &child_name, + std::vector const &string_values){ + // If the child already exists, add_child returns it + conduit::Node &child_node = parent.add_child(child_name); + for(auto &value : string_values) + { + auto &list_entry = child_node.append(); + list_entry.set(value); + } + + // If there were no children, this will be a null node rather than a list. + // We prefer empty lists to null values in our serialized JSON, so force + // this to be a list + if (child_node.number_of_children() == 0) { + child_node.set_dtype(conduit::DataType::list()); + } +} + +std::vector toDoubleVector(conduit::Node const &node, + std::string const &name) { + if (node.dtype().is_list() && node.dtype().number_of_elements() == 0) { + return std::vector{}; + } + conduit::Node asDoubles; + try { + node.to_double_array(asDoubles); + } catch (conduit::Error const &err) { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of doubles" << err.what(); + throw std::invalid_argument(errStream.str()); + } + double const *start = asDoubles.as_double_ptr(); + auto count = static_cast::size_type>( + asDoubles.dtype().number_of_elements()); + return std::vector{start, start + count}; +} + +std::vector toStringVector(conduit::Node const &node, + std::string const &name) { + std::vector converted; + if (!node.dtype().is_list()) { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of strings. It is not a list. " + << node.to_json_default(); + throw std::invalid_argument(errStream.str()); + } + for (auto iter = node.children(); iter.has_next(); ) { + auto &child = iter.next(); + if (child.dtype().is_string()) { + converted.emplace_back(child.as_string()); + } else { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of strings. A value is not a string. " + << node.to_json_default(); + throw std::invalid_argument(errStream.str()); + } + } + return converted; +} + +} diff --git a/src/axom/sina/src/Curve.cpp b/src/axom/sina/src/Curve.cpp new file mode 100644 index 0000000000..486277e93c --- /dev/null +++ b/src/axom/sina/src/Curve.cpp @@ -0,0 +1,57 @@ +#include "sina/Curve.hpp" +#include "sina/ConduitUtil.hpp" + +#include + + +namespace sina { + +namespace { +constexpr auto CURVE_TYPE_NAME = "curve"; +constexpr auto VALUES_KEY = "value"; +constexpr auto UNITS_KEY = "units"; +constexpr auto TAGS_KEY = "tags"; +} + +Curve::Curve(std::string name_, std::vector values_) : + name{std::move(name_)}, values{std::move(values_)}, units{}, tags{} {} + + +Curve::Curve(std::string name_, double const *values_, std::size_t numValues) : + name{std::move(name_)}, values{values_, values_ + numValues}, + units{}, tags{} {} + +Curve::Curve(std::string name_, conduit::Node const &curveAsNode) : + name{std::move(name_)}, values{}, units{}, tags{} { + auto &valuesAsNode = getRequiredField(VALUES_KEY, curveAsNode, + CURVE_TYPE_NAME); + values = toDoubleVector(valuesAsNode, VALUES_KEY); + + units = getOptionalString(UNITS_KEY, curveAsNode, CURVE_TYPE_NAME); + + if (curveAsNode.has_child(TAGS_KEY)) { + tags = toStringVector(curveAsNode[TAGS_KEY], TAGS_KEY); + } +} + +void Curve::setUnits(std::string units_) { + units = std::move(units_); +} + +void Curve::setTags(std::vector tags_) { + tags = std::move(tags_); +} + +conduit::Node Curve::toNode() const { + conduit::Node asNode; + asNode[VALUES_KEY] = values; + if (!units.empty()) { + asNode[UNITS_KEY] = units; + } + if (!tags.empty()) { + addStringsToNode(asNode, TAGS_KEY, tags); + } + return asNode; +} + +} diff --git a/src/axom/sina/src/CurveSet.cpp b/src/axom/sina/src/CurveSet.cpp new file mode 100644 index 0000000000..b60da4df42 --- /dev/null +++ b/src/axom/sina/src/CurveSet.cpp @@ -0,0 +1,97 @@ +#include "sina/CurveSet.hpp" + +#include + +#include "sina/ConduitUtil.hpp" + +namespace sina { + +namespace { + +constexpr auto INDEPENDENT_KEY = "independent"; +constexpr auto DEPENDENT_KEY = "dependent"; + +/** + * Add a curve to the given curve map. + * + * @param curve the curve to add + * @param curves the CurveMap to which to add the curve + */ +void addCurve(Curve &&curve, CurveSet::CurveMap &curves) { + auto &curveName = curve.getName(); + auto existing = curves.find(curveName); + if (existing == curves.end()) { + curves.insert(std::make_pair(curveName, curve)); + } else { + existing->second = curve; + } +} + +/** + * Extract a CurveMap from the given node. + * + * @param parent the parent node + * @param childNodeName the name of the child node + * @return a CurveMap representing the specified child + */ +CurveSet::CurveMap extractCurveMap(conduit::Node const &parent, + std::string const &childNodeName) { + CurveSet::CurveMap curveMap; + if (!parent.has_child(childNodeName)) { + return curveMap; + } + + auto &mapAsNode = parent.child(childNodeName); + for (auto iter = mapAsNode.children(); iter.has_next(); ) { + auto &curveAsNode = iter.next(); + std::string curveName = iter.name(); + Curve curve{curveName, curveAsNode}; + curveMap.insert(std::make_pair(std::move(curveName), std::move(curve))); + } + + return curveMap; +}; + +/** + * Create a Conduit node to represent the given CurveMap. + * + * @param curveMap the CurveMap to convert + * @return the map as a node + */ +conduit::Node createCurveMapNode(CurveSet::CurveMap const &curveMap) { + conduit::Node mapNode; + mapNode.set_dtype(conduit::DataType::object()); + for (auto &entry : curveMap) { + mapNode.add_child(entry.first) = entry.second.toNode(); + } + return mapNode; +} + +} + +CurveSet::CurveSet(std::string name_) : name{std::move(name_)}, + independentCurves{}, + dependentCurves{} {} + +CurveSet::CurveSet(std::string name_, conduit::Node const &node) + : name{std::move(name_)}, + independentCurves{extractCurveMap(node, INDEPENDENT_KEY)}, + dependentCurves{extractCurveMap(node, DEPENDENT_KEY)} { +} + +void CurveSet::addIndependentCurve(Curve curve) { + addCurve(std::move(curve), independentCurves); +} + +void CurveSet::addDependentCurve(Curve curve) { + addCurve(std::move(curve), dependentCurves); +} + +conduit::Node CurveSet::toNode() const { + conduit::Node asNode; + asNode[INDEPENDENT_KEY] = createCurveMapNode(independentCurves); + asNode[DEPENDENT_KEY] = createCurveMapNode(dependentCurves); + return asNode; +} + +} diff --git a/src/axom/sina/src/DataHolder.cpp b/src/axom/sina/src/DataHolder.cpp new file mode 100644 index 0000000000..9620340c58 --- /dev/null +++ b/src/axom/sina/src/DataHolder.cpp @@ -0,0 +1,128 @@ +#include "sina/DataHolder.hpp" + +#include "sina/ConduitUtil.hpp" +#include "sina/Datum.hpp" + +#include + +namespace { + +char const DATA_FIELD[] = "data"; +char const CURVE_SETS_FIELD[] = "curve_sets"; +char const LIBRARY_DATA_FIELD[] = "library_data"; +char const USER_DEFINED_FIELD[] = "user_defined"; + +} + +namespace sina { + +void DataHolder::add(std::string name, Datum datum) { + auto existing = data.find(name); + if (existing == data.end()) { + data.emplace(std::make_pair(std::move(name), datum)); + } else { + existing->second = datum; + } +} + +void DataHolder::add(CurveSet curveSet) { + auto name = curveSet.getName(); + auto existing = curveSets.find(name); + if (existing == curveSets.end()) { + curveSets.emplace(name, std::move(curveSet)); + } else { + existing->second = std::move(curveSet); + } +} + +std::shared_ptr DataHolder::addLibraryData(std::string const &name) { + auto existing = libraryData.find(name); + if (existing == libraryData.end()) { + libraryData.emplace(name, std::make_shared()); + } else { + existing->second = std::make_shared(); + } + return libraryData.at(name); +} + +std::shared_ptr DataHolder::addLibraryData(std::string const &name, conduit::Node existingLibraryData) { + auto existing = libraryData.find(name); + if (existing == libraryData.end()) { + libraryData.emplace(name, std::make_shared(existingLibraryData)); + } else { + existing->second = std::make_shared(existingLibraryData); + } + return libraryData.at(name); +} + +void DataHolder::setUserDefinedContent(conduit::Node userDefined_) { + userDefined = std::move(userDefined_); +} + +conduit::Node DataHolder::toNode() const { + conduit::Node asNode; + asNode.set(conduit::DataType::object()); + if(!libraryData.empty()){ + //Loop through vector of data and append Json + conduit::Node libRef; + for(auto &lib : libraryData){ + libRef.add_child(lib.first) = lib.second->toNode(); + } + asNode[LIBRARY_DATA_FIELD] = libRef; + } + if(!curveSets.empty()){ + conduit::Node curveSetsNode; + for(auto &entry : curveSets){ + curveSetsNode.add_child(entry.first) = entry.second.toNode(); + } + asNode[CURVE_SETS_FIELD] = curveSetsNode; + } + if(!data.empty()){ + //Loop through vector of data and append Json + conduit::Node datumRef; + for(auto &datum : data){ + datumRef.add_child(datum.first) = datum.second.toNode(); + } + asNode[DATA_FIELD] = datumRef; + } + if(!userDefined.dtype().is_empty()){ + asNode[USER_DEFINED_FIELD] = userDefined; + } + return asNode; +} + +DataHolder::DataHolder(conduit::Node const &asNode) { + if(asNode.has_child(DATA_FIELD)){ + auto dataIter = asNode[DATA_FIELD].children(); + //Loop through DATA_FIELD objects and add them to data: + while(dataIter.has_next()){ + auto &namedDatum = dataIter.next(); + data.emplace(std::make_pair(dataIter.name(), Datum(namedDatum))); + } + } + if (asNode.has_child(CURVE_SETS_FIELD)) { + auto curveSetsIter = asNode[CURVE_SETS_FIELD].children(); + while(curveSetsIter.has_next()){ + auto &curveSetNode = curveSetsIter.next(); + std::string name = curveSetsIter.name(); + CurveSet cs{name, curveSetNode}; + curveSets.emplace(std::make_pair(std::move(name), std::move(cs))); + } + } + if(asNode.has_child(LIBRARY_DATA_FIELD)) { + auto libraryIter = asNode[LIBRARY_DATA_FIELD].children(); + while(libraryIter.has_next()){ + auto &libraryDataNode = libraryIter.next(); + std::string name = libraryIter.name(); + libraryData.emplace(std::make_pair(std::move(name), + std::make_shared(libraryDataNode))); + } + } + if(asNode.has_child(USER_DEFINED_FIELD)) { + userDefined = asNode[USER_DEFINED_FIELD]; + if (!userDefined.dtype().is_object()) { + throw std::invalid_argument("user_defined must be an object Node"); + } + } + } +} diff --git a/src/axom/sina/src/Datum.cpp b/src/axom/sina/src/Datum.cpp new file mode 100644 index 0000000000..d238cf9bee --- /dev/null +++ b/src/axom/sina/src/Datum.cpp @@ -0,0 +1,167 @@ +/// @file + +#include "sina/Datum.hpp" +#include "conduit.hpp" + +#include +#include +#include + +namespace { + +char const VALUE_FIELD[] = "value"; +char const UNITS_FIELD[] = "units"; +char const TAGS_FIELD[] = "tags"; +char const DATA_PARENT_TYPE[] = "data"; + +} + +namespace sina { + +Datum::Datum(std::string value_) : + stringValue{std::move(value_)}{ + //Set type to String, as we know it uses strings + type = ValueType::String; +} + +Datum::Datum(double value_) : + scalarValue{std::move(value_)}{ + //Set type to Scalar, as we know it uses doubles + type = ValueType::Scalar; +} + +Datum::Datum(std::vector value_) : + stringArrayValue{std::move(value_)}{ + //Set type to StringArray, as we know it uses an array of strings + type = ValueType::StringArray; +} + +Datum::Datum(std::vector value_) : + scalarArrayValue{std::move(value_)}{ + //Set type to ScalarArray, as we know it uses an array of doubles + type = ValueType::ScalarArray; +} + +Datum::Datum(conduit::Node const &asNode) { + //Need to determine what type of Datum we have: Scalar (double), String, + //or list of one of those two. + conduit::Node valueNode = getRequiredField(VALUE_FIELD, asNode, DATA_PARENT_TYPE); + if(valueNode.dtype().is_string()){ + stringValue = valueNode.as_string(); + type = ValueType::String; + } + else if(valueNode.dtype().is_number() && valueNode.dtype().number_of_elements() == 1){ + scalarValue = valueNode.to_double(); + type = ValueType::Scalar; + } + // There are two different ways to end up with a "list" of numbers in conduit, but + // only one of them tests True for is_list. This handles the other. + else if(valueNode.dtype().is_number()){ + type = ValueType::ScalarArray; + // What's passed in could be an array of any numeric type + // We pass a cast copy into captureNode + conduit::Node captureNode; + valueNode.to_float64_array(captureNode); + std::vector array_as_vect(captureNode.as_double_ptr(), + captureNode.as_double_ptr() + captureNode.dtype().number_of_elements()); + scalarArrayValue = array_as_vect; + } + else if(valueNode.dtype().is_list()){ + //An empty list is assumed to be an empty list of doubles. + //This only works because this field is immutable! + //If this ever changes, or if Datum's type is used directly to make + //decisions (ex: Sina deciding where to store data), this logic + //should be revisited. + if(valueNode.number_of_children() == 0 || valueNode[0].dtype().is_number()){ + type = ValueType::ScalarArray; + } + else if(valueNode[0].dtype().is_string()){ + type = ValueType::StringArray; + } + else { + std::ostringstream message; + message << "The only valid types for an array '" << VALUE_FIELD + << "' are strings and numbers. Got '" << valueNode.to_json() << "'"; + throw std::invalid_argument(message.str()); + } + + auto itr = valueNode.children(); + while(itr.has_next()) + { + conduit::Node const &entry = itr.next(); + if(entry.dtype().is_string() && type == ValueType::StringArray){ + stringArrayValue.emplace_back(entry.as_string()); + } + else if(entry.dtype().is_number() && type == ValueType::ScalarArray){ + scalarArrayValue.emplace_back(entry.to_double()); + } + else { + std::ostringstream message; + message << "If the required field '" << VALUE_FIELD + << "' is an array, it must consist of only strings or only numbers, " + << "but got '" << entry.dtype().name() << "' (" << entry.to_json() << ")"; + throw std::invalid_argument(message.str()); + } + } + } + else { + std::ostringstream message; + message << "The required field '" << VALUE_FIELD + << "' must be a string, double, list of strings, or list of doubles."; + throw std::invalid_argument(message.str()); + } + + //Get the units, if there are any + units = getOptionalString(UNITS_FIELD, asNode, DATA_PARENT_TYPE); + + //Need to grab the tags and add them to a vector of strings + if(asNode.has_child(TAGS_FIELD)){ + auto tagNodeIter = asNode[TAGS_FIELD].children(); + while(tagNodeIter.has_next()){ + auto &tag = tagNodeIter.next(); + if(tag.dtype().is_string()){ + tags.emplace_back(std::string(tag.as_string())); + } else { + std::ostringstream message; + message << "The optional field '" << TAGS_FIELD + << "' must be an array of strings. Found '" + << tag.dtype().name() << "' instead."; + throw std::invalid_argument(message.str()); + } + } + } +} + +void Datum::setUnits(std::string units_) { + units = std::move(units_); +} + +void Datum::setTags(std::vector tags_){ + tags = std::move(tags_); +} + +conduit::Node Datum::toNode() const { + conduit::Node asNode; + switch(type){ + case ValueType::Scalar: + asNode[VALUE_FIELD] = scalarValue; + break; + case ValueType::String: + asNode[VALUE_FIELD] = stringValue; + break; + case ValueType::ScalarArray: + asNode[VALUE_FIELD] = scalarArrayValue; + break; + case ValueType::StringArray: + addStringsToNode(asNode, VALUE_FIELD, stringArrayValue); + break; + } + if(tags.size() > 0) + addStringsToNode(asNode, TAGS_FIELD, tags); + if(!units.empty()) + asNode[UNITS_FIELD] = units; + return asNode; +}; + + +} diff --git a/src/axom/sina/src/Document.cpp b/src/axom/sina/src/Document.cpp new file mode 100644 index 0000000000..fd2bf37eae --- /dev/null +++ b/src/axom/sina/src/Document.cpp @@ -0,0 +1,135 @@ +/// @file + +#include "sina/Document.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace sina { + +namespace { +char const RECORDS_KEY[] = "records"; +char const RELATIONSHIPS_KEY[] = "relationships"; +char const SAVE_TMP_FILE_EXTENSION[] = ".sina.tmp"; +} + +void Document::add(std::unique_ptr record) { + records.emplace_back(std::move(record)); +} + +void Document::add(Relationship relationship) { + relationships.emplace_back(std::move(relationship)); +} + +conduit::Node Document::toNode() const { + conduit::Node document(conduit::DataType::object()); + document[RECORDS_KEY] = conduit::Node(conduit::DataType::list()); + document[RELATIONSHIPS_KEY] = conduit::Node(conduit::DataType::list()); + for(auto &record : records) + { + auto &list_entry = document[RECORDS_KEY].append(); + list_entry.set_node(record->toNode()); + } + for(auto &relationship : relationships) + { + auto &list_entry = document[RELATIONSHIPS_KEY].append(); + list_entry = relationship.toNode(); + } + return document; +} + +void Document::createFromNode(conduit::Node const &asNode, + RecordLoader const &recordLoader) { + if (asNode.has_child(RECORDS_KEY)) { + conduit::Node record_nodes = asNode[RECORDS_KEY]; + if (record_nodes.dtype().is_list()) { + auto recordIter = record_nodes.children(); + while (recordIter.has_next()){ + auto record = recordIter.next(); + add(recordLoader.load(record)); + } + } else { + std::ostringstream message; + message << "The '" << RECORDS_KEY + << "' element of a document must be an array"; + throw std::invalid_argument(message.str()); + } + } + + if (asNode.has_child(RELATIONSHIPS_KEY)){ + conduit::Node relationship_nodes = asNode[RELATIONSHIPS_KEY]; + if (relationship_nodes.dtype().is_list()) { + auto relationshipsIter = relationship_nodes.children(); + while (relationshipsIter.has_next()){ + auto &relationship = relationshipsIter.next(); + add(Relationship{relationship}); + } + } else { + std::ostringstream message; + message << "The '" << RELATIONSHIPS_KEY + << "' element of a document must be an array"; + throw std::invalid_argument(message.str()); + } + } +} + +Document::Document(conduit::Node const &asNode, + RecordLoader const &recordLoader) { + this->createFromNode(asNode, recordLoader); +} + +Document::Document(std::string const &asJson, RecordLoader const &recordLoader) { + conduit::Node asNode; + asNode.parse(asJson, "json"); + this->createFromNode(asNode, recordLoader); +} + +std::string Document::toJson(conduit::index_t indent, conduit::index_t depth, + const std::string &pad, const std::string &eoe) const{ + return this->toNode().to_json("json", indent, depth, pad, eoe); +} + +void saveDocument(Document const &document, std::string const &fileName) { + // It is a common use case for users to want to overwrite their files as + // the simulation progresses. However, this operation should be atomic so + // that if a write fails, the old file is left intact. For this reason, + // we write to a temporary file first and then move the file. The temporary + // file is in the same directory to ensure that it is part of the same + // file system as the destination file so that the move operation is + // atomic. + std::string tmpFileName = fileName + SAVE_TMP_FILE_EXTENSION; + auto asJson = document.toJson(); + std::ofstream fout{tmpFileName}; + fout.exceptions(std::ostream::failbit | std::ostream::badbit); + fout << asJson; + fout.close(); + + if (rename(tmpFileName.c_str(), fileName.c_str()) != 0) { + std::string message{"Could not save to '"}; + message += fileName; + message += "'"; + throw std::ios::failure{message}; + } +} + +Document loadDocument(std::string const &path) { + return loadDocument(path, createRecordLoaderWithAllKnownTypes()); +} + +Document loadDocument(std::string const &path, + RecordLoader const &recordLoader) { + conduit::Node nodeFromJson; + std::ifstream file_in{path}; + std::ostringstream file_contents; + file_contents << file_in.rdbuf(); + file_in.close(); + nodeFromJson.parse(file_contents.str(), "json"); + return Document{nodeFromJson, recordLoader}; +} + +} diff --git a/src/axom/sina/src/File.cpp b/src/axom/sina/src/File.cpp new file mode 100644 index 0000000000..1534e78cf9 --- /dev/null +++ b/src/axom/sina/src/File.cpp @@ -0,0 +1,63 @@ +/// @file + +#include "sina/File.hpp" +#include "sina/ConduitUtil.hpp" + +#include +#include +#include + +#include "conduit.hpp" + +namespace sina { + +namespace { +char const MIMETYPE_KEY[] = "mimetype"; +char const FILE_TYPE_NAME[] = "File"; +char const TAGS_KEY[] = "tags"; +} + +File::File(std::string uri_) : uri{std::move(uri_)} {} + +File::File(std::string uri_, conduit::Node const &asNode) : + uri{std::move(uri_)}, + mimeType{getOptionalString(MIMETYPE_KEY, asNode, FILE_TYPE_NAME)} { + if (asNode.has_child(TAGS_KEY)){ + auto tagsIter = asNode[TAGS_KEY].children(); + while(tagsIter.has_next()){ + auto &tag = tagsIter.next(); + if(tag.dtype().is_string()) + tags.emplace_back(tag.as_string()); + else { + std::ostringstream message; + message << "The optional field '" << TAGS_KEY + << "' must be an array of strings. Found '" + << tag.dtype().name() << "' instead."; + throw std::invalid_argument(message.str()); + } + } + } + + } + +void File::setMimeType(std::string mimeType_) { + File::mimeType = std::move(mimeType_); +} + +void File::setTags(std::vector tags_) { + File::tags = std::move(tags_); +} + +conduit::Node File::toNode() const { + conduit::Node asNode; + if (!mimeType.empty()) { + asNode[MIMETYPE_KEY] = mimeType; + } + if(tags.size() > 0) { + std::vector tags_copy(tags); + addStringsToNode(asNode, TAGS_KEY, tags_copy); + } + return asNode; +} + +} diff --git a/src/axom/sina/src/ID.cpp b/src/axom/sina/src/ID.cpp new file mode 100644 index 0000000000..0f98d6486c --- /dev/null +++ b/src/axom/sina/src/ID.cpp @@ -0,0 +1,55 @@ + + +/// @file + +#include "sina/ID.hpp" + +#include +#include + +namespace sina { + +ID::ID(std::string id_, IDType type_) : id{std::move(id_)}, type{type_} {} + +namespace internal { + +namespace { +/** + * Extract an ID from a given JSON object. + * + * @param parentObject the object from which to extract the ID + * @param localName the local variant of the ID field + * @param globalName the global variant of the ID field + * @return the ID from the object + */ +ID extractIDFromObject(conduit::Node const &parentObject, + std::string const &localName, std::string const &globalName) { + if (parentObject.has_child(globalName)) { + return ID{std::string(parentObject[globalName].as_string()), IDType::Global}; + } + if (parentObject.has_child(localName)) { + return ID{std::string(parentObject[localName].as_string()), IDType::Local}; + } + std::string message{ + "Could not find either of the required ID fields '"}; + message += localName + "' or '" + globalName + "'"; + throw std::invalid_argument(message); +} +} + +IDField::IDField(ID value_, std::string localName_, std::string globalName_) + : value{std::move(value_)}, localName{std::move(localName_)}, + globalName{std::move(globalName_)} {} + +IDField::IDField(conduit::Node const &parentObject, std::string localName_, + std::string globalName_) : IDField{ + extractIDFromObject(parentObject, localName_, globalName_), + std::move(localName_), std::move(globalName_)} {} + +void IDField::addTo(conduit::Node &object) const { + auto &key = value.getType() == IDType::Global ? globalName : localName; + object[key] = value.getId(); +} + +} // namespace internal +} // namespace sina diff --git a/src/axom/sina/src/Record.cpp b/src/axom/sina/src/Record.cpp new file mode 100644 index 0000000000..8743fecf95 --- /dev/null +++ b/src/axom/sina/src/Record.cpp @@ -0,0 +1,105 @@ +/// @file + +#include "sina/Record.hpp" + +#include +#include + +#include "sina/CppBridge.hpp" +#include "sina/ConduitUtil.hpp" +#include "sina/DataHolder.hpp" +#include "sina/Run.hpp" + +namespace { + +char const LOCAL_ID_FIELD[] = "local_id"; +char const GLOBAL_ID_FIELD[] = "id"; +char const TYPE_FIELD[] = "type"; +char const FILES_FIELD[] = "files"; + +// Used to preserve information when appending "standalone" library data +char const LIBRARY_DATA_ID_DATUM[] = "SINA_librarydata_id"; +char const LIBRARY_DATA_TYPE_DATUM[] = "SINA_librarydata_type"; + +} + +namespace sina { + +Record::Record(ID id_, std::string type_) : + DataHolder{}, + id{std::move(id_), LOCAL_ID_FIELD, GLOBAL_ID_FIELD}, + type{std::move(type_)} {} + +conduit::Node Record::toNode() const { + conduit::Node asNode = DataHolder::toNode(); + asNode[TYPE_FIELD] = type; + id.addTo(asNode); + // Optional fields + if(!files.empty()){ + conduit::Node fileRef; + for (auto &file : files) { + auto &n = fileRef.add_child(file.getUri()); + n.set(file.toNode()); + asNode[FILES_FIELD] = fileRef; + } + } + return asNode; +} + +Record::Record(conduit::Node const &asNode) : + DataHolder{asNode}, + id{asNode, LOCAL_ID_FIELD, GLOBAL_ID_FIELD}, + type{getRequiredString(TYPE_FIELD, asNode, "record")} { + + if(asNode.has_child(FILES_FIELD)){ + auto filesIter = asNode[FILES_FIELD].children(); + while(filesIter.has_next()){ + auto &namedFile = filesIter.next(); + files.insert(File(filesIter.name(), namedFile)); + } + } +} + +void Record::remove(File const &file) { + files.erase(file); +} + +void Record::add(File file) { + files.erase(file); + files.insert(std::move(file)); +} + +void Record::addRecordAsLibraryData(Record const &childRecord, std::string const &name) { + if(!childRecord.files.empty()){ + for (auto &file : childRecord.files) { + add(file); + } + } + auto newLibData = addLibraryData(name, childRecord.toNode()); + newLibData->add(LIBRARY_DATA_ID_DATUM, Datum{childRecord.getId().getId()}); + newLibData->add(LIBRARY_DATA_TYPE_DATUM, Datum{childRecord.type}); +} + +void RecordLoader::addTypeLoader(std::string const &type, TypeLoader loader) { + typeLoaders[type] = std::move(loader); +} + +std::unique_ptr +RecordLoader::load(conduit::Node const &recordAsNode) const { + auto loaderIter = typeLoaders.find(recordAsNode[TYPE_FIELD].as_string()); + if (loaderIter != typeLoaders.end()) { + return loaderIter->second(recordAsNode); + } + return internal::make_unique(recordAsNode); +} + +bool RecordLoader::canLoad(std::string const &type) const { + return typeLoaders.count(type) > 0; +} + +RecordLoader createRecordLoaderWithAllKnownTypes() { + RecordLoader loader; + addRunLoader(loader); + return loader; +} +} diff --git a/src/axom/sina/src/Relationship.cpp b/src/axom/sina/src/Relationship.cpp new file mode 100644 index 0000000000..0a10348f37 --- /dev/null +++ b/src/axom/sina/src/Relationship.cpp @@ -0,0 +1,37 @@ +/// @file + +#include "sina/Relationship.hpp" + +#include + +#include "sina/ConduitUtil.hpp" + +namespace sina { + +namespace { +char const GLOBAL_SUBJECT_KEY[] = "subject"; +char const LOCAL_SUBJECT_KEY[] = "local_subject"; +char const GLOBAL_OBJECT_KEY[] = "object"; +char const LOCAL_OBJECT_KEY[] = "local_object"; +char const PREDICATE_KEY[] = "predicate"; +} + +Relationship::Relationship(ID subject_, std::string predicate_, ID object_) : + subject{std::move(subject_), LOCAL_SUBJECT_KEY, GLOBAL_SUBJECT_KEY}, + object{std::move(object_), LOCAL_OBJECT_KEY, GLOBAL_OBJECT_KEY}, + predicate{std::move(predicate_)} {} + +Relationship::Relationship(conduit::Node const &asNode) : + subject{asNode, LOCAL_SUBJECT_KEY, GLOBAL_SUBJECT_KEY}, + object{asNode, LOCAL_OBJECT_KEY, GLOBAL_OBJECT_KEY}, + predicate{getRequiredString(PREDICATE_KEY, asNode, "Relationship")} {} + +conduit::Node Relationship::toNode() const { + conduit::Node relationshipNode; + relationshipNode[PREDICATE_KEY] = predicate; + subject.addTo(relationshipNode); + object.addTo(relationshipNode); + return relationshipNode; +} + +} diff --git a/src/axom/sina/src/Run.cpp b/src/axom/sina/src/Run.cpp new file mode 100644 index 0000000000..aab59837aa --- /dev/null +++ b/src/axom/sina/src/Run.cpp @@ -0,0 +1,45 @@ +/// @file + +#include "sina/Run.hpp" + +#include + +#include "sina/CppBridge.hpp" +#include "sina/ConduitUtil.hpp" + +namespace sina { + +namespace { +char const RUN_TYPE[] = "run"; +char const APPLICATION_FIELD[] = "application"; +char const VERSION_FIELD[] = "version"; +char const USER_FIELD[] = "user"; +} + +Run::Run(sina::ID id, std::string application_, std::string version_, + std::string user_) : Record{std::move(id), RUN_TYPE}, + application{std::move(application_)}, + version{std::move(version_)}, + user{std::move(user_)} {} + +Run::Run(conduit::Node const &asNode) : + Record(asNode), + application{getRequiredString(APPLICATION_FIELD, asNode, RUN_TYPE)}, + version{getOptionalString(VERSION_FIELD, asNode, RUN_TYPE)}, + user{getOptionalString(USER_FIELD, asNode, RUN_TYPE)} {} + +conduit::Node Run::toNode() const { + auto asNode = Record::toNode(); + asNode[APPLICATION_FIELD] = application; + asNode[VERSION_FIELD] = version; + asNode[USER_FIELD] = user; + return asNode; +} + +void addRunLoader(RecordLoader &loader) { + loader.addTypeLoader(RUN_TYPE, [](conduit::Node const &value) { + return internal::make_unique(value); + }); +} + +} From c8bdb5785ca69b7f877f7e03c4f4454b4a014bfd Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 24 Apr 2024 13:52:23 -0700 Subject: [PATCH 02/60] add doxygen mainpage for sina --- src/axom/sina/doxygen_mainpage.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/axom/sina/doxygen_mainpage.md diff --git a/src/axom/sina/doxygen_mainpage.md b/src/axom/sina/doxygen_mainpage.md new file mode 100644 index 0000000000..21015e1266 --- /dev/null +++ b/src/axom/sina/doxygen_mainpage.md @@ -0,0 +1,14 @@ +Sina {#sinatop} +========= + +[Sina](@ref axom::sina) provides an easy way to collect data directly within codes and output them to a common file format designed. This is accomplished in an object oriented manner through the following classes: + +- [Curve](@ref axom::sina::Curve): represents a 1D curve +- [CurveSet](@ref axom::sina::CurveSet): represents an entry in a record's "curve_set" +- [DataHolder](@ref axom::sina::DataHolder): a basic container for certain types of information +- [Datum](@ref axom::sina::Datum): tracks the value and (optionally) tags and/or units of a value associated with a Record +- [Document](@ref axom::sina::Document): represents the top-lvevl object of a JSON file conforming to the Sina schema +- [File](@ref axom::sina::File): tracks the location (URI) and mimetype of a file on the file system, plus any tags +- [Record](@ref axom::sina::Record): entry in a Document's Record list +- [Relationship](@ref axom::sina::Relationship): represents correlations between records; consists of three parts: a subject, an object, and a predicate +- [Run](@ref axom::sina::Run): a subtype of Record corresponding to a single run of an application, as specified in the Sina schema \ No newline at end of file From 4455cc7184345caab7e007d2b100e7b478aac896 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 24 Apr 2024 13:52:55 -0700 Subject: [PATCH 03/60] first pass at the cmake file for sina component --- src/axom/sina/CMakeLists.txt | 86 ++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/axom/sina/CMakeLists.txt diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt new file mode 100644 index 0000000000..fdeaec10d2 --- /dev/null +++ b/src/axom/sina/CMakeLists.txt @@ -0,0 +1,86 @@ +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level LICENSE file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) +#------------------------------------------------------------------------------ +# Sina -- API to write data to Sina's common file format +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Check necessary dependencies +#------------------------------------------------------------------------------ +axom_component_requires(NAME Sina + COMPONENTS SLIC + TPLS Conduit ) + +#------------------------------------------------------------------------------ +# Add the sina sources +#------------------------------------------------------------------------------ +set(sina_headers + include/sina/ConduitUtil.hpp + include/sina/CppBridge.hpp + include/sina/Curve.hpp + include/sina/CurveSet.hpp + include/sina/DataHolder.hpp + include/sina/Datum.hpp + include/sina/Document.hpp + include/sina/File.hpp + include/sina/ID.hpp + include/sina/Record.hpp + include/sina/Relationship.hpp + include/sina/Run.hpp + include/sina/sina.hpp ) + +set(sina_sources + src/ConduitUtil.cpp + src/Curve.cpp + src/CurveSet.cpp + src/DataHolder.cpp + src/Datum.cpp + src/Document.cpp + src/File.cpp + src/ID.cpp + src/Record.cpp + src/Relationship.cpp + src/Run.cpp ) + +# # Add Adiak header and source +# if (AXOM_BUILD_SINA_ADIAK_BINDINGS) # BRIAN LOOK AT ME RIGHT HERE -> you modified this variable at the end of the week. Is this correct? Should it be modified in the src code like you did? +# list(APPEND sina_headers include/sina/AdiakWriter.hpp) +# list(APPEND sina_sources src/AdiakWriter.cpp) +# endif() + +#------------------------------------------------------------------------------ +# Build and install the library +#------------------------------------------------------------------------------ +set(sina_depends + slic + conduit::conduit + ) + +# if(AXOM_BUILD_SINA_ADIAK_BINDINGS) +# blt_list_append(TO sidre_depends ELEMENTS adiak) +# endif() + +axom_add_library(NAME sina + SOURCES ${sina_sources} + HEADERS ${sina_headers} + DEPENDS_ON ${sina_depends} + FOLDER axom/sina) + +axom_write_unified_header(NAME sina + HEADERS ${sina_headers}) + +axom_install_component(NAME sina + HEADERS ${sina_headers}) + +#------------------------------------------------------------------------------ +# Add tests and examples +#------------------------------------------------------------------------------ +if(AXOM_ENABLE_TESTS) + add_subdirectory(tests) +endif() + +if(AXOM_ENABLE_EXAMPLES) + add_subdirectory(examples) +endif() From d8d7a493511d9e46fc59c23123868cf7a1d0d487 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 8 May 2024 14:50:53 -0700 Subject: [PATCH 04/60] move sina to axom and add cmake files so it can be built --- src/axom/CMakeLists.txt | 1 + src/axom/config.hpp.in | 1 + src/axom/core/utilities/About.cpp.in | 4 + src/axom/sina/CMakeLists.txt | 109 ++++++++++ src/axom/sina/config.hpp.in | 9 + src/axom/sina/include/AdiakWriter.hpp | 50 +++++ src/axom/sina/include/ConduitUtil.hpp | 106 ++++++++++ src/axom/sina/include/CppBridge.hpp | 40 ++++ src/axom/sina/include/Curve.hpp | 118 +++++++++++ src/axom/sina/include/CurveSet.hpp | 107 ++++++++++ src/axom/sina/include/DataHolder.hpp | 168 ++++++++++++++++ src/axom/sina/include/Datum.hpp | 194 ++++++++++++++++++ src/axom/sina/include/Document.hpp | 189 +++++++++++++++++ src/axom/sina/include/File.hpp | 104 ++++++++++ src/axom/sina/include/ID.hpp | 141 +++++++++++++ src/axom/sina/include/Record.hpp | 216 ++++++++++++++++++++ src/axom/sina/include/Relationship.hpp | 134 +++++++++++++ src/axom/sina/include/Run.hpp | 95 +++++++++ src/axom/sina/src/AdiakWriter.cpp | 267 +++++++++++++++++++++++++ src/axom/sina/src/ConduitUtil.cpp | 145 ++++++++++++++ src/axom/sina/src/Curve.cpp | 60 ++++++ src/axom/sina/src/CurveSet.cpp | 101 ++++++++++ src/axom/sina/src/DataHolder.cpp | 133 ++++++++++++ src/axom/sina/src/Datum.cpp | 170 ++++++++++++++++ src/axom/sina/src/Document.cpp | 139 +++++++++++++ src/axom/sina/src/File.cpp | 67 +++++++ src/axom/sina/src/ID.cpp | 60 ++++++ src/axom/sina/src/Record.cpp | 110 ++++++++++ src/axom/sina/src/Relationship.cpp | 41 ++++ src/axom/sina/src/Run.cpp | 49 +++++ src/cmake/axom-config.cmake.in | 1 + 31 files changed, 3129 insertions(+) create mode 100644 src/axom/sina/CMakeLists.txt create mode 100644 src/axom/sina/config.hpp.in create mode 100644 src/axom/sina/include/AdiakWriter.hpp create mode 100644 src/axom/sina/include/ConduitUtil.hpp create mode 100644 src/axom/sina/include/CppBridge.hpp create mode 100644 src/axom/sina/include/Curve.hpp create mode 100644 src/axom/sina/include/CurveSet.hpp create mode 100644 src/axom/sina/include/DataHolder.hpp create mode 100644 src/axom/sina/include/Datum.hpp create mode 100644 src/axom/sina/include/Document.hpp create mode 100644 src/axom/sina/include/File.hpp create mode 100644 src/axom/sina/include/ID.hpp create mode 100644 src/axom/sina/include/Record.hpp create mode 100644 src/axom/sina/include/Relationship.hpp create mode 100644 src/axom/sina/include/Run.hpp create mode 100644 src/axom/sina/src/AdiakWriter.cpp create mode 100644 src/axom/sina/src/ConduitUtil.cpp create mode 100644 src/axom/sina/src/Curve.cpp create mode 100644 src/axom/sina/src/CurveSet.cpp create mode 100644 src/axom/sina/src/DataHolder.cpp create mode 100644 src/axom/sina/src/Datum.cpp create mode 100644 src/axom/sina/src/Document.cpp create mode 100644 src/axom/sina/src/File.cpp create mode 100644 src/axom/sina/src/ID.cpp create mode 100644 src/axom/sina/src/Record.cpp create mode 100644 src/axom/sina/src/Relationship.cpp create mode 100644 src/axom/sina/src/Run.cpp diff --git a/src/axom/CMakeLists.txt b/src/axom/CMakeLists.txt index a78442837e..a2fe71cc7c 100644 --- a/src/axom/CMakeLists.txt +++ b/src/axom/CMakeLists.txt @@ -28,6 +28,7 @@ endif() # Add components so that later ones depend on earlier ones # (i.e. quest depends on mint, so quest follows mint) axom_add_component(COMPONENT_NAME slic DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) +axom_add_component(COMPONENT_NAME sina DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME slam DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME primal DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME sidre DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) diff --git a/src/axom/config.hpp.in b/src/axom/config.hpp.in index 59050667e1..47fe53ea5d 100644 --- a/src/axom/config.hpp.in +++ b/src/axom/config.hpp.in @@ -111,6 +111,7 @@ #cmakedefine AXOM_USE_SLAM #cmakedefine AXOM_USE_SLIC #cmakedefine AXOM_USE_SPIN +#cmakedefine AXOM_USE_SINA /* * For gradual removal of types that have been deprecated in favor of diff --git a/src/axom/core/utilities/About.cpp.in b/src/axom/core/utilities/About.cpp.in index 6e8a60d611..468f2f0ba9 100644 --- a/src/axom/core/utilities/About.cpp.in +++ b/src/axom/core/utilities/About.cpp.in @@ -102,6 +102,10 @@ void about(std::ostream &oss) comps.push_back("sidre"); #endif +#ifdef AXOM_USE_SINA + comps.push_back("sina"); +#endif + #ifdef AXOM_USE_SLAM comps.push_back("slam"); #endif diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt new file mode 100644 index 0000000000..de8d9520be --- /dev/null +++ b/src/axom/sina/CMakeLists.txt @@ -0,0 +1,109 @@ +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level LICENSE file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) +#------------------------------------------------------------------------------ +# Sina -- API to write data to Sina's common file format +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Specify necessary dependencies +# +# Note: Sina also optionally depends on Adiak when AXOM_SINA_USE_ADIAK=ON +#------------------------------------------------------------------------------ +axom_component_requires(NAME Sina + TPLS Conduit ) + +#------------------------------------------------------------------------------ +# Add Sina options +#------------------------------------------------------------------------------ +cmake_dependent_option( AXOM_SINA_USE_ADIAK "Enables Adiak support in Sina" ON + "AXOM_USE_ADIAK" OFF) + +#------------------------------------------------------------------------------ +# Generate Sina config +#------------------------------------------------------------------------------ +axom_configure_file ( config.hpp.in ${PROJECT_BINARY_DIR}/include/axom/sina/config.hpp ) + +#------------------------------------------------------------------------------ +# Specify the sina headers/sources +#------------------------------------------------------------------------------ +set(sina_headers + include/ConduitUtil.hpp + include/CppBridge.hpp + include/Curve.hpp + include/CurveSet.hpp + include/DataHolder.hpp + include/Datum.hpp + include/Document.hpp + include/File.hpp + include/ID.hpp + include/Record.hpp + include/Relationship.hpp + include/Run.hpp + ) + +set(sina_sources + src/ConduitUtil.cpp + src/Curve.cpp + src/CurveSet.cpp + src/DataHolder.cpp + src/Datum.cpp + src/Document.cpp + src/File.cpp + src/ID.cpp + src/Record.cpp + src/Relationship.cpp + src/Run.cpp + ) + +# Add Adiak header and source +if (AXOM_SINA_USE_ADIAK) + list(APPEND sina_headers include/AdiakWriter.hpp) + list(APPEND sina_sources src/AdiakWriter.cpp) +endif() + +blt_list_append( TO sina_headers ELEMENTS include/AdiakWriter.hpp IF AXOM_SINA_USE_ADIAK ) +blt_list_append( TO sina_sources ELEMENTS src/AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK ) + +#------------------------------------------------------------------------------ +# Build and install the library +#------------------------------------------------------------------------------ +set(sina_depends + core + conduit::conduit + ) + +blt_list_append( TO sina_depends ELEMENTS adiak::adiak IF AXOM_SINA_USE_ADIAK ) + +axom_add_library(NAME sina + SOURCES ${sina_sources} + HEADERS ${sina_headers} + DEPENDS_ON ${sina_depends} + FOLDER axom/sina) + +axom_write_unified_header(NAME sina + HEADERS ${sina_headers}) + +axom_install_component(NAME sina + HEADERS ${sina_headers}) + +install(FILES ${PROJECT_BINARY_DIR}/include/axom/sina/config.hpp + DESTINATION include/axom/sina + ) + +#------------------------------------------------------------------------------ +# Add tests and examples +#------------------------------------------------------------------------------ +# if(AXOM_ENABLE_TESTS) +# add_subdirectory(tests) +# endif() + +# if(AXOM_ENABLE_EXAMPLES) +# add_subdirectory(examples) +# endif() + +#------------------------------------------------------------------------------ +# Add code checks +#------------------------------------------------------------------------------ +axom_add_code_checks(PREFIX sina) diff --git a/src/axom/sina/config.hpp.in b/src/axom/sina/config.hpp.in new file mode 100644 index 0000000000..59e251c62e --- /dev/null +++ b/src/axom/sina/config.hpp.in @@ -0,0 +1,9 @@ +#ifndef SINA_CONFIG_HPP_ +#define SINA_CONFIG_HPP_ + +/// \file + +#cmakedefine AXOM_SINA_USE_ADIAK + +#endif // SINA_CONFIG_HPP_ + diff --git a/src/axom/sina/include/AdiakWriter.hpp b/src/axom/sina/include/AdiakWriter.hpp new file mode 100644 index 0000000000..209137e2fb --- /dev/null +++ b/src/axom/sina/include/AdiakWriter.hpp @@ -0,0 +1,50 @@ +#ifndef SINA_ADIAK_HPP +#define SINA_ADIAK_HPP + +/// @file + +#include "axom/sina/config.hpp" +#ifdef AXOM_SINA_USE_ADIAK + +#include +#include + +#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/include/Record.hpp" +#include "axom/sina/include/Run.hpp" + +extern "C" { +#include "adiak_tool.h" +} + +namespace axom +{ +namespace sina +{ + +/** + * The callback function to pass to Adiak in order to write collected data to a Sina Record. + * + * To register this with Adiak, you should call adiak_register_cb, passing in + * a pointer to the record as the last value. Your code should look something + * like this: + * + * \code + * Record record{ID{"my_id", IDType::Local}, "my_record_type"}; + * adiak_register_cb(1, adiak_category_all, adiakSinaCallback, 0, &record); + * \endcode + * + * Note that not everything that Sina can capture an be captured through the + * Adiak API. For example, there is currently no support in Adiak to capture + * anything like a CurveSet. As a result, to do that, you must hold on to + * the Record object passed here as the opaque value and manipulate it directly. + **/ +void adiakSinaCallback(const char *name, adiak_category_t category, const char *subcategory, adiak_value_t *value, adiak_datatype_t *t, void *opaque_value); + +} // end sina namespace +} // end axom namespace + +#endif // AXOM_SINA_USE_ADIAK + +#endif // SINA_ADIAK_HPP + diff --git a/src/axom/sina/include/ConduitUtil.hpp b/src/axom/sina/include/ConduitUtil.hpp new file mode 100644 index 0000000000..68f419b7e3 --- /dev/null +++ b/src/axom/sina/include/ConduitUtil.hpp @@ -0,0 +1,106 @@ +#ifndef SINA_JSONUTIL_HPP +#define SINA_JSONUTIL_HPP + +#include +#include + +#include "conduit.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * Get a required field from a conduit Node. + * + * @param fieldName the name of the field to get + * @param parent the parent object from which to get the field + * @param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * @return the requested field as a Node + * @throws std::invalid_argument if the field does not exist + */ +conduit::Node const &getRequiredField(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType); + +/** + * Get the value of a required field from a conduit Node. The field value + * must be a string. + * + * @param fieldName the name of the field to get + * @param parent the parent object from which to get the field + * @param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * @return the value of the requested field + * @throws std::invalid_argument if the field does not exist or is not a string + */ +std::string getRequiredString(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType); + +/** + * Get the value of a required field from a conduit Node. The field value + * must be a double. + * + * @param fieldName the name of the field to get + * @param parent the parent object from which to get the field + * @param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * @return the value of the requested field + * @throws std::invalid_argument if the field does not exist or is not a double + */ +double getRequiredDouble(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType); + +/** + * Get the value of an optional field from a conduit Node. The field value + * must be a string if it is present. + * + * @param fieldName the name of the field to get + * @param parent the parent object from which to get the field + * @param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * @return the value of the requested field, or an empty string if it + * does not exist + * @throws std::invalid_argument if the field exists but is not a string + */ +std::string getOptionalString(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType); + +/** + * Convert the given node to a vector of doubles. + * + * @param node the node to convert + * @param name the name of the node, used in error reporting + * @return the node as a list of doubles + * @throws std::invalid_argument if the node is not a list of doubles + */ +std::vector toDoubleVector(conduit::Node const &node, + std::string const &name); + +/** + * Convert the given node to a vector of strings. + * + * @param node the node to convert + * @param name the name of the node, used in error reporting + * @return the node as a list of strings + * @throws std::invalid_argument if the node is not a list of strings + */ +std::vector toStringVector(conduit::Node const &node, + std::string const &name); + +/** + * Add a vector of strings to a Node. This operation's not natively + * part of Conduit. + * + * @param parent the node to add the strings to + * @param child_name the name of the child (aka the name of the field) + * @param string_values the data values for the field + */ +void addStringsToNode(conduit::Node &parent, const std::string &child_name, + std::vector const &string_values); + +} // end namespace sina +} // end namespace axom + +#endif //SINA_JSONUTIL_HPP diff --git a/src/axom/sina/include/CppBridge.hpp b/src/axom/sina/include/CppBridge.hpp new file mode 100644 index 0000000000..65a3b237e6 --- /dev/null +++ b/src/axom/sina/include/CppBridge.hpp @@ -0,0 +1,40 @@ +#ifndef SINA_CPPBRIDGE_HPP +#define SINA_CPPBRIDGE_HPP + +/** + * @file + * + * This file contains functions which can go away once this library is + * migrated to C++14 or later. + */ + +#include +#include + +namespace axom +{ +namespace sina +{ +namespace internal +{ + +/** + * Make a unique pointer pointing to a new object. + * + * Delete when switch to C++14 in favor of std::make_unique. + * + * @tparam T the type of the object + * @tparam ParamTypes the types of the parameters + * @param params the parameters to the constructor + * @return a unique_ptr to the newly-constructed object + */ +template +std::unique_ptr make_unique(ParamTypes &&... params) { + return std::unique_ptr(new T(std::forward(params)...)); +}; + +} // end internal namespace +} // end sina namespace +} // end axom namespace + +#endif //SINA_CPPBRIDGE_HPP diff --git a/src/axom/sina/include/Curve.hpp b/src/axom/sina/include/Curve.hpp new file mode 100644 index 0000000000..2e65a82bc7 --- /dev/null +++ b/src/axom/sina/include/Curve.hpp @@ -0,0 +1,118 @@ +#ifndef SINA_CURVE_HPP +#define SINA_CURVE_HPP + +/** + * @file + * + * Contains the definition of the Curve class. + */ + +#include +#include + +#include "conduit.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * A Curve represents a 1-dimensional curve inside a \see CurveSet. + */ +class Curve { +public: + /** + * Create a Curve with the given name and values + * + * @param name the name of the curve + * @param values the curve's values + */ + Curve(std::string name, std::vector values); + + /** + * Create a Curve with the given name and values + * + * @param name the name of the curve + * @param values the curve's values + * @param numValues the number of values. + */ + Curve(std::string name, double const *values, std::size_t numValues); + + /** + * Create a Curve by deserializing a conduit node. + * + * @param name the name of the curve + * @param curveAsNode the serialized version of a curve + */ + Curve(std::string name, conduit::Node const &curveAsNode); + + /** + * Get the curve's name. + * + * @return the curve's name + */ + std::string const &getName() const { + return name; + } + + /** + * Get the values of the curve. + * + * @return the curve's values + */ + std::vector const &getValues() const { + return values; + } + + /** + * Set the units of the values. + * + * @param units the value's units + */ + void setUnits(std::string units); + + /** + * Get the units of the values. + * + * @return the value's units + */ + std::string const &getUnits() const { + return units; + } + + /** + * Set the tags for this curve. + * + * @param tags the curve's tags + */ + void setTags(std::vector tags); + + /** + * Get the tags for this curve. + * + * @return the curve's tags + */ + std::vector const &getTags() const { + return tags; + } + + /** + * Convert this curve to a Conduit node. + * + * @return a Conduit representation of this curve + */ + conduit::Node toNode() const; + +private: + std::string name; + std::vector values; + std::string units; + std::vector tags; +}; + +} // end sina namespace +} // end axom namespace + + +#endif //SINA_CURVE_HPP diff --git a/src/axom/sina/include/CurveSet.hpp b/src/axom/sina/include/CurveSet.hpp new file mode 100644 index 0000000000..cdb22e2892 --- /dev/null +++ b/src/axom/sina/include/CurveSet.hpp @@ -0,0 +1,107 @@ +#ifndef SINA_CURVESET_HPP +#define SINA_CURVESET_HPP + +/** + * @file + * + * Contains the definition of the CurveSet class. + */ + +#include +#include + +#include "conduit.hpp" + +#include "axom/sina/include/Curve.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * A CurveSet represents an entry in a record's "curve_set". + * + * A CurveSet consist of a set of independent and dependent curves. Each curve + * is a list of numbers along with optional units and tags. + * + * \see Record + * \see Curve + */ +class CurveSet { +public: + using CurveMap = std::unordered_map; + + /** + * Create a CurveSet with the given name + * + * @param name the name of the CurveSet + */ + explicit CurveSet(std::string name); + + /** + * Create a CurveSet from the given Conduit node. + * + * @param name the name of the CurveSet + * @param node the Conduit node representing the CurveSet + */ + CurveSet(std::string name, conduit::Node const &node); + + /** + * Get the name of the this CurveSet. + * + * @return the curve set's name + */ + std::string const & getName() const { + return name; + } + + /** + * Add an independent curve. + * + * @param curve the curve to add + */ + void addIndependentCurve(Curve curve); + + /** + * Add a dependent curve. + * + * @param curve the curve to add + */ + void addDependentCurve(Curve curve); + + /** + * Get a map of all the independent curves. + * + * @return a map of all the independent curves + */ + CurveMap const &getIndependentCurves() const { + return independentCurves; + } + + /** + * Get a map of all the dependent curves. + * + * @return a map of all the dependent curves + */ + CurveMap const &getDependentCurves() const { + return dependentCurves; + } + + /** + * Convert his CurveSet to a Conduit node. + * + * @return the Node representation of this CurveSet + */ + conduit::Node toNode() const; + +private: + std::string name; + CurveMap independentCurves; + CurveMap dependentCurves; +}; + +} // end sina namespace +} // end axom namespace + +#endif //SINA_CURVESET_HPP diff --git a/src/axom/sina/include/DataHolder.hpp b/src/axom/sina/include/DataHolder.hpp new file mode 100644 index 0000000000..edce5190b7 --- /dev/null +++ b/src/axom/sina/include/DataHolder.hpp @@ -0,0 +1,168 @@ +#ifndef SINA_DATAHOLDER_HPP +#define SINA_DATAHOLDER_HPP + +/** + * @file + * + * Contains the definition of the DataHolder class. + */ + +#include +#include +#include + +#include "conduit.hpp" + +#include "axom/sina/include/Datum.hpp" +#include "axom/sina/include/CurveSet.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * A DataHolder is a basic container for certain types of information. + * + * DataHolders contain curves, libraries, and data (\see Datum), and represent + * all the information a library can have associated with it. Records expand + * on DataHolders to contain additional info. + * + * \see Record + * \see LibraryData + */ +class DataHolder { +public: + using DatumMap = std::unordered_map; + using CurveSetMap = std::unordered_map; + using LibraryDataMap = std::unordered_map>; + + /** + * Construct an empty DataHolder. + */ + DataHolder() = default; + + virtual ~DataHolder() = default; + + DataHolder(DataHolder const &) = delete; + + DataHolder &operator=(DataHolder const &) = delete; + + /** + * Construct a DataHolder from its conduit Node representation. + * + * @param asNode the DataHolder as a Node + */ + explicit DataHolder(conduit::Node const &asNode); + + /** + * Get the DataHolder's data. + * + * @return the DataHolder's data + */ + DatumMap const &getData() const noexcept { + return data; + } + + /** + * Add a Datum to this DataHolder. + * + * @param name the key for the Datum to add + * @param datum the Datum to add + */ + void add(std::string name, Datum datum); + + /** + * Add a CurveSet to this DataHolder. + * + * @param curveSet the CurveSet to add + */ + void add(CurveSet curveSet); + + /** + * Get the curve sets associated with this DataHolder. + * + * @return the dataholder's curve sets + */ + CurveSetMap const &getCurveSets() const noexcept { + return curveSets; + } + + /** + * Add a new library to this DataHolder. + * + * If you try to add a library with a name that already exists, the old + * library will be replaced. + * + * @return a pointer to a new DataHolder for a library + * of the given name. + */ + std::shared_ptr addLibraryData(std::string const &name); + + std::shared_ptr addLibraryData(std::string const &name, conduit::Node existingLibraryData); + + /** + * Get all library data associated with this DataHolder. + * + * @return the dataholder's library data + */ + LibraryDataMap const &getLibraryData() const noexcept { + return libraryData; + } + + /** + * Get a specific library associated with this DataHolder. + * + * @return the dataholder's library data + */ + std::shared_ptr getLibraryData(std::string const &libraryName) { + return libraryData.at(libraryName); + } + std::shared_ptr const getLibraryData(std::string const &libraryName) const { + return libraryData.at(libraryName); + } + + /** + * Get the user-defined content of the object. + * + * @return the user-defined content + */ + conduit::Node const &getUserDefinedContent() const noexcept { + return userDefined; + } + + /** + * Get the user-defined content of the object. + * + * @return the user-defined content + */ + conduit::Node &getUserDefinedContent() noexcept { + return userDefined; + } + + /** + * Set the user-defined content of the object. + * + * @param userDefined the user-defined content. Must be an object (key/value pairs) + */ + void setUserDefinedContent(conduit::Node userDefined); + + /** + * Convert this DataHolder to its conduit Node representation. + * + * @return the Node representation of this DataHolder. + */ + virtual conduit::Node toNode() const; + + +private: + CurveSetMap curveSets; + DatumMap data; + LibraryDataMap libraryData; + conduit::Node userDefined; +}; + +} // end sina namespace +} // end axom namespace + +#endif //SINA_DATAHOLDER_HPP diff --git a/src/axom/sina/include/Datum.hpp b/src/axom/sina/include/Datum.hpp new file mode 100644 index 0000000000..ca91a84f38 --- /dev/null +++ b/src/axom/sina/include/Datum.hpp @@ -0,0 +1,194 @@ +#ifndef SINA_DATUM_HPP +#define SINA_DATUM_HPP + +/// @file + +#include +#include + +#include "axom/sina/include/ConduitUtil.hpp" +#include "conduit.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * Represents whether a Datum is a String, Scalar (double), array of Strings, + * or array of Scalars. + */ +enum class ValueType { + String, + Scalar, + StringArray, + ScalarArray +}; + +/** + * A Datum tracks the value and (optionally) tags and/or units of a + * value associated with a Record, e.g. a scalar, a piece of metadata, + * or an input parameter. In the Sina schema, a Datum always + * belongs to a Record or one of Record's inheriting types. + * + * Every Datum must have a value; units and tags are optional. + * + * The value of a Datum may be a string, a double, an array of strings, + * or an array of doubles. + * + * \code + * sina::Datum myDatum{12.34}; + * std::string value = "foobar"; + * sina::Datum myOtherDatum{value}; + * std::vector scalars = {1, 2, 20.0}; + * sina::Datum myArrayDatum{scalars}; + * //prints 1, corresponding to Scalar + * std::cout << static_cast::type>(myDatum.getType()) << std::endl; + * //prints 0, corresponding to String + * std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; + * //prints 3, corresponding to ScalarArray + * std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; + * myRecord->add(myDatum); + * myOtherDatum.setUnits("km/s"); + * myRecord->add(myOtherDatum); + * std::vector tags = {"input", "core"}; + * myArrayDatum.setTags(tags); + * myRecord->add(myArrayDatum); + * \endcode + */ +class Datum { +public: + /** + * Construct a new Datum. + * + * @param value the string value of the datum + */ + Datum(std::string value); + + /** + * Construct a new Datum. + * + * @param value the double value of the datum + */ + Datum(double value); + + /** + * Construct a new Datum. + * + * @param value the string array value of the datum + */ + Datum(std::vector value); + + /** + * Construct a new Datum. + * + * @param value the scalar array value of the datum + */ + Datum(std::vector value); + + /** + * Construct a Datum from its Node representation. + * + * @param asNode the Datum as conduit Node + */ + explicit Datum(conduit::Node const &asNode); + + /** + * Get the string value of the Datum. + * + * @return the string value + */ + std::string const &getValue() const noexcept { + return stringValue; + } + + /** + * Get the scalar value of the Datum. + * + * @return the scalar value + */ + double const &getScalar() const noexcept { + return scalarValue; + } + + /** + * Get the string array value of the Datum. + * + * @return the string vector value + */ + std::vector const &getStringArray() const noexcept { + return stringArrayValue; + } + + /** + * Get the scalar array value of the Datum. + * + * @return the scalar vector value + */ + std::vector const &getScalarArray() const noexcept { + return scalarArrayValue; + } + + /** + * Get the tags of the Datum + * + * @return the tags of the value + */ + std::vector const &getTags() const noexcept { + return tags; + } + + /** + * Set the tags of the Datum + * + * @param tags the tags of the value + */ + void setTags(std::vector tags); + + /** + * Get the units of the Datum + * + * @return the units of the value + */ + std::string const &getUnits() const noexcept { + return units; + } + + /** + * Set the units of the Datum + * + * @param units the units of the value + */ + void setUnits(std::string units); + + + /** + * Get the type of the Datum + * + * @return the type of the value + */ + ValueType getType() const noexcept { + return type; + } + + /** + * Convert this Datum to its conduit Node representation. + * + * @return the Node representation of this Datum. + */ + conduit::Node toNode() const; + +private: + std::string stringValue; + double scalarValue; + std::vector stringArrayValue; + std::vector scalarArrayValue; + std::string units; + std::vector tags; + ValueType type; +}; + +} // end sina namespace +} // end axom namespace + +#endif //SINA_DATUM_HPP diff --git a/src/axom/sina/include/Document.hpp b/src/axom/sina/include/Document.hpp new file mode 100644 index 0000000000..231a832bd0 --- /dev/null +++ b/src/axom/sina/include/Document.hpp @@ -0,0 +1,189 @@ +#ifndef SINA_DOCUMENT_HPP +#define SINA_DOCUMENT_HPP + +/// @file + +#include +#include + +#include "conduit.hpp" + +#include "axom/sina/include/Record.hpp" +#include "axom/sina/include/Relationship.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * A Document represents the top-level object of a JSON file conforming to the + * Sina schema. When serialized, these documents can be ingested into a + * Sina database and used with the Sina tool. + * + * Documents contain at most two objects: a list of Records and a list of Relationships. A simple, empty document: + * \code{.json} + * { + * "records": [], + * "relationships": [] + * } + * \endcode + * + * The "records" list can contain Record objects and their inheriting types, such as Run (for a full list, please see + * the inheritance diagram in the Record documentation). The "relationships" list can contain Relationship objects. + * + * Documents can be assembled programatically and/or generated from existing JSON. An example of an assembled + * Document is provided on the main page. To load a Document from an existing JSON file: + * \code + * sina::Document myDocument = sina::loadDocument("path/to/infile.json"); + * \endcode + * + * To generate a Document from a JSON string and vice versa: + * \code + * std::string my_json = "{\"records\":[{\"type\":\"run\",\"id\":\"test\"}],\"relationships\":[]}"; + * sina::Document myDocument = sina::Document(my_json, sina::createRecordLoaderWithAllKnownTypes()); + * std::cout << myDocument.toJson() << std::endl;); + * \endcode + * + * You can add further entries to the Document using add(): + * \code + * std::unique_ptr myRun{new sina::Run{someID, "My Sim Code", "1.2.3", "jdoe"}}; + * sina::Relationship myRelationship{someID, "comes before", someOtherID}; + * myDocument.add(myRun); + * myDocument.add(myRelationship); + * \endcode + * + * You can also export your Document to file: + * \code + * sina::saveDocument(myDocument, "path/to/outfile.json") + * \endcode + * + */ +class Document { +public: + using RecordList = std::vector>; + using RelationshipList = std::vector; + + Document() = default; + + // Since we hold pointers to polymorphic objects, we can't support + // copying or assignment + Document(Document const &) = delete; + + Document &operator=(Document const &) = delete; + + Document(Document &&) = default; + + Document &operator=(Document &&) = default; + + /** + * Create a Document from its Conduit Node representation + * + * @param asNode the Document as a Node + * @param recordLoader an RecordLoader to use to load the different + * types of records which may be in the document + */ + Document(conduit::Node const &asNode, RecordLoader const &recordLoader); + + /** + * Create a Document from a JSON string representation + * + * @param asJson the Document as a JSON string + * @param recordLoader an RecordLoader to use to load the different + * types of records which may be in the document + */ + Document(std::string const &asJson, RecordLoader const &recordLoader); + + /** + * Add the given record to this document. + * + * @param record the record to add + */ + void add(std::unique_ptr record); + + /** + * Get the list of records currently in this document. + * + * @return the list of records + */ + RecordList const &getRecords() const noexcept { + return records; + } + + /** + * Add a relationship to this document + * + * @param relationship the relationship to add + */ + void add(Relationship relationship); + + /** + * Get the list of relationships in this document. + * + * @return the list of relationships + */ + RelationshipList const &getRelationships() const noexcept { + return relationships; + } + + + /** + * Convert this document to a conduit Node. + * + * @return the contents of the document as a Node + */ + conduit::Node toNode() const; + + /** + * Convert this document to a JSON string. + * + * @return the contents of the document as a JSON string + */ + std::string toJson(conduit::index_t indent=0, conduit::index_t depth=0, + const std::string &pad="", const std::string &eoe="") const; + +private: + /** + * Constructor helper method, extracts info from a conduit Node. + */ + void createFromNode(conduit::Node const &asNode, + RecordLoader const &recordLoader); + RecordList records; + RelationshipList relationships; +}; + +/** + * Save the given Document to the specified location. If the given file exists, + * it will be overwritten. + * + * @param document the Document to save + * @param fileName the location to which to save the file + * @throws std::ios::failure if there are any IO errors + */ +void saveDocument(Document const &document, std::string const &fileName); + +/** + * Load a document from the given path. Only records which this library + * knows about will be able to be loaded. + * + * @param path the file system path from which to load the document + * @return the loaded Document + */ +Document loadDocument(std::string const &path); + +/** + * Load a document from the given path. + * + * @param path the file system path from which to load the document + * @param recordLoader the RecordLoader to use to load the different types + * of records + * @return the loaded Document + */ +Document loadDocument(std::string const &path, + RecordLoader const &recordLoader); + +} // end sina namespace +} // end axom namespace + + +#endif //SINA_DOCUMENT_HPP diff --git a/src/axom/sina/include/File.hpp b/src/axom/sina/include/File.hpp new file mode 100644 index 0000000000..c40ff3a8a8 --- /dev/null +++ b/src/axom/sina/include/File.hpp @@ -0,0 +1,104 @@ +#ifndef SINA_FILE_HPP +#define SINA_FILE_HPP + +/// @file + +#include +#include + +#include "conduit.hpp" + +namespace axom +{ +namespace sina +{ +/** + * A File tracks 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. + * + * \code + * sina::File myFile{"/path/to/file.png"}; + * myFile.setMimeType("image/png"); + * sina::File myOtherFile{"/path/to/other/file.txt"}; + * myOtherFile.setTags({"these","are","tags"}); + * myRecord->add(myFile); + * myRecord->add(myOtherFile); + * \endcode + */ +class File { +public: + /** + * Construct a new File. + * + * @param uri the location of the file + */ + explicit File(std::string uri); + + /** + * Construct a new File. + * + * @param uri the uri for a file + * @param asNode the Node representation of the file's additional info + */ + File(std::string uri, conduit::Node const &asNode); + + /** + * Get the File's URI. + * + * @return the URI + */ + std::string const &getUri() const noexcept { + return uri; + } + + /** + * Get the File's MIME type. + * + * @return the MIME type + */ + std::string const &getMimeType() const noexcept { + return mimeType; + } + + /** + * Get the File's tags. + * + * @return the tags + */ + std::vector const &getTags() const noexcept { + return tags; + } + + /** + * Set the File's MIME type. + * + * @param mimeType the MIME type + */ + void setMimeType(std::string mimeType); + + /** + * Set the File's tags. + * + * @param tags the File's tags + */ + void setTags(std::vector tags); + + /** + * Convert this File to its conduit Node representation. + * + * @return the File in its Node representation + */ + conduit::Node toNode() const; + +private: + std::string uri; + std::string mimeType; + std::vector tags; +}; + +} // end sina namespace +} // end axom namespace + +#endif //SINA_FILE_HPP diff --git a/src/axom/sina/include/ID.hpp b/src/axom/sina/include/ID.hpp new file mode 100644 index 0000000000..5982ca2f5a --- /dev/null +++ b/src/axom/sina/include/ID.hpp @@ -0,0 +1,141 @@ +#ifndef SINA_ID_HPP +#define SINA_ID_HPP + +/** + * @file + * + * The Sina schema allows records to have either a local ID or a global ID. + * When a global ID is specified, that will be used in the database. When a + * local ID is specified, an ID will be automatically generated when inserting + * the record into the database. + */ + +#include + +#include "conduit.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * Represents whether an ID is local or global. + */ +enum class IDType { + Local, + Global +}; + +/** + * An ID is used to represent the ID of an record. This class holds both the + * actual ID and whether it is a local or global ID. + */ +class ID { +public: + /** + * Create a new ID. + * @param id the actual value of the ID + * @param type whether the ID is local or global + */ + ID(std::string id, IDType type); + + /** + * Get the value of the ID. + * + * @return the actual ID + */ + std::string const &getId() const noexcept { + return id; + } + + /** + * Get the type of the ID. + * + * @return whether the ID is local or global + */ + IDType getType() const noexcept { + return type; + } + +private: + std::string id; + IDType type; +}; + +namespace internal +{ + +/** + * IDField instances are used to describe a pair of ID fields in a schema + * object which correspond to global and local names for the field. For + * example, the "id" and "local_id" fields in records, or the + * "subject"/"local_subject" and "object"/"local_object" pairs in + * relationships. + */ +class IDField { +public: + /** + * Construct a new IDField. + * + * @param value the value of the ID + * @param localName the name of the local variant of the field + * @param globalName the name of the global variant of the field + */ + IDField(ID value, std::string localName, std::string globalName); + + /** + * Construct an IDField by looking for its values in a conduit Node. + * + * @param parentObject the conduit Node containing the ID field + * @param localName the local name of the field + * @param globalName the global name of the field + */ + IDField(conduit::Node const &parentObject, std::string localName, + std::string globalName); + + /** + * Get the value of this field. + * + * @return the ID describing the field's value + */ + ID const &getID() const noexcept { + return value; + } + + /** + * Get the name to use for this field when the ID is local. + * + * @return the name of the local ID field + */ + std::string const &getLocalName() const noexcept { + return localName; + } + + /** + * Get the name to use for this field when the ID is global. + * + * @return the name of the global ID field + */ + std::string const &getGlobalName() const noexcept { + return globalName; + } + + /** + * Add this field to the given Node. + * + * @param object the Node to which to add the field + */ + void addTo(conduit::Node &object) const; + +private: + ID value; + std::string localName; + std::string globalName; +}; + +} // end internal namespace +} // end sina namespace +} // end axom namespace + +#endif //SINA_ID_HPP diff --git a/src/axom/sina/include/Record.hpp b/src/axom/sina/include/Record.hpp new file mode 100644 index 0000000000..f00ceaf574 --- /dev/null +++ b/src/axom/sina/include/Record.hpp @@ -0,0 +1,216 @@ +#ifndef SINA_RECORD_HPP +#define SINA_RECORD_HPP + +/// @file + +#include +#include +#include +#include +#include +#include + +#include "conduit.hpp" + +#include "axom/sina/include/ID.hpp" +#include "axom/sina/include/DataHolder.hpp" +#include "axom/sina/include/CurveSet.hpp" +#include "axom/sina/include/Datum.hpp" +#include "axom/sina/include/File.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * FileEqualByURI is used to store files in a Record. + */ +struct FileEqualByURI { + bool operator()(const File &file1, const File &file2) const { + return file1.getUri() == file2.getUri(); + } +}; + +/** + * FileHashByURI is used to store files in a Record. Files are stored according + * to the hash of their URI. + */ +struct FileHashByURI { + size_t operator()(const File &file) const { + return std::hash()(file.getUri()); + } +}; + +/** + * The Record class represents an entry in a Document's Record list. Records represent the data to be stored + * (as opposed to the relationships between data)--natural scopes for Records include things like a single run + * of an application, an msub job, 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), etc. + * + * Each Record must have a type and an id. Each Record can also have a list of + * File objects and a map of Datum objects. + * + * \code + * sina::ID myID{"my_record", sina::IDType::Local}; + * std::unique_ptr myRecord{new sina::Record{myID, "my_type"}}; + * std::vector myTags{"input"}; + * sina::Datum myDatum{12, myTags}; + * myRecord->add("my_scalar",std::move(myDatum)); + * std::cout << myRecord->toNode().to_json() << std::endl; + * \endcode + * + * The output would be: + * \code{.json} + * {"local_id":"my_record","type":"my_type","data":{"my_scalar":{"tags":["input"],"value":12.0}}} + * \endcode + */ +class Record : public DataHolder { +public: + using FileSet = std::unordered_set; + + /** + * Construct a new Record. + * + * @param id the ID of the record + * @param type the type of the record + */ + Record(ID id, std::string type); + + /** + * Construct a Record from its conduit Node representation. + * + * @param asNode the Record as a Node + */ + explicit Record(conduit::Node const &asNode); + + Record(Record const &) = delete; + + Record &operator=(Record const &) = delete; + + /** + * Get the Record's ID. + * + * @return the ID + */ + ID const &getId() const noexcept { + return id.getID(); + } + + /** + * Get the Record's type. + * + * @return the Record's type + */ + std::string const &getType() const noexcept { + return type; + } + + /** + * Remove a File from this record. + * + * @param file the File to remove + */ + void remove(File const& file); + + + + using DataHolder::add; + /** + * Add a File to this record. + * + * @param file the File to add + */ + void add(File file); + + + /** + * Get the files associated with this record. + * + * @return the record's files + */ + FileSet const &getFiles() const noexcept { + return files; + } + + /** + * Convert this record to its conduit Node representation. + * + * @return the Node representation of this record. + */ + conduit::Node toNode() const override; + + /** + * Add another record to this one as library data. + * + * Useful for libraries that can run in standalone mode; the host + * simply calls this method on the record the library produces. + * Merges file lists. + */ + void addRecordAsLibraryData(Record const &childRecord, std::string const &name); + +private: + internal::IDField id; + std::string type; + FileSet files; +}; + + +/** + * A RecordLoader is used to convert conduit::Node instances which represent + * Sina Records into instances of their corresponding sina::Record + * subclasses. For convenience, a RecordLoader capable of handling Records of all known + * types can be created using createRecordLoaderWithAllKnownTypes: + * + * \code + * sina::Document myDocument = sina::Document(jObj, sina::createRecordLoaderWithAllKnownTypes()); + * \endcode + */ +class RecordLoader { +public: + /** + * A TypeLoader is a function which converts records of a specific type + * to their corresponding sub classes. + */ + using TypeLoader = std::function( + conduit::Node const &)>; + + /** + * Add a function for loading records of the specified type. + * + * @param type the type of records this function can load + * @param loader the function which can load the records + */ + void addTypeLoader(std::string const &type, TypeLoader loader); + + /** + * Load a sina::Record from its conduit Node representation. + * + * @param recordAsNode the Record as a Node + * @return the Record + */ + std::unique_ptr load(conduit::Node const &recordAsNode) const; + + /** + * Check whether this loader can load records of the given type. + * + * @param type the type of the records to check + * @return whether records of the given type can be loaded + */ + bool canLoad(std::string const &type) const; + +private: + std::unordered_map typeLoaders; +}; + +/** + * Create a RecordLoader which can load records of all known types. + * + * @return the newly-created loader + */ +RecordLoader createRecordLoaderWithAllKnownTypes(); + +} // end sina namespace +} // end axom namespace + +#endif //SINA_RECORD_HPP diff --git a/src/axom/sina/include/Relationship.hpp b/src/axom/sina/include/Relationship.hpp new file mode 100644 index 0000000000..f99e2797ff --- /dev/null +++ b/src/axom/sina/include/Relationship.hpp @@ -0,0 +1,134 @@ +#ifndef SINA_RELATIONSHIP_HPP +#define SINA_RELATIONSHIP_HPP + +/** + * @file + * + * Contains the definition of the Relationship class. + */ + +#include + +#include "conduit.hpp" + +#include "axom/sina/include/ID.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * A Relationship consists of three parts: a subject, an object, and a predicate. It describes + * a relationship between two Records (and/or Record inheritors, e.g. Run). + * The subject and object must be IDs referring to valid records, while the predicate may be + * any string. + * + * In describing the connection between objects, a Relationship is read as + * " ". For example, in the relationship + * "Alice knows Bob", "Alice" is the subject, "knows" is the predicate, and + * "Bob" is the object. For further examples: + * + * - Task_22 contains Run_1024 + * - msub_1_1 describes out_j_1_1 + * - Carlos sends an email to Dani + * - local_task_12 runs before local_run_14 + * + * Note that Relationships are described in the active voice. **Avoiding the passive voice + * in predicates is recommended**, as this keeps the "direction" of the relationship constant. + * An example of a passively-voiced Relationship is "Dani is emailed by Carlos". Instead, + * this should be phrased as "Carlos emails Dani". + * + * If assembling Relationships programatically, it may be useful to reference the + * ID documentation. + * + * \code + * sina::ID task22{"Task_22", sina::IDType::Global}; + * sina::ID run1024{"Run_1024", sina::IDType::Global}; + * sina::Relationship myRelationship{task22, "contains", run1024}; + * std::cout << myRelationship.toNode().to_json() << std::endl; + * \endcode + * + * This would output: + * \code{.json} + * {"object":"Run_1024","predicate":"contains","subject":"Task_22"} + * \endcode + * + * As with any other Sina ID, the subject or object may be either local (uniquely refer to one object + * in a Sina file) or global (uniquely refer to one object in a database). Local IDs are replaced with + * global ones upon ingestion; all Relationships referring to that Local ID (as well as the Record possessing + * that ID) will be updated to use the same global ID. + * + * \code + * sina::ID myLocalID{"my_local_run", sina::IDType::Local}; + * std::unique_ptr myRun{new sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; + * sina::Relationship myRelationship{task22, "contains", myLocalID}; + * \endcode + * + * In the above code, "my_local_run" would be replaced by a global ID on ingestion. If this new global ID was, + * for example, "5Aed-BCds-23G1", then "my_local_run" would automatically be replaced by "5Aed-BCds-23G1" in both + * the Record and Relationship entries. + */ +class Relationship { +public: + /** + * Create a new relationship. + * + * @param subject the subject of the relationship + * @param predicate the predicate describing the relationship from the + * subject to the object + * @param object the object of the relationship + */ + Relationship(ID subject, std::string predicate, ID object); + + /** + * Create a Relationship object from its representation as a conduit Node. + * + * @param asNode the relationship as a Node + */ + explicit Relationship(conduit::Node const &asNode); + + /** + * Get the subject. + * + * @return the subject + */ + ID const &getSubject() const noexcept { + return subject.getID(); + } + + /** + * Get the object. + * + * @return the object + */ + ID const &getObject() const noexcept { + return object.getID(); + } + + /** + * Get the predicate. + * + * @return the predicate + */ + std::string const &getPredicate() const noexcept { + return predicate; + } + + /** + * Convert this Relationship to its Node representation. + * + * @return this relationship as a conduit Node + */ + conduit::Node toNode() const; + +private: + internal::IDField subject; + internal::IDField object; + std::string predicate; +}; + +} // end sina namespace +} // end axom namespace + +#endif //SINA_RELATIONSHIP_HPP diff --git a/src/axom/sina/include/Run.hpp b/src/axom/sina/include/Run.hpp new file mode 100644 index 0000000000..b498bd1a7d --- /dev/null +++ b/src/axom/sina/include/Run.hpp @@ -0,0 +1,95 @@ +#ifndef SINA_RUN_HPP +#define SINA_RUN_HPP + +/// @file + +#include "axom/sina/include/Record.hpp" + +namespace axom +{ +namespace sina +{ + +/** + * A Run is a subtype of Record corresponding to a single run of an application, as + * specified in the Sina schema. A Run has a few additional fields required in addition + * to the id required by a Record (type is automatically set to "run"): + * + * - 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 + * + * To create a Run: + * \code + * sina::ID run1ID{"run1", sina::IDType::Local}; + * std::unique_ptr run1{new sina::Run{run1ID, "My Sim Code", "1.2.3", "jdoe"}}; + * \endcode + * + */ +class Run : public Record { +public: + /** + * Create a new Run. + * + * @param id the run's ID + * @param application the application that was run + * @param version (optional) the version of the application + * @param user (optional) the user who executed the run + */ + Run(ID id, std::string application, std::string version = "", std::string user = ""); + + /** + * Create a Run from its representation as a conduit Node + * + * @param asNode the run as a Node + */ + explicit Run(conduit::Node const &asNode); + + /** + * Get the application that was run. + * + * @return the application's name + */ + std::string const &getApplication() const { + return application; + } + + /** + * Get the version of the application that was run. + * + * @return the application's version + */ + std::string const &getVersion() const { + return version; + } + + /** + * Get the name of the user who ran the application. + * + * @return the user's name + */ + std::string const &getUser() const { + return user; + } + + conduit::Node toNode() const override; + +private: + std::string application; + std::string version; + std::string user; +}; + +/** + * Add a type loader to the given RecordLoader for loading Run instances. + * + * @param loader the RecordLoader to which to add the function for loading + * Run instances. + */ +void addRunLoader(RecordLoader &loader); + +} // end sina namespace +} // end axom namespace + + +#endif //SINA_RUN_HPP diff --git a/src/axom/sina/src/AdiakWriter.cpp b/src/axom/sina/src/AdiakWriter.cpp new file mode 100644 index 0000000000..0a78c4fef4 --- /dev/null +++ b/src/axom/sina/src/AdiakWriter.cpp @@ -0,0 +1,267 @@ +/// @file + +#include "axom/sina/include/AdiakWriter.hpp" + +#ifdef AXOM_SINA_USE_ADIAK + +#include +#include +#include +#include +#include + +extern "C" { +#include "adiak_tool.h" +} + +#include "axom/sina/include/CppBridge.hpp" +#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/include/Record.hpp" +#include "axom/sina/include/Datum.hpp" +#include "axom/sina/include/Document.hpp" + +namespace axom +{ +namespace sina +{ +namespace +{ + +/** +* Adiak has a much wider array of supported types than Sina. We will convert +* Adiak types to ones Sina understands; SinaType holds the possibilities. +**/ +enum SinaType {sina_scalar, sina_string, sina_list, sina_file, sina_unknown}; + +/** +* Add a sina::Datum object to a Record. These are the sina equivalent +* of an Adiak datapoint. Since we track slightly different info, this function +* harvests what it can and hands it off to the Record. +**/ +template +void addDatum(const std::string &name, T sina_safe_val, const std::vector &tags, sina::Record *record){ + sina::Datum datum{sina_safe_val}; + datum.setTags(std::move(tags)); + record->add(name, datum); +} + +/** +* Add a sina::File object to our current Record. Adiak stores paths, +* which are essentially the same as Sina's idea of storing files. +**/ +void addFile(const std::string &name, const std::string &uri, sina::Record *record){ + // We don't care about type here, there's only one adiak type that acts as a file + sina::File file{uri}; + file.setTags(std::vector{name}); + record->add(std::move(file)); +} + +/** +* Given an Adiak type, return its corresponding Sina type. +**/ +SinaType findSinaType(adiak_datatype_t *t){ + switch (t->dtype){ + case adiak_long: + case adiak_ulong: + case adiak_int: + case adiak_uint: + case adiak_double: + case adiak_timeval: + return sina_scalar; + case adiak_date: + case adiak_version: + case adiak_string: + case adiak_catstring: + return sina_string; + case adiak_path: + return sina_file; + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + return sina_list; + case adiak_type_unset: + return sina_unknown; + default: + return sina_unknown; + } +} + +/** +* Several Adiak types become what Sina views as a "scalar" (a double). +* Manage the conversions from various Adiak types to the final double +* representation +**/ +double toScalar(adiak_value_t *val, adiak_datatype_t *adiak_type){ + switch (adiak_type->dtype){ + case adiak_long: + case adiak_ulong: + return static_cast(val->v_long); + case adiak_int: + case adiak_uint: + return static_cast(val->v_int); + case adiak_double: + return val->v_double; + case adiak_timeval: { + struct timeval *tval = static_cast(val->v_ptr); + return static_cast(tval->tv_sec) + + (static_cast(tval->tv_usec) / 1000000.0); + } + // None of the rest of these should ever be reachable, so special error message + case adiak_date: + case adiak_version: + case adiak_string: + case adiak_catstring: + case adiak_path: + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + case adiak_type_unset: { + std::string msg("Logic error, contact maintainer: Adiak-to-Sina double converter given "); + char *s = adiak_type_to_string(adiak_type, 1); + msg += s; + free(s); + throw std::runtime_error(msg); + } + default: + throw std::runtime_error("Adiak-to-Sina double converter given something not convertible to double"); + } +} + +/** +* Some Adiak types become what Sina views as a string. +* Manage the conversions from various Adiak types to said string. +**/ +std::string toString(adiak_value_t *val, adiak_datatype_t *adiak_type){ + switch (adiak_type->dtype){ + case adiak_date: { + char datestr[512]; + signed long seconds_since_epoch = static_cast(val->v_long); + struct tm *loc = localtime(&seconds_since_epoch); + strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z", loc); + return static_cast(datestr); + } + case adiak_catstring: + case adiak_version: + case adiak_string: + case adiak_path: + return std::string(static_cast(val->v_ptr)); + case adiak_long: + case adiak_ulong: + case adiak_int: + case adiak_uint: + case adiak_double: + case adiak_timeval: + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + case adiak_type_unset:{ + std::string msg("Logic error, contact maintainer: Adiak-to-Sina string converter given "); + char *s = adiak_type_to_string(adiak_type, 1); + msg += s; + free(s); + throw std::runtime_error(msg); + } + default: + throw std::runtime_error("Adiak-to-Sina string converter given something not convertible to string"); + } +} + +/** +* Some Adiak types become a list of some form. Sina, being concerned +* with queries and visualization, only handles lists that are all scalars +* or all strings. Manage conversions from various Adiak list types that +* contain scalars to a simple list (vector) of scalars. +**/ +std::vector toScalarList(adiak_value_t *subvals, adiak_datatype_t *t){ + std::vector sina_safe_list; + for (int i = 0; i < t->num_elements; i++) { + sina_safe_list.emplace_back(toScalar(subvals+i, t->subtype[0])); + } + return sina_safe_list; +} + + +/** +* Partner method to toScalarList, invoked when the children of an adiak list +* type are strings (according to Sina). +**/ +std::vector toStringList(adiak_value_t *subvals, adiak_datatype_t *t){ + std::vector sina_safe_list; + for (int i = 0; i < t->num_elements; i++) { + sina_safe_list.emplace_back(toString(subvals+i, t->subtype[0])); + } + return sina_safe_list; +} + +} + +void adiakSinaCallback(const char *name, adiak_category_t, const char *subcategory, adiak_value_t *val, adiak_datatype_t *adiak_type, void *void_record) +{ + const SinaType sina_type = findSinaType(adiak_type); + sina::Record *record = static_cast(void_record); + std::vector tags; + if(subcategory && subcategory[0]!='\0'){ + tags.emplace_back(subcategory); + } + switch (sina_type) { + case sina_unknown: + // If we don't know what it is, we can't store it, so as above... + throw std::runtime_error("Unknown Adiak type cannot be added to Sina record."); + case sina_scalar: { + char * s = adiak_type_to_string(adiak_type, 1); + tags.emplace_back(s); + free(s); + addDatum(name, toScalar(val, adiak_type), tags, record); + break; + } + case sina_string: { + char * s = adiak_type_to_string(adiak_type, 1); + tags.emplace_back(s); + free(s); + addDatum(name, toString(val, adiak_type), tags, record); + break; + } + case sina_file: + addFile(name, toString(val, adiak_type), record); + break; + case sina_list: { + // Sina doesn't really know/care the difference between list, tuple, set + // Further simplification: everything has to be the same type + // Even further simplification: nothing nested. In the future, depth>1 lists + // should be sent to user_defined + adiak_value_t *subvals = static_cast(val->v_ptr); + SinaType list_type = findSinaType(adiak_type->subtype[0]); + char * s = adiak_type_to_string(adiak_type->subtype[0], 1); + tags.emplace_back(s); + free(s); + switch (list_type) { + case sina_string: + addDatum(name, toStringList(subvals, adiak_type), tags, record); + break; + // Weird case wherein we're given a list of filenames, which we can somewhat manage + case sina_file: + for (int i=0; i < adiak_type->num_elements; i++) { + addFile(name, toString(subvals+i, adiak_type->subtype[0]), record); + } + break; + case sina_scalar: + addDatum(name, toScalarList(subvals, adiak_type), tags, record); + break; + case sina_unknown: + throw std::runtime_error("Type must not be unknown for list entries to be added to a Sina record"); + case sina_list: + throw std::runtime_error("Lists must not be nested for list entries to be added to a Sina record"); + default: + throw std::runtime_error("Type must be set for list entries to be added to a Sina record"); + } + } + } +} + +} // end sina namespace +} // end axom namespace + +#endif // AXOM_SINA_USE_ADIAK diff --git a/src/axom/sina/src/ConduitUtil.cpp b/src/axom/sina/src/ConduitUtil.cpp new file mode 100644 index 0000000000..d8e394d50c --- /dev/null +++ b/src/axom/sina/src/ConduitUtil.cpp @@ -0,0 +1,145 @@ +#include "axom/sina/include/ConduitUtil.hpp" + +#include +#include +#include +#include +#include + +#include "conduit.hpp" + +namespace axom +{ +namespace sina +{ + +namespace +{ +/** + * Get the given field as string. If it is not a string, an exception is + * thrown with a user-friendly message. + * + * @param field the value of the field + * @param fieldName the name of the field + * @param parentType the name of the parent which contained the field + * @return the avlue of the field + * @throws std::invalid_argument if the field is not a string + */ +std::string getExpectedString(conduit::Node const &field, + std::string const &fieldName, + std::string const &parentType) { + if (!field.dtype().is_string()) { + std::ostringstream message; + message << "The field '" << fieldName + << "' for objects of type '" << parentType + << "' must be a string, was '" + << field.dtype().name() << "'"; + throw std::invalid_argument(message.str()); + } + return field.as_string(); +} +} + +conduit::Node const &getRequiredField(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType) { + if (!parent.has_child(fieldName)) { + std::ostringstream message; + message << "The field '" << fieldName + << "' is required for objects of type '" << parentType << "'"; + throw std::invalid_argument(message.str()); + } + return parent.child(fieldName); +} + +std::string getRequiredString(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType) { + conduit::Node const &field = getRequiredField(fieldName, parent, parentType); + return getExpectedString(field, fieldName, parentType); +} + +std::string getOptionalString(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType) { + if (!parent.has_child(fieldName) || parent.child(fieldName).dtype().is_empty()) { + return ""; + } + return getExpectedString(parent.child(fieldName), fieldName, parentType); +} + +double getRequiredDouble(std::string const &fieldName, + conduit::Node const &parent, std::string const &parentType) { + auto &ref = getRequiredField(fieldName, parent, parentType); + if (!ref.dtype().is_number()) { + std::ostringstream message; + message << "The field '" << fieldName + << "' for objects of type '" << parentType + << "' must be a double"; + throw std::invalid_argument(message.str()); + } + return ref.as_double(); +} + +void addStringsToNode(conduit::Node &parent, std::string const &child_name, + std::vector const &string_values){ + // If the child already exists, add_child returns it + conduit::Node &child_node = parent.add_child(child_name); + for(auto &value : string_values) + { + auto &list_entry = child_node.append(); + list_entry.set(value); + } + + // If there were no children, this will be a null node rather than a list. + // We prefer empty lists to null values in our serialized JSON, so force + // this to be a list + if (child_node.number_of_children() == 0) { + child_node.set_dtype(conduit::DataType::list()); + } +} + +std::vector toDoubleVector(conduit::Node const &node, + std::string const &name) { + if (node.dtype().is_list() && node.dtype().number_of_elements() == 0) { + return std::vector{}; + } + conduit::Node asDoubles; + try { + node.to_double_array(asDoubles); + } catch (conduit::Error const &err) { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of doubles" << err.what(); + throw std::invalid_argument(errStream.str()); + } + double const *start = asDoubles.as_double_ptr(); + auto count = static_cast::size_type>( + asDoubles.dtype().number_of_elements()); + return std::vector{start, start + count}; +} + +std::vector toStringVector(conduit::Node const &node, + std::string const &name) { + std::vector converted; + if (!node.dtype().is_list()) { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of strings. It is not a list. " + << node.to_json_default(); + throw std::invalid_argument(errStream.str()); + } + for (auto iter = node.children(); iter.has_next(); ) { + auto &child = iter.next(); + if (child.dtype().is_string()) { + converted.emplace_back(child.as_string()); + } else { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of strings. A value is not a string. " + << node.to_json_default(); + throw std::invalid_argument(errStream.str()); + } + } + return converted; +} + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/Curve.cpp b/src/axom/sina/src/Curve.cpp new file mode 100644 index 0000000000..d765d53b26 --- /dev/null +++ b/src/axom/sina/src/Curve.cpp @@ -0,0 +1,60 @@ +#include "axom/sina/include/Curve.hpp" +#include "axom/sina/include/ConduitUtil.hpp" + +#include + +namespace axom +{ +namespace sina +{ + +namespace { +constexpr auto CURVE_TYPE_NAME = "curve"; +constexpr auto VALUES_KEY = "value"; +constexpr auto UNITS_KEY = "units"; +constexpr auto TAGS_KEY = "tags"; +} + +Curve::Curve(std::string name_, std::vector values_) : + name{std::move(name_)}, values{std::move(values_)}, units{}, tags{} {} + + +Curve::Curve(std::string name_, double const *values_, std::size_t numValues) : + name{std::move(name_)}, values{values_, values_ + numValues}, + units{}, tags{} {} + +Curve::Curve(std::string name_, conduit::Node const &curveAsNode) : + name{std::move(name_)}, values{}, units{}, tags{} { + auto &valuesAsNode = getRequiredField(VALUES_KEY, curveAsNode, + CURVE_TYPE_NAME); + values = toDoubleVector(valuesAsNode, VALUES_KEY); + + units = getOptionalString(UNITS_KEY, curveAsNode, CURVE_TYPE_NAME); + + if (curveAsNode.has_child(TAGS_KEY)) { + tags = toStringVector(curveAsNode[TAGS_KEY], TAGS_KEY); + } +} + +void Curve::setUnits(std::string units_) { + units = std::move(units_); +} + +void Curve::setTags(std::vector tags_) { + tags = std::move(tags_); +} + +conduit::Node Curve::toNode() const { + conduit::Node asNode; + asNode[VALUES_KEY] = values; + if (!units.empty()) { + asNode[UNITS_KEY] = units; + } + if (!tags.empty()) { + addStringsToNode(asNode, TAGS_KEY, tags); + } + return asNode; +} + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/CurveSet.cpp b/src/axom/sina/src/CurveSet.cpp new file mode 100644 index 0000000000..42160528d4 --- /dev/null +++ b/src/axom/sina/src/CurveSet.cpp @@ -0,0 +1,101 @@ +#include "axom/sina/include/CurveSet.hpp" + +#include + +#include "axom/sina/include/ConduitUtil.hpp" + +namespace axom +{ +namespace sina +{ + +namespace { + +constexpr auto INDEPENDENT_KEY = "independent"; +constexpr auto DEPENDENT_KEY = "dependent"; + +/** + * Add a curve to the given curve map. + * + * @param curve the curve to add + * @param curves the CurveMap to which to add the curve + */ +void addCurve(Curve &&curve, CurveSet::CurveMap &curves) { + auto &curveName = curve.getName(); + auto existing = curves.find(curveName); + if (existing == curves.end()) { + curves.insert(std::make_pair(curveName, curve)); + } else { + existing->second = curve; + } +} + +/** + * Extract a CurveMap from the given node. + * + * @param parent the parent node + * @param childNodeName the name of the child node + * @return a CurveMap representing the specified child + */ +CurveSet::CurveMap extractCurveMap(conduit::Node const &parent, + std::string const &childNodeName) { + CurveSet::CurveMap curveMap; + if (!parent.has_child(childNodeName)) { + return curveMap; + } + + auto &mapAsNode = parent.child(childNodeName); + for (auto iter = mapAsNode.children(); iter.has_next(); ) { + auto &curveAsNode = iter.next(); + std::string curveName = iter.name(); + Curve curve{curveName, curveAsNode}; + curveMap.insert(std::make_pair(std::move(curveName), std::move(curve))); + } + + return curveMap; +}; + +/** + * Create a Conduit node to represent the given CurveMap. + * + * @param curveMap the CurveMap to convert + * @return the map as a node + */ +conduit::Node createCurveMapNode(CurveSet::CurveMap const &curveMap) { + conduit::Node mapNode; + mapNode.set_dtype(conduit::DataType::object()); + for (auto &entry : curveMap) { + mapNode.add_child(entry.first) = entry.second.toNode(); + } + return mapNode; +} + +} + +CurveSet::CurveSet(std::string name_) : name{std::move(name_)}, + independentCurves{}, + dependentCurves{} {} + +CurveSet::CurveSet(std::string name_, conduit::Node const &node) + : name{std::move(name_)}, + independentCurves{extractCurveMap(node, INDEPENDENT_KEY)}, + dependentCurves{extractCurveMap(node, DEPENDENT_KEY)} { +} + +void CurveSet::addIndependentCurve(Curve curve) { + addCurve(std::move(curve), independentCurves); +} + +void CurveSet::addDependentCurve(Curve curve) { + addCurve(std::move(curve), dependentCurves); +} + +conduit::Node CurveSet::toNode() const { + conduit::Node asNode; + asNode[INDEPENDENT_KEY] = createCurveMapNode(independentCurves); + asNode[DEPENDENT_KEY] = createCurveMapNode(dependentCurves); + return asNode; +} + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/DataHolder.cpp b/src/axom/sina/src/DataHolder.cpp new file mode 100644 index 0000000000..0f07c886c9 --- /dev/null +++ b/src/axom/sina/src/DataHolder.cpp @@ -0,0 +1,133 @@ +#include "axom/sina/include/DataHolder.hpp" + +#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/include/Datum.hpp" + +#include + +namespace { + +char const DATA_FIELD[] = "data"; +char const CURVE_SETS_FIELD[] = "curve_sets"; +char const LIBRARY_DATA_FIELD[] = "library_data"; +char const USER_DEFINED_FIELD[] = "user_defined"; + +} + +namespace axom +{ +namespace sina +{ + +void DataHolder::add(std::string name, Datum datum) { + auto existing = data.find(name); + if (existing == data.end()) { + data.emplace(std::make_pair(std::move(name), datum)); + } else { + existing->second = datum; + } +} + +void DataHolder::add(CurveSet curveSet) { + auto name = curveSet.getName(); + auto existing = curveSets.find(name); + if (existing == curveSets.end()) { + curveSets.emplace(name, std::move(curveSet)); + } else { + existing->second = std::move(curveSet); + } +} + +std::shared_ptr DataHolder::addLibraryData(std::string const &name) { + auto existing = libraryData.find(name); + if (existing == libraryData.end()) { + libraryData.emplace(name, std::make_shared()); + } else { + existing->second = std::make_shared(); + } + return libraryData.at(name); +} + +std::shared_ptr DataHolder::addLibraryData(std::string const &name, conduit::Node existingLibraryData) { + auto existing = libraryData.find(name); + if (existing == libraryData.end()) { + libraryData.emplace(name, std::make_shared(existingLibraryData)); + } else { + existing->second = std::make_shared(existingLibraryData); + } + return libraryData.at(name); +} + +void DataHolder::setUserDefinedContent(conduit::Node userDefined_) { + userDefined = std::move(userDefined_); +} + +conduit::Node DataHolder::toNode() const { + conduit::Node asNode; + asNode.set(conduit::DataType::object()); + if(!libraryData.empty()){ + //Loop through vector of data and append Json + conduit::Node libRef; + for(auto &lib : libraryData){ + libRef.add_child(lib.first) = lib.second->toNode(); + } + asNode[LIBRARY_DATA_FIELD] = libRef; + } + if(!curveSets.empty()){ + conduit::Node curveSetsNode; + for(auto &entry : curveSets){ + curveSetsNode.add_child(entry.first) = entry.second.toNode(); + } + asNode[CURVE_SETS_FIELD] = curveSetsNode; + } + if(!data.empty()){ + //Loop through vector of data and append Json + conduit::Node datumRef; + for(auto &datum : data){ + datumRef.add_child(datum.first) = datum.second.toNode(); + } + asNode[DATA_FIELD] = datumRef; + } + if(!userDefined.dtype().is_empty()){ + asNode[USER_DEFINED_FIELD] = userDefined; + } + return asNode; +} + +DataHolder::DataHolder(conduit::Node const &asNode) { + if(asNode.has_child(DATA_FIELD)){ + auto dataIter = asNode[DATA_FIELD].children(); + //Loop through DATA_FIELD objects and add them to data: + while(dataIter.has_next()){ + auto &namedDatum = dataIter.next(); + data.emplace(std::make_pair(dataIter.name(), Datum(namedDatum))); + } + } + if (asNode.has_child(CURVE_SETS_FIELD)) { + auto curveSetsIter = asNode[CURVE_SETS_FIELD].children(); + while(curveSetsIter.has_next()){ + auto &curveSetNode = curveSetsIter.next(); + std::string name = curveSetsIter.name(); + CurveSet cs{name, curveSetNode}; + curveSets.emplace(std::make_pair(std::move(name), std::move(cs))); + } + } + if(asNode.has_child(LIBRARY_DATA_FIELD)) { + auto libraryIter = asNode[LIBRARY_DATA_FIELD].children(); + while(libraryIter.has_next()){ + auto &libraryDataNode = libraryIter.next(); + std::string name = libraryIter.name(); + libraryData.emplace(std::make_pair(std::move(name), + std::make_shared(libraryDataNode))); + } + } + if(asNode.has_child(USER_DEFINED_FIELD)) { + userDefined = asNode[USER_DEFINED_FIELD]; + if (!userDefined.dtype().is_object()) { + throw std::invalid_argument("user_defined must be an object Node"); + } + } + } + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/Datum.cpp b/src/axom/sina/src/Datum.cpp new file mode 100644 index 0000000000..f272c4d91e --- /dev/null +++ b/src/axom/sina/src/Datum.cpp @@ -0,0 +1,170 @@ +/// @file + +#include "axom/sina/include/Datum.hpp" +#include "conduit.hpp" + +#include +#include +#include + +namespace { + +char const VALUE_FIELD[] = "value"; +char const UNITS_FIELD[] = "units"; +char const TAGS_FIELD[] = "tags"; +char const DATA_PARENT_TYPE[] = "data"; + +} + +namespace axom +{ +namespace sina +{ + +Datum::Datum(std::string value_) : + stringValue{std::move(value_)}{ + //Set type to String, as we know it uses strings + type = ValueType::String; +} + +Datum::Datum(double value_) : + scalarValue{std::move(value_)}{ + //Set type to Scalar, as we know it uses doubles + type = ValueType::Scalar; +} + +Datum::Datum(std::vector value_) : + stringArrayValue{std::move(value_)}{ + //Set type to StringArray, as we know it uses an array of strings + type = ValueType::StringArray; +} + +Datum::Datum(std::vector value_) : + scalarArrayValue{std::move(value_)}{ + //Set type to ScalarArray, as we know it uses an array of doubles + type = ValueType::ScalarArray; +} + +Datum::Datum(conduit::Node const &asNode) { + //Need to determine what type of Datum we have: Scalar (double), String, + //or list of one of those two. + conduit::Node valueNode = getRequiredField(VALUE_FIELD, asNode, DATA_PARENT_TYPE); + if(valueNode.dtype().is_string()){ + stringValue = valueNode.as_string(); + type = ValueType::String; + } + else if(valueNode.dtype().is_number() && valueNode.dtype().number_of_elements() == 1){ + scalarValue = valueNode.to_double(); + type = ValueType::Scalar; + } + // There are two different ways to end up with a "list" of numbers in conduit, but + // only one of them tests True for is_list. This handles the other. + else if(valueNode.dtype().is_number()){ + type = ValueType::ScalarArray; + // What's passed in could be an array of any numeric type + // We pass a cast copy into captureNode + conduit::Node captureNode; + valueNode.to_float64_array(captureNode); + std::vector array_as_vect(captureNode.as_double_ptr(), + captureNode.as_double_ptr() + captureNode.dtype().number_of_elements()); + scalarArrayValue = array_as_vect; + } + else if(valueNode.dtype().is_list()){ + //An empty list is assumed to be an empty list of doubles. + //This only works because this field is immutable! + //If this ever changes, or if Datum's type is used directly to make + //decisions (ex: Sina deciding where to store data), this logic + //should be revisited. + if(valueNode.number_of_children() == 0 || valueNode[0].dtype().is_number()){ + type = ValueType::ScalarArray; + } + else if(valueNode[0].dtype().is_string()){ + type = ValueType::StringArray; + } + else { + std::ostringstream message; + message << "The only valid types for an array '" << VALUE_FIELD + << "' are strings and numbers. Got '" << valueNode.to_json() << "'"; + throw std::invalid_argument(message.str()); + } + + auto itr = valueNode.children(); + while(itr.has_next()) + { + conduit::Node const &entry = itr.next(); + if(entry.dtype().is_string() && type == ValueType::StringArray){ + stringArrayValue.emplace_back(entry.as_string()); + } + else if(entry.dtype().is_number() && type == ValueType::ScalarArray){ + scalarArrayValue.emplace_back(entry.to_double()); + } + else { + std::ostringstream message; + message << "If the required field '" << VALUE_FIELD + << "' is an array, it must consist of only strings or only numbers, " + << "but got '" << entry.dtype().name() << "' (" << entry.to_json() << ")"; + throw std::invalid_argument(message.str()); + } + } + } + else { + std::ostringstream message; + message << "The required field '" << VALUE_FIELD + << "' must be a string, double, list of strings, or list of doubles."; + throw std::invalid_argument(message.str()); + } + + //Get the units, if there are any + units = getOptionalString(UNITS_FIELD, asNode, DATA_PARENT_TYPE); + + //Need to grab the tags and add them to a vector of strings + if(asNode.has_child(TAGS_FIELD)){ + auto tagNodeIter = asNode[TAGS_FIELD].children(); + while(tagNodeIter.has_next()){ + auto &tag = tagNodeIter.next(); + if(tag.dtype().is_string()){ + tags.emplace_back(std::string(tag.as_string())); + } else { + std::ostringstream message; + message << "The optional field '" << TAGS_FIELD + << "' must be an array of strings. Found '" + << tag.dtype().name() << "' instead."; + throw std::invalid_argument(message.str()); + } + } + } +} + +void Datum::setUnits(std::string units_) { + units = std::move(units_); +} + +void Datum::setTags(std::vector tags_){ + tags = std::move(tags_); +} + +conduit::Node Datum::toNode() const { + conduit::Node asNode; + switch(type){ + case ValueType::Scalar: + asNode[VALUE_FIELD] = scalarValue; + break; + case ValueType::String: + asNode[VALUE_FIELD] = stringValue; + break; + case ValueType::ScalarArray: + asNode[VALUE_FIELD] = scalarArrayValue; + break; + case ValueType::StringArray: + addStringsToNode(asNode, VALUE_FIELD, stringArrayValue); + break; + } + if(tags.size() > 0) + addStringsToNode(asNode, TAGS_FIELD, tags); + if(!units.empty()) + asNode[UNITS_FIELD] = units; + return asNode; +}; + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/Document.cpp b/src/axom/sina/src/Document.cpp new file mode 100644 index 0000000000..7ed1eec11c --- /dev/null +++ b/src/axom/sina/src/Document.cpp @@ -0,0 +1,139 @@ +/// @file + +#include "axom/sina/include/Document.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace axom +{ +namespace sina +{ + +namespace { +char const RECORDS_KEY[] = "records"; +char const RELATIONSHIPS_KEY[] = "relationships"; +char const SAVE_TMP_FILE_EXTENSION[] = ".sina.tmp"; +} + +void Document::add(std::unique_ptr record) { + records.emplace_back(std::move(record)); +} + +void Document::add(Relationship relationship) { + relationships.emplace_back(std::move(relationship)); +} + +conduit::Node Document::toNode() const { + conduit::Node document(conduit::DataType::object()); + document[RECORDS_KEY] = conduit::Node(conduit::DataType::list()); + document[RELATIONSHIPS_KEY] = conduit::Node(conduit::DataType::list()); + for(auto &record : records) + { + auto &list_entry = document[RECORDS_KEY].append(); + list_entry.set_node(record->toNode()); + } + for(auto &relationship : relationships) + { + auto &list_entry = document[RELATIONSHIPS_KEY].append(); + list_entry = relationship.toNode(); + } + return document; +} + +void Document::createFromNode(conduit::Node const &asNode, + RecordLoader const &recordLoader) { + if (asNode.has_child(RECORDS_KEY)) { + conduit::Node record_nodes = asNode[RECORDS_KEY]; + if (record_nodes.dtype().is_list()) { + auto recordIter = record_nodes.children(); + while (recordIter.has_next()){ + auto record = recordIter.next(); + add(recordLoader.load(record)); + } + } else { + std::ostringstream message; + message << "The '" << RECORDS_KEY + << "' element of a document must be an array"; + throw std::invalid_argument(message.str()); + } + } + + if (asNode.has_child(RELATIONSHIPS_KEY)){ + conduit::Node relationship_nodes = asNode[RELATIONSHIPS_KEY]; + if (relationship_nodes.dtype().is_list()) { + auto relationshipsIter = relationship_nodes.children(); + while (relationshipsIter.has_next()){ + auto &relationship = relationshipsIter.next(); + add(Relationship{relationship}); + } + } else { + std::ostringstream message; + message << "The '" << RELATIONSHIPS_KEY + << "' element of a document must be an array"; + throw std::invalid_argument(message.str()); + } + } +} + +Document::Document(conduit::Node const &asNode, + RecordLoader const &recordLoader) { + this->createFromNode(asNode, recordLoader); +} + +Document::Document(std::string const &asJson, RecordLoader const &recordLoader) { + conduit::Node asNode; + asNode.parse(asJson, "json"); + this->createFromNode(asNode, recordLoader); +} + +std::string Document::toJson(conduit::index_t indent, conduit::index_t depth, + const std::string &pad, const std::string &eoe) const{ + return this->toNode().to_json("json", indent, depth, pad, eoe); +} + +void saveDocument(Document const &document, std::string const &fileName) { + // It is a common use case for users to want to overwrite their files as + // the simulation progresses. However, this operation should be atomic so + // that if a write fails, the old file is left intact. For this reason, + // we write to a temporary file first and then move the file. The temporary + // file is in the same directory to ensure that it is part of the same + // file system as the destination file so that the move operation is + // atomic. + std::string tmpFileName = fileName + SAVE_TMP_FILE_EXTENSION; + auto asJson = document.toJson(); + std::ofstream fout{tmpFileName}; + fout.exceptions(std::ostream::failbit | std::ostream::badbit); + fout << asJson; + fout.close(); + + if (rename(tmpFileName.c_str(), fileName.c_str()) != 0) { + std::string message{"Could not save to '"}; + message += fileName; + message += "'"; + throw std::ios::failure{message}; + } +} + +Document loadDocument(std::string const &path) { + return loadDocument(path, createRecordLoaderWithAllKnownTypes()); +} + +Document loadDocument(std::string const &path, + RecordLoader const &recordLoader) { + conduit::Node nodeFromJson; + std::ifstream file_in{path}; + std::ostringstream file_contents; + file_contents << file_in.rdbuf(); + file_in.close(); + nodeFromJson.parse(file_contents.str(), "json"); + return Document{nodeFromJson, recordLoader}; +} + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/File.cpp b/src/axom/sina/src/File.cpp new file mode 100644 index 0000000000..d11e24290f --- /dev/null +++ b/src/axom/sina/src/File.cpp @@ -0,0 +1,67 @@ +/// @file + +#include "axom/sina/include/File.hpp" +#include "axom/sina/include/ConduitUtil.hpp" + +#include +#include +#include + +#include "conduit.hpp" + +namespace axom +{ +namespace sina +{ + +namespace { +char const MIMETYPE_KEY[] = "mimetype"; +char const FILE_TYPE_NAME[] = "File"; +char const TAGS_KEY[] = "tags"; +} + +File::File(std::string uri_) : uri{std::move(uri_)} {} + +File::File(std::string uri_, conduit::Node const &asNode) : + uri{std::move(uri_)}, + mimeType{getOptionalString(MIMETYPE_KEY, asNode, FILE_TYPE_NAME)} { + if (asNode.has_child(TAGS_KEY)){ + auto tagsIter = asNode[TAGS_KEY].children(); + while(tagsIter.has_next()){ + auto &tag = tagsIter.next(); + if(tag.dtype().is_string()) + tags.emplace_back(tag.as_string()); + else { + std::ostringstream message; + message << "The optional field '" << TAGS_KEY + << "' must be an array of strings. Found '" + << tag.dtype().name() << "' instead."; + throw std::invalid_argument(message.str()); + } + } + } + + } + +void File::setMimeType(std::string mimeType_) { + File::mimeType = std::move(mimeType_); +} + +void File::setTags(std::vector tags_) { + File::tags = std::move(tags_); +} + +conduit::Node File::toNode() const { + conduit::Node asNode; + if (!mimeType.empty()) { + asNode[MIMETYPE_KEY] = mimeType; + } + if(tags.size() > 0) { + std::vector tags_copy(tags); + addStringsToNode(asNode, TAGS_KEY, tags_copy); + } + return asNode; +} + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/ID.cpp b/src/axom/sina/src/ID.cpp new file mode 100644 index 0000000000..7e2e4b365d --- /dev/null +++ b/src/axom/sina/src/ID.cpp @@ -0,0 +1,60 @@ + + +/// @file + +#include "axom/sina/include/ID.hpp" + +#include +#include + +namespace axom +{ +namespace sina +{ + +ID::ID(std::string id_, IDType type_) : id{std::move(id_)}, type{type_} {} + +namespace internal +{ + +namespace { +/** + * Extract an ID from a given JSON object. + * + * @param parentObject the object from which to extract the ID + * @param localName the local variant of the ID field + * @param globalName the global variant of the ID field + * @return the ID from the object + */ +ID extractIDFromObject(conduit::Node const &parentObject, + std::string const &localName, std::string const &globalName) { + if (parentObject.has_child(globalName)) { + return ID{std::string(parentObject[globalName].as_string()), IDType::Global}; + } + if (parentObject.has_child(localName)) { + return ID{std::string(parentObject[localName].as_string()), IDType::Local}; + } + std::string message{ + "Could not find either of the required ID fields '"}; + message += localName + "' or '" + globalName + "'"; + throw std::invalid_argument(message); +} +} + +IDField::IDField(ID value_, std::string localName_, std::string globalName_) + : value{std::move(value_)}, localName{std::move(localName_)}, + globalName{std::move(globalName_)} {} + +IDField::IDField(conduit::Node const &parentObject, std::string localName_, + std::string globalName_) : IDField{ + extractIDFromObject(parentObject, localName_, globalName_), + std::move(localName_), std::move(globalName_)} {} + +void IDField::addTo(conduit::Node &object) const { + auto &key = value.getType() == IDType::Global ? globalName : localName; + object[key] = value.getId(); +} + +} // end internal namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/Record.cpp b/src/axom/sina/src/Record.cpp new file mode 100644 index 0000000000..5a73ac79bc --- /dev/null +++ b/src/axom/sina/src/Record.cpp @@ -0,0 +1,110 @@ +/// @file + +#include "axom/sina/include/Record.hpp" + +#include +#include + +#include "axom/sina/include/CppBridge.hpp" +#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/include/DataHolder.hpp" +#include "axom/sina/include/Run.hpp" + +namespace { + +char const LOCAL_ID_FIELD[] = "local_id"; +char const GLOBAL_ID_FIELD[] = "id"; +char const TYPE_FIELD[] = "type"; +char const FILES_FIELD[] = "files"; + +// Used to preserve information when appending "standalone" library data +char const LIBRARY_DATA_ID_DATUM[] = "SINA_librarydata_id"; +char const LIBRARY_DATA_TYPE_DATUM[] = "SINA_librarydata_type"; + +} + +namespace axom +{ +namespace sina +{ + +Record::Record(ID id_, std::string type_) : + DataHolder{}, + id{std::move(id_), LOCAL_ID_FIELD, GLOBAL_ID_FIELD}, + type{std::move(type_)} {} + +conduit::Node Record::toNode() const { + conduit::Node asNode = DataHolder::toNode(); + asNode[TYPE_FIELD] = type; + id.addTo(asNode); + // Optional fields + if(!files.empty()){ + conduit::Node fileRef; + for (auto &file : files) { + auto &n = fileRef.add_child(file.getUri()); + n.set(file.toNode()); + asNode[FILES_FIELD] = fileRef; + } + } + return asNode; +} + +Record::Record(conduit::Node const &asNode) : + DataHolder{asNode}, + id{asNode, LOCAL_ID_FIELD, GLOBAL_ID_FIELD}, + type{getRequiredString(TYPE_FIELD, asNode, "record")} { + + if(asNode.has_child(FILES_FIELD)){ + auto filesIter = asNode[FILES_FIELD].children(); + while(filesIter.has_next()){ + auto &namedFile = filesIter.next(); + files.insert(File(filesIter.name(), namedFile)); + } + } +} + +void Record::remove(File const &file) { + files.erase(file); +} + +void Record::add(File file) { + files.erase(file); + files.insert(std::move(file)); +} + +void Record::addRecordAsLibraryData(Record const &childRecord, std::string const &name) { + if(!childRecord.files.empty()){ + for (auto &file : childRecord.files) { + add(file); + } + } + auto newLibData = addLibraryData(name, childRecord.toNode()); + newLibData->add(LIBRARY_DATA_ID_DATUM, Datum{childRecord.getId().getId()}); + newLibData->add(LIBRARY_DATA_TYPE_DATUM, Datum{childRecord.type}); +} + +void RecordLoader::addTypeLoader(std::string const &type, TypeLoader loader) { + typeLoaders[type] = std::move(loader); +} + +std::unique_ptr +RecordLoader::load(conduit::Node const &recordAsNode) const { + auto loaderIter = typeLoaders.find(recordAsNode[TYPE_FIELD].as_string()); + if (loaderIter != typeLoaders.end()) { + return loaderIter->second(recordAsNode); + } + return internal::make_unique(recordAsNode); +} + +bool RecordLoader::canLoad(std::string const &type) const { + return typeLoaders.count(type) > 0; +} + +RecordLoader createRecordLoaderWithAllKnownTypes() { + RecordLoader loader; + addRunLoader(loader); + return loader; +} + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/Relationship.cpp b/src/axom/sina/src/Relationship.cpp new file mode 100644 index 0000000000..a2d21dc4c6 --- /dev/null +++ b/src/axom/sina/src/Relationship.cpp @@ -0,0 +1,41 @@ +/// @file + +#include "axom/sina/include/Relationship.hpp" + +#include + +#include "axom/sina/include/ConduitUtil.hpp" + +namespace axom +{ +namespace sina +{ + +namespace { +char const GLOBAL_SUBJECT_KEY[] = "subject"; +char const LOCAL_SUBJECT_KEY[] = "local_subject"; +char const GLOBAL_OBJECT_KEY[] = "object"; +char const LOCAL_OBJECT_KEY[] = "local_object"; +char const PREDICATE_KEY[] = "predicate"; +} + +Relationship::Relationship(ID subject_, std::string predicate_, ID object_) : + subject{std::move(subject_), LOCAL_SUBJECT_KEY, GLOBAL_SUBJECT_KEY}, + object{std::move(object_), LOCAL_OBJECT_KEY, GLOBAL_OBJECT_KEY}, + predicate{std::move(predicate_)} {} + +Relationship::Relationship(conduit::Node const &asNode) : + subject{asNode, LOCAL_SUBJECT_KEY, GLOBAL_SUBJECT_KEY}, + object{asNode, LOCAL_OBJECT_KEY, GLOBAL_OBJECT_KEY}, + predicate{getRequiredString(PREDICATE_KEY, asNode, "Relationship")} {} + +conduit::Node Relationship::toNode() const { + conduit::Node relationshipNode; + relationshipNode[PREDICATE_KEY] = predicate; + subject.addTo(relationshipNode); + object.addTo(relationshipNode); + return relationshipNode; +} + +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/src/Run.cpp b/src/axom/sina/src/Run.cpp new file mode 100644 index 0000000000..79697e00d6 --- /dev/null +++ b/src/axom/sina/src/Run.cpp @@ -0,0 +1,49 @@ +/// @file + +#include "axom/sina/include/Run.hpp" + +#include + +#include "axom/sina/include/CppBridge.hpp" +#include "axom/sina/include/ConduitUtil.hpp" + +namespace axom +{ +namespace sina +{ + +namespace { +char const RUN_TYPE[] = "run"; +char const APPLICATION_FIELD[] = "application"; +char const VERSION_FIELD[] = "version"; +char const USER_FIELD[] = "user"; +} + +Run::Run(sina::ID id, std::string application_, std::string version_, + std::string user_) : Record{std::move(id), RUN_TYPE}, + application{std::move(application_)}, + version{std::move(version_)}, + user{std::move(user_)} {} + +Run::Run(conduit::Node const &asNode) : + Record(asNode), + application{getRequiredString(APPLICATION_FIELD, asNode, RUN_TYPE)}, + version{getOptionalString(VERSION_FIELD, asNode, RUN_TYPE)}, + user{getOptionalString(USER_FIELD, asNode, RUN_TYPE)} {} + +conduit::Node Run::toNode() const { + auto asNode = Record::toNode(); + asNode[APPLICATION_FIELD] = application; + asNode[VERSION_FIELD] = version; + asNode[USER_FIELD] = user; + return asNode; +} + +void addRunLoader(RecordLoader &loader) { + loader.addTypeLoader(RUN_TYPE, [](conduit::Node const &value) { + return internal::make_unique(value); + }); +} + +} // end sina namespace +} // end axom namespace diff --git a/src/cmake/axom-config.cmake.in b/src/cmake/axom-config.cmake.in index 8e5ddf6e74..45bed90441 100644 --- a/src/cmake/axom-config.cmake.in +++ b/src/cmake/axom-config.cmake.in @@ -48,6 +48,7 @@ if(NOT AXOM_FOUND) set(AXOM_ENABLE_SLAM "@AXOM_ENABLE_SLAM@") set(AXOM_ENABLE_SLIC "@AXOM_ENABLE_SLIC@") set(AXOM_ENABLE_SPIN "@AXOM_ENABLE_SPIN@") + set(AXOM_ENABLE_SINA "@AXOM_ENABLE_SINA@") # Axom built-in TPLs set(AXOM_USE_CLI11 "@AXOM_USE_CLI11@") From 39e5a2e4036e900aaabde4bdcebf2593a596d429 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 8 May 2024 14:54:27 -0700 Subject: [PATCH 05/60] add doxygen mainpage for sina --- src/axom/sina/doxygen_mainpage.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/axom/sina/doxygen_mainpage.md diff --git a/src/axom/sina/doxygen_mainpage.md b/src/axom/sina/doxygen_mainpage.md new file mode 100644 index 0000000000..21015e1266 --- /dev/null +++ b/src/axom/sina/doxygen_mainpage.md @@ -0,0 +1,14 @@ +Sina {#sinatop} +========= + +[Sina](@ref axom::sina) provides an easy way to collect data directly within codes and output them to a common file format designed. This is accomplished in an object oriented manner through the following classes: + +- [Curve](@ref axom::sina::Curve): represents a 1D curve +- [CurveSet](@ref axom::sina::CurveSet): represents an entry in a record's "curve_set" +- [DataHolder](@ref axom::sina::DataHolder): a basic container for certain types of information +- [Datum](@ref axom::sina::Datum): tracks the value and (optionally) tags and/or units of a value associated with a Record +- [Document](@ref axom::sina::Document): represents the top-lvevl object of a JSON file conforming to the Sina schema +- [File](@ref axom::sina::File): tracks the location (URI) and mimetype of a file on the file system, plus any tags +- [Record](@ref axom::sina::Record): entry in a Document's Record list +- [Relationship](@ref axom::sina::Relationship): represents correlations between records; consists of three parts: a subject, an object, and a predicate +- [Run](@ref axom::sina::Run): a subtype of Record corresponding to a single run of an application, as specified in the Sina schema \ No newline at end of file From a5fba8bb9afaa524876b6e31a42405701dcbe24d Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 29 May 2024 16:37:44 -0700 Subject: [PATCH 06/60] remove extra adiak list append and uncomment test addition --- src/axom/sina/CMakeLists.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index de8d9520be..e186164970 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -58,10 +58,6 @@ set(sina_sources ) # Add Adiak header and source -if (AXOM_SINA_USE_ADIAK) - list(APPEND sina_headers include/AdiakWriter.hpp) - list(APPEND sina_sources src/AdiakWriter.cpp) -endif() blt_list_append( TO sina_headers ELEMENTS include/AdiakWriter.hpp IF AXOM_SINA_USE_ADIAK ) blt_list_append( TO sina_sources ELEMENTS src/AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK ) @@ -95,9 +91,9 @@ install(FILES ${PROJECT_BINARY_DIR}/include/axom/sina/config.hpp #------------------------------------------------------------------------------ # Add tests and examples #------------------------------------------------------------------------------ -# if(AXOM_ENABLE_TESTS) -# add_subdirectory(tests) -# endif() +if(AXOM_ENABLE_TESTS) + add_subdirectory(tests) +endif() # if(AXOM_ENABLE_EXAMPLES) # add_subdirectory(examples) From 44e6f74f7b3d3657b06dd2a8b95d20d675adfa2f Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 29 May 2024 16:42:38 -0700 Subject: [PATCH 07/60] add tests for sina --- src/axom/sina/tests/CMakeLists.txt | 68 +++ .../sina/tests/include/ConduitTestUtils.hpp | 39 ++ src/axom/sina/tests/include/TestRecord.hpp | 81 +++ src/axom/sina/tests/src/ConduitTestUtils.cpp | 24 + src/axom/sina/tests/src/TestRecord.cpp | 23 + src/axom/sina/tests/src/sina_AdiakWriter.cpp | 154 ++++++ src/axom/sina/tests/src/sina_ConduitUtil.cpp | 216 ++++++++ src/axom/sina/tests/src/sina_CppBridge.cpp | 33 ++ src/axom/sina/tests/src/sina_Curve.cpp | 111 +++++ src/axom/sina/tests/src/sina_CurveSet.cpp | 204 ++++++++ src/axom/sina/tests/src/sina_DataHolder.cpp | 297 +++++++++++ src/axom/sina/tests/src/sina_Datum.cpp | 178 +++++++ src/axom/sina/tests/src/sina_Document.cpp | 323 ++++++++++++ src/axom/sina/tests/src/sina_File.cpp | 81 +++ src/axom/sina/tests/src/sina_ID.cpp | 95 ++++ src/axom/sina/tests/src/sina_Record.cpp | 460 ++++++++++++++++++ src/axom/sina/tests/src/sina_Relationship.cpp | 156 ++++++ src/axom/sina/tests/src/sina_Run.cpp | 98 ++++ 18 files changed, 2641 insertions(+) create mode 100644 src/axom/sina/tests/CMakeLists.txt create mode 100644 src/axom/sina/tests/include/ConduitTestUtils.hpp create mode 100644 src/axom/sina/tests/include/TestRecord.hpp create mode 100644 src/axom/sina/tests/src/ConduitTestUtils.cpp create mode 100644 src/axom/sina/tests/src/TestRecord.cpp create mode 100644 src/axom/sina/tests/src/sina_AdiakWriter.cpp create mode 100644 src/axom/sina/tests/src/sina_ConduitUtil.cpp create mode 100644 src/axom/sina/tests/src/sina_CppBridge.cpp create mode 100644 src/axom/sina/tests/src/sina_Curve.cpp create mode 100644 src/axom/sina/tests/src/sina_CurveSet.cpp create mode 100644 src/axom/sina/tests/src/sina_DataHolder.cpp create mode 100644 src/axom/sina/tests/src/sina_Datum.cpp create mode 100644 src/axom/sina/tests/src/sina_Document.cpp create mode 100644 src/axom/sina/tests/src/sina_File.cpp create mode 100644 src/axom/sina/tests/src/sina_ID.cpp create mode 100644 src/axom/sina/tests/src/sina_Record.cpp create mode 100644 src/axom/sina/tests/src/sina_Relationship.cpp create mode 100644 src/axom/sina/tests/src/sina_Run.cpp diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt new file mode 100644 index 0000000000..ffb71417da --- /dev/null +++ b/src/axom/sina/tests/CMakeLists.txt @@ -0,0 +1,68 @@ +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level LICENSE file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) +#------------------------------------------------------------------------------ +# Sina unit tests +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Specify list of tests +#------------------------------------------------------------------------------ + +set(gtest_sina_tests + src/sina_ConduitUtil.cpp + src/sina_CppBridge.cpp + src/sina_Curve.cpp + src/sina_CurveSet.cpp + src/sina_DataHolder.cpp + src/sina_Datum.cpp + src/sina_Document.cpp + src/sina_File.cpp + src/sina_ID.cpp + src/sina_Record.cpp + src/sina_Relationship.cpp + src/sina_Run.cpp + ) + +# Define Sina test utility sources and headers +set(sina_test_utils_sources + src/ConduitTestUtils.cpp + src/TestRecord.cpp +) +set(sina_test_utils_headers + include/ConduitTestUtils.hpp + include/TestRecord.hpp +) + +# Define Sina test dependencies +set(sina_gtests_depends_on sina gtest gmock conduit::conduit) + +# Add tests using Adiak if necessary and Adiak dependency +blt_list_append( TO gtest_sina_tests ELEMENTS src/sina_AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK) +blt_list_append( TO sina_gtests_depends_on ELEMENTS adiak::adiak IF AXOM_SINA_USE_ADIAK ) + +# Create a library for the test utilities so they can be used +axom_add_library( + NAME sina_test_utils + SOURCES ${sina_test_utils_sources} + HEADERS ${sina_test_utils_headers} + DEPENDS_ON ${sina_gtests_depends_on} + FOLDER axom/sina/tests) + +#------------------------------------------------------------------------------ +# Add gtest C++ tests +#------------------------------------------------------------------------------ +foreach(test ${gtest_sina_tests}) + get_filename_component( test_name ${test} NAME_WE ) + axom_add_executable(NAME ${test_name}_test + SOURCES ${test} + OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} + DEPENDS_ON sina_test_utils + FOLDER axom/sina/tests + ) + + axom_add_test( NAME ${test_name} + COMMAND ${test_name}_test + ) +endforeach() \ No newline at end of file diff --git a/src/axom/sina/tests/include/ConduitTestUtils.hpp b/src/axom/sina/tests/include/ConduitTestUtils.hpp new file mode 100644 index 0000000000..88a3ff2c55 --- /dev/null +++ b/src/axom/sina/tests/include/ConduitTestUtils.hpp @@ -0,0 +1,39 @@ +#ifndef SINA_CONDUITTESTUTILS_HPP +#define SINA_CONDUITTESTUTILS_HPP + +#include + +#include "gmock/gmock.h" + +#include "conduit.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ + +/** + * Parse a JSON value. + * + * @param valueAsString the value as a string + * @return the value as a Conduit node + */ +conduit::Node parseJsonValue(std::string const &valueAsString); + +// A matcher which verifies that a given Conduit node produces the expected +// JSON string +MATCHER_P(MatchesJson, expectedJsonString, "") { + conduit::Node expected = parseJsonValue(expectedJsonString); + *result_listener << "Given node is " << arg.to_json_default(); + conduit::Node diff; + bool differ = expected.diff(arg, diff); + return !differ; +} + +} // end testing namespace +} // end sina namespace +} // end axom namespace + +#endif //SINA_CONDUITTESTUTILS_HPP diff --git a/src/axom/sina/tests/include/TestRecord.hpp b/src/axom/sina/tests/include/TestRecord.hpp new file mode 100644 index 0000000000..eba82af795 --- /dev/null +++ b/src/axom/sina/tests/include/TestRecord.hpp @@ -0,0 +1,81 @@ +#ifndef SINA_TESTRECORD_HPP +#define SINA_TESTRECORD_HPP + +#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/include/Record.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ + +char constexpr TEST_RECORD_VALUE_KEY[] = "testKey"; + +/** + * A TestRecord is a class template that's a subclass of Record and simply + * stores a value of a specified type. + * + * @tparam T the type of the value to store + */ +template +class TestRecord : public Record { +public: + + /** + * Create a new TestRecord. + * + * @param id the ID of the record. It is always a global ID. + * @param type the type of the record + * @param value the value of the record + */ + TestRecord(std::string id, std::string type, T value); + + /** + * Create a new TestRecord from its conduit Node representation. + * + * NOTE: This needs to be implemented explicitly for each type of value + * + * @param asValue the record in its Node representation + */ + explicit TestRecord(conduit::Node const &asValue); + + /** + * Get the record's value. + * + * @return the record's value + */ + const T &getValue() const noexcept { + return value; + } + + conduit::Node toNode() const override; + +private: + T value; +}; + +template +TestRecord::TestRecord(std::string id, std::string type, T value_) : + Record{ID{std::move(id), IDType::Global}, std::move(type)}, + value{std::move(value_)} {} + +template<> +TestRecord::TestRecord(conduit::Node const &asNode); + +template<> +TestRecord::TestRecord(conduit::Node const &asJson); + +template +conduit::Node TestRecord::toNode() const { + auto asJson = Record::toNode(); + asJson[TEST_RECORD_VALUE_KEY] = value; + return asJson; +} + +} // end testing namespace +} // end sina namespace +} // end axom namespace + +#endif //SINA_TESTRECORD_HPP diff --git a/src/axom/sina/tests/src/ConduitTestUtils.cpp b/src/axom/sina/tests/src/ConduitTestUtils.cpp new file mode 100644 index 0000000000..2b1656dae0 --- /dev/null +++ b/src/axom/sina/tests/src/ConduitTestUtils.cpp @@ -0,0 +1,24 @@ +#include "axom/sina/tests/include/ConduitTestUtils.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ + +conduit::Node parseJsonValue(std::string const &valueAsString) { + // If we just try to do node.parse(valueAsString, "json"), then passing + // in strings does not work. We need to create a document with a key + // so that valueAsString can be parsed as a value. + conduit::Node node; + std::string fullContents = "{\"TEST_KEY\": "; + fullContents += valueAsString; + fullContents += "}"; + node.parse(fullContents, "json"); + return node.child("TEST_KEY"); +} + +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/TestRecord.cpp b/src/axom/sina/tests/src/TestRecord.cpp new file mode 100644 index 0000000000..c382f45843 --- /dev/null +++ b/src/axom/sina/tests/src/TestRecord.cpp @@ -0,0 +1,23 @@ +#include "axom/sina/tests/include/TestRecord.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ + +template<> +TestRecord::TestRecord(conduit::Node const &asNode) : + Record{asNode}, + value{getRequiredString(TEST_RECORD_VALUE_KEY, asNode, "TestRecord")} {} + +template<> +TestRecord::TestRecord(conduit::Node const &asNode) : + Record{asNode}, + value{getRequiredField(TEST_RECORD_VALUE_KEY, asNode, + "TestRecord").as_int()} {} + +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_AdiakWriter.cpp b/src/axom/sina/tests/src/sina_AdiakWriter.cpp new file mode 100644 index 0000000000..fcb700b6ee --- /dev/null +++ b/src/axom/sina/tests/src/sina_AdiakWriter.cpp @@ -0,0 +1,154 @@ +#include "axom/sina/include/AdiakWriter.hpp" + +#ifdef AXOM_SINA_USE_ADIAK + +#include +#include +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "adiak.hpp" +extern "C" { +#include "adiak_tool.h" +#include "adiak.h" +} + +#include "axom/sina/include/Datum.hpp" +#include "axom/sina/include/ID.hpp" +#include "axom/sina/include/Run.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::HasSubstr; +using ::testing::DoubleEq; +using ::testing::ElementsAre; + +char const EXPECTED_DATA_KEY[] = "data"; +char const EXPECTED_FILES_KEY[] = "files"; + +class AdiakWriterTest : public ::testing::Test { + + protected: + static void SetUpTestCase() { + adiak::init(nullptr); + adiak_register_cb(1, adiak_category_all, AdiakWriterTest::callbackWrapper, 0, ¤t_test); + } + + void SetUp() override { + current_test=this; + } + + static void callbackWrapper(const char *name, adiak_category_t category, const char *subcategory, adiak_value_t *val, adiak_datatype_t *adiak_type, void *adiakwriter){ + auto test = static_cast(adiakwriter); + adiakSinaCallback(name, category, subcategory, val, adiak_type, &((*test)->record)); + } + + axom::sina::Record record{axom::sina::ID{"test_run", axom::sina::IDType::Local}, "test_type"}; + static AdiakWriterTest *current_test; +}; + +AdiakWriterTest *AdiakWriterTest::current_test; + +TEST_F(AdiakWriterTest, basic_assignment) { + //adiak::init(nullptr); + //adiak_register_cb(1, adiak_category_all, sina::adiakSinaCallback, 0, callback_record_ptr); + std::string name1 = "name1"; + std::string value1 = "value1"; + std::vector tags1 = {"string"}; + std::string name2 = "name2"; + int value2 = 2; + std::vector tags2 = {"int"}; + auto result1 = adiak::value(name1, value1); + auto result2 = adiak::value(name2, value2); + EXPECT_TRUE(result1 && result2); + auto asNode = record.toNode(); + EXPECT_EQ(value1, asNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); + EXPECT_EQ(value2, int(asNode[EXPECTED_DATA_KEY][name2]["value"].as_double())); + EXPECT_EQ(tags1[0], asNode[EXPECTED_DATA_KEY][name1]["tags"][0].as_string()); + EXPECT_EQ(tags2[0], asNode[EXPECTED_DATA_KEY][name2]["tags"][0].as_string()); +} + +TEST_F(AdiakWriterTest, scalar_types) { + std::string name1 = "my_long"; + long value1 = 0; + std::string name2 = "my_double"; + double value2 = 3.14; + auto result1 = adiak::value(name1, value1); + auto result2 = adiak::value(name2, value2); + EXPECT_TRUE(result1 && result2); + auto asNode = record.toNode(); + EXPECT_EQ(value1, asNode[EXPECTED_DATA_KEY][name1]["value"].as_float64()); + EXPECT_EQ(value2, asNode[EXPECTED_DATA_KEY][name2]["value"].as_double()); +} + +// No extra test for string_types (besides date) as they're handled identically +TEST_F(AdiakWriterTest, date_type) { + std::string name1 = "my_date"; + auto result = adiak::value(name1, adiak::date(1568397849)); + EXPECT_TRUE(result); + auto toNode = record.toNode(); + EXPECT_EQ("Fri, 13 Sep 2019 11:04:09 -0700", toNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); +} + +TEST_F(AdiakWriterTest, list_types) { + std::string name1 = "my_scalar_list"; + std::vector value1{4.5, 0, 5.12, 42}; + std::string name2 = "my_string_list"; + std::set value2{"spam", "egg and bacon", "egg and spam"}; + auto result1 = adiak::value(name1, value1); + auto result2 = adiak::value(name2, value2); + EXPECT_TRUE(result1 && result2); + auto asNode = record.toNode(); + auto doub_array = asNode[EXPECTED_DATA_KEY][name1]["value"].as_double_ptr(); + std::vectorscal_child_vals(doub_array, doub_array+asNode[EXPECTED_DATA_KEY][name1]["value"].dtype().number_of_elements()); + EXPECT_EQ(value1, scal_child_vals); + std::set node_vals; + auto val_itr = asNode[EXPECTED_DATA_KEY][name2]["value"].children(); + while(val_itr.has_next()) + node_vals.insert(val_itr.next().as_string()); + EXPECT_EQ(value2, node_vals); +} + +TEST_F(AdiakWriterTest, files) { + std::string name1 = "my_bash"; + std::string value1 = "/bin/bash"; + std::string name2 = "my_cat_pics"; + std::string value2 = "~/pictures/neighbor_cat.png"; + std::vector tags2{name2}; + auto result1 = adiak::value(name1, adiak::path(value1)); + auto result2 = adiak::value(name2, adiak::path(value2)); + EXPECT_TRUE(result1 && result2); + auto asNode = record.toNode(); + EXPECT_FALSE(asNode[EXPECTED_FILES_KEY].child(value1).dtype().is_empty()); + EXPECT_EQ(1, asNode[EXPECTED_FILES_KEY].child(value2)["tags"].number_of_children()); + EXPECT_EQ(tags2[0], asNode[EXPECTED_FILES_KEY].child(value2)["tags"][0].as_string()); +} + +TEST_F(AdiakWriterTest, files_list){ + std::string fileListName = "my_gecko_pics"; + std::string fileListVal1 = "~/pictures/spike.png"; + std::string fileListVal2 = "~/pictures/sandy.png"; + std::vector fileListAdiak{adiak::path(fileListVal1), adiak::path(fileListVal2)}; + std::vector tags = {"string"}; + EXPECT_TRUE(adiak::value(fileListName, fileListAdiak)); + auto asNode = record.toNode(); + EXPECT_FALSE(asNode[EXPECTED_FILES_KEY].child(fileListVal1).dtype().is_empty()); + EXPECT_EQ(1, asNode[EXPECTED_FILES_KEY].child(fileListVal2)["tags"].number_of_children()); + EXPECT_EQ(fileListName, asNode[EXPECTED_FILES_KEY].child(fileListVal2)["tags"][0].as_string()); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace + +#endif // AXOM_SINA_USE_ADIAK diff --git a/src/axom/sina/tests/src/sina_ConduitUtil.cpp b/src/axom/sina/tests/src/sina_ConduitUtil.cpp new file mode 100644 index 0000000000..54e690acd3 --- /dev/null +++ b/src/axom/sina/tests/src/sina_ConduitUtil.cpp @@ -0,0 +1,216 @@ +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/tests/include/ConduitTestUtils.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::ContainerEq; +using ::testing::ElementsAre; +using ::testing::DoubleEq; +using ::testing::HasSubstr; + +TEST(ConduitUtil, getRequiredField_present) { + conduit::Node parent; + parent["fieldName"] = "field value"; + auto &field = getRequiredField("fieldName", parent, "parent name"); + EXPECT_TRUE(field.dtype().is_string()); + EXPECT_EQ("field value", field.as_string()); +} + +TEST(ConduitUtil, getRequiredField_missing) { + conduit::Node parent; + try { + auto &field = getRequiredField("fieldName", parent, "parent name"); + FAIL() << "Should not have found field, but got " << field.name(); + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + } +} + +TEST(ConduitUtil, getRequiredField_slashes) { + conduit::Node parent; + // Conduit by default parses /, creating parent["some"]["name"] + parent["some/name"] = 24; + // This is how we provide a literal name with slashes + parent.add_child("some/name") = 42; + EXPECT_EQ(42, + getRequiredField("some/name", parent, "parent name").to_int64()); +} + +TEST(ConduitUtil, getRequiredString_valid) { + conduit::Node parent; + parent["fieldName"] = "field value"; + EXPECT_EQ("field value", + getRequiredString("fieldName", parent, "parent name")); +} + +TEST(ConduitUtil, getRequiredString_missing) { + conduit::Node parent; + try { + auto value = getRequiredString("fieldName", parent, "parent name"); + FAIL() << "Should not have found string, but got " << value; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + } +} + +TEST(ConduitUtil, getRequiredString_wrongType) { + conduit::Node parent; + parent["fieldName"] = 123; + try { + auto value = getRequiredString("fieldName", parent, "parent name"); + FAIL() << "Should not have found string, but got " << value; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + EXPECT_THAT(expected.what(), HasSubstr("string")); + } +} + +TEST(ConduitUtil, getRequiredString_slashes) { + conduit::Node parent; + parent["some/name"] = "undesired value"; + parent.add_child("some/name") = "desired value"; + EXPECT_EQ("desired value", + getRequiredString("some/name", parent, "parent name")); +} + +TEST(ConduitUtil, getRequiredDouble_valid) { + conduit::Node parent; + parent["fieldName"] = 3.14; + EXPECT_THAT(3.14, + DoubleEq(getRequiredDouble("fieldName", parent, "parent name"))); +} + +TEST(ConduitUtil, getRequiredDouble_missing) { + conduit::Node parent; + try { + auto value = getRequiredDouble("fieldName", parent, "parent name"); + FAIL() << "Should not have found double, but got " << value; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + } +} + +TEST(ConduitUtil, getRequiredDouble_wrongType) { + conduit::Node parent; + parent["fieldName"] = "field value"; + try { + auto value = getRequiredDouble("fieldName", parent, "parent name"); + FAIL() << "Should not have found double, but got " << value; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + EXPECT_THAT(expected.what(), HasSubstr("double")); + } +} + +TEST(ConduitUtil, getOptionalString_valid) { + conduit::Node parent; + parent["fieldName"] = "the value"; + EXPECT_EQ("the value", + getOptionalString("fieldName", parent, "parent name")); +} + +TEST(ConduitUtil, getOptionalString_missing) { + conduit::Node parent; + EXPECT_EQ("", getOptionalString("fieldName", parent, "parent name")); +} + +TEST(ConduitUtil, getOptionalString_explicitNullValue) { + conduit::Node parent; + parent["fieldName"]; + EXPECT_EQ("", getOptionalString("fieldName", parent, "parent name")); +} + +TEST(ConduitUtil, getOptionalString_wrongType) { + conduit::Node parent; + parent["fieldName"] = 123; + try { + auto value = getOptionalString("fieldName", parent, "parent name"); + FAIL() << "Should not have found string, but got " << value; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + EXPECT_THAT(expected.what(), HasSubstr("string")); + } +} + +TEST(ConduitUtil, getOptionalField_slashes) { + conduit::Node parent; + parent["some/name"] = "undesired value"; + EXPECT_EQ("", + getOptionalString("some/name", parent, "parent name")); +} + +TEST(ConduitUtil, toDoubleVector_empty) { + conduit::Node emptyList = parseJsonValue("[]"); + EXPECT_EQ(std::vector{}, toDoubleVector(emptyList, "testNode")); +} + +TEST(ConduitUtil, toDoubleVector_validValues) { + conduit::Node nonEmptyList = parseJsonValue("[1, 2.0, 3, 4]"); + EXPECT_THAT(toDoubleVector(nonEmptyList, "testNode"), + ElementsAre(1.0, 2.0, 3.0, 4.0)); +} + +TEST(ConduitUtil, toDoubleVector_NotList) { + conduit::Node notList = parseJsonValue("\"this is not a list of doubles\""); + try { + toDoubleVector(notList, "someName"); + FAIL() << "Should have thrown an exception"; + } catch (std::invalid_argument const &ex) { + EXPECT_THAT(ex.what(), HasSubstr("someName")); + } +} + +TEST(ConduitUtil, toStringVector_empty) { + conduit::Node emptyList = parseJsonValue("[]"); + EXPECT_THAT(toStringVector(emptyList, "testNode"), + ContainerEq(std::vector{})); +} + +TEST(ConduitUtil, toStringVector_validValues) { + conduit::Node nonEmptyList = parseJsonValue(R"(["s1", "s2", "s3"])"); + EXPECT_THAT(toStringVector(nonEmptyList, "testNode"), + ElementsAre("s1", "s2", "s3")); +} + +TEST(ConduitUtil, toStringVector_NotList) { + conduit::Node notList = parseJsonValue("\"this is not a list of doubles\""); + try { + toStringVector(notList, "someName"); + FAIL() << "Should have thrown an exception."; + } catch (std::invalid_argument const &ex) { + EXPECT_THAT(ex.what(), HasSubstr("someName")); + } +} + +TEST(ConduitUtil, toStringVector_NotListOfStrings) { + conduit::Node notList = parseJsonValue(R"([1, 2, "a string"])"); + try { + toStringVector(notList, "someName"); + FAIL() << "Should have thrown an exception."; + } catch (std::invalid_argument const &ex) { + EXPECT_THAT(ex.what(), HasSubstr("someName")); + } +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_CppBridge.cpp b/src/axom/sina/tests/src/sina_CppBridge.cpp new file mode 100644 index 0000000000..0ca08f40fe --- /dev/null +++ b/src/axom/sina/tests/src/sina_CppBridge.cpp @@ -0,0 +1,33 @@ +#include + +#include "gtest/gtest.h" + +#include "axom/sina/include/CppBridge.hpp" + +namespace axom +{ +namespace sina +{ +namespace internal +{ +namespace testing +{ +namespace +{ + + +TEST(CppBridge, make_unique_noParams) { + std::unique_ptr ptr = make_unique(); + EXPECT_TRUE(ptr->empty()); +} + +TEST(CppBridge, make_unique_withParam) { + std::unique_ptr ptr = make_unique("foo"); + EXPECT_EQ("foo", *ptr); +} + +} // end nameless namespace +} // end testing namespace +} // end internal namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_Curve.cpp b/src/axom/sina/tests/src/sina_Curve.cpp new file mode 100644 index 0000000000..41092684e2 --- /dev/null +++ b/src/axom/sina/tests/src/sina_Curve.cpp @@ -0,0 +1,111 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/Curve.hpp" +#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/tests/include/ConduitTestUtils.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::ElementsAre; +using ::testing::ContainerEq; + +TEST(Curve, createFromVector) { + std::vector values{1, 2, 3, 4, 5, 6}; + Curve const curve{"theName", values}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); + EXPECT_EQ("", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre()); +} + +TEST(Curve, createFromPointer) { + double values[]{1, 2, 3, 4, 5, 6}; + Curve const curve{"theName", values, sizeof(values) / sizeof(double)}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); + EXPECT_EQ("", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre()); +} + +TEST(Curve, createFromInitializerList) { + Curve const curve{"theName", {1, 2, 3, 4, 5, 6}}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); + EXPECT_EQ("", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre()); +} + +TEST(Curve, setUnits) { + Curve curve{"theName", {1, 2, 3}}; + EXPECT_EQ("", curve.getUnits()); + curve.setUnits("cm"); + EXPECT_EQ("cm", curve.getUnits()); +} + +TEST(Curve, setTags) { + Curve curve{"theName", {1, 2, 3}}; + EXPECT_THAT(curve.getTags(), ElementsAre()); + curve.setTags({"t1", "t2", "t3"}); + EXPECT_THAT(curve.getTags(), ElementsAre("t1", "t2", "t3")); +} + +TEST(Curve, createFromNode_requiredOnly) { + conduit::Node curveAsNode = parseJsonValue(R"( + { + "value": [1.0, 2.0, 3.0] + } + )"); + Curve curve{"theName", curveAsNode}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3)); + EXPECT_EQ("", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre()); +} + +TEST(Curve, createFromNode_optionalFields) { + conduit::Node curveAsNode = parseJsonValue(R"( + { + "value": [1.0, 2.0, 3.0], + "units": "cm", + "tags": ["t1", "t2", "t3"] + } + )"); + Curve curve{"theName", curveAsNode}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3)); + EXPECT_EQ("cm", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre("t1", "t2", "t3")); +} + +TEST(Curve, toNode_requiredOnly) { + Curve const curve{"theName", {1, 2, 3, 4}}; + auto expected = (R"({ + "value": [1.0, 2.0, 3.0, 4.0] + })"); + EXPECT_THAT(curve.toNode(), MatchesJson(expected)); +} + +TEST(Curve, toNode_optionalFields) { + Curve curve{"theName", {1, 2, 3, 4}}; + curve.setUnits("cm"); + curve.setTags({"t1", "t2", "t3"}); + auto expected = R"({ + "value": [1.0, 2.0, 3.0, 4.0], + "units": "cm", + "tags": ["t1", "t2", "t3"] + })"; + EXPECT_THAT(curve.toNode(), MatchesJson(expected)); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_CurveSet.cpp b/src/axom/sina/tests/src/sina_CurveSet.cpp new file mode 100644 index 0000000000..0d370ded45 --- /dev/null +++ b/src/axom/sina/tests/src/sina_CurveSet.cpp @@ -0,0 +1,204 @@ +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/CurveSet.hpp" +#include "axom/sina/tests/include/ConduitTestUtils.hpp" + +#include +#include + +namespace axom +{ +namespace sina +{ + +// NOTE: We need an operator== for the tests. For it to be able to be found +// by different matchers, it needs to be in the same namespace as Curve. +// If we need up needing it in another test, we'll have to move it to another +// file. +// +// NOTE: Since this isn't in an unnamed namespace, we need a forward +// declaration to satisfy strict compiler warnings. +bool operator==(Curve const &lhs, Curve const &rhs); + +/** + * Compare two curves for equality. All fields must be equal, including the + * doubles in the lists of values. This is not suitable for checking any + * calculated values. + * + * @param lhs the left-hand-side operand + * @param rhs the right-hand-side operand + * @return whether the curves are equal + */ +bool operator==(Curve const &lhs, Curve const &rhs) { + bool r = lhs.getName() == rhs.getName() + && lhs.getUnits() == rhs.getUnits() + && lhs.getTags() == rhs.getTags() + && lhs.getValues() == rhs.getValues(); + return r; +} + +namespace testing +{ +namespace +{ + +using ::testing::ContainerEq; +using ::testing::ElementsAre; + +TEST(CurveSet, initialState) { + CurveSet const cs{"theName"}; + ASSERT_EQ("theName", cs.getName()); + ASSERT_TRUE(cs.getIndependentCurves().empty()); + ASSERT_TRUE(cs.getDependentCurves().empty()); + ASSERT_NE(&cs.getIndependentCurves(), &cs.getDependentCurves()); +} + +TEST(CurveSet, addIndependentCurves) { + CurveSet cs{"testSet"}; + std::unordered_map expectedCurves; + + Curve i1{"i1", {1, 2, 3}}; + cs.addIndependentCurve(i1); + expectedCurves.insert(std::make_pair(i1.getName(), i1)); + EXPECT_THAT(cs.getIndependentCurves(), ContainerEq(expectedCurves)); + + Curve i2{"i2", {4, 5, 6}}; + cs.addIndependentCurve(i2); + expectedCurves.insert(std::make_pair(i2.getName(), i2)); + EXPECT_THAT(cs.getIndependentCurves(), ContainerEq(expectedCurves)); +} + +TEST(CurveSet, addIndependentCurves_replaceExisting) { + CurveSet cs{"testSet"}; + + Curve i1{"theName", {1, 2, 3}}; + cs.addIndependentCurve(i1); + EXPECT_THAT(cs.getIndependentCurves().at("theName").getValues(), + ElementsAre(1, 2, 3)); + + Curve i2{"theName", {4, 5, 6}}; + cs.addIndependentCurve(i2); + EXPECT_THAT(cs.getIndependentCurves().at("theName").getValues(), + ElementsAre(4, 5, 6)); +} + +TEST(CurveSet, addDpendentCurves) { + CurveSet cs{"testSet"}; + std::unordered_map expectedCurves; + + Curve i1{"i1", {1, 2, 3}}; + cs.addDependentCurve(i1); + expectedCurves.insert(std::make_pair(i1.getName(), i1)); + EXPECT_THAT(cs.getDependentCurves(), ContainerEq(expectedCurves)); + + Curve i2{"i2", {4, 5, 6}}; + cs.addDependentCurve(i2); + expectedCurves.insert(std::make_pair(i2.getName(), i2)); + EXPECT_THAT(cs.getDependentCurves(), ContainerEq(expectedCurves)); +} + +TEST(CurveSet, addDependentCurves_replaceExisting) { + CurveSet cs{"testSet"}; + + Curve d1{"theName", {1, 2, 3}}; + cs.addDependentCurve(d1); + EXPECT_THAT(cs.getDependentCurves().at("theName").getValues(), + ElementsAre(1, 2, 3)); + + Curve d2{"theName", {4, 5, 6}}; + cs.addDependentCurve(d2); + EXPECT_THAT(cs.getDependentCurves().at("theName").getValues(), + ElementsAre(4, 5, 6)); +} + +TEST(CurveSet, createFromNode_empty) { + conduit::Node curveSetAsNode = parseJsonValue(R"({})"); + CurveSet curveSet{"theName", curveSetAsNode}; + EXPECT_EQ("theName", curveSet.getName()); + std::unordered_map emptyMap; + EXPECT_THAT(curveSet.getDependentCurves(), ContainerEq(emptyMap)); + EXPECT_THAT(curveSet.getIndependentCurves(), ContainerEq(emptyMap)); +} + +TEST(CurveSet, createFromNode_emptySets) { + conduit::Node curveSetAsNode = parseJsonValue(R"({ + "dependent": {}, + "independent": {} + })"); + CurveSet curveSet{"theName", curveSetAsNode}; + EXPECT_EQ("theName", curveSet.getName()); + std::unordered_map emptyMap; + EXPECT_THAT(curveSet.getDependentCurves(), ContainerEq(emptyMap)); + EXPECT_THAT(curveSet.getIndependentCurves(), ContainerEq(emptyMap)); +} + +TEST(CurveSet, createFromNode_curveSetsDefined) { + conduit::Node curveSetAsNode = parseJsonValue(R"({ + "independent": { + "indep1": { "value": [10, 20, 30]}, + "indep2/with/slash": { "value": [40, 50, 60]} + }, + "dependent": { + "dep1": { "value": [1, 2, 3]}, + "dep2/with/slash": { "value": [4, 5, 6]} + } + })"); + CurveSet curveSet{"theName", curveSetAsNode}; + EXPECT_EQ("theName", curveSet.getName()); + + std::unordered_map expectedDependents { + {"dep1", Curve{"dep1", {1, 2, 3}}}, + {"dep2/with/slash", Curve{"dep2/with/slash", {4, 5, 6}}}, + }; + EXPECT_THAT(curveSet.getDependentCurves(), + ContainerEq(expectedDependents)); + + std::unordered_map expectedIndependents { + {"indep1", Curve{"indep1", {10, 20, 30}}}, + {"indep2/with/slash", Curve{"indep2/with/slash", {40, 50, 60}}}, + }; + EXPECT_THAT(curveSet.getIndependentCurves(), + ContainerEq(expectedIndependents)); +} + +TEST(CurveSet, toNode_empty) { + CurveSet curveSet{"theName"}; + auto expected = R"({ + "independent": {}, + "dependent": {} + })"; + EXPECT_THAT(curveSet.toNode(), MatchesJson(expected)); +} + +TEST(CurveSet, toNode_withCurves) { + CurveSet curveSet{"theName"}; + curveSet.addIndependentCurve(Curve{"i1", {1, 2, 3}}); + curveSet.addIndependentCurve(Curve{"i2/with/slash", {4, 5, 6}}); + curveSet.addDependentCurve(Curve{"d1", {10, 20, 30}}); + curveSet.addDependentCurve(Curve{"d2/with/slash", {40, 50, 60}}); + auto expected = R"({ + "independent": { + "i1": { + "value": [1.0, 2.0, 3.0] + }, + "i2/with/slash": { + "value": [4.0, 5.0, 6.0] + } + }, + "dependent": { + "d1": { + "value": [10.0, 20.0, 30.0] + }, + "d2/with/slash": { + "value": [40.0, 50.0, 60.0] + } + } + })"; + EXPECT_THAT(curveSet.toNode(), MatchesJson(expected)); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_DataHolder.cpp b/src/axom/sina/tests/src/sina_DataHolder.cpp new file mode 100644 index 0000000000..e457389252 --- /dev/null +++ b/src/axom/sina/tests/src/sina_DataHolder.cpp @@ -0,0 +1,297 @@ +#include +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/DataHolder.hpp" + +#include "axom/sina/tests/include/ConduitTestUtils.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::Contains; +using ::testing::ElementsAre; +using ::testing::Key; +using ::testing::HasSubstr; +using ::testing::DoubleEq; +using ::testing::Not; + +char const EXPECTED_DATA_KEY[] = "data"; +char const EXPECTED_CURVE_SETS_KEY[] = "curve_sets"; +char const EXPECTED_LIBRARY_DATA_KEY[] = "library_data"; +char const EXPECTED_USER_DEFINED_KEY[] = "user_defined"; + +TEST(DataHolder, add_data_existing_key) { + DataHolder dh{}; + dh.add("key1", Datum{"val1"}); + EXPECT_EQ("val1", dh.getData().at("key1").getValue()); + dh.add("key1", Datum{"val2"}); + EXPECT_EQ("val2", dh.getData().at("key1").getValue()); +} + +TEST(DataHolder, add_curve_set_existing_key) { + DataHolder dh{}; + CurveSet cs1{"cs1"}; + cs1.addDependentCurve(Curve{"original", {1, 2, 3}}); + dh.add(cs1); + + auto &csAfterFirstInsert = dh.getCurveSets(); + ASSERT_THAT(csAfterFirstInsert, Contains(Key("cs1"))); + EXPECT_THAT(csAfterFirstInsert.at("cs1").getDependentCurves(), + Contains(Key("original"))); + + CurveSet cs2{"cs1"}; + cs2.addDependentCurve(Curve{"new", {1, 2, 3}}); + dh.add(cs2); + + auto &csAfterSecondInsert = dh.getCurveSets(); + ASSERT_THAT(csAfterSecondInsert, Contains(Key("cs1"))); + EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), + Not(Contains(Key("original")))); + EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), + Contains(Key("new"))); +} + +TEST(DataHolder, create_fromNode_userDefined) { + conduit::Node originalNode; + originalNode[EXPECTED_USER_DEFINED_KEY]["k1"] = "v1"; + originalNode[EXPECTED_USER_DEFINED_KEY]["k2"] = 123; + std::vector k3_vals{1, 2, 3}; + originalNode[EXPECTED_USER_DEFINED_KEY]["k3"] = k3_vals; + + DataHolder holder{originalNode}; + auto const &userDefined = holder.getUserDefinedContent(); + EXPECT_EQ("v1", userDefined["k1"].as_string()); + EXPECT_EQ(123, userDefined["k2"].as_int()); + auto int_array = userDefined["k3"].as_int_ptr(); + std::vectorudef_ints(int_array, int_array+userDefined["k3"].dtype().number_of_elements()); + EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); +} + +TEST(DataHolder, create_fromNode_userDefined_not_object) { + conduit::Node originalNode; + originalNode[EXPECTED_USER_DEFINED_KEY] = "not an object"; + EXPECT_THROW(DataHolder{originalNode}, std::invalid_argument); +} + +TEST(DataHolder, getUserDefined_initialConst) { + DataHolder const holder; + conduit::Node const &userDefined = holder.getUserDefinedContent(); + EXPECT_TRUE(userDefined.dtype().is_empty()); +} + +TEST(DataHolder, getUserDefined_initialNonConst) { + DataHolder holder; + conduit::Node &initialUserDefined = holder.getUserDefinedContent(); + EXPECT_TRUE(initialUserDefined.dtype().is_empty()); + initialUserDefined["foo"] = 123; + EXPECT_EQ(123, holder.getUserDefinedContent()["foo"].as_int()); +} + +TEST(DataHolder, add_new_library) { + DataHolder dh{}; + auto outer = dh.addLibraryData("outer"); + auto &libDataAfterFirstInsert = dh.getLibraryData(); + ASSERT_THAT(libDataAfterFirstInsert, Contains(Key("outer"))); + dh.addLibraryData("other_outer"); + auto &libDataAfterSecondInsert = dh.getLibraryData(); + ASSERT_THAT(libDataAfterSecondInsert, Contains(Key("outer"))); + ASSERT_THAT(libDataAfterSecondInsert, Contains(Key("other_outer"))); + outer->addLibraryData("inner"); + auto &libDataAfterThirdInsert = dh.getLibraryData(); + ASSERT_THAT(libDataAfterThirdInsert.at("outer")->getLibraryData(), + Contains(Key("inner"))); + ASSERT_THAT(libDataAfterThirdInsert.at("other_outer")->getLibraryData(), + Not(Contains(Key("inner")))); +} + +TEST(DataHolder, add_library_existing_key) { + std::string libName = "outer"; + DataHolder dh{}; + auto outer = dh.addLibraryData(libName); + outer->add("key1", Datum{"val1"}); + ASSERT_THAT(dh.getLibraryData(libName)->getData(), + Contains(Key("key1"))); + dh.addLibraryData(libName); + ASSERT_THAT(dh.getLibraryData(libName)->getData(), + Not(Contains(Key("key1")))); +} + +TEST(DataHolder, create_fromNode_data) { + conduit::Node originalNode; + originalNode[EXPECTED_DATA_KEY]; + + std::string name1 = "datum name 1"; + std::string name2 = "datum name 2/with/slash"; + + conduit::Node name1_node; + name1_node["value"] = "value 1"; + originalNode[EXPECTED_DATA_KEY][name1] = name1_node; + conduit::Node name2_node; + name2_node["value"] = 2.22; + name2_node["units"] = "g/L"; + addStringsToNode(name2_node, "tags", {"tag1","tag2"}); + name2_node["value"] = 2.22; + originalNode[EXPECTED_DATA_KEY].add_child(name2) = name2_node; + DataHolder dh{originalNode}; + auto &data = dh.getData(); + ASSERT_EQ(2u, data.size()); + EXPECT_EQ("value 1", data.at(name1).getValue()); + EXPECT_THAT(2.22, DoubleEq(data.at(name2).getScalar())); + EXPECT_EQ("g/L", data.at(name2).getUnits()); + EXPECT_EQ("tag1", data.at(name2).getTags()[0]); + EXPECT_EQ("tag2", data.at(name2).getTags()[1]); +} + +TEST(DataHolder, create_fromNode_curveSets) { + conduit::Node dataHolderAsNode = parseJsonValue(R"({ + "curve_sets": { + "cs1": { + "independent": { + "i1": { "value": [1, 2, 3]} + }, + "dependent": { + "d1": { "value": [4, 5, 6]} + } + } + } + })"); + DataHolder dh{dataHolderAsNode}; + auto &curveSets = dh.getCurveSets(); + ASSERT_THAT(curveSets, Contains(Key("cs1"))); +} + +TEST(DataHolder, create_fromNode_libraryData) { + conduit::Node dataHolderAsNode = parseJsonValue(R"({ + "library_data": { + "outer_lib": { + "library_data": { + "inner_lib": { "data": {"i2": { "value": "good morning!"}}} + } + } + } + })"); + DataHolder dh{dataHolderAsNode}; + auto &fullLibData = dh.getLibraryData(); + ASSERT_THAT(fullLibData, Contains(Key("outer_lib"))); + auto outerLibData = fullLibData.at("outer_lib")->getLibraryData(); + ASSERT_THAT(outerLibData, Contains(Key("inner_lib"))); + auto &innerData = outerLibData.at("inner_lib")->getData(); + EXPECT_EQ("good morning!", innerData.at("i2").getValue()); +} + +TEST(DataHolder, toNode_default_values) { + DataHolder dh{}; + auto asNode = dh.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + // We want to be sure that unset optional fields aren't present + EXPECT_FALSE(asNode.has_child(EXPECTED_DATA_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_CURVE_SETS_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_LIBRARY_DATA_KEY)); +} + +TEST(DataHolder, toNode_data) { + DataHolder dh{}; + std::string name1 = "name1"; + std::string value1 = "value1"; + Datum datum1 = Datum{value1}; + datum1.setUnits("some units"); + datum1.setTags({"tag1"}); + dh.add(name1, datum1); + std::string name2 = "name2"; + dh.add(name2, Datum{2.}); + auto asNode = dh.toNode(); + ASSERT_EQ(2u, asNode[EXPECTED_DATA_KEY].number_of_children()); + EXPECT_EQ("value1", asNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); + EXPECT_EQ("some units", asNode[EXPECTED_DATA_KEY][name1]["units"].as_string()); + EXPECT_EQ("tag1", asNode[EXPECTED_DATA_KEY][name1]["tags"][0].as_string()); + + EXPECT_THAT(asNode[EXPECTED_DATA_KEY][name2]["value"].as_double(), + DoubleEq(2.)); + EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["units"].dtype().is_empty()); + EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["tags"].dtype().is_empty()); +} + +TEST(DataHolder, toNode_dataWithSlashes) { + DataHolder dh{}; + std::string name = "name/with/slashes"; + std::string value = "the value"; + Datum datum = Datum{value}; + dh.add(name, datum); + auto asNode = dh.toNode(); + ASSERT_EQ(1u, asNode[EXPECTED_DATA_KEY].number_of_children()); + EXPECT_EQ("the value", asNode[EXPECTED_DATA_KEY].child(name)["value"].as_string()); +} + +TEST(DataHolder, toNode_curveSets) { + DataHolder dh{}; + CurveSet cs{"myCurveSet/with/slash"}; + cs.addIndependentCurve(Curve{"myCurve", {1, 2, 3}}); + dh.add(cs); + auto expected = R"({ + "curve_sets": { + "myCurveSet/with/slash": { + "independent": { + "myCurve": { + "value": [1.0, 2.0, 3.0] + } + }, + "dependent": {} + } + } + })"; + EXPECT_THAT(dh.toNode(), MatchesJson(expected)); +} + +TEST(DataHolder, toNode_libraryData) { + DataHolder dh{}; + auto outer = dh.addLibraryData("outer"); + outer->add("scal", Datum{"goodbye!"}); + auto inner = outer->addLibraryData("inner"); + inner->add("str", Datum{"hello!"}); + auto expected = R"({ + "library_data": { + "outer": { + "library_data": { + "inner": { + "data": {"str": {"value": "hello!"}} + } + }, + "data": {"scal": {"value": "goodbye!"}} + } + } + })"; + EXPECT_THAT(dh.toNode(), MatchesJson(expected)); +} + +TEST(DataHolder, toNode_userDefined) { + DataHolder holder; + conduit::Node userDef; + userDef["k1"] = "v1"; + userDef["k2"] = 123; + std::vector int_vals{1, 2, 3}; + userDef["k3"] = int_vals; + holder.setUserDefinedContent(userDef); + + auto asNode = holder.toNode(); + + auto userDefined = asNode[EXPECTED_USER_DEFINED_KEY]; + EXPECT_EQ("v1", userDefined["k1"].as_string()); + EXPECT_EQ(123, userDefined["k2"].as_int()); + auto int_array = userDefined["k3"].as_int_ptr(); + std::vectorudef_ints(int_array, int_array+userDefined["k3"].dtype().number_of_elements()); + EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_Datum.cpp b/src/axom/sina/tests/src/sina_Datum.cpp new file mode 100644 index 0000000000..963e5b72ca --- /dev/null +++ b/src/axom/sina/tests/src/sina_Datum.cpp @@ -0,0 +1,178 @@ +#include +#include +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/Datum.hpp" +#include "axom/sina/include/ConduitUtil.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::HasSubstr; +using ::testing::DoubleEq; +using ::testing::ElementsAre; + +TEST(Datum, create) { + std::vector tags = {"tag1", "tag2"}; + std::string value = "value"; + std::vector val_list = {"val1", "val2"}; + std::vector scal_list = {100, 2.0}; + Datum datum1{value}; + datum1.setUnits("some units"); + datum1.setTags(tags); + Datum datum2{3.14}; + Datum datum3{val_list}; + Datum datum4{scal_list}; + + EXPECT_EQ(ValueType::String, datum1.getType()); + EXPECT_EQ("value", datum1.getValue()); + EXPECT_EQ("some units", datum1.getUnits()); + EXPECT_EQ(tags, datum1.getTags()); + + EXPECT_EQ(ValueType::Scalar, datum2.getType()); + EXPECT_THAT(datum2.getScalar(), DoubleEq(3.14)); + + EXPECT_EQ(ValueType::StringArray, datum3.getType()); + EXPECT_EQ(val_list, datum3.getStringArray()); + + EXPECT_EQ(ValueType::ScalarArray, datum4.getType()); + EXPECT_EQ(scal_list, datum4.getScalarArray()); +} + +TEST(Datum, createFromNode) { + conduit::Node val_with_tags; + conduit::Node scalar_with_units; + conduit::Node val_list_node; + conduit::Node scal_list_node; + conduit::Node empty_list_node; + conduit::Node int_array_node; + conduit::Node char_array_node; + + std::vector tags = {"hello", "world"}; + std::vector val_list = {"val1", "val2"}; + std::vector scal_list = {100, 2.0}; + // Conduit has some special treatment of arrays + int int_array [] = {-2, 2, 4, 8}; + std::vector array_equiv = {-2, 2, 4, 8}; + char coerced_to_string [] = {'a', 'b', 'c', '\0'}; + //Empty lists are valid + conduit::Node empty_list(conduit::DataType::list()); + + val_with_tags["value"] = "the value"; + addStringsToNode(val_with_tags, "tags", tags); + scalar_with_units["units"] = "some units"; + scalar_with_units["value"] = 3.14; + addStringsToNode(val_list_node, "value", val_list); + scal_list_node["value"] = scal_list; + empty_list_node["value"] = empty_list; + int_array_node["value"].set(int_array, 4); + char_array_node["value"] = coerced_to_string; + + Datum datum1{val_with_tags}; + Datum datum2{scalar_with_units}; + Datum datum3{val_list_node}; + Datum datum4{scal_list_node}; + Datum datum5{empty_list_node}; + Datum datum6{int_array_node}; + Datum datum7{char_array_node}; + + EXPECT_EQ("the value", datum1.getValue()); + EXPECT_EQ(tags, datum1.getTags()); + EXPECT_THAT(3.14, DoubleEq(datum2.getScalar())); + EXPECT_EQ("some units", datum2.getUnits()); + EXPECT_EQ(val_list, datum3.getStringArray()); + EXPECT_EQ(scal_list, datum4.getScalarArray()); + EXPECT_EQ(ValueType::ScalarArray, datum5.getType()); + EXPECT_EQ(array_equiv, datum6.getScalarArray()); + EXPECT_EQ("abc", datum7.getValue()); +} + +TEST(Datum, setUnits) { + std::string value = "value"; + Datum datum1{value}; + datum1.setUnits("new units"); + EXPECT_EQ("new units", datum1.getUnits()); +} + +TEST(Datum, setTags) { + std::string value = "value"; + Datum datum1{value}; + datum1.setTags({"new_tag"}); + EXPECT_EQ("new_tag", datum1.getTags()[0]); +} + +TEST(Datum, createFromJson_missingKeys) { + conduit::Node object1; + try { + Datum datum1{object1}; + FAIL() << "Should have gotten a value error"; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr("value")); + } +} + +TEST(Datum, createFromJson_badListValue) { + conduit::Node object1; + auto &mixed_scal = object1["value"].append(); + mixed_scal.set(1.0); + auto &mixed_val = object1["value"].append(); + mixed_val.set("two"); + try { + Datum datum1{object1}; + FAIL() << "Should have gotten a value error"; + } catch (std::invalid_argument const &expected) { + std::string warning = "it must consist of only strings or only numbers"; + EXPECT_THAT(expected.what(), HasSubstr(warning)); + } +} + +TEST(Datum, toJson) { + std::vector tags = {"list", "of", "tags"}; + std::string value = "Datum value"; + std::vector scal_list = {-14, 22, 9}; + std::vector val_list = {"east", "west"}; + Datum datum1{value}; + datum1.setTags(tags); + Datum datum2{3.14}; + datum2.setUnits("Datum units"); + Datum datum3{scal_list}; + Datum datum4{val_list}; + conduit::Node datumRef1 = datum1.toNode(); + conduit::Node datumRef2 = datum2.toNode(); + conduit::Node datumRef3 = datum3.toNode(); + conduit::Node datumRef4 = datum4.toNode(); + EXPECT_EQ("Datum value", datumRef1["value"].as_string()); + std::vector node_tags; + auto tags_itr = datumRef1["tags"].children(); + while(tags_itr.has_next()) + node_tags.emplace_back(tags_itr.next().as_string()); + EXPECT_EQ(tags, node_tags); + + EXPECT_EQ("Datum units", datumRef2["units"].as_string()); + EXPECT_THAT(3.14, DoubleEq(datumRef2["value"].value())); + + // Conduit will pack vectors of numbers into arrays, but + // strings can only live as lists of Nodes + auto doub_array = datumRef3["value"].as_double_ptr(); + std::vectorscal_child_vals(doub_array, doub_array+datumRef3["value"].dtype().number_of_elements()); + std::vectorstr_child_vals; + auto str_itr = datumRef4["value"].children(); + while(str_itr.has_next()) + str_child_vals.emplace_back(str_itr.next().as_string()); + EXPECT_EQ(scal_list, scal_child_vals); + EXPECT_EQ(val_list, str_child_vals); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_Document.cpp b/src/axom/sina/tests/src/sina_Document.cpp new file mode 100644 index 0000000000..44ad9c8114 --- /dev/null +++ b/src/axom/sina/tests/src/sina_Document.cpp @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/CppBridge.hpp" +#include "axom/sina/include/Document.hpp" +#include "axom/sina/include/Run.hpp" + +#include "axom/sina/tests/include/TestRecord.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::ElementsAre; +using ::testing::HasSubstr; + +char const TEST_RECORD_TYPE[] = "test type"; +char const EXPECTED_RECORDS_KEY[] = "records"; +char const EXPECTED_RELATIONSHIPS_KEY[] = "relationships"; + +TEST(Document, create_fromNode_empty) { + conduit::Node documentAsNode; + RecordLoader loader; + Document document{documentAsNode, loader}; + EXPECT_EQ(0u, document.getRecords().size()); + EXPECT_EQ(0u, document.getRelationships().size()); +} + +TEST(Document, create_fromNode_wrongRecordsType) { + conduit::Node recordsAsNodes; + recordsAsNodes[EXPECTED_RECORDS_KEY] = 123; + RecordLoader loader; + try { + Document document{recordsAsNodes, loader}; + FAIL() << "Should not have been able to parse records. Have " + << document.getRecords().size(); + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_RECORDS_KEY)); + } +} + +TEST(Document, create_fromNode_withRecords) { + conduit::Node recordAsNode; + recordAsNode["type"] = "IntTestRecord"; + recordAsNode["id"] = "the ID"; + recordAsNode[TEST_RECORD_VALUE_KEY] = 123; + + conduit::Node recordsAsNodes; + recordsAsNodes.append().set(recordAsNode); + + conduit::Node documentAsNode; + documentAsNode[EXPECTED_RECORDS_KEY] = recordsAsNodes; + + RecordLoader loader; + loader.addTypeLoader("IntTestRecord", [](conduit::Node const &asNode) { + return internal::make_unique>(asNode); + }); + + Document document{documentAsNode, loader}; + auto &records = document.getRecords(); + ASSERT_EQ(1u, records.size()); + auto testRecord = dynamic_cast const *>(records[0].get()); + ASSERT_NE(nullptr, testRecord); + ASSERT_EQ(123, testRecord->getValue()); +} + +TEST(Document, create_fromNode_withRelationships) { + conduit::Node relationshipAsNode; + relationshipAsNode["subject"] = "the subject"; + relationshipAsNode["object"] = "the object"; + relationshipAsNode["predicate"] = "is related to"; + + conduit::Node relationshipsAsNodes; + relationshipsAsNodes.append().set(relationshipAsNode); + + conduit::Node documentAsNode; + documentAsNode[EXPECTED_RELATIONSHIPS_KEY] = relationshipsAsNodes; + + Document document{documentAsNode, RecordLoader{}}; + auto &relationships = document.getRelationships(); + ASSERT_EQ(1u, relationships.size()); + EXPECT_EQ("the subject", relationships[0].getSubject().getId()); + EXPECT_EQ(IDType::Global, relationships[0].getSubject().getType()); + EXPECT_EQ("the object", relationships[0].getObject().getId()); + EXPECT_EQ(IDType::Global, relationships[0].getObject().getType()); + EXPECT_EQ("is related to", relationships[0].getPredicate()); +} + +TEST(Document, create_fromJson_roundtrip) { + std::string orig_json = "{\"records\": [{\"type\": \"test_rec\",\"id\": \"test\"}],\"relationships\": []}"; + axom::sina::Document myDocument = Document(orig_json, createRecordLoaderWithAllKnownTypes()); + EXPECT_EQ(0, myDocument.getRelationships().size()); + ASSERT_EQ(1, myDocument.getRecords().size()); + EXPECT_EQ("test_rec", myDocument.getRecords()[0]->getType()); + std::string returned_json = myDocument.toJson(0,0,"",""); + EXPECT_EQ(orig_json, returned_json); +} + +TEST(Document, create_fromJson_full) { + std::string long_json = "{\"records\": [{\"type\": \"foo\",\"id\": \"test_1\",\"user_defined\":{\"name\":\"bob\"},\"files\":{\"foo/bar.png\":{\"mimetype\":\"image\"}},\"data\":{\"scalar\": {\"value\": 500,\"units\": \"miles\"}}},{\"type\":\"bar\",\"id\": \"test_2\",\"data\": {\"scalar_list\": {\"value\": [1, 2, 3]}, \"string_list\": {\"value\": [\"a\",\"wonderful\",\"world\"], \"tags\":[\"observation\"]}}},{\"type\": \"run\",\"application\":\"sina_test\",\"id\": \"test_3\",\"data\":{\"scalar\": {\"value\": 12.3, \"units\": \"g/s\", \"tags\": [\"hi\"]}, \"scalar_list\": {\"value\": [1,2,3.0,4]}}}, {\"type\": \"bar\",\"id\": \"test_4\",\"data\":{\"string\": {\"value\": \"yarr\"}, \"string_list\": {\"value\": [\"y\",\"a\",\"r\"]}}, \"files\":{\"test/test.png\":{}}, \"user_defined\":{\"hello\":\"there\"}}],\"relationships\": [{\"predicate\": \"completes\",\"subject\": \"test_2\",\"object\": \"test_1\"},{\"subject\": \"test_3\", \"predicate\": \"overrides\", \"object\": \"test_4\"}]}"; + axom::sina::Document myDocument = Document(long_json, createRecordLoaderWithAllKnownTypes()); + EXPECT_EQ(2, myDocument.getRelationships().size()); + auto &records = myDocument.getRecords(); + EXPECT_EQ(4, records.size()); +} + +TEST(Document, create_fromJson_value_check) { + std::string data_json = "{\"records\": [{\"type\": \"run\", \"application\":\"test\", \"id\": \"test_1\",\"data\":{\"int\": {\"value\": 500,\"units\": \"miles\"}, \"str/ings\": {\"value\":[\"z\", \"o\", \"o\"]}}, \"files\":{\"test/test.png\":{}}}]}"; + axom::sina::Document myDocument = Document(data_json, createRecordLoaderWithAllKnownTypes()); + EXPECT_EQ(0, myDocument.getRelationships().size()); + auto &records = myDocument.getRecords(); + EXPECT_EQ(1, records.size()); + EXPECT_EQ(records[0]->getType(), "run"); + auto &data = records[0]->getData(); + EXPECT_EQ(data.at("int").getScalar(), 500.0); + std::vector expected_string_vals = {"z", "o", "o"}; + EXPECT_EQ(data.at("str/ings").getStringArray(), expected_string_vals); + EXPECT_EQ(records[0]->getFiles().count(File{"test/test.png"}), 1); +} + +TEST(Document, toNode_empty) { + // A sina document should always have, at minimum, both records and + // relationships as empty arrays. + Document const document; + conduit::Node asNode = document.toNode(); + EXPECT_TRUE(asNode[EXPECTED_RECORDS_KEY].dtype().is_list()); + EXPECT_EQ(0, asNode[EXPECTED_RECORDS_KEY].number_of_children()); + EXPECT_TRUE(asNode[EXPECTED_RELATIONSHIPS_KEY].dtype().is_list()); + EXPECT_EQ(0, asNode[EXPECTED_RELATIONSHIPS_KEY].number_of_children()); +} + +TEST(Document, toNode_records) { + Document document; + std::string expectedIds[] = {"id 1", "id 2", "id 3"}; + std::string expectedValues[] = {"value 1", "value 2", "value 3"}; + + auto numRecords = sizeof(expectedIds) / sizeof(expectedIds[0]); + for (std::size_t i = 0; i < numRecords; ++i) { + document.add(internal::make_unique>( + expectedIds[i], TEST_RECORD_TYPE, expectedValues[i])); + } + + auto asNode = document.toNode(); + + auto record_nodes = asNode[EXPECTED_RECORDS_KEY]; + ASSERT_EQ(numRecords, record_nodes.number_of_children()); + for (auto i = 0; i < record_nodes.number_of_children(); ++i) { + auto &actualNode = record_nodes[i]; + EXPECT_EQ(expectedIds[i], actualNode["id"].as_string()); + EXPECT_EQ(TEST_RECORD_TYPE, actualNode["type"].as_string()); + EXPECT_EQ(expectedValues[i], + actualNode[TEST_RECORD_VALUE_KEY].as_string()); + } +} + +TEST(Document, toNode_relationships) { + Document document; + std::string expectedSubjects[] = {"subject 1", "subject 2"}; + std::string expectedObjects[] = {"object 1", "object 2"}; + std::string expectedPredicates[] = {"predicate 1", "predicate 2"}; + + auto numRecords = sizeof(expectedSubjects) / sizeof(expectedSubjects[0]); + for (unsigned long i = 0; i < numRecords; ++i) { + document.add(Relationship{ + ID{expectedSubjects[i], IDType::Global}, + expectedPredicates[i], + ID{expectedObjects[i], IDType::Global}, + }); + } + + auto asNode = document.toNode(); + + auto relationship_nodes = asNode[EXPECTED_RELATIONSHIPS_KEY]; + ASSERT_EQ(numRecords, relationship_nodes.number_of_children()); + for (auto i = 0; i < relationship_nodes.number_of_children(); ++i) { + auto &actualRelationship = relationship_nodes[i]; + EXPECT_EQ(expectedSubjects[i], actualRelationship["subject"].as_string()); + EXPECT_EQ(expectedObjects[i], actualRelationship["object"].as_string()); + EXPECT_EQ(expectedPredicates[i], actualRelationship["predicate"].as_string()); + } +} + + +/** + * Instances of this class acquire a temporary file name when created + * and delete the file when destructed. + * + * NOTE: This class uses unsafe methods and should only be used for testing + * purposes. DO NOT move it to the main code!!!! + */ +class NamedTempFile { +public: + NamedTempFile(); + + // As a resource-holding class, we don't want this to be copyable + // (or movable since there is no reason to return it from a function) + NamedTempFile(NamedTempFile const &) = delete; + + NamedTempFile(NamedTempFile &&) = delete; + + NamedTempFile &operator=(NamedTempFile const &) = delete; + + NamedTempFile &operator=(NamedTempFile &&) = delete; + + ~NamedTempFile(); + + std::string const &getName() const { + return fileName; + } + +private: + std::string fileName; +}; + +NamedTempFile::NamedTempFile() { + std::vector tmpFileName; + tmpFileName.resize(L_tmpnam); + // tmpnam is not the best way to do this, but it is standard and this is + // only a test. + if (!std::tmpnam(tmpFileName.data())) { + throw std::ios::failure{"Could not get temporary file"}; + } + fileName = tmpFileName.data(); +} + +NamedTempFile::~NamedTempFile() { + std::remove(fileName.data()); +} + +TEST(Document, saveDocument) { + NamedTempFile tmpFile; + + // First, write some random stuff to the temp file to make sure it is + // overwritten. + { + std::ofstream fout{tmpFile.getName()}; + fout << "Initial contents"; + } + + Document document; + document.add(internal::make_unique(ID{"the id", IDType::Global}, + "the type")); + + saveDocument(document, tmpFile.getName()); + + conduit::Node readContents; + { + std::ifstream fin{tmpFile.getName()}; + std::stringstream f_buf; + f_buf << fin.rdbuf(); + readContents.parse(f_buf.str(),"json"); + } + + ASSERT_TRUE(readContents[EXPECTED_RECORDS_KEY].dtype().is_list()); + EXPECT_EQ(1, readContents[EXPECTED_RECORDS_KEY].number_of_children()); + auto &readRecord = readContents[EXPECTED_RECORDS_KEY][0]; + EXPECT_EQ("the id", readRecord["id"].as_string()); + EXPECT_EQ("the type", readRecord["type"].as_string()); +} + +TEST(Document, load_specifiedRecordLoader) { + using RecordType = TestRecord; + auto originalRecord = internal::make_unique( + "the ID", "my type", 123); + Document originalDocument; + originalDocument.add(std::move(originalRecord)); + + NamedTempFile file; + { + std::ofstream fout{file.getName()}; + fout << originalDocument.toNode().to_json(); + } + + RecordLoader loader; + loader.addTypeLoader("my type", [](conduit::Node const &asNode) { + return internal::make_unique( + getRequiredString("id", asNode, "Test type"), + getRequiredString("type", asNode, "Test type"), + static_cast(getRequiredField(TEST_RECORD_VALUE_KEY, asNode, + "Test type").as_int64())); + }); + Document loadedDocument = loadDocument(file.getName(), loader); + ASSERT_EQ(1u, loadedDocument.getRecords().size()); + auto loadedRecord = dynamic_cast( + loadedDocument.getRecords()[0].get()); + ASSERT_NE(nullptr, loadedRecord); + EXPECT_EQ(123, loadedRecord->getValue()); +} + +TEST(Document, load_defaultRecordLoaders) { + auto originalRun = internal::make_unique( + ID{"the ID", IDType::Global}, "the app", "1.2.3", "jdoe"); + Document originalDocument; + originalDocument.add(std::move(originalRun)); + + NamedTempFile file; + { + std::ofstream fout{file.getName()}; + fout << originalDocument.toNode().to_json(); + } + + Document loadedDocument = loadDocument(file.getName()); + ASSERT_EQ(1u, loadedDocument.getRecords().size()); + auto loadedRun = dynamic_cast( + loadedDocument.getRecords()[0].get()); + EXPECT_NE(nullptr, loadedRun); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_File.cpp b/src/axom/sina/tests/src/sina_File.cpp new file mode 100644 index 0000000000..47385eb708 --- /dev/null +++ b/src/axom/sina/tests/src/sina_File.cpp @@ -0,0 +1,81 @@ +#include "gtest/gtest.h" + +#include "axom/sina/include/File.hpp" +#include "axom/sina/include/ConduitUtil.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +char const EXPECTED_MIMETYPE_KEY[] = "mimetype"; +char const EXPECTED_TAGS_KEY[] = "tags"; + +TEST(File, construct_differentType) { + File f1{"from literal"}; + File f2{std::string{"from std::string"}}; + EXPECT_EQ("from literal", f1.getUri()); + EXPECT_EQ("from std::string", f2.getUri()); +} + +TEST(File, setMimeType) { + File file{"the URI"}; + file.setMimeType("mime"); + EXPECT_EQ("the URI", file.getUri()); + EXPECT_EQ("mime", file.getMimeType()); +} + +TEST(File, setTags) { + std::vector tags = {"these", "are", "tags"}; + File file{"the URI"}; + file.setTags(tags); + EXPECT_EQ("the URI", file.getUri()); + EXPECT_EQ(tags, file.getTags()); +} + +TEST(File, create_fromNode_basic) { + std::string uri = "the URI"; + conduit::Node basic_file(conduit::DataType::object()); + File file{uri, basic_file}; + EXPECT_EQ(uri, file.getUri()); + EXPECT_EQ("", file.getMimeType()); + EXPECT_EQ(0, file.getTags().size()); +} + +TEST(File, create_fromNode_complete) { + std::string uri = "another/uri.txt"; + std::vector tags = {"tags", "are", "fun"}; + conduit::Node full_file(conduit::DataType::object()); + full_file[EXPECTED_MIMETYPE_KEY] = "the mime type"; + addStringsToNode(full_file, EXPECTED_TAGS_KEY, tags); + File file{uri, full_file}; + EXPECT_EQ(uri, file.getUri()); + EXPECT_EQ("the mime type", file.getMimeType()); + EXPECT_EQ(tags, file.getTags()); +} + +TEST(File, toNode_basic) { + File file{"the URI"}; + auto asNode = file.toNode(); + EXPECT_FALSE(asNode.has_child(EXPECTED_MIMETYPE_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_TAGS_KEY)); +} + +TEST(File, toNode_complete) { + std::vector tags = {"these", "are", "tags"}; + File file{"the URI"}; + file.setMimeType("the mime type"); + file.setTags(tags); + auto asNode = file.toNode(); + EXPECT_EQ("the mime type", asNode[EXPECTED_MIMETYPE_KEY].as_string()); + EXPECT_EQ(tags, file.getTags()); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_ID.cpp b/src/axom/sina/tests/src/sina_ID.cpp new file mode 100644 index 0000000000..bcdb5a97d3 --- /dev/null +++ b/src/axom/sina/tests/src/sina_ID.cpp @@ -0,0 +1,95 @@ +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "conduit.hpp" + +#include "axom/sina/include/ID.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::HasSubstr; + +TEST(ID, create) { + ID id1{"the name", IDType::Local}; + ID id2{"another name", IDType::Global}; + + EXPECT_EQ("the name", id1.getId()); + EXPECT_EQ("another name", id2.getId()); + + EXPECT_EQ(IDType::Local, id1.getType()); + EXPECT_EQ(IDType::Global, id2.getType()); +} + +TEST(IDField, create) { + ID id{"the id", IDType::Global}; + internal::IDField field{id, "local name", "global name"}; + EXPECT_EQ("the id", field.getID().getId()); + EXPECT_EQ(IDType::Global, field.getID().getType()); + EXPECT_EQ("local name", field.getLocalName()); + EXPECT_EQ("global name", field.getGlobalName()); +} + +TEST(IDField, createFromNode_local) { + conduit::Node object; + object["local id key"] = "the id"; + internal::IDField field{object, "local id key", "global id key"}; + EXPECT_EQ("the id", field.getID().getId()); + EXPECT_EQ(IDType::Local, field.getID().getType()); + EXPECT_EQ("local id key", field.getLocalName()); + EXPECT_EQ("global id key", field.getGlobalName()); +} + +TEST(IDField, createFromNode_global) { + conduit::Node object; + object["local id key"] = "local id"; + object["global id key"] = "global id"; + + internal::IDField field{object, "local id key", "global id key"}; + EXPECT_EQ("global id", field.getID().getId()); + EXPECT_EQ(IDType::Global, field.getID().getType()); + EXPECT_EQ("local id key", field.getLocalName()); + EXPECT_EQ("global id key", field.getGlobalName()); +} + +TEST(IDField, createFromNode_missingKeys) { + conduit::Node object(conduit::DataType::object()); + try { + internal::IDField field{object, "local id key", "global id key"}; + FAIL() << "Should have gotten a value error"; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr("local id key")); + EXPECT_THAT(expected.what(), HasSubstr("global id key")); + } +} + +TEST(IDField, toNode_local) { + ID id{"the id", IDType::Local}; + internal::IDField field{id, "local name", "global name"}; + conduit::Node value; + field.addTo(value); + EXPECT_EQ("the id", value["local name"].as_string()); + EXPECT_FALSE(value.has_child("global name")); +} + +TEST(IDField, toNode_global) { + ID id{"the id", IDType::Global}; + internal::IDField field{id, "local name", "global name"}; + conduit::Node value; + field.addTo(value); + EXPECT_EQ("the id", value["global name"].as_string()); + EXPECT_FALSE(value.has_child("local name")); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_Record.cpp b/src/axom/sina/tests/src/sina_Record.cpp new file mode 100644 index 0000000000..3878cfd6e2 --- /dev/null +++ b/src/axom/sina/tests/src/sina_Record.cpp @@ -0,0 +1,460 @@ +#include +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/Record.hpp" +#include "axom/sina/include/CppBridge.hpp" + +#include "axom/sina/tests/include/ConduitTestUtils.hpp" +#include "axom/sina/tests/include/TestRecord.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::Contains; +using ::testing::ElementsAre; +using ::testing::Key; +using ::testing::HasSubstr; +using ::testing::DoubleEq; +using ::testing::Not; + +char const EXPECTED_TYPE_KEY[] = "type"; +char const EXPECTED_LOCAL_ID_KEY[] = "local_id"; +char const EXPECTED_GLOBAL_ID_KEY[] = "id"; +char const EXPECTED_DATA_KEY[] = "data"; +char const EXPECTED_LIBRARY_DATA_KEY[] = "library_data"; +char const EXPECTED_FILES_KEY[] = "files"; +char const EXPECTED_USER_DEFINED_KEY[] = "user_defined"; +char const LIBRARY_DATA_ID_DATUM[] = "SINA_librarydata_id"; +char const LIBRARY_DATA_TYPE_DATUM[] = "SINA_librarydata_type"; + +TEST(Record, create_typeMissing) { + conduit::Node originalNode; + originalNode[EXPECTED_LOCAL_ID_KEY] = "the ID"; + try { + Record record{originalNode}; + FAIL() << "Should have failed due to missing type"; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_TYPE_KEY)); + } +} + +TEST(Record, add_data_existing_key) { + Record record{ID{"the id", IDType::Local}, "test_record"}; + record.add("key1", Datum{"val1"}); + EXPECT_EQ("val1", record.getData().at("key1").getValue()); + record.add("key1", Datum{"val2"}); + EXPECT_EQ("val2", record.getData().at("key1").getValue()); +} + +TEST(Record, add_curve_set_existing_key) { + Record record{ID{"the id", IDType::Local}, "test_record"}; + + CurveSet cs1{"cs1"}; + cs1.addDependentCurve(Curve{"original", {1, 2, 3}}); + record.add(cs1); + + auto &csAfterFirstInsert = record.getCurveSets(); + ASSERT_THAT(csAfterFirstInsert, Contains(Key("cs1"))); + EXPECT_THAT(csAfterFirstInsert.at("cs1").getDependentCurves(), + Contains(Key("original"))); + + CurveSet cs2{"cs1"}; + cs2.addDependentCurve(Curve{"new", {1, 2, 3}}); + record.add(cs2); + + auto &csAfterSecondInsert = record.getCurveSets(); + ASSERT_THAT(csAfterSecondInsert, Contains(Key("cs1"))); + EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), + Not(Contains(Key("original")))); + EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), + Contains(Key("new"))); +} + +TEST(Record, remove_file) { + Record record{ID{"the id", IDType::Local}, "test_record"}; + std::string path = "the/path.txt"; + + File original{path}; + original.setMimeType("txt"); + record.add(original); + EXPECT_EQ(1u, record.getFiles().size()); + EXPECT_EQ("txt", record.getFiles().find(File{path})->getMimeType()); + + record.remove(original); + EXPECT_EQ(0u, record.getFiles().size()); +} + +TEST(Record, add_file_existing_key) { + Record record{ID{"the id", IDType::Local}, "test_record"}; + std::string path = "the/path.txt"; + + File original{path}; + original.setMimeType("txt"); + record.add(original); + EXPECT_EQ(1u, record.getFiles().size()); + EXPECT_EQ("txt", record.getFiles().find(File{path})->getMimeType()); + + File replacement{path}; + replacement.setMimeType("image"); + record.add(replacement); + EXPECT_EQ(1u, record.getFiles().size()); + EXPECT_EQ("image", record.getFiles().find(File{path})->getMimeType()); +} + +TEST(Record, add_child_record_as_library_data) { + Record parentRecord{ID{"parent id", IDType::Local}, "test_record_parent"}; + Record childRecord{ID{"child id", IDType::Local}, "test_record_child"}; + parentRecord.addRecordAsLibraryData(childRecord, "child"); + auto &parentLibData = parentRecord.getLibraryData(); + ASSERT_THAT(parentLibData, Contains(Key("child"))); + auto &childLibContents = parentLibData.at("child")->getData(); + ASSERT_THAT(childLibContents, Contains(Key(LIBRARY_DATA_ID_DATUM))); + EXPECT_EQ("child id", childLibContents.at(LIBRARY_DATA_ID_DATUM).getValue()); + ASSERT_THAT(childLibContents, Contains(Key(LIBRARY_DATA_TYPE_DATUM))); + EXPECT_EQ("test_record_child", childLibContents.at(LIBRARY_DATA_TYPE_DATUM).getValue()); +} + +TEST(Record, add_child_record_as_library_data_with_data) { + Record parentRecord{ID{"parent id", IDType::Local}, "test_record_parent"}; + Record childRecord{ID{"child id", IDType::Local}, "test_record_child"}; + childRecord.add("key1", Datum{"val1"}); + parentRecord.addRecordAsLibraryData(childRecord, "child"); + auto &childLibContents = parentRecord.getLibraryData().at("child")->getData(); + ASSERT_THAT(childLibContents, Contains(Key("key1"))); + EXPECT_EQ("val1", childLibContents.at("key1").getValue()); +} + +TEST(Record, add_child_record_as_library_data_with_files) { + Record parentRecord{ID{"parent id", IDType::Local}, "test_record_parent"}; + Record childRecord{ID{"child id", IDType::Local}, "test_record_child"}; + std::string path = "the/path.txt"; + File childFile{path}; + childFile.setMimeType("txt"); + childRecord.add(childFile); + parentRecord.addRecordAsLibraryData(childRecord, "child"); + EXPECT_EQ(1u, parentRecord.getFiles().size()); + EXPECT_EQ("txt", parentRecord.getFiles().find(File{path})->getMimeType()); +} + +TEST(Record, create_localId_fromNode) { + conduit::Node originalNode; + originalNode[EXPECTED_LOCAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + Record record{originalNode}; + EXPECT_EQ("my type", record.getType()); + EXPECT_EQ("the ID", record.getId().getId()); + EXPECT_EQ(IDType::Local, record.getId().getType()); +} + +TEST(Record, create_globalId_fromNode) { + conduit::Node originalNode; + originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + Record record{originalNode}; + EXPECT_EQ("my type", record.getType()); + EXPECT_EQ("the ID", record.getId().getId()); + EXPECT_EQ(IDType::Global, record.getId().getType()); +} + +TEST(Record, create_globalId_withContent) { + conduit::Node originalNode; + originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + originalNode[EXPECTED_DATA_KEY]; + originalNode[EXPECTED_LIBRARY_DATA_KEY]; + + std::string name1 = "datum name 1"; + std::string name2 = "datum name 2/with/slash"; + + conduit::Node name1_node; + name1_node["value"] = "value 1"; + originalNode[EXPECTED_DATA_KEY][name1] = name1_node; + + conduit::Node name2_node; + name2_node["value"] = 2.22; + name2_node["units"] = "g/L"; + addStringsToNode(name2_node, "tags", {"tag1","tag2"}); + name2_node["value"] = 2.22; + originalNode[EXPECTED_DATA_KEY].add_child(name2) = name2_node; + + std::string libName = "my_lib"; + conduit::Node libNode; + std::string name3 = "datum name 3"; + conduit::Node name3_node; + name3_node["value"] = "value 3"; + libNode[EXPECTED_DATA_KEY]; + libNode[EXPECTED_DATA_KEY][name3] = name3_node; + originalNode[EXPECTED_LIBRARY_DATA_KEY][libName] = libNode; + + Record record{originalNode}; + auto &data = record.getData(); + ASSERT_EQ(2u, data.size()); + EXPECT_EQ("value 1", data.at(name1).getValue()); + EXPECT_THAT(2.22, DoubleEq(data.at(name2).getScalar())); + EXPECT_EQ("g/L", data.at(name2).getUnits()); + EXPECT_EQ("tag1", data.at(name2).getTags()[0]); + EXPECT_EQ("tag2", data.at(name2).getTags()[1]); + + auto &libdata = record.getLibraryData(); + EXPECT_THAT(libdata, Contains(Key(libName))); + EXPECT_EQ("value 3", libdata.at(libName)->getData().at(name3).getValue()); +} + +TEST(Record, create_globalId_files) { + conduit::Node originalNode; + originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + originalNode[EXPECTED_FILES_KEY]; + + std::string uri1 = "/some/uri.txt"; + std::string uri2 = "www.anotheruri.com"; + std::string uri3 = "yet another uri"; + originalNode[EXPECTED_FILES_KEY].add_child(uri1); + originalNode[EXPECTED_FILES_KEY].add_child(uri2); + originalNode[EXPECTED_FILES_KEY].add_child(uri3); + Record record{originalNode}; + auto &files = record.getFiles(); + ASSERT_EQ(3u, files.size()); + EXPECT_EQ(1, files.count(File{uri1})); + EXPECT_EQ(1, files.count(File{uri2})); + EXPECT_EQ(1, files.count(File{uri3})); +} + + +TEST(Record, create_fromNode_curveSets) { + conduit::Node recordAsNode = parseJsonValue(R"({ + "id": "myId", + "type": "myType", + "curve_sets": { + "cs1": { + "independent": { + "i1": { "value": [1, 2, 3]} + }, + "dependent": { + "d1": { "value": [4, 5, 6]} + } + } + } + })"); + Record record{recordAsNode}; + auto &curveSets = record.getCurveSets(); + ASSERT_THAT(curveSets, Contains(Key("cs1"))); +} + +TEST(Record, create_fromNode_userDefined) { + conduit::Node originalNode; + originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + originalNode[EXPECTED_USER_DEFINED_KEY]["k1"] = "v1"; + originalNode[EXPECTED_USER_DEFINED_KEY]["k2"] = 123; + std::vector k3_vals{1, 2, 3}; + originalNode[EXPECTED_USER_DEFINED_KEY]["k3"] = k3_vals; + + Record record{originalNode}; + auto const &userDefined = record.getUserDefinedContent(); + EXPECT_EQ("v1", userDefined["k1"].as_string()); + EXPECT_EQ(123, userDefined["k2"].as_int()); + auto int_array = userDefined["k3"].as_int_ptr(); + std::vectorudef_ints(int_array, int_array+userDefined["k3"].dtype().number_of_elements()); + EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); +} + +TEST(Record, getUserDefined_initialConst) { + ID id{"the id", IDType::Local}; + Record const record{id, "my type"}; + conduit::Node const &userDefined = record.getUserDefinedContent(); + EXPECT_TRUE(userDefined.dtype().is_empty()); +} + +TEST(Record, getUserDefined_initialNonConst) { + ID id{"the id", IDType::Local}; + Record record{id, "my type"}; + conduit::Node &initialUserDefined = record.getUserDefinedContent(); + EXPECT_TRUE(initialUserDefined.dtype().is_empty()); + initialUserDefined["foo"] = 123; + EXPECT_EQ(123, record.getUserDefinedContent()["foo"].as_int()); +} + +TEST(Record, toNode_localId) { + ID id{"the id", IDType::Global}; + Record record{id, "my type"}; + auto asNode = record.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + EXPECT_EQ("my type", asNode[EXPECTED_TYPE_KEY].as_string()); + EXPECT_EQ("the id", asNode[EXPECTED_GLOBAL_ID_KEY].as_string()); + EXPECT_TRUE(asNode[EXPECTED_LOCAL_ID_KEY].dtype().is_empty()); +} + +TEST(Record, toNode_globalId) { + ID id{"the id", IDType::Local}; + Record record{id, "my type"}; + auto asNode = record.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + EXPECT_EQ("my type", asNode[EXPECTED_TYPE_KEY].as_string()); + EXPECT_EQ("the id", asNode[EXPECTED_LOCAL_ID_KEY].as_string()); + EXPECT_TRUE(asNode[EXPECTED_GLOBAL_ID_KEY].dtype().is_empty()); +} + +TEST(Record, toNode_default_values) { + ID id{"the id", IDType::Global}; + Record record{id, "my type"}; + auto asNode = record.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + // We want to be sure that unset optional fields aren't present + EXPECT_FALSE(asNode.has_child(EXPECTED_DATA_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_FILES_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_USER_DEFINED_KEY)); +} + +TEST(Record, toNode_userDefined) { + ID id{"the id", IDType::Local}; + Record record{id, "my type"}; + conduit::Node userDef; + userDef["k1"] = "v1"; + userDef["k2"] = 123; + std::vector int_vals{1, 2, 3}; + userDef["k3"] = int_vals; + record.setUserDefinedContent(userDef); + + auto asNode = record.toNode(); + + auto userDefined = asNode[EXPECTED_USER_DEFINED_KEY]; + EXPECT_EQ("v1", userDefined["k1"].as_string()); + EXPECT_EQ(123, userDefined["k2"].as_int()); + auto int_array = userDefined["k3"].as_int_ptr(); + std::vectorudef_ints(int_array, int_array+userDefined["k3"].dtype().number_of_elements()); + EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); +} + +TEST(Record, toNode_data) { + ID id{"the id", IDType::Local}; + Record record{id, "my type"}; + std::string name1 = "name1"; + std::string value1 = "value1"; + Datum datum1 = Datum{value1}; + datum1.setUnits("some units"); + datum1.setTags({"tag1"}); + record.add(name1, datum1); + std::string name2 = "name2"; + record.add(name2, Datum{2.}); + auto asNode = record.toNode(); + ASSERT_EQ(2u, asNode[EXPECTED_DATA_KEY].number_of_children()); + EXPECT_EQ("value1", asNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); + EXPECT_EQ("some units", asNode[EXPECTED_DATA_KEY][name1]["units"].as_string()); + EXPECT_EQ("tag1", asNode[EXPECTED_DATA_KEY][name1]["tags"][0].as_string()); + + EXPECT_THAT(asNode[EXPECTED_DATA_KEY][name2]["value"].as_double(), + DoubleEq(2.)); + EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["units"].dtype().is_empty()); + EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["tags"].dtype().is_empty()); +} + +TEST(Record, toNode_dataWithSlashes) { + ID id{"the id", IDType::Local}; + Record record{id, "my type"}; + std::string name = "name/with/slashes"; + std::string value = "the value"; + Datum datum = Datum{value}; + record.add(name, datum); + auto asNode = record.toNode(); + ASSERT_EQ(1u, asNode[EXPECTED_DATA_KEY].number_of_children()); + EXPECT_EQ("the value", asNode[EXPECTED_DATA_KEY].child(name)["value"].as_string()); +} + +TEST(Record, toNode_files) { + ID id{"the id", IDType::Local}; + Record record{id, "my type"}; + std::string uri1 = "a/file/path/foo.png"; + std::string uri2 = "uri2"; + File file{uri1}; + file.setMimeType("mt1"); + record.add(file); + record.add(File{uri2}); + // Identical uris should overwrite + record.add(File{uri2}); + auto asNode = record.toNode(); + ASSERT_EQ(2u, asNode[EXPECTED_FILES_KEY].number_of_children()); + auto &child_with_slashes = asNode[EXPECTED_FILES_KEY].child(uri1); + EXPECT_EQ("mt1", child_with_slashes["mimetype"].as_string()); + EXPECT_TRUE(asNode[EXPECTED_FILES_KEY][uri2]["mimetype"].dtype().is_empty()); +} + +TEST(Record, toNode_curveSets) { + ID id{"the id", IDType::Local}; + Record record{id, "my type"}; + CurveSet cs{"myCurveSet/with/slash"}; + cs.addIndependentCurve(Curve{"myCurve", {1, 2, 3}}); + record.add(cs); + auto expected = R"({ + "local_id": "the id", + "type": "my type", + "curve_sets": { + "myCurveSet/with/slash": { + "independent": { + "myCurve": { + "value": [1.0, 2.0, 3.0] + } + }, + "dependent": {} + } + } + })"; + EXPECT_THAT(record.toNode(), MatchesJson(expected)); +} + +TEST(RecordLoader, load_missingLoader) { + RecordLoader loader; + conduit::Node asNode; + asNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + asNode[EXPECTED_TYPE_KEY] = "unknownType"; + auto loaded = loader.load(asNode); + auto &actualType = typeid(*loaded); + EXPECT_EQ(typeid(Record), actualType) << "Type was " << actualType.name(); +} + +TEST(RecordLoader, load_loaderPresent) { + RecordLoader loader; + EXPECT_FALSE(loader.canLoad("TestInt")); + EXPECT_FALSE(loader.canLoad("TestString")); + + loader.addTypeLoader("TestInt", + [](conduit::Node const &value) { + return internal::make_unique>(value); + }); + EXPECT_TRUE(loader.canLoad("TestInt")); + + loader.addTypeLoader("TestString", + [](conduit::Node const &value) { + return internal::make_unique>(value); + }); + EXPECT_TRUE(loader.canLoad("TestString")); + + conduit::Node asNode; + asNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + asNode[EXPECTED_TYPE_KEY] = "TestString"; + asNode[TEST_RECORD_VALUE_KEY] = "The value"; + auto loaded = loader.load(asNode); + auto testObjPointer = dynamic_cast *>(loaded.get()); + ASSERT_NE(nullptr, testObjPointer); + EXPECT_EQ("The value", testObjPointer->getValue()); + EXPECT_EQ("TestString", testObjPointer->getType()); +} + +TEST(RecordLoader, createRecordLoaderWithAllKnownTypes) { + RecordLoader loader = createRecordLoaderWithAllKnownTypes(); + EXPECT_TRUE(loader.canLoad("run")); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace diff --git a/src/axom/sina/tests/src/sina_Relationship.cpp b/src/axom/sina/tests/src/sina_Relationship.cpp new file mode 100644 index 0000000000..59149bd400 --- /dev/null +++ b/src/axom/sina/tests/src/sina_Relationship.cpp @@ -0,0 +1,156 @@ +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/Relationship.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +char const EXPECTED_GLOBAL_OBJECT_ID_KEY[] = "object"; +char const EXPECTED_LOCAL_OBJECT_ID_KEY[] = "local_object"; +char const EXPECTED_GLOBAL_SUBJECT_ID_KEY[] = "subject"; +char const EXPECTED_LOCAL_SUBJECT_ID_KEY[] = "local_subject"; +char const EXPECTED_PREDICATE_KEY[] = "predicate"; + +using ::testing::HasSubstr; + +TEST(Relationship, create) { + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; + + Relationship relationship{ID{subjectID, IDType::Global}, predicate, + ID{objectID, IDType::Local}}; + + EXPECT_EQ(subjectID, relationship.getSubject().getId()); + EXPECT_EQ(IDType::Global, relationship.getSubject().getType()); + EXPECT_EQ(objectID, relationship.getObject().getId()); + EXPECT_EQ(IDType::Local, relationship.getObject().getType()); + EXPECT_EQ(predicate, relationship.getPredicate()); +} + +TEST(Relationship, create_fromNode_validGlobalIDs) { + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; + + conduit::Node asNode; + asNode[EXPECTED_GLOBAL_SUBJECT_ID_KEY] = subjectID; + asNode[EXPECTED_GLOBAL_OBJECT_ID_KEY] = objectID; + asNode[EXPECTED_PREDICATE_KEY] = predicate; + + Relationship relationship{asNode}; + + EXPECT_EQ(subjectID, relationship.getSubject().getId()); + EXPECT_EQ(IDType::Global, relationship.getSubject().getType()); + EXPECT_EQ(objectID, relationship.getObject().getId()); + EXPECT_EQ(IDType::Global, relationship.getObject().getType()); + EXPECT_EQ(predicate, relationship.getPredicate()); +} + +TEST(Relationship, create_from_validLocalIDs) { + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; + + conduit::Node asNode; + asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY] = subjectID; + asNode[EXPECTED_LOCAL_OBJECT_ID_KEY] = objectID; + asNode[EXPECTED_PREDICATE_KEY] = predicate; + + Relationship relationship{asNode}; + + EXPECT_EQ(subjectID, relationship.getSubject().getId()); + EXPECT_EQ(IDType::Local, relationship.getSubject().getType()); + EXPECT_EQ(objectID, relationship.getObject().getId()); + EXPECT_EQ(IDType::Local, relationship.getObject().getType()); + EXPECT_EQ(predicate, relationship.getPredicate()); +} + +TEST(Relationship, create_fromNode_missingSubect) { + conduit::Node asNode; + asNode[EXPECTED_LOCAL_OBJECT_ID_KEY] = "the object"; + asNode[EXPECTED_PREDICATE_KEY] = "some predicate"; + try { + Relationship relationship{asNode}; + FAIL() << "Should have gotten an exception about a missing subject"; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_LOCAL_SUBJECT_ID_KEY)); + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_GLOBAL_SUBJECT_ID_KEY)); + } +} + +TEST(Relationship, create_fromNode_missingObject) { + conduit::Node asNode; + asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY] = "the subject"; + asNode[EXPECTED_PREDICATE_KEY] = "some predicate"; + + try { + Relationship relationship{asNode}; + FAIL() << "Should have gotten an exception about a missing object"; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_LOCAL_OBJECT_ID_KEY)); + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_GLOBAL_OBJECT_ID_KEY)); + } +} + +TEST(Relationship, create_fromNode_missingPredicate) { + conduit::Node asNode; + asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY] = "the subject"; + asNode[EXPECTED_LOCAL_OBJECT_ID_KEY] = "the object"; + + try { + Relationship relationship{asNode}; + FAIL() << "Should have gotten an exception about a missing predicate"; + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_PREDICATE_KEY)); + EXPECT_THAT(expected.what(), HasSubstr("Relationship")); + } +} + +TEST(Relationship, toNode_localIds) { + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; + + Relationship relationship{ID{subjectID, IDType::Local}, predicate, + ID{objectID, IDType::Local}}; + + conduit::Node asNode = relationship.toNode(); + + EXPECT_EQ(subjectID, asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY].as_string()); + EXPECT_EQ(objectID, asNode[EXPECTED_LOCAL_OBJECT_ID_KEY].as_string()); + EXPECT_EQ(predicate, asNode[EXPECTED_PREDICATE_KEY].as_string()); + EXPECT_FALSE(asNode.has_child(EXPECTED_GLOBAL_SUBJECT_ID_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_GLOBAL_OBJECT_ID_KEY)); +} + +TEST(Relationship, toNode_globalIds) { + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; + + Relationship relationship{ID{subjectID, IDType::Global}, predicate, + ID{objectID, IDType::Global}}; + + conduit::Node asNode = relationship.toNode(); + + EXPECT_EQ(subjectID, asNode[EXPECTED_GLOBAL_SUBJECT_ID_KEY].as_string()); + EXPECT_EQ(objectID, asNode[EXPECTED_GLOBAL_OBJECT_ID_KEY].as_string()); + EXPECT_EQ(predicate, asNode[EXPECTED_PREDICATE_KEY].as_string()); + EXPECT_FALSE(asNode.has_child(EXPECTED_LOCAL_SUBJECT_ID_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_LOCAL_OBJECT_ID_KEY)); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace \ No newline at end of file diff --git a/src/axom/sina/tests/src/sina_Run.cpp b/src/axom/sina/tests/src/sina_Run.cpp new file mode 100644 index 0000000000..4bb43d2a67 --- /dev/null +++ b/src/axom/sina/tests/src/sina_Run.cpp @@ -0,0 +1,98 @@ +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "axom/sina/include/Run.hpp" + +namespace axom +{ +namespace sina +{ +namespace testing +{ +namespace +{ + +using ::testing::HasSubstr; + +char const EXPECTED_TYPE_KEY[] = "type"; +char const EXPECTED_LOCAL_ID_KEY[] = "local_id"; +char const EXPECTED_GLOBAL_ID_KEY[] = "id"; +char const EXPECTED_APPLICATION_KEY[] = "application"; +char const EXPECTED_VERSION_KEY[] = "version"; +char const EXPECTED_USER_KEY[] = "user"; + +// Throughout, we have to use "axom::sina::Run" instead of just "Run" due to +// a conflict with the Run() function in gtest + +TEST(Run, create_fromnode_valid) { + conduit::Node originNode; + originNode[EXPECTED_TYPE_KEY] = "run"; + originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; + originNode[EXPECTED_APPLICATION_KEY] = "the app"; + originNode[EXPECTED_VERSION_KEY] = "1.2.3"; + originNode[EXPECTED_USER_KEY] = "jdoe"; + axom::sina::Run run{originNode}; + EXPECT_EQ("run", run.getType()); + EXPECT_EQ("the id", run.getId().getId()); + EXPECT_EQ(IDType::Global, run.getId().getType()); + EXPECT_EQ("the app", run.getApplication()); + EXPECT_EQ("1.2.3", run.getVersion()); + EXPECT_EQ("jdoe", run.getUser()); +} + +TEST(Run, create_fromNode_missingApplication) { + conduit::Node originNode; + originNode[EXPECTED_TYPE_KEY] = "run"; + originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; + originNode[EXPECTED_VERSION_KEY] = "1.2.3"; + originNode[EXPECTED_USER_KEY] = "jdoe"; + try { + axom::sina::Run run{originNode}; + FAIL() << "Application should be missing, but is " + << run.getApplication(); + } catch (std::invalid_argument const &expected) { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_APPLICATION_KEY)); + } +} + +TEST(Run, toNode) { + ID id{"the id", IDType::Global}; + axom::sina::Run run{id, "the app", "1.2.3", "jdoe"}; + auto asNode = run.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + EXPECT_EQ("run", asNode[EXPECTED_TYPE_KEY].as_string()); + EXPECT_EQ("the id", asNode[EXPECTED_GLOBAL_ID_KEY].as_string()); + EXPECT_TRUE(asNode[EXPECTED_LOCAL_ID_KEY].dtype().is_empty()); + EXPECT_EQ("the app", asNode[EXPECTED_APPLICATION_KEY].as_string()); + EXPECT_EQ("1.2.3", asNode[EXPECTED_VERSION_KEY].as_string()); + EXPECT_EQ("jdoe", asNode[EXPECTED_USER_KEY].as_string()); +} + +TEST(Run, addRunLoader) { + conduit::Node originNode; + originNode[EXPECTED_TYPE_KEY] = "run"; + originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; + originNode[EXPECTED_APPLICATION_KEY] = "the app"; + originNode[EXPECTED_VERSION_KEY] = "1.2.3"; + originNode[EXPECTED_USER_KEY] = "jdoe"; + + RecordLoader loader; + addRunLoader(loader); + + auto record = loader.load(originNode); + auto run = dynamic_cast(record.get()); + ASSERT_NE(nullptr, run); + EXPECT_EQ("run", run->getType()); + EXPECT_EQ("the id", run->getId().getId()); + EXPECT_EQ(IDType::Global, run->getId().getType()); + EXPECT_EQ("the app", run->getApplication()); + EXPECT_EQ("1.2.3", run->getVersion()); + EXPECT_EQ("jdoe", run->getUser()); +} + +} // end nameless namespace +} // end testing namespace +} // end sina namespace +} // end axom namespace From f21262763b1eafb40438ccb4dbf89f02dfedb1c6 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 30 May 2024 14:04:32 -0700 Subject: [PATCH 08/60] initial add of examples --- src/axom/sina/CMakeLists.txt | 6 +- src/axom/sina/examples/CMakeLists.txt | 31 +++++ src/axom/sina/examples/sina_basic.cpp | 15 +++ src/axom/sina/examples/sina_tutorial.cpp | 160 +++++++++++++++++++++++ 4 files changed, 209 insertions(+), 3 deletions(-) create mode 100644 src/axom/sina/examples/CMakeLists.txt create mode 100644 src/axom/sina/examples/sina_basic.cpp create mode 100644 src/axom/sina/examples/sina_tutorial.cpp diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index e186164970..5dde9ce19a 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -95,9 +95,9 @@ if(AXOM_ENABLE_TESTS) add_subdirectory(tests) endif() -# if(AXOM_ENABLE_EXAMPLES) -# add_subdirectory(examples) -# endif() +if(AXOM_ENABLE_EXAMPLES) + add_subdirectory(examples) +endif() #------------------------------------------------------------------------------ # Add code checks diff --git a/src/axom/sina/examples/CMakeLists.txt b/src/axom/sina/examples/CMakeLists.txt new file mode 100644 index 0000000000..6254fff79c --- /dev/null +++ b/src/axom/sina/examples/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level LICENSE file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) +#------------------------------------------------------------------------------ +# Sina examples +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# List of single source file examples +#------------------------------------------------------------------------------ +set(sina_example_sources + sina_basic.cpp + sina_tutorial.cpp +) + +set(sina_example_depends sina conduit::conduit) + +#------------------------------------------------------------------------------ +# Add targets and tests for Sina examples +#------------------------------------------------------------------------------ +foreach(src ${sina_example_sources}) + get_filename_component(exe_name ${src} NAME_WE) + axom_add_executable( + NAME ${exe_name}_ex + SOURCES ${src} + OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} + DEPENDS_ON ${sina_example_depends} + FOLDER axom/sina/examples + ) +endforeach() diff --git a/src/axom/sina/examples/sina_basic.cpp b/src/axom/sina/examples/sina_basic.cpp new file mode 100644 index 0000000000..b69d69b546 --- /dev/null +++ b/src/axom/sina/examples/sina_basic.cpp @@ -0,0 +1,15 @@ +#include "axom/sina.hpp" + +int main (void) { + // Create a new document + axom::sina::Document document; + // 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 run{ + new axom::sina::Run{runID, "My Sim Code", "1.2.3", "jdoe"}}; + // Add the run to the document + document.add(std::move(run)); + // Save the document directly to a file. + saveDocument(document, "MySinaData.json"); +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_tutorial.cpp b/src/axom/sina/examples/sina_tutorial.cpp new file mode 100644 index 0000000000..03340346a3 --- /dev/null +++ b/src/axom/sina/examples/sina_tutorial.cpp @@ -0,0 +1,160 @@ +#include "axom/sina.hpp" + +#include +#include + +namespace { + +//! [create record] +void createRecord() { + axom::sina::ID id{"some_record_id", axom::sina::IDType::Local}; + std::unique_ptr record{new axom::sina::Record{id, "my_record_type"}}; + + // Add the record to a document + axom::sina::Document doc; + doc.add(std::move(record)); +} +//! [create record] + +//! [create run] +void createRun() { + axom::sina::ID id{"some_run_id", axom::sina::IDType::Local}; + std::unique_ptr run{new axom::sina::Run{id, "My Sim Code", "1.2.3", "jdoe"}}; + + // Add the record to a document + axom::sina::Document doc; + doc.add(std::move(run)); +} +//! [create run] + +//! [adding data] +void addData(axom::sina::Record &record) { + // Add a scalar named "my_scalar" with the value 123.456 + record.add("my_scalar", axom::sina::Datum{123.456}); + + // Add a string named "my_string" with the value "abc" + record.add("my_string", axom::sina::Datum{"abc"}); + + // Add a list of scalars named "my_scalar_list" + std::vector scalarList = {1.2, -3.4, 5.6}; + record.add("my_scalar_list", axom::sina::Datum{scalarList}); + + // Add a list of strings named "my_string_list" + std::vector stringList = {"hi", "hello", "howdy"}; + record.add("my_string_list", axom::sina::Datum{stringList}); +} +//! [adding data] + +//! [curve sets] +void addCurveSets(axom::sina::Record &record) { + axom::sina::CurveSet timePlots{"time_plots"}; + + // Add the independent variable + timePlots.addIndependentCurve( + axom::sina::Curve{"time", {0.0, 0.1, 0.25, 0.3}}); + + // Add some dependent variables. + // The length of each must be the same as the length of the independent. + timePlots.addDependentCurve( + axom::sina::Curve{"temperature", {300.0, 310.0, 350.0, 400.0}}); + + timePlots.addDependentCurve( + axom::sina::Curve{"energy", {0.0, 10.0, 20.0, 30.0}}); + + // Associate the curve sets with the record + record.add(timePlots); +} + +//! [curve sets] + + +//! [file add_and_remove] +void addAndRemoveFileToRecord(axom::sina::Record &run) { + axom::sina::File my_file{"some/path.txt"}; + // Adds the file to the record's file list + run.add(my_file); + // Removes the file from the record's file list + run.remove(my_file); +} + +//! [file add_and_remove] + +//! [relationships] +void associateRunToStudy(axom::sina::Document &doc, axom::sina::Record const &uqStudy, axom::sina::Record const &run) { + doc.add(axom::sina::Relationship{uqStudy.getId(), "contains", run.getId()}); +} +//! [relationships] + + +//! [library data foo] +void foo_collectData(axom::sina::DataHolder &fooData) { + fooData.add("temperature", axom::sina::Datum{500}); + fooData.add("energy", axom::sina::Datum{1.2e10}); +} +//! [library data foo] + +//! [library data bar] +void bar_gatherData(axom::sina::DataHolder &barData) { + barData.add("temperature", axom::sina::Datum{400}); + barData.add("mass", axom::sina::Datum{15}); +} +//! [library data bar] + +//! [library data host] +void gatherAllData(axom::sina::Record &record) { + auto fooData = record.addLibraryData("foo"); + auto barData = record.addLibraryData("bar"); + + foo_collectData(*fooData); + bar_gatherData(*barData); + + record.add("temperature", axom::sina::Datum{450}); +} +//! [library data host] + +//! [io write] +void save(axom::sina::Document const &doc) { + axom::sina::saveDocument(doc, "my_output.json"); +} +//! [io write] + +//! [io read] +void load() { + axom::sina::Document doc = axom::sina::loadDocument("my_output.json"); +} +//! [io read] + +//! [user defined] +void addUserDefined(axom::sina::Record &record) { + conduit::Node &userDefined = record.getUserDefinedContent(); + userDefined["var_1"] = "a"; + userDefined["var_2"] = "b"; + + conduit::Node subNode; + subNode["sub_1"] = 10; + subNode["sub_2"] = 20; + userDefined["sub_structure"] = subNode; +} +//! [user defined] + +} + +int main() { + // Call everything to keep the compiler from complaining about unused functions + axom::sina::Record run{axom::sina::ID{"my_record", axom::sina::IDType::Global}, "my_record_type"}; + axom::sina::Record study{axom::sina::ID{"my_run", axom::sina::IDType::Global}, "UQ study"}; + axom::sina::Document doc; + addData(run); + createRecord(); + createRun(); + associateRunToStudy(doc, study, run); + gatherAllData(run); + addCurveSets(run); + addAndRemoveFileToRecord(run); + addUserDefined(run); + // TODO + // - Add Record to doc + // - Check output file to see if record shows up + save(doc); + load(); +} From 45628bff15123fe7d510f3a2eda5101e62b1f2d0 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Fri, 21 Jun 2024 13:06:22 -0700 Subject: [PATCH 09/60] get doxygen docs up and running --- src/axom/doxygen_mainpage.md | 2 ++ src/axom/sina/doxygen_mainpage.md | 2 +- src/axom/sina/include/Curve.hpp | 2 +- src/axom/sina/include/CurveSet.hpp | 3 +++ src/axom/sina/include/DataHolder.hpp | 36 ++++++++++++++++++++++++++-- src/axom/sina/include/Datum.hpp | 4 ++++ src/axom/sina/include/Document.hpp | 25 +++++++++++++++++-- src/axom/sina/include/ID.hpp | 2 +- src/axom/sina/include/Record.hpp | 9 +++++++ src/docs/dependencies.dot | 1 + src/docs/doxygen/Doxyfile.in | 4 ++++ 11 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/axom/doxygen_mainpage.md b/src/axom/doxygen_mainpage.md index 47e818fd85..0b53c447d5 100644 --- a/src/axom/doxygen_mainpage.md +++ b/src/axom/doxygen_mainpage.md @@ -14,6 +14,7 @@ Axom provides libraries that address common computer science needs. It grew fro * @subpage primaltop provides an API for geometric primitives and computational geometry tests. * @subpage questtop provides an API to query point distance and position relative to meshes. * @subpage sidretop provides a data store with hierarchical structure. +* @subpage sinatop provides an API to convert data to a common file format. * @subpage slamtop provides an API to construct and process meshes. * @subpage slictop provides infrastructure for logging application messages. * @subpage spintop provides spatial acceleration data structures, also known as spatial indexes. @@ -29,6 +30,7 @@ Dependencies between components are as follows: - Quest depends on Slam, Primal, Spin, and Mint - Klee depends on Sidre, Inlet and Primal - Multimat depends on Slic, and Slam +- Sina only depends on Core The figure below summarizes the dependencies between the modules. Solid links indicate hard dependencies; dashed links indicate optional dependencies. diff --git a/src/axom/sina/doxygen_mainpage.md b/src/axom/sina/doxygen_mainpage.md index 21015e1266..f45fac1e0f 100644 --- a/src/axom/sina/doxygen_mainpage.md +++ b/src/axom/sina/doxygen_mainpage.md @@ -1,7 +1,7 @@ Sina {#sinatop} ========= -[Sina](@ref axom::sina) provides an easy way to collect data directly within codes and output them to a common file format designed. This is accomplished in an object oriented manner through the following classes: +[Sina](@ref axom::sina) provides an easy way to collect data directly within codes and output them to a common file format. This is accomplished in an object oriented manner through the following classes: - [Curve](@ref axom::sina::Curve): represents a 1D curve - [CurveSet](@ref axom::sina::CurveSet): represents an entry in a record's "curve_set" diff --git a/src/axom/sina/include/Curve.hpp b/src/axom/sina/include/Curve.hpp index 2e65a82bc7..ea7f37bcd0 100644 --- a/src/axom/sina/include/Curve.hpp +++ b/src/axom/sina/include/Curve.hpp @@ -18,7 +18,7 @@ namespace sina { /** - * A Curve represents a 1-dimensional curve inside a \see CurveSet. + * A Curve represents a 1-dimensional curve inside a CurveSet. */ class Curve { public: diff --git a/src/axom/sina/include/CurveSet.hpp b/src/axom/sina/include/CurveSet.hpp index cdb22e2892..4ed4f4635b 100644 --- a/src/axom/sina/include/CurveSet.hpp +++ b/src/axom/sina/include/CurveSet.hpp @@ -30,6 +30,9 @@ namespace sina */ class CurveSet { public: + /** + * An unordered map of Curve objects. + */ using CurveMap = std::unordered_map; /** diff --git a/src/axom/sina/include/DataHolder.hpp b/src/axom/sina/include/DataHolder.hpp index edce5190b7..3e06a76333 100644 --- a/src/axom/sina/include/DataHolder.hpp +++ b/src/axom/sina/include/DataHolder.hpp @@ -24,17 +24,28 @@ namespace sina /** * A DataHolder is a basic container for certain types of information. * - * DataHolders contain curves, libraries, and data (\see Datum), and represent + * DataHolders contain curves, libraries, and data (Datum), and represent * all the information a library can have associated with it. Records expand * on DataHolders to contain additional info. * * \see Record - * \see LibraryData + * \see LibraryDataMap */ class DataHolder { public: + /** + * An unordered map of Datum objects. + */ using DatumMap = std::unordered_map; + + /** + * An unordered map of CurveSet objects. + */ using CurveSetMap = std::unordered_map; + + /** + * An unordered map of shared pointers to DataHolder objects. + */ using LibraryDataMap = std::unordered_map>; /** @@ -42,10 +53,19 @@ class DataHolder { */ DataHolder() = default; + /** + * Virtual destructor to automatically clean up resources held by an instance of the DataHolder class. + */ virtual ~DataHolder() = default; + /** + * Copy constructor that disallows this constructor type. + */ DataHolder(DataHolder const &) = delete; + /** + * Disable copy assignment. + */ DataHolder &operator=(DataHolder const &) = delete; /** @@ -99,6 +119,11 @@ class DataHolder { */ std::shared_ptr addLibraryData(std::string const &name); + /** + * Add a new library to this DataHolder with existing library data. + * + * @return a pointer to a new DataHolder for a library of the given name. + */ std::shared_ptr addLibraryData(std::string const &name, conduit::Node existingLibraryData); /** @@ -118,6 +143,13 @@ class DataHolder { std::shared_ptr getLibraryData(std::string const &libraryName) { return libraryData.at(libraryName); } + + /** + * Get a specific library associated with this DataHolder. + * Used when the object on which this function is called is a const. + * + * @return the dataholder's library data + */ std::shared_ptr const getLibraryData(std::string const &libraryName) const { return libraryData.at(libraryName); } diff --git a/src/axom/sina/include/Datum.hpp b/src/axom/sina/include/Datum.hpp index ca91a84f38..5600e1f1bb 100644 --- a/src/axom/sina/include/Datum.hpp +++ b/src/axom/sina/include/Datum.hpp @@ -42,12 +42,16 @@ enum class ValueType { * sina::Datum myOtherDatum{value}; * std::vector scalars = {1, 2, 20.0}; * sina::Datum myArrayDatum{scalars}; + * * //prints 1, corresponding to Scalar * std::cout << static_cast::type>(myDatum.getType()) << std::endl; + * * //prints 0, corresponding to String * std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; + * * //prints 3, corresponding to ScalarArray * std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; + * * myRecord->add(myDatum); * myOtherDatum.setUnits("km/s"); * myRecord->add(myOtherDatum); diff --git a/src/axom/sina/include/Document.hpp b/src/axom/sina/include/Document.hpp index 231a832bd0..6567a751f5 100644 --- a/src/axom/sina/include/Document.hpp +++ b/src/axom/sina/include/Document.hpp @@ -61,19 +61,40 @@ namespace sina */ class Document { public: + /** + * A vector of pointers to Record objects. + */ using RecordList = std::vector>; + + /** + * A vector of Relationship objects. + */ using RelationshipList = std::vector; + /** + * Construct an empty Document. + */ Document() = default; - // Since we hold pointers to polymorphic objects, we can't support - // copying or assignment + /** + * Disable copying Document objects. We must do this since we hold + * pointers to polymorphic objects. + */ Document(Document const &) = delete; + /** + * Disabling copy assignment. + */ Document &operator=(Document const &) = delete; + /** + * Move constructor which should be handled by the compiler. + */ Document(Document &&) = default; + /** + * Move assignment which should be handled by the compiler. + */ Document &operator=(Document &&) = default; /** diff --git a/src/axom/sina/include/ID.hpp b/src/axom/sina/include/ID.hpp index 5982ca2f5a..1ccdf09556 100644 --- a/src/axom/sina/include/ID.hpp +++ b/src/axom/sina/include/ID.hpp @@ -28,7 +28,7 @@ enum class IDType { }; /** - * An ID is used to represent the ID of an record. This class holds both the + * An ID is used to represent the ID of a Record. This class holds both the * actual ID and whether it is a local or global ID. */ class ID { diff --git a/src/axom/sina/include/Record.hpp b/src/axom/sina/include/Record.hpp index f00ceaf574..3e05356eef 100644 --- a/src/axom/sina/include/Record.hpp +++ b/src/axom/sina/include/Record.hpp @@ -67,6 +67,9 @@ struct FileHashByURI { */ class Record : public DataHolder { public: + /** + * An unordered set of File objects. + */ using FileSet = std::unordered_set; /** @@ -84,8 +87,14 @@ class Record : public DataHolder { */ explicit Record(conduit::Node const &asNode); + /** + * Disable the copy constructor. + */ Record(Record const &) = delete; + /** + * Disable copy assignment. + */ Record &operator=(Record const &) = delete; /** diff --git a/src/docs/dependencies.dot b/src/docs/dependencies.dot index 6ff79e1377..74eb633644 100644 --- a/src/docs/dependencies.dot +++ b/src/docs/dependencies.dot @@ -7,6 +7,7 @@ digraph dependencies { multimat -> {slic slam}; spin -> {slam primal}; sidre -> {slic core}; + sina -> {core}; slic -> core; slic -> lumberjack [style="dashed"]; lumberjack -> core; diff --git a/src/docs/doxygen/Doxyfile.in b/src/docs/doxygen/Doxyfile.in index 548d1bfad6..9f6d6cc9d6 100644 --- a/src/docs/doxygen/Doxyfile.in +++ b/src/docs/doxygen/Doxyfile.in @@ -797,6 +797,10 @@ INPUT = @PROJECT_SOURCE_DIR@/axom/doxygen_mainpage.md \ @PROJECT_SOURCE_DIR@/axom/sidre/doxygen_mainpage.md \ @PROJECT_SOURCE_DIR@/axom/sidre/core \ @PROJECT_SOURCE_DIR@/axom/sidre/spio \ + @PROJECT_SOURCE_DIR@/axom/sina \ + @PROJECT_SOURCE_DIR@/axom/sina/doxygen_mainpage.md \ + @PROJECT_SOURCE_DIR@/axom/sina/src \ + @PROJECT_SOURCE_DIR@/axom/sina/include \ @PROJECT_SOURCE_DIR@/axom/slam \ @PROJECT_SOURCE_DIR@/axom/slam/doxygen_mainpage.md \ @PROJECT_SOURCE_DIR@/axom/slam/policies \ From 234e1eb82dfdb550fe6fdb52ad901f3f7dd0e62c Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Fri, 21 Jun 2024 14:56:54 -0700 Subject: [PATCH 10/60] start work on sphinx docs --- src/axom/sina/docs/sphinx/core_concepts.rst | 25 +++ src/axom/sina/docs/sphinx/curve_sets.rst | 10 ++ src/axom/sina/docs/sphinx/documents.rst | 10 ++ src/axom/sina/docs/sphinx/index.rst | 44 +++++ src/axom/sina/docs/sphinx/records.rst | 10 ++ src/axom/sina/docs/sphinx/relationships.rst | 10 ++ src/axom/sina/docs/sphinx/tutorial.rst | 174 ++++++++++++++++++++ src/index.rst | 5 + 8 files changed, 288 insertions(+) create mode 100644 src/axom/sina/docs/sphinx/core_concepts.rst create mode 100644 src/axom/sina/docs/sphinx/curve_sets.rst create mode 100644 src/axom/sina/docs/sphinx/documents.rst create mode 100644 src/axom/sina/docs/sphinx/index.rst create mode 100644 src/axom/sina/docs/sphinx/records.rst create mode 100644 src/axom/sina/docs/sphinx/relationships.rst create mode 100644 src/axom/sina/docs/sphinx/tutorial.rst diff --git a/src/axom/sina/docs/sphinx/core_concepts.rst b/src/axom/sina/docs/sphinx/core_concepts.rst new file mode 100644 index 0000000000..3343bc17d1 --- /dev/null +++ b/src/axom/sina/docs/sphinx/core_concepts.rst @@ -0,0 +1,25 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +============= +Core Concepts +============= + +Sina provides four main classes: + + - ``Document`` - represents the top-level object of a JSON file conforming to the Sina schema. + - ``Record`` - represents the data to be stored. + - ``Relationship`` - represents a way to define the relationship between two ``Record`` objects. + - ``CurveSet`` - a class to store related independent and dependent ``Curve`` objects. + +More details on each class can be found in their respective pages below. + +.. toctree:: + :maxdepth: 2 + + documents + records + relationships + curve_sets \ No newline at end of file diff --git a/src/axom/sina/docs/sphinx/curve_sets.rst b/src/axom/sina/docs/sphinx/curve_sets.rst new file mode 100644 index 0000000000..f465fd49b4 --- /dev/null +++ b/src/axom/sina/docs/sphinx/curve_sets.rst @@ -0,0 +1,10 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +.. _curvesets-label: + +========== +Curve Sets +========== \ No newline at end of file diff --git a/src/axom/sina/docs/sphinx/documents.rst b/src/axom/sina/docs/sphinx/documents.rst new file mode 100644 index 0000000000..04d0edc4c5 --- /dev/null +++ b/src/axom/sina/docs/sphinx/documents.rst @@ -0,0 +1,10 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +.. _documents-label: + +========= +Documents +========= \ No newline at end of file diff --git a/src/axom/sina/docs/sphinx/index.rst b/src/axom/sina/docs/sphinx/index.rst new file mode 100644 index 0000000000..9e3ee663a7 --- /dev/null +++ b/src/axom/sina/docs/sphinx/index.rst @@ -0,0 +1,44 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +=================== +Sina C++ User Guide +=================== + +The Sina C++ library can read and write JSON files in the Sina schema. It +can be used by simulation applications to summarize run data to be ingested +into a database using the Sina tool suite. + +The top-level object in the Sina schema is the Document. It contains lists +of Record and Relationship objects. The example below shows the basics. +For more details, see the `Tutorial `_. + +.. literalinclude:: ../../examples/sina_basic.cpp + :language: cpp + +After running the above, the file "MySinaData.json" will contain the +following: + +.. code:: json + + { + "records": [ + { + "application": "My Sim Code", + "local_id": "run1", + "type": "run", + "user": "jdoe", + "version": "1.2.3" + } + ], + "relationships": [] + } + +.. toctree:: + :caption: Contents + :maxdepth: 2 + + tutorial + core_concepts \ No newline at end of file diff --git a/src/axom/sina/docs/sphinx/records.rst b/src/axom/sina/docs/sphinx/records.rst new file mode 100644 index 0000000000..27af157037 --- /dev/null +++ b/src/axom/sina/docs/sphinx/records.rst @@ -0,0 +1,10 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +.. _records-label: + +======= +Records +======= \ No newline at end of file diff --git a/src/axom/sina/docs/sphinx/relationships.rst b/src/axom/sina/docs/sphinx/relationships.rst new file mode 100644 index 0000000000..174df22387 --- /dev/null +++ b/src/axom/sina/docs/sphinx/relationships.rst @@ -0,0 +1,10 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +.. _relationships-label: + +============= +Relationships +============= \ No newline at end of file diff --git a/src/axom/sina/docs/sphinx/tutorial.rst b/src/axom/sina/docs/sphinx/tutorial.rst new file mode 100644 index 0000000000..399a0ea5bb --- /dev/null +++ b/src/axom/sina/docs/sphinx/tutorial.rst @@ -0,0 +1,174 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for 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 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 + :lines: 9-16 + +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 + :lines: 20-27 + +----------- +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 JSON file. + +.. literalinclude:: ../../examples/sina_tutorial.cpp + :language: cpp + :lines: 31-45 + +----------------- +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 + :lines: 49-66 + +------------ +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 + :lines: 72-78 + +----------------------------- +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 + :lines: 83-85 + +--------------------- +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 + :lines: 90-93 + +Library ``bar`` defines ``bar_gatherData()`` like this: + +.. literalinclude:: ../../examples/sina_tutorial.cpp + :language: cpp + :lines: 97-100 + +In your host application, you can define sections for ``foo`` and ``bar`` +to add their own data. + +.. literalinclude:: ../../examples/sina_tutorial.cpp + :language: cpp + :lines: 104-112 + +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. After executing +the below, your will output a file named "my_output.json" which you can ingest +into a Sina datastore. + +.. literalinclude:: ../../examples/sina_tutorial.cpp + :language: cpp + :lines: 116-118 + +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. + +.. literalinclude:: ../../examples/sina_tutorial.cpp + :language: cpp + :lines: 122-124 + +--------------------------------- +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 + :lines: 128-137 diff --git a/src/index.rst b/src/index.rst index 091ee817bd..fdfffd8421 100644 --- a/src/index.rst +++ b/src/index.rst @@ -55,6 +55,7 @@ are identified. * Primal: Computational geometry primitives * Quest: Querying on surface tool * Sidre: Simulation data repository + * Sina: Write data in a common file format * Slam: Set-theoretic lightweight API for meshes * Slic: Simple Logging Interface Code * Spin: Spatial index structures for managing and accelerating spatial searches @@ -98,6 +99,9 @@ User guides and source code documentation are always linked on this site. * - Sidre - :doc:`User Guide ` - `Source documentation `__ + * - Sina + - :doc:`User Guide ` + - `Source documentation `__ * - Slam - :doc:`User Guide ` - `Source documentation `__ @@ -206,6 +210,7 @@ LLNL-CODE-741217 Primal (Computational geometry primitives) Quest (Querying on surface tool) Sidre (Simulation data repository) + Sina (Write data in a common format) Slam (Set-theoretic lightweight API for meshes) Slic (Simple Logging Interface Code) Spin (Spatial indexes) From c9b9ccade7ec5f9744abd561b28c1adb679aa634 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 27 Jun 2024 14:35:00 -0700 Subject: [PATCH 11/60] finish creating sina docs --- .../sina/docs/imgs/ball-bounce-y-axis.png | Bin 0 -> 35919 bytes src/axom/sina/docs/sphinx/curve_sets.rst | 26 +- src/axom/sina/docs/sphinx/documents.rst | 184 +++++++- src/axom/sina/docs/sphinx/index.rst | 9 +- src/axom/sina/docs/sphinx/records.rst | 420 +++++++++++++++++- src/axom/sina/docs/sphinx/relationships.rst | 82 +++- src/axom/sina/examples/CMakeLists.txt | 3 +- src/axom/sina/examples/sina_curve_set.cpp | 108 +++++ 8 files changed, 823 insertions(+), 9 deletions(-) create mode 100644 src/axom/sina/docs/imgs/ball-bounce-y-axis.png create mode 100644 src/axom/sina/examples/sina_curve_set.cpp diff --git a/src/axom/sina/docs/imgs/ball-bounce-y-axis.png b/src/axom/sina/docs/imgs/ball-bounce-y-axis.png new file mode 100644 index 0000000000000000000000000000000000000000..de927a2c513e7d179ca6734c67004c56bd870ec3 GIT binary patch literal 35919 zcmeFZAmA5)#tg-67o#(%l_v0sCIR>%HE+ z-?Psja6X(5TzcVJ&w8GD&pGB8W6oumf}8|8DhVna92~mj=Z{KoaL+X0;NTmO5y5Yi z5~WPQAG}VVG@O)eO`Kf6I2gmpesQw1vURdDH>7egc5pPewc%jlW?^HZGIMgWbL3-X zwf^rHu-H17vM!<`hk%Qq*nQS?goDHU0{el_7tS+>gNtaE{3xR0mU_7C>K3nh-F|kG zCgWin82HbVRq2Ju$L}ou;Tq+?EB6~Ai@J*}W-Sh!T*Qrz=WM=v%dO}3foPGn5vYA->g&hDLLIqVnIWHLBR;3cBSNO8}l|MTLG|NkZbpN$I#DY+k=q^zv-?iuvqAuJ|l zXR1IsK0co3a!$0gtg8RYnWdq1gl2ZOp|G)4Qc@C8VoPhnpXNVDZxCyM0y}A*c0pKg4gByNtEk76uKQutk27}biYxVe)| zhJV`MEW0X~=?NlammVfu6+rFYF8c5bw11qOGdMoPlkeK4LU@e}uKH2d-3v`kQ4t%R zlv_|#@bLA$yo}u6zuS9$=xObAD+rNH#>e!&-^xHZ z*vMPeFN!85|IZCt{N2zl=X2iwm?xK3=p|!0%)R2(wL6~OuwPgNm&|UAdIPG)a;Esp z*l&^2mshdS2fXo)+PpV>b3@5o6#{1uij}C$9}(T5$~4`^^z`)9tgNHE?_T~$Nuj5C zp8C18%qS>0c>UzW)``fbt4O#;K3zc4#DtcdQ6=j25GV5Vfg$+c{eMq`KZZAIn1 z9aq@eV<6H!F?lpr@LO;_5ShP!Kg-a@*xHq`#q|eHTX6EdECa`2U#!Ncsi_$l6eJm| z+FOKMJTxQ&_v-a)#wAK(;{LtKJl1-BEiDprbMu5_Ok`v#YU++JXDP!b5Z5^N3SOFD zXH_@w3T72JxK<_$HRvcWk?rPqz5DNG)p^nxOKsKE5=Uv(Mcr`X`agdC`i7U6x4WmO zAHu4aZjhiXrCMK7)4F@jVKQWJf4P@%oWbYJFqtds_|**Vql5%5QM~x<{kQ;E{tLMI zAcAch{*h{YiQC=U(l_FW{LtpsSA2X6yI9RXIm&EJOmgMhL=a+i_)>KA``A)sl7~&! zH#UxL?#>})>TQmnK7E3(<=(r<*gQNulq%8M`6b}a{#raT-^usG2RLwO4jNu2RVll> zX6)?jZyA`nzt-S_?=3AQ z<<*EL(O8};U0Sx05Fc;1KUKi6LOCMe?Db8ZPNT+R`V|91I61$|Sia(GI3@zLCf5^_ z&Oj8xB~~`JAKl&EQa5?IxqQYSa(WCq3QNqaOYFRgX5RiTpL>PCR_539YIZbb^Pa?X zxzSnRdd>)XyAhM*9jUJGoiLdve=+L&IQi`duUwVMutuAYpuN4loZx7jypUv_%`y?E z1s-ej=`%X{^v%1SjIuL(aBE$)){FV7MFd8D(Z=iDVUc9K=l+ZO&eN*RciX?5_9nKZ zI$aw#H@{N&+DE zGdvw?Y?k>-TkkEp4DU85pZ*?{wdt+3K0t2x1skNKrH!TwRbNsHEfwsXeFb&;hMM}@ z)?gATd3fLHEkSVr5~dmv>s3Ep2HaSgzHqKgO6T7nujyG>wp(ugy=G;N$(8+ez8NQv zLuLg{&edF$SIA-~nq_$AS7PqIbTFc_0^giznBqh?mm(&;Tl2dMjnYV)pCUPlIR*%bB{ z<>+>l+%{w<%@-IPrXycLUQd;$@rk?TM73Zmh`!=9z0xHV|DnWFdymbN7{}& zmD+wTyT=v}*Jy@?{@jkRsP+xCZC9xwj_ zv6!=^qoV`$Lh3m?AVKN<@#J5N`C=_|jKDea-QArk1uSKBT$i1zEvCaOjQSi_+u>B%F`FqSF43wnyx1PTJe;rU z2T$cQOID?~2*t<5{237HX}vx87v&I&Q!7 zee}|ETYK@7)3O(2B+6Xll3cm~_+#>)55yV-Ml6DW*$rUze62MpIPW}=K+P8;{MQ@! zczCDa&?XujO&gu|I!SGt?K=+IJgzJ}OS&&M+8(YK&BwD|0En&fhL%iQY=N80^oCr+ zo(Xc?6cZU4iAK@H$7MD50TzsUO4*M4kZG~cOvZW{$`xqGJz541EqeJ z_>MUn8rHt+mMQ_+U^HW zxMVt4{)I`a`mc}YT4#`s@59wC1X5)-{z?dP$YC;?KD<5rt3rbbdein);uKkBI_dy| zda)z?lmbxao!#mOTG^i;&CTfvB7zoe+tQ-R`Ohy#g)cT@geOY0>rf$(ySp>c<|gXx zjjm5tM$-j7cV|jSrRaSg?{-itR5^3ax`vdZI_dZKJc+N+X?U2cD_Q z8{!fh8|$_nMt2y4PRt$%ilE%*!Q=e=+->hqQlWs2v-ia?Hv0a zr{{yLj=N*jY9(4d(7Ov5S*s@qLuOOFlzdH5Qri>vL>(rd50btr25! z&LyNHgx32P>y`SN^*b+%FifkMIb%E_ZADp^yc~6GieA)Q>YJWQWQv?C@v0)OdQs@fpoP0RWstw5rNn@jMZ9 zIbH&^3+2U&7tlR6ciLf9q(+uw)qxNMd_o0+}M%*$t30EouVlxQ#d+Lo(Yj*g6!-sfaJGw)#6`0SU`uTFCYT}7OVc}Z22A5S=wdwyBC zkvecOoI+^x=eJc@VkI}B;E9-1baO*!1SyBfOEdx|ID<#%T_lCzeb!eP@JsA#`8yZXmrA^0t zr7hPBXti%tC9JH9A9jBK{w-B^ad81a`ejg1(B;wMtj{tG;XMy#PJ%*0)U~4bpedVS zVPP62YT$+U7WY_dM- zyko^M?@nqOedjuBUYoOh7Sa>a&|)MY_EX|Z>8W|ordyGzrYR&(#ty{|O^yXghsa@ar76G_D84`2Y7l5%t`Q|yQtvBq+CJB%(p zzjABK3#2ao0H;}rq_p&FKq+ElaR4j^&)jE8s;S0sg8B{&d_GgG*_HJv;&wfPmF15A z_U>-HM4Q_aO;S|!8Efn9D=o`JX6;@COo4WQn8>%w4EWI&H8Ejf)NkL0Mv-!>F%p~2 zSJEJ1Qsih>nOs9g^oL(FGo$~(V`F3Ux|&wETBr^SLL*daavLKR z_>JeWjC%Pmt+#h8J6j}%nOB?HTz`##}E5m3_1TGjQdCw3sGtL!(#O-52R*IYH4 z-Prc__TK&p4Gk?9j8WAYt*_?^i97)mKa!k3(f8p%|Esa_mm014HeX=?(?t#t%hHD2 z&Nm2t;0(?|k5f`n>5gD+{kz%`!1ikxKQ4cU$p&@&;KxIG!z$ z!t0pZvSkbUZS&=XG`R8)&J8aDYpU#UiQ)iWTC zv{+xcDt1)hW4z*qQq%J2=@Ud|SX4E*5E*%%Pv_9=TP)rZ9GKc>c~`ro=cmKif$x)# zcod0h$r1Q4IS?{pVq$!gV;D31Wz!fIhD$fm?CuCdOwhU1&7bDxGyrZ2K7yt^26!1P zUm+)LG4ocvR$Lc=*;E-15&+fOJ*}><`(@Rm0f=Wdz!w1SOMzG(-NcY^hKq$@=vEeS zu(QX4G_SN?q&nbj`1hi|dGF6MzR7X^b!^|gh zvB}7WKnnnWLx=HFpfpu&_+1V)z^$l#aajU|>3-6318Y%$m~H_UQfo_!jV%JXJ|Y(M zcn?@%TNY0TmzmG)>Do(jzIgkM-g#}tr^`HH#Wo*rm}(G@ChG^hgBTz#9N>V)Kww`U z%*KFl^ry$G_5pG#`T4T}piwYZH!1{q=~t@T1d{~kpubK6!dlw)=;3+(q8#uk8^Ep^ zAHBVUCfJz^7XW0DdkFaY`U1kk>%2epf^j*0fN`xvb`P3rVJT^jh5whbv{3efXXC)F zp=n=dMY^K(>6J$p!_-{UJ9LVuk*n#yRqDI3i2q{GJE?fWtG>aq3^`Q*u}=$s{WP$;6j z_+1Gqida-tl>C_Vc9uL3*B-&Y%$%G+Orcx9vyDEWro_Ae*>l<*>nMsXTJgN!%cE&% z3=Kvn)njkK!I$*kf{$0yS$^lw9n6{K+l5?ALhzW_Ar^sI#msy%frOPbnr@I*lv~!Q zk6yz6?a%Xrp}HKd#Qaqt9YAA^&6bE^H=lR|d4MGc0KZA9}IdL5VLI4jo+r+nHKdF4MOu)rVY6ZOi?(TI=kLMr*Xiw*I8w1Lvj^G*cg z)y%F0bO-g09gEGkf8NQ~Qyx@@7>v4H_D<(hJfNd0)c;COCc>Z)(AawfM_vg^2jNvC zJ7LiW*1{ct3tW89zrdMyoPS7*>vXf$Fbb{-6^9ol8`x%*j{9Si$bpwQoQqnh5vVXN z^>NvN(XC*oIc8+g+4(DQj+THyMZqw)(%qtq>*iyJU=eg5m#Ex{yFy7Vm^ZgO)-3Iw z61+NqW>eIWO8&@q!>7`uJ9You_f2KYW-l-a$1ePOz=JkK{`DSSde6h8eHy!7%eu$W)Q)pzERETWiC7j=ENz8jT zu1@fW7behYh;+Q240=*g(RjX}+XOcjAuN$0<@(9#zXGF2%QP0_i%~3baBFZbRSY%i zVe=9EVe6!kUa-2v&YP6wf#FAR^t+zuVCUqsHPe@R0NB9*P`3PIxGiTQfv3>K@_X=d zzo_gy#uwVPw*%b7t;U1WDs`7KnMxV;1sPzY_KR7RT0_7+7BB4C_Uqu3y%|E%S>#ep19_``rc$h;F_)igLw z+n@*!3$ItJ!GQ+Irsr0ak!1iGg8FVwR^)YbQsIB-g4P9-iS|Jdm~ruVH|opxumoPj z=elH%lp9g425Ib%qq_iVnut8O@4I?z5-R&Iw?bdt#y3GqI)&~Y)AqGFDrIVuj0eiZ zycgV%S!>4|wxbZEH>S69$)%n9bq4VH-HrLwM)2i6k1Txpv0BsuA|fL2Kqni5mLwjs zJ_7{DCm$an;30AG;l)s)Bod;%|K$eV=hIQ;j>IY2o~MkddNh-(xjsrI8jvvhc|M+; z3w1Q`hT=gjr|sz^4)vVy2RC)XN%#mXXhdc-f!D!H&%QSDKWv8cEc|iM9*2R8IrKV4 zKoqzD4Qs)q3@QQobvER)x~}9fZ{TpQf{e!w4z6f=0LVdCApE7ljS>mCy#w!kv07ME zy1tX)3)(m@%pD@sbNdF`%Y0HZZyOpD4NVXVF6~&OGdTNg63`4xgl|B2-GPcDV*iQ^ zn80cW5@k+(y+cb2=;ISb>Sd@mf=|$k>x-8A{UWFRsW8Ch9fvt*%WYR$rTXf(61Cql zFjTVA#HFX}We!?jAI>j13^GHvSbZ~rpo9sj>gxCDZtEyowO0M*U%Eca3v(knJltQw z^z`*XVrr_+*3}A-Jpk#c+1V>QAHc(80p$-ou^@Tjhbf<@O5;Hy9y==FdBDiKdb7Ab za7CjsJoA^LctJ}eU@Dib{*Q$Pj0hs&0m00X8Esn{4vxvzBjAD@j+z5M0;V?MK;M>0 zVGjabda}w?9xx$g>Y1X_#&FO%b;nN*7(oR(Jo?Z+F4SRolLi`9bo$QZl;7K_ab7Eb zMEVtm!8OT48_H&F$eU(MT{IRlfTdC)GxF!{6Pyh0G17;!dqnTKi$rmIQ!PtWW;Llp=35D-NyK=SK0;ffIn6NJVbm+WHMwvy2s~U)3S5qfkqLTCJ>CNO0_*$0JHYk4KKud%Ttf7Ci>E8F zKIU4zJ%b*^BZ)e`At2p?My=84MDKZXytVAQLc_pNIKd|L1a$-sBaHC@izLtGXhBsk z4;}&W6j+C`$;p576|(^&Kx%S1$^zXLCffa&%~OHl`vlz)o(GQ!D9zS4B#QiV_oX^p z$|w@fLZ_z~;m5zQpAbr)zu@M((gka^T)%wz0>T!s;sc3-F-@3YZY)02+If=W^gb>! z0{R;YsMS}{wt>zPf}V=OMU(M%3f?SrWjtQext#nP2EHK7Z8bQEm6ei$*=b&&w+h@W zIYNO8a&5$AKGE$%-rdu$6)4nhr6jPxh~nG!-iIfCSo&b%ZG}atHI8b@1DaHEu~4e4 zVSSrziK~VJ7eW;x3W1-+GPbj-Flcug!k!3|xU9dSUb~IFii!m$(8B>{%F=h9UXK1l z`>~7DMd~pG%vykL2*3OK7Yj)Ic0h(vP#f(EV$L6WV<<7(#V`fCfoU)d5KaS>_|Za* z(dlZ3pEcQ#=SfT7NE$zkI&W^;0Sl4O zx*mz59Dkw(#2t7L7;p$k7{!$lpXqx*)oE0lVS}NC!ECAS=FZL}IPuuHIC=&K!?_2*WQPF6D3Sq~+Q5i9 z8NW;9{nddwkBN&5S7v5{cHz@$h%goji4=@Ta#<096J!RMRXC=^ZO{$}g8)!^#P7|< zj34Aw1#kLdC`|zr5PBUC_RZ=JovwA61L4(rq33SS_iTF1MEvb=7yn>k=Ejs$=T0ni7}@f^c4F){$34djkQ~nPTZO3 z_r9oGNsS%6o%bXb_GINAo$t$y*Lj@C!=QEE!z3zG3=lbP%j=c(|E|E}f&9>Ah=fGD zw4BUlbg0XDu`_CVxjUY;L`P5G|L6!J4$OX#)v>sAa&y9P@G`WxK(MFs*!#D7LmVJS zbr--TN&t_^;eGEUC1p`dYwqE}2i}9P)w&OAKC|cu+Hv|Hsje#6Y6mPnCOs?9mZTVS zC8=>Rs>qc~8vr(;JcuC7)RU2uy9#E6{=2Jq{^A8TDXI3Fi%)Bbr7%EpU?Yqbsxm^< zL1O~OMZVKeXehFSBV&xB;1jQ;lvL$O6Nnff>XN^>tj(qi@d2bzz24|N$xBk2cJu{q zbcE2&^ciHQ1iFy(=g;_WkpQX1yquix85#0088UlVH7c|LnEbW1gQH_uXlVY~$! zi@}iO`1MB{lv@Zr!9!w4p9UA>C$(t zFOQ`d%S1s*v;HrBzsgeN{ulOmiGJyyvdhw`$ER;$Q7G5}s1gku8+N&K zw&3~S*FYf?0Q~|+7CxW;XtLZiU}9k8{$AB7>DqTurlp}#CE8If1@QUt<42WU9B2!u zRB(_*7HNI6k|2E*rR-gIv?8sIAY~^3C3GoOMGnlVR8?W!r*Jr32xb(7LzJabIe!g} z=fBAiN?84dqOo4;kQa_pnJu7(qgMDlmLecr=t|N157COXTAcqsOa&gF>D^aC9+z(& zw})U_1dLeHz!NEf!7b=Z?ZA%FsI?*no;8qmSU5PMAQR?Wz4_aq=V;)U<>l{&OdJz- zfWfo@qN|!-2kbJK)&R4aAM5LeFr)2!Yw&2}UUleDp6QB+g}h8ApW3~;jS?aZM2F+A+h!HDh?aNQkuMz+9AYMO5a z^d(p?1B2>ziyB}ti#j;4gOY^_QF_mv&y;7vk3TyY^>Kd(H4J|gRz(W9inBobLS(se|{F@D6nfF<7 z#igbFrXpCeaYNojD>XH>!0l?lOERAC zFb`}ux3~bn_yuTbf zn5gkC4&j2CNDyEr$<7BXx=t>D;KJq{!0S}2U-`;D&lrK$$NzMno15#MV`DBJp#rO*NRgo|GX~h>`HPpYbJ8WsolyEoCZD z`SvZDR>1FSdT$p$Vob19WZKqik32|%&-0oIq)Ij*!~!td52>@7?_{o9QZk1CF|XD{ zs_2#*A$XJF``7@FjiUbjxinnun*#-L>OoACv$mRo=Jz?&XPH9m+NYux<#QK*@?CTl zbUm_t^&-IBjZN@cVBk$nMYE!ej7$PYHSoIz9v=u<1Q<(n{~aieww%gMj58e`AN>5V zvHQUxT+0114^PU=>;vS)+>#zILDsi5l21cbk$c5RdIn5v%St`UOMu)y0eY+)rpFHV z`2t@g&-vzlzwv2>{-G4mTWeTgPtwj^yHQbLvY4`l+r7*9%`wWiSq~Gf@o`~%D3 zD2@^WpTW`uGlZqZCR3f^gyQ`##mDFa2Gxg<4*|If-(%7r(z~J=!NOF_XMY-oJuSLO z`y;EM&npY@BWsbO0k7HpyrBt3rm_ril(~y}e(afb#Udy7^O=1&;!3$6kVmO}i-4U& zQ~18snk)@RjGkp$;Km5Gp=VmVW!aL)&JS`@a4)NX5Q1QkcE{D2|95TYlmJX@fD_tJ zT8Cvb0)Q(aL3Jx@zE#QuJDlMz1J#OB$7rrR`d5zAV%ju0N)OVZ0fdcnqDGErV__lu zdus!I+E-R)(ZWIP^3?Ycr#C9u+xd@TaOFPN1dTa!G&}gf<{^tU2qS4~&?O@u4F4{4 zPy#-Hya_yHz9KF)JEB}}R$JOmf^MJUZE+MOE`L(8NsTS*|&%n899KG`f%h?icp8TxUvAi{&vsZTS@ES0s<)+c^1bP+ z9DU_vlM=k5tm`SZIATyS*NED}Y!B{=5`?MVYKM?ItziRiak3>JxgcWTJv3bjb+Y6} z>;v54a+qG*dDh|qg6G>7Tf9H4^3IBZpoR*?aG2~rjj)Q|w{iYc3{cnJBu;3`(8y*5Dv%sd_70`j6pFk=})1p@i>ZRBc*jK32}>0`Sfk z;GJDgCAhucO%&mpYxV+nT<{4~%0#Lj(GZdz-PpiqV7 z`@jC<;LvG@z@BCEV0ZMH6;Gm=g&dpyqetSe#Xe$krD0;8_J~ zA9b>1z;GQl(?qba5 ziSPhGs?gx%;7U8K|2Es`m1*iKSw3KClrnsJz;y|grxGTdHpMEG3kcWO^_bVH1Dk?G zQaDU&c2e}mCy5{YG7S5|ywu2B?a1lsR9a=CMQBJ0vn*ys&>Q+)HbQ#Fnwc4L&WYHj z8wknxBZVBsdazL?Ce;}Kr0CU(`eO-w1ee0A=|Uh{nc;fe?<%3|r$7JwA`@-d^ySRm z)b|R7CJYUxe6285+sWT3J|`?6InCDBcLTFEx0~hnyASA>FXP5Dk*Qe%-e`21i`Rx{ z>V1XN{Pf4Gc{sqrh`1W&&wx!>Tne}kWsex%W@R1ND5jOiE9h#BG#M>nBZHiLWYizg z0XV1juE?Jm+$_L(`M!(Lm)|BOQ{*JBhE-t6)?SAMerb=gMu}!H{S3^Lgf)9rT??k7#xmw%qR$ zwZDAak5W(dc-{sv->^}rEnvZPa&;;E^z}DoUuC#vdEW!l(f&C*y3PB)4_*ucIGopV zaF_m?yBri#4-AEL+-OqLBJ?sXh|jjBq0ER#w1N378K296oz(@Fgc!V*fo2g8+2ZI# zc&IOOWQH?2;gj`{tLVVi70oP>32>-_;YLeYkV#2qJu+~E-E`<*+M^KH`hJvAWV>B) zU=0is8^FFifAQa!hTESm>VwVWo;=oq2qQ82^Db@iL?D4!;nJM8wk{V78h6L4X4MQ* z^fGmV$UY`>tthqD@A%%VeKy`Ah8nI9YBABl|0eW~7D}-B`c*X4;ymT~;8%8Re0<}l z@X%23%8aadLx-E^L$p1$)dDkQ{cL zYaixvtS*^V>_{teM+V$3e0N7|F-RO=LzbYIFJHIPJ;2vua<%zZau69QhpdP$R59lW zHvC|Vq+sm+)x_i+Q1UY{9y$ZFX&ed)J#z?fHzy`07?vr)bW>E+ziXZS*D!u7#Qzun z)OOx$-kh(59F{x8xtIAH)sDI&SJN{}QiFg4hDiK|KBVFN>biv}dq61Py06h+hWFfF z5eF80QBF!-Ak=RwRfA94%)MP0{#Z({4YtuM zfl~}b*t~Bkpw}?(6U?g&!OFr*Qm*2O|9i>P&1-yo978yl-_znV9Olr!UhcNEt=!Tm z^SIrmY@|*6n>MK`B6LrNMu^um(NFv*KhsCSIgYqIQr$?+*12m>sXmO6sw+dsi;~mb zI{q*x{u2i$xHULCONZFtd)`k6#@D}KMi=miVS^S_R8+7;lQEuoSDa79;p$R}zM59^ z@ILQ+X%llyme8XV!};oJET0#Tz##3RbbKvzFh@F^dX{Uj){9{6q`*Fo>J45rg=m@1 z;lODi*7PPW$_!9d?pq1!Zksku`B{ zykqAj%$A{*!OipQe=b}mb~0z>^m*MOe!9I=7*1JZdIMMW<9pEi;KuYvMKc7C3U~kW zA-aJu^%I35qSBFrhv`%{Oxeael)KRYYkV;t38v1CvDe>8GzhcyIYo8(aayc|W;8k8 z)tH5#zGUZPQp7bzNemAcDL{*nRq-wrD}%seJO*DyxSo2RqNsm*8<>xnVb+0jT;!ud zm94$li`r7P_56+oaklsT^f}sxsQMR%>uW=jmszZl7!p$ocejjL8!U}!>?s2lMt2>x z=)3w{jZ}QpADsc&4;SCHwj~bu(}+O-X!N zpCfj4tc+PE@;-Q_;fmdv$tc72<-sU0Cd1#igzxzB!Xu5O8u0h_5C54{uCkTs>1|`vs)YVmzs(YpTTUC%(o$zEb)uAzoe-v*ULP0XmR6YvaE~~J(sVUEu z*P~DPt^vn*hY3mFbGBSrro0R0ulQ2feLvq)2B^mInH1`(?@pi<&r}i&qQw|bStey8 zF2NyP{~qNjwX4L+dIi|jVkin1M@4ptQ*T~d>bgwpfu)f-Cd;G(KIbZ8{>t<7i?J$U zyLtEjw&8`>z9~Jg5@)xZ`J^%8bkTx^_cG)rQ=PTMfmBx|^j{_7v@dd@H}_>DzB)+> z*7`Hhlx>XdCg1+-K~yxTIPd2|cx+;kFZUN{dx7>L1^Q0b3o9O_#hlaQhAS`4(ou?K z!}P=$df2igU`qK*$>B~qgD;sy$>M`bPK}T{Qc-ZittwEFmme4Hw|)Itgh%fBR-m|V zrbMPoBN_{ZJ&qg}d4sSp=s*-1eQ0|&E8$Oy;;MLmYHlvT4d5{0uah%tz!iSs(3S@E zdRJSbRF4ampVziBqukuhUO)2J!vi7)vrOq6mvf}r=>CodRqo{F!rt_RWj2O~dOq&> zuk5n^e{j=lt2Tc>aCWc2r}6{MwWrDY5`DDJR?^Lc3aw>a}%+ikur zG7%Hpur>HM!AN5QEXl$;37>!T3{)bd$nj>VGuS$#qD#DZKIP`B9kGQ6ezr5lntW=7 zbN`#tT0g)j?eAF@*U!76q2aIq@6BAFy)}5KAe&9Jep~i>v%|T(Fw0-;-Le-Y-2Ec! zxOrcyuiOjNPxb|l(jVOtDv1L>_UK$ws|3*If9vuC+3`x7v0U$>rNpXF|2WI({H+vS zjA+?CMvJo`AL~uH7@?@j8}Mw>SJ}@R8Z)iQYf~0=c?br9I#HQupsPMW{XsP`?Mip2S&yVPcPe&xqLP$e*|so&J+l;%wPo&d8VdU)3b_5_5Hd_6HOH z_a0Mzxr@tokCBH{<_r;JJr0-@)p-WAF-#ioc>CF`pyN~6cN}q`GFizEZ|zJ8_(%@E zF5A>`&5#u7no|H@m*^p2?D_L^U1{+qR%!U|$f&mOy@IprS~Tt*)4yLLNAG+~ZHPqL zg0ZKfUOJ809*K}m0dF5@dyM5T5L)e^n?&-$)aFdS8&O{lLhntgLXkK|p)#yaalmzF zdTXLKLd50|`F@OEUwZM;@>LuhH{+lWY_`<=nrg@1y<()^LcSlwRCEGaAJTIu?I&m>L*rwdiZj zq>7Ehl0PY1P2^I+m6a>^qh}1x3~&LaH0QncfVhtP>%rDp}yu;!MD_wpl7{>cxcHd9HT}o zbv9g8=y6~WvHmHY{TG{Ts{hqpTjp#jRng)HAC!W=(E@iX*}>O&`G)wXxojd%1-Md4 zIq!b(- zke@;}oPg}0kOOhQwz0q+yg_20cg_7iOyA&CMtYozJ6}}q>FWQ&?oBQ=Ddb<@D6h}q zib-eT#9>U({jc3*BB@yrX}Om)40D{exBxxWxg%HigR#F7YH4`B51H$5R;P)}xA8pu#X?Du_cq6hkdjRbi3^r;A zy#2MS$eIemXO@IzXsdalvS9e_P-Zd842f35$0yk)!~bf&?KgMv#~Ttqi|LLpeK!PK zgUBiAUhqKX9jN^dUa@l0=nMd~L7;PYroyX9IBs+3)eQM?rhEVz=1{9nw&f#Uq?1$X z3XhzuU-ySc^uBYTgw4I#^1(mto)qb4Z|Wvr&AhR z8F9Ql6HH@z{f0~y94(u8btJ98SWNt(A?!_1lxC}x)I(9UGV9M96(=#VshS_%{KABF zRDbQ17J;oPs^7(2Gl7tfL(C>$5!?Im)%>Eq96W1_pwm^=nmaogj`TzI5Kv5;1lsQA zx+J3y@uDW*dUoGg_JT@eLs;^a4+_lcT|x7EbV;|~BZL3TF51Zp`W40N?v)s}66+ET z{X+pU$$|iFdeni${yEo>LTUHH&^*{W?86)vma~^ zw>y90zEnvMxX-b3*WKtUqKp}3$ozBC=&kdy*sfy1SZgP@>Fr${bX7%N&qXa$CW08V z3MYI`@y|Rkdm?*jOyK7(l`trZa;QPB5$5p=V-gHgyz{xr?B)u#v{XYz^sHy@HVWQg ze*O95*rx%8mlOy!2cmKW_eJtLL=_viDR>(TJ!fqPq!VxXz8_{OJyy{sJe5jPLtOvR zEh%ZDB|5CW>|Ci*y=bF^BCOVt@1hr_y0FMo4g&G0m%g?2y+@E%Ep?iy8uhUtm{#qF-fmzD?oG&$rL`sj3$+SYqevfDTmmMpqLL}=5b@gvlCW{s*?|WrFybuxIt-J|v9`U_6~aO8qI2{OP0+GL)A7wO`Us4w z)4G?%Bp$PvJ5g&tDE~lQ#8B|>u(TnlW+vJ=^ z@00M0VlG?t(|qJyl^g9jHA;3l=-8I8J|`^3v%Y>~`96FQ zc(bRUXWxgvp%?H2M^5mRxRxBSfELy0il$~?kTB|+y1qse0y-R~Z`^vKi+wX!F*ew) zEZv=B5O~Jvf&=FupHsj_kav#e%a(xtBv z**rhc+#Kz>3sm2hTNC_Iodly@bz8IU;iC<*TC2!?PPm_SLFyKx{w8wj~?Hsc+bksu`%69~ht?NV_TzSg7 ze^GEbRrRu{-x6(g&Sn61ysR=~*x<%>uhKX;V*RGYmm97_@P5s8SOE@@&>)txG&O-W zFCYnHjFrMsKzJf<(rKzr&ykERi>`FgL_;Ao8fe$4TQ%r=8k{f9$T~>7NXMH*0w)vC z6vCVp#cZCp-}mtEp3exfj?+>Hst0o!ey(~8*E*ln|5_TO3X&tzW*Y1m5vdosKTfGt z8xm_+%&~I*6VTyth_%tkvqh(FuZ=XNH9ejn7~D9Qk&0K%QK2&bWc3wvyhCDODlb7m zvYGb(Q)9Ayt?X5P^#(3mtLM}%=A$U|A=Sr*^O80{0XfBVT;F&5@}fbjye$mUG;4D2 z=g}RKEIG=&7IqSIzBUs#q8YyTYGjz|Jyi#o5CkH%TLtUafti=R`^(ZCX{psyAGl|W zE}}ufnY}AUo`;JKyO(&cyKwbjUhjB(rEDM4t&2Ww-UTa|%QSnAjao5w?jjazT^uLk z4$T`&li40%^`)h)>(<^DpJSGLpFZ>0!f`tHNlKla=A#Wh-!B)}S$u;NWi7K(L^`4K z>ubVvyF<^*&cgf!X9Y2vX9Isb-3t2H~H(lDHcf_(Av~S)R`DxjeqZW z>PHwioS&)ZnphwD%3rI2Z@k?owdnG|l>J89N_Y*_fOe0!hf_F|beM{XrRIDohQ3&8 zq*uNpy9I}MLSUiXZR%DM3kOvKGfN%(ml7E2yOUJ@ZI=Z`(v`8gNnaZb$vdSL0Z7|0 zT2|;hFyD{qbs5^zdiqZ{%LYLh)#{(%(ss5Jo`M!(&m6>6Bf1+;Bvla1>XKf12fy^D zV}O<3_0F{35&;6TJ<26Knidz41Ato~KM zLkC~rbf#9V#8q>zr8ZMd4IaiJeEU?E^`E{Y{A0&TuiDNQJ2Ig0XOqV|xU%h>9k_tA zZBbq35)hPLg~kl^zGHJq`%5=xWw_6IwX3x=fEvOVzVAr>-W=uA3yn6nzFoW2QqvYk z$(j94D?JyN*aY(>rp^^*Zdv~^JHRAc3^T{qc+~b47qgDPb>w?QF$(Hrc0R}t9+v2e z>R`Y=*^GcxCdT872B%kRc5n$(P;ae4+6e2~*-4DUsueZ%Hy;6SYwrbGq7_|DxfB`W znhL1FBI4z>ta!+#(+-`cFs|2~;>u~{CF%~;7=c;C1_OfoA^L#V4rHF4 zQ#mgN%Jx(0VW62I;&-B2PZ$kclv9Sf+87CV%b0DuQyGyf*XlB{-Fst)35RfZuX$`R ztidHU+@2S?GAAM3Jm+EgG;JN)=<>#|q|@^YdTCk6z9wsGTOysNL?xg$g&T3jOH6N> zU;LqZHBiylDpPn}*oqW7Vw zIksKMa6=2L=Re%%IQJt~wI%JP#;*HlfmgbYPyA4)63Hi| z^FwF!eEjW`$_srAe4;?}&Ih=77Rb+aG04m4znrCogd_TpROw2+Vw2db|JybT+Tn5t zAP!Vjbl%TKjDc^zzvHjH!V#R`!>zSEG7SzMW^Hf}LcOW_cbyJEnH_*~SsW2XdwXwf zimPo2uS!drj=*1UOA8idb+XrGb5dY!KE1V8NKxc?+{2#!zw9eDcD<)MYm-FG`oiL- zF)M#g&d-&6biuIoZm;*b^iO0Ol)^8bw49e7>uWLXxLpvxs7|48P_=S>O&4KJ247@X z?-qIl3Q}eDk9L@KO~rAYYW+$Lev5HQ+-tmlFfp9aTNlvpawNf!JE7`f>A5*mINNv-y8CWmiC72frDx+*sAYiD=arxfj{AF${+ zz6l|m(c|kszxjVzRrWfP;iex~z{kf5-BZ(!_@h@rACJd|EA% zubA={T*RFZ&=$h4Cb`LG3^hxRm60Z{;g1O^eXl$%{%YhWY+ivo*>L~I--I>oq}`|W zJ_7S~Q-1s`cdoA2%OG!p%nO|2yQb08`(CGF5=8E(b@`Uq(;E&S&Gf-7ZabpKVJ$Hc z3cx_Y{}-XeDOt&tx5@5vt=BvHZ*bW0x`Li22_R@dfmI5D>oK^U-eqgZ&c_+A|l3oV!w(Y?=geu?QM-#)t8(2wuOeusS#LiZtM?3b_T$%2K`XO`&}8=jb} zl8E^n4cGU1#m1EmvC}Nnfk`1;>%b1YJy}I`l6v{&e!wVoh`FdNUh&_j-E?LRgM(HT zBO9a-z7O<#8|<@CXqvQ32A|a3ZBfpBet9U#NvW3ehpV?mc2E7Jdg`DGLtrq#8HKqK zJ%R$){B(gb!BQ=ki97fncu|9NZF8|jVdU!#*~-jy8VU>8&wkc++m0c_Ey@(F|5P2j zaa!IFKD(WEe)kLSAVxLq>x-2C)!JExRn>O;ej(B=-JwW>poB=53P^X00!lYhf^0pddOdyWiQh{8o~bapmSA2~!>_ICB90KvoS-OUxeXI3c{+LKiOU`~ zH*EU=$QhR8V$OH3t-m}^>d7#rpB*SC+(=%Jc7$WlZ#7M>Dd^*tCGp!w%5{}AkMZ8T z=}L`Xk~cLPs^oR^$bCa zeee*oCqCcf9?F~?8yfkKxh7N;T+Q{oiQhiGzLe$T?`?La?|Vum@x*=RBm8?qzAc#GjmpqkAcX9eb&)j_kL zC(7XDFqc*4di+x;Ks?*+`jtbxXe$cm%WXlEIU0(J#iw)LLu;BhyVw1H>$c_FQxQ4g zn5fU4Xly@Ux9i75t6s&I53aCk%I5dP9n?#c#l{N97uZsZkvpkb7QuVmpJkJAk7AU=!LZJc}>&9Q3&wLao+V7R@!M zFTbaPRU`@D9jX&a5c+R!$-1rFqm3VF%&qM3M1heuh@{eiTS?E0DPDIp|F>d`PrzIR zRz0ObiP#~MAg2St8~z5vfS#wtPGk#SZ2sc^xad4x$z4&_5;YCl+4`-Fi@d z$;2SJZx^d-V^?!>Y{#QKeA7$g*;{v#Bm+UL0gBgxg2^zjza%w#_eq)2kH{d@iE|~5 z{k0xP^@9j*od{=V_V;`*d-?ojPMHoR@@Sxh?t7xv)W|M4<#Ga!sHO#;!?gY+BKq;V zAJOIm(={&A$WMnih_jtd(S=H zG3NF(tQylGc;tu!w%3TTo>gnq!{uUjzjcN{cV=+3wwsSMH%de~{IU4vpfNeJZ$y9X zkcu1K0CAaCfcLl*e9x+b7whWH1+*ju{0DB|g_9-Et#p>tbPz%wD{ZGl(F}5K>oK{sUNq&@&6`NbgjnR zbhhCkSHipBelgZZ;!88`?T~ip(Dvp<{M%6(L2JS&!!NDE4+1uc9QXEXMsTU~nZ+?O zUYui#mNns(Q*TIzl?N*w4=GAqR}?@SS)yJ5lXN?+->@mZFN)&_k{t z=x%Z%3zqDc!?&203!dK&b4&UNvTb~Bc$rG$jRwpNY{FuMYP(WZ`ftrXYKM6RL!?6! zqg~--+jU#N?p65=`9pGH$#Y?$S5>oh_(#XelcUT7N%s>!u()Jna%?5E~Ab(xm#JbI9c7y)8JP*GA}WKVTWD-Zo&{kHO9F?kb+yG;7sxk)K z8xGPEm)37Gezj4mCtM9q=bjLazDd~L=U{tVRZrxat-Z}}yJ0$-Yr)OwF5$!BWD`^c z+hh|bl;5mE48mh$gA@zyDdoJr$~6Df?so3`*L;!P?&;~=BJTAv-?W)=@UW|E9xW`& z^b;n=aiJi0?I5jT=^|E(^RLug@LT$P1%HV-L7{!zay8>V(LP zYiqRd?HCrC&BUKBThrqAmrsDh9`AGJ$%4zhPXivZkq@|aK42NDZtf3!PEm@aHl9A1 z4LWbL{_~urt;*u;xC@W=ko03$Mch11b1ImI#3bKSVo-w1LffNmjsgV7AB={3aYm+v zueHZW-^ryKz7`SRzzU;aJ~C$ux63V93XetOWbel^>I#Qt`Q_ev{zl8CS?9pXF!9JA z<#_i(R)B1lHrC-6+0vR)`C{(zPocXV&jM?|xF0{A-nlyR+=H4L+|FO(5Pu5JZOFiT zb2;mXcluL)O>IUwd>z*De97wK`>VB;{;5`WI3`Xc>B_+=B1a5`jm}NhAJ4aWSTtlQ zfx~(48W1rUl{|i1HCBv}b%BqU`cv~VT2Xn4>H>rS|4iIPyR2CH{9NR#hswl6uDdPL ztCYD3o!92jN;FSVZL3ladcn({QA2mBt?w68DJlwP+DmrlH&yYEt@dxcH>DGmU~Br= z$<~(sn$MnL*})vX)Hp_vdpE%aGd@{&tFHC9*^XOd_i;f#m6i$D`%LhC^Vi>?_ePE;SH-kh59cV{i=n&2f#HNxP=9`Dbc`Y` z?|d|CF44MpvTKHNpud!f&5b9CBs{B!p=sIB01x-lJ6%gz{V~j9oqx2;NhrNAmU38n zMeixKDn@X%ECyqxz~Xoht}VVB6s8cf-|*bQRdF)>TIch{^*p1!`0dcI*_Y(Cj-QNO z=T8XrzMMp0t-82aEk?Gto%JVD{dbLe*W27hzlaxyXpO$QS`X@xj)6FmVu?=c7tau) zG{PPqfpa&X-sh;XZk0@VXVsR@l5g970Gr?B5dVt;Gs@->k2Q?d#?ApAs9z-gd@AlT z{w`f3!m_gCS|q`bvVodC*44>JrSs$6KDEc!AV|^$S=mqR>1E9yk^7aaF!;H#CX6%E z2+9gtTQc^%jKpPTBCcVM8opgrXo{sZyzz^aEQS6UZhRrTb9Hrb{z%&0Ss4ZHoO? z3D$Le$T<-brK@vvX%cxW_r(Bnx^!ecA~|H-@0EWWuXv|nno6vuZJxR-(;z|7;6-s# z<9H+WQFG6g`-aJrt;S1kk|N*hE#q@+=nai45sG9sL(M~K%BS5$`e1cnQPO`4t|`1^ zrDa6<+X%yBF{;h^5;@g<)m!~DRylzmu0`XRE2rtHEE@K5mgF#`#y4nkzkrK7Gw$Y< zY|nao=IoQpe=<1kPnTQq*6&Aei%Cp+UGS%HlD-sjNXoF3!s_1qOyKYyTu@NQpI^He z3ri?-blcHceyPkvA$$ccqLJoJUw3s52YcnZawn>94&SEBY97rUgoKnZWqrF~YeRm{ zYZ_}Q{g=`&^{d%vkHPa9v4|x+3;v^@abywNT@XAJ+q@&7S}r;;{TXAaDu4FFJ|_bkHd<@!J$$}bfgm&=L)_D{Sl$kJcW<0Mx?Ir z%ez7x2SQKlzqjg??w?3eWS%&Gnx=mQ(ZaPQt;h$IHG0N~g9{iP2=8E1b~qzOWI?Rd zCU_&pfSFFz<}mcZ)DRt9(O<(G)OK`FQcWfNA*rn)760%f!RYN^t#%e)^#d4;qZU77 z*}qM>$fGAi_Iy##VpYGKBj}#w#?@XtBJ+UZNRy`;`w19L~<&-N=+KyKpfZNft6dQCy z#G%qEk8IbY{EllyDzXwaYMH7>pSR@Wu~+Ef5N*$|j}7PDkzx0%=X}Nzxf9Xzaqk>C zO?2Z!2=)B7-7a*6f)qV{0`9gYJ%%NdG%!;ttoLS~lL=*xDH^ODt+gLp;o=CT)#oem zn9zKR6|%3*s<-}h0kR2EdXm1R#@~*J<{L?#hz(zSMtw2OQTDC-ImO89QX+%LDV<#q zhuBo3NWyzsT7na)?IKnjM$qG&Qn!K+Whe`IZiVls+@9~;bP;*3t(A*WUVP!c&@*ru zm_I^&m=7TE{oC~4r)%}SjczM@OSv)ii!Zz%09UX-nQswN5{3C%wl6q2C2N{!JVLjt z`PzitPw5>E#SC){HL0zeB9Xh6A4^rJIL5nQCLj0wNtU>MDQMi1=gcecaKxr8JVGnx zWXX*SVxM|eEm1$`3R?}o=J=$h7f>B3M}&G`($FZ%VpOdcJ|oVSzq#XmA1)3t1K4Ojf;z3xC4JlW!V`QEYY{*Qsv zjXTb0;MVr^^rfkCPq$QFp~4gj|E^IiypWs_7iawbcZoS1$1Y+`Ve|UkFBiyM3Xk5s z%yQR5?XaL1kdjEX^hzzuomUfP@&D-mJjU2Lf0x=LqI;@g-84Ut$D}z0it7=MAZo37 zOQqIfW&lVC^-$hT5UoP>s(`q!iD=m(wMtyND)MuUuuE-ZUp`}8j$9nma_)WENb(e?Bn5!OdJ`Y9jK=o8uEjjhhRgRP4 z8{rxmoscqdv=w^sJ4vtZMD*+3>eWnt%x6!O*M3kJV-$S6VkmgJYa+#$8n`x1sV#9l zlS=r->oUu|-1<^)aRm?Vg^A--?n#%Vcu&^mmi_av$ymBhJi7cR;s@Y3-&t*nBxx%v zx?6L>54_ibQj4q&(AJ~`rUdEJeuI$G!d&Unf6C6g{+lyU1KPgLd80c+ZncQcB zcPljf(_*58Ii2&Nyc>$P_0&o}Ec2PCeulbqR>~H&v#qDcZ|l%}e{X*FSbc z-*1mQ(2#Yb=j2W}9-gaj7NOc^k6-M`?4y!4`-`r-Ix>f*>%&_aBGoY(Os|!QWaj6& z#DqBbpMNw8TJ4?nh4o3fcU#0tq&h*4*EBt)1 zk@o3zZMPYKl2P-U%jF%SpF1bwww%!Q4S)^6P>z50>`6C^`~+R8=-AkEXF3!;mC=HW zVLv`&jGc6BTX_2WDkGoA$qo&l#>yL4r5~N}JGZ6Ygi5;kzTpndR zJvUZ+Ga&Uv?sH9Z!nay+F}F`2i>dR=U++$L?qu}gxjL+P#YJ1@ycyR!bq(tUIs0%~ z%()v8ncpHL){DIS!cL7lS5|#kI-oG4{l0*k7^6hh)mSTmtPu(wK7pHSkxo9M8L&yGE1(k+Vbd~ zyiH5+&eKx0(&(oROEr;11hZO?lX}`Dmuzl!HZn3Di*#2>wqN@#y_jw_W6xeArG=kV zpf@aEmewYDNdePkS*y&@XeNkwH*I)5Y~@>jro-CqQ_&~mwy%It24I}r&SQWaWXa>r z?3e3ny;^0Zk%=OBbMq_}9zU8Dwg~|Ec3w{}n-- z)s1_2>Y5Tm^4K4tG#v1Jdw@56*V~(A(&8m{vNta=#>V|PpSwdN&Wu`Nr**XU%Epr^ zJ;}@`GmI|;L@(uk*0;dXsdlIan)khRne{LC4Hc}qWrk<@32)T$@&vz5;vXrAf`ZjI z3s7i-X9i3Wdb)MUN8=7 z{S29$ofp;av1LnoVL;JwqmqMGdZ6MAooYzVIYSS>jDzUwuZVTqts1d|3*uTLnl9-n zXh0OMjF!NEXUpSb@P6`_uC5H-?y0c?6Z9-kLw4S`qBfXcrB}`;#kA$zR!Vp$d|3&5 z7bx=^cKeGIUKDRNgr9~&IgS%e%d88;v}lY|{CF#OD=E0yi+}h{1pWm@t$2mf)%hGH z#{Jgqyn|fApG!DoTixX*rwaQydWiOsiy}LQ?wSY-8;9Zd)LCM!Scld%Epy}HSXux( z{T^s&>*{}56S7GiBygg5RtV_8l^{T{CReliba|ArdPL~+`{m$~p1!xHBtN~8^k7+s z0B{v}QnZ-;NTmODpXU4hCvf!G8*E+T$V}_Y4d#ki1vDf*L>>IAjXnA>X3;vw+@=*~IW3P## zjCd*}N>i5`GJ{`PGy98UyHiPbYi!@s@hHr0#{splh^gQiQKc!TQLTp-Kjji^h zIl0##t9p=(_kUGlP}2`AaB&Su$t6@j*OA7+y!%{D=Y!w=x$Vj>%9%MxWhx~$HGEr* zQwJ$bX%&`Mo#9%;OB2kO3B^w}bD?}R_*1|Fhn6y(bwa6yuHQbt=#8$~Fy8q0OwE@@ z%D7yAVvNFT)q#hCbG|(2@Y(tPJ8(x1@t0CwZOrl_3_5Nz-}zofOq8|7+Oc^FB1vj( zINuku7kwPxt?oP#PnZ^t_>`RA$kezzB+%Yspe#INllI8@5#vEm9_8fPbaNCWwjlUWoS2aYa}PM-<{YL)iBelzj(W1QtD?kkD8u8jVBlC zl|QOLld)x)Ht^z>^TG{px|YVuacpFY50+~TiZTA5_iaOH0yv!x@JAZq^>hq z-6Y7BL?^A$68qW%TntZ0&D5Tr?R{)W-?VY-)-7jWSfc@e{cP6|npRZx{vs5)F-;Qd ze170Xrx)kc>{4?IzVq%aU;@yy2>Tv4N^ zQdy&Ce}dCuV-wud-(xydBB;oL&O-L>+ocYHl$!dFwGlp{YXw*g&#?JA!@or-O{1!%IMG1ydBer<7 zMxV}{I1}p6s!aJ|$4LrO!NLCXe^zjeRiwDx=KucVi+wLuiM6IQbFF@`zlkVghBKK) z%$E~sT>Oy6DEw%(u%gr`!gebQEAxFHOg+UbGmo&eqX_wTmWURn=sJJ*8C2YlZL-n; zM;u9A&Z2M7i(PPl31j2PLpmVbdy9N@?5S4_2eXCCWC*A%mD?B4Cy&vkC|JuN4EC;e!B}Eo0q_$#&uZA`5Ndqr7|#K zb%U?sOC|ND3*rG;zE-ezR&>$N{)ak}ZULCyhT?Nskz z_jdcdw!_%JE2BG?1}EEm`8JutC3*Fl^<7&j8Ka;}dYE{&)0k~fy3oKEeHai- z8hr0z>7cfAvh*5*YE;9Nj-$Y_h}TbDg-}hwROv_EFmHflAtl9By%K?oe@*lfGcVW; z!e*_%|Ge%DFNA6$5IJ#R=k(t!JpLrV^jqrz6-0ngORO#**)$2F3vj8Cg&g3<3Jipl zlSAfLho_dV>@r4>nq)795&9x6+aroHQ&$L4;)mk0c#$tuZH%*(1Bp8i zfwZcC6v))u(Y$_f$Sf*6F&oyb_XOW@SI2sT>aP>BTmteC(vFPSGuz<=AeNAZ-pDPZ z*Vhr@rxjnnnA`Q+pi!(N&JDXIdUF>L&*-Qn#x_~TqYtxTB4b|U6w#Z>{C+Zf=V0r5 z3RNNi3_?N4*mkQi=FRVUP|lY`!Cn6Hd7bC&@Q;~ ztB%Of!MNcArSJ|C&+OOIYl?5pJbB#ajfp7;Ic|RJr=+5Ze9uJIxqB;M;ni@;4clPS z&j^%2Sd?!4V*mF?;^YiZXl~%3T9#b4f0Dk%fQb^kaUFW8un)coIamF?GJ_J*J2a>d z6EaS~54if2%P)=te1>~8@YX5Ij|HojQLNqe`XWQQK&nzzQyhPL?I88;BE^@+=~$8K zga^c(i}2%bZ5auP63A61L#B`7AuWJ_Cveg?M6o22#`z)&Y?bT6fy0R}Bh|1ok@K&L zT%{R@ohv7I=Iie?xZa>r&oY+^>jOxIr;I@YZ5nW*1{T#7#~DIe-Gv5m0fpozx=?C7K?)7=W`caW_&2+3spnxcYNo6$IE}pADS0%=eWL?PX!iKS_G5Eu;uD<3y9JU zTAD_x8C3X$M}#R%J;FjAtmuP}Hp5SYLx3|#HSTPqR zH11d5j0RNY4@F9NeD!qI(GOr1gk{GOXXeQciNs;$1gTb#K6(whz?^T0X)3T)83WV& zxuYb?YhMN|p@Hk_3UD4h?YPjH4-LxJlFG89rk6UcIPd%2ANqk9@F&!N%^e*suz{rv zGt_96EJ<+Z;(^m?R^-NpSjVNco#6v}jvF!tKq6s)xVbu4tt(+JjZ;eKr1SNhN?_xntmwlwy!OULRa5+d7 z+TB$c_Fy<=vokz1yZ^0sSR}hmhX!b7^#Rf)OInk?_vWDIj1v~`sdoZ3OfkrIZT4R_ z)DfwyK&?=&!#&`TD^@=!hs9#^j2lA=Jd!12$s6D67cGeuce99FA&S6f#+AN@N>Q$G zqxU+r%$0R^2!;cwKw0oX0F4MfV*Cur%j-V=_?kvf5wjj|&IMY2p~ybhC72YPo4lOY zgqS=o^XPp$H{5H8q)TOjPtJD%t9por!H)r7Fvh)9~+t-zmY!{c~lJC3V}#9BmbXC?}(|+W4Y&!@wOeI zCwh?dF?pPAW+uHRBELVHY{x|Nq%V>8;Yljeb337N3NZ+N(?M-F$9x=56_g06HDKoI za+mG9hJDs(8BS3O!J-n!Z@QUT{WIT#qgoI%q&v9qNmsVX#oj^r!Q+Y_SiK(tK53|3 z4twhE<1d3<2N%p_jj;qumG>^Tb8@VabQ<>bpcra!M*})7ExYEf{O7RrjPB=yC zE?RBOQu=l!adFG*+{ljl&@va@F%cXRPOCLzUmdV8FsP}bYzi=e?_@7kt{3F?!!YGl zO9(DD;Gpj%CO^26+CEkuTtFe{#o{&LCGsrb#GS$V4bR9|)d?FjxFiPgZH7~D`_Imr zfwspl)7DtXE>|bDl_yk1lH=gsH=j}Wu}Y)wi(b%8wKE(L*xVVBDv~BG^!-ZLdXY0E zZm`;nN3X)4uk9!f==5HHe!T-xlPhV(Cp-VcOImnzL>bY=d~}f`V-Lkz4z=h9{kWi0>`{@B>d$<4UYb&H7M-Os3Ej>&CO>mL(u#D&o(pI+jCc0kq4Se7)pjI1o8ppkmz?l}sIRR{LX=(tAV@$c<>lp-h@)d}&H`AL7Bck*6dt_mx9cgC(DmwTI~ zFd6Y;mpN&C{hr+f206peNted(t@3g~VOkVD*{>s;wTowi=r!x8*L3bvw7S@#xN_a; zT<1hmR0k(P-pmCEsD=+WB(uR>@>IbMnd%%@me}V;~8L$Su7UUIVA+06( zrl3vKvNyfI9sdI*^3#muv36W+l)jb`@?_(n5euO-eSjtd0XP?6mwS7UFbU~;feRT$ z_<^{{2~VqSWvIBd`(Qi`%Dy@Kk}W@H=)RH;>(8@qMnCRXxOF4M0&Z59F)V;2Og9bMbcxBX65q*FrS`?&oxQ^ShnP&BKHS`Ny|At%0 zaGY)74fih56}t)1i7RP$ z$8@%nY8DyZgEbnw;TC7M&wdyh=!g0>9z9+E6<#qHT#Im$(MgSz1=5()`sG=*MGg$s zh{vk862b)$XRa8|nY)~=LT5#f*Vl8moWVTA=rcraVUEOt_qi_(KPkcAJ{t7j0?QTj zK~16TAx1OsrYuz1y_10|JE+dc#CN77e06jTwk`_gwNtm$1z17K# z|7!Ed4-)P7)mPs1chi017eV zLzP^GrsBAzZ0d)K6{|hZ)jW^QOW=s3RI6F}CcG-B9O&cHqRAWtXLQavBmuvS$PDN{ zu8&`IkX(<2BNy;;@-V{_p_C|F=feFAdB5XA=+&o5GR#T!@F(HUC*aeeH`l1^fK*qW501WKkXClZt%9lW;V!rG6>&iiBw?e4P z0uaSO?lTgcs=T-4<+9&Sca`06CR~hxe3V$(cNf$T|5;UDgFg ztVn*!@#-7a_VLe4!BtNQ7dLysVhAGUh3i%U#ea43GF%hM=~sJUhH*O;6TUD2bFV;X zqzn&AyebS3O4rxryET=NGe!({4Uo=^5agcv{CXw91=Z;oljDq`IjywOuM(I_m<>pu z2_c}pf&!}HrEr80nF9+O*k9!20VCFj`pbwos-pn%mBPnkvR#&EILKCbgsSICqJXqn zY-J_%-G&$PBbt{bl@VS}5YR|kwybhx8?PPWcbX+=ynV|8eYgUx$Iv^CE11U7$ z8sjx#_P;oa{N$uDG}qBk#|u46zm)Hx7e#<&_xG_lp^$wk4W7=-NGSeYwiPlY2nB^|q<|XGC4WmJIfR6=ww7SR5P^Y< zv=&+F2{eKvEca&ba~`C5!ZeIdcnF^$&A|!PV9rjlZ^c3#Pl>r6I0ath)O=vm5vPwp z2aVwBJ@?4iOLl_y3}IL!B%0&GqbJa74C8waj<`s9@-Iu&HbN&He~Wp^g65xY>U=nyrLLBb_T4gf(f7U{9kRu+Ob}29f=+HH z2F!4k-1n9U;mqHpiLPZ`_ue@U+F8P0ZW&~jf_K}IbEKNQXx$A}+nvS3${Qn{jO-&) zsULVH+n`}HaNBhhb(^OrX25ya1N8$<68=8OSwP%bgFHN6rb~VPGFlc805g?c>`=k5 zu|SJ#VA2@L7>_U-xcQ$Ie7ZlhCS#J7v+>tJ-?0+{OaDD|b!;!~y zjV>Cw{yHx={IdZdzZIf3M^aGfaW0E;Kqm9L=g@!gMUNic-#?QhKI@4P^?%dpN~kGI zqw>ahZxYm_5&n|Sysn+Q_2ujsLgo-87*Z*4SkP{)S%uK2-JLk#to!`GBO}$TNE2D% z?sT@!ktStpY4}2*3=&=ZD2-c7K6q~b$YjbUEOIvnJQq|SjFnvQd7|Y^3G>hZ=3!SM zt$n~5DDHUYy<*IY;7qR9cHPv23F-*kyMuqM8BPDwmm!*HPiie(QO8{uwx6N@>4ABB zYdU6mdaSneUryK&(c002<~(OOM&bR(u7GMYF{dDi87KSaS581oZQrz^-61LHC{>ovt3Vy(D)H~jp>Luqgpm;mn8pz z*c1q5aN64Vu3QI%O#xaLua~kh%!pJbwZD@Bv@%D5lidwa>f0(RZ$Xs`;+ooDe}LiL z43d9{NW%H^=K;O$fS}ahTgII3v_wQaRfuY!7vJIm@e*k%;_r(&2lz6&#;iZ z&&>3<`X~|z2^BaNNpm%R7bH4rL|~+oKd|UkqjVpbejN~(Y)W`<$-fZ1$^b5 zm9g^8-(Wqu9PK$Vcr7v02wKZQ{zn67m4Xxv#{Pby(mCvV91!e$gVk|A;1|B~afnun zap2{>7N{%BdM6)~j|Wx_OimbYQInaQkY*#XJXWr%to-!2m5^RkO+%v-)RF+wZI|Z@ zq`&_?p`^4qQlpX6FJIk75i=%xd%_c9w9;lx0|o6_G>p)#6NVL9x7gvH6dgUB{|;Dq zhye|^j<}T-bQ~Q}l79B=8TlF>9$puazf)877bO0NDD$JIe|dfRRnuG8KPAiaw(rxR z^|!KEf4Z3LmOcqyz$ed!-t_d`5Rg+xaB_&m_djNOo$?tIve|QsxA6n8{FUM_Q?4^` z>W<7>mhNC+iG!MTD#=9!twu@EwgaGcE#Y+cf4j;|ce<|JS2TSA$g)&2w_o9fCb=}L z4nk@C$u(x=X~*;jlQ^EoqG^AdWTb6uYI?{v#zR^Hk{~apTf9wO)0QOw)UFPSHCdg$ z4Gj$mNl7}^Wf$h1{o3&{8<33ew*qxLDQsnBrxn=43d!>F!L|5u^}1(Ak6*l-8=XnO+7vA zFPPP1rH07}B)=<^sLEwgMdEl{6YdbVZiRvD#|?7QP#Rxa)IopDkUq8GSJTCJXNN)A zViJ#@AU~*Yf^dsn+c8K&kXI1gasW9tP?iJ@({xy6F>N5Li7c~xRYuVNeW2Bw6EZAV z8?i=r-T%dw#PalM@t@Yen<6$trWvRl2Xkn?GIC7=DLw}8U;LEvfL8g7FVE?QRGA#R5Iueh_1`OgjlFiPh1MR$V! zOl1ut!n#8Srmil?$#izwo;~`ey5ikNH-Yo#J6lZwqhsV~XlxL{%Uv-Q%X@ZeA}k3J z&RPm^cp%*79GBO{(-lmolY{@9=5Y3j#nFhITNVnMq=OT?Xi_1WP45H}G22!G5Y?ke=9a} zZ2e#ghnp2PeZUL;?1aS#2xr8SM$T+8gq{ot8%fk3+nWwXcHT^FKKO8bLyl0m&&-i) zmC8Y}FzTbr^v%VE7qo$_foBIg;Eg+N3OfF!V0N%)l&ubf1V{bZC+t;{uO@(I3jo;_ zhY7pg|C~Q2@`4VLa9cr%?xd`${Iyz;bJyf%R10Y*%fM99U-Jn;ExG+w5gV9dcKi3E zxe8yPUvc{x%)xz5Gw`xP=tZy*dQzeYrLe8k6z`sNX};-s>v&*`dGt5~8N1GKxHUZD z$s_AiJqd-*o&yUQOl4vj5qM8%WDqeSbRvAf9E4z#-~;SAv;RL|QM4(IBJ9-7%gY0G zM*PdFFO<#AbB5*~(KodIdjmY%lel>FLJ&1`P!R@+6KeVhUD$e!oP9Ht%F12d?YQwa zwEpXj(*JKSP0bFX@gO>I@CzT$D*nGe;_q=x+&TC68b#dMadx1<>bi0^aM;Xr(C0|k zU}If{BcIO@^eFHsc_F1P9s0)v;+00eM#AokkE%iKmb0~a&v*yu!xA)e)4j1R8Wu#k#T>J;|1YNP-_Ix1lSx_ zpelwb&-!NJ-QXY=6F2w!*;~yqBi0t6bwNf>J_L%O5EJA?WIkXhazG>#gyh^leg2#Z zl31Frl;=Qo<_8G)Dumw0{>gGp3M7<3`lPqF7kBrjfO*BU86(V6K}NqLevr#} z`S|z%{1}4K4B~xYFd<^nK-4{&IXz|s`3*r)?(#~|jV5H2cn?RtjVrrJK1eIO|V%_hw zACcq&ndTnQg6{yuO7MhpfeP#M7>R>0MDQ9WBD7HyX&=~lK=;iA;iIiD_hQg$D}!1U zAjD2gLh|I@{i0!mM9?oN)KC!H zf(V)fPH0b*qQHly5`^a-o@e|f^0Kw!?5b-E7o*Gwqw zA;`%?24I1jgc+!Vf#Up!_THfSYtV^48)&e4%t72_xJbKyDYp5==yOof$nDy|g#nv~ zDQ6mdIPJB<15Ky8?)(zuH*_>KWE2%6RFjw>O2-5q;rW^|lU#7@UvrpFU+7OptcAac zfDGjo6|vjfu7|6D?~O}OSJu-@NoaS31II5@&}MW4k%R*Oy|%0RvMSz=;0i2B|J zbwUu%f;Er~adD;=uN@XPwot^m51R(U>Yk60RPpg?hOMC)G{^;@s}EOUb)5HyNyKl_ zN0!^UNwbiLNEzwOognK3wBws3nE2x1`Cz-LC@5fpNDK&d`LkvEXjnGk#5n%>rH`l; zzOJeQDX1)%TVmh=mDnGDRXzH3i3!=;n4800g^+9Z|MLVmX=}M*`S4Oi=anN?Lb;r{ zN^CiPV%povc@zOlsvkA*(y)c1!-fV16p7OZUTSOL1bopi3d^F$*6(+dm?^z!jJ zTGm0$*8R>_N3{yvxKS*}lC6CC;_Xc_RU#sy2sswq^XE(_=uk(OA19)>k8$_M*-e;E zVWN&mF2hqUCig$|_CD--J}=U@xMg5qpkZvBQCnME-P~Lf9*#qfi!n7lU6h+EJ3l}F zbz>xz(9zMUuBys)ZYX&qRyH?xH}CV$@-LkJesrYV zs8)Ufft>z+rS5qr0s?~DalE{|kL%a>_w{LMY5Odzz{MZ8(&%igJLjl(_3G8e*4Ctz zM#anV%RhfUyR@+7IW;-Sp7x=rvQixj3+s7W+UL&BfalMjOSomCPJHQ{v1V!E50fl7 z$Z+3*pi}L!&=arqogMAm++1rrJ8f_8rpBfwH)rRZ%1Zv4>gs~xVx^z11~+fsjC)yJ z%(c3rk&TsUP$T3Xu3#6)p-xBTk*da)k&@~>aYD=RCaVq(`m3Zi`8oX7Jo-`?Hj zf`Rn%GT`ImbEfOLW!&RbU0eG~COfOC>4xC7YXzmHcQP_E6qeE3y?wCoP%xhuNY0^9 wR0{3z-z!i3F;FNoDzvi?{@X-Dp+bDov;I^hyNk?b!Q?~TmRFH0lQ9eUUjk?^{Qv*} literal 0 HcmV?d00001 diff --git a/src/axom/sina/docs/sphinx/curve_sets.rst b/src/axom/sina/docs/sphinx/curve_sets.rst index f465fd49b4..ec01e6fa0c 100644 --- a/src/axom/sina/docs/sphinx/curve_sets.rst +++ b/src/axom/sina/docs/sphinx/curve_sets.rst @@ -7,4 +7,28 @@ ========== Curve Sets -========== \ No newline at end of file +========== + +Sina ``CurveSet`` objects act as a way to group related independent and dependent +``Curve`` objects. Each ``Curve`` is a 1-dimensional list of numbers along with +optional units and tags. + +A common example of a ``CurveSet`` could be time series data. Here the independent +curve would be time and the dependent curve(s) would be the data you're measuring +over time. + +We will demonstrate the creation of a ``CurveSet`` using a simple experiment that +measures the 2D position and velocity of a ball while it bounces. In the below script +We will create one function to generate the data for the ball bounce experiment and +another function to assemble the ``CurveSet`` object and add it to our ``Record``: + +.. literalinclude:: ../../examples/sina_curve_set.cpp + :language: cpp + +Once this code is compiled and executed a json file will be created. Here, we'll +use `PyDV `_ to injest the Sina json +file in order to view the position and velocity of the ball along the y-axis over +time. + +.. image:: ../imgs/ball-bounce-y-axis.png + :alt: The position and velocity of the ball over time along the y-axis diff --git a/src/axom/sina/docs/sphinx/documents.rst b/src/axom/sina/docs/sphinx/documents.rst index 04d0edc4c5..fd43fa0b3c 100644 --- a/src/axom/sina/docs/sphinx/documents.rst +++ b/src/axom/sina/docs/sphinx/documents.rst @@ -7,4 +7,186 @@ ========= Documents -========= \ No newline at end of file +========= + +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: + +.. code:: json + + { + "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 <./records>`_ +and `Relationships <./relationships>`_. + +-------------------- +Assembling Documents +-------------------- + +``Document`` objects can be assembled programatically. To accomplish this: + +1. Create a new instance of the ``Document`` class +2. Create a ``Record`` +3. Add the instance of the ``Record`` with the ``add`` method + +On the `Sina C++ User Guide <./index>`_ page, you can see an example of this +process. Below we will expand on this example to add a ``Relationship``: + +.. code:: cpp + + #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 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 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. + saveDocument(document, "MySinaData.json"); + } + +After executing the above code, the resulting ``MySinaData.json`` file will +look like so: + +.. code:: json + + { + "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: + +.. code:: cpp + + #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: + +.. code:: cpp + + #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; + } + +--------------------------------------------------------- +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: + +.. code:: cpp + + #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 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 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(); + + std::cout << "Number of Records: " << records.size() << std::endl; + std::cout << "Number of Relationships: " << relationships.size() << std::endl; + } + +Running this will show that both records and the one relationship were +properly queried: + +.. code:: bash + + Number of Records: 2 + Number of Relationships: 1 diff --git a/src/axom/sina/docs/sphinx/index.rst b/src/axom/sina/docs/sphinx/index.rst index 9e3ee663a7..40c2abf70a 100644 --- a/src/axom/sina/docs/sphinx/index.rst +++ b/src/axom/sina/docs/sphinx/index.rst @@ -11,9 +11,10 @@ The Sina C++ library can read and write JSON files in the Sina schema. It can be used by simulation applications to summarize run data to be ingested into a database using the Sina tool suite. -The top-level object in the Sina schema is the Document. It contains lists -of Record and Relationship objects. The example below shows the basics. -For more details, see the `Tutorial `_. +The top-level object in the Sina schema is the :doc:`Document `. +It contains lists of :doc:`Record ` and :doc:`Relationship ` +objects. The example below shows the basics. For more details, see the +:doc:`Tutorial `. .. literalinclude:: ../../examples/sina_basic.cpp :language: cpp @@ -41,4 +42,4 @@ following: :maxdepth: 2 tutorial - core_concepts \ No newline at end of file + core_concepts diff --git a/src/axom/sina/docs/sphinx/records.rst b/src/axom/sina/docs/sphinx/records.rst index 27af157037..96b097708f 100644 --- a/src/axom/sina/docs/sphinx/records.rst +++ b/src/axom/sina/docs/sphinx/records.rst @@ -7,4 +7,422 @@ ======= Records -======= \ No newline at end of file +======= + +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 + - an msub job + - a cluster of runs that has some metadata attached to the cluster (this + ``Record`` might have a "contains" :doc:`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. + +.. contents:: Topics Covered in this Page + :depth: 2 + :local: + +------ +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``: + +.. code:: cpp + + #include "axom/sina.hpp" + + int main(void) { + // Create the record + axom::sina::ID myID{"my_record", axom::sina::IDType::Local}; + std::unique_ptr myRecord{new axom::sina::Record{myID, "my_type"}}; + + // Create the datum with an array of strings + std::vector myTags{"input"}; + axom::sina::Datum myDatum{12, myTags}; + + // Add the datum to the record + myRecord->add("my_scalar", std::move(myDatum)); + std::cout << myRecord->toNode().to_json() << std::endl; + } + +Once executed, this code will output: + +.. code:: json + + { + "local_id": "my_record", + "type": "my_type", + "data": + { + "my_scalar": + { + "tags": ["input"], + "value":12.0 + } + } + } + +.. _datum-type-label: + ++++++++++++++++++++ +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: + +.. code:: cpp + + #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 scalars = {1, 2, 20.0}; + axom::sina::Datum myArrayDatum{scalars}; + + // Prints 0, corresponding to string + std::cout << static_cast::type>(myDatum.getType()) << std::endl; + + // Prints 1, corresponding to scalar + std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; + + // Prints 3, corresponding to scalar array + std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; + } + +++++++++++++++++++++++ +Setting Units and Tags +++++++++++++++++++++++ + +For certain ``Datum`` instances it may be helpful to assign them units and/or tags. +This can be accomplished with the ``setUnits()`` and ``setTags()`` methods respectively. + +Below is an example of this functionality: + +.. code:: cpp + + #include "axom/sina.hpp" + + int main(void) { + // Define 2 different datums + axom::sina::Datum myDatum{12.34}; + std::vector scalars = {1, 2, 20.0}; + axom::sina::Datum myArrayDatum{scalars}; + + // Set the units for one datum and the tags for the other + myDatum.setUnits("km/s"); + std::vector tags = {"input", "core"}; + myArrayDatum.setTags(tags); + } + ++++++++++++++++++++++++++++++++++++++ +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: + +.. code:: cpp + + #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 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 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 keys and type of datum + for (const auto& pair : data) { + std::cout << pair.first << " is type: " << static_cast::type>(pair.second.getType()) << std::endl; + } + } + +Executing this code will print out: + +.. code:: bash + + datum1 is type: 1 + datum2 is type: 0 + datum3 is type: 3 + +Which, we know from `Checking Datum Type <#datum-type-label>`_, 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: + +.. code:: cpp + + #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 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 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 + std::cout << "datum1: " << data.at("datum1").getScalar() << std::endl; + std::cout << "datum2: " << data.at("datum2").getValue() << std::endl; + std::cout << "datum3: "; + for (const auto& value : data.at("datum3").getScalarArray()) { + std::cout << value << " "; + } + std::cout << std::endl; + } + +This will provide the following output: + +.. code:: bash + + 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: + +.. code:: cpp + + #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 myRecord{new axom::sina::Record{myID, "my_type"}}; + + // Add the files to the record + myRecord->add(myFile); + myRecord->add(myOtherFile); + + std::cout << myRecord->toNode().to_json() << std::endl; + } + +This code will produce the following output: + +.. code:: json + + { + "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: + +.. code:: cpp + + #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 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); + + std::cout << std::cout << myRecord->toNode().to_json() << std::endl; + } + +As we see from the output, the contents of ``myFile`` are no longer in the +``Record`` instance: + +.. code:: json + + { + "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: + +.. code:: cpp + + #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 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: + +.. code:: bash + + 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: + +.. code:: cpp + + #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 run1{new sina::Run{run1ID, "My Sim Code", "1.2.3", "jdoe"}}; + } diff --git a/src/axom/sina/docs/sphinx/relationships.rst b/src/axom/sina/docs/sphinx/relationships.rst index 174df22387..c4b3e61712 100644 --- a/src/axom/sina/docs/sphinx/relationships.rst +++ b/src/axom/sina/docs/sphinx/relationships.rst @@ -7,4 +7,84 @@ ============= Relationships -============= \ No newline at end of file +============= + +Sina ``Relationship`` objects are used as a way to describe the correlation between +two ``Record`` instances (and/or ``Record`` inheritors, e.g. ``Run``). A ``Relationship`` +consists of three parts: a subject, an object, and a predicate. The subject and object +must be IDs referring to valid instances of ``Records``, while the predicate may be +any string. + +In describing the connection between objects, a ``Relationship`` is read as " + ". For example, in the relationship "Alice knows Bob", "Alice" is +the subject, "knows" is the predicate, and "Bob" is the object. Below are some additional +examples: + + - Task_22 contains Run_1024 + - msub_1_1 describes out_j_1_1 + - Carlos sends an email to Dani + - local_task_12 runs before local_run_14 + +Note that a ``Relationship`` is described in the active voice. **Avoiding the passive +voice in predicates is recommended**, as this keeps the "direction" of the relationship +constant. An example of a passively-voiced ``Relationship`` is "Dani is emailed by Carlos". +Instead, this should be phrased as "Carlos emails Dani". + +Below is an example showcasing how to construct a ``Relationship`` programmatically. Here, +we assemble a ``Relationship`` showing that "Task_22 contains Run_1024": + +.. code:: cpp + + #include "axom/sina.hpp" + + int main(void) { + // Create IDs for both Task 22 and Run 1024 + axom::sina::ID task22{"Task_22", sina::IDType::Global}; + axom::sina::ID run1024{"Run_1024", sina::IDType::Global}; + + // Create the relationship and print it out + axom::sina::Relationship myRelationship{task22, "contains", run1024}; + std::cout << myRelationship.toNode().to_json() << std::endl; + } + +If executed, the above code will output: + +.. code:: json + + { + "object": "Run_1024", + "predicate": "contains", + "subject": "Task_22" + } + +As with any other Sina ID, the subject or object may be either local (uniquely refer to +one object in a Sina file) or global (uniquely refer to one object in a database). Local +IDs are replaced with global ones upon ingestion; all ``Relationship`` instances referring +to that local ID (as well as the ``Record`` possessing that ID) will be updated to use the +same global ID. + +Let's add on to our previous example to demonstrate this: + +.. code:: cpp + + #include "axom/sina.hpp" + + int main(void) { + // Create IDs for Task 22 and Run 1024 + axom::sina::ID task22{"Task_22", sina::IDType::Global}; + axom::sina::ID run1024{"Run_1024", sina::IDType::Global}; + + // Create the relationship and print it out + axom::sina::Relationship myRelationship{task22, "contains", run1024}; + std::cout << myRelationship.toNode().to_json() << std::endl; + + // Create a new ID with local scope and use it to create a Record and Relationship + axom::sina::ID myLocalID{"my_local_run", axom::sina::IDType::Local}; + std::unique_ptr myRun{new axom::sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; + axom::sina::Relationship myRelationship{task22, "containts", myLocalID}; + } + +In the above code, the "my_local_run" ID would be replaced by a global ID on ingestion. +If this new global ID was, for example, "5Aed-BCds-23G1", then "my_local_run" would +automatically be replaced by "5Aed-BCds-23G1" in both the ``Record`` and ``Relationship`` +entries. diff --git a/src/axom/sina/examples/CMakeLists.txt b/src/axom/sina/examples/CMakeLists.txt index 6254fff79c..aca92d8312 100644 --- a/src/axom/sina/examples/CMakeLists.txt +++ b/src/axom/sina/examples/CMakeLists.txt @@ -11,13 +11,14 @@ #------------------------------------------------------------------------------ set(sina_example_sources sina_basic.cpp + sina_curve_set.cpp sina_tutorial.cpp ) set(sina_example_depends sina conduit::conduit) #------------------------------------------------------------------------------ -# Add targets and tests for Sina examples +# Add targets for Sina examples #------------------------------------------------------------------------------ foreach(src ${sina_example_sources}) get_filename_component(exe_name ${src} NAME_WE) diff --git a/src/axom/sina/examples/sina_curve_set.cpp b/src/axom/sina/examples/sina_curve_set.cpp new file mode 100644 index 0000000000..8e56b7fcf7 --- /dev/null +++ b/src/axom/sina/examples/sina_curve_set.cpp @@ -0,0 +1,108 @@ +#include "axom/sina.hpp" + +#include +#include +#include + +using namespace std; + +struct BounceData { + vector time; + vector xPosition; + vector yPosition; + vector xVelocity; + vector yVelocity; +}; + +BounceData generateBounceData(double initialY, + double initialXVelocity, + double coefficientOfRestitution, + double timeStep, + double simulationTime, + double airResistanceCoefficient) { + BounceData data; + double time = 0.0; + double yPosition = initialY; + double xPosition = 0.0; + double xVelocity = initialXVelocity; + double yVelocity = 0.0; + + while (time < simulationTime) { + // Update position and velocity based on time step + xPosition += xVelocity * timeStep; + yVelocity += -9.81 * timeStep; // Acceleration due to gravity + + // Apply air resistance on x-velocity + xVelocity -= airResistanceCoefficient * xVelocity * timeStep; + + // Update height (y position) + yPosition += yVelocity * timeStep; + + // Check for bounce (when y position becomes negative) + if (yPosition < 0) { + yPosition = 0; // Set y position to 0 at the ground + yVelocity *= -coefficientOfRestitution; // Reverse y velocity with energy loss + } + + // Add data points for this time step + data.time.push_back(time); + data.xPosition.push_back(xPosition); + data.yPosition.push_back(yPosition); + data.xVelocity.push_back(xVelocity); + data.yVelocity.push_back(yVelocity); + + time += timeStep; + } + + return data; +} + +void addCurveSet(axom::sina::Record &record, BounceData bounceData, string curveName) { + // Create the curve set object + axom::sina::CurveSet bounceCurveSet{curveName}; + + // Add the independent variable + axom::sina::Curve timeCurve{"time", bounceData.time}; + timeCurve.setUnits("seconds"); + bounceCurveSet.addIndependentCurve(timeCurve); + + // Add the dependent variables + bounceCurveSet.addDependentCurve(axom::sina::Curve{"x_position", bounceData.xPosition}); + bounceCurveSet.addDependentCurve(axom::sina::Curve{"y_position", bounceData.yPosition}); + axom::sina::Curve xVelCurve{"x_velocity", bounceData.xVelocity}; + xVelCurve.setUnits("m/s"); + bounceCurveSet.addDependentCurve(xVelCurve); + axom::sina::Curve yVelCurve{"y_velocity", bounceData.yVelocity}; + yVelCurve.setUnits("m/s"); + bounceCurveSet.addDependentCurve(yVelCurve); + + // Add the curve set to the record + record.add(bounceCurveSet); +} + +int main() { + double initialY = 10.0; + double initialXVelocity = 2.0; + double coefficientOfRestitution = 0.7; + double timeStep = 0.2; + double simulationTime = 5.0; + double airResistanceCoefficient = 0.01; + + BounceData bounceData = generateBounceData(initialY, + initialXVelocity, + coefficientOfRestitution, + timeStep, + simulationTime, + airResistanceCoefficient); + + axom::sina::Document doc; + + axom::sina::ID id{"ball_bounce_run", axom::sina::IDType::Global}; + unique_ptr study{new axom::sina::Record{id, "ball bounce study"}}; + + addCurveSet(*study, bounceData, "ball_bounce"); + doc.add(move(study)); + axom::sina::saveDocument(doc, "ball_bounce.json"); + + return 0; +} \ No newline at end of file From 76c7c867dca4e1087c0837f052366bc8cbad258e Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 27 Jun 2024 16:00:27 -0700 Subject: [PATCH 12/60] fix references to sina namespace in doxygen --- src/axom/sina/include/AdiakWriter.hpp | 4 ++-- src/axom/sina/include/Datum.hpp | 6 +++--- src/axom/sina/include/Document.hpp | 10 +++++----- src/axom/sina/include/File.hpp | 4 ++-- src/axom/sina/include/Record.hpp | 12 ++++++------ src/axom/sina/include/Relationship.hpp | 12 ++++++------ src/axom/sina/include/Run.hpp | 4 ++-- src/axom/sina/src/AdiakWriter.cpp | 14 +++++++------- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/axom/sina/include/AdiakWriter.hpp b/src/axom/sina/include/AdiakWriter.hpp index 209137e2fb..c720640873 100644 --- a/src/axom/sina/include/AdiakWriter.hpp +++ b/src/axom/sina/include/AdiakWriter.hpp @@ -30,8 +30,8 @@ namespace sina * like this: * * \code - * Record record{ID{"my_id", IDType::Local}, "my_record_type"}; - * adiak_register_cb(1, adiak_category_all, adiakSinaCallback, 0, &record); + * axom::sina::Record record{ID{"my_id", axom::sina::IDType::Local}, "my_record_type"}; + * axom::sina::adiak_register_cb(1, adiak_category_all, axom::sina::adiakSinaCallback, 0, &record); * \endcode * * Note that not everything that Sina can capture an be captured through the diff --git a/src/axom/sina/include/Datum.hpp b/src/axom/sina/include/Datum.hpp index 5600e1f1bb..280bf83182 100644 --- a/src/axom/sina/include/Datum.hpp +++ b/src/axom/sina/include/Datum.hpp @@ -37,11 +37,11 @@ enum class ValueType { * or an array of doubles. * * \code - * sina::Datum myDatum{12.34}; + * axom::sina::Datum myDatum{12.34}; * std::string value = "foobar"; - * sina::Datum myOtherDatum{value}; + * axom::sina::Datum myOtherDatum{value}; * std::vector scalars = {1, 2, 20.0}; - * sina::Datum myArrayDatum{scalars}; + * axom::sina::Datum myArrayDatum{scalars}; * * //prints 1, corresponding to Scalar * std::cout << static_cast::type>(myDatum.getType()) << std::endl; diff --git a/src/axom/sina/include/Document.hpp b/src/axom/sina/include/Document.hpp index 6567a751f5..a4e8cc8c34 100644 --- a/src/axom/sina/include/Document.hpp +++ b/src/axom/sina/include/Document.hpp @@ -35,27 +35,27 @@ namespace sina * Documents can be assembled programatically and/or generated from existing JSON. An example of an assembled * Document is provided on the main page. To load a Document from an existing JSON file: * \code - * sina::Document myDocument = sina::loadDocument("path/to/infile.json"); + * axom::sina::Document myDocument = axom::sina::loadDocument("path/to/infile.json"); * \endcode * * To generate a Document from a JSON string and vice versa: * \code * std::string my_json = "{\"records\":[{\"type\":\"run\",\"id\":\"test\"}],\"relationships\":[]}"; - * sina::Document myDocument = sina::Document(my_json, sina::createRecordLoaderWithAllKnownTypes()); + * axom::sina::Document myDocument = axom::sina::Document(my_json, axom::sina::createRecordLoaderWithAllKnownTypes()); * std::cout << myDocument.toJson() << std::endl;); * \endcode * * You can add further entries to the Document using add(): * \code - * std::unique_ptr myRun{new sina::Run{someID, "My Sim Code", "1.2.3", "jdoe"}}; - * sina::Relationship myRelationship{someID, "comes before", someOtherID}; + * std::unique_ptr myRun{new axom::sina::Run{someID, "My Sim Code", "1.2.3", "jdoe"}}; + * axom::sina::Relationship myRelationship{someID, "comes before", someOtherID}; * myDocument.add(myRun); * myDocument.add(myRelationship); * \endcode * * You can also export your Document to file: * \code - * sina::saveDocument(myDocument, "path/to/outfile.json") + * axom::sina::saveDocument(myDocument, "path/to/outfile.json") * \endcode * */ diff --git a/src/axom/sina/include/File.hpp b/src/axom/sina/include/File.hpp index c40ff3a8a8..56f68eef11 100644 --- a/src/axom/sina/include/File.hpp +++ b/src/axom/sina/include/File.hpp @@ -19,9 +19,9 @@ namespace sina * Every File must have a URI, while mimetype and tags are optional. * * \code - * sina::File myFile{"/path/to/file.png"}; + * axom::sina::File myFile{"/path/to/file.png"}; * myFile.setMimeType("image/png"); - * sina::File myOtherFile{"/path/to/other/file.txt"}; + * axom::sina::File myOtherFile{"/path/to/other/file.txt"}; * myOtherFile.setTags({"these","are","tags"}); * myRecord->add(myFile); * myRecord->add(myOtherFile); diff --git a/src/axom/sina/include/Record.hpp b/src/axom/sina/include/Record.hpp index 3e05356eef..76021d14a7 100644 --- a/src/axom/sina/include/Record.hpp +++ b/src/axom/sina/include/Record.hpp @@ -52,10 +52,10 @@ struct FileHashByURI { * File objects and a map of Datum objects. * * \code - * sina::ID myID{"my_record", sina::IDType::Local}; - * std::unique_ptr myRecord{new sina::Record{myID, "my_type"}}; + * axom::sina::ID myID{"my_record", axom::sina::IDType::Local}; + * std::unique_ptr myRecord{new axom::sina::Record{myID, "my_type"}}; * std::vector myTags{"input"}; - * sina::Datum myDatum{12, myTags}; + * axom::sina::Datum myDatum{12, myTags}; * myRecord->add("my_scalar",std::move(myDatum)); * std::cout << myRecord->toNode().to_json() << std::endl; * \endcode @@ -167,12 +167,12 @@ class Record : public DataHolder { /** * A RecordLoader is used to convert conduit::Node instances which represent - * Sina Records into instances of their corresponding sina::Record + * Sina Records into instances of their corresponding axom::sina::Record * subclasses. For convenience, a RecordLoader capable of handling Records of all known * types can be created using createRecordLoaderWithAllKnownTypes: * * \code - * sina::Document myDocument = sina::Document(jObj, sina::createRecordLoaderWithAllKnownTypes()); + * axom::sina::Document myDocument = axom::sina::Document(jObj, axom::sina::createRecordLoaderWithAllKnownTypes()); * \endcode */ class RecordLoader { @@ -193,7 +193,7 @@ class RecordLoader { void addTypeLoader(std::string const &type, TypeLoader loader); /** - * Load a sina::Record from its conduit Node representation. + * Load a axom::sina::Record from its conduit Node representation. * * @param recordAsNode the Record as a Node * @return the Record diff --git a/src/axom/sina/include/Relationship.hpp b/src/axom/sina/include/Relationship.hpp index f99e2797ff..f6947b5fff 100644 --- a/src/axom/sina/include/Relationship.hpp +++ b/src/axom/sina/include/Relationship.hpp @@ -43,9 +43,9 @@ namespace sina * ID documentation. * * \code - * sina::ID task22{"Task_22", sina::IDType::Global}; - * sina::ID run1024{"Run_1024", sina::IDType::Global}; - * sina::Relationship myRelationship{task22, "contains", run1024}; + * axom::sina::ID task22{"Task_22", axom::sina::IDType::Global}; + * axom::sina::ID run1024{"Run_1024", axom::sina::IDType::Global}; + * axom::sina::Relationship myRelationship{task22, "contains", run1024}; * std::cout << myRelationship.toNode().to_json() << std::endl; * \endcode * @@ -60,9 +60,9 @@ namespace sina * that ID) will be updated to use the same global ID. * * \code - * sina::ID myLocalID{"my_local_run", sina::IDType::Local}; - * std::unique_ptr myRun{new sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; - * sina::Relationship myRelationship{task22, "contains", myLocalID}; + * axom::sina::ID myLocalID{"my_local_run", axom::sina::IDType::Local}; + * std::unique_ptr myRun{new axom::sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; + * axom::sina::Relationship myRelationship{task22, "contains", myLocalID}; * \endcode * * In the above code, "my_local_run" would be replaced by a global ID on ingestion. If this new global ID was, diff --git a/src/axom/sina/include/Run.hpp b/src/axom/sina/include/Run.hpp index b498bd1a7d..ccf79fa76e 100644 --- a/src/axom/sina/include/Run.hpp +++ b/src/axom/sina/include/Run.hpp @@ -21,8 +21,8 @@ namespace sina * * To create a Run: * \code - * sina::ID run1ID{"run1", sina::IDType::Local}; - * std::unique_ptr run1{new sina::Run{run1ID, "My Sim Code", "1.2.3", "jdoe"}}; + * axom::sina::ID run1ID{"run1", axom::sina::IDType::Local}; + * std::unique_ptr run1{new axom::sina::Run{run1ID, "My Sim Code", "1.2.3", "jdoe"}}; * \endcode * */ diff --git a/src/axom/sina/src/AdiakWriter.cpp b/src/axom/sina/src/AdiakWriter.cpp index 0a78c4fef4..7aec013d31 100644 --- a/src/axom/sina/src/AdiakWriter.cpp +++ b/src/axom/sina/src/AdiakWriter.cpp @@ -34,24 +34,24 @@ namespace enum SinaType {sina_scalar, sina_string, sina_list, sina_file, sina_unknown}; /** -* Add a sina::Datum object to a Record. These are the sina equivalent +* Add a axom::sina::Datum object to a Record. These are the sina equivalent * of an Adiak datapoint. Since we track slightly different info, this function * harvests what it can and hands it off to the Record. **/ template -void addDatum(const std::string &name, T sina_safe_val, const std::vector &tags, sina::Record *record){ - sina::Datum datum{sina_safe_val}; +void addDatum(const std::string &name, T sina_safe_val, const std::vector &tags, axom::sina::Record *record){ + axom::sina::Datum datum{sina_safe_val}; datum.setTags(std::move(tags)); record->add(name, datum); } /** -* Add a sina::File object to our current Record. Adiak stores paths, +* Add a axom::sina::File object to our current Record. Adiak stores paths, * which are essentially the same as Sina's idea of storing files. **/ -void addFile(const std::string &name, const std::string &uri, sina::Record *record){ +void addFile(const std::string &name, const std::string &uri, axom::sina::Record *record){ // We don't care about type here, there's only one adiak type that acts as a file - sina::File file{uri}; + axom::sina::File file{uri}; file.setTags(std::vector{name}); record->add(std::move(file)); } @@ -201,7 +201,7 @@ std::vector toStringList(adiak_value_t *subvals, adiak_datatype_t * void adiakSinaCallback(const char *name, adiak_category_t, const char *subcategory, adiak_value_t *val, adiak_datatype_t *adiak_type, void *void_record) { const SinaType sina_type = findSinaType(adiak_type); - sina::Record *record = static_cast(void_record); + axom::sina::Record *record = static_cast(void_record); std::vector tags; if(subcategory && subcategory[0]!='\0'){ tags.emplace_back(subcategory); From 7d78852e229e44cb7f060852a9b7deec502ef18c Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 3 Jul 2024 14:22:51 -0700 Subject: [PATCH 13/60] add sina fortran interface --- src/axom/sina/CMakeLists.txt | 8 +- src/axom/sina/examples/CMakeLists.txt | 4 + src/axom/sina/examples/sina_fortran.f90 | 161 ++++++++ .../include/sina_fortran_interface.h | 24 ++ .../interface/src/sina_fortran_interface.cpp | 380 ++++++++++++++++++ .../interface/src/sina_fortran_interface.f90 | 118 ++++++ src/axom/sina/interface/src/sina_schema.json | 255 ++++++++++++ src/axom/sina/tests/CMakeLists.txt | 13 +- .../sina/tests/test_fortran_integration.py | 125 ++++++ 9 files changed, 1086 insertions(+), 2 deletions(-) create mode 100644 src/axom/sina/examples/sina_fortran.f90 create mode 100644 src/axom/sina/interface/include/sina_fortran_interface.h create mode 100644 src/axom/sina/interface/src/sina_fortran_interface.cpp create mode 100644 src/axom/sina/interface/src/sina_fortran_interface.f90 create mode 100644 src/axom/sina/interface/src/sina_schema.json create mode 100644 src/axom/sina/tests/test_fortran_integration.py diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index 5dde9ce19a..4657a16580 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -58,10 +58,16 @@ set(sina_sources ) # Add Adiak header and source - blt_list_append( TO sina_headers ELEMENTS include/AdiakWriter.hpp IF AXOM_SINA_USE_ADIAK ) blt_list_append( TO sina_sources ELEMENTS src/AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK ) +# Add fortran interface for Sina +if (ENABLE_FORTRAN) + blt_list_append( TO sina_headers ELEMENTS interface/include/sina_fortran_interface.h) + blt_list_append( TO sina_sources + ELEMENTS interface/src/sina_fortran_interface.cpp interface/src/sina_fortran_interface.f90) +endif() + #------------------------------------------------------------------------------ # Build and install the library #------------------------------------------------------------------------------ diff --git a/src/axom/sina/examples/CMakeLists.txt b/src/axom/sina/examples/CMakeLists.txt index aca92d8312..882c2456bc 100644 --- a/src/axom/sina/examples/CMakeLists.txt +++ b/src/axom/sina/examples/CMakeLists.txt @@ -17,6 +17,10 @@ set(sina_example_sources set(sina_example_depends sina conduit::conduit) +if (ENABLE_FORTRAN) + blt_list_append( TO sina_example_sources ELEMENTS sina_fortran.f90) +endif() + #------------------------------------------------------------------------------ # Add targets for Sina examples #------------------------------------------------------------------------------ diff --git a/src/axom/sina/examples/sina_fortran.f90 b/src/axom/sina/examples/sina_fortran.f90 new file mode 100644 index 0000000000..ad15c9c660 --- /dev/null +++ b/src/axom/sina/examples/sina_fortran.f90 @@ -0,0 +1,161 @@ +program example + use sina_functions + implicit none + + ! data types + integer (KIND=4) :: int_val + integer (KIND=8) :: long_val + real :: real_val + double precision :: double_val + character :: char_val + logical :: is_val + integer :: i + logical :: independent + + ! 1D real Array + real, dimension(20) :: real_arr + double precision, dimension(20) :: double_arr + + + ! Strings + character(:), allocatable :: fle_nme + character(:), allocatable :: ofle_nme + character(17) :: wrk_dir + character(29) :: full_path + character(36) :: ofull_path + character(:), allocatable :: rec_id + character(:), allocatable :: mime_type + character(:), allocatable :: tag + character(:), allocatable :: units + character(20) :: json_fn + character(15) :: name + character(25) :: curve + + ! 1D integer Array + integer, dimension(20) :: int_arr + integer (kind=8), dimension(20) :: long_arr + + int_val = 10 + long_val = 1000000000 + real_val = 1.234567 + double_val = 1./1.2345678901234567890123456789 + char_val = 'A' + is_val = .false. + + do i = 1, 20 + real_arr(i) = i + double_arr(i) = i*2. + int_arr(i) = i*3 + long_arr(i) = i*4 + end do + + rec_id = make_cstring('my_rec_id') + fle_nme = 'my_file.txt' + ofle_nme = 'my_other_file.txt' + wrk_dir = '/path/to/my/file/' + full_path = make_cstring(wrk_dir//''//fle_nme) + ofull_path = make_cstring(wrk_dir//''//ofle_nme) + json_fn = make_cstring('sina_dump.json') + + + mime_type = make_cstring('') + units = make_cstring('') + tag = make_cstring('') + + print *,rec_id + + ! ========== USAGE ========== + + ! create sina record and document + print *,'Creating the document' + call create_document_and_record(rec_id) + + ! add file to sina record + print *,'Adding a file to the Sina record' + call sina_add_file(full_path, mime_type) + mime_type = make_cstring('png') + print *,'Adding another file (PNG) to the Sina record' + call sina_add_file(ofull_path, mime_type) + print *, "Adding int", int_val + name = make_cstring('int') + call sina_add(name, int_val, units, tag) + print *, "Adding logical" + name = make_cstring('logical') + call sina_add(name, is_val, units, tag) + print *, "Adding long" + name = make_cstring('long') + call sina_add(name, long_val, units, tag) + print *, "Adding real" + name = make_cstring('real') + call sina_add(name, real_val, units, tag) + print *, "Adding double" + name = make_cstring('double') + call sina_add(name, double_val, units, tag) + print *, "Adding char" + name = make_cstring('char') + call sina_add(name, trim(char_val)//char(0), units, tag) + units = make_cstring("kg") + print *, "Adding int", int_val + name = make_cstring('u_int') + call sina_add(name, int_val, units, tag) + print *, "Adding logical" + name = make_cstring('u_logical') + is_val = .true. + call sina_add(name, is_val, units, tag) + print *, "Adding long" + name = make_cstring('u_long') + call sina_add(name, long_val, units, tag) + print *, "Adding real" + name = make_cstring('u_real') + call sina_add(name, real_val, units, tag) + print *, "Adding double" + name = make_cstring('u_double') + call sina_add(name, double_val, units, tag) + + print *, "Adding double with tag" + name = make_cstring('u_double_w_tag') + tag = make_cstring('new_fancy_tag') + call sina_add(name, double_val, units, tag) + + deallocate(tag) + print *, "Adding char" + name = make_cstring('u_char') + call sina_add(name, trim(char_val)//char(0), units, tag) + + name = make_cstring('my_curveset') + call sina_add_curveset(name) + + curve = make_cstring('my_indep_curve_double') + independent = .TRUE. + call sina_add_curve(name, curve, double_arr, size(double_arr), independent) + curve = make_cstring('my_indep_curve_real') + call sina_add_curve(name, curve, real_arr, size(real_arr), independent) + curve = make_cstring('my_indep_curve_int') + call sina_add_curve(name, curve, int_arr, size(int_arr), independent) + curve = make_cstring('my_indep_curve_long') + call sina_add_curve(name, curve, long_arr, size(long_arr), independent) + curve = make_cstring('my_dep_curve_double') + independent = .false. + call sina_add_curve(name, curve, double_arr, size(double_arr), independent) + curve = make_cstring('my_dep_curve_double_2') + call sina_add_curve(name, curve, double_arr, size(double_arr), independent) + curve = make_cstring('my_dep_curve_real') + call sina_add_curve(name, curve, real_arr, size(real_arr), independent) + curve = make_cstring('my_dep_curve_int') + call sina_add_curve(name, curve, int_arr, size(int_arr), independent) + curve = make_cstring('my_dep_curve_long') + call sina_add_curve(name, curve, long_arr, size(long_arr), independent) + ! write out the Sina Document + print *,'Writing out the Sina Document' + call write_sina_document(json_fn) + + +contains + function make_cstring(string) result(cstring) + character(len=*), intent(in) :: string + character(len=:), allocatable :: cstring + cstring = trim(string) // char(0) + end function make_cstring + + +end program example diff --git a/src/axom/sina/interface/include/sina_fortran_interface.h b/src/axom/sina/interface/include/sina_fortran_interface.h new file mode 100644 index 0000000000..130b996b57 --- /dev/null +++ b/src/axom/sina/interface/include/sina_fortran_interface.h @@ -0,0 +1,24 @@ + +#include "axom/sina/include/Document.hpp" +#include "axom/sina/include/Record.hpp" +#include "axom/sina/include/Run.hpp" +#include "axom/sina.hpp" + +extern "C" char* Get_File_Extension(char *); +extern "C" void create_document_and_run_(char *); +extern "C" axom::sina::Record *Sina_Get_Run(); +extern "C" void sina_add_file_to_record_(char *); +extern "C" void sina_add_file_with_mimetype_to_record_(char *, char *); +extern "C" void write_sina_document_(char *); +extern "C" void sina_add_long_(char *, long long int *, char *, char *); +extern "C" void sina_add_int_(char *, int *, char *, char *); +extern "C" void sina_add_float_(char *, float *, char *, char *); +extern "C" void sina_add_double_(char *, double *, char *, char *); +extern "C" void sina_add_logical_(char *, bool *, char *, char *); +extern "C" void sina_add_string_(char *, char *, char *, char *); +extern "C" void sina_add_curveset_(char *); +extern "C" void sina_add_curve_double_(char *, char *, double *, int *, bool *); +extern "C" void sina_add_curve_float_(char *, char *, float *, int *, bool *); +extern "C" void sina_add_curve_int_(char *, char *, int *, int *, bool *); +extern "C" void sina_add_curve_long_(char *, char *, long long int *, int *, bool *); + diff --git a/src/axom/sina/interface/src/sina_fortran_interface.cpp b/src/axom/sina/interface/src/sina_fortran_interface.cpp new file mode 100644 index 0000000000..1d46f78006 --- /dev/null +++ b/src/axom/sina/interface/src/sina_fortran_interface.cpp @@ -0,0 +1,380 @@ +#include + +#include "axom/sina/interface/include/sina_fortran_interface.h" + + +axom::sina::Document *sina_document; + +extern "C" char* Get_File_Extension(char * input_fn) +{ + char* ext = strrchr(input_fn, '.'); + if (!ext) { return(new char[1]{'\0'}); } + return(ext + 1); +} + +extern "C" void create_document_and_record_(char * recID) +{ + sina_document = new axom::sina::Document; + // Create a record 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 id{recID, axom::sina::IDType::Global}; + std::unique_ptr myRecord{new axom::sina::Record{id, "my_type"}}; + sina_document->add(std::move(myRecord)); +} + +extern "C" axom::sina::Record *Sina_Get_Record() +{ + if(sina_document) + { + axom::sina::Document::RecordList const &allRecords = sina_document->getRecords(); + if(allRecords.size()) + { + std::unique_ptr const &myRecord = allRecords.front(); + return myRecord.get(); + } + } + return nullptr; +} + +extern "C" void sina_add_logical_(char * key, bool *value, char *units, char *tags) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) + { + axom::sina::Datum datum{static_cast(*value)}; + if (units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if (tags) + { + std::vector tagVector = { tags }; + if(tagVector.front() != "") + { + datum.setTags(tagVector); + } + } + sina_record->add(key_name, datum); + } + } +} + +extern "C" void sina_add_long_(char * key, long long int *value, char *units, char * tags) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) + { + axom::sina::Datum datum{static_cast(*value)}; + if (units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if (tags) + { + std::vector tagVector = { tags }; + if(tagVector.front() != "") + { + datum.setTags(tagVector); + } + } + sina_record->add(key_name, datum); + } + } +} + +extern "C" void sina_add_int_(char * key, int *value, char *units, char *tags) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) + { + axom::sina::Datum datum{static_cast(*value)}; + if (units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if (tags) + { + std::vector tagVector = { tags }; + if(tagVector.front() != "") + { + datum.setTags(tagVector); + } + } + sina_record->add(key_name, datum); + } + } +} + +extern "C" void sina_add_double_(char * key, double *value, char *units, char *tags) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) + { + axom::sina::Datum datum{*value}; + if (units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if (tags) + { + std::vector tagVector = { tags }; + if(tagVector.front() != "") + { + datum.setTags(tagVector); + } + } + sina_record->add(key_name, datum); + } + } +} + +extern "C" void sina_add_float_(char * key, float *value, char *units, char *tags) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) + { + axom::sina::Datum datum{*value}; + if (units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if (tags) + { + std::vector tagVector = { tags }; + if(tagVector.front() != "") + { + datum.setTags(tagVector); + } + } + sina_record->add(key_name, datum); + } + } +} + +extern "C" void sina_add_string_(char * key, char *value, char *units, char *tags) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + std::string key_value = std::string(value); + std::string key_units = std::string(units); + if(sina_record) + { + axom::sina::Datum datum{key_value}; + if (units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if (tags) + { + std::vector tagVector = { tags }; + if(tagVector.front() != "") + { + datum.setTags(tagVector); + } + } + sina_record->add(key_name, datum); + } + } +} + +extern "C" void sina_add_file_(char * filename, char *mime_type) +{ + std::string used_mime_type = ""; + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(mime_type) + { + used_mime_type = std::string(mime_type); + } + axom::sina::File my_file{filename}; + if(used_mime_type != "") + { + my_file.setMimeType(used_mime_type); + } + else + { + used_mime_type = Get_File_Extension(filename); + my_file.setMimeType(used_mime_type); + } + + if(sina_record) + { + sina_record->add(my_file); + } + } +} + +extern "C" void write_sina_document_(char * input_fn) +{ + std::string filename(input_fn); + // Save everything + if(sina_document) + { + axom::sina::saveDocument(*sina_document, filename.c_str()); + } +} + +extern "C" void sina_add_curveset_(char *name) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) + { + axom::sina::CurveSet cs{name}; + sina_record->add(cs); + } + } +} + +extern "C" void sina_add_curve_long_(char *curveset_name, char *curve_name, long long int *values, int *n, bool *independent) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) + { + double y[*n]; + for (int i=0; i<*n;i++){ + y[i] = values[i]; + } + axom::sina::Curve curve{curve_name, y, static_cast(*n)}; + + auto &curvesets = sina_record->getCurveSets(); + axom::sina::CurveSet cs=curvesets.at(curveset_name); + if (*independent) + { + cs.addIndependentCurve(curve); + } + else + { + cs.addDependentCurve(curve); + } + sina_record->add(cs); + } + } +} + +extern "C" void sina_add_curve_int_(char *curveset_name, char *curve_name, int *values, int *n, bool *independent) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) + { + double y[*n]; + for (int i=0; i<*n;i++){ + y[i] = values[i]; + } + axom::sina::Curve curve{curve_name, y, static_cast(*n)}; + + auto &curvesets = sina_record->getCurveSets(); + axom::sina::CurveSet cs=curvesets.at(curveset_name); + if (*independent) + { + cs.addIndependentCurve(curve); + } + else + { + cs.addDependentCurve(curve); + } + sina_record->add(cs); + } + } +} + +extern "C" void sina_add_curve_float_(char *curveset_name, char *curve_name, float *values, int *n, bool *independent) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) + { + double y[*n]; + for (int i=0; i<*n;i++){ + y[i] = values[i]; + } + axom::sina::Curve curve{curve_name, y, static_cast(*n)}; + + auto &curvesets = sina_record->getCurveSets(); + axom::sina::CurveSet cs=curvesets.at(curveset_name); + if (*independent) + { + cs.addIndependentCurve(curve); + } + else + { + cs.addDependentCurve(curve); + } + sina_record->add(cs); + } + } +} + +extern "C" void sina_add_curve_double_(char *curveset_name, char *curve_name, double *values, int *n, bool *independent) +{ + if (sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) + { + axom::sina::Curve curve{curve_name, values, static_cast(*n)}; + + auto &curvesets = sina_record->getCurveSets(); + axom::sina::CurveSet cs=curvesets.at(curveset_name); + if (*independent) + { + cs.addIndependentCurve(curve); + } + else + { + cs.addDependentCurve(curve); + } + sina_record->add(cs); + } + } +} + + diff --git a/src/axom/sina/interface/src/sina_fortran_interface.f90 b/src/axom/sina/interface/src/sina_fortran_interface.f90 new file mode 100644 index 0000000000..d674f26f3a --- /dev/null +++ b/src/axom/sina/interface/src/sina_fortran_interface.f90 @@ -0,0 +1,118 @@ +module sina_functions + + interface + + subroutine create_document_and_record(id) + character(*) id + end subroutine create_document_and_record + + end interface + + interface + + subroutine sina_add_file(file_nm, mime_type) + character(*) file_nm + character(*) mime_type + end subroutine sina_add_file + + end interface + + interface + + subroutine write_sina_document(file_nm) + character(*) file_nm + end subroutine write_sina_document + + end interface + + interface sina_add + + subroutine sina_add_long(key, value, units, tags) + character(*) key + integer (KIND=8) value + character(*) units + character(*) tags + end subroutine sina_add_long + + subroutine sina_add_logical(key, value, units, tags) + character(*) key + logical value + character(*) units + character(*) tags + end subroutine sina_add_logical + + subroutine sina_add_int(key, value, units, tags) + character(*) key + integer value + character(*) units + character(*) tags + end subroutine sina_add_int + + subroutine sina_add_double(key, value, units, tags) + character(*) key + double precision value + character(*) units + character(*) tags + end subroutine sina_add_double + + subroutine sina_add_float(key, value, units, tags) + character(*) key + real value + character(*) units + character(*) tags + end subroutine sina_add_float + + subroutine sina_add_string(key, value, units, tags) + character(*) key + character(*) value + character(*) units + character(*) tags + end subroutine sina_add_string + + end interface + + interface sina_add_curveset + + subroutine sina_add_curveset(name) + character(*) name + end subroutine sina_add_curveset + + end interface + + interface sina_add_curve + + subroutine sina_add_curve_double(name, curve, values, n, independent) + character(*) name + character(*) curve + double precision values(n) + integer n + logical independent + end subroutine sina_add_curve_double + + subroutine sina_add_curve_float(name, curve, values, n, independent) + character(*) name + character(*) curve + real values(n) + integer n + logical independent + end subroutine sina_add_curve_float + + subroutine sina_add_curve_int(name, curve, values, n, independent) + character(*) name + character(*) curve + integer (KIND=4), dimension(n) :: values + integer n + logical independent + end subroutine sina_add_curve_int + + subroutine sina_add_curve_long(name, curve, values, n, independent) + character(*) name + character(*) curve + integer (KIND=8), dimension(n) :: values + integer n + logical independent + end subroutine sina_add_curve_long + + end interface + +end module \ No newline at end of file diff --git a/src/axom/sina/interface/src/sina_schema.json b/src/axom/sina/interface/src/sina_schema.json new file mode 100644 index 0000000000..eff79e533b --- /dev/null +++ b/src/axom/sina/interface/src/sina_schema.json @@ -0,0 +1,255 @@ +{ + "$id" : "https://llnl.gov/sina.schema.json", + "$schema" : "http://json-schema.org/draft-07/schema#", + "title" : "Sina Schema", + "description" : "Sina schema for simulation data", + "type" : "object", + "definitions" : { + "userDefDict" : { + "description": "Dictionary of additional misc. values not belonging elsewhere.", + "type": "object" + }, + "libraryDataDict" : { + "description": "Dictionary of libraries and associated data", + "type": "object", + "additionalProperties": {"$ref": "#/definitions/libraryType" } + }, + "libraryType" : { + "description": "Library with associated data", + "type": "object", + "properties": { + "data": {"$ref": "#/definitions/dataDict" }, + "curve_sets": {"$ref": "#/definitions/curveSetDict" }, + "library_data": {"$ref": "#/definitions/libraryDataDict" } + } + }, + "fileDict" : { + "description": "Dictionary of files associated with Record", + "type": "object", + "additionalProperties": {"$ref": "#/definitions/fileType" } + }, + "fileType" : { + "description": "User-defined file values", + "type": "object", + "properties": { + "mimetype": { "type": "string" }, + "tags": { "$ref": "#/definitions/tagArray" } + } + }, + "stringDataArray" : { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": false + }, + "scalarDataArray" : { + "type": "array", + "items": { "type": "number" }, + "uniqueItems": false + }, + "objectType" : { + "description": "Object being acted upon by the subject record", + "oneOf": [ + { + "properties": { + "object": { + "description": "Global id of the object record", + "type": "string" + } + }, + "required": [ "object" ] + }, { + "properties": { + "local_object": { + "description": "Local id of the object record", + "type": "string" + } + }, + "required": [ "local_object" ] + } + ] + }, + "record" : { + "description": "A component of application execution", + "allOf": [ + { "$ref": "#/definitions/recordType" }, + { "$ref": "#/definitions/recordIdType" }, + { "$ref": "#/definitions/recordData" } + ] + }, + "recordData" : { + "description": "Optional, indexed simulation data", + "properties": { + "files": { "$ref": "#/definitions/fileDict" }, + "data": { "$ref": "#/definitions/dataDict" }, + "library_data": { "$ref": "#/definitions/libraryDataDict" }, + "curve_sets": { "$ref": "#/definitions/curveSetDict" }, + "user_defined": { "$ref": "#/definitions/userDefDict"} + } + }, + "recordIdType" : { + "oneOf": [ + { + "properties": { + "id": { + "description": "Unique identifier", + "type": "string" + } + }, + "required": [ "id" ] + }, { + "properties": { + "local_id": { + "description": "Unique, auto-assigned identifier", + "type": "string" + } + }, + "required": [ "local_id" ] + } + ] + }, + "recordType" : { + "properties": { + "type": { + "description": "The type of record", + "type": "string" + } + }, + "required": [ "type" ] + }, + "relationship" : { + "description": "Relationship between two records", + "allOf": [ + { "$ref": "#/definitions/subjectType" }, + { + "properties": { + "predicate": { "type": "string" } + }, + "required": [ "predicate" ] + }, + { "$ref": "#/definitions/objectType" } + ] + }, + "run" : { + "description": "An individual simulation run", + "allOf": [ + { "$ref": "#/definitions/recordIdType" }, + { + "properties": { + "type": { "enum": [ "run" ] }, + "user": { "type": "string" }, + "application": { "type": "string" }, + "version": { "type": "string" } + }, + "required": [ "type", "application" ] + }, + { "$ref": "#/definitions/recordData" } + ], + "additionalProperties": false + }, + "tagArray" : { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "subjectType" : { + "description": "Record acting on the object record", + "oneOf": [ + { + "properties": { + "subject": { + "description": "Global id of the subject record", + "type": "string" + } + }, + "required": [ "subject" ] + }, { + "properties": { + "local_subject": { + "description": "Local id of the subject record", + "type": "string" + } + }, + "required": [ "local_subject" ] + } + ] + }, + "dataDict" : { + "description": "Dictionary of data values", + "type": "object", + "additionalProperties": {"$ref": "#/definitions/dataType" } + }, + "curveSetDict" : { + "description": "Dictionary describing a set of curves", + "type": "object", + "additionalProperties": {"$ref": "#/definitions/curveSetType" } + }, + "dataType" : { + "description": "User-defined data values", + "type": "object", + "properties": { + "value": { + "oneOf": [ + { "type": "string" }, + { "type": "number" }, + { "$ref": "#/definitions/scalarDataArray" }, + { "$ref": "#/definitions/stringDataArray" } + ] + }, + "units": { "type": "string" }, + "tags": { "$ref": "#/definitions/tagArray" } + }, + "required" : [ "value" ] + }, + "curveSetType" : { + "description": "User-defined associations of curves", + "type": "object", + "properties": { + "independent": { + "type": "object", + "additionalProperties": { "$ref": "#/definitions/curveEntryType" } + }, + "dependent": { + "type": "object", + "additionalProperties": { "$ref": "#/definitions/curveEntryType" } + }, + "dependent_order": { "$ref": "#/definitions/stringDataArray"}, + "independent_order": { "$ref": "#/definitions/stringDataArray"}, + "tags": { "$ref": "#/definitions/tagArray" } + }, + "required" : [ "independent", "dependent" ] + }, + "curveEntryType" : { + "description": "Data for a single entry in a curve set", + "type": "object", + "properties": { + "value": { "$ref": "#/definitions/scalarDataArray" }, + "units": { "type": "string" }, + "tags": { "$ref": "#/definitions/tagArray" } + }, + "required" : [ "value" ] + } + }, + + "properties" : { + "records" : { + "description" : "Simulation metadata (e.g., runs, invocations)", + "type" : "array", + "minItems" : 1, + "items": { + "oneOf": [ + { "$ref": "#/definitions/record" }, + { "$ref": "#/definitions/run" } + ] + }, + "uniqueItems" : true + }, + "relationships" : { + "description" : "Associations between records", + "type" : "array", + "minItems" : 0, + "items": { "$ref": "#/definitions/relationship" }, + "uniqueItems" : true + } + }, + "required": [ "records" ] +} diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index ffb71417da..b241c93b1c 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -65,4 +65,15 @@ foreach(test ${gtest_sina_tests}) axom_add_test( NAME ${test_name} COMMAND ${test_name}_test ) -endforeach() \ No newline at end of file +endforeach() + +#------------------------------------------------------------------------------ +# Add fortran integration test +#------------------------------------------------------------------------------ +if (ENABLE_FORTRAN) + configure_file(${CMAKE_SOURCE_DIR}/axom/sina/tests/test_fortran_integration.py ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py COPYONLY) + configure_file(${CMAKE_SOURCE_DIR}/axom/sina/interface/src/sina_schema.json ${TEST_OUTPUT_DIRECTORY}/sina_schema.json COPYONLY) + + axom_add_test( NAME sina_fortran_integration_test + COMMAND ${PYTHON_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR}) +endif() diff --git a/src/axom/sina/tests/test_fortran_integration.py b/src/axom/sina/tests/test_fortran_integration.py new file mode 100644 index 0000000000..525f92ef62 --- /dev/null +++ b/src/axom/sina/tests/test_fortran_integration.py @@ -0,0 +1,125 @@ +import argparse +import io +import json +import os +import subprocess +import unittest + +def parse_args(): + """Helper function to obtain the binary directory path of Axom from CLI""" + parser = argparse.ArgumentParser(description="Unit test arguments") + parser.add_argument("-bd", "--binary-dir", type=str, + help="Path to the binary directory for Axom") + # Add other arguments as needed + return parser.parse_args() + + +class TestFortranExampleIntegration(unittest.TestCase): + + @classmethod + def setUpClass(cls): + """ + Obtain the binary directory from the CLI and compile the sina fortran + example needed for these tests if necessary. + """ + cwd = os.getcwd() + + args = parse_args() + cls.binary_dir = args.binary_dir + if cls.binary_dir is None: + # Assume we're at /path/to/build_dir/axom/sina/tests so move up to build_dir + cls.binary_dir = f"{cwd}/../../../" + + os.chdir(cls.binary_dir) + + if not os.path.exists(f"{cls.binary_dir}/examples/sina_fortran_ex"): + subprocess.run(["make", "sina_fortran_ex"]) + + os.chdir(cwd) + + + def setUp(self): + """ Invoke example Fortran application to dump a sina file """ + subprocess.run([f"{self.binary_dir}/examples/sina_fortran_ex"]) + self.dump_file = "sina_dump.json" + + def tearDown(self): + """ Clean up output directory after each test. """ + os.remove(self.dump_file) + + def test_file_validity(self): + """ Make sure the files we"re importing follow the Sina schema. """ + try: + import jsonschema + schema_file = os.path.join(f"{self.binary_dir}/tests/sina_schema.json") + with io.open(schema_file, "r", encoding="utf-8") as schema: + schema = json.load(schema) + with io.open(self.dump_file, "r", encoding="utf-8") as loaded_test: + file_json = json.load(loaded_test) + jsonschema.validate(file_json, schema) + except ModuleNotFoundError: + print("jsonschema module not found. Skipping test_file_validity.") + pass + + + def test_validate_contents_of_record(self): + """ Ensure that the record written out matches what we expect """ + with open(self.dump_file, "r", encoding="utf-8") as loaded_test: + rec = json.load(loaded_test) + + record = rec["records"][0] + + # Test the metadata in the record + self.assertEqual("my_rec_id", record["id"]) + self.assertEqual("my_type", record["type"]) + + # Test the files + self.assertEqual(list(record["files"].keys()), ["/path/to/my/file/my_other_file.txt", "/path/to/my/file/my_file.txt"]) + self.assertEqual(record["files"]["/path/to/my/file/my_other_file.txt"]["mimetype"], "png") + self.assertEqual(record["files"]["/path/to/my/file/my_file.txt"]["mimetype"], "txt") + + # Test the signed variants + self.assertEqual("A", record["data"]["char"]["value"]) + self.assertEqual(10, record["data"]["int"]["value"]) + self.assertEqual(0, record["data"]["logical"]["value"]) + self.assertEqual(1000000000.0, record["data"]["long"]["value"]) + self.assertEqual(1.23456704616547, record["data"]["real"]["value"]) + self.assertEqual(0.810000002384186, record["data"]["double"]["value"]) + + # Test the unsigned variants + self.assertEqual("A", record["data"]["u_char"]["value"]) + self.assertEqual("kg", record["data"]["u_char"]["units"]) + self.assertEqual(10, record["data"]["u_int"]["value"]) + self.assertEqual("kg", record["data"]["u_int"]["units"]) + self.assertEqual(1.0, record["data"]["u_logical"]["value"]) + self.assertEqual("kg", record["data"]["u_logical"]["units"]) + self.assertEqual(1000000000.0, record["data"]["u_long"]["value"]) + self.assertEqual("kg", record["data"]["u_long"]["units"]) + self.assertEqual(1.23456704616547, record["data"]["u_real"]["value"]) + self.assertEqual("kg", record["data"]["u_real"]["units"]) + self.assertEqual(0.810000002384186, record["data"]["u_double"]["value"]) + self.assertEqual("kg", record["data"]["u_double"]["units"]) + self.assertEqual(0.810000002384186, record["data"]["u_double_w_tag"]["value"]) + self.assertEqual("kg", record["data"]["u_double_w_tag"]["units"]) + self.assertEqual(["new_fancy_tag"], record["data"]["u_double_w_tag"]["tags"]) + + # Test the curves + nums = range(1, 21) + real_arr = [i for i in nums] + double_arr = [i*2 for i in nums] + int_arr = [i*3 for i in nums] + long_arr = [i*4 for i in nums] + curveset = "my_curveset" + for kind, loc in (("indep", "independent"), ("dep", "dependent")): + for val_type, target in (("real", real_arr), ("double", double_arr), ("int", int_arr), ("long", long_arr)): + name = "my_{}_curve_{}".format(kind, val_type) + self.assertEqual(target, record["curve_sets"][curveset][loc][name]["value"]) + double_2_name = "my_dep_curve_double_2" + self.assertEqual(double_arr, record["curve_sets"][curveset]["dependent"][double_2_name]["value"]) + + +if __name__ == "__main__": + # Doing the below instead of unittest.main() so that we can print to stdout + suite = unittest.TestLoader().loadTestsFromTestCase(TestFortranExampleIntegration) + runner = unittest.TextTestRunner(buffer=False) + runner.run(suite) \ No newline at end of file From b23292f809d93a37aed7f0f40521fb1f16332e30 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 9 Jul 2024 14:16:07 -0700 Subject: [PATCH 14/60] fix small issues with docs --- src/axom/sina/docs/sphinx/curve_sets.rst | 2 +- src/axom/sina/docs/sphinx/documents.rst | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/axom/sina/docs/sphinx/curve_sets.rst b/src/axom/sina/docs/sphinx/curve_sets.rst index ec01e6fa0c..d43f6cd0e6 100644 --- a/src/axom/sina/docs/sphinx/curve_sets.rst +++ b/src/axom/sina/docs/sphinx/curve_sets.rst @@ -26,7 +26,7 @@ another function to assemble the ``CurveSet`` object and add it to our ``Record` :language: cpp Once this code is compiled and executed a json file will be created. Here, we'll -use `PyDV `_ to injest the Sina json +use `PyDV `_ to ingest the Sina json file in order to view the position and velocity of the ball along the y-axis over time. diff --git a/src/axom/sina/docs/sphinx/documents.rst b/src/axom/sina/docs/sphinx/documents.rst index fd43fa0b3c..b704449109 100644 --- a/src/axom/sina/docs/sphinx/documents.rst +++ b/src/axom/sina/docs/sphinx/documents.rst @@ -14,7 +14,7 @@ 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 +``records`` and ``relationships``. Each of these entries will store a list of their respective objects. An example of an empty document is shown below: .. code:: json @@ -24,8 +24,8 @@ respective objects. An example of an empty document is shown below: "relationships": [] } -The "records" list can contain ``Record`` objects and their inheritying types, -such as ``Run`` objects. The "relationships" list can contain ``Relationship`` +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 <./records>`_ and `Relationships <./relationships>`_. @@ -72,7 +72,7 @@ process. Below we will expand on this example to add a ``Relationship``: document.add(relationship); // Save the document directly to a file. - saveDocument(document, "MySinaData.json"); + axom::sina::saveDocument(document, "MySinaData.json"); } After executing the above code, the resulting ``MySinaData.json`` file will From d9de3ef7f2a7fef3a808c0187665dcc006f4416f Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 9 Jul 2024 14:16:49 -0700 Subject: [PATCH 15/60] make ordering alphabetical for cmakedefines --- src/axom/config.hpp.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/config.hpp.in b/src/axom/config.hpp.in index 47fe53ea5d..4b176a34bb 100644 --- a/src/axom/config.hpp.in +++ b/src/axom/config.hpp.in @@ -108,10 +108,10 @@ #cmakedefine AXOM_USE_PRIMAL #cmakedefine AXOM_USE_QUEST #cmakedefine AXOM_USE_SIDRE +#cmakedefine AXOM_USE_SINA #cmakedefine AXOM_USE_SLAM #cmakedefine AXOM_USE_SLIC #cmakedefine AXOM_USE_SPIN -#cmakedefine AXOM_USE_SINA /* * For gradual removal of types that have been deprecated in favor of From 7bf7fc80a55dfc3d6fcaf78546a57029ab420577 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 9 Jul 2024 14:17:38 -0700 Subject: [PATCH 16/60] fix indentation on doxygen page --- src/axom/doxygen_mainpage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/doxygen_mainpage.md b/src/axom/doxygen_mainpage.md index 0b53c447d5..90711ec5ec 100644 --- a/src/axom/doxygen_mainpage.md +++ b/src/axom/doxygen_mainpage.md @@ -30,7 +30,7 @@ Dependencies between components are as follows: - Quest depends on Slam, Primal, Spin, and Mint - Klee depends on Sidre, Inlet and Primal - Multimat depends on Slic, and Slam -- Sina only depends on Core + - Sina only depends on Core The figure below summarizes the dependencies between the modules. Solid links indicate hard dependencies; dashed links indicate optional dependencies. From f15a5014278cbdf28ebbaf9d7f0365a528e03de2 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 9 Jul 2024 16:48:13 -0700 Subject: [PATCH 17/60] protect against GMOCK not available --- src/axom/sina/tests/CMakeLists.txt | 112 ++++++++++++++++++----------- 1 file changed, 71 insertions(+), 41 deletions(-) diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index b241c93b1c..eaf4ad6a56 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -6,59 +6,23 @@ # Sina unit tests #------------------------------------------------------------------------------ + #------------------------------------------------------------------------------ -# Specify list of tests +# Add gtest C++ tests #------------------------------------------------------------------------------ - set(gtest_sina_tests - src/sina_ConduitUtil.cpp src/sina_CppBridge.cpp - src/sina_Curve.cpp - src/sina_CurveSet.cpp - src/sina_DataHolder.cpp - src/sina_Datum.cpp - src/sina_Document.cpp src/sina_File.cpp - src/sina_ID.cpp - src/sina_Record.cpp - src/sina_Relationship.cpp - src/sina_Run.cpp ) -# Define Sina test utility sources and headers -set(sina_test_utils_sources - src/ConduitTestUtils.cpp - src/TestRecord.cpp -) -set(sina_test_utils_headers - include/ConduitTestUtils.hpp - include/TestRecord.hpp -) - -# Define Sina test dependencies -set(sina_gtests_depends_on sina gtest gmock conduit::conduit) - -# Add tests using Adiak if necessary and Adiak dependency -blt_list_append( TO gtest_sina_tests ELEMENTS src/sina_AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK) -blt_list_append( TO sina_gtests_depends_on ELEMENTS adiak::adiak IF AXOM_SINA_USE_ADIAK ) - -# Create a library for the test utilities so they can be used -axom_add_library( - NAME sina_test_utils - SOURCES ${sina_test_utils_sources} - HEADERS ${sina_test_utils_headers} - DEPENDS_ON ${sina_gtests_depends_on} - FOLDER axom/sina/tests) +set(sina_gtests_depends_on gtest sina conduit::conduit) -#------------------------------------------------------------------------------ -# Add gtest C++ tests -#------------------------------------------------------------------------------ foreach(test ${gtest_sina_tests}) get_filename_component( test_name ${test} NAME_WE ) axom_add_executable(NAME ${test_name}_test SOURCES ${test} OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} - DEPENDS_ON sina_test_utils + DEPENDS_ON ${sina_gtests_depends_on} FOLDER axom/sina/tests ) @@ -67,13 +31,79 @@ foreach(test ${gtest_sina_tests}) ) endforeach() + +if (ENABLE_GMOCK) + + #------------------------------------------------------------------------------ + # Create test utilities library for Sina + #------------------------------------------------------------------------------ + + # Define Sina test utility sources and headers + set(sina_test_utils_sources + src/ConduitTestUtils.cpp + src/TestRecord.cpp + ) + set(sina_test_utils_headers + include/ConduitTestUtils.hpp + include/TestRecord.hpp + ) + + set(sina_test_utils_depends_on gmock ${sina_gtests_depends_on}) + + # Create a library for the test utilities so they can be used + axom_add_library( + NAME sina_test_utils + SOURCES ${sina_test_utils_sources} + HEADERS ${sina_test_utils_headers} + DEPENDS_ON ${sina_test_utils_depends_on} + FOLDER axom/sina/tests) + + #------------------------------------------------------------------------------ + # Add gmock C++ tests + #------------------------------------------------------------------------------ + + set(gmock_sina_tests + src/sina_ConduitUtil.cpp + src/sina_Curve.cpp + src/sina_CurveSet.cpp + src/sina_DataHolder.cpp + src/sina_Datum.cpp + src/sina_Document.cpp + src/sina_ID.cpp + src/sina_Record.cpp + src/sina_Relationship.cpp + src/sina_Run.cpp + ) + + # set(sina_gmock_depends_on ${sina_gtests_depends_on} gmock) + + # Add tests using Adiak if necessary and Adiak dependency + blt_list_append( TO gmock_sina_tests ELEMENTS src/sina_AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK) + blt_list_append( TO sina_gmock_depends_on ELEMENTS adiak::adiak IF AXOM_SINA_USE_ADIAK ) + + foreach(test ${gmock_sina_tests}) + get_filename_component( test_name ${test} NAME_WE ) + axom_add_executable(NAME ${test_name}_test + SOURCES ${test} + OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} + DEPENDS_ON sina_test_utils + FOLDER axom/sina/tests + ) + + axom_add_test( NAME ${test_name} + COMMAND ${test_name}_test + ) + endforeach() +endif() + #------------------------------------------------------------------------------ # Add fortran integration test #------------------------------------------------------------------------------ if (ENABLE_FORTRAN) + find_package(PythonInterp REQUIRED) configure_file(${CMAKE_SOURCE_DIR}/axom/sina/tests/test_fortran_integration.py ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py COPYONLY) configure_file(${CMAKE_SOURCE_DIR}/axom/sina/interface/src/sina_schema.json ${TEST_OUTPUT_DIRECTORY}/sina_schema.json COPYONLY) axom_add_test( NAME sina_fortran_integration_test - COMMAND ${PYTHON_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR}) + COMMAND ${PYTHON_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR}) endif() From 46d0fcd68e3bb2695fbee51423272c78c96aa117 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 10 Jul 2024 12:52:43 -0700 Subject: [PATCH 18/60] move document examples to actual examples --- src/axom/sina/docs/sphinx/documents.rst | 72 +----- src/axom/sina/docs/sphinx/records.rst | 223 ++---------------- src/axom/sina/docs/sphinx/relationships.rst | 39 +-- src/axom/sina/examples/CMakeLists.txt | 12 + src/axom/sina/examples/sina_basic.cpp | 2 +- .../sina/examples/sina_check_datum_type.cpp | 19 ++ src/axom/sina/examples/sina_create_datum.cpp | 15 ++ .../sina/examples/sina_document_assembly.cpp | 36 +++ .../examples/sina_file_object_creation.cpp | 19 ++ .../examples/sina_file_object_removal.cpp | 22 ++ .../examples/sina_local_id_relationship.cpp | 16 ++ .../examples/sina_query_record_for_files.cpp | 27 +++ .../sina_query_records_relationships.cpp | 40 ++++ .../examples/sina_relationship_assembly.cpp | 11 + .../examples/sina_set_datum_units_tags.cpp | 13 + .../sina/examples/sina_view_datum_types.cpp | 27 +++ .../sina/examples/sina_view_datum_values.cpp | 31 +++ 17 files changed, 325 insertions(+), 299 deletions(-) create mode 100644 src/axom/sina/examples/sina_check_datum_type.cpp create mode 100644 src/axom/sina/examples/sina_create_datum.cpp create mode 100644 src/axom/sina/examples/sina_document_assembly.cpp create mode 100644 src/axom/sina/examples/sina_file_object_creation.cpp create mode 100644 src/axom/sina/examples/sina_file_object_removal.cpp create mode 100644 src/axom/sina/examples/sina_local_id_relationship.cpp create mode 100644 src/axom/sina/examples/sina_query_record_for_files.cpp create mode 100644 src/axom/sina/examples/sina_query_records_relationships.cpp create mode 100644 src/axom/sina/examples/sina_relationship_assembly.cpp create mode 100644 src/axom/sina/examples/sina_set_datum_units_tags.cpp create mode 100644 src/axom/sina/examples/sina_view_datum_types.cpp create mode 100644 src/axom/sina/examples/sina_view_datum_values.cpp diff --git a/src/axom/sina/docs/sphinx/documents.rst b/src/axom/sina/docs/sphinx/documents.rst index b704449109..8cdc6ebfb6 100644 --- a/src/axom/sina/docs/sphinx/documents.rst +++ b/src/axom/sina/docs/sphinx/documents.rst @@ -42,38 +42,8 @@ Assembling Documents On the `Sina C++ User Guide <./index>`_ page, you can see an example of this process. Below we will expand on this example to add a ``Relationship``: -.. code:: cpp - - #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 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 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. - axom::sina::saveDocument(document, "MySinaData.json"); - } +.. literalinclude:: ../../examples/sina_document_assembly.cpp + :language: cpp After executing the above code, the resulting ``MySinaData.json`` file will look like so: @@ -146,42 +116,8 @@ objects that are associated with a ``Document``. The ``getRecords()`` and Below is an example showcasing their usage: -.. code:: cpp - - #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 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 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(); - - std::cout << "Number of Records: " << records.size() << std::endl; - std::cout << "Number of Relationships: " << relationships.size() << std::endl; - } +.. literalinclude:: ../../examples/sina_query_records_relationships.cpp + :language: cpp Running this will show that both records and the one relationship were properly queried: diff --git a/src/axom/sina/docs/sphinx/records.rst b/src/axom/sina/docs/sphinx/records.rst index 96b097708f..0f7bf0b489 100644 --- a/src/axom/sina/docs/sphinx/records.rst +++ b/src/axom/sina/docs/sphinx/records.rst @@ -46,39 +46,26 @@ of doubles. Below showcases an example of creating an instance of ``Datum`` with an array of strings and adding it to a ``Record``: -.. code:: cpp - - #include "axom/sina.hpp" - - int main(void) { - // Create the record - axom::sina::ID myID{"my_record", axom::sina::IDType::Local}; - std::unique_ptr myRecord{new axom::sina::Record{myID, "my_type"}}; - - // Create the datum with an array of strings - std::vector myTags{"input"}; - axom::sina::Datum myDatum{12, myTags}; - - // Add the datum to the record - myRecord->add("my_scalar", std::move(myDatum)); - std::cout << myRecord->toNode().to_json() << std::endl; - } +.. literalinclude:: ../../examples/sina_create_datum.cpp + :language: cpp Once executed, this code will output: .. code:: json { - "local_id": "my_record", - "type": "my_type", - "data": + "data": { - "my_scalar": + "my_scalar": { - "tags": ["input"], - "value":12.0 + "value": + [ + "input" + ] } - } + }, + "type": "my_type", + "local_id": "my_record" } .. _datum-type-label: @@ -97,27 +84,8 @@ are tracked in an enumeration called ``ValueType``. The enumeration is as follow Below is an example of this in action: -.. code:: cpp - - #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 scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum{scalars}; - - // Prints 0, corresponding to string - std::cout << static_cast::type>(myDatum.getType()) << std::endl; - - // Prints 1, corresponding to scalar - std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; - - // Prints 3, corresponding to scalar array - std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; - } +.. literalinclude:: ../../examples/sina_check_datum_type.cpp + :language: cpp ++++++++++++++++++++++ Setting Units and Tags @@ -128,21 +96,8 @@ This can be accomplished with the ``setUnits()`` and ``setTags()`` methods respe Below is an example of this functionality: -.. code:: cpp - - #include "axom/sina.hpp" - - int main(void) { - // Define 2 different datums - axom::sina::Datum myDatum{12.34}; - std::vector scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum{scalars}; - - // Set the units for one datum and the tags for the other - myDatum.setUnits("km/s"); - std::vector tags = {"input", "core"}; - myArrayDatum.setTags(tags); - } +.. literalinclude:: ../../examples/sina_datum_units_tags.cpp + :language: cpp +++++++++++++++++++++++++++++++++++++ Viewing Datum From an existing Record @@ -154,35 +109,8 @@ This method will return an unordered map of ``Datum`` instances. Below is an example of this process: -.. code:: cpp - - #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 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 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 keys and type of datum - for (const auto& pair : data) { - std::cout << pair.first << " is type: " << static_cast::type>(pair.second.getType()) << std::endl; - } - } +.. literalinclude:: ../../examples/sina_view_datum_types.cpp + :language: cpp Executing this code will print out: @@ -197,39 +125,8 @@ 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: -.. code:: cpp - - #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 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 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 - std::cout << "datum1: " << data.at("datum1").getScalar() << std::endl; - std::cout << "datum2: " << data.at("datum2").getValue() << std::endl; - std::cout << "datum3: "; - for (const auto& value : data.at("datum3").getScalarArray()) { - std::cout << value << " "; - } - std::cout << std::endl; - } +.. literalinclude:: ../../examples/sina_view_datum_values.cpp + :language: cpp This will provide the following output: @@ -251,27 +148,8 @@ 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: -.. code:: cpp - - #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 myRecord{new axom::sina::Record{myID, "my_type"}}; - - // Add the files to the record - myRecord->add(myFile); - myRecord->add(myOtherFile); - - std::cout << myRecord->toNode().to_json() << std::endl; - } +.. literalinclude:: ../../examples/sina_file_object_creation.cpp + :language: cpp This code will produce the following output: @@ -282,7 +160,7 @@ This code will produce the following output: "local_id": "my_record", "files": { - "/path/to/other/file.txt": + "/path/to/other/file.txt": { "tags": [ @@ -300,30 +178,8 @@ This code will produce the following output: Similarly, files can be removed from a ``Record`` with the ``remove()`` method: -.. code:: cpp - - #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 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); - - std::cout << std::cout << myRecord->toNode().to_json() << std::endl; - } +.. literalinclude:: ../../examples/sina_file_object_removal.cpp + :language: cpp As we see from the output, the contents of ``myFile`` are no longer in the ``Record`` instance: @@ -358,35 +214,8 @@ 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: -.. code:: cpp - - #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 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; - } - } +.. literalinclude:: ../../examples/sina_query_record_for_files.cpp + :language: cpp The above code will output: diff --git a/src/axom/sina/docs/sphinx/relationships.rst b/src/axom/sina/docs/sphinx/relationships.rst index c4b3e61712..ec2b76ff4d 100644 --- a/src/axom/sina/docs/sphinx/relationships.rst +++ b/src/axom/sina/docs/sphinx/relationships.rst @@ -33,28 +33,17 @@ Instead, this should be phrased as "Carlos emails Dani". Below is an example showcasing how to construct a ``Relationship`` programmatically. Here, we assemble a ``Relationship`` showing that "Task_22 contains Run_1024": -.. code:: cpp - - #include "axom/sina.hpp" - - int main(void) { - // Create IDs for both Task 22 and Run 1024 - axom::sina::ID task22{"Task_22", sina::IDType::Global}; - axom::sina::ID run1024{"Run_1024", sina::IDType::Global}; - - // Create the relationship and print it out - axom::sina::Relationship myRelationship{task22, "contains", run1024}; - std::cout << myRelationship.toNode().to_json() << std::endl; - } +.. literalinclude:: ../../examples/sina_relationship_assembly.cpp + :language: cpp If executed, the above code will output: .. code:: json { - "object": "Run_1024", "predicate": "contains", - "subject": "Task_22" + "subject": "Task_22", + "object": "Run_1024" } As with any other Sina ID, the subject or object may be either local (uniquely refer to @@ -65,24 +54,8 @@ same global ID. Let's add on to our previous example to demonstrate this: -.. code:: cpp - - #include "axom/sina.hpp" - - int main(void) { - // Create IDs for Task 22 and Run 1024 - axom::sina::ID task22{"Task_22", sina::IDType::Global}; - axom::sina::ID run1024{"Run_1024", sina::IDType::Global}; - - // Create the relationship and print it out - axom::sina::Relationship myRelationship{task22, "contains", run1024}; - std::cout << myRelationship.toNode().to_json() << std::endl; - - // Create a new ID with local scope and use it to create a Record and Relationship - axom::sina::ID myLocalID{"my_local_run", axom::sina::IDType::Local}; - std::unique_ptr myRun{new axom::sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; - axom::sina::Relationship myRelationship{task22, "containts", myLocalID}; - } +.. literalinclude:: ../../examples/sina_local_id_relationship.cpp + :language: cpp In the above code, the "my_local_run" ID would be replaced by a global ID on ingestion. If this new global ID was, for example, "5Aed-BCds-23G1", then "my_local_run" would diff --git a/src/axom/sina/examples/CMakeLists.txt b/src/axom/sina/examples/CMakeLists.txt index 882c2456bc..e201064f4b 100644 --- a/src/axom/sina/examples/CMakeLists.txt +++ b/src/axom/sina/examples/CMakeLists.txt @@ -11,8 +11,20 @@ #------------------------------------------------------------------------------ set(sina_example_sources sina_basic.cpp + sina_check_datum_type.cpp + sina_create_datum.cpp sina_curve_set.cpp + sina_document_assembly.cpp + sina_file_object_creation.cpp + sina_file_object_removal.cpp + sina_local_id_relationship.cpp + sina_query_record_for_files.cpp + sina_query_records_relationships.cpp + sina_relationship_assembly.cpp + sina_set_datum_units_tags.cpp sina_tutorial.cpp + sina_view_datum_types.cpp + sina_view_datum_values.cpp ) set(sina_example_depends sina conduit::conduit) diff --git a/src/axom/sina/examples/sina_basic.cpp b/src/axom/sina/examples/sina_basic.cpp index b69d69b546..34424e8e44 100644 --- a/src/axom/sina/examples/sina_basic.cpp +++ b/src/axom/sina/examples/sina_basic.cpp @@ -11,5 +11,5 @@ int main (void) { // Add the run to the document document.add(std::move(run)); // Save the document directly to a file. - saveDocument(document, "MySinaData.json"); + axom::sina::saveDocument(document, "MySinaData.json"); } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_check_datum_type.cpp b/src/axom/sina/examples/sina_check_datum_type.cpp new file mode 100644 index 0000000000..68c099ff44 --- /dev/null +++ b/src/axom/sina/examples/sina_check_datum_type.cpp @@ -0,0 +1,19 @@ +#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 scalars = {1, 2, 20.0}; + axom::sina::Datum myArrayDatum{scalars}; + + // Prints 0, corresponding to string + std::cout << static_cast::type>(myDatum.getType()) << std::endl; + + // Prints 1, corresponding to scalar + std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; + + // Prints 3, corresponding to scalar array + std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_create_datum.cpp b/src/axom/sina/examples/sina_create_datum.cpp new file mode 100644 index 0000000000..fbb1192ac3 --- /dev/null +++ b/src/axom/sina/examples/sina_create_datum.cpp @@ -0,0 +1,15 @@ +#include "axom/sina.hpp" + +int main(void) { + // Create the record + axom::sina::ID myID{"my_record", axom::sina::IDType::Local}; + std::unique_ptr myRecord{new axom::sina::Record{myID, "my_type"}}; + + // Create the datum with an array of strings + std::vector myTags{"input"}; + axom::sina::Datum myDatum{myTags}; + + // Add the datum to the record + myRecord->add("my_scalar", std::move(myDatum)); + std::cout << myRecord->toNode().to_json() << std::endl; +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_document_assembly.cpp b/src/axom/sina/examples/sina_document_assembly.cpp new file mode 100644 index 0000000000..e69744b78a --- /dev/null +++ b/src/axom/sina/examples/sina_document_assembly.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2017-2024, 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 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 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 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. + axom::sina::saveDocument(document, "MySinaData.json"); +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_file_object_creation.cpp b/src/axom/sina/examples/sina_file_object_creation.cpp new file mode 100644 index 0000000000..6a4093b95c --- /dev/null +++ b/src/axom/sina/examples/sina_file_object_creation.cpp @@ -0,0 +1,19 @@ +#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 myRecord{new axom::sina::Record{myID, "my_type"}}; + + // Add the files to the record + myRecord->add(myFile); + myRecord->add(myOtherFile); + + std::cout << myRecord->toNode().to_json() << std::endl; +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_file_object_removal.cpp b/src/axom/sina/examples/sina_file_object_removal.cpp new file mode 100644 index 0000000000..7b8f71e540 --- /dev/null +++ b/src/axom/sina/examples/sina_file_object_removal.cpp @@ -0,0 +1,22 @@ +#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 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); + + std::cout << myRecord->toNode().to_json() << std::endl; +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_local_id_relationship.cpp b/src/axom/sina/examples/sina_local_id_relationship.cpp new file mode 100644 index 0000000000..0041744ef7 --- /dev/null +++ b/src/axom/sina/examples/sina_local_id_relationship.cpp @@ -0,0 +1,16 @@ +#include "axom/sina.hpp" + +int main(void) { + // Create IDs for Task 22 and Run 1024 + axom::sina::ID task22{"Task_22", axom::sina::IDType::Global}; + axom::sina::ID run1024{"Run_1024", axom::sina::IDType::Global}; + + // Create the relationship and print it out + axom::sina::Relationship myRelationship{task22, "contains", run1024}; + std::cout << myRelationship.toNode().to_json() << std::endl; + + // Create a new ID with local scope and use it to create a Record and Relationship + axom::sina::ID myLocalID{"my_local_run", axom::sina::IDType::Local}; + std::unique_ptr myRun{new axom::sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; + axom::sina::Relationship myLocalRelationship{task22, "containts", myLocalID}; +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_query_record_for_files.cpp b/src/axom/sina/examples/sina_query_record_for_files.cpp new file mode 100644 index 0000000000..2c83bbcada --- /dev/null +++ b/src/axom/sina/examples/sina_query_record_for_files.cpp @@ -0,0 +1,27 @@ +#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 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; + } +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_query_records_relationships.cpp b/src/axom/sina/examples/sina_query_records_relationships.cpp new file mode 100644 index 0000000000..ef9cd52382 --- /dev/null +++ b/src/axom/sina/examples/sina_query_records_relationships.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2017-2024, 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 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 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 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(); + + std::cout << "Number of Records: " << records.size() << std::endl; + std::cout << "Number of Relationships: " << relationships.size() << std::endl; +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_relationship_assembly.cpp b/src/axom/sina/examples/sina_relationship_assembly.cpp new file mode 100644 index 0000000000..d96220d7bd --- /dev/null +++ b/src/axom/sina/examples/sina_relationship_assembly.cpp @@ -0,0 +1,11 @@ +#include "axom/sina.hpp" + +int main(void) { + // Create IDs for both Task 22 and Run 1024 + axom::sina::ID task22{"Task_22", axom::sina::IDType::Global}; + axom::sina::ID run1024{"Run_1024", axom::sina::IDType::Global}; + + // Create the relationship and print it out + axom::sina::Relationship myRelationship{task22, "contains", run1024}; + std::cout << myRelationship.toNode().to_json() << std::endl; +} diff --git a/src/axom/sina/examples/sina_set_datum_units_tags.cpp b/src/axom/sina/examples/sina_set_datum_units_tags.cpp new file mode 100644 index 0000000000..deb0f625ba --- /dev/null +++ b/src/axom/sina/examples/sina_set_datum_units_tags.cpp @@ -0,0 +1,13 @@ +#include "axom/sina.hpp" + +int main(void) { + // Define 2 different datums + axom::sina::Datum myDatum{12.34}; + std::vector scalars = {1, 2, 20.0}; + axom::sina::Datum myArrayDatum{scalars}; + + // Set the units for one datum and the tags for the other + myDatum.setUnits("km/s"); + std::vector tags = {"input", "core"}; + myArrayDatum.setTags(tags); +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_view_datum_types.cpp b/src/axom/sina/examples/sina_view_datum_types.cpp new file mode 100644 index 0000000000..17c823b611 --- /dev/null +++ b/src/axom/sina/examples/sina_view_datum_types.cpp @@ -0,0 +1,27 @@ +#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 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 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::type>(pair.second.getType()) << std::endl; + } +} \ No newline at end of file diff --git a/src/axom/sina/examples/sina_view_datum_values.cpp b/src/axom/sina/examples/sina_view_datum_values.cpp new file mode 100644 index 0000000000..e125c75402 --- /dev/null +++ b/src/axom/sina/examples/sina_view_datum_values.cpp @@ -0,0 +1,31 @@ +#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 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 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 + std::cout << "datum1: " << data.at("datum1").getScalar() << std::endl; + std::cout << "datum2: " << data.at("datum2").getValue() << std::endl; + std::cout << "datum3: "; + for (const auto& value : data.at("datum3").getScalarArray()) { + std::cout << value << " "; + } + std::cout << std::endl; +} \ No newline at end of file From 5c181c7d2601b5d92f2cea3a6163f437a0fb3b1d Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 10 Jul 2024 13:32:35 -0700 Subject: [PATCH 19/60] add copyright header to sina files --- src/axom/sina/examples/sina_basic.cpp | 6 ++++++ src/axom/sina/examples/sina_check_datum_type.cpp | 6 ++++++ src/axom/sina/examples/sina_create_datum.cpp | 6 ++++++ src/axom/sina/examples/sina_curve_set.cpp | 6 ++++++ src/axom/sina/examples/sina_file_object_creation.cpp | 6 ++++++ src/axom/sina/examples/sina_file_object_removal.cpp | 6 ++++++ src/axom/sina/examples/sina_local_id_relationship.cpp | 6 ++++++ src/axom/sina/examples/sina_query_record_for_files.cpp | 6 ++++++ src/axom/sina/examples/sina_relationship_assembly.cpp | 6 ++++++ src/axom/sina/examples/sina_set_datum_units_tags.cpp | 6 ++++++ src/axom/sina/examples/sina_tutorial.cpp | 6 ++++++ src/axom/sina/examples/sina_view_datum_types.cpp | 6 ++++++ src/axom/sina/examples/sina_view_datum_values.cpp | 6 ++++++ src/axom/sina/include/AdiakWriter.hpp | 6 ++++++ src/axom/sina/include/ConduitUtil.hpp | 6 ++++++ src/axom/sina/include/CppBridge.hpp | 6 ++++++ src/axom/sina/include/Curve.hpp | 6 ++++++ src/axom/sina/include/CurveSet.hpp | 6 ++++++ src/axom/sina/include/DataHolder.hpp | 6 ++++++ src/axom/sina/include/Datum.hpp | 6 ++++++ src/axom/sina/include/Document.hpp | 6 ++++++ src/axom/sina/include/File.hpp | 6 ++++++ src/axom/sina/include/ID.hpp | 6 ++++++ src/axom/sina/include/Record.hpp | 6 ++++++ src/axom/sina/include/Relationship.hpp | 6 ++++++ src/axom/sina/include/Run.hpp | 6 ++++++ src/axom/sina/interface/include/sina_fortran_interface.h | 6 ++++++ src/axom/sina/interface/src/sina_fortran_interface.cpp | 6 ++++++ src/axom/sina/src/AdiakWriter.cpp | 6 ++++++ src/axom/sina/src/ConduitUtil.cpp | 6 ++++++ src/axom/sina/src/Curve.cpp | 6 ++++++ src/axom/sina/src/CurveSet.cpp | 6 ++++++ src/axom/sina/src/DataHolder.cpp | 6 ++++++ src/axom/sina/src/Datum.cpp | 6 ++++++ src/axom/sina/src/Document.cpp | 6 ++++++ src/axom/sina/src/File.cpp | 6 ++++++ src/axom/sina/src/ID.cpp | 6 ++++++ src/axom/sina/src/Record.cpp | 6 ++++++ src/axom/sina/src/Relationship.cpp | 6 ++++++ src/axom/sina/src/Run.cpp | 6 ++++++ src/axom/sina/tests/include/ConduitTestUtils.hpp | 6 ++++++ src/axom/sina/tests/include/TestRecord.hpp | 6 ++++++ src/axom/sina/tests/src/ConduitTestUtils.cpp | 6 ++++++ src/axom/sina/tests/src/TestRecord.cpp | 6 ++++++ src/axom/sina/tests/src/sina_AdiakWriter.cpp | 6 ++++++ src/axom/sina/tests/src/sina_ConduitUtil.cpp | 6 ++++++ src/axom/sina/tests/src/sina_CppBridge.cpp | 6 ++++++ src/axom/sina/tests/src/sina_Curve.cpp | 6 ++++++ src/axom/sina/tests/src/sina_CurveSet.cpp | 6 ++++++ src/axom/sina/tests/src/sina_DataHolder.cpp | 6 ++++++ src/axom/sina/tests/src/sina_Datum.cpp | 6 ++++++ src/axom/sina/tests/src/sina_Document.cpp | 6 ++++++ src/axom/sina/tests/src/sina_File.cpp | 6 ++++++ src/axom/sina/tests/src/sina_ID.cpp | 6 ++++++ src/axom/sina/tests/src/sina_Record.cpp | 6 ++++++ src/axom/sina/tests/src/sina_Relationship.cpp | 6 ++++++ src/axom/sina/tests/src/sina_Run.cpp | 6 ++++++ 57 files changed, 342 insertions(+) diff --git a/src/axom/sina/examples/sina_basic.cpp b/src/axom/sina/examples/sina_basic.cpp index 34424e8e44..61f75d5d1b 100644 --- a/src/axom/sina/examples/sina_basic.cpp +++ b/src/axom/sina/examples/sina_basic.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_check_datum_type.cpp b/src/axom/sina/examples/sina_check_datum_type.cpp index 68c099ff44..c70947a0cc 100644 --- a/src/axom/sina/examples/sina_check_datum_type.cpp +++ b/src/axom/sina/examples/sina_check_datum_type.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_create_datum.cpp b/src/axom/sina/examples/sina_create_datum.cpp index fbb1192ac3..b5ea31ee10 100644 --- a/src/axom/sina/examples/sina_create_datum.cpp +++ b/src/axom/sina/examples/sina_create_datum.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_curve_set.cpp b/src/axom/sina/examples/sina_curve_set.cpp index 8e56b7fcf7..3fd00675cb 100644 --- a/src/axom/sina/examples/sina_curve_set.cpp +++ b/src/axom/sina/examples/sina_curve_set.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 diff --git a/src/axom/sina/examples/sina_file_object_creation.cpp b/src/axom/sina/examples/sina_file_object_creation.cpp index 6a4093b95c..e9a55d349e 100644 --- a/src/axom/sina/examples/sina_file_object_creation.cpp +++ b/src/axom/sina/examples/sina_file_object_creation.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_file_object_removal.cpp b/src/axom/sina/examples/sina_file_object_removal.cpp index 7b8f71e540..0ab2994288 100644 --- a/src/axom/sina/examples/sina_file_object_removal.cpp +++ b/src/axom/sina/examples/sina_file_object_removal.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_local_id_relationship.cpp b/src/axom/sina/examples/sina_local_id_relationship.cpp index 0041744ef7..0669308986 100644 --- a/src/axom/sina/examples/sina_local_id_relationship.cpp +++ b/src/axom/sina/examples/sina_local_id_relationship.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_query_record_for_files.cpp b/src/axom/sina/examples/sina_query_record_for_files.cpp index 2c83bbcada..71abe6ae22 100644 --- a/src/axom/sina/examples/sina_query_record_for_files.cpp +++ b/src/axom/sina/examples/sina_query_record_for_files.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_relationship_assembly.cpp b/src/axom/sina/examples/sina_relationship_assembly.cpp index d96220d7bd..42bf0f1020 100644 --- a/src/axom/sina/examples/sina_relationship_assembly.cpp +++ b/src/axom/sina/examples/sina_relationship_assembly.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_set_datum_units_tags.cpp b/src/axom/sina/examples/sina_set_datum_units_tags.cpp index deb0f625ba..7dbd111b69 100644 --- a/src/axom/sina/examples/sina_set_datum_units_tags.cpp +++ b/src/axom/sina/examples/sina_set_datum_units_tags.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_tutorial.cpp b/src/axom/sina/examples/sina_tutorial.cpp index 03340346a3..51d560e400 100644 --- a/src/axom/sina/examples/sina_tutorial.cpp +++ b/src/axom/sina/examples/sina_tutorial.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 diff --git a/src/axom/sina/examples/sina_view_datum_types.cpp b/src/axom/sina/examples/sina_view_datum_types.cpp index 17c823b611..51d7271ac3 100644 --- a/src/axom/sina/examples/sina_view_datum_types.cpp +++ b/src/axom/sina/examples/sina_view_datum_types.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/examples/sina_view_datum_values.cpp b/src/axom/sina/examples/sina_view_datum_values.cpp index e125c75402..b832b302f6 100644 --- a/src/axom/sina/examples/sina_view_datum_values.cpp +++ b/src/axom/sina/examples/sina_view_datum_values.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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) { diff --git a/src/axom/sina/include/AdiakWriter.hpp b/src/axom/sina/include/AdiakWriter.hpp index c720640873..b5e5ae1b41 100644 --- a/src/axom/sina/include/AdiakWriter.hpp +++ b/src/axom/sina/include/AdiakWriter.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_ADIAK_HPP #define SINA_ADIAK_HPP diff --git a/src/axom/sina/include/ConduitUtil.hpp b/src/axom/sina/include/ConduitUtil.hpp index 68f419b7e3..d35167f229 100644 --- a/src/axom/sina/include/ConduitUtil.hpp +++ b/src/axom/sina/include/ConduitUtil.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_JSONUTIL_HPP #define SINA_JSONUTIL_HPP diff --git a/src/axom/sina/include/CppBridge.hpp b/src/axom/sina/include/CppBridge.hpp index 65a3b237e6..ef090b7900 100644 --- a/src/axom/sina/include/CppBridge.hpp +++ b/src/axom/sina/include/CppBridge.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_CPPBRIDGE_HPP #define SINA_CPPBRIDGE_HPP diff --git a/src/axom/sina/include/Curve.hpp b/src/axom/sina/include/Curve.hpp index ea7f37bcd0..996fd23d27 100644 --- a/src/axom/sina/include/Curve.hpp +++ b/src/axom/sina/include/Curve.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_CURVE_HPP #define SINA_CURVE_HPP diff --git a/src/axom/sina/include/CurveSet.hpp b/src/axom/sina/include/CurveSet.hpp index 4ed4f4635b..d7b0be00c4 100644 --- a/src/axom/sina/include/CurveSet.hpp +++ b/src/axom/sina/include/CurveSet.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_CURVESET_HPP #define SINA_CURVESET_HPP diff --git a/src/axom/sina/include/DataHolder.hpp b/src/axom/sina/include/DataHolder.hpp index 3e06a76333..72f5770c2f 100644 --- a/src/axom/sina/include/DataHolder.hpp +++ b/src/axom/sina/include/DataHolder.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_DATAHOLDER_HPP #define SINA_DATAHOLDER_HPP diff --git a/src/axom/sina/include/Datum.hpp b/src/axom/sina/include/Datum.hpp index 280bf83182..1a52261333 100644 --- a/src/axom/sina/include/Datum.hpp +++ b/src/axom/sina/include/Datum.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_DATUM_HPP #define SINA_DATUM_HPP diff --git a/src/axom/sina/include/Document.hpp b/src/axom/sina/include/Document.hpp index a4e8cc8c34..934b5e4997 100644 --- a/src/axom/sina/include/Document.hpp +++ b/src/axom/sina/include/Document.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_DOCUMENT_HPP #define SINA_DOCUMENT_HPP diff --git a/src/axom/sina/include/File.hpp b/src/axom/sina/include/File.hpp index 56f68eef11..b056da221b 100644 --- a/src/axom/sina/include/File.hpp +++ b/src/axom/sina/include/File.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_FILE_HPP #define SINA_FILE_HPP diff --git a/src/axom/sina/include/ID.hpp b/src/axom/sina/include/ID.hpp index 1ccdf09556..ee39375710 100644 --- a/src/axom/sina/include/ID.hpp +++ b/src/axom/sina/include/ID.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_ID_HPP #define SINA_ID_HPP diff --git a/src/axom/sina/include/Record.hpp b/src/axom/sina/include/Record.hpp index 76021d14a7..e08f9050dc 100644 --- a/src/axom/sina/include/Record.hpp +++ b/src/axom/sina/include/Record.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_RECORD_HPP #define SINA_RECORD_HPP diff --git a/src/axom/sina/include/Relationship.hpp b/src/axom/sina/include/Relationship.hpp index f6947b5fff..fafe76e308 100644 --- a/src/axom/sina/include/Relationship.hpp +++ b/src/axom/sina/include/Relationship.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_RELATIONSHIP_HPP #define SINA_RELATIONSHIP_HPP diff --git a/src/axom/sina/include/Run.hpp b/src/axom/sina/include/Run.hpp index ccf79fa76e..0b700b2389 100644 --- a/src/axom/sina/include/Run.hpp +++ b/src/axom/sina/include/Run.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_RUN_HPP #define SINA_RUN_HPP diff --git a/src/axom/sina/interface/include/sina_fortran_interface.h b/src/axom/sina/interface/include/sina_fortran_interface.h index 130b996b57..b05db6810a 100644 --- a/src/axom/sina/interface/include/sina_fortran_interface.h +++ b/src/axom/sina/interface/include/sina_fortran_interface.h @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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/include/Document.hpp" #include "axom/sina/include/Record.hpp" diff --git a/src/axom/sina/interface/src/sina_fortran_interface.cpp b/src/axom/sina/interface/src/sina_fortran_interface.cpp index 1d46f78006..40ef5991f6 100644 --- a/src/axom/sina/interface/src/sina_fortran_interface.cpp +++ b/src/axom/sina/interface/src/sina_fortran_interface.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include "axom/sina/interface/include/sina_fortran_interface.h" diff --git a/src/axom/sina/src/AdiakWriter.cpp b/src/axom/sina/src/AdiakWriter.cpp index 7aec013d31..e2b2114655 100644 --- a/src/axom/sina/src/AdiakWriter.cpp +++ b/src/axom/sina/src/AdiakWriter.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + /// @file #include "axom/sina/include/AdiakWriter.hpp" diff --git a/src/axom/sina/src/ConduitUtil.cpp b/src/axom/sina/src/ConduitUtil.cpp index d8e394d50c..4985bbf377 100644 --- a/src/axom/sina/src/ConduitUtil.cpp +++ b/src/axom/sina/src/ConduitUtil.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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/include/ConduitUtil.hpp" #include diff --git a/src/axom/sina/src/Curve.cpp b/src/axom/sina/src/Curve.cpp index d765d53b26..e812db8a8e 100644 --- a/src/axom/sina/src/Curve.cpp +++ b/src/axom/sina/src/Curve.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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/include/Curve.hpp" #include "axom/sina/include/ConduitUtil.hpp" diff --git a/src/axom/sina/src/CurveSet.cpp b/src/axom/sina/src/CurveSet.cpp index 42160528d4..384c893a16 100644 --- a/src/axom/sina/src/CurveSet.cpp +++ b/src/axom/sina/src/CurveSet.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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/include/CurveSet.hpp" #include diff --git a/src/axom/sina/src/DataHolder.cpp b/src/axom/sina/src/DataHolder.cpp index 0f07c886c9..579ea4a7b5 100644 --- a/src/axom/sina/src/DataHolder.cpp +++ b/src/axom/sina/src/DataHolder.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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/include/DataHolder.hpp" #include "axom/sina/include/ConduitUtil.hpp" diff --git a/src/axom/sina/src/Datum.cpp b/src/axom/sina/src/Datum.cpp index f272c4d91e..ff40a04b16 100644 --- a/src/axom/sina/src/Datum.cpp +++ b/src/axom/sina/src/Datum.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + /// @file #include "axom/sina/include/Datum.hpp" diff --git a/src/axom/sina/src/Document.cpp b/src/axom/sina/src/Document.cpp index 7ed1eec11c..74007e4d2b 100644 --- a/src/axom/sina/src/Document.cpp +++ b/src/axom/sina/src/Document.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + /// @file #include "axom/sina/include/Document.hpp" diff --git a/src/axom/sina/src/File.cpp b/src/axom/sina/src/File.cpp index d11e24290f..b06101ce8e 100644 --- a/src/axom/sina/src/File.cpp +++ b/src/axom/sina/src/File.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + /// @file #include "axom/sina/include/File.hpp" diff --git a/src/axom/sina/src/ID.cpp b/src/axom/sina/src/ID.cpp index 7e2e4b365d..9991263bfe 100644 --- a/src/axom/sina/src/ID.cpp +++ b/src/axom/sina/src/ID.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + /// @file diff --git a/src/axom/sina/src/Record.cpp b/src/axom/sina/src/Record.cpp index 5a73ac79bc..eb5154da15 100644 --- a/src/axom/sina/src/Record.cpp +++ b/src/axom/sina/src/Record.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + /// @file #include "axom/sina/include/Record.hpp" diff --git a/src/axom/sina/src/Relationship.cpp b/src/axom/sina/src/Relationship.cpp index a2d21dc4c6..dc801a914c 100644 --- a/src/axom/sina/src/Relationship.cpp +++ b/src/axom/sina/src/Relationship.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + /// @file #include "axom/sina/include/Relationship.hpp" diff --git a/src/axom/sina/src/Run.cpp b/src/axom/sina/src/Run.cpp index 79697e00d6..4b1eae7904 100644 --- a/src/axom/sina/src/Run.cpp +++ b/src/axom/sina/src/Run.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + /// @file #include "axom/sina/include/Run.hpp" diff --git a/src/axom/sina/tests/include/ConduitTestUtils.hpp b/src/axom/sina/tests/include/ConduitTestUtils.hpp index 88a3ff2c55..fc31630f71 100644 --- a/src/axom/sina/tests/include/ConduitTestUtils.hpp +++ b/src/axom/sina/tests/include/ConduitTestUtils.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_CONDUITTESTUTILS_HPP #define SINA_CONDUITTESTUTILS_HPP diff --git a/src/axom/sina/tests/include/TestRecord.hpp b/src/axom/sina/tests/include/TestRecord.hpp index eba82af795..9f28bd1ea6 100644 --- a/src/axom/sina/tests/include/TestRecord.hpp +++ b/src/axom/sina/tests/include/TestRecord.hpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + #ifndef SINA_TESTRECORD_HPP #define SINA_TESTRECORD_HPP diff --git a/src/axom/sina/tests/src/ConduitTestUtils.cpp b/src/axom/sina/tests/src/ConduitTestUtils.cpp index 2b1656dae0..2ab85d407f 100644 --- a/src/axom/sina/tests/src/ConduitTestUtils.cpp +++ b/src/axom/sina/tests/src/ConduitTestUtils.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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/tests/include/ConduitTestUtils.hpp" namespace axom diff --git a/src/axom/sina/tests/src/TestRecord.cpp b/src/axom/sina/tests/src/TestRecord.cpp index c382f45843..e24e50f6c0 100644 --- a/src/axom/sina/tests/src/TestRecord.cpp +++ b/src/axom/sina/tests/src/TestRecord.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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/tests/include/TestRecord.hpp" namespace axom diff --git a/src/axom/sina/tests/src/sina_AdiakWriter.cpp b/src/axom/sina/tests/src/sina_AdiakWriter.cpp index fcb700b6ee..cc86402753 100644 --- a/src/axom/sina/tests/src/sina_AdiakWriter.cpp +++ b/src/axom/sina/tests/src/sina_AdiakWriter.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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/include/AdiakWriter.hpp" #ifdef AXOM_SINA_USE_ADIAK diff --git a/src/axom/sina/tests/src/sina_ConduitUtil.cpp b/src/axom/sina/tests/src/sina_ConduitUtil.cpp index 54e690acd3..885e9bb3ce 100644 --- a/src/axom/sina/tests/src/sina_ConduitUtil.cpp +++ b/src/axom/sina/tests/src/sina_ConduitUtil.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include "gtest/gtest.h" diff --git a/src/axom/sina/tests/src/sina_CppBridge.cpp b/src/axom/sina/tests/src/sina_CppBridge.cpp index 0ca08f40fe..652638f963 100644 --- a/src/axom/sina/tests/src/sina_CppBridge.cpp +++ b/src/axom/sina/tests/src/sina_CppBridge.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include "gtest/gtest.h" diff --git a/src/axom/sina/tests/src/sina_Curve.cpp b/src/axom/sina/tests/src/sina_Curve.cpp index 41092684e2..c7d8587abd 100644 --- a/src/axom/sina/tests/src/sina_Curve.cpp +++ b/src/axom/sina/tests/src/sina_Curve.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 "gtest/gtest.h" #include "gmock/gmock.h" diff --git a/src/axom/sina/tests/src/sina_CurveSet.cpp b/src/axom/sina/tests/src/sina_CurveSet.cpp index 0d370ded45..f90d493026 100644 --- a/src/axom/sina/tests/src/sina_CurveSet.cpp +++ b/src/axom/sina/tests/src/sina_CurveSet.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 "gtest/gtest.h" #include "gmock/gmock.h" diff --git a/src/axom/sina/tests/src/sina_DataHolder.cpp b/src/axom/sina/tests/src/sina_DataHolder.cpp index e457389252..0c29288a1a 100644 --- a/src/axom/sina/tests/src/sina_DataHolder.cpp +++ b/src/axom/sina/tests/src/sina_DataHolder.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include diff --git a/src/axom/sina/tests/src/sina_Datum.cpp b/src/axom/sina/tests/src/sina_Datum.cpp index 963e5b72ca..fd1993a14b 100644 --- a/src/axom/sina/tests/src/sina_Datum.cpp +++ b/src/axom/sina/tests/src/sina_Datum.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include #include diff --git a/src/axom/sina/tests/src/sina_Document.cpp b/src/axom/sina/tests/src/sina_Document.cpp index 44ad9c8114..6520c93b84 100644 --- a/src/axom/sina/tests/src/sina_Document.cpp +++ b/src/axom/sina/tests/src/sina_Document.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include #include diff --git a/src/axom/sina/tests/src/sina_File.cpp b/src/axom/sina/tests/src/sina_File.cpp index 47385eb708..0283ef65aa 100644 --- a/src/axom/sina/tests/src/sina_File.cpp +++ b/src/axom/sina/tests/src/sina_File.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 "gtest/gtest.h" #include "axom/sina/include/File.hpp" diff --git a/src/axom/sina/tests/src/sina_ID.cpp b/src/axom/sina/tests/src/sina_ID.cpp index bcdb5a97d3..b920c74d37 100644 --- a/src/axom/sina/tests/src/sina_ID.cpp +++ b/src/axom/sina/tests/src/sina_ID.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include "gtest/gtest.h" diff --git a/src/axom/sina/tests/src/sina_Record.cpp b/src/axom/sina/tests/src/sina_Record.cpp index 3878cfd6e2..04bd65c963 100644 --- a/src/axom/sina/tests/src/sina_Record.cpp +++ b/src/axom/sina/tests/src/sina_Record.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include diff --git a/src/axom/sina/tests/src/sina_Relationship.cpp b/src/axom/sina/tests/src/sina_Relationship.cpp index 59149bd400..80ae1f4553 100644 --- a/src/axom/sina/tests/src/sina_Relationship.cpp +++ b/src/axom/sina/tests/src/sina_Relationship.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include "gtest/gtest.h" diff --git a/src/axom/sina/tests/src/sina_Run.cpp b/src/axom/sina/tests/src/sina_Run.cpp index 4bb43d2a67..8896ff4124 100644 --- a/src/axom/sina/tests/src/sina_Run.cpp +++ b/src/axom/sina/tests/src/sina_Run.cpp @@ -1,3 +1,9 @@ +// Copyright (c) 2017-2024, 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 #include "gtest/gtest.h" From 4a635bf877472c858db881d9f3bddb5d9f270c23 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 11 Jul 2024 09:27:06 -0700 Subject: [PATCH 20/60] move cpp/hpp files to same directory for core and tests --- src/axom/sina/CMakeLists.txt | 54 +++++++++---------- src/axom/sina/{src => core}/AdiakWriter.cpp | 12 ++--- .../sina/{include => core}/AdiakWriter.hpp | 6 +-- src/axom/sina/{src => core}/ConduitUtil.cpp | 2 +- .../sina/{include => core}/ConduitUtil.hpp | 0 src/axom/sina/{include => core}/CppBridge.hpp | 0 src/axom/sina/{src => core}/Curve.cpp | 4 +- src/axom/sina/{include => core}/Curve.hpp | 0 src/axom/sina/{src => core}/CurveSet.cpp | 4 +- src/axom/sina/{include => core}/CurveSet.hpp | 2 +- src/axom/sina/{src => core}/DataHolder.cpp | 6 +-- .../sina/{include => core}/DataHolder.hpp | 4 +- src/axom/sina/{src => core}/Datum.cpp | 2 +- src/axom/sina/{include => core}/Datum.hpp | 2 +- src/axom/sina/{src => core}/Document.cpp | 2 +- src/axom/sina/{include => core}/Document.hpp | 4 +- src/axom/sina/{src => core}/File.cpp | 4 +- src/axom/sina/{include => core}/File.hpp | 0 src/axom/sina/{src => core}/ID.cpp | 2 +- src/axom/sina/{include => core}/ID.hpp | 0 src/axom/sina/{src => core}/Record.cpp | 10 ++-- src/axom/sina/{include => core}/Record.hpp | 10 ++-- src/axom/sina/{src => core}/Relationship.cpp | 4 +- .../sina/{include => core}/Relationship.hpp | 2 +- src/axom/sina/{src => core}/Run.cpp | 6 +-- src/axom/sina/{include => core}/Run.hpp | 2 +- .../{src => }/sina_fortran_interface.cpp | 2 +- .../{src => }/sina_fortran_interface.f90 | 0 .../{include => }/sina_fortran_interface.h | 6 +-- .../sina/interface/{src => }/sina_schema.json | 0 src/axom/sina/tests/CMakeLists.txt | 36 ++++++------- .../sina/tests/{src => }/ConduitTestUtils.cpp | 2 +- .../tests/{include => }/ConduitTestUtils.hpp | 0 src/axom/sina/tests/{src => }/TestRecord.cpp | 2 +- .../sina/tests/{include => }/TestRecord.hpp | 4 +- .../sina/tests/{src => }/sina_AdiakWriter.cpp | 8 +-- .../sina/tests/{src => }/sina_ConduitUtil.cpp | 4 +- .../sina/tests/{src => }/sina_CppBridge.cpp | 2 +- src/axom/sina/tests/{src => }/sina_Curve.cpp | 6 +-- .../sina/tests/{src => }/sina_CurveSet.cpp | 4 +- .../sina/tests/{src => }/sina_DataHolder.cpp | 4 +- src/axom/sina/tests/{src => }/sina_Datum.cpp | 4 +- .../sina/tests/{src => }/sina_Document.cpp | 8 +-- src/axom/sina/tests/{src => }/sina_File.cpp | 4 +- src/axom/sina/tests/{src => }/sina_ID.cpp | 2 +- src/axom/sina/tests/{src => }/sina_Record.cpp | 8 +-- .../tests/{src => }/sina_Relationship.cpp | 2 +- src/axom/sina/tests/{src => }/sina_Run.cpp | 2 +- 48 files changed, 127 insertions(+), 127 deletions(-) rename src/axom/sina/{src => core}/AdiakWriter.cpp (97%) rename src/axom/sina/{include => core}/AdiakWriter.hpp (92%) rename src/axom/sina/{src => core}/ConduitUtil.cpp (99%) rename src/axom/sina/{include => core}/ConduitUtil.hpp (100%) rename src/axom/sina/{include => core}/CppBridge.hpp (100%) rename src/axom/sina/{src => core}/Curve.cpp (95%) rename src/axom/sina/{include => core}/Curve.hpp (100%) rename src/axom/sina/{src => core}/CurveSet.cpp (97%) rename src/axom/sina/{include => core}/CurveSet.hpp (98%) rename src/axom/sina/{src => core}/DataHolder.cpp (97%) rename src/axom/sina/{include => core}/DataHolder.hpp (98%) rename src/axom/sina/{src => core}/Datum.cpp (99%) rename src/axom/sina/{include => core}/Datum.hpp (99%) rename src/axom/sina/{src => core}/Document.cpp (99%) rename src/axom/sina/{include => core}/Document.hpp (98%) rename src/axom/sina/{src => core}/File.cpp (95%) rename src/axom/sina/{include => core}/File.hpp (100%) rename src/axom/sina/{src => core}/ID.cpp (98%) rename src/axom/sina/{include => core}/ID.hpp (100%) rename src/axom/sina/{src => core}/Record.cpp (93%) rename src/axom/sina/{include => core}/Record.hpp (96%) rename src/axom/sina/{src => core}/Relationship.cpp (93%) rename src/axom/sina/{include => core}/Relationship.hpp (99%) rename src/axom/sina/{src => core}/Run.cpp (92%) rename src/axom/sina/{include => core}/Run.hpp (98%) rename src/axom/sina/interface/{src => }/sina_fortran_interface.cpp (99%) rename src/axom/sina/interface/{src => }/sina_fortran_interface.f90 (100%) rename src/axom/sina/interface/{include => }/sina_fortran_interface.h (91%) rename src/axom/sina/interface/{src => }/sina_schema.json (100%) rename src/axom/sina/tests/{src => }/ConduitTestUtils.cpp (93%) rename src/axom/sina/tests/{include => }/ConduitTestUtils.hpp (100%) rename src/axom/sina/tests/{src => }/TestRecord.cpp (93%) rename src/axom/sina/tests/{include => }/TestRecord.hpp (95%) rename src/axom/sina/tests/{src => }/sina_AdiakWriter.cpp (97%) rename src/axom/sina/tests/{src => }/sina_ConduitUtil.cpp (98%) rename src/axom/sina/tests/{src => }/sina_CppBridge.cpp (94%) rename src/axom/sina/tests/{src => }/sina_Curve.cpp (95%) rename src/axom/sina/tests/{src => }/sina_CurveSet.cpp (98%) rename src/axom/sina/tests/{src => }/sina_DataHolder.cpp (99%) rename src/axom/sina/tests/{src => }/sina_Datum.cpp (98%) rename src/axom/sina/tests/{src => }/sina_Document.cpp (98%) rename src/axom/sina/tests/{src => }/sina_File.cpp (96%) rename src/axom/sina/tests/{src => }/sina_ID.cpp (98%) rename src/axom/sina/tests/{src => }/sina_Record.cpp (98%) rename src/axom/sina/tests/{src => }/sina_Relationship.cpp (99%) rename src/axom/sina/tests/{src => }/sina_Run.cpp (98%) diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index 4657a16580..aa1c608b8b 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -29,43 +29,43 @@ axom_configure_file ( config.hpp.in ${PROJECT_BINARY_DIR}/include/axom/sina/conf # Specify the sina headers/sources #------------------------------------------------------------------------------ set(sina_headers - include/ConduitUtil.hpp - include/CppBridge.hpp - include/Curve.hpp - include/CurveSet.hpp - include/DataHolder.hpp - include/Datum.hpp - include/Document.hpp - include/File.hpp - include/ID.hpp - include/Record.hpp - include/Relationship.hpp - include/Run.hpp + core/ConduitUtil.hpp + core/CppBridge.hpp + core/Curve.hpp + core/CurveSet.hpp + core/DataHolder.hpp + core/Datum.hpp + core/Document.hpp + core/File.hpp + core/ID.hpp + core/Record.hpp + core/Relationship.hpp + core/Run.hpp ) set(sina_sources - src/ConduitUtil.cpp - src/Curve.cpp - src/CurveSet.cpp - src/DataHolder.cpp - src/Datum.cpp - src/Document.cpp - src/File.cpp - src/ID.cpp - src/Record.cpp - src/Relationship.cpp - src/Run.cpp + core/ConduitUtil.cpp + core/Curve.cpp + core/CurveSet.cpp + core/DataHolder.cpp + core/Datum.cpp + core/Document.cpp + core/File.cpp + core/ID.cpp + core/Record.cpp + core/Relationship.cpp + core/Run.cpp ) # Add Adiak header and source -blt_list_append( TO sina_headers ELEMENTS include/AdiakWriter.hpp IF AXOM_SINA_USE_ADIAK ) -blt_list_append( TO sina_sources ELEMENTS src/AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK ) +blt_list_append( TO sina_headers ELEMENTS core/AdiakWriter.hpp IF AXOM_SINA_USE_ADIAK ) +blt_list_append( TO sina_sources ELEMENTS core/AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK ) # Add fortran interface for Sina if (ENABLE_FORTRAN) - blt_list_append( TO sina_headers ELEMENTS interface/include/sina_fortran_interface.h) + blt_list_append( TO sina_headers ELEMENTS interface/sina_fortran_interface.h) blt_list_append( TO sina_sources - ELEMENTS interface/src/sina_fortran_interface.cpp interface/src/sina_fortran_interface.f90) + ELEMENTS interface/sina_fortran_interface.cpp interface/sina_fortran_interface.f90) endif() #------------------------------------------------------------------------------ diff --git a/src/axom/sina/src/AdiakWriter.cpp b/src/axom/sina/core/AdiakWriter.cpp similarity index 97% rename from src/axom/sina/src/AdiakWriter.cpp rename to src/axom/sina/core/AdiakWriter.cpp index e2b2114655..bf0153e18e 100644 --- a/src/axom/sina/src/AdiakWriter.cpp +++ b/src/axom/sina/core/AdiakWriter.cpp @@ -6,7 +6,7 @@ /// @file -#include "axom/sina/include/AdiakWriter.hpp" +#include "axom/sina/core/AdiakWriter.hpp" #ifdef AXOM_SINA_USE_ADIAK @@ -20,11 +20,11 @@ extern "C" { #include "adiak_tool.h" } -#include "axom/sina/include/CppBridge.hpp" -#include "axom/sina/include/ConduitUtil.hpp" -#include "axom/sina/include/Record.hpp" -#include "axom/sina/include/Datum.hpp" -#include "axom/sina/include/Document.hpp" +#include "axom/sina/core/CppBridge.hpp" +#include "axom/sina/core/ConduitUtil.hpp" +#include "axom/sina/core/Record.hpp" +#include "axom/sina/core/Datum.hpp" +#include "axom/sina/core/Document.hpp" namespace axom { diff --git a/src/axom/sina/include/AdiakWriter.hpp b/src/axom/sina/core/AdiakWriter.hpp similarity index 92% rename from src/axom/sina/include/AdiakWriter.hpp rename to src/axom/sina/core/AdiakWriter.hpp index b5e5ae1b41..2acd396de1 100644 --- a/src/axom/sina/include/AdiakWriter.hpp +++ b/src/axom/sina/core/AdiakWriter.hpp @@ -15,9 +15,9 @@ #include #include -#include "axom/sina/include/ConduitUtil.hpp" -#include "axom/sina/include/Record.hpp" -#include "axom/sina/include/Run.hpp" +#include "axom/sina/core/ConduitUtil.hpp" +#include "axom/sina/core/Record.hpp" +#include "axom/sina/core/Run.hpp" extern "C" { #include "adiak_tool.h" diff --git a/src/axom/sina/src/ConduitUtil.cpp b/src/axom/sina/core/ConduitUtil.cpp similarity index 99% rename from src/axom/sina/src/ConduitUtil.cpp rename to src/axom/sina/core/ConduitUtil.cpp index 4985bbf377..e2f7c07762 100644 --- a/src/axom/sina/src/ConduitUtil.cpp +++ b/src/axom/sina/core/ConduitUtil.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/ConduitUtil.hpp" #include #include diff --git a/src/axom/sina/include/ConduitUtil.hpp b/src/axom/sina/core/ConduitUtil.hpp similarity index 100% rename from src/axom/sina/include/ConduitUtil.hpp rename to src/axom/sina/core/ConduitUtil.hpp diff --git a/src/axom/sina/include/CppBridge.hpp b/src/axom/sina/core/CppBridge.hpp similarity index 100% rename from src/axom/sina/include/CppBridge.hpp rename to src/axom/sina/core/CppBridge.hpp diff --git a/src/axom/sina/src/Curve.cpp b/src/axom/sina/core/Curve.cpp similarity index 95% rename from src/axom/sina/src/Curve.cpp rename to src/axom/sina/core/Curve.cpp index e812db8a8e..df33010e11 100644 --- a/src/axom/sina/src/Curve.cpp +++ b/src/axom/sina/core/Curve.cpp @@ -4,8 +4,8 @@ // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/sina/include/Curve.hpp" -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/Curve.hpp" +#include "axom/sina/core/ConduitUtil.hpp" #include diff --git a/src/axom/sina/include/Curve.hpp b/src/axom/sina/core/Curve.hpp similarity index 100% rename from src/axom/sina/include/Curve.hpp rename to src/axom/sina/core/Curve.hpp diff --git a/src/axom/sina/src/CurveSet.cpp b/src/axom/sina/core/CurveSet.cpp similarity index 97% rename from src/axom/sina/src/CurveSet.cpp rename to src/axom/sina/core/CurveSet.cpp index 384c893a16..1065e207c9 100644 --- a/src/axom/sina/src/CurveSet.cpp +++ b/src/axom/sina/core/CurveSet.cpp @@ -4,11 +4,11 @@ // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/sina/include/CurveSet.hpp" +#include "axom/sina/core/CurveSet.hpp" #include -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/ConduitUtil.hpp" namespace axom { diff --git a/src/axom/sina/include/CurveSet.hpp b/src/axom/sina/core/CurveSet.hpp similarity index 98% rename from src/axom/sina/include/CurveSet.hpp rename to src/axom/sina/core/CurveSet.hpp index d7b0be00c4..02b72e2838 100644 --- a/src/axom/sina/include/CurveSet.hpp +++ b/src/axom/sina/core/CurveSet.hpp @@ -18,7 +18,7 @@ #include "conduit.hpp" -#include "axom/sina/include/Curve.hpp" +#include "axom/sina/core/Curve.hpp" namespace axom { diff --git a/src/axom/sina/src/DataHolder.cpp b/src/axom/sina/core/DataHolder.cpp similarity index 97% rename from src/axom/sina/src/DataHolder.cpp rename to src/axom/sina/core/DataHolder.cpp index 579ea4a7b5..e868887770 100644 --- a/src/axom/sina/src/DataHolder.cpp +++ b/src/axom/sina/core/DataHolder.cpp @@ -4,10 +4,10 @@ // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/sina/include/DataHolder.hpp" +#include "axom/sina/core/DataHolder.hpp" -#include "axom/sina/include/ConduitUtil.hpp" -#include "axom/sina/include/Datum.hpp" +#include "axom/sina/core/ConduitUtil.hpp" +#include "axom/sina/core/Datum.hpp" #include diff --git a/src/axom/sina/include/DataHolder.hpp b/src/axom/sina/core/DataHolder.hpp similarity index 98% rename from src/axom/sina/include/DataHolder.hpp rename to src/axom/sina/core/DataHolder.hpp index 72f5770c2f..cc5ac9661f 100644 --- a/src/axom/sina/include/DataHolder.hpp +++ b/src/axom/sina/core/DataHolder.hpp @@ -19,8 +19,8 @@ #include "conduit.hpp" -#include "axom/sina/include/Datum.hpp" -#include "axom/sina/include/CurveSet.hpp" +#include "axom/sina/core/Datum.hpp" +#include "axom/sina/core/CurveSet.hpp" namespace axom { diff --git a/src/axom/sina/src/Datum.cpp b/src/axom/sina/core/Datum.cpp similarity index 99% rename from src/axom/sina/src/Datum.cpp rename to src/axom/sina/core/Datum.cpp index ff40a04b16..dd6dfdce5d 100644 --- a/src/axom/sina/src/Datum.cpp +++ b/src/axom/sina/core/Datum.cpp @@ -6,7 +6,7 @@ /// @file -#include "axom/sina/include/Datum.hpp" +#include "axom/sina/core/Datum.hpp" #include "conduit.hpp" #include diff --git a/src/axom/sina/include/Datum.hpp b/src/axom/sina/core/Datum.hpp similarity index 99% rename from src/axom/sina/include/Datum.hpp rename to src/axom/sina/core/Datum.hpp index 1a52261333..97503cb3df 100644 --- a/src/axom/sina/include/Datum.hpp +++ b/src/axom/sina/core/Datum.hpp @@ -12,7 +12,7 @@ #include #include -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/ConduitUtil.hpp" #include "conduit.hpp" namespace axom diff --git a/src/axom/sina/src/Document.cpp b/src/axom/sina/core/Document.cpp similarity index 99% rename from src/axom/sina/src/Document.cpp rename to src/axom/sina/core/Document.cpp index 74007e4d2b..a16eb1b6d4 100644 --- a/src/axom/sina/src/Document.cpp +++ b/src/axom/sina/core/Document.cpp @@ -6,7 +6,7 @@ /// @file -#include "axom/sina/include/Document.hpp" +#include "axom/sina/core/Document.hpp" #include #include diff --git a/src/axom/sina/include/Document.hpp b/src/axom/sina/core/Document.hpp similarity index 98% rename from src/axom/sina/include/Document.hpp rename to src/axom/sina/core/Document.hpp index 934b5e4997..d91eade4f3 100644 --- a/src/axom/sina/include/Document.hpp +++ b/src/axom/sina/core/Document.hpp @@ -14,8 +14,8 @@ #include "conduit.hpp" -#include "axom/sina/include/Record.hpp" -#include "axom/sina/include/Relationship.hpp" +#include "axom/sina/core/Record.hpp" +#include "axom/sina/core/Relationship.hpp" namespace axom { diff --git a/src/axom/sina/src/File.cpp b/src/axom/sina/core/File.cpp similarity index 95% rename from src/axom/sina/src/File.cpp rename to src/axom/sina/core/File.cpp index b06101ce8e..32331cfe29 100644 --- a/src/axom/sina/src/File.cpp +++ b/src/axom/sina/core/File.cpp @@ -6,8 +6,8 @@ /// @file -#include "axom/sina/include/File.hpp" -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/File.hpp" +#include "axom/sina/core/ConduitUtil.hpp" #include #include diff --git a/src/axom/sina/include/File.hpp b/src/axom/sina/core/File.hpp similarity index 100% rename from src/axom/sina/include/File.hpp rename to src/axom/sina/core/File.hpp diff --git a/src/axom/sina/src/ID.cpp b/src/axom/sina/core/ID.cpp similarity index 98% rename from src/axom/sina/src/ID.cpp rename to src/axom/sina/core/ID.cpp index 9991263bfe..78aeded854 100644 --- a/src/axom/sina/src/ID.cpp +++ b/src/axom/sina/core/ID.cpp @@ -8,7 +8,7 @@ /// @file -#include "axom/sina/include/ID.hpp" +#include "axom/sina/core/ID.hpp" #include #include diff --git a/src/axom/sina/include/ID.hpp b/src/axom/sina/core/ID.hpp similarity index 100% rename from src/axom/sina/include/ID.hpp rename to src/axom/sina/core/ID.hpp diff --git a/src/axom/sina/src/Record.cpp b/src/axom/sina/core/Record.cpp similarity index 93% rename from src/axom/sina/src/Record.cpp rename to src/axom/sina/core/Record.cpp index eb5154da15..aafd3538c5 100644 --- a/src/axom/sina/src/Record.cpp +++ b/src/axom/sina/core/Record.cpp @@ -6,15 +6,15 @@ /// @file -#include "axom/sina/include/Record.hpp" +#include "axom/sina/core/Record.hpp" #include #include -#include "axom/sina/include/CppBridge.hpp" -#include "axom/sina/include/ConduitUtil.hpp" -#include "axom/sina/include/DataHolder.hpp" -#include "axom/sina/include/Run.hpp" +#include "axom/sina/core/CppBridge.hpp" +#include "axom/sina/core/ConduitUtil.hpp" +#include "axom/sina/core/DataHolder.hpp" +#include "axom/sina/core/Run.hpp" namespace { diff --git a/src/axom/sina/include/Record.hpp b/src/axom/sina/core/Record.hpp similarity index 96% rename from src/axom/sina/include/Record.hpp rename to src/axom/sina/core/Record.hpp index e08f9050dc..dac1d074b6 100644 --- a/src/axom/sina/include/Record.hpp +++ b/src/axom/sina/core/Record.hpp @@ -18,11 +18,11 @@ #include "conduit.hpp" -#include "axom/sina/include/ID.hpp" -#include "axom/sina/include/DataHolder.hpp" -#include "axom/sina/include/CurveSet.hpp" -#include "axom/sina/include/Datum.hpp" -#include "axom/sina/include/File.hpp" +#include "axom/sina/core/ID.hpp" +#include "axom/sina/core/DataHolder.hpp" +#include "axom/sina/core/CurveSet.hpp" +#include "axom/sina/core/Datum.hpp" +#include "axom/sina/core/File.hpp" namespace axom { diff --git a/src/axom/sina/src/Relationship.cpp b/src/axom/sina/core/Relationship.cpp similarity index 93% rename from src/axom/sina/src/Relationship.cpp rename to src/axom/sina/core/Relationship.cpp index dc801a914c..ebec7f7695 100644 --- a/src/axom/sina/src/Relationship.cpp +++ b/src/axom/sina/core/Relationship.cpp @@ -6,11 +6,11 @@ /// @file -#include "axom/sina/include/Relationship.hpp" +#include "axom/sina/core/Relationship.hpp" #include -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/ConduitUtil.hpp" namespace axom { diff --git a/src/axom/sina/include/Relationship.hpp b/src/axom/sina/core/Relationship.hpp similarity index 99% rename from src/axom/sina/include/Relationship.hpp rename to src/axom/sina/core/Relationship.hpp index fafe76e308..455946e529 100644 --- a/src/axom/sina/include/Relationship.hpp +++ b/src/axom/sina/core/Relationship.hpp @@ -17,7 +17,7 @@ #include "conduit.hpp" -#include "axom/sina/include/ID.hpp" +#include "axom/sina/core/ID.hpp" namespace axom { diff --git a/src/axom/sina/src/Run.cpp b/src/axom/sina/core/Run.cpp similarity index 92% rename from src/axom/sina/src/Run.cpp rename to src/axom/sina/core/Run.cpp index 4b1eae7904..c23e27ff98 100644 --- a/src/axom/sina/src/Run.cpp +++ b/src/axom/sina/core/Run.cpp @@ -6,12 +6,12 @@ /// @file -#include "axom/sina/include/Run.hpp" +#include "axom/sina/core/Run.hpp" #include -#include "axom/sina/include/CppBridge.hpp" -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/CppBridge.hpp" +#include "axom/sina/core/ConduitUtil.hpp" namespace axom { diff --git a/src/axom/sina/include/Run.hpp b/src/axom/sina/core/Run.hpp similarity index 98% rename from src/axom/sina/include/Run.hpp rename to src/axom/sina/core/Run.hpp index 0b700b2389..e16f0e3738 100644 --- a/src/axom/sina/include/Run.hpp +++ b/src/axom/sina/core/Run.hpp @@ -9,7 +9,7 @@ /// @file -#include "axom/sina/include/Record.hpp" +#include "axom/sina/core/Record.hpp" namespace axom { diff --git a/src/axom/sina/interface/src/sina_fortran_interface.cpp b/src/axom/sina/interface/sina_fortran_interface.cpp similarity index 99% rename from src/axom/sina/interface/src/sina_fortran_interface.cpp rename to src/axom/sina/interface/sina_fortran_interface.cpp index 40ef5991f6..960c306698 100644 --- a/src/axom/sina/interface/src/sina_fortran_interface.cpp +++ b/src/axom/sina/interface/sina_fortran_interface.cpp @@ -6,7 +6,7 @@ #include -#include "axom/sina/interface/include/sina_fortran_interface.h" +#include "axom/sina/interface/sina_fortran_interface.h" axom::sina::Document *sina_document; diff --git a/src/axom/sina/interface/src/sina_fortran_interface.f90 b/src/axom/sina/interface/sina_fortran_interface.f90 similarity index 100% rename from src/axom/sina/interface/src/sina_fortran_interface.f90 rename to src/axom/sina/interface/sina_fortran_interface.f90 diff --git a/src/axom/sina/interface/include/sina_fortran_interface.h b/src/axom/sina/interface/sina_fortran_interface.h similarity index 91% rename from src/axom/sina/interface/include/sina_fortran_interface.h rename to src/axom/sina/interface/sina_fortran_interface.h index b05db6810a..2a4d4bdbfc 100644 --- a/src/axom/sina/interface/include/sina_fortran_interface.h +++ b/src/axom/sina/interface/sina_fortran_interface.h @@ -5,9 +5,9 @@ -#include "axom/sina/include/Document.hpp" -#include "axom/sina/include/Record.hpp" -#include "axom/sina/include/Run.hpp" +#include "axom/sina/core/Document.hpp" +#include "axom/sina/core/Record.hpp" +#include "axom/sina/core/Run.hpp" #include "axom/sina.hpp" extern "C" char* Get_File_Extension(char *); diff --git a/src/axom/sina/interface/src/sina_schema.json b/src/axom/sina/interface/sina_schema.json similarity index 100% rename from src/axom/sina/interface/src/sina_schema.json rename to src/axom/sina/interface/sina_schema.json diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index eaf4ad6a56..e1cf840853 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -11,8 +11,8 @@ # Add gtest C++ tests #------------------------------------------------------------------------------ set(gtest_sina_tests - src/sina_CppBridge.cpp - src/sina_File.cpp + sina_CppBridge.cpp + sina_File.cpp ) set(sina_gtests_depends_on gtest sina conduit::conduit) @@ -40,12 +40,12 @@ if (ENABLE_GMOCK) # Define Sina test utility sources and headers set(sina_test_utils_sources - src/ConduitTestUtils.cpp - src/TestRecord.cpp + ConduitTestUtils.cpp + TestRecord.cpp ) set(sina_test_utils_headers - include/ConduitTestUtils.hpp - include/TestRecord.hpp + ConduitTestUtils.hpp + TestRecord.hpp ) set(sina_test_utils_depends_on gmock ${sina_gtests_depends_on}) @@ -63,22 +63,22 @@ if (ENABLE_GMOCK) #------------------------------------------------------------------------------ set(gmock_sina_tests - src/sina_ConduitUtil.cpp - src/sina_Curve.cpp - src/sina_CurveSet.cpp - src/sina_DataHolder.cpp - src/sina_Datum.cpp - src/sina_Document.cpp - src/sina_ID.cpp - src/sina_Record.cpp - src/sina_Relationship.cpp - src/sina_Run.cpp + sina_ConduitUtil.cpp + sina_Curve.cpp + sina_CurveSet.cpp + sina_DataHolder.cpp + sina_Datum.cpp + sina_Document.cpp + sina_ID.cpp + sina_Record.cpp + sina_Relationship.cpp + sina_Run.cpp ) # set(sina_gmock_depends_on ${sina_gtests_depends_on} gmock) # Add tests using Adiak if necessary and Adiak dependency - blt_list_append( TO gmock_sina_tests ELEMENTS src/sina_AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK) + blt_list_append( TO gmock_sina_tests ELEMENTS sina_AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK) blt_list_append( TO sina_gmock_depends_on ELEMENTS adiak::adiak IF AXOM_SINA_USE_ADIAK ) foreach(test ${gmock_sina_tests}) @@ -102,7 +102,7 @@ endif() if (ENABLE_FORTRAN) find_package(PythonInterp REQUIRED) configure_file(${CMAKE_SOURCE_DIR}/axom/sina/tests/test_fortran_integration.py ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py COPYONLY) - configure_file(${CMAKE_SOURCE_DIR}/axom/sina/interface/src/sina_schema.json ${TEST_OUTPUT_DIRECTORY}/sina_schema.json COPYONLY) + configure_file(${CMAKE_SOURCE_DIR}/axom/sina/interface/sina_schema.json ${TEST_OUTPUT_DIRECTORY}/sina_schema.json COPYONLY) axom_add_test( NAME sina_fortran_integration_test COMMAND ${PYTHON_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR}) diff --git a/src/axom/sina/tests/src/ConduitTestUtils.cpp b/src/axom/sina/tests/ConduitTestUtils.cpp similarity index 93% rename from src/axom/sina/tests/src/ConduitTestUtils.cpp rename to src/axom/sina/tests/ConduitTestUtils.cpp index 2ab85d407f..c75e011b1f 100644 --- a/src/axom/sina/tests/src/ConduitTestUtils.cpp +++ b/src/axom/sina/tests/ConduitTestUtils.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/sina/tests/include/ConduitTestUtils.hpp" +#include "axom/sina/tests/ConduitTestUtils.hpp" namespace axom { diff --git a/src/axom/sina/tests/include/ConduitTestUtils.hpp b/src/axom/sina/tests/ConduitTestUtils.hpp similarity index 100% rename from src/axom/sina/tests/include/ConduitTestUtils.hpp rename to src/axom/sina/tests/ConduitTestUtils.hpp diff --git a/src/axom/sina/tests/src/TestRecord.cpp b/src/axom/sina/tests/TestRecord.cpp similarity index 93% rename from src/axom/sina/tests/src/TestRecord.cpp rename to src/axom/sina/tests/TestRecord.cpp index e24e50f6c0..23242b627b 100644 --- a/src/axom/sina/tests/src/TestRecord.cpp +++ b/src/axom/sina/tests/TestRecord.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/sina/tests/include/TestRecord.hpp" +#include "axom/sina/tests/TestRecord.hpp" namespace axom { diff --git a/src/axom/sina/tests/include/TestRecord.hpp b/src/axom/sina/tests/TestRecord.hpp similarity index 95% rename from src/axom/sina/tests/include/TestRecord.hpp rename to src/axom/sina/tests/TestRecord.hpp index 9f28bd1ea6..b89f543c0d 100644 --- a/src/axom/sina/tests/include/TestRecord.hpp +++ b/src/axom/sina/tests/TestRecord.hpp @@ -7,8 +7,8 @@ #ifndef SINA_TESTRECORD_HPP #define SINA_TESTRECORD_HPP -#include "axom/sina/include/ConduitUtil.hpp" -#include "axom/sina/include/Record.hpp" +#include "axom/sina/core/ConduitUtil.hpp" +#include "axom/sina/core/Record.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_AdiakWriter.cpp b/src/axom/sina/tests/sina_AdiakWriter.cpp similarity index 97% rename from src/axom/sina/tests/src/sina_AdiakWriter.cpp rename to src/axom/sina/tests/sina_AdiakWriter.cpp index cc86402753..7dd5290a17 100644 --- a/src/axom/sina/tests/src/sina_AdiakWriter.cpp +++ b/src/axom/sina/tests/sina_AdiakWriter.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/sina/include/AdiakWriter.hpp" +#include "axom/sina/core/AdiakWriter.hpp" #ifdef AXOM_SINA_USE_ADIAK @@ -21,9 +21,9 @@ extern "C" { #include "adiak.h" } -#include "axom/sina/include/Datum.hpp" -#include "axom/sina/include/ID.hpp" -#include "axom/sina/include/Run.hpp" +#include "axom/sina/core/Datum.hpp" +#include "axom/sina/core/ID.hpp" +#include "axom/sina/core/Run.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_ConduitUtil.cpp b/src/axom/sina/tests/sina_ConduitUtil.cpp similarity index 98% rename from src/axom/sina/tests/src/sina_ConduitUtil.cpp rename to src/axom/sina/tests/sina_ConduitUtil.cpp index 885e9bb3ce..41b9284ff1 100644 --- a/src/axom/sina/tests/src/sina_ConduitUtil.cpp +++ b/src/axom/sina/tests/sina_ConduitUtil.cpp @@ -9,8 +9,8 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/ConduitUtil.hpp" -#include "axom/sina/tests/include/ConduitTestUtils.hpp" +#include "axom/sina/core/ConduitUtil.hpp" +#include "axom/sina/tests/ConduitTestUtils.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_CppBridge.cpp b/src/axom/sina/tests/sina_CppBridge.cpp similarity index 94% rename from src/axom/sina/tests/src/sina_CppBridge.cpp rename to src/axom/sina/tests/sina_CppBridge.cpp index 652638f963..1a9629c7f4 100644 --- a/src/axom/sina/tests/src/sina_CppBridge.cpp +++ b/src/axom/sina/tests/sina_CppBridge.cpp @@ -8,7 +8,7 @@ #include "gtest/gtest.h" -#include "axom/sina/include/CppBridge.hpp" +#include "axom/sina/core/CppBridge.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_Curve.cpp b/src/axom/sina/tests/sina_Curve.cpp similarity index 95% rename from src/axom/sina/tests/src/sina_Curve.cpp rename to src/axom/sina/tests/sina_Curve.cpp index c7d8587abd..c6ddc9e97b 100644 --- a/src/axom/sina/tests/src/sina_Curve.cpp +++ b/src/axom/sina/tests/sina_Curve.cpp @@ -7,9 +7,9 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/Curve.hpp" -#include "axom/sina/include/ConduitUtil.hpp" -#include "axom/sina/tests/include/ConduitTestUtils.hpp" +#include "axom/sina/core/Curve.hpp" +#include "axom/sina/core/ConduitUtil.hpp" +#include "axom/sina/tests/ConduitTestUtils.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_CurveSet.cpp b/src/axom/sina/tests/sina_CurveSet.cpp similarity index 98% rename from src/axom/sina/tests/src/sina_CurveSet.cpp rename to src/axom/sina/tests/sina_CurveSet.cpp index f90d493026..e4dd4d8d86 100644 --- a/src/axom/sina/tests/src/sina_CurveSet.cpp +++ b/src/axom/sina/tests/sina_CurveSet.cpp @@ -7,8 +7,8 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/CurveSet.hpp" -#include "axom/sina/tests/include/ConduitTestUtils.hpp" +#include "axom/sina/core/CurveSet.hpp" +#include "axom/sina/tests/ConduitTestUtils.hpp" #include #include diff --git a/src/axom/sina/tests/src/sina_DataHolder.cpp b/src/axom/sina/tests/sina_DataHolder.cpp similarity index 99% rename from src/axom/sina/tests/src/sina_DataHolder.cpp rename to src/axom/sina/tests/sina_DataHolder.cpp index 0c29288a1a..ff57df3f16 100644 --- a/src/axom/sina/tests/src/sina_DataHolder.cpp +++ b/src/axom/sina/tests/sina_DataHolder.cpp @@ -10,9 +10,9 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/DataHolder.hpp" +#include "axom/sina/core/DataHolder.hpp" -#include "axom/sina/tests/include/ConduitTestUtils.hpp" +#include "axom/sina/tests/ConduitTestUtils.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_Datum.cpp b/src/axom/sina/tests/sina_Datum.cpp similarity index 98% rename from src/axom/sina/tests/src/sina_Datum.cpp rename to src/axom/sina/tests/sina_Datum.cpp index fd1993a14b..c1d2acd81c 100644 --- a/src/axom/sina/tests/src/sina_Datum.cpp +++ b/src/axom/sina/tests/sina_Datum.cpp @@ -11,8 +11,8 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/Datum.hpp" -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/Datum.hpp" +#include "axom/sina/core/ConduitUtil.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_Document.cpp b/src/axom/sina/tests/sina_Document.cpp similarity index 98% rename from src/axom/sina/tests/src/sina_Document.cpp rename to src/axom/sina/tests/sina_Document.cpp index 6520c93b84..9c20775d28 100644 --- a/src/axom/sina/tests/src/sina_Document.cpp +++ b/src/axom/sina/tests/sina_Document.cpp @@ -13,11 +13,11 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/CppBridge.hpp" -#include "axom/sina/include/Document.hpp" -#include "axom/sina/include/Run.hpp" +#include "axom/sina/core/CppBridge.hpp" +#include "axom/sina/core/Document.hpp" +#include "axom/sina/core/Run.hpp" -#include "axom/sina/tests/include/TestRecord.hpp" +#include "axom/sina/tests/TestRecord.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_File.cpp b/src/axom/sina/tests/sina_File.cpp similarity index 96% rename from src/axom/sina/tests/src/sina_File.cpp rename to src/axom/sina/tests/sina_File.cpp index 0283ef65aa..72a67505b4 100644 --- a/src/axom/sina/tests/src/sina_File.cpp +++ b/src/axom/sina/tests/sina_File.cpp @@ -6,8 +6,8 @@ #include "gtest/gtest.h" -#include "axom/sina/include/File.hpp" -#include "axom/sina/include/ConduitUtil.hpp" +#include "axom/sina/core/File.hpp" +#include "axom/sina/core/ConduitUtil.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_ID.cpp b/src/axom/sina/tests/sina_ID.cpp similarity index 98% rename from src/axom/sina/tests/src/sina_ID.cpp rename to src/axom/sina/tests/sina_ID.cpp index b920c74d37..9a0eb50f72 100644 --- a/src/axom/sina/tests/src/sina_ID.cpp +++ b/src/axom/sina/tests/sina_ID.cpp @@ -11,7 +11,7 @@ #include "conduit.hpp" -#include "axom/sina/include/ID.hpp" +#include "axom/sina/core/ID.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_Record.cpp b/src/axom/sina/tests/sina_Record.cpp similarity index 98% rename from src/axom/sina/tests/src/sina_Record.cpp rename to src/axom/sina/tests/sina_Record.cpp index 04bd65c963..212547b500 100644 --- a/src/axom/sina/tests/src/sina_Record.cpp +++ b/src/axom/sina/tests/sina_Record.cpp @@ -10,11 +10,11 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/Record.hpp" -#include "axom/sina/include/CppBridge.hpp" +#include "axom/sina/core/Record.hpp" +#include "axom/sina/core/CppBridge.hpp" -#include "axom/sina/tests/include/ConduitTestUtils.hpp" -#include "axom/sina/tests/include/TestRecord.hpp" +#include "axom/sina/tests/ConduitTestUtils.hpp" +#include "axom/sina/tests/TestRecord.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_Relationship.cpp b/src/axom/sina/tests/sina_Relationship.cpp similarity index 99% rename from src/axom/sina/tests/src/sina_Relationship.cpp rename to src/axom/sina/tests/sina_Relationship.cpp index 80ae1f4553..60220cd754 100644 --- a/src/axom/sina/tests/src/sina_Relationship.cpp +++ b/src/axom/sina/tests/sina_Relationship.cpp @@ -9,7 +9,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/Relationship.hpp" +#include "axom/sina/core/Relationship.hpp" namespace axom { diff --git a/src/axom/sina/tests/src/sina_Run.cpp b/src/axom/sina/tests/sina_Run.cpp similarity index 98% rename from src/axom/sina/tests/src/sina_Run.cpp rename to src/axom/sina/tests/sina_Run.cpp index 8896ff4124..6ab15ce7c5 100644 --- a/src/axom/sina/tests/src/sina_Run.cpp +++ b/src/axom/sina/tests/sina_Run.cpp @@ -9,7 +9,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/include/Run.hpp" +#include "axom/sina/core/Run.hpp" namespace axom { From 820ce9fed957e7cdeab6ce093b61501a37879057 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 11 Jul 2024 09:51:46 -0700 Subject: [PATCH 21/60] remove CppBridge for C++<14 --- src/axom/sina/CMakeLists.txt | 1 - src/axom/sina/core/AdiakWriter.cpp | 1 - src/axom/sina/core/CppBridge.hpp | 46 -------------------------- src/axom/sina/core/Record.cpp | 3 +- src/axom/sina/core/Run.cpp | 3 +- src/axom/sina/tests/CMakeLists.txt | 3 -- src/axom/sina/tests/sina_CppBridge.cpp | 39 ---------------------- src/axom/sina/tests/sina_Document.cpp | 13 ++++---- src/axom/sina/tests/sina_Record.cpp | 5 ++- 9 files changed, 10 insertions(+), 104 deletions(-) delete mode 100644 src/axom/sina/core/CppBridge.hpp delete mode 100644 src/axom/sina/tests/sina_CppBridge.cpp diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index aa1c608b8b..5227161e3d 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -30,7 +30,6 @@ axom_configure_file ( config.hpp.in ${PROJECT_BINARY_DIR}/include/axom/sina/conf #------------------------------------------------------------------------------ set(sina_headers core/ConduitUtil.hpp - core/CppBridge.hpp core/Curve.hpp core/CurveSet.hpp core/DataHolder.hpp diff --git a/src/axom/sina/core/AdiakWriter.cpp b/src/axom/sina/core/AdiakWriter.cpp index bf0153e18e..2f44e06413 100644 --- a/src/axom/sina/core/AdiakWriter.cpp +++ b/src/axom/sina/core/AdiakWriter.cpp @@ -20,7 +20,6 @@ extern "C" { #include "adiak_tool.h" } -#include "axom/sina/core/CppBridge.hpp" #include "axom/sina/core/ConduitUtil.hpp" #include "axom/sina/core/Record.hpp" #include "axom/sina/core/Datum.hpp" diff --git a/src/axom/sina/core/CppBridge.hpp b/src/axom/sina/core/CppBridge.hpp deleted file mode 100644 index ef090b7900..0000000000 --- a/src/axom/sina/core/CppBridge.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level LICENSE file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - - -#ifndef SINA_CPPBRIDGE_HPP -#define SINA_CPPBRIDGE_HPP - -/** - * @file - * - * This file contains functions which can go away once this library is - * migrated to C++14 or later. - */ - -#include -#include - -namespace axom -{ -namespace sina -{ -namespace internal -{ - -/** - * Make a unique pointer pointing to a new object. - * - * Delete when switch to C++14 in favor of std::make_unique. - * - * @tparam T the type of the object - * @tparam ParamTypes the types of the parameters - * @param params the parameters to the constructor - * @return a unique_ptr to the newly-constructed object - */ -template -std::unique_ptr make_unique(ParamTypes &&... params) { - return std::unique_ptr(new T(std::forward(params)...)); -}; - -} // end internal namespace -} // end sina namespace -} // end axom namespace - -#endif //SINA_CPPBRIDGE_HPP diff --git a/src/axom/sina/core/Record.cpp b/src/axom/sina/core/Record.cpp index aafd3538c5..13bf3886d0 100644 --- a/src/axom/sina/core/Record.cpp +++ b/src/axom/sina/core/Record.cpp @@ -11,7 +11,6 @@ #include #include -#include "axom/sina/core/CppBridge.hpp" #include "axom/sina/core/ConduitUtil.hpp" #include "axom/sina/core/DataHolder.hpp" #include "axom/sina/core/Run.hpp" @@ -99,7 +98,7 @@ RecordLoader::load(conduit::Node const &recordAsNode) const { if (loaderIter != typeLoaders.end()) { return loaderIter->second(recordAsNode); } - return internal::make_unique(recordAsNode); + return std::make_unique(recordAsNode); } bool RecordLoader::canLoad(std::string const &type) const { diff --git a/src/axom/sina/core/Run.cpp b/src/axom/sina/core/Run.cpp index c23e27ff98..53a7b62591 100644 --- a/src/axom/sina/core/Run.cpp +++ b/src/axom/sina/core/Run.cpp @@ -10,7 +10,6 @@ #include -#include "axom/sina/core/CppBridge.hpp" #include "axom/sina/core/ConduitUtil.hpp" namespace axom @@ -47,7 +46,7 @@ conduit::Node Run::toNode() const { void addRunLoader(RecordLoader &loader) { loader.addTypeLoader(RUN_TYPE, [](conduit::Node const &value) { - return internal::make_unique(value); + return std::make_unique(value); }); } diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index e1cf840853..cba02c1e9c 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -11,7 +11,6 @@ # Add gtest C++ tests #------------------------------------------------------------------------------ set(gtest_sina_tests - sina_CppBridge.cpp sina_File.cpp ) @@ -75,8 +74,6 @@ if (ENABLE_GMOCK) sina_Run.cpp ) - # set(sina_gmock_depends_on ${sina_gtests_depends_on} gmock) - # Add tests using Adiak if necessary and Adiak dependency blt_list_append( TO gmock_sina_tests ELEMENTS sina_AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK) blt_list_append( TO sina_gmock_depends_on ELEMENTS adiak::adiak IF AXOM_SINA_USE_ADIAK ) diff --git a/src/axom/sina/tests/sina_CppBridge.cpp b/src/axom/sina/tests/sina_CppBridge.cpp deleted file mode 100644 index 1a9629c7f4..0000000000 --- a/src/axom/sina/tests/sina_CppBridge.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2017-2024, 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 - -#include "gtest/gtest.h" - -#include "axom/sina/core/CppBridge.hpp" - -namespace axom -{ -namespace sina -{ -namespace internal -{ -namespace testing -{ -namespace -{ - - -TEST(CppBridge, make_unique_noParams) { - std::unique_ptr ptr = make_unique(); - EXPECT_TRUE(ptr->empty()); -} - -TEST(CppBridge, make_unique_withParam) { - std::unique_ptr ptr = make_unique("foo"); - EXPECT_EQ("foo", *ptr); -} - -} // end nameless namespace -} // end testing namespace -} // end internal namespace -} // end sina namespace -} // end axom namespace diff --git a/src/axom/sina/tests/sina_Document.cpp b/src/axom/sina/tests/sina_Document.cpp index 9c20775d28..240b2bb068 100644 --- a/src/axom/sina/tests/sina_Document.cpp +++ b/src/axom/sina/tests/sina_Document.cpp @@ -13,7 +13,6 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "axom/sina/core/CppBridge.hpp" #include "axom/sina/core/Document.hpp" #include "axom/sina/core/Run.hpp" @@ -70,7 +69,7 @@ TEST(Document, create_fromNode_withRecords) { RecordLoader loader; loader.addTypeLoader("IntTestRecord", [](conduit::Node const &asNode) { - return internal::make_unique>(asNode); + return std::make_unique>(asNode); }); Document document{documentAsNode, loader}; @@ -153,7 +152,7 @@ TEST(Document, toNode_records) { auto numRecords = sizeof(expectedIds) / sizeof(expectedIds[0]); for (std::size_t i = 0; i < numRecords; ++i) { - document.add(internal::make_unique>( + document.add(std::make_unique>( expectedIds[i], TEST_RECORD_TYPE, expectedValues[i])); } @@ -255,7 +254,7 @@ TEST(Document, saveDocument) { } Document document; - document.add(internal::make_unique(ID{"the id", IDType::Global}, + document.add(std::make_unique(ID{"the id", IDType::Global}, "the type")); saveDocument(document, tmpFile.getName()); @@ -277,7 +276,7 @@ TEST(Document, saveDocument) { TEST(Document, load_specifiedRecordLoader) { using RecordType = TestRecord; - auto originalRecord = internal::make_unique( + auto originalRecord = std::make_unique( "the ID", "my type", 123); Document originalDocument; originalDocument.add(std::move(originalRecord)); @@ -290,7 +289,7 @@ TEST(Document, load_specifiedRecordLoader) { RecordLoader loader; loader.addTypeLoader("my type", [](conduit::Node const &asNode) { - return internal::make_unique( + return std::make_unique( getRequiredString("id", asNode, "Test type"), getRequiredString("type", asNode, "Test type"), static_cast(getRequiredField(TEST_RECORD_VALUE_KEY, asNode, @@ -305,7 +304,7 @@ TEST(Document, load_specifiedRecordLoader) { } TEST(Document, load_defaultRecordLoaders) { - auto originalRun = internal::make_unique( + auto originalRun = std::make_unique( ID{"the ID", IDType::Global}, "the app", "1.2.3", "jdoe"); Document originalDocument; originalDocument.add(std::move(originalRun)); diff --git a/src/axom/sina/tests/sina_Record.cpp b/src/axom/sina/tests/sina_Record.cpp index 212547b500..7288e9ba05 100644 --- a/src/axom/sina/tests/sina_Record.cpp +++ b/src/axom/sina/tests/sina_Record.cpp @@ -11,7 +11,6 @@ #include "gmock/gmock.h" #include "axom/sina/core/Record.hpp" -#include "axom/sina/core/CppBridge.hpp" #include "axom/sina/tests/ConduitTestUtils.hpp" #include "axom/sina/tests/TestRecord.hpp" @@ -434,13 +433,13 @@ TEST(RecordLoader, load_loaderPresent) { loader.addTypeLoader("TestInt", [](conduit::Node const &value) { - return internal::make_unique>(value); + return std::make_unique>(value); }); EXPECT_TRUE(loader.canLoad("TestInt")); loader.addTypeLoader("TestString", [](conduit::Node const &value) { - return internal::make_unique>(value); + return std::make_unique>(value); }); EXPECT_TRUE(loader.canLoad("TestString")); From 10dccf73783c4963297176e031d3127c6fc5da8a Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 11 Jul 2024 12:29:21 -0700 Subject: [PATCH 22/60] replace AXOM_SINA_USE_ADIAK with AXOM_USE_ADIAK --- src/axom/sina/CMakeLists.txt | 23 ++++------------------- src/axom/sina/core/AdiakWriter.cpp | 4 ++-- src/axom/sina/core/AdiakWriter.hpp | 6 +++--- src/axom/sina/tests/CMakeLists.txt | 4 ++-- src/axom/sina/tests/sina_AdiakWriter.cpp | 4 ++-- 5 files changed, 13 insertions(+), 28 deletions(-) diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index 5227161e3d..8dfba39f20 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -9,22 +9,11 @@ #------------------------------------------------------------------------------ # Specify necessary dependencies # -# Note: Sina also optionally depends on Adiak when AXOM_SINA_USE_ADIAK=ON +# Note: Sina also optionally depends on Adiak when AXOM_USE_ADIAK=ON #------------------------------------------------------------------------------ axom_component_requires(NAME Sina TPLS Conduit ) -#------------------------------------------------------------------------------ -# Add Sina options -#------------------------------------------------------------------------------ -cmake_dependent_option( AXOM_SINA_USE_ADIAK "Enables Adiak support in Sina" ON - "AXOM_USE_ADIAK" OFF) - -#------------------------------------------------------------------------------ -# Generate Sina config -#------------------------------------------------------------------------------ -axom_configure_file ( config.hpp.in ${PROJECT_BINARY_DIR}/include/axom/sina/config.hpp ) - #------------------------------------------------------------------------------ # Specify the sina headers/sources #------------------------------------------------------------------------------ @@ -57,8 +46,8 @@ set(sina_sources ) # Add Adiak header and source -blt_list_append( TO sina_headers ELEMENTS core/AdiakWriter.hpp IF AXOM_SINA_USE_ADIAK ) -blt_list_append( TO sina_sources ELEMENTS core/AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK ) +blt_list_append( TO sina_headers ELEMENTS core/AdiakWriter.hpp IF AXOM_USE_ADIAK ) +blt_list_append( TO sina_sources ELEMENTS core/AdiakWriter.cpp IF AXOM_USE_ADIAK ) # Add fortran interface for Sina if (ENABLE_FORTRAN) @@ -75,7 +64,7 @@ set(sina_depends conduit::conduit ) -blt_list_append( TO sina_depends ELEMENTS adiak::adiak IF AXOM_SINA_USE_ADIAK ) +blt_list_append( TO sina_depends ELEMENTS adiak::adiak IF AXOM_USE_ADIAK ) axom_add_library(NAME sina SOURCES ${sina_sources} @@ -89,10 +78,6 @@ axom_write_unified_header(NAME sina axom_install_component(NAME sina HEADERS ${sina_headers}) -install(FILES ${PROJECT_BINARY_DIR}/include/axom/sina/config.hpp - DESTINATION include/axom/sina - ) - #------------------------------------------------------------------------------ # Add tests and examples #------------------------------------------------------------------------------ diff --git a/src/axom/sina/core/AdiakWriter.cpp b/src/axom/sina/core/AdiakWriter.cpp index 2f44e06413..3a70bee292 100644 --- a/src/axom/sina/core/AdiakWriter.cpp +++ b/src/axom/sina/core/AdiakWriter.cpp @@ -8,7 +8,7 @@ #include "axom/sina/core/AdiakWriter.hpp" -#ifdef AXOM_SINA_USE_ADIAK +#ifdef AXOM_USE_ADIAK #include #include @@ -269,4 +269,4 @@ void adiakSinaCallback(const char *name, adiak_category_t, const char *subcatego } // end sina namespace } // end axom namespace -#endif // AXOM_SINA_USE_ADIAK +#endif // AXOM_USE_ADIAK diff --git a/src/axom/sina/core/AdiakWriter.hpp b/src/axom/sina/core/AdiakWriter.hpp index 2acd396de1..72e80e975b 100644 --- a/src/axom/sina/core/AdiakWriter.hpp +++ b/src/axom/sina/core/AdiakWriter.hpp @@ -9,8 +9,8 @@ /// @file -#include "axom/sina/config.hpp" -#ifdef AXOM_SINA_USE_ADIAK +#include "axom/config.hpp" +#ifdef AXOM_USE_ADIAK #include #include @@ -50,7 +50,7 @@ void adiakSinaCallback(const char *name, adiak_category_t category, const char * } // end sina namespace } // end axom namespace -#endif // AXOM_SINA_USE_ADIAK +#endif // AXOM_USE_ADIAK #endif // SINA_ADIAK_HPP diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index cba02c1e9c..583d22c764 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -75,8 +75,8 @@ if (ENABLE_GMOCK) ) # Add tests using Adiak if necessary and Adiak dependency - blt_list_append( TO gmock_sina_tests ELEMENTS sina_AdiakWriter.cpp IF AXOM_SINA_USE_ADIAK) - blt_list_append( TO sina_gmock_depends_on ELEMENTS adiak::adiak IF AXOM_SINA_USE_ADIAK ) + blt_list_append( TO gmock_sina_tests ELEMENTS sina_AdiakWriter.cpp IF AXOM_USE_ADIAK) + blt_list_append( TO sina_gmock_depends_on ELEMENTS adiak::adiak IF AXOM_USE_ADIAK ) foreach(test ${gmock_sina_tests}) get_filename_component( test_name ${test} NAME_WE ) diff --git a/src/axom/sina/tests/sina_AdiakWriter.cpp b/src/axom/sina/tests/sina_AdiakWriter.cpp index 7dd5290a17..bf42e59538 100644 --- a/src/axom/sina/tests/sina_AdiakWriter.cpp +++ b/src/axom/sina/tests/sina_AdiakWriter.cpp @@ -6,7 +6,7 @@ #include "axom/sina/core/AdiakWriter.hpp" -#ifdef AXOM_SINA_USE_ADIAK +#ifdef AXOM_USE_ADIAK #include #include @@ -157,4 +157,4 @@ TEST_F(AdiakWriterTest, files_list){ } // end sina namespace } // end axom namespace -#endif // AXOM_SINA_USE_ADIAK +#endif // AXOM_USE_ADIAK From d998facd15308f0c0e39a5486c0f3e7b367f5620 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 11 Jul 2024 17:18:32 -0700 Subject: [PATCH 23/60] change doxygen syntax to match axom's standard --- src/axom/sina/core/AdiakWriter.cpp | 11 ++- src/axom/sina/core/AdiakWriter.hpp | 20 ++++-- src/axom/sina/core/ConduitUtil.cpp | 10 +++ src/axom/sina/core/ConduitUtil.hpp | 104 +++++++++++++++------------ src/axom/sina/core/Curve.cpp | 9 +++ src/axom/sina/core/Curve.hpp | 58 ++++++++------- src/axom/sina/core/CurveSet.cpp | 11 +++ src/axom/sina/core/CurveSet.hpp | 52 ++++++++------ src/axom/sina/core/DataHolder.cpp | 9 +++ src/axom/sina/core/DataHolder.hpp | 77 ++++++++++---------- src/axom/sina/core/Datum.cpp | 10 ++- src/axom/sina/core/Datum.hpp | 106 +++++++++++++++------------- src/axom/sina/core/Document.cpp | 10 ++- src/axom/sina/core/Document.hpp | 106 +++++++++++++++------------- src/axom/sina/core/File.cpp | 10 ++- src/axom/sina/core/File.hpp | 58 ++++++++------- src/axom/sina/core/ID.cpp | 13 ++-- src/axom/sina/core/ID.hpp | 61 +++++++++------- src/axom/sina/core/Record.cpp | 12 +++- src/axom/sina/core/Record.hpp | 90 +++++++++++++---------- src/axom/sina/core/Relationship.cpp | 11 ++- src/axom/sina/core/Relationship.hpp | 58 ++++++++------- src/axom/sina/core/Run.cpp | 13 +++- src/axom/sina/core/Run.hpp | 50 ++++++++----- src/docs/doxygen/Doxyfile.in | 3 +- 25 files changed, 588 insertions(+), 384 deletions(-) diff --git a/src/axom/sina/core/AdiakWriter.cpp b/src/axom/sina/core/AdiakWriter.cpp index 3a70bee292..3286995979 100644 --- a/src/axom/sina/core/AdiakWriter.cpp +++ b/src/axom/sina/core/AdiakWriter.cpp @@ -3,8 +3,15 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - -/// @file +/*! + ****************************************************************************** + * + * \file AdiakWriter.cpp + * + * \brief Implementation file for the Adiak Sina callback function. + * + ****************************************************************************** + */ #include "axom/sina/core/AdiakWriter.hpp" diff --git a/src/axom/sina/core/AdiakWriter.hpp b/src/axom/sina/core/AdiakWriter.hpp index 72e80e975b..3612b60667 100644 --- a/src/axom/sina/core/AdiakWriter.hpp +++ b/src/axom/sina/core/AdiakWriter.hpp @@ -7,7 +7,15 @@ #ifndef SINA_ADIAK_HPP #define SINA_ADIAK_HPP -/// @file +/*! + ****************************************************************************** + * + * \file AdiakWriter.hpp + * + * \brief Implementation file for the Adiak Sina callback function. + * + ****************************************************************************** + */ #include "axom/config.hpp" #ifdef AXOM_USE_ADIAK @@ -29,7 +37,7 @@ namespace sina { /** - * The callback function to pass to Adiak in order to write collected data to a Sina Record. + * \brief The callback function to pass to Adiak in order to write collected data to a Sina Record. * * To register this with Adiak, you should call adiak_register_cb, passing in * a pointer to the record as the last value. Your code should look something @@ -40,10 +48,10 @@ namespace sina * axom::sina::adiak_register_cb(1, adiak_category_all, axom::sina::adiakSinaCallback, 0, &record); * \endcode * - * Note that not everything that Sina can capture an be captured through the - * Adiak API. For example, there is currently no support in Adiak to capture - * anything like a CurveSet. As a result, to do that, you must hold on to - * the Record object passed here as the opaque value and manipulate it directly. + * \attention Not everything that Sina can capture an be captured through the + * Adiak API. For example, there is currently no support in Adiak to capture + * anything like a CurveSet. As a result, to do that, you must hold on to + * the Record object passed here as the opaque value and manipulate it directly. **/ void adiakSinaCallback(const char *name, adiak_category_t category, const char *subcategory, adiak_value_t *value, adiak_datatype_t *t, void *opaque_value); diff --git a/src/axom/sina/core/ConduitUtil.cpp b/src/axom/sina/core/ConduitUtil.cpp index e2f7c07762..36a65bd7e7 100644 --- a/src/axom/sina/core/ConduitUtil.cpp +++ b/src/axom/sina/core/ConduitUtil.cpp @@ -4,6 +4,16 @@ // SPDX-License-Identifier: (BSD-3-Clause) +/*! + ****************************************************************************** + * + * \file ConduitUtil.cpp + * + * \brief Implementation file for Sina Conduit utility functions + * + ****************************************************************************** + */ + #include "axom/sina/core/ConduitUtil.hpp" #include diff --git a/src/axom/sina/core/ConduitUtil.hpp b/src/axom/sina/core/ConduitUtil.hpp index d35167f229..79c04ddf63 100644 --- a/src/axom/sina/core/ConduitUtil.hpp +++ b/src/axom/sina/core/ConduitUtil.hpp @@ -7,6 +7,16 @@ #ifndef SINA_JSONUTIL_HPP #define SINA_JSONUTIL_HPP +/*! + ****************************************************************************** + * + * \file ConduitUtil.hpp + * + * \brief Implementation file for Sina Conduit utility functions + * + ****************************************************************************** + */ + #include #include @@ -18,90 +28,90 @@ namespace sina { /** - * Get a required field from a conduit Node. + * \brief Get a required field from a conduit Node. * - * @param fieldName the name of the field to get - * @param parent the parent object from which to get the field - * @param parentType a user-friendly name of the type of the parent to use - * in an error message if the field doesn't exist. - * @return the requested field as a Node - * @throws std::invalid_argument if the field does not exist + * \param fieldName the name of the field to get + * \param parent the parent object from which to get the field + * \param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * \return the requested field as a Node + * \throws std::invalid_argument if the field does not exist */ conduit::Node const &getRequiredField(std::string const &fieldName, conduit::Node const &parent, std::string const &parentType); /** - * Get the value of a required field from a conduit Node. The field value - * must be a string. + * \brief Get the value of a required field from a conduit Node. The field value + * must be a string. * - * @param fieldName the name of the field to get - * @param parent the parent object from which to get the field - * @param parentType a user-friendly name of the type of the parent to use - * in an error message if the field doesn't exist. - * @return the value of the requested field - * @throws std::invalid_argument if the field does not exist or is not a string + * \param fieldName the name of the field to get + * \param parent the parent object from which to get the field + * \param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * \return the value of the requested field + * \throws std::invalid_argument if the field does not exist or is not a string */ std::string getRequiredString(std::string const &fieldName, conduit::Node const &parent, std::string const &parentType); /** - * Get the value of a required field from a conduit Node. The field value - * must be a double. + * \brief Get the value of a required field from a conduit Node. The field value + * must be a double. * - * @param fieldName the name of the field to get - * @param parent the parent object from which to get the field - * @param parentType a user-friendly name of the type of the parent to use - * in an error message if the field doesn't exist. - * @return the value of the requested field - * @throws std::invalid_argument if the field does not exist or is not a double + * \param fieldName the name of the field to get + * \param parent the parent object from which to get the field + * \param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * \return the value of the requested field + * \throws std::invalid_argument if the field does not exist or is not a double */ double getRequiredDouble(std::string const &fieldName, conduit::Node const &parent, std::string const &parentType); /** - * Get the value of an optional field from a conduit Node. The field value - * must be a string if it is present. + * \brief Get the value of an optional field from a conduit Node. The field value + * must be a string if it is present. * - * @param fieldName the name of the field to get - * @param parent the parent object from which to get the field - * @param parentType a user-friendly name of the type of the parent to use - * in an error message if the field doesn't exist. - * @return the value of the requested field, or an empty string if it - * does not exist - * @throws std::invalid_argument if the field exists but is not a string + * \param fieldName the name of the field to get + * \param parent the parent object from which to get the field + * \param parentType a user-friendly name of the type of the parent to use + * in an error message if the field doesn't exist. + * \return the value of the requested field, or an empty string if it + * does not exist + * \throws std::invalid_argument if the field exists but is not a string */ std::string getOptionalString(std::string const &fieldName, conduit::Node const &parent, std::string const &parentType); /** - * Convert the given node to a vector of doubles. + * \brief Convert the given node to a vector of doubles. * - * @param node the node to convert - * @param name the name of the node, used in error reporting - * @return the node as a list of doubles - * @throws std::invalid_argument if the node is not a list of doubles + * \param node the node to convert + * \param name the name of the node, used in error reporting + * \return the node as a list of doubles + * \throws std::invalid_argument if the node is not a list of doubles */ std::vector toDoubleVector(conduit::Node const &node, std::string const &name); /** - * Convert the given node to a vector of strings. + * \brief Convert the given node to a vector of strings. * - * @param node the node to convert - * @param name the name of the node, used in error reporting - * @return the node as a list of strings - * @throws std::invalid_argument if the node is not a list of strings + * \param node the node to convert + * \param name the name of the node, used in error reporting + * \return the node as a list of strings + * \throws std::invalid_argument if the node is not a list of strings */ std::vector toStringVector(conduit::Node const &node, std::string const &name); /** - * Add a vector of strings to a Node. This operation's not natively - * part of Conduit. + * \brief Add a vector of strings to a Node. This operation's not natively + * part of Conduit. * - * @param parent the node to add the strings to - * @param child_name the name of the child (aka the name of the field) - * @param string_values the data values for the field + * \param parent the node to add the strings to + * \param child_name the name of the child (aka the name of the field) + * \param string_values the data values for the field */ void addStringsToNode(conduit::Node &parent, const std::string &child_name, std::vector const &string_values); diff --git a/src/axom/sina/core/Curve.cpp b/src/axom/sina/core/Curve.cpp index df33010e11..99a3e447a2 100644 --- a/src/axom/sina/core/Curve.cpp +++ b/src/axom/sina/core/Curve.cpp @@ -3,6 +3,15 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/*! + ****************************************************************************** + * + * \file Curve.cpp + * + * \brief Implementation file for Sina Curve class + * + ****************************************************************************** + */ #include "axom/sina/core/Curve.hpp" #include "axom/sina/core/ConduitUtil.hpp" diff --git a/src/axom/sina/core/Curve.hpp b/src/axom/sina/core/Curve.hpp index 996fd23d27..5975e773d9 100644 --- a/src/axom/sina/core/Curve.hpp +++ b/src/axom/sina/core/Curve.hpp @@ -7,10 +7,14 @@ #ifndef SINA_CURVE_HPP #define SINA_CURVE_HPP -/** - * @file +/*! + ****************************************************************************** + * + * \file Curve.hpp + * + * \brief Implementation file for Sina Curve class * - * Contains the definition of the Curve class. + ****************************************************************************** */ #include @@ -29,84 +33,84 @@ namespace sina class Curve { public: /** - * Create a Curve with the given name and values + * \brief Create a Curve with the given name and values * - * @param name the name of the curve - * @param values the curve's values + * \param name the name of the curve + * \param values the curve's values */ Curve(std::string name, std::vector values); /** - * Create a Curve with the given name and values + * \brief Create a Curve with the given name and values * - * @param name the name of the curve - * @param values the curve's values - * @param numValues the number of values. + * \param name the name of the curve + * \param values the curve's values + * \param numValues the number of values. */ Curve(std::string name, double const *values, std::size_t numValues); /** - * Create a Curve by deserializing a conduit node. + * \brief Create a Curve by deserializing a conduit node. * - * @param name the name of the curve - * @param curveAsNode the serialized version of a curve + * \param name the name of the curve + * \param curveAsNode the serialized version of a curve */ Curve(std::string name, conduit::Node const &curveAsNode); /** - * Get the curve's name. + * \brief Get the curve's name. * - * @return the curve's name + * \return the curve's name */ std::string const &getName() const { return name; } /** - * Get the values of the curve. + * \brief Get the values of the curve. * - * @return the curve's values + * \return the curve's values */ std::vector const &getValues() const { return values; } /** - * Set the units of the values. + * \brief Set the units of the values. * - * @param units the value's units + * \param units the value's units */ void setUnits(std::string units); /** - * Get the units of the values. + * \brief Get the units of the values. * - * @return the value's units + * \return the value's units */ std::string const &getUnits() const { return units; } /** - * Set the tags for this curve. + * \brief Set the tags for this curve. * - * @param tags the curve's tags + * \param tags the curve's tags */ void setTags(std::vector tags); /** - * Get the tags for this curve. + * \brief Get the tags for this curve. * - * @return the curve's tags + * \return the curve's tags */ std::vector const &getTags() const { return tags; } /** - * Convert this curve to a Conduit node. + * \brief Convert this curve to a Conduit node. * - * @return a Conduit representation of this curve + * \return a Conduit representation of this curve */ conduit::Node toNode() const; diff --git a/src/axom/sina/core/CurveSet.cpp b/src/axom/sina/core/CurveSet.cpp index 1065e207c9..08e16fb9fd 100644 --- a/src/axom/sina/core/CurveSet.cpp +++ b/src/axom/sina/core/CurveSet.cpp @@ -3,6 +3,17 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/*! + ****************************************************************************** + * + * \file CurveSet.cpp + * + * \brief Implementation file for Sina CurveSet class + * + * \sa Curve.hpp + * + ****************************************************************************** + */ #include "axom/sina/core/CurveSet.hpp" diff --git a/src/axom/sina/core/CurveSet.hpp b/src/axom/sina/core/CurveSet.hpp index 02b72e2838..efc5538b20 100644 --- a/src/axom/sina/core/CurveSet.hpp +++ b/src/axom/sina/core/CurveSet.hpp @@ -7,10 +7,16 @@ #ifndef SINA_CURVESET_HPP #define SINA_CURVESET_HPP -/** - * @file +/*! + ****************************************************************************** + * + * \file CurveSet.hpp + * + * \brief Implementation file for Sina CurveSet class + * + * \sa Curve.hpp * - * Contains the definition of the CurveSet class. + ****************************************************************************** */ #include @@ -26,13 +32,13 @@ namespace sina { /** - * A CurveSet represents an entry in a record's "curve_set". + * \brief A CurveSet represents an entry in a record's "curve_set". * * A CurveSet consist of a set of independent and dependent curves. Each curve * is a list of numbers along with optional units and tags. * - * \see Record - * \see Curve + * \sa Record + * \sa Curve */ class CurveSet { public: @@ -42,65 +48,65 @@ class CurveSet { using CurveMap = std::unordered_map; /** - * Create a CurveSet with the given name + * \brief Create a CurveSet with the given name * - * @param name the name of the CurveSet + * \param name the name of the CurveSet */ explicit CurveSet(std::string name); /** - * Create a CurveSet from the given Conduit node. + * \brief Create a CurveSet from the given Conduit node. * - * @param name the name of the CurveSet - * @param node the Conduit node representing the CurveSet + * \param name the name of the CurveSet + * \param node the Conduit node representing the CurveSet */ CurveSet(std::string name, conduit::Node const &node); /** - * Get the name of the this CurveSet. + * \brief Get the name of the this CurveSet. * - * @return the curve set's name + * \return the curve set's name */ std::string const & getName() const { return name; } /** - * Add an independent curve. + * \brief Add an independent curve. * - * @param curve the curve to add + * \param curve the curve to add */ void addIndependentCurve(Curve curve); /** - * Add a dependent curve. + * \brief Add a dependent curve. * - * @param curve the curve to add + * \param curve the curve to add */ void addDependentCurve(Curve curve); /** - * Get a map of all the independent curves. + * \brief Get a map of all the independent curves. * - * @return a map of all the independent curves + * \return a map of all the independent curves */ CurveMap const &getIndependentCurves() const { return independentCurves; } /** - * Get a map of all the dependent curves. + * \brief Get a map of all the dependent curves. * - * @return a map of all the dependent curves + * \return a map of all the dependent curves */ CurveMap const &getDependentCurves() const { return dependentCurves; } /** - * Convert his CurveSet to a Conduit node. + * \brief Convert his CurveSet to a Conduit node. * - * @return the Node representation of this CurveSet + * \return the Node representation of this CurveSet */ conduit::Node toNode() const; diff --git a/src/axom/sina/core/DataHolder.cpp b/src/axom/sina/core/DataHolder.cpp index e868887770..c9aa3b50de 100644 --- a/src/axom/sina/core/DataHolder.cpp +++ b/src/axom/sina/core/DataHolder.cpp @@ -3,6 +3,15 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/*! + ****************************************************************************** + * + * \file DataHolder.cpp + * + * \brief Implementation file for Sina DataHolder class + * + ****************************************************************************** + */ #include "axom/sina/core/DataHolder.hpp" diff --git a/src/axom/sina/core/DataHolder.hpp b/src/axom/sina/core/DataHolder.hpp index cc5ac9661f..b315e5dc66 100644 --- a/src/axom/sina/core/DataHolder.hpp +++ b/src/axom/sina/core/DataHolder.hpp @@ -7,10 +7,14 @@ #ifndef SINA_DATAHOLDER_HPP #define SINA_DATAHOLDER_HPP -/** - * @file +/*! + ****************************************************************************** + * + * \file DataHolder.hpp + * + * \brief Implementation file for Sina DataHolder class * - * Contains the definition of the DataHolder class. + ****************************************************************************** */ #include @@ -28,14 +32,14 @@ namespace sina { /** - * A DataHolder is a basic container for certain types of information. + * \brief A DataHolder is a basic container for certain types of information. * * DataHolders contain curves, libraries, and data (Datum), and represent * all the information a library can have associated with it. Records expand * on DataHolders to contain additional info. * - * \see Record - * \see LibraryDataMap + * \sa Record + * \sa LibraryDataMap */ class DataHolder { public: @@ -75,120 +79,119 @@ class DataHolder { DataHolder &operator=(DataHolder const &) = delete; /** - * Construct a DataHolder from its conduit Node representation. + * \brief Construct a DataHolder from its conduit Node representation. * - * @param asNode the DataHolder as a Node + * \param asNode the DataHolder as a Node */ explicit DataHolder(conduit::Node const &asNode); /** - * Get the DataHolder's data. + * \brief Get the DataHolder's data. * - * @return the DataHolder's data + * \return the DataHolder's data */ DatumMap const &getData() const noexcept { return data; } /** - * Add a Datum to this DataHolder. + * \brief Add a Datum to this DataHolder. * - * @param name the key for the Datum to add - * @param datum the Datum to add + * \param name the key for the Datum to add + * \param datum the Datum to add */ void add(std::string name, Datum datum); /** - * Add a CurveSet to this DataHolder. + * \brief Add a CurveSet to this DataHolder. * - * @param curveSet the CurveSet to add + * \param curveSet the CurveSet to add */ void add(CurveSet curveSet); /** - * Get the curve sets associated with this DataHolder. + * \brief Get the curve sets associated with this DataHolder. * - * @return the dataholder's curve sets + * \return the dataholder's curve sets */ CurveSetMap const &getCurveSets() const noexcept { return curveSets; } /** - * Add a new library to this DataHolder. + * \brief Add a new library to this DataHolder. * * If you try to add a library with a name that already exists, the old * library will be replaced. * - * @return a pointer to a new DataHolder for a library - * of the given name. + * \return a pointer to a new DataHolder for a library + * of the given name. */ std::shared_ptr addLibraryData(std::string const &name); /** - * Add a new library to this DataHolder with existing library data. + * \brief Add a new library to this DataHolder with existing library data. * - * @return a pointer to a new DataHolder for a library of the given name. + * \return a pointer to a new DataHolder for a library of the given name. */ std::shared_ptr addLibraryData(std::string const &name, conduit::Node existingLibraryData); /** - * Get all library data associated with this DataHolder. + * \brief Get all library data associated with this DataHolder. * - * @return the dataholder's library data + * \return the dataholder's library data */ LibraryDataMap const &getLibraryData() const noexcept { return libraryData; } /** - * Get a specific library associated with this DataHolder. + * \brief Get a specific library associated with this DataHolder. * - * @return the dataholder's library data + * \return the dataholder's library data */ std::shared_ptr getLibraryData(std::string const &libraryName) { return libraryData.at(libraryName); } /** - * Get a specific library associated with this DataHolder. - * Used when the object on which this function is called is a const. + * \brief Get a specific library associated with this DataHolder. * - * @return the dataholder's library data + * \return the dataholder's library data */ std::shared_ptr const getLibraryData(std::string const &libraryName) const { return libraryData.at(libraryName); } /** - * Get the user-defined content of the object. + * \brief Get the user-defined content of the object. * - * @return the user-defined content + * \return the user-defined content */ conduit::Node const &getUserDefinedContent() const noexcept { return userDefined; } /** - * Get the user-defined content of the object. + * \brief Get the user-defined content of the object. * - * @return the user-defined content + * \return the user-defined content */ conduit::Node &getUserDefinedContent() noexcept { return userDefined; } /** - * Set the user-defined content of the object. + * \brief Set the user-defined content of the object. * - * @param userDefined the user-defined content. Must be an object (key/value pairs) + * \param userDefined the user-defined content. Must be an object (key/value pairs) */ void setUserDefinedContent(conduit::Node userDefined); /** - * Convert this DataHolder to its conduit Node representation. + * \brief Convert this DataHolder to its conduit Node representation. * - * @return the Node representation of this DataHolder. + * \return the Node representation of this DataHolder. */ virtual conduit::Node toNode() const; diff --git a/src/axom/sina/core/Datum.cpp b/src/axom/sina/core/Datum.cpp index dd6dfdce5d..5de49be3ea 100644 --- a/src/axom/sina/core/Datum.cpp +++ b/src/axom/sina/core/Datum.cpp @@ -4,7 +4,15 @@ // SPDX-License-Identifier: (BSD-3-Clause) -/// @file +/*! + ****************************************************************************** + * + * \file Datum.cpp + * + * \brief Implementation file for Sina Datum class + * + ****************************************************************************** + */ #include "axom/sina/core/Datum.hpp" #include "conduit.hpp" diff --git a/src/axom/sina/core/Datum.hpp b/src/axom/sina/core/Datum.hpp index 97503cb3df..8625e32cc1 100644 --- a/src/axom/sina/core/Datum.hpp +++ b/src/axom/sina/core/Datum.hpp @@ -7,7 +7,15 @@ #ifndef SINA_DATUM_HPP #define SINA_DATUM_HPP -/// @file +/*! + ****************************************************************************** + * + * \file Datum.hpp + * + * \brief Implementation file for Sina Datum class + * + ****************************************************************************** + */ #include #include @@ -32,6 +40,8 @@ enum class ValueType { }; /** + * \brief An object to track a value associated with a Record + * * A Datum tracks the value and (optionally) tags and/or units of a * value associated with a Record, e.g. a scalar, a piece of metadata, * or an input parameter. In the Sina schema, a Datum always @@ -43,148 +53,148 @@ enum class ValueType { * or an array of doubles. * * \code - * axom::sina::Datum myDatum{12.34}; - * std::string value = "foobar"; - * axom::sina::Datum myOtherDatum{value}; - * std::vector scalars = {1, 2, 20.0}; - * axom::sina::Datum myArrayDatum{scalars}; + * axom::sina::Datum myDatum{12.34}; + * std::string value = "foobar"; + * axom::sina::Datum myOtherDatum{value}; + * std::vector scalars = {1, 2, 20.0}; + * axom::sina::Datum myArrayDatum{scalars}; * - * //prints 1, corresponding to Scalar - * std::cout << static_cast::type>(myDatum.getType()) << std::endl; + * //prints 1, corresponding to Scalar + * std::cout << static_cast::type>(myDatum.getType()) << std::endl; * - * //prints 0, corresponding to String - * std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; + * //prints 0, corresponding to String + * std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; * - * //prints 3, corresponding to ScalarArray - * std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; + * //prints 3, corresponding to ScalarArray + * std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; * - * myRecord->add(myDatum); - * myOtherDatum.setUnits("km/s"); - * myRecord->add(myOtherDatum); - * std::vector tags = {"input", "core"}; - * myArrayDatum.setTags(tags); - * myRecord->add(myArrayDatum); + * myRecord->add(myDatum); + * myOtherDatum.setUnits("km/s"); + * myRecord->add(myOtherDatum); + * std::vector tags = {"input", "core"}; + * myArrayDatum.setTags(tags); + * myRecord->add(myArrayDatum); * \endcode */ class Datum { public: /** - * Construct a new Datum. + * \brief Construct a new Datum. * - * @param value the string value of the datum + * \param value the string value of the datum */ Datum(std::string value); /** - * Construct a new Datum. + * \brief Construct a new Datum. * - * @param value the double value of the datum + * \param value the double value of the datum */ Datum(double value); /** - * Construct a new Datum. + * \brief Construct a new Datum. * - * @param value the string array value of the datum + * \param value the string array value of the datum */ Datum(std::vector value); /** - * Construct a new Datum. + * \brief Construct a new Datum. * - * @param value the scalar array value of the datum + * \param value the scalar array value of the datum */ Datum(std::vector value); /** - * Construct a Datum from its Node representation. + * \brief Construct a Datum from its Node representation. * - * @param asNode the Datum as conduit Node + * \param asNode the Datum as conduit Node */ explicit Datum(conduit::Node const &asNode); /** - * Get the string value of the Datum. + * \brief Get the string value of the Datum. * - * @return the string value + * \return the string value */ std::string const &getValue() const noexcept { return stringValue; } /** - * Get the scalar value of the Datum. + * \brief Get the scalar value of the Datum. * - * @return the scalar value + * \return the scalar value */ double const &getScalar() const noexcept { return scalarValue; } /** - * Get the string array value of the Datum. + * \brief Get the string array value of the Datum. * - * @return the string vector value + * \return the string vector value */ std::vector const &getStringArray() const noexcept { return stringArrayValue; } /** - * Get the scalar array value of the Datum. + * \brief Get the scalar array value of the Datum. * - * @return the scalar vector value + * \return the scalar vector value */ std::vector const &getScalarArray() const noexcept { return scalarArrayValue; } /** - * Get the tags of the Datum + * \brief Get the tags of the Datum * - * @return the tags of the value + * \return the tags of the value */ std::vector const &getTags() const noexcept { return tags; } /** - * Set the tags of the Datum + * \brief Set the tags of the Datum * - * @param tags the tags of the value + * \param tags the tags of the value */ void setTags(std::vector tags); /** - * Get the units of the Datum + * \brief Get the units of the Datum * - * @return the units of the value + * \return the units of the value */ std::string const &getUnits() const noexcept { return units; } /** - * Set the units of the Datum + * \brief Set the units of the Datum * - * @param units the units of the value + * \param units the units of the value */ void setUnits(std::string units); /** - * Get the type of the Datum + * \brief Get the type of the Datum * - * @return the type of the value + * \return the type of the value */ ValueType getType() const noexcept { return type; } /** - * Convert this Datum to its conduit Node representation. + * \brief Convert this Datum to its conduit Node representation. * - * @return the Node representation of this Datum. + * \return the Node representation of this Datum. */ conduit::Node toNode() const; diff --git a/src/axom/sina/core/Document.cpp b/src/axom/sina/core/Document.cpp index a16eb1b6d4..30afa4b015 100644 --- a/src/axom/sina/core/Document.cpp +++ b/src/axom/sina/core/Document.cpp @@ -4,7 +4,15 @@ // SPDX-License-Identifier: (BSD-3-Clause) -/// @file +/*! + ****************************************************************************** + * + * \file Document.cpp + * + * \brief Implementation file for Sina Document class + * + ****************************************************************************** + */ #include "axom/sina/core/Document.hpp" diff --git a/src/axom/sina/core/Document.hpp b/src/axom/sina/core/Document.hpp index d91eade4f3..89c19aaf22 100644 --- a/src/axom/sina/core/Document.hpp +++ b/src/axom/sina/core/Document.hpp @@ -7,7 +7,15 @@ #ifndef SINA_DOCUMENT_HPP #define SINA_DOCUMENT_HPP -/// @file +/*! + ****************************************************************************** + * + * \file Document.hpp + * + * \brief Implementation file for Sina Document class + * + ****************************************************************************** + */ #include #include @@ -23,16 +31,18 @@ namespace sina { /** + * \brief An object representing the top-level object of a Sina JSON file + * * A Document represents the top-level object of a JSON file conforming to the * Sina schema. When serialized, these documents can be ingested into a * Sina database and used with the Sina tool. * * Documents contain at most two objects: a list of Records and a list of Relationships. A simple, empty document: * \code{.json} - * { - * "records": [], - * "relationships": [] - * } + * { + * "records": [], + * "relationships": [] + * } * \endcode * * The "records" list can contain Record objects and their inheriting types, such as Run (for a full list, please see @@ -41,27 +51,27 @@ namespace sina * Documents can be assembled programatically and/or generated from existing JSON. An example of an assembled * Document is provided on the main page. To load a Document from an existing JSON file: * \code - * axom::sina::Document myDocument = axom::sina::loadDocument("path/to/infile.json"); + * axom::sina::Document myDocument = axom::sina::loadDocument("path/to/infile.json"); * \endcode * * To generate a Document from a JSON string and vice versa: * \code - * 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;); + * 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;); * \endcode * * You can add further entries to the Document using add(): * \code - * std::unique_ptr myRun{new axom::sina::Run{someID, "My Sim Code", "1.2.3", "jdoe"}}; - * axom::sina::Relationship myRelationship{someID, "comes before", someOtherID}; - * myDocument.add(myRun); - * myDocument.add(myRelationship); + * std::unique_ptr myRun{new axom::sina::Run{someID, "My Sim Code", "1.2.3", "jdoe"}}; + * axom::sina::Relationship myRelationship{someID, "comes before", someOtherID}; + * myDocument.add(myRun); + * myDocument.add(myRelationship); * \endcode * * You can also export your Document to file: * \code - * axom::sina::saveDocument(myDocument, "path/to/outfile.json") + * axom::sina::saveDocument(myDocument, "path/to/outfile.json") * \endcode * */ @@ -104,50 +114,50 @@ class Document { Document &operator=(Document &&) = default; /** - * Create a Document from its Conduit Node representation + * \brief Create a Document from its Conduit Node representation * - * @param asNode the Document as a Node - * @param recordLoader an RecordLoader to use to load the different - * types of records which may be in the document + * \param asNode the Document as a Node + * \param recordLoader an RecordLoader to use to load the different + * types of records which may be in the document */ Document(conduit::Node const &asNode, RecordLoader const &recordLoader); /** - * Create a Document from a JSON string representation + * \brief Create a Document from a JSON string representation * - * @param asJson the Document as a JSON string - * @param recordLoader an RecordLoader to use to load the different - * types of records which may be in the document + * \param asJson the Document as a JSON string + * \param recordLoader an RecordLoader to use to load the different + * types of records which may be in the document */ Document(std::string const &asJson, RecordLoader const &recordLoader); /** - * Add the given record to this document. + * \brief Add the given record to this document. * - * @param record the record to add + * \param record the record to add */ void add(std::unique_ptr record); /** - * Get the list of records currently in this document. + * \brief Get the list of records currently in this document. * - * @return the list of records + * \return the list of records */ RecordList const &getRecords() const noexcept { return records; } /** - * Add a relationship to this document + * \brief Add a relationship to this document * - * @param relationship the relationship to add + * \param relationship the relationship to add */ void add(Relationship relationship); /** - * Get the list of relationships in this document. + * \brief Get the list of relationships in this document. * - * @return the list of relationships + * \return the list of relationships */ RelationshipList const &getRelationships() const noexcept { return relationships; @@ -155,16 +165,16 @@ class Document { /** - * Convert this document to a conduit Node. + * \brief Convert this document to a conduit Node. * - * @return the contents of the document as a Node + * \return the contents of the document as a Node */ conduit::Node toNode() const; /** - * Convert this document to a JSON string. + * \brief Convert this document to a JSON string. * - * @return the contents of the document as a JSON string + * \return the contents of the document as a JSON string */ std::string toJson(conduit::index_t indent=0, conduit::index_t depth=0, const std::string &pad="", const std::string &eoe="") const; @@ -180,31 +190,31 @@ class Document { }; /** - * Save the given Document to the specified location. If the given file exists, - * it will be overwritten. + * \brief Save the given Document to the specified location. If the given file exists, + * it will be overwritten. * - * @param document the Document to save - * @param fileName the location to which to save the file - * @throws std::ios::failure if there are any IO errors + * \param document the Document to save + * \param fileName the location to which to save the file + * \throws std::ios::failure if there are any IO errors */ void saveDocument(Document const &document, std::string const &fileName); /** - * Load a document from the given path. Only records which this library - * knows about will be able to be loaded. + * \brief Load a document from the given path. Only records which this library + * knows about will be able to be loaded. * - * @param path the file system path from which to load the document - * @return the loaded Document + * \param path the file system path from which to load the document + * \return the loaded Document */ Document loadDocument(std::string const &path); /** - * Load a document from the given path. + * \brief Load a document from the given path. * - * @param path the file system path from which to load the document - * @param recordLoader the RecordLoader to use to load the different types - * of records - * @return the loaded Document + * \param path the file system path from which to load the document + * \param recordLoader the RecordLoader to use to load the different types + * of records + * \return the loaded Document */ Document loadDocument(std::string const &path, RecordLoader const &recordLoader); diff --git a/src/axom/sina/core/File.cpp b/src/axom/sina/core/File.cpp index 32331cfe29..10d52f0d62 100644 --- a/src/axom/sina/core/File.cpp +++ b/src/axom/sina/core/File.cpp @@ -4,7 +4,15 @@ // SPDX-License-Identifier: (BSD-3-Clause) -/// @file +/*! + ****************************************************************************** + * + * \file File.cpp + * + * \brief Implementation file for Sina File class + * + ****************************************************************************** + */ #include "axom/sina/core/File.hpp" #include "axom/sina/core/ConduitUtil.hpp" diff --git a/src/axom/sina/core/File.hpp b/src/axom/sina/core/File.hpp index b056da221b..c16d266457 100644 --- a/src/axom/sina/core/File.hpp +++ b/src/axom/sina/core/File.hpp @@ -7,7 +7,15 @@ #ifndef SINA_FILE_HPP #define SINA_FILE_HPP -/// @file +/*! + ****************************************************************************** + * + * \file File.hpp + * + * \brief Implementation file for Sina File class + * + ****************************************************************************** + */ #include #include @@ -19,82 +27,84 @@ namespace axom namespace sina { /** + * \brief An object to help track locations of files in the file system + * * A File tracks 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. * * \code - * 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"}); - * myRecord->add(myFile); - * myRecord->add(myOtherFile); + * 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"}); + * myRecord->add(myFile); + * myRecord->add(myOtherFile); * \endcode */ class File { public: /** - * Construct a new File. + * \brief Construct a new File. * - * @param uri the location of the file + * \param uri the location of the file */ explicit File(std::string uri); /** - * Construct a new File. + * \brief Construct a new File. * - * @param uri the uri for a file - * @param asNode the Node representation of the file's additional info + * \param uri the uri for a file + * \param asNode the Node representation of the file's additional info */ File(std::string uri, conduit::Node const &asNode); /** - * Get the File's URI. + * \brief Get the File's URI. * - * @return the URI + * \return the URI */ std::string const &getUri() const noexcept { return uri; } /** - * Get the File's MIME type. + * \brief Get the File's MIME type. * - * @return the MIME type + * \return the MIME type */ std::string const &getMimeType() const noexcept { return mimeType; } /** - * Get the File's tags. + * \brief Get the File's tags. * - * @return the tags + * \return the tags */ std::vector const &getTags() const noexcept { return tags; } /** - * Set the File's MIME type. + * \brief Set the File's MIME type. * - * @param mimeType the MIME type + * \param mimeType the MIME type */ void setMimeType(std::string mimeType); /** - * Set the File's tags. + * \brief Set the File's tags. * - * @param tags the File's tags + * \param tags the File's tags */ void setTags(std::vector tags); /** - * Convert this File to its conduit Node representation. + * \brief Convert this File to its conduit Node representation. * - * @return the File in its Node representation + * \return the File in its Node representation */ conduit::Node toNode() const; diff --git a/src/axom/sina/core/ID.cpp b/src/axom/sina/core/ID.cpp index 78aeded854..9d0bd3205a 100644 --- a/src/axom/sina/core/ID.cpp +++ b/src/axom/sina/core/ID.cpp @@ -3,10 +3,15 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - - - -/// @file +/*! + ****************************************************************************** + * + * \file ID.cpp + * + * \brief Implementation file for Sina ID class + * + ****************************************************************************** + */ #include "axom/sina/core/ID.hpp" diff --git a/src/axom/sina/core/ID.hpp b/src/axom/sina/core/ID.hpp index ee39375710..a675f8a2f9 100644 --- a/src/axom/sina/core/ID.hpp +++ b/src/axom/sina/core/ID.hpp @@ -7,13 +7,19 @@ #ifndef SINA_ID_HPP #define SINA_ID_HPP -/** - * @file +/*! + ****************************************************************************** + * + * \file ID.hpp + * + * \brief Implementation file for Sina ID class * * The Sina schema allows records to have either a local ID or a global ID. * When a global ID is specified, that will be used in the database. When a * local ID is specified, an ID will be automatically generated when inserting * the record into the database. + * + ****************************************************************************** */ #include @@ -34,31 +40,34 @@ enum class IDType { }; /** + * \brief The ID of a Record + * * An ID is used to represent the ID of a Record. This class holds both the * actual ID and whether it is a local or global ID. */ class ID { public: /** - * Create a new ID. - * @param id the actual value of the ID - * @param type whether the ID is local or global + * \brief Create a new ID. + * + * \param id the actual value of the ID + * \param type whether the ID is local or global */ ID(std::string id, IDType type); /** - * Get the value of the ID. + * \brief Get the value of the ID. * - * @return the actual ID + * \return the actual ID */ std::string const &getId() const noexcept { return id; } /** - * Get the type of the ID. + * \brief Get the type of the ID. * - * @return whether the ID is local or global + * \return whether the ID is local or global */ IDType getType() const noexcept { return type; @@ -73,6 +82,8 @@ namespace internal { /** + * \brief An object representing a pair of ID fields + * * IDField instances are used to describe a pair of ID fields in a schema * object which correspond to global and local names for the field. For * example, the "id" and "local_id" fields in records, or the @@ -82,55 +93,55 @@ namespace internal class IDField { public: /** - * Construct a new IDField. + * \brief Construct a new IDField. * - * @param value the value of the ID - * @param localName the name of the local variant of the field - * @param globalName the name of the global variant of the field + * \param value the value of the ID + * \param localName the name of the local variant of the field + * \param globalName the name of the global variant of the field */ IDField(ID value, std::string localName, std::string globalName); /** - * Construct an IDField by looking for its values in a conduit Node. + * \brief Construct an IDField by looking for its values in a conduit Node. * - * @param parentObject the conduit Node containing the ID field - * @param localName the local name of the field - * @param globalName the global name of the field + * \param parentObject the conduit Node containing the ID field + * \param localName the local name of the field + * \param globalName the global name of the field */ IDField(conduit::Node const &parentObject, std::string localName, std::string globalName); /** - * Get the value of this field. + * \brief Get the value of this field. * - * @return the ID describing the field's value + * \return the ID describing the field's value */ ID const &getID() const noexcept { return value; } /** - * Get the name to use for this field when the ID is local. + * \brief Get the name to use for this field when the ID is local. * - * @return the name of the local ID field + * \return the name of the local ID field */ std::string const &getLocalName() const noexcept { return localName; } /** - * Get the name to use for this field when the ID is global. + * \brief Get the name to use for this field when the ID is global. * - * @return the name of the global ID field + * \return the name of the global ID field */ std::string const &getGlobalName() const noexcept { return globalName; } /** - * Add this field to the given Node. + * \brief Add this field to the given Node. * - * @param object the Node to which to add the field + * \param object the Node to which to add the field */ void addTo(conduit::Node &object) const; diff --git a/src/axom/sina/core/Record.cpp b/src/axom/sina/core/Record.cpp index 13bf3886d0..dff0395cff 100644 --- a/src/axom/sina/core/Record.cpp +++ b/src/axom/sina/core/Record.cpp @@ -4,7 +4,17 @@ // SPDX-License-Identifier: (BSD-3-Clause) -/// @file +/*! + ****************************************************************************** + * + * \file Record.cpp + * + * \brief Implementation file for Sina Record class + * + * \sa DataHolder.hpp + * + ****************************************************************************** + */ #include "axom/sina/core/Record.hpp" diff --git a/src/axom/sina/core/Record.hpp b/src/axom/sina/core/Record.hpp index dac1d074b6..40de3fb267 100644 --- a/src/axom/sina/core/Record.hpp +++ b/src/axom/sina/core/Record.hpp @@ -7,7 +7,17 @@ #ifndef SINA_RECORD_HPP #define SINA_RECORD_HPP -/// @file +/*! + ****************************************************************************** + * + * \file Record.hpp + * + * \brief Implementation file for Sina Record class + * + * \sa DataHolder.hpp + * + ****************************************************************************** + */ #include #include @@ -49,6 +59,8 @@ struct FileHashByURI { }; /** + * \brief An object representing an entry in a Document's Record list + * * The Record class represents an entry in a Document's Record list. Records represent the data to be stored * (as opposed to the relationships between data)--natural scopes for Records include things like a single run * of an application, an msub job, a cluster of runs that has some metadata attached to the cluster (this Record @@ -58,17 +70,17 @@ struct FileHashByURI { * File objects and a map of Datum objects. * * \code - * axom::sina::ID myID{"my_record", axom::sina::IDType::Local}; - * std::unique_ptr myRecord{new axom::sina::Record{myID, "my_type"}}; - * std::vector myTags{"input"}; - * axom::sina::Datum myDatum{12, myTags}; - * myRecord->add("my_scalar",std::move(myDatum)); - * std::cout << myRecord->toNode().to_json() << std::endl; + * axom::sina::ID myID{"my_record", axom::sina::IDType::Local}; + * std::unique_ptr myRecord{new axom::sina::Record{myID, "my_type"}}; + * std::vector myTags{"input"}; + * axom::sina::Datum myDatum{12, myTags}; + * myRecord->add("my_scalar",std::move(myDatum)); + * std::cout << myRecord->toNode().to_json() << std::endl; * \endcode * * The output would be: * \code{.json} - * {"local_id":"my_record","type":"my_type","data":{"my_scalar":{"tags":["input"],"value":12.0}}} + * {"local_id":"my_record","type":"my_type","data":{"my_scalar":{"tags":["input"],"value":12.0}}} * \endcode */ class Record : public DataHolder { @@ -79,17 +91,17 @@ class Record : public DataHolder { using FileSet = std::unordered_set; /** - * Construct a new Record. + * \brief Construct a new Record. * - * @param id the ID of the record - * @param type the type of the record + * \param id the ID of the record + * \param type the type of the record */ Record(ID id, std::string type); /** - * Construct a Record from its conduit Node representation. + * \brief Construct a Record from its conduit Node representation. * - * @param asNode the Record as a Node + * \param asNode the Record as a Node */ explicit Record(conduit::Node const &asNode); @@ -104,27 +116,27 @@ class Record : public DataHolder { Record &operator=(Record const &) = delete; /** - * Get the Record's ID. + * \brief Get the Record's ID. * - * @return the ID + * \return the ID */ ID const &getId() const noexcept { return id.getID(); } /** - * Get the Record's type. + * \brief Get the Record's type. * - * @return the Record's type + * \return the Record's type */ std::string const &getType() const noexcept { return type; } /** - * Remove a File from this record. + * \brief Remove a File from this record. * - * @param file the File to remove + * \param file the File to remove */ void remove(File const& file); @@ -132,31 +144,31 @@ class Record : public DataHolder { using DataHolder::add; /** - * Add a File to this record. + * \brief Add a File to this record. * - * @param file the File to add + * \param file the File to add */ void add(File file); /** - * Get the files associated with this record. + * \brief Get the files associated with this record. * - * @return the record's files + * \return the record's files */ FileSet const &getFiles() const noexcept { return files; } /** - * Convert this record to its conduit Node representation. + * \brief Convert this record to its conduit Node representation. * - * @return the Node representation of this record. + * \return the Node representation of this record. */ conduit::Node toNode() const override; /** - * Add another record to this one as library data. + * \brief Add another record to this one as library data. * * Useful for libraries that can run in standalone mode; the host * simply calls this method on the record the library produces. @@ -172,13 +184,15 @@ class Record : public DataHolder { /** + * \brief An object to convert Conduit Nodes into Records + * * A RecordLoader is used to convert conduit::Node instances which represent * Sina Records into instances of their corresponding axom::sina::Record * subclasses. For convenience, a RecordLoader capable of handling Records of all known * types can be created using createRecordLoaderWithAllKnownTypes: * * \code - * axom::sina::Document myDocument = axom::sina::Document(jObj, axom::sina::createRecordLoaderWithAllKnownTypes()); + * axom::sina::Document myDocument = axom::sina::Document(jObj, axom::sina::createRecordLoaderWithAllKnownTypes()); * \endcode */ class RecordLoader { @@ -191,26 +205,26 @@ class RecordLoader { conduit::Node const &)>; /** - * Add a function for loading records of the specified type. + * \brief Add a function for loading records of the specified type. * - * @param type the type of records this function can load - * @param loader the function which can load the records + * \param type the type of records this function can load + * \param loader the function which can load the records */ void addTypeLoader(std::string const &type, TypeLoader loader); /** - * Load a axom::sina::Record from its conduit Node representation. + * \brief Load a Record from its conduit Node representation. * - * @param recordAsNode the Record as a Node - * @return the Record + * \param recordAsNode the Record as a Node + * \return the Record */ std::unique_ptr load(conduit::Node const &recordAsNode) const; /** - * Check whether this loader can load records of the given type. + * \brief Check whether this loader can load records of the given type. * - * @param type the type of the records to check - * @return whether records of the given type can be loaded + * \param type the type of the records to check + * \return whether records of the given type can be loaded */ bool canLoad(std::string const &type) const; @@ -219,9 +233,9 @@ class RecordLoader { }; /** - * Create a RecordLoader which can load records of all known types. + * \brief Create a RecordLoader which can load records of all known types. * - * @return the newly-created loader + * \return the newly-created loader */ RecordLoader createRecordLoaderWithAllKnownTypes(); diff --git a/src/axom/sina/core/Relationship.cpp b/src/axom/sina/core/Relationship.cpp index ebec7f7695..905a8bd3e2 100644 --- a/src/axom/sina/core/Relationship.cpp +++ b/src/axom/sina/core/Relationship.cpp @@ -3,8 +3,15 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - -/// @file +/*! + ****************************************************************************** + * + * \file Relationship.cpp + * + * \brief Implementation file for Sina Relationship class + * + ****************************************************************************** + */ #include "axom/sina/core/Relationship.hpp" diff --git a/src/axom/sina/core/Relationship.hpp b/src/axom/sina/core/Relationship.hpp index 455946e529..7128f83aa3 100644 --- a/src/axom/sina/core/Relationship.hpp +++ b/src/axom/sina/core/Relationship.hpp @@ -7,10 +7,14 @@ #ifndef SINA_RELATIONSHIP_HPP #define SINA_RELATIONSHIP_HPP -/** - * @file +/*! + ****************************************************************************** + * + * \file Relationship.hpp + * + * \brief Implementation file for Sina Relationship class * - * Contains the definition of the Relationship class. + ****************************************************************************** */ #include @@ -25,6 +29,8 @@ namespace sina { /** + * \brief An object used to correlate 2 Records + * * A Relationship consists of three parts: a subject, an object, and a predicate. It describes * a relationship between two Records (and/or Record inheritors, e.g. Run). * The subject and object must be IDs referring to valid records, while the predicate may be @@ -49,15 +55,15 @@ namespace sina * ID documentation. * * \code - * axom::sina::ID task22{"Task_22", axom::sina::IDType::Global}; - * axom::sina::ID run1024{"Run_1024", axom::sina::IDType::Global}; - * axom::sina::Relationship myRelationship{task22, "contains", run1024}; - * std::cout << myRelationship.toNode().to_json() << std::endl; + * axom::sina::ID task22{"Task_22", axom::sina::IDType::Global}; + * axom::sina::ID run1024{"Run_1024", axom::sina::IDType::Global}; + * axom::sina::Relationship myRelationship{task22, "contains", run1024}; + * std::cout << myRelationship.toNode().to_json() << std::endl; * \endcode * * This would output: * \code{.json} - * {"object":"Run_1024","predicate":"contains","subject":"Task_22"} + * {"object":"Run_1024","predicate":"contains","subject":"Task_22"} * \endcode * * As with any other Sina ID, the subject or object may be either local (uniquely refer to one object @@ -66,9 +72,9 @@ namespace sina * that ID) will be updated to use the same global ID. * * \code - * axom::sina::ID myLocalID{"my_local_run", axom::sina::IDType::Local}; - * std::unique_ptr myRun{new axom::sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; - * axom::sina::Relationship myRelationship{task22, "contains", myLocalID}; + * axom::sina::ID myLocalID{"my_local_run", axom::sina::IDType::Local}; + * std::unique_ptr myRun{new axom::sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; + * axom::sina::Relationship myRelationship{task22, "contains", myLocalID}; * \endcode * * In the above code, "my_local_run" would be replaced by a global ID on ingestion. If this new global ID was, @@ -78,53 +84,53 @@ namespace sina class Relationship { public: /** - * Create a new relationship. + * \brief Create a new relationship. * - * @param subject the subject of the relationship - * @param predicate the predicate describing the relationship from the - * subject to the object - * @param object the object of the relationship + * \param subject the subject of the relationship + * \param predicate the predicate describing the relationship from the + * subject to the object + * \param object the object of the relationship */ Relationship(ID subject, std::string predicate, ID object); /** - * Create a Relationship object from its representation as a conduit Node. + * \brief Create a Relationship object from its representation as a conduit Node. * - * @param asNode the relationship as a Node + * \param asNode the relationship as a Node */ explicit Relationship(conduit::Node const &asNode); /** - * Get the subject. + * \brief Get the subject. * - * @return the subject + * \return the subject */ ID const &getSubject() const noexcept { return subject.getID(); } /** - * Get the object. + * \brief Get the object. * - * @return the object + * \return the object */ ID const &getObject() const noexcept { return object.getID(); } /** - * Get the predicate. + * \brief Get the predicate. * - * @return the predicate + * \return the predicate */ std::string const &getPredicate() const noexcept { return predicate; } /** - * Convert this Relationship to its Node representation. + * \brief Convert this Relationship to its Node representation. * - * @return this relationship as a conduit Node + * \return this relationship as a conduit Node */ conduit::Node toNode() const; diff --git a/src/axom/sina/core/Run.cpp b/src/axom/sina/core/Run.cpp index 53a7b62591..2d6b9515b8 100644 --- a/src/axom/sina/core/Run.cpp +++ b/src/axom/sina/core/Run.cpp @@ -3,8 +3,17 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - -/// @file +/*! + ****************************************************************************** + * + * \file Run.cpp + * + * \brief Implementation file for Sina Run class + * + * \sa Record.hpp + * + ****************************************************************************** + */ #include "axom/sina/core/Run.hpp" diff --git a/src/axom/sina/core/Run.hpp b/src/axom/sina/core/Run.hpp index e16f0e3738..7d0b86e5c7 100644 --- a/src/axom/sina/core/Run.hpp +++ b/src/axom/sina/core/Run.hpp @@ -7,7 +7,17 @@ #ifndef SINA_RUN_HPP #define SINA_RUN_HPP -/// @file +/*! + ****************************************************************************** + * + * \file Run.hpp + * + * \brief Implementation file for Sina Run class + * + * \sa Record.hpp + * + ****************************************************************************** + */ #include "axom/sina/core/Record.hpp" @@ -17,6 +27,8 @@ namespace sina { /** + * \brief A sub-type of Record representing a single run of an applicaiton + * * A Run is a subtype of Record corresponding to a single run of an application, as * specified in the Sina schema. A Run has a few additional fields required in addition * to the id required by a Record (type is automatically set to "run"): @@ -27,52 +39,52 @@ namespace sina * * To create a Run: * \code - * axom::sina::ID run1ID{"run1", axom::sina::IDType::Local}; - * std::unique_ptr run1{new axom::sina::Run{run1ID, "My Sim Code", "1.2.3", "jdoe"}}; + * axom::sina::ID run1ID{"run1", axom::sina::IDType::Local}; + * std::unique_ptr run1{new axom::sina::Run{run1ID, "My Sim Code", "1.2.3", "jdoe"}}; * \endcode * */ class Run : public Record { public: /** - * Create a new Run. + * \brief Create a new Run. * - * @param id the run's ID - * @param application the application that was run - * @param version (optional) the version of the application - * @param user (optional) the user who executed the run + * \param id the run's ID + * \param application the application that was run + * \param version (optional) the version of the application + * \param user (optional) the user who executed the run */ Run(ID id, std::string application, std::string version = "", std::string user = ""); /** - * Create a Run from its representation as a conduit Node + * \brief Create a Run from its representation as a conduit Node * - * @param asNode the run as a Node + * \param asNode the run as a Node */ explicit Run(conduit::Node const &asNode); /** - * Get the application that was run. + * \brief Get the application that was run. * - * @return the application's name + * \return the application's name */ std::string const &getApplication() const { return application; } /** - * Get the version of the application that was run. + * \brief Get the version of the application that was run. * - * @return the application's version + * \return the application's version */ std::string const &getVersion() const { return version; } /** - * Get the name of the user who ran the application. + * \brief Get the name of the user who ran the application. * - * @return the user's name + * \return the user's name */ std::string const &getUser() const { return user; @@ -87,10 +99,10 @@ class Run : public Record { }; /** - * Add a type loader to the given RecordLoader for loading Run instances. + * \brief Add a type loader to the given RecordLoader for loading Run instances. * - * @param loader the RecordLoader to which to add the function for loading - * Run instances. + * \param loader the RecordLoader to which to add the function for loading + * Run instances. */ void addRunLoader(RecordLoader &loader); diff --git a/src/docs/doxygen/Doxyfile.in b/src/docs/doxygen/Doxyfile.in index 9f6d6cc9d6..9d892011b6 100644 --- a/src/docs/doxygen/Doxyfile.in +++ b/src/docs/doxygen/Doxyfile.in @@ -799,8 +799,7 @@ INPUT = @PROJECT_SOURCE_DIR@/axom/doxygen_mainpage.md \ @PROJECT_SOURCE_DIR@/axom/sidre/spio \ @PROJECT_SOURCE_DIR@/axom/sina \ @PROJECT_SOURCE_DIR@/axom/sina/doxygen_mainpage.md \ - @PROJECT_SOURCE_DIR@/axom/sina/src \ - @PROJECT_SOURCE_DIR@/axom/sina/include \ + @PROJECT_SOURCE_DIR@/axom/sina/core \ @PROJECT_SOURCE_DIR@/axom/slam \ @PROJECT_SOURCE_DIR@/axom/slam/doxygen_mainpage.md \ @PROJECT_SOURCE_DIR@/axom/slam/policies \ From 4d7c8c2d9fb861b4108a95abe364ac19f642a392 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 16 Jul 2024 11:07:56 -0700 Subject: [PATCH 24/60] remove unused config file and reference to msub job in docs --- src/axom/sina/config.hpp.in | 9 --------- src/axom/sina/docs/sphinx/records.rst | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 src/axom/sina/config.hpp.in diff --git a/src/axom/sina/config.hpp.in b/src/axom/sina/config.hpp.in deleted file mode 100644 index 59e251c62e..0000000000 --- a/src/axom/sina/config.hpp.in +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef SINA_CONFIG_HPP_ -#define SINA_CONFIG_HPP_ - -/// \file - -#cmakedefine AXOM_SINA_USE_ADIAK - -#endif // SINA_CONFIG_HPP_ - diff --git a/src/axom/sina/docs/sphinx/records.rst b/src/axom/sina/docs/sphinx/records.rst index 0f7bf0b489..24d1622c89 100644 --- a/src/axom/sina/docs/sphinx/records.rst +++ b/src/axom/sina/docs/sphinx/records.rst @@ -14,7 +14,7 @@ study. Some examples for the natural scope of ``Record`` objects include things like: - a single run of an application - - an msub job + - a `Maestro`_ step - a cluster of runs that has some metadata attached to the cluster (this ``Record`` might have a "contains" :doc:`Relationship ` for all the runs within it) From 2860e092695560f58ed2b0067c7cf869a935e9ef Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 16 Jul 2024 11:13:50 -0700 Subject: [PATCH 25/60] fix issue with Maestro link --- src/axom/sina/docs/sphinx/records.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/sina/docs/sphinx/records.rst b/src/axom/sina/docs/sphinx/records.rst index 24d1622c89..26eea2d887 100644 --- a/src/axom/sina/docs/sphinx/records.rst +++ b/src/axom/sina/docs/sphinx/records.rst @@ -14,7 +14,7 @@ study. Some examples for the natural scope of ``Record`` objects include things like: - a single run of an application - - a `Maestro`_ step + - a `Maestro `_ step - a cluster of runs that has some metadata attached to the cluster (this ``Record`` might have a "contains" :doc:`Relationship ` for all the runs within it) From c8e1f39f0747acf27d3a05edf8ed9afc5846fa32 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 16 Jul 2024 15:44:10 -0700 Subject: [PATCH 26/60] convert Datum to pass by reference --- src/axom/sina/core/Datum.cpp | 24 ++++++++++++------------ src/axom/sina/core/Datum.hpp | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/axom/sina/core/Datum.cpp b/src/axom/sina/core/Datum.cpp index 5de49be3ea..7e40be7a9a 100644 --- a/src/axom/sina/core/Datum.cpp +++ b/src/axom/sina/core/Datum.cpp @@ -35,26 +35,26 @@ namespace axom namespace sina { -Datum::Datum(std::string value_) : - stringValue{std::move(value_)}{ +Datum::Datum(const std::string &value_) : + stringValue{value_}{ //Set type to String, as we know it uses strings type = ValueType::String; } -Datum::Datum(double value_) : - scalarValue{std::move(value_)}{ +Datum::Datum(const double &value_) : + scalarValue{value_}{ //Set type to Scalar, as we know it uses doubles type = ValueType::Scalar; } -Datum::Datum(std::vector value_) : - stringArrayValue{std::move(value_)}{ +Datum::Datum(const std::vector &value_) : + stringArrayValue{value_}{ //Set type to StringArray, as we know it uses an array of strings type = ValueType::StringArray; } -Datum::Datum(std::vector value_) : - scalarArrayValue{std::move(value_)}{ +Datum::Datum(const std::vector &value_) : + scalarArrayValue{value_}{ //Set type to ScalarArray, as we know it uses an array of doubles type = ValueType::ScalarArray; } @@ -149,12 +149,12 @@ Datum::Datum(conduit::Node const &asNode) { } } -void Datum::setUnits(std::string units_) { - units = std::move(units_); +void Datum::setUnits(const std::string &units_) { + units = units_; } -void Datum::setTags(std::vector tags_){ - tags = std::move(tags_); +void Datum::setTags(const std::vector &tags_){ + tags = tags_; } conduit::Node Datum::toNode() const { diff --git a/src/axom/sina/core/Datum.hpp b/src/axom/sina/core/Datum.hpp index 8625e32cc1..658894e731 100644 --- a/src/axom/sina/core/Datum.hpp +++ b/src/axom/sina/core/Datum.hpp @@ -83,28 +83,28 @@ class Datum { * * \param value the string value of the datum */ - Datum(std::string value); + Datum(const std::string &value); /** * \brief Construct a new Datum. * * \param value the double value of the datum */ - Datum(double value); + Datum(const double &value); /** * \brief Construct a new Datum. * * \param value the string array value of the datum */ - Datum(std::vector value); + Datum(const std::vector &value); /** * \brief Construct a new Datum. * * \param value the scalar array value of the datum */ - Datum(std::vector value); + Datum(const std::vector &value); /** * \brief Construct a Datum from its Node representation. @@ -163,7 +163,7 @@ class Datum { * * \param tags the tags of the value */ - void setTags(std::vector tags); + void setTags(const std::vector &tags); /** * \brief Get the units of the Datum @@ -179,7 +179,7 @@ class Datum { * * \param units the units of the value */ - void setUnits(std::string units); + void setUnits(const std::string &units); /** From a57111a8f02b28bd3098171dae340a59f8e8f965 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 17 Jul 2024 14:37:38 -0700 Subject: [PATCH 27/60] run make style --- src/axom/sina/core/AdiakWriter.cpp | 435 ++++++----- src/axom/sina/core/AdiakWriter.hpp | 29 +- src/axom/sina/core/ConduitUtil.cpp | 192 ++--- src/axom/sina/core/ConduitUtil.hpp | 24 +- src/axom/sina/core/Curve.cpp | 80 +- src/axom/sina/core/Curve.hpp | 67 +- src/axom/sina/core/CurveSet.cpp | 113 +-- src/axom/sina/core/CurveSet.hpp | 58 +- src/axom/sina/core/DataHolder.cpp | 216 +++--- src/axom/sina/core/DataHolder.hpp | 128 ++-- src/axom/sina/core/Datum.cpp | 278 +++---- src/axom/sina/core/Datum.hpp | 116 ++- src/axom/sina/core/Document.cpp | 199 ++--- src/axom/sina/core/Document.hpp | 96 ++- src/axom/sina/core/File.cpp | 86 ++- src/axom/sina/core/File.hpp | 54 +- src/axom/sina/core/ID.cpp | 62 +- src/axom/sina/core/ID.hpp | 81 +-- src/axom/sina/core/Record.cpp | 137 ++-- src/axom/sina/core/Record.hpp | 119 ++- src/axom/sina/core/Relationship.cpp | 44 +- src/axom/sina/core/Relationship.hpp | 46 +- src/axom/sina/core/Run.cpp | 60 +- src/axom/sina/core/Run.hpp | 48 +- src/axom/sina/examples/sina_basic.cpp | 26 +- .../sina/examples/sina_check_datum_type.cpp | 34 +- src/axom/sina/examples/sina_create_datum.cpp | 23 +- src/axom/sina/examples/sina_curve_set.cpp | 189 ++--- .../sina/examples/sina_document_assembly.cpp | 56 +- .../examples/sina_file_object_creation.cpp | 29 +- .../examples/sina_file_object_removal.cpp | 33 +- .../examples/sina_local_id_relationship.cpp | 25 +- .../examples/sina_query_record_for_files.cpp | 46 +- .../sina_query_records_relationships.cpp | 64 +- .../examples/sina_relationship_assembly.cpp | 16 +- .../examples/sina_set_datum_units_tags.cpp | 20 +- src/axom/sina/examples/sina_tutorial.cpp | 207 +++--- .../sina/examples/sina_view_datum_types.cpp | 47 +- .../sina/examples/sina_view_datum_values.cpp | 52 +- .../sina/interface/sina_fortran_interface.cpp | 579 ++++++++------- .../sina/interface/sina_fortran_interface.h | 5 +- src/axom/sina/tests/ConduitTestUtils.cpp | 28 +- src/axom/sina/tests/ConduitTestUtils.hpp | 22 +- src/axom/sina/tests/TestRecord.cpp | 26 +- src/axom/sina/tests/TestRecord.hpp | 57 +- src/axom/sina/tests/sina_AdiakWriter.cpp | 130 ++-- src/axom/sina/tests/sina_ConduitUtil.cpp | 423 ++++++----- src/axom/sina/tests/sina_Curve.cpp | 128 ++-- src/axom/sina/tests/sina_CurveSet.cpp | 220 +++--- src/axom/sina/tests/sina_DataHolder.cpp | 407 ++++++----- src/axom/sina/tests/sina_Datum.cpp | 300 ++++---- src/axom/sina/tests/sina_Document.cpp | 507 +++++++------ src/axom/sina/tests/sina_File.cpp | 108 +-- src/axom/sina/tests/sina_ID.cpp | 129 ++-- src/axom/sina/tests/sina_Record.cpp | 686 +++++++++--------- src/axom/sina/tests/sina_Relationship.cpp | 213 +++--- src/axom/sina/tests/sina_Run.cpp | 127 ++-- 57 files changed, 4104 insertions(+), 3626 deletions(-) diff --git a/src/axom/sina/core/AdiakWriter.cpp b/src/axom/sina/core/AdiakWriter.cpp index 3286995979..ca0e548a38 100644 --- a/src/axom/sina/core/AdiakWriter.cpp +++ b/src/axom/sina/core/AdiakWriter.cpp @@ -17,24 +17,24 @@ #ifdef AXOM_USE_ADIAK -#include -#include -#include -#include -#include + #include + #include + #include + #include + #include extern "C" { -#include "adiak_tool.h" + #include "adiak_tool.h" } -#include "axom/sina/core/ConduitUtil.hpp" -#include "axom/sina/core/Record.hpp" -#include "axom/sina/core/Datum.hpp" -#include "axom/sina/core/Document.hpp" + #include "axom/sina/core/ConduitUtil.hpp" + #include "axom/sina/core/Record.hpp" + #include "axom/sina/core/Datum.hpp" + #include "axom/sina/core/Document.hpp" namespace axom { -namespace sina +namespace sina { namespace { @@ -43,7 +43,14 @@ namespace * Adiak has a much wider array of supported types than Sina. We will convert * Adiak types to ones Sina understands; SinaType holds the possibilities. **/ -enum SinaType {sina_scalar, sina_string, sina_list, sina_file, sina_unknown}; +enum SinaType +{ + sina_scalar, + sina_string, + sina_list, + sina_file, + sina_unknown +}; /** * Add a axom::sina::Datum object to a Record. These are the sina equivalent @@ -51,52 +58,61 @@ enum SinaType {sina_scalar, sina_string, sina_list, sina_file, sina_unknown}; * harvests what it can and hands it off to the Record. **/ template -void addDatum(const std::string &name, T sina_safe_val, const std::vector &tags, axom::sina::Record *record){ - axom::sina::Datum datum{sina_safe_val}; - datum.setTags(std::move(tags)); - record->add(name, datum); +void addDatum(const std::string &name, + T sina_safe_val, + const std::vector &tags, + axom::sina::Record *record) +{ + axom::sina::Datum datum {sina_safe_val}; + datum.setTags(std::move(tags)); + record->add(name, datum); } /** * Add a axom::sina::File object to our current Record. Adiak stores paths, * which are essentially the same as Sina's idea of storing files. **/ -void addFile(const std::string &name, const std::string &uri, axom::sina::Record *record){ - // We don't care about type here, there's only one adiak type that acts as a file - axom::sina::File file{uri}; - file.setTags(std::vector{name}); - record->add(std::move(file)); +void addFile(const std::string &name, + const std::string &uri, + axom::sina::Record *record) +{ + // We don't care about type here, there's only one adiak type that acts as a file + axom::sina::File file {uri}; + file.setTags(std::vector {name}); + record->add(std::move(file)); } /** * Given an Adiak type, return its corresponding Sina type. **/ -SinaType findSinaType(adiak_datatype_t *t){ - switch (t->dtype){ - case adiak_long: - case adiak_ulong: - case adiak_int: - case adiak_uint: - case adiak_double: - case adiak_timeval: - return sina_scalar; - case adiak_date: - case adiak_version: - case adiak_string: - case adiak_catstring: - return sina_string; - case adiak_path: - return sina_file; - case adiak_set: - case adiak_tuple: - case adiak_range: - case adiak_list: - return sina_list; - case adiak_type_unset: - return sina_unknown; - default: - return sina_unknown; - } +SinaType findSinaType(adiak_datatype_t *t) +{ + switch(t->dtype) + { + case adiak_long: + case adiak_ulong: + case adiak_int: + case adiak_uint: + case adiak_double: + case adiak_timeval: + return sina_scalar; + case adiak_date: + case adiak_version: + case adiak_string: + case adiak_catstring: + return sina_string; + case adiak_path: + return sina_file; + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + return sina_list; + case adiak_type_unset: + return sina_unknown; + default: + return sina_unknown; + } } /** @@ -104,81 +120,95 @@ SinaType findSinaType(adiak_datatype_t *t){ * Manage the conversions from various Adiak types to the final double * representation **/ -double toScalar(adiak_value_t *val, adiak_datatype_t *adiak_type){ - switch (adiak_type->dtype){ - case adiak_long: - case adiak_ulong: - return static_cast(val->v_long); - case adiak_int: - case adiak_uint: - return static_cast(val->v_int); - case adiak_double: - return val->v_double; - case adiak_timeval: { - struct timeval *tval = static_cast(val->v_ptr); - return static_cast(tval->tv_sec) - + (static_cast(tval->tv_usec) / 1000000.0); - } - // None of the rest of these should ever be reachable, so special error message - case adiak_date: - case adiak_version: - case adiak_string: - case adiak_catstring: - case adiak_path: - case adiak_set: - case adiak_tuple: - case adiak_range: - case adiak_list: - case adiak_type_unset: { - std::string msg("Logic error, contact maintainer: Adiak-to-Sina double converter given "); - char *s = adiak_type_to_string(adiak_type, 1); - msg += s; - free(s); - throw std::runtime_error(msg); - } - default: - throw std::runtime_error("Adiak-to-Sina double converter given something not convertible to double"); - } +double toScalar(adiak_value_t *val, adiak_datatype_t *adiak_type) +{ + switch(adiak_type->dtype) + { + case adiak_long: + case adiak_ulong: + return static_cast(val->v_long); + case adiak_int: + case adiak_uint: + return static_cast(val->v_int); + case adiak_double: + return val->v_double; + case adiak_timeval: + { + struct timeval *tval = static_cast(val->v_ptr); + return static_cast(tval->tv_sec) + + (static_cast(tval->tv_usec) / 1000000.0); + } + // None of the rest of these should ever be reachable, so special error message + case adiak_date: + case adiak_version: + case adiak_string: + case adiak_catstring: + case adiak_path: + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + case adiak_type_unset: + { + std::string msg( + "Logic error, contact maintainer: Adiak-to-Sina double converter given "); + char *s = adiak_type_to_string(adiak_type, 1); + msg += s; + free(s); + throw std::runtime_error(msg); + } + default: + throw std::runtime_error( + "Adiak-to-Sina double converter given something not convertible to " + "double"); + } } /** * Some Adiak types become what Sina views as a string. * Manage the conversions from various Adiak types to said string. **/ -std::string toString(adiak_value_t *val, adiak_datatype_t *adiak_type){ - switch (adiak_type->dtype){ - case adiak_date: { - char datestr[512]; - signed long seconds_since_epoch = static_cast(val->v_long); - struct tm *loc = localtime(&seconds_since_epoch); - strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z", loc); - return static_cast(datestr); - } - case adiak_catstring: - case adiak_version: - case adiak_string: - case adiak_path: - return std::string(static_cast(val->v_ptr)); - case adiak_long: - case adiak_ulong: - case adiak_int: - case adiak_uint: - case adiak_double: - case adiak_timeval: - case adiak_set: - case adiak_tuple: - case adiak_range: - case adiak_list: - case adiak_type_unset:{ - std::string msg("Logic error, contact maintainer: Adiak-to-Sina string converter given "); - char *s = adiak_type_to_string(adiak_type, 1); - msg += s; - free(s); - throw std::runtime_error(msg); - } - default: - throw std::runtime_error("Adiak-to-Sina string converter given something not convertible to string"); - } +std::string toString(adiak_value_t *val, adiak_datatype_t *adiak_type) +{ + switch(adiak_type->dtype) + { + case adiak_date: + { + char datestr[512]; + signed long seconds_since_epoch = static_cast(val->v_long); + struct tm *loc = localtime(&seconds_since_epoch); + strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z", loc); + return static_cast(datestr); + } + case adiak_catstring: + case adiak_version: + case adiak_string: + case adiak_path: + return std::string(static_cast(val->v_ptr)); + case adiak_long: + case adiak_ulong: + case adiak_int: + case adiak_uint: + case adiak_double: + case adiak_timeval: + case adiak_set: + case adiak_tuple: + case adiak_range: + case adiak_list: + case adiak_type_unset: + { + std::string msg( + "Logic error, contact maintainer: Adiak-to-Sina string converter given "); + char *s = adiak_type_to_string(adiak_type, 1); + msg += s; + free(s); + throw std::runtime_error(msg); + } + default: + throw std::runtime_error( + "Adiak-to-Sina string converter given something not convertible to " + "string"); + } } /** @@ -187,93 +217,114 @@ std::string toString(adiak_value_t *val, adiak_datatype_t *adiak_type){ * or all strings. Manage conversions from various Adiak list types that * contain scalars to a simple list (vector) of scalars. **/ -std::vector toScalarList(adiak_value_t *subvals, adiak_datatype_t *t){ - std::vector sina_safe_list; - for (int i = 0; i < t->num_elements; i++) { - sina_safe_list.emplace_back(toScalar(subvals+i, t->subtype[0])); - } - return sina_safe_list; +std::vector toScalarList(adiak_value_t *subvals, adiak_datatype_t *t) +{ + std::vector sina_safe_list; + for(int i = 0; i < t->num_elements; i++) + { + sina_safe_list.emplace_back(toScalar(subvals + i, t->subtype[0])); + } + return sina_safe_list; } - /** * Partner method to toScalarList, invoked when the children of an adiak list * type are strings (according to Sina). **/ -std::vector toStringList(adiak_value_t *subvals, adiak_datatype_t *t){ - std::vector sina_safe_list; - for (int i = 0; i < t->num_elements; i++) { - sina_safe_list.emplace_back(toString(subvals+i, t->subtype[0])); - } - return sina_safe_list; +std::vector toStringList(adiak_value_t *subvals, adiak_datatype_t *t) +{ + std::vector sina_safe_list; + for(int i = 0; i < t->num_elements; i++) + { + sina_safe_list.emplace_back(toString(subvals + i, t->subtype[0])); + } + return sina_safe_list; } -} +} // namespace -void adiakSinaCallback(const char *name, adiak_category_t, const char *subcategory, adiak_value_t *val, adiak_datatype_t *adiak_type, void *void_record) +void adiakSinaCallback(const char *name, + adiak_category_t, + const char *subcategory, + adiak_value_t *val, + adiak_datatype_t *adiak_type, + void *void_record) { - const SinaType sina_type = findSinaType(adiak_type); - axom::sina::Record *record = static_cast(void_record); - std::vector tags; - if(subcategory && subcategory[0]!='\0'){ - tags.emplace_back(subcategory); - } - switch (sina_type) { - case sina_unknown: - // If we don't know what it is, we can't store it, so as above... - throw std::runtime_error("Unknown Adiak type cannot be added to Sina record."); - case sina_scalar: { - char * s = adiak_type_to_string(adiak_type, 1); - tags.emplace_back(s); - free(s); - addDatum(name, toScalar(val, adiak_type), tags, record); - break; - } - case sina_string: { - char * s = adiak_type_to_string(adiak_type, 1); - tags.emplace_back(s); - free(s); - addDatum(name, toString(val, adiak_type), tags, record); - break; - } - case sina_file: - addFile(name, toString(val, adiak_type), record); - break; - case sina_list: { - // Sina doesn't really know/care the difference between list, tuple, set - // Further simplification: everything has to be the same type - // Even further simplification: nothing nested. In the future, depth>1 lists - // should be sent to user_defined - adiak_value_t *subvals = static_cast(val->v_ptr); - SinaType list_type = findSinaType(adiak_type->subtype[0]); - char * s = adiak_type_to_string(adiak_type->subtype[0], 1); - tags.emplace_back(s); - free(s); - switch (list_type) { - case sina_string: - addDatum(name, toStringList(subvals, adiak_type), tags, record); - break; - // Weird case wherein we're given a list of filenames, which we can somewhat manage - case sina_file: - for (int i=0; i < adiak_type->num_elements; i++) { - addFile(name, toString(subvals+i, adiak_type->subtype[0]), record); - } - break; - case sina_scalar: - addDatum(name, toScalarList(subvals, adiak_type), tags, record); - break; - case sina_unknown: - throw std::runtime_error("Type must not be unknown for list entries to be added to a Sina record"); - case sina_list: - throw std::runtime_error("Lists must not be nested for list entries to be added to a Sina record"); - default: - throw std::runtime_error("Type must be set for list entries to be added to a Sina record"); - } + const SinaType sina_type = findSinaType(adiak_type); + axom::sina::Record *record = static_cast(void_record); + std::vector tags; + if(subcategory && subcategory[0] != '\0') + { + tags.emplace_back(subcategory); + } + switch(sina_type) + { + case sina_unknown: + // If we don't know what it is, we can't store it, so as above... + throw std::runtime_error( + "Unknown Adiak type cannot be added to Sina record."); + case sina_scalar: + { + char *s = adiak_type_to_string(adiak_type, 1); + tags.emplace_back(s); + free(s); + addDatum(name, toScalar(val, adiak_type), tags, record); + break; + } + case sina_string: + { + char *s = adiak_type_to_string(adiak_type, 1); + tags.emplace_back(s); + free(s); + addDatum(name, toString(val, adiak_type), tags, record); + break; + } + case sina_file: + addFile(name, toString(val, adiak_type), record); + break; + case sina_list: + { + // Sina doesn't really know/care the difference between list, tuple, set + // Further simplification: everything has to be the same type + // Even further simplification: nothing nested. In the future, depth>1 lists + // should be sent to user_defined + adiak_value_t *subvals = static_cast(val->v_ptr); + SinaType list_type = findSinaType(adiak_type->subtype[0]); + char *s = adiak_type_to_string(adiak_type->subtype[0], 1); + tags.emplace_back(s); + free(s); + switch(list_type) + { + case sina_string: + addDatum(name, toStringList(subvals, adiak_type), tags, record); + break; + // Weird case wherein we're given a list of filenames, which we can somewhat manage + case sina_file: + for(int i = 0; i < adiak_type->num_elements; i++) + { + addFile(name, toString(subvals + i, adiak_type->subtype[0]), record); } - } + break; + case sina_scalar: + addDatum(name, toScalarList(subvals, adiak_type), tags, record); + break; + case sina_unknown: + throw std::runtime_error( + "Type must not be unknown for list entries to be added to a Sina " + "record"); + case sina_list: + throw std::runtime_error( + "Lists must not be nested for list entries to be added to a Sina " + "record"); + default: + throw std::runtime_error( + "Type must be set for list entries to be added to a Sina record"); + } + } + } } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom -#endif // AXOM_USE_ADIAK +#endif // AXOM_USE_ADIAK diff --git a/src/axom/sina/core/AdiakWriter.hpp b/src/axom/sina/core/AdiakWriter.hpp index 3612b60667..aba4d2c2e2 100644 --- a/src/axom/sina/core/AdiakWriter.hpp +++ b/src/axom/sina/core/AdiakWriter.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_ADIAK_HPP #define SINA_ADIAK_HPP @@ -20,15 +19,15 @@ #include "axom/config.hpp" #ifdef AXOM_USE_ADIAK -#include -#include + #include + #include -#include "axom/sina/core/ConduitUtil.hpp" -#include "axom/sina/core/Record.hpp" -#include "axom/sina/core/Run.hpp" + #include "axom/sina/core/ConduitUtil.hpp" + #include "axom/sina/core/Record.hpp" + #include "axom/sina/core/Run.hpp" extern "C" { -#include "adiak_tool.h" + #include "adiak_tool.h" } namespace axom @@ -53,12 +52,16 @@ namespace sina * anything like a CurveSet. As a result, to do that, you must hold on to * the Record object passed here as the opaque value and manipulate it directly. **/ -void adiakSinaCallback(const char *name, adiak_category_t category, const char *subcategory, adiak_value_t *value, adiak_datatype_t *t, void *opaque_value); - -} // end sina namespace -} // end axom namespace +void adiakSinaCallback(const char *name, + adiak_category_t category, + const char *subcategory, + adiak_value_t *value, + adiak_datatype_t *t, + void *opaque_value); -#endif // AXOM_USE_ADIAK +} // namespace sina +} // namespace axom -#endif // SINA_ADIAK_HPP +#endif // AXOM_USE_ADIAK +#endif // SINA_ADIAK_HPP diff --git a/src/axom/sina/core/ConduitUtil.cpp b/src/axom/sina/core/ConduitUtil.cpp index 36a65bd7e7..afaf2ae95d 100644 --- a/src/axom/sina/core/ConduitUtil.cpp +++ b/src/axom/sina/core/ConduitUtil.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - /*! ****************************************************************************** * @@ -42,120 +41,145 @@ namespace * @throws std::invalid_argument if the field is not a string */ std::string getExpectedString(conduit::Node const &field, - std::string const &fieldName, - std::string const &parentType) { - if (!field.dtype().is_string()) { - std::ostringstream message; - message << "The field '" << fieldName - << "' for objects of type '" << parentType - << "' must be a string, was '" - << field.dtype().name() << "'"; - throw std::invalid_argument(message.str()); - } - return field.as_string(); -} + std::string const &fieldName, + std::string const &parentType) +{ + if(!field.dtype().is_string()) + { + std::ostringstream message; + message << "The field '" << fieldName << "' for objects of type '" + << parentType << "' must be a string, was '" << field.dtype().name() + << "'"; + throw std::invalid_argument(message.str()); + } + return field.as_string(); } +} // namespace conduit::Node const &getRequiredField(std::string const &fieldName, - conduit::Node const &parent, std::string const &parentType) { - if (!parent.has_child(fieldName)) { - std::ostringstream message; - message << "The field '" << fieldName - << "' is required for objects of type '" << parentType << "'"; - throw std::invalid_argument(message.str()); - } - return parent.child(fieldName); + conduit::Node const &parent, + std::string const &parentType) +{ + if(!parent.has_child(fieldName)) + { + std::ostringstream message; + message << "The field '" << fieldName + << "' is required for objects of type '" << parentType << "'"; + throw std::invalid_argument(message.str()); + } + return parent.child(fieldName); } std::string getRequiredString(std::string const &fieldName, - conduit::Node const &parent, std::string const &parentType) { - conduit::Node const &field = getRequiredField(fieldName, parent, parentType); - return getExpectedString(field, fieldName, parentType); + conduit::Node const &parent, + std::string const &parentType) +{ + conduit::Node const &field = getRequiredField(fieldName, parent, parentType); + return getExpectedString(field, fieldName, parentType); } std::string getOptionalString(std::string const &fieldName, - conduit::Node const &parent, std::string const &parentType) { - if (!parent.has_child(fieldName) || parent.child(fieldName).dtype().is_empty()) { - return ""; - } - return getExpectedString(parent.child(fieldName), fieldName, parentType); + conduit::Node const &parent, + std::string const &parentType) +{ + if(!parent.has_child(fieldName) || parent.child(fieldName).dtype().is_empty()) + { + return ""; + } + return getExpectedString(parent.child(fieldName), fieldName, parentType); } double getRequiredDouble(std::string const &fieldName, - conduit::Node const &parent, std::string const &parentType) { - auto &ref = getRequiredField(fieldName, parent, parentType); - if (!ref.dtype().is_number()) { - std::ostringstream message; - message << "The field '" << fieldName - << "' for objects of type '" << parentType - << "' must be a double"; - throw std::invalid_argument(message.str()); - } - return ref.as_double(); + conduit::Node const &parent, + std::string const &parentType) +{ + auto &ref = getRequiredField(fieldName, parent, parentType); + if(!ref.dtype().is_number()) + { + std::ostringstream message; + message << "The field '" << fieldName << "' for objects of type '" + << parentType << "' must be a double"; + throw std::invalid_argument(message.str()); + } + return ref.as_double(); } -void addStringsToNode(conduit::Node &parent, std::string const &child_name, - std::vector const &string_values){ +void addStringsToNode(conduit::Node &parent, + std::string const &child_name, + std::vector const &string_values) +{ // If the child already exists, add_child returns it conduit::Node &child_node = parent.add_child(child_name); for(auto &value : string_values) { - auto &list_entry = child_node.append(); - list_entry.set(value); + auto &list_entry = child_node.append(); + list_entry.set(value); } // If there were no children, this will be a null node rather than a list. // We prefer empty lists to null values in our serialized JSON, so force // this to be a list - if (child_node.number_of_children() == 0) { - child_node.set_dtype(conduit::DataType::list()); + if(child_node.number_of_children() == 0) + { + child_node.set_dtype(conduit::DataType::list()); } } std::vector toDoubleVector(conduit::Node const &node, - std::string const &name) { - if (node.dtype().is_list() && node.dtype().number_of_elements() == 0) { - return std::vector{}; - } - conduit::Node asDoubles; - try { - node.to_double_array(asDoubles); - } catch (conduit::Error const &err) { - std::ostringstream errStream; - errStream << "Error trying to convert node \"" << name - << "\" into a list of doubles" << err.what(); - throw std::invalid_argument(errStream.str()); - } - double const *start = asDoubles.as_double_ptr(); - auto count = static_cast::size_type>( - asDoubles.dtype().number_of_elements()); - return std::vector{start, start + count}; + std::string const &name) +{ + if(node.dtype().is_list() && node.dtype().number_of_elements() == 0) + { + return std::vector {}; + } + conduit::Node asDoubles; + try + { + node.to_double_array(asDoubles); + } + catch(conduit::Error const &err) + { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of doubles" << err.what(); + throw std::invalid_argument(errStream.str()); + } + double const *start = asDoubles.as_double_ptr(); + auto count = static_cast::size_type>( + asDoubles.dtype().number_of_elements()); + return std::vector {start, start + count}; } std::vector toStringVector(conduit::Node const &node, - std::string const &name) { - std::vector converted; - if (!node.dtype().is_list()) { - std::ostringstream errStream; - errStream << "Error trying to convert node \"" << name - << "\" into a list of strings. It is not a list. " - << node.to_json_default(); - throw std::invalid_argument(errStream.str()); + std::string const &name) +{ + std::vector converted; + if(!node.dtype().is_list()) + { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of strings. It is not a list. " + << node.to_json_default(); + throw std::invalid_argument(errStream.str()); + } + for(auto iter = node.children(); iter.has_next();) + { + auto &child = iter.next(); + if(child.dtype().is_string()) + { + converted.emplace_back(child.as_string()); } - for (auto iter = node.children(); iter.has_next(); ) { - auto &child = iter.next(); - if (child.dtype().is_string()) { - converted.emplace_back(child.as_string()); - } else { - std::ostringstream errStream; - errStream << "Error trying to convert node \"" << name - << "\" into a list of strings. A value is not a string. " - << node.to_json_default(); - throw std::invalid_argument(errStream.str()); - } + else + { + std::ostringstream errStream; + errStream << "Error trying to convert node \"" << name + << "\" into a list of strings. A value is not a string. " + << node.to_json_default(); + throw std::invalid_argument(errStream.str()); } - return converted; + } + return converted; } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/ConduitUtil.hpp b/src/axom/sina/core/ConduitUtil.hpp index 79c04ddf63..453a9a62d0 100644 --- a/src/axom/sina/core/ConduitUtil.hpp +++ b/src/axom/sina/core/ConduitUtil.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_JSONUTIL_HPP #define SINA_JSONUTIL_HPP @@ -38,7 +37,8 @@ namespace sina * \throws std::invalid_argument if the field does not exist */ conduit::Node const &getRequiredField(std::string const &fieldName, - conduit::Node const &parent, std::string const &parentType); + conduit::Node const &parent, + std::string const &parentType); /** * \brief Get the value of a required field from a conduit Node. The field value @@ -52,7 +52,8 @@ conduit::Node const &getRequiredField(std::string const &fieldName, * \throws std::invalid_argument if the field does not exist or is not a string */ std::string getRequiredString(std::string const &fieldName, - conduit::Node const &parent, std::string const &parentType); + conduit::Node const &parent, + std::string const &parentType); /** * \brief Get the value of a required field from a conduit Node. The field value @@ -66,7 +67,8 @@ std::string getRequiredString(std::string const &fieldName, * \throws std::invalid_argument if the field does not exist or is not a double */ double getRequiredDouble(std::string const &fieldName, - conduit::Node const &parent, std::string const &parentType); + conduit::Node const &parent, + std::string const &parentType); /** * \brief Get the value of an optional field from a conduit Node. The field value @@ -81,7 +83,8 @@ double getRequiredDouble(std::string const &fieldName, * \throws std::invalid_argument if the field exists but is not a string */ std::string getOptionalString(std::string const &fieldName, - conduit::Node const &parent, std::string const &parentType); + conduit::Node const &parent, + std::string const &parentType); /** * \brief Convert the given node to a vector of doubles. @@ -92,7 +95,7 @@ std::string getOptionalString(std::string const &fieldName, * \throws std::invalid_argument if the node is not a list of doubles */ std::vector toDoubleVector(conduit::Node const &node, - std::string const &name); + std::string const &name); /** * \brief Convert the given node to a vector of strings. @@ -103,7 +106,7 @@ std::vector toDoubleVector(conduit::Node const &node, * \throws std::invalid_argument if the node is not a list of strings */ std::vector toStringVector(conduit::Node const &node, - std::string const &name); + std::string const &name); /** * \brief Add a vector of strings to a Node. This operation's not natively @@ -113,10 +116,11 @@ std::vector toStringVector(conduit::Node const &node, * \param child_name the name of the child (aka the name of the field) * \param string_values the data values for the field */ -void addStringsToNode(conduit::Node &parent, const std::string &child_name, - std::vector const &string_values); +void addStringsToNode(conduit::Node &parent, + const std::string &child_name, + std::vector const &string_values); } // end namespace sina } // end namespace axom -#endif //SINA_JSONUTIL_HPP +#endif //SINA_JSONUTIL_HPP diff --git a/src/axom/sina/core/Curve.cpp b/src/axom/sina/core/Curve.cpp index 99a3e447a2..516d560378 100644 --- a/src/axom/sina/core/Curve.cpp +++ b/src/axom/sina/core/Curve.cpp @@ -23,53 +23,63 @@ namespace axom namespace sina { -namespace { +namespace +{ constexpr auto CURVE_TYPE_NAME = "curve"; constexpr auto VALUES_KEY = "value"; constexpr auto UNITS_KEY = "units"; constexpr auto TAGS_KEY = "tags"; -} +} // namespace -Curve::Curve(std::string name_, std::vector values_) : - name{std::move(name_)}, values{std::move(values_)}, units{}, tags{} {} +Curve::Curve(std::string name_, std::vector values_) + : name {std::move(name_)} + , values {std::move(values_)} + , units {} + , tags {} +{ } +Curve::Curve(std::string name_, double const *values_, std::size_t numValues) + : name {std::move(name_)} + , values {values_, values_ + numValues} + , units {} + , tags {} +{ } -Curve::Curve(std::string name_, double const *values_, std::size_t numValues) : - name{std::move(name_)}, values{values_, values_ + numValues}, - units{}, tags{} {} - -Curve::Curve(std::string name_, conduit::Node const &curveAsNode) : - name{std::move(name_)}, values{}, units{}, tags{} { - auto &valuesAsNode = getRequiredField(VALUES_KEY, curveAsNode, - CURVE_TYPE_NAME); - values = toDoubleVector(valuesAsNode, VALUES_KEY); +Curve::Curve(std::string name_, conduit::Node const &curveAsNode) + : name {std::move(name_)} + , values {} + , units {} + , tags {} +{ + auto &valuesAsNode = getRequiredField(VALUES_KEY, curveAsNode, CURVE_TYPE_NAME); + values = toDoubleVector(valuesAsNode, VALUES_KEY); - units = getOptionalString(UNITS_KEY, curveAsNode, CURVE_TYPE_NAME); + units = getOptionalString(UNITS_KEY, curveAsNode, CURVE_TYPE_NAME); - if (curveAsNode.has_child(TAGS_KEY)) { - tags = toStringVector(curveAsNode[TAGS_KEY], TAGS_KEY); - } + if(curveAsNode.has_child(TAGS_KEY)) + { + tags = toStringVector(curveAsNode[TAGS_KEY], TAGS_KEY); + } } -void Curve::setUnits(std::string units_) { - units = std::move(units_); -} +void Curve::setUnits(std::string units_) { units = std::move(units_); } -void Curve::setTags(std::vector tags_) { - tags = std::move(tags_); -} +void Curve::setTags(std::vector tags_) { tags = std::move(tags_); } -conduit::Node Curve::toNode() const { - conduit::Node asNode; - asNode[VALUES_KEY] = values; - if (!units.empty()) { - asNode[UNITS_KEY] = units; - } - if (!tags.empty()) { - addStringsToNode(asNode, TAGS_KEY, tags); - } - return asNode; +conduit::Node Curve::toNode() const +{ + conduit::Node asNode; + asNode[VALUES_KEY] = values; + if(!units.empty()) + { + asNode[UNITS_KEY] = units; + } + if(!tags.empty()) + { + addStringsToNode(asNode, TAGS_KEY, tags); + } + return asNode; } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/Curve.hpp b/src/axom/sina/core/Curve.hpp index 5975e773d9..17b093dd27 100644 --- a/src/axom/sina/core/Curve.hpp +++ b/src/axom/sina/core/Curve.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_CURVE_HPP #define SINA_CURVE_HPP @@ -30,99 +29,91 @@ namespace sina /** * A Curve represents a 1-dimensional curve inside a CurveSet. */ -class Curve { +class Curve +{ public: - /** + /** * \brief Create a Curve with the given name and values * * \param name the name of the curve * \param values the curve's values */ - Curve(std::string name, std::vector values); + Curve(std::string name, std::vector values); - /** + /** * \brief Create a Curve with the given name and values * * \param name the name of the curve * \param values the curve's values * \param numValues the number of values. */ - Curve(std::string name, double const *values, std::size_t numValues); + Curve(std::string name, double const *values, std::size_t numValues); - /** + /** * \brief Create a Curve by deserializing a conduit node. * * \param name the name of the curve * \param curveAsNode the serialized version of a curve */ - Curve(std::string name, conduit::Node const &curveAsNode); + Curve(std::string name, conduit::Node const &curveAsNode); - /** + /** * \brief Get the curve's name. * * \return the curve's name */ - std::string const &getName() const { - return name; - } + std::string const &getName() const { return name; } - /** + /** * \brief Get the values of the curve. * * \return the curve's values */ - std::vector const &getValues() const { - return values; - } + std::vector const &getValues() const { return values; } - /** + /** * \brief Set the units of the values. * * \param units the value's units */ - void setUnits(std::string units); + void setUnits(std::string units); - /** + /** * \brief Get the units of the values. * * \return the value's units */ - std::string const &getUnits() const { - return units; - } + std::string const &getUnits() const { return units; } - /** + /** * \brief Set the tags for this curve. * * \param tags the curve's tags */ - void setTags(std::vector tags); + void setTags(std::vector tags); - /** + /** * \brief Get the tags for this curve. * * \return the curve's tags */ - std::vector const &getTags() const { - return tags; - } + std::vector const &getTags() const { return tags; } - /** + /** * \brief Convert this curve to a Conduit node. * * \return a Conduit representation of this curve */ - conduit::Node toNode() const; + conduit::Node toNode() const; private: - std::string name; - std::vector values; - std::string units; - std::vector tags; + std::string name; + std::vector values; + std::string units; + std::vector tags; }; -} // end sina namespace -} // end axom namespace - +} // namespace sina +} // namespace axom -#endif //SINA_CURVE_HPP +#endif //SINA_CURVE_HPP diff --git a/src/axom/sina/core/CurveSet.cpp b/src/axom/sina/core/CurveSet.cpp index 08e16fb9fd..c16afa2b68 100644 --- a/src/axom/sina/core/CurveSet.cpp +++ b/src/axom/sina/core/CurveSet.cpp @@ -26,7 +26,8 @@ namespace axom namespace sina { -namespace { +namespace +{ constexpr auto INDEPENDENT_KEY = "independent"; constexpr auto DEPENDENT_KEY = "dependent"; @@ -37,14 +38,18 @@ constexpr auto DEPENDENT_KEY = "dependent"; * @param curve the curve to add * @param curves the CurveMap to which to add the curve */ -void addCurve(Curve &&curve, CurveSet::CurveMap &curves) { - auto &curveName = curve.getName(); - auto existing = curves.find(curveName); - if (existing == curves.end()) { - curves.insert(std::make_pair(curveName, curve)); - } else { - existing->second = curve; - } +void addCurve(Curve &&curve, CurveSet::CurveMap &curves) +{ + auto &curveName = curve.getName(); + auto existing = curves.find(curveName); + if(existing == curves.end()) + { + curves.insert(std::make_pair(curveName, curve)); + } + else + { + existing->second = curve; + } } /** @@ -55,21 +60,24 @@ void addCurve(Curve &&curve, CurveSet::CurveMap &curves) { * @return a CurveMap representing the specified child */ CurveSet::CurveMap extractCurveMap(conduit::Node const &parent, - std::string const &childNodeName) { - CurveSet::CurveMap curveMap; - if (!parent.has_child(childNodeName)) { - return curveMap; - } - - auto &mapAsNode = parent.child(childNodeName); - for (auto iter = mapAsNode.children(); iter.has_next(); ) { - auto &curveAsNode = iter.next(); - std::string curveName = iter.name(); - Curve curve{curveName, curveAsNode}; - curveMap.insert(std::make_pair(std::move(curveName), std::move(curve))); - } - + std::string const &childNodeName) +{ + CurveSet::CurveMap curveMap; + if(!parent.has_child(childNodeName)) + { return curveMap; + } + + auto &mapAsNode = parent.child(childNodeName); + for(auto iter = mapAsNode.children(); iter.has_next();) + { + auto &curveAsNode = iter.next(); + std::string curveName = iter.name(); + Curve curve {curveName, curveAsNode}; + curveMap.insert(std::make_pair(std::move(curveName), std::move(curve))); + } + + return curveMap; }; /** @@ -78,41 +86,48 @@ CurveSet::CurveMap extractCurveMap(conduit::Node const &parent, * @param curveMap the CurveMap to convert * @return the map as a node */ -conduit::Node createCurveMapNode(CurveSet::CurveMap const &curveMap) { - conduit::Node mapNode; - mapNode.set_dtype(conduit::DataType::object()); - for (auto &entry : curveMap) { - mapNode.add_child(entry.first) = entry.second.toNode(); - } - return mapNode; +conduit::Node createCurveMapNode(CurveSet::CurveMap const &curveMap) +{ + conduit::Node mapNode; + mapNode.set_dtype(conduit::DataType::object()); + for(auto &entry : curveMap) + { + mapNode.add_child(entry.first) = entry.second.toNode(); + } + return mapNode; } -} +} // namespace -CurveSet::CurveSet(std::string name_) : name{std::move(name_)}, - independentCurves{}, - dependentCurves{} {} +CurveSet::CurveSet(std::string name_) + : name {std::move(name_)} + , independentCurves {} + , dependentCurves {} +{ } CurveSet::CurveSet(std::string name_, conduit::Node const &node) - : name{std::move(name_)}, - independentCurves{extractCurveMap(node, INDEPENDENT_KEY)}, - dependentCurves{extractCurveMap(node, DEPENDENT_KEY)} { -} + : name {std::move(name_)} + , independentCurves {extractCurveMap(node, INDEPENDENT_KEY)} + , dependentCurves {extractCurveMap(node, DEPENDENT_KEY)} +{ } -void CurveSet::addIndependentCurve(Curve curve) { - addCurve(std::move(curve), independentCurves); +void CurveSet::addIndependentCurve(Curve curve) +{ + addCurve(std::move(curve), independentCurves); } -void CurveSet::addDependentCurve(Curve curve) { - addCurve(std::move(curve), dependentCurves); +void CurveSet::addDependentCurve(Curve curve) +{ + addCurve(std::move(curve), dependentCurves); } -conduit::Node CurveSet::toNode() const { - conduit::Node asNode; - asNode[INDEPENDENT_KEY] = createCurveMapNode(independentCurves); - asNode[DEPENDENT_KEY] = createCurveMapNode(dependentCurves); - return asNode; +conduit::Node CurveSet::toNode() const +{ + conduit::Node asNode; + asNode[INDEPENDENT_KEY] = createCurveMapNode(independentCurves); + asNode[DEPENDENT_KEY] = createCurveMapNode(dependentCurves); + return asNode; } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/CurveSet.hpp b/src/axom/sina/core/CurveSet.hpp index efc5538b20..fb5ec3d5e3 100644 --- a/src/axom/sina/core/CurveSet.hpp +++ b/src/axom/sina/core/CurveSet.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_CURVESET_HPP #define SINA_CURVESET_HPP @@ -40,83 +39,78 @@ namespace sina * \sa Record * \sa Curve */ -class CurveSet { +class CurveSet +{ public: - /** + /** * An unordered map of Curve objects. */ - using CurveMap = std::unordered_map; + using CurveMap = std::unordered_map; - /** + /** * \brief Create a CurveSet with the given name * * \param name the name of the CurveSet */ - explicit CurveSet(std::string name); + explicit CurveSet(std::string name); - /** + /** * \brief Create a CurveSet from the given Conduit node. * * \param name the name of the CurveSet * \param node the Conduit node representing the CurveSet */ - CurveSet(std::string name, conduit::Node const &node); + CurveSet(std::string name, conduit::Node const &node); - /** + /** * \brief Get the name of the this CurveSet. * * \return the curve set's name */ - std::string const & getName() const { - return name; - } + std::string const &getName() const { return name; } - /** + /** * \brief Add an independent curve. * * \param curve the curve to add */ - void addIndependentCurve(Curve curve); + void addIndependentCurve(Curve curve); - /** + /** * \brief Add a dependent curve. * * \param curve the curve to add */ - void addDependentCurve(Curve curve); + void addDependentCurve(Curve curve); - /** + /** * \brief Get a map of all the independent curves. * * \return a map of all the independent curves */ - CurveMap const &getIndependentCurves() const { - return independentCurves; - } + CurveMap const &getIndependentCurves() const { return independentCurves; } - /** + /** * \brief Get a map of all the dependent curves. * * \return a map of all the dependent curves */ - CurveMap const &getDependentCurves() const { - return dependentCurves; - } + CurveMap const &getDependentCurves() const { return dependentCurves; } - /** + /** * \brief Convert his CurveSet to a Conduit node. * * \return the Node representation of this CurveSet */ - conduit::Node toNode() const; + conduit::Node toNode() const; private: - std::string name; - CurveMap independentCurves; - CurveMap dependentCurves; + std::string name; + CurveMap independentCurves; + CurveMap dependentCurves; }; -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom -#endif //SINA_CURVESET_HPP +#endif //SINA_CURVESET_HPP diff --git a/src/axom/sina/core/DataHolder.cpp b/src/axom/sina/core/DataHolder.cpp index c9aa3b50de..f84414a8d9 100644 --- a/src/axom/sina/core/DataHolder.cpp +++ b/src/axom/sina/core/DataHolder.cpp @@ -20,129 +20,167 @@ #include -namespace { +namespace +{ char const DATA_FIELD[] = "data"; char const CURVE_SETS_FIELD[] = "curve_sets"; char const LIBRARY_DATA_FIELD[] = "library_data"; char const USER_DEFINED_FIELD[] = "user_defined"; -} +} // namespace namespace axom { namespace sina { -void DataHolder::add(std::string name, Datum datum) { - auto existing = data.find(name); - if (existing == data.end()) { - data.emplace(std::make_pair(std::move(name), datum)); - } else { - existing->second = datum; - } +void DataHolder::add(std::string name, Datum datum) +{ + auto existing = data.find(name); + if(existing == data.end()) + { + data.emplace(std::make_pair(std::move(name), datum)); + } + else + { + existing->second = datum; + } } -void DataHolder::add(CurveSet curveSet) { - auto name = curveSet.getName(); - auto existing = curveSets.find(name); - if (existing == curveSets.end()) { - curveSets.emplace(name, std::move(curveSet)); - } else { - existing->second = std::move(curveSet); - } +void DataHolder::add(CurveSet curveSet) +{ + auto name = curveSet.getName(); + auto existing = curveSets.find(name); + if(existing == curveSets.end()) + { + curveSets.emplace(name, std::move(curveSet)); + } + else + { + existing->second = std::move(curveSet); + } } -std::shared_ptr DataHolder::addLibraryData(std::string const &name) { +std::shared_ptr DataHolder::addLibraryData(std::string const &name) +{ auto existing = libraryData.find(name); - if (existing == libraryData.end()) { - libraryData.emplace(name, std::make_shared()); - } else { - existing->second = std::make_shared(); - } + if(existing == libraryData.end()) + { + libraryData.emplace(name, std::make_shared()); + } + else + { + existing->second = std::make_shared(); + } return libraryData.at(name); } -std::shared_ptr DataHolder::addLibraryData(std::string const &name, conduit::Node existingLibraryData) { +std::shared_ptr DataHolder::addLibraryData( + std::string const &name, + conduit::Node existingLibraryData) +{ auto existing = libraryData.find(name); - if (existing == libraryData.end()) { - libraryData.emplace(name, std::make_shared(existingLibraryData)); - } else { - existing->second = std::make_shared(existingLibraryData); - } + if(existing == libraryData.end()) + { + libraryData.emplace(name, std::make_shared(existingLibraryData)); + } + else + { + existing->second = std::make_shared(existingLibraryData); + } return libraryData.at(name); } -void DataHolder::setUserDefinedContent(conduit::Node userDefined_) { - userDefined = std::move(userDefined_); +void DataHolder::setUserDefinedContent(conduit::Node userDefined_) +{ + userDefined = std::move(userDefined_); } -conduit::Node DataHolder::toNode() const { - conduit::Node asNode; - asNode.set(conduit::DataType::object()); - if(!libraryData.empty()){ - //Loop through vector of data and append Json - conduit::Node libRef; - for(auto &lib : libraryData){ - libRef.add_child(lib.first) = lib.second->toNode(); - } - asNode[LIBRARY_DATA_FIELD] = libRef; - } - if(!curveSets.empty()){ - conduit::Node curveSetsNode; - for(auto &entry : curveSets){ - curveSetsNode.add_child(entry.first) = entry.second.toNode(); - } - asNode[CURVE_SETS_FIELD] = curveSetsNode; +conduit::Node DataHolder::toNode() const +{ + conduit::Node asNode; + asNode.set(conduit::DataType::object()); + if(!libraryData.empty()) + { + //Loop through vector of data and append Json + conduit::Node libRef; + for(auto &lib : libraryData) + { + libRef.add_child(lib.first) = lib.second->toNode(); } - if(!data.empty()){ - //Loop through vector of data and append Json - conduit::Node datumRef; - for(auto &datum : data){ - datumRef.add_child(datum.first) = datum.second.toNode(); - } - asNode[DATA_FIELD] = datumRef; + asNode[LIBRARY_DATA_FIELD] = libRef; + } + if(!curveSets.empty()) + { + conduit::Node curveSetsNode; + for(auto &entry : curveSets) + { + curveSetsNode.add_child(entry.first) = entry.second.toNode(); } - if(!userDefined.dtype().is_empty()){ - asNode[USER_DEFINED_FIELD] = userDefined; + asNode[CURVE_SETS_FIELD] = curveSetsNode; + } + if(!data.empty()) + { + //Loop through vector of data and append Json + conduit::Node datumRef; + for(auto &datum : data) + { + datumRef.add_child(datum.first) = datum.second.toNode(); } - return asNode; + asNode[DATA_FIELD] = datumRef; + } + if(!userDefined.dtype().is_empty()) + { + asNode[USER_DEFINED_FIELD] = userDefined; + } + return asNode; } -DataHolder::DataHolder(conduit::Node const &asNode) { - if(asNode.has_child(DATA_FIELD)){ - auto dataIter = asNode[DATA_FIELD].children(); - //Loop through DATA_FIELD objects and add them to data: - while(dataIter.has_next()){ - auto &namedDatum = dataIter.next(); - data.emplace(std::make_pair(dataIter.name(), Datum(namedDatum))); - } +DataHolder::DataHolder(conduit::Node const &asNode) +{ + if(asNode.has_child(DATA_FIELD)) + { + auto dataIter = asNode[DATA_FIELD].children(); + //Loop through DATA_FIELD objects and add them to data: + while(dataIter.has_next()) + { + auto &namedDatum = dataIter.next(); + data.emplace(std::make_pair(dataIter.name(), Datum(namedDatum))); } - if (asNode.has_child(CURVE_SETS_FIELD)) { - auto curveSetsIter = asNode[CURVE_SETS_FIELD].children(); - while(curveSetsIter.has_next()){ - auto &curveSetNode = curveSetsIter.next(); - std::string name = curveSetsIter.name(); - CurveSet cs{name, curveSetNode}; - curveSets.emplace(std::make_pair(std::move(name), std::move(cs))); - } + } + if(asNode.has_child(CURVE_SETS_FIELD)) + { + auto curveSetsIter = asNode[CURVE_SETS_FIELD].children(); + while(curveSetsIter.has_next()) + { + auto &curveSetNode = curveSetsIter.next(); + std::string name = curveSetsIter.name(); + CurveSet cs {name, curveSetNode}; + curveSets.emplace(std::make_pair(std::move(name), std::move(cs))); } - if(asNode.has_child(LIBRARY_DATA_FIELD)) { - auto libraryIter = asNode[LIBRARY_DATA_FIELD].children(); - while(libraryIter.has_next()){ - auto &libraryDataNode = libraryIter.next(); - std::string name = libraryIter.name(); - libraryData.emplace(std::make_pair(std::move(name), - std::make_shared(libraryDataNode))); - } + } + if(asNode.has_child(LIBRARY_DATA_FIELD)) + { + auto libraryIter = asNode[LIBRARY_DATA_FIELD].children(); + while(libraryIter.has_next()) + { + auto &libraryDataNode = libraryIter.next(); + std::string name = libraryIter.name(); + libraryData.emplace( + std::make_pair(std::move(name), + std::make_shared(libraryDataNode))); } - if(asNode.has_child(USER_DEFINED_FIELD)) { - userDefined = asNode[USER_DEFINED_FIELD]; - if (!userDefined.dtype().is_object()) { - throw std::invalid_argument("user_defined must be an object Node"); - } + } + if(asNode.has_child(USER_DEFINED_FIELD)) + { + userDefined = asNode[USER_DEFINED_FIELD]; + if(!userDefined.dtype().is_object()) + { + throw std::invalid_argument("user_defined must be an object Node"); } } +} -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/DataHolder.hpp b/src/axom/sina/core/DataHolder.hpp index b315e5dc66..fe79d670a4 100644 --- a/src/axom/sina/core/DataHolder.hpp +++ b/src/axom/sina/core/DataHolder.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_DATAHOLDER_HPP #define SINA_DATAHOLDER_HPP @@ -41,84 +40,82 @@ namespace sina * \sa Record * \sa LibraryDataMap */ -class DataHolder { +class DataHolder +{ public: - /** + /** * An unordered map of Datum objects. */ - using DatumMap = std::unordered_map; + using DatumMap = std::unordered_map; - /** + /** * An unordered map of CurveSet objects. */ - using CurveSetMap = std::unordered_map; + using CurveSetMap = std::unordered_map; - /** + /** * An unordered map of shared pointers to DataHolder objects. */ - using LibraryDataMap = std::unordered_map>; + using LibraryDataMap = + std::unordered_map>; - /** + /** * Construct an empty DataHolder. */ - DataHolder() = default; + DataHolder() = default; - /** + /** * Virtual destructor to automatically clean up resources held by an instance of the DataHolder class. */ - virtual ~DataHolder() = default; + virtual ~DataHolder() = default; - /** + /** * Copy constructor that disallows this constructor type. */ - DataHolder(DataHolder const &) = delete; + DataHolder(DataHolder const &) = delete; - /** + /** * Disable copy assignment. */ - DataHolder &operator=(DataHolder const &) = delete; + DataHolder &operator=(DataHolder const &) = delete; - /** + /** * \brief Construct a DataHolder from its conduit Node representation. * * \param asNode the DataHolder as a Node */ - explicit DataHolder(conduit::Node const &asNode); + explicit DataHolder(conduit::Node const &asNode); - /** + /** * \brief Get the DataHolder's data. * * \return the DataHolder's data */ - DatumMap const &getData() const noexcept { - return data; - } + DatumMap const &getData() const noexcept { return data; } - /** + /** * \brief Add a Datum to this DataHolder. * * \param name the key for the Datum to add * \param datum the Datum to add */ - void add(std::string name, Datum datum); + void add(std::string name, Datum datum); - /** + /** * \brief Add a CurveSet to this DataHolder. * * \param curveSet the CurveSet to add */ - void add(CurveSet curveSet); + void add(CurveSet curveSet); - /** + /** * \brief Get the curve sets associated with this DataHolder. * * \return the dataholder's curve sets */ - CurveSetMap const &getCurveSets() const noexcept { - return curveSets; - } + CurveSetMap const &getCurveSets() const noexcept { return curveSets; } - /** + /** * \brief Add a new library to this DataHolder. * * If you try to add a library with a name that already exists, the old @@ -127,83 +124,82 @@ class DataHolder { * \return a pointer to a new DataHolder for a library * of the given name. */ - std::shared_ptr addLibraryData(std::string const &name); + std::shared_ptr addLibraryData(std::string const &name); - /** + /** * \brief Add a new library to this DataHolder with existing library data. * * \return a pointer to a new DataHolder for a library of the given name. */ - std::shared_ptr addLibraryData(std::string const &name, conduit::Node existingLibraryData); + std::shared_ptr addLibraryData(std::string const &name, + conduit::Node existingLibraryData); - /** + /** * \brief Get all library data associated with this DataHolder. * * \return the dataholder's library data */ - LibraryDataMap const &getLibraryData() const noexcept { - return libraryData; - } + LibraryDataMap const &getLibraryData() const noexcept { return libraryData; } - /** + /** * \brief Get a specific library associated with this DataHolder. * * \return the dataholder's library data */ - std::shared_ptr getLibraryData(std::string const &libraryName) { - return libraryData.at(libraryName); - } + std::shared_ptr getLibraryData(std::string const &libraryName) + { + return libraryData.at(libraryName); + } - /** + /** * \brief Get a specific library associated with this DataHolder. * * \return the dataholder's library data */ - std::shared_ptr const getLibraryData(std::string const &libraryName) const { - return libraryData.at(libraryName); - } + std::shared_ptr const getLibraryData(std::string const &libraryName) const + { + return libraryData.at(libraryName); + } - /** + /** * \brief Get the user-defined content of the object. * * \return the user-defined content */ - conduit::Node const &getUserDefinedContent() const noexcept { - return userDefined; - } + conduit::Node const &getUserDefinedContent() const noexcept + { + return userDefined; + } - /** + /** * \brief Get the user-defined content of the object. * * \return the user-defined content */ - conduit::Node &getUserDefinedContent() noexcept { - return userDefined; - } + conduit::Node &getUserDefinedContent() noexcept { return userDefined; } - /** + /** * \brief Set the user-defined content of the object. * * \param userDefined the user-defined content. Must be an object (key/value pairs) */ - void setUserDefinedContent(conduit::Node userDefined); + void setUserDefinedContent(conduit::Node userDefined); - /** + /** * \brief Convert this DataHolder to its conduit Node representation. * * \return the Node representation of this DataHolder. */ - virtual conduit::Node toNode() const; - + virtual conduit::Node toNode() const; private: - CurveSetMap curveSets; - DatumMap data; - LibraryDataMap libraryData; - conduit::Node userDefined; + CurveSetMap curveSets; + DatumMap data; + LibraryDataMap libraryData; + conduit::Node userDefined; }; -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom -#endif //SINA_DATAHOLDER_HPP +#endif //SINA_DATAHOLDER_HPP diff --git a/src/axom/sina/core/Datum.cpp b/src/axom/sina/core/Datum.cpp index 7e40be7a9a..cf457a3335 100644 --- a/src/axom/sina/core/Datum.cpp +++ b/src/axom/sina/core/Datum.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - /*! ****************************************************************************** * @@ -21,164 +20,185 @@ #include #include -namespace { +namespace +{ char const VALUE_FIELD[] = "value"; char const UNITS_FIELD[] = "units"; char const TAGS_FIELD[] = "tags"; char const DATA_PARENT_TYPE[] = "data"; -} +} // namespace namespace axom { namespace sina { -Datum::Datum(const std::string &value_) : - stringValue{value_}{ - //Set type to String, as we know it uses strings - type = ValueType::String; +Datum::Datum(const std::string &value_) : stringValue {value_} +{ + //Set type to String, as we know it uses strings + type = ValueType::String; } -Datum::Datum(const double &value_) : - scalarValue{value_}{ - //Set type to Scalar, as we know it uses doubles - type = ValueType::Scalar; +Datum::Datum(const double &value_) : scalarValue {value_} +{ + //Set type to Scalar, as we know it uses doubles + type = ValueType::Scalar; } -Datum::Datum(const std::vector &value_) : - stringArrayValue{value_}{ - //Set type to StringArray, as we know it uses an array of strings - type = ValueType::StringArray; +Datum::Datum(const std::vector &value_) : stringArrayValue {value_} +{ + //Set type to StringArray, as we know it uses an array of strings + type = ValueType::StringArray; } -Datum::Datum(const std::vector &value_) : - scalarArrayValue{value_}{ - //Set type to ScalarArray, as we know it uses an array of doubles - type = ValueType::ScalarArray; +Datum::Datum(const std::vector &value_) : scalarArrayValue {value_} +{ + //Set type to ScalarArray, as we know it uses an array of doubles + type = ValueType::ScalarArray; } -Datum::Datum(conduit::Node const &asNode) { - //Need to determine what type of Datum we have: Scalar (double), String, - //or list of one of those two. - conduit::Node valueNode = getRequiredField(VALUE_FIELD, asNode, DATA_PARENT_TYPE); - if(valueNode.dtype().is_string()){ - stringValue = valueNode.as_string(); - type = ValueType::String; - } - else if(valueNode.dtype().is_number() && valueNode.dtype().number_of_elements() == 1){ - scalarValue = valueNode.to_double(); - type = ValueType::Scalar; +Datum::Datum(conduit::Node const &asNode) +{ + //Need to determine what type of Datum we have: Scalar (double), String, + //or list of one of those two. + conduit::Node valueNode = + getRequiredField(VALUE_FIELD, asNode, DATA_PARENT_TYPE); + if(valueNode.dtype().is_string()) + { + stringValue = valueNode.as_string(); + type = ValueType::String; + } + else if(valueNode.dtype().is_number() && + valueNode.dtype().number_of_elements() == 1) + { + scalarValue = valueNode.to_double(); + type = ValueType::Scalar; + } + // There are two different ways to end up with a "list" of numbers in conduit, but + // only one of them tests True for is_list. This handles the other. + else if(valueNode.dtype().is_number()) + { + type = ValueType::ScalarArray; + // What's passed in could be an array of any numeric type + // We pass a cast copy into captureNode + conduit::Node captureNode; + valueNode.to_float64_array(captureNode); + std::vector array_as_vect( + captureNode.as_double_ptr(), + captureNode.as_double_ptr() + captureNode.dtype().number_of_elements()); + scalarArrayValue = array_as_vect; + } + else if(valueNode.dtype().is_list()) + { + //An empty list is assumed to be an empty list of doubles. + //This only works because this field is immutable! + //If this ever changes, or if Datum's type is used directly to make + //decisions (ex: Sina deciding where to store data), this logic + //should be revisited. + if(valueNode.number_of_children() == 0 || valueNode[0].dtype().is_number()) + { + type = ValueType::ScalarArray; } - // There are two different ways to end up with a "list" of numbers in conduit, but - // only one of them tests True for is_list. This handles the other. - else if(valueNode.dtype().is_number()){ - type = ValueType::ScalarArray; - // What's passed in could be an array of any numeric type - // We pass a cast copy into captureNode - conduit::Node captureNode; - valueNode.to_float64_array(captureNode); - std::vector array_as_vect(captureNode.as_double_ptr(), - captureNode.as_double_ptr() + captureNode.dtype().number_of_elements()); - scalarArrayValue = array_as_vect; + else if(valueNode[0].dtype().is_string()) + { + type = ValueType::StringArray; } - else if(valueNode.dtype().is_list()){ - //An empty list is assumed to be an empty list of doubles. - //This only works because this field is immutable! - //If this ever changes, or if Datum's type is used directly to make - //decisions (ex: Sina deciding where to store data), this logic - //should be revisited. - if(valueNode.number_of_children() == 0 || valueNode[0].dtype().is_number()){ - type = ValueType::ScalarArray; - } - else if(valueNode[0].dtype().is_string()){ - type = ValueType::StringArray; - } - else { - std::ostringstream message; - message << "The only valid types for an array '" << VALUE_FIELD - << "' are strings and numbers. Got '" << valueNode.to_json() << "'"; - throw std::invalid_argument(message.str()); - } - - auto itr = valueNode.children(); - while(itr.has_next()) - { - conduit::Node const &entry = itr.next(); - if(entry.dtype().is_string() && type == ValueType::StringArray){ - stringArrayValue.emplace_back(entry.as_string()); - } - else if(entry.dtype().is_number() && type == ValueType::ScalarArray){ - scalarArrayValue.emplace_back(entry.to_double()); - } - else { - std::ostringstream message; - message << "If the required field '" << VALUE_FIELD - << "' is an array, it must consist of only strings or only numbers, " - << "but got '" << entry.dtype().name() << "' (" << entry.to_json() << ")"; - throw std::invalid_argument(message.str()); - } - } + else + { + std::ostringstream message; + message << "The only valid types for an array '" << VALUE_FIELD + << "' are strings and numbers. Got '" << valueNode.to_json() + << "'"; + throw std::invalid_argument(message.str()); } - else { + + auto itr = valueNode.children(); + while(itr.has_next()) + { + conduit::Node const &entry = itr.next(); + if(entry.dtype().is_string() && type == ValueType::StringArray) + { + stringArrayValue.emplace_back(entry.as_string()); + } + else if(entry.dtype().is_number() && type == ValueType::ScalarArray) + { + scalarArrayValue.emplace_back(entry.to_double()); + } + else + { std::ostringstream message; - message << "The required field '" << VALUE_FIELD - << "' must be a string, double, list of strings, or list of doubles."; + message + << "If the required field '" << VALUE_FIELD + << "' is an array, it must consist of only strings or only numbers, " + << "but got '" << entry.dtype().name() << "' (" << entry.to_json() + << ")"; throw std::invalid_argument(message.str()); + } } - - //Get the units, if there are any - units = getOptionalString(UNITS_FIELD, asNode, DATA_PARENT_TYPE); - - //Need to grab the tags and add them to a vector of strings - if(asNode.has_child(TAGS_FIELD)){ - auto tagNodeIter = asNode[TAGS_FIELD].children(); - while(tagNodeIter.has_next()){ - auto &tag = tagNodeIter.next(); - if(tag.dtype().is_string()){ - tags.emplace_back(std::string(tag.as_string())); - } else { - std::ostringstream message; - message << "The optional field '" << TAGS_FIELD - << "' must be an array of strings. Found '" - << tag.dtype().name() << "' instead."; - throw std::invalid_argument(message.str()); - } + } + else + { + std::ostringstream message; + message + << "The required field '" << VALUE_FIELD + << "' must be a string, double, list of strings, or list of doubles."; + throw std::invalid_argument(message.str()); + } + + //Get the units, if there are any + units = getOptionalString(UNITS_FIELD, asNode, DATA_PARENT_TYPE); + + //Need to grab the tags and add them to a vector of strings + if(asNode.has_child(TAGS_FIELD)) + { + auto tagNodeIter = asNode[TAGS_FIELD].children(); + while(tagNodeIter.has_next()) + { + auto &tag = tagNodeIter.next(); + if(tag.dtype().is_string()) + { + tags.emplace_back(std::string(tag.as_string())); + } + else + { + std::ostringstream message; + message << "The optional field '" << TAGS_FIELD + << "' must be an array of strings. Found '" + << tag.dtype().name() << "' instead."; + throw std::invalid_argument(message.str()); } - } + } + } } -void Datum::setUnits(const std::string &units_) { - units = units_; -} +void Datum::setUnits(const std::string &units_) { units = units_; } -void Datum::setTags(const std::vector &tags_){ - tags = tags_; -} +void Datum::setTags(const std::vector &tags_) { tags = tags_; } -conduit::Node Datum::toNode() const { - conduit::Node asNode; - switch(type){ - case ValueType::Scalar: - asNode[VALUE_FIELD] = scalarValue; - break; - case ValueType::String: - asNode[VALUE_FIELD] = stringValue; - break; - case ValueType::ScalarArray: - asNode[VALUE_FIELD] = scalarArrayValue; - break; - case ValueType::StringArray: - addStringsToNode(asNode, VALUE_FIELD, stringArrayValue); - break; - } - if(tags.size() > 0) - addStringsToNode(asNode, TAGS_FIELD, tags); - if(!units.empty()) - asNode[UNITS_FIELD] = units; - return asNode; +conduit::Node Datum::toNode() const +{ + conduit::Node asNode; + switch(type) + { + case ValueType::Scalar: + asNode[VALUE_FIELD] = scalarValue; + break; + case ValueType::String: + asNode[VALUE_FIELD] = stringValue; + break; + case ValueType::ScalarArray: + asNode[VALUE_FIELD] = scalarArrayValue; + break; + case ValueType::StringArray: + addStringsToNode(asNode, VALUE_FIELD, stringArrayValue); + break; + } + if(tags.size() > 0) addStringsToNode(asNode, TAGS_FIELD, tags); + if(!units.empty()) asNode[UNITS_FIELD] = units; + return asNode; }; -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/Datum.hpp b/src/axom/sina/core/Datum.hpp index 658894e731..784a996044 100644 --- a/src/axom/sina/core/Datum.hpp +++ b/src/axom/sina/core/Datum.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_DATUM_HPP #define SINA_DATUM_HPP @@ -32,11 +31,12 @@ namespace sina * Represents whether a Datum is a String, Scalar (double), array of Strings, * or array of Scalars. */ -enum class ValueType { - String, - Scalar, - StringArray, - ScalarArray +enum class ValueType +{ + String, + Scalar, + StringArray, + ScalarArray }; /** @@ -76,139 +76,131 @@ enum class ValueType { * myRecord->add(myArrayDatum); * \endcode */ -class Datum { +class Datum +{ public: - /** + /** * \brief Construct a new Datum. * * \param value the string value of the datum */ - Datum(const std::string &value); + Datum(const std::string &value); - /** + /** * \brief Construct a new Datum. * * \param value the double value of the datum */ - Datum(const double &value); + Datum(const double &value); - /** + /** * \brief Construct a new Datum. * * \param value the string array value of the datum */ - Datum(const std::vector &value); + Datum(const std::vector &value); - /** + /** * \brief Construct a new Datum. * * \param value the scalar array value of the datum */ - Datum(const std::vector &value); + Datum(const std::vector &value); - /** + /** * \brief Construct a Datum from its Node representation. * * \param asNode the Datum as conduit Node */ - explicit Datum(conduit::Node const &asNode); + explicit Datum(conduit::Node const &asNode); - /** + /** * \brief Get the string value of the Datum. * * \return the string value */ - std::string const &getValue() const noexcept { - return stringValue; - } + std::string const &getValue() const noexcept { return stringValue; } - /** + /** * \brief Get the scalar value of the Datum. * * \return the scalar value */ - double const &getScalar() const noexcept { - return scalarValue; - } + double const &getScalar() const noexcept { return scalarValue; } - /** + /** * \brief Get the string array value of the Datum. * * \return the string vector value */ - std::vector const &getStringArray() const noexcept { - return stringArrayValue; - } + std::vector const &getStringArray() const noexcept + { + return stringArrayValue; + } - /** + /** * \brief Get the scalar array value of the Datum. * * \return the scalar vector value */ - std::vector const &getScalarArray() const noexcept { - return scalarArrayValue; - } + std::vector const &getScalarArray() const noexcept + { + return scalarArrayValue; + } - /** + /** * \brief Get the tags of the Datum * * \return the tags of the value */ - std::vector const &getTags() const noexcept { - return tags; - } + std::vector const &getTags() const noexcept { return tags; } - /** + /** * \brief Set the tags of the Datum * * \param tags the tags of the value */ - void setTags(const std::vector &tags); + void setTags(const std::vector &tags); - /** + /** * \brief Get the units of the Datum * * \return the units of the value */ - std::string const &getUnits() const noexcept { - return units; - } + std::string const &getUnits() const noexcept { return units; } - /** + /** * \brief Set the units of the Datum * * \param units the units of the value */ - void setUnits(const std::string &units); - + void setUnits(const std::string &units); - /** + /** * \brief Get the type of the Datum * * \return the type of the value */ - ValueType getType() const noexcept { - return type; - } + ValueType getType() const noexcept { return type; } - /** + /** * \brief Convert this Datum to its conduit Node representation. * * \return the Node representation of this Datum. */ - conduit::Node toNode() const; + conduit::Node toNode() const; private: - std::string stringValue; - double scalarValue; - std::vector stringArrayValue; - std::vector scalarArrayValue; - std::string units; - std::vector tags; - ValueType type; + std::string stringValue; + double scalarValue; + std::vector stringArrayValue; + std::vector scalarArrayValue; + std::string units; + std::vector tags; + ValueType type; }; -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom -#endif //SINA_DATUM_HPP +#endif //SINA_DATUM_HPP diff --git a/src/axom/sina/core/Document.cpp b/src/axom/sina/core/Document.cpp index 30afa4b015..d53892e477 100644 --- a/src/axom/sina/core/Document.cpp +++ b/src/axom/sina/core/Document.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - /*! ****************************************************************************** * @@ -29,125 +28,147 @@ namespace axom namespace sina { -namespace { +namespace +{ char const RECORDS_KEY[] = "records"; char const RELATIONSHIPS_KEY[] = "relationships"; char const SAVE_TMP_FILE_EXTENSION[] = ".sina.tmp"; +} // namespace + +void Document::add(std::unique_ptr record) +{ + records.emplace_back(std::move(record)); } -void Document::add(std::unique_ptr record) { - records.emplace_back(std::move(record)); +void Document::add(Relationship relationship) +{ + relationships.emplace_back(std::move(relationship)); } -void Document::add(Relationship relationship) { - relationships.emplace_back(std::move(relationship)); +conduit::Node Document::toNode() const +{ + conduit::Node document(conduit::DataType::object()); + document[RECORDS_KEY] = conduit::Node(conduit::DataType::list()); + document[RELATIONSHIPS_KEY] = conduit::Node(conduit::DataType::list()); + for(auto &record : records) + { + auto &list_entry = document[RECORDS_KEY].append(); + list_entry.set_node(record->toNode()); + } + for(auto &relationship : relationships) + { + auto &list_entry = document[RELATIONSHIPS_KEY].append(); + list_entry = relationship.toNode(); + } + return document; } -conduit::Node Document::toNode() const { - conduit::Node document(conduit::DataType::object()); - document[RECORDS_KEY] = conduit::Node(conduit::DataType::list()); - document[RELATIONSHIPS_KEY] = conduit::Node(conduit::DataType::list()); - for(auto &record : records) +void Document::createFromNode(conduit::Node const &asNode, + RecordLoader const &recordLoader) +{ + if(asNode.has_child(RECORDS_KEY)) + { + conduit::Node record_nodes = asNode[RECORDS_KEY]; + if(record_nodes.dtype().is_list()) { - auto &list_entry = document[RECORDS_KEY].append(); - list_entry.set_node(record->toNode()); + auto recordIter = record_nodes.children(); + while(recordIter.has_next()) + { + auto record = recordIter.next(); + add(recordLoader.load(record)); + } } - for(auto &relationship : relationships) + else { - auto &list_entry = document[RELATIONSHIPS_KEY].append(); - list_entry = relationship.toNode(); + std::ostringstream message; + message << "The '" << RECORDS_KEY + << "' element of a document must be an array"; + throw std::invalid_argument(message.str()); } - return document; -} + } -void Document::createFromNode(conduit::Node const &asNode, - RecordLoader const &recordLoader) { - if (asNode.has_child(RECORDS_KEY)) { - conduit::Node record_nodes = asNode[RECORDS_KEY]; - if (record_nodes.dtype().is_list()) { - auto recordIter = record_nodes.children(); - while (recordIter.has_next()){ - auto record = recordIter.next(); - add(recordLoader.load(record)); - } - } else { - std::ostringstream message; - message << "The '" << RECORDS_KEY - << "' element of a document must be an array"; - throw std::invalid_argument(message.str()); - } + if(asNode.has_child(RELATIONSHIPS_KEY)) + { + conduit::Node relationship_nodes = asNode[RELATIONSHIPS_KEY]; + if(relationship_nodes.dtype().is_list()) + { + auto relationshipsIter = relationship_nodes.children(); + while(relationshipsIter.has_next()) + { + auto &relationship = relationshipsIter.next(); + add(Relationship {relationship}); + } } - - if (asNode.has_child(RELATIONSHIPS_KEY)){ - conduit::Node relationship_nodes = asNode[RELATIONSHIPS_KEY]; - if (relationship_nodes.dtype().is_list()) { - auto relationshipsIter = relationship_nodes.children(); - while (relationshipsIter.has_next()){ - auto &relationship = relationshipsIter.next(); - add(Relationship{relationship}); - } - } else { - std::ostringstream message; - message << "The '" << RELATIONSHIPS_KEY - << "' element of a document must be an array"; - throw std::invalid_argument(message.str()); - } + else + { + std::ostringstream message; + message << "The '" << RELATIONSHIPS_KEY + << "' element of a document must be an array"; + throw std::invalid_argument(message.str()); } + } } -Document::Document(conduit::Node const &asNode, - RecordLoader const &recordLoader) { - this->createFromNode(asNode, recordLoader); +Document::Document(conduit::Node const &asNode, RecordLoader const &recordLoader) +{ + this->createFromNode(asNode, recordLoader); } -Document::Document(std::string const &asJson, RecordLoader const &recordLoader) { +Document::Document(std::string const &asJson, RecordLoader const &recordLoader) +{ conduit::Node asNode; asNode.parse(asJson, "json"); this->createFromNode(asNode, recordLoader); } -std::string Document::toJson(conduit::index_t indent, conduit::index_t depth, - const std::string &pad, const std::string &eoe) const{ +std::string Document::toJson(conduit::index_t indent, + conduit::index_t depth, + const std::string &pad, + const std::string &eoe) const +{ return this->toNode().to_json("json", indent, depth, pad, eoe); } -void saveDocument(Document const &document, std::string const &fileName) { - // It is a common use case for users to want to overwrite their files as - // the simulation progresses. However, this operation should be atomic so - // that if a write fails, the old file is left intact. For this reason, - // we write to a temporary file first and then move the file. The temporary - // file is in the same directory to ensure that it is part of the same - // file system as the destination file so that the move operation is - // atomic. - std::string tmpFileName = fileName + SAVE_TMP_FILE_EXTENSION; - auto asJson = document.toJson(); - std::ofstream fout{tmpFileName}; - fout.exceptions(std::ostream::failbit | std::ostream::badbit); - fout << asJson; - fout.close(); - - if (rename(tmpFileName.c_str(), fileName.c_str()) != 0) { - std::string message{"Could not save to '"}; - message += fileName; - message += "'"; - throw std::ios::failure{message}; - } +void saveDocument(Document const &document, std::string const &fileName) +{ + // It is a common use case for users to want to overwrite their files as + // the simulation progresses. However, this operation should be atomic so + // that if a write fails, the old file is left intact. For this reason, + // we write to a temporary file first and then move the file. The temporary + // file is in the same directory to ensure that it is part of the same + // file system as the destination file so that the move operation is + // atomic. + std::string tmpFileName = fileName + SAVE_TMP_FILE_EXTENSION; + auto asJson = document.toJson(); + std::ofstream fout {tmpFileName}; + fout.exceptions(std::ostream::failbit | std::ostream::badbit); + fout << asJson; + fout.close(); + + if(rename(tmpFileName.c_str(), fileName.c_str()) != 0) + { + std::string message {"Could not save to '"}; + message += fileName; + message += "'"; + throw std::ios::failure {message}; + } } -Document loadDocument(std::string const &path) { - return loadDocument(path, createRecordLoaderWithAllKnownTypes()); +Document loadDocument(std::string const &path) +{ + return loadDocument(path, createRecordLoaderWithAllKnownTypes()); } -Document loadDocument(std::string const &path, - RecordLoader const &recordLoader) { - conduit::Node nodeFromJson; - std::ifstream file_in{path}; - std::ostringstream file_contents; - file_contents << file_in.rdbuf(); - file_in.close(); - nodeFromJson.parse(file_contents.str(), "json"); - return Document{nodeFromJson, recordLoader}; +Document loadDocument(std::string const &path, RecordLoader const &recordLoader) +{ + conduit::Node nodeFromJson; + std::ifstream file_in {path}; + std::ostringstream file_contents; + file_contents << file_in.rdbuf(); + file_in.close(); + nodeFromJson.parse(file_contents.str(), "json"); + return Document {nodeFromJson, recordLoader}; } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/Document.hpp b/src/axom/sina/core/Document.hpp index 89c19aaf22..48a8f846b0 100644 --- a/src/axom/sina/core/Document.hpp +++ b/src/axom/sina/core/Document.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_DOCUMENT_HPP #define SINA_DOCUMENT_HPP @@ -75,118 +74,119 @@ namespace sina * \endcode * */ -class Document { +class Document +{ public: - /** + /** * A vector of pointers to Record objects. */ - using RecordList = std::vector>; + using RecordList = std::vector>; - /** + /** * A vector of Relationship objects. */ - using RelationshipList = std::vector; + using RelationshipList = std::vector; - /** + /** * Construct an empty Document. */ - Document() = default; + Document() = default; - /** + /** * Disable copying Document objects. We must do this since we hold * pointers to polymorphic objects. */ - Document(Document const &) = delete; + Document(Document const &) = delete; - /** + /** * Disabling copy assignment. */ - Document &operator=(Document const &) = delete; + Document &operator=(Document const &) = delete; - /** + /** * Move constructor which should be handled by the compiler. */ - Document(Document &&) = default; + Document(Document &&) = default; - /** + /** * Move assignment which should be handled by the compiler. */ - Document &operator=(Document &&) = default; + Document &operator=(Document &&) = default; - /** + /** * \brief Create a Document from its Conduit Node representation * * \param asNode the Document as a Node * \param recordLoader an RecordLoader to use to load the different * types of records which may be in the document */ - Document(conduit::Node const &asNode, RecordLoader const &recordLoader); + Document(conduit::Node const &asNode, RecordLoader const &recordLoader); - /** + /** * \brief Create a Document from a JSON string representation * * \param asJson the Document as a JSON string * \param recordLoader an RecordLoader to use to load the different * types of records which may be in the document */ - Document(std::string const &asJson, RecordLoader const &recordLoader); + Document(std::string const &asJson, RecordLoader const &recordLoader); - /** + /** * \brief Add the given record to this document. * * \param record the record to add */ - void add(std::unique_ptr record); + void add(std::unique_ptr record); - /** + /** * \brief Get the list of records currently in this document. * * \return the list of records */ - RecordList const &getRecords() const noexcept { - return records; - } + RecordList const &getRecords() const noexcept { return records; } - /** + /** * \brief Add a relationship to this document * * \param relationship the relationship to add */ - void add(Relationship relationship); + void add(Relationship relationship); - /** + /** * \brief Get the list of relationships in this document. * * \return the list of relationships */ - RelationshipList const &getRelationships() const noexcept { - return relationships; - } + RelationshipList const &getRelationships() const noexcept + { + return relationships; + } - - /** + /** * \brief Convert this document to a conduit Node. * * \return the contents of the document as a Node */ - conduit::Node toNode() const; + conduit::Node toNode() const; - /** + /** * \brief Convert this document to a JSON string. * * \return the contents of the document as a JSON string */ - std::string toJson(conduit::index_t indent=0, conduit::index_t depth=0, - const std::string &pad="", const std::string &eoe="") const; + std::string toJson(conduit::index_t indent = 0, + conduit::index_t depth = 0, + const std::string &pad = "", + const std::string &eoe = "") const; private: - /** + /** * Constructor helper method, extracts info from a conduit Node. */ - void createFromNode(conduit::Node const &asNode, - RecordLoader const &recordLoader); - RecordList records; - RelationshipList relationships; + void createFromNode(conduit::Node const &asNode, + RecordLoader const &recordLoader); + RecordList records; + RelationshipList relationships; }; /** @@ -216,11 +216,9 @@ Document loadDocument(std::string const &path); * of records * \return the loaded Document */ -Document loadDocument(std::string const &path, - RecordLoader const &recordLoader); - -} // end sina namespace -} // end axom namespace +Document loadDocument(std::string const &path, RecordLoader const &recordLoader); +} // namespace sina +} // namespace axom -#endif //SINA_DOCUMENT_HPP +#endif //SINA_DOCUMENT_HPP diff --git a/src/axom/sina/core/File.cpp b/src/axom/sina/core/File.cpp index 10d52f0d62..d624ac4175 100644 --- a/src/axom/sina/core/File.cpp +++ b/src/axom/sina/core/File.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - /*! ****************************************************************************** * @@ -28,54 +27,63 @@ namespace axom namespace sina { -namespace { +namespace +{ char const MIMETYPE_KEY[] = "mimetype"; char const FILE_TYPE_NAME[] = "File"; char const TAGS_KEY[] = "tags"; -} +} // namespace -File::File(std::string uri_) : uri{std::move(uri_)} {} - -File::File(std::string uri_, conduit::Node const &asNode) : - uri{std::move(uri_)}, - mimeType{getOptionalString(MIMETYPE_KEY, asNode, FILE_TYPE_NAME)} { - if (asNode.has_child(TAGS_KEY)){ - auto tagsIter = asNode[TAGS_KEY].children(); - while(tagsIter.has_next()){ - auto &tag = tagsIter.next(); - if(tag.dtype().is_string()) - tags.emplace_back(tag.as_string()); - else { - std::ostringstream message; - message << "The optional field '" << TAGS_KEY - << "' must be an array of strings. Found '" - << tag.dtype().name() << "' instead."; - throw std::invalid_argument(message.str()); - } - } - } +File::File(std::string uri_) : uri {std::move(uri_)} { } +File::File(std::string uri_, conduit::Node const &asNode) + : uri {std::move(uri_)} + , mimeType {getOptionalString(MIMETYPE_KEY, asNode, FILE_TYPE_NAME)} +{ + if(asNode.has_child(TAGS_KEY)) + { + auto tagsIter = asNode[TAGS_KEY].children(); + while(tagsIter.has_next()) + { + auto &tag = tagsIter.next(); + if(tag.dtype().is_string()) + tags.emplace_back(tag.as_string()); + else + { + std::ostringstream message; + message << "The optional field '" << TAGS_KEY + << "' must be an array of strings. Found '" + << tag.dtype().name() << "' instead."; + throw std::invalid_argument(message.str()); + } } + } +} -void File::setMimeType(std::string mimeType_) { - File::mimeType = std::move(mimeType_); +void File::setMimeType(std::string mimeType_) +{ + File::mimeType = std::move(mimeType_); } -void File::setTags(std::vector tags_) { - File::tags = std::move(tags_); +void File::setTags(std::vector tags_) +{ + File::tags = std::move(tags_); } -conduit::Node File::toNode() const { - conduit::Node asNode; - if (!mimeType.empty()) { - asNode[MIMETYPE_KEY] = mimeType; - } - if(tags.size() > 0) { - std::vector tags_copy(tags); - addStringsToNode(asNode, TAGS_KEY, tags_copy); - } - return asNode; +conduit::Node File::toNode() const +{ + conduit::Node asNode; + if(!mimeType.empty()) + { + asNode[MIMETYPE_KEY] = mimeType; + } + if(tags.size() > 0) + { + std::vector tags_copy(tags); + addStringsToNode(asNode, TAGS_KEY, tags_copy); + } + return asNode; } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/File.hpp b/src/axom/sina/core/File.hpp index c16d266457..dd32b75eb2 100644 --- a/src/axom/sina/core/File.hpp +++ b/src/axom/sina/core/File.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_FILE_HPP #define SINA_FILE_HPP @@ -43,78 +42,73 @@ namespace sina * myRecord->add(myOtherFile); * \endcode */ -class File { +class File +{ public: - /** + /** * \brief Construct a new File. * * \param uri the location of the file */ - explicit File(std::string uri); + explicit File(std::string uri); - /** + /** * \brief Construct a new File. * * \param uri the uri for a file * \param asNode the Node representation of the file's additional info */ - File(std::string uri, conduit::Node const &asNode); + File(std::string uri, conduit::Node const &asNode); - /** + /** * \brief Get the File's URI. * * \return the URI */ - std::string const &getUri() const noexcept { - return uri; - } + std::string const &getUri() const noexcept { return uri; } - /** + /** * \brief Get the File's MIME type. * * \return the MIME type */ - std::string const &getMimeType() const noexcept { - return mimeType; - } + std::string const &getMimeType() const noexcept { return mimeType; } - /** + /** * \brief Get the File's tags. * * \return the tags */ - std::vector const &getTags() const noexcept { - return tags; - } + std::vector const &getTags() const noexcept { return tags; } - /** + /** * \brief Set the File's MIME type. * * \param mimeType the MIME type */ - void setMimeType(std::string mimeType); + void setMimeType(std::string mimeType); - /** + /** * \brief Set the File's tags. * * \param tags the File's tags */ - void setTags(std::vector tags); + void setTags(std::vector tags); - /** + /** * \brief Convert this File to its conduit Node representation. * * \return the File in its Node representation */ - conduit::Node toNode() const; + conduit::Node toNode() const; private: - std::string uri; - std::string mimeType; - std::vector tags; + std::string uri; + std::string mimeType; + std::vector tags; }; -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom -#endif //SINA_FILE_HPP +#endif //SINA_FILE_HPP diff --git a/src/axom/sina/core/ID.cpp b/src/axom/sina/core/ID.cpp index 9d0bd3205a..8d87e40713 100644 --- a/src/axom/sina/core/ID.cpp +++ b/src/axom/sina/core/ID.cpp @@ -23,12 +23,13 @@ namespace axom namespace sina { -ID::ID(std::string id_, IDType type_) : id{std::move(id_)}, type{type_} {} +ID::ID(std::string id_, IDType type_) : id {std::move(id_)}, type {type_} { } namespace internal { -namespace { +namespace +{ /** * Extract an ID from a given JSON object. * @@ -38,34 +39,43 @@ namespace { * @return the ID from the object */ ID extractIDFromObject(conduit::Node const &parentObject, - std::string const &localName, std::string const &globalName) { - if (parentObject.has_child(globalName)) { - return ID{std::string(parentObject[globalName].as_string()), IDType::Global}; - } - if (parentObject.has_child(localName)) { - return ID{std::string(parentObject[localName].as_string()), IDType::Local}; - } - std::string message{ - "Could not find either of the required ID fields '"}; - message += localName + "' or '" + globalName + "'"; - throw std::invalid_argument(message); -} + std::string const &localName, + std::string const &globalName) +{ + if(parentObject.has_child(globalName)) + { + return ID {std::string(parentObject[globalName].as_string()), IDType::Global}; + } + if(parentObject.has_child(localName)) + { + return ID {std::string(parentObject[localName].as_string()), IDType::Local}; + } + std::string message {"Could not find either of the required ID fields '"}; + message += localName + "' or '" + globalName + "'"; + throw std::invalid_argument(message); } +} // namespace IDField::IDField(ID value_, std::string localName_, std::string globalName_) - : value{std::move(value_)}, localName{std::move(localName_)}, - globalName{std::move(globalName_)} {} + : value {std::move(value_)} + , localName {std::move(localName_)} + , globalName {std::move(globalName_)} +{ } -IDField::IDField(conduit::Node const &parentObject, std::string localName_, - std::string globalName_) : IDField{ - extractIDFromObject(parentObject, localName_, globalName_), - std::move(localName_), std::move(globalName_)} {} +IDField::IDField(conduit::Node const &parentObject, + std::string localName_, + std::string globalName_) + : IDField {extractIDFromObject(parentObject, localName_, globalName_), + std::move(localName_), + std::move(globalName_)} +{ } -void IDField::addTo(conduit::Node &object) const { - auto &key = value.getType() == IDType::Global ? globalName : localName; - object[key] = value.getId(); +void IDField::addTo(conduit::Node &object) const +{ + auto &key = value.getType() == IDType::Global ? globalName : localName; + object[key] = value.getId(); } -} // end internal namespace -} // end sina namespace -} // end axom namespace +} // namespace internal +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/ID.hpp b/src/axom/sina/core/ID.hpp index a675f8a2f9..a1ba7ba015 100644 --- a/src/axom/sina/core/ID.hpp +++ b/src/axom/sina/core/ID.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_ID_HPP #define SINA_ID_HPP @@ -34,9 +33,10 @@ namespace sina /** * Represents whether an ID is local or global. */ -enum class IDType { - Local, - Global +enum class IDType +{ + Local, + Global }; /** @@ -45,37 +45,34 @@ enum class IDType { * An ID is used to represent the ID of a Record. This class holds both the * actual ID and whether it is a local or global ID. */ -class ID { +class ID +{ public: - /** + /** * \brief Create a new ID. * * \param id the actual value of the ID * \param type whether the ID is local or global */ - ID(std::string id, IDType type); + ID(std::string id, IDType type); - /** + /** * \brief Get the value of the ID. * * \return the actual ID */ - std::string const &getId() const noexcept { - return id; - } + std::string const &getId() const noexcept { return id; } - /** + /** * \brief Get the type of the ID. * * \return whether the ID is local or global */ - IDType getType() const noexcept { - return type; - } + IDType getType() const noexcept { return type; } private: - std::string id; - IDType type; + std::string id; + IDType type; }; namespace internal @@ -90,69 +87,65 @@ namespace internal * "subject"/"local_subject" and "object"/"local_object" pairs in * relationships. */ -class IDField { +class IDField +{ public: - /** + /** * \brief Construct a new IDField. * * \param value the value of the ID * \param localName the name of the local variant of the field * \param globalName the name of the global variant of the field */ - IDField(ID value, std::string localName, std::string globalName); + IDField(ID value, std::string localName, std::string globalName); - /** + /** * \brief Construct an IDField by looking for its values in a conduit Node. * * \param parentObject the conduit Node containing the ID field * \param localName the local name of the field * \param globalName the global name of the field */ - IDField(conduit::Node const &parentObject, std::string localName, - std::string globalName); + IDField(conduit::Node const &parentObject, + std::string localName, + std::string globalName); - /** + /** * \brief Get the value of this field. * * \return the ID describing the field's value */ - ID const &getID() const noexcept { - return value; - } + ID const &getID() const noexcept { return value; } - /** + /** * \brief Get the name to use for this field when the ID is local. * * \return the name of the local ID field */ - std::string const &getLocalName() const noexcept { - return localName; - } + std::string const &getLocalName() const noexcept { return localName; } - /** + /** * \brief Get the name to use for this field when the ID is global. * * \return the name of the global ID field */ - std::string const &getGlobalName() const noexcept { - return globalName; - } + std::string const &getGlobalName() const noexcept { return globalName; } - /** + /** * \brief Add this field to the given Node. * * \param object the Node to which to add the field */ - void addTo(conduit::Node &object) const; + void addTo(conduit::Node &object) const; private: - ID value; - std::string localName; - std::string globalName; + ID value; + std::string localName; + std::string globalName; }; -} // end internal namespace -} // end sina namespace -} // end axom namespace +} // namespace internal +} // namespace sina +} // namespace axom -#endif //SINA_ID_HPP +#endif //SINA_ID_HPP diff --git a/src/axom/sina/core/Record.cpp b/src/axom/sina/core/Record.cpp index dff0395cff..58fd35c917 100644 --- a/src/axom/sina/core/Record.cpp +++ b/src/axom/sina/core/Record.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - /*! ****************************************************************************** * @@ -25,7 +24,8 @@ #include "axom/sina/core/DataHolder.hpp" #include "axom/sina/core/Run.hpp" -namespace { +namespace +{ char const LOCAL_ID_FIELD[] = "local_id"; char const GLOBAL_ID_FIELD[] = "id"; @@ -36,90 +36,103 @@ char const FILES_FIELD[] = "files"; char const LIBRARY_DATA_ID_DATUM[] = "SINA_librarydata_id"; char const LIBRARY_DATA_TYPE_DATUM[] = "SINA_librarydata_type"; -} +} // namespace namespace axom { namespace sina { -Record::Record(ID id_, std::string type_) : - DataHolder{}, - id{std::move(id_), LOCAL_ID_FIELD, GLOBAL_ID_FIELD}, - type{std::move(type_)} {} - -conduit::Node Record::toNode() const { - conduit::Node asNode = DataHolder::toNode(); - asNode[TYPE_FIELD] = type; - id.addTo(asNode); - // Optional fields - if(!files.empty()){ - conduit::Node fileRef; - for (auto &file : files) { - auto &n = fileRef.add_child(file.getUri()); - n.set(file.toNode()); +Record::Record(ID id_, std::string type_) + : DataHolder {} + , id {std::move(id_), LOCAL_ID_FIELD, GLOBAL_ID_FIELD} + , type {std::move(type_)} +{ } + +conduit::Node Record::toNode() const +{ + conduit::Node asNode = DataHolder::toNode(); + asNode[TYPE_FIELD] = type; + id.addTo(asNode); + // Optional fields + if(!files.empty()) + { + conduit::Node fileRef; + for(auto &file : files) + { + auto &n = fileRef.add_child(file.getUri()); + n.set(file.toNode()); asNode[FILES_FIELD] = fileRef; - } } - return asNode; + } + return asNode; } -Record::Record(conduit::Node const &asNode) : - DataHolder{asNode}, - id{asNode, LOCAL_ID_FIELD, GLOBAL_ID_FIELD}, - type{getRequiredString(TYPE_FIELD, asNode, "record")} { - - if(asNode.has_child(FILES_FIELD)){ - auto filesIter = asNode[FILES_FIELD].children(); - while(filesIter.has_next()){ - auto &namedFile = filesIter.next(); - files.insert(File(filesIter.name(), namedFile)); - } +Record::Record(conduit::Node const &asNode) + : DataHolder {asNode} + , id {asNode, LOCAL_ID_FIELD, GLOBAL_ID_FIELD} + , type {getRequiredString(TYPE_FIELD, asNode, "record")} +{ + if(asNode.has_child(FILES_FIELD)) + { + auto filesIter = asNode[FILES_FIELD].children(); + while(filesIter.has_next()) + { + auto &namedFile = filesIter.next(); + files.insert(File(filesIter.name(), namedFile)); } + } } -void Record::remove(File const &file) { - files.erase(file); -} +void Record::remove(File const &file) { files.erase(file); } -void Record::add(File file) { - files.erase(file); - files.insert(std::move(file)); +void Record::add(File file) +{ + files.erase(file); + files.insert(std::move(file)); } -void Record::addRecordAsLibraryData(Record const &childRecord, std::string const &name) { - if(!childRecord.files.empty()){ - for (auto &file : childRecord.files) { - add(file); - } +void Record::addRecordAsLibraryData(Record const &childRecord, + std::string const &name) +{ + if(!childRecord.files.empty()) + { + for(auto &file : childRecord.files) + { + add(file); } - auto newLibData = addLibraryData(name, childRecord.toNode()); - newLibData->add(LIBRARY_DATA_ID_DATUM, Datum{childRecord.getId().getId()}); - newLibData->add(LIBRARY_DATA_TYPE_DATUM, Datum{childRecord.type}); + } + auto newLibData = addLibraryData(name, childRecord.toNode()); + newLibData->add(LIBRARY_DATA_ID_DATUM, Datum {childRecord.getId().getId()}); + newLibData->add(LIBRARY_DATA_TYPE_DATUM, Datum {childRecord.type}); } -void RecordLoader::addTypeLoader(std::string const &type, TypeLoader loader) { - typeLoaders[type] = std::move(loader); +void RecordLoader::addTypeLoader(std::string const &type, TypeLoader loader) +{ + typeLoaders[type] = std::move(loader); } -std::unique_ptr -RecordLoader::load(conduit::Node const &recordAsNode) const { - auto loaderIter = typeLoaders.find(recordAsNode[TYPE_FIELD].as_string()); - if (loaderIter != typeLoaders.end()) { - return loaderIter->second(recordAsNode); - } - return std::make_unique(recordAsNode); +std::unique_ptr RecordLoader::load(conduit::Node const &recordAsNode) const +{ + auto loaderIter = typeLoaders.find(recordAsNode[TYPE_FIELD].as_string()); + if(loaderIter != typeLoaders.end()) + { + return loaderIter->second(recordAsNode); + } + return std::make_unique(recordAsNode); } -bool RecordLoader::canLoad(std::string const &type) const { - return typeLoaders.count(type) > 0; +bool RecordLoader::canLoad(std::string const &type) const +{ + return typeLoaders.count(type) > 0; } -RecordLoader createRecordLoaderWithAllKnownTypes() { - RecordLoader loader; - addRunLoader(loader); - return loader; +RecordLoader createRecordLoaderWithAllKnownTypes() +{ + RecordLoader loader; + addRunLoader(loader); + return loader; } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/Record.hpp b/src/axom/sina/core/Record.hpp index 40de3fb267..14b13aa8d5 100644 --- a/src/axom/sina/core/Record.hpp +++ b/src/axom/sina/core/Record.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_RECORD_HPP #define SINA_RECORD_HPP @@ -42,20 +41,24 @@ namespace sina /** * FileEqualByURI is used to store files in a Record. */ -struct FileEqualByURI { - bool operator()(const File &file1, const File &file2) const { - return file1.getUri() == file2.getUri(); - } +struct FileEqualByURI +{ + bool operator()(const File &file1, const File &file2) const + { + return file1.getUri() == file2.getUri(); + } }; /** * FileHashByURI is used to store files in a Record. Files are stored according * to the hash of their URI. */ -struct FileHashByURI { - size_t operator()(const File &file) const { - return std::hash()(file.getUri()); - } +struct FileHashByURI +{ + size_t operator()(const File &file) const + { + return std::hash()(file.getUri()); + } }; /** @@ -83,106 +86,97 @@ struct FileHashByURI { * {"local_id":"my_record","type":"my_type","data":{"my_scalar":{"tags":["input"],"value":12.0}}} * \endcode */ -class Record : public DataHolder { +class Record : public DataHolder +{ public: - /** + /** * An unordered set of File objects. */ - using FileSet = std::unordered_set; + using FileSet = std::unordered_set; - /** + /** * \brief Construct a new Record. * * \param id the ID of the record * \param type the type of the record */ - Record(ID id, std::string type); + Record(ID id, std::string type); - /** + /** * \brief Construct a Record from its conduit Node representation. * * \param asNode the Record as a Node */ - explicit Record(conduit::Node const &asNode); + explicit Record(conduit::Node const &asNode); - /** + /** * Disable the copy constructor. */ - Record(Record const &) = delete; + Record(Record const &) = delete; - /** + /** * Disable copy assignment. */ - Record &operator=(Record const &) = delete; + Record &operator=(Record const &) = delete; - /** + /** * \brief Get the Record's ID. * * \return the ID */ - ID const &getId() const noexcept { - return id.getID(); - } + ID const &getId() const noexcept { return id.getID(); } - /** + /** * \brief Get the Record's type. * * \return the Record's type */ - std::string const &getType() const noexcept { - return type; - } + std::string const &getType() const noexcept { return type; } - /** + /** * \brief Remove a File from this record. * * \param file the File to remove */ - void remove(File const& file); - + void remove(File const &file); - - using DataHolder::add; - /** + using DataHolder::add; + /** * \brief Add a File to this record. * * \param file the File to add */ - void add(File file); - + void add(File file); - /** + /** * \brief Get the files associated with this record. * * \return the record's files */ - FileSet const &getFiles() const noexcept { - return files; - } + FileSet const &getFiles() const noexcept { return files; } - /** + /** * \brief Convert this record to its conduit Node representation. * * \return the Node representation of this record. */ - conduit::Node toNode() const override; + conduit::Node toNode() const override; - /** + /** * \brief Add another record to this one as library data. * * Useful for libraries that can run in standalone mode; the host * simply calls this method on the record the library produces. * Merges file lists. */ - void addRecordAsLibraryData(Record const &childRecord, std::string const &name); + void addRecordAsLibraryData(Record const &childRecord, std::string const &name); private: - internal::IDField id; - std::string type; - FileSet files; + internal::IDField id; + std::string type; + FileSet files; }; - /** * \brief An object to convert Conduit Nodes into Records * @@ -195,41 +189,42 @@ class Record : public DataHolder { * axom::sina::Document myDocument = axom::sina::Document(jObj, axom::sina::createRecordLoaderWithAllKnownTypes()); * \endcode */ -class RecordLoader { +class RecordLoader +{ public: - /** + /** * A TypeLoader is a function which converts records of a specific type * to their corresponding sub classes. */ - using TypeLoader = std::function( - conduit::Node const &)>; + using TypeLoader = + std::function(conduit::Node const &)>; - /** + /** * \brief Add a function for loading records of the specified type. * * \param type the type of records this function can load * \param loader the function which can load the records */ - void addTypeLoader(std::string const &type, TypeLoader loader); + void addTypeLoader(std::string const &type, TypeLoader loader); - /** + /** * \brief Load a Record from its conduit Node representation. * * \param recordAsNode the Record as a Node * \return the Record */ - std::unique_ptr load(conduit::Node const &recordAsNode) const; + std::unique_ptr load(conduit::Node const &recordAsNode) const; - /** + /** * \brief Check whether this loader can load records of the given type. * * \param type the type of the records to check * \return whether records of the given type can be loaded */ - bool canLoad(std::string const &type) const; + bool canLoad(std::string const &type) const; private: - std::unordered_map typeLoaders; + std::unordered_map typeLoaders; }; /** @@ -239,7 +234,7 @@ class RecordLoader { */ RecordLoader createRecordLoaderWithAllKnownTypes(); -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom -#endif //SINA_RECORD_HPP +#endif //SINA_RECORD_HPP diff --git a/src/axom/sina/core/Relationship.cpp b/src/axom/sina/core/Relationship.cpp index 905a8bd3e2..0a49a6de94 100644 --- a/src/axom/sina/core/Relationship.cpp +++ b/src/axom/sina/core/Relationship.cpp @@ -24,31 +24,35 @@ namespace axom namespace sina { -namespace { +namespace +{ char const GLOBAL_SUBJECT_KEY[] = "subject"; char const LOCAL_SUBJECT_KEY[] = "local_subject"; char const GLOBAL_OBJECT_KEY[] = "object"; char const LOCAL_OBJECT_KEY[] = "local_object"; char const PREDICATE_KEY[] = "predicate"; -} +} // namespace + +Relationship::Relationship(ID subject_, std::string predicate_, ID object_) + : subject {std::move(subject_), LOCAL_SUBJECT_KEY, GLOBAL_SUBJECT_KEY} + , object {std::move(object_), LOCAL_OBJECT_KEY, GLOBAL_OBJECT_KEY} + , predicate {std::move(predicate_)} +{ } -Relationship::Relationship(ID subject_, std::string predicate_, ID object_) : - subject{std::move(subject_), LOCAL_SUBJECT_KEY, GLOBAL_SUBJECT_KEY}, - object{std::move(object_), LOCAL_OBJECT_KEY, GLOBAL_OBJECT_KEY}, - predicate{std::move(predicate_)} {} - -Relationship::Relationship(conduit::Node const &asNode) : - subject{asNode, LOCAL_SUBJECT_KEY, GLOBAL_SUBJECT_KEY}, - object{asNode, LOCAL_OBJECT_KEY, GLOBAL_OBJECT_KEY}, - predicate{getRequiredString(PREDICATE_KEY, asNode, "Relationship")} {} - -conduit::Node Relationship::toNode() const { - conduit::Node relationshipNode; - relationshipNode[PREDICATE_KEY] = predicate; - subject.addTo(relationshipNode); - object.addTo(relationshipNode); - return relationshipNode; +Relationship::Relationship(conduit::Node const &asNode) + : subject {asNode, LOCAL_SUBJECT_KEY, GLOBAL_SUBJECT_KEY} + , object {asNode, LOCAL_OBJECT_KEY, GLOBAL_OBJECT_KEY} + , predicate {getRequiredString(PREDICATE_KEY, asNode, "Relationship")} +{ } + +conduit::Node Relationship::toNode() const +{ + conduit::Node relationshipNode; + relationshipNode[PREDICATE_KEY] = predicate; + subject.addTo(relationshipNode); + object.addTo(relationshipNode); + return relationshipNode; } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/Relationship.hpp b/src/axom/sina/core/Relationship.hpp index 7128f83aa3..7910cc0956 100644 --- a/src/axom/sina/core/Relationship.hpp +++ b/src/axom/sina/core/Relationship.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_RELATIONSHIP_HPP #define SINA_RELATIONSHIP_HPP @@ -81,9 +80,10 @@ namespace sina * for example, "5Aed-BCds-23G1", then "my_local_run" would automatically be replaced by "5Aed-BCds-23G1" in both * the Record and Relationship entries. */ -class Relationship { +class Relationship +{ public: - /** + /** * \brief Create a new relationship. * * \param subject the subject of the relationship @@ -91,56 +91,50 @@ class Relationship { * subject to the object * \param object the object of the relationship */ - Relationship(ID subject, std::string predicate, ID object); + Relationship(ID subject, std::string predicate, ID object); - /** + /** * \brief Create a Relationship object from its representation as a conduit Node. * * \param asNode the relationship as a Node */ - explicit Relationship(conduit::Node const &asNode); + explicit Relationship(conduit::Node const &asNode); - /** + /** * \brief Get the subject. * * \return the subject */ - ID const &getSubject() const noexcept { - return subject.getID(); - } + ID const &getSubject() const noexcept { return subject.getID(); } - /** + /** * \brief Get the object. * * \return the object */ - ID const &getObject() const noexcept { - return object.getID(); - } + ID const &getObject() const noexcept { return object.getID(); } - /** + /** * \brief Get the predicate. * * \return the predicate */ - std::string const &getPredicate() const noexcept { - return predicate; - } + std::string const &getPredicate() const noexcept { return predicate; } - /** + /** * \brief Convert this Relationship to its Node representation. * * \return this relationship as a conduit Node */ - conduit::Node toNode() const; + conduit::Node toNode() const; private: - internal::IDField subject; - internal::IDField object; - std::string predicate; + internal::IDField subject; + internal::IDField object; + std::string predicate; }; -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom -#endif //SINA_RELATIONSHIP_HPP +#endif //SINA_RELATIONSHIP_HPP diff --git a/src/axom/sina/core/Run.cpp b/src/axom/sina/core/Run.cpp index 2d6b9515b8..061ab5e5cc 100644 --- a/src/axom/sina/core/Run.cpp +++ b/src/axom/sina/core/Run.cpp @@ -26,38 +26,46 @@ namespace axom namespace sina { -namespace { +namespace +{ char const RUN_TYPE[] = "run"; char const APPLICATION_FIELD[] = "application"; char const VERSION_FIELD[] = "version"; char const USER_FIELD[] = "user"; -} +} // namespace + +Run::Run(sina::ID id, + std::string application_, + std::string version_, + std::string user_) + : Record {std::move(id), RUN_TYPE} + , application {std::move(application_)} + , version {std::move(version_)} + , user {std::move(user_)} +{ } -Run::Run(sina::ID id, std::string application_, std::string version_, - std::string user_) : Record{std::move(id), RUN_TYPE}, - application{std::move(application_)}, - version{std::move(version_)}, - user{std::move(user_)} {} - -Run::Run(conduit::Node const &asNode) : - Record(asNode), - application{getRequiredString(APPLICATION_FIELD, asNode, RUN_TYPE)}, - version{getOptionalString(VERSION_FIELD, asNode, RUN_TYPE)}, - user{getOptionalString(USER_FIELD, asNode, RUN_TYPE)} {} - -conduit::Node Run::toNode() const { - auto asNode = Record::toNode(); - asNode[APPLICATION_FIELD] = application; - asNode[VERSION_FIELD] = version; - asNode[USER_FIELD] = user; - return asNode; +Run::Run(conduit::Node const &asNode) + : Record(asNode) + , application {getRequiredString(APPLICATION_FIELD, asNode, RUN_TYPE)} + , version {getOptionalString(VERSION_FIELD, asNode, RUN_TYPE)} + , user {getOptionalString(USER_FIELD, asNode, RUN_TYPE)} +{ } + +conduit::Node Run::toNode() const +{ + auto asNode = Record::toNode(); + asNode[APPLICATION_FIELD] = application; + asNode[VERSION_FIELD] = version; + asNode[USER_FIELD] = user; + return asNode; } -void addRunLoader(RecordLoader &loader) { - loader.addTypeLoader(RUN_TYPE, [](conduit::Node const &value) { - return std::make_unique(value); - }); +void addRunLoader(RecordLoader &loader) +{ + loader.addTypeLoader(RUN_TYPE, [](conduit::Node const &value) { + return std::make_unique(value); + }); } -} // end sina namespace -} // end axom namespace +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/core/Run.hpp b/src/axom/sina/core/Run.hpp index 7d0b86e5c7..b057a5c30b 100644 --- a/src/axom/sina/core/Run.hpp +++ b/src/axom/sina/core/Run.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_RUN_HPP #define SINA_RUN_HPP @@ -44,9 +43,10 @@ namespace sina * \endcode * */ -class Run : public Record { +class Run : public Record +{ public: - /** + /** * \brief Create a new Run. * * \param id the run's ID @@ -54,48 +54,45 @@ class Run : public Record { * \param version (optional) the version of the application * \param user (optional) the user who executed the run */ - Run(ID id, std::string application, std::string version = "", std::string user = ""); + Run(ID id, + std::string application, + std::string version = "", + std::string user = ""); - /** + /** * \brief Create a Run from its representation as a conduit Node * * \param asNode the run as a Node */ - explicit Run(conduit::Node const &asNode); + explicit Run(conduit::Node const &asNode); - /** + /** * \brief Get the application that was run. * * \return the application's name */ - std::string const &getApplication() const { - return application; - } + std::string const &getApplication() const { return application; } - /** + /** * \brief Get the version of the application that was run. * * \return the application's version */ - std::string const &getVersion() const { - return version; - } + std::string const &getVersion() const { return version; } - /** + /** * \brief Get the name of the user who ran the application. * * \return the user's name */ - std::string const &getUser() const { - return user; - } + std::string const &getUser() const { return user; } - conduit::Node toNode() const override; + conduit::Node toNode() const override; private: - std::string application; - std::string version; - std::string user; + std::string application; + std::string version; + std::string user; }; /** @@ -106,8 +103,7 @@ class Run : public Record { */ void addRunLoader(RecordLoader &loader); -} // end sina namespace -} // end axom namespace - +} // namespace sina +} // namespace axom -#endif //SINA_RUN_HPP +#endif //SINA_RUN_HPP diff --git a/src/axom/sina/examples/sina_basic.cpp b/src/axom/sina/examples/sina_basic.cpp index 61f75d5d1b..2d59f4429d 100644 --- a/src/axom/sina/examples/sina_basic.cpp +++ b/src/axom/sina/examples/sina_basic.cpp @@ -3,19 +3,19 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina.hpp" -int main (void) { - // Create a new document - axom::sina::Document document; - // 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 run{ - new axom::sina::Run{runID, "My Sim Code", "1.2.3", "jdoe"}}; - // Add the run to the document - document.add(std::move(run)); - // Save the document directly to a file. - axom::sina::saveDocument(document, "MySinaData.json"); +int main(void) +{ + // Create a new document + axom::sina::Document document; + // 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 run { + new axom::sina::Run {runID, "My Sim Code", "1.2.3", "jdoe"}}; + // Add the run to the document + document.add(std::move(run)); + // Save the document directly to a file. + axom::sina::saveDocument(document, "MySinaData.json"); } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_check_datum_type.cpp b/src/axom/sina/examples/sina_check_datum_type.cpp index c70947a0cc..4efb97decc 100644 --- a/src/axom/sina/examples/sina_check_datum_type.cpp +++ b/src/axom/sina/examples/sina_check_datum_type.cpp @@ -3,23 +3,29 @@ // // 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 scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum{scalars}; +int main(void) +{ + // Define 3 different datums + axom::sina::Datum myDatum {12.34}; + std::string value = "foobar"; + axom::sina::Datum myOtherDatum {value}; + std::vector scalars = {1, 2, 20.0}; + axom::sina::Datum myArrayDatum {scalars}; - // Prints 0, corresponding to string - std::cout << static_cast::type>(myDatum.getType()) << std::endl; + // Prints 0, corresponding to string + std::cout << static_cast::type>( + myDatum.getType()) + << std::endl; - // Prints 1, corresponding to scalar - std::cout << static_cast::type>(myOtherDatum.getType()) << std::endl; + // Prints 1, corresponding to scalar + std::cout << static_cast::type>( + myOtherDatum.getType()) + << std::endl; - // Prints 3, corresponding to scalar array - std::cout << static_cast::type>(myArrayDatum.getType()) << std::endl; + // Prints 3, corresponding to scalar array + std::cout << static_cast::type>( + myArrayDatum.getType()) + << std::endl; } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_create_datum.cpp b/src/axom/sina/examples/sina_create_datum.cpp index b5ea31ee10..8bd32bb10f 100644 --- a/src/axom/sina/examples/sina_create_datum.cpp +++ b/src/axom/sina/examples/sina_create_datum.cpp @@ -3,19 +3,20 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina.hpp" -int main(void) { - // Create the record - axom::sina::ID myID{"my_record", axom::sina::IDType::Local}; - std::unique_ptr myRecord{new axom::sina::Record{myID, "my_type"}}; +int main(void) +{ + // Create the record + axom::sina::ID myID {"my_record", axom::sina::IDType::Local}; + std::unique_ptr myRecord { + new axom::sina::Record {myID, "my_type"}}; - // Create the datum with an array of strings - std::vector myTags{"input"}; - axom::sina::Datum myDatum{myTags}; + // Create the datum with an array of strings + std::vector myTags {"input"}; + axom::sina::Datum myDatum {myTags}; - // Add the datum to the record - myRecord->add("my_scalar", std::move(myDatum)); - std::cout << myRecord->toNode().to_json() << std::endl; + // Add the datum to the record + myRecord->add("my_scalar", std::move(myDatum)); + std::cout << myRecord->toNode().to_json() << std::endl; } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_curve_set.cpp b/src/axom/sina/examples/sina_curve_set.cpp index 3fd00675cb..430b7551ec 100644 --- a/src/axom/sina/examples/sina_curve_set.cpp +++ b/src/axom/sina/examples/sina_curve_set.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina.hpp" #include @@ -12,103 +11,113 @@ using namespace std; -struct BounceData { - vector time; - vector xPosition; - vector yPosition; - vector xVelocity; - vector yVelocity; +struct BounceData +{ + vector time; + vector xPosition; + vector yPosition; + vector xVelocity; + vector yVelocity; }; BounceData generateBounceData(double initialY, - double initialXVelocity, - double coefficientOfRestitution, - double timeStep, - double simulationTime, - double airResistanceCoefficient) { - BounceData data; - double time = 0.0; - double yPosition = initialY; - double xPosition = 0.0; - double xVelocity = initialXVelocity; - double yVelocity = 0.0; - - while (time < simulationTime) { - // Update position and velocity based on time step - xPosition += xVelocity * timeStep; - yVelocity += -9.81 * timeStep; // Acceleration due to gravity - - // Apply air resistance on x-velocity - xVelocity -= airResistanceCoefficient * xVelocity * timeStep; - - // Update height (y position) - yPosition += yVelocity * timeStep; - - // Check for bounce (when y position becomes negative) - if (yPosition < 0) { - yPosition = 0; // Set y position to 0 at the ground - yVelocity *= -coefficientOfRestitution; // Reverse y velocity with energy loss - } - - // Add data points for this time step - data.time.push_back(time); - data.xPosition.push_back(xPosition); - data.yPosition.push_back(yPosition); - data.xVelocity.push_back(xVelocity); - data.yVelocity.push_back(yVelocity); - - time += timeStep; + double initialXVelocity, + double coefficientOfRestitution, + double timeStep, + double simulationTime, + double airResistanceCoefficient) +{ + BounceData data; + double time = 0.0; + double yPosition = initialY; + double xPosition = 0.0; + double xVelocity = initialXVelocity; + double yVelocity = 0.0; + + while(time < simulationTime) + { + // Update position and velocity based on time step + xPosition += xVelocity * timeStep; + yVelocity += -9.81 * timeStep; // Acceleration due to gravity + + // Apply air resistance on x-velocity + xVelocity -= airResistanceCoefficient * xVelocity * timeStep; + + // Update height (y position) + yPosition += yVelocity * timeStep; + + // Check for bounce (when y position becomes negative) + if(yPosition < 0) + { + yPosition = 0; // Set y position to 0 at the ground + yVelocity *= + -coefficientOfRestitution; // Reverse y velocity with energy loss } + // Add data points for this time step + data.time.push_back(time); + data.xPosition.push_back(xPosition); + data.yPosition.push_back(yPosition); + data.xVelocity.push_back(xVelocity); + data.yVelocity.push_back(yVelocity); + + time += timeStep; + } + return data; } -void addCurveSet(axom::sina::Record &record, BounceData bounceData, string curveName) { - // Create the curve set object - axom::sina::CurveSet bounceCurveSet{curveName}; - - // Add the independent variable - axom::sina::Curve timeCurve{"time", bounceData.time}; - timeCurve.setUnits("seconds"); - bounceCurveSet.addIndependentCurve(timeCurve); - - // Add the dependent variables - bounceCurveSet.addDependentCurve(axom::sina::Curve{"x_position", bounceData.xPosition}); - bounceCurveSet.addDependentCurve(axom::sina::Curve{"y_position", bounceData.yPosition}); - axom::sina::Curve xVelCurve{"x_velocity", bounceData.xVelocity}; - xVelCurve.setUnits("m/s"); - bounceCurveSet.addDependentCurve(xVelCurve); - axom::sina::Curve yVelCurve{"y_velocity", bounceData.yVelocity}; - yVelCurve.setUnits("m/s"); - bounceCurveSet.addDependentCurve(yVelCurve); - - // Add the curve set to the record - record.add(bounceCurveSet); +void addCurveSet(axom::sina::Record &record, BounceData bounceData, string curveName) +{ + // Create the curve set object + axom::sina::CurveSet bounceCurveSet {curveName}; + + // Add the independent variable + axom::sina::Curve timeCurve {"time", bounceData.time}; + timeCurve.setUnits("seconds"); + bounceCurveSet.addIndependentCurve(timeCurve); + + // Add the dependent variables + bounceCurveSet.addDependentCurve( + axom::sina::Curve {"x_position", bounceData.xPosition}); + bounceCurveSet.addDependentCurve( + axom::sina::Curve {"y_position", bounceData.yPosition}); + axom::sina::Curve xVelCurve {"x_velocity", bounceData.xVelocity}; + xVelCurve.setUnits("m/s"); + bounceCurveSet.addDependentCurve(xVelCurve); + axom::sina::Curve yVelCurve {"y_velocity", bounceData.yVelocity}; + yVelCurve.setUnits("m/s"); + bounceCurveSet.addDependentCurve(yVelCurve); + + // Add the curve set to the record + record.add(bounceCurveSet); } -int main() { - double initialY = 10.0; - double initialXVelocity = 2.0; - double coefficientOfRestitution = 0.7; - double timeStep = 0.2; - double simulationTime = 5.0; - double airResistanceCoefficient = 0.01; - - BounceData bounceData = generateBounceData(initialY, - initialXVelocity, - coefficientOfRestitution, - timeStep, - simulationTime, - airResistanceCoefficient); - - axom::sina::Document doc; - - axom::sina::ID id{"ball_bounce_run", axom::sina::IDType::Global}; - unique_ptr study{new axom::sina::Record{id, "ball bounce study"}}; - - addCurveSet(*study, bounceData, "ball_bounce"); - doc.add(move(study)); - axom::sina::saveDocument(doc, "ball_bounce.json"); - - return 0; +int main() +{ + double initialY = 10.0; + double initialXVelocity = 2.0; + double coefficientOfRestitution = 0.7; + double timeStep = 0.2; + double simulationTime = 5.0; + double airResistanceCoefficient = 0.01; + + BounceData bounceData = generateBounceData(initialY, + initialXVelocity, + coefficientOfRestitution, + timeStep, + simulationTime, + airResistanceCoefficient); + + axom::sina::Document doc; + + axom::sina::ID id {"ball_bounce_run", axom::sina::IDType::Global}; + unique_ptr study { + new axom::sina::Record {id, "ball bounce study"}}; + + addCurveSet(*study, bounceData, "ball_bounce"); + doc.add(move(study)); + axom::sina::saveDocument(doc, "ball_bounce.json"); + + return 0; } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_document_assembly.cpp b/src/axom/sina/examples/sina_document_assembly.cpp index e69744b78a..428c076767 100644 --- a/src/axom/sina/examples/sina_document_assembly.cpp +++ b/src/axom/sina/examples/sina_document_assembly.cpp @@ -3,34 +3,34 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #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 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 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. - axom::sina::saveDocument(document, "MySinaData.json"); +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 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 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. + axom::sina::saveDocument(document, "MySinaData.json"); } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_file_object_creation.cpp b/src/axom/sina/examples/sina_file_object_creation.cpp index e9a55d349e..4d9cdaeaad 100644 --- a/src/axom/sina/examples/sina_file_object_creation.cpp +++ b/src/axom/sina/examples/sina_file_object_creation.cpp @@ -3,23 +3,24 @@ // // 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"}); +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 myRecord{new axom::sina::Record{myID, "my_type"}}; + // Create a record to store the files + axom::sina::ID myID {"my_record", axom::sina::IDType::Local}; + std::unique_ptr myRecord { + new axom::sina::Record {myID, "my_type"}}; - // Add the files to the record - myRecord->add(myFile); - myRecord->add(myOtherFile); + // Add the files to the record + myRecord->add(myFile); + myRecord->add(myOtherFile); - std::cout << myRecord->toNode().to_json() << std::endl; + std::cout << myRecord->toNode().to_json() << std::endl; } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_file_object_removal.cpp b/src/axom/sina/examples/sina_file_object_removal.cpp index 0ab2994288..83c7bd3a7e 100644 --- a/src/axom/sina/examples/sina_file_object_removal.cpp +++ b/src/axom/sina/examples/sina_file_object_removal.cpp @@ -3,26 +3,27 @@ // // 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"}); +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 myRecord{new axom::sina::Record{myID, "my_type"}}; + // Create a record to store the files + axom::sina::ID myID {"my_record", axom::sina::IDType::Local}; + std::unique_ptr myRecord { + new axom::sina::Record {myID, "my_type"}}; - // Add the files to the record - myRecord->add(myFile); - myRecord->add(myOtherFile); + // Add the files to the record + myRecord->add(myFile); + myRecord->add(myOtherFile); - // Remove a file from the record - myRecord->remove(myFile); + // Remove a file from the record + myRecord->remove(myFile); - std::cout << myRecord->toNode().to_json() << std::endl; + std::cout << myRecord->toNode().to_json() << std::endl; } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_local_id_relationship.cpp b/src/axom/sina/examples/sina_local_id_relationship.cpp index 0669308986..5395c5bfb9 100644 --- a/src/axom/sina/examples/sina_local_id_relationship.cpp +++ b/src/axom/sina/examples/sina_local_id_relationship.cpp @@ -3,20 +3,21 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina.hpp" -int main(void) { - // Create IDs for Task 22 and Run 1024 - axom::sina::ID task22{"Task_22", axom::sina::IDType::Global}; - axom::sina::ID run1024{"Run_1024", axom::sina::IDType::Global}; +int main(void) +{ + // Create IDs for Task 22 and Run 1024 + axom::sina::ID task22 {"Task_22", axom::sina::IDType::Global}; + axom::sina::ID run1024 {"Run_1024", axom::sina::IDType::Global}; - // Create the relationship and print it out - axom::sina::Relationship myRelationship{task22, "contains", run1024}; - std::cout << myRelationship.toNode().to_json() << std::endl; + // Create the relationship and print it out + axom::sina::Relationship myRelationship {task22, "contains", run1024}; + std::cout << myRelationship.toNode().to_json() << std::endl; - // Create a new ID with local scope and use it to create a Record and Relationship - axom::sina::ID myLocalID{"my_local_run", axom::sina::IDType::Local}; - std::unique_ptr myRun{new axom::sina::Run{myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; - axom::sina::Relationship myLocalRelationship{task22, "containts", myLocalID}; + // Create a new ID with local scope and use it to create a Record and Relationship + axom::sina::ID myLocalID {"my_local_run", axom::sina::IDType::Local}; + std::unique_ptr myRun { + new axom::sina::Run {myLocalID, "My Sim Code", "1.2.3", "jdoe"}}; + axom::sina::Relationship myLocalRelationship {task22, "containts", myLocalID}; } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_query_record_for_files.cpp b/src/axom/sina/examples/sina_query_record_for_files.cpp index 71abe6ae22..2a63b0c551 100644 --- a/src/axom/sina/examples/sina_query_record_for_files.cpp +++ b/src/axom/sina/examples/sina_query_record_for_files.cpp @@ -3,31 +3,35 @@ // // 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"}); +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 myRecord{new axom::sina::Record{myID, "my_type"}}; + // Create a record to store the files + axom::sina::ID myID {"my_record", axom::sina::IDType::Local}; + std::unique_ptr myRecord { + new axom::sina::Record {myID, "my_type"}}; - // Add the files to the record - myRecord->add(myFile); - myRecord->add(myOtherFile); + // 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; + // 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; + } } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_query_records_relationships.cpp b/src/axom/sina/examples/sina_query_records_relationships.cpp index ef9cd52382..f76661437f 100644 --- a/src/axom/sina/examples/sina_query_records_relationships.cpp +++ b/src/axom/sina/examples/sina_query_records_relationships.cpp @@ -3,38 +3,38 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #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 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 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(); - - std::cout << "Number of Records: " << records.size() << std::endl; - std::cout << "Number of Relationships: " << relationships.size() << std::endl; +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 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 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(); + + std::cout << "Number of Records: " << records.size() << std::endl; + std::cout << "Number of Relationships: " << relationships.size() << std::endl; } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_relationship_assembly.cpp b/src/axom/sina/examples/sina_relationship_assembly.cpp index 42bf0f1020..62048881f4 100644 --- a/src/axom/sina/examples/sina_relationship_assembly.cpp +++ b/src/axom/sina/examples/sina_relationship_assembly.cpp @@ -3,15 +3,15 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina.hpp" -int main(void) { - // Create IDs for both Task 22 and Run 1024 - axom::sina::ID task22{"Task_22", axom::sina::IDType::Global}; - axom::sina::ID run1024{"Run_1024", axom::sina::IDType::Global}; +int main(void) +{ + // Create IDs for both Task 22 and Run 1024 + axom::sina::ID task22 {"Task_22", axom::sina::IDType::Global}; + axom::sina::ID run1024 {"Run_1024", axom::sina::IDType::Global}; - // Create the relationship and print it out - axom::sina::Relationship myRelationship{task22, "contains", run1024}; - std::cout << myRelationship.toNode().to_json() << std::endl; + // Create the relationship and print it out + axom::sina::Relationship myRelationship {task22, "contains", run1024}; + std::cout << myRelationship.toNode().to_json() << std::endl; } diff --git a/src/axom/sina/examples/sina_set_datum_units_tags.cpp b/src/axom/sina/examples/sina_set_datum_units_tags.cpp index 7dbd111b69..db8723f985 100644 --- a/src/axom/sina/examples/sina_set_datum_units_tags.cpp +++ b/src/axom/sina/examples/sina_set_datum_units_tags.cpp @@ -3,17 +3,17 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina.hpp" -int main(void) { - // Define 2 different datums - axom::sina::Datum myDatum{12.34}; - std::vector scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum{scalars}; +int main(void) +{ + // Define 2 different datums + axom::sina::Datum myDatum {12.34}; + std::vector scalars = {1, 2, 20.0}; + axom::sina::Datum myArrayDatum {scalars}; - // Set the units for one datum and the tags for the other - myDatum.setUnits("km/s"); - std::vector tags = {"input", "core"}; - myArrayDatum.setTags(tags); + // Set the units for one datum and the tags for the other + myDatum.setUnits("km/s"); + std::vector tags = {"input", "core"}; + myArrayDatum.setTags(tags); } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_tutorial.cpp b/src/axom/sina/examples/sina_tutorial.cpp index 51d560e400..a3de2392db 100644 --- a/src/axom/sina/examples/sina_tutorial.cpp +++ b/src/axom/sina/examples/sina_tutorial.cpp @@ -3,164 +3,181 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina.hpp" #include #include -namespace { +namespace +{ //! [create record] -void createRecord() { - axom::sina::ID id{"some_record_id", axom::sina::IDType::Local}; - std::unique_ptr record{new axom::sina::Record{id, "my_record_type"}}; - - // Add the record to a document - axom::sina::Document doc; - doc.add(std::move(record)); +void createRecord() +{ + axom::sina::ID id {"some_record_id", axom::sina::IDType::Local}; + std::unique_ptr record { + new axom::sina::Record {id, "my_record_type"}}; + + // Add the record to a document + axom::sina::Document doc; + doc.add(std::move(record)); } //! [create record] //! [create run] -void createRun() { - axom::sina::ID id{"some_run_id", axom::sina::IDType::Local}; - std::unique_ptr run{new axom::sina::Run{id, "My Sim Code", "1.2.3", "jdoe"}}; - - // Add the record to a document - axom::sina::Document doc; - doc.add(std::move(run)); +void createRun() +{ + axom::sina::ID id {"some_run_id", axom::sina::IDType::Local}; + std::unique_ptr run { + new axom::sina::Run {id, "My Sim Code", "1.2.3", "jdoe"}}; + + // Add the record to a document + axom::sina::Document doc; + doc.add(std::move(run)); } //! [create run] //! [adding data] -void addData(axom::sina::Record &record) { - // Add a scalar named "my_scalar" with the value 123.456 - record.add("my_scalar", axom::sina::Datum{123.456}); +void addData(axom::sina::Record &record) +{ + // Add a scalar named "my_scalar" with the value 123.456 + record.add("my_scalar", axom::sina::Datum {123.456}); - // Add a string named "my_string" with the value "abc" - record.add("my_string", axom::sina::Datum{"abc"}); + // Add a string named "my_string" with the value "abc" + record.add("my_string", axom::sina::Datum {"abc"}); - // Add a list of scalars named "my_scalar_list" - std::vector scalarList = {1.2, -3.4, 5.6}; - record.add("my_scalar_list", axom::sina::Datum{scalarList}); + // Add a list of scalars named "my_scalar_list" + std::vector scalarList = {1.2, -3.4, 5.6}; + record.add("my_scalar_list", axom::sina::Datum {scalarList}); - // Add a list of strings named "my_string_list" - std::vector stringList = {"hi", "hello", "howdy"}; - record.add("my_string_list", axom::sina::Datum{stringList}); + // Add a list of strings named "my_string_list" + std::vector stringList = {"hi", "hello", "howdy"}; + record.add("my_string_list", axom::sina::Datum {stringList}); } //! [adding data] //! [curve sets] -void addCurveSets(axom::sina::Record &record) { - axom::sina::CurveSet timePlots{"time_plots"}; +void addCurveSets(axom::sina::Record &record) +{ + axom::sina::CurveSet timePlots {"time_plots"}; - // Add the independent variable - timePlots.addIndependentCurve( - axom::sina::Curve{"time", {0.0, 0.1, 0.25, 0.3}}); + // Add the independent variable + timePlots.addIndependentCurve(axom::sina::Curve {"time", {0.0, 0.1, 0.25, 0.3}}); - // Add some dependent variables. - // The length of each must be the same as the length of the independent. - timePlots.addDependentCurve( - axom::sina::Curve{"temperature", {300.0, 310.0, 350.0, 400.0}}); + // Add some dependent variables. + // The length of each must be the same as the length of the independent. + timePlots.addDependentCurve( + axom::sina::Curve {"temperature", {300.0, 310.0, 350.0, 400.0}}); - timePlots.addDependentCurve( - axom::sina::Curve{"energy", {0.0, 10.0, 20.0, 30.0}}); + timePlots.addDependentCurve( + axom::sina::Curve {"energy", {0.0, 10.0, 20.0, 30.0}}); - // Associate the curve sets with the record - record.add(timePlots); + // Associate the curve sets with the record + record.add(timePlots); } //! [curve sets] - //! [file add_and_remove] -void addAndRemoveFileToRecord(axom::sina::Record &run) { - axom::sina::File my_file{"some/path.txt"}; - // Adds the file to the record's file list - run.add(my_file); - // Removes the file from the record's file list - run.remove(my_file); +void addAndRemoveFileToRecord(axom::sina::Record &run) +{ + axom::sina::File my_file {"some/path.txt"}; + // Adds the file to the record's file list + run.add(my_file); + // Removes the file from the record's file list + run.remove(my_file); } //! [file add_and_remove] //! [relationships] -void associateRunToStudy(axom::sina::Document &doc, axom::sina::Record const &uqStudy, axom::sina::Record const &run) { - doc.add(axom::sina::Relationship{uqStudy.getId(), "contains", run.getId()}); +void associateRunToStudy(axom::sina::Document &doc, + axom::sina::Record const &uqStudy, + axom::sina::Record const &run) +{ + doc.add(axom::sina::Relationship {uqStudy.getId(), "contains", run.getId()}); } //! [relationships] - //! [library data foo] -void foo_collectData(axom::sina::DataHolder &fooData) { - fooData.add("temperature", axom::sina::Datum{500}); - fooData.add("energy", axom::sina::Datum{1.2e10}); +void foo_collectData(axom::sina::DataHolder &fooData) +{ + fooData.add("temperature", axom::sina::Datum {500}); + fooData.add("energy", axom::sina::Datum {1.2e10}); } //! [library data foo] //! [library data bar] -void bar_gatherData(axom::sina::DataHolder &barData) { - barData.add("temperature", axom::sina::Datum{400}); - barData.add("mass", axom::sina::Datum{15}); +void bar_gatherData(axom::sina::DataHolder &barData) +{ + barData.add("temperature", axom::sina::Datum {400}); + barData.add("mass", axom::sina::Datum {15}); } //! [library data bar] //! [library data host] -void gatherAllData(axom::sina::Record &record) { - auto fooData = record.addLibraryData("foo"); - auto barData = record.addLibraryData("bar"); +void gatherAllData(axom::sina::Record &record) +{ + auto fooData = record.addLibraryData("foo"); + auto barData = record.addLibraryData("bar"); - foo_collectData(*fooData); - bar_gatherData(*barData); + foo_collectData(*fooData); + bar_gatherData(*barData); - record.add("temperature", axom::sina::Datum{450}); + record.add("temperature", axom::sina::Datum {450}); } //! [library data host] //! [io write] -void save(axom::sina::Document const &doc) { - axom::sina::saveDocument(doc, "my_output.json"); +void save(axom::sina::Document const &doc) +{ + axom::sina::saveDocument(doc, "my_output.json"); } //! [io write] //! [io read] -void load() { - axom::sina::Document doc = axom::sina::loadDocument("my_output.json"); +void load() +{ + axom::sina::Document doc = axom::sina::loadDocument("my_output.json"); } //! [io read] //! [user defined] -void addUserDefined(axom::sina::Record &record) { - conduit::Node &userDefined = record.getUserDefinedContent(); - userDefined["var_1"] = "a"; - userDefined["var_2"] = "b"; - - conduit::Node subNode; - subNode["sub_1"] = 10; - subNode["sub_2"] = 20; - userDefined["sub_structure"] = subNode; +void addUserDefined(axom::sina::Record &record) +{ + conduit::Node &userDefined = record.getUserDefinedContent(); + userDefined["var_1"] = "a"; + userDefined["var_2"] = "b"; + + conduit::Node subNode; + subNode["sub_1"] = 10; + subNode["sub_2"] = 20; + userDefined["sub_structure"] = subNode; } //! [user defined] -} - -int main() { - // Call everything to keep the compiler from complaining about unused functions - axom::sina::Record run{axom::sina::ID{"my_record", axom::sina::IDType::Global}, "my_record_type"}; - axom::sina::Record study{axom::sina::ID{"my_run", axom::sina::IDType::Global}, "UQ study"}; - axom::sina::Document doc; - addData(run); - createRecord(); - createRun(); - associateRunToStudy(doc, study, run); - gatherAllData(run); - addCurveSets(run); - addAndRemoveFileToRecord(run); - addUserDefined(run); - // TODO - // - Add Record to doc - // - Check output file to see if record shows up - save(doc); - load(); +} // namespace + +int main() +{ + // Call everything to keep the compiler from complaining about unused functions + axom::sina::Record run { + axom::sina::ID {"my_record", axom::sina::IDType::Global}, + "my_record_type"}; + axom::sina::Record study {axom::sina::ID {"my_run", axom::sina::IDType::Global}, + "UQ study"}; + axom::sina::Document doc; + addData(run); + createRecord(); + createRun(); + associateRunToStudy(doc, study, run); + gatherAllData(run); + addCurveSets(run); + addAndRemoveFileToRecord(run); + addUserDefined(run); + // TODO + // - Add Record to doc + // - Check output file to see if record shows up + save(doc); + load(); } diff --git a/src/axom/sina/examples/sina_view_datum_types.cpp b/src/axom/sina/examples/sina_view_datum_types.cpp index 51d7271ac3..01d50c5150 100644 --- a/src/axom/sina/examples/sina_view_datum_types.cpp +++ b/src/axom/sina/examples/sina_view_datum_types.cpp @@ -3,31 +3,36 @@ // // 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 scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum{scalars}; +int main(void) +{ + // Define 3 different datums + axom::sina::Datum myDatum {12.34}; + std::string value = "foobar"; + axom::sina::Datum myOtherDatum {value}; + std::vector 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 myRecord{new axom::sina::Record{myID, "my_type"}}; + // Create a record to store the datum + axom::sina::ID myID {"my_record", axom::sina::IDType::Local}; + std::unique_ptr 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)); + // 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(); + // 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::type>(pair.second.getType()) << std::endl; - } + // Print the keys and type of datum + for(const auto& pair : data) + { + std::cout << pair.first << " is type: " + << static_cast::type>( + pair.second.getType()) + << std::endl; + } } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_view_datum_values.cpp b/src/axom/sina/examples/sina_view_datum_values.cpp index b832b302f6..019abf9b85 100644 --- a/src/axom/sina/examples/sina_view_datum_values.cpp +++ b/src/axom/sina/examples/sina_view_datum_values.cpp @@ -3,35 +3,37 @@ // // 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 scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum{scalars}; +int main(void) +{ + // Define 3 different datums + axom::sina::Datum myDatum {12.34}; + std::string value = "foobar"; + axom::sina::Datum myOtherDatum {value}; + std::vector 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 myRecord{new axom::sina::Record{myID, "my_type"}}; + // Create a record to store the datum + axom::sina::ID myID {"my_record", axom::sina::IDType::Local}; + std::unique_ptr 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)); + // 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(); + // Query the datum + auto& data = myRecord->getData(); - // Print the datum values - std::cout << "datum1: " << data.at("datum1").getScalar() << std::endl; - std::cout << "datum2: " << data.at("datum2").getValue() << std::endl; - std::cout << "datum3: "; - for (const auto& value : data.at("datum3").getScalarArray()) { - std::cout << value << " "; - } - std::cout << std::endl; + // Print the datum values + std::cout << "datum1: " << data.at("datum1").getScalar() << std::endl; + std::cout << "datum2: " << data.at("datum2").getValue() << std::endl; + std::cout << "datum3: "; + for(const auto& value : data.at("datum3").getScalarArray()) + { + std::cout << value << " "; + } + std::cout << std::endl; } \ No newline at end of file diff --git a/src/axom/sina/interface/sina_fortran_interface.cpp b/src/axom/sina/interface/sina_fortran_interface.cpp index 960c306698..0f3c55fcec 100644 --- a/src/axom/sina/interface/sina_fortran_interface.cpp +++ b/src/axom/sina/interface/sina_fortran_interface.cpp @@ -3,384 +3,407 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include "axom/sina/interface/sina_fortran_interface.h" - axom::sina::Document *sina_document; -extern "C" char* Get_File_Extension(char * input_fn) +extern "C" char *Get_File_Extension(char *input_fn) { - char* ext = strrchr(input_fn, '.'); - if (!ext) { return(new char[1]{'\0'}); } - return(ext + 1); + char *ext = strrchr(input_fn, '.'); + if(!ext) + { + return (new char[1] {'\0'}); + } + return (ext + 1); } -extern "C" void create_document_and_record_(char * recID) +extern "C" void create_document_and_record_(char *recID) { sina_document = new axom::sina::Document; // Create a record 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 id{recID, axom::sina::IDType::Global}; - std::unique_ptr myRecord{new axom::sina::Record{id, "my_type"}}; + axom::sina::ID id {recID, axom::sina::IDType::Global}; + std::unique_ptr myRecord { + new axom::sina::Record {id, "my_type"}}; sina_document->add(std::move(myRecord)); } extern "C" axom::sina::Record *Sina_Get_Record() { - if(sina_document) + if(sina_document) + { + axom::sina::Document::RecordList const &allRecords = + sina_document->getRecords(); + if(allRecords.size()) { - axom::sina::Document::RecordList const &allRecords = sina_document->getRecords(); - if(allRecords.size()) - { - std::unique_ptr const &myRecord = allRecords.front(); - return myRecord.get(); - } + std::unique_ptr const &myRecord = allRecords.front(); + return myRecord.get(); } - return nullptr; + } + return nullptr; } -extern "C" void sina_add_logical_(char * key, bool *value, char *units, char *tags) +extern "C" void sina_add_logical_(char *key, bool *value, char *units, char *tags) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - std::string key_name = std::string(key); - if(sina_record) + axom::sina::Datum datum {static_cast(*value)}; + if(units) + { + std::string key_units = std::string(units); + if(key_units != "") { - axom::sina::Datum datum{static_cast(*value)}; - if (units) - { - std::string key_units = std::string(units); - if(key_units != "") - { - datum.setUnits(key_units); - } - } - if (tags) - { - std::vector tagVector = { tags }; - if(tagVector.front() != "") - { - datum.setTags(tagVector); - } - } - sina_record->add(key_name, datum); + datum.setUnits(key_units); } - } -} - -extern "C" void sina_add_long_(char * key, long long int *value, char *units, char * tags) -{ - if (sina_document) - { - axom::sina::Record *sina_record = Sina_Get_Record(); - std::string key_name = std::string(key); - if(sina_record) + } + if(tags) + { + std::vector tagVector = {tags}; + if(tagVector.front() != "") { - axom::sina::Datum datum{static_cast(*value)}; - if (units) - { - std::string key_units = std::string(units); - if(key_units != "") - { - datum.setUnits(key_units); - } - } - if (tags) - { - std::vector tagVector = { tags }; - if(tagVector.front() != "") - { - datum.setTags(tagVector); - } - } - sina_record->add(key_name, datum); + datum.setTags(tagVector); } + } + sina_record->add(key_name, datum); } + } } -extern "C" void sina_add_int_(char * key, int *value, char *units, char *tags) +extern "C" void sina_add_long_(char *key, + long long int *value, + char *units, + char *tags) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - std::string key_name = std::string(key); - if(sina_record) + axom::sina::Datum datum {static_cast(*value)}; + if(units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if(tags) + { + std::vector tagVector = {tags}; + if(tagVector.front() != "") { - axom::sina::Datum datum{static_cast(*value)}; - if (units) - { - std::string key_units = std::string(units); - if(key_units != "") - { - datum.setUnits(key_units); - } - } - if (tags) - { - std::vector tagVector = { tags }; - if(tagVector.front() != "") - { - datum.setTags(tagVector); - } - } - sina_record->add(key_name, datum); + datum.setTags(tagVector); } + } + sina_record->add(key_name, datum); } + } } -extern "C" void sina_add_double_(char * key, double *value, char *units, char *tags) +extern "C" void sina_add_int_(char *key, int *value, char *units, char *tags) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - std::string key_name = std::string(key); - if(sina_record) + axom::sina::Datum datum {static_cast(*value)}; + if(units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if(tags) + { + std::vector tagVector = {tags}; + if(tagVector.front() != "") { - axom::sina::Datum datum{*value}; - if (units) - { - std::string key_units = std::string(units); - if(key_units != "") - { - datum.setUnits(key_units); - } - } - if (tags) - { - std::vector tagVector = { tags }; - if(tagVector.front() != "") - { - datum.setTags(tagVector); - } - } - sina_record->add(key_name, datum); + datum.setTags(tagVector); } + } + sina_record->add(key_name, datum); } + } } -extern "C" void sina_add_float_(char * key, float *value, char *units, char *tags) +extern "C" void sina_add_double_(char *key, double *value, char *units, char *tags) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - std::string key_name = std::string(key); - if(sina_record) + axom::sina::Datum datum {*value}; + if(units) + { + std::string key_units = std::string(units); + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if(tags) + { + std::vector tagVector = {tags}; + if(tagVector.front() != "") { - axom::sina::Datum datum{*value}; - if (units) - { - std::string key_units = std::string(units); - if(key_units != "") - { - datum.setUnits(key_units); - } - } - if (tags) - { - std::vector tagVector = { tags }; - if(tagVector.front() != "") - { - datum.setTags(tagVector); - } - } - sina_record->add(key_name, datum); + datum.setTags(tagVector); } + } + sina_record->add(key_name, datum); } + } } -extern "C" void sina_add_string_(char * key, char *value, char *units, char *tags) +extern "C" void sina_add_float_(char *key, float *value, char *units, char *tags) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - std::string key_name = std::string(key); - std::string key_value = std::string(value); + axom::sina::Datum datum {*value}; + if(units) + { std::string key_units = std::string(units); - if(sina_record) + if(key_units != "") + { + datum.setUnits(key_units); + } + } + if(tags) + { + std::vector tagVector = {tags}; + if(tagVector.front() != "") { - axom::sina::Datum datum{key_value}; - if (units) - { - std::string key_units = std::string(units); - if(key_units != "") - { - datum.setUnits(key_units); - } - } - if (tags) - { - std::vector tagVector = { tags }; - if(tagVector.front() != "") - { - datum.setTags(tagVector); - } - } - sina_record->add(key_name, datum); + datum.setTags(tagVector); } + } + sina_record->add(key_name, datum); } + } } -extern "C" void sina_add_file_(char * filename, char *mime_type) +extern "C" void sina_add_string_(char *key, char *value, char *units, char *tags) { - std::string used_mime_type = ""; - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + std::string key_name = std::string(key); + std::string key_value = std::string(value); + std::string key_units = std::string(units); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - if(mime_type) - { - used_mime_type = std::string(mime_type); - } - axom::sina::File my_file{filename}; - if(used_mime_type != "") - { - my_file.setMimeType(used_mime_type); - } - else + axom::sina::Datum datum {key_value}; + if(units) + { + std::string key_units = std::string(units); + if(key_units != "") { - used_mime_type = Get_File_Extension(filename); - my_file.setMimeType(used_mime_type); + datum.setUnits(key_units); } - - if(sina_record) + } + if(tags) + { + std::vector tagVector = {tags}; + if(tagVector.front() != "") { - sina_record->add(my_file); + datum.setTags(tagVector); } + } + sina_record->add(key_name, datum); } + } } -extern "C" void write_sina_document_(char * input_fn) +extern "C" void sina_add_file_(char *filename, char *mime_type) { - std::string filename(input_fn); - // Save everything - if(sina_document) + std::string used_mime_type = ""; + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(mime_type) + { + used_mime_type = std::string(mime_type); + } + axom::sina::File my_file {filename}; + if(used_mime_type != "") + { + my_file.setMimeType(used_mime_type); + } + else { - axom::sina::saveDocument(*sina_document, filename.c_str()); + used_mime_type = Get_File_Extension(filename); + my_file.setMimeType(used_mime_type); } + + if(sina_record) + { + sina_record->add(my_file); + } + } +} + +extern "C" void write_sina_document_(char *input_fn) +{ + std::string filename(input_fn); + // Save everything + if(sina_document) + { + axom::sina::saveDocument(*sina_document, filename.c_str()); + } } extern "C" void sina_add_curveset_(char *name) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - if(sina_record) - { - axom::sina::CurveSet cs{name}; - sina_record->add(cs); - } + axom::sina::CurveSet cs {name}; + sina_record->add(cs); } + } } -extern "C" void sina_add_curve_long_(char *curveset_name, char *curve_name, long long int *values, int *n, bool *independent) +extern "C" void sina_add_curve_long_(char *curveset_name, + char *curve_name, + long long int *values, + int *n, + bool *independent) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - if(sina_record) - { - double y[*n]; - for (int i=0; i<*n;i++){ - y[i] = values[i]; - } - axom::sina::Curve curve{curve_name, y, static_cast(*n)}; + double y[*n]; + for(int i = 0; i < *n; i++) + { + y[i] = values[i]; + } + axom::sina::Curve curve {curve_name, y, static_cast(*n)}; - auto &curvesets = sina_record->getCurveSets(); - axom::sina::CurveSet cs=curvesets.at(curveset_name); - if (*independent) - { - cs.addIndependentCurve(curve); - } - else - { - cs.addDependentCurve(curve); - } - sina_record->add(cs); - } + auto &curvesets = sina_record->getCurveSets(); + axom::sina::CurveSet cs = curvesets.at(curveset_name); + if(*independent) + { + cs.addIndependentCurve(curve); + } + else + { + cs.addDependentCurve(curve); + } + sina_record->add(cs); } + } } -extern "C" void sina_add_curve_int_(char *curveset_name, char *curve_name, int *values, int *n, bool *independent) +extern "C" void sina_add_curve_int_(char *curveset_name, + char *curve_name, + int *values, + int *n, + bool *independent) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - if(sina_record) - { - double y[*n]; - for (int i=0; i<*n;i++){ - y[i] = values[i]; - } - axom::sina::Curve curve{curve_name, y, static_cast(*n)}; + double y[*n]; + for(int i = 0; i < *n; i++) + { + y[i] = values[i]; + } + axom::sina::Curve curve {curve_name, y, static_cast(*n)}; - auto &curvesets = sina_record->getCurveSets(); - axom::sina::CurveSet cs=curvesets.at(curveset_name); - if (*independent) - { - cs.addIndependentCurve(curve); - } - else - { - cs.addDependentCurve(curve); - } - sina_record->add(cs); - } + auto &curvesets = sina_record->getCurveSets(); + axom::sina::CurveSet cs = curvesets.at(curveset_name); + if(*independent) + { + cs.addIndependentCurve(curve); + } + else + { + cs.addDependentCurve(curve); + } + sina_record->add(cs); } + } } -extern "C" void sina_add_curve_float_(char *curveset_name, char *curve_name, float *values, int *n, bool *independent) +extern "C" void sina_add_curve_float_(char *curveset_name, + char *curve_name, + float *values, + int *n, + bool *independent) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - if(sina_record) - { - double y[*n]; - for (int i=0; i<*n;i++){ - y[i] = values[i]; - } - axom::sina::Curve curve{curve_name, y, static_cast(*n)}; + double y[*n]; + for(int i = 0; i < *n; i++) + { + y[i] = values[i]; + } + axom::sina::Curve curve {curve_name, y, static_cast(*n)}; - auto &curvesets = sina_record->getCurveSets(); - axom::sina::CurveSet cs=curvesets.at(curveset_name); - if (*independent) - { - cs.addIndependentCurve(curve); - } - else - { - cs.addDependentCurve(curve); - } - sina_record->add(cs); - } + auto &curvesets = sina_record->getCurveSets(); + axom::sina::CurveSet cs = curvesets.at(curveset_name); + if(*independent) + { + cs.addIndependentCurve(curve); + } + else + { + cs.addDependentCurve(curve); + } + sina_record->add(cs); } + } } -extern "C" void sina_add_curve_double_(char *curveset_name, char *curve_name, double *values, int *n, bool *independent) +extern "C" void sina_add_curve_double_(char *curveset_name, + char *curve_name, + double *values, + int *n, + bool *independent) { - if (sina_document) + if(sina_document) + { + axom::sina::Record *sina_record = Sina_Get_Record(); + if(sina_record) { - axom::sina::Record *sina_record = Sina_Get_Record(); - if(sina_record) - { - axom::sina::Curve curve{curve_name, values, static_cast(*n)}; + axom::sina::Curve curve {curve_name, values, static_cast(*n)}; - auto &curvesets = sina_record->getCurveSets(); - axom::sina::CurveSet cs=curvesets.at(curveset_name); - if (*independent) - { - cs.addIndependentCurve(curve); - } - else - { - cs.addDependentCurve(curve); - } - sina_record->add(cs); - } + auto &curvesets = sina_record->getCurveSets(); + axom::sina::CurveSet cs = curvesets.at(curveset_name); + if(*independent) + { + cs.addIndependentCurve(curve); + } + else + { + cs.addDependentCurve(curve); + } + sina_record->add(cs); } + } } - - diff --git a/src/axom/sina/interface/sina_fortran_interface.h b/src/axom/sina/interface/sina_fortran_interface.h index 2a4d4bdbfc..ed004c81be 100644 --- a/src/axom/sina/interface/sina_fortran_interface.h +++ b/src/axom/sina/interface/sina_fortran_interface.h @@ -3,14 +3,12 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - - #include "axom/sina/core/Document.hpp" #include "axom/sina/core/Record.hpp" #include "axom/sina/core/Run.hpp" #include "axom/sina.hpp" -extern "C" char* Get_File_Extension(char *); +extern "C" char *Get_File_Extension(char *); extern "C" void create_document_and_run_(char *); extern "C" axom::sina::Record *Sina_Get_Run(); extern "C" void sina_add_file_to_record_(char *); @@ -27,4 +25,3 @@ extern "C" void sina_add_curve_double_(char *, char *, double *, int *, bool *); extern "C" void sina_add_curve_float_(char *, char *, float *, int *, bool *); extern "C" void sina_add_curve_int_(char *, char *, int *, int *, bool *); extern "C" void sina_add_curve_long_(char *, char *, long long int *, int *, bool *); - diff --git a/src/axom/sina/tests/ConduitTestUtils.cpp b/src/axom/sina/tests/ConduitTestUtils.cpp index c75e011b1f..2abb1edecc 100644 --- a/src/axom/sina/tests/ConduitTestUtils.cpp +++ b/src/axom/sina/tests/ConduitTestUtils.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina/tests/ConduitTestUtils.hpp" namespace axom @@ -13,18 +12,19 @@ namespace sina namespace testing { -conduit::Node parseJsonValue(std::string const &valueAsString) { - // If we just try to do node.parse(valueAsString, "json"), then passing - // in strings does not work. We need to create a document with a key - // so that valueAsString can be parsed as a value. - conduit::Node node; - std::string fullContents = "{\"TEST_KEY\": "; - fullContents += valueAsString; - fullContents += "}"; - node.parse(fullContents, "json"); - return node.child("TEST_KEY"); +conduit::Node parseJsonValue(std::string const &valueAsString) +{ + // If we just try to do node.parse(valueAsString, "json"), then passing + // in strings does not work. We need to create a document with a key + // so that valueAsString can be parsed as a value. + conduit::Node node; + std::string fullContents = "{\"TEST_KEY\": "; + fullContents += valueAsString; + fullContents += "}"; + node.parse(fullContents, "json"); + return node.child("TEST_KEY"); } -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/ConduitTestUtils.hpp b/src/axom/sina/tests/ConduitTestUtils.hpp index fc31630f71..70c2c3af46 100644 --- a/src/axom/sina/tests/ConduitTestUtils.hpp +++ b/src/axom/sina/tests/ConduitTestUtils.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_CONDUITTESTUTILS_HPP #define SINA_CONDUITTESTUTILS_HPP @@ -30,16 +29,17 @@ conduit::Node parseJsonValue(std::string const &valueAsString); // A matcher which verifies that a given Conduit node produces the expected // JSON string -MATCHER_P(MatchesJson, expectedJsonString, "") { - conduit::Node expected = parseJsonValue(expectedJsonString); - *result_listener << "Given node is " << arg.to_json_default(); - conduit::Node diff; - bool differ = expected.diff(arg, diff); - return !differ; +MATCHER_P(MatchesJson, expectedJsonString, "") +{ + conduit::Node expected = parseJsonValue(expectedJsonString); + *result_listener << "Given node is " << arg.to_json_default(); + conduit::Node diff; + bool differ = expected.diff(arg, diff); + return !differ; } -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace testing +} // namespace sina +} // namespace axom -#endif //SINA_CONDUITTESTUTILS_HPP +#endif //SINA_CONDUITTESTUTILS_HPP diff --git a/src/axom/sina/tests/TestRecord.cpp b/src/axom/sina/tests/TestRecord.cpp index 23242b627b..d333f019f9 100644 --- a/src/axom/sina/tests/TestRecord.cpp +++ b/src/axom/sina/tests/TestRecord.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina/tests/TestRecord.hpp" namespace axom @@ -13,17 +12,18 @@ namespace sina namespace testing { -template<> -TestRecord::TestRecord(conduit::Node const &asNode) : - Record{asNode}, - value{getRequiredString(TEST_RECORD_VALUE_KEY, asNode, "TestRecord")} {} +template <> +TestRecord::TestRecord(conduit::Node const &asNode) + : Record {asNode} + , value {getRequiredString(TEST_RECORD_VALUE_KEY, asNode, "TestRecord")} +{ } -template<> -TestRecord::TestRecord(conduit::Node const &asNode) : - Record{asNode}, - value{getRequiredField(TEST_RECORD_VALUE_KEY, asNode, - "TestRecord").as_int()} {} +template <> +TestRecord::TestRecord(conduit::Node const &asNode) + : Record {asNode} + , value {getRequiredField(TEST_RECORD_VALUE_KEY, asNode, "TestRecord").as_int()} +{ } -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/TestRecord.hpp b/src/axom/sina/tests/TestRecord.hpp index b89f543c0d..b665cd4b57 100644 --- a/src/axom/sina/tests/TestRecord.hpp +++ b/src/axom/sina/tests/TestRecord.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef SINA_TESTRECORD_HPP #define SINA_TESTRECORD_HPP @@ -25,63 +24,63 @@ char constexpr TEST_RECORD_VALUE_KEY[] = "testKey"; * * @tparam T the type of the value to store */ -template -class TestRecord : public Record { +template +class TestRecord : public Record +{ public: - - /** + /** * Create a new TestRecord. * * @param id the ID of the record. It is always a global ID. * @param type the type of the record * @param value the value of the record */ - TestRecord(std::string id, std::string type, T value); + TestRecord(std::string id, std::string type, T value); - /** + /** * Create a new TestRecord from its conduit Node representation. * * NOTE: This needs to be implemented explicitly for each type of value * * @param asValue the record in its Node representation */ - explicit TestRecord(conduit::Node const &asValue); + explicit TestRecord(conduit::Node const &asValue); - /** + /** * Get the record's value. * * @return the record's value */ - const T &getValue() const noexcept { - return value; - } + const T &getValue() const noexcept { return value; } - conduit::Node toNode() const override; + conduit::Node toNode() const override; private: - T value; + T value; }; -template -TestRecord::TestRecord(std::string id, std::string type, T value_) : - Record{ID{std::move(id), IDType::Global}, std::move(type)}, - value{std::move(value_)} {} +template +TestRecord::TestRecord(std::string id, std::string type, T value_) + : Record {ID {std::move(id), IDType::Global}, std::move(type)} + , value {std::move(value_)} +{ } -template<> +template <> TestRecord::TestRecord(conduit::Node const &asNode); -template<> +template <> TestRecord::TestRecord(conduit::Node const &asJson); -template -conduit::Node TestRecord::toNode() const { - auto asJson = Record::toNode(); - asJson[TEST_RECORD_VALUE_KEY] = value; - return asJson; +template +conduit::Node TestRecord::toNode() const +{ + auto asJson = Record::toNode(); + asJson[TEST_RECORD_VALUE_KEY] = value; + return asJson; } -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace testing +} // namespace sina +} // namespace axom -#endif //SINA_TESTRECORD_HPP +#endif //SINA_TESTRECORD_HPP diff --git a/src/axom/sina/tests/sina_AdiakWriter.cpp b/src/axom/sina/tests/sina_AdiakWriter.cpp index bf42e59538..bb1f4617d8 100644 --- a/src/axom/sina/tests/sina_AdiakWriter.cpp +++ b/src/axom/sina/tests/sina_AdiakWriter.cpp @@ -3,27 +3,26 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "axom/sina/core/AdiakWriter.hpp" #ifdef AXOM_USE_ADIAK -#include -#include -#include + #include + #include + #include -#include "gtest/gtest.h" -#include "gmock/gmock.h" + #include "gtest/gtest.h" + #include "gmock/gmock.h" -#include "adiak.hpp" + #include "adiak.hpp" extern "C" { -#include "adiak_tool.h" -#include "adiak.h" + #include "adiak_tool.h" + #include "adiak.h" } -#include "axom/sina/core/Datum.hpp" -#include "axom/sina/core/ID.hpp" -#include "axom/sina/core/Run.hpp" + #include "axom/sina/core/Datum.hpp" + #include "axom/sina/core/ID.hpp" + #include "axom/sina/core/Run.hpp" namespace axom { @@ -34,37 +33,54 @@ namespace testing namespace { -using ::testing::HasSubstr; using ::testing::DoubleEq; using ::testing::ElementsAre; +using ::testing::HasSubstr; char const EXPECTED_DATA_KEY[] = "data"; char const EXPECTED_FILES_KEY[] = "files"; -class AdiakWriterTest : public ::testing::Test { - - protected: - static void SetUpTestCase() { +class AdiakWriterTest : public ::testing::Test +{ +protected: + static void SetUpTestCase() + { adiak::init(nullptr); - adiak_register_cb(1, adiak_category_all, AdiakWriterTest::callbackWrapper, 0, ¤t_test); - } - - void SetUp() override { - current_test=this; + adiak_register_cb(1, + adiak_category_all, + AdiakWriterTest::callbackWrapper, + 0, + ¤t_test); } - static void callbackWrapper(const char *name, adiak_category_t category, const char *subcategory, adiak_value_t *val, adiak_datatype_t *adiak_type, void *adiakwriter){ - auto test = static_cast(adiakwriter); - adiakSinaCallback(name, category, subcategory, val, adiak_type, &((*test)->record)); + void SetUp() override { current_test = this; } + + static void callbackWrapper(const char *name, + adiak_category_t category, + const char *subcategory, + adiak_value_t *val, + adiak_datatype_t *adiak_type, + void *adiakwriter) + { + auto test = static_cast(adiakwriter); + adiakSinaCallback(name, + category, + subcategory, + val, + adiak_type, + &((*test)->record)); } - axom::sina::Record record{axom::sina::ID{"test_run", axom::sina::IDType::Local}, "test_type"}; + axom::sina::Record record { + axom::sina::ID {"test_run", axom::sina::IDType::Local}, + "test_type"}; static AdiakWriterTest *current_test; }; AdiakWriterTest *AdiakWriterTest::current_test; -TEST_F(AdiakWriterTest, basic_assignment) { +TEST_F(AdiakWriterTest, basic_assignment) +{ //adiak::init(nullptr); //adiak_register_cb(1, adiak_category_all, sina::adiakSinaCallback, 0, callback_record_ptr); std::string name1 = "name1"; @@ -83,7 +99,8 @@ TEST_F(AdiakWriterTest, basic_assignment) { EXPECT_EQ(tags2[0], asNode[EXPECTED_DATA_KEY][name2]["tags"][0].as_string()); } -TEST_F(AdiakWriterTest, scalar_types) { +TEST_F(AdiakWriterTest, scalar_types) +{ std::string name1 = "my_long"; long value1 = 0; std::string name2 = "my_double"; @@ -97,64 +114,79 @@ TEST_F(AdiakWriterTest, scalar_types) { } // No extra test for string_types (besides date) as they're handled identically -TEST_F(AdiakWriterTest, date_type) { +TEST_F(AdiakWriterTest, date_type) +{ std::string name1 = "my_date"; auto result = adiak::value(name1, adiak::date(1568397849)); EXPECT_TRUE(result); auto toNode = record.toNode(); - EXPECT_EQ("Fri, 13 Sep 2019 11:04:09 -0700", toNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); + EXPECT_EQ("Fri, 13 Sep 2019 11:04:09 -0700", + toNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); } -TEST_F(AdiakWriterTest, list_types) { +TEST_F(AdiakWriterTest, list_types) +{ std::string name1 = "my_scalar_list"; - std::vector value1{4.5, 0, 5.12, 42}; + std::vector value1 {4.5, 0, 5.12, 42}; std::string name2 = "my_string_list"; - std::set value2{"spam", "egg and bacon", "egg and spam"}; + std::set value2 {"spam", "egg and bacon", "egg and spam"}; auto result1 = adiak::value(name1, value1); auto result2 = adiak::value(name2, value2); EXPECT_TRUE(result1 && result2); auto asNode = record.toNode(); auto doub_array = asNode[EXPECTED_DATA_KEY][name1]["value"].as_double_ptr(); - std::vectorscal_child_vals(doub_array, doub_array+asNode[EXPECTED_DATA_KEY][name1]["value"].dtype().number_of_elements()); + std::vector scal_child_vals( + doub_array, + doub_array + + asNode[EXPECTED_DATA_KEY][name1]["value"].dtype().number_of_elements()); EXPECT_EQ(value1, scal_child_vals); std::set node_vals; auto val_itr = asNode[EXPECTED_DATA_KEY][name2]["value"].children(); - while(val_itr.has_next()) - node_vals.insert(val_itr.next().as_string()); + while(val_itr.has_next()) node_vals.insert(val_itr.next().as_string()); EXPECT_EQ(value2, node_vals); } -TEST_F(AdiakWriterTest, files) { +TEST_F(AdiakWriterTest, files) +{ std::string name1 = "my_bash"; std::string value1 = "/bin/bash"; std::string name2 = "my_cat_pics"; std::string value2 = "~/pictures/neighbor_cat.png"; - std::vector tags2{name2}; + std::vector tags2 {name2}; auto result1 = adiak::value(name1, adiak::path(value1)); auto result2 = adiak::value(name2, adiak::path(value2)); EXPECT_TRUE(result1 && result2); auto asNode = record.toNode(); EXPECT_FALSE(asNode[EXPECTED_FILES_KEY].child(value1).dtype().is_empty()); - EXPECT_EQ(1, asNode[EXPECTED_FILES_KEY].child(value2)["tags"].number_of_children()); - EXPECT_EQ(tags2[0], asNode[EXPECTED_FILES_KEY].child(value2)["tags"][0].as_string()); + EXPECT_EQ( + 1, + asNode[EXPECTED_FILES_KEY].child(value2)["tags"].number_of_children()); + EXPECT_EQ(tags2[0], + asNode[EXPECTED_FILES_KEY].child(value2)["tags"][0].as_string()); } -TEST_F(AdiakWriterTest, files_list){ +TEST_F(AdiakWriterTest, files_list) +{ std::string fileListName = "my_gecko_pics"; std::string fileListVal1 = "~/pictures/spike.png"; std::string fileListVal2 = "~/pictures/sandy.png"; - std::vector fileListAdiak{adiak::path(fileListVal1), adiak::path(fileListVal2)}; + std::vector fileListAdiak {adiak::path(fileListVal1), + adiak::path(fileListVal2)}; std::vector tags = {"string"}; EXPECT_TRUE(adiak::value(fileListName, fileListAdiak)); auto asNode = record.toNode(); EXPECT_FALSE(asNode[EXPECTED_FILES_KEY].child(fileListVal1).dtype().is_empty()); - EXPECT_EQ(1, asNode[EXPECTED_FILES_KEY].child(fileListVal2)["tags"].number_of_children()); - EXPECT_EQ(fileListName, asNode[EXPECTED_FILES_KEY].child(fileListVal2)["tags"][0].as_string()); + EXPECT_EQ( + 1, + asNode[EXPECTED_FILES_KEY].child(fileListVal2)["tags"].number_of_children()); + EXPECT_EQ( + fileListName, + asNode[EXPECTED_FILES_KEY].child(fileListVal2)["tags"][0].as_string()); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom -#endif // AXOM_USE_ADIAK +#endif // AXOM_USE_ADIAK diff --git a/src/axom/sina/tests/sina_ConduitUtil.cpp b/src/axom/sina/tests/sina_ConduitUtil.cpp index 41b9284ff1..145d7adc3d 100644 --- a/src/axom/sina/tests/sina_ConduitUtil.cpp +++ b/src/axom/sina/tests/sina_ConduitUtil.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include "gtest/gtest.h" @@ -22,201 +21,247 @@ namespace { using ::testing::ContainerEq; -using ::testing::ElementsAre; using ::testing::DoubleEq; +using ::testing::ElementsAre; using ::testing::HasSubstr; -TEST(ConduitUtil, getRequiredField_present) { - conduit::Node parent; - parent["fieldName"] = "field value"; +TEST(ConduitUtil, getRequiredField_present) +{ + conduit::Node parent; + parent["fieldName"] = "field value"; + auto &field = getRequiredField("fieldName", parent, "parent name"); + EXPECT_TRUE(field.dtype().is_string()); + EXPECT_EQ("field value", field.as_string()); +} + +TEST(ConduitUtil, getRequiredField_missing) +{ + conduit::Node parent; + try + { auto &field = getRequiredField("fieldName", parent, "parent name"); - EXPECT_TRUE(field.dtype().is_string()); - EXPECT_EQ("field value", field.as_string()); -} - -TEST(ConduitUtil, getRequiredField_missing) { - conduit::Node parent; - try { - auto &field = getRequiredField("fieldName", parent, "parent name"); - FAIL() << "Should not have found field, but got " << field.name(); - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr("fieldName")); - EXPECT_THAT(expected.what(), HasSubstr("parent name")); - } -} - -TEST(ConduitUtil, getRequiredField_slashes) { - conduit::Node parent; - // Conduit by default parses /, creating parent["some"]["name"] - parent["some/name"] = 24; - // This is how we provide a literal name with slashes - parent.add_child("some/name") = 42; - EXPECT_EQ(42, - getRequiredField("some/name", parent, "parent name").to_int64()); -} - -TEST(ConduitUtil, getRequiredString_valid) { - conduit::Node parent; - parent["fieldName"] = "field value"; - EXPECT_EQ("field value", - getRequiredString("fieldName", parent, "parent name")); -} - -TEST(ConduitUtil, getRequiredString_missing) { - conduit::Node parent; - try { - auto value = getRequiredString("fieldName", parent, "parent name"); - FAIL() << "Should not have found string, but got " << value; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr("fieldName")); - EXPECT_THAT(expected.what(), HasSubstr("parent name")); - } -} - -TEST(ConduitUtil, getRequiredString_wrongType) { - conduit::Node parent; - parent["fieldName"] = 123; - try { - auto value = getRequiredString("fieldName", parent, "parent name"); - FAIL() << "Should not have found string, but got " << value; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr("fieldName")); - EXPECT_THAT(expected.what(), HasSubstr("parent name")); - EXPECT_THAT(expected.what(), HasSubstr("string")); - } -} - -TEST(ConduitUtil, getRequiredString_slashes) { - conduit::Node parent; - parent["some/name"] = "undesired value"; - parent.add_child("some/name") = "desired value"; - EXPECT_EQ("desired value", - getRequiredString("some/name", parent, "parent name")); -} - -TEST(ConduitUtil, getRequiredDouble_valid) { - conduit::Node parent; - parent["fieldName"] = 3.14; - EXPECT_THAT(3.14, - DoubleEq(getRequiredDouble("fieldName", parent, "parent name"))); -} - -TEST(ConduitUtil, getRequiredDouble_missing) { - conduit::Node parent; - try { - auto value = getRequiredDouble("fieldName", parent, "parent name"); - FAIL() << "Should not have found double, but got " << value; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr("fieldName")); - EXPECT_THAT(expected.what(), HasSubstr("parent name")); - } -} - -TEST(ConduitUtil, getRequiredDouble_wrongType) { - conduit::Node parent; - parent["fieldName"] = "field value"; - try { - auto value = getRequiredDouble("fieldName", parent, "parent name"); - FAIL() << "Should not have found double, but got " << value; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr("fieldName")); - EXPECT_THAT(expected.what(), HasSubstr("parent name")); - EXPECT_THAT(expected.what(), HasSubstr("double")); - } -} - -TEST(ConduitUtil, getOptionalString_valid) { - conduit::Node parent; - parent["fieldName"] = "the value"; - EXPECT_EQ("the value", - getOptionalString("fieldName", parent, "parent name")); -} - -TEST(ConduitUtil, getOptionalString_missing) { - conduit::Node parent; - EXPECT_EQ("", getOptionalString("fieldName", parent, "parent name")); -} - -TEST(ConduitUtil, getOptionalString_explicitNullValue) { - conduit::Node parent; - parent["fieldName"]; - EXPECT_EQ("", getOptionalString("fieldName", parent, "parent name")); -} - -TEST(ConduitUtil, getOptionalString_wrongType) { - conduit::Node parent; - parent["fieldName"] = 123; - try { - auto value = getOptionalString("fieldName", parent, "parent name"); - FAIL() << "Should not have found string, but got " << value; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr("fieldName")); - EXPECT_THAT(expected.what(), HasSubstr("parent name")); - EXPECT_THAT(expected.what(), HasSubstr("string")); - } -} - -TEST(ConduitUtil, getOptionalField_slashes) { - conduit::Node parent; - parent["some/name"] = "undesired value"; - EXPECT_EQ("", - getOptionalString("some/name", parent, "parent name")); -} - -TEST(ConduitUtil, toDoubleVector_empty) { - conduit::Node emptyList = parseJsonValue("[]"); - EXPECT_EQ(std::vector{}, toDoubleVector(emptyList, "testNode")); -} - -TEST(ConduitUtil, toDoubleVector_validValues) { - conduit::Node nonEmptyList = parseJsonValue("[1, 2.0, 3, 4]"); - EXPECT_THAT(toDoubleVector(nonEmptyList, "testNode"), - ElementsAre(1.0, 2.0, 3.0, 4.0)); -} - -TEST(ConduitUtil, toDoubleVector_NotList) { - conduit::Node notList = parseJsonValue("\"this is not a list of doubles\""); - try { - toDoubleVector(notList, "someName"); - FAIL() << "Should have thrown an exception"; - } catch (std::invalid_argument const &ex) { - EXPECT_THAT(ex.what(), HasSubstr("someName")); - } + FAIL() << "Should not have found field, but got " << field.name(); + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + } +} + +TEST(ConduitUtil, getRequiredField_slashes) +{ + conduit::Node parent; + // Conduit by default parses /, creating parent["some"]["name"] + parent["some/name"] = 24; + // This is how we provide a literal name with slashes + parent.add_child("some/name") = 42; + EXPECT_EQ(42, getRequiredField("some/name", parent, "parent name").to_int64()); +} + +TEST(ConduitUtil, getRequiredString_valid) +{ + conduit::Node parent; + parent["fieldName"] = "field value"; + EXPECT_EQ("field value", + getRequiredString("fieldName", parent, "parent name")); +} + +TEST(ConduitUtil, getRequiredString_missing) +{ + conduit::Node parent; + try + { + auto value = getRequiredString("fieldName", parent, "parent name"); + FAIL() << "Should not have found string, but got " << value; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + } } -TEST(ConduitUtil, toStringVector_empty) { - conduit::Node emptyList = parseJsonValue("[]"); - EXPECT_THAT(toStringVector(emptyList, "testNode"), - ContainerEq(std::vector{})); -} +TEST(ConduitUtil, getRequiredString_wrongType) +{ + conduit::Node parent; + parent["fieldName"] = 123; + try + { + auto value = getRequiredString("fieldName", parent, "parent name"); + FAIL() << "Should not have found string, but got " << value; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + EXPECT_THAT(expected.what(), HasSubstr("string")); + } +} + +TEST(ConduitUtil, getRequiredString_slashes) +{ + conduit::Node parent; + parent["some/name"] = "undesired value"; + parent.add_child("some/name") = "desired value"; + EXPECT_EQ("desired value", + getRequiredString("some/name", parent, "parent name")); +} + +TEST(ConduitUtil, getRequiredDouble_valid) +{ + conduit::Node parent; + parent["fieldName"] = 3.14; + EXPECT_THAT(3.14, + DoubleEq(getRequiredDouble("fieldName", parent, "parent name"))); +} -TEST(ConduitUtil, toStringVector_validValues) { - conduit::Node nonEmptyList = parseJsonValue(R"(["s1", "s2", "s3"])"); - EXPECT_THAT(toStringVector(nonEmptyList, "testNode"), - ElementsAre("s1", "s2", "s3")); -} - -TEST(ConduitUtil, toStringVector_NotList) { - conduit::Node notList = parseJsonValue("\"this is not a list of doubles\""); - try { - toStringVector(notList, "someName"); - FAIL() << "Should have thrown an exception."; - } catch (std::invalid_argument const &ex) { - EXPECT_THAT(ex.what(), HasSubstr("someName")); - } -} +TEST(ConduitUtil, getRequiredDouble_missing) +{ + conduit::Node parent; + try + { + auto value = getRequiredDouble("fieldName", parent, "parent name"); + FAIL() << "Should not have found double, but got " << value; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + } +} + +TEST(ConduitUtil, getRequiredDouble_wrongType) +{ + conduit::Node parent; + parent["fieldName"] = "field value"; + try + { + auto value = getRequiredDouble("fieldName", parent, "parent name"); + FAIL() << "Should not have found double, but got " << value; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + EXPECT_THAT(expected.what(), HasSubstr("double")); + } +} + +TEST(ConduitUtil, getOptionalString_valid) +{ + conduit::Node parent; + parent["fieldName"] = "the value"; + EXPECT_EQ("the value", getOptionalString("fieldName", parent, "parent name")); +} -TEST(ConduitUtil, toStringVector_NotListOfStrings) { - conduit::Node notList = parseJsonValue(R"([1, 2, "a string"])"); - try { - toStringVector(notList, "someName"); - FAIL() << "Should have thrown an exception."; - } catch (std::invalid_argument const &ex) { - EXPECT_THAT(ex.what(), HasSubstr("someName")); - } +TEST(ConduitUtil, getOptionalString_missing) +{ + conduit::Node parent; + EXPECT_EQ("", getOptionalString("fieldName", parent, "parent name")); +} + +TEST(ConduitUtil, getOptionalString_explicitNullValue) +{ + conduit::Node parent; + parent["fieldName"]; + EXPECT_EQ("", getOptionalString("fieldName", parent, "parent name")); +} + +TEST(ConduitUtil, getOptionalString_wrongType) +{ + conduit::Node parent; + parent["fieldName"] = 123; + try + { + auto value = getOptionalString("fieldName", parent, "parent name"); + FAIL() << "Should not have found string, but got " << value; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr("fieldName")); + EXPECT_THAT(expected.what(), HasSubstr("parent name")); + EXPECT_THAT(expected.what(), HasSubstr("string")); + } +} + +TEST(ConduitUtil, getOptionalField_slashes) +{ + conduit::Node parent; + parent["some/name"] = "undesired value"; + EXPECT_EQ("", getOptionalString("some/name", parent, "parent name")); +} + +TEST(ConduitUtil, toDoubleVector_empty) +{ + conduit::Node emptyList = parseJsonValue("[]"); + EXPECT_EQ(std::vector {}, toDoubleVector(emptyList, "testNode")); +} + +TEST(ConduitUtil, toDoubleVector_validValues) +{ + conduit::Node nonEmptyList = parseJsonValue("[1, 2.0, 3, 4]"); + EXPECT_THAT(toDoubleVector(nonEmptyList, "testNode"), + ElementsAre(1.0, 2.0, 3.0, 4.0)); +} + +TEST(ConduitUtil, toDoubleVector_NotList) +{ + conduit::Node notList = parseJsonValue("\"this is not a list of doubles\""); + try + { + toDoubleVector(notList, "someName"); + FAIL() << "Should have thrown an exception"; + } + catch(std::invalid_argument const &ex) + { + EXPECT_THAT(ex.what(), HasSubstr("someName")); + } +} + +TEST(ConduitUtil, toStringVector_empty) +{ + conduit::Node emptyList = parseJsonValue("[]"); + EXPECT_THAT(toStringVector(emptyList, "testNode"), + ContainerEq(std::vector {})); +} + +TEST(ConduitUtil, toStringVector_validValues) +{ + conduit::Node nonEmptyList = parseJsonValue(R"(["s1", "s2", "s3"])"); + EXPECT_THAT(toStringVector(nonEmptyList, "testNode"), + ElementsAre("s1", "s2", "s3")); +} + +TEST(ConduitUtil, toStringVector_NotList) +{ + conduit::Node notList = parseJsonValue("\"this is not a list of doubles\""); + try + { + toStringVector(notList, "someName"); + FAIL() << "Should have thrown an exception."; + } + catch(std::invalid_argument const &ex) + { + EXPECT_THAT(ex.what(), HasSubstr("someName")); + } +} + +TEST(ConduitUtil, toStringVector_NotListOfStrings) +{ + conduit::Node notList = parseJsonValue(R"([1, 2, "a string"])"); + try + { + toStringVector(notList, "someName"); + FAIL() << "Should have thrown an exception."; + } + catch(std::invalid_argument const &ex) + { + EXPECT_THAT(ex.what(), HasSubstr("someName")); + } } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_Curve.cpp b/src/axom/sina/tests/sina_Curve.cpp index c6ddc9e97b..00fec7cad5 100644 --- a/src/axom/sina/tests/sina_Curve.cpp +++ b/src/axom/sina/tests/sina_Curve.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "gtest/gtest.h" #include "gmock/gmock.h" @@ -20,98 +19,107 @@ namespace testing namespace { -using ::testing::ElementsAre; using ::testing::ContainerEq; +using ::testing::ElementsAre; -TEST(Curve, createFromVector) { - std::vector values{1, 2, 3, 4, 5, 6}; - Curve const curve{"theName", values}; - EXPECT_EQ("theName", curve.getName()); - EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); - EXPECT_EQ("", curve.getUnits()); - EXPECT_THAT(curve.getTags(), ElementsAre()); +TEST(Curve, createFromVector) +{ + std::vector values {1, 2, 3, 4, 5, 6}; + Curve const curve {"theName", values}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); + EXPECT_EQ("", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre()); } -TEST(Curve, createFromPointer) { - double values[]{1, 2, 3, 4, 5, 6}; - Curve const curve{"theName", values, sizeof(values) / sizeof(double)}; - EXPECT_EQ("theName", curve.getName()); - EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); - EXPECT_EQ("", curve.getUnits()); - EXPECT_THAT(curve.getTags(), ElementsAre()); +TEST(Curve, createFromPointer) +{ + double values[] {1, 2, 3, 4, 5, 6}; + Curve const curve {"theName", values, sizeof(values) / sizeof(double)}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); + EXPECT_EQ("", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre()); } -TEST(Curve, createFromInitializerList) { - Curve const curve{"theName", {1, 2, 3, 4, 5, 6}}; - EXPECT_EQ("theName", curve.getName()); - EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); - EXPECT_EQ("", curve.getUnits()); - EXPECT_THAT(curve.getTags(), ElementsAre()); +TEST(Curve, createFromInitializerList) +{ + Curve const curve {"theName", {1, 2, 3, 4, 5, 6}}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3, 4, 5, 6)); + EXPECT_EQ("", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre()); } -TEST(Curve, setUnits) { - Curve curve{"theName", {1, 2, 3}}; - EXPECT_EQ("", curve.getUnits()); - curve.setUnits("cm"); - EXPECT_EQ("cm", curve.getUnits()); +TEST(Curve, setUnits) +{ + Curve curve {"theName", {1, 2, 3}}; + EXPECT_EQ("", curve.getUnits()); + curve.setUnits("cm"); + EXPECT_EQ("cm", curve.getUnits()); } -TEST(Curve, setTags) { - Curve curve{"theName", {1, 2, 3}}; - EXPECT_THAT(curve.getTags(), ElementsAre()); - curve.setTags({"t1", "t2", "t3"}); - EXPECT_THAT(curve.getTags(), ElementsAre("t1", "t2", "t3")); +TEST(Curve, setTags) +{ + Curve curve {"theName", {1, 2, 3}}; + EXPECT_THAT(curve.getTags(), ElementsAre()); + curve.setTags({"t1", "t2", "t3"}); + EXPECT_THAT(curve.getTags(), ElementsAre("t1", "t2", "t3")); } -TEST(Curve, createFromNode_requiredOnly) { - conduit::Node curveAsNode = parseJsonValue(R"( +TEST(Curve, createFromNode_requiredOnly) +{ + conduit::Node curveAsNode = parseJsonValue(R"( { "value": [1.0, 2.0, 3.0] } )"); - Curve curve{"theName", curveAsNode}; - EXPECT_EQ("theName", curve.getName()); - EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3)); - EXPECT_EQ("", curve.getUnits()); - EXPECT_THAT(curve.getTags(), ElementsAre()); + Curve curve {"theName", curveAsNode}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3)); + EXPECT_EQ("", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre()); } -TEST(Curve, createFromNode_optionalFields) { - conduit::Node curveAsNode = parseJsonValue(R"( +TEST(Curve, createFromNode_optionalFields) +{ + conduit::Node curveAsNode = parseJsonValue(R"( { "value": [1.0, 2.0, 3.0], "units": "cm", "tags": ["t1", "t2", "t3"] } )"); - Curve curve{"theName", curveAsNode}; - EXPECT_EQ("theName", curve.getName()); - EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3)); - EXPECT_EQ("cm", curve.getUnits()); - EXPECT_THAT(curve.getTags(), ElementsAre("t1", "t2", "t3")); + Curve curve {"theName", curveAsNode}; + EXPECT_EQ("theName", curve.getName()); + EXPECT_THAT(curve.getValues(), ElementsAre(1, 2, 3)); + EXPECT_EQ("cm", curve.getUnits()); + EXPECT_THAT(curve.getTags(), ElementsAre("t1", "t2", "t3")); } -TEST(Curve, toNode_requiredOnly) { - Curve const curve{"theName", {1, 2, 3, 4}}; - auto expected = (R"({ +TEST(Curve, toNode_requiredOnly) +{ + Curve const curve {"theName", {1, 2, 3, 4}}; + auto expected = (R"({ "value": [1.0, 2.0, 3.0, 4.0] })"); - EXPECT_THAT(curve.toNode(), MatchesJson(expected)); + EXPECT_THAT(curve.toNode(), MatchesJson(expected)); } -TEST(Curve, toNode_optionalFields) { - Curve curve{"theName", {1, 2, 3, 4}}; - curve.setUnits("cm"); - curve.setTags({"t1", "t2", "t3"}); - auto expected = R"({ +TEST(Curve, toNode_optionalFields) +{ + Curve curve {"theName", {1, 2, 3, 4}}; + curve.setUnits("cm"); + curve.setTags({"t1", "t2", "t3"}); + auto expected = R"({ "value": [1.0, 2.0, 3.0, 4.0], "units": "cm", "tags": ["t1", "t2", "t3"] })"; - EXPECT_THAT(curve.toNode(), MatchesJson(expected)); + EXPECT_THAT(curve.toNode(), MatchesJson(expected)); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_CurveSet.cpp b/src/axom/sina/tests/sina_CurveSet.cpp index e4dd4d8d86..6f90392a5e 100644 --- a/src/axom/sina/tests/sina_CurveSet.cpp +++ b/src/axom/sina/tests/sina_CurveSet.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "gtest/gtest.h" #include "gmock/gmock.h" @@ -36,12 +35,11 @@ bool operator==(Curve const &lhs, Curve const &rhs); * @param rhs the right-hand-side operand * @return whether the curves are equal */ -bool operator==(Curve const &lhs, Curve const &rhs) { - bool r = lhs.getName() == rhs.getName() - && lhs.getUnits() == rhs.getUnits() - && lhs.getTags() == rhs.getTags() - && lhs.getValues() == rhs.getValues(); - return r; +bool operator==(Curve const &lhs, Curve const &rhs) +{ + bool r = lhs.getName() == rhs.getName() && lhs.getUnits() == rhs.getUnits() && + lhs.getTags() == rhs.getTags() && lhs.getValues() == rhs.getValues(); + return r; } namespace testing @@ -52,95 +50,103 @@ namespace using ::testing::ContainerEq; using ::testing::ElementsAre; -TEST(CurveSet, initialState) { - CurveSet const cs{"theName"}; - ASSERT_EQ("theName", cs.getName()); - ASSERT_TRUE(cs.getIndependentCurves().empty()); - ASSERT_TRUE(cs.getDependentCurves().empty()); - ASSERT_NE(&cs.getIndependentCurves(), &cs.getDependentCurves()); +TEST(CurveSet, initialState) +{ + CurveSet const cs {"theName"}; + ASSERT_EQ("theName", cs.getName()); + ASSERT_TRUE(cs.getIndependentCurves().empty()); + ASSERT_TRUE(cs.getDependentCurves().empty()); + ASSERT_NE(&cs.getIndependentCurves(), &cs.getDependentCurves()); } -TEST(CurveSet, addIndependentCurves) { - CurveSet cs{"testSet"}; - std::unordered_map expectedCurves; - - Curve i1{"i1", {1, 2, 3}}; - cs.addIndependentCurve(i1); - expectedCurves.insert(std::make_pair(i1.getName(), i1)); - EXPECT_THAT(cs.getIndependentCurves(), ContainerEq(expectedCurves)); - - Curve i2{"i2", {4, 5, 6}}; - cs.addIndependentCurve(i2); - expectedCurves.insert(std::make_pair(i2.getName(), i2)); - EXPECT_THAT(cs.getIndependentCurves(), ContainerEq(expectedCurves)); +TEST(CurveSet, addIndependentCurves) +{ + CurveSet cs {"testSet"}; + std::unordered_map expectedCurves; + + Curve i1 {"i1", {1, 2, 3}}; + cs.addIndependentCurve(i1); + expectedCurves.insert(std::make_pair(i1.getName(), i1)); + EXPECT_THAT(cs.getIndependentCurves(), ContainerEq(expectedCurves)); + + Curve i2 {"i2", {4, 5, 6}}; + cs.addIndependentCurve(i2); + expectedCurves.insert(std::make_pair(i2.getName(), i2)); + EXPECT_THAT(cs.getIndependentCurves(), ContainerEq(expectedCurves)); } -TEST(CurveSet, addIndependentCurves_replaceExisting) { - CurveSet cs{"testSet"}; +TEST(CurveSet, addIndependentCurves_replaceExisting) +{ + CurveSet cs {"testSet"}; - Curve i1{"theName", {1, 2, 3}}; - cs.addIndependentCurve(i1); - EXPECT_THAT(cs.getIndependentCurves().at("theName").getValues(), - ElementsAre(1, 2, 3)); + Curve i1 {"theName", {1, 2, 3}}; + cs.addIndependentCurve(i1); + EXPECT_THAT(cs.getIndependentCurves().at("theName").getValues(), + ElementsAre(1, 2, 3)); - Curve i2{"theName", {4, 5, 6}}; - cs.addIndependentCurve(i2); - EXPECT_THAT(cs.getIndependentCurves().at("theName").getValues(), - ElementsAre(4, 5, 6)); + Curve i2 {"theName", {4, 5, 6}}; + cs.addIndependentCurve(i2); + EXPECT_THAT(cs.getIndependentCurves().at("theName").getValues(), + ElementsAre(4, 5, 6)); } -TEST(CurveSet, addDpendentCurves) { - CurveSet cs{"testSet"}; - std::unordered_map expectedCurves; - - Curve i1{"i1", {1, 2, 3}}; - cs.addDependentCurve(i1); - expectedCurves.insert(std::make_pair(i1.getName(), i1)); - EXPECT_THAT(cs.getDependentCurves(), ContainerEq(expectedCurves)); - - Curve i2{"i2", {4, 5, 6}}; - cs.addDependentCurve(i2); - expectedCurves.insert(std::make_pair(i2.getName(), i2)); - EXPECT_THAT(cs.getDependentCurves(), ContainerEq(expectedCurves)); +TEST(CurveSet, addDpendentCurves) +{ + CurveSet cs {"testSet"}; + std::unordered_map expectedCurves; + + Curve i1 {"i1", {1, 2, 3}}; + cs.addDependentCurve(i1); + expectedCurves.insert(std::make_pair(i1.getName(), i1)); + EXPECT_THAT(cs.getDependentCurves(), ContainerEq(expectedCurves)); + + Curve i2 {"i2", {4, 5, 6}}; + cs.addDependentCurve(i2); + expectedCurves.insert(std::make_pair(i2.getName(), i2)); + EXPECT_THAT(cs.getDependentCurves(), ContainerEq(expectedCurves)); } -TEST(CurveSet, addDependentCurves_replaceExisting) { - CurveSet cs{"testSet"}; +TEST(CurveSet, addDependentCurves_replaceExisting) +{ + CurveSet cs {"testSet"}; - Curve d1{"theName", {1, 2, 3}}; - cs.addDependentCurve(d1); - EXPECT_THAT(cs.getDependentCurves().at("theName").getValues(), - ElementsAre(1, 2, 3)); + Curve d1 {"theName", {1, 2, 3}}; + cs.addDependentCurve(d1); + EXPECT_THAT(cs.getDependentCurves().at("theName").getValues(), + ElementsAre(1, 2, 3)); - Curve d2{"theName", {4, 5, 6}}; - cs.addDependentCurve(d2); - EXPECT_THAT(cs.getDependentCurves().at("theName").getValues(), - ElementsAre(4, 5, 6)); + Curve d2 {"theName", {4, 5, 6}}; + cs.addDependentCurve(d2); + EXPECT_THAT(cs.getDependentCurves().at("theName").getValues(), + ElementsAre(4, 5, 6)); } -TEST(CurveSet, createFromNode_empty) { - conduit::Node curveSetAsNode = parseJsonValue(R"({})"); - CurveSet curveSet{"theName", curveSetAsNode}; - EXPECT_EQ("theName", curveSet.getName()); - std::unordered_map emptyMap; - EXPECT_THAT(curveSet.getDependentCurves(), ContainerEq(emptyMap)); - EXPECT_THAT(curveSet.getIndependentCurves(), ContainerEq(emptyMap)); +TEST(CurveSet, createFromNode_empty) +{ + conduit::Node curveSetAsNode = parseJsonValue(R"({})"); + CurveSet curveSet {"theName", curveSetAsNode}; + EXPECT_EQ("theName", curveSet.getName()); + std::unordered_map emptyMap; + EXPECT_THAT(curveSet.getDependentCurves(), ContainerEq(emptyMap)); + EXPECT_THAT(curveSet.getIndependentCurves(), ContainerEq(emptyMap)); } -TEST(CurveSet, createFromNode_emptySets) { - conduit::Node curveSetAsNode = parseJsonValue(R"({ +TEST(CurveSet, createFromNode_emptySets) +{ + conduit::Node curveSetAsNode = parseJsonValue(R"({ "dependent": {}, "independent": {} })"); - CurveSet curveSet{"theName", curveSetAsNode}; - EXPECT_EQ("theName", curveSet.getName()); - std::unordered_map emptyMap; - EXPECT_THAT(curveSet.getDependentCurves(), ContainerEq(emptyMap)); - EXPECT_THAT(curveSet.getIndependentCurves(), ContainerEq(emptyMap)); + CurveSet curveSet {"theName", curveSetAsNode}; + EXPECT_EQ("theName", curveSet.getName()); + std::unordered_map emptyMap; + EXPECT_THAT(curveSet.getDependentCurves(), ContainerEq(emptyMap)); + EXPECT_THAT(curveSet.getIndependentCurves(), ContainerEq(emptyMap)); } -TEST(CurveSet, createFromNode_curveSetsDefined) { - conduit::Node curveSetAsNode = parseJsonValue(R"({ +TEST(CurveSet, createFromNode_curveSetsDefined) +{ + conduit::Node curveSetAsNode = parseJsonValue(R"({ "independent": { "indep1": { "value": [10, 20, 30]}, "indep2/with/slash": { "value": [40, 50, 60]} @@ -150,40 +156,40 @@ TEST(CurveSet, createFromNode_curveSetsDefined) { "dep2/with/slash": { "value": [4, 5, 6]} } })"); - CurveSet curveSet{"theName", curveSetAsNode}; - EXPECT_EQ("theName", curveSet.getName()); - - std::unordered_map expectedDependents { - {"dep1", Curve{"dep1", {1, 2, 3}}}, - {"dep2/with/slash", Curve{"dep2/with/slash", {4, 5, 6}}}, - }; - EXPECT_THAT(curveSet.getDependentCurves(), - ContainerEq(expectedDependents)); - - std::unordered_map expectedIndependents { - {"indep1", Curve{"indep1", {10, 20, 30}}}, - {"indep2/with/slash", Curve{"indep2/with/slash", {40, 50, 60}}}, - }; - EXPECT_THAT(curveSet.getIndependentCurves(), - ContainerEq(expectedIndependents)); + CurveSet curveSet {"theName", curveSetAsNode}; + EXPECT_EQ("theName", curveSet.getName()); + + std::unordered_map expectedDependents { + {"dep1", Curve {"dep1", {1, 2, 3}}}, + {"dep2/with/slash", Curve {"dep2/with/slash", {4, 5, 6}}}, + }; + EXPECT_THAT(curveSet.getDependentCurves(), ContainerEq(expectedDependents)); + + std::unordered_map expectedIndependents { + {"indep1", Curve {"indep1", {10, 20, 30}}}, + {"indep2/with/slash", Curve {"indep2/with/slash", {40, 50, 60}}}, + }; + EXPECT_THAT(curveSet.getIndependentCurves(), ContainerEq(expectedIndependents)); } -TEST(CurveSet, toNode_empty) { - CurveSet curveSet{"theName"}; - auto expected = R"({ +TEST(CurveSet, toNode_empty) +{ + CurveSet curveSet {"theName"}; + auto expected = R"({ "independent": {}, "dependent": {} })"; - EXPECT_THAT(curveSet.toNode(), MatchesJson(expected)); + EXPECT_THAT(curveSet.toNode(), MatchesJson(expected)); } -TEST(CurveSet, toNode_withCurves) { - CurveSet curveSet{"theName"}; - curveSet.addIndependentCurve(Curve{"i1", {1, 2, 3}}); - curveSet.addIndependentCurve(Curve{"i2/with/slash", {4, 5, 6}}); - curveSet.addDependentCurve(Curve{"d1", {10, 20, 30}}); - curveSet.addDependentCurve(Curve{"d2/with/slash", {40, 50, 60}}); - auto expected = R"({ +TEST(CurveSet, toNode_withCurves) +{ + CurveSet curveSet {"theName"}; + curveSet.addIndependentCurve(Curve {"i1", {1, 2, 3}}); + curveSet.addIndependentCurve(Curve {"i2/with/slash", {4, 5, 6}}); + curveSet.addDependentCurve(Curve {"d1", {10, 20, 30}}); + curveSet.addDependentCurve(Curve {"d2/with/slash", {40, 50, 60}}); + auto expected = R"({ "independent": { "i1": { "value": [1.0, 2.0, 3.0] @@ -201,10 +207,10 @@ TEST(CurveSet, toNode_withCurves) { } } })"; - EXPECT_THAT(curveSet.toNode(), MatchesJson(expected)); + EXPECT_THAT(curveSet.toNode(), MatchesJson(expected)); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_DataHolder.cpp b/src/axom/sina/tests/sina_DataHolder.cpp index ff57df3f16..bd389bc524 100644 --- a/src/axom/sina/tests/sina_DataHolder.cpp +++ b/src/axom/sina/tests/sina_DataHolder.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include @@ -24,10 +23,10 @@ namespace { using ::testing::Contains; +using ::testing::DoubleEq; using ::testing::ElementsAre; -using ::testing::Key; using ::testing::HasSubstr; -using ::testing::DoubleEq; +using ::testing::Key; using ::testing::Not; char const EXPECTED_DATA_KEY[] = "data"; @@ -35,130 +34,140 @@ char const EXPECTED_CURVE_SETS_KEY[] = "curve_sets"; char const EXPECTED_LIBRARY_DATA_KEY[] = "library_data"; char const EXPECTED_USER_DEFINED_KEY[] = "user_defined"; -TEST(DataHolder, add_data_existing_key) { - DataHolder dh{}; - dh.add("key1", Datum{"val1"}); - EXPECT_EQ("val1", dh.getData().at("key1").getValue()); - dh.add("key1", Datum{"val2"}); - EXPECT_EQ("val2", dh.getData().at("key1").getValue()); +TEST(DataHolder, add_data_existing_key) +{ + DataHolder dh {}; + dh.add("key1", Datum {"val1"}); + EXPECT_EQ("val1", dh.getData().at("key1").getValue()); + dh.add("key1", Datum {"val2"}); + EXPECT_EQ("val2", dh.getData().at("key1").getValue()); } -TEST(DataHolder, add_curve_set_existing_key) { - DataHolder dh{}; - CurveSet cs1{"cs1"}; - cs1.addDependentCurve(Curve{"original", {1, 2, 3}}); - dh.add(cs1); - - auto &csAfterFirstInsert = dh.getCurveSets(); - ASSERT_THAT(csAfterFirstInsert, Contains(Key("cs1"))); - EXPECT_THAT(csAfterFirstInsert.at("cs1").getDependentCurves(), - Contains(Key("original"))); - - CurveSet cs2{"cs1"}; - cs2.addDependentCurve(Curve{"new", {1, 2, 3}}); - dh.add(cs2); - - auto &csAfterSecondInsert = dh.getCurveSets(); - ASSERT_THAT(csAfterSecondInsert, Contains(Key("cs1"))); - EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), - Not(Contains(Key("original")))); - EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), - Contains(Key("new"))); +TEST(DataHolder, add_curve_set_existing_key) +{ + DataHolder dh {}; + CurveSet cs1 {"cs1"}; + cs1.addDependentCurve(Curve {"original", {1, 2, 3}}); + dh.add(cs1); + + auto &csAfterFirstInsert = dh.getCurveSets(); + ASSERT_THAT(csAfterFirstInsert, Contains(Key("cs1"))); + EXPECT_THAT(csAfterFirstInsert.at("cs1").getDependentCurves(), + Contains(Key("original"))); + + CurveSet cs2 {"cs1"}; + cs2.addDependentCurve(Curve {"new", {1, 2, 3}}); + dh.add(cs2); + + auto &csAfterSecondInsert = dh.getCurveSets(); + ASSERT_THAT(csAfterSecondInsert, Contains(Key("cs1"))); + EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), + Not(Contains(Key("original")))); + EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), + Contains(Key("new"))); } -TEST(DataHolder, create_fromNode_userDefined) { - conduit::Node originalNode; - originalNode[EXPECTED_USER_DEFINED_KEY]["k1"] = "v1"; - originalNode[EXPECTED_USER_DEFINED_KEY]["k2"] = 123; - std::vector k3_vals{1, 2, 3}; - originalNode[EXPECTED_USER_DEFINED_KEY]["k3"] = k3_vals; - - DataHolder holder{originalNode}; - auto const &userDefined = holder.getUserDefinedContent(); - EXPECT_EQ("v1", userDefined["k1"].as_string()); - EXPECT_EQ(123, userDefined["k2"].as_int()); - auto int_array = userDefined["k3"].as_int_ptr(); - std::vectorudef_ints(int_array, int_array+userDefined["k3"].dtype().number_of_elements()); - EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); +TEST(DataHolder, create_fromNode_userDefined) +{ + conduit::Node originalNode; + originalNode[EXPECTED_USER_DEFINED_KEY]["k1"] = "v1"; + originalNode[EXPECTED_USER_DEFINED_KEY]["k2"] = 123; + std::vector k3_vals {1, 2, 3}; + originalNode[EXPECTED_USER_DEFINED_KEY]["k3"] = k3_vals; + + DataHolder holder {originalNode}; + auto const &userDefined = holder.getUserDefinedContent(); + EXPECT_EQ("v1", userDefined["k1"].as_string()); + EXPECT_EQ(123, userDefined["k2"].as_int()); + auto int_array = userDefined["k3"].as_int_ptr(); + std::vector udef_ints( + int_array, + int_array + userDefined["k3"].dtype().number_of_elements()); + EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); } -TEST(DataHolder, create_fromNode_userDefined_not_object) { - conduit::Node originalNode; - originalNode[EXPECTED_USER_DEFINED_KEY] = "not an object"; - EXPECT_THROW(DataHolder{originalNode}, std::invalid_argument); +TEST(DataHolder, create_fromNode_userDefined_not_object) +{ + conduit::Node originalNode; + originalNode[EXPECTED_USER_DEFINED_KEY] = "not an object"; + EXPECT_THROW(DataHolder {originalNode}, std::invalid_argument); } -TEST(DataHolder, getUserDefined_initialConst) { - DataHolder const holder; - conduit::Node const &userDefined = holder.getUserDefinedContent(); - EXPECT_TRUE(userDefined.dtype().is_empty()); +TEST(DataHolder, getUserDefined_initialConst) +{ + DataHolder const holder; + conduit::Node const &userDefined = holder.getUserDefinedContent(); + EXPECT_TRUE(userDefined.dtype().is_empty()); } -TEST(DataHolder, getUserDefined_initialNonConst) { - DataHolder holder; - conduit::Node &initialUserDefined = holder.getUserDefinedContent(); - EXPECT_TRUE(initialUserDefined.dtype().is_empty()); - initialUserDefined["foo"] = 123; - EXPECT_EQ(123, holder.getUserDefinedContent()["foo"].as_int()); +TEST(DataHolder, getUserDefined_initialNonConst) +{ + DataHolder holder; + conduit::Node &initialUserDefined = holder.getUserDefinedContent(); + EXPECT_TRUE(initialUserDefined.dtype().is_empty()); + initialUserDefined["foo"] = 123; + EXPECT_EQ(123, holder.getUserDefinedContent()["foo"].as_int()); } -TEST(DataHolder, add_new_library) { - DataHolder dh{}; - auto outer = dh.addLibraryData("outer"); - auto &libDataAfterFirstInsert = dh.getLibraryData(); - ASSERT_THAT(libDataAfterFirstInsert, Contains(Key("outer"))); - dh.addLibraryData("other_outer"); - auto &libDataAfterSecondInsert = dh.getLibraryData(); - ASSERT_THAT(libDataAfterSecondInsert, Contains(Key("outer"))); - ASSERT_THAT(libDataAfterSecondInsert, Contains(Key("other_outer"))); - outer->addLibraryData("inner"); - auto &libDataAfterThirdInsert = dh.getLibraryData(); - ASSERT_THAT(libDataAfterThirdInsert.at("outer")->getLibraryData(), - Contains(Key("inner"))); - ASSERT_THAT(libDataAfterThirdInsert.at("other_outer")->getLibraryData(), - Not(Contains(Key("inner")))); +TEST(DataHolder, add_new_library) +{ + DataHolder dh {}; + auto outer = dh.addLibraryData("outer"); + auto &libDataAfterFirstInsert = dh.getLibraryData(); + ASSERT_THAT(libDataAfterFirstInsert, Contains(Key("outer"))); + dh.addLibraryData("other_outer"); + auto &libDataAfterSecondInsert = dh.getLibraryData(); + ASSERT_THAT(libDataAfterSecondInsert, Contains(Key("outer"))); + ASSERT_THAT(libDataAfterSecondInsert, Contains(Key("other_outer"))); + outer->addLibraryData("inner"); + auto &libDataAfterThirdInsert = dh.getLibraryData(); + ASSERT_THAT(libDataAfterThirdInsert.at("outer")->getLibraryData(), + Contains(Key("inner"))); + ASSERT_THAT(libDataAfterThirdInsert.at("other_outer")->getLibraryData(), + Not(Contains(Key("inner")))); } -TEST(DataHolder, add_library_existing_key) { - std::string libName = "outer"; - DataHolder dh{}; - auto outer = dh.addLibraryData(libName); - outer->add("key1", Datum{"val1"}); - ASSERT_THAT(dh.getLibraryData(libName)->getData(), - Contains(Key("key1"))); - dh.addLibraryData(libName); - ASSERT_THAT(dh.getLibraryData(libName)->getData(), - Not(Contains(Key("key1")))); +TEST(DataHolder, add_library_existing_key) +{ + std::string libName = "outer"; + DataHolder dh {}; + auto outer = dh.addLibraryData(libName); + outer->add("key1", Datum {"val1"}); + ASSERT_THAT(dh.getLibraryData(libName)->getData(), Contains(Key("key1"))); + dh.addLibraryData(libName); + ASSERT_THAT(dh.getLibraryData(libName)->getData(), Not(Contains(Key("key1")))); } -TEST(DataHolder, create_fromNode_data) { - conduit::Node originalNode; - originalNode[EXPECTED_DATA_KEY]; - - std::string name1 = "datum name 1"; - std::string name2 = "datum name 2/with/slash"; - - conduit::Node name1_node; - name1_node["value"] = "value 1"; - originalNode[EXPECTED_DATA_KEY][name1] = name1_node; - conduit::Node name2_node; - name2_node["value"] = 2.22; - name2_node["units"] = "g/L"; - addStringsToNode(name2_node, "tags", {"tag1","tag2"}); - name2_node["value"] = 2.22; - originalNode[EXPECTED_DATA_KEY].add_child(name2) = name2_node; - DataHolder dh{originalNode}; - auto &data = dh.getData(); - ASSERT_EQ(2u, data.size()); - EXPECT_EQ("value 1", data.at(name1).getValue()); - EXPECT_THAT(2.22, DoubleEq(data.at(name2).getScalar())); - EXPECT_EQ("g/L", data.at(name2).getUnits()); - EXPECT_EQ("tag1", data.at(name2).getTags()[0]); - EXPECT_EQ("tag2", data.at(name2).getTags()[1]); +TEST(DataHolder, create_fromNode_data) +{ + conduit::Node originalNode; + originalNode[EXPECTED_DATA_KEY]; + + std::string name1 = "datum name 1"; + std::string name2 = "datum name 2/with/slash"; + + conduit::Node name1_node; + name1_node["value"] = "value 1"; + originalNode[EXPECTED_DATA_KEY][name1] = name1_node; + conduit::Node name2_node; + name2_node["value"] = 2.22; + name2_node["units"] = "g/L"; + addStringsToNode(name2_node, "tags", {"tag1", "tag2"}); + name2_node["value"] = 2.22; + originalNode[EXPECTED_DATA_KEY].add_child(name2) = name2_node; + DataHolder dh {originalNode}; + auto &data = dh.getData(); + ASSERT_EQ(2u, data.size()); + EXPECT_EQ("value 1", data.at(name1).getValue()); + EXPECT_THAT(2.22, DoubleEq(data.at(name2).getScalar())); + EXPECT_EQ("g/L", data.at(name2).getUnits()); + EXPECT_EQ("tag1", data.at(name2).getTags()[0]); + EXPECT_EQ("tag2", data.at(name2).getTags()[1]); } -TEST(DataHolder, create_fromNode_curveSets) { - conduit::Node dataHolderAsNode = parseJsonValue(R"({ +TEST(DataHolder, create_fromNode_curveSets) +{ + conduit::Node dataHolderAsNode = parseJsonValue(R"({ "curve_sets": { "cs1": { "independent": { @@ -170,13 +179,14 @@ TEST(DataHolder, create_fromNode_curveSets) { } } })"); - DataHolder dh{dataHolderAsNode}; - auto &curveSets = dh.getCurveSets(); - ASSERT_THAT(curveSets, Contains(Key("cs1"))); + DataHolder dh {dataHolderAsNode}; + auto &curveSets = dh.getCurveSets(); + ASSERT_THAT(curveSets, Contains(Key("cs1"))); } -TEST(DataHolder, create_fromNode_libraryData) { - conduit::Node dataHolderAsNode = parseJsonValue(R"({ +TEST(DataHolder, create_fromNode_libraryData) +{ + conduit::Node dataHolderAsNode = parseJsonValue(R"({ "library_data": { "outer_lib": { "library_data": { @@ -185,64 +195,69 @@ TEST(DataHolder, create_fromNode_libraryData) { } } })"); - DataHolder dh{dataHolderAsNode}; - auto &fullLibData = dh.getLibraryData(); - ASSERT_THAT(fullLibData, Contains(Key("outer_lib"))); - auto outerLibData = fullLibData.at("outer_lib")->getLibraryData(); - ASSERT_THAT(outerLibData, Contains(Key("inner_lib"))); - auto &innerData = outerLibData.at("inner_lib")->getData(); - EXPECT_EQ("good morning!", innerData.at("i2").getValue()); + DataHolder dh {dataHolderAsNode}; + auto &fullLibData = dh.getLibraryData(); + ASSERT_THAT(fullLibData, Contains(Key("outer_lib"))); + auto outerLibData = fullLibData.at("outer_lib")->getLibraryData(); + ASSERT_THAT(outerLibData, Contains(Key("inner_lib"))); + auto &innerData = outerLibData.at("inner_lib")->getData(); + EXPECT_EQ("good morning!", innerData.at("i2").getValue()); } -TEST(DataHolder, toNode_default_values) { - DataHolder dh{}; - auto asNode = dh.toNode(); - EXPECT_TRUE(asNode.dtype().is_object()); - // We want to be sure that unset optional fields aren't present - EXPECT_FALSE(asNode.has_child(EXPECTED_DATA_KEY)); - EXPECT_FALSE(asNode.has_child(EXPECTED_CURVE_SETS_KEY)); - EXPECT_FALSE(asNode.has_child(EXPECTED_LIBRARY_DATA_KEY)); +TEST(DataHolder, toNode_default_values) +{ + DataHolder dh {}; + auto asNode = dh.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + // We want to be sure that unset optional fields aren't present + EXPECT_FALSE(asNode.has_child(EXPECTED_DATA_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_CURVE_SETS_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_LIBRARY_DATA_KEY)); } -TEST(DataHolder, toNode_data) { - DataHolder dh{}; - std::string name1 = "name1"; - std::string value1 = "value1"; - Datum datum1 = Datum{value1}; - datum1.setUnits("some units"); - datum1.setTags({"tag1"}); - dh.add(name1, datum1); - std::string name2 = "name2"; - dh.add(name2, Datum{2.}); - auto asNode = dh.toNode(); - ASSERT_EQ(2u, asNode[EXPECTED_DATA_KEY].number_of_children()); - EXPECT_EQ("value1", asNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); - EXPECT_EQ("some units", asNode[EXPECTED_DATA_KEY][name1]["units"].as_string()); - EXPECT_EQ("tag1", asNode[EXPECTED_DATA_KEY][name1]["tags"][0].as_string()); - - EXPECT_THAT(asNode[EXPECTED_DATA_KEY][name2]["value"].as_double(), - DoubleEq(2.)); - EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["units"].dtype().is_empty()); - EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["tags"].dtype().is_empty()); +TEST(DataHolder, toNode_data) +{ + DataHolder dh {}; + std::string name1 = "name1"; + std::string value1 = "value1"; + Datum datum1 = Datum {value1}; + datum1.setUnits("some units"); + datum1.setTags({"tag1"}); + dh.add(name1, datum1); + std::string name2 = "name2"; + dh.add(name2, Datum {2.}); + auto asNode = dh.toNode(); + ASSERT_EQ(2u, asNode[EXPECTED_DATA_KEY].number_of_children()); + EXPECT_EQ("value1", asNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); + EXPECT_EQ("some units", asNode[EXPECTED_DATA_KEY][name1]["units"].as_string()); + EXPECT_EQ("tag1", asNode[EXPECTED_DATA_KEY][name1]["tags"][0].as_string()); + + EXPECT_THAT(asNode[EXPECTED_DATA_KEY][name2]["value"].as_double(), + DoubleEq(2.)); + EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["units"].dtype().is_empty()); + EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["tags"].dtype().is_empty()); } -TEST(DataHolder, toNode_dataWithSlashes) { - DataHolder dh{}; - std::string name = "name/with/slashes"; - std::string value = "the value"; - Datum datum = Datum{value}; - dh.add(name, datum); - auto asNode = dh.toNode(); - ASSERT_EQ(1u, asNode[EXPECTED_DATA_KEY].number_of_children()); - EXPECT_EQ("the value", asNode[EXPECTED_DATA_KEY].child(name)["value"].as_string()); +TEST(DataHolder, toNode_dataWithSlashes) +{ + DataHolder dh {}; + std::string name = "name/with/slashes"; + std::string value = "the value"; + Datum datum = Datum {value}; + dh.add(name, datum); + auto asNode = dh.toNode(); + ASSERT_EQ(1u, asNode[EXPECTED_DATA_KEY].number_of_children()); + EXPECT_EQ("the value", + asNode[EXPECTED_DATA_KEY].child(name)["value"].as_string()); } -TEST(DataHolder, toNode_curveSets) { - DataHolder dh{}; - CurveSet cs{"myCurveSet/with/slash"}; - cs.addIndependentCurve(Curve{"myCurve", {1, 2, 3}}); - dh.add(cs); - auto expected = R"({ +TEST(DataHolder, toNode_curveSets) +{ + DataHolder dh {}; + CurveSet cs {"myCurveSet/with/slash"}; + cs.addIndependentCurve(Curve {"myCurve", {1, 2, 3}}); + dh.add(cs); + auto expected = R"({ "curve_sets": { "myCurveSet/with/slash": { "independent": { @@ -254,16 +269,17 @@ TEST(DataHolder, toNode_curveSets) { } } })"; - EXPECT_THAT(dh.toNode(), MatchesJson(expected)); + EXPECT_THAT(dh.toNode(), MatchesJson(expected)); } -TEST(DataHolder, toNode_libraryData) { - DataHolder dh{}; - auto outer = dh.addLibraryData("outer"); - outer->add("scal", Datum{"goodbye!"}); - auto inner = outer->addLibraryData("inner"); - inner->add("str", Datum{"hello!"}); - auto expected = R"({ +TEST(DataHolder, toNode_libraryData) +{ + DataHolder dh {}; + auto outer = dh.addLibraryData("outer"); + outer->add("scal", Datum {"goodbye!"}); + auto inner = outer->addLibraryData("inner"); + inner->add("str", Datum {"hello!"}); + auto expected = R"({ "library_data": { "outer": { "library_data": { @@ -275,29 +291,32 @@ TEST(DataHolder, toNode_libraryData) { } } })"; - EXPECT_THAT(dh.toNode(), MatchesJson(expected)); + EXPECT_THAT(dh.toNode(), MatchesJson(expected)); } -TEST(DataHolder, toNode_userDefined) { - DataHolder holder; - conduit::Node userDef; - userDef["k1"] = "v1"; - userDef["k2"] = 123; - std::vector int_vals{1, 2, 3}; - userDef["k3"] = int_vals; - holder.setUserDefinedContent(userDef); - - auto asNode = holder.toNode(); - - auto userDefined = asNode[EXPECTED_USER_DEFINED_KEY]; - EXPECT_EQ("v1", userDefined["k1"].as_string()); - EXPECT_EQ(123, userDefined["k2"].as_int()); - auto int_array = userDefined["k3"].as_int_ptr(); - std::vectorudef_ints(int_array, int_array+userDefined["k3"].dtype().number_of_elements()); - EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); +TEST(DataHolder, toNode_userDefined) +{ + DataHolder holder; + conduit::Node userDef; + userDef["k1"] = "v1"; + userDef["k2"] = 123; + std::vector int_vals {1, 2, 3}; + userDef["k3"] = int_vals; + holder.setUserDefinedContent(userDef); + + auto asNode = holder.toNode(); + + auto userDefined = asNode[EXPECTED_USER_DEFINED_KEY]; + EXPECT_EQ("v1", userDefined["k1"].as_string()); + EXPECT_EQ(123, userDefined["k2"].as_int()); + auto int_array = userDefined["k3"].as_int_ptr(); + std::vector udef_ints( + int_array, + int_array + userDefined["k3"].dtype().number_of_elements()); + EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_Datum.cpp b/src/axom/sina/tests/sina_Datum.cpp index c1d2acd81c..5126ca8fe3 100644 --- a/src/axom/sina/tests/sina_Datum.cpp +++ b/src/axom/sina/tests/sina_Datum.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include #include @@ -23,162 +22,177 @@ namespace testing namespace { -using ::testing::HasSubstr; using ::testing::DoubleEq; using ::testing::ElementsAre; +using ::testing::HasSubstr; -TEST(Datum, create) { - std::vector tags = {"tag1", "tag2"}; - std::string value = "value"; - std::vector val_list = {"val1", "val2"}; - std::vector scal_list = {100, 2.0}; - Datum datum1{value}; - datum1.setUnits("some units"); - datum1.setTags(tags); - Datum datum2{3.14}; - Datum datum3{val_list}; - Datum datum4{scal_list}; - - EXPECT_EQ(ValueType::String, datum1.getType()); - EXPECT_EQ("value", datum1.getValue()); - EXPECT_EQ("some units", datum1.getUnits()); - EXPECT_EQ(tags, datum1.getTags()); - - EXPECT_EQ(ValueType::Scalar, datum2.getType()); - EXPECT_THAT(datum2.getScalar(), DoubleEq(3.14)); - - EXPECT_EQ(ValueType::StringArray, datum3.getType()); - EXPECT_EQ(val_list, datum3.getStringArray()); - - EXPECT_EQ(ValueType::ScalarArray, datum4.getType()); - EXPECT_EQ(scal_list, datum4.getScalarArray()); +TEST(Datum, create) +{ + std::vector tags = {"tag1", "tag2"}; + std::string value = "value"; + std::vector val_list = {"val1", "val2"}; + std::vector scal_list = {100, 2.0}; + Datum datum1 {value}; + datum1.setUnits("some units"); + datum1.setTags(tags); + Datum datum2 {3.14}; + Datum datum3 {val_list}; + Datum datum4 {scal_list}; + + EXPECT_EQ(ValueType::String, datum1.getType()); + EXPECT_EQ("value", datum1.getValue()); + EXPECT_EQ("some units", datum1.getUnits()); + EXPECT_EQ(tags, datum1.getTags()); + + EXPECT_EQ(ValueType::Scalar, datum2.getType()); + EXPECT_THAT(datum2.getScalar(), DoubleEq(3.14)); + + EXPECT_EQ(ValueType::StringArray, datum3.getType()); + EXPECT_EQ(val_list, datum3.getStringArray()); + + EXPECT_EQ(ValueType::ScalarArray, datum4.getType()); + EXPECT_EQ(scal_list, datum4.getScalarArray()); } -TEST(Datum, createFromNode) { - conduit::Node val_with_tags; - conduit::Node scalar_with_units; - conduit::Node val_list_node; - conduit::Node scal_list_node; - conduit::Node empty_list_node; - conduit::Node int_array_node; - conduit::Node char_array_node; - - std::vector tags = {"hello", "world"}; - std::vector val_list = {"val1", "val2"}; - std::vector scal_list = {100, 2.0}; - // Conduit has some special treatment of arrays - int int_array [] = {-2, 2, 4, 8}; - std::vector array_equiv = {-2, 2, 4, 8}; - char coerced_to_string [] = {'a', 'b', 'c', '\0'}; - //Empty lists are valid - conduit::Node empty_list(conduit::DataType::list()); - - val_with_tags["value"] = "the value"; - addStringsToNode(val_with_tags, "tags", tags); - scalar_with_units["units"] = "some units"; - scalar_with_units["value"] = 3.14; - addStringsToNode(val_list_node, "value", val_list); - scal_list_node["value"] = scal_list; - empty_list_node["value"] = empty_list; - int_array_node["value"].set(int_array, 4); - char_array_node["value"] = coerced_to_string; - - Datum datum1{val_with_tags}; - Datum datum2{scalar_with_units}; - Datum datum3{val_list_node}; - Datum datum4{scal_list_node}; - Datum datum5{empty_list_node}; - Datum datum6{int_array_node}; - Datum datum7{char_array_node}; - - EXPECT_EQ("the value", datum1.getValue()); - EXPECT_EQ(tags, datum1.getTags()); - EXPECT_THAT(3.14, DoubleEq(datum2.getScalar())); - EXPECT_EQ("some units", datum2.getUnits()); - EXPECT_EQ(val_list, datum3.getStringArray()); - EXPECT_EQ(scal_list, datum4.getScalarArray()); - EXPECT_EQ(ValueType::ScalarArray, datum5.getType()); - EXPECT_EQ(array_equiv, datum6.getScalarArray()); - EXPECT_EQ("abc", datum7.getValue()); +TEST(Datum, createFromNode) +{ + conduit::Node val_with_tags; + conduit::Node scalar_with_units; + conduit::Node val_list_node; + conduit::Node scal_list_node; + conduit::Node empty_list_node; + conduit::Node int_array_node; + conduit::Node char_array_node; + + std::vector tags = {"hello", "world"}; + std::vector val_list = {"val1", "val2"}; + std::vector scal_list = {100, 2.0}; + // Conduit has some special treatment of arrays + int int_array[] = {-2, 2, 4, 8}; + std::vector array_equiv = {-2, 2, 4, 8}; + char coerced_to_string[] = {'a', 'b', 'c', '\0'}; + //Empty lists are valid + conduit::Node empty_list(conduit::DataType::list()); + + val_with_tags["value"] = "the value"; + addStringsToNode(val_with_tags, "tags", tags); + scalar_with_units["units"] = "some units"; + scalar_with_units["value"] = 3.14; + addStringsToNode(val_list_node, "value", val_list); + scal_list_node["value"] = scal_list; + empty_list_node["value"] = empty_list; + int_array_node["value"].set(int_array, 4); + char_array_node["value"] = coerced_to_string; + + Datum datum1 {val_with_tags}; + Datum datum2 {scalar_with_units}; + Datum datum3 {val_list_node}; + Datum datum4 {scal_list_node}; + Datum datum5 {empty_list_node}; + Datum datum6 {int_array_node}; + Datum datum7 {char_array_node}; + + EXPECT_EQ("the value", datum1.getValue()); + EXPECT_EQ(tags, datum1.getTags()); + EXPECT_THAT(3.14, DoubleEq(datum2.getScalar())); + EXPECT_EQ("some units", datum2.getUnits()); + EXPECT_EQ(val_list, datum3.getStringArray()); + EXPECT_EQ(scal_list, datum4.getScalarArray()); + EXPECT_EQ(ValueType::ScalarArray, datum5.getType()); + EXPECT_EQ(array_equiv, datum6.getScalarArray()); + EXPECT_EQ("abc", datum7.getValue()); } -TEST(Datum, setUnits) { - std::string value = "value"; - Datum datum1{value}; - datum1.setUnits("new units"); - EXPECT_EQ("new units", datum1.getUnits()); +TEST(Datum, setUnits) +{ + std::string value = "value"; + Datum datum1 {value}; + datum1.setUnits("new units"); + EXPECT_EQ("new units", datum1.getUnits()); } -TEST(Datum, setTags) { - std::string value = "value"; - Datum datum1{value}; - datum1.setTags({"new_tag"}); - EXPECT_EQ("new_tag", datum1.getTags()[0]); +TEST(Datum, setTags) +{ + std::string value = "value"; + Datum datum1 {value}; + datum1.setTags({"new_tag"}); + EXPECT_EQ("new_tag", datum1.getTags()[0]); } -TEST(Datum, createFromJson_missingKeys) { - conduit::Node object1; - try { - Datum datum1{object1}; - FAIL() << "Should have gotten a value error"; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr("value")); - } +TEST(Datum, createFromJson_missingKeys) +{ + conduit::Node object1; + try + { + Datum datum1 {object1}; + FAIL() << "Should have gotten a value error"; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr("value")); + } } -TEST(Datum, createFromJson_badListValue) { - conduit::Node object1; - auto &mixed_scal = object1["value"].append(); - mixed_scal.set(1.0); - auto &mixed_val = object1["value"].append(); - mixed_val.set("two"); - try { - Datum datum1{object1}; - FAIL() << "Should have gotten a value error"; - } catch (std::invalid_argument const &expected) { - std::string warning = "it must consist of only strings or only numbers"; - EXPECT_THAT(expected.what(), HasSubstr(warning)); - } +TEST(Datum, createFromJson_badListValue) +{ + conduit::Node object1; + auto &mixed_scal = object1["value"].append(); + mixed_scal.set(1.0); + auto &mixed_val = object1["value"].append(); + mixed_val.set("two"); + try + { + Datum datum1 {object1}; + FAIL() << "Should have gotten a value error"; + } + catch(std::invalid_argument const &expected) + { + std::string warning = "it must consist of only strings or only numbers"; + EXPECT_THAT(expected.what(), HasSubstr(warning)); + } } -TEST(Datum, toJson) { - std::vector tags = {"list", "of", "tags"}; - std::string value = "Datum value"; - std::vector scal_list = {-14, 22, 9}; - std::vector val_list = {"east", "west"}; - Datum datum1{value}; - datum1.setTags(tags); - Datum datum2{3.14}; - datum2.setUnits("Datum units"); - Datum datum3{scal_list}; - Datum datum4{val_list}; - conduit::Node datumRef1 = datum1.toNode(); - conduit::Node datumRef2 = datum2.toNode(); - conduit::Node datumRef3 = datum3.toNode(); - conduit::Node datumRef4 = datum4.toNode(); - EXPECT_EQ("Datum value", datumRef1["value"].as_string()); - std::vector node_tags; - auto tags_itr = datumRef1["tags"].children(); - while(tags_itr.has_next()) - node_tags.emplace_back(tags_itr.next().as_string()); - EXPECT_EQ(tags, node_tags); - - EXPECT_EQ("Datum units", datumRef2["units"].as_string()); - EXPECT_THAT(3.14, DoubleEq(datumRef2["value"].value())); - - // Conduit will pack vectors of numbers into arrays, but - // strings can only live as lists of Nodes - auto doub_array = datumRef3["value"].as_double_ptr(); - std::vectorscal_child_vals(doub_array, doub_array+datumRef3["value"].dtype().number_of_elements()); - std::vectorstr_child_vals; - auto str_itr = datumRef4["value"].children(); - while(str_itr.has_next()) - str_child_vals.emplace_back(str_itr.next().as_string()); - EXPECT_EQ(scal_list, scal_child_vals); - EXPECT_EQ(val_list, str_child_vals); +TEST(Datum, toJson) +{ + std::vector tags = {"list", "of", "tags"}; + std::string value = "Datum value"; + std::vector scal_list = {-14, 22, 9}; + std::vector val_list = {"east", "west"}; + Datum datum1 {value}; + datum1.setTags(tags); + Datum datum2 {3.14}; + datum2.setUnits("Datum units"); + Datum datum3 {scal_list}; + Datum datum4 {val_list}; + conduit::Node datumRef1 = datum1.toNode(); + conduit::Node datumRef2 = datum2.toNode(); + conduit::Node datumRef3 = datum3.toNode(); + conduit::Node datumRef4 = datum4.toNode(); + EXPECT_EQ("Datum value", datumRef1["value"].as_string()); + std::vector node_tags; + auto tags_itr = datumRef1["tags"].children(); + while(tags_itr.has_next()) + node_tags.emplace_back(tags_itr.next().as_string()); + EXPECT_EQ(tags, node_tags); + + EXPECT_EQ("Datum units", datumRef2["units"].as_string()); + EXPECT_THAT(3.14, DoubleEq(datumRef2["value"].value())); + + // Conduit will pack vectors of numbers into arrays, but + // strings can only live as lists of Nodes + auto doub_array = datumRef3["value"].as_double_ptr(); + std::vector scal_child_vals( + doub_array, + doub_array + datumRef3["value"].dtype().number_of_elements()); + std::vector str_child_vals; + auto str_itr = datumRef4["value"].children(); + while(str_itr.has_next()) + str_child_vals.emplace_back(str_itr.next().as_string()); + EXPECT_EQ(scal_list, scal_child_vals); + EXPECT_EQ(val_list, str_child_vals); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_Document.cpp b/src/axom/sina/tests/sina_Document.cpp index 240b2bb068..d44565d46d 100644 --- a/src/axom/sina/tests/sina_Document.cpp +++ b/src/axom/sina/tests/sina_Document.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include #include @@ -34,95 +33,131 @@ char const TEST_RECORD_TYPE[] = "test type"; char const EXPECTED_RECORDS_KEY[] = "records"; char const EXPECTED_RELATIONSHIPS_KEY[] = "relationships"; -TEST(Document, create_fromNode_empty) { - conduit::Node documentAsNode; - RecordLoader loader; - Document document{documentAsNode, loader}; - EXPECT_EQ(0u, document.getRecords().size()); - EXPECT_EQ(0u, document.getRelationships().size()); +TEST(Document, create_fromNode_empty) +{ + conduit::Node documentAsNode; + RecordLoader loader; + Document document {documentAsNode, loader}; + EXPECT_EQ(0u, document.getRecords().size()); + EXPECT_EQ(0u, document.getRelationships().size()); } -TEST(Document, create_fromNode_wrongRecordsType) { - conduit::Node recordsAsNodes; - recordsAsNodes[EXPECTED_RECORDS_KEY] = 123; - RecordLoader loader; - try { - Document document{recordsAsNodes, loader}; - FAIL() << "Should not have been able to parse records. Have " - << document.getRecords().size(); - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_RECORDS_KEY)); - } +TEST(Document, create_fromNode_wrongRecordsType) +{ + conduit::Node recordsAsNodes; + recordsAsNodes[EXPECTED_RECORDS_KEY] = 123; + RecordLoader loader; + try + { + Document document {recordsAsNodes, loader}; + FAIL() << "Should not have been able to parse records. Have " + << document.getRecords().size(); + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_RECORDS_KEY)); + } } -TEST(Document, create_fromNode_withRecords) { - conduit::Node recordAsNode; - recordAsNode["type"] = "IntTestRecord"; - recordAsNode["id"] = "the ID"; - recordAsNode[TEST_RECORD_VALUE_KEY] = 123; - - conduit::Node recordsAsNodes; - recordsAsNodes.append().set(recordAsNode); - - conduit::Node documentAsNode; - documentAsNode[EXPECTED_RECORDS_KEY] = recordsAsNodes; - - RecordLoader loader; - loader.addTypeLoader("IntTestRecord", [](conduit::Node const &asNode) { - return std::make_unique>(asNode); - }); - - Document document{documentAsNode, loader}; - auto &records = document.getRecords(); - ASSERT_EQ(1u, records.size()); - auto testRecord = dynamic_cast const *>(records[0].get()); - ASSERT_NE(nullptr, testRecord); - ASSERT_EQ(123, testRecord->getValue()); +TEST(Document, create_fromNode_withRecords) +{ + conduit::Node recordAsNode; + recordAsNode["type"] = "IntTestRecord"; + recordAsNode["id"] = "the ID"; + recordAsNode[TEST_RECORD_VALUE_KEY] = 123; + + conduit::Node recordsAsNodes; + recordsAsNodes.append().set(recordAsNode); + + conduit::Node documentAsNode; + documentAsNode[EXPECTED_RECORDS_KEY] = recordsAsNodes; + + RecordLoader loader; + loader.addTypeLoader("IntTestRecord", [](conduit::Node const &asNode) { + return std::make_unique>(asNode); + }); + + Document document {documentAsNode, loader}; + auto &records = document.getRecords(); + ASSERT_EQ(1u, records.size()); + auto testRecord = dynamic_cast const *>(records[0].get()); + ASSERT_NE(nullptr, testRecord); + ASSERT_EQ(123, testRecord->getValue()); } -TEST(Document, create_fromNode_withRelationships) { - conduit::Node relationshipAsNode; - relationshipAsNode["subject"] = "the subject"; - relationshipAsNode["object"] = "the object"; - relationshipAsNode["predicate"] = "is related to"; - - conduit::Node relationshipsAsNodes; - relationshipsAsNodes.append().set(relationshipAsNode); - - conduit::Node documentAsNode; - documentAsNode[EXPECTED_RELATIONSHIPS_KEY] = relationshipsAsNodes; - - Document document{documentAsNode, RecordLoader{}}; - auto &relationships = document.getRelationships(); - ASSERT_EQ(1u, relationships.size()); - EXPECT_EQ("the subject", relationships[0].getSubject().getId()); - EXPECT_EQ(IDType::Global, relationships[0].getSubject().getType()); - EXPECT_EQ("the object", relationships[0].getObject().getId()); - EXPECT_EQ(IDType::Global, relationships[0].getObject().getType()); - EXPECT_EQ("is related to", relationships[0].getPredicate()); +TEST(Document, create_fromNode_withRelationships) +{ + conduit::Node relationshipAsNode; + relationshipAsNode["subject"] = "the subject"; + relationshipAsNode["object"] = "the object"; + relationshipAsNode["predicate"] = "is related to"; + + conduit::Node relationshipsAsNodes; + relationshipsAsNodes.append().set(relationshipAsNode); + + conduit::Node documentAsNode; + documentAsNode[EXPECTED_RELATIONSHIPS_KEY] = relationshipsAsNodes; + + Document document {documentAsNode, RecordLoader {}}; + auto &relationships = document.getRelationships(); + ASSERT_EQ(1u, relationships.size()); + EXPECT_EQ("the subject", relationships[0].getSubject().getId()); + EXPECT_EQ(IDType::Global, relationships[0].getSubject().getType()); + EXPECT_EQ("the object", relationships[0].getObject().getId()); + EXPECT_EQ(IDType::Global, relationships[0].getObject().getType()); + EXPECT_EQ("is related to", relationships[0].getPredicate()); } -TEST(Document, create_fromJson_roundtrip) { - std::string orig_json = "{\"records\": [{\"type\": \"test_rec\",\"id\": \"test\"}],\"relationships\": []}"; - axom::sina::Document myDocument = Document(orig_json, createRecordLoaderWithAllKnownTypes()); +TEST(Document, create_fromJson_roundtrip) +{ + std::string orig_json = + "{\"records\": [{\"type\": \"test_rec\",\"id\": " + "\"test\"}],\"relationships\": []}"; + axom::sina::Document myDocument = + Document(orig_json, createRecordLoaderWithAllKnownTypes()); EXPECT_EQ(0, myDocument.getRelationships().size()); ASSERT_EQ(1, myDocument.getRecords().size()); EXPECT_EQ("test_rec", myDocument.getRecords()[0]->getType()); - std::string returned_json = myDocument.toJson(0,0,"",""); + std::string returned_json = myDocument.toJson(0, 0, "", ""); EXPECT_EQ(orig_json, returned_json); } -TEST(Document, create_fromJson_full) { - std::string long_json = "{\"records\": [{\"type\": \"foo\",\"id\": \"test_1\",\"user_defined\":{\"name\":\"bob\"},\"files\":{\"foo/bar.png\":{\"mimetype\":\"image\"}},\"data\":{\"scalar\": {\"value\": 500,\"units\": \"miles\"}}},{\"type\":\"bar\",\"id\": \"test_2\",\"data\": {\"scalar_list\": {\"value\": [1, 2, 3]}, \"string_list\": {\"value\": [\"a\",\"wonderful\",\"world\"], \"tags\":[\"observation\"]}}},{\"type\": \"run\",\"application\":\"sina_test\",\"id\": \"test_3\",\"data\":{\"scalar\": {\"value\": 12.3, \"units\": \"g/s\", \"tags\": [\"hi\"]}, \"scalar_list\": {\"value\": [1,2,3.0,4]}}}, {\"type\": \"bar\",\"id\": \"test_4\",\"data\":{\"string\": {\"value\": \"yarr\"}, \"string_list\": {\"value\": [\"y\",\"a\",\"r\"]}}, \"files\":{\"test/test.png\":{}}, \"user_defined\":{\"hello\":\"there\"}}],\"relationships\": [{\"predicate\": \"completes\",\"subject\": \"test_2\",\"object\": \"test_1\"},{\"subject\": \"test_3\", \"predicate\": \"overrides\", \"object\": \"test_4\"}]}"; - axom::sina::Document myDocument = Document(long_json, createRecordLoaderWithAllKnownTypes()); +TEST(Document, create_fromJson_full) +{ + std::string long_json = + "{\"records\": [{\"type\": \"foo\",\"id\": " + "\"test_1\",\"user_defined\":{\"name\":\"bob\"},\"files\":{\"foo/" + "bar.png\":{\"mimetype\":\"image\"}},\"data\":{\"scalar\": {\"value\": " + "500,\"units\": \"miles\"}}},{\"type\":\"bar\",\"id\": " + "\"test_2\",\"data\": {\"scalar_list\": {\"value\": [1, 2, 3]}, " + "\"string_list\": {\"value\": [\"a\",\"wonderful\",\"world\"], " + "\"tags\":[\"observation\"]}}},{\"type\": " + "\"run\",\"application\":\"sina_test\",\"id\": " + "\"test_3\",\"data\":{\"scalar\": {\"value\": 12.3, \"units\": \"g/s\", " + "\"tags\": [\"hi\"]}, \"scalar_list\": {\"value\": [1,2,3.0,4]}}}, " + "{\"type\": \"bar\",\"id\": \"test_4\",\"data\":{\"string\": {\"value\": " + "\"yarr\"}, \"string_list\": {\"value\": [\"y\",\"a\",\"r\"]}}, " + "\"files\":{\"test/test.png\":{}}, " + "\"user_defined\":{\"hello\":\"there\"}}],\"relationships\": " + "[{\"predicate\": \"completes\",\"subject\": \"test_2\",\"object\": " + "\"test_1\"},{\"subject\": \"test_3\", \"predicate\": \"overrides\", " + "\"object\": \"test_4\"}]}"; + axom::sina::Document myDocument = + Document(long_json, createRecordLoaderWithAllKnownTypes()); EXPECT_EQ(2, myDocument.getRelationships().size()); auto &records = myDocument.getRecords(); EXPECT_EQ(4, records.size()); } -TEST(Document, create_fromJson_value_check) { - std::string data_json = "{\"records\": [{\"type\": \"run\", \"application\":\"test\", \"id\": \"test_1\",\"data\":{\"int\": {\"value\": 500,\"units\": \"miles\"}, \"str/ings\": {\"value\":[\"z\", \"o\", \"o\"]}}, \"files\":{\"test/test.png\":{}}}]}"; - axom::sina::Document myDocument = Document(data_json, createRecordLoaderWithAllKnownTypes()); +TEST(Document, create_fromJson_value_check) +{ + std::string data_json = + "{\"records\": [{\"type\": \"run\", \"application\":\"test\", \"id\": " + "\"test_1\",\"data\":{\"int\": {\"value\": 500,\"units\": \"miles\"}, " + "\"str/ings\": {\"value\":[\"z\", \"o\", \"o\"]}}, " + "\"files\":{\"test/test.png\":{}}}]}"; + axom::sina::Document myDocument = + Document(data_json, createRecordLoaderWithAllKnownTypes()); EXPECT_EQ(0, myDocument.getRelationships().size()); auto &records = myDocument.getRecords(); EXPECT_EQ(1, records.size()); @@ -131,72 +166,78 @@ TEST(Document, create_fromJson_value_check) { EXPECT_EQ(data.at("int").getScalar(), 500.0); std::vector expected_string_vals = {"z", "o", "o"}; EXPECT_EQ(data.at("str/ings").getStringArray(), expected_string_vals); - EXPECT_EQ(records[0]->getFiles().count(File{"test/test.png"}), 1); + EXPECT_EQ(records[0]->getFiles().count(File {"test/test.png"}), 1); } -TEST(Document, toNode_empty) { - // A sina document should always have, at minimum, both records and - // relationships as empty arrays. - Document const document; - conduit::Node asNode = document.toNode(); - EXPECT_TRUE(asNode[EXPECTED_RECORDS_KEY].dtype().is_list()); - EXPECT_EQ(0, asNode[EXPECTED_RECORDS_KEY].number_of_children()); - EXPECT_TRUE(asNode[EXPECTED_RELATIONSHIPS_KEY].dtype().is_list()); - EXPECT_EQ(0, asNode[EXPECTED_RELATIONSHIPS_KEY].number_of_children()); +TEST(Document, toNode_empty) +{ + // A sina document should always have, at minimum, both records and + // relationships as empty arrays. + Document const document; + conduit::Node asNode = document.toNode(); + EXPECT_TRUE(asNode[EXPECTED_RECORDS_KEY].dtype().is_list()); + EXPECT_EQ(0, asNode[EXPECTED_RECORDS_KEY].number_of_children()); + EXPECT_TRUE(asNode[EXPECTED_RELATIONSHIPS_KEY].dtype().is_list()); + EXPECT_EQ(0, asNode[EXPECTED_RELATIONSHIPS_KEY].number_of_children()); } -TEST(Document, toNode_records) { - Document document; - std::string expectedIds[] = {"id 1", "id 2", "id 3"}; - std::string expectedValues[] = {"value 1", "value 2", "value 3"}; - - auto numRecords = sizeof(expectedIds) / sizeof(expectedIds[0]); - for (std::size_t i = 0; i < numRecords; ++i) { - document.add(std::make_unique>( - expectedIds[i], TEST_RECORD_TYPE, expectedValues[i])); - } - - auto asNode = document.toNode(); - - auto record_nodes = asNode[EXPECTED_RECORDS_KEY]; - ASSERT_EQ(numRecords, record_nodes.number_of_children()); - for (auto i = 0; i < record_nodes.number_of_children(); ++i) { - auto &actualNode = record_nodes[i]; - EXPECT_EQ(expectedIds[i], actualNode["id"].as_string()); - EXPECT_EQ(TEST_RECORD_TYPE, actualNode["type"].as_string()); - EXPECT_EQ(expectedValues[i], - actualNode[TEST_RECORD_VALUE_KEY].as_string()); - } +TEST(Document, toNode_records) +{ + Document document; + std::string expectedIds[] = {"id 1", "id 2", "id 3"}; + std::string expectedValues[] = {"value 1", "value 2", "value 3"}; + + auto numRecords = sizeof(expectedIds) / sizeof(expectedIds[0]); + for(std::size_t i = 0; i < numRecords; ++i) + { + document.add(std::make_unique>(expectedIds[i], + TEST_RECORD_TYPE, + expectedValues[i])); + } + + auto asNode = document.toNode(); + + auto record_nodes = asNode[EXPECTED_RECORDS_KEY]; + ASSERT_EQ(numRecords, record_nodes.number_of_children()); + for(auto i = 0; i < record_nodes.number_of_children(); ++i) + { + auto &actualNode = record_nodes[i]; + EXPECT_EQ(expectedIds[i], actualNode["id"].as_string()); + EXPECT_EQ(TEST_RECORD_TYPE, actualNode["type"].as_string()); + EXPECT_EQ(expectedValues[i], actualNode[TEST_RECORD_VALUE_KEY].as_string()); + } } -TEST(Document, toNode_relationships) { - Document document; - std::string expectedSubjects[] = {"subject 1", "subject 2"}; - std::string expectedObjects[] = {"object 1", "object 2"}; - std::string expectedPredicates[] = {"predicate 1", "predicate 2"}; - - auto numRecords = sizeof(expectedSubjects) / sizeof(expectedSubjects[0]); - for (unsigned long i = 0; i < numRecords; ++i) { - document.add(Relationship{ - ID{expectedSubjects[i], IDType::Global}, - expectedPredicates[i], - ID{expectedObjects[i], IDType::Global}, - }); - } - - auto asNode = document.toNode(); - - auto relationship_nodes = asNode[EXPECTED_RELATIONSHIPS_KEY]; - ASSERT_EQ(numRecords, relationship_nodes.number_of_children()); - for (auto i = 0; i < relationship_nodes.number_of_children(); ++i) { - auto &actualRelationship = relationship_nodes[i]; - EXPECT_EQ(expectedSubjects[i], actualRelationship["subject"].as_string()); - EXPECT_EQ(expectedObjects[i], actualRelationship["object"].as_string()); - EXPECT_EQ(expectedPredicates[i], actualRelationship["predicate"].as_string()); - } +TEST(Document, toNode_relationships) +{ + Document document; + std::string expectedSubjects[] = {"subject 1", "subject 2"}; + std::string expectedObjects[] = {"object 1", "object 2"}; + std::string expectedPredicates[] = {"predicate 1", "predicate 2"}; + + auto numRecords = sizeof(expectedSubjects) / sizeof(expectedSubjects[0]); + for(unsigned long i = 0; i < numRecords; ++i) + { + document.add(Relationship { + ID {expectedSubjects[i], IDType::Global}, + expectedPredicates[i], + ID {expectedObjects[i], IDType::Global}, + }); + } + + auto asNode = document.toNode(); + + auto relationship_nodes = asNode[EXPECTED_RELATIONSHIPS_KEY]; + ASSERT_EQ(numRecords, relationship_nodes.number_of_children()); + for(auto i = 0; i < relationship_nodes.number_of_children(); ++i) + { + auto &actualRelationship = relationship_nodes[i]; + EXPECT_EQ(expectedSubjects[i], actualRelationship["subject"].as_string()); + EXPECT_EQ(expectedObjects[i], actualRelationship["object"].as_string()); + EXPECT_EQ(expectedPredicates[i], actualRelationship["predicate"].as_string()); + } } - /** * Instances of this class acquire a temporary file name when created * and delete the file when destructed. @@ -204,125 +245,129 @@ TEST(Document, toNode_relationships) { * NOTE: This class uses unsafe methods and should only be used for testing * purposes. DO NOT move it to the main code!!!! */ -class NamedTempFile { +class NamedTempFile +{ public: - NamedTempFile(); + NamedTempFile(); - // As a resource-holding class, we don't want this to be copyable - // (or movable since there is no reason to return it from a function) - NamedTempFile(NamedTempFile const &) = delete; + // As a resource-holding class, we don't want this to be copyable + // (or movable since there is no reason to return it from a function) + NamedTempFile(NamedTempFile const &) = delete; - NamedTempFile(NamedTempFile &&) = delete; + NamedTempFile(NamedTempFile &&) = delete; - NamedTempFile &operator=(NamedTempFile const &) = delete; + NamedTempFile &operator=(NamedTempFile const &) = delete; - NamedTempFile &operator=(NamedTempFile &&) = delete; + NamedTempFile &operator=(NamedTempFile &&) = delete; - ~NamedTempFile(); + ~NamedTempFile(); - std::string const &getName() const { - return fileName; - } + std::string const &getName() const { return fileName; } private: - std::string fileName; + std::string fileName; }; -NamedTempFile::NamedTempFile() { - std::vector tmpFileName; - tmpFileName.resize(L_tmpnam); - // tmpnam is not the best way to do this, but it is standard and this is - // only a test. - if (!std::tmpnam(tmpFileName.data())) { - throw std::ios::failure{"Could not get temporary file"}; - } - fileName = tmpFileName.data(); +NamedTempFile::NamedTempFile() +{ + std::vector tmpFileName; + tmpFileName.resize(L_tmpnam); + // tmpnam is not the best way to do this, but it is standard and this is + // only a test. + if(!std::tmpnam(tmpFileName.data())) + { + throw std::ios::failure {"Could not get temporary file"}; + } + fileName = tmpFileName.data(); } -NamedTempFile::~NamedTempFile() { - std::remove(fileName.data()); -} +NamedTempFile::~NamedTempFile() { std::remove(fileName.data()); } -TEST(Document, saveDocument) { - NamedTempFile tmpFile; - - // First, write some random stuff to the temp file to make sure it is - // overwritten. - { - std::ofstream fout{tmpFile.getName()}; - fout << "Initial contents"; - } - - Document document; - document.add(std::make_unique(ID{"the id", IDType::Global}, - "the type")); - - saveDocument(document, tmpFile.getName()); - - conduit::Node readContents; - { - std::ifstream fin{tmpFile.getName()}; - std::stringstream f_buf; - f_buf << fin.rdbuf(); - readContents.parse(f_buf.str(),"json"); - } - - ASSERT_TRUE(readContents[EXPECTED_RECORDS_KEY].dtype().is_list()); - EXPECT_EQ(1, readContents[EXPECTED_RECORDS_KEY].number_of_children()); - auto &readRecord = readContents[EXPECTED_RECORDS_KEY][0]; - EXPECT_EQ("the id", readRecord["id"].as_string()); - EXPECT_EQ("the type", readRecord["type"].as_string()); +TEST(Document, saveDocument) +{ + NamedTempFile tmpFile; + + // First, write some random stuff to the temp file to make sure it is + // overwritten. + { + std::ofstream fout {tmpFile.getName()}; + fout << "Initial contents"; + } + + Document document; + document.add( + std::make_unique(ID {"the id", IDType::Global}, "the type")); + + saveDocument(document, tmpFile.getName()); + + conduit::Node readContents; + { + std::ifstream fin {tmpFile.getName()}; + std::stringstream f_buf; + f_buf << fin.rdbuf(); + readContents.parse(f_buf.str(), "json"); + } + + ASSERT_TRUE(readContents[EXPECTED_RECORDS_KEY].dtype().is_list()); + EXPECT_EQ(1, readContents[EXPECTED_RECORDS_KEY].number_of_children()); + auto &readRecord = readContents[EXPECTED_RECORDS_KEY][0]; + EXPECT_EQ("the id", readRecord["id"].as_string()); + EXPECT_EQ("the type", readRecord["type"].as_string()); } -TEST(Document, load_specifiedRecordLoader) { - using RecordType = TestRecord; - auto originalRecord = std::make_unique( - "the ID", "my type", 123); - Document originalDocument; - originalDocument.add(std::move(originalRecord)); - - NamedTempFile file; - { - std::ofstream fout{file.getName()}; - fout << originalDocument.toNode().to_json(); - } - - RecordLoader loader; - loader.addTypeLoader("my type", [](conduit::Node const &asNode) { - return std::make_unique( - getRequiredString("id", asNode, "Test type"), - getRequiredString("type", asNode, "Test type"), - static_cast(getRequiredField(TEST_RECORD_VALUE_KEY, asNode, - "Test type").as_int64())); - }); - Document loadedDocument = loadDocument(file.getName(), loader); - ASSERT_EQ(1u, loadedDocument.getRecords().size()); - auto loadedRecord = dynamic_cast( - loadedDocument.getRecords()[0].get()); - ASSERT_NE(nullptr, loadedRecord); - EXPECT_EQ(123, loadedRecord->getValue()); +TEST(Document, load_specifiedRecordLoader) +{ + using RecordType = TestRecord; + auto originalRecord = std::make_unique("the ID", "my type", 123); + Document originalDocument; + originalDocument.add(std::move(originalRecord)); + + NamedTempFile file; + { + std::ofstream fout {file.getName()}; + fout << originalDocument.toNode().to_json(); + } + + RecordLoader loader; + loader.addTypeLoader("my type", [](conduit::Node const &asNode) { + return std::make_unique( + getRequiredString("id", asNode, "Test type"), + getRequiredString("type", asNode, "Test type"), + static_cast( + getRequiredField(TEST_RECORD_VALUE_KEY, asNode, "Test type").as_int64())); + }); + Document loadedDocument = loadDocument(file.getName(), loader); + ASSERT_EQ(1u, loadedDocument.getRecords().size()); + auto loadedRecord = + dynamic_cast(loadedDocument.getRecords()[0].get()); + ASSERT_NE(nullptr, loadedRecord); + EXPECT_EQ(123, loadedRecord->getValue()); } -TEST(Document, load_defaultRecordLoaders) { - auto originalRun = std::make_unique( - ID{"the ID", IDType::Global}, "the app", "1.2.3", "jdoe"); - Document originalDocument; - originalDocument.add(std::move(originalRun)); - - NamedTempFile file; - { - std::ofstream fout{file.getName()}; - fout << originalDocument.toNode().to_json(); - } - - Document loadedDocument = loadDocument(file.getName()); - ASSERT_EQ(1u, loadedDocument.getRecords().size()); - auto loadedRun = dynamic_cast( - loadedDocument.getRecords()[0].get()); - EXPECT_NE(nullptr, loadedRun); +TEST(Document, load_defaultRecordLoaders) +{ + auto originalRun = + std::make_unique(ID {"the ID", IDType::Global}, + "the app", + "1.2.3", + "jdoe"); + Document originalDocument; + originalDocument.add(std::move(originalRun)); + + NamedTempFile file; + { + std::ofstream fout {file.getName()}; + fout << originalDocument.toNode().to_json(); + } + + Document loadedDocument = loadDocument(file.getName()); + ASSERT_EQ(1u, loadedDocument.getRecords().size()); + auto loadedRun = + dynamic_cast(loadedDocument.getRecords()[0].get()); + EXPECT_NE(nullptr, loadedRun); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_File.cpp b/src/axom/sina/tests/sina_File.cpp index 72a67505b4..5481ea067b 100644 --- a/src/axom/sina/tests/sina_File.cpp +++ b/src/axom/sina/tests/sina_File.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include "gtest/gtest.h" #include "axom/sina/core/File.hpp" @@ -21,67 +20,74 @@ namespace char const EXPECTED_MIMETYPE_KEY[] = "mimetype"; char const EXPECTED_TAGS_KEY[] = "tags"; -TEST(File, construct_differentType) { - File f1{"from literal"}; - File f2{std::string{"from std::string"}}; - EXPECT_EQ("from literal", f1.getUri()); - EXPECT_EQ("from std::string", f2.getUri()); +TEST(File, construct_differentType) +{ + File f1 {"from literal"}; + File f2 {std::string {"from std::string"}}; + EXPECT_EQ("from literal", f1.getUri()); + EXPECT_EQ("from std::string", f2.getUri()); } -TEST(File, setMimeType) { - File file{"the URI"}; - file.setMimeType("mime"); - EXPECT_EQ("the URI", file.getUri()); - EXPECT_EQ("mime", file.getMimeType()); +TEST(File, setMimeType) +{ + File file {"the URI"}; + file.setMimeType("mime"); + EXPECT_EQ("the URI", file.getUri()); + EXPECT_EQ("mime", file.getMimeType()); } -TEST(File, setTags) { - std::vector tags = {"these", "are", "tags"}; - File file{"the URI"}; - file.setTags(tags); - EXPECT_EQ("the URI", file.getUri()); - EXPECT_EQ(tags, file.getTags()); +TEST(File, setTags) +{ + std::vector tags = {"these", "are", "tags"}; + File file {"the URI"}; + file.setTags(tags); + EXPECT_EQ("the URI", file.getUri()); + EXPECT_EQ(tags, file.getTags()); } -TEST(File, create_fromNode_basic) { - std::string uri = "the URI"; - conduit::Node basic_file(conduit::DataType::object()); - File file{uri, basic_file}; - EXPECT_EQ(uri, file.getUri()); - EXPECT_EQ("", file.getMimeType()); - EXPECT_EQ(0, file.getTags().size()); +TEST(File, create_fromNode_basic) +{ + std::string uri = "the URI"; + conduit::Node basic_file(conduit::DataType::object()); + File file {uri, basic_file}; + EXPECT_EQ(uri, file.getUri()); + EXPECT_EQ("", file.getMimeType()); + EXPECT_EQ(0, file.getTags().size()); } -TEST(File, create_fromNode_complete) { - std::string uri = "another/uri.txt"; - std::vector tags = {"tags", "are", "fun"}; - conduit::Node full_file(conduit::DataType::object()); - full_file[EXPECTED_MIMETYPE_KEY] = "the mime type"; - addStringsToNode(full_file, EXPECTED_TAGS_KEY, tags); - File file{uri, full_file}; - EXPECT_EQ(uri, file.getUri()); - EXPECT_EQ("the mime type", file.getMimeType()); - EXPECT_EQ(tags, file.getTags()); +TEST(File, create_fromNode_complete) +{ + std::string uri = "another/uri.txt"; + std::vector tags = {"tags", "are", "fun"}; + conduit::Node full_file(conduit::DataType::object()); + full_file[EXPECTED_MIMETYPE_KEY] = "the mime type"; + addStringsToNode(full_file, EXPECTED_TAGS_KEY, tags); + File file {uri, full_file}; + EXPECT_EQ(uri, file.getUri()); + EXPECT_EQ("the mime type", file.getMimeType()); + EXPECT_EQ(tags, file.getTags()); } -TEST(File, toNode_basic) { - File file{"the URI"}; - auto asNode = file.toNode(); - EXPECT_FALSE(asNode.has_child(EXPECTED_MIMETYPE_KEY)); - EXPECT_FALSE(asNode.has_child(EXPECTED_TAGS_KEY)); +TEST(File, toNode_basic) +{ + File file {"the URI"}; + auto asNode = file.toNode(); + EXPECT_FALSE(asNode.has_child(EXPECTED_MIMETYPE_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_TAGS_KEY)); } -TEST(File, toNode_complete) { - std::vector tags = {"these", "are", "tags"}; - File file{"the URI"}; - file.setMimeType("the mime type"); - file.setTags(tags); - auto asNode = file.toNode(); - EXPECT_EQ("the mime type", asNode[EXPECTED_MIMETYPE_KEY].as_string()); - EXPECT_EQ(tags, file.getTags()); +TEST(File, toNode_complete) +{ + std::vector tags = {"these", "are", "tags"}; + File file {"the URI"}; + file.setMimeType("the mime type"); + file.setTags(tags); + auto asNode = file.toNode(); + EXPECT_EQ("the mime type", asNode[EXPECTED_MIMETYPE_KEY].as_string()); + EXPECT_EQ(tags, file.getTags()); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_ID.cpp b/src/axom/sina/tests/sina_ID.cpp index 9a0eb50f72..c4b10353bb 100644 --- a/src/axom/sina/tests/sina_ID.cpp +++ b/src/axom/sina/tests/sina_ID.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include "gtest/gtest.h" @@ -24,78 +23,88 @@ namespace using ::testing::HasSubstr; -TEST(ID, create) { - ID id1{"the name", IDType::Local}; - ID id2{"another name", IDType::Global}; +TEST(ID, create) +{ + ID id1 {"the name", IDType::Local}; + ID id2 {"another name", IDType::Global}; - EXPECT_EQ("the name", id1.getId()); - EXPECT_EQ("another name", id2.getId()); + EXPECT_EQ("the name", id1.getId()); + EXPECT_EQ("another name", id2.getId()); - EXPECT_EQ(IDType::Local, id1.getType()); - EXPECT_EQ(IDType::Global, id2.getType()); + EXPECT_EQ(IDType::Local, id1.getType()); + EXPECT_EQ(IDType::Global, id2.getType()); } -TEST(IDField, create) { - ID id{"the id", IDType::Global}; - internal::IDField field{id, "local name", "global name"}; - EXPECT_EQ("the id", field.getID().getId()); - EXPECT_EQ(IDType::Global, field.getID().getType()); - EXPECT_EQ("local name", field.getLocalName()); - EXPECT_EQ("global name", field.getGlobalName()); +TEST(IDField, create) +{ + ID id {"the id", IDType::Global}; + internal::IDField field {id, "local name", "global name"}; + EXPECT_EQ("the id", field.getID().getId()); + EXPECT_EQ(IDType::Global, field.getID().getType()); + EXPECT_EQ("local name", field.getLocalName()); + EXPECT_EQ("global name", field.getGlobalName()); } -TEST(IDField, createFromNode_local) { - conduit::Node object; - object["local id key"] = "the id"; - internal::IDField field{object, "local id key", "global id key"}; - EXPECT_EQ("the id", field.getID().getId()); - EXPECT_EQ(IDType::Local, field.getID().getType()); - EXPECT_EQ("local id key", field.getLocalName()); - EXPECT_EQ("global id key", field.getGlobalName()); +TEST(IDField, createFromNode_local) +{ + conduit::Node object; + object["local id key"] = "the id"; + internal::IDField field {object, "local id key", "global id key"}; + EXPECT_EQ("the id", field.getID().getId()); + EXPECT_EQ(IDType::Local, field.getID().getType()); + EXPECT_EQ("local id key", field.getLocalName()); + EXPECT_EQ("global id key", field.getGlobalName()); } -TEST(IDField, createFromNode_global) { - conduit::Node object; - object["local id key"] = "local id"; - object["global id key"] = "global id"; - - internal::IDField field{object, "local id key", "global id key"}; - EXPECT_EQ("global id", field.getID().getId()); - EXPECT_EQ(IDType::Global, field.getID().getType()); - EXPECT_EQ("local id key", field.getLocalName()); - EXPECT_EQ("global id key", field.getGlobalName()); +TEST(IDField, createFromNode_global) +{ + conduit::Node object; + object["local id key"] = "local id"; + object["global id key"] = "global id"; + + internal::IDField field {object, "local id key", "global id key"}; + EXPECT_EQ("global id", field.getID().getId()); + EXPECT_EQ(IDType::Global, field.getID().getType()); + EXPECT_EQ("local id key", field.getLocalName()); + EXPECT_EQ("global id key", field.getGlobalName()); } -TEST(IDField, createFromNode_missingKeys) { - conduit::Node object(conduit::DataType::object()); - try { - internal::IDField field{object, "local id key", "global id key"}; - FAIL() << "Should have gotten a value error"; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr("local id key")); - EXPECT_THAT(expected.what(), HasSubstr("global id key")); - } +TEST(IDField, createFromNode_missingKeys) +{ + conduit::Node object(conduit::DataType::object()); + try + { + internal::IDField field {object, "local id key", "global id key"}; + FAIL() << "Should have gotten a value error"; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr("local id key")); + EXPECT_THAT(expected.what(), HasSubstr("global id key")); + } } -TEST(IDField, toNode_local) { - ID id{"the id", IDType::Local}; - internal::IDField field{id, "local name", "global name"}; - conduit::Node value; - field.addTo(value); - EXPECT_EQ("the id", value["local name"].as_string()); - EXPECT_FALSE(value.has_child("global name")); +TEST(IDField, toNode_local) +{ + ID id {"the id", IDType::Local}; + internal::IDField field {id, "local name", "global name"}; + conduit::Node value; + field.addTo(value); + EXPECT_EQ("the id", value["local name"].as_string()); + EXPECT_FALSE(value.has_child("global name")); } -TEST(IDField, toNode_global) { - ID id{"the id", IDType::Global}; - internal::IDField field{id, "local name", "global name"}; - conduit::Node value; - field.addTo(value); - EXPECT_EQ("the id", value["global name"].as_string()); - EXPECT_FALSE(value.has_child("local name")); +TEST(IDField, toNode_global) +{ + ID id {"the id", IDType::Global}; + internal::IDField field {id, "local name", "global name"}; + conduit::Node value; + field.addTo(value); + EXPECT_EQ("the id", value["global name"].as_string()); + EXPECT_FALSE(value.has_child("local name")); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_Record.cpp b/src/axom/sina/tests/sina_Record.cpp index 7288e9ba05..c8e8f9b68c 100644 --- a/src/axom/sina/tests/sina_Record.cpp +++ b/src/axom/sina/tests/sina_Record.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include @@ -25,10 +24,10 @@ namespace { using ::testing::Contains; +using ::testing::DoubleEq; using ::testing::ElementsAre; -using ::testing::Key; using ::testing::HasSubstr; -using ::testing::DoubleEq; +using ::testing::Key; using ::testing::Not; char const EXPECTED_TYPE_KEY[] = "type"; @@ -41,83 +40,92 @@ char const EXPECTED_USER_DEFINED_KEY[] = "user_defined"; char const LIBRARY_DATA_ID_DATUM[] = "SINA_librarydata_id"; char const LIBRARY_DATA_TYPE_DATUM[] = "SINA_librarydata_type"; -TEST(Record, create_typeMissing) { - conduit::Node originalNode; - originalNode[EXPECTED_LOCAL_ID_KEY] = "the ID"; - try { - Record record{originalNode}; - FAIL() << "Should have failed due to missing type"; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_TYPE_KEY)); - } +TEST(Record, create_typeMissing) +{ + conduit::Node originalNode; + originalNode[EXPECTED_LOCAL_ID_KEY] = "the ID"; + try + { + Record record {originalNode}; + FAIL() << "Should have failed due to missing type"; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_TYPE_KEY)); + } } -TEST(Record, add_data_existing_key) { - Record record{ID{"the id", IDType::Local}, "test_record"}; - record.add("key1", Datum{"val1"}); - EXPECT_EQ("val1", record.getData().at("key1").getValue()); - record.add("key1", Datum{"val2"}); - EXPECT_EQ("val2", record.getData().at("key1").getValue()); +TEST(Record, add_data_existing_key) +{ + Record record {ID {"the id", IDType::Local}, "test_record"}; + record.add("key1", Datum {"val1"}); + EXPECT_EQ("val1", record.getData().at("key1").getValue()); + record.add("key1", Datum {"val2"}); + EXPECT_EQ("val2", record.getData().at("key1").getValue()); } -TEST(Record, add_curve_set_existing_key) { - Record record{ID{"the id", IDType::Local}, "test_record"}; - - CurveSet cs1{"cs1"}; - cs1.addDependentCurve(Curve{"original", {1, 2, 3}}); - record.add(cs1); - - auto &csAfterFirstInsert = record.getCurveSets(); - ASSERT_THAT(csAfterFirstInsert, Contains(Key("cs1"))); - EXPECT_THAT(csAfterFirstInsert.at("cs1").getDependentCurves(), - Contains(Key("original"))); - - CurveSet cs2{"cs1"}; - cs2.addDependentCurve(Curve{"new", {1, 2, 3}}); - record.add(cs2); - - auto &csAfterSecondInsert = record.getCurveSets(); - ASSERT_THAT(csAfterSecondInsert, Contains(Key("cs1"))); - EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), - Not(Contains(Key("original")))); - EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), - Contains(Key("new"))); +TEST(Record, add_curve_set_existing_key) +{ + Record record {ID {"the id", IDType::Local}, "test_record"}; + + CurveSet cs1 {"cs1"}; + cs1.addDependentCurve(Curve {"original", {1, 2, 3}}); + record.add(cs1); + + auto &csAfterFirstInsert = record.getCurveSets(); + ASSERT_THAT(csAfterFirstInsert, Contains(Key("cs1"))); + EXPECT_THAT(csAfterFirstInsert.at("cs1").getDependentCurves(), + Contains(Key("original"))); + + CurveSet cs2 {"cs1"}; + cs2.addDependentCurve(Curve {"new", {1, 2, 3}}); + record.add(cs2); + + auto &csAfterSecondInsert = record.getCurveSets(); + ASSERT_THAT(csAfterSecondInsert, Contains(Key("cs1"))); + EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), + Not(Contains(Key("original")))); + EXPECT_THAT(csAfterSecondInsert.at("cs1").getDependentCurves(), + Contains(Key("new"))); } -TEST(Record, remove_file) { - Record record{ID{"the id", IDType::Local}, "test_record"}; - std::string path = "the/path.txt"; +TEST(Record, remove_file) +{ + Record record {ID {"the id", IDType::Local}, "test_record"}; + std::string path = "the/path.txt"; - File original{path}; - original.setMimeType("txt"); - record.add(original); - EXPECT_EQ(1u, record.getFiles().size()); - EXPECT_EQ("txt", record.getFiles().find(File{path})->getMimeType()); + File original {path}; + original.setMimeType("txt"); + record.add(original); + EXPECT_EQ(1u, record.getFiles().size()); + EXPECT_EQ("txt", record.getFiles().find(File {path})->getMimeType()); - record.remove(original); - EXPECT_EQ(0u, record.getFiles().size()); + record.remove(original); + EXPECT_EQ(0u, record.getFiles().size()); } -TEST(Record, add_file_existing_key) { - Record record{ID{"the id", IDType::Local}, "test_record"}; - std::string path = "the/path.txt"; - - File original{path}; - original.setMimeType("txt"); - record.add(original); - EXPECT_EQ(1u, record.getFiles().size()); - EXPECT_EQ("txt", record.getFiles().find(File{path})->getMimeType()); - - File replacement{path}; - replacement.setMimeType("image"); - record.add(replacement); - EXPECT_EQ(1u, record.getFiles().size()); - EXPECT_EQ("image", record.getFiles().find(File{path})->getMimeType()); +TEST(Record, add_file_existing_key) +{ + Record record {ID {"the id", IDType::Local}, "test_record"}; + std::string path = "the/path.txt"; + + File original {path}; + original.setMimeType("txt"); + record.add(original); + EXPECT_EQ(1u, record.getFiles().size()); + EXPECT_EQ("txt", record.getFiles().find(File {path})->getMimeType()); + + File replacement {path}; + replacement.setMimeType("image"); + record.add(replacement); + EXPECT_EQ(1u, record.getFiles().size()); + EXPECT_EQ("image", record.getFiles().find(File {path})->getMimeType()); } -TEST(Record, add_child_record_as_library_data) { - Record parentRecord{ID{"parent id", IDType::Local}, "test_record_parent"}; - Record childRecord{ID{"child id", IDType::Local}, "test_record_child"}; +TEST(Record, add_child_record_as_library_data) +{ + Record parentRecord {ID {"parent id", IDType::Local}, "test_record_parent"}; + Record childRecord {ID {"child id", IDType::Local}, "test_record_child"}; parentRecord.addRecordAsLibraryData(childRecord, "child"); auto &parentLibData = parentRecord.getLibraryData(); ASSERT_THAT(parentLibData, Contains(Key("child"))); @@ -125,118 +133,125 @@ TEST(Record, add_child_record_as_library_data) { ASSERT_THAT(childLibContents, Contains(Key(LIBRARY_DATA_ID_DATUM))); EXPECT_EQ("child id", childLibContents.at(LIBRARY_DATA_ID_DATUM).getValue()); ASSERT_THAT(childLibContents, Contains(Key(LIBRARY_DATA_TYPE_DATUM))); - EXPECT_EQ("test_record_child", childLibContents.at(LIBRARY_DATA_TYPE_DATUM).getValue()); + EXPECT_EQ("test_record_child", + childLibContents.at(LIBRARY_DATA_TYPE_DATUM).getValue()); } -TEST(Record, add_child_record_as_library_data_with_data) { - Record parentRecord{ID{"parent id", IDType::Local}, "test_record_parent"}; - Record childRecord{ID{"child id", IDType::Local}, "test_record_child"}; - childRecord.add("key1", Datum{"val1"}); +TEST(Record, add_child_record_as_library_data_with_data) +{ + Record parentRecord {ID {"parent id", IDType::Local}, "test_record_parent"}; + Record childRecord {ID {"child id", IDType::Local}, "test_record_child"}; + childRecord.add("key1", Datum {"val1"}); parentRecord.addRecordAsLibraryData(childRecord, "child"); auto &childLibContents = parentRecord.getLibraryData().at("child")->getData(); ASSERT_THAT(childLibContents, Contains(Key("key1"))); EXPECT_EQ("val1", childLibContents.at("key1").getValue()); } -TEST(Record, add_child_record_as_library_data_with_files) { - Record parentRecord{ID{"parent id", IDType::Local}, "test_record_parent"}; - Record childRecord{ID{"child id", IDType::Local}, "test_record_child"}; +TEST(Record, add_child_record_as_library_data_with_files) +{ + Record parentRecord {ID {"parent id", IDType::Local}, "test_record_parent"}; + Record childRecord {ID {"child id", IDType::Local}, "test_record_child"}; std::string path = "the/path.txt"; - File childFile{path}; + File childFile {path}; childFile.setMimeType("txt"); childRecord.add(childFile); parentRecord.addRecordAsLibraryData(childRecord, "child"); EXPECT_EQ(1u, parentRecord.getFiles().size()); - EXPECT_EQ("txt", parentRecord.getFiles().find(File{path})->getMimeType()); + EXPECT_EQ("txt", parentRecord.getFiles().find(File {path})->getMimeType()); } -TEST(Record, create_localId_fromNode) { - conduit::Node originalNode; - originalNode[EXPECTED_LOCAL_ID_KEY] = "the ID"; - originalNode[EXPECTED_TYPE_KEY] = "my type"; - Record record{originalNode}; - EXPECT_EQ("my type", record.getType()); - EXPECT_EQ("the ID", record.getId().getId()); - EXPECT_EQ(IDType::Local, record.getId().getType()); +TEST(Record, create_localId_fromNode) +{ + conduit::Node originalNode; + originalNode[EXPECTED_LOCAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + Record record {originalNode}; + EXPECT_EQ("my type", record.getType()); + EXPECT_EQ("the ID", record.getId().getId()); + EXPECT_EQ(IDType::Local, record.getId().getType()); } -TEST(Record, create_globalId_fromNode) { - conduit::Node originalNode; - originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; - originalNode[EXPECTED_TYPE_KEY] = "my type"; - Record record{originalNode}; - EXPECT_EQ("my type", record.getType()); - EXPECT_EQ("the ID", record.getId().getId()); - EXPECT_EQ(IDType::Global, record.getId().getType()); +TEST(Record, create_globalId_fromNode) +{ + conduit::Node originalNode; + originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + Record record {originalNode}; + EXPECT_EQ("my type", record.getType()); + EXPECT_EQ("the ID", record.getId().getId()); + EXPECT_EQ(IDType::Global, record.getId().getType()); } -TEST(Record, create_globalId_withContent) { - conduit::Node originalNode; - originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; - originalNode[EXPECTED_TYPE_KEY] = "my type"; - originalNode[EXPECTED_DATA_KEY]; - originalNode[EXPECTED_LIBRARY_DATA_KEY]; - - std::string name1 = "datum name 1"; - std::string name2 = "datum name 2/with/slash"; - - conduit::Node name1_node; - name1_node["value"] = "value 1"; - originalNode[EXPECTED_DATA_KEY][name1] = name1_node; - - conduit::Node name2_node; - name2_node["value"] = 2.22; - name2_node["units"] = "g/L"; - addStringsToNode(name2_node, "tags", {"tag1","tag2"}); - name2_node["value"] = 2.22; - originalNode[EXPECTED_DATA_KEY].add_child(name2) = name2_node; - - std::string libName = "my_lib"; - conduit::Node libNode; - std::string name3 = "datum name 3"; - conduit::Node name3_node; - name3_node["value"] = "value 3"; - libNode[EXPECTED_DATA_KEY]; - libNode[EXPECTED_DATA_KEY][name3] = name3_node; - originalNode[EXPECTED_LIBRARY_DATA_KEY][libName] = libNode; - - Record record{originalNode}; - auto &data = record.getData(); - ASSERT_EQ(2u, data.size()); - EXPECT_EQ("value 1", data.at(name1).getValue()); - EXPECT_THAT(2.22, DoubleEq(data.at(name2).getScalar())); - EXPECT_EQ("g/L", data.at(name2).getUnits()); - EXPECT_EQ("tag1", data.at(name2).getTags()[0]); - EXPECT_EQ("tag2", data.at(name2).getTags()[1]); - - auto &libdata = record.getLibraryData(); - EXPECT_THAT(libdata, Contains(Key(libName))); - EXPECT_EQ("value 3", libdata.at(libName)->getData().at(name3).getValue()); +TEST(Record, create_globalId_withContent) +{ + conduit::Node originalNode; + originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + originalNode[EXPECTED_DATA_KEY]; + originalNode[EXPECTED_LIBRARY_DATA_KEY]; + + std::string name1 = "datum name 1"; + std::string name2 = "datum name 2/with/slash"; + + conduit::Node name1_node; + name1_node["value"] = "value 1"; + originalNode[EXPECTED_DATA_KEY][name1] = name1_node; + + conduit::Node name2_node; + name2_node["value"] = 2.22; + name2_node["units"] = "g/L"; + addStringsToNode(name2_node, "tags", {"tag1", "tag2"}); + name2_node["value"] = 2.22; + originalNode[EXPECTED_DATA_KEY].add_child(name2) = name2_node; + + std::string libName = "my_lib"; + conduit::Node libNode; + std::string name3 = "datum name 3"; + conduit::Node name3_node; + name3_node["value"] = "value 3"; + libNode[EXPECTED_DATA_KEY]; + libNode[EXPECTED_DATA_KEY][name3] = name3_node; + originalNode[EXPECTED_LIBRARY_DATA_KEY][libName] = libNode; + + Record record {originalNode}; + auto &data = record.getData(); + ASSERT_EQ(2u, data.size()); + EXPECT_EQ("value 1", data.at(name1).getValue()); + EXPECT_THAT(2.22, DoubleEq(data.at(name2).getScalar())); + EXPECT_EQ("g/L", data.at(name2).getUnits()); + EXPECT_EQ("tag1", data.at(name2).getTags()[0]); + EXPECT_EQ("tag2", data.at(name2).getTags()[1]); + + auto &libdata = record.getLibraryData(); + EXPECT_THAT(libdata, Contains(Key(libName))); + EXPECT_EQ("value 3", libdata.at(libName)->getData().at(name3).getValue()); } -TEST(Record, create_globalId_files) { - conduit::Node originalNode; - originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; - originalNode[EXPECTED_TYPE_KEY] = "my type"; - originalNode[EXPECTED_FILES_KEY]; - - std::string uri1 = "/some/uri.txt"; - std::string uri2 = "www.anotheruri.com"; - std::string uri3 = "yet another uri"; - originalNode[EXPECTED_FILES_KEY].add_child(uri1); - originalNode[EXPECTED_FILES_KEY].add_child(uri2); - originalNode[EXPECTED_FILES_KEY].add_child(uri3); - Record record{originalNode}; - auto &files = record.getFiles(); - ASSERT_EQ(3u, files.size()); - EXPECT_EQ(1, files.count(File{uri1})); - EXPECT_EQ(1, files.count(File{uri2})); - EXPECT_EQ(1, files.count(File{uri3})); +TEST(Record, create_globalId_files) +{ + conduit::Node originalNode; + originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + originalNode[EXPECTED_FILES_KEY]; + + std::string uri1 = "/some/uri.txt"; + std::string uri2 = "www.anotheruri.com"; + std::string uri3 = "yet another uri"; + originalNode[EXPECTED_FILES_KEY].add_child(uri1); + originalNode[EXPECTED_FILES_KEY].add_child(uri2); + originalNode[EXPECTED_FILES_KEY].add_child(uri3); + Record record {originalNode}; + auto &files = record.getFiles(); + ASSERT_EQ(3u, files.size()); + EXPECT_EQ(1, files.count(File {uri1})); + EXPECT_EQ(1, files.count(File {uri2})); + EXPECT_EQ(1, files.count(File {uri3})); } - -TEST(Record, create_fromNode_curveSets) { - conduit::Node recordAsNode = parseJsonValue(R"({ +TEST(Record, create_fromNode_curveSets) +{ + conduit::Node recordAsNode = parseJsonValue(R"({ "id": "myId", "type": "myType", "curve_sets": { @@ -250,156 +265,172 @@ TEST(Record, create_fromNode_curveSets) { } } })"); - Record record{recordAsNode}; - auto &curveSets = record.getCurveSets(); - ASSERT_THAT(curveSets, Contains(Key("cs1"))); + Record record {recordAsNode}; + auto &curveSets = record.getCurveSets(); + ASSERT_THAT(curveSets, Contains(Key("cs1"))); } -TEST(Record, create_fromNode_userDefined) { - conduit::Node originalNode; - originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; - originalNode[EXPECTED_TYPE_KEY] = "my type"; - originalNode[EXPECTED_USER_DEFINED_KEY]["k1"] = "v1"; - originalNode[EXPECTED_USER_DEFINED_KEY]["k2"] = 123; - std::vector k3_vals{1, 2, 3}; - originalNode[EXPECTED_USER_DEFINED_KEY]["k3"] = k3_vals; - - Record record{originalNode}; - auto const &userDefined = record.getUserDefinedContent(); - EXPECT_EQ("v1", userDefined["k1"].as_string()); - EXPECT_EQ(123, userDefined["k2"].as_int()); - auto int_array = userDefined["k3"].as_int_ptr(); - std::vectorudef_ints(int_array, int_array+userDefined["k3"].dtype().number_of_elements()); - EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); +TEST(Record, create_fromNode_userDefined) +{ + conduit::Node originalNode; + originalNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + originalNode[EXPECTED_TYPE_KEY] = "my type"; + originalNode[EXPECTED_USER_DEFINED_KEY]["k1"] = "v1"; + originalNode[EXPECTED_USER_DEFINED_KEY]["k2"] = 123; + std::vector k3_vals {1, 2, 3}; + originalNode[EXPECTED_USER_DEFINED_KEY]["k3"] = k3_vals; + + Record record {originalNode}; + auto const &userDefined = record.getUserDefinedContent(); + EXPECT_EQ("v1", userDefined["k1"].as_string()); + EXPECT_EQ(123, userDefined["k2"].as_int()); + auto int_array = userDefined["k3"].as_int_ptr(); + std::vector udef_ints( + int_array, + int_array + userDefined["k3"].dtype().number_of_elements()); + EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); } -TEST(Record, getUserDefined_initialConst) { - ID id{"the id", IDType::Local}; - Record const record{id, "my type"}; - conduit::Node const &userDefined = record.getUserDefinedContent(); - EXPECT_TRUE(userDefined.dtype().is_empty()); +TEST(Record, getUserDefined_initialConst) +{ + ID id {"the id", IDType::Local}; + Record const record {id, "my type"}; + conduit::Node const &userDefined = record.getUserDefinedContent(); + EXPECT_TRUE(userDefined.dtype().is_empty()); } -TEST(Record, getUserDefined_initialNonConst) { - ID id{"the id", IDType::Local}; - Record record{id, "my type"}; - conduit::Node &initialUserDefined = record.getUserDefinedContent(); - EXPECT_TRUE(initialUserDefined.dtype().is_empty()); - initialUserDefined["foo"] = 123; - EXPECT_EQ(123, record.getUserDefinedContent()["foo"].as_int()); +TEST(Record, getUserDefined_initialNonConst) +{ + ID id {"the id", IDType::Local}; + Record record {id, "my type"}; + conduit::Node &initialUserDefined = record.getUserDefinedContent(); + EXPECT_TRUE(initialUserDefined.dtype().is_empty()); + initialUserDefined["foo"] = 123; + EXPECT_EQ(123, record.getUserDefinedContent()["foo"].as_int()); } -TEST(Record, toNode_localId) { - ID id{"the id", IDType::Global}; - Record record{id, "my type"}; - auto asNode = record.toNode(); - EXPECT_TRUE(asNode.dtype().is_object()); - EXPECT_EQ("my type", asNode[EXPECTED_TYPE_KEY].as_string()); - EXPECT_EQ("the id", asNode[EXPECTED_GLOBAL_ID_KEY].as_string()); - EXPECT_TRUE(asNode[EXPECTED_LOCAL_ID_KEY].dtype().is_empty()); +TEST(Record, toNode_localId) +{ + ID id {"the id", IDType::Global}; + Record record {id, "my type"}; + auto asNode = record.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + EXPECT_EQ("my type", asNode[EXPECTED_TYPE_KEY].as_string()); + EXPECT_EQ("the id", asNode[EXPECTED_GLOBAL_ID_KEY].as_string()); + EXPECT_TRUE(asNode[EXPECTED_LOCAL_ID_KEY].dtype().is_empty()); } -TEST(Record, toNode_globalId) { - ID id{"the id", IDType::Local}; - Record record{id, "my type"}; - auto asNode = record.toNode(); - EXPECT_TRUE(asNode.dtype().is_object()); - EXPECT_EQ("my type", asNode[EXPECTED_TYPE_KEY].as_string()); - EXPECT_EQ("the id", asNode[EXPECTED_LOCAL_ID_KEY].as_string()); - EXPECT_TRUE(asNode[EXPECTED_GLOBAL_ID_KEY].dtype().is_empty()); +TEST(Record, toNode_globalId) +{ + ID id {"the id", IDType::Local}; + Record record {id, "my type"}; + auto asNode = record.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + EXPECT_EQ("my type", asNode[EXPECTED_TYPE_KEY].as_string()); + EXPECT_EQ("the id", asNode[EXPECTED_LOCAL_ID_KEY].as_string()); + EXPECT_TRUE(asNode[EXPECTED_GLOBAL_ID_KEY].dtype().is_empty()); } -TEST(Record, toNode_default_values) { - ID id{"the id", IDType::Global}; - Record record{id, "my type"}; - auto asNode = record.toNode(); - EXPECT_TRUE(asNode.dtype().is_object()); - // We want to be sure that unset optional fields aren't present - EXPECT_FALSE(asNode.has_child(EXPECTED_DATA_KEY)); - EXPECT_FALSE(asNode.has_child(EXPECTED_FILES_KEY)); - EXPECT_FALSE(asNode.has_child(EXPECTED_USER_DEFINED_KEY)); +TEST(Record, toNode_default_values) +{ + ID id {"the id", IDType::Global}; + Record record {id, "my type"}; + auto asNode = record.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + // We want to be sure that unset optional fields aren't present + EXPECT_FALSE(asNode.has_child(EXPECTED_DATA_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_FILES_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_USER_DEFINED_KEY)); } -TEST(Record, toNode_userDefined) { - ID id{"the id", IDType::Local}; - Record record{id, "my type"}; - conduit::Node userDef; - userDef["k1"] = "v1"; - userDef["k2"] = 123; - std::vector int_vals{1, 2, 3}; - userDef["k3"] = int_vals; - record.setUserDefinedContent(userDef); - - auto asNode = record.toNode(); - - auto userDefined = asNode[EXPECTED_USER_DEFINED_KEY]; - EXPECT_EQ("v1", userDefined["k1"].as_string()); - EXPECT_EQ(123, userDefined["k2"].as_int()); - auto int_array = userDefined["k3"].as_int_ptr(); - std::vectorudef_ints(int_array, int_array+userDefined["k3"].dtype().number_of_elements()); - EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); +TEST(Record, toNode_userDefined) +{ + ID id {"the id", IDType::Local}; + Record record {id, "my type"}; + conduit::Node userDef; + userDef["k1"] = "v1"; + userDef["k2"] = 123; + std::vector int_vals {1, 2, 3}; + userDef["k3"] = int_vals; + record.setUserDefinedContent(userDef); + + auto asNode = record.toNode(); + + auto userDefined = asNode[EXPECTED_USER_DEFINED_KEY]; + EXPECT_EQ("v1", userDefined["k1"].as_string()); + EXPECT_EQ(123, userDefined["k2"].as_int()); + auto int_array = userDefined["k3"].as_int_ptr(); + std::vector udef_ints( + int_array, + int_array + userDefined["k3"].dtype().number_of_elements()); + EXPECT_THAT(udef_ints, ElementsAre(1, 2, 3)); } -TEST(Record, toNode_data) { - ID id{"the id", IDType::Local}; - Record record{id, "my type"}; - std::string name1 = "name1"; - std::string value1 = "value1"; - Datum datum1 = Datum{value1}; - datum1.setUnits("some units"); - datum1.setTags({"tag1"}); - record.add(name1, datum1); - std::string name2 = "name2"; - record.add(name2, Datum{2.}); - auto asNode = record.toNode(); - ASSERT_EQ(2u, asNode[EXPECTED_DATA_KEY].number_of_children()); - EXPECT_EQ("value1", asNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); - EXPECT_EQ("some units", asNode[EXPECTED_DATA_KEY][name1]["units"].as_string()); - EXPECT_EQ("tag1", asNode[EXPECTED_DATA_KEY][name1]["tags"][0].as_string()); - - EXPECT_THAT(asNode[EXPECTED_DATA_KEY][name2]["value"].as_double(), - DoubleEq(2.)); - EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["units"].dtype().is_empty()); - EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["tags"].dtype().is_empty()); +TEST(Record, toNode_data) +{ + ID id {"the id", IDType::Local}; + Record record {id, "my type"}; + std::string name1 = "name1"; + std::string value1 = "value1"; + Datum datum1 = Datum {value1}; + datum1.setUnits("some units"); + datum1.setTags({"tag1"}); + record.add(name1, datum1); + std::string name2 = "name2"; + record.add(name2, Datum {2.}); + auto asNode = record.toNode(); + ASSERT_EQ(2u, asNode[EXPECTED_DATA_KEY].number_of_children()); + EXPECT_EQ("value1", asNode[EXPECTED_DATA_KEY][name1]["value"].as_string()); + EXPECT_EQ("some units", asNode[EXPECTED_DATA_KEY][name1]["units"].as_string()); + EXPECT_EQ("tag1", asNode[EXPECTED_DATA_KEY][name1]["tags"][0].as_string()); + + EXPECT_THAT(asNode[EXPECTED_DATA_KEY][name2]["value"].as_double(), + DoubleEq(2.)); + EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["units"].dtype().is_empty()); + EXPECT_TRUE(asNode[EXPECTED_DATA_KEY][name2]["tags"].dtype().is_empty()); } -TEST(Record, toNode_dataWithSlashes) { - ID id{"the id", IDType::Local}; - Record record{id, "my type"}; - std::string name = "name/with/slashes"; - std::string value = "the value"; - Datum datum = Datum{value}; - record.add(name, datum); - auto asNode = record.toNode(); - ASSERT_EQ(1u, asNode[EXPECTED_DATA_KEY].number_of_children()); - EXPECT_EQ("the value", asNode[EXPECTED_DATA_KEY].child(name)["value"].as_string()); +TEST(Record, toNode_dataWithSlashes) +{ + ID id {"the id", IDType::Local}; + Record record {id, "my type"}; + std::string name = "name/with/slashes"; + std::string value = "the value"; + Datum datum = Datum {value}; + record.add(name, datum); + auto asNode = record.toNode(); + ASSERT_EQ(1u, asNode[EXPECTED_DATA_KEY].number_of_children()); + EXPECT_EQ("the value", + asNode[EXPECTED_DATA_KEY].child(name)["value"].as_string()); } -TEST(Record, toNode_files) { - ID id{"the id", IDType::Local}; - Record record{id, "my type"}; - std::string uri1 = "a/file/path/foo.png"; - std::string uri2 = "uri2"; - File file{uri1}; - file.setMimeType("mt1"); - record.add(file); - record.add(File{uri2}); - // Identical uris should overwrite - record.add(File{uri2}); - auto asNode = record.toNode(); - ASSERT_EQ(2u, asNode[EXPECTED_FILES_KEY].number_of_children()); - auto &child_with_slashes = asNode[EXPECTED_FILES_KEY].child(uri1); - EXPECT_EQ("mt1", child_with_slashes["mimetype"].as_string()); - EXPECT_TRUE(asNode[EXPECTED_FILES_KEY][uri2]["mimetype"].dtype().is_empty()); +TEST(Record, toNode_files) +{ + ID id {"the id", IDType::Local}; + Record record {id, "my type"}; + std::string uri1 = "a/file/path/foo.png"; + std::string uri2 = "uri2"; + File file {uri1}; + file.setMimeType("mt1"); + record.add(file); + record.add(File {uri2}); + // Identical uris should overwrite + record.add(File {uri2}); + auto asNode = record.toNode(); + ASSERT_EQ(2u, asNode[EXPECTED_FILES_KEY].number_of_children()); + auto &child_with_slashes = asNode[EXPECTED_FILES_KEY].child(uri1); + EXPECT_EQ("mt1", child_with_slashes["mimetype"].as_string()); + EXPECT_TRUE(asNode[EXPECTED_FILES_KEY][uri2]["mimetype"].dtype().is_empty()); } -TEST(Record, toNode_curveSets) { - ID id{"the id", IDType::Local}; - Record record{id, "my type"}; - CurveSet cs{"myCurveSet/with/slash"}; - cs.addIndependentCurve(Curve{"myCurve", {1, 2, 3}}); - record.add(cs); - auto expected = R"({ +TEST(Record, toNode_curveSets) +{ + ID id {"the id", IDType::Local}; + Record record {id, "my type"}; + CurveSet cs {"myCurveSet/with/slash"}; + cs.addIndependentCurve(Curve {"myCurve", {1, 2, 3}}); + record.add(cs); + auto expected = R"({ "local_id": "the id", "type": "my type", "curve_sets": { @@ -413,53 +444,54 @@ TEST(Record, toNode_curveSets) { } } })"; - EXPECT_THAT(record.toNode(), MatchesJson(expected)); + EXPECT_THAT(record.toNode(), MatchesJson(expected)); } -TEST(RecordLoader, load_missingLoader) { - RecordLoader loader; - conduit::Node asNode; - asNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; - asNode[EXPECTED_TYPE_KEY] = "unknownType"; - auto loaded = loader.load(asNode); - auto &actualType = typeid(*loaded); - EXPECT_EQ(typeid(Record), actualType) << "Type was " << actualType.name(); +TEST(RecordLoader, load_missingLoader) +{ + RecordLoader loader; + conduit::Node asNode; + asNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + asNode[EXPECTED_TYPE_KEY] = "unknownType"; + auto loaded = loader.load(asNode); + auto &actualType = typeid(*loaded); + EXPECT_EQ(typeid(Record), actualType) << "Type was " << actualType.name(); } -TEST(RecordLoader, load_loaderPresent) { - RecordLoader loader; - EXPECT_FALSE(loader.canLoad("TestInt")); - EXPECT_FALSE(loader.canLoad("TestString")); - - loader.addTypeLoader("TestInt", - [](conduit::Node const &value) { - return std::make_unique>(value); - }); - EXPECT_TRUE(loader.canLoad("TestInt")); - - loader.addTypeLoader("TestString", - [](conduit::Node const &value) { - return std::make_unique>(value); - }); - EXPECT_TRUE(loader.canLoad("TestString")); - - conduit::Node asNode; - asNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; - asNode[EXPECTED_TYPE_KEY] = "TestString"; - asNode[TEST_RECORD_VALUE_KEY] = "The value"; - auto loaded = loader.load(asNode); - auto testObjPointer = dynamic_cast *>(loaded.get()); - ASSERT_NE(nullptr, testObjPointer); - EXPECT_EQ("The value", testObjPointer->getValue()); - EXPECT_EQ("TestString", testObjPointer->getType()); +TEST(RecordLoader, load_loaderPresent) +{ + RecordLoader loader; + EXPECT_FALSE(loader.canLoad("TestInt")); + EXPECT_FALSE(loader.canLoad("TestString")); + + loader.addTypeLoader("TestInt", [](conduit::Node const &value) { + return std::make_unique>(value); + }); + EXPECT_TRUE(loader.canLoad("TestInt")); + + loader.addTypeLoader("TestString", [](conduit::Node const &value) { + return std::make_unique>(value); + }); + EXPECT_TRUE(loader.canLoad("TestString")); + + conduit::Node asNode; + asNode[EXPECTED_GLOBAL_ID_KEY] = "the ID"; + asNode[EXPECTED_TYPE_KEY] = "TestString"; + asNode[TEST_RECORD_VALUE_KEY] = "The value"; + auto loaded = loader.load(asNode); + auto testObjPointer = dynamic_cast *>(loaded.get()); + ASSERT_NE(nullptr, testObjPointer); + EXPECT_EQ("The value", testObjPointer->getValue()); + EXPECT_EQ("TestString", testObjPointer->getType()); } -TEST(RecordLoader, createRecordLoaderWithAllKnownTypes) { - RecordLoader loader = createRecordLoaderWithAllKnownTypes(); - EXPECT_TRUE(loader.canLoad("run")); +TEST(RecordLoader, createRecordLoaderWithAllKnownTypes) +{ + RecordLoader loader = createRecordLoaderWithAllKnownTypes(); + EXPECT_TRUE(loader.canLoad("run")); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/sina_Relationship.cpp b/src/axom/sina/tests/sina_Relationship.cpp index 60220cd754..33109de8e2 100644 --- a/src/axom/sina/tests/sina_Relationship.cpp +++ b/src/axom/sina/tests/sina_Relationship.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include "gtest/gtest.h" @@ -28,135 +27,155 @@ char const EXPECTED_PREDICATE_KEY[] = "predicate"; using ::testing::HasSubstr; -TEST(Relationship, create) { - std::string subjectID = "the subject"; - std::string objectID = "the object"; - std::string predicate = "is somehow related to"; - - Relationship relationship{ID{subjectID, IDType::Global}, predicate, - ID{objectID, IDType::Local}}; - - EXPECT_EQ(subjectID, relationship.getSubject().getId()); - EXPECT_EQ(IDType::Global, relationship.getSubject().getType()); - EXPECT_EQ(objectID, relationship.getObject().getId()); - EXPECT_EQ(IDType::Local, relationship.getObject().getType()); - EXPECT_EQ(predicate, relationship.getPredicate()); +TEST(Relationship, create) +{ + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; + + Relationship relationship {ID {subjectID, IDType::Global}, + predicate, + ID {objectID, IDType::Local}}; + + EXPECT_EQ(subjectID, relationship.getSubject().getId()); + EXPECT_EQ(IDType::Global, relationship.getSubject().getType()); + EXPECT_EQ(objectID, relationship.getObject().getId()); + EXPECT_EQ(IDType::Local, relationship.getObject().getType()); + EXPECT_EQ(predicate, relationship.getPredicate()); } -TEST(Relationship, create_fromNode_validGlobalIDs) { - std::string subjectID = "the subject"; - std::string objectID = "the object"; - std::string predicate = "is somehow related to"; +TEST(Relationship, create_fromNode_validGlobalIDs) +{ + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; - conduit::Node asNode; - asNode[EXPECTED_GLOBAL_SUBJECT_ID_KEY] = subjectID; - asNode[EXPECTED_GLOBAL_OBJECT_ID_KEY] = objectID; - asNode[EXPECTED_PREDICATE_KEY] = predicate; + conduit::Node asNode; + asNode[EXPECTED_GLOBAL_SUBJECT_ID_KEY] = subjectID; + asNode[EXPECTED_GLOBAL_OBJECT_ID_KEY] = objectID; + asNode[EXPECTED_PREDICATE_KEY] = predicate; - Relationship relationship{asNode}; + Relationship relationship {asNode}; - EXPECT_EQ(subjectID, relationship.getSubject().getId()); - EXPECT_EQ(IDType::Global, relationship.getSubject().getType()); - EXPECT_EQ(objectID, relationship.getObject().getId()); - EXPECT_EQ(IDType::Global, relationship.getObject().getType()); - EXPECT_EQ(predicate, relationship.getPredicate()); + EXPECT_EQ(subjectID, relationship.getSubject().getId()); + EXPECT_EQ(IDType::Global, relationship.getSubject().getType()); + EXPECT_EQ(objectID, relationship.getObject().getId()); + EXPECT_EQ(IDType::Global, relationship.getObject().getType()); + EXPECT_EQ(predicate, relationship.getPredicate()); } -TEST(Relationship, create_from_validLocalIDs) { - std::string subjectID = "the subject"; - std::string objectID = "the object"; - std::string predicate = "is somehow related to"; +TEST(Relationship, create_from_validLocalIDs) +{ + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; - conduit::Node asNode; - asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY] = subjectID; - asNode[EXPECTED_LOCAL_OBJECT_ID_KEY] = objectID; - asNode[EXPECTED_PREDICATE_KEY] = predicate; + conduit::Node asNode; + asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY] = subjectID; + asNode[EXPECTED_LOCAL_OBJECT_ID_KEY] = objectID; + asNode[EXPECTED_PREDICATE_KEY] = predicate; - Relationship relationship{asNode}; + Relationship relationship {asNode}; - EXPECT_EQ(subjectID, relationship.getSubject().getId()); - EXPECT_EQ(IDType::Local, relationship.getSubject().getType()); - EXPECT_EQ(objectID, relationship.getObject().getId()); - EXPECT_EQ(IDType::Local, relationship.getObject().getType()); - EXPECT_EQ(predicate, relationship.getPredicate()); + EXPECT_EQ(subjectID, relationship.getSubject().getId()); + EXPECT_EQ(IDType::Local, relationship.getSubject().getType()); + EXPECT_EQ(objectID, relationship.getObject().getId()); + EXPECT_EQ(IDType::Local, relationship.getObject().getType()); + EXPECT_EQ(predicate, relationship.getPredicate()); } -TEST(Relationship, create_fromNode_missingSubect) { - conduit::Node asNode; - asNode[EXPECTED_LOCAL_OBJECT_ID_KEY] = "the object"; - asNode[EXPECTED_PREDICATE_KEY] = "some predicate"; - try { - Relationship relationship{asNode}; - FAIL() << "Should have gotten an exception about a missing subject"; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_LOCAL_SUBJECT_ID_KEY)); - EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_GLOBAL_SUBJECT_ID_KEY)); - } +TEST(Relationship, create_fromNode_missingSubect) +{ + conduit::Node asNode; + asNode[EXPECTED_LOCAL_OBJECT_ID_KEY] = "the object"; + asNode[EXPECTED_PREDICATE_KEY] = "some predicate"; + try + { + Relationship relationship {asNode}; + FAIL() << "Should have gotten an exception about a missing subject"; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_LOCAL_SUBJECT_ID_KEY)); + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_GLOBAL_SUBJECT_ID_KEY)); + } } -TEST(Relationship, create_fromNode_missingObject) { +TEST(Relationship, create_fromNode_missingObject) +{ conduit::Node asNode; asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY] = "the subject"; asNode[EXPECTED_PREDICATE_KEY] = "some predicate"; - try { - Relationship relationship{asNode}; - FAIL() << "Should have gotten an exception about a missing object"; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_LOCAL_OBJECT_ID_KEY)); - EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_GLOBAL_OBJECT_ID_KEY)); - } + try + { + Relationship relationship {asNode}; + FAIL() << "Should have gotten an exception about a missing object"; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_LOCAL_OBJECT_ID_KEY)); + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_GLOBAL_OBJECT_ID_KEY)); + } } -TEST(Relationship, create_fromNode_missingPredicate) { +TEST(Relationship, create_fromNode_missingPredicate) +{ conduit::Node asNode; asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY] = "the subject"; asNode[EXPECTED_LOCAL_OBJECT_ID_KEY] = "the object"; - try { - Relationship relationship{asNode}; - FAIL() << "Should have gotten an exception about a missing predicate"; - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_PREDICATE_KEY)); - EXPECT_THAT(expected.what(), HasSubstr("Relationship")); - } + try + { + Relationship relationship {asNode}; + FAIL() << "Should have gotten an exception about a missing predicate"; + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_PREDICATE_KEY)); + EXPECT_THAT(expected.what(), HasSubstr("Relationship")); + } } -TEST(Relationship, toNode_localIds) { - std::string subjectID = "the subject"; - std::string objectID = "the object"; - std::string predicate = "is somehow related to"; +TEST(Relationship, toNode_localIds) +{ + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; - Relationship relationship{ID{subjectID, IDType::Local}, predicate, - ID{objectID, IDType::Local}}; + Relationship relationship {ID {subjectID, IDType::Local}, + predicate, + ID {objectID, IDType::Local}}; - conduit::Node asNode = relationship.toNode(); + conduit::Node asNode = relationship.toNode(); - EXPECT_EQ(subjectID, asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY].as_string()); - EXPECT_EQ(objectID, asNode[EXPECTED_LOCAL_OBJECT_ID_KEY].as_string()); - EXPECT_EQ(predicate, asNode[EXPECTED_PREDICATE_KEY].as_string()); - EXPECT_FALSE(asNode.has_child(EXPECTED_GLOBAL_SUBJECT_ID_KEY)); - EXPECT_FALSE(asNode.has_child(EXPECTED_GLOBAL_OBJECT_ID_KEY)); + EXPECT_EQ(subjectID, asNode[EXPECTED_LOCAL_SUBJECT_ID_KEY].as_string()); + EXPECT_EQ(objectID, asNode[EXPECTED_LOCAL_OBJECT_ID_KEY].as_string()); + EXPECT_EQ(predicate, asNode[EXPECTED_PREDICATE_KEY].as_string()); + EXPECT_FALSE(asNode.has_child(EXPECTED_GLOBAL_SUBJECT_ID_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_GLOBAL_OBJECT_ID_KEY)); } -TEST(Relationship, toNode_globalIds) { - std::string subjectID = "the subject"; - std::string objectID = "the object"; - std::string predicate = "is somehow related to"; +TEST(Relationship, toNode_globalIds) +{ + std::string subjectID = "the subject"; + std::string objectID = "the object"; + std::string predicate = "is somehow related to"; - Relationship relationship{ID{subjectID, IDType::Global}, predicate, - ID{objectID, IDType::Global}}; + Relationship relationship {ID {subjectID, IDType::Global}, + predicate, + ID {objectID, IDType::Global}}; - conduit::Node asNode = relationship.toNode(); + conduit::Node asNode = relationship.toNode(); - EXPECT_EQ(subjectID, asNode[EXPECTED_GLOBAL_SUBJECT_ID_KEY].as_string()); - EXPECT_EQ(objectID, asNode[EXPECTED_GLOBAL_OBJECT_ID_KEY].as_string()); - EXPECT_EQ(predicate, asNode[EXPECTED_PREDICATE_KEY].as_string()); - EXPECT_FALSE(asNode.has_child(EXPECTED_LOCAL_SUBJECT_ID_KEY)); - EXPECT_FALSE(asNode.has_child(EXPECTED_LOCAL_OBJECT_ID_KEY)); + EXPECT_EQ(subjectID, asNode[EXPECTED_GLOBAL_SUBJECT_ID_KEY].as_string()); + EXPECT_EQ(objectID, asNode[EXPECTED_GLOBAL_OBJECT_ID_KEY].as_string()); + EXPECT_EQ(predicate, asNode[EXPECTED_PREDICATE_KEY].as_string()); + EXPECT_FALSE(asNode.has_child(EXPECTED_LOCAL_SUBJECT_ID_KEY)); + EXPECT_FALSE(asNode.has_child(EXPECTED_LOCAL_OBJECT_ID_KEY)); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace \ No newline at end of file +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom \ No newline at end of file diff --git a/src/axom/sina/tests/sina_Run.cpp b/src/axom/sina/tests/sina_Run.cpp index 6ab15ce7c5..f843bc02e5 100644 --- a/src/axom/sina/tests/sina_Run.cpp +++ b/src/axom/sina/tests/sina_Run.cpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #include #include "gtest/gtest.h" @@ -32,73 +31,79 @@ char const EXPECTED_USER_KEY[] = "user"; // Throughout, we have to use "axom::sina::Run" instead of just "Run" due to // a conflict with the Run() function in gtest -TEST(Run, create_fromnode_valid) { - conduit::Node originNode; - originNode[EXPECTED_TYPE_KEY] = "run"; - originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; - originNode[EXPECTED_APPLICATION_KEY] = "the app"; - originNode[EXPECTED_VERSION_KEY] = "1.2.3"; - originNode[EXPECTED_USER_KEY] = "jdoe"; - axom::sina::Run run{originNode}; - EXPECT_EQ("run", run.getType()); - EXPECT_EQ("the id", run.getId().getId()); - EXPECT_EQ(IDType::Global, run.getId().getType()); - EXPECT_EQ("the app", run.getApplication()); - EXPECT_EQ("1.2.3", run.getVersion()); - EXPECT_EQ("jdoe", run.getUser()); +TEST(Run, create_fromnode_valid) +{ + conduit::Node originNode; + originNode[EXPECTED_TYPE_KEY] = "run"; + originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; + originNode[EXPECTED_APPLICATION_KEY] = "the app"; + originNode[EXPECTED_VERSION_KEY] = "1.2.3"; + originNode[EXPECTED_USER_KEY] = "jdoe"; + axom::sina::Run run {originNode}; + EXPECT_EQ("run", run.getType()); + EXPECT_EQ("the id", run.getId().getId()); + EXPECT_EQ(IDType::Global, run.getId().getType()); + EXPECT_EQ("the app", run.getApplication()); + EXPECT_EQ("1.2.3", run.getVersion()); + EXPECT_EQ("jdoe", run.getUser()); } -TEST(Run, create_fromNode_missingApplication) { - conduit::Node originNode; - originNode[EXPECTED_TYPE_KEY] = "run"; - originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; - originNode[EXPECTED_VERSION_KEY] = "1.2.3"; - originNode[EXPECTED_USER_KEY] = "jdoe"; - try { - axom::sina::Run run{originNode}; - FAIL() << "Application should be missing, but is " - << run.getApplication(); - } catch (std::invalid_argument const &expected) { - EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_APPLICATION_KEY)); - } +TEST(Run, create_fromNode_missingApplication) +{ + conduit::Node originNode; + originNode[EXPECTED_TYPE_KEY] = "run"; + originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; + originNode[EXPECTED_VERSION_KEY] = "1.2.3"; + originNode[EXPECTED_USER_KEY] = "jdoe"; + try + { + axom::sina::Run run {originNode}; + FAIL() << "Application should be missing, but is " << run.getApplication(); + } + catch(std::invalid_argument const &expected) + { + EXPECT_THAT(expected.what(), HasSubstr(EXPECTED_APPLICATION_KEY)); + } } -TEST(Run, toNode) { - ID id{"the id", IDType::Global}; - axom::sina::Run run{id, "the app", "1.2.3", "jdoe"}; - auto asNode = run.toNode(); - EXPECT_TRUE(asNode.dtype().is_object()); - EXPECT_EQ("run", asNode[EXPECTED_TYPE_KEY].as_string()); - EXPECT_EQ("the id", asNode[EXPECTED_GLOBAL_ID_KEY].as_string()); - EXPECT_TRUE(asNode[EXPECTED_LOCAL_ID_KEY].dtype().is_empty()); - EXPECT_EQ("the app", asNode[EXPECTED_APPLICATION_KEY].as_string()); - EXPECT_EQ("1.2.3", asNode[EXPECTED_VERSION_KEY].as_string()); - EXPECT_EQ("jdoe", asNode[EXPECTED_USER_KEY].as_string()); +TEST(Run, toNode) +{ + ID id {"the id", IDType::Global}; + axom::sina::Run run {id, "the app", "1.2.3", "jdoe"}; + auto asNode = run.toNode(); + EXPECT_TRUE(asNode.dtype().is_object()); + EXPECT_EQ("run", asNode[EXPECTED_TYPE_KEY].as_string()); + EXPECT_EQ("the id", asNode[EXPECTED_GLOBAL_ID_KEY].as_string()); + EXPECT_TRUE(asNode[EXPECTED_LOCAL_ID_KEY].dtype().is_empty()); + EXPECT_EQ("the app", asNode[EXPECTED_APPLICATION_KEY].as_string()); + EXPECT_EQ("1.2.3", asNode[EXPECTED_VERSION_KEY].as_string()); + EXPECT_EQ("jdoe", asNode[EXPECTED_USER_KEY].as_string()); } -TEST(Run, addRunLoader) { - conduit::Node originNode; - originNode[EXPECTED_TYPE_KEY] = "run"; - originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; - originNode[EXPECTED_APPLICATION_KEY] = "the app"; - originNode[EXPECTED_VERSION_KEY] = "1.2.3"; - originNode[EXPECTED_USER_KEY] = "jdoe"; +TEST(Run, addRunLoader) +{ + conduit::Node originNode; + originNode[EXPECTED_TYPE_KEY] = "run"; + originNode[EXPECTED_GLOBAL_ID_KEY] = "the id"; + originNode[EXPECTED_APPLICATION_KEY] = "the app"; + originNode[EXPECTED_VERSION_KEY] = "1.2.3"; + originNode[EXPECTED_USER_KEY] = "jdoe"; - RecordLoader loader; - addRunLoader(loader); + RecordLoader loader; + addRunLoader(loader); - auto record = loader.load(originNode); - auto run = dynamic_cast(record.get()); - ASSERT_NE(nullptr, run); - EXPECT_EQ("run", run->getType()); - EXPECT_EQ("the id", run->getId().getId()); - EXPECT_EQ(IDType::Global, run->getId().getType()); - EXPECT_EQ("the app", run->getApplication()); - EXPECT_EQ("1.2.3", run->getVersion()); - EXPECT_EQ("jdoe", run->getUser()); + auto record = loader.load(originNode); + auto run = dynamic_cast(record.get()); + ASSERT_NE(nullptr, run); + EXPECT_EQ("run", run->getType()); + EXPECT_EQ("the id", run->getId().getId()); + EXPECT_EQ(IDType::Global, run->getId().getType()); + EXPECT_EQ("the app", run->getApplication()); + EXPECT_EQ("1.2.3", run->getVersion()); + EXPECT_EQ("jdoe", run->getUser()); } -} // end nameless namespace -} // end testing namespace -} // end sina namespace -} // end axom namespace +} // namespace +} // namespace testing +} // namespace sina +} // namespace axom From 917aa2719b11c6aebe91030e0211cd56014b2ad0 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 1 Aug 2024 12:16:02 -0700 Subject: [PATCH 28/60] disable sina for osx_gcc and windows azure tests --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f8f0a25f49..11680cde18 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -74,11 +74,11 @@ jobs: HOST_CONFIG: 'clang@14.0.0' osx_gcc: VM_ImageName: 'macos-12' - CMAKE_EXTRA_FLAGS: '-DAXOM_ENABLE_SIDRE:BOOL=OFF -DAXOM_ENABLE_INLET:BOOL=OFF -DAXOM_ENABLE_KLEE:BOOL=OFF' + CMAKE_EXTRA_FLAGS: '-DAXOM_ENABLE_SIDRE:BOOL=OFF -DAXOM_ENABLE_INLET:BOOL=OFF -DAXOM_ENABLE_KLEE:BOOL=OFF -DAXOM_ENABLE_SINA:BOOL=OFF' TEST_TARGET: 'osx_gcc' windows: VM_ImageName: 'windows-2019' - CMAKE_EXTRA_FLAGS: '-DAXOM_ENABLE_SIDRE:BOOL=OFF -DAXOM_ENABLE_INLET:BOOL=OFF -DAXOM_ENABLE_KLEE:BOOL=OFF' + CMAKE_EXTRA_FLAGS: '-DAXOM_ENABLE_SIDRE:BOOL=OFF -DAXOM_ENABLE_INLET:BOOL=OFF -DAXOM_ENABLE_KLEE:BOOL=OFF -DAXOM_ENABLE_SINA:BOOL=OFF' TEST_TARGET: 'win_vs' pool: From b9b415b8480a9e0874dba627fc410acc41aed000 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 1 Aug 2024 16:49:24 -0700 Subject: [PATCH 29/60] add custom sina matchers so that blueos will build --- src/axom/sina/tests/CMakeLists.txt | 8 ++-- src/axom/sina/tests/ConduitTestUtils.cpp | 30 ------------- src/axom/sina/tests/ConduitTestUtils.hpp | 45 -------------------- src/axom/sina/tests/SinaMatchers.cpp | 54 ++++++++++++++++++++++++ src/axom/sina/tests/SinaMatchers.hpp | 49 +++++++++++++++++++++ src/axom/sina/tests/sina_ConduitUtil.cpp | 2 +- src/axom/sina/tests/sina_Curve.cpp | 10 ++--- src/axom/sina/tests/sina_CurveSet.cpp | 10 ++--- src/axom/sina/tests/sina_DataHolder.cpp | 11 +++-- src/axom/sina/tests/sina_Record.cpp | 6 +-- 10 files changed, 127 insertions(+), 98 deletions(-) delete mode 100644 src/axom/sina/tests/ConduitTestUtils.cpp delete mode 100644 src/axom/sina/tests/ConduitTestUtils.hpp create mode 100644 src/axom/sina/tests/SinaMatchers.cpp create mode 100644 src/axom/sina/tests/SinaMatchers.hpp diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index 583d22c764..ee4defd116 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -39,11 +39,11 @@ if (ENABLE_GMOCK) # Define Sina test utility sources and headers set(sina_test_utils_sources - ConduitTestUtils.cpp + SinaMatchers.cpp TestRecord.cpp ) set(sina_test_utils_headers - ConduitTestUtils.hpp + SinaMatchers.hpp TestRecord.hpp ) @@ -74,6 +74,8 @@ if (ENABLE_GMOCK) sina_Run.cpp ) + set(sina_gmock_depends_on sina_test_utils) + # Add tests using Adiak if necessary and Adiak dependency blt_list_append( TO gmock_sina_tests ELEMENTS sina_AdiakWriter.cpp IF AXOM_USE_ADIAK) blt_list_append( TO sina_gmock_depends_on ELEMENTS adiak::adiak IF AXOM_USE_ADIAK ) @@ -83,7 +85,7 @@ if (ENABLE_GMOCK) axom_add_executable(NAME ${test_name}_test SOURCES ${test} OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} - DEPENDS_ON sina_test_utils + DEPENDS_ON ${sina_gmock_depends_on} FOLDER axom/sina/tests ) diff --git a/src/axom/sina/tests/ConduitTestUtils.cpp b/src/axom/sina/tests/ConduitTestUtils.cpp deleted file mode 100644 index 2abb1edecc..0000000000 --- a/src/axom/sina/tests/ConduitTestUtils.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2017-2024, 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/tests/ConduitTestUtils.hpp" - -namespace axom -{ -namespace sina -{ -namespace testing -{ - -conduit::Node parseJsonValue(std::string const &valueAsString) -{ - // If we just try to do node.parse(valueAsString, "json"), then passing - // in strings does not work. We need to create a document with a key - // so that valueAsString can be parsed as a value. - conduit::Node node; - std::string fullContents = "{\"TEST_KEY\": "; - fullContents += valueAsString; - fullContents += "}"; - node.parse(fullContents, "json"); - return node.child("TEST_KEY"); -} - -} // namespace testing -} // namespace sina -} // namespace axom diff --git a/src/axom/sina/tests/ConduitTestUtils.hpp b/src/axom/sina/tests/ConduitTestUtils.hpp deleted file mode 100644 index 70c2c3af46..0000000000 --- a/src/axom/sina/tests/ConduitTestUtils.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level LICENSE file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#ifndef SINA_CONDUITTESTUTILS_HPP -#define SINA_CONDUITTESTUTILS_HPP - -#include - -#include "gmock/gmock.h" - -#include "conduit.hpp" - -namespace axom -{ -namespace sina -{ -namespace testing -{ - -/** - * Parse a JSON value. - * - * @param valueAsString the value as a string - * @return the value as a Conduit node - */ -conduit::Node parseJsonValue(std::string const &valueAsString); - -// A matcher which verifies that a given Conduit node produces the expected -// JSON string -MATCHER_P(MatchesJson, expectedJsonString, "") -{ - conduit::Node expected = parseJsonValue(expectedJsonString); - *result_listener << "Given node is " << arg.to_json_default(); - conduit::Node diff; - bool differ = expected.diff(arg, diff); - return !differ; -} - -} // namespace testing -} // namespace sina -} // namespace axom - -#endif //SINA_CONDUITTESTUTILS_HPP diff --git a/src/axom/sina/tests/SinaMatchers.cpp b/src/axom/sina/tests/SinaMatchers.cpp new file mode 100644 index 0000000000..b2eb769db1 --- /dev/null +++ b/src/axom/sina/tests/SinaMatchers.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "axom/sina/tests/SinaMatchers.hpp" +#include + +namespace axom +{ +namespace sina +{ +namespace testing +{ + +conduit::Node parseJsonValue(const std::string &valueAsString) +{ + // If we just try to do node.parse(valueAsString, "json"), then passing + // in strings does not work. We need to create a document with a key + // so that valueAsString can be parsed as a value. + conduit::Node node; + std::string fullContents = "{\"TEST_KEY\": "; + fullContents += valueAsString; + fullContents += "}"; + node.parse(fullContents, "json"); + return node.child("TEST_KEY"); +} + +// Define the MatchesJson class +MatchesJson::MatchesJson(const std::string &expectedJsonString) + : expectedJsonString_(expectedJsonString) +{ } + +bool MatchesJson::MatchAndExplain(const conduit::Node& node, ::testing::MatchResultListener* listener) const +{ + conduit::Node expected = parseJsonValue(expectedJsonString_); + *listener << "Given node is " << node.to_json_default(); + conduit::Node diff; + bool differ = expected.diff(node, diff); + return !differ; +} + +void MatchesJson::DescribeTo(std::ostream* os) const +{ + *os << "matches JSON: " << expectedJsonString_; +} + +void MatchesJson::DescribeNegationTo(std::ostream* os) const +{ + *os << "does not match JSON: " << expectedJsonString_; +} + +} // namespace testing +} // namespace sina +} // namespace axom diff --git a/src/axom/sina/tests/SinaMatchers.hpp b/src/axom/sina/tests/SinaMatchers.hpp new file mode 100644 index 0000000000..3221f074f0 --- /dev/null +++ b/src/axom/sina/tests/SinaMatchers.hpp @@ -0,0 +1,49 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_SINAMATCHERS_HPP +#define AXOM_SINAMATCHERS_HPP + +#include +#include "conduit.hpp" + +// Note: LLNL's blueos complained about using GMock's MATCHER_P macros, as originally implemented. +// This required explicitly generating equivalent classes for the Matcher functionality. + +namespace axom +{ +namespace sina +{ +namespace testing +{ + +// Function to parse JSON value +conduit::Node parseJsonValue(const std::string &valueAsString); + +// Matcher class +class MatchesJson { +public: + explicit MatchesJson(const std::string &expectedJsonString); + + bool MatchAndExplain(const conduit::Node& node, ::testing::MatchResultListener* listener) const; + + void DescribeTo(std::ostream* os) const; + + void DescribeNegationTo(std::ostream* os) const; + +private: + const std::string expectedJsonString_; +}; + +// Helper function to create the matcher +inline ::testing::PolymorphicMatcher MatchesJsonMatcher(const std::string &expectedJsonString) +{ + return ::testing::MakePolymorphicMatcher(MatchesJson(expectedJsonString)); +} + +} // namespace testing +} // namespace sina +} // namespace axom + +#endif //AXOM_SINAMATCHERS_HPP \ No newline at end of file diff --git a/src/axom/sina/tests/sina_ConduitUtil.cpp b/src/axom/sina/tests/sina_ConduitUtil.cpp index 145d7adc3d..4743bcb278 100644 --- a/src/axom/sina/tests/sina_ConduitUtil.cpp +++ b/src/axom/sina/tests/sina_ConduitUtil.cpp @@ -9,7 +9,7 @@ #include "gmock/gmock.h" #include "axom/sina/core/ConduitUtil.hpp" -#include "axom/sina/tests/ConduitTestUtils.hpp" +#include "axom/sina/tests/SinaMatchers.hpp" namespace axom { diff --git a/src/axom/sina/tests/sina_Curve.cpp b/src/axom/sina/tests/sina_Curve.cpp index 00fec7cad5..54d5d7b078 100644 --- a/src/axom/sina/tests/sina_Curve.cpp +++ b/src/axom/sina/tests/sina_Curve.cpp @@ -8,7 +8,7 @@ #include "axom/sina/core/Curve.hpp" #include "axom/sina/core/ConduitUtil.hpp" -#include "axom/sina/tests/ConduitTestUtils.hpp" +#include "axom/sina/tests/SinaMatchers.hpp" namespace axom { @@ -100,10 +100,10 @@ TEST(Curve, createFromNode_optionalFields) TEST(Curve, toNode_requiredOnly) { Curve const curve {"theName", {1, 2, 3, 4}}; - auto expected = (R"({ + std::string expected = (R"({ "value": [1.0, 2.0, 3.0, 4.0] })"); - EXPECT_THAT(curve.toNode(), MatchesJson(expected)); + EXPECT_THAT(curve.toNode(), MatchesJsonMatcher(expected)); } TEST(Curve, toNode_optionalFields) @@ -111,12 +111,12 @@ TEST(Curve, toNode_optionalFields) Curve curve {"theName", {1, 2, 3, 4}}; curve.setUnits("cm"); curve.setTags({"t1", "t2", "t3"}); - auto expected = R"({ + std::string expected = R"({ "value": [1.0, 2.0, 3.0, 4.0], "units": "cm", "tags": ["t1", "t2", "t3"] })"; - EXPECT_THAT(curve.toNode(), MatchesJson(expected)); + EXPECT_THAT(curve.toNode(), MatchesJsonMatcher(expected)); } } // namespace diff --git a/src/axom/sina/tests/sina_CurveSet.cpp b/src/axom/sina/tests/sina_CurveSet.cpp index 6f90392a5e..7bd8d941d6 100644 --- a/src/axom/sina/tests/sina_CurveSet.cpp +++ b/src/axom/sina/tests/sina_CurveSet.cpp @@ -7,7 +7,7 @@ #include "gmock/gmock.h" #include "axom/sina/core/CurveSet.hpp" -#include "axom/sina/tests/ConduitTestUtils.hpp" +#include "axom/sina/tests/SinaMatchers.hpp" #include #include @@ -175,11 +175,11 @@ TEST(CurveSet, createFromNode_curveSetsDefined) TEST(CurveSet, toNode_empty) { CurveSet curveSet {"theName"}; - auto expected = R"({ + std::string expected = R"({ "independent": {}, "dependent": {} })"; - EXPECT_THAT(curveSet.toNode(), MatchesJson(expected)); + EXPECT_THAT(curveSet.toNode(), MatchesJsonMatcher(expected)); } TEST(CurveSet, toNode_withCurves) @@ -189,7 +189,7 @@ TEST(CurveSet, toNode_withCurves) curveSet.addIndependentCurve(Curve {"i2/with/slash", {4, 5, 6}}); curveSet.addDependentCurve(Curve {"d1", {10, 20, 30}}); curveSet.addDependentCurve(Curve {"d2/with/slash", {40, 50, 60}}); - auto expected = R"({ + std::string expected = R"({ "independent": { "i1": { "value": [1.0, 2.0, 3.0] @@ -207,7 +207,7 @@ TEST(CurveSet, toNode_withCurves) } } })"; - EXPECT_THAT(curveSet.toNode(), MatchesJson(expected)); + EXPECT_THAT(curveSet.toNode(), MatchesJsonMatcher(expected)); } } // namespace diff --git a/src/axom/sina/tests/sina_DataHolder.cpp b/src/axom/sina/tests/sina_DataHolder.cpp index bd389bc524..0a4869ade5 100644 --- a/src/axom/sina/tests/sina_DataHolder.cpp +++ b/src/axom/sina/tests/sina_DataHolder.cpp @@ -10,8 +10,7 @@ #include "gmock/gmock.h" #include "axom/sina/core/DataHolder.hpp" - -#include "axom/sina/tests/ConduitTestUtils.hpp" +#include "axom/sina/tests/SinaMatchers.hpp" namespace axom { @@ -257,7 +256,7 @@ TEST(DataHolder, toNode_curveSets) CurveSet cs {"myCurveSet/with/slash"}; cs.addIndependentCurve(Curve {"myCurve", {1, 2, 3}}); dh.add(cs); - auto expected = R"({ + std::string expected = R"({ "curve_sets": { "myCurveSet/with/slash": { "independent": { @@ -269,7 +268,7 @@ TEST(DataHolder, toNode_curveSets) } } })"; - EXPECT_THAT(dh.toNode(), MatchesJson(expected)); + EXPECT_THAT(dh.toNode(), MatchesJsonMatcher(expected)); } TEST(DataHolder, toNode_libraryData) @@ -279,7 +278,7 @@ TEST(DataHolder, toNode_libraryData) outer->add("scal", Datum {"goodbye!"}); auto inner = outer->addLibraryData("inner"); inner->add("str", Datum {"hello!"}); - auto expected = R"({ + std::string expected = R"({ "library_data": { "outer": { "library_data": { @@ -291,7 +290,7 @@ TEST(DataHolder, toNode_libraryData) } } })"; - EXPECT_THAT(dh.toNode(), MatchesJson(expected)); + EXPECT_THAT(dh.toNode(), MatchesJsonMatcher(expected)); } TEST(DataHolder, toNode_userDefined) diff --git a/src/axom/sina/tests/sina_Record.cpp b/src/axom/sina/tests/sina_Record.cpp index c8e8f9b68c..0bdb6746d0 100644 --- a/src/axom/sina/tests/sina_Record.cpp +++ b/src/axom/sina/tests/sina_Record.cpp @@ -11,7 +11,7 @@ #include "axom/sina/core/Record.hpp" -#include "axom/sina/tests/ConduitTestUtils.hpp" +#include "axom/sina/tests/SinaMatchers.hpp" #include "axom/sina/tests/TestRecord.hpp" namespace axom @@ -430,7 +430,7 @@ TEST(Record, toNode_curveSets) CurveSet cs {"myCurveSet/with/slash"}; cs.addIndependentCurve(Curve {"myCurve", {1, 2, 3}}); record.add(cs); - auto expected = R"({ + std::string expected = R"({ "local_id": "the id", "type": "my type", "curve_sets": { @@ -444,7 +444,7 @@ TEST(Record, toNode_curveSets) } } })"; - EXPECT_THAT(record.toNode(), MatchesJson(expected)); + EXPECT_THAT(record.toNode(), MatchesJsonMatcher(expected)); } TEST(RecordLoader, load_missingLoader) From 3ef59eb1eaca9aa727de206a74ef47cd2d607345 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Mon, 5 Aug 2024 10:50:58 -0700 Subject: [PATCH 30/60] remove code check call from Sina CMake and run make style --- src/axom/sina/CMakeLists.txt | 5 ----- src/axom/sina/tests/SinaMatchers.cpp | 8 +++++--- src/axom/sina/tests/SinaMatchers.hpp | 22 +++++++++++++--------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index 8dfba39f20..49fe02e78d 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -88,8 +88,3 @@ endif() if(AXOM_ENABLE_EXAMPLES) add_subdirectory(examples) endif() - -#------------------------------------------------------------------------------ -# Add code checks -#------------------------------------------------------------------------------ -axom_add_code_checks(PREFIX sina) diff --git a/src/axom/sina/tests/SinaMatchers.cpp b/src/axom/sina/tests/SinaMatchers.cpp index b2eb769db1..6538091c4c 100644 --- a/src/axom/sina/tests/SinaMatchers.cpp +++ b/src/axom/sina/tests/SinaMatchers.cpp @@ -2,6 +2,7 @@ // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) + #include "axom/sina/tests/SinaMatchers.hpp" #include @@ -12,7 +13,7 @@ namespace sina namespace testing { -conduit::Node parseJsonValue(const std::string &valueAsString) +conduit::Node parseJsonValue(const std::string& valueAsString) { // If we just try to do node.parse(valueAsString, "json"), then passing // in strings does not work. We need to create a document with a key @@ -26,11 +27,12 @@ conduit::Node parseJsonValue(const std::string &valueAsString) } // Define the MatchesJson class -MatchesJson::MatchesJson(const std::string &expectedJsonString) +MatchesJson::MatchesJson(const std::string& expectedJsonString) : expectedJsonString_(expectedJsonString) { } -bool MatchesJson::MatchAndExplain(const conduit::Node& node, ::testing::MatchResultListener* listener) const +bool MatchesJson::MatchAndExplain(const conduit::Node& node, + ::testing::MatchResultListener* listener) const { conduit::Node expected = parseJsonValue(expectedJsonString_); *listener << "Given node is " << node.to_json_default(); diff --git a/src/axom/sina/tests/SinaMatchers.hpp b/src/axom/sina/tests/SinaMatchers.hpp index 3221f074f0..289019289c 100644 --- a/src/axom/sina/tests/SinaMatchers.hpp +++ b/src/axom/sina/tests/SinaMatchers.hpp @@ -2,6 +2,7 @@ // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) + #ifndef AXOM_SINAMATCHERS_HPP #define AXOM_SINAMATCHERS_HPP @@ -19,27 +20,30 @@ namespace testing { // Function to parse JSON value -conduit::Node parseJsonValue(const std::string &valueAsString); +conduit::Node parseJsonValue(const std::string& valueAsString); // Matcher class -class MatchesJson { +class MatchesJson +{ public: - explicit MatchesJson(const std::string &expectedJsonString); + explicit MatchesJson(const std::string& expectedJsonString); - bool MatchAndExplain(const conduit::Node& node, ::testing::MatchResultListener* listener) const; + bool MatchAndExplain(const conduit::Node& node, + ::testing::MatchResultListener* listener) const; - void DescribeTo(std::ostream* os) const; + void DescribeTo(std::ostream* os) const; - void DescribeNegationTo(std::ostream* os) const; + void DescribeNegationTo(std::ostream* os) const; private: - const std::string expectedJsonString_; + const std::string expectedJsonString_; }; // Helper function to create the matcher -inline ::testing::PolymorphicMatcher MatchesJsonMatcher(const std::string &expectedJsonString) +inline ::testing::PolymorphicMatcher MatchesJsonMatcher( + const std::string& expectedJsonString) { - return ::testing::MakePolymorphicMatcher(MatchesJson(expectedJsonString)); + return ::testing::MakePolymorphicMatcher(MatchesJson(expectedJsonString)); } } // namespace testing From 5856843c6c8dbb0f0c50ba86d80672951c24fd56 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 6 Aug 2024 17:20:31 -0700 Subject: [PATCH 31/60] add compiler flag for xl fortran builds --- src/axom/sina/CMakeLists.txt | 4 ++++ src/axom/sina/examples/sina_fortran.f90 | 5 +++-- src/axom/sina/interface/sina_fortran_interface.f90 | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index 49fe02e78d..a4ebe666a2 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -54,6 +54,10 @@ if (ENABLE_FORTRAN) blt_list_append( TO sina_headers ELEMENTS interface/sina_fortran_interface.h) blt_list_append( TO sina_sources ELEMENTS interface/sina_fortran_interface.cpp interface/sina_fortran_interface.f90) + + if(CMAKE_Fortran_COMPILER_ID STREQUAL "XL") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -qextname") + endif() endif() #------------------------------------------------------------------------------ diff --git a/src/axom/sina/examples/sina_fortran.f90 b/src/axom/sina/examples/sina_fortran.f90 index ad15c9c660..8dd2a5e0d4 100644 --- a/src/axom/sina/examples/sina_fortran.f90 +++ b/src/axom/sina/examples/sina_fortran.f90 @@ -116,11 +116,12 @@ program example name = make_cstring('u_double_w_tag') tag = make_cstring('new_fancy_tag') call sina_add(name, double_val, units, tag) - - deallocate(tag) + print *, "Adding char" name = make_cstring('u_char') call sina_add(name, trim(char_val)//char(0), units, tag) + + deallocate(tag) name = make_cstring('my_curveset') call sina_add_curveset(name) diff --git a/src/axom/sina/interface/sina_fortran_interface.f90 b/src/axom/sina/interface/sina_fortran_interface.f90 index d674f26f3a..56fe05ebe6 100644 --- a/src/axom/sina/interface/sina_fortran_interface.f90 +++ b/src/axom/sina/interface/sina_fortran_interface.f90 @@ -9,7 +9,7 @@ end subroutine create_document_and_record end interface interface - + subroutine sina_add_file(file_nm, mime_type) character(*) file_nm character(*) mime_type @@ -114,5 +114,5 @@ subroutine sina_add_curve_long(name, curve, values, n, independent) end subroutine sina_add_curve_long end interface - + end module \ No newline at end of file From 64ce0ecf68c52ff002770df2df8f0cc370962420 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 8 Aug 2024 12:44:11 -0700 Subject: [PATCH 32/60] update sina description in doxygen mainpage --- src/axom/doxygen_mainpage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/doxygen_mainpage.md b/src/axom/doxygen_mainpage.md index 90711ec5ec..fd3662f7c2 100644 --- a/src/axom/doxygen_mainpage.md +++ b/src/axom/doxygen_mainpage.md @@ -14,7 +14,7 @@ Axom provides libraries that address common computer science needs. It grew fro * @subpage primaltop provides an API for geometric primitives and computational geometry tests. * @subpage questtop provides an API to query point distance and position relative to meshes. * @subpage sidretop provides a data store with hierarchical structure. -* @subpage sinatop provides an API to convert data to a common file format. +* @subpage sinatop ([S]imulation [In]sight and [A]nalysis) unified output library collects data directly within codes, outputting them to a common file output format co-designed with application developers and users. * @subpage slamtop provides an API to construct and process meshes. * @subpage slictop provides infrastructure for logging application messages. * @subpage spintop provides spatial acceleration data structures, also known as spatial indexes. From 455208364471de5aab98523ebbff773f618e8950 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Mon, 12 Aug 2024 13:05:05 -0700 Subject: [PATCH 33/60] add version for sina --- src/axom/sina/CMakeLists.txt | 9 +++++++++ src/axom/sina/config.hpp.in | 13 +++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/axom/sina/config.hpp.in diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index a4ebe666a2..0926b154e6 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -14,6 +14,15 @@ axom_component_requires(NAME Sina TPLS Conduit ) +#------------------------------------------------------------------------------ +# Set sina version +#------------------------------------------------------------------------------ +set(SINA_VERSION_MAJOR 1) +set(SINA_VERSION_MINOR 14) +set(SINA_VERSION_PATCH 0) +axom_configure_file ( config.hpp.in + ${PROJECT_BINARY_DIR}/include/axom/sina/config.hpp ) + #------------------------------------------------------------------------------ # Specify the sina headers/sources #------------------------------------------------------------------------------ diff --git a/src/axom/sina/config.hpp.in b/src/axom/sina/config.hpp.in new file mode 100644 index 0000000000..f6fe736066 --- /dev/null +++ b/src/axom/sina/config.hpp.in @@ -0,0 +1,13 @@ +#ifndef SINA_CONFIG_HPP_ +#define SINA_CONFIG_HPP_ + +/// \file + +#define SINA_VERSION_MAJOR @SINA_VERSION_MAJOR@ +#define SINA_VERSION_MINOR @SINA_VERSION_MINOR@ +#define SINA_VERSION_PATCH @SINA_VERSION_PATCH@ + +#define SINA_VERSION "@SINA_VERSION_MAJOR@.@SINA_VERSION_MINOR@.@SINA_VERSION_PATCH@" + +#endif // SINA_CONFIG_HPP_ + From caab17d9f1edca083713a2b5e9e56568b751326f Mon Sep 17 00:00:00 2001 From: Brian Gunnarson <49216024+bgunnar5@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:41:45 -0700 Subject: [PATCH 34/60] Update src/axom/sina/core/ID.hpp Co-authored-by: Kenny Weiss --- src/axom/sina/core/ID.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/sina/core/ID.hpp b/src/axom/sina/core/ID.hpp index a1ba7ba015..d35b057bd3 100644 --- a/src/axom/sina/core/ID.hpp +++ b/src/axom/sina/core/ID.hpp @@ -11,7 +11,7 @@ * * \file ID.hpp * - * \brief Implementation file for Sina ID class + * \brief Header file for Sina ID class * * The Sina schema allows records to have either a local ID or a global ID. * When a global ID is specified, that will be used in the database. When a From bce3aaa38652c14d1268967df769f3353b273c62 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 29 Aug 2024 17:48:47 -0700 Subject: [PATCH 35/60] implementation -> header in hpp file brief statements --- src/axom/sina/core/AdiakWriter.hpp | 2 +- src/axom/sina/core/ConduitUtil.hpp | 2 +- src/axom/sina/core/Curve.hpp | 2 +- src/axom/sina/core/CurveSet.hpp | 2 +- src/axom/sina/core/DataHolder.hpp | 2 +- src/axom/sina/core/Datum.hpp | 2 +- src/axom/sina/core/Document.hpp | 2 +- src/axom/sina/core/File.hpp | 2 +- src/axom/sina/core/ID.hpp | 2 +- src/axom/sina/core/Record.hpp | 2 +- src/axom/sina/core/Relationship.hpp | 2 +- src/axom/sina/core/Run.hpp | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/axom/sina/core/AdiakWriter.hpp b/src/axom/sina/core/AdiakWriter.hpp index aba4d2c2e2..27ea1d4d50 100644 --- a/src/axom/sina/core/AdiakWriter.hpp +++ b/src/axom/sina/core/AdiakWriter.hpp @@ -11,7 +11,7 @@ * * \file AdiakWriter.hpp * - * \brief Implementation file for the Adiak Sina callback function. + * \brief Header file for the Adiak Sina callback function. * ****************************************************************************** */ diff --git a/src/axom/sina/core/ConduitUtil.hpp b/src/axom/sina/core/ConduitUtil.hpp index 453a9a62d0..46ee94b7bd 100644 --- a/src/axom/sina/core/ConduitUtil.hpp +++ b/src/axom/sina/core/ConduitUtil.hpp @@ -11,7 +11,7 @@ * * \file ConduitUtil.hpp * - * \brief Implementation file for Sina Conduit utility functions + * \brief Header file for Sina Conduit utility functions * ****************************************************************************** */ diff --git a/src/axom/sina/core/Curve.hpp b/src/axom/sina/core/Curve.hpp index 17b093dd27..88a3eb597e 100644 --- a/src/axom/sina/core/Curve.hpp +++ b/src/axom/sina/core/Curve.hpp @@ -11,7 +11,7 @@ * * \file Curve.hpp * - * \brief Implementation file for Sina Curve class + * \brief Header file for Sina Curve class * ****************************************************************************** */ diff --git a/src/axom/sina/core/CurveSet.hpp b/src/axom/sina/core/CurveSet.hpp index fb5ec3d5e3..d6c546e20b 100644 --- a/src/axom/sina/core/CurveSet.hpp +++ b/src/axom/sina/core/CurveSet.hpp @@ -11,7 +11,7 @@ * * \file CurveSet.hpp * - * \brief Implementation file for Sina CurveSet class + * \brief Header file for Sina CurveSet class * * \sa Curve.hpp * diff --git a/src/axom/sina/core/DataHolder.hpp b/src/axom/sina/core/DataHolder.hpp index fe79d670a4..daad0e00ba 100644 --- a/src/axom/sina/core/DataHolder.hpp +++ b/src/axom/sina/core/DataHolder.hpp @@ -11,7 +11,7 @@ * * \file DataHolder.hpp * - * \brief Implementation file for Sina DataHolder class + * \brief Header file for Sina DataHolder class * ****************************************************************************** */ diff --git a/src/axom/sina/core/Datum.hpp b/src/axom/sina/core/Datum.hpp index 784a996044..cc23117696 100644 --- a/src/axom/sina/core/Datum.hpp +++ b/src/axom/sina/core/Datum.hpp @@ -11,7 +11,7 @@ * * \file Datum.hpp * - * \brief Implementation file for Sina Datum class + * \brief Header file for Sina Datum class * ****************************************************************************** */ diff --git a/src/axom/sina/core/Document.hpp b/src/axom/sina/core/Document.hpp index 48a8f846b0..e048ea4ef2 100644 --- a/src/axom/sina/core/Document.hpp +++ b/src/axom/sina/core/Document.hpp @@ -11,7 +11,7 @@ * * \file Document.hpp * - * \brief Implementation file for Sina Document class + * \brief Header file for Sina Document class * ****************************************************************************** */ diff --git a/src/axom/sina/core/File.hpp b/src/axom/sina/core/File.hpp index dd32b75eb2..4c463b90dd 100644 --- a/src/axom/sina/core/File.hpp +++ b/src/axom/sina/core/File.hpp @@ -11,7 +11,7 @@ * * \file File.hpp * - * \brief Implementation file for Sina File class + * \brief Header file for Sina File class * ****************************************************************************** */ diff --git a/src/axom/sina/core/ID.hpp b/src/axom/sina/core/ID.hpp index a1ba7ba015..d35b057bd3 100644 --- a/src/axom/sina/core/ID.hpp +++ b/src/axom/sina/core/ID.hpp @@ -11,7 +11,7 @@ * * \file ID.hpp * - * \brief Implementation file for Sina ID class + * \brief Header file for Sina ID class * * The Sina schema allows records to have either a local ID or a global ID. * When a global ID is specified, that will be used in the database. When a diff --git a/src/axom/sina/core/Record.hpp b/src/axom/sina/core/Record.hpp index 14b13aa8d5..49d4e6b05c 100644 --- a/src/axom/sina/core/Record.hpp +++ b/src/axom/sina/core/Record.hpp @@ -11,7 +11,7 @@ * * \file Record.hpp * - * \brief Implementation file for Sina Record class + * \brief Header file for Sina Record class * * \sa DataHolder.hpp * diff --git a/src/axom/sina/core/Relationship.hpp b/src/axom/sina/core/Relationship.hpp index 7910cc0956..ac636f0be9 100644 --- a/src/axom/sina/core/Relationship.hpp +++ b/src/axom/sina/core/Relationship.hpp @@ -11,7 +11,7 @@ * * \file Relationship.hpp * - * \brief Implementation file for Sina Relationship class + * \brief Header file for Sina Relationship class * ****************************************************************************** */ diff --git a/src/axom/sina/core/Run.hpp b/src/axom/sina/core/Run.hpp index b057a5c30b..9b7ade1821 100644 --- a/src/axom/sina/core/Run.hpp +++ b/src/axom/sina/core/Run.hpp @@ -11,7 +11,7 @@ * * \file Run.hpp * - * \brief Implementation file for Sina Run class + * \brief Header file for Sina Run class * * \sa Record.hpp * From 274851fc4c5b2d41e7f42b31c1d428ed84ae640f Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Fri, 30 Aug 2024 11:11:11 -0700 Subject: [PATCH 36/60] make docs suggestions from PR --- src/axom/sina/docs/sphinx/index.rst | 6 ++-- src/axom/sina/docs/sphinx/relationships.rst | 7 ++-- src/axom/sina/docs/sphinx/tutorial.rst | 40 +++++++++++++-------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/axom/sina/docs/sphinx/index.rst b/src/axom/sina/docs/sphinx/index.rst index 40c2abf70a..a00c03b2a2 100644 --- a/src/axom/sina/docs/sphinx/index.rst +++ b/src/axom/sina/docs/sphinx/index.rst @@ -7,9 +7,9 @@ Sina C++ User Guide =================== -The Sina C++ library can read and write JSON files in the Sina schema. It -can be used by simulation applications to summarize run data to be ingested -into a database using the Sina tool suite. +The Sina ([S]imulation [In]sight and [A]nalysis) C++ library can read and write +JSON files in the Sina schema. It can be used by simulation applications to summarize +run data to be ingested into a database using the Sina tool suite. The top-level object in the Sina schema is the :doc:`Document `. It contains lists of :doc:`Record ` and :doc:`Relationship ` diff --git a/src/axom/sina/docs/sphinx/relationships.rst b/src/axom/sina/docs/sphinx/relationships.rst index ec2b76ff4d..6d497f39b6 100644 --- a/src/axom/sina/docs/sphinx/relationships.rst +++ b/src/axom/sina/docs/sphinx/relationships.rst @@ -25,10 +25,9 @@ examples: - Carlos sends an email to Dani - local_task_12 runs before local_run_14 -Note that a ``Relationship`` is described in the active voice. **Avoiding the passive -voice in predicates is recommended**, as this keeps the "direction" of the relationship -constant. An example of a passively-voiced ``Relationship`` is "Dani is emailed by Carlos". -Instead, this should be phrased as "Carlos emails Dani". +A ``Relationship`` should be described in the active voice. **Using active voice in predicates +is recommended** to maintain a clear direction in the relationship. For example, instead of the +passive construction "Dani is emailed by Carlos," use the active form "Carlos emails Dani." Below is an example showcasing how to construct a ``Relationship`` programmatically. Here, we assemble a ``Relationship`` showing that "Task_22 contains Run_1024": diff --git a/src/axom/sina/docs/sphinx/tutorial.rst b/src/axom/sina/docs/sphinx/tutorial.rst index 399a0ea5bb..70131b748d 100644 --- a/src/axom/sina/docs/sphinx/tutorial.rst +++ b/src/axom/sina/docs/sphinx/tutorial.rst @@ -23,14 +23,15 @@ 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 UQ study. A Relationship describes how two records -relate to each user (e.g. UQ studies contain runs). +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 - :lines: 9-16 + :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 @@ -43,7 +44,8 @@ The type is automatically set to "run". .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 20-27 + :start-after: //! [begin create run] + :end-before: //! [end create run] ----------- Adding Data @@ -55,7 +57,8 @@ the JSON file. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 31-45 + :start-after: //! [begin adding data] + :end-before: //! [end adding data] ----------------- Adding Curve Sets @@ -69,7 +72,8 @@ variable (e.g. "time"), and possibly multiple dependent variables (e.g. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 49-66 + :start-after: //! [begin curve sets] + :end-before: //! [end curve sets] ------------ Adding Files @@ -84,7 +88,8 @@ For example if the file was deleted or renamed. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 72-78 + :start-after: //! [begin file add_and_remove] + :end-before: //! [end file add_and_remove] ----------------------------- Relationships Between Records @@ -99,7 +104,8 @@ than "is part of", as in "the run is part of the study". .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 83-85 + :start-after: //! [begin relationships] + :end-before: //! [end relationships] --------------------- Library-Specific Data @@ -114,20 +120,23 @@ library ``foo`` defines ``foo_collectData()`` like this: .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 90-93 + :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 - :lines: 97-100 + :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 - :lines: 104-112 + :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), @@ -143,7 +152,8 @@ into a Sina datastore. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 116-118 + :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 @@ -151,7 +161,8 @@ continue from where you left off. .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 122-124 + :start-after: //! [begin io read] + :end-before: //! [end io read] --------------------------------- Non-Conforming, User-Defined Data @@ -171,4 +182,5 @@ convert to and from JSON. The user-defined section is exposed as a .. literalinclude:: ../../examples/sina_tutorial.cpp :language: cpp - :lines: 128-137 + :start-after: //! [begin user defined] + :end-before: //! [end user defined] From 528d0d97dcb0b759858dc149b445c592c84b0cca Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Fri, 30 Aug 2024 11:12:03 -0700 Subject: [PATCH 37/60] modify code block comments for docs --- src/axom/sina/examples/sina_tutorial.cpp | 53 +++++++++++------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/src/axom/sina/examples/sina_tutorial.cpp b/src/axom/sina/examples/sina_tutorial.cpp index a3de2392db..13445f0fe9 100644 --- a/src/axom/sina/examples/sina_tutorial.cpp +++ b/src/axom/sina/examples/sina_tutorial.cpp @@ -11,7 +11,7 @@ namespace { -//! [create record] +//! [begin create record] void createRecord() { axom::sina::ID id {"some_record_id", axom::sina::IDType::Local}; @@ -22,9 +22,9 @@ void createRecord() axom::sina::Document doc; doc.add(std::move(record)); } -//! [create record] +//! [end create record] -//! [create run] +//! [begin create run] void createRun() { axom::sina::ID id {"some_run_id", axom::sina::IDType::Local}; @@ -35,9 +35,9 @@ void createRun() axom::sina::Document doc; doc.add(std::move(run)); } -//! [create run] +//! [end create run] -//! [adding data] +//! [begin adding data] void addData(axom::sina::Record &record) { // Add a scalar named "my_scalar" with the value 123.456 @@ -54,9 +54,9 @@ void addData(axom::sina::Record &record) std::vector stringList = {"hi", "hello", "howdy"}; record.add("my_string_list", axom::sina::Datum {stringList}); } -//! [adding data] +//! [end adding data] -//! [curve sets] +//! [begin curve sets] void addCurveSets(axom::sina::Record &record) { axom::sina::CurveSet timePlots {"time_plots"}; @@ -75,10 +75,9 @@ void addCurveSets(axom::sina::Record &record) // Associate the curve sets with the record record.add(timePlots); } +//! [end curve sets] -//! [curve sets] - -//! [file add_and_remove] +//! [begin file add_and_remove] void addAndRemoveFileToRecord(axom::sina::Record &run) { axom::sina::File my_file {"some/path.txt"}; @@ -87,35 +86,34 @@ void addAndRemoveFileToRecord(axom::sina::Record &run) // Removes the file from the record's file list run.remove(my_file); } +//! [end file add_and_remove] -//! [file add_and_remove] - -//! [relationships] +//! [begin relationships] void associateRunToStudy(axom::sina::Document &doc, axom::sina::Record const &uqStudy, axom::sina::Record const &run) { doc.add(axom::sina::Relationship {uqStudy.getId(), "contains", run.getId()}); } -//! [relationships] +//! [end relationships] -//! [library data foo] +//! [begin library data foo] void foo_collectData(axom::sina::DataHolder &fooData) { fooData.add("temperature", axom::sina::Datum {500}); fooData.add("energy", axom::sina::Datum {1.2e10}); } -//! [library data foo] +//! [end library data foo] -//! [library data bar] +//! [begin library data bar] void bar_gatherData(axom::sina::DataHolder &barData) { barData.add("temperature", axom::sina::Datum {400}); barData.add("mass", axom::sina::Datum {15}); } -//! [library data bar] +//! [end library data bar] -//! [library data host] +//! [begin library data host] void gatherAllData(axom::sina::Record &record) { auto fooData = record.addLibraryData("foo"); @@ -126,23 +124,23 @@ void gatherAllData(axom::sina::Record &record) record.add("temperature", axom::sina::Datum {450}); } -//! [library data host] +//! [end library data host] -//! [io write] +//! [begin io write] void save(axom::sina::Document const &doc) { axom::sina::saveDocument(doc, "my_output.json"); } -//! [io write] +//! [end io write] -//! [io read] +//! [begin io read] void load() { axom::sina::Document doc = axom::sina::loadDocument("my_output.json"); } -//! [io read] +//! [end io read] -//! [user defined] +//! [begin user defined] void addUserDefined(axom::sina::Record &record) { conduit::Node &userDefined = record.getUserDefinedContent(); @@ -154,7 +152,7 @@ void addUserDefined(axom::sina::Record &record) subNode["sub_2"] = 20; userDefined["sub_structure"] = subNode; } -//! [user defined] +//! [end user defined] } // namespace @@ -175,9 +173,6 @@ int main() addCurveSets(run); addAndRemoveFileToRecord(run); addUserDefined(run); - // TODO - // - Add Record to doc - // - Check output file to see if record shows up save(doc); load(); } From 6246b2db8af525d4c8f38755fc333a08150fd719 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Fri, 30 Aug 2024 11:59:18 -0700 Subject: [PATCH 38/60] some final fixes for sina docs --- src/axom/sina/docs/sphinx/core_concepts.rst | 3 ++- src/axom/sina/docs/sphinx/records.rst | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/axom/sina/docs/sphinx/core_concepts.rst b/src/axom/sina/docs/sphinx/core_concepts.rst index 3343bc17d1..0a9b2e1dfe 100644 --- a/src/axom/sina/docs/sphinx/core_concepts.rst +++ b/src/axom/sina/docs/sphinx/core_concepts.rst @@ -12,7 +12,8 @@ Sina provides four main classes: - ``Document`` - represents the top-level object of a JSON file conforming to the Sina schema. - ``Record`` - represents the data to be stored. - ``Relationship`` - represents a way to define the relationship between two ``Record`` objects. - - ``CurveSet`` - a class to store related independent and dependent ``Curve`` objects. + - ``CurveSet`` - a class to store related independent and dependent ``Curve`` objects. \ + ``Curve`` and ``CurveSet`` objects are just two more types of data that ``Record`` objects can store. More details on each class can be found in their respective pages below. diff --git a/src/axom/sina/docs/sphinx/records.rst b/src/axom/sina/docs/sphinx/records.rst index 26eea2d887..c0bf40c1af 100644 --- a/src/axom/sina/docs/sphinx/records.rst +++ b/src/axom/sina/docs/sphinx/records.rst @@ -58,10 +58,10 @@ Once executed, this code will output: { "my_scalar": { - "value": - [ - "input" - ] + "value": + [ + "input" + ] } }, "type": "my_type", @@ -96,7 +96,7 @@ This can be accomplished with the ``setUnits()`` and ``setTags()`` methods respe Below is an example of this functionality: -.. literalinclude:: ../../examples/sina_datum_units_tags.cpp +.. literalinclude:: ../../examples/sina_set_datum_units_tags.cpp :language: cpp +++++++++++++++++++++++++++++++++++++ From dbf9d5ab0657beaac3ff2e10ce22493af2e5956e Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Fri, 30 Aug 2024 16:25:36 -0700 Subject: [PATCH 39/60] fix python interpreter package find to not use deprecated call --- src/axom/sina/tests/CMakeLists.txt | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index ee4defd116..fbfa9dd6e6 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -99,10 +99,21 @@ endif() # Add fortran integration test #------------------------------------------------------------------------------ if (ENABLE_FORTRAN) - find_package(PythonInterp REQUIRED) - configure_file(${CMAKE_SOURCE_DIR}/axom/sina/tests/test_fortran_integration.py ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py COPYONLY) - configure_file(${CMAKE_SOURCE_DIR}/axom/sina/interface/sina_schema.json ${TEST_OUTPUT_DIRECTORY}/sina_schema.json COPYONLY) + find_package(Python REQUIRED COMPONENTS Interpreter) + if (Python_FOUND) + configure_file( + ${CMAKE_SOURCE_DIR}/axom/sina/tests/test_fortran_integration.py + ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py + COPYONLY + ) + configure_file( + ${CMAKE_SOURCE_DIR}/axom/sina/interface/sina_schema.json + ${TEST_OUTPUT_DIRECTORY}/sina_schema.json + COPYONLY + ) - axom_add_test( NAME sina_fortran_integration_test - COMMAND ${PYTHON_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR}) + axom_add_test( NAME sina_fortran_integration_test + COMMAND ${PYTHON_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR} + ) + endif() endif() From ae026bfa4e300746c8cbe025ce2a1c41c6b498f6 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Fri, 30 Aug 2024 16:27:35 -0700 Subject: [PATCH 40/60] move custom XL flag to just be used for sina fortran example executable --- src/axom/sina/CMakeLists.txt | 4 ---- src/axom/sina/examples/CMakeLists.txt | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index 0926b154e6..cf93a99bdb 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -63,10 +63,6 @@ if (ENABLE_FORTRAN) blt_list_append( TO sina_headers ELEMENTS interface/sina_fortran_interface.h) blt_list_append( TO sina_sources ELEMENTS interface/sina_fortran_interface.cpp interface/sina_fortran_interface.f90) - - if(CMAKE_Fortran_COMPILER_ID STREQUAL "XL") - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -qextname") - endif() endif() #------------------------------------------------------------------------------ diff --git a/src/axom/sina/examples/CMakeLists.txt b/src/axom/sina/examples/CMakeLists.txt index e201064f4b..2f6179f950 100644 --- a/src/axom/sina/examples/CMakeLists.txt +++ b/src/axom/sina/examples/CMakeLists.txt @@ -45,4 +45,9 @@ foreach(src ${sina_example_sources}) DEPENDS_ON ${sina_example_depends} FOLDER axom/sina/examples ) + + # Need to add this flag so XL will ignore trailing underscores in fortran function names + if (${exe_name}_ex STREQUAL "sina_fortran_ex" AND CMAKE_Fortran_COMPILER_ID STREQUAL "XL") + target_compile_options(${exe_name}_ex PRIVATE -qextname) + endif() endforeach() From 7289033cf6f9e1fb9302a277db134ee1583a9136 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 3 Sep 2024 17:16:35 -0700 Subject: [PATCH 41/60] fix python executable variable name --- src/axom/sina/tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index fbfa9dd6e6..f8a7e3f019 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -113,7 +113,7 @@ if (ENABLE_FORTRAN) ) axom_add_test( NAME sina_fortran_integration_test - COMMAND ${PYTHON_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR} + COMMAND ${Python_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR} ) endif() endif() From 5ff841befd00fc6ed7b233f13427b39d3ba20590 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Mon, 21 Oct 2024 10:06:11 -0700 Subject: [PATCH 42/60] fix code example for AdiakWriter --- src/axom/sina/core/AdiakWriter.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/axom/sina/core/AdiakWriter.hpp b/src/axom/sina/core/AdiakWriter.hpp index 27ea1d4d50..182ce19368 100644 --- a/src/axom/sina/core/AdiakWriter.hpp +++ b/src/axom/sina/core/AdiakWriter.hpp @@ -43,8 +43,10 @@ namespace sina * like this: * * \code - * axom::sina::Record record{ID{"my_id", axom::sina::IDType::Local}, "my_record_type"}; - * axom::sina::adiak_register_cb(1, adiak_category_all, axom::sina::adiakSinaCallback, 0, &record); + * #include "axom/sina.hpp" + * + * axom::sina::Record record{axom::sina::ID{"my_id", axom::sina::IDType::Local}, "my_record_type"}; + * adiak_register_cb(1, adiak_category_all, axom::sina::adiakSinaCallback, 0, &record); * \endcode * * \attention Not everything that Sina can capture an be captured through the From a9e6a7888a3d954153f2da35f651a66c039cc374 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Mon, 21 Oct 2024 10:06:44 -0700 Subject: [PATCH 43/60] change sina version to sina file format version --- src/axom/sina/config.hpp.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axom/sina/config.hpp.in b/src/axom/sina/config.hpp.in index f6fe736066..c0aab3cf9b 100644 --- a/src/axom/sina/config.hpp.in +++ b/src/axom/sina/config.hpp.in @@ -3,11 +3,11 @@ /// \file -#define SINA_VERSION_MAJOR @SINA_VERSION_MAJOR@ -#define SINA_VERSION_MINOR @SINA_VERSION_MINOR@ -#define SINA_VERSION_PATCH @SINA_VERSION_PATCH@ +#define SINA_FILE_FORMAT_VERSION_MAJOR @SINA_FILE_FORMAT_VERSION_MAJOR@ +#define SINA_FILE_FORMAT_VERSION_MINOR @SINA_FILE_FORMAT_VERSION_MINOR@ +#define SINA_FILE_FORMAT_VERSION_PATCH @SINA_FILE_FORMAT_VERSION_PATCH@ -#define SINA_VERSION "@SINA_VERSION_MAJOR@.@SINA_VERSION_MINOR@.@SINA_VERSION_PATCH@" +#define SINA_FILE_FORMAT_VERSION "@SINA_FILE_FORMAT_VERSION_MAJOR@.@SINA_FILE_FORMAT_VERSION_MINOR@.@SINA_FILE_FORMAT_VERSION_PATCH@" #endif // SINA_CONFIG_HPP_ From 159ca6ddeb13fedaf84de5345b1cad93bfd712b9 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson <49216024+bgunnar5@users.noreply.github.com> Date: Tue, 29 Oct 2024 08:45:39 -0700 Subject: [PATCH 44/60] change common file format to Sina acronym Co-authored-by: Kenny Weiss --- src/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.rst b/src/index.rst index fdfffd8421..f1526cf624 100644 --- a/src/index.rst +++ b/src/index.rst @@ -55,7 +55,7 @@ are identified. * Primal: Computational geometry primitives * Quest: Querying on surface tool * Sidre: Simulation data repository - * Sina: Write data in a common file format + * Sina: Simulation insight and analysis * Slam: Set-theoretic lightweight API for meshes * Slic: Simple Logging Interface Code * Spin: Spatial index structures for managing and accelerating spatial searches From 22ee4ec6e68f9c7e253c3345a07c14ca9877e33d Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 29 Oct 2024 15:51:47 -0700 Subject: [PATCH 45/60] renamed fortran file to use .f extension instead of .f90 --- .../{sina_fortran_interface.f90 => sina_fortran_interface.f} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/axom/sina/interface/{sina_fortran_interface.f90 => sina_fortran_interface.f} (100%) diff --git a/src/axom/sina/interface/sina_fortran_interface.f90 b/src/axom/sina/interface/sina_fortran_interface.f similarity index 100% rename from src/axom/sina/interface/sina_fortran_interface.f90 rename to src/axom/sina/interface/sina_fortran_interface.f From 1aadbf7013e72789602fa78a7963a8663def63bf Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 29 Oct 2024 15:52:46 -0700 Subject: [PATCH 46/60] SINA_VERSION -> SINA_FILE_FORMAT_VERSION --- src/axom/sina/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index cf93a99bdb..70fb582617 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -17,9 +17,9 @@ axom_component_requires(NAME Sina #------------------------------------------------------------------------------ # Set sina version #------------------------------------------------------------------------------ -set(SINA_VERSION_MAJOR 1) -set(SINA_VERSION_MINOR 14) -set(SINA_VERSION_PATCH 0) +set(SINA_FILE_FORMAT_VERSION_MAJOR 1) +set(SINA_FILE_FORMAT_VERSION_MINOR 14) +set(SINA_FILE_FORMAT_VERSION_PATCH 0) axom_configure_file ( config.hpp.in ${PROJECT_BINARY_DIR}/include/axom/sina/config.hpp ) @@ -62,7 +62,7 @@ blt_list_append( TO sina_sources ELEMENTS core/AdiakWriter.cpp IF AXOM_USE_ADIAK if (ENABLE_FORTRAN) blt_list_append( TO sina_headers ELEMENTS interface/sina_fortran_interface.h) blt_list_append( TO sina_sources - ELEMENTS interface/sina_fortran_interface.cpp interface/sina_fortran_interface.f90) + ELEMENTS interface/sina_fortran_interface.cpp interface/sina_fortran_interface.f) endif() #------------------------------------------------------------------------------ From 146b36479bdc9e3ef9c0ea6072924e1b51e02f66 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 29 Oct 2024 15:53:13 -0700 Subject: [PATCH 47/60] update check_datum_type example to use SLIC_ASSERT --- src/axom/sina/examples/CMakeLists.txt | 2 +- .../sina/examples/sina_check_datum_type.cpp | 42 ++++++++++++------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/axom/sina/examples/CMakeLists.txt b/src/axom/sina/examples/CMakeLists.txt index 2f6179f950..36e42a56fc 100644 --- a/src/axom/sina/examples/CMakeLists.txt +++ b/src/axom/sina/examples/CMakeLists.txt @@ -27,7 +27,7 @@ set(sina_example_sources sina_view_datum_values.cpp ) -set(sina_example_depends sina conduit::conduit) +set(sina_example_depends sina conduit slic) if (ENABLE_FORTRAN) blt_list_append( TO sina_example_sources ELEMENTS sina_fortran.f90) diff --git a/src/axom/sina/examples/sina_check_datum_type.cpp b/src/axom/sina/examples/sina_check_datum_type.cpp index 4efb97decc..53079212d3 100644 --- a/src/axom/sina/examples/sina_check_datum_type.cpp +++ b/src/axom/sina/examples/sina_check_datum_type.cpp @@ -4,28 +4,38 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/sina.hpp" +#include "axom/slic.hpp" + +using ValueTypeUnderlying = typename std::underlying_type::type; + +void getType(axom::sina::Datum datum, std::string datumName, std::string errMsg) +{ + auto datumType = static_cast(datum.getType()); + SLIC_ASSERT_MSG(static_cast(std::is_same::value), 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 scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum {scalars}; - - // Prints 0, corresponding to string - std::cout << static_cast::type>( - myDatum.getType()) - << std::endl; - - // Prints 1, corresponding to scalar - std::cout << static_cast::type>( - myOtherDatum.getType()) - << std::endl; - - // Prints 3, corresponding to scalar array - std::cout << static_cast::type>( - myArrayDatum.getType()) - << std::endl; + axom::sina::Datum myArrayDatum {scalars}; + + // Prints 1, corresponding to Scalar + getType(myDatum, "myDatum", "myDatumType did not match the expected type 'Scalar' (numerically represented as 1)."); + + // Prints 0, corresponding to String + getType(myOtherDatum, "myOtherDatum", "myDatumType did not match the expected type 'String' (numerically represented as 0)."); + + // Prints 3, corresponding to ScalarArray + getType(myArrayDatum, "myArrayDatum", "myArrayDatum did not match the expected type 'ScalarArray' (numerically represented as 3)."); + + // Finalize slic + axom::slic::finalize(); } \ No newline at end of file From 2b748d1a3f7ef8936656c1a73f0c69b5459a6a93 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson <49216024+bgunnar5@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:09:55 -0700 Subject: [PATCH 48/60] Fix typo in fortran test docstring Co-authored-by: Kenny Weiss --- src/axom/sina/tests/test_fortran_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/sina/tests/test_fortran_integration.py b/src/axom/sina/tests/test_fortran_integration.py index 525f92ef62..4ae4494c69 100644 --- a/src/axom/sina/tests/test_fortran_integration.py +++ b/src/axom/sina/tests/test_fortran_integration.py @@ -48,7 +48,7 @@ def tearDown(self): os.remove(self.dump_file) def test_file_validity(self): - """ Make sure the files we"re importing follow the Sina schema. """ + """ Make sure the files we're importing follow the Sina schema. """ try: import jsonschema schema_file = os.path.join(f"{self.binary_dir}/tests/sina_schema.json") From b09971278c177bb955d057022e5969c656d0e337 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 29 Oct 2024 17:08:47 -0700 Subject: [PATCH 49/60] remove 'using namespace std' --- src/axom/sina/examples/sina_curve_set.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/axom/sina/examples/sina_curve_set.cpp b/src/axom/sina/examples/sina_curve_set.cpp index 430b7551ec..623fc36421 100644 --- a/src/axom/sina/examples/sina_curve_set.cpp +++ b/src/axom/sina/examples/sina_curve_set.cpp @@ -9,15 +9,13 @@ #include #include -using namespace std; - struct BounceData { - vector time; - vector xPosition; - vector yPosition; - vector xVelocity; - vector yVelocity; + std::vector time; + std::vector xPosition; + std::vector yPosition; + std::vector xVelocity; + std::vector yVelocity; }; BounceData generateBounceData(double initialY, @@ -67,7 +65,7 @@ BounceData generateBounceData(double initialY, return data; } -void addCurveSet(axom::sina::Record &record, BounceData bounceData, string curveName) +void addCurveSet(axom::sina::Record &record, BounceData bounceData, std::string curveName) { // Create the curve set object axom::sina::CurveSet bounceCurveSet {curveName}; @@ -112,7 +110,7 @@ int main() axom::sina::Document doc; axom::sina::ID id {"ball_bounce_run", axom::sina::IDType::Global}; - unique_ptr study { + std::unique_ptr study { new axom::sina::Record {id, "ball bounce study"}}; addCurveSet(*study, bounceData, "ball_bounce"); From 60256295047f327397b85dcbb8f2059e05a76bc6 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 29 Oct 2024 17:35:19 -0700 Subject: [PATCH 50/60] add SLIC_ASSERT messages to examples where necessary --- src/axom/sina/examples/sina_create_datum.cpp | 28 +++++++++++++++- .../examples/sina_file_object_creation.cpp | 33 ++++++++++++++++++- .../examples/sina_file_object_removal.cpp | 29 +++++++++++++++- .../sina_query_records_relationships.cpp | 9 +++++ .../examples/sina_relationship_assembly.cpp | 21 ++++++++++-- .../sina/examples/sina_view_datum_values.cpp | 32 +++++++++++++----- 6 files changed, 139 insertions(+), 13 deletions(-) diff --git a/src/axom/sina/examples/sina_create_datum.cpp b/src/axom/sina/examples/sina_create_datum.cpp index 8bd32bb10f..08f7d4aac5 100644 --- a/src/axom/sina/examples/sina_create_datum.cpp +++ b/src/axom/sina/examples/sina_create_datum.cpp @@ -4,9 +4,13 @@ // 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 myRecord { @@ -18,5 +22,27 @@ int main(void) // Add the datum to the record myRecord->add("my_scalar", std::move(myDatum)); - std::cout << myRecord->toNode().to_json() << std::endl; + + // 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(); } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_file_object_creation.cpp b/src/axom/sina/examples/sina_file_object_creation.cpp index 4d9cdaeaad..31b1f9eb7e 100644 --- a/src/axom/sina/examples/sina_file_object_creation.cpp +++ b/src/axom/sina/examples/sina_file_object_creation.cpp @@ -4,9 +4,13 @@ // 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"); @@ -22,5 +26,32 @@ int main(void) myRecord->add(myFile); myRecord->add(myOtherFile); - std::cout << myRecord->toNode().to_json() << std::endl; + // 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(); } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_file_object_removal.cpp b/src/axom/sina/examples/sina_file_object_removal.cpp index 83c7bd3a7e..5fec42face 100644 --- a/src/axom/sina/examples/sina_file_object_removal.cpp +++ b/src/axom/sina/examples/sina_file_object_removal.cpp @@ -4,9 +4,13 @@ // 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"); @@ -25,5 +29,28 @@ int main(void) // Remove a file from the record myRecord->remove(myFile); - std::cout << myRecord->toNode().to_json() << std::endl; + // 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(); } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_query_records_relationships.cpp b/src/axom/sina/examples/sina_query_records_relationships.cpp index f76661437f..66ae7601a6 100644 --- a/src/axom/sina/examples/sina_query_records_relationships.cpp +++ b/src/axom/sina/examples/sina_query_records_relationships.cpp @@ -4,9 +4,13 @@ // 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; @@ -35,6 +39,11 @@ int main(void) 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(); } \ No newline at end of file diff --git a/src/axom/sina/examples/sina_relationship_assembly.cpp b/src/axom/sina/examples/sina_relationship_assembly.cpp index 62048881f4..1e2ae30367 100644 --- a/src/axom/sina/examples/sina_relationship_assembly.cpp +++ b/src/axom/sina/examples/sina_relationship_assembly.cpp @@ -4,14 +4,31 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/sina.hpp" +#include "axom/slic.hpp" int main(void) { + // Initialize slic + axom::slic::initialize(); + // Create IDs for both Task 22 and Run 1024 axom::sina::ID task22 {"Task_22", axom::sina::IDType::Global}; axom::sina::ID run1024 {"Run_1024", axom::sina::IDType::Global}; - // Create the relationship and print it out + // Create the relationship axom::sina::Relationship myRelationship {task22, "contains", run1024}; - std::cout << myRelationship.toNode().to_json() << std::endl; + + // Compare the actual output to the expected JSON output, then print it to console + std::string actualJsonString = myRelationship.toNode().to_json(); + std::string expectedJsonString = R"( +{ + "predicate": "contains", + "subject": "Task_22", + "object": "Run_1024" +})"; + SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, "JSON output does not match expected structure."); + std::cout << actualJsonString << std::endl; + + // Finalize slic + axom::slic::finalize(); } diff --git a/src/axom/sina/examples/sina_view_datum_values.cpp b/src/axom/sina/examples/sina_view_datum_values.cpp index 019abf9b85..16bedd9a06 100644 --- a/src/axom/sina/examples/sina_view_datum_values.cpp +++ b/src/axom/sina/examples/sina_view_datum_values.cpp @@ -4,15 +4,20 @@ // 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 - axom::sina::Datum myDatum {12.34}; - std::string value = "foobar"; - axom::sina::Datum myOtherDatum {value}; - std::vector scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum {scalars}; + double scalarValue = 12.34; + axom::sina::Datum myDatum {scalarValue}; + std::string stringValue = "foobar"; + axom::sina::Datum myOtherDatum {stringValue}; + std::vector 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}; @@ -28,12 +33,23 @@ int main(void) auto& data = myRecord->getData(); // Print the datum values - std::cout << "datum1: " << data.at("datum1").getScalar() << std::endl; - std::cout << "datum2: " << data.at("datum2").getValue() << std::endl; + 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 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 : data.at("datum3").getScalarArray()) + for(const auto& value : datum3Val) { std::cout << value << " "; } std::cout << std::endl; + + // Finalize slic + axom::slic::finalize(); } \ No newline at end of file From 0db396b76435e815966b9baf2deec02a91697446 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 30 Oct 2024 10:10:50 -0700 Subject: [PATCH 51/60] use axom utility to remove file rather than std::remove --- src/axom/sina/tests/sina_Document.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/axom/sina/tests/sina_Document.cpp b/src/axom/sina/tests/sina_Document.cpp index d44565d46d..09e04090a8 100644 --- a/src/axom/sina/tests/sina_Document.cpp +++ b/src/axom/sina/tests/sina_Document.cpp @@ -12,6 +12,7 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" +#include "axom/core/utilities/FileUtilities.hpp" #include "axom/sina/core/Document.hpp" #include "axom/sina/core/Run.hpp" @@ -281,7 +282,7 @@ NamedTempFile::NamedTempFile() fileName = tmpFileName.data(); } -NamedTempFile::~NamedTempFile() { std::remove(fileName.data()); } +NamedTempFile::~NamedTempFile() { axom::utilities::filesystem::removeFile(fileName.data()); } TEST(Document, saveDocument) { From db04e532df59373e4d247f26e4f74bac329028fc Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 30 Oct 2024 10:35:46 -0700 Subject: [PATCH 52/60] make long_json string a raw string literal --- src/axom/sina/tests/sina_Document.cpp | 98 ++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 18 deletions(-) diff --git a/src/axom/sina/tests/sina_Document.cpp b/src/axom/sina/tests/sina_Document.cpp index 09e04090a8..57c4cbe817 100644 --- a/src/axom/sina/tests/sina_Document.cpp +++ b/src/axom/sina/tests/sina_Document.cpp @@ -125,24 +125,86 @@ TEST(Document, create_fromJson_roundtrip) TEST(Document, create_fromJson_full) { - std::string long_json = - "{\"records\": [{\"type\": \"foo\",\"id\": " - "\"test_1\",\"user_defined\":{\"name\":\"bob\"},\"files\":{\"foo/" - "bar.png\":{\"mimetype\":\"image\"}},\"data\":{\"scalar\": {\"value\": " - "500,\"units\": \"miles\"}}},{\"type\":\"bar\",\"id\": " - "\"test_2\",\"data\": {\"scalar_list\": {\"value\": [1, 2, 3]}, " - "\"string_list\": {\"value\": [\"a\",\"wonderful\",\"world\"], " - "\"tags\":[\"observation\"]}}},{\"type\": " - "\"run\",\"application\":\"sina_test\",\"id\": " - "\"test_3\",\"data\":{\"scalar\": {\"value\": 12.3, \"units\": \"g/s\", " - "\"tags\": [\"hi\"]}, \"scalar_list\": {\"value\": [1,2,3.0,4]}}}, " - "{\"type\": \"bar\",\"id\": \"test_4\",\"data\":{\"string\": {\"value\": " - "\"yarr\"}, \"string_list\": {\"value\": [\"y\",\"a\",\"r\"]}}, " - "\"files\":{\"test/test.png\":{}}, " - "\"user_defined\":{\"hello\":\"there\"}}],\"relationships\": " - "[{\"predicate\": \"completes\",\"subject\": \"test_2\",\"object\": " - "\"test_1\"},{\"subject\": \"test_3\", \"predicate\": \"overrides\", " - "\"object\": \"test_4\"}]}"; + std::string long_json = R"({ + "records": [ + { + "type": "foo", + "id": "test_1", + "user_defined": { + "name": "bob" + }, + "files": { + "foo/bar.png": { + "mimetype": "image" + } + }, + "data": { + "scalar": { + "value": 500, + "units": "miles" + } + } + }, + { + "type": "bar", + "id": "test_2", + "data": { + "scalar_list": { + "value": [1, 2, 3] + }, + "string_list": { + "value": ["a", "wonderful", "world"], + "tags": ["observation"] + } + } + }, + { + "type": "run", + "application": "sina_test", + "id": "test_3", + "data": { + "scalar": { + "value": 12.3, + "units": "g/s", + "tags": ["hi"] + }, + "scalar_list": { + "value": [1, 2, 3.0, 4] + } + } + }, + { + "type": "bar", + "id": "test_4", + "data": { + "string": { + "value": "yarr" + }, + "string_list": { + "value": ["y", "a", "r"] + } + }, + "files": { + "test/test.png": {} + }, + "user_defined": { + "hello": "there" + } + } + ], + "relationships": [ + { + "predicate": "completes", + "subject": "test_2", + "object": "test_1" + }, + { + "subject": "test_3", + "predicate": "overrides", + "object": "test_4" + } + ] + })"; axom::sina::Document myDocument = Document(long_json, createRecordLoaderWithAllKnownTypes()); EXPECT_EQ(2, myDocument.getRelationships().size()); From 3faf719a37194aab799b5e827d97408bd361a27e Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 30 Oct 2024 11:37:12 -0700 Subject: [PATCH 53/60] rename sina_fortran example to use .f extension instead of .f90 --- src/axom/sina/examples/CMakeLists.txt | 2 +- src/axom/sina/examples/{sina_fortran.f90 => sina_fortran.f} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/axom/sina/examples/{sina_fortran.f90 => sina_fortran.f} (100%) diff --git a/src/axom/sina/examples/CMakeLists.txt b/src/axom/sina/examples/CMakeLists.txt index 36e42a56fc..f5d44b94ab 100644 --- a/src/axom/sina/examples/CMakeLists.txt +++ b/src/axom/sina/examples/CMakeLists.txt @@ -30,7 +30,7 @@ set(sina_example_sources set(sina_example_depends sina conduit slic) if (ENABLE_FORTRAN) - blt_list_append( TO sina_example_sources ELEMENTS sina_fortran.f90) + blt_list_append( TO sina_example_sources ELEMENTS sina_fortran.f) endif() #------------------------------------------------------------------------------ diff --git a/src/axom/sina/examples/sina_fortran.f90 b/src/axom/sina/examples/sina_fortran.f similarity index 100% rename from src/axom/sina/examples/sina_fortran.f90 rename to src/axom/sina/examples/sina_fortran.f From f39a41e20eae864a03524be27ee67f88e298a11f Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 30 Oct 2024 14:18:57 -0700 Subject: [PATCH 54/60] use PYTHON_EXECUTABLE from host-config instead of setting up own Python --- src/axom/sina/tests/CMakeLists.txt | 31 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index f8a7e3f019..8657166c1c 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -99,21 +99,18 @@ endif() # Add fortran integration test #------------------------------------------------------------------------------ if (ENABLE_FORTRAN) - find_package(Python REQUIRED COMPONENTS Interpreter) - if (Python_FOUND) - configure_file( - ${CMAKE_SOURCE_DIR}/axom/sina/tests/test_fortran_integration.py - ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py - COPYONLY - ) - configure_file( - ${CMAKE_SOURCE_DIR}/axom/sina/interface/sina_schema.json - ${TEST_OUTPUT_DIRECTORY}/sina_schema.json - COPYONLY - ) - - axom_add_test( NAME sina_fortran_integration_test - COMMAND ${Python_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR} - ) - endif() + configure_file( + ${CMAKE_SOURCE_DIR}/axom/sina/tests/test_fortran_integration.py + ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py + COPYONLY + ) + configure_file( + ${CMAKE_SOURCE_DIR}/axom/sina/interface/sina_schema.json + ${TEST_OUTPUT_DIRECTORY}/sina_schema.json + COPYONLY + ) + + axom_add_test( NAME sina_fortran_integration_test + COMMAND ${PYTHON_EXECUTABLE} ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py -bd ${PROJECT_BINARY_DIR} + ) endif() From 483c75008e089d20e4b11c2409108d11a2a0691a Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 30 Oct 2024 14:20:24 -0700 Subject: [PATCH 55/60] remove call to make sina_fortran_ex and use os.path.join for file paths --- .../sina/tests/test_fortran_integration.py | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/axom/sina/tests/test_fortran_integration.py b/src/axom/sina/tests/test_fortran_integration.py index 4ae4494c69..08cb20cf15 100644 --- a/src/axom/sina/tests/test_fortran_integration.py +++ b/src/axom/sina/tests/test_fortran_integration.py @@ -5,6 +5,7 @@ import subprocess import unittest + def parse_args(): """Helper function to obtain the binary directory path of Axom from CLI""" parser = argparse.ArgumentParser(description="Unit test arguments") @@ -19,28 +20,22 @@ class TestFortranExampleIntegration(unittest.TestCase): @classmethod def setUpClass(cls): """ - Obtain the binary directory from the CLI and compile the sina fortran - example needed for these tests if necessary. + Obtain the binary directory from the CLI. """ - cwd = os.getcwd() - args = parse_args() cls.binary_dir = args.binary_dir if cls.binary_dir is None: # Assume we're at /path/to/build_dir/axom/sina/tests so move up to build_dir - cls.binary_dir = f"{cwd}/../../../" - - os.chdir(cls.binary_dir) - - if not os.path.exists(f"{cls.binary_dir}/examples/sina_fortran_ex"): - subprocess.run(["make", "sina_fortran_ex"]) - - os.chdir(cwd) - + cls.binary_dir = os.path.join(os.getcwd(), "..", "..", "..") def setUp(self): """ Invoke example Fortran application to dump a sina file """ - subprocess.run([f"{self.binary_dir}/examples/sina_fortran_ex"]) + sina_fortran_ex_path = os.path.join(self.binary_dir, "examples", "sina_fortran_ex") + if not os.path.exists(sina_fortran_ex_path): + raise FileNotFoundError( + f"The sina_fortran_ex needed for running fortran tests could not be found at path '{sina_fortran_ex_path}'" + ) + subprocess.run([sina_fortran_ex_path]) self.dump_file = "sina_dump.json" def tearDown(self): @@ -51,7 +46,7 @@ def test_file_validity(self): """ Make sure the files we're importing follow the Sina schema. """ try: import jsonschema - schema_file = os.path.join(f"{self.binary_dir}/tests/sina_schema.json") + schema_file = os.path.join(self.binary_dir, "tests", "sina_schema.json") with io.open(schema_file, "r", encoding="utf-8") as schema: schema = json.load(schema) with io.open(self.dump_file, "r", encoding="utf-8") as loaded_test: @@ -74,9 +69,12 @@ def test_validate_contents_of_record(self): self.assertEqual("my_type", record["type"]) # Test the files - self.assertEqual(list(record["files"].keys()), ["/path/to/my/file/my_other_file.txt", "/path/to/my/file/my_file.txt"]) - self.assertEqual(record["files"]["/path/to/my/file/my_other_file.txt"]["mimetype"], "png") - self.assertEqual(record["files"]["/path/to/my/file/my_file.txt"]["mimetype"], "txt") + path_to_my_file = os.path.join("/path", "to", "my", "file") + my_file = os.path.join(path_to_my_file, "my_file.txt") + other_file = os.path.join(path_to_my_file, "my_other_file.txt") + self.assertEqual(list(record["files"].keys()), [other_file, my_file]) + self.assertEqual(record["files"][other_file]["mimetype"], "png") + self.assertEqual(record["files"][my_file]["mimetype"], "txt") # Test the signed variants self.assertEqual("A", record["data"]["char"]["value"]) From e67cf685a0c847b1e69e0a4488159c101af22733 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Wed, 30 Oct 2024 14:51:02 -0700 Subject: [PATCH 56/60] run make style --- .../sina/examples/sina_check_datum_type.cpp | 25 ++++++++++++++----- src/axom/sina/examples/sina_create_datum.cpp | 3 ++- src/axom/sina/examples/sina_curve_set.cpp | 4 ++- .../examples/sina_file_object_creation.cpp | 3 ++- .../examples/sina_file_object_removal.cpp | 3 ++- .../sina_query_records_relationships.cpp | 3 ++- .../examples/sina_relationship_assembly.cpp | 3 ++- .../sina/examples/sina_view_datum_values.cpp | 9 ++++--- src/axom/sina/tests/sina_Document.cpp | 5 +++- 9 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/axom/sina/examples/sina_check_datum_type.cpp b/src/axom/sina/examples/sina_check_datum_type.cpp index 53079212d3..06092385b6 100644 --- a/src/axom/sina/examples/sina_check_datum_type.cpp +++ b/src/axom/sina/examples/sina_check_datum_type.cpp @@ -6,12 +6,16 @@ #include "axom/sina.hpp" #include "axom/slic.hpp" -using ValueTypeUnderlying = typename std::underlying_type::type; +using ValueTypeUnderlying = + typename std::underlying_type::type; void getType(axom::sina::Datum datum, std::string datumName, std::string errMsg) { auto datumType = static_cast(datum.getType()); - SLIC_ASSERT_MSG(static_cast(std::is_same::value), errMsg); + SLIC_ASSERT_MSG( + static_cast( + std::is_same::value), + errMsg); std::cout << datumName << " type: " << datumType << std::endl; } @@ -25,16 +29,25 @@ int main(void) std::string value = "foobar"; axom::sina::Datum myOtherDatum {value}; std::vector scalars = {1, 2, 20.0}; - axom::sina::Datum myArrayDatum {scalars}; + axom::sina::Datum myArrayDatum {scalars}; // Prints 1, corresponding to Scalar - getType(myDatum, "myDatum", "myDatumType did not match the expected type 'Scalar' (numerically represented as 1)."); + getType(myDatum, + "myDatum", + "myDatumType did not match the expected type 'Scalar' (numerically " + "represented as 1)."); // Prints 0, corresponding to String - getType(myOtherDatum, "myOtherDatum", "myDatumType did not match the expected type 'String' (numerically represented as 0)."); + getType(myOtherDatum, + "myOtherDatum", + "myDatumType did not match the expected type 'String' (numerically " + "represented as 0)."); // Prints 3, corresponding to ScalarArray - getType(myArrayDatum, "myArrayDatum", "myArrayDatum did not match the expected type 'ScalarArray' (numerically represented as 3)."); + getType(myArrayDatum, + "myArrayDatum", + "myArrayDatum did not match the expected type 'ScalarArray' " + "(numerically represented as 3)."); // Finalize slic axom::slic::finalize(); diff --git a/src/axom/sina/examples/sina_create_datum.cpp b/src/axom/sina/examples/sina_create_datum.cpp index 08f7d4aac5..d0f841d120 100644 --- a/src/axom/sina/examples/sina_create_datum.cpp +++ b/src/axom/sina/examples/sina_create_datum.cpp @@ -40,7 +40,8 @@ int main(void) "type": "my_type", "local_id": "my_record" })"; - SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, "JSON output does not match expected structure."); + SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, + "JSON output does not match expected structure."); std::cout << actualJsonString << std::endl; // Finalize slic diff --git a/src/axom/sina/examples/sina_curve_set.cpp b/src/axom/sina/examples/sina_curve_set.cpp index 623fc36421..decbfcd6cc 100644 --- a/src/axom/sina/examples/sina_curve_set.cpp +++ b/src/axom/sina/examples/sina_curve_set.cpp @@ -65,7 +65,9 @@ BounceData generateBounceData(double initialY, return data; } -void addCurveSet(axom::sina::Record &record, BounceData bounceData, std::string curveName) +void addCurveSet(axom::sina::Record &record, + BounceData bounceData, + std::string curveName) { // Create the curve set object axom::sina::CurveSet bounceCurveSet {curveName}; diff --git a/src/axom/sina/examples/sina_file_object_creation.cpp b/src/axom/sina/examples/sina_file_object_creation.cpp index 31b1f9eb7e..694fd87e10 100644 --- a/src/axom/sina/examples/sina_file_object_creation.cpp +++ b/src/axom/sina/examples/sina_file_object_creation.cpp @@ -49,7 +49,8 @@ int main(void) } } })"; - SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, "JSON output does not match expected structure."); + SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, + "JSON output does not match expected structure."); std::cout << actualJsonString << std::endl; // Finalize slic diff --git a/src/axom/sina/examples/sina_file_object_removal.cpp b/src/axom/sina/examples/sina_file_object_removal.cpp index 5fec42face..5c45396849 100644 --- a/src/axom/sina/examples/sina_file_object_removal.cpp +++ b/src/axom/sina/examples/sina_file_object_removal.cpp @@ -48,7 +48,8 @@ int main(void) } } })"; - SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, "JSON output does not match expected structure."); + SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, + "JSON output does not match expected structure."); std::cout << actualJsonString << std::endl; // Finalize slic diff --git a/src/axom/sina/examples/sina_query_records_relationships.cpp b/src/axom/sina/examples/sina_query_records_relationships.cpp index 66ae7601a6..8d8105cdce 100644 --- a/src/axom/sina/examples/sina_query_records_relationships.cpp +++ b/src/axom/sina/examples/sina_query_records_relationships.cpp @@ -41,7 +41,8 @@ int main(void) 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."); + SLIC_ASSERT_MSG(relationships.size() == 1, + "Unexpected number of relationships found."); std::cout << "Number of Relationships: " << relationships.size() << std::endl; // Finalize slic diff --git a/src/axom/sina/examples/sina_relationship_assembly.cpp b/src/axom/sina/examples/sina_relationship_assembly.cpp index 1e2ae30367..7bdef2760d 100644 --- a/src/axom/sina/examples/sina_relationship_assembly.cpp +++ b/src/axom/sina/examples/sina_relationship_assembly.cpp @@ -26,7 +26,8 @@ int main(void) "subject": "Task_22", "object": "Run_1024" })"; - SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, "JSON output does not match expected structure."); + SLIC_ASSERT_MSG(actualJsonString.compare(expectedJsonString) == 0, + "JSON output does not match expected structure."); std::cout << actualJsonString << std::endl; // Finalize slic diff --git a/src/axom/sina/examples/sina_view_datum_values.cpp b/src/axom/sina/examples/sina_view_datum_values.cpp index 16bedd9a06..02604bd487 100644 --- a/src/axom/sina/examples/sina_view_datum_values.cpp +++ b/src/axom/sina/examples/sina_view_datum_values.cpp @@ -34,15 +34,18 @@ int main(void) // Print the datum values double datum1Val = data.at("datum1").getScalar(); - SLIC_ASSERT_MSG(datum1Val == scalarValue, "Data stored in record at 'datum1' is unexpected."); + 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."); + SLIC_ASSERT_MSG(datum2Val == stringValue, + "Data stored in record at 'datum2' is unexpected."); std::cout << "datum2: " << datum2Val << std::endl; std::vector datum3Val = data.at("datum3").getScalarArray(); - SLIC_ASSERT_MSG(datum3Val == scalarArrayValue, "Data stored in record at 'datum3' is unexpected."); + SLIC_ASSERT_MSG(datum3Val == scalarArrayValue, + "Data stored in record at 'datum3' is unexpected."); std::cout << "datum3: "; for(const auto& value : datum3Val) { diff --git a/src/axom/sina/tests/sina_Document.cpp b/src/axom/sina/tests/sina_Document.cpp index 57c4cbe817..b8186a0408 100644 --- a/src/axom/sina/tests/sina_Document.cpp +++ b/src/axom/sina/tests/sina_Document.cpp @@ -344,7 +344,10 @@ NamedTempFile::NamedTempFile() fileName = tmpFileName.data(); } -NamedTempFile::~NamedTempFile() { axom::utilities::filesystem::removeFile(fileName.data()); } +NamedTempFile::~NamedTempFile() +{ + axom::utilities::filesystem::removeFile(fileName.data()); +} TEST(Document, saveDocument) { From 1bce8b34156b1d7fd6bd168ac7add25eae3736e0 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Tue, 5 Nov 2024 12:56:49 -0800 Subject: [PATCH 57/60] move sina version into API --- src/axom/sina/CMakeLists.txt | 9 --------- src/axom/sina/config.hpp.in | 13 ------------- src/axom/sina/core/Document.hpp | 18 ++++++++++++++++++ 3 files changed, 18 insertions(+), 22 deletions(-) delete mode 100644 src/axom/sina/config.hpp.in diff --git a/src/axom/sina/CMakeLists.txt b/src/axom/sina/CMakeLists.txt index 70fb582617..b4957f92b3 100644 --- a/src/axom/sina/CMakeLists.txt +++ b/src/axom/sina/CMakeLists.txt @@ -14,15 +14,6 @@ axom_component_requires(NAME Sina TPLS Conduit ) -#------------------------------------------------------------------------------ -# Set sina version -#------------------------------------------------------------------------------ -set(SINA_FILE_FORMAT_VERSION_MAJOR 1) -set(SINA_FILE_FORMAT_VERSION_MINOR 14) -set(SINA_FILE_FORMAT_VERSION_PATCH 0) -axom_configure_file ( config.hpp.in - ${PROJECT_BINARY_DIR}/include/axom/sina/config.hpp ) - #------------------------------------------------------------------------------ # Specify the sina headers/sources #------------------------------------------------------------------------------ diff --git a/src/axom/sina/config.hpp.in b/src/axom/sina/config.hpp.in deleted file mode 100644 index c0aab3cf9b..0000000000 --- a/src/axom/sina/config.hpp.in +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SINA_CONFIG_HPP_ -#define SINA_CONFIG_HPP_ - -/// \file - -#define SINA_FILE_FORMAT_VERSION_MAJOR @SINA_FILE_FORMAT_VERSION_MAJOR@ -#define SINA_FILE_FORMAT_VERSION_MINOR @SINA_FILE_FORMAT_VERSION_MINOR@ -#define SINA_FILE_FORMAT_VERSION_PATCH @SINA_FILE_FORMAT_VERSION_PATCH@ - -#define SINA_FILE_FORMAT_VERSION "@SINA_FILE_FORMAT_VERSION_MAJOR@.@SINA_FILE_FORMAT_VERSION_MINOR@.@SINA_FILE_FORMAT_VERSION_PATCH@" - -#endif // SINA_CONFIG_HPP_ - diff --git a/src/axom/sina/core/Document.hpp b/src/axom/sina/core/Document.hpp index e048ea4ef2..f65f8f2308 100644 --- a/src/axom/sina/core/Document.hpp +++ b/src/axom/sina/core/Document.hpp @@ -24,6 +24,9 @@ #include "axom/sina/core/Record.hpp" #include "axom/sina/core/Relationship.hpp" +#define SINA_FILE_FORMAT_VERSION_MAJOR 1 +#define SINA_FILE_FORMAT_VERSION_MINOR 0 + namespace axom { namespace sina @@ -73,6 +76,11 @@ namespace sina * axom::sina::saveDocument(myDocument, "path/to/outfile.json") * \endcode * + * Check the Sina file format version with: + * \code + * axom::sina::getSinaFileFormatVersion(); + * \endcode + * */ class Document { @@ -199,6 +207,16 @@ class Document */ void saveDocument(Document const &document, std::string const &fileName); +/** + * \brief Get the current file format version. + * + * \return A string representing the file format version. + */ +inline std::string getSinaFileFormatVersion() { + return std::to_string(SINA_FILE_FORMAT_VERSION_MAJOR) + "." + + std::to_string(SINA_FILE_FORMAT_VERSION_MINOR); +} + /** * \brief Load a document from the given path. Only records which this library * knows about will be able to be loaded. From af8bccc6a186f0ebb776e8c04ef60ff68f5357a3 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 7 Nov 2024 08:59:50 -0800 Subject: [PATCH 58/60] fix style, attempt to fix python issue w/ fortran test, and update RELEASE_NOTES --- RELEASE-NOTES.md | 1 + src/axom/sina/core/Document.hpp | 5 +++-- src/axom/sina/tests/CMakeLists.txt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 173ab74da4..b1a44c7481 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -21,6 +21,7 @@ The Axom project release numbers follow [Semantic Versioning](http://semver.org/ ### Added - `sidre::View` holding array data may now be re-shaped. See `sidre::View::reshapeArray`. +- Sina C++ library is now a component of Axom ### Changed - Importing Conduit array data into `sidre::View` now allocates destination diff --git a/src/axom/sina/core/Document.hpp b/src/axom/sina/core/Document.hpp index f65f8f2308..aead416b79 100644 --- a/src/axom/sina/core/Document.hpp +++ b/src/axom/sina/core/Document.hpp @@ -212,9 +212,10 @@ void saveDocument(Document const &document, std::string const &fileName); * * \return A string representing the file format version. */ -inline std::string getSinaFileFormatVersion() { +inline std::string getSinaFileFormatVersion() +{ return std::to_string(SINA_FILE_FORMAT_VERSION_MAJOR) + "." + - std::to_string(SINA_FILE_FORMAT_VERSION_MINOR); + std::to_string(SINA_FILE_FORMAT_VERSION_MINOR); } /** diff --git a/src/axom/sina/tests/CMakeLists.txt b/src/axom/sina/tests/CMakeLists.txt index 8657166c1c..502d3307f7 100644 --- a/src/axom/sina/tests/CMakeLists.txt +++ b/src/axom/sina/tests/CMakeLists.txt @@ -98,7 +98,7 @@ endif() #------------------------------------------------------------------------------ # Add fortran integration test #------------------------------------------------------------------------------ -if (ENABLE_FORTRAN) +if (ENABLE_FORTRAN AND DEFINED PYTHON_EXECUTABLE) configure_file( ${CMAKE_SOURCE_DIR}/axom/sina/tests/test_fortran_integration.py ${TEST_OUTPUT_DIRECTORY}/test_fortran_integration.py From 0f131dfe2c540d7abe66fe2ec4d53b230ae242a1 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 7 Nov 2024 11:14:54 -0800 Subject: [PATCH 59/60] change getType to printType in check_datum_type example --- src/axom/sina/examples/sina_check_datum_type.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axom/sina/examples/sina_check_datum_type.cpp b/src/axom/sina/examples/sina_check_datum_type.cpp index 06092385b6..65b1512579 100644 --- a/src/axom/sina/examples/sina_check_datum_type.cpp +++ b/src/axom/sina/examples/sina_check_datum_type.cpp @@ -9,7 +9,7 @@ using ValueTypeUnderlying = typename std::underlying_type::type; -void getType(axom::sina::Datum datum, std::string datumName, std::string errMsg) +void printType(axom::sina::Datum datum, std::string datumName, std::string errMsg) { auto datumType = static_cast(datum.getType()); SLIC_ASSERT_MSG( @@ -32,19 +32,19 @@ int main(void) axom::sina::Datum myArrayDatum {scalars}; // Prints 1, corresponding to Scalar - getType(myDatum, + printType(myDatum, "myDatum", "myDatumType did not match the expected type 'Scalar' (numerically " "represented as 1)."); // Prints 0, corresponding to String - getType(myOtherDatum, + printType(myOtherDatum, "myOtherDatum", "myDatumType did not match the expected type 'String' (numerically " "represented as 0)."); // Prints 3, corresponding to ScalarArray - getType(myArrayDatum, + printType(myArrayDatum, "myArrayDatum", "myArrayDatum did not match the expected type 'ScalarArray' " "(numerically represented as 3)."); From c1daa71e3d87b5fbe0a9f433db46cc7425392318 Mon Sep 17 00:00:00 2001 From: Brian Gunnarson Date: Thu, 7 Nov 2024 12:24:57 -0800 Subject: [PATCH 60/60] fix style --- .../sina/examples/sina_check_datum_type.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/axom/sina/examples/sina_check_datum_type.cpp b/src/axom/sina/examples/sina_check_datum_type.cpp index 65b1512579..5ccfe68845 100644 --- a/src/axom/sina/examples/sina_check_datum_type.cpp +++ b/src/axom/sina/examples/sina_check_datum_type.cpp @@ -33,21 +33,21 @@ int main(void) // Prints 1, corresponding to Scalar printType(myDatum, - "myDatum", - "myDatumType did not match the expected type 'Scalar' (numerically " - "represented as 1)."); + "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)."); + "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)."); + "myArrayDatum", + "myArrayDatum did not match the expected type 'ScalarArray' " + "(numerically represented as 3)."); // Finalize slic axom::slic::finalize();