void do_neighbour_tables(cell *p, cell *q, vektor pbc) { int i, j; int jstart; int q_typ, p_typ; vektor d, tmp_d; real radius; /* For each atom in first cell */ for (i=0; i<p->n; ++i) { /* Compute only neighbour tables for existing atoms */ if ( p->del[i] == 0 ) { /* All atoms are white */ p->color[i] = -1; tmp_d.x = p->ort[i].x - pbc.x; tmp_d.y = p->ort[i].y - pbc.y; #ifndef TWOD tmp_d.z = p->ort[i].z - pbc.z; #endif p_typ = p->sorte[i]; jstart = (p==q ? i+1 : 0); /* For each atom in neighbouring cell */ for (j = jstart; j < q->n; ++j) { if ( q->del[j] == 0 ) { q_typ = q->sorte[j]; /* Calculate distance */ d.x = q->ort[j].x - tmp_d.x; d.y = q->ort[j].y - tmp_d.y; #ifndef TWOD d.z = q->ort[j].z - tmp_d.z; #endif radius = sqrt(SPROD(d,d)); /* Make neighbour tables */ #ifdef TERSOFF if (radius <= ter_r_cut[p_typ][q_typ]) #else if (radius <= r_max) #endif { neightab *neigh; /* Update neighbour table of particle i */ if ( first == 0 ) neigh = &p->neightab_array[i]; else neigh = &p->perm_neightab_array[i]; if (neigh->n_max <= neigh->n ) { error("Neighbour table too small, increase neigh_len"); } neigh->typ[neigh->n] = q_typ; neigh->cl [neigh->n] = q; neigh->num[neigh->n] = j; neigh->n++; /* Update neighbour table of particle j */ if ( first == 0 ) neigh = &q->neightab_array[j]; else neigh = &q->perm_neightab_array[j]; if (neigh->n_max <= neigh->n ) { error("Neighbour table too small, increase neigh_len"); } neigh->typ[neigh->n] = p_typ; neigh->cl [neigh->n] = p; neigh->num[neigh->n] = i; neigh->n++; } } } /* for j */ } } /* for i */ }
void calc_extpot(void) { int k, i, n; int isinx,isiny,isinz; real tmpvec1[4], tmpvec2[4]; vektor d,addforce,totaddforce; real dd,cc; real dn,ddn,ee; vektor force; real tmp_virial; #ifdef P_AXIAL vektor tmp_vir_vect; #endif real pot_zwi, pot_grad; int col, is_short=0; for (k=0; k<ep_n; k++) { ep_fext[k] = 0.0; ep_xmax[k] = 0.0; ep_ymax[k] = 0.0; ep_atomsincontact[k]=0; ep_xmin[k] = 1.e8; ep_ymin[k] = 1.e8; } if(ep_key == 0) { /* default: original harmonic potential */ for (k=0; k<NCELLS; ++k) { cell *p = CELLPTR(k); for (i=0; i<p->n; ++i) { for (n=0; n<ep_n; ++n) { isinx= ep_dir[n].x; isiny= ep_dir[n].y; isinz= ep_dir[n].z; d.x = ep_pos[n].x - ORT(p,i,X); d.y = ep_pos[n].y - ORT(p,i,Y); d.z = ep_pos[n].z - ORT(p,i,Z); dn = SPROD(d,ep_dir[n]); /* spherical indentor*/ if (n<ep_nind) { if (dn > -ep_rcut) { real d2 = SPROD(d,d); real d1 = SQRT(d2); dd = ep_rcut - d1; if (dd > 0.0) { real f = ep_a * dd * dd / d1; /* force on atoms and indentor */ KRAFT(p,i,X) -= f * d.x; KRAFT(p,i,Y) -= f * d.y; KRAFT(p,i,Z) -= f * d.z; ep_fext[n] += f * ABS(dn); /* normal force on indentor */ ep_atomsincontact[n]++; /* for determination of contact area */ if(isinz) { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,X) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Y) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,X) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Y) ); } else if(isiny) { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,X) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Z) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,X) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Z) ); } else { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,Y) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Z) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,Y) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Z) ); } } } } /* potential wall */ else { if (dn*dn < ep_rcut*ep_rcut) { real d1 = (dn>0) ? dn : -1*dn ; dd = ep_rcut - d1; if (dd > 0.0) { ep_atomsincontact[n]++; real f = ep_a * dd * dd / d1; /* force on atoms and indentor */ KRAFT(p,i,X) += f * ep_dir[n].x; KRAFT(p,i,Y) += f * ep_dir[n].y; KRAFT(p,i,Z) += f * ep_dir[n].z; ep_fext[n] += f; /* magnitude of force on wall */ } } } } } } } else if(ep_key == 1) /* Ju Li's spherical indenter, see PRB 67, 104105 */ { totaddforce.x=0.0; totaddforce.y=0.0; totaddforce.z=0.0; for (k=0; k<NCELLS; ++k) { cell *p = CELLPTR(k); for (i=0; i<p->n; ++i) { for (n=0; n<ep_n; ++n) { isinx= ep_dir[n].x; isiny= ep_dir[n].y; isinz= ep_dir[n].z; if (n<ep_nind) { d.x = ORT(p,i,X)-ep_pos[n].x; d.y = ORT(p,i,Y)-ep_pos[n].y; d.z = ORT(p,i,Z)-ep_pos[n].z; dn = SPROD(d,ep_dir[n]); dd = SPROD(d,d); if ( dd < ep_rcut*ep_rcut) { ep_atomsincontact[n]++; /* for the determination of the contact area */ if(isinz) { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,X) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Y) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,X) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Y) ); } else if(isiny) { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,X) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Z) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,X) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Z) ); } else { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,Y) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Z) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,Y) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Z) ); } if(have_extpotfile == 1){ PAIR_INT3(pot_zwi, pot_grad, ext_pot, n, ep_nind, dd, is_short); tot_pot_energy += pot_zwi; force.x = -1.0* pot_grad * d.x; force.y = -1.0* pot_grad * d.y; force.z = -1.0* pot_grad * d.z; KRAFT(p,i,X) += force.x; KRAFT(p,i,Y) += force.y; KRAFT(p,i,Z) += force.z; totaddforce.x += force.x; totaddforce.y += force.y; totaddforce.z += force.z; ep_fext[n] += -pot_grad * ABS(dn); /* normal force on indentor */ #ifdef P_AXIAL tmp_vir_vect.x -= d.x * force.x; tmp_vir_vect.y -= d.y * force.y; #ifndef TWOD tmp_vir_vect.z -= d.z * force.z; #endif #else tmp_virial -= dd * pot_grad; #endif #ifdef STRESS_TENS if (do_press_calc) { PRESSTENS(p,i,xx) -= d.x * force.x; PRESSTENS(p,i,yy) -= d.y * force.y; PRESSTENS(p,i,xy) -= d.x * force.y; #ifndef TWOD PRESSTENS(p,i,zz) -= d.z * force.z; PRESSTENS(p,i,yz) -= d.y * force.z; PRESSTENS(p,i,zx) -= d.z * force.x; #endif } #endif } /* old version of extpot, kept for downwards compatibility */ else{ ddn= sqrt(dd); cc = (ep_rcut - ddn)/ep_a; if (cc > UPPER_EXP) cc = UPPER_EXP; if (cc < LOWER_EXP) cc = LOWER_EXP; ee = exp(cc - 1.0/cc); tot_pot_energy += ee; POTENG(p,i) += ee; ee = ee / ep_a / ddn * (1.0 + 1.0 /(cc*cc)); KRAFT(p,i,X) += ee * d.x; KRAFT(p,i,Y) += ee * d.y; KRAFT(p,i,Z) += ee * d.z; totaddforce.x += ee * d.x; totaddforce.y += ee * d.y; totaddforce.z += ee * d.z; ep_fext[n] += ee * ABS(dn); /* normal force on indentor */ } } } } } } #ifdef MPI tmpvec1[0] = totaddforce.x ; tmpvec1[1] = totaddforce.y ; tmpvec1[2] = totaddforce.z ; // printf("before totaddforcereduce allreduce\n");fflush(stdout); MPI_Allreduce( tmpvec1, tmpvec2, 4, REAL, MPI_SUM, cpugrid); // printf("after totaddforce allreduce\n");fflush(stdout); totaddforce.x = tmpvec2[0]; totaddforce.y = tmpvec2[1]; totaddforce.z = tmpvec2[2]; #endif /* no need for a wall as the total additional impuls is substracted */ totaddforce.x *= 1.0/nactive_vect[0]; totaddforce.y *= 1.0/nactive_vect[1]; totaddforce.z *= 1.0/nactive_vect[2]; for (k=0; k<NCELLS; ++k) { cell *p = CELLPTR(k); for (i=0; i<p->n; ++i) { KRAFT(p,i,X) -= totaddforce.x; KRAFT(p,i,Y) -= totaddforce.y; KRAFT(p,i,Z) -= totaddforce.z; } } } else if(ep_key == 2) /* Ju Li's spherical indenter made flat, see PRB 67, 104105 with subtraction of total additional impulse works only with indentation directions parallel to box vectors*/ { // vektor d,addforce,totaddforce; //real dd,cc; //real dn,ddn,ee; totaddforce.x=0.0; totaddforce.y=0.0; totaddforce.z=0.0; for (k=0; k<NCELLS; ++k) { cell *p = CELLPTR(k); for (i=0; i<p->n; ++i) { for (n=0; n<ep_n; ++n) { isinx= ep_dir[n].x; isiny= ep_dir[n].y; isinz= ep_dir[n].z; // vektor d; // real dn; d.x = (ep_dir[n].x==0) ? 0 : (ORT(p,i,X)-ep_pos[n].x); d.y = (ep_dir[n].y==0) ? 0 : (ORT(p,i,Y)-ep_pos[n].y); d.z = (ep_dir[n].z==0) ? 0 : (ORT(p,i,Z)-ep_pos[n].z); dn = SPROD(d,ep_dir[n]); dd = SPROD(d,d); if ( dd < ep_rcut*ep_rcut) { /* for the determination of the contact area */ ep_atomsincontact[n]++; if(isinz) { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,X) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Y) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,X) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Y) ); } else if(isiny) { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,X) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Z) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,X) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Z) ); } else { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,Y) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Z) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,Y) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Z) ); } if(have_extpotfile == 1){ PAIR_INT3(pot_zwi, pot_grad, ext_pot, n, ep_nind, dd, is_short); tot_pot_energy += pot_zwi; force.x = -1.0* pot_grad * d.x; force.y = -1.0* pot_grad * d.y; force.z = -1.0* pot_grad * d.z; KRAFT(p,i,X) += force.x; KRAFT(p,i,Y) += force.y; KRAFT(p,i,Z) += force.z; totaddforce.x += force.x; totaddforce.y += force.y; totaddforce.z += force.z; ep_fext[n] += -pot_grad * ABS(dn); /* normal force on indentor */ #ifdef P_AXIAL tmp_vir_vect.x -= d.x * force.x; tmp_vir_vect.y -= d.y * force.y; #ifndef TWOD tmp_vir_vect.z -= d.z * force.z; #endif #else tmp_virial -= dd * pot_grad; #endif #ifdef STRESS_TENS if (do_press_calc) { PRESSTENS(p,i,xx) -= d.x * force.x; PRESSTENS(p,i,yy) -= d.y * force.y; PRESSTENS(p,i,xy) -= d.x * force.y; #ifndef TWOD PRESSTENS(p,i,zz) -= d.z * force.z; PRESSTENS(p,i,yz) -= d.y * force.z; PRESSTENS(p,i,zx) -= d.z * force.x; #endif } #endif } /* old version of extpot, kept for downwards compatibility */ else{ ddn= sqrt(dd); cc = (ep_rcut - ddn)/ep_a; if (cc > UPPER_EXP) cc = UPPER_EXP; if (cc < LOWER_EXP) cc = LOWER_EXP; ee = exp(cc - 1.0/cc); tot_pot_energy += ee; POTENG(p,i) += ee; ee = ee / ep_a / ddn * (1.0 + 1.0 /(cc*cc)); KRAFT(p,i,X) += ee * d.x; KRAFT(p,i,Y) += ee * d.y; KRAFT(p,i,Z) += ee * d.z; totaddforce.x += ee * d.x; totaddforce.y += ee * d.y; totaddforce.z += ee * d.z; ep_fext[n] += ee * ABS(dn); /* normal force on indentor */ } } } } } #ifdef MPI tmpvec1[0] = totaddforce.x ; tmpvec1[1] = totaddforce.y ; tmpvec1[2] = totaddforce.z ; // printf("before totaddforcereduce allreduce\n");fflush(stdout); MPI_Allreduce( tmpvec1, tmpvec2, 4, REAL, MPI_SUM, cpugrid); // printf("after totaddforce allreduce\n");fflush(stdout); totaddforce.x = tmpvec2[0]; totaddforce.y = tmpvec2[1]; totaddforce.z = tmpvec2[2]; #endif /* no need for a wall as the total additional impuls is substracted */ totaddforce.x *= 1.0/nactive_vect[0]; totaddforce.y *= 1.0/nactive_vect[1]; totaddforce.z *= 1.0/nactive_vect[2]; for (k=0; k<NCELLS; ++k) { cell *p = CELLPTR(k); for (i=0; i<p->n; ++i) { KRAFT(p,i,X) -= totaddforce.x; KRAFT(p,i,Y) -= totaddforce.y; KRAFT(p,i,Z) -= totaddforce.z; } } } else if(ep_key == 3) /* Ju Li's spherical indenter made flat, see PRB 67, 104105 without subtraction of the total additional impulse works only with indentation directions parallel to box vectors*/ { // vektor d,addforce,totaddforce; // real dd,cc; // real dn,ddn,ee; totaddforce.x=0.0; totaddforce.y=0.0; totaddforce.z=0.0; for (k=0; k<NCELLS; ++k) { cell *p = CELLPTR(k); for (i=0; i<p->n; ++i) { for (n=0; n<ep_n; ++n) { isinx= ep_dir[n].x; isiny= ep_dir[n].y; isinz= ep_dir[n].z; vektor d; real dn; d.x = (ep_dir[n].x==0) ? 0 : (ORT(p,i,X)-ep_pos[n].x) ; d.y = (ep_dir[n].y==0) ? 0 : (ORT(p,i,Y)-ep_pos[n].y); d.z = (ep_dir[n].z==0) ? 0 : (ORT(p,i,Z)-ep_pos[n].z); dn = SPROD(d,ep_dir[n]); dd = SPROD(d,d); if ( dd < ep_rcut*ep_rcut) { /* for the determination of the contact area */ ep_atomsincontact[n]++; if(isinz) { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,X) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Y) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,X) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Y) ); } else if(isiny) { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,X) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Z) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,X) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Z) ); } else { ep_xmax[n] = MAX(ep_xmax[n], ORT(p,i,Y) ); ep_ymax[n] = MAX(ep_ymax[n], ORT(p,i,Z) ); ep_xmin[n] = MIN(ep_xmin[n], ORT(p,i,Y) ); ep_ymin[n] = MIN(ep_ymin[n], ORT(p,i,Z) ); } if(have_extpotfile == 1){ PAIR_INT3(pot_zwi, pot_grad, ext_pot, n, ep_nind, dd, is_short); tot_pot_energy += pot_zwi; force.x = - pot_grad * d.x; force.y = - pot_grad * d.y; force.z = - pot_grad * d.z; KRAFT(p,i,X) += force.x; KRAFT(p,i,Y) += force.y; KRAFT(p,i,Z) += force.z; totaddforce.x += force.x; totaddforce.y += force.y; totaddforce.z += force.z; ep_fext[n] += -pot_grad * ABS(dn); /* normal force on indentor */ #ifdef P_AXIAL tmp_vir_vect.x -= d.x * force.x; tmp_vir_vect.y -= d.y * force.y; #ifndef TWOD tmp_vir_vect.z -= d.z * force.z; #endif #else tmp_virial -= dd * pot_grad; #endif #ifdef STRESS_TENS if (do_press_calc) { PRESSTENS(p,i,xx) -= d.x * force.x; PRESSTENS(p,i,yy) -= d.y * force.y; PRESSTENS(p,i,xy) -= d.x * force.y; #ifndef TWOD PRESSTENS(p,i,zz) -= d.z * force.z; PRESSTENS(p,i,yz) -= d.y * force.z; PRESSTENS(p,i,zx) -= d.z * force.x; #endif } #endif } /* old version of extpot, kept for downwards compatibility */ else{ ddn= sqrt(dd); cc = (ep_rcut - ddn)/ep_a; if (cc > UPPER_EXP) cc = UPPER_EXP; if (cc < LOWER_EXP) cc = LOWER_EXP; ee = exp(cc - 1.0/cc); tot_pot_energy += ee; POTENG(p,i) += ee; ee = ee / ep_a / ddn * (1.0 + 1.0 /(cc*cc)); KRAFT(p,i,X) += ee * d.x; KRAFT(p,i,Y) += ee * d.y; KRAFT(p,i,Z) += ee * d.z; ep_fext[n] += ee * ABS(dn); /* normal force on indentor */ } } } } } } else { error("Error: external potential ep_key not defined.\n"); } #ifdef P_AXIAL vir_xx += tmp_vir_vect.x; vir_yy += tmp_vir_vect.y; virial += tmp_vir_vect.x; virial += tmp_vir_vect.y; #ifndef TWOD vir_zz += tmp_vir_vect.z; virial += tmp_vir_vect.z; #endif #else virial += tmp_virial; #endif }
void calc_strain(void) { cell *p,*q; int i,j,k; int l,m,n; int r,s,t; int u,v,w; int g,h; int number; /* number of neighbours */ int totalnumber = 0; /* number of neighbours of all atoms */ int emptynumber = 0; /* number of atoms with less than 3 neighbours */ int maxnumber = 0, minnumber = 1000; /* maximal and minimal number of neighbours occuring */ int num = 4; vektor pbc; vektor *d, *du, *tmp; real a[3][3], b[3][3]; real radius; real det; /* Allocate memory for temporary variables */ d = (vektor *) malloc( num * sizeof(vektor)); du = (vektor *) malloc( num * sizeof(vektor)); tmp = (vektor *) malloc( num * sizeof(vektor)); /* Initialization */ /* for each cell */ for (i=0; i < cell_dim.x; ++i) for (j=0; j < cell_dim.y; ++j) #ifndef TWOD for (k=0; k < cell_dim.z; ++k) #endif { #ifdef TWOD p = PTR_2D_V(cell_array,i,j ,cell_dim); #else p = PTR_3D_V(cell_array,i,j,k,cell_dim); #endif /* For each atom in this cell */ for (u=0; u < p->n; ++u) { p->strain[u].x = 0.0; p->strain[u].y = 0.0; #ifndef TWOD p->strain[u].z = 0.0; #endif p->strain_offdia[u].x = 0.0; #ifndef TWOD p->strain_offdia[u].y = 0.0; p->strain_offdia[u].z = 0.0; #endif p->empty[u] = 0; } } /* Compute strain tensor */ /* For each cell */ for (i=0; i < cell_dim.x; ++i) for (j=0; j < cell_dim.y; ++j) #ifndef TWOD for (k=0; k < cell_dim.z; ++k) #endif { #ifdef TWOD p = PTR_2D_V(cell_array,i,j ,cell_dim); #else p = PTR_3D_V(cell_array,i,j,k,cell_dim); #endif /* For each atom in this first cell */ for (u=0; u<p->n; ++u) { number = 0; /* For the neighbours of the first cell */ for (l=-1; l <= 1; ++l) for (m=-1; m <= 1; ++m) #ifndef TWOD for (n=-1; n <= 1; ++n) #endif { /* Calculate Indices of Neighbour */ r = i+l; pbc.x = 0; s = j+m; pbc.y = 0; #ifndef TWOD t = k+n; pbc.z = 0; #endif /* Deal with periodic boundary conditions if necessary */ if (r<0) { if (pbc_dirs.x==1) { r = cell_dim.x-1; pbc.x -= box_x.x; pbc.y -= box_x.y; #ifndef TWOD pbc.z -= box_x.z; #endif } else continue; } if (s<0) { if (pbc_dirs.y==1) { s = cell_dim.y-1; pbc.x -= box_y.x; pbc.y -= box_y.y; #ifndef TWOD pbc.z -= box_y.z; #endif } else continue; } #ifndef TWOD if (t<0) { if (pbc_dirs.z==1) { t = cell_dim.z-1; pbc.x -= box_z.x; pbc.y -= box_z.y; pbc.z -= box_z.z; } else continue; } #endif if (r>cell_dim.x-1) { if (pbc_dirs.x==1) { r = 0; pbc.x += box_x.x; pbc.y += box_x.y; #ifndef TWOD pbc.z += box_x.z; #endif } else continue; } if (s>cell_dim.y-1) { if (pbc_dirs.y==1) { s = 0; pbc.x += box_y.x; pbc.y += box_y.y; #ifndef TWOD pbc.z += box_y.z; #endif } else continue; } #ifndef TWOD if (t>cell_dim.z-1) { if (pbc_dirs.z==1) { t = 0; pbc.x += box_z.x; pbc.y += box_z.y; pbc.z += box_z.z; } else continue; } #endif /* Neighbour cell (note that p==q ist possible) */ #ifdef TWOD q = PTR_2D_V(cell_array,r,s,cell_dim); #else q = PTR_3D_V(cell_array,r,s,t,cell_dim); #endif /* For each atom in the second cell */ for( v=0; v<q->n; ++v) { /* Check whether there is enough memory for tmp variables */ if( number >= num) { ++num; d = (vektor *) realloc( d, num * sizeof(vektor)); du = (vektor *) realloc( du, num * sizeof(vektor)); tmp = (vektor *) realloc( tmp, num * sizeof(vektor)); } /* Calculate distance */ d[number].x = q->ort[v].x - q->dsp[v].x - p->ort[u].x + p->dsp[u].x + pbc.x; d[number].y = q->ort[v].y - q->dsp[v].y - p->ort[u].y + p->dsp[u].y + pbc.y; #ifndef TWOD d[number].z = q->ort[v].z - q->dsp[v].z - p->ort[u].z + p->dsp[u].z + pbc.z; #endif radius = sqrt( (double)(SPROD(d[number],d[number])) ); if ( radius > 0.01 && radius < r_max ) { /* Calculate differences of displacements */ du[number].x = q->dsp[v].x - p->dsp[u].x; du[number].y = q->dsp[v].y - p->dsp[u].y; #ifndef TWOD du[number].z = q->dsp[v].z - p->dsp[u].z; #endif ++number; /* Number of neighbours*/ } } /* v */ } /* Neighbours of p */ /* Calculate transformation matrix of du */ /* a = dx * dxT */ totalnumber += number; maxnumber = (number>maxnumber) ? number : maxnumber; minnumber = (number<minnumber) ? number : minnumber; /* At least 3 neighbour atoms are required */ if (number < 3) { p->empty[u] = 1; ++emptynumber; } else #ifdef TWOD { for(g=0; g<2; ++g) for(h=0; h<2; ++h) a[g][h] = 0.0; for (w=0; w<number; ++w) { a[0][0] += d[w].x * d[w].x; a[0][1] += d[w].x * d[w].y; a[1][0] += d[w].y * d[w].x; a[1][1] += d[w].y * d[w].y; } /* b = Inverse of a */ det = a[0][0] * a[1][1] - a[0][1] * a[1][0]; if (det == 0.0) error("Transformation matrix zero."); b[0][0] = a[1][1] / det; b[0][1] = -a[0][1] / det; b[1][0] = -a[1][0] / det; b[1][1] = a[0][0] / det; /* tmp = dx * b */ for (w=0; w<number; ++w) { tmp[w].x = d[w].x * b[0][0] + d[w].y * b[1][0]; tmp[w].y = d[w].x * b[0][1] + d[w].y * b[1][1]; } /* strain = (symmetrized) du * tmp */ for (w=0; w<number; ++w) { p->strain[u].x += du[w].x * tmp[w].x; p->strain[u].y += du[w].y * tmp[w].y; p->strain_offdia[u].x += ( du[w].y * tmp[w].x + du[w].x * tmp[w].y ) / 2; } #else /* 3D */ { for (g=0; g<3; ++g) for (h=0; h<3; ++h) a[g][h] = 0.0; for (w=0; w<number; ++w) { a[0][0] += d[w].x * d[w].x; a[0][1] += d[w].x * d[w].y; a[0][2] += d[w].x * d[w].z; a[1][0] += d[w].y * d[w].x; a[1][1] += d[w].y * d[w].y; a[1][2] += d[w].y * d[w].z; a[2][0] += d[w].z * d[w].x; a[2][1] += d[w].z * d[w].y; a[2][2] += d[w].z * d[w].z; } /* b = Inverse of a */ det = a[0][0] * a[1][1] * a[2][2] + a[0][1] * a[1][2] * a[2][0] + a[0][2] * a[1][0] * a[2][1] - a[2][0] * a[1][1] * a[0][2] - a[2][1] * a[1][2] * a[0][0] - a[2][2] * a[1][0] * a[0][1]; if (det == 0.0) error("Transformation matrix singular."); b[0][0] = ( a[1][1] * a[2][2] - a[1][2] * a[2][1] ) / det; b[0][1] = ( a[2][1] * a[0][2] - a[0][1] * a[2][2] ) / det; b[0][2] = ( a[0][1] * a[1][2] - a[1][1] * a[0][2] ) / det; b[1][0] = ( a[1][2] * a[2][0] - a[1][0] * a[2][2] ) / det; b[1][1] = ( a[0][0] * a[2][2] - a[2][0] * a[0][2] ) / det; b[1][2] = ( a[0][2] * a[1][0] - a[0][0] * a[1][2] ) / det; b[2][0] = ( a[1][0] * a[2][1] - a[1][1] * a[2][0] ) / det; b[2][1] = ( a[0][1] * a[2][0] - a[0][0] * a[2][1] ) / det; b[2][2] = ( a[0][0] * a[1][1] - a[0][1] * a[1][0] ) / det; /* tmp = dx * b */ for (w=0; w<number; ++w) { tmp[w].x = d[w].x * b[0][0] + d[w].y * b[1][0] + d[w].z * b[2][0]; tmp[w].y = d[w].x * b[0][1] + d[w].y * b[1][1] + d[w].z * b[2][1]; tmp[w].z = d[w].x * b[0][2] + d[w].y * b[1][2] + d[w].z * b[2][2]; } /* strain = (symmetrized) du * tmp */ for (w=0; w<number; ++w) { p->strain[u].x += du[w].x * tmp[w].x; p->strain[u].y += du[w].y * tmp[w].y; p->strain[u].z += du[w].z * tmp[w].z; p->strain_offdia[u].x += ( du[w].y * tmp[w].z + du[w].z * tmp[w].y ) / 2; p->strain_offdia[u].y += ( du[w].x * tmp[w].z + du[w].z * tmp[w].x ) / 2; p->strain_offdia[u].z += ( du[w].x * tmp[w].y + du[w].y * tmp[w].x ) / 2; } #endif /* Not TWOD */ } /* number > 2 */ } /* u */ } /* First cell */ /* Statistics */ printf("Maximal number of neighbour atoms: %d\n", maxnumber); printf("Minimal number of neighbour atoms: %d\n", minnumber); printf("Average number of neighbour atoms: %f\n", (float) totalnumber/natoms ); if(emptynumber>0) printf("Number of omitted atoms: %d (%.2f %%)\n", emptynumber, (float) emptynumber/natoms ); }
void do_elco_eam(cell *p, cell *q, vektor pbc) { int i, j, jstart; vektor tmp_d, d; int same_cell; int p_typ, q_typ, col1, col2; real r2, eam_energy, pref1, pref2, tmp; real f_i_strich, f_i_zweistrich, f_i_dreistrich; real f_j_strich, f_j_zweistrich, f_j_dreistrich; real rho_j_strich, rho_j_zweistrich, rho_j_dreistrich; real rho_i_strich, rho_i_zweistrich, rho_i_dreistrich; int is_short=0, inc=ntypes*ntypes; real xx, xy, yy, yz, zz, zx; /* for each atom in first cell */ for (i=0; i<p->n; ++i) { tmp_d.x = p->ort[i].x - pbc.x; tmp_d.y = p->ort[i].y - pbc.y; #ifndef TWOD tmp_d.z = p->ort[i].z - pbc.z; #endif p_typ = p->sorte[i]; #ifdef TWOD same_cell = ((p==q) && (pbc.x==0) && (pbc.y==0)); #else same_cell = ((p==q) && (pbc.x==0) && (pbc.y==0) && (pbc.z==0)); #endif if (same_cell) { PAIR_INT4(eam_energy, f_i_strich, f_i_zweistrich, f_i_dreistrich, embed_pot, p_typ, ntypes, EAM_RHO(p,i), is_short) epot += eam_energy; xx = p->eam_stress[i].xx; xy = p->eam_stress[i].xy; yy = p->eam_stress[i].yy; #ifndef TWOD yz = p->eam_stress[i].yz; zz = p->eam_stress[i].zz; zx = p->eam_stress[i].zx; #endif p->stress[i].xx += f_i_strich * xx; sigma.xx += f_i_strich * xx; p->stress[i].xy += f_i_strich * xy; sigma.xy += f_i_strich * xy; p->stress[i].yy += f_i_strich * yy; sigma.yy += f_i_strich * yy; #ifndef TWOD p->stress[i].yz += f_i_strich * yz; sigma.yz += f_i_strich * yz; p->stress[i].zz += f_i_strich * zz; sigma.zz += f_i_strich * zz; p->stress[i].zx += f_i_strich * zx; sigma.zx += f_i_strich * zx; #endif p->elco[i].c11 += f_i_zweistrich * xx * xx; c.c11 += f_i_zweistrich * xx * xx; p->elco[i].c12 += f_i_zweistrich * xx * yy; c.c12 += f_i_zweistrich * xx * yy; p->elco[i].c66 += f_i_zweistrich * xy * xy; c.c66 += f_i_zweistrich * xy * xy; p->elco[i].c22 += f_i_zweistrich * yy * yy; c.c22 += f_i_zweistrich * yy * yy; #ifndef TWOD p->elco[i].c13 += f_i_zweistrich * xx * zz; c.c13 += f_i_zweistrich * xx * zz; p->elco[i].c55 += f_i_zweistrich * zx * zx; c.c55 += f_i_zweistrich * zx * zx; p->elco[i].c23 += f_i_zweistrich * yy * zz; c.c23 += f_i_zweistrich * yy * zz; p->elco[i].c44 += f_i_zweistrich * yz * yz; c.c44 += f_i_zweistrich * yz * yz; p->elco[i].c33 += f_i_zweistrich * zz * zz; c.c33 += f_i_zweistrich * zz * zz; p->elco[i].c45 += f_i_zweistrich * yz * zx; c.c45 += f_i_zweistrich * yz * zx; p->elco[i].c46 += f_i_zweistrich * yz * xy; c.c46 += f_i_zweistrich * yz * xy; p->elco[i].c25 += f_i_zweistrich * yy * zx; c.c25 += f_i_zweistrich * yy * zx; p->elco[i].c56 += f_i_zweistrich * zx * xy; c.c56 += f_i_zweistrich * zx * xy; p->elco[i].c14 += f_i_zweistrich * xx * yz; c.c14 += f_i_zweistrich * xx * yz; p->elco[i].c15 += f_i_zweistrich * xx * zx; c.c15 += f_i_zweistrich * xx * zx; #endif p->elco[i].c16 += f_i_zweistrich * xx * xy; c.c16 += f_i_zweistrich * xx * xy; p->elco[i].c26 += f_i_zweistrich * yy * xy; c.c26 += f_i_zweistrich * yy * xy; #ifndef TWOD p->elco[i].c24 += f_i_zweistrich * yy * yz; c.c24 += f_i_zweistrich * yy * yz; p->elco[i].c34 += f_i_zweistrich * zz * yz; c.c34 += f_i_zweistrich * zz * yz; p->elco[i].c35 += f_i_zweistrich * zz * zx; c.c35 += f_i_zweistrich * zz * zx; p->elco[i].c36 += f_i_zweistrich * zz * xy; c.c36 += f_i_zweistrich * zz * xy; #endif press += f_i_strich * p->eam_press[i]; bulkm += f_i_zweistrich * p->eam_press[i] * p->eam_press[i] + f_i_strich * p->eam_bulkm[i]; dbulkm_dp += f_i_dreistrich * p->eam_press[i] * p->eam_press[i] * p->eam_press[i] + 3.0 * f_i_zweistrich * p->eam_press[i] * p->eam_bulkm[i] + f_i_strich * p->eam_dbulkm[i]; } else { DERIV_FUNC(f_i_strich, f_i_zweistrich, f_i_dreistrich, embed_pot, p_typ, ntypes, EAM_RHO(p,i), is_short) } jstart = (same_cell ? i+1 : 0); /* for each atom in neighbouring cell */ for (j=jstart; j<q->n; ++j) { /* calculate distance */ d.x = q->ort[j].x - tmp_d.x; d.y = q->ort[j].y - tmp_d.y; #ifndef TWOD d.z = q->ort[j].z - tmp_d.z; #endif q_typ = q->sorte[j]; r2 = SPROD(d,d); col1 = q_typ * ntypes + p_typ; col2 = p_typ * ntypes + q_typ; if ((r2 < rho_h_tab.end[col1]) || (r2 < rho_h_tab.end[col2])) { DERIV_FUNC(f_j_strich, f_j_zweistrich, f_j_dreistrich, embed_pot, q_typ, ntypes, EAM_RHO(q,j), is_short); DERIV_FUNC(rho_i_strich, rho_i_zweistrich, rho_i_dreistrich, rho_h_tab, col1, inc, r2, is_short); if ( col1 == col2 ) { rho_j_strich = rho_i_strich; rho_j_zweistrich = rho_i_zweistrich; } else { DERIV_FUNC(rho_j_strich, rho_j_zweistrich, rho_j_dreistrich, rho_h_tab, col2, inc, r2, is_short); } pref1 = 2.0 * ( f_i_strich * rho_j_zweistrich + f_j_strich * rho_i_zweistrich ); pref2 = f_i_strich * rho_j_strich + f_j_strich * rho_i_strich; tmp = d.x * d.x * d.x * d.x * pref1 + d.x * d.x * pref2; p->elco[i].c11 += tmp; q->elco[j].c11 += tmp; c.c11 += 2.0 * tmp; tmp = d.x * d.x * d.y * d.y * pref1; p->elco[i].c12 += tmp; q->elco[j].c12 += tmp; c.c12 += 2.0 * tmp; tmp += 0.5 * ( d.x * d.x + d.y * d.y ) * pref2; p->elco[i].c66 += tmp; q->elco[j].c66 += tmp; c.c66 += 2.0 * tmp; tmp = d.y * d.y * d.y * d.y * pref1 + d.y * d.y * pref2; p->elco[i].c22 += tmp; q->elco[j].c22 += tmp; c.c22 += 2.0 * tmp; #ifndef TWOD tmp = d.x * d.x * d.z * d.z * pref1; p->elco[i].c13 += tmp; q->elco[j].c13 += tmp; c.c13 += 2.0 * tmp; tmp += 0.5 * ( d.x * d.x + d.z * d.z ) * pref2; p->elco[i].c55 += tmp; q->elco[j].c55 += tmp; c.c55 += 2.0 * tmp; tmp = d.y * d.y * d.z * d.z * pref1; p->elco[i].c23 += tmp; q->elco[j].c23 += tmp; c.c23 += 2.0 * tmp; tmp += 0.5 * ( d.y * d.y + d.z * d.z ) * pref2; p->elco[i].c44 += tmp; q->elco[j].c44 += tmp; c.c44 += 2.0 * tmp; tmp = d.z * d.z * d.z * d.z * pref1 + d.z * d.z * pref2; p->elco[i].c33 += tmp; q->elco[j].c33 += tmp; c.c33 += 2.0 * tmp; tmp = d.x * d.y * d.z * d.z * pref1 + 0.25 * d.x * d.y * pref2; p->elco[i].c45 += tmp; q->elco[j].c45 += tmp; c.c45 += 2.0 * tmp; tmp = d.x * d.y * d.y * d.z * pref1 + 0.25 * d.x * d.z * pref2; p->elco[i].c46 += tmp; q->elco[j].c46 += tmp; c.c46 += 2.0 * tmp; tmp -= 0.25 * d.x * d.z * pref2; p->elco[i].c25 += tmp; q->elco[j].c25 += tmp; c.c25 += 2.0 * tmp; tmp = d.x * d.x * d.y * d.z * pref1 + 0.25 * d.y * d.z * pref2; p->elco[i].c56 += tmp; q->elco[j].c56 += tmp; c.c56 += 2.0 * tmp; tmp -= 0.25 * d.y * d.z * pref2; p->elco[i].c14 += tmp; q->elco[j].c14 += tmp; c.c14 += 2.0 * tmp; tmp = d.x * d.x * d.x * d.z * pref1 + 0.5 * d.x * d.z * pref2; p->elco[i].c15 += tmp; q->elco[j].c15 += tmp; c.c15 += 2.0 * tmp; #endif tmp = d.x * d.x * d.x * d.y * pref1 + 0.5 * d.x * d.y * pref2; p->elco[i].c16 += tmp; q->elco[j].c16 += tmp; c.c16 += 2.0 * tmp; tmp = d.x * d.y * d.y * d.y * pref1 + 0.5 * d.x * d.y * pref2; p->elco[i].c26 += tmp; q->elco[j].c26 += tmp; c.c26 += 2.0 * tmp; #ifndef TWOD tmp = d.y * d.y * d.y * d.z * pref1 + 0.5 * d.y * d.z * pref2; p->elco[i].c24 += tmp; q->elco[j].c24 += tmp; c.c24 += 2.0 * tmp; tmp = d.y * d.z * d.z * d.z * pref1 + 0.5 * d.y * d.z * pref2; p->elco[i].c34 += tmp; q->elco[j].c34 += tmp; c.c34 += 2.0 * tmp; tmp = d.x * d.z * d.z * d.z * pref1 + 0.5 * d.x * d.z * pref2; p->elco[i].c35 += tmp; q->elco[j].c35 += tmp; c.c35 += 2.0 * tmp; tmp = d.x * d.y * d.z * d.z * pref1; p->elco[i].c36 += tmp; q->elco[j].c36 += tmp; c.c36 += 2.0 * tmp; #endif } } } }
double calc_forces(double* xi_opt, double* forces, int flag) { double tmpsum = 0.0; double sum = 0.0; int first = 0; int col = 0; int ne = 0; int size = 0; int i = flag; double* xi = NULL; apot_table_t* apt = &g_pot.apot_table; double charge[g_param.ntypes]; double sum_charges; double dp_kappa; #if defined(DIPOLE) double dp_alpha[g_param.ntypes]; double dp_b[apt->number]; double dp_c[apt->number]; #endif // DIPOLE switch (g_pot.format_type) { case POTENTIAL_FORMAT_UNKNOWN: break; case POTENTIAL_FORMAT_ANALYTIC: xi = g_pot.calc_pot.table; break; case POTENTIAL_FORMAT_TABULATED_EQ_DIST: case POTENTIAL_FORMAT_TABULATED_NON_EQ_DIST: xi = xi_opt; break; } ne = g_pot.apot_table.total_ne_par; size = apt->number; /* This is the start of an infinite loop */ while (1) { tmpsum = 0.0; /* sum of squares of local process */ #if defined(APOT) && !defined(MPI) if (g_pot.format_type == POTENTIAL_FORMAT_ANALYTIC) { apot_check_params(xi_opt); update_calc_table(xi_opt, xi, 0); } #endif // APOT && !MPI #if defined(MPI) /* exchange potential and flag value */ #if !defined(APOT) MPI_Bcast(xi, g_pot.calc_pot.len, MPI_DOUBLE, 0, MPI_COMM_WORLD); #endif // !APOT MPI_Bcast(&flag, 1, MPI_INT, 0, MPI_COMM_WORLD); if (flag == 1) break; /* Exception: flag 1 means clean up */ #if defined(APOT) if (g_mpi.myid == 0) apot_check_params(xi_opt); MPI_Bcast(xi_opt, g_calc.ndimtot, MPI_DOUBLE, 0, MPI_COMM_WORLD); if (g_pot.format_type == POTENTIAL_FORMAT_ANALYTIC) update_calc_table(xi_opt, xi, 0); #else // APOT /* if flag==2 then the potential parameters have changed -> sync */ if (flag == 2) potsync(); #endif // APOT #endif // MPI /* local arrays for electrostatic parameters */ sum_charges = 0; for (i = 0; i < g_param.ntypes - 1; i++) { if (xi_opt[2 * size + ne + i]) { charge[i] = xi_opt[2 * size + ne + i]; sum_charges += apt->ratio[i] * charge[i]; } else { charge[i] = 0.0; } } apt->last_charge = -sum_charges / apt->ratio[g_param.ntypes - 1]; charge[g_param.ntypes - 1] = apt->last_charge; if (xi_opt[2 * size + ne + g_param.ntypes - 1]) { dp_kappa = xi_opt[2 * size + ne + g_param.ntypes - 1]; } else { dp_kappa = 0.0; } #if defined(DIPOLE) for (i = 0; i < g_param.ntypes; i++) { if (xi_opt[2 * size + ne + g_param.ntypes + i]) { dp_alpha[i] = xi_opt[2 * size + ne + g_param.ntypes + i]; } else { dp_alpha[i] = 0.0; } } for (i = 0; i < size; i++) { if (xi_opt[2 * size + ne + 2 * g_param.ntypes + i]) { dp_b[i] = xi_opt[2 * size + ne + 2 * g_param.ntypes + i]; } else { dp_b[i] = 0.0; } if (xi_opt[3 * size + ne + 2 * g_param.ntypes + i]) { dp_c[i] = xi_opt[3 * size + ne + 2 * g_param.ntypes + i]; } else { dp_c[i] = 0.0; } } #endif // DIPOLE /* init second derivatives for splines */ for (col = 0; col < g_calc.paircol; col++) { first = g_pot.calc_pot.first[col]; switch (g_pot.format_type) { case POTENTIAL_FORMAT_UNKNOWN: error(1, "Unknown potential format detected! (%s:%d)\n", __FILE__, __LINE__); case POTENTIAL_FORMAT_ANALYTIC: case POTENTIAL_FORMAT_TABULATED_EQ_DIST: { spline_ed(g_pot.calc_pot.step[col], xi + first, g_pot.calc_pot.last[col] - first + 1, *(xi + first - 2), 0.0, g_pot.calc_pot.d2tab + first); break; } case POTENTIAL_FORMAT_TABULATED_NON_EQ_DIST: { spline_ne(g_pot.calc_pot.xcoord + first, xi + first, g_pot.calc_pot.last[col] - first + 1, *(xi + first - 2), 0.0, g_pot.calc_pot.d2tab + first); } } } #if !defined(MPI) g_mpi.myconf = g_config.nconf; #endif // MPI /* region containing loop over configurations, also OMP-parallelized region */ { int self; vector tmp_force; int h, j, type1, type2, uf; #if defined(STRESS) int us, stresses; #endif // STRESS int n_i, n_j; double fnval, grad, fnval_tail, grad_tail, grad_i, grad_j; #if defined(DIPOLE) double p_sr_tail; #endif // DIPOLE atom_t* atom; neigh_t* neigh; /* loop over configurations: M A I N LOOP CONTAINING ALL ATOM-LOOPS */ for (h = g_mpi.firstconf; h < g_mpi.firstconf + g_mpi.myconf; h++) { uf = g_config.conf_uf[h - g_mpi.firstconf]; #if defined(STRESS) us = g_config.conf_us[h - g_mpi.firstconf]; #endif // STRESS /* reset energies and stresses */ forces[g_calc.energy_p + h] = 0.0; #if defined(STRESS) stresses = g_calc.stress_p + 6 * h; for (i = 0; i < 6; i++) forces[stresses + i] = 0.0; #endif // STRESS #if defined(DIPOLE) /* reset dipoles and fields: LOOP Z E R O */ for (i = 0; i < g_config.inconf[h]; i++) { atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; atom->E_stat.x = 0.0; atom->E_stat.y = 0.0; atom->E_stat.z = 0.0; atom->p_sr.x = 0.0; atom->p_sr.y = 0.0; atom->p_sr.z = 0.0; } #endif // DIPOLE /* F I R S T LOOP OVER ATOMS: reset forces, dipoles */ for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ n_i = 3 * (g_config.cnfstart[h] + i); if (uf) { forces[n_i + 0] = -g_config.force_0[n_i + 0]; forces[n_i + 1] = -g_config.force_0[n_i + 1]; forces[n_i + 2] = -g_config.force_0[n_i + 2]; } else { forces[n_i + 0] = 0.0; forces[n_i + 1] = 0.0; forces[n_i + 2] = 0.0; } } /* end F I R S T LOOP */ /* S E C O N D loop: calculate short-range and monopole forces, calculate static field- and dipole-contributions */ for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; n_i = 3 * (g_config.cnfstart[h] + i); for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* updating tail-functions - only necessary with variing kappa */ if (!apt->sw_kappa) elstat_shift(neigh->r, dp_kappa, &neigh->fnval_el, &neigh->grad_el, &neigh->ggrad_el); /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + g_config.cnfstart[h]) ? 1 : 0; /* calculate short-range forces */ if (neigh->r < g_pot.calc_pot.end[col]) { if (uf) { fnval = splint_comb_dir(&g_pot.calc_pot, xi, neigh->slot[0], neigh->shift[0], neigh->step[0], &grad); } else { fnval = splint_dir(&g_pot.calc_pot, xi, neigh->slot[0], neigh->shift[0], neigh->step[0]); } /* avoid double counting if atom is interacting with a copy of itself */ if (self) { fnval *= 0.5; grad *= 0.5; } forces[g_calc.energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad; tmp_force.y = neigh->dist_r.y * grad; tmp_force.z = neigh->dist_r.z * grad; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #if defined(STRESS) /* calculate pair stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif // STRESS } } /* calculate monopole forces */ if (neigh->r < g_config.dp_cut && (charge[type1] || charge[type2])) { fnval_tail = neigh->fnval_el; grad_tail = neigh->grad_el; grad_i = charge[type2] * grad_tail; if (type1 == type2) { grad_j = grad_i; } else { grad_j = charge[type1] * grad_tail; } fnval = charge[type1] * charge[type2] * fnval_tail; grad = charge[type1] * grad_i; if (self) { grad_i *= 0.5; grad_j *= 0.5; fnval *= 0.5; grad *= 0.5; } forces[g_calc.energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist.x * grad; tmp_force.y = neigh->dist.y * grad; tmp_force.z = neigh->dist.z * grad; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #if defined(STRESS) /* calculate coulomb stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif // STRESS } #if defined(DIPOLE) /* calculate static field-contributions */ atom->E_stat.x += neigh->dist.x * grad_i; atom->E_stat.y += neigh->dist.y * grad_i; atom->E_stat.z += neigh->dist.z * grad_i; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_stat.x -= neigh->dist.x * grad_j; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_stat.y -= neigh->dist.y * grad_j; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_stat.z -= neigh->dist.z * grad_j; /* calculate short-range dipoles */ if (dp_alpha[type1] && dp_b[col] && dp_c[col]) { p_sr_tail = grad_tail * neigh->r * shortrange_value(neigh->r, dp_alpha[type1], dp_b[col], dp_c[col]); atom->p_sr.x += charge[type2] * neigh->dist_r.x * p_sr_tail; atom->p_sr.y += charge[type2] * neigh->dist_r.y * p_sr_tail; atom->p_sr.z += charge[type2] * neigh->dist_r.z * p_sr_tail; } if (dp_alpha[type2] && dp_b[col] && dp_c[col] && !self) { p_sr_tail = grad_tail * neigh->r * shortrange_value(neigh->r, dp_alpha[type2], dp_b[col], dp_c[col]); g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_sr.x -= charge[type1] * neigh->dist_r.x * p_sr_tail; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_sr.y -= charge[type1] * neigh->dist_r.y * p_sr_tail; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_sr.z -= charge[type1] * neigh->dist_r.z * p_sr_tail; } #endif // DIPOLE } } /* loop over neighbours */ } /* end S E C O N D loop over atoms */ #if defined(DIPOLE) /* T H I R D loop: calculate whole dipole moment for every atom */ double rp, dp_sum; int dp_converged = 0, dp_it = 0; double max_diff = 10; while (dp_converged == 0) { dp_sum = 0; for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; if (dp_alpha[type1]) { if (dp_it) { /* note: mixing parameter is different from that on in IMD */ atom->E_tot.x = (1 - g_config.dp_mix) * atom->E_ind.x + g_config.dp_mix * atom->E_old.x + atom->E_stat.x; atom->E_tot.y = (1 - g_config.dp_mix) * atom->E_ind.y + g_config.dp_mix * atom->E_old.y + atom->E_stat.y; atom->E_tot.z = (1 - g_config.dp_mix) * atom->E_ind.z + g_config.dp_mix * atom->E_old.z + atom->E_stat.z; } else { atom->E_tot.x = atom->E_ind.x + atom->E_stat.x; atom->E_tot.y = atom->E_ind.y + atom->E_stat.y; atom->E_tot.z = atom->E_ind.z + atom->E_stat.z; } atom->p_ind.x = dp_alpha[type1] * atom->E_tot.x + atom->p_sr.x; atom->p_ind.y = dp_alpha[type1] * atom->E_tot.y + atom->p_sr.y; atom->p_ind.z = dp_alpha[type1] * atom->E_tot.z + atom->p_sr.z; atom->E_old.x = atom->E_ind.x; atom->E_old.y = atom->E_ind.y; atom->E_old.z = atom->E_ind.z; atom->E_ind.x = 0.0; atom->E_ind.y = 0.0; atom->E_ind.z = 0.0; } } for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + g_config.cnfstart[h]) ? 1 : 0; if (neigh->r < g_config.dp_cut && dp_alpha[type1] && dp_alpha[type2]) { rp = SPROD( g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind, neigh->dist_r); atom->E_ind.x += neigh->grad_el * (3 * rp * neigh->dist_r.x - g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.x); atom->E_ind.y += neigh->grad_el * (3 * rp * neigh->dist_r.y - g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.y); atom->E_ind.z += neigh->grad_el * (3 * rp * neigh->dist_r.z - g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.z); if (!self) { rp = SPROD(atom->p_ind, neigh->dist_r); g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_ind.x += neigh->grad_el * (3 * rp * neigh->dist_r.x - atom->p_ind.x); g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_ind.y += neigh->grad_el * (3 * rp * neigh->dist_r.y - atom->p_ind.y); g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_ind.z += neigh->grad_el * (3 * rp * neigh->dist_r.z - atom->p_ind.z); } } } } for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; if (dp_alpha[type1]) { dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.x - atom->E_ind.x)); dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.y - atom->E_ind.y)); dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.z - atom->E_ind.z)); } } dp_sum /= 3 * g_config.inconf[h]; dp_sum = sqrt(dp_sum); if (dp_it) { if ((dp_sum > max_diff) || (dp_it > 50)) { dp_converged = 1; for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; if (dp_alpha[type1]) { atom->p_ind.x = dp_alpha[type1] * atom->E_stat.x + atom->p_sr.x; atom->p_ind.y = dp_alpha[type1] * atom->E_stat.y + atom->p_sr.y; atom->p_ind.z = dp_alpha[type1] * atom->E_stat.z + atom->p_sr.z; atom->E_ind.x = atom->E_stat.x; atom->E_ind.y = atom->E_stat.y; atom->E_ind.z = atom->E_stat.z; } } } } if (dp_sum < g_config.dp_tol) dp_converged = 1; dp_it++; } /* end T H I R D loop over atoms */ /* F O U R T H loop: calculate monopole-dipole and dipole-dipole forces */ double rp_i, rp_j, pp_ij, tmp_1, tmp_2; double grad_1, grad_2, srval, srgrad, srval_tail, srgrad_tail, fnval_sum, grad_sum; for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; n_i = 3 * (g_config.cnfstart[h] + i); for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + g_config.cnfstart[h]) ? 1 : 0; if (neigh->r < g_config.dp_cut && (dp_alpha[type1] || dp_alpha[type2])) { fnval_tail = -neigh->grad_el; grad_tail = -neigh->ggrad_el; if (dp_b[col] && dp_c[col]) { shortrange_term(neigh->r, dp_b[col], dp_c[col], &srval_tail, &srgrad_tail); srval = fnval_tail * srval_tail; srgrad = fnval_tail * srgrad_tail + grad_tail * srval_tail; } if (self) { fnval_tail *= 0.5; grad_tail *= 0.5; } /* monopole-dipole contributions */ if (charge[type1] && dp_alpha[type2]) { if (dp_b[col] && dp_c[col]) { fnval_sum = fnval_tail + srval; grad_sum = grad_tail + srgrad; } else { fnval_sum = fnval_tail; grad_sum = grad_tail; } rp_j = SPROD( g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind, neigh->dist_r); fnval = charge[type1] * rp_j * fnval_sum * neigh->r; grad_1 = charge[type1] * rp_j * grad_sum * neigh->r2; grad_2 = charge[type1] * fnval_sum; forces[g_calc.energy_p + h] -= fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad_1 + g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.x * grad_2; tmp_force.y = neigh->dist_r.y * grad_1 + g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.y * grad_2; tmp_force.z = neigh->dist_r.z * grad_1 + g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.z * grad_2; forces[n_i + 0] -= tmp_force.x; forces[n_i + 1] -= tmp_force.y; forces[n_i + 2] -= tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] += tmp_force.x; forces[n_j + 1] += tmp_force.y; forces[n_j + 2] += tmp_force.z; #if defined(STRESS) /* calculate stresses */ if (us) { forces[stresses + 0] += neigh->dist.x * tmp_force.x; forces[stresses + 1] += neigh->dist.y * tmp_force.y; forces[stresses + 2] += neigh->dist.z * tmp_force.z; forces[stresses + 3] += neigh->dist.x * tmp_force.y; forces[stresses + 4] += neigh->dist.y * tmp_force.z; forces[stresses + 5] += neigh->dist.z * tmp_force.x; } #endif // STRESS } } /* dipole-monopole contributions */ if (dp_alpha[type1] && charge[type2]) { if (dp_b[col] && dp_c[col]) { fnval_sum = fnval_tail + srval; grad_sum = grad_tail + srgrad; } else { fnval_sum = fnval_tail; grad_sum = grad_tail; } rp_i = SPROD(atom->p_ind, neigh->dist_r); fnval = charge[type2] * rp_i * fnval_sum * neigh->r; grad_1 = charge[type2] * rp_i * grad_sum * neigh->r2; grad_2 = charge[type2] * fnval_sum; forces[g_calc.energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad_1 + atom->p_ind.x * grad_2; tmp_force.y = neigh->dist_r.y * grad_1 + atom->p_ind.y * grad_2; tmp_force.z = neigh->dist_r.z * grad_1 + atom->p_ind.z * grad_2; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #if defined(STRESS) /* calculate stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif // STRESS } } /* dipole-dipole contributions */ if (dp_alpha[type1] && dp_alpha[type2]) { pp_ij = SPROD( atom->p_ind, g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind); tmp_1 = 3 * rp_i * rp_j; tmp_2 = 3 * fnval_tail / neigh->r2; fnval = -(tmp_1 - pp_ij) * fnval_tail; grad_1 = (tmp_1 - pp_ij) * grad_tail; grad_2 = 2 * rp_i * rp_j; forces[g_calc.energy_p + h] += fnval; if (uf) { tmp_force.x = grad_1 * neigh->dist.x - tmp_2 * (grad_2 * neigh->dist.x - rp_i * neigh->r * g_config.conf_atoms[neigh->nr - g_mpi.firstatom] .p_ind.x - rp_j * neigh->r * atom->p_ind.x); tmp_force.y = grad_1 * neigh->dist.y - tmp_2 * (grad_2 * neigh->dist.y - rp_i * neigh->r * g_config.conf_atoms[neigh->nr - g_mpi.firstatom] .p_ind.y - rp_j * neigh->r * atom->p_ind.y); tmp_force.z = grad_1 * neigh->dist.z - tmp_2 * (grad_2 * neigh->dist.z - rp_i * neigh->r * g_config.conf_atoms[neigh->nr - g_mpi.firstatom] .p_ind.z - rp_j * neigh->r * atom->p_ind.z); forces[n_i + 0] -= tmp_force.x; forces[n_i + 1] -= tmp_force.y; forces[n_i + 2] -= tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] += tmp_force.x; forces[n_j + 1] += tmp_force.y; forces[n_j + 2] += tmp_force.z; #if defined(STRESS) /* calculate stresses */ if (us) { forces[stresses + 0] += neigh->dist.x * tmp_force.x; forces[stresses + 1] += neigh->dist.y * tmp_force.y; forces[stresses + 2] += neigh->dist.z * tmp_force.z; forces[stresses + 3] += neigh->dist.x * tmp_force.y; forces[stresses + 4] += neigh->dist.y * tmp_force.z; forces[stresses + 5] += neigh->dist.z * tmp_force.x; } #endif // STRESS } } } } /* loop over neighbours */ } /* end F O U R T H loop over atoms */ #endif // DIPOLE /* F I F T H loop: self energy contributions and sum-up force * contributions */ double qq; #if defined(DIPOLE) double pp; #endif // DIPOLE for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; n_i = 3 * (g_config.cnfstart[h] + i); /* self energy contributions */ if (charge[type1]) { qq = charge[type1] * charge[type1]; fnval = DP_EPS * dp_kappa * qq / sqrt(M_PI); forces[g_calc.energy_p + h] -= fnval; } #if defined(DIPOLE) if (dp_alpha[type1]) { pp = SPROD(atom->p_ind, atom->p_ind); fnval = pp / (2 * dp_alpha[type1]); forces[g_calc.energy_p + h] += fnval; } /* alternative dipole self energy including kappa-dependence */ // if (dp_alpha[type1]) { // pp = SPROD(atom->p_ind, atom->p_ind); // fnval = kkk * pp / sqrt(M_PI); // forces[energy_p + h] += fnval; //} #endif // DIPOLE /* sum-up: whole force contributions flow into tmpsum */ if (uf) { #if defined(FWEIGHT) /* Weigh by absolute value of force */ forces[n_i + 0] /= FORCE_EPS + atom->absforce; forces[n_i + 1] /= FORCE_EPS + atom->absforce; forces[n_i + 2] /= FORCE_EPS + atom->absforce; #endif // FWEIGHT #if defined(CONTRIB) if (atom->contrib) #endif // CONTRIB tmpsum += g_config.conf_weight[h] * (dsquare(forces[n_i + 0]) + dsquare(forces[n_i + 1]) + dsquare(forces[n_i + 2])); } } /* end F I F T H loop over atoms */ /* whole energy contributions flow into tmpsum */ forces[g_calc.energy_p + h] /= (double)g_config.inconf[h]; forces[g_calc.energy_p + h] -= g_config.force_0[g_calc.energy_p + h]; tmpsum += g_config.conf_weight[h] * g_param.eweight * dsquare(forces[g_calc.energy_p + h]); #if defined(STRESS) /* whole stress contributions flow into tmpsum */ if (uf && us) { for (i = 0; i < 6; i++) { forces[stresses + i] /= g_config.conf_vol[h - g_mpi.firstconf]; forces[stresses + i] -= g_config.force_0[stresses + i]; tmpsum += g_config.conf_weight[h] * g_param.sweight * dsquare(forces[stresses + i]); } } #endif // STRESS } /* end M A I N loop over configurations */ } /* parallel region */ /* dummy constraints (global) */ #if defined(APOT) /* add punishment for out of bounds (mostly for powell_lsq) */ if (g_mpi.myid == 0) { tmpsum += apot_punish(xi_opt, forces); } #endif // APOT #if defined(MPI) /* reduce global sum */ sum = 0.0; MPI_Reduce(&tmpsum, &sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); /* gather forces, energies, stresses */ if (g_mpi.myid == 0) { /* root node already has data in place */ /* forces */ MPI_Gatherv(MPI_IN_PLACE, g_mpi.myatoms, g_mpi.MPI_VECTOR, forces, g_mpi.atom_len, g_mpi.atom_dist, g_mpi.MPI_VECTOR, 0, MPI_COMM_WORLD); /* energies */ MPI_Gatherv(MPI_IN_PLACE, g_mpi.myconf, MPI_DOUBLE, forces + g_calc.energy_p, g_mpi.conf_len, g_mpi.conf_dist, MPI_DOUBLE, 0, MPI_COMM_WORLD); #if defined(STRESS) /* stresses */ MPI_Gatherv(MPI_IN_PLACE, g_mpi.myconf, g_mpi.MPI_STENS, forces + g_calc.stress_p, g_mpi.conf_len, g_mpi.conf_dist, g_mpi.MPI_STENS, 0, MPI_COMM_WORLD); #endif // STRESS } else { /* forces */ MPI_Gatherv(forces + g_mpi.firstatom * 3, g_mpi.myatoms, g_mpi.MPI_VECTOR, forces, g_mpi.atom_len, g_mpi.atom_dist, g_mpi.MPI_VECTOR, 0, MPI_COMM_WORLD); /* energies */ MPI_Gatherv(forces + g_calc.energy_p + g_mpi.firstconf, g_mpi.myconf, MPI_DOUBLE, forces + g_calc.energy_p, g_mpi.conf_len, g_mpi.conf_dist, MPI_DOUBLE, 0, MPI_COMM_WORLD); #if defined(STRESS) /* stresses */ MPI_Gatherv(forces + g_calc.stress_p + 6 * g_mpi.firstconf, g_mpi.myconf, g_mpi.MPI_STENS, forces + g_calc.stress_p, g_mpi.conf_len, g_mpi.conf_dist, g_mpi.MPI_STENS, 0, MPI_COMM_WORLD); #endif // STRESS } #else sum = tmpsum; /* global sum = local sum */ #endif // MPI /* root process exits this function now */ if (g_mpi.myid == 0) { g_calc.fcalls++; /* Increase function call counter */ if (isnan(sum)) { #if defined(DEBUG) printf("\n--> Force is nan! <--\n\n"); #endif // DEBUG return 10e10; } else return sum; } } /* once a non-root process arrives here, all is done. */ return -1.0; }
double calc_forces_elstat(double *xi_opt, double *forces, int flag) { double tmpsum, sum = 0.; int first, col, ne, size, i; double *xi = NULL; apot_table_t *apt = &apot_table; double charge[ntypes]; double sum_charges; double dp_kappa; #ifdef DIPOLE double dp_alpha[ntypes]; double dp_b[apt->number]; double dp_c[apt->number]; #endif /* DIPOLE */ switch (format) { case 0: xi = calc_pot.table; break; case 3: /* fall through */ case 4: xi = xi_opt; /* calc-table is opt-table */ break; case 5: xi = calc_pot.table; /* we need to update the calc-table */ } ne = apot_table.total_ne_par; size = apt->number; /* This is the start of an infinite loop */ while (1) { tmpsum = 0.; /* sum of squares of local process */ #if defined APOT && !defined MPI if (format == 0) { apot_check_params(xi_opt); update_calc_table(xi_opt, xi, 0); } #endif /* APOT && !MPI */ #ifdef MPI /* exchange potential and flag value */ #ifndef APOT MPI_Bcast(xi, calc_pot.len, MPI_DOUBLE, 0, MPI_COMM_WORLD); #endif /* APOT */ MPI_Bcast(&flag, 1, MPI_INT, 0, MPI_COMM_WORLD); if (flag == 1) break; /* Exception: flag 1 means clean up */ #ifdef APOT if (myid == 0) apot_check_params(xi_opt); MPI_Bcast(xi_opt, ndimtot, MPI_DOUBLE, 0, MPI_COMM_WORLD); if (format == 0) update_calc_table(xi_opt, xi, 0); #else /* APOT */ /* if flag==2 then the potential parameters have changed -> sync */ if (flag == 2) potsync(); #endif /* APOT */ #endif /* MPI */ /* local arrays for electrostatic parameters */ sum_charges = 0; for (i = 0; i < ntypes - 1; i++) { if (xi_opt[2 * size + ne + i]) { charge[i] = xi_opt[2 * size + ne + i]; sum_charges += apt->ratio[i] * charge[i]; } else { charge[i] = 0.; } } apt->last_charge = -sum_charges / apt->ratio[ntypes - 1]; charge[ntypes - 1] = apt->last_charge; if (xi_opt[2 * size + ne + ntypes - 1]) { dp_kappa = xi_opt[2 * size + ne + ntypes - 1]; } else { dp_kappa = 0.; } #ifdef DIPOLE for (i = 0; i < ntypes; i++) { if (xi_opt[2 * size + ne + ntypes + i]) { dp_alpha[i] = xi_opt[2 * size + ne + ntypes + i]; } else { dp_alpha[i] = 0.; } } for (i = 0; i < size; i++) { if (xi_opt[2 * size + ne + 2 * ntypes + i]) { dp_b[i] = xi_opt[2 * size + ne + 2 * ntypes + i]; } else { dp_b[i] = 0.; } if (xi_opt[3 * size + ne + 2 * ntypes + i]) { dp_c[i] = xi_opt[3 * size + ne + 2 * ntypes + i]; } else { dp_c[i] = 0.; } } #endif /* DIPOLE */ /* init second derivatives for splines */ for (col = 0; col < paircol; col++) { first = calc_pot.first[col]; if (format == 3 || format == 0) { spline_ed(calc_pot.step[col], xi + first, calc_pot.last[col] - first + 1, *(xi + first - 2), 0.0, calc_pot.d2tab + first); } else { /* format >= 4 ! */ spline_ne(calc_pot.xcoord + first, xi + first, calc_pot.last[col] - first + 1, *(xi + first - 2), 0.0, calc_pot.d2tab + first); } } #ifndef MPI myconf = nconf; #endif /* MPI */ /* region containing loop over configurations, also OMP-parallelized region */ { int self; vector tmp_force; int h, j, type1, type2, uf, us, stresses; int n_i, n_j; double fnval, grad, fnval_tail, grad_tail, grad_i, grad_j; #ifdef DIPOLE double p_sr_tail; #endif /* DIPOLE */ atom_t *atom; neigh_t *neigh; /* loop over configurations: M A I N LOOP CONTAINING ALL ATOM-LOOPS */ for (h = firstconf; h < firstconf + myconf; h++) { uf = conf_uf[h - firstconf]; #ifdef STRESS us = conf_us[h - firstconf]; #endif /* STRESS */ /* reset energies and stresses */ forces[energy_p + h] = 0.0; #ifdef STRESS stresses = stress_p + 6 * h; for (i = 0; i < 6; i++) forces[stresses + i] = 0.0; #endif /* STRESS */ #ifdef DIPOLE /* reset dipoles and fields: LOOP Z E R O */ for (i = 0; i < inconf[h]; i++) { atom = conf_atoms + i + cnfstart[h] - firstatom; atom->E_stat.x = 0.0; atom->E_stat.y = 0.0; atom->E_stat.z = 0.0; atom->p_sr.x = 0.0; atom->p_sr.y = 0.0; atom->p_sr.z = 0.0; } #endif /* DIPOLE */ /* F I R S T LOOP OVER ATOMS: reset forces, dipoles */ for (i = 0; i < inconf[h]; i++) { /* atoms */ n_i = 3 * (cnfstart[h] + i); if (uf) { forces[n_i + 0] = -force_0[n_i + 0]; forces[n_i + 1] = -force_0[n_i + 1]; forces[n_i + 2] = -force_0[n_i + 2]; } else { forces[n_i + 0] = 0.0; forces[n_i + 1] = 0.0; forces[n_i + 2] = 0.0; } } /* end F I R S T LOOP */ /* S E C O N D loop: calculate short-range and monopole forces, calculate static field- and dipole-contributions */ for (i = 0; i < inconf[h]; i++) { /* atoms */ atom = conf_atoms + i + cnfstart[h] - firstatom; type1 = atom->type; n_i = 3 * (cnfstart[h] + i); for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* updating tail-functions - only necessary with variing kappa */ if (!apt->sw_kappa) elstat_shift(neigh->r, dp_kappa, &neigh->fnval_el, &neigh->grad_el, &neigh->ggrad_el); /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + cnfstart[h]) ? 1 : 0; /* calculate short-range forces */ if (neigh->r < calc_pot.end[col]) { if (uf) { fnval = splint_comb_dir(&calc_pot, xi, neigh->slot[0], neigh->shift[0], neigh->step[0], &grad); } else { fnval = splint_dir(&calc_pot, xi, neigh->slot[0], neigh->shift[0], neigh->step[0]); } /* avoid double counting if atom is interacting with a copy of itself */ if (self) { fnval *= 0.5; grad *= 0.5; } forces[energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad; tmp_force.y = neigh->dist_r.y * grad; tmp_force.z = neigh->dist_r.z * grad; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #ifdef STRESS /* calculate pair stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif /* STRESS */ } } /* calculate monopole forces */ if (neigh->r < dp_cut && (charge[type1] || charge[type2])) { fnval_tail = neigh->fnval_el; grad_tail = neigh->grad_el; grad_i = charge[type2] * grad_tail; if (type1 == type2) { grad_j = grad_i; } else { grad_j = charge[type1] * grad_tail; } fnval = charge[type1] * charge[type2] * fnval_tail; grad = charge[type1] * grad_i; if (self) { grad_i *= 0.5; grad_j *= 0.5; fnval *= 0.5; grad *= 0.5; } forces[energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist.x * grad; tmp_force.y = neigh->dist.y * grad; tmp_force.z = neigh->dist.z * grad; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #ifdef STRESS /* calculate coulomb stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif /* STRESS */ } #ifdef DIPOLE /* calculate static field-contributions */ atom->E_stat.x += neigh->dist.x * grad_i; atom->E_stat.y += neigh->dist.y * grad_i; atom->E_stat.z += neigh->dist.z * grad_i; conf_atoms[neigh->nr - firstatom].E_stat.x -= neigh->dist.x * grad_j; conf_atoms[neigh->nr - firstatom].E_stat.y -= neigh->dist.y * grad_j; conf_atoms[neigh->nr - firstatom].E_stat.z -= neigh->dist.z * grad_j; /* calculate short-range dipoles */ if (dp_alpha[type1] && dp_b[col] && dp_c[col]) { p_sr_tail = grad_tail * neigh->r * shortrange_value(neigh->r, dp_alpha[type1], dp_b[col], dp_c[col]); atom->p_sr.x += charge[type2] * neigh->dist_r.x * p_sr_tail; atom->p_sr.y += charge[type2] * neigh->dist_r.y * p_sr_tail; atom->p_sr.z += charge[type2] * neigh->dist_r.z * p_sr_tail; } if (dp_alpha[type2] && dp_b[col] && dp_c[col] && !self) { p_sr_tail = grad_tail * neigh->r * shortrange_value(neigh->r, dp_alpha[type2], dp_b[col], dp_c[col]); conf_atoms[neigh->nr - firstatom].p_sr.x -= charge[type1] * neigh->dist_r.x * p_sr_tail; conf_atoms[neigh->nr - firstatom].p_sr.y -= charge[type1] * neigh->dist_r.y * p_sr_tail; conf_atoms[neigh->nr - firstatom].p_sr.z -= charge[type1] * neigh->dist_r.z * p_sr_tail; } #endif /* DIPOLE */ } } /* loop over neighbours */ } /* end S E C O N D loop over atoms */ #ifdef DIPOLE /* T H I R D loop: calculate whole dipole moment for every atom */ double rp, dp_sum; int dp_converged = 0, dp_it = 0; double max_diff = 10; while (dp_converged == 0) { dp_sum = 0; for (i = 0; i < inconf[h]; i++) { /* atoms */ atom = conf_atoms + i + cnfstart[h] - firstatom; type1 = atom->type; if (dp_alpha[type1]) { if (dp_it) { /* note: mixing parameter is different from that on in IMD */ atom->E_tot.x = (1 - dp_mix) * atom->E_ind.x + dp_mix * atom->E_old.x + atom->E_stat.x; atom->E_tot.y = (1 - dp_mix) * atom->E_ind.y + dp_mix * atom->E_old.y + atom->E_stat.y; atom->E_tot.z = (1 - dp_mix) * atom->E_ind.z + dp_mix * atom->E_old.z + atom->E_stat.z; } else { atom->E_tot.x = atom->E_ind.x + atom->E_stat.x; atom->E_tot.y = atom->E_ind.y + atom->E_stat.y; atom->E_tot.z = atom->E_ind.z + atom->E_stat.z; } atom->p_ind.x = dp_alpha[type1] * atom->E_tot.x + atom->p_sr.x; atom->p_ind.y = dp_alpha[type1] * atom->E_tot.y + atom->p_sr.y; atom->p_ind.z = dp_alpha[type1] * atom->E_tot.z + atom->p_sr.z; atom->E_old.x = atom->E_ind.x; atom->E_old.y = atom->E_ind.y; atom->E_old.z = atom->E_ind.z; atom->E_ind.x = 0.; atom->E_ind.y = 0.; atom->E_ind.z = 0.; } } for (i = 0; i < inconf[h]; i++) { /* atoms */ atom = conf_atoms + i + cnfstart[h] - firstatom; type1 = atom->type; for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + cnfstart[h]) ? 1 : 0; if (neigh->r < dp_cut && dp_alpha[type1] && dp_alpha[type2]) { rp = SPROD(conf_atoms[neigh->nr - firstatom].p_ind, neigh->dist_r); atom->E_ind.x += neigh->grad_el * (3 * rp * neigh->dist_r.x - conf_atoms[neigh->nr - firstatom].p_ind.x); atom->E_ind.y += neigh->grad_el * (3 * rp * neigh->dist_r.y - conf_atoms[neigh->nr - firstatom].p_ind.y); atom->E_ind.z += neigh->grad_el * (3 * rp * neigh->dist_r.z - conf_atoms[neigh->nr - firstatom].p_ind.z); if (!self) { rp = SPROD(atom->p_ind, neigh->dist_r); conf_atoms[neigh->nr - firstatom].E_ind.x += neigh->grad_el * (3 * rp * neigh->dist_r.x - atom->p_ind.x); conf_atoms[neigh->nr - firstatom].E_ind.y += neigh->grad_el * (3 * rp * neigh->dist_r.y - atom->p_ind.y); conf_atoms[neigh->nr - firstatom].E_ind.z += neigh->grad_el * (3 * rp * neigh->dist_r.z - atom->p_ind.z); } } } } for (i = 0; i < inconf[h]; i++) { /* atoms */ atom = conf_atoms + i + cnfstart[h] - firstatom; type1 = atom->type; if (dp_alpha[type1]) { dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.x - atom->E_ind.x)); dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.y - atom->E_ind.y)); dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.z - atom->E_ind.z)); } } dp_sum /= 3 * inconf[h]; dp_sum = sqrt(dp_sum); if (dp_it) { if ((dp_sum > max_diff) || (dp_it > 50)) { dp_converged = 1; for (i = 0; i < inconf[h]; i++) { /* atoms */ atom = conf_atoms + i + cnfstart[h] - firstatom; type1 = atom->type; if (dp_alpha[type1]) { atom->p_ind.x = dp_alpha[type1] * atom->E_stat.x + atom->p_sr.x; atom->p_ind.y = dp_alpha[type1] * atom->E_stat.y + atom->p_sr.y; atom->p_ind.z = dp_alpha[type1] * atom->E_stat.z + atom->p_sr.z; atom->E_ind.x = atom->E_stat.x; atom->E_ind.y = atom->E_stat.y; atom->E_ind.z = atom->E_stat.z; } } } } if (dp_sum < dp_tol) { dp_converged = 1; } dp_it++; } /* end T H I R D loop over atoms */ /* F O U R T H loop: calculate monopole-dipole and dipole-dipole forces */ double rp_i, rp_j, pp_ij, tmp_1, tmp_2; double grad_1, grad_2, srval, srgrad, srval_tail, srgrad_tail, fnval_sum, grad_sum; for (i = 0; i < inconf[h]; i++) { /* atoms */ atom = conf_atoms + i + cnfstart[h] - firstatom; type1 = atom->type; n_i = 3 * (cnfstart[h] + i); for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + cnfstart[h]) ? 1 : 0; if (neigh->r < dp_cut && (dp_alpha[type1] || dp_alpha[type2])) { fnval_tail = -neigh->grad_el; grad_tail = -neigh->ggrad_el; if (dp_b[col] && dp_c[col]) { shortrange_term(neigh->r, dp_b[col], dp_c[col], &srval_tail, &srgrad_tail); srval = fnval_tail * srval_tail; srgrad = fnval_tail * srgrad_tail + grad_tail * srval_tail; } if (self) { fnval_tail *= 0.5; grad_tail *= 0.5; } /* monopole-dipole contributions */ if (charge[type1] && dp_alpha[type2]) { if (dp_b[col] && dp_c[col]) { fnval_sum = fnval_tail + srval; grad_sum = grad_tail + srgrad; } else { fnval_sum = fnval_tail; grad_sum = grad_tail; } rp_j = SPROD(conf_atoms[neigh->nr - firstatom].p_ind, neigh->dist_r); fnval = charge[type1] * rp_j * fnval_sum * neigh->r; grad_1 = charge[type1] * rp_j * grad_sum * neigh->r2; grad_2 = charge[type1] * fnval_sum; forces[energy_p + h] -= fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad_1 + conf_atoms[neigh->nr - firstatom].p_ind.x * grad_2; tmp_force.y = neigh->dist_r.y * grad_1 + conf_atoms[neigh->nr - firstatom].p_ind.y * grad_2; tmp_force.z = neigh->dist_r.z * grad_1 + conf_atoms[neigh->nr - firstatom].p_ind.z * grad_2; forces[n_i + 0] -= tmp_force.x; forces[n_i + 1] -= tmp_force.y; forces[n_i + 2] -= tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] += tmp_force.x; forces[n_j + 1] += tmp_force.y; forces[n_j + 2] += tmp_force.z; #ifdef STRESS /* calculate stresses */ if (us) { forces[stresses + 0] += neigh->dist.x * tmp_force.x; forces[stresses + 1] += neigh->dist.y * tmp_force.y; forces[stresses + 2] += neigh->dist.z * tmp_force.z; forces[stresses + 3] += neigh->dist.x * tmp_force.y; forces[stresses + 4] += neigh->dist.y * tmp_force.z; forces[stresses + 5] += neigh->dist.z * tmp_force.x; } #endif /* STRESS */ } } /* dipole-monopole contributions */ if (dp_alpha[type2] && charge[type2]) { if (dp_b[col] && dp_c[col]) { fnval_sum = fnval_tail + srval; grad_sum = grad_tail + srgrad; } else { fnval_sum = fnval_tail; grad_sum = grad_tail; } rp_i = SPROD(atom->p_ind, neigh->dist_r); fnval = charge[type2] * rp_i * fnval_sum * neigh->r; grad_1 = charge[type2] * rp_i * grad_sum * neigh->r2; grad_2 = charge[type2] * fnval_sum; forces[energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad_1 + atom->p_ind.x * grad_2; tmp_force.y = neigh->dist_r.y * grad_1 + atom->p_ind.y * grad_2; tmp_force.z = neigh->dist_r.z * grad_1 + atom->p_ind.z * grad_2; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #ifdef STRESS /* calculate stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif /* STRESS */ } } /* dipole-dipole contributions */ if (dp_alpha[type1] && dp_alpha[type2]) { pp_ij = SPROD(atom->p_ind, conf_atoms[neigh->nr - firstatom].p_ind); tmp_1 = 3 * rp_i * rp_j; tmp_2 = 3 * fnval_tail / neigh->r2; fnval = -(tmp_1 - pp_ij) * fnval_tail; grad_1 = (tmp_1 - pp_ij) * grad_tail; grad_2 = 2 * rp_i * rp_j; forces[energy_p + h] += fnval; if (uf) { tmp_force.x = grad_1 * neigh->dist.x - tmp_2 * (grad_2 * neigh->dist.x - rp_i * neigh->r * conf_atoms[neigh->nr - firstatom].p_ind.x - rp_j * neigh->r * atom->p_ind.x); tmp_force.y = grad_1 * neigh->dist.y - tmp_2 * (grad_2 * neigh->dist.y - rp_i * neigh->r * conf_atoms[neigh->nr - firstatom].p_ind.y - rp_j * neigh->r * atom->p_ind.y); tmp_force.z = grad_1 * neigh->dist.z - tmp_2 * (grad_2 * neigh->dist.z - rp_i * neigh->r * conf_atoms[neigh->nr - firstatom].p_ind.z - rp_j * neigh->r * atom->p_ind.z); forces[n_i + 0] -= tmp_force.x; forces[n_i + 1] -= tmp_force.y; forces[n_i + 2] -= tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] += tmp_force.x; forces[n_j + 1] += tmp_force.y; forces[n_j + 2] += tmp_force.z; #ifdef STRESS /* calculate stresses */ if (us) { forces[stresses + 0] += neigh->dist.x * tmp_force.x; forces[stresses + 1] += neigh->dist.y * tmp_force.y; forces[stresses + 2] += neigh->dist.z * tmp_force.z; forces[stresses + 3] += neigh->dist.x * tmp_force.y; forces[stresses + 4] += neigh->dist.y * tmp_force.z; forces[stresses + 5] += neigh->dist.z * tmp_force.x; } #endif /* STRESS */ } } } } /* loop over neighbours */ } /* end F O U R T H loop over atoms */ #endif /* DIPOLE */ /* F I F T H loop: self energy contributions and sum-up force contributions */ double qq; #ifdef DIPOLE double pp; #endif /* DIPOLE */ for (i = 0; i < inconf[h]; i++) { /* atoms */ atom = conf_atoms + i + cnfstart[h] - firstatom; type1 = atom->type; n_i = 3 * (cnfstart[h] + i); /* self energy contributions */ if (charge[type1]) { qq = charge[type1] * charge[type1]; fnval = dp_eps * dp_kappa * qq / sqrt(M_PI); forces[energy_p + h] -= fnval; } #ifdef DIPOLE if (dp_alpha[type1]) { pp = SPROD(atom->p_ind, atom->p_ind); fnval = pp / (2 * dp_alpha[type1]); forces[energy_p + h] += fnval; } /* alternative dipole self energy including kappa-dependence */ //if (dp_alpha[type1]) { // pp = SPROD(atom->p_ind, atom->p_ind); // fnval = kkk * pp / sqrt(M_PI); // forces[energy_p + h] += fnval; //} #endif /* DIPOLE */ /* sum-up: whole force contributions flow into tmpsum */ if (uf) { #ifdef FWEIGHT /* Weigh by absolute value of force */ forces[n_i + 0] /= FORCE_EPS + atom->absforce; forces[n_i + 1] /= FORCE_EPS + atom->absforce; forces[n_i + 2] /= FORCE_EPS + atom->absforce; #endif /* FWEIGHT */ #ifdef CONTRIB if (atom->contrib) #endif /* CONTRIB */ tmpsum += conf_weight[h] * (dsquare(forces[n_i + 0]) + dsquare(forces[n_i + 1]) + dsquare(forces[n_i + 2])); } } /* end F I F T H loop over atoms */ /* whole energy contributions flow into tmpsum */ forces[energy_p + h] /= (double)inconf[h]; forces[energy_p + h] -= force_0[energy_p + h]; tmpsum += conf_weight[h] * eweight * dsquare(forces[energy_p + h]); #ifdef STRESS /* whole stress contributions flow into tmpsum */ if (uf && us) { for (i = 0; i < 6; i++) { forces[stresses + i] /= conf_vol[h - firstconf]; forces[stresses + i] -= force_0[stresses + i]; tmpsum += conf_weight[h] * sweight * dsquare(forces[stresses + i]); } } #endif /* STRESS */ } /* end M A I N loop over configurations */ } /* parallel region */ /* dummy constraints (global) */ #ifdef APOT /* add punishment for out of bounds (mostly for powell_lsq) */ if (myid == 0) { tmpsum += apot_punish(xi_opt, forces); } #endif /* APOT */ sum = tmpsum; /* global sum = local sum */ #ifdef MPI /* reduce global sum */ sum = 0.; MPI_Reduce(&tmpsum, &sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); /* gather forces, energies, stresses */ if (myid == 0) { /* root node already has data in place */ /* forces */ MPI_Gatherv(MPI_IN_PLACE, myatoms, MPI_VECTOR, forces, atom_len, atom_dist, MPI_VECTOR, 0, MPI_COMM_WORLD); /* energies */ MPI_Gatherv(MPI_IN_PLACE, myconf, MPI_DOUBLE, forces + natoms * 3, conf_len, conf_dist, MPI_DOUBLE, 0, MPI_COMM_WORLD); /* stresses */ MPI_Gatherv(MPI_IN_PLACE, myconf, MPI_STENS, forces + natoms * 3 + nconf, conf_len, conf_dist, MPI_STENS, 0, MPI_COMM_WORLD); } else { /* forces */ MPI_Gatherv(forces + firstatom * 3, myatoms, MPI_VECTOR, forces, atom_len, atom_dist, MPI_VECTOR, 0, MPI_COMM_WORLD); /* energies */ MPI_Gatherv(forces + natoms * 3 + firstconf, myconf, MPI_DOUBLE, forces + natoms * 3, conf_len, conf_dist, MPI_DOUBLE, 0, MPI_COMM_WORLD); /* stresses */ MPI_Gatherv(forces + natoms * 3 + nconf + 6 * firstconf, myconf, MPI_STENS, forces + natoms * 3 + nconf, conf_len, conf_dist, MPI_STENS, 0, MPI_COMM_WORLD); } #endif /* MPI */ /* root process exits this function now */ if (myid == 0) { fcalls++; /* Increase function call counter */ if (isnan(sum)) { #ifdef DEBUG printf("\n--> Force is nan! <--\n\n"); #endif /* DEBUG */ return 10e10; } else return sum; } } /* once a non-root process arrives here, all is done. */ return -1.; }
void calc_forces_neb(void) { real dl2=0.0, dr2=0.0, drl=0.0, d2=0.0, f2=0.0, f2max=0.0, drlmax=0.0,df=0.0; real tmp,tmp1,tmp2, cosphi, fphi, src[3], dest[3], *d=pos; real kr,kl; int k, i; int var_k=0; int myimage,maximage; real V_previous, V_actual, V_next; real deltaVmin,deltaVmax; real normdr,normdl,inormd; real Eref,Emax,Emin,delta_E; real ratio_plus,ratio_minus,abs_next,abs_previous ; real k_sum, k_diff,tmpl,tmpr; real tmp_neb_ks[NEB_MAXNREP] INIT(zero100); real felastfact=0.0; myimage = myrank; /* get info about the energies of the different images */ neb_image_energies[ myimage]=tot_pot_energy; MPI_Allreduce(neb_image_energies , neb_epot_im, NEB_MAXNREP, REAL, MPI_SUM, MPI_COMM_WORLD); Emax=-999999999999999; Emin=999999999999999; for(i=0;i<neb_nrep;i++) { if(neb_epot_im[i]>=Emax) { Emax=neb_epot_im[i]; maximage=i; } if(neb_epot_im[i]<=Emin) { Emin=neb_epot_im[i]; } } if(steps == neb_cineb_start) { if(neb_climbing_image > 0) { if(myrank==0) { if( neb_climbing_image == maximage) printf("Starting climbing image = %d (= max_Epot = %lf)\n",neb_climbing_image, Emax); else printf("Starting climbing image = %d \n WARNING: %d != %d with max_Epot = %lf)\n", \ neb_climbing_image,neb_climbing_image,maximage, Emax); } } else { neb_climbing_image = maximage; if(myrank==0) { printf("Starting climbing image, image set to %d (= max_Epot = %lf)\n",maximage, Emax); } } } /* determine variable spring constants (jcp113 p. 9901) */ tmp_neb_ks[myimage]=0; if(myrank != 0 && myrank != neb_nrep-1) { V_previous = neb_epot_im[myimage-1]; V_actual = neb_epot_im[myimage]; V_next = neb_epot_im[myimage+1]; if ( neb_kmax > 0 & neb_kmin >0 && steps > neb_vark_start) { var_k=1; k_sum = neb_kmax + neb_kmin; k_diff = neb_kmax - neb_kmin; delta_E = Emax - Emin; if (delta_E > 1.0e-12) { tmp_neb_ks[myimage] = 0.5 *(k_sum - k_diff * cos(3.141592653589793238*( neb_epot_im[myimage] - Emin )/delta_E )); } } else { tmp_neb_ks[myimage] = neb_k; } } MPI_Allreduce(tmp_neb_ks , neb_ks, NEB_MAXNREP, REAL, MPI_SUM, MPI_COMM_WORLD); /* exchange positions with neighbor replicas */ neb_sendrecv_pos(); /* determine tangent vector and the elastic spring force */ if(myrank != 0 && myrank != neb_nrep-1) { dl2=0.0;d2=0.0;dr2=0.0; kr = 0.5 * (neb_ks[myimage]+neb_ks[myimage+1]); kl = 0.5 * (neb_ks[myimage]+neb_ks[myimage-1]); /* preparation: calculate distance to left and right immage */ for (i=0; i<DIM*natoms; i+=DIM) { vektor dr,dl; real x; dl.x = pos [i ] - pos_l[i ]; dl.y = pos [i+1] - pos_l[i+1]; dl.z = pos [i+2] - pos_l[i+2]; dr.x = pos_r[i ] - pos [i ]; dr.y = pos_r[i+1] - pos [i+1]; dr.z = pos_r[i+2] - pos [i+2]; /* apply periodic boundary conditions */ if (1==pbc_dirs.x) { x = - round( SPROD(dl,tbox_x) ); dl.x += x * box_x.x; dl.y += x * box_x.y; dl.z += x * box_x.z; x = - round( SPROD(dr,tbox_x) ); dr.x += x * box_x.x; dr.y += x * box_x.y; dr.z += x * box_x.z; } if (1==pbc_dirs.y) { x = - round( SPROD(dl,tbox_y) ); dl.x += x * box_y.x; dl.y += x * box_y.y; dl.z += x * box_y.z; x = - round( SPROD(dr,tbox_y) ); dr.x += x * box_y.x; dr.y += x * box_y.y; dr.z += x * box_y.z; } if (1==pbc_dirs.z) { x = - round( SPROD(dl,tbox_z) ); dl.x += x * box_z.x; dl.y += x * box_z.y; dl.z += x * box_z.z; x = - round( SPROD(dr,tbox_z) ); dr.x += x * box_z.x; dr.y += x * box_z.y; dr.z += x * box_z.z; } dRleft[i ] = dl.x; dRleft[i+1] = dl.y; dRleft[i+2] = dl.z; dRright[i ] = dr.x; dRright[i+1] = dr.y; dRright[i+2] = dr.z; } /* computation of the tangent requires 2 steps: determination of the direction and then normalization */ /* here we use only the improved tangent method */ if ( ( V_next > V_actual ) && ( V_actual > V_previous ) ) { for (i=0; i<DIM*natoms; i+=DIM) { tau[i ] = dRright[i ]; tau[i+1] = dRright[i+1]; tau[i+2] = dRright[i+2]; d2 += dRright[i ]*dRright[i ]; d2 += dRright[i+1]*dRright[i+1]; d2 += dRright[i+2]*dRright[i+2]; } tmp=1.0/sqrt(d2); for (i=0; i<DIM*natoms; i+=DIM) { tau[i ] *= tmp; tau[i+1] *= tmp; tau[i+2] *= tmp; if (var_k==1) { felastfact += tau[i ] * ( -kr *dRright[i ] + kl * dRleft[i ]); felastfact += tau[i+1] * ( -kr *dRright[i+1] + kl * dRleft[i+1]); felastfact += tau[i+2] * ( -kr *dRright[i+2] + kl * dRleft[i+2]); } else { felastfact += tau[i ] * ( - dRright[i ] + dRleft[i ]); felastfact += tau[i+1] * ( - dRright[i+1] + dRleft[i+1]); felastfact += tau[i+2] * ( - dRright[i+2] + dRleft[i+2]); } } } else if ( ( V_next < V_actual ) && ( V_actual < V_previous ) ) { for (i=0; i<DIM*natoms; i+=DIM) { tau[i ] = dRleft[i ]; tau[i+1] = dRleft[i+1]; tau[i+2] = dRleft[i+2]; d2 += dRleft[i ]*dRleft[i ]; d2 += dRleft[i+1]*dRleft[i+1]; d2 += dRleft[i+2]*dRleft[i+2]; } tmp=1.0/sqrt(d2); for (i=0; i<DIM*natoms; i+=DIM) { tau[i ] *= tmp; tau[i+1] *= tmp; tau[i+2] *= tmp; if (var_k==1) { felastfact += tau[i ] * ( -kr *dRright[i ] + kl * dRleft[i ]); felastfact += tau[i+1] * ( -kr *dRright[i+1] + kl * dRleft[i+1]); felastfact += tau[i+2] * ( -kr *dRright[i+2] + kl * dRleft[i+2]); } else { felastfact += tau[i ] * ( - dRright[i ] + dRleft[i ]); felastfact += tau[i+1] * ( - dRright[i+1] + dRleft[i+1]); felastfact += tau[i+2] * ( - dRright[i+2] + dRleft[i+2]); } } } else { abs_next = FABS( V_next - V_actual ); abs_previous = FABS( V_previous - V_actual ); deltaVmax = MAX( abs_next, abs_previous ); deltaVmin = MIN( abs_next, abs_previous ); for (i=0; i<DIM*natoms; i+=DIM) { dr2 += dRright[i ]*dRright[i ]; dr2 += dRright[i+1]*dRright[i+1]; dr2 += dRright[i+2]*dRright[i+2]; dl2 += dRleft[i ]*dRleft[i ]; dl2 += dRleft[i+1]*dRleft[i+1]; dl2 += dRleft[i+2]*dRleft[i+2]; } tmpl=1.0/sqrt(dl2); tmpr=1.0/sqrt(dr2); for (i=0; i<DIM*natoms; i+=DIM) { vektor dl, dr; dr.x = dRright[i ]*tmpr; dr.y = dRright[i+1]*tmpr; dr.z = dRright[i+2]*tmpr; dl.x = dRleft[i ]*tmpl; dl.y = dRleft[i+1]*tmpl; dl.z = dRleft[i+2]*tmpl; if (V_next > V_previous ) { tau[i ] = dr.x * deltaVmax + dl.x * deltaVmin ; tau[i+1] = dr.y * deltaVmax + dl.y * deltaVmin; tau[i+2] = dr.z * deltaVmax + dl.z * deltaVmin; } else if ( V_next < V_previous ) { tau[i ] = dr.x * deltaVmin + dl.x * deltaVmax ; tau[i+1] = dr.y * deltaVmin + dl.y * deltaVmax; tau[i+2] = dr.z * deltaVmin + dl.z * deltaVmax; } else { tau[i ] = dr.x + dl.x; tau[i+1] = dr.y + dl.y; tau[i+2] = dr.z + dl.z; } d2 += tau[i ]*tau[i ]; d2 += tau[i+1]*tau[i+1]; d2 += tau[i+2]*tau[i+2]; } tmp=1.0/sqrt(d2); for (i=0; i<DIM*natoms; i+=DIM) { tau[i ] *= tmp; tau[i+1] *= tmp; tau[i+2] *= tmp; if (var_k==1) { felastfact += tau[i ] * ( -kr *dRright[i ] + kl * dRleft[i ]); felastfact += tau[i+1] * ( -kr *dRright[i+1] + kl * dRleft[i+1]); felastfact += tau[i+2] * ( -kr *dRright[i+2] + kl * dRleft[i+2]); } else { felastfact += tau[i ] * ( - dRright[i ] + dRleft[i ]); felastfact += tau[i+1] * ( - dRright[i+1] + dRleft[i+1]); felastfact += tau[i+2] * ( - dRright[i+2] + dRleft[i+2]); } } } /* finally construct the spring force */ for (i=0; i<DIM*natoms; i+=DIM) { if (var_k==1) { f[i ] = - tau[i ] *felastfact; f[i+1] = - tau[i+1] *felastfact; f[i+2] = - tau[i+2] *felastfact; } else { f[i ] = - neb_k * tau[i ] *felastfact; f[i+1] = - neb_k * tau[i+1] *felastfact; f[i+2] = - neb_k * tau[i+2] *felastfact; } } }// end if(myrank != 0 && mrank != neb_nrep-1) /* calculate the neb-force */ if(myrank != 0 && myrank != neb_nrep-1) { // first scalar product of -force and tangent vector tmp = 0.0; for (k=0; k<NCELLS; k++) { cell *p = CELLPTR(k); for (i=0; i<p->n; i++) { int n = NUMMER(p,i); tmp -= tau X(n) * KRAFT(p,i,X); tmp -= tau Y(n) * KRAFT(p,i,Y); tmp -= tau Z(n) * KRAFT(p,i,Z); } } // add tmp times the tangent vector // and the spring force for (k=0; k<NCELLS; k++) { cell *p = CELLPTR(k); for (i=0; i<p->n; i++) { int n = NUMMER(p,i); if(myimage == neb_climbing_image && (steps >= neb_cineb_start)) { KRAFT(p,i,X) += 2.0*tmp * tau X(n); KRAFT(p,i,Y) += 2.0*tmp * tau Y(n); KRAFT(p,i,Z) += 2.0*tmp * tau Z(n); } else { KRAFT(p,i,X) += tmp * tau X(n) + f X(n); KRAFT(p,i,Y) += tmp * tau Y(n) + f Y(n); KRAFT(p,i,Z) += tmp * tau Z(n) + f Z(n); } } } } // if(myrank != 0 && mrank != neb_nrep-1) }
void do_elco_pair(cell *p, cell *q, vektor pbc) { int i, j; vektor d; int p_typ, q_typ, col1, inc = ntypes * ntypes, is_short = 0; real r2, tmp, phi, dphi, ddphi, dddphi; #ifdef EAM real rho_h; int col2; real rho_i_strich, rho_i_zweistrich, rho_i_dreistrich; real rho_j_strich, rho_j_zweistrich, rho_j_dreistrich; #endif /* For each atom in first cell */ for (i=0; i<p->n; ++i) /* For each atom in neighbouring cell */ for (j=((p==q) ? i+1 : 0); j<q->n; ++j) { /* Calculate distance */ d.x = q->ort[j].x - p->ort[i].x + pbc.x; d.y = q->ort[j].y - p->ort[i].y + pbc.y; #ifndef TWOD d.z = q->ort[j].z - p->ort[i].z + pbc.z; #endif r2 = SPROD(d,d); p_typ = p->sorte[i]; q_typ = q->sorte[j]; col1 = p_typ * ntypes + q_typ; if ( r2 <= pair_pot.end[col1] ) { PAIR_INT4(phi, dphi, ddphi, dddphi, pair_pot, col1, inc, r2, is_short) /* Compute potential energy */ epot += phi; /* Compute stress and elastic constants */ tmp = d.x * d.x * dphi; p->stress[i].xx += tmp; q->stress[j].xx += tmp; sigma.xx += 2.0 * tmp; tmp = d.x * d.y * dphi; p->stress[i].xy += tmp; q->stress[j].xy += tmp; sigma.xy += 2.0 * tmp; tmp = d.y * d.y * dphi; p->stress[i].yy += tmp; q->stress[j].yy += tmp; sigma.yy += 2.0 * tmp; #ifndef TWOD tmp = d.y * d.z * dphi; p->stress[i].yz += tmp; q->stress[j].yz += tmp; sigma.yz += 2.0 * tmp; tmp = d.z * d.z * dphi; p->stress[i].zz += tmp; q->stress[j].zz += tmp; sigma.zz += 2.0 * tmp; tmp = d.z * d.x * dphi; p->stress[i].zx += tmp; q->stress[j].zx += tmp; sigma.zx += 2.0 * tmp; #endif tmp = 2.0 * d.x * d.x * d.x * d.x * ddphi + d.x * d.x * dphi; p->elco[i].c11 += tmp; q->elco[j].c11 += tmp; c.c11 += 2.0 * tmp; tmp = 2.0 * d.x * d.x * d.y * d.y * ddphi; p->elco[i].c12 += tmp; q->elco[j].c12 += tmp; c.c12 += 2.0 * tmp; tmp += 0.5 * ( d.x * d.x + d.y * d.y ) * dphi; p->elco[i].c66 += tmp; q->elco[j].c66 += tmp; c.c66 += 2.0 * tmp; tmp = 2.0 * d.y * d.y * d.y * d.y * ddphi + d.y * d.y * dphi; p->elco[i].c22 += tmp; q->elco[j].c22 += tmp; c.c22 += 2.0 * tmp; #ifndef TWOD tmp = 2.0 * d.x * d.x * d.z * d.z * ddphi; p->elco[i].c13 += tmp; q->elco[j].c13 += tmp; c.c13 += 2.0 * tmp; tmp += 0.5 * ( d.x * d.x + d.z * d.z ) * dphi; p->elco[i].c55 += tmp; q->elco[j].c55 += tmp; c.c55 += 2.0 * tmp; tmp = 2.0 * d.y * d.y * d.z * d.z * ddphi; p->elco[i].c23 += tmp; q->elco[j].c23 += tmp; c.c23 += 2.0 * tmp; tmp += 0.5 * ( d.y * d.y + d.z * d.z ) * dphi; p->elco[i].c44 += tmp; q->elco[j].c44 += tmp; c.c44 += 2.0 * tmp; tmp = 2.0 * d.z * d.z * d.z * d.z * ddphi + d.z * d.z * dphi; p->elco[i].c33 += tmp; q->elco[j].c33 += tmp; c.c33 += 2.0 * tmp; tmp = 2.0 * d.x * d.y * d.z * d.z * ddphi + 0.25 * d.x * d.y * dphi; p->elco[i].c45 += tmp; q->elco[j].c45 += tmp; c.c45 += 2.0 * tmp; tmp = 2.0 * d.x * d.y * d.y * d.z * ddphi + 0.25 * d.x * d.z * dphi; p->elco[i].c46 += tmp; q->elco[j].c46 += tmp; c.c46 += 2.0 * tmp; tmp -= 0.25 * d.x * d.z * dphi; p->elco[i].c25 += tmp; q->elco[j].c25 += tmp; c.c25 += 2.0 * tmp; tmp = 2.0 * d.x * d.x * d.y * d.z * ddphi + 0.25 * d.y * d.z * dphi; p->elco[i].c56 += tmp; q->elco[j].c56 += tmp; c.c56 += 2.0 * tmp; tmp -= 0.25 * d.y * d.z * dphi; p->elco[i].c14 += tmp; q->elco[j].c14 += tmp; c.c14 += 2.0 * tmp; tmp = 2.0 * d.x * d.x * d.x * d.z * ddphi + 0.5 * d.x * d.z * dphi; p->elco[i].c15 += tmp; q->elco[j].c15 += tmp; c.c15 += 2.0 * tmp; #endif tmp = 2.0 * d.x * d.x * d.x * d.y * ddphi + 0.5 * d.x * d.y * dphi; p->elco[i].c16 += tmp; q->elco[j].c16 += tmp; c.c16 += 2.0 * tmp; tmp = 2.0 * d.x * d.y * d.y * d.y * ddphi + 0.5 * d.x * d.y * dphi; p->elco[i].c26 += tmp; q->elco[j].c26 += tmp; c.c26 += 2.0 * tmp; #ifndef TWOD tmp = 2.0 * d.y * d.y * d.y * d.z * ddphi + 0.5 * d.y * d.z * dphi; p->elco[i].c24 += tmp; q->elco[j].c24 += tmp; c.c24 += 2.0 * tmp; tmp = 2.0 * d.y * d.z * d.z * d.z * ddphi + 0.5 * d.y * d.z * dphi; p->elco[i].c34 += tmp; q->elco[j].c34 += tmp; c.c34 += 2.0 * tmp; tmp = 2.0 * d.x * d.z * d.z * d.z * ddphi + 0.5 * d.x * d.z * dphi; p->elco[i].c35 += tmp; q->elco[j].c35 += tmp; c.c35 += 2.0 * tmp; tmp = 2.0 * d.x * d.y * d.z * d.z * ddphi; p->elco[i].c36 += tmp; q->elco[j].c36 += tmp; c.c36 += 2.0 * tmp; #endif press += 2.0 * dphi * r2; bulkm += ( 2.0 * ddphi * r2 + dphi ) * 2.0 * r2; dbulkm_dp += ( 2.0 * dddphi * r2 + 3.0 * ddphi ) * 4.0 * r2 * r2; } #ifdef EAM col2 = q_typ * ntypes + p_typ; /* compute host electron density */ if ( r2 < rho_h_tab.end[col1] ) { VAL_FUNC(rho_h, rho_h_tab, col1, inc, r2, is_short); EAM_RHO(p,i) += rho_h; } if ( p_typ == q_typ ) { if ( r2 < rho_h_tab.end[col1] ) EAM_RHO(q,j) += rho_h; } else { if ( r2 < rho_h_tab.end[col2] ) { VAL_FUNC(rho_h, rho_h_tab, col2, inc, r2, is_short); EAM_RHO(q,j) += rho_h; } } /* Compute stress for EAM potential */ if ( (r2 < rho_h_tab.end[col1]) || (r2 < rho_h_tab.end[col2]) ) { DERIV_FUNC(rho_i_strich, rho_i_zweistrich, rho_i_dreistrich, rho_h_tab, col2, inc, r2, is_short); q->eam_stress[j].xx += 2.0 * rho_i_strich * d.x * d.x; q->eam_stress[j].xy += 2.0 * rho_i_strich * d.x * d.y; q->eam_stress[j].yy += 2.0 * rho_i_strich * d.y * d.y; #ifndef TWOD q->eam_stress[j].yz += 2.0 * rho_i_strich * d.y * d.z; q->eam_stress[j].zz += 2.0 * rho_i_strich * d.z * d.z; q->eam_stress[j].zx += 2.0 * rho_i_strich * d.z * d.x; #endif q->eam_press[j] += 2.0 * rho_i_strich * r2; q->eam_bulkm[j] += ( 2.0 * rho_i_zweistrich * r2 + rho_i_strich ) * 2.0 * r2; q->eam_dbulkm[j] += ( 2.0 * rho_i_dreistrich * r2 + 3.0 * rho_i_zweistrich ) * 4.0 * r2 * r2; if ( col1 == col2 ) { rho_j_strich = rho_i_strich; rho_j_zweistrich = rho_i_zweistrich; rho_j_dreistrich = rho_i_dreistrich; } else { DERIV_FUNC(rho_j_strich, rho_j_zweistrich, rho_j_dreistrich, rho_h_tab, col1, inc, r2, is_short); } p->eam_stress[i].xx += 2.0 * rho_j_strich * d.x * d.x; p->eam_stress[i].xy += 2.0 * rho_j_strich * d.x * d.y; p->eam_stress[i].yy += 2.0 * rho_j_strich * d.y * d.y; #ifndef TWOD p->eam_stress[i].yz += 2.0 * rho_j_strich * d.y * d.z; p->eam_stress[i].zz += 2.0 * rho_j_strich * d.z * d.z; p->eam_stress[i].zx += 2.0 * rho_j_strich * d.z * d.x; #endif p->eam_press[i] += 2.0 * rho_j_strich * r2; p->eam_bulkm[i] += ( 2.0 * rho_j_zweistrich * r2 + rho_j_strich ) * 2.0 * r2; p->eam_dbulkm[i] += ( 2.0 * rho_j_dreistrich * r2 + 3.0 * rho_j_zweistrich ) * 4.0 * r2 * r2; } #endif /* EAM */ } }
void do_angle(cell *p, cell *q, cell *r, cell *s, vektor pbc_q, vektor pbc_r, vektor pbc_s) { int i, j, k, l; int ang; int temp; vektor d_ij, d_ik, d_jk, d_il, d_jl, d_kl; vektor v_k, v_l; real radius_ij, radius_ik, radius_jk, radius_il, radius_jl, radius_kl; real phi; real betrag_v_k, betrag_v_l; double sprod; int p_typ, q_typ, r_typ, s_typ; /* l / / i-----j / / k */ /* For each atom in cell p */ for (i = 0; i < p->n; ++i) /* For each atom in neighbouring cell q */ for (j = (p == q ? i+1 : 0); j < q->n; ++j) /* For each atom in neighbouring cell r of p */ for (k = 0; k < r->n; ++k) /* for each atom in neighbouring cell s of q */ for (l = 0; l < s->n; ++l) { /* Calculate distance vectors */ d_ij.x = q->ort[j].x - p->ort[i].x + pbc_q.x; d_ij.y = q->ort[j].y - p->ort[i].y + pbc_q.y; d_ij.z = q->ort[j].z - p->ort[i].z + pbc_q.z; d_ik.x = r->ort[k].x - p->ort[i].x + pbc_r.x; d_ik.y = r->ort[k].y - p->ort[i].y + pbc_r.y; d_ik.z = r->ort[k].z - p->ort[i].z + pbc_r.z; d_jl.x = s->ort[l].x - q->ort[j].x + pbc_s.x; d_jl.y = s->ort[l].y - q->ort[j].y + pbc_s.y; d_jl.z = s->ort[l].z - q->ort[j].z + pbc_s.z; d_jk.x = d_ik.x - d_ij.x; d_jk.y = d_ik.y - d_ij.y; d_jk.z = d_ik.z - d_ij.z; d_il.x = d_ij.x + d_jl.x; d_il.y = d_ij.y + d_jl.y; d_il.z = d_ij.z + d_jl.z; d_kl.x = d_jl.x - d_jk.x; d_kl.y = d_jl.y - d_jk.y; d_kl.z = d_jl.z - d_jk.z; radius_ij = sqrt( (double)(SPROD(d_ij,d_ij)) ); radius_ik = sqrt( (double)(SPROD(d_ik,d_ik)) ); radius_jl = sqrt( (double)(SPROD(d_jl,d_jl)) ); radius_jk = sqrt( (double)(SPROD(d_jk,d_jk)) ); radius_il = sqrt( (double)(SPROD(d_il,d_il)) ); radius_kl = sqrt( (double)(SPROD(d_kl,d_kl)) ); /* v_k = d_ik x d_jk */ v_k.x = d_ik.y * d_jk.z - d_ik.z * d_jk.y; v_k.y = d_ik.z * d_jk.x - d_ik.x * d_jk.z; v_k.z = d_ik.x * d_jk.y - d_ik.y * d_jk.x; betrag_v_k = sqrt( (double)(SPROD(v_k,v_k)) ); /* v_l = d_il x d_jl */ v_l.x = d_il.y * d_jl.z - d_il.z * d_jl.y; v_l.y = d_il.z * d_jl.x - d_il.x * d_jl.z; v_l.z = d_il.x * d_jl.y - d_il.y * d_jl.x; betrag_v_l = sqrt( (double)(SPROD(v_l,v_l)) ); /* Calculate torsion angles */ if ( (radius_ij < r_max) && (radius_ik < r_max) && (radius_jl < r_max) && (radius_ij*radius_ik*radius_jl*radius_jk*radius_il*radius_kl > 0.0) && (betrag_v_k > 0.0) && (betrag_v_l > 0.0) ) { ++nangles; sprod = (double)( SPROD(v_k,v_l)/( betrag_v_k * betrag_v_l )); if (sprod < -1.0) sprod = -1.0; phi = (double) (acos(sprod)); ang = (int) ( slots * phi / 3.141592654 ); p_typ = p->sorte[i]; q_typ = q->sorte[j]; r_typ = r->sorte[k]; s_typ = s->sorte[l]; if (p_typ == q_typ) { if ( r_typ > s_typ ) { temp = s_typ; s_typ = r_typ; r_typ = temp; } } else if (p_typ > q_typ) { temp = q_typ; q_typ = p_typ; p_typ = temp; temp = s_typ; s_typ = r_typ; r_typ = temp; } if ((ang >= 0) && (ang < slots)) ++*PTR_5D_V(histogram, ang , p_typ, q_typ, r_typ, s_typ, hist_dim); } } }
void do_forces(cell *p, cell *q, vektor pbc, real *Epot, real *Virial, real *Vir_xx, real *Vir_yy, real *Vir_zz, real *Vir_yz, real *Vir_zx, real *Vir_xy) { int i, j ; int jstart ; real tmp_virial ; vektor tmp_vir_vect ; vektor tmp_r12 ; vektor r12 ; vektor e1 ; vektor e2 ; real rsqr ; real pot12 ; vektor force12 ; vektor torque12 ; vektor torque21 ; /* actual pair virial and virial components */ tmp_virial = 0.0; tmp_vir_vect.x = 0.0; tmp_vir_vect.y = 0.0; tmp_vir_vect.z = 0.0; /* For each atom in first cell */ for (i = 0;i < p->n; ++i) { /* For each atom in neighbouring cell */ /* Some compilers don't find the expressions that are invariant to the inner loop. I'll have to define my own temp variables. */ tmp_r12.x = ORT(p,i,X) - pbc.x; tmp_r12.y = ORT(p,i,Y) - pbc.y; tmp_r12.z = ORT(p,i,Z) - pbc.z; e1.x = ACHSE(p,i,X); e1.y = ACHSE(p,i,Y); e1.z = ACHSE(p,i,Z); #ifdef TWOD jstart = (((p==q) && (pbc.x==0) && (pbc.y==0)) ? i+1 : 0); #else jstart = (((p==q) && (pbc.x==0) && (pbc.y==0) && (pbc.z==0)) ? i+1 : 0); #endif for (j = jstart; j < q->n; ++j) { /* Calculate distance */ r12.x = tmp_r12.x - ORT(q,j,X); r12.y = tmp_r12.y - ORT(q,j,Y); r12.z = tmp_r12.z - ORT(q,j,Z); rsqr = SPROD(r12,r12); e2.x = ACHSE(q,j,X); e2.y = ACHSE(q,j,Y); e2.z = ACHSE(q,j,Z); #ifndef NODBG_DIST if (0==rsqr) { char msgbuf[256]; sprintf(msgbuf,"Distance is zero: i=%d, j=%d\n",i,j); error(msgbuf); } #else if (0==rsqr) error("Distance is zero."); #endif if (rsqr <= uniax_r2_cut) { /* calculate interactions, if distance smaller than cutoff radius */ gay_berne( r12, e1, e2, rsqr, uniax_sig, uniax_eps, &pot12, &force12, &torque12, &torque21) ; /* accumulate forces */ KRAFT(p,i,X) += force12.x; KRAFT(p,i,Y) += force12.y; KRAFT(p,i,Z) += force12.z; KRAFT(q,j,X) -= force12.x; KRAFT(q,j,Y) -= force12.y; KRAFT(q,j,Z) -= force12.z; /* accumulate torques */ DREH_MOMENT(p,i,X) += torque12.x; DREH_MOMENT(p,i,Y) += torque12.y; DREH_MOMENT(p,i,Z) += torque12.z; DREH_MOMENT(q,j,X) += torque21.x; DREH_MOMENT(q,j,Y) += torque21.y; DREH_MOMENT(q,j,Z) += torque21.z; *Epot += pot12; pot12 *= 0.5; /* avoid double counting */ POTENG(p,i) += pot12; POTENG(q,j) += pot12; tmp_vir_vect.x += r12.x * force12.x ; tmp_vir_vect.y += r12.y * force12.y ; tmp_vir_vect.z += r12.z * force12.z ; tmp_virial += r12.x * force12.x + r12.y * force12.y + r12.z * force12.z ; }; /* if */ }; /* for j */ }; /* for i */ *Vir_xx += tmp_vir_vect.x; *Vir_yy += tmp_vir_vect.y; *Vir_zz += tmp_vir_vect.z; *Virial += tmp_virial ; } /* do_forces_uniax */
double calc_forces(double* xi_opt, double* forces, int flag) { double tmpsum, sum = 0.0; int first, col, ne, size, i = flag; double* xi = NULL; apot_table_t* apt = &g_pot.apot_table; double charge[g_param.ntypes]; double sum_charges; double dp_kappa; #if defined(DIPOLE) double dp_alpha[g_param.ntypes]; double dp_b[g_calc.paircol]; double dp_c[g_calc.paircol]; #endif // DIPOLE static double rho_sum_loc, rho_sum; rho_sum_loc = rho_sum = 0.0; switch (g_pot.format_type) { case POTENTIAL_FORMAT_UNKNOWN: error(1, "Unknown potential format detected! (%s:%d)\n", __FILE__, __LINE__); case POTENTIAL_FORMAT_ANALYTIC: xi = g_pot.calc_pot.table; break; case POTENTIAL_FORMAT_TABULATED_EQ_DIST: case POTENTIAL_FORMAT_TABULATED_NON_EQ_DIST: xi = xi_opt; break; case POTENTIAL_FORMAT_KIM: error(1, "KIM format is not supported by EAM elstat force routine!"); } #if !defined(MPI) g_mpi.myconf = g_config.nconf; #endif // MPI ne = g_pot.apot_table.total_ne_par; size = apt->number; /* This is the start of an infinite loop */ while (1) { tmpsum = 0.0; /* sum of squares of local process */ rho_sum_loc = 0.0; #if defined APOT && !defined MPI if (g_pot.format_type == POTENTIAL_FORMAT_ANALYTIC) { apot_check_params(xi_opt); update_calc_table(xi_opt, xi, 0); } #endif // APOT && !MPI #if defined(MPI) /* exchange potential and flag value */ #if !defined(APOT) MPI_Bcast(xi, g_pot.calc_pot.len, MPI_DOUBLE, 0, MPI_COMM_WORLD); #endif // APOT MPI_Bcast(&flag, 1, MPI_INT, 0, MPI_COMM_WORLD); if (flag == 1) break; /* Exception: flag 1 means clean up */ #if defined(APOT) if (g_mpi.myid == 0) apot_check_params(xi_opt); MPI_Bcast(xi_opt, g_calc.ndimtot, MPI_DOUBLE, 0, MPI_COMM_WORLD); if (g_pot.format_type == POTENTIAL_FORMAT_ANALYTIC) update_calc_table(xi_opt, xi, 0); #else /* APOT */ /* if flag==2 then the potential parameters have changed -> sync */ if (flag == 2) potsync(); #endif // APOT #endif // MPI /* local arrays for electrostatic parameters */ sum_charges = 0; for (i = 0; i < g_param.ntypes - 1; i++) { if (xi_opt[2 * size + ne + i]) { charge[i] = xi_opt[2 * size + ne + i]; sum_charges += apt->ratio[i] * charge[i]; } else { charge[i] = 0.0; } } apt->last_charge = -sum_charges / apt->ratio[g_param.ntypes - 1]; charge[g_param.ntypes - 1] = apt->last_charge; if (xi_opt[2 * size + ne + g_param.ntypes - 1]) { dp_kappa = xi_opt[2 * size + ne + g_param.ntypes - 1]; } else { dp_kappa = 0.0; } #if defined(DIPOLE) for (i = 0; i < g_param.ntypes; i++) { if (xi_opt[2 * size + ne + g_param.ntypes + i]) { dp_alpha[i] = xi_opt[2 * size + ne + g_param.ntypes + i]; } else { dp_alpha[i] = 0.0; } } for (i = 0; i < g_calc.paircol; i++) { if (xi_opt[2 * size + ne + 2 * g_param.ntypes + i]) { dp_b[i] = xi_opt[2 * size + ne + 2 * g_param.ntypes + i]; } else { dp_b[i] = 0.0; } if (xi_opt[2 * size + ne + 2 * g_param.ntypes + g_calc.paircol + i]) { dp_c[i] = xi_opt[2 * size + ne + 2 * g_param.ntypes + g_calc.paircol + i]; } else { dp_c[i] = 0.0; } } #endif // DIPOLE /* init second derivatives for splines */ /* pair potentials & rho */ for (col = 0; col < g_calc.paircol + g_param.ntypes; col++) { first = g_pot.calc_pot.first[col]; switch (g_pot.format_type) { case POTENTIAL_FORMAT_UNKNOWN: error(1, "Unknown potential format detected! (%s:%d)\n", __FILE__, __LINE__); case POTENTIAL_FORMAT_ANALYTIC: case POTENTIAL_FORMAT_TABULATED_EQ_DIST: { spline_ed(g_pot.calc_pot.step[col], xi + first, g_pot.calc_pot.last[col] - first + 1, *(xi + first - 2), 0.0, g_pot.calc_pot.d2tab + first); break; } case POTENTIAL_FORMAT_TABULATED_NON_EQ_DIST: { spline_ne(g_pot.calc_pot.xcoord + first, xi + first, g_pot.calc_pot.last[col] - first + 1, *(xi + first - 2), 0.0, g_pot.calc_pot.d2tab + first); } case POTENTIAL_FORMAT_KIM: error(1, "KIM format is not supported by EAM elstat force routine!"); } } /* F */ for (col = g_calc.paircol + g_param.ntypes; col < g_calc.paircol + 2 * g_param.ntypes; col++) { first = g_pot.calc_pot.first[col]; /* gradient at left boundary matched to square root function, when 0 not in domain(F), else natural spline */ switch (g_pot.format_type) { case POTENTIAL_FORMAT_UNKNOWN: error(1, "Unknown potential format detected! (%s:%d)\n", __FILE__, __LINE__); case POTENTIAL_FORMAT_ANALYTIC: case POTENTIAL_FORMAT_TABULATED_EQ_DIST: { spline_ed(g_pot.calc_pot.step[col], xi + first, g_pot.calc_pot.last[col] - first + 1, *(xi + first - 2), *(xi + first - 1), g_pot.calc_pot.d2tab + first); break; } case POTENTIAL_FORMAT_TABULATED_NON_EQ_DIST: { spline_ne(g_pot.calc_pot.xcoord + first, xi + first, g_pot.calc_pot.last[col] - first + 1, *(xi + first - 2), *(xi + first - 1), g_pot.calc_pot.d2tab + first); } case POTENTIAL_FORMAT_KIM: error(1, "KIM format is not supported by EAM elstat force routine!"); } } /* region containing loop over configurations */ { int self; vector tmp_force; int h, j, type1, type2, uf; #if defined(STRESS) int us = 0; int stresses = 0; #endif int n_i, n_j; double fnval, grad, fnval_tail, grad_tail, grad_i, grad_j; #if defined(DIPOLE) double p_sr_tail = 0.0; #endif atom_t* atom; neigh_t* neigh; double r; int col_F; double eam_force; double rho_val, rho_grad, rho_grad_j; /* loop over configurations: M A I N LOOP CONTAINING ALL ATOM-LOOPS */ for (h = g_mpi.firstconf; h < g_mpi.firstconf + g_mpi.myconf; h++) { uf = g_config.conf_uf[h - g_mpi.firstconf]; #if defined(STRESS) us = g_config.conf_us[h - g_mpi.firstconf]; #endif // STRESS /* reset energies and stresses */ forces[g_calc.energy_p + h] = 0.0; #if defined(STRESS) stresses = g_calc.stress_p + 6 * h; for (i = 0; i < 6; i++) forces[stresses + i] = 0.0; #endif // STRESS /* set limiting constraints */ forces[g_calc.limit_p + h] = -g_config.force_0[g_calc.limit_p + h]; #if defined(DIPOLE) /* reset dipoles and fields: LOOP Z E R O */ for (i = 0; i < g_config.inconf[h]; i++) { atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; atom->E_stat.x = 0.0; atom->E_stat.y = 0.0; atom->E_stat.z = 0.0; atom->p_sr.x = 0.0; atom->p_sr.y = 0.0; atom->p_sr.z = 0.0; } #endif // DIPOLE /* F I R S T LOOP OVER ATOMS: reset forces, dipoles */ for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ n_i = 3 * (g_config.cnfstart[h] + i); if (uf) { forces[n_i + 0] = -g_config.force_0[n_i + 0]; forces[n_i + 1] = -g_config.force_0[n_i + 1]; forces[n_i + 2] = -g_config.force_0[n_i + 2]; } else { forces[n_i + 0] = 0.0; forces[n_i + 1] = 0.0; forces[n_i + 2] = 0.0; } /* reset atomic density */ g_config.conf_atoms[g_config.cnfstart[h] - g_mpi.firstatom + i].rho = 0.0; } /* end F I R S T LOOP */ /* S E C O N D loop: calculate short-range and monopole forces, calculate static field- and dipole-contributions, calculate atomic densities */ for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; n_i = 3 * (g_config.cnfstart[h] + i); for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* updating tail-functions - only necessary with variing kappa */ if (!apt->sw_kappa) #if defined(DSF) elstat_dsf(neigh->r, dp_kappa, &neigh->fnval_el, &neigh->grad_el, &neigh->ggrad_el); #else elstat_shift(neigh->r, dp_kappa, &neigh->fnval_el, &neigh->grad_el, &neigh->ggrad_el); #endif // DSF /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + g_config.cnfstart[h]) ? 1 : 0; /* calculate short-range forces */ if (neigh->r < g_pot.calc_pot.end[col]) { if (uf) { fnval = splint_comb_dir(&g_pot.calc_pot, xi, neigh->slot[0], neigh->shift[0], neigh->step[0], &grad); } else { fnval = splint_dir(&g_pot.calc_pot, xi, neigh->slot[0], neigh->shift[0], neigh->step[0]); } /* avoid double counting if atom is interacting with a copy of * itself */ if (self) { fnval *= 0.5; grad *= 0.5; } forces[g_calc.energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad; tmp_force.y = neigh->dist_r.y * grad; tmp_force.z = neigh->dist_r.z * grad; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #if defined(STRESS) /* calculate pair stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif // STRESS } } /* calculate monopole forces */ if (neigh->r < g_config.dp_cut && (charge[type1] || charge[type2])) { fnval_tail = neigh->fnval_el; grad_tail = neigh->grad_el; grad_i = charge[type2] * grad_tail; if (type1 == type2) { grad_j = grad_i; } else { grad_j = charge[type1] * grad_tail; } fnval = charge[type1] * charge[type2] * fnval_tail; grad = charge[type1] * grad_i; if (self) { grad_i *= 0.5; grad_j *= 0.5; fnval *= 0.5; grad *= 0.5; } forces[g_calc.energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist.x * grad; tmp_force.y = neigh->dist.y * grad; tmp_force.z = neigh->dist.z * grad; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #if defined(STRESS) /* calculate coulomb stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif // STRESS } #if defined(DIPOLE) /* calculate static field-contributions */ atom->E_stat.x += neigh->dist.x * grad_i; atom->E_stat.y += neigh->dist.y * grad_i; atom->E_stat.z += neigh->dist.z * grad_i; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_stat.x -= neigh->dist.x * grad_j; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_stat.y -= neigh->dist.y * grad_j; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_stat.z -= neigh->dist.z * grad_j; /* calculate short-range dipoles */ if (dp_alpha[type1] && dp_b[col] && dp_c[col]) { p_sr_tail = grad_tail * neigh->r * shortrange_value(neigh->r, dp_alpha[type1], dp_b[col], dp_c[col]); atom->p_sr.x += charge[type2] * neigh->dist_r.x * p_sr_tail; atom->p_sr.y += charge[type2] * neigh->dist_r.y * p_sr_tail; atom->p_sr.z += charge[type2] * neigh->dist_r.z * p_sr_tail; } if (dp_alpha[type2] && dp_b[col] && dp_c[col] && !self) { p_sr_tail = grad_tail * neigh->r * shortrange_value(neigh->r, dp_alpha[type2], dp_b[col], dp_c[col]); g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_sr.x -= charge[type1] * neigh->dist_r.x * p_sr_tail; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_sr.y -= charge[type1] * neigh->dist_r.y * p_sr_tail; g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_sr.z -= charge[type1] * neigh->dist_r.z * p_sr_tail; } #endif // DIPOLE } /* calculate atomic densities */ if (atom->type == neigh->type) { /* then transfer(a->b)==transfer(b->a) */ if (neigh->r < g_pot.calc_pot.end[neigh->col[1]]) { rho_val = splint_dir(&g_pot.calc_pot, xi, neigh->slot[1], neigh->shift[1], neigh->step[1]); atom->rho += rho_val; /* avoid double counting if atom is interacting with a copy of itself */ if (!self) { g_config.conf_atoms[neigh->nr - g_mpi.firstatom].rho += rho_val; } } } else { /* transfer(a->b)!=transfer(b->a) */ if (neigh->r < g_pot.calc_pot.end[neigh->col[1]]) { atom->rho += splint_dir(&g_pot.calc_pot, xi, neigh->slot[1], neigh->shift[1], neigh->step[1]); } /* cannot use slot/shift to access splines */ if (neigh->r < g_pot.calc_pot.end[g_calc.paircol + atom->type]) g_config.conf_atoms[neigh->nr - g_mpi.firstatom].rho += (*g_splint)(&g_pot.calc_pot, xi, g_calc.paircol + atom->type, neigh->r); } } /* loop over neighbours */ col_F = g_calc.paircol + g_param.ntypes + atom->type; /* column of F */ if (atom->rho > g_pot.calc_pot.end[col_F]) { /* then punish target function -> bad potential */ forces[g_calc.limit_p + h] += DUMMY_WEIGHT * 10.0 * dsquare(atom->rho - g_pot.calc_pot.end[col_F]); atom->rho = g_pot.calc_pot.end[col_F]; } if (atom->rho < g_pot.calc_pot.begin[col_F]) { /* then punish target function -> bad potential */ forces[g_calc.limit_p + h] += DUMMY_WEIGHT * 10.0 * dsquare(g_pot.calc_pot.begin[col_F] - atom->rho); atom->rho = g_pot.calc_pot.begin[col_F]; } /* embedding energy, embedding gradient */ /* contribution to cohesive energy is F(n) */ #if defined(NORESCALE) if (atom->rho < g_pot.calc_pot.begin[col_F]) { /* linear extrapolation left */ rho_val = splint_comb(&calc_pot, xi, col_F, g_pot.calc_pot.begin[col_F], &atom->gradF); forces[energy_p + h] += rho_val + (atom->rho - g_pot.calc_pot.begin[col_F]) * atom->gradF; #if defined(APOT) forces[limit_p + h] += DUMMY_WEIGHT * 10.0 * dsquare(calc_pot.begin[col_F] - atom->rho); #endif // APOT } else if (atom->rho > g_pot.calc_pot.end[col_F]) { /* and right */ rho_val = splint_comb( &calc_pot, xi, col_F, g_pot.calc_pot.end[col_F] - 0.5 * g_pot.calc_pot.step[col_F], &atom->gradF); forces[energy_p + h] += rho_val + (atom->rho - g_pot.calc_pot.end[col_F]) * atom->gradF; #if defined(APOT) forces[limit_p + h] += DUMMY_WEIGHT * 10.0 * dsquare(atom->rho - g_pot.calc_pot.end[col_F]); #endif // APOT } /* and in-between */ else { forces[energy_p + h] += splint_comb(&calc_pot, xi, col_F, atom->rho, &atom->gradF); } #else forces[g_calc.energy_p + h] += (*g_splint_comb)( &g_pot.calc_pot, xi, col_F, atom->rho, &atom->gradF); #endif // NORESCALE /* sum up rho */ rho_sum_loc += atom->rho; } /* end S E C O N D loop over atoms */ #if defined(DIPOLE) /* T H I R D loop: calculate whole dipole moment for every atom */ double rp, dp_sum; int dp_converged = 0, dp_it = 0; double max_diff = 10; while (dp_converged == 0) { dp_sum = 0; for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; if (dp_alpha[type1]) { if (dp_it) { /* note: mixing parameter is different from that on in IMD */ atom->E_tot.x = (1 - g_config.dp_mix) * atom->E_ind.x + g_config.dp_mix * atom->E_old.x + atom->E_stat.x; atom->E_tot.y = (1 - g_config.dp_mix) * atom->E_ind.y + g_config.dp_mix * atom->E_old.y + atom->E_stat.y; atom->E_tot.z = (1 - g_config.dp_mix) * atom->E_ind.z + g_config.dp_mix * atom->E_old.z + atom->E_stat.z; } else { atom->E_tot.x = atom->E_ind.x + atom->E_stat.x; atom->E_tot.y = atom->E_ind.y + atom->E_stat.y; atom->E_tot.z = atom->E_ind.z + atom->E_stat.z; } atom->p_ind.x = dp_alpha[type1] * atom->E_tot.x + atom->p_sr.x; atom->p_ind.y = dp_alpha[type1] * atom->E_tot.y + atom->p_sr.y; atom->p_ind.z = dp_alpha[type1] * atom->E_tot.z + atom->p_sr.z; atom->E_old.x = atom->E_ind.x; atom->E_old.y = atom->E_ind.y; atom->E_old.z = atom->E_ind.z; atom->E_ind.x = 0.0; atom->E_ind.y = 0.0; atom->E_ind.z = 0.0; } } for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + g_config.cnfstart[h]) ? 1 : 0; if (neigh->r < g_config.dp_cut && dp_alpha[type1] && dp_alpha[type2]) { rp = SPROD( g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind, neigh->dist_r); atom->E_ind.x += neigh->grad_el * (3 * rp * neigh->dist_r.x - g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.x); atom->E_ind.y += neigh->grad_el * (3 * rp * neigh->dist_r.y - g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.y); atom->E_ind.z += neigh->grad_el * (3 * rp * neigh->dist_r.z - g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.z); if (!self) { rp = SPROD(atom->p_ind, neigh->dist_r); g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_ind.x += neigh->grad_el * (3 * rp * neigh->dist_r.x - atom->p_ind.x); g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_ind.y += neigh->grad_el * (3 * rp * neigh->dist_r.y - atom->p_ind.y); g_config.conf_atoms[neigh->nr - g_mpi.firstatom].E_ind.z += neigh->grad_el * (3 * rp * neigh->dist_r.z - atom->p_ind.z); } } } } for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; if (dp_alpha[type1]) { dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.x - atom->E_ind.x)); dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.y - atom->E_ind.y)); dp_sum += dsquare(dp_alpha[type1] * (atom->E_old.z - atom->E_ind.z)); } } dp_sum /= 3 * g_config.inconf[h]; dp_sum = sqrt(dp_sum); if (dp_it) { if ((dp_sum > max_diff) || (dp_it > 50)) { dp_converged = 1; for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; if (dp_alpha[type1]) { atom->p_ind.x = dp_alpha[type1] * atom->E_stat.x + atom->p_sr.x; atom->p_ind.y = dp_alpha[type1] * atom->E_stat.y + atom->p_sr.y; atom->p_ind.z = dp_alpha[type1] * atom->E_stat.z + atom->p_sr.z; atom->E_ind.x = atom->E_stat.x; atom->E_ind.y = atom->E_stat.y; atom->E_ind.z = atom->E_stat.z; } } } } if (dp_sum < g_config.dp_tol) dp_converged = 1; dp_it++; } /* end T H I R D loop over atoms */ /* F O U R T H loop: calculate monopole-dipole and dipole-dipole forces */ double rp_i, rp_j, pp_ij, tmp_1, tmp_2; double grad_1, grad_2, srval, srgrad, srval_tail, srgrad_tail, fnval_sum, grad_sum; for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; n_i = 3 * (g_config.cnfstart[h] + i); for (j = 0; j < atom->num_neigh; j++) { /* neighbors */ neigh = atom->neigh + j; type2 = neigh->type; col = neigh->col[0]; /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + g_config.cnfstart[h]) ? 1 : 0; if (neigh->r < g_config.dp_cut && (dp_alpha[type1] || dp_alpha[type2])) { fnval_tail = -neigh->grad_el; grad_tail = -neigh->ggrad_el; if (dp_b[col] && dp_c[col]) { shortrange_term(neigh->r, dp_b[col], dp_c[col], &srval_tail, &srgrad_tail); srval = fnval_tail * srval_tail; srgrad = fnval_tail * srgrad_tail + grad_tail * srval_tail; } if (self) { fnval_tail *= 0.5; grad_tail *= 0.5; } /* monopole-dipole contributions */ if (charge[type1] && dp_alpha[type2]) { if (dp_b[col] && dp_c[col]) { fnval_sum = fnval_tail + srval; grad_sum = grad_tail + srgrad; } else { fnval_sum = fnval_tail; grad_sum = grad_tail; } rp_j = SPROD( g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind, neigh->dist_r); fnval = charge[type1] * rp_j * fnval_sum * neigh->r; grad_1 = charge[type1] * rp_j * grad_sum * neigh->r2; grad_2 = charge[type1] * fnval_sum; forces[g_calc.energy_p + h] -= fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad_1 + g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.x * grad_2; tmp_force.y = neigh->dist_r.y * grad_1 + g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.y * grad_2; tmp_force.z = neigh->dist_r.z * grad_1 + g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind.z * grad_2; forces[n_i + 0] -= tmp_force.x; forces[n_i + 1] -= tmp_force.y; forces[n_i + 2] -= tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] += tmp_force.x; forces[n_j + 1] += tmp_force.y; forces[n_j + 2] += tmp_force.z; #if defined(STRESS) /* calculate stresses */ if (us) { forces[stresses + 0] += neigh->dist.x * tmp_force.x; forces[stresses + 1] += neigh->dist.y * tmp_force.y; forces[stresses + 2] += neigh->dist.z * tmp_force.z; forces[stresses + 3] += neigh->dist.x * tmp_force.y; forces[stresses + 4] += neigh->dist.y * tmp_force.z; forces[stresses + 5] += neigh->dist.z * tmp_force.x; } #endif // STRESS } } /* dipole-monopole contributions */ if (dp_alpha[type2] && charge[type2]) { if (dp_b[col] && dp_c[col]) { fnval_sum = fnval_tail + srval; grad_sum = grad_tail + srgrad; } else { fnval_sum = fnval_tail; grad_sum = grad_tail; } rp_i = SPROD(atom->p_ind, neigh->dist_r); fnval = charge[type2] * rp_i * fnval_sum * neigh->r; grad_1 = charge[type2] * rp_i * grad_sum * neigh->r2; grad_2 = charge[type2] * fnval_sum; forces[g_calc.energy_p + h] += fnval; if (uf) { tmp_force.x = neigh->dist_r.x * grad_1 + atom->p_ind.x * grad_2; tmp_force.y = neigh->dist_r.y * grad_1 + atom->p_ind.y * grad_2; tmp_force.z = neigh->dist_r.z * grad_1 + atom->p_ind.z * grad_2; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #if defined(STRESS) /* calculate stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif // STRESS } } /* dipole-dipole contributions */ if (dp_alpha[type1] && dp_alpha[type2]) { pp_ij = SPROD( atom->p_ind, g_config.conf_atoms[neigh->nr - g_mpi.firstatom].p_ind); tmp_1 = 3 * rp_i * rp_j; tmp_2 = 3 * fnval_tail / neigh->r2; fnval = -(tmp_1 - pp_ij) * fnval_tail; grad_1 = (tmp_1 - pp_ij) * grad_tail; grad_2 = 2 * rp_i * rp_j; forces[g_calc.energy_p + h] += fnval; if (uf) { tmp_force.x = grad_1 * neigh->dist.x - tmp_2 * (grad_2 * neigh->dist.x - rp_i * neigh->r * g_config.conf_atoms[neigh->nr - g_mpi.firstatom] .p_ind.x - rp_j * neigh->r * atom->p_ind.x); tmp_force.y = grad_1 * neigh->dist.y - tmp_2 * (grad_2 * neigh->dist.y - rp_i * neigh->r * g_config.conf_atoms[neigh->nr - g_mpi.firstatom] .p_ind.y - rp_j * neigh->r * atom->p_ind.y); tmp_force.z = grad_1 * neigh->dist.z - tmp_2 * (grad_2 * neigh->dist.z - rp_i * neigh->r * g_config.conf_atoms[neigh->nr - g_mpi.firstatom] .p_ind.z - rp_j * neigh->r * atom->p_ind.z); forces[n_i + 0] -= tmp_force.x; forces[n_i + 1] -= tmp_force.y; forces[n_i + 2] -= tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] += tmp_force.x; forces[n_j + 1] += tmp_force.y; forces[n_j + 2] += tmp_force.z; #if defined(STRESS) /* calculate stresses */ if (us) { forces[stresses + 0] += neigh->dist.x * tmp_force.x; forces[stresses + 1] += neigh->dist.y * tmp_force.y; forces[stresses + 2] += neigh->dist.z * tmp_force.z; forces[stresses + 3] += neigh->dist.x * tmp_force.y; forces[stresses + 4] += neigh->dist.y * tmp_force.z; forces[stresses + 5] += neigh->dist.z * tmp_force.x; } #endif // STRESS } } } } /* loop over neighbours */ } /* end F O U R T H loop over atoms */ #endif // DIPOLE /* F I F T H loop: self energy contributions and sum-up force * contributions */ double qq; #if defined(DSF) double fnval_cut, gtail_cut, ggrad_cut; elstat_value(g_config.dp_cut, dp_kappa, &fnval_cut, >ail_cut, &ggrad_cut); #endif // DSF for (i = 0; i < g_config.inconf[h]; i++) { /* atoms */ atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; type1 = atom->type; n_i = 3 * (g_config.cnfstart[h] + i); /* self energy contributions */ if (charge[type1]) { qq = charge[type1] * charge[type1]; #if defined(DSF) fnval = qq * ( DP_EPS * dp_kappa / sqrt(M_PI) + (fnval_cut - gtail_cut * g_config.dp_cut * g_config.dp_cut )*0.5 ); #else fnval = DP_EPS * dp_kappa * qq / sqrt(M_PI); #endif // DSF forces[g_calc.energy_p + h] -= fnval; } #if defined(DIPOLE) double pp; if (dp_alpha[type1]) { pp = SPROD(atom->p_ind, atom->p_ind); fnval = pp / (2 * dp_alpha[type1]); forces[g_calc.energy_p + h] += fnval; } /* alternative dipole self energy including kappa-dependence */ // if (dp_alpha[type1]) { // pp = SPROD(atom->p_ind, atom->p_ind); // fnval = kkk * pp / sqrt(M_PI); // forces[energy_p + h] += fnval; //} #endif // DIPOLE /* sum-up: whole force contributions flow into tmpsum */ /* if (uf) {*/ /*#ifdef FWEIGHT*/ /* Weigh by absolute value of force */ /* forces[k] /= FORCE_EPS + atom->absforce;*/ /* forces[k + 1] /= FORCE_EPS + atom->absforce;*/ /* forces[k + 2] /= FORCE_EPS + atom->absforce;*/ /*#endif |+ FWEIGHT +|*/ /*#ifdef CONTRIB*/ /* if (atom->contrib)*/ /*#endif |+ CONTRIB +|*/ /* tmpsum +=*/ /* conf_weight[h] * (dsquare(forces[k]) + * dsquare(forces[k + 1]) + dsquare(forces[k + 2]));*/ /* printf("tmpsum = %f (forces)\n",tmpsum);*/ /* }*/ } /* end F I F T H loop over atoms */ /* S I X T H loop: EAM force */ if (uf) { /* only required if we calc forces */ for (i = 0; i < g_config.inconf[h]; i++) { atom = g_config.conf_atoms + i + g_config.cnfstart[h] - g_mpi.firstatom; n_i = 3 * (g_config.cnfstart[h] + i); for (j = 0; j < atom->num_neigh; j++) { /* loop over neighbors */ neigh = atom->neigh + j; /* In small cells, an atom might interact with itself */ self = (neigh->nr == i + g_config.cnfstart[h]) ? 1 : 0; col_F = g_calc.paircol + g_param.ntypes + atom->type; /* column of F */ r = neigh->r; /* are we within reach? */ if ((r < g_pot.calc_pot.end[neigh->col[1]]) || (r < g_pot.calc_pot.end[col_F - g_param.ntypes])) { rho_grad = (r < g_pot.calc_pot.end[neigh->col[1]]) ? splint_grad_dir(&g_pot.calc_pot, xi, neigh->slot[1], neigh->shift[1], neigh->step[1]) : 0.0; if (atom->type == neigh->type) /* use actio = reactio */ rho_grad_j = rho_grad; else rho_grad_j = (r < g_pot.calc_pot.end[col_F - g_param.ntypes]) ? (*g_splint_grad)(&g_pot.calc_pot, xi, col_F - g_param.ntypes, r) : 0.0; /* now we know everything - calculate forces */ eam_force = (rho_grad * atom->gradF + rho_grad_j * g_config.conf_atoms[(neigh->nr) - g_mpi.firstatom] .gradF); /* avoid double counting if atom is interacting with a copy of itself */ if (self) eam_force *= 0.5; tmp_force.x = neigh->dist_r.x * eam_force; tmp_force.y = neigh->dist_r.y * eam_force; tmp_force.z = neigh->dist_r.z * eam_force; forces[n_i + 0] += tmp_force.x; forces[n_i + 1] += tmp_force.y; forces[n_i + 2] += tmp_force.z; /* actio = reactio */ n_j = 3 * neigh->nr; forces[n_j + 0] -= tmp_force.x; forces[n_j + 1] -= tmp_force.y; forces[n_j + 2] -= tmp_force.z; #if defined(STRESS) /* and stresses */ if (us) { forces[stresses + 0] -= neigh->dist.x * tmp_force.x; forces[stresses + 1] -= neigh->dist.y * tmp_force.y; forces[stresses + 2] -= neigh->dist.z * tmp_force.z; forces[stresses + 3] -= neigh->dist.x * tmp_force.y; forces[stresses + 4] -= neigh->dist.y * tmp_force.z; forces[stresses + 5] -= neigh->dist.z * tmp_force.x; } #endif // STRESS } /* within reach */ } /* loop over neighbours */ #if defined(FWEIGHT) /* Weigh by absolute value of force */ forces[n_i + 0] /= FORCE_EPS + atom->absforce; forces[n_i + 1] /= FORCE_EPS + atom->absforce; forces[n_i + 2] /= FORCE_EPS + atom->absforce; #endif // FWEIGHT /* sum up forces */ #if defined(CONTRIB) if (atom->contrib) #endif // CONTRIB tmpsum += g_config.conf_weight[h] * (dsquare(forces[n_i + 0]) + dsquare(forces[n_i + 1]) + dsquare(forces[n_i + 2])); } } /* end S I X T H loop over atoms */ /* whole energy contributions flow into tmpsum */ forces[g_calc.energy_p + h] /= (double)g_config.inconf[h]; forces[g_calc.energy_p + h] -= g_config.force_0[g_calc.energy_p + h]; tmpsum += g_config.conf_weight[h] * g_param.eweight * dsquare(forces[g_calc.energy_p + h]); #if defined(STRESS) /* whole stress contributions flow into tmpsum */ if (uf && us) { for (i = 0; i < 6; i++) { forces[stresses + i] /= g_config.conf_vol[h - g_mpi.firstconf]; forces[stresses + i] -= g_config.force_0[stresses + i]; tmpsum += g_config.conf_weight[h] * g_param.sweight * dsquare(forces[stresses + i]); } } #endif // STRESS /* limiting constraints per configuration */ tmpsum += g_config.conf_weight[h] * dsquare(forces[g_calc.limit_p + h]); } /* end M A I N loop over configurations */ } /* parallel region */ #if defined(MPI) /* Reduce rho_sum */ MPI_Reduce(&rho_sum_loc, &rho_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); #else /* MPI */ rho_sum = rho_sum_loc; #endif // MPI /* dummy constraints (global) */ #if defined(APOT) /* add punishment for out of bounds (mostly for powell_lsq) */ if (g_mpi.myid == 0) { tmpsum += apot_punish(xi_opt, forces); } #endif // APOT #if !defined(NOPUNISH) if (g_mpi.myid == 0) { int g; for (g = 0; g < g_param.ntypes; g++) { #if defined(NORESCALE) /* clear field */ forces[g_calc.dummy_p + g_param.ntypes + g] = 0.0; /* Free end... */ /* NEW: Constraint on U': U'(1.0)=0.0; */ forces[g_calc.dummy_p + g] = DUMMY_WEIGHT * splint_grad(&calc_pot, xi, paircol + g_param.ntypes + g, 1.0); #else /* NOTHING */ forces[g_calc.dummy_p + g_param.ntypes + g] = 0.0; /* Free end... */ /* constraints on U`(n) */ forces[g_calc.dummy_p + g] = DUMMY_WEIGHT * (*g_splint_grad)( &g_pot.calc_pot, xi, g_calc.paircol + g_param.ntypes + g, 0.5 * (g_pot.calc_pot .begin[g_calc.paircol + g_param.ntypes + g] + g_pot.calc_pot .end[g_calc.paircol + g_param.ntypes + g])) - g_config.force_0[g_calc.dummy_p + g]; #endif // NORESCALE tmpsum += dsquare(forces[g_calc.dummy_p + g_param.ntypes + g]); tmpsum += dsquare(forces[g_calc.dummy_p + g]); } /* loop over types */ #if defined(NORESCALE) /* NEW: Constraint on n: <n>=1.0 ONE CONSTRAINT ONLY */ /* Calculate averages */ rho_sum /= (double)natoms; /* ATTN: if there are invariant potentials, things might be problematic */ forces[dummy_p + g_param.ntypes] = DUMMY_WEIGHT * (rho_sum - 1.0); tmpsum += dsquare(forces[dummy_p + g_param.ntypes]); #endif // NORESCALE } #endif // NOPUNISH #if defined(MPI) /* reduce global sum */ sum = 0.0; MPI_Reduce(&tmpsum, &sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); /* gather forces, energies, stresses */ if (g_mpi.myid == 0) { /* root node already has data in place */ /* forces */ MPI_Gatherv(MPI_IN_PLACE, g_mpi.myatoms, g_mpi.MPI_VECTOR, forces, g_mpi.atom_len, g_mpi.atom_dist, g_mpi.MPI_VECTOR, 0, MPI_COMM_WORLD); /* energies */ MPI_Gatherv(MPI_IN_PLACE, g_mpi.myconf, MPI_DOUBLE, forces + g_calc.energy_p, g_mpi.conf_len, g_mpi.conf_dist, MPI_DOUBLE, 0, MPI_COMM_WORLD); #if defined(STRESS) /* stresses */ MPI_Gatherv(MPI_IN_PLACE, g_mpi.myconf, g_mpi.MPI_STENS, forces + g_calc.stress_p, g_mpi.conf_len, g_mpi.conf_dist, g_mpi.MPI_STENS, 0, MPI_COMM_WORLD); #endif // STRESS #if !defined(NORESCALE) /* punishment constraints */ MPI_Gatherv(MPI_IN_PLACE, g_mpi.myconf, MPI_DOUBLE, forces + g_calc.limit_p, g_mpi.conf_len, g_mpi.conf_dist, MPI_DOUBLE, 0, MPI_COMM_WORLD); #endif // !NORESCALE } else { /* forces */ MPI_Gatherv(forces + g_mpi.firstatom * 3, g_mpi.myatoms, g_mpi.MPI_VECTOR, forces, g_mpi.atom_len, g_mpi.atom_dist, g_mpi.MPI_VECTOR, 0, MPI_COMM_WORLD); /* energies */ MPI_Gatherv(forces + g_calc.energy_p + g_mpi.firstconf, g_mpi.myconf, MPI_DOUBLE, forces + g_calc.energy_p, g_mpi.conf_len, g_mpi.conf_dist, MPI_DOUBLE, 0, MPI_COMM_WORLD); #if defined(STRESS) /* stresses */ MPI_Gatherv(forces + g_calc.stress_p + 6 * g_mpi.firstconf, g_mpi.myconf, g_mpi.MPI_STENS, forces + g_calc.stress_p, g_mpi.conf_len, g_mpi.conf_dist, g_mpi.MPI_STENS, 0, MPI_COMM_WORLD); #endif // STRESS #if !defined(NORESCALE) /* punishment constraints */ MPI_Gatherv(forces + g_calc.limit_p + g_mpi.firstconf, g_mpi.myconf, MPI_DOUBLE, forces + g_calc.limit_p, g_mpi.conf_len, g_mpi.conf_dist, MPI_DOUBLE, 0, MPI_COMM_WORLD); #endif // !NORESCALE } /* no need to pick up dummy constraints - they are already @ root */ #else sum = tmpsum; /* global sum = local sum */ #endif // MPI /* root process exits this function now */ if (g_mpi.myid == 0) { g_calc.fcalls++; /* Increase function call counter */ if (isnan(sum)) { #if defined(DEBUG) printf("\n--> Force is nan! <--\n\n"); #endif // DEBUG return 10e10; } else return sum; } } /* once a non-root process arrives here, all is done. */ return -1.0; }
void do_voronoi_2d(void) { int i, j, l, n; real det, detinv; vektor icoord, jcoord, tmpvertex, vertex[NUM]; int ok, index[NUM]; int vertexcount, vertexnum, edgesnum; int *edges; ivektor ivertex[NUM]; real idist2, jdist2; real sin, cos, maxcos; int minj, ord[NUM]; /* Allocate memory for data of edges */ edges = (int *) malloc( neighnum * sizeof(int) ); if( edges == NULL ) error("Cannot allocate memory for vertices"); vertexcount = 0; area = 0.0; /* Each possible vertex defined by the intersection of 2 lines is examined */ for (i=0; i<(neighnum-1); ++i) { icoord.x = candcoord[i].x; icoord.y = candcoord[i].y; idist2 = -canddist2[i]; for (j=i+1; j<neighnum; ++j) { jcoord.x = candcoord[j].x; jcoord.y = candcoord[j].y; jdist2 = -canddist2[j]; det = icoord.x * jcoord.y - icoord.y * jcoord.x; /* check whether edges intersect */ if ( SQR(det) > TOL2) { detinv = 1.0 / det; tmpvertex.x = ( icoord.y * jdist2 - jcoord.y * idist2 ) * detinv; tmpvertex.y = ( jcoord.x * idist2 - icoord.x * jdist2 ) * detinv; /* Check whether vertex belongs to voronoi cell */ l = 0; ok = 1; do { if( l!=i && l!=j ) ok = ( SPROD(candcoord[l] , tmpvertex ) <= canddist2[l] ); ++l; } while ( (ok==1) && (l<neighnum) ); if( ok==1 ) { ivertex[vertexcount].x = i; ivertex[vertexcount].y = j; vertex[vertexcount].x = 0.5 * tmpvertex.x; vertex[vertexcount].y = 0.5 * tmpvertex.y; ++vertexcount; } } } } vertexnum = vertexcount; /* Check whether some vertices coincide */ for ( i=0; i<vertexnum; ++i ) { index[i] = 1; for (j=i+1; j<vertexnum; ++j ) if ( (SQR(vertex[j].x-vertex[i].x)<TOL2) && (SQR(vertex[j].y-vertex[i].y)<TOL2) ) index[i] = 0; } /* Remove coincident vertices */ j = 0; for ( i=0; i<vertexnum; ++i ) if ( index[i] != 0 ) { ivertex[j].x = ivertex[i].x; ivertex[j].y = ivertex[i].y; vertex[j].x = vertex[i].x; vertex[j].y = vertex[i].y; ++j; } vertexnum = j; /* Number of vertices of Voronoi cell must be greater than 2 */ if ( vertexnum < 3 ) area = 0.0; else { /* Initialization */ for (n=0; n<neighnum; ++n) edges[n] = 0; for (n=0; n<vertexnum; ++n) { ++edges[ivertex[n].x]; ++edges[ivertex[n].y]; } /* Number of edges of Voronoi cell */ edgesnum = 0; for (n=0; n<neighnum; ++n) { edgesnum += edges[n]; } edgesnum /= 2; /* Check whether number of vertices equals number of edges */ if ( edgesnum == vertexnum ) { /* Statistics */ if( neighnum > maxneigh ) maxneigh = neighnum; sumneigh += neighnum; if( vertexnum > maxvert ) maxvert = vertexnum; sumvert += vertexnum; if( edgesnum > maxedges ) maxedges = edgesnum; sumedges += edgesnum; ++atomcount; /* Order vertices */ ord[0] = 0; for ( i=0; i<vertexnum-1; ++i) { maxcos = -1.0; for (j=0; j<vertexnum; ++j) { sin = vertex[j].x * vertex[ord[i]].y - vertex[j].y * vertex[ord[i]].x; if ( sin > TOL ) { cos = SPROD( vertex[j], vertex[ord[i]] )/ sqrt(SPROD(vertex[j],vertex[j]))/ sqrt(SPROD(vertex[ord[i]],vertex[ord[i]])); if ( cos > maxcos ) { maxcos = cos; minj = j; } } } ord[i+1] = minj; } /* Compute area of voronoi cell */ for (i=0; i<vertexnum-1; ++i) area += 0.5 * (vertex[ord[i+1]].x * vertex[ord[i]].y - vertex[ord[i+1]].y * vertex[ord[i]].x); area += 0.5 * (vertex[ord[0]].x * vertex[ord[vertexnum-1]].y - vertex[ord[0]].y * vertex[ord[vertexnum-1]].x); } /* number of edges == number of vertices */ else area = 0.0; } /* vertesnum < 3 */ free(edges); }
void do_voronoi_3d(void) { int i, j, k, l, n; real ab, bc, ca, da, db, dc, det, detinv, tmp; vektor icoord, jcoord, kcoord; real idist2, jdist2, kdist2; vektor tmpvek, tmpvertex, vertex[NUM]; int ok, vertexcount, vertexnum, facesnum, edgesnum; int *vertexnumi; real area_i, height; real sin, cos, maxcos; int mink, ord[NUM], index[NUM], surfind[NUM]; vektor *coord, center; vektorstr *vertexloc; /* Allocate memory for vertices */ vertexnumi = (int *) malloc( neighnum * sizeof(int)); coord = (vektor *) malloc( neighnum * sizeof(vektor)); vertexloc = (vektorstr *) malloc( neighnum * sizeof(vektorstr)); if( vertexloc == NULL || coord == NULL || vertexnumi == NULL ) error("Cannot allocate memory for vertices!\n"); vertexcount = 0; volume = 0.0; /* Each possible vertex defined by the intersection of 3 planes is examined */ for (i=0; i<neighnum-2; ++i) { icoord.x = candcoord[i].x; icoord.y = candcoord[i].y; icoord.z = candcoord[i].z; idist2 = -canddist2[i]; for (j=i+1; j<neighnum-1; ++j) { jcoord.x = candcoord[j].x; jcoord.y = candcoord[j].y; jcoord.z = candcoord[j].z; jdist2 = -canddist2[j]; ab = icoord.x * jcoord.y - jcoord.x * icoord.y; bc = icoord.y * jcoord.z - jcoord.y * icoord.z; ca = icoord.z * jcoord.x - jcoord.z * icoord.x; da = idist2 * jcoord.x - jdist2 * icoord.x; db = idist2 * jcoord.y - jdist2 * icoord.y; dc = idist2 * jcoord.z - jdist2 * icoord.z; for (k=j+1; k<neighnum; ++k) { kcoord.x = candcoord[k].x; kcoord.y = candcoord[k].y; kcoord.z = candcoord[k].z; kdist2 = -canddist2[k]; det = kcoord.x * bc + kcoord.y * ca + kcoord.z * ab; /* Check whether planes intersect */ if( SQR(det) > TOL2 ) { detinv = 1.0 / det; tmpvertex.x = ( -kdist2 * bc + kcoord.y * dc - kcoord.z * db ) * detinv; tmpvertex.y = ( -kcoord.x * dc - kdist2 * ca + kcoord.z * da ) * detinv; tmpvertex.z = ( kcoord.x * db - kcoord.y * da - kdist2 * ab ) * detinv; /* Check whether vertex belongs to the Voronoi cell */ l = 0; ok = 1; do { if( l!=i && l!=j && l!=k) ok = ( SPROD( candcoord[l], tmpvertex ) <= canddist2[l] + TOL_VERT2 ) ; ++l; } while( ok && (l<neighnum)); if( ok ) { vertex[vertexcount].x = 0.5 * tmpvertex.x; vertex[vertexcount].y = 0.5 * tmpvertex.y; vertex[vertexcount].z = 0.5 * tmpvertex.z; ++vertexcount; } } /* Planes intersect */ } /* k */ } /* j */ } /* i */ vertexnum = vertexcount; /* Check whether some vertices coincide */ for ( i=0; i<vertexnum; ++i ) { index[i] = 1; for ( j=i+1; j<vertexnum; ++j ) { tmpvek.x = vertex[j].x - vertex[i].x; tmpvek.y = vertex[j].y - vertex[i].y; tmpvek.z = vertex[j].z - vertex[i].z; if ( SPROD( tmpvek, tmpvek) < TOL2 ) index[i] = 0; } } /* Remove coincident vertices */ j = 0; for ( i=0; i<vertexnum; ++i ) if ( index[i] != 0 ) { vertex[j].x = vertex[i].x; vertex[j].y = vertex[i].y; vertex[j].z = vertex[i].z; ++j; } vertexnum = j; /* Number of vertices of Voronoi cell must be greater than 3 */ if(vertexnum > 3 ) { /* Check whether faces exist */ facesnum = 0; /* Each neighbour atom i corresponds to at most one surface * * Sum over all surfaces */ for (i=0; i<neighnum; ++i) { /* Coordinates of center of surface i */ coord[i].x = 0.5 * candcoord[i].x; coord[i].y = 0.5 * candcoord[i].y; coord[i].z = 0.5 * candcoord[i].z; /* Look for vertices that belong to surface i */ vertexnumi[i] = 0; for (j=0; j<vertexnum; ++j) { surfind[j] = 0; vertexloc[i][j].x = vertex[j].x - coord[i].x; vertexloc[i][j].y = vertex[j].y - coord[i].y; vertexloc[i][j].z = vertex[j].z - coord[i].z; tmp = SPROD(coord[i],vertexloc[i][j]); if( SQR(tmp) < TOL_DIST2 ) { /* vertex j belongs to surface i */ surfind[j] = 1; ++vertexnumi[i]; } } /* Surface i exists */ if (vertexnumi[i] > 2) { ++facesnum; /* Compute coordinates of vertices belonging to surface i */ k = 0; for (j=0; j<vertexnum; ++j) if( surfind[j] == 1) { vertexloc[i][k].x = vertexloc[i][j].x; vertexloc[i][k].y = vertexloc[i][j].y; vertexloc[i][k].z = vertexloc[i][j].z; ++k; } } /* Transform into center of mass system */ center.x = 0.0; center.y = 0.0; center.z = 0.0; if( vertexnumi[i] > 2) { for ( j=0; j<vertexnumi[i]; ++j) { center.x += vertexloc[i][j].x; center.y += vertexloc[i][j].y; center.z += vertexloc[i][j].z; } tmp = 1.0 / vertexnumi[i]; center.x *= tmp; center.y *= tmp; center.z *= tmp; for ( j=0; j<vertexnumi[i]; ++j) { vertexloc[i][j].x -= center.x; vertexloc[i][j].y -= center.y; vertexloc[i][j].z -= center.z; } } } /* i */ /* Number of edges of Voronoi cell */ edgesnum = 0; for ( n=0; n<neighnum; ++n) if( vertexnumi[n] > 2) edgesnum += vertexnumi[n]; edgesnum /= 2; /* Check whether Euler relation holds */ if ( (vertexnum - edgesnum + facesnum) == 2 ) { /* Statistics */ if( neighnum > maxneigh ) maxneigh = neighnum; sumneigh += neighnum; if( vertexnum > maxvert ) maxvert = vertexnum; sumvert += vertexnum; if( edgesnum > maxedges ) maxedges = edgesnum; sumedges += edgesnum; if( facesnum > maxfaces ) maxfaces = facesnum; sumfaces += facesnum; ++ atomcount; /* Compute volume of Voronoi cell */ /* For all potential faces */ for (i=0; i<neighnum; ++i) /* Surface i exists */ if(vertexnumi[i] > 2) { /* Sort vertices of face i */ ord[0] = 0; for (j=0; j<vertexnumi[i]-1; ++j) { maxcos = -1.0; for (k=0; k<vertexnumi[i]; ++k) { tmpvek.x = vertexloc[i][k].y * vertexloc[i][ord[j]].z - vertexloc[i][k].z * vertexloc[i][ord[j]].y; tmpvek.y = vertexloc[i][k].z * vertexloc[i][ord[j]].x - vertexloc[i][k].x * vertexloc[i][ord[j]].z; tmpvek.z = vertexloc[i][k].x * vertexloc[i][ord[j]].y - vertexloc[i][k].y * vertexloc[i][ord[j]].x; sin = SPROD( tmpvek, coord[i]); if( sin > TOL ) { cos = SPROD( vertexloc[i][k], vertexloc[i][ord[j]] )/ sqrt(SPROD(vertexloc[i][k],vertexloc[i][k]))/ sqrt(SPROD(vertexloc[i][ord[j]],vertexloc[i][ord[j]])); if( cos > maxcos ) { maxcos = cos; mink = k; } } } ord[j+1] = mink; } /* Compute area of surface i */ area_i = 0.0; height = sqrt(SPROD( coord[i], coord[i] )); tmp = 1.0 / height; for (j=0; j<vertexnumi[i]-1; ++j) { tmpvek.x = vertexloc[i][ord[j+1]].y * vertexloc[i][ord[j]].z - vertexloc[i][ord[j+1]].z * vertexloc[i][ord[j]].y; tmpvek.y = vertexloc[i][ord[j+1]].z * vertexloc[i][ord[j]].x - vertexloc[i][ord[j+1]].x * vertexloc[i][ord[j]].z; tmpvek.z = vertexloc[i][ord[j+1]].x * vertexloc[i][ord[j]].y - vertexloc[i][ord[j+1]].y * vertexloc[i][ord[j]].x; area_i += 0.5 * SPROD( tmpvek, coord[i] ) * tmp; } tmpvek.x = vertexloc[i][ord[0]].y * vertexloc[i][ord[vertexnumi[i]-1]].z - vertexloc[i][ord[0]].z * vertexloc[i][ord[vertexnumi[i]-1]].y; tmpvek.y = vertexloc[i][ord[0]].z * vertexloc[i][ord[vertexnumi[i]-1]].x - vertexloc[i][ord[0]].x * vertexloc[i][ord[vertexnumi[i]-1]].z; tmpvek.z = vertexloc[i][ord[0]].x * vertexloc[i][ord[vertexnumi[i]-1]].y - vertexloc[i][ord[0]].y * vertexloc[i][ord[vertexnumi[i]-1]].x; area_i += 0.5 * SPROD( tmpvek, coord[i] ) * tmp; /* Volume of Voronoi cell */ volume += area_i * height / 3.0; } /* vertexnum[i] > 2 */ } /* Euler relation holds */ } /* Number of vertices > 3 */ free(vertexloc); free(coord); free(vertexnumi); }
void voronoi(void) { cell *p, *q; int i, j, k, l, m, n, r, s, t; int v, w, neighcount; vektor tmp; vektor pbc; real tmpdist2; int num = NUM; candcoord = (vektor *) malloc( num * sizeof(vektor)); canddist2 = (real *) malloc( num * sizeof(real)); /* for each cell */ for (i=0; i < cell_dim.x; ++i) for (j=0; j < cell_dim.y; ++j) #ifndef TWOD for (k=0; k < cell_dim.z; ++k) #endif { #ifdef TWOD p = PTR_2D_V(cell_array,i,j ,cell_dim); #else p = PTR_3D_V(cell_array,i,j,k,cell_dim); #endif /* for each atom in first cell */ for (v=0; v<p->n; ++v) { neighcount = 0; /* For each neighbour of this cell */ for (l=-1; l <= 1; ++l) for (m=-1; m <= 1; ++m) #ifndef TWOD for (n=-1; n <= 1; ++n) #endif { /* Calculate Indicies of Neighbour */ r = i+l; pbc.x = 0; s = j+m; pbc.y = 0; #ifndef TWOD t = k+n; pbc.z = 0; #endif /* deal with periodic boundary conditions if necessary */ if (r<0) { if (pbc_dirs.x==1) { r = cell_dim.x-1; pbc.x -= box_x.x; pbc.y -= box_x.y; #ifndef TWOD pbc.z -= box_x.z; #endif } else continue; } if (s<0) { if (pbc_dirs.y==1) { s = cell_dim.y-1; pbc.x -= box_y.x; pbc.y -= box_y.y; #ifndef TWOD pbc.z -= box_y.z; #endif } else continue; } #ifndef TWOD if (t<0) { if (pbc_dirs.z==1) { t = cell_dim.z-1; pbc.x -= box_z.x; pbc.y -= box_z.y; pbc.z -= box_z.z; } else continue; } #endif if (r>cell_dim.x-1) { if (pbc_dirs.x==1) { r = 0; pbc.x += box_x.x; pbc.y += box_x.y; #ifndef TWOD pbc.z += box_x.z; #endif } else continue; } if (s>cell_dim.y-1) { if (pbc_dirs.y==1) { s = 0; pbc.x += box_y.x; pbc.y += box_y.y; #ifndef TWOD pbc.z += box_y.z; #endif } else continue; } #ifndef TWOD if (t>cell_dim.z-1) { if (pbc_dirs.z==1) { t = 0; pbc.x += box_z.x; pbc.y += box_z.y; pbc.z += box_z.z; } else continue; } #endif /* Neighbour cell (note that p==q ist possible) */ #ifdef TWOD q = PTR_2D_V(cell_array,r,s,cell_dim); #else q = PTR_3D_V(cell_array,r,s,t,cell_dim); #endif /* for each particle in second cell */ for (w=0; w<q->n; ++w) { tmp.x = q->ort[w].x - p->ort[v].x + pbc.x; tmp.y = q->ort[w].y - p->ort[v].y + pbc.y; #ifndef TWOD tmp.z = q->ort[w].z - p->ort[v].z + pbc.z; #endif tmpdist2 = SPROD( tmp, tmp ); /* Candidates for Voronoi cells */ #ifdef STRESS if( (tmpdist2 <= r2_cut) && (tmpdist2 > TOL2)) #else if( (tmpdist2 <= SQR(r_max)) && (tmpdist2 > TOL2)) #endif { if( neighcount > num-1 ) { num += 10; candcoord = (vektor *) realloc(candcoord, num * sizeof(vektor)); canddist2 = (real *) realloc(canddist2, num * sizeof(real)); } candcoord[neighcount].x = tmp.x; candcoord[neighcount].y = tmp.y; #ifndef TWOD candcoord[neighcount].z = tmp.z; #endif canddist2[neighcount] = tmpdist2; ++ neighcount; } } /* w */ } /* lmn */ /* If there are less than four (three) points, a polyhedron (polygon) cannot be constructed */ #ifndef TWOD if( (neighnum = neighcount) < 4 ) volume = 0.0; #else if( (neighnum = neighcount) < 3 ) area = 0.0; #endif else { /* Sort candidates in ascending order of distance */ sort(); /* Perform Voronoi analysis */ #ifdef TWOD do_voronoi_2d(); #else do_voronoi_3d(); #endif } #ifndef TWOD p->vol[v] = volume; #else p->vol[v] = area; #endif } /* v */ } /* ijk */ }