LCOV - code coverage report
Current view: top level - src/grpp - grpp_outercore_integrals.c (source / functions) Hit Total Coverage
Test: CP2K Regtests (git:b4bd748) Lines: 0 212 0.0 %
Date: 2025-03-09 07:56:22 Functions: 0 10 0.0 %

          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             :  * Integration of the non-local terms in the GRPP operator.
      17             :  * These integrals are reduced to the type 1 integrals and overlap integrals.
      18             :  */
      19             : 
      20             : #include <assert.h>
      21             : #include <math.h>
      22             : #include <stdio.h>
      23             : #include <stdlib.h>
      24             : #include <string.h>
      25             : 
      26             : #ifndef M_PI
      27             : #define M_PI 3.14159265358979323846
      28             : #endif
      29             : 
      30             : #include "grpp_factorial.h"
      31             : #include "grpp_lmatrix.h"
      32             : #include "grpp_overlap.h"
      33             : #include "grpp_spherical_harmonics.h"
      34             : #include "grpp_utils.h"
      35             : #include "libgrpp.h"
      36             : 
      37             : /*
      38             :  * pre-definitions of function used below
      39             :  */
      40             : 
      41             : void libgrpp_outercore_potential_integrals_part_1(
      42             :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *C,
      43             :     libgrpp_potential_t *oc_potential, libgrpp_shell_t *oc_shell,
      44             :     double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
      45             :     double *so_z_matrix);
      46             : 
      47             : void libgrpp_outercore_potential_integrals_part_2(
      48             :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *C,
      49             :     libgrpp_potential_t *oc_potential_1, libgrpp_shell_t *oc_shell_1,
      50             :     libgrpp_potential_t *oc_potential_2, libgrpp_shell_t *oc_shell_2,
      51             :     double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
      52             :     double *so_z_matrix);
      53             : 
      54             : static double calculate_delta_integral(libgrpp_potential_t *oc_pot_1,
      55             :                                        libgrpp_shell_t *oc_shell_1,
      56             :                                        libgrpp_potential_t *oc_pot_2,
      57             :                                        libgrpp_shell_t *oc_shell_2);
      58             : 
      59             : static void transform_to_sph_basis_ket(int dim_bra, int dim_ket_cart,
      60             :                                        int dim_ket_sph, double *A_in,
      61             :                                        double *A_out, double *S_lm_coef);
      62             : 
      63             : static void transform_to_sph_basis_bra(int dim_bra_cart, int dim_bra_sph,
      64             :                                        int dim_ket, double *A_in, double *A_out,
      65             :                                        double *S_lm_coef);
      66             : 
      67             : static double ang_norm_factor(int lx, int ly, int lz);
      68             : 
      69             : static double analytic_one_center_rpp_integral_contracted(
      70             :     libgrpp_shell_t *bra, libgrpp_shell_t *ket, libgrpp_potential_t *pot);
      71             : 
      72             : static double analytic_one_center_rpp_integral_primitive(int L, double alpha1,
      73             :                                                          double alpha2, int n,
      74             :                                                          double zeta);
      75             : 
      76             : static double radial_gto_norm_factor(int L, double alpha);
      77             : 
      78             : /**
      79             :  * Calculates non-local contributions to the scalar-relativistic ECP and
      80             :  * effective spin-orbit interaction matrices from the outercore (OC) potentials.
      81             :  */
      82           0 : void libgrpp_outercore_potential_integrals(
      83             :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *rpp_origin,
      84             :     int num_oc_shells, libgrpp_potential_t **oc_potentials,
      85             :     libgrpp_shell_t **oc_shells, double *arep, double *esop_x, double *esop_y,
      86             :     double *esop_z) {
      87           0 :   assert(libgrpp_is_initialized());
      88             : 
      89             :   // clear output matrices
      90           0 :   int size_A = libgrpp_get_shell_size(shell_A);
      91           0 :   int size_B = libgrpp_get_shell_size(shell_B);
      92           0 :   memset(arep, 0, size_A * size_B * sizeof(double));
      93           0 :   memset(esop_x, 0, size_A * size_B * sizeof(double));
      94           0 :   memset(esop_y, 0, size_A * size_B * sizeof(double));
      95           0 :   memset(esop_z, 0, size_A * size_B * sizeof(double));
      96             : 
      97             :   // bind outercore shells of the GRPP to the RPP center
      98           0 :   for (int ioc = 0; ioc < num_oc_shells; ioc++) {
      99           0 :     oc_shells[ioc]->origin[0] = rpp_origin[0];
     100           0 :     oc_shells[ioc]->origin[1] = rpp_origin[1];
     101           0 :     oc_shells[ioc]->origin[2] = rpp_origin[2];
     102             :   }
     103             : 
     104             :   // 1. the U * |nlj><nlj| + |nlj><nlj| * U part
     105           0 :   for (int ioc = 0; ioc < num_oc_shells; ioc++) {
     106           0 :     libgrpp_potential_t *pot = oc_potentials[ioc];
     107           0 :     libgrpp_shell_t *nlj = oc_shells[ioc];
     108             : 
     109           0 :     libgrpp_outercore_potential_integrals_part_1(
     110             :         shell_A, shell_B, rpp_origin, pot, nlj, arep, esop_x, esop_y, esop_z);
     111             :   }
     112             : 
     113             :   // 2. the |nlj><nlj| U |n'lj><n'lj| part
     114           0 :   for (int ioc = 0; ioc < num_oc_shells; ioc++) {
     115           0 :     for (int joc = 0; joc < num_oc_shells; joc++) {
     116             : 
     117           0 :       libgrpp_potential_t *pot_1 = oc_potentials[ioc];
     118           0 :       libgrpp_potential_t *pot_2 = oc_potentials[joc];
     119           0 :       libgrpp_shell_t *nlj_1 = oc_shells[ioc];
     120           0 :       libgrpp_shell_t *nlj_2 = oc_shells[joc];
     121             : 
     122           0 :       libgrpp_outercore_potential_integrals_part_2(
     123             :           shell_A, shell_B, rpp_origin, pot_1, nlj_1, pot_2, nlj_2, arep,
     124             :           esop_x, esop_y, esop_z);
     125             :     }
     126             :   }
     127           0 : }
     128             : 
     129             : /**
     130             :  * integration of the non-local outercore potential:
     131             :  * the U*|nlj><nlj| + |nlj><nlj|*U part
     132             :  */
     133           0 : void libgrpp_outercore_potential_integrals_part_1(
     134             :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *C,
     135             :     libgrpp_potential_t *oc_potential, libgrpp_shell_t *oc_shell,
     136             :     double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
     137             :     double *so_z_matrix) {
     138           0 :   int L = oc_shell->L;
     139           0 :   double J = oc_potential->J / 2.0;
     140           0 :   int size_A = libgrpp_get_shell_size(shell_A);
     141           0 :   int size_B = libgrpp_get_shell_size(shell_B);
     142           0 :   int size_nlj = libgrpp_get_shell_size(oc_shell);
     143             : 
     144             :   /*
     145             :    * foolproof: bind outercore shells to the ECP center
     146             :    */
     147           0 :   oc_shell->origin[0] = C[0];
     148           0 :   oc_shell->origin[1] = C[1];
     149           0 :   oc_shell->origin[2] = C[2];
     150             : 
     151             :   /*
     152             :    * Transformation coefficients: Cartesian -> Real spherical
     153             :    * Rows: real spherical harmonics S_lm
     154             :    * Columns: cartesians x^r y^s z^t
     155             :    */
     156           0 :   double *S_lm_coef = (double *)calloc((2 * L + 1) * size_nlj, sizeof(double));
     157           0 :   for (int m = -L; m <= +L; m++) {
     158           0 :     for (int icart = 0; icart < size_nlj; icart++) {
     159           0 :       int r = oc_shell->cart_list[3 * icart + 0];
     160           0 :       int s = oc_shell->cart_list[3 * icart + 1];
     161           0 :       int t = oc_shell->cart_list[3 * icart + 2];
     162           0 :       double u = libgrpp_spherical_to_cartesian_coef(L, m, r, s) /
     163           0 :                  ang_norm_factor(r, s, t);
     164           0 :       S_lm_coef[size_nlj * (m + L) + icart] = u;
     165             :     }
     166             :   }
     167             : 
     168             :   /*
     169             :    * Overlap integrals of the A and B shells with the outercore shell |nlj>:
     170             :    * <A|nljm> and <nljm|B>
     171             :    *
     172             :    * Integrals are evaluated first for the cartesian components of |nlj>, and
     173             :    * then transformed to the basis of real spherical components |nljm>.
     174             :    * Resulting integrals are stored in the 'S_a_nljm' and 'S_nljm_b' arrays.
     175             :    */
     176           0 :   double *S_nljm_b_cart =
     177           0 :       alloc_zeros_1d(size_nlj * size_B); // in cart_list basis
     178           0 :   double *S_a_nljm_cart = alloc_zeros_1d(size_A * size_nlj);
     179           0 :   double *S_nljm_b = alloc_zeros_1d((2 * L + 1) * size_B); // in spherical basis
     180           0 :   double *S_a_nljm = alloc_zeros_1d(size_A * (2 * L + 1));
     181           0 :   libgrpp_overlap_integrals(oc_shell, shell_B, S_nljm_b_cart); // <nljm|B>
     182           0 :   libgrpp_overlap_integrals(shell_A, oc_shell, S_a_nljm_cart); // <A|nljm>
     183           0 :   transform_to_sph_basis_ket(size_A, size_nlj, 2 * L + 1, S_a_nljm_cart,
     184             :                              S_a_nljm, S_lm_coef);
     185           0 :   transform_to_sph_basis_bra(size_nlj, 2 * L + 1, size_B, S_nljm_b_cart,
     186             :                              S_nljm_b, S_lm_coef);
     187           0 :   free(S_nljm_b_cart);
     188           0 :   free(S_a_nljm_cart);
     189             : 
     190             :   /*
     191             :    * ECP type-2 (semilocal) integrals of the A and B shells with the outercore
     192             :    * shell |nlj>: <A|U(r)P_L|nljm> and <nljm|U(r)P_L|B>
     193             :    *
     194             :    * Integrals are evaluated first for the cartesian components of |nlj>, and
     195             :    * then transformed to the basis of real spherical components |nljm>.
     196             :    * Resulting integrals are stored in the 'U_a_nljm' and 'U_nljm_b' arrays.
     197             :    */
     198           0 :   double *U_nljm_b_cart =
     199           0 :       alloc_zeros_1d(size_nlj * size_B); // in cart_list basis
     200           0 :   double *U_a_nljm_cart = alloc_zeros_1d(size_A * size_nlj);
     201           0 :   double *U_nljm_b = alloc_zeros_1d((2 * L + 1) * size_B); // in spherical basis
     202           0 :   double *U_a_nljm = alloc_zeros_1d(size_A * (2 * L + 1));
     203           0 :   libgrpp_type1_integrals(oc_shell, shell_B, C, oc_potential,
     204             :                           U_nljm_b_cart); // <nljm|U(r)P_L|B>
     205           0 :   libgrpp_type1_integrals(shell_A, oc_shell, C, oc_potential,
     206             :                           U_a_nljm_cart); // <A|U(r)P_L|nljm>
     207           0 :   transform_to_sph_basis_ket(size_A, size_nlj, 2 * L + 1, U_a_nljm_cart,
     208             :                              U_a_nljm, S_lm_coef);
     209           0 :   transform_to_sph_basis_bra(size_nlj, 2 * L + 1, size_B, U_nljm_b_cart,
     210             :                              U_nljm_b, S_lm_coef);
     211           0 :   free(U_nljm_b_cart);
     212           0 :   free(U_a_nljm_cart);
     213             : 
     214             :   /*
     215             :    * Construct outercore AREP matrix elements
     216             :    * < a | P_nlj U(r) P_L + U(r) P_nlj P_L | b >
     217             :    */
     218           0 :   double arep_factor =
     219           0 :       (J < L) ? (L / (2.0 * L + 1)) : ((L + 1) / (2.0 * L + 1));
     220           0 :   double *buf = alloc_zeros_1d(size_A * size_B);
     221           0 :   libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, S_a_nljm, U_nljm_b, buf);
     222           0 :   libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, U_a_nljm, S_nljm_b, buf);
     223           0 :   libgrpp_daxpy(size_A * size_B, arep_factor, buf, arep_matrix);
     224           0 :   free(buf);
     225             : 
     226             :   /*
     227             :    * Construct outercore effective SO potential matrix elements
     228             :    * < a | P_nlj U(r) L P_L + U(r) P_nlj L P_L | b >
     229             :    */
     230           0 :   double **L_matrices = alloc_zeros_2d(3, (2 * L + 1) * (2 * L + 1));
     231           0 :   libgrpp_construct_angular_momentum_matrices_rsh(L, L_matrices[0],
     232             :                                                   L_matrices[1], L_matrices[2]);
     233             : 
     234           0 :   double **so_buf = alloc_zeros_2d(3, size_A * size_B);
     235           0 :   buf = alloc_zeros_1d((2 * L + 1) * int_max2(size_A, size_B));
     236             : 
     237           0 :   for (int icoord = 0; icoord < 3; icoord++) {
     238             :     // U(r) P_nlj
     239           0 :     memset(buf, 0, (2 * L + 1) * size_B * sizeof(double));
     240           0 :     libgrpp_multiply_matrices(2 * L + 1, size_B, 2 * L + 1, L_matrices[icoord],
     241             :                               S_nljm_b, buf);
     242           0 :     libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, U_a_nljm, buf,
     243           0 :                               so_buf[icoord]);
     244             : 
     245             :     // P_nlj U(r)
     246           0 :     memset(buf, 0, (2 * L + 1) * size_A * sizeof(double));
     247           0 :     libgrpp_multiply_matrices(size_A, 2 * L + 1, 2 * L + 1, S_a_nljm,
     248             :                               L_matrices[icoord], buf);
     249           0 :     libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, buf, U_nljm_b,
     250             :                               so_buf[icoord]);
     251             :   }
     252             : 
     253           0 :   free(buf);
     254             : 
     255           0 :   double esop_factor = (J < L) ? (-2.0 / (2 * L + 1)) : (+2.0 / (2 * L + 1));
     256           0 :   libgrpp_daxpy(size_A * size_B, esop_factor, so_buf[0], so_x_matrix);
     257           0 :   libgrpp_daxpy(size_A * size_B, esop_factor, so_buf[1], so_y_matrix);
     258           0 :   libgrpp_daxpy(size_A * size_B, esop_factor, so_buf[2], so_z_matrix);
     259             : 
     260             :   /*
     261             :    * Cleanup
     262             :    */
     263           0 :   free(S_lm_coef);
     264           0 :   free(S_a_nljm);
     265           0 :   free(S_nljm_b);
     266           0 :   free(U_a_nljm);
     267           0 :   free(U_nljm_b);
     268           0 :   free_2d(L_matrices, 3);
     269           0 :   free_2d(so_buf, 3);
     270           0 : }
     271             : 
     272             : /**
     273             :  * integration of the non-local outercore potential:
     274             :  * the |nlj><nlj| U |n'lj><n'lj| part
     275             :  */
     276           0 : void libgrpp_outercore_potential_integrals_part_2(
     277             :     libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double *C,
     278             :     libgrpp_potential_t *oc_potential_1, libgrpp_shell_t *oc_shell_1,
     279             :     libgrpp_potential_t *oc_potential_2, libgrpp_shell_t *oc_shell_2,
     280             :     double *arep_matrix, double *so_x_matrix, double *so_y_matrix,
     281             :     double *so_z_matrix) {
     282           0 :   int size_A = libgrpp_get_shell_size(shell_A);
     283           0 :   int size_B = libgrpp_get_shell_size(shell_B);
     284             : 
     285           0 :   if (oc_potential_1->L != oc_potential_2->L) {
     286             :     return;
     287             :   }
     288           0 :   if (oc_potential_1->J != oc_potential_2->J) {
     289             :     return;
     290             :   }
     291             : 
     292             :   /*
     293             :    * foolproof: bind outercore shells to the ECP center
     294             :    */
     295           0 :   oc_shell_1->origin[0] = C[0];
     296           0 :   oc_shell_1->origin[1] = C[1];
     297           0 :   oc_shell_1->origin[2] = C[2];
     298           0 :   oc_shell_2->origin[0] = C[0];
     299           0 :   oc_shell_2->origin[1] = C[1];
     300           0 :   oc_shell_2->origin[2] = C[2];
     301             : 
     302             :   /*
     303             :    * just to be consistent with the MOLGEP code by A. Titov, N. Mosyagin, A.
     304             :    * Petrov: off-diagonal elements <n'lj|U|n''lj> = 0
     305             :    */
     306             :   /*if (!ecps_are_equal(oc_potential_1, oc_potential_2)) {
     307             :       return;
     308             :   }*/
     309             : 
     310           0 :   int L = oc_potential_1->L;
     311           0 :   double J = oc_potential_1->J / 2.0;
     312           0 :   int size_nlj = libgrpp_get_shell_size(oc_shell_1);
     313             : 
     314           0 :   double delta = calculate_delta_integral(oc_potential_1, oc_shell_1,
     315             :                                           oc_potential_2, oc_shell_2);
     316             : 
     317             :   /*
     318             :    * Transformation coefficients: Cartesian -> Real spherical
     319             :    * Rows: real spherical harmonics S_lm
     320             :    * Columns: cartesians x^r y^s z^t
     321             :    */
     322           0 :   double *S_lm_coef = (double *)calloc((2 * L + 1) * size_nlj, sizeof(double));
     323           0 :   for (int m = -L; m <= +L; m++) {
     324           0 :     for (int icart = 0; icart < size_nlj; icart++) {
     325           0 :       int r = oc_shell_1->cart_list[3 * icart + 0];
     326           0 :       int s = oc_shell_1->cart_list[3 * icart + 1];
     327           0 :       int t = oc_shell_1->cart_list[3 * icart + 2];
     328           0 :       double u = libgrpp_spherical_to_cartesian_coef(L, m, r, s) /
     329           0 :                  ang_norm_factor(r, s, t);
     330           0 :       S_lm_coef[size_nlj * (m + L) + icart] = u;
     331             :     }
     332             :   }
     333             : 
     334             :   /*
     335             :    * Overlap integrals of the A and B shells with the outercore shells |nlj> and
     336             :    * |n'lj>: <A|nljm> and <n'ljm|B>
     337             :    *
     338             :    * Integrals are evaluated first for the cartesian components of |nlj>, and
     339             :    * then transformed to the basis of real spherical components |nljm>.
     340             :    * Resulting integrals are stored in the 'S_a_nljm1' and 'S_nljm2_b' arrays.
     341             :    */
     342           0 :   double *S_nljm2_b_cart =
     343           0 :       alloc_zeros_1d(size_nlj * size_B); // in cart_list basis
     344           0 :   double *S_a_nljm1_cart = alloc_zeros_1d(size_A * size_nlj);
     345           0 :   double *S_nljm2_b =
     346           0 :       alloc_zeros_1d((2 * L + 1) * size_B); // in spherical basis
     347           0 :   double *S_a_nljm1 = alloc_zeros_1d(size_A * (2 * L + 1));
     348           0 :   libgrpp_overlap_integrals(oc_shell_2, shell_B, S_nljm2_b_cart); // <nljm|B>
     349           0 :   libgrpp_overlap_integrals(shell_A, oc_shell_1, S_a_nljm1_cart); // <A|nljm>
     350           0 :   transform_to_sph_basis_ket(size_A, size_nlj, 2 * L + 1, S_a_nljm1_cart,
     351             :                              S_a_nljm1, S_lm_coef);
     352           0 :   transform_to_sph_basis_bra(size_nlj, 2 * L + 1, size_B, S_nljm2_b_cart,
     353             :                              S_nljm2_b, S_lm_coef);
     354           0 :   free(S_nljm2_b_cart);
     355           0 :   free(S_a_nljm1_cart);
     356             : 
     357             :   /*
     358             :    * Construct outercore AREP matrix elements
     359             :    * < a | P_nlj U(r) P_n'lj | b >
     360             :    */
     361           0 :   double arep_factor =
     362           0 :       (J < L) ? (L / (2.0 * L + 1)) : ((L + 1) / (2.0 * L + 1));
     363           0 :   double *buf = alloc_zeros_1d(size_A * size_B);
     364           0 :   libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, S_a_nljm1, S_nljm2_b,
     365             :                             buf);
     366           0 :   libgrpp_daxpy(size_A * size_B, (-1.0) * delta * arep_factor, buf,
     367             :                 arep_matrix);
     368           0 :   free(buf);
     369             : 
     370             :   /*
     371             :    * Construct outercore effective SO potential matrix elements
     372             :    * < a | P_nlj U(r) L P_L + U(r) P_nlj L P_L | b >
     373             :    */
     374           0 :   double **L_matrices = alloc_zeros_2d(3, (2 * L + 1) * (2 * L + 1));
     375           0 :   libgrpp_construct_angular_momentum_matrices_rsh(L, L_matrices[0],
     376             :                                                   L_matrices[1], L_matrices[2]);
     377             : 
     378           0 :   double **so_buf = alloc_zeros_2d(3, size_A * size_B);
     379           0 :   buf = alloc_zeros_1d((2 * L + 1) * size_B);
     380           0 :   for (int icoord = 0; icoord < 3; icoord++) {
     381           0 :     memset(buf, 0, (2 * L + 1) * size_B * sizeof(double));
     382           0 :     libgrpp_multiply_matrices(2 * L + 1, size_B, 2 * L + 1, L_matrices[icoord],
     383             :                               S_nljm2_b, buf);
     384           0 :     libgrpp_multiply_matrices(size_A, size_B, 2 * L + 1, S_a_nljm1, buf,
     385           0 :                               so_buf[icoord]);
     386             :   }
     387           0 :   free(buf);
     388             : 
     389           0 :   double esop_factor = (J < L) ? (-2.0 / (2 * L + 1)) : (+2.0 / (2 * L + 1));
     390           0 :   libgrpp_daxpy(size_A * size_B, (-1.0) * delta * esop_factor, so_buf[0],
     391             :                 so_x_matrix);
     392           0 :   libgrpp_daxpy(size_A * size_B, (-1.0) * delta * esop_factor, so_buf[1],
     393             :                 so_y_matrix);
     394           0 :   libgrpp_daxpy(size_A * size_B, (-1.0) * delta * esop_factor, so_buf[2],
     395             :                 so_z_matrix);
     396             : 
     397           0 :   free_2d(so_buf, 3);
     398           0 :   free_2d(L_matrices, 3);
     399             : 
     400           0 :   free(S_nljm2_b);
     401           0 :   free(S_a_nljm1);
     402           0 :   free(S_lm_coef);
     403             : }
     404             : 
     405             : /**
     406             :  * Calculation of radial "delta" integrals.
     407             :  * Is performed analytically.
     408             :  */
     409           0 : static double calculate_delta_integral(libgrpp_potential_t *oc_pot_1,
     410             :                                        libgrpp_shell_t *oc_shell_1,
     411             :                                        libgrpp_potential_t *oc_pot_2,
     412             :                                        libgrpp_shell_t *oc_shell_2) {
     413             :   // both shells must have equal L,J quantum numbers, otherwise
     414             :   // the < nlj | U | n'l'j' > integral is strictly zero
     415           0 :   if (oc_pot_1->L != oc_pot_2->L || oc_pot_1->J != oc_pot_2->J) {
     416             :     return 0.0;
     417             :   }
     418             : 
     419           0 :   double U1 = analytic_one_center_rpp_integral_contracted(oc_shell_1,
     420             :                                                           oc_shell_2, oc_pot_1);
     421           0 :   double U2 = analytic_one_center_rpp_integral_contracted(oc_shell_1,
     422             :                                                           oc_shell_2, oc_pot_2);
     423             : 
     424           0 :   return 0.5 * (U1 + U2);
     425             : }
     426             : 
     427             : /**
     428             :  * analytic formula for one-center RPP integral between two contracted gaussian
     429             :  * functions.
     430             :  */
     431           0 : static double analytic_one_center_rpp_integral_contracted(
     432             :     libgrpp_shell_t *bra, libgrpp_shell_t *ket, libgrpp_potential_t *pot) {
     433           0 :   double sum = 0.0;
     434           0 :   int L = pot->L;
     435             : 
     436           0 :   for (int i = 0; i < bra->num_primitives; i++) {
     437           0 :     double coef_i = bra->coeffs[i];
     438           0 :     double alpha_i = bra->alpha[i];
     439           0 :     double N_i = radial_gto_norm_factor(L, alpha_i);
     440             : 
     441           0 :     for (int j = 0; j < ket->num_primitives; j++) {
     442           0 :       double coef_j = ket->coeffs[j];
     443           0 :       double alpha_j = ket->alpha[j];
     444           0 :       double N_j = radial_gto_norm_factor(L, alpha_j);
     445           0 :       double factor = N_i * N_j * coef_i * coef_j;
     446             : 
     447           0 :       for (int k = 0; k < pot->num_primitives; k++) {
     448           0 :         double coef_k = pot->coeffs[k];
     449           0 :         double zeta = pot->alpha[k];
     450           0 :         int n_rpp = pot->powers[k];
     451             : 
     452           0 :         double u_ijk = analytic_one_center_rpp_integral_primitive(
     453             :             L, alpha_i, alpha_j, n_rpp, zeta);
     454             : 
     455           0 :         sum += factor * coef_k * u_ijk;
     456             :       }
     457             :     }
     458             :   }
     459             : 
     460           0 :   return sum;
     461             : }
     462             : 
     463             : /**
     464             :  * analytic formula for one-center RPP integral between two gaussian primitives.
     465             :  * normalization factors are omitted here.
     466             :  */
     467           0 : static double analytic_one_center_rpp_integral_primitive(int L, double alpha1,
     468             :                                                          double alpha2, int n,
     469             :                                                          double zeta) {
     470           0 :   double a = alpha1 + alpha2 + zeta;
     471             : 
     472           0 :   if (n % 2 == 0) { // even n
     473           0 :     int k = L + n / 2;
     474           0 :     return libgrpp_double_factorial(2 * k - 1) / (pow(2.0, k + 1) * pow(a, k)) *
     475           0 :            sqrt(M_PI / a);
     476             :   } else { // odd n
     477           0 :     int k = L + (n - 1) / 2;
     478           0 :     return libgrpp_factorial(k) / (2.0 * pow(a, k + 1));
     479             :   }
     480             : }
     481             : 
     482             : /**
     483             :  * calculate normalization factor for the radial Gaussian-type orbital:
     484             :  * G(r) = N * r^L * exp(-alpha * r^2)
     485             :  */
     486           0 : static double radial_gto_norm_factor(int L, double alpha) {
     487             :   // pre-tabulated factors for calculation of normalization constants
     488             :   // (for each value of L)
     489           0 :   static const double factors[] = {
     490             :       2.5264751109842587,    2.9173221708553032,    2.6093322745198853,
     491             :       1.9724697960897537,    1.3149798640598356,    7.9296269381073192e-1,
     492             :       4.3985656185609934e-1, 2.2714095183849672e-1, 1.1017954545099481e-1,
     493             :       5.0553842554329785e-2, 2.2063505731056757e-2, 9.2011179391124215e-3,
     494             :       3.6804471756449694e-3, 1.4166047783978804e-3, 5.2611380677564405e-4,
     495             :       1.8898565833279173e-4, 6.5796360823633550e-5, 2.2243229718298637e-5,
     496             :       7.3135288801774484e-6, 2.3422037547660024e-6};
     497             : 
     498           0 :   return factors[L] * pow(alpha, 0.75 + L / 2.0);
     499             : }
     500             : 
     501             : /**
     502             :  * Transforms matrix from the basis of unitary sphere polynomials
     503             :  * to the basis of real spherical harmonics S_lm
     504             :  * (separately for 'bra' and 'ket' vectors)
     505             :  */
     506           0 : static void transform_to_sph_basis_ket(int dim_bra, int dim_ket_cart,
     507             :                                        int dim_ket_sph, double *A_in,
     508             :                                        double *A_out, double *S_lm_coef) {
     509           0 :   for (int i = 0; i < dim_bra; i++) {
     510           0 :     for (int j = 0; j < dim_ket_sph; j++) {
     511             :       double s = 0.0;
     512             : 
     513           0 :       for (int icart = 0; icart < dim_ket_cart; icart++) {
     514           0 :         double u_nljm = S_lm_coef[dim_ket_cart * j + icart];
     515           0 :         s += u_nljm * A_in[i * dim_ket_cart + icart];
     516             :       }
     517             : 
     518           0 :       A_out[i * dim_ket_sph + j] = s;
     519             :     }
     520             :   }
     521           0 : }
     522             : 
     523           0 : static void transform_to_sph_basis_bra(int dim_bra_cart, int dim_bra_sph,
     524             :                                        int dim_ket, double *A_in, double *A_out,
     525             :                                        double *S_lm_coef) {
     526           0 :   for (int j = 0; j < dim_ket; j++) {
     527           0 :     for (int i = 0; i < dim_bra_sph; i++) {
     528             : 
     529             :       double s = 0.0;
     530             : 
     531           0 :       for (int icart = 0; icart < dim_bra_cart; icart++) {
     532           0 :         double u_nljm = S_lm_coef[dim_bra_cart * i + icart];
     533           0 :         s += u_nljm * A_in[icart * dim_ket + j];
     534             :       }
     535             : 
     536           0 :       A_out[i * dim_ket + j] = s;
     537             :     }
     538             :   }
     539           0 : }
     540             : 
     541           0 : static double ang_norm_factor(int lx, int ly, int lz) {
     542           0 :   int L = lx + ly + lz;
     543             : 
     544           0 :   return 1.0 / (2.0 * sqrt(M_PI)) *
     545           0 :          sqrt(libgrpp_double_factorial(2 * L + 1)
     546             :               /*(double_factorial(2 * lx - 1) * double_factorial(2 * ly - 1) *
     547             :                  double_factorial(2 * lz - 1))*/
     548             :          );
     549             : }

Generated by: LCOV version 1.15