Version 26 (modified by jorisborgdorff, 11 years ago) (diff)

--

MUSCLE configuration file

MUSCLE has a renewed configuration syntax, but the old syntax can still be used. The MUSCLE configuration file, or historically, the  Complex Automata (CxA) file specifies what code will be used in a simulation, and how its coupled together. It is actually a Ruby file, so any Ruby syntax will work inside it.

To use it, make a file add kernels to it by giving a name and a Java class that has the submodel implementation

w = Instance.new('w', 'examples.simplejava.Sender')
r = Instance.new('r', 'examples.simplejava.ConsoleWriter')

For running a non-Java instance, see the next section.

To add properties, add them to the global $env hash or to the instances directly:

$env['max_timesteps'] = 4
w['dt'] = 1
w['someDoubleProperty'] = 6.1
w['someOtherProperty'] = "this is w text"
r['someOtherProperty'] = "this is r text"

Properties that are only meant for a single submodel are prepended with the name and a colon (e.g., "submodelName:propertyName"). Other properties are global and will be used by all submodels.

The scale of the submodels can also be specified in the CxA file. For the timestep of a submodel, use "dt", for the total time it will run, "T". For the first 3 spatial dimensions, use dx, dy, dz as step size, and X, Y, Z as total size. In Java, the scale can be accessed with the getScale() method of a submodel.

In the example, submodel w is attached to submodel r by tying the conduit entrance dataOut of w to the conduit exit dataIn of r. It also ties conduit entrance otherOut of w to other of r.

w.couple(r, {'dataOut' => 'dataIn', 'otherOut' => 'other'})

If the conduit entrance and exit have the same name, the second argument of tie is optional.

Non-Java instances

For non-Java instances that use the MUSCLE API, the Ruby classes NativeInstance (any executable binary), PythonInstance (Python script), MPIInstance (executable binary using MPI), and MatlabInstance (Matlab script) may be used. The first parameter of the constructor is the instance name, the second is the executable or script. For a normal executable, for example:

w = NativeInstance.new('w', '/path/to/w')

Optional parameters may also be provided:

Parameter Meaning Example Recognized by class
args Arguments to the script or executable 'param1 param2' All
java_class Custom Java subclass of NativeKernel 'uni.dep.MyKernel' All
mpiexec Command to run MPI 'mpiexec' MPIInstance
mpiexec_args Arguments to MPI executor '-np 4' MPIInstance
matlab Command to run Matlab '/opt/bin/matlab' MatlabInstance
matlab_args Arguments to Matlab '-matlab_arg1 -matlab_arg2' MatlabInstance
python Command to run Python '/opt/bin/python' PythonInstance

The optional arguments are specified by stating the parameter, a colon, and the parameter value.

To provide arguments and a Java class to an executable, state for example:

w = NativeInstance.new('w', '/path/to/w', args: 'param1 param2', java_class: 'uni.dep.MyNativeKernel')

Equivalently, to specify a MPI executable to run with four cores under the mpiexec command, use:

w = MPIInstance.new('w', '/path/to/w', mpiexec_args: '-np 4')

For Python:

w = PythonInstance.new('w', 'myscript.py')

Filters

If a conduit filter should be applied to a conduit, these can be added as a list as the last argument of tie():

w.couple(r, {'dataOut' => 'dataIn'}, ['muscle.core.conduit.filter.MultiplyDoubleFilter_0.5'])

In the example, the MUSCLE filter MultiplyDoubleFilter is applied, which multiplies each double with a value, in this case 0.5. For user defined filters, one double argument may be given, separated from the class name by an underscore. MUSCLE supplies some filters in package muscle.core.conduit.filter:

Filter Class Arguments Message datatype Behavior
null NullFilter none any Removes all incoming messages
pipe PipeFilter none any Forwards all incoming messages unchanged
console ConsoleWriterFilter none any Prints all messages to console and forwards them
thread ThreadedFilter none any Runs a thread, so the following filter will run in a separate thread
serialize SerializeFilter none any to byte[] Serializes any serializable Java object to a byte array
deserialize DeserializeFilter none byte[] to object Deserializes a serialized byte array to a Java object
compress CompressFilter none byte[] Compresses byte arrays using the Deflate algorithm
decompress DecompressFilter none byte[] Decompresses compressed byte arrays
chunk ChunkFilter int chunks byte[] Splits up a byte array into chunks smaller byte arrays for separate processing.
dechunk DechunkFilter int chunks byte[] Combines a byte array that was split up by the chunk filter.
linearinterpolation LinearInterpolationFilterDouble none double[] Creates a double[] of length-1 of the original, and linearly interpolates between the original values: k'_i <- (k_i+k_{i+1})/2
lineartimeinterpolationLinearTimeInterpolationFilterDoubleint stepdouble[] For step==2, forwards the first message and then sends two messages for every message received, interpolating between one message and the next.
multiply MultiplyFilterDouble double factor double[] Multiplies each value of the incoming message by factor
drop DropFilter int step any Drops messages that are not a multiple of step
timeoffset TimeOffsetFilter double time any Adds an offset time to the timestamps of messages
timefactor TimeFactorFilter double factor any Multiplies the sent timestamp of messages
blockafter BlockAfterTimeFilter double time any Drops messages with a timestamp greater than time

For convenience, the MUSCLE filters may be referred to by their name instead of their class:

w.couple(r, {'dataOut' => 'dataIn'}, ['multiply_0.5','console'])

By default, the conduit filters get applied at the receiving submodel. If a filter should be applied at the sending submodel or if filters should be applied at both locations, the couple function takes an additional argument, so that the first list of filters is applied at the sending side and the second list of filters is applied at the receiving side. The following fragment multiplies the data with a constant on the sending side, and prints it on the receiving side:

w.couple(r, {'dataOut' => 'dataIn'}, ['multiply_0.5'], ['console'])

And the following fragment compresses data on the sending side and uncompresses it on the receiving side:

w.couple(r, {'dataOut' => 'dataIn'}, ['serialize','compress'], ['decompress','deserialize'])

For large data sets it may increase performance to split the data into multiple chunks before compressing. In the following configuration, it gets sent in separate chunks and compressing is done in a separate thread from sending:

w.couple(r, {'dataOut' => 'dataIn'}, ['serialize','chunk_16','compress','thread'], ['decompress','dechunk_16','deserialize'])

Terminals

It may be convenient to couple a submodel to dummy terminals, to evaluate its individual behavior, or to read a message from file instead of receiving it from another submodel. A terminal is initialized by calling

readA = Terminal.new('readA', 'muscle.core.conduit.terminal.DoubleFileSource')
readA['filename'] = "/path/to/some.file"
readA['suffix'] = 'dat'
readA['relative'] = false
readA['delimiter'] = ','

readA.couple(r, 'dataIn')

Here, we're reading the file /path/to/some.file.dat, and the path is not relative to the runtime path of MUSCLE. The doubles in that file are delimited by commas. Finally, a terminal port takes any name of the receiving or sending end, so only one value is given to couple. For the moment, it is not possible to apply filters to terminals.

<< Back to Documentation