// EMT.cpp -- Implements the EMT potential for multiple elements.
// Copyright (C) 2008-2011 Jakob Schiotz and Center for Individual
// Nanoparticle Functionality, Department of Physics, Technical
// University of Denmark. Email: schiotz@fysik.dtu.dk
//
// This file is part of Asap version 3.
// Asap is released under the GNU Lesser Public License (LGPL) version 3.
// However, the parts of Asap distributed within the OpenKIM project
// (including this file) are also released under the Common Development
// and Distribution License (CDDL) version 1.0.
//
// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// version 3 as published by the Free Software Foundation. Permission
// to use other versions of the GNU Lesser General Public License may
// granted by Jakob Schiotz or the head of department of the
// Department of Physics, Technical University of Denmark, as
// described in section 14 of the GNU General Public License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// and the GNU Lesser Public License along with this program. If not,
// see .
#include "EMT.h"
#include "Asap.h"
#include "EMTParameterProvider.h"
#include "Atoms.h"
#include "Vec.h"
#include "NeighborCellLocator.h"
#include "NeighborList.h"
#include "Exception.h"
#include "Timing.h"
#include "mass.h"
#include "Debug.h"
#include
#include
#include
#include
#include
#include
using std::vector;
using std::set;
using std::cerr;
using std::endl;
using std::flush;
using std::sort;
#undef INTERLEAVETHREADS
// Choose the neighbor list implementation.
// 2: NeighborCellLocator: Simple, but cost almost a factor 2 in performance.
// 3: NeighborList: New 'standard' neighborlist implementation in Asap-3.
#define NBLIST 3
#if 1
#define VERB(x) if (verbose == 1) cerr << x
#else
#define VERB(x)
#endif
using namespace std;
// Standard mapping of the six independent parts of the stress tensor to
// vector notation
const static int stresscomp[3][3] = {{0, 5, 4}, {5, 1, 3}, {4, 3, 2}};
EMT::EMT(PyObject *prov)
{
CONSTRUCTOR;
// Extract the parameter provider from the Python object, and store
// the Python object.
if (prov != NULL)
{
provider = ((PyAsap_EMTParamProvObject *) prov)->cobj;
provider_obj = prov;
Py_INCREF(provider_obj);
}
else
{
provider_obj = NULL;
provider = NULL;
}
DEBUGPRINT;
nblist = NULL;
nblist_obj = NULL;
driftfactor = 0.05; // Drift factor for the neighbor list.
initialized = false;
atoms = NULL;
ghostatoms = false;
nAtoms = 0;
nSize = 0;
subtractE0 = true;
counters.ids = counters.nblist = counters.sigma1 = counters.sigma2 =
counters.energies = counters.forces = counters.stresses =
counters.fullstresses = counters.beforeforces = 0;
recalc.ids = recalc.nblist = recalc.sigma1 = recalc.sigma2 =
recalc.energies = recalc.forces = recalc.stresses =
recalc.fullstresses = recalc.beforeforces = false;
skip_begin = false;
stresses_are_normalized = false;
DEBUGPRINT;
}
/// If an EMTParameterProvider was given when constructing the EMT
/// object, it will NOT be deleted, but any default parameter provider
/// and the automatically generated neigbor list will be deleted.
/// XXXX: Handle this with Python refcounting.
EMT::~EMT()
{
DESTRUCTOR;
Py_XDECREF(provider_obj);
Py_XDECREF(nblist_obj);
if (atoms != NULL)
AsapAtoms_DECREF(atoms);
}
string EMT::GetRepresentation() const
{
char buffer[50];
sprintf(buffer, "0x%p", this);
return "GetName() +
") potential object at " + buffer + ">";
}
long EMT::PrintMemory() const
{
long atomsmem = 0;
if (atoms != NULL)
atomsmem = atoms->PrintMemory();
long mem = 0; // Count the big stuff.
for (vector >::const_iterator i = sigma1.begin();
i != sigma1.end(); i++)
mem += i->size() * sizeof(int);
for (vector >::const_iterator i = sigma2.begin();
i != sigma2.end(); i++)
mem += i->size() * sizeof(int);
mem += Ec.size() * sizeof(double);
mem += Eas.size() * sizeof(double);
mem += Epot.size() * sizeof(double);
mem += radius.size() * sizeof(double);
mem += dEds.size() * sizeof(double);
mem += force.size() * sizeof(Vec);
mem += stress.size() * sizeof(SymTensor);
mem += tmp_double.size() * sizeof(double);
mem += id.size() * sizeof(int);
mem = (mem + 512*1024) / (1024*1024);
char buffer[500];
snprintf(buffer, 500,
"*MEM* EMT %ld MB. [ sizeof(int)=%ld sizeof(double)=%ld ]",
mem, (long) sizeof(int), (long) sizeof(double));
cerr << buffer << endl;
if (nblist != NULL)
mem += nblist->PrintMemory();
return mem + atomsmem;
}
void EMT::SetAtoms(PyObject *pyatoms, Atoms* accessobj /* = NULL */)
{
DEBUGPRINT;
if (atoms != NULL)
{
// SetAtoms should only do anything the first time it is called.
// Subsequent calls should just check for accessobj being NULL.
if (accessobj != NULL)
throw AsapError("EMT::SetAtoms called multiple times with accessobj != NULL");
// SetAtoms should not do anything if called more than once!
return;
}
// The first time SetAtoms is being called some initialization is done.
if (accessobj != NULL)
{
atoms = accessobj;
AsapAtoms_INCREF(atoms);
}
else
atoms = new Atoms();
assert(atoms != NULL);
atoms->Begin(pyatoms);
nAtoms = atoms->GetNumberOfAtoms();
nSize = 0; // Will be set when the NB list is created. The value 0
// should create enough trouble to detect if it is used before then.
InitParameters();
initialized = true;
if (nelements == 1)
{
singleelement = parameters[0];
}
else
{
singleelement = 0;
}
atoms->End();
DEBUGPRINT;
}
void EMT::Allocate()
{
RETURNIFASAPERROR;
USETIMER("EMT::Allocate");
DEBUGPRINT;
VERB(" Allocate[" << nAtoms << "," << nSize << "]" << flush);
// WARNING: Resizing the vector may allocate way too much memory. It
// appears that calling reserve solves this problem. For efficiency,
// reserve is called with 5% extra space. This is only necessary if the
// atoms have ghosts, otherwise no reallocation will happen.
// First, check if reallocation is necessary.
if (nSize != Ec.size() || nAtoms != Eas.size())
{
DEBUGPRINT;
/* Resize/intialize the internal variables. */
sigma1.resize(nelements);
sigma2.resize(nelements);
// Do the reserve trick if the atoms have ghosts.
if (ghostatoms)
{
if (Ec.capacity() < nSize)
{
nSizeRes = nSize + nSize/20;
for (int i = 0; i < nelements; i++) {
sigma1[i].reserve(nSizeRes);
sigma2[i].reserve(nSizeRes);
}
Ec.reserve(nSizeRes);
dEds.reserve(nSizeRes);
radius.reserve(nSizeRes);
id.reserve(nSizeRes);
force.reserve(nSizeRes); // Will briefly be expanded to nSize
}
if (Eas.capacity() < nAtoms)
{
nAtomsRes = nAtoms + nAtoms/20;
Eas.reserve(nAtomsRes);
Epot.reserve(nAtomsRes);
}
}
for (int i = 0; i < nelements; i++) {
sigma1[i].resize(nSize);
sigma2[i].resize(nSize);
}
DEBUGPRINT;
Ec.resize(nSize);
Eas.resize(nAtoms);
Epot.resize(nAtoms);
radius.resize(nSize);
dEds.resize(nSize);
id.resize(nSize);
force.resize(nAtoms);
tmp_double.resize(nSize);
ex2.resize(nSize);
if (stress.size())
AllocateStress();
// The IDs are set to zero, then they do not have to be calculated if
// there is only one element.
if (nelements == 1)
for (int i = 0; i < nSize; i++)
id[i] = 0;
}
DEBUGPRINT;
}
// Stresses are reallocated if they exist when Allocate is called, or if
// they do not exist, and GetStress is called.
void EMT::AllocateStress()
{
DEBUGPRINT;
if (ghostatoms && (stress.capacity() < nAtoms))
stress.reserve(nAtomsRes);
stress.resize(nAtoms);
DEBUGPRINT;
}
void EMT::InitParameters()
{
DEBUGPRINT;
// Extract the elements from the list of atoms, and set up an array
// of EMT parameters.
set elements_set;
vector elements;
// Extract the elements from the list of atoms.
// elements.clear();
atoms->GetListOfElements(elements_set);
for (set::iterator i = elements_set.begin(); i != elements_set.end();
++i)
elements.push_back(*i);
nelements = elements.size();
assert(nelements == elements_set.size());
sort(elements.begin(), elements.end());
// Get the EMT parameters
parameters.clear();
for (vector::iterator i = elements.begin(); i != elements.end(); ++i)
parameters.push_back(provider->GetParameters(*i));
//assert(nelements == provider->GetNumberOfElements());
// Calculate the quantities that can only be calculated, once the
// elements in the simulations are known.
provider->CalcGammaEtc();
rFermi = provider->GetCutoffDistance();
//rNbCut = 1.04500185048 * rFermi;
rNbCut = provider->GetListCutoffDistance();
cutoffslope = provider->GetCutoffSlope();
chi = provider->GetChi();
if (verbose)
cerr << "EMT::InitParameters: rFermi = " << rFermi << " rNbCut = "
<< rNbCut << " cutoffslope = " << cutoffslope << endl;
DEBUGPRINT;
}
bool EMT::CalcReq_Forces(PyObject *pyatoms)
{
atoms->Begin(pyatoms);
bool required = (counters.forces != atoms->GetPositionsCounter());
atoms->End();
return required;
}
const vector &EMT::GetForces(PyObject *pyatoms)
{
USETIMER("EMT::GetForces")
DEBUGPRINT;
VERB(" Force[");
assert(atoms != NULL);
atoms->Begin(pyatoms);
recalc.nblist = CheckNeighborList();
recalc.forces = (counters.forces != atoms->GetPositionsCounter());
if (recalc.forces)
{
recalc.ids = (counters.ids != atoms->GetPositionsCounter());
recalc.sigma1 = (counters.sigma1 != atoms->GetPositionsCounter());
recalc.beforeforces = (counters.beforeforces !=
atoms->GetPositionsCounter());
DEBUGPRINT;
CalculateForces();
counters.beforeforces = atoms->GetPositionsCounter();
counters.forces = atoms->GetPositionsCounter();
VERB("]" << flush);
}
else
{
VERB("-]");
assert(recalc.nblist == false);
}
DEBUGPRINT;
atoms->End();
assert(force.size() == nAtoms);
MEMORY;
DEBUGPRINT;
return force;
}
void EMT::CalculateForces()
{
CHECKNOASAPERROR;
#ifdef _OPENMP
#pragma omp parallel
#endif // _OPENMP
{
if (recalc.nblist)
UpdateNeighborList();
CalculateIDs();
CalculateSigmas(false); /* Skip sigma2 */
CalculateEnergiesAfterSigmas(false); /* Skip actual energy calc. */
if (nelements > 1)
CalculateForcesAfterEnergies();
else
CalculateForcesAfterEnergiesSingle();
}
PROPAGATEASAPERROR;
}
bool EMT::CalcReq_Stress(PyObject *pyatoms)
{
atoms->Begin(pyatoms);
bool required = ( (counters.stresses != atoms->GetPositionsCounter())
|| (counters.fullstresses != atoms->GetPositionsCounter())
|| (counters.fullstresses_mom != atoms->GetMomentaCounter()) );
atoms->End();
return required;
}
const vector &EMT::GetStresses(PyObject *pyatoms, bool virialonly /*=false*/)
{
USETIMER("EMT::GetStresses")
DEBUGPRINT;
VERB(" Stresses[");
assert(atoms != NULL);
atoms->Begin(pyatoms);
recalc.nblist = CheckNeighborList();
recalc.stresses = (counters.stresses != atoms->GetPositionsCounter());
recalc.fullstresses = (counters.fullstresses != atoms->GetPositionsCounter());
// In addition, if momenta have changed, we have to recalculate all
// the stresses. This is a rare situation not necessarily handled
// optimally.
if (counters.fullstresses_mom != atoms->GetMomentaCounter())
{
recalc.fullstresses = true;
recalc.stresses = true;
recalc.forces = true;
}
if (recalc.stresses || recalc.fullstresses)
{
recalc.ids = (counters.ids != atoms->GetPositionsCounter());
recalc.sigma1 = (counters.sigma1 != atoms->GetPositionsCounter());
recalc.beforeforces = (counters.beforeforces !=
atoms->GetPositionsCounter());
recalc.forces = (counters.forces != atoms->GetPositionsCounter());
DEBUGPRINT;
if (this->stress.size() == 0)
AllocateStress();
CalculateStresses(virialonly);
}
else
{
assert(recalc.nblist == false);
}
VERB("]" << flush);
DEBUGPRINT;
unitcellvolume = atoms->GetVolume();
counters.stresses = atoms->GetPositionsCounter();
if (!virialonly)
{
counters.fullstresses = atoms->GetPositionsCounter();
counters.fullstresses_mom = atoms->GetMomentaCounter();
}
counters.beforeforces = atoms->GetPositionsCounter();
counters.forces = atoms->GetPositionsCounter();
atoms->End();
return stress;
}
void EMT::CalculateStresses(bool virialonly)
{
if (recalc.stresses)
{
CHECKNOASAPERROR;
#ifdef _OPENMP
#pragma omp parallel
#endif // _OPENMP
{
if (recalc.nblist)
UpdateNeighborList();
CalculateIDs();
CalculateSigmas(false); // Skip sigma2
CalculateEnergiesAfterSigmas(false); // Skip actual energy calc.
if (nelements > 1)
CalculateForcesAfterEnergies();
else
CalculateForcesAfterEnergiesSingle();
}
PROPAGATEASAPERROR;
}
if (recalc.fullstresses)
{
VERB("S");
DEBUGPRINT;
// Now calculate the dynamic part of the stress, and normalize with the
// volume.
assert(stresses_are_normalized == false);
DEBUGPRINT;
for (int i = 0; i < 6; i++)
unnormalizedstress[i] = 0.0;
DEBUGPRINT;
int *id = &(this->id)[0]; // This is stepped up in the loop.
DEBUGPRINT;
assert (this->stress.size() == nAtoms);
const Vec *momenta = atoms->GetMomenta();
const double *masses = atoms->GetMasses();
for (int i = 0; i < nAtoms; i++, id++)
{
const double fourpithird = 4.1887902048; // 4 * PI / 3
double s0 = parameters[*id]->seq;
double vol = fourpithird * s0 * s0 * s0;
double invvol = 1.0 / vol;
for (int alpha = 0; alpha < 3; alpha++)
for (int beta = alpha; beta < 3; beta++)
{
int j = stresscomp[alpha][beta];
if (momenta && !virialonly)
stress[i][j] -= momenta[i][alpha] * momenta[i][beta] / masses[i];
unnormalizedstress[j] += stress[i][j];
if (!virialonly)
stress[i][j] *= invvol;
}
}
if (!virialonly)
stresses_are_normalized = true;
}
else
{
VERB("-");
}
}
void EMT::GetStress(PyObject *pyatoms, double totalstress[6], bool virialonly /*=false*/)
{
USETIMER("EMT::GetStress");
double vol;
DEBUGPRINT;
VERB(" Stress[");
// Calculate the atomic stresses. Their sum is stored in unnormalizedstress.
(void) GetStresses(pyatoms, virialonly);
if (virialonly)
{
// Copy the virial to the return array.
for (int i = 0; i < 6; i++)
totalstress[i] = unnormalizedstress[i];
}
else
{
// Always use the volume of the supercell to normalize the total stress.
// The alternative, using the sum of atomic volumes when the supercell
// volume is ill-defined due to open boundary conditions, will fail for
// parallel simulations.
vol = unitcellvolume;
// Normalize the total stress with the volume.
for (int i = 0; i < 6; i++)
totalstress[i] = unnormalizedstress[i] / vol;
}
DEBUGPRINT;
VERB("]" << flush);
}
bool EMT::CalcReq_Energy(PyObject *pyatoms)
{
atoms->Begin(pyatoms);
bool required = (counters.energies != atoms->GetPositionsCounter());
atoms->End();
return required;
}
const vector &EMT::GetPotentialEnergies(PyObject *pyatoms)
{
USETIMER("EMT::GetPotentialEnergies");
DEBUGPRINT;
VERB(" Energies[");
assert(atoms != NULL);
// Atoms may already be open if called from MonteCarloEMT object.
if (!skip_begin)
atoms->Begin(pyatoms);
else
skip_begin = false;
recalc.nblist = CheckNeighborList();
recalc.energies = (counters.energies != atoms->GetPositionsCounter());
if (recalc.energies)
{
recalc.ids = (counters.ids != atoms->GetPositionsCounter());
recalc.sigma1 = (counters.sigma1 != atoms->GetPositionsCounter());
recalc.sigma2 = (counters.sigma2 != atoms->GetPositionsCounter());
recalc.beforeforces = (counters.beforeforces !=
atoms->GetPositionsCounter());
DEBUGPRINT;
CalculateEnergies();
counters.energies = atoms->GetPositionsCounter();
counters.beforeforces = atoms->GetPositionsCounter();
}
else
{
assert(counters.beforeforces == atoms->GetPositionsCounter());
assert(recalc.nblist == false);
if(subtractE0)
for (int i = 0; i < nAtoms; i++)
Epot[i] = Ec[i] + Eas[i] - parameters[id[i]]->e0;
else
for (int i = 0; i < nAtoms; i++)
Epot[i] = Ec[i] + Eas[i];
VERB("-");
}
assert(Epot.size() == nAtoms);
DEBUGPRINT;
VERB("]" << flush);
atoms->End();
return Epot;
}
void EMT::CalculateEnergies()
{
CHECKNOASAPERROR;
#ifdef _OPENMP
#pragma omp parallel
#endif // _OPENMP
{
if (recalc.nblist)
UpdateNeighborList();
CalculateIDs();
CalculateSigmas(true);
CalculateEnergiesAfterSigmas(true);
}
PROPAGATEASAPERROR;
}
double EMT::GetPotentialEnergy(PyObject *pyatoms)
{
USETIMER("EMT::GetPotentialEnergy");
VERB(" Energy[");
DEBUGPRINT;
double etot = 0.0;
const vector &e = GetPotentialEnergies(pyatoms);
for (int i = 0; i < nAtoms; i++)
etot += e[i];
DEBUGPRINT;
VERB("]" << flush);
return etot;
}
bool EMT::CheckNeighborList()
{
RETURNIFASAPERROR2(false);
USETIMER("EMT::CheckNeighborList");
DEBUGPRINT;
assert(atoms != NULL);
bool update = (nblist == NULL) || nblist->IsInvalid(); // Update if invalid
if (!update && (counters.nblist != atoms->GetPositionsCounter()))
{
VERB("n");
update = nblist->CheckNeighborList();
}
// May communicate
update = atoms->UpdateBeforeCalculation(update,
rNbCut * (1 + driftfactor));
counters.nblist = atoms->GetPositionsCounter();
return update;
}
void EMT::UpdateNeighborList()
{
RETURNIFASAPERROR;
USETIMER("EMT::UpdateNeighborList");
DEBUGPRINT;
VERB("N");
DEBUGPRINT;
RETURNIFASAPERROR;
if (nblist)
{
DEBUGPRINT;
nblist->UpdateNeighborList();
RETURNIFASAPERROR;
#ifdef _OPENMP
#pragma omp single
#endif // _OPENMP
{
if ((nAtoms != atoms->GetNumberOfAtoms())
|| (nSize - nAtoms != atoms->GetNumberOfGhostAtoms()))
{
nAtoms = atoms->GetNumberOfAtoms();
nSize = nAtoms + atoms->GetNumberOfGhostAtoms();
ghostatoms = atoms->HasGhostAtoms();
Allocate();
}
}
}
else
{
#ifdef _OPENMP
#pragma omp barrier
#endif // _OPENMP
// First call, create the neighbor list.
DEBUGPRINT;
CreateNeighborList();
RETURNIFASAPERROR;
#ifdef _OPENMP
#pragma omp single
#endif // _OPENMP
{
nAtoms = atoms->GetNumberOfAtoms();
nSize = nAtoms + atoms->GetNumberOfGhostAtoms();
ghostatoms = atoms->HasGhostAtoms();
Allocate();
}
}
DEBUGPRINT;
}
// Will be replaced in MonteCarloEMT
void EMT::CreateNeighborList()
{
RETURNIFASAPERROR;
MEMORY;
USETIMER("EMT::CreateNeighborList");
if (!initialized)
THROW_RETURN( AsapError("EMT object has not been initialized!") );
#ifdef _OPENMP
#pragma omp single
#endif // _OPENMP
{
#if NBLIST==2
PyAsap_NeighborLocatorObject *nbl;
nbl = PyAsap_NewNeighborCellLocator(atoms, rNbCut, driftfactor);
nblist = nbl->cobj;
nblist_obj = (PyObject *) nbl;
#elif NBLIST==3
PyAsap_NeighborLocatorObject *nbl;
nbl = PyAsap_NewNeighborList(atoms, rNbCut, driftfactor);
nblist = nbl->cobj;
nblist_obj = (PyObject *) nbl;
MEMORY;
#else
#error "NO NBLIST defined"
#endif //NBLIST
}
nblist->UpdateNeighborList();
MEMORY;
}
/// IDs are a recoding of the atomic numbers used as indices into
/// various arrays. Atomic numbers are not practical for this as they
/// are not contiguous.
void EMT::CalculateIDs()
{
RETURNIFASAPERROR;
USETIMER("EMT::CalculateIDs");
DEBUGPRINT;
if (!recalc.ids || nelements == 1)
return;
VERB("i");
DEBUGPRINT;
// If there is only one element, all IDs are 0 and do not have to be
// calculated. Otherwise the ID is the offset into the list of elements.
const asap_z_int *z = atoms->GetAtomicNumbers();
for (int i = 0; i < nelements; i++)
{
int zcand = parameters[i]->Z;
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int j = 0; j < nSize; j++)
if (z[j] == zcand)
id[j] = i;
}
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
counters.ids = atoms->GetPositionsCounter();
DEBUGPRINT;
}
void EMT::CalculateSigmas(bool calculatesigma2)
{
RETURNIFASAPERROR;
USETIMER("EMT::CalculateSigmas");
DEBUGPRINT;
if (!(recalc.sigma1 || (calculatesigma2 && recalc.sigma2)))
return;
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
if (calculatesigma2)
{
VERB("2");
}
else
{
VERB("1");
}
}
DEBUGPRINT;
// const Vec *pos = atoms->GetPositions();
int maxnblen = nblist->MaxNeighborListLength();
if (maxnblen > BUFLEN)
THROW_RETURN( AsapError("Neighborlist overrun (did you squeeze your atoms?). Longest neighbor list is ") << maxnblen );
// Buffer data:
TinyMatrix nbatch(nelements,nelements);
TinyMatrix self(nelements,nelements);
TinyMatrix other(nelements,nelements);
TinyMatrix rnb(nelements,nelements);
TinyMatrix sqdist(nelements,nelements);
vector other_buf(BUFLEN);
vector rnb_buf(BUFLEN);
vector sqdist_buf(BUFLEN);
DEBUGPRINT;
/* Set sigmas to zero */
for (int i = 0; i < nelements; i++)
{
#ifdef _OPENMP
#pragma omp for nowait
#endif // _OPENMP
for (int j = 0; j < nSize; j++)
{
sigma1[i][j] = 0.0;
sigma2[i][j] = 0.0;
}
}
#ifdef _OPENMP
#pragma omp barrier
#endif // _OPENMP
/* No atoms in batch pools */
for (int i = 0; i < nelements; i++)
for (int j = 0; j < nelements; j++)
nbatch[i][j] = 0;
// Loop over atoms
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int atom = 0; atom < nAtoms; atom++)
{
int zself = id[atom];
// Get neighbors and loop over them. Simplest if only one element
if (nelements == 1)
{
int nbat = nbatch[0][0]; // only element
int size = BUFLEN-nbat;
// WARNING: Assume we get all ghost neighbors to the real atoms!
int n = nblist->GetNeighbors(atom, other[0][0]+nbat,
rnb[0][0]+nbat, sqdist[0][0]+nbat,
size);
assert(size >= 0); // REMOVE LATER !!!
for (int i = nbat; i < nbat+n; i++)
self[0][0][i] = atom;
nbatch[0][0] += n;
}
else
{
int size = BUFLEN;
int n = nblist->GetNeighbors(atom, &other_buf[0], &rnb_buf[0], &sqdist_buf[0],
size);
assert(size >= 0); // REMOVE LATER !!!
for (int i = 0; i < n; i++)
{
int zother = id[other_buf[i]];
int nbat = nbatch[zself][zother]++; // Count this atom
self[zself][zother][nbat] = atom;
other[zself][zother][nbat] = other_buf[i];
rnb[zself][zother][nbat][0] = rnb_buf[i][0];
rnb[zself][zother][nbat][1] = rnb_buf[i][1];
rnb[zself][zother][nbat][2] = rnb_buf[i][2];
sqdist[zself][zother][nbat] = sqdist_buf[i];
}
}
// Now process any full batch
for (int zo = 0; zo < nelements; zo++)
if (nbatch[zself][zo] >= BUFLEN - maxnblen)
{
sigma_batch(self[zself][zo], other[zself][zo], rnb[zself][zo],
sqdist[zself][zo], zself, zo, nbatch[zself][zo],
calculatesigma2);
nbatch[zself][zo] = 0;
}
} // Loop over atoms
/* Process the remaining incomplete batches */
for (int zs = 0; zs < nelements; zs++)
for (int zo = 0; zo < nelements; zo++)
if (nbatch[zs][zo])
sigma_batch(self[zs][zo], other[zs][zo], rnb[zs][zo],
sqdist[zs][zo], zs, zo, nbatch[zs][zo],
calculatesigma2);
#ifdef _OPENMP
#pragma omp single
#endif // _OPENMP
{
sigma2isvalid = calculatesigma2; /* Remember if it was calculated. */
counters.sigma1 = atoms->GetPositionsCounter();
if (calculatesigma2)
counters.sigma2 = atoms->GetPositionsCounter();
}
}
void EMT::sigma_batch(int *self, int *other, Vec rnb[],
double *sq_dist, int zs, int zo, int n,
bool calculatesigma2, bool partialupdate /* = false */)
{
USETIMER("EMT::sigma_batch");
#ifdef SPLITLOOPS
double *temporary = new double[9*BUFLEN];
#else
double *temporary = new double[4*BUFLEN];
#endif
double *dsigma1s = &temporary[0];
double *dsigma2s = dsigma1s + BUFLEN;
double *ds1o = dsigma2s + BUFLEN;
double *ds2o = ds1o + BUFLEN;
#ifdef SPLITLOOPS
double *dist = ds2o + BUFLEN;
double *arg = dist + BUFLEN;
double *res = arg + BUFLEN;
double *res2 = res + BUFLEN;
double *res3 = res2 + BUFLEN;
assert(res3 - &temporary[0] == (9 - 1) * BUFLEN);
#else
assert(ds2o - &temporary[0] == (4 - 1) * BUFLEN);
#endif
double *dsigma1o = NULL;
double *dsigma2o = NULL;
double cutslopecutdist;
double other_eta2betaseq, self_eta2betaseq;
double other_kappaoverbeta, self_kappaoverbeta;
double other_kappaseq, self_kappaseq;
double *s1s, *s1o, *s2s, *s2o;
const emt_parameters *emtself, *emtother;
assert(n <= BUFLEN);
/* Get EMT parameters REWRITE !!! XXXX */
emtself = parameters[zs];
emtother = parameters[zo];
cutslopecutdist = cutoffslope * rFermi;
other_eta2betaseq = emtother->eta2 * Beta * emtother->seq;
self_eta2betaseq = emtself->eta2 * Beta * emtself->seq;
other_kappaoverbeta = emtother->kappa / Beta;
self_kappaoverbeta = emtself->kappa / Beta;
other_kappaseq = emtother->kappa * emtother->seq;
self_kappaseq = emtself->kappa * emtself->seq;
double other_eta2 = emtother->eta2;
double self_eta2 = emtself->eta2;
assert(n <= BUFLEN);
if ((zs == zo) && !calculatesigma2)
{
#ifdef SPLITLOOPS
for (int i = 0; i < n; i++)
dist[i] = sqrt(sq_dist[i]);
for (int i = 0; i < n; i++)
arg[i] = cutoffslope * dist[i] - cutslopecutdist;
for (int i = 0; i < n; i++)
res[i] = exp(arg[i]);
for (int i = 0; i < n; i++)
arg[i] = -other_eta2 * dist[i] + other_eta2betaseq;
for (int i = 0; i < n; i++)
res2[i] = exp(arg[i]);
for (int i = 0; i < n; i++)
{
double wght = 1.0 / (1.0 + res[i]);
dsigma1s[i] = wght * res2[i];
}
#else
for (int i = 0; i < n; i++)
{
/* dist = sqrt(sq_dist), distances between atoms */
double dist = sqrt(sq_dist[i]);
/* Calculate weight factor (cutoff function) */
double wght = 1.0 / (1.0 + exp(cutoffslope * dist
- cutslopecutdist));
/* Contribution to sigma1 */
dsigma1s[i] = wght * exp(-other_eta2 * dist + other_eta2betaseq);
}
#endif // SPLITLOOPS
dsigma1o = dsigma1s;
}
else if ((zs != zo) && !calculatesigma2)
{
for (int i = 0; i < n; i++)
{
/* dist = sqrt(sq_dist), distances between atoms */
double dist = sqrt(sq_dist[i]);
/* Calculate weight factor (cutoff function) */
double wght = 1.0 / (1.0 + exp(cutoffslope * dist
- cutslopecutdist));
/* Contributions to sigma1 */
dsigma1s[i] = wght * exp(-other_eta2 * dist + other_eta2betaseq);
ds1o[i] = wght * exp(-self_eta2 * dist + self_eta2betaseq);
}
dsigma1o = ds1o;
}
else if ((zs == zo) && calculatesigma2)
{
#ifdef SPLITLOOPS
for (int i = 0; i < n; i++)
dist[i] = sqrt(sq_dist[i]);
for (int i = 0; i < n; i++)
arg[i] = cutoffslope * dist[i] - cutslopecutdist;
for (int i = 0; i < n; i++)
res[i] = exp(arg[i]);
for (int i = 0; i < n; i++)
arg[i] = -other_eta2 * dist[i] + other_eta2betaseq;
for (int i = 0; i < n; i++)
res2[i] = exp(arg[i]);
for (int i = 0; i < n; i++)
arg[i] = -other_kappaoverbeta * dist[i] + other_kappaseq;
for (int i = 0; i < n; i++)
res3[i] = exp(arg[i]);
for (int i = 0; i < n; i++)
{
double wght = 1.0 / (1.0 + res[i]);
dsigma1s[i] = wght * res2[i];
dsigma2s[i] = wght * res3[i];
}
#else
for (int i = 0; i < n; i++)
{
/* dist = sqrt(sq_dist), distances between atoms */
double dist = sqrt(sq_dist[i]);
/* Calculate weight factor (cutoff function) */
double wght = 1.0 / (1.0 + exp(cutoffslope * dist
- cutslopecutdist));
/* Contribution to sigma1 */
dsigma1s[i] = wght * exp(-other_eta2 * dist + other_eta2betaseq);
dsigma2s[i] = wght * exp(-other_kappaoverbeta * dist
+ other_kappaseq);
}
#endif // SPLITLOOPS
dsigma1o = dsigma1s;
dsigma2o = dsigma2s;
}
else
{
for (int i = 0; i < n; i++)
{
/* dist = sqrt(sq_dist), distances between atoms */
double dist = sqrt(sq_dist[i]);
/* Calculate weight factor (cutoff function) */
double wght = 1.0 / (1.0 + exp(cutoffslope * dist
- cutslopecutdist));
/* Contributions to sigma1 */
dsigma1s[i] = wght * exp(-other_eta2 * dist + other_eta2betaseq);
ds1o[i] = wght * exp(-self_eta2 * dist + self_eta2betaseq);
dsigma2s[i] = wght * exp(-other_kappaoverbeta * dist
+ other_kappaseq);
ds2o[i] = wght * exp(-self_kappaoverbeta * dist + self_kappaseq);
}
dsigma1o = ds1o;
dsigma2o = ds2o;
}
// Distribute the results to the atoms.
if (!partialupdate)
{
// This branch is always taken by EMT. MonteCarloEMT may take the other.
if (calculatesigma2)
{
/* Distribute contributions to sigma1 and sigma2 */
s1o = &sigma1[zo][0];
s1s = &sigma1[zs][0];
s2o = &sigma2[zo][0];
s2s = &sigma2[zs][0];
#ifdef _OPENMP
#pragma omp critical
#endif // _OPENMP
{
for (int i = 0; i < n; i++)
{
int s = self[i];
int o = other[i];
s1o[s] += dsigma1s[i];
s2o[s] += dsigma2s[i];
if (o < nAtoms) // Dont add to ghost atoms
{
s1s[o] += dsigma1o[i];
s2s[o] += dsigma2o[i];
}
}
}
}
else
{
/* Distribute contributions to sigma1. */
s1o = &sigma1[zo][0];
s1s = &sigma1[zs][0];
#ifdef _OPENMP
#pragma omp critical
#endif // _OPENMP
{
for (int i = 0; i < n; i++)
{
int s = self[i];
int o = other[i];
s1o[s] += dsigma1s[i];
if (o < nAtoms)
s1s[o] += dsigma1o[i];
}
}
}
}
else // partialupdate
{
// This branch may be taken by MonteCarloEMT. Since it uses
// full neighbor lists for partial updates, only update the
// 'self' atom, not the 'other' atom.
s1o = &sigma1[zo][0];
s2o = &sigma2[zo][0];
#ifdef _OPENMP
#pragma omp critical
#endif // _OPENMP
{
for (int i = 0; i < n; i++)
{
int s = self[i];
s1o[s] += dsigma1s[i];
s2o[s] += dsigma2s[i];
}
}
}
delete[] temporary;
}
// Calculate the energies if Epot != 0, otherwise just calculate the
// derivatives needed for the forces.
void EMT::CalculateEnergiesAfterSigmas(bool calc_Epot)
{
RETURNIFASAPERROR;
USETIMER("EMT::CalculateEnergiesAfterSigmas");
DEBUGPRINT;
//vector sigma(nSize);
double *sigma = &tmp_double[0];
bool dosigmapart = (recalc.beforeforces || (calc_Epot && recalc.energies));
bool doepotpart = (calc_Epot && recalc.energies);
int zs, zo;
double s;
// Better performance if static ???
assert(nelements < NMAXELEMENTS);
double inv12gamma1[NMAXELEMENTS];
double neginvbetaeta2[NMAXELEMENTS];
double neglambda[NMAXELEMENTS];
double lambdaseq[NMAXELEMENTS];
double negkappa[NMAXELEMENTS];
double kappaseq[NMAXELEMENTS];
double nege0lambdalambda[NMAXELEMENTS];
double e0lambdalambdaseq[NMAXELEMENTS];
double neg6v0kappa[NMAXELEMENTS];
double invbetaeta2[NMAXELEMENTS];
double e0lambda[NMAXELEMENTS];
double eccnst[NMAXELEMENTS];
double sixv0[NMAXELEMENTS];
double neghalfv0overgamma2[NMAXELEMENTS];
double seq[NMAXELEMENTS];
int *id = &(this->id)[0];
if (dosigmapart)
{
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
VERB("b");
DEBUGPRINT;
}
/* Calculate total sigma1 */
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int i = 0; i < nAtoms; i++)
{
double s = 0.0;
int zs = id[i];
for (int zo = 0; zo < nelements; zo++)
s += (*chi)[zs][zo] * sigma1[zo][i];
if (s < 1.0e-9)
s = 1.0e-9;
sigma[i] = s;
}
if (ghostatoms)
{
// Only the master thread does MPI communications, the other wait.
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
atoms->CommunicateData(sigma);
#ifdef _OPENMP
#pragma omp barrier
#endif // _OPENMP
}
assert(nSize == radius.size() && nSize == Ec.size() &&
nSize == dEds.size());
}
/* Calculate conbinations of EMT parameters */
for (int i = 0; i < nelements; i++)
{
inv12gamma1[i] = 1.0 / (12.0 * parameters[i]->gamma1);
neginvbetaeta2[i] = -1.0 / (Beta * parameters[i]->eta2);
neglambda[i] = - parameters[i]->lambda;
lambdaseq[i] = parameters[i]->lambda * parameters[i]->seq;
negkappa[i] = - parameters[i]->kappa;
kappaseq[i] = parameters[i]->kappa * parameters[i]->seq;
nege0lambdalambda[i] = - parameters[i]->e0 * parameters[i]->lambda *
parameters[i]->lambda;
e0lambdalambdaseq[i] = parameters[i]->e0 * parameters[i]->lambda *
parameters[i]->lambda * parameters[i]->seq;
neg6v0kappa[i] = - 6.0 * parameters[i]->V0 * parameters[i]->kappa;
invbetaeta2[i] = 1.0 / (Beta * parameters[i]->eta2);
e0lambda[i] = parameters[i]->e0 * parameters[i]->lambda;
eccnst[i] = parameters[i]->e0 * (1.0 - parameters[i]->lambda *
parameters[i]->seq);
sixv0[i] = 6.0 * parameters[i]->V0;
neghalfv0overgamma2[i] = -0.5 * parameters[i]->V0 /
parameters[i]->gamma2;
seq[i] = parameters[i]->seq;
}
if (dosigmapart)
{
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int i = 0; i < nSize; i++)
{
asap_z_int z = id[i]; // Actually not Z (atomic no) but ID no.
double r = seq[z] + neginvbetaeta2[z] * log(sigma[i] * inv12gamma1[z]);
radius[i] = r;
double ex1 = exp(neglambda[z] * r + lambdaseq[z]);
double e2 = exp(negkappa[z] * r + kappaseq[z]);
ex2[i] = e2;
double tmp = (nege0lambdalambda[z] * r + e0lambdalambdaseq[z]) * ex1
+ neg6v0kappa[z] * e2;
dEds[i] = tmp * neginvbetaeta2[z] / sigma[i];
Ec[i] = (e0lambda[z] * r + eccnst[z]) * ex1;
}
}
if (calc_Epot)
{
DEBUGPRINT;
if (doepotpart)
{
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
VERB("e");
DEBUGPRINT;
}
/* We also need Eas, but only for the real atoms */
assert(sigma2isvalid);
assert(counters.sigma2 == atoms->GetPositionsCounter());
/* Calculate total sigma2 */
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int i = 0; i < nAtoms; i++)
{
s = 0.0;
zs = id[i];
for (zo = 0; zo < nelements; zo++)
s += (*chi)[zs][zo] * sigma2[zo][i];
Eas[i] = sixv0[zs] * ex2[i] + neghalfv0overgamma2[zs] * s;
}
}
/* Add the energies */
DEBUGPRINT;
if(subtractE0)
{
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int i = 0; i < nAtoms; i++)
Epot[i] = Ec[i] + Eas[i] - parameters[id[i]]->e0;
}
else
{
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int i = 0; i < nAtoms; i++)
Epot[i] = Ec[i] + Eas[i];
}
} // if (calc_Epot)
DEBUGPRINT;
}
void EMT::CalculateForcesAfterEnergies()
{
RETURNIFASAPERROR;
if (!(recalc.forces || (stress.size() && recalc.stresses)))
return;
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
VERB("f");
if (stress.size())
{
VERB("s");
}
force.resize(nSize);
}
#ifdef _OPENMP
#pragma omp barrier
#endif // _OPENMP
DEBUGPRINT;
int maxnblen = nblist->MaxNeighborListLength();
// Buffer data:
TinyMatrix nbatch(nelements,nelements);
TinyMatrixOfVector > self(nelements,nelements, BUFLEN);
TinyMatrixOfVector > other(nelements,nelements, BUFLEN);
TinyMatrixOfVector > rnb(nelements,nelements, BUFLEN);
TinyMatrixOfVector > sqdist(nelements,nelements, BUFLEN);
TinyMatrixOfVector > dEdss(nelements,nelements, BUFLEN);
TinyMatrixOfVector > dEdso(nelements,nelements, BUFLEN);
vector other_buf(BUFLEN);
vector rnb_buf(BUFLEN);
vector sqdist_buf(BUFLEN);
// If there is only one element, CalculateForcesAfterEnergiesSingle should
// be used.
assert(nelements > 1);
/* Set forces and stresses to zero */
if (stress.size())
{
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
stresses_are_normalized = false;
}
assert(stress.size() == nAtoms);
#ifdef _OPENMP
#pragma omp for nowait
#endif // _OPENMP
for (int i = 0; i < nAtoms; i++)
for (int j = 0; j < 6; j++)
stress[i][j] = 0.0;
}
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int i = 0; i < nAtoms; i++)
force[i][0] = force[i][1] = force[i][2] = 0.0;
/* Calculate forces */
/* No atoms in batch pools */
for (int i = 0; i < nelements; i++)
for (int j = 0; j < nelements; j++)
nbatch[i][j] = 0;
// Loop over atoms
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int atom = 0; atom < nAtoms; atom++) {
int zself = id[atom];
// Get neighbors and loop over them.
int size = BUFLEN;
int n = nblist->GetNeighbors(atom, &other_buf[0], &rnb_buf[0], &sqdist_buf[0],
size);
assert(size >= 0); // REMOVE LATER
for (int i = 0; i < n; i++) {
int zother = id[other_buf[i]];
int nbat = nbatch[zself][zother]++; // Count this atom
self[zself][zother][nbat] = atom;
other[zself][zother][nbat] = other_buf[i];
rnb[zself][zother][nbat][0] = rnb_buf[i][0];
rnb[zself][zother][nbat][1] = rnb_buf[i][1];
rnb[zself][zother][nbat][2] = rnb_buf[i][2];
sqdist[zself][zother][nbat] = sqdist_buf[i];
dEdss[zself][zother][nbat] = dEds[atom];
dEdso[zself][zother][nbat] = dEds[other_buf[i]];
}
// Now process any full batch
for (int zo = 0; zo < nelements; zo++)
if (nbatch[zself][zo] >= BUFLEN - maxnblen) {
force_batch(&self[zself][zo][0], &other[zself][zo][0],
&rnb[zself][zo][0],
&sqdist[zself][zo][0], &dEdss[zself][zo][0],
&dEdso[zself][zo][0], zself, zo, nbatch[zself][zo]);
nbatch[zself][zo] = 0;
}
} // Loop over atoms
/* Process the remaining incomplete batches */
for (int zs = 0; zs < nelements; zs++)
for (int zo = 0; zo < nelements; zo++)
if (nbatch[zs][zo])
force_batch(&self[zs][zo][0], &other[zs][zo][0], &rnb[zs][zo][0],
&sqdist[zs][zo][0], &dEdss[zs][zo][0],
&dEdso[zs][zo][0], zs, zo, nbatch[zs][zo]);
DEBUGPRINT;
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
force.resize(nAtoms);
}
#ifdef _OPENMP
#pragma omp barrier
#endif // _OPENMP
}
void EMT::CalculateForcesAfterEnergiesSingle()
{
RETURNIFASAPERROR;
USETIMER("EMT::CalculateForcesAfterEnergiesSingle");
if (!(recalc.forces || (stress.size() && recalc.stresses)))
return;
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
VERB("f");
if (stress.size())
{
VERB("s");
}
force.resize(nSize);
}
#ifdef _OPENMP
#pragma omp barrier
#endif // _OPENMP
int maxnblen = nblist->MaxNeighborListLength();
DEBUGPRINT;
// Buffer data:
int nbatch;
vector self(BUFLEN);
vector other(BUFLEN);
vector rnb(BUFLEN);
vector sqdist(BUFLEN);
vector dEdss(BUFLEN);
vector dEdso(BUFLEN);
DEBUGPRINT;
// If there is more than one element, CalculateForcesAfterEnergies should
// be used.
assert(nelements == 1);
/* Set forces and stresses to zero */
assert(force.size() >= nAtoms);
if (stress.size())
{
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
stresses_are_normalized = false;
assert(stress.size() == nAtoms);
}
#ifdef _OPENMP
#pragma omp for nowait
#endif // _OPENMP
for (int i = 0; i < nAtoms; i++)
for (int j = 0; j < 6; j++)
stress[i][j] = 0.0;
}
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int i = 0; i < nAtoms; i++)
force[i][0] = force[i][1] = force[i][2] = 0.0;
/* Calculate forces */
/* No atoms in batch pool */
DEBUGPRINT;
nbatch = 0;
// Loop over atoms
DEBUGPRINT;
#ifdef _OPENMP
#pragma omp for
#endif // _OPENMP
for (int atom = 0; atom < nAtoms; atom++) {
// Get neighbors and loop over them.
int size = BUFLEN-nbatch;
int n = nblist->GetNeighbors(atom, &other[nbatch], &rnb[nbatch],
&sqdist[nbatch], size);
double dEdsatom = dEds[atom];
for (int i = nbatch; i < nbatch+n; i++) {
self[i] = atom;
dEdss[i] = dEdsatom;
dEdso[i] = dEds[other[i]];
}
nbatch += n;
// Now process any full batch
if (nbatch >= BUFLEN - maxnblen) {
force_batch(&self[0], &other[0], &rnb[0], &sqdist[0], &dEdss[0],
&dEdso[0], 0, 0, nbatch);
nbatch = 0;
}
} // Loop over atoms
/* Process the remaining incomplete batch */
DEBUGPRINT;
if (nbatch)
force_batch(&self[0], &other[0], &rnb[0], &sqdist[0], &dEdss[0],
&dEdso[0], 0, 0, nbatch);
DEBUGPRINT;
#ifdef _OPENMP
#pragma omp master
#endif // _OPENMP
{
force.resize(nAtoms);
}
#ifdef _OPENMP
#pragma omp barrier
#endif // _OPENMP
}
// It would be natural to transfer the arrays as vector<> instead of
// as pointes, but that confuses the compiler so it cannot discover
// that various arrays are disjunct, ruining optimization. For the
// same reason, the temporary array is not a vector<>.
void EMT::force_batch(const int *self, const int *other,
const Vec *rnb, const double *sq_dist,
const double *dEdss, const double *dEdso,
int zs, int zo, int n)
{
USETIMER("EMT::force_batch");
#ifdef SPLITLOOPS
double *temporary = new double[7*BUFLEN];
double *df = temporary;
double *dist = df + BUFLEN;
double *arg = dist + BUFLEN;
double *res = arg + BUFLEN;
double *arg2 = res + BUFLEN;
double *res2 = arg2 + BUFLEN;
double *wght= res2 + BUFLEN;
#else // SPLITLOOPS
double *temporary = new double[BUFLEN];
double *df = temporary;
#endif // SPLITLOOPS
double cutslopecutdist, other_eta2betaseq, other_kappaoverbeta;
double other_kappaseq, self_eta2betaseq, self_kappaoverbeta;
double self_kappaseq, cnst_s, cnst_o;
const emt_parameters *emtself, *emtother;
//double pairA, exprcut, pairD;
//double *tmp;
assert(n <= BUFLEN);
/* Get EMT parameters */
emtself = parameters[zs];
emtother = parameters[zo];
cutslopecutdist = cutoffslope * rFermi;
other_eta2betaseq = emtother->eta2 * Beta * emtother->seq;
other_kappaoverbeta = emtother->kappa / Beta;
other_kappaseq = emtother->kappa * emtother->seq;
self_eta2betaseq = emtself->eta2 * Beta * emtself->seq;
self_kappaoverbeta = emtself->kappa / Beta;
self_kappaseq = emtself->kappa * emtself->seq;
double other_eta2 = emtother->eta2;
double self_eta2 = emtself->eta2;
cnst_s = -0.5 * emtself->V0 * (*chi)[zs][zo] / emtself->gamma2;
cnst_o = -0.5 * emtother->V0 * (*chi)[zo][zs] / emtother->gamma2;
double chi_zs_zo = (*chi)[zs][zo];
double chi_zo_zs = (*chi)[zo][zs];
if (zs == zo)
{
#ifdef SPLITLOOPS
for (int i = 0; i < n; i++)
dist[i] = sqrt(sq_dist[i]);
for (int i = 0; i < n; i++)
arg[i] = cutoffslope * dist[i] - cutslopecutdist;
for (int i = 0; i < n; i++)
res[i] = exp(arg[i]);
for (int i = 0; i < n; i++)
{
wght[i] = 1.0 / (1.0 + res[i]);
arg[i] = -other_eta2 * dist[i] + other_eta2betaseq;
arg2[i] = -other_kappaoverbeta * dist[i] + other_kappaseq;
}
for (int i = 0; i < n; i++)
res[i] = exp(arg[i]);
for (int i = 0; i < n; i++)
res2[i] = exp(arg2[i]);
for (int i = 0; i < n; i++)
{
double dwdr = -cutoffslope * wght[i] * (1 - wght[i]);
double dsigma1dr = (dwdr - wght[i] * other_eta2) * res[i];
double dsigma2dr = (dwdr + wght[i] * -other_kappaoverbeta) * res2[i];
double inv_dist = 1.0 / dist[i];
df[i] = inv_dist * (dsigma1dr * dEdss[i] * chi_zs_zo
+ cnst_s * dsigma2dr
+ dsigma1dr * dEdso[i] * chi_zo_zs
+ cnst_o * dsigma2dr);
}
#else
for (int i = 0; i < n; i++)
{
/* Get the distances from their squares */
double dist = sqrt(sq_dist[i]);
double inv_dist = 1.0 / dist;
/* Calculate cutoff function (weight factor) */
double wght = 1.0 / (1.0 + exp(cutoffslope * dist
- cutslopecutdist));
/* Calculate derivative of the cutoff function. */
double dwdr = -cutoffslope * wght * (1 - wght);
double dsigma1dr = (dwdr - wght * other_eta2) *
exp(-other_eta2 * dist + other_eta2betaseq);
double dsigma2dr = (dwdr + wght * -other_kappaoverbeta) *
exp(-other_kappaoverbeta * dist + other_kappaseq);
df[i] = inv_dist * (dsigma1dr * dEdss[i] * chi_zs_zo
+ cnst_s * dsigma2dr
+ dsigma1dr * dEdso[i] * chi_zo_zs
+ cnst_o * dsigma2dr);
}
#endif //SPLITLOOPS
}
else
{
for (int i = 0; i < n; i++)
{
/* Get the distances from their squares */
double dist = sqrt(sq_dist[i]);
double inv_dist = 1.0 / dist;
/* Calculate cutoff function (weight factor) */
double wght = 1.0 / (1.0 + exp(cutoffslope * dist -
cutslopecutdist));
/* Calculate derivative of the cutoff function. */
double dwdr = -cutoffslope * wght * (1 - wght);
double dsigma1dr_o = (dwdr - wght * other_eta2) *
exp(-other_eta2 * dist + other_eta2betaseq);
double dsigma2dr_o = (dwdr + wght * -other_kappaoverbeta) *
exp(-other_kappaoverbeta * dist + other_kappaseq);
double dsigma1dr_s = (dwdr - wght * self_eta2) *
exp(-self_eta2 * dist + self_eta2betaseq);
double dsigma2dr_s = (dwdr + wght * -self_kappaoverbeta) *
exp(-self_kappaoverbeta * dist + self_kappaseq);
df[i] = inv_dist * (dsigma1dr_o * dEdss[i] * chi_zs_zo
+ cnst_s * dsigma2dr_o
+ dsigma1dr_s * dEdso[i] * chi_zo_zs
+ cnst_o * dsigma2dr_s);
}
}
distribute_force(self, other, df, rnb, n);
delete[] temporary;
}
void EMT::distribute_force(const int *self, const int *other,
const double *df, const Vec *rnb, int n)
{
/* Distribute force contributions */
#ifdef _OPENMP
#pragma omp critical
#endif // _OPENMP
{
for (int i = 0; i < n; i++)
for (int j = 0; j < 3; j++)
{
int o = other[i];
double dfx = df[i] * rnb[i][j];
force[self[i]][j] += dfx;
force[o][j] -= dfx; // force has been extended to nSize.
}
/* Distribute stress contributions */
if (stress.size())
{
for (int i = 0; i < n; i++)
for (int alpha = 0; alpha < 3; alpha++)
for (int beta = alpha; beta < 3; beta++)
{
int o = other[i];
double dsx = 0.5 * df[i] * rnb[i][alpha] * rnb[i][beta];
int ii = stresscomp[alpha][beta];
stress[self[i]][ii] += dsx;
if (o < nAtoms)
stress[o][ii] += dsx;
}
}
}
}
double EMT::GetLatticeConstant() const
{
assert(singleelement != 0);
return Beta * singleelement->seq * sqrt(2.0);
}
void EMT::PrintParameters()
{
for (int i = 0; i < nelements; i++)
{
const emt_parameters *p = parameters[i];
cerr << endl;
cerr << "Parameters for element " << i << " (" << p->name << ", Z=" << p->Z << ")" << endl;
cerr << "E0:" << p->e0 << " s0:" << p->seq << " V0:" << p->V0 <<
" eta2:" << p->eta2 << " kappa:" << p->kappa << " lambda:" << p->lambda <<
" rFermi:" << rFermi << " cutSlope" << cutoffslope <<
" gamma1:" << p->gamma1 << " gamma2:" << p->gamma2 << endl <<
endl;
cerr << "Chi:";
for (int j = 0; j < nelements; j++)
cerr << " " << (*chi)[i][j];
cerr << endl;
}
}
void EMT::BoundaryConditionsChanged()
{
if (nblist)
nblist->Invalidate();
}