413 lines
11 KiB
C++
413 lines
11 KiB
C++
/* ----------------------------------------------------------------------
|
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
|
https://www.lammps.org/, Sandia National Laboratories
|
|
LAMMPS development team: developers@lammps.org
|
|
|
|
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: Sebastian Hütter (OvGU)
|
|
------------------------------------------------------------------------- */
|
|
|
|
#include "meam.h"
|
|
|
|
#include "math_special.h"
|
|
|
|
#include <cmath>
|
|
|
|
using namespace LAMMPS_NS;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Compute G(gamma) based on selection flag ibar:
|
|
// 0 => G = sqrt(1+gamma)
|
|
// 1 => G = exp(gamma/2)
|
|
// 2 => not implemented
|
|
// 3 => G = 2/(1+exp(-gamma))
|
|
// 4 => G = sqrt(1+gamma)
|
|
// -5 => G = +-sqrt(abs(1+gamma))
|
|
//
|
|
double MEAM::G_gam(const double gamma, const int ibar, int &errorflag) const
|
|
{
|
|
double gsmooth_switchpoint;
|
|
|
|
switch (ibar) {
|
|
case 0:
|
|
case 4:
|
|
gsmooth_switchpoint = -gsmooth_factor / (gsmooth_factor + 1);
|
|
if (gamma < gsmooth_switchpoint) {
|
|
// e.g. gsmooth_factor is 99, {:
|
|
// gsmooth_switchpoint = -0.99
|
|
// G = 0.01*(-0.99/gamma)**99
|
|
double G = 1 / (gsmooth_factor + 1) * pow((gsmooth_switchpoint / gamma), gsmooth_factor);
|
|
return sqrt(G);
|
|
} else {
|
|
return sqrt(1.0 + gamma);
|
|
}
|
|
case 1:
|
|
return MathSpecial::fm_exp(gamma / 2.0);
|
|
case 3:
|
|
return 2.0 / (1.0 + MathSpecial::fm_exp(-gamma));
|
|
case -5:
|
|
if ((1.0 + gamma) >= 0) {
|
|
return sqrt(1.0 + gamma);
|
|
} else {
|
|
return -sqrt(-1.0 - gamma);
|
|
}
|
|
}
|
|
errorflag = 1;
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Compute G(gamma and dG(gamma) based on selection flag ibar:
|
|
// 0 => G = sqrt(1+gamma)
|
|
// 1 => G = exp(gamma/2)
|
|
// 2 => not implemented
|
|
// 3 => G = 2/(1+exp(-gamma))
|
|
// 4 => G = sqrt(1+gamma)
|
|
// -5 => G = +-sqrt(abs(1+gamma))
|
|
//
|
|
double MEAM::dG_gam(const double gamma, const int ibar, double &dG) const
|
|
{
|
|
double gsmooth_switchpoint;
|
|
double G;
|
|
|
|
switch (ibar) {
|
|
case 0:
|
|
case 4:
|
|
gsmooth_switchpoint = -gsmooth_factor / (gsmooth_factor + 1);
|
|
if (gamma < gsmooth_switchpoint) {
|
|
// e.g. gsmooth_factor is 99, {:
|
|
// gsmooth_switchpoint = -0.99
|
|
// G = 0.01*(-0.99/gamma)**99
|
|
G = 1 / (gsmooth_factor + 1) * pow((gsmooth_switchpoint / gamma), gsmooth_factor);
|
|
G = sqrt(G);
|
|
dG = -gsmooth_factor * G / (2.0 * gamma);
|
|
return G;
|
|
} else {
|
|
G = sqrt(1.0 + gamma);
|
|
dG = 1.0 / (2.0 * G);
|
|
return G;
|
|
}
|
|
case 1:
|
|
G = MathSpecial::fm_exp(gamma / 2.0);
|
|
dG = G / 2.0;
|
|
return G;
|
|
case 3:
|
|
G = 2.0 / (1.0 + MathSpecial::fm_exp(-gamma));
|
|
dG = G * (2.0 - G) / 2;
|
|
return G;
|
|
case -5:
|
|
if ((1.0 + gamma) >= 0) {
|
|
G = sqrt(1.0 + gamma);
|
|
dG = 1.0 / (2.0 * G);
|
|
return G;
|
|
} else {
|
|
G = -sqrt(-1.0 - gamma);
|
|
dG = -1.0 / (2.0 * G);
|
|
return G;
|
|
}
|
|
}
|
|
dG = 1.0;
|
|
return 0.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Compute ZBL potential
|
|
//
|
|
double MEAM::zbl(const double r, const int z1, const int z2)
|
|
{
|
|
int i;
|
|
const double c[] = {0.028171, 0.28022, 0.50986, 0.18175};
|
|
const double d[] = {0.20162, 0.40290, 0.94229, 3.1998};
|
|
const double azero = 0.4685;
|
|
const double cc = 14.3997;
|
|
double a, x;
|
|
// azero = (9pi^2/128)^1/3 (0.529) Angstroms
|
|
a = azero / (pow(z1, 0.23) + pow(z2, 0.23));
|
|
double result = 0.0;
|
|
x = r / a;
|
|
for (i = 0; i <= 3; i++) { result = result + c[i] * MathSpecial::fm_exp(-d[i] * x); }
|
|
if (r > 0.0) result = result * z1 * z2 / r * cc;
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Compute embedding function F(rhobar) and derivative F'(rhobar), eqn I.5
|
|
//
|
|
double MEAM::embedding(const double A, const double Ec, const double rhobar, double &dF) const
|
|
{
|
|
const double AEc = A * Ec;
|
|
|
|
if (rhobar > 0.0) {
|
|
const double lrb = log(rhobar);
|
|
dF = AEc * (1.0 + lrb);
|
|
return AEc * rhobar * lrb;
|
|
} else {
|
|
if (emb_lin_neg == 0) {
|
|
dF = 0.0;
|
|
return 0.0;
|
|
} else {
|
|
dF = -AEc;
|
|
return -AEc * rhobar;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Compute Rose energy function, I.16
|
|
//
|
|
double MEAM::erose(const double r, const double re, const double alpha, const double Ec,
|
|
const double repuls, const double attrac, const int form)
|
|
{
|
|
double astar, a3;
|
|
double result = 0.0;
|
|
|
|
if (r > 0.0) {
|
|
astar = alpha * (r / re - 1.0);
|
|
a3 = 0.0;
|
|
if (astar >= 0)
|
|
a3 = attrac;
|
|
else if (astar < 0)
|
|
a3 = repuls;
|
|
|
|
if (form == 1)
|
|
result = -Ec * (1 + astar + (-attrac + repuls / r) * MathSpecial::cube(astar)) *
|
|
MathSpecial::fm_exp(-astar);
|
|
else if (form == 2)
|
|
result = -Ec * (1 + astar + a3 * MathSpecial::cube(astar)) * MathSpecial::fm_exp(-astar);
|
|
else
|
|
result = -Ec * (1 + astar + a3 * MathSpecial::cube(astar) / (r / re)) *
|
|
MathSpecial::fm_exp(-astar);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Shape factors for various configurations
|
|
//
|
|
void MEAM::get_shpfcn(const lattice_t latt, const double sthe, const double cthe, double (&s)[3])
|
|
{
|
|
switch (latt) {
|
|
case FCC:
|
|
case BCC:
|
|
case B1:
|
|
case B2:
|
|
case SC:
|
|
s[0] = 0.0;
|
|
s[1] = 0.0;
|
|
s[2] = 0.0;
|
|
break;
|
|
case HCP:
|
|
s[0] = 0.0;
|
|
s[1] = 0.0;
|
|
s[2] = 1.0 / 3.0;
|
|
break;
|
|
case CH4: // CH4 actually needs shape factor for diamond for C, dimer for H
|
|
case DIA:
|
|
case DIA3:
|
|
s[0] = 0.0;
|
|
s[1] = 0.0;
|
|
s[2] = 32.0 / 9.0;
|
|
break;
|
|
case DIM:
|
|
s[0] = 1.0;
|
|
s[1] = 2.0 / 3.0;
|
|
// s(3) = 1.d0 // this should be 0.4 unless (1-legendre) is multiplied in the density calc.
|
|
s[2] = 0.40; // this is (1-legendre) where legendre = 0.6 in dynamo is accounted.
|
|
break;
|
|
case LIN: //linear, theta being 180
|
|
s[0] = 0.0;
|
|
s[1] = 8.0 / 3.0; // 4*(co**4 + si**4 - 1.0/3.0) in zig become 4*(1-1/3)
|
|
s[2] = 0.0;
|
|
break;
|
|
case ZIG: //zig-zag
|
|
case TRI: //trimer e.g. H2O
|
|
s[0] = 4.0 * pow(cthe, 2);
|
|
s[1] = 4.0 * (pow(cthe, 4) + pow(sthe, 4) - 1.0 / 3.0);
|
|
s[2] = 4.0 * (pow(cthe, 2) * (3 * pow(sthe, 4) + pow(cthe, 4)));
|
|
s[2] = s[2] - 0.6 * s[0]; //legend in dyn, 0.6 is default value.
|
|
break;
|
|
default:
|
|
s[0] = 0.0;
|
|
// call error('Lattice not defined in get_shpfcn.')
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Number of first neighbors for reference structure
|
|
//
|
|
int MEAM::get_Zij(const lattice_t latt)
|
|
{
|
|
switch (latt) {
|
|
case FCC:
|
|
return 12;
|
|
case BCC:
|
|
return 8;
|
|
case HCP:
|
|
return 12;
|
|
case DIA:
|
|
case DIA3:
|
|
return 4;
|
|
case DIM:
|
|
return 1;
|
|
case B1:
|
|
case SC:
|
|
return 6;
|
|
case C11:
|
|
return 10;
|
|
case L12:
|
|
return 12;
|
|
case B2:
|
|
return 8;
|
|
case CH4: // DYNAMO currently implemented this way while it needs two Z values, 4 and 1
|
|
return 4;
|
|
case LIN:
|
|
case ZIG:
|
|
case TRI:
|
|
return 2;
|
|
// call error('Lattice not defined in get_Zij.')
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Number of second neighbors for the reference structure
|
|
// a = distance ratio R1/R2 (a2nn in dynamo)
|
|
// numscr = number of atoms that screen the 2NN bond
|
|
// S = second neighbor screening function (xfac, a part of b2nn in dynamo)
|
|
//
|
|
int MEAM::get_Zij2(const lattice_t latt, const double cmin, const double cmax, const double stheta,
|
|
double &a, double &S)
|
|
{
|
|
|
|
double C, x, sijk;
|
|
int Zij2 = 0, numscr = 0;
|
|
|
|
switch (latt) {
|
|
|
|
case FCC:
|
|
Zij2 = 6;
|
|
a = sqrt(2.0);
|
|
numscr = 4;
|
|
break;
|
|
|
|
case BCC:
|
|
Zij2 = 6;
|
|
a = 2.0 / sqrt(3.0);
|
|
numscr = 4;
|
|
break;
|
|
|
|
case HCP:
|
|
Zij2 = 6;
|
|
a = sqrt(2.0);
|
|
numscr = 4;
|
|
break;
|
|
|
|
case B1:
|
|
case SC:
|
|
Zij2 = 12;
|
|
a = sqrt(2.0);
|
|
numscr = 2;
|
|
break;
|
|
|
|
case DIA: // 2NN
|
|
Zij2 = 12;
|
|
a = sqrt(8.0 / 3.0);
|
|
numscr = 1;
|
|
if (cmin < 0.500001) {
|
|
// call error('can not do 2NN MEAM for dia')
|
|
}
|
|
break;
|
|
|
|
case DIA3: // 3NN
|
|
Zij2 = 12;
|
|
a = sqrt(11.0 / 3.0);
|
|
numscr = 4;
|
|
if (cmin < 0.500001) {
|
|
// call error('can not do 2NN MEAM for dia')
|
|
}
|
|
break;
|
|
|
|
case CH4: //does not have 2nn structure so it returns 0
|
|
case LIN: //line
|
|
case DIM:
|
|
// this really shouldn't be allowed; make sure screening is zero
|
|
a = 1.0;
|
|
S = 0.0;
|
|
return 0;
|
|
|
|
case TRI: //TRI
|
|
Zij2 = 1;
|
|
a = 2.0 * stheta;
|
|
numscr = 2;
|
|
break;
|
|
|
|
case ZIG: //zig-zag
|
|
Zij2 = 2;
|
|
a = 2.0 * stheta;
|
|
numscr = 1;
|
|
break;
|
|
|
|
case L12:
|
|
Zij2 = 6;
|
|
a = sqrt(2.0);
|
|
numscr = 4;
|
|
break;
|
|
|
|
case B2:
|
|
Zij2 = 6;
|
|
a = 2.0 / sqrt(3.0);
|
|
numscr = 4;
|
|
break;
|
|
case C11:
|
|
// unsupported lattice flag C11 in get_Zij
|
|
break;
|
|
default:
|
|
// unknown lattic flag in get Zij
|
|
// call error('Lattice not defined in get_Zij.')
|
|
break;
|
|
}
|
|
|
|
// Compute screening for each first neighbor
|
|
if (latt == DIA3) { // special case for 3NN diamond structure
|
|
C = 1.0;
|
|
} else {
|
|
C = 4.0 / (a * a) - 1.0;
|
|
}
|
|
x = (C - cmin) / (cmax - cmin);
|
|
sijk = fcut(x);
|
|
// There are numscr first neighbors screening the second neighbors
|
|
S = MathSpecial::powint(sijk, numscr);
|
|
return Zij2;
|
|
}
|
|
|
|
int MEAM::get_Zij2_b2nn(const lattice_t latt, const double cmin, const double cmax, double &S)
|
|
{
|
|
|
|
double x, sijk, C;
|
|
int numscr = 0, Zij2 = 0;
|
|
switch (latt) {
|
|
case ZIG: //zig-zag for b11s and b22s term
|
|
case TRI: //trimer for b11s
|
|
Zij2 = 2;
|
|
numscr = 1;
|
|
break;
|
|
default:
|
|
// unknown lattic flag in get Zij
|
|
// call error('Lattice not defined in get_Zij.')
|
|
break;
|
|
}
|
|
C = 1.0;
|
|
x = (C - cmin) / (cmax - cmin);
|
|
sijk = fcut(x);
|
|
S = MathSpecial::powint(sijk, numscr);
|
|
return Zij2;
|
|
}
|