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