// Compute matrix that transforms coordinates from an arbitrary PBC cell // into an orthonormal unitcell. Since the cell origin is not stored by VMD // you have to specify it. int measure_pbc2onc(MoleculeList *mlist, int molid, int frame, const float origin[3], Matrix4 &transform) { int orig_ts, max_ts; Molecule *mol = mlist->mol_from_id(molid); if( !mol ) return MEASURE_ERR_NOMOLECULE; // get current frame number and make sure there are frames if((orig_ts = mol->frame()) < 0) return MEASURE_ERR_NOFRAMES; // get the max frame number and determine frame range max_ts = mol->numframes()-1; if (frame==-2) frame = orig_ts; else if (frame>max_ts || frame==-1) frame = max_ts; Timestep *ts = mol->get_frame(frame); Matrix4 AA, BB, CC; ts->get_transforms(AA, BB, CC); // Construct the cell spanning vectors float cell[9]; cell[0] = AA.mat[12]; cell[1] = AA.mat[13]; cell[2] = AA.mat[14]; cell[3] = BB.mat[12]; cell[4] = BB.mat[13]; cell[5] = BB.mat[14]; cell[6] = CC.mat[12]; cell[7] = CC.mat[13]; cell[8] = CC.mat[14]; get_transform_to_orthonormal_cell(cell, origin, transform); return MEASURE_NOERR; }
int measure_pbc_neighbors(MoleculeList *mlist, AtomSel *sel, int molid, int frame, const Matrix4 *alignment, const float *center, const float *cutoff, const float *box, ResizeArray<float> *extcoord_array, ResizeArray<int> *indexmap_array) { int orig_ts, max_ts; if (!box && !cutoff[0] && !cutoff[1] && !cutoff[2]) return MEASURE_NOERR; Molecule *mol = mlist->mol_from_id(molid); if( !mol ) return MEASURE_ERR_NOMOLECULE; // get current frame number and make sure there are frames if((orig_ts = mol->frame()) < 0) return MEASURE_ERR_NOFRAMES; // get the max frame number and determine current frame max_ts = mol->numframes()-1; if (frame==-2) frame = orig_ts; else if (frame>max_ts || frame==-1) frame = max_ts; Timestep *ts = mol->get_frame(frame); if (!ts) return MEASURE_ERR_NOMOLECULE; // Get the displacement vectors (in form of translation matrices) Matrix4 Tpbc[3][2]; ts->get_transforms(Tpbc[0][1], Tpbc[1][1], Tpbc[2][1]); // Assign the negative cell translation vectors Tpbc[0][0] = Tpbc[0][1]; Tpbc[1][0] = Tpbc[1][1]; Tpbc[2][0] = Tpbc[2][1]; Tpbc[0][0].inverse(); Tpbc[1][0].inverse(); Tpbc[2][0].inverse(); // Construct the cell spanning vectors float cell[9]; cell[0] = Tpbc[0][1].mat[12]; cell[1] = Tpbc[0][1].mat[13]; cell[2] = Tpbc[0][1].mat[14]; cell[3] = Tpbc[1][1].mat[12]; cell[4] = Tpbc[1][1].mat[13]; cell[5] = Tpbc[1][1].mat[14]; cell[6] = Tpbc[2][1].mat[12]; cell[7] = Tpbc[2][1].mat[13]; cell[8] = Tpbc[2][1].mat[14]; float len[3]; len[0] = sqrtf(dot_prod(&cell[0], &cell[0])); len[1] = sqrtf(dot_prod(&cell[3], &cell[3])); len[2] = sqrtf(dot_prod(&cell[6], &cell[6])); //printf("len={%.3f %.3f %.3f}\n", len[0], len[1], len[2]); int i; float minlen = len[0]; if (len[1] && len[1]<minlen) minlen = len[1]; if (len[2] && len[2]<minlen) minlen = len[2]; minlen--; // The algorithm works only for atoms in adjacent neighbor cells. if (!box && (cutoff[0]>=len[0] || cutoff[1]>=len[1] || cutoff[2]>=len[2])) { return MEASURE_ERR_BADCUTOFF; } bool bigrim = 1; float corecell[9]; float diag[3]; float origin[3]; memset(origin, 0, 3*sizeof(float)); Matrix4 M_norm; if (box) { // Get the matrix M_norm that transforms all atoms inside the // unit cell into the normalized unitcell spanned by // {1/len[0] 0 0} {0 1/len[1] 0} {0 0 1/len[2]}. bigrim = 1; float vtmp[3]; vec_add(vtmp, &cell[0], &cell[3]); vec_add(diag, &cell[6], vtmp); //printf("diag={%.3f %.3f %.3f}\n", diag[0], diag[1], diag[2]); // Finally we need to apply the translation of the cell origin vec_copy(origin, center); vec_scaled_add(origin, -0.5, &cell[0]); vec_scaled_add(origin, -0.5, &cell[3]); vec_scaled_add(origin, -0.5, &cell[6]); vec_negate(origin, origin); //printf("origin={%.3f %.3f %.3f}\n", origin[0], origin[1], origin[2]); } else if (2.0f*cutoff[0]<minlen && 2.0f*cutoff[1]<minlen && 2.0f*cutoff[2]<minlen) { // The cutoff must not be larger than half of the smallest cell dimension // otherwise we would have to use a less efficient algorithm. // Get the matrix M_norm that transforms all atoms inside the // corecell into the orthonormal unitcell spanned by {1 0 0} {0 1 0} {0 0 1}. // The corecell ist the pbc cell minus cutoffs for each dimension. vec_scale(&corecell[0], (len[0]-cutoff[0])/len[0], &cell[0]); vec_scale(&corecell[3], (len[1]-cutoff[1])/len[1], &cell[3]); vec_scale(&corecell[6], (len[2]-cutoff[2])/len[2], &cell[6]); get_transform_to_orthonormal_cell(corecell, center, M_norm); //printf("Using algorithm for small PBC environment.\n"); } else { // Get the matrix M_norm that transforms all atoms inside the // unit cell into the orthonormal unitcell spanned by {1 0 0} {0 1 0} {0 0 1}. get_transform_to_orthonormal_cell(cell, center, M_norm); bigrim = 1; //printf("Using algorithm for large PBC environment.\n"); } // In case the molecule was aligned our pbc cell is rotated and shifted. // In order to transform a point P into the orthonormal cell (P') it // first has to be unaligned (the inverse of the alignment): // P' = M_norm * (alignment^-1) * P Matrix4 alignmentinv(*alignment); alignmentinv.inverse(); Matrix4 M_coretransform(M_norm); M_coretransform.multmatrix(alignmentinv); //printf("alignment = \n"); //print_Matrix4(alignment); // Similarly if we want to transform a point P into its image P' we // first have to unalign it, then apply the PBC translation and // finally realign: // P' = alignment * Tpbc * (alignment^-1) * P // `-------------v--------------' // transform int j, u; Matrix4 Tpbc_aligned[3][2]; if (!box) { for (i=0; i<3; i++) { for (j=0; j<2; j++) { Tpbc_aligned[i][j].loadmatrix(*alignment); Tpbc_aligned[i][j].multmatrix(Tpbc[i][j]); Tpbc_aligned[i][j].multmatrix(alignmentinv); } } } Matrix4 M[3]; float *coords = ts->pos; float *coor; float orthcoor[3], wrapcoor[3]; //printf("cutoff={%.3f %.3f %.3f}\n", cutoff[0], cutoff[1], cutoff[2]); if (box) { float min_coord[3], max_coord[3]; // Increase box by cutoff vec_sub(min_coord, box, cutoff); vec_add(max_coord, box+3, cutoff); //printf("Wrapping atoms into rectangular bounding box.\n"); //printf("min_coord={%.3f %.3f %.3f}\n", min_coord[0], min_coord[1], min_coord[2]); //printf("max_coord={%.3f %.3f %.3f}\n", max_coord[0], max_coord[1], max_coord[2]); vec_add(min_coord, min_coord, origin); vec_add(max_coord, max_coord, origin); float testcoor[9]; int idx, k; // Loop over all atoms for (idx=0; idx<ts->num; idx++) { coor = coords+3*idx; // Apply the inverse alignment transformation // to the current test point. M_coretransform.multpoint3d(coor, orthcoor); // Loop over all 26 neighbor cells // x for (i=-1; i<=1; i++) { // Choose the direction of translation if (i>0) M[0].loadmatrix(Tpbc[0][1]); else if (i<0) M[0].loadmatrix(Tpbc[0][0]); else M[0].identity(); // Translate the unaligned atom M[0].multpoint3d(orthcoor, testcoor); // y for (j=-1; j<=1; j++) { // Choose the direction of translation if (j>0) M[1].loadmatrix(Tpbc[1][1]); else if (j<0) M[1].loadmatrix(Tpbc[1][0]); else M[1].identity(); // Translate the unaligned atom M[1].multpoint3d(testcoor, testcoor+3); // z for (k=-1; k<=1; k++) { if(i==0 && j==0 && k==0) continue; // Choose the direction of translation if (k>0) M[2].loadmatrix(Tpbc[2][1]); else if (k<0) M[2].loadmatrix(Tpbc[2][0]); else M[2].identity(); // Translate the unaligned atom M[2].multpoint3d(testcoor+3, testcoor+6); // Realign atom alignment->multpoint3d(testcoor+6, wrapcoor); vec_add(testcoor+6, wrapcoor, origin); if (testcoor[6]<min_coord[0] || testcoor[6]>max_coord[0]) continue; if (testcoor[7]<min_coord[1] || testcoor[7]>max_coord[1]) continue; if (testcoor[8]<min_coord[2] || testcoor[8]>max_coord[2]) continue; // Atom is inside cutoff, add it to the list for (int n=0; n<3; n++) extcoord_array->append(wrapcoor[n]); indexmap_array->append(idx); } } } } } else if (bigrim) { // This is the more general but slower algorithm. // We loop over all atoms, move each atom to all 26 neighbor cells // and check if it lies inside cutoff float min_coord[3], max_coord[3]; min_coord[0] = -cutoff[0]/len[0]; min_coord[1] = -cutoff[1]/len[1]; min_coord[2] = -cutoff[2]/len[2]; max_coord[0] = 1.0f + cutoff[0]/len[0]; max_coord[1] = 1.0f + cutoff[1]/len[1]; max_coord[2] = 1.0f + cutoff[2]/len[2]; float testcoor[3]; int idx, k; // Loop over all atoms for (idx=0; idx<ts->num; idx++) { coor = coords+3*idx; // Apply the PBC --> orthonormal unitcell transformation // to the current test point. M_coretransform.multpoint3d(coor, orthcoor); // Loop over all 26 neighbor cells // x for (i=-1; i<=1; i++) { testcoor[0] = orthcoor[0]+(float)(i); if (testcoor[0]<min_coord[0] || testcoor[0]>max_coord[0]) continue; // Choose the direction of translation if (i>0) M[0].loadmatrix(Tpbc_aligned[0][1]); else if (i<0) M[0].loadmatrix(Tpbc_aligned[0][0]); else M[0].identity(); // y for (j=-1; j<=1; j++) { testcoor[1] = orthcoor[1]+(float)(j); if (testcoor[1]<min_coord[1] || testcoor[1]>max_coord[1]) continue; // Choose the direction of translation if (j>0) M[1].loadmatrix(Tpbc_aligned[1][1]); else if (j<0) M[1].loadmatrix(Tpbc_aligned[1][0]); else M[1].identity(); // z for (k=-1; k<=1; k++) { testcoor[2] = orthcoor[2]+(float)(k); if (testcoor[2]<min_coord[2] || testcoor[2]>max_coord[2]) continue; if(i==0 && j==0 && k==0) continue; // Choose the direction of translation if (k>0) M[2].loadmatrix(Tpbc_aligned[2][1]); else if (k<0) M[2].loadmatrix(Tpbc_aligned[2][0]); else M[2].identity(); M[0].multpoint3d(coor, wrapcoor); M[1].multpoint3d(wrapcoor, wrapcoor); M[2].multpoint3d(wrapcoor, wrapcoor); // Atom is inside cutoff, add it to the list for (int n=0; n<3; n++) extcoord_array->append(wrapcoor[n]); indexmap_array->append(idx); } } } } } else { Matrix4 Mtmp; for (i=0; i < ts->num; i++) { // Apply the PBC --> orthonormal unitcell transformation // to the current test point. M_coretransform.multpoint3d(coords+3*i, orthcoor); // Determine in which cell we are. int cellindex[3]; if (orthcoor[0]<0) cellindex[0] = -1; else if (orthcoor[0]>1) cellindex[0] = 1; else cellindex[0] = 0; if (orthcoor[1]<0) cellindex[1] = -1; else if (orthcoor[1]>1) cellindex[1] = 1; else cellindex[1] = 0; if (orthcoor[2]<0) cellindex[2] = -1; else if (orthcoor[2]>1) cellindex[2] = 1; else cellindex[2] = 0; // All zero means we're inside the core --> no image. if (!cellindex[0] && !cellindex[1] && !cellindex[2]) continue; // Choose the direction of translation if (orthcoor[0]<0) M[0].loadmatrix(Tpbc_aligned[0][1]); else if (orthcoor[0]>1) M[0].loadmatrix(Tpbc_aligned[0][0]); if (orthcoor[1]<0) M[1].loadmatrix(Tpbc_aligned[1][1]); else if (orthcoor[1]>1) M[1].loadmatrix(Tpbc_aligned[1][0]); if (orthcoor[2]<0) M[2].loadmatrix(Tpbc_aligned[2][1]); else if (orthcoor[2]>1) M[2].loadmatrix(Tpbc_aligned[2][0]); // Create wrapped copies of the atom: // x, y, z planes coor = coords+3*i; for (u=0; u<3; u++) { if (cellindex[u] && cutoff[u]) { M[u].multpoint3d(coor, wrapcoor); for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]); indexmap_array->append(i); } } Mtmp = M[0]; // xy edge if (cellindex[0] && cellindex[1] && cutoff[0] && cutoff[1]) { M[0].multmatrix(M[1]); M[0].multpoint3d(coor, wrapcoor); for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]); indexmap_array->append(i); } // yz edge if (cellindex[1] && cellindex[2] && cutoff[1] && cutoff[2]) { M[1].multmatrix(M[2]); M[1].multpoint3d(coor, wrapcoor); for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]); indexmap_array->append(i); } // zx edge if (cellindex[0] && cellindex[2] && cutoff[0] && cutoff[2]) { M[2].multmatrix(Mtmp); M[2].multpoint3d(coor, wrapcoor); for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]); indexmap_array->append(i); } // xyz corner if (cellindex[0] && cellindex[1] && cellindex[2]) { M[1].multmatrix(Mtmp); M[1].multpoint3d(coor, wrapcoor); for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]); indexmap_array->append(i); } } } // endif // If a selection was provided we select extcoords // within cutoff of the original selection: if (sel) { int numext = sel->selected+indexmap_array->num(); float *extcoords = new float[3*numext]; int *indexmap = new int[numext]; int *others = new int[numext]; memset(others, 0, numext); // Use the largest given cutoff float maxcutoff = cutoff[0]; for (i=1; i<3; i++) { if (cutoff[i]>maxcutoff) maxcutoff = cutoff[i]; } // Prepare C-array of coordinates for find_within() j=0; for (i=0; i < sel->num_atoms; i++) { if (!sel->on[i]) continue; //atom is not selected extcoords[3*j] = coords[3*i]; extcoords[3*j+1] = coords[3*i+1]; extcoords[3*j+2] = coords[3*i+2]; indexmap[j] = i; others[j++] = 1; } for (i=0; i<indexmap_array->num(); i++) { extcoords[3*j] = (*extcoord_array)[3*i]; extcoords[3*j+1] = (*extcoord_array)[3*i+1]; extcoords[3*j+2] = (*extcoord_array)[3*i+2]; indexmap[j] = (*indexmap_array)[i]; others[j++] = 0; } // Initialize flags array to true, find_within() results are AND'd/OR'd in. int *flgs = new int[numext]; for (i=0; i<numext; i++) { flgs[i] = 1; } // Find coordinates from extcoords that are within cutoff of the ones // with flagged in 'others' and set the flgs accordingly: find_within(extcoords, flgs, others, numext, maxcutoff); extcoord_array->clear(); indexmap_array->clear(); for (i=sel->selected; i<numext; i++) { if (!flgs[i]) continue; extcoord_array->append(extcoords[3*i]); extcoord_array->append(extcoords[3*i+1]); extcoord_array->append(extcoords[3*i+2]); indexmap_array->append(indexmap[i]); } } return MEASURE_NOERR; }