Bridges > Python Bridge > Python to IDL

# Python to IDL Bridge

The Python to IDL bridge provides a mechanism for calling IDL code from Python. The IDL module contains a set of methods that allow you to initialize and interact with the IDL interpreter. In addition, each object passed back from IDL is wrapped in an instance of the IDL class; you can then call methods on the underlying IDL object, or set and retrieve attributes on the object.

Note: See Python Bridge for installation instructions.

### Methods and Additional Information

- Import the IDL Module
- IDL.run( )
- Calling IDL Routines
- IDL Object Classes
- Case Sensitivity
- Passing Variables to and from IDL
- Retrieving IDL System Variables
- Mechanism for Passing Parameters
- Datatype Conversion
- Array Dimensions and Array Majority

## Examples

Call an IDL function or method directly from Python code, as if it was a Python object:

>>> from idlpy import IDL

>>> arr = IDL.findgen(100)/50*3.14159

>>> x = 50*IDL.sin(arr)

>>> y = 50*IDL.cos(arr)

>>> m = IDL.map(test=1)

% Compiled module: MAP.

>>> p = IDL.plot(x - 90, y, 'r-o2', overplot=1)

% Compiled module: PLOT.

>>> p = IDL.plot(x + 90, y, 'b-o2', overplot=1)

>>> m.save('map.png', resolution=96, border=0, transparent=1)

Notice that any output from IDL gets printed out in the Python console.

Execute the same commands, but called directly within the IDL interpreter:

>>> IDL.run("arr = findgen(100)/50*3.14159")

>>> IDL.run("help, arr", stdout=1)

ARR FLOAT = Array[100]

>>> IDL.run("x = 50*sin(arr)")

>>> IDL.run("y = 50*cos(arr)")

>>> IDL.run("m = map(/test)")

>>> IDL.run("p = plot(x - 90, y, 'r-o2', /overplot)")

>>> IDL.run("p = plot(x + 90, y, 'b-o2', /overplot)")

>>> IDL.run("m.save, 'map.png', resolution=96, border=0, /transparent")

# Import the IDL Module

To start using the IDL module from within Python, you can use the following command:

>>> from idlpy import IDL

At this point you now have access to all of the methods on the IDL class.

# IDL.run

The IDL.run method executes IDL commands within the IDL interpreter. Any output produced by IDL is either returned in the result or redirected to the Python console.

## Examples

Execute some arbitrary IDL code and print the result:

>>> IDL.run('arr = FINDGEN(100)')

>>> IDL.run('help, arr', stdout=1)

ARR FLOAT = Array[100]

Execute multiple IDL statements using the "&" operator:

>>> result = IDL.run('for i=0,9 do begin & print, i & endfor')

>>> print(result)

0

1

...

9

## Syntax

Result = IDL.run( command [, stdout=False] [, silent=False] )

## Return Value

By default the return value is a Python string containing all of the IDL output. If `stdout` is set to `True` then all of IDL's output is redirected to the Python console and the return value is `None`.

## Arguments

### Command

A string containing the IDL statements to execute in the IDL interpreter. You can send a multi-statement command by using a single string and separating the statements with the IDL "&" operator.

## Keywords

### stdout

Set this keyword to `True` to redirect all of IDL's output to the Python console.

### silent

Set this keyword to `True` to avoid saving the command to IDL's command history.

# Additional Information on the IDL Class

## Calling IDL Routines

Once you have imported the IDL module, you can call any function or procedure on IDL's path using the "dot" notation. Arguments and keywords can be passed to the routine. For example:

>>> from idlpy import IDL

>>> arr = IDL.randomu(None, 10000)

>>> arr = IDL.smooth(arr, 50)

>>> spec = IDL.fft_powerspectrum(arr, 0.1)

% Compiled module: FFT_POWERSPECTRUM.

>>> import matplotlib.pyplot as plt

>>> plt.semilogy(spec)

