Switched the sign of spherical harmonics for m odd
This commit is contained in:
@ -48,14 +48,17 @@ For each atom, :math:`Q_l` is a real number defined as follows:
|
|||||||
\bar{Y}_{lm} = & \frac{1}{nnn}\sum_{j = 1}^{nnn} Y_{lm}( \theta( {\bf r}_{ij} ), \phi( {\bf r}_{ij} ) ) \\
|
\bar{Y}_{lm} = & \frac{1}{nnn}\sum_{j = 1}^{nnn} Y_{lm}( \theta( {\bf r}_{ij} ), \phi( {\bf r}_{ij} ) ) \\
|
||||||
Q_l = & \sqrt{\frac{4 \pi}{2 l + 1} \sum_{m = -l}^{m = l} \bar{Y}_{lm} \bar{Y}^*_{lm}}
|
Q_l = & \sqrt{\frac{4 \pi}{2 l + 1} \sum_{m = -l}^{m = l} \bar{Y}_{lm} \bar{Y}^*_{lm}}
|
||||||
|
|
||||||
The first equation defines the spherical harmonic order parameters.
|
The first equation defines the local order parameters as averages
|
||||||
|
of the spherical harmonics :math:`Y_{lm}` for each neighbor.
|
||||||
These are complex number components of the 3D analog of the 2D order
|
These are complex number components of the 3D analog of the 2D order
|
||||||
parameter :math:`q_n`, which is implemented as LAMMPS compute
|
parameter :math:`q_n`, which is implemented as LAMMPS compute
|
||||||
:doc:`hexorder/atom <compute_hexorder_atom>`.
|
:doc:`hexorder/atom <compute_hexorder_atom>`.
|
||||||
The summation is over the *nnn* nearest
|
The summation is over the *nnn* nearest
|
||||||
neighbors of the central atom.
|
neighbors of the central atom.
|
||||||
The angles theta and phi are the standard spherical polar angles
|
The angles :math:`theta` and :math:`phi` are the standard spherical polar angles
|
||||||
defining the direction of the bond vector :math:`r_{ij}`.
|
defining the direction of the bond vector :math:`r_{ij}`.
|
||||||
|
The phase and sign of :math:`Y_{lm}` follow the standard conventions,
|
||||||
|
so that :math:`{\rm sign}(Y_{ll}(0,0)) = (-1)^l`.
|
||||||
The second equation defines :math:`Q_l`, which is a
|
The second equation defines :math:`Q_l`, which is a
|
||||||
rotationally invariant non-negative amplitude obtained by summing
|
rotationally invariant non-negative amplitude obtained by summing
|
||||||
over all the components of degree *l*\ .
|
over all the components of degree *l*\ .
|
||||||
@ -98,8 +101,8 @@ structures are given in Table I of :ref:`Steinhardt <Steinhardt>`, and these
|
|||||||
can be reproduced with this keyword.
|
can be reproduced with this keyword.
|
||||||
|
|
||||||
The optional keyword *components* will output the components of the
|
The optional keyword *components* will output the components of the
|
||||||
normalized complex vector :math:`\bar{Y}_{lm}` of degree *ldegree*\ , which must be
|
*normalized* complex vector :math:`\hat{Y}_{lm} = \bar{Y}_{lm}/|\bar{Y}_{lm}|` of degree *ldegree*\,
|
||||||
explicitly included in the keyword *degrees*\ . This option can be used
|
which must be included in the list of order parameters to be computed. This option can be used
|
||||||
in conjunction with :doc:`compute coord_atom <compute_coord_atom>` to
|
in conjunction with :doc:`compute coord_atom <compute_coord_atom>` to
|
||||||
calculate the ten Wolde's criterion to identify crystal-like
|
calculate the ten Wolde's criterion to identify crystal-like
|
||||||
particles, as discussed in :ref:`ten Wolde <tenWolde2>`.
|
particles, as discussed in :ref:`ten Wolde <tenWolde2>`.
|
||||||
@ -141,11 +144,15 @@ If the keyword *wl/hat* is set to yes, then the :math:`\hat{W}_l`
|
|||||||
values for each atom will be added to the output array, which are real numbers.
|
values for each atom will be added to the output array, which are real numbers.
|
||||||
|
|
||||||
If the keyword *components* is set, then the real and imaginary parts
|
If the keyword *components* is set, then the real and imaginary parts
|
||||||
of each component of (normalized) :math:`\bar{Y}_{lm}` will be added to the
|
of each component of *normalized* :math:`\hat{Y}_{lm}` will be added to the
|
||||||
output array in the following order: :math:`Re(\bar{Y}_{-m}) Im(\bar{Y}_{-m})
|
output array in the following order: :math:`{\rm Re}(\hat{Y}_{-m}), {\rm Im}(\hat{Y}_{-m}),
|
||||||
Re(\bar{Y}_{-m+1}) Im(\bar{Y}_{-m+1}) ... Re(\bar{Y}_m) Im(\bar{Y}_m)`. This
|
{\rm Re}(\hat{Y}_{-m+1}), {\rm Im}(\hat{Y}_{-m+1}), \dots , {\rm Re}(\hat{Y}_m), {\rm Im}(\hat{Y}_m)`.
|
||||||
way, the per-atom array will have a total of *nlvalues*\ +2\*(2\ *l*\ +1)
|
|
||||||
columns.
|
In summary, the per-atom array will contain *nlvalues* columns, followed by
|
||||||
|
an additional *nlvalues* columns if *wl* is set to yes, followed by
|
||||||
|
an additional *nlvalues* columns if *wl/hat* is set to yes, followed
|
||||||
|
by an additional 2\*(2\* *ldegree*\ +1) columns if the *components*
|
||||||
|
keyword is set.
|
||||||
|
|
||||||
These values can be accessed by any command that uses per-atom values
|
These values can be accessed by any command that uses per-atom values
|
||||||
from a compute as input. See the :doc:`Howto output <Howto_output>` doc
|
from a compute as input. See the :doc:`Howto output <Howto_output>` doc
|
||||||
|
|||||||
@ -456,21 +456,26 @@ void ComputeOrientOrderAtom::calc_boop(double **rlist,
|
|||||||
for (int il = 0; il < nqlist; il++) {
|
for (int il = 0; il < nqlist; il++) {
|
||||||
int l = qlist[il];
|
int l = qlist[il];
|
||||||
|
|
||||||
|
// calculate spherical harmonics
|
||||||
|
// Ylm, -l <= m <= l
|
||||||
|
// sign convention: sign(Yll(0,0)) = (-1)^l
|
||||||
|
|
||||||
qnm_r[il][l] += polar_prefactor(l, 0, costheta);
|
qnm_r[il][l] += polar_prefactor(l, 0, costheta);
|
||||||
double expphim_r = expphi_r;
|
double expphim_r = expphi_r;
|
||||||
double expphim_i = expphi_i;
|
double expphim_i = expphi_i;
|
||||||
for(int m = 1; m <= +l; m++) {
|
for(int m = 1; m <= +l; m++) {
|
||||||
|
|
||||||
double prefactor = polar_prefactor(l, m, costheta);
|
double prefactor = polar_prefactor(l, m, costheta);
|
||||||
double c_r = prefactor * expphim_r;
|
double ylm_r = prefactor * expphim_r;
|
||||||
double c_i = prefactor * expphim_i;
|
double ylm_i = prefactor * expphim_i;
|
||||||
qnm_r[il][m+l] += c_r;
|
qnm_r[il][m+l] += ylm_r;
|
||||||
qnm_i[il][m+l] += c_i;
|
qnm_i[il][m+l] += ylm_i;
|
||||||
if(m & 1) {
|
if(m & 1) {
|
||||||
qnm_r[il][-m+l] -= c_r;
|
qnm_r[il][-m+l] -= ylm_r;
|
||||||
qnm_i[il][-m+l] += c_i;
|
qnm_i[il][-m+l] += ylm_i;
|
||||||
} else {
|
} else {
|
||||||
qnm_r[il][-m+l] += c_r;
|
qnm_r[il][-m+l] += ylm_r;
|
||||||
qnm_i[il][-m+l] -= c_i;
|
qnm_i[il][-m+l] -= ylm_i;
|
||||||
}
|
}
|
||||||
double tmp_r = expphim_r*expphi_r - expphim_i*expphi_i;
|
double tmp_r = expphim_r*expphi_r - expphim_i*expphi_i;
|
||||||
double tmp_i = expphim_r*expphi_i + expphim_i*expphi_r;
|
double tmp_i = expphim_r*expphi_i + expphim_i*expphi_r;
|
||||||
@ -505,19 +510,6 @@ void ComputeOrientOrderAtom::calc_boop(double **rlist,
|
|||||||
qn[jj++] = qnormfac * sqrt(qm_sum);
|
qn[jj++] = qnormfac * sqrt(qm_sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// 1. [done]Need to allocate extra memory in qnarray[] for this option
|
|
||||||
// 2. [done]Need to add keyword option
|
|
||||||
// 3. [done]Need to calculate Clebsch-Gordan/Wigner 3j coefficients
|
|
||||||
// (Can try getting them from boop.py first)
|
|
||||||
// 5. [done]Compare to bcc values in /Users/athomps/netapp/codes/MatMiner/matminer/matminer/featurizers/boop.py
|
|
||||||
// 6. [done]I get the right answer for W_l, but need to make sure that factor of 1/sqrt(l+1) is right for cglist
|
|
||||||
// 7. Add documentation
|
|
||||||
// 8. [done] run valgrind
|
|
||||||
// 9. [done] Add Wlhat
|
|
||||||
// 10. Update memory_usage()
|
|
||||||
// 11. Add exact FCC values for W_4, W_4_hat
|
|
||||||
|
|
||||||
// calculate W_l
|
// calculate W_l
|
||||||
|
|
||||||
if (wlflag) {
|
if (wlflag) {
|
||||||
@ -554,7 +546,6 @@ void ComputeOrientOrderAtom::calc_boop(double **rlist,
|
|||||||
idxcg_count++;
|
idxcg_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Whats = [w/(q/np.sqrt(np.pi * 4 / (2 * l + 1)))**3 if abs(q) > 1.0e-6 else 0.0 for l,q,w in zip(range(1,max_l+1),Qs,Ws)]
|
|
||||||
if (qn[il] < QEPSILON)
|
if (qn[il] < QEPSILON)
|
||||||
qn[jj++] = 0.0;
|
qn[jj++] = 0.0;
|
||||||
else {
|
else {
|
||||||
@ -565,7 +556,7 @@ void ComputeOrientOrderAtom::calc_boop(double **rlist,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate components of Q_l, for l=qlcomp
|
// Calculate components of Q_l/|Q_l|, for l=qlcomp
|
||||||
|
|
||||||
if (qlcompflag) {
|
if (qlcompflag) {
|
||||||
int il = iqlcomp;
|
int il = iqlcomp;
|
||||||
@ -619,6 +610,7 @@ double ComputeOrientOrderAtom::polar_prefactor(int l, int m, double costheta)
|
|||||||
|
|
||||||
/* ----------------------------------------------------------------------
|
/* ----------------------------------------------------------------------
|
||||||
associated legendre polynomial
|
associated legendre polynomial
|
||||||
|
sign convention: P(l,l) = (2l-1)!!(-sqrt(1-x^2))^l
|
||||||
------------------------------------------------------------------------- */
|
------------------------------------------------------------------------- */
|
||||||
|
|
||||||
double ComputeOrientOrderAtom::associated_legendre(int l, int m, double x)
|
double ComputeOrientOrderAtom::associated_legendre(int l, int m, double x)
|
||||||
@ -628,9 +620,9 @@ double ComputeOrientOrderAtom::associated_legendre(int l, int m, double x)
|
|||||||
double p(1.0), pm1(0.0), pm2(0.0);
|
double p(1.0), pm1(0.0), pm2(0.0);
|
||||||
|
|
||||||
if (m != 0) {
|
if (m != 0) {
|
||||||
const double sqx = sqrt(1.0-x*x);
|
const double msqx = -sqrt(1.0-x*x);
|
||||||
for (int i=1; i < m+1; ++i)
|
for (int i=1; i < m+1; ++i)
|
||||||
p *= static_cast<double>(2*i-1) * sqx;
|
p *= static_cast<double>(2*i-1) * msqx;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=m+1; i < l+1; ++i) {
|
for (int i=m+1; i < l+1; ++i) {
|
||||||
|
|||||||
Reference in New Issue
Block a user