Chapter 1 - Building a Module

Chapter 1 - Building a Module

This chapter takes you step by step through the process of creating a simple module in the Module Builder. The proposition is to build a module that acts as a channel selector. This module will take in an array containing a vector of values per sample, and output an array with a scalar at each sample. The user manipulates a widget to select which scalar, or channel, is output.

The example shows how to build a module with a user function written either in C or in Fortran. The process is identical for both, but some menus differ, depending on the language used. These differences are called out clearly in the text.

The information on building a module spans two chapters:

If you want more information than is given in Chapter 1 about a particular step, refer to the parallel section in Chapter 2.

Introduction to Module building

The IRIS Explorer product uses modules to carry out operations on data in order to visualize the data in a way that has meaning to the observer. The modules supplied with IRIS Explorer offer a range of functions, but many users will want to construct their own modules, providing a more specific function or greater power than already exists.

The Module Builder is a tool that lets you build your own IRIS Explorer modules, either by modifying and renaming existing IRIS Explorer modules or by creating new ones. The great virtue of the Module Builder is its graphical user interface, which lets you build a basic module with no programming beyond that needed to write the computational function.

The module-building process has three main stages:

Once the module is built, you can wire it into a map in the Map Editor and use it like any IRIS Explorer module.

Creating the User Function

The user function is the computational function or subroutine that acts on the data fed into the module through its input ports and produces new or changed data on the output ports. It provides the core functionality of the module.

Designing the Example Module

You are designing a module that will select one data variable, or channel, from an array that has a number of data variables. Arrays in IRIS Explorer are called lattices. The module is a simple selector with no limits on the range or data type. Here is a brief description of the structure you need in the new module, which you will call ChannelSelector.

It should take in all the data variables from the input lattice and put out values for only one data variable. It should have a widget for selecting a particular data variable. It needs an input port, an output port, some defined variables for the lattice data and coordinate values, and a computational function to select out the data values for a specific channel.

Writing the User Function

The example shows how you can create a channel selector module with a C function or a Fortran subroutine. The module accepts input data in the form of an IRIS Explorer lattice. You need not be concerned with lattices now, except to note that as a matter of convention, the variable names in this function correspond closely with the variable names in the cxLattice data type structure.

To find out more about lattices, read Chapter 3, "Using the Lattice Data Type".

Example Code and Modules

Working example code for this module can be found in /usr/explorer/src/MWGcode/Tutorial/C/channel.c and /usr/explorer/src/MWGcode/Tutorial/Fortran/channelselect.f. There are also complete module resources for this code, called ChannelSelector.mres in the same directories. You can open the respective files to see how yours should look.

In the following sections you are taken through the steps required to construct a module using existing code, while imposing as few restrictions on the module as is feasible. This situation will occur frequently if you reuse existing and well tested code, for example from a library. Some steps might well be different if you decide to write code specifically for the module. This is indicated in the text.

The C User Function

The function called chan in the file channel.c serves as the user function in this module. It is written in C. The code shows the source of the function arguments used later in this chapter.

Note that the code is written for float data only. Through the specification in the module resources file, IRIS Explorer can ensure that this code may also be used for other data of a different type. To find out more about automatic type coercion see "Automatic Type Coercion of Arrays" in Chapter 2.

/* User function to select a single data channel
 * from a lattice of type float
 *
 * The Iris Explorer wrapper code will ensure that lattice data
 * of a different type will be converted to float before being
 * passed to this function, and the output lattice converted from
 * float to the type of the input lattice.
 *
 * This feature enables you to write a much more general module that
 * can deal with all data types, not just float. This will be of
 * particular use when you need to utilise already existing code.
 */
long sizes(long nDim, long *dims);

void chan(long nDim, long *dims, long *nDataVar, long which,
	     float *dataIn, float *dataOut)
{
  int i, numElems, sel;

  /* Number of data channels in the input lattice */
  numElems = sizes(nDim, dims);

  /* Use a legal range for the data */
  sel = which - 1;
  if (sel < 0)
    sel = 0;
  else if (sel >= (*nDataVar))
    sel = (*nDataVar) - 1;

  /* Copy the data to the output lattice */
  for (i = 0; i < numElems; i++)
    dataOut[i] = dataIn[i*(*nDataVar)+sel];
  *nDataVar = 1;
  return;
}

