File: xc_rVV10.f90

package info (click to toggle)
espresso 6.7-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 311,040 kB
  • sloc: f90: 447,429; ansic: 52,566; sh: 40,631; xml: 37,561; tcl: 20,077; lisp: 5,923; makefile: 4,502; python: 4,379; perl: 1,219; cpp: 761; fortran: 618; java: 568; awk: 128
file content (1386 lines) | stat: -rw-r--r-- 49,155 bytes parent folder | download | duplicates (3)
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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
!
! Copyright (C) 2001-2009 Quantum ESPRESSO group
! This file is distributed under the terms of the
! GNU General Public License. See the file `License'
! in the root directory of the present distribution,
! or http://www.gnu.org/copyleft/gpl.txt .
!
!----------------------------------------------------------------------------

MODULE rVV10 
 
! This module is modeled after the vdW-DF implementation in
! Modules/xc_vdW_DF.f90. See that file for references, explanations, and
! many useful comments.

  USE kinds,             ONLY : dp
  USE constants,         ONLY : pi
  USE mp,                ONLY : mp_sum
  USE mp_bands,          ONLY : intra_bgrp_comm
  USE io_global,         ONLY : ionode, stdout
  USE fft_base,          ONLY : dfftp
  USE fft_interfaces,    ONLY : fwfft, invfft 
  USE control_flags,     ONLY : gamma_only, iverbosity

  IMPLICIT NONE
  SAVE

  real(dp), parameter :: epsr      = 1.d-12
  real(dp), parameter :: epsg      = 1.D-10
  integer,  parameter :: Nr_points = 1024
  real(dp), parameter :: r_max     = 100.0D0
  real(dp), parameter :: dr        = r_max/Nr_points
  real(dp), parameter :: dk        = 2.0D0*pi/r_max
  real(dp), parameter :: q_min     = 1.0D-4
  real(dp), parameter :: q_cut     = 0.5D0
  integer,  parameter :: Nqs       = 20
  real(dp), parameter, dimension(Nqs):: q_mesh= (/ q_min, 3.0D-4, 5.893850845618885D-4, 1.008103720396345D-3, &
            1.613958359589310D-3, 2.490584839564653D-3, 3.758997979748929D-3, 5.594297198907115D-3, &
            8.249838297569416D-3, 1.209220822453922D-2, 1.765183095571029D-2, 2.569619042667097D-2, &
            3.733577865542191D-2, 5.417739477463518D-2, 7.854595729872216D-2, 0.113805449932145D0,  &
            0.164823306218807D0 , 0.238642339497217D0 , 0.345452975434964D0 , q_cut /)

  real(dp) :: kernel( 0:Nr_points, Nqs, Nqs ), d2phi_dk2( 0:Nr_points, Nqs, Nqs )

  real(dp) :: b_value = 6.3_DP
  real(dp) :: C_value = 0.0093 

  private  
  public :: xc_rVV10,  &
            interpolate_kernel, &
            initialize_spline_interpolation, &
            rVV10_stress, b_value, &
            q_mesh, Nr_points, r_max, q_min, q_cut, Nqs

CONTAINS

!! #################################################################################################
!!                                       |             |
!!                                       |  xc_rVV10   |
!!                                       |_____________|

  SUBROUTINE xc_rVV10(rho_valence, rho_core, nspin, etxc, vtxc, v, b_value_)
    
    !! Modules to include
    !! -------------------------------------------------------------------------
    
    use gvect,           ONLY : ngm, g
    USE fft_base,        ONLY : dfftp
    USE cell_base,       ONLY : omega, tpiba
    !! -------------------------------------------------------------------------
    
    !! Local variables
    !! ----------------------------------------------------------------------------------
    !                                               _
    real(dp), intent(IN) :: rho_valence(:)         !     
    real(dp), intent(IN) :: rho_core(:)            !  PWSCF input variables 
    INTEGER,  INTENT(IN) :: nspin                  !
    real(dp), intent(inout) :: etxc, vtxc, v(:,:)  !_  
    real(DP),optional,intent(in) :: b_value_
   
    
    integer :: i_grid, theta_i, i_proc, I      
    real(dp) :: grid_cell_volume                
    
    real(dp), allocatable :: total_rho(:)
    real(dp), allocatable :: gradient_rho(:,:)  

    real(dp), allocatable :: q0(:)                                        
    real(dp), allocatable :: dq0_drho(:) 
    real(dp), allocatable :: dq0_dgradrho(:) 
    complex(dp), allocatable :: thetas(:,:)    
                                                
    real(dp) :: Ec_nl                          
    real(dp), allocatable :: potential(:)                                                 

    logical, save :: first_iteration = .true.  

    real(dp) ::  beta
 
 
    !! ---------------------------------------------------------------------------------------------
    !!   Begin calculations
  
    !call errore('xc_rVV10','rVV10 functional not implemented for spin polarized runs', size(rho_valence,2)-1)
    if (nspin>2) call errore('xc_vdW_DF','vdW functional not implemented for nspin > 2', nspin)

    if(present(b_value_)) b_value = b_value_
    
    !! --------------------------------------------------------------------------------------------------------

    call start_clock( 'rVV10' )

    beta = 0.0625d0 * (3.0d0 / (b_value**2.0D0) )**(0.75d0)


    !! Write parameters during the first iteratio
    !!
    if (first_iteration) then

       first_iteration = .false.
      
       CALL generate_kernel

       if (ionode .and. iverbosity > -1 ) then

          WRITE(stdout,'(/ /A )') "---------------------------------------------------------------------------------"
          WRITE(stdout,'(A)') "Carrying out rVV10 run using the following parameters:"
          WRITE(stdout,'(A,I6,A,I6,A,F8.3)') "Nqs =  ",Nqs, "    Nr_points =  ", Nr_points,"   r_max =  ",r_max
          WRITE(stdout, '(A, F8.5, A, F8.5 )') "b_value = ", b_value, "    beta = ", beta        
          WRITE(stdout,'(5X,"q_mesh =",4F12.8)') (q_mesh(I), I=1, 4)
          WRITE(stdout,'(13X,4F12.8)') (q_mesh(I), I=5, Nqs)
                 
          WRITE(stdout,'(/ A )') "Gradients computed in Reciprocal space"
          WRITE(stdout,'(/ A / /)') "---------------------------------------------------------------------------------"

          
       end if
       
    end if

    !! --------------------------------------------------------------------------------------------------
    !! Allocate arrays.   
    !! ---------------------------------------------------------------------------------------

    allocate( q0(dfftp%nnr) )
    allocate( gradient_rho(3,dfftp%nnr) )
    allocate( dq0_drho(dfftp%nnr), dq0_dgradrho(dfftp%nnr) )
    allocate( total_rho(dfftp%nnr) )
   
 
    !! ---------------------------------------------------------------------------------------
    !! Add together the valence and core charge densities to get the total charge density    
    !
    total_rho = rho_valence(:) + rho_core(:)

    !! -------------------------------------------------------------------------
    !! Here we calculate the gradient in reciprocal space using FFT
    !! -------------------------------------------------------------------------
    call fft_gradient_r2r( dfftp, total_rho, g, gradient_rho)

    !! -------------------------------------------------------------------------
    !! Get Q and all the derivatives
    !! -------------------------------------------------------------------------
    CALL get_q0_on_grid(total_rho, gradient_rho, q0, dq0_drho, dq0_dgradrho)

    !! ---------------------------------------------------------------------------------    

    allocate( thetas(dfftp%nnr, Nqs) )
    CALL get_thetas_on_grid(total_rho, q0, thetas)
    
    call start_clock( 'rVV10_energy')

    call vdW_energy(thetas, Ec_nl)
   
    Ec_nl = Ec_nl + beta * SUM(total_rho) * (omega/(dfftp%nr1x*dfftp%nr2x*dfftp%nr3x))
    etxc = etxc + Ec_nl

    call stop_clock( 'rVV10_energy')

    !! Print stuff if verbose run
    !!
    if (iverbosity > 0) then

       call mp_sum(Ec_nl,intra_bgrp_comm)
       if (ionode) write(*,'(/ / A /)') "     ----------------------------------------------------------------"
       if (ionode) write(*,'(A, F22.15 /)') "     Non-local correlation energy =         ", Ec_nl
       if (ionode) write(*,'(A /)') "     ----------------------------------------------------------------"
       
    end if

    !! ----------------------------------------------------------------------------------------
    !! Inverse Fourier transform the u_i(k) to get the u_i(r) 
    !!---------------------------------------------------------------------------------------

    call start_clock( 'rVV10_ffts')
    
    do theta_i = 1, Nqs
       CALL invfft('Rho', thetas(:,theta_i), dfftp) 
    end do

    call stop_clock( 'rVV10_ffts')

    !! -------------------------------------------------------------------------

    call start_clock( 'rVV10_v' )

    allocate( potential(dfftp%nnr) )
    call get_potential(q0, dq0_drho, dq0_dgradrho, total_rho, gradient_rho, thetas, potential)
    
    !! -------------------------------------------------------------------------
    !! Add beta
    !! ------------------------------------------------------------------------- 
    potential = potential + beta
    
    v(:,1) = v(:,1) + potential(:)
    if (nspin==2) v(:,2) = v(:,2) + potential(:) 

    call stop_clock( 'rVV10_v' )

    !! -----------------------------------------------------------------------
    !! The integral of rho(r)*potential(r) for the vtxc output variable
    !! --------------------------------------------------------------------

    grid_cell_volume = omega/(dfftp%nr1*dfftp%nr2*dfftp%nr3)  
 
    do i_grid = 1, dfftp%nnr
       vtxc = vtxc + grid_cell_volume*rho_valence(i_grid)*potential(i_grid)
    end do

    deallocate(potential)  

    !! ----------------------------------------------------------------------
    
    !! Deallocate all arrays.
    deallocate(q0, gradient_rho, dq0_drho, dq0_dgradrho, total_rho, thetas)  

    call stop_clock('rVV10')

  END SUBROUTINE xc_rVV10 


  !! #################################################################################################
  !!                   |                 |
  !!                   |  rVV10_STRESS   |
  !!                   |_________________|

  SUBROUTINE rVV10_stress (rho_valence, rho_core, nspin, sigma)

      USE fft_base,        ONLY : dfftp
      use gvect,           ONLY : ngm, g
      USE cell_base,       ONLY : tpiba

      implicit none

      real(dp), intent(IN) :: rho_valence(:)             !
      real(dp), intent(IN) :: rho_core(:)                ! Input variables 
      INTEGER,  INTENT(IN) :: nspin
      real(dp), intent(inout) :: sigma(3,3)              !  

      real(dp), allocatable :: gradient_rho(:,:)         !
      real(dp), allocatable :: total_rho(:)              ! Rho values

      real(dp), allocatable :: q0(:)                     !
      real(dp), allocatable :: dq0_drho(:)               ! Q-values
      real(dp), allocatable :: dq0_dgradrho(:)           !

      complex(dp), allocatable :: thetas(:,:)            ! Thetas
      integer :: i_proc, theta_i, l, m

      real(dp)  :: sigma_grad(3,3)
      real(dp)  :: sigma_ker(3,3)

      !! ---------------------------------------------------------------------------------------------
      !!   Tests
      !! --------------------------------------------------------------------------------------------------------

      if (nspin>2) call errore('rV10_stress',' rVV10 stress not implemented for nspin > 2', nspin)

      sigma(:,:) = 0.0_DP
      sigma_grad(:,:) = 0.0_DP
      sigma_ker(:,:) = 0.0_DP

      !! ---------------------------------------------------------------------------------------
      !! Allocations
      !! ---------------------------------------------------------------------------------------

      allocate( gradient_rho(3,dfftp%nnr) )
      allocate( total_rho(dfftp%nnr) )
      allocate( q0(dfftp%nnr) )
      allocate( dq0_drho(dfftp%nnr), dq0_dgradrho(dfftp%nnr) )
      allocate( thetas(dfftp%nnr, Nqs) )
 
      !! ---------------------------------------------------------------------------------------
      !! Charge
      !! ---------------------------------------------------------------------------------------
      total_rho = rho_valence(:) + rho_core(:)

      !! -------------------------------------------------------------------------
      !! Here we calculate the gradient in reciprocal space using FFT
      !! -------------------------------------------------------------------------
      call fft_gradient_r2r( dfftp, total_rho, g, gradient_rho)
      
      !! -------------------------------------------------------------------------------------------------------------
      !! Get q0.
      !! ---------------------------------------------------------------------------------

      CALL get_q0_on_grid(total_rho, gradient_rho, q0, dq0_drho, dq0_dgradrho)

      !! ---------------------------------------------------------------------------------
      !! Get thetas in reciprocal space.
      !! ---------------------------------------------------------------------------------

      CALL get_thetas_on_grid(total_rho, q0, thetas)

      !! ---------------------------------------------------------------------------------------
      !! Stress
      !! ---------------------------------------------------------------------------------------
      CALL rVV10_stress_gradient(total_rho, gradient_rho, q0, dq0_drho, &
                                  dq0_dgradrho, thetas, sigma_grad)

      CALL rVV10_stress_kernel(total_rho, q0, thetas, sigma_ker)

      sigma = - (sigma_grad + sigma_ker) 

      do l = 1, 3
         do m = 1, l - 1
            sigma (m, l) = sigma (l, m)
         enddo
      enddo

      deallocate( gradient_rho, total_rho, q0, dq0_drho, dq0_dgradrho, thetas )
 
   END SUBROUTINE rVV10_stress

   !! ###############################################################################################################
   !!                             |                          |
   !!                             |  rVV10_stress_gradient   |

   SUBROUTINE rVV10_stress_gradient (total_rho, gradient_rho, q0, dq0_drho, &
                                      dq0_dgradrho, thetas, sigma)

      !!-----------------------------------------------------------------------------------
      !! Modules to include
      !! ----------------------------------------------------------------------------------
      use gvect,                 ONLY : ngm, g, gg, igtongl, &
                                        gl, ngl, gstart
      USE fft_base,              ONLY : dfftp
      USE cell_base,             ONLY : omega, tpiba, alat, at, tpiba2

      !! ----------------------------------------------------------------------------------

      implicit none

      real(dp), intent(IN) :: total_rho(:)               !
      real(dp), intent(IN) :: gradient_rho(:, :)         ! Input variables
      real(dp), intent(inout) :: sigma(:,:)              !  
      real(dp), intent(IN) :: q0(:)                      !
      real(dp), intent(IN) :: dq0_drho(:)                ! 
      real(dp), intent(IN) :: dq0_dgradrho(:)            !
      complex(dp), intent(IN) :: thetas(:,:)             !

      complex(dp), allocatable :: u_vdW(:,:)             !

      real(dp), allocatable    :: d2y_dx2(:,:)           !
      real(dp) :: y(Nqs), dP_dq0, P, a, b, c, d, e, f    ! Interpolation
      real(dp) :: dq                                     !

      integer  :: q_low, q_hi, q, q1_i, q2_i , g_i       ! Loop and q-points

      integer  :: l, m
      real(dp) :: prefactor                              ! Final summation of sigma

      integer  :: i_proc, theta_i, i_grid, q_i, &        !
                  ix, iy, iz                             ! Iterators
      
      character(LEN=1) :: intvar
      real(dp) :: const

      !real(dp)       :: at_inverse(3,3)

      allocate( d2y_dx2(Nqs, Nqs) ) 
      allocate( u_vdW(dfftp%nnr, Nqs) )

      const = 1.0D0 / (3.0D0 * b_value**(3.0D0/2.0D0) * pi**(5.0D0/4.0D0) )
      sigma(:,:) = 0.0_DP
      prefactor = 0.0_DP
      
      !! --------------------------------------------------------------------------------------------------
      !! Get u in k-space.
      !! ---------------------------------------------------------------------------------------------------

      call thetas_to_uk(thetas, u_vdW)

      !! --------------------------------------------------------------------------------------------------
      !! Get u in real space.
      !! ---------------------------------------------------------------------------------------------------

      call start_clock( 'rVV10_ffts')

      do theta_i = 1, Nqs
         CALL invfft('Rho', u_vdW(:,theta_i), dfftp) 
      end do

      call stop_clock( 'rVV10_ffts')

      !! --------------------------------------------------------------------------------------------------
      !! Get the second derivatives for interpolating the P_i
      !! ---------------------------------------------------------------------------------------------------

      call initialize_spline_interpolation(q_mesh, d2y_dx2(:,:))

      !! ---------------------------------------------------------------------------------------------

      i_grid = 0

      !! ----------------------------------------------------------------------------------------------------
      !! Do the real space integration to obtain the stress component
      !! ----------------------------------------------------------------------------------------------------

      do i_grid = 1, dfftp%nnr

                  q_low = 1
                  q_hi = Nqs 

                  !
                  ! Figure out which bin our value of q0 is in in the q_mesh
                  !
                  do while ( (q_hi - q_low) > 1)

                      q = int((q_hi + q_low)/2)

                      if (q_mesh(q) > q0(i_grid)) then
                          q_hi = q
                      else 
                          q_low = q
                      end if

                  end do

                  if (q_hi == q_low) call errore('stress_vdW_gradient','qhi == qlow',1)

                  ! ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                  dq = q_mesh(q_hi) - q_mesh(q_low)

                  a = (q_mesh(q_hi) - q0(i_grid))/dq
                  b = (q0(i_grid) - q_mesh(q_low))/dq
                  c = (a**3 - a)*dq**2/6.0D0
                  d = (b**3 - b)*dq**2/6.0D0
                  e = (3.0D0*a**2 - 1.0D0)*dq/6.0D0
                  f = (3.0D0*b**2 - 1.0D0)*dq/6.0D0
                 
                  do q_i = 1, Nqs

                      y(:) = 0.0D0
                      y(q_i) = 1.0D0

                      dP_dq0 = (y(q_hi) - y(q_low))/dq - e*d2y_dx2(q_i,q_low) + f*d2y_dx2(q_i,q_hi)

                      ! +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                      
                      if (total_rho(i_grid) > epsr) then

                        prefactor = u_vdW(i_grid,q_i) * const * total_rho(i_grid)**(3.0D0/4.0D0) * dP_dq0 * dq0_dgradrho(i_grid) 
                      
                        do l = 1, 3
                          do m = 1, l
                                        
                              sigma (l, m) = sigma (l, m) -  prefactor * &
                                             (gradient_rho(l,i_grid) * gradient_rho(m,i_grid))
                           enddo
                        enddo
                     endif

                     !! +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

                 end do
           
      end do

      call mp_sum(  sigma, intra_bgrp_comm )

      call dscal (9, 1.d0 / (dfftp%nr1 * dfftp%nr2 * dfftp%nr3), sigma, 1)

      deallocate( d2y_dx2, u_vdW )

   END SUBROUTINE rVV10_stress_gradient



   !! ###############################################################################################################
   !!                      |                        |
   !!                      |  rVV10_stress_kernel   |
   !!                      |                        |

   SUBROUTINE rVV10_stress_kernel (total_rho, q0, thetas, sigma)

      !! Modules to include
      !! ----------------------------------------------------------------------------------
      use gvect,                 ONLY : ngm, g, gg, igtongl, gl, ngl, gstart 
      USE fft_base,              ONLY : dfftp
      USE cell_base,             ONLY : omega, tpiba, tpiba2
      USE constants, ONLY: pi

      implicit none
     
      real(dp), intent(IN) :: q0(:) 
      real(dp), intent(IN) :: total_rho(:)
      real(dp), intent(inout) :: sigma(3,3)                     !  
      complex(dp), intent(IN) :: thetas(:,:) 

      real(dp), allocatable :: dkernel_of_dk(:,:)               !
      
      integer               :: l, m, q1_i, q2_i , g_i           !
      real(dp)              :: g2, ngmod2, g_kernel, G_multiplier             ! 
      integer               :: last_g, theta_i

      allocate( dkernel_of_dk(Nqs, Nqs) )

      sigma(:,:) = 0.0_DP

      !! --------------------------------------------------------------------------------------------------
      !! Integration in g-space
      !! ---------------------------------------------------------------------------------------------------

      last_g = -1

      G_multiplier = 1.0D0
      if (gamma_only) G_multiplier = 2.0D0

      do g_i = gstart, ngm

          g2 = gg (g_i) * tpiba2
          g_kernel = sqrt(g2)

          if ( igtongl(g_i) .ne. last_g) then

             call interpolate_Dkernel_Dk(g_kernel, dkernel_of_dk)  ! Gets the derivatives
             last_g = igtongl(g_i)

          end if
          
          do q2_i = 1, Nqs
             do q1_i = 1, Nqs
                 do l = 1, 3
                     do m = 1, l

                     sigma (l, m) = sigma (l, m) - G_multiplier * 0.5 * &
                                     thetas(dfftp%nl(g_i),q1_i)*dkernel_of_dk(q1_i,q2_i)*conjg(thetas(dfftp%nl(g_i),q2_i))* &
                                     (g (l, g_i) * g (m, g_i) * tpiba2) / g_kernel 
                     end do
                 end do 
             enddo
         end do      

         if (g_i < gstart ) sigma(:,:) = sigma(:,:) / G_multiplier
         
      enddo

      call mp_sum(  sigma, intra_bgrp_comm )
      
      deallocate( dkernel_of_dk )
      
   END SUBROUTINE rVV10_stress_kernel


  !! ###############################################################################################################
  !!                                    |                  |
  !!                                    |  GET_Q0_ON_GRID  |
  !!                                    |__________________|

  SUBROUTINE get_q0_on_grid (total_rho, gradient_rho, q0, dq0_drho, dq0_dgradrho)
    
    USE fft_base,        ONLY : dfftp
    
    real(dp),  intent(IN)    :: total_rho(:), gradient_rho(:,:)   
    real(dp),  intent(OUT) :: q0(:), dq0_drho(:), dq0_dgradrho(:)

    integer,    parameter      :: m_cut = 12

    real(dp) :: dw0_dn, dk_dn, gmod2
    real(dp) :: mod_grad, wp2, wg2, w0, k   
    real(dp) :: q, exponent, dq0_dq                
    integer  :: i_grid, index, count=0 
 
    ! initialize q0-related arrays ... 
    q0(:) = q_cut
    dq0_drho(:) = 0.0_DP
    dq0_dgradrho(:) = 0.0_DP
  
    do i_grid = 1, dfftp%nnr
          
     if (total_rho(i_grid) > epsr) then

      gmod2 = gradient_rho(1,i_grid)**2 + &
              gradient_rho(2,i_grid)**2 + &
              gradient_rho(3,i_grid)**2
 
       !! Calculate some intermediate values needed to find q
       !! ------------------------------------------------------------------------------------
       mod_grad = sqrt(gmod2)

       wp2= 16.0_dp*pi*total_rho(i_grid)
       wg2 = 4.0_dp*C_value * (mod_grad/total_rho(i_grid))**4

       k = b_value*3.0_dp*pi* ((total_rho(i_grid)/(9.0_dp*pi))**(1.0_dp/6.0_dp))
       w0 = sqrt( wg2 + wp2/3.0_dp  )

       q = w0 / k 
     
       !! Here, we calculate q0 by saturating q according 
       !! ---------------------------------------------------------------------------------------

       exponent = 0.0_dp
       dq0_dq = 0.0_dp
     
       do index = 1, m_cut
        
          exponent = exponent + ( (q/q_cut)**index)/index
          dq0_dq = dq0_dq + ( (q/q_cut)**(index-1))
        
       end do

       q0(i_grid) = q_cut*(1.0_dp - exp(-exponent))
       dq0_dq = dq0_dq * exp(-exponent)

       !! ---------------------------------------------------------------------------------------

       if (q0(i_grid) < q_min) then
         q0(i_grid) = q_min
       end if

       !!---------------------------------Final values---------------------------------  

       dw0_dn = 1.0_dp/(2.0_dp*w0) * (16.0_dp/3.0_dp*pi - 4.0_dp*wg2 / total_rho(i_grid) )                
       dk_dn = k / ( 6.0_dp * total_rho(i_grid) )

       dq0_drho(i_grid) = dq0_dq / (k**2) * (dw0_dn * k - dk_dn * w0 )   
       IF ( gmod2 > epsr) THEN
          dq0_dgradrho(i_grid) = dq0_dq  / ( 2.0_dp*k*w0 ) * 4.0_dp*wg2 / (mod_grad**2)
       ELSE
          dq0_dgradrho(i_grid) = 0.0_dp
       ENDIF

     endif  
 
    end do

  end SUBROUTINE get_q0_on_grid


!! ###############################################################################################################
!!                                      |                      |
!!                                      |  GET_THETAS_ON_GRID  |


  SUBROUTINE get_thetas_on_grid (total_rho, q0_on_grid, thetas)

    real(dp), intent(in) :: total_rho(:), q0_on_grid(:)  

    complex(dp), intent(inout):: thetas(:,:)         

    integer :: i_grid, Ngrid_points                
    integer :: theta_i                        
  
    Ngrid_points = size(q0_on_grid)
  
    !! Interpolate the P_i polynomials 
    CALL spline_interpolation(q_mesh, q0_on_grid, thetas)
  
    !! Form the thetas where theta is defined as rho*p_i(q0)
    !! ------------------------------------------------------------------------------------

    do i_grid = 1, Ngrid_points
  
      if (total_rho(i_grid) > epsr ) then 
       thetas(i_grid,:) = thetas(i_grid,:) * (1.0 / (3.0 * sqrt(pi) &
         * ( b_value**(3.0/2.0) ) ) ) * (total_rho(i_grid) / pi)**(3.0/4.0)
      else
       thetas(i_grid,:) = 0.0d0
      endif
     
    end do

    !! ------------------------------------------------------------------------------------
  
    call start_clock( 'rVV10_ffts')

    do theta_i = 1, Nqs

     CALL fwfft ('Rho', thetas(:,theta_i), dfftp)
    end do

    call stop_clock( 'rVV10_ffts')
  
  END SUBROUTINE get_thetas_on_grid


!! ###############################################################################################################
!!                                     |                        | 
!!                                     |  SPLINE_INTERPOLATION  |
!!                                     |________________________|


SUBROUTINE spline_interpolation (x, evaluation_points, values)
  
  
  real(dp), intent(in) :: x(:), evaluation_points(:)          !! Input variables.  The x values used to form the interpolation
  !                                                           !! (q_mesh in this case) and the values of q0 for which we are 
  !                                                           !! interpolating the function
  
  complex(dp), intent(inout) :: values(:,:)                   !! An output array (allocated outside this routine) that stores the
  !                                                           !! interpolated values of the P_i (SOLER equation 3) polynomials.  The
  !                                                           !! format is values(grid_point, P_i)
  
  integer :: Ngrid_points, Nx                                 !! Total number of grid points to evaluate and input x points
  
  real(dp), allocatable, save :: d2y_dx2(:,:)                 !! The second derivatives required to do the interpolation
  
  integer :: i_grid, lower_bound, upper_bound, index, P_i     !! Some indexing variables
  
  real(dp), allocatable :: y(:)                               !! Temporary variables needed for the interpolation
  real(dp) :: a, b, c, d, dx                                  !!
 
 
  Nx = size(x)
  Ngrid_points = size(evaluation_points)
  

  !! Allocate the temporary array
  allocate( y(Nx) )

  !! If this is the first time this routine has been called we need to get the second
  !! derivatives (d2y_dx2) required to perform the interpolations.  So we allocate the
  !! array and call initialize_spline_interpolation to get d2y_dx2.
  !! ------------------------------------------------------------------------------------
  if (.not. allocated(d2y_dx2) ) then

     allocate( d2y_dx2(Nx,Nx) )
     call initialize_spline_interpolation(x, d2y_dx2)
     
  end if

  !! ------------------------------------------------------------------------------------
  
  
  do i_grid=1, Ngrid_points
     
     lower_bound = 1
     upper_bound = Nx
     
     do while ( (upper_bound - lower_bound) > 1 )
        
        index = (upper_bound+lower_bound)/2
        
        if ( evaluation_points(i_grid) > x(index) ) then
           lower_bound = index 
        else
           upper_bound = index
        end if
        
     end do
     
     dx = x(upper_bound)-x(lower_bound)
     
     a = (x(upper_bound) - evaluation_points(i_grid))/dx
     b = (evaluation_points(i_grid) - x(lower_bound))/dx
     c = ((a**3-a)*dx**2)/6.0D0
     d = ((b**3-b)*dx**2)/6.0D0

     
     do P_i = 1, Nx
        
        y = 0
        y(P_i) = 1
        
        values(i_grid, P_i) = a*y(lower_bound) + b*y(upper_bound) &
             + (c*d2y_dx2(P_i,lower_bound) + d*d2y_dx2(P_i, upper_bound))
        
     end do
     
  end do

  deallocate( y )

END SUBROUTINE spline_interpolation

  
!! ###############################################################################################################
!!                                |                                   |
!!                                |  INITIALIZE_SPLINE_INTERPOLATION  |
!!                                |___________________________________|


!! This routine is modeled after an algorithm from "Numerical Recipes in C" by Cambridge
!! University Press, pages 96-97.  It was adapted for Fortran and for the problem at hand.

SUBROUTINE initialize_spline_interpolation (x, d2y_dx2)
  
  real(dp), intent(in)  :: x(:)                    !! The input abscissa values 
  real(dp), intent(inout) :: d2y_dx2(:,:)          !! The output array (allocated outside this routine)
  !                                                !! that holds the second derivatives required for 
  !                                                !! interpolating the function

  integer :: Nx, P_i, index                        !! The total number of x points and some indexing
  !                                                !! variables

  real(dp), allocatable :: temp_array(:), y(:)     !! Some temporary arrays required.  y is the array
  !                                                !! that holds the funcion values (all either 0 or 1 here).

  real(dp) :: temp1, temp2                         !! Some temporary variables required


  
  Nx = size(x)
  
  allocate( temp_array(Nx), y(Nx) )

  do P_i=1, Nx
     

     !! In the Soler method, the polynomicals that are interpolated are Kroneker delta funcions
     !! at a particular q point.  So, we set all y values to 0 except the one corresponding to 
     !! the particular function P_i.
     !! ----------------------------------------------------------------------------------------

     y = 0.0D0
     y(P_i) = 1.0D0

     !! ----------------------------------------------------------------------------------------
     
     d2y_dx2(P_i,1) = 0.0D0
     temp_array(1) = 0.0D0
     
     do index = 2, Nx-1
        
        temp1 = (x(index)-x(index-1))/(x(index+1)-x(index-1))
        temp2 = temp1 * d2y_dx2(P_i,index-1) + 2.0D0
        d2y_dx2(P_i,index) = (temp1-1.0D0)/temp2
        temp_array(index) = (y(index+1)-y(index))/(x(index+1)-x(index)) &
             - (y(index)-y(index-1))/(x(index)-x(index-1))
        temp_array(index) = (6.0D0*temp_array(index)/(x(index+1)-x(index-1)) &
             - temp1*temp_array(index-1))/temp2
        
     end do
     
     d2y_dx2(P_i,Nx) = 0.0D0
     
     do index=Nx-1, 1, -1
        
        d2y_dx2(P_i,index) = d2y_dx2(P_i,index) * d2y_dx2(P_i,index+1) + temp_array(index)
        
     end do

  end do

  deallocate( temp_array, y)

end SUBROUTINE initialize_spline_interpolation


!! ###############################################################################################################
!!                                         |                    |
!!                                         | INTERPOLATE_KERNEL |
!!                                         |____________________|


!! This routine is modeled after an algorithm from "Numerical Recipes in C" by Cambridge
!! University Press, page 97.  Adapted for Fortran and the problem at hand.  This function is used to 
!! find the Phi_alpha_beta needed for equations 11 and 14 of SOLER.


subroutine interpolate_kernel(k, kernel_of_k)
  
  real(dp), intent(in) :: k                     !! Input value, the magnitude of the g-vector for the 
  !                                             !! current point.
  
  real(dp), intent(inout) :: kernel_of_k(:,:)   !! An output array (allocated outside this routine)
  !                                             !! that holds the interpolated value of the kernel
  !                                             !! for each pair of q points (i.e. the phi_alpha_beta 
  !                                             !! of the Soler method.

  integer :: q1_i, q2_i, k_i                    !! Indexing variables
 
  real(dp) :: A, B, C, D                        !! Intermediate values for the interpolation
  

  !! Check to make sure that the kernel table we have is capable of dealing with this
  !! value of k.  If k is larger than Nr_points*2*pi/r_max then we can't perform the 
  !! interpolation.  In that case, a kernel file should be generated with a larger number
  !! of radial points.
  !! -------------------------------------------------------------------------------------

  if ( k >= Nr_points*dk ) then
     
     write(*,'(A,F10.5,A,F10.5)') "k =  ", k, "     k_max =  ",Nr_points*dk
     call errore('interpolate kernel', 'k value requested is out of range',1)
     
  end if

  !! -------------------------------------------------------------------------------------
  
  kernel_of_k = 0.0D0
  
  !! This integer division figures out which bin k is in since the kernel
  !! is set on a uniform grid.
  k_i = int(k/dk)
  
  !! Test to see if we are trying to interpolate a k that is one of the actual
  !! function points we have.  The value is just the value of the function in that
  !! case.
  !! ----------------------------------------------------------------------------------------

  if (mod(k,dk) == 0) then
     
     do q1_i = 1, Nqs
        do q2_i = 1, q1_i
           
           kernel_of_k(q1_i, q2_i) = kernel(k_i,q1_i, q2_i)
           kernel_of_k(q2_i, q1_i) = kernel(k_i,q2_i, q1_i)
           
        end do
     end do
     
     return
     
  end if

  !! ----------------------------------------------------------------------------------------


  !! If we are not on a function point then we carry out the interpolation
  !! ----------------------------------------------------------------------------------------
  
  A = (dk*(k_i+1.0D0) - k)/dk
  B = (k - dk*k_i)/dk
  C = (A**3-A)*dk**2/6.0D0
  D = (B**3-B)*dk**2/6.0D0
  
  do q1_i = 1, Nqs
     do q2_i = 1, q1_i
        
        kernel_of_k(q1_i, q2_i) = A*kernel(k_i, q1_i, q2_i) + B*kernel(k_i+1, q1_i, q2_i) &
             +(C*d2phi_dk2(k_i, q1_i, q2_i) + D*d2phi_dk2(k_i+1, q1_i, q2_i))
        
        kernel_of_k(q2_i, q1_i) = kernel_of_k(q1_i, q2_i)
        
     end do
  end do

  !! ----------------------------------------------------------------------------------------

  
end subroutine interpolate_kernel


!! ###############################################################################################################
!!                                         |                        |
!!                                         | INTERPOLATE_DKERNEL_DK |
!!                                         |________________________|



subroutine interpolate_Dkernel_Dk(k, dkernel_of_dk)
  
  implicit none 

  real(dp), intent(in) :: k        
  real(dp), intent(inout) :: dkernel_of_dk(Nqs,Nqs)

  integer :: q1_i, q2_i, k_i  
  real(dp) :: A, B, dAdk, dBdk, dCdk, dDdk    
  

  !! -------------------------------------------------------------------------------------

  if ( k >= Nr_points*dk ) then
     
     write(*,'(A,F10.5,A,F10.5)') "k =  ", k, "     k_max =  ",Nr_points*dk
     call errore('interpolate kernel', 'k value requested is out of range',1)
     
  end if

  !! -------------------------------------------------------------------------------------

  dkernel_of_dk = 0.0D0

  k_i = int(k/dk)

  !! ----------------------------------------------------------------------------------------

  A = (dk*(k_i+1.0D0) - k)/dk
  B = (k - dk*k_i)/dk

  dAdk = -1.0D0/dk
  dBdk = 1.0D0/dk
  dCdk = -((3*A**2 -1.0D0)/6.0D0)*dk
  dDdk = ((3*B**2 -1.0D0)/6.0D0)*dk

  do q1_i = 1, Nqs
     do q2_i = 1, q1_i

        dkernel_of_dk(q1_i, q2_i) = dAdk*kernel(k_i, q1_i, q2_i) + dBdk*kernel(k_i+1, q1_i, q2_i) &
             + dCdk*d2phi_dk2(k_i, q1_i, q2_i) + dDdk*d2phi_dk2(k_i+1, q1_i, q2_i)

        dkernel_of_dk(q2_i, q1_i) = dkernel_of_dk(q1_i, q2_i)

     end do
  end do

  !! ----------------------------------------------------------------------------------------

  
end subroutine interpolate_Dkernel_Dk 

!! #################################################################################################
!!                                          |              |
!!                                          | thetas_to_uk |
!!                                          |______________|


subroutine thetas_to_uk(thetas, u_vdW)
  
  USE gvect,           ONLY : gg, ngm, igtongl, gl, ngl, gstart
  USE fft_base,        ONLY : dfftp
  USE cell_base,       ONLY : tpiba, omega

  complex(dp), intent(in) :: thetas(:,:)  
  complex(dp), intent(out) :: u_vdW(:,:)

  real(dp), allocatable :: kernel_of_k(:,:)    

  real(dp) :: g
  integer :: last_g, g_i, q1_i, q2_i, count, i_grid   

  complex(dp) :: theta(Nqs)        
  
  !! -------------------------------------------------------------------------------------------------
  
  allocate( kernel_of_k(Nqs, Nqs) )

  u_vdW(:,:) = CMPLX(0.0_DP,0.0_DP, kind=dp)
  
  last_g = -1 

  do g_i = 1, ngm
    
     if ( igtongl(g_i) .ne. last_g) then
        
        g = sqrt(gl(igtongl(g_i))) * tpiba
        call interpolate_kernel(g, kernel_of_k)
        last_g = igtongl(g_i)
        
     end if
     
     theta = thetas(dfftp%nl(g_i),:)
     
     do q2_i = 1, Nqs
        do q1_i = 1, Nqs
           u_vdW(dfftp%nl(g_i),q2_i) = u_vdW(dfftp%nl(g_i),q2_i) + kernel_of_k(q2_i,q1_i)*theta(q1_i)
        end do
     end do

  end do

  if (gamma_only) u_vdW(dfftp%nlm(:),:) = CONJG(u_vdW(dfftp%nl(:),:))
  
  deallocate( kernel_of_k )
     
  !! -----------------------------------------------------------------------------------------------
  
end subroutine thetas_to_uk

!! #################################################################################################
!!                                              |             |
!!                                              | VDW_ENERGY  |
!!                                              |_____________|

subroutine vdW_energy(thetas, vdW_xc_energy)
  
  USE gvect,           ONLY : gg, ngm, igtongl, gl, ngl, gstart
  USE fft_base,        ONLY : dfftp
  USE cell_base,       ONLY : tpiba, omega

  complex(dp), intent(inout) :: thetas(:,:)  
  real(dp), intent(out) :: vdW_xc_energy     

  real(dp), allocatable :: kernel_of_k(:,:)   

  real(dp) :: g                      
  integer  :: last_g               
  
  integer :: g_i, q1_i, q2_i, count, i_grid  

  complex(dp) :: theta(Nqs), thetam(Nqs), theta_g(Nqs)  
  real(dp)    :: G0_term, G_multiplier 

  complex(dp), allocatable :: u_vdw(:,:)   

  vdW_xc_energy = 0.0D0
 
  allocate (u_vdW(dfftp%nnr,Nqs))
  u_vdW(:,:) = CMPLX(0.0_DP,0.0_DP, kind=dp)

  allocate( kernel_of_k(Nqs, Nqs) )
  
  !!
  !! Here we should use gstart,ngm but all the cases are handeld by conditionals inside the loop
  !!
  G_multiplier = 1.0D0
  if (gamma_only) G_multiplier = 2.0D0

  last_g = -1 

  do g_i = 1, ngm
     
     if ( igtongl(g_i) .ne. last_g) then
        
        g = sqrt(gl(igtongl(g_i))) * tpiba
        call interpolate_kernel(g, kernel_of_k)
        last_g = igtongl(g_i)
        
     end if
     
     theta = thetas(dfftp%nl(g_i),:)

     do q2_i = 1, Nqs
        do q1_i = 1, Nqs
           u_vdW(dfftp%nl(g_i),q2_i)  = u_vdW(dfftp%nl(g_i),q2_i) + kernel_of_k(q2_i,q1_i)*theta(q1_i)
        end do
        vdW_xc_energy = vdW_xc_energy + G_multiplier * (u_vdW(dfftp%nl(g_i),q2_i)*conjg(theta(q2_i)))
     end do
     
     if (g_i < gstart ) vdW_xc_energy = vdW_xc_energy / G_multiplier

  end do

  if (gamma_only) u_vdW(dfftp%nlm(:),:) = CONJG(u_vdW(dfftp%nl(:),:))

  !! Final value 
  vdW_xc_energy = 0.5D0 * omega * vdW_xc_energy   
  
  deallocate( kernel_of_k )
  thetas(:,:) = u_vdW(:,:)
  deallocate (u_vdW)
  !! ---------------------------------------------------------------------------------------------------
  
end subroutine vdW_energy


!! ###############################################################################################################
!!                                             |                 |
!!                                             |  GET_POTENTIAL  |
!!                                             |_________________|

  subroutine get_potential(q0, dq0_drho, dq0_dgradrho, total_rho, gradient_rho, u_vdW, potential)

    use gvect,               ONLY : g
    USE fft_base,            ONLY : dfftp
    USE cell_base,           ONLY : alat, tpiba

    real(dp), intent(in) ::  q0(:), gradient_rho(:,:)   
    real(dp), intent(in) :: dq0_drho(:), dq0_dgradrho(:)
    real(dp), intent(in) :: total_rho(:)
    complex(dp), intent(in) :: u_vdW(:,:)    
    real(dp), intent(inout) :: potential(:)
  
    real(dp), allocatable, save :: d2y_dx2(:,:)         

    integer :: i_grid, P_i,icar                         
    integer :: q_low, q_hi, q                           
    real(dp) :: dq, a, b, c, d, e, f                    
    real(dp) :: y(Nqs), dP_dq0, P                       
    !                                                   

    real(dp), allocatable ::h_prefactor(:)
    complex(dp), allocatable ::h(:)
    real(dp) :: dtheta_dn, dtheta_dgradn

    real(dp) :: const 


    allocate (h_prefactor(dfftp%nnr),h(dfftp%nnr))

    const = 1.0D0 / (3.0D0 * b_value**(3.0D0/2.0D0) * pi**(5.0D0/4.0D0) )
    potential = 0.0D0
    h_prefactor   = 0.0D0

    !! -------------------------------------------------------------------------------------------
    !! Get the second derivatives of the P_i functions for interpolation
    !! ---------------------------------------------------------------------------------------------

    if (.not. allocated( d2y_dx2) ) then
     
     allocate( d2y_dx2(Nqs, Nqs) )
     call initialize_spline_interpolation(q_mesh, d2y_dx2(:,:))
     
    end if
  
    !! ---------------------------------------------------------------------------------------------
  

    do i_grid = 1,dfftp%nnr
           
     q_low = 1
     q_hi = Nqs 

     ! Figure out which bin our value of q0 is in in the q_mesh
     ! +++++++++++++++++++++++++++++++++++++++++++++++++++++++++

     do while ( (q_hi - q_low) > 1)
              
        q = int((q_hi + q_low)/2)
              
        if (q_mesh(q) > q0(i_grid)) then
           q_hi = q
        else 
           q_low = q
        end if
              
     end do
           
     if (q_hi == q_low) call errore('get_potential','qhi == qlow',1)
           
     ! ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

     dq = q_mesh(q_hi) - q_mesh(q_low)
           
     a = (q_mesh(q_hi) - q0(i_grid))/dq
     b = (q0(i_grid) - q_mesh(q_low))/dq
     c = (a**3 - a)*dq**2/6.0D0
     d = (b**3 - b)*dq**2/6.0D0
     e = (3.0D0*a**2 - 1.0D0)*dq/6.0D0
     f = (3.0D0*b**2 - 1.0D0)*dq/6.0D0
           
     do P_i = 1, Nqs
        y = 0.0D0
        y(P_i) = 1.0D0
              
        dP_dq0 = (y(q_hi) - y(q_low))/dq - e*d2y_dx2(P_i,q_low) + f*d2y_dx2(P_i,q_hi)
        P = a*y(q_low) + b*y(q_hi) + c*d2y_dx2(P_i,q_low) + d*d2y_dx2(P_i,q_hi)
 

        !! IF THE CHARGE DENSITY IS NEGATIVE WE PUT POTENTIAL = 0, OUTSIDE THE SUBROUTINE WE ADD BETA. 
        if (total_rho(i_grid) > epsr) then

          dtheta_dn = const * (3.0D0/4.0D0) / (total_rho(i_grid)**(1.0D0/4.0D0))  * P + &
                      const * total_rho(i_grid)**(3.0D0/4.0D0) *  dP_dq0 * dq0_drho(i_grid)
          dtheta_dgradn = const * total_rho(i_grid)**(3.0D0/4.0D0) * dP_dq0 * dq0_dgradrho(i_grid)

          potential(i_grid) = potential(i_grid) + u_vdW(i_grid,P_i)* dtheta_dn 

          if (q0(i_grid) .ne. q_mesh(Nqs)) then
            h_prefactor(i_grid) = h_prefactor(i_grid) +  u_vdW(i_grid,P_i)* dtheta_dgradn
          end if

        end if

     end do
    end do

    do icar = 1,3
      h(:) = CMPLX( h_prefactor(:)*gradient_rho(icar,:), 0.0_DP, kind=dp)
      CALL fwfft ('Rho', h, dfftp) 
      h(dfftp%nl(:)) = CMPLX(0.0_DP,1.0_DP,kind=dp)*tpiba*g(icar,:)*h(dfftp%nl(:))
      if (gamma_only) h(dfftp%nlm(:)) = CONJG(h(dfftp%nl(:)))
      CALL invfft ('Rho', h, dfftp) 
      potential(:) = potential(:) - REAL(h(:))
    end do

    !! ------------------------------------------------------------------------------------------------------------------------
    deallocate (h_prefactor,h)

  end subroutine get_potential


!! ###############################################################################################################
!!                                                 |                   |
!!                                                 |  generate_kernel  |
!!                                                 |___________________|
SUBROUTINE generate_kernel

  implicit none

  integer  :: q1_i, q2_i, r_i                   ! Indexing variables
  real(dp) :: d1, d2                            ! Intermediate values


  kernel    = 0.0D0
  d2phi_dk2 = 0.0D0

  do q1_i = 1, Nqs
     do q2_i = 1, q1_i

        do r_i = 1, Nr_points
           d1 = q_mesh(q1_i) * (dr * r_i)**2    ! Different definition of d1 and d2 for vv10
           d2 = q_mesh(q2_i) * (dr * r_i)**2    ! Different definition of d1 and d2 for vv10
           kernel(r_i, q1_i, q2_i) = -24.0D0 / ( ( d1+1.0 ) * ( d2+1.0 ) * ( d1+d2+2.0 ) )
        end do

        call radial_fft( kernel(:, q1_i, q2_i) )
        call set_up_splines( kernel(:, q1_i, q2_i), d2phi_dk2(:, q1_i, q2_i) )

        kernel    (:, q2_i, q1_i) = kernel   (:, q1_i, q2_i)
        d2phi_dk2 (:, q2_i, q1_i) = d2phi_dk2(:, q1_i, q2_i)

     end do
  end do

END SUBROUTINE generate_kernel


!! ###############################################################################################################
!!                                                 |              |
!!                                                 |  radial_fft  |
!!                                                 |______________|
SUBROUTINE radial_fft(phi)

  REAL(DP), INTENT(INOUT) :: phi(0:Nr_points)
  REAL(DP)                :: phi_k(0:Nr_points)
  INTEGER                 :: k_i, r_i
  REAL(DP)                :: r, k
  
  
  phi_k = 0.0D0
  
  DO r_i = 1, Nr_points
     r        = r_i * dr
     phi_k(0) = phi_k(0) + phi(r_i)*r**2
  END DO
  
  phi_k(0) = phi_k(0) - 0.5D0 * (Nr_points*dr)**2 * phi(Nr_points)
  
  DO k_i = 1, Nr_points
     k = k_i * dk
     DO r_i = 1, Nr_points
        r          = r_i * dr
        phi_k(k_i) = phi_k(k_i) + phi(r_i) * r * SIN(k*r) / k
     END DO
     phi_k(k_i) = phi_k(k_i) - 0.5D0 * phi(Nr_points) * r * SIN(k*r) / k
  END DO
  
  phi = 4.0D0 * pi * phi_k * dr

END SUBROUTINE radial_fft


!! ###############################################################################################################
!!                                              |                  |
!!                                              |  set_up_splines  |
!!                                              |__________________|
SUBROUTINE set_up_splines(phi, D2)

  REAL(DP), INTENT(IN)    :: phi(0:Nr_points)
  REAL(DP), INTENT(INOUT) :: D2(0:Nr_points)
  REAL(DP), ALLOCATABLE   :: temp_array(:)
  REAL(DP)                :: temp_1, temp_2
  INTEGER                 :: r_i
  
  
  ALLOCATE( temp_array(0:Nr_points) )
  
  D2         = 0
  temp_array = 0
  
  DO r_i = 1, Nr_points - 1
     temp_1  = DBLE(r_i - (r_i - 1))/DBLE( (r_i + 1) - (r_i - 1) )
     temp_2  = temp_1 * D2(r_i-1) + 2.0D0
     D2(r_i) = (temp_1 - 1.0D0)/temp_2
     temp_array(r_i) = ( phi(r_i+1) - phi(r_i))/DBLE( dk*((r_i+1) - r_i) ) - &
          ( phi(r_i) - phi(r_i-1))/DBLE( dk*(r_i - (r_i-1)) )
     temp_array(r_i) = (6.0D0*temp_array(r_i)/DBLE( dk*((r_i+1) - (r_i-1)) )-&
          temp_1*temp_array(r_i-1))/temp_2
  END DO
  
  D2(Nr_points) = 0.0D0
  DO  r_i = Nr_points-1, 0, -1
     D2(r_i) = D2(r_i)*D2(r_i+1) + temp_array(r_i)
  END DO
  
  DEALLOCATE( temp_array )

END SUBROUTINE set_up_splines


END MODULE rVV10