/*****************************/ /* Lattice Dynamics of Argon */ /* */ /* ver 1.2 Mar.22, 1999 */ /* developed by Ju Li (MIT) */ /*****************************/ /* Version Notes: 1. TTY interface. 2. Woon's pair potential. 3. Upon request, save the real space dynamical matrices of atom(s) on a .dm file so if it is a perfect crystal, it can be used by dm.m to plot out the phonon dispersion curves. 4. Calculate and diagonalize the dynamical matrix for arbitrary configuration and save the eigenmodes on a .eig file, which can be handled by collect.m. 5. User-defined supercell k sampling or random sampling. 6. Upon request, evaluate DOS and LDOS(s). */ #include #include #include /** universal constants **/ #ifndef PI #define PI 3.14159265358979323846 #endif #define AVO 6.0221367E+23 #define BOLZ 1.380658E-23 /* in J/K */ #define HBAR 1.054589E-34 /* in J.s */ #define EV_IN_J 1.60217733E-19 #define HARTREE_IN_J 4.3597482E-18 #define BOHR_RADIUS_IN_A 0.529177249 /** argon LJ6-12 constants **/ #define SIGMA_IN_M (3.405E-10) #define EPSILON_IN_J (119.8*BOLZ) #define ARGON_MASS_IN_KG (39.948*1E-3/AVO) /** fundamental reduced units **/ #define ULENGTH_IN_M SIGMA_IN_M #define UENERGY_IN_J EPSILON_IN_J #define UMASS_IN_KG ARGON_MASS_IN_KG /** derived reduced units **/ #define ULENGTH_IN_A (ULENGTH_IN_M*1E10) #define UTIME_IN_SECOND (ULENGTH_IN_M*sqrt(UMASS_IN_KG/UENERGY_IN_J)) #define UTIME_IN_PS (UTIME_IN_SECOND*1E12) #define UFREQ_IN_HZ (1./UTIME_IN_SECOND/2./PI) #define UFREQ_IN_THZ (1E-12*UFREQ_IN_HZ) #define UFREQ_IN_MEV (HBAR*2*PI*UFREQ_IN_HZ/EV_IN_J*1000.) /* interaction cutoff */ #define RCUT (2.5*SIGMA_IN_M/ULENGTH_IN_M) /* maximum possible reduced density of the solid */ #define MAXDENSITY 2.0 #define MAXNEIGHBOR ((int)(4./3.*PI*RCUT*RCUT*RCUT*MAXDENSITY)) #define MAX_STRLEN 150 #define MAX_LDOS_ATOM_BRACKETS 10 #define DEFAULT_ATOM_TP "Ar" #define TP_STRLEN 2 static int np, LJ6_12, WOON, random_k_sampling, save_dm, save_eig, save_dos, save_ldos, num_dm, num_kpts, iseed, num_bins, num_ldos_atom_brackets, num_ldos_atoms, *dm_index, nc[3], ldos_atom_head[MAX_LDOS_ATOM_BRACKETS], ldos_atom_tail[MAX_LDOS_ATOM_BRACKETS]; static double volume, minfreq, maxfreq, delfreq, *s, *mass, *dm, *dos, *ldos, *kpts, *ap, *w, *z, *work, *rwork, delta_s[3*MAXNEIGHBOR], H0[3][3], M[3][3], H[3][3], HI[3][3]; static char config_name[MAX_STRLEN], potential_function[MAX_STRLEN], ldos_atom_list[MAX_STRLEN], fn_config_read[MAX_STRLEN], fn_dm[MAX_STRLEN], fn_kpt[MAX_STRLEN], fn_eig[MAX_STRLEN], fn_dos[MAX_STRLEN], fn_ldos[MAX_STRLEN], **tp; static FILE *filehandle; static void force_constants (double H[3][3], double *s, double *force_constant); static int images_within_radius (double H[3][3], double HI[3][3], double *s, int j, int i, double delta_s[3*MAXNEIGHBOR]); static int parse_number_string(char *sentence, int head[], int tail[]); static double matinv(double A[3][3], double B[3][3]); static double frandom(); int main() { int i, j, k, l, m, ip; double cc, dd, ee; char *p; /* read instructions from input, such as "don" file */ printf ("Reading in instructions...\n"); fscanf (stdin, "Name of configuration (->*, " "default=Data/fcc_unit_cell):\n%s\n\n", config_name); if (!strcasecmp(config_name,"default")) sprintf(config_name, "Data/fcc_unit_cell"); fscanf (stdin, "Pair potential to be used (LJ6_12 or WOON):\n%s\n\n", potential_function); LJ6_12 = !strcmp(potential_function, "LJ6_12"); WOON = !strcmp(potential_function, "WOON"); if ( (!LJ6_12) && (!WOON) ) { fprintf (stdout, "Error: choose between LJ6_12 and WOON " "pair potential\n"); exit(1); } fscanf (stdin, "Load configuration from file (default=*):\n%s\n\n", fn_config_read); if (!strcasecmp(fn_config_read,"default")) sprintf(fn_config_read, "%s", config_name); fscanf (stdin, "Apply affine transformation " "(11,22,33,21=12,31=13,32=23):\n"); fscanf (stdin, "%lf %lf %lf %lf %lf %lf\n\n", &M[0][0], &M[1][1], &M[2][2], &M[0][1], &M[0][2], &M[1][2]); /* symmetrize */ M[1][0] = M[0][1]; M[2][0] = M[0][2]; M[2][1] = M[1][2]; fscanf (stdin, "Save real-space dynamical matrices on file\n"); fscanf (stdin, "(NULL=don't, default=*.dm):\n%s\n\n", fn_dm); if (save_dm=strcasecmp(fn_dm, "NULL")) if (!strcasecmp(fn_dm, "default")) sprintf(fn_dm, "%s.dm", config_name); fscanf (stdin, "Supercell k-point sampling file (if numeric then\n"); fscanf (stdin, "apply random sampling in BZ, default=Kpts/fcckpt.10):\n%s\n\n", fn_kpt); if (random_k_sampling=((*fn_kpt>='0')&&(*fn_kpt<='9'))) { /* user inputs a integer, do random k sampling */ for (p=fn_kpt;(*p>='0')&&(*p<='9');p++); *p = 0; sscanf(fn_kpt, "%d", &num_kpts); } else if (!strcasecmp(fn_kpt, "default")) sprintf(fn_kpt, "Kpts/fcckpt.10"); fscanf (stdin, "Save normal modes on file (NULL=don't, default=*.eig):\n%s\n\n", fn_eig); if (save_eig=strcasecmp(fn_eig, "NULL")) if (!strcasecmp(fn_eig, "default")) sprintf(fn_eig, "%s.eig", config_name); fscanf (stdin, "List atoms (NULL=don't, default=all atoms):\n%s\n\n", ldos_atom_list); fscanf (stdin, "Save DOS on file (NULL=don't, default=*.out):\n%s\n\n", fn_dos); if (save_dos=strcasecmp(fn_dos, "NULL")) if (!strcasecmp(fn_dos, "default")) sprintf(fn_dos, "%s.out", config_name); fscanf (stdin, "Save LDOS on file (NULL=don't, default=*_ldos.out):\n%s\n\n", fn_ldos); if (save_ldos=strcasecmp(fn_ldos, "NULL")) if (!strcasecmp(fn_ldos, "default")) sprintf(fn_ldos, "%s_ldos.out", config_name); fscanf (stdin, "Minimum frequency (in THz):\n%lf\n\n", &minfreq); fscanf (stdin, "Maximum frequency (in THz):\n%lf\n\n", &maxfreq); fscanf (stdin, "How many bins to represent DOS and LDOS:\n%d\n\n", &num_bins); delfreq = (maxfreq-minfreq)/num_bins; fscanf (stdin, "Random number generator seed (0->time seed):\n"); fscanf (stdin, "%d\n", &iseed); if (iseed == 0) iseed = time(NULL); srandom ((unsigned)iseed); printf("Instructions read complete.\n\n"); printf ("Vibrational eigenmodes will be calculated for \"%s\"\n", config_name); printf ("using %s potential with Rcut = %f = %f Angstrom.\n\n", potential_function, RCUT, RCUT*ULENGTH_IN_A); printf ("The configuration is being loaded from \"%s\".\n", fn_config_read); if ((filehandle=fopen(fn_config_read, "r"))==NULL) { fprintf(stdout, "Error: cannot open file \"%s\"\n.", fn_config_read); exit(1); } fscanf (filehandle, "Number of particles = %d\n\n", &np); printf ("Allocate simple arrays for %d particles.\n\n", np); s = (double *) malloc(3*np*sizeof(double)); mass = (double *) malloc(np*sizeof(double)); /* name space for atom type */ tp = (char **) malloc (np*sizeof(char *)); tp[0] = (char *) malloc (np*(TP_STRLEN+1)*sizeof(char)); for (i=1; i 0.85*MAXDENSITY) { printf("Current density %f is above 85%% of MAXDENSITY = %f.\n", np/volume, MAXDENSITY); printf("Overflow possible. Please increase MAXDENSITY.\n"); exit(1); } /* read in reduced coordinates from configuration file */ fscanf(filehandle, "TP Mass(amu) sx sy sz" " sx1(1/ns) sy1(1/ns) sz1(1/ns)\n"); for (i=0; i<3*np; i+=3) { fscanf (filehandle, "%2s %lf %lf %lf %lf %lf %lf %lf\n", tp[i/3], &mass[i/3], &s[i], &s[i+1], &s[i+2], &cc, &cc, &cc); /* convert from AMU to reduced mass unit */ mass[i/3] = mass[i/3]/AVO*1E-3/UMASS_IN_KG; } fclose (filehandle); printf ("Configuration \"%s\" generated successfully on the computer.\n\n", config_name); /* count the total number of interactions */ printf ("Counting all possible (image) interactions within Rcut = %fA.\n", RCUT*ULENGTH_IN_A); for (num_dm=0,k=0,i=0; iMAX_LDOS_ATOM_BRACKETS) { printf ("number of brackets more than MAX_LDOS_ATOM_BRACKETS.\n"); exit(1); } } /* find out how many atoms we need to keep track of */ printf ("Number of brackets recognized = %d. They are\n", num_ldos_atom_brackets); for (num_ldos_atoms=0,i=0; i\n"); } if (k%(int)ceil(10000./np/np/np)==0) printf ("Doing k-point %d.\n", k); /* assemble the k-space dynamical matrix */ for (i=0; i<3*np*(3*np+1); i++) ap[i]=0.; for (ip=0; ip=0.)?sqrt(w[l]):-sqrt(-w[l]); if (save_eig) fprintf (filehandle, "%f ", w[l]); /* frequency bin index */ ip = floor((w[l]-minfreq)/delfreq); if ( (ip<0) || (ip>=num_bins) ) { printf ("mode w=%f THz out of freq. bound\n", w[l]); ip = -1; } if (save_dos && (ip!=-1)) dos[ip] += kpts[4*k+3]; for (m=0,i=0; iMAX_STRLEN)) break; if ((*p>='0')&&(*p<='9')) { /* read in a pair */ strcpy(buffer, p); for (q=buffer;(*q>='0')&&(*q<='9');q++);*q=0; /* read in head */ sscanf(buffer, "%d", head+number_of_brackets); for (q++;(!((*q>='0')&&(*q<='9')))&&(*q!=0);q++); /* if there is no tail */ if (*q==0) { tail[number_of_brackets]=head[number_of_brackets]; return (number_of_brackets+1); } else { /* read in tail */ for (r=q;(*q>='0')&&(*q<='9');q++);*q=0; sscanf(r,"%d",tail+number_of_brackets); number_of_brackets++; p+=q-buffer; } } p++; } return number_of_brackets; } /* end parse_number_string() */ double matinv(double A[3][3], double B[3][3]) { double D11,D22,D33,D12,D21,D13,D31,D32,D23,volume; D11=A[1][1]*A[2][2]-A[1][2]*A[2][1]; D22=A[2][2]*A[0][0]-A[2][0]*A[0][2]; D33=A[0][0]*A[1][1]-A[0][1]*A[1][0]; D12=A[1][2]*A[2][0]-A[1][0]*A[2][2]; D23=A[2][0]*A[0][1]-A[2][1]*A[0][0]; D31=A[0][1]*A[1][2]-A[0][2]*A[1][1]; D13=A[1][0]*A[2][1]-A[2][0]*A[1][1]; D21=A[2][1]*A[0][2]-A[0][1]*A[2][2]; D32=A[0][2]*A[1][0]-A[1][2]*A[0][0]; volume=A[0][0]*D11+A[0][1]*D12+A[0][2]*D13; B[0][0]=D11/volume; B[1][1]=D22/volume; B[2][2]=D33/volume; B[0][1]=D21/volume; B[1][2]=D32/volume; B[2][0]=D13/volume; B[1][0]=D12/volume; B[2][1]=D23/volume; B[0][2]=D31/volume; return (volume); } /* end matinv() */ /* returns pseudo-random number on (0,1) */ double frandom() { return ((random()+0.5)/pow((double)2, (double)31)); } /* end frandom() */