/// return true if it's time to print a status update message int wkf_msg_timer_timeout(wkfmsgtimer *mt) { double elapsed = wkf_timer_timenow(mt->timer); if (elapsed > mt->updatetime) { // reset the clock and return true that our timer expired wkf_timer_start(mt->timer); return 1; } else if (elapsed < 0) { // time went backwards, best reset our clock! wkf_timer_start(mt->timer); } return 0; }
int QuickSurf::calc_surf(AtomSel *atomSel, DrawMolecule *mol, const float *atompos, const float *atomradii, int quality, float radscale, float gridspacing, float isoval, const int *colidx, const float *cmap, VMDDisplayList *cmdList) { wkf_timer_start(timer); int colorperatom = (colidx != NULL && cmap != NULL); int usebeads=0; // clean up any existing CPU arrays before going any further... if (voltexmap != NULL) free(voltexmap); voltexmap = NULL; ResizeArray<float> beadpos(64 + (3 * atomSel->selected) / 20); ResizeArray<float> beadradii(64 + (3 * atomSel->selected) / 20); ResizeArray<float> beadcolors(64 + (3 * atomSel->selected) / 20); if (getenv("VMDQUICKSURFBEADS")) { usebeads=1; #if !defined(ARCH_BLUEWATERS) printf("QuickSurf using residue beads representation...\n"); #endif } int numbeads = 0; if (usebeads) { int i, resid, numres; // draw a bead for each residue numres = mol->residueList.num(); for (resid=0; resid<numres; resid++) { float com[3] = {0.0, 0.0, 0.0}; const ResizeArray<int> &atoms = mol->residueList[resid]->atoms; int numatoms = atoms.num(); int oncount = 0; // find COM for residue for (i=0; i<numatoms; i++) { int idx = atoms[i]; if (atomSel->on[idx]) { oncount++; vec_add(com, com, atompos + 3*idx); } } if (oncount < 1) continue; // exit if there weren't any atoms vec_scale(com, 1.0f / (float) oncount, com); // find radius of bounding sphere and save last atom index for color int atomcolorindex=0; // initialize, to please compilers float boundradsq = 0.0f; for (i=0; i<numatoms; i++) { int idx = atoms[i]; if (atomSel->on[idx]) { float tmpdist[3]; atomcolorindex = idx; vec_sub(tmpdist, com, atompos + 3*idx); float distsq = dot_prod(tmpdist, tmpdist); if (distsq > boundradsq) { boundradsq = distsq; } } } beadpos.append(com[0]); beadpos.append(com[1]); beadpos.append(com[2]); beadradii.append(sqrtf(boundradsq) + 1.0f); if (colorperatom) { const float *cp = &cmap[colidx[atomcolorindex] * 3]; beadcolors.append(cp[0]); beadcolors.append(cp[1]); beadcolors.append(cp[2]); } // XXX still need to add pick points... } numbeads = beadpos.num() / 3; } // initialize class variables isovalue=isoval; // If no volumetric texture will be computed we will use the cmap // parameter to pass in the solid color to be applied to all vertices vec_copy(solidcolor, cmap); // compute min/max atom radius, build list of selected atom radii, // and compute bounding box for the selected atoms float minx, miny, minz, maxx, maxy, maxz; float minrad, maxrad; int i; if (usebeads) { minx = maxx = beadpos[0]; miny = maxy = beadpos[1]; minz = maxz = beadpos[2]; minrad = maxrad = beadradii[0]; for (i=0; i<numbeads; i++) { int ind = i * 3; float tmpx = beadpos[ind ]; float tmpy = beadpos[ind+1]; float tmpz = beadpos[ind+2]; minx = (tmpx < minx) ? tmpx : minx; maxx = (tmpx > maxx) ? tmpx : maxx; miny = (tmpy < miny) ? tmpy : miny; maxy = (tmpy > maxy) ? tmpy : maxy; minz = (tmpz < minz) ? tmpz : minz; maxz = (tmpz > maxz) ? tmpz : maxz; // we always have to compute the rmin/rmax for beads // since these radii are defined on-the-fly float r = beadradii[i]; minrad = (r < minrad) ? r : minrad; maxrad = (r > maxrad) ? r : maxrad; } } else { minx = maxx = atompos[atomSel->firstsel*3 ]; miny = maxy = atompos[atomSel->firstsel*3+1]; minz = maxz = atompos[atomSel->firstsel*3+2]; // Query min/max atom radii for the entire molecule mol->get_radii_minmax(minrad, maxrad); // We only compute rmin/rmax for the actual group of selected atoms if // (rmax/rmin > 2.5) for the whole molecule, otherwise it's a small // enough range that we don't care since it won't hurt our performance. if (minrad <= 0.001 || maxrad/minrad > 2.5) { minrad = maxrad = atomradii[atomSel->firstsel]; for (i=atomSel->firstsel; i<=atomSel->lastsel; i++) { if (atomSel->on[i]) { int ind = i * 3; float tmpx = atompos[ind ]; float tmpy = atompos[ind+1]; float tmpz = atompos[ind+2]; minx = (tmpx < minx) ? tmpx : minx; maxx = (tmpx > maxx) ? tmpx : maxx; miny = (tmpy < miny) ? tmpy : miny; maxy = (tmpy > maxy) ? tmpy : maxy; minz = (tmpz < minz) ? tmpz : minz; maxz = (tmpz > maxz) ? tmpz : maxz; float r = atomradii[i]; minrad = (r < minrad) ? r : minrad; maxrad = (r > maxrad) ? r : maxrad; } } } else { for (i=atomSel->firstsel; i<=atomSel->lastsel; i++) { if (atomSel->on[i]) { int ind = i * 3; float tmpx = atompos[ind ]; float tmpy = atompos[ind+1]; float tmpz = atompos[ind+2]; minx = (tmpx < minx) ? tmpx : minx; maxx = (tmpx > maxx) ? tmpx : maxx; miny = (tmpy < miny) ? tmpy : miny; maxy = (tmpy > maxy) ? tmpy : maxy; minz = (tmpz < minz) ? tmpz : minz; maxz = (tmpz > maxz) ? tmpz : maxz; } } } } float mincoord[3], maxcoord[3]; mincoord[0] = minx; mincoord[1] = miny; mincoord[2] = minz; maxcoord[0] = maxx; maxcoord[1] = maxy; maxcoord[2] = maxz; // crude estimate of the grid padding we require to prevent the // resulting isosurface from being clipped float gridpadding = radscale * maxrad * 1.70f; float padrad = gridpadding; padrad = 0.65f * sqrtf(4.0f/3.0f*((float) VMD_PI)*padrad*padrad*padrad); gridpadding = MAX(gridpadding, padrad); // Handle coarse-grained structures and whole-cell models // XXX The switch at 4.0A from an assumed all-atom scale structure to // CG or cell models is a simple heuristic at a somewhat arbitrary // threshold value. // For all-atom models the units shown in the GUI are in Angstroms // and are absolute, but for CG or cell models the units in the GUI // are relative to the atom with the minimum radius. // This code doesn't do anything to handle structures with a minrad // of zero, where perhaps only one particle has an unset radius. if (minrad > 4.0f) { gridspacing *= minrad; } #if !defined(ARCH_BLUEWATERS) printf("QuickSurf: R*%.1f, I=%.1f, H=%.1f Pad: %.1f minR: %.1f maxR: %.1f)\n", radscale, isovalue, gridspacing, gridpadding, minrad, maxrad); #endif mincoord[0] -= gridpadding; mincoord[1] -= gridpadding; mincoord[2] -= gridpadding; maxcoord[0] += gridpadding; maxcoord[1] += gridpadding; maxcoord[2] += gridpadding; // compute the real grid dimensions from the selected atoms xaxis[0] = maxcoord[0]-mincoord[0]; yaxis[1] = maxcoord[1]-mincoord[1]; zaxis[2] = maxcoord[2]-mincoord[2]; numvoxels[0] = (int) ceil(xaxis[0] / gridspacing); numvoxels[1] = (int) ceil(yaxis[1] / gridspacing); numvoxels[2] = (int) ceil(zaxis[2] / gridspacing); // recalc the grid dimensions from rounded/padded voxel counts xaxis[0] = (numvoxels[0]-1) * gridspacing; yaxis[1] = (numvoxels[1]-1) * gridspacing; zaxis[2] = (numvoxels[2]-1) * gridspacing; maxcoord[0] = mincoord[0] + xaxis[0]; maxcoord[1] = mincoord[1] + yaxis[1]; maxcoord[2] = mincoord[2] + zaxis[2]; #if !defined(ARCH_BLUEWATERS) printf(" GridSZ: (%4d %4d %4d) BBox: (%.1f %.1f %.1f)->(%.1f %.1f %.1f)\n", numvoxels[0], numvoxels[1], numvoxels[2], mincoord[0], mincoord[1], mincoord[2], maxcoord[0], maxcoord[1], maxcoord[2]); #endif vec_copy(origin, mincoord); // build compacted lists of bead coordinates, radii, and colors float *xyzr = NULL; float *colors = NULL; if (usebeads) { int ind =0; int ind4=0; xyzr = (float *) malloc(numbeads * sizeof(float) * 4); if (colorperatom) { colors = (float *) malloc(numbeads * sizeof(float) * 4); // build compacted lists of bead coordinates, radii, and colors for (i=0; i<numbeads; i++) { const float *fp = &beadpos[0] + ind; xyzr[ind4 ] = fp[0]-origin[0]; xyzr[ind4 + 1] = fp[1]-origin[1]; xyzr[ind4 + 2] = fp[2]-origin[2]; xyzr[ind4 + 3] = beadradii[i]; const float *cp = &beadcolors[0] + ind; colors[ind4 ] = cp[0]; colors[ind4 + 1] = cp[1]; colors[ind4 + 2] = cp[2]; colors[ind4 + 3] = 1.0f; ind4 += 4; ind += 3; } } else { // build compacted lists of bead coordinates and radii only for (i=0; i<numbeads; i++) { const float *fp = &beadpos[0] + ind; xyzr[ind4 ] = fp[0]-origin[0]; xyzr[ind4 + 1] = fp[1]-origin[1]; xyzr[ind4 + 2] = fp[2]-origin[2]; xyzr[ind4 + 3] = beadradii[i]; ind4 += 4; ind += 3; } } } else { int ind = atomSel->firstsel * 3; int ind4=0; xyzr = (float *) malloc(atomSel->selected * sizeof(float) * 4); if (colorperatom) { colors = (float *) malloc(atomSel->selected * sizeof(float) * 4); // build compacted lists of atom coordinates, radii, and colors for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) { if (atomSel->on[i]) { const float *fp = atompos + ind; xyzr[ind4 ] = fp[0]-origin[0]; xyzr[ind4 + 1] = fp[1]-origin[1]; xyzr[ind4 + 2] = fp[2]-origin[2]; xyzr[ind4 + 3] = atomradii[i]; const float *cp = &cmap[colidx[i] * 3]; colors[ind4 ] = cp[0]; colors[ind4 + 1] = cp[1]; colors[ind4 + 2] = cp[2]; colors[ind4 + 3] = 1.0f; ind4 += 4; } ind += 3; } } else { // build compacted lists of atom coordinates and radii only for (i=atomSel->firstsel; i <= atomSel->lastsel; i++) { if (atomSel->on[i]) { const float *fp = atompos + ind; xyzr[ind4 ] = fp[0]-origin[0]; xyzr[ind4 + 1] = fp[1]-origin[1]; xyzr[ind4 + 2] = fp[2]-origin[2]; xyzr[ind4 + 3] = atomradii[i]; ind4 += 4; } ind += 3; } } } // set gaussian window size based on user-specified quality parameter float gausslim = 2.0f; switch (quality) { case 3: gausslim = 4.0f; break; // max quality case 2: gausslim = 3.0f; break; // high quality case 1: gausslim = 2.5f; break; // medium quality case 0: default: gausslim = 2.0f; // low quality break; } pretime = wkf_timer_timenow(timer); #if defined(VMDCUDA) if (!getenv("VMDNOCUDA")) { // compute both density map and floating point color texture map int pcount = (usebeads) ? numbeads : atomSel->selected; int rc = cudaqs->calc_surf(pcount, &xyzr[0], (colorperatom) ? &colors[0] : &cmap[0], colorperatom, origin, numvoxels, maxrad, radscale, gridspacing, isovalue, gausslim, cmdList); if (rc == 0) { free(xyzr); if (colors) free(colors); voltime = wkf_timer_timenow(timer); return 0; } } #endif #if !defined(ARCH_BLUEWATERS) printf(" Computing density map grid on CPUs "); #endif long volsz = numvoxels[0] * numvoxels[1] * numvoxels[2]; volmap = new float[volsz]; if (colidx != NULL && cmap != NULL) { voltexmap = (float*) calloc(1, 3 * sizeof(float) * numvoxels[0] * numvoxels[1] * numvoxels[2]); } fflush(stdout); memset(volmap, 0, sizeof(float) * volsz); if ((volsz * atomSel->selected) > 20000000) { vmd_gaussdensity_threaded(atomSel->selected, &xyzr[0], (voltexmap!=NULL) ? &colors[0] : NULL, volmap, voltexmap, numvoxels, radscale, gridspacing, isovalue, gausslim); } else { vmd_gaussdensity_opt(atomSel->selected, &xyzr[0], (voltexmap!=NULL) ? &colors[0] : NULL, volmap, voltexmap, numvoxels, radscale, gridspacing, isovalue, gausslim); } free(xyzr); if (colors) free(colors); voltime = wkf_timer_timenow(timer); // draw the surface draw_trimesh(cmdList); #if !defined(ARCH_BLUEWATERS) printf(" Done.\n"); #endif return 0; }
void DrawMolItem::draw_orbital(int density, int wavefnctype, int wavefncspin, int wavefncexcitation, int orbid, float isovalue, int drawbox, int style, float gridspacing, int stepsize, int thickness) { if (!mol->numframes() || gridspacing <= 0.0f) return; // only recalculate the orbital grid if necessary int regenorbital=0; if (density != orbgridisdensity || wavefnctype != waveftype || wavefncspin != wavefspin || wavefncexcitation != wavefexcitation || orbid != gridorbid || gridspacing != orbgridspacing || orbvol == NULL || needRegenerate & MOL_REGEN || needRegenerate & SEL_REGEN) { regenorbital=1; } double motime=0, voltime=0, gradtime=0; wkf_timerhandle timer = wkf_timer_create(); wkf_timer_start(timer); if (regenorbital) { // XXX this needs to be fixed so that things like the // draw multiple frames feature will work correctly for Orbitals int frame = mol->frame(); // draw currently active frame const Timestep *ts = mol->get_frame(frame); if (!ts->qm_timestep || !mol->qm_data || !mol->qm_data->num_basis || orbid < 1) { wkf_timer_destroy(timer); return; } // Find the timestep independent wavefunction ID tag // by comparing type, spin, and excitation with the // signatures of existing wavefunctions. int waveid = mol->qm_data->find_wavef_id_from_gui_specs( wavefnctype, wavefncspin, wavefncexcitation); // Translate the wavefunction ID into the index the // wavefunction has in this timestep int iwave = ts->qm_timestep->get_wavef_index(waveid); if (iwave<0 || !ts->qm_timestep->get_wavecoeffs(iwave) || !ts->qm_timestep->get_num_orbitals(iwave) || orbid > ts->qm_timestep->get_num_orbitals(iwave)) { wkf_timer_destroy(timer); return; } // Get the orbital index for this timestep from the orbital ID. int orbindex = ts->qm_timestep->get_orbital_index_from_id(iwave, orbid); // Build an Orbital object and prepare to calculate a grid Orbital *orbital = mol->qm_data->create_orbital(iwave, orbindex, ts->pos, ts->qm_timestep); // Set the bounding box of the atom coordinates as the grid dimensions orbital->set_grid_to_bbox(ts->pos, 3.0, gridspacing); // XXX needs more testing, can get stuck for certain orbitals #if 0 // XXX for GPU, we need to only optimize to a stepsize of 4 or more, as // otherwise doing this actually slows us down rather than speeding up // orbital.find_optimal_grid(0.01, 4, 8); // // optimize: minstep 2, maxstep 8, threshold 0.01 orbital->find_optimal_grid(0.01, 2, 8); #endif // Calculate the molecular orbital orbital->calculate_mo(mol, density); motime = wkf_timer_timenow(timer); // query orbital grid origin, dimensions, and axes const int *numvoxels = orbital->get_numvoxels(); const float *origin = orbital->get_origin(); float xaxis[3], yaxis[3], zaxis[3]; orbital->get_grid_axes(xaxis, yaxis, zaxis); // build a VolumetricData object for rendering char dataname[64]; sprintf(dataname, "molecular orbital %i", orbid); // update attributes of cached orbital grid orbgridisdensity = density; waveftype = wavefnctype; wavefspin = wavefncspin; wavefexcitation = wavefncexcitation; gridorbid = orbid; orbgridspacing = gridspacing; delete orbvol; orbvol = new VolumetricData(dataname, origin, xaxis, yaxis, zaxis, numvoxels[0], numvoxels[1], numvoxels[2], orbital->get_grid_data()); delete orbital; voltime = wkf_timer_timenow(timer); orbvol->compute_volume_gradient(); // calc gradients: smooth vertex normals gradtime = wkf_timer_timenow(timer); } // regen the orbital grid... // draw the newly created VolumetricData object sprintf(commentBuffer, "MoleculeID: %d ReprID: %d Beginning Orbital", mol->id(), repNumber); cmdCommentX.putdata(commentBuffer, cmdList); if (drawbox > 0) { // don't texture the box if color by volume is active if (atomColor->method() == AtomColor::VOLUME) { append(DVOLTEXOFF); } // wireframe only? or solid? if (style > 0 || drawbox == 2) { draw_volume_box_lines(orbvol); } else { draw_volume_box_solid(orbvol); } if (atomColor->method() == AtomColor::VOLUME) { append(DVOLTEXON); } } if ((drawbox == 2) || (drawbox == 0)) { switch (style) { case 3: // shaded points isosurface looping over X-axis, 1 point per voxel draw_volume_isosurface_lit_points(orbvol, isovalue, stepsize, thickness); break; case 2: // points isosurface looping over X-axis, max of 1 point per voxel draw_volume_isosurface_points(orbvol, isovalue, stepsize, thickness); break; case 1: // lines implementation, max of 18 line per voxel (3-per triangle) draw_volume_isosurface_lines(orbvol, isovalue, stepsize, thickness); break; case 0: default: // trimesh polygonalized surface, max of 6 triangles per voxel draw_volume_isosurface_trimesh(orbvol, isovalue, stepsize); break; } } if (regenorbital) { double surftime = wkf_timer_timenow(timer); if (surftime > 5) { char strmsg[1024]; sprintf(strmsg, "Total MO rep time: %.3f [MO: %.3f vol: %.3f grad: %.3f surf: %.2f]", surftime, motime, voltime - motime, gradtime - motime, surftime - gradtime); msgInfo << strmsg << sendmsg; } } wkf_timer_destroy(timer); }
// Extract the isosurface from the QuickSurf density map int QuickSurf::get_trimesh(int &numverts, float *&v3fv, float *&n3fv, float *&c3fv, int &numfacets, int *&fiv) { #if !defined(ARCH_BLUEWATERS) printf("Running marching cubes on CPU...\n"); #endif VolumetricData *surfvol; ///< Container used to generate isosurface on the CPU surfvol = new VolumetricData("molecular surface", origin, xaxis, yaxis, zaxis, numvoxels[0], numvoxels[1], numvoxels[2], volmap); // XXX we should calculate the volume gradient only for those // vertices we extract, since for this rep any changes to settings // will require recomputation of the entire volume surfvol->compute_volume_gradient(); // calc gradients: smooth vertex normals gradtime = wkf_timer_timenow(timer); // trimesh polygonalized surface, max of 6 triangles per voxel const int stepsize = 1; s.clear(); // initialize isosurface data s.compute(surfvol, isovalue, stepsize); // compute the isosurface mctime = wkf_timer_timenow(timer); s.vertexfusion(surfvol, 9, 9); // identify and eliminate duplicated vertices s.normalize(); // normalize interpolated gradient/surface normals if (s.numtriangles > 0) { if (voltexmap != NULL) { // assign per-vertex colors by a 3-D texture map s.set_color_voltex_rgb3fv(voltexmap); } else { // use a single color for the entire mesh s.set_color_rgb3fv(solidcolor); } } numverts = s.v.num() / 3; v3fv=&s.v[0]; n3fv=&s.n[0]; c3fv=&s.c[0]; numfacets = s.numtriangles; fiv=&s.f[0]; delete surfvol; mcverttime = wkf_timer_timenow(timer); reptime = mcverttime; #if !defined(ARCH_BLUEWATERS) char strmsg[1024]; sprintf(strmsg, "QuickSurf: %.3f [pre:%.3f vol:%.3f gr:%.3f mc:%.2f mcv:%.3f]", reptime, pretime, voltime-pretime, gradtime-voltime, mctime-gradtime, mcverttime-mctime); msgInfo << strmsg << sendmsg; #endif return 0; }
CoorData::CoorDataState CoorPluginData::next(Molecule *m) { if (!plugin) return DONE; if (is_input) { if (recentFrame < 0) { recentFrame = 0; while (recentFrame < begFrame) { plugin->skip(m); recentFrame++; } } else { for (int i=1; i<frameSkip; i++) plugin->skip(m); recentFrame += frameSkip; } if (endFrame < 0 || recentFrame <= endFrame) { Timestep *ts = plugin->next(m); if (ts) { m->append_frame(ts); totalframes++; return NOTDONE; } } } else if (m->numframes() > 0) { // output if (recentFrame < 0) recentFrame = begFrame; else recentFrame += frameSkip; // get next frame, and write to file if ((endFrame < 0 || recentFrame <= endFrame) && m->numframes() > recentFrame) { Timestep *ts = m->get_frame(recentFrame); if (ts) { if (!plugin->write_timestep(ts, selection)) { totalframes++; return NOTDONE; } else { msgErr << "write_timestep returned nonzero" << sendmsg; } } } } if (tm != NULL && totalframes > 0) { double iotime = wkf_timer_timenow(tm); // emit I/O stats if requested, or it took more than 3 seconds if ((getenv("VMDTSTIMER") != NULL) || (iotime > 3.0)) { float framerate = (float) (((double) totalframes) / iotime); char tmbuf[1024], frameratebuf[1024]; sprintf(tmbuf, "%.1f", iotime); if (framerate > 10) sprintf(frameratebuf, "%.1f", framerate); else sprintf(frameratebuf, "%.2f", framerate); msgInfo << "Coordinate I/O rate " << frameratebuf << " frames/sec, " << (int) (((totalframes * kbytesperframe) / 1024.0) / iotime) << " MB/sec, " << tmbuf << " sec" << sendmsg; } } // we're done; close file and stop reading/writing delete plugin; plugin = NULL; return DONE; }