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