c Create LAMMPS data file for collection of
c   polymer bead-spring chains of various lengths and bead sizes
c Syntax: chain < def.chain > data.file
c   def.chain is input file that specifies the chains
c   data.file is output file that will be input for LAMMPS
c includes image flags in data file so chains can be unraveled later

      program chain

      integer swaptype
      integer, allocatable :: nchain(:),nmonomer(:)
      integer, allocatable :: ntype(:),nbondtype(:)
      integer, allocatable :: type(:),molecule(:)
      integer, allocatable :: imagex(:),imagey(:),imagez(:)
      real*8, allocatable :: x(:),y(:),z(:)
      real*8, allocatable :: bondlength(:),restrict(:)
      common xprd,yprd,zprd,xboundlo,xboundhi,
     $     yboundlo,yboundhi,zboundlo,zboundhi
      real*8 random
 900  format(a)
 901  format(2f10.6,a)
 902  format(i3,f5.1)
 903  format(i10,i8,i8,3f10.4,3i4)
 904  format(i6,i3,2i6)

c read chain definitions

      read (5,*)
      read (5,*)
      read (5,*) rhostar
      read (5,*) iseed
      read (5,*) nsets
      read (5,*) swaptype

      allocate(nchain(nsets))
      allocate(nmonomer(nsets))
      allocate(ntype(nsets))
      allocate(nbondtype(nsets))
      allocate(bondlength(nsets))
      allocate(restrict(nsets))

      do iset = 1,nsets
        read (5,*)
        read (5,*) nchain(iset)
        read (5,*) nmonomer(iset)
        read (5,*) ntype(iset)
        read (5,*) nbondtype(iset)
        read (5,*) bondlength(iset)
        read (5,*) restrict(iset)
      enddo

c natoms = total # of monomers

      natoms = 0
      do iset = 1,nsets
        natoms = natoms + nchain(iset)*nmonomer(iset)
      enddo

      allocate(x(natoms))
      allocate(y(natoms))
      allocate(z(natoms))
      allocate(type(natoms))
      allocate(molecule(natoms))
      allocate(imagex(natoms))
      allocate(imagey(natoms))
      allocate(imagez(natoms))

c setup box size (sigma = 1.0)

      volume = natoms/rhostar
      xprd = volume**(1.0/3.0)
      yprd = xprd
      zprd = xprd

      xboundlo = -xprd/2.0
      xboundhi = -xboundlo
      yboundlo = xboundlo
      yboundhi = xboundhi
      zboundlo = xboundlo
      zboundhi = xboundhi

c generate random chains
c loop over sets and chains in each set
      
      n = 0
      nmolecule = 0

      do iset = 1,nsets
        do ichain = 1,nchain(iset)
          nmolecule = nmolecule + 1

c random starting point for the chain in the box

          x1 = 0.0
          y1 = 0.0
          z1 = 0.0
          x2 = xboundlo + random(iseed)*xprd
          y2 = yboundlo + random(iseed)*yprd
          z2 = zboundlo + random(iseed)*zprd

c store 1st monomer of chain
c 1st monomer is always in original box (image = 0)

          call pbc(x2,y2,z2)
          n = n + 1
          x(n) = x2
          y(n) = y2
          z(n) = z2
          type(n) = ntype(iset)
          imagex(n) = 0
          imagey(n) = 0
          imagez(n) = 0
          if (swaptype == 0) then
            molecule(n) = nmolecule
          else
            molecule(n) = 1
          endif

c generate rest of monomers in this chain

          do imonomer = 2,nmonomer(iset)

            x0 = x1
            y0 = y1
            z0 = z1
            x1 = x2
            y1 = y2
            z1 = z2

c random point inside sphere of unit radius

 10         xinner = 2.0*random(iseed) - 1.0
            yinner = 2.0*random(iseed) - 1.0
            zinner = 2.0*random(iseed) - 1.0
            rsq = xinner*xinner + yinner*yinner + zinner*zinner
            if (rsq > 1.0) goto 10

c project point to surface of sphere of unit radius

            r = sqrt(rsq)
            xsurf = xinner/r
            ysurf = yinner/r
            zsurf = zinner/r

c create new point by scaling unit offsets by bondlength (sigma = 1.0)

            x2 = x1 + xsurf*bondlength(iset)
            y2 = y1 + ysurf*bondlength(iset)
            z2 = z1 + zsurf*bondlength(iset)

c check that new point meets restriction requirement
c only for 3rd monomer and beyond

            dx = x2 - x0
            dy = y2 - y0
            dz = z2 - z0
            r = sqrt(dx*dx + dy*dy + dz*dz)

            if (imonomer > 2 .and. r <= restrict(iset)) goto 10

