JSON input for standalone apps

Celeritas standalone applications such as celer-sim and celer-optical accept structured JSON input based on the celeritas::inp data structures. The Input documents the underlying C++ types; this page explains how those types are represented in JSON and gives minimal working examples.

JSON encoding conventions

Structs

A C++ struct is represented as a JSON object with matching field names.

For example, a struct like:

struct Timers
{
    bool action{false};
    bool step{false};
};

becomes a JSON object where the keys have the same names as the members:

{
  "action": false,
  "step": false
}

Arrays, maps, and sets

std::vector<T> and celeritas::Array<T> values become JSON arrays. std::unordered_set<T> values become JSON arrays of unique values, and std::map<K, V> values become JSON objects.

Enums

Celeritas enumerations are encoded in JSON as strings rather than integer values.

For example, the C++ enumeration

enum class InterpolationType
{
    linear,
    poly_spline,
    cubic_spline,
    size_
};

is represented in JSON using the corresponding string values: "linear", "poly_spline", and "cubic_spline". A field InterpolationType type whose value is InterpolationType::linear in JSON becomes:

{
  "type": "linear"
}

Optional values

Fields with type std::optional<T> support several different JSON behaviors:

  • If the field is omitted, the existing value is unchanged. This allows the containing input structure to define a custom default value.

  • If the field is set to null, the optional is reset to std::nullopt.

  • If the field is set to a non-null value, the optional is initialized to the given value.

  • If the field is set to {}, the optional is initialized with a default-constructed T.

For example, omitting the optional device field in the System input preserves the existing value supplied by the containing structure (i.e., the device remains disabled):

{
  "system": {}
}

Setting the field to null explicitly clears the optional value (i.e., disables the device):

{
  "system": {
    "device": null
  }
}

Setting the field to an object initializes the optional and reads the nested value (i.e., enables the device with custom stack and heap sizes):

{
  "system": {
    "device": {
      "stack_size": 1024,
      "heap_size": 8192
    }
  }
}

Setting the field to an empty object initializes the optional with the contained type’s default (i.e., enables the device with the default stack and heap size):

{
  "system": {
    "device": {}
  }
}

This means omission and {} are not equivalent: omission preserves the value that was already present, while {} replaces it with an initialized contained object using default values.

Variants

Many Celeritas input types are std::variant objects. In JSON, these are encoded as objects with a required _type field that identifies the selected alternative.

For example, the Field input type is defined as:

using Field = std::variant<NoField,
                           UniformField,
                           RZMapField,
                           CylMapField,
                           CartMapField>;

A C++ value such as

inp::Field field = inp::NoField{};

is represented in JSON as:

{
  "_type": "none"
}

Similarly, a uniform magnetic field can be specified in C++ as:

inp::Field field = [] {
    inp::UniformField result;
    result.strength = {0.0, 0.0, 1.0};
    return result;
}();

and in JSON as:

{
  "_type": "uniform",
  "strength": [0.0, 0.0, 1.0]
}

The _type field selects the variant alternative, and the remaining fields specify the data for that alternative. The _type value does not necessarily match the C++ type name. Refer to the documented string values for the variant being encoded.

See Example: encoding inp::Events in JSON for an example combining several variant-valued fields.

Example: encoding inp::Events in JSON

The inp::Events input type is a useful example of Celeritas JSON encoding because it combines several common patterns in one place.

A C++ setup constructing a primary generator for celer-sim might look like:

inp::Events events = [] {
    inp::CorePrimaryGenerator result;
    result.shape = inp::PointDistribution{{0.0, 0.0, 0.0}};
    result.angle = inp::IsotropicDistribution{};
    result.energy = inp::MonoenergeticDistribution{10.0};
    result.num_events = 1;
    result.primaries_per_event = 10000;
    result.pdg = {PDGNumber{11}};
}();
events.merge = false;

In JSON, this is represented as:

{
  "generator": {
    "_type": "primary",
    "shape": {
      "_type": "delta",
      "value": [0.0, 0.0, 0.0]
    },
    "angle": {
      "_type": "isotropic"
    },
    "energy": {
      "_type": "delta",
      "value": 10.0
    },
    "num_events": 1,
    "primaries_per_event": 10000,
    "pdg": [11]
  },
  "merge": false
}

This example illustrates several of the JSON encoding conventions used by Celeritas:

  • generator is a std::variant and is encoded with a _type field.

  • shape, angle, and energy are also variants and each uses its own _type field.

  • The pdg vector is encoded as a JSON array.

  • Scalar fields such as num_events and merge are encoded directly as JSON numbers and booleans.

See also