/* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator http://lammps.sandia.gov, Sandia National Laboratories Steve Plimpton, sjplimp@sandia.gov Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. Contributing author: David Nicholson (MIT) ------------------------------------------------------------------------- This class contains functions to calculate the evolution of the periodic simulation box under elongational flow as described by Matthew Dobson in the arXiv preprint at http://arxiv.org/abs/1408.7078 Additionally, there are methods to do a lattice reduction to further reduce the simulation box using the method of Igor Semaev at http://link.springer.com/chapter/10.1007%2F3-540-44670-2_13 */ #include #include "uef_utils.h" namespace LAMMPS_NS { namespace UEF_utils{ UEFBox::UEFBox() { // initial box (also an inverse eigenvector matrix of automorphisms) double x = 0.327985277605681; double y = 0.591009048506103; double z = 0.736976229099578; l0[0][0]= z; l0[0][1]= y; l0[0][2]= x; l0[1][0]=-x; l0[1][1]= z; l0[1][2]=-y; l0[2][0]=-y; l0[2][1]= x; l0[2][2]= z; // spectra of the two automorpisms (log of eigenvalues) w1[0]=-1.177725211523360; w1[1]=-0.441448620566067; w1[2]= 1.619173832089425; w2[0]= w1[1]; w2[1]= w1[2]; w2[2]= w1[0]; // initialize theta // strain = w1 * theta1 + w2 * theta2 theta[0]=theta[1]=0; //set up the initial box l and change of basis matrix r for (int k=0;k<3;k++) for (int j=0;j<3;j++) { l[k][j] = l0[k][j]; r[j][k]=(j==k); } // get the initial rotation and upper triangular matrix rotation_matrix(rot, lrot ,l); // this is just a way to calculate the automorphisms // themselves, which play a minor role in the calculations // it's overkill, but only called once double t1[3][3]; double t1i[3][3]; double t2[3][3]; double t2i[3][3]; double l0t[3][3]; for (int k=0; k<3; ++k) for (int j=0; j<3; ++j) { t1[k][j] = exp(w1[k])*l0[k][j]; t1i[k][j] = exp(-w1[k])*l0[k][j]; t2[k][j] = exp(w2[k])*l0[k][j]; t2i[k][j] = exp(-w2[k])*l0[k][j]; l0t[k][j] = l0[j][k]; } mul_m2(l0t,t1); mul_m2(l0t,t1i); mul_m2(l0t,t2); mul_m2(l0t,t2i); for (int k=0; k<3; ++k) for (int j=0; j<3; ++j) { a1[k][j] = round(t1[k][j]); a1i[k][j] = round(t1i[k][j]); a2[k][j] = round(t2[k][j]); a2i[k][j] = round(t2i[k][j]); } // winv used to transform between // strain increments and theta increments winv[0][0] = w2[1]; winv[0][1] = -w2[0]; winv[1][0] = -w1[1]; winv[1][1] = w1[0]; double d = w1[0]*w2[1] - w2[0]*w1[1]; for (int k=0;k<2;k++) for (int j=0;j<2;j++) winv[k][j] /= d; } // get volume-correct r basis in: basis*cbrt(vol) = q*r void UEFBox::get_box(double x[3][3], double v) { v = cbrtf(v); for (int k=0;k<3;k++) for (int j=0;j<3;j++) x[k][j] = lrot[k][j]*v; } // get rotation matrix q in: basis = q*r void UEFBox::get_rot(double x[3][3]) { for (int k=0;k<3;k++) for (int j=0;j<3;j++) x[k][j]=rot[k][j]; } // diagonal, incompressible deformation void UEFBox::step_deform(const double ex, const double ey) { // increment theta values used in the reduction theta[0] +=winv[0][0]*ex + winv[0][1]*ey; theta[1] +=winv[1][0]*ex + winv[1][1]*ey; // deformation of the box. reduce() needs to // be called regularly or calculation will become // unstable double eps[3]; eps[0]=ex; eps[1] = ey; eps[2] = -ex-ey; for (int k=0;k<3;k++) { eps[k] = exp(eps[k]); l[k][0] = eps[k]*l[k][0]; l[k][1] = eps[k]*l[k][1]; l[k][2] = eps[k]*l[k][2]; } rotation_matrix(rot,lrot, l); } // reuduce the current basis bool UEFBox::reduce() { // determine how many times to apply the automorphisms // and find new theta values int f1 = round(theta[0]); int f2 = round(theta[1]); theta[0] -= f1; theta[1] -= f2; // store old change or basis matrix to determine if it // changes int r0[3][3]; for (int k=0;k<3;k++) for (int j=0;j<3;j++) r0[k][j]=r[k][j]; // this modifies the old change basis matrix to // handle the case where the automorphism transforms // the box but the reduced basis doesn't change // (r0 should still equal r at the end) if (f1 > 0) for (int k=0;k 0) for (int k=0;k (-q)*m = (-r) will hold row-wise if (r[0][0] < 0){ neg_row(q,0); neg_row(r,0); } if (r[1][1] < 0){ neg_row(q,1); neg_row(r,1); } if (r[2][2] < 0){ neg_row(q,2); neg_row(r,2); } } //sort columns in order of increasing length void col_sort(double b[3][3],int r[3][3]) { if (col_prod(b,0,0)>col_prod(b,1,1)) { col_swap(b,0,1); col_swap(r,0,1); } if (col_prod(b,0,0)>col_prod(b,2,2)) { col_swap(b,0,2); col_swap(r,0,2); } if (col_prod(b,1,1)>col_prod(b,2,2)) { col_swap(b,1,2); col_swap(r,1,2); } } // 1-2 reduction (Graham-Schmidt) void red12(double b[3][3],int r[3][3]) { int y = round(col_prod(b,0,1)/col_prod(b,0,0)); b[0][1] -= y*b[0][0]; b[1][1] -= y*b[1][0]; b[2][1] -= y*b[2][0]; r[0][1] -= y*r[0][0]; r[1][1] -= y*r[1][0]; r[2][1] -= y*r[2][0]; if (col_prod(b,1,1) < col_prod(b,0,0)) { col_swap(b,0,1); col_swap(r,0,1); red12(b,r); } } // The Semaev condition for a 3-reduced basis void red3(double b[3][3], int r[3][3]) { double b11 = col_prod(b,0,0); double b22 = col_prod(b,1,1); double b12 = col_prod(b,0,1); double b13 = col_prod(b,0,2); double b23 = col_prod(b,1,2); double y2 =-(b23/b22-b12/b22*b13/b11)/(1-b12/b11*b12/b22); double y1 =-(b13/b11-b12/b11*b23/b22)/(1-b12/b11*b12/b22); int x1=0; int x2=0; double min = col_prod(b,2,2); int x1v[2]; int x2v[2]; x1v[0] = floor(y1); x1v[1] = x1v[0]+1; x2v[0] = floor(y2); x2v[1] = x2v[0]+1; for (int k=0;k<2;k++) for (int j=0;j<2;j++) { double a[3]; a[0] = b[0][2] + x1v[k]*b[0][0] + x2v[j]*b[0][1]; a[1] = b[1][2] + x1v[k]*b[1][0] + x2v[j]*b[1][1]; a[2] = b[2][2] + x1v[k]*b[2][0] + x2v[j]*b[2][1]; double val=a[0]*a[0]+a[1]*a[1]+a[2]*a[2]; if (val