c store new point
c if delta to previous bead is large, then increment/decrement image flag

            call pbc(x2,y2,z2)
            n = n + 1
            x(n) = x2
            y(n) = y2
            z(n) = z2
            type(n) = ntype(iset)

            if (abs(x(n)-x(n-1)) < 2.0*bondlength(iset)) then
              imagex(n) = imagex(n-1)
            else if (x(n) - x(n-1) < 0.0) then
              imagex(n) = imagex(n-1) + 1
            else if (x(n) - x(n-1) > 0.0) then
              imagex(n) = imagex(n-1) - 1
            endif

            if (abs(y(n)-y(n-1)) < 2.0*bondlength(iset)) then
              imagey(n) = imagey(n-1)
            else if (y(n) - y(n-1) < 0.0) then
              imagey(n) = imagey(n-1) + 1
            else if (y(n) - y(n-1) > 0.0) then
              imagey(n) = imagey(n-1) - 1
            endif

            if (abs(z(n)-z(n-1)) < 2.0*bondlength(iset)) then
              imagez(n) = imagez(n-1)
            else if (z(n) - z(n-1) < 0.0) then
              imagez(n) = imagez(n-1) + 1
            else if (z(n) - z(n-1) > 0.0) then
              imagez(n) = imagez(n-1) - 1
            endif

            if (swaptype == 0) then
              molecule(n) = nmolecule
            else if (swaptype == 1) then
              molecule(n) = imonomer
            else if (swaptype == 2) then
              if (imonomer <= nmonomer(iset)/2) then
                molecule(n) = imonomer
              else
                molecule(n) = nmonomer(iset)+1-imonomer
              endif
            endif

          enddo

        enddo
      enddo

c compute quantities needed for LAMMPS file

      nbonds = 0
      ntypes = 0
      nbondtypes = 0
      do iset = 1,nsets
        nbonds = nbonds + nchain(iset)*(nmonomer(iset)-1)
        if (ntype(iset) > ntypes) ntypes = ntype(iset)
        if (nbondtype(iset) > nbondtypes)
     $       nbondtypes = nbondtype(iset)
      enddo

c write out LAMMPS file

      write (6,900) 'LAMMPS FENE chain data file'
      write (6,*)

      write (6,*) natoms,' atoms'
      write (6,*) nbonds,' bonds'
      write (6,*) 0,' angles'
      write (6,*) 0,' dihedrals'
      write (6,*) 0,' impropers'
      write (6,*)

      write (6,*) ntypes,' atom types'
      write (6,*) nbondtypes,' bond types'
      write (6,*) 0,' angle types'
      write (6,*) 0,' dihedral types'
      write (6,*) 0,' improper types'
      write (6,*)

      write (6,901) xboundlo,xboundhi,' xlo xhi'
      write (6,901) yboundlo,yboundhi,' ylo yhi'
      write (6,901) zboundlo,zboundhi,' zlo zhi'

      write (6,*)
      write (6,900) 'Masses'
      write (6,*)

      do i = 1,ntypes
        write (6,902) i,1.0
      enddo

      write (6,*)
      write (6,900) 'Atoms'
      write (6,*)

      do i = 1,natoms
        write (6,903) i,molecule(i),type(i),x(i),y(i),z(i),
     $       imagex(i),imagey(i),imagez(i)
      enddo

      if (nbonds > 0) then

        write (6,*)
        write (6,900) 'Bonds'
        write (6,*)

        n = 0
        m = 0
        do iset = 1,nsets
          do ichain = 1,nchain(iset)
            do imonomer = 1,nmonomer(iset)
              n = n + 1
              if (imonomer /= nmonomer(iset)) then
                m = m + 1
                write (6,904) m,nbondtype(iset),n,n+1
              endif
            enddo
          enddo
        enddo

      endif

      end

c ************
c Subroutines
c ************

c periodic boundary conditions - map atom back into periodic box

      subroutine pbc(x,y,z)
      common xprd,yprd,zprd,xboundlo,xboundhi,
     $     yboundlo,yboundhi,zboundlo,zboundhi

      if (x < xboundlo) x = x + xprd
      if (x >= xboundhi) x = x - xprd
      if (y < yboundlo) y = y + yprd
      if (y >= yboundhi) y = y - yprd
      if (z < zboundlo) z = z + zprd
      if (z >= zboundhi) z = z - zprd

      return
      end


c RNG from Numerical Recipes
      
      real*8 function random(iseed)
      real*8 aa,mm,sseed
      parameter (aa=16807.0D0,mm=2147483647.0D0)
      
      sseed = iseed
      sseed = mod(aa*sseed,mm)
      random = sseed/mm
      iseed = sseed

      return
      end