>>> plt.show()

This creates the figure shown to the right.

## IDL Object Classes

If you have retrieved an object from IDL, you can call methods or get and set properties on that object by using the "dot" notation. For example, we could construct an IDL graphics plot, modify some properties on the plot, and then save the plot to a file:

>>> from idlpy import IDL

>>> import numpy.random as ran

>>> arr = ran.rand(100)

>>> p = IDL.plot(arr, title='My Plot')

>>> p.color = 'red'

>>> p.save('myplot.pdf')

>>> p.close()

## Case Sensitivity

IDL variable and method names are case insensitive while Python names are case sensitive. When you call an IDL function or method, such as the `save` method on the IDL PLOT class, the bridge will automatically convert the name to uppercase before asking IDL for the routine.

## Passing Variables to and from IDL

Normally, you will pass Python variables into IDL function calls as input arguments or keywords, and receive the result back as an Python variable. However, if you use the `IDL.run` method, you may want to pass variables to or from IDL directly. To do this, you can use the standard Python attribute notation on the IDL class. For example:

#### Set an IDL variable

>>> from idlpy import IDL

>>> import numpy.random as ran

>>> arr = ran.rand(5)

>>> IDL.arr = arr # pass by reference

>>> IDL.run('print, arr')

0.64949746 0.42264582 0.54525948 0.54869483 0.97396999

>>> arr[0] = 99

>>> IDL.run('print, arr')

99.000000 0.42264582 0.54525948 0.54869483 0.97396999

#### Retrieve an IDL variable

>>> from idlpy import IDL

>>> IDL.run('arr2 = FINDGEN(10,20,30)')

>>> arr2 = IDL.arr2 # pass by value

>>> arr2.shape

(30, 20, 10)

Note: All variables that are passed from IDL back to Python will be given a lowercase name.

## Retrieving IDL System Variables

To retrieve an IDL system variable such as `!DPI` or `!CONST`, you can use the Python `getattr()` method:

>>> from idlpy import IDL

>>> getattr(IDL, "!dpi")

3.1415926535897931

>>> const = getattr(IDL, "!const")

>>> const.keys()

dict_keys(['ME', 'PARSEC', 'DTOR', 'RYDBERG', 'MP', 'F', 'NA', 'M_EARTH', 'RE', 'P0', 'MN', 'G', 'H', 'PI', 'N0', 'T0', 'K', 'MU0', 'R_EARTH', 'VM', 'R', 'EPS0', 'LY', 'E', 'PHI', 'RTOD', 'ALPHA', 'U', 'AU', 'EULER', 'HBAR', 'I', 'GN', 'SIGMA', 'C', 'M_SUN'])

>>> const['PARSEC']

30856775814671912.0

## Mechanism for Passing Parameters

- All scalar values are passed to and from IDL by value — in other words, a copy of the variable is made, and changes to the variable on one side will not affect the other side's value.
- String arrays, lists, tuples, and dictionaries are passed to and from by value.
- Numeric arrays are passed by reference when they are used as arguments or keywords to IDL methods.
- Numeric arrays are passed by reference when they are passed from Python `__main__` variables to IDL. Changing one of the elements in either IDL or Python will change the corresponding element on the other side.
- Numeric arrays are passed by value when they are passed from IDL to the Python `__main__` level. In other words, a copy of the entire array is made. This is because the IDL code could delete the variable, but we have no way of knowing if the variable is still in use on the Python side since Python has no simple way to notify when all references to a variable are gone.

## Datatype Conversion

When variables are passed from IDL to Python or Python to IDL (as arguments, keywords, or `__main__` variables), the following datatype conversions take place in both directions:

