static void calc_x_times_f(int nxf, const rvec x[], const rvec f[], gmx_bool bScrewPBC, const matrix box, matrix x_times_f) { clear_mat(x_times_f); for (int i = 0; i < nxf; i++) { for (int d = 0; d < DIM; d++) { for (int n = 0; n < DIM; n++) { x_times_f[d][n] += x[i][d]*f[i][n]; } } if (bScrewPBC) { int isx = IS2X(i); /* We should correct all odd x-shifts, but the range of isx is -2 to 2 */ if (isx == 1 || isx == -1) { for (int d = 0; d < DIM; d++) { for (int n = 0; n < DIM; n++) { x_times_f[d][n] += box[d][d]*f[i][n]; } } } } } }
void calc_vir(int nxf, rvec x[], rvec f[], tensor vir, gmx_bool bScrewPBC, matrix box) { int i; double dvxx = 0, dvxy = 0, dvxz = 0, dvyx = 0, dvyy = 0, dvyz = 0, dvzx = 0, dvzy = 0, dvzz = 0; #pragma omp parallel for num_threads(gmx_omp_nthreads_get(emntDefault)) \ schedule(static) \ reduction(+: dvxx, dvxy, dvxz, dvyx, dvyy, dvyz, dvzx, dvzy, dvzz) for (i = 0; i < nxf; i++) { dvxx += x[i][XX]*f[i][XX]; dvxy += x[i][XX]*f[i][YY]; dvxz += x[i][XX]*f[i][ZZ]; dvyx += x[i][YY]*f[i][XX]; dvyy += x[i][YY]*f[i][YY]; dvyz += x[i][YY]*f[i][ZZ]; dvzx += x[i][ZZ]*f[i][XX]; dvzy += x[i][ZZ]*f[i][YY]; dvzz += x[i][ZZ]*f[i][ZZ]; if (bScrewPBC) { int isx = IS2X(i); /* We should correct all odd x-shifts, but the range of isx is -2 to 2 */ if (isx == 1 || isx == -1) { dvyy += box[YY][YY]*f[i][YY]; dvyz += box[YY][YY]*f[i][ZZ]; dvzy += box[ZZ][ZZ]*f[i][YY]; dvzz += box[ZZ][ZZ]*f[i][ZZ]; } } } upd_vir(vir[XX], dvxx, dvxy, dvxz); upd_vir(vir[YY], dvyx, dvyy, dvyz); upd_vir(vir[ZZ], dvzx, dvzy, dvzz); }
void calc_vir(int nxf, rvec x[], rvec f[], tensor vir, gmx_bool bScrewPBC, matrix box) { int i, isx; double dvxx = 0, dvxy = 0, dvxz = 0, dvyx = 0, dvyy = 0, dvyz = 0, dvzx = 0, dvzy = 0, dvzz = 0; for (i = 0; (i < nxf); i++) { dvxx += x[i][XX]*f[i][XX]; dvxy += x[i][XX]*f[i][YY]; dvxz += x[i][XX]*f[i][ZZ]; dvyx += x[i][YY]*f[i][XX]; dvyy += x[i][YY]*f[i][YY]; dvyz += x[i][YY]*f[i][ZZ]; dvzx += x[i][ZZ]*f[i][XX]; dvzy += x[i][ZZ]*f[i][YY]; dvzz += x[i][ZZ]*f[i][ZZ]; if (bScrewPBC) { isx = IS2X(i); /* We should correct all odd x-shifts, but the range of isx is -2 to 2 */ if (isx == 1 || isx == -1) { dvyy += box[YY][YY]*f[i][YY]; dvyz += box[YY][YY]*f[i][ZZ]; dvzy += box[ZZ][ZZ]*f[i][YY]; dvzz += box[ZZ][ZZ]*f[i][ZZ]; } } } upd_vir(vir[XX], dvxx, dvxy, dvxz); upd_vir(vir[YY], dvyx, dvyy, dvyz); upd_vir(vir[ZZ], dvzx, dvzy, dvzz); }
void update_QMMMrec(t_commrec *cr, t_forcerec *fr, rvec x[], t_mdatoms *md, matrix box, gmx_localtop_t *top) { /* updates the coordinates of both QM atoms and MM atoms and stores * them in the QMMMrec. * * NOTE: is NOT yet working if there are no PBC. Also in ns.c, simple * ns needs to be fixed! */ int mm_max=0,mm_nr=0,mm_nr_new,i,j,is,k,shift; t_j_particle *mm_j_particles=NULL,*qm_i_particles=NULL; t_QMMMrec *qr; t_nblist QMMMlist; rvec dx,crd; int *MMatoms; t_QMrec *qm; t_MMrec *mm; t_pbc pbc; int *parallelMMarray=NULL; real c12au,c6au; c6au = (HARTREE2KJ*AVOGADRO*pow(BOHR2NM,6)); c12au = (HARTREE2KJ*AVOGADRO*pow(BOHR2NM,12)); /* every cpu has this array. On every processor we fill this array * with 1's and 0's. 1's indicate the atoms is a QM atom on the * current cpu in a later stage these arrays are all summed. indexes * > 0 indicate the atom is a QM atom. Every node therefore knows * whcih atoms are part of the QM subsystem. */ /* copy some pointers */ qr = fr->qr; mm = qr->mm; QMMMlist = fr->QMMMlist; /* init_pbc(box); needs to be called first, see pbc.h */ set_pbc_dd(&pbc,fr->ePBC,DOMAINDECOMP(cr) ? cr->dd : NULL,FALSE,box); /* only in standard (normal) QMMM we need the neighbouring MM * particles to provide a electric field of point charges for the QM * atoms. */ if(qr->QMMMscheme==eQMMMschemenormal){ /* also implies 1 QM-layer */ /* we NOW create/update a number of QMMMrec entries: * * 1) the shiftQM, containing the shifts of the QM atoms * * 2) the indexMM array, containing the index of the MM atoms * * 3) the shiftMM, containing the shifts of the MM atoms * * 4) the shifted coordinates of the MM atoms * * the shifts are used for computing virial of the QM/MM particles. */ qm = qr->qm[0]; /* in case of normal QMMM, there is only one group */ snew(qm_i_particles,QMMMlist.nri); if(QMMMlist.nri){ qm_i_particles[0].shift = XYZ2IS(0,0,0); for(i=0;i<QMMMlist.nri;i++){ qm_i_particles[i].j = QMMMlist.iinr[i]; if(i){ qm_i_particles[i].shift = pbc_dx_aiuc(&pbc,x[QMMMlist.iinr[0]], x[QMMMlist.iinr[i]],dx); } /* However, since nri >= nrQMatoms, we do a quicksort, and throw * out double, triple, etc. entries later, as we do for the MM * list too. */ /* compute the shift for the MM j-particles with respect to * the QM i-particle and store them. */ crd[0] = IS2X(QMMMlist.shift[i]) + IS2X(qm_i_particles[i].shift); crd[1] = IS2Y(QMMMlist.shift[i]) + IS2Y(qm_i_particles[i].shift); crd[2] = IS2Z(QMMMlist.shift[i]) + IS2Z(qm_i_particles[i].shift); is = XYZ2IS(crd[0],crd[1],crd[2]); for(j=QMMMlist.jindex[i]; j<QMMMlist.jindex[i+1]; j++){ if(mm_nr >= mm_max){ mm_max += 1000; srenew(mm_j_particles,mm_max); } mm_j_particles[mm_nr].j = QMMMlist.jjnr[j]; mm_j_particles[mm_nr].shift = is; mm_nr++; } } /* quicksort QM and MM shift arrays and throw away multiple entries */ qsort(qm_i_particles,QMMMlist.nri, (size_t)sizeof(qm_i_particles[0]), struct_comp); qsort(mm_j_particles,mm_nr, (size_t)sizeof(mm_j_particles[0]), struct_comp); /* remove multiples in the QM shift array, since in init_QMMM() we * went through the atom numbers from 0 to md.nr, the order sorted * here matches the one of QMindex already. */ j=0; for(i=0;i<QMMMlist.nri;i++){ if (i==0 || qm_i_particles[i].j!=qm_i_particles[i-1].j){ qm_i_particles[j++] = qm_i_particles[i]; } } mm_nr_new = 0; if(qm->bTS||qm->bOPT){ /* only remove double entries for the MM array */ for(i=0;i<mm_nr;i++){ if((i==0 || mm_j_particles[i].j!=mm_j_particles[i-1].j) && !md->bQM[mm_j_particles[i].j]){ mm_j_particles[mm_nr_new++] = mm_j_particles[i]; } } } /* we also remove mm atoms that have no charges! * actually this is already done in the ns.c */ else{ for(i=0;i<mm_nr;i++){ if((i==0 || mm_j_particles[i].j!=mm_j_particles[i-1].j) && !md->bQM[mm_j_particles[i].j] && (md->chargeA[mm_j_particles[i].j] || (md->chargeB && md->chargeB[mm_j_particles[i].j]))) { mm_j_particles[mm_nr_new++] = mm_j_particles[i]; } } } mm_nr = mm_nr_new; /* store the data retrieved above into the QMMMrec */ k=0; /* Keep the compiler happy, * shift will always be set in the loop for i=0 */ shift = 0; for(i=0;i<qm->nrQMatoms;i++){ /* not all qm particles might have appeared as i * particles. They might have been part of the same charge * group for instance. */ if (qm->indexQM[i] == qm_i_particles[k].j) { shift = qm_i_particles[k++].shift; } /* use previous shift, assuming they belong the same charge * group anyway, */ qm->shiftQM[i] = shift; } } /* parallel excecution */ if(PAR(cr)){ snew(parallelMMarray,2*(md->nr)); /* only MM particles have a 1 at their atomnumber. The second part * of the array contains the shifts. Thus: * p[i]=1/0 depending on wether atomnumber i is a MM particle in the QM * step or not. p[i+md->nr] is the shift of atomnumber i. */ for(i=0;i<2*(md->nr);i++){ parallelMMarray[i]=0; } for(i=0;i<mm_nr;i++){ parallelMMarray[mm_j_particles[i].j]=1; parallelMMarray[mm_j_particles[i].j+(md->nr)]=mm_j_particles[i].shift; } gmx_sumi(md->nr,parallelMMarray,cr); mm_nr=0; mm_max = 0; for(i=0;i<md->nr;i++){ if(parallelMMarray[i]){ if(mm_nr >= mm_max){ mm_max += 1000; srenew(mm->indexMM,mm_max); srenew(mm->shiftMM,mm_max); } mm->indexMM[mm_nr] = i; mm->shiftMM[mm_nr++]= parallelMMarray[i+md->nr]/parallelMMarray[i]; } } mm->nrMMatoms=mm_nr; free(parallelMMarray); } /* serial execution */ else{ mm->nrMMatoms = mm_nr; srenew(mm->shiftMM,mm_nr); srenew(mm->indexMM,mm_nr); for(i=0;i<mm_nr;i++){ mm->indexMM[i]=mm_j_particles[i].j; mm->shiftMM[i]=mm_j_particles[i].shift; } } /* (re) allocate memory for the MM coordiate array. The QM * coordinate array was already allocated in init_QMMM, and is * only (re)filled in the update_QMMM_coordinates routine */ srenew(mm->xMM,mm->nrMMatoms); /* now we (re) fill the array that contains the MM charges with * the forcefield charges. If requested, these charges will be * scaled by a factor */ srenew(mm->MMcharges,mm->nrMMatoms); for(i=0;i<mm->nrMMatoms;i++){/* no free energy yet */ mm->MMcharges[i]=md->chargeA[mm->indexMM[i]]*mm->scalefactor; } if(qm->bTS||qm->bOPT){ /* store (copy) the c6 and c12 parameters into the MMrec struct */ srenew(mm->c6,mm->nrMMatoms); srenew(mm->c12,mm->nrMMatoms); for (i=0;i<mm->nrMMatoms;i++){ mm->c6[i] = C6(fr->nbfp,top->idef.atnr, md->typeA[mm->indexMM[i]], md->typeA[mm->indexMM[i]])/c6au; mm->c12[i] =C12(fr->nbfp,top->idef.atnr, md->typeA[mm->indexMM[i]], md->typeA[mm->indexMM[i]])/c12au; } punch_QMMM_excl(qr->qm[0],mm,&(top->excls)); } /* the next routine fills the coordinate fields in the QMMM rec of * both the qunatum atoms and the MM atoms, using the shifts * calculated above. */ update_QMMM_coord(x,fr,qr->qm[0],qr->mm); free(qm_i_particles); free(mm_j_particles); } else { /* ONIOM */ /* ????? */ mm->nrMMatoms=0; /* do for each layer */ for (j=0;j<qr->nrQMlayers;j++){ qm = qr->qm[j]; qm->shiftQM[0]=XYZ2IS(0,0,0); for(i=1;i<qm->nrQMatoms;i++){ qm->shiftQM[i] = pbc_dx_aiuc(&pbc,x[qm->indexQM[0]],x[qm->indexQM[i]], dx); } update_QMMM_coord(x,fr,qm,mm); } } } /* update_QMMM_rec */