Getting started — cis_interface 0.3.0 documentation

# Getting started

The cis_interface runs user defined models and orchestrates asynchronous communication between models using drivers that coordinate the different components via threads. Model drivers run the models as seperate processes and monitor them to redirect output to stdout and determine if the model is still running, needs to be terminated, or has encountered an error. Input/output drivers connect communication channels (comms) between models and/or files. On the model side, interface API functions/classes are provided in different programming languages to allow models to access these comms.

## Running a model

Models are run by creating a YAML file that specifies the location of the model code and the type of model. Consider the following model which just prints a single line of output to stdout:

Model Code:

    1
    print('Hello from Python')

(Example in other languages)

The YAML file to run this model would then be:

Model YAML:

    1
    2
    3
    4
    models:
      - name: python_model
        language: python  # Runs the python script using default Python
        args: ./src/gs_lesson1.py

(Example in other languages)

The first line signals that there is a model, the second line is the name that should be associated with the model for logging, the third line tells the framework which driver should be used to run the model, and the forth line is the path to the model source code that should be run. There are specialized drivers for simple source written in Python, Matlab, C, and C++, but any executable can be run as a model using the ‘ModelDriver’ driver. Then the ‘args’ model parameter should be the path to the execuatable. Additional information on the format cis_interface YAML files should take can be found in the YAML Files section.

This model can then be run using the cis_interface framework by calling the commandline entry point cisrun followed by the path to the YAML.:

    $ cisrun model.yml

## Running multiple models

Multiple models can be run by either passing multiple YAML files to cisrun:

    $ cisrun model1.yml model2.yml

or including multiple models in a single YAML file.

Model YAML:

    1
    2
    3
    4
    5
    6
    7
    8
    models:
      - name: python_model1
        language: python  # Runs the python script using default Python
        args: ./src/gs_lesson2.py

      - name: python_model2
        language: python
        args: ./src/gs_lesson2.py

(Example in other languages)

## Model file input/output

Models can get input from or send output to files via input and output channels. To do so cis_interface provides several useful functions for interfacing with these channels. In the example below, the model receives input from a channel named ‘input’ and sends output to a channel named ‘output’.

Model Code:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # Import classes for input/output channels
    from cis_interface.interface.CisInterface import CisInput, CisOutput

    # Initialize input/output channels
    in_channel = CisInput('input')
    out_channel = CisOutput('output')

    # Loop until there is no longer input or the queues are closed
    while True:

        # Receive input from input channel
        # If there is an error, the flag will be False
        flag, msg = in_channel.recv()
        if not flag:
            print("No more input.")
            break

        # Print received message
        print(msg)

        # Send output to output channel
        # If there is an error, the flag will be False
        flag = out_channel.send(msg)
        if not flag:
            print("Error sending output.")
            break

(Example in other languages)

In the YAML used to run this model, those channels are declared in the model definition and then linked to files by entries in the `connections` section of the YAML.

Model YAML:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    models:
      - name: python_model
        language: python  # Runs the python script using default Python
        args: ./src/gs_lesson3.py
        inputs:
          - input
        outputs:
          - output

    connections:
      - input_file: ./Input/input.txt
        output: input
      - input: output
        output: ./output.txt

(Example in other languages)

The `input_file` and `output_file` connection fields can either be the path to the file (either absolute or relative to the directory containing the YAML file) or a mapping with fields descripting the file. In particular, the ‘filetype’ keyword specifies the format of the file being read/written. Supported values include:

Value | Description
--- | ---
binary | The entire file is read/written all at once.
ascii | The file is read/written one line at a time.
table | The file is an ASCII table that will be read/written one row at a time. If `as_array: True` is also specified, the table will be read/written all at once.
pandas | The file is a Pandas frame output as a table.
pickle | The file contains one or more pickled Python objects.
ply | The file is in Ply data format for 3D structures.
obj | The file is in Obj data format for 3D structures.

