vel scaling fixed/changed
[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
27 int moldyn_init(t_moldyn *moldyn,int argc,char **argv) {
28
29         //int ret;
30
31         //ret=moldyn_parse_argv(moldyn,argc,argv);
32         //if(ret<0) return ret;
33
34         memset(moldyn,0,sizeof(t_moldyn));
35
36         rand_init(&(moldyn->random),NULL,1);
37         moldyn->random.status|=RAND_STAT_VERBOSE;
38
39         return 0;
40 }
41
42 int moldyn_shutdown(t_moldyn *moldyn) {
43
44         printf("[moldyn] shutdown\n");
45         moldyn_log_shutdown(moldyn);
46         link_cell_shutdown(moldyn);
47         rand_close(&(moldyn->random));
48         free(moldyn->atom);
49
50         return 0;
51 }
52
53 int set_int_alg(t_moldyn *moldyn,u8 algo) {
54
55         switch(algo) {
56                 case MOLDYN_INTEGRATE_VERLET:
57                         moldyn->integrate=velocity_verlet;
58                         break;
59                 default:
60                         printf("unknown integration algorithm: %02x\n",algo);
61                         return -1;
62         }
63
64         return 0;
65 }
66
67 int set_cutoff(t_moldyn *moldyn,double cutoff) {
68
69         moldyn->cutoff=cutoff;
70
71         return 0;
72 }
73
74 int set_temperature(t_moldyn *moldyn,double t) {
75         
76         moldyn->t=t;
77
78         return 0;
79 }
80
81 int set_dim(t_moldyn *moldyn,double x,double y,double z,u8 visualize) {
82
83         moldyn->dim.x=x;
84         moldyn->dim.y=y;
85         moldyn->dim.z=z;
86
87         if(visualize) {
88                 moldyn->vis.dim.x=x;
89                 moldyn->vis.dim.y=y;
90                 moldyn->vis.dim.z=z;
91         }
92
93         return 0;
94 }
95
96 int set_nn_dist(t_moldyn *moldyn,double dist) {
97
98         moldyn->nnd=dist;
99
100         return 0;
101 }
102
103 int set_pbc(t_moldyn *moldyn,u8 x,u8 y,u8 z) {
104
105         if(x)
106                 moldyn->status|=MOLDYN_STAT_PBX;
107
108         if(y)
109                 moldyn->status|=MOLDYN_STAT_PBY;
110
111         if(z)
112                 moldyn->status|=MOLDYN_STAT_PBZ;
113
114         return 0;
115 }
116
117 int set_potential1b(t_moldyn *moldyn,pf_func1b func,void *params) {
118
119         moldyn->func1b=func;
120         moldyn->pot1b_params=params;
121
122         return 0;
123 }
124
125 int set_potential2b(t_moldyn *moldyn,pf_func2b func,void *params) {
126
127         moldyn->func2b=func;
128         moldyn->pot2b_params=params;
129
130         return 0;
131 }
132
133 int set_potential3b(t_moldyn *moldyn,pf_func3b func,void *params) {
134
135         moldyn->func3b=func;
136         moldyn->pot3b_params=params;
137
138         return 0;
139 }
140
141 int moldyn_set_log(t_moldyn *moldyn,u8 type,char *fb,int timer) {
142
143         switch(type) {
144                 case LOG_TOTAL_ENERGY:
145                         moldyn->ewrite=timer;
146                         moldyn->efd=open(fb,O_WRONLY|O_CREAT|O_TRUNC);
147                         if(moldyn->efd<0) {
148                                 perror("[moldyn] efd open");
149                                 return moldyn->efd;
150                         }
151                         dprintf(moldyn->efd,"# total energy log file\n");
152                         break;
153                 case LOG_TOTAL_MOMENTUM:
154                         moldyn->mwrite=timer;
155                         moldyn->mfd=open(fb,O_WRONLY|O_CREAT|O_TRUNC);
156                         if(moldyn->mfd<0) {
157                                 perror("[moldyn] mfd open");
158                                 return moldyn->mfd;
159                         }
160                         dprintf(moldyn->efd,"# total momentum log file\n");
161                         break;
162                 case SAVE_STEP:
163                         moldyn->swrite=timer;
164                         strncpy(moldyn->sfb,fb,63);
165                         break;
166                 case VISUAL_STEP:
167                         moldyn->vwrite=timer;
168                         strncpy(moldyn->vfb,fb,63);
169                         visual_init(&(moldyn->vis),fb);
170                         break;
171                 default:
172                         printf("unknown log mechanism: %02x\n",type);
173                         return -1;
174         }
175
176         return 0;
177 }
178
179 int moldyn_log_shutdown(t_moldyn *moldyn) {
180
181         printf("[moldyn] log shutdown\n");
182         if(moldyn->efd) close(moldyn->efd);
183         if(moldyn->mfd) close(moldyn->mfd);
184         if(&(moldyn->vis)) visual_tini(&(moldyn->vis));
185
186         return 0;
187 }
188
189 int create_lattice(t_moldyn *moldyn,u8 type,double lc,int element,double mass,
190                    u8 attr,u8 bnum,int a,int b,int c) {
191
192         int count;
193         int ret;
194         t_3dvec origin;
195
196         count=a*b*c;
197
198         if(type==FCC) count*=4;
199
200         if(type==DIAMOND) count*=8;
201
202         moldyn->atom=malloc(count*sizeof(t_atom));
203         if(moldyn->atom==NULL) {
204                 perror("malloc (atoms)");
205                 return -1;
206         }
207
208         v3_zero(&origin);
209
210         switch(type) {
211                 case FCC:
212                         ret=fcc_init(a,b,c,lc,moldyn->atom,&origin);
213                         break;
214                 case DIAMOND:
215                         ret=diamond_init(a,b,c,lc,moldyn->atom,&origin);
216                         break;
217                 default:
218                         printf("unknown lattice type (%02x)\n",type);
219                         return -1;
220         }
221
222         /* debug */
223         if(ret!=count) {
224                 printf("ok, there is something wrong ...\n");
225                 printf("calculated -> %d atoms\n",count);
226                 printf("created -> %d atoms\n",ret);
227                 return -1;
228         }
229
230         moldyn->count=count;
231         printf("[moldyn] created lattice with %d atoms\n",count);
232
233         while(count) {
234                 moldyn->atom[count-1].element=element;
235                 moldyn->atom[count-1].mass=mass;
236                 moldyn->atom[count-1].attr=attr;
237                 moldyn->atom[count-1].bnum=bnum;
238                 count-=1;
239         }
240
241         return ret;
242 }
243
244 int add_atom(t_moldyn *moldyn,int element,double mass,u8 bnum,u8 attr,
245              t_3dvec *r,t_3dvec *v) {
246
247         t_atom *atom;
248         void *ptr;
249         int count;
250         
251         atom=moldyn->atom;
252         count=++(moldyn->count);
253
254         ptr=realloc(atom,count*sizeof(t_atom));
255         if(!ptr) {
256                 perror("[moldyn] realloc (add atom)");
257                 return -1;
258         }
259         moldyn->atom=ptr;
260
261         atom=moldyn->atom;
262         atom[count-1].r=*r;
263         atom[count-1].v=*v;
264         atom[count-1].element=element;
265         atom[count-1].mass=mass;
266         atom[count-1].bnum=bnum;
267         atom[count-1].attr=attr;
268
269         return 0;
270 }
271
272 int destroy_atoms(t_moldyn *moldyn) {
273
274         if(moldyn->atom) free(moldyn->atom);
275
276         return 0;
277 }
278
279 int thermal_init(t_moldyn *moldyn) {
280
281         /*
282          * - gaussian distribution of velocities
283          * - zero total momentum
284          * - velocity scaling (E = 3/2 N k T), E: kinetic energy
285          */
286
287         int i;
288         double v,sigma;
289         t_3dvec p_total,delta;
290         t_atom *atom;
291         t_random *random;
292
293         atom=moldyn->atom;
294         random=&(moldyn->random);
295
296         /* gaussian distribution of velocities */
297         v3_zero(&p_total);
298         for(i=0;i<moldyn->count;i++) {
299                 sigma=sqrt(2.0*K_BOLTZMANN*moldyn->t/atom[i].mass);
300                 /* x direction */
301                 v=sigma*rand_get_gauss(random);
302                 atom[i].v.x=v;
303                 p_total.x+=atom[i].mass*v;
304                 /* y direction */
305                 v=sigma*rand_get_gauss(random);
306                 atom[i].v.y=v;
307                 p_total.y+=atom[i].mass*v;
308                 /* z direction */
309                 v=sigma*rand_get_gauss(random);
310                 atom[i].v.z=v;
311                 p_total.z+=atom[i].mass*v;
312         }
313
314         /* zero total momentum */
315         v3_scale(&p_total,&p_total,1.0/moldyn->count);
316         for(i=0;i<moldyn->count;i++) {
317                 v3_scale(&delta,&p_total,1.0/atom[i].mass);
318                 v3_sub(&(atom[i].v),&(atom[i].v),&delta);
319         }
320
321         /* velocity scaling */
322         scale_velocity(moldyn,VSCALE_INIT_EQUI);
323
324         return 0;
325 }
326
327 int scale_velocity(t_moldyn *moldyn,u8 type) {
328
329         int i;
330         double e,scale;
331         t_atom *atom;
332
333         atom=moldyn->atom;
334
335         /*
336          * - velocity scaling (E = 3/2 N k T), E: kinetic energy
337          */
338
339         e=0.0;
340         for(i=0;i<moldyn->count;i++)
341                 e+=0.5*atom[i].mass*v3_absolute_square(&(atom[i].v));
342         scale=(1.5*moldyn->count*K_BOLTZMANN*moldyn->t)/e;
343         if(type&VSCALE_INIT_EQUI) scale*=2.0; /* equipartition theorem */
344         scale=sqrt(scale);
345         for(i=0;i<moldyn->count;i++)
346                 v3_scale(&(atom[i].v),&(atom[i].v),scale);
347
348         return 0;
349 }
350
351 double get_e_kin(t_moldyn *moldyn) {
352
353         int i;
354         t_atom *atom;
355
356         atom=moldyn->atom;
357         moldyn->ekin=0.0;
358
359         for(i=0;i<moldyn->count;i++)
360                 moldyn->ekin+=0.5*atom[i].mass*v3_absolute_square(&(atom[i].v));
361
362         return moldyn->ekin;
363 }
364
365 double get_e_pot(t_moldyn *moldyn) {
366
367         return moldyn->energy;
368 }
369
370 double update_e_kin(t_moldyn *moldyn) {
371
372         return(get_e_kin(moldyn));
373 }
374
375 double get_total_energy(t_moldyn *moldyn) {
376
377         return(moldyn->ekin+moldyn->energy);
378 }
379
380 t_3dvec get_total_p(t_moldyn *moldyn) {
381
382         t_3dvec p,p_total;
383         int i;
384         t_atom *atom;
385
386         atom=moldyn->atom;
387
388         v3_zero(&p_total);
389         for(i=0;i<moldyn->count;i++) {
390                 v3_scale(&p,&(atom[i].v),atom[i].mass);
391                 v3_add(&p_total,&p_total,&p);
392         }
393
394         return p_total;
395 }
396
397 double estimate_time_step(t_moldyn *moldyn,double nn_dist) {
398
399         double tau;
400
401         /* nn_dist is the nearest neighbour distance */
402
403         if(moldyn->t==5.0) {
404                 printf("[moldyn] i do not estimate timesteps below %f K!\n",
405                        MOLDYN_CRITICAL_EST_TEMP);
406                 return 23.42;
407         }
408
409         tau=(0.05*nn_dist*moldyn->atom[0].mass)/sqrt(3.0*K_BOLTZMANN*moldyn->t);
410
411         return tau;     
412 }
413
414 /*
415  * numerical tricks
416  */
417
418 /* linked list / cell method */
419
420 int link_cell_init(t_moldyn *moldyn) {
421
422         t_linkcell *lc;
423         int i;
424         int fd;
425
426         fd=open("/dev/null",O_WRONLY);
427
428         lc=&(moldyn->lc);
429
430         /* partitioning the md cell */
431         lc->nx=moldyn->dim.x/moldyn->cutoff;
432         lc->x=moldyn->dim.x/lc->nx;
433         lc->ny=moldyn->dim.y/moldyn->cutoff;
434         lc->y=moldyn->dim.y/lc->ny;
435         lc->nz=moldyn->dim.z/moldyn->cutoff;
436         lc->z=moldyn->dim.z/lc->nz;
437
438         lc->cells=lc->nx*lc->ny*lc->nz;
439         lc->subcell=malloc(lc->cells*sizeof(t_list));
440
441         printf("[moldyn] initializing linked cells (%d)\n",lc->cells);
442
443         for(i=0;i<lc->cells;i++)
444                 //list_init(&(lc->subcell[i]),1);
445                 list_init(&(lc->subcell[i]),fd);
446
447         link_cell_update(moldyn);
448         
449         return 0;
450 }
451
452 int link_cell_update(t_moldyn *moldyn) {
453
454         int count,i,j,k;
455         int nx,ny,nz;
456         t_atom *atom;
457         t_linkcell *lc;
458
459         atom=moldyn->atom;
460         lc=&(moldyn->lc);
461
462         nx=lc->nx;
463         ny=lc->ny;
464         nz=lc->nz;
465
466         for(i=0;i<lc->cells;i++)
467                 list_destroy(&(moldyn->lc.subcell[i]));
468         
469         for(count=0;count<moldyn->count;count++) {
470                 i=(atom[count].r.x+(moldyn->dim.x/2))/lc->x;
471                 j=(atom[count].r.y+(moldyn->dim.y/2))/lc->y;
472                 k=(atom[count].r.z+(moldyn->dim.z/2))/lc->z;
473                 list_add_immediate_ptr(&(moldyn->lc.subcell[i+j*nx+k*nx*ny]),
474                                        &(atom[count]));
475         }
476
477         return 0;
478 }
479
480 int link_cell_neighbour_index(t_moldyn *moldyn,int i,int j,int k,t_list *cell) {
481
482         t_linkcell *lc;
483         int a;
484         int count1,count2;
485         int ci,cj,ck;
486         int nx,ny,nz;
487         int x,y,z;
488         u8 bx,by,bz;
489
490         lc=&(moldyn->lc);
491         nx=lc->nx;
492         ny=lc->ny;
493         nz=lc->nz;
494         count1=1;
495         count2=27;
496         a=nx*ny;
497
498
499         cell[0]=lc->subcell[i+j*nx+k*a];
500         for(ci=-1;ci<=1;ci++) {
501                 bx=0;
502                 x=i+ci;
503                 if((x<0)||(x>=nx)) {
504                         x=(x+nx)%nx;
505                         bx=1;
506                 }
507                 for(cj=-1;cj<=1;cj++) {
508                         by=0;
509                         y=j+cj;
510                         if((y<0)||(y>=ny)) {
511                                 y=(y+ny)%ny;
512                                 by=1;
513                         }
514                         for(ck=-1;ck<=1;ck++) {
515                                 bz=0;
516                                 z=k+ck;
517                                 if((z<0)||(z>=nz)) {
518                                         z=(z+nz)%nz;
519                                         bz=1;
520                                 }
521                                 if(!(ci|cj|ck)) continue;
522                                 if(bx|by|bz) {
523                                         cell[--count2]=lc->subcell[x+y*nx+z*a];
524                                 }
525                                 else {
526                                         cell[count1++]=lc->subcell[x+y*nx+z*a];
527                                 }
528                         }
529                 }
530         }
531
532         lc->dnlc=count2;
533         lc->countn=27;
534
535         return count2;
536 }
537
538 int link_cell_shutdown(t_moldyn *moldyn) {
539
540         int i;
541         t_linkcell *lc;
542
543         lc=&(moldyn->lc);
544
545         for(i=0;i<lc->nx*lc->ny*lc->nz;i++)
546                 list_shutdown(&(moldyn->lc.subcell[i]));
547
548         return 0;
549 }
550
551 int moldyn_add_schedule(t_moldyn *moldyn,int runs,double tau) {
552
553         int count;
554         void *ptr;
555         t_moldyn_schedule *schedule;
556
557         schedule=&(moldyn->schedule);
558         count=++(schedule->content_count);
559
560         ptr=realloc(moldyn->schedule.runs,count*sizeof(int));
561         if(!ptr) {
562                 perror("[moldyn] realloc (runs)");
563                 return -1;
564         }
565         moldyn->schedule.runs=ptr;
566         moldyn->schedule.runs[count-1]=runs;
567
568         ptr=realloc(schedule->tau,count*sizeof(double));
569         if(!ptr) {
570                 perror("[moldyn] realloc (tau)");
571                 return -1;
572         }
573         moldyn->schedule.tau=ptr;
574         moldyn->schedule.tau[count-1]=tau;
575
576         return 0;
577 }
578
579 int moldyn_set_schedule_hook(t_moldyn *moldyn,void *hook,void *hook_params) {
580
581         moldyn->schedule.hook=hook;
582         moldyn->schedule.hook_params=hook_params;
583         
584         return 0;
585 }
586
587 /*
588  *
589  * 'integration of newtons equation' - algorithms
590  *
591  */
592
593 /* start the integration */
594
595 int moldyn_integrate(t_moldyn *moldyn) {
596
597         int i,sched;
598         unsigned int e,m,s,v;
599         t_3dvec p;
600         t_moldyn_schedule *schedule;
601         t_atom *atom;
602         int fd;
603         char fb[128];
604         double ds;
605
606         schedule=&(moldyn->schedule);
607         atom=moldyn->atom;
608
609         /* initialize linked cell method */
610         link_cell_init(moldyn);
611
612         /* logging & visualization */
613         e=moldyn->ewrite;
614         m=moldyn->mwrite;
615         s=moldyn->swrite;
616         v=moldyn->vwrite;
617
618         /* sqaure of some variables */
619         moldyn->tau_square=moldyn->tau*moldyn->tau;
620         moldyn->cutoff_square=moldyn->cutoff*moldyn->cutoff;
621
622         /* calculate initial forces */
623         potential_force_calc(moldyn);
624
625         /* do some checks before we actually start calculating bullshit */
626         if(moldyn->cutoff>0.5*moldyn->dim.x)
627                 printf("[moldyn] warning: cutoff > 0.5 x dim.x\n");
628         if(moldyn->cutoff>0.5*moldyn->dim.y)
629                 printf("[moldyn] warning: cutoff > 0.5 x dim.y\n");
630         if(moldyn->cutoff>0.5*moldyn->dim.z)
631                 printf("[moldyn] warning: cutoff > 0.5 x dim.z\n");
632         ds=0.5*atom[0].f.x*moldyn->tau_square/atom[0].mass;
633         if(ds>0.05*moldyn->nnd)
634                 printf("[moldyn] warning: forces too high / tau too small!\n");
635
636         /* zero absolute time */
637         moldyn->time=0.0;
638
639         for(sched=0;sched<moldyn->schedule.content_count;sched++) {
640
641                 /* setting amount of runs and finite time step size */
642                 moldyn->tau=schedule->tau[sched];
643                 moldyn->tau_square=moldyn->tau*moldyn->tau;
644                 moldyn->time_steps=schedule->runs[sched];
645
646         /* integration according to schedule */
647
648         for(i=0;i<moldyn->time_steps;i++) {
649
650                 /* integration step */
651                 moldyn->integrate(moldyn);
652
653                 /* increase absolute time */
654                 moldyn->time+=moldyn->tau;
655
656                 /* check for log & visualization */
657                 if(e) {
658                         if(!(i%e))
659                                 dprintf(moldyn->efd,
660                                         "%.15f %.45f %.45f %.45f\n",
661                                         moldyn->time,update_e_kin(moldyn),
662                                         moldyn->energy,
663                                         get_total_energy(moldyn));
664                 }
665                 if(m) {
666                         if(!(i%m)) {
667                                 p=get_total_p(moldyn);
668                                 dprintf(moldyn->mfd,
669                                         "%.15f %.45f\n",moldyn->time,
670                                         v3_norm(&p));
671                         }
672                 }
673                 if(s) {
674                         if(!(i%s)) {
675                                 snprintf(fb,128,"%s-%f-%.15f.save",moldyn->sfb,
676                                          moldyn->t,i*moldyn->tau);
677                                 fd=open(fb,O_WRONLY|O_TRUNC|O_CREAT);
678                                 if(fd<0) perror("[moldyn] save fd open");
679                                 else {
680                                         write(fd,moldyn,sizeof(t_moldyn));
681                                         write(fd,moldyn->atom,
682                                               moldyn->count*sizeof(t_atom));
683                                 }
684                                 close(fd);
685                         }       
686                 }
687                 if(v) {
688                         if(!(i%v)) {
689                                 visual_atoms(&(moldyn->vis),moldyn->time,
690                                              moldyn->atom,moldyn->count);
691                                 printf("\rsched: %d, steps: %d",sched,i);
692                                 fflush(stdout);
693                         }
694                 }
695
696         }
697
698                 /* check for hooks */
699                 if(schedule->hook)
700                         schedule->hook(moldyn,schedule->hook_params);
701
702         }
703
704         return 0;
705 }
706
707 /* velocity verlet */
708
709 int velocity_verlet(t_moldyn *moldyn) {
710
711         int i,count;
712         double tau,tau_square;
713         t_3dvec delta;
714         t_atom *atom;
715
716         atom=moldyn->atom;
717         count=moldyn->count;
718         tau=moldyn->tau;
719         tau_square=moldyn->tau_square;
720
721         for(i=0;i<count;i++) {
722                 /* new positions */
723                 v3_scale(&delta,&(atom[i].v),tau);
724                 v3_add(&(atom[i].r),&(atom[i].r),&delta);
725                 v3_scale(&delta,&(atom[i].f),0.5*tau_square/atom[i].mass);
726                 v3_add(&(atom[i].r),&(atom[i].r),&delta);
727                 check_per_bound(moldyn,&(atom[i].r));
728
729                 /* velocities */
730                 v3_scale(&delta,&(atom[i].f),0.5*tau/atom[i].mass);
731                 v3_add(&(atom[i].v),&(atom[i].v),&delta);
732         }
733
734         /* neighbour list update */
735         link_cell_update(moldyn);
736
737         /* forces depending on chosen potential */
738         potential_force_calc(moldyn);
739         //moldyn->potential_force_function(moldyn);
740
741         for(i=0;i<count;i++) {
742                 /* again velocities */
743                 v3_scale(&delta,&(atom[i].f),0.5*tau/atom[i].mass);
744                 v3_add(&(atom[i].v),&(atom[i].v),&delta);
745         }
746
747         return 0;
748 }
749
750
751 /*
752  *
753  * potentials & corresponding forces
754  * 
755  */
756
757 /* generic potential and force calculation */
758
759 int potential_force_calc(t_moldyn *moldyn) {
760
761         int i,j,k,count;
762         t_atom *atom,*btom,*ktom;
763         t_linkcell *lc;
764         t_list neighbour[27];
765         t_list *this,*thisk,*neighbourk;
766         u8 bc,bck;
767         int countn,dnlc;
768
769         count=moldyn->count;
770         atom=moldyn->atom;
771         lc=&(moldyn->lc);
772
773         /* reset energy */
774         moldyn->energy=0.0;
775
776         for(i=0;i<count;i++) {
777         
778                 /* reset force */
779                 v3_zero(&(atom[i].f));
780
781                 /* single particle potential/force */
782                 if(atom[i].attr&ATOM_ATTR_1BP)
783                         moldyn->func1b(moldyn,&(atom[i]));
784
785                 /* 2 body pair potential/force */
786                 if(atom[i].attr&(ATOM_ATTR_2BP|ATOM_ATTR_3BP)) {
787         
788                         link_cell_neighbour_index(moldyn,
789                                 (atom[i].r.x+moldyn->dim.x/2)/lc->x,
790                                 (atom[i].r.y+moldyn->dim.y/2)/lc->y,
791                                 (atom[i].r.z+moldyn->dim.z/2)/lc->z,
792                                 neighbour);
793
794                         countn=lc->countn;
795                         dnlc=lc->dnlc;
796
797                         for(j=0;j<countn;j++) {
798
799                                 this=&(neighbour[j]);
800                                 list_reset(this);
801
802                                 if(this->start==NULL)
803                                         continue;
804
805                                 bc=(j<dnlc)?0:1;
806
807                                 do {
808                                         btom=this->current->data;
809
810                                         if(btom==&(atom[i]))
811                                                 continue;
812
813                                         if((btom->attr&ATOM_ATTR_2BP)&
814                                            (atom[i].attr&ATOM_ATTR_2BP))
815                                                 moldyn->func2b(moldyn,
816                                                                &(atom[i]),
817                                                                btom,
818                                                                bc);
819
820                                         /* 3 body potential/force */
821
822                                         if(!(atom[i].attr&ATOM_ATTR_3BP)||
823                                            !(btom->attr&ATOM_ATTR_3BP))
824                                                 continue;
825
826                                         link_cell_neighbour_index(moldyn,
827                                            (btom->r.x+moldyn->dim.x/2)/lc->x,
828                                            (btom->r.y+moldyn->dim.y/2)/lc->y,
829                                            (btom->r.z+moldyn->dim.z/2)/lc->z,
830                                            neighbourk);
831
832                                         for(k=0;k<lc->countn;k++) {
833
834                                                 thisk=&(neighbourk[k]);
835                                                 list_reset(thisk);
836                                         
837                                                 if(thisk->start==NULL)
838                                                         continue;
839
840                                                 bck=(k<lc->dnlc)?0:1;
841
842                                                 do {
843
844                         ktom=thisk->current->data;
845
846                         if(!(ktom->attr&ATOM_ATTR_3BP))
847                                 continue;
848
849                         if(ktom==btom)
850                                 continue;
851
852                         if(ktom==&(atom[i]))
853                                 continue;
854
855                         moldyn->func3b(moldyn,&(atom[i]),btom,ktom,bck);
856
857                                                 } while(list_next(thisk)!=\
858                                                         L_NO_NEXT_ELEMENT);
859
860                                         }
861                                         
862                                 } while(list_next(this)!=L_NO_NEXT_ELEMENT);
863                         }
864                 }
865         }
866
867         return 0;
868 }
869
870 /*
871  * periodic boundayr checking
872  */
873
874 int check_per_bound(t_moldyn *moldyn,t_3dvec *a) {
875         
876         double x,y,z;
877         t_3dvec *dim;
878
879         dim=&(moldyn->dim);
880
881         x=0.5*dim->x;
882         y=0.5*dim->y;
883         z=0.5*dim->z;
884
885         if(moldyn->status&MOLDYN_STAT_PBX) {
886                 if(a->x>=x) a->x-=dim->x;
887                 else if(-a->x>x) a->x+=dim->x;
888         }
889         if(moldyn->status&MOLDYN_STAT_PBY) {
890                 if(a->y>=y) a->y-=dim->y;
891                 else if(-a->y>y) a->y+=dim->y;
892         }
893         if(moldyn->status&MOLDYN_STAT_PBZ) {
894                 if(a->z>=z) a->z-=dim->z;
895                 else if(-a->z>z) a->z+=dim->z;
896         }
897
898         return 0;
899 }
900         
901
902 /*
903  * example potentials
904  */
905
906 /* harmonic oscillator potential and force */
907
908 int harmonic_oscillator(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
909
910         t_ho_params *params;
911         t_3dvec force,distance;
912         double d;
913         double sc,equi_dist;
914
915         params=moldyn->pot2b_params;
916         sc=params->spring_constant;
917         equi_dist=params->equilibrium_distance;
918
919         v3_sub(&distance,&(ai->r),&(aj->r));
920         
921         if(bc) check_per_bound(moldyn,&distance);
922         d=v3_norm(&distance);
923         if(d<=moldyn->cutoff) {
924                 /* energy is 1/2 (d-d0)^2, but we will add this twice ... */
925                 moldyn->energy+=(0.25*sc*(d-equi_dist)*(d-equi_dist));
926                 v3_scale(&force,&distance,-sc*(1.0-(equi_dist/d)));
927                 v3_add(&(ai->f),&(ai->f),&force);
928         }
929
930         return 0;
931 }
932
933 /* lennard jones potential & force for one sort of atoms */
934  
935 int lennard_jones(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
936
937         t_lj_params *params;
938         t_3dvec force,distance;
939         double d,h1,h2;
940         double eps,sig6,sig12;
941
942         params=moldyn->pot2b_params;
943         eps=params->epsilon4;
944         sig6=params->sigma6;
945         sig12=params->sigma12;
946
947         v3_sub(&distance,&(ai->r),&(aj->r));
948         if(bc) check_per_bound(moldyn,&distance);
949         d=v3_absolute_square(&distance);        /* 1/r^2 */
950         if(d<=moldyn->cutoff_square) {
951                 d=1.0/d;                        /* 1/r^2 */
952                 h2=d*d;                         /* 1/r^4 */
953                 h2*=d;                          /* 1/r^6 */
954                 h1=h2*h2;                       /* 1/r^12 */
955                 /* energy is eps*..., but we will add this twice ... */
956                 moldyn->energy+=0.5*eps*(sig12*h1-sig6*h2);
957                 h2*=d;                          /* 1/r^8 */
958                 h1*=d;                          /* 1/r^14 */
959                 h2*=6*sig6;
960                 h1*=12*sig12;
961                 d=+h1-h2;
962                 d*=eps;
963                 v3_scale(&force,&distance,d);
964                 v3_add(&(ai->f),&(ai->f),&force);
965         }
966
967         return 0;
968 }
969
970 /*
971  * tersoff potential & force for 2 sorts of atoms
972  */
973
974 /* tersoff 1 body part */
975 int tersoff_mult_1bp(t_moldyn *moldyn,t_atom *ai) {
976
977         int num;
978         t_tersoff_mult_params *params;
979         t_tersoff_exchange *exchange;
980         
981         num=ai->bnum;
982         params=moldyn->pot1b_params;
983         exchange=&(params->exchange);
984
985         /*
986          * simple: point constant parameters only depending on atom i to
987          *         their right values
988          */
989
990         exchange->beta=&(params->beta[num]);
991         exchange->n=&(params->n[num]);
992         exchange->c=&(params->c[num]);
993         exchange->d=&(params->d[num]);
994         exchange->h=&(params->h[num]);
995
996         exchange->betan=pow(*(exchange->beta),*(exchange->n));
997         exchange->c2=params->c[num]*params->c[num];
998         exchange->d2=params->d[num]*params->d[num];
999         exchange->c2d2=exchange->c2/exchange->d2;
1000
1001         return 0;
1002 }
1003         
1004 /* tersoff 2 body part */
1005 int tersoff_mult_2bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,u8 bc) {
1006
1007         t_tersoff_mult_params *params;
1008         t_tersoff_exchange *exchange;
1009         t_3dvec dist_ij,force;
1010         double d_ij;
1011         double A,B,R,S,lambda,mu;
1012         double f_r,df_r;
1013         double f_c,df_c;
1014         int num;
1015         double s_r;
1016         double arg;
1017         double scale;
1018
1019         params=moldyn->pot2b_params;
1020         num=ai->bnum;
1021         exchange=&(params->exchange);
1022
1023         exchange->run3bp=0;
1024         
1025         /*
1026          * we need: f_c, df_c, f_r, df_r
1027          *
1028          * therefore we need: R, S, A, lambda
1029          */
1030
1031         v3_sub(&dist_ij,&(ai->r),&(aj->r));
1032
1033         if(bc) check_per_bound(moldyn,&dist_ij);
1034
1035         /* save for use in 3bp */ /* REALLY ?!?!?! */
1036         exchange->dist_ij=dist_ij;
1037
1038         /* constants */
1039         if(num==aj->bnum) {
1040                 S=params->S[num];
1041                 R=params->R[num];
1042                 A=params->A[num];
1043                 lambda=params->lambda[num];
1044                 /* more constants depending of atoms i and j, needed in 3bp */
1045                 params->exchange.B=&(params->B[num]);
1046                 params->exchange.mu=&(params->mu[num]);
1047                 mu=params->mu[num];
1048                 params->exchange.chi=1.0;
1049         }
1050         else {
1051                 S=params->Smixed;
1052                 R=params->Rmixed;
1053                 A=params->Amixed;
1054                 lambda=params->lambda_m;
1055                 /* more constants depending of atoms i and j, needed in 3bp */
1056                 params->exchange.B=&(params->Bmixed);
1057                 params->exchange.mu=&(params->mu_m);
1058                 mu=params->mu_m;
1059                 params->exchange.chi=params->chi;
1060         }
1061
1062         d_ij=v3_norm(&dist_ij);
1063
1064         /* save for use in 3bp */
1065         exchange->d_ij=d_ij;
1066
1067         if(d_ij>S)
1068                 return 0;
1069
1070         f_r=A*exp(-lambda*d_ij);
1071         df_r=-lambda*f_r/d_ij;
1072
1073         /* f_a, df_a calc + save for 3bp use */
1074         exchange->f_a=-B*exp(-mu*d_ij);
1075         exchange->df_a=-mu*exchange->f_a/d_ij;
1076
1077         if(d_ij<R) {
1078                 /* f_c = 1, df_c = 0 */
1079                 f_c=1.0;
1080                 df_c=0.0;
1081                 v3_scale(&force,&dist_ij,df_r);
1082         }
1083         else {
1084                 s_r=S-R;
1085                 arg=M_PI*(d_ij-R)/s_r;
1086                 f_c=0.5+0.5*cos(arg);
1087                 df_c=-0.5*sin(arg)*(M_PI/(s_r*d_ij));
1088                 scale=df_c*f_r+df_r*f_c;
1089                 v3_scale(&force,&dist_ij,scale);
1090         }
1091
1092         /* add forces */
1093         v3_add(&(ai->f),&(ai->f),&force);
1094         /* energy is 0.5 f_r f_c, but we will sum it up twice ... */
1095         moldyn->energy+=(0.25*f_r*f_c);
1096
1097         /* save for use in 3bp */
1098         exchange->f_c=f_c;
1099         exchange->df_c=df_c;
1100
1101         /* enable the run of 3bp function */
1102         exchange->run3bp=1;
1103
1104         return 0;
1105 }
1106
1107 /* tersoff 3 body part */
1108
1109 int tersoff_mult_3bp(t_moldyn *moldyn,t_atom *ai,t_atom *aj,t_atom *ak,u8 bc) {
1110
1111         t_tersoff_mult_params *params;
1112         t_tersoff_exchange *exchange;
1113         t_3dvec dist_ij,dist_ik,dist_jk;
1114         t_3dvec temp,force;
1115         double R,S,s_r;
1116         double d_ij,d_ij2,d_ik,d_jk;
1117         double f_c,df_c,b_ij,f_a,df_a;
1118         double f_c_ik,df_c_ik,arg;
1119         double scale;
1120         double chi;
1121         double n,c,d,h,beta,betan;
1122         double c2,d2,c2d2;
1123         double numer,denom;
1124         double theta,cos_theta,sin_theta;
1125         double d_theta,d_theta1,d_theta2;
1126         double h_cos,h_cos2,d2_h_cos2;
1127         double frac1,bracket1,bracket2,bracket2_n_1,bracket2_n;
1128         double bracket3,bracket3_pow_1,bracket3_pow;
1129         int num;
1130
1131         params=moldyn->pot3b_params;
1132         num=ai->bnum;
1133         exchange=&(params->exchange);
1134
1135         if(!(exchange->run3bp))
1136                 return 0;
1137
1138         /*
1139          * we need: f_c, d_fc, b_ij, db_ij, f_a, df_a
1140          *
1141          * we got f_c, df_c, f_a, df_a from 2bp calculation
1142          */
1143
1144         d_ij=exchange->d_ij;
1145         d_ij2=exchange->d_ij2;
1146
1147         f_a=params->exchange.f_a;
1148         df_a=params->exchange.df_a;
1149         
1150         /* d_ij is <= S, as we didn't return so far! */
1151
1152         /*
1153          * calc of b_ij (scalar) and db_ij (vector)
1154          *
1155          * - for b_ij: chi, beta, f_c_ik, w(=1), c, d, h, n, cos_theta
1156          *
1157          * - for db_ij: d_theta, sin_theta, cos_theta, f_c_ik, df_c_ik,
1158          *              w_ik,
1159          *
1160          */
1161
1162         
1163         v3_sub(&dist_ik,&(ai->r),&(ak->r));
1164         if(bc) check_per_bound(moldyn,&dist_ik);
1165         d_ik=v3_norm(&dist_ik);
1166
1167         /* constants for f_c_ik calc */
1168         if(num==ak->bnum) {
1169                 R=params->R[num];
1170                 S=params->S[num];
1171         }
1172         else {
1173                 R=params->Rmixed;
1174                 S=params->Smixed;
1175         }
1176
1177         /* calc of f_c_ik */
1178         if(d_ik>S)
1179                 return 0;
1180
1181         if(d_ik<R) {
1182                 /* f_c_ik = 1, df_c_ik = 0 */
1183                 f_c_ik=1.0;
1184                 df_c_ik=0.0;
1185         }
1186         else {
1187                 s_r=S-R;
1188                 arg=M_PI*(d_ik-R)/s_r;
1189                 f_c_ik=0.5+0.5*cos(arg);
1190                 df_c_ik=-0.5*sin(arg)*(M_PI/(s_r*d_ik));
1191         }
1192         
1193         v3_sub(&dist_jk,&(aj->r),&(ak->r));
1194         if(bc) check_per_bound(moldyn,&dist_jk);
1195         d_jk=v3_norm(&dist_jk);
1196
1197         beta=*(exchange->beta);
1198         betan=exchange->betan;
1199         n=*(exchange->n);
1200         c=*(exchange->c);
1201         d=*(exchange->d);
1202         h=*(exchange->h);
1203         c2=exchange->c2;
1204         d2=exchange->d2;
1205         c2d2=exchange->c2d2;
1206
1207         numer=d_ij2+d_ik*d_ik-d_jk*d_jk;
1208         denom=2*d_ij*d_ik;
1209         cos_theta=numer/denom;
1210         sin_theta=sqrt(1.0-(cos_theta*cos_theta));
1211         theta=acos(cos_theta);
1212         d_theta=(-1.0/sqrt(1.0-cos_theta*cos_theta))/(denom*denom);
1213         d_theta1=2*denom-numer*2*d_ik/d_ij;
1214         d_theta2=2*denom-numer*2*d_ij/d_ik;
1215         d_theta1*=d_theta;
1216         d_theta2*=d_theta;
1217
1218         h_cos=(h-cos_theta);
1219         h_cos2=h_cos*h_cos;
1220         d2_h_cos2=d2-h_cos2;
1221
1222         /* some usefull expressions */
1223         frac1=c2/(d2-h_cos2);
1224         bracket1=1+c2d2-frac1;
1225         bracket2=f_c_ik*bracket1;
1226         bracket2_n_1=pow(bracket2,n-1.0);
1227         bracket2_n=bracket2_n_1*bracket2;
1228         bracket3=1+betan*bracket2_n;
1229         bracket3_pow_1=pow(bracket3,(-1.0/(2.0*n))-1.0);
1230         bracket3_pow=bracket3_pow_1*bracket3;
1231
1232         /* now go on with calc of b_ij and derivation of b_ij */
1233         b_ij=chi*bracket3_pow;
1234
1235         /* derivation of theta */
1236         v3_scale(&force,&dist_ij,d_theta1);
1237         v3_scale(&temp,&dist_ik,d_theta2);
1238         v3_add(&force,&force,&temp);
1239
1240         /* part 1 of derivation of b_ij */
1241         v3_scale(&force,&force,sin_theta*2*h_cos*f_c_ik*frac1);
1242
1243         /* part 2 of derivation of b_ij */
1244         v3_scale(&temp,&dist_ik,df_c_ik*bracket1);
1245
1246         /* sum up and scale ... */
1247         v3_add(&temp,&temp,&force);
1248         scale=bracket2_n_1*n*betan*(1+betan*bracket3_pow_1)*chi*(1.0/(2.0*n));
1249         v3_scale(&temp,&temp,scale);
1250
1251         /* now construct an energy and a force out of that */
1252         v3_scale(&temp,&temp,f_a);
1253         v3_scale(&force,&dist_ij,df_a*b_ij);
1254         v3_add(&temp,&temp,&force);
1255         v3_scale(&temp,&temp,f_c);
1256         v3_scale(&force,&dist_ij,df_c*b_ij*f_a);
1257         v3_add(&force,&force,&temp);
1258
1259         /* add forces */
1260         v3_add(&(ai->f),&(ai->f),&force);
1261         /* energy is 0.5 f_r f_c, but we will sum it up twice ... */
1262         moldyn->energy+=(0.25*f_a*b_ij*f_c);
1263                                 
1264         return 0;
1265 }
1266