-
Notifications
You must be signed in to change notification settings - Fork 6
MUSCLE configuration file
The MUSCLE configuration file, historically called the [Complex Automata](http://www.complex-automata.org/) (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')
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"
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'})
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')
If a conduit filter should be applied to a conduit, these can be added as a list as the last argument of `couple`:
w.couple(r, {'dataOut' => 'dataIn'}, ['muscle.core.conduit.filter.MultiplyDoubleFilter_0.5'])
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+ki+1)/2` |
`lineartimeinterpolation` | `LinearTimeInterpolationFilterDouble` | `int step` | `double[]` | 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'])
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')