1!!------ Forming basin for specific real space function, and integrate real space functions in the basins 2!!------ The method is adapted from J. Phys.: Condens. Matter 21 (2009) 084204 3!Grid must be ortho 4subroutine basinana 5use defvar 6use basinintmod 7use util 8implicit real*8(a-h,o-z) 9integer walltime1,walltime2 10integer :: igridmethod=3 11integer,allocatable :: attconvold(:),realattconv(:),usercluslist(:) 12character basinfilename*200,selectyn,c80tmp*80,ctmp1*20,ctmp2*20,ctmp3*20,ctmp4*20,c1000tmp*1000,c200tmp*200 13real*8 :: threslowvalatt=1D-5 14ishowattlab=1 15ishowatmlab=0 16ishowatt=1 17!Don't show topology analysis results to avoid confusion 18ishowCPlab=0 19ishowpathlab=1 20ishow3n3=0 21ishow3n1=0 22ishow3p1=0 23ishow3p3=0 24!When enter this module, we reset the whole state, which is signified by numatt. Because the user may calculate grid data at external functions, 25!and hence messed up grid setting (orgx/y/z,dx/y/z...), so all of the functions in this module that rely on grid setting will be totally wrong 26numatt=0 27 28call delvirorb(1) 29 30do while(.true.) 31 write(*,*) 32 write(*,*) " ============= Basin analysis =============" 33 write(*,*) "-10 Return to main menu" 34 if (numatt>0) write(*,*) "-6 Set parameter for attractor clustering or manually perform clustering" 35 if (numatt==0) write(*,*) "-6 Set parameter for attractor clustering" 36 if (numatt>0) write(*,*) "-5 Export basins as cube file" 37 if (numatt>0) write(*,*) "-4 Export attractors as pdb file" 38 if (numatt>0) write(*,*) "-3 Show information of attractors" 39 if (numatt>0) write(*,*) "-2 Measure distances, angles and dihedral angles between attractors or atoms" 40 if (igridmethod==1) write(*,*) "-1 Select the method for generating basins, current: On-grid" 41 if (igridmethod==2) write(*,*) "-1 Select the method for generating basins, current: Near-grid" 42 if (igridmethod==3) write(*,*) "-1 Select the method for generating basins, current: Near-grid with refinement" 43 if (numatt>0) write(*,*) " 0 Visualize attractors and basins" 44 if (numatt>0) then 45 write(*,*) " 1 Regenerate basins and relocate attractors" 46 else 47 write(*,*) " 1 Generate basins and locate attractors" 48 end if 49 if (numatt>0) then 50 write(*,*) " 2 Integrate real space functions in the basins" 51 write(*,*) " 3 Calculate electric multipole moments in the basins" 52 write(*,*) " 4 Calculate localization index and delocalization index for the basins" 53 write(*,*) " 5 Output orbital overlap matrix in basins to BOM.txt in current folder" 54! write(*,*) " 6 Integrate real space functions in the basins with multi-level refinement" 55 if (ifuncbasin==1) then 56 write(*,*) " 7 Integrate real space functions in AIM basins with mixed type of grids" 57 write(*,*) " 8 Calculate electric multipole moments in AIM basins with mixed type of grids" 58 write(*,*) " 9 Obtain atomic contribution to population of external basins" 59 end if 60 end if 61 read(*,*) isel 62 if (numatt==0.and.(isel==-5.or.isel==-4.or.isel==-3.or.isel==-2.or.isel==0.or.isel==2.or.isel==3.or.isel==4.or.isel==5)) then 63 write(*,*) "Error: You should use option 1 to generate basins first!" 64 cycle 65 end if 66 67 if (isel==-10) then 68 idrawbasinidx=-10 !Don't display interbasin in any other GUI 69 ishowattlab=0 !Don't show attractors in other GUIs 70 ishowatt=1 71 return 72 73 else if (isel==-6) then 74 do while(.true.) 75 write(*,*) "0 Return" 76 write(*,"(a,f14.5,a)") " 1 Set relative value difference criterion, current:",valcritclus*100,"%" 77 write(*,"(a,i3)") " 2 Set the multiplier for distance criterion, current:",mergeattdist 78 if (numatt>=2) write(*,*) "3 Cluster specified attractors" 79 read(*,*) isel2 80 if (isel2==0) then 81 exit 82 else if (isel2==1) then 83 write(*,"(a)") " Input a value in percentage, e.g. 0.5" 84 write(*,"(a)") " Note: Inputting X means if the value difference between two attractors is less than X%, & 85 then they will be regarded as degenerate and may be clustered according to distance criterion" 86 read(*,*) valcritclus 87 valcritclus=valcritclus/100D0 88 else if (isel2==2) then 89 write(*,"(a)") " Input a value, e.g. 6" 90 write(*,"(a)") " Note: Inputting P means for any two attractors that have relative value difference less than the one set by option 1, & 91 if their interval is less than P*sqrt(dx^2+dy^2+dz^2), & 92 where dx,dy,dz are grid spacing in X,Y,Z,then they will be clustered together. If you want to nullify the clustering step after generating & 93 basins, simply set this value to 0" 94 read(*,*) mergeattdist 95 else if (isel2==3) then 96 if (numatt<2) then 97 write(*,*) "Error: At least two attractors must be presented!" 98 cycle 99 end if 100 write(*,*) "Input the attractors you want to cluster, e.g. 4,5,8,9,22,21" 101 read(*,"(a)") c1000tmp 102 call str2arr(c1000tmp,ntobeclus) !Find how many terms 103 if (allocated(attconvold)) deallocate(attconvold,realattconv,usercluslist) 104 allocate(attconvold(-2:numatt),realattconv(-2:numrealatt),usercluslist(ntobeclus)) 105 realattconv(-2)=-2 106 realattconv(-1)=-1 107 realattconv(0)=0 108 call str2arr(c1000tmp,ntobeclus,usercluslist) 109 call sorti4(usercluslist) 110 attconvold=attconv 111 idesrealatt=usercluslist(1) 112 do itmp=2,ntobeclus 113 irealatt=usercluslist(itmp) 114 do jtmp=1,nrealatthas(irealatt) 115 iatt=realatttable(irealatt,jtmp) 116 attconv(iatt)=idesrealatt 117 end do 118 end do 119 call clusdegenatt(1) 120 realattconv(1)=1 121 do itmp=1,numatt !Build conversion relationship between previous real attractors and new real attractors 122 if (itmp/=1) then 123 if (attconvold(itmp)/=attconvold(itmp-1)) realattconv(attconvold(itmp))=attconv(itmp) 124 end if 125 end do 126 do iz=2,nz-1 127 do iy=2,ny-1 128 do ix=2,nx-1 129 gridbas(ix,iy,iz)=realattconv(gridbas(ix,iy,iz)) 130 end do 131 end do 132 end do 133 call detectinterbasgrd(6) !Detect interbasin grids 134 write(*,*) "Done!" 135 write(*,*) 136 end if 137 end do 138 139 else if (isel==-5) then !Outout cube file for basins for visualizing interbasin surface 140 write(*,*) "Input the index range of basin" 141 write(*,"(a)") " Inputting 4,6 will output basin 4,5,6 to basin0004.cub, basin0005.cub, basin0006.cub to current folder. Inputting 5,5 will only output basin 5" 142 write(*,"(a)") " Inputting 0,0, will output all basins into basins.cub, the grid value corresponds to basin index" 143 read(*,*) ilow,ihigh 144 if (ilow/=0) then 145 write(*,*) "If output internal region of the basin? (y/n)" 146 read(*,*) selectyn 147 end if 148 if (allocated(cubmattmp)) deallocate(cubmattmp) 149 allocate(cubmattmp(nx,ny,nz)) 150 do ibas=ilow,ihigh 151 if (ilow/=0) then 152 write(basinfilename,"('basin',i4.4,'.cub')") ibas 153 write(*,"(' Outputting basin',i8,' as ',a)") ibas,trim(basinfilename) 154 cubmattmp=gridbas(:,:,:) 155 where(cubmattmp/=ibas) 156 cubmattmp=0 157 elsewhere 158 cubmattmp=1 159 end where 160 if (selectyn=='n') where (interbasgrid .eqv. .false.) cubmattmp=0 161 else 162 write(*,*) "Outputting basin.cub..." 163 write(basinfilename,"('basin.cub')") 164 cubmattmp=gridbas(:,:,:) 165 end if 166 open(10,file=basinfilename,status="replace") 167 write(10,"(' Generated by Multiwfn')") 168 write(10,"(' Totally ',i12,' grid points')") nx*ny*nz 169 write(10,"(i5,3f12.6)") ncenter,orgx,orgy,orgz 170 write(10,"(i5,3f12.6)") nx,dx,0.0,0.0 171 write(10,"(i5,3f12.6)") ny,0.0,dy,0.0 172 write(10,"(i5,3f12.6)") nz,0.0,0.0,dz 173 do icenter=1,ncenter 174 write(10,"(i5,4f12.6)") a(icenter)%index,a(icenter)%charge,a(icenter)%x,a(icenter)%y,a(icenter)%z 175 end do 176 do ix=1,nx 177 do iy=1,ny 178 write(10,"(6(1PE13.5))",advance="no") cubmattmp(ix,iy,1:nz) 179 write(10,*) 180 end do 181 end do 182 close(10) 183 end do 184 deallocate(cubmattmp) 185 if (ilow/=0) then 186 write(*,"(a)") " Done! Cube files for the selected basins have been outputted to current folder" 187 write(*,"(a)") " The value 1 and 0 in the files denote the corresponding point belongs and not belongs to the basin, & 188 respectively. You can plot such as the isosurface with isovalue=0.5 to visualize the basins" 189 else 190 write(*,"(a)") " Done! basin.cub has been outputted to current folder. The grid values correspond to basin index" 191 end if 192 193 else if (isel==-4) then 194 open(10,file="attractors.pdb",status="replace") 195 do iatt=1,numatt 196 write(10,"(a6,i5,1x,a4,1x,a3, 1x,a1,i4,4x,3f8.3,2f6.2,10x,a2)") "HETATM",iatt,' '//"C "//' ',"ATT",'A',attconv(iatt),attxyz(iatt,:)*b2a,1.0,0.0,"C " 197 end do 198 close(10) 199 write(*,*) "Done, all attractors have been exported to attractors.pdb in current folder" 200 write(*,*) "Note: The residue indices in the pdb file correspond to attractor indices" 201 202 else if (isel==-3) then 203 write(*,*) " Attractor X,Y,Z coordinate (Angstrom) Value" 204 do irealatt=1,numrealatt 205 write(*,"(i8,3f14.8,1E18.9)") irealatt,realattxyz(irealatt,1:3)*b2a,realattval(irealatt) 206 end do 207 do irealatt=1,numrealatt 208 if (nrealatthas(irealatt)/=1) then 209 write(*,*) 210 write(*,"(' The members of degenerate attractor',i6,':')") irealatt 211 do itmp=1,nrealatthas(irealatt) 212 iatt=realatttable(irealatt,itmp) 213 write(*,"(i8,' XYZ:',3f13.7,' Value:',1E18.9)") iatt,attxyz(iatt,:)*b2a,attval(iatt) 214 end do 215 end if 216 end do 217 218 else if (isel==-2) then 219 write(*,*) "q = Quit. Selection method:" 220 write(*,*) "a? = Atom ?" 221 write(*,*) "c? = Attractor ? (If the attractor is degenerate, average coordinate is used)" 222 write(*,*) "d? = Attractor ? (Only for degenerate attractors, cycle each of its members)" 223 write(*,*) "For example:" 224 write(*,*) """a1 c3"" returns the distance between atom1 and att.3" 225 write(*,*) """a4 a2"" returns the distance between atom4 and atom2" 226 write(*,*) """c6 a2 a5"" returns the angle of att.6-atom2-atom5" 227 write(*,*) """c2 c4 a3 c7"" returns the dihedral angle of att.2-att.4-atom3-att.7" 228 write(*,*) """d3 a1"" returns the distance between members of att.3 and atom1" 229 write(*,"(a)") " ""d3 a1 c2"" returns the the angle of (members of att.3)--atom1--att.2, & 230 meanwhile outputs the vertical distance from (members of att.3) to the line linking atom1 and att.2" 231 do while(.true.) 232 read(*,"(a)") c80tmp 233 c80tmp=adjustl(c80tmp) 234 imeasure=0 235 do ichar=1,len_trim(c80tmp) !imeasure=1/2/3: measure distance,angle,dihedral 236 if (c80tmp(ichar:ichar)==','.or.c80tmp(ichar:ichar)==' ') imeasure=imeasure+1 237 end do 238 nelement=0 !Validate "d" selection 239 do ichar=1,len_trim(c80tmp) 240 if (c80tmp(ichar:ichar)=='d') nelement=nelement+1 241 end do 242 if (nelement/=0) then 243 if (c80tmp(1:1)/='d'.or.nelement>1) then 244 write(*,*) "Error: ""d"" type of selection can only occur once and must be the first term" 245 cycle 246 else if (imeasure==3) then 247 write(*,*) "Error: Dihedral angle calculation doesn't support ""d"" type of selection" 248 cycle 249 end if 250 end if 251 252 if (c80tmp(1:1)=='q') then 253 exit 254 else if (imeasure==1.or.imeasure==2.or.imeasure==3) then 255 if (imeasure==1) read(c80tmp,*) ctmp1,ctmp2 !Read two terms 256 if (imeasure==2) read(c80tmp,*) ctmp1,ctmp2,ctmp3 !Read three terms 257 if (imeasure==3) read(c80tmp,*) ctmp1,ctmp2,ctmp3,ctmp4 !Read four terms 258 259 if (ctmp1(1:1)=='a') then 260 read(ctmp1(2:),*) iatm 261 tmpx1=a(iatm)%x 262 tmpy1=a(iatm)%y 263 tmpz1=a(iatm)%z 264 else if (ctmp1(1:1)=='c') then 265 read(ctmp1(2:),*) irealatt 266 tmpx1=realattxyz(irealatt,1) 267 tmpy1=realattxyz(irealatt,2) 268 tmpz1=realattxyz(irealatt,3) 269 else if (ctmp1(1:1)=='d') then 270 read(ctmp1(2:),*) irealattde 271 end if 272 if (ctmp2(1:1)=='a') then 273 read(ctmp2(2:),*) iatm 274 tmpx2=a(iatm)%x 275 tmpy2=a(iatm)%y 276 tmpz2=a(iatm)%z 277 else if (ctmp2(1:1)=='c') then 278 read(ctmp2(2:),*) irealatt 279 tmpx2=realattxyz(irealatt,1) 280 tmpy2=realattxyz(irealatt,2) 281 tmpz2=realattxyz(irealatt,3) 282 end if 283 284 if (imeasure==1) then 285 if (ctmp1(1:1)/='d') then 286 write(*,"(' The distance is',f12.6,' Bohr (',f12.6 ' Angstrom)')") & 287 dsqrt((tmpx1-tmpx2)**2+(tmpy1-tmpy2)**2+(tmpz1-tmpz2)**2),dsqrt((tmpx1-tmpx2)**2+(tmpy1-tmpy2)**2+(tmpz1-tmpz2)**2)*b2a 288 else if (ctmp1(1:1)=='d') then 289 distmin=9999999 290 distmax=-1 291 distavg=0 292 do itmp=1,nrealatthas(irealattde) 293 iatt=realatttable(irealattde,itmp) 294 tmpx1=attxyz(iatt,1) 295 tmpy1=attxyz(iatt,2) 296 tmpz1=attxyz(iatt,3) 297 distnow=dsqrt((tmpx1-tmpx2)**2+(tmpy1-tmpy2)**2+(tmpz1-tmpz2)**2) 298 distavg=distavg+distnow 299 if (distnow<distmin) distmin=distnow 300 if (distnow>distmax) distmax=distnow 301 end do 302 distavg=distavg/nrealatthas(irealattde) 303 write(*,"(' Note: Attractor',i6,' has',i6,' members')") irealattde,nrealatthas(irealattde) 304 write(*,"(' The minimum distance is',f12.6,' Bohr (',f12.6 ' Angstrom)')") distmin,distmin*b2a 305 write(*,"(' The maximum distance is',f12.6,' Bohr (',f12.6 ' Angstrom)')") distmax,distmax*b2a 306 write(*,"(' The average distance is',f12.6,' Bohr (',f12.6 ' Angstrom)')") distavg,distavg*b2a 307 end if 308 end if 309 310 if (imeasure==2.or.imeasure==3) then !Analyze one more term, then print angle 311 if (ctmp3(1:1)=='a') then 312 read(ctmp3(2:),*) iatm 313 tmpx3=a(iatm)%x 314 tmpy3=a(iatm)%y 315 tmpz3=a(iatm)%z 316 else if (ctmp3(1:1)=='c') then 317 read(ctmp3(2:),*) irealatt 318 tmpx3=realattxyz(irealatt,1) 319 tmpy3=realattxyz(irealatt,2) 320 tmpz3=realattxyz(irealatt,3) 321 end if 322 end if 323 if (imeasure==2) then 324 if (ctmp1(1:1)/='d') then 325 write(*,"(' The angle is',f12.6,' degree')") xyz2angle(tmpx1,tmpy1,tmpz1,tmpx2,tmpy2,tmpz2,tmpx3,tmpy3,tmpz3) 326 else if (ctmp1(1:1)=='d') then 327 angmin=180 328 angmax=-1 329 angavg=0 330 distmin=999999 331 distmax=-1 332 distavg=0 333 prjx=0 334 prjy=0 335 prjz=0 336 do itmp=1,nrealatthas(irealattde) 337 iatt=realatttable(irealattde,itmp) 338 tmpx1=attxyz(iatt,1) 339 tmpy1=attxyz(iatt,2) 340 tmpz1=attxyz(iatt,3) 341 angnow=xyz2angle(tmpx1,tmpy1,tmpz1,tmpx2,tmpy2,tmpz2,tmpx3,tmpy3,tmpz3) 342 angavg=angavg+angnow 343 if (angnow<angmin) angmin=angnow 344 if (angnow>angmax) angmax=angnow 345 distnow=potlinedis(tmpx1,tmpy1,tmpz1,tmpx2,tmpy2,tmpz2,tmpx3,tmpy3,tmpz3) 346 distavg=distavg+distnow 347 if (distnow<distmin) distmin=distnow 348 if (distnow>distmax) distmax=distnow 349 call pointprjline(tmpx1,tmpy1,tmpz1,tmpx2,tmpy2,tmpz2,tmpx3,tmpy3,tmpz3,prjxtmp,prjytmp,prjztmp) 350 prjx=prjx+prjxtmp 351 prjy=prjy+prjytmp 352 prjz=prjz+prjztmp 353 end do 354 angavg=angavg/nrealatthas(irealattde) 355 distavg=distavg/nrealatthas(irealattde) 356 prjx=prjx/nrealatthas(irealattde) 357 prjy=prjy/nrealatthas(irealattde) 358 prjz=prjz/nrealatthas(irealattde) 359 write(*,"(' Note: Attractor',i6,' has',i6,' members')") irealattde,nrealatthas(irealattde) 360 write(*,"(' The minimum angle is',f12.6,' degree')") angmin 361 write(*,"(' The maximum angle is',f12.6,' degree')") angmax 362 write(*,"(' The average angle is',f12.6,' degree')") angavg 363 write(*,"(' The minimum distance is',f12.6,' Bohr (',f12.6 ' Angstrom)')") distmin,distmin*b2a 364 write(*,"(' The maximum distance is',f12.6,' Bohr (',f12.6 ' Angstrom)')") distmax,distmax*b2a 365 write(*,"(' The average distance is',f12.6,' Bohr (',f12.6 ' Angstrom)')") distavg,distavg*b2a 366 write(*,"(' The average X,Y,Z coordinate by projecting the members of ',a,' to the line linking ',a,' and ',a)") trim(ctmp1),trim(ctmp2),trim(ctmp3) 367 write(*,"(3f14.8,' Angstrom')") prjx*b2a,prjy*b2a,prjz*b2a 368 end if 369 end if 370 371 if (imeasure==3) then !Analyze one more term, then print dihedral angle 372 if (ctmp4(1:1)=='a') then 373 read(ctmp4(2:),*) iatm 374 tmpx4=a(iatm)%x 375 tmpy4=a(iatm)%y 376 tmpz4=a(iatm)%z 377 else if (ctmp4(1:1)=='c') then 378 read(ctmp4(2:),*) irealatt 379 tmpx4=realattxyz(irealatt,1) 380 tmpy4=realattxyz(irealatt,2) 381 tmpz4=realattxyz(irealatt,3) 382 end if 383 write(*,"(' The dihedral angle is',f12.6,' degree')") xyz2dih(tmpx1,tmpy1,tmpz1,tmpx2,tmpy2,tmpz2,tmpx3,tmpy3,tmpz3,tmpx4,tmpy4,tmpz4) 384 end if 385 else 386 write(*,*) "Error: Invalid input" 387 end if 388 end do 389 390 else if (isel==-1) then 391 write(*,*) "1: On-grid method, Comput .Mat. Sci., 36, 354 (2006)" 392 write(*,*) "2: Near-grid method, J. Phys.: Condens. Matter, 21, 08420 (2009)" 393 write(*,*) "3: Near-grid method with boundary refinement step" 394 write(*,"(a)") " Note: Near-grid method (adapted by Tian Lu) is more accurate than On-grid method and thus is more recommended; with the boundary refinement step, the result will be better" 395 read(*,*) igridmethod 396 397 else if (isel==0) then 398 ioldtextheigh=textheigh 399 textheigh=40 !Default textheigh is too small 400 textheigh=ioldtextheigh 401 402 else if (isel==1) then 403 isourcedata=1 !Default status is calculating grid data here 404 if (allocated(cubmat)) then 405 write(*,"(a)") " Note: There has been a grid data in the memory, please select generating the basins by which manner" 406 write(*,*) "0 Return" 407 write(*,*) "1 Generate the basins by selecting a real space function" 408 write(*,*) "2 Generate the basins by using the grid data stored in memory" 409 read(*,*) isourcedata 410 end if 411 if (isourcedata==0) then 412 cycle 413 else if (isourcedata==1) then 414 write(*,*) "Select the real space function to be integrated" 415 call selfunc_interface(ifuncbasin) 416 call setgridforbasin(ifuncbasin) 417 if (allocated(cubmat)) deallocate(cubmat) 418 allocate(cubmat(nx,ny,nz)) 419 call savecubmat(ifuncbasin,0,iorbsel) 420 else if (isourcedata==2) then 421 ifuncbasin=1 !I assume that the file loaded records electron density 422 write(*,"('The range of x is from ',f12.6,' to ',f12.6,' Bohr,' i5,' points')") ,orgx,orgx+(nx-1)*dx,nx 423 write(*,"('The range of y is from ',f12.6,' to ',f12.6,' Bohr,',i5,' points')") ,orgy,orgy+(ny-1)*dy,ny 424 write(*,"('The range of z is from ',f12.6,' to ',f12.6,' Bohr,',i5,' points')") ,orgz,orgz+(nz-1)*dz,nz 425 write(*,"('Total number of grid points is ',i10)") nx*ny*nz 426 write(*,"('Grid spacing in X,Y,Z (Bohr):',3f12.6)") dx,dy,dz 427 end if 428 429 allocate(grdposneg(nx,ny,nz)) !Record which grids have negative value 430 grdposneg=.true. 431 do iz=1,nz 432 do iy=1,ny 433 do ix=1,nx 434 if (cubmat(ix,iy,iz)<0D0) then 435 grdposneg(ix,iy,iz)=.false. 436 cubmat(ix,iy,iz)=-cubmat(ix,iy,iz) 437 end if 438 end do 439 end do 440 end do 441! where (cubmat<0D0) !DO NOT USE THIS, because I found that "where" will consuming vary large amount of memory! 442! grdposneg=.false. 443! cubmat=-cubmat !Invert negative values to positive, after basins are generated the values will be recovered 444! end where 445 if (allocated(gridbas)) deallocate(gridbas) 446 allocate(gridbas(nx,ny,nz)) 447 call setupmovevec 448 write(*,*) 449 write(*,*) "Generating basins, please wait..." 450 call walltime(walltime1) 451 CALL CPU_TIME(time_begin) 452 call generatebasin(igridmethod) 453 CALL CPU_TIME(time_end) 454 call walltime(walltime2) 455 write(*,"(' Generating basins took up CPU time',f12.2,'s, wall clock time',i10,'s')") time_end-time_begin,walltime2-walltime1 456 numunassign=count(gridbas(2:nx-1,2:ny-1,2:nz-1)==0) 457 write(*,"(' The number of unassigned grids:',i12)") numunassign 458 numgotobound=count(gridbas(2:nx-1,2:ny-1,2:nz-1)==-1) 459 write(*,"(' The number of grids travelled to box boundary:',i12)") numgotobound 460 where (grdposneg .eqv. .false.) cubmat=-cubmat !Recover original grid data 461 deallocate(grdposneg) 462 463 do iatt=1,numatt !Eliminate the attractors with very low value 464 if ( abs(cubmat(attgrid(iatt,1),attgrid(iatt,2),attgrid(iatt,3)))<threslowvalatt ) then 465 write(*,"(' Note: There are attractors having very low absolute value (<',1PE8.2,') and thus insignificant, how to deal with them?')") threslowvalatt 466 write(*,*) "1 Do nothing" 467 write(*,*) "2 Set corresponding grids as unassigned status" 468 write(*,*) "3 Assign corresponding grids to the nearest significant attractors" 469 write(*,*) "Hint: For most cases, option 3 is recommended" 470 read(*,*) isel2 471 call elimlowvalatt(threslowvalatt,isel2) 472 exit 473 end if 474 end do 475 476 !Generate actual coordinate of attractors, which will be used to plot in the local GUI, and in any other external GUIs 477 if (allocated(attxyz)) deallocate(attxyz,attval) 478 allocate(attxyz(numatt,3),attval(numatt)) 479 do iatt=1,numatt 480 ix=attgrid(iatt,1) 481 iy=attgrid(iatt,2) 482 iz=attgrid(iatt,3) 483 attxyz(iatt,1)=orgx+(ix-1)*dx 484 attxyz(iatt,2)=orgy+(iy-1)*dy 485 attxyz(iatt,3)=orgz+(iz-1)*dz 486 attval(iatt)=cubmat(ix,iy,iz) 487 end do 488 !Currently attractors have been finally determined, one shouldn't perturb them further more 489 490 call clusdegenatt(0) !Cluster degenerate attractors as "real attractors" and calculate average coordinate and value for the real attractors 491 492 call detectinterbasgrd(6) !Detect interbasin grids 493 numinterbas=count(interbasgrid .eqv. .true.) 494 write(*,"(' The number of interbasin grids:',i12)") numinterbas 495 496 else if (isel==2) then 497 call integratebasin 498 499 else if (isel==3.or.isel==4.or.isel==5.or.isel==7.or.isel==-7.or.isel==8) then 500 if (.not.allocated(b)) then 501 write(*,"(a)") " Note: No GTF (gauss type function) information is available in your input file, please input the file & 502 containing GTF information of your system, such as .wfn/.wfx and .fch file. e.g. c:\abc.wfn" 503 read(*,"(a)") c200tmp 504 call readinfile(c200tmp,1) 505 end if 506 if (isel==3) then 507 call multipolebasin 508 else if (isel==4) then 509 call LIDIbasin(0) 510 else if (isel==5) then 511 call LIDIbasin(1) 512 else if (isel==7) then 513 write(*,*) "0 Return" 514 write(*,*) "1 Integrate a specific function with atomic-center + uniform grids" 515 write(*,*) "2 The same as 1, but with exact refinement of basin boundary" 516 write(*,*) "3 The same as 2, but with approximate refinement of basin boundary" 517 write(*,*) "Hint:" 518 write(*,*) "Accuracy: 2>=3>>1 Time spent: 2>3>>1 Memory requirement: 3>2=1" 519 ! write(*,*) "Robost: 1=2>3" 520 read(*,*) iseltmp 521 call integratebasinmix(iseltmp) 522 else if (isel==-7) then 523 call integratebasinmix_LSB 524 else if (isel==8) then 525 call integratebasinmix(10) 526 end if 527 528! else if (isel==6) then 529! call integratebasinrefine 530 else if (isel==9) then 531 call atmpopinbasin 532 end if 533end do 534 535end subroutine 536 537 538 539!!---- setup move vector and move length for the 26 neighbour 540subroutine setupmovevec 541use defvar 542use basinintmod 543vec26x=0 544vec26y=0 545vec26z=0 546!The nearest neighbours: 547len26(1:2)=dx 548vec26x(1)=1 549vec26x(2)=-1 550len26(3:4)=dy 551vec26y(3)=1 552vec26y(4)=-1 553len26(5:6)=dz 554vec26z(5)=1 555vec26z(6)=-1 556!On the edges: 557len26(7:10)=dsqrt(dx*dx+dy*dy) 558vec26x(7)=1 559vec26y(7)=1 560vec26x(8)=-1 561vec26y(8)=1 562vec26x(9)=-1 563vec26y(9)=-1 564vec26x(10)=1 565vec26y(10)=-1 566len26(11:14)=dsqrt(dx*dx+dz*dz) 567vec26x(11)=1 568vec26z(11)=1 569vec26x(12)=-1 570vec26z(12)=1 571vec26x(13)=-1 572vec26z(13)=-1 573vec26x(14)=1 574vec26z(14)=-1 575len26(15:18)=dsqrt(dy*dy+dz*dz) 576vec26y(15)=1 577vec26z(15)=1 578vec26y(16)=1 579vec26z(16)=-1 580vec26y(17)=-1 581vec26z(17)=-1 582vec26y(18)=-1 583vec26z(18)=1 584!At the vertices: 585len26(19:26)=dsqrt(dx*dx+dy*dy+dz*dz) 586vec26z(19:22)=1 587vec26x(19)=1 588vec26y(19)=1 589vec26x(20)=-1 590vec26y(20)=1 591vec26x(21)=-1 592vec26y(21)=-1 593vec26x(22)=1 594vec26y(22)=-1 595vec26z(23:26)=-1 596vec26x(23)=1 597vec26y(23)=-1 598vec26x(24)=1 599vec26y(24)=1 600vec26x(25)=-1 601vec26y(25)=1 602vec26x(26)=-1 603vec26y(26)=-1 604gridbas=0 !Unassigned state 605gridbas(1,:,:)=-2 !Set status for box boundary grids 606gridbas(nx,:,:)=-2 607gridbas(:,1,:)=-2 608gridbas(:,ny,:)=-2 609gridbas(:,:,1)=-2 610gridbas(:,:,nz)=-2 611end subroutine 612 613 614 615!!------- Generate basins from regular grid 616! igridmethod=1: On grid, Comput.Mat.Sci.,36,354 =2: Near-grid method (slower, but more accurate), see J.Phys.:Condens.Matter,21,084204 617! =3: Near-grid with refinement 618! The near-grid method is improved by Tian Lu, namely at later stage automatically switch to on-grid method to guarantee convergence 619subroutine generatebasin(igridmethod) 620use defvar 621use basinintmod 622implicit real*8(a-h,o-z) 623integer,parameter :: nmaxtrjgrid=3000 624integer igridmethod 625integer ntrjgrid !Recording trjgrid contains how many elements now 626integer trjgrid(nmaxtrjgrid,3) !The trajectory contains which grids, record their indices sequentially, trjgrid(i,1/2/3) = ix/iy/iz of the ith grid 627! real*8 trjval(nmaxtrjgrid),gradmaxval(nmaxtrjgrid) !******For debugging****** 628if (allocated(attgrid)) deallocate(attgrid) 629allocate(attgrid(nint(nx*ny*nz/20D0),3)) !I think the number of attractors in general is impossible to exceeds nx*ny*nz/20 630numatt=0 631write(*,*) " Attractor X,Y,Z coordinate (Angstrom) Value" 632!Cycle all grids, but the box boundary ones are ignored (due to gridbas=-2), since can't evalute its gradients in all directions by finite difference 633 634nsteplimit=min( nmaxtrjgrid,nint(dsqrt(dfloat(nx*nx+ny*ny+nz*nz))*2) ) 635nstepdiscorr=nint(nsteplimit/2D0) 636if (igridmethod==1) then 6371 continue 638nthreads=getNThreads() 639!$OMP PARALLEL DO private(ix,iy,iz,ntrjgrid,inowx,inowy,inowz,trjgrid,valnow,imove,gradtmp,igradmax,gradmax,iatt,itrjgrid,idtmp) & 640!$OMP shared(gridbas,numatt,attgrid) schedule(DYNAMIC) NUM_THREADS(nthreads) 641 do iz=2,nz-1 642 do iy=2,ny-1 643 do ix=2,nx-1 644 if (gridbas(ix,iy,iz)/=0) cycle 645! if (interbasgrid(ix,iy,iz) .eqv. .false.) cycle 646 ntrjgrid=0 647 inowx=ix 648 inowy=iy 649 inowz=iz 650 do while(.true.) !Steepest ascent 651 ntrjgrid=ntrjgrid+1 652 if (ntrjgrid>nsteplimit) exit !Unconverged. The gridbas for these unassigned grids will still be 0 653 trjgrid(ntrjgrid,1)=inowx 654 trjgrid(ntrjgrid,2)=inowy 655 trjgrid(ntrjgrid,3)=inowz 656 !Test all 26 directions to find out the maximal gradient direction 657 valnow=cubmat(inowx,inowy,inowz) 658 do imove=1,26 659 gradtmp=(cubmat(inowx+vec26x(imove),inowy+vec26y(imove),inowz+vec26z(imove))-valnow)/len26(imove) 660 if (imove==1.or.gradtmp>gradmax) then 661 igradmax=imove 662 gradmax=gradtmp 663 end if 664 end do 665 !Test if this is an attractor, if yes, assign all grid in this trajectory 666 if (gradmax<=0) then !Equal sign is important, because when system has symmetry, adjacent grid may be degenerate about mirrow plane, now the ascent should be terminated 667 if (valnow==0D0) exit !The region far beyond system, the value may be exactly zero due to cutoff of exponent, these grids shouldn't be regarded as attractors 668!$OMP CRITICAL 669cyciatt3: do iatt=1,numatt 670 if (inowx==attgrid(iatt,1).and.inowy==attgrid(iatt,2).and.inowz==attgrid(iatt,3)) then 671 do itrjgrid=1,ntrjgrid 672 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=iatt 673 end do 674 exit cyciatt3 675 end if 676 do imove=1,26 677 if (inowx+vec26x(imove)==attgrid(iatt,1).and.inowy+vec26y(imove)==attgrid(iatt,2).and.inowz+vec26z(imove)==attgrid(iatt,3)) then 678 do itrjgrid=1,ntrjgrid 679 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=iatt 680 end do 681 exit cyciatt3 682 end if 683 end do 684 end do cyciatt3 685 if (iatt>numatt) then !A new attractor, iatt=numatt+1 currently 686 numatt=numatt+1 687 do itrjgrid=1,ntrjgrid 688 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=numatt 689 end do 690 attgrid(numatt,1)=inowx 691 attgrid(numatt,2)=inowy 692 attgrid(numatt,3)=inowz 693 if (grdposneg(inowx,inowy,inowz) .eqv. .true.) then 694 write(*,"(i8,3f14.8,f20.8)") numatt,(orgx+(inowx-1)*dx)*b2a,(orgy+(inowy-1)*dy)*b2a,(orgz+(inowz-1)*dz)*b2a,cubmat(inowx,inowy,inowz) 695 else !This grid should has negative value 696 write(*,"(i8,3f14.8,f20.8)") numatt,(orgx+(inowx-1)*dx)*b2a,(orgy+(inowy-1)*dy)*b2a,(orgz+(inowz-1)*dz)*b2a,-cubmat(inowx,inowy,inowz) 697 end if 698 end if 699!$OMP end CRITICAL 700 exit 701 end if 702 !Move to next grid 703 inowx=inowx+vec26x(igradmax) 704 inowy=inowy+vec26y(igradmax) 705 inowz=inowz+vec26z(igradmax) 706 !Test if this grid has already been assigned 707 idtmp=gridbas(inowx,inowy,inowz) 708 if (idtmp>0) then 709 do itrjgrid=1,ntrjgrid 710 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=idtmp 711 end do 712 exit 713 end if 714 !Test if encountered box boundary 715 if (inowx==1.or.inowx==nx.or.inowy==1.or.inowy==ny.or.inowz==1.or.inowz==nz) then 716 do itrjgrid=1,ntrjgrid 717 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=-1 718 end do 719 exit 720 end if 721 end do !End ascent 722 end do !End cycle x 723 end do !End cycle y 724 end do !End cycle z 725!$OMP END PARALLEL DO 726 727else if (igridmethod==2.or.igridmethod==3) then 728nthreads=getNThreads() 729!$OMP PARALLEL DO private(ix,iy,iz,corrx,corry,corrz,ntrjgrid,inowx,inowy,inowz,trjgrid,valnow,imove,gradtmp,igradmax,gradmax,iatt,& 730!$OMP itrjgrid,idtmp,icorrx,icorry,icorrz,gradx,grady,gradz,sclgrad,ineiidx) shared(gridbas,numatt,attgrid) schedule(DYNAMIC) NUM_THREADS(nthreads) 731 do iz=2,nz-1 732 do iy=2,ny-1 733 do ix=2,nx-1 734 if (gridbas(ix,iy,iz)/=0) cycle 735 ntrjgrid=0 736 inowx=ix 737 inowy=iy 738 inowz=iz 739 corrx=0D0 !Correction vector 740 corry=0D0 741 corrz=0D0 742 do while(.true.) !Steepest ascent 743 ntrjgrid=ntrjgrid+1 744 trjgrid(ntrjgrid,1)=inowx 745 trjgrid(ntrjgrid,2)=inowy 746 trjgrid(ntrjgrid,3)=inowz 747 if (ntrjgrid>nsteplimit) then !These gridbas for these unconverged grids will still be 0 748! do itmp=1,ntrjgrid-1 !******For debugging****** 749! write(*,"(i5,3i6,2f16.10)") itmp,trjgrid(itmp,1:3),trjval(itmp),gradmaxval(itmp) 750! end do 751 exit 752 end if 753 !Test all 26 directions to find out the maximal gradient direction 754 valnow=cubmat(inowx,inowy,inowz) 755 do imove=1,26 756 gradtmp=(cubmat(inowx+vec26x(imove),inowy+vec26y(imove),inowz+vec26z(imove))-valnow)/len26(imove) 757 if (imove==1.or.gradtmp>gradmax) then 758 igradmax=imove 759 gradmax=gradtmp 760 end if 761 end do 762! write(*,"(3i5,2f14.8)") inowx,inowy,inowz,gradmax,valnow !Trace the trajectory !******For debugging****** 763 if (gradmax<=0D0) then !Equal sign is important, because when system has symmetry, adjacent grid may be degenerate about mirrow plane, now the ascent should be terminated 764 if (valnow==0D0) exit !The region far beyond system, the value may be exactly zero due to cutoff of exponent, these grids shouldn't be regarded as attractors 765!$OMP CRITICAL 766cyciatt: do iatt=1,numatt 767 !Test if current grid is attractor iatt, is yes, assign all grid in this trajectory 768 if (inowx==attgrid(iatt,1).and.inowy==attgrid(iatt,2).and.inowz==attgrid(iatt,3)) then 769 do itrjgrid=1,ntrjgrid 770 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=iatt 771 end do 772 exit cyciatt 773 end if 774 !Test if neighbour grid (+/-x,+/-y,+/-z) is attractor iatt, is yes, assign all grid in this trajectory 775 !The reason I do this is because when the points are symmetric to Cartesian plane, many adjacent and degenerate attractors will occur,& 776 !while this treatment can combine them as a single one 777 do imove=1,26 778 if (inowx+vec26x(imove)==attgrid(iatt,1).and.inowy+vec26y(imove)==attgrid(iatt,2).and.inowz+vec26z(imove)==attgrid(iatt,3)) then 779 do itrjgrid=1,ntrjgrid 780 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=iatt 781 end do 782 exit cyciatt 783 end if 784 end do 785 end do cyciatt 786 !A new attractor 787 if (iatt==numatt+1) then 788 numatt=numatt+1 789 do itrjgrid=1,ntrjgrid 790 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=numatt 791 end do 792 attgrid(numatt,1)=inowx 793 attgrid(numatt,2)=inowy 794 attgrid(numatt,3)=inowz 795 if (grdposneg(inowx,inowy,inowz) .eqv. .true.) then 796 write(*,"(i8,3f14.8,f20.8)") numatt,(orgx+(inowx-1)*dx)*b2a,(orgy+(inowy-1)*dy)*b2a,(orgz+(inowz-1)*dz)*b2a,cubmat(inowx,inowy,inowz) 797 else !This grid should has negative value 798 write(*,"(i8,3f14.8,f20.8)") numatt,(orgx+(inowx-1)*dx)*b2a,(orgy+(inowy-1)*dy)*b2a,(orgz+(inowz-1)*dz)*b2a,-cubmat(inowx,inowy,inowz) 799 end if 800 end if 801!$OMP end CRITICAL 802 exit 803 end if 804 !Correction step may lead to oscillator when encountering circular ELF/LOL attractor, so if the current number of step is already large 805 !(larger than half of upper limit of step number), then correction will be disabled (namely switch to on-grid method) to guarantee convergence. 806 if ( ntrjgrid<nstepdiscorr .and. (abs(corrx)>(dx/2D0).or.abs(corry)>(dy/2D0).or.abs(corrz)>(dz/2D0)) ) then !This time we do correction step 807 if (abs(corrx)>(dx/2D0)) then 808 icorrx=nint(corrx/abs(corrx)) !Get sign of corrx 809 inowx=inowx+icorrx 810 corrx=corrx-icorrx*dx 811 end if 812 if (abs(corry)>(dy/2D0)) then 813 icorry=nint(corry/abs(corry)) 814 inowy=inowy+icorry 815 corry=corry-icorry*dy 816 end if 817 if (abs(corrz)>(dz/2D0)) then 818 icorrz=nint(corrz/abs(corrz)) 819 inowz=inowz+icorrz 820 corrz=corrz-icorrz*dz 821 end if 822 else !Move to next grid according to maximal gradient and then update correction vector 823 !Calculate true gradient 824 gradx=(cubmat(inowx+1,inowy,inowz)-cubmat(inowx-1,inowy,inowz))/(2*dx) 825 grady=(cubmat(inowx,inowy+1,inowz)-cubmat(inowx,inowy-1,inowz))/(2*dy) 826 gradz=(cubmat(inowx,inowy,inowz+1)-cubmat(inowx,inowy,inowz-1))/(2*dz) 827 sclgrad=min(dx/abs(gradx),dy/abs(grady),dz/abs(gradz)) 828 inowx=inowx+vec26x(igradmax) 829 inowy=inowy+vec26y(igradmax) 830 inowz=inowz+vec26z(igradmax) 831 corrx=corrx+gradx*sclgrad-vec26x(igradmax)*dx 832 corry=corry+grady*sclgrad-vec26y(igradmax)*dy 833 corrz=corrz+gradz*sclgrad-vec26z(igradmax)*dz 834! if (abs(corrx)>(dx/2D0).or.abs(corry)>(dy/2D0).or.abs(corrz)>(dz/2D0)) cycle 835 end if 836 !Test if this grid has already been assigned and all of the neighbours were also assigned to the same attractor 837 idtmp=gridbas(inowx,inowy,inowz) 838 if (idtmp>0) then 839 do imove=1,26 840 ineiidx=gridbas(inowx+vec26x(imove),inowy+vec26y(imove),inowz+vec26z(imove)) 841 if (ineiidx/=idtmp) exit 842 end do 843 if (imove==27) then 844 do itrjgrid=1,ntrjgrid 845 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=idtmp 846 end do 847 exit 848 end if 849 end if 850 !Test if encountered box boundary 851 if (inowx==1.or.inowx==nx.or.inowy==1.or.inowy==ny.or.inowz==1.or.inowz==nz) then 852 do itrjgrid=1,ntrjgrid 853 gridbas(trjgrid(itrjgrid,1),trjgrid(itrjgrid,2),trjgrid(itrjgrid,3))=-1 854 end do 855 exit 856 end if 857 end do !End ascent 858 end do !End cycle x 859 end do !End cycle y 860 end do !End cycle z 861!$OMP END PARALLEL DO 862 863 !!!! Refining the basin boundary 864 if (igridmethod==3) then 865! do itime=1,3 !Refine one time in general is sufficient 866 write(*,*) "Detecting boundary grids..." 867 call detectinterbasgrd(6) !It seems that using 26 directions to determine boundary grids doesn't bring evident benefit 868 write(*,"(' There are',i12,' grids at basin boundary')") count(interbasgrid .eqv. .true.) 869 write(*,*) "Refining basin boundary..." 870 !Below code is the adapted copy of above near-grid code 871nthreads=getNThreads() 872!$OMP PARALLEL DO private(ix,iy,iz,corrx,corry,corrz,ntrjgrid,inowx,inowy,inowz,valnow,imove,gradtmp,igradmax,gradmax,iatt,& 873!$OMP ineiidx,idtmp,icorrx,icorry,icorrz,gradx,grady,gradz,sclgrad) shared(gridbas,attgrid) schedule(DYNAMIC) NUM_THREADS(nthreads) 874 do iz=2,nz-1 875 do iy=2,ny-1 876 do ix=2,nx-1 877 if (interbasgrid(ix,iy,iz) .eqv. .false.) cycle 878 if (gridbas(ix,iy,iz)<=0) cycle !Ignored the ones unassigned or gone to box boundary 879 ntrjgrid=0 880 inowx=ix 881 inowy=iy 882 inowz=iz 883 corrx=0D0 !Correction vector 884 corry=0D0 885 corrz=0D0 886 do while(.true.) !Steepest ascent 887 ntrjgrid=ntrjgrid+1 888 if (ntrjgrid>nsteplimit) exit 889 valnow=cubmat(inowx,inowy,inowz) 890 do imove=1,26 891 gradtmp=(cubmat(inowx+vec26x(imove),inowy+vec26y(imove),inowz+vec26z(imove))-valnow)/len26(imove) 892 if (imove==1.or.gradtmp>gradmax) then 893 igradmax=imove 894 gradmax=gradtmp 895 end if 896 end do 897 if (gradmax<=0) then !Equal sign is important, because when system has symmetry, adjacent grid may be degenerate about mirrow plane, now the ascent should be terminated 898cyciatt2: do iatt=1,numatt 899 if (inowx==attgrid(iatt,1).and.inowy==attgrid(iatt,2).and.inowz==attgrid(iatt,3)) then 900 gridbas(ix,iy,iz)=iatt 901 exit cyciatt2 902 end if 903 do imove=1,26 !Test if neighbour grid (+/-x,+/-y,+/-z) is attractor iatt 904 if (inowx+vec26x(imove)==attgrid(iatt,1).and.inowy+vec26y(imove)==attgrid(iatt,2).and.inowz+vec26z(imove)==attgrid(iatt,3)) then 905 gridbas(ix,iy,iz)=iatt 906 exit cyciatt2 907 end if 908 end do 909 end do cyciatt2 910 if (iatt>numatt) then 911 write(*,*) "Warning: Found new attractor at refining process!" 912 end if 913 exit 914 end if 915 if ( ntrjgrid<nstepdiscorr .and. (abs(corrx)>(dx/2D0).or.abs(corry)>(dy/2D0).or.abs(corrz)>(dz/2D0)) ) then !This time we do correction step 916 if (abs(corrx)>(dx/2D0)) then 917 icorrx=nint(corrx/abs(corrx)) !Get sign of corrx 918 inowx=inowx+icorrx 919 corrx=corrx-icorrx*dx 920 end if 921 if (abs(corry)>(dy/2D0)) then 922 icorry=nint(corry/abs(corry)) 923 inowy=inowy+icorry 924 corry=corry-icorry*dy 925 end if 926 if (abs(corrz)>(dz/2D0)) then 927 icorrz=nint(corrz/abs(corrz)) 928 inowz=inowz+icorrz 929 corrz=corrz-icorrz*dz 930 end if 931 else !Move to next grid according to maximal gradient and then update correction vector 932 gradx=(cubmat(inowx+1,inowy,inowz)-cubmat(inowx-1,inowy,inowz))/(2*dx) 933 grady=(cubmat(inowx,inowy+1,inowz)-cubmat(inowx,inowy-1,inowz))/(2*dy) 934 gradz=(cubmat(inowx,inowy,inowz+1)-cubmat(inowx,inowy,inowz-1))/(2*dz) 935 sclgrad=min(dx/abs(gradx),dy/abs(grady),dz/abs(gradz)) 936 inowx=inowx+vec26x(igradmax) 937 inowy=inowy+vec26y(igradmax) 938 inowz=inowz+vec26z(igradmax) 939 corrx=corrx+gradx*sclgrad-vec26x(igradmax)*dx 940 corry=corry+grady*sclgrad-vec26y(igradmax)*dy 941 corrz=corrz+gradz*sclgrad-vec26z(igradmax)*dz 942 end if 943 idtmp=gridbas(inowx,inowy,inowz) 944 if (ntrjgrid>60.and.idtmp>0) then !If enable this doesn't affect result detectably, but enabling it will evidently reduce computational cost 945 do imove=1,26 946 ineiidx=gridbas(inowx+vec26x(imove),inowy+vec26y(imove),inowz+vec26z(imove)) 947 if (ineiidx/=idtmp) exit 948 end do 949 if (imove==27) then 950 gridbas(ix,iy,iz)=idtmp 951 exit 952 end if 953 end if 954 if (inowx==1.or.inowx==nx.or.inowy==1.or.inowy==ny.or.inowz==1.or.inowz==nz) exit 955 end do !End ascent 956 end do !End cycle x 957 end do !End cycle y 958 end do !End cycle z 959!$OMP END PARALLEL DO 960! end do 961 end if 962 963end if 964end subroutine 965 966 967 968!!------- Eliminate low value attractors and the corresponding basin 969! imode=1 Do nothing 970! imode=2 Set corresponding grids as unassigned status 971! imode=3 Assign corresponding grids to the nearest significant attractors 972subroutine elimlowvalatt(threslowvalatt,imode) 973use defvar 974use basinintmod 975implicit real*8(a-h,o-z) 976integer imode 977real*8 threslowvalatt 978integer highvalatt(numatt) 979integer att2att(-2:numatt) !Convert indices of old attractors to new ones 980integer atttypelist(numatt) 981if (imode==1) then 982 return 983else 984 icounthigh=0 985 atttypelist=0 986 do iatt=1,numatt !Generate list 987 if (abs(cubmat(attgrid(iatt,1),attgrid(iatt,2),attgrid(iatt,3)))>=threslowvalatt) then 988 icounthigh=icounthigh+1 989 highvalatt(icounthigh)=iatt 990 atttypelist(iatt)=1 !high 991 end if 992 end do 993 nlowvalatt=numatt-icounthigh 994 if (imode==2) then 995 do iz=2,nz-1 996 do iy=2,ny-1 997 do ix=2,nx-1 998 if (atttypelist(gridbas(ix,iy,iz))==0) gridbas(ix,iy,iz)=0 999 end do 1000 end do 1001 end do 1002 else if (imode==3) then 1003 do iz=2,nz-1 1004 rnowz=orgz+(iz-1)*dz 1005 do iy=2,ny-1 1006 rnowy=orgy+(iy-1)*dy 1007 do ix=2,nx-1 1008 rnowx=orgx+(ix-1)*dx 1009 if (atttypelist(gridbas(ix,iy,iz))==0) then !Find out which grid belongs to insignificant attractors 1010 shortmaxsqr=1D20 1011 do iatt=1,numatt !Will be attribute to the nearest significant attractors 1012 if (atttypelist(iatt)==1) then 1013 xatt=orgx+(attgrid(iatt,1)-1)*dx 1014 yatt=orgy+(attgrid(iatt,2)-1)*dy 1015 zatt=orgz+(attgrid(iatt,3)-1)*dz 1016 disttmpsqr=(xatt-rnowx)**2+(yatt-rnowy)**2+(zatt-rnowz)**2 1017 if (disttmpsqr<shortmaxsqr) then 1018 ishortmax=iatt 1019 shortmaxsqr=disttmpsqr 1020 end if 1021 end if 1022 end do 1023 gridbas(ix,iy,iz)=ishortmax 1024 end if 1025 end do 1026 end do 1027 end do 1028 end if 1029 !Build conversion table between old and new attractors, so that the indices of attractors could be contiguous 1030 att2att=-3 1031 att2att(-2)=-2 1032 att2att(-1)=-1 1033 att2att(0)=0 1034 icount=0 1035 do iatt=1,numatt 1036 if (atttypelist(iatt)==0) cycle 1037 icount=icount+1 1038 att2att(iatt)=icount 1039 end do 1040 numatt=numatt-nlowvalatt 1041 !Update attgrid 1042 do iatt=1,numatt 1043 attgrid(iatt,:)=attgrid(highvalatt(iatt),:) 1044 end do 1045 !Final update of grid attribution 1046 do iz=2,nz-1 1047 do iy=2,ny-1 1048 do ix=2,nx-1 1049 gridbas(ix,iy,iz)=att2att(gridbas(ix,iy,iz)) 1050 end do 1051 end do 1052 end do 1053 write(*,"(i6,' insignificant attractors have been eliminated')") nlowvalatt 1054end if 1055end subroutine 1056 1057 1058 1059!!------- !Detect interbasin grids for real attractors 1060!ndir can be 6 and 26, meaning if only primary 6 directions or all 26 directions are used to determine boundary grids 1061subroutine detectinterbasgrd(ndir) 1062use defvar 1063use basinintmod 1064implicit real*8(a-h,o-z) 1065integer ndir 1066if (allocated(interbasgrid)) deallocate(interbasgrid) 1067allocate(interbasgrid(nx,ny,nz)) 1068interbasgrid=.false. 1069do iz=2,nz-1 1070 do iy=2,ny-1 1071 do ix=2,nx-1 1072 idxnow=gridbas(ix,iy,iz) 1073 do imove=1,ndir !To reduce the number of interbasin grids and thus speed up displaying, I don't use full 26 direction as criterion 1074 idxmove=gridbas(ix+vec26x(imove),iy+vec26y(imove),iz+vec26z(imove)) 1075 if ((idxmove/=idxnow).and.idxmove/=-2) then !This is a grid between two or more basins, and is not adjacent to box boundary 1076 interbasgrid(ix,iy,iz)=.true. 1077 exit 1078 end if 1079 end do 1080 end do 1081 end do 1082end do 1083end subroutine 1084 1085 1086 1087!!------- Cluster degenerate and adjacent attractors together 1088!!Nearest neighbour method is used to cluster the attractors, see Leach book p493. Each cluster corresponds to a final attractor 1089!If imode=0, run the whole subroutine; if imode=1, then external attconv is provided, and only some code in this routine is used to make the index contiguous 1090subroutine clusdegenatt(imode) 1091use defvar 1092use basinintmod 1093implicit real*8(a-h,o-z) 1094integer imode 1095integer neleatt,eleatt(numatt) !eleatt(i)=j means the ith element in clustering stage corresponds to actual jth attractor. nclustlist is its total current elements 1096integer passlist(numatt) !if the ith element is 1, means i attractor has passed clustering stage 1097integer ncluster,ncluele(numatt),cluele(numatt,numatt) !cluele(i,4/5/9...) means cluster i has element 4,5,9..., ncluele(i) is current size of cluster i, ncluster is total number of cluster 1098real*8 attdismat(numatt,numatt),attvalmat(numatt,numatt) !Distance matrix and relative value difference matrix of the original attractors 1099distcrit=mergeattdist*dsqrt(dx*dx+dy*dy+dz*dz) !Distance criterion 1100irefined=0 !If 1, that means this combining process takes effects 1101idebugclusdegenatt=0 !If output debug information 1102 1103if (imode==1) goto 2 1104 1105if (allocated(attconv)) deallocate(attconv) 1106allocate(attconv(-2:numatt)) 1107do iatt=-2,numatt !First assume that each attractor is a real attractor 1108 attconv(iatt)=iatt 1109end do 1110 1111!Generate difference matrix for distance and relative value 1112attdismat=0D0 1113attvalmat=0D0 1114do iatt=1,numatt 1115 valiatt=attval(iatt) 1116 xiatt=attxyz(iatt,1) 1117 yiatt=attxyz(iatt,2) 1118 ziatt=attxyz(iatt,3) 1119 do jatt=iatt+1,numatt 1120 valjatt=attval(jatt) 1121 xjatt=attxyz(jatt,1) 1122 yjatt=attxyz(jatt,2) 1123 zjatt=attxyz(jatt,3) 1124 distdiff=dsqrt( (xiatt-xjatt)**2+(yiatt-yjatt)**2+(ziatt-zjatt)**2 ) 1125 attdismat(iatt,jatt)=distdiff 1126 valdiff=abs((valiatt-valjatt)/valiatt) 1127 attvalmat(iatt,jatt)=valdiff 1128 if (valdiff<valcritclus.and.distdiff<distcrit) irefined=1 1129! write(10,"(2i5,f16.10)") iatt,jatt,attvalmat(iatt,jatt) 1130 end do 1131end do 1132attdismat=attdismat+transpose(attdismat) 1133attvalmat=attvalmat+transpose(attvalmat) 1134do iatt=1,numatt 1135 attdismat(iatt,iatt)=attdismat(iatt,iatt)/2D0 1136 attvalmat(iatt,iatt)=attvalmat(iatt,iatt)/2D0 1137end do 1138if (irefined==0) then !Nothing to do. Construct real attractor information and then directly return 1139 numrealatt=numatt 1140 if (allocated(nrealatthas)) deallocate(nrealatthas,realattval,realattxyz,realatttable) 1141 allocate(nrealatthas(numrealatt),realattval(numrealatt),realattxyz(numrealatt,3),realatttable(numrealatt,1)) 1142 nrealatthas=1 1143 do iatt=1,numatt 1144 realattxyz(iatt,:)=attxyz(iatt,:) 1145 realattval(iatt)=attval(iatt) 1146 realatttable(iatt,1)=iatt 1147 end do 1148 return 1149else 1150 write(*,*) "Degenerate attractors detected, clustering them..." 1151 write(*,"(' Criterion for clustering: Interval <',f8.5,' Bohr, value difference <',f8.5,'%')") distcrit,valcritclus*100 1152end if 1153! do i=1,numatt !Print distance matrix 1154! do j=1,numatt 1155! write(10,*) i,j,attdismat(i,j) 1156! end do 1157! end do 1158 1159!Clustering via nearest neighbour method, and accordingly update conversion list 1160passlist=0 1161do iatt=1,numatt 1162 if (passlist(iatt)==1) cycle 1163 if (count(attdismat(iatt,:)<distcrit)==1) then !This attractor has no neighbour 1164 if (passlist(iatt)==1) cycle 1165 cycle 1166 end if 1167 !Construct mapping table of attractor, capacity is neleatt 1168 neleatt=0 !The number of degenerate attractors in this batch 1169 do jatt=iatt,numatt 1170 if (attvalmat(iatt,jatt)<valcritclus) then 1171 neleatt=neleatt+1 1172 eleatt(neleatt)=jatt 1173 passlist(jatt)=1 1174 end if 1175 end do 1176 if (neleatt==1) cycle !Although this attractor has one or more neighbours, but no other attractor is degenerate with itself, so pass 1177 if (idebugclusdegenatt==1) then 1178 write(*,"(a,i5)") " Attractors in this time of clustering triggered by attractor:",iatt 1179 write(*,"(' The number of initial clusters:',i5,', corresponding to these attractor:')") neleatt 1180 write(*,"(15i5)") eleatt(1:neleatt) 1181 end if 1182 1183 !Start clustering. Now you should forget actual index of attractors. Only consider element 1,2,3... 1184 !Initialize, assume that each element in this batch of degenerate attractors is a cluster 1185 ncluster=neleatt 1186 ncluele(:)=1 1187 do iclu=1,ncluster 1188 cluele(iclu,1)=iclu 1189 end do 1190 ntimeclu=0 1191 do while(.true.) !Gradually clustering until closet distance between any cluster pair is larger than criteria 1192 ntimeclu=ntimeclu+1 1193 imergeclu=0 1194 do iclu=1,ncluster !Note that ncluster remain unchanged, eliminated cluster is simply emptyed but still reserve its index 1195 if (ncluele(iclu)==0) cycle !Has been incorporated to others 1196 shortmax=9999999D0 1197 ishortmax=0 1198 do jclu=iclu+1,ncluster 1199 if (ncluele(jclu)==0) cycle 1200 !Calculate shortest distance between cluster i and j 1201 shortmaxtmp=9999999D0 1202 do ieletmp=1,ncluele(iclu) 1203 do jeletmp=1,ncluele(jclu) 1204 iele=cluele(iclu,ieletmp) 1205 jele=cluele(jclu,jeletmp) 1206 distele=attdismat(eleatt(iele),eleatt(jele)) 1207 if (distele<shortmaxtmp) shortmaxtmp=distele 1208 end do 1209 end do 1210 if (shortmaxtmp<shortmax) then !Find out the closest cluster to cluster iclu 1211 shortmax=shortmaxtmp 1212 ishortmax=jclu 1213 end if 1214 end do 1215! write(*,"(i3,f16.6,4i5,i2)") ntimeclu,shortmax,iclu,ishortmax,abs(shortmax<distcrit) 1216 if (shortmax<distcrit) then !Combine cluster ishortmax to cluster iclu 1217 imergeclu=1 1218 newlen=ncluele(iclu)+ncluele(ishortmax) 1219 cluele(iclu,ncluele(iclu)+1:newlen)=cluele(ishortmax,1:ncluele(ishortmax)) 1220 ncluele(iclu)=newlen 1221 ncluele(ishortmax)=0 !Empty 1222 end if 1223 end do 1224 if (imergeclu==0) exit !Between all cluster pair the shortmax is longer than distcrit, therefore exit 1225 end do 1226 do iclu=1,ncluster !Update attractor conversion list 1227 if (ncluele(iclu)==0) cycle 1228 if (idebugclusdegenatt==1) then 1229 write(*,"(' Clustering finished after',i5,' times of cycle')") ntimeclu 1230 write(*,"(' Cluster',i5,', deriving from attractor',i5,', contains:')") iclu,eleatt(iclu) 1231 write(*,"(' Element: ',15i5)") cluele(iclu,1:ncluele(iclu)) 1232 write(*,"(' Attractor:',15i5)") eleatt(cluele(iclu,1:ncluele(iclu))) 1233 end if 1234 do itmp=1,ncluele(iclu) 1235 ielimele=cluele(iclu,itmp) 1236 ielimatt=eleatt(ielimele) 1237 itargetatt=eleatt(iclu) 1238 attconv(ielimatt)=itargetatt 1239 end do 1240 end do 1241end do 1242 1243! do iatt=1,numatt !Print conversion list just after clustering 1244! write(*,*) iatt,attconv(iatt) 1245! end do 1246!Update attractor conversion list to make the attractor index contiguous, and hence get "real" attractor 1247!e.g. old conversion list, after slash is the new one 1248! 1 1/1 1249! 2 2/2 1250! ... 2/2 1251! 13 2/2 1252! 14 14/3 <---After above clustering, 14,16,17 will convert to 14. The first new entry at right column always matched corresponding left column, this is guaranteed by above clustering code 1253! 15 2/2 1254! 16 14/3 1255! 17 14/3 1256! 18 18/4 12572 numrealatt=0 1258passlist=0 !Record which attractor has already been converted 1259do iatt=1,numatt 1260 if (passlist(iatt)==1) cycle 1261 numrealatt=numrealatt+1 !Of course, numrealatt always <=iatt, so will not unexpectly overlap data during update conversion list 1262 idestmp=attconv(iatt) 1263 do jatt=iatt,numatt 1264 if (attconv(jatt)==idestmp) then 1265 passlist(jatt)=1 1266 attconv(jatt)=numrealatt 1267 end if 1268 end do 1269end do 1270! do iatt=1,numatt !Print conversion list after reordered 1271! write(*,*) iatt,attconv(iatt) 1272! end do 1273 1274call updaterealattprop 1275 1276if (imode==1) return 1277!Update basin attribution 1278do iz=2,nz-1 1279 do iy=2,ny-1 1280 do ix=2,nx-1 1281 gridbas(ix,iy,iz)=attconv(gridbas(ix,iy,iz)) 1282 end do 1283 end do 1284end do 1285!Output final attractors 1286write(*,*) "The attractors after clustering:" 1287write(*,*) " Index Average X,Y,Z coordinate (Angstrom) Value" 1288do irealatt=1,numrealatt 1289 write(*,"(i8,3f15.8,f20.8)") irealatt,realattxyz(irealatt,:)*b2a,realattval(irealatt) 1290end do 1291end subroutine 1292 1293 1294 1295!!------- Update real attractors' properties (average value, xyz, the number of members) 1296subroutine updaterealattprop 1297use defvar 1298use basinintmod 1299implicit real*8(a-h,o-z) 1300if (allocated(nrealatthas)) deallocate(nrealatthas,realattval,realattxyz,realatttable) 1301allocate(nrealatthas(numrealatt),realattval(numrealatt),realattxyz(numrealatt,3),realatttable(numrealatt,numatt)) 1302nrealatthas=0 1303realattval=0D0 1304realattxyz=0D0 1305do iatt=1,numatt 1306 irealatt=attconv(iatt) 1307 nrealatthas(irealatt)=nrealatthas(irealatt)+1 1308 realatttable(irealatt,nrealatthas(irealatt))=iatt 1309 realattxyz(irealatt,:)=realattxyz(irealatt,:)+attxyz(iatt,:) 1310 realattval(irealatt)=realattval(irealatt)+attval(iatt) 1311end do 1312do irealatt=1,numrealatt 1313 realattval(irealatt)=realattval(irealatt)/nrealatthas(irealatt) 1314 realattxyz(irealatt,:)=realattxyz(irealatt,:)/nrealatthas(irealatt) 1315end do 1316end subroutine 1317 1318 1319 1320!!------------------ Set grid for generating basin, adapted from the subroutine "setgrid" 1321subroutine setgridforbasin(ifuncsel) 1322use defvar 1323implicit real*8 (a-h,o-z) 1324integer :: iselexttype=3,ifuncsel 1325real*8 :: molxlen,molylen,molzlen,tmpx,tmpy,tmpz,rhocrit=1D-6 1326real*8 :: gridextdist=5D0,enlarbox=2.1D0,spclowqual=0.2D0,spcmedqual=0.1D0,spchighqual=0.06D0,spclunaqual=0.04D0,tmparr6(6) 1327character c80tmp*80,cubefilename*200 1328if (.not.allocated(b)) iselexttype=2 !Unable to set box using rho 1329do while(.true.) 1330 if (iselexttype==1) then 1331 orgx=minval(a%x)-gridextdist 1332 orgy=minval(a%y)-gridextdist 1333 orgz=minval(a%z)-gridextdist 1334 endx=maxval(a%x)+gridextdist 1335 endy=maxval(a%y)+gridextdist 1336 endz=maxval(a%z)+gridextdist 1337 else if (iselexttype==2) then 1338 orgx=minval( a(:)%x-enlarbox*vdwr_tianlu(a(:)%index) ) 1339 orgy=minval( a(:)%y-enlarbox*vdwr_tianlu(a(:)%index) ) 1340 orgz=minval( a(:)%z-enlarbox*vdwr_tianlu(a(:)%index) ) 1341 endx=maxval( a(:)%x+enlarbox*vdwr_tianlu(a(:)%index) ) 1342 endy=maxval( a(:)%y+enlarbox*vdwr_tianlu(a(:)%index) ) 1343 endz=maxval( a(:)%z+enlarbox*vdwr_tianlu(a(:)%index) ) 1344 end if 1345 molxlen=endx-orgx 1346 molylen=endy-orgy 1347 molzlen=endz-orgz 1348 ntotlow=(nint(molxlen/spclowqual)+1)*(nint(molylen/spclowqual)+1)*(nint(molzlen/spclowqual)+1) 1349 ntotmed=(nint(molxlen/spcmedqual)+1)*(nint(molylen/spcmedqual)+1)*(nint(molzlen/spcmedqual)+1) 1350 ntothigh=(nint(molxlen/spchighqual)+1)*(nint(molylen/spchighqual)+1)*(nint(molzlen/spchighqual)+1) 1351 ntotluna=(nint(molxlen/spclunaqual)+1)*(nint(molylen/spclunaqual)+1)*(nint(molzlen/spclunaqual)+1) 1352 1353 write(*,*) "Please select a method for setting up grid" 1354 if (iselexttype==1) write(*,"(a,f10.5,a)") " -10 Set grid extension distance for mode 1~6, current: Fixed,",gridextdist," Bohr" 1355 if (iselexttype==2) write(*,"(a)") " -10 Set grid extension distance for mode 1~6, current: Adaptive" 1356 if (iselexttype==3) write(*,"(a)") " -10 Set grid extension distance for mode 1~6, current: Detect rho isosurface" 1357 if (iselexttype==1.or.iselexttype==2) then 1358 write(*,"(a,f4.2,a,i14)") " 1 Low quality grid, spacing=",spclowqual," Bohr, number of grids: ",ntotlow 1359 write(*,"(a,f4.2,a,i14)") " 2 Medium quality grid, spacing=",spcmedqual," Bohr, number of grids: ",ntotmed 1360 write(*,"(a,f4.2,a,i14)") " 3 High quality grid, spacing=",spchighqual," Bohr, number of grids: ",ntothigh 1361 write(*,"(a,f4.2,a,i14)") " 4 Lunatic quality grid, spacing=",spclunaqual," Bohr, number of grids:",ntotluna 1362 else 1363 write(*,"(a,f4.2,a,i14)") " 1 Low quality grid, spacing=",spclowqual," Bohr, cost: 1x" 1364 write(*,"(a,f4.2,a,i14)") " 2 Medium quality grid, spacing=",spcmedqual," Bohr, cost: 8x" 1365 write(*,"(a,f4.2,a,i14)") " 3 High quality grid, spacing=",spchighqual," Bohr, cost: 36x" 1366 write(*,"(a,f4.2,a,i14)") " 4 Lunatic quality grid, spacing=",spclunaqual," Bohr, cost: 120x" 1367 end if 1368 write(*,*) "5 Only input grid spacing, automatically set other parameters" 1369 write(*,*) "6 Only input the number of points in X,Y,Z, automatically set other parameters" 1370 write(*,*) "7 Input original point, translation vector and the number of points" 1371 write(*,*) "8 Set center position, grid spacing and box length" 1372 write(*,*) "9 Use grid setting of another cube file" 1373 1374 read(*,*) igridsel 1375 if (igridsel/=-10) then 1376 exit 1377 else 1378 write(*,*) "Please select the type of extension distance:" 1379 write(*,*) "1 Use fixed value in each direction" 1380 write(*,*) "2 Adaptively determined according to van der Waals radius" 1381 write(*,"(a,1PE7.1)") " 3 Detect rho to make the box just accommodate its isosurface, isoval: ",rhocrit 1382 write(*,*) "Hint: In general mode 3 is recommended, mode 1 should be avoided to be used" 1383 read(*,*) iselexttype 1384 if (iselexttype==1) then 1385 do while(.true.) 1386 write(*,*) "Input extension distance (Bohr) e.g. 6.5" 1387 read(*,*) gridextdist 1388 if (gridextdist>0D0) exit 1389 write(*,*) "Error: The value must be larger than 0!" 1390 end do 1391 else if (iselexttype==2) then 1392 do while(.true.) 1393 write(*,*) "Input the ratio for scaling vdW radii, e.g. 1.7" 1394 write(*,"(' Current value is',f12.6)") enlarbox 1395 read(*,*) enlarbox 1396 if (enlarbox>0D0) exit 1397 write(*,*) "Error: The value must be larger than 0!" 1398 end do 1399 else if (iselexttype==3) then 1400 write(*,*) "Select the isovalue of rho" 1401 write(*,*) "1: 0.0001 (10^-4)" 1402 write(*,*) "2: 0.00001 (10^-5)" 1403 write(*,*) "3: 0.000005 (5*10^-6)" 1404 write(*,*) "4: 0.000003 (3*10^-6)" 1405 write(*,*) "5: 0.000001 (10^-6) In general recommended" 1406 write(*,*) "6: 0.0000005 (5*10^-7)" 1407 write(*,*) "7: 0.0000001 (10^-7)" 1408 write(*,*) "8: 0.00000005 (5*10^-8)" 1409 write(*,*) "9: 0.00000001 (10^-8)" 1410 write(*,*) "10: Input by yourself" 1411 read(*,*) iselrho 1412 if (iselrho==1) rhocrit=1D-4 1413 if (iselrho==2) rhocrit=1D-5 1414 if (iselrho==3) rhocrit=5D-6 1415 if (iselrho==4) rhocrit=3D-6 1416 if (iselrho==5) rhocrit=1D-6 1417 if (iselrho==6) rhocrit=5D-7 1418 if (iselrho==7) rhocrit=1D-7 1419 if (iselrho==8) rhocrit=5D-8 1420 if (iselrho==9) rhocrit=1D-8 1421 if (iselrho==10) then 1422 write(*,*) "Input the value, e.g. 0.000005 or 5D-6" 1423 read(*,*) rhocrit 1424 end if 1425 end if 1426 end if 1427end do 1428 1429if (igridsel==1.or.igridsel==2.or.igridsel==3.or.igridsel==4.or.igridsel==5.or.igridsel==6) then 1430 if (iselexttype==1) then 1431 aug3D=gridextdist !Set aug3D, which only affects GUI display, so that the scope of the axis is wide enough to contain the whole grid region 1432 else if (iselexttype==2) then 1433 aug3D=maxval(enlarbox*vdwr_tianlu(a(:)%index))*1.35D0 1434 else if (iselexttype==3) then 1435 call detectboxrho(rhocrit) !determine orgx/y/z and endx/y/z 1436 molxlen=endx-orgx 1437 molylen=endy-orgy 1438 molzlen=endz-orgz 1439 tmparr6(1)=abs(orgx-minval(a%x)) 1440 tmparr6(2)=abs(orgy-minval(a%y)) 1441 tmparr6(3)=abs(orgz-minval(a%z)) 1442 tmparr6(4)=abs(endx-maxval(a%x)) 1443 tmparr6(5)=abs(endy-maxval(a%y)) 1444 tmparr6(6)=abs(endz-maxval(a%z)) 1445 aug3D=maxval(tmparr6)+1 1446 end if 1447end if 1448 1449!Note: orgx,orgy,orgz,endx,endy,endz as well as molx/y/zlen for igridsel==1~6 have already been set above 1450if (igridsel==1.or.igridsel==2.or.igridsel==3.or.igridsel==4.or.igridsel==5) then 1451 if (igridsel==1) dx=spclowqual 1452 if (igridsel==2) dx=spcmedqual 1453 if (igridsel==3) dx=spchighqual 1454 if (igridsel==4) dx=spclunaqual 1455 if (igridsel==5) then 1456 write(*,*) "Input the grid spacing (bohr) e.g. 0.08" 1457 read(*,*) dx 1458 end if 1459 dy=dx 1460 dz=dx 1461 nx=nint(molxlen/dx)+1 1462 ny=nint(molylen/dy)+1 1463 nz=nint(molzlen/dz)+1 1464else if (igridsel==6) then 1465 write(*,*) "Input the number of grid points in X,Y,Z direction e.g. 139,59,80" 1466 read(*,*) nx,ny,nz 1467 dx=molxlen/(nx-1) 1468 dy=molylen/(ny-1) 1469 dz=molzlen/(nz-1) 1470else if (igridsel==7) then 1471 write(*,*) "Input X,Y,Z coordinate of original point (Bohr) e.g. 0.1,4,-1" 1472 read(*,*) orgx,orgy,orgz 1473 write(*,*) "Input X,Y,Z component of translation vector (Bohr) e.g. 0.1,0.1,0.15" 1474 read(*,*) dx,dy,dz 1475 write(*,*) "Input the number of points in X,Y,Z direction e.g. 139,59,80" 1476 read(*,*) nx,ny,nz 1477 endx=orgx+dx*(nx-1) 1478 endy=orgy+dy*(ny-1) 1479 endz=orgz+dz*(nz-1) 1480else if (igridsel==8) then 1481 write(*,*) "Input X,Y,Z coordinate of box center (in Angstrom)" 1482 write(*,*) "or input such as a8 to take the coordinate of atom 8 as box center" 1483 write(*,*) "or input such as a3,a7 to take the midpoint of atom 3 and atom 7 as box center" 1484 read(*,"(a)") c80tmp 1485 if (c80tmp(1:1)=='a') then 1486 do ich=1,len_trim(c80tmp) 1487 if (c80tmp(ich:ich)==',') exit 1488 end do 1489 if (ich==len_trim(c80tmp)+1) then 1490 read(c80tmp(2:),*) itmp 1491 cenx=a(itmp)%x 1492 ceny=a(itmp)%y 1493 cenz=a(itmp)%z 1494 else 1495 read(c80tmp(2:ich-1),*) itmp 1496 read(c80tmp(ich+2:),*) jtmp 1497 cenx=(a(itmp)%x+a(jtmp)%x)/2D0 1498 ceny=(a(itmp)%y+a(jtmp)%y)/2D0 1499 cenz=(a(itmp)%z+a(jtmp)%z)/2D0 1500 end if 1501 else 1502 read(c80tmp,*) cenx,ceny,cenz 1503 cenx=cenx/b2a 1504 ceny=ceny/b2a 1505 cenz=cenz/b2a 1506 end if 1507 write(*,*) "Input the grid spacing (bohr) e.g. 0.08" 1508 read(*,*) dx 1509 dy=dx 1510 dz=dx 1511 write(*,*) "Input the box lengths in X,Y,Z direction (Bohr) e.g. 8.0,8.0,13.5" 1512 read(*,*) molxlen,molylen,molzlen 1513 orgx=cenx-molxlen/2D0 1514 orgy=ceny-molylen/2D0 1515 orgz=cenz-molzlen/2D0 1516 endx=orgx+molxlen 1517 endy=orgy+molylen 1518 endz=orgz+molzlen 1519 nx=nint(molxlen/dx)+1 1520 ny=nint(molylen/dy)+1 1521 nz=nint(molzlen/dz)+1 1522else if (igridsel==9) then 1523 write(*,*) "Input filename of a cube file" 1524 do while(.true.) 1525 read(*,"(a)") cubefilename 1526 inquire(file=cubefilename,exist=alive) 1527 if (alive) then 1528 open(10,file=cubefilename,status="old") 1529 read(10,*) 1530 read(10,*) 1531 read(10,*) nouse,orgx,orgy,orgz 1532 read(10,*) nx,dx 1533 read(10,*) ny,rnouse,dy 1534 read(10,*) nz,rnouse,rnouse,dz 1535 close(10) 1536 exit 1537 else 1538 write(*,*) "Error: File cannot be found, input again" 1539 end if 1540 end do 1541 endx=orgx+dx*(nx-1) 1542 endy=orgy+dy*(ny-1) 1543 endz=orgz+dz*(nz-1) 1544end if 1545 1546!If system is symmetric to Cartesian plane, then slightly adjust grid setting, so that the distribution of grids are symmetric to Cartesian plane 1547!This treatment will make the integrals have much better symmetry 1548diffx=abs(orgx+endx) 1549diffy=abs(orgy+endy) 1550diffz=abs(orgz+endz) 1551if (igridsel>=1.and.igridsel<=6) then 1552 if (diffx<0.05D0) then !The system is symmetry to YZ plane 1553 distxmin=1D10 1554 do ix=1,nx 1555 rnowx=orgx+(ix-1)*dx 1556 if (rnowx>=0D0.and.rnowx<distxmin) distxmin=rnowx 1557 end do 1558 !1D-12 is a perturbation to avoid the grids are degenerate with respect to Cartesian plane, which results in cumbersome degenerate attractors 1559 !However if it is introduced, the integrals will not so close to symmetry 1560 orgx=orgx+(dx/2D0-distxmin) !+1D-15 1561 end if 1562 if (diffy<0.05D0) then !The system is symmetry to XZ plane 1563 distymin=1D10 1564 do iy=1,ny 1565 rnowy=orgy+(iy-1)*dy 1566 if (rnowy>=0D0.and.rnowy<distymin) distymin=rnowy 1567 end do 1568 orgy=orgy+(dy/2D0-distymin) !+1D-15 1569 end if 1570 if (diffz<0.05D0) then !The system is symmetry to XY plane 1571 distzmin=1D10 1572 do iz=1,nz 1573 rnowz=orgz+(iz-1)*dz 1574 if (rnowz>=0D0.and.rnowz<distzmin) distzmin=rnowz 1575 end do 1576 orgz=orgz+(dz/2D0-distzmin) !+1D-15 1577 end if 1578end if 1579 1580write(*,"(' Coordinate of origin in X,Y,Z is ',3f12.6)") orgx,orgy,orgz 1581write(*,"(' Coordinate of end point in X,Y,Z is',3f12.6)") endx,endy,endz 1582write(*,"(' Spacing in X,Y,Z is',3f11.6)") dx,dy,dz 1583write(*,"(' Number of points in X,Y,Z is',3i5,' Total',i10)") nx,ny,nz,nx*ny*nz 1584! do i=1,nx 1585! write(c80tmp,"(D20.13)") orgx+(i-1)*dx 1586! read(c80tmp,*) tmpval 1587! write(*,*) i," x ",tmpval 1588! end do 1589! do i=1,ny 1590! write(c80tmp,"(D20.13)") orgy+(i-1)*dy 1591! read(c80tmp,*) tmpval 1592! write(*,*) i," y ",tmpval 1593! end do 1594! do i=1,nz 1595! write(c80tmp,"(D20.13)") orgz+dfloat(i-1)*dz 1596! read(c80tmp,*) tmpval 1597! write(*,*) i," z ",tmpval 1598! end do 1599! read(*,*) 1600end subroutine 1601 1602 1603!!-- Detect proper box (namely set orgx/y/z,endx/y/z) so that the box can just enclose the isosurface of rho=rhocrit 1604subroutine detectboxrho(rhocrit) 1605use defvar 1606use function 1607implicit real*8(a-h,o-z) 1608real*8 rhocrit 1609tmpfac=1.0D0 1610orgxtmp=minval( a(:)%x-tmpfac*vdwr_tianlu(a(:)%index) ) !Define a too small box, extend each side (to obtain orgx/y/z,endx/y/z) by detecting rho 1611orgytmp=minval( a(:)%y-tmpfac*vdwr_tianlu(a(:)%index) ) 1612orgztmp=minval( a(:)%z-tmpfac*vdwr_tianlu(a(:)%index) ) 1613endxtmp=maxval( a(:)%x+tmpfac*vdwr_tianlu(a(:)%index) ) 1614endytmp=maxval( a(:)%y+tmpfac*vdwr_tianlu(a(:)%index) ) 1615endztmp=maxval( a(:)%z+tmpfac*vdwr_tianlu(a(:)%index) ) 1616scanstp=0.3D0 !Spacing of scan 1617nstpx=nint((endxtmp-orgxtmp)/scanstp)+1 !The number of detection grid in X direction 1618nstpy=nint((endytmp-orgytmp)/scanstp)+1 !The number of detection grid in Y direction 1619nstpz=nint((endztmp-orgztmp)/scanstp)+1 !The number of detection grid in Z direction 1620distmove=0.2D0 1621write(*,*) "Detecting proper box size..." 1622!Detect Z low. Scan XY plane, if rho at a point is larger than "rhocrit", lower down the plane by "distmove" and check again, until all points have rho<rhocrit 1623orgz=orgztmp 1624do while(.true.) 1625 iok=1 1626zl:do ix=1,nstpx 1627 rnowx=orgxtmp+(ix-1)*scanstp 1628 do iy=1,nstpy 1629 rnowy=orgytmp+(iy-1)*scanstp 1630 if (fdens(rnowx,rnowy,orgz)>rhocrit) then 1631 iok=0 1632 exit zl 1633 end if 1634 end do 1635 end do zl 1636 if (iok==1) exit 1637 orgz=orgz-0.2D0 !Lower down the layer 1638end do 1639!Detect Z high 1640endz=endztmp 1641do while(.true.) 1642 iok=1 1643zh:do ix=1,nstpx 1644 rnowx=orgxtmp+(ix-1)*scanstp 1645 do iy=1,nstpy 1646 rnowy=orgytmp+(iy-1)*scanstp 1647 if (fdens(rnowx,rnowy,endz)>rhocrit) then 1648 iok=0 1649 exit zh 1650 end if 1651 end do 1652 end do zh 1653 if (iok==1) exit 1654 endz=endz+0.2D0 1655end do 1656!Detect X low. Scan YZ plane 1657orgx=orgxtmp 1658do while(.true.) 1659 iok=1 1660xl:do iy=1,nstpy 1661 rnowy=orgytmp+(iy-1)*scanstp 1662 do iz=1,nstpz 1663 rnowz=orgztmp+(iz-1)*scanstp 1664 if (fdens(orgx,rnowy,rnowz)>rhocrit) then 1665 iok=0 1666 exit xl 1667 end if 1668 end do 1669 end do xl 1670 if (iok==1) exit 1671 orgx=orgx-0.2D0 1672end do 1673!Detect X high 1674endx=endxtmp 1675do while(.true.) 1676 iok=1 1677xh:do iy=1,nstpy 1678 rnowy=orgytmp+(iy-1)*scanstp 1679 do iz=1,nstpz 1680 rnowz=orgztmp+(iz-1)*scanstp 1681 if (fdens(endx,rnowy,rnowz)>rhocrit) then 1682 iok=0 1683 exit xh 1684 end if 1685 end do 1686 end do xh 1687 if (iok==1) exit 1688 endx=endx+0.2D0 1689end do 1690!Detect Y low. Scan XZ plane 1691orgy=orgytmp 1692do while(.true.) 1693 iok=1 1694yl:do ix=1,nstpx 1695 rnowx=orgxtmp+(ix-1)*scanstp 1696 do iz=1,nstpz 1697 rnowz=orgztmp+(iz-1)*scanstp 1698 if (fdens(rnowx,orgy,rnowz)>rhocrit) then 1699 iok=0 1700 exit yl 1701 end if 1702 end do 1703 end do yl 1704 if (iok==1) exit 1705 orgy=orgy-0.2D0 1706end do 1707!Detect Y high 1708endy=endytmp 1709do while(.true.) 1710 iok=1 1711yh:do ix=1,nstpx 1712 rnowx=orgxtmp+(ix-1)*scanstp 1713 do iz=1,nstpz 1714 rnowz=orgztmp+(iz-1)*scanstp 1715 if (fdens(rnowx,endy,rnowz)>rhocrit) then 1716 iok=0 1717 exit yh 1718 end if 1719 end do 1720 end do yh 1721 if (iok==1) exit 1722 endy=endy+0.2D0 1723end do 1724 1725end subroutine 1726 1727 1728 1729 1730!!------- Integrate a real space function in the basins already partitioned 1731subroutine integratebasin 1732use defvar 1733use util 1734use function 1735use basinintmod 1736implicit real*8(a-h,o-z) 1737real*8 intval(-1:numatt),basinvol(-1:numatt),intvalpriv(-1:numatt),basinvolpriv(-1:numatt) 1738integer walltime1,walltime2 1739character grdfilename*200 1740 1741write(*,*) "Please select integrand:" 1742write(*,*) "-2 Return" 1743write(*,*) "-1 The values of the grid data stored in an external file (.cub/.grd)" 1744write(*,*) "0 The values of the grid data stored in memory" 1745call selfunc_interface(ifuncint) 1746if (ifuncint==-1) then 1747 do while(.true.) 1748 write(*,*) "Input another .cub or .grd file name" 1749 read(*,"(a)") grdfilename 1750 inquire(file=grdfilename,exist=alive) 1751 if (alive) exit 1752 write(*,*) "File not found, input again" 1753 write(*,*) 1754 end do 1755 inamelen=len_trim(grdfilename) 1756 if (grdfilename(inamelen-2:inamelen)=="cub".or.grdfilename(inamelen-3:inamelen)=="cube") then 1757 call readcubetmp(grdfilename,inconsis) 1758 else if (grdfilename(inamelen-2:inamelen)=="grd") then 1759 call readgrdtmp(grdfilename,inconsis) 1760 end if 1761 if (inconsis==1) return 1762 write(*,*) 1763else if (ifuncint==-2) then 1764 return 1765end if 1766 1767call walltime(walltime1) 1768CALL CPU_TIME(time_begin) 1769write(*,*) "Integrating, please wait..." 1770intval=0D0 1771basinvol=0D0 1772ifinish=0 1773nthreads=getNThreads() 1774!$OMP PARALLEL private(ix,iy,iz,irealatt,rnowx,rnowy,rnowz,tmpval,intvalpriv,basinvolpriv) shared(intval,basinvol,ifinish) NUM_THREADS(nthreads) 1775intvalpriv=0D0 1776basinvolpriv=0D0 1777!$OMP do schedule(DYNAMIC) 1778do iz=2,nz-1 1779 do iy=2,ny-1 1780 do ix=2,nx-1 1781 rnowx=orgx+(ix-1)*dx 1782 rnowy=orgy+(iy-1)*dy 1783 rnowz=orgz+(iz-1)*dz 1784 if (ifuncint==-1) then 1785 tmpval=cubmattmp(ix,iy,iz) 1786 else if (ifuncint==0) then 1787 tmpval=cubmat(ix,iy,iz) 1788 else 1789 tmpval=calcfuncall(ifuncint,rnowx,rnowy,rnowz) 1790 end if 1791 irealatt=gridbas(ix,iy,iz) 1792 intvalpriv(irealatt)=intvalpriv(irealatt)+tmpval 1793 basinvolpriv(irealatt)=basinvolpriv(irealatt)+1 1794 end do 1795 end do 1796 ifinish=ifinish+1 1797 if (ifuncint>=1) write(*,"(' Finished:',i5,'/',i5)") ifinish,nz 1798end do 1799!$OMP end do 1800!$OMP CRITICAL 1801 intval=intval+intvalpriv 1802 basinvol=basinvol+basinvolpriv 1803!$OMP end CRITICAL 1804!$OMP END PARALLEL 1805dvol=dx*dy*dz 1806intval=intval*dvol 1807basinvol=basinvol*dvol !Basin volume 1808write(*,*) " #Basin Integral(a.u.) Volume(a.u.^3)" 1809do irealatt=1,numrealatt 1810 write(*,"(i8,f22.10,f20.8)") irealatt,intval(irealatt),basinvol(irealatt) 1811end do 1812write(*,"(' Sum of above values:',f20.8)") sum(intval(1:numrealatt)) 1813if (any(gridbas(2:nx-1,2:ny-1,2:nz-1)==0)) write(*,"(' Integral of unassigned grids:',f20.8)") intval(0) 1814if (any(gridbas(2:nx-1,2:ny-1,2:nz-1)==-1)) write(*,"(' Integral of the grids travelled to box boundary:',f20.8)") intval(-1) 1815 1816CALL CPU_TIME(time_end) 1817call walltime(walltime2) 1818write(*,"(' Integrating basins took up CPU time',f12.2,'s, wall clock time',i10,'s')") time_end-time_begin,walltime2-walltime1 1819end subroutine 1820 1821 1822 1823!!------- Obtain atomic population in a basin region (e.g. ELF bond basin) via AIM partition 1824subroutine atmpopinbasin 1825use defvar 1826use basinintmod 1827implicit real*8(a-h,o-z) 1828inquire(file="basin.cub",exist=alive) 1829if (alive .eqv. .false.) then 1830 write(*,*) "Error: basin.cub is not existed in current folder!" 1831 return 1832else 1833 if (allocated(cubmattmp)) deallocate(cubmattmp) 1834 call readcubetmp("basin.cub",inconsis) 1835 if (inconsis==1) then 1836 write(*,*) "Error: The grid setting of basin.cub is inconsistent with present grid data" 1837 return 1838 end if 1839end if 1840do while(.true.) 1841 write(*,*) 1842 write(*,*) "Study population of which atom? Input the index of corresponding attractor" 1843 write(*,*) "For example, 3" 1844 write(*,*) "Input 0 can exit" 1845 read(*,*) iatt 1846 if (iatt==0) then 1847 exit 1848 else if (iatt<0.or.iatm>numrealatt) then 1849 write(*,*) "Error: Attractor index exceeded valid range!" 1850 return 1851 end if 1852 write(*,*) "Study its population in which basin of the basin.cub file? e.g. 6" 1853 read(*,*) ibasin 1854 atmpop=0 1855 do iz=2,nz-1 1856 do iy=2,ny-1 1857 do ix=2,nx-1 1858 if (nint(cubmattmp(ix,iy,iz))==ibasin.and.gridbas(ix,iy,iz)==iatt) atmpop=atmpop+cubmat(ix,iy,iz) 1859 end do 1860 end do 1861 end do 1862 atmpop=atmpop*dx*dy*dz 1863 write(*,"(' Population of attractor',i4,' in external basin',i4,' is',f12.5)") iatt,ibasin,atmpop 1864end do 1865deallocate(cubmattmp) 1866end subroutine 1867 1868 1869 1870!!----------------- Calculate multipole moment in the basins 1871subroutine multipolebasin 1872use defvar 1873use basinintmod 1874use function 1875implicit real*8 (a-h,o-z) 1876do while(.true.) 1877 write(*,*) "Input the index of the basin in question, e.g. 5" 1878 write(*,"(a)") " Note: -1 means printing all basin results on screen, -2 means printing to multipol.txt in current folder. Input 0 can return" 1879 read(*,*) itmp 1880 if (itmp==-1.or.itmp==-2) then 1881 ibegin=1 1882 iend=numrealatt 1883 if (itmp==-2) then 1884 ioutid=10 1885 open(10,file="multipol.txt",status="replace") 1886 end if 1887 else if (itmp==0) then 1888 return 1889 else 1890 ibegin=itmp 1891 iend=itmp 1892 ioutid=6 1893 end if 1894 write(*,*) "Calculating, please wait..." 1895 write(ioutid,*) "Note: All units shown below are in a.u.!" 1896 write(ioutid,*) 1897 1898 dipelextot=0D0 1899 dipeleytot=0D0 1900 dipeleztot=0D0 1901 eleinttot=0D0 1902 do ibas=ibegin,iend 1903 ! xcen=0 !Use centroid of basin as center, but this is a bad idea 1904 ! ycen=0 1905 ! zcen=0 1906 ! nbasgrid=0 1907 ! do iz=2,nz-1 1908 ! do iy=2,ny-1 1909 ! do ix=2,nx-1 1910 ! if (gridbas(ix,iy,iz)==ibas) then 1911 ! nbasgrid=nbasgrid+1 1912 ! rnowx=orgx+(ix-1)*dx 1913 ! rnowy=orgy+(iy-1)*dy 1914 ! rnowz=orgz+(iz-1)*dz 1915 ! tmpdens=ELF_LOL(rnowx,rnowy,rnowz,"ELF") 1916 ! xcen=xcen+rnowx*tmpdens 1917 ! ycen=ycen+rnowy*tmpdens 1918 ! zcen=zcen+rnowz*tmpdens 1919 ! end if 1920 ! end do 1921 ! end do 1922 ! end do 1923 ! xcen=xcen/nbasgrid 1924 ! ycen=ycen/nbasgrid 1925 ! zcen=zcen/nbasgrid 1926 xcen=realattxyz(ibas,1) 1927 ycen=realattxyz(ibas,2) 1928 zcen=realattxyz(ibas,3) 1929 dvol=dx*dy*dz 1930 ! write(*,"(' The X,Y,Z of the center of the basin:')") 1931 ! write(*,"(3f14.8,' Bohr',/)") xcen,ycen,zcen 1932 1933 eleint=0D0 1934 xint=0D0 1935 yint=0D0 1936 zint=0D0 1937 xxint=0D0 1938 yyint=0D0 1939 zzint=0D0 1940 xyint=0D0 1941 yzint=0D0 1942 xzint=0D0 1943nthreads=getNThreads() 1944!$OMP PARALLEL private(ix,iy,iz,rnowx,rnowy,rnowz,tmpmul,rx,ry,rz,eleintp,xintp,yintp,zintp,xxintp,yyintp,zzintp,xyintp,yzintp,xzintp) NUM_THREADS(nthreads) 1945 eleintp=0D0 1946 xintp=0D0 1947 yintp=0D0 1948 zintp=0D0 1949 xxintp=0D0 1950 yyintp=0D0 1951 zzintp=0D0 1952 xyintp=0D0 1953 yzintp=0D0 1954 xzintp=0D0 1955!$OMP do schedule(DYNAMIC) 1956 do iz=2,nz-1 1957 rnowz=orgz+(iz-1)*dz 1958 do iy=2,ny-1 1959 rnowy=orgy+(iy-1)*dy 1960 do ix=2,nx-1 1961 if (gridbas(ix,iy,iz)==ibas) then 1962 rnowx=orgx+(ix-1)*dx 1963 if (ifuncbasin==1) then 1964 tmpmul=dvol*cubmat(ix,iy,iz) !The cubmat currently is just electron density 1965 else 1966 tmpmul=dvol*fdens(rnowx,rnowy,rnowz) 1967 end if 1968 rx=rnowx-xcen 1969 ry=rnowy-ycen 1970 rz=rnowz-zcen 1971 eleintp=eleintp+tmpmul !monopole 1972 xintp=xintp+rx*tmpmul 1973 yintp=yintp+ry*tmpmul 1974 zintp=zintp+rz*tmpmul 1975 xxintp=xxintp+rx*rx*tmpmul 1976 yyintp=yyintp+ry*ry*tmpmul 1977 zzintp=zzintp+rz*rz*tmpmul 1978 xyintp=xyintp+rx*ry*tmpmul 1979 yzintp=yzintp+ry*rz*tmpmul 1980 xzintp=xzintp+rx*rz*tmpmul 1981 end if 1982 end do 1983 end do 1984 end do 1985!$OMP end do 1986!$OMP CRITICAL 1987 eleint=eleint+eleintp 1988 xint=xint+xintp 1989 yint=yint+yintp 1990 zint=zint+zintp 1991 xxint=xxint+xxintp 1992 yyint=yyint+yyintp 1993 zzint=zzint+zzintp 1994 xyint=xyint+xyintp 1995 yzint=yzint+yzintp 1996 xzint=xzint+xzintP 1997!$OMP end CRITICAL 1998!$OMP END PARALLEL 1999 if (itmp==-1.or.itmp==-2) write(ioutid,"(' Basin',i8)") ibas 2000 if (itmp==-2) write(*,"(' Outputting basin',i8)") ibas 2001 write(ioutid,"(' Basin electric monopole moment:',f12.6)") -eleint 2002 write(ioutid,"(' Basin electric dipole moment:')") 2003 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Magnitude=',f12.6)") -xint,-yint,-zint,sqrt(xint**2+yint**2+zint**2) 2004 eleinttot=eleinttot+eleint 2005 dipelex=-eleint*xcen+(-xint) !Contribution to molecular total dipole moment 2006 dipeley=-eleint*ycen+(-yint) 2007 dipelez=-eleint*zcen+(-zint) 2008 dipelextot=dipelextot+dipelex 2009 dipeleytot=dipeleytot+dipeley 2010 dipeleztot=dipeleztot+dipelez 2011 write(ioutid,"(' Basin electron contribution to molecular dipole moment:')") 2012 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Magnitude=',f12.6)") dipelex,dipeley,dipelez,sqrt(dipelex**2+dipeley**2+dipelez**2) 2013 write(ioutid,"(' Basin electric quadrupole moment (Cartesian form):')") 2014 rrint=xxint+yyint+zzint 2015 QXX=-(3*xxint-rrint)/2 2016 QYY=-(3*yyint-rrint)/2 2017 QZZ=-(3*zzint-rrint)/2 2018 write(ioutid,"(' QXX=',f12.6,' QXY=',f12.6,' QXZ=',f12.6)") QXX,-(3*xyint)/2,-(3*xzint)/2 2019 write(ioutid,"(' QYX=',f12.6,' QYY=',f12.6,' QYZ=',f12.6)") -(3*xyint)/2,QYY,-(3*yzint)/2 2020 write(ioutid,"(' QZX=',f12.6,' QZY=',f12.6,' QZZ=',f12.6)") -(3*xzint)/2,-(3*yzint)/2,QZZ 2021 write(ioutid,"( ' The magnitude of electric quadrupole moment (Cartesian form):',f12.6)") dsqrt(2D0/3D0*(QXX**2+QYY**2+QZZ**2)) 2022 R20=-(3*zzint-rrint)/2D0 !Notice that the negative sign, because electrons carry negative charge 2023 R2n1=-dsqrt(3D0)*yzint 2024 R2p1=-dsqrt(3D0)*xzint 2025 R2n2=-dsqrt(3D0)*xyint 2026 R2p2=-dsqrt(3D0)/2D0*(xxint-yyint) 2027 write(ioutid,"(' Electric quadrupole moments (Spherical harmonic form):')") 2028 write(ioutid,"(' Q_2,0 =',f11.6,' Q_2,-1=',f11.6,' Q_2,1=',f11.6)") R20,R2n1,R2p1 2029 write(ioutid,"(' Q_2,-2=',f11.6,' Q_2,2 =',f11.6)") R2n2,R2p2 2030 write(ioutid,"( ' Magnitude: |Q_2|=',f12.6)") dsqrt(R20**2+R2n1**2+R2p1**2+R2n2**2+R2p2**2) 2031 write(ioutid,*) 2032 end do 2033 if (itmp==-1.or.itmp==-2) then !Output overall properties, most users are not interested in them 2034 dipnucx=sum(a(:)%x*a(:)%charge) 2035 dipnucy=sum(a(:)%y*a(:)%charge) 2036 dipnucz=sum(a(:)%z*a(:)%charge) 2037 write(ioutid,"( ' Molecular net charge:',f12.6)") sum(a%charge)-eleinttot 2038 write(ioutid,"( ' Nuclear contribution to molecular dipole moment:')") 2039 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Norm=',f12.6)") dipnucx,dipnucy,dipnucz,sqrt(dipnucx**2+dipnucy**2+dipnucz**2) 2040 write(ioutid,"( ' Electron contribution to molecular dipole moment:')") 2041 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Norm=',f12.6)") dipelextot,dipeleytot,dipeleztot,sqrt(dipelextot**2+dipeleytot**2+dipeleztot**2) 2042 dipmolx=dipnucx+dipelextot 2043 dipmoly=dipnucy+dipeleytot 2044 dipmolz=dipnucz+dipeleztot 2045 write(ioutid,"( ' Molecular dipole moment:')") 2046 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Norm=',f12.6)") dipmolx,dipmoly,dipmolz,sqrt(dipmolx**2+dipmoly**2+dipmolz**2) 2047 write(ioutid,*) 2048 end if 2049 if (itmp==-2) then 2050 close(10) 2051 write(*,*) "Outputting finished!" 2052 write(*,*) 2053 end if 2054end do 2055end subroutine 2056 2057 2058!!------------------ Calculate localized index and delocalization index within and between basins 2059!Most of the codes are adapted from fuzzyana.f90 2060!itask==0 means calculate BOM and LI,DI, itask==1 means calculate and output BOM to BOM.txt in current folder 2061!BOM means overlap matrix of all occupied orbitals in basins 2062subroutine LIDIbasin(itask) 2063use defvar 2064use basinintmod 2065use function 2066use util 2067implicit real*8 (a-h,o-z) 2068real*8 DI(numrealatt,numrealatt),DIa(numrealatt,numrealatt),DIb(numrealatt,numrealatt) !Delocalization index matrix 2069real*8 LI(numrealatt),LIa(numrealatt),LIb(numrealatt) !Localization index array 2070real*8,allocatable :: BOM(:,:,:),BOMa(:,:,:),BOMb(:,:,:),BOMsum(:,:),BOMsuma(:,:),BOMsumb(:,:) !BOM(i,j,k) means overlap matrix of MO i,j in atom k space 2071real*8 :: BOMtmp(nmo,nmo),orbval(nmo) 2072character selectyn 2073integer itask 2074 2075!Allocate space for BOM 2076if (wfntype==0.or.wfntype==2.or.wfntype==3) then !RHF,ROHF,R-post-HF 2077 if (wfntype==3) then !R-post-HF, need to consider all orbitals 2078 nmatsize=nmo 2079 else !RHF,ROHF 2080 !High-lying virtual orbitals will be deleted, especially for .fch case (In fact, before entering this function, those >= LUMO+10 has already been discarded) 2081 !Notice that occupation number may be not contiguous, some low-lying orbital may have 2082 !zero occupation due to modification by users, so we can't simply use nelec to determine matrix size 2083 do nmatsize=nmo,1,-1 2084 if (MOocc(nmatsize)/=0) exit 2085 end do 2086 if (nmo-nmatsize>0) write(*,"(' Note: The highest',i6,' virtual orbitals will not be taken into account')") nmo-nmatsize 2087 end if 2088 if (allocated(BOM)) deallocate(BOM,BOMsum) 2089 allocate(BOM(nmatsize,nmatsize,numrealatt),BOMsum(nmatsize,nmatsize)) 2090 BOM=0 2091 BOMsum=0 2092else if (wfntype==1.or.wfntype==4) then !UHF,U-post-HF 2093 do iendalpha=nmo,1,-1 2094 if (MOtype(iendalpha)==1) exit 2095 end do 2096 if (wfntype==4) then 2097 nmatsizea=iendalpha !Total number of alpha orbitals 2098 nmatsizeb=nmo-nmatsizea !Total number of beta orbitals 2099 else 2100 do nmatsizea=iendalpha,1,-1 2101 if (MOocc(nmatsizea)/=0D0) exit 2102 end do 2103 if (nint(nbelec)==0) then 2104 nmatsizeb=0 2105 else 2106 do nmatsizeb=nmo,iendalpha+1,-1 2107 if (MOocc(nmatsizeb)/=0D0) exit 2108 end do 2109 nmatsizeb=nmatsizeb-iendalpha 2110 end if 2111 if (iendalpha-nmatsizea>0) write(*,"('Note: The highest',i6,' alpha virtual orbitals will not be taken into account')") iendalpha-nmatsizea 2112 if (nmo-iendalpha-nmatsizeb>0) write(*,"('Note: The highest',i6,' beta virtual orbitals will not be taken into account')") nmo-iendalpha-nmatsizeb 2113 end if 2114 if (allocated(BOMa)) deallocate(BOMa,BOMb,BOMsuma,BOMsumb) 2115 allocate( BOMa(nmatsizea,nmatsizea,numrealatt),BOMb(nmatsizeb,nmatsizeb,numrealatt) ) 2116 allocate( BOMsuma(nmatsizea,nmatsizea),BOMsumb(nmatsizeb,nmatsizeb) ) 2117 BOMa=0 2118 BOMb=0 2119 BOMsuma=0 2120 BOMsumb=0 2121end if 2122 2123!Calculate BOM 2124dvol=dx*dy*dz 2125call walltime(nwalltime1) 2126if (wfntype==0.or.wfntype==2.or.wfntype==3) then !RHF,ROHF,R-post-HF 2127 do ibas=1,numrealatt 2128 write(*,"(' Generating orbital overlap matrix for basin',i6,' of',i6,' ......')") ibas,numrealatt 2129nthreads=getNThreads() 2130!$OMP parallel shared(BOM) private(ix,iy,iz,rnowx,rnowy,rnowz,imo,jmo,BOMtmp,orbval) num_threads(nthreads) 2131 BOMtmp=0D0 2132!$OMP do schedule(DYNAMIC) 2133 do iz=2,nz-1 2134 do iy=2,ny-1 2135 do ix=2,nx-1 2136 if (gridbas(ix,iy,iz)==ibas) then 2137 rnowx=orgx+(ix-1)*dx 2138 rnowy=orgy+(iy-1)*dy 2139 rnowz=orgz+(iz-1)*dz 2140 call orbderv(1,1,nmatsize,rnowx,rnowy,rnowz,orbval) 2141 do imo=1,nmatsize 2142 do jmo=imo,nmatsize 2143 BOMtmp(imo,jmo)=BOMtmp(imo,jmo)+orbval(imo)*orbval(jmo)*dvol 2144 end do 2145 end do 2146 end if 2147 end do 2148 end do 2149 end do 2150!$OMP end do 2151!$OMP CRITICAL 2152 BOM(:,:,ibas)=BOM(:,:,ibas)+BOMtmp(1:nmatsize,1:nmatsize) 2153!$OMP end CRITICAL 2154!$OMP end parallel 2155 BOM(:,:,ibas)=BOM(:,:,ibas)+transpose(BOM(:,:,ibas)) 2156 do imo=1,nmatsize 2157 BOM(imo,imo,ibas)=BOM(imo,imo,ibas)/2D0 2158 end do 2159 end do 2160else if (wfntype==1.or.wfntype==4) then !UHF,U-post-HF 2161 do ibas=1,numrealatt 2162 !Alpha part 2163 write(*,"(' Generating orbital overlap matrix for basin',i6,' of',i6,' ......')") ibas,numrealatt 2164nthreads=getNThreads() 2165!$OMP parallel shared(BOMa) private(ix,iy,iz,rnowx,rnowy,rnowz,imo,jmo,BOMtmp,orbval) num_threads(nthreads) 2166 BOMtmp=0D0 2167!$OMP do schedule(DYNAMIC) 2168 do iz=2,nz-1 2169 do iy=2,ny-1 2170 do ix=2,nx-1 2171 if (gridbas(ix,iy,iz)==ibas) then 2172 rnowx=orgx+(ix-1)*dx 2173 rnowy=orgy+(iy-1)*dy 2174 rnowz=orgz+(iz-1)*dz 2175 call orbderv(1,1,nmatsizea,rnowx,rnowy,rnowz,orbval) 2176 do imo=1,nmatsizea 2177 do jmo=imo,nmatsizea 2178 BOMtmp(imo,jmo)=BOMtmp(imo,jmo)+orbval(imo)*orbval(jmo)*dvol 2179 end do 2180 end do 2181 end if 2182 end do 2183 end do 2184 end do 2185!$OMP end do 2186!$OMP CRITICAL 2187 BOMa(:,:,ibas)=BOMa(:,:,ibas)+BOMtmp(1:nmatsizea,1:nmatsizea) 2188!$OMP end CRITICAL 2189!$OMP end parallel 2190 BOMa(:,:,ibas)=BOMa(:,:,ibas)+transpose(BOMa(:,:,ibas)) 2191 do imo=1,nmatsizea 2192 BOMa(imo,imo,ibas)=BOMa(imo,imo,ibas)/2D0 2193 end do 2194 2195 !Beta part 2196 if (nmatsizeb>0) then !If there is no beta orbital, then nmatsizeb=0, and we will do nothing 2197 MOinit=iendalpha+1 2198 MOend=iendalpha+nmatsizeb 2199! write(*,*) MOinit,MOend,nmatsizeb 2200nthreads=getNThreads() 2201!$OMP parallel shared(BOMb) private(ix,iy,iz,rnowx,rnowy,rnowz,imo,imotmp,jmo,jmotmp,BOMtmp,orbval) num_threads(nthreads) 2202 BOMtmp=0D0 2203!$OMP do schedule(DYNAMIC) 2204 do iz=2,nz-1 2205 do iy=2,ny-1 2206 do ix=2,nx-1 2207 if (gridbas(ix,iy,iz)==ibas) then 2208 rnowx=orgx+(ix-1)*dx 2209 rnowy=orgy+(iy-1)*dy 2210 rnowz=orgz+(iz-1)*dz 2211 call orbderv(1,MOinit,MOend,rnowx,rnowy,rnowz,orbval) 2212 do imo=MOinit,MOend 2213 imotmp=imo-iendalpha !So that the index start from 1 to nbelec 2214 do jmo=imo,MOend 2215 jmotmp=jmo-iendalpha 2216 BOMtmp(imotmp,jmotmp)=BOMtmp(imotmp,jmotmp)+orbval(imo)*orbval(jmo)*dvol 2217 end do 2218 end do 2219 end if 2220 end do 2221 end do 2222 end do 2223!$OMP end do 2224!$OMP CRITICAL 2225 BOMb(:,:,ibas)=BOMb(:,:,ibas)+BOMtmp(1:nmatsizeb,1:nmatsizeb) 2226!$OMP end CRITICAL 2227!$OMP end parallel 2228 BOMb(:,:,ibas)=BOMb(:,:,ibas)+transpose(BOMb(:,:,ibas)) 2229 do imo=1,nmatsizeb 2230 BOMb(imo,imo,ibas)=BOMb(imo,imo,ibas)/2D0 2231 end do 2232 end if 2233 end do 2234end if 2235 2236write(*,*) 2237!Check sanity of BOM 2238if (wfntype==0.or.wfntype==2.or.wfntype==3) then !RHF,ROHF,R-post-HF 2239 do ibas=1,numrealatt 2240 BOMsum=BOMsum+BOM(:,:,ibas) 2241 end do 2242 BOMerror=identmaterr(BOMsum)/numrealatt 2243 write(*,"(' Error of BOM is',f14.8)") BOMerror 2244 if (BOMerror>0.02D0) write(*,"(a)") " Warning: The integration is not very accurate" 2245! call showmatgau(BOMsum,"BOMsum",0,"f14.8",6) 2246else if (wfntype==1.or.wfntype==4) then !UHF,U-post-HF 2247 BOMerrorb=0D0 2248 do ibas=1,numrealatt 2249 BOMsuma=BOMsuma+BOMa(:,:,ibas) 2250 if (nmatsizeb>0) BOMsumb=BOMsumb+BOMb(:,:,ibas) 2251 end do 2252 BOMerrora=identmaterr(BOMsuma)/numrealatt 2253 if (nmatsizeb>0) BOMerrorb=identmaterr(BOMsumb)/numrealatt 2254 write(*,"(' Error of alpha BOM is',f14.8)") BOMerrora 2255 if (nmatsizeb>0) write(*,"(' Error of Beta BOM is ',f14.8)") BOMerrorb 2256 if (BOMerrora>0.02D0.or.BOMerrorb>0.02D0) write(*,"(a)") " Warning: The integration is not very accurate" 2257! call showmatgau(BOMsumb,"BOMsumb",0,"f14.8",6) 2258end if 2259 2260!Output BOM 2261if (itask==1) then 2262 open(10,file="BOM.txt",status="replace") 2263 if (wfntype==0.or.wfntype==2.or.wfntype==3) then 2264 do ibas=1,numrealatt 2265 write(10,"('Orbital overlap matrix of basin',i6)") ibas 2266 call showmatgau(BOM(:,:,ibas),"",1,"f14.8",10) 2267 write(10,*) 2268 end do 2269 else if (wfntype==1.or.wfntype==4) then 2270 do ibas=1,numrealatt 2271 write(10,"('Alpha part of orbital overlap matrix of basin',i6)") ibas 2272 call showmatgau(BOMa(:,:,ibas),"",1,"f14.8",10) 2273 if (nmatsizeb>0) then 2274 write(10,"('Beta part of orbital overlap matrix of basin',i6)") ibas 2275 call showmatgau(BOMb(:,:,ibas),"",1,"f14.8",10) 2276 end if 2277 write(10,*) 2278 end do 2279 end if 2280 close(10) 2281 write(*,*) 2282 write(*,*) "Done, the matrices have been exported to BOM.txt in current folder" 2283 return 2284end if 2285 2286!Generate DI and LI 2287!RHF,R-post-HF, DI_A,B=2��[i,j]dsqrt(n_i*n_j)*S_i,j_A * S_i,j_B where i and j are non-spin orbitals 2288write(*,*) "Generating LI and DI..." 2289if (any(MOocc<0)) then 2290 where(MOocc<0) MOocc=0 2291 write(*,"(a)") " Note: Some occupation numbers are negative. In order to make the calculation feasible, they have been set to zero" 2292 write(*,*) "Press ENTER to continue" 2293 read(*,*) 2294end if 2295if (wfntype==0.or.wfntype==3) then 2296 DI=0D0 2297 do ibas=1,numrealatt 2298 do jbas=ibas,numrealatt 2299 do iorb=1,nmatsize 2300 do jorb=1,nmatsize 2301 DI(ibas,jbas)=DI(ibas,jbas)+dsqrt(MOocc(iorb)*MOocc(jorb))*BOM(iorb,jorb,ibas)*BOM(iorb,jorb,jbas) 2302 end do 2303 end do 2304 end do 2305 LI(ibas)=DI(ibas,ibas) 2306 end do 2307 DI=2*(DI+transpose(DI)) 2308 do ibas=1,numrealatt !Diagonal terms are the sum of corresponding row or column 2309 DI(ibas,ibas)=0D0 2310 DI(ibas,ibas)=sum(DI(ibas,:)) 2311 end do 2312else if (wfntype==2) then !ROHF 2313 DIa=0D0 2314 DIb=0D0 2315 do nmoclose=nmatsize,1,-1 2316 if (MOtype(nmoclose)==0) exit 2317 end do 2318 do ibas=1,numrealatt 2319 do jbas=ibas,numrealatt 2320 !Alpha 2321 do iorb=1,nmatsize !The number of close or alpha orbitals needed to be concerned 2322 occi=MOocc(iorb) 2323 if (MOtype(iorb)==0) occi=occi/2D0 2324 do jorb=1,nmatsize 2325 occj=MOocc(jorb) 2326 if (MOtype(jorb)==0) occj=occj/2D0 2327 DIa(ibas,jbas)=DIa(ibas,jbas)+dsqrt(occi*occj)*BOM(iorb,jorb,ibas)*BOM(iorb,jorb,jbas) 2328 end do 2329 end do 2330 !Beta 2331 do iorb=1,nmoclose !The number of close orbitals needed to be concerned 2332 do jorb=1,nmoclose 2333 DIb(ibas,jbas)=DIb(ibas,jbas)+dsqrt(MOocc(iorb)/2D0*MOocc(jorb)/2D0)*BOM(iorb,jorb,ibas)*BOM(iorb,jorb,jbas) 2334 end do 2335 end do 2336 end do 2337 LIa(ibas)=DIa(ibas,ibas) 2338 LIb(ibas)=DIb(ibas,ibas) 2339 end do 2340 DIa=2*(DIa+transpose(DIa)) 2341 DIb=2*(DIb+transpose(DIb)) 2342 do ibas=1,numrealatt !Diagonal terms are the sum of corresponding row or column 2343 DIa(ibas,ibas)=0D0 2344 DIb(ibas,ibas)=0D0 2345 DIa(ibas,ibas)=sum(DIa(ibas,:)) 2346 DIb(ibas,ibas)=sum(DIb(ibas,:)) 2347 end do 2348 !Combine alpha and Beta to total 2349 DI=DIa+DIb 2350 LI=LIa+LIb 2351!UHF,U-post-HF DI(A,B)=2��[i,j]dsqrt(n_i*n_j)*S_i,j_A * S_i,j_B where i and j are spin orbitals 2352else if (wfntype==1.or.wfntype==4) then 2353 !Alpha 2354 DIa=0D0 2355 do ibas=1,numrealatt 2356 do jbas=ibas,numrealatt 2357 do iorb=1,nmatsizea 2358 do jorb=1,nmatsizea 2359 DIa(ibas,jbas)=DIa(ibas,jbas)+dsqrt(MOocc(iorb)*MOocc(jorb))*BOMa(iorb,jorb,ibas)*BOMa(iorb,jorb,jbas) 2360 end do 2361 end do 2362 end do 2363 LIa(ibas)=DIa(ibas,ibas) 2364 end do 2365 DIa=2*(DIa+transpose(DIa)) 2366 !Beta 2367 if (nmatsizeb>0) then 2368 DIb=0D0 2369 MOinit=iendalpha+1 !Index range of beta orbitals 2370 MOend=iendalpha+nmatsizeb 2371 do ibas=1,numrealatt 2372 do jbas=ibas,numrealatt 2373 do iorb=MOinit,MOend 2374 iorbtmp=iorb-iendalpha 2375 do jorb=MOinit,MOend 2376 jorbtmp=jorb-iendalpha 2377 DIb(ibas,jbas)=DIb(ibas,jbas)+dsqrt(MOocc(iorb)*MOocc(jorb))*BOMb(iorbtmp,jorbtmp,ibas)*BOMb(iorbtmp,jorbtmp,jbas) 2378 end do 2379 end do 2380 end do 2381 LIb(ibas)=DIb(ibas,ibas) 2382 end do 2383 DIb=2*(DIb+transpose(DIb)) 2384 end if 2385 do ibas=1,numrealatt !Diagonal terms are the sum of corresponding row or column 2386 DIa(ibas,ibas)=0D0 2387 DIb(ibas,ibas)=0D0 2388 DIa(ibas,ibas)=sum(DIa(ibas,:)) 2389 DIb(ibas,ibas)=sum(DIb(ibas,:)) 2390 end do 2391 !Combine alpha and Beta to total 2392 DI=DIa+DIb 2393 LI=LIa+LIb 2394end if 2395 2396call walltime(nwalltime2) 2397write(*,"(' Calculation took up',i8,' seconds wall clock time',/)") nwalltime2-nwalltime1 2398 2399!Output LI and DI 2400ioutid=6 2401100 write(ioutid,"(a,/)") " Note: Diagonal terms are the sum of corresponding row or column elements" 2402if (wfntype==1.or.wfntype==2.or.wfntype==4) then !UHF,ROHF,U-post-HF, output each spin component first 2403 !Alpha 2404 call showmatgau(DIa,"Delocalization index matrix for alpha spin",0,"f14.8",ioutid) 2405 write(ioutid,*) 2406 write(ioutid,*) "Localization index for alpha electron:" 2407 do ibas=1,numrealatt 2408 write(ioutid,"(i6,':',f7.3)",advance='no') ibas,LIa(ibas) 2409 if (mod(ibas,5)==0) write(ioutid,*) 2410 end do 2411 write(ioutid,*) 2412 write(ioutid,*) 2413 !Beta 2414 call showmatgau(DIb,"Delocalization index matrix for beta spin",0,"f14.8",ioutid) 2415 write(ioutid,*) 2416 write(ioutid,*) "Localization index for beta spin:" 2417 do ibas=1,numrealatt 2418 write(ioutid,"(i6,':',f7.3)",advance='no') ibas,LIb(ibas) 2419 if (mod(ibas,5)==0) write(ioutid,*) 2420 end do 2421 write(ioutid,*) 2422 write(ioutid,*) 2423end if 2424!Alpha+Beta 2425call showmatgau(DI,"Total delocalization index matrix",0,"f14.8",ioutid) 2426write(ioutid,*) 2427write(ioutid,*) "Total localization index:" 2428do ibas=1,numrealatt 2429 write(ioutid,"(i6,':',f7.3)",advance='no') ibas,LI(ibas) 2430 if (mod(ibas,5)==0) write(ioutid,*) 2431end do 2432if (ioutid==10) then 2433 write(*,*) "Done!" 2434 close(10) 2435 return 2436end if 2437write(*,*) 2438write(*,*) 2439write(*,*) "If also output the result to LIDI.txt in current folder? y/n" 2440read(*,*) selectyn 2441if (selectyn=='y'.or.selectyn=='Y') then 2442 open(10,file="LIDI.txt",status="replace") 2443 ioutid=10 2444 goto 100 2445end if 2446end subroutine 2447 2448 2449 2450!!------- Integrate a real space function in the basins with multi-level refinement, indenspensible for Laplacian 2451!DEPRECATED, since mixed type of grids perform better and faster 2452subroutine integratebasinrefine 2453use defvar 2454use util 2455use function 2456use basinintmod 2457implicit real*8(a-h,o-z) 2458character c200tmp*200 2459real*8 intval(-1:numatt),basinvol(-1:numatt),intvalpriv(-1:numatt),basinvolpriv(-1:numatt) 2460integer walltime1,walltime2 2461real*8 :: critlevel1=0.1D0,critlevel2=0.5D0,critlevel3=1D0 2462integer :: nrefine1=1,nrefine2=2,nrefine3=5,nrefine4=7 2463if (ifuncbasin/=1) then 2464 write(*,*) "Error: This function is only applicable to AIM basins!" 2465 return 2466end if 2467 2468do while(.true.) 2469 write(*,*) "-2 Return" 2470 write(*,*) "-1 Print and set parameters for multi-level refinement" 2471 call selfunc_interface(ifuncint) 2472 if (ifuncint==-2) then 2473 return 2474 else if (ifuncint==-1) then 2475 write(*,"(a)") " Note: A number n means each grid in corresponding value range will be transformed & 2476 as n^3 grids around it during the integration to gain a higher integration accuracy. n=1 means the grids will remain unchanged. & 2477 The ""value range"" referred here is the value range of the function used to generate basins" 2478 write(*,*) "The value range and the times of refinement:" 2479 write(*,"(' Smaller than ',f10.5,' :',i4)") critlevel1,nrefine1 2480 write(*,"(' Between',f10.5,' and ',f10.5,' :',i4)") critlevel1,critlevel2,nrefine2 2481 write(*,"(' Between',f10.5,' and ',f10.5,' :',i4)") critlevel2,critlevel3,nrefine3 2482 write(*,"(' Larger than ',f10.5,' :',i4)") critlevel3,nrefine4 2483 write(*,*) 2484 write(*,*) "Please input three thresholds to define the four ranges, e.g. 0.1,0.5,1" 2485 write(*,*) "Note: Press ENTER button can retain current values unchanged" 2486 read(*,"(a)") c200tmp 2487 if (c200tmp/=' ') read(c200tmp,*) critlevel1,critlevel2,critlevel3 2488 write(*,*) "Input the times of refinement for the grids in the four ranges, e.g. 1,2,5,7" 2489 write(*,*) "Note: Press ENTER button can retain current values unchanged" 2490 read(*,"(a)") c200tmp 2491 if (c200tmp/=' ') read(c200tmp,*) nrefine1,nrefine2,nrefine3,nrefine4 2492 write(*,*) "Done!" 2493 write(*,*) 2494 end if 2495end do 2496 2497call walltime(walltime1) 2498CALL CPU_TIME(time_begin) 2499write(*,*) "Integrating, please wait..." 2500intval=0D0 2501basinvol=0D0 2502ifinish=0 2503nthreads=getNThreads() 2504!$OMP PARALLEL private(ix,iy,iz,ixref,iyref,izref,ndiv,irealatt,rnowx,rnowy,rnowz,rnowxtmp,rnowytmp,rnowztmp,orgxref,orgyref,orgzref,dxref,dyref,dzref,& 2505!$OMP tmpval,tmpvalrefine,intvalpriv,basinvolpriv,nrefine) shared(intval,basinvol,ifinish) NUM_THREADS(nthreads) 2506intvalpriv=0D0 2507basinvolpriv=0D0 2508!$OMP do schedule(DYNAMIC) 2509do iz=2,nz-1 2510 do iy=2,ny-1 2511 do ix=2,nx-1 2512 if (cubmat(ix,iy,iz)<critlevel1) then 2513 nrefine=nrefine1 !The number of point to represent each edge 2514 else if (cubmat(ix,iy,iz)<critlevel2) then 2515 nrefine=nrefine2 2516 else if (cubmat(ix,iy,iz)<critlevel3) then 2517 nrefine=nrefine3 2518 else 2519 nrefine=nrefine4 2520 end if 2521 ndiv=nrefine**3 2522 rnowx=orgx+(ix-1)*dx 2523 rnowy=orgy+(iy-1)*dy 2524 rnowz=orgz+(iz-1)*dz 2525 orgxref=rnowx-dx/2 !Take corner position as original point of microcycle 2526 orgyref=rnowy-dy/2 2527 orgzref=rnowz-dz/2 2528 dxref=dx/nrefine 2529 dyref=dy/nrefine 2530 dzref=dz/nrefine 2531 tmpval=0D0 2532 do ixref=1,nrefine 2533 do iyref=1,nrefine 2534 do izref=1,nrefine 2535 rnowxtmp=orgxref+(ixref-0.5D0)*dxref 2536 rnowytmp=orgyref+(iyref-0.5D0)*dyref 2537 rnowztmp=orgzref+(izref-0.5D0)*dzref 2538 if (ifuncint==-1) then 2539 tmpvalrefine=cubmattmp(ix,iy,iz) 2540 else if (ifuncint==0) then 2541 tmpvalrefine=cubmat(ix,iy,iz) 2542 else 2543 tmpvalrefine=calcfuncall(ifuncint,rnowxtmp,rnowytmp,rnowztmp) 2544 end if 2545 tmpval=tmpval+tmpvalrefine/ndiv 2546 end do 2547 end do 2548 end do 2549 irealatt=gridbas(ix,iy,iz) 2550 intvalpriv(irealatt)=intvalpriv(irealatt)+tmpval 2551 basinvolpriv(irealatt)=basinvolpriv(irealatt)+1 2552 end do 2553 end do 2554 ifinish=ifinish+1 2555 write(*,"(' Finished:',i5,'/',i5)") ifinish,nz 2556end do 2557!$OMP end do 2558!$OMP CRITICAL 2559 intval=intval+intvalpriv 2560 basinvol=basinvol+basinvolpriv 2561!$OMP end CRITICAL 2562!$OMP END PARALLEL 2563dvol=dx*dy*dz 2564intval=intval*dvol 2565basinvol=basinvol*dvol !Basin volume 2566write(*,*) " #Basin Integral Volume(a.u.^3)" 2567do irealatt=1,numrealatt 2568 write(*,"(i8,f22.10,f20.8)") irealatt,intval(irealatt),basinvol(irealatt) 2569end do 2570write(*,"(' Sum of above values:',f20.8)") sum(intval(1:numrealatt)) 2571if (any(gridbas(2:nx-1,2:ny-1,2:nz-1)==0)) write(*,"(' Integral of unassigned grids:',f20.8)") intval(0) 2572if (any(gridbas(2:nx-1,2:ny-1,2:nz-1)==-1)) write(*,"(' Integral of the grids travelled to box boundary:',f20.8)") intval(-1) 2573 2574CALL CPU_TIME(time_end) 2575call walltime(walltime2) 2576write(*,"(' Integrating basins took up CPU time',f12.2,'s, wall clock time',i10,'s')") time_end-time_begin,walltime2-walltime1 2577end subroutine 2578 2579 2580 2581!!------- Integrate AIM basins using mixed atomic-center and uniform grids 2582! itype=1: Integrate specific real space function 2583! itype=2: Integrate specific real space function with exact refinement of basin boundary 2584! itype=3: Integrate specific real space function with approximate refinement of basin boundary by exact+linear interpolation 2585! itype=10: Produce electric multipole moments 2586!NNA and ECP are supported 2587!Notice that the grid generated must cover the whole space! 2588subroutine integratebasinmix(itype) 2589use defvar 2590use util 2591use function 2592use basinintmod 2593use topo 2594implicit real*8(a-h,o-z) 2595!p suffix means private variable for parallel mode 2596real*8 intval(-1:numrealatt,20),intvalp(-1:numrealatt,20) !Up to 20 functions can be evaluated and stored simultaneously 2597real*8 basinvol(-1:numrealatt),basinvolp(-1:numrealatt),basinvdwvol(-1:numrealatt),basinvdwvolp(-1:numrealatt) !vdW is used to obtain the basin volume enclosed by 0.001 isosurface of rho 2598real*8 trustrad(numrealatt),intbasinthread(numrealatt),intbasin(numrealatt) 2599real*8 dens,grad(3),hess(3,3),k1(3),k2(3),k3(3),k4(3),xarr(nx),yarr(ny),zarr(nz) 2600real*8,allocatable :: potx(:),poty(:),potz(:),potw(:) 2601real*8,allocatable :: rhogrid(:,:,:),rhogradgrid(:,:,:,:) !Used in shubin's 2nd project 2602real*8,allocatable :: prorhogrid(:,:,:) !Used in integrating deformation density 2603type(content),allocatable :: gridatt(:) !Record correspondence between attractor and grid 2604integer att2atm(numrealatt) !The attractor corresponds to which atom. If =0, means this is a NNA 2605real*8 eleint(-1:numrealatt),xint(-1:numrealatt),yint(-1:numrealatt),zint(-1:numrealatt),& 2606xxint(-1:numrealatt),yyint(-1:numrealatt),zzint(-1:numrealatt),xyint(-1:numrealatt),yzint(-1:numrealatt),xzint(-1:numrealatt) 2607real*8 eleintp(-1:numrealatt),xintp(-1:numrealatt),yintp(-1:numrealatt),zintp(-1:numrealatt),& 2608xxintp(-1:numrealatt),yyintp(-1:numrealatt),zzintp(-1:numrealatt),xyintp(-1:numrealatt),yzintp(-1:numrealatt),xzintp(-1:numrealatt) 2609integer walltime1,walltime2,radpotAIM,sphpotAIM 2610real*8 prodensgrad(0:4),gridval(100000,6) !Used for storing various information during integrating inside sphere 2611character c80tmp*80,selectyn 2612nbeckeiter=8 2613if (ifuncbasin/=1) then 2614 write(*,"(a)") " Error: This function is only applicable to AIM basins! That means in option 1, you should select electron density to construct the basins." 2615 return 2616end if 2617 2618if (ispecial==0) then 2619 if (itype==1.or.itype==2.or.itype==3) then 2620 write(*,*) "Please select integrand:" 2621 write(*,*) "-2 Return" 2622 write(*,*) "-1 Deformation density" 2623 call selfunc_interface(ifuncint) 2624 if (ifuncint==-2) then 2625 return 2626 else if (ifuncint==-1) then 2627 call setpromol 2628 end if 2629 end if 2630else if (ispecial==1) then 2631 continue !Don't let user to select integrand 2632else if (ispecial==2) then 2633 call setpromol !Don't let user to select integrand 2634 expcutoff=1 !Use full accuracy for shubin's 2nd project 2635end if 2636 2637call walltime(walltime1) 2638CALL CPU_TIME(time_begin) 2639intval=0D0 2640basinvol=0D0 2641basinvdwvol=0D0 2642eleint=0D0 2643xint=0D0 2644yint=0D0 2645zint=0D0 2646xxint=0D0 2647yyint=0D0 2648zzint=0D0 2649xyint=0D0 2650yzint=0D0 2651xzint=0D0 2652numcp=0 2653 2654att2atm=0 2655!Determine trust radius and then integrate in the trust sphere 2656!We use CPpos to record the center of the trust sphere 2657write(*,*) "Integrating in trust sphere..." 2658do iatt=1,numrealatt !Cycle each attractors 2659 !Refine the crude position of attractor by exact newton method 2660 do iatm=1,ncenter 2661 disttest=dsqrt( (realattxyz(iatt,1)-a(iatm)%x)**2+(realattxyz(iatt,2)-a(iatm)%y)**2+(realattxyz(iatt,3)-a(iatm)%z)**2 ) 2662 if (disttest<0.3D0) then 2663 att2atm(iatt)=iatm 2664 write(*,"(' Attractor',i6,' corresponds to atom',i6,' (',a,')')") iatt,iatm,a(iatm)%name 2665 numcpold=numcp 2666 call findcp(a(iatm)%x,a(iatm)%y,a(iatm)%z,1,0) 2667 if (numcp==numcpold) then 2668 write(*,*) "Note: Unable to locate exact CP position! Use nuclear position" 2669 numcp=numcp+1 2670 CPpos(1,numcp)=a(iatm)%x 2671 CPpos(2,numcp)=a(iatm)%y 2672 CPpos(3,numcp)=a(iatm)%z 2673 end if 2674! write(*,"(' Coordinate after refinement:',3f16.8)") CPpos(:,numcp) 2675 exit 2676 end if 2677 end do 2678 if (att2atm(iatt)==0) then 2679 write(*,"(a,i6,a)") " Warning: Unable to determine the attractor",iatt," belongs to which atom!" 2680 write(*,"(a)") " If this is a non-nuclear attractor, simply press ENTER button to continue. If you used pseudopotential & 2681 and this attractor corresponds to the cluster of all maxima of its valence electron, then input the index of this atom (e.g. 9). & 2682 Else you should input q to return and regenerate basins with smaller grid spacing" 2683 read(*,"(a)") c80tmp 2684 if (c80tmp=='q') then 2685 return 2686 else if (c80tmp==" ") then 2687 numcpold=numcp 2688 call findcp(realattxyz(iatt,1),realattxyz(iatt,2),realattxyz(iatt,3),1,0) 2689 if (numcp==numcpold) then 2690 write(*,*) "Unable to locate exact CP position! Exit..." 2691 return 2692 end if 2693 else !ECP, input the corresponding atom by user 2694 read(c80tmp,*) iatmtmp 2695 att2atm(iatt)=iatmtmp 2696 numcp=numcp+1 2697 CPpos(1,numcp)=a(iatmtmp)%x 2698 CPpos(2,numcp)=a(iatmtmp)%y 2699 CPpos(3,numcp)=a(iatmtmp)%z 2700 end if 2701 end if 2702 2703 !Determine trust radius and set integration points and weight 2704 radpotAIM=200 2705 parm=1 2706 isettrustrad=0 2707 nintgrid=0 !Then number of integration grids within trust radius 2708 if (allocated(gridatt)) deallocate(gridatt) !Used to record grids in trust sphere of this attractor 2709 allocate(gridatt(radpotAIM*500)) 2710 do ish=1,radpotAIM !Cycle each radial shell. Radius distance is from near to far 2711 if (isettrustrad==1) exit !The trust radius has been finally determined in last shell cycle 2712 !Becke, namely the second-kind Gauss-Chebyshev 2713 itmp=radpotAIM+1-ish !Invert ish to make radr from near to far 2714 radx=cos(itmp*pi/(radpotAIM+1D0)) 2715 radr=(1+radx)/(1-radx)*parm 2716 radw=2*pi/(radpotAIM+1)*parm**3 *(1+radx)**2.5D0/(1-radx)**3.5D0 *4*pi 2717! !Handy, also known as Euler-Maclaurin. See: Murray, C. W.; Handy, N. C.; Laming, G. J. Mol Phys 1993, 78, 997 2718! radx=dfloat(ish)/(radpotAIM+1D0) 2719! radr=radx**2/(1-radx)**2*parm 2720! radw=2*radx**5/dfloat(radpotAIM+1)/(1-radx)**7*parm**3 *4*pi 2721 2722 !Set Lebedev grids according to shell radius 2723 !For more inner shell, the distribution is more akin to spherically symmetric, therefore lower number of grids could be used 2724 if (att2atm(iatt)==0) then !NNA 2725 sphpotAIM=302 2726 else 2727 radtmp=covr(a(att2atm(iatt))%index) 2728 if (radr<0.2D0*radtmp) then 2729 sphpotAIM=26 2730 else if (radr<0.5D0*radtmp) then 2731 sphpotAIM=74 2732 else if (radr<0.8D0*radtmp) then 2733 sphpotAIM=146 2734 else 2735 sphpotAIM=194 2736 end if 2737 end if 2738 if (allocated(potx)) deallocate(potx,poty,potz,potw) 2739 allocate(potx(sphpotAIM),poty(sphpotAIM),potz(sphpotAIM),potw(sphpotAIM)) 2740 call Lebedevgen(sphpotAIM,potx,poty,potz,potw) 2741 !Combine radial point and weights with angular part, and make them centered at current attractor 2742 gridatt( nintgrid+1:nintgrid+sphpotAIM )%x=radr*potx+CPpos(1,numcp) 2743 gridatt( nintgrid+1:nintgrid+sphpotAIM )%y=radr*poty+CPpos(2,numcp) 2744 gridatt( nintgrid+1:nintgrid+sphpotAIM )%z=radr*potz+CPpos(3,numcp) 2745 gridatt( nintgrid+1:nintgrid+sphpotAIM )%value=radw*potw 2746 !Find out trust radius for present attractor 2747 !If in a shell, the angle between "linking line between nucleus and a shell point" and "gradient vector of this point" & 2748 !is larger than 45 degree, then this shell is trust radius 2749 angmax=0 2750 if (att2atm(iatt)==0) then 2751 radinit=0 2752 else 2753 radrinit=0.15D0 2754 if (a(att2atm(iatt))%index>2) radrinit=0.5D0 2755 end if 2756 if (isettrustrad==0.and.radr>radrinit) then 2757 do isphpt=1,sphpotAIM 2758 xtmp=gridatt(nintgrid+isphpt)%x 2759 ytmp=gridatt(nintgrid+isphpt)%y 2760 ztmp=gridatt(nintgrid+isphpt)%z 2761 call calchessmat_dens(1,xtmp,ytmp,ztmp,dens,grad,hess) !Only value and gradient 2762 dirx=CPpos(1,numcp)-xtmp 2763 diry=CPpos(2,numcp)-ytmp 2764 dirz=CPpos(3,numcp)-ztmp 2765 angtmp=vecang(dirx,diry,dirz,grad(1),grad(2),grad(3)) 2766 if (angtmp>angmax) angmax=angtmp 2767 if (angtmp>45) then 2768 write(*,"(' The trust radius of attractor',i6,' is',f10.3,' Bohr',/)") iatt,trustrad(iatt) 2769 isettrustrad=1 !The radius of last shell should be the final trust radius. Now exit 2770 exit 2771 end if 2772 end do 2773 if (isettrustrad==0) trustrad(iatt)=radr !Passed this shell and temporarily set the radius as trust radius. Continue to enlarge the trust radius, until reached angmax>45 degree 2774 end if 2775 nintgrid=nintgrid+sphpotAIM 2776 end do 2777 if (isettrustrad==0) then !Trust radius was not set after run over all shells 2778 trustrad(iatt)=1000 !Infinite, for isolated atom 2779 if (ispecial==2) trustrad(iatt)=20 !For Shubin's 2nd project, should not be as large as 1000, because for a point very far from nucleus the relative entropy cannot be evaluated 2780 write(*,"(' The trust radius of attractor',i6,' is',f10.3,' Bohr',/)") iatt,trustrad(iatt) 2781 end if 2782 2783 !Use DFT integration algorithm to integrate the region inside trust radius 2784nthreads=getNThreads() 2785!$OMP PARALLEL private(ipt,ptx,pty,ptz,rx,ry,rz,dist,tmps,iter,switchwei,intvalp,& 2786!$OMP eleintp,xintp,yintp,zintp,xxintp,yyintp,zzintp,xyintp,yzintp,xzintp,tmpval,tmpval2,tmpval3,tmpval4) & 2787!$OMP shared(intval,gridval) NUM_THREADS(nthreads) 2788 intvalp=0D0 2789 eleintp=0D0 2790 xintp=0D0 2791 yintp=0D0 2792 zintp=0D0 2793 xxintp=0D0 2794 yyintp=0D0 2795 zzintp=0D0 2796 xyintp=0D0 2797 yzintp=0D0 2798 xzintp=0D0 2799!$OMP do schedule(DYNAMIC) 2800 do ipt=1,nintgrid 2801 ptx=gridatt(ipt)%x 2802 pty=gridatt(ipt)%y 2803 ptz=gridatt(ipt)%z 2804 rx=ptx-CPpos(1,numcp) !The relative distance between current point to corresponding attractor 2805 ry=pty-CPpos(2,numcp) 2806 rz=ptz-CPpos(3,numcp) 2807 !Calculate switching function 2808 dist=dsqrt(rx*rx+ry*ry+rz*rz) 2809 tmps=dist-trustrad(iatt) 2810 if (tmps>1) then 2811 switchwei=0 2812 else if (tmps<-1) then 2813 switchwei=1 2814 else 2815 do iter=1,nbeckeiter 2816 tmps=1.5D0*(tmps)-0.5D0*(tmps)**3 2817 end do 2818 switchwei=0.5D0*(1-tmps) 2819 end if 2820 gridval(ipt,3)=switchwei 2821! if (dist>trustrad(iatt)) cycle !Discrete separation between atomic-center and uniform integration 2822 if (switchwei<1D-7) cycle !For saving computational time 2823 2824 if (itype==1.or.itype==2.or.itype==3) then !Integrate a function 2825 if (ifuncint==-1) then !Deformation density, store molecular density of present center temporarily 2826 gridval(ipt,1)=fdens(ptx,pty,ptz) 2827 else if (ispecial==0) then !Normal case 2828 tmpval=calcfuncall(ifuncint,ptx,pty,ptz) 2829 intvalp(iatt,1)=intvalp(iatt,1)+gridatt(ipt)%value*tmpval*switchwei 2830 else if (ispecial==1) then !For Shubin's project, simultaneously output many properties 2831 tmpval=infoentro(2,ptx,pty,ptz) !Shannon entropy density 2832 tmpval2=Fisherinfo(1,ptx,pty,ptz) !Fisher information density 2833 tmpval3=weizsacker(ptx,pty,ptz) !Steric energy 2834 tmpval4=fdens(ptx,pty,ptz) !Electron density 2835 intvalp(iatt,1)=intvalp(iatt,1)+gridatt(ipt)%value*tmpval*switchwei 2836 intvalp(iatt,2)=intvalp(iatt,2)+gridatt(ipt)%value*tmpval2*switchwei 2837 intvalp(iatt,3)=intvalp(iatt,3)+gridatt(ipt)%value*tmpval3*switchwei 2838 intvalp(iatt,4)=intvalp(iatt,4)+gridatt(ipt)%value*tmpval4*switchwei 2839 else if (ispecial==2) then !For Shubin's 2nd project 2840 call calchessmat_dens(1,ptx,pty,ptz,gridval(ipt,1),gridval(ipt,4:6),hess) 2841 gridval(ipt,2)=sum(gridval(ipt,4:6)**2) 2842 end if 2843 else if (itype==10) then !Calculate multipole moment 2844 tmpval=gridatt(ipt)%value*fdens(ptx,pty,ptz)*switchwei 2845 eleintp(iatt)=eleintp(iatt)+tmpval 2846 xintp(iatt)=xintp(iatt)+rx*tmpval 2847 yintp(iatt)=yintp(iatt)+ry*tmpval 2848 zintp(iatt)=zintp(iatt)+rz*tmpval 2849 xxintp(iatt)=xxintp(iatt)+rx*rx*tmpval 2850 yyintp(iatt)=yyintp(iatt)+ry*ry*tmpval 2851 zzintp(iatt)=zzintp(iatt)+rz*rz*tmpval 2852 xyintp(iatt)=xyintp(iatt)+rx*ry*tmpval 2853 yzintp(iatt)=yzintp(iatt)+ry*rz*tmpval 2854 xzintp(iatt)=xzintp(iatt)+rx*rz*tmpval 2855 end if 2856 end do 2857!$OMP end do 2858!$OMP CRITICAL 2859 if (itype==1.or.itype==2.or.itype==3) then 2860 intval=intval+intvalp 2861 else if (itype==10) then 2862 eleint=eleint+eleintp 2863 xint=xint+xintp 2864 yint=yint+yintp 2865 zint=zint+zintp 2866 xxint=xxint+xxintp 2867 yyint=yyint+yyintp 2868 zzint=zzint+zzintp 2869 xyint=xyint+xyintp 2870 yzint=yzint+yzintp 2871 xzint=xzint+xzintp 2872 end if 2873!$OMP end CRITICAL 2874!$OMP END PARALLEL 2875 2876 !Some special cases: 2877 if (ispecial==2) then !Shubin's 2nd project, integrate relative Shannon and Fisher information 2878 !Current gridval content: 1=rho, 2=gradrho^2, 3=switchwei, 4=gradrho_x, 5=gradrho_y, 6=gradrho_z 2879 call dealloall 2880 write(*,"(' Loading ',a,/)") trim(custommapname(att2atm(iatt))) 2881 call readwfn(custommapname(att2atm(iatt)),1) 2882 do ipt=1,nintgrid 2883 switchwei=gridval(ipt,3) 2884 if (switchwei<1D-7) cycle 2885 ptx=gridatt(ipt)%x 2886 pty=gridatt(ipt)%y 2887 ptz=gridatt(ipt)%z 2888 call calchessmat_dens(1,ptx,pty,ptz,prodensgrad(0),prodensgrad(1:3),hess) 2889 prodens=prodensgrad(0) !rho0_A 2890 prodensgrad2=sum(prodensgrad(1:3)**2) 2891 !Relative Shannon entropy, integrate rho*log(rho/rho0_A)] 2892 tmpval=gridval(ipt,1)*log(gridval(ipt,1)/prodens) 2893 intval(iatt,1)=intval(iatt,1)+gridatt(ipt)%value*tmpval*switchwei 2894 !Relative Fisher information (old and incorrect implementation) 2895 tmpval=gridval(ipt,2)/gridval(ipt,1)-prodensgrad2/prodens 2896 intval(iatt,2)=intval(iatt,2)+gridatt(ipt)%value*tmpval*switchwei 2897 !Relative Fisher information (new and correct implementation) 2898 tmpvalx=gridval(ipt,4)/gridval(ipt,1) - prodensgrad(1)/prodens 2899 tmpvaly=gridval(ipt,5)/gridval(ipt,1) - prodensgrad(2)/prodens 2900 tmpvalz=gridval(ipt,6)/gridval(ipt,1) - prodensgrad(3)/prodens 2901 tmpval=gridval(ipt,1)*(tmpvalx**2+tmpvaly**2+tmpvalz**2) 2902 intval(iatt,3)=intval(iatt,3)+gridatt(ipt)%value*tmpval*switchwei 2903 end do 2904 call dealloall 2905 call readinfile(firstfilename,1) !Retrieve to first loaded file(whole molecule) to calc real rho again 2906 else if (ifuncint==-1) then !Integrate deformation density 2907 gridval(:,2)=0D0 2908 do iatm=1,ncenter_org !Cycle each atom to calculate deformation density at all integration grid 2909 call dealloall 2910 call readwfn(custommapname(iatm),1) 2911 do ipt=1,nintgrid 2912 switchwei=gridval(ipt,3) 2913 if (switchwei<1D-7) cycle 2914 gridval(ipt,2)=gridval(ipt,2)+fdens(gridatt(ipt)%x,gridatt(ipt)%y,gridatt(ipt)%z) 2915 end do 2916 end do 2917 do ipt=1,nintgrid !Now gridval(ipt,1/2/3) records actual, promolecular density and weight at ipt 2918 defdens=gridval(ipt,1)-gridval(ipt,2) 2919 intval(iatt,1)=intval(iatt,1)+gridatt(ipt)%value*defdens*gridval(ipt,3) 2920 end do 2921 call dealloall 2922 call readinfile(firstfilename,1) !Retrieve to first loaded file(whole molecule) to calc real rho again 2923 end if 2924end do !End cycle attractors 2925 2926if (itype==1.or.itype==2.or.itype==3) then 2927 write(*,*) "Integration result inside trust spheres" 2928 if (ispecial/=2) then 2929 write(*,*) " #Sphere Integral(a.u.)" 2930 do iatt=1,numrealatt 2931 write(*,"(i8,f22.10)") iatt,intval(iatt,1) 2932 end do 2933 write(*,"(' Sum of above values:',f20.8)") sum(intval(1:numrealatt,1)) 2934 else if (ispecial==2) then !Shubin's 2nd project 2935 write(*,*) " #Sphere Rel.Shannon Rel.Fisher(old) Rel.Fisher(new)" 2936 do iatt=1,numrealatt 2937 write(*,"(i8,3f20.10)") iatt,intval(iatt,1:3) 2938 end do 2939 write(*,"(' Sum of rel.Shannon:',f20.8)") sum(intval(1:numrealatt,1)) 2940 write(*,"(' Sum of rel.Fisher(old): ',f20.8)") sum(intval(1:numrealatt,2)) 2941 write(*,"(' Sum of rel.Fisher(new): ',f20.8)") sum(intval(1:numrealatt,3)) 2942 end if 2943end if 2944 2945!Set coordinate of uniform grids 2946dvol=dx*dy*dz 2947do ix=1,nx 2948 xarr(ix)=orgx+(ix-1)*dx 2949end do 2950do iy=1,ny 2951 yarr(iy)=orgy+(iy-1)*dy 2952end do 2953do iz=1,nz 2954 zarr(iz)=orgz+(iz-1)*dz 2955end do 2956 2957!--------- Integrating uniform grids, basin boundary grids will be calculated in the later stage 2958!NOTE: For shubin's 2nd project or deformation density, do not integrate here. At next stage boundary grids will be updated, and then at the next stage,& 2959!they will be integrated by a special module. Because at each grid if we reload atomic .wfn file will be too time consuming 2960if (ispecial==2.or.ifuncint==-1) goto 10 2961write(*,*) 2962write(*,*) "Integrating uniform grids..." 2963ifinish=0 2964nthreads=getNThreads() 2965!$OMP PARALLEL private(ix,iy,iz,iatt,icp,rnowx,rnowy,rnowz,rx,ry,rz,tmpval,tmpval2,tmpval3,intvalp,basinvolp,basinvdwvolp,dist,tmps,iter,switchwei,& 2966!$OMP eleintp,xintp,yintp,zintp,xxintp,yyintp,zzintp,xyintp,xzintp,yzintp) shared(intval,basinvol,basinvdwvol,ifinish) NUM_THREADS(nthreads) 2967intvalp=0D0 2968basinvolp=0D0 2969basinvdwvolp=0D0 2970eleintp=0D0 2971xintp=0D0 2972yintp=0D0 2973zintp=0D0 2974xxintp=0D0 2975yyintp=0D0 2976zzintp=0D0 2977xyintp=0D0 2978yzintp=0D0 2979xzintp=0D0 2980!$OMP do schedule(DYNAMIC) 2981do iz=2,nz-1 2982 rnowz=zarr(iz) 2983 do iy=2,ny-1 2984 rnowy=yarr(iy) 2985 do ix=2,nx-1 2986 rnowx=xarr(ix) 2987 if ((itype==2.or.itype==3).and.interbasgrid(ix,iy,iz) .eqv. .true.) cycle !If refine boundary grid at next stage, we don't calculate them at present stage 2988! if (iz==nint(nz/2D0)) write(1,"('C',3f14.8)") rnowx*b2a,rnowy*b2a,rnowz*b2a !Examine grid distribution 2989 iatt=gridbas(ix,iy,iz) 2990! do icp=1,numcp 2991! dist=dsqrt( (rnowx-CPpos(1,icp))**2+(rnowy-CPpos(2,icp))**2+(rnowz-CPpos(3,icp))**2 ) 2992! if (disttest<trustrad(icp)) cycle cycix !The function inside trust radius is integrated by DFT integration 2993! end do 2994 rx=rnowx-CPpos(1,iatt) !The relative distance between current point to corresponding attractor 2995 ry=rnowy-CPpos(2,iatt) 2996 rz=rnowz-CPpos(3,iatt) 2997 !Calculate switching function at current grid 2998 dist=dsqrt(rx*rx+ry*ry+rz*rz) 2999 tmps=dist-trustrad(iatt) 3000 if (tmps>1) then 3001 switchwei=0 3002 else if (tmps<-1) then 3003 switchwei=1 3004 else 3005 do iter=1,nbeckeiter 3006 tmps=1.5D0*(tmps)-0.5D0*(tmps)**3 3007 end do 3008 switchwei=0.5D0*(1-tmps) 3009 end if 3010 switchwei=1-switchwei 3011 basinvolp(iatt)=basinvolp(iatt)+1 !Calculate basin volume 3012 if (cubmat(ix,iy,iz)>0.001D0) basinvdwvolp(iatt)=basinvdwvolp(iatt)+1 3013 if (switchwei<1D-7) cycle !For saving time 3014 if (itype==1.or.itype==2.or.itype==3) then 3015 if (ispecial==0) then 3016 if (ifuncint==1) then !Electron density on each grid has already been calculated 3017 tmpval=cubmat(ix,iy,iz) 3018 else 3019 tmpval=calcfuncall(ifuncint,rnowx,rnowy,rnowz) 3020 end if 3021 intvalp(iatt,1)=intvalp(iatt,1)+tmpval*switchwei 3022 else if (ispecial==1) then !For RCY 3023 tmpval=infoentro(2,rnowx,rnowy,rnowz) !Shannon entropy density, see JCP,126,191107 for example 3024 tmpval2=Fisherinfo(1,rnowx,rnowy,rnowz) !Fisher information density, see JCP,126,191107 for example 3025 tmpval3=weizsacker(rnowx,rnowy,rnowz) !Steric energy 3026 tmpval4=fdens(rnowx,rnowy,rnowz) !Electron density 3027 intvalp(iatt,1)=intvalp(iatt,1)+tmpval*switchwei 3028 intvalp(iatt,2)=intvalp(iatt,2)+tmpval2*switchwei 3029 intvalp(iatt,3)=intvalp(iatt,3)+tmpval3*switchwei 3030 intvalp(iatt,4)=intvalp(iatt,4)+tmpval4*switchwei 3031 end if 3032 else if (itype==10) then 3033 tmpval=cubmat(ix,iy,iz)*switchwei 3034 eleintp(iatt)=eleintp(iatt)+tmpval 3035 xintp(iatt)=xintp(iatt)+rx*tmpval 3036 yintp(iatt)=yintp(iatt)+ry*tmpval 3037 zintp(iatt)=zintp(iatt)+rz*tmpval 3038 xxintp(iatt)=xxintp(iatt)+rx*rx*tmpval 3039 yyintp(iatt)=yyintp(iatt)+ry*ry*tmpval 3040 zzintp(iatt)=zzintp(iatt)+rz*rz*tmpval 3041 xyintp(iatt)=xyintp(iatt)+rx*ry*tmpval 3042 yzintp(iatt)=yzintp(iatt)+ry*rz*tmpval 3043 xzintp(iatt)=xzintp(iatt)+rx*rz*tmpval 3044 end if 3045 end do 3046 end do 3047 ifinish=ifinish+1 3048 if (ifuncint/=1) write(*,"(' Integrating uniform grids, finished:',i5,'/',i5)") ifinish,nz 3049end do 3050!$OMP end do 3051!$OMP CRITICAL 3052if (itype==1.or.itype==2.or.itype==3) then 3053 intval=intval+intvalp*dvol 3054 basinvol=basinvol+basinvolp*dvol 3055 basinvdwvol=basinvdwvol+basinvdwvolp*dvol 3056else if (itype==10) then 3057 eleint=eleint+eleintp*dvol 3058 xint=xint+xintp*dvol 3059 yint=yint+yintp*dvol 3060 zint=zint+zintp*dvol 3061 xxint=xxint+xxintp*dvol 3062 yyint=yyint+yyintp*dvol 3063 zzint=zzint+zzintp*dvol 3064 xyint=xyint+xyintp*dvol 3065 yzint=yzint+yzintp*dvol 3066 xzint=xzint+xzintp*dvol 3067end if 3068!$OMP end CRITICAL 3069!$OMP END PARALLEL 3070 307110 continue 3072!---- Exact refinement with/without multi-level splitting of boundary grids 3073if (itype==2.or.itype==3) then 3074 if (itype==3) then !Calculate grid data of gradient of electron density used to linear interpolation to obtain the value at any point 3075 write(*,*) 3076 call gengradmat 3077 end if 3078 write(*,*) 3079 nrk4lim=100 3080 nrk4gradswitch=40 3081 hsizeinit=0.25D0 3082 ifinish=0 3083nthreads=getNThreads() 3084!$OMP PARALLEL private(ix,iy,iz,iatt,rnowx,rnowy,rnowz,rx,ry,rz,tmpval,tmpval2,tmpval3,intvalp,basinvolp,basinvdwvolp,dist,tmps,iter,switchwei,& 3085!$OMP rnowxtmp,rnowytmp,rnowztmp,orgxref,orgyref,orgzref,dxref,dyref,dzref,ixref,iyref,izref,nrefine,ndiv,& 3086!$OMP k1,k2,k3,k4,dens,denshold,grad,hess,iattref,xtmp,ytmp,ztmp,irk4,hsize,ixtest,iytest,iztest,tmpdist) shared(intval,basinvol,basinvdwvol,ifinish) NUM_THREADS(nthreads) 3087 intvalp=0D0 3088 basinvolp=0D0 3089 basinvdwvolp=0D0 3090!$OMP do schedule(DYNAMIC) 3091 do iz=2,nz-1 3092 rnowz=zarr(iz) 3093 do iy=2,ny-1 3094 rnowy=yarr(iy) 3095 do ix=2,nx-1 3096 rnowx=xarr(ix) 3097 if (interbasgrid(ix,iy,iz) .eqv. .false.) cycle 3098! if (cubmat(ix,iy,iz)>0.001D0) then 3099! nrefine=2 !3 is the best 3100! else if (cubmat(ix,iy,iz)>0.001D0) then !0.0001 is the best 3101! nrefine=1 3102! else 3103! nrefine=1 3104! end if 3105 nrefine=1 3106 ndiv=nrefine**3 3107 orgxref=rnowx-dx/2 !Take corner position as original point of microcycle 3108 orgyref=rnowy-dy/2 3109 orgzref=rnowz-dz/2 3110 dxref=dx/nrefine 3111 dyref=dy/nrefine 3112 dzref=dz/nrefine 3113 do ixref=1,nrefine 3114 do iyref=1,nrefine 3115 do izref=1,nrefine 3116 rnowxtmp=orgxref+(ixref-0.5D0)*dxref !Coordinate of current refined grid 3117 rnowytmp=orgyref+(iyref-0.5D0)*dyref 3118 rnowztmp=orgzref+(izref-0.5D0)*dzref 3119 if (cubmat(ix,iy,iz)<=0.001D0) then !Only refine the boundary inside vdW surface 3120 iattref=gridbas(ix,iy,iz) 3121 else 3122 xtmp=rnowxtmp !This point will continuously move in the iteration 3123 ytmp=rnowytmp 3124 ztmp=rnowztmp 3125 hsize=hsizeinit 3126 densold=0D0 3127 !** Tracing steepest ascent trajectory using 4-order Runge-Kutta (RK4) 3128 cycrk4: do irk4=1,nrk4lim 3129 !For full accuracy refinement, or the first step, or when interpolation gradient works worse,& 3130 !namely has not converge until nrk4gradswitch, use exactly evaluated gradient 3131 if (itype==2.or.irk4==1.or.irk4==2.or.irk4>nrk4gradswitch) then 3132 if (itype==3.and.irk4==nrk4gradswitch+1) then !Interpolated gradient doesn't work well, switch to full accuracy, reset the coordinate 3133 xtmp=rnowxtmp 3134 ytmp=rnowytmp 3135 ztmp=rnowztmp 3136 hsize=hsizeinit 3137 end if 3138 call calchessmat_dens(1,xtmp,ytmp,ztmp,dens,grad,hess) !Only value and gradient 3139 if (dens<densold-1D-10) then 3140 hsize=hsize*0.75D0 !Reduce step size if density decrease 3141 else if (dens>densold+1D-10) then 3142 hsize=hsizeinit !Recover to initial step size 3143 end if 3144 denshold=dens 3145 k1=grad/dsqrt(sum(grad**2)) 3146 call calchessmat_dens(1,xtmp+hsize/2*k1(1),ytmp+hsize/2*k1(2),ztmp+hsize/2*k1(3),dens,grad,hess) !Only value and gradient 3147 k2=grad/dsqrt(sum(grad**2)) 3148 call calchessmat_dens(1,xtmp+hsize/2*k2(1),ytmp+hsize/2*k2(2),ztmp+hsize/2*k2(3),dens,grad,hess) !Only value and gradient 3149 k3=grad/dsqrt(sum(grad**2)) 3150 call calchessmat_dens(1,xtmp+hsize*k3(1),ytmp+hsize*k3(2),ztmp+hsize*k3(3),dens,grad,hess) !Only value and gradient 3151 k4=grad/dsqrt(sum(grad**2)) 3152 else !Using the gradients evaluated by trilinear interpolation from pre-calculated grid data to save computational time 3153 call linintp3dvec(xtmp,ytmp,ztmp,grad) !Only value and gradient 3154! if (dens<densold-1D-10) then 3155! hsize=hsize*0.75D0 3156! else if (dens>densold+1D-10) then 3157! hsize=hsizeinit !Recover to initial step size 3158! end if 3159! denshold=dens 3160 k1=grad/dsqrt(sum(grad**2)) 3161 call linintp3dvec(xtmp+hsize/2*k1(1),ytmp+hsize/2*k1(2),ztmp+hsize/2*k1(3),grad) 3162 k2=grad/dsqrt(sum(grad**2)) 3163 call linintp3dvec(xtmp+hsize/2*k2(1),ytmp+hsize/2*k2(2),ztmp+hsize/2*k2(3),grad) 3164 k3=grad/dsqrt(sum(grad**2)) 3165 call linintp3dvec(xtmp+hsize*k3(1),ytmp+hsize*k3(2),ztmp+hsize*k3(3),grad) 3166 k4=grad/dsqrt(sum(grad**2)) 3167 end if 3168 xtmp=xtmp+hsize/6*(k1(1)+2*k2(1)+2*k3(1)+k4(1)) !Update current coordinate 3169 ytmp=ytmp+hsize/6*(k1(2)+2*k2(2)+2*k3(2)+k4(2)) 3170 ztmp=ztmp+hsize/6*(k1(3)+2*k2(3)+2*k3(3)+k4(3)) 3171 !Check if current position has entered trust radius of an attractor 3172 do iatttmp=1,numrealatt 3173 dist=dsqrt( (xtmp-CPpos(1,iatttmp))**2+(ytmp-CPpos(2,iatttmp))**2+(ztmp-CPpos(3,iatttmp))**2 ) 3174 if (dist<trustrad(iatttmp)) then 3175 iattref=iatttmp 3176 exit cycrk4 3177 end if 3178 end do 3179 !Check if the closest grid and its 26 neighbours have the same attribution, if yes, employ its attribution then exit 3180 do ixtest=2,nx-1 3181 tmpdist=abs(xtmp-xarr(ixtest)) 3182 if (tmpdist<dx/2D0) exit 3183 end do 3184 do iytest=2,ny-1 3185 tmpdist=abs(ytmp-yarr(iytest)) 3186 if (tmpdist<dy/2D0) exit 3187 end do 3188 do iztest=2,nz-1 3189 tmpdist=abs(ztmp-zarr(iztest)) 3190 if (tmpdist<dz/2D0) exit 3191 end do 3192 iattref=gridbas(ixtest,iytest,iztest) 3193 do imove=1,26 3194 if ( gridbas(ixtest+vec26x(imove),iytest+vec26y(imove),iztest+vec26z(imove))/=iattref ) exit 3195 end do 3196 if (imove==27) exit !Successfully passed neighbour test 3197 end do cycrk4 3198 if (irk4==nrk4lim+1) then !Didn't enter trust radius or didn't approach a grid who and whose neighbour have the same attribution 3199 write(*,*) "Warning: Exceeded the step limit of steepest ascent process!" 3200 iattref=gridbas(ix,iy,iz) !Use its original attribution 3201 end if 3202! write(*,*) irk4 3203 end if 3204 gridbas(ix,iy,iz)=iattref !Update attribution of boundary grids 3205 !Calculate switching function at current grid 3206 rx=rnowxtmp-CPpos(1,iattref) !The relative distance between current point to corresponding attractor 3207 ry=rnowytmp-CPpos(2,iattref) 3208 rz=rnowztmp-CPpos(3,iattref) 3209 dist=dsqrt(rx*rx+ry*ry+rz*rz) 3210 tmps=dist-trustrad(iattref) 3211 if (tmps>1) then 3212 switchwei=0 3213 else if (tmps<-1) then 3214 switchwei=1 3215 else 3216 do iter=1,nbeckeiter 3217 tmps=1.5D0*(tmps)-0.5D0*(tmps)**3 3218 end do 3219 switchwei=0.5D0*(1-tmps) 3220 end if 3221 switchwei=1-switchwei 3222 basinvolp(iattref)=basinvolp(iattref)+1D0/ndiv !Calculate boundary basin volume 3223 if (cubmat(ix,iy,iz)>0.001D0) basinvdwvolp(iattref)=basinvdwvolp(iattref)+1D0/ndiv 3224 if (ispecial==2.or.ifuncint==-1) then 3225 continue !Don't calculate function value, but only update attribution of boundary grids at this stage 3226 else if (ispecial==0) then 3227 tmpval=calcfuncall(ifuncint,rnowxtmp,rnowytmp,rnowztmp) 3228 intvalp(iattref,1)=intvalp(iattref,1)+tmpval*switchwei/ndiv 3229 else if (ispecial==1) then 3230 tmpval=infoentro(2,rnowxtmp,rnowytmp,rnowztmp) !Shannon entropy density, see JCP,126,191107 for example 3231 tmpval2=Fisherinfo(1,rnowxtmp,rnowytmp,rnowztmp) !Fisher information density, see JCP,126,191107 for example 3232 tmpval3=weizsacker(rnowxtmp,rnowytmp,rnowztmp) !Steric energy 3233 tmpval4=fdens(rnowxtmp,rnowytmp,rnowztmp) !Electron density 3234 intvalp(iattref,1)=intvalp(iattref,1)+tmpval*switchwei/ndiv 3235 intvalp(iattref,2)=intvalp(iattref,2)+tmpval2*switchwei/ndiv 3236 intvalp(iattref,3)=intvalp(iattref,3)+tmpval3*switchwei/ndiv 3237 intvalp(iattref,4)=intvalp(iattref,4)+tmpval4*switchwei/ndiv 3238 end if 3239 end do !End refine grid 3240 end do 3241 end do 3242 3243 end do !End cycle ix grid 3244 end do 3245 ifinish=ifinish+1 3246 write(*,"(' Integrating grids at basin boundary, finished:',i5,'/',i5)") ifinish,nz 3247 end do 3248!$OMP end do 3249!$OMP CRITICAL 3250 intval=intval+intvalp*dvol 3251 basinvol=basinvol+basinvolp*dvol 3252 basinvdwvol=basinvdwvol+basinvdwvolp*dvol 3253!$OMP end CRITICAL 3254!$OMP END PARALLEL 3255 call detectinterbasgrd(6) 3256 write(*,*) "Basin boundary has been updated" 3257 numinterbas=count(interbasgrid .eqv. .true.) 3258 write(*,"(' The number of interbasin grids:',i12)") numinterbas 3259end if 3260 3261!Below are special modules for the cases when density of atom in free-state are involved 3262if (ispecial==2) then !Shubin's 2nd project, integrate relative Shannon and relative Fisher information 3263 allocate(rhogrid(nx,ny,nz),rhogradgrid(3,nx,ny,nz)) 3264 ifinish=0 3265 write(*,*) 3266 write(*,*) "Calculating electron density and its gradient for actual system at each grid" 3267nthreads=getNThreads() 3268!$OMP PARALLEL DO SHARED(rhogrid,rhogradgrid,ifinish) PRIVATE(ix,iy,iz,ptx,pty,ptz) schedule(dynamic) NUM_THREADS(nthreads) 3269 do iz=2,nz-1 3270 ptz=zarr(iz) 3271 do iy=2,ny-1 3272 pty=yarr(iy) 3273 do ix=2,nx-1 3274 ptx=xarr(ix) 3275 call calchessmat_dens(1,ptx,pty,ptz,rhogrid(ix,iy,iz),rhogradgrid(:,ix,iy,iz),hess) 3276 end do 3277 end do 3278!$OMP CRITICAL 3279 ifinish=ifinish+1 3280 write(*,"(' Finished',i6,' /',i6)") ifinish,nz-1 3281!$OMP end CRITICAL 3282 end do 3283!$OMP end PARALLEL DO 3284 write(*,*) 3285 write(*,*) "Calculating electron density and its gradient for free-state atom at each grid" 3286 do iatt=1,numrealatt !Cycle each attractors 3287 write(*,"(' Processing ',a)") trim(custommapname(att2atm(iatt))) 3288 call dealloall 3289 call readwfn(custommapname(att2atm(iatt)),1) 3290!$OMP PARALLEL private(intvalp,ix,iy,iz,ptx,pty,ptz,rx,ry,rz,dist,tmps,switchwei,& 3291!$OMP rho,rhograd2,prodens,prodensgrad,prodensgrad2,tmpval1,tmpval2,tmpval3,tmpx,tmpy,tmpz) shared(intval) NUM_THREADS(nthreads) 3292 intvalp=0D0 3293!$OMP do schedule(DYNAMIC) 3294 do iz=2,nz-1 3295 ptz=zarr(iz) 3296 do iy=2,ny-1 3297 pty=yarr(iy) 3298 do ix=2,nx-1 3299 ptx=xarr(ix) 3300 if (gridbas(ix,iy,iz)==iatt) then 3301 !Calculate switching function at current grid 3302 rx=ptx-CPpos(1,iatt) !The relative distance between current point to corresponding attractor 3303 ry=pty-CPpos(2,iatt) 3304 rz=ptz-CPpos(3,iatt) 3305 dist=dsqrt(rx*rx+ry*ry+rz*rz) 3306 tmps=dist-trustrad(iatt) 3307 if (tmps>1) then 3308 switchwei=0 3309 else if (tmps<-1) then 3310 switchwei=1 3311 else 3312 do iter=1,nbeckeiter 3313 tmps=1.5D0*(tmps)-0.5D0*(tmps)**3 3314 end do 3315 switchwei=0.5D0*(1-tmps) 3316 end if 3317 switchwei=1-switchwei 3318 rho=rhogrid(ix,iy,iz) 3319 rhograd2=sum(rhogradgrid(1:3,ix,iy,iz)**2) 3320 call calchessmat_dens(1,ptx,pty,ptz,prodens,prodensgrad(1:3),hess) 3321 prodensgrad2=sum(prodensgrad(1:3)**2) 3322 !Relative Shannon entropy 3323 tmpval1=rho*log(rho/prodens) 3324 intvalp(iatt,1)=intvalp(iatt,1)+tmpval1*switchwei 3325 !Relative Fisher information (old and incorrect form) 3326 tmpval2=rhograd2/rho-prodensgrad2/prodens 3327 intvalp(iatt,2)=intvalp(iatt,2)+tmpval2*switchwei 3328 !Relative Fisher information (new and correct form) 3329 tmpx=rhogradgrid(1,ix,iy,iz)/rho-prodensgrad(1)/prodens 3330 tmpy=rhogradgrid(2,ix,iy,iz)/rho-prodensgrad(2)/prodens 3331 tmpz=rhogradgrid(3,ix,iy,iz)/rho-prodensgrad(3)/prodens 3332 tmpval3=(tmpx**2+tmpy**2+tmpz**2)*rho 3333 intvalp(iatt,3)=intvalp(iatt,3)+tmpval3*switchwei 3334 end if 3335 end do 3336 end do 3337 end do 3338!$OMP end do 3339!$OMP CRITICAL 3340 intval=intval+intvalp*dvol 3341!$OMP end CRITICAL 3342!$OMP END PARALLEL 3343 end do 3344 deallocate(rhogrid,rhogradgrid) 3345 call dealloall 3346 write(*,"(' Reloading ',a)") trim(firstfilename) 3347 call readinfile(firstfilename,1) !Retrieve to first loaded file(whole molecule) 3348else if (ifuncint==-1) then !Deformation density 3349 allocate(prorhogrid(nx,ny,nz)) 3350 write(*,*) 3351 write(*,*) "Calculating promolecular density at each grid" 3352 prorhogrid=0D0 3353 do iatm=1,ncenter_org !Cycle each atom 3354 write(*,"(' Processing atom',i6,a,'...')") iatm,a_org(iatm)%name 3355 call dealloall 3356 call readwfn(custommapname(iatm),1) 3357nthreads=getNThreads() 3358!$OMP PARALLEL DO SHARED(prorhogrid) PRIVATE(ix,iy,iz) schedule(dynamic) NUM_THREADS(nthreads) 3359 do iz=2,nz-1 3360 do iy=2,ny-1 3361 do ix=2,nx-1 3362 prorhogrid(ix,iy,iz)=prorhogrid(ix,iy,iz)+fdens(xarr(ix),yarr(iy),zarr(iz)) 3363 end do 3364 end do 3365 end do 3366!$OMP end PARALLEL DO 3367 end do 3368 do iatt=1,numrealatt !Cycle each attractors 3369 do iz=2,nz-1 3370 do iy=2,ny-1 3371 do ix=2,nx-1 3372 if (gridbas(ix,iy,iz)==iatt) then 3373 !Calculate switching function at current grid 3374 rx=xarr(ix)-CPpos(1,iatt) !The relative distance between current point to corresponding attractor 3375 ry=yarr(iy)-CPpos(2,iatt) 3376 rz=zarr(iz)-CPpos(3,iatt) 3377 dist=dsqrt(rx*rx+ry*ry+rz*rz) 3378 tmps=dist-trustrad(iatt) 3379 if (tmps>1) then 3380 switchwei=0 3381 else if (tmps<-1) then 3382 switchwei=1 3383 else 3384 do iter=1,nbeckeiter 3385 tmps=1.5D0*(tmps)-0.5D0*(tmps)**3 3386 end do 3387 switchwei=0.5D0*(1-tmps) 3388 end if 3389 switchwei=1-switchwei 3390 defdens=cubmat(ix,iy,iz)-prorhogrid(ix,iy,iz) 3391 intval(iatt,1)=intval(iatt,1)+defdens*switchwei*dvol 3392 end if 3393 end do 3394 end do 3395 end do 3396 end do 3397 deallocate(prorhogrid) 3398 call dealloall 3399 call readinfile(firstfilename,1) !Retrieve to first loaded file(whole molecule) to calc real rho again 3400end if 3401 3402!!----------- Output SUMMARY 3403if (itype==1.or.itype==2.or.itype==3) then !Integrate specific real space function(s) 3404 write(*,*) 3405 if (ifuncint==1) then 3406 write(*,*) "Total result:" 3407 write(*,*) " #Basin Integral(a.u.) Vol(Bohr^3) Vol(rho>0.001)" 3408 do iatt=1,numrealatt 3409 write(*,"(i8,f22.10,2f16.3)") iatt,intval(iatt,1),basinvol(iatt),basinvdwvol(iatt) 3410 end do 3411 write(*,"(' Sum of above integrals:',f20.8)") sum(intval(1:numrealatt,1)) 3412 write(*,"(' Sum of basin volumes (rho>0.001):',f12.3,' Bohr^3')") sum(basinvdwvol(1:numrealatt)) 3413 if (any(gridbas(2:nx-1,2:ny-1,2:nz-1)==0)) write(*,"(' Integral of unassigned grids:',f20.8)") intval(0,1) 3414 if (any(gridbas(2:nx-1,2:ny-1,2:nz-1)==-1)) write(*,"(' Integral of the grids travelled to box boundary:',f20.8)") intval(-1,1) 3415 write(*,*) 3416 rnormfac=sum(intval(1:numrealatt,1))/(nelec+nEDFelec) !The electrons represented by EDF must be taken into account! 3417 write(*,"(' Normalization factor of the integral of electron density is',f12.6)") rnormfac 3418 write(*,*) "The atomic charges after normalization and atomic volumes:" 3419 do iatm=0,ncenter 3420 do iatt=1,numrealatt 3421 if (att2atm(iatt)==iatm.and.iatm==0) then 3422 write(*,"(i7,' (NNA) Charge:',f12.6,' Volume:',f10.3,' Bohr^3')") iatt,-intval(iatt,1)/rnormfac,basinvdwvol(iatt) 3423 else if (att2atm(iatt)==iatm.and.iatm/=0) then 3424 if (nEDFelec==0) then !Normal case, all electron basis or using pseudopotential but not accompanied by EDF 3425 write(*,"(i7,' (',a,') Charge:',f12.6,' Volume:',f10.3,' Bohr^3')") iatm,a(iatm)%name,a(iatm)%charge-intval(iatt,1)/rnormfac,basinvdwvol(iatt) 3426 else !EDF is used, so using a(iatm)%index instead of a(iatm)%charge 3427 write(*,"(i7,' (',a,') Charge:',f12.6,' Volume:',f10.3,' Bohr^3')") iatm,a(iatm)%name,a(iatm)%index-intval(iatt,1)/rnormfac,basinvdwvol(iatt) 3428 end if 3429 end if 3430 end do 3431 end do 3432 else 3433 if (ispecial==0) then 3434 write(*,*) "Total result:" 3435 write(*,*) " Atom Basin Integral(a.u.) Vol(Bohr^3) Vol(rho>0.001)" 3436 do iatm=0,ncenter 3437 do iatt=1,numrealatt 3438 if (att2atm(iatt)==iatm.and.iatm==0) then 3439 write(*,"(' NNA ',i8,f20.8,2f14.3)") iatt,intval(iatt,1),basinvol(iatt),basinvdwvol(iatt) 3440 else if (att2atm(iatt)==iatm.and.iatm/=0) then 3441 write(*,"(i7,' (',a,')',i8,f20.8,2f14.3)") iatm,a(iatm)%name,iatt,intval(iatt,1),basinvol(iatt),basinvdwvol(iatt) 3442 end if 3443 end do 3444 end do 3445 write(*,"(' Sum of above integrals:',f23.8)") sum(intval(1:numrealatt,1)) 3446 write(*,"(' Sum of basin volumes (rho>0.001):',f12.3,' Bohr^3')") sum(basinvdwvol(1:numrealatt)) 3447 if (any(gridbas(2:nx-1,2:ny-1,2:nz-1)==0)) write(*,"(' Integral of unassigned grids:',f20.8)") intval(0,1) 3448 if (any(gridbas(2:nx-1,2:ny-1,2:nz-1)==-1)) write(*,"(' Integral of the grids travelled to box boundary:',f20.8)") intval(-1,1) 3449 else if (ispecial==1) then 3450 write(*,*) "Total result:" 3451 write(*,*) " Atom Basin Shannon Fisher Steric ene Vol(rho>0.001)" 3452 do iatm=0,ncenter 3453 do iatt=1,numrealatt 3454 if (att2atm(iatt)==iatm.and.iatm==0) then 3455 write(*,"(' NNA ',i8,3f14.7,f13.5)") iatt,intval(iatt,1),intval(iatt,1:3),basinvdwvol(iatt) 3456 else if (att2atm(iatt)==iatm.and.iatm/=0) then 3457 write(*,"(i7,' (',a,')',i8,3f14.7,f13.5)") iatm,a(iatm)%name,iatt,intval(iatt,1:3),basinvdwvol(iatt) 3458 end if 3459 end do 3460 end do 3461 write(*,"(' Total Shannon entropy: ',f23.8)") sum(intval(1:numrealatt,1)) 3462 write(*,"(' Total Fisher information:',f23.8)") sum(intval(1:numrealatt,2)) 3463 write(*,"(' Total steric energy: ',f23.8)") sum(intval(1:numrealatt,3)) 3464 write(*,"(' Sum of basin volumes (rho>0.001):',f12.3,' Bohr^3')") sum(basinvdwvol(1:numrealatt)) 3465 rnormfac=sum(intval(1:numrealatt,4))/(nelec+nEDFelec) !The electrons represented by EDF must be taken into account! 3466 write(*,"(/,' Normalization factor of the integral of electron density is',f12.6)") rnormfac 3467 write(*,*) "The atomic charges after normalization and atomic volumes:" 3468 do iatm=0,ncenter 3469 do iatt=1,numrealatt 3470 if (att2atm(iatt)==iatm.and.iatm==0) then 3471 write(*,"(i7,' (NNA) Charge:',f12.6,' Volume:',f10.3,' Bohr^3')") iatt,-intval(iatt,1)/rnormfac,basinvdwvol(iatt) 3472 else if (att2atm(iatt)==iatm.and.iatm/=0) then 3473 if (nEDFelec==0) then !Normal case, all electron basis or using pseudopotential but not accompanied by EDF 3474 write(*,"(i7,' (',a,') Charge:',f12.6,' Volume:',f10.3,' Bohr^3')") iatm,a(iatm)%name,a(iatm)%charge-intval(iatt,4)/rnormfac,basinvdwvol(iatt) 3475 else !EDF is used, so using a(iatm)%index instead of a(iatm)%charge 3476 write(*,"(i7,' (',a,') Charge:',f12.6,' Volume:',f10.3,' Bohr^3')") iatm,a(iatm)%name,a(iatm)%index-intval(iatt,4)/rnormfac,basinvdwvol(iatt) 3477 end if 3478 end if 3479 end do 3480 end do 3481 else if (ispecial==2) then 3482 write(*,*) "Total result:" 3483 write(*,*) " Atom Basin Rel.Shannon Rel.Fisher(old) Rel.Fisher(new)" 3484 do iatm=1,ncenter 3485 do iatt=1,numrealatt 3486 if (att2atm(iatt)==iatm) write(*,"(i7,' (',a,')',i7,3f20.8)") iatm,a(iatm)%name,iatt,intval(iatt,1:3) 3487 end do 3488 end do 3489 write(*,"(' Sum of relat_Shannon: ',f23.8)") sum(intval(1:numrealatt,1)) 3490 write(*,"(' Sum of relat_Fisher(old): ',f23.8)") sum(intval(1:numrealatt,2)) 3491 write(*,"(' Sum of relat_Fisher(new): ',f23.8)") sum(intval(1:numrealatt,3)) 3492 end if 3493 end if 3494 write(*,*) 3495else if (itype==10) then !Electric multipole moment 3496 ioutid=6 3497101 eleinttot=0D0 3498 dipelextot=0D0 3499 dipeleytot=0D0 3500 dipeleztot=0D0 3501 if (ioutid==6) write(*,*) 3502 write(ioutid,*) "Note: All units shown below are in a.u.!" 3503 write(ioutid,*) 3504 do iatm=0,ncenter 3505 do iatt=1,numrealatt 3506 if (att2atm(iatt)/=iatm) cycle 3507 if (iatm==0) then 3508 write(ioutid,"(' Result of NNA',i6)") iatt 3509 else 3510 write(ioutid,"(' Result of atom',i6,' (',a,')')") iatm,a(iatm)%name 3511 end if 3512 write(ioutid,"(' Basin electric monopole moment:',f12.6)") -eleint(iatt) 3513 write(ioutid,"(' Basin electric dipole moment:')") 3514 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Magnitude=',f12.6)") -xint(iatt),-yint(iatt),-zint(iatt),sqrt(xint(iatt)**2+yint(iatt)**2+zint(iatt)**2) 3515 eleinttot=eleinttot+eleint(iatt) 3516 dipelex=-eleint(iatt)*CPpos(1,iatt)+(-xint(iatt)) !Contribution to molecular total dipole moment 3517 dipeley=-eleint(iatt)*CPpos(2,iatt)+(-yint(iatt)) 3518 dipelez=-eleint(iatt)*CPpos(3,iatt)+(-zint(iatt)) 3519 dipelextot=dipelextot+dipelex 3520 dipeleytot=dipeleytot+dipeley 3521 dipeleztot=dipeleztot+dipelez 3522 write(ioutid,"(' Basin electron contribution to molecular dipole moment:')") 3523 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Magnitude=',f12.6)") dipelex,dipeley,dipelez,sqrt(dipelex**2+dipeley**2+dipelez**2) 3524 write(ioutid,"(' Basin electric quadrupole moment (Cartesian form):')") 3525 rrint=xxint(iatt)+yyint(iatt)+zzint(iatt) 3526 QXX=-(3*xxint(iatt)-rrint)/2 3527 QYY=-(3*yyint(iatt)-rrint)/2 3528 QZZ=-(3*zzint(iatt)-rrint)/2 3529 write(ioutid,"(' QXX=',f12.6,' QXY=',f12.6,' QXZ=',f12.6)") QXX,-(3*xyint(iatt))/2,-(3*xzint(iatt))/2 3530 write(ioutid,"(' QYX=',f12.6,' QYY=',f12.6,' QYZ=',f12.6)") -(3*xyint(iatt))/2,QYY,-(3*yzint(iatt))/2 3531 write(ioutid,"(' QZX=',f12.6,' QZY=',f12.6,' QZZ=',f12.6)") -(3*xzint(iatt))/2,-(3*yzint(iatt))/2,QZZ 3532 write(ioutid,"( ' The magnitude of electric quadrupole moment (Cartesian form):',f12.6)") dsqrt(2D0/3D0*(QXX**2+QYY**2+QZZ**2)) 3533 R20=-(3*zzint(iatt)-rrint)/2D0 !Notice that the negative sign, because electrons carry negative charge 3534 R2n1=-dsqrt(3D0)*yzint(iatt) 3535 R2p1=-dsqrt(3D0)*xzint(iatt) 3536 R2n2=-dsqrt(3D0)*xyint(iatt) 3537 R2p2=-dsqrt(3D0)/2D0*(xxint(iatt)-yyint(iatt)) 3538 write(ioutid,"(' Electric quadrupole moments (Spherical harmonic form):')") 3539 write(ioutid,"(' Q_2,0 =',f11.6,' Q_2,-1=',f11.6,' Q_2,1=',f11.6)") R20,R2n1,R2p1 3540 write(ioutid,"(' Q_2,-2=',f11.6,' Q_2,2 =',f11.6)") R2n2,R2p2 3541 write(ioutid,"( ' Magnitude: |Q_2|=',f12.6)") dsqrt(R20**2+R2n1**2+R2p1**2+R2n2**2+R2p2**2) 3542 write(ioutid,*) 3543 end do 3544 end do 3545 !Output overall electric properties 3546 dipnucx=sum(a(:)%x*a(:)%charge) 3547 dipnucy=sum(a(:)%y*a(:)%charge) 3548 dipnucz=sum(a(:)%z*a(:)%charge) 3549 write(ioutid,"( ' Molecular net charge:',f12.6)") sum(a%charge)-eleinttot 3550 write(ioutid,"( ' Nuclear contribution to molecular dipole moment:')") 3551 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Norm=',f12.6)") dipnucx,dipnucy,dipnucz,sqrt(dipnucx**2+dipnucy**2+dipnucz**2) 3552 write(ioutid,"( ' Electron contribution to molecular dipole moment:')") 3553 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Norm=',f12.6)") dipelextot,dipeleytot,dipeleztot,sqrt(dipelextot**2+dipeleytot**2+dipeleztot**2) 3554 dipmolx=dipnucx+dipelextot 3555 dipmoly=dipnucy+dipeleytot 3556 dipmolz=dipnucz+dipeleztot 3557 write(ioutid,"( ' Molecular dipole moment:')") 3558 write(ioutid,"(' X=',f12.6,' Y=',f12.6,' Z=',f12.6,' Norm=',f12.6)") dipmolx,dipmoly,dipmolz,sqrt(dipmolx**2+dipmoly**2+dipmolz**2) 3559 if (ioutid==6) write(*,*) 3560 if (ioutid==10) then 3561 close(10) 3562 write(*,*) "Done!" 3563 return 3564 end if 3565end if 3566 3567CALL CPU_TIME(time_end) 3568call walltime(walltime2) 3569write(*,"(' Integrating basins took up CPU time',f12.2,'s, wall clock time',i10,'s')") time_end-time_begin,walltime2-walltime1 3570 3571if (itype==10) then 3572 write(*,*) 3573 write(*,*) "If also output the result to multipol.txt in current folder? y/n" 3574 read(*,*) selectyn 3575 if (selectyn=='y'.or.selectyn=='Y') then 3576 ioutid=10 3577 open(10,file="multipol.txt",status="replace") 3578 goto 101 3579 end if 3580end if 3581end subroutine 3582 3583 3584 3585 3586 3587!!------ Generate grid data of gradient of electron density, used to refine basin boundary 3588subroutine gengradmat 3589use defvar 3590use function 3591implicit real*8 (a-h,o-z) 3592real*8 wfnval(nmo),wfnderv(3,nmo),gradrho(3),EDFgrad(3),sumgrad2 3593if (allocated(cubmatvec)) then 3594 if (size(cubmatvec,2)==nx.and.size(cubmatvec,3)==ny.and.size(cubmatvec,4)==nz) return !Already generated 3595 deallocate(cubmatvec) 3596end if 3597allocate(cubmatvec(3,nx,ny,nz)) 3598ifinish=0 3599nthreads=getNThreads() 3600!$OMP PARALLEL DO SHARED(cubmatvec,ifinish) PRIVATE(ix,iy,iz,tmpx,tmpy,tmpz,wfnval,wfnderv,gradrho,imo) schedule(dynamic) NUM_THREADS(nthreads) 3601do iz=1,nz 3602 tmpz=orgz+(iz-1)*dz 3603 do iy=1,ny 3604 tmpy=orgy+(iy-1)*dy 3605 do ix=1,nx 3606 tmpx=orgx+(ix-1)*dx 3607 call orbderv(2,1,nmo,tmpx,tmpy,tmpz,wfnval,wfnderv) 3608 gradrho=0D0 3609 do imo=1,nmo 3610 gradrho(:)=gradrho(:)+MOocc(imo)*wfnval(imo)*wfnderv(:,imo) 3611 end do 3612 cubmatvec(:,ix,iy,iz)=2*gradrho(:) 3613 end do 3614 end do 3615 ifinish=ifinish+1 3616 write(*,"(' Generating grid data of gradient, finished:',i5,' /',i5)") ifinish,nz 3617end do 3618!$OMP END PARALLEL DO 3619end subroutine 3620