Accessing the Environment

As detailed in the earlier chapter detailing the defining of environmental properties, there are two types of environment property which can be interacted with in host functions. Unlike agent functions, host functions have full mutable access to both forms of environment property.

The HostEnvironment instance can be accessed in host functions via FLAMEGPU->environment.

Environment Properties

Host functions can both read and update environment properties using setProperty() and getProperty() respectively.

Unlike agent functions, host functions are able to access environment property arrays in a single transaction, rather than individually accessing each element. Otherwise, the syntax matches that found in agent functions.

Environmental properties are accessed, using HostEnvironment, as follows:

FLAMEGPU_HOST_FUNCTION(ExampleHostFn) {
    // Get the value of scalar environment property 'scalar_f' and store it in local variable 'scalar_f'
    float scalar_f = FLAMEGPU->environment.getProperty<float>("scalar_f");
    // Set the value of the scalar environment property 'scalar_f'
    FLAMEGPU->environment.setProperty<float>("scalar_f", scalar_f + 1.0f);

    // Get the value of array environment property 'array_i3' and store it in local variable 'array_i3'
    std::array<int, 3> array_i3 = FLAMEGPU->environment.getProperty<int, 3>("array_i3");
    // Set the value of the array environment property 'array_i3'
    FLAMEGPU->environment.setProperty<int, 3>("array_i3", std::array<int, 3>{0, 0, 0});

    // Get the value of the 2nd element of the array environment property 'array_u4'
    unsigned int array_u4_1 = FLAMEGPU->environment.getProperty<unsigned int>("array_u4", 1);
    // Set the value of the 3rd element of the array environment property 'array_u4'
    FLAMEGPU->environment.setProperty<unsigned int>("array_u4", 2, array_u4_1 + 2u);
}

Environment Macro Properties

Similar to regular environment properties, macro environment properties can be read and updated within host functions.

Environmental macro properties can be read via the returned HostMacroProperty, as follows:

// Define an host function called read_env_hostfn
FLAMEGPU_HOST_FUNCTION(read_env_hostfn) {
    // Retrieve the environment macro property foo of type float
    const float foo = FLAMEGPU->environment.getMacroProperty<float>("foo");
    // Retrieve the environment macro property bar of type int array[3][3][3]
    auto bar = FLAMEGPU->environment.getMacroProperty<int, 3, 3, 3>("bar");
    const int bar_1_1_1 = bar[1][1][1];
}

Macro properties in host functions are designed to behave as closely to their representative data type as possible. So most assignment and arithmetic operations should behave as expected.

Python has several exceptions to this rule:

  • The assignment operator is only available when it maps to __setitem__(index, val) (e.g. foo[0] = 10)

  • The increment/decrement operators are not available, as they cannot be overridden.

Below are several examples of how environment macro properties can be updated in host functions:

// Define an host function called write_env_hostfn
FLAMEGPU_HOST_FUNCTION(write_env_hostfn) {
    // Retrieve the environment macro property foo of type float
    auto foo = FLAMEGPU->environment.getMacroProperty<float>("foo");
    // Retrieve the environment macro property bar of type int array[3][3][3]
    auto bar = FLAMEGPU->environment.getMacroProperty<int, 3, 3, 3>("bar");
    // Update some of the values
    foo = 12.0f;
    bar[0][0][0]+= 1;
    bar[0][1][0] = 5;
    ++bar[0][0][2];
}

Warning

Be careful when using HostMacroProperty via the C++ API. When you retrieve an element e.g. bar[0][0][0] (from the example above), it is of type HostMacroProperty not int. Therefore you cannot pass it directly to functions which take generic arguments such as printf(), as it will be interpreted incorrectly. You must either store it in a variable of the correct type which you instead pass, or explicitly cast it to the correct type when passing it e.g. (int)bar[0][0][0] or static_cast<int>(bar[0][0][0]).

Macro Property File Input/Output

Environment macro properties are best suited for large datasets. For this reason it may be necessary to initialise them from file. As such, the HostEnvironment provides methods for importing and exporting macro properties. Unlike model state export, these operate on a single property. The additional .bin (binary) file format is supported.

// Define an host function called macro_prop_io_hostfn
FLAMEGPU_HOST_FUNCTION(macro_prop_io_hostfn) {
    // Export the macro property
    FLAMEGPU->environment.exportMacroProperty("macro_float_3_3_3", "out.bin");
    // Import a macro property
    FLAMEGPU->environment.importMacroProperty("macro_float_3_3_3", "in.json");
}

Environment Directed Graph

The environment directed graph can be initialised within host functions, defining the connectivity and initialising any properties stored within.

The host API allows vertices and edges to be managed via a map/dictionary interface, where the ID is used to access a vertex, or source and destination vertex IDs to access an edge.

Vertex IDs are unsigned integers, however the value 0 is reserved so cannot be assigned. Vertex IDs are not required to be contiguous, however they are stored sparsely such that two vertices with IDs 1 and 1000001 will require an index of length 1000000. It may be possible to run out of memory if IDs are too sparsely distributed.

// Define an host function called directed_graph_hostfn
FLAMEGPU_HOST_FUNCTION(directed_graph_hostfn) {
    // Fetch a handle to the directed graph
    HostEnvironmentDirectedGraph fgraph = FLAMEGPU->environment.getDirectedGraph("fgraph");
    // Declare the number of vertices and edges
    fgraph.setVertexCount(5);
    fgraph.setEdgeCount(5);
    // Initialise the vertices
    HostEnvironmentDirectedGraph::VertexMap vertices = graph.vertices();
    for (int i = 1; i <= 5; ++i) {
        // Create (or fetch) vertex with ID i
        HostEnvironmentDirectedGraph::VertexMap::Vertex vertex = vertices[i];
        vertex.setProperty<float, 2>("bar", {0.0f, 10.0f});
    }
    // Initialise the edges
    HostEnvironmentDirectedGraph::EdgeMap edges = graph.edges();
    for (int i = 1; i <= 5; ++i) {
        // Create (or fetch) edge with specified source/dest vertex IDs
        HostEnvironmentDirectedGraph::EdgeMap::Edge edge = edges[{i, ((i + 1)%5) + 1}];
        edge.setProperty<int>("foo", 12);
    }
}

Directed Graph File Input/Output

HostEnvironmentDirectedGraph provides importGraph() and exportGraph() to import and export the graph respectively using a common JSON format.

// Define an host function called directed_graph_hostfn
FLAMEGPU_HOST_FUNCTION(directed_graph_hostfn) {
    // Fetch a handle to the directed graph
    HostEnvironmentDirectedGraph fgraph = FLAMEGPU->environment.getDirectedGraph("fgraph");
    // Export the graph
    fgraph.exportGraph("out.json");
    // Import a different graph
    fgraph.importGraph("in.json");
}

An example of this format is shown below:

{
    "nodes": [
        {
            "id": "1",
            "bar": [
                12.0,
                22.0
            ]
        },
        {
            "id": "2",
            "bar": [
                13.0,
                23.0
            ]
        }
    ],
    "links": [
        {
            "source": "1",
            "target": "2",
            "foo": 21
        },
        {
            "source": "2",
            "target": "1",
            "foo": 22
        }
    ]
}