API Documentation for C++

This page provides full documentation for the C++ API of MUSCLE 3.

Note that in a few places, classes are referred to as libmuscle::impl::<class>. This is a bug in the documentation rendering process, the class is actually available as libmuscle::<class> and should be used as such.

Namespace libmuscle

class Data : public libmuscle::impl::DataConstRef

A data object.

This represents a data object, which is what MUSCLE 3 sends and receives.

Data objects refer to a simple value of a basic type, or refer to a dictionary or list. They model a reference, so if you copy a Data object to a new Data object, you’ll have two Data objects referring to the same data item. If you assign to a Data object, you modify the referred-to item, which will be visible also via any existing copies of the Data object.

With respect to memory management, this is like a shared_ptr, in that it will automatically manage any referenced memory, and only remove any actual items once all Data objects referring to them have been destructed.

Note that this publicly derives from DataConstRef, so see that class for the constructors and read-only member functions; they work on Data as well.

See the C++ tutorial for examples of how to use this.

Subclassed by libmuscle::impl::ClosePort

Public Functions

Data &operator=(Data const &rhs)

Copy-assign the given value to this Data object.

If the argument is a basic type, then the value will be copied. If it is a list or a dict, then this Data object will refer to the same list or dict as the argument.

Parameters
  • rhs: The object to copy from.

Data operator[](std::string const &key)

Access a dictionary value by key.

Returns a Data object referring to the value for the given key. Assign to that object to modify a value, as in

mydict["stress"] = 12.4;

If no key with the given name exists, a new entry is added to the dictionary with the given key and a nil value, and a reference to it is then returned. This way new key/value pairs can be added to a dictionary.

Return

A writable reference to the requested value.

Parameters
  • key: The key to search for.

Exceptions
  • std::runtime_error: if the object is not a dictionary.

Data value(std::size_t i) const

Access a value in a dictionary by index.

Use only if is_a_dict() returns true.

Indices match those of key(), so value(i) will give you the value corresponding to key(i).

See

size()

Parameters
  • i: The index of the value to retrieve.

Exceptions
  • std::runtime_error: if the object is not a map.

  • std::domain_error: if the index is out of bounds.

Data operator[](std::size_t index)

Access a list entry by index.

Returns a Data object referring to the value at the given index. Assign to that object to modify a value, as in

mylist[3] = 12.4;
mylist[4] = "MUSCLE 3";

Return

A writable reference to the requested value.

Parameters
  • index: The index to refer to.

Exceptions
  • std::runtime_error: if the object is not a list.

  • std::out_of_range: if the index is beyond the end of the list.

char *as_byte_array()

Access a byte array.

Use is_a_byte_array() to check whether this object represents a byte array. Use size() to get the size in bytes.

The returned buffer will remain valid and accessible at least until this Data goes out of scope.

Return

A pointer to a the first byte of a consecutive buffer.

Exceptions
  • std::runtime_error: if this is not a byte array.

Public Static Functions

template<typename Element>
Data grid(Element const *const data, std::vector<std::size_t> const &shape, std::vector<std::string> const &indexes = {}, StorageOrder storage_order = StorageOrder::last_adjacent)

Create a Data object containing a grid object.

This creates a DataConstRef that represents a grid or array of a given element type.

Supported types are std::int32_t, std::int64_t, float, double and bool. Note that unless you have exotic hardware, int, long and long long will be aliased as either int32_t or int64_t, and will therefore work as well. Unsigned integer types are not supported.

Besides a type, arrays have a shape. This is a list of sizes, one for each dimension of the array.

They also have a storage order, which specifies in which order the elements are arranged in memory. StorageOrder::first_adjacent means that array items who only differ by one in their first index are adjacent in memory, while StorageOrder::last_adjacent means that array items which only differ by one in their last index are adjacent in memory. Last adjacent is the standard in C and C++, and is also known as column-major, while first adjacent is the standard in Fortran, and is also known as row-major.

The data argument should be a pointer to a contiguous array of elements of the given type.

Finally, the optional index_names argument may be used to specify the names of the indices. For a Cartesian grid, these may be x and y, while for for example a polar grid you may have rho and phi. These names are optional, but help to make it easier to interpret the data, and so adding them is very much recommended.

Template Parameters
  • Element: The type of the elements.

Parameters
  • data: Pointer to the array data.

  • shape: The shape of the array.

  • indexes: Names of the array’s indexes.

  • storage_order: The storage order of the array data.

Data dict()

Create a Data containing an empty dictionary.

Return

A Data containing an empty dictionary.

template<typename ...Args>
static Data dict(Args const&... args)

Create a Data containing a dictionary with the given keys and values.

An even number of arguments must be given. The even arguments must be strings, and are the keys, while the odd arguments are the values. These are Data objects, so you can pass those, or a value of any type representable by Data.

Example:

auto mydict = Data::dict(
    "id", "element1",
    "stress", 12.3,
    "strain", 1.23);

Return

A Data containing a dictionary with the given keys and values.

Data list()

Create a Data containing an empty list.

Return

A Data containing an empty list.

Data nils(std::size_t size)

Create a Data containing a list of the given size.

The items in the list will be initialised to the nil value.

Return

A Data containing a list of nil values of length size.

Parameters
  • size: The size of the new list.

template<typename ...Args>
static Data list(Args const&... args)

Create a Data containing a list of the given items.

Each argument must be either a Data object, or an object of a type representable by a Data object.

Data byte_array(uint32_t size)

Create a byte array of a given size.

The buffer will be owned by this Data object. Use as_byte_array() to get a pointer to put data into it.

Data byte_array(char const *buffer, uint32_t size)

Create a Data referencing a byte array.

The buffer passed will not be copied! This creates a Data object that refers to your buffer, and you need to make sure that that buffer exists for as long as the Data object (and/or any copies of it) is used.

Parameters
  • buffer: A pointer to the beginning of the buffer.

  • size: The size of the buffer.

