+ double temp2,ighc;
+
+ /* averages needed for heat capacity calc */
+ if(moldyn->total_steps<moldyn->avg_skip)
+ return 0;
+
+ /* (temperature average)^2 */
+ temp2=moldyn->t_avg*moldyn->t_avg;
+ printf("[moldyn] specific heat capacity for T=%f K [J/(kg K)]\n",
+ moldyn->t_avg);
+
+ /* ideal gas contribution */
+ ighc=3.0*moldyn->count*K_BOLTZMANN/2.0;
+ printf(" ideal gas contribution: %f\n",
+ ighc/moldyn->mass*KILOGRAM/JOULE);
+
+ /* specific heat for nvt ensemble */
+ moldyn->c_v_nvt=moldyn->dv2_avg/(K_BOLTZMANN*temp2)+ighc;
+ moldyn->c_v_nvt/=moldyn->mass;
+
+ /* specific heat for nve ensemble */
+ moldyn->c_v_nve=ighc/(1.0-(moldyn->dv2_avg/(ighc*K_BOLTZMANN*temp2)));
+ moldyn->c_v_nve/=moldyn->mass;
+
+ printf(" NVE: %f\n",moldyn->c_v_nve*KILOGRAM/JOULE);
+ printf(" NVT: %f\n",moldyn->c_v_nvt*KILOGRAM/JOULE);
+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)));
+
+ return 0;
+}
+
+double thermodynamic_pressure_calc(t_moldyn *moldyn) {
+
+ t_3dvec dim;
+ //t_3dvec *tp;
+ double h,dv;
+ double y0,y1;
+ double su,sd;
+ t_atom *store;
+
+ /*
+ * dU = - p dV
+ *
+ * => p = - dU/dV
+ *
+ */
+
+ /* store atomic configuration + dimension */
+ store=malloc(moldyn->count*sizeof(t_atom));
+ if(store==NULL) {
+ printf("[moldyn] allocating store mem failed\n");
+ return -1;
+ }
+ memcpy(store,moldyn->atom,moldyn->count*sizeof(t_atom));
+ dim=moldyn->dim;
+
+ /* x1, y1 */
+ sd=0.00001;
+ h=(1.0-sd)*(1.0-sd)*(1.0-sd);
+ su=pow(2.0-h,ONE_THIRD)-1.0;
+ dv=(1.0-h)*moldyn->volume;
+
+ /* scale up dimension and atom positions */
+ scale_dim(moldyn,SCALE_UP,su,TRUE,TRUE,TRUE);
+ scale_atoms(moldyn,SCALE_UP,su,TRUE,TRUE,TRUE);
+ link_cell_shutdown(moldyn);
+ link_cell_init(moldyn,QUIET);
+ potential_force_calc(moldyn);
+ y1=moldyn->energy;
+
+ /* restore atomic configuration + dim */
+ memcpy(moldyn->atom,store,moldyn->count*sizeof(t_atom));
+ moldyn->dim=dim;
+
+ /* scale down dimension and atom positions */
+ scale_dim(moldyn,SCALE_DOWN,sd,TRUE,TRUE,TRUE);
+ scale_atoms(moldyn,SCALE_DOWN,sd,TRUE,TRUE,TRUE);
+ link_cell_shutdown(moldyn);
+ link_cell_init(moldyn,QUIET);
+ potential_force_calc(moldyn);
+ y0=moldyn->energy;
+
+ /* calculate pressure */
+ moldyn->tp=-(y1-y0)/(2.0*dv);
+
+ /* restore atomic configuration */
+ memcpy(moldyn->atom,store,moldyn->count*sizeof(t_atom));
+ moldyn->dim=dim;
+ link_cell_shutdown(moldyn);
+ link_cell_init(moldyn,QUIET);
+ //potential_force_calc(moldyn);
+
+ /* free store buffer */
+ if(store)
+ free(store);
+
+ return moldyn->tp;
+}
+
+double get_pressure(t_moldyn *moldyn) {
+
+ return moldyn->p;
+
+}
+
+int scale_dim(t_moldyn *moldyn,u8 dir,double scale,u8 x,u8 y,u8 z) {
+
+ t_3dvec *dim;
+
+ dim=&(moldyn->dim);
+
+ if(dir==SCALE_UP)
+ scale=1.0+scale;
+
+ if(dir==SCALE_DOWN)
+ scale=1.0-scale;
+
+ if(x) dim->x*=scale;
+ if(y) dim->y*=scale;
+ if(z) dim->z*=scale;
+
+ return 0;
+}
+
+int scale_atoms(t_moldyn *moldyn,u8 dir,double scale,u8 x,u8 y,u8 z) {
+
+ int i;
+ t_3dvec *r;
+
+ if(dir==SCALE_UP)
+ scale=1.0+scale;
+
+ if(dir==SCALE_DOWN)
+ scale=1.0-scale;
+
+ for(i=0;i<moldyn->count;i++) {
+ r=&(moldyn->atom[i].r);
+ if(x) r->x*=scale;
+ if(y) r->y*=scale;
+ if(z) r->z*=scale;
+ }
+
+ return 0;
+}
+
+int scale_atoms_ind(t_moldyn *moldyn,double x,double y,double z) {
+
+ int i;
+ t_3dvec *r;
+
+ for(i=0;i<moldyn->count;i++) {
+ r=&(moldyn->atom[i].r);
+ r->x*=x;
+ r->y*=y;
+ r->z*=z;
+ }
+
+ return 0;
+}
+
+int scale_dim_ind(t_moldyn *moldyn,double x,double y,double z) {
+
+ t_3dvec *dim;
+
+ dim=&(moldyn->dim);
+
+ dim->x*=x;
+ dim->y*=y;
+ dim->z*=z;
+
+ return 0;
+}
+
+int scale_volume(t_moldyn *moldyn) {
+
+ t_3dvec *dim,*vdim;
+ double scale;
+ t_linkcell *lc;
+ //double sx,sy,sz;
+
+ vdim=&(moldyn->vis.dim);
+ dim=&(moldyn->dim);
+ lc=&(moldyn->lc);
+
+ /* scaling factor */
+ if(moldyn->pt_scale&P_SCALE_BERENDSEN) {
+ scale=1.0-(moldyn->p_ref-moldyn->p)*moldyn->p_tc*moldyn->tau;
+ scale=pow(scale,ONE_THIRD);
+ }
+ else {
+ scale=pow(moldyn->p/moldyn->p_ref,ONE_THIRD);
+ }
+
+
+ /*
+ sx=1.0-(moldyn->p_ref-moldyn->px)*moldyn->p_tc*moldyn->tau;
+ sy=1.0-(moldyn->p_ref-moldyn->py)*moldyn->p_tc*moldyn->tau;
+ sz=1.0-(moldyn->p_ref-moldyn->pz)*moldyn->p_tc*moldyn->tau;
+ sx=pow(sx,ONE_THIRD);
+ sy=pow(sy,ONE_THIRD);
+ sz=pow(sz,ONE_THIRD);
+ */
+
+ /* scale the atoms and dimensions */
+ scale_atoms(moldyn,SCALE_DIRECT,scale,TRUE,TRUE,TRUE);
+ scale_dim(moldyn,SCALE_DIRECT,scale,TRUE,TRUE,TRUE);
+ //scale_atoms_ind(moldyn,sx,sy,sz);
+ //scale_dim_ind(moldyn,sx,sy,sz);
+
+ /* visualize dimensions */
+ if(vdim->x!=0) {
+ vdim->x=dim->x;
+ vdim->y=dim->y;
+ vdim->z=dim->z;
+ }
+
+ /* recalculate scaled volume */
+ moldyn->volume=dim->x*dim->y*dim->z;
+
+ /* adjust/reinit linkcell */
+ if(((int)(dim->x/moldyn->cutoff)!=lc->nx)||
+ ((int)(dim->y/moldyn->cutoff)!=lc->ny)||
+ ((int)(dim->z/moldyn->cutoff)!=lc->nx)) {
+ link_cell_shutdown(moldyn);
+ link_cell_init(moldyn,QUIET);
+ } else {
+ lc->x*=scale;
+ lc->y*=scale;
+ lc->z*=scale;
+ //lc->x*=sx;
+ //lc->y*=sx;
+ //lc->z*=sy;
+ }
+
+ return 0;
+
+}
+
+double e_kin_calc(t_moldyn *moldyn) {
+
+ int i;
+ t_atom *atom;
+
+ atom=moldyn->atom;
+ moldyn->ekin=0.0;
+ //moldyn->ekinx=0.0;
+ //moldyn->ekiny=0.0;
+ //moldyn->ekinz=0.0;
+
+ for(i=0;i<moldyn->count;i++) {
+ atom[i].ekin=0.5*atom[i].mass*v3_absolute_square(&(atom[i].v));
+ moldyn->ekin+=atom[i].ekin;
+ //moldyn->ekinx+=0.5*atom[i].mass*atom[i].v.x*atom[i].v.x;
+ //moldyn->ekiny+=0.5*atom[i].mass*atom[i].v.y*atom[i].v.y;
+ //moldyn->ekinz+=0.5*atom[i].mass*atom[i].v.z*atom[i].v.z;
+ }
+
+ return moldyn->ekin;
+}
+
+double get_total_energy(t_moldyn *moldyn) {
+
+ return(moldyn->ekin+moldyn->energy);
+}
+
+t_3dvec get_total_p(t_moldyn *moldyn) {
+
+ t_3dvec p,p_total;
+ int i;
+ t_atom *atom;
+
+ atom=moldyn->atom;
+
+ v3_zero(&p_total);
+ for(i=0;i<moldyn->count;i++) {
+ v3_scale(&p,&(atom[i].v),atom[i].mass);
+ v3_add(&p_total,&p_total,&p);
+ }
+
+ return p_total;
+}
+
+double estimate_time_step(t_moldyn *moldyn,double nn_dist) {
+
+ double tau;
+
+ /* nn_dist is the nearest neighbour distance */
+
+ tau=(0.05*nn_dist*moldyn->atom[0].mass)/sqrt(3.0*K_BOLTZMANN*moldyn->t);
+
+ return tau;
+}
+
+/*
+ * numerical tricks
+ */
+
+/* linked list / cell method */
+
+int link_cell_init(t_moldyn *moldyn,u8 vol) {
+
+ t_linkcell *lc;
+#ifndef LOWMEM_LISTS
+ int i;
+#endif
+
+ lc=&(moldyn->lc);
+
+ /* partitioning the md cell */
+ lc->nx=moldyn->dim.x/moldyn->cutoff;
+ lc->x=moldyn->dim.x/lc->nx;
+ lc->ny=moldyn->dim.y/moldyn->cutoff;
+ lc->y=moldyn->dim.y/lc->ny;
+ lc->nz=moldyn->dim.z/moldyn->cutoff;
+ lc->z=moldyn->dim.z/lc->nz;
+ lc->cells=lc->nx*lc->ny*lc->nz;
+
+#ifdef STATIC_LISTS
+ lc->subcell=malloc(lc->cells*sizeof(int*));
+#elif LOWMEM_LISTS
+ lc->subcell=malloc(sizeof(t_lowmem_list));
+#else
+ lc->subcell=malloc(lc->cells*sizeof(t_list));
+#endif
+
+ if(lc->subcell==NULL) {
+ perror("[moldyn] cell init (malloc)");
+ return -1;
+ }
+
+ if(lc->cells<27)
+ printf("[moldyn] FATAL: less then 27 subcells! (%d)\n",
+ lc->cells);
+
+ if(vol) {
+#ifdef STATIC_LISTS
+ printf("[moldyn] initializing 'static' linked cells (%d)\n",
+ lc->cells);
+#elif LOWMEM_LISTS
+ printf("[moldyn] initializing 'lowmem' linked cells (%d)\n",
+ lc->cells);
+#else
+ printf("[moldyn] initializing 'dynamic' linked cells (%d)\n",
+ lc->cells);
+#endif
+ printf(" x: %d x %f A\n",lc->nx,lc->x);
+ printf(" y: %d x %f A\n",lc->ny,lc->y);
+ printf(" z: %d x %f A\n",lc->nz,lc->z);
+ }
+
+#ifdef STATIC_LISTS
+ /* list init */
+ for(i=0;i<lc->cells;i++) {
+ lc->subcell[i]=malloc((MAX_ATOMS_PER_LIST+1)*sizeof(int));
+ if(lc->subcell[i]==NULL) {
+ perror("[moldyn] list init (malloc)");
+ return -1;
+ }
+ /*
+ if(i==0)
+ printf(" ---> %d malloc %p (%p)\n",
+ i,lc->subcell[0],lc->subcell);
+ */
+ }
+#elif LOWMEM_LISTS
+ lc->subcell->head=malloc(lc->cells*sizeof(int));
+ if(lc->subcell->head==NULL) {
+ perror("[moldyn] head init (malloc)");
+ return -1;
+ }
+ lc->subcell->list=malloc(moldyn->count*sizeof(int));
+ if(lc->subcell->list==NULL) {
+ perror("[moldyn] list init (malloc)");
+ return -1;
+ }
+#else
+ for(i=0;i<lc->cells;i++)
+ list_init_f(&(lc->subcell[i]));
+#endif
+
+ /* update the list */
+ link_cell_update(moldyn);
+
+ return 0;
+}
+
+int link_cell_update(t_moldyn *moldyn) {
+
+ int count,i,j,k;
+ int nx,nxy;
+ t_atom *atom;
+ t_linkcell *lc;
+#ifdef STATIC_LISTS
+ int p;
+#elif LOWMEM_LISTS
+ int p;
+#endif
+
+ atom=moldyn->atom;
+ lc=&(moldyn->lc);
+
+ nx=lc->nx;
+ nxy=nx*lc->ny;
+
+ for(i=0;i<lc->cells;i++)
+#ifdef STATIC_LISTS
+ memset(lc->subcell[i],-1,(MAX_ATOMS_PER_LIST+1)*sizeof(int));
+#elif LOWMEM_LISTS
+ lc->subcell->head[i]=-1;
+#else
+ list_destroy_f(&(lc->subcell[i]));
+#endif
+
+ for(count=0;count<moldyn->count;count++) {
+ i=((atom[count].r.x+(moldyn->dim.x/2))/lc->x);
+ j=((atom[count].r.y+(moldyn->dim.y/2))/lc->y);
+ k=((atom[count].r.z+(moldyn->dim.z/2))/lc->z);
+
+#ifdef STATIC_LISTS
+ p=0;
+ while(lc->subcell[i+j*nx+k*nxy][p]!=-1)
+ p++;
+
+ if(p>=MAX_ATOMS_PER_LIST) {
+ printf("[moldyn] FATAL: amount of atoms too high!\n");
+ return -1;
+ }
+
+ lc->subcell[i+j*nx+k*nxy][p]=count;
+#elif LOWMEM_LISTS
+ p=i+j*nx+k*nxy;
+ lc->subcell->list[count]=lc->subcell->head[p];
+ lc->subcell->head[p]=count;
+#else
+ list_add_immediate_f(&(lc->subcell[i+j*nx+k*nxy]),
+ &(atom[count]));
+ /*
+ if(j==0&&k==0)
+ printf(" ---> %d %d malloc %p (%p)\n",
+ i,count,lc->subcell[i].current,lc->subcell);
+ */
+#endif
+ }
+
+ return 0;
+}
+
+int link_cell_neighbour_index(t_moldyn *moldyn,int i,int j,int k,
+#ifdef STATIC_LISTS
+ int **cell
+#elif LOWMEM_LISTS
+ int *cell
+#else
+ t_list *cell
+#endif
+ ) {
+
+ t_linkcell *lc;