Initializing help system before first use

Communication of data between models

Runtime parameters are a means of communicating single data values to a submodel but they are not suited, for instance, to pass data tables or sets to a submodel. Also, they cannot be employed to retrieve any information from a submodel or to exchange data between models during their execution. All these tasks are addressed by the two I/O drivers defined by the module mmjobs: the shmem driver and the mempipe driver. As was already stated earlier, the shmem driver is meant for one-to-many communication (one model writing, many reading) and the mempipe driver serves for many-to-one communication. In the case of one model writing and one model reading we may use either, where shmem is conceptionally probably the easier to use.

Using the shared memory driver

With the shmem shared memory driver we write and read data blocks from/to memory. The use of this driver is quite similar to the way we would work with physical files. We have already encountered an example of its use in Section Compilation to memory: the filename is replaced by a label, prefixed by the name of the driver, such as "mmjobs.shmem:aLabel". If the module mmjobs is loaded by the model (or another model held in memory at the same time, such as its master model) we may use the short form "shmem:aLabel". Another case where we would have to explicitly add the name of a module to a driver occurs when we need to distinguish between several shmem drivers defined by different modules.

The exchange of data between different models is carried out through initializations blocks. In general, the shmem driver will be combined with the bin driver to save data in binary format (an alternative, somewhat more restrictive binary format is generated by the raw driver).

Let us now take a look at a modified version testsubshm.mos of our initial test submodel (Section Executing a submodel). This model reads in the index range from memory and writes back the resulting array to memory:

model "Test submodel"
 declarations
  A: range
  B: array(A) of real
 end-declarations

 initializations from "bin:shmem:indata"
  A
 end-initializations

 forall(i in A) B(i):= i^2

 initializations to "bin:shmem:resdata"
  B
 end-initializations

end-model 

The master model runsubshm.mos to run this submodel may look as follows:

model "Run model testsubshm"
 uses "mmjobs"

 declarations
  modSub: Model
  A = 30..40
  B: array(A) of real
 end-declarations
                                   ! Compile the model file
 if compile("testsubshm.mos")<>0 then exit(1); end-if
 load(modSub, "testsubshm.bim")    ! Load the bim file

 initializations to "bin:shmem:indata"
  A
 end-initializations

 run(modSub)                       ! Start model execution
 wait                              ! Wait for model termination
 dropnextevent                     ! Ignore termination event message

 initializations from "bin:shmem:resdata"
  B
 end-initializations

 writeln(B)

end-model 

Before the submodel run is started the index range is written to memory and after its end we retrieve the result array to print it out from the master model.

If memory blocks are no longer used it is recommended to free up these blocks by calling fdelete (subroutine provided by module mmsystem), especially if the data blocks are large, since the data blocks survive the termination of the model that has created them, even if the model is unloaded explicitly, until the module mmjobs is unloaded (explicitly or by the termination of the Mosel session). At the end of our master model we might thus add the lines

 fdelete("shmem:A")
 fdelete("shmem:B") 

Using the memory pipe driver

The memory pipe I/O driver mempipe works in the opposite way to what we have seen for the shared memory driver: a pipe first needs to be opened before it can be written to. That means we need to call initializations from before initializations to. The submodel (file testsubpip.mos) now looks as follows:

model "Test submodel"
 declarations
  A: range
  B: array(A) of real
 end-declarations

 initializations from "mempipe:indata"
  A
 end-initializations

 forall(i in A) B(i):= i^2

 initializations to "mempipe:resdata"
  B
 end-initializations

end-model 

This is indeed not very different from the previous submodel. However, there are more changes to the master model (file runsubpip.mos): the input data is now written by the master model after the submodel run has started. The master model then opens a new pipe to read the result data before the submodel terminates its execution.

model "Run model testsubpip"
 uses "mmjobs"

 declarations
  modSub: Model
  A = 30..40
  B: array(A) of real
 end-declarations
                                   ! Compile the model file
 if compile("testsubpip.mos")<>0 then exit(1); end-if
 load(modSub, "testsubpip.bim")    ! Load the bim file

 run(modSub)                       ! Start model execution

 initializations to "mempipe:indata"
  A
 end-initializations

 initializations from "mempipe:resdata"
  B
 end-initializations

 wait                              ! Wait for model termination
 dropnextevent                     ! Ignore termination event message

 writeln(B)

end-model 

Once a file has opened a pipe for reading it remains blocked in this state until it has received the requested data through this pipe. The program control flow is therefore the following in the present case:

  1. The master model starts the submodel.
  2. The submodel opens the input data pipe and waits for the master to write to it.
  3. Once the input data has been communicated the submodel continues its execution while the master model opens the result data pipe and waits for the results.
  4. When the result data pipe is open, the submodel writes to the result data pipe and then terminates.
  5. The master model prints out the result.

Shared data structures for cloned models

For the special case of submodels that are a clone of the master model itself (that is, the load for the submodel instance duplicates the master model in the same Mosel instance instead of reading in another BIM file) it is possible to declare shared data structures that are immediately accessible for all clones without any copying via initializations from/to. Note that only arrays, lists and sets of basic types (integer, real, boolean, string) can be shared between cloned models and the structure of the data cannot be changed while they are shared.

  • It is possible to assign new values to existing entries of an array, but it is not possible to add or remove entries from an array.
  • Sets and lists cannot be modified while being shared.
The parameter sharingstatus indicates the sharing status of a model which is one of: -1 (the model does not share any data), 0 (the model shares data but no submodel is using it), 1 (shared data is in use), 2 (the model is a submodel using shared data).

If we wish to work with this functionality for the small example from the previous section, we need to implement the master and submodel in a single file runsubclone.mos as shown below.

model "Sharing data between clones"
 uses "mmjobs"

 declarations
  modSub: Model
  A = 30..40                           ! Constant sets are shared
  B: shared array(A) of real           ! Shared array structure
 end-declarations

 if getparam("sharingstatus")<=0 then
  ! **** In master model ****
  writeln_("In master: ", B)           ! *** Output:  In master: [0,...0]
  load(modSub)                         ! Clone the current model
  run(modSub)                          ! Run the submodel...
  waitforend(modSub)                   ! ...and wait for its end
  writeln_("After sub: ", B)           ! *** Output:  After sub: [900,...1600]

 else
  ! **** In submodel ****
  writeln_("In sub: ", B)              ! *** Output:  In sub: [0,...0]
  forall(i in A) B(i):= i^2            ! Modify the shared data

 end-if

end-model