/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include /* this routine uses "P" for DXDebug() to print how many partitions are * actually created compared to how many are requested. for more * detail on the starts and sizes of each partition, it uses "Q". */ #include #include #include #include #define MAXRANK 16 #define MAXSHAPE 128 #define MIN(a,b) (ab? a : b) /* * arg block for parallel workers: this version of partition goes * parallel as soon as we compute how many divisions along each axis * we are going to make. */ struct argblock { Field parent; /* original field to partition */ Field newpart; /* new composite field member being created */ int partid; /* which partition number this one is */ int rank; /* the dimensionality of the connections */ int divisions[MAXRANK]; /* number of parts along each axis */ int counts[MAXRANK]; /* number of positions on each axis */ }; /* * struct for computing the offsets and lengths along each dimension. */ struct cornerinfo { int thispart; /* input, this partition number */ int rank; /* input, dimensionality of connections */ int divisions[MAXRANK]; /* input, num of divisions along each dim */ int totalcounts[MAXRANK]; /* input, num of positions along each dim */ int corners[MAXRANK]; /* output, start position numbers per dim */ int length[MAXRANK]; /* output, number of positions per dim */ int offset[MAXRANK]; /* output, partition offset each dim */ int stride[MAXRANK]; /* output, number of partitions to skip */ }; static void computecorners(struct cornerinfo *c); static Error RegularDivisions(int rank, int *counts, int parts, int minsize, int *divisions); static int Partition_Worker(Pointer); static Array Array_Subset_Pos(Array a, int *startpos, int **prevcounts, int *offsets, int *thick, int numdims, int *ccounts); static Array Array_Subset_Con(Array a, int *startpos, int **prevcounts, int *offsets, int *thick, int numdims, int *ccounts); Field _dxf_has_ref_lists(Field f, int *pos, int *con); Error _dxf_make_map_template(Array a, Array *map); Error _dxf_fix_map_template(Array map, Array newmap); Array _dxf_remap_by_map(Array a, Array map); static Array remap_by_computation(Array a, int ndims, int *ccounts, int *corners, int *ncounts); static Error todouble(Type t, Pointer v1, int i1, double *v2); static Error fromdouble(Type t, double v1, Pointer v2, int i2); static int my_iszero (Type t, Pointer v1, int i1); Group _dxf_PartitionRegular(Field f, int n, int size) { int i, rank, parts; int counts[MAXRANK], divisions[MAXRANK]; int intask = 0; Array pos, conn, data; struct argblock arg; Field nf = NULL; Group g = NULL; /* do some error checks to be sure this is an ok field for this * code to work on. if it's malformed, set an error and return. * if it's just not got regular connections, then return NULL * without setting error and the irregular partitioner will try it. */ /* internal error, shouldn't happen */ if (DXGetObjectClass((Object)f) != CLASS_FIELD) DXErrorReturn(ERROR_BAD_CLASS, "PartitionRegular not called with a field"); /* it's an error if no positions component, but ok if no connections */ if (!(pos = (Array)DXGetComponentValue(f, "positions"))) DXErrorReturn(ERROR_BAD_PARAMETER, "no positions component found"); if (!(conn = (Array)DXGetComponentValue(f, "connections"))) return NULL; if (!DXQueryGridConnections(conn, &rank, counts)) return NULL; /* compute the number of divisions along each axis we should make to * generate the closest number of parts to what was requested. */ if (!RegularDivisions(rank, counts, n, size, divisions)) return NULL; /* make sure there is actually some work to do */ parts = 1; for (i=0; i 0 && minsize > points)) return OK; /* if user didn't set a min size, use a single primitive as the minimum * element size. this is fine for testing, but in real use it is * much too small to be efficient - but i don't know how to compute * what a "reasonable" number of items per partition is. you have to * look at the modules which will subsequently process this data, and * amortize the overhead of starting a new task for this partition * over the actual time it takes to do the processing - unless the * processing is incredibly expensive per point, it is hard to imagine * that one primitive is reasonable. */ if (minsize == 0) minsize = 1; /* check for integer wrap if the number of points is too large */ if (points <= 0 || points >= DXD_MAX_INT || (points / minsize) >= DXD_MAX_INT) { DXSetError(ERROR_BAD_PARAMETER, "too many positions"); return ERROR; } if ((elements / minsize) < parts) parts = elements / minsize; #if 0 DXDebug("R", "points %f, minsize %d, parts %d", points, minsize, parts); #endif if (parts == 1) return OK; if (rank == 1) divisions[0] = parts; else { int divmap[MAXRANK]; int newdiv[MAXRANK]; int newdim[MAXRANK]; int temp, curpts; for (i=0; i newdim[j]) { temp = newdim[j]; newdim[j] = newdim[i]; newdim[i] = temp; temp = divmap[j]; divmap[j] = divmap[i]; divmap[i] = temp; } for (i=0; i 1) { root = pow((double)parts/curpts, 1.0 / ((double)rank-i)); newdiv[i] = floor(root * newdim[i] + ROUNDUP + F_ERROR); } else { #if 1 root = (double)parts/curpts; newdiv[i] = floor(root * newdim[i] + F_ERROR); #endif } #if 0 DXDebug("R", "%d pts; newdiv = %g, roundup = %g, floor = %d", newdim[i]+1, root * newdim[i] + F_ERROR, ROUNDUP, newdiv[i]); #endif #if 0 DXDebug("R", "%d pts; rt %d of %g; __(%g * %d + %f) = %d", newdim[i]+1, rank-i, (double)parts/curpts, root, newdim[i], F_ERROR, newdiv[i]); #endif /* don't allow more parts along an axis than there are elements, * and don't let the number of partitions along an axis be less * than one. one means don't divide along this axis. zero * screws up other computations because we multiply by it. */ newdiv[i] = MIN(newdiv[i], newdim[i]); newdiv[i] = MAX(newdiv[i], 1); divisions[divmap[i]] = newdiv[i]; } } return OK; } /* worker task which operates in parallel to create * a single new partition from the original field. */ static int Partition_Worker(Pointer ptr) { int i, j, n; char *name; char *dep, *ref, *der; struct cornerinfo c, pc; int *prevcounts[MAXRANK]; Array a, na = NULL; int refpos, refcon; Array posmap = NULL; Array conmap = NULL; /* temp for now - should use algorithm */ struct argblock *ap = (struct argblock *)ptr; /* decide where the origin of the connections is going to be and * how long each side is. compute both origins and lengths first * as floats, and then round to ints so any unevenness is spread * throughout the volume, so when you get to the end, you don't leave * off part of the volume or create empty partitions. */ c.thispart = ap->partid; c.rank = ap->rank; for (i=0; irank; i++) { c.divisions[i] = ap->divisions[i]; c.totalcounts[i] = ap->counts[i]; } computecorners(&c); /* now compute the counts in each of the previous partitions. */ pc.rank = ap->rank; for (i=0; irank; i++) { pc.divisions[i] = ap->divisions[i]; pc.totalcounts[i] = ap->counts[i]; } for (i=0; irank; i++) { if (!(prevcounts[i] = (int *)DXAllocate(sizeof(int) * (c.offset[i]+1)))) goto error; } for (i=0; irank; i++) { for (j=0; j <= c.offset[i]; j++) { pc.thispart = j * c.stride[i]; computecorners(&pc); prevcounts[i][j] = pc.length[i] - 1; } } #if 0 if (DXQueryDebug("Q")) { DXDebug("Q", "part %d:", ap->partid); DXDebug("Q", "dim: counts, divisions, offsets, corner, length"); for (i=0; irank; i++) { DXDebug("Q", "%d: %3d, %2d, %2d, %2d, %2d", i, ap->counts[i], ap->divisions[i], c.offset[i], c.corners[i], c.length[i]); for (j=0; jparent, "connections"); if (!a) goto error; na = (Array)DXMakeGridConnectionsV(ap->rank, c.length); if (!na) goto error; if (!DXCopyAttributes((Object)na, (Object)a)) goto error; if (!DXSetMeshOffsets((MeshArray)na, c.corners)) goto error; if (!DXSetComponentValue(ap->newpart, "connections", (Object)na)) goto error; na = NULL; /* check to see if we will need to make a map for any ref components. * since we know that the connections are regular (you can't get into * this code section otherwise), we don't have to build a connections * map, but the positions may not be regular, so we have to build a map * for them. (the following code does make a map for the connections * anyway, as a quick hack to get the function into the system). */ _dxf_has_ref_lists(ap->parent, &refpos, &refcon); /* make a refpos map (and a refcon map for now until i get a * map routine which keeps the map compact) if needed. * the map is made by making a regular array of 0-N items and then * permuting them the same way we are going to permute the positions. */ if (refpos) { Array tmap = NULL; a = (Array)DXGetComponentValue(ap->parent, "positions"); if (!a) goto error; if (!_dxf_make_map_template(a, &posmap)) goto error; tmap = Array_Subset_Pos(posmap, c.corners, prevcounts, c.offset, c.length, ap->rank, ap->counts); if (!tmap) goto error; if (!_dxf_fix_map_template(posmap, tmap)) { DXDelete((Object) tmap); goto error; } DXDelete((Object) tmap); /* posmap now captures where the positions have moved to */ } if (refcon) { Array tmap = NULL; a = (Array)DXGetComponentValue(ap->parent, "connections"); if (!a) goto error; if (!_dxf_make_map_template(a, &conmap)) goto error; tmap = Array_Subset_Con(conmap, c.corners, prevcounts, c.offset, c.length, ap->rank, ap->counts); if (!tmap) goto error; if (!_dxf_fix_map_template(conmap, tmap)) { DXDelete((Object) tmap); goto error; } DXDelete((Object) tmap); /* conmap now captures where the connections have moved to */ } /* for each other component in the field, partition it accordingly. */ for (i = 0; a = (Array)DXGetEnumeratedComponentValue(ap->parent, i, &name); i++) { /* skip connections - we've already done them */ if (!strcmp(name, "connections")) continue; if (!DXGetStringAttribute((Object)a, "dep", &dep)) dep = NULL; if (!DXGetStringAttribute((Object)a, "ref", &ref)) ref = NULL; if (!DXGetStringAttribute((Object)a, "der", &der)) der = NULL; /* skip any derived components - they will be recomputed if needed. */ if (der) continue; /* if it is not dep or ref anything, just replicate it. */ if (!dep && !ref) { if (!DXSetComponentValue(ap->newpart, name, (Object)a)) goto error; continue; } #if 0 DXDebug("A", "thispart = %d, this comp = %d, corners = %d,%d,%d", c.thispart, i, c.corners[0], c.corners[1], c.corners[2]); #endif /* positions get done here with the rest of the things which * are dep on positions. */ if (dep && !strcmp(dep, "positions")) { na = Array_Subset_Pos(a, c.corners, prevcounts, c.offset, c.length, ap->rank, ap->counts); } else if (dep && !strcmp(dep, "connections")) { na = Array_Subset_Con(a, c.corners, prevcounts, c.offset, c.length, ap->rank, ap->counts); } else if (ref && !strcmp(ref, "positions")) { na = _dxf_remap_by_map(a, posmap); if (!DXGetArrayInfo(na, &n, NULL, NULL, NULL, NULL)) goto error; if (n == 0) { DXDelete((Object)na); continue; } } else if (ref && !strcmp(ref, "connections")) { na = _dxf_remap_by_map(a, conmap); if (!DXGetArrayInfo(na, &n, NULL, NULL, NULL, NULL)) goto error; if (n == 0) { DXDelete((Object)na); continue; } } else { /* not dep or ref something we recognize. just replicate it. * (this didn't use to be here - these components were skipped). */ if (!DXSetComponentValue(ap->newpart, name, (Object)a)) goto error; continue; } if (!na) goto error; if (!DXCopyAttributes((Object)na, (Object)a)) goto error; if (!DXSetComponentValue(ap->newpart, name, (Object)na)) goto error; na = NULL; } if (!DXEndField(ap->newpart)) goto error; for (i=0; inewpart) DXDelete((Object)ap->newpart); #endif for (i=0; i=0; i--) { ostride[i] = j; j *= ccounts[i]; } for (i=0; i=0; i--) { nstride[i] = j; j *= ncounts[i]; } nitems = j; na = DXNewArrayV(t, c, rank, shape); if (!na) return NULL; np = DXGetArrayData(DXAddArrayData(na, 0, nitems, NULL)); if (!np) { DXDelete((Object)na); return NULL; } /* call permute to move the data */ _dxfPermute(numdims, np, nstride, ncounts, itemsize, op, ostride); break; case CLASS_REGULARARRAY: /* need shape[0] from this */ DXGetArrayInfo(a, &nitems, &t, &c, &rank, shape); DXGetRegularArrayInfo((RegularArray)a, &i, (Pointer)origins, (Pointer)deltas); /* if dim > 1 && all deltas != 0, this can't be regular anymore */ if (numdims > 1) { for (k=0; k=0; i--) { ostride[i] = j; j *= ccounts[i] - 1; } for (i=0; i=0; i--) { nstride[i] = j; j *= ncounts[i]; } nitems = j; na = DXNewArrayV(t, c, rank, shape); if (!na) return NULL; np = DXGetArrayData(DXAddArrayData(na, 0, nitems, NULL)); if (!np) { DXDelete((Object)na); return NULL; } /* call permute to move the data */ _dxfPermute(numdims, np, nstride, ncounts, itemsize, op, ostride); break; case CLASS_REGULARARRAY: /* need shape[0] from this */ DXGetArrayInfo(a, &nitems, &t, &c, &rank, shape); DXGetRegularArrayInfo((RegularArray)a, &i, (Pointer)origins, (Pointer)deltas); /* if dim > 1 && all deltas != 0, this can't be regular anymore */ if (numdims > 1) { for (k=0; krank; i++) { c->stride[i] = n; n *= c->divisions[i]; reallength[i] = (float)(c->totalcounts[i] - 1) / c->divisions[i]; } /* now compute which position number should be picked as the start * of this partition, and how many positions long this partition is. * the offset and lengths are computed as floats, but then are rounded * down to the previous whole integer. this means some partitions * may have a length which is 1 more or less than others. * the realXXX variables below are floats, the others are integers. * also compute the partition offset - the number of partitions * between the origin and this one, along each axis. */ part = c->thispart; for (i=c->rank-1; i>=0; i--) { realcorners[i] = (part / c->stride[i]) * reallength[i]; c->corners[i] = (int)(realcorners[i]); realend = ((part / c->stride[i]) + 1) * reallength[i]; c->length[i] = (int)(realend) - c->corners[i] + 1; c->offset[i] = part / c->stride[i]; part %= c->stride[i]; } /* if the last partition extends past the end of the field, adjust * the length. */ for (i=0; irank; i++) { if (c->corners[i] + c->length[i] > c->totalcounts[i]) c->length[i] = c->totalcounts[i] - c->corners[i]; } return; } /* check to see if this field has components which don't dep * positions or connections but do ref one of them. in that * case, a map will need to be built to remap the references, * and it might as well be built while the original component is * being partitioned. */ Field _dxf_has_ref_lists(Field f, int *pos, int *con) { int i; Object comp; char *name; char *dep, *ref, *der; *pos = *con = 0; /* check attributes for each component in the field. */ for (i = 0; comp = DXGetEnumeratedComponentValue(f, i, &name); i++) { /* skip positions and connections. */ if (!strcmp(name, "positions") || !strcmp(name, "connections")) continue; if (!DXGetStringAttribute(comp, "dep", &dep)) dep = NULL; if (!DXGetStringAttribute(comp, "ref", &ref)) ref = NULL; if (!DXGetStringAttribute(comp, "der", &der)) der = NULL; /* skip derived components. */ if (der) continue; /* if it is dep positions or connections, it will be handled just * like those components, so no map will be needed. */ if (dep && (!strcmp(dep, "positions") || !strcmp(dep, "connections"))) continue; /* if it is not dep and not ref anything, it will just be copied * through unchanged, so again, no map will be needed. */ if (!dep && !ref) continue; /* these are the interesting cases. */ if (ref && !strcmp(ref, "positions")) { (*pos)++; continue; } if (ref && !strcmp(ref, "connections")) { (*con)++; continue; } /* what here? either dep on something we don't recognize, or * ref on something we don't recognize. i suppose this should * be handled with either a warning, and/or it should just be * passed thru unchanged. */ } /* return f if we found components which will need maps built for them. * otherwise return NULL. */ return ((*pos || *con) ? f : NULL); } /* given a map saying where the old items have ended up and an * array which refs those items, renumber the ref component, and remove * any refs to items which are no longer in the original component. */ Array _dxf_remap_by_map(Array a, Array map) { int i; int index; int nitems, nbytes; int mapitems; int newitems; Array na = NULL; Type t; Category c; int rank; int shape[MAXRANK]; int *sp, *mp; if (!DXGetArrayInfo(a, &nitems, &t, &c, &rank, shape)) return NULL; if (!DXTypeCheck(a, TYPE_INT, CATEGORY_REAL, 0) && !DXTypeCheck(a, TYPE_INT, CATEGORY_REAL, 1, 1)) { DXSetError(ERROR_INVALID_DATA, "#11385"); return NULL; } if (!DXGetArrayInfo(map, &mapitems, NULL, NULL, NULL, NULL)) return NULL; nbytes = sizeof(int); na = DXNewArray(TYPE_INT, CATEGORY_REAL, 0); if (!na) return NULL; if (!DXCopyAttributes((Object)na, (Object)a)) goto error; if (!DXAllocateArray(na, nitems)) goto error; sp = (int *)DXGetArrayData(a); mp = (int *)DXGetArrayData(map); if (!sp || !mp) goto error; newitems = 0; for (i=0; i= mapitems) continue; if (mp[index] < 0) continue; if (!DXAddArrayData(na, newitems, 1, (Pointer)&mp[index])) goto error; newitems++; } DXTrim(na); return na; error: DXDelete((Object)na); return NULL; } /* this needs to be implemented to save memory. for now, use remap_by_map() */ static Array remap_by_computation(Array a, int ndims, int *ccounts, int *corners, int *ncounts) { /* compute where the new points should go */ return a; } /* this builds a regular array of 0 to N-1 items, which are going * to be partitioned just like the target, so we can see where the * items are going to be moved to. */ Error _dxf_make_map_template(Array a, Array *map) { int i, *ip; int nitems; if (!DXGetArrayInfo(a, &nitems, NULL, NULL, NULL, NULL)) return NULL; *map = DXNewArray(TYPE_INT, CATEGORY_REAL, 0); if (! *map) return NULL; if (!DXAddArrayData(*map, 0, nitems, NULL)) goto error; ip = (int *)DXGetArrayData(*map); if (!ip) goto error; for (i=0; i