C===============
      SUBROUTINE EVEAN (EV, WHICH)
C     
C     Calls EVEAN2, which does the actual energy calculation
C     
C     by Jens Meiler / Michael Nilges Jan 1999
C===============
      IMPLICIT NONE
C     include files
      INCLUDE 'cns.inc'
      INCLUDE 'vectangl.inc'
      INCLUDE 'heap.inc'
C     i/o
      DOUBLE PRECISION EV
      CHARACTER*7 WHICH
C     begin
C     
C     
      CALL EVEAN2(EV, HEAP(VEANIPTR), HEAP(VEANJPTR), HEAP(VEANKPTR),
     &     HEAP(VEANLPTR),
     &     HEAP(VEANVOBSPTR), HEAP(VEANVERRPTR), HEAP(CALCVEANPTR),
     &     WHICH, HEAP(VEANCV))
      RETURN
      END
C===============
      SUBROUTINE EVEAN2 (EV, ATOMI, ATOMJ, ATOMK, ATOML,
     &     VOBS, VERR, VCALC, WHICH, VCV)
C     
C     Calculates vectorangle constant energies
C     
C     energies are of the form
C     E = kbord*deltaV**2 or E*kcent*(1+cos(deltaV / delta))
C     where
C     kbord = energy constant for angles at border,
C     kcent = energy constant for angles in center,
C     
C     by Jens Meiler / Michael Nilges Jan 1999
C     
C===============
      IMPLICIT NONE
C     include files
      INCLUDE 'cns.inc'
      INCLUDE 'coord.inc'
      INCLUDE 'numbers.inc'
      INCLUDE 'deriv.inc'
      INCLUDE 'vectangl.inc'
      INCLUDE 'consta.inc'
      INCLUDE 'comand.inc'
C     
      INCLUDE 'mtf.inc'
C     i/o
      INTEGER ATOMI(*), ATOMJ(*), ATOMK(*), ATOML(*), VCV(*)
      DOUBLE PRECISION VOBS(2,*), VERR(2,*), VCALC(2,*)
      DOUBLE PRECISION EV
      CHARACTER*7 WHICH
C     local variables
      INTEGER COUNT, CLASS
      DOUBLE PRECISION Kveancent, Kveanbord, Ealpha, Dalpha
      DOUBLE PRECISION XI, XJ, XK, XL, YI, YJ, YK, YL, ZI, ZJ, ZK, ZL
      DOUBLE PRECISION XIJ, XKL, YIJ, YKL, ZIJ, ZKL, LIJ, LKL
      DOUBLE PRECISION COSALPHA, SINALPHA, RECSIN, ALPHA, VOBSA
      DOUBLE PRECISION VOBSB, VOBSC, VOBSD, DV
      DOUBLE PRECISION DPRIJX, DPRIJY, DPRIJZ, DPRKLX, DPRKLY, DPRKLZ
C     begin
C     
C     following Axel's code in ETOR,
C     
C     zero out partial energy
C     
      EV = ZERO
C     
      CLASS = 1
      Kveancent = VEANFORCES(2,CLASS)
      Kveanbord = VEANFORCES(1,CLASS)
      DO COUNT = 1, NVEANS
         IF (VEANASSNDX(CLASS).LT.COUNT) THEN
            CLASS = CLASS + 1
            Kveancent = VEANFORCES(1,CLASS)
            Kveanbord = VEANFORCES(2,CLASS)
         END IF
C     
         XI = X(ATOMI(COUNT))
         XJ = X(ATOMJ(COUNT))
         XK = X(ATOMK(COUNT))
         XL = X(ATOML(COUNT))
C     
         YI = Y(ATOMI(COUNT))
         YJ = Y(ATOMJ(COUNT))
         YK = Y(ATOMK(COUNT))
         YL = Y(ATOML(COUNT))
C     
         ZI = Z(ATOMI(COUNT))
         ZJ = Z(ATOMJ(COUNT))
         ZK = Z(ATOMK(COUNT))
         ZL = Z(ATOML(COUNT))
C     
         VOBSA = (VOBS(1,COUNT) - VERR(1,COUNT)) * PI / 180
         VOBSB = (VOBS(1,COUNT) + VERR(1,COUNT)) * PI / 180
         VOBSC = (VOBS(2,COUNT) - VERR(2,COUNT)) * PI / 180
         VOBSD = (VOBS(2,COUNT) + VERR(2,COUNT)) * PI / 180
         if(VOBSC.lt.VOBSB) then
            VOBSC = PI/2
            VOBSB = PI/2
         end if