long sizes(long nDim,long *dims)
{
  long foo, i;

  foo = 1;
  for (i = 0; i < nDim; i++)
    foo *= dims[i];
  return(foo);
}
    

The Fortran User Function

The Fortran subroutine called chan in the file channelSelect.f is the user function in this module. This code fragment shows the source of the function arguments used later in this chapter (footnote) .

Note that the code is written for REAL data only. Through the specification in the module resources file, IRIS Explorer can ensure that this code may also be used for other data of a different type. To find out more about automatic type coercion see "Automatic Type Coercion of Arrays" in Chapter 2.

C     Subroutine to read in a two-dimensional array
C     and output a one-dimensional array, both of type REAL
C
C     The IRIS Explorer wrapper code will ensure that lattice data
C     of a different type will be converted to REAL before being
C     passed to this function, and the output lattice converted from
C     REAL to the type of the input lattice.
C
C     This feature enables you to write a much more general module that
C     can deal with all data types, not just REAL. This will be of
C     particular use when you need to utilise already existing code.
C
      SUBROUTINE CHAN(NDIM,DIMS,NVARS,WHICH,DATAIN,DATOUT)
C     .. Scalar Arguments ..
      INTEGER         NDIM, NVARS, WHICH
C     .. Array Arguments ..
      REAL            DATAIN(NVARS,1), DATOUT(1)
      INTEGER         DIMS(NDIM)
C     .. Local Scalars ..
      INTEGER         I, J, NUM
C     .. External Functions ..
      INTEGER         SIZES
      EXTERNAL        SIZES
C     .. Intrinsic Functions ..
      INTRINSIC       MAX, MIN
C     .. Executable Statements ..
C
C     Number of elements that needs to be copied
C
      NUM = SIZES(NDIM,DIMS)
C
C     Data channel that needs to be copied
C
      J = MIN(MAX(1,WHICH),NVARS)
C
C     Copy the elements into the output array
C
      DO 20 I = 1, NUM
         DATOUT(I) = DATAIN(J,I)
   20 CONTINUE
C
C     Number of data channels in the output lattice
C
      NVARS = 1
C
      RETURN
      END
C     Subroutine to determine the number of elements in a lattice
C
      INTEGER FUNCTION SIZES(NDIMS,DIMS)
C     .. Scalar Arguments ..
      INTEGER                NDIMS
C     .. Array Arguments ..
      INTEGER                DIMS(NDIMS)
C     .. Local Scalars ..
      INTEGER                I
C     .. Executable Statements ..
C
      SIZES = 1
      DO 20 I = 1, NDIMS
         SIZES = SIZES*DIMS(I)
   20 CONTINUE
C
      RETURN
      END
    

Since cxLattice is a C data structure, the Fortran code contains pointers to the cxLattice data type (see Chapter 3, "Using the Lattice Data Type"). )

Setting up your Build Environment

