LCOV - code coverage report
Current view: top level - src/grpp - grpp_spherical_harmonics.c (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:b4bd748) Lines: 97 128 75.8 %
Date: 2025-03-09 07:56:22 Functions: 6 7 85.7 %

          Line data    Source code
       1             : /*----------------------------------------------------------------------------*/
       2             : /*  CP2K: A general program to perform molecular dynamics simulations         */
       3             : /*  Copyright 2000-2025 CP2K developers group <https://cp2k.org>              */
       4             : /*                                                                            */
       5             : /*  SPDX-License-Identifier: MIT                                              */
       6             : /*----------------------------------------------------------------------------*/
       7             : 
       8             : /*
       9             :  *  libgrpp - a library for the evaluation of integrals over
      10             :  *            generalized relativistic pseudopotentials.
      11             :  *
      12             :  *  Copyright (C) 2021-2023 Alexander Oleynichenko
      13             :  */
      14             : 
      15             : /*
      16             :  * Constructs tables with the expansion coefficients of real spherical harmonics
      17             :  * in the basis of (cartesian) unitary spherical polynomials.
      18             :  *
      19             :  * For more details about the algorithm used, see:
      20             :  * R. Flores-Moreno et al, J. Comput. Chem. 27, 1009 (2006),
      21             :  * doi: 10.1002/jcc.20410
      22             :  */
      23             : 
      24             : #include <math.h>
      25             : #include <stdio.h>
      26             : #include <stdlib.h>
      27             : #include <string.h>
      28             : 
      29             : #ifndef M_PI
      30             : #define M_PI 3.1415926535897932384626433
      31             : #endif
      32             : 
      33             : #ifndef M_SQRT1_2
      34             : #define M_SQRT1_2 0.70710678118654752440
      35             : #endif
      36             : 
      37             : #include "grpp_binomial.h"
      38             : #include "grpp_factorial.h"
      39             : #include "grpp_spherical_harmonics.h"
      40             : #include "libgrpp.h"
      41             : /*
      42             :  * Tables with pretabulated expansion coefficients
      43             :  */
      44             : 
      45             : static rsh_coef_table_t **rsh_coef_tables = NULL;
      46             : static int rsh_tables_lmax = -1;
      47             : 
      48             : /*
      49             :  * Function pre-definitions
      50             :  */
      51             : rsh_coef_table_t *libgrpp_tabulate_real_spherical_harmonic_coeffs(int L);
      52             : 
      53             : static int *generate_cartesian_combinations(int L, int *num);
      54             : 
      55             : /**
      56             :  * Constructs the set of tables with C_{l,m}^{lx,ly,lz} coefficients
      57             :  * (up to maximum angular momentum Lmax).
      58             :  * (pretabulation step)
      59             :  */
      60          14 : void libgrpp_create_real_spherical_harmonic_coeffs_tables(int Lmax) {
      61          14 :   if (Lmax <= rsh_tables_lmax) {
      62             :     // nothing to do
      63             :   } else {
      64             :     // expand tables: realloc memory and add tables for the highest L values
      65          14 :     rsh_coef_tables = (rsh_coef_table_t **)realloc(
      66          14 :         rsh_coef_tables, (Lmax + 1) * sizeof(rsh_coef_table_t *));
      67             : 
      68         588 :     for (int L = rsh_tables_lmax + 1; L <= Lmax; L++) {
      69         574 :       rsh_coef_tables[L] = libgrpp_tabulate_real_spherical_harmonic_coeffs(L);
      70             :     }
      71          14 :     rsh_tables_lmax = Lmax;
      72             :   }
      73          14 : }
      74             : 
      75             : /**
      76             :  * Calculates all C_{l,m}^{lx,ly,lz} coefficients for the given angular momentum
      77             :  * L.
      78             :  */
      79         574 : rsh_coef_table_t *libgrpp_tabulate_real_spherical_harmonic_coeffs(int L) {
      80         574 :   int ncart = (L + 1) * (L + 2) / 2;
      81             : 
      82         574 :   rsh_coef_table_t *coef_table =
      83         574 :       (rsh_coef_table_t *)calloc(1, sizeof(rsh_coef_table_t));
      84         574 :   coef_table->n_cart_comb = ncart;
      85         574 :   coef_table->cartesian_comb = generate_cartesian_combinations(L, &ncart);
      86         574 :   coef_table->coeffs = (double *)calloc((2 * L + 1) * ncart, sizeof(double));
      87             : 
      88       24108 :   for (int m = -L; m <= L; m++) {
      89    10562748 :     for (int icomb = 0; icomb < ncart; icomb++) {
      90    10539214 :       int lx = coef_table->cartesian_comb[3 * icomb];
      91    10539214 :       int ly = coef_table->cartesian_comb[3 * icomb + 1];
      92             :       // int lz = coef_table->cartesian_comb[3 * icomb + 2];
      93    10539214 :       double u_lm_lx_ly_lz = libgrpp_spherical_to_cartesian_coef(L, m, lx, ly);
      94    10539214 :       int index = (m + L) * ncart + icomb;
      95    10539214 :       coef_table->coeffs[index] = u_lm_lx_ly_lz;
      96             :     }
      97             :   }
      98             : 
      99         574 :   return coef_table;
     100             : }
     101             : 
     102             : /**
     103             :  * Access to the table for the angular momentum value L.
     104             :  */
     105    50585262 : rsh_coef_table_t *libgrpp_get_real_spherical_harmonic_table(int L) {
     106    50585262 :   if (L > rsh_tables_lmax) {
     107           0 :     printf("get_real_spherical_harmonic_table(): %d > Lmax\n", L);
     108           0 :     return NULL;
     109             :   }
     110             : 
     111    50585262 :   return rsh_coef_tables[L];
     112             : }
     113             : 
     114             : /**
     115             :  * For the given real spherical harmonic (RSH) S_lm calculates the coefficient
     116             :  * C_{l,m}^{lx,ly,lz} before the unitary spherical polynomial (USP) in its
     117             :  * expansion.
     118             :  *
     119             :  * The formula is taken from:
     120             :  * R. Flores-Moreno et al, J. Comput. Chem. 27, 1009 (2006)
     121             :  * doi: 10.1002/jcc.20410
     122             :  * (formula 32)
     123             :  */
     124    10539214 : double libgrpp_spherical_to_cartesian_coef(int l, int m, int lx, int ly) {
     125    10539214 :   int j = lx + ly - abs(m);
     126    10539214 :   if (j % 2 != 0) {
     127             :     return 0.0;
     128             :   }
     129     5272694 :   j /= 2;
     130             : 
     131     5272694 :   if (!((m > 0 && (abs(m) - lx) % 2 == 0) || (m == 0 && lx % 2 == 0) ||
     132     2593080 :         (m < 0 && (abs(m) - lx) % 2 != 0))) {
     133             :     return 0.0;
     134             :   }
     135             : 
     136     2639434 :   double prefactor =
     137     2639434 :       sqrt((2 * l + 1) / (2 * M_PI) * libgrpp_factorial(l - abs(m)) /
     138     2639434 :            libgrpp_factorial(l + abs(m)));
     139     2639434 :   prefactor /= pow(2, l) * libgrpp_factorial(l);
     140             : 
     141     2639434 :   double u_lm_lx_ly_lz = 0.0;
     142    18795420 :   for (int i = j; i <= (l - abs(m)) / 2; i++) {
     143             :     // any term that implies the factorial of a negative number is neglected
     144    16155986 :     if (2 * l - 2 * i < 0) {
     145             :       u_lm_lx_ly_lz = 0.0;
     146             :       break;
     147             :     }
     148    16155986 :     if (l - abs(m) - 2 * i < 0) {
     149             :       u_lm_lx_ly_lz = 0.0;
     150             :       break;
     151             :     }
     152             : 
     153    32311972 :     double factor_1 =
     154    16155986 :         libgrpp_binomial(l, i) * libgrpp_binomial(i, j) * pow(-1, i) *
     155    16155986 :         libgrpp_factorial_ratio(2 * l - 2 * i, l - abs(m) - 2 * i);
     156             : 
     157    16155986 :     double sum = 0.0;
     158    66537394 :     for (int k = 0; k <= j; k++) {
     159    50381408 :       sum += libgrpp_binomial(j, k) * libgrpp_binomial(abs(m), lx - 2 * k) *
     160    50381408 :              pow(-1, (abs(m) - lx + 2 * k) / 2);
     161             :     }
     162             : 
     163    16155986 :     u_lm_lx_ly_lz += factor_1 * sum;
     164             :   }
     165             : 
     166     2639434 :   u_lm_lx_ly_lz *= prefactor;
     167     2639434 :   if (m == 0 && (lx % 2 == 0)) {
     168       46354 :     u_lm_lx_ly_lz *= M_SQRT1_2; // x 1/sqrt(2)
     169             :   }
     170             : 
     171             :   return u_lm_lx_ly_lz;
     172             : }
     173             : 
     174             : /**
     175             :  * Calculates value of the real spherical harmonic S_lm at the point k/|k| of
     176             :  * the unit sphere.
     177             :  */
     178           0 : double libgrpp_evaluate_real_spherical_harmonic(const int l, const int m,
     179             :                                                 const double *k) {
     180           0 :   double unitary_kx;
     181           0 :   double unitary_ky;
     182           0 :   double unitary_kz;
     183           0 :   double kx_powers[200];
     184           0 :   double ky_powers[200];
     185           0 :   double kz_powers[200];
     186             : 
     187           0 :   rsh_coef_table_t *rsh_coef_l = libgrpp_get_real_spherical_harmonic_table(l);
     188             : 
     189           0 :   double length_k = sqrt(k[0] * k[0] + k[1] * k[1] + k[2] * k[2]);
     190             : 
     191           0 :   if (length_k > LIBGRPP_ZERO_THRESH) {
     192           0 :     unitary_kx = k[0] / length_k;
     193           0 :     unitary_ky = k[1] / length_k;
     194           0 :     unitary_kz = k[2] / length_k;
     195             :   } else {
     196             :     unitary_kx = 0.0;
     197             :     unitary_ky = 0.0;
     198             :     unitary_kz = 0.0;
     199             :   }
     200             : 
     201           0 :   kx_powers[0] = 1.0;
     202           0 :   ky_powers[0] = 1.0;
     203           0 :   kz_powers[0] = 1.0;
     204             : 
     205           0 :   for (int i = 1; i <= l; i++) {
     206           0 :     kx_powers[i] = kx_powers[i - 1] * unitary_kx;
     207           0 :     ky_powers[i] = ky_powers[i - 1] * unitary_ky;
     208           0 :     kz_powers[i] = kz_powers[i - 1] * unitary_kz;
     209             :   }
     210             : 
     211           0 :   double value = 0.0;
     212             : 
     213           0 :   int ncart = rsh_coef_l->n_cart_comb;
     214           0 :   for (int icomb = 0; icomb < ncart; icomb++) {
     215           0 :     int r = rsh_coef_l->cartesian_comb[3 * icomb];
     216           0 :     int s = rsh_coef_l->cartesian_comb[3 * icomb + 1];
     217           0 :     int t = rsh_coef_l->cartesian_comb[3 * icomb + 2];
     218           0 :     double y_lm_rst = rsh_coef_l->coeffs[(m + l) * ncart + icomb];
     219             : 
     220           0 :     value += y_lm_rst * kx_powers[r] * ky_powers[s] * kz_powers[t];
     221             :   }
     222             : 
     223           0 :   return value;
     224             : }
     225             : 
     226             : /**
     227             :  * Calculates values of the real spherical harmonic S_lm at the point k/|k| of
     228             :  * the unit sphere for all m = -l, ..., +l
     229             :  */
     230     1281430 : void libgrpp_evaluate_real_spherical_harmonics_array(const int l,
     231             :                                                      const double *k,
     232             :                                                      double *rsh_array) {
     233     1281430 :   double unitary_kx;
     234     1281430 :   double unitary_ky;
     235     1281430 :   double unitary_kz;
     236     1281430 :   double kx_powers[200];
     237     1281430 :   double ky_powers[200];
     238     1281430 :   double kz_powers[200];
     239             : 
     240     1281430 :   rsh_coef_table_t *rsh_coef_l = libgrpp_get_real_spherical_harmonic_table(l);
     241             : 
     242     1281430 :   double length_k = sqrt(k[0] * k[0] + k[1] * k[1] + k[2] * k[2]);
     243             : 
     244     1281430 :   if (length_k > LIBGRPP_ZERO_THRESH) {
     245     1008601 :     double inv_length = 1.0 / length_k;
     246     1008601 :     unitary_kx = k[0] * inv_length;
     247     1008601 :     unitary_ky = k[1] * inv_length;
     248     1008601 :     unitary_kz = k[2] * inv_length;
     249             :   } else {
     250             :     unitary_kx = 0.0;
     251             :     unitary_ky = 0.0;
     252             :     unitary_kz = 0.0;
     253             :   }
     254             : 
     255     1281430 :   kx_powers[0] = 1.0;
     256     1281430 :   ky_powers[0] = 1.0;
     257     1281430 :   kz_powers[0] = 1.0;
     258             : 
     259     3779668 :   for (int i = 1; i <= l; i++) {
     260     2498238 :     kx_powers[i] = kx_powers[i - 1] * unitary_kx;
     261     2498238 :     ky_powers[i] = ky_powers[i - 1] * unitary_ky;
     262     2498238 :     kz_powers[i] = kz_powers[i - 1] * unitary_kz;
     263             :   }
     264             : 
     265     1281430 :   memset(rsh_array, 0, (2 * l + 1) * sizeof(double));
     266             : 
     267     1281430 :   int ncart = rsh_coef_l->n_cart_comb;
     268     1281430 :   int *rst_array = rsh_coef_l->cartesian_comb;
     269             : 
     270    10469930 :   for (int icomb = 0; icomb < ncart; icomb++) {
     271     9188500 :     int r = rst_array[3 * icomb];
     272     9188500 :     int s = rst_array[3 * icomb + 1];
     273     9188500 :     int t = rst_array[3 * icomb + 2];
     274             : 
     275     9188500 :     double k_xyz = kx_powers[r] * ky_powers[s] * kz_powers[t];
     276             : 
     277    81421352 :     for (int m = -l; m <= l; m++) {
     278    72232852 :       double y_lm_rst = rsh_coef_l->coeffs[(m + l) * ncart + icomb];
     279    72232852 :       rsh_array[m + l] += y_lm_rst * k_xyz;
     280             :     }
     281             :   }
     282     1281430 : }
     283             : 
     284         574 : static int *generate_cartesian_combinations(int L, int *num) {
     285         574 :   *num = (L + 1) * (L + 2) / 2;
     286             : 
     287         574 :   int *combinations = (int *)calloc(*num, 3 * sizeof(int));
     288             : 
     289         574 :   int n = 0;
     290       12628 :   for (int i = 0; i <= L; i++) {
     291      345548 :     for (int j = 0; j <= L; j++) {
     292    10711988 :       for (int k = 0; k <= L; k++) {
     293    10378494 :         if (i + j + k == L) {
     294      172774 :           combinations[3 * n + 0] = i;
     295      172774 :           combinations[3 * n + 1] = j;
     296      172774 :           combinations[3 * n + 2] = k;
     297      172774 :           n++;
     298             :         }
     299             :       }
     300             :     }
     301             :   }
     302             : 
     303         574 :   return combinations;
     304             : }

Generated by: LCOV version 1.15