source: nanovis/trunk/dxReader.cpp @ 5119

Last change on this file since 5119 was 4897, checked in by ldelgass, 9 years ago

add header deps

  • Property svn:eol-style set to native
File size: 16.8 KB
RevLine 
[2798]1 /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
[835]2/*
3 * ----------------------------------------------------------------------
4 * Nanovis: Visualization of Nanoelectronics Data
5 *
6 *  dxReader.cpp
[927]7 *      This module contains openDX readers for 2D and 3D volumes.
[835]8 *
9 * ======================================================================
10 *  AUTHOR:  Wei Qiao <qiaow@purdue.edu>
11 *           Michael McLennan <mmclennan@purdue.edu>
12 *           Purdue Rendering and Perceptualization Lab (PURPL)
13 *
[3502]14 *  Copyright (c) 2004-2013  HUBzero Foundation, LLC
[835]15 *
16 *  See the file "license.terms" for information on usage and
17 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 * ======================================================================
19 */
20#include <stdio.h>
21#include <math.h>
[2880]22#include <sys/types.h>
23#include <unistd.h>
24#include <float.h>
25
[835]26#include <fstream>
27#include <iostream>
28#include <sstream>
29#include <string>
30
[4897]31#include <RpMesh1D.h>
[2831]32#include <RpFieldRect3D.h>
33#include <RpFieldPrism3D.h>
34
[3502]35// common file/data reader functions
36#include "ReaderCommon.h"
[2831]37
[2881]38#include "config.h"
[835]39#include "nanovis.h"
[3611]40#include "dxReader.h"
[2831]41#include "Unirect.h"
[2849]42#include "Volume.h"
[835]43
[3611]44using namespace nv;
45
[2849]46/**
47 * \brief Load a 3D volume from a dx-format file
[2880]48 *
49 * In DX format:
50 *  rank 0 means scalars,
51 *  rank 1 means vectors,
52 *  rank 2 means matrices,
53 *  rank 3 means tensors
54 *
55 *  For rank 1, shape is a single number equal to the number of dimensions.
56 *  e.g. rank 1 shape 3 means a 3-component vector field
[835]57 */
[1493]58Volume *
[4063]59nv::load_dx_volume_stream(const char *tag, std::iostream& fin)
[851]60{
[3502]61    TRACE("Enter tag:%s", tag);
[2849]62
[835]63    Rappture::MeshTri2D xymesh;
64    int dummy, nx, ny, nz, nxy, npts;
[2880]65    // origin
66    double x0, y0, z0;
67    // deltas (cell dimensions)
68    double dx, dy, dz;
69    // length of volume edges
70    double lx, ly, lz;
71    // temps for delta values
72    double ddx, ddy, ddz;
[835]73    char line[128], type[128], *start;
74
75    int isrect = 1;
[2849]76
77    dx = dy = dz = 0.0;
78    x0 = y0 = z0 = 0.0; // May not have an origin line.
[1474]79    nx = ny = nz = npts = nxy = 0;
[2880]80    while (!fin.eof()) {
[2849]81        fin.getline(line, sizeof(line) - 1);
[2861]82        if (fin.fail()) {
[4063]83            ERROR("error in data stream");
[2861]84            return NULL;
85        }
[2849]86        for (start = line; *start == ' ' || *start == '\t'; start++)
[835]87            ;  // skip leading blanks
88
89        if (*start != '#') {  // skip comment lines
[2849]90            if (sscanf(start, "object %d class gridpositions counts %d %d %d",
[2839]91                       &dummy, &nx, &ny, &nz) == 4) {
[835]92                // found grid size
93                isrect = 1;
[2849]94            } else if (sscanf(start, "object %d class array type float rank 1 shape 3 items %d data follows",
95                              &dummy, &nxy) == 2) {
[835]96                isrect = 0;
[2849]97
[835]98                double xx, yy, zz;
[2849]99                for (int i = 0; i < nxy; i++) {
[835]100                    fin.getline(line,sizeof(line)-1);
101                    if (sscanf(line, "%lg %lg %lg", &xx, &yy, &zz) == 3) {
[2880]102                        xymesh.addNode(Rappture::Node2D(xx, yy));
[835]103                    }
104                }
105
106                char fpts[128];
107                sprintf(fpts, "/tmp/tmppts%d", getpid());
108                char fcells[128];
109                sprintf(fcells, "/tmp/tmpcells%d", getpid());
110
111                std::ofstream ftmp(fpts);
112                // save corners of bounding box first, to work around meshing
113                // problems in voronoi utility
[2942]114                int numBoundaryPoints = 4;
115
116                // Bump out the bounds by an epsilon to avoid problem when
117                // corner points are already nodes
118                double XEPS = (xymesh.rangeMax(Rappture::xaxis) -
119                               xymesh.rangeMin(Rappture::xaxis)) / 10.0f;
120
121                double YEPS = (xymesh.rangeMax(Rappture::yaxis) -
122                               xymesh.rangeMin(Rappture::yaxis)) / 10.0f;
123
124                ftmp << xymesh.rangeMin(Rappture::xaxis) - XEPS << " "
125                     << xymesh.rangeMin(Rappture::yaxis) - YEPS << std::endl;
126                ftmp << xymesh.rangeMax(Rappture::xaxis) + XEPS << " "
127                     << xymesh.rangeMin(Rappture::yaxis) - YEPS << std::endl;
128                ftmp << xymesh.rangeMax(Rappture::xaxis) + XEPS << " "
129                     << xymesh.rangeMax(Rappture::yaxis) + YEPS << std::endl;
130                ftmp << xymesh.rangeMin(Rappture::xaxis) - XEPS << " "
131                     << xymesh.rangeMax(Rappture::yaxis) + YEPS << std::endl;
132
[2849]133                for (int i = 0; i < nxy; i++) {
[835]134                    ftmp << xymesh.atNode(i).x() << " " << xymesh.atNode(i).y() << std::endl;
135                }
136                ftmp.close();
137
138                char cmdstr[512];
139                sprintf(cmdstr, "voronoi -t < %s > %s", fpts, fcells);
140                if (system(cmdstr) == 0) {
141                    int cx, cy, cz;
142                    std::ifstream ftri(fcells);
143                    while (!ftri.eof()) {
144                        ftri.getline(line,sizeof(line)-1);
145                        if (sscanf(line, "%d %d %d", &cx, &cy, &cz) == 3) {
[2942]146                            if (cx >= numBoundaryPoints &&
147                                cy >= numBoundaryPoints &&
148                                cz >= numBoundaryPoints) {
149                                // skip boundary points we added
150                                xymesh.addCell(cx - numBoundaryPoints,
151                                               cy - numBoundaryPoints,
152                                               cz - numBoundaryPoints);
[835]153                            }
154                        }
155                    }
156                    ftri.close();
157                } else {
[4063]158                    ERROR("triangularization failed");
[2839]159                    return NULL;
[835]160                }
[2849]161                unlink(fpts);
162                unlink(fcells);
[911]163            } else if (sscanf(start, "object %d class regulararray count %d", &dummy, &nz) == 2) {
[835]164                // found z-grid
[911]165            } else if (sscanf(start, "origin %lg %lg %lg", &x0, &y0, &z0) == 3) {
[835]166                // found origin
[911]167            } else if (sscanf(start, "delta %lg %lg %lg", &ddx, &ddy, &ddz) == 3) {
[2880]168                // found one of the delta lines
[2840]169                int count = 0;
170                if (ddx != 0.0) {
171                    dx = ddx;
172                    count++;
173                }
174                if (ddy != 0.0) {
175                    dy = ddy;
176                    count++;
177                }
178                if (ddz != 0.0) {
179                    dz = ddz;
180                    count++;
181                }
182                if (count > 1) {
[4063]183                    ERROR("don't know how to handle multiple non-zero"
184                          " delta values");
[2840]185                    return NULL;
186                }
[2849]187            } else if (sscanf(start, "object %d class array type %s rank 0 items %d data follows",
188                              &dummy, type, &npts) == 3) {
[835]189                if (isrect && (npts != nx*ny*nz)) {
[4063]190                    ERROR("inconsistent data: expected %d points"
191                          " but found %d points", nx*ny*nz, npts);
[1493]192                    return NULL;
[911]193                } else if (!isrect && (npts != nxy*nz)) {
[4063]194                    ERROR("inconsistent data: expected %d points"
195                          " but found %d points", nxy*nz, npts);
[1493]196                    return NULL;
[835]197                }
198                break;
[2849]199            } else if (sscanf(start, "object %d class array type %s rank 0 times %d data follows",
200                              &dummy, type, &npts) == 3) {
[835]201                if (npts != nx*ny*nz) {
[4063]202                    ERROR("inconsistent data: expected %d points"
203                          " but found %d points", nx*ny*nz, npts);
[1493]204                    return NULL;
[835]205                }
206                break;
207            }
208        }
[2880]209    }
[835]210
[3874]211    TRACE("found nx=%d ny=%d nxy=%d nz=%d\ndx=%g dy=%g dz=%g\nx0=%g y0=%g z0=%g",
[2953]212          nx, ny, nxy, nz, dx, dy, dz, x0, y0, z0);
[2861]213
[2942]214    lx = (nx - 1) * dx;
215    ly = (ny - 1) * dy;
216    lz = (nz - 1) * dz;
217
[835]218    // read data points
[2880]219    if (fin.eof()) {
[4063]220        ERROR("data not found in stream");
[2839]221        return NULL;
[1382]222    }
[3597]223    Volume *volume = NULL;
[2942]224    float *data = NULL;
225    double vmin = DBL_MAX;
226    double nzero_min = DBL_MAX;
227    double vmax = -DBL_MAX;
[1382]228    if (isrect) {
[3362]229#ifdef DOWNSAMPLE_DATA
[2880]230        Rappture::Mesh1D xgrid(x0, x0 + lx, nx);
231        Rappture::Mesh1D ygrid(y0, y0 + ly, ny);
232        Rappture::Mesh1D zgrid(z0, z0 + lz, nz);
233        Rappture::FieldRect3D field(xgrid, ygrid, zgrid);
[3362]234#else // !DOWNSAMPLE_DATA
235        data = new float[nx *  ny *  nz * 4];
236        memset(data, 0, nx*ny*nz*4);
237#endif // DOWNSAMPLE_DATA
[2839]238        double dval[6];
239        int nread = 0;
240        int ix = 0;
241        int iy = 0;
242        int iz = 0;
[2849]243
[2839]244        while (!fin.eof() && nread < npts) {
245            fin.getline(line,sizeof(line)-1);
[2861]246            if (fin.fail()) {
[4063]247                ERROR("error reading data points");
[2861]248                return NULL;
249            }
[2849]250            int n = sscanf(line, "%lg %lg %lg %lg %lg %lg",
251                           &dval[0], &dval[1], &dval[2], &dval[3], &dval[4], &dval[5]);
252            for (int p = 0; p < n; p++) {
[2880]253#ifdef notdef
254                if (isnan(dval[p])) {
[3452]255                    TRACE("Found NAN in input at %d,%d,%d", ix, iy, iz);
[2880]256                }
257#endif
[3362]258#ifdef DOWNSAMPLE_DATA
259                int nindex = iz*nx*ny + iy*nx + ix;
260                field.define(nindex, dval[p]);
261#else // !DOWNSAMPLE_DATA
[2839]262                int nindex = (iz*nx*ny + iy*nx + ix) * 4;
263                data[nindex] = dval[p];
[2849]264
[2839]265                if (dval[p] < vmin) {
266                    vmin = dval[p];
267                } else if (dval[p] > vmax) {
268                    vmax = dval[p];
269                }
[2849]270                if (dval[p] != 0.0 && dval[p] < nzero_min) {
[2839]271                    nzero_min = dval[p];
272                }
[3362]273#endif // DOWNSAMPLE_DATA
[2839]274                nread++;
275                if (++iz >= nz) {
276                    iz = 0;
277                    if (++iy >= ny) {
278                        iy = 0;
279                        ++ix;
280                    }
281                }
282            }
283        }
[2849]284
[2839]285        // make sure that we read all of the expected points
[3574]286        if (nread != npts) {
[4063]287            ERROR("inconsistent data: expected %d points"
288                  " but found %d points", npts, nread);
[2839]289            return NULL;
290        }
[2849]291
[3362]292#ifndef DOWNSAMPLE_DATA
[2849]293
[3574]294        // scale all values [0-1], -1 => out of bounds
295        normalizeScalar(data, npts, 4, vmin, vmax);
[2880]296        computeSimpleGradient(data, nx, ny, nz,
297                              dx, dy, dz);
[3362]298#else // DOWNSAMPLE_DATA
[2839]299        // figure out a good mesh spacing
[3576]300        int nsample = 64;
[2880]301        double dmin = pow((lx*ly*lz)/((nsample-1)*(nsample-1)*(nsample-1)), 0.333);
[2849]302
[3362]303        nx = (int)ceil(lx/dmin) + 1;
304        ny = (int)ceil(ly/dmin) + 1;
305        nz = (int)ceil(lz/dmin) + 1;
[2880]306
[2877]307#ifndef HAVE_NPOT_TEXTURES
[2839]308        // must be an even power of 2 for older cards
309        nx = (int)pow(2.0, ceil(log10((double)nx)/log10(2.0)));
310        ny = (int)pow(2.0, ceil(log10((double)ny)/log10(2.0)));
311        nz = (int)pow(2.0, ceil(log10((double)nz)/log10(2.0)));
[835]312#endif
[2849]313
[2880]314        dx = lx /(double)(nx - 1);
315        dy = ly /(double)(ny - 1);
316        dz = lz /(double)(nz - 1);
317
[2942]318        vmin = field.valueMin();
319        vmax = field.valueMax();
320        nzero_min = DBL_MAX;
[2880]321        double dv = vmax - vmin;
[2839]322        if (dv == 0.0) {
[2849]323            dv = 1.0;
[2839]324        }
[2942]325
[2880]326        int ngen = 0;
[3362]327#ifdef FILTER_GRADIENTS
[2880]328        // Sobel filter expects a single float at
329        // each node
330        const int step = 1;
331        float *cdata = new float[nx*ny*nz * step];
332#else // !FILTER_GRADIENTS
333        // Leave 3 floats of space for gradients
334        // to be filled in by computeSimpleGradient()
335        const int step = 4;
[2942]336        data = new float[nx*ny*nz * step];
[2880]337#endif // FILTER_GRADIENTS
[2849]338
339        for (int iz = 0; iz < nz; iz++) {
[2880]340            double zval = z0 + iz*dz;
[2849]341            for (int iy = 0; iy < ny; iy++) {
[2880]342                double yval = y0 + iy*dy;
[2849]343                for (int ix = 0; ix < nx; ix++) {
[2880]344                    double xval = x0 + ix*dx;
[2849]345                    double v = field.value(xval, yval, zval);
[2880]346#ifdef notdef
347                    if (isnan(v)) {
348                        TRACE("NAN at %d,%d,%d: (%g, %g, %g)", ix, iy, iz, xval, yval, zval);
[2839]349                    }
[835]350#endif
[2849]351                    if (v != 0.0 && v < nzero_min) {
[2839]352                        nzero_min = v;
353                    }
[3362]354#ifdef FILTER_GRADIENTS
[3574]355                    // NOT normalized, may have NaNs (FIXME: how does gradient filter handle NaN?)
[2839]356                    cdata[ngen] = v;
[2880]357#else // !FILTER_GRADIENTS
358                    // scale all values [0-1], -1 => out of bounds
359                    v = (isnan(v)) ? -1.0 : (v - vmin)/dv;
360                    data[ngen] = v;
361#endif // FILTER_GRADIENTS
[2839]362                    ngen += step;
363                }
364            }
365        }
[3362]366#ifdef FILTER_GRADIENTS
[2880]367        // computeGradient returns a new array with gradients
368        // filled in, so data is now 4 floats per node
[2942]369        data = computeGradient(cdata, nx, ny, nz,
370                               dx, dy, dz,
371                               vmin, vmax);
[2874]372        delete [] cdata;
[2880]373#else // !FILTER_GRADIENTS
374        computeSimpleGradient(data, nx, ny, nz,
375                              dx, dy, dz);
376#endif // FILTER_GRADIENTS
[2849]377
[3362]378#endif // DOWNSAMPLE_DATA
[1382]379    } else {
[2880]380        Rappture::Mesh1D zgrid(z0, z0 + (nz-1)*dz, nz);
[2839]381        Rappture::FieldPrism3D field(xymesh, zgrid);
[2849]382
[2839]383        double dval;
384        int nread = 0;
385        int ixy = 0;
386        int iz = 0;
387        while (!fin.eof() && nread < npts) {
388            fin >> dval;
389            if (fin.fail()) {
[4063]390                ERROR("after %d of %d points: can't read number",
391                      nread, npts);
[2839]392                return NULL;
393            } else {
394                int nid = nxy*iz + ixy;
395                field.define(nid, dval);
[2849]396
[2839]397                nread++;
398                if (++iz >= nz) {
399                    iz = 0;
400                    ixy++;
401                }
402            }
403        }
[2849]404
[2839]405        // make sure that we read all of the expected points
[3574]406        if (nread != npts) {
[4063]407            ERROR("inconsistent data: expected %d points"
408                  " but found %d points", npts, nread);
[2839]409            return NULL;
410        }
[2849]411
[2839]412        x0 = field.rangeMin(Rappture::xaxis);
[2880]413        lx = field.rangeMax(Rappture::xaxis) - field.rangeMin(Rappture::xaxis);
[2839]414        y0 = field.rangeMin(Rappture::yaxis);
[2880]415        ly = field.rangeMax(Rappture::yaxis) - field.rangeMin(Rappture::yaxis);
[2839]416        z0 = field.rangeMin(Rappture::zaxis);
[2880]417        lz = field.rangeMax(Rappture::zaxis) - field.rangeMin(Rappture::zaxis);
[2953]418
419        // figure out a good mesh spacing
[3576]420        int nsample = 64;
[2880]421        double dmin = pow((lx*ly*lz)/((nsample-1)*(nsample-1)*(nsample-1)), 0.333);
[2849]422
[3362]423        nx = (int)ceil(lx/dmin) + 1;
424        ny = (int)ceil(ly/dmin) + 1;
425        nz = (int)ceil(lz/dmin) + 1;
[2877]426#ifndef HAVE_NPOT_TEXTURES
[2839]427        // must be an even power of 2 for older cards
428        nx = (int)pow(2.0, ceil(log10((double)nx)/log10(2.0)));
429        ny = (int)pow(2.0, ceil(log10((double)ny)/log10(2.0)));
430        nz = (int)pow(2.0, ceil(log10((double)nz)/log10(2.0)));
[1000]431#endif
[2880]432
433        dx = lx /(double)(nx - 1);
434        dy = ly /(double)(ny - 1);
435        dz = lz /(double)(nz - 1);
436
[2942]437        vmin = field.valueMin();
438        vmax = field.valueMax();
439        nzero_min = DBL_MAX;
[2880]440        double dv = vmax - vmin;
[2849]441        if (dv == 0.0) {
442            dv = 1.0;
443        }
444
[2953]445        data = new float[4*nx*ny*nz];
[2839]446        // generate the uniformly sampled data that we need for a volume
447        int ngen = 0;
[2849]448        for (int iz = 0; iz < nz; iz++) {
[2880]449            double zval = z0 + iz*dz;
[2849]450            for (int iy = 0; iy < ny; iy++) {
[2880]451                double yval = y0 + iy*dy;
[2849]452                for (int ix = 0; ix < nx; ix++) {
[2880]453                    double xval = x0 + ix*dx;
[2849]454                    double v = field.value(xval, yval, zval);
455
456                    if (v != 0.0 && v < nzero_min) {
[2839]457                        nzero_min = v;
458                    }
459                    // scale all values [0-1], -1 => out of bounds
460                    v = (isnan(v)) ? -1.0 : (v - vmin)/dv;
461                    data[ngen] = v;
[2849]462
[2839]463                    ngen += 4;
464                }
465            }
466        }
[2849]467
[2880]468        computeSimpleGradient(data, nx, ny, nz,
469                              dx, dy, dz);
[2942]470    }
[2849]471
[3452]472    TRACE("nx = %i ny = %i nz = %i", nx, ny, nz);
473    TRACE("x0 = %lg y0 = %lg z0 = %lg", x0, y0, z0);
474    TRACE("lx = %lg ly = %lg lz = %lg", lx, ly, lz);
475    TRACE("dx = %lg dy = %lg dz = %lg", dx, dy, dz);
476    TRACE("dataMin = %lg dataMax = %lg nzero_min = %lg",
[2942]477          vmin, vmax, nzero_min);
478
[3597]479    volume = NanoVis::loadVolume(tag, nx, ny, nz, 4, data,
[2942]480                                 vmin, vmax, nzero_min);
[3597]481    volume->xAxis.setRange(x0, x0 + lx);
482    volume->yAxis.setRange(y0, y0 + ly);
483    volume->zAxis.setRange(z0, z0 + lz);
484    volume->updatePending = true;
[2942]485
486    delete [] data;
[1000]487
[3597]488    return volume;
[1000]489}
Note: See TracBrowser for help on using the repository browser.