IBM Visualization Data Explorer Programmer's Reference

[ Bottom of Page | Previous Page | Next Page | Table of Contents | Partial Table of Contents | Index ]


10.2 A Parallel Version of the AverageCell Module

Writing a version of AverageCell for a parallel environment introduces a "problem" that does not arise with the Add module: the implementation of parallelism by dividing Fields into spatially disjoint subsets called partitions. Each partition is stored as a Field inside a Group Object. This Group is a special subclass of Group objects called a "Composite Field."

The AverageCell algorithm requires information about the neighbors of each cell. But for cells on a partition boundary, at least some of those neighbors are in another partition. DXGrow deals with this difficulty and obtains the needed information by "growing" the partition by a specified number of cells. In effect it "restores the old neighborhood." The desired operation can then be performed on the "grown" Field. DXShrink restores the partition to its pre-growth state by removing the extra cells and "cleaning up." (See 14.4 , "Growing and Shrinking Partitioned Data".)

To create a version of Data Explorer that includes the AverageCellParallel module, copy the following files to the directory where you want to work:

/usr/local/dx/samples/program_guide/Makefile_workstation
/usr/local/dx/samples/program_guide/averagecell_parallel.c
/usr/local/dx/samples/program_guide/averagecellpar.mdf
Now rename the makefile to Makefile and enter: make avgcell_par.

To run this version (from the directory to which the files were copied), enter:

dx  -mdf ./averagecellpar.mdf -exec ./dxexec

You can now run any visual program that uses the AverageCellParallel module. One such program is averagecell_parallel.net in the directory /usr/local/dx/samples/program_guide.

The example AverageCellParallel code follows:

01   #include <dx/dx.h>
02
03   static Error DoAverageCell(Object);
04
05
06
07   Error m_AverageCellParallel(Object *in, Object *out)
08   {
09     Object o=NULL;
10
11     if (!in[0]) {
12       DXSetError(ERROR_BAD_PARAMETER,"missing input");
13       goto error;
14     }
15
16     o = DXCopy(in[0], COPY_STRUCTURE);

"Grow" the Fields so that averaging can be performed across partition boundaries. Since it is not necessary to grow a Field beyond the original boundaries of the data, and since only the "data" component is affected, grow the partition by one cell. (The original components--"positions," "data," etc.--are copied into components named "original positions," "original data," and so on.)

17     if (!DXGrow(o, 1, GROW_NONE, "data", NULL))
18       goto error;

Create the task Group.

19     if (!DXCreateTaskGroup())
20       goto error;

The add tasks will be added in DoAverageCell().

21     if (!DoAverageCell(o)) {
22       DXAbortTaskGroup();
23       goto error;
24     }
25
26     if (!DXExecuteTaskGroup())
27       goto error;

Do not call DXShrink to shrink the grown Field until you have recursively removed any "original data" component(s), assuming that you want to save the newly created one(s). Otherwise the new "data" component(s) will be replaced by the (unprocessed) "original data" components(s). Now you can call DXShrink.

28     if (DXExists(o, "original data"))
29       DXRemove(o,"original data");
30     if (!DXShrink(o))
31       goto error;
32
33     out[0] = o;
34     return OK;
35    error:
36     DXDelete((Object)o);
37     return ERROR;
38   }
39
40   struct arg {
41     Field field;
42   };
43
44   static Error AddCellTask(Pointer p)
45   {
46     struct arg *arg = (struct arg *)p;
47     int i, j, numitems, shape, *neighbors_ptr, sum, neighbor;
48     int dim, counts[3];
49     char *attribute;
50     float *data_ptr, *newdata_ptr, dataaverage;
51     Array connections, data, newdata=NULL, neighbors;
52     Field field;
53
54     field = arg->field;
55

Get the connections component; determine the number of connections and their element type.

56
57     connections = (Array)DXGetComponentValue(field,"connections");
58     if (!connections) {
59       DXSetError(ERROR_MISSING_DATA,"input has no connections");
60       goto error;
61     }
62     if (!DXGetArrayInfo(connections, &numitems, NULL, NULL, NULL, NULL)) {
63       goto error;
64     }
65     if (!(attribute=
66           (char *)DXGetString((String)DXGetComponentAttribute(field,
67                                                               "connections",
68                                                               "element type")))) {
69       DXSetError(ERROR_MISSING_DATA,
70                  "missing connection element type attribute");
71       goto error;
72     }
73
74

Get the data component, and get the data dependency attribute.

75     data = (Array)DXGetComponentValue(field,"data");
76     if (!data) {
77       DXSetError(ERROR_MISSING_DATA,"input has no data");
78       goto error;
79     }
80     if (!(attribute=
81           (char *)DXGetString((String)DXGetComponentAttribute(field,
82                                                               "data",
83                                                               "dep")))) {
84       DXSetError(ERROR_MISSING_DATA,
85                  "missing data dependency attribute");
86       goto error;
87     }
88

In this example, the data must be dependent on the connections.

89     if (strcmp(attribute,"connections")) {
90       DXSetError(ERROR_INVALID_DATA,
91                  "data must be dependent on connections");
92       goto error;
93     }
94

For this example, the data must be floating-point scalar.

95     if (!DXTypeCheck(data, TYPE_FLOAT, CATEGORY_REAL, 0, NULL)) {
96       DXSetError(ERROR_INVALID_DATA, "data must be floating point scalar");
97       goto error;
98     }

Get a pointer to the data.

99     data_ptr = (float *)DXGetArrayData(data);
100

Make a new data component, allocate space in it, and get a pointer to it.

101    newdata = DXNewArray(TYPE_FLOAT,CATEGORY_REAL, 0);
102    if (!DXAddArrayData(newdata, 0, numitems, NULL))
103      goto error;
104    newdata_ptr = (float *)DXGetArrayData(newdata);
105

If the data is ungridded, use the neighbors component. If it is gridded, use a different method.

106    if (!DXQueryGridConnections(connections, &dim,  counts)) {
107

Now the program needs the neighbors of the connections. Note that neighbors can be obtained only for ungridded data: for gridded data there are more efficient ways to determine neighbors.

108      neighbors = DXNeighbors(field);
109      if (!neighbors)
110        goto error;
111      neighbors_ptr = (int *)DXGetArrayData(neighbors);
112      if (!DXGetArrayInfo(neighbors, NULL, NULL, NULL, NULL, &shape))
113        goto error;
114
115
116      for (i=0; i<numitems; i++) {
117        dataaverage = data_ptr[i];
118        sum = 1;

shape is the number of neighbors of a connection element.

119        for (j=0; j<shape; j++) {
120          neighbor = neighbors_ptr[shape*i + j];
121          if (neighbor != -1) {
122            dataaverage = dataaverage + data_ptr[neighbor];
123            sum++;
124          }
125        }
126        dataaverage = dataaverage/sum;
127        newdata_ptr[i] = dataaverage;
128      }
129    }
130
131    else {

The connections are gridded. This example handles only 2-dimensional connections (quads).

132
133      if (dim != 2) {
134        DXSetError(ERROR_INVALID_DATA,"connections must be 2-dimensional");
135        goto error;
136      }
137
138      for (i=0; i< numitems; i++) {
139        dataaverage = data_ptr[i];
140        sum = 1;

 There are as many as four (4) neighbors for every quad. 

141        if ((i % (counts[1]-1)) > 0) {
142          neighbor = i-1;
143          dataaverage = dataaverage + data_ptr[neighbor];
144          sum++;
145        }
146        if ((i % (counts[1]-1)) < (counts[1] - 2)) {
147          neighbor = i+1;
148          dataaverage = dataaverage + data_ptr[neighbor];
149          sum++;
150        }
151        neighbor = i-(counts[1]-1);
152        if (neighbor>=0 && neighbor<numitems) {
153          dataaverage = dataaverage + data_ptr[neighbor];
154          sum++;
155        }
156        neighbor = i+(counts[1]-1);
157        if (neighbor>=0 && neighbor<numitems) {
158          dataaverage = dataaverage + data_ptr[neighbor];
159          sum++;
160        }
161        dataaverage = dataaverage/sum;
162        newdata_ptr[i] = dataaverage;
163      }
164    }

Place the new data component in the Field.

165    DXSetComponentValue(field, "data", (Object)newdata);
166    newdata=NULL;

The data component has been changed (lines 162 and 165)

167    if (!DXChangedComponentValues(field,"data"))
168      goto error;
169
170
171    return OK;
172   error:
173    DXDelete((Object)newdata);
174    return ERROR;
175  }
176
177
178  static Error DoAverageCell(Object object)
179  {
180    Object subo;
181    struct arg arg;
182    int i;
183
184    switch (DXGetObjectClass(object)) {
185    case (CLASS_FIELD):
186
187      arg.field = (Field)object;
188      if (!DXAddTask(AddCellTask, &arg, sizeof(arg), 0.0))
189        goto error;
190      break;
191
192    case (CLASS_GROUP):

If object is a Group, recursively call DoAverageCell().

193      for (i=0; subo=DXGetEnumeratedMember((Group)object, i, NULL); i++) {
194        if (!DoAverageCell(subo))
195          return ERROR;
196      }
197      break;
198    }
199    return OK;
200   error:
201    return ERROR;
202  }


[ Top of Page | Previous Page | Next Page | Table of Contents | Partial Table of Contents | Index ]
[Data Explorer Documentation | QuickStart Guide | User's Guide | User's Reference | Programmer's Reference | Installation and Configuration Guide ]

[Data Explorer Home Page]


[IBM Home Page | Order | Search | Contact IBM | Legal ]