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