aad6a64a7c5483dc8093d5eded1089e199b146df
[alexxy/gromacs.git] / src / tools / addconf.c
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  *                        VERSION 3.2.0
10  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12  * Copyright (c) 2001-2004, The GROMACS development team,
13  * check out http://www.gromacs.org for more information.
14
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * If you want to redistribute modifications, please consider that
21  * scientific software is very special. Version control is crucial -
22  * bugs must be traceable. We will be happy to consider code for
23  * inclusion in the official distribution, but derived work must not
24  * be called official GROMACS. Details are found in the README & COPYING
25  * files - if they are missing, get the official version at www.gromacs.org.
26  *
27  * To help us fund GROMACS development, we humbly ask that you cite
28  * the papers on the package - you can find them in the top README file.
29  *
30  * For more info, check our website at http://www.gromacs.org
31  *
32  * And Hey:
33  * Green Red Orange Magenta Azure Cyan Skyblue
34  */
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include <stdlib.h>
40 #include <string.h>
41 #include "vec.h"
42 #include "macros.h"
43 #include "smalloc.h"
44 #include "addconf.h"
45 #include "force.h"
46 #include "gstat.h"
47 #include "names.h"
48 #include "nsgrid.h"
49 #include "mdatoms.h"
50 #include "nrnb.h"
51 #include "ns.h"
52 #include "mtop_util.h"
53 #include "chargegroup.h"
54
55 static real box_margin;
56
57 static real max_dist(rvec *x, real *r, int start, int end)
58 {
59   real maxd;
60   int i,j;
61
62   maxd=0;
63   for(i=start; i<end; i++)
64     for(j=i+1; j<end; j++)
65       maxd=max(maxd,sqrt(distance2(x[i],x[j]))+0.5*(r[i]+r[j]));
66
67   return 0.5*maxd;
68 }
69
70 static gmx_bool outside_box_minus_margin2(rvec x,matrix box)
71 {
72   return ( (x[XX]<2*box_margin) || (x[XX]>box[XX][XX]-2*box_margin) ||
73            (x[YY]<2*box_margin) || (x[YY]>box[YY][YY]-2*box_margin) ||
74            (x[ZZ]<2*box_margin) || (x[ZZ]>box[ZZ][ZZ]-2*box_margin) );
75 }
76
77 static gmx_bool outside_box_plus_margin(rvec x,matrix box)
78 {
79   return ( (x[XX]<-box_margin) || (x[XX]>box[XX][XX]+box_margin) ||
80            (x[YY]<-box_margin) || (x[YY]>box[YY][YY]+box_margin) ||
81            (x[ZZ]<-box_margin) || (x[ZZ]>box[ZZ][ZZ]+box_margin) );
82 }
83
84 static int mark_res(int at, gmx_bool *mark, int natoms, t_atom *atom,int *nmark)
85 {
86   int resind;
87
88   resind = atom[at].resind;
89   while( (at > 0) && (resind==atom[at-1].resind) )
90     at--;
91   while( (at < natoms) && (resind==atom[at].resind) ) {
92     if (!mark[at]) {
93       mark[at]=TRUE;
94       (*nmark)++;
95     }
96     at++;
97   }
98
99   return at;
100 }
101
102 static real find_max_real(int n,real radius[])
103 {
104   int  i;
105   real rmax;
106
107   rmax = 0;
108   if (n > 0) {
109     rmax = radius[0];
110     for(i=1; (i<n); i++)
111       rmax = max(rmax,radius[i]);
112   }
113   return rmax;
114 }
115
116 static void combine_atoms(t_atoms *ap,t_atoms *as,
117                           rvec xp[],rvec *vp,rvec xs[],rvec *vs,
118                           t_atoms **a_comb,rvec **x_comb,rvec **v_comb)
119 {
120   t_atoms *ac;
121   rvec    *xc,*vc=NULL;
122   int     i,j,natot,res0;
123
124   /* Total number of atoms */
125   natot = ap->nr+as->nr;
126
127   snew(ac,1);
128   init_t_atoms(ac,natot,FALSE);
129
130   snew(xc,natot);
131   if (vp && vs) snew(vc,natot);
132
133   /* Fill the new structures */
134   for(i=j=0; (i<ap->nr); i++,j++) {
135     copy_rvec(xp[i],xc[j]);
136     if (vc) copy_rvec(vp[i],vc[j]);
137     memcpy(&(ac->atom[j]),&(ap->atom[i]),sizeof(ap->atom[i]));
138     ac->atom[j].type = 0;
139   }
140   res0 = ap->nres;
141   for(i=0; (i<as->nr); i++,j++) {
142     copy_rvec(xs[i],xc[j]);
143     if (vc) copy_rvec(vs[i],vc[j]);
144     memcpy(&(ac->atom[j]),&(as->atom[i]),sizeof(as->atom[i]));
145     ac->atom[j].type    = 0;
146     ac->atom[j].resind += res0;
147   }
148   ac->nr   = j;
149   ac->nres = ac->atom[j-1].resind+1;
150   /* Fill all elements to prevent uninitialized memory */
151   for(i=0; i<ac->nr; i++) {
152     ac->atom[i].m     = 1;
153     ac->atom[i].q     = 0;
154     ac->atom[i].mB    = 1;
155     ac->atom[i].qB    = 0;
156     ac->atom[i].type  = 0;
157     ac->atom[i].typeB = 0;
158     ac->atom[i].ptype = eptAtom;
159   }
160
161   /* Return values */
162   *a_comb = ac;
163   *x_comb = xc;
164   *v_comb = vc;
165 }
166
167 static t_forcerec *fr=NULL;
168
169 void do_nsgrid(FILE *fp,gmx_bool bVerbose,
170                matrix box,rvec x[],t_atoms *atoms,real rlong,
171                const output_env_t oenv)
172 {
173   gmx_mtop_t *mtop;
174   gmx_localtop_t *top;
175   t_mdatoms  *md;
176   t_block    *cgs;
177   t_inputrec *ir;
178   t_nrnb     nrnb;
179   t_commrec  *cr;
180   int        *cg_index;
181   gmx_moltype_t *molt;
182   gmx_ffparams_t *ffp;
183   ivec       *nFreeze;
184   int        i,m,natoms;
185   rvec       box_size;
186   real       *lambda,*dvdl;
187
188   natoms = atoms->nr;
189
190   /* Charge group index */
191   snew(cg_index,natoms);
192   for(i=0; (i<natoms); i++)
193     cg_index[i]=i;
194
195   /* Topology needs charge groups and exclusions */
196   snew(mtop,1);
197   init_mtop(mtop);
198   mtop->natoms = natoms;
199   /* Make one moltype that contains the whol system */
200   mtop->nmoltype = 1;
201   snew(mtop->moltype,mtop->nmoltype);
202   molt = &mtop->moltype[0];
203   molt->name  = mtop->name;
204   molt->atoms = *atoms;
205   stupid_fill_block(&molt->cgs,mtop->natoms,FALSE);
206   stupid_fill_blocka(&molt->excls,natoms);
207   /* Make one molblock for the whole system */
208   mtop->nmolblock = 1;
209   snew(mtop->molblock,mtop->nmolblock);
210   mtop->molblock[0].type = 0;
211   mtop->molblock[0].nmol = 1;
212   mtop->molblock[0].natoms_mol = natoms;
213   /* Initialize a single energy group */
214   mtop->groups.grps[egcENER].nr = 1;
215   mtop->groups.ngrpnr[egcENER]  = 0;
216   mtop->groups.grpnr[egcENER]   = NULL;
217
218   ffp = &mtop->ffparams;
219
220   ffp->ntypes = 1;
221   ffp->atnr   = 1;
222   ffp->reppow = 12;
223   snew(ffp->functype,1);
224   snew(ffp->iparams,1);
225   ffp->iparams[0].lj.c6  = 1;
226   ffp->iparams[0].lj.c12 = 1;
227
228   /* inputrec structure */
229   snew(ir,1);
230   ir->coulombtype = eelCUT;
231   ir->vdwtype     = evdwCUT;
232   ir->ndelta      = 2;
233   ir->ns_type     = ensGRID;
234   snew(ir->opts.egp_flags,1);
235
236   top = gmx_mtop_generate_local_top(mtop,ir);
237
238   /* Some nasty shortcuts */
239   cgs  = &(top->cgs);
240
241     /* mdatoms structure */
242   snew(nFreeze,2);
243   snew(md,1);
244   md = init_mdatoms(fp,mtop,FALSE);
245   atoms2md(mtop,ir,0,NULL,0,mtop->natoms,md);
246   sfree(nFreeze);
247
248   /* forcerec structure */
249   if (fr == NULL)
250     fr = mk_forcerec();
251   snew(cr,1);
252   cr->nnodes   = 1;
253   /* cr->nthreads = 1; */
254
255   /*    ir->rlist       = ir->rcoulomb = ir->rvdw = rlong;
256         printf("Neighborsearching with a cut-off of %g\n",rlong);
257         init_forcerec(stdout,fr,ir,top,cr,md,box,FALSE,NULL,NULL,NULL,TRUE);*/
258   fr->cg0 = 0;
259   fr->hcg = top->cgs.nr;
260   fr->nWatMol = 0;
261
262   /* Prepare for neighboursearching */
263   init_nrnb(&nrnb);
264
265   /* Init things dependent on parameters */
266   ir->rlistlong = ir->rlist = ir->rcoulomb = ir->rvdw = rlong;
267   /* create free energy data to avoid NULLs */
268   snew(ir->fepvals,1);
269   printf("Neighborsearching with a cut-off of %g\n",rlong);
270   init_forcerec(stdout,oenv,fr,NULL,ir,mtop,cr,box,FALSE,NULL,NULL,NULL,NULL,
271                 TRUE,-1);
272   if (debug)
273     pr_forcerec(debug,fr,cr);
274
275   /* Calculate new stuff dependent on coords and box */
276   for(m=0; (m<DIM); m++)
277     box_size[m] = box[m][m];
278   calc_shifts(box,fr->shift_vec);
279   put_charge_groups_in_box(fp,0,cgs->nr,fr->ePBC,box,cgs,x,fr->cg_cm);
280
281   /* Do the actual neighboursearching */
282   snew(lambda,efptNR);
283   snew(dvdl,efptNR);
284   init_neighbor_list(fp,fr,md->homenr);
285   search_neighbours(fp,fr,x,box,top,
286                     &mtop->groups,cr,&nrnb,md,lambda,dvdl,NULL,
287                     TRUE,FALSE,FALSE,NULL);
288
289   if (debug)
290     dump_nblist(debug,cr,fr,0);
291
292   if (bVerbose)
293     fprintf(stderr,"Successfully made neighbourlist\n");
294 }
295
296 gmx_bool bXor(gmx_bool b1,gmx_bool b2)
297 {
298   return (b1 && !b2) || (b2 && !b1);
299 }
300
301 void add_conf(t_atoms *atoms, rvec **x, rvec **v, real **r, gmx_bool bSrenew,
302               int ePBC, matrix box, gmx_bool bInsert,
303               t_atoms *atoms_solvt,rvec *x_solvt,rvec *v_solvt,real *r_solvt,
304               gmx_bool bVerbose,real rshell,int max_sol, const output_env_t oenv)
305 {
306   t_nblist   *nlist;
307   t_atoms    *atoms_all;
308   real       max_vdw,*r_prot,*r_all,n2,r2,ib1,ib2;
309   int        natoms_prot,natoms_solvt;
310   int        i,j,jj,m,j0,j1,jjj,jnres,jnr,inr,iprot,is1,is2;
311   int        prev,resnr,nresadd,d,k,ncells,maxincell;
312   int        dx0,dx1,dy0,dy1,dz0,dz1;
313   int        ntest,nremove,nkeep;
314   rvec       dx,xi,xj,xpp,*x_all,*v_all;
315   gmx_bool       *remove,*keep;
316   int        bSolSol;
317
318   natoms_prot  = atoms->nr;
319   natoms_solvt = atoms_solvt->nr;
320   if (natoms_solvt <= 0) {
321     fprintf(stderr,"WARNING: Nothing to add\n");
322     return;
323   }
324
325   if (ePBC == epbcSCREW)
326     gmx_fatal(FARGS,"Sorry, %s pbc is not yet supported",epbc_names[ePBC]);
327
328   if (bVerbose)
329     fprintf(stderr,"Calculating Overlap...\n");
330
331   /* Set margin around box edges to largest solvent dimension.
332    * The maximum distance between atoms in a solvent molecule should
333    * be calculated. At the moment a fudge factor of 3 is used.
334    */
335   r_prot     = *r;
336   box_margin = 3*find_max_real(natoms_solvt,r_solvt);
337   max_vdw    = max(3*find_max_real(natoms_prot,r_prot),box_margin);
338   fprintf(stderr,"box_margin = %g\n",box_margin);
339
340   snew(remove,natoms_solvt);
341
342   nremove = 0;
343   if (!bInsert) {
344     for(i=0; i<atoms_solvt->nr; i++)
345       if ( outside_box_plus_margin(x_solvt[i],box) )
346         i=mark_res(i,remove,atoms_solvt->nr,atoms_solvt->atom,&nremove);
347     fprintf(stderr,"Removed %d atoms that were outside the box\n",nremove);
348   }
349
350   /* Define grid stuff for genbox */
351   /* Largest VDW radius */
352   snew(r_all,natoms_prot+natoms_solvt);
353   for(i=j=0; i<natoms_prot; i++,j++)
354     r_all[j]=r_prot[i];
355   for(i=0; i<natoms_solvt; i++,j++)
356     r_all[j]=r_solvt[i];
357
358   /* Combine arrays */
359   combine_atoms(atoms,atoms_solvt,*x,v?*v:NULL,x_solvt,v_solvt,
360                 &atoms_all,&x_all,&v_all);
361
362   /* Do neighboursearching step */
363   do_nsgrid(stdout,bVerbose,box,x_all,atoms_all,max_vdw,oenv);
364
365   /* check solvent with solute */
366   nlist = &(fr->nblists[0].nlist_sr[eNL_VDW]);
367   fprintf(stderr,"nri = %d, nrj = %d\n",nlist->nri,nlist->nrj);
368   for(bSolSol=0; (bSolSol<=(bInsert ? 0 : 1)); bSolSol++) {
369     ntest = nremove = 0;
370     fprintf(stderr,"Checking %s-Solvent overlap:",
371             bSolSol ? "Solvent" : "Protein");
372     for(i=0; (i<nlist->nri && nremove<natoms_solvt); i++) {
373       inr = nlist->iinr[i];
374       j0  = nlist->jindex[i];
375       j1  = nlist->jindex[i+1];
376       rvec_add(x_all[inr],fr->shift_vec[nlist->shift[i]],xi);
377
378       for(j=j0; (j<j1 && nremove<natoms_solvt); j++) {
379         jnr = nlist->jjnr[j];
380         copy_rvec(x_all[jnr],xj);
381
382         /* Check solvent-protein and solvent-solvent */
383         is1 = inr-natoms_prot;
384         is2 = jnr-natoms_prot;
385
386         /* Check if at least one of the atoms is a solvent that is not yet
387          * listed for removal, and if both are solvent, that they are not in the
388          * same residue.
389          */
390         if ((!bSolSol &&
391              bXor((is1 >= 0),(is2 >= 0)) &&  /* One atom is protein */
392              ((is1 < 0) || ((is1 >= 0) && !remove[is1])) &&
393              ((is2 < 0) || ((is2 >= 0) && !remove[is2]))) ||
394
395             (bSolSol  &&
396              (is1 >= 0) && (!remove[is1]) &&   /* is1 is solvent */
397              (is2 >= 0) && (!remove[is2]) &&   /* is2 is solvent */
398              (bInsert || /* when inserting also check inside the box */
399               (outside_box_minus_margin2(x_solvt[is1],box) && /* is1 on edge */
400                outside_box_minus_margin2(x_solvt[is2],box))   /* is2 on edge */
401               ) &&
402              (atoms_solvt->atom[is1].resind !=  /* Not the same residue */
403               atoms_solvt->atom[is2].resind))) {
404
405           ntest++;
406           rvec_sub(xi,xj,dx);
407           n2 = norm2(dx);
408           r2 = sqr(r_all[inr]+r_all[jnr]);
409           if (n2 < r2) {
410             if (bInsert) {
411               nremove = natoms_solvt;
412               for(k=0; k<nremove; k++) {
413                 remove[k] = TRUE;
414               }
415             }
416             /* Need only remove one of the solvents... */
417             if (is2 >= 0)
418               (void) mark_res(is2,remove,natoms_solvt,atoms_solvt->atom,
419                               &nremove);
420             else if (is1 >= 0)
421               (void) mark_res(is1,remove,natoms_solvt,atoms_solvt->atom,
422                               &nremove);
423             else
424               fprintf(stderr,"Neither atom is solvent%d %d\n",is1,is2);
425           }
426         }
427       }
428     }
429     if (!bInsert) {
430       fprintf(stderr," tested %d pairs, removed %d atoms.\n",ntest,nremove);
431     }
432   }
433   if (debug)
434     for(i=0; i<natoms_solvt; i++)
435       fprintf(debug,"remove[%5d] = %s\n",i,bool_names[remove[i]]);
436
437   /* Search again, now with another cut-off */
438   if (rshell > 0) {
439     do_nsgrid(stdout,bVerbose,box,x_all,atoms_all,rshell,oenv);
440     nlist = &(fr->nblists[0].nlist_sr[eNL_VDW]);
441     fprintf(stderr,"nri = %d, nrj = %d\n",nlist->nri,nlist->nrj);
442     nkeep = 0;
443     snew(keep,natoms_solvt);
444     for(i=0; i<nlist->nri; i++) {
445       inr = nlist->iinr[i];
446       j0  = nlist->jindex[i];
447       j1  = nlist->jindex[i+1];
448
449       for(j=j0; j<j1; j++) {
450         jnr = nlist->jjnr[j];
451
452         /* Check solvent-protein and solvent-solvent */
453         is1 = inr-natoms_prot;
454         is2 = jnr-natoms_prot;
455
456         /* Check if at least one of the atoms is a solvent that is not yet
457          * listed for removal, and if both are solvent, that they are not in the
458          * same residue.
459          */
460         if (is1>=0 && is2<0)
461           mark_res(is1,keep,natoms_solvt,atoms_solvt->atom,&nkeep);
462         else if (is1<0 && is2>=0)
463           mark_res(is2,keep,natoms_solvt,atoms_solvt->atom,&nkeep);
464       }
465     }
466     fprintf(stderr,"Keeping %d solvent atoms after proximity check\n",
467             nkeep);
468     for (i=0; i<natoms_solvt; i++)
469       remove[i] = remove[i] || !keep[i];
470     sfree(keep);
471   }
472   /* count how many atoms and residues will be added and make space */
473   if (bInsert) {
474     j     = atoms_solvt->nr;
475     jnres = atoms_solvt->nres;
476   } else {
477     j     = 0;
478     jnres = 0;
479     for (i=0; ((i<atoms_solvt->nr) &&
480                ((max_sol == 0) || (jnres < max_sol))); i++) {
481       if (!remove[i]) {
482         j++;
483         if ((i == 0) ||
484             (atoms_solvt->atom[i].resind != atoms_solvt->atom[i-1].resind))
485           jnres++;
486       }
487     }
488   }
489   if (debug)
490     fprintf(debug,"Will add %d atoms in %d residues\n",j,jnres);
491   if (!bInsert) {
492     /* Flag the remaing solvent atoms to be removed */
493     jjj = atoms_solvt->atom[i-1].resind;
494     for ( ; (i<atoms_solvt->nr); i++) {
495       if (atoms_solvt->atom[i].resind > jjj)
496         remove[i] = TRUE;
497       else
498         j++;
499     }
500   }
501
502   if (bSrenew) {
503     srenew(atoms->resinfo,  atoms->nres+jnres);
504     srenew(atoms->atomname, atoms->nr+j);
505     srenew(atoms->atom,     atoms->nr+j);
506     srenew(*x,              atoms->nr+j);
507     if (v) srenew(*v,       atoms->nr+j);
508     srenew(*r,              atoms->nr+j);
509   }
510
511   /* add the selected atoms_solvt to atoms */
512   if (atoms->nr > 0) {
513     resnr = atoms->resinfo[atoms->atom[atoms->nr-1].resind].nr;
514   } else {
515     resnr = 0;
516   }
517   prev = -1;
518   nresadd = 0;
519   for (i=0; i<atoms_solvt->nr; i++) {
520     if (!remove[i]) {
521       if (prev == -1 ||
522           atoms_solvt->atom[i].resind != atoms_solvt->atom[prev].resind) {
523         nresadd ++;
524         atoms->nres++;
525         resnr++;
526         atoms->resinfo[atoms->nres-1] =
527           atoms_solvt->resinfo[atoms_solvt->atom[i].resind];
528         atoms->resinfo[atoms->nres-1].nr = resnr;
529         /* calculate shift of the solvent molecule using the first atom */
530         copy_rvec(x_solvt[i],dx);
531         put_atoms_in_box(box,1,&dx);
532         rvec_dec(dx,x_solvt[i]);
533       }
534       atoms->atom[atoms->nr] = atoms_solvt->atom[i];
535       atoms->atomname[atoms->nr] = atoms_solvt->atomname[i];
536       rvec_add(x_solvt[i],dx,(*x)[atoms->nr]);
537       if (v) copy_rvec(v_solvt[i],(*v)[atoms->nr]);
538       (*r)[atoms->nr]   = r_solvt[i];
539       atoms->atom[atoms->nr].resind = atoms->nres-1;
540       atoms->nr++;
541       prev=i;
542     }
543   }
544   if (bSrenew)
545     srenew(atoms->resinfo,  atoms->nres+nresadd);
546
547   if (bVerbose)
548     fprintf(stderr,"Added %d molecules\n",nresadd);
549
550   sfree(remove);
551   done_atom(atoms_all);
552   sfree(x_all);
553   sfree(v_all);
554 }