class DataConstRef

A const reference to some kind of data.

This defines a read-only API for variable data objects, which is what MUSCLE 3 sends and receives.

As a user, you should be creating Data objects, not DataConstRef objects. Data objects have the same interface as DataConstRef objects, but have additional member functions for modification.

This class models a const reference. You can create it and access the referenced data, but you cannot assign to this because that would change the referenced data, and this is a const reference.

With respect to memory management, this is like a shared_ptr, in that it will automatically manage referenced memory, even if you copy the object.

Subclassed by libmuscle::impl::Data

Public Functions

DataConstRef()

Create a Data object representing nil.

Nil is a special “no data” value, like nullptr or None in Python.

DataConstRef(bool value)

Create a DataConstRef object representing a boolean value.

Parameters
  • value: The value to represent.

DataConstRef(char const *const value)

Create a DataConstRef object representing a string value.

Parameters
  • value: The value to represent, a null-terminated C string.

DataConstRef(std::string const &value)

Create a DataConstRef object representing a string value.

Parameters
  • value: The value to represent.

DataConstRef(int value)

Create a DataConstRef object representing an integer value.

Parameters
  • value: The value to represent.

DataConstRef(long int value)

Create a DataConstRef object representing an integer value.

Parameters
  • value: The value to represent.

DataConstRef(long long int value)

Create a DataConstRef object representing an integer value.

Parameters
  • value: The value to represent.

DataConstRef(unsigned int value)

Create a DataConstRef object representing an unsigned integer value.

Parameters
  • value: The value to represent.

DataConstRef(unsigned long int value)

Create a DataConstRef object representing an unsigned integer value.

Parameters
  • value: The value to represent.

DataConstRef(unsigned long long int value)

Create a DataConstRef object representing an unsigned integer value.

Parameters
  • value: The value to represent.

DataConstRef(float value)

Create a DataConstRef object representing a 32-bit floating point value.

Parameters
  • value: The value to represent.

DataConstRef(double value)

Create a DataConstRef object representing a 64-bit floating point value.

Parameters
  • value: The value to represent.

DataConstRef(ymmsl::SettingValue const &value)

Create a DataConstRef object from a SettingValue’s value.

Note that this will decode to whichever type is stored in the SettingValue, not to a SettingValue object.

Parameters
  • value: The value to represent.

DataConstRef(ymmsl::Settings const &settings)

Create a DataConstRef object representing a Settings object.

Parameters
  • value: The value to represent.

DataConstRef(DataConstRef const&)

Copy-construct a DataConstRef object.

DataConstRef(DataConstRef&&)

Move-construct a DataConstRef object.

DataConstRef &operator=(DataConstRef const&)

Assignment is forbidden, this models a const reference.

DataConstRef &operator=(DataConstRef&&)

Assignment is forbidden, this models a const reference.

void reseat(DataConstRef const &target)

Reseat the reference.

This makes this DataConstRef object refer to the object referred to by the argument.

Parameters
  • target: The object to refer to.

template<typename T>
bool is_a() const

Determine the type of the referenced data.

This works for the following types:

  • bool

  • std::string

  • char (checks for integer, not for string!)

  • short int

  • int

  • long int

  • long long int

  • unsigned char

  • unsigned short int

  • unsigned int

  • unsigned long int

  • unsigned long long int

  • float

  • double

  • ymmsl::SettingValue

  • ymmsl::Settings

For checking nil, list and dict, see is_nil(), is_a_list() and is_a_dict().

Template Parameters
  • T: The type to check.

bool is_nil() const

Return whether this references a nil value.

Return

True iff this references a nil value.

bool is_a_dict() const

Return whether this references a dict value.

If so, operator[key] can be used to obtain values.

Return

True iff this references a dict.

bool is_a_list() const

Return whether this references a list value.

If so, operator[index] can be used to obtain values.

Return

True iff this references a list.

template<typename Element>
bool is_a_grid_of() const

Return whether this references a grid of the given element type.

Supported element types are std::int32_t, std::int64_t, float, double, and bool. Unless you’re on some exotic machine, int, long, and long long are aliases for int32_t or int64_t, and so will also work. Unsigned types are not supported.

Template Parameters
  • Element: The type of the elements of the array.

bool is_a_byte_array() const

Return whether this references a byte array.

If so, as_byte_array() can be used to obtain values, and size() to get the number of bytes in the array.

Return

True iff this references a byte array.

std::size_t size() const

Returns the size of a list, dict, grid or byte array.

Return

The number of items in a referenced list or dict value, the number of elements in a grid, or the number of bytes in a byte array.

template<typename T>
T as() const

Access a referenced scalar value.

Use is_a* first to check whether the type is what you expect. If the type mismatches, an exception will be thrown.

The following types can be used:

  • bool

  • std::string

  • char (accesses as integer, not as string!)

  • short int

  • int

  • long int

  • long long int

  • unsigned char

  • unsigned short int

  • unsigned int

  • unsigned long int

  • unsigned long long int

  • float

  • double

  • ymmsl::SettingValue

  • ymmsl::Settings

Return

The referenced value, as the given type.

Template Parameters
  • T: The type to access, as above.

Exceptions
  • std::runtime_error: if the type does not match.

char const *as_byte_array() const

Access a byte array.

Use is_a_byte_array() to check whether this object represents a byte array. Use size() to get the size.

The returned buffer will remain valid and accessible at least until this DataConstRef goes out of scope.

Return

A pointer to a the first byte of a consecutive buffer.

Exceptions
  • std::runtime_error: if this is not a byte array.

DataConstRef operator[](std::string const &key) const

Access an item in a dictionary by key.

Use only if is_a_dict() returns true.

Parameters
  • key: The key whose value to retrieve.

Exceptions
  • std::runtime_error: if the object is not a map.

  • std::domain_error: if the key does not exist.

std::string key(std::size_t i) const

Access a key in a dictionary by index.