C     
C     now calculate diffs RIJ=RI-RJ, RJK=RJ-RK, RKL=RK-RL
C     
         XIJ = XI - XJ
         XKL = XK - XL
C     
         YIJ = YI - YJ
         YKL = YK - YL
C     
         ZIJ = ZI - ZJ
         ZKL = ZK - ZL
C     
C     calculate the norm of A, B, & C & set to MCONST if it's too small
C     
         LIJ = ONE/SQRT(MAX(MCONST, XIJ**2+YIJ**2+ZIJ**2))
         LKL = ONE/SQRT(MAX(MCONST, XKL**2+YKL**2+ZKL**2))
C     
C     normalize A, B, & C
C     
         XIJ = XIJ * LIJ
         YIJ = YIJ * LIJ
         ZIJ = ZIJ * LIJ
         XKL = XKL * LKL
         YKL = YKL * LKL
         ZKL = ZKL * LKL
C     
C     calculate cos(alpha)
C     
         COSALPHA = XIJ*XKL+YIJ*YKL+ZIJ*ZKL
         SINALPHA = SQRT(MAX(ZERO,ONE-COSALPHA**2))
C     
C     calculate alpha (make sure cos is within bounds and get sign from
C     sin) and keep it it radians
C     
         ALPHA = ACOS(MIN(ONE,MAX(-ONE,COSALPHA)))
C     
C     calculate energy and forces for normal entries
C     
         if (ALPHA.lt.VOBSA) then
            Ealpha = Kveanbord*((ALPHA-VOBSA)**2)
            Dalpha = Kveanbord* (ALPHA-VOBSA) *2
         elseif (ALPHA.lt.VOBSB) then
            Ealpha = 0
            Dalpha = 0
         elseif (ALPHA.lt.VOBSC.AND.VOBSB.lt.VOBSC) then
            Ealpha = 0.5*Kveancent*(1+COS(2*PI*(ALPHA-VOBSB)/
     $           (VOBSC-VOBSB)-PI))
            Dalpha = Kveancent*PI/(VOBSB-VOBSC)*
     $           SIN(2*PI*(ALPHA-VOBSB)/(VOBSC-VOBSB)-PI)
         elseif (ALPHA.lt.VOBSD) then
            Ealpha = 0
            Dalpha = 0
         else
            Ealpha = Kveanbord*((ALPHA-VOBSD)**2)
            Dalpha = Kveanbord* (ALPHA-VOBSD) *2
         end if
C     
C     If we're printing out the couplings, then we need to
C     try both possible assignments of observed with the
C     the angle and pick the one with the lower total violation.
C     
         IF (WHICH.EQ.'ANALYZE') VCALC(1,COUNT) = ALPHA
C     
C     accumulate energy (only for working set)
C     
         EV = Ealpha + EV
         DV = Dalpha
C     
C     compute reciprocal of SP multiplied by the derivative of the
C     function DF
         RECSIN = DV/MAX(MCONST,SINALPHA)
C     
C     compute:
C     d (alpha)
C     DF ---------,  etc.
C     d rij
         DPRIJX = RECSIN*LIJ*(COSALPHA*XIJ-XKL)
         DPRIJY = RECSIN*LIJ*(COSALPHA*YIJ-YKL)
         DPRIJZ = RECSIN*LIJ*(COSALPHA*ZIJ-ZKL)
         DPRKLX = RECSIN*LKL*(COSALPHA*XKL-XIJ)
         DPRKLY = RECSIN*LKL*(COSALPHA*YKL-YIJ)
         DPRKLZ = RECSIN*LKL*(COSALPHA*ZKL-ZIJ)
