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.
If you are more comfortable with Python, then we've made a Python (Jython) version of this tutorial that shows the examples in that language.
If you are new to Java, but come from a Fortran background, then this might be more familiar:double a,b,c; a = 10.; b = 255.; c = a + b; System.out.println("sum = "+c);
Well, if you want to use the VisAD Data model, here's what you'd say (the complete program is provided):REAL A,B,C A = 10 B = 255 C = A + B WRITE(*,*)'sum = ',C
When you run this example, you get:import visad.*; public class dataex1 { public static void main (String arg[]) { try { Real a,b,c; a = new Real(10.); b = new Real(255.); c = (Real) a.add(b); System.out.println("sum = "+c); } catch (Exception e) {System.out.println(e);} } }
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.sum = 265.0
allows us to provide an error estimate for the value. This estimate can then be propagated through mathematical operations to provide an error estimate.Real(double value, double error)
When you run this example, you still get:Real a,b,c; a = new Real(10.,1.); b = new Real(255.,10.); c = (Real) a.add(b); System.out.println("sum = "+c);
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 estimate to propagate. This is done by using an alternate signature of the add method (and all other math operators):sum = 265.0
When you run this example, you get:Real a,b,c; a = new Real(10.,1.); b = new Real(255.,10.); c = (Real) a.add(b, Data.NEAREST_NEIGHBOR, Data.INDEPENDENT); System.out.println("sum = "+c); System.out.println("error of sum is="+c.getError().getErrorValue());
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.sum = 265.0 error of sum is=10.04987562112089
In the simplest form for dealing with Units, the constructor for a MathType which defines Real values is:
which allows you to assign a unique name to this MathType, a Unit for this, and define a default Set. In practice, the Set is seldom used and should just be passed as null in most cases.RealType(String name, Unit u, Set s)
To make use of this, we modify the program to read as follows:
When you run this example, you get:Real t1,t2,sum; RealType k = new RealType("kelvin",SI.kelvin,null); t2 = new Real(k,273.); t1 = new Real(k,255.); sum = (Real) t1.add(t2); System.out.println("sum = "+sum+" "+sum.getUnit());
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, Unit, and Set. The only thing that is different is the numeric value.sum = 528.0 K
If you are using some other unit, VisAD provides mechanisms for making up Units for those. As an example, you can use the Parser.parse() method from the visad.data.netcdf.units package to create a VisAD Unit from a String name.
When you run this example, you get:Real t1,t2,sum; Unit degC = visad.data.netcdf.units.Parser.parse("degC");RealType tc = new RealType("tempsC",degC,null); t2 = new Real(tc,10.); RealType k = new RealType("kelvin",SI.kelvin,null); t1 = new Real(k,255.); sum = (Real) t1.add(t2); System.out.println("sum = "+sum+" "+sum.getUnit());
Observe that although we defined the value of variable y 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.sum = 538.15 K
If you'd like to get the value listing in Celsius, then change the println to read:
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:System.out.println("sum = "+sum.getValue(degC)+" "+degC);
When you run this program, you get:Real t1,t2,average; Unit degC = visad.data.netcdf.units.Parser.parse("degC");RealType tc = new RealType("tempsC",degC,null); t2 = new Real(tc,10.); RealType k = new RealType("kelvin",SI.kelvin,null); t1 = new Real(k,255.); Real two = new Real(2.0); average = (Real) t1.add(t2).divide(two); System.out.println("average = "+average+" "+average.getUnit());
average = 269.075 K
RealType temperature, speed, time; Unit degC = visad.data.netcdf.units.Parser.parse("degC");temperature = new RealType("temperature", degC, null); Unit kts = visad.data.netcdf.units.Parser.parse("kts"); speed = new RealType("speed", kts, null); Unit sec = visad.data.netcdf.units.Parser.parse("seconds"); time = new RealType("time", sec, null); RealTupleType mydata = new RealTupleType(time, speed, temperature);
When you run all this now, you get:double obsTemp = 32.; double obsSpeed = 15.; double obsTime = 4096.; double[] values = {obsTime, obsSpeed, obsTemp}; RealTuple obs = new RealTuple(mydata, values); System.out.println("obs = "+obs);
obs = (4096.0, 15.0, 32.0)
When you run this addition, you get:double obsTemp2 = -10.; double obsSpeed2 = 7.; double obsTime2 = 1234.; double[] values2 = {obsTime2, obsSpeed2, obsTemp2}; RealTuple obs2 = new RealTuple(mydata, values2); System.out.println("obs2 = "+obs2);
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:obs = (4096.0, 15.0, 32.0) obs2 = (1234.0, 7.0, -10.0)
Finally when you run the complete example, you get:Real two = new Real(2.0); RealTuple avg = (RealTuple) obs.add(obs2).divide(two); System.out.println("avg = "+avg);
Note that the temperature was converted to the base unit of kelvin.obs = (4096.0, 15.0, 32.0) obs2 = (1234.0, 7.0, -10.0) avg = (2665.0, 11.0, 284.15)
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.
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.
will produce this output when run:Linear1DSet s = new Linear1DSet(-33., 33., 5); System.out.println("set s = "+s);
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 s = Linear1DSet: Length = 5 Range = -33.0 to 33.0
the program would produce the following output:float[][] sam = s.getSamples(); for (int i=0; i<sam[0].length; i++) { System.out.println("i = "+i+" sample = "+sam[0][i]); }
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().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
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.
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.RealType domain = new RealType("temp_F"); RealType range = new RealType("temp_Kelvin"); FunctionType convertTemp = new FunctionType(domain, range);
Set domain_set = new Linear1DSet(-40., 212., 2);
Which constructs a FlatField which is defined by a FunctionType defined over the values of the domain_set.FlatField convertData = new FlatField(convertTemp, domain_set);
First, create an array that contains the numeric values of the range samples at the two points in the domain_set:
double[][] values = new double[1][2]; values[0][0] = 233.15; // = -40F values[0][1] = 373.15; // = 212FThen put the range samples into the FlatField using:
convertData.setSamples(values);
When you run this whole example, you get:Real e = new Real(14.0); Data v = convertData.evaluate(e); System.out.println("value for 14.0F = "+v); double vf = (((Real)v).getValue() - 273.15)*9./5. + 32; System.out.println(" or (doing the math) = "+vf);
value for 14.0F = 263.1499938964844 or (doing the math) = 13.999989013671915
If we change the evalute() call to read:
then results in this output when you run the program now:double v = convertData.evaluate(e, Data.NEAREST_NEIGHBOR, Data.NO_ERRORS);
value for 14.0F = 233.14999389648438 or (doing the math) = -40.00001098632808