Use only if is_a_dict() returns true.

Indices match those of value(), so value(i) will give you the value corresponding to key(i).

See

size()

Parameters
  • i: The index of the key to retrieve.

Exceptions
  • std::runtime_error: if the object is not a map.

  • std::domain_error: if the index is out of bounds.

DataConstRef value(std::size_t i) const

Access a value in a dictionary by index.

Use only if is_a_dict() returns true.

Indices match those of key(), so value(i) will give you the value corresponding to key(i).

See

size()

Parameters
  • i: The index of the value to retrieve.

Exceptions
  • std::runtime_error: if the object is not a map.

  • std::domain_error: if the index is out of bounds.

DataConstRef operator[](std::size_t index) const

Access an item in a list.

Use only if is_a_list() returns true.

Indexes are zero-based, use size() to find the valid range.

Parameters
  • index: The index at which to retrieve a value.

Exceptions
  • std::runtime_error: if the object is not a list.

  • std::domain_error: if the index is out of range.

std::vector<std::size_t> shape() const

Get the shape of a grid.

Use only if is_a_grid_of() returns true.

The shape of an array is a list of sizes of the array, one for each of its dimensions.

Return

The shape of the contained grid.

Exceptions
  • std::runtime_error: if the object is not a grid.

StorageOrder storage_order() const

Get the storage order of the grid.

Use only if is_a_grid_of() returns true.

The storage order is either StorageOrder::first_adjacent or StorageOrder::last_adjacent. StorageOrder::first_adjacent means that array items who only differ by one in their first index are adjacent in memory, while StorageOrder::last_adjacent means that array items which only differ by one in their last index are adjacent in memory. Last adjacent is the standard in C and C++, and is also known as column-major, while first adjacent is the standard in Fortran, and is also known as row-major.

Return

The storage order of the grid.

Exceptions
  • std::runtime_error: if the object is not a grid.

bool has_indexes() const

Return whether a grid has index names.

Use only if is_a_grid_of() returns true.

This function determines whether the grid has named indexes. If so, you can access them through indexes().

Return

True iff the grid has named indexes.

Exceptions
  • std::runtime_Error: if the object is not a grid.

std::vector<std::string> indexes() const

Get the index names of the grid.

Use only if is_a_grid_of() returns true and has_indexes() returns true.

The optional index names returned by this function specify which index refers to what. For a 2D Cartesian grid, these may be 'x' and 'y' for example, or for a polar grid, 'phi' and 'rho'. They’re intended to help annotate and use the data, and may be absent if the sender of a message did not include them.

Return

The indexes.

Exceptions
  • std::runtime_error: if the object is not a grid.

template<typename Element>
Element const *elements() const

Get the elements (data values) of a grid.

Use only if is_a_grid_of<Element>() returns true.

This returns a pointer to the specified type which points to a block of memory containing the grid’s element values. They are contiguous in memory in the order specified by storage_order().

The returned pointer is valid at least as long as this object exists.

Return

A pointer to the data, as specified above.

Template Parameters
  • Element: The type of the data stored in the grid.

Exceptions
  • std::runtime_error: if the object is not a grid of this type.

Public Static Functions

template<typename Element>
DataConstRef grid(Element const *const data, std::vector<std::size_t> const &shape, std::vector<std::string> const &indexes = {}, StorageOrder storage_order = StorageOrder::last_adjacent)

Create a DataConstRef object containing a grid object.

This creates a DataConstRef that represents a grid or array of a given element type.

Supported types are std::int32_t, std::int64_t, float, double and bool. Note that unless you have exotic hardware, int, long and long long will be aliased as either int32_t or int64_t, and will therefore work as well. Unsigned integer types are not supported.

Besides a type, arrays have a shape. This is a list of sizes, one for each dimension of the array.

They also have a storage order, which specifies in which order the elements are arranged in memory. StorageOrder::first_adjacent means that array items who only differ by one in their first index are adjacent in memory, while StorageOrder::last_adjacent means that array items which only differ by one in their last index are adjacent in memory. Last adjacent is the standard in C and C++, and is also known as column-major, while first adjacent is the standard in Fortran, and is also known as row-major.

The data argument should be a pointer to a contiguous array of elements of the given type.

Finally, the optional index_names argument may be used to specify the names of the indices. For a Cartesian grid, these may be x and y, while for for example a polar grid you may have rho and phi. These names are optional, but help to make it easier to interpret the data, and so adding them is very much recommended.

Template Parameters
  • Element: The type of the elements.

Parameters
  • data: Pointer to the array data.

  • shape: The shape of the array.

  • indexes: Names of the array’s indexes.

  • storage_order: The storage order of the array data.

class Instance

Represents a compute element instance in a MUSCLE3 simulation.

This class provides a low-level send/receive API for the instance to use.

Public Functions

Instance(int argc, char const *const argv[], MPI_Comm const &communicator = MPI_COMM_WORLD, int root = 0)

Create an Instance.

For MPI-based compute elements, creating an Instance is a collective operation, so it must be done in all processes simultaneously, with the same communicator and the same root.

Parameters
  • argc: The number of command-line arguments.

  • argv: Command line arguments.

  • communicator: MPI communicator containing all processes in this instance (MPI only).

  • root: The designated root process (MPI only).

Instance(int argc, char const *const argv[], PortsDescription const &ports, MPI_Comm const &communicator = MPI_COMM_WORLD, int root = 0)

Create an instance.

A PortsDescription can be written like this:

PortsDescription ports({ {Operator::F_INIT, {“port1”, “port2”}}, {Operator::O_F, {“port3[]”}} });

For MPI-based compute elements, creating an Instance is a collective operation, so it must be done in all processes simultaneously, with the same communicator and the same root.

Parameters
  • argc: The number of command-line arguments.

  • argv: Command line arguments.

  • ports: A description of the ports that this instance has.

  • communicator: MPI communicator containing all processes in this instance (MPI only).

  • root: The designated root process (MPI only).