C     
C     now update forces if in energy & force mode
C     
         IF (WHICH.NE.'ANALYZE') THEN
            IF (VCV(COUNT).NE.VICV) THEN
               DX(ATOMI(COUNT))=DX(ATOMI(COUNT))+DPRIJX
               DY(ATOMI(COUNT))=DY(ATOMI(COUNT))+DPRIJY
               DZ(ATOMI(COUNT))=DZ(ATOMI(COUNT))+DPRIJZ
               DX(ATOMJ(COUNT))=DX(ATOMJ(COUNT))-DPRIJX
               DY(ATOMJ(COUNT))=DY(ATOMJ(COUNT))-DPRIJY
               DZ(ATOMJ(COUNT))=DZ(ATOMJ(COUNT))-DPRIJZ
               DX(ATOMK(COUNT))=DX(ATOMK(COUNT))+DPRKLX
               DY(ATOMK(COUNT))=DY(ATOMK(COUNT))+DPRKLY
               DZ(ATOMK(COUNT))=DZ(ATOMK(COUNT))+DPRKLZ
               DX(ATOML(COUNT))=DX(ATOML(COUNT))-DPRKLX
               DY(ATOML(COUNT))=DY(ATOML(COUNT))-DPRKLY
               DZ(ATOML(COUNT))=DZ(ATOML(COUNT))-DPRKLZ
            END IF
         END IF
      END DO
      RETURN
      END
C================
      SUBROUTINE READVEAN
C     
C     reads in vectorangle constant information
C     
C     by Jens Meiler / Michael Nilges Jan 1999
C================
      IMPLICIT NONE
C     include files
      INCLUDE 'cns.inc'
      INCLUDE 'comand.inc'
      INCLUDE 'vectangl.inc'
      INCLUDE 'funct.inc'
      INCLUDE 'mtf.inc'
      INCLUDE 'heap.inc'
      INCLUDE 'numbers.inc'
      INCLUDE 'ctitla.inc'
C     i/o
C     local variables
      INTEGER COUNT, SPTR, OLDCLASS, OLDMAXVEANS, TEMP
      DOUBLE PRECISION K1, K2, CUTOFF
      CHARACTER*4 THENAME
C     begin
C     
C     this is used by READVEAN2 to hold the selection
C     
      SPTR=ALLHP(INTEG4(NATOM))
C     
C     reset database only if no vectorangles have been entered
C     ie., this is the first time in the xplor script that
C     vectorangles has appeared
C     
      IF (VEANIPTR.EQ.0) THEN
         CALL VEANDEFAULTS
         CALL ALLOCVEANS(0, MAXVEANS)
      END IF
C     
C     now read input
C     
      CALL PUSEND('VEANORANGLES>')
      DO WHILE (.NOT.DONE)
         CALL NEXTWD('VEANORANGLES>')
         CALL MISCOM('VEANORANGLES>',USED)
         IF (.NOT.USED) THEN
C     
            IF (WD(1:4).EQ.'HELP') THEN
               CALL CNSHELP('cns-vectangle')
C     
C     Get class name.  Determine if it's an already-defined class.
C     Insert a new class if it's not.
C     
            ELSE IF (WD(1:4).EQ.'CLAS') THEN
               OLDCLASS = CURCLASS
               CALL NEXTA4('class name =', THENAME)
               MODE = NEW
               DO COUNT = 1, NCLASSES
                  IF (VEANCLASSNAMES(COUNT).EQ.THENAME) THEN
                     MODE = UPDATE
                     CURCLASS = COUNT
                  END IF
               END DO
               IF (MODE.EQ.NEW) THEN
C     
C     make sure you can't add more than the maximum
C     number of classes
C     
                  IF (OLDCLASS.EQ.MAXVEANCLASSES) THEN
                     CALL DSPERR('VEAN','Too many classes.')
                     CALL DSPERR('VEAN',
     &                    'Increase MAXVEANCLASSES and recompile.')
                     CALL WRNDIE(-5, 'READVEAN',
     &                    'Too many vectorangle classes.')
                  END IF
                  NCLASSES = NCLASSES + 1
                  CURCLASS = NCLASSES
                  VEANCLASSNAMES(CURCLASS) = THENAME
C     
C     If this isn't the first class, close off the old class
C     
                  IF (NCLASSES.GT.1) THEN
                     VEANASSNDX(OLDCLASS) = NVEANS
                  END IF
               END IF
C     
C     set force constant for current class
C     
            ELSE IF (WD(1:4).EQ.'FORC') THEN
               CALL NEXTF('border force constant =', K1)
               CALL NEXTF('center force constant =', K2)
C     
C     start a default class if there isn't one defined
C     
               IF (CURCLASS.EQ.0) THEN
                  NCLASSES = 1
                  CURCLASS = 1
               END IF
               WRITE(DUNIT, '(A, A, A, F8.3, F8.3)')
     &              'Setting force consts for class ',
     &              VEANCLASSNAMES(CURCLASS), ' to ', K1, K2
               VEANFORCES(1,CURCLASS) = K1
               VEANFORCES(2,CURCLASS) = K2
