Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F90416237
fix_phonon.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Fri, Nov 1, 11:55
Size
29 KB
Mime Type
text/x-c
Expires
Sun, Nov 3, 11:55 (2 d)
Engine
blob
Format
Raw Data
Handle
22070766
Attached To
rLAMMPS lammps
fix_phonon.cpp
View Options
/* ----------------------------------------------------------------------
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 authors:
Ling-Ti Kong
Contact:
School of Materials Science and Engineering,
Shanghai Jiao Tong University,
800 Dongchuan Road, Minhang,
Shanghai 200240, CHINA
konglt@sjtu.edu.cn; konglt@gmail.com
------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "fix_phonon.h"
#include "fft3d_wrap.h"
#include "atom.h"
#include "compute.h"
#include "domain.h"
#include "force.h"
#include "group.h"
#include "lattice.h"
#include "modify.h"
#include "update.h"
#include "citeme.h"
#include "memory.h"
#include "error.h"
using namespace LAMMPS_NS;
using namespace FixConst;
#define INVOKED_SCALAR 1
#define INVOKED_VECTOR 2
#define MAXLINE 512
static const char cite_fix_phonon[] =
"fix phonon command:\n\n"
"@Article{Kong11,\n"
" author = {L. T. Kong},\n"
" title = {Phonon dispersion measured directly from molecular dynamics simulations},\n"
" journal = {Comp.~Phys.~Comm.},\n"
" year = 2011,\n"
" volume = 182,\n"
" pages = {2201--2207}\n"
"}\n\n";
/* ---------------------------------------------------------------------- */
FixPhonon::FixPhonon(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg)
{
if (lmp->citeme) lmp->citeme->add(cite_fix_phonon);
MPI_Comm_rank(world,&me);
MPI_Comm_size(world,&nprocs);
if (narg < 8) error->all(FLERR,"Illegal fix phonon command: number of arguments < 8");
nevery = force->inumeric(FLERR, arg[3]); // Calculate this fix every n steps!
if (nevery < 1) error->all(FLERR,"Illegal fix phonon command");
nfreq = force->inumeric(FLERR, arg[4]); // frequency to output result
if (nfreq < 1) error->all(FLERR,"Illegal fix phonon command");
waitsteps = force->bnumeric(FLERR,arg[5]); // Wait this many timesteps before actually measuring
if (waitsteps < 0) error->all(FLERR,"Illegal fix phonon command: waitsteps < 0 !");
int n = strlen(arg[6]) + 1; // map file
mapfile = new char[n];
strcpy(mapfile, arg[6]);
n = strlen(arg[7]) + 1; // prefix of output
prefix = new char[n];
strcpy(prefix, arg[7]);
logfile = new char[n+4];
sprintf(logfile,"%s.log",prefix);
int sdim = sysdim = domain->dimension;
int iarg = 8;
nasr = 20;
// other command line options
while (iarg < narg){
if (strcmp(arg[iarg],"sysdim") == 0){
if (++iarg >= narg) error->all(FLERR,"Illegal fix phonon command: incomplete command line options.");
sdim = force->inumeric(FLERR, arg[iarg]);
if (sdim < 1) error->all(FLERR,"Illegal fix phonon command: sysdim should not be less than 1.");
} else if (strcmp(arg[iarg],"nasr") == 0){
if (++iarg >= narg) error->all(FLERR,"Illegal fix phonon command: incomplete command line options.");
nasr = force->inumeric(FLERR, arg[iarg]);
} else {
error->all(FLERR,"Illegal fix phonon command: unknown option read!");
}
++iarg;
}
// get the dimension of the simulation; 1D is possible by specifying the option of "sysdim 1"
if (sdim < sysdim) sysdim = sdim;
nasr = MAX(0, nasr);
// get the total number of atoms in group and run min/max checks
bigint ng = group->count(igroup);
if (ng > MAXSMALLINT) error->all(FLERR,"Too many atoms for fix phonon");
if (ng < 1) error->all(FLERR,"No atom found for fix phonon!");
ngroup = static_cast<int>(ng);
// MPI gatherv related variables
recvcnts = new int[nprocs];
displs = new int[nprocs];
// mapping index
tag2surf.clear(); // clear map info
surf2tag.clear();
// get the mapping between lattice indices and atom IDs
readmap(); delete []mapfile;
if (nucell == 1) nasr = MIN(1,nasr);
// get the mass matrix for dynamic matrix
getmass();
// create FFT and allocate memory for FFT
// here the parallization is done on the x direction only
nxlo = 0;
int *nx_loc = new int [nprocs];
for (int i = 0; i < nprocs; ++i){
nx_loc[i] = nx / nprocs;
if (i < nx%nprocs) ++nx_loc[i];
}
for (int i = 0; i < me; ++i) nxlo += nx_loc[i];
nxhi = nxlo + nx_loc[me] - 1;
mynpt = nx_loc[me] * ny * nz;
mynq = mynpt;
fft_dim = nucell * sysdim;
fft_dim2 = fft_dim * fft_dim;
fft_nsend = mynpt * fft_dim;
fft_cnts = new int[nprocs];
fft_disp = new int[nprocs];
fft_disp[0] = 0;
for (int i = 0; i < nprocs; ++i) fft_cnts[i] = nx_loc[i] * ny * nz * fft_dim;
for (int i = 1; i < nprocs; ++i) fft_disp[i] = fft_disp[i-1] + fft_cnts[i-1];
delete []nx_loc;
fft = new FFT3d(lmp,world,nz,ny,nx,0,nz-1,0,ny-1,nxlo,nxhi,0,nz-1,0,ny-1,nxlo,nxhi,0,0,&mysize,0);
memory->create(fft_data, MAX(1,mynq)*2, "fix_phonon:fft_data");
// allocate variables; MAX(1,... is used because NULL buffer will result in error for MPI
memory->create(RIloc,ngroup,(sysdim+1),"fix_phonon:RIloc");
memory->create(RIall,ngroup,(sysdim+1),"fix_phonon:RIall");
memory->create(Rsort,ngroup, sysdim, "fix_phonon:Rsort");
memory->create(Rnow, MAX(1,mynpt),fft_dim,"fix_phonon:Rnow");
memory->create(Rsum, MAX(1,mynpt),fft_dim,"fix_phonon:Rsum");
memory->create(basis,nucell, sysdim, "fix_phonon:basis");
// because of hermit, only nearly half of q points are stored
memory->create(Rqnow,MAX(1,mynq),fft_dim, "fix_phonon:Rqnow");
memory->create(Rqsum,MAX(1,mynq),fft_dim2,"fix_phonon:Rqsum");
memory->create(Phi_q,MAX(1,mynq),fft_dim2,"fix_phonon:Phi_q");
// variable to collect all local Phi to root
if (me == 0) memory->create(Phi_all,ntotal,fft_dim2,"fix_phonon:Phi_all");
else memory->create(Phi_all,1,1,"fix_phonon:Phi_all");
// output some information on the system to log file
if (me == 0){
flog = fopen(logfile, "w");
if (flog == NULL) {
char str[MAXLINE];
sprintf(str,"Can not open output file %s",logfile);
error->one(FLERR,str);
}
fprintf(flog,"############################################################\n");
fprintf(flog,"# group name of the atoms under study : %s\n", group->names[igroup]);
fprintf(flog,"# total number of atoms in the group : %d\n", ngroup);
fprintf(flog,"# dimension of the system : %d D\n", sysdim);
fprintf(flog,"# number of atoms per unit cell : %d\n", nucell);
fprintf(flog,"# dimension of the FFT mesh : %d x %d x %d\n", nx, ny, nz);
fprintf(flog,"# number of wait steps before measurement : " BIGINT_FORMAT "\n", waitsteps);
fprintf(flog,"# frequency of the measurement : %d\n", nevery);
fprintf(flog,"# output result after this many measurement: %d\n", nfreq);
fprintf(flog,"# number of processors used by this run : %d\n", nprocs);
fprintf(flog,"############################################################\n");
fprintf(flog,"# mapping information between lattice indices and atom id\n");
fprintf(flog,"# nx ny nz nucell\n");
fprintf(flog,"%d %d %d %d\n", nx, ny, nz, nucell);
fprintf(flog,"# l1 l2 l3 k atom_id\n");
int ix, iy, iz, iu;
for (idx = 0; idx < ngroup; ++idx){
itag = surf2tag[idx];
iu = idx%nucell;
iz = (idx/nucell)%nz;
iy = (idx/(nucell*nz))%ny;
ix = (idx/(nucell*nz*ny))%nx;
fprintf(flog,"%d %d %d %d " TAGINT_FORMAT "\n", ix, iy, iz, iu, itag);
}
fprintf(flog,"############################################################\n");
fflush(flog);
}
surf2tag.clear();
// default temperature is from thermo
TempSum = new double[sysdim];
id_temp = new char[12];
strcpy(id_temp,"thermo_temp");
int icompute = modify->find_compute(id_temp);
temperature = modify->compute[icompute];
inv_nTemp = 1./group->count(temperature->igroup);
} // end of constructor
/* ---------------------------------------------------------------------- */
void FixPhonon::post_run()
{
// compute and output final results
if (ifreq > 0 && ifreq != nfreq) postprocess();
if (me == 0) fclose(flog);
}
/* ---------------------------------------------------------------------- */
FixPhonon::~FixPhonon()
{
// delete locally stored array
memory->destroy(RIloc);
memory->destroy(RIall);
memory->destroy(Rsort);
memory->destroy(Rnow);
memory->destroy(Rsum);
memory->destroy(basis);
memory->destroy(Rqnow);
memory->destroy(Rqsum);
memory->destroy(Phi_q);
memory->destroy(Phi_all);
delete []recvcnts;
delete []displs;
delete []prefix;
delete []logfile;
delete []fft_cnts;
delete []fft_disp;
delete []id_temp;
delete []TempSum;
delete []M_inv_sqrt;
delete []basetype;
// destroy FFT
delete fft;
memory->sfree(fft_data);
// clear map info
tag2surf.clear();
surf2tag.clear();
}
/* ---------------------------------------------------------------------- */
int FixPhonon::setmask()
{
int mask = 0;
mask |= END_OF_STEP;
return mask;
}
/* ---------------------------------------------------------------------- */
void FixPhonon::init()
{
// warn if more than one fix-phonon
int count = 0;
for (int i = 0; i < modify->nfix; ++i) if (strcmp(modify->fix[i]->style,"phonon") == 0) ++count;
if (count > 1 && me == 0) error->warning(FLERR,"More than one fix phonon defined"); // just warn, but allowed.
}
/* ---------------------------------------------------------------------- */
void FixPhonon::setup(int flag)
{
// initialize accumulating variables
for (int i = 0; i < sysdim; ++i) TempSum[i] = 0.;
for (int i = 0; i < mynpt; ++i)
for (int j = 0; j < fft_dim; ++j) Rsum[i][j] = 0.;
for (int i =0; i < mynq; ++i)
for (int j =0; j < fft_dim2; ++j) Rqsum[i][j] = std::complex<double> (0.,0.);
for (int i = 0; i < 6; ++i) hsum[i] = 0.;
for (int i = 0; i < nucell; ++i)
for (int j = 0; j < sysdim; ++j) basis[i][j] = 0.;
neval = ifreq = 0;
prev_nstep = update->ntimestep;
}
/* ---------------------------------------------------------------------- */
void FixPhonon::end_of_step()
{
if ( (update->ntimestep-prev_nstep) <= waitsteps) return;
double **x = atom->x;
int *mask = atom->mask;
tagint *tag = atom->tag;
imageint *image = atom->image;
int nlocal = atom->nlocal;
double *h = domain->h;
int i,idim,jdim,ndim;
double xcur[3];
// to get the current temperature
if (!(temperature->invoked_flag & INVOKED_VECTOR)) temperature->compute_vector();
for (idim = 0; idim < sysdim; ++idim) TempSum[idim] += temperature->vector[idim];
// evaluate R(r) on local proc
nfind = 0;
for (i = 0; i < nlocal; ++i){
if (mask[i] & groupbit){
itag = tag[i];
idx = tag2surf[itag];
domain->unmap(x[i], image[i], xcur);
for (idim = 0; idim < sysdim; ++idim) RIloc[nfind][idim] = xcur[idim];
RIloc[nfind++][sysdim] = static_cast<double>(idx);
}
}
// gather R(r) on local proc, then sort and redistribute to all procs for FFT
nfind *= (sysdim+1);
displs[0] = 0;
for (i = 0; i < nprocs; ++i) recvcnts[i] = 0;
MPI_Gather(&nfind,1,MPI_INT,recvcnts,1,MPI_INT,0,world);
for (i = 1; i < nprocs; ++i) displs[i] = displs[i-1] + recvcnts[i-1];
MPI_Gatherv(RIloc[0],nfind,MPI_DOUBLE,RIall[0],recvcnts,displs,MPI_DOUBLE,0,world);
if (me == 0){
for (i = 0; i < ngroup; ++i){
idx = static_cast<int>(RIall[i][sysdim]);
for (idim = 0; idim < sysdim; ++idim) Rsort[idx][idim] = RIall[i][idim];
}
}
MPI_Scatterv(Rsort[0],fft_cnts,fft_disp, MPI_DOUBLE, Rnow[0], fft_nsend, MPI_DOUBLE,0,world);
// get Rsum
for (idx = 0; idx < mynpt; ++idx)
for (idim = 0; idim < fft_dim; ++idim) Rsum[idx][idim] += Rnow[idx][idim];
// FFT R(r) to get R(q)
for (idim = 0; idim < fft_dim; ++idim){
int m = 0;
for (idx = 0; idx < mynpt; ++idx){
fft_data[m++] = static_cast<FFT_SCALAR>(Rnow[idx][idim]);
fft_data[m++] = static_cast<FFT_SCALAR>(0.);
}
fft->compute(fft_data, fft_data, -1);
m = 0;
for (idq = 0; idq < mynq; ++idq){
Rqnow[idq][idim] = std::complex<double>(static_cast<double>(fft_data[m]), static_cast<double>(fft_data[m+1]));
m += 2;
}
}
// to get sum(R(q).R(q)*)
for (idq = 0; idq < mynq; ++idq){
ndim = 0;
for (idim = 0; idim < fft_dim; ++idim)
for (jdim = 0; jdim < fft_dim; ++jdim) Rqsum[idq][ndim++] += Rqnow[idq][idim] * std::conj(Rqnow[idq][jdim]);
}
// get basis info
if (fft_dim > sysdim){
double dist2orig[3];
for (idx = 0; idx < mynpt; ++idx){
ndim = sysdim;
for (i = 1; i < nucell; ++i){
for (idim = 0; idim < sysdim; ++idim) dist2orig[idim] = Rnow[idx][ndim++] - Rnow[idx][idim];
domain->minimum_image(dist2orig);
for (idim = 0; idim < sysdim; ++idim) basis[i][idim] += dist2orig[idim];
}
}
}
// get lattice vector info
for (int i = 0; i < 6; ++i) hsum[i] += h[i];
// increment counter
++neval;
// compute and output Phi_q after every nfreq evaluations
if (++ifreq == nfreq) postprocess();
} // end of end_of_step()
/* ---------------------------------------------------------------------- */
double FixPhonon::memory_usage()
{
double bytes = sizeof(double)*2*mynq
+ sizeof(std::map<int,int>)*2*ngroup
+ sizeof(double)*(ngroup*(3*sysdim+2)+mynpt*fft_dim*2)
+ sizeof(std::complex<double>)*MAX(1,mynq)*fft_dim *(1+2*fft_dim)
+ sizeof(std::complex<double>)*ntotal*fft_dim2
+ sizeof(int) * nprocs * 4;
return bytes;
}
/* ---------------------------------------------------------------------- */
int FixPhonon::modify_param(int narg, char **arg)
{
if (strcmp(arg[0],"temp") == 0) {
if (narg < 2) error->all(FLERR,"Illegal fix_modify command");
delete [] id_temp;
int n = strlen(arg[1]) + 1;
id_temp = new char[n];
strcpy(id_temp,arg[1]);
int icompute = modify->find_compute(id_temp);
if (icompute < 0) error->all(FLERR,"Could not find fix_modify temp ID");
temperature = modify->compute[icompute];
if (temperature->tempflag == 0)
error->all(FLERR,"Fix_modify temp ID does not compute temperature");
inv_nTemp = 1.0/group->count(temperature->igroup);
return 2;
}
return 0;
}
/* ----------------------------------------------------------------------
* private method, to get the mass matrix for dynamic matrix
* --------------------------------------------------------------------*/
void FixPhonon::getmass()
{
int nlocal = atom->nlocal;
int *mask = atom->mask;
tagint *tag = atom->tag;
int *type = atom->type;
double *rmass = atom->rmass;
double *mass = atom->mass;
double *mass_one, *mass_all;
double *type_one, *type_all;
mass_one = new double[nucell];
mass_all = new double[nucell];
type_one = new double[nucell];
type_all = new double[nucell];
for (int i = 0; i < nucell; ++i) mass_one[i] = type_one[i] = 0.;
if (rmass){
for (int i = 0; i < nlocal; ++i){
if (mask[i] & groupbit){
itag = tag[i];
idx = tag2surf[itag];
int iu = idx%nucell;
mass_one[iu] += rmass[i];
type_one[iu] += double(type[i]);
}
}
} else {
for (int i = 0; i < nlocal; ++i){
if (mask[i] & groupbit){
itag = tag[i];
idx = tag2surf[itag];
int iu = idx%nucell;
mass_one[iu] += mass[type[i]];
type_one[iu] += double(type[i]);
}
}
}
MPI_Allreduce(mass_one,mass_all,nucell,MPI_DOUBLE,MPI_SUM,world);
MPI_Allreduce(type_one,type_all,nucell,MPI_DOUBLE,MPI_SUM,world);
M_inv_sqrt = new double[nucell];
basetype = new int[nucell];
double inv_total = 1./double(ntotal);
for (int i = 0; i < nucell; ++i){
mass_all[i] *= inv_total;
M_inv_sqrt[i] = sqrt(1./mass_all[i]);
basetype[i] = int(type_all[i]*inv_total);
}
delete []mass_one;
delete []mass_all;
delete []type_one;
delete []type_all;
}
/* ----------------------------------------------------------------------
* private method, to read the mapping info from file
* --------------------------------------------------------------------*/
void FixPhonon::readmap()
{
int info = 0;
// auto-generate mapfile for "cluster" (gamma only system)
if (strcmp(mapfile, "GAMMA") == 0){
nx = ny = nz = ntotal = 1;
nucell = ngroup;
tagint *tag_loc, *tag_all;
memory->create(tag_loc,ngroup,"fix_phonon:tag_loc");
memory->create(tag_all,ngroup,"fix_phonon:tag_all");
// get atom IDs on local proc
int nfind = 0;
for (int i = 0; i < atom->nlocal; ++i){
if (atom->mask[i] & groupbit) tag_loc[nfind++] = atom->tag[i];
}
// gather IDs on local proc
displs[0] = 0;
for (int i = 0; i < nprocs; ++i) recvcnts[i] = 0;
MPI_Allgather(&nfind,1,MPI_INT,recvcnts,1,MPI_INT,world);
for (int i = 1; i < nprocs; ++i) displs[i] = displs[i-1] + recvcnts[i-1];
MPI_Allgatherv(tag_loc,nfind,MPI_LMP_TAGINT,tag_all,recvcnts,displs,MPI_LMP_TAGINT,world);
for (int i = 0; i < ngroup; ++i){
itag = tag_all[i];
tag2surf[itag] = i;
surf2tag[i] = itag;
}
memory->destroy(tag_loc);
memory->destroy(tag_all);
return;
}
// read from map file for others
char line[MAXLINE];
FILE *fp = fopen(mapfile, "r");
if (fp == NULL){
sprintf(line,"Cannot open input map file %s", mapfile);
error->all(FLERR,line);
}
if (fgets(line,MAXLINE,fp) == NULL)
error->all(FLERR,"Error while reading header of mapping file!");
nx = force->inumeric(FLERR, strtok(line, " \n\t\r\f"));
ny = force->inumeric(FLERR, strtok(NULL, " \n\t\r\f"));
nz = force->inumeric(FLERR, strtok(NULL, " \n\t\r\f"));
nucell = force->inumeric(FLERR, strtok(NULL, " \n\t\r\f"));
ntotal = nx*ny*nz;
if (ntotal*nucell != ngroup)
error->all(FLERR,"FFT mesh and number of atoms in group mismatch!");
// second line of mapfile is comment
if (fgets(line,MAXLINE,fp) == NULL)
error->all(FLERR,"Error while reading comment of mapping file!");
int ix, iy, iz, iu;
// the remaining lines carry the mapping info
for (int i = 0; i < ngroup; ++i){
if (fgets(line,MAXLINE,fp) == NULL) {info = 1; break;}
ix = force->inumeric(FLERR, strtok(line, " \n\t\r\f"));
iy = force->inumeric(FLERR, strtok(NULL, " \n\t\r\f"));
iz = force->inumeric(FLERR, strtok(NULL, " \n\t\r\f"));
iu = force->inumeric(FLERR, strtok(NULL, " \n\t\r\f"));
itag = force->inumeric(FLERR, strtok(NULL, " \n\t\r\f"));
// check if index is in correct range
if (ix < 0 || ix >= nx || iy < 0 || iy >= ny ||
iz < 0 || iz >= nz || iu < 0 || iu >= nucell) {info = 2; break;}
// 1 <= itag <= natoms
if (itag < 1 || itag > static_cast<tagint>(atom->natoms)) {info = 3; break;}
idx = ((ix*ny+iy)*nz+iz)*nucell + iu;
tag2surf[itag] = idx;
surf2tag[idx] = itag;
}
fclose(fp);
if (tag2surf.size() != surf2tag.size() ||
tag2surf.size() != static_cast<std::size_t>(ngroup) )
error->all(FLERR,"The mapping is incomplete!");
if (info) error->all(FLERR,"Error while reading mapping file!");
// check the correctness of mapping
int *mask = atom->mask;
tagint *tag = atom->tag;
int nlocal = atom->nlocal;
for (int i = 0; i < nlocal; ++i) {
if (mask[i] & groupbit){
itag = tag[i];
idx = tag2surf[itag];
if (itag != surf2tag[idx])
error->one(FLERR,"The mapping info read is incorrect!");
}
}
}
/* ----------------------------------------------------------------------
* private method, to output the force constant matrix
* --------------------------------------------------------------------*/
void FixPhonon::postprocess( )
{
if (neval < 1) return;
ifreq = 0;
int idim, jdim, ndim;
double inv_neval = 1. /double(neval);
// to get <Rq.Rq*>
for (idq = 0; idq < mynq; ++idq)
for (idim = 0; idim < fft_dim2; ++idim) Phi_q[idq][idim] = Rqsum[idq][idim] * inv_neval;
// to get <R>
for (idx = 0; idx < mynpt; ++idx)
for (idim = 0; idim < fft_dim; ++idim) Rnow[idx][idim] = Rsum[idx][idim] * inv_neval;
// to get <R>q
for (idim = 0; idim < fft_dim; ++idim){
int m = 0;
for (idx = 0; idx < mynpt; ++idx){
fft_data[m++] = static_cast<FFT_SCALAR>(Rnow[idx][idim]);
fft_data[m++] = static_cast<FFT_SCALAR>(0.);
}
fft->compute(fft_data,fft_data,-1);
m = 0;
for (idq = 0; idq < mynq; ++idq){
Rqnow[idq][idim] = std::complex<double>(static_cast<double>(fft_data[m]), static_cast<double>(fft_data[m+1]));
m += 2;
}
}
// to get G(q) = <Rq.Rq*> - <R>q.<R*>q
for (idq = 0; idq < mynq; ++idq){
ndim = 0;
for (idim = 0; idim < fft_dim; ++idim)
for (jdim = 0; jdim < fft_dim; ++jdim) Phi_q[idq][ndim++] -= Rqnow[idq][idim] * std::conj(Rqnow[idq][jdim]);
}
// to get Phi = KT.G^-1; normalization of FFTW data is done here
double boltz = force->boltz, kbtsqrt[sysdim], TempAve = 0.;
double TempFac = inv_neval * inv_nTemp;
double NormFac = TempFac * double(ntotal);
for (idim = 0; idim < sysdim; ++idim){
kbtsqrt[idim] = sqrt(TempSum[idim] * NormFac);
TempAve += TempSum[idim] * TempFac;
}
TempAve /= sysdim*boltz;
for (idq = 0; idq < mynq; ++idq){
GaussJordan(fft_dim, Phi_q[idq]);
ndim =0;
for (idim = 0; idim < fft_dim; ++idim)
for (jdim = 0; jdim < fft_dim; ++jdim) Phi_q[idq][ndim++] *= kbtsqrt[idim%sysdim]*kbtsqrt[jdim%sysdim];
}
// to collect all local Phi_q to root
displs[0]=0;
for (int i = 0; i < nprocs; ++i) recvcnts[i] = fft_cnts[i]*fft_dim*2;
for (int i = 1; i < nprocs; ++i) displs[i] = displs[i-1] + recvcnts[i-1];
MPI_Gatherv(Phi_q[0],mynq*fft_dim2*2,MPI_DOUBLE,Phi_all[0],recvcnts,displs,MPI_DOUBLE,0,world);
// to collect all basis info and averaged it on root
double basis_root[fft_dim];
if (fft_dim > sysdim) MPI_Reduce(&basis[1][0], &basis_root[sysdim], fft_dim-sysdim, MPI_DOUBLE, MPI_SUM, 0, world);
if (me == 0){ // output dynamic matrix by root
// get basis info
for (idim = 0; idim < sysdim; ++idim) basis_root[idim] = 0.;
for (idim = sysdim; idim < fft_dim; ++idim) basis_root[idim] /= double(ntotal)*double(neval);
// get unit cell base vector info; might be incorrect if MD pbc and FixPhonon pbc mismatch.
double basevec[9];
basevec[1] = basevec[2] = basevec[5] = 0.;
basevec[0] = hsum[0] * inv_neval / double(nx);
basevec[4] = hsum[1] * inv_neval / double(ny);
basevec[8] = hsum[2] * inv_neval / double(nz);
basevec[7] = hsum[3] * inv_neval / double(nz);
basevec[6] = hsum[4] * inv_neval / double(nz);
basevec[3] = hsum[5] * inv_neval / double(ny);
// write binary file, in fact, it is the force constants matrix that is written
// Enforcement of ASR and the conversion of dynamical matrix is done in the postprocessing code
char fname[MAXLINE];
sprintf(fname,"%s.bin." BIGINT_FORMAT,prefix,update->ntimestep);
FILE *fp_bin = fopen(fname,"wb");
fwrite(&sysdim, sizeof(int), 1, fp_bin);
fwrite(&nx, sizeof(int), 1, fp_bin);
fwrite(&ny, sizeof(int), 1, fp_bin);
fwrite(&nz, sizeof(int), 1, fp_bin);
fwrite(&nucell, sizeof(int), 1, fp_bin);
fwrite(&boltz, sizeof(double), 1, fp_bin);
fwrite(Phi_all[0],sizeof(double),ntotal*fft_dim2*2,fp_bin);
fwrite(&TempAve, sizeof(double),1, fp_bin);
fwrite(&basevec[0], sizeof(double),9, fp_bin);
fwrite(&basis_root[0],sizeof(double),fft_dim,fp_bin);
fwrite(basetype, sizeof(int), nucell, fp_bin);
fwrite(M_inv_sqrt, sizeof(double),nucell, fp_bin);
fclose(fp_bin);
// write log file, here however, it is the dynamical matrix that is written
fprintf(flog,"############################################################\n");
fprintf(flog,"# Current time step : " BIGINT_FORMAT "\n", update->ntimestep);
fprintf(flog,"# Total number of measurements : %d\n", neval);
fprintf(flog,"# Average temperature of the measurement : %lg\n", TempAve);
fprintf(flog,"# Boltzmann constant under current units : %lg\n", boltz);
fprintf(flog,"# basis vector A1 = [%lg %lg %lg]\n", basevec[0], basevec[1], basevec[2]);
fprintf(flog,"# basis vector A2 = [%lg %lg %lg]\n", basevec[3], basevec[4], basevec[5]);
fprintf(flog,"# basis vector A3 = [%lg %lg %lg]\n", basevec[6], basevec[7], basevec[8]);
fprintf(flog,"############################################################\n");
fprintf(flog,"# qx\t qy \t qz \t\t Phi(q)\n");
EnforceASR();
// to get D = 1/M x Phi
for (idq = 0; idq < ntotal; ++idq){
ndim =0;
for (idim = 0; idim < fft_dim; ++idim)
for (jdim = 0; jdim < fft_dim; ++jdim) Phi_all[idq][ndim++] *= M_inv_sqrt[idim/sysdim]*M_inv_sqrt[jdim/sysdim];
}
idq =0;
for (int ix = 0; ix < nx; ++ix){
double qx = double(ix)/double(nx);
for (int iy = 0; iy < ny; ++iy){
double qy = double(iy)/double(ny);
for (int iz = 0; iz < nz; ++iz){
double qz = double(iz)/double(nz);
fprintf(flog,"%lg %lg %lg", qx, qy, qz);
for (idim = 0; idim < fft_dim2; ++idim)
fprintf(flog, " %lg %lg", std::real(Phi_all[idq][idim]),
std::imag(Phi_all[idq][idim]));
fprintf(flog, "\n");
++idq;
}
}
}
fflush(flog);
}
} // end of postprocess
/* ----------------------------------------------------------------------
* private method, to get the inverse of a complex matrix by means of
* Gaussian-Jordan Elimination with full pivoting; square matrix required.
*
* Adapted from the Numerical Recipes in Fortran.
* --------------------------------------------------------------------*/
void FixPhonon::GaussJordan(int n, std::complex<double> *Mat)
{
int i,icol,irow,j,k,l,ll,idr,idc;
int *indxc,*indxr,*ipiv;
double big, nmjk;
std::complex<double> dum, pivinv;
indxc = new int[n];
indxr = new int[n];
ipiv = new int[n];
for (i = 0; i < n; ++i) ipiv[i] = 0;
for (i = 0; i < n; ++i){
big = 0.;
for (j = 0; j < n; ++j){
if (ipiv[j] != 1){
for (k = 0; k < n; ++k){
if (ipiv[k] == 0){
idr = j*n+k;
nmjk = norm(Mat[idr]);
if (nmjk >= big){
big = nmjk;
irow = j;
icol = k;
}
} else if (ipiv[k] > 1) error->one(FLERR,"Singular matrix in complex GaussJordan!");
}
}
}
ipiv[icol] += 1;
if (irow != icol){
for (l = 0; l < n; ++l){
idr = irow*n+l;
idc = icol*n+l;
dum = Mat[idr];
Mat[idr] = Mat[idc];
Mat[idc] = dum;
}
}
indxr[i] = irow;
indxc[i] = icol;
idr = icol*n+icol;
if (Mat[idr] == std::complex<double>(0.,0.)) error->one(FLERR,"Singular matrix in complex GaussJordan!");
pivinv = 1./ Mat[idr];
Mat[idr] = std::complex<double>(1.,0.);
idr = icol*n;
for (l = 0; l < n; ++l) Mat[idr+l] *= pivinv;
for (ll = 0; ll < n; ++ll){
if (ll != icol){
idc = ll*n + icol;
dum = Mat[idc];
Mat[idc] = 0.;
idc -= icol;
for (l = 0; l < n; ++l) Mat[idc+l] -= Mat[idr+l]*dum;
}
}
}
for (l = n-1; l >= 0; --l){
int rl = indxr[l];
int cl = indxc[l];
if (rl != cl){
for (k = 0; k < n; ++k){
idr = k*n + rl;
idc = k*n + cl;
dum = Mat[idr];
Mat[idr] = Mat[idc];
Mat[idc] = dum;
}
}
}
delete []indxr;
delete []indxc;
delete []ipiv;
}
/* ----------------------------------------------------------------------
* private method, to apply the acoustic sum rule on force constant matrix
* at gamma point. Should be executed on root only.
* --------------------------------------------------------------------*/
void FixPhonon::EnforceASR()
{
if (nasr < 1) return;
for (int iit = 0; iit < nasr; ++iit){
// simple ASR; the resultant matrix might not be symmetric
for (int a = 0; a < sysdim; ++a)
for (int b = 0; b < sysdim; ++b){
for (int k = 0; k < nucell; ++k){
double sum = 0.;
for (int kp = 0; kp < nucell; ++kp){
int idx = (k*sysdim+a)*fft_dim + kp*sysdim + b;
sum += std::real(Phi_all[0][idx]);
}
sum /= double(nucell);
for (int kp = 0; kp < nucell; ++kp){
int idx = (k*sysdim+a)*fft_dim + kp*sysdim + b;
Phi_all[0][idx] -= sum;
}
}
}
// symmetrize
for (int k = 0; k < nucell; ++k)
for (int kp = k; kp < nucell; ++kp){
double csum = 0.;
for (int a = 0; a < sysdim; ++a)
for (int b = 0; b < sysdim; ++b){
int idx = (k*sysdim+a)*fft_dim + kp*sysdim + b;
int jdx = (kp*sysdim+b)*fft_dim + k*sysdim + a;
csum = (std::real(Phi_all[0][idx])+std::real(Phi_all[0][jdx]))*0.5;
Phi_all[0][idx] = std::complex<double>(csum, std::imag(Phi_all[0][idx]));
Phi_all[0][jdx] = std::complex<double>(csum, std::imag(Phi_all[0][jdx]));
}
}
}
// symmetric ASR
for (int a = 0; a < sysdim; ++a)
for (int b = 0; b < sysdim; ++b){
for (int k = 0; k < nucell; ++k){
double sum = 0.;
for (int kp = 0; kp < nucell; ++kp){
int idx = (k*sysdim+a)*fft_dim + kp*sysdim + b;
sum += std::real(Phi_all[0][idx]);
}
sum /= double(nucell-k);
for (int kp = k; kp < nucell; ++kp){
int idx = (k*sysdim+a)*fft_dim + kp*sysdim + b;
int jdx = (kp*sysdim+b)*fft_dim + k*sysdim + a;
Phi_all[0][idx] -= sum;
Phi_all[0][jdx] = std::complex<double>(std::real(Phi_all[0][idx]),
std::imag(Phi_all[0][jdx]));
}
}
}
}
/* --------------------------------------------------------------------*/
Event Timeline
Log In to Comment