3 Using the Lattice Data Type
This chapter defines the lattice data type and how it is used in the IRIS Explorer environment. It describes the forms that lattices can take in one, two, and three dimensions and gives examples of how to create them.
The API (Application Programming Interface) routines are listed and examples of user function code for writing modules that manipulate lattices are included.
3.1 Understanding Lattices
The IRIS Explorer lattice data type, cxLattice, contains all the information IRIS Explorer requires for creating arrays. An array is a regular, structured matrix of points, which can be 1D or multidimensional. Because it is extremely versatile, you can use the cxLattice data structure to represent a wide variety of array data. Thus, some modules may accept a cxLattice with any number of dimensions, containing any number of data values, of any type. Other modules may, for example, accept only 1D lattices in byte format. In general, the form of the array data you plan to feed into a module will determine how narrowly you define the lattice specifications on the input port in the Module Builder.
The IRIS Explorer lattice data structure has two parts. One holds data values and the other holds node coordinates. A node is a point in a lattice defined by a unique coordinate or set of coordinates in Cartesian space, usually indicating the position of the data value (or values). The data and coordinate arrays are optional, however. You can create a lattice with an empty data structure, and node coordinates only, or one with data values and no coordinate values.
The two array structures in cxLattice are cxData and cxCoord. The data and coordinate arrays are defined in separate variables because:

The Cartesian space in which the lattice exists may be irregular, for example, in a finite differencebased simulation program. Such programs may require complicated physical mappings even though the data is straightforward.

Two different lattices, such as an input and an output lattice, can share the same data or coordinate array, thereby saving memory. For example, the lattices may have different data but use the same coordinate values.
Figure 31 illustrates the principle of sharing array values. The lattices on the module input port and on the module output port use the same coordinate values, which are passed directly from input to output, but the data values change from input to output as they are processed by the user function.
3.1.1 The Lattice Data Type
The cxLattice data type is one of the root data types, which means it can be placed on module ports and will pass data into and out of modules. It has three main parts, or subsidiary data structures:

the dimension variables, nDim and dims

the subtype, cxData, which defines the number of data variables, the primitive type, and the data values

the subtype, cxCoord, which defines the number of coordinates, their Cartesian mapping, and their values
This is the type definition for cxLattice:
shared root typedef struct { long *nDim "Num Dimensions"; long* *dims[nDim] "Dimensions Array"; cxData(nDim,dims) *data "Data Structure"; cxCoord cDim, dims) *coord "Coord Structure"; } cxLattice;
The type definitions for cxData and cxCoord are given below. Figure 32 shows a schematic representation of cxLattice.
3.1.1.1 The Dimension Variables
The dimension variables in the cxLattice data type are:
 nDim

The number of dimensions. It determines the dimensionality of the array that holds data, independent of its Cartesian mapping and the number of data variables. For curvilinear lattices it also determines the dimensionality of the array which holds the node coordinates.
 dims

A vector of integer values specifying the number of nodes in each of the nDim dimensions.
The nDim variable indicates the number of dimensions of the lattice. The dims array specifies the number of nodes in each dimension – that is, the number of data values in each dimension.
From Figure 32, it can be seen that nDim and dims are stored with both cxData and cxCoord; the dimensions of these arrays are set by the values of nDim and dims. Thus, these values must be consistent throughout the cxLattice structure; otherwise, you may get some bizarre results. For example, if you define nDim as 3 and dims as [10,6,6] in cxData, you must also define them as 3 and [10,6,6] in cxCoord.
3.1.1.2 The cxData Structure
Data values go into the cxData type, which contains the value or values stored at each node of the lattice. Its elements include:

the dimension variables, nDim and dims

the number of data variables per node, nDataVar

the type of primitive variable that can be used for data values, primType. There are five options (see Table 31).

the array of data values
This is the data type definition for cxData:
shared typedef struct { /* IRIS Explorer Lattice's Data array */ long nDim; long dims[nDim]; long nDataVar "Num Data Variables"; cxPrimType primType "Primitive Data Type"; switch (primType) { case cx_prim_byte: char values[nDataVar, dims] "Data Array"; case cx_prim_short: short values[nDataVar, dims] "Data Array"; case cx_prim_long: long values[nDataVar, dims] "Data Array"; case cx_prim_float: float values[nDataVar, dims] "Data Array"; case cx_prim_double: double values[nDataVar, dims] "Data Array"; } d; } cxData(nDim, dims); typedef enum { cx_prim_byte, cx_prim_short, cx_prim_long, cx_prim_float, cx_prim_double } cxPrimType;
3.1.1.3 The cxCoord Structure
The Cartesian coordinate values that define the position of the lattice nodes go into the cxCoord data type. These values map the lattice data to Cartesian space. Its elements include:

