1608 lines
46 KiB
C++
1608 lines
46 KiB
C++
// clang-format off
|
|
/* ----------------------------------------------------------------------
|
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
|
https://www.lammps.org/, 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: Aidan Thompson, Christian Trott, SNL
|
|
------------------------------------------------------------------------- */
|
|
|
|
#include "sna.h"
|
|
|
|
#include "comm.h"
|
|
#include "error.h"
|
|
#include "math_const.h"
|
|
#include "math_special.h"
|
|
#include "memory.h"
|
|
|
|
#include <cmath>
|
|
|
|
using namespace std;
|
|
using namespace LAMMPS_NS;
|
|
using namespace MathConst;
|
|
using namespace MathSpecial;
|
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
this implementation is based on the method outlined
|
|
in Bartok[1], using formulae from VMK[2].
|
|
|
|
for the Clebsch-Gordan coefficients, we
|
|
convert the VMK half-integral labels
|
|
a, b, c, alpha, beta, gamma
|
|
to array offsets j1, j2, j, m1, m2, m
|
|
using the following relations:
|
|
|
|
j1 = 2*a
|
|
j2 = 2*b
|
|
j = 2*c
|
|
|
|
m1 = alpha+a 2*alpha = 2*m1 - j1
|
|
m2 = beta+b or 2*beta = 2*m2 - j2
|
|
m = gamma+c 2*gamma = 2*m - j
|
|
|
|
in this way:
|
|
|
|
-a <= alpha <= a
|
|
-b <= beta <= b
|
|
-c <= gamma <= c
|
|
|
|
becomes:
|
|
|
|
0 <= m1 <= j1
|
|
0 <= m2 <= j2
|
|
0 <= m <= j
|
|
|
|
and the requirement that
|
|
a+b+c be integral implies that
|
|
j1+j2+j must be even.
|
|
The requirement that:
|
|
|
|
gamma = alpha+beta
|
|
|
|
becomes:
|
|
|
|
2*m - j = 2*m1 - j1 + 2*m2 - j2
|
|
|
|
Similarly, for the Wigner U-functions U(J,m,m') we
|
|
convert the half-integral labels J,m,m' to
|
|
array offsets j,ma,mb:
|
|
|
|
j = 2*J
|
|
ma = J+m
|
|
mb = J+m'
|
|
|
|
so that:
|
|
|
|
0 <= j <= 2*Jmax
|
|
0 <= ma, mb <= j.
|
|
|
|
For the bispectrum components B(J1,J2,J) we convert to:
|
|
|
|
j1 = 2*J1
|
|
j2 = 2*J2
|
|
j = 2*J
|
|
|
|
and the requirement:
|
|
|
|
|J1-J2| <= J <= J1+J2, for j1+j2+j integral
|
|
|
|
becomes:
|
|
|
|
|j1-j2| <= j <= j1+j2, for j1+j2+j even integer
|
|
|
|
or
|
|
|
|
j = |j1-j2|, |j1-j2|+2,...,j1+j2-2,j1+j2
|
|
|
|
[1] Albert Bartok-Partay, "Gaussian Approximation..."
|
|
Doctoral Thesis, Cambridge University, (2009)
|
|
|
|
[2] D. A. Varshalovich, A. N. Moskalev, and V. K. Khersonskii,
|
|
"Quantum Theory of Angular Momentum," World Scientific (1988)
|
|
|
|
------------------------------------------------------------------------- */
|
|
|
|
SNA::SNA(LAMMPS* lmp, double rfac0_in, int twojmax_in,
|
|
double rmin0_in, int switch_flag_in, int bzero_flag_in,
|
|
int chem_flag_in, int bnorm_flag_in, int wselfall_flag_in,
|
|
int nelements_in, int switch_inner_flag_in) : Pointers(lmp)
|
|
{
|
|
wself = 1.0;
|
|
|
|
rfac0 = rfac0_in;
|
|
rmin0 = rmin0_in;
|
|
switch_flag = switch_flag_in;
|
|
switch_inner_flag = switch_inner_flag_in;
|
|
bzero_flag = bzero_flag_in;
|
|
chem_flag = chem_flag_in;
|
|
bnorm_flag = bnorm_flag_in;
|
|
wselfall_flag = wselfall_flag_in;
|
|
|
|
if (bnorm_flag != chem_flag)
|
|
lmp->error->warning(FLERR, "bnormflag and chemflag are not equal."
|
|
"This is probably not what you intended");
|
|
|
|
if (chem_flag)
|
|
nelements = nelements_in;
|
|
else
|
|
nelements = 1;
|
|
|
|
twojmax = twojmax_in;
|
|
|
|
compute_ncoeff();
|
|
|
|
rij = nullptr;
|
|
inside = nullptr;
|
|
wj = nullptr;
|
|
rcutij = nullptr;
|
|
rinnerij = nullptr;
|
|
drinnerij = nullptr;
|
|
element = nullptr;
|
|
nmax = 0;
|
|
idxz = nullptr;
|
|
idxb = nullptr;
|
|
ulist_r_ij = nullptr;
|
|
ulist_i_ij = nullptr;
|
|
|
|
build_indexlist();
|
|
create_twojmax_arrays();
|
|
|
|
if (bzero_flag) {
|
|
double www = wself*wself*wself;
|
|
for (int j = 0; j <= twojmax; j++)
|
|
if (bnorm_flag)
|
|
bzero[j] = www;
|
|
else
|
|
bzero[j] = www*(j+1);
|
|
}
|
|
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
SNA::~SNA()
|
|
{
|
|
memory->destroy(rij);
|
|
memory->destroy(inside);
|
|
memory->destroy(wj);
|
|
memory->destroy(rcutij);
|
|
if (switch_inner_flag) {
|
|
memory->destroy(rinnerij);
|
|
memory->destroy(drinnerij);
|
|
}
|
|
if (chem_flag) memory->destroy(element);
|
|
memory->destroy(ulist_r_ij);
|
|
memory->destroy(ulist_i_ij);
|
|
delete[] idxz;
|
|
delete[] idxb;
|
|
destroy_twojmax_arrays();
|
|
}
|
|
|
|
void SNA::build_indexlist()
|
|
{
|
|
|
|
// index list for cglist
|
|
|
|
int jdim = twojmax + 1;
|
|
memory->create(idxcg_block, jdim, jdim, jdim,
|
|
"sna:idxcg_block");
|
|
|
|
int idxcg_count = 0;
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
for (int j = j1 - j2; j <= MIN(twojmax, j1 + j2); j += 2) {
|
|
idxcg_block[j1][j2][j] = idxcg_count;
|
|
for (int m1 = 0; m1 <= j1; m1++)
|
|
for (int m2 = 0; m2 <= j2; m2++)
|
|
idxcg_count++;
|
|
}
|
|
idxcg_max = idxcg_count;
|
|
|
|
// index list for uarray
|
|
// need to include both halves
|
|
|
|
memory->create(idxu_block, jdim,
|
|
"sna:idxu_block");
|
|
|
|
int idxu_count = 0;
|
|
|
|
for (int j = 0; j <= twojmax; j++) {
|
|
idxu_block[j] = idxu_count;
|
|
for (int mb = 0; mb <= j; mb++)
|
|
for (int ma = 0; ma <= j; ma++)
|
|
idxu_count++;
|
|
}
|
|
idxu_max = idxu_count;
|
|
|
|
// index list for beta and B
|
|
|
|
int idxb_count = 0;
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
for (int j = j1 - j2; j <= MIN(twojmax, j1 + j2); j += 2)
|
|
if (j >= j1) idxb_count++;
|
|
|
|
idxb_max = idxb_count;
|
|
idxb = new SNA_BINDICES[idxb_max];
|
|
|
|
idxb_count = 0;
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
for (int j = j1 - j2; j <= MIN(twojmax, j1 + j2); j += 2)
|
|
if (j >= j1) {
|
|
idxb[idxb_count].j1 = j1;
|
|
idxb[idxb_count].j2 = j2;
|
|
idxb[idxb_count].j = j;
|
|
idxb_count++;
|
|
}
|
|
|
|
// reverse index list for beta and b
|
|
|
|
memory->create(idxb_block, jdim, jdim, jdim,
|
|
"sna:idxb_block");
|
|
idxb_count = 0;
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
for (int j = j1 - j2; j <= MIN(twojmax, j1 + j2); j += 2) {
|
|
if (j >= j1) {
|
|
idxb_block[j1][j2][j] = idxb_count;
|
|
idxb_count++;
|
|
}
|
|
}
|
|
|
|
// index list for zlist
|
|
|
|
int idxz_count = 0;
|
|
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
for (int j = j1 - j2; j <= MIN(twojmax, j1 + j2); j += 2)
|
|
for (int mb = 0; 2*mb <= j; mb++)
|
|
for (int ma = 0; ma <= j; ma++)
|
|
idxz_count++;
|
|
|
|
idxz_max = idxz_count;
|
|
idxz = new SNA_ZINDICES[idxz_max];
|
|
|
|
memory->create(idxz_block, jdim, jdim, jdim,
|
|
"sna:idxz_block");
|
|
|
|
idxz_count = 0;
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
for (int j = j1 - j2; j <= MIN(twojmax, j1 + j2); j += 2) {
|
|
idxz_block[j1][j2][j] = idxz_count;
|
|
|
|
// find right beta[jjb] entry
|
|
// multiply and divide by j+1 factors
|
|
// account for multiplicity of 1, 2, or 3
|
|
|
|
for (int mb = 0; 2*mb <= j; mb++)
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
idxz[idxz_count].j1 = j1;
|
|
idxz[idxz_count].j2 = j2;
|
|
idxz[idxz_count].j = j;
|
|
idxz[idxz_count].ma1min = MAX(0, (2 * ma - j - j2 + j1) / 2);
|
|
idxz[idxz_count].ma2max = (2 * ma - j - (2 * idxz[idxz_count].ma1min - j1) + j2) / 2;
|
|
idxz[idxz_count].na = MIN(j1, (2 * ma - j + j2 + j1) / 2) - idxz[idxz_count].ma1min + 1;
|
|
idxz[idxz_count].mb1min = MAX(0, (2 * mb - j - j2 + j1) / 2);
|
|
idxz[idxz_count].mb2max = (2 * mb - j - (2 * idxz[idxz_count].mb1min - j1) + j2) / 2;
|
|
idxz[idxz_count].nb = MIN(j1, (2 * mb - j + j2 + j1) / 2) - idxz[idxz_count].mb1min + 1;
|
|
// apply to z(j1,j2,j,ma,mb) to unique element of y(j)
|
|
|
|
const int jju = idxu_block[j] + (j+1)*mb + ma;
|
|
idxz[idxz_count].jju = jju;
|
|
|
|
idxz_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void SNA::init()
|
|
{
|
|
init_clebsch_gordan();
|
|
// print_clebsch_gordan();
|
|
init_rootpqarray();
|
|
}
|
|
|
|
void SNA::grow_rij(int newnmax)
|
|
{
|
|
if (newnmax <= nmax) return;
|
|
|
|
nmax = newnmax;
|
|
|
|
memory->destroy(rij);
|
|
memory->destroy(inside);
|
|
memory->destroy(wj);
|
|
memory->destroy(rcutij);
|
|
if (switch_inner_flag) {
|
|
memory->destroy(rinnerij);
|
|
memory->destroy(drinnerij);
|
|
}
|
|
if (chem_flag) memory->destroy(element);
|
|
memory->destroy(ulist_r_ij);
|
|
memory->destroy(ulist_i_ij);
|
|
memory->create(rij, nmax, 3, "pair:rij");
|
|
memory->create(inside, nmax, "pair:inside");
|
|
memory->create(wj, nmax, "pair:wj");
|
|
memory->create(rcutij, nmax, "pair:rcutij");
|
|
if (switch_inner_flag) {
|
|
memory->create(rinnerij, nmax, "pair:rinnerij");
|
|
memory->create(drinnerij, nmax, "pair:drinnerij");
|
|
}
|
|
if (chem_flag) memory->create(element, nmax, "sna:element");
|
|
memory->create(ulist_r_ij, nmax, idxu_max, "sna:ulist_ij");
|
|
memory->create(ulist_i_ij, nmax, idxu_max, "sna:ulist_ij");
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute Ui by summing over neighbors j
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_ui(int jnum, int ielem)
|
|
{
|
|
double rsq, r, x, y, z, z0, theta0;
|
|
|
|
// utot(j,ma,mb) = 0 for all j,ma,ma
|
|
// utot(j,ma,ma) = 1 for all j,ma
|
|
// for j in neighbors of i:
|
|
// compute r0 = (x,y,z,z0)
|
|
// utot(j,ma,mb) += u(r0;j,ma,mb) for all j,ma,mb
|
|
|
|
zero_uarraytot(ielem);
|
|
|
|
for (int j = 0; j < jnum; j++) {
|
|
x = rij[j][0];
|
|
y = rij[j][1];
|
|
z = rij[j][2];
|
|
rsq = x * x + y * y + z * z;
|
|
r = sqrt(rsq);
|
|
|
|
theta0 = (r - rmin0) * rfac0 * MY_PI / (rcutij[j] - rmin0);
|
|
// theta0 = (r - rmin0) * rscale0;
|
|
z0 = r / tan(theta0);
|
|
|
|
compute_uarray(x, y, z, z0, r, j);
|
|
add_uarraytot(r, j);
|
|
}
|
|
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute Zi by summing over products of Ui
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_zi()
|
|
{
|
|
|
|
int idouble = 0;
|
|
double * zptr_r;
|
|
double * zptr_i;
|
|
for (int elem1 = 0; elem1 < nelements; elem1++)
|
|
for (int elem2 = 0; elem2 < nelements; elem2++) {
|
|
|
|
zptr_r = &zlist_r[idouble*idxz_max];
|
|
zptr_i = &zlist_i[idouble*idxz_max];
|
|
|
|
for (int jjz = 0; jjz < idxz_max; jjz++) {
|
|
const int j1 = idxz[jjz].j1;
|
|
const int j2 = idxz[jjz].j2;
|
|
const int j = idxz[jjz].j;
|
|
const int ma1min = idxz[jjz].ma1min;
|
|
const int ma2max = idxz[jjz].ma2max;
|
|
const int na = idxz[jjz].na;
|
|
const int mb1min = idxz[jjz].mb1min;
|
|
const int mb2max = idxz[jjz].mb2max;
|
|
const int nb = idxz[jjz].nb;
|
|
|
|
const double *cgblock = cglist + idxcg_block[j1][j2][j];
|
|
|
|
zptr_r[jjz] = 0.0;
|
|
zptr_i[jjz] = 0.0;
|
|
|
|
int jju1 = idxu_block[j1] + (j1 + 1) * mb1min;
|
|
int jju2 = idxu_block[j2] + (j2 + 1) * mb2max;
|
|
int icgb = mb1min * (j2 + 1) + mb2max;
|
|
for (int ib = 0; ib < nb; ib++) {
|
|
|
|
double suma1_r = 0.0;
|
|
double suma1_i = 0.0;
|
|
|
|
const double *u1_r = &ulisttot_r[elem1*idxu_max+jju1];
|
|
const double *u1_i = &ulisttot_i[elem1*idxu_max+jju1];
|
|
const double *u2_r = &ulisttot_r[elem2*idxu_max+jju2];
|
|
const double *u2_i = &ulisttot_i[elem2*idxu_max+jju2];
|
|
|
|
int ma1 = ma1min;
|
|
int ma2 = ma2max;
|
|
int icga = ma1min * (j2 + 1) + ma2max;
|
|
|
|
for (int ia = 0; ia < na; ia++) {
|
|
suma1_r += cgblock[icga] * (u1_r[ma1] * u2_r[ma2] - u1_i[ma1] * u2_i[ma2]);
|
|
suma1_i += cgblock[icga] * (u1_r[ma1] * u2_i[ma2] + u1_i[ma1] * u2_r[ma2]);
|
|
ma1++;
|
|
ma2--;
|
|
icga += j2;
|
|
} // end loop over ia
|
|
|
|
zptr_r[jjz] += cgblock[icgb] * suma1_r;
|
|
zptr_i[jjz] += cgblock[icgb] * suma1_i;
|
|
|
|
jju1 += j1 + 1;
|
|
jju2 -= j2 + 1;
|
|
icgb += j2;
|
|
} // end loop over ib
|
|
if (bnorm_flag) {
|
|
zptr_r[jjz] /= (j+1);
|
|
zptr_i[jjz] /= (j+1);
|
|
}
|
|
} // end loop over jjz
|
|
idouble++;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute Yi from Ui without storing Zi, looping over zlist indices
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_yi(const double* beta)
|
|
{
|
|
int jju;
|
|
double betaj;
|
|
int itriple;
|
|
|
|
for (int ielem1 = 0; ielem1 < nelements; ielem1++)
|
|
for (int j = 0; j <= twojmax; j++) {
|
|
jju = idxu_block[j];
|
|
for (int mb = 0; 2*mb <= j; mb++)
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
ylist_r[ielem1*idxu_max+jju] = 0.0;
|
|
ylist_i[ielem1*idxu_max+jju] = 0.0;
|
|
jju++;
|
|
} // end loop over ma, mb
|
|
} // end loop over j
|
|
|
|
for (int elem1 = 0; elem1 < nelements; elem1++)
|
|
for (int elem2 = 0; elem2 < nelements; elem2++) {
|
|
for (int jjz = 0; jjz < idxz_max; jjz++) {
|
|
const int j1 = idxz[jjz].j1;
|
|
const int j2 = idxz[jjz].j2;
|
|
const int j = idxz[jjz].j;
|
|
const int ma1min = idxz[jjz].ma1min;
|
|
const int ma2max = idxz[jjz].ma2max;
|
|
const int na = idxz[jjz].na;
|
|
const int mb1min = idxz[jjz].mb1min;
|
|
const int mb2max = idxz[jjz].mb2max;
|
|
const int nb = idxz[jjz].nb;
|
|
|
|
const double *cgblock = cglist + idxcg_block[j1][j2][j];
|
|
|
|
double ztmp_r = 0.0;
|
|
double ztmp_i = 0.0;
|
|
|
|
int jju1 = idxu_block[j1] + (j1 + 1) * mb1min;
|
|
int jju2 = idxu_block[j2] + (j2 + 1) * mb2max;
|
|
int icgb = mb1min * (j2 + 1) + mb2max;
|
|
for (int ib = 0; ib < nb; ib++) {
|
|
|
|
double suma1_r = 0.0;
|
|
double suma1_i = 0.0;
|
|
|
|
const double *u1_r = &ulisttot_r[elem1*idxu_max+jju1];
|
|
const double *u1_i = &ulisttot_i[elem1*idxu_max+jju1];
|
|
const double *u2_r = &ulisttot_r[elem2*idxu_max+jju2];
|
|
const double *u2_i = &ulisttot_i[elem2*idxu_max+jju2];
|
|
|
|
int ma1 = ma1min;
|
|
int ma2 = ma2max;
|
|
int icga = ma1min * (j2 + 1) + ma2max;
|
|
|
|
for (int ia = 0; ia < na; ia++) {
|
|
suma1_r += cgblock[icga] * (u1_r[ma1] * u2_r[ma2] - u1_i[ma1] * u2_i[ma2]);
|
|
suma1_i += cgblock[icga] * (u1_r[ma1] * u2_i[ma2] + u1_i[ma1] * u2_r[ma2]);
|
|
ma1++;
|
|
ma2--;
|
|
icga += j2;
|
|
} // end loop over ia
|
|
|
|
|
|
ztmp_r += cgblock[icgb] * suma1_r;
|
|
ztmp_i += cgblock[icgb] * suma1_i;
|
|
|
|
jju1 += j1 + 1;
|
|
jju2 -= j2 + 1;
|
|
icgb += j2;
|
|
} // end loop over ib
|
|
|
|
// apply to z(j1,j2,j,ma,mb) to unique element of y(j)
|
|
// find right y_list[jju] and beta[jjb] entries
|
|
// multiply and divide by j+1 factors
|
|
// account for multiplicity of 1, 2, or 3
|
|
|
|
if (bnorm_flag) {
|
|
ztmp_i /= j+1;
|
|
ztmp_r /= j+1;
|
|
}
|
|
|
|
jju = idxz[jjz].jju;
|
|
for (int elem3 = 0; elem3 < nelements; elem3++) {
|
|
// pick out right beta value
|
|
if (j >= j1) {
|
|
const int jjb = idxb_block[j1][j2][j];
|
|
itriple = ((elem1 * nelements + elem2) * nelements + elem3) * idxb_max + jjb;
|
|
if (j1 == j) {
|
|
if (j2 == j) betaj = 3*beta[itriple];
|
|
else betaj = 2*beta[itriple];
|
|
} else betaj = beta[itriple];
|
|
} else if (j >= j2) {
|
|
const int jjb = idxb_block[j][j2][j1];
|
|
itriple = ((elem3 * nelements + elem2) * nelements + elem1) * idxb_max + jjb;
|
|
if (j2 == j) betaj = 2*beta[itriple];
|
|
else betaj = beta[itriple];
|
|
} else {
|
|
const int jjb = idxb_block[j2][j][j1];
|
|
itriple = ((elem2 * nelements + elem3) * nelements + elem1) * idxb_max + jjb;
|
|
betaj = beta[itriple];
|
|
}
|
|
|
|
if (!bnorm_flag && j1 > j)
|
|
betaj *= (j1 + 1) / (j + 1.0);
|
|
|
|
ylist_r[elem3 * idxu_max + jju] += betaj * ztmp_r;
|
|
ylist_i[elem3 * idxu_max + jju] += betaj * ztmp_i;
|
|
}
|
|
} // end loop over jjz
|
|
}
|
|
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute dEidRj
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_deidrj(double* dedr)
|
|
{
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
dedr[k] = 0.0;
|
|
|
|
int jelem = elem_duarray;
|
|
for (int j = 0; j <= twojmax; j++) {
|
|
int jju = idxu_block[j];
|
|
|
|
for (int mb = 0; 2*mb < j; mb++)
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
|
|
double* dudr_r = dulist_r[jju];
|
|
double* dudr_i = dulist_i[jju];
|
|
double jjjmambyarray_r = ylist_r[jelem*idxu_max+jju];
|
|
double jjjmambyarray_i = ylist_i[jelem*idxu_max+jju];
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
dedr[k] +=
|
|
dudr_r[k] * jjjmambyarray_r +
|
|
dudr_i[k] * jjjmambyarray_i;
|
|
jju++;
|
|
} //end loop over ma mb
|
|
|
|
// For j even, handle middle column
|
|
|
|
if (j%2 == 0) {
|
|
|
|
int mb = j/2;
|
|
for (int ma = 0; ma < mb; ma++) {
|
|
double* dudr_r = dulist_r[jju];
|
|
double* dudr_i = dulist_i[jju];
|
|
double jjjmambyarray_r = ylist_r[jelem*idxu_max+jju];
|
|
double jjjmambyarray_i = ylist_i[jelem*idxu_max+jju];
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
dedr[k] +=
|
|
dudr_r[k] * jjjmambyarray_r +
|
|
dudr_i[k] * jjjmambyarray_i;
|
|
jju++;
|
|
}
|
|
|
|
double* dudr_r = dulist_r[jju];
|
|
double* dudr_i = dulist_i[jju];
|
|
double jjjmambyarray_r = ylist_r[jelem*idxu_max+jju];
|
|
double jjjmambyarray_i = ylist_i[jelem*idxu_max+jju];
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
dedr[k] +=
|
|
(dudr_r[k] * jjjmambyarray_r +
|
|
dudr_i[k] * jjjmambyarray_i)*0.5;
|
|
// jju++;
|
|
|
|
} // end if jeven
|
|
|
|
} // end loop over j
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
dedr[k] *= 2.0;
|
|
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute Bi by summing conj(Ui)*Zi
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_bi(int ielem) {
|
|
// for j1 = 0,...,twojmax
|
|
// for j2 = 0,twojmax
|
|
// for j = |j1-j2|,Min(twojmax,j1+j2),2
|
|
// b(j1,j2,j) = 0
|
|
// for mb = 0,...,jmid
|
|
// for ma = 0,...,j
|
|
// b(j1,j2,j) +=
|
|
// 2*Conj(u(j,ma,mb))*z(j1,j2,j,ma,mb)
|
|
|
|
int itriple = 0;
|
|
int idouble = 0;
|
|
for (int elem1 = 0; elem1 < nelements; elem1++)
|
|
for (int elem2 = 0; elem2 < nelements; elem2++) {
|
|
|
|
double *zptr_r = &zlist_r[idouble*idxz_max];
|
|
double *zptr_i = &zlist_i[idouble*idxz_max];
|
|
|
|
for (int elem3 = 0; elem3 < nelements; elem3++) {
|
|
for (int jjb = 0; jjb < idxb_max; jjb++) {
|
|
const int j1 = idxb[jjb].j1;
|
|
const int j2 = idxb[jjb].j2;
|
|
const int j = idxb[jjb].j;
|
|
|
|
int jjz = idxz_block[j1][j2][j];
|
|
int jju = idxu_block[j];
|
|
double sumzu = 0.0;
|
|
for (int mb = 0; 2 * mb < j; mb++)
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
sumzu += ulisttot_r[elem3*idxu_max+jju] * zptr_r[jjz] +
|
|
ulisttot_i[elem3*idxu_max+jju] * zptr_i[jjz];
|
|
jjz++;
|
|
jju++;
|
|
} // end loop over ma, mb
|
|
|
|
// For j even, handle middle column
|
|
|
|
if (j % 2 == 0) {
|
|
int mb = j / 2;
|
|
for (int ma = 0; ma < mb; ma++) {
|
|
sumzu += ulisttot_r[elem3*idxu_max+jju] * zptr_r[jjz] +
|
|
ulisttot_i[elem3*idxu_max+jju] * zptr_i[jjz];
|
|
jjz++;
|
|
jju++;
|
|
}
|
|
|
|
sumzu += 0.5 * (ulisttot_r[elem3*idxu_max+jju] * zptr_r[jjz] +
|
|
ulisttot_i[elem3*idxu_max+jju] * zptr_i[jjz]);
|
|
} // end if jeven
|
|
|
|
blist[itriple*idxb_max+jjb] = 2.0 * sumzu;
|
|
|
|
}
|
|
itriple++;
|
|
}
|
|
idouble++;
|
|
}
|
|
|
|
// apply bzero shift
|
|
|
|
if (bzero_flag) {
|
|
if (!wselfall_flag) {
|
|
itriple = (ielem*nelements+ielem)*nelements+ielem;
|
|
for (int jjb = 0; jjb < idxb_max; jjb++) {
|
|
const int j = idxb[jjb].j;
|
|
blist[itriple*idxb_max+jjb] -= bzero[j];
|
|
} // end loop over JJ
|
|
} else {
|
|
int itriple = 0;
|
|
for (int elem1 = 0; elem1 < nelements; elem1++)
|
|
for (int elem2 = 0; elem2 < nelements; elem2++) {
|
|
for (int elem3 = 0; elem3 < nelements; elem3++) {
|
|
for (int jjb = 0; jjb < idxb_max; jjb++) {
|
|
const int j = idxb[jjb].j;
|
|
blist[itriple*idxb_max+jjb] -= bzero[j];
|
|
} // end loop over JJ
|
|
itriple++;
|
|
} // end loop over elem3
|
|
} // end loop over elem1,elem2
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
calculate derivative of Bi w.r.t. atom j
|
|
variant using indexlist for j1,j2,j
|
|
variant using symmetry relation
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_dbidrj()
|
|
{
|
|
// for j1 = 0,...,twojmax
|
|
// for j2 = 0,twojmax
|
|
// for j = |j1-j2|,Min(twojmax,j1+j2),2
|
|
// zdb = 0
|
|
// for mb = 0,...,jmid
|
|
// for ma = 0,...,j
|
|
// zdb +=
|
|
// Conj(dudr(j,ma,mb))*z(j1,j2,j,ma,mb)
|
|
// dbdr(j1,j2,j) += 2*zdb
|
|
// zdb = 0
|
|
// for mb1 = 0,...,j1mid
|
|
// for ma1 = 0,...,j1
|
|
// zdb +=
|
|
// Conj(dudr(j1,ma1,mb1))*z(j,j2,j1,ma1,mb1)
|
|
// dbdr(j1,j2,j) += 2*zdb*(j+1)/(j1+1)
|
|
// zdb = 0
|
|
// for mb2 = 0,...,j2mid
|
|
// for ma2 = 0,...,j2
|
|
// zdb +=
|
|
// Conj(dudr(j2,ma2,mb2))*z(j1,j,j2,ma2,mb2)
|
|
// dbdr(j1,j2,j) += 2*zdb*(j+1)/(j2+1)
|
|
|
|
double* dbdr;
|
|
double* dudr_r, *dudr_i;
|
|
double sumzdu_r[3];
|
|
double* zptr_r;
|
|
double* zptr_i;
|
|
int jjz, jju;
|
|
|
|
int idouble;
|
|
int itriple;
|
|
|
|
// set all the derivatives to zero once
|
|
|
|
for (int jjb = 0; jjb < idxb_max; jjb++) {
|
|
|
|
for (int elem1 = 0; elem1 < nelements; elem1++)
|
|
for (int elem2 = 0; elem2 < nelements; elem2++)
|
|
for (int elem3 = 0; elem3 < nelements; elem3++) {
|
|
|
|
itriple = (elem1 * nelements + elem2) * nelements + elem3;
|
|
|
|
dbdr = dblist[itriple*idxb_max+jjb];
|
|
dbdr[0] = 0.0;
|
|
dbdr[1] = 0.0;
|
|
dbdr[2] = 0.0;
|
|
}
|
|
|
|
}
|
|
|
|
int elem3 = elem_duarray;
|
|
|
|
for (int jjb = 0; jjb < idxb_max; jjb++) {
|
|
const int j1 = idxb[jjb].j1;
|
|
const int j2 = idxb[jjb].j2;
|
|
const int j = idxb[jjb].j;
|
|
|
|
|
|
// Sum terms Conj(dudr(j,ma,mb))*z(j1,j2,j,ma,mb)
|
|
|
|
for (int elem1 = 0; elem1 < nelements; elem1++)
|
|
for (int elem2 = 0; elem2 < nelements; elem2++) {
|
|
|
|
jjz = idxz_block[j1][j2][j];
|
|
jju = idxu_block[j];
|
|
idouble = elem1*nelements+elem2;
|
|
itriple = (elem1*nelements+elem2)*nelements+elem3;
|
|
dbdr = dblist[itriple*idxb_max+jjb];
|
|
zptr_r = &zlist_r[idouble*idxz_max];
|
|
zptr_i = &zlist_i[idouble*idxz_max];
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] = 0.0;
|
|
|
|
for (int mb = 0; 2 * mb < j; mb++)
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz];
|
|
jjz++;
|
|
jju++;
|
|
} //end loop over ma mb
|
|
|
|
// For j even, handle middle column
|
|
|
|
if (j % 2 == 0) {
|
|
int mb = j / 2;
|
|
for (int ma = 0; ma < mb; ma++) {
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz];
|
|
jjz++;
|
|
jju++;
|
|
}
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
(dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz]) * 0.5;
|
|
// jjz++;
|
|
// jju++;
|
|
} // end if jeven
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
dbdr[k] += 2.0 * sumzdu_r[k];
|
|
// Sum over Conj(dudr(j1,ma1,mb1))*z(j,j2,j1,ma1,mb1)
|
|
|
|
double j1fac = (j + 1) / (j1 + 1.0);
|
|
|
|
idouble = elem1*nelements+elem2;
|
|
itriple = (elem3*nelements+elem2)*nelements+elem1;
|
|
dbdr = dblist[itriple*idxb_max+jjb];
|
|
jjz = idxz_block[j][j2][j1];
|
|
jju = idxu_block[j1];
|
|
zptr_r = &zlist_r[idouble*idxz_max];
|
|
zptr_i = &zlist_i[idouble*idxz_max];
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] = 0.0;
|
|
|
|
for (int mb = 0; 2 * mb < j1; mb++)
|
|
for (int ma = 0; ma <= j1; ma++) {
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz];
|
|
jjz++;
|
|
jju++;
|
|
} //end loop over ma mb
|
|
|
|
// For j1 even, handle middle column
|
|
|
|
if (j1 % 2 == 0) {
|
|
int mb = j1 / 2;
|
|
for (int ma = 0; ma < mb; ma++) {
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz];
|
|
jjz++;
|
|
jju++;
|
|
}
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
(dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz]) * 0.5;
|
|
// jjz++;
|
|
// jju++;
|
|
} // end if j1even
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
if (bnorm_flag)
|
|
dbdr[k] += 2.0 * sumzdu_r[k];
|
|
else
|
|
dbdr[k] += 2.0 * sumzdu_r[k] * j1fac;
|
|
|
|
// Sum over Conj(dudr(j2,ma2,mb2))*z(j,j1,j2,ma2,mb2)
|
|
|
|
double j2fac = (j + 1) / (j2 + 1.0);
|
|
|
|
idouble = elem2*nelements+elem1;
|
|
itriple = (elem1*nelements+elem3)*nelements+elem2;
|
|
dbdr = dblist[itriple*idxb_max+jjb];
|
|
jjz = idxz_block[j][j1][j2];
|
|
jju = idxu_block[j2];
|
|
zptr_r = &zlist_r[idouble*idxz_max];
|
|
zptr_i = &zlist_i[idouble*idxz_max];
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] = 0.0;
|
|
|
|
for (int mb = 0; 2 * mb < j2; mb++)
|
|
for (int ma = 0; ma <= j2; ma++) {
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz];
|
|
jjz++;
|
|
jju++;
|
|
} //end loop over ma mb
|
|
|
|
// For j2 even, handle middle column
|
|
|
|
if (j2 % 2 == 0) {
|
|
int mb = j2 / 2;
|
|
for (int ma = 0; ma < mb; ma++) {
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz];
|
|
jjz++;
|
|
jju++;
|
|
}
|
|
dudr_r = dulist_r[jju];
|
|
dudr_i = dulist_i[jju];
|
|
for (int k = 0; k < 3; k++)
|
|
sumzdu_r[k] +=
|
|
(dudr_r[k] * zptr_r[jjz] +
|
|
dudr_i[k] * zptr_i[jjz]) * 0.5;
|
|
// jjz++;
|
|
// jju++;
|
|
} // end if j2even
|
|
|
|
for (int k = 0; k < 3; k++)
|
|
if (bnorm_flag)
|
|
dbdr[k] += 2.0 * sumzdu_r[k];
|
|
else
|
|
dbdr[k] += 2.0 * sumzdu_r[k] * j2fac;
|
|
|
|
}
|
|
} //end loop over j1 j2 j
|
|
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
calculate derivative of Ui w.r.t. atom j
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_duidrj(int jj)
|
|
{
|
|
double rsq, r, x, y, z, z0, theta0, cs, sn;
|
|
double dz0dr;
|
|
double rcut = rcutij[jj];
|
|
|
|
x = rij[jj][0];
|
|
y = rij[jj][1];
|
|
z = rij[jj][2];
|
|
rsq = x * x + y * y + z * z;
|
|
r = sqrt(rsq);
|
|
double rscale0 = rfac0 * MY_PI / (rcut - rmin0);
|
|
theta0 = (r - rmin0) * rscale0;
|
|
cs = cos(theta0);
|
|
sn = sin(theta0);
|
|
z0 = r * cs / sn;
|
|
dz0dr = z0 / r - (r*rscale0) * (rsq + z0 * z0) / rsq;
|
|
|
|
if (chem_flag) elem_duarray = element[jj];
|
|
else elem_duarray = 0;
|
|
|
|
compute_duarray(x, y, z, z0, r, dz0dr, wj[jj], rcut, jj);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void SNA::zero_uarraytot(int ielem)
|
|
{
|
|
for (int jelem = 0; jelem < nelements; jelem++)
|
|
for (int j = 0; j <= twojmax; j++) {
|
|
int jju = idxu_block[j];
|
|
for (int mb = 0; mb <= j; mb++) {
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
ulisttot_r[jelem*idxu_max+jju] = 0.0;
|
|
ulisttot_i[jelem*idxu_max+jju] = 0.0;
|
|
|
|
// utot(j,ma,ma) = wself, sometimes
|
|
if (jelem == ielem || wselfall_flag)
|
|
if (ma==mb)
|
|
ulisttot_r[jelem*idxu_max+jju] = wself; ///// double check this
|
|
jju++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
add Wigner U-functions for one neighbor to the total
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::add_uarraytot(double r, int jj)
|
|
{
|
|
double sfac;
|
|
int jelem;
|
|
|
|
sfac = compute_sfac(r, rcutij[jj]);
|
|
sfac *= wj[jj];
|
|
|
|
if (switch_inner_flag)
|
|
sfac *= compute_sfac_inner(r, rinnerij[jj], drinnerij[jj]);
|
|
|
|
if (chem_flag) jelem = element[jj];
|
|
else jelem = 0;
|
|
|
|
double* ulist_r = ulist_r_ij[jj];
|
|
double* ulist_i = ulist_i_ij[jj];
|
|
|
|
for (int j = 0; j <= twojmax; j++) {
|
|
int jju = idxu_block[j];
|
|
for (int mb = 0; mb <= j; mb++)
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
ulisttot_r[jelem*idxu_max+jju] +=
|
|
sfac * ulist_r[jju];
|
|
ulisttot_i[jelem*idxu_max+jju] +=
|
|
sfac * ulist_i[jju];
|
|
jju++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute Wigner U-functions for one neighbor
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_uarray(double x, double y, double z,
|
|
double z0, double r, int jj)
|
|
{
|
|
double r0inv;
|
|
double a_r, b_r, a_i, b_i;
|
|
double rootpq;
|
|
|
|
// compute Cayley-Klein parameters for unit quaternion
|
|
|
|
r0inv = 1.0 / sqrt(r * r + z0 * z0);
|
|
a_r = r0inv * z0;
|
|
a_i = -r0inv * z;
|
|
b_r = r0inv * y;
|
|
b_i = -r0inv * x;
|
|
|
|
// VMK Section 4.8.2
|
|
|
|
|
|
double* ulist_r = ulist_r_ij[jj];
|
|
double* ulist_i = ulist_i_ij[jj];
|
|
|
|
ulist_r[0] = 1.0;
|
|
ulist_i[0] = 0.0;
|
|
|
|
for (int j = 1; j <= twojmax; j++) {
|
|
int jju = idxu_block[j];
|
|
int jjup = idxu_block[j-1];
|
|
|
|
// fill in left side of matrix layer from previous layer
|
|
|
|
for (int mb = 0; 2*mb <= j; mb++) {
|
|
ulist_r[jju] = 0.0;
|
|
ulist_i[jju] = 0.0;
|
|
|
|
for (int ma = 0; ma < j; ma++) {
|
|
rootpq = rootpqarray[j - ma][j - mb];
|
|
ulist_r[jju] +=
|
|
rootpq *
|
|
(a_r * ulist_r[jjup] +
|
|
a_i * ulist_i[jjup]);
|
|
ulist_i[jju] +=
|
|
rootpq *
|
|
(a_r * ulist_i[jjup] -
|
|
a_i * ulist_r[jjup]);
|
|
|
|
rootpq = rootpqarray[ma + 1][j - mb];
|
|
ulist_r[jju+1] =
|
|
-rootpq *
|
|
(b_r * ulist_r[jjup] +
|
|
b_i * ulist_i[jjup]);
|
|
ulist_i[jju+1] =
|
|
-rootpq *
|
|
(b_r * ulist_i[jjup] -
|
|
b_i * ulist_r[jjup]);
|
|
jju++;
|
|
jjup++;
|
|
}
|
|
jju++;
|
|
}
|
|
|
|
// copy left side to right side with inversion symmetry VMK 4.4(2)
|
|
// u[ma-j][mb-j] = (-1)^(ma-mb)*Conj([u[ma][mb])
|
|
|
|
jju = idxu_block[j];
|
|
jjup = jju+(j+1)*(j+1)-1;
|
|
int mbpar = 1;
|
|
for (int mb = 0; 2*mb <= j; mb++) {
|
|
int mapar = mbpar;
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
if (mapar == 1) {
|
|
ulist_r[jjup] = ulist_r[jju];
|
|
ulist_i[jjup] = -ulist_i[jju];
|
|
} else {
|
|
ulist_r[jjup] = -ulist_r[jju];
|
|
ulist_i[jjup] = ulist_i[jju];
|
|
}
|
|
mapar = -mapar;
|
|
jju++;
|
|
jjup--;
|
|
}
|
|
mbpar = -mbpar;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
Compute derivatives of Wigner U-functions for one neighbor
|
|
see comments in compute_uarray()
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_duarray(double x, double y, double z,
|
|
double z0, double r, double dz0dr,
|
|
double wj, double rcut, int jj)
|
|
{
|
|
double r0inv;
|
|
double a_r, a_i, b_r, b_i;
|
|
double da_r[3], da_i[3], db_r[3], db_i[3];
|
|
double dz0[3], dr0inv[3], dr0invdr;
|
|
double rootpq;
|
|
|
|
double rinv = 1.0 / r;
|
|
double ux = x * rinv;
|
|
double uy = y * rinv;
|
|
double uz = z * rinv;
|
|
|
|
r0inv = 1.0 / sqrt(r * r + z0 * z0);
|
|
a_r = z0 * r0inv;
|
|
a_i = -z * r0inv;
|
|
b_r = y * r0inv;
|
|
b_i = -x * r0inv;
|
|
|
|
dr0invdr = -pow(r0inv, 3.0) * (r + z0 * dz0dr);
|
|
|
|
dr0inv[0] = dr0invdr * ux;
|
|
dr0inv[1] = dr0invdr * uy;
|
|
dr0inv[2] = dr0invdr * uz;
|
|
|
|
dz0[0] = dz0dr * ux;
|
|
dz0[1] = dz0dr * uy;
|
|
dz0[2] = dz0dr * uz;
|
|
|
|
for (int k = 0; k < 3; k++) {
|
|
da_r[k] = dz0[k] * r0inv + z0 * dr0inv[k];
|
|
da_i[k] = -z * dr0inv[k];
|
|
}
|
|
|
|
da_i[2] += -r0inv;
|
|
|
|
for (int k = 0; k < 3; k++) {
|
|
db_r[k] = y * dr0inv[k];
|
|
db_i[k] = -x * dr0inv[k];
|
|
}
|
|
|
|
db_i[0] += -r0inv;
|
|
db_r[1] += r0inv;
|
|
|
|
double* ulist_r = ulist_r_ij[jj];
|
|
double* ulist_i = ulist_i_ij[jj];
|
|
|
|
dulist_r[0][0] = 0.0;
|
|
dulist_r[0][1] = 0.0;
|
|
dulist_r[0][2] = 0.0;
|
|
dulist_i[0][0] = 0.0;
|
|
dulist_i[0][1] = 0.0;
|
|
dulist_i[0][2] = 0.0;
|
|
|
|
for (int j = 1; j <= twojmax; j++) {
|
|
int jju = idxu_block[j];
|
|
int jjup = idxu_block[j-1];
|
|
for (int mb = 0; 2*mb <= j; mb++) {
|
|
dulist_r[jju][0] = 0.0;
|
|
dulist_r[jju][1] = 0.0;
|
|
dulist_r[jju][2] = 0.0;
|
|
dulist_i[jju][0] = 0.0;
|
|
dulist_i[jju][1] = 0.0;
|
|
dulist_i[jju][2] = 0.0;
|
|
|
|
for (int ma = 0; ma < j; ma++) {
|
|
rootpq = rootpqarray[j - ma][j - mb];
|
|
for (int k = 0; k < 3; k++) {
|
|
dulist_r[jju][k] +=
|
|
rootpq * (da_r[k] * ulist_r[jjup] +
|
|
da_i[k] * ulist_i[jjup] +
|
|
a_r * dulist_r[jjup][k] +
|
|
a_i * dulist_i[jjup][k]);
|
|
dulist_i[jju][k] +=
|
|
rootpq * (da_r[k] * ulist_i[jjup] -
|
|
da_i[k] * ulist_r[jjup] +
|
|
a_r * dulist_i[jjup][k] -
|
|
a_i * dulist_r[jjup][k]);
|
|
}
|
|
|
|
rootpq = rootpqarray[ma + 1][j - mb];
|
|
for (int k = 0; k < 3; k++) {
|
|
dulist_r[jju+1][k] =
|
|
-rootpq * (db_r[k] * ulist_r[jjup] +
|
|
db_i[k] * ulist_i[jjup] +
|
|
b_r * dulist_r[jjup][k] +
|
|
b_i * dulist_i[jjup][k]);
|
|
dulist_i[jju+1][k] =
|
|
-rootpq * (db_r[k] * ulist_i[jjup] -
|
|
db_i[k] * ulist_r[jjup] +
|
|
b_r * dulist_i[jjup][k] -
|
|
b_i * dulist_r[jjup][k]);
|
|
}
|
|
jju++;
|
|
jjup++;
|
|
}
|
|
jju++;
|
|
}
|
|
|
|
// copy left side to right side with inversion symmetry VMK 4.4(2)
|
|
// u[ma-j][mb-j] = (-1)^(ma-mb)*Conj([u[ma][mb])
|
|
|
|
jju = idxu_block[j];
|
|
jjup = jju+(j+1)*(j+1)-1;
|
|
int mbpar = 1;
|
|
for (int mb = 0; 2*mb <= j; mb++) {
|
|
int mapar = mbpar;
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
if (mapar == 1) {
|
|
for (int k = 0; k < 3; k++) {
|
|
dulist_r[jjup][k] = dulist_r[jju][k];
|
|
dulist_i[jjup][k] = -dulist_i[jju][k];
|
|
}
|
|
} else {
|
|
for (int k = 0; k < 3; k++) {
|
|
dulist_r[jjup][k] = -dulist_r[jju][k];
|
|
dulist_i[jjup][k] = dulist_i[jju][k];
|
|
}
|
|
}
|
|
mapar = -mapar;
|
|
jju++;
|
|
jjup--;
|
|
}
|
|
mbpar = -mbpar;
|
|
}
|
|
}
|
|
|
|
double sfac = compute_sfac(r, rcut);
|
|
double dsfac = compute_dsfac(r, rcut);
|
|
|
|
if (switch_inner_flag) {
|
|
double sfac_inner = compute_sfac_inner(r, rinnerij[jj], drinnerij[jj]);
|
|
dsfac = dsfac*sfac_inner + sfac*compute_dsfac_inner(r, rinnerij[jj], drinnerij[jj]);
|
|
sfac *= sfac_inner;
|
|
}
|
|
|
|
sfac *= wj;
|
|
dsfac *= wj;
|
|
for (int j = 0; j <= twojmax; j++) {
|
|
int jju = idxu_block[j];
|
|
for (int mb = 0; 2*mb <= j; mb++)
|
|
for (int ma = 0; ma <= j; ma++) {
|
|
dulist_r[jju][0] = dsfac * ulist_r[jju] * ux +
|
|
sfac * dulist_r[jju][0];
|
|
dulist_i[jju][0] = dsfac * ulist_i[jju] * ux +
|
|
sfac * dulist_i[jju][0];
|
|
dulist_r[jju][1] = dsfac * ulist_r[jju] * uy +
|
|
sfac * dulist_r[jju][1];
|
|
dulist_i[jju][1] = dsfac * ulist_i[jju] * uy +
|
|
sfac * dulist_i[jju][1];
|
|
dulist_r[jju][2] = dsfac * ulist_r[jju] * uz +
|
|
sfac * dulist_r[jju][2];
|
|
dulist_i[jju][2] = dsfac * ulist_i[jju] * uz +
|
|
sfac * dulist_i[jju][2];
|
|
jju++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
memory usage of arrays
|
|
------------------------------------------------------------------------- */
|
|
|
|
double SNA::memory_usage()
|
|
{
|
|
int jdimpq = twojmax + 2;
|
|
int jdim = twojmax + 1;
|
|
double bytes;
|
|
|
|
bytes = 0;
|
|
|
|
bytes += (double)jdimpq*jdimpq * sizeof(double); // pqarray
|
|
bytes += (double)idxcg_max * sizeof(double); // cglist
|
|
|
|
bytes += (double)nmax * idxu_max * sizeof(double) * 2; // ulist_ij
|
|
bytes += (double)idxu_max * nelements * sizeof(double) * 2; // ulisttot
|
|
bytes += (double)idxu_max * 3 * sizeof(double) * 2; // dulist
|
|
|
|
bytes += (double)idxz_max * ndoubles * sizeof(double) * 2; // zlist
|
|
bytes += (double)idxb_max * ntriples * sizeof(double); // blist
|
|
bytes += (double)idxb_max * ntriples * 3 * sizeof(double); // dblist
|
|
bytes += (double)idxu_max * nelements * sizeof(double) * 2; // ylist
|
|
|
|
bytes += (double)jdim * jdim * jdim * sizeof(int); // idxcg_block
|
|
bytes += (double)jdim * sizeof(int); // idxu_block
|
|
bytes += (double)jdim * jdim * jdim * sizeof(int); // idxz_block
|
|
bytes += (double)jdim * jdim * jdim * sizeof(int); // idxb_block
|
|
|
|
bytes += (double)idxz_max * sizeof(SNA_ZINDICES); // idxz
|
|
bytes += (double)idxb_max * sizeof(SNA_BINDICES); // idxb
|
|
|
|
if (bzero_flag)
|
|
bytes += (double)jdim * sizeof(double); // bzero
|
|
|
|
bytes += (double)nmax * 3 * sizeof(double); // rij
|
|
bytes += (double)nmax * sizeof(int); // inside
|
|
bytes += (double)nmax * sizeof(double); // wj
|
|
bytes += (double)nmax * sizeof(double); // rcutij
|
|
if (switch_inner_flag) {
|
|
bytes += (double)nmax * sizeof(double); // rinnerij
|
|
bytes += (double)nmax * sizeof(double); // drinnerij
|
|
}
|
|
if (chem_flag) bytes += (double)nmax * sizeof(int); // element
|
|
|
|
return bytes;
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void SNA::create_twojmax_arrays()
|
|
{
|
|
int jdimpq = twojmax + 2;
|
|
memory->create(rootpqarray, jdimpq, jdimpq,
|
|
"sna:rootpqarray");
|
|
memory->create(cglist, idxcg_max, "sna:cglist");
|
|
memory->create(ulisttot_r, idxu_max*nelements, "sna:ulisttot");
|
|
memory->create(ulisttot_i, idxu_max*nelements, "sna:ulisttot");
|
|
memory->create(dulist_r, idxu_max, 3, "sna:dulist");
|
|
memory->create(dulist_i, idxu_max, 3, "sna:dulist");
|
|
memory->create(zlist_r, idxz_max*ndoubles, "sna:zlist");
|
|
memory->create(zlist_i, idxz_max*ndoubles, "sna:zlist");
|
|
memory->create(blist, idxb_max*ntriples, "sna:blist");
|
|
memory->create(dblist, idxb_max*ntriples, 3, "sna:dblist");
|
|
memory->create(ylist_r, idxu_max*nelements, "sna:ylist");
|
|
memory->create(ylist_i, idxu_max*nelements, "sna:ylist");
|
|
|
|
if (bzero_flag)
|
|
memory->create(bzero, twojmax+1,"sna:bzero");
|
|
else
|
|
bzero = nullptr;
|
|
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void SNA::destroy_twojmax_arrays()
|
|
{
|
|
memory->destroy(rootpqarray);
|
|
memory->destroy(cglist);
|
|
memory->destroy(ulisttot_r);
|
|
memory->destroy(ulisttot_i);
|
|
memory->destroy(dulist_r);
|
|
memory->destroy(dulist_i);
|
|
memory->destroy(zlist_r);
|
|
memory->destroy(zlist_i);
|
|
memory->destroy(blist);
|
|
memory->destroy(dblist);
|
|
memory->destroy(ylist_r);
|
|
memory->destroy(ylist_i);
|
|
|
|
memory->destroy(idxcg_block);
|
|
memory->destroy(idxu_block);
|
|
memory->destroy(idxz_block);
|
|
memory->destroy(idxb_block);
|
|
|
|
if (bzero_flag)
|
|
memory->destroy(bzero);
|
|
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
the function delta given by VMK Eq. 8.2(1)
|
|
------------------------------------------------------------------------- */
|
|
|
|
double SNA::deltacg(int j1, int j2, int j)
|
|
{
|
|
double sfaccg = factorial((j1 + j2 + j) / 2 + 1);
|
|
return sqrt(factorial((j1 + j2 - j) / 2) *
|
|
factorial((j1 - j2 + j) / 2) *
|
|
factorial((-j1 + j2 + j) / 2) / sfaccg);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
assign Clebsch-Gordan coefficients using
|
|
the quasi-binomial formula VMK 8.2.1(3)
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::init_clebsch_gordan()
|
|
{
|
|
double sum,dcg,sfaccg;
|
|
int m, aa2, bb2, cc2;
|
|
int ifac;
|
|
|
|
int idxcg_count = 0;
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
for (int j = j1 - j2; j <= MIN(twojmax, j1 + j2); j += 2) {
|
|
for (int m1 = 0; m1 <= j1; m1++) {
|
|
aa2 = 2 * m1 - j1;
|
|
|
|
for (int m2 = 0; m2 <= j2; m2++) {
|
|
|
|
// -c <= cc <= c
|
|
|
|
bb2 = 2 * m2 - j2;
|
|
m = (aa2 + bb2 + j) / 2;
|
|
|
|
if (m < 0 || m > j) {
|
|
cglist[idxcg_count] = 0.0;
|
|
idxcg_count++;
|
|
continue;
|
|
}
|
|
|
|
sum = 0.0;
|
|
|
|
for (int z = MAX(0, MAX(-(j - j2 + aa2)
|
|
/ 2, -(j - j1 - bb2) / 2));
|
|
z <= MIN((j1 + j2 - j) / 2,
|
|
MIN((j1 - aa2) / 2, (j2 + bb2) / 2));
|
|
z++) {
|
|
ifac = z % 2 ? -1 : 1;
|
|
sum += ifac /
|
|
(factorial(z) *
|
|
factorial((j1 + j2 - j) / 2 - z) *
|
|
factorial((j1 - aa2) / 2 - z) *
|
|
factorial((j2 + bb2) / 2 - z) *
|
|
factorial((j - j2 + aa2) / 2 + z) *
|
|
factorial((j - j1 - bb2) / 2 + z));
|
|
}
|
|
|
|
cc2 = 2 * m - j;
|
|
dcg = deltacg(j1, j2, j);
|
|
sfaccg = sqrt(factorial((j1 + aa2) / 2) *
|
|
factorial((j1 - aa2) / 2) *
|
|
factorial((j2 + bb2) / 2) *
|
|
factorial((j2 - bb2) / 2) *
|
|
factorial((j + cc2) / 2) *
|
|
factorial((j - cc2) / 2) *
|
|
(j + 1));
|
|
|
|
cglist[idxcg_count] = sum * dcg * sfaccg;
|
|
idxcg_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
print out values of Clebsch-Gordan coefficients
|
|
format and notation follows VMK Table 8.11
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::print_clebsch_gordan()
|
|
{
|
|
if (comm->me) return;
|
|
|
|
int aa2, bb2, cc2;
|
|
for (int j = 0; j <= twojmax; j += 1) {
|
|
printf("c = %g\n",j/2.0);
|
|
printf("a alpha b beta C_{a alpha b beta}^{c alpha+beta}\n");
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
if (j1-j2 <= j && j1+j2 >= j && (j1+j2+j)%2 == 0) {
|
|
int idxcg_count = idxcg_block[j1][j2][j];
|
|
for (int m1 = 0; m1 <= j1; m1++) {
|
|
aa2 = 2*m1-j1;
|
|
for (int m2 = 0; m2 <= j2; m2++) {
|
|
bb2 = 2*m2-j2;
|
|
double cgtmp = cglist[idxcg_count];
|
|
cc2 = aa2+bb2;
|
|
if (cc2 >= -j && cc2 <= j)
|
|
if (j1 != j2 || (aa2 > bb2 && aa2 >= -bb2) || (aa2 == bb2 && aa2 >= 0))
|
|
printf("%4g %4g %4g %4g %10.6g\n",
|
|
j1/2.0,aa2/2.0,j2/2.0,bb2/2.0,cgtmp);
|
|
idxcg_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
pre-compute table of sqrt[p/m2], p, q = 1,twojmax
|
|
the p = 0, q = 0 entries are allocated and skipped for convenience.
|
|
------------------------------------------------------------------------- */
|
|
|
|
void SNA::init_rootpqarray()
|
|
{
|
|
for (int p = 1; p <= twojmax; p++)
|
|
for (int q = 1; q <= twojmax; q++)
|
|
rootpqarray[p][q] = sqrt(static_cast<double>(p)/q);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void SNA::compute_ncoeff()
|
|
{
|
|
int ncount;
|
|
|
|
ncount = 0;
|
|
|
|
for (int j1 = 0; j1 <= twojmax; j1++)
|
|
for (int j2 = 0; j2 <= j1; j2++)
|
|
for (int j = j1 - j2;
|
|
j <= MIN(twojmax, j1 + j2); j += 2)
|
|
if (j >= j1) ncount++;
|
|
|
|
ndoubles = nelements*nelements;
|
|
ntriples = nelements*nelements*nelements;
|
|
if (chem_flag)
|
|
ncoeff = ncount*ntriples;
|
|
else
|
|
ncoeff = ncount;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
double SNA::compute_sfac(double r, double rcut)
|
|
{
|
|
double sfac;
|
|
if (switch_flag == 0) sfac = 1.0;
|
|
else if (r <= rmin0) sfac = 1.0;
|
|
else if (r > rcut) sfac = 0.0;
|
|
else {
|
|
double rcutfac = MY_PI / (rcut - rmin0);
|
|
sfac = 0.5 * (cos((r - rmin0) * rcutfac) + 1.0);
|
|
}
|
|
return sfac;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
double SNA::compute_dsfac(double r, double rcut)
|
|
{
|
|
double dsfac;
|
|
if (switch_flag == 0) dsfac = 0.0;
|
|
else if (r <= rmin0) dsfac = 0.0;
|
|
else if (r > rcut) dsfac = 0.0;
|
|
else {
|
|
double rcutfac = MY_PI / (rcut - rmin0);
|
|
dsfac = -0.5 * sin((r - rmin0) * rcutfac) * rcutfac;
|
|
}
|
|
return dsfac;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
double SNA::compute_sfac_inner(double r, double rinner, double drinner)
|
|
{
|
|
double sfac;
|
|
if (switch_inner_flag == 0) sfac = 1.0;
|
|
else if (r >= rinner + drinner) sfac = 1.0;
|
|
else if (r <= rinner) sfac = 0.0;
|
|
else {
|
|
double rcutfac = MY_PI / drinner;
|
|
sfac = 0.5 * (1.0 - cos((r - rinner) * rcutfac));
|
|
}
|
|
return sfac;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
double SNA::compute_dsfac_inner(double r, double rinner, double drinner)
|
|
{
|
|
double dsfac;
|
|
if (switch_inner_flag == 0) dsfac = 0.0;
|
|
else if (r >= rinner + drinner) dsfac = 0.0;
|
|
else if (r <= rinner) dsfac = 0.0;
|
|
else {
|
|
double rcutfac = MY_PI / drinner;
|
|
dsfac = 0.5 * sin((r - rinner) * rcutfac) * rcutfac;
|
|
}
|
|
return dsfac;
|
|
}
|
|
|