Example of Callable IDL

Don't be daunted by using callable IDL.  It might look complex at first glance but you only need to remember 2 rules:

  1. Use your computer's copy and paste (or save as) abilities to duplicate this example and make your modifications.
  2. Read the comments which will direct you where to make your changes from the example.  To make it easier, a color code is used.  Black text is the original fortran.  Green is what you should simply duplicate and add to your own code without changes.  Blue should be used as model and customized to fit your own progam.  To make the colors easier to see, a darker grey background is used fo this page.  Your browser preferences can be set to use these colors, or it may be overridden.


This example uses callable IDL to plot graphs from a program called align.f.  This is the same align.f program that was re-written in IDL in the re-write example.  You'll see that it's a little more complex, but if your code is longer than one page, this may be the easiest way afterall.

INTRO:  IDL was written as a language to write from scratch.  Using Fortran to call IDL's capabilites to plot graphs is a little klugy, but it can be done without too much trouble if you use this example as a template.  The Fortran code calls a "C wrapper."  This short C program will not need to be changed or even opened.  Just keep a copy of it in the directory where you have your program.  Then you will add a

Believe it or not, the easiest way for this to work is for your Fortran program to call a C language "wrapper" procedure, which in turn calls IDL.  You will have to delete the disspla calls from your code and add the new calls.  For this example, the edited Fortran code is called align2.f.  The C wrapper is wrapalign.c.  You will also need a Makefile and shell script to compile and execute the programs.  For further information on compiling, go to  Compiling Callable IDL.  Remember, all you have to do is copy much of this without worrying.  If you click on the links to the code, you'll see complete versions of the code.  Below, the code has been commented on with colored text.


 align2.f
      PROGRAM ALIGN
      DIMENSION Y1(80),Y2(80),Y3(80),Y4(80),XP(80),Y5(80)

The following three variables must be added to your declarations
      INTEGER*4 options
      INTEGER i_ret
      INTEGER just_cleanup

C -----------------------------------------------------------------------------
You must initialize IDL before you make any calls.  I initialize up near the top of my program so I don't accidently put code in that calls IDL before it's been initialized.  Just cut and paste this as is.

C  Call IDL Initialization
      call IDL_Init (options, i_ret)

C -----------------------------------------------------------------------------
      EX=0.0
      DEX=.1
      JMAX=80
      DO 15 J=1,JMAX
      X1=EX
      X2=EX/2.
      X3=EX/3.
      X4=EX/4.
      X5=EX/5.
      Y1(J)=X1*(2.+X1)/(1.+X1)**2
      Y2(J)=X2*(2.+X2)/(1.+X2)**2
      Y3(J)=X3*(2.+X3)/(1.+X3)**2
      Y4(J)=X4*(2.+X4)/(1.+X4)**2
      Y5(J)=X5*(2.+X5)/(1.+X5)**2
      XP(J)=EX
 15   EX=EX+DEX

C -----------------------------------------------------------------------------
This is the meat of calling IDL. It requires importing the Fortran arrays to IDL.  Here, you are actually calling a C procedure, with the slightly deceptive name "IDL_ImportNamedArray."  The arguments that are passed to C are the arrays that will be plotted by IDL.
C
C  Call IDL ImportNamedArray
      call IDL_ImportNamedArray (Y1, Y2, Y3, Y4, Y5, XP, i_ret)
 

C -----------------------------------------------------------------------------
By now, in your Fortran program you've sent the arrays to C which has had IDL make spiffy graphs.  IDL finished its job and the C wrapper has sent control back to Fortran.  Fortran is done, too, so now you have to close out IDL by using IDL_Cleanup.  Again, this is one of those times where you just copy this code into your program and don't worry about the why's and wherefores.  One minor note, however, when just_cleanup is set to 1, IDL shuts down.  The usual way is for just_cleanup to be set to 0 which causes IDL to exit after it shuts down.

C  Call IDL Cleanup
      just_cleanup = 1
      call IDL_Cleanup (just_cleanup, i_ret)

C -----------------------------------------------------------------------------
      STOP
      END


wrapalign.c

#include <stdio.h>

