/* * Performant implementation of atomic cluster expansion and interface to LAMMPS * * Copyright 2021 (c) Yury Lysogorskiy^1, Cas van der Oord^2, Anton Bochkarev^1, * Sarath Menon^1, Matteo Rinaldi^1, Thomas Hammerschmidt^1, Matous Mrovec^1, * Aidan Thompson^3, Gabor Csanyi^2, Christoph Ortner^4, Ralf Drautz^1 * * ^1: Ruhr-University Bochum, Bochum, Germany * ^2: University of Cambridge, Cambridge, United Kingdom * ^3: Sandia National Laboratories, Albuquerque, New Mexico, USA * ^4: University of British Columbia, Vancouver, BC, Canada * * * See the LICENSE file. * This FILENAME is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * 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 * along with this program. If not, see . */ // Created by Ralf Drautz, Yury Lysogorskiy #include #include "ace_spherical_cart.h" ACECartesianSphericalHarmonics::ACECartesianSphericalHarmonics(LS_TYPE lm) { init(lm); } void ACECartesianSphericalHarmonics::init(LS_TYPE lm) { lmax = lm; alm.init(lmax, "alm"); blm.init(lmax, "blm"); cl.init(lmax + 1); dl.init(lmax + 1); plm.init(lmax, "plm"); dplm.init(lmax, "dplm"); ylm.init(lmax, "ylm"); dylm.init(lmax, "dylm"); pre_compute(); } /** Destructor for ACECartesianSphericalHarmonics. @param None @returns None */ ACECartesianSphericalHarmonics::~ACECartesianSphericalHarmonics() {} void ACECartesianSphericalHarmonics::pre_compute() { DOUBLE_TYPE a, b; DOUBLE_TYPE lsq, ld, l1, l2; DOUBLE_TYPE msq; for (LS_TYPE l = 1; l <= lmax; l++) { lsq = l * l; ld = 2 * l; l1 = (4 * lsq - 1); l2 = lsq - ld + 1; for (MS_TYPE m = 0; m < l - 1; m++) { msq = m * m; a = sqrt((DOUBLE_TYPE(l1)) / (DOUBLE_TYPE(lsq - msq))); b = -sqrt((DOUBLE_TYPE(l2 - msq)) / (DOUBLE_TYPE(4 * l2 - 1))); alm(l, m) = a; blm(l, m) = b; } } for (LS_TYPE l = 1; l <= lmax; l++) { cl(l) = -sqrt(1.0 + 0.5 / (DOUBLE_TYPE(l))); dl(l) = sqrt(DOUBLE_TYPE(2 * (l - 1) + 3)); } } void ACECartesianSphericalHarmonics::compute_barplm(DOUBLE_TYPE rz, LS_TYPE lmaxi) { // requires -1 <= rz <= 1 , NO CHECKING IS PERFORMED !!!!!!!!! // prefactors include 1/sqrt(2) factor compared to reference DOUBLE_TYPE t; // l=0, m=0 //plm(0, 0) = Y00/sq1o4pi; //= sq1o4pi; plm(0, 0) = Y00; //= 1; dplm(0, 0) = 0.0; if (lmaxi > 0) { // l=1, m=0 plm(1, 0) = Y00 * sq3 * rz; dplm(1, 0) = Y00 * sq3; // l=1, m=1 plm(1, 1) = -sq3o2 * Y00; dplm(1, 1) = 0.0; // loop l = 2, lmax for (LS_TYPE l = 2; l <= lmaxi; l++) { for (MS_TYPE m = 0; m < l - 1; m++) { plm(l, m) = alm(l, m) * (rz * plm(l - 1, m) + blm(l, m) * plm(l - 2, m)); dplm(l, m) = alm(l, m) * (plm(l - 1, m) + rz * dplm(l - 1, m) + blm(l, m) * dplm(l - 2, m)); } t = dl(l) * plm(l - 1, l - 1); plm(l, l - 1) = t * rz; dplm(l, l - 1) = t; plm(l, l) = cl(l) * plm(l - 1, l - 1); dplm(l, l) = 0.0; } } } //end compute_barplm void ACECartesianSphericalHarmonics::compute_ylm(DOUBLE_TYPE rx, DOUBLE_TYPE ry, DOUBLE_TYPE rz, LS_TYPE lmaxi) { // requires rx^2 + ry^2 + rz^2 = 1 , NO CHECKING IS PERFORMED !!!!!!!!! DOUBLE_TYPE real; DOUBLE_TYPE img; MS_TYPE m; ACEComplex phase; ACEComplex phasem, mphasem1; ACEComplex dyx, dyy, dyz; ACEComplex rdy; phase.real = rx; phase.img = ry; //compute barplm compute_barplm(rz, lmaxi); //m = 0 m = 0; for (LS_TYPE l = 0; l <= lmaxi; l++) { ylm(l, m).real = plm(l, m); ylm(l, m).img = 0.0; dyz.real = dplm(l, m); rdy.real = dyz.real * rz; dylm(l, m).a[0].real = -rdy.real * rx; dylm(l, m).a[0].img = 0.0; dylm(l, m).a[1].real = -rdy.real * ry; dylm(l, m).a[1].img = 0.0; dylm(l, m).a[2].real = dyz.real - rdy.real * rz; dylm(l, m).a[2].img = 0; } //m = 0 m = 1; for (LS_TYPE l = 1; l <= lmaxi; l++) { ylm(l, m) = phase * plm(l, m); // std::cout << "Re ylm(" << l << "," << m <<")= " << ylm(l, m).real << std::endl; // std::cout << "Im ylm(" << l << "," << m <<")= " << ylm(l, m).img << std::endl; dyx.real = plm(l, m); dyx.img = 0.0; dyy.real = 0.0; dyy.img = plm(l, m); dyz.real = phase.real * dplm(l, m); dyz.img = phase.img * dplm(l, m); rdy.real = rx * dyx.real + +rz * dyz.real; rdy.img = ry * dyy.img + rz * dyz.img; dylm(l, m).a[0].real = dyx.real - rdy.real * rx; dylm(l, m).a[0].img = -rdy.img * rx; dylm(l, m).a[1].real = -rdy.real * ry; dylm(l, m).a[1].img = dyy.img - rdy.img * ry; dylm(l, m).a[2].real = dyz.real - rdy.real * rz; dylm(l, m).a[2].img = dyz.img - rdy.img * rz; } // m > 1 phasem = phase; for (MS_TYPE m = 2; m <= lmaxi; m++) { mphasem1.real = phasem.real * DOUBLE_TYPE(m); mphasem1.img = phasem.img * DOUBLE_TYPE(m); phasem = phasem * phase; for (LS_TYPE l = m; l <= lmaxi; l++) { ylm(l, m).real = phasem.real * plm(l, m); ylm(l, m).img = phasem.img * plm(l, m); dyx = mphasem1 * plm(l, m); dyy.real = -dyx.img; dyy.img = dyx.real; dyz = phasem * dplm(l, m); rdy.real = rx * dyx.real + ry * dyy.real + rz * dyz.real; rdy.img = rx * dyx.img + ry * dyy.img + rz * dyz.img; dylm(l, m).a[0].real = dyx.real - rdy.real * rx; dylm(l, m).a[0].img = dyx.img - rdy.img * rx; dylm(l, m).a[1].real = dyy.real - rdy.real * ry; dylm(l, m).a[1].img = dyy.img - rdy.img * ry; dylm(l, m).a[2].real = dyz.real - rdy.real * rz; dylm(l, m).a[2].img = dyz.img - rdy.img * rz; } } }