The above example shows the basic case of receiving raw messages from a channel, but there are also interface functions which can process these raw messages to extract variables and fields for the model `inputs` and `outputs` to specify how that should be done. For examples of how to use formatted messages with the above file types and input/output options, see Formatted I/O.

## Model-to-model communication (with connections)

Models can also communicate with each other in the same fashion. In the example below, model A receives input from a channel named ‘inputA’ and sends output to a channel named ‘outputA’, while model B receives input from a channel named ‘inputB’ and sends output to a channel named ‘outputB’.

Model Code:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # Import classes for input/output channels
    from cis_interface.interface.CisInterface import CisInput, CisOutput

    # Initialize input/output channels
    in_channel = CisInput('inputA')
    out_channel = CisOutput('outputA')

    # Loop until there is no longer input or the queues are closed
    while True:

        # Receive input from input channel
        # If there is an error, the flag will be False
        flag, msg = in_channel.recv()
        if not flag:
            print("Model A: No more input.")
            break

        # Print received message
        print('Model A: %s' % msg)

        # Send output to output channel
        # If there is an error, the flag will be False
        flag = out_channel.send(msg)
        if not flag:
            print("Model A: Error sending output.")
            break

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # Import classes for input/output channels
    from cis_interface.interface.CisInterface import CisInput, CisOutput

    # Initialize input/output channels
    in_channel = CisInput('inputB')
    out_channel = CisOutput('outputB')

    # Loop until there is no longer input or the queues are closed
    while True:

        # Receive input from input channel
        # If there is an error, the flag will be False
        flag, msg = in_channel.recv()
        if not flag:
            print("Model B: No more input.")
            break

        # Print received message
        print('Model B: %s' % msg)

        # Send output to output channel
        # If there is an error, the flag will be False
        flag = out_channel.send(msg)
        if not flag:
            print("Model B: Error sending output.")
            break

(Example in other languages)

In the YAML, ‘inputA’ is connected to a local file, ‘outputA’ is connected to ‘inputB’, and ‘outputB’ is connected to a local file in the `connections` section of the YAML.

Model YAML:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    models:
      - name: python_modelA
        language: python
        args: ./src/gs_lesson4_modelA.py
        inputs: inputA
        outputs: outputA

      - name: python_modelB
        language: python
        args: ./src/gs_lesson4_modelB.py
        inputs: inputB
        outputs: outputB

    connections:
      - input: outputA  # Connection between model A output & model B input
        output: inputB
      - input: ./Input/input.txt  # Connection between file and model A input
        output: inputA
      - input: outputB  # Connection between model B output and file
        output: ./output.txt

(Example in other languages)

## Model-to-model communication (with drivers)

For backwards compatibility, connections can also be specified in terms of the underlying drivers without an explicit `connections` section. The exact same models from the previous example can be connected using the following YAML.

Model YAML:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    models:
      - name: python_modelA
        language: python
        args: ./src/gs_lesson4b_modelA.py

        inputs:
          - name: inputA
            driver: FileInputDriver
            args: ./Input/input.txt

        outputs:
          - name: outputA
            driver: OutputDriver  # Output to another channel
            args: A_to_B  # Connection to inputB

      - name: python_modelB
        language: python
        args: ./src/gs_lesson4b_modelB.py

        inputs:
          - name: inputB
            driver: InputDriver  # Input from another channel
            args: A_to_B  # Conneciton to inputA

        outputs:
          - name: outputB
            driver: FileOutputDriver
            args: ./output.txt

(Example in other languages)

In this schema, model `input` and `output` entries must have the following fields:

Field | Description
--- | ---
name | The name of the channel that will be used by the model.
driver | The name of the driver that should be used to process input/output.
args | A string matching the args field of an opposing `input` / `output` field in another model or the path to a file that should be read/written.

A list of possible Input/Output drivers can be found here.

Todo

Link to example with translation at connection.