static void do_lincs(rvec *x,rvec *xp,matrix box,t_pbc *pbc, struct gmx_lincsdata *lincsd,real *invmass, t_commrec *cr, real wangle,int *warn, real invdt,rvec *v, gmx_bool bCalcVir,tensor rmdr) { int b,i,j,k,n,iter; real tmp0,tmp1,tmp2,im1,im2,mvb,rlen,len,len2,dlen2,wfac,lam; rvec dx; int ncons,*bla,*blnr,*blbnb; rvec *r; real *blc,*blmf,*bllen,*blcc,*rhs1,*rhs2,*sol,*lambda; int *nlocat; ncons = lincsd->nc; bla = lincsd->bla; r = lincsd->tmpv; blnr = lincsd->blnr; blbnb = lincsd->blbnb; blc = lincsd->blc; blmf = lincsd->blmf; bllen = lincsd->bllen; blcc = lincsd->tmpncc; rhs1 = lincsd->tmp1; rhs2 = lincsd->tmp2; sol = lincsd->tmp3; lambda = lincsd->lambda; if (DOMAINDECOMP(cr) && cr->dd->constraints) { nlocat = dd_constraints_nlocalatoms(cr->dd); } else if (PARTDECOMP(cr)) { nlocat = pd_constraints_nlocalatoms(cr->pd); } else { nlocat = NULL; } *warn = 0; if (pbc) { /* Compute normalized i-j vectors */ for(b=0; b<ncons; b++) { pbc_dx_aiuc(pbc,x[bla[2*b]],x[bla[2*b+1]],dx); unitv(dx,r[b]); } for(b=0; b<ncons; b++) { for(n=blnr[b]; n<blnr[b+1]; n++) { blcc[n] = blmf[n]*iprod(r[b],r[blbnb[n]]); } pbc_dx_aiuc(pbc,xp[bla[2*b]],xp[bla[2*b+1]],dx); mvb = blc[b]*(iprod(r[b],dx) - bllen[b]); rhs1[b] = mvb; sol[b] = mvb; } } else { /* Compute normalized i-j vectors */ for(b=0; b<ncons; b++) { i = bla[2*b]; j = bla[2*b+1]; tmp0 = x[i][0] - x[j][0]; tmp1 = x[i][1] - x[j][1]; tmp2 = x[i][2] - x[j][2]; rlen = gmx_invsqrt(tmp0*tmp0+tmp1*tmp1+tmp2*tmp2); r[b][0] = rlen*tmp0; r[b][1] = rlen*tmp1; r[b][2] = rlen*tmp2; } /* 16 ncons flops */ for(b=0; b<ncons; b++) { tmp0 = r[b][0]; tmp1 = r[b][1]; tmp2 = r[b][2]; len = bllen[b]; i = bla[2*b]; j = bla[2*b+1]; for(n=blnr[b]; n<blnr[b+1]; n++) { k = blbnb[n]; blcc[n] = blmf[n]*(tmp0*r[k][0] + tmp1*r[k][1] + tmp2*r[k][2]); } /* 6 nr flops */ mvb = blc[b]*(tmp0*(xp[i][0] - xp[j][0]) + tmp1*(xp[i][1] - xp[j][1]) + tmp2*(xp[i][2] - xp[j][2]) - len); rhs1[b] = mvb; sol[b] = mvb; /* 10 flops */ } /* Together: 26*ncons + 6*nrtot flops */ } lincs_matrix_expand(lincsd,blcc,rhs1,rhs2,sol); /* nrec*(ncons+2*nrtot) flops */ for(b=0; b<ncons; b++) { i = bla[2*b]; j = bla[2*b+1]; mvb = blc[b]*sol[b]; lambda[b] = -mvb; im1 = invmass[i]; im2 = invmass[j]; tmp0 = r[b][0]*mvb; tmp1 = r[b][1]*mvb; tmp2 = r[b][2]*mvb; xp[i][0] -= tmp0*im1; xp[i][1] -= tmp1*im1; xp[i][2] -= tmp2*im1; xp[j][0] += tmp0*im2; xp[j][1] += tmp1*im2; xp[j][2] += tmp2*im2; } /* 16 ncons flops */ /* ******** Correction for centripetal effects ******** */ wfac = cos(DEG2RAD*wangle); wfac = wfac*wfac; for(iter=0; iter<lincsd->nIter; iter++) { if (DOMAINDECOMP(cr) && cr->dd->constraints) { /* Communicate the corrected non-local coordinates */ dd_move_x_constraints(cr->dd,box,xp,NULL); } else if (PARTDECOMP(cr)) { pd_move_x_constraints(cr,xp,NULL); } for(b=0; b<ncons; b++) { len = bllen[b]; if (pbc) { pbc_dx_aiuc(pbc,xp[bla[2*b]],xp[bla[2*b+1]],dx); } else { rvec_sub(xp[bla[2*b]],xp[bla[2*b+1]],dx); } len2 = len*len; dlen2 = 2*len2 - norm2(dx); if (dlen2 < wfac*len2 && (nlocat==NULL || nlocat[b])) { *warn = b; } if (dlen2 > 0) { mvb = blc[b]*(len - dlen2*gmx_invsqrt(dlen2)); } else { mvb = blc[b]*len; } rhs1[b] = mvb; sol[b] = mvb; } /* 20*ncons flops */ lincs_matrix_expand(lincsd,blcc,rhs1,rhs2,sol); /* nrec*(ncons+2*nrtot) flops */ for(b=0; b<ncons; b++) { i = bla[2*b]; j = bla[2*b+1]; lam = lambda[b]; mvb = blc[b]*sol[b]; lambda[b] = lam - mvb; im1 = invmass[i]; im2 = invmass[j]; tmp0 = r[b][0]*mvb; tmp1 = r[b][1]*mvb; tmp2 = r[b][2]*mvb; xp[i][0] -= tmp0*im1; xp[i][1] -= tmp1*im1; xp[i][2] -= tmp2*im1; xp[j][0] += tmp0*im2; xp[j][1] += tmp1*im2; xp[j][2] += tmp2*im2; } /* 17 ncons flops */ } /* nit*ncons*(37+9*nrec) flops */ if (v) { /* Correct the velocities */ for(b=0; b<ncons; b++) { i = bla[2*b]; j = bla[2*b+1]; im1 = invmass[i]*lambda[b]*invdt; im2 = invmass[j]*lambda[b]*invdt; v[i][0] += im1*r[b][0]; v[i][1] += im1*r[b][1]; v[i][2] += im1*r[b][2]; v[j][0] -= im2*r[b][0]; v[j][1] -= im2*r[b][1]; v[j][2] -= im2*r[b][2]; } /* 16 ncons flops */ } if (nlocat) { /* Only account for local atoms */ for(b=0; b<ncons; b++) { lambda[b] *= 0.5*nlocat[b]; } } if (bCalcVir) { /* Constraint virial */ for(b=0; b<ncons; b++) { tmp0 = bllen[b]*lambda[b]; for(i=0; i<DIM; i++) { tmp1 = tmp0*r[b][i]; for(j=0; j<DIM; j++) { rmdr[i][j] -= tmp1*r[b][j]; } } } /* 22 ncons flops */ } /* Total: * 26*ncons + 6*nrtot + nrec*(ncons+2*nrtot) * + nit * (20*ncons + nrec*(ncons+2*nrtot) + 17 ncons) * * (26+nrec)*ncons + (6+2*nrec)*nrtot * + nit * ((37+nrec)*ncons + 2*nrec*nrtot) * if nit=1 * (63+nrec)*ncons + (6+4*nrec)*nrtot */ }
gmx_bool constrain(FILE *fplog, gmx_bool bLog, gmx_bool bEner, struct gmx_constr *constr, t_idef *idef, t_inputrec *ir, gmx_ekindata_t *ekind, t_commrec *cr, gmx_int64_t step, int delta_step, t_mdatoms *md, rvec *x, rvec *xprime, rvec *min_proj, gmx_bool bMolPBC, matrix box, real lambda, real *dvdlambda, rvec *v, tensor *vir, t_nrnb *nrnb, int econq, gmx_bool bPscal, real veta, real vetanew) { gmx_bool bOK, bDump; int start, homenr, nrend; int i, j, d; int ncons, settle_error; tensor vir_r_m_dr; rvec *vstor; real invdt, vir_fac, t; t_ilist *settle; int nsettle; t_pbc pbc, *pbc_null; char buf[22]; t_vetavars vetavar; int nth, th; if (econq == econqForceDispl && !EI_ENERGY_MINIMIZATION(ir->eI)) { gmx_incons("constrain called for forces displacements while not doing energy minimization, can not do this while the LINCS and SETTLE constraint connection matrices are mass weighted"); } bOK = TRUE; bDump = FALSE; start = 0; homenr = md->homenr; nrend = start+homenr; /* set constants for pressure control integration */ init_vetavars(&vetavar, econq != econqCoord, veta, vetanew, ir, ekind, bPscal); if (ir->delta_t == 0) { invdt = 0; } else { invdt = 1/ir->delta_t; } if (ir->efep != efepNO && EI_DYNAMICS(ir->eI)) { /* Set the constraint lengths for the step at which this configuration * is meant to be. The invmasses should not be changed. */ lambda += delta_step*ir->fepvals->delta_lambda; } if (vir != NULL) { clear_mat(vir_r_m_dr); } where(); settle = &idef->il[F_SETTLE]; nsettle = settle->nr/(1+NRAL(F_SETTLE)); if (nsettle > 0) { nth = gmx_omp_nthreads_get(emntSETTLE); } else { nth = 1; } if (nth > 1 && constr->vir_r_m_dr_th == NULL) { snew(constr->vir_r_m_dr_th, nth); snew(constr->settle_error, nth); } settle_error = -1; /* We do not need full pbc when constraints do not cross charge groups, * i.e. when dd->constraint_comm==NULL. * Note that PBC for constraints is different from PBC for bondeds. * For constraints there is both forward and backward communication. */ if (ir->ePBC != epbcNONE && (cr->dd || bMolPBC) && !(cr->dd && cr->dd->constraint_comm == NULL)) { /* With pbc=screw the screw has been changed to a shift * by the constraint coordinate communication routine, * so that here we can use normal pbc. */ pbc_null = set_pbc_dd(&pbc, ir->ePBC, cr->dd, FALSE, box); } else { pbc_null = NULL; } /* Communicate the coordinates required for the non-local constraints * for LINCS and/or SETTLE. */ if (cr->dd) { dd_move_x_constraints(cr->dd, box, x, xprime, econq == econqCoord); } if (constr->lincsd != NULL) { bOK = constrain_lincs(fplog, bLog, bEner, ir, step, constr->lincsd, md, cr, x, xprime, min_proj, box, pbc_null, lambda, dvdlambda, invdt, v, vir != NULL, vir_r_m_dr, econq, nrnb, constr->maxwarn, &constr->warncount_lincs); if (!bOK && constr->maxwarn >= 0) { if (fplog != NULL) { fprintf(fplog, "Constraint error in algorithm %s at step %s\n", econstr_names[econtLINCS], gmx_step_str(step, buf)); } bDump = TRUE; } } if (constr->nblocks > 0) { switch (econq) { case (econqCoord): bOK = bshakef(fplog, constr->shaked, md->invmass, constr->nblocks, constr->sblock, idef, ir, x, xprime, nrnb, constr->lagr, lambda, dvdlambda, invdt, v, vir != NULL, vir_r_m_dr, constr->maxwarn >= 0, econq, &vetavar); break; case (econqVeloc): bOK = bshakef(fplog, constr->shaked, md->invmass, constr->nblocks, constr->sblock, idef, ir, x, min_proj, nrnb, constr->lagr, lambda, dvdlambda, invdt, NULL, vir != NULL, vir_r_m_dr, constr->maxwarn >= 0, econq, &vetavar); break; default: gmx_fatal(FARGS, "Internal error, SHAKE called for constraining something else than coordinates"); break; } if (!bOK && constr->maxwarn >= 0) { if (fplog != NULL) { fprintf(fplog, "Constraint error in algorithm %s at step %s\n", econstr_names[econtSHAKE], gmx_step_str(step, buf)); } bDump = TRUE; } } if (nsettle > 0) { int calcvir_atom_end; if (vir == NULL) { calcvir_atom_end = 0; } else { calcvir_atom_end = md->homenr; } switch (econq) { case econqCoord: #pragma omp parallel for num_threads(nth) schedule(static) for (th = 0; th < nth; th++) { int start_th, end_th; if (th > 0) { clear_mat(constr->vir_r_m_dr_th[th]); } start_th = (nsettle* th )/nth; end_th = (nsettle*(th+1))/nth; if (start_th >= 0 && end_th - start_th > 0) { csettle(constr->settled, end_th-start_th, settle->iatoms+start_th*(1+NRAL(F_SETTLE)), pbc_null, x[0], xprime[0], invdt, v ? v[0] : NULL, calcvir_atom_end, th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th], th == 0 ? &settle_error : &constr->settle_error[th], &vetavar); } } inc_nrnb(nrnb, eNR_SETTLE, nsettle); if (v != NULL) { inc_nrnb(nrnb, eNR_CONSTR_V, nsettle*3); } if (vir != NULL) { inc_nrnb(nrnb, eNR_CONSTR_VIR, nsettle*3); } break; case econqVeloc: case econqDeriv: case econqForce: case econqForceDispl: #pragma omp parallel for num_threads(nth) schedule(static) for (th = 0; th < nth; th++) { int start_th, end_th; if (th > 0) { clear_mat(constr->vir_r_m_dr_th[th]); } start_th = (nsettle* th )/nth; end_th = (nsettle*(th+1))/nth; if (start_th >= 0 && end_th - start_th > 0) { settle_proj(constr->settled, econq, end_th-start_th, settle->iatoms+start_th*(1+NRAL(F_SETTLE)), pbc_null, x, xprime, min_proj, calcvir_atom_end, th == 0 ? vir_r_m_dr : constr->vir_r_m_dr_th[th], &vetavar); } } /* This is an overestimate */ inc_nrnb(nrnb, eNR_SETTLE, nsettle); break; case econqDeriv_FlexCon: /* Nothing to do, since the are no flexible constraints in settles */ break; default: gmx_incons("Unknown constraint quantity for settle"); } } if (settle->nr > 0) { /* Combine virial and error info of the other threads */ for (i = 1; i < nth; i++) { m_add(vir_r_m_dr, constr->vir_r_m_dr_th[i], vir_r_m_dr); settle_error = constr->settle_error[i]; } if (econq == econqCoord && settle_error >= 0) { bOK = FALSE; if (constr->maxwarn >= 0) { char buf[256]; sprintf(buf, "\nstep " "%"GMX_PRId64 ": Water molecule starting at atom %d can not be " "settled.\nCheck for bad contacts and/or reduce the timestep if appropriate.\n", step, ddglatnr(cr->dd, settle->iatoms[settle_error*(1+NRAL(F_SETTLE))+1])); if (fplog) { fprintf(fplog, "%s", buf); } fprintf(stderr, "%s", buf); constr->warncount_settle++; if (constr->warncount_settle > constr->maxwarn) { too_many_constraint_warnings(-1, constr->warncount_settle); } bDump = TRUE; } } } free_vetavars(&vetavar); if (vir != NULL) { switch (econq) { case econqCoord: vir_fac = 0.5/(ir->delta_t*ir->delta_t); break; case econqVeloc: vir_fac = 0.5/ir->delta_t; break; case econqForce: case econqForceDispl: vir_fac = 0.5; break; default: vir_fac = 0; gmx_incons("Unsupported constraint quantity for virial"); } if (EI_VV(ir->eI)) { vir_fac *= 2; /* only constraining over half the distance here */ } for (i = 0; i < DIM; i++) { for (j = 0; j < DIM; j++) { (*vir)[i][j] = vir_fac*vir_r_m_dr[i][j]; } } } if (bDump) { dump_confs(fplog, step, constr->warn_mtop, start, homenr, cr, x, xprime, box); } if (econq == econqCoord) { if (ir->ePull == epullCONSTRAINT) { if (EI_DYNAMICS(ir->eI)) { t = ir->init_t + (step + delta_step)*ir->delta_t; } else { t = ir->init_t; } set_pbc(&pbc, ir->ePBC, box); pull_constraint(ir->pull, md, &pbc, cr, ir->delta_t, t, x, xprime, v, *vir); } if (constr->ed && delta_step > 0) { /* apply the essential dynamcs constraints here */ do_edsam(ir, step, cr, xprime, v, box, constr->ed); } } return bOK; }
gmx_bool constrain_lincs(FILE *fplog,gmx_bool bLog,gmx_bool bEner, t_inputrec *ir, gmx_large_int_t step, struct gmx_lincsdata *lincsd,t_mdatoms *md, t_commrec *cr, rvec *x,rvec *xprime,rvec *min_proj,matrix box, real lambda,real *dvdlambda, real invdt,rvec *v, gmx_bool bCalcVir,tensor rmdr, int econq, t_nrnb *nrnb, int maxwarn,int *warncount) { char buf[STRLEN],buf2[22],buf3[STRLEN]; int i,warn,p_imax,error; real ncons_loc,p_ssd,p_max; t_pbc pbc,*pbc_null; rvec dx; gmx_bool bOK; bOK = TRUE; if (lincsd->nc == 0 && cr->dd == NULL) { if (bLog || bEner) { lincsd->rmsd_data[0] = 0; if (ir->eI == eiSD2 && v == NULL) { i = 2; } else { i = 1; } lincsd->rmsd_data[i] = 0; } return bOK; } /* We do not need full pbc when constraints do not cross charge groups, * i.e. when dd->constraint_comm==NULL */ if ((cr->dd || ir->bPeriodicMols) && !(cr->dd && cr->dd->constraint_comm==NULL)) { /* With pbc=screw the screw has been changed to a shift * by the constraint coordinate communication routine, * so that here we can use normal pbc. */ pbc_null = set_pbc_dd(&pbc,ir->ePBC,cr->dd,FALSE,box); } else { pbc_null = NULL; } if (cr->dd) { /* Communicate the coordinates required for the non-local constraints */ dd_move_x_constraints(cr->dd,box,x,xprime); /* dump_conf(dd,lincsd,NULL,"con",TRUE,xprime,box); */ } else if (PARTDECOMP(cr)) { pd_move_x_constraints(cr,x,xprime); } if (econq == econqCoord) { if (ir->efep != efepNO) { if (md->nMassPerturbed && lincsd->matlam != md->lambda) { set_lincs_matrix(lincsd,md->invmass,md->lambda); } for(i=0; i<lincsd->nc; i++) { lincsd->bllen[i] = lincsd->bllen0[i] + lambda*lincsd->ddist[i]; } } if (lincsd->ncg_flex) { /* Set the flexible constraint lengths to the old lengths */ if (pbc_null) { for(i=0; i<lincsd->nc; i++) { if (lincsd->bllen[i] == 0) { pbc_dx_aiuc(pbc_null,x[lincsd->bla[2*i]],x[lincsd->bla[2*i+1]],dx); lincsd->bllen[i] = norm(dx); } } } else { for(i=0; i<lincsd->nc; i++) { if (lincsd->bllen[i] == 0) { lincsd->bllen[i] = sqrt(distance2(x[lincsd->bla[2*i]], x[lincsd->bla[2*i+1]])); } } } } if (bLog && fplog) { cconerr(cr->dd,lincsd->nc,lincsd->bla,lincsd->bllen,xprime,pbc_null, &ncons_loc,&p_ssd,&p_max,&p_imax); } do_lincs(x,xprime,box,pbc_null,lincsd,md->invmass,cr, ir->LincsWarnAngle,&warn, invdt,v,bCalcVir,rmdr); if (ir->efep != efepNO) { real dt_2,dvdl=0; dt_2 = 1.0/(ir->delta_t*ir->delta_t); for(i=0; (i<lincsd->nc); i++) { dvdl += lincsd->lambda[i]*dt_2*lincsd->ddist[i]; } *dvdlambda += dvdl; } if (bLog && fplog && lincsd->nc > 0) { fprintf(fplog," Rel. Constraint Deviation: RMS MAX between atoms\n"); fprintf(fplog," Before LINCS %.6f %.6f %6d %6d\n", sqrt(p_ssd/ncons_loc),p_max, ddglatnr(cr->dd,lincsd->bla[2*p_imax]), ddglatnr(cr->dd,lincsd->bla[2*p_imax+1])); } if (bLog || bEner) { cconerr(cr->dd,lincsd->nc,lincsd->bla,lincsd->bllen,xprime,pbc_null, &ncons_loc,&p_ssd,&p_max,&p_imax); /* Check if we are doing the second part of SD */ if (ir->eI == eiSD2 && v == NULL) { i = 2; } else { i = 1; } lincsd->rmsd_data[0] = ncons_loc; lincsd->rmsd_data[i] = p_ssd; } else { lincsd->rmsd_data[0] = 0; lincsd->rmsd_data[1] = 0; lincsd->rmsd_data[2] = 0; } if (bLog && fplog && lincsd->nc > 0) { fprintf(fplog, " After LINCS %.6f %.6f %6d %6d\n\n", sqrt(p_ssd/ncons_loc),p_max, ddglatnr(cr->dd,lincsd->bla[2*p_imax]), ddglatnr(cr->dd,lincsd->bla[2*p_imax+1])); } if (warn > 0) { if (maxwarn >= 0) { cconerr(cr->dd,lincsd->nc,lincsd->bla,lincsd->bllen,xprime,pbc_null, &ncons_loc,&p_ssd,&p_max,&p_imax); if (MULTISIM(cr)) { sprintf(buf3," in simulation %d", cr->ms->sim); } else { buf3[0] = 0; } sprintf(buf,"\nStep %s, time %g (ps) LINCS WARNING%s\n" "relative constraint deviation after LINCS:\n" "rms %.6f, max %.6f (between atoms %d and %d)\n", gmx_step_str(step,buf2),ir->init_t+step*ir->delta_t, buf3, sqrt(p_ssd/ncons_loc),p_max, ddglatnr(cr->dd,lincsd->bla[2*p_imax]), ddglatnr(cr->dd,lincsd->bla[2*p_imax+1])); if (fplog) { fprintf(fplog,"%s",buf); } fprintf(stderr,"%s",buf); lincs_warning(fplog,cr->dd,x,xprime,pbc_null, lincsd->nc,lincsd->bla,lincsd->bllen, ir->LincsWarnAngle,maxwarn,warncount); } bOK = (p_max < 0.5); } if (lincsd->ncg_flex) { for(i=0; (i<lincsd->nc); i++) if (lincsd->bllen0[i] == 0 && lincsd->ddist[i] == 0) lincsd->bllen[i] = 0; } } else { do_lincsp(x,xprime,min_proj,pbc_null,lincsd,md->invmass,econq,dvdlambda, bCalcVir,rmdr); } /* count assuming nit=1 */ inc_nrnb(nrnb,eNR_LINCS,lincsd->nc); inc_nrnb(nrnb,eNR_LINCSMAT,(2+lincsd->nOrder)*lincsd->ncc); if (lincsd->ntriangle > 0) { inc_nrnb(nrnb,eNR_LINCSMAT,lincsd->nOrder*lincsd->ncc_triangle); } if (v) { inc_nrnb(nrnb,eNR_CONSTR_V,lincsd->nc*2); } if (bCalcVir) { inc_nrnb(nrnb,eNR_CONSTR_VIR,lincsd->nc); } return bOK; }