Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

QuickSurf.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2019 The Board of Trustees of the           
00004  *cr                        University of Illinois                       
00005  *cr                         All Rights Reserved                        
00006  *cr                                                                   
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: QuickSurf.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.136 $      $Date: 2022/05/23 19:10:01 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *   Fast gaussian surface representation
00019  ***************************************************************************/
00020 
00021 // pgcc 2016 has troubles with hand-vectorized x86 intrinsics presently
00022 #if !defined(__PGIC__)
00023 
00024 // Intel x86 hardware
00025 #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64))
00026 #if !defined(__SSE2__) && defined(_WIN64)
00027 #define __SSE2__ 1      /* MSVC fails to define SSE macros */
00028 #endif
00029 #endif
00030 
00031 #define VMDQSURFUSESSE 1
00032 #endif
00033 
00034 // The OpenPOWER VSX code path runs on POWER8 and later hardware, but is
00035 // untested on older platforms that support VSX instructions.
00036 // XXX GCC 4.8.5 breaks with conflicts between vec_xxx() routines
00037 //     defined in utilities.h vs. VSX intrinsics in altivec.h and similar.
00038 //     For now, we disable VSX for GCC for this source file.
00039 // IBM Power 8/9/10 Altivec/VSX instructions:
00040 //   https://www.nxp.com/docs/en/reference-manual/ALTIVECPEM.pdf
00041 #if !defined(__GNUC__) && defined(__VEC__)
00042 #define VMDQSURFUSEVSX 1
00043 #endif
00044 
00045 #include <stdio.h>
00046 #include <stdlib.h>
00047 #if VMDQSURFUSESSE && defined(__SSE2__) 
00048 #include <emmintrin.h>
00049 #endif
00050 
00051 #if (defined(VMDQSURFUSEVSX) && defined(__VSX__))
00052 #if defined(__GNUC__) && defined(__VEC__)
00053 #include <altivec.h>
00054 #endif
00055 #endif
00056 
00057 #include <string.h>
00058 #include <math.h>
00059 #include "QuickSurf.h"
00060 #if defined(VMDCUDA)
00061 #include "CUDAQuickSurf.h"
00062 #endif
00063 #include "Measure.h"
00064 #include "Inform.h"
00065 #include "utilities.h"
00066 #include "WKFUtils.h"
00067 #include "VolumetricData.h"
00068 
00069 #include "VMDDisplayList.h"
00070 #include "Displayable.h"
00071 #include "DispCmds.h"
00072 #include "ProfileHooks.h"
00073 #include "VMDApp.h" // access global CPU insn flags for dispatch
00074 
00075 #define MIN(X,Y) (((X)<(Y))? (X) : (Y))
00076 #define MAX(X,Y) (((X)>(Y))? (X) : (Y))
00077 
00078 // fctn prototypes for runtime dispatched AVX2 kernels, etc
00079 void vmd_gaussdensity_avx2(int verbose,
00080                            int natoms, const float *xyzr,
00081                            const float *atomicnum,
00082                            const float *colors,
00083                            float *densitymap, float *voltexmap,
00084                            const int *numvoxels,
00085                            float radscale, float gridspacing,
00086                            float isovalue, float gausslim);
00087 
00088 void vmd_gaussdensity_neon(int verbose,
00089                            int natoms, const float *xyzr,
00090                            const float *atomicnum,
00091                            const float *colors,
00092                            float *densitymap, float *voltexmap,
00093                            const int *numvoxels,
00094                            float radscale, float gridspacing,
00095                            float isovalue, float gausslim);
00096 
00097 /*
00098  * David J. Hardy
00099  * 12 Dec 2008
00100  *
00101  * aexpfnx() - Approximate expf() for negative x.
00102  *
00103  * Assumes that x <= 0.
00104  *
00105  * Assumes IEEE format for single precision float, specifically:
00106  * 1 sign bit, 8 exponent bits biased by 127, and 23 mantissa bits.
00107  *
00108  * Interpolates exp() on interval (-1/log2(e), 0], then shifts it by
00109  * multiplication of a fast calculation for 2^(-N).  The interpolation
00110  * uses a linear blending of 3rd degree Taylor polynomials at the end
00111  * points, so the approximation is once differentiable.
00112  *
00113  * The error is small (max relative error per interval is calculated
00114  * to be 0.131%, with a max absolute error of -0.000716).
00115  *
00116  * The cutoff is chosen so as to speed up the computation by early
00117  * exit from function, with the value chosen to give less than the
00118  * the max absolute error.  Use of a cutoff is unnecessary, except
00119  * for needing to shift smallest floating point numbers to zero,
00120  * i.e. you could remove cutoff and replace by:
00121  *
00122  * #define MINXNZ  -88.0296919311130  // -127 * log(2)
00123  *
00124  *   if (x < MINXNZ) return 0.f;
00125  *
00126  * Use of a cutoff causes a discontinuity which can be eliminated
00127  * through the use of a switching function.
00128  *
00129  * We can obtain arbitrarily smooth approximation by taking k+1 nodes on
00130  * the interval and weighting their respective Taylor polynomials by the
00131  * kth order Lagrange interpolant through those nodes.  The wiggle in the
00132  * polynomial interpolation due to equidistant nodes (Runge's phenomenon)
00133  * can be reduced by using Chebyshev nodes.
00134  */
00135 
00136 #if defined(__GNUC__) && ! defined(__INTEL_COMPILER)
00137 #define __align(X)  __attribute__((aligned(X) ))
00138 #if (__GNUC__ < 4)
00139 #define MISSING_mm_cvtsd_f64
00140 #endif
00141 #else
00142 #define __align(X) __declspec(align(X) )
00143 #endif
00144 
00145 #define MLOG2EF    -1.44269504088896f
00146 
00147 /*
00148  * Interpolating coefficients for linear blending of the
00149  * 3rd degree Taylor expansion of 2^x about 0 and -1.
00150  */
00151 #define SCEXP0     1.0000000000000000f
00152 #define SCEXP1     0.6987082824680118f
00153 #define SCEXP2     0.2633174272827404f
00154 #define SCEXP3     0.0923611991471395f
00155 #define SCEXP4     0.0277520543324108f
00156 
00157 /* for single precision float */
00158 #define EXPOBIAS   127
00159 #define EXPOSHIFT   23
00160 
00161 /* cutoff is optional, but can help avoid unnecessary work */
00162 #define ACUTOFF    -10
00163 
00164 typedef union flint_t {
00165   float f;
00166   int n;
00167 } flint;
00168 
00169 #if VMDQSURFUSESSE && defined(__SSE2__)
00170 // SSE variant of the 'flint' union above
00171 typedef union SSEreg_t {
00172   __m128  f;  // 4x float (SSE)
00173   __m128i i;  // 4x 32-bit int (SSE2)
00174 } SSEreg;
00175 #endif
00176 
00177 
00178 #if 0
00179 static float aexpfnx(float x) {
00180   /* assume x <= 0 */
00181   float mb;
00182   int mbflr;
00183   float d;
00184   float sy;
00185   flint scalfac;
00186 
00187   if (x < ACUTOFF) return 0.f;
00188 
00189   mb = x * MLOG2EF;    /* change base to 2, mb >= 0 */
00190   mbflr = (int) mb;    /* get int part, floor() */
00191   d = mbflr - mb;      /* remaining exponent, -1 < d <= 0 */
00192   sy = SCEXP0 + d*(SCEXP1 + d*(SCEXP2 + d*(SCEXP3 + d*SCEXP4)));
00193                        /* approx with linear blend of Taylor polys */
00194   scalfac.n = (EXPOBIAS - mbflr) << EXPOSHIFT;  /* 2^(-mbflr) */
00195   return (sy * scalfac.f);  /* scaled approx */
00196 }
00197 
00198 
00199 static void vmd_gaussdensity(int verbose, 
00200                              int natoms, const float *xyzr,
00201                              const float *atomicnum,
00202                              const float *colors,
00203                              float *densitymap, float *voltexmap, 
00204                              const int *numvoxels, 
00205                              float radscale, float gridspacing, 
00206                              float isovalue, float gausslim) {
00207   int i, x, y, z;
00208   int maxvoxel[3];
00209   maxvoxel[0] = numvoxels[0]-1; 
00210   maxvoxel[1] = numvoxels[1]-1; 
00211   maxvoxel[2] = numvoxels[2]-1; 
00212   const float invgridspacing = 1.0f / gridspacing;
00213 
00214   // compute colors only if necessary, since they are costly
00215   if (voltexmap != NULL) {
00216     float invisovalue = 1.0f / isovalue;
00217     // compute both density map and floating point color texture map
00218     for (i=0; i<natoms; i++) {
00219       if (verbose && ((i & 0x3fff) == 0)) {
00220         printf("."); 
00221         fflush(stdout);
00222       }
00223 
00224       ptrdiff_t ind = i*4L;
00225       float scaledrad = xyzr[ind + 3L] * radscale;
00226 
00227       // MDFF atomic number weighted density factor
00228       float atomicnumfactor = 1.0f;
00229       if (atomicnum != NULL) {
00230         atomicnumfactor = atomicnum[i];
00231       }
00232 
00233       float arinv = 1.0f/(2.0f*scaledrad*scaledrad);
00234       float radlim = gausslim * scaledrad;
00235       float radlim2 = radlim * radlim; // cutoff test done in cartesian coords
00236       radlim *= invgridspacing;
00237 
00238       float tmp;
00239       tmp = xyzr[ind  ] * invgridspacing;
00240       int xmin = MAX((int) (tmp - radlim), 0);
00241       int xmax = MIN((int) (tmp + radlim), maxvoxel[0]);
00242       tmp = xyzr[ind+1] * invgridspacing;
00243       int ymin = MAX((int) (tmp - radlim), 0);
00244       int ymax = MIN((int) (tmp + radlim), maxvoxel[1]);
00245       tmp = xyzr[ind+2] * invgridspacing;
00246       int zmin = MAX((int) (tmp - radlim), 0);
00247       int zmax = MIN((int) (tmp + radlim), maxvoxel[2]);
00248 
00249       float dz = zmin*gridspacing - xyzr[ind+2];
00250       for (z=zmin; z<=zmax; z++,dz+=gridspacing) {
00251         float dy = ymin*gridspacing - xyzr[ind+1];
00252         for (y=ymin; y<=ymax; y++,dy+=gridspacing) {
00253           float dy2dz2 = dy*dy + dz*dz;
00254 
00255           // early-exit when outside the cutoff radius in the Y-Z plane
00256           if (dy2dz2 >= radlim2) 
00257             continue;
00258 
00259           int addr = z * numvoxels[0] * numvoxels[1] + y * numvoxels[0];
00260           float dx = xmin*gridspacing - xyzr[ind];
00261           for (x=xmin; x<=xmax; x++,dx+=gridspacing) {
00262             float r2 = dx*dx + dy2dz2;
00263             float expval = -r2 * arinv;
00264 #if VMDUSEFULLEXP
00265             // use the math library exponential routine
00266             float density = exp(expval);
00267 #else
00268             // use our (much faster) fast exponential approximation
00269             float density = aexpfnx(expval);
00270 #endif
00271 
00272             density *= atomicnumfactor; // MDFF Cryo-EM atomic number density
00273 
00274             // accumulate density value to density map
00275             densitymap[addr + x] += density;
00276 
00277             // Accumulate density-weighted color to texture map.
00278             // Pre-multiply colors by the inverse isovalue we will extract   
00279             // the surface on, to cause the final color to be normalized.
00280             density *= invisovalue;
00281             ptrdiff_t caddr = (addr + x) * 3L;
00282 
00283             // color by atom colors
00284             voltexmap[caddr    ] += density * colors[ind    ];
00285             voltexmap[caddr + 1] += density * colors[ind + 1];
00286             voltexmap[caddr + 2] += density * colors[ind + 2];
00287           }
00288         }
00289       }
00290     }
00291   } else {
00292     // compute density map only
00293     for (i=0; i<natoms; i++) {
00294       if (verbose && ((i & 0x3fff) == 0)) {
00295         printf("."); 
00296         fflush(stdout);
00297       }
00298 
00299       ptrdiff_t ind = i*4L;
00300       float scaledrad = xyzr[ind + 3] * radscale;
00301 
00302       // MDFF atomic number weighted density factor
00303       float atomicnumfactor = 1.0f;
00304       if (atomicnum != NULL) {
00305         atomicnumfactor = atomicnum[i];
00306       }
00307 
00308       float arinv = 1.0f/(2.0f*scaledrad*scaledrad);
00309       float radlim = gausslim * scaledrad;
00310       float radlim2 = radlim * radlim; // cutoff test done in cartesian coords
00311       radlim *= invgridspacing;
00312 
00313       float tmp;
00314       tmp = xyzr[ind  ] * invgridspacing;
00315       int xmin = MAX((int) (tmp - radlim), 0);
00316       int xmax = MIN((int) (tmp + radlim), maxvoxel[0]);
00317       tmp = xyzr[ind+1] * invgridspacing;
00318       int ymin = MAX((int) (tmp - radlim), 0);
00319       int ymax = MIN((int) (tmp + radlim), maxvoxel[1]);
00320       tmp = xyzr[ind+2] * invgridspacing;
00321       int zmin = MAX((int) (tmp - radlim), 0);
00322       int zmax = MIN((int) (tmp + radlim), maxvoxel[2]);
00323 
00324       float dz = zmin*gridspacing - xyzr[ind+2];
00325       for (z=zmin; z<=zmax; z++,dz+=gridspacing) {
00326         float dy = ymin*gridspacing - xyzr[ind+1];
00327         for (y=ymin; y<=ymax; y++,dy+=gridspacing) {
00328           float dy2dz2 = dy*dy + dz*dz;
00329 
00330           // early-exit when outside the cutoff radius in the Y-Z plane
00331           if (dy2dz2 >= radlim2) 
00332             continue;
00333 
00334           int addr = z * numvoxels[0] * numvoxels[1] + y * numvoxels[0];
00335           float dx = xmin*gridspacing - xyzr[ind];
00336           for (x=xmin; x<=xmax; x++,dx+=gridspacing) {
00337             float r2 = dx*dx + dy2dz2;
00338             float expval = -r2 * arinv;
00339 #if VMDUSEFULLEXP
00340             // use the math library exponential routine
00341             float density = exp(expval);
00342 #else
00343             // use our (much faster) fast exponential approximation
00344             float density = aexpfnx(expval);
00345 #endif
00346 
00347             density *= atomicnumfactor; // MDFF Cryo-EM atomic number density
00348 
00349             // accumulate density value to density map
00350             densitymap[addr + x] += density;
00351           }
00352         }
00353       }
00354     }
00355   }
00356 }
00357 #endif
00358 
00359 
00360 
00361 static void vmd_gaussdensity_opt(wkf_cpu_caps_t *cpucaps, int verbose,
00362                                  int natoms, const float *xyzr,
00363                                  const float *atomicnum,
00364                                  const float *colors,
00365                                  float *densitymap, float *voltexmap, 
00366                                  const int *numvoxels, 
00367                                  float radscale, float gridspacing, 
00368                                  float isovalue, float gausslim) {
00369   int i, x, y, z;
00370   int maxvoxel[3];
00371   maxvoxel[0] = numvoxels[0]-1; 
00372   maxvoxel[1] = numvoxels[1]-1; 
00373   maxvoxel[2] = numvoxels[2]-1; 
00374   const float invgridspacing = 1.0f / gridspacing;
00375 
00376   //
00377   // runtime CPU dispatch
00378   //   check for optional vector instructions and execute custom kernels
00379   //   for the fastest code path supported by the detected hardware
00380   //
00381 #if defined(VMDCPUDISPATCH)
00382 
00383 // Intel x86
00384 #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64))
00385 
00386   if ((cpucaps != NULL) && 
00387       (cpucaps->flags & CPU_FMA) && (cpucaps->flags & CPU_AVX2) && 
00388       (getenv("VMDNOAVX2")==NULL)) {
00389     if (verbose)
00390       printf("vmd_gaussdensity_avx2()\n");
00391 
00392     vmd_gaussdensity_avx2(verbose, natoms, xyzr, atomicnum, colors, 
00393                           densitymap, voltexmap, numvoxels, radscale, 
00394                           gridspacing, isovalue, gausslim);
00395     return;
00396   }
00397 #endif
00398 
00399 #if defined(VMDUSENEON)
00400 // if ((cpucaps->flags & CPU_NEON) && (getenv("VMDNONEON") == NULL)) {
00401   if (1 && (getenv("VMDNONEON") == NULL)) {
00402     if (verbose)
00403       printf("vmd_gaussdensity_neon()\n");
00404 
00405     vmd_gaussdensity_neon(verbose, natoms, xyzr, atomicnum, colors, 
00406                           densitymap, voltexmap, numvoxels, radscale, 
00407                           gridspacing, isovalue, gausslim);
00408     return;
00409   }
00410 #endif
00411 
00412 
00413   if (verbose)
00414     printf("vmd_gaussdensity_opt()\n");
00415 #endif
00416 
00417 #if VMDQSURFUSESSE && defined(__SSE2__)
00418   int usesse=1;
00419   if (getenv("VMDNOSSE")) {
00420     usesse=0;
00421   }
00422 #endif
00423 
00424 #if VMDQSURFUSEVSX && defined(__VEC__)
00425   int usevsx=1;
00426   if (getenv("VMDNOVSX")) {
00427     usevsx=0;
00428   }
00429 #endif
00430 
00431 #if VMDQSURFUSESSE && defined(__SSE2__)
00432   // Variables for SSE optimized inner loop
00433   __m128 gridspacing4_4;
00434   __align(16) float sxdelta4[4]; // 16-byte aligned for SSE
00435 
00436   if (usesse) {
00437     gridspacing4_4 = _mm_set1_ps(gridspacing * 4.0f);
00438     for (x=0; x<4; x++)
00439       sxdelta4[x] = ((float) x) * gridspacing;
00440   }
00441 #endif
00442 
00443 #if VMDQSURFUSEVSX && defined(__VEC__)
00444   // Variables for VSX optimized inner loop
00445   vector float gridspacing4_4;
00446   __attribute__((aligned(16))) float sxdelta4[4]; // 16-byte aligned for VSX
00447 
00448   if (usevsx) {
00449     gridspacing4_4 = vec_splats(gridspacing * 4.0f);
00450     for (x=0; x<4; x++)
00451       sxdelta4[x] = ((float) x) * gridspacing;
00452   }
00453 #endif
00454 
00455   // compute colors only if necessary, since they are costly
00456   if (voltexmap != NULL) {
00457     float invisovalue = 1.0f / isovalue;
00458     // compute both density map and floating point color texture map
00459     for (i=0; i<natoms; i++) {
00460       if (verbose && ((i & 0x3fff) == 0)) {
00461         printf("."); 
00462         fflush(stdout);
00463       }
00464 
00465       ptrdiff_t ind = i*4L;
00466       float scaledrad = xyzr[ind + 3] * radscale;
00467 
00468       // MDFF atomic number weighted density factor
00469       float atomicnumfactor = 1.0f;
00470       if (atomicnum != NULL) {
00471         atomicnumfactor = atomicnum[i];
00472       }
00473 
00474       // negate, precompute reciprocal, and change to base 2 from the outset
00475       float arinv = -(1.0f/(2.0f*scaledrad*scaledrad)) * MLOG2EF;
00476       float radlim = gausslim * scaledrad;
00477       float radlim2 = radlim * radlim; // cutoff test done in cartesian coords
00478       radlim *= invgridspacing;
00479 
00480 #if VMDQSURFUSESSE && defined(__SSE2__)
00481       __m128 atomicnumfactor_4 = { 0 };
00482       __m128 arinv_4;
00483       if (usesse) {
00484         atomicnumfactor_4 = _mm_set1_ps(atomicnumfactor);
00485 #if VMDUSESVMLEXP
00486         // Use of Intel's SVML requires changing the pre-scaling factor
00487         arinv_4 = _mm_set1_ps(arinv * (2.718281828f/2.0f) / MLOG2EF); 
00488 #else
00489         // Use our fully inlined exp approximation
00490         arinv_4 = _mm_set1_ps(arinv);
00491 #endif
00492       }
00493 #endif
00494 
00495       float tmp;
00496       tmp = xyzr[ind  ] * invgridspacing;
00497       int xmin = MAX((int) (tmp - radlim), 0);
00498       int xmax = MIN((int) (tmp + radlim), maxvoxel[0]);
00499       tmp = xyzr[ind+1] * invgridspacing;
00500       int ymin = MAX((int) (tmp - radlim), 0);
00501       int ymax = MIN((int) (tmp + radlim), maxvoxel[1]);
00502       tmp = xyzr[ind+2] * invgridspacing;
00503       int zmin = MAX((int) (tmp - radlim), 0);
00504       int zmax = MIN((int) (tmp + radlim), maxvoxel[2]);
00505 
00506       float dz = zmin*gridspacing - xyzr[ind+2];
00507       for (z=zmin; z<=zmax; z++,dz+=gridspacing) {
00508         float dy = ymin*gridspacing - xyzr[ind+1];
00509         for (y=ymin; y<=ymax; y++,dy+=gridspacing) {
00510           float dy2dz2 = dy*dy + dz*dz;
00511 
00512           // early-exit when outside the cutoff radius in the Y-Z plane
00513           if (dy2dz2 >= radlim2) 
00514             continue;
00515 
00516           ptrdiff_t addr = ptrdiff_t(z * numvoxels[0]) * ptrdiff_t(numvoxels[1]) + ptrdiff_t(y * numvoxels[0]);
00517           float dx = xmin*gridspacing - xyzr[ind];
00518           x=xmin;
00519 
00520 #if VMDQSURFUSESSE && defined(__SSE2__)
00521           // Use SSE when we have a multiple-of-4 to compute
00522           // finish all remaining density map points with regular non-SSE loop
00523           if (usesse) {
00524             __align(16) SSEreg n;
00525             __align(16) SSEreg y;
00526             __m128 dy2dz2_4 = _mm_set1_ps(dy2dz2);
00527             __m128 dx_4 = _mm_add_ps(_mm_set1_ps(dx), _mm_load_ps(&sxdelta4[0]));
00528 
00529             for (; (x+3)<=xmax; x+=4,dx_4=_mm_add_ps(dx_4, gridspacing4_4)) {
00530               __m128 r2 = _mm_add_ps(_mm_mul_ps(dx_4, dx_4), dy2dz2_4);
00531               __m128 d;
00532 #if VMDUSESVMLEXP
00533               // use Intel's SVML exp2() routine
00534               y.f = _mm_exp2_ps(_mm_mul_ps(r2, arinv_4));
00535 #else
00536               // use our (much faster) fully inlined exponential approximation
00537               y.f = _mm_mul_ps(r2, arinv_4);         /* already negated and in base 2 */
00538               n.i = _mm_cvttps_epi32(y.f);
00539               d = _mm_cvtepi32_ps(n.i);
00540               d = _mm_sub_ps(d, y.f);
00541 
00542               // Approximate 2^{-d}, 0 <= d < 1, by interpolation.
00543               // Perform Horner's method to evaluate interpolating polynomial.
00544 #if 0
00545               // SSE 4.x FMADD instructions are not universally available
00546               y.f = _mm_fmadd_ps(d, _mm_set1_ps(SCEXP4), _mm_set1_ps(SCEXP3)); 
00547               y.f = _mm_fmadd_ps(y.f, d, _mm_set1_ps(SCEXP2));
00548               y.f = _mm_fmadd_ps(y.f, d, _mm_set1_ps(SCEXP1));
00549               y.f = _mm_fmadd_ps(y.f, d, _mm_set1_ps(SCEXP0));
00550 #else
00551               y.f = _mm_mul_ps(d, _mm_set_ps1(SCEXP4));      /* for x^4 term */
00552               y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP3));    /* for x^3 term */
00553               y.f = _mm_mul_ps(y.f, d);
00554               y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP2));    /* for x^2 term */
00555               y.f = _mm_mul_ps(y.f, d);
00556               y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP1));    /* for x^1 term */
00557               y.f = _mm_mul_ps(y.f, d);
00558               y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP0));    /* for x^0 term */
00559 #endif
00560 
00561               // Calculate 2^N exactly by directly manipulating floating point exponent,
00562               // then use it to scale y for the final result.
00563               n.i = _mm_sub_epi32(_mm_set1_epi32(EXPOBIAS), n.i);
00564               n.i = _mm_slli_epi32(n.i, EXPOSHIFT);
00565               y.f = _mm_mul_ps(y.f, n.f);
00566 #endif
00567 
00568               // At present, we do unaligned loads/stores since we can't guarantee
00569               // that the X-dimension is always a multiple of 4.
00570               float *ufptr = &densitymap[addr + x];
00571               d = _mm_loadu_ps(ufptr);
00572               y.f = _mm_mul_ps(y.f, atomicnumfactor_4); // MDFF density maps
00573               _mm_storeu_ps(ufptr, _mm_add_ps(d, y.f)); 
00574 
00575               // Accumulate density-weighted color to texture map.
00576               // Pre-multiply colors by the inverse isovalue we will extract   
00577               // the surface on, to cause the final color to be normalized.
00578               d = _mm_mul_ps(y.f, _mm_set_ps1(invisovalue));
00579               ptrdiff_t caddr = (addr + x) * 3L;
00580 
00581 #if 1
00582               float *txptr = &voltexmap[caddr];
00583               // unaligned load of 4 consecutive rgb3f texture map texels
00584               __m128 r0g0b0r1 = _mm_loadu_ps(txptr+0);
00585               __m128 g1b1r2g2 = _mm_loadu_ps(txptr+4); 
00586               __m128 b2r3g3b3 = _mm_loadu_ps(txptr+8);
00587 
00588               // convert rgb3f AOS format to 4-element SOA vectors using shuffle instructions
00589               __m128 r2g2r3g3 = _mm_shuffle_ps(g1b1r2g2, b2r3g3b3, _MM_SHUFFLE(2, 1, 3, 2)); 
00590               __m128 g0b0g1b1 = _mm_shuffle_ps(r0g0b0r1, g1b1r2g2, _MM_SHUFFLE(1, 0, 2, 1));
00591               __m128 r        = _mm_shuffle_ps(r0g0b0r1, r2g2r3g3, _MM_SHUFFLE(2, 0, 3, 0)); // r0r1r2r3
00592               __m128 g        = _mm_shuffle_ps(g0b0g1b1, r2g2r3g3, _MM_SHUFFLE(3, 1, 2, 0)); // g0g1g2g3
00593               __m128 b        = _mm_shuffle_ps(g0b0g1b1, b2r3g3b3, _MM_SHUFFLE(3, 0, 3, 1)); // b0g1b2b3
00594 
00595               // accumulate density-scaled colors into texels
00596               r = _mm_add_ps(r, _mm_mul_ps(d, _mm_set_ps1(colors[ind    ])));
00597               g = _mm_add_ps(g, _mm_mul_ps(d, _mm_set_ps1(colors[ind + 1])));
00598               b = _mm_add_ps(b, _mm_mul_ps(d, _mm_set_ps1(colors[ind + 2])));
00599 
00600               // convert 4-element SOA vectors to rgb3f AOS format using shuffle instructions
00601               __m128 r0r2g0g2  = _mm_shuffle_ps(r, g, _MM_SHUFFLE(2, 0, 2, 0));
00602               __m128 g1g3b1b3  = _mm_shuffle_ps(g, b, _MM_SHUFFLE(3, 1, 3, 1));
00603               __m128 b0b2r1r3  = _mm_shuffle_ps(b, r, _MM_SHUFFLE(3, 1, 2, 0));
00604  
00605               __m128 rr0g0b0r1 = _mm_shuffle_ps(r0r2g0g2, b0b2r1r3, _MM_SHUFFLE(2, 0, 2, 0)); 
00606               __m128 rg1b1r2g2 = _mm_shuffle_ps(g1g3b1b3, r0r2g0g2, _MM_SHUFFLE(3, 1, 2, 0)); 
00607               __m128 rb2r3g3b3 = _mm_shuffle_ps(b0b2r1r3, g1g3b1b3, _MM_SHUFFLE(3, 1, 3, 1)); 
00608  
00609               // unaligned store of 4 consecutive rgb3f texture map texels
00610               _mm_storeu_ps(txptr+0, rr0g0b0r1);
00611               _mm_storeu_ps(txptr+4, rg1b1r2g2);
00612               _mm_storeu_ps(txptr+8, rb2r3g3b3);
00613 
00614 #else
00615 
00616               // color by atom colors
00617               float r, g, b;
00618               r = colors[ind    ];
00619               g = colors[ind + 1];
00620               b = colors[ind + 2];
00621 
00622               SSEreg tmp; 
00623               tmp.f = d;
00624               float density;
00625               density = tmp.floatreg.r0; 
00626               voltexmap[caddr     ] += density * r;
00627               voltexmap[caddr +  1] += density * g;
00628               voltexmap[caddr +  2] += density * b;
00629 
00630               density = tmp.floatreg.r1; 
00631               voltexmap[caddr +  3] += density * r;
00632               voltexmap[caddr +  4] += density * g;
00633               voltexmap[caddr +  5] += density * b;
00634 
00635               density = tmp.floatreg.r2; 
00636               voltexmap[caddr +  6] += density * r;
00637               voltexmap[caddr +  7] += density * g;
00638               voltexmap[caddr +  8] += density * b;
00639 
00640               density = tmp.floatreg.r3; 
00641               voltexmap[caddr +  9] += density * r;
00642               voltexmap[caddr + 10] += density * g;
00643               voltexmap[caddr + 11] += density * b;
00644 #endif
00645             }
00646           }
00647 #endif
00648 
00649           // finish all remaining density map points with regular non-SSE loop
00650           for (; x<=xmax; x++,dx+=gridspacing) {
00651             float r2 = dx*dx + dy2dz2;
00652 
00653             // use our (much faster) fully inlined exponential approximation
00654             float mb = r2 * arinv;         /* already negated and in base 2 */
00655             int mbflr = (int) mb;          /* get int part, floor() */
00656             float d = mbflr - mb;          /* remaining exponent, -1 < d <= 0 */
00657 
00658             /* approx with linear blend of Taylor polys */
00659             float sy = SCEXP0 + d*(SCEXP1 + d*(SCEXP2 + d*(SCEXP3 + d*SCEXP4)));
00660 
00661             /* 2^(-mbflr) */
00662             flint scalfac;
00663             scalfac.n = (EXPOBIAS - mbflr) << EXPOSHIFT;  
00664 
00665             // XXX assume we are never beyond the cutoff value in this loop
00666             float density = (sy * scalfac.f);
00667 
00668             density *= atomicnumfactor; // MDFF Cryo-EM atomic number density
00669 
00670             // accumulate density value to density map
00671             densitymap[addr + x] += density;
00672 
00673             // Accumulate density-weighted color to texture map.
00674             // Pre-multiply colors by the inverse isovalue we will extract   
00675             // the surface on, to cause the final color to be normalized.
00676             density *= invisovalue;
00677             ptrdiff_t caddr = (addr + x) * 3L;
00678 
00679             // color by atom colors
00680             voltexmap[caddr    ] += density * colors[ind    ];
00681             voltexmap[caddr + 1] += density * colors[ind + 1];
00682             voltexmap[caddr + 2] += density * colors[ind + 2];
00683           }
00684         }
00685       }
00686     }
00687   } else {
00688     // compute density map only
00689     for (i=0; i<natoms; i++) {
00690       if (verbose && ((i & 0x3fff) == 0)) {
00691         printf("."); 
00692         fflush(stdout);
00693       }
00694 
00695       ptrdiff_t ind = i*4L;
00696       float scaledrad = xyzr[ind+3] * radscale;
00697 
00698       // MDFF atomic number weighted density factor
00699       float atomicnumfactor = 1.0f;
00700       if (atomicnum != NULL) {
00701         atomicnumfactor = atomicnum[i];
00702       }
00703 
00704       // negate, precompute reciprocal, and change to base 2 from the outset
00705       float arinv = -(1.0f/(2.0f*scaledrad*scaledrad)) * MLOG2EF;
00706       float radlim = gausslim * scaledrad;
00707       float radlim2 = radlim * radlim; // cutoff test done in cartesian coords
00708       radlim *= invgridspacing;
00709 
00710 #if VMDQSURFUSESSE && defined(__SSE2__)
00711       __m128 atomicnumfactor_4 = { 0 };
00712       __m128 arinv_4;
00713       if (usesse) {
00714         atomicnumfactor_4 = _mm_set1_ps(atomicnumfactor);
00715 #if VMDUSESVMLEXP
00716         // Use of Intel's SVML requires changing the pre-scaling factor
00717         arinv_4 = _mm_set1_ps(arinv * (2.718281828f/2.0f) / MLOG2EF); 
00718 #else
00719         // Use our fully inlined exp approximation
00720         arinv_4 = _mm_set1_ps(arinv);
00721 #endif
00722       }
00723 #endif
00724 
00725 #if VMDQSURFUSEVSX && defined(__VEC__)
00726       vector float atomicnumfactor_4;
00727       vector float arinv_4;
00728       if (usevsx) {
00729         atomicnumfactor_4 = vec_splats(atomicnumfactor);
00730 
00731         // Use our fully inlined exp approximation
00732         arinv_4 = vec_splats(arinv);
00733       }
00734 #endif
00735 
00736       float tmp;
00737       tmp = xyzr[ind  ] * invgridspacing;
00738       int xmin = MAX((int) (tmp - radlim), 0);
00739       int xmax = MIN((int) (tmp + radlim), maxvoxel[0]);
00740       tmp = xyzr[ind+1] * invgridspacing;
00741       int ymin = MAX((int) (tmp - radlim), 0);
00742       int ymax = MIN((int) (tmp + radlim), maxvoxel[1]);
00743       tmp = xyzr[ind+2] * invgridspacing;
00744       int zmin = MAX((int) (tmp - radlim), 0);
00745       int zmax = MIN((int) (tmp + radlim), maxvoxel[2]);
00746 
00747       float dz = zmin*gridspacing - xyzr[ind+2];
00748       for (z=zmin; z<=zmax; z++,dz+=gridspacing) {
00749         float dy = ymin*gridspacing - xyzr[ind+1];
00750         for (y=ymin; y<=ymax; y++,dy+=gridspacing) {
00751           float dy2dz2 = dy*dy + dz*dz;
00752 
00753           // early-exit when outside the cutoff radius in the Y-Z plane
00754           if (dy2dz2 >= radlim2) 
00755             continue;
00756 
00757           ptrdiff_t addr = ptrdiff_t(z * numvoxels[0]) * ptrdiff_t(numvoxels[1]) + ptrdiff_t(y * numvoxels[0]);
00758           float dx = xmin*gridspacing - xyzr[ind];
00759           x=xmin;
00760 
00761 #if VMDQSURFUSESSE && defined(__SSE2__)
00762           // Use SSE when we have a multiple-of-4 to compute
00763           // finish all remaining density map points with regular non-SSE loop
00764           if (usesse) {
00765             __align(16) SSEreg n;
00766             __align(16) SSEreg y;
00767             __m128 dy2dz2_4 = _mm_set1_ps(dy2dz2);
00768             __m128 dx_4 = _mm_add_ps(_mm_set1_ps(dx), _mm_load_ps(&sxdelta4[0]));
00769 
00770             for (; (x+3)<=xmax; x+=4,dx_4=_mm_add_ps(dx_4, gridspacing4_4)) {
00771               __m128 r2 = _mm_add_ps(_mm_mul_ps(dx_4, dx_4), dy2dz2_4);
00772               __m128 d;
00773 #if VMDUSESVMLEXP
00774               // use Intel's SVML exp2() routine
00775               y.f = _mm_exp2_ps(_mm_mul_ps(r2, arinv_4));
00776 #else
00777               // use our (much faster) fully inlined exponential approximation
00778               y.f = _mm_mul_ps(r2, arinv_4);         /* already negated and in base 2 */
00779               n.i = _mm_cvttps_epi32(y.f);
00780               d = _mm_cvtepi32_ps(n.i);
00781               d = _mm_sub_ps(d, y.f);
00782 
00783               // Approximate 2^{-d}, 0 <= d < 1, by interpolation.
00784               // Perform Horner's method to evaluate interpolating polynomial.
00785 #if 0
00786               // SSE 4.x FMADD instructions are not universally available
00787               y.f = _mm_fmadd_ps(d, _mm_set1_ps(SCEXP4), _mm_set1_ps(SCEXP3)); 
00788               y.f = _mm_fmadd_ps(y.f, d, _mm_set1_ps(SCEXP2));
00789               y.f = _mm_fmadd_ps(y.f, d, _mm_set1_ps(SCEXP1));
00790               y.f = _mm_fmadd_ps(y.f, d, _mm_set1_ps(SCEXP0));
00791 #else
00792               y.f = _mm_mul_ps(d, _mm_set_ps1(SCEXP4));      /* for x^4 term */
00793               y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP3));    /* for x^3 term */
00794               y.f = _mm_mul_ps(y.f, d);
00795               y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP2));    /* for x^2 term */
00796               y.f = _mm_mul_ps(y.f, d);
00797               y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP1));    /* for x^1 term */
00798               y.f = _mm_mul_ps(y.f, d);
00799               y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP0));    /* for x^0 term */
00800 #endif
00801 
00802               // Calculate 2^N exactly by directly manipulating floating point exponent,
00803               // then use it to scale y for the final result.
00804               n.i = _mm_sub_epi32(_mm_set1_epi32(EXPOBIAS), n.i);
00805               n.i = _mm_slli_epi32(n.i, EXPOSHIFT);
00806               y.f = _mm_mul_ps(y.f, n.f);
00807               y.f = _mm_mul_ps(y.f, atomicnumfactor_4); // MDFF density maps
00808 #endif
00809 
00810               // At present, we do unaligned loads/stores since we can't guarantee
00811               // that the X-dimension is always a multiple of 4.
00812               float *ufptr = &densitymap[addr + x];
00813               d = _mm_loadu_ps(ufptr); 
00814               _mm_storeu_ps(ufptr, _mm_add_ps(d, y.f)); 
00815             }
00816           }
00817 #endif
00818 
00819 
00820 #if VMDQSURFUSEVSX && defined(__VEC__)
00821           // Use VSX when we have a multiple-of-4 to compute
00822           // finish all remaining density map points with regular non-VSX loop
00823           //
00824           // XXX it may be useful to compare the speed/accuracy of the
00825           // polynomial approximation vs. the hardware-provided 
00826           // exp2f() approximation: vec_expte()
00827           //
00828           if (usevsx) {
00829             vector float dy2dz2_4 = vec_splats(dy2dz2);
00830             vector float tmpvsxdelta4 = *((__vector float *) &sxdelta4[0]);
00831             vector float dx_4 = vec_add(vec_splats(dx), tmpvsxdelta4);
00832 
00833             for (; (x+3)<=xmax; x+=4,dx_4=vec_add(dx_4, gridspacing4_4)) {
00834               vector float r2 = vec_add(vec_mul(dx_4, dx_4), dy2dz2_4);
00835 
00836               // use our (much faster) fully inlined exponential approximation
00837               vector float mb = vec_mul(r2, arinv_4);   /* already negated and in base 2 */
00838               vector float mbflr = vec_floor(mb);
00839               vector float d = vec_sub(mbflr, mb);
00840               vector float y;
00841 
00842               // Approximate 2^{-d}, 0 <= d < 1, by interpolation.
00843               // Perform Horner's method to evaluate interpolating polynomial.
00844               y = vec_madd(d, vec_splats(SCEXP4), vec_splats(SCEXP3)); // x^4
00845               y = vec_madd(y, d, vec_splats(SCEXP2)); // x^2 
00846               y = vec_madd(y, d, vec_splats(SCEXP1)); // x^1 
00847               y = vec_madd(y, d, vec_splats(SCEXP0)); // x^0 
00848 
00849               // Calculate 2^N exactly via vec_expte()
00850               // then use it to scale y for the final result.
00851               y = vec_mul(y, vec_expte(-mbflr));
00852               y = vec_mul(y, atomicnumfactor_4); // MDFF density maps
00853 
00854               // At present, we do unaligned loads/stores since we can't 
00855               // guarantee that the X-dimension is always a multiple of 4.
00856               float *ufptr = &densitymap[addr + x];
00857               d = *((__vector float *) &ufptr[0]);
00858               // XXX there must be a cleaner way to implement this
00859               // d = _mm_loadu_ps(ufptr); 
00860               // _mm_storeu_ps(ufptr, _mm_add_ps(d, y.f)); 
00861               d = vec_add(d, y);
00862 
00863               ufptr[0] = d[0];
00864               ufptr[1] = d[1];
00865               ufptr[2] = d[2];
00866               ufptr[3] = d[3];
00867             }
00868           }
00869 #endif
00870 
00871           // finish all remaining density map points with regular non-SSE loop
00872           for (; x<=xmax; x++,dx+=gridspacing) {
00873             float r2 = dx*dx + dy2dz2;
00874 
00875             // use our (much faster) fully inlined exponential approximation
00876             float mb = r2 * arinv;         /* already negated and in base 2 */
00877             int mbflr = (int) mb;          /* get int part, floor() */
00878             float d = mbflr - mb;          /* remaining exponent, -1 < d <= 0 */
00879 
00880             /* approx with linear blend of Taylor polys */
00881             float sy = SCEXP0 + d*(SCEXP1 + d*(SCEXP2 + d*(SCEXP3 + d*SCEXP4)));
00882 
00883             /* 2^(-mbflr) */
00884             flint scalfac;
00885             scalfac.n = (EXPOBIAS - mbflr) << EXPOSHIFT;  
00886 
00887             // XXX assume we are never beyond the cutoff value in this loop
00888             float density = (sy * scalfac.f);
00889 
00890             density *= atomicnumfactor; // MDFF Cryo-EM atomic number density
00891 
00892             densitymap[addr + x] += density;
00893           }
00894         }
00895       }
00896     }
00897   }
00898 }
00899 
00900 
00901 typedef struct {
00902   wkf_cpu_caps_t *cpucaps;
00903   int verbose;
00904   int natoms;
00905   float radscale;
00906   float gridspacing;
00907   float isovalue;
00908   float gausslim;
00909   const int *numvoxels;
00910   const float *xyzr; 
00911   const float *atomicnum;
00912   const float *colors;
00913   float **thrdensitymaps;
00914   float **thrvoltexmaps;
00915 } densitythrparms;
00916 
00917 
00918 static void * densitythread(void *voidparms) {
00919   wkf_tasktile_t tile;
00920   densitythrparms *parms = NULL;
00921   int threadid;
00922 
00923   wkf_threadlaunch_getid(voidparms, &threadid, NULL);
00924   wkf_threadlaunch_getdata(voidparms, (void **) &parms);
00925 
00926   while (wkf_threadlaunch_next_tile(voidparms, 16384, &tile) != WKF_SCHED_DONE) {
00927     int natoms = tile.end-tile.start;
00928     const float *atomicnum = (parms->atomicnum == NULL) ? NULL : &parms->atomicnum[tile.start]; 
00929     vmd_gaussdensity_opt(parms->cpucaps,
00930                          parms->verbose, natoms, 
00931                          &parms->xyzr[4L*tile.start],
00932                          atomicnum,
00933                          (parms->thrvoltexmaps[0]!=NULL) ? &parms->colors[4L*tile.start] : NULL,
00934                          parms->thrdensitymaps[threadid], 
00935                          parms->thrvoltexmaps[threadid], 
00936                          parms->numvoxels, 
00937                          parms->radscale, 
00938                          parms->gridspacing, 
00939                          parms->isovalue, 
00940                          parms->gausslim);
00941   }
00942 
00943   return NULL;
00944 }
00945 
00946 
00947 static void * reductionthread(void *voidparms) {
00948   wkf_tasktile_t tile;
00949   densitythrparms *parms = NULL;
00950   int threadid, numthreads;
00951 
00952   wkf_threadlaunch_getid(voidparms, &threadid, &numthreads);
00953   wkf_threadlaunch_getdata(voidparms, (void **) &parms);
00954 
00955   while (wkf_threadlaunch_next_tile(voidparms, 1, &tile) != WKF_SCHED_DONE) {
00956     // do a reduction over each of the individual density grids
00957     ptrdiff_t planesz = ptrdiff_t(parms->numvoxels[0]) * ptrdiff_t(parms->numvoxels[1]);
00958     ptrdiff_t start = ptrdiff_t(tile.start) * planesz;
00959     ptrdiff_t end   = ptrdiff_t(tile.end)   * planesz;
00960 
00961     ptrdiff_t i, x;
00962     for (x=start; x<end; x++) {
00963       float tmp = 0.0f;
00964       for (i=1; i<numthreads; i++) {
00965         tmp += parms->thrdensitymaps[i][x];
00966       }
00967       parms->thrdensitymaps[0][x] += tmp;
00968     }
00969 
00970     // do a reduction over each of the individual texture grids
00971     if (parms->thrvoltexmaps[0] != NULL) {
00972       for (x=start*3L; x<end*3L; x++) {
00973         float tmp = 0.0f;
00974         for (i=1; i<numthreads; i++) {
00975           tmp += parms->thrvoltexmaps[i][x];
00976         }
00977         parms->thrvoltexmaps[0][x] += tmp;
00978       }
00979     }
00980   }
00981 
00982   return NULL;
00983 }
00984 
00985 
00986 static int vmd_gaussdensity_threaded(wkf_cpu_caps_t *cpucaps, int verbose, 
00987                                      int natoms, const float *xyzr,
00988                                      const float *atomicnum,
00989                                      const float *colors,
00990                                      float *densitymap, float *voltexmap, 
00991                                      const int *numvoxels, 
00992                                      float radscale, float gridspacing, 
00993                                      float isovalue, float gausslim) {
00994   densitythrparms parms;
00995   memset(&parms, 0, sizeof(parms));
00996 
00997   parms.cpucaps = cpucaps;
00998   parms.verbose = verbose;
00999   parms.natoms = natoms;
01000   parms.radscale = radscale;
01001   parms.gridspacing = gridspacing;
01002   parms.isovalue = isovalue;
01003   parms.gausslim = gausslim;
01004   parms.numvoxels = numvoxels;
01005   parms.xyzr = xyzr;
01006   parms.atomicnum = atomicnum;
01007   parms.colors = colors;
01008  
01009   int physprocs = wkf_thread_numprocessors();
01010   int maxprocs = physprocs;
01011 
01012   // We can productively use only a few cores per socket due to the
01013   // limited memory bandwidth per socket. Also, hyperthreading
01014   // actually hurts performance.  These two considerations combined
01015   // with the linear increase in memory use prevent us from using large
01016   // numbers of cores with this simple approach, so if we've got more 
01017   // than 8 CPU cores, we'll iteratively cutting the core count in 
01018   // half until we're under 8 cores.
01019   while (maxprocs > 8) 
01020     maxprocs /= 2;
01021 
01022   // Limit the number of CPU cores used so we don't run the 
01023   // machine out of memory during surface computation.
01024   // Use either a dynamic or hard-coded heuristic to limit the
01025   // number of CPU threads we will spawn so that we don't run
01026   // the machine out of memory.  
01027   ptrdiff_t volsz = ptrdiff_t(numvoxels[0]) * 
01028                     ptrdiff_t(numvoxels[1]) * ptrdiff_t(numvoxels[2]);
01029   ptrdiff_t volmemsz = sizeof(float) * volsz;
01030   ptrdiff_t volmemszkb = volmemsz / 1024;
01031   ptrdiff_t volmemtexszkb = volmemszkb + ((voltexmap != NULL) ? 3L*volmemszkb : 0);
01032 
01033   // Platforms that don't have a means of determining available
01034   // physical memory will return -1, in which case we fall back to the
01035   // simple hard-coded 2GB-max-per-core heuristic.
01036   ptrdiff_t vmdcorefree = -1;
01037 
01038 #if defined(ARCH_BLUEWATERS) || defined(ARCH_CRAY_XC) || defined(ARCH_CRAY_XK) || defined(ARCH_LINUXAMD64) || defined(ARCH_SOLARIS2_64) || defined(ARCH_SOLARISX86_64) || defined(ARCH_AIX6_64) || defined(MACOSXARM64) || defined(ARCH_MACOSXX86_64) 
01039   // XXX The core-free query scheme has one weakness in that we might have a 
01040   // 32-bit version of VMD running on a 64-bit machine, where the available
01041   // physical memory may be much larger than is possible for a 
01042   // 32-bit VMD process to address.  To do this properly we must therefore
01043   // use conditional compilation safety checks here until we  have a better
01044   // way of determining this with a standardized helper routine.
01045   vmdcorefree = vmd_get_avail_physmem_mb();
01046 #endif
01047 
01048   if (vmdcorefree >= 0) {
01049     // Make sure QuickSurf uses no more than a fraction of the free memory
01050     // as an upper bound alternative to the hard-coded heuristic.
01051     // This should be highly preferable to the fixed-size heuristic
01052     // we had used in all cases previously.
01053     while ((volmemtexszkb * maxprocs) > (1024L*vmdcorefree/4)) {
01054       maxprocs /= 2;
01055     }
01056   } else {
01057     // Set a practical per-core maximum memory use limit to 2GB, for all cores
01058     while ((volmemtexszkb * maxprocs) > (2L * 1024L * 1024L))
01059       maxprocs /= 2;
01060   }
01061 
01062   if (maxprocs < 1) 
01063     maxprocs = 1;
01064 
01065   // Loop over number of physical processors and try to create 
01066   // per-thread volumetric maps for each of them.
01067   parms.thrdensitymaps = (float **) calloc(1,maxprocs * sizeof(float *));
01068   parms.thrvoltexmaps = (float **) calloc(1, maxprocs * sizeof(float *));
01069 
01070   // first thread is already ready to go
01071   parms.thrdensitymaps[0] = densitymap;
01072   parms.thrvoltexmaps[0] = voltexmap;
01073 
01074   int i;
01075   int numprocs = maxprocs; // ever the optimist
01076   for (i=1; i<maxprocs; i++) {
01077     parms.thrdensitymaps[i] = (float *) calloc(1, volmemsz);
01078     if (parms.thrdensitymaps[i] == NULL) {
01079       numprocs = i;
01080       break;
01081     }
01082     if (voltexmap != NULL) {
01083       parms.thrvoltexmaps[i] = (float *) calloc(1, 3L * volmemsz);
01084       if (parms.thrvoltexmaps[i] == NULL) {
01085         free(parms.thrdensitymaps[i]);
01086         parms.thrdensitymaps[i] = NULL;
01087         numprocs = i;
01088         break;
01089       }
01090     }
01091   }
01092 
01093   // launch independent thread calculations
01094   wkf_tasktile_t tile;
01095   tile.start = 0;
01096   tile.end = natoms;
01097   wkf_threadlaunch(numprocs, &parms, densitythread, &tile);
01098 
01099   // do a parallel reduction of the resulting density maps
01100   tile.start = 0;
01101   tile.end = numvoxels[2];
01102   wkf_threadlaunch(numprocs, &parms, reductionthread, &tile);
01103 
01104   // free work area
01105   for (i=1; i<maxprocs; i++) {
01106     if (parms.thrdensitymaps[i] != NULL)
01107       free(parms.thrdensitymaps[i]);
01108 
01109     if (parms.thrvoltexmaps[i] != NULL)
01110       free(parms.thrvoltexmaps[i]);
01111   }
01112   free(parms.thrdensitymaps);
01113   free(parms.thrvoltexmaps);
01114 
01115   return 0;
01116 }
01117 
01118 QuickSurf::QuickSurf(int forcecpuonly) {
01119   volmap = NULL;
01120   voltexmap = NULL;
01121   s.clear();
01122   isovalue = 0.5f;
01123 
01124   numvoxels[0] = 128;
01125   numvoxels[1] = 128;
01126   numvoxels[2] = 128;
01127 
01128   origin[0] = 0.0f;
01129   origin[1] = 0.0f;
01130   origin[2] = 0.0f;
01131 
01132   xaxis[0] = 1.0f;
01133   xaxis[1] = 0.0f;
01134   xaxis[2] = 0.0f;
01135 
01136   yaxis[0] = 0.0f;
01137   yaxis[1] = 1.0f;
01138   yaxis[2] = 0.0f;
01139 
01140   zaxis[0] = 0.0f;
01141   zaxis[1] = 0.0f;
01142   zaxis[2] = 1.0f;
01143    
01144   cudaqs = NULL;
01145   force_cpuonly = forcecpuonly;
01146 #if defined(VMDCUDA)
01147   if (!force_cpuonly && !getenv("VMDNOCUDA")) {
01148     cudaqs = new CUDAQuickSurf();
01149   }
01150 #endif
01151 
01152   timer = wkf_timer_create();
01153 }
01154 
01155 
01156 void QuickSurf::free_gpu_memory(void) {
01157   if (cudaqs) {
01158 #if defined(VMDCUDA)
01159     delete cudaqs; 
01160 #endif
01161     cudaqs = NULL; 
01162   }
01163 }
01164 
01165 
01166 int QuickSurf::calc_surf(AtomSel *atomSel, DrawMolecule *mol,
01167                          const float *atompos, const float *atomradii,
01168                          int quality, float radscale, float gridspacing,
01169                          float isoval, const int *colidx, const float *cmap,
01170                          VMDDisplayList *cmdList) {
01171   PROFILE_PUSH_RANGE("QuickSurf", 3);
01172 
01173   wkf_timer_start(timer);
01174   int colorperatom = (colidx != NULL && cmap != NULL);
01175   int usebeads=0;
01176 
01177   int verbose = (getenv("VMDQUICKSURFVERBOSE") != NULL);
01178 
01179   // Disable MDFF atomic number weighted densities until we implement
01180   // GUI controls for this if it turns out to be useful for more than
01181   // than just analytical usage.
01182   const float *atomicnum = NULL;
01183 
01184   // clean up any existing CPU arrays before going any further...
01185   if (voltexmap != NULL)
01186     free(voltexmap);
01187   voltexmap = NULL;
01188 
01189   ResizeArray<float> beadpos(64 + (3L * atomSel->selected) / 20);
01190   ResizeArray<float> beadradii(64 + (3L * atomSel->selected) / 20);
01191   ResizeArray<float> beadcolors(64 + (3L * atomSel->selected) / 20);
01192 
01193   if (getenv("VMDQUICKSURFBEADS")) {
01194     usebeads=1;
01195     if (verbose)
01196       printf("QuickSurf using residue beads representation...\n");
01197   }
01198 
01199   int numbeads = 0;
01200   if (usebeads) {
01201     int i, resid, numres;
01202 
01203     // draw a bead for each residue
01204     numres = mol->residueList.num();
01205     for (resid=0; resid<numres; resid++) {
01206       float com[3] = {0.0, 0.0, 0.0};
01207       const ResizeArray<int> &atoms = mol->residueList[resid]->atoms;
01208       int numatoms = atoms.num();
01209       int oncount = 0;
01210    
01211       // find COM for residue
01212       for (i=0; i<numatoms; i++) {
01213         int idx = atoms[i];
01214         if (atomSel->on[idx]) {
01215           oncount++;
01216           vec_add(com, com, atompos + 3L*idx);
01217         }
01218       }
01219 
01220       if (oncount < 1)
01221         continue; // exit if there weren't any atoms
01222 
01223       vec_scale(com, 1.0f / (float) oncount, com);
01224 
01225       // find radius of bounding sphere and save last atom index for color
01226       int atomcolorindex=0; // initialize, to please compilers
01227       float boundradsq = 0.0f;
01228       for (i=0; i<numatoms; i++) {
01229         int idx = atoms[i];
01230         if (atomSel->on[idx]) {
01231           float tmpdist[3];
01232           atomcolorindex = idx;
01233           vec_sub(tmpdist, com, atompos + 3L*idx);
01234           float distsq = dot_prod(tmpdist, tmpdist);
01235           if (distsq > boundradsq) {
01236             boundradsq = distsq;
01237           }
01238         }
01239       }
01240       beadpos.append3(&com[0]);
01241       beadradii.append(sqrtf(boundradsq) + 1.0f);
01242 
01243       if (colorperatom) {
01244         const float *cp = &cmap[colidx[atomcolorindex] * 3L];
01245         beadcolors.append3(&cp[0]);
01246       }
01247 
01248       // XXX still need to add pick points...
01249     }
01250 
01251     numbeads = beadpos.num() / 3;
01252   }
01253 
01254   // initialize class variables
01255   isovalue=isoval;
01256 
01257   // If no volumetric texture will be computed we will use the cmap
01258   // parameter to pass in the solid color to be applied to all vertices
01259   // Since QS can now also be called by MDFF, we have to check whether
01260   // display related parms are set or not before using them.
01261   if (cmap != NULL)
01262     vec_copy(solidcolor, cmap);
01263 
01264   // compute min/max atom radius, build list of selected atom radii,
01265   // and compute bounding box for the selected atoms
01266   float minx, miny, minz, maxx, maxy, maxz;
01267   float minrad, maxrad;
01268   int i;
01269   if (usebeads) {
01270     minx = maxx = beadpos[0];
01271     miny = maxy = beadpos[1];
01272     minz = maxz = beadpos[2];
01273     minrad = maxrad = beadradii[0];
01274     for (i=0; i<numbeads; i++) {
01275       ptrdiff_t ind = i * 3L;
01276       float tmpx = beadpos[ind  ];
01277       float tmpy = beadpos[ind+1];
01278       float tmpz = beadpos[ind+2];
01279 
01280       minx = (tmpx < minx) ? tmpx : minx;
01281       maxx = (tmpx > maxx) ? tmpx : maxx;
01282 
01283       miny = (tmpy < miny) ? tmpy : miny;
01284       maxy = (tmpy > maxy) ? tmpy : maxy;
01285 
01286       minz = (tmpz < minz) ? tmpz : minz;
01287       maxz = (tmpz > maxz) ? tmpz : maxz;
01288  
01289       // we always have to compute the rmin/rmax for beads
01290       // since these radii are defined on-the-fly
01291       float r = beadradii[i];
01292       minrad = (r < minrad) ? r : minrad;
01293       maxrad = (r > maxrad) ? r : maxrad;
01294     }
01295   } else {
01296     minx = maxx = atompos[atomSel->firstsel*3L  ];
01297     miny = maxy = atompos[atomSel->firstsel*3L+1];
01298     minz = maxz = atompos[atomSel->firstsel*3L+2];
01299 
01300     // Query min/max atom radii for the entire molecule
01301     mol->get_radii_minmax(minrad, maxrad);
01302 
01303     // We only compute rmin/rmax for the actual group of selected atoms if 
01304     // (rmax/rmin > 2.5) for the whole molecule, otherwise it's a small 
01305     // enough range that we don't care since it won't hurt our performance. 
01306     if (minrad <= 0.001 || maxrad/minrad > 2.5) {
01307       minrad = maxrad = atomradii[atomSel->firstsel];
01308       for (i=atomSel->firstsel; i<=atomSel->lastsel; i++) {
01309         if (atomSel->on[i]) {
01310           ptrdiff_t ind = i * 3L;
01311           float tmpx = atompos[ind  ];
01312           float tmpy = atompos[ind+1];
01313           float tmpz = atompos[ind+2];
01314 
01315           minx = (tmpx < minx) ? tmpx : minx;
01316           maxx = (tmpx > maxx) ? tmpx : maxx;
01317 
01318           miny = (tmpy < miny) ? tmpy : miny;
01319           maxy = (tmpy > maxy) ? tmpy : maxy;
01320 
01321           minz = (tmpz < minz) ? tmpz : minz;
01322           maxz = (tmpz > maxz) ? tmpz : maxz;
01323   
01324           float r = atomradii[i];
01325           minrad = (r < minrad) ? r : minrad;
01326           maxrad = (r > maxrad) ? r : maxrad;
01327         }
01328       }
01329     } else {
01330 #if 1
01331       float fmin[3], fmax[3];
01332       minmax_selected_3fv_aligned(atompos, atomSel->on, atomSel->num_atoms,
01333                                   atomSel->firstsel, atomSel->lastsel,
01334                                   fmin, fmax);
01335       minx = fmin[0];
01336       miny = fmin[1];
01337       minz = fmin[2];
01338 
01339       maxx = fmax[0]; 
01340       maxy = fmax[1]; 
01341       maxz = fmax[2]; 
01342 #else
01343       for (i=atomSel->firstsel; i<=atomSel->lastsel; i++) {
01344         if (atomSel->on[i]) {
01345           ptrdiff_t ind = i * 3L;
01346           float tmpx = atompos[ind  ];
01347           float tmpy = atompos[ind+1];
01348           float tmpz = atompos[ind+2];
01349 
01350           minx = (tmpx < minx) ? tmpx : minx;
01351           maxx = (tmpx > maxx) ? tmpx : maxx;
01352 
01353           miny = (tmpy < miny) ? tmpy : miny;
01354           maxy = (tmpy > maxy) ? tmpy : maxy;
01355 
01356           minz = (tmpz < minz) ? tmpz : minz;
01357           maxz = (tmpz > maxz) ? tmpz : maxz;
01358         }
01359       }
01360 #endif
01361     }
01362   }
01363 
01364   float mincoord[3], maxcoord[3];
01365   mincoord[0] = minx;
01366   mincoord[1] = miny;
01367   mincoord[2] = minz;
01368   maxcoord[0] = maxx;
01369   maxcoord[1] = maxy;
01370   maxcoord[2] = maxz;
01371 
01372   // crude estimate of the grid padding we require to prevent the
01373   // resulting isosurface from being clipped
01374   float gridpadding = radscale * maxrad * 1.70f;
01375   float padrad = gridpadding;
01376   padrad = 0.65f * sqrtf(4.0f/3.0f*((float) VMD_PI)*padrad*padrad*padrad);
01377   gridpadding = MAX(gridpadding, padrad);
01378 
01379   // Handle coarse-grained structures and whole-cell models
01380   // XXX The switch at 4.0A from an assumed all-atom scale structure to 
01381   //     CG or cell models is a simple heuristic at a somewhat arbitrary 
01382   //     threshold value.  
01383   //     For all-atom models the units shown in the GUI are in Angstroms
01384   //     and are absolute, but for CG or cell models the units in the GUI 
01385   //     are relative to the atom with the minimum radius.
01386   //     This code doesn't do anything to handle structures with a minrad 
01387   //     of zero, where perhaps only one particle has an unset radius.
01388   if (minrad > 4.0f) {
01389     gridspacing *= minrad;
01390   }
01391 
01392   if (verbose) {
01393     printf("QuickSurf: R*%.1f, I=%.1f, H=%.1f Pad: %.1f minR: %.1f maxR: %.1f)\n",
01394            radscale, isovalue, gridspacing, gridpadding, minrad, maxrad);
01395   }
01396 
01397   mincoord[0] -= gridpadding;
01398   mincoord[1] -= gridpadding;
01399   mincoord[2] -= gridpadding;
01400   maxcoord[0] += gridpadding;
01401   maxcoord[1] += gridpadding;
01402   maxcoord[2] += gridpadding;
01403 
01404   // compute the real grid dimensions from the selected atoms
01405   numvoxels[0] = (int) ceil((maxcoord[0]-mincoord[0]) / gridspacing);
01406   numvoxels[1] = (int) ceil((maxcoord[1]-mincoord[1]) / gridspacing);
01407   numvoxels[2] = (int) ceil((maxcoord[2]-mincoord[2]) / gridspacing);
01408 
01409   // recalc the grid dimensions from rounded/padded voxel counts
01410   xaxis[0] = (numvoxels[0]-1) * gridspacing;
01411   yaxis[1] = (numvoxels[1]-1) * gridspacing;
01412   zaxis[2] = (numvoxels[2]-1) * gridspacing;
01413   maxcoord[0] = mincoord[0] + xaxis[0];
01414   maxcoord[1] = mincoord[1] + yaxis[1];
01415   maxcoord[2] = mincoord[2] + zaxis[2];
01416 
01417   int boundserr=0;
01418   for (i=0; i<3; i++) {
01419     if (isnan(mincoord[i]) || isnan(maxcoord[i]) || (numvoxels[i] < 1))
01420       boundserr = 1;
01421   }
01422 
01423   if (boundserr)
01424     msgErr << "QuickSurf) NaN or illegal bounding box, grid size" << sendmsg;
01425 
01426   if (verbose || boundserr) {
01427     printf("  GridSZ: (%4d %4d %4d)  BBox: (%.1f %.1f %.1f)->(%.1f %.1f %.1f)\n",
01428            numvoxels[0], numvoxels[1], numvoxels[2],
01429            mincoord[0], mincoord[1], mincoord[2],
01430            maxcoord[0], maxcoord[1], maxcoord[2]);
01431   }
01432 
01433   if (boundserr)
01434     return -1; 
01435 
01436   vec_copy(origin, mincoord);
01437 
01438   // build compacted lists of bead coordinates, radii, and colors
01439   float *xyzr = NULL;
01440   float *colors = NULL;
01441   if (usebeads) { 
01442     int ind =0;
01443     int ind4=0; 
01444     xyzr = (float *) malloc(numbeads * sizeof(float) * 4L);
01445     if (colorperatom) {
01446       colors = (float *) malloc(numbeads * sizeof(float) * 4L);
01447 
01448       // build compacted lists of bead coordinates, radii, and colors
01449       for (i=0; i<numbeads; i++) {
01450         const float *fp = &beadpos[0] + ind;
01451         xyzr[ind4    ] = fp[0]-origin[0];
01452         xyzr[ind4 + 1] = fp[1]-origin[1];
01453         xyzr[ind4 + 2] = fp[2]-origin[2];
01454         xyzr[ind4 + 3] = beadradii[i];
01455  
01456         const float *cp = &beadcolors[0] + ind;
01457         colors[ind4    ] = cp[0];
01458         colors[ind4 + 1] = cp[1];
01459         colors[ind4 + 2] = cp[2];
01460         colors[ind4 + 3] = 1.0f;
01461         ind4 += 4;
01462         ind += 3;
01463       }
01464     } else {
01465       // build compacted lists of bead coordinates and radii only
01466       for (i=0; i<numbeads; i++) {
01467         const float *fp = &beadpos[0] + ind;
01468         xyzr[ind4    ] = fp[0]-origin[0];
01469         xyzr[ind4 + 1] = fp[1]-origin[1];
01470         xyzr[ind4 + 2] = fp[2]-origin[2];
01471         xyzr[ind4 + 3] = beadradii[i];
01472         ind4 += 4;
01473         ind += 3;
01474       }
01475     }
01476   } else {
01477     ptrdiff_t ind = atomSel->firstsel * 3L;
01478     ptrdiff_t ind4=0; 
01479     xyzr = (float *) malloc(atomSel->selected * sizeof(float) * 4L);
01480     if (colorperatom) {
01481       colors = (float *) malloc(atomSel->selected * sizeof(float) * 4L);
01482 
01483       // build compacted lists of atom coordinates, radii, and colors
01484       for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) {
01485         if (atomSel->on[i]) {
01486           const float *fp = atompos + ind;
01487           xyzr[ind4    ] = fp[0]-origin[0];
01488           xyzr[ind4 + 1] = fp[1]-origin[1];
01489           xyzr[ind4 + 2] = fp[2]-origin[2];
01490           xyzr[ind4 + 3] = atomradii[i];
01491  
01492           const float *cp = &cmap[colidx[i] * 3L];
01493           colors[ind4    ] = cp[0];
01494           colors[ind4 + 1] = cp[1];
01495           colors[ind4 + 2] = cp[2];
01496           colors[ind4 + 3] = 1.0f;
01497           ind4 += 4;
01498         }
01499         ind += 3;
01500       }
01501     } else {
01502       // build compacted lists of atom coordinates and radii only
01503       for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) {
01504         if (atomSel->on[i]) {
01505           const float *fp = atompos + ind;
01506           xyzr[ind4    ] = fp[0]-origin[0];
01507           xyzr[ind4 + 1] = fp[1]-origin[1];
01508           xyzr[ind4 + 2] = fp[2]-origin[2];
01509           xyzr[ind4 + 3] = atomradii[i];
01510           ind4 += 4;
01511         }
01512         ind += 3;
01513       }
01514     }
01515   }
01516 
01517   // set gaussian window size based on user-specified quality parameter
01518   float gausslim = 2.0f;
01519   switch (quality) {
01520     case 3: gausslim = 4.0f; break; // max quality
01521 
01522     case 2: gausslim = 3.0f; break; // high quality
01523 
01524     case 1: gausslim = 2.5f; break; // medium quality
01525 
01526     case 0: 
01527     default: gausslim = 2.0f; // low quality
01528       break;
01529   }
01530 
01531   pretime = wkf_timer_timenow(timer);
01532 
01533 #if defined(VMDCUDA)
01534   if (!force_cpuonly && !getenv("VMDNOCUDA")) {
01535     // allocate a new CUDAQuickSurf object if we destroyed the old one...
01536     if (cudaqs == NULL)
01537       cudaqs = new CUDAQuickSurf();
01538 
01539     // Assign texture format according to quality
01540     CUDAQuickSurf::VolTexFormat voltexformat = CUDAQuickSurf::RGB3F;
01541     switch (quality) {
01542       case 3: voltexformat = CUDAQuickSurf::RGB3F; break; // max quality
01543 
01544       case 2: voltexformat = CUDAQuickSurf::RGB3F; break; // high quality
01545 
01546       case 1: voltexformat = CUDAQuickSurf::RGB3F; break; // medium quality
01547 
01548       case 0: 
01549       default: voltexformat = CUDAQuickSurf::RGB4U; // low quality
01550         break;
01551     }
01552 
01553     // compute both density map and floating point color texture map
01554     int pcount = (usebeads) ? numbeads : atomSel->selected; 
01555     int rc = cudaqs->calc_surf(pcount, &xyzr[0],
01556                                (colorperatom) ? &colors[0] : &cmap[0],
01557                                colorperatom, voltexformat, 
01558                                origin, numvoxels, maxrad,
01559                                radscale, gridspacing, isovalue, gausslim,
01560                                cmdList);
01561 
01562     // If we're running in a memory-limited scenario, we can force
01563     // VMD to dump the QuickSurf GPU data to prevent out-of-memory
01564     // problems later on, either during other surface calcs or when 
01565     // using the GPU for things like OptiX ray tracing
01566     if (getenv("VMDQUICKSURFMINMEM")) {
01567       free_gpu_memory();
01568     }
01569 
01570     if (rc == 0) {
01571       free(xyzr);
01572       if (colors)
01573         free(colors);
01574   
01575       voltime = wkf_timer_timenow(timer);
01576 
01577       PROFILE_POP_RANGE(); // first return point
01578 
01579       return 0;
01580     }
01581   }
01582 #endif
01583 
01584   if (verbose) {
01585     printf("  Computing density map grid on CPUs ");
01586   }
01587 
01588   ptrdiff_t volsz = numvoxels[0] * numvoxels[1] * numvoxels[2];
01589   volmap = new float[volsz];
01590   if (colidx != NULL && cmap != NULL) {
01591     voltexmap = (float*) calloc(1, 3L * sizeof(float) * numvoxels[0] * numvoxels[1] * numvoxels[2]);
01592   }
01593 
01594   fflush(stdout);
01595   memset(volmap, 0, sizeof(float) * volsz);
01596   if ((volsz * atomSel->selected) > 20000000) {
01597     vmd_gaussdensity_threaded(mol->app->cpucaps, verbose, 
01598                               atomSel->selected, &xyzr[0], atomicnum,
01599                               (voltexmap!=NULL) ? &colors[0] : NULL,
01600                               volmap, voltexmap, numvoxels, radscale, 
01601                               gridspacing, isovalue, gausslim);
01602   } else {
01603     vmd_gaussdensity_opt(mol->app->cpucaps, verbose, 
01604                          atomSel->selected, &xyzr[0], atomicnum,
01605                          (voltexmap!=NULL) ? &colors[0] : NULL,
01606                          volmap, voltexmap, numvoxels, radscale, 
01607                          gridspacing, isovalue, gausslim);
01608   }
01609 
01610   free(xyzr);
01611   if (colors)
01612     free(colors);
01613 
01614   voltime = wkf_timer_timenow(timer);
01615 
01616   // draw the surface if the caller provided the display list
01617   if (cmdList != NULL) {
01618     draw_trimesh(cmdList);
01619   }
01620 
01621   if (verbose) {
01622     printf(" Done.\n");
01623   }
01624 
01625   PROFILE_POP_RANGE(); // second return point
01626 
01627   return 0;
01628 }
01629 
01630 
01631 // compute synthetic density map, but nothing else
01632 VolumetricData * QuickSurf::calc_density_map(AtomSel * atomSel, 
01633                                              DrawMolecule *mymol,  
01634                                              const float *atompos, 
01635                                              const float *atomradii,
01636                                              int quality, float radscale, 
01637                                              float gridspacing) {
01638   if (!calc_surf(atomSel, mymol, atompos, atomradii, quality, 
01639                  radscale, gridspacing, 1.0f, NULL, NULL, NULL)) {
01640     VolumetricData *surfvol;
01641     surfvol = new VolumetricData("density map", origin, xaxis, yaxis, zaxis,
01642                                  numvoxels[0], numvoxels[1], numvoxels[2],
01643                                  volmap);
01644     return surfvol;
01645   }
01646 
01647   return NULL;
01648 }
01649 
01650 
01651 // Extract the isosurface from the QuickSurf density map
01652 int QuickSurf::get_trimesh(int &numverts, 
01653                            float *&v3fv, float *&n3fv, float *&c3fv, 
01654                            int &numfacets, int *&fiv) {
01655 
01656   int verbose = (getenv("VMDQUICKSURFVERBOSE") != NULL);
01657 
01658   if (verbose)
01659     printf("Running marching cubes on CPU...\n");
01660 
01661   VolumetricData *surfvol; 
01662   surfvol = new VolumetricData("molecular surface",
01663                                origin, xaxis, yaxis, zaxis,
01664                                numvoxels[0], numvoxels[1], numvoxels[2],
01665                                volmap);
01666 
01667   // XXX we should calculate the volume gradient only for those
01668   //     vertices we extract, since for this rep any changes to settings
01669   //     will require recomputation of the entire volume
01670   surfvol->compute_volume_gradient(); // calc gradients: smooth vertex normals
01671   gradtime = wkf_timer_timenow(timer);
01672 
01673   // trimesh polygonalized surface, max of 6 triangles per voxel
01674   const int stepsize = 1;
01675   s.clear();                              // initialize isosurface data
01676   s.compute(surfvol, isovalue, stepsize); // compute the isosurface
01677 
01678   mctime = wkf_timer_timenow(timer);
01679 
01680   s.vertexfusion(9, 9);                   // eliminate duplicated vertices
01681   s.normalize();                          // normalize interpolated gradient/surface normals
01682 
01683   if (s.numtriangles > 0) {
01684     if (voltexmap != NULL) {
01685       // assign per-vertex colors by a 3-D texture map
01686       s.set_color_voltex_rgb3fv(voltexmap);
01687     } else {
01688       // use a single color for the entire mesh
01689       s.set_color_rgb3fv(solidcolor);
01690     }
01691   }
01692 
01693   numverts = s.v.num() / 3;
01694   v3fv=&s.v[0];
01695   n3fv=&s.n[0];
01696   c3fv=&s.c[0];
01697 
01698   numfacets = s.numtriangles;
01699   fiv=&s.f[0];
01700 
01701   delete surfvol;
01702 
01703   mcverttime = wkf_timer_timenow(timer);
01704   reptime = mcverttime;
01705 
01706   if (verbose) {
01707     char strmsg[1024];
01708     sprintf(strmsg, "QuickSurf: %.3f [pre:%.3f vol:%.3f gr:%.3f mc:%.2f mcv:%.3f]",
01709             reptime, pretime, voltime-pretime, gradtime-voltime, 
01710             mctime-gradtime, mcverttime-mctime);
01711 
01712     msgInfo << strmsg << sendmsg;
01713   }
01714  
01715   return 0;
01716 }
01717 
01718 
01719 int QuickSurf::draw_trimesh(VMDDisplayList *cmdList) {
01720   DispCmdTriMesh cmdTriMesh;
01721 
01722   int numverts=0;
01723   float *v=NULL, *n=NULL, *c=NULL;
01724   int numfacets=0;
01725   int *f=NULL;
01726 
01727   get_trimesh(numverts, v, n, c, numfacets, f);
01728 
01729   // Create a triangle mesh
01730   if (numfacets > 0) {
01731     cmdTriMesh.putdata(v, n, c, numverts, f, numfacets, 0, cmdList);
01732   }
01733  
01734   return 0;
01735 }
01736 
01737 
01738 QuickSurf::~QuickSurf() {
01739 #if defined(VMDCUDA)
01740   free_gpu_memory();
01741 #endif
01742 
01743   if (voltexmap != NULL)
01744     free(voltexmap);
01745   voltexmap = NULL;
01746 
01747   wkf_timer_destroy(timer);
01748 }
01749 
01750 

Generated on Wed Oct 9 02:43:29 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002