-
Notifications
You must be signed in to change notification settings - Fork 0
/
mp2.c
242 lines (173 loc) · 5.85 KB
/
mp2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/* Includes */
#include <stdio.h>
#include <trexio.h>
#include <stdint.h>
#include <err.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
/* Reordering function */
/* There are permutation symmetries in the indices of integrals, which */
/* are exploited to reduce the storage of the integrals in the TREXIO file. */
/* \[ */
/* \langle i j | k l \rangle = */
/* \langle k j | i l \rangle = */
/* \langle k l | i j \rangle = */
/* \langle i l | k j \rangle = */
/* \langle j i | l k \rangle = */
/* \langle j k | l i \rangle = */
/* \langle l k | j i \rangle = */
/* \langle l i | j k \rangle */
/* \] */
/* The following function swaps the four indices such that any of the 8 */
/* possible combinations gives the same quartet. */
void reorder(int *i_, int *j_, int *k_, int *l_) {
{
assert (*i_ >= 0); assert (*j_ >= 0);
assert (*k_ >= 0); assert (*l_ >= 0);
const int i = *i_; const int j = *j_;
const int k = *k_; const int l = *l_;
if (k<i) {
*k_ = i ; *i_ = k;
}
if (l<j) {
*l_ = j ; *j_ = l;
}
}
{
const int i = *i_; const int j = *j_;
const int k = *k_; const int l = *l_;
if (j<i) {
*i_ = j ; *j_ = i;
*k_ = l ; *l_ = k;
}
}
}
/* MP2 program */
int main(int argc, char** argv)
{
/* Open the TREXIO file */
/* The name of the TREXIO file should be given as a command-line argument. */
if (argc < 2) {
fprintf(stderr, "usage: mp2 trexio_file.hdf5\n");
exit(1);
}
trexio_exit_code rc = TREXIO_SUCCESS;
trexio_t* trexio_file = trexio_open(argv[1], 'r', TREXIO_HDF5, &rc);
if (rc != TREXIO_SUCCESS) {
fprintf(stderr, "Error opening file %s", argv[1]);
exit(1);
}
assert (trexio_file != NULL);
/* Read parameters from TREXIO */
/* We need to read small scalar variables. The first ones are the */
/* number of up-spin and down-spin electrons to define the number of */
/* occupied orbitals ~n_occ~, and check that we are in a closed-shell system. */
int n_up = 0;
rc = trexio_read_electron_up_num(trexio_file, &n_up);
if (rc != TREXIO_SUCCESS) {
fprintf(stderr, "Error reading n_up");
exit(1);
}
assert(n_up > 0);
int n_dn = 0;
rc = trexio_read_electron_up_num(trexio_file, &n_dn);
if (rc != TREXIO_SUCCESS) {
fprintf(stderr, "Error reading n_dn");
exit(1);
}
assert(n_up > 0);
if (n_up != n_dn) {
fprintf(stderr, "This code is works only for n_up = n_dn");
exit(1);
}
const int n_occ = n_up;
/* We also need to read the total number of molecular orbitals to */
/* compute the number of virtual orbitals ~n_virt~. */
int mo_num = 0;
rc = trexio_read_mo_num(trexio_file, &mo_num);
if (rc != TREXIO_SUCCESS) {
fprintf(stderr, "Error reading mo_num");
exit(1);
}
assert(mo_num > 0);
const int n_virt = mo_num - n_up;
/* Quantities involved in the MP2 equation */
/* We assume the TREXIO file contains electron repulsion integrals */
/* (ERI) in the molecular orbital basis, and orbital energies. */
/* We first read the orbital energies: */
double* epsilon = malloc(mo_num * sizeof(double));
rc = trexio_read_mo_energy(trexio_file, epsilon);
if (rc != TREXIO_SUCCESS) {
fprintf(stderr, "Error reading mo_energy");
exit(1);
}
/* Then we read the electron repulsion integrals. They are stored in a */
/* sparse data format, so we obtain quartets of indices and values for */
/* non-zero integrals. */
int64_t n_integrals;
rc = trexio_read_mo_2e_int_eri_size(trexio_file, &n_integrals);
if (rc != TREXIO_SUCCESS) {
fprintf(stderr, "Error reading n_integrals");
exit(1);
}
assert(n_integrals > 0);
int* const index = malloc(4*n_integrals * sizeof(int));
if (index == NULL) {
fprintf(stderr, "Malloc failed for index");
exit(1);
}
double* const value = malloc(n_integrals * sizeof(double));
if (index == NULL) {
fprintf(stderr, "Malloc failed for value");
exit(1);
}
int64_t count = n_integrals;
rc = trexio_read_mo_2e_int_eri(trexio_file, 0L, &count, index, value);
/* We transform these arrays of indices and values into an array of */
/* double, where the index of the quartet ~(i,j,a,b)~ is located at */
/* address ~b-n_occ + n_virt*(a-n_occ + n_virt*(j + n_occ*i))~. Only */
/* required integrals are stored. */
/* By symmetry, the integral at ~(i,j,a,b)~ is equal to the integral at */
/* ~(j,i,b,a)~. As integrals may be stored only once in the integrals */
/* file, to be sure we don't miss integrals we store the value at both */
/* addresses. */
size_t nmax = n_occ*n_occ*n_virt*n_virt;
double* integral = malloc(nmax * sizeof(double));
memset(integral, 0, nmax*sizeof(double));
for (size_t kk=0; kk<n_integrals ; ++kk) {
int i = index[4*kk+0]; assert (i >= 0);
int j = index[4*kk+1]; assert (j >= 0);
int a = index[4*kk+2]; assert (a >= 0);
int b = index[4*kk+3]; assert (b >= 0);
reorder(&i, &j, &a, &b);
if (i >= n_occ || j >= n_occ || a < n_occ || b < n_occ ) {
continue;
} else {
a -= n_occ;
b -= n_occ;
const size_t ijab = b + n_virt*(a + n_virt*(j + n_occ*i));
const size_t jiba = a + n_virt*(b + n_virt*(i + n_occ*j));
integral[ijab] = value[kk];
integral[jiba] = value[kk];
}
}
/* MP2 computation */
double Emp2 = 0.;
for (int i=0 ; i<n_occ ; ++i) {
for (int j=0 ; j<n_occ ; ++j) {
const size_t shift = n_virt*(j + n_occ*i);
for (int a=0 ; a<n_virt ; ++a) {
for (int b=0 ; b<n_virt ; ++b) {
const size_t ijab = b + n_virt*(a + shift);
const size_t ijba = a + n_virt*(b + shift);
Emp2 += ( integral[ijab]*(2.*integral[ijab]-integral[ijba]) ) /
(epsilon[i] + epsilon[j] - epsilon[n_occ+a] - epsilon[n_occ+b]);
}
}
}
}
/* Termination */
/* Print the result: */
printf("Emp2 = %15.12f\n", Emp2);
}