1 #include <math.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <complex.h>
6 
7 #include "vdw_kernel.h"
8 #include "vdwxc.h"
9 #include "vdw_q0.h"
10 
11 #if defined HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14 
15 #include "vdw_core.h"
16 
17 #if defined VDW_TIMING
18 #include "vdw_timing.h"
19 #endif
20 
21 #define VDWXC_DEBUG 0
22 
23 // Get name of functional from numeric code.
vdwxc_funcname(int functional)24 const char* vdwxc_funcname(int functional)
25 {
26     static char* names[] = { "vdW-DF", "vdW-DF2", "vdW-DF-cx", "custom", "unknown" };
27     switch(functional) {
28     case FUNC_VDWDF:
29         return names[0];
30     case FUNC_VDWDF2:
31         return names[1];
32     case FUNC_VDWDFCX:
33         return names[2];
34     case FUNC_CUSTOM:
35         return names[3];
36     default:
37         return names[4];
38     }
39 }
40 
41 // Get name of parallelization mode from numeric code.
vdwxc_fftname(int fftcode)42 const char* vdwxc_fftname(int fftcode)
43 {
44     static char* names[] = {"fftw-serial", "fftw-mpi", "pfft", "unknown"};
45     switch(fftcode) {
46     case VDWXC_FFTW_SERIAL:
47         return names[0];
48     case VDWXC_FFTW_MPI:
49         return names[1];
50     case VDWXC_PFFT:
51         return names[2];
52     default:
53         return names[3];
54     }
55 }
56 
vdwxc_errstring(int errcode)57 const char* vdwxc_errstring(int errcode)
58 {
59     static char* errstrs[] = {
60         "No errorcode set.",
61         "struct vdwxc_data uninitialized. Call vdwxc_initialize(...);",
62         "Unkown functional code.",
63         "Unit cell not set. Call vdwxc_set_unit_cell(...);",
64         "Unit cell already set in call to vdwxc_set_unit_cell(...);",
65         "Radial grid not set. Call vdwxc_set_radial_grid(...);",
66         "Unknown error code or uninitialized vdwxc_data object."
67     };
68     if(errcode >= 0 && errcode <= 5)
69         return errstrs[errcode];
70     else
71         return errstrs[6];
72 }
73 
vdwxc_print_error(vdwxc_data data)74 void vdwxc_print_error(vdwxc_data data)
75 {
76     // Internal error: Print the error and abort program.
77     // We should implement something nicer for user errors though.
78     printf("%s (errcode=%d)\n",
79            vdwxc_errstring(data->errorcode), data->errorcode);
80     if(data->errorcode != VDWXC_ERROR_NOERROR) {
81         assert(0);
82     }
83 }
84 
85 // Get the size of a vdwxc_data.
vdwxc_get_struct_size(void)86 int vdwxc_get_struct_size(void)
87 {
88     return sizeof(struct vdwxc_data_obj);
89 }
90 
91 // Set all bytes of vdwxc_data to zero.
vdwxc_nullify(vdwxc_data data)92 void vdwxc_nullify(vdwxc_data data)
93 {
94     memset(data, 0, vdwxc_get_struct_size());
95 }
96 
97 // Set functional type and some default values.
vdwxc_set_defaults(vdwxc_data vdw,int functional,int nspin)98 void vdwxc_set_defaults(vdwxc_data vdw, int functional, int nspin)
99 {
100     switch (functional) {
101     case FUNC_VDWDF:
102         vdw->Z_ab = -0.8491;
103         break;
104     case FUNC_VDWDF2:
105         vdw->Z_ab = -1.887;
106         break;
107     case FUNC_VDWDFCX:
108         vdw->Z_ab = -0.8491;
109         break;
110     default:
111         printf("unknown vdw-df functional %d\n", functional);
112         assert(0);
113         break;
114     }
115     vdw->functional = functional;
116     assert((nspin == 1) || (nspin == 2));
117     vdw->nspin = nspin;
118     vdw->q_cut = 5.0; // XXX to be declared by kernel
119     vdw->fft_type = VDWXC_FFTW_SERIAL;
120     vdw->mpi_size = 1;
121     vdw->initialized = VDWXC_INITIALIZED;
122 }
123 
124 // Allocate a new vdwxc_data and return a pointer to it.
125 
vdwxc_new_anyspin(int functional,int nspin)126 vdwxc_data vdwxc_new_anyspin(int functional, int nspin)
127 {
128     vdwxc_data vdw = malloc(vdwxc_get_struct_size());
129     vdwxc_nullify(vdw);
130     vdwxc_set_defaults(vdw, functional, nspin);
131     vdw->kernel = vdwxc_default_kernel();
132     return vdw;
133 }
134 
vdwxc_new(int functional)135 vdwxc_data vdwxc_new(int functional)
136 {
137     return vdwxc_new_anyspin(functional, 1);
138 }
139 
vdwxc_new_spin(int functional)140 vdwxc_data vdwxc_new_spin(int functional)
141 {
142     return vdwxc_new_anyspin(functional, 2);
143 }
144 
145 
146 // Write unit cell to string
vdwxc_sprint_cell(char * str,struct vdwxc_unit_cell * cell)147 int vdwxc_sprint_cell(char* str, struct vdwxc_unit_cell* cell)
148 {
149     char* ch = str;
150     ch += sprintf(ch, "    Nglobal = %d x %d x %d\n", cell->Nglobal[0], cell->Nglobal[1], cell->Nglobal[2]);
151     ch += sprintf(ch, "    Nlocal  = %d x %d x %d\n", cell->Nlocal[0], cell->Nlocal[1], cell->Nlocal[2]);
152     ch += sprintf(ch, "    offset  = %d, %d, %d\n", cell->offset[0], cell->offset[1], cell->offset[2]);
153 
154     double lengths[3];
155     int i, j;
156     for(i=0; i < 3; i++) {
157         lengths[i] = 0.0;
158         for(j=0; j < 3; j++) {
159             lengths[i] += cell->vec[3 * i + j] * cell->vec[3 * i + j];
160         }
161         lengths[i] = sqrt(lengths[i]);
162     }
163 
164     ch += sprintf(ch, "    Cell lengths %f, %f, %f\n", lengths[0], lengths[1], lengths[2]);
165     ch += sprintf(ch, "      %f, %f, %f\n", cell->vec[0], cell->vec[1], cell->vec[2]);
166     ch += sprintf(ch, "      %f, %f, %f\n", cell->vec[3], cell->vec[4], cell->vec[5]);
167     ch += sprintf(ch, "      %f, %f, %f\n", cell->vec[6], cell->vec[7], cell->vec[8]);
168     ch += sprintf(ch, "    dV %e\n", cell->dV);
169     int nbytes = ch - str;
170     return nbytes;
171 }
172 
173 #ifdef HAVE_MPI
vdwxc_has_mpi(void)174 int vdwxc_has_mpi(void){ return 1; }
175 #else
vdwxc_has_mpi(void)176 int vdwxc_has_mpi(void){ return 0; }
177 #endif
178 
179 #ifdef HAVE_PFFT
vdwxc_has_pfft(void)180 int vdwxc_has_pfft(void){ return 1; }
181 #else
vdwxc_has_pfft(void)182 int vdwxc_has_pfft(void){  return 0; }
183 #endif
184 
185 // Print representation of vdwxc_data to stdout.
vdwxc_print(vdwxc_data data)186 void vdwxc_print(vdwxc_data data)
187 {
188     // Buffer can hold 200 lines of full-width standard-size terminal.
189     // We will probably not print that much, ever.
190     int maxsize = 80 * 200;
191     char str[maxsize];
192     vdwxc_tostring(data, maxsize, str);
193     // printf(str) yields a warning
194     printf("%s", str);
195 }
196 
197 // Write representation of vdwxc_data to string.  Returns number of bytes written.
198 // Truncates to maxsize if necessary.
vdwxc_tostring(vdwxc_data data,int maxsize,char * str)199 int vdwxc_tostring(vdwxc_data data, int maxsize, char *str)
200 {
201     // We build within our own buffer, then copy into user-supplied str.
202     int maxlen = 200 * 80; // max 200 lines of length 80
203     if(maxsize > maxlen)
204         maxlen = maxsize;
205     char buf[maxlen];
206     char* ch = buf;
207     ch += sprintf(ch, "  === vdW-DF data at %p ===\n", data);
208     ch += sprintf(ch, "  functional %s [%d]\n", vdwxc_funcname(data->functional), data->functional);
209     if (data->nspin == 1) {
210         ch += sprintf(ch, "  spins: %d (spin-paired)\n", data->nspin);
211     } else {
212         ch += sprintf(ch, "  spins: %d (spin-polarized)\n", data->nspin);
213     }
214     ch += sprintf(ch, "  Z=%f :: qcut=%f\n", data->Z_ab, data->q_cut);
215     ch += sprintf(ch, "  cell\n");
216     ch += vdwxc_sprint_cell(ch, &data->cell);
217     ch += sprintf(ch, "  icell\n");
218     ch += vdwxc_sprint_cell(ch, &data->icell);
219     ch += sprintf(ch, "  FFT: %s [%d]\n", vdwxc_fftname(data->fft_type), data->fft_type);
220     if(data->fft_type == VDWXC_PFFT) {
221         ch += sprintf(ch, "  pfft grid %d x %d\n", data->pfft_grid[0], data->pfft_grid[1]);
222     }
223 #ifdef HAVE_MPI
224     ch += sprintf(ch, "  MPI :: rank=%d :: size=%d :: ptr=<%p>\n",
225                   data->mpi_rank, data->mpi_size, data->mpi_comm);
226 #endif
227     ch += sprintf(ch, "  alloc Ng=%d Nglobal=%d gLDA=%d\n",
228                   data->Ng, data->Nglobal, data->gLDA);
229     ch += sprintf(ch, "  kLDA=%d\n", data->kLDA);
230     ch += sprintf(ch, "  ptrs1 q0=<%p> work_ka=<%p>\n",
231                   data->q0_g, data->work_ka);
232     ch += sprintf(ch, "  ptrs2 rho_dq0drho_sg=<%p> rho_dq0dsigma_sg=<%p>\n",
233                   data->rho_dq0drho_sg, data->rho_dq0dsigma_sg);
234     ch += sprintf(ch, "  fftw ptrs  r2c=<%p> c2r=<%p>\n", data->plan_r2c, data->plan_c2r);
235 #ifdef HAVE_PFFT
236     ch += sprintf(ch, "  pfft ptrs r2c=<%p> c2r=<%p>\n", data->pfft_plan_r2c, data->pfft_plan_c2r);
237 #endif
238     ch += sprintf(ch, "  Error state %d: %s\n", data->errorcode, vdwxc_errstring(data->errorcode));
239     ch += sprintf(ch, "  =============================\n");
240     int len = strlen(buf);
241     assert(ch - buf == len);
242     assert(len < maxlen);
243     assert(len < maxsize);
244     strcpy(str, buf);
245     return len;
246 }
247 
vdwxc_set_unit_cell(vdwxc_data data,int Nx,int Ny,int Nz,double C00,double C10,double C20,double C01,double C11,double C21,double C02,double C12,double C22)248 void vdwxc_set_unit_cell(vdwxc_data data,
249                          int Nx, int Ny, int Nz,
250                          double C00, double C10, double C20,
251                          double C01, double C11, double C21,
252                          double C02, double C12, double C22)
253 {
254     double det = (C00 * (C11 * C22 - C21 * C12) \
255                   - C01 * (C10 * C22 - C12 * C20) \
256                   + C02 * (C10 * C21 - C11 * C20));
257     data->cell.dV = det / (Nx * Ny * Nz);
258     data->cell.vec[0] = C00;
259     data->cell.vec[1] = C01;
260     data->cell.vec[2] = C02;
261     data->cell.vec[3] = C10;
262     data->cell.vec[4] = C11;
263     data->cell.vec[5] = C12;
264     data->cell.vec[6] = C20;
265     data->cell.vec[7] = C21;
266     data->cell.vec[8] = C22;
267 
268     data->cell.Nglobal[0] = Nx;
269     data->cell.Nglobal[1] = Ny;
270     data->cell.Nglobal[2] = Nz;
271 
272     // Calculate reciprocal cell
273     double idet = 1.0 / (C00 * (C11 * C22 - C21 * C12) \
274                          - C01 * (C10 * C22 - C12 * C20) \
275                          + C02 * (C10 * C21 - C11 * C20)) * 2 * VDWXC_PI;
276     data->icell.Nglobal[0] = Nx;
277     data->icell.Nglobal[1] = Ny;
278     data->icell.Nglobal[2] = Nz / 2 + 1;
279     data->icell.dV = 1e100;
280     data->icell.vec[0] =  (C11 * C22 - C21 * C12) * idet;
281     data->icell.vec[1] = -(C01 * C22 - C02 * C21) * idet;
282     data->icell.vec[2] =  (C01 * C12 - C02 * C11) * idet;
283     data->icell.vec[3] = -(C10 * C22 - C12 * C20) * idet;
284     data->icell.vec[4] =  (C00 * C22 - C02 * C20) * idet;
285     data->icell.vec[5] = -(C00 * C12 - C10 * C02) * idet;
286     data->icell.vec[6] =  (C10 * C21 - C20 * C11) * idet;
287     data->icell.vec[7] = -(C00 * C21 - C20 * C01) * idet;
288     data->icell.vec[8] =  (C00 * C11 - C10 * C01) * idet;
289 
290     data->Nglobal = data->cell.Nglobal[0]
291                   * data->cell.Nglobal[1]
292                   * data->cell.Nglobal[2];
293 }
294 
vdwxc_allocate_buffers(vdwxc_data data)295 void vdwxc_allocate_buffers(vdwxc_data data)
296 {
297     data->Ng = data->cell.Nlocal[0] * data->cell.Nlocal[1] * data->cell.Nlocal[2];
298 
299     size_t gridsize = data->Ng * sizeof(double);
300 
301     data->q0_g = (double *) malloc(gridsize);
302     data->rho_dq0drho_sg = (double *) malloc(gridsize * data->nspin);
303     data->rho_dq0dsigma_sg = (double *) malloc(gridsize * data->nspin);
304 }
305 
vdwxc_init_serial(vdwxc_data data)306 void vdwxc_init_serial(vdwxc_data data)
307 {
308     data->kLDA = data->icell.Nglobal[2];
309     data->gLDA = 2 * data->kLDA;
310     int nktotal = data->cell.Nglobal[0] * data->cell.Nglobal[1] * data->kLDA * data->kernel.nalpha;
311     data->work_ka = (double complex*) fftw_malloc(nktotal * sizeof(double complex));
312     int i;
313     for(i = 0; i < 3; i++) {
314         data->cell.Nlocal[i] = data->cell.Nglobal[i];
315         data->icell.Nlocal[i]= data->cell.Nglobal[i];
316     }
317     data->icell.Nlocal[2] = data->icell.Nlocal[2] / 2 + 1;
318 
319     data->plan_r2c = fftw_plan_many_dft_r2c(3,
320                                             data->cell.Nglobal,
321                                             data->kernel.nalpha,
322                                             (double*)data->work_ka,
323                                             NULL,
324                                             data->kernel.nalpha,
325                                             1,
326                                             data->work_ka,
327                                             NULL,
328                                             data->kernel.nalpha,
329                                             1,
330                                             FFTW_ESTIMATE);
331     data->plan_c2r = fftw_plan_many_dft_c2r(3,
332                                             data->cell.Nglobal,
333                                             data->kernel.nalpha,
334                                             data->work_ka,
335                                             NULL,
336                                             data->kernel.nalpha,
337                                             1,
338                                             (double*)data->work_ka,
339                                             NULL,
340                                             data->kernel.nalpha,
341                                             1,
342                                             FFTW_ESTIMATE);
343     assert(data->plan_r2c != NULL);
344     assert(data->plan_c2r != NULL);
345     vdwxc_allocate_buffers(data);
346 }
347 
vdwxc_compute_thetas(struct vdwxc_kernel * kernel,int nx,int ny,int nz,int leading_dim,double * rho,double * q0,double * output)348 void vdwxc_compute_thetas(struct vdwxc_kernel *kernel,
349                         int nx, int ny, int nz, int leading_dim,
350                         double* rho, double* q0, double* output)
351 {
352     // We have to translate between the contiguous array rho
353     // and the padded array output.
354     int ix, iy, iz;
355     int ixyz_fftw;
356     for(ix = 0; ix < nx; ix++) {
357         for(iy = 0; iy < ny; iy++) {
358             for(iz = 0; iz < nz; iz++) {
359                 //ijk = iz + nz * (iy + (ny * ix));
360                 ixyz_fftw = (iz + leading_dim * (iy + ny * ix));
361                 vdwxc_evaluate_palpha_splines(kernel, *rho, *q0, output + ixyz_fftw * kernel->nalpha);
362                 rho++;
363                 q0++;
364             }
365         }
366     }
367 }
368 
369 
vdwxc_write_workbuffer(vdwxc_data data,char * fname)370 void vdwxc_write_workbuffer(vdwxc_data data, char *fname)
371 {
372     FILE *fd;
373     int ix, iy, iz;
374     int ixyz_fftw;
375     int alpha;
376     int nx = data->cell.Nlocal[0];
377     int ny = data->cell.Nlocal[1];
378     int nz = data->cell.Nlocal[2];
379     int leading_dim = data->gLDA;
380     double *work_ka = (double*)data->work_ka;
381     fd = fopen(fname, "w");
382     for(ix = 0; ix < nx; ix++) {
383         for(iy = 0; iy < ny; iy++) {
384             for(iz = 0; iz < nz; iz++) {
385                 //ijk = iz + nz * (iy + (ny * ix));
386                 ixyz_fftw = (iz + leading_dim * (iy + ny * ix));
387                 for(alpha=0; alpha < data->kernel.nalpha; alpha++) {
388                     fprintf(fd, "%.14e\n", work_ka[alpha + ixyz_fftw * data->kernel.nalpha]);
389                 }
390             }
391         }
392     }
393     fclose(fd);
394 }
395 
396 // XXXXX this is a copy of calculate_thetas.  Fix this!
vdwxc_compute_thetas_spin(struct vdwxc_kernel * kernel,int nx,int ny,int nz,int leading_dim,double * rho_up,double * rho_dn,double * q0,double * output)397 void vdwxc_compute_thetas_spin(struct vdwxc_kernel *kernel,
398                              int nx, int ny, int nz, int leading_dim,
399                              double *rho_up, double *rho_dn,
400                              double *q0, double *output)
401 {
402     // We have to translate between the contiguous array rho
403     // and the padded array output.
404     int ix, iy, iz;
405     int ixyz_fftw;
406     for(ix = 0; ix < nx; ix++) {
407         for(iy = 0; iy < ny; iy++) {
408             for(iz = 0; iz < nz; iz++) {
409                 //ijk = iz + nz * (iy + (ny * ix));
410                 double rho = (*rho_up) + (*rho_dn);
411                 ixyz_fftw = (iz + leading_dim * (iy + ny * ix));
412                 vdwxc_evaluate_palpha_splines(kernel, rho, *q0, output + ixyz_fftw * kernel->nalpha);
413                 rho_up++;
414                 rho_dn++;
415                 q0++;
416             }
417         }
418     }
419 }
420 
vdwxc_calculate_thetas(vdwxc_data data)421 void vdwxc_calculate_thetas(vdwxc_data data)
422 {
423     if(data->nspin == 1) {
424         vdwxc_compute_thetas(&data->kernel,
425                            data->cell.Nlocal[0],
426                            data->cell.Nlocal[1],
427                            data->cell.Nlocal[2],
428                            data->gLDA,
429                            data->rho, data->q0_g,
430                            (double*)data->work_ka);
431     } else if (data->nspin == 2) {
432         vdwxc_compute_thetas_spin(&data->kernel,
433                                 data->cell.Nlocal[0],
434                                 data->cell.Nlocal[1],
435                                 data->cell.Nlocal[2],
436                                 data->gLDA,
437                                 data->rho_up, data->rho_dn, data->q0_g,
438                                 (double*)data->work_ka);
439     } else {
440         assert(0);
441     }
442 }
443 
444 // Calculate potential and derivatives from F_ka.
potential(struct vdwxc_kernel * kernel,int nx,int ny,int nz,int gLDA,int Nglobal,double * rho,double * q0,double * rho_dq0drho_sg,double * rho_dq0dsigma_sg,double * F_ga,double * dedn,double * dedsigma)445 void potential(struct vdwxc_kernel *kernel, int nx, int ny, int nz, int gLDA, int Nglobal,
446                double *rho, double *q0, double *rho_dq0drho_sg, double *rho_dq0dsigma_sg,
447                double *F_ga, double *dedn, double *dedsigma)
448 {
449     START_TIMER(potential, "potential");
450     double prefactor = 1.0 / Nglobal;
451     //double* F_ga = ; // XXXXX
452     int ia_fftw;
453     int ixyz, ixyz_fftw;
454     int ix, iy, iz;
455     int Ng = nx * ny * nz;
456     //double integrals[2] = {0.0, 0.0};
457     int alpha;
458     // TODO: Move this to inner loop to save Memory
459 
460     double *p_a = (double *)malloc(sizeof(double) * kernel->nalpha);
461     double *dpdq_a = (double *)malloc(sizeof(double) * kernel->nalpha);
462 
463     for(ix = 0; ix < nx; ix++) {
464         for(iy = 0; iy < ny; iy++) {
465             for(iz = 0; iz < nz; iz++) {
466                 ixyz = iz + nz * (iy + (ny * ix));
467                 ixyz_fftw = (iz + gLDA * (iy + ny * ix));
468                 double rho_dq0drho = rho_dq0drho_sg[ixyz];
469                 double rho_dq0dsigma = rho_dq0dsigma_sg[ixyz];
470                 assert(ixyz < Ng);
471                 dedn[ixyz] = 0.0; // zero or not?  From perspective of calling code...
472                 dedsigma[ixyz] = 0.0; // zero or not?
473                 vdwxc_evaluate_palpha_splines(kernel, 1.0, q0[ixyz], p_a);
474                 vdwxc_evaluate_palpha_splines_derivative(kernel, q0[ixyz], dpdq_a);
475                 for(alpha=0; alpha < kernel->nalpha; alpha++) {
476                     ia_fftw = ixyz_fftw * kernel->nalpha + alpha;
477                     dedn[ixyz] += prefactor * F_ga[ia_fftw] *
478                         (p_a[alpha] + dpdq_a[alpha] * rho_dq0drho);
479                     //printf("%.14e\n", prefactor * F_ga[ia_fftw] *
480                     //       (p_a[alpha] + dpdq_a[alpha] * rho_dq0drho));
481                     dedsigma[ixyz] += prefactor * F_ga[ia_fftw] *
482                         dpdq_a[alpha] * rho_dq0dsigma;
483                 }
484                 // Here we can evaluate energy density as well:
485                 //
486                 //   epsilon(r) = (1/2) sum[alpha] n(r) p[alpha](r) F[alpha](r)
487                 //
488                 // n(r) times p[alpha](r) is actually theta[alpha](r),
489                 // which we do not have because we overwrote it.
490                 // But I don't know if anyone actually needs epsilon(r) for anything.
491 
492                 //integrals[0] += dedn_g[ixyz] * rho_g[ixyz];
493                 //integrals[1] += dedsigma_g[ixyz] * rho_g[ixyz];
494             }
495         }
496     }
497     free(p_a);
498     free(dpdq_a);
499     //integrals[0] *= data->cell.dV;
500     //integrals[1] *= data->cell.dV;
501     END_TIMER(potential, "potential");
502     // store integrals somehow for checking/debugging?
503 }
504 
505 // Calculate potential and derivatives from F_ka.
vdwxc_potential(vdwxc_data data)506 void vdwxc_potential(vdwxc_data data)
507 {
508     int Ng = data->Ng;
509     if(data->nspin == 1) {
510         potential(&data->kernel,
511                   data->cell.Nlocal[0], data->cell.Nlocal[1], data->cell.Nlocal[2],
512                   data->gLDA, data->Nglobal,
513                   data->rho, data->q0_g, data->rho_dq0drho_sg, data->rho_dq0dsigma_sg, (double*)data->work_ka,
514                   data->dedn, data->dedsigma);
515     } else {
516         assert(data->nspin == 2);
517         potential(&data->kernel,
518                   data->cell.Nlocal[0], data->cell.Nlocal[1], data->cell.Nlocal[2],
519                   data->gLDA, data->Nglobal,
520                   data->rho_up, data->q0_g,
521                   data->rho_dq0drho_sg, data->rho_dq0dsigma_sg,
522                   (double*)data->work_ka,
523                   data->dedn_up, data->dedsigma_up);
524         potential(&data->kernel,
525                   data->cell.Nlocal[0], data->cell.Nlocal[1], data->cell.Nlocal[2],
526                   data->gLDA, data->Nglobal,
527                   data->rho_dn, data->q0_g,
528                   data->rho_dq0drho_sg + Ng, data->rho_dq0dsigma_sg + Ng,
529                   (double*)data->work_ka,
530                   data->dedn_dn, data->dedsigma_dn);
531     }
532 }
533 
vdwxc_writefile(char * name,int N,double * array)534 void vdwxc_writefile(char *name, int N, double *array)
535 {
536     FILE *fd;
537     int i;
538     fd = fopen(name, "w");
539     for(i=0; i < N; i++) {
540         fprintf(fd, "%.14e\n", array[i]);
541     }
542     fclose(fd);
543 }
544 
vdwxc_dump(vdwxc_data data)545 void vdwxc_dump(vdwxc_data data)
546 {
547     if(data->nspin == 1) {
548         vdwxc_writefile("libvdwxc.rho.dat", data->Ng, data->rho);
549         vdwxc_writefile("libvdwxc.sigma.dat", data->Ng, data->sigma);
550         vdwxc_writefile("libvdwxc.dedn.dat", data->Ng, data->dedn);
551         vdwxc_writefile("libvdwxc.dedsigma.dat", data->Ng, data->dedsigma);
552     } else {
553         vdwxc_writefile("libvdwxc.rho_up.dat", data->Ng, data->rho_up);
554         vdwxc_writefile("libvdwxc.rho_dn.dat", data->Ng, data->rho_dn);
555         vdwxc_writefile("libvdwxc.sigma_up.dat", data->Ng, data->sigma_up);
556         vdwxc_writefile("libvdwxc.sigma_dn.dat", data->Ng, data->sigma_dn);
557         vdwxc_writefile("libvdwxc.dedn_up.dat", data->Ng, data->dedn_up);
558         vdwxc_writefile("libvdwxc.dedn_dn.dat", data->Ng, data->dedn_dn);
559         vdwxc_writefile("libvdwxc.dedsigma_up.dat", data->Ng, data->dedsigma_up);
560         vdwxc_writefile("libvdwxc.dedsigma_dn.dat", data->Ng, data->dedsigma_dn);
561     }
562     vdwxc_writefile("libvdwxc.q0.dat", data->Ng, data->q0_g);
563     vdwxc_writefile("libvdwxc.rho_dq0drho_sg.dat", data->Ng * data->nspin, data->rho_dq0drho_sg);
564     vdwxc_writefile("libvdwxc.rho_dq0dsigma_sg.dat", data->Ng * data->nspin, data->rho_dq0dsigma_sg);
565 }
566 
567 
vdwxc_calculate_q0_anyspin(vdwxc_data data)568 void vdwxc_calculate_q0_anyspin(vdwxc_data data)
569 {
570     if(data->nspin == 1) {
571         vdwxc_calculate_q0(data->Ng, data->Z_ab, data->q_cut,
572                            data->rho, data->sigma, data->q0_g,
573                            data->rho_dq0drho_sg, data->rho_dq0dsigma_sg);
574     } else if(data->nspin == 2) {
575         vdwxc_calculate_q0_spin(data->Ng, data->Z_ab, //data->q_cut,
576                                 data->rho_up, data->rho_dn,
577                                 data->sigma_up, data->sigma_dn,
578                                 data->q0_g,
579                                 data->rho_dq0drho_sg, data->rho_dq0dsigma_sg);
580     } else {
581         assert(0);
582     }
583 }
584 
vdwxc_get_q0(vdwxc_data data,double * q0)585 void vdwxc_get_q0(vdwxc_data data, double *q0)
586 {
587     int i;
588     for(i=0; i < data->Ng; i++) {
589         q0[i] = data->q0_g[i];
590     }
591 }
592 
593 // Calculate q0 and theta from density and gradient.
vdwxc_q0_and_theta(vdwxc_data data)594 void vdwxc_q0_and_theta(vdwxc_data data)
595 {
596     if (data->initialized != VDWXC_INITIALIZED)
597     {
598         data->errorcode = VDWXC_ERROR_UNINITIALIZED;
599         vdwxc_print_error(data);
600         return;
601     }
602 
603     START_TIMER(q0, "q0");
604     vdwxc_calculate_q0_anyspin(data);
605     END_TIMER(q0, "q0");
606     START_TIMER(thetas, "thetas");
607     vdwxc_calculate_thetas(data);
608     END_TIMER(thetas, "thetas");
609 }
610 
611 #define MODE_SERIAL
612 #define METH(name) name##_serial
613 #include "vdw_include.c"
614 #undef NAME
615 #undef METH
616 #undef MODE_SERIAL
617 
618 #define MODE_MPI
619 #define METH(name) name##_mpi
620 #include "vdw_include.c"
621 #undef NAME
622 #undef METH
623 #undef MODE_MPI
624 
625 #define MODE_PFFT
626 #define METH(name) name##_pfft
627 #include "vdw_include.c"
628 #undef NAME
629 #undef METH
630 #undef MODE_PFFT
631 
632 
vdwxc_check_convenience_pointers(vdwxc_data data)633 void vdwxc_check_convenience_pointers(vdwxc_data data)
634 {
635     if(data->nspin == 1) {
636         assert(data->rho != NULL);
637         assert(data->sigma != NULL);
638         assert(data->dedn != NULL);
639         assert(data->dedsigma != NULL);
640     } else if(data->nspin == 2) {
641         assert(data->rho_up != NULL);
642         assert(data->rho_dn != NULL);
643         assert(data->sigma_up != NULL);
644         assert(data->sigma_dn != NULL);
645         assert(data->dedn_up != NULL);
646         assert(data->dedn_dn != NULL);
647         assert(data->dedsigma_up != NULL);
648         assert(data->dedsigma_dn != NULL);
649     } else {
650         assert(0);
651     }
652 }
653 
654 
vdwxc_nullify_convenience_pointers(vdwxc_data data)655 void vdwxc_nullify_convenience_pointers(vdwxc_data data)
656 {
657     // Set all the pointers to input/output parameters to zero.
658     // We have not allocated any of these arrays so no freeing.
659     // This is standard cleanup after a single calculation.
660     data->rho = NULL;
661     data->rho_up = NULL;
662     data->rho_dn = NULL;
663     data->sigma = NULL;
664     data->sigma_up = NULL;
665     data->sigma_dn = NULL;
666     data->dedn = NULL;
667     data->dedn_up = NULL;
668     data->dedn_dn = NULL;
669     data->dedsigma = NULL;
670     data->dedsigma_up = NULL;
671     data->dedsigma_dn = NULL;
672 }
673 
674 
vdwxc_calculate_anyspin(vdwxc_data data)675 double vdwxc_calculate_anyspin(vdwxc_data data)
676 {
677     // Internal calculate function.  The convenience pointers must be allocated
678     // appropriately
679     if (data->initialized != VDWXC_INITIALIZED)
680     {
681         data->errorcode = VDWXC_ERROR_UNINITIALIZED;
682         vdwxc_print_error(data);
683         return 0.0;
684     }
685     if (data->cell.Nglobal[0] * data->cell.Nglobal[1] * data->cell.Nglobal[2] <= 0)
686     {
687         data->errorcode = VDWXC_ERROR_UNIT_CELL_NOT_SET;
688         vdwxc_print_error(data);
689         return 0.0;
690     }
691 
692     vdwxc_check_convenience_pointers(data);
693 
694     double energy;
695     switch(data->fft_type) {
696     case VDWXC_FFTW_SERIAL:
697         energy = vdwxc_calculate_serial(data);
698         break;
699     case VDWXC_FFTW_MPI:
700         energy = vdwxc_calculate_mpi(data);
701         break;
702     case VDWXC_PFFT:
703         energy = vdwxc_calculate_pfft(data);
704         break;
705     default:
706         energy = 0.0;
707         assert(0);
708     }
709 #if VDWXC_DEBUG
710     vdwxc_dump(data);
711 #endif
712     return energy;
713 }
714 
715 
716 // Calculate van der Waals energy plus energy/density derivative and
717 // energy/gradient-density derivative from density and gradient.
vdwxc_calculate(vdwxc_data data,double * rho_g,double * sigma_g,double * dedn_g,double * dedsigma_g)718 double vdwxc_calculate(vdwxc_data data,
719                        double *rho_g, double *sigma_g,
720                        double *dedn_g, double *dedsigma_g)
721 {
722     assert(data->nspin == 1);
723     data->rho = rho_g;
724     data->sigma = sigma_g;
725     data->dedn = dedn_g;
726     data->dedsigma = dedsigma_g;
727     double energy = vdwxc_calculate_anyspin(data);
728     vdwxc_nullify_convenience_pointers(data);
729     return energy;
730 }
731 
vdwxc_calculate_spin(vdwxc_data data,double * rho_up_g,double * rho_dn_g,double * sigma_up_g,double * sigma_dn_g,double * dedn_up_g,double * dedn_dn_g,double * dedsigma_up_g,double * dedsigma_dn_g)732 double vdwxc_calculate_spin(vdwxc_data data,
733                             double *rho_up_g, double *rho_dn_g,
734                             double *sigma_up_g, double *sigma_dn_g,
735                             double *dedn_up_g, double *dedn_dn_g,
736                             double *dedsigma_up_g, double *dedsigma_dn_g)
737 {
738     assert(data->nspin == 2);
739     data->rho_up = rho_up_g;
740     data->rho_dn = rho_dn_g;
741     data->sigma_up = sigma_up_g;
742     data->sigma_dn = sigma_dn_g;
743     data->dedn_up = dedn_up_g;
744     data->dedn_dn = dedn_dn_g;
745     data->dedsigma_up = dedsigma_up_g;
746     data->dedsigma_dn = dedsigma_dn_g;
747     double energy = vdwxc_calculate_anyspin(data);
748     vdwxc_nullify_convenience_pointers(data);
749     return energy;
750 }
751 
752 
vdwxc_fatal(char * msg)753 void vdwxc_fatal(char* msg)
754 {
755     printf("Fatal: %s", msg);
756     assert(0); // TODO: Do things well
757 }
758 
vdwxc_finalize(vdwxc_data * vdw)759 void vdwxc_finalize(vdwxc_data* vdw)
760 {
761     // We have to free all the arrays that we have allocated and zero
762     // the pointers.  We will zero the pointers in the end by setting
763     // all the data to zero before freeing the vdwxc_data.  We
764     // actually take a pointer *to* a vdwxc_data, so we can zero the
765     // vdwxc_data pointer as well.  (Like MPI_Finalize.)
766     vdwxc_data data = vdw[0];
767 #ifdef HAVE_PFFT
768     if(data->fft_type == VDWXC_PFFT) {
769         if (data->pfft_plan_r2c != NULL) {
770             pfft_destroy_plan(data->pfft_plan_r2c);
771         }
772         if (data->pfft_plan_c2r != NULL) {
773             pfft_destroy_plan(data->pfft_plan_c2r);
774         }
775         if (data->mpi_cart_comm != NULL) {
776             MPI_Comm_free(&data->mpi_cart_comm);
777         }
778         if (data->work_ka != NULL) {
779             pfft_free(data->work_ka);
780             // Set to null so we won't call fftw_free on it in a moment
781             data->work_ka = NULL;
782         }
783     }
784 #else
785     assert(data->fft_type != VDWXC_PFFT);
786 #endif
787     if (data->plan_r2c != NULL) {
788         fftw_destroy_plan(data->plan_r2c);
789     }
790     if (data->plan_c2r != NULL) {
791         fftw_destroy_plan(data->plan_c2r);
792     }
793     if (data->work_ka != NULL) {
794         fftw_free(data->work_ka);
795     }
796     if (data->q0_g != NULL) {
797         free(data->q0_g);
798     }
799     if (data->rho_dq0drho_sg != NULL) {
800         free(data->rho_dq0drho_sg);
801     }
802     if (data->rho_dq0dsigma_sg != NULL) {
803         free(data->rho_dq0dsigma_sg);
804     }
805     vdwxc_nullify(data);
806     free(data);
807     vdw[0] = NULL;
808 }
809