implemented basic p ctrl stuff + video with 13 fps
[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 <sys/time.h>
17 #include <time.h>
18 #include <math.h>
19
20 #include "moldyn.h"
21 #include "report/report.h"
22
23 /*
24  * global variables, pse and atom colors (only needed here)
25  */ 
26
27 static char *pse_name[]={
28         "*",
29         "H",
30         "He",
31         "Li",
32         "Be",
33         "B",
34         "C",
35         "N",
36         "O",
37         "F",
38         "Ne",
39         "Na",
40         "Mg",
41         "Al",
42         "Si",
43         "P",
44         "S",
45         "Cl",
46         "Ar",
47 };
48
49 static char *pse_col[]={
50         "*",
51         "White",
52         "He",
53         "Li",
54         "Be",
55         "B",
56         "Gray",
57         "N",
58         "Blue",
59         "F",
60         "Ne",
61         "Na",
62         "Mg",
63         "Al",
64         "Yellow",
65         "P",
66         "S",
67         "Cl",
68         "Ar",
69 };
70
71 /*
72  * the moldyn functions
73  */
74
75 int moldyn_init(t_moldyn *moldyn,int argc,char **argv) {
76
77         printf("[moldyn] init\n");
78
79         memset(moldyn,0,sizeof(t_moldyn));
80
81         moldyn->argc=argc;
82         moldyn->args=argv;
83
84         rand_init(&(moldyn->random),NULL,1);
85         moldyn->random.status|=RAND_STAT_VERBOSE;
86
87         return 0;
88 }
89
90 int moldyn_shutdown(t_moldyn *moldyn) {
91
92         printf("[moldyn] shutdown\n");
93
94         moldyn_log_shutdown(moldyn);
95         link_cell_shutdown(moldyn);
96         rand_close(&(moldyn->random));
97         free(moldyn->atom);
98
99         return 0;
100 }
101
102 int set_int_alg(t_moldyn *moldyn,u8 algo) {
103
104         printf("[moldyn] integration algorithm: ");
105
106         switch(algo) {
107                 case MOLDYN_INTEGRATE_VERLET:
108                         moldyn->integrate=velocity_verlet;
109                         printf("velocity verlet\n");
110                         break;
111                 default:
112                         printf("unknown integration algorithm: %02x\n",algo);
113                         printf("unknown\n");
114                         return -1;
115         }
116
117         return 0;
118 }
119
120 int set_cutoff(t_moldyn *moldyn,double cutoff) {
121
122         moldyn->cutoff=cutoff;
123
124         printf("[moldyn] cutoff [A]: %f\n",moldyn->cutoff);
125
126         return 0;
127 }
128
129 int set_bondlen(t_moldyn *moldyn,double b0,double b1,double bm) {
130
131         moldyn->bondlen[0]=b0*b0;
132         moldyn->bondlen[1]=b1*b1;
133         if(bm<0)
134                 moldyn->bondlen[2]=b0*b1;
135         else
136                 moldyn->bondlen[2]=bm*bm;
137
138         return 0;
139 }
140
141 int set_temperature(t_moldyn *moldyn,double t_ref) {
142
143         moldyn->t_ref=t_ref;
144
145         printf("[moldyn] temperature [K]: %f\n",moldyn->t_ref);
146
147         return 0;
148 }
149
150 int set_pressure(t_moldyn *moldyn,double p_ref) {
151
152         moldyn->p_ref=p_ref;
153
154         printf("[moldyn] pressure [bar]: %f\n",moldyn->p_ref/BAR);
155
156         return 0;
157 }
158
159 int set_pt_scale(t_moldyn *moldyn,u8 ptype,double ptc,u8 ttype,double ttc) {
160
161         moldyn->pt_scale=(ptype|ttype);
162         moldyn->t_tc=ttc;
163         moldyn->p_tc=ptc;
164
165         printf("[moldyn] p/t scaling:\n");
166
167         printf("  p: %s",ptype?"yes":"no ");
168         if(ptype)
169                 printf(" | type: %02x | factor: %f",ptype,ptc);
170         printf("\n");
171
172         printf("  t: %s",ttype?"yes":"no ");
173         if(ttype)
174                 printf(" | type: %02x | factor: %f",ttype,ttc);
175         printf("\n");
176
177         return 0;
178 }
179
180 int set_dim(t_moldyn *moldyn,double x,double y,double z,u8 visualize) {
181
182         moldyn->dim.x=x;
183         moldyn->dim.y=y;
184         moldyn->dim.z=z;
185
186         moldyn->volume=x*y*z;
187
188         if(visualize) {
189                 moldyn->vis.dim.x=x;
190                 moldyn->vis.dim.y=y;
191                 moldyn->vis.dim.z=z;
192         }
193
194         printf("[moldyn] dimensions in A and A^3 respectively:\n");
195         printf("  x: %f\n",moldyn->dim.x);
196         printf("  y: %f\n",moldyn->dim.y);
197         printf("  z: %f\n",moldyn->dim.z);
198         printf("  volume: %f\n",moldyn->volume);
199         printf("  visualize simulation box: %s\n",visualize?"yes":"no");
200
201         return 0;
202 }
203
204 int set_nn_dist(t_moldyn *moldyn,double dist) {
205
206         moldyn->nnd=dist;
207
208         return 0;
209 }
210
211 int set_pbc(t_moldyn *moldyn,u8 x,u8 y,u8 z) {
212
213         printf("[moldyn] periodic boundary conditions:\n");
214
215         if(x)
216                 moldyn->status|=MOLDYN_STAT_PBX;
217
218         if(y)
219                 moldyn->status|=MOLDYN_STAT_PBY;
220
221         if(z)
222                 moldyn->status|=MOLDYN_STAT_PBZ;
223
224         printf("  x: %s\n",x?"yes":"no");
225         printf("  y: %s\n",y?"yes":"no");
226         printf("  z: %s\n",z?"yes":"no");
227
228         return 0;
229 }
230
231 int set_potential1b(t_moldyn *moldyn,pf_func1b func) {
232
233         moldyn->func1b=func;
234
235         return 0;
236 }
237
238 int set_potential2b(t_moldyn *moldyn,pf_func2b func) {
239
240         moldyn->func2b=func;
241
242         return 0;
243 }
244
245 int set_potential3b_j1(t_moldyn *moldyn,pf_func2b func) {
246
247         moldyn->func3b_j1=func;
248
249         return 0;
250 }
251
252 int set_potential3b_j2(t_moldyn *moldyn,pf_func2b func) {
253
254         moldyn->func3b_j2=func;
255
256         return 0;
257 }
258
259 int set_potential3b_j3(t_moldyn *moldyn,pf_func2b func) {
260
261         moldyn->func3b_j3=func;
262
263         return 0;
264 }
265
266 int set_potential3b_k1(t_moldyn *moldyn,pf_func3b func) {
267
268         moldyn->func3b_k1=func;
269
270         return 0;
271 }
272
273 int set_potential3b_k2(t_moldyn *moldyn,pf_func3b func) {
274
275         moldyn->func3b_k2=func;
276
277         return 0;
278 }
279
280 int set_potential_params(t_moldyn *moldyn,void *params) {
281
282         moldyn->pot_params=params;
283
284         return 0;
285 }
286
287 int set_avg_skip(t_moldyn *moldyn,int skip) {
288
289         printf("[moldyn] skip %d steps before starting average calc\n",skip);
290         moldyn->avg_skip=skip;
291
292         return 0;
293 }
294
295 int moldyn_set_log_dir(t_moldyn *moldyn,char *dir) {
296
297         strncpy(moldyn->vlsdir,dir,127);
298
299         return 0;
300 }
301
302 int moldyn_set_report(t_moldyn *moldyn,char *author,char *title) {
303
304         strncpy(moldyn->rauthor,author,63);
305         strncpy(moldyn->rtitle,title,63);
306
307         return 0;
308 }
309         
310 int moldyn_set_log(t_moldyn *moldyn,u8 type,int timer) {
311
312         char filename[128];
313         int ret;
314
315         printf("[moldyn] set log: ");
316
317         switch(type) {
318                 case LOG_TOTAL_ENERGY:
319                         moldyn->ewrite=timer;
320                         snprintf(filename,127,"%s/energy",moldyn->vlsdir);
321                         moldyn->efd=open(filename,
322                                          O_WRONLY|O_CREAT|O_EXCL,
323                                          S_IRUSR|S_IWUSR);
324                         if(moldyn->efd<0) {
325                                 perror("[moldyn] energy log fd open");
326                                 return moldyn->efd;
327                         }
328                         dprintf(moldyn->efd,"# total energy log file\n");
329                         printf("total energy (%d)\n",timer);
330                         break;
331                 case LOG_TOTAL_MOMENTUM:
332                         moldyn->mwrite=timer;
333                         snprintf(filename,127,"%s/momentum",moldyn->vlsdir);
334                         moldyn->mfd=open(filename,
335                                          O_WRONLY|O_CREAT|O_EXCL,
336                                          S_IRUSR|S_IWUSR);
337                         if(moldyn->mfd<0) {
338                                 perror("[moldyn] momentum log fd open");
339                                 return moldyn->mfd;
340                         }
341                         dprintf(moldyn->efd,"# total momentum log file\n");
342                         printf("total momentum (%d)\n",timer);
343                         break;
344                 case LOG_PRESSURE:
345                         moldyn->pwrite=timer;
346                         snprintf(filename,127,"%s/pressure",moldyn->vlsdir);
347                         moldyn->pfd=open(filename,
348                                          O_WRONLY|O_CREAT|O_EXCL,
349                                          S_IRUSR|S_IWUSR);
350                         if(moldyn->pfd<0) {
351                                 perror("[moldyn] pressure log file\n");
352                                 return moldyn->pfd;
353                         }
354                         dprintf(moldyn->pfd,"# pressure log file\n");
355                         printf("pressure (%d)\n",timer);
356                         break;
357                 case LOG_TEMPERATURE:
358                         moldyn->twrite=timer;
359                         snprintf(filename,127,"%s/temperature",moldyn->vlsdir);
360                         moldyn->tfd=open(filename,
361                                          O_WRONLY|O_CREAT|O_EXCL,
362                                          S_IRUSR|S_IWUSR);
363                         if(moldyn->tfd<0) {
364                                 perror("[moldyn] temperature log file\n");
365                                 return moldyn->tfd;
366                         }
367                         dprintf(moldyn->tfd,"# temperature log file\n");
368                         printf("temperature (%d)\n",timer);
369                         break;
370                 case LOG_VOLUME:
371                         moldyn->vwrite=timer;
372                         snprintf(filename,127,"%s/volume",moldyn->vlsdir);
373                         moldyn->vfd=open(filename,
374                                          O_WRONLY|O_CREAT|O_EXCL,
375                                          S_IRUSR|S_IWUSR);
376                         if(moldyn->vfd<0) {
377                                 perror("[moldyn] volume log file\n");
378                                 return moldyn->vfd;
379                         }
380                         dprintf(moldyn->vfd,"# volume log file\n");
381                         printf("volume (%d)\n",timer);
382                         break;
383                 case SAVE_STEP:
384                         moldyn->swrite=timer;
385                         printf("save file (%d)\n",timer);
386                         break;
387                 case VISUAL_STEP:
388                         moldyn->awrite=timer;
389                         ret=visual_init(moldyn,moldyn->vlsdir);
390                         if(ret<0) {
391                                 printf("[moldyn] visual init failure\n");
392                                 return ret;
393                         }
394                         printf("visual file (%d)\n",timer);
395                         break;
396                 case CREATE_REPORT:
397                         snprintf(filename,127,"%s/report.tex",moldyn->vlsdir);
398                         moldyn->rfd=open(filename,
399                                          O_WRONLY|O_CREAT|O_EXCL,
400                                          S_IRUSR|S_IWUSR);
401                         if(moldyn->rfd<0) {
402                                 perror("[moldyn] report fd open");      
403                                 return moldyn->rfd;
404                         }
405                         printf("report -> ");
406                         if(moldyn->efd) {
407                                 snprintf(filename,127,"%s/e_plot.scr",
408                                          moldyn->vlsdir);
409                                 moldyn->epfd=open(filename,
410                                                  O_WRONLY|O_CREAT|O_EXCL,
411                                                  S_IRUSR|S_IWUSR);
412                                 if(moldyn->epfd<0) {
413                                         perror("[moldyn] energy plot fd open");
414                                         return moldyn->epfd;
415                                 }
416                                 dprintf(moldyn->epfd,e_plot_script);
417                                 close(moldyn->epfd);
418                                 printf("energy ");
419                         }
420                         if(moldyn->pfd) {
421                                 snprintf(filename,127,"%s/pressure_plot.scr",
422                                          moldyn->vlsdir);
423                                 moldyn->ppfd=open(filename,
424                                                   O_WRONLY|O_CREAT|O_EXCL,
425                                                   S_IRUSR|S_IWUSR);
426                                 if(moldyn->ppfd<0) {
427                                         perror("[moldyn] p plot fd open");
428                                         return moldyn->ppfd;
429                                 }
430                                 dprintf(moldyn->ppfd,pressure_plot_script);
431                                 close(moldyn->ppfd);
432                                 printf("pressure ");
433                         }
434                         if(moldyn->tfd) {
435                                 snprintf(filename,127,"%s/temperature_plot.scr",
436                                          moldyn->vlsdir);
437                                 moldyn->tpfd=open(filename,
438                                                   O_WRONLY|O_CREAT|O_EXCL,
439                                                   S_IRUSR|S_IWUSR);
440                                 if(moldyn->tpfd<0) {
441                                         perror("[moldyn] t plot fd open");
442                                         return moldyn->tpfd;
443                                 }
444                                 dprintf(moldyn->tpfd,temperature_plot_script);
445                                 close(moldyn->tpfd);
446                                 printf("temperature ");
447                         }
448                         dprintf(moldyn->rfd,report_start,
449                                 moldyn->rauthor,moldyn->rtitle);
450                         printf("\n");
451                         break;
452                 default:
453                         printf("unknown log type: %02x\n",type);
454                         return -1;
455         }
456
457         return 0;
458 }
459
460 int moldyn_log_shutdown(t_moldyn *moldyn) {
461
462         char sc[256];
463
464         printf("[moldyn] log shutdown\n");
465         if(moldyn->efd) {
466                 close(moldyn->efd);
467                 if(moldyn->rfd) {
468                         dprintf(moldyn->rfd,report_energy);
469                         snprintf(sc,255,"cd %s && gnuplot e_plot.scr",
470                                  moldyn->vlsdir);
471                         system(sc);
472                 }
473         }
474         if(moldyn->mfd) close(moldyn->mfd);
475         if(moldyn->pfd) {
476                 close(moldyn->pfd);
477                 if(moldyn->rfd)
478                         dprintf(moldyn->rfd,report_pressure);
479                         snprintf(sc,255,"cd %s && gnuplot pressure_plot.scr",
480                                  moldyn->vlsdir);
481                         system(sc);
482         }
483         if(moldyn->tfd) {
484                 close(moldyn->tfd);
485                 if(moldyn->rfd)
486                         dprintf(moldyn->rfd,report_temperature);
487                         snprintf(sc,255,"cd %s && gnuplot temperature_plot.scr",
488                                  moldyn->vlsdir);
489                         system(sc);
490         }
491         if(moldyn->rfd) {
492                 dprintf(moldyn->rfd,report_end);
493                 close(moldyn->rfd);
494                 snprintf(sc,255,"cd %s && pdflatex report >/dev/null 2>&1",
495                          moldyn->vlsdir);
496                 system(sc);
497                 snprintf(sc,255,"cd %s && pdflatex report >/dev/null 2>&1",
498                          moldyn->vlsdir);
499                 system(sc);
500                 snprintf(sc,255,"cd %s && dvipdf report >/dev/null 2>&1",
501                          moldyn->vlsdir);
502                 system(sc);
503         }
504
505         return 0;
506 }
507
508 /*
509  * creating lattice functions
510  */
511
512 int create_lattice(t_moldyn *moldyn,u8 type,double lc,int element,double mass,
513                    u8 attr,u8 brand,int a,int b,int c,t_3dvec *origin) {
514
515         int new,count;
516         int ret;
517         t_3dvec orig;
518         void *ptr;
519         t_atom *atom;
520
521         new=a*b*c;
522         count=moldyn->count;
523
524         /* how many atoms do we expect */
525         if(type==CUBIC) new*=1;
526         if(type==FCC) new*=4;
527         if(type==DIAMOND) new*=8;
528
529         /* allocate space for atoms */
530         ptr=realloc(moldyn->atom,(count+new)*sizeof(t_atom));
531         if(!ptr) {
532                 perror("[moldyn] realloc (create lattice)");
533                 return -1;
534         }
535         moldyn->atom=ptr;
536         atom=&(moldyn->atom[count]);
537
538         /* no atoms on the boundaries (only reason: it looks better!) */
539         if(!origin) {
540                 orig.x=0.5*lc;
541                 orig.y=0.5*lc;
542                 orig.z=0.5*lc;
543         }
544         else {
545                 orig.x=origin->x;
546                 orig.y=origin->y;
547                 orig.z=origin->z;
548         }
549
550         switch(type) {
551                 case CUBIC:
552                         set_nn_dist(moldyn,lc);
553                         ret=cubic_init(a,b,c,lc,atom,&orig);
554                         break;
555                 case FCC:
556                         if(!origin)
557                                 v3_scale(&orig,&orig,0.5);
558                         set_nn_dist(moldyn,0.5*sqrt(2.0)*lc);
559                         ret=fcc_init(a,b,c,lc,atom,&orig);
560                         break;
561                 case DIAMOND:
562                         if(!origin)
563                                 v3_scale(&orig,&orig,0.25);
564                         set_nn_dist(moldyn,0.25*sqrt(3.0)*lc);
565                         ret=diamond_init(a,b,c,lc,atom,&orig);
566                         break;
567                 default:
568                         printf("unknown lattice type (%02x)\n",type);
569                         return -1;
570         }
571
572         /* debug */
573         if(ret!=new) {
574                 printf("[moldyn] creating lattice failed\n");
575                 printf("  amount of atoms\n");
576                 printf("  - expected: %d\n",new);
577                 printf("  - created: %d\n",ret);
578                 return -1;
579         }
580
581         moldyn->count+=new;
582         printf("[moldyn] created lattice with %d atoms\n",new);
583
584         for(ret=0;ret<new;ret++) {
585                 atom[ret].element=element;
586                 atom[ret].mass=mass;
587                 atom[ret].attr=attr;
588                 atom[ret].brand=brand;
589                 atom[ret].tag=count+ret;
590                 check_per_bound(moldyn,&(atom[ret].r));
591                 atom[ret].r_0=atom[ret].r;
592         }
593
594         /* update total system mass */
595         total_mass_calc(moldyn);
596
597         return ret;
598 }
599
600 int add_atom(t_moldyn *moldyn,int element,double mass,u8 brand,u8 attr,
601              t_3dvec *r,t_3dvec *v) {
602
603         t_atom *atom;
604         void *ptr;
605         int count;
606         
607         atom=moldyn->atom;
608         count=(moldyn->count)++;        // asshole style!
609
610         ptr=realloc(atom,(count+1)*sizeof(t_atom));
611         if(!ptr) {
612                 perror("[moldyn] realloc (add atom)");
613                 return -1;
614         }
615         moldyn->atom=ptr;
616
617         atom=moldyn->atom;
618
619         /* initialize new atom */
620         memset(&(atom[count]),0,sizeof(t_atom));
621         atom[count].r=*r;
622         atom[count].v=*v;
623         atom[count].element=element;
624         atom[count].mass=mass;
625         atom[count].brand=brand;
626         atom[count].tag=count;
627         atom[count].attr=attr;
628         check_per_bound(moldyn,&(atom[count].r));
629         atom[count].r_0=atom[count].r;
630
631         /* update total system mass */
632         total_mass_calc(moldyn);
633
634         return 0;
635 }
636
637 int del_atom(t_moldyn *moldyn,int tag) {
638
639         t_atom *new,*old;
640         int cnt;
641
642         old=moldyn->atom;
643
644         new=(t_atom *)malloc((moldyn->count-1)*sizeof(t_atom));
645         if(!new) {
646                 perror("[moldyn]malloc (del atom)");
647                 return -1;
648         }
649
650         for(cnt=0;cnt<tag;cnt++)
651                 new[cnt]=old[cnt];
652         
653         for(cnt=tag+1;cnt<moldyn->count;cnt++) {
654                 new[cnt-1]=old[cnt];
655                 new[cnt-1].tag=cnt-1;
656         }
657
658         moldyn->count-=1;
659         moldyn->atom=new;
660
661         free(old);
662
663         return 0;
664 }
665
666 /* cubic init */
667 int cubic_init(int a,int b,int c,double lc,t_atom *atom,t_3dvec *origin) {
668
669         int count;
670         t_3dvec r;
671         int i,j,k;
672         t_3dvec o;
673
674         count=0;
675         if(origin)
676                 v3_copy(&o,origin);
677         else
678                 v3_zero(&o);
679
680         r.x=o.x;
681         for(i=0;i<a;i++) {
682                 r.y=o.y;
683                 for(j=0;j<b;j++) {
684                         r.z=o.z;
685                         for(k=0;k<c;k++) {
686                                 v3_copy(&(atom[count].r),&r);
687                                 count+=1;
688                                 r.z+=lc;
689                         }
690                         r.y+=lc;
691                 }
692                 r.x+=lc;
693         }
694
695         for(i=0;i<count;i++) {
696                 atom[i].r.x-=(a*lc)/2.0;
697                 atom[i].r.y-=(b*lc)/2.0;
698                 atom[i].r.z-=(c*lc)/2.0;
699         }
700
701         return count;
702 }
703
704 /* fcc lattice init */
705 int fcc_init(int a,int b,int c,double lc,t_atom *atom,t_3dvec *origin) {
706
707         int count;
708         int i,j,k,l;
709         t_3dvec o,r,n;
710         t_3dvec basis[3];
711
712         count=0;
713         if(origin)
714                 v3_copy(&o,origin);
715         else
716                 v3_zero(&o);
717
718         /* construct the basis */
719         memset(basis,0,3*sizeof(t_3dvec));
720         basis[0].x=0.5*lc;
721         basis[0].y=0.5*lc;
722         basis[1].x=0.5*lc;
723         basis[1].z=0.5*lc;
724         basis[2].y=0.5*lc;
725         basis[2].z=0.5*lc;
726
727         /* fill up the room */
728         r.x=o.x;
729         for(i=0;i<a;i++) {
730                 r.y=o.y;
731                 for(j=0;j<b;j++) {
732                         r.z=o.z;
733                         for(k=0;k<c;k++) {
734                                 /* first atom */
735                                 v3_copy(&(atom[count].r),&r);
736                                 count+=1;
737                                 r.z+=lc;
738                                 /* the three face centered atoms */
739                                 for(l=0;l<3;l++) {
740                                         v3_add(&n,&r,&basis[l]);
741                                         v3_copy(&(atom[count].r),&n);
742                                         count+=1;
743                                 }
744                         }
745                         r.y+=lc;
746                 }
747                 r.x+=lc;
748         }
749                                 
750         /* coordinate transformation */
751         for(i=0;i<count;i++) {
752                 atom[i].r.x-=(a*lc)/2.0;
753                 atom[i].r.y-=(b*lc)/2.0;
754                 atom[i].r.z-=(c*lc)/2.0;
755         }
756
757         return count;
758 }
759
760 int diamond_init(int a,int b,int c,double lc,t_atom *atom,t_3dvec *origin) {
761
762         int count;
763         t_3dvec o;
764
765         count=fcc_init(a,b,c,lc,atom,origin);
766
767         o.x=0.25*lc;
768         o.y=0.25*lc;
769         o.z=0.25*lc;
770
771         if(origin) v3_add(&o,&o,origin);
772
773         count+=fcc_init(a,b,c,lc,&atom[count],&o);
774
775         return count;
776 }
777
778 int destroy_atoms(t_moldyn *moldyn) {
779
780         if(moldyn->atom) free(moldyn->atom);
781
782         return 0;
783 }
784
785 int thermal_init(t_moldyn *moldyn,u8 equi_init) {
786
787         /*
788          * - gaussian distribution of velocities
789          * - zero total momentum
790          * - velocity scaling (E = 3/2 N k T), E: kinetic energy
791          */
792
793         int i;
794         double v,sigma;
795         t_3dvec p_total,delta;
796         t_atom *atom;
797         t_random *random;
798
799         atom=moldyn->atom;
800         random=&(moldyn->random);
801
802         printf("[moldyn] thermal init (equi init: %s)\n",equi_init?"yes":"no");
803
804         /* gaussian distribution of velocities */
805         v3_zero(&p_total);
806         for(i=0;i<moldyn->count;i++) {
807                 sigma=sqrt(2.0*K_BOLTZMANN*moldyn->t_ref/atom[i].mass);
808                 /* x direction */
809                 v=sigma*rand_get_gauss(random);
810                 atom[i].v.x=v;
811                 p_total.x+=atom[i].mass*v;
812                 /* y direction */
813                 v=sigma*rand_get_gauss(random);
814                 atom[i].v.y=v;
815                 p_total.y+=atom[i].mass*v;
816                 /* z direction */
817                 v=sigma*rand_get_gauss(random);
818                 atom[i].v.z=v;
819                 p_total.z+=atom[i].mass*v;
820         }
821
822         /* zero total momentum */
823         v3_scale(&p_total,&p_total,1.0/moldyn->count);
824         for(i=0;i<moldyn->count;i++) {
825                 v3_scale(&delta,&p_total,1.0/atom[i].mass);
826                 v3_sub(&(atom[i].v),&(atom[i].v),&delta);
827         }
828
829         /* velocity scaling */
830         scale_velocity(moldyn,equi_init);
831
832         return 0;
833 }
834
835 double total_mass_calc(t_moldyn *moldyn) {
836
837         int i;
838
839         moldyn->mass=0.0;
840
841         for(i=0;i<moldyn->count;i++)
842                 moldyn->mass+=moldyn->atom[i].mass;
843
844         return moldyn->mass;
845 }
846
847 double temperature_calc(t_moldyn *moldyn) {
848
849         /* assume up to date kinetic energy, which is 3/2 N k_B T */
850
851         moldyn->t=(2.0*moldyn->ekin)/(3.0*K_BOLTZMANN*moldyn->count);
852
853         return moldyn->t;
854 }
855
856 double get_temperature(t_moldyn *moldyn) {
857
858         return moldyn->t;
859 }
860
861 int scale_velocity(t_moldyn *moldyn,u8 equi_init) {
862
863         int i;
864         double e,scale;
865         t_atom *atom;
866         int count;
867
868         atom=moldyn->atom;
869
870         /*
871          * - velocity scaling (E = 3/2 N k T), E: kinetic energy
872          */
873
874         /* get kinetic energy / temperature & count involved atoms */
875         e=0.0;
876         count=0;
877         for(i=0;i<moldyn->count;i++) {
878                 if((equi_init&TRUE)||(atom[i].attr&ATOM_ATTR_HB)) {
879                         e+=atom[i].mass*v3_absolute_square(&(atom[i].v));
880                         count+=1;
881                 }
882         }
883         e*=0.5;
884         if(count!=0) moldyn->t=e/(1.5*count*K_BOLTZMANN);
885         else return 0;  /* no atoms involved in scaling! */
886         
887         /* (temporary) hack for e,t = 0 */
888         if(e==0.0) {
889         moldyn->t=0.0;
890                 if(moldyn->t_ref!=0.0) {
891                         thermal_init(moldyn,equi_init);
892                         return 0;
893                 }
894                 else
895                         return 0; /* no scaling needed */
896         }
897
898
899         /* get scaling factor */
900         scale=moldyn->t_ref/moldyn->t;
901         if(equi_init&TRUE)
902                 scale*=2.0;
903         else
904                 if(moldyn->pt_scale&T_SCALE_BERENDSEN)
905                         scale=1.0+(scale-1.0)/moldyn->t_tc;
906         scale=sqrt(scale);
907
908         /* velocity scaling */
909         for(i=0;i<moldyn->count;i++) {
910                 if((equi_init&TRUE)||(atom[i].attr&ATOM_ATTR_HB))
911                         v3_scale(&(atom[i].v),&(atom[i].v),scale);
912         }
913
914         return 0;
915 }
916
917 double ideal_gas_law_pressure(t_moldyn *moldyn) {
918
919         double p;
920
921         p=moldyn->count*moldyn->t*K_BOLTZMANN/moldyn->volume;
922
923         return p;
924 }
925
926 double virial_sum(t_moldyn *moldyn) {
927
928         int i;
929         t_virial *virial;
930
931         /* virial (sum over atom virials) */
932         moldyn->virial=0.0;
933         moldyn->vir.xx=0.0;
934         moldyn->vir.yy=0.0;
935         moldyn->vir.zz=0.0;
936         moldyn->vir.xy=0.0;
937         moldyn->vir.xz=0.0;
938         moldyn->vir.yz=0.0;
939         for(i=0;i<moldyn->count;i++) {
940                 virial=&(moldyn->atom[i].virial);
941                 moldyn->virial+=(virial->xx+virial->yy+virial->zz);
942                 moldyn->vir.xx+=virial->xx;
943                 moldyn->vir.yy+=virial->yy;
944                 moldyn->vir.zz+=virial->zz;
945                 moldyn->vir.xy+=virial->xy;
946                 moldyn->vir.xz+=virial->xz;
947                 moldyn->vir.yz+=virial->yz;
948         }
949
950         /* global virial (absolute coordinates) */
951         virial=&(moldyn->gvir);
952         moldyn->gv=virial->xx+virial->yy+virial->zz;
953
954         return moldyn->virial;
955 }
956
957 double pressure_calc(t_moldyn *moldyn) {
958
959         /*
960          * PV = NkT + <W>
961          * with W = 1/3 sum_i f_i r_i (- skipped!)
962          * virial = sum_i f_i r_i
963          * 
964          * => P = (2 Ekin + virial) / (3V)
965          */
966
967         /* assume up to date virial & up to date kinetic energy */
968
969         /* pressure (atom virials) */
970         moldyn->p=2.0*moldyn->ekin+moldyn->virial;
971         moldyn->p/=(3.0*moldyn->volume);
972
973         /* pressure (absolute coordinates) */
974         moldyn->gp=2.0*moldyn->ekin+moldyn->gv;
975         moldyn->gp/=(3.0*moldyn->volume);
976
977         return moldyn->p;
978 }
979
980 int average_reset(t_moldyn *moldyn) {
981
982         printf("[moldyn] average reset\n");
983
984         /* update skip value */
985         moldyn->avg_skip=moldyn->total_steps;
986
987         /* kinetic energy */
988         moldyn->k_sum=0.0;
989         moldyn->k2_sum=0.0;
990         
991         /* potential energy */
992         moldyn->v_sum=0.0;
993         moldyn->v2_sum=0.0;
994
995         /* temperature */
996         moldyn->t_sum=0.0;
997
998         /* virial */
999         moldyn->virial_sum=0.0;
1000         moldyn->gv_sum=0.0;
1001
1002         /* pressure */
1003         moldyn->p_sum=0.0;
1004         moldyn->gp_sum=0.0;
1005         moldyn->tp_sum=0.0;
1006
1007         return 0;
1008 }
1009
1010 int average_and_fluctuation_calc(t_moldyn *moldyn) {
1011
1012         int denom;
1013
1014         if(moldyn->total_steps<moldyn->avg_skip)
1015                 return 0;
1016
1017         denom=moldyn->total_steps+1-moldyn->avg_skip;
1018
1019         /* assume up to date energies, temperature, pressure etc */
1020
1021         /* kinetic energy */
1022         moldyn->k_sum+=moldyn->ekin;
1023         moldyn->k2_sum+=(moldyn->ekin*moldyn->ekin);
1024         moldyn->k_avg=moldyn->k_sum/denom;
1025         moldyn->k2_avg=moldyn->k2_sum/denom;
1026         moldyn->dk2_avg=moldyn->k2_avg-(moldyn->k_avg*moldyn->k_avg);
1027
1028         /* potential energy */
1029         moldyn->v_sum+=moldyn->energy;
1030         moldyn->v2_sum+=(moldyn->energy*moldyn->energy);
1031         moldyn->v_avg=moldyn->v_sum/denom;
1032         moldyn->v2_avg=moldyn->v2_sum/denom;
1033         moldyn->dv2_avg=moldyn->v2_avg-(moldyn->v_avg*moldyn->v_avg);
1034
1035         /* temperature */
1036         moldyn->t_sum+=moldyn->t;
1037         moldyn->t_avg=moldyn->t_sum/denom;
1038
1039         /* virial */
1040         moldyn->virial_sum+=moldyn->virial;
1041         moldyn->virial_avg=moldyn->virial_sum/denom;
1042         moldyn->gv_sum+=moldyn->gv;
1043         moldyn->gv_avg=moldyn->gv_sum/denom;
1044
1045         /* pressure */
1046         moldyn->p_sum+=moldyn->p;
1047         moldyn->p_avg=moldyn->p_sum/denom;
1048         moldyn->gp_sum+=moldyn->gp;
1049         moldyn->gp_avg=moldyn->gp_sum/denom;
1050         moldyn->tp_sum+=moldyn->tp;
1051         moldyn->tp_avg=moldyn->tp_sum/denom;
1052
1053         return 0;
1054 }
1055
1056 int get_heat_capacity(t_moldyn *moldyn) {
1057
1058         double temp2,ighc;
1059
1060         /* averages needed for heat capacity calc */
1061         if(moldyn->total_steps<moldyn->avg_skip)
1062                 return 0;
1063
1064         /* (temperature average)^2 */
1065         temp2=moldyn->t_avg*moldyn->t_avg;
1066         printf("[moldyn] specific heat capacity for T=%f K [J/(kg K)]\n",
1067                moldyn->t_avg);
1068
1069         /* ideal gas contribution */
1070         ighc=3.0*moldyn->count*K_BOLTZMANN/2.0;
1071         printf("  ideal gas contribution: %f\n",
1072                ighc/moldyn->mass*KILOGRAM/JOULE);
1073
1074         /* specific heat for nvt ensemble */
1075         moldyn->c_v_nvt=moldyn->dv2_avg/(K_BOLTZMANN*temp2)+ighc;
1076         moldyn->c_v_nvt/=moldyn->mass;
1077
1078         /* specific heat for nve ensemble */
1079         moldyn->c_v_nve=ighc/(1.0-(moldyn->dv2_avg/(ighc*K_BOLTZMANN*temp2)));
1080         moldyn->c_v_nve/=moldyn->mass;
1081
1082         printf("  NVE: %f\n",moldyn->c_v_nve*KILOGRAM/JOULE);
1083         printf("  NVT: %f\n",moldyn->c_v_nvt*KILOGRAM/JOULE);
1084 printf("  --> <dV2> sim: %f experimental: %f\n",moldyn->dv2_avg,1.5*moldyn->count*K_B2*moldyn->t_avg*moldyn->t_avg*(1.0-1.5*moldyn->count*K_BOLTZMANN/(700*moldyn->mass*JOULE/KILOGRAM)));
1085
1086         return 0;
1087 }
1088
1089 double thermodynamic_pressure_calc(t_moldyn *moldyn) {
1090
1091         t_3dvec dim;
1092         //t_3dvec *tp;
1093         double h,dv;
1094         double y0,y1;
1095         double su,sd;
1096         t_atom *store;
1097
1098         /*
1099          * dU = - p dV
1100          *
1101          * => p = - dU/dV
1102          *
1103          */
1104
1105         /* store atomic configuration + dimension */
1106         store=malloc(moldyn->count*sizeof(t_atom));
1107         if(store==NULL) {
1108                 printf("[moldyn] allocating store mem failed\n");
1109                 return -1;
1110         }
1111         memcpy(store,moldyn->atom,moldyn->count*sizeof(t_atom));
1112         dim=moldyn->dim;
1113
1114         /* x1, y1 */
1115         sd=0.00001;
1116         h=(1.0-sd)*(1.0-sd)*(1.0-sd);
1117         su=pow(2.0-h,ONE_THIRD)-1.0;
1118         dv=(1.0-h)*moldyn->volume;
1119
1120         /* scale up dimension and atom positions */
1121         scale_dim(moldyn,SCALE_UP,su,TRUE,TRUE,TRUE);
1122         scale_atoms(moldyn,SCALE_UP,su,TRUE,TRUE,TRUE);
1123         link_cell_shutdown(moldyn);
1124         link_cell_init(moldyn,QUIET);
1125         potential_force_calc(moldyn);
1126         y1=moldyn->energy;
1127
1128         /* restore atomic configuration + dim */
1129         memcpy(moldyn->atom,store,moldyn->count*sizeof(t_atom));
1130         moldyn->dim=dim;
1131
1132         /* scale down dimension and atom positions */
1133         scale_dim(moldyn,SCALE_DOWN,sd,TRUE,TRUE,TRUE);
1134         scale_atoms(moldyn,SCALE_DOWN,sd,TRUE,TRUE,TRUE);
1135         link_cell_shutdown(moldyn);
1136         link_cell_init(moldyn,QUIET);
1137         potential_force_calc(moldyn);
1138         y0=moldyn->energy;
1139         
1140         /* calculate pressure */
1141         moldyn->tp=-(y1-y0)/(2.0*dv);
1142
1143         /* restore atomic configuration */
1144         memcpy(moldyn->atom,store,moldyn->count*sizeof(t_atom));
1145         moldyn->dim=dim;
1146         link_cell_shutdown(moldyn);
1147         link_cell_init(moldyn,QUIET);
1148         //potential_force_calc(moldyn);
1149
1150         /* free store buffer */
1151         if(store)
1152                 free(store);
1153
1154         return moldyn->tp;
1155 }
1156
1157 double get_pressure(t_moldyn *moldyn) {
1158
1159         return moldyn->p;
1160
1161 }
1162
1163 int scale_dim(t_moldyn *moldyn,u8 dir,double scale,u8 x,u8 y,u8 z) {
1164
1165         t_3dvec *dim;
1166
1167         dim=&(moldyn->dim);
1168
1169         if(dir==SCALE_UP)
1170                 scale=1.0+scale;
1171
1172         if(dir==SCALE_DOWN)
1173                 scale=1.0-scale;
1174
1175         if(x) dim->x*=scale;
1176         if(y) dim->y*=scale;
1177         if(z) dim->z*=scale;
1178
1179         return 0;
1180 }
1181
1182 int scale_atoms(t_moldyn *moldyn,u8 dir,double scale,u8 x,u8 y,u8 z) {
1183
1184         int i;
1185         t_3dvec *r;
1186
1187         if(dir==SCALE_UP)
1188                 scale=1.0+scale;
1189
1190         if(dir==SCALE_DOWN)
1191                 scale=1.0-scale;
1192
1193         for(i=0;i<moldyn->count;i++) {
1194                 r=&(moldyn->atom[i].r);
1195                 if(x) r->x*=scale;
1196                 if(y) r->y*=scale;
1197                 if(z) r->z*=scale;
1198         }
1199
1200         return 0;
1201 }
1202
1203 int scale_volume(t_moldyn *moldyn) {
1204
1205         t_3dvec *dim,*vdim;
1206         double scale;
1207         t_linkcell *lc;
1208
1209         vdim=&(moldyn->vis.dim);
1210         dim=&(moldyn->dim);
1211         lc=&(moldyn->lc);
1212
1213         /* scaling factor */
1214         if(moldyn->pt_scale&P_SCALE_BERENDSEN) {
1215                 scale=1.0-(moldyn->p_ref-moldyn->p)*moldyn->p_tc;
1216                 scale=pow(scale,ONE_THIRD);
1217         }
1218         else {
1219                 scale=pow(moldyn->p/moldyn->p_ref,ONE_THIRD);
1220         }
1221
1222         /* scale the atoms and dimensions */
1223         scale_atoms(moldyn,SCALE_DIRECT,scale,TRUE,TRUE,TRUE);
1224         scale_dim(moldyn,SCALE_DIRECT,scale,TRUE,TRUE,TRUE);
1225
1226         /* visualize dimensions */
1227         if(vdim->x!=0) {
1228                 vdim->x=dim->x;
1229                 vdim->y=dim->y;
1230                 vdim->z=dim->z;
1231         }
1232
1233         /* recalculate scaled volume */
1234         moldyn->volume=dim->x*dim->y*dim->z;
1235
1236         /* adjust/reinit linkcell */
1237         if(((int)(dim->x/moldyn->cutoff)!=lc->nx)||
1238            ((int)(dim->y/moldyn->cutoff)!=lc->ny)||
1239            ((int)(dim->z/moldyn->cutoff)!=lc->nx)) {
1240                 link_cell_shutdown(moldyn);
1241                 link_cell_init(moldyn,QUIET);
1242         } else {
1243                 lc->x*=scale;
1244                 lc->y*=scale;
1245                 lc->z*=scale;
1246         }
1247
1248         return 0;
1249
1250 }
1251
1252 double e_kin_calc(t_moldyn *moldyn) {
1253
1254         int i;
1255         t_atom *atom;
1256
1257         atom=moldyn->atom;
1258         moldyn->ekin=0.0;
1259
1260         for(i=0;i<moldyn->count;i++) {
1261                 atom[i].ekin=0.5*atom[i].mass*v3_absolute_square(&(atom[i].v));
1262                 moldyn->ekin+=atom[i].ekin;
1263         }
1264
1265         return moldyn->ekin;
1266 }
1267
1268 double get_total_energy(t_moldyn *moldyn) {
1269
1270         return(moldyn->ekin+moldyn->energy);
1271 }
1272
1273 t_3dvec get_total_p(t_moldyn *moldyn) {
1274
1275         t_3dvec p,p_total;
1276         int i;
1277         t_atom *atom;
1278
1279         atom=moldyn->atom;
1280
1281         v3_zero(&p_total);
1282         for(i=0;i<moldyn->count;i++) {
1283                 v3_scale(&p,&(atom[i].v),atom[i].mass);
1284                 v3_add(&p_total,&p_total,&p);
1285         }
1286
1287         return p_total;
1288 }
1289
1290 double estimate_time_step(t_moldyn *moldyn,double nn_dist) {
1291
1292         double tau;
1293
1294         /* nn_dist is the nearest neighbour distance */
1295
1296         tau=(0.05*nn_dist*moldyn->atom[0].mass)/sqrt(3.0*K_BOLTZMANN*moldyn->t);
1297
1298         return tau;     
1299 }
1300
1301 /*
1302  * numerical tricks
1303  */
1304
1305 /* linked list / cell method */
1306
1307 int link_cell_init(t_moldyn *moldyn,u8 vol) {
1308
1309         t_linkcell *lc;
1310         int i;
1311
1312         lc=&(moldyn->lc);
1313
1314         /* partitioning the md cell */
1315         lc->nx=moldyn->dim.x/moldyn->cutoff;
1316         lc->x=moldyn->dim.x/lc->nx;
1317         lc->ny=moldyn->dim.y/moldyn->cutoff;
1318         lc->y=moldyn->dim.y/lc->ny;
1319         lc->nz=moldyn->dim.z/moldyn->cutoff;
1320         lc->z=moldyn->dim.z/lc->nz;
1321         lc->cells=lc->nx*lc->ny*lc->nz;
1322
1323 #ifdef STATIC_LISTS
1324         lc->subcell=malloc(lc->cells*sizeof(int*));
1325 #else
1326         lc->subcell=malloc(lc->cells*sizeof(t_list));
1327 #endif
1328
1329         if(lc->subcell==NULL) {
1330                 perror("[moldyn] cell init (malloc)");
1331                 return -1;
1332         }
1333
1334         if(lc->cells<27)
1335                 printf("[moldyn] FATAL: less then 27 subcells!\n");
1336
1337         if(vol) {
1338 #ifdef STATIC_LISTS
1339                 printf("[moldyn] initializing 'static' linked cells (%d)\n",
1340                        lc->cells);
1341 #else
1342                 printf("[moldyn] initializing 'dynamic' linked cells (%d)\n",
1343                        lc->cells);
1344 #endif
1345                 printf("  x: %d x %f A\n",lc->nx,lc->x);
1346                 printf("  y: %d x %f A\n",lc->ny,lc->y);
1347                 printf("  z: %d x %f A\n",lc->nz,lc->z);
1348         }
1349
1350 #ifdef STATIC_LISTS
1351         /* list init */
1352         for(i=0;i<lc->cells;i++) {
1353                 lc->subcell[i]=malloc((MAX_ATOMS_PER_LIST+1)*sizeof(int));
1354                 if(lc->subcell[i]==NULL) {
1355                         perror("[moldyn] list init (malloc)");
1356                         return -1;
1357                 }
1358                 /*
1359                 if(i==0)
1360                         printf(" ---> %d malloc %p (%p)\n",
1361                                i,lc->subcell[0],lc->subcell);
1362                 */
1363         }
1364 #else
1365         for(i=0;i<lc->cells;i++)
1366                 list_init_f(&(lc->subcell[i]));
1367 #endif
1368
1369         /* update the list */
1370         link_cell_update(moldyn);
1371
1372         return 0;
1373 }
1374
1375 int link_cell_update(t_moldyn *moldyn) {
1376
1377         int count,i,j,k;
1378         int nx,ny;
1379         t_atom *atom;
1380         t_linkcell *lc;
1381 #ifdef STATIC_LISTS
1382         int p;
1383 #endif
1384
1385         atom=moldyn->atom;
1386         lc=&(moldyn->lc);
1387
1388         nx=lc->nx;
1389         ny=lc->ny;
1390
1391         for(i=0;i<lc->cells;i++)
1392 #ifdef STATIC_LISTS
1393                 memset(lc->subcell[i],0,(MAX_ATOMS_PER_LIST+1)*sizeof(int));
1394 #else
1395                 list_destroy_f(&(lc->subcell[i]));
1396 #endif
1397
1398         for(count=0;count<moldyn->count;count++) {
1399                 i=((atom[count].r.x+(moldyn->dim.x/2))/lc->x);
1400                 j=((atom[count].r.y+(moldyn->dim.y/2))/lc->y);
1401                 k=((atom[count].r.z+(moldyn->dim.z/2))/lc->z);
1402         
1403 #ifdef STATIC_LISTS
1404                 p=0;
1405                 while(lc->subcell[i+j*nx+k*nx*ny][p]!=0)
1406                         p++;
1407
1408                 if(p>=MAX_ATOMS_PER_LIST) {
1409                         printf("[moldyn] FATAL: amount of atoms too high!\n");
1410                         return -1;
1411                 }
1412
1413                 lc->subcell[i+j*nx+k*nx*ny][p]=count;
1414 #else
1415                 list_add_immediate_f(&(lc->subcell[i+j*nx+k*nx*ny]),
1416                                      &(atom[count]));
1417                 /*
1418                 if(j==0&&k==0)
1419                         printf(" ---> %d %d malloc %p (%p)\n",
1420                                i,count,lc->subcell[i].current,lc->subcell);
1421                 */
1422 #endif
1423         }
1424
1425         return 0;
1426 }
1427
1428 int link_cell_neighbour_index(t_moldyn *moldyn,int i,int j,int k,
1429 #ifdef STATIC_LISTS
1430                               int **cell
1431 #else
1432                               t_list *cell
1433 #endif
1434                              ) {
1435
1436         t_linkcell *lc;
1437         int a;
1438         int count1,count2;
1439         int ci,cj,ck;
1440         int nx,ny,nz;
1441         int x,y,z;
1442         u8 bx,by,bz;
1443
1444         lc=&(moldyn->lc);
1445         nx=lc->nx;
1446         ny=lc->ny;
1447         nz=lc->nz;
1448         count1=1;
1449         count2=27;
1450         a=nx*ny;
1451
1452         if(i>=nx||j>=ny||k>=nz)
1453                 printf("[moldyn] WARNING: lcni %d/%d %d/%d %d/%d\n",
1454                        i,nx,j,ny,k,nz);
1455
1456         cell[0]=lc->subcell[i+j*nx+k*a];
1457         for(ci=-1;ci<=1;ci++) {
1458                 bx=0;
1459                 x=i+ci;
1460                 if((x<0)||(x>=nx)) {
1461                         x=(x+nx)%nx;
1462                         bx=1;
1463                 }
1464                 for(cj=-1;cj<=1;cj++) {
1465                         by=0;
1466                         y=j+cj;
1467                         if((y<0)||(y>=ny)) {
1468                                 y=(y+ny)%ny;
1469                                 by=1;
1470                         }
1471                         for(ck=-1;ck<=1;ck++) {
1472                                 bz=0;
1473                                 z=k+ck;
1474                                 if((z<0)||(z>=nz)) {
1475                                         z=(z+nz)%nz;
1476                                         bz=1;
1477                                 }
1478                                 if(!(ci|cj|ck)) continue;
1479                                 if(bx|by|bz) {
1480                                         cell[--count2]=lc->subcell[x+y*nx+z*a];
1481                                 }
1482                                 else {
1483                                         cell[count1++]=lc->subcell[x+y*nx+z*a];
1484                                 }
1485                         }
1486                 }
1487         }
1488
1489         lc->dnlc=count1;
1490
1491         return count1;
1492 }
1493
1494 int link_cell_shutdown(t_moldyn *moldyn) {
1495
1496         int i;
1497         t_linkcell *lc;
1498
1499         lc=&(moldyn->lc);
1500
1501         for(i=0;i<lc->cells;i++) {
1502 #ifdef STATIC_LISTS
1503                 free(lc->subcell[i]);
1504 #else
1505                 //printf(" ---> %d free %p\n",i,lc->subcell[i].start);
1506                 list_destroy_f(&(lc->subcell[i]));
1507 #endif
1508         }
1509
1510         free(lc->subcell);
1511
1512         return 0;
1513 }
1514
1515 int moldyn_add_schedule(t_moldyn *moldyn,int runs,double tau) {
1516
1517         int count;
1518         void *ptr;
1519         t_moldyn_schedule *schedule;
1520
1521         schedule=&(moldyn->schedule);
1522         count=++(schedule->total_sched);
1523
1524         ptr=realloc(schedule->runs,count*sizeof(int));
1525         if(!ptr) {
1526                 perror("[moldyn] realloc (runs)");
1527                 return -1;
1528         }
1529         schedule->runs=ptr;
1530         schedule->runs[count-1]=runs;
1531
1532         ptr=realloc(schedule->tau,count*sizeof(double));
1533         if(!ptr) {
1534                 perror("[moldyn] realloc (tau)");
1535                 return -1;
1536         }
1537         schedule->tau=ptr;
1538         schedule->tau[count-1]=tau;
1539
1540         printf("[moldyn] schedule added:\n");
1541         printf("  number: %d | runs: %d | tau: %f\n",count-1,runs,tau);
1542                                        
1543
1544         return 0;
1545 }
1546
1547 int moldyn_set_schedule_hook(t_moldyn *moldyn,set_hook hook,void *hook_params) {
1548
1549         moldyn->schedule.hook=hook;
1550         moldyn->schedule.hook_params=hook_params;
1551         
1552         return 0;
1553 }
1554
1555 /*
1556  *
1557  * 'integration of newtons equation' - algorithms
1558  *
1559  */
1560
1561 /* start the integration */
1562
1563 int moldyn_integrate(t_moldyn *moldyn) {
1564
1565         int i;
1566         unsigned int e,m,s,v,p,t,a;
1567         t_3dvec momentum;
1568         t_moldyn_schedule *sched;
1569         t_atom *atom;
1570         int fd;
1571         char dir[128];
1572         double ds;
1573         double energy_scale;
1574         struct timeval t1,t2;
1575         //double tp;
1576
1577         sched=&(moldyn->schedule);
1578         atom=moldyn->atom;
1579
1580         /* initialize linked cell method */
1581         link_cell_init(moldyn,VERBOSE);
1582
1583         /* logging & visualization */
1584         e=moldyn->ewrite;
1585         m=moldyn->mwrite;
1586         s=moldyn->swrite;
1587         v=moldyn->vwrite;
1588         a=moldyn->awrite;
1589         p=moldyn->pwrite;
1590         t=moldyn->twrite;
1591
1592         /* sqaure of some variables */
1593         moldyn->tau_square=moldyn->tau*moldyn->tau;
1594         moldyn->cutoff_square=moldyn->cutoff*moldyn->cutoff;
1595
1596         /* get current time */
1597         gettimeofday(&t1,NULL);
1598
1599         /* calculate initial forces */
1600         potential_force_calc(moldyn);
1601 #ifdef DEBUG
1602 //return 0;
1603 #endif
1604
1605         /* some stupid checks before we actually start calculating bullshit */
1606         if(moldyn->cutoff>0.5*moldyn->dim.x)
1607                 printf("[moldyn] WARNING: cutoff > 0.5 x dim.x\n");
1608         if(moldyn->cutoff>0.5*moldyn->dim.y)
1609                 printf("[moldyn] WARNING: cutoff > 0.5 x dim.y\n");
1610         if(moldyn->cutoff>0.5*moldyn->dim.z)
1611                 printf("[moldyn] WARNING: cutoff > 0.5 x dim.z\n");
1612         ds=0.5*atom[0].f.x*moldyn->tau_square/atom[0].mass;
1613         if(ds>0.05*moldyn->nnd)
1614                 printf("[moldyn] WARNING: forces too high / tau too small!\n");
1615
1616         /* zero absolute time */
1617         moldyn->time=0.0;
1618         moldyn->total_steps=0;
1619
1620         /* debugging, ignore */
1621         moldyn->debug=0;
1622
1623         /* tell the world */
1624         printf("[moldyn] integration start, go get a coffee ...\n");
1625
1626         /* executing the schedule */
1627         sched->count=0;
1628         while(sched->count<sched->total_sched) {
1629
1630                 /* setting amount of runs and finite time step size */
1631                 moldyn->tau=sched->tau[sched->count];
1632                 moldyn->tau_square=moldyn->tau*moldyn->tau;
1633                 moldyn->time_steps=sched->runs[sched->count];
1634
1635                 /* energy scaling factor (might change!) */
1636                 energy_scale=moldyn->count*EV;
1637
1638         /* integration according to schedule */
1639
1640         for(i=0;i<moldyn->time_steps;i++) {
1641
1642                 /* integration step */
1643                 moldyn->integrate(moldyn);
1644
1645                 /* calculate kinetic energy, temperature and pressure */
1646                 e_kin_calc(moldyn);
1647                 temperature_calc(moldyn);
1648                 virial_sum(moldyn);
1649                 pressure_calc(moldyn);
1650                 //thermodynamic_pressure_calc(moldyn);
1651
1652                 /* calculate fluctuations + averages */
1653                 average_and_fluctuation_calc(moldyn);
1654
1655                 /* p/t scaling */
1656                 if(moldyn->pt_scale&(T_SCALE_BERENDSEN|T_SCALE_DIRECT))
1657                         scale_velocity(moldyn,FALSE);
1658                 if(moldyn->pt_scale&(P_SCALE_BERENDSEN|P_SCALE_DIRECT))
1659                         scale_volume(moldyn);
1660
1661                 /* check for log & visualization */
1662                 if(e) {
1663                         if(!(moldyn->total_steps%e))
1664                                 dprintf(moldyn->efd,
1665                                         "%f %f %f %f\n",
1666                                         moldyn->time,moldyn->ekin/energy_scale,
1667                                         moldyn->energy/energy_scale,
1668                                         get_total_energy(moldyn)/energy_scale);
1669                 }
1670                 if(m) {
1671                         if(!(moldyn->total_steps%m)) {
1672                                 momentum=get_total_p(moldyn);
1673                                 dprintf(moldyn->mfd,
1674                                         "%f %f %f %f %f\n",moldyn->time,
1675                                         momentum.x,momentum.y,momentum.z,
1676                                         v3_norm(&momentum));
1677                         }
1678                 }
1679                 if(p) {
1680                         if(!(moldyn->total_steps%p)) {
1681                                 dprintf(moldyn->pfd,
1682                                         "%f %f %f %f %f %f %f\n",moldyn->time,
1683                                          moldyn->p/BAR,moldyn->p_avg/BAR,
1684                                          moldyn->gp/BAR,moldyn->gp_avg/BAR,
1685                                          moldyn->tp/BAR,moldyn->tp_avg/BAR);
1686                         }
1687                 }
1688                 if(t) {
1689                         if(!(moldyn->total_steps%t)) {
1690                                 dprintf(moldyn->tfd,
1691                                         "%f %f %f\n",
1692                                         moldyn->time,moldyn->t,moldyn->t_avg);
1693                         }
1694                 }
1695                 if(v) {
1696                         if(!(moldyn->total_steps%v)) {
1697                                 dprintf(moldyn->vfd,
1698                                         "%f %f\n",moldyn->time,moldyn->volume);
1699                         }
1700                 }
1701                 if(s) {
1702                         if(!(moldyn->total_steps%s)) {
1703                                 snprintf(dir,128,"%s/s-%07.f.save",
1704                                          moldyn->vlsdir,moldyn->time);
1705                                 fd=open(dir,O_WRONLY|O_TRUNC|O_CREAT,
1706                                         S_IRUSR|S_IWUSR);
1707                                 if(fd<0) perror("[moldyn] save fd open");
1708                                 else {
1709                                         write(fd,moldyn,sizeof(t_moldyn));
1710                                         write(fd,moldyn->atom,
1711                                               moldyn->count*sizeof(t_atom));
1712                                 }
1713                                 close(fd);
1714                         }       
1715                 }
1716                 if(a) {
1717                         if(!(moldyn->total_steps%a)) {
1718                                 visual_atoms(moldyn);
1719                         }
1720                 }
1721
1722                 /* display progress */
1723                 //if(!(moldyn->total_steps%10)) {
1724                         /* get current time */
1725                         gettimeofday(&t2,NULL);
1726
1727 printf("\rsched:%d, steps:%d/%d, T:%4.1f/%4.1f P:%4.1f/%4.1f V:%6.1f (%d)",
1728        sched->count,i,moldyn->total_steps,
1729        moldyn->t,moldyn->t_avg,
1730        moldyn->p/BAR,moldyn->p_avg/BAR,
1731        moldyn->volume,
1732        (int)(t2.tv_sec-t1.tv_sec));
1733
1734                         fflush(stdout);
1735
1736                         /* copy over time */
1737                         t1=t2;
1738                 //}
1739
1740                 /* increase absolute time */
1741                 moldyn->time+=moldyn->tau;
1742                 moldyn->total_steps+=1;
1743
1744         }
1745
1746                 /* check for hooks */
1747                 if(sched->hook) {
1748                         printf("\n ## schedule hook %d start ##\n",
1749                                sched->count);
1750                         sched->hook(moldyn,sched->hook_params);
1751                         printf(" ## schedule hook end ##\n");
1752                 }
1753
1754                 /* increase the schedule counter */
1755                 sched->count+=1;
1756
1757         }
1758
1759         return 0;
1760 }
1761
1762 /* velocity verlet */
1763
1764 int velocity_verlet(t_moldyn *moldyn) {
1765
1766         int i,count;
1767         double tau,tau_square,h;
1768         t_3dvec delta;
1769         t_atom *atom;
1770
1771         atom=moldyn->atom;
1772         count=moldyn->count;
1773         tau=moldyn->tau;
1774         tau_square=moldyn->tau_square;
1775
1776         for(i=0;i<count;i++) {
1777                 /* check whether fixed atom */
1778                 if(atom[i].attr&ATOM_ATTR_FP)
1779                         continue;
1780                 /* new positions */
1781                 h=0.5/atom[i].mass;
1782                 v3_scale(&delta,&(atom[i].v),tau);
1783                 v3_add(&(atom[i].r),&(atom[i].r),&delta);
1784                 v3_scale(&delta,&(atom[i].f),h*tau_square);
1785                 v3_add(&(atom[i].r),&(atom[i].r),&delta);
1786                 check_per_bound(moldyn,&(atom[i].r));
1787
1788                 /* velocities [actually v(t+tau/2)] */
1789                 v3_scale(&delta,&(atom[i].f),h*tau);
1790                 v3_add(&(atom[i].v),&(atom[i].v),&delta);
1791         }
1792
1793         /* criticial check */
1794         moldyn_bc_check(moldyn);
1795
1796         /* neighbour list update */
1797         link_cell_update(moldyn);
1798
1799         /* forces depending on chosen potential */
1800         potential_force_calc(moldyn);
1801
1802         for(i=0;i<count;i++) {
1803                 /* check whether fixed atom */
1804                 if(atom[i].attr&ATOM_ATTR_FP)
1805                         continue;
1806                 /* again velocities [actually v(t+tau)] */
1807                 v3_scale(&delta,&(atom[i].f),0.5*tau/atom[i].mass);
1808                 v3_add(&(atom[i].v),&(atom[i].v),&delta);
1809         }
1810
1811         return 0;
1812 }
1813
1814
1815 /*
1816  *
1817  * potentials & corresponding forces & virial routine
1818  * 
1819  */
1820
1821 /* generic potential and force calculation */
1822
1823 int potential_force_calc(t_moldyn *moldyn) {
1824
1825         int i,j,k,count;
1826         t_atom *itom,*jtom,*ktom;
1827         t_virial *virial;
1828         t_linkcell *lc;
1829 #ifdef STATIC_LISTS
1830         int *neighbour_i[27];
1831         int p,q;
1832         t_atom *atom;
1833 #else
1834         t_list neighbour_i[27];
1835         t_list neighbour_i2[27];
1836         t_list *this,*that;
1837 #endif
1838         u8 bc_ij,bc_ik;
1839         int dnlc;
1840
1841         count=moldyn->count;
1842         itom=moldyn->atom;
1843         lc=&(moldyn->lc);
1844 #ifdef STATIC_LISTS
1845         atom=moldyn->atom;
1846 #endif
1847
1848         /* reset energy */
1849         moldyn->energy=0.0;
1850
1851         /* reset global virial */
1852         memset(&(moldyn->gvir),0,sizeof(t_virial));
1853
1854         /* reset force, site energy and virial of every atom */
1855         for(i=0;i<count;i++) {
1856
1857                 /* reset force */
1858                 v3_zero(&(itom[i].f));
1859
1860                 /* reset virial */
1861                 virial=(&(itom[i].virial));
1862                 virial->xx=0.0;
1863                 virial->yy=0.0;
1864                 virial->zz=0.0;
1865                 virial->xy=0.0;
1866                 virial->xz=0.0;
1867                 virial->yz=0.0;
1868         
1869                 /* reset site energy */
1870                 itom[i].e=0.0;
1871
1872         }
1873
1874         /* get energy, force and virial of every atom */
1875
1876         /* first (and only) loop over atoms i */
1877         for(i=0;i<count;i++) {
1878
1879                 /* single particle potential/force */
1880                 if(itom[i].attr&ATOM_ATTR_1BP)
1881                         if(moldyn->func1b)
1882                                 moldyn->func1b(moldyn,&(itom[i]));
1883
1884                 if(!(itom[i].attr&(ATOM_ATTR_2BP|ATOM_ATTR_3BP)))
1885                         continue;
1886
1887                 /* 2 body pair potential/force */
1888         
1889                 link_cell_neighbour_index(moldyn,
1890                                           (itom[i].r.x+moldyn->dim.x/2)/lc->x,
1891                                           (itom[i].r.y+moldyn->dim.y/2)/lc->y,
1892                                           (itom[i].r.z+moldyn->dim.z/2)/lc->z,
1893                                           neighbour_i);
1894
1895                 dnlc=lc->dnlc;
1896
1897                 /* first loop over atoms j */
1898                 if(moldyn->func2b) {
1899                         for(j=0;j<27;j++) {
1900
1901                                 bc_ij=(j<dnlc)?0:1;
1902 #ifdef STATIC_LISTS
1903                                 p=0;
1904
1905                                 while(neighbour_i[j][p]!=0) {
1906
1907                                         jtom=&(atom[neighbour_i[j][p]]);
1908                                         p++;
1909
1910                                         if(jtom==&(itom[i]))
1911                                                 continue;
1912
1913                                         if((jtom->attr&ATOM_ATTR_2BP)&
1914                                            (itom[i].attr&ATOM_ATTR_2BP)) {
1915                                                 moldyn->func2b(moldyn,
1916                                                                &(itom[i]),
1917                                                                jtom,
1918                                                                bc_ij);
1919                                         }
1920                                 }
1921 #else
1922                                 this=&(neighbour_i[j]);
1923                                 list_reset_f(this);
1924
1925                                 if(this->start==NULL)
1926                                         continue;
1927
1928                                 do {
1929                                         jtom=this->current->data;
1930
1931                                         if(jtom==&(itom[i]))
1932                                                 continue;
1933
1934                                         if((jtom->attr&ATOM_ATTR_2BP)&
1935                                            (itom[i].attr&ATOM_ATTR_2BP)) {
1936                                                 moldyn->func2b(moldyn,
1937                                                                &(itom[i]),
1938                                                                jtom,
1939                                                                bc_ij);
1940                                         }
1941                                 } while(list_next_f(this)!=L_NO_NEXT_ELEMENT);
1942 #endif
1943
1944                         }
1945                 }
1946
1947                 /* 3 body potential/force */
1948
1949                 if(!(itom[i].attr&ATOM_ATTR_3BP))
1950                         continue;
1951
1952                 /* copy the neighbour lists */
1953 #ifdef STATIC_LISTS
1954                 /* no copy needed for static lists */
1955 #else
1956                 memcpy(neighbour_i2,neighbour_i,27*sizeof(t_list));
1957 #endif
1958
1959                 /* second loop over atoms j */
1960                 for(j=0;j<27;j++) {
1961
1962                         bc_ij=(j<dnlc)?0:1;
1963 #ifdef STATIC_LISTS
1964                         p=0;
1965
1966                         while(neighbour_i[j][p]!=0) {
1967
1968                                 jtom=&(atom[neighbour_i[j][p]]);
1969                                 p++;
1970 #else
1971                         this=&(neighbour_i[j]);
1972                         list_reset_f(this);
1973
1974                         if(this->start==NULL)
1975                                 continue;
1976
1977                         do {
1978
1979                                 jtom=this->current->data;
1980 #endif
1981
1982                                 if(jtom==&(itom[i]))
1983                                         continue;
1984
1985                                 if(!(jtom->attr&ATOM_ATTR_3BP))
1986                                         continue;
1987
1988                                 /* reset 3bp run */
1989                                 moldyn->run3bp=1;
1990
1991                                 if(moldyn->func3b_j1)
1992                                         moldyn->func3b_j1(moldyn,
1993                                                           &(itom[i]),
1994                                                           jtom,
1995                                                           bc_ij);
1996
1997                                 /* in first j loop, 3bp run can be skipped */
1998                                 if(!(moldyn->run3bp))
1999                                         continue;
2000                         
2001                                 /* first loop over atoms k */
2002                                 if(moldyn->func3b_k1) {
2003
2004                                 for(k=0;k<27;k++) {
2005
2006                                         bc_ik=(k<dnlc)?0:1;
2007 #ifdef STATIC_LISTS
2008                                         q=0;
2009
2010                                         while(neighbour_i[j][q]!=0) {
2011
2012                                                 ktom=&(atom[neighbour_i[k][q]]);
2013                                                 q++;
2014 #else
2015                                         that=&(neighbour_i2[k]);
2016                                         list_reset_f(that);
2017                                         
2018                                         if(that->start==NULL)
2019                                                 continue;
2020
2021                                         do {
2022                                                 ktom=that->current->data;
2023 #endif
2024
2025                                                 if(!(ktom->attr&ATOM_ATTR_3BP))
2026                                                         continue;
2027
2028                                                 if(ktom==jtom)
2029                                                         continue;
2030
2031                                                 if(ktom==&(itom[i]))
2032                                                         continue;
2033
2034                                                 moldyn->func3b_k1(moldyn,
2035                                                                   &(itom[i]),
2036                                                                   jtom,
2037                                                                   ktom,
2038                                                                   bc_ik|bc_ij);
2039 #ifdef STATIC_LISTS
2040                                         }
2041 #else
2042                                         } while(list_next_f(that)!=\
2043                                                 L_NO_NEXT_ELEMENT);
2044 #endif
2045
2046                                 }
2047
2048                                 }
2049
2050                                 if(moldyn->func3b_j2)
2051                                         moldyn->func3b_j2(moldyn,
2052                                                           &(itom[i]),
2053                                                           jtom,
2054                                                           bc_ij);
2055
2056                                 /* second loop over atoms k */
2057                                 if(moldyn->func3b_k2) {
2058
2059                                 for(k=0;k<27;k++) {
2060
2061                                         bc_ik=(k<dnlc)?0:1;
2062 #ifdef STATIC_LISTS
2063                                         q=0;
2064
2065                                         while(neighbour_i[j][q]!=0) {
2066
2067                                                 ktom=&(atom[neighbour_i[k][q]]);
2068                                                 q++;
2069 #else
2070                                         that=&(neighbour_i2[k]);
2071                                         list_reset_f(that);
2072                                         
2073                                         if(that->start==NULL)
2074                                                 continue;
2075
2076                                         do {
2077                                                 ktom=that->current->data;
2078 #endif
2079
2080                                                 if(!(ktom->attr&ATOM_ATTR_3BP))
2081                                                         continue;
2082
2083                                                 if(ktom==jtom)
2084                                                         continue;
2085
2086                                                 if(ktom==&(itom[i]))
2087                                                         continue;
2088
2089                                                 moldyn->func3b_k2(moldyn,
2090                                                                   &(itom[i]),
2091                                                                   jtom,
2092                                                                   ktom,
2093                                                                   bc_ik|bc_ij);
2094
2095 #ifdef STATIC_LISTS
2096                                         }
2097 #else
2098                                         } while(list_next_f(that)!=\
2099                                                 L_NO_NEXT_ELEMENT);
2100 #endif
2101
2102                                 }
2103                                 
2104                                 }
2105
2106                                 /* 2bp post function */
2107                                 if(moldyn->func3b_j3) {
2108                                         moldyn->func3b_j3(moldyn,
2109                                                           &(itom[i]),
2110                                                           jtom,bc_ij);
2111                                 }
2112 #ifdef STATIC_LISTS
2113                         }
2114 #else
2115                         } while(list_next_f(this)!=L_NO_NEXT_ELEMENT);
2116 #endif
2117                 
2118                 }
2119                 
2120 #ifdef DEBUG
2121         //printf("\n\n");
2122 #endif
2123 #ifdef VDEBUG
2124         printf("\n\n");
2125 #endif
2126
2127         }
2128
2129 #ifdef DEBUG
2130         //printf("\nATOM 0: %f %f %f\n\n",itom->f.x,itom->f.y,itom->f.z);
2131         if(moldyn->time>DSTART&&moldyn->time<DEND) {
2132                 printf("force:\n");
2133                 printf("  x: %0.40f\n",moldyn->atom[DATOM].f.x);
2134                 printf("  y: %0.40f\n",moldyn->atom[DATOM].f.y);
2135                 printf("  z: %0.40f\n",moldyn->atom[DATOM].f.z);
2136         }
2137 #endif
2138
2139         /* some postprocessing */
2140         for(i=0;i<count;i++) {
2141                 /* calculate global virial */
2142                 moldyn->gvir.xx+=itom[i].r.x*itom[i].f.x;
2143                 moldyn->gvir.yy+=itom[i].r.y*itom[i].f.y;
2144                 moldyn->gvir.zz+=itom[i].r.z*itom[i].f.z;
2145                 moldyn->gvir.xy+=itom[i].r.y*itom[i].f.x;
2146                 moldyn->gvir.xz+=itom[i].r.z*itom[i].f.x;
2147                 moldyn->gvir.yz+=itom[i].r.z*itom[i].f.y;
2148
2149                 /* check forces regarding the given timestep */
2150                 if(v3_norm(&(itom[i].f))>\
2151                    0.1*moldyn->nnd*itom[i].mass/moldyn->tau_square)
2152                         printf("[moldyn] WARNING: pfc (high force: atom %d)\n",
2153                                i);
2154         }
2155
2156         return 0;
2157 }
2158
2159 /*
2160  * virial calculation
2161  */
2162
2163 //inline int virial_calc(t_atom *a,t_3dvec *f,t_3dvec *d) {
2164 int virial_calc(t_atom *a,t_3dvec *f,t_3dvec *d) {
2165
2166         a->virial.xx+=f->x*d->x;
2167         a->virial.yy+=f->y*d->y;
2168         a->virial.zz+=f->z*d->z;
2169         a->virial.xy+=f->x*d->y;
2170         a->virial.xz+=f->x*d->z;
2171         a->virial.yz+=f->y*d->z;
2172
2173         return 0;
2174 }
2175
2176 /*
2177  * periodic boundary checking
2178  */
2179
2180 //inline int check_per_bound(t_moldyn *moldyn,t_3dvec *a) {
2181 int check_per_bound(t_moldyn *moldyn,t_3dvec *a) {
2182         
2183         double x,y,z;
2184         t_3dvec *dim;
2185
2186         dim=&(moldyn->dim);
2187
2188         x=dim->x/2;
2189         y=dim->y/2;
2190         z=dim->z/2;
2191
2192         if(moldyn->status&MOLDYN_STAT_PBX) {
2193                 if(a->x>=x) a->x-=dim->x;
2194                 else if(-a->x>x) a->x+=dim->x;
2195         }
2196         if(moldyn->status&MOLDYN_STAT_PBY) {
2197                 if(a->y>=y) a->y-=dim->y;
2198                 else if(-a->y>y) a->y+=dim->y;
2199         }
2200         if(moldyn->status&MOLDYN_STAT_PBZ) {
2201                 if(a->z>=z) a->z-=dim->z;
2202                 else if(-a->z>z) a->z+=dim->z;
2203         }
2204
2205         return 0;
2206 }
2207         
2208 /*
2209  * debugging / critical check functions
2210  */
2211
2212 int moldyn_bc_check(t_moldyn *moldyn) {
2213
2214         t_atom *atom;
2215         t_3dvec *dim;
2216         int i;
2217         double x;
2218         u8 byte;
2219         int j,k;
2220
2221         atom=moldyn->atom;
2222         dim=&(moldyn->dim);
2223         x=dim->x/2;
2224
2225         for(i=0;i<moldyn->count;i++) {
2226                 if(atom[i].r.x>=dim->x/2||-atom[i].r.x>dim->x/2) {
2227                         printf("FATAL: atom %d: x: %.20f (%.20f)\n",
2228                                i,atom[i].r.x,dim->x/2);
2229                         printf("diagnostic:\n");
2230                         printf("-----------\natom.r.x:\n");
2231                         for(j=0;j<8;j++) {
2232                                 memcpy(&byte,(u8 *)(&(atom[i].r.x))+j,1);
2233                                 for(k=0;k<8;k++)
2234                                         printf("%d%c",
2235                                         ((byte)&(1<<k))?1:0,
2236                                         (k==7)?'\n':'|');
2237                         }
2238                         printf("---------------\nx=dim.x/2:\n");
2239                         for(j=0;j<8;j++) {
2240                                 memcpy(&byte,(u8 *)(&x)+j,1);
2241                                 for(k=0;k<8;k++)
2242                                         printf("%d%c",
2243                                         ((byte)&(1<<k))?1:0,
2244                                         (k==7)?'\n':'|');
2245                         }
2246                         if(atom[i].r.x==x) printf("the same!\n");
2247                         else printf("different!\n");
2248                 }
2249                 if(atom[i].r.y>=dim->y/2||-atom[i].r.y>dim->y/2)
2250                         printf("FATAL: atom %d: y: %.20f (%.20f)\n",
2251                                i,atom[i].r.y,dim->y/2);
2252                 if(atom[i].r.z>=dim->z/2||-atom[i].r.z>dim->z/2)
2253                         printf("FATAL: atom %d: z: %.20f (%.20f)\n",
2254                                i,atom[i].r.z,dim->z/2);
2255         }
2256
2257         return 0;
2258 }
2259
2260 /*
2261  * restore function
2262  */
2263
2264 int moldyn_read_save_file(t_moldyn *moldyn,char *file) {
2265
2266         int fd;
2267         int cnt,size;
2268         int fsize;
2269         int corr;
2270
2271         fd=open(file,O_RDONLY);
2272         if(fd<0) {
2273                 perror("[moldyn] load save file open");
2274                 return fd;
2275         }
2276
2277         fsize=lseek(fd,0,SEEK_END);
2278         lseek(fd,0,SEEK_SET);
2279
2280         size=sizeof(t_moldyn);
2281
2282         while(size) {
2283                 cnt=read(fd,moldyn,size);
2284                 if(cnt<0) {
2285                         perror("[moldyn] load save file read (moldyn)");
2286                         return cnt;
2287                 }
2288                 size-=cnt;
2289         }
2290
2291         size=moldyn->count*sizeof(t_atom);
2292
2293         /* correcting possible atom data offset */
2294         corr=0;
2295         if(fsize!=sizeof(t_moldyn)+size) {
2296                 corr=fsize-sizeof(t_moldyn)-size;
2297                 printf("[moldyn] WARNING: lsf (illegal file size)\n");
2298                 printf("  moifying offset:\n");
2299                 printf("  - current pos: %d\n",sizeof(t_moldyn));
2300                 printf("  - atom size: %d\n",size);
2301                 printf("  - file size: %d\n",fsize);
2302                 printf("  => correction: %d\n",corr);
2303                 lseek(fd,corr,SEEK_CUR);
2304         }
2305
2306         moldyn->atom=(t_atom *)malloc(size);
2307         if(moldyn->atom==NULL) {
2308                 perror("[moldyn] load save file malloc (atoms)");
2309                 return -1;
2310         }
2311
2312         while(size) {
2313                 cnt=read(fd,moldyn->atom,size);
2314                 if(cnt<0) {
2315                         perror("[moldyn] load save file read (atoms)");
2316                         return cnt;
2317                 }
2318                 size-=cnt;
2319         }
2320
2321         // hooks etc ...
2322
2323         return 0;
2324 }
2325
2326 int moldyn_load(t_moldyn *moldyn) {
2327
2328         // later ...
2329
2330         return 0;
2331 }
2332
2333 /*
2334  * post processing functions
2335  */
2336
2337 int get_line(int fd,char *line,int max) {
2338
2339         int count,ret;
2340
2341         count=0;
2342
2343         while(1) {
2344                 if(count==max) return count;
2345                 ret=read(fd,line+count,1);
2346                 if(ret<=0) return ret;
2347                 if(line[count]=='\n') {
2348                         line[count]='\0';
2349                         return count+1;
2350                 }
2351                 count+=1;
2352         }
2353 }
2354
2355 int pair_correlation_init(t_moldyn *moldyn,double dr) {
2356
2357         
2358         return 0;
2359 }
2360
2361 int calculate_diffusion_coefficient(t_moldyn *moldyn,double *dc) {
2362
2363         int i;
2364         t_atom *atom;
2365         t_3dvec dist;
2366         double d2;
2367         int a_cnt;
2368         int b_cnt;
2369
2370         atom=moldyn->atom;
2371         dc[0]=0;
2372         dc[1]=0;
2373         dc[2]=0;
2374         a_cnt=0;
2375         b_cnt=0;
2376
2377         for(i=0;i<moldyn->count;i++) {
2378
2379                 v3_sub(&dist,&(atom[i].r),&(atom[i].r_0));
2380                 check_per_bound(moldyn,&dist);
2381                 d2=v3_absolute_square(&dist);
2382
2383                 if(atom[i].brand) {
2384                         b_cnt+=1;
2385                         dc[1]+=d2;
2386                 }
2387                 else {
2388                         a_cnt+=1;
2389                         dc[0]+=d2;
2390                 }
2391
2392                 dc[2]+=d2;
2393         }
2394
2395         dc[0]*=(1.0/(6.0*moldyn->time*a_cnt));
2396         dc[1]*=(1.0/(6.0*moldyn->time*b_cnt));
2397         dc[2]*=(1.0/(6.0*moldyn->time*moldyn->count));
2398                 
2399         return 0;
2400 }
2401
2402 int calculate_pair_correlation(t_moldyn *moldyn,double dr,void *ptr) {
2403
2404         int slots;
2405         double *stat;
2406         int i,j;
2407         t_linkcell *lc;
2408 #ifdef STATIC_LISTS
2409         int *neighbour[27];
2410         int p;
2411 #else
2412         t_list neighbour[27];
2413 #endif
2414         t_atom *itom,*jtom;
2415         t_list *this;
2416         unsigned char bc;
2417         t_3dvec dist;
2418         double d;
2419         double norm;
2420         int o,s;
2421         unsigned char ibrand;
2422
2423         lc=&(moldyn->lc);
2424
2425         slots=2.0*moldyn->cutoff/dr;
2426         o=2*slots;
2427
2428         if(slots*dr<=moldyn->cutoff)
2429                 printf("[moldyn] WARNING: pcc (low #slots)\n");
2430
2431         printf("[moldyn] pair correlation calc info:\n");
2432         printf("  time: %f\n",moldyn->time);
2433         printf("  count: %d\n",moldyn->count);
2434         printf("  cutoff: %f\n",moldyn->cutoff);
2435         printf("  temperature: cur=%f avg=%f\n",moldyn->t,moldyn->t_avg);
2436
2437         if(ptr!=NULL) {
2438                 stat=(double *)ptr;
2439         }
2440         else {
2441                 stat=(double *)malloc(3*slots*sizeof(double));
2442                 if(stat==NULL) {
2443                         perror("[moldyn] pair correlation malloc");
2444                         return -1;
2445                 }
2446         }
2447
2448         memset(stat,0,3*slots*sizeof(double));
2449
2450         link_cell_init(moldyn,VERBOSE);
2451
2452         itom=moldyn->atom;
2453         
2454         for(i=0;i<moldyn->count;i++) {
2455                 /* neighbour indexing */
2456                 link_cell_neighbour_index(moldyn,
2457                                           (itom[i].r.x+moldyn->dim.x/2)/lc->x,
2458                                           (itom[i].r.y+moldyn->dim.y/2)/lc->x,
2459                                           (itom[i].r.z+moldyn->dim.z/2)/lc->x,
2460                                           neighbour);
2461
2462                 /* brand of atom i */
2463                 ibrand=itom[i].brand;
2464         
2465                 for(j=0;j<27;j++) {
2466
2467                         bc=(j<lc->dnlc)?0:1;
2468
2469 #ifdef STATIC_LISTS
2470                         p=0;
2471
2472                         while(neighbour[j][p]!=0) {
2473
2474                                 jtom=&(moldyn->atom[neighbour[j][p]]);
2475                                 p++;
2476 #else
2477                         this=&(neighbour[j]);
2478                         list_reset_f(this);
2479
2480                         if(this->start==NULL)
2481                                 continue;
2482
2483                         do {
2484
2485                                 jtom=this->current->data;
2486 #endif
2487                                 /* only count pairs once,
2488                                  * skip same atoms */
2489                                 if(itom[i].tag>=jtom->tag)
2490                                         continue;
2491
2492                                 /*
2493                                  * pair correlation calc
2494                                  */
2495
2496                                 /* distance */
2497                                 v3_sub(&dist,&(jtom->r),&(itom[i].r));
2498                                 if(bc) check_per_bound(moldyn,&dist);
2499                                 d=v3_absolute_square(&dist);
2500
2501                                 /* ignore if greater or equal cutoff */
2502                                 if(d>=4.0*moldyn->cutoff_square)
2503                                         continue;
2504
2505                                 /* fill the slots */
2506                                 d=sqrt(d);
2507                                 s=(int)(d/dr);
2508
2509                                 /* should never happen but it does 8) -
2510                                  * related to -ffloat-store problem! */
2511                                 if(s>=slots) {
2512                                         printf("[moldyn] WARNING: pcc (%d/%d)",
2513                                                s,slots);
2514                                         printf("\n");
2515                                         s=slots-1;
2516                                 }
2517
2518                                 if(ibrand!=jtom->brand) {
2519                                         /* mixed */
2520                                         stat[s]+=1;
2521                                 }
2522                                 else {
2523                                         /* type a - type a bonds */
2524                                         if(ibrand==0)
2525                                                 stat[s+slots]+=1;
2526                                         else
2527                                         /* type b - type b bonds */
2528                                                 stat[s+o]+=1;
2529                                 }
2530 #ifdef STATIC_LISTS
2531                         }
2532 #else
2533                         } while(list_next_f(this)!=L_NO_NEXT_ELEMENT);
2534 #endif
2535                 }
2536         }
2537
2538         /* normalization */
2539         for(i=1;i<slots;i++) {
2540                  // normalization: 4 pi r^2 dr
2541                  // here: not double counting pairs -> 2 pi r r dr
2542                  // ... and actually it's a constant times r^2
2543                 norm=i*i*dr*dr;
2544                 stat[i]/=norm;
2545                 stat[slots+i]/=norm;
2546                 stat[o+i]/=norm;
2547         }
2548         /* */
2549
2550         if(ptr==NULL) {
2551                 /* todo: store/print pair correlation function */
2552                 free(stat);
2553         }
2554
2555         free(moldyn->atom);
2556
2557         link_cell_shutdown(moldyn);
2558
2559         return 0;
2560 }
2561
2562 int analyze_bonds(t_moldyn *moldyn) {
2563
2564         
2565         
2566
2567         return 0;
2568 }
2569
2570 /*
2571  * visualization code
2572  */
2573
2574 int visual_init(t_moldyn *moldyn,char *filebase) {
2575
2576         strncpy(moldyn->vis.fb,filebase,128);
2577
2578         return 0;
2579 }
2580
2581 int visual_atoms(t_moldyn *moldyn) {
2582
2583         int i,j,fd;
2584         char file[128+64];
2585         t_3dvec dim;
2586         double help;
2587         t_visual *v;
2588         t_atom *atom;
2589         t_atom *btom;
2590         t_linkcell *lc;
2591 #ifdef STATIC_LISTS
2592         int *neighbour[27];
2593         int p;
2594 #else
2595         t_list neighbour[27];
2596 #endif
2597         u8 bc;
2598         t_3dvec dist;
2599         double d2;
2600         u8 brand;
2601
2602         v=&(moldyn->vis);
2603         dim.x=v->dim.x;
2604         dim.y=v->dim.y;
2605         dim.z=v->dim.z;
2606         atom=moldyn->atom;
2607         lc=&(moldyn->lc);
2608
2609         help=(dim.x+dim.y);
2610
2611         sprintf(file,"%s/atomic_conf_%07.f.xyz",v->fb,moldyn->time);
2612         fd=open(file,O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR);
2613         if(fd<0) {
2614                 perror("open visual save file fd");
2615                 return -1;
2616         }
2617
2618         /* write the actual data file */
2619
2620         // povray header
2621         dprintf(fd,"# [P] %d %07.f <%f,%f,%f>\n",
2622                 moldyn->count,moldyn->time,help/40.0,help/40.0,-0.8*help);
2623
2624         // atomic configuration
2625         for(i=0;i<moldyn->count;i++) {
2626                 // atom type, positions, color and kinetic energy
2627                 dprintf(fd,"%s %f %f %f %s %f\n",pse_name[atom[i].element],
2628                                                  atom[i].r.x,
2629                                                  atom[i].r.y,
2630                                                  atom[i].r.z,
2631                                                  pse_col[atom[i].element],
2632                                                  atom[i].ekin);
2633
2634                 /*
2635                  * bond detection should usually be done by potential
2636                  * functions. brrrrr! EVIL!
2637                  * 
2638                  * todo: potentials need to export a 'find_bonds' function!
2639                  */
2640
2641                 // bonds between atoms
2642                 if(!(atom[i].attr&ATOM_ATTR_VB))
2643                         continue;
2644                 link_cell_neighbour_index(moldyn,
2645                                           (atom[i].r.x+moldyn->dim.x/2)/lc->x,
2646                                           (atom[i].r.y+moldyn->dim.y/2)/lc->y,
2647                                           (atom[i].r.z+moldyn->dim.z/2)/lc->z,
2648                                           neighbour);
2649                 for(j=0;j<27;j++) {
2650                         bc=j<lc->dnlc?0:1;
2651 #ifdef STATIC_LISTS
2652                         p=0;
2653                         while(neighbour[j][p]!=0) {
2654                                 btom=&(atom[neighbour[j][p]]);
2655                                 p++;
2656 #else
2657                         list_reset_f(&neighbour[j]);
2658                         if(neighbour[j].start==NULL)
2659                                 continue;
2660                         do {
2661                                 btom=neighbour[j].current->data;
2662 #endif
2663                                 if(btom==&atom[i])      // skip identical atoms
2664                                         continue;
2665                                 //if(btom<&atom[i])     // skip half of them
2666                                 //      continue;
2667                                 v3_sub(&dist,&(atom[i].r),&(btom->r));
2668                                 if(bc) check_per_bound(moldyn,&dist);
2669                                 d2=v3_absolute_square(&dist);
2670                                 brand=atom[i].brand;
2671                                 if(brand==btom->brand) {
2672                                         if(d2>moldyn->bondlen[brand])
2673                                                 continue;
2674                                 }
2675                                 else {
2676                                         if(d2>moldyn->bondlen[2])
2677                                                 continue;
2678                                 }
2679                                 dprintf(fd,"# [B] %f %f %f %f %f %f\n",
2680                                         atom[i].r.x,atom[i].r.y,atom[i].r.z,
2681                                         btom->r.x,btom->r.y,btom->r.z);
2682 #ifdef STATIC_LISTS
2683                         }
2684 #else
2685                         } while(list_next_f(&neighbour[j])!=L_NO_NEXT_ELEMENT);
2686 #endif
2687                 }
2688         }
2689
2690         // boundaries
2691         if(dim.x) {
2692                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2693                         -dim.x/2,-dim.y/2,-dim.z/2,
2694                         dim.x/2,-dim.y/2,-dim.z/2);
2695                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2696                         -dim.x/2,-dim.y/2,-dim.z/2,
2697                         -dim.x/2,dim.y/2,-dim.z/2);
2698                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2699                         dim.x/2,dim.y/2,-dim.z/2,
2700                         dim.x/2,-dim.y/2,-dim.z/2);
2701                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2702                         -dim.x/2,dim.y/2,-dim.z/2,
2703                         dim.x/2,dim.y/2,-dim.z/2);
2704
2705                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2706                         -dim.x/2,-dim.y/2,dim.z/2,
2707                         dim.x/2,-dim.y/2,dim.z/2);
2708                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2709                         -dim.x/2,-dim.y/2,dim.z/2,
2710                         -dim.x/2,dim.y/2,dim.z/2);
2711                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2712                         dim.x/2,dim.y/2,dim.z/2,
2713                         dim.x/2,-dim.y/2,dim.z/2);
2714                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2715                         -dim.x/2,dim.y/2,dim.z/2,
2716                         dim.x/2,dim.y/2,dim.z/2);
2717
2718                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2719                         -dim.x/2,-dim.y/2,dim.z/2,
2720                         -dim.x/2,-dim.y/2,-dim.z/2);
2721                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2722                         -dim.x/2,dim.y/2,dim.z/2,
2723                         -dim.x/2,dim.y/2,-dim.z/2);
2724                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2725                         dim.x/2,-dim.y/2,dim.z/2,
2726                         dim.x/2,-dim.y/2,-dim.z/2);
2727                 dprintf(fd,"# [D] %f %f %f %f %f %f\n",
2728                         dim.x/2,dim.y/2,dim.z/2,
2729                         dim.x/2,dim.y/2,-dim.z/2);
2730         }
2731
2732         close(fd);
2733
2734         return 0;
2735 }
2736