Basic Concepts

Node basics

The Node class is the primary object in conduit.

Think of it as a hierarchical variant object.

import conduit
n = conduit.Node()
n["my"] = "data"
print(n)
{
  "my": "data"
}

The Node class supports hierarchical construction.

n = conduit.Node()
n["my"] = "data";
n["a/b/c"] = "d";
n["a"]["b"]["e"] = 64.0;
print(n)
print("total bytes: {}\n".format(n.total_strided_bytes()))
{
  "my": "data",
  "a": 
  {
    "b": 
    {
      "c": "d",
      "e": 64.0
    }
  }
}
total bytes: 15

Borrowing form JSON (and other similar notations), collections of named nodes are called Objects and collections of unnamed nodes are called Lists, all other types are leaves that represent concrete data.

n = conduit.Node()
n["object_example/val1"] = "data"
n["object_example/val2"] = 10
n["object_example/val3"] = 3.1415

for i in range(5):
    l_entry = n["list_example"].append()
    l_entry.set(i)
print(n)
{
  "object_example": 
  {
    "val1": "data",
    "val2": 10,
    "val3": 3.1415
  },
  "list_example": 
  [
    0,
    1,
    2,
    3,
    4
  ]
}

You can iterate through a Node’s children.

n = conduit.Node()
n["object_example/val1"] = "data"
n["object_example/val2"] = 10
n["object_example/val3"] = 3.1415

for i in range(5):
    l_entry = n["list_example"].append()
    l_entry.set(i)
print(n)

for v in n["object_example"].children():
    print("{}: {}".format(v.name(),str(v.node())))

for v in n["list_example"].children():
    print(v.node())
{
  "object_example": 
  {
    "val1": "data",
    "val2": 10,
    "val3": 3.1415
  },
  "list_example": 
  [
    0,
    1,
    2,
    3,
    4
  ]
}
val1: "data"
val2: 10
val3: 3.1415
0
1
2
3
4

Behind the scenes, Node instances manage a collection of memory spaces.

n = conduit.Node()
n["my"] = "data"
n["a/b/c"] = "d"
n["a"]["b"]["e"] = 64.0
print(n.info())
{
  "mem_spaces": 
  {
    "0x7fb538d51320": 
    {
      "path": "my",
      "type": "allocated",
      "bytes": 5
    },
    "0x7fb538d31db0": 
    {
      "path": "a/b/c",
      "type": "allocated",
      "bytes": 2
    },
    "0x7fb538daf890": 
    {
      "path": "a/b/e",
      "type": "allocated",
      "bytes": 8
    }
  },
  "total_bytes_allocated": 15,
  "total_bytes_mmaped": 0,
  "total_bytes_compact": 15,
  "total_strided_bytes": 15
}

There is no absolute path construct, all paths are fetched relative to the current node (a leading / is ignored when fetching). Empty paths names are also ignored, fetching a///b is equalvalent to fetching a/b.

Bitwidth Style Types

When sharing data in scientific codes, knowing the precision of the underlining types is very important.

Conduit uses well defined bitwidth style types (inspired by NumPy) for leaf values. In Python, leaves are provided as NumPy ndarrays.

n = conduit.Node()
n["test"] = numpy.uint32(100)
print(n)
{
  "test": 100
}

Standard Python numeric types will be mapped to bitwidth style types.

n = conduit.Node()
n["test"] = 10
n.print_detailed()
{
  "test": {"dtype":"int64", "number_of_elements": 1, "offset": 0, "stride": 8, "element_bytes": 8, "endianness": "little", "value": 10}
}
Supported Bitwidth Style Types:
  • signed integers: int8,int16,int32,int64
  • unsigned integers: uint8,uint16,uint32,uint64
  • floating point numbers: float32,float64
Conduit provides these types by constructing a mapping for the current platform the from the following C++ types:
  • char, short, int, long, long long, float, double, long double

Compatible Schemas

When a set method is called on a Node, if the data passed to the set is compatible with the Node’s Schema the data is simply copied. No allocation or Schema changes occur. If the data is not compatible the Node will be reconfigured to store the passed data.

Schemas do not need to be identical to be compatible.

You can check if a Schema is compatible with another Schema using the Schema::compatible(Schema &test) method. Here is the criteria for checking if two Schemas are compatible:

  • If the calling Schema describes an Object : The passed test Schema must describe an Object and the test Schema’s children must be compatible with the calling Schema’s children that have the same name.
  • If the calling Schema describes a List: The passed test Schema must describe a List, the calling Schema must have at least as many children as the test Schema, and when compared in list order each of the test Schema’s children must be compatible with the calling Schema’s children.
  • If the calling Schema describes a leaf data type: The calling Schema’s and test Schema’s dtype().id() and dtype().element_bytes() must match, and the calling Schema dtype().number_of_elements() must be greater than or equal than the test Schema’s.