Before you start to build this module, you need to set up a test directory and copy the example code into it.

  1. Make a subdirectory in your home directory as follows:
    cd ~
    mkdir testMod
        
  2. Change to the new subdirectory and copy the example code from /usr/explorer/src/MWGcode/Tutorial/* into testMod:
    cd testMod
    cp /usr/explorer/src/MWGcode/Tutorial/C/channel.c . (C)
    cp /usr/explorer/src/MWGcode/Tutorial/Fortran/channelselect.f . (Fortran)
        

You can now build your module using the Module Builder and run it in the Map Editor. Just follow the steps in the next sections.

C or Fortran?

The method for building modules is the same for both languages, although some of the information you enter varies according to whether the user function is written in C or Fortran. Because the similarities are so great, this tutorial presents the process for users of both languages together. Where the activities for the two language diverge, the differences are clearly marked and parallel instructions are given for each language.

Invoking the Module Builder

Make sure you are in the testMod directory, then start up the Module Builder:

mbuilder
   

The Module Builder main window appears (see Figure 1-1 and Figure 1-2). Click in the Module Name slot to select it and delete the default, then type in the module name ChannelSelector. In the next slot, enter the name of the file that contains the user function:

C Users:
channel.c
Fortran Users:
channelselect.f

You can also specify include files, additional libraries or your own Makefiles, but none are required for building the ChannelSelector module. To learn more about these options, read through"The Include Files" and "User Makefiles" in Chapter 2.



Figure 1-1 The Module Builder Window for C Users



Figure 1-2 The Module Builder Window for Fortran Users

Saving the Module Resources

The Module Builder creates a number of files as you go through the construction process. The Module Builder automatically saves all the module characteristics, or resources, that you have just specified in the module resource (.mres) file before it builds the module. This is a binary file and cannot be accessed directly.

You can use "Save Resources" on the File menu to save your work at any time during the module construction phase. It is a good idea to save your work periodically if you expect to be interrupted or if you are building a very complex module.

Exiting from the Module Builder

To quit the Module Builder at any time once it has been invoked, click on the File menu on the main window and select "Quit" (see Figure 1-3).



Figure 1-3 Module Builder File Menu

Defining the Internal Structure

The inputs, outputs, function arguments, and connections among different variables constitute the internal structure of the module, and you define this structure in the Module Builder windows. You can have only one window open at a time, other than the main window.

Creating an Input Port

Click on the first button, Input Ports, to bring up the Input Ports window (see Figure 1-4).

Note: The window is initially displayed with only one text slot. Click on the slot to highlight it and press Enter to produce a new text slot.

The module requires an input port that will accept data in the form of the lattice data type.

  1. Click in the first text slot to highlight it, then type the name "Input" in the first text slot to create an input port.
  2. Click on the button on the option menu in the middle column to choose the data type menu and select cxLattice.


Figure 1-4 Input Ports Window

Defining the Data Type

When you select cxLattice from the data type option menu, the Lattice Constraints window appears (see Figure 1-5). To define the characteristics of the lattices the input port will accept:

  1. Click off the yellow buttons to get the results shown in Figure 1-5.

    The input port called "Input" will now accept a lattice of any dimension and data type, containing any number of data variables and with any number of coordinates per node. Note that it is good practice to write modules with as few restrictions as possible. As the user functions have restrictions on the data types, the generality is in this case achieved through automatic type coercion by IRIS Explorer wrapper code. In general, you need not change existing code to widen its application range; IRIS Explorer can do this for you.

  2. Data values are required, but coordinate values need not be given, so Data Structure is marked "Required" and Coord Structure is "Optional".
  3. Click OK to save the setting and close the window.


Figure 1-5 The Lattice Constraints Window

Defining the Parameters

The next step is to define a parameter for the input port in the Input Ports window (see Figure 1-4).

  1. Click the second slot in the Input Ports window and enter the parameter name "Which".
  2. Select cxParameter from the data type option menu.
  3. Select Required from the option buttons in the third column for both ports.
  4. Click OK to save the setting and close the window.

The module must receive data on both its input ports before it will fire, so each port is made a "Required" port.

Creating an Output Port

Click the Output Ports button to bring up the Output Ports window (see Figure 1-6). The module needs an output port so that it can pass the data from the selected channel downstream to the next module.



Figure 1-6 The Output Ports Window

To create the output port:

  1. Enter the name of the output port, "Output", in the text slot.
  2. Select cxLattice from the data type option menu.

    When the Lattice Constraints window appears (see Figure 1-7), set the constraints. All the constraints, except the Num Data Variables, are the same as those for the input lattice, however, the Num Data Variables is set to 1 in the output lattice because only one data channel is being output.



    Figure 1-7 Lattice Constraints for the Output Port
  3. Click OK to save the constraints setting and close the window.
  4. Click OK to save the output port setting and close the Output Ports window.

For more information about input and output ports, read "Creating Ports" in Chapter 2.

Defining the Function Arguments

The Function Arguments window defines each function argument in the user function. Each function argument must be connected to an input or output item. You list the function arguments in their calling sequence, as shown in Figure 1-8 and Figure 1-9.

To define the function arguments:

  1. Click the Function Args button to display the Function Arguments window. When the window first appears, all of its fields are blank.
  2. Enter the name of the function, chan, in the Func Name slot. This is the name of the function contained in the channel.c or channelselect.f file.
  3. Set the language option. If you are using the C function, select "C" from the language option menu. If you are using the Fortran subroutine, select "Fortran" from the language option menu.
  4. Enter the first function argument for the user function in the text slot. Press Enter after typing each function argument name to generate a new text slot.

    The function arguments for the C function are shown in Table 1-1 and Figure 1-8.

    The function arguments for the Fortran subroutine are shown in Table 1-2 and Figure 1-9.

  5. Set the corresponding values for the argument type and reference by clicking each option menu button in turn. The C values differ from the Fortran values, as you will see from the function argument tables.
  6. When you have entered all of the arguments, click Apply to have them accepted but keep the window open.
  7. At the bottom of the window, you will see the function name displayed with all its arguments listed. Click OK to close the window and return to the Module Builder main window.

For more information about the data types and reference mechanisms, read "Defining Function Arguments" in Chapter 2.

These are the function arguments for the C function.

Argument Name Type References
nDim int Scalar
dims int Array
nDataVar int &Scalar
which int Scalar
dataIn float Array
dataOut float Array
Table 1-1 Function Arguments for the C Function

These are the function arguments for the Fortran subroutine.

Argument Name Type References
nDim integer Scalar
dims integer Array
nDataVar integer Scalar
which integer Scalar
dataIn real Array
dataOut real Array
Table 1-2 Function Arguments for the Fortran Subroutine

Figure 1-8 shows the argument names, data types, and references you enter if you are building your module around the C user function.



Figure 1-8 The Function Args Window for C Users

Figure 1-9 shows the argument names, data types, and references you enter if you are building your module around the Fortran user function.



Figure 1-9 The Function Args Window for Fortran Users

Connecting Ports and Function Arguments

Each port must be connected with one or more function arguments, so that the incoming data can be routed to the correct function argument and the outgoing data can be directed to the correct output port.

The connections are identical for C and Fortran user functions.

  1. Click on the Connections button to invoke the Connections window (see Figure 1-10). When it is first displayed, no connecting wires exist. These appear as you wire ports and function arguments together.
  2. To connect an input port to a function argument or output port, click with the right mouse button on the input port name bar. A popup menu appears, listing the data structures associated with the port. For example, the input port "Input" accepts lattice data, and its popup menu (see Figure 1-11) lists the lattice substructures. They contain data arriving on the module's input port from upstream. The connections you make from the data structures on the input ports to the function arguments determine which data are recognized and processed, and which data are ignored.
  3. Select the structure member you want, for example, Dimensions Array. The function arguments and ports that can accept a connection are highlighted in green (output ports are darker green).


    Figure 1-10 The Connections Window
  4. Then click on a function argument, for example, dims, with the right mouse button. A small Select button appears (see Figure 1-10).

    When you click Select, a blue wire links the input port with the function argument.

  5. Follow the same procedure to link input ports or function arguments to the output port. The output port menu items are printed in blue if they require a connection for the integrity of the data structure (see "Connecting Arguments to Ports", in Chapter 2).

To break a connection, simply click on both ends of the connection, as in the Map Editor.

The popup menu in Figure 1-11 shows one connection from the input port (top-level Lattice Structure) to the output port.



Figure 1-11 Input Port Popup Menu

It also shows four connections from lattice components, such as the number of dimensions (Num Dimensions), to function arguments. The Output port has three connections passing into it, although the port menu is not shown. The details of the data type structures are explained in Chapters 3 through 7.

Making the Right Connections

The correct connections are critical to the proper operation of the module. In this example, the values affected by the function arguments are passed separately to the lattice structure on the output port. The values for all members of the lattice data type not explicitly linked to function arguments are passed directly from the input port to the output port as default values.

For more about connecting ports and function arguments, read "Connecting Arguments to Ports" in Chapter 2.

To build the ChannelSelector module correctly, you must make these connections. They are the same for the C and the Fortran modules.

Input
Lattice Structure to port Output: Lattice Structure

Num Dimensions to argument nDim

Dimensions Array to argument dims (switch off Copy by pressing the yellow button; see Figure 1-12)

Num Data Variables to argument nDataVar

Data Array to argument dataIn

Which
Value to argument which (see the Port popup menu)


Figure 1-12 Array Function Argument. The Copy button on the left is switched off, so the array itself is passed to the function.


Figure 1-13 Port Popup menu for a Parameter
<<Storage>>
"Select" to argument dataOut, then set the storage value (see Figure 1-14 or Figure 1-15).

Note that storage for arrays must be allocated in this way for Fortran programs, as in general there is no Fortran equivalent of the C malloc function. C programmers might prefer to allocate storage for arrays in the usual way with malloc inside their programs, rather than using the IRIS Explorer <<Storage>> connection.

nDataVar
"Select" to port Output: Num Data Variables
dataOut
"Select" to port Output: Data Array

Using the Pseudoports

<<Storage>>, <<Constant Value>> and <<Extern>> are "pseudo-ports". When you connect one to a function argument, you use the second item on the function argument popup menu) to define the value for the pseudo-port. In this example, you need to define the storage value. The value is different for C and Fortran modules.



Figure 1-14 Function Argument Menu for an Array Item

To set the storage size for dataOut, click the menu item "Set storage size" and enter the text shown in Figure 1-15 and Figure 1-16 for the C and Fortran modules respectively. There is a default setting which you should delete first.

This text is expected by the code that the Module Builder generates when the module is built. In this example, a routine is called to compute the number of floating (C) or real (Fortran) values that must be allocated for the dataOut function argument.



Figure 1-15 Setting Storage Size for C Users


Figure 1-16 Setting Storage Size for Fortran Users

You can enter comments after the values if you wish, to remind you and explain to others why you chose this value.

When you have filled in all the popup windows and made all the connections you need, click OK to save the configuration and close the window.

For more information about the use of pseudo ports, read "Creating Ports", in Chapter 2.

Defining the User Interface

The module has an interface that allows it to be controlled by the user. This interface is the module control panel. See "Editing Control Panels and Functions" in the IRIS Explorer User's Guide for more details on control panel design and widget use.

The module control panel holds widgets, such as dials and sliders, that let you control the values of input port parameters interactively.

Click the Control Panels button to invoke the Control Panel Editor. The Control Panel Editor and a prototype control panel appear (see Figure 1-17).



Figure 1-17 Control Panel Editor and Control Panel

All ports of type cxParameter appear in the "Parameters" list in the Control Panel Editor. In this example, "Which" is a parameter port (see Figure 1-17). It is characterized in the control panel as a slider by default because its values are floating or real.

To change the widget for "Which" to another kind, click the left mouse button on it to select it, then click the Type option menu under Widget Attributes. You can select any widget type that is not grayed out.

To change the widget's position in the control panel, click it to highlight it, then hold down the left mouse button and drag it into a new position.

To change the widget's size, use the type-in slots to enter a new set of dimensions, or drag on any of the eight black handles around the widget.

To set the lower limit for the slider, double-click on the left-hand text slot beneath the slider and type 1, then hit Enter. The module input port can accept a lattice with any number of data variables, and so the upper limit for the slider will be unknown in general, but you can still set a sensible value for this number. So, type 5 in the right-hand slot and hit Enter (see Figure 1-17). Finally, you can enter a current value for the parameter in the text field above the slider.

Creating a Menu Item

You can create your own menu bar options for the module control panel. For this module, you will create a File menu with a "Reset" option.

  1. Select "Menu Bar" from the Edit menu on the Control Panel Editor. The Menu Bar Editor appears (see Figure 1-18).
  2. Type File in the first slot to name the menu.


    Figure 1-18 Menu Bar Editor
  3. Click the Menu button underneath the slot. The Menu Editor appears (see Figure 1-19).
  4. Type Reset in the first menu item slot to name the action.
  5. Click the option menu button next to the first slot and select the action "Set param" from the option menu.

    A new option menu button appears on the right, together with a text slot.

  6. Click the option menu button and select the parameter which is to be set (in this case, there is only one parameter, "Which").
  7. Type 1 in the text slot to specify the parameter value.
  8. Click OK to apply the selection and close the window.

    You can close each open window individually, or you can select OK on the Control Panel Editor, which closes all the subsidiary windows and applies the changes you have made.



Figure 1-19 Menu Editor

Testing the Menu Bar

You can click on the Control Panels button to bring up the prototype control panel again. You will see that it now has a File menu on its menu bar. Click File to see the "Reset" option.

This completes the construction of the module. The next stage is to build the module and install it in IRIS Explorer so that you can use it in a map.

Building the Module

You have a blueprint for a module that must now be turned into an executable program. The code is linked and compiled during the build process.

Selecting Build Options

The Module Builder Build menu provides lets you set defaults and build the module. To check the build options for the Channel module, select "Options" from the Build menu. The Build Options window appears (see Figure 1-20).



Figure 1-20 Build Options Menu

You want to accept all the current defaults. You want the Module Builder to:

Click OK to accept the default options and close the window.

For more information about build options, read "Using the Build Menu", in Chapter 2.

Running the Build Command

The Build command is on the Build menu (see Figure 1-21).

  1. Click on the Build menu and select "Build". This command builds the module in the current directory, which is ~/testMod in this case.

    If you receive this message:

    compile, link or install MAY have failed,
         

    check to see that you are running the Module Builder from your current directory.



    Figure 1-21 Build Menu Options
  2. Select "Quit" from the File menu to exit the Module Builder.

You can now run the Map Editor by typing explorer at the shell prompt, and launch the ChannelSelector module from the Module Librarian.

To see how it works, connect these modules: ReadImg to ChannelSelector to DisplayImg. Read in the image file /usr/explorer/data/image/flowers2.rgb and select each of the color channels, 1 to 3, in turn. You may also reset the channel to 1 by clicking on the "File" option on the menu bar and selecting "Reset".

Building a Module which uses the API

The source for the example module discussed here does not make use of the IRIS Explorer Application Programming Interface (API). In this section, we present an alternative module which performs the same function as the example module, but which uses the API. The API is a library of routines which give access to the creation and manipulation of the IRIS Explorer data types. It is described fully in the IRIS Explorer Reference Pages. The advantage of using the API is that it allows IRIS Explorer types to be handled within the function, which simplifies the way in which the function is connected to the module ports. The disadvantage is that the source must of course be modified to to invoke the relevant routines.

The alternative source (in C and Fortran) which uses the API is presented in The Channel Select Module in Chapter 2. Here, we describe the differences between its module resources and those which have been constructed in this Chapter. These only appear in two places - the Function Arguments and the Connections. Everything else - the definition of the ports and the control panel - is unchanged.

The Function Arguments

Figure 1-22 and Figure 1-23 show the Function Args Window for the C and Fortran versions of the module. In C, the input and output lattices are both typed as cxLattice*, with the input lattice being passed as a Scalar, and the output lattice as a &Scalar (i.e. the address of a scalar). In Fortran, both lattices are typed as an integer and passed as a Scalar. The close correspondence between the definitions in the window and those on the function call line can clearly be seen.



Figure 1-22 The Function Args Window for C Users


Figure 1-23 The Function Args Window for Fortran Users

The Connections

The Connections Window is the same for both C and Fortran versions of the module. The Lattice Structure component of the Input port is connected directly to inLat, and outLat is connected directly to the Lattice Structure component of the Output port. Because the output lattice is created inside the module (via a call to the cxLatNew API function) there is no need to use the <<Storage>> pseudo input port together with an internal function to calculate its size, as was required in the example module.



Figure 1-24 The Connections Window

You can mix IRIS Explorer datatypes with the other primitive datatypes on the function call line. For example, there is a connection from Value component of Which to the which argument, as before.


Last modified: Tue Nov 26 11:30:59 1996
[ Documentation Home ]
© NAG Ltd. Oxford, UK, 1996