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