C     
C     reset vectorangle database
C     
            ELSE IF (WD(1:4).EQ.'RESE') THEN
               CALL VEANDEFAULTS
               CALL ALLOCVEANS(MAXVEANS, MAXVEANS)
C     
C     change number of assignment slots
C     
            ELSE IF (WD(1:4).EQ.'NRES') THEN
               OLDMAXVEANS = MAXVEANS
               CALL NEXTI('number of slots =', MAXVEANS)
               CALL ALLOCVEANS(OLDMAXVEANS, MAXVEANS)
C     
C     read in an assignment
C     
            ELSE IF (WD(1:4).EQ.'ASSI') THEN
C     
C     make sure you can't add more vectorangle assignments
C     than you have slots for
C     
               IF (NVEANS.EQ.MAXVEANS) THEN
                  CALL DSPERR('VEAN','Too many assignments.')
                  CALL DSPERR('VEAN',
     &                 'Increase NREStraints and run again.')
                  CALL WRNDIE(-1,'VEAN>',
     &                 'exceeded allocation for vectorangle restr.')
               END IF
C     
C     if there isn't a class specified,
C     start a default class
C     
               IF (CURCLASS.EQ.0) THEN
                  NCLASSES = 1
                  CURCLASS = 1
               END IF
               CALL READVEAN2(HEAP(VEANIPTR), HEAP(VEANJPTR),
     &              HEAP(VEANKPTR), HEAP(VEANLPTR),
     &              HEAP(VEANVOBSPTR), HEAP(VEANVERRPTR),
     &              HEAP(SPTR), HEAP(VEANCV))
C     
C     print violations
C     
            ELSE IF (WD(1:4).EQ.'PRIN') THEN
               CALL NEXTWD('PRINt>')
               IF (WD(1:4).NE.'THRE') THEN
                  CALL DSPERR('VEANORANGLES',
     &                 'print expects THREshold parameter.')
               ELSE
                  CALL NEXTF('THREshold =', CUTOFF)
                  IF (CUTOFF.LT.ZERO) THEN
                     CALL DSPERR('VEANORANGLES',
     &                    'cutoff must be positive.')
                     CUTOFF = ABS(CUTOFF)
                  END IF
                  CALL NEXTA4('ALL or CLASs>', THENAME)
                  IF (THENAME(1:3).EQ.'ALL') THEN
                     DO COUNT = 1,NCLASSES
                        PRINTCLASS(COUNT) = .TRUE.
                     END DO
                  ELSE IF (THENAME(1:4).EQ.'CLAS') THEN
                     CALL NEXTA4('class name =', THENAME)
                     DO COUNT = 1,NCLASSES
                        IF (VEANCLASSNAMES(COUNT).EQ.
     &                       THENAME) THEN
                           PRINTCLASS(COUNT) = .TRUE.
                        ELSE
                           PRINTCLASS(COUNT) = .FALSE.
                        END IF
                     END DO
                  ELSE
                     CALL DSPERR('VEANORANGLES',
     &                    'not understood.  Printing all classes')
                     DO COUNT = 1,NCLASSES
                        PRINTCLASS(COUNT) = .TRUE.
                     END DO
                  END IF
C     
                  CALL PRINTVEANS(CUTOFF, HEAP(CALCVEANPTR),
     &                 HEAP(VEANVOBSPTR), HEAP(VEANVERRPTR),
     &                 HEAP(VEANIPTR), HEAP(VEANJPTR),
     &                 HEAP(VEANKPTR), HEAP(VEANLPTR),
     &                 HEAP(VEANCV), 0)
                  IF (VICV.GT.0) THEN
                     CALL PRINTVEANS(CUTOFF, HEAP(CALCVEANPTR),
     &                    HEAP(VEANVOBSPTR), HEAP(VEANVERRPTR),
     &                    HEAP(VEANIPTR), HEAP(VEANJPTR),
     &                    HEAP(VEANKPTR), HEAP(VEANLPTR),
     &                    HEAP(VEANCV), 1)
                  END IF
               END IF
C====================================================================
            ELSE IF (WD(1:2).EQ.'CV') THEN
               CALL NEXTI('CV excluded partition number:',VICV)