IDL Datatype | Python Type
--- | ---
!NULL (0) | None
Byte (1) | numpy.uint8
Int (2) | numpy.int16
Long (3) | numpy.int32
Float (4) | numpy.float32
Double (5) | numpy.float64, float
Complex (6) | numpy.complex64
String (7) | str, unicode
Structure (8) | see tables below
Double Complex (9) | numpy.complex128
Pointer (10) | not allowed
Objref (11) | see tables below
Uint (12) | numpy.uint16
Ulong (13) | numpy.uint32
Long64 (14) | numpy.int64
Ulong64 (15) | numpy.uint64

The following datatypes are used when converting from IDL to Python:

IDL Datatype | Python Type
--- | ---
Structure (8) | dict
List object | list
Hash, Dictionary, OrderedHash object | dict
IDL PYTHON object | corresponding Python object
Other IDL class | not allowed

The following datatypes are used when converting from Python to IDL:

Python Type | IDL Type
--- | ---
bool, numpy.bool_ | Byte (1), with boolean flag
int, long | Long (3) or Long64 (14), depending upon value
float | Double (5)
complex | Double Complex (9)
bytes, bytearray | String (7)
unicode | String (7)
list | List object
tuple | List object
dict | Hash object
other Python object | IDL PYTHON object that wraps the Python object
Python IDL object | corresponding IDL PYTHON object

## Array Dimensions and Array Majority

In IDL the data within a multi-dimensional array is organized as "column major," while in Python the data is organized as "row major."

#### Column Major Versus Row Major

In IDL, for a two-dimensional array the first dimension represents the "columns" of that array, while the second dimension represents the "rows". In terms of memory layout, the first dimension varies the "fastest", and has a stride equal to the size of the datatype in bytes. The second dimension varies the "slowest" and has a stride equal to the number of columns multiplied by the datatype size in bytes. For example, for a 16-bit integer array each column is separated by two bytes while each row is separated by 6 bytes:

IDL> x = INDGEN(3,2)+1 ; 3 columns by 2 rows

IDL> PRINT, x

1 2 3

4 5 6

IDL> PRINT, x[2,1] ; value in column 2, row 1

6

IDL> PRINT, BYTE(x, 0, 6*2) ; print the byte representation

1 0 2 0 3 0 4 0 5 0 6 0

In Python, for a two-dimensional array the first dimension represents the "rows" of that array, while the second dimension represents the "columns". In terms of memory layout, the first dimension varies the "slowest", and has a stride equal to the number of columns multiplied by the datatype size in bytes. The second dimension varies the "fastest" and has a stride equal to the size of the datatype in bytes. For example, for a 16-bit integer array each column is separated by two bytes while each row is separated by 6 bytes:

>>> import numpy as np

>>> x = np.array([[1, 2, 3], [4, 5, 6]], np.int16)

>>> x.shape

(2L, 3L)

>>> x.strides

(6L, 2L)

>>> x[1,2] # value in row 1, column 2

6

>>> x.tobytes()

'\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00'

Notice that in both Python and IDL the bytes are organized the same in memory — it is only the way that the dimensions are reported and the indexing order that is different.

#### Array Conversion

When Python passes an array to IDL, or when IDL passes an array back to Python, the bytes within the data array remain unchanged. Instead, the order of the dimensions is reversed. For example, if we create an array in Python that has dimensions [3, 640, 480], then in IDL the dimensions will be reported as [480, 640, 3]:

>>> import numpy as np

>>> arr = np.ndarray((640, 480, 3), np.uint8)

>>> arr.shape

(640, 480, 3)

>>> IDL.arr = arr

>>> IDL.run('help, arr')

ARR BYTE = Array[3, 480, 640]

In most cases this will be the desired behavior. However, in some cases you may need to transpose your actual data values. For example, you might read in the data from a file in Python, and then run the data through an IDL or ENVI algorithm that expects a certain order. In IDL you can use the TRANSPOSE function to re-order the values within an array before sending it to Python, while in Python you can use `numpy.transpose`.

## Version History

8.5 | Introduced

## See Also

!NULL, HASH, LIST, IDL to Python Bridge (PYTHON class)