security ci
[physik/posic.git] / moldyn.c
1 /*
2  * moldyn.c - molecular dynamics library main file
3  *
4  * author: Frank Zirkelbach <frank.zirkelbach@physik.uni-augsburg.de>
5  *
6  */
7
8 #define _GNU_SOURCE
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <math.h>
17
18 #include "moldyn.h"
19
20 #include "math/math.h"
21 #include "init/init.h"
22 #include "random/random.h"
23 #include "visual/visual.h"
24 #include "list/list.h"
25
26 int moldyn_usage(char **argv) {
27
28         printf("\n%s usage:\n\n",argv[0]);
29         printf("--- general options ---\n");
30         printf("-E <steps> <file> (log total energy)\n");
31         printf("-M <steps> <file> (log total momentum)\n");
32         printf("-D <steps> <file> (dump total information)\n");
33         printf("-S <steps> <filebase> (single save file)\n");
34         printf("-V <steps> <filebase> (rasmol file)\n");
35         printf("--- physics options ---\n");
36         printf("-T <temperature> [K] (%f)\n",MOLDYN_TEMP);
37         printf("-t <timestep tau> [s] (%.15f)\n",MOLDYN_TAU);
38         printf("-C <cutoff radius> [m] (%.15f)\n",MOLDYN_CUTOFF);
39         printf("-R <runs> (%d)\n",MOLDYN_RUNS);
40         printf(" -- integration algo --\n");
41         printf("  -I <number> (%d)\n",MOLDYN_INTEGRATE_DEFAULT);
42         printf("     0: velocity verlet\n");
43         printf(" -- potential --\n");
44         printf("  -P <number> <param1 param2 ...>\n");
45         printf("     0: harmonic oscillator\n");
46         printf("        param1: spring constant\n");
47         printf("        param2: equilibrium distance\n");
48         printf("     1: lennard jones\n");
49         printf("        param1: epsilon\n");
50         printf("        param2: sigma\n");
51         printf("\n");
52
53         return 0;
54 }
55
56 int moldyn_parse_argv(t_moldyn *moldyn,int argc,char **argv) {
57
58         int i;
59         t_ho_params hop;
60         t_lj_params ljp;
61         t_tersoff_params tp;
62         double s,e;
63
64         memset(moldyn,0,sizeof(t_moldyn));
65
66         /* default values */
67         moldyn->t=MOLDYN_TEMP;
68         moldyn->tau=MOLDYN_TAU;
69         moldyn->time_steps=MOLDYN_RUNS;
70         moldyn->integrate=velocity_verlet;
71         moldyn->potential_force_function=lennard_jones;
72
73         /* parse argv */
74         for(i=1;i<argc;i++) {
75                 if(argv[i][0]=='-') {
76                         switch(argv[i][1]){
77                                 case 'E':
78                                         moldyn->ewrite=atoi(argv[++i]);
79                                         strncpy(moldyn->efb,argv[++i],64);
80                                         break;
81                                 case 'M':
82                                         moldyn->mwrite=atoi(argv[++i]);
83                                         strncpy(moldyn->mfb,argv[++i],64);
84                                         break;
85                                 case 'S':
86                                         moldyn->swrite=atoi(argv[++i]);
87                                         strncpy(moldyn->sfb,argv[++i],64);
88                                         break;
89                                 case 'V':
90                                         moldyn->vwrite=atoi(argv[++i]);
91                                         strncpy(moldyn->vfb,argv[++i],64);
92                                         break;
93                                 case 'T':
94                                         moldyn->t=atof(argv[++i]);
95                                         break;
96                                 case 't':
97                                         moldyn->tau=atof(argv[++i]);
98                                         break;
99                                 case 'C':
100                                         moldyn->cutoff=atof(argv[++i]);
101                                         break;
102                                 case 'R':
103                                         moldyn->time_steps=atoi(argv[++i]);
104                                         break;
105                                 case 'I':
106         /* integration algorithm */
107         switch(atoi(argv[++i])) {
108                 case MOLDYN_INTEGRATE_VERLET:
109                         moldyn->integrate=velocity_verlet;
110                         break;
111                 default:
112                         printf("unknown integration algo %s\n",argv[i]);
113                         moldyn_usage(argv);
114                         return -1;
115         }
116
117                                 case 'P':
118         /* potential + params */
119         switch(atoi(argv[++i])) {
120                 case MOLDYN_POTENTIAL_HO:
121                         hop.spring_constant=atof(argv[++i]);
122                         hop.equilibrium_distance=atof(argv[++i]);
123                         moldyn->pot_params=malloc(sizeof(t_ho_params));
124                         memcpy(moldyn->pot_params,&hop,sizeof(t_ho_params));
125                         moldyn->potential_force_function=harmonic_oscillator;
126                         break;
127                 case MOLDYN_POTENTIAL_LJ:
128                         e=atof(argv[++i]);
129                         s=atof(argv[++i]);
130                         ljp.epsilon4=4*e;
131                         ljp.sigma6=s*s*s*s*s*s;
132                         ljp.sigma12=ljp.sigma6*ljp.sigma6;
133                         moldyn->pot_params=malloc(sizeof(t_lj_params));
134                         memcpy(moldyn->pot_params,&ljp,sizeof(t_lj_params));
135                         moldyn->potential_force_function=lennard_jones;
136                         break;
137                 default:
138                         printf("unknown potential %s\n",argv[i]);
139                         moldyn_usage(argv);
140                         return -1;
141         }
142
143                                 default:
144                                         printf("unknown option %s\n",argv[i]);
145                                         moldyn_usage(argv);
146                                         return -1;
147                         }
148                 } else {
149                         moldyn_usage(argv);
150                         return -1;
151                 }
152         }
153
154         return 0;
155 }
156
157 int moldyn_log_init(t_moldyn *moldyn) {
158
159         moldyn->lvstat=0;
160         t_visual *vis;
161
162         vis=&(moldyn->vis);
163
164         if(moldyn->ewrite) {
165                 moldyn->efd=open(moldyn->efb,O_WRONLY|O_CREAT|O_TRUNC);
166                 if(moldyn->efd<0) {
167                         perror("[moldyn] efd open");
168                         return moldyn->efd;
169                 }
170                 dprintf(moldyn->efd,"# moldyn total energy logfile\n");
171                 moldyn->lvstat|=MOLDYN_LVSTAT_TOTAL_E;
172         }
173
174         if(moldyn->mwrite) {
175                 moldyn->mfd=open(moldyn->mfb,O_WRONLY|O_CREAT|O_TRUNC);
176                 if(moldyn->mfd<0) {
177                         perror("[moldyn] mfd open");
178                         return moldyn->mfd;
179                 }
180                 dprintf(moldyn->mfd,"# moldyn total momentum logfile\n");
181                 moldyn->lvstat|=MOLDYN_LVSTAT_TOTAL_M;
182         }
183
184         if(moldyn->swrite)
185                 moldyn->lvstat|=MOLDYN_LVSTAT_SAVE;
186
187         if((moldyn->vwrite)&&(vis)) {
188                 moldyn->visual=vis;
189                 visual_init(vis,moldyn->vfb);
190                 moldyn->lvstat|=MOLDYN_LVSTAT_VISUAL;
191         }
192
193         moldyn->lvstat|=MOLDYN_LVSTAT_INITIALIZED;
194
195         return 0;
196 }
197
198 int moldyn_log_shutdown(t_moldyn *moldyn) {
199
200         if(moldyn->efd) close(moldyn->efd);
201         if(moldyn->mfd) close(moldyn->efd);
202         if(moldyn->dfd) close(moldyn->efd);
203         if(moldyn->visual) visual_tini(moldyn->visual);
204
205         return 0;
206 }
207
208 int moldyn_init(t_moldyn *moldyn,int argc,char **argv) {
209
210         int ret;
211
212         ret=moldyn_parse_argv(moldyn,argc,argv);
213         if(ret<0) return ret;
214
215         ret=moldyn_log_init(moldyn);
216         if(ret<0) return ret;
217
218         rand_init(&(moldyn->random),NULL,1);
219         moldyn->random.status|=RAND_STAT_VERBOSE;
220
221         moldyn->status=0;
222
223         return 0;
224 }
225
226 int moldyn_shutdown(t_moldyn *moldyn) {
227
228         moldyn_log_shutdown(moldyn);
229         rand_close(&(moldyn->random));
230         free(moldyn->atom);
231
232         return 0;
233 }
234
235 int create_lattice(unsigned char type,int element,double mass,double lc,
236                    int a,int b,int c,t_atom **atom) {
237
238         int count;
239         int ret;
240         t_3dvec origin;
241
242         count=a*b*c;
243
244         if(type==FCC) count*=4;
245         if(type==DIAMOND) count*=8;
246
247         *atom=malloc(count*sizeof(t_atom));
248         if(*atom==NULL) {
249                 perror("malloc (atoms)");
250                 return -1;
251         }
252
253         v3_zero(&origin);
254
255         switch(type) {
256                 case FCC:
257                         ret=fcc_init(a,b,c,lc,*atom,&origin);
258                         break;
259                 case DIAMOND:
260                         ret=diamond_init(a,b,c,lc,*atom,&origin);
261                         break;
262                 default:
263                         printf("unknown lattice type (%02x)\n",type);
264                         return -1;
265         }
266
267         /* debug */
268         if(ret!=count) {
269                 printf("ok, there is something wrong ...\n");
270                 printf("calculated -> %d atoms\n",count);
271                 printf("created -> %d atoms\n",ret);
272                 return -1;
273         }
274
275         while(count) {
276                 (*atom)[count-1].element=element;
277                 (*atom)[count-1].mass=mass;
278                 count-=1;
279         }
280
281         return ret;
282 }
283
284 int destroy_lattice(t_atom *atom) {
285
286         if(atom) free(atom);
287
288         return 0;
289 }
290
291 int thermal_init(t_moldyn *moldyn) {
292
293         /*
294          * - gaussian distribution of velocities
295          * - zero total momentum
296          * - velocity scaling (E = 3/2 N k T), E: kinetic energy
297          */
298
299         int i;
300         double v,sigma;
301         t_3dvec p_total,delta;
302         t_atom *atom;
303         t_random *random;
304
305         atom=moldyn->atom;
306         random=&(moldyn->random);
307
308         /* gaussian distribution of velocities */
309         v3_zero(&p_total);
310         for(i=0;i<moldyn->count;i++) {
311                 sigma=sqrt(2.0*K_BOLTZMANN*moldyn->t/atom[i].mass);
312                 /* x direction */
313                 v=sigma*rand_get_gauss(random);
314                 atom[i].v.x=v;
315                 p_total.x+=atom[i].mass*v;
316                 /* y direction */
317                 v=sigma*rand_get_gauss(random);
318                 atom[i].v.y=v;
319                 p_total.y+=atom[i].mass*v;
320                 /* z direction */
321                 v=sigma*rand_get_gauss(random);
322                 atom[i].v.z=v;
323                 p_total.z+=atom[i].mass*v;
324         }
325
326         /* zero total momentum */
327         v3_scale(&p_total,&p_total,1.0/moldyn->count);
328         for(i=0;i<moldyn->count;i++) {
329                 v3_scale(&delta,&p_total,1.0/atom[i].mass);
330                 v3_sub(&(atom[i].v),&(atom[i].v),&delta);
331         }
332
333         /* velocity scaling */
334         scale_velocity(moldyn);
335
336         return 0;
337 }
338
339 int scale_velocity(t_moldyn *moldyn) {
340
341         int i;
342         double e,c;
343         t_atom *atom;
344
345         atom=moldyn->atom;
346
347         /*
348          * - velocity scaling (E = 3/2 N k T), E: kinetic energy
349          */
350         e=0.0;
351         for(i=0;i<moldyn->count;i++)
352                 e+=0.5*atom[i].mass*v3_absolute_square(&(atom[i].v));
353         c=sqrt((2.0*e)/(3.0*moldyn->count*K_BOLTZMANN*moldyn->t));
354         for(i=0;i<moldyn->count;i++)
355                 v3_scale(&(atom[i].v),&(atom[i].v),(1.0/c));
356
357         return 0;
358 }
359
360 double get_e_kin(t_atom *atom,int count) {
361
362         int i;
363         double e;
364
365         e=0.0;
366
367         for(i=0;i<count;i++) {
368                 e+=0.5*atom[i].mass*v3_absolute_square(&(atom[i].v));
369         }
370
371         return e;
372 }
373
374 double get_e_pot(t_moldyn *moldyn) {
375
376         return moldyn->energy;
377 }
378
379 double get_total_energy(t_moldyn *moldyn) {
380
381         double e;
382
383         e=get_e_kin(moldyn->atom,moldyn->count);
384         e+=get_e_pot(moldyn);
385
386         return e;
387 }
388
389 t_3dvec get_total_p(t_atom *atom, int count) {
390
391         t_3dvec p,p_total;
392         int i;
393
394         v3_zero(&p_total);
395         for(i=0;i<count;i++) {
396                 v3_scale(&p,&(atom[i].v),atom[i].mass);
397                 v3_add(&p_total,&p_total,&p);
398         }
399
400         return p_total;
401 }
402
403 double estimate_time_step(t_moldyn *moldyn,double nn_dist,double t) {
404
405         double tau;
406
407         tau=0.05*nn_dist/(sqrt(3.0*K_BOLTZMANN*t/moldyn->atom[0].mass));
408         tau*=1.0E-9;
409         if(tau<moldyn->tau)
410                 printf("[moldyn] warning: time step  (%f > %.15f)\n",
411                        moldyn->tau,tau);
412
413         return tau;     
414 }
415
416 /*
417  * numerical tricks
418  */
419
420 /* linked list / cell method */
421
422 int link_cell_init(t_moldyn *moldyn) {
423
424         t_linkcell *lc;
425         int i;
426
427         lc=&(moldyn->lc);
428
429         /* list log fd */
430         lc->listfd=open("/dev/null",O_WRONLY);
431
432         /* partitioning the md cell */
433         lc->nx=moldyn->dim.x/moldyn->cutoff;
434         lc->x=moldyn->dim.x/lc->nx;
435         lc->ny=moldyn->dim.y/moldyn->cutoff;
436         lc->y=moldyn->dim.y/lc->ny;
437         lc->nz=moldyn->dim.z/moldyn->cutoff;
438         lc->z=moldyn->dim.z/lc->nz;
439
440         lc->cells=lc->nx*lc->ny*lc->nz;
441         lc->subcell=malloc(lc->cells*sizeof(t_list));
442
443         printf("initializing linked cells (%d)\n",lc->cells);
444
445         for(i=0;i<lc->cells;i++)
446                 //list_init(&(lc->subcell[i]),1);
447                 list_init(&(lc->subcell[i]));
448
449         link_cell_update(moldyn);
450         
451         return 0;
452 }
453
454 int link_cell_update(t_moldyn *moldyn) {
455
456         int count,i,j,k;
457         int nx,ny,nz;
458         t_atom *atom;
459         t_linkcell *lc;
460
461         atom=moldyn->atom;
462         lc=&(moldyn->lc);
463
464         nx=lc->nx;
465         ny=lc->ny;
466         nz=lc->nz;
467
468         for(i=0;i<lc->cells;i++)
469                 list_destroy(&(moldyn->lc.subcell[i]));
470         
471         for(count=0;count<moedyn->count;count++) {
472                 i=(atom[count].r.x+(moldyn->dim.x/2))/lc->x;
473                 j=(atom[count].r.y+(moldyn->dim.y/2))/lc->y;
474                 k=(atom[count].r.z+(moldyn->dim.z/2))/lc->z;
475                 list_add_immediate_ptr(&(moldyn->lc.subcell[i+j*nx+k*nx*ny]),
476                                        &(atom[count]));
477         }
478
479         return 0;
480 }
481
482 int link_cell_neighbour_index(t_moldyn *moldyn,int i,int j,int k,t_list *cell) {
483
484         t_linkcell *lc;
485         int a;
486         int count1,count2;
487         int ci,cj,ck;
488         int nx,ny,nz;
489         int x,y,z;
490         unsigned char bx,by,bz;
491
492         lc=&(moldyn->lc);
493         nx=lc->nx;
494         ny=lc->ny;
495         nz=lc->nz;
496         count1=1;
497         count2=27;
498         a=nx*ny;
499
500
501         cell[0]=lc->subcell[i+j*nx+k*a];
502         for(ci=-1;ci<=1;ci++) {
503                 bx=0;
504                 x=i+ci;
505                 if((x<0)||(x>=nx)) {
506                         x=(x+nx)%nx;
507                         bx=1;
508                 }
509                 for(cj=-1;cj<=1;cj++) {
510                         by=0;
511                         y=j+cj;
512                         if((y<0)||(y>=ny)) {
513                                 y=(y+ny)%ny;
514                                 by=1;
515                         }
516                         for(ck=-1;ck<=1;ck++) {
517                                 bz=0;
518                                 z=k+ck;
519                                 if((z<0)||(z>=nz)) {
520                                         z=(z+nz)%nz;
521                                         bz=1;
522                                 }
523                                 if(!(ci|cj|ck)) continue;
524                                 if(bx|by|bz) {
525                                         cell[--count2]=lc->subcell[x+y*nx+z*a];
526                                 }
527                                 else {
528                                         cell[count1++]=lc->subcell[x+y*nx+z*a];
529                                 }
530                         }
531                 }
532         }
533
534         lc->dnlc=count2;
535         lc->countn=27;
536
537         return count2;
538 }
539
540 int link_cell_shutdown(t_moldyn *moldyn) {
541
542         int i;
543         t_linkcell *lc;
544
545         lc=&(moldyn->lc);
546
547         for(i=0;i<lc->nx*lc->ny*lc->nz;i++)
548                 list_shutdown(&(moldyn->lc.subcell[i]));
549
550         if(lc->listfd) close(lc->listfd);
551
552         return 0;
553 }
554
555 /*
556  *
557  * 'integration of newtons equation' - algorithms
558  *
559  */
560
561 /* start the integration */
562
563 int moldyn_integrate(t_moldyn *moldyn) {
564
565         int i;
566         unsigned int e,m,s,d,v;
567         t_3dvec p;
568
569         int fd;
570         char fb[128];
571
572         /* initialize linked cell method */
573         link_cell_init(moldyn);
574
575         /* logging & visualization */
576         e=moldyn->ewrite;
577         m=moldyn->mwrite;
578         s=moldyn->swrite;
579         d=moldyn->dwrite;
580         v=moldyn->vwrite;
581
582         if(!(moldyn->lvstat&MOLDYN_LVSTAT_INITIALIZED)) {
583                 printf("[moldyn] warning, lv system not initialized\n");
584                 return -1;
585         }
586
587         /* sqaure of some variables */
588         moldyn->tau_square=moldyn->tau*moldyn->tau;
589         moldyn->cutoff_square=moldyn->cutoff*moldyn->cutoff;
590
591         /* calculate initial forces */
592         moldyn->potential_force_function(moldyn);
593
594         for(i=0;i<moldyn->time_steps;i++) {
595
596                 /* integration step */
597                 moldyn->integrate(moldyn);
598
599                 /* check for log & visualization */
600                 if(e) {
601                         if(!(i%e))
602                                 dprintf(moldyn->efd,
603                                         "%.15f %.45f\n",i*moldyn->tau,
604                                         get_total_energy(moldyn));
605                 }
606                 if(m) {
607                         if(!(i%m)) {
608                                 p=get_total_p(moldyn->atom,moldyn->count);
609                                 dprintf(moldyn->mfd,
610                                         "%.15f %.45f\n",i*moldyn->tau,
611                                         v3_norm(&p));
612                         }
613                 }
614                 if(s) {
615                         if(!(i%s)) {
616                                 snprintf(fb,128,"%s-%f-%.15f.save",moldyn->sfb,
617                                          moldyn->t,i*moldyn->tau);
618                                 fd=open(fb,O_WRONLY|O_TRUNC|O_CREAT);
619                                 if(fd<0) perror("[moldyn] save fd open");
620                                 else {
621                                         write(fd,moldyn,sizeof(t_moldyn));
622                                         write(fd,moldyn->atom,
623                                               moldyn->count*sizeof(t_atom));
624                                 }
625                                 close(fd);
626                         }       
627                 }
628                 if(v) {
629                         if(!(i%v)) {
630                                 visual_atoms(moldyn->visual,i*moldyn->tau,
631                                              moldyn->atom,moldyn->count);
632                                 printf("\rsteps: %d",i);
633                                 fflush(stdout);
634                         }
635                 }
636         }
637
638         return 0;
639 }
640
641 /* velocity verlet */
642
643 int velocity_verlet(t_moldyn *moldyn) {
644
645         int i,count;
646         double tau,tau_square;
647         t_3dvec delta;
648         t_atom *atom;
649
650         atom=moldyn->atom;
651         count=moldyn->count;
652         tau=moldyn->tau;
653         tau_square=moldyn->tau_square;
654
655         for(i=0;i<count;i++) {
656                 /* new positions */
657                 v3_scale(&delta,&(atom[i].v),tau);
658                 v3_add(&(atom[i].r),&(atom[i].r),&delta);
659                 v3_scale(&delta,&(atom[i].f),0.5*tau_square/atom[i].mass);
660                 v3_add(&(atom[i].r),&(atom[i].r),&delta);
661                 v3_per_bound(&(atom[i].r),&(moldyn->dim));
662
663                 /* velocities */
664                 v3_scale(&delta,&(atom[i].f),0.5*tau/atom[i].mass);
665                 v3_add(&(atom[i].v),&(atom[i].v),&delta);
666         }
667
668         /* neighbour list update */
669 printf("list update ...\n");
670         link_cell_update(moldyn);
671 printf("done\n");
672
673         /* forces depending on chosen potential */
674 printf("calc potential/force ...\n");
675         potential_force_calc(moldyn);
676         //moldyn->potential_force_function(moldyn);
677 printf("done\n");
678
679         for(i=0;i<count;i++) {
680                 /* again velocities */
681                 v3_scale(&delta,&(atom[i].f),0.5*tau/atom[i].mass);
682                 v3_add(&(atom[i].v),&(atom[i].v),&delta);
683         }
684
685         return 0;
686 }
687
688
689 /*
690  *
691  * potentials & corresponding forces
692  * 
693  */
694
695 /* generic potential and force calculation */
696
697 int potential_force_calc(t_moldyn *moldyn) {
698
699         int i,count;
700         t_atom *atom;
701         t_linkcell *lc;
702         t_list neighbour[27];
703         t_list *this;
704         double u;
705         unsigned char bc,bc3;
706         int countn,dnlc;
707
708         count=moldyn->count;
709         atom=moldyn->atom;
710         lc=&(moldyn->lc);
711
712         /* reset energy */
713         moldyn->energy=0.0;
714
715         for(i=0;i<count;i++) {
716         
717                 /* reset force */
718                 v3_zero(&(atom[i].f));
719
720                 /* single particle potential/force */
721                 if(atom[i].attr&ATOM_ATTR_1BP)
722                         moldyn->pf_func1b(moldyn,&(atom[i]));
723
724                 /* 2 body pair potential/force */
725                 if(atom[i].attr&(ATOM_ATTR_2BP|ATOM_ATTR_3BP)) {
726                 
727                         link_cell_neighbour_index(moldyn,
728                                 (atom[i].r.x+moldyn->dim.x/2)/lc->x,
729                                 (atom[i].r.y+moldyn->dim.y/2)/lc->y,
730                                 (atom[i].r.z+moldyn->dim.z/2)/lc->z,
731                                 neighbour);
732
733                         countn=lc->countn;
734                         dnlc=lc->dnlc;
735
736                         for(j=0;j<countn;j++) {
737
738                                 this=&(neighbour[j]);
739                                 list_reset(this);
740
741                                 if(this->start==NULL)
742                                         continue;
743
744                                 bc=(j<dnlc)?0:1;
745
746                                 do {
747                                         btom=this->current->data;
748
749                                         if(btom==&(atom[i]))
750                                                 continue;
751
752                                         if((btom->attr&ATOM_ATTR_2BP)&
753                                            (atom[i].attr&ATOM_ATTR_2BP))
754                                                 moldyn->pf_func2b(moldyn,
755                                                                   &(atom[i]),
756                                                                   btom,
757                                                                   bc);
758
759                                         /* 3 body potential/force */
760
761                                         if(!(atom[i].attr&ATOM_ATTR_3BP)||
762                                            !(btom->attr&ATOM_ATTR_3BP))
763                                                 continue;
764
765                                         link_cell_neighbour_index(moldyn,
766                                            (btom->r.x+moldyn->dim.x/2)/lc->x,
767                                            (btom->r.y+moldyn->dim.y/2)/lc->y,
768                                            (btom->r.z+moldyn->dim.z/2)/lc->z,
769                                            neighbourk);
770
771                                         for(k=0;k<lc->countn;k++) {
772
773                                                 thisk=&(neighbourk[k]);
774                                                 list_reset(thisk);
775                                         
776                                                 if(thisk->start==NULL)
777                                                         continue;
778
779                                                 bck=(k<lc->dnlc)?0:1;
780
781                                                 do {
782
783                         ktom=thisk->current->data;
784
785                         if(!(ktom->attr&ATOM_ATTR_3BP))
786                                 continue;
787
788                         if(ktom==btom)
789                                 continue;
790
791                         if(ktom==&(atom[i]))
792                                 continue;
793
794                         moldyn->pf_func3b(moldyn,&(atom[i]),btom,ktom,bck);
795
796                                                 } while(list_next(thisk)!=\
797                                                         L_NO_NEXT_ELEMENT);
798                                         
799                                 } while(list_next(this)!=L_NO_NEXT_ELEMENT);
800                         }
801                 }
802         }
803
804         return 0;
805 }
806
807 /*
808  * example potentials
809  */
810
811 /* harmonic oscillator potential and force */
812
813 int harmonic_oscillator(t_moldyn *moldyn,t_atom *ai,t_atom *aj,unsigned char bc)) {
814
815         t_ho_params *params;
816         t_atom *atom,*btom;
817         t_linkcell *lc;
818         t_list *this,neighbour[27];
819         int i,j,c;
820         int count;
821         t_3dvec force,distance;
822         double d,u;
823         double sc,equi_dist;
824         int ni,nj,nk;
825
826         params=moldyn->pot_params;
827         atom=moldyn->atom;
828         lc=&(moldyn->lc);
829         sc=params->spring_constant;
830         equi_dist=params->equilibrium_distance;
831         count=moldyn->count;
832
833         /* reset energy counter */
834         u=0.0;
835
836         for(i=0;i<count;i++) {
837                 /* reset force */
838                 v3_zero(&(atom[i].f));
839
840                 /* determine cell + neighbours */
841                 ni=(atom[i].r.x+(moldyn->dim.x/2))/lc->x;
842                 nj=(atom[i].r.y+(moldyn->dim.y/2))/lc->y;
843                 nk=(atom[i].r.z+(moldyn->dim.z/2))/lc->z;
844                 c=link_cell_neighbour_index(moldyn,ni,nj,nk,neighbour);
845
846                 /*
847                  * processing cell of atom i
848                  * => no need to check for empty list (1 element at minimum)
849                  */
850                 this=&(neighbour[0]);
851                 list_reset(this);
852                 do {
853                         btom=this->current->data;
854                         if(btom==&(atom[i]))
855                                 continue;
856                         v3_sub(&distance,&(atom[i].r),&(btom->r));
857                         d=v3_norm(&distance);
858                         if(d<=moldyn->cutoff) {
859                                 u+=(0.5*sc*(d-equi_dist)*(d-equi_dist));
860                                 v3_scale(&force,&distance,
861                                          -sc*(1.0-(equi_dist/d)));
862                                 v3_add(&(atom[i].f),&(atom[i].f),&force);
863                         }
864                 } while(list_next(this)!=L_NO_NEXT_ELEMENT);
865
866                 /*
867                  * direct neighbour cells
868                  * => no boundary condition check necessary
869                  */
870                 for(j=1;j<c;j++) {
871                         this=&(neighbour[j]);
872                         list_reset(this); /* there might not be a single atom */
873                         if(this->start!=NULL) {
874
875                         do {
876                                 btom=this->current->data;
877                                 v3_sub(&distance,&(atom[i].r),&(btom->r));
878                                 d=v3_norm(&distance);
879                                 if(d<=moldyn->cutoff) {
880                                         u+=(0.5*sc*(d-equi_dist)*(d-equi_dist));
881                                         v3_scale(&force,&distance,
882                                                  -sc*(1.0-(equi_dist/d)));
883                                         v3_add(&(atom[i].f),&(atom[i].f),
884                                                &force);
885                                 }
886                         } while(list_next(this)!=L_NO_NEXT_ELEMENT);
887
888                         }
889                 }
890
891                 /*
892                  * indirect neighbour cells
893                  * => check boundary conditions
894                  */
895                 for(j=c;j<27;j++) {
896                         this=&(neighbour[j]);
897                         list_reset(this); /* check boundary conditions */
898                         if(this->start!=NULL) {
899
900                         do {
901                                 btom=this->current->data;
902                                 v3_sub(&distance,&(atom[i].r),&(btom->r));
903                                 v3_per_bound(&distance,&(moldyn->dim));
904                                 d=v3_norm(&distance);
905                                 if(d<=moldyn->cutoff) {
906                                         u+=(0.5*sc*(d-equi_dist)*(d-equi_dist));
907                                         v3_scale(&force,&distance,
908                                                  -sc*(1.0-(equi_dist/d)));
909                                         v3_add(&(atom[i].f),&(atom[i].f),
910                                                &force);
911                                 }
912                         } while(list_next(this)!=L_NO_NEXT_ELEMENT);
913
914                         }
915                 }
916         }
917
918         moldyn->energy=0.5*u;
919
920         return 0;
921 }
922
923 /* lennard jones potential & force for one sort of atoms */
924  
925 int lennard_jones(t_moldyn *moldyn) {
926
927         t_lj_params *params;
928         t_atom *atom,*btom;
929         t_linkcell *lc;
930         t_list *this,neighbour[27];
931         int i,j,c;
932         int count;
933         t_3dvec force,distance;
934         double d,h1,h2,u;
935         double eps,sig6,sig12;
936         int ni,nj,nk;
937
938         params=moldyn->pot_params;
939         atom=moldyn->atom;
940         lc=&(moldyn->lc);
941         count=moldyn->count;
942         eps=params->epsilon4;
943         sig6=params->sigma6;
944         sig12=params->sigma12;
945
946         /* reset energy counter */
947         u=0.0;
948
949         for(i=0;i<count;i++) {
950                 /* reset force */
951                 v3_zero(&(atom[i].f));
952
953                 /* determine cell + neighbours */
954                 ni=(atom[i].r.x+(moldyn->dim.x/2))/lc->x;
955                 nj=(atom[i].r.y+(moldyn->dim.y/2))/lc->y;
956                 nk=(atom[i].r.z+(moldyn->dim.z/2))/lc->z;
957                 c=link_cell_neighbour_index(moldyn,ni,nj,nk,neighbour);
958
959                 /* processing cell of atom i */
960                 this=&(neighbour[0]);
961                 list_reset(this); /* list has 1 element at minimum */
962                 do {
963                         btom=this->current->data;
964                         if(btom==&(atom[i]))
965                                 continue;
966                         v3_sub(&distance,&(atom[i].r),&(btom->r));
967                         d=v3_absolute_square(&distance);        /* 1/r^2 */
968                         if(d<=moldyn->cutoff_square) {
969                                 d=1.0/d;        /* 1/r^2 */
970                                 h2=d*d;         /* 1/r^4 */
971                                 h2*=d;          /* 1/r^6 */
972                                 h1=h2*h2;       /* 1/r^12 */
973                                 u+=eps*(sig12*h1-sig6*h2);
974                                 h2*=d;          /* 1/r^8 */
975                                 h1*=d;          /* 1/r^14 */
976                                 h2*=6*sig6;
977                                 h1*=12*sig12;
978                                 d=+h1-h2;
979                                 d*=eps;
980                                 v3_scale(&force,&distance,d);
981                                 v3_add(&(atom[i].f),&(atom[i].f),&force);
982                         }
983                 } while(list_next(this)!=L_NO_NEXT_ELEMENT);
984
985                 /* neighbours not doing boundary condition overflow */
986                 for(j=1;j<c;j++) {
987                         this=&(neighbour[j]);
988                         list_reset(this); /* there might not be a single atom */
989                         if(this->start!=NULL) {
990
991                         do {
992                                 btom=this->current->data;
993                                 v3_sub(&distance,&(atom[i].r),&(btom->r));
994                                 d=v3_absolute_square(&distance); /* r^2 */
995                                 if(d<=moldyn->cutoff_square) {
996                                         d=1.0/d;        /* 1/r^2 */
997                                         h2=d*d;         /* 1/r^4 */
998                                         h2*=d;          /* 1/r^6 */
999                                         h1=h2*h2;       /* 1/r^12 */
1000                                         u+=eps*(sig12*h1-sig6*h2);
1001                                         h2*=d;          /* 1/r^8 */
1002                                         h1*=d;          /* 1/r^14 */
1003                                         h2*=6*sig6;
1004                                         h1*=12*sig12;
1005                                         d=+h1-h2;
1006                                         d*=eps;
1007                                         v3_scale(&force,&distance,d);
1008                                         v3_add(&(atom[i].f),&(atom[i].f),
1009                                                &force);
1010                                 }
1011                         } while(list_next(this)!=L_NO_NEXT_ELEMENT);
1012                                 
1013                         }
1014                 }
1015
1016                 /* neighbours due to boundary conditions */
1017                 for(j=c;j<27;j++) {
1018                         this=&(neighbour[j]);
1019                         list_reset(this); /* check boundary conditions */
1020                         if(this->start!=NULL) {
1021
1022                         do {
1023                                 btom=this->current->data;
1024                                 v3_sub(&distance,&(atom[i].r),&(btom->r));
1025                                 v3_per_bound(&distance,&(moldyn->dim));
1026                                 d=v3_absolute_square(&distance); /* r^2 */
1027                                 if(d<=moldyn->cutoff_square) {
1028                                         d=1.0/d;        /* 1/r^2 */
1029                                         h2=d*d;         /* 1/r^4 */
1030                                         h2*=d;          /* 1/r^6 */
1031                                         h1=h2*h2;       /* 1/r^12 */
1032                                         u+=eps*(sig12*h1-sig6*h2);
1033                                         h2*=d;          /* 1/r^8 */
1034                                         h1*=d;          /* 1/r^14 */
1035                                         h2*=6*sig6;
1036                                         h1*=12*sig12;
1037                                         d=+h1-h2;
1038                                         d*=eps;
1039                                         v3_scale(&force,&distance,d);
1040                                         v3_add(&(atom[i].f),&(atom[i].f),
1041                                                &force);
1042                                 }
1043                         } while(list_next(this)!=L_NO_NEXT_ELEMENT);
1044
1045                         }
1046                 }
1047         }
1048
1049         moldyn->energy=0.5*u;
1050         
1051         return 0;
1052 }
1053
1054 /* tersoff potential & force for 2 sorts of atoms */
1055
1056 int tersoff(t_moldyn *moldyn) {
1057
1058         t_tersoff_params *params;
1059         t_atom *atom,*btom,*ktom;
1060         t_linkcell *lc;
1061         t_list *this,*thisk,neighbour[27],neighbourk[27];
1062         int i,j,k,c,ck;
1063         int count;
1064         double u;
1065         int ni,nj,nk;
1066         int ki,kj,kk;
1067         
1068
1069         params=moldyn->pot_params;
1070         atom=moldyn->atom;
1071         lc=&(moldyn->lc);
1072         count=moldyn->count;
1073         
1074         /* reset energy counter */
1075         u=0.0;
1076
1077         for(i=0;i<count;i++) {
1078                 /* reset force */
1079                 v3_zero(&(atom[i].f));
1080
1081                 /* determin cell neighbours */
1082                 ni=(atom[i].r.x+(moldyn->dim.x/2))/lc->x;
1083                 nj=(atom[i].r.y+(moldyn->dim.y/2))/lc->y;
1084                 nk=(atom[i].r.z+(moldyn->dim.z/2))/lc->z;
1085                 c=link_cell_neighbour_index(moldyn,ni,nj,nk,neighbour);
1086
1087                 /*
1088                  * processing cell of atom i
1089                  * => no need to check for empty list (1 element at minimum)
1090                  */
1091                 this=&(neighbour[0]);
1092                 list_reset(this);
1093                 do {
1094                         btom=this->current->data;
1095                         if(btom==&(atom[i]))
1096                                 continue;
1097
1098                         /* 2 body stuff */
1099
1100                         /* we need: f_c, df_c, f_r, df_r */
1101
1102                         v3_sub(&dist_ij,btom,&(atom[i]));
1103                         d_ij=v3_norm(&dist_ij);
1104                         if(d_ij<=S) {
1105
1106                                 /* determine the tersoff parameters */
1107                                 if(atom[i].element!=btom->element) {
1108                                 S=sqrt(TERSOFF_S[e1]*TERSOFF_S[e2]);
1109                                 R=R_m;
1110                                 A=;
1111                                 lambda=;
1112                                 B=;
1113                                 mu=;
1114                                 chi=;
1115                                 beta=;
1116                                 betaN=;
1117
1118                                 if(d_ij<=R) {
1119                                         df_r=-lambda*A*exp(-lambda*d_ij)/d_ij;
1120                                         v3_scale(&force,&dist_ij,df_r);
1121                                         v3_add(&(atom[i].f),&(atom[i].f),
1122                                                 &force);
1123                                 }
1124                                 else {
1125                                         s_r=S-R;
1126                                         arg1=PI*(d_ij-R)/s_r;
1127                                         f_c=0.5+0.5*cos(arg1);
1128                                         df_c=-0.5*sin(arg1)*(PI/(s_r*d_ij));
1129                                         f_r=A*exp(-lambda*d_ij);
1130                                         df_r=-lambda*f_r/d_ij;
1131                                         scale=df_c*f_r+df_r*f_c;
1132                                         v3_scale(&force,&dist_ij,scale);
1133                                         v3_add(&(atom[i].f),&(atom[i].f),
1134                                                &force);
1135                                 }
1136                         }
1137                         else 
1138                                 continue;               
1139
1140                         
1141                         /* end 2 body stuff */ 
1142
1143                         /* determine cell neighbours of btom */
1144                         ki=(btom->r.x+(moldyn->dim.x/2))/lc->x;
1145                         kj=(btom->r.y+(moldyn->dim.y/2))/lc->y;
1146                         kk=(btom->r.z+(moldyn->dim.z/2))/lc->z;
1147                         ck=link_cell_neighbour_index(moldyn,ki,kj,kk,
1148                                                      neighbourk);
1149
1150                         /* go for zeta - 3 body stuff! */
1151                         zeta=0.0;
1152                         d_ij2=d_ij*d_ij;
1153
1154                         /* cell of btom */
1155                         thisk=&(neighbourk[0]);
1156                         list_reset(thisk);
1157                         do {
1158                                 ktom=thisk->current->data;
1159                                 if(ktom==btom)
1160                                         continue;
1161                                 if(ktom==&(atom[i]))
1162                                         continue;
1163                                 
1164                                 /* 3 body stuff (1) */
1165                                 
1166                                 v3_sub(&dist_ik,ktom,&(atom[i]));
1167                                 d_ik=v3_norm(&dist_ik);
1168                                 if(d_ik<=Sik) {
1169
1170                                 Rik=;
1171                                 Sik=;
1172                                 Aik=;
1173                                 lambda_ik=;
1174                                 Bik=;
1175                                 mu_ik=;
1176                                 omega_ik=;
1177                                 c_i=;
1178                                 d_i=;
1179                                 h_i=;
1180                         
1181
1182                                         if(d_ik<=Rik) {
1183                                                 f_cik=1.0;
1184                                                 df_cik=0.0;
1185                                         }
1186                                         else {
1187                                                 sik_rik=Sik-Rik;
1188                                                 arg1ik=PI*(d_ik-Rik)/sik_rik;
1189                                                 f_cik=0.5+0.5*cos(arg1ik);
1190                                                 df_cik=-0.5*sin(arg1ik)* \
1191                                                        (PI/(sik_rik*d_ik));
1192                                                 f_rik=Aik*exp(-lambda_ik*d_ik);
1193                                                 f_aik=-Bik*exp(-mu_ik*d_ik);
1194                                         }
1195                         
1196                                         v3_sub(&distance_jk,ktom,btom);
1197                                         cos_theta=(d_ij2+d_ik*d_ik-d_jk*d_jk)/\
1198                                                   (2*d_ij*d_ik);
1199                                         sin_theta=sqrt(1.0/\
1200                                                   (cos_theta*cos_theta));
1201                                         theta=arccos(cos_theta);
1202
1203                                         
1204                                 }
1205                                 else
1206                                         continue;
1207
1208                                 /* end 3 body stuff (1) */
1209
1210
1211                         } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1212
1213                         /* direct neighbours of btom cell */
1214                         for(k=1;k<ck;k++) {
1215                                 thisk=&(neighbourk[k]);
1216                                 list_reset(thisk);
1217                                 if(thisk->start!=NULL) {
1218
1219                                 do {
1220                                         ktom=thisk->current->data;
1221                                         if(ktom==&(atom[i]))
1222                                                 continue;
1223
1224                                 /* 3 body stuff (2) */
1225
1226                                 } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1227
1228                                 }
1229                         }
1230
1231                         /* indirect neighbours of btom cell */
1232                         for(k=ck;k<27;k++) {
1233                                 thisk=&(neighbourk[k]);
1234                                 list_reset(thisk);
1235                                 if(thisk->start!=NULL) {
1236
1237                                 do {
1238                                         ktom=thisk->current->data;
1239                                         if(ktom==&(atom[i]))
1240                                                 continue;
1241
1242                                 /* 3 body stuff */
1243
1244                                 } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1245
1246                                 }
1247                         }
1248
1249
1250                 } while(list_next(this)!=L_NO_NEXT_ELEMENT);
1251
1252                 /*
1253                  * direct neighbour cells of atom i
1254                  */
1255                 for(j=1;j<c;j++) {
1256                         this=&(neighbour[j]);
1257                         list_reset(this);
1258                         if(this->start!=NULL) {
1259
1260                         do {
1261                                 btom=this->current->data;
1262
1263                                 /* 2 body stuff */
1264
1265
1266                         /* determine cell neighbours of btom */
1267                         ki=(btom->r.x+(moldyn->dim.x/2))/lc->x;
1268                         kj=(btom->r.y+(moldyn->dim.y/2))/lc->y;
1269                         kk=(btom->r.z+(moldyn->dim.z/2))/lc->z;
1270                         ck=link_cell_neighbour_index(moldyn,ki,kj,kk,
1271                                                      neighbourk);
1272
1273                         /* cell of btom */
1274                         thisk=&(neighbourk[0]);
1275                         list_reset(thisk);
1276                         do {
1277                                 ktom=thisk->current->data;
1278                                 if(ktom==btom)
1279                                         continue;
1280                                 if(ktom==&(atom[i]))
1281                                         continue;
1282                                 
1283                                 /* 3 body stuff (1) */
1284
1285                         } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1286
1287                         /* direct neighbours of btom cell */
1288                         for(k=1;k<ck;k++) {
1289                                 thisk=&(neighbourk[k]);
1290                                 list_reset(thisk);
1291                                 if(thisk->start!=NULL) {
1292
1293                                 do {
1294                                         ktom=thisk->current->data;
1295                                         if(ktom==&(atom[i]))
1296                                                 continue;
1297
1298                                 /* 3 body stuff (2) */
1299
1300                                 } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1301
1302                                 }
1303                         }
1304
1305                         /* indirect neighbours of btom cell */
1306                         for(k=ck;k<27;k++) {
1307                                 thisk=&(neighbourk[k]);
1308                                 list_reset(thisk);
1309                                 if(thisk->start!=NULL) {
1310
1311                                 do {
1312                                         ktom=thisk->current->data;
1313                                         if(ktom==&(atom[i]))
1314                                                 continue;
1315
1316                                 /* 3 body stuff (3) */
1317
1318                                 } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1319
1320                                 }
1321                         }
1322
1323
1324                         } while(list_next(this)!=L_NO_NEXT_ELEMENT);
1325
1326                         }
1327                 }
1328
1329                 /*
1330                  * indirect neighbour cells of atom i
1331                  */
1332                 for(j=c;j<27;j++) {
1333                         this=&(neighbour[j]);
1334                         list_reset(this);
1335                         if(this->start!=NULL) {
1336
1337                         do {
1338                                 btom=this->current->data;
1339
1340                                 /* 2 body stuff */
1341
1342
1343                         /* determine cell neighbours of btom */
1344                         ki=(btom->r.x+(moldyn->dim.x/2))/lc->x;
1345                         kj=(btom->r.y+(moldyn->dim.y/2))/lc->y;
1346                         kk=(btom->r.z+(moldyn->dim.z/2))/lc->z;
1347                         ck=link_cell_neighbour_index(moldyn,ki,kj,kk,
1348                                                      neighbourk);
1349
1350                         /* cell of btom */
1351                         thisk=&(neighbourk[0]);
1352                         list_reset(thisk);
1353                         do {
1354                                 ktom=thisk->current->data;
1355                                 if(ktom==btom)
1356                                         continue;
1357                                 if(ktom==&(atom[i]))
1358                                         continue;
1359                                 
1360                                 /* 3 body stuff (1) */
1361
1362                         } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1363
1364                         /* direct neighbours of btom cell */
1365                         for(k=1;k<ck;k++) {
1366                                 thisk=&(neighbourk[k]);
1367                                 list_reset(thisk);
1368                                 if(thisk->start!=NULL) {
1369
1370                                 do {
1371                                         ktom=thisk->current->data;
1372                                         if(ktom==&(atom[i]))
1373                                                 continue;
1374
1375                                 /* 3 body stuff (2) */
1376
1377                                 } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1378
1379                                 }
1380                         }
1381
1382                         /* indirect neighbours of btom cell */
1383                         for(k=ck;k<27;k++) {
1384                                 thisk=&(neighbourk[k]);
1385                                 list_reset(thisk);
1386                                 if(thisk->start!=NULL) {
1387
1388                                 do {
1389                                         ktom=thisk->current->data;
1390                                         if(ktom==&(atom[i]))
1391                                                 continue;
1392
1393                                 /* 3 body stuff (3) */
1394
1395                                 } while(list_next(thisk)!=L_NO_NEXT_ELEMENT);
1396
1397                                 }
1398                         }
1399
1400
1401                         } while(list_next(this)!=L_NO_NEXT_ELEMENT);
1402
1403                         }
1404                 }
1405                 
1406         }
1407
1408         moldyn->energy=0.5*u;
1409
1410         return 0;
1411 }
1412