bool reuse_instance(bool apply_overlay = true)

Decide whether to run this instance again.

In a multiscale simulation, instances get reused all the time. For example, in a macro-micro simulation, the micromodel does a complete run for every timestep of the macromodel. Rather than starting up a new instance of the micromodel, which could be expensive, we reuse a single instance many times.

This may bring other advantages, such as faster convergence when starting from the previous final state, and in some cases may be necessary if micromodel state needs to be preserved from one macro timestep to the next.

So in MUSCLE, submodels run in a reuse loop, which runs them over and over again until their work is done and they should be shut down. Whether to do another F_INIT, O_I, S, O_F cycle is decided by this method.

This method must be called at the beginning of the reuse loop, i.e. before the F_INIT operator, and its return value should decide whether to enter that loop again.

MPI-based compute elements must execute the reuse loop in each process in parallel, and call this function at the top of the reuse loop in each process.

Parameters
  • apply_overlay: Whether to apply the received settings overlay or to save it. If you’re going to use receive_with_settings() on your F_INIT ports, set this to false. If you don’t know what that means, just call reuse_instance() without specifying this and everything will be fine. If it turns out that you did need to specify false, MUSCLE 3 will tell you about it in an error message and you can add it.

void error_shutdown(std::string const &message)

Logs an error and shuts down the Instance.

If you detect that something is wrong (invalid input, invalid settings, simulation diverged, or anything else really), you should call this method before calling exit() or raising an exception that you don’t expect to catch.

If you do so, the Instance will tell the rest of the simulation that it encountered an error and will shut down. That makes it easier to debug the situation (the message will be logged), and it reduces the chance that other parts of the simulation will sit around waiting forever for a message that this instance was supposed to send.

MPI-based compute elements may either call this function in all processes, or only in the root process (as passed to the constructor).

Parameters
  • message: An error message describing the problem.

ymmsl::SettingValue get_setting(std::string const &name) const

Returns the value of a model setting.

MPI-based compute elements may call this function at any time within the reuse loop, in any or all processes, simultaneously or not.

Parameters
  • name: The name of the setting, without any instance prefix.

Exceptions
  • std::out_of_range: if no setting with the given name exists.

template<typename ValueType>
ValueType get_setting_as(std::string const &name) const

Returns the value of a model setting.

MPI-based compute elements may call this function at any time within the reuse loop, in any or all processes, simultaneously or not.

Template Parameters
  • ValueType: The (expected) type of the setting. Needs to match exactly or an exception will be thrown, this will not convert e.g. an integer into a string.

Parameters
  • name: The name of the setting, without any instance prefix.

Exceptions
  • std::out_of_range: if no setting with the given name exists.

  • std::bad_cast: if the value is not of the specified type.

std::unordered_map<ymmsl::Operator, std::vector<std::string>> list_ports() const

Returns a description of the ports that this CE has.

This keeps Doxygen from getting confused.

Note that the result has almost the same format as the port declarations you pass when making an Instance. The only difference is that the port names never have [] at the end, even if the port is a vector port.

MPI-based compute elements may call this function only in the root process.

Return

A map, indexed by operator, containing lists of port names. Operators with no associated ports are not included.

bool is_connected(std::string const &port) const

Returns whether the given port is connected.

MPI-based compute elements may call this function only in the root process.

Return

true if there is a conduit attached to this port, false if not.

Parameters
  • port: The name of the port to inspect.

bool is_vector_port(std::string const &port) const

Returns whether a port is a vector or scalar port.

If a port has been declared to be a vector port (i.e. the name passed when creating this Instance had ‘[]’ at the end), then you can pass a ‘slot’ argument when sending or receiving. It’s like the port is a vector of slots on which you can send or receive messages.

MPI-based compute elements may call this function only in the root process.

This function returns True if the given port is a vector port, and False if it is a scalar port.

Parameters
  • port: The port to check this property of.

bool is_resizable(std::string const &port) const

Returns whether the given port is resizable.

Scalar ports are never resizable. Whether a vector port is resizable depends on what it is connected to.

MPI-based compute elements may call this function only in the root process.

Return

: true if the port can be resized, false if not.

Parameters
  • port: Name of the port to inspect.

int get_port_length(std::string const &port) const

Returns the current length of the port.

MPI-based compute elements may call this function only in the root process.

Parameters
  • port: The name of the port to measure.

Exceptions
  • std::runtime_error: if this is a scalar port.

void set_port_length(std::string const &port, int length)

Resizes the port to the given length.

You should check whether the port is resizable using is_resizable() first; whether it is depends on how this compute element is wired up, so you should check.

MPI-based compute elements may call this function only in the root process.

Parameters
  • port: Name of the port to resize.

  • length: The new length.

Exceptions
  • std::runtime_error: if the port is not resizable.

void send(std::string const &port_name, Message const &message)

Send a message to the outside world.

Sending is non-blocking, a copy of the message will be made and stored until the receiver is ready to receive it.

MPI-based compute elements may call this function either in all processes, or only in the root process. In both cases, the message given by the root process will be sent, the others ignored.

Parameters
  • port_name: The port on which this message is to be sent.

  • message: The message to be sent.

void send(std::string const &port_name, Message const &message, int slot)

Send a message to the outside world.

Sending is non-blocking, a copy of the message will be made and stored until the receiver is ready to receive it.

MPI-based compute elements may call this function either in all processes, or only in the root process. In both cases, the message given by the root process will be sent, the others ignored. You may want to do a gather operation first to collect all the information that is to be sent in the root process.

Parameters
  • port_name: The port on which this message is to be sent.

  • message: The message to be sent.

  • slot: The slot to send the message on.

Message receive(std::string const &port_name)

Receive a message from the outside world.

Receiving is a blocking operation. This function will contact the sender, wait for a message to be available, and receive and return it.

If the port you are receiving on is not connected, an exception will be thrown.