the dimension variables, nDim and dims

the type of physical mapping, coordType. There are three options: uniform, perimeter, and curvilinear mapping, described later in this chapter.

The number of coordinates per node, nCoordVar

the array of coordinate values
This is the data type definition for cxCoord:
shared typedef struct { /* IRIS Explorer Lattice's Coordinate array */ long nDim; long dims[nDim]; cxCoordType coordType "Coord Type"; switch (coordType) { case cx_coord_uniform: float bBox[2, nDim] "Coordinates Array"; case cx_coord_perimeter: long sumCoord "Perim Coord Array Length"; float perimCoord[sumCoord] "Coordinates Array"; case cx_coord_curvilinear: long nCoordVar "Num Coord Dimensions"; float values[nCoordVar, dims] "Coordinates Array"; } c; } cxCoord(nDim, dims);
3.1.1.4 How Variables Interact
Figure 33 shows the relationship of some lattice variables. This example depicts a 2D lattice with seven nodes in each dimension and three data variables per node. Thus, nDim=2, dims[0]=7 (x direction), dims[1]=7 (y direction), and nDataVar=3.
3.2 Manipulating Lattices
The lattice and coordinate data values are stored separately and in different formats. Figure 34 shows how data from arrays in C and Fortran formats is stored in computer memory, and illustrates the difference between rowmajor (C) and columnmajor format (Fortran). The formats used by each data and coordinate lattice type in cxLattice format are described below.
3.2.1 Storing Data Values
Lattice data is located at the coordinate nodes. In a 1D array, or vector, each node in the array has two neighbors (except the end points, which each have only one), as shown in Figure 35. In two dimensions, each internal node has four neighbors. A node internal to a 3D array has six neighbors. An internal node in an nD array has 2*n neighbors. This regular structure is the computational space of the lattice. This topology is completely determined by the value of nDim and the dims vector, and is to be distinguished from the physical space of the lattice, which is determined by its coordinates part.
Lattice data is stored in the Fortran convention, using a columnmajor layout, in which the i direction of the array varies the fastest (see Figure 36). For all lattice types, the i direction corresponds to the x direction. Similarly, j corresponds to y, and k to z.
If a lattice has several data values at each node (that is, if nDataVar>1), then the data is stored in interleaved format. In a color image, for example, the interleaving of RGB data looks like this:
R(node 1) G(node 1) B(node 1) R(node 2) G(node 2) B(node 2) R(node 3) G(node 3) B(node 3)...
To locate a particular node within an array, you use array indexing. For example, the node located at (i,j,k) is:
Array[k][j][i] in C Array(i,j,k) in Fortran
You can compute the total number of data values in a lattice by calling the API function cxDimsProd. Since this number is the product of the number of nodes in the lattice and nDataVar, the number of data values at each node, you can use this result to calculate the number of nodes in the lattice. See the IRIS Explorer Reference Pages for details on the API routines.
3.2.2 Defining Primitive Values
The primitive data type is defined in terms of C types in the lattice data type. If you are programming in Fortran, choose the C primitive that is equivalent to the Fortran variable that your subroutine expects. Table 31 lists the equivalences between the two.
3.2.3 Storing Coordinate Values
Coordinates are always stored in singleprecision floating point (float) format. The lattice data type allows for three types of coordinate mapping to physical space: uniform, perimeter, and curvilinear. The interleaving of the coordinate storage varies from type to type. Each type is described in detail below.
3.2.3.1 Uniform Lattices
A lattice with uniform coordinates has a uniform cell size throughout (see Figure 37). Most generated data is in this format.
The data structure for a uniform lattice is:
struct { float *bBox; /* An array of length [2, ndim]*/ } cx_coord_uniform;
The coordinate values are stored in rowmajor format, in the C convention:
IRIS Explorer uses a bounding box to set the size and aspect ratio of uniform lattice coordinates. Bounding boxes are dimensioned as a constant and a scalar in the cxCoord data type. That is, dims=[2,nDim]. IRIS Explorer saves only the bounding box coordinates for a uniform lattice. It can construct the complete lattice coordinate set from these values.
For example, this is how PrintLat prints out the coordinate structure for the uniform lattice shown in Figure 37. It shows the values of nDim (in this case 2), dims, coordType, and the bounding box coordinates. Comparing this output with the variables of Figure 37, it can be seen that xmin=0.0, ymin=0.0, xmax=dims[0]1.0 and ymax=dims[1]1.0. In this example, the spacing between nodes in the x and y directions are both equal to 1.0. This can be altered by changing the coordinates of the bounding box (see Section 3.2.3.2).
coord nDim 2 dims 9 5 /* Number of nodes in the x and y dimensions /* coordType cx_coord_uniform bBox 0.000000e+00 8.0000000e+00 /* The values of xmin and xmax /* 0.000000e+00 4.0000000e+00 /* The values of ymin and ymax /*
Figure 38 shows an example of a 3D uniform lattice.
3.2.3.2 Changing the Aspect Ratio
A 2D image is an example of a uniform lattice; all the pixels in the image have the same size and aspect ratio. You can change the aspect ratio of a lattice by manipulating the bounding box coordinates.
For example, the lattice in Figure 37 has nine nodes in the x direction and five nodes in the y direction. The default mapping provides a 1:1 aspect ratio. Since the bounding box for this lattice is [0.0,8.0] by [0.0,4.0], the lattice is mapped into a 9 by 5 grid to be displayed. However, if the bounding box coordinates were [1.0,1.0] by [1.0,1.0], the lattice would occupy a 2 by 2 space when mapped to the screen, with a pixel aspect ratio of 2:1. Uniform lattices can have this nonuniform aspect.
3.2.3.3 Perimeter Lattices
A perimeter lattice has a list of coordinate values sufficient to specify an irregularly spaced rectangular structure.
The data structure for a perimeter lattice is:
struct { long sumCoord; /* Total number of nodes in all dims */ float *perimCoord; /* Ordered list of all coordinates */ } cx_coord_perimeter;
Here, *perimCoord is an array of length sumCoord containing an ordered list of all the coordinates for each dimension. You can compute sumCoord by calling the API subroutine cxDimsSum. See the IRIS Explorer Reference Pages for details on the API subroutines.
Figure 39 shows the data set for a 2D perimeter lattice. The x and y perimeter vectors contain coordinate values that specify the layout of the lattice. It contains eight nodes in the x dimension and six nodes in the y dimension.
Coordinates for perimeter lattices are stored in rowmajor format in the C convention (see Figure 310).
Figure 311 shows an example of a 3D perimeter lattice. The dims values are the same for each of the perimeter vectors, because there are the same number of nodes in each dimension.
3.2.3.4 Curvilinear Lattices
Curvilinear lattices are used to store datasets where the data values at a node need to be associated explicitly with the Cartesian coordinates of the node. Examples of these include a collection of atoms in 3D space, points on the surface of a sphere and computational fluid dynamics data calculated in a bodyfitted coordinate system.
The data structure for the coordinates part of a curvilinear lattice is:
struct { long nCoordVar; /* Number of physical dimensions */ float *values; /* Array containing node coordinates */ } cx_coord_curvilinear;
You can use cxDimsProd to compute the total number of coordinate values as the product of the number of nodes in the lattice (i.e., the dims vector) and nCoordVar.
Coordinate values for curvilinear lattices are stored interlaced at the node level in the Fortran convention, with the i dimension varying the fastest. This is the same storage method used to store lattice data (see Figure 36) and is the reverse of the method used for storing uniform and perimeter coordinates.
The number of computational dimensions for the lattice is defined by the variable nDim (see Figure 35). For uniform and perimeter lattices, this number is also equal to the number of physical dimensions for the lattice, but for curvilinear lattices, the number of physical dimensions is defined by the variable nCoordVar, the number of coordinate variables for each node. In principle, this can have any value, although since each node in an nDimlattice usually requires at least this number of coordinates to locate it in physical space, useful lattices will have nDim<=nCoordVar. Some examples of curvilinear lattices with this property are shown in Figure 313.
This figure shows the wide variety of datasets that can be stored in a curvilinear lattice; from collections of points (nDim=1) in 1D, 2D or 3D space (nCoordVar=1, 2 or 3) through areas (nDim=2) in 2D or 3D space (nCoordVar=2 or 3) to volumes (nDim=3) in 3D space (nCoordVar=3). In addition it should be noted that, because of its greater generality, a curvilinear lattice can always store datasets that are stored in a perimeter or uniform lattice (and a perimeter lattice can always store datasets from a uniform lattice, for the same reason). However, this would be an inefficient use of storage space, since much of the coordinate information would be redundant under these circumstances, and it is always best to use the simplest type of lattice to store a given set of data.
3.3 Limiting Lattice Values
When you build a module, you specify the range of lattice types the module can accept on its input port or produce on its output port. You can define in general terms the lattice constraints that encompass a large range of values for a given element, or you can be very specific. The range you choose will depend on the kind of data you want the module to handle.
The Lattice Constraints window in Figure 314 shows the settings for a more narrowly defined lattice, such as a colormap (see Section 3.4). The port will accept a 1D lattice with four data variables. The primitive data type must be a float, and the lattice coordinate type must be uniform. The number of coordinate dimensions is not limited.
See Section 2.4.2.1 for more information on using this window.
3.4 Lattice Examples
These examples show how to create some simple, commonly used lattices. They include code for a colormap and a 2D image.
3.4.1 A Colormap Example
A colormap is a 1D lattice with four variables per node (RGBA). Nodes are spaced uniformly. The data is usually in floating point format. The elements are:
 nDim

