Using VisAD Data Model in Everyday Python (Jython) Programming

April, 2001
updated May 22, 2001


Fundamental to VisAD is the Data Model.  The Data Model is a collection of VisAD interfaces and classes that allow the programmer to describe the data to be used in their program: the associated units and error estimates, how is it organized, related to other data, and so on.  Some Data Model  class objects contain no "real" data, but may contain meta data (data about the data, such as the units) or show how different data sets should be grouped for making a display.

While there are many extensions to this collection of classes and interfaces, we shall only introduce the fundamental Data object types and illustrate the parallels with quantities you may already be familiar with.  Each will have a source code example to allow you to experiment on your own.


In VisAD the values of a Scalar may be either Real which is used for numeric quantities, or Text for strings of characters. In this discussion, we deal only with Real objects.

Real (actual) numbers

No doubt about it, if you've written Java code, you are already familiar with doing something like:
double a,b,c;
a = 10.;
b = 255.;
c = a + b;
System.out.println("sum = "+c);
If you are new to Java, but come from a Fortran background, then this might be more familiar:
A = 10
B = 255
C = A + B
WRITE(*,*)'sum = ',C
Well, if you want to use the VisAD Data model, here's what you'd say (the complete program is provided):
from visad import Real
a = Real(10)
b = Real(255)
c = a + b
print "sum=",c
When you run this example, you get:
sum = 265.0
By doing this, of course, you are not going to be convinced that there is any advantage to using the VisAD Data model.  So, let's explore another form of constructor for visad.Real.

Estimating Errors

The form of constructor for Real
Real(value, error)
allows us to provide an error estimate for the value.  This estimate can then be propagated through mathematical operations to provide an error estimate.
from visad import Real
a = Real(10.,1.)
b = Real(255.,10.)
c = a + b
print "sum=",c
When you run this example, you still get:
sum = 265.0
The VisAD default for most math operations, however, is to not propagate errors, so to make use of this, we must explicitly indicate how we want error estimates to propagate. This is done by using an alternate signature of the add method (and all other math operators):
from visad import Real, Data
a = Real(10,1)
b = Real(255,10)
print "sum =",c
print "error of sum is=",c.getError().getErrorValue()
When you run this example, you get:
sum = 265.0
error of sum is=10.04987562112089
The constants supplied to the add method are the type of interpolation and the type of error propagation. In this simple case, the type of interpolation is not really relevant, but as you will see later, VisAD Data types may contain finite approximations to continuous functions and when these are combined mathematically, may need to be resampled in order to match domains.

Using Units

Another powerful feature of the VisAD Data model is that it may handle units. If your quantities are physical and have associated Units, then you might prefer to create a VisAD MathType that explicitly defines the metadata characteristics of your quantities. A MathType is used to define the kind of mathematical object that the Data object approximates. Every Data object in VisAD must have a MathType. In the previous examples, a default MathType with the name "Generic" was implicitly used for our Real objects.

In the simplest form for dealing with Units, the constructor for a MathType which defines Real values is:

getRealType(String name, Unit u)
which allows you to assign a unique name to this MathType, as well as a Unit.

To make use of this, we modify the program to read as follows:

from visad import Real, SI
from visad.python.JPythonMethods import *

k = getRealType("kelvin", SI.kelvin)
t2 = Real(k, 273)
t1 = Real(k, 255)

sum = t1 + t2
print "sum =",sum,sum.getUnit()
When you run this example, you get:
sum = 528.0 K
In this example, we were able to use an SI Unit (ampere, candela, kelvin, kilogram, meter, second, mole, radian, steradian). Note that we constructed two variables with the same MathType, that is the same name and Unit. The only thing that is different is the numeric value.

If you are using some other unit, VisAD provides mechanisms for making up Units for those. As an example, you can use the makeUnit() method from the visad.python.JPythonMethods package to create a VisAD Unit from a String name.

from visad import Real
from visad.python.JPythonMethods import *

degC = makeUnit("degC");
tc = getRealType("tempsC",degC);
t2 = Real(tc,10.);

k = getRealType("kelvin",SI.kelvin);
t1 = Real(k,255.);

sum = t1 + t2

print "sum =",sum,sum.getUnit()
When you run this example, you get:
sum = 538.15 K
Observe that although we defined the value of variable tc to be in degree Celsius, when we added the two variables together, the value of y was automatically converted to degrees Kelvin. As long as the units are transformable, VisAD handles this. If you attempt to combine quantities with incompatible units, an Exception is thrown.

If you'd like to get the value listing in Celsius, then change the println to read:

print "sum = ",sum.getValue(degC)
When doing arithmetic on Real objects, you may need at some point to use a constant value for something. As with all VisAD Data objects, in order to perform these operations, the Units must all match. When you use the simplest form of constructor for Real to define a numeric value, VisAD sets its Unit to a default value which can then be used to do arithmetic with any other Real. To illustrate, let's modify the previous example to compute the average of the two temperature values:
from visad import Real, Unit, RealType
from visad.python.JPythonMethods import *

degC = makeUnit("degC")
tc = getRealType("tempsC", degC)
t2 = Real(tc, 10)

rtk = getRealType("kelvin", SI.kelvin)
t1 = Real(rtk, 255)

average = (t1 + t2) / 2
print "average =",average,average.getUnit()
When you run this program, you get:
average = 269.075 K


A VisAD Tuple object contains a collection of Data objects whose number, sequence and type are defined by the MathType of the Tuple. There is also a subclass of Tuple named RealTuple which is a collection of only Real objects, but again whose number and sequence are fixed by the RealTupleType associated with the RealTuple. This object is like a fixed-length vector, like (x, y, z), and is similar to the Python Tuple, except that in VisAD, metadata (such as Units, error estimates, etc) are also included. Contrast this with a Java array which is a set of identical objects of that particular type. You will, in fact, find a constructor for VisAD's RealTuple where the data values are passed in as an array of Reals.

Making the MathTypes

Let's suppose we want to have a single object that keeps a collection of values of temperature, wind speed, and time together. The approach is to first define the MathTypes for each of these quantities. For example:
from visad import Real, Unit, RealType, RealTupleType
from visad.python.JPythonMethods import *

degC = makeUnit("degC")
temperature = getRealType("temperature", degC)

kts = makeUnit("kts")
speed = getRealType("speed", kts)

sec = makeUnit("seconds")
time = getRealType("time", SI.second)

mydata = RealTupleType(time, speed, temperature)

Using numbers

Now that we've defined the MathTypes, let's see how this works with some "real" data. Add the following lines of code to the above fragment:

obsTemp = 32
obsSpeed = 15
obsTime = 4096

values = (obsTime, obsSpeed, obsTemp)

obs = RealTuple(mydata, values)
print "obs =",obs
When you run all this now, you get:
obs = (4096.0, 15.0, 32.0)

Arithmetic with Tuples

Let us now suppose we have a second set of observed data and add this code onto the end of our example:
obsTemp2 = -10
obsSpeed2 = 7
obsTime2 = 1234

values2 = (obsTime2, obsSpeed2, obsTemp2)

obs2 = RealTuple(mydata, values2)
print "obs2 =",obs2
When you run this addition, you get:
obs = (4096.0, 15.0, 32.0)
obs2 = (1234.0, 7.0, -10.0)
Our main purpose is to average all the values together. Again we need to define a Real for our constant, and then do just what we did previously:
avg = (obs + obs2) / 2
print "avg =",avg
Finally when you run the complete example, you get:
obs = (4096.0, 15.0, 32.0)
obs2 = (1234.0, 7.0, -10.0)
avg = (2665.0, 11.0, 284.15)
Note that the temperature was converted to the base unit of kelvin.

Most important -- as you can see the arithmetic capability of VisAD applies to all types of Data objects in the same manner.

Although we have used a VisAD RealTuple, it is of course just a specific kind of a Tuple that only contains Reals. Tuples can be used to collect together all types of VisAD Data objects...including Sets and Functions.


As shown in the next section, Set objects are most often used to define the finite sampling of the domain of Field Objects (which approximates a function by interpolating its values at a finite subset of its domain). In VisAD, the Set class has many sub-classes for different ways of defining finite subsets of the Set's domain.  Near the top of the list is the DoubleSet which includes all the double precision values that can be represented in the computer's 64 bit is a finite, but very large Set. Farther down the hierarchy, for example, is the Linear1DSet which consists of n values of a simple arithmetic progression between two specified values.

VisAD comes with lots of implementations of the Set interface, in order to represent lots of common topologies. In this introduction, however, we'll deal only with one form -- the Linear1DSet.

The Set object also defines the CoordinateSystem of the Field's domain and the Units of the domain's RealType components. The following section will deal more explicitly with Fields.

Making a Set

Working with Sets is deceptively easy. For example, a program that contains these three lines of code:
from visad import Linear1DSet

s = Linear1DSet(-33, 33, 5)
print "set s=",s
will produce this output when run:
set s = Linear1DSet: Length = 5 Range = -33.0 to 33.0
The Linear1DSet defined in this case has associated MathType of "Generic". There is an alternate form of the constructor for a Linear1DSet that allows you to define the MathType for this set of numbers, as well.

Set methods

