put repulsive part to 3bp/j2 function
[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 #include "report/report.h"
20
21 int moldyn_init(t_moldyn *moldyn,int argc,char **argv) {
22
23         printf("[moldyn] init\n");
24
25         memset(moldyn,0,sizeof(t_moldyn));
26
27         rand_init(&(moldyn->random),NULL,1);
28         moldyn->random.status|=RAND_STAT_VERBOSE;
29
30         return 0;
31 }
32
33 int moldyn_shutdown(t_moldyn *moldyn) {
34
35         printf("[moldyn] shutdown\n");
36
37         moldyn_log_shutdown(moldyn);
38         link_cell_shutdown(moldyn);
39         rand_close(&(moldyn->random));
40         free(moldyn->atom);
41
42         return 0;
43 }
44
45 int set_int_alg(t_moldyn *moldyn,u8 algo) {
46
47         printf("[moldyn] integration algorithm: ");
48
49         switch(algo) {
50                 case MOLDYN_INTEGRATE_VERLET:
51                         moldyn->integrate=velocity_verlet;
52                         printf("velocity verlet\n");
53                         break;
54                 default:
55                         printf("unknown integration algorithm: %02x\n",algo);
56                         printf("unknown\n");
57                         return -1;
58         }
59
60         return 0;
61 }
62
63 int set_cutoff(t_moldyn *moldyn,double cutoff) {
64
65         moldyn->cutoff=cutoff;
66
67         printf("[moldyn] cutoff [A]: %f\n",moldyn->cutoff);
68
69         return 0;
70 }
71
72 int set_temperature(t_moldyn *moldyn,double t_ref) {
73
74         moldyn->t_ref=t_ref;
75
76         printf("[moldyn] temperature [K]: %f\n",moldyn->t_ref);
77
78         return 0;
79 }
80
81 int set_pressure(t_moldyn *moldyn,double p_ref) {
82
83         moldyn->p_ref=p_ref;
84
85         printf("[moldyn] pressure [bar]: %f\n",moldyn->p_ref/BAR);
86
87         return 0;
88 }
89
90 int set_pt_scale(t_moldyn *moldyn,u8 ptype,double ptc,u8 ttype,double ttc) {
91
92         moldyn->pt_scale=(ptype|ttype);
93         moldyn->t_tc=ttc;
94         moldyn->p_tc=ptc;
95
96         printf("[moldyn] p/t scaling:\n");
97
98         printf("  p: %s",ptype?"yes":"no ");
99         if(ptype)
100                 printf(" | type: %02x | factor: %f",ptype,ptc);
101         printf("\n");
102
103         printf("  t: %s",ttype?"yes":"no ");
104         if(ttype)
105                 printf(" | type: %02x | factor: %f",ttype,ttc);
106         printf("\n");
107
108         return 0;
109 }
110
111 int set_dim(t_moldyn *moldyn,double x,double y,double z,u8 visualize) {
112
113         moldyn->dim.x=x;
114         moldyn->dim.y=y;
115         moldyn->dim.z=z;
116
117         moldyn->volume=x*y*z;
118
119         if(visualize) {
120                 moldyn->vis.dim.x=x;
121                 moldyn->vis.dim.y=y;
122                 moldyn->vis.dim.z=z;
123         }
124
125         moldyn->dv=0.000001*moldyn->volume;
126
127         printf("[moldyn] dimensions in A and A^3 respectively:\n");
128         printf("  x: %f\n",moldyn->dim.x);
129         printf("  y: %f\n",moldyn->dim.y);
130         printf("  z: %f\n",moldyn->dim.z);
131         printf("  volume: %f\n",moldyn->volume);
132         printf("  visualize simulation box: %s\n",visualize?"yes":"no");
133         printf("  delta volume (pressure calc): %f\n",moldyn->dv);
134
135         return 0;
136 }
137
138 int set_nn_dist(t_moldyn *moldyn,double dist) {
139
140         moldyn->nnd=dist;
141
142         return 0;
143 }
144
145 int set_pbc(t_moldyn *moldyn,u8 x,u8 y,u8 z) {
146
147         printf("[moldyn] periodic boundary conditions:\n");
148
149         if(x)
150                 moldyn->status|=MOLDYN_STAT_PBX;
151
152         if(y)
153                 moldyn->status|=MOLDYN_STAT_PBY;
154
155         if(z)
156                 moldyn->status|=MOLDYN_STAT_PBZ;
157
158         printf("  x: %s\n",x?"yes":"no");
159         printf("  y: %s\n",y?"yes":"no");
160         printf("  z: %s\n",z?"yes":"no");
161
162         return 0;
163 }
164
165 int set_potential1b(t_moldyn *moldyn,pf_func1b func) {
166
167         moldyn->func1b=func;
168
169         return 0;
170 }
171
172 int set_potential2b(t_moldyn *moldyn,pf_func2b func) {
173
174         moldyn->func2b=func;
175
176         return 0;
177 }
178
179 int set_potential3b_j1(t_moldyn *moldyn,pf_func2b func) {
180
181         moldyn->func3b_j1=func;
182
183         return 0;
184 }
185
186 int set_potential3b_j2(t_moldyn *moldyn,pf_func2b func) {
187
188         moldyn->func3b_j2=func;
189
190         return 0;
191 }
192
193 int set_potential3b_j3(t_moldyn *moldyn,pf_func2b func) {
194
195         moldyn->func3b_j3=func;
196
197         return 0;
198 }
199
200 int set_potential3b_k1(t_moldyn *moldyn,pf_func3b func) {
201
202         moldyn->func3b_k1=func;
203
204         return 0;
205 }
206
207 int set_potential3b_k2(t_moldyn *moldyn,pf_func3b func) {
208
209         moldyn->func3b_k2=func;
210
211         return 0;
212 }
213
214 int set_potential_params(t_moldyn *moldyn,void *params) {
215
216         moldyn->pot_params=params;
217
218         return 0;
219 }
220
221 int moldyn_set_log_dir(t_moldyn *moldyn,char *dir) {
222
223         strncpy(moldyn->vlsdir,dir,127);
224
225         return 0;
226 }
227
228 int moldyn_set_report(t_moldyn *moldyn,char *author,char *title) {
229
230         strncpy(moldyn->rauthor,author,63);
231         strncpy(moldyn->rtitle,title,63);
232
233         return 0;
234 }
235         
236 int moldyn_set_log(t_moldyn *moldyn,u8 type,int timer) {
237
238         char filename[128];
239         int ret;
240
241         printf("[moldyn] set log: ");
242
243         switch(type) {
244                 case LOG_TOTAL_ENERGY:
245                         moldyn->ewrite=timer;
246                         snprintf(filename,127,"%s/energy",moldyn->vlsdir);
247                         moldyn->efd=open(filename,
248                                          O_WRONLY|O_CREAT|O_EXCL,
249                                          S_IRUSR|S_IWUSR);
250                         if(moldyn->efd<0) {
251                                 perror("[moldyn] energy log fd open");
252                                 return moldyn->efd;
253                         }
254                         dprintf(moldyn->efd,"# total energy log file\n");
255                         printf("total energy (%d)\n",timer);
256                         break;
257                 case LOG_TOTAL_MOMENTUM:
258                         moldyn->mwrite=timer;
259                         snprintf(filename,127,"%s/momentum",moldyn->vlsdir);
260                         moldyn->mfd=open(filename,
261                                          O_WRONLY|O_CREAT|O_EXCL,
262                                          S_IRUSR|S_IWUSR);
263                         if(moldyn->mfd<0) {
264                                 perror("[moldyn] momentum log fd open");
265                                 return moldyn->mfd;
266                         }
267                         dprintf(moldyn->efd,"# total momentum log file\n");
268                         printf("total momentum (%d)\n",timer);
269                         break;
270                 case LOG_PRESSURE:
271                         moldyn->pwrite=timer;
272                         snprintf(filename,127,"%s/pressure",moldyn->vlsdir);
273                         moldyn->pfd=open(filename,
274                                          O_WRONLY|O_CREAT|O_EXCL,
275                                          S_IRUSR|S_IWUSR);
276                         if(moldyn->pfd<0) {
277                                 perror("[moldyn] pressure log file\n");
278                                 return moldyn->pfd;
279                         }
280                         dprintf(moldyn->pfd,"# pressure log file\n");
281                         printf("pressure (%d)\n",timer);
282                         break;
283                 case LOG_TEMPERATURE:
284                         moldyn->twrite=timer;
285                         snprintf(filename,127,"%s/temperature",moldyn->vlsdir);
286                         moldyn->tfd=open(filename,
287                                          O_WRONLY|O_CREAT|O_EXCL,
288                                          S_IRUSR|S_IWUSR);
289                         if(moldyn->tfd<0) {
290                                 perror("[moldyn] temperature log file\n");
291                                 return moldyn->tfd;
292                         }
293                         dprintf(moldyn->tfd,"# temperature log file\n");
294                         printf("temperature (%d)\n",timer);
295                         break;
296                 case SAVE_STEP:
297                         moldyn->swrite=timer;
298                         printf("save file (%d)\n",timer);
299                         break;
300                 case VISUAL_STEP:
301                         moldyn->vwrite=timer;
302                         ret=visual_init(&(moldyn->vis),moldyn->vlsdir);
303                         if(ret<0) {
304                                 printf("[moldyn] visual init failure\n");
305                                 return ret;
306                         }
307                         printf("visual file (%d)\n",timer);
308                         break;
309                 case CREATE_REPORT:
310                         snprintf(filename,127,"%s/report.tex",moldyn->vlsdir);
311                         moldyn->rfd=open(filename,
312                                          O_WRONLY|O_CREAT|O_EXCL,
313                                          S_IRUSR|S_IWUSR);
314                         if(moldyn->rfd<0) {
315                                 perror("[moldyn] report fd open");      
316                                 return moldyn->rfd;
317                         }
318                         printf("report -> ");
319                         if(moldyn->efd) {
320                                 snprintf(filename,127,"%s/e_plot.scr",
321                                          moldyn->vlsdir);
322                                 moldyn->epfd=open(filename,
323                                                  O_WRONLY|O_CREAT|O_EXCL,
324                                                  S_IRUSR|S_IWUSR);
325                                 if(moldyn->epfd<0) {
326                                         perror("[moldyn] energy plot fd open");
327                                         return moldyn->epfd;
328                                 }
329                                 dprintf(moldyn->epfd,e_plot_script);
330                                 close(moldyn->epfd);
331                                 printf("energy ");
332                         }
333                         if(moldyn->pfd) {
334                                 snprintf(filename,127,"%s/pressure_plot.scr",
335                                          moldyn->vlsdir);
336                                 moldyn->ppfd=open(filename,
337                                                   O_WRONLY|O_CREAT|O_EXCL,
338                                                   S_IRUSR|S_IWUSR);
339                                 if(moldyn->ppfd<0) {
340                                         perror("[moldyn] p plot fd open");
341                                         return moldyn->ppfd;
342                                 }
343                                 dprintf(moldyn->ppfd,pressure_plot_script);
344                                 close(moldyn->ppfd);
345                                 printf("pressure ");
346                         }
347                         if(moldyn->tfd) {
348                                 snprintf(filename,127,"%s/temperature_plot.scr",
349                                          moldyn->vlsdir);
350                                 moldyn->tpfd=open(filename,
351                                                   O_WRONLY|O_CREAT|O_EXCL,
352                                                   S_IRUSR|S_IWUSR);
353                                 if(moldyn->tpfd<0) {
354                                         perror("[moldyn] t plot fd open");
355                                         return moldyn->tpfd;
356                                 }
357                                 dprintf(moldyn->tpfd,temperature_plot_script);
358                                 close(moldyn->tpfd);
359                                 printf("temperature ");
360                         }
361                         dprintf(moldyn->rfd,report_start,
362                                 moldyn->rauthor,moldyn->rtitle);
363                         printf("\n");
364                         break;
365                 default:
366                         printf("unknown log type: %02x\n",type);
367                         return -1;
368         }
369
370         return 0;
371 }
372
373 int moldyn_log_shutdown(t_moldyn *moldyn) {
374
375         char sc[256];
376
377         printf("[moldyn] log shutdown\n");
378         if(moldyn->efd) {
379                 close(moldyn->efd);
380                 if(moldyn->rfd) {
381                         dprintf(moldyn->rfd,report_energy);
382                         snprintf(sc,255,"cd %s && gnuplot e_plot.scr",
383                                  moldyn->vlsdir);
384                         system(sc);
385                 }
386         }
387         if(moldyn->mfd) close(moldyn->mfd);
388         if(moldyn->pfd) {
389                 close(moldyn->pfd);
390                 if(moldyn->rfd)
391                         dprintf(moldyn->rfd,report_pressure);
392                         snprintf(sc,255,"cd %s && gnuplot pressure_plot.scr",
393                                  moldyn->vlsdir);
394                         system(sc);
395         }
396         if(moldyn->tfd) {
397                 close(moldyn->tfd);
398                 if(moldyn->rfd)
399                         dprintf(moldyn->rfd,report_temperature);
400                         snprintf(sc,255,"cd %s && gnuplot temperature_plot.scr",
401                                  moldyn->vlsdir);
402                         system(sc);
403         }
404         if(moldyn->rfd) {
405                 dprintf(moldyn->rfd,report_end);
406                 close(moldyn->rfd);
407                 snprintf(sc,255,"cd %s && pdflatex report",moldyn->vlsdir);
408                 system(sc);
409                 snprintf(sc,255,"cd %s && pdflatex report",moldyn->vlsdir);
410                 system(sc);
411                 snprintf(sc,255,"cd %s && dvipdf report",moldyn->vlsdir);
412                 system(sc);
413         }
414         if(&(moldyn->vis)) visual_tini(&(moldyn->vis));
415
416         return 0;
417 }
418
419 /*
420  * creating lattice functions
421  */
422
423 int create_lattice(t_moldyn *moldyn,u8 type,double lc,int element,double mass,
424                    u8 attr,u8 brand,int a,int b,int c) {
425
426         int new,count;
427         int ret;
428         t_3dvec origin;
429         void *ptr;
430         t_atom *atom;
431
432         new=a*b*c;
433         count=moldyn->count;
434
435         /* how many atoms do we expect */
436         if(type==CUBIC) new*=1;
437         if(type==FCC) new*=4;
438         if(type==DIAMOND) new*=8;
439
440         /* allocate space for atoms */
441         ptr=realloc(moldyn->atom,(count+new)*sizeof(t_atom));
442         if(!ptr) {
443                 perror("[moldyn] realloc (create lattice)");
444                 return -1;
445         }
446         moldyn->atom=ptr;
447         atom=&(moldyn->atom[count]);
448
449         /* no atoms on the boundaries (only reason: it looks better!) */
450         origin.x=0.5*lc;
451         origin.y=0.5*lc;
452         origin.z=0.5*lc;
453
454         switch(type) {
455                 case CUBIC:
456                         set_nn_dist(moldyn,lc);
457                         ret=cubic_init(a,b,c,lc,atom,&origin);
458                         break;
459                 case FCC:
460                         v3_scale(&origin,&origin,0.5);
461                         set_nn_dist(moldyn,0.5*sqrt(2.0)*lc);
462                         ret=fcc_init(a,b,c,lc,atom,&origin);
463                         break;
464                 case DIAMOND:
465                         v3_scale(&origin,&origin,0.25);
466                         set_nn_dist(moldyn,0.25*sqrt(3.0)*lc);
467                         ret=diamond_init(a,b,c,lc,atom,&origin);
468                         break;
469                 default:
470                         printf("unknown lattice type (%02x)\n",type);
471                         return -1;
472         }
473
474         /* debug */
475         if(ret!=new) {
476                 printf("[moldyn] creating lattice failed\n");
477                 printf("  amount of atoms\n");
478                 printf("  - expected: %d\n",new);
479                 printf("  - created: %d\n",ret);
480                 return -1;
481         }
482
483         moldyn->count+=new;
484         printf("[moldyn] created lattice with %d atoms\n",new);
485
486         for(ret=0;ret<new;ret++) {
487                 atom[ret].element=element;
488                 atom[ret].mass=mass;
489                 atom[ret].attr=attr;
490                 atom[ret].brand=brand;
491                 atom[ret].tag=count+ret;
492                 check_per_bound(moldyn,&(atom[ret].r));
493         }
494
495         return ret;
496 }
497
498 /* cubic init */
499 int cubic_init(int a,int b,int c,double lc,t_atom *atom,t_3dvec *origin) {
500
501         int count;
502         t_3dvec r;
503         int i,j,k;
504         t_3dvec o;
505
506         count=0;
507         if(origin)
508                 v3_copy(&o,origin);
509         else
510                 v3_zero(&o);
511
512         r.x=o.x;
513         for(i=0;i<a;i++) {
514                 r.y=o.y;
515                 for(j=0;j<b;j++) {
516                         r.z=o.z;
517                         for(k=0;k<c;k++) {
518                                 v3_copy(&(atom[count].r),&r);
519                                 count+=1;
520                                 r.z+=lc;
521                         }
522                         r.y+=lc;
523                 }
524                 r.x+=lc;
525         }
526
527         for(i=0;i<count;i++) {
528                 atom[i].r.x-=(a*lc)/2.0;
529                 atom[i].r.y-=(b*lc)/2.0;
530                 atom[i].r.z-=(c*lc)/2.0;
531         }
532
533         return count;
534 }
535
536 /* fcc lattice init */
537 int fcc_init(int a,int b,int c,double lc,t_atom *atom,t_3dvec *origin) {
538
539         int count;
540         int i,j,k,l;
541         t_3dvec o,r,n;
542         t_3dvec basis[3];
543
544         count=0;
545         if(origin)
546                 v3_copy(&o,origin);
547         else
548                 v3_zero(&o);
549
550         /* construct the basis */
551         memset(basis,0,3*sizeof(t_3dvec));
552         basis[0].x=0.5*lc;
553         basis[0].y=0.5*lc;
554         basis[1].x=0.5*lc;
555         basis[1].z=0.5*lc;
556         basis[2].y=0.5*lc;
557         basis[2].z=0.5*lc;
558
559         /* fill up the room */
560         r.x=o.x;
561         for(i=0;i<a;i++) {
562                 r.y=o.y;
563                 for(j=0;j<b;j++) {
564                         r.z=o.z;
565                         for(k=0;k<c;k++) {
566                                 /* first atom */
567                                 v3_copy(&(atom[count].r),&r);
568                                 count+=1;
569                                 r.z+=lc;
570                                 /* the three face centered atoms */
571                                 for(l=0;l<3;l++) {
572                                         v3_add(&n,&r,&basis[l]);
573                                         v3_copy(&(atom[count].r),&n);
574                                         count+=1;
575                                 }
576                         }
577                         r.y+=lc;
578                 }
579                 r.x+=lc;
580         }
581                                 
582         /* coordinate transformation */
583         for(i=0;i<count;i++) {
584                 atom[i].r.x-=(a*lc)/2.0;
585                 atom[i].r.y-=(b*lc)/2.0;
586                 atom[i].r.z-=(c*lc)/2.0;
587         }
588
589         return count;
590 }
591
592 int diamond_init(int a,int b,int c,double lc,t_atom *atom,t_3dvec *origin) {
593
594         int count;
595         t_3dvec o;
596
597         count=fcc_init(a,b,c,lc,atom,origin);
598
599         o.x=0.25*lc;
600         o.y=0.25*lc;
601         o.z=0.25*lc;
602
603         if(origin) v3_add(&o,&o,origin);
604
605         count+=fcc_init(a,b,c,lc,&atom[count],&o);
606
607         return count;
608 }
609
610 int add_atom(t_moldyn *moldyn,int element,double mass,u8 brand,u8 attr,
611              t_3dvec *r,t_3dvec *v) {
612
613         t_atom *atom;
614         void *ptr;
615         int count;
616         
617         atom=moldyn->atom;
618         count=(moldyn->count)++;
619
620         ptr=realloc(atom,(count+1)*sizeof(t_atom));
621         if(!ptr) {
622                 perror("[moldyn] realloc (add atom)");
623                 return -1;
624         }
625         moldyn->atom=ptr;
626
627         atom=moldyn->atom;
628         atom[count].r=*r;
629         atom[count].v=*v;
630         atom[count].element=element;
631         atom[count].mass=mass;
632         atom[count].brand=brand;
633         atom[count].tag=count;
634         atom[count].attr=attr;
635
636         return 0;
637 }
638
639 int destroy_atoms(t_moldyn *moldyn) {
640
641         if(moldyn->atom) free(moldyn->atom);
642
643         return 0;
644 }
645
646 int thermal_init(t_moldyn *moldyn,u8 equi_init) {
647
648         /*
649          * - gaussian distribution of velocities
650          * - zero total momentum
651          * - velocity scaling (E = 3/2 N k T), E: kinetic energy
652          */
653
654         int i;
655         double v,sigma;
656         t_3dvec p_total,delta;
657         t_atom *atom;
658         t_random *random;
659
660         atom=moldyn->atom;
661         random=&(moldyn->random);
662
663         printf("[moldyn] thermal init (equi init: %s)\n",equi_init?"yes":"no");
664
665         /* gaussian distribution of velocities */
666         v3_zero(&p_total);
667         for(i=0;i<moldyn->count;i++) {
668                 sigma=sqrt(2.0*K_BOLTZMANN*moldyn->t_ref/atom[i].mass);
669                 /* x direction */
670                 v=sigma*rand_get_gauss(random);
671                 atom[i].v.x=v;
672                 p_total.x+=atom[i].mass*v;
673                 /* y direction */
674                 v=sigma*rand_get_gauss(random);
675                 atom[i].v.y=v;
676                 p_total.y+=atom[i].mass*v;
677                 /* z direction */
678                 v=sigma*rand_get_gauss(random);
679                 atom[i].v.z=v;
680                 p_total.z+=atom[i].mass*v;
681         }
682
683         /* zero total momentum */
684         v3_scale(&p_total,&p_total,1.0/moldyn->count);
685         for(i=0;i<moldyn->count;i++) {
686                 v3_scale(&delta,&p_total,1.0/atom[i].mass);
687                 v3_sub(&(atom[i].v),&(atom[i].v),&delta);
688         }
689
690         /* velocity scaling */
691         scale_velocity(moldyn,equi_init);
692
693         return 0;
694 }
695
696 double temperature_calc(t_moldyn *moldyn) {
697
698         /* assume up to date kinetic energy, which is 3/2 N k_B T */
699
700         moldyn->t=(2.0*moldyn->ekin)/(3.0*K_BOLTZMANN*moldyn->count);
701         moldyn->t_sum+=moldyn->t;
702         moldyn->mean_t=moldyn->t_sum/moldyn->total_steps;
703
704         return moldyn->t;
705 }
706
707 double get_temperature(t_moldyn *moldyn) {
708
709         return moldyn->t;
710 }
711
712 int scale_velocity(t_moldyn *moldyn,u8 equi_init) {
713
714         int i;
715         double e,scale;
716         t_atom *atom;
717         int count;
718
719         atom=moldyn->atom;
720
721         /*
722          * - velocity scaling (E = 3/2 N k T), E: kinetic energy
723          */
724
725         /* get kinetic energy / temperature & count involved atoms */
726         e=0.0;
727         count=0;
728         for(i=0;i<moldyn->count;i++) {
729                 if((equi_init&TRUE)||(atom[i].attr&ATOM_ATTR_HB)) {
730                         e+=atom[i].mass*v3_absolute_square(&(atom[i].v));
731                         count+=1;
732                 }
733         }
734         e*=0.5;
735         if(count!=0) moldyn->t=e/(1.5*count*K_BOLTZMANN);
736         else return 0;  /* no atoms involved in scaling! */
737         
738         /* (temporary) hack for e,t = 0 */
739         if(e==0.0) {
740         moldyn->t=0.0;
741                 if(moldyn->t_ref!=0.0) {
742                         thermal_init(moldyn,equi_init);
743                         return 0;
744                 }
745                 else
746                         return 0; /* no scaling needed */
747         }
748
749
750         /* get scaling factor */
751         scale=moldyn->t_ref/moldyn->t;
752         if(equi_init&TRUE)
753                 scale*=2.0;
754         else
755                 if(moldyn->pt_scale&T_SCALE_BERENDSEN)
756                         scale=1.0+(scale-1.0)/moldyn->t_tc;
757         scale=sqrt(scale);
758
759         /* velocity scaling */
760         for(i=0;i<moldyn->count;i++) {
761                 if((equi_init&TRUE)||(atom[i].attr&ATOM_ATTR_HB))
762                         v3_scale(&(atom[i].v),&(atom[i].v),scale);
763         }
764
765         return 0;
766 }
767
768 double ideal_gas_law_pressure(t_moldyn *moldyn) {
769
770         double p;
771
772         p=moldyn->count*moldyn->t*K_BOLTZMANN/moldyn->volume;
773
774         return p;
775 }
776
777 double pressure_calc(t_moldyn *moldyn) {
778
779         int i;
780         double v;
781         t_virial *virial;
782
783         /*
784          * PV = NkT + <W>
785          * W = 1/3 sum_i f_i r_i
786          * virial = sum_i f_i r_i
787          * 
788          * => P = (2 Ekin + virial) / (3V)
789          */
790
791         v=0.0;
792         for(i=0;i<moldyn->count;i++) {
793                 virial=&(moldyn->atom[i].virial);
794                 v+=(virial->xx+virial->yy+virial->zz);
795         }
796
797         /* assume up to date kinetic energy */
798         moldyn->p=2.0*moldyn->ekin+v;
799         moldyn->p/=(3.0*moldyn->volume);
800         moldyn->p_sum+=moldyn->p;
801         moldyn->mean_p=moldyn->p_sum/moldyn->total_steps;
802
803         /* pressure from 'absolute coordinates' virial */
804         virial=&(moldyn->virial);
805         v=virial->xx+virial->yy+virial->zz;
806         moldyn->gp=2.0*moldyn->ekin+v;
807         moldyn->gp/=(3.0*moldyn->volume);
808         moldyn->gp_sum+=moldyn->gp;
809         moldyn->mean_gp=moldyn->gp_sum/moldyn->total_steps;
810
811         return moldyn->p;
812 }       
813
814 double thermodynamic_pressure_calc(t_moldyn *moldyn) {
815
816         t_3dvec dim,*tp;
817         double u,p;
818         double scale,dv;
819         t_atom *store;
820
821         /*
822          * dU = - p dV
823          *
824          * => p = - dU/dV
825          *
826          * dV: dx,y,z = 0.001 x,y,z
827          */
828
829         scale=1.00000000000001;
830 printf("\n\nP-DEBUG:\n");
831
832         tp=&(moldyn->tp);
833         store=malloc(moldyn->count*sizeof(t_atom));
834         if(store==NULL) {
835                 printf("[moldyn] allocating store mem failed\n");
836                 return -1;
837         }
838
839         /* save unscaled potential energy + atom/dim configuration */
840         u=moldyn->energy;
841         memcpy(store,moldyn->atom,moldyn->count*sizeof(t_atom));
842         dim=moldyn->dim;
843
844         /* derivative with respect to x direction */
845         scale_dim(moldyn,scale,TRUE,0,0);
846         scale_atoms(moldyn,scale,TRUE,0,0);
847         dv=0.00000000000001*moldyn->dim.x*moldyn->dim.y*moldyn->dim.z;
848         link_cell_shutdown(moldyn);
849         link_cell_init(moldyn,QUIET);
850         potential_force_calc(moldyn);
851         tp->x=(moldyn->energy-u)/dv;
852         p=tp->x*tp->x;
853
854         /* restore atomic configuration + dim */
855         memcpy(moldyn->atom,store,moldyn->count*sizeof(t_atom));
856         moldyn->dim=dim;
857
858         /* derivative with respect to y direction */
859         scale_dim(moldyn,scale,0,TRUE,0);
860         scale_atoms(moldyn,scale,0,TRUE,0);
861         dv=0.00000000000001*moldyn->dim.y*moldyn->dim.x*moldyn->dim.z;
862         link_cell_shutdown(moldyn);
863         link_cell_init(moldyn,QUIET);
864         potential_force_calc(moldyn);
865         tp->y=(moldyn->energy-u)/dv;
866         p+=tp->y*tp->y;
867
868         /* restore atomic configuration + dim */
869         memcpy(moldyn->atom,store,moldyn->count*sizeof(t_atom));
870         moldyn->dim=dim;
871
872         /* derivative with respect to z direction */
873         scale_dim(moldyn,scale,0,0,TRUE);
874         scale_atoms(moldyn,scale,0,0,TRUE);
875         dv=0.00000000000001*moldyn->dim.z*moldyn->dim.x*moldyn->dim.y;
876         link_cell_shutdown(moldyn);
877         link_cell_init(moldyn,QUIET);
878         potential_force_calc(moldyn);
879         tp->z=(moldyn->energy-u)/dv;
880         p+=tp->z*tp->z;
881
882         /* restore atomic configuration + dim */
883         memcpy(moldyn->atom,store,moldyn->count*sizeof(t_atom));
884         moldyn->dim=dim;
885
886         /* restore energy */
887         moldyn->energy=u;
888
889         link_cell_shutdown(moldyn);
890         link_cell_init(moldyn,QUIET);
891
892         return sqrt(p);
893 }
894
895 double get_pressure(t_moldyn *moldyn) {
896
897         return moldyn->p;
898
899 }
900
901 int scale_dim(t_moldyn *moldyn,double scale,u8 x,u8 y,u8 z) {
902
903         t_3dvec *dim;
904
905         dim=&(moldyn->dim);
906
907         if(x) dim->x*=scale;
908         if(y) dim->y*=scale;
909         if(z) dim->z*=scale;
910
911         return 0;
912 }
913
914 int scale_atoms(t_moldyn *moldyn,double scale,u8 x,u8 y,u8 z) {
915
916         int i;
917         t_3dvec *r;
918
919         for(i=0;i<moldyn->count;i++) {
920                 r=&(moldyn->atom[i].r);
921                 if(x) r->x*=scale;
922                 if(y) r->y*=scale;
923                 if(z) r->z*=scale;
924         }
925
926         return 0;
927 }
928
929 int scale_volume(t_moldyn *moldyn) {
930
931         t_3dvec *dim,*vdim;
932         double scale;
933         t_linkcell *lc;
934
935         vdim=&(moldyn->vis.dim);
936         dim=&(moldyn->dim);
937         lc=&(moldyn->lc);
938
939         /* scaling factor */
940         if(moldyn->pt_scale&P_SCALE_BERENDSEN) {
941                 scale=1.0-(moldyn->p_ref-moldyn->p)/moldyn->p_tc;
942                 scale=pow(scale,ONE_THIRD);
943         }
944         else {
945                 scale=pow(moldyn->p/moldyn->p_ref,ONE_THIRD);
946         }
947 moldyn->debug=scale;
948
949         /* scale the atoms and dimensions */
950         scale_atoms(moldyn,scale,TRUE,TRUE,TRUE);
951         scale_dim(moldyn,scale,TRUE,TRUE,TRUE);
952
953         /* visualize dimensions */
954         if(vdim->x!=0) {
955                 vdim->x=dim->x;
956                 vdim->y=dim->y;
957                 vdim->z=dim->z;
958         }
959
960         /* recalculate scaled volume */
961         moldyn->volume=dim->x*dim->y*dim->z;
962
963         /* adjust/reinit linkcell */
964         if(((int)(dim->x/moldyn->cutoff)!=lc->nx)||
965            ((int)(dim->y/moldyn->cutoff)!=lc->ny)||
966            ((int)(dim->z/moldyn->cutoff)!=lc->nx)) {
967                 link_cell_shutdown(moldyn);
968                 link_cell_init(moldyn,QUIET);
969         } else {
970                 lc->x*=scale;
971                 lc->y*=scale;
972                 lc->z*=scale;
973         }
974
975         return 0;
976
977 }
978
979 double e_kin_calc(t_moldyn *moldyn) {
980
981         int i;
982         t_atom *atom;
983
984         atom=moldyn->atom;
985         moldyn->ekin=0.0;
986
987         for(i=0;i<moldyn->count;i++)
988                 moldyn->ekin+=0.5*atom[i].mass*v3_absolute_square(&(atom[i].v));
989
990         return moldyn->ekin;
991 }
992
993 double get_total_energy(t_moldyn *moldyn) {
994
995         return(moldyn->ekin+moldyn->energy);
996 }
997
998 t_3dvec get_total_p(t_moldyn *moldyn) {
999
1000         t_3dvec p,p_total;
1001         int i;
1002         t_atom *atom;
1003
1004         atom=moldyn->atom;
1005
1006         v3_zero(&p_total);
1007         for(i=0;i<moldyn->count;i++) {
1008                 v3_scale(&p,&(atom[i].v),atom[i].mass);
1009                 v3_add(&p_total,&p_total,&p);
1010         }
1011
1012         return p_total;
1013 }
1014
1015 double estimate_time_step(t_moldyn *moldyn,double nn_dist) {
1016
1017         double tau;
1018
1019         /* nn_dist is the nearest neighbour distance */
1020
1021         tau=(0.05*nn_dist*moldyn->atom[0].mass)/sqrt(3.0*K_BOLTZMANN*moldyn->t);
1022
1023         return tau;     
1024 }
1025
1026 /*
1027  * numerical tricks
1028  */
1029
1030 /* linked list / cell method */
1031
1032 int link_cell_init(t_moldyn *moldyn,u8 vol) {
1033
1034         t_linkcell *lc;
1035         int i;
1036
1037         lc=&(moldyn->lc);
1038
1039         /* partitioning the md cell */
1040         lc->nx=moldyn->dim.x/moldyn->cutoff;
1041         lc->x=moldyn->dim.x/lc->nx;
1042         lc->ny=moldyn->dim.y/moldyn->cutoff;
1043         lc->y=moldyn->dim.y/lc->ny;
1044         lc->nz=moldyn->dim.z/moldyn->cutoff;
1045         lc->z=moldyn->dim.z/lc->nz;
1046
1047         lc->cells=lc->nx*lc->ny*lc->nz;
1048         lc->subcell=malloc(lc->cells*sizeof(t_list));
1049
1050         if(lc->cells<27)
1051                 printf("[moldyn] FATAL: less then 27 subcells!\n");
1052
1053         if(vol) {
1054                 printf("[moldyn] initializing linked cells (%d)\n",lc->cells);
1055                 printf("  x: %d x %f A\n",lc->nx,lc->x);
1056                 printf("  y: %d x %f A\n",lc->ny,lc->y);
1057                 printf("  z: %d x %f A\n",lc->nz,lc->z);
1058         }
1059
1060         for(i=0;i<lc->cells;i++)
1061                 list_init_f(&(lc->subcell[i]));
1062
1063         link_cell_update(moldyn);
1064         
1065         return 0;
1066 }
1067
1068 int link_cell_update(t_moldyn *moldyn) {
1069
1070         int count,i,j,k;
1071         int nx,ny;
1072         t_atom *atom;
1073         t_linkcell *lc;
1074         double x,y,z;
1075
1076         atom=moldyn->atom;
1077         lc=&(moldyn->lc);
1078
1079         nx=lc->nx;
1080         ny=lc->ny;
1081
1082         x=moldyn->dim.x/2;
1083         y=moldyn->dim.y/2;
1084         z=moldyn->dim.z/2;
1085
1086         for(i=0;i<lc->cells;i++)
1087                 list_destroy_f(&(lc->subcell[i]));
1088         
1089         for(count=0;count<moldyn->count;count++) {
1090                 i=((atom[count].r.x+(moldyn->dim.x/2))/lc->x);
1091                 j=((atom[count].r.y+(moldyn->dim.y/2))/lc->y);
1092                 k=((atom[count].r.z+(moldyn->dim.z/2))/lc->z);
1093                 list_add_immediate_f(&(lc->subcell[i+j*nx+k*nx*ny]),
1094                                      &(atom[count]));
1095         }
1096
1097         return 0;
1098 }
1099
1100 int link_cell_neighbour_index(t_moldyn *moldyn,int i,int j,int k,t_list *cell) {
1101
1102         t_linkcell *lc;
1103         int a;
1104         int count1,count2;
1105         int ci,cj,ck;
1106         int nx,ny,nz;
1107         int x,y,z;
1108         u8 bx,by,bz;
1109
1110         lc=&(moldyn->lc);
1111         nx=lc->nx;
1112         ny=lc->ny;
1113         nz=lc->nz;
1114         count1=1;
1115         count2=27;
1116         a=nx*ny;
1117
1118         cell[0]=lc->subcell[i+j*nx+k*a];
1119         for(ci=-1;ci<=1;ci++) {
1120                 bx=0;
1121                 x=i+ci;
1122                 if((x<0)||(x>=nx)) {
1123                         x=(x+nx)%nx;
1124                         bx=1;
1125                 }
1126                 for(cj=-1;cj<=1;cj++) {
1127                         by=0;
1128                         y=j+cj;
1129                         if((y<0)||(y>=ny)) {
1130                                 y=(y+ny)%ny;
1131                                 by=1;
1132                         }
1133                         for(ck=-1;ck<=1;ck++) {
1134                                 bz=0;
1135                                 z=k+ck;
1136                                 if((z<0)||(z>=nz)) {
1137                                         z=(z+nz)%nz;
1138                                         bz=1;
1139                                 }
1140                                 if(!(ci|cj|ck)) continue;
1141                                 if(bx|by|bz) {
1142                                         cell[--count2]=lc->subcell[x+y*nx+z*a];
1143                                 }
1144                                 else {
1145                                         cell[count1++]=lc->subcell[x+y*nx+z*a];
1146                                 }
1147                         }
1148                 }
1149         }
1150
1151         lc->dnlc=count1;
1152
1153         return count1;
1154 }
1155
1156 int link_cell_shutdown(t_moldyn *moldyn) {
1157
1158         int i;
1159         t_linkcell *lc;
1160
1161         lc=&(moldyn->lc);
1162
1163         for(i=0;i<lc->nx*lc->ny*lc->nz;i++)
1164                 list_destroy_f(&(moldyn->lc.subcell[i]));
1165
1166         free(lc->subcell);
1167
1168         return 0;
1169 }
1170
1171 int moldyn_add_schedule(t_moldyn *moldyn,int runs,double tau) {
1172
1173         int count;
1174         void *ptr;
1175         t_moldyn_schedule *schedule;
1176
1177         schedule=&(moldyn->schedule);
1178         count=++(schedule->total_sched);
1179
1180         ptr=realloc(schedule->runs,count*sizeof(int));
1181         if(!ptr) {
1182                 perror("[moldyn] realloc (runs)");
1183                 return -1;
1184         }
1185         schedule->runs=ptr;
1186         schedule->runs[count-1]=runs;
1187
1188         ptr=realloc(schedule->tau,count*sizeof(double));
1189         if(!ptr) {
1190                 perror("[moldyn] realloc (tau)");
1191                 return -1;
1192         }
1193         schedule->tau=ptr;
1194         schedule->tau[count-1]=tau;
1195
1196         printf("[moldyn] schedule added:\n");
1197         printf("  number: %d | runs: %d | tau: %f\n",count-1,runs,tau);
1198                                        
1199
1200         return 0;
1201 }
1202
1203 int moldyn_set_schedule_hook(t_moldyn *moldyn,set_hook hook,void *hook_params) {
1204
1205         moldyn->schedule.hook=hook;
1206         moldyn->schedule.hook_params=hook_params;
1207         
1208         return 0;
1209 }
1210
1211 /*
1212  *
1213  * 'integration of newtons equation' - algorithms
1214  *
1215  */
1216
1217 /* start the integration */
1218
1219 int moldyn_integrate(t_moldyn *moldyn) {
1220
1221         int i;
1222         unsigned int e,m,s,v,p,t;
1223         t_3dvec momentum;
1224         t_moldyn_schedule *sched;
1225         t_atom *atom;
1226         int fd;
1227         char dir[128];
1228         double ds;
1229         double energy_scale;
1230         //double tp;
1231
1232         sched=&(moldyn->schedule);
1233         atom=moldyn->atom;
1234
1235         /* initialize linked cell method */
1236         link_cell_init(moldyn,VERBOSE);
1237
1238         /* logging & visualization */
1239         e=moldyn->ewrite;
1240         m=moldyn->mwrite;
1241         s=moldyn->swrite;
1242         v=moldyn->vwrite;
1243         p=moldyn->pwrite;
1244         t=moldyn->twrite;
1245
1246         /* sqaure of some variables */
1247         moldyn->tau_square=moldyn->tau*moldyn->tau;
1248         moldyn->cutoff_square=moldyn->cutoff*moldyn->cutoff;
1249
1250         /* energy scaling factor */
1251         energy_scale=moldyn->count*EV;
1252
1253         /* calculate initial forces */
1254         potential_force_calc(moldyn);
1255
1256         /* some stupid checks before we actually start calculating bullshit */
1257         if(moldyn->cutoff>0.5*moldyn->dim.x)
1258                 printf("[moldyn] warning: cutoff > 0.5 x dim.x\n");
1259         if(moldyn->cutoff>0.5*moldyn->dim.y)
1260                 printf("[moldyn] warning: cutoff > 0.5 x dim.y\n");
1261         if(moldyn->cutoff>0.5*moldyn->dim.z)
1262                 printf("[moldyn] warning: cutoff > 0.5 x dim.z\n");
1263         ds=0.5*atom[0].f.x*moldyn->tau_square/atom[0].mass;
1264         if(ds>0.05*moldyn->nnd)
1265                 printf("[moldyn] warning: forces too high / tau too small!\n");
1266
1267         /* zero absolute time */
1268         moldyn->time=0.0;
1269         moldyn->total_steps=0;
1270
1271         /* debugging, ignore */
1272         moldyn->debug=0;
1273
1274         /* tell the world */
1275         printf("[moldyn] integration start, go get a coffee ...\n");
1276
1277         /* executing the schedule */
1278         for(sched->count=0;sched->count<sched->total_sched;sched->count++) {
1279
1280                 /* setting amount of runs and finite time step size */
1281                 moldyn->tau=sched->tau[sched->count];
1282                 moldyn->tau_square=moldyn->tau*moldyn->tau;
1283                 moldyn->time_steps=sched->runs[sched->count];
1284
1285         /* integration according to schedule */
1286
1287         for(i=0;i<moldyn->time_steps;i++) {
1288
1289                 /* integration step */
1290                 moldyn->integrate(moldyn);
1291
1292                 /* calculate kinetic energy, temperature and pressure */
1293                 e_kin_calc(moldyn);
1294                 temperature_calc(moldyn);
1295                 pressure_calc(moldyn);
1296                 //tp=thermodynamic_pressure_calc(moldyn);
1297 //printf("thermodynamic p: %f %f %f - %f\n",moldyn->tp.x/BAR,moldyn->tp.y/BAR,moldyn->tp.z/BAR,tp/BAR);
1298
1299                 /* p/t scaling */
1300                 if(moldyn->pt_scale&(T_SCALE_BERENDSEN|T_SCALE_DIRECT))
1301                         scale_velocity(moldyn,FALSE);
1302                 if(moldyn->pt_scale&(P_SCALE_BERENDSEN|P_SCALE_DIRECT))
1303                         scale_volume(moldyn);
1304
1305                 /* check for log & visualization */
1306                 if(e) {
1307                         if(!(i%e))
1308                                 dprintf(moldyn->efd,
1309                                         "%f %f %f %f\n",
1310                                         moldyn->time,moldyn->ekin/energy_scale,
1311                                         moldyn->energy/energy_scale,
1312                                         get_total_energy(moldyn)/energy_scale);
1313                 }
1314                 if(m) {
1315                         if(!(i%m)) {
1316                                 momentum=get_total_p(moldyn);
1317                                 dprintf(moldyn->mfd,
1318                                         "%f %f %f %f %f\n",moldyn->time,
1319                                         momentum.x,momentum.y,momentum.z,
1320                                         v3_norm(&momentum));
1321                         }
1322                 }
1323                 if(p) {
1324                         if(!(i%p)) {
1325                                 dprintf(moldyn->pfd,
1326                                         "%f %f %f %f %f\n",moldyn->time,
1327                                          moldyn->p/BAR,moldyn->mean_p/BAR,
1328                                          moldyn->gp/BAR,moldyn->mean_gp/BAR);
1329                         }
1330                 }
1331                 if(t) {
1332                         if(!(i%t)) {
1333                                 dprintf(moldyn->tfd,
1334                                         "%f %f %f\n",
1335                                         moldyn->time,moldyn->t,moldyn->mean_t);
1336                         }
1337                 }
1338                 if(s) {
1339                         if(!(i%s)) {
1340                                 snprintf(dir,128,"%s/s-%07.f.save",
1341                                          moldyn->vlsdir,moldyn->time);
1342                                 fd=open(dir,O_WRONLY|O_TRUNC|O_CREAT);
1343                                 if(fd<0) perror("[moldyn] save fd open");
1344                                 else {
1345                                         write(fd,moldyn,sizeof(t_moldyn));
1346                                         write(fd,moldyn->atom,
1347                                               moldyn->count*sizeof(t_atom));
1348                                 }
1349                                 close(fd);
1350                         }       
1351                 }
1352                 if(v) {
1353                         if(!(i%v)) {
1354                                 visual_atoms(&(moldyn->vis),moldyn->time,
1355                                              moldyn->atom,moldyn->count);
1356                         }
1357                 }
1358
1359                 /* display progress */
1360                 if(!(i%10)) {
1361                         printf("\rsched: %d, steps: %d, T: %f, P: %f %f V: %f",
1362                                sched->count,i,
1363                                moldyn->mean_t,
1364                                moldyn->mean_p/BAR,
1365                                moldyn->mean_gp/BAR,
1366                                moldyn->volume);
1367                         fflush(stdout);
1368                 }
1369
1370                 /* increase absolute time */
1371                 moldyn->time+=moldyn->tau;
1372                 moldyn->total_steps+=1;
1373
1374         }
1375
1376                 /* check for hooks */
1377                 if(sched->hook)
1378                         sched->hook(moldyn,sched->hook_params);
1379
1380                 /* get a new info line */
1381                 printf("\n");
1382
1383         }
1384
1385         return 0;
1386 }
1387
1388 /* velocity verlet */
1389
1390 int velocity_verlet(t_moldyn *moldyn) {
1391
1392         int i,count;
1393         double tau,tau_square,h;
1394         t_3dvec delta;
1395         t_atom *atom;
1396
1397         atom=moldyn->atom;
1398         count=moldyn->count;
1399         tau=moldyn->tau;
1400         tau_square=moldyn->tau_square;
1401
1402         for(i=0;i<count;i++) {
1403                 /* new positions */
1404                 h=0.5/atom[i].mass;
1405                 v3_scale(&delta,&(atom[i].v),tau);
1406                 v3_add(&(atom[i].r),&(atom[i].r),&delta);
1407                 v3_scale(&delta,&(atom[i].f),h*tau_square);
1408                 v3_add(&(atom[i].r),&(atom[i].r),&delta);
1409                 check_per_bound(moldyn,&(atom[i].r));
1410
1411                 /* velocities [actually v(t+tau/2)] */
1412                 v3_scale(&delta,&(atom[i].f),h*tau);
1413                 v3_add(&(atom[i].v),&(atom[i].v),&delta);
1414         }
1415
1416         /* neighbour list update */
1417         link_cell_update(moldyn);
1418
1419         /* forces depending on chosen potential */
1420         potential_force_calc(moldyn);
1421
1422         for(i=0;i<count;i++) {
1423                 /* again velocities [actually v(t+tau)] */
1424                 v3_scale(&delta,&(atom[i].f),0.5*tau/atom[i].mass);
1425                 v3_add(&(atom[i].v),&(atom[i].v),&delta);
1426         }
1427
1428         return 0;
1429 }
1430
1431
1432 /*
1433  *
1434  * potentials & corresponding forces & virial routine
1435  * 
1436  */
1437
1438 /* generic potential and force calculation */
1439
1440 int potential_force_calc(t_moldyn *moldyn) {
1441
1442         int i,j,k,count;
1443         t_atom *itom,*jtom,*ktom;
1444         t_virial *virial;
1445         t_linkcell *lc;
1446         t_list neighbour_i[27];
1447         t_list neighbour_i2[27];
1448         t_list *this,*that;
1449         u8 bc_ij,bc_ik;
1450         int dnlc;
1451
1452         count=moldyn->count;
1453         itom=moldyn->atom;
1454         lc=&(moldyn->lc);
1455
1456         /* reset energy */
1457         moldyn->energy=0.0;
1458
1459         /* reset global virial */
1460         memset(&(moldyn->virial),0,sizeof(t_virial));
1461
1462         /* reset force, site energy and virial of every atom */
1463         for(i=0;i<count;i++) {
1464
1465                 /* reset force */
1466                 v3_zero(&(itom[i].f));
1467
1468                 /* reset virial */
1469                 virial=(&(itom[i].virial));
1470                 virial->xx=0.0;
1471                 virial->yy=0.0;
1472                 virial->zz=0.0;
1473                 virial->xy=0.0;
1474                 virial->xz=0.0;
1475                 virial->yz=0.0;
1476         
1477                 /* reset site energy */
1478                 itom[i].e=0.0;
1479
1480         }
1481
1482         /* get energy, force and virial of every atom */
1483
1484         /* first (and only) loop over atoms i */
1485         for(i=0;i<count;i++) {
1486
1487                 /* single particle potential/force */
1488                 if(itom[i].attr&ATOM_ATTR_1BP)
1489                         moldyn->func1b(moldyn,&(itom[i]));
1490
1491                 if(!(itom[i].attr&(ATOM_ATTR_2BP|ATOM_ATTR_3BP)))
1492                         continue;
1493
1494                 /* 2 body pair potential/force */
1495         
1496                 link_cell_neighbour_index(moldyn,
1497                                           (itom[i].r.x+moldyn->dim.x/2)/lc->x,
1498                                           (itom[i].r.y+moldyn->dim.y/2)/lc->y,
1499                                           (itom[i].r.z+moldyn->dim.z/2)/lc->z,
1500                                           neighbour_i);
1501
1502                 dnlc=lc->dnlc;
1503
1504                 /* first loop over atoms j */
1505                 if(moldyn->func2b) {
1506                         for(j=0;j<27;j++) {
1507
1508                                 this=&(neighbour_i[j]);
1509                                 list_reset_f(this);
1510
1511                                 if(this->start==NULL)
1512                                         continue;
1513
1514                                 bc_ij=(j<dnlc)?0:1;
1515
1516                                 do {
1517                                         jtom=this->current->data;
1518
1519                                         if(jtom==&(itom[i]))
1520                                                 continue;
1521
1522                                         if((jtom->attr&ATOM_ATTR_2BP)&
1523                                            (itom[i].attr&ATOM_ATTR_2BP)) {
1524                                                 moldyn->func2b(moldyn,
1525                                                                &(itom[i]),
1526                                                                jtom,
1527                                                                bc_ij);
1528                                         }
1529                                 } while(list_next_f(this)!=L_NO_NEXT_ELEMENT);
1530
1531                         }
1532                 }
1533
1534                 /* 3 body potential/force */
1535
1536                 if(!(itom[i].attr&ATOM_ATTR_3BP))
1537                         continue;
1538
1539                 /* copy the neighbour lists */
1540                 memcpy(neighbour_i2,neighbour_i,27*sizeof(t_list));
1541
1542                 /* second loop over atoms j */
1543                 for(j=0;j<27;j++) {
1544
1545                         this=&(neighbour_i[j]);
1546                         list_reset_f(this);
1547
1548                         if(this->start==NULL)
1549                                 continue;
1550
1551                         bc_ij=(j<dnlc)?0:1;
1552
1553                         do {
1554                                 jtom=this->current->data;
1555
1556                                 if(jtom==&(itom[i]))
1557                                         continue;
1558
1559                                 if(!(jtom->attr&ATOM_ATTR_3BP))
1560                                         continue;
1561
1562                                 /* reset 3bp run */
1563                                 moldyn->run3bp=1;
1564
1565                                 if(moldyn->func3b_j1)
1566                                         moldyn->func3b_j1(moldyn,
1567                                                           &(itom[i]),
1568                                                           jtom,
1569                                                           bc_ij);
1570
1571                                 /* in first j loop, 3bp run can be skipped */
1572                                 if(!(moldyn->run3bp))
1573                                         continue;
1574                         
1575                                 /* first loop over atoms k */
1576                                 if(moldyn->func3b_k1) {
1577
1578                                 for(k=0;k<27;k++) {
1579
1580                                         that=&(neighbour_i2[k]);
1581                                         list_reset_f(that);
1582                                         
1583                                         if(that->start==NULL)
1584                                                 continue;
1585
1586                                         bc_ik=(k<dnlc)?0:1;
1587
1588                                         do {
1589
1590                                                 ktom=that->current->data;
1591
1592                                                 if(!(ktom->attr&ATOM_ATTR_3BP))
1593                                                         continue;
1594
1595                                                 if(ktom==jtom)
1596                                                         continue;
1597
1598                                                 if(ktom==&(itom[i]))
1599                                                         continue;
1600
1601                                                 moldyn->func3b_k1(moldyn,
1602                                                                   &(itom[i]),
1603                                                                   jtom,
1604                                                                   ktom,
1605                                                                   bc_ik|bc_ij);
1606
1607                                         } while(list_next_f(that)!=\
1608                                                 L_NO_NEXT_ELEMENT);
1609
1610                                 }
1611
1612                                 }
1613
1614                                 if(moldyn->func3b_j2)
1615                                         moldyn->func3b_j2(moldyn,
1616                                                           &(itom[i]),
1617                                                           jtom,
1618                                                           bc_ij);
1619
1620                                 /* second loop over atoms k */
1621                                 if(moldyn->func3b_k2) {
1622
1623                                 for(k=0;k<27;k++) {
1624
1625                                         that=&(neighbour_i2[k]);
1626                                         list_reset_f(that);
1627                                         
1628                                         if(that->start==NULL)
1629                                                 continue;
1630
1631                                         bc_ik=(k<dnlc)?0:1;
1632
1633                                         do {
1634
1635                                                 ktom=that->current->data;
1636
1637                                                 if(!(ktom->attr&ATOM_ATTR_3BP))
1638                                                         continue;
1639
1640                                                 if(ktom==jtom)
1641                                                         continue;
1642
1643                                                 if(ktom==&(itom[i]))
1644                                                         continue;
1645
1646                                                 moldyn->func3b_k2(moldyn,
1647                                                                   &(itom[i]),
1648                                                                   jtom,
1649                                                                   ktom,
1650                                                                   bc_ik|bc_ij);
1651
1652                                         } while(list_next_f(that)!=\
1653                                                 L_NO_NEXT_ELEMENT);
1654
1655                                 }
1656                                 
1657                                 }
1658
1659                                 /* 2bp post function */
1660                                 if(moldyn->func3b_j3) {
1661                                         moldyn->func3b_j3(moldyn,
1662                                                           &(itom[i]),
1663                                                           jtom,bc_ij);
1664                                 }
1665                                         
1666                         } while(list_next_f(this)!=L_NO_NEXT_ELEMENT);
1667                 
1668                 }
1669                 
1670 #ifdef DEBUG
1671         //printf("\n\n");
1672 #endif
1673 #ifdef VDEBUG
1674         printf("\n\n");
1675 #endif
1676
1677         }
1678
1679 #ifdef DEBUG
1680         printf("\nATOM 0: %f %f %f\n\n",itom->f.x,itom->f.y,itom->f.z);
1681 #endif
1682
1683         /* calculate global virial */
1684         for(i=0;i<count;i++) {
1685                 moldyn->virial.xx+=moldyn->atom[i].r.x*moldyn->atom[i].f.x;
1686                 moldyn->virial.yy+=moldyn->atom[i].r.y*moldyn->atom[i].f.y;
1687                 moldyn->virial.zz+=moldyn->atom[i].r.z*moldyn->atom[i].f.z;
1688                 moldyn->virial.xy+=moldyn->atom[i].r.y*moldyn->atom[i].f.x;
1689                 moldyn->virial.xz+=moldyn->atom[i].r.z*moldyn->atom[i].f.x;
1690                 moldyn->virial.yz+=moldyn->atom[i].r.z*moldyn->atom[i].f.y;
1691         }
1692
1693         return 0;
1694 }
1695
1696 /*
1697  * virial calculation
1698  */
1699
1700 //inline int virial_calc(t_atom *a,t_3dvec *f,t_3dvec *d) {
1701 int virial_calc(t_atom *a,t_3dvec *f,t_3dvec *d) {
1702
1703         a->virial.xx+=f->x*d->x;
1704         a->virial.yy+=f->y*d->y;
1705         a->virial.zz+=f->z*d->z;
1706         a->virial.xy+=f->x*d->y;
1707         a->virial.xz+=f->x*d->z;
1708         a->virial.yz+=f->y*d->z;
1709
1710         return 0;
1711 }
1712
1713 /*
1714  * periodic boundary checking
1715  */
1716
1717 //inline int check_per_bound(t_moldyn *moldyn,t_3dvec *a) {
1718 int check_per_bound(t_moldyn *moldyn,t_3dvec *a) {
1719         
1720         double x,y,z;
1721         t_3dvec *dim;
1722
1723         dim=&(moldyn->dim);
1724
1725         x=dim->x/2;
1726         y=dim->y/2;
1727         z=dim->z/2;
1728
1729         if(moldyn->status&MOLDYN_STAT_PBX) {
1730                 if(a->x>=x) a->x-=dim->x;
1731                 else if(-a->x>x) a->x+=dim->x;
1732         }
1733         if(moldyn->status&MOLDYN_STAT_PBY) {
1734                 if(a->y>=y) a->y-=dim->y;
1735                 else if(-a->y>y) a->y+=dim->y;
1736         }
1737         if(moldyn->status&MOLDYN_STAT_PBZ) {
1738                 if(a->z>=z) a->z-=dim->z;
1739                 else if(-a->z>z) a->z+=dim->z;
1740         }
1741
1742         return 0;
1743 }
1744         
1745 /*
1746  * debugging / critical check functions
1747  */
1748
1749 int moldyn_bc_check(t_moldyn *moldyn) {
1750
1751         t_atom *atom;
1752         t_3dvec *dim;
1753         int i;
1754         double x;
1755         u8 byte;
1756         int j,k;
1757
1758         atom=moldyn->atom;
1759         dim=&(moldyn->dim);
1760         x=dim->x/2;
1761
1762         for(i=0;i<moldyn->count;i++) {
1763                 if(atom[i].r.x>=dim->x/2||-atom[i].r.x>dim->x/2) {
1764                         printf("FATAL: atom %d: x: %.20f (%.20f)\n",
1765                                i,atom[i].r.x,dim->x/2);
1766                         printf("diagnostic:\n");
1767                         printf("-----------\natom.r.x:\n");
1768                         for(j=0;j<8;j++) {
1769                                 memcpy(&byte,(u8 *)(&(atom[i].r.x))+j,1);
1770                                 for(k=0;k<8;k++)
1771                                         printf("%d%c",
1772                                         ((byte)&(1<<k))?1:0,
1773                                         (k==7)?'\n':'|');
1774                         }
1775                         printf("---------------\nx=dim.x/2:\n");
1776                         for(j=0;j<8;j++) {
1777                                 memcpy(&byte,(u8 *)(&x)+j,1);
1778                                 for(k=0;k<8;k++)
1779                                         printf("%d%c",
1780                                         ((byte)&(1<<k))?1:0,
1781                                         (k==7)?'\n':'|');
1782                         }
1783                         if(atom[i].r.x==x) printf("the same!\n");
1784                         else printf("different!\n");
1785                 }
1786                 if(atom[i].r.y>=dim->y/2||-atom[i].r.y>dim->y/2)
1787                         printf("FATAL: atom %d: y: %.20f (%.20f)\n",
1788                                i,atom[i].r.y,dim->y/2);
1789                 if(atom[i].r.z>=dim->z/2||-atom[i].r.z>dim->z/2)
1790                         printf("FATAL: atom %d: z: %.20f (%.20f)\n",
1791                                i,atom[i].r.z,dim->z/2);
1792         }
1793
1794         return 0;
1795 }