module gdma ! Distributed Multipole Analysis for Gaussian Wavefunctions ! ! Copyright (C) 2005-08 Anthony J. Stone ! ! This program is free software; you can redistribute it and/or ! modify it under the terms of the GNU General Public License ! as published by the Free Software Foundation; either version 2 ! of the License, or (at your option) any later version. ! ! This program is distributed in the hope that it will be useful, ! but WITHOUT ANY WARRANTY; without even the implied warranty of ! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! GNU General Public License for more details. ! ! You should have received a copy of the GNU General Public License ! along with this program; if not, write to ! the Free Software Foundation, Inc., 51 Franklin Street, ! Fifth Floor, Boston, MA 02110-1301, USA. ! ! This version has been modified by Andy Simmonett (03/16) to link ! into Psi4, rather than serve as a standalone executable. USE input use iso_c_binding USE dma USE atom_grids, ONLY: debug_grid => debug USE timing, ONLY: start_timer, timer, time_and_date IMPLICIT NONE INTEGER, PARAMETER :: dp=kind(1d0) CHARACTER(LEN=100) :: file CHARACTER(LEN=80) :: buffer CHARACTER(LEN=20) :: key CHARACTER(LEN=8) :: whichd="SCF" CHARACTER(LEN=24) :: datestring ! Maximum number of sites is number of atoms + nextra INTEGER :: nextra=16 INTEGER :: ncoorb, maxl, cmax, nprim, nx, num, ich, mul INTEGER, ALLOCATABLE :: shell_type(:) INTEGER :: i, j, k, kp=0 LOGICAL :: eof, fchk, first, ok=.false. INTEGER open_status, infile REAL(dp), ALLOCATABLE :: densty(:,:), dtri(:) INTEGER :: ir=5 ! Input stream LOGICAL :: verbose=.false., debug(0:2)=.false. CONTAINS subroutine run_gdma(c_outfilename, c_datfilename) bind(c, name='run_gdma') !character(kind=c_char,len=1), intent(in) :: c_outfilename CHARACTER(kind=C_CHAR) :: c_outfilename(*), c_datfilename(*) character(len=:), allocatable :: outfilename, datfilename integer i, nchars integer outfile i = 1 do if (c_outfilename(i) == c_null_char) exit i = i + 1 end do nchars = i - 1 ! Exclude null character from Fortran string !allocate(character(len=nchars) :: outfilename) outfilename = (repeat(' ', nchars)) outfilename = transfer(c_outfilename(1:nchars), outfilename) i = 1 do if (c_datfilename(i) == c_null_char) exit i = i + 1 end do nchars = i - 1 ! Exclude null character from Fortran string !allocate(character(len=nchars) :: datfilename) datfilename = (repeat(' ', nchars)) datfilename = transfer(c_datfilename(1:nchars), datfilename) ! ! Added file IO (ACS 03/16) ! infile = 51 outfile = 52 open (unit=infile, file=datfilename, status='old', & iostat=open_status, action='read', position='rewind') if ( open_status /= 0 ) then write(outfile, *) 'Could not open GDMA input for reading.', & 'unit = ', infile stop endif open (unit=outfile, file=outfilename, status='old', & iostat=open_status, action='write', position='append') if ( open_status /= 0 ) then write(outfile, *) 'Could not open psi4 output for writing.', & 'unit = ', infile stop endif !!! write(outfile, "(15x,a/)") & " G D M A", & " by Anthony Stone", & " version 2.2.06, 22 June 2011", & "Distributed Multipoles from Gaussian wavefunctions" call time_and_date(datestring) write(outfile, "(/2A)") "Starting at ", datestring call start_timer punchfile="dma.punch" nat=0 fchk=.false. first=.true. do call read_line(eof, infile) if (eof) exit call readu(key) select case(key) case("","NOTE","!") cycle case("VERBOSE") verbose=.true. case("QUIET") debug=.false. verbose=.false. case("DEBUG") debug(0)=.true. do while (item0) then debug(k)=.true. else debug(-k)=.false. end if end do debug_grid=.true. verbose=.true. case("ANGSTROM") rfact=bohr case("BOHR") rfact=1d0 case("SI") Qfactor(0)=echarge do k=1,20 Qfactor(k)=Qfactor(k-1)*bohr end do case("AU") Qfactor=1d0 case("COMMENT","TITLE") call reada(buffer) write(outfile, "(/a/)") trim(buffer) case("DENSITY") if (fchk) call die & ("Specify density to use before reading data file",.true.) call readu(whichd) case("FILE","READ") nat=0 fchk=.false. ok=.false. first=.true. if (allocated(dtri)) deallocate(dtri) do while (item0) call die("Can't allocate atom arrays") maxcen=nat maxs=maxcen+nextra ! Arbitrary limit on number of sites if (allocated(name)) deallocate(name) allocate(name(maxs),stat=aok) if (aok>0) call die("Can't allocate site-name array") case("Charge") read(buffer,"(55X,I6)") ich if (verbose) write(outfile, "(a,i0)") "Charge ", ich case("Multiplicity") read(buffer,"(55X,I6)") mul if (verbose) write(outfile, "(a,i0)") "Multiplicity ", mul case("Number of basis functions") read(buffer,"(55X,I6)") ncoorb ! This number may be increased following conversion from ! spherical to cartesian if (verbose) write(outfile, "(i0,a)") ncoorb, " basis functions" case("Number of contracted shells") read(buffer,"(55X,I6)") nshell if (verbose) write(outfile, "(i0,a)") nshell, " shells" if (allocated(kstart)) & deallocate(kstart,katom,ktype,kng,kloc,kmin,kmax,shell_type) allocate (kstart(nshell), katom(nshell+1), ktype(nshell), & kng(nshell), kloc(nshell), kmin(nshell), kmax(nshell), & shell_type(nshell),stat=aok) if (aok>0) call die("Can't allocate shell arrays") shell_type=0 case("Highest angular momentum") read(buffer,"(55X,I6)") maxl if (verbose) write(outfile, "(a,i0)") "Highest angular momentum ", maxl if (maxl .gt. 4) call die & ("Sorry -- GDMA can only handle s, p, d, f and g basis functions",.false.) case("Largest degree of contraction") read(buffer,"(55X,I6)") cmax if (verbose) write(outfile, "(a,i0)") "Largest contraction depth ", cmax if (cmax .gt. 16) call die & ("Sorry -- maximum contraction depth is 16",.false.) case("Number of primitive shells") read(buffer,"(55X,I6)") nprim if (verbose) write(outfile, "(i0,a)") nprim, " primitive shells" if (allocated(ex)) deallocate(ex,cs,cp) allocate(ex(nprim), cs(nprim), cp(nprim), stat=aok) if (aok>0) then call die("Can't allocate arrays for primitives.") end if ex=0d0; cs=0d0; cp=0d0 case("Atomic numbers") call read_line(eof) do i=1,nat call geti(k) name(i)=element(k) end do if (verbose) write(outfile, "(a,20a3/(17x,20a3))") & "Atoms: ", name(1:nat) case("Nuclear charges") call read_line(eof) do i=1,nat call getf(zan(i)) end do if (verbose) write(outfile, "(a,20i3/(16x,20i3))") & "Nuclear charges:", nint(zan(1:nat)) case("Current cartesian coordinates") call read_line(eof) if (verbose) write(outfile, "(a)") "Atom Z Position (a.u.)" do i=1,nat do j=1,3 call getf(c(j,i),rfact) end do if (verbose) write(outfile, "(a3,i4,3f10.5)") name(i), nint(zan(i)), c(:,i) end do case("Shell types") call read_line(eof) ! num is the original number of basis functions. n is the ! increased number when conversion from spherical to ! cartesian is taken into account. num=0 n=0 do i=1,nshell call geti(shell_type(i)) select case(shell_type(i)) case(0) num=num+1; n=n+1 case(1) num=num+3; n=n+3 case(-1) num=num+4; n=n+4 case(2) num=num+6; n=n+6 case(-2) num=num+5; n=n+6 case(3) num=num+10; n=n+10 case(-3) num=num+7; n=n+10 case(4) num=num+15; n=n+15 case(-4) num=num+9; n=n+15 end select end do if (verbose) write(outfile, "(i0,a)") num, " basis functions" if ((verbose) .and. n>num) & write(outfile, "(a,i0,a)") "(", n, " after conversion to cartesian)" maxbfn=n if (allocated(iax)) deallocate(iax) allocate(iax(n+1), stat=aok) if (aok>0) then call die("Can't allocate IAX array") end if case("Number of primitives per shell") call read_line(eof) k=1 do i=1,nshell call geti(j) kstart(i)=k k=k+j kng(i)=j end do if (verbose) then write(outfile,"(a,20i3/(19x,20i3))") "Contraction depths:", kng(1:nshell) write(outfile,"(a,i0)") "Total number of primitives required: ", k-1 end if if (k .ne. nprim+1) call die & ("Shell contractions do not match number of primitives",.false.) case("Shell to atom map") call read_line(eof) do i=1,nshell call geti(katom(i)) end do if (verbose) then write(outfile,"(a,120i3)") "shell to atom", katom(1:nshell) endif case("Primitive exponents") call read_line(eof) do i=1,nprim call getf(ex(i)) end do ! print "(a,20F10.6)", "primitive exps", ex(1:nprim) case("Contraction coefficients") call read_line(eof) do i=1,nshell do j=kstart(i),kstart(i)+kng(i)-1 call getf(e) if (shell_type(i) .eq. 1) then cp(j)=e else cs(j)=e end if ! select case(shell_type(i)) ! case(-1,0) ! cs(j)=e ! case(1) ! cp(j)=e ! case(2,-2,3,-3,4,-4,5,-5) ! cd(j)=e ! end select end do end do case("P(S=P) Contraction coefficients") call read_line(eof) do i=1,nshell do j=kstart(i),kstart(i)+kng(i)-1 call getf(e) if (shell_type(i) .eq. -1) then cp(j)=e end if end do end do ! case("Alpha MO coefficients","Beta MO coefficients") ! if (debug(2)) then ! print "(/a)", text ! allocate(temp(ncoorb,ncoorb)) ! do i=1,ncoorb ! do j=1,ncoorb ! call getf(temp(j,i)) ! end do ! end do ! call matwrtt(temp,1,ncoorb,1,ncoorb,format="6f12.5") ! deallocate(temp) ! end if case default if (text .eq. density_header) then ncoorb=n nx=n*(n+1)/2 allocate(densty(n,n),temp(n,n)) call read_line(eof) do i=1,num do j=1,i call getf(densty(i,j)); densty(j,i)=densty(i,j) end do end do ok=.true. else ! Ignore this section if (nn .gt. 0) then if (type .eq. "I") then do i=1,(nn+5)/6 read(ir,"(A8)") dummy end do else if (type .eq. "R") then do i=1,(nn+4)/5 read(ir,"(A8)") dummy end do end if endif endif end select end do if (verbose) then atom=0 do i=1,nshell if (katom(i) .ne. atom) then atom=katom(i) write(outfile, "(a)") name(atom) end if write(outfile, "(a,i0,3x,a)") "Shell ", i, label(shell_type(i)) do j=kstart(i),kstart(i)+kng(i)-1 select case (shell_type(i)) case (-1) write(outfile, "(i10,f16.8, 2f14.8)") j, ex(j), cs(j), cp(j) case(0) write(outfile, "(i10,f16.8, 2f14.8)") j, ex(j), cs(j) case(1) write(outfile, "(i10,f16.8, 2f14.8)") j, ex(j), cp(j) case(2,-2,3,-3,4,-4) write(outfile, "(i10,f16.8, 2f14.8)") j, ex(j), cs(j) end select end do end do end if ! We use unnormalized primitive functions, so we transfer the ! normalising factor to the contraction coefficients. This is ! the factor for z^n exp(-e*r^2). General formula is ! (4e)^(n/2).(2e/pi)^{3/4}/sqrt{(2n-1)!!} do i=1,nshell do j=kstart(i),kstart(i)+kng(i)-1 e=ex(j) select case(abs(shell_type(i))) case(0,1) cs(j)=cs(j)*sqrt(sqrt((2d0*e/pi)**3)) cp(j)=cp(j)*sqrt(4d0*e*sqrt((2d0*e/pi)**3)) case(5) ! h shell cs(j)=cs(j)*(4d0*e)**2*sqrt(4d0*e*sqrt((2d0*e/pi)**3)/945d0) case(4) ! g shell cs(j)=cs(j)*(4d0*e)**2*sqrt(sqrt((2d0*e/pi)**3)/105d0) case(3) ! f shell cs(j)=cs(j)*4d0*e*sqrt(4d0*e*sqrt((2d0*e/pi)**3)/15d0) case(2) ! d shell cs(j)=cs(j)*4d0*e*sqrt(sqrt((2d0*e/pi)**3)/3d0) end select end do end do if (.not. ok) return ! call matwrtt(densty,1,num,1,num,format='5F10.5', cols=5) ! Deal with shell types, transforming from spherical to cartesian ! basis if necessary k=0 do i=1,nshell kloc(i)=k+1 ! First basis function for shell i select case(shell_type(i)) case(-1) ! sp shell kmin(i)=1 kmax(i)=4 ktype(i)=2 case(0) ! s shell kmin(i)=1 kmax(i)=1 ktype(i)=1 case(1) ! p shell kmin(i)=2 kmax(i)=4 ktype(i)=2 case(2,-2) ! d shell kmin(i)=5 kmax(i)=10 ktype(i)=3 if (shell_type(i) .lt. 0) then ! Spherical d shell temp(1:num,1:k)=densty(1:num,1:k) temp(1:num,k+1:k+6)=matmul(densty(1:num,k+1:k+5),td) temp(1:num,k+7:num+1)=densty(1:num,k+6:num) num=num+1 densty(1:k,1:num)=temp(1:k,1:num) densty(k+1:k+6,1:num)=matmul(transpose(td),temp(k+1:k+5,1:num)) densty(k+7:num,1:num)=temp(k+6:num-1,1:num) endif case(3,-3) ! f shell kmin(i)=11 kmax(i)=20 ktype(i)=4 if (shell_type(i) .lt. 0) then ! Spherical f shell temp(1:num,1:k)=densty(1:num,1:k) temp(1:num,k+1:k+10)=matmul(densty(1:num,k+1:k+7),tf) if (i