#include "export.h"   export.h is a header file that does all the the nasty coding that you'd have to do explicitly in Fortran.  This example uses the power of export.h to avoid writing lots of confusing code.

/**************************************************************************/
static void
free_callback(UCHAR *addr)
 {
 printf("IDL released(%u)\r\n", addr);
 }
This is another one of those pieces of code you copy into your files without worrying about why.  Be sure to keep it up here near the top of your C wrapper.

/**************************************************************************/
void
idl_init_ (int *options, int *i_ret)
 {
 int argc;
 char *argv;

 argc = 0;
 argv = NULL;
 *i_ret = IDL_Init (*options, &argc, &argv);
 }
This is another one of those pieces of code you copy into your files without worrying about why.  Be sure to keep it up here near the top of your C wrapper.

/**************************************************************************/
This is the meat of where you pass your array(s) to IDL to have them printed.  Pay attention to the notes because you'll have to modify the code to match your data.

In the function definition you'll need to add your arrays for the arguments.  In this case there are 6 arrays of type float, and one return variable.  I've made it type long because it's a pointer which will hold a very large number, and it isn't adequate to have it be type int.  Also, the arguments must be pointers here in C.  That's why there's an * before i_ret.  The arrays don't have *s because by definitions they are pointers.

This is a C function in spite of the name that is prefaced with idl. Within here, you'll actually call IDL.  Note that when your Fortran program calls this C function it's going to need two things:  1) all lower case letters, and 2) an underscore at the end of the name.
void
idl_importnamedarray_ (float Y1[], float Y2[], float Y3[], float Y4[],  \
   float Y5[], float XP[], long *i_ret)
        {

This is the declaration of an array of commands that IDL will use later on. This set of commands creates a "widget" that is a little window that pops up.  It remains on the monitor until you place the mouse arrow into the window and click.  At that point the window closes.  Without the widget the graph is sent to the monitor, then when the program finishes the graph window closes.  All this occurs in microseconds and you'd never have a chance to inspect the graph.  With the widget, you can look at the graph on the monitor as long as you like before clicking on the widget and removing both the widget and the graph.  If you want to see the graph on the monitor, cut and paste these commands into your C wrapper.
 static char *cmds2[] = {"a = widget_base()",
  "b = widget_button(a, value='Press When Done', \
  xsize=300, ysize=200)",
  "widget_control,/realize, a",
  "dummy = widget_event(a)",
  "widget_control,/destroy, a" };

This is the declaration of an array of commands that IDL will use to send the graph to a postscript file.
static char *cmds[] = { "SET_PLOT, 'PS'",
        "DEVICE, FILENAME='align.ps'",
        "DEVICE,/INCHES,YOFFSET=2",
      };
 
These are the declarations for your IDL variables.  You'll have one of these pointers for each of your arrays.  In this case I've taken IDL's conventional "v" and appended the name of the array to them.
 IDL_VPTR vY1;
 IDL_VPTR vY2;
 IDL_VPTR vY3;
 IDL_VPTR vY4;
 IDL_VPTR vY5;
 IDL_VPTR vXP;
 
dim is the dimension of the arrays.  It is of type IDL_LONG.  IDL sets up some predetermined maximum number of dimensions.  I don't know what that maximum is, but it's probably much bigger than your number of dimensions which will probably be only 1 or 2.  Anyway, this declaration simply announces that you'll be using the variable dim (i.e., cut and paste this statement into your code without worrying about it).
 IDL_LONG dim[IDL_MAX_ARRAY_DIM];

All of the arrays in this example are one dimensional.  Because IDL is like C, it describes the first (and only) dimension as "0."  In this case, all the arrays are the same size:  they each have 80 elements.
 dim[0] = 80;

Here, at the end of the variable declarations, I'm putting the call to execute the commands in the array which sets up the PostScript file.  Note you can either set up the array of commands and execute them all at once using "IDL_Execute" or you can type them in one at a time using "IDL_ExecuteStr" which is used below to make the plots.
 IDL_Execute(sizeof(cmds)/sizeof(char *), cmds);

Here's the important part where you send the array from C to IDL.  You set the pointer equal to the return of the IDL function IDL_ImportNamedArray.  There are several arguments, some of them you simply copy as shown here, and others you must tailor to your own arrays.  The first argument, in this case "idl_Y1" is the name of the array once IDL recognizes it; the variable either created or modified.  I simplyl added idl_ to the name of the array in C.  Note that you must use quotation marks here.  The second argument, in this case 1, indicates that there is 1 dimension to the array.  Note that it is a 1 dimensional array, whose first (and only) dimension is 0 (don't blame me for this convention confusion!).  The third argument is simply dim.  The fourth argument gives the type that the array is.  Other types you might use are:  IDL_TYP_INT, IDL_TYP_LONG, IDL_TYP_DOUBLE, IDL_TYP_COMPLEX, AND IDL_TYP_STRING (for more details see table 5-1 in the IDL Advance Development Guide (v. 5.0) or IDL External Development Guide (v. 5.1).  The fifth argument is a pointer to your array data.  Type (UCHAR *) variable-name-from-arguments-in-the-C-function-definition.  The sixth argument is always free_callback.  This is where that function defined at the top of your C wrapper is called.  The seventh argument is used for structures, that in most cases aren't used, so simply leave it as (void *) 0.
 vY1 = IDL_ImportNamedArray("idl_Y1", 1, dim, IDL_TYP_FLOAT,
  (UCHAR *) Y1, free_callback, (void *) 0);

 vY2 = IDL_ImportNamedArray("idl_Y2", 1, dim, IDL_TYP_FLOAT,
  (UCHAR *) Y2, free_callback, (void *) 0);

 vY3 = IDL_ImportNamedArray("idl_Y3", 1, dim, IDL_TYP_FLOAT,
  (UCHAR *) Y3, free_callback, (void *) 0);

 vY4 = IDL_ImportNamedArray("idl_Y4", 1, dim, IDL_TYP_FLOAT,
  (UCHAR *) Y4, free_callback, (void *) 0);

 vY5 = IDL_ImportNamedArray("idl_Y5", 1, dim, IDL_TYP_FLOAT,
  (UCHAR *) Y5, free_callback, (void *) 0);

 vXP = IDL_ImportNamedArray("idl_XP", 1, dim, IDL_TYP_FLOAT,
  (UCHAR *) XP, free_callback, (void *) 0);

Now that IDL has received your arrays, you're ready to plot them.  Here I used IDL_ExecuteStr.  Note that after typing IDL_ExcuteStr you must put your IDL command in quotation marks because you're executing a string that contains a command.  Be sure to use the IDL version of the name of your variables/arrays.  Be sure to not use the IDL symbol $ at the end of each line that is still part of the same command.  You're in a C function and even though you're issuing an IDL command, the compiler will choke with a $.
 IDL_ExecuteStr("PLOT, idl_XP, idl_Y1, \
  XTITLE = 'INCIDENT ENERGY (EV)', \
  YTITLE = 'T TO V EFFICIENCY', \
  PSYM = 0, \
  XSTYLE = 1, XRANGE = [0,8], \
  YSTYLE = 1, YRANGE = [0,1], \
  XTICKS = 4, XMINOR = 10, \
  YTICKS = 1, YMINOR = 10, \
  CHARSIZE = 1");

The following commands make the overlay plots.  Alternatively, you could have declared an array with all of these plotting commands in them, then executed them with IDL_Execute().
 IDL_ExecuteStr("OPLOT, idl_XP, idl_Y2");
 IDL_ExecuteStr("OPLOT, idl_XP, idl_Y3");
 IDL_ExecuteStr("OPLOT, idl_XP, idl_Y4");
 IDL_ExecuteStr("OPLOT, idl_XP, idl_Y5");

Here the widget is called and executed.
 IDL_Execute(sizeof(cmds2)/sizeof(char *), cmds2);

Here I've simply set i_ret to the return of the last Y array that was passed to IDL.  Be sure and use the asterisk with i_ret, but not the IDL pointer.
 *i_ret = (long) vY5;

 }
 

/**************************************************************************/
This is another one of those pieces of code you copy into your files without worrying about why.  Be sure to keep it down here at the bottom of your C wrapper.
void
idl_cleanup_ (int *just_cleanup, long *i_ret)
 {
 *i_ret = IDL_Cleanup (*just_cleanup);
 }