There are several usage methods available for working with Sets. For example, you may need an enumeration of the values of our little Linear1DSet. If you add the code:
sam = s.getSamples()
for i in range( len(sam[0]) ):
  print "i =",i,"  sample =",sam[0][i]
the program would produce the following output:
set s = Linear1DSet: Length = 5 Range = -33.0 to 33.0

i = 0   sample = -33.0
i = 1   sample = -16.5
i = 2   sample = 0.0
i = 3   sample = 16.5
i = 4   sample = 33.0
Note that the first dimension of variable s is 1; the standard notation in VisAD for samples is
[number of components] [number of samples]

Other methods worth checking out include indexToDouble() which returns an array of doubles given an array of indices. There is also an inverse method, doubleToIndex(). Refer to the VisAD Javadoc for more details.


A VisAD Function Data object represents a function from a domain to values of some specific type (a range). Field is the subclass of Function for functions represented by finite sets of samples of function values (for example, a satellite image samples a continuous radiance function at a finite set of pixel locations). This object is really the heart of VisAD's Data model when working with many forms of geophysical data, which tend to be samples of continuous fields.

In order to use Function objects, it is necessary to define the sampling using a Set of some kind for the domain and to supply appropriate samples for the corresponding range values. Let's make a small example. In this case, I want to define a Function that can convert temperatures from degrees Fahrenheit to Kelvin.

1. The MathTypes

First, we must define the appropriate MathTypes:

from visad import FunctionType, Linear1DSet, FlatField, Real
from visad.python.JPythonMethods import *

domain = getRealType("temp_F")
range = getRealType("temp_Kelvin")
convertTemp = FunctionType(domain, range)

Here, we have defined the RealType for our domain to represent degrees F, and for the range for degrees K. The FunctionType defines the mapping from the domain to the range. (Note that we have not included Units in this defining these two RealTypes, because we are going to do the conversions ourselves; otherwise, the visad.units package would have be able to do the conversion for us.)

2. The samples

Now we need to define and set the values for the samples of the Function. Let's say that we know the values of temperature in both units at -40F and 212F:
domain_set = Linear1DSet(-40, 212, 2)
We use a 1D Set because we are only defining a scalar at each point in the range (rather than a vector).

3. The FlatField object

Finally, we need to construct the VisAD Data object that will provide for the desired finite sampling. The FlatField, which is a subclass of Field designed for use with the Java primitive type double, provides just such a representation and functionality. So we can say:
convertData = FlatField(convertTemp, domain_set)
Which constructs a FlatField which is defined by a FunctionType defined over the values of the domain_set.

First, create an array that contains the numeric values of the range samples at the two points in the domain_set:

values = ( (233.15, 373.15), ) # deg K values for -40F and 212F
Then put the range samples into the FlatField using:

4. Evaluating Functions

Okay, so now let's test our Function by providing a domain value (that is, a temperature in degrees F) that we want to convert:
e = Real(14.0)
v = convertData.evaluate(e)
print "value for 14.0F =",v
vf = (v.getValue() - 273.15) * 9 / 5. + 32
print "     or (doing some math) = ",vf
When you run this whole example, you get:
value for 14.0F = 233.14999389648438
        or (doing the math) = 13.999989013671915
Please note that utility functions in the JPythonMethods class would allow you to code this example as simply:
domain_set = makeDomain("temp_F" -40, 212, 2)
convertData = field(domain_set, "temp_Kelvin", (233.15, 373.15))
v = evaluate(convertData, 14.0)

print "value for 14F =",v
vf = (v.getValue() - 273.15) * 9 / 5. + 32
print "     or (doing some math) = ",vf

Sampling modes

By default, the evaluate() method in a Function uses the sampling mode called Data.WEIGHTED_AVERAGE. You may also sample using Data.NEAREST_NEIGHBOR, which in this case would give a different result, since the domain value of 14.0 would be closest to the domain sample at -40.0, which then means that 233.15 is the associated range value.

If we change the evalute() call to read:

      v = convertData.evaluate(e, Data.NEAREST_NEIGHBOR, Data.NO_ERRORS);
then results in this output when you run now:
value for 14.0F = 263.1499938964844
         or (doing the math) = -40.00001098632808

Using methods from visad.python.JPythonMethods

We are providing static, short-cut methods to support using VisAD from Jython (Python in Java). Here is the same program just written, but converted to using JPythonMethods functions:
from visad.python.JPythonMethods import *
from visad import Real

domain = makeDomain(-40, 212, 2)
values =  (233.15, 373.15) # deg K values for -40F and 212F

convertData = field(domain, "degF2K", values)

e = Real(14)
v = convertData.evaluate(e)
print "value for 14.0F =",v

vf = (v.getValue() - 273.15) * 9 / 5. + 32
print "     or (doing some math) = ",vf

Parting points...