C====================================================================
            ELSE IF (WD(1:4).EQ.'PART') THEN
               CALL NEXTI('number of PARTitions:',TEMP)
               TEMP=MAX(0,TEMP)
               CALL VCVS(NVEANS,HEAP(VEANCV),TEMP)
               IF (TEMP.EQ.0) THEN
                  VICV=0
               END IF
C     
C     check for END statement
C     
            ELSE
               CALL CHKEND('VEANANGL>', DONE)
            END IF
         END IF
      END DO
      DONE = .FALSE.
      CALL FREHP(SPTR,INTEG4(NATOM))
      RETURN
      END
C===============
      SUBROUTINE ALLOCVEANS (OLDSIZE, NEWSIZE)
C     
C     resets vectorangle constant arrays to hold SIZE entries
C     
C     by Jens Meiler / Michael Nilges Jan 1999
C===============
      IMPLICIT NONE
C     include files
      INCLUDE 'cns.inc'
      INCLUDE 'funct.inc'
      INCLUDE 'vectangl.inc'
C     i/o
      INTEGER OLDSIZE, NEWSIZE
C     begin
      IF (OLDSIZE.NE.0) THEN
         CALL FREHP(VEANIPTR, INTEG4(OLDSIZE))
         CALL FREHP(VEANJPTR, INTEG4(OLDSIZE))
         CALL FREHP(VEANKPTR, INTEG4(OLDSIZE))
         CALL FREHP(VEANLPTR, INTEG4(OLDSIZE))
         CALL FREHP(VEANCV  , INTEG4(OLDSIZE))
         CALL FREHP(VEANVOBSPTR, IREAL8(2*OLDSIZE))
         CALL FREHP(VEANVERRPTR, IREAL8(2*OLDSIZE))
         CALL FREHP(CALCVEANPTR, IREAL8(2*OLDSIZE))
      END IF
C     
      VEANIPTR = ALLHP(INTEG4(NEWSIZE))
      VEANJPTR = ALLHP(INTEG4(NEWSIZE))
      VEANKPTR = ALLHP(INTEG4(NEWSIZE))
      VEANLPTR = ALLHP(INTEG4(NEWSIZE))
      VEANCV   = ALLHP(INTEG4(NEWSIZE))
      VEANVOBSPTR = ALLHP(IREAL8(2*NEWSIZE))
      VEANVERRPTR = ALLHP(IREAL8(2*NEWSIZE))
      CALCVEANPTR = ALLHP(IREAL8(2*NEWSIZE))
      RETURN
      END
C==============
      SUBROUTINE VEANDEFAULTS
C
C sets up defaults
C
C by Jens Meiler / Michael Nilges Jan 1999
C==============
      IMPLICIT NONE
C include files
      INCLUDE 'vectangl.inc'
C local variables
      INTEGER COUNT
      DOUBLE PRECISION P
C begin
      MODE = NEW
      MAXVEANS = 1000
      NVEANS = 0
      NCLASSES = 0
      CURCLASS = 0
      VICV = 0
      DO COUNT = 1, MAXVEANCLASSES
           VEANCLASSNAMES(COUNT) = 'DEFAULT'
           VEANASSNDX(COUNT) = 0
           VEANFORCES (1,COUNT) = 200
           VEANFORCES (2,COUNT) =  50
      END DO
      RETURN
      END
C==============
      SUBROUTINE READVEAN2 (ATOMI, ATOMJ, ATOMK, ATOML,
     &     VOBS, VERR, SEL, VCV)
C     
C     reads actual vectorangle assignments into arrays
C     
C     by Jens Meiler / Michael Nilges Jan 1999
C==============
      IMPLICIT NONE
C     include files
      INCLUDE 'cns.inc'
      INCLUDE 'coord.inc'
      INCLUDE 'vectangl.inc'
      INCLUDE 'mtf.inc'
      INCLUDE 'comand.inc'
C     i/o
      INTEGER ATOMI(*), ATOMJ(*), ATOMK(*), ATOML(*),
     &     SEL(*), VCV(*)
      DOUBLE PRECISION VOBS(2,*), VERR(2,*)
C     local variables
      INTEGER NSEL, INSERTPOS, COUNT, CURSTOP, OTHERSTOP
      DOUBLE PRECISION JO, JE
