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 : * Calculation of kinetic-energy integrals.
17 : *
18 : * For details, see:
19 : * T. Helgaker, P. Jorgensen, J. Olsen, Molecular Electronic-Structure Theory,
20 : * John Wiley & Sons Ltd, 2000.
21 : * Chapter 9.3.4, "Momentum and kinetic-energy integrals"
22 : */
23 : #include <math.h>
24 : #include <stdlib.h>
25 : #include <string.h>
26 :
27 : #ifndef M_PI
28 : #define M_PI 3.14159265358979323846
29 : #endif
30 :
31 : #include "grpp_kinetic.h"
32 :
33 : #include "grpp_norm_gaussian.h"
34 : #include "grpp_utils.h"
35 : #include "libgrpp.h"
36 :
37 : static void kinetic_energy_integrals_shell_pair_obara_saika(
38 : libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double alpha_A,
39 : double alpha_B, double *kinetic_matrix);
40 :
41 0 : void libgrpp_kinetic_energy_integrals(libgrpp_shell_t *shell_A,
42 : libgrpp_shell_t *shell_B,
43 : double *kinetic_matrix) {
44 0 : int size_A = libgrpp_get_shell_size(shell_A);
45 0 : int size_B = libgrpp_get_shell_size(shell_B);
46 :
47 0 : double *buf = calloc(size_A * size_B, sizeof(double));
48 :
49 0 : memset(kinetic_matrix, 0, size_A * size_B * sizeof(double));
50 :
51 : // loop over primitives in contractions
52 0 : for (int i = 0; i < shell_A->num_primitives; i++) {
53 0 : for (int j = 0; j < shell_B->num_primitives; j++) {
54 0 : double alpha_i = shell_A->alpha[i];
55 0 : double alpha_j = shell_B->alpha[j];
56 0 : double coef_A_i = shell_A->coeffs[i];
57 0 : double coef_B_j = shell_B->coeffs[j];
58 :
59 0 : kinetic_energy_integrals_shell_pair_obara_saika(shell_A, shell_B, alpha_i,
60 : alpha_j, buf);
61 :
62 0 : libgrpp_daxpy(size_A * size_B, coef_A_i * coef_B_j, buf, kinetic_matrix);
63 : }
64 : }
65 :
66 0 : free(buf);
67 0 : }
68 :
69 0 : static void kinetic_energy_integrals_shell_pair_obara_saika(
70 : libgrpp_shell_t *shell_A, libgrpp_shell_t *shell_B, double alpha_A,
71 : double alpha_B, double *kinetic_matrix) {
72 0 : int size_A = libgrpp_get_shell_size(shell_A);
73 0 : int size_B = libgrpp_get_shell_size(shell_B);
74 0 : int L_A = shell_A->L;
75 0 : int L_B = shell_B->L;
76 0 : double N_A = libgrpp_gaussian_norm_factor(L_A, 0, 0, alpha_A);
77 0 : double N_B = libgrpp_gaussian_norm_factor(L_B, 0, 0, alpha_B);
78 :
79 0 : double p = alpha_A + alpha_B;
80 0 : double mu = alpha_A * alpha_B / (alpha_A + alpha_B);
81 0 : double *A = shell_A->origin;
82 0 : double *B = shell_B->origin;
83 :
84 : // calculate S_ij
85 0 : double S[3][LIBGRPP_MAX_BASIS_L + 2][LIBGRPP_MAX_BASIS_L + 2];
86 :
87 0 : for (int coord = 0; coord < 3; coord++) {
88 0 : double P = (alpha_A * A[coord] + alpha_B * B[coord]) / p;
89 :
90 0 : double X_AB = A[coord] - B[coord];
91 0 : double X_PA = P - A[coord];
92 0 : double X_PB = P - B[coord];
93 0 : double pfac = 1.0 / (2.0 * p);
94 :
95 0 : for (int i = 0; i <= L_A + 2; i++) {
96 0 : for (int j = 0; j <= L_B + 2; j++) {
97 0 : double S_ij = 0.0;
98 :
99 0 : if (i + j == 0) {
100 0 : S[coord][0][0] = sqrt(M_PI / p) * exp(-mu * X_AB * X_AB);
101 0 : continue;
102 : }
103 :
104 0 : if (i == 0) { // upward by j
105 0 : S_ij += X_PB * S[coord][i][j - 1];
106 0 : if (j - 1 > 0) {
107 0 : S_ij += (j - 1) * pfac * S[coord][i][j - 2];
108 : }
109 : } else { // upward by i
110 0 : S_ij += X_PA * S[coord][i - 1][j];
111 0 : if (i - 1 > 0) {
112 0 : S_ij += (i - 1) * pfac * S[coord][i - 2][j];
113 : }
114 0 : if (j > 0) {
115 0 : S_ij += j * pfac * S[coord][i - 1][j - 1];
116 : }
117 : }
118 :
119 0 : S[coord][i][j] = S_ij;
120 : }
121 : }
122 : }
123 :
124 : // calculate D^2_ij
125 :
126 : double D2[3][LIBGRPP_MAX_BASIS_L][LIBGRPP_MAX_BASIS_L];
127 :
128 0 : for (int coord = 0; coord < 3; coord++) {
129 0 : for (int i = 0; i <= L_A; i++) {
130 0 : for (int j = 0; j <= L_B; j++) {
131 :
132 0 : double D2_ij = 0.0;
133 0 : D2_ij += 4.0 * alpha_A * alpha_A * S[coord][i + 2][j];
134 0 : D2_ij -= 2.0 * alpha_A * (2 * i + 1) * S[coord][i][j];
135 0 : if (i >= 2) {
136 0 : D2_ij += i * (i - 1) * S[coord][i - 2][j];
137 : }
138 :
139 0 : D2[coord][i][j] = D2_ij;
140 : }
141 : }
142 : }
143 :
144 : // loop over cartesian functions inside the shells
145 0 : for (int m = 0; m < size_A; m++) {
146 0 : for (int n = 0; n < size_B; n++) {
147 0 : int n_A = shell_A->cart_list[3 * m + 0];
148 0 : int l_A = shell_A->cart_list[3 * m + 1];
149 0 : int m_A = shell_A->cart_list[3 * m + 2];
150 0 : int n_B = shell_B->cart_list[3 * n + 0];
151 0 : int l_B = shell_B->cart_list[3 * n + 1];
152 0 : int m_B = shell_B->cart_list[3 * n + 2];
153 :
154 0 : kinetic_matrix[m * size_B + n] =
155 0 : -0.5 * N_A * N_B *
156 0 : (D2[0][n_A][n_B] * S[1][l_A][l_B] * S[2][m_A][m_B] +
157 0 : S[0][n_A][n_B] * D2[1][l_A][l_B] * S[2][m_A][m_B] +
158 0 : S[0][n_A][n_B] * S[1][l_A][l_B] * D2[2][m_A][m_B]);
159 : }
160 : }
161 0 : }
|