MPI-based compute elements must call this function in all processes simultaneously. The received message will be returned in the root process, all other processes will receive a dummy message. It is therefore up to the model code to scatter or broadcast the received message to the non-root processes, if necessary.

Return

The received message. The settings attribute of the received message will not be set.

Parameters
  • port_name: The endpoint on which a message is to be received.

Exceptions
  • std::runtime_error: if the given port is not connected.

Message receive(std::string const &port_name, Message const &default_msg)

Receive a message from the outside world.

Receiving is a blocking operation. This function will contact the sender, wait for a message to be available, and receive and return it.

If the port you are receiving on is not connected, the default value you specified will be returned exactly as you passed it.

MPI-based compute elements must call this function in all processes simultaneously. The received message will be returned in the root process, all other processes will receive a dummy message. It is therefore up to the model code to scatter or broadcast the received message to the non-root processes, if necessary.

Return

The received message. The settings attribute of the received message will not be set.

Parameters
  • port_name: The endpoint on which a message is to be received.

  • default_msg: A default value to return if this port is not connected.

Exceptions
  • std::runtime_error: if the given port is not connected and no default value was given.

Message receive(std::string const &port_name, int slot)

Receive a message from the outside world.

Receiving is a blocking operation. This function will contact the sender, wait for a message to be available, and receive and return it.

If the port you are receiving on is not connected, an exception will be thrown.

MPI-based compute elements must call this function in all processes simultaneously. The received message will be returned in the root process, all other processes will receive a dummy message. It is therefore up to the model code to scatter or broadcast the received message to the non-root processes, if necessary.

Return

The received message. The settings attribute of the received message will not be set.

Parameters
  • port_name: The endpoint on which a message is to be received.

  • slot: The slot to receive the message, on, if any.

Exceptions
  • std::runtime_error: if the given port is not connected and no default value was given.

Message receive(std::string const &port_name, int slot, Message const &default_msg)

Receive a message from the outside world.

Receiving is a blocking operation. This function will contact the sender, wait for a message to be available, and receive and return it.

If the port you are receiving on is not connected, the default value you specified will be returned exactly as you passed it. If you didn’t specify a default value (e.g. because there is no reasonable default, you really need the outside input) and the port is not connected, you’ll get a std::runtime_error thrown.

MPI-based compute elements must call this function in all processes simultaneously. The received message will be returned in the root process, all other processes will receive a dummy message. It is therefore up to the model code to scatter or broadcast the received message to the non-root processes, if necessary.

Return

The received message. The settings attribute of the received message will not be set.

Parameters
  • port_name: The endpoint on which a message is to be received.

  • slot: The slot to receive the message, on, if any.

  • default_msg: A default value to return if this port is not connected.

Exceptions
  • std::runtime_error: if the given port is not connected and no default value was given.

Message receive_with_settings(std::string const &port_name)

Receive a message with attached settings overlay.

This function should not be used in submodels. It is intended for use by special compute elements that are ensemble-aware and have to pass on overlay settings explicitly.

Receiving is a blocking operation. This function will contact the sender, wait for a message to be available, and receive and return it.

If the port you are receiving on is not connected, an exception will be thrown.

MPI-based compute elements must call this function in all processes simultaneously. The received message will be returned in the root process, all other processes will receive a dummy message. It is therefore up to the model code to scatter or broadcast the received message to the non-root processes, if necessary.

Return

The received message. The settings attribute of the received message will contain the received settings.

Parameters
  • port_name: The port on which a message is to be received.

Exceptions
  • std::runtime_error: if the given port is not connected.

Message receive_with_settings(std::string const &port_name, int slot)

Receive a message with attached settings overlay.

This function should not be used in submodels. It is intended for use by special compute elements that are ensemble-aware and have to pass on overlay settings explicitly.

Receiving is a blocking operation. This function will contact the sender, wait for a message to be available, and receive and return it.

If the port you are receiving on is not connected, an exception will be thrown.

MPI-based compute elements must call this function in all processes simultaneously. The received message will be returned in the root process, all other processes will receive a dummy message. It is therefore up to the model code to scatter or broadcast the received message to the non-root processes, if necessary.

Return

The received message. The settings attribute of the received message will contain the received settings.

Parameters
  • port_name: The endpoint on which a message is to be received.

  • slot: The slot to receive the message, on, if any.

Exceptions
  • std::runtime_error: if the given port is not connected.

Message receive_with_settings(std::string const &port_name, Message const &default_msg)

Receive a message with attached settings overlay.

This function should not be used in submodels. It is intended for use by special compute elements that are ensemble-aware and have to pass on overlay settings explicitly.

Receiving is a blocking operation. This function will contact the sender, wait for a message to be available, and receive and return it.

If the port you are receiving on is not connected, the default value you specified will be returned exactly as you passed it. If you didn’t specify a default value (e.g. because there is no reasonable default, you really need the outside input) and the port is not connected, you’ll get a std::runtime_error thrown.

MPI-based compute elements must call this function in all processes simultaneously. The received message will be returned in the root process, all other processes will receive a dummy message. It is therefore up to the model code to scatter or broadcast the received message to the non-root processes, if necessary.

Return

The received message. The settings attribute of the received message will contain the received settings.

Parameters
  • port_name: The endpoint on which a message is to be received.

  • default_msg: A default value to return if this port is not connected.

Exceptions
  • std::runtime_error: if the given port is not connected and no default value was given.

Message receive_with_settings(std::string const &port_name, int slot, Message const &default_msg)

Receive a message with attached settings overlay.

This function should not be used in submodels. It is intended for use by special compute elements that are ensemble-aware and have to pass on overlay settings explicitly.

Receiving is a blocking operation. This function will contact the sender, wait for a message to be available, and receive and return it.

If the port you are receiving on is not connected, the default value you specified will be returned exactly as you passed it. If you didn’t specify a default value (e.g. because there is no reasonable default, you really need the outside input) and the port is not connected, you’ll get a std::runtime_error thrown.

MPI-based compute elements must call this function in all processes simultaneously. The received message will be returned in the root process, all other processes will receive a dummy message. It is therefore up to the model code to scatter or broadcast the received message to the non-root processes, if necessary.

Return

The received message. The settings attribute of the received message will contain the received settings.

Parameters
  • port_name: The endpoint on which a message is to be received.

  • slot: The slot to receive the message, on, if any.

  • default_msg: A default value to return if this port is not connected.

Exceptions
  • std::runtime_error: if the given port is not connected and no default value was given.

class Message

A message to be sent or received.

This class describes a message to be sent or that has been received.

Public Functions

Message(double timestamp, DataConstRef const &data)

Create a Message.

Parameters
  • timestamp: Simulation time for which this data is valid.

  • data: An object to send or that was received.

Message(double timestamp, double next_timestamp, DataConstRef const &data)

Create a Message.

Parameters
  • timestamp: Simulation time for which this data is valid.

  • next_timestamp: Simulation time for the next message to be transmitted through this port.

  • data: An object to send or that was received.

Message(double timestamp, DataConstRef const &data, ymmsl::Settings const &settings)

Create a Message.

Parameters
  • timestamp: Simulation time for which this data is valid.

  • data: An object to send or that was received.

  • settings: Overlay settings to send or that were received.

Message(double timestamp, double next_timestamp, DataConstRef const &data, ymmsl::Settings const &settings)

Create a Message.

Parameters
  • timestamp: Simulation time for which this data is valid.

  • next_timestamp: Simulation time for the next message to be transmitted through this port.

  • data: An object to send or that was received.

  • settings: Overlay settings to send or that were received.

Message(Message const &message)

Copy constructor.

Message(Message &&message)

Move constructor.

Message &operator=(Message const &message)

Copy assignment.

Message &operator=(Message &&message)

Move assignment.

double timestamp() const

Returns the timestamp of the message.

void set_timestamp(double timestamp)

Sets the timestamp of the message.

Parameters
  • timestamp: The new value.

bool has_next_timestamp() const

Returns whether the message has a next timestamp.

double next_timestamp() const

Returns the next timestamp of the message.

Only call if has_next_timestamp() returns true.

Exceptions
  • std::logic_error: if the next timestamp is not set.

void set_next_timestamp(double next_timestamp)

Sets the next timestamp of the message.

Parameters
  • next_timestamp: The new value.

void unset_next_timestamp()

Unsets the next timestamp of the message.

DataConstRef const &data() const

Returns the data of the message.

void set_data(DataConstRef const &data)

Sets data to the given value.

Parameters
  • data: The new data to set.

bool has_settings() const

Returns whether the message carries settings.

Settings const &settings() const

Returns the settings carried by the message.

Only call if has_settings() returns true.

void set_settings(ymmsl::Settings const &settings)

Sets settings to the given value.

This overwrites the entire Settings object, not a single value.

Parameters
  • settings: The new settings to use.

void unset_settings()

Unsets the settings of the message.

typedef std::unordered_map<ymmsl::Operator, std::vector<std::string>> libmuscle::impl::PortsDescription

A description of which ports a compute element has.

You can create one like this:

PortsDescription ports({
    {Operator::F_INIT, {"port1", "port2"}},
    {Operator::O_F, {"port3[]"}}
    });

and access elements as

ports[Operator::F_INIT][0] == "port1";

or for a const reference to a PortsDescription

ports.at(Operator::F_INIT)[1] == "port2";

Namespace ymmsl

bool ymmsl::impl::allows_sending(Operator op)

Whether the given operator allows sending messages.

Return

true If and only if the operator allows sending

Parameters
  • op: The operator to check.

bool ymmsl::impl::allows_receiving(Operator op)

Whether the given operator allows receiving messages.

Return

true If and only if the operator allows receiving

Parameters
  • op: The operator to check.

class Conduit

A conduit transports data between compute elements.

A conduit has two endpoints, which are references to a Port on a Compute Element. These references must be of one of the following forms:

  • submodel.port

  • namespace.submodel.port (or several namespace prefixes)

sender The sending port that this conduit is connected to. receiver The receiving port that this conduit is connected to.

Public Functions

Conduit(std::string const &sender, std::string const &receiver)

Create a Conduit.

Parameters
  • sender: The sending port that this conduit is connected to, including the compute element name and the port name.

  • receiver: The receiving port that this conduit is connected to, including the compute element name and the port name.

operator std::string() const

Convert to string.

bool operator==(Conduit const &rhs) const

Compare two conduits for equality.

Return

true iff the two conduits are identical.

Parameters
  • rhs: The other conduit to compare with.

Reference sending_compute_element() const

Returns a reference to the sending compute element.

Identifier sending_port() const

Returns the identity of the sending port.

std::vector<int> sending_slot() const

Returns the slot on the sending port.

If no slot was given, an empty list is returned.

Note that conduits connected to specific slots are currently not supported by MUSCLE 3.

Return

A list of slot indexes.

Reference receiving_compute_element() const

Returns a reference to the receiving compute element.

Identifier receiving_port() const

Returns the identity of the receiving port.

std::vector<int> receiving_slot() const

Returns the slot on the receiving port.

If no slot was given, an empty list is returned.

Note that conduits connected to specific slots are currently not supported by MUSCLE 3.

Return

A list of slot indexes.

class Identifier

A custom string type that represents an identifier.

An identifier may consist of upper- and lowercase characters, digits, and underscores.

Public Functions

Identifier(std::string const &contents)

Create an Identifier.

This creates a new identifier object, using the given string.

Parameters
  • contents: The contents of the identifier.

Exceptions
  • std::invalid_argument: if contents is not a valid identifier.

Identifier(char const *const contents)

Create an Identifier.

This creates a new identifier object, using the given C-style string.

Parameters
  • contents: The contents of the identifier.

Exceptions
  • std::invalid_argument: if contents is not a valid identifier.

operator std::string() const

Implicit conversion to std::string.

This is a custom string class, so that’s appropriate.

Return

The string representation of this Identifier.

bool operator==(Identifier const &rhs) const

Compare for equality.

Return

True iff both Identifiers are equal.

Parameters

bool operator==(std::string const &rhs) const

Compare for equality against a string.

Return

True iff the Identifier matches the string.

Parameters
  • rhs: The string to compare against.

bool operator==(char const *const rhs) const

Compare for equality against a C-style string.

Return

True iff the Identifier matches the string.

Parameters
  • rhs: The string to compare against.

bool operator!=(Identifier const &rhs) const

Compare for inequality.

Return

True iff both Identifiers are different.

Parameters

bool operator!=(std::string const &rhs) const

Compare for inequality against a string.

Return

True iff the Identifier is different from the string.

Parameters
  • rhs: The string to compare against.

bool operator!=(char const *const rhs) const

Compare for inequality against a C-style string.

Return

True iff the Identifier is different from the string.

Parameters
  • rhs: The string to compare against.

std::ostream &ymmsl::impl::operator<<(std::ostream &os, Identifier const &i)
enum ymmsl::impl::Operator

An operator of a Compute Element.

This is a combination of the Submodel Execution Loop operators, and operators for other components such as mappers.

Values:

NONE = 0
F_INIT = 1
O_I = 2
S = 3
B = 4
O_F = 5
struct Port

A port on a compute element.

Ports are used by compute elements to send or receive messages on. They are connected by conduits to enable communication between compute elements.

Subclassed by libmuscle::impl::Port

Public Functions

Port(Identifier const &name, Operator oper)

The MMSL operator in which this port is used.

Create a Port.

Parameters
  • name: The name of the port.

  • oper: The MMSL operator in which this port is used.

Public Members

Operator oper

The name of the port.

class Reference

A reference to an object in the MMSL execution model.

References in string form are written as either:

In object form, they consist of a list of Identifiers and ints. The first list item is always an Identifier. For the rest of the list, an Identifier represents a period operator with that argument, while an int represents the indexing operator with that argument.

Reference objects act like a list of Identifiers and ints, you can get their length using length(), iterate through the parts using cbegin() and cend(), and get individual items using []. Note that the sublist has to be a valid Reference, so it cannot start with an int.

References can be compared for equality to each other or to a plain string, and they can be used as dictionary keys. Reference objects are immutable (or they’re supposed to be anyway), so do not try to change any of the elements. Instead, make a new Reference. Especially References that are used as dictionary keys must not be modified, this will get your dictionary in a very confused state.

Public Types

typedef std::vector<ReferencePart>::const_iterator const_iterator

Random access iterator type for a Reference.

Public Functions

Reference(std::string const &content)

Create a Reference from a string.

Creates a Reference from a string, which will be parsed.

Parameters
  • content: A string to parse.

Exceptions
  • std::invalid_argument: if the argument does not define a valid Reference.

Reference(char const *content)

Create a Reference from a C string.

Creates a Reference from a C string, which will be parsed.

Parameters
  • content: A string to parse.

Exceptions
  • std::invalid_argument: if the argument does not define a valid Reference.

template<class ForwardIt>
Reference(ForwardIt begin, ForwardIt end)

Create a Reference from a ReferencePart range.

*begin and *end must be of type ReferencePart.

Parameters
  • begin: An iterator to the start of the range.

  • end: An iterator to the end of the range.

operator std::string() const

Conversion to std::string.

Return

The string representation of this Identifier.

std::size_t length() const

Returns the number of parts in the Reference.

bool operator==(Reference const &rhs) const

Compares for equality.

Will compare part-by-part.

Return

True iff the two References are equal.

Parameters

bool operator==(std::string const &rhs) const

Compares for equality.

Compares string representations.

Return

True iff this Reference matches the given string.

Parameters
  • rhs: The string to compare with.

bool operator==(char const *rhs) const

Compares for equality.

Compares string representations.

Return

True iff this Reference matches the given string.

Parameters
  • rhs: The string to compare with.

bool operator!=(Reference const &rhs) const

Compares for inequality.

Will compare part-by-part.

Return

True iff the two References are different.

Parameters

bool operator!=(std::string const &rhs) const

Compares for inequality.

Compares string representations.

Return

True iff this Reference does not match the given string.

Parameters
  • rhs: The string to compare with.

bool operator!=(char const *rhs) const

Compares for inequality.

Compares string representations.

Return

True iff this Reference does not match the given string.

Parameters
  • rhs: The string to compare with.

Reference::const_iterator cbegin() const

Returns a const_iterator to the beginning of the Reference.

Reference::const_iterator cend() const

Returns a const_iterator to one-past-the-end of the Reference.

Reference::const_iterator begin() const

Returns a const_iterator to the beginning of the Reference.

Reference::const_iterator end() const

Returns a const_iterator to one-past-the-end of the Reference.

ReferencePart const &operator[](int i) const

Returns the ReferencePart at the given index.

Return

The ReferencePart at that index.

Parameters
  • i: The index to dereference.

Reference const &operator+=(ReferencePart const &rhs)

Append a part to this Reference.

Return

A reference to this (updated) object.

Parameters
  • rhs: The part to append.

Reference const &operator+=(std::vector<int> const &rhs)

Append a list of indexes to this Reference.

Return

A reference to this (updated) object.

Parameters
  • rhs: The indexes to append.

Reference operator+(Reference const &rhs) const

Concatenate two References.

Parameters

Reference operator+(ReferencePart const &rhs) const

Append a ReferencePart to this Reference to create a new one.

Parameters

Reference operator+(std::vector<int> const &rhs) const

Append a list of indexes to this Reference.

Parameters
  • rhs: The indexes to append.

std::ostream &ymmsl::impl::operator<<(std::ostream &os, Reference const &r)
class ReferencePart

An item in a Reference.

Public Functions

ReferencePart(Identifier const &i)

Create a ReferencePart containing an Identifier.

This is implicit, so that you can give an Identifier wherever a ReferencePart is required.

Parameters

ReferencePart(int index)

Create a ReferencePart containing an index.

This is implicit, so that you can give an int wherever a ReferencePart is required.

Parameters
  • index: The index to store.

bool is_identifier() const

Returns whether this Part holds an Identifier.

Return

True iff this holds an Identifier.

bool is_index() const

Returns whether this Part holds an index.

Return

True iff this holds an index.

Identifier const &identifier() const

Returns the Identifier value.

Return

The identifier value.

Exceptions
  • std::runtime_error: if this object does not hold an Identifier.

int index() const

Returns the index value.

Return

The index value.

Exceptions
  • std::runtime_error: if this object does not hold an index.

bool operator==(ReferencePart const &rhs) const

Compares for equality.

Return

True if both type and value match.

Parameters

class Settings

Settings for doing an experiment.

An experiment is done by running a model with particular settings, e.g. the submodel scales, model parameters, and any other settings.

Public Functions

bool operator==(Settings const &rhs) const

Compare Settings objects for equality.

Return

True iff the Settings are equal.

bool operator!=(Settings const &rhs) const

Compare Settings objects for inequality.

Return

True iff the Settings are not equal.

std::size_t size() const

Return the number of settings in this object.

bool empty() const

Return true iff this object has no settings.

bool contains(Reference const &setting) const

Return true iff a setting with this name exists here.

Return

true iff it exists.

Parameters
  • setting: Name of the setting to check for.

SettingValue const &at(Reference const &setting) const

Get the value of a given setting.

Return

The value of that setting.

Parameters
  • setting: The name of the setting to read.

Exceptions
  • std::out_of_range: if there is no setting with that name.

SettingValue &operator[](Reference const &setting)

Set the value of a given setting.

Adds the setting first if it does not exist.

Return

A reference to the setting’s value that can be modified.

Parameters
  • setting: The name of the setting to read.

std::size_t erase(std::string const &setting)

Removes setting with the given key.

Return

The number of settings removed (0u or 1u).

Parameters
  • setting: Key of the setting to remove.

void clear()

Removes all settings.

Settings::const_iterator begin() const

Return an iterator to the first setting.

The iterator dereferences to a std::pair<Reference, SettingValue>.

Settings::const_iterator end() const

Return an iterator past the last setting.

std::ostream &ymmsl::impl::operator<<(std::ostream &os, ymmsl::impl::Settings const &settings)

Outputs a human-readable representation of the Settings to a stream.

This makes it so you can use e.g. std::cout << settings.

class SettingValue

Holds the value of a setting.

This is a discriminated union that can contain a std::string, an int64_t, a double, a bool, a std::vector<double> or a std::vector<std::vector<double>>.

Values of these types will be automatically converted wherever a SettingValue is required.

Public Functions

SettingValue()

Create an empty (and invalid) SettingValue.

SettingValue(std::string const &value)

Create a SettingValue containing a string.

Parameters
  • value: The string value to hold.

SettingValue(char const *value)

Create a SettingValue containing a string.

Parameters
  • value: The string value to hold.

SettingValue(int value)

Create a SettingValue containing an int64_t.

Parameters
  • value: The value to hold.

SettingValue(int64_t value)

Create a SettingValue containing an int64_t.

Parameters
  • value: The value to hold.

SettingValue(double value)

Create a SettingValue containing a double.

Parameters
  • value: The value to hold.

SettingValue(bool value)

Create a SettingValue containing a bool.

Parameters
  • value: The value to hold.

SettingValue(std::initializer_list<double> value)

Create a SettingValue containing a std::vector<double>.

This covers SettingValue({1.0, 2.0, 3.0});

Parameters
  • value: The value to hold.

SettingValue(std::vector<double> const &value)

Create a SettingValue containing a std::vector<double>.

Parameters
  • value: The value to hold.

SettingValue(std::initializer_list<std::vector<double>> const &value)

Create a SettingValue containing a std::vector<std::vector<double>>.

This covers SettingValue({{1.0, 2.0}, {3.0, 4.0}});

Parameters
  • value: The value to hold.

SettingValue(std::vector<std::vector<double>> const &value)

Create a SettingValue containing a std::vector<std::vector<double>>.

Parameters
  • value: The value to hold.

SettingValue(SettingValue const &other)

Copy-constructs a SettingValue.

SettingValue(SettingValue &&other)

Move-constructs a SettingValue.

SettingValue const &operator=(SettingValue const &other)

Copy-assigns a SettingValue.

SettingValue const &operator=(SettingValue &&other)

Move-assigns a SettingValue.

~SettingValue()

Destructs a SettingValue.

bool operator==(SettingValue const &rhs) const

Compare against another SettingValue.

Returns true iff both type and value are the same.

Parameters
  • rhs: The value to compare with.

bool operator!=(SettingValue const &rhs) const

Compare against another SettingValue.

Returns true iff both type and value are the same.

Parameters
  • rhs: The value to compare with.

template<typename T>
bool is_a() const

Return whether this SettingValue holds a value of the given type.

Parameters
  • T: A valid type, being one of std::string, int64_t, double, bool, std::vector<double>, or std::vector<std::vector<double>>.

template<typename T>
T as() const

Return the value as the given type.

Only call if is_a<T>() returns true.

Parameters
  • T: A valid type, being one of std::string, int64_t, double, bool, std::vector<double>, or std::vector<std::vector<double>>.

Exceptions
  • std::bad_cast: if the type of this value does not match the template parameter.

std::ostream &ymmsl::impl::operator<<(std::ostream &os, ymmsl::impl::SettingValue const &val)

Outputs a human-readable representation of the SettingValue to a stream.

This makes it so you can use e.g. std::cout << setting_value.