[[PageOutline]] == About the API == There are two ways to exploit the MUSCLE API: either by using these defined by the Multiscale Modeling Language, or by writing free-form code, where all orchestration and deadlock detection is your own responsibility. The former gives code that is easy to read and to debug, while the latter makes it possible to optimally exploit any parallelism in the model. This page section is split in two parts, one for each approach. With care, it is also possible to combine these approaches in a model, choosing the more appropriate form per submodel. The computational elements that the Multiscale Modeling Language (MML) defines are: * the conduit, for sending messages; * submodel, for modeling phenomena at certain scales; * mapper, for dimensionless mappings of data; * filter, for time and datatype filtering; and * terminal, for terminating empty conduits. These elements are all supported in the MUSCLE API. Conduits and parameters are used the same in MML-based or free-form code. === Conduits === The conduit is the mechanism in the MUSCLE runtime environment to send data. In the API, the ConduitEntrance and ConduitExit are accessible. The ConduitEntrance is used to send data, while the ConduitExit receives data. These entrances and exits, as we call them, can be accessed in two ways. Either they are created in the addPortals() method, or they are called on the fly in the code using the out() and in() methods. The addPortals method is called before the submodel starts, so the exits and entrances are stored as fields of your class. The conduit exit and entrance uses Java Generics to define what kind of data will be received or sent, both in the field declaration and in the addExit or addEntrance method. This allows for compile-time and run-time checking of data that is sent over the conduit. {{{ ConduitExit exitA; ConduitEntrance entranceA; @Override protected void addPortals() { exitA = addExit("exitName", double[].class); entranceA = addEntrance("entranceName", double[].class); } }}} To send or receive, use the fields that were initialized in addPortals(). {{{ double[] dataA = exitA.receive(); entranceA.send(dataA); }}} If addPortals() is not overridden it is possible to use the out() and in() methods to send or receive data. It is then not necessary to store the ConduitExit and ConduitEntrance as a field. {{{ double[] dataA = (double[]) in("exitName").receive(); out("entranceName").send(dataA); }}} In this case received data needs to be cast to double[] to use it, and in the send statement, the Java compiler will give a warning that an unchecked conversion is being done. If the cast is not correct, a ClassCastException will be thrown by Java. If the exit or entrance name is not configured, an IllegalArgumentException will be thrown by MUSCLE. The advantage of the first method is that it is type-safe, so there is no need to cast the data. Also if the code and the configuration file do not match this is detected immediately. The advantage of the second method is that it is less verbose and leads to smaller classes. A ConduitExit is used for receiving in a blocking mode. There are two receive methods: receive() and receiveObservation(). In the first case only the data is received. In the second, an observation is received, which contains the data but also the timestamp at which the data was sent, and the timestamp at which the next message will arrive. So, {{{ double[] dataA = exitA.receive(); }}} or {{{ Observation obsA = exitA.receiveObservation(); double[] dataA = obsA.getData(); Timestamp time = obsA.getTimestamp(); }}} If receive is called and the submodel on the other end has stopped, a MUSCLEConduitExhaustedException is thrown. To prevent this, it is possible to first call exitA.hasNext(), which is also blocking but returns a boolean. If a lot of conduit exits are used and the order in which they are received is not important, it is possible to loop over multiple conduits and call the ready() method. This is non-blocking, and it will return true when hasNext() will return an answer without blocking. A ConduitEntrance has several different send functions. The most basic send(data) sends the data and deduces the timestamp at which the data is sent, by adding delta T of the timescale to the previous timestamp. If the instance is dimensionless, or if required, you can explicitly set the timestamp of the data, and the timestamp at which you send the next message with send(data, time, nextTime): {{{ Timestamp time = Timestamp.ZERO; Timestamp nextTime = new Timestamp(2.0); entranceA.send(data, time, nextTime); }}} Finally, a muscle.core.model.Observation object can also be sent and it contains the same information: data, a time and the next time. {{{ Observation obs = new Observation(data, time, nextTime); entranceA.send(obs); }}} Although the entrance is automatically closed once the instance is finished, it is also possible to close the conduit earlier with the close() method, if it is not going to be used again. === Parameters === Parameters that are defined in the configuration file are accessed through a range of get*Property() methods. The most basic form is {{{ String parameter = getProperty("propName"); }}} In this form the property is read as a string. It first tries to find the instance property by searching for "instanceName:propName" and then for a global property named "propName". All get*Property() methods will throw an IllegalArgumentException if the property does not exist, this can be prevented by first calling hasProperty(). If a instance property is specifically required, this can be checked by first calling {{{ String parameter = null; if (hasInstanceProperty("propName")) { parameter = getProperty("propName"); } }}} If on the other hand only a global property is wanted, write {{{ String parameter = getGlobalProperty("propName"); }}} To get other types of parameters than strings, use getPathProperty for a File, getIntProperty for an int, getDoubleProperty for a double and getBoolProperty for a boolean. == MUSCLE MML API == === Submodels === In MML, submodels are governed by a Submodel Execution Loop. Interpreting this for MUSCLE, this looks like: {{{ while (true) { state, timeOrigin = init(t0) // we are allowed to receive messages while not endCondition() { intermediateObservation() // we are allowed to send messages state = solvingStep() // we are allowed to receive messages } finalObservation() // we are allowed to send messages if not restartSubmodel() { break } } }}} Reading this code step by step, a submodel can be restarted any number of times depending on the couplings, this is what the outer loop does. Next, each time a submodel is started, it has an initialization phase where it determines the initial state and what the simulation time of this initial state should be. Then it enters a while loop while some end condition is not met. Each iteration it sends some observation of the state, and it computes the next state. When the end condition is met, it is possible to do some cleaning and to send a final observation. At the end of the submodel it decides whether it should restart. In MUSCLE, endCondition is implemented as the willStop() method, which looks at all the messages sent and received and the message with the highest simulation time is compared with the total time in the timescale of the submodel, with parameter "submodelName:T". Restart submodel returns false by default, but could depend on whether one of the conduits will receive more messages. == MUSCLE free-form API == In the free-form API all kernels extend muscle.core.kernel.Instance. At least the execute() method of Instance should be overridden. In this method any sequence of send and receive is possible, and the conceptual coupling between different submodels can be as small or large as wanted. One notable difference with the MUSCLE MML API is that it can quit a submodel more directly. If there should still be some synchronization with other submodels, the willStop() method checks when the conduits have sent messages that are larger than the total timescale of the current submodel, or larger than the "max_timesteps" property. An example submodel Sender with name "w" (see src/java/examples/simplejava for the full code), which sends data each iteration would look as follows when using willStop(): {{{ import muscle.core.ConduitEntrance; import muscle.core.kernel.Instance; /** a simple java example kernel which sends data */ public class Sender extends Instance { private ConduitEntrance entrance; @Override protected void addPortals() { entrance = addEntrance("data", double[].class); } @Override protected void execute() { double[] dataA = new double[5]; while (!this.willStop()) { // process data for(int i = 0; i < dataA.length; i++) { dataA[i] = i; } // send the data entrance.send(dataA); } } } }}} By changing the "max_timesteps" and the "w:dt" property, this submodel will send different number of messages. For example, if max_timesteps is 4, and w:dt is 1 it will send 4 messages.