C     begin
C     
C     if we're in update mode, make a space for the new line
C     
      IF (MODE.EQ.UPDATE) THEN
         DO COUNT = NVEANS+1, VEANASSNDX(CURCLASS)+1, -1
            ATOMI(COUNT) = ATOMI(COUNT-1)
            ATOMJ(COUNT) = ATOMJ(COUNT-1)
            ATOMK(COUNT) = ATOMK(COUNT-1)
            ATOML(COUNT) = ATOML(COUNT-1)
            VOBS(1,COUNT) = VOBS(1,COUNT-1)
            VOBS(2,COUNT) = VOBS(2,COUNT-1)
            VERR(1,COUNT) = VERR(1,COUNT-1)
            VERR(2,COUNT) = VERR(2,COUNT-1)
            VCV(COUNT) = VCV(COUNT-1)
         END DO
         CURSTOP = VEANASSNDX(CURCLASS)
         DO COUNT = 1, NCLASSES
            OTHERSTOP = VEANASSNDX(COUNT)
            IF (OTHERSTOP.GT.CURSTOP) THEN
               VEANASSNDX(COUNT) = OTHERSTOP + 1
            END IF
         END DO
         VEANASSNDX(CURCLASS) = CURSTOP + 1
         INSERTPOS = CURSTOP
         NVEANS = NVEANS + 1
      ELSE
         NVEANS = NVEANS + 1
         INSERTPOS = NVEANS
         VEANASSNDX(CURCLASS) = INSERTPOS
      END IF
C     
      CALL SELCTA(SEL, NSEL, X, Y, Z,.TRUE.)
      IF (NSEL.GT.1) THEN
         CALL DSPERR('VEAN',
     &        'more than 1 atom in selection for atom i. Using first')
      END IF
      CALL MAKIND(SEL, NATOM, NSEL)
      ATOMI(INSERTPOS) = SEL(1)
C     
      CALL SELCTA(SEL, NSEL, X, Y, Z,.TRUE.)
      IF (NSEL.GT.1) THEN
         CALL DSPERR('VEAN',
     &        'more than 1 atom in selection for atom j. Using first')
      END IF
      CALL MAKIND(SEL, NATOM, NSEL)
      ATOMJ(INSERTPOS) = SEL(1)
C     
      CALL SELCTA(SEL, NSEL, X, Y, Z,.TRUE.)
      IF (NSEL.GT.1) THEN
         CALL DSPERR('VEAN',
     &        'more than 1 atom in selection for atom k. Using first')
      END IF
      CALL MAKIND(SEL, NATOM, NSEL)
      ATOMK(INSERTPOS) = SEL(1)
C     
      CALL SELCTA(SEL, NSEL, X, Y, Z,.TRUE.)
      IF (NSEL.GT.1) THEN
         CALL DSPERR('VEAN',
     &        'more than 1 atom in selection for atom l. Using first')
      END IF
      CALL MAKIND(SEL, NATOM, NSEL)
      ATOML(INSERTPOS) = SEL(1)
C     
      CALL NEXTF('observed vectorangle =', JO)
      CALL NEXTF('error in vectorangle =', JE)
      VOBS(1,INSERTPOS) = JO
      VERR(1,INSERTPOS) = JE
      CALL NEXTF('observed vectorangle =', JO)
      CALL NEXTF('error in vectorangle =', JE)
      VOBS(2,INSERTPOS) = JO
      VERR(2,INSERTPOS) = JE
      VCV(INSERTPOS)  = -1
      RETURN
      END
C===============
      SUBROUTINE VEANINIT
C     
C     initializes vectorangles
C     
C     by Jens Meiler / Michael Nilges Jan 1999
C==============
      IMPLICIT NONE
C     include files
      INCLUDE 'vectangl.inc'
C     begin
      CALL VEANDEFAULTS
      CALL ALLOCVEANS(0, MAXVEANS)
      RETURN
      END
C===============
      SUBROUTINE VEANHP
C
C deallocates vectorangles
C
C by Jens Meiler / Michael Nilges Jan 1999
C================
      IMPLICIT NONE
C include files
      INCLUDE 'vectangl.inc'
C begin
      CALL ALLOCVEANS(MAXVEANS,0)
      RETURN
      END
C=================
      SUBROUTINE PRINTVEANS (CUTOFF, VCALC, VOBS, VERR,
     &     ATOMI, ATOMJ, ATOMK, ATOML,
     &     VCV, ITEST)
C     
C     prints vectorangles with delta alpha greater than cutoff
C     calculates RMS deviation and puts it into $RESULT
C     
C     by Jens Meiler / Michael Nilges Jan 1999
C=================
      IMPLICIT NONE
C     include files
      INCLUDE 'cns.inc'
      INCLUDE 'vectangl.inc'
      INCLUDE 'comand.inc'
      INCLUDE 'mtf.inc'
      INCLUDE 'numbers.inc'
      INCLUDE 'consta.inc'
C     i/o
      DOUBLE PRECISION CUTOFF, VCALC(2,*), VOBS(2,*), VERR(2,*)
      INTEGER ATOMI(*), ATOMJ(*), ATOMK(*), ATOML(*)
      INTEGER VCV(*), ITEST
C     local variables
      DOUBLE PRECISION RMS, VIOLS, VENERGY, DELTA, CALCV, VOBSA
      DOUBLE PRECISION VOBSB, VOBSC, VOBSD
      INTEGER NASSIGNSINCLUDED, COUNT, CLASS, I, J, K, L
      LOGICAL PRINTTHISCLASS
      DOUBLE COMPLEX DUMMY2
C     begin
      RMS = ZERO
      VIOLS = ZERO
      NASSIGNSINCLUDED = ZERO
C     
C     make sure that the calcJ array is up to date
C     
      CALL EVEAN(VENERGY, 'ANALYZE')
      IF (VICV.GT.0) THEN
         IF (ITEST.EQ.0) THEN
            WRITE(PUNIT,'(A)')
     &           ' $$$$$$$$$$$$$$$$$$ working set $$$$$$$$$$$$$$$$$$$$$'
         ELSE
            WRITE(PUNIT,'(A,I5,A)')
     &           ' $$$$$$$$$$$$$$$$$$$$ test set (TEST=',VICV,
     &           ')  $$$$$$$$$$$$$$$$$$$$$$'
         END IF
      END IF
      WRITE (PUNIT, '(A)') 'The following vectorangles have delta'
      WRITE (PUNIT, '(A)') 'greater than the cutoff:'
      WRITE (PUNIT, '(A,A)')
     $     '(calculated)(observed borderA borderB borderC borderD)',
     $     '(delta)'
C     
C     write out first class heading
C     
      CLASS = 1
      PRINTTHISCLASS = PRINTCLASS(CLASS)
      IF (PRINTTHISCLASS) THEN
         WRITE (PUNIT, '(A, A)') 'class ', VEANCLASSNAMES(1)
      END IF
C     
C     for every vectorangle entry,
C     
      DO COUNT = 1, NVEANS
C     
C     is this the start of a new class?
C     
         IF (VEANASSNDX(CLASS).LT.COUNT) THEN
            CLASS = CLASS + 1
            PRINTTHISCLASS = PRINTCLASS(CLASS)
            IF (PRINTTHISCLASS) THEN
               WRITE (PUNIT, '(A, A)') 'class ', VEANCLASSNAMES(CLASS)
            END IF
         END IF
C     
C     check if in test set or not (cross-validation)
C     
         IF ((ITEST.EQ.0.AND.VCV(COUNT).NE.VICV).OR.
     &        (ITEST.EQ.1.AND.VCV(COUNT).EQ.VICV)) THEN
C     IF (1.EQ.1) THEN
C     
C     if this ASSIgnment is in a class that will be printed,
C     
            IF (PRINTTHISCLASS) THEN
C     
C     update RMS delta J
C     
               CALCV = VCALC(1,COUNT) * 180 / PI
               VOBSA = (VOBS(1,COUNT) - VERR(1,COUNT))
               VOBSB = (VOBS(1,COUNT) + VERR(1,COUNT))
               VOBSC = (VOBS(2,COUNT) - VERR(2,COUNT))
               VOBSD = (VOBS(2,COUNT) + VERR(2,COUNT))
               if (CALCV.lt.VOBSA) then
                  DELTA = VOBSA - CALCV
               else if (CALCV.lt.VOBSB) then
                  DELTA = 0
               else if (CALCV.lt.VOBSC.AND.VOBSB.lt.VOBSC) then
                  DELTA = MIN(CALCV - VOBSB, VOBSC - CALCV)
               else if (CALCV.lt.VOBSD) then
                  DELTA = 0
               else
                  DELTA = CALCV - VOBSD
               end if
               RMS = RMS + DELTA**2
               NASSIGNSINCLUDED = NASSIGNSINCLUDED + 1
C     
C     print out delta Js greater than cutoff
C     and update number of violations
C     
               IF (ABS(DELTA).GT.CUTOFF) THEN
                  I = ATOMI(COUNT)
                  J = ATOMJ(COUNT)
                  K = ATOMK(COUNT)
                  L = ATOML(COUNT)
                  WRITE(PUNIT,'(9X,16(1X,A), 6(F8.3))')
     &                 SEGID(I),RESID(I),RES(I),TYPE(I),
     &                 SEGID(J),RESID(J),RES(J),TYPE(J),
     &                 SEGID(K),RESID(K),RES(K),TYPE(K),
     &                 SEGID(L),RESID(L),RES(L),TYPE(L),
     &                 CALCV, VOBSA, VOBSB, VOBSC, VOBSD, DELTA
                  VIOLS = VIOLS + ONE
               END IF
            END IF
         END IF
      END DO
C     
      IF (NASSIGNSINCLUDED.GT.ZERO) THEN
         RMS = SQRT(RMS / NASSIGNSINCLUDED)
      ELSE
         RMS = ZERO
      END IF
      WRITE(PUNIT,'(A,F8.3,A,F5.2,A,F6.0,A,I6,A)')
     &     '  RMS diff. =',RMS,
     &     ', #(violat.>',CUTOFF,')=',VIOLS,
     &     ' of ',NASSIGNSINCLUDED,' vectorangles'
      IF (ITEST.EQ.1) THEN
         CALL DECLAR('RESULT', 'DP', ' ', DUMMY2, RMS)
         CALL DECLAR('TEST_RMS', 'DP', ' ', DUMMY2, RMS)
         CALL DECLAR('TEST_VIOLATIONS', 'DP', ' ', DUMMY2, VIOLS)
      ELSE
         CALL DECLAR('RESULT', 'DP', ' ', DUMMY2, RMS)
         CALL DECLAR('RMS', 'DP', ' ', DUMMY2, RMS)
         CALL DECLAR('VIOLATIONS', 'DP', ' ', DUMMY2, VIOLS)
      END IF
      RETURN
      END
C     
C====================================================================
      SUBROUTINE VCVS(VNUM,VCV,PART)
C     
C     Routine partitions vectorangle data into PART sets.
C     VCV will contain integer numbers between 1 and PART.
C     
C     Author: Axel T. Brunger
C     Modifed for vectorangles: Jens Meiler / Michael Nilges Jan 1999
C     
C     
C     IMPLICIT NONE
C     I/O
      INTEGER VNUM, VCV(*)
      INTEGER PART
C     local
      INTEGER I, P, NP, NRETRY, NTRYTOT
      DOUBLE PRECISION RNUM
      NRETRY = 0
      NTRYTOT = 0
C     begin
 100  CONTINUE
      IF (PART.GT.0) THEN
         DO I=1,VNUM
            CALL GGUBFS(RNUM)
            VCV(I)=MAX(1,MIN(PART,INT(RNUM*PART)+1))
         END DO
C     
         IF (PART.EQ.VNUM) THEN
            DO I=1,VNUM
               VCV(I)=I
            END DO
         END IF
C     
         DO P=1,PART
            NP=0
            DO I=1,VNUM
               IF (VCV(I).EQ.P) THEN
                  NP=NP+1
               END IF
            END DO
            IF (NP .EQ. 0) THEN
               NRETRY = 1
            ENDIF
            WRITE(6,'(A,I3,A,I5,A)') ' For set ',P,
     &           ' there are ',NP,' vectorangle restraints.'
         END DO
      ELSE
         WRITE(6,'(A)')
     &        ' Data are not partitioned or partitioning removed.'
         DO I=1,VNUM
            VCV(I)=-1
         END DO
      END IF
      NTRYTOT = NTRYTOT + NRETRY
      IF (NTRYTOT .GT. 0 .AND. NTRYTOT .LE. 10) THEN
         WRITE(6,'(A)')
     &        ' Test set with 0 constraints! New trial...'
         GOTO 100
      ELSE IF (NTRYTOT .GT. 10) THEN
         CALL WRNDIE(-1,'VCVS',
     &        'Unable to partition the vectorangle data in ten trials')
      ENDIF
C     
      RETURN
      END
C