usually 1, although colormaps can be a function of more than one variable^{[3]}.
 dims

256
 nDataVar

4 (red, green, blue, alpha)
 primType

float
 coordType

uniform
See Section 3.7 for an example of a user function for a module that accepts a colormap.
3.4.2 A 2D Image Example
An image is a 2D lattice with one variable (greyscale), three variables (RGB), or four variables (RGBA) per node. The data is usually in byte format and the coordinate spacing between nodes is usually uniform. IRIS Explorer imageprocessing modules accept images of any size. The elements are:
 nDim

2
 dims

any
 nDataVar

4 (red, green, blue, opacity)
 primType

byte
 coordType

usually uniform, although certain Landsat images and ‘fisheye’ photographic images may be distorted and therefore use curvilinear coordinates
See Section 3.7 for examples of user functions for modules that work with both 2D and 3D lattices.
3.4.3 Preparing Your Data for Lattices
To prepare your data for input into an IRIS Explorer lattice data type, follow these points:

All information in the data portion of a lattice must be of only one of the primitive types. You cannot have a lattice that mixes primitive types. For example, a data file containing a mixture of bytes and floats must either be read into two lattices (one containing the bytes, and the other containing the floats) or the data must be converted so that it is all of one type.

If you are coding in Fortran, remember to use zerobased indices instead of onebased indices.

If you have Fortran scalars, convert your primitive type into the equivalent IRIS Explorer primitive type (see Section 3.2.2).

The coordinate data for uniform and perimeter lattices is stored differently from curvilinear lattices. Check in the appropriate part of Section 3.2.3 to make sure you have your coordinates correctly arranged.
The easiest way to import your data into an IRIS Explorer map as a lattice is to make use of the ‘plain’ ASCII format. Files written in this format can be immediately read by the ReadLat module. The ReadLat help page contains details of the plain ASCII lattice format. Alternatively, see the file $EXPLORERHOME/data/lattice/README.PlainFormat. Example data files in the plain format may be found in the same directory.
3.5 The Data Type Declaration
The cxLattice data type, though defined in the IRIS Explorer typing language, can be considered as a C structure. Fortran users need to set pointers to the data type structures when they use them. The type declaration resides in the header file $EXPLORERHOME/include/cx/cxLattice.h.
This is the cxLattice data type declaration:
#include <cx/Typedefs.h> typedef enum { cx_coord_uniform, cx_coord_perimeter, cx_coord_curvilinear } cxCoordType; typedef struct cxCoord { long nDim; long *dims; cxCoordType coordType; union { struct { float *bBox; } cx_coord_uniform; struct { long sumCoord; float *perimCoord; } cx_coord_perimeter; struct { long nCoordVar; float *values; } cx_coord_curvilinear; } c; } cxCoord; typedef struct cxData { long nDim; long *dims; long nDataVar; cxPrimType primType; union { struct { unsigned char *values; } cx_prim_byte; struct { short *values; } cx_prim_short; struct { long *values; } cx_prim_long; struct { float *values; } cx_prim_float; struct { double *values; } cx_prim_double; } d; } cxData; typedef struct cxLattice { long nDim; long *dims; cxData *data; cxCoord *coord; } cxLattice;
The Fortran type enumerations reside in the file $EXPLORERHOME/include/cx/cxLattice.inc.
The three coordinate mappings are specified as follows:
integer cx_coord_uniform integer cx_coord_perimeter integer cx_coord_curvilinear parameter (cx_coord_uniform = 0 ) parameter (cx_coord_perimeter = 1 ) parameter (cx_coord_curvilinear = 2 )
Note  

All Fortran data type access routines use zerobased indexing, as in the C language (except where otherwise stated). 
3.6 The Lattice API Routines
You can use the API (Application Programming Interface) routines to manipulate data types in IRIS Explorer. The lattice subroutines are listed below and described in detail in the IRIS Explorer Reference Pages. They let you:

create a lattice with both data and coordinates, either one, or neither

calculate the size of the data set

set lattice values

copy a source lattice (or parts thereof) and allocate it to the destination

extract specific values or descriptive information from the lattice
Some routines check the validity of the data on the inputs before the module is fired. Table 32 lists the subroutines and briefly describes the purpose of each one.
Subroutine  Purpose 

cxLatNew  Creates a lattice with data and coordinates 
cxLatDataNew  Creates a lattice with data and no coordinates 
cxLatCoordNew  Creates a lattice with coordinates and no data 
cxLatRootNew  Creates a lattice with no data and no coordinates 
cxDataNew  Creates new data structure 
cxCoordNew  Creates new coordinate data structure 
cxCoordDefaultNew  Creates default index coordinates 
cxDataPrimSize  Returns the size of the primitive data type 
cxDataPrimType  Returns the primitive data type 
cxCoordType  Returns the lattice coordinate type 
cxDimsProd  Returns the total number of data points in a lattice 
cxDimsSum  Computes the sum of a dimensions vector (for perimeter lattices only) 
cxLatPtrSet  Sets lattice data and coordinate pointers 
cxLatPtrGet  Gets pointers to lattice data and coordinates 
cxLatDup  Duplicates a lattice with data or coordinates 
cxLatRootDup  Duplicates a lattice without data or coordinates 
cxDataDup  Duplicates data values 
cxCoordDup  Duplicates coordinates 
cxLatDescGet  Gets descriptive information about a lattice 
cxDataValsGet  Returns pointer to data values 
cxDataValsSet  Sets pointer to data values 
cxCoordValsGet  Returns pointer to coordinate value 
cxCoordValsSet  Sets pointer to coordinate values 
cxCoordNVarGet  Gets number of coordinate variables 
cxCoordNVarSet  Sets number of coordinate values 
3.7 Code Examples
This section presents three examples of source code using the lattice data structure. Each one is written in C and in Fortran. The source code files and modules for all the examples reside in $EXPLORERHOME/src/MWGcode.
3.7.1 A 1D Lattice
This example shows how to create a a simple colormap module. It inverts the colors of the input colormap. You may test it by connecting these modules: GenerateColorMap to this module and PrintLat; this module to another copy of PrintLat. Compare the output from the two PrintLat modules. The code resides in $EXPLORERHOME/src/MWGcode/Lattice/C/ColorMap.c and in $EXPLORERHOME/src/MWGcode/Lattice/Fortran/ColorMap.f.
#include <stdio.h> #include <cx/DataAccess.h> #include <cx/cxLattice.h> #include <cx/cxLattice.api.h> /* This user function takes a colormap input (1D lattice with 4 floats per sample) and inverts the red, green, and blue components of each lookup value. */ void cchange( cxLattice *cin, /* input colormap lattice */ cxLattice **cout) /* output colormap lattice */ { int i; /* loop variable */ int n; /* number of colormap entries */ float *a,*b; /* pointers to colormap data */ cxErrorCode err; /* create the new lattice */ *cout = cxLatDataNew( 1, /* number of dimensions */ cxLatticeDimensionsArrayGet(cin,&err), 4, /* number of data values */ cx_prim_float); /* data type */ /* use the same coordinate space as the input lattice */ cxLatPtrSet(*cout,NULL,NULL, cxLatticeCoordStructureGet(cin,&err),NULL); /* get the number of samples */ n = (int)*cxLatticeDimensionsArrayGet(cin,&err); /* get the data pointers */ cxLatPtrGet(cin,NULL,(void **)&a,NULL,NULL); cxLatPtrGet(*cout,NULL,(void **)&b,NULL,NULL); /* invert the colors of the colormap entries */ for ( i = 0; i < n; i++ ) { b[0] = 1.0  a[0]; /* red */ b[1] = 1.0  a[1]; /* green */ b[2] = 1.0  a[2]; /* blue */ b[3] = a[3]; /* alpha */ a += 4; b += 4; } }
C This user function takes a colormap input C (1D lattice with 4 real values per sample) C The red, green, and blue of each lookup value are inverted C SUBROUTINE CHANGE(CIN,COUT) C INCLUDE '/usr/explorer/include/cx/Typedefs.inc' INCLUDE '/usr/explorer/include/cx/DataAccess.inc' C .. Scalar Arguments .. #if defined(IS_64BIT) INTEGER*8 CIN, COUT, P0, COORD INTEGER*8 N(1) #else INTEGER CIN, COUT, P0, COORD INTEGER N(1) #endif C .. Local Scalars .. INTEGER I, IER, NN C .. Local Arrays .. REAL A(1), B(1) C .. External Functions .. EXTERNAL CXLATDATANEW, CXLATDESCGET, CXLATPTRGET, * CXLATPTRSET C .. Pointers to Lattice Structures .. POINTER (PA,A) POINTER (PB,B) POINTER (PN,N) C .. Executable Statements .. C C Get the number of samples C P0 = 0 IER = CXLATDESCGET(CIN,P0,PN,P0,P0,P0,P0,P0,P0) NN = N(1) C C Create the new lattice C COUT = CXLATDATANEW(1,N,4,CX_PRIM_FLOAT) C C Use the same coordinate space as the input lattice C P0 = 0 IER = CXLATPTRGET(CIN,P0,P0,COORD,P0) P0 = 0 #ifdef WIN32 IER = CXLATPTRSET(COUT,P0,P0,COORD,P0) #else IER = CXLATPTRSET(COUT,P0,%VAL(0),COORD,%VAL(0)) #endif C C Get the data pointers C Note that we could have had the data pointers passed into C the user function as arguments, which may be easier C P0 = 0 IER = CXLATPTRGET(CIN,P0,PA,P0,P0) P0 = 0 IER = CXLATPTRGET(COUT,P0,PB,P0,P0) C C Invert the red, green, and blue lookup values C DO 20 I = 1, NN B(4*I3) = 1.0  A(4*I3) B(4*I2) = 1.0  A(4*I2) B(4*I1) = 1.0  A(4*I1) B(4*I) = A(4*I) 20 CONTINUE C RETURN END
3.7.2 A 2D Lattice
This example shows how to work with a 2D image, and negates all elements of the data in the input image. Test it by connecting ReadImg to this module and DisplayImg; this module to DisplayImg (DisplayImg accepts more than one image, and you can display them in the same window; see DisplayImg for more details). Read in the image file $EXPLORERHOME/data/image/flowers2.rgb and compare the two images. The code resides in $EXPLORERHOME/src/MWGcode/Lattice/C/Image2D.c and in $EXPLORERHOME/src/MWGcode/Lattice/Fortran/Image2D.f.
#include <stdio.h> #include <cx/DataAccess.h> #include <cx/cxLattice.h> #include <cx/cxLattice.api.h> /* This user function takes an input 2D image and negates all the elements of the data array. It works for any primitive data type. */ void cimage(cxLattice *in,cxLattice **out) { int i,j,k; /* loop variables */ void *a,*b; /* data pointers */ cxErrorCode err; /* create the new lattice */ *out = cxLatDataNew( 2, /* number of dimensions */ in>dims, /* dimensions array */ in>data>nDataVar, /* number of data variables */ in>data>primType); /* data type */ /* use the same coordinate space as the input lattice */ cxLatPtrSet(*out,NULL,NULL,cxLatticeCoordStructureGet(in,&err),NULL); /* extract the data pointers */ cxLatPtrGet(in,NULL,&a,NULL,NULL); cxLatPtrGet(*out,NULL,&b,NULL,NULL); /* loop over the data elements */ for ( i = 0; i < in>dims[1]; i++ ) for ( j = 0; j < in>dims[0]; j++ ) for ( k = 0; k < in>data>nDataVar; k++ ) { /* switch on the data type */ /* This should be outside the loops for efficiency. It is shown inside here for greater clarity. */ switch ( in>data>primType ) { #define CASE(CXTYPE,TYPE) \ case CXTYPE: \ /* This is where the actual computation takes place */\ *(TYPE *)b = *(TYPE *)a; \ a = (TYPE *)a + 1; b = (TYPE *)b + 1; \ break; CASE(cx_prim_byte,unsigned char) CASE(cx_prim_short,short) CASE(cx_prim_long,long) CASE(cx_prim_float,float) CASE(cx_prim_double,double) default: break; #undef CASE } } }
C This user function takes an image input C (2D lattice of any data type) C and takes the negative of every data element. C SUBROUTINE IMAGE(IN,OUT) C INCLUDE '/usr/explorer/include/cx/Typedefs.inc' INCLUDE '/usr/explorer/include/cx/DataAccess.inc' C C .. Scalar Arguments .. #if defined(IS_64BIT) INTEGER*8 IN, OUT, P0, NDVAR,COORD INTEGER*8 DIMS(2) #else INTEGER IN, OUT, P0, NDVAR,COORD INTEGER DIMS(2) #endif C .. Local Scalars .. INTEGER I, IER, J, K, M, N, P, PTYPE C .. Local Arrays .. CHARACTER B0(1), B1(1) DOUBLE PRECISION D0(1), D1(1) REAL F0(1), F1(1) #if defined(__alpha) INTEGER*8 L0(1), L1(1) #else INTEGER L0(1), L1(1) #endif INTEGER *2 S0(1), S1(1) C .. External Functions .. EXTERNAL CXLATDATANEW, CXLATDESCGET, CXLATPTRGET, * CXLATPTRSET C .. Pointers to Lattice Structures .. #ifdef WIN32 POINTER (PDIMS,DIMS) POINTER (PB0,B0) POINTER (PB1,B1) POINTER (PS0,S0) POINTER (PS1,S1) POINTER (PL0,L0) POINTER (PL1,L1) POINTER (PF0,F0) POINTER (PF1,F1) POINTER (PD0,D0) POINTER (PD1,D1) #else POINTER (PDIMS,DIMS) POINTER (PB0,B0),(PB1,B1) POINTER (PS0,S0),(PS1,S1) POINTER (PL0,L0),(PL1,L1) POINTER (PF0,F0),(PF1,F1) POINTER (PD0,D0),(PD1,D1) #endif C .. Executable Statements .. C C Extract information from the input lattice C P0 = 0 IER = CXLATDESCGET(IN,P0,PDIMS,P0,NDVAR,PTYPE,P0,P0,P0) C C Compiler bug requires this hack C N = DIMS(2) M = DIMS(1) C C Create the new lattice C OUT = CXLATDATANEW(2,DIMS,NDVAR,PTYPE) C C Use the same coordinate space as the input lattice C P0 = 0 IER = CXLATPTRGET(IN,P0,P0,COORD,P0) P0 = 0 #ifdef WIN32 IER = CXLATPTRSET(OUT,P0,P0,COORD,P0) #else IER = CXLATPTRSET(OUT,P0,%VAL(0),COORD,%VAL(0)) #endif C C Extract data pointers based on primitive type C IF (PTYPE.EQ.CX_PRIM_BYTE) THEN P0 = 0 IER = CXLATPTRGET(IN,P0,PB0,P0,P0) P0 = 0 IER = CXLATPTRGET(OUT,P0,PB1,P0,P0) ELSE IF (PTYPE.EQ.CX_PRIM_SHORT) THEN P0 = 0 IER = CXLATPTRGET(IN,P0,PS0,P0,P0) P0 = 0 IER = CXLATPTRGET(OUT,P0,PS1,P0,P0) ELSE IF (PTYPE.EQ.CX_PRIM_LONG) THEN P0 = 0 IER = CXLATPTRGET(IN,P0,PL0,P0,P0) P0 = 0 IER = CXLATPTRGET(OUT,P0,PL1,P0,P0) ELSE IF (PTYPE.EQ.CX_PRIM_FLOAT) THEN P0 = 0 IER = CXLATPTRGET(IN,P0,PF0,P0,P0) P0 = 0 IER = CXLATPTRGET(OUT,P0,PF1,P0,P0) ELSE IF (PTYPE.EQ.CX_PRIM_DOUBLE) THEN P0 = 0 IER = CXLATPTRGET(IN,P0,PD0,P0,P0) P0 = 0 IER = CXLATPTRGET(OUT,P0,PD1,P0,P0) END IF C C Loop over all the data items C P = 1 DO 60 I = 1, N DO 40 J = 1, M DO 20 K = 1, NDVAR C C Do the computation. The data type switching should be C outside the loop for efficiency. It is shown inside C here for greater clarity. C IF (PTYPE.EQ.CX_PRIM_BYTE) THEN B1(P) = CHAR(ICHAR(B0(P))) ELSE IF (PTYPE.EQ.CX_PRIM_SHORT) THEN S1(P) = S0(P) ELSE IF (PTYPE.EQ.CX_PRIM_LONG) THEN L1(P) = L0(P) ELSE IF (PTYPE.EQ.CX_PRIM_FLOAT) THEN F1(P) = F0(P) ELSE IF (PTYPE.EQ.CX_PRIM_DOUBLE) THEN D1(P) = D0(P) END IF C C Increment the data item C P = P + 1 20 CONTINUE 40 CONTINUE 60 CONTINUE C RETURN END
3.7.3 A 3D Curvilinear Lattice
This example illustrates how to create a curvilinear lattice and rotate the coordinates. Test it by connecting GenLat to this module and PrintLat; this module to another copy of PrintLat. Compare the output from both PrintLat modules. The code resides in $EXPLORERHOME/src/MWGcode/Lattice/C/Curvi3D.c and in $EXPLORERHOME/src/MWGcode/Lattice/Fortran/Curvi3D.f.
#include <stdio.h> #include <math.h> #include <cx/DataAccess.h> #include <cx/DataExtract.h> #include <cx/cxLattice.h> #include <cx/cxLattice.api.h> void cwind(float rot,cxLattice *in,cxLattice **out) { int i,j,k; /* loop variables */ int index[3]; /* index vector for coordinate lookup */ float *c; /* pointer to output lattice coordinates */ float coord[3]; /* input coordinate vector */ float cosa,sina; /* utility variables */ /* create the new lattice */ *out = cxLatCoordNew( 3, /* number of dimensions */ in>dims, /* dimensions array */ 3, /* coordinate dimensions */ cx_coord_curvilinear); /* type of coordinates */ /* use the same data from the input lattice */ cxLatPtrSet(*out,in>data,NULL,NULL,NULL); /* get the coordinate data pointer */ cxLatPtrGet(*out,NULL,NULL,NULL,(void **)&c); /* loop over the elements */ for ( i = 0; i < in>dims[2]; i++ ) { index[2] = i; /* set z index */ for ( j = 0; j < in>dims[1]; j++ ) { index[1] = j; /* set y index */ for ( k = 0; k < in>dims[0]; k++ ) { index[0] = k; /* set x index */ /* get the original coordinates */ cxLatCoordExtract(in,index,coord); /* wind the coordinates around the zaxis */ cosa = cos(rot*coord[2]); sina = sin(rot*coord[2]); c[0] = coord[0]*cosa + coord[1]*sina; c[1] = coord[0]*sina  coord[1]*cosa; c[2] = coord[2]; /* increment pointer */ c += 3; } } } }
SUBROUTINE FWIND(ROT,IN,OUT) C INCLUDE '/usr/explorer/include/cx/cxLattice.inc' INCLUDE '/usr/explorer/include/cx/DataAccess.inc' INCLUDE '/usr/explorer/include/cx/Typedefs.inc' C C .. Scalar Arguments .. REAL ROT #if defined(IS_64BIT) INTEGER*8 IN, OUT, P0 INTEGER*8 DIMS(3) #else INTEGER IN, OUT, P0 INTEGER DIMS(3) #endif C .. Local Scalars .. REAL COSA, SINA INTEGER D, I, IER, J, K, M, NX, NY, NZ INTEGER INDEX(3) C .. Local Arrays .. REAL C(1), COORD(3) C .. External Subroutines .. EXTERNAL CXLATCOORDEXTRACT C .. External Functions .. EXTERNAL CXLATCOORDNEW, CXLATDESCGET, CXLATPTRGET, * CXLATPTRSET C .. Intrinsic Functions .. INTRINSIC COS, SIN C .. Pointers to Lattice Structures .. POINTER (PDIMS,DIMS) POINTER (PC,C) C .. Executable Statements .. C C Get the dimensions vector C P0 = 0 IER = CXLATDESCGET(IN,P0,PDIMS,P0,P0,P0,P0,P0,P0) NX = DIMS(1) NY = DIMS(2) NZ = DIMS(3) C C Create the new lattice C OUT = CXLATCOORDNEW(3,DIMS,3,CX_COORD_CURVILINEAR) C C Use the same data as the input lattice C P0 = 0 IER = CXLATPTRGET(IN,D,P0,P0,P0) P0 = 0 #ifdef WIN32 IER = CXLATPTRSET(OUT,D,P0,P0,P0) #else IER = CXLATPTRSET(OUT,D,%VAL(0),P0,%VAL(0)) #endif C C Get the output data pointer C P0 = 0 IER = CXLATPTRGET(OUT,P0,P0,P0,PC) C C Loop over the coordinates C M = 1 C C Set z index (0 based for api routine) C DO 60 I = 1, NZ INDEX(3) = I  1 C C set y index C DO 40 J = 1, NY INDEX(2) = J  1 C C Set x index C DO 20 K = 1, NX INDEX(1) = K  1 C C Get the original coordinates C CALL CXLATCOORDEXTRACT(IN,INDEX,COORD) C C Wind the coordinates around the zaxis C COSA = COS(ROT*COORD(3)) SINA = SIN(ROT*COORD(3)) C C(M) = COORD(1)*COSA + COORD(2)*SINA C(M+1) = COORD(1)*SINA  COORD(2)*COSA C(M+2) = COORD(3) C C Increment index C M = M + 3 20 CONTINUE 40 CONTINUE 60 CONTINUE C RETURN END
[3] If you use a 2D colormap, you must create your own modules to process it. IRIS Explorer currently offers tools for 1D colormaps only.