API Documentation for Python

class libmuscle.Grid(array: ndarray, indexes: Optional[List[str]] = None)[source]

Bases: object

Represents a grid of data to send or receive.

Note that for received grids, the array of data is a read-only NumPy array. If you have another array that you want to put the received data into, use np.copyto(dest, source) to copy the contents of the received array across into your destination array. If you don’t have an array yet and want a writable version of the received array, use array.copy() to create a writable copy. See the tutorial for examples.

array

An array of data

Type

np.ndarray

indexes

The names of the array’s indexes.

Type

Optional[List[str]]

Creates a Grid object.

A Grid object represents an multi-dimensional array of data. It has a type, a shape, and optionally a list of index names.

Supported data types are 4- and 8-byte integers (numpy.int32, numpy.int64), 4- and 8-byte floats (numpy.float32, numpy.float64), and booleans (np.bool_, np.bool8). The data argument must be a NumPy array of one of those types.

If indexes is given, then it must be a list of strings of the same length as the number of dimensions of data, and contain the names of the indexes of the array. For a 2D Cartesian grid, these may be 'x' and 'y' for example, or for a polar grid, 'phi' and 'rho'.

Parameters
  • array – An array of data, of a supported type (see above).

  • indexes – Names of the indexes (see above).

class libmuscle.Instance(ports: Optional[Dict[Operator, List[str]]] = None)[source]

Bases: object

Represents a compute element instance in a MUSCLE3 simulation.

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

Create an Instance.

Parameters

ports – A list of port names for each operator of this compute element.

error_shutdown(message: str) None[source]

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.

Parameters

message – An error message describing the problem.

get_port_length(port: str) int[source]

Returns the current length of the port.

Parameters

port – The name of the port to measure.

Raises: RuntimeError if this is a scalar port.

get_setting(name: str, typ: Optional[str] = None) Union[str, int, float, List[float], List[List[float]], bool_union_fix][source]

Returns the value of a model setting.

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

  • typ – The expected type of the value. If the value does not match this type, a TypeError will be raised. If not specified, any of the supported types will be accepted, and you’ll have to figure out what you got yourself.

Raises
  • KeyError – If no value was set for this setting.

  • TypeError – If the type of the setting’s value was not as expected.

is_connected(port: str) bool[source]

Returns whether the given port is connected.

Parameters

port – The name of the port to inspect.

Returns

True if there is a conduit attached to this port, False if not.

is_resizable(port: str) bool[source]

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.

Parameters

port – Name of the port to inspect.

Returns

True if the port can be resized.

is_vector_port(port: str) bool[source]

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.

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.

list_ports() Dict[Operator, List[str]][source]

Returns a description of the ports that this CE has.

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.

Returns

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

receive(port_name: str, slot: Optional[int] = None, default: Optional[Message] = None) Message[source]

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 RuntimeError.

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

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

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

Returns

The received message. The settings attribute of the received message will be None.

Raises

RuntimeError – If the given port is not connected and no default value was given.

receive_with_settings(port_name: str, slot: Optional[int] = None, default: Optional[Message] = None) Message[source]

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, and you really need the outside input) and the port is not connected, then you’ll get a RuntimeError.

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

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

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

Returns

The received message. The settings attribute will contain the received Settings, and will not be None.

Raises

RuntimeError – If the given port is not connected and no default value was given.

reuse_instance(apply_overlay: bool = True) bool[source]

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.

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 still.

send(port_name: str, message: Message, slot: Optional[int] = None) None[source]

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.

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, if any.

set_port_length(port: str, length: int) None[source]

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.

Parameters
  • port – Name of the port to resize.

  • length – The new length.

Raises

RuntimeError – If the port is not resizable.

class libmuscle.Message(timestamp: float, next_timestamp: Optional[float], data: Any, settings: Optional[Settings] = None)[source]

Bases: object

A message to be sent or received.

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

timestamp

Simulation time for which this data is valid.

Type

float

next_timestamp

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

Type

Optional[float]

data

An object to send or that was received.

Type

MessageObject

settings

Overlay settings to send or that was received.

Type

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.

A simple runner for Python-only models.

Starting instances is out of scope for MUSCLE 3, but is also very useful for testing and prototyping. So we have a little bit of support for it in this module.

libmuscle.runner.run_simulation(configuration: Configuration, implementations: Dict[str, Callable]) None[source]

Runs a simulation with the given configuration and instances.

The yMMSL document must contain both a model and settings.

This function will start the necessary instances described in the yMMSL document. To do so, it needs the corresponding implementations, which are given as a dictionary mapping the implementation name to a Python function (or any callable).

Parameters
  • configuration – A description of the model and settings.

  • instances – A dictionary of instances to run.