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