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

Orbital.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  * RCS INFORMATION:
00010  *
00011  *      $RCSfile: Orbital.C,v $
00012  *      $Author: johns $        $Locker:  $             $State: Exp $
00013  *      $Revision: 1.166 $      $Date: 2022/05/23 19:10:01 $
00014  *
00015  ***************************************************************************/
00021 // Intel x86 hardware 
00022 #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64))
00023 #if !defined(__SSE2__) && defined(_WIN64)
00024 #define __SSE2__ 1      /* MSVC fails to define SSE macros */
00025 #endif
00026 #if defined(__SSE2__)
00027 #include <emmintrin.h>
00028 #define VMDORBUSESSE 1  /* build SSE code for static launch */
00029 #endif
00030 
00031 #if defined(VMDCPUDISPATCH)
00032 #define VECPADSZ    16  /* max vec size is for x86 AVX512F or AVX512ER */
00033 #else
00034 #define VECPADSZ     4  /* fall-back to x86 SSE vector size */
00035 #endif
00036 
00037 // IBM Power 8/9/10 Altivec/VSX instructions:
00038 //   https://www.nxp.com/docs/en/reference-manual/ALTIVECPEM.pdf
00039 #elif defined(__VSX__)
00040 #if defined(__GNUC__) && defined(__VEC__)
00041 #include <altivec.h>
00042 #endif
00043 // The OpenPOWER VSX code path runs on POWER8 and later hardware, but is
00044 // untested on older platforms that support VSX instructions.
00045 // XXX GCC 4.8.5 breaks with conflicts between vec_xxx() routines 
00046 //     defined in utilities.h vs. VSX intrinsics in altivec.h and similar.
00047 //     For now, we disable VSX for GCC for this source file.
00048 #define VECPADSZ     4  /* IBM POWER VSX vector size */
00049 #define VMDORBUSEVSX 1  /* build POWER VSX code for static launch */
00050 
00051 // ARM64 SVE... 
00052 #elif defined(__ARM_ARCH_ISA_A64) && !defined(ARCH_MACOSXARM64)
00053 #define VMDUSESVE 1  /* build SVE code for static launch */
00054 
00055 #if defined(VMDCPUDISPATCH)
00056 #define VECPADSZ    16  /* max vec size is for Fujitsu A64fx */
00057 #else
00058 #define VECPADSZ     4  /* fall-back to NEON vector size */
00059 #endif
00060 
00061 // generic scalar C++ code
00062 #else
00063 #define VECPADSZ     1  /* scalar code, no padding */
00064 #endif
00065 
00066 // padding mask determined from hardware-specific 
00067 #define VECPADMASK (VECPADSZ - 1)
00068 
00069 // #define DEBUGORBS 1
00070 
00071 #include <math.h>
00072 #include <stdio.h>
00073 #include "VMDApp.h"
00074 #include "Orbital.h"
00075 #include "DrawMolecule.h"
00076 #include "utilities.h"
00077 #include "Inform.h"
00078 #include "WKFThreads.h"
00079 #include "WKFUtils.h"
00080 #if defined(VMDCUDA)
00081 #include "CUDAOrbital.h"
00082 #endif
00083 #if defined(VMDOPENCL)
00084 #include "OpenCLUtils.h"
00085 #include "OpenCLKernels.h"
00086 #endif
00087 #include "ProfileHooks.h"
00088 
00089 //
00090 // fctn prototypes for CPU runtime dispatch kernels
00091 //
00092 
00093 // AVX-512ER implementation for Xeon Phi w/ special fctn units
00094 extern int evaluate_grid_avx512er(int numatoms,
00095                            const float *wave_f, const float *basis_array,
00096                            const float *atompos,
00097                            const int *atom_basis,
00098                            const int *num_shells_per_atom,
00099                            const int *num_prim_per_shell,
00100                            const int *shell_types,
00101                            const int *numvoxels,
00102                            float voxelsize,
00103                            const float *origin,
00104                            int density,
00105                            float * orbitalgrid);
00106 
00107 // AVX-512F implementation for CPUs without exponent/reciprocal support
00108 extern int evaluate_grid_avx512f(int numatoms,
00109                           const float *wave_f, const float *basis_array,
00110                           const float *atompos,
00111                           const int *atom_basis,
00112                           const int *num_shells_per_atom,
00113                           const int *num_prim_per_shell,
00114                           const int *shell_types,
00115                           const int *numvoxels,
00116                           float voxelsize,
00117                           const float *origin,
00118                           int density,
00119                           float * orbitalgrid);
00120 
00121 // AVX2 implementation 
00122 extern int evaluate_grid_avx2(int numatoms,
00123                           const float *wave_f, const float *basis_array,
00124                           const float *atompos,
00125                           const int *atom_basis,
00126                           const int *num_shells_per_atom,
00127                           const int *num_prim_per_shell,
00128                           const int *shell_types,
00129                           const int *numvoxels,
00130                           float voxelsize,
00131                           const float *origin,
00132                           int density,
00133                           float * orbitalgrid);
00134 
00135 // ARM NEON implementation 
00136 extern int evaluate_grid_neon(int numatoms,
00137                           const float *wave_f, const float *basis_array,
00138                           const float *atompos,
00139                           const int *atom_basis,
00140                           const int *num_shells_per_atom,
00141                           const int *num_prim_per_shell,
00142                           const int *shell_types,
00143                           const int *numvoxels,
00144                           float voxelsize,
00145                           const float *origin,
00146                           int density,
00147                           float * orbitalgrid);
00148 
00149 // ARM SVE implementation 
00150 extern int evaluate_grid_sve(int numatoms,
00151                           const float *wave_f, const float *basis_array,
00152                           const float *atompos,
00153                           const int *atom_basis,
00154                           const int *num_shells_per_atom,
00155                           const int *num_prim_per_shell,
00156                           const int *shell_types,
00157                           const int *numvoxels,
00158                           float voxelsize,
00159                           const float *origin,
00160                           int density,
00161                           float * orbitalgrid);
00162 
00163 
00164 #define ANGS_TO_BOHR 1.88972612478289694072f
00165 
00167 Orbital::Orbital(const float *pos,
00168                  const float *wfn,
00169                  const float *barray,
00170                  const basis_atom_t *bset,
00171                  const int *types,
00172                  const int *asort,
00173                  const int *abasis,
00174                  const float **norm,
00175                  const int *nshells,
00176                  const int *nprimshell,
00177                  const int *shelltypes, 
00178                  int natoms, int ntypes, int numwave, int numbasis, 
00179                  int orbid) :
00180   numatoms(natoms), atompos(pos),
00181   num_wave_f(numwave),
00182   wave_f(NULL),
00183   num_basis_funcs(numbasis),
00184   basis_array(barray),
00185   numtypes(ntypes), 
00186   basis_set(bset),
00187   atom_types(types),
00188   atom_sort(asort),
00189   atom_basis(abasis),
00190   norm_factors(norm),
00191   num_shells_per_atom(nshells),
00192   num_prim_per_shell(nprimshell),
00193   shell_types(shelltypes), 
00194   grid_data(NULL)
00195 {
00196   origin[0] = origin[1] = origin[2] = 0.0;
00197 
00198   // Multiply wavefunction coefficients with the
00199   // angular momentum dependent part of the basis set
00200   // normalization factors.
00201   normalize_wavefunction(wfn + num_wave_f*orbid);
00202 
00203   //print_wavefunction();
00204 }
00205 
00207 Orbital::~Orbital() {
00208   if (wave_f) delete [] wave_f;
00209 }
00210 
00211 
00212 // Multiply wavefunction coefficients with the
00213 // basis set normalization factors. We do this
00214 // here rather than normalizing the basisset itself
00215 // because we need different factors for the different
00216 // cartesian components of a shell and the basis set
00217 // stores data only per shell.
00218 // By doing the multiplication here we save a lot of
00219 // flops during orbital rendering.
00220 void Orbital::normalize_wavefunction(const float *wfn) {
00221 #ifdef DEBUGORBS
00222   char shellname[8] = {'S', 'P', 'D', 'F', 'G', 'H', 'I', 'K'};
00223 #endif
00224   int i, j, k;
00225   // Get size of the symmetry-expanded wavefunction array
00226 //   int wave_size = 0;
00227 //   for (i=0; i<numatoms; i++) {
00228 //     printf("atom[%d]: type = %d\n", i, atom_types[i]);
00229 //     const basis_atom_t *basis_atom = &basis_set[atom_types[i]];
00230 //     for (j=0; j<basis_atom->numshells; j++) {
00231 //       wave_size += basis_atom->shell[j].num_cart_func;
00232 //     }
00233 //   }
00234 //   printf("num_wave_f/wave_size = %d/%d\n", num_wave_f,  wave_size);
00235 
00236   wave_f = new float[num_wave_f];
00237   int ifunc = 0;
00238   for (i=0; i<numatoms; i++) {
00239     const basis_atom_t *basis_atom = &basis_set[atom_types[i]];
00240     for (j=0; j<basis_atom->numshells; j++) {
00241       int stype = basis_atom->shell[j].type;
00242 #ifdef DEBUGORBS
00243       printf("atom %i/%i,  %i/%i %c-shell\n", i+1, numatoms, j+1, basis_atom->numshells, shellname[stype]);
00244 #endif
00245       for (k=0; k<basis_atom->shell[j].num_cart_func; k++) {
00246         wave_f[ifunc] = wfn[ifunc] * norm_factors[stype][k];
00247 
00248 #ifdef DEBUGORBS
00249         printf("%3i %c %2i wave_f[%3i]=% f  norm=%.3f  normwave=% f\n",
00250                i, shellname[stype], k, ifunc, wfn[ifunc],
00251                norm_factors[stype][k], wave_f[ifunc]);
00252 #endif
00253         ifunc++;
00254       }
00255     }
00256   }
00257 }
00258 
00259 
00260 // Sets the grid dimensions to the bounding box of the given
00261 // set of atoms *pos including a padding in all dimensions.
00262 // The resulting grid dimensions will be rounded to a multiple
00263 // of the voxel size.
00264 int Orbital::set_grid_to_bbox(const float *pos, float padding,
00265                               float resolution) {
00266   int i = 0;
00267   float xyzdim[3];
00268 
00269   /* set initial values of temp values to the coordinates
00270    * of the first atom.  */
00271   origin[0] = xyzdim[0] = pos[0];
00272   origin[1] = xyzdim[1] = pos[1];
00273   origin[2] = xyzdim[2] = pos[2];
00274 
00275   /* now loop over the rest of the atoms to check if there's
00276    * something larger/smaller for the maximum and minimum
00277    * respectively */
00278   for(i=1; i<numatoms; i++) {
00279     if (pos[3*i  ] < origin[0]) origin[0] = pos[3*i];
00280     if (pos[3*i+1] < origin[1]) origin[1] = pos[3*i+1];
00281     if (pos[3*i+2] < origin[2]) origin[2] = pos[3*i+2];
00282     if (pos[3*i  ] > xyzdim[0]) xyzdim[0] = pos[3*i];
00283     if (pos[3*i+1] > xyzdim[1]) xyzdim[1] = pos[3*i+1];
00284     if (pos[3*i+2] > xyzdim[2]) xyzdim[2] = pos[3*i+2];
00285   }
00286 
00287   // Apply padding in each direction
00288   origin[0] -= padding;
00289   origin[1] -= padding;
00290   origin[2] -= padding;
00291   gridsize[0] = xyzdim[0] + padding - origin[0];
00292   gridsize[1] = xyzdim[1] + padding - origin[1];
00293   gridsize[2] = xyzdim[2] + padding - origin[2];  
00294 
00295   set_resolution(resolution);
00296 
00297   return TRUE;
00298 }
00299 
00300 
00301 // Set the dimensions and resolution of the grid for which 
00302 // the orbital shall be computed.
00303 // The given grid dimensions will be rounded to a multiple
00304 // of the voxel size.
00305 void Orbital::set_grid(float newori[3], float newdim[3], float newvoxelsize) {
00306   origin[0] = newori[0];
00307   origin[1] = newori[1];
00308   origin[2] = newori[2];
00309   gridsize[0] = newdim[0];
00310   gridsize[1] = newdim[1];
00311   gridsize[2] = newdim[2];
00312   set_resolution(newvoxelsize);
00313 }
00314 
00315 // Change the resolution of the grid
00316 void Orbital::set_resolution(float resolution) {
00317   voxelsize = resolution;
00318   int i;
00319   for (i=0; i<3; i++) {
00320     numvoxels[i] = (int)(gridsize[i]/voxelsize) + 1;
00321     gridsize[i] = voxelsize*(numvoxels[i]-1);
00322   }
00323 }
00324 
00325 #define XNEG 0
00326 #define YNEG 1
00327 #define ZNEG 2
00328 #define XPOS 3
00329 #define YPOS 4
00330 #define ZPOS 5
00331 
00332 // Check if all values in the boundary plane given by dir 
00333 // are below threshold.
00334 // If not, jump back, decrease the stepsize and test again.
00335 int Orbital::check_plane(int dir, float threshold, int minstepsize,
00336                          int &stepsize) {
00337   bool repeat=0;
00338   int u, v, w, nu, nv;
00339   // w is the dimension we want to adjust,
00340   // u and v are the other two, i.e. the plane in which we test
00341   // the orbital values. 
00342   u = (dir+1)%3;
00343   v = (dir+2)%3;
00344   w = dir%3;
00345 
00346   // for debugging
00347   //char axis[3] = {'X', 'Y', 'Z'};
00348   //char sign[2] = {'-', '+'};
00349   //printf("%c%c: ", sign[dir/3], axis[w]);
00350 
00351   do {
00352     int success = 0;
00353     int gridstep = stepsize;
00354 
00355     if (repeat) {
00356       // We are repeating the test on the previous slate but with
00357       // twice the resolution. Hence we only have to test the new
00358       // grid points lying between the old ones.
00359       gridstep = 2*stepsize;
00360     }
00361 
00362 
00363     float grid[3];
00364     grid[w] = origin[w] + (dir/3)*(numvoxels[w]-1) * voxelsize;
00365 
00366     // Search for a value of the wave function larger than threshold.
00367     for (nu=0; nu<numvoxels[u]; nu+=gridstep) {
00368       grid[u] = origin[u] + nu * voxelsize;
00369     
00370       for (nv=0; nv<numvoxels[v]; nv+=gridstep) {
00371         grid[v] = origin[v] + nv * voxelsize;
00372       
00373         if (fabs(evaluate_grid_point(grid[0], grid[1], grid[2])) > threshold) {
00374           success = 1;
00375           break;
00376         }
00377       }
00378       if (success) break;
00379     }
00380 
00381     if (success) {
00382       // Found an orbital value higher than the threshold.
00383       // We want the threshold isosurface to be completely inside the grid.
00384       // The boundary must be between the previous and this plane.
00385       if (!(dir/3)) origin[w] -= stepsize*voxelsize;
00386       numvoxels[w] += stepsize;
00387       if (stepsize<=minstepsize) {
00388         //printf("success!\n");
00389         return 1;
00390       }
00391       stepsize /=2;
00392       repeat = 1;
00393       //printf("increase by %i, reduce stepsize to %i.\n", 2*stepsize, stepsize);
00394 
00395     } else {
00396       // All values lower than threshold, we decrease the grid size.
00397       if (!(dir/3)) origin[w] += stepsize*voxelsize;
00398       numvoxels[w] -= stepsize;
00399       //printf("decrease by %i\n", stepsize);
00400       repeat = 0;
00401       if (numvoxels[w] <= 1) {
00402         // Here we ended up with a zero grid size.
00403         // We must have missed something. Let's increase grid again and 
00404         // try a smaller step size.
00405         numvoxels[w] = stepsize; 
00406         if (!(dir/3)) origin[w] -= stepsize*voxelsize;
00407         stepsize /=2;
00408         repeat = 1;
00409         //printf("zero grid size - increase to %i, reduce stepsize to %i.\n", 2*stepsize, stepsize);
00410       }
00411     }
00412 
00413   } while (repeat);
00414 
00415   return 0;
00416 }
00417 
00418 
00419 // Optimize position and dimension of current grid so that all orbital
00420 // values higher than threshold are contained in the grid.
00421 //
00422 // Algorithm:
00423 // Based on the idea that the wave function trails off in a distance of
00424 // a few Angstroms from the molecule.
00425 // We start from the current grid size (which could be for instance the
00426 // molecular bounding box plus a padding region) and test the values on
00427 // each of the six boundary planes. If there is no value larger than the
00428 // given threshold in a plane then we shrink the system along the plane
00429 // normal. In the distance the wave function tends to be smoother so we
00430 // start the testing on a coarser grid. A parameter maxstepsize=4 means
00431 // to begin with a grid using a four times higher voxel side length than
00432 // the original grid. When we find the first value above the threshold we
00433 // jump back one step and continue with half of the previous stepsize.
00434 // When stepsize has reached minstepsize then we consider the corresponding
00435 // boundary plane optimal. Note that starting out with a too coarse
00436 // grid one might miss some features of the wave function.
00437 // If you want to be sure not to miss anything then use the voxelsize
00438 // for both minstepsize and maxstepsize.
00439 void Orbital::find_optimal_grid(float threshold, 
00440                                 int minstepsize, int maxstepsize) {
00441   int optimal[6] = {0, 0, 0, 0, 0, 0};
00442   int stepsize[6];
00443   int i;
00444   for (i=0; i<6; i++) stepsize[i] = maxstepsize;
00445 
00446 #ifdef DEBUGORBS
00447   printf("origin = {%f %f %f}\n", origin[0], origin[1], origin[2]);
00448   printf("gridsize = {%f %f %f}\n", gridsize[0], gridsize[1], gridsize[2]);
00449 #endif  
00450   
00451   
00452   // Loop until we have optimal grid boundaries in all
00453   // dimensions
00454   int iter = 0;
00455   while ( !optimal[0] || !optimal[1] || !optimal[2] ||
00456           !optimal[3] || !optimal[4] || !optimal[5] )
00457   {
00458     if (iter>100) {
00459       msgInfo << "WARNING: Could not optimize orbital grid boundaries in"
00460               << iter << "steps!" << sendmsg; 
00461       break;
00462     }
00463     iter++;
00464 
00465     // Examine the current grid boundaries and shrink if
00466     // all values are smaller than threshold    .
00467     if (!optimal[XNEG])
00468       optimal[XNEG] = check_plane(XNEG, threshold, minstepsize, stepsize[XNEG]);
00469 
00470     if (!optimal[XPOS])
00471       optimal[XPOS] = check_plane(XPOS, threshold, minstepsize, stepsize[XPOS]);
00472 
00473     if (!optimal[YNEG])
00474       optimal[YNEG] = check_plane(YNEG, threshold, minstepsize, stepsize[YNEG]);
00475 
00476     if (!optimal[YPOS])
00477       optimal[YPOS] = check_plane(YPOS, threshold, minstepsize, stepsize[YPOS]);
00478 
00479     if (!optimal[ZNEG])
00480       optimal[ZNEG] = check_plane(ZNEG, threshold, minstepsize, stepsize[ZNEG]);
00481 
00482     if (!optimal[ZPOS])
00483       optimal[ZPOS] = check_plane(ZPOS, threshold, minstepsize, stepsize[ZPOS]);
00484 
00485 #if defined(DEBUGORBS)
00486     printf("origin {%f %f %f}\n", origin[0], origin[1], origin[2]);
00487     printf("ngrid {%i %i %i}\n", numvoxels[0], numvoxels[1], numvoxels[2]);
00488     printf("stepsize {%i %i %i %i %i %i}\n", stepsize[0], stepsize[1], stepsize[2],
00489            stepsize[3], stepsize[4], stepsize[5]);
00490 #endif
00491   }
00492 
00493   
00494   gridsize[0] = numvoxels[0]*voxelsize;
00495   gridsize[1] = numvoxels[1]*voxelsize;
00496   gridsize[2] = numvoxels[2]*voxelsize;
00497 }
00498 
00499 
00500 // this function creates the orbital grid given the system dimensions
00501 int Orbital::calculate_mo(DrawMolecule *mol, int density) {
00502   PROFILE_PUSH_RANGE("Orbital", 4);
00503 
00504   wkf_timerhandle timer=wkf_timer_create();
00505   wkf_timer_start(timer);
00506 
00507   //
00508   // Force vectorized N-element padding for the X dimension to prevent
00509   // the possibility of an out-of-bounds orbital grid read/write operation
00510   //
00511   int vecpadmask = VECPADMASK;
00512   if ((mol->app->cpucaps != NULL) && (mol->app->cpucaps->flags & CPU_AVX2)) {
00513     vecpadmask = 7; // AVX2 kernels pad to multiples of 8 
00514   }
00515   if ((mol->app->cpucaps != NULL) && (mol->app->cpucaps->flags & (CPU_AVX512F | CPU_AVX512ER))) {
00516     vecpadmask = 15; // AVX512 kernels pad to multiples of 16
00517   }
00518 
00519   // pad the grid X dimension for vector-multiple length and memory alignment 
00520   numvoxels[0] = (numvoxels[0] + vecpadmask) & ~(vecpadmask);
00521   gridsize[0] = numvoxels[0]*voxelsize;
00522  
00523   // Allocate memory for the volumetric grid
00524   int numgridpoints = numvoxels[0] * numvoxels[1] * numvoxels[2];
00525   grid_data = new float[numgridpoints];
00526 
00527 #if defined(DEBUGORBS)
00528   printf("num_wave_f=%i\n", num_wave_f);
00529 
00530   int i=0;
00531   for (i=0; i<num_wave_f; i++) {
00532     printf("wave_f[%i] = %f\n", i, wave_f[i]);
00533   }
00534 
00535   // perhaps give the user a warning, since the calculation
00536   // could take a while, otherwise they might think the system is borked 
00537   printf("Calculating %ix%ix%i orbital grid.\n", 
00538          numvoxels[0], numvoxels[1], numvoxels[2]);
00539 #endif
00540 
00541 
00542   int rc=-1; // initialize to sentinel value
00543 
00544   // Calculate the value of the orbital at each gridpoint
00545 #if defined(VMDCUDA)
00546   // The CUDA kernel currently only handles up to "G" shells,
00547   // and up to 32 primitives per basis function
00548   if ((max_shell_type() <= G_SHELL) &&
00549       (max_primitives() <= 32) &&
00550       (!getenv("VMDNOCUDA"))) {
00551     rc = vmd_cuda_evaluate_orbital_grid(mol->cuda_devpool(), 
00552                                         numatoms, wave_f, num_wave_f,
00553                                         basis_array, num_basis_funcs,
00554                                         atompos, atom_basis,
00555                                         num_shells_per_atom, 
00556                                         num_prim_per_shell,
00557                                         shell_types, total_shells(),
00558                                         numvoxels, voxelsize, 
00559                                         origin, density, grid_data);
00560   }
00561 #endif
00562 #if defined(VMDOPENCL)
00563   // The OpenCL kernel currently only handles up to "G" shells,
00564   // and up to 32 primitives per basis function
00565   if (rc!=0 &&
00566       (max_shell_type() <= G_SHELL) &&
00567       (max_primitives() <= 32) &&
00568       (!getenv("VMDNOOPENCL"))) {
00569 
00570 #if 1
00571     // XXX this would be done during app startup normally...
00572     static vmd_opencl_orbital_handle *orbh = NULL;
00573     static cl_context clctx = NULL;
00574     static cl_command_queue clcmdq = NULL;
00575     static cl_device_id *cldevs = NULL;
00576     if (orbh == NULL) {
00577       printf("Attaching OpenCL device:\n");
00578       wkf_timer_start(timer);
00579       cl_int clerr = CL_SUCCESS;
00580 
00581       cl_platform_id clplatid = vmd_cl_get_platform_index(0);
00582       cl_context_properties clctxprops[] = {(cl_context_properties) CL_CONTEXT_PLATFORM, (cl_context_properties) clplatid, (cl_context_properties) 0};
00583       clctx = clCreateContextFromType(clctxprops, CL_DEVICE_TYPE_GPU, NULL, NULL, &clerr);
00584 
00585       size_t parmsz;
00586       clerr |= clGetContextInfo(clctx, CL_CONTEXT_DEVICES, 0, NULL, &parmsz);
00587       if (clerr != CL_SUCCESS) return -1;
00588       cldevs = (cl_device_id *) malloc(parmsz);
00589       if (clerr != CL_SUCCESS) return -1;
00590       clerr |= clGetContextInfo(clctx, CL_CONTEXT_DEVICES, parmsz, cldevs, NULL);
00591       if (clerr != CL_SUCCESS) return -1;
00592       clcmdq = clCreateCommandQueue(clctx, cldevs[0], 0, &clerr);
00593       if (clerr != CL_SUCCESS) return -1;
00594       wkf_timer_stop(timer);
00595       printf("  OpenCL context creation time: %.3f sec\n", wkf_timer_time(timer));
00596 
00597       wkf_timer_start(timer);
00598       orbh = vmd_opencl_create_orbital_handle(clctx, clcmdq, cldevs);
00599       wkf_timer_stop(timer);
00600       printf("  OpenCL kernel compilation time: %.3f sec\n", wkf_timer_time(timer));
00601 
00602       wkf_timer_start(timer);
00603     }
00604 #endif
00605 
00606     rc = vmd_opencl_evaluate_orbital_grid(mol->cuda_devpool(), orbh,
00607                                         numatoms, wave_f, num_wave_f,
00608                                         basis_array, num_basis_funcs,
00609                                         atompos, atom_basis,
00610                                         num_shells_per_atom, 
00611                                         num_prim_per_shell,
00612                                         shell_types, total_shells(),
00613                                         numvoxels, voxelsize, 
00614                                         origin, density, grid_data);
00615 
00616 #if 0
00617     // XXX this would normally be done at program shutdown
00618     vmd_opencl_destroy_orbital_handle(parms.orbh);
00619     clReleaseCommandQueue(clcmdq);
00620     clReleaseContext(clctx);
00621     free(cldevs);
00622 #endif
00623   }
00624 #endif
00625 #if 0
00626   int numprocs = 1;
00627   if (getenv("VMDDUMPORBITALS")) {
00628     write_orbital_data(getenv("VMDDUMPORBITALS"), numatoms,
00629                        wave_f, num_wave_f, basis_array, num_basis,
00630                        atompos, atom_basis, num_shells_per_atom,
00631                        num_prim_per_shell, shell_types,
00632                        num_shells, numvoxels, voxelsize, origin);
00633 
00634     read_calc_orbitals(devpool, getenv("VMDDUMPORBITALS"));
00635   }
00636 #endif
00637 
00638 
00639 #if !defined(VMDORBUSETHRPOOL)
00640 #if defined(VMDTHREADS)
00641   int numcputhreads = wkf_thread_numprocessors();
00642 #else
00643   int numcputhreads = 1;
00644 #endif
00645 #endif
00646   if (rc!=0)  rc = evaluate_grid_fast(mol->app->cpucaps,
00647 #if defined(VMDORBUSETHRPOOL)
00648                                       mol->cpu_threadpool(), 
00649 #else
00650                                       numcputhreads,
00651 #endif
00652                                       numatoms, wave_f, basis_array,
00653                                       atompos, atom_basis,
00654                                       num_shells_per_atom, num_prim_per_shell,
00655                                       shell_types, numvoxels, voxelsize, 
00656                                       origin, density, grid_data);
00657 
00658   if (rc!=0) {
00659     msgErr << "Error computing orbital grid" << sendmsg;
00660     delete [] grid_data;
00661     grid_data=NULL;
00662 
00663     PROFILE_POP_RANGE(); // first return point
00664 
00665     return FALSE;
00666   }
00667 
00668   wkf_timer_stop(timer);
00669 
00670 #if 1
00671   if (getenv("VMDORBTIMING") != NULL) { 
00672     double gflops = (numgridpoints * flops_per_gridpoint()) / (wkf_timer_time(timer) * 1000000000.0);
00673 
00674     char strbuf[1024];
00675     sprintf(strbuf, "Orbital calc. time %.3f secs, %.2f gridpoints/sec, %.2f GFLOPS",
00676             wkf_timer_time(timer), 
00677             (((double) numgridpoints) / wkf_timer_time(timer)),
00678             gflops);
00679     msgInfo << strbuf << sendmsg;
00680   }
00681 #endif
00682 
00683   wkf_timer_destroy(timer);
00684 
00685   PROFILE_POP_RANGE(); // second return point
00686 
00687   return TRUE;
00688 }
00689 
00690 
00691 /*********************************************************
00692  *
00693  * This function calculates the value of the wavefunction
00694  * corresponding to a particular orbital at grid point
00695  * grid_x, grid_y, grid_z.
00696 
00697 
00698  Here's an example of a basis set definition for one atom:
00699 
00700   SHELL TYPE  PRIMITIVE        EXPONENT          CONTRACTION COEFFICIENT(S)
00701 
00702  Oxygen
00703 
00704       1   S       1          5484.6716600    0.001831074430
00705       1   S       2           825.2349460    0.013950172200
00706       1   S       3           188.0469580    0.068445078098
00707       1   S       4            52.9645000    0.232714335992
00708       1   S       5            16.8975704    0.470192897984
00709       1   S       6             5.7996353    0.358520852987
00710 
00711       2   L       7            15.5396162   -0.110777549525    0.070874268231
00712       2   L       8             3.5999336   -0.148026262701    0.339752839147
00713       2   L       9             1.0137618    1.130767015354    0.727158577316
00714 
00715       3   L      10             0.2700058    1.000000000000    1.000000000000
00716 
00717  *********************************************************/
00718 float Orbital::evaluate_grid_point(float grid_x, float grid_y, float grid_z) {
00719   int at;
00720   int prim, shell;
00721 
00722   // initialize value of orbital at gridpoint
00723   float value = 0.0;
00724 
00725   // initialize the wavefunction and shell counters
00726   int ifunc = 0; 
00727   int shell_counter = 0;
00728 
00729   // loop over all the QM atoms
00730   for (at=0; at<numatoms; at++) {
00731     int maxshell = num_shells_per_atom[at];
00732     int prim_counter = atom_basis[at];
00733 
00734     // calculate distance between grid point and center of atom
00735     float xdist = (grid_x - atompos[3*at  ])*ANGS_TO_BOHR;
00736     float ydist = (grid_y - atompos[3*at+1])*ANGS_TO_BOHR;
00737     float zdist = (grid_z - atompos[3*at+2])*ANGS_TO_BOHR;
00738     float dist2 = xdist*xdist + ydist*ydist + zdist*zdist;
00739 
00740     // loop over the shells belonging to this atom
00741     // XXX this is maybe a misnomer because in split valence
00742     //     basis sets like 6-31G we have more than one basis
00743     //     function per (valence-)shell and we are actually
00744     //     looping over the individual contracted GTOs
00745     for (shell=0; shell < maxshell; shell++) {
00746       float contracted_gto = 0.0f;
00747 
00748       // Loop over the Gaussian primitives of this contracted 
00749       // basis function to build the atomic orbital
00750       int maxprim = num_prim_per_shell[shell_counter];
00751       int shelltype = shell_types[shell_counter];
00752       for (prim=0; prim < maxprim;  prim++) {
00753         float exponent       = basis_array[prim_counter    ];
00754         float contract_coeff = basis_array[prim_counter + 1];
00755         contracted_gto += contract_coeff * expf(-exponent*dist2);
00756         prim_counter += 2;
00757       }
00758 
00759       /* multiply with the appropriate wavefunction coefficient */
00760       float tmpshell=0;
00761       // Loop over the cartesian angular momenta of the shell.
00762       // avoid unnecessary branching and minimize use of pow()
00763       int i, j; 
00764       float xdp, ydp, zdp;
00765       float xdiv = 1.0f / xdist;
00766       for (j=0, zdp=1.0f; j<=shelltype; j++, zdp*=zdist) {
00767         int imax = shelltype - j; 
00768         for (i=0, ydp=1.0f, xdp=powf(xdist, float(imax)); i<=imax; i++, ydp*=ydist, xdp*=xdiv) {
00769           tmpshell += wave_f[ifunc++] * xdp * ydp * zdp;
00770         }
00771       }
00772       value += tmpshell * contracted_gto;
00773 
00774       shell_counter++;
00775     } 
00776   }
00777 
00778   /* return the final value at grid point */
00779   return value;
00780 }
00781 
00782 
00783 //
00784 // Return the max number of primitives that occur in a basis function
00785 //
00786 int Orbital::max_primitives(void) {
00787   int maxprim=-1;
00788 
00789   int shell_counter = 0;
00790   for (int at=0; at<numatoms; at++) {
00791     for (int shell=0; shell < num_shells_per_atom[at]; shell++) {
00792       int numprim = num_prim_per_shell[shell_counter];
00793       if (numprim > maxprim)
00794         maxprim = numprim; 
00795     }
00796   }
00797 
00798   return maxprim;
00799 }
00800 
00801 
00802 //
00803 // Return the maximum shell type used
00804 //
00805 int Orbital::max_shell_type(void) {
00806   int maxshell=-1;
00807 
00808   int shell_counter = 0;
00809   for (int at=0; at<numatoms; at++) {
00810     for (int shell=0; shell < num_shells_per_atom[at]; shell++) {
00811       int shelltype = shell_types[shell_counter];
00812       shell_counter++;
00813       if (shelltype > maxshell)
00814         maxshell=shelltype;
00815     }
00816   }
00817 
00818   return maxshell;
00819 }
00820 
00821 
00822 //
00823 // count the maximum number of wavefunction coefficient accesses
00824 // required for the highest shell types contained in this orbital
00825 //
00826 int Orbital::max_wave_f_count(void) {
00827   int maxcount=0;
00828 
00829   int shell_counter = 0;
00830   for (int at=0; at<numatoms; at++) {
00831     for (int shell=0; shell < num_shells_per_atom[at]; shell++) {
00832       int shelltype = shell_types[shell_counter];
00833       int i, j; 
00834       int count=0;
00835       for (i=0; i<=shelltype; i++) {
00836         int jmax = shelltype - i; 
00837         for (j=0; j<=jmax; j++) {
00838           count++;
00839         }
00840       }
00841       shell_counter++;
00842       if (count > maxcount)
00843         maxcount=count;
00844     }
00845   }
00846 
00847   return maxcount;
00848 }
00849 
00850 
00851 //
00852 // compute the FLOPS per grid point for performance measurement purposes
00853 //
00854 double Orbital::flops_per_gridpoint() {
00855   double flops=0.0;
00856 
00857   int shell_counter = 0;
00858   for (int at=0; at<numatoms; at++) {
00859     flops += 7;
00860 
00861     for (int shell=0; shell < num_shells_per_atom[at]; shell++) {
00862       for (int prim=0; prim < num_prim_per_shell[shell_counter];  prim++)
00863         flops += 4; // expf() costs far more, but we count as one.
00864 
00865       int shelltype = shell_types[shell_counter];
00866 
00867       switch (shelltype) {
00868         // separately count for the hand-optimized cases
00869         case S_SHELL: flops += 2; break;
00870         case P_SHELL: flops += 8; break;
00871         case D_SHELL: flops += 17; break;
00872         case F_SHELL: flops += 30; break;
00873         case G_SHELL: flops += 50; break;
00874 
00875         // count up for catch-all loop
00876         default:
00877           int i, j; 
00878           for (i=0; i<=shelltype; i++) {
00879             int jmax = shelltype - i; 
00880             flops += 1;
00881             for (j=0; j<=jmax; j++) {
00882               flops += 6;
00883             }
00884           }
00885           break;
00886       }
00887 
00888       shell_counter++;
00889     } 
00890   }
00891 
00892   return flops;
00893 }
00894 
00895 
00896 //
00897 // Fast single-precision expf() implementation
00898 // Adapted from the free cephes math library on Netlib
00899 //   http://www.netlib.org/cephes/
00900 //
00901 // Cephes Math Library Release 2.2:  June, 1992
00902 // Copyright 1984, 1987, 1989 by Stephen L. Moshier
00903 // Direct inquiries to 30 Frost Street, Cambridge, MA 02140
00904 //
00905 static const float MAXNUMF =    3.4028234663852885981170418348451692544e38f;
00906 static const float MAXLOGF =   88.72283905206835f;
00907 static const float MINLOGF = -103.278929903431851103f; /* log(2^-149) */
00908 static const float LOG2EF  =    1.44269504088896341f;
00909 static const float C1      =    0.693359375f;
00910 static const float C2      =   -2.12194440e-4f;
00911 
00912 static inline float cephesfastexpf(float x) {
00913   float z;
00914   int n;
00915   
00916   if(x > MAXLOGF) 
00917     return MAXNUMF;
00918 
00919   if(x < MINLOGF) 
00920     return 0.0;
00921 
00922   // Express e^x = e^g 2^n = e^g e^(n loge(2)) = e^(g + n loge(2))
00923   z = floorf( LOG2EF * x + 0.5f ); // floor() truncates toward -infinity.
00924   x -= z * C1;
00925   x -= z * C2;
00926   n = (int) z;
00927 
00928   z = x * x;
00929   // Theoretical peak relative error in [-0.5, +0.5] is 4.2e-9.
00930   z = ((((( 1.9875691500E-4f  * x + 1.3981999507E-3f) * x
00931           + 8.3334519073E-3f) * x + 4.1665795894E-2f) * x
00932           + 1.6666665459E-1f) * x + 5.0000001201E-1f) * z + x + 1.0f;
00933 
00934   x = ldexpf(z, n); // multiply by power of 2
00935   return x;
00936 }
00937 
00938 
00939 
00940 
00941 /*
00942  * David J. Hardy
00943  * 12 Dec 2008
00944  *
00945  * aexpfnx() - Approximate expf() for negative x.
00946  *
00947  * Assumes that x <= 0.
00948  *
00949  * Assumes IEEE format for single precision float, specifically:
00950  * 1 sign bit, 8 exponent bits biased by 127, and 23 mantissa bits.
00951  *
00952  * Interpolates exp() on interval (-1/log2(e), 0], then shifts it by
00953  * multiplication of a fast calculation for 2^(-N).  The interpolation
00954  * uses a linear blending of 3rd degree Taylor polynomials at the end
00955  * points, so the approximation is once differentiable.
00956  *
00957  * The error is small (max relative error per interval is calculated
00958  * to be 0.131%, with a max absolute error of -0.000716).
00959  *
00960  * The cutoff is chosen so as to speed up the computation by early
00961  * exit from function, with the value chosen to give less than the
00962  * the max absolute error.  Use of a cutoff is unnecessary, except
00963  * for needing to shift smallest floating point numbers to zero,
00964  * i.e. you could remove cutoff and replace by:
00965  *
00966  * #define MINXNZ  -88.0296919311130  // -127 * log(2)
00967  *
00968  *   if (x < MINXNZ) return 0.f;
00969  *
00970  * Use of a cutoff causes a discontinuity which can be eliminated
00971  * through the use of a switching function.
00972  *
00973  * We can obtain arbitrarily smooth approximation by taking k+1 nodes on
00974  * the interval and weighting their respective Taylor polynomials by the
00975  * kth order Lagrange interpolant through those nodes.  The wiggle in the
00976  * polynomial interpolation due to equidistant nodes (Runge's phenomenon)
00977  * can be reduced by using Chebyshev nodes.
00978  */
00979 
00980 #define MLOG2EF    -1.44269504088896f
00981 
00982 /*
00983  * Interpolating coefficients for linear blending of the
00984  * 3rd degree Taylor expansion of 2^x about 0 and -1.
00985  */
00986 #define SCEXP0     1.0000000000000000f
00987 #define SCEXP1     0.6987082824680118f
00988 #define SCEXP2     0.2633174272827404f
00989 #define SCEXP3     0.0923611991471395f
00990 #define SCEXP4     0.0277520543324108f
00991 
00992 /* for single precision float */
00993 #define EXPOBIAS   127
00994 #define EXPOSHIFT   23
00995 
00996 /* cutoff is optional, but can help avoid unnecessary work */
00997 #define ACUTOFF    -10
00998 
00999 typedef union flint_t {
01000   float f;
01001   int n;
01002 } flint;
01003 
01004 float aexpfnx(float x) {
01005   /* assume x <= 0 */
01006   float mb;
01007   int mbflr;
01008   float d;
01009   float sy;
01010   flint scalfac;
01011 
01012   if (x < ACUTOFF) return 0.f;
01013 
01014   mb = x * MLOG2EF;    /* change base to 2, mb >= 0 */
01015   mbflr = (int) mb;    /* get int part, floor() */
01016   d = mbflr - mb;      /* remaining exponent, -1 < d <= 0 */
01017   sy = SCEXP0 + d*(SCEXP1 + d*(SCEXP2 + d*(SCEXP3 + d*SCEXP4)));
01018                        /* approx with linear blend of Taylor polys */
01019   scalfac.n = (EXPOBIAS - mbflr) << EXPOSHIFT;  /* 2^(-mbflr) */
01020   return (sy * scalfac.f);  /* scaled approx */
01021 }
01022 
01023 
01024 
01025 //
01026 // Optimized molecular orbital grid evaluation code
01027 //
01028 #define S_SHELL 0
01029 #define P_SHELL 1
01030 #define D_SHELL 2
01031 #define F_SHELL 3
01032 #define G_SHELL 4
01033 #define H_SHELL 5
01034 
01035 int evaluate_grid(int numatoms,
01036                   const float *wave_f, const float *basis_array,
01037                   const float *atompos,
01038                   const int *atom_basis,
01039                   const int *num_shells_per_atom,
01040                   const int *num_prim_per_shell,
01041                   const int *shell_types,
01042                   const int *numvoxels,
01043                   float voxelsize,
01044                   const float *origin,
01045                   int density,
01046                   float * orbitalgrid) {
01047   if (!orbitalgrid)
01048     return -1;
01049 
01050   int nx, ny, nz;
01051   // Calculate the value of the orbital at each gridpoint and store in 
01052   // the current oribtalgrid array
01053   int numgridxy = numvoxels[0]*numvoxels[1];
01054   for (nz=0; nz<numvoxels[2]; nz++) {
01055     float grid_x, grid_y, grid_z;
01056     grid_z = origin[2] + nz * voxelsize;
01057     for (ny=0; ny<numvoxels[1]; ny++) {
01058       grid_y = origin[1] + ny * voxelsize;
01059       int gaddrzy = ny*numvoxels[0] + nz*numgridxy;
01060       for (nx=0; nx<numvoxels[0]; nx++) {
01061         grid_x = origin[0] + nx * voxelsize;
01062 
01063         // calculate the value of the wavefunction of the
01064         // selected orbital at the current grid point
01065         int at;
01066         int prim, shell;
01067 
01068         // initialize value of orbital at gridpoint
01069         float value = 0.0;
01070 
01071         // initialize the wavefunction and shell counters
01072         int ifunc = 0; 
01073         int shell_counter = 0;
01074 
01075         // loop over all the QM atoms
01076         for (at=0; at<numatoms; at++) {
01077           int maxshell = num_shells_per_atom[at];
01078           int prim_counter = atom_basis[at];
01079 
01080           // calculate distance between grid point and center of atom
01081           float xdist = (grid_x - atompos[3*at  ])*ANGS_TO_BOHR;
01082           float ydist = (grid_y - atompos[3*at+1])*ANGS_TO_BOHR;
01083           float zdist = (grid_z - atompos[3*at+2])*ANGS_TO_BOHR;
01084 
01085           float xdist2 = xdist*xdist;
01086           float ydist2 = ydist*ydist;
01087           float zdist2 = zdist*zdist;
01088           float xdist3 = xdist2*xdist;
01089           float ydist3 = ydist2*ydist;
01090           float zdist3 = zdist2*zdist;
01091 
01092           float dist2 = xdist2 + ydist2 + zdist2;
01093 
01094           // loop over the shells belonging to this atom
01095           // XXX this is maybe a misnomer because in split valence
01096           //     basis sets like 6-31G we have more than one basis
01097           //     function per (valence-)shell and we are actually
01098           //     looping over the individual contracted GTOs
01099           for (shell=0; shell < maxshell; shell++) {
01100             float contracted_gto = 0.0f;
01101 
01102             // Loop over the Gaussian primitives of this contracted 
01103             // basis function to build the atomic orbital
01104             // 
01105             // XXX there's a significant opportunity here for further
01106             //     speedup if we replace the entire set of primitives
01107             //     with the single gaussian that they are attempting 
01108             //     to model.  This could give us another 6x speedup in 
01109             //     some of the common/simple cases.
01110             int maxprim = num_prim_per_shell[shell_counter];
01111             int shelltype = shell_types[shell_counter];
01112             for (prim=0; prim<maxprim; prim++) {
01113               float exponent       = basis_array[prim_counter    ];
01114               float contract_coeff = basis_array[prim_counter + 1];
01115 
01116               // XXX By premultiplying the stored exponent factors etc,
01117               //     we should be able to use exp2f() rather than exp(),
01118               //     saving several FLOPS per iteration of this loop
01119 #if defined(__GNUC__) && !defined(__ICC)
01120               // Use David Hardy's fast spline approximation instead
01121               // Works well for GCC, but runs slower for Intel C.
01122               contracted_gto += contract_coeff * aexpfnx(-exponent*dist2);
01123 #elif defined(__ICC)
01124               // When compiling with ICC, we'll use an inlined 
01125               // single-precision expf() implementation based on the
01126               // cephes math library found on Netlib.  This outruns the
01127               // standard glibc expf() by over 2x in this algorithm.
01128               contracted_gto += contract_coeff * cephesfastexpf(-exponent*dist2);
01129 #else
01130               // XXX By far the most costly operation here is exp(),
01131               //     for gcc builds, exp() accounts for 90% of the runtime
01132               contracted_gto += contract_coeff * expf(-exponent*dist2);
01133 #endif
01134               prim_counter += 2;
01135             }
01136 
01137             /* multiply with the appropriate wavefunction coefficient */
01138             float tmpshell=0;
01139             switch (shelltype) {
01140               case S_SHELL:
01141                 value += wave_f[ifunc++] * contracted_gto;
01142                 break;
01143 
01144               case P_SHELL:
01145                 tmpshell += wave_f[ifunc++] * xdist;
01146                 tmpshell += wave_f[ifunc++] * ydist;
01147                 tmpshell += wave_f[ifunc++] * zdist;
01148                 value += tmpshell * contracted_gto;
01149                 break;
01150 
01151               case D_SHELL:
01152                 tmpshell += wave_f[ifunc++] * xdist2;
01153                 tmpshell += wave_f[ifunc++] * xdist * ydist;
01154                 tmpshell += wave_f[ifunc++] * ydist2;
01155                 tmpshell += wave_f[ifunc++] * xdist * zdist;
01156                 tmpshell += wave_f[ifunc++] * ydist * zdist;
01157                 tmpshell += wave_f[ifunc++] * zdist2;
01158                 value += tmpshell * contracted_gto;
01159                 break;
01160 
01161               case F_SHELL:
01162                 tmpshell += wave_f[ifunc++] * xdist3;         // xxx
01163                 tmpshell += wave_f[ifunc++] * xdist2 * ydist; // xxy
01164                 tmpshell += wave_f[ifunc++] * ydist2 * xdist; // xyy
01165                 tmpshell += wave_f[ifunc++] * ydist3;         // yyy
01166                 tmpshell += wave_f[ifunc++] * xdist2 * zdist; // xxz
01167                 tmpshell += wave_f[ifunc++] * xdist * ydist * zdist; // xyz
01168                 tmpshell += wave_f[ifunc++] * ydist2 * zdist; // yyz
01169                 tmpshell += wave_f[ifunc++] * zdist2 * xdist; // xzz
01170                 tmpshell += wave_f[ifunc++] * zdist2 * ydist; // yzz
01171                 tmpshell += wave_f[ifunc++] * zdist3;         // zzz
01172                 value += tmpshell * contracted_gto;
01173                 break;
01174  
01175               case G_SHELL:
01176                 tmpshell += wave_f[ifunc++] * xdist2 * xdist2; // xxxx
01177                 tmpshell += wave_f[ifunc++] * xdist3 * ydist;  // xxxy
01178                 tmpshell += wave_f[ifunc++] * xdist2 * ydist2; // xxyy
01179                 tmpshell += wave_f[ifunc++] * ydist3 * xdist;  // xyyy
01180                 tmpshell += wave_f[ifunc++] * ydist2 * ydist2; // yyyy
01181                 tmpshell += wave_f[ifunc++] * xdist3 * zdist;  // xxxz
01182                 tmpshell += wave_f[ifunc++] * xdist2 * ydist * zdist; // xxyz
01183                 tmpshell += wave_f[ifunc++] * ydist2 * xdist * zdist; // xyyz
01184                 tmpshell += wave_f[ifunc++] * ydist3 * zdist;  // yyyz
01185                 tmpshell += wave_f[ifunc++] * xdist2 * zdist2; // xxzz
01186                 tmpshell += wave_f[ifunc++] * zdist2 * xdist * ydist; // xyzz
01187                 tmpshell += wave_f[ifunc++] * ydist2 * zdist2; // yyzz
01188                 tmpshell += wave_f[ifunc++] * zdist3 * xdist;  // zzzx
01189                 tmpshell += wave_f[ifunc++] * zdist3 * ydist;  // zzzy
01190                 tmpshell += wave_f[ifunc++] * zdist2 * zdist2; // zzzz
01191                 value += tmpshell * contracted_gto;
01192                 break;
01193 
01194               default:
01195 #if 1
01196                 // avoid unnecessary branching and minimize use of pow()
01197                 int i, j; 
01198                 float xdp, ydp, zdp;
01199                 float xdiv = 1.0f / xdist;
01200                 for (j=0, zdp=1.0f; j<=shelltype; j++, zdp*=zdist) {
01201                   int imax = shelltype - j; 
01202                   for (i=0, ydp=1.0f, xdp=powf(xdist, float(imax)); i<=imax; i++, ydp*=ydist, xdp*=xdiv) {
01203                     tmpshell += wave_f[ifunc++] * xdp * ydp * zdp;
01204                   }
01205                 }
01206                 value += tmpshell * contracted_gto;
01207 #else
01208                 int i, j, k;
01209                 for (k=0; k<=shelltype; k++) {
01210                   for (j=0; j<=shelltype; j++) {
01211                     for (i=0; i<=shelltype; i++) {
01212                       if (i+j+k==shelltype) {
01213                         value += wave_f[ifunc++] * contracted_gto
01214                           * pow(xdist,i) * pow(ydist,j) * pow(zdist,k);
01215                       }
01216                     }
01217                   }
01218                 }
01219 #endif
01220             } // end switch
01221 
01222             shell_counter++;
01223           } // end shell
01224         } // end atom
01225 
01226         // return either orbital density or orbital wavefunction amplitude
01227         if (density) {
01228           float orbdensity = value * value;
01229           if (value < 0.0)
01230             orbdensity = -orbdensity;
01231           orbitalgrid[gaddrzy + nx] = orbdensity;
01232         } else {
01233           orbitalgrid[gaddrzy + nx] = value;
01234         }
01235       }
01236     }
01237   }
01238 
01239   return 0;
01240 }
01241 
01242 
01243 
01244 #if defined(VMDORBUSESSE) && defined(__SSE2__)
01245 
01246 #if 0 && !defined(_WIN64) // MSVC doesn't support old MMX intrinsics
01247 //
01248 // Adaptation of the Cephes exp() to an SSE-ized exp_ps() routine 
01249 // originally by Julien Pommier
01250 //   Copyright (C) 2007  Julien Pommier, ZLIB license
01251 //   http://gruntthepeon.free.fr/ssemath/
01252 // 
01253 #ifdef _MSC_VER /* visual c++ */
01254 # define ALIGN16_BEG __declspec(align(16))
01255 # define ALIGN16_END
01256 #else /* gcc or icc */
01257 # define ALIGN16_BEG
01258 # define ALIGN16_END __attribute__((aligned(16)))
01259 #endif
01260 
01261 #define _PS_CONST(Name, Val)                                            \
01262   static const ALIGN16_BEG float _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
01263 #define _PI32_CONST(Name, Val)                                            \
01264   static const ALIGN16_BEG int _pi32_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
01265 
01266 _PS_CONST(exp_hi,       88.3762626647949f);
01267 _PS_CONST(exp_lo,       -88.3762626647949f);
01268 
01269 _PS_CONST(cephes_LOG2EF, 1.44269504088896341);
01270 _PS_CONST(cephes_exp_C1, 0.693359375);
01271 _PS_CONST(cephes_exp_C2, -2.12194440e-4);
01272 
01273 _PS_CONST(cephes_exp_p0, 1.9875691500E-4);
01274 _PS_CONST(cephes_exp_p1, 1.3981999507E-3);
01275 _PS_CONST(cephes_exp_p2, 8.3334519073E-3);
01276 _PS_CONST(cephes_exp_p3, 4.1665795894E-2);
01277 _PS_CONST(cephes_exp_p4, 1.6666665459E-1);
01278 _PS_CONST(cephes_exp_p5, 5.0000001201E-1);
01279 _PS_CONST(one, 1.0);
01280 _PS_CONST(half, 0.5);
01281 
01282 _PI32_CONST(0x7f, 0x7f);
01283 
01284 typedef union xmm_mm_union {
01285   __m128 xmm;
01286   __m64 mm[2];
01287 } xmm_mm_union;
01288 
01289 #define COPY_XMM_TO_MM(xmm_, mm0_, mm1_) {          \
01290     xmm_mm_union u; u.xmm = xmm_;                   \
01291     mm0_ = u.mm[0];                                 \
01292     mm1_ = u.mm[1];                                 \
01293 }
01294 
01295 #define COPY_MM_TO_XMM(mm0_, mm1_, xmm_) {                         \
01296     xmm_mm_union u; u.mm[0]=mm0_; u.mm[1]=mm1_; xmm_ = u.xmm;      \
01297   }
01298 
01299 __m128 exp_ps(__m128 x) {
01300   __m128 tmp = _mm_setzero_ps(), fx;
01301   __m64 mm0, mm1;
01302 
01303   x = _mm_min_ps(x, *(__m128*)_ps_exp_hi);
01304   x = _mm_max_ps(x, *(__m128*)_ps_exp_lo);
01305 
01306   /* express exp(x) as exp(g + n*log(2)) */
01307   fx = _mm_mul_ps(x, *(__m128*)_ps_cephes_LOG2EF);
01308   fx = _mm_add_ps(fx,*(__m128*)_ps_half);
01309 
01310   /* how to perform a floorf with SSE: just below */
01311   /* step 1 : cast to int */
01312   tmp = _mm_movehl_ps(tmp, fx);
01313   mm0 = _mm_cvttps_pi32(fx);
01314   mm1 = _mm_cvttps_pi32(tmp);
01315   /* step 2 : cast back to float */
01316   tmp = _mm_cvtpi32x2_ps(mm0, mm1);
01317   /* if greater, substract 1 */
01318   __m128 mask = _mm_cmpgt_ps(tmp, fx);
01319   mask = _mm_and_ps(mask, *(__m128*)_ps_one);
01320   fx = _mm_sub_ps(tmp, mask);
01321 
01322   tmp = _mm_mul_ps(fx, *(__m128*)_ps_cephes_exp_C1);
01323   __m128 z = _mm_mul_ps(fx, *(__m128*)_ps_cephes_exp_C2);
01324   x = _mm_sub_ps(x, tmp);
01325   x = _mm_sub_ps(x, z);
01326 
01327   z = _mm_mul_ps(x,x);
01328 
01329   __m128 y = *(__m128*)_ps_cephes_exp_p0;
01330   y = _mm_mul_ps(y, x);
01331   y = _mm_add_ps(y, *(__m128*)_ps_cephes_exp_p1);
01332   y = _mm_mul_ps(y, x);
01333   y = _mm_add_ps(y, *(__m128*)_ps_cephes_exp_p2);
01334   y = _mm_mul_ps(y, x);
01335   y = _mm_add_ps(y, *(__m128*)_ps_cephes_exp_p3);
01336   y = _mm_mul_ps(y, x);
01337   y = _mm_add_ps(y, *(__m128*)_ps_cephes_exp_p4);
01338   y = _mm_mul_ps(y, x);
01339   y = _mm_add_ps(y, *(__m128*)_ps_cephes_exp_p5);
01340   y = _mm_mul_ps(y, z);
01341   y = _mm_add_ps(y, x);
01342   y = _mm_add_ps(y, *(__m128*)_ps_one);
01343 
01344   /* build 2^n */
01345   z = _mm_movehl_ps(z, fx);
01346   mm0 = _mm_cvttps_pi32(fx);
01347   mm1 = _mm_cvttps_pi32(z);
01348   mm0 = _mm_add_pi32(mm0, *(__m64*)_pi32_0x7f);
01349   mm1 = _mm_add_pi32(mm1, *(__m64*)_pi32_0x7f);
01350   mm0 = _mm_slli_pi32(mm0, 23);
01351   mm1 = _mm_slli_pi32(mm1, 23);
01352 
01353   __m128 pow2n;
01354   COPY_MM_TO_XMM(mm0, mm1, pow2n);
01355 
01356   y = _mm_mul_ps(y, pow2n);
01357   _mm_empty();
01358   return y;
01359 }
01360 #endif // MSVC doesn't support old MMX intrinsics
01361 
01362 
01363 //
01364 // David J. Hardy
01365 // 12 Dec 2008
01366 //
01367 // aexpfnxsse() - SSE2 version of aexpfnx().
01368 //
01369 //
01370 #if defined(__GNUC__) && ! defined(__INTEL_COMPILER)
01371 #define __align(X)  __attribute__((aligned(X) ))
01372 #if (__GNUC__ < 4)
01373 #define MISSING_mm_cvtsd_f64
01374 #endif
01375 #else
01376 #define __align(X) __declspec(align(X) )
01377 #endif
01378 
01379 #define MLOG2EF    -1.44269504088896f
01380 
01381 /*
01382  * Interpolating coefficients for linear blending of the
01383  * 3rd degree Taylor expansion of 2^x about 0 and -1.
01384  */
01385 #define SCEXP0     1.0000000000000000f
01386 #define SCEXP1     0.6987082824680118f
01387 #define SCEXP2     0.2633174272827404f
01388 #define SCEXP3     0.0923611991471395f
01389 #define SCEXP4     0.0277520543324108f
01390 
01391 /* for single precision float */
01392 #define EXPOBIAS   127
01393 #define EXPOSHIFT   23
01394 
01395 /* cutoff is optional, but can help avoid unnecessary work */
01396 #define ACUTOFF    -10
01397 
01398 typedef union SSEreg_t {
01399   __m128  f;  // 4x float (SSE)
01400   __m128i i;  // 4x 32-bit int (SSE2)
01401 } SSEreg;
01402 
01403 __m128 aexpfnxsse(__m128 x) {
01404   __align(16) SSEreg scal;
01405   __align(16) SSEreg n;
01406   __align(16) SSEreg y;
01407 
01408   scal.f = _mm_cmpge_ps(x, _mm_set_ps1(ACUTOFF));  /* Is x within cutoff? */
01409 
01410   /* If all x are outside of cutoff, return 0s. */
01411   if (_mm_movemask_ps(scal.f) == 0) {
01412     return _mm_setzero_ps();
01413   }
01414   /* Otherwise, scal.f contains mask to be ANDed with the scale factor */
01415 
01416   /*
01417    * Convert base:  exp(x) = 2^(N-d) where N is integer and 0 <= d < 1.
01418    *
01419    * Below we calculate n=N and x=-d, with "y" for temp storage,
01420    * calculate floor of x*log2(e) and subtract to get -d.
01421    */
01422   y.f = _mm_mul_ps(x, _mm_set_ps1(MLOG2EF));
01423   n.i = _mm_cvttps_epi32(y.f);
01424   x = _mm_cvtepi32_ps(n.i);
01425   x = _mm_sub_ps(x, y.f);
01426 
01427   /*
01428    * Approximate 2^{-d}, 0 <= d < 1, by interpolation.
01429    * Perform Horner's method to evaluate interpolating polynomial.
01430    */
01431   y.f = _mm_mul_ps(x, _mm_set_ps1(SCEXP4));      /* for x^4 term */
01432   y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP3));    /* for x^3 term */
01433   y.f = _mm_mul_ps(y.f, x);
01434   y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP2));    /* for x^2 term */
01435   y.f = _mm_mul_ps(y.f, x);
01436   y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP1));    /* for x^1 term */
01437   y.f = _mm_mul_ps(y.f, x);
01438   y.f = _mm_add_ps(y.f, _mm_set_ps1(SCEXP0));    /* for x^0 term */
01439 
01440   /*
01441    * Calculate 2^N exactly by directly manipulating floating point exponent.
01442    * Bitwise AND the result with scal.f mask to create the scale factor,
01443    * then use it to scale y for the final result.
01444    */
01445   n.i = _mm_sub_epi32(_mm_set1_epi32(EXPOBIAS), n.i);
01446   n.i = _mm_slli_epi32(n.i, EXPOSHIFT);
01447   scal.f = _mm_and_ps(scal.f, n.f);
01448   y.f = _mm_mul_ps(y.f, scal.f);
01449 
01450   return y.f;
01451 }
01452 
01453 
01454 int evaluate_grid_sse(int numatoms,
01455                   const float *wave_f, const float *basis_array,
01456                   const float *atompos,
01457                   const int *atom_basis,
01458                   const int *num_shells_per_atom,
01459                   const int *num_prim_per_shell,
01460                   const int *shell_types,
01461                   const int *numvoxels,
01462                   float voxelsize,
01463                   const float *origin,
01464                   int density,
01465                   float * orbitalgrid) {
01466   if (!orbitalgrid)
01467     return -1;
01468 
01469   int nx, ny, nz;
01470   __align(16) float sxdelta[4]; // 16-byte aligned for SSE
01471   for (nx=0; nx<4; nx++) 
01472     sxdelta[nx] = ((float) nx) * voxelsize * ANGS_TO_BOHR;
01473 
01474   // Calculate the value of the orbital at each gridpoint and store in 
01475   // the current oribtalgrid array
01476   int numgridxy = numvoxels[0]*numvoxels[1];
01477   for (nz=0; nz<numvoxels[2]; nz++) {
01478     float grid_x, grid_y, grid_z;
01479     grid_z = origin[2] + nz * voxelsize;
01480     for (ny=0; ny<numvoxels[1]; ny++) {
01481       grid_y = origin[1] + ny * voxelsize;
01482       int gaddrzy = ny*numvoxels[0] + nz*numgridxy;
01483       for (nx=0; nx<numvoxels[0]; nx+=4) {
01484         grid_x = origin[0] + nx * voxelsize;
01485 
01486         // calculate the value of the wavefunction of the
01487         // selected orbital at the current grid point
01488         int at;
01489         int prim, shell;
01490 
01491         // initialize value of orbital at gridpoint
01492         __m128 value = _mm_setzero_ps();
01493 
01494         // initialize the wavefunction and shell counters
01495         int ifunc = 0; 
01496         int shell_counter = 0;
01497 
01498         // loop over all the QM atoms
01499         for (at=0; at<numatoms; at++) {
01500           int maxshell = num_shells_per_atom[at];
01501           int prim_counter = atom_basis[at];
01502 
01503           // calculate distance between grid point and center of atom
01504           float sxdist = (grid_x - atompos[3*at  ])*ANGS_TO_BOHR;
01505           float sydist = (grid_y - atompos[3*at+1])*ANGS_TO_BOHR;
01506           float szdist = (grid_z - atompos[3*at+2])*ANGS_TO_BOHR;
01507 
01508           float sydist2 = sydist*sydist;
01509           float szdist2 = szdist*szdist;
01510           float yzdist2 = sydist2 + szdist2;
01511 
01512           __m128 xdelta = _mm_load_ps(&sxdelta[0]); // aligned load
01513           __m128 xdist  = _mm_load_ps1(&sxdist);
01514           xdist = _mm_add_ps(xdist, xdelta);
01515           __m128 ydist  = _mm_load_ps1(&sydist);
01516           __m128 zdist  = _mm_load_ps1(&szdist);
01517           __m128 xdist2 = _mm_mul_ps(xdist, xdist);
01518           __m128 ydist2 = _mm_mul_ps(ydist, ydist);
01519           __m128 zdist2 = _mm_mul_ps(zdist, zdist);
01520           __m128 dist2  = _mm_load_ps1(&yzdist2); 
01521           dist2 = _mm_add_ps(dist2, xdist2);
01522  
01523           // loop over the shells belonging to this atom
01524           // XXX this is maybe a misnomer because in split valence
01525           //     basis sets like 6-31G we have more than one basis
01526           //     function per (valence-)shell and we are actually
01527           //     looping over the individual contracted GTOs
01528           for (shell=0; shell < maxshell; shell++) {
01529             __m128 contracted_gto = _mm_setzero_ps();
01530 
01531             // Loop over the Gaussian primitives of this contracted 
01532             // basis function to build the atomic orbital
01533             // 
01534             // XXX there's a significant opportunity here for further
01535             //     speedup if we replace the entire set of primitives
01536             //     with the single gaussian that they are attempting 
01537             //     to model.  This could give us another 6x speedup in 
01538             //     some of the common/simple cases.
01539             int maxprim = num_prim_per_shell[shell_counter];
01540             int shelltype = shell_types[shell_counter];
01541             for (prim=0; prim<maxprim; prim++) {
01542               // XXX pre-negate exponent value
01543               float exponent       = -basis_array[prim_counter    ];
01544               float contract_coeff =  basis_array[prim_counter + 1];
01545 
01546               // contracted_gto += contract_coeff * exp(-exponent*dist2);
01547               __m128 expval = _mm_mul_ps(_mm_load_ps1(&exponent), dist2);
01548               // SSE expf() required here
01549 #if 1
01550               __m128 retval = aexpfnxsse(expval);
01551 #else
01552               __m128 retval = exp_ps(expval);
01553 #endif
01554               __m128 ctmp = _mm_mul_ps(_mm_load_ps1(&contract_coeff), retval);
01555               contracted_gto = _mm_add_ps(contracted_gto, ctmp);
01556               prim_counter += 2;
01557             }
01558 
01559             /* multiply with the appropriate wavefunction coefficient */
01560             __m128 tmpshell = _mm_setzero_ps();
01561             switch (shelltype) {
01562               case S_SHELL:
01563                 value = _mm_add_ps(value, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), contracted_gto));
01564                 break;
01565 
01566               case P_SHELL:
01567                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), xdist));
01568                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), ydist));
01569                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), zdist));
01570                 value = _mm_add_ps(value, _mm_mul_ps(tmpshell, contracted_gto));
01571                 break;
01572 
01573               case D_SHELL:
01574                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), xdist2));
01575                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(xdist, ydist)));
01576                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), ydist2));
01577                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(xdist, zdist)));
01578                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(ydist, zdist)));
01579                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), zdist2));
01580                 value = _mm_add_ps(value, _mm_mul_ps(tmpshell, contracted_gto));
01581                 break;
01582 
01583               case F_SHELL:
01584                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(xdist2, xdist)));
01585                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(xdist2, ydist)));
01586                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(ydist2, xdist)));
01587                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(ydist2, ydist)));
01588                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(xdist2, zdist)));
01589                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(_mm_mul_ps(xdist, ydist), zdist)));
01590                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(ydist2, zdist)));
01591                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(zdist2, xdist)));
01592                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(zdist2, ydist)));
01593                 tmpshell = _mm_add_ps(tmpshell, _mm_mul_ps(_mm_load_ps1(&wave_f[ifunc++]), _mm_mul_ps(zdist2, zdist)));
01594                 value = _mm_add_ps(value, _mm_mul_ps(tmpshell, contracted_gto));
01595                 break;
01596  
01597 #if 0
01598               default:
01599                 // avoid unnecessary branching and minimize use of pow()
01600                 int i, j; 
01601                 float xdp, ydp, zdp;
01602                 float xdiv = 1.0f / xdist;
01603                 for (j=0, zdp=1.0f; j<=shelltype; j++, zdp*=zdist) {
01604                   int imax = shelltype - j; 
01605                   for (i=0, ydp=1.0f, xdp=pow(xdist, imax); i<=imax; i++, ydp*=ydist, xdp*=xdiv) {
01606                     tmpshell += wave_f[ifunc++] * xdp * ydp * zdp;
01607                   }
01608                 }
01609                 value += tmpshell * contracted_gto;
01610 #endif
01611             } // end switch
01612 
01613             shell_counter++;
01614           } // end shell
01615         } // end atom
01616 
01617         // return either orbital density or orbital wavefunction amplitude
01618         if (density) {
01619           __m128 mask = _mm_cmplt_ps(value, _mm_setzero_ps());
01620           __m128 sqdensity = _mm_mul_ps(value, value);
01621           __m128 orbdensity = sqdensity;
01622           __m128 nsqdensity = _mm_and_ps(sqdensity, mask);
01623           orbdensity = _mm_sub_ps(orbdensity, nsqdensity);
01624           orbdensity = _mm_sub_ps(orbdensity, nsqdensity);
01625           _mm_storeu_ps(&orbitalgrid[gaddrzy + nx], orbdensity);
01626         } else {
01627           _mm_storeu_ps(&orbitalgrid[gaddrzy + nx], value);
01628         }
01629       }
01630     }
01631   }
01632 
01633   return 0;
01634 }
01635 
01636 #endif
01637 
01638 
01639 
01640 #if defined(VMDORBUSEVSX) && defined(__VSX__)
01641 //
01642 // John Stone, June 2016
01643 //
01644 // aexpfnxsse() - VSX version of aexpfnx().
01645 //
01646 #if defined(__GNUC__) && ! defined(__INTEL_COMPILER)
01647 #define __align(X)  __attribute__((aligned(X) ))
01648 #else
01649 #define __align(X) __declspec(align(X) )
01650 #endif
01651 
01652 #define MLOG2EF    -1.44269504088896f
01653 
01654 /*
01655  * Interpolating coefficients for linear blending of the
01656  * 3rd degree Taylor expansion of 2^x about 0 and -1.
01657  */
01658 #define SCEXP0     1.0000000000000000f
01659 #define SCEXP1     0.6987082824680118f
01660 #define SCEXP2     0.2633174272827404f
01661 #define SCEXP3     0.0923611991471395f
01662 #define SCEXP4     0.0277520543324108f
01663 
01664 /* for single precision float */
01665 #define EXPOBIAS   127
01666 #define EXPOSHIFT   23
01667 
01668 /* cutoff is optional, but can help avoid unnecessary work */
01669 #define ACUTOFF    -10
01670 
01671 #if 0
01672 vector float ref_expf(vector float x) {
01673   vector float result;
01674 
01675   int i;
01676   for (i=0; i<4; i++) {
01677     result[i] = expf(x[i]);
01678   }
01679 
01680   return result;
01681 }
01682 #endif
01683 
01684 vector float aexpfnxvsx(vector float x) {
01685   // scal.f = _mm_cmpge_ps(x, _mm_set_ps1(ACUTOFF));  /* Is x within cutoff? */
01686   // 
01687   // If all x are outside of cutoff, return 0s.
01688   // if (_mm_movemask_ps(scal.f) == 0) {
01689   //   return _mm_setzero_ps();
01690   // }
01691   // Otherwise, scal.f contains mask to be ANDed with the scale factor
01692 
01693   /*
01694    * Convert base:  exp(x) = 2^(N-d) where N is integer and 0 <= d < 1.
01695    *
01696    * Below we calculate n=N and x=-d, with "y" for temp storage,
01697    * calculate floor of x*log2(e) and subtract to get -d.
01698    */
01699   vector float mb = vec_mul(x, vec_splats(MLOG2EF));
01700   vector float mbflr = vec_floor(mb);
01701   vector float d = vec_sub(mbflr, mb);
01702   vector float y;
01703 
01704   // Approximate 2^{-d}, 0 <= d < 1, by interpolation.
01705   // Perform Horner's method to evaluate interpolating polynomial.
01706   y = vec_madd(d, vec_splats(SCEXP4), vec_splats(SCEXP3));
01707   y = vec_madd(y, d, vec_splats(SCEXP2));
01708   y = vec_madd(y, d, vec_splats(SCEXP1));
01709   y = vec_madd(y, d, vec_splats(SCEXP0));
01710 
01711   return vec_mul(y, vec_expte(-mbflr));
01712 }
01713 
01714 
01715 int evaluate_grid_vsx(int numatoms,
01716                   const float *wave_f, const float *basis_array,
01717                   const float *atompos,
01718                   const int *atom_basis,
01719                   const int *num_shells_per_atom,
01720                   const int *num_prim_per_shell,
01721                   const int *shell_types,
01722                   const int *numvoxels,
01723                   float voxelsize,
01724                   const float *origin,
01725                   int density,
01726                   float * orbitalgrid) {
01727   if (!orbitalgrid)
01728     return -1;
01729 
01730   int nx, ny, nz;
01731   __attribute__((aligned(16))) float sxdelta[4]; // 16-byte aligned for VSX
01732   for (nx=0; nx<4; nx++) 
01733     sxdelta[nx] = ((float) nx) * voxelsize * ANGS_TO_BOHR;
01734 
01735   // Calculate the value of the orbital at each gridpoint and store in 
01736   // the current oribtalgrid array
01737   int numgridxy = numvoxels[0]*numvoxels[1];
01738   for (nz=0; nz<numvoxels[2]; nz++) {
01739     float grid_x, grid_y, grid_z;
01740     grid_z = origin[2] + nz * voxelsize;
01741     for (ny=0; ny<numvoxels[1]; ny++) {
01742       grid_y = origin[1] + ny * voxelsize;
01743       int gaddrzy = ny*numvoxels[0] + nz*numgridxy;
01744       for (nx=0; nx<numvoxels[0]; nx+=4) {
01745         grid_x = origin[0] + nx * voxelsize;
01746 
01747         // calculate the value of the wavefunction of the
01748         // selected orbital at the current grid point
01749         int at;
01750         int prim, shell;
01751 
01752         // initialize value of orbital at gridpoint
01753         vector float value = vec_splats(0.0f); 
01754 
01755         // initialize the wavefunction and shell counters
01756         int ifunc = 0; 
01757         int shell_counter = 0;
01758 
01759         // loop over all the QM atoms
01760         for (at=0; at<numatoms; at++) {
01761           int maxshell = num_shells_per_atom[at];
01762           int prim_counter = atom_basis[at];
01763 
01764           // calculate distance between grid point and center of atom
01765           float sxdist = (grid_x - atompos[3*at  ])*ANGS_TO_BOHR;
01766           float sydist = (grid_y - atompos[3*at+1])*ANGS_TO_BOHR;
01767           float szdist = (grid_z - atompos[3*at+2])*ANGS_TO_BOHR;
01768 
01769           float sydist2 = sydist*sydist;
01770           float szdist2 = szdist*szdist;
01771           float yzdist2 = sydist2 + szdist2;
01772 
01773           vector float xdelta =  *((__vector float *) &sxdelta[0]); // aligned load
01774           vector float xdist  = vec_splats(sxdist);
01775           xdist = vec_add(xdist, xdelta);
01776           vector float ydist  = vec_splats(sydist);
01777           vector float zdist  = vec_splats(szdist);
01778           vector float xdist2 = vec_mul(xdist, xdist);
01779           vector float ydist2 = vec_mul(ydist, ydist);
01780           vector float zdist2 = vec_mul(zdist, zdist);
01781           vector float dist2  = vec_splats(yzdist2); 
01782           dist2 = vec_add(dist2, xdist2);
01783  
01784           // loop over the shells belonging to this atom
01785           // XXX this is maybe a misnomer because in split valence
01786           //     basis sets like 6-31G we have more than one basis
01787           //     function per (valence-)shell and we are actually
01788           //     looping over the individual contracted GTOs
01789           for (shell=0; shell < maxshell; shell++) {
01790             vector float contracted_gto = vec_splats(0.0f);
01791 
01792             // Loop over the Gaussian primitives of this contracted 
01793             // basis function to build the atomic orbital
01794             // 
01795             // XXX there's a significant opportunity here for further
01796             //     speedup if we replace the entire set of primitives
01797             //     with the single gaussian that they are attempting 
01798             //     to model.  This could give us another 6x speedup in 
01799             //     some of the common/simple cases.
01800             int maxprim = num_prim_per_shell[shell_counter];
01801             int shelltype = shell_types[shell_counter];
01802             for (prim=0; prim<maxprim; prim++) {
01803               // XXX pre-negate exponent value
01804               float exponent       = -basis_array[prim_counter    ];
01805               float contract_coeff =  basis_array[prim_counter + 1];
01806 
01807               // contracted_gto += contract_coeff * exp(-exponent*dist2);
01808               vector float expval = vec_mul(vec_splats(exponent), dist2);
01809 
01810               // VSX expf() required here
01811               vector float retval = aexpfnxvsx(expval);
01812 
01813               vector float ctmp = vec_mul(vec_splats(contract_coeff), retval);
01814               contracted_gto = vec_add(contracted_gto, ctmp);
01815               prim_counter += 2;
01816             }
01817 
01818             /* multiply with the appropriate wavefunction coefficient */
01819             vector float tmpshell = vec_splats(0.0f);
01820             switch (shelltype) {
01821               case S_SHELL:
01822                 value = vec_add(value, vec_mul(vec_splats(wave_f[ifunc++]), contracted_gto));
01823                 break;
01824 
01825               case P_SHELL:
01826                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), xdist));
01827                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), ydist));
01828                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), zdist));
01829                 value = vec_add(value, vec_mul(tmpshell, contracted_gto));
01830                 break;
01831 
01832               case D_SHELL:
01833                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), xdist2));
01834                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(xdist, ydist)));
01835                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), ydist2));
01836                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(xdist, zdist)));
01837                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(ydist, zdist)));
01838                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), zdist2));
01839                 value = vec_add(value, vec_mul(tmpshell, contracted_gto));
01840                 break;
01841 
01842               case F_SHELL:
01843                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(xdist2, xdist)));
01844                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(xdist2, ydist)));
01845                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(ydist2, xdist)));
01846                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(ydist2, ydist)));
01847                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(xdist2, zdist)));
01848                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(vec_mul(xdist, ydist), zdist)));
01849                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(ydist2, zdist)));
01850                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(zdist2, xdist)));
01851                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(zdist2, ydist)));
01852                 tmpshell = vec_add(tmpshell, vec_mul(vec_splats(wave_f[ifunc++]), vec_mul(zdist2, zdist)));
01853                 value = vec_add(value, vec_mul(tmpshell, contracted_gto));
01854                 break;
01855  
01856 #if 0
01857               default:
01858                 // avoid unnecessary branching and minimize use of pow()
01859                 int i, j; 
01860                 float xdp, ydp, zdp;
01861                 float xdiv = 1.0f / xdist;
01862                 for (j=0, zdp=1.0f; j<=shelltype; j++, zdp*=zdist) {
01863                   int imax = shelltype - j; 
01864                   for (i=0, ydp=1.0f, xdp=pow(xdist, imax); i<=imax; i++, ydp*=ydist, xdp*=xdiv) {
01865                     tmpshell += wave_f[ifunc++] * xdp * ydp * zdp;
01866                   }
01867                 }
01868                 value += tmpshell * contracted_gto;
01869 #endif
01870             } // end switch
01871 
01872             shell_counter++;
01873           } // end shell
01874         } // end atom
01875 
01876         // return either orbital density or orbital wavefunction amplitude
01877         if (density) {
01878           value = vec_cpsgn(value, vec_mul(value, value));
01879 
01880           float *ufptr = &orbitalgrid[gaddrzy + nx];
01881           ufptr[0] = value[0];
01882           ufptr[1] = value[1];
01883           ufptr[2] = value[2];
01884           ufptr[3] = value[3];
01885         } else {
01886           float *ufptr = &orbitalgrid[gaddrzy + nx];
01887           ufptr[0] = value[0];
01888           ufptr[1] = value[1];
01889           ufptr[2] = value[2];
01890           ufptr[3] = value[3];
01891         }
01892       }
01893     }
01894   }
01895 
01896   return 0;
01897 }
01898 
01899 #endif
01900 
01901 
01902 
01903 //
01904 // Multithreaded molecular orbital computation engine
01905 //
01906 
01907 typedef struct {
01908   wkf_cpu_caps_t *cpucaps;
01909   int numatoms;
01910   const float *wave_f;
01911   const float *basis_array;
01912   const float *atompos;
01913   const int *atom_basis;
01914   const int *num_shells_per_atom;
01915   const int *num_prim_per_shell;
01916   const int *shell_types;
01917   const int *numvoxels;
01918   float voxelsize;
01919   int density;
01920   const float *origin;
01921   float *orbitalgrid;
01922 } orbthrparms;
01923 
01924 
01925 extern "C" void * orbitalthread(void *voidparms) {
01926   int numvoxels[3];
01927   float origin[3];
01928   orbthrparms *parms = NULL;
01929 #if defined(VMDORBUSETHRPOOL)
01930   wkf_threadpool_worker_getdata(voidparms, (void **) &parms);
01931 #else
01932   wkf_threadlaunch_getdata(voidparms, (void **) &parms);
01933 #endif
01934 
01935 #if defined(VMDCPUDISPATCH)
01936   wkf_cpu_caps_t *cpucaps = parms->cpucaps;
01937 
01938 #if defined(VMDUSEAVX512)
01939   int dispatch_AVX512ER = 0;
01940   if ((cpucaps->flags & CPU_AVX512ER) && (getenv("VMDNOAVX512ER") == NULL)) {
01941 //    printf("evaluate_grid_avx512er\n");
01942     dispatch_AVX512ER = 1;
01943   }
01944 
01945   int dispatch_AVX512F = 0;
01946   if ((cpucaps->flags & CPU_AVX512F) && (getenv("VMDNOAVX512F") == NULL)) {
01947     dispatch_AVX512F = 1;
01948 //    printf("evaluate_grid_avx512f\n");
01949   }
01950 #endif
01951 
01952 #if defined(VMDUSEAVX2)
01953   int dispatch_AVX2 = 0;
01954   if ((cpucaps->flags & CPU_AVX2) && (getenv("VMDNOAVX2") == NULL)) {
01955     dispatch_AVX2 = 1;
01956 //    printf("evaluate_grid_avx2\n");
01957   }
01958 #endif
01959 
01960 #if defined(VMDUSESVE)
01961   int dispatch_SVE = 0;
01962   if ((cpucaps->flags & CPU_ARM64_SVE) && (getenv("VMDNOSVE") == NULL)) {
01963     dispatch_SVE = 1;
01964 //  printf("evaluate_grid_sve\n");
01965   }
01966 #endif
01967 
01968 #if defined(VMDUSENEON)
01969   int dispatch_NEON = 0;
01970   if ((cpucaps->flags & CPU_ARM64_ASIMD) && (getenv("VMDNONEON") == NULL)) {
01971     dispatch_NEON = 1;
01972 //  printf("evaluate_grid_neon\n");
01973   }
01974 #endif
01975 
01976 #endif // VMDCPUDISPATCH
01977 
01978   // 
01979   // Hard-coded compile-time fall-through vectorization paths
01980   //
01981 #if defined(VMDORBUSESSE) && defined(__SSE2__)
01982   int dispatch_SSE2 = 0;
01983   if ((getenv("VMDNOSSE2") == NULL)) {
01984 //  printf("evaluate_grid_sse\n");
01985     dispatch_SSE2 = 1;
01986   }
01987 #endif
01988 
01989 #if defined(VMDORBUSEVSX) && defined(__VSX__)
01990   int dispatch_VSX = 0;
01991   if (getenv("VMDNOVSX") == NULL) {
01992 //  printf("evaluate_grid_vsx\n");
01993     dispatch_VSX = 1;
01994   }
01995 #endif
01996 
01997   numvoxels[0] = parms->numvoxels[0];
01998   numvoxels[1] = parms->numvoxels[1];
01999   numvoxels[2] = 1; // we compute only a single plane
02000 
02001   origin[0] = parms->origin[0];
02002   origin[1] = parms->origin[1];
02003 
02004   // loop over orbital planes
02005   int planesize = numvoxels[0] * numvoxels[1];
02006   wkf_tasktile_t tile;
02007 #if defined(VMDORBUSETHRPOOL)
02008   while (wkf_threadpool_next_tile(voidparms, 1, &tile) != WKF_SCHED_DONE) {
02009 #else
02010   while (wkf_threadlaunch_next_tile(voidparms, 1, &tile) != WKF_SCHED_DONE) {
02011 #endif
02012     int k;
02013     for (k=tile.start; k<tile.end; k++) {
02014       origin[2] = parms->origin[2] + parms->voxelsize * k;
02015 
02016 #if defined(VMDCPUDISPATCH)
02017       //
02018       // runtime CPU dispatch
02019       //   check for optional vector instructions and execute custom kernels 
02020       //   for the fastest code path supported by the detected hardware
02021       // 
02022       if (cpucaps != NULL) {
02023 #if defined(VMDUSEAVX512)
02024         if (dispatch_AVX512ER) {
02025           evaluate_grid_avx512er(parms->numatoms, parms->wave_f, 
02026               parms->basis_array, parms->atompos, parms->atom_basis,
02027               parms->num_shells_per_atom, parms->num_prim_per_shell,
02028               parms->shell_types, numvoxels, parms->voxelsize,
02029               origin, parms->density, parms->orbitalgrid + planesize*k);
02030           continue;
02031         }
02032 
02033         if (dispatch_AVX512F) {
02034           evaluate_grid_avx512f(parms->numatoms, parms->wave_f, 
02035               parms->basis_array, parms->atompos, parms->atom_basis,
02036               parms->num_shells_per_atom, parms->num_prim_per_shell,
02037               parms->shell_types, numvoxels, parms->voxelsize,
02038               origin, parms->density, parms->orbitalgrid + planesize*k);
02039           continue;
02040         }
02041 #endif
02042 
02043 #if defined(VMDUSEAVX2)
02044         if (dispatch_AVX2) {
02045           evaluate_grid_avx2(parms->numatoms, parms->wave_f, 
02046               parms->basis_array, parms->atompos, parms->atom_basis,
02047               parms->num_shells_per_atom, parms->num_prim_per_shell,
02048               parms->shell_types, numvoxels, parms->voxelsize,
02049               origin, parms->density, parms->orbitalgrid + planesize*k);
02050           continue;
02051         }
02052 #endif
02053 
02054 #if defined(VMDUSESVE)
02055         if (dispatch_SVE) {
02056           evaluate_grid_sve(parms->numatoms, parms->wave_f,
02057               parms->basis_array, parms->atompos, parms->atom_basis,
02058               parms->num_shells_per_atom, parms->num_prim_per_shell,
02059               parms->shell_types, numvoxels, parms->voxelsize,
02060               origin, parms->density, parms->orbitalgrid + planesize*k);
02061           continue;
02062         }
02063 #endif
02064 
02065 #if defined(VMDUSENEON)
02066         if (dispatch_NEON) {
02067           evaluate_grid_neon(parms->numatoms, parms->wave_f, 
02068               parms->basis_array, parms->atompos, parms->atom_basis,
02069               parms->num_shells_per_atom, parms->num_prim_per_shell,
02070               parms->shell_types, numvoxels, parms->voxelsize,
02071               origin, parms->density, parms->orbitalgrid + planesize*k);
02072           continue;
02073         }
02074 #endif
02075 
02076       } // runtime cpucaps-based dispatch
02077 #endif
02078 
02079 
02080 //
02081 // hard-coded fall-through path if runtime CPU dispatch doesn't match up
02082 //
02083 #if defined(VMDORBUSESSE) && defined(__SSE2__)
02084       if (dispatch_SSE2) {
02085         evaluate_grid_sse(parms->numatoms, parms->wave_f, 
02086             parms->basis_array, parms->atompos, parms->atom_basis,
02087             parms->num_shells_per_atom, parms->num_prim_per_shell,
02088             parms->shell_types, numvoxels, parms->voxelsize,
02089             origin, parms->density, parms->orbitalgrid + planesize*k);
02090         continue;
02091       }
02092 #endif
02093 
02094 #if defined(VMDORBUSEVSX) && defined(__VSX__)
02095       if (dispatch_VSX) {
02096         evaluate_grid_vsx(parms->numatoms, parms->wave_f,
02097             parms->basis_array, parms->atompos, parms->atom_basis,
02098             parms->num_shells_per_atom, parms->num_prim_per_shell,
02099             parms->shell_types, numvoxels, parms->voxelsize,
02100             origin, parms->density, parms->orbitalgrid + planesize*k);
02101         continue;
02102       }
02103 #endif
02104 
02105       // Standard C++-based implementation that uses wither the
02106       // standard math library expf(), a faster modified Cephes expf(),
02107       // our our own fast exponential approximation routine.
02108       evaluate_grid(parms->numatoms, parms->wave_f, 
02109             parms->basis_array, parms->atompos, parms->atom_basis,
02110             parms->num_shells_per_atom, parms->num_prim_per_shell,
02111             parms->shell_types, numvoxels, parms->voxelsize,
02112             origin, parms->density, parms->orbitalgrid + planesize*k);
02113     }
02114   }
02115 
02116   return NULL;
02117 }
02118 
02119 
02120 int evaluate_grid_fast(wkf_cpu_caps_t *cpucaps,
02121 #if defined(VMDORBUSETHRPOOL) 
02122                        wkf_threadpool_t *thrpool, 
02123 #else
02124                        int numcputhreads,
02125 #endif
02126                        int numatoms,
02127                        const float *wave_f,
02128                        const float *basis_array,
02129                        const float *atompos,
02130                        const int *atom_basis,
02131                        const int *num_shells_per_atom,
02132                        const int *num_prim_per_shell,
02133                        const int *shell_types,
02134                        const int *numvoxels,
02135                        float voxelsize,
02136                        const float *origin,
02137                        int density,
02138                        float * orbitalgrid) {
02139   int rc=0;
02140   orbthrparms parms;
02141 
02142   parms.cpucaps = cpucaps;
02143   parms.numatoms = numatoms;
02144   parms.wave_f = wave_f;
02145   parms.basis_array = basis_array;
02146   parms.atompos = atompos;
02147   parms.atom_basis = atom_basis;
02148   parms.num_shells_per_atom = num_shells_per_atom;
02149   parms.num_prim_per_shell = num_prim_per_shell;
02150   parms.shell_types = shell_types;
02151   parms.numvoxels = numvoxels;
02152   parms.voxelsize = voxelsize;
02153   parms.origin = origin;
02154   parms.density = density;
02155   parms.orbitalgrid = orbitalgrid;
02156 
02157   /* spawn child threads to do the work */
02158   wkf_tasktile_t tile;
02159   tile.start = 0;
02160   tile.end = numvoxels[2];
02161 
02162 #if defined(VMDORBUSETHRPOOL) 
02163   wkf_threadpool_sched_dynamic(thrpool, &tile);
02164   rc = wkf_threadpool_launch(thrpool, orbitalthread, &parms, 1);
02165 #else
02166   rc = wkf_threadlaunch(numcputhreads, &parms, orbitalthread, &tile);
02167 #endif
02168 
02169   return rc;
02170 }
02171 
02172 
02173 void Orbital::print_wavefunction() {
02174   // XXX Android, IRIX, and Windows don't provide log2f(), nor log2() ?!?!?!?!
02175   // for now we'll just avoid compiling this debugging code
02176 #if !(defined(_MSC_VER) || defined(ARCH_IRIX6) || defined(ARCH_IRIX6_64) || defined(ARCH_ANDROIDARMV7A))
02177   char shellname[6] = {'S', 'P', 'D', 'F', 'G', 'H'};
02178   int ifunc = 0;
02179   int at;
02180   int shell;
02181   for (at=0; at<numatoms; at++) {
02182     for (shell=0; shell < num_shells_per_atom[at]; shell++) {
02183       int shelltype = basis_set[at].shell[shell].type;
02184 
02185       // avoid unnecessary branching and minimize use of pow()
02186       int i, j, iang=0; 
02187       float xdist=2.0;
02188       float ydist=2.0;
02189       float zdist=2.0;
02190       float xdp, ydp, zdp;
02191       float xdiv = 1.0f / xdist;
02192       for (j=0, zdp=1.0f; j<=shelltype; j++, zdp*=zdist) {
02193         int imax = shelltype - j; 
02194         for (i=0, ydp=1.0f, xdp=pow(xdist, imax); i<=imax; i++, ydp*=ydist, xdp*=xdiv) {
02195           printf("%3i %c", at, shellname[shelltype]);
02196           int k, m=0;
02197           char buf[20]; buf[0] = '\0';
02198           for (k=0; k<(int)log2f(xdp); k++, m++) sprintf(buf+m, "x");
02199           for (k=0; k<(int)log2f(ydp); k++, m++) sprintf(buf+m, "y");
02200           for (k=0; k<(int)log2f(zdp); k++, m++) sprintf(buf+m, "z");
02201           //char *ang = qmdata->get_angular_momentum(at, shell, iang);
02202           printf("%-5s (%1.0f%1.0f%1.0f) wave_f[%3i] = % 11.6f\n", buf,
02203                  log2f(xdp), log2f(ydp), log2f(zdp), ifunc, wave_f[ifunc]);
02204           //delete [] ang;
02205           iang++;
02206           ifunc++;
02207         }
02208       }
02209     }
02210   }
02211 #endif
02212 
02213 }

Generated on Thu Apr 18 02:45:17 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002