*> \brief \b ZHGEQZ
*
*  =========== DOCUMENTATION ===========
*
* Online html documentation available at
*            http://www.netlib.org/lapack/explore-html/
*
*> \htmlonly
*> Download ZHGEQZ + dependencies
*>
*> [TGZ]
*>
*> [ZIP]
*>
*> [TXT]
*> \endhtmlonly
*
*  Definition:
*  ===========
*
*       SUBROUTINE ZHGEQZ( JOB, COMPQ, COMPZ, N, ILO, IHI, H, LDH, T, LDT,
*                          ALPHA, BETA, Q, LDQ, Z, LDZ, WORK, LWORK,
*                          RWORK, INFO )
*
*       .. Scalar Arguments ..
*       CHARACTER          COMPQ, COMPZ, JOB
*       INTEGER            IHI, ILO, INFO, LDH, LDQ, LDT, LDZ, LWORK, N
*       ..
*       .. Array Arguments ..
*       DOUBLE PRECISION   RWORK( * )
*       COMPLEX*16         ALPHA( * ), BETA( * ), H( LDH, * ),
*      $                   Q( LDQ, * ), T( LDT, * ), WORK( * ),
*      $                   Z( LDZ, * )
*       ..
*
*
*> \par Purpose:
*  =============
*>
*> \verbatim
*>
*> ZHGEQZ computes the eigenvalues of a complex matrix pair (H,T),
*> where H is an upper Hessenberg matrix and T is upper triangular,
*> using the single-shift QZ method.
*> Matrix pairs of this type are produced by the reduction to
*> generalized upper Hessenberg form of a complex matrix pair (A,B):
*>
*>    A = Q1*H*Z1**H,  B = Q1*T*Z1**H,
*>
*> as computed by ZGGHRD.
*>
*> If JOB='S', then the Hessenberg-triangular pair (H,T) is
*> also reduced to generalized Schur form,
*>
*>    H = Q*S*Z**H,  T = Q*P*Z**H,
*>
*> where Q and Z are unitary matrices and S and P are upper triangular.
*>
*> Optionally, the unitary matrix Q from the generalized Schur
*> factorization may be postmultiplied into an input matrix Q1, and the
*> unitary matrix Z may be postmultiplied into an input matrix Z1.
*> If Q1 and Z1 are the unitary matrices from ZGGHRD that reduced
*> the matrix pair (A,B) to generalized Hessenberg form, then the output
*> matrices Q1*Q and Z1*Z are the unitary factors from the generalized
*> Schur factorization of (A,B):
*>
*>    A = (Q1*Q)*S*(Z1*Z)**H,  B = (Q1*Q)*P*(Z1*Z)**H.
*>
*> To avoid overflow, eigenvalues of the matrix pair (H,T)
*> (equivalently, of (A,B)) are computed as a pair of complex values
*> (alpha,beta).  If beta is nonzero, lambda = alpha / beta is an
*> eigenvalue of the generalized nonsymmetric eigenvalue problem (GNEP)
*>    A*x = lambda*B*x
*> and if alpha is nonzero, mu = beta / alpha is an eigenvalue of the
*> alternate form of the GNEP
*>    mu*A*y = B*y.
*> The values of alpha and beta for the i-th eigenvalue can be read
*> directly from the generalized Schur form:  alpha = S(i,i),
*> beta = P(i,i).
*>
*> Ref: C.B. Moler & G.W. Stewart, "An Algorithm for Generalized Matrix
*>      Eigenvalue Problems", SIAM J. Numer. Anal., 10(1973),
*>      pp. 241--256.
*> \endverbatim
*
*  Arguments:
*  ==========
*
*> \param[in] JOB
*> \verbatim
*>          JOB is CHARACTER*1
*>          = 'E': Compute eigenvalues only;
*>          = 'S': Computer eigenvalues and the Schur form.
*> \endverbatim
*>
*> \param[in] COMPQ
*> \verbatim
*>          COMPQ is CHARACTER*1
*>          = 'N': Left Schur vectors (Q) are not computed;
*>          = 'I': Q is initialized to the unit matrix and the matrix Q
*>                 of left Schur vectors of (H,T) is returned;
*>          = 'V': Q must contain a unitary matrix Q1 on entry and
*>                 the product Q1*Q is returned.
*> \endverbatim
*>
*> \param[in] COMPZ
*> \verbatim
*>          COMPZ is CHARACTER*1
*>          = 'N': Right Schur vectors (Z) are not computed;
*>          = 'I': Q is initialized to the unit matrix and the matrix Z
*>                 of right Schur vectors of (H,T) is returned;
*>          = 'V': Z must contain a unitary matrix Z1 on entry and
*>                 the product Z1*Z is returned.
*> \endverbatim
*>
*> \param[in] N
*> \verbatim
*>          N is INTEGER
*>          The order of the matrices H, T, Q, and Z.  N >= 0.
*> \endverbatim
*>
*> \param[in] ILO
*> \verbatim
*>          ILO is INTEGER
*> \endverbatim
*>
*> \param[in] IHI
*> \verbatim
*>          IHI is INTEGER
*>          ILO and IHI mark the rows and columns of H which are in
*>          Hessenberg form.  It is assumed that A is already upper
*>          triangular in rows and columns 1:ILO-1 and IHI+1:N.
*>          If N > 0, 1 <= ILO <= IHI <= N; if N = 0, ILO=1 and IHI=0.
*> \endverbatim
*>
*> \param[in,out] H
*> \verbatim
*>          H is COMPLEX*16 array, dimension (LDH, N)
*>          On entry, the N-by-N upper Hessenberg matrix H.
*>          On exit, if JOB = 'S', H contains the upper triangular
*>          matrix S from the generalized Schur factorization.
*>          If JOB = 'E', the diagonal of H matches that of S, but
*>          the rest of H is unspecified.
*> \endverbatim
*>
*> \param[in] LDH
*> \verbatim
*>          LDH is INTEGER
*>          The leading dimension of the array H.  LDH >= max( 1, N ).
*> \endverbatim
*>
*> \param[in,out] T
*> \verbatim
*>          T is COMPLEX*16 array, dimension (LDT, N)
*>          On entry, the N-by-N upper triangular matrix T.
*>          On exit, if JOB = 'S', T contains the upper triangular
*>          matrix P from the generalized Schur factorization.
*>          If JOB = 'E', the diagonal of T matches that of P, but
*>          the rest of T is unspecified.
*> \endverbatim
*>
*> \param[in] LDT
*> \verbatim
*>          LDT is INTEGER
*>          The leading dimension of the array T.  LDT >= max( 1, N ).
*> \endverbatim
*>
*> \param[out] ALPHA
*> \verbatim
*>          ALPHA is COMPLEX*16 array, dimension (N)
*>          The complex scalars alpha that define the eigenvalues of
*>          GNEP.  ALPHA(i) = S(i,i) in the generalized Schur
*>          factorization.
*> \endverbatim
*>
*> \param[out] BETA
*> \verbatim
*>          BETA is COMPLEX*16 array, dimension (N)
*>          The real non-negative scalars beta that define the
*>          eigenvalues of GNEP.  BETA(i) = P(i,i) in the generalized
*>          Schur factorization.
*>
*>          Together, the quantities alpha = ALPHA(j) and beta = BETA(j)
*>          represent the j-th eigenvalue of the matrix pair (A,B), in
*>          one of the forms lambda = alpha/beta or mu = beta/alpha.
*>          Since either lambda or mu may overflow, they should not,
*>          in general, be computed.
*> \endverbatim
*>
*> \param[in,out] Q
*> \verbatim
*>          Q is COMPLEX*16 array, dimension (LDQ, N)
*>          On entry, if COMPQ = 'V', the unitary matrix Q1 used in the
*>          reduction of (A,B) to generalized Hessenberg form.
*>          On exit, if COMPQ = 'I', the unitary matrix of left Schur
*>          vectors of (H,T), and if COMPQ = 'V', the unitary matrix of
*>          left Schur vectors of (A,B).
*>          Not referenced if COMPQ = 'N'.
*> \endverbatim
*>
*> \param[in] LDQ
*> \verbatim
*>          LDQ is INTEGER
*>          The leading dimension of the array Q.  LDQ >= 1.
*>          If COMPQ='V' or 'I', then LDQ >= N.
*> \endverbatim
*>
*> \param[in,out] Z
*> \verbatim
*>          Z is COMPLEX*16 array, dimension (LDZ, N)
*>          On entry, if COMPZ = 'V', the unitary matrix Z1 used in the
*>          reduction of (A,B) to generalized Hessenberg form.
*>          On exit, if COMPZ = 'I', the unitary matrix of right Schur
*>          vectors of (H,T), and if COMPZ = 'V', the unitary matrix of
*>          right Schur vectors of (A,B).
*>          Not referenced if COMPZ = 'N'.
*> \endverbatim
*>
*> \param[in] LDZ
*> \verbatim
*>          LDZ is INTEGER
*>          The leading dimension of the array Z.  LDZ >= 1.
*>          If COMPZ='V' or 'I', then LDZ >= N.
*> \endverbatim
*>
*> \param[out] WORK
*> \verbatim
*>          WORK is COMPLEX*16 array, dimension (MAX(1,LWORK))
*>          On exit, if INFO >= 0, WORK(1) returns the optimal LWORK.
*> \endverbatim
*>
*> \param[in] LWORK
*> \verbatim
*>          LWORK is INTEGER
*>          The dimension of the array WORK.  LWORK >= max(1,N).
*>
*>          If LWORK = -1, then a workspace query is assumed; the routine
*>          only calculates the optimal size of the WORK array, returns
*>          this value as the first entry of the WORK array, and no error
*>          message related to LWORK is issued by XERBLA.
*> \endverbatim
*>
*> \param[out] RWORK
*> \verbatim
*>          RWORK is DOUBLE PRECISION array, dimension (N)
*> \endverbatim
*>
*> \param[out] INFO
*> \verbatim
*>          INFO is INTEGER
*>          = 0: successful exit
*>          < 0: if INFO = -i, the i-th argument had an illegal value
*>          = 1,...,N: the QZ iteration did not converge.  (H,T) is not
*>                     in Schur form, but ALPHA(i) and BETA(i),
*>                     i=INFO+1,...,N should be correct.
*>          = N+1,...,2*N: the shift calculation failed.  (H,T) is not
*>                     in Schur form, but ALPHA(i) and BETA(i),
*>                     i=INFO-N+1,...,N should be correct.
*> \endverbatim
*
*  Authors:
*  ========
*
*> \author Univ. of Tennessee
*> \author Univ. of California Berkeley
*> \author Univ. of Colorado Denver
*> \author NAG Ltd.
*
*> \date April 2012
*
*> \ingroup complex16GEcomputational
*
*> \par Further Details:
*  =====================
*>
*> \verbatim
*>
*>  We assume that complex ABS works as long as its value is less than
*>  overflow.
*> \endverbatim
*>
*  =====================================================================
      SUBROUTINE ZHGEQZ( JOB, COMPQ, COMPZ, N, ILO, IHI, H, LDH, T, LDT,
     $                   ALPHA, BETA, Q, LDQ, Z, LDZ, WORK, LWORK,
     $                   RWORK, INFO )
*
*  -- LAPACK computational routine (version 3.7.0) --
*  -- LAPACK is a software package provided by Univ. of Tennessee,    --
*  -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..--
*     April 2012
*
*     .. Scalar Arguments ..
      CHARACTER          COMPQ, COMPZ, JOB
      INTEGER            IHI, ILO, INFO, LDH, LDQ, LDT, LDZ, LWORK, N
*     ..
*     .. Array Arguments ..
      DOUBLE PRECISION   RWORK( * )
      COMPLEX*16         ALPHA( * ), BETA( * ), H( LDH, * ),
     $                   Q( LDQ, * ), T( LDT, * ), WORK( * ),
     $                   Z( LDZ, * )
*     ..
*
*  =====================================================================
*
*     .. Parameters ..
      COMPLEX*16         CZERO, CONE
      PARAMETER          ( CZERO = ( 0.0D+0, 0.0D+0 ),
     $                   CONE = ( 1.0D+0, 0.0D+0 ) )
      DOUBLE PRECISION   ZERO, ONE
      PARAMETER          ( ZERO = 0.0D+0, ONE = 1.0D+0 )
      DOUBLE PRECISION   HALF
      PARAMETER          ( HALF = 0.5D+0 )
*     ..
*     .. Local Scalars ..
      LOGICAL            ILAZR2, ILAZRO, ILQ, ILSCHR, ILZ, LQUERY
      INTEGER            ICOMPQ, ICOMPZ, IFIRST, IFRSTM, IITER, ILAST,
     $                   ILASTM, IN, ISCHUR, ISTART, J, JC, JCH, JITER,
     $                   JR, MAXIT
      DOUBLE PRECISION   ABSB, ANORM, ASCALE, ATOL, BNORM, BSCALE, BTOL,
     $                   C, SAFMIN, TEMP, TEMP2, TEMPR, ULP
      COMPLEX*16         ABI22, AD11, AD12, AD21, AD22, CTEMP, CTEMP2,
     $                   CTEMP3, ESHIFT, RTDISC, S, SHIFT, SIGNBC, T1,
     $                   U12, X, ABI12, Y
*     ..
*     .. External Functions ..
      COMPLEX*16         ZLADIV
      LOGICAL            LSAME
      DOUBLE PRECISION   DLAMCH, ZLANHS
      EXTERNAL           ZLADIV, LSAME, DLAMCH, ZLANHS
*     ..
*     .. External Subroutines ..
      EXTERNAL           XERBLA, ZLARTG, ZLASET, ZROT, ZSCAL
*     ..
*     .. Intrinsic Functions ..
      INTRINSIC          ABS, DBLE, DCMPLX, DCONJG, DIMAG, MAX, MIN,
     $                   SQRT
*     ..
*     .. Statement Functions ..
      DOUBLE PRECISION   ABS1
*     ..
*     .. Statement Function definitions ..
      ABS1( X ) = ABS( DBLE( X ) ) + ABS( DIMAG( X ) )
*     ..
*     .. Executable Statements ..
*
*     Decode JOB, COMPQ, COMPZ
*
      IF( LSAME( JOB, 'E' ) ) THEN
         ILSCHR = .FALSE.
         ISCHUR = 1
      ELSE IF( LSAME( JOB, 'S' ) ) THEN
         ILSCHR = .TRUE.
         ISCHUR = 2
      ELSE
         ISCHUR = 0
      END IF
*
      IF( LSAME( COMPQ, 'N' ) ) THEN
         ILQ = .FALSE.
         ICOMPQ = 1
      ELSE IF( LSAME( COMPQ, 'V' ) ) THEN
         ILQ = .TRUE.
         ICOMPQ = 2
      ELSE IF( LSAME( COMPQ, 'I' ) ) THEN
         ILQ = .TRUE.
         ICOMPQ = 3
      ELSE
         ICOMPQ = 0
      END IF
*
      IF( LSAME( COMPZ, 'N' ) ) THEN
         ILZ = .FALSE.
         ICOMPZ = 1
      ELSE IF( LSAME( COMPZ, 'V' ) ) THEN
         ILZ = .TRUE.
         ICOMPZ = 2
      ELSE IF( LSAME( COMPZ, 'I' ) ) THEN
         ILZ = .TRUE.
         ICOMPZ = 3
      ELSE
         ICOMPZ = 0
      END IF
*
*     Check Argument Values
*
      INFO = 0
      WORK( 1 ) = MAX( 1, N )
      LQUERY = ( LWORK.EQ.-1 )
      IF( ISCHUR.EQ.0 ) THEN
         INFO = -1
      ELSE IF( ICOMPQ.EQ.0 ) THEN
         INFO = -2
      ELSE IF( ICOMPZ.EQ.0 ) THEN
         INFO = -3
      ELSE IF( N.LT.0 ) THEN
         INFO = -4
      ELSE IF( ILO.LT.1 ) THEN
         INFO = -5
      ELSE IF( IHI.GT.N .OR. IHI.LT.ILO-1 ) THEN
         INFO = -6
      ELSE IF( LDH.LT.N ) THEN
         INFO = -8
      ELSE IF( LDT.LT.N ) THEN
         INFO = -10
      ELSE IF( LDQ.LT.1 .OR. ( ILQ .AND. LDQ.LT.N ) ) THEN
         INFO = -14
      ELSE IF( LDZ.LT.1 .OR. ( ILZ .AND. LDZ.LT.N ) ) THEN
         INFO = -16
      ELSE IF( LWORK.LT.MAX( 1, N ) .AND. .NOT.LQUERY ) THEN
         INFO = -18
      END IF
      IF( INFO.NE.0 ) THEN
         CALL XERBLA( 'ZHGEQZ', -INFO )
         RETURN
      ELSE IF( LQUERY ) THEN
         RETURN
      END IF
*
*     Quick return if possible
*
*     WORK( 1 ) = CMPLX( 1 )
      IF( N.LE.0 ) THEN
         WORK( 1 ) = DCMPLX( 1 )
         RETURN
      END IF
*
*     Initialize Q and Z
*
      IF( ICOMPQ.EQ.3 )
     $   CALL ZLASET( 'Full', N, N, CZERO, CONE, Q, LDQ )
      IF( ICOMPZ.EQ.3 )
     $   CALL ZLASET( 'Full', N, N, CZERO, CONE, Z, LDZ )
*
*     Machine Constants
*
      IN = IHI + 1 - ILO
      SAFMIN = DLAMCH( 'S' )
      ULP = DLAMCH( 'E' )*DLAMCH( 'B' )
      ANORM = ZLANHS( 'F', IN, H( ILO, ILO ), LDH, RWORK )
      BNORM = ZLANHS( 'F', IN, T( ILO, ILO ), LDT, RWORK )
      ATOL = MAX( SAFMIN, ULP*ANORM )
      BTOL = MAX( SAFMIN, ULP*BNORM )
      ASCALE = ONE / MAX( SAFMIN, ANORM )
      BSCALE = ONE / MAX( SAFMIN, BNORM )
*
*
*     Set Eigenvalues IHI+1:N
*
      DO 10 J = IHI + 1, N
         ABSB = ABS( T( J, J ) )
         IF( ABSB.GT.SAFMIN ) THEN
            SIGNBC = DCONJG( T( J, J ) / ABSB )
            T( J, J ) = ABSB
            IF( ILSCHR ) THEN
               CALL ZSCAL( J-1, SIGNBC, T( 1, J ), 1 )
               CALL ZSCAL( J, SIGNBC, H( 1, J ), 1 )
            ELSE
               CALL ZSCAL( 1, SIGNBC, H( J, J ), 1 )
            END IF
            IF( ILZ )
     $         CALL ZSCAL( N, SIGNBC, Z( 1, J ), 1 )
         ELSE
            T( J, J ) = CZERO
         END IF
         ALPHA( J ) = H( J, J )
         BETA( J ) = T( J, J )
   10 CONTINUE
*
*     If IHI < ILO, skip QZ steps
*
      IF( IHI.LT.ILO )
     $   GO TO 190
*
*     MAIN QZ ITERATION LOOP
*
*     Initialize dynamic indices
*
*     Eigenvalues ILAST+1:N have been found.
*        Column operations modify rows IFRSTM:whatever
*        Row operations modify columns whatever:ILASTM
*
*     If only eigenvalues are being computed, then
*        IFRSTM is the row of the last splitting row above row ILAST;
*        this is always at least ILO.
*     IITER counts iterations since the last eigenvalue was found,
*        to tell when to use an extraordinary shift.
*     MAXIT is the maximum number of QZ sweeps allowed.
*
      ILAST = IHI
      IF( ILSCHR ) THEN
         IFRSTM = 1
         ILASTM = N
      ELSE
         IFRSTM = ILO
         ILASTM = IHI
      END IF
      IITER = 0
      ESHIFT = CZERO
      MAXIT = 30*( IHI-ILO+1 )
*
      DO 170 JITER = 1, MAXIT
*
*        Check for too many iterations.
*
         IF( JITER.GT.MAXIT )
     $      GO TO 180
*
*        Split the matrix if possible.
*
*        Two tests:
*           1: H(j,j-1)=0  or  j=ILO
*           2: T(j,j)=0
*
*        Special case: j=ILAST
*
         IF( ILAST.EQ.ILO ) THEN
            GO TO 60
         ELSE
            IF( ABS1( H( ILAST, ILAST-1 ) ).LE.ATOL ) THEN
               H( ILAST, ILAST-1 ) = CZERO
               GO TO 60
            END IF
         END IF
*
         IF( ABS( T( ILAST, ILAST ) ).LE.BTOL ) THEN
            T( ILAST, ILAST ) = CZERO
            GO TO 50
         END IF
*
*        General case: j<ILAST
*
         DO 40 J = ILAST - 1, ILO, -1
*
*           Test 1: for H(j,j-1)=0 or j=ILO
*
            IF( J.EQ.ILO ) THEN
               ILAZRO = .TRUE.
            ELSE
               IF( ABS1( H( J, J-1 ) ).LE.ATOL ) THEN
                  H( J, J-1 ) = CZERO
                  ILAZRO = .TRUE.
               ELSE
                  ILAZRO = .FALSE.
               END IF
            END IF
*
*           Test 2: for T(j,j)=0
*
            IF( ABS( T( J, J ) ).LT.BTOL ) THEN
               T( J, J ) = CZERO
*
*              Test 1a: Check for 2 consecutive small subdiagonals in A
*
               ILAZR2 = .FALSE.
               IF( .NOT.ILAZRO ) THEN
                  IF( ABS1( H( J, J-1 ) )*( ASCALE*ABS1( H( J+1,
     $                J ) ) ).LE.ABS1( H( J, J ) )*( ASCALE*ATOL ) )
     $                ILAZR2 = .TRUE.
               END IF
*
*              If both tests pass (1 & 2), i.e., the leading diagonal
*              element of B in the block is zero, split a 1x1 block off
*              at the top. (I.e., at the J-th row/column) The leading
*              diagonal element of the remainder can also be zero, so
*              this may have to be done repeatedly.
*
               IF( ILAZRO .OR. ILAZR2 ) THEN
                  DO 20 JCH = J, ILAST - 1
                     CTEMP = H( JCH, JCH )
                     CALL ZLARTG( CTEMP, H( JCH+1, JCH ), C, S,
     $                            H( JCH, JCH ) )
                     H( JCH+1, JCH ) = CZERO
                     CALL ZROT( ILASTM-JCH, H( JCH, JCH+1 ), LDH,
     $                          H( JCH+1, JCH+1 ), LDH, C, S )
                     CALL ZROT( ILASTM-JCH, T( JCH, JCH+1 ), LDT,
     $                          T( JCH+1, JCH+1 ), LDT, C, S )
                     IF( ILQ )
     $                  CALL ZROT( N, Q( 1, JCH ), 1, Q( 1, JCH+1 ), 1,
     $                             C, DCONJG( S ) )
                     IF( ILAZR2 )
     $                  H( JCH, JCH-1 ) = H( JCH, JCH-1 )*C
                     ILAZR2 = .FALSE.
                     IF( ABS1( T( JCH+1, JCH+1 ) ).GE.BTOL ) THEN
                        IF( JCH+1.GE.ILAST ) THEN
                           GO TO 60
                        ELSE
                           IFIRST = JCH + 1
                           GO TO 70
                        END IF
                     END IF
                     T( JCH+1, JCH+1 ) = CZERO
   20             CONTINUE
                  GO TO 50
               ELSE
*
*                 Only test 2 passed -- chase the zero to T(ILAST,ILAST)
*                 Then process as in the case T(ILAST,ILAST)=0
*
                  DO 30 JCH = J, ILAST - 1
                     CTEMP = T( JCH, JCH+1 )
                     CALL ZLARTG( CTEMP, T( JCH+1, JCH+1 ), C, S,
     $                            T( JCH, JCH+1 ) )
                     T( JCH+1, JCH+1 ) = CZERO
                     IF( JCH.LT.ILASTM-1 )
     $                  CALL ZROT( ILASTM-JCH-1, T( JCH, JCH+2 ), LDT,
     $                             T( JCH+1, JCH+2 ), LDT, C, S )
                     CALL ZROT( ILASTM-JCH+2, H( JCH, JCH-1 ), LDH,
     $                          H( JCH+1, JCH-1 ), LDH, C, S )
                     IF( ILQ )
     $                  CALL ZROT( N, Q( 1, JCH ), 1, Q( 1, JCH+1 ), 1,
     $                             C, DCONJG( S ) )
                     CTEMP = H( JCH+1, JCH )
                     CALL ZLARTG( CTEMP, H( JCH+1, JCH-1 ), C, S,
     $                            H( JCH+1, JCH ) )
                     H( JCH+1, JCH-1 ) = CZERO
                     CALL ZROT( JCH+1-IFRSTM, H( IFRSTM, JCH ), 1,
     $                          H( IFRSTM, JCH-1 ), 1, C, S )
                     CALL ZROT( JCH-IFRSTM, T( IFRSTM, JCH ), 1,
     $                          T( IFRSTM, JCH-1 ), 1, C, S )
                     IF( ILZ )
     $                  CALL ZROT( N, Z( 1, JCH ), 1, Z( 1, JCH-1 ), 1,
     $                             C, S )
   30             CONTINUE
                  GO TO 50
               END IF
            ELSE IF( ILAZRO ) THEN
*
*              Only test 1 passed -- work on J:ILAST
*
               IFIRST = J
               GO TO 70
            END IF
*
*           Neither test passed -- try next J
*
   40    CONTINUE
*
*        (Drop-through is "impossible")
*
         INFO = 2*N + 1
         GO TO 210
*
*        T(ILAST,ILAST)=0 -- clear H(ILAST,ILAST-1) to split off a
*        1x1 block.
*
   50    CONTINUE
         CTEMP = H( ILAST, ILAST )
         CALL ZLARTG( CTEMP, H( ILAST, ILAST-1 ), C, S,
     $                H( ILAST, ILAST ) )
         H( ILAST, ILAST-1 ) = CZERO
         CALL ZROT( ILAST-IFRSTM, H( IFRSTM, ILAST ), 1,
     $              H( IFRSTM, ILAST-1 ), 1, C, S )
         CALL ZROT( ILAST-IFRSTM, T( IFRSTM, ILAST ), 1,
     $              T( IFRSTM, ILAST-1 ), 1, C, S )
         IF( ILZ )
     $      CALL ZROT( N, Z( 1, ILAST ), 1, Z( 1, ILAST-1 ), 1, C, S )
*
*        H(ILAST,ILAST-1)=0 -- Standardize B, set ALPHA and BETA
*
   60    CONTINUE
         ABSB = ABS( T( ILAST, ILAST ) )
         IF( ABSB.GT.SAFMIN ) THEN
            SIGNBC = DCONJG( T( ILAST, ILAST ) / ABSB )
            T( ILAST, ILAST ) = ABSB
            IF( ILSCHR ) THEN
               CALL ZSCAL( ILAST-IFRSTM, SIGNBC, T( IFRSTM, ILAST ), 1 )
               CALL ZSCAL( ILAST+1-IFRSTM, SIGNBC, H( IFRSTM, ILAST ),
     $                     1 )
            ELSE
               CALL ZSCAL( 1, SIGNBC, H( ILAST, ILAST ), 1 )
            END IF
            IF( ILZ )
     $         CALL ZSCAL( N, SIGNBC, Z( 1, ILAST ), 1 )
         ELSE
            T( ILAST, ILAST ) = CZERO
         END IF
         ALPHA( ILAST ) = H( ILAST, ILAST )
         BETA( ILAST ) = T( ILAST, ILAST )
*
*        Go to next block -- exit if finished.
*
         ILAST = ILAST - 1
         IF( ILAST.LT.ILO )
     $      GO TO 190
*
*        Reset counters
*
         IITER = 0
         ESHIFT = CZERO
         IF( .NOT.ILSCHR ) THEN
            ILASTM = ILAST
            IF( IFRSTM.GT.ILAST )
     $         IFRSTM = ILO
         END IF
         GO TO 160
*
*        QZ step
*
*        This iteration only involves rows/columns IFIRST:ILAST.  We
*        assume IFIRST < ILAST, and that the diagonal of B is non-zero.
*
   70    CONTINUE
         IITER = IITER + 1
         IF( .NOT.ILSCHR ) THEN
            IFRSTM = IFIRST
         END IF
*
*        Compute the Shift.
*
*        At this point, IFIRST < ILAST, and the diagonal elements of
*        T(IFIRST:ILAST,IFIRST,ILAST) are larger than BTOL (in
*        magnitude)
*
         IF( ( IITER / 10 )*10.NE.IITER ) THEN
*
*           The Wilkinson shift (AEP p.512), i.e., the eigenvalue of
*           the bottom-right 2x2 block of A inv(B) which is nearest to
*           the bottom-right element.
*
*           We factor B as U*D, where U has unit diagonals, and
*           compute (A*inv(D))*inv(U).
*
            U12 = ( BSCALE*T( ILAST-1, ILAST ) ) /
     $            ( BSCALE*T( ILAST, ILAST ) )
            AD11 = ( ASCALE*H( ILAST-1, ILAST-1 ) ) /
     $             ( BSCALE*T( ILAST-1, ILAST-1 ) )
            AD21 = ( ASCALE*H( ILAST, ILAST-1 ) ) /
     $             ( BSCALE*T( ILAST-1, ILAST-1 ) )
            AD12 = ( ASCALE*H( ILAST-1, ILAST ) ) /
     $             ( BSCALE*T( ILAST, ILAST ) )
            AD22 = ( ASCALE*H( ILAST, ILAST ) ) /
     $             ( BSCALE*T( ILAST, ILAST ) )
            ABI22 = AD22 - U12*AD21
            ABI12 = AD12 - U12*AD11
*
            SHIFT = ABI22
            CTEMP = SQRT( ABI12 )*SQRT( AD21 )
            TEMP = ABS1( CTEMP )
            IF( CTEMP.NE.ZERO ) THEN
               X = HALF*( AD11-SHIFT )
               TEMP2 = ABS1( X )
               TEMP = MAX( TEMP, ABS1( X ) )
               Y = TEMP*SQRT( ( X / TEMP )**2+( CTEMP / TEMP )**2 )
               IF( TEMP2.GT.ZERO ) THEN
                  IF( DBLE( X / TEMP2 )*DBLE( Y )+
     $                DIMAG( X / TEMP2 )*DIMAG( Y ).LT.ZERO )Y = -Y
               END IF
               SHIFT = SHIFT - CTEMP*ZLADIV( CTEMP, ( X+Y ) )
            END IF
         ELSE
*
*           Exceptional shift.  Chosen for no particularly good reason.
*
            IF( ( IITER / 20 )*20.EQ.IITER .AND.
     $         BSCALE*ABS1(T( ILAST, ILAST )).GT.SAFMIN ) THEN
               ESHIFT = ESHIFT + ( ASCALE*H( ILAST,
     $            ILAST ) )/( BSCALE*T( ILAST, ILAST ) )
            ELSE
               ESHIFT = ESHIFT + ( ASCALE*H( ILAST,
     $            ILAST-1 ) )/( BSCALE*T( ILAST-1, ILAST-1 ) )
            END IF
            SHIFT = ESHIFT
         END IF
*
*        Now check for two consecutive small subdiagonals.
*
         DO 80 J = ILAST - 1, IFIRST + 1, -1
            ISTART = J
            CTEMP = ASCALE*H( J, J ) - SHIFT*( BSCALE*T( J, J ) )
            TEMP = ABS1( CTEMP )
            TEMP2 = ASCALE*ABS1( H( J+1, J ) )
            TEMPR = MAX( TEMP, TEMP2 )
            IF( TEMPR.LT.ONE .AND. TEMPR.NE.ZERO ) THEN
               TEMP = TEMP / TEMPR
               TEMP2 = TEMP2 / TEMPR
            END IF
            IF( ABS1( H( J, J-1 ) )*TEMP2.LE.TEMP*ATOL )
     $         GO TO 90
   80    CONTINUE
*
         ISTART = IFIRST
         CTEMP = ASCALE*H( IFIRST, IFIRST ) -
     $           SHIFT*( BSCALE*T( IFIRST, IFIRST ) )
   90    CONTINUE
*
*        Do an implicit-shift QZ sweep.
*
*        Initial Q
*
         CTEMP2 = ASCALE*H( ISTART+1, ISTART )
         CALL ZLARTG( CTEMP, CTEMP2, C, S, CTEMP3 )
*
*        Sweep
*
         DO 150 J = ISTART, ILAST - 1
            IF( J.GT.ISTART ) THEN
               CTEMP = H( J, J-1 )
               CALL ZLARTG( CTEMP, H( J+1, J-1 ), C, S, H( J, J-1 ) )
               H( J+1, J-1 ) = CZERO
            END IF
*
            DO 100 JC = J, ILASTM
               CTEMP = C*H( J, JC ) + S*H( J+1, JC )
               H( J+1, JC ) = -DCONJG( S )*H( J, JC ) + C*H( J+1, JC )
               H( J, JC ) = CTEMP
               CTEMP2 = C*T( J, JC ) + S*T( J+1, JC )
               T( J+1, JC ) = -DCONJG( S )*T( J, JC ) + C*T( J+1, JC )
               T( J, JC ) = CTEMP2
  100       CONTINUE
            IF( ILQ ) THEN
               DO 110 JR = 1, N
                  CTEMP = C*Q( JR, J ) + DCONJG( S )*Q( JR, J+1 )
                  Q( JR, J+1 ) = -S*Q( JR, J ) + C*Q( JR, J+1 )
                  Q( JR, J ) = CTEMP
  110          CONTINUE
            END IF
*
            CTEMP = T( J+1, J+1 )
            CALL ZLARTG( CTEMP, T( J+1, J ), C, S, T( J+1, J+1 ) )
            T( J+1, J ) = CZERO
*
            DO 120 JR = IFRSTM, MIN( J+2, ILAST )
               CTEMP = C*H( JR, J+1 ) + S*H( JR, J )
               H( JR, J ) = -DCONJG( S )*H( JR, J+1 ) + C*H( JR, J )
               H( JR, J+1 ) = CTEMP
  120       CONTINUE
            DO 130 JR = IFRSTM, J
               CTEMP = C*T( JR, J+1 ) + S*T( JR, J )
               T( JR, J ) = -DCONJG( S )*T( JR, J+1 ) + C*T( JR, J )
               T( JR, J+1 ) = CTEMP
  130       CONTINUE
            IF( ILZ ) THEN
               DO 140 JR = 1, N
                  CTEMP = C*Z( JR, J+1 ) + S*Z( JR, J )
                  Z( JR, J ) = -DCONJG( S )*Z( JR, J+1 ) + C*Z( JR, J )
                  Z( JR, J+1 ) = CTEMP
  140          CONTINUE
            END IF
  150    CONTINUE
*
  160    CONTINUE
*
  170 CONTINUE
*
*     Drop-through = non-convergence
*
  180 CONTINUE
      INFO = ILAST
      GO TO 210
*
*     Successful completion of all QZ steps
*
  190 CONTINUE
*
*     Set Eigenvalues 1:ILO-1
*
      DO 200 J = 1, ILO - 1
         ABSB = ABS( T( J, J ) )
         IF( ABSB.GT.SAFMIN ) THEN
            SIGNBC = DCONJG( T( J, J ) / ABSB )
            T( J, J ) = ABSB
            IF( ILSCHR ) THEN
               CALL ZSCAL( J-1, SIGNBC, T( 1, J ), 1 )
               CALL ZSCAL( J, SIGNBC, H( 1, J ), 1 )
            ELSE
               CALL ZSCAL( 1, SIGNBC, H( J, J ), 1 )
            END IF
            IF( ILZ )
     $         CALL ZSCAL( N, SIGNBC, Z( 1, J ), 1 )
         ELSE
            T( J, J ) = CZERO
         END IF
         ALPHA( J ) = H( J, J )
         BETA( J ) = T( J, J )
  200 CONTINUE
*
*     Normal Termination
*
      INFO = 0
*
*     Exit (other than argument error) -- return optimal workspace size
*
  210 CONTINUE
      WORK( 1 ) = DCMPLX( N )
      RETURN
*
*     End of ZHGEQZ
*
      END


      subroutine percmicro(ly1)

!!    ~ ~ ~ PURPOSE ~ ~ ~
!!    this subroutine computes percolation and lateral subsurface flow
!!    from a soil layer when field capacity is exceeded

!!    ~ ~ ~ INCOMING VARIABLES ~ ~ ~
!!    name         |units         |definition
!!    ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
!!    hru_slp(:)   |m/m           |average slope steepness
!!    ihru         |none          |HRU number
!!    iwatable     |none          |high water table code:
!!                                |0 no high water table
!!                                |1 high water table
!!    ldrain(:)    |none          |soil layer where drainage tile is located
!!    slsoil(:)    |m             |slope length for lateral subsurface flow
!!    sol_fc(:,:)  |mm H2O        |amount of water available to plants in soil
!!                                |layer at field capacity (fc - wp water)
!!    sol_hk(:,:)  |none          |beta coefficient to calculate hydraulic
!!                                |conductivity
!!    sol_k(:,:)   |mm/hr         |saturated hydraulic conductivity of soil
!!                                |layer
!!    sol_nly(:)   |none          |number of soil layers in HRU
!!    sol_st(:,:)  |mm H2O        |amount of water stored in the soil layer
!!                                |on any given day
!!    sol_sumfc(:) |mm H2O        |amount of water held in the soil profile
!!                                |at field capacity
!!    sol_sw(:)    |mm H2O        |amount of water stored in the soil profile
!!                                |on any given day
!!    sol_tmp(:,:) |deg C         |daily average temperature of soil layer
!!    sol_ul(:,:)  |mm H2O        |amount of water held in the soil layer at
!!                                |saturation (sat - wp water)
!!    sol_z(:,:)   |mm            |depth to bottom of soil layer
!!    sw_excess    |mm H2O        |amount of water in soil that exceeds field
!!                                |capacity (gravity drained water)
!!    tdrain(:)    |hrs           |time to drain soil to field capacity
!!    ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

!!    ~ ~ ~ OUTGOING VARIABLES ~ ~ ~
!!    name         |units         |definition
!!    ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
!!    latlyr       |mm H2O        |lateral subsurface flow in layer
!!    lyrtile      |mm H2O        |drainage tile flow in layer for day in HRU
!!    sepday       |mm H2O        |percolation from soil layer
!!    ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

!!    ~ ~ ~ LOCAL DEFINITIONS ~ ~ ~
!!    name         |units         |definition
!!    ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
!!    adjf         |none          |adjustment factor for lateral flow
!!    dg           |mm            |depth of soil layer
!!    ho           |none          |variable to hold intermediate calculation
!!                                |result
!!    j            |none          |HRU number
!!    ly1          |none          |soil layer number
!!    ratio        |none          |ratio of seepage to (latq + sepday)
!!    yy           |mm            |depth to top of soil layer
!!    ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

!!    ~ ~ ~ SUBROUTINES/FUNCTIONS CALLED ~ ~ ~
!!    Intrinsic: Exp

!!    ~ ~ ~ ~ ~ ~ END SPECIFICATIONS ~ ~ ~ ~ ~ ~

      use parm

      integer, intent (in) :: ly1
      integer :: j
      real :: adjf, yy, dg, ho, ratio, sol_k_sep

      j = 0
      j = ihru

      adjf = 1.

      !! if temperature of layer is 0 degrees C or below
      !! there is no water flow
      if (sol_tmp(ly1,j) <= 0.) then
        sepday = 0.
        return
      end if

        !! COMPUTE LATERAL FLOW USING HILLSLOPE STORAGE METHOD
        if (ly1 == 1) then
          yy = 0.
        else
          yy = 0.
          yy = sol_z(ly1-1,j)
        end if

        dg = 0.
        ho = 0.
        latlyr = 0.
        dg = sol_z(ly1,j) - yy
        if (sol_ul(ly1,j) - sol_fc(ly1,j)==0.) then
          ho=0.
        else
          ho = 2. * sw_excess / ((sol_ul(ly1,j) - sol_fc(ly1,j)) /  dg)
        end if
        latlyr = adjf * ho * sol_k(ly1,j) * hru_slp(j) / slsoil(j)
     &                                                            * .024

      if (latlyr < 0.) latlyr = 0.
      if (latlyr > sw_excess) latlyr = sw_excess

      sol_hk(ly1,j) = (sol_ul(ly1,j) - sol_fc(ly1,j)) / sol_k(ly1,j)

!!  septic changes 1/28/09
      if (ly1 == i_sep(j)) then
         if (isep_opt(j) == 1) then !active system
           sol_k_sep = sol_k(ly1,j)*
     &              (sol_st(ly1,j) - sol_fc(ly1,j))/
     &              (sol_ul(ly1,j) - sol_fc(ly1,j))
           sol_k_sep = Max(1.e-6, sol_k_sep)
           sol_k_sep = Min(sol_k(ly1,j), sol_k_sep)

           sol_hk(ly1,j) = (sol_ul(ly1,j) - sol_fc(ly1,j))
     &      / sol_k_sep

         elseif (isep_opt(j) == 2) then !failing system
           sol_hk(ly1,j) = 1.e10
         endif
      endif
!!  septic changes 1/28/09

      sol_hk(ly1,j) = Max(2., sol_hk(ly1,j))

      !! compute seepage to the next layer
      sepday = 0.
      sepday = sw_excess * (1. - Exp(-24. / sol_hk(ly1,j)))

      !! limit maximum seepage from biozone layer below potential perc amount
	if(ly1 == i_sep(j).and.isep_opt(j)==1) then
	   sepday = min(sepday,sol_k_sep *24.)
	   bz_perc(j) = sepday
	end if

      !! restrict seepage if next layer is saturated
      if (ly1 == sol_nly(j)) then
        xx = (dep_imp(j) - sol_z(ly1,j)) / 1000.
        if (xx < 1.e-4) then
          sepday = 0.
        else
          sepday = sepday * xx / (xx + Exp(8.833 - 2.598 * xx))
        end if
      end if

      !! check mass balance
      if (sepday + latlyr > sw_excess) then
        ratio = 0.
        ratio = sepday / (latlyr + sepday)
        sepday = 0.
        latlyr = 0.
        sepday = sw_excess * ratio
        latlyr = sw_excess * (1. - ratio)
      endif
      if (sepday + lyrtile > sw_excess) then
        sepday = 0.
        sepday = sw_excess - lyrtile
      endif

      return
      end


      DOUBLE PRECISION FUNCTION FMOD2P(X)
c  ------------------------------------------------------------------------
c  Part of "poa_calfos" (STECF/IPMG)
c  Part of "NORAD TLES" base ephemeris program (NASA)
c
c  History:
c  --------
c  Version   Date        Author          Description
c      1.0       Jul 00  M. Rosa         POA calibration code
c  ------------------------------------------------------------------------
c
c return angle (in rad) in range 0 < angle < 2pi
c
      INTEGER*4 I
      REAL*8 X

      REAL*8 DE2RA,PI,PIO2,TWOPI,X3PIO2
      COMMON /C2/ DE2RA,PI,PIO2,TWOPI,X3PIO2

      FMOD2P=X
      I=FMOD2P/TWOPI
      FMOD2P=FMOD2P-I*TWOPI
      IF(FMOD2P.LT.0) FMOD2P=FMOD2P+TWOPI
      RETURN
      END


MODULE mzprojection
!-------------------------------------------------------------------------------
!
!  Module of Mori-Zwanzig Projection operator method for data analysis
!
!    f(t) = Omega*u(t) - int_0^t Gamma(s)*u(t-s) ds + r(t)
!
!  Reference: S. Maeyama, T.-H. Watanabe, to be published JPSJ (2020).
!
!-------------------------------------------------------------------------------
  implicit none
  private

  public :: mzprojection_ensemble_of_time_series, &
            mzprojection_long_time_series

 CONTAINS

SUBROUTINE mzprojection_ensemble_of_time_series(  &
               nsample, nperiod, delta_t,         &
               u, dudt, f,                        &
               omega, memoryf, s, r,              &
               uu, ududt, fdudt, rr, rdudt, ru, fu, ff)
!-------------------------------------------------------------------------------
!
!  Evaluate projection of f(t) on u(t)
!
!    *User prepares ensembles of time-series data u(t)^i, du/dt(t)^i, f(t)^i
!
!-------------------------------------------------------------------------------
  implicit none
  != INPUT =
  integer,         intent(in)  :: nsample ! # of samples for ensemble average
  integer,         intent(in)  :: nperiod ! Length of a sample
  real(kind=8),    intent(in)  :: delta_t ! Time step size
  complex(kind=8), intent(in)  ::    u(0:nsample-1,0:nperiod-1) ! Variable u(t)
  complex(kind=8), intent(in)  :: dudt(0:nsample-1,0:nperiod-1) ! = du/dt
  complex(kind=8), intent(in)  ::    f(0:nsample-1,0:nperiod-1) ! Analyzed f(t)
  != OUTPUT =
  complex(kind=8), intent(out) :: omega                      ! Markov coefficient Omega
  complex(kind=8), intent(out) :: memoryf(0:nperiod-1)       ! Memory function Gamma(t)
  complex(kind=8), intent(out) :: s(0:nsample-1,0:nperiod-1) ! Memory term
  complex(kind=8), intent(out) :: r(0:nsample-1,0:nperiod-1) ! Uncorrelated term r(t)
  != OUTPUT for check =
  complex(kind=8), intent(out) ::    uu(0:nperiod-1) ! Correlation <u(t)u>
  complex(kind=8), intent(out) :: ududt(0:nperiod-1) ! Correlation <u(t)du/dt>
  complex(kind=8), intent(out) :: fdudt(0:nperiod-1) ! Correlation <f(t)du/dt>
  complex(kind=8), intent(out) ::    rr(0:nperiod-1) ! Correlation <r(t)r>
  complex(kind=8), intent(out) :: rdudt(0:nperiod-1) ! Correlation <r(t)du/dt>
  complex(kind=8), intent(out) ::    ru(0:nperiod-1) ! Correlation <r(t)u>
  complex(kind=8), intent(out) ::    fu(0:nperiod-1) ! Correlation <f(t)u>
  complex(kind=8), intent(out) ::    ff(0:nperiod-1) ! Correlation <f(t)f>

  integer :: iperiod, isample

    != Evaluate Markov coefficient Omega =
    omega = sum(f(0:nsample-1,0)*conjg(u(0:nsample-1,0)))  &
          / sum(u(0:nsample-1,0)*conjg(u(0:nsample-1,0)))

    != Evaluate memory function Gamma(t) =
    do iperiod = 0, nperiod-1
         uu(iperiod) = sum(u(0:nsample-1,iperiod) * conjg(   u(0:nsample-1,0))) / nsample
      ududt(iperiod) = sum(u(0:nsample-1,iperiod) * conjg(dudt(0:nsample-1,0))) / nsample
      fdudt(iperiod) = sum(f(0:nsample-1,iperiod) * conjg(dudt(0:nsample-1,0))) / nsample
    end do
    !- 2nd-order solver for memory equation -
    memoryf(0) = (fdudt(0) - omega*ududt(0)) / uu(0)
    memoryf(1) = (fdudt(1) - omega*ududt(1) + memoryf(0)*ududt(1)*0.5d0*delta_t) / uu(0)
    memoryf(1) = memoryf(1) / (1.d0 - ududt(0)*0.5d0*delta_t/uu(0))
    do iperiod = 2, nperiod-1
      memoryf(iperiod) = (fdudt(iperiod) - omega*ududt(iperiod)    &
                       + memoryf(0)*ududt(iperiod)*0.5d0*delta_t  &
                       + sum(memoryf(1:iperiod-1)*ududt(iperiod-1:1:-1))*delta_t) / uu(0)
      memoryf(iperiod) = memoryf(iperiod) / (1.d0 - ududt(0)*0.5d0*delta_t/uu(0))
    end do
    !-

    != Evaluate uncorrelated term r(t) as a residual =
    !- 2nd-order integration for memory term s(t) -
    s(:,0) = 0.d0
    r(:,0) = f(:,0) - omega*u(:,0)
    do iperiod = 1, nperiod-1
      do isample = 0, nsample-1
        s(isample,iperiod) = - (sum(memoryf(0:iperiod)*u(isample,iperiod:0:-1))*delta_t  &
                             - memoryf(0)*u(isample,iperiod) *0.5d0*delta_t              &
                             - memoryf(iperiod)*u(isample,0) *0.5d0*delta_t)
        r(isample,iperiod) = f(isample,iperiod) - omega*u(isample,iperiod) - s(isample,iperiod)
      end do
    end do
    !-

    != For check memory function and <r(t)u>=0 =
    do iperiod = 0, nperiod-1
         rr(iperiod) = sum(r(0:nsample-1,iperiod) * conjg(   r(0:nsample-1,0))) / nsample
      rdudt(iperiod) = sum(r(0:nsample-1,iperiod) * conjg(dudt(0:nsample-1,0))) / nsample
         ru(iperiod) = sum(r(0:nsample-1,iperiod) * conjg(   u(0:nsample-1,0))) / nsample
         fu(iperiod) = sum(f(0:nsample-1,iperiod) * conjg(   u(0:nsample-1,0))) / nsample
         ff(iperiod) = sum(f(0:nsample-1,iperiod) * conjg(   f(0:nsample-1,0))) / nsample
    end do

    !%%% NOTE %%%
    !% Since the above s, r is defined from u(isample,:) and f(isample,:),
    !%     s(isample,0) = 0
    !% is always imposed. Then, <s(t)s>=0, because s(0)=0.
    !% However, after a long time, s and r is also expected to be in a statistically
    !% steady state, <s(t)s> = <s(t+x)s(x)> /= 0.
    !%%%%%%%%%%%%%%%

END SUBROUTINE mzprojection_ensemble_of_time_series

SUBROUTINE mzprojection_long_time_series(             &
               nrec, ista, nperiod, nshift, delta_t,  &
               u_raw, dudt_raw, f_raw,                &
               omega, memoryf, s_raw, r_raw,          &
               uu, ududt, fdudt, rr, rdudt, ru, fu, ff)
!-------------------------------------------------------------------------------
!
!  Evaluate projection of f(t) on u(t)
!
!    *User prepares long time-series data u(t), du/dt(t), f(t)
!
!-------------------------------------------------------------------------------
  implicit none
  != INPUT =
  integer,         intent(in)  :: nrec      ! Total record number
  integer,         intent(in)  :: ista      ! Start record number for sampling
  integer,         intent(in)  :: nperiod   ! Length of a sample
  integer,         intent(in)  :: nshift    ! Length of time shift while sampling
  real(kind=8),    intent(in)  :: delta_t   ! Time step size
  complex(kind=8), intent(in)  ::    u_raw(0:nrec-1) ! Variable of interest u(t)
  complex(kind=8), intent(in)  :: dudt_raw(0:nrec-1) ! = du/dt
  complex(kind=8), intent(in)  ::    f_raw(0:nrec-1) ! Analyzed data f(t)
  != OUTPUT =
  complex(kind=8), intent(out) :: omega                ! Markov coefficient Omega
  complex(kind=8), intent(out) :: memoryf(0:nperiod-1) ! Memory function Gamma(t)
  complex(kind=8), intent(out) :: s_raw(0:nrec-1)      ! Memory term
  complex(kind=8), intent(out) :: r_raw(0:nrec-1)      ! Uncorrelated term r(t)
  != OUTPUT for check =
  complex(kind=8), intent(out) ::    uu(0:nperiod-1) ! Correlation <u(t)u>
  complex(kind=8), intent(out) :: ududt(0:nperiod-1) ! Correlation <u(t)du/dt>
  complex(kind=8), intent(out) :: fdudt(0:nperiod-1) ! Correlation <f(t)du/dt>
  complex(kind=8), intent(out) ::    rr(0:nperiod-1) ! Correlation <r(t)r>
  complex(kind=8), intent(out) :: rdudt(0:nperiod-1) ! Correlation <r(t)du/dt>
  complex(kind=8), intent(out) ::    ru(0:nperiod-1) ! Correlation <r(t)u>
  complex(kind=8), intent(out) ::    fu(0:nperiod-1) ! Correlation <f(t)u>
  complex(kind=8), intent(out) ::    ff(0:nperiod-1) ! Correlation <f(t)f>

  integer :: nsample_raw ! Number of samples for ensemble average
  complex(kind=8), dimension(0:(nrec-ista-nperiod)/nshift) ::  &
              u0, dudt0, f0, s0, r0, &! Initial values of sample u(0)
              u,  dudt,  f,  s,  r    ! Temporal valudes of samples u(t)
  integer :: irec, iperiod, isample

    nsample_raw = (nrec-ista-nperiod)/nshift + 1

    != Split raw data into ensembles =
    !!!!nsample_raw = (nrec-ista-nperiod)/nshift
    !!!!do isample = 0, nsample_raw-1
    !!!!  do iperiod = 0, nperiod-1
    !!!!    irec = ista + nshift * isample + iperiod
    !!!!    u(isample,iperiod) = u_raw(irec)
    !!!!    dudt(isample,iperiod) = dudt_raw(irec)
    !!!!    f(isample,iperiod) = f_raw(irec)
    !!!!  end do
    !!!!end do
    iperiod = 0
      do isample = 0, nsample_raw-1
        irec = ista + nshift * isample + iperiod
           u0(isample) = u_raw(irec)
        dudt0(isample) = dudt_raw(irec)
           f0(isample) = f_raw(irec)
      end do

    != Evaluate Markov correlation Omega =
    omega = sum(f0(0:nsample_raw-1)*conjg(u0(0:nsample_raw-1)))  &
          / sum(u0(0:nsample_raw-1)*conjg(u0(0:nsample_raw-1)))

    != Evaluate memory function Gamma(t) =
    do iperiod = 0, nperiod-1
      do isample = 0, nsample_raw-1
        irec = ista + nshift * isample + iperiod
           u(isample) = u_raw(irec)
        dudt(isample) = dudt_raw(irec)
           f(isample) = f_raw(irec)
      end do
         uu(iperiod) = sum(u(0:nsample_raw-1) * conjg(   u0(0:nsample_raw-1))) / nsample_raw
      ududt(iperiod) = sum(u(0:nsample_raw-1) * conjg(dudt0(0:nsample_raw-1))) / nsample_raw
      fdudt(iperiod) = sum(f(0:nsample_raw-1) * conjg(dudt0(0:nsample_raw-1))) / nsample_raw
      != For check <r(t)u>=0 =
         fu(iperiod) = sum(f(0:nsample_raw-1) * conjg(   u0(0:nsample_raw-1))) / nsample_raw
         ff(iperiod) = sum(f(0:nsample_raw-1) * conjg(   f0(0:nsample_raw-1))) / nsample_raw
    end do
    !- 2nd-order solver for memory equation -
    memoryf(0) = (fdudt(0) - omega*ududt(0)) / uu(0)
    memoryf(1) = (fdudt(1) - omega*ududt(1) + memoryf(0)*ududt(1)*0.5d0*delta_t) / uu(0)
    memoryf(1) = memoryf(1) / (1.d0 - ududt(0)*0.5d0*delta_t/uu(0))
    do iperiod = 2, nperiod-1
      memoryf(iperiod) = (fdudt(iperiod) - omega*ududt(iperiod)    &
                       + memoryf(0)*ududt(iperiod)*0.5d0*delta_t  &
                       + sum(memoryf(1:iperiod-1)*ududt(iperiod-1:1:-1))*delta_t) / uu(0)
      memoryf(iperiod) = memoryf(iperiod) / (1.d0 - ududt(0)*0.5d0*delta_t/uu(0))
    end do
    !-

    != Evaluate uncorrelated term r(t) as a residual =
    s_raw(0:ista-1) = 0.d0
    r_raw(0:ista-1) = 0.d0
    !- 2nd-order integration for memory term s(t) -
    s_raw(ista) = 0.d0
    r_raw(ista) = f_raw(ista) - omega*u_raw(ista)
    do irec = 1, nperiod-1
      s_raw(ista+irec) = - (sum(memoryf(0:irec)*u_raw(ista+irec:ista:-1)) *delta_t  &
                         - memoryf(0)*u_raw(ista+irec) *0.5d0*delta_t            &
                         - memoryf(irec)*u_raw(ista) *0.5d0*delta_t)
      r_raw(ista+irec) = f_raw(ista+irec) - omega*u_raw(ista+irec) - s_raw(ista+irec)
    end do
    do irec = ista+nperiod, nrec-1
      s_raw(irec) = - (sum(memoryf(0:nperiod-1)*u_raw(irec:irec-nperiod+1:-1)) *delta_t  &
                         - memoryf(0)*u_raw(irec) *0.5d0*delta_t                         &
                         - memoryf(nperiod-1)*u_raw(irec-nperiod+1) *0.5d0*delta_t)
      r_raw(irec) = f_raw(irec) - omega*u_raw(irec) - s_raw(irec)
    end do
    !-

    != For check memory function =
    !- 2nd-order integration for memory term s -
    iperiod = 0
      do isample = 0, nsample_raw-1
        irec = ista + nshift * isample + iperiod
        s0(isample) = (0.d0, 0.d0)
        r0(isample) = f_raw(irec) - omega*u_raw(irec)
      end do
         rr(iperiod) = sum(r0(0:nsample_raw-1) * conjg(   r0(0:nsample_raw-1))) / nsample_raw
      rdudt(iperiod) = sum(r0(0:nsample_raw-1) * conjg(dudt0(0:nsample_raw-1))) / nsample_raw
         ru(iperiod) = sum(r0(0:nsample_raw-1) * conjg(   u0(0:nsample_raw-1))) / nsample_raw
    do iperiod = 1, nperiod-1
      do isample = 0, nsample_raw-1
        irec = ista + nshift * isample + iperiod
        s(isample) = - (sum(memoryf(0:iperiod)*u_raw(irec:irec-iperiod:-1))*delta_t  &
                     - memoryf(0)*u_raw(irec) *0.5d0*delta_t              &
                     - memoryf(iperiod)*u_raw(irec-iperiod) *0.5d0*delta_t)
        r(isample) = f_raw(irec) - omega*u_raw(irec) - s(isample)
      end do
         rr(iperiod) = sum(r(0:nsample_raw-1) * conjg(   r0(0:nsample_raw-1))) / nsample_raw
      rdudt(iperiod) = sum(r(0:nsample_raw-1) * conjg(dudt0(0:nsample_raw-1))) / nsample_raw
         ru(iperiod) = sum(r(0:nsample_raw-1) * conjg(   u0(0:nsample_raw-1))) / nsample_raw
    end do
    !-

END SUBROUTINE mzprojection_long_time_series

END MODULE mzprojection


      subroutine runY_00i1i2i3i4(k,l,i1,i2,i3,i4,Xtwiddle,Gtwiddle,
     . Shat6,N0)
      implicit none
C---  Expression for extension of Eq. 5.60c
C---  Calculates D00i1i2i3i4, requires D00li1i2i3
C---  Small terms of order Xtwiddle(0,k)*Diiiii,Xtwiddle(0,0)*Diiiiii
C---  Denominator Gtwiddle(k,l)
      include 'pvDnames.f'
      include 'pvDv.f'
      include 'Darraydef.f'
      include 'Darrays.f'
      integer ep,N0,k,l,i1,i2,i3,i4,np
      parameter(np=3)
      double precision Xtwiddle(0:np,0:np),Gtwiddle(np,np)
      double complex Shat6(np,z5max,-2:0)

      if (  (i1.eq.l) .or. (i2.eq.l) .or. (i3.eq.l) .or. (i4.eq.l)
     . .or. (i1.eq.0) .or. (i2.eq.0) .or. (i3.eq.0) .or. (i4.eq.0)) then
      return
      endif

      do ep=-2,0
      Dv(dzziiii(z4(i1,i2,i3,i4))+N0,ep)=
     .(-2d0*Gtwiddle(k,i1)*Dv(dzziiii(z4(l,i2,i3,i4))+N0,ep)
     . -2d0*Gtwiddle(k,i2)*Dv(dzziiii(z4(l,i1,i3,i4))+N0,ep)
     . -2d0*Gtwiddle(k,i3)*Dv(dzziiii(z4(l,i1,i2,i4))+N0,ep)
     . -2d0*Gtwiddle(k,i4)*Dv(dzziiii(z4(l,i1,i2,i3))+N0,ep)
     . +Gtwiddle(k,1)*Shat6(1,z5(l,i1,i2,i3,i4),ep)
     . +Gtwiddle(k,2)*Shat6(2,z5(l,i1,i2,i3,i4),ep)
     . +Gtwiddle(k,3)*Shat6(3,z5(l,i1,i2,i3,i4),ep)
     . +Xtwiddle(k,0)*Dv(diiiii(z5(l,i1,i2,i3,i4))+N0,ep)
     . -Xtwiddle(0,0)*Dv(diiiiii(z6(k,l,i1,i2,i3,i4))+N0,ep))
     . /(2d0*Gtwiddle(k,l))

      enddo

      return
      end


*deck dihangle_element
      subroutine dihangle_element(ielmtyp,xice,yice,zice,
     *                            minangle,maxangle)
C
C
C #####################################################################
C
C     PURPOSE -
C
C        Finds the max and min dihedral angles between adjacent
C        faces of a 3D element or edges of a 2D element.
C
C     INPUT ARGUMENTS -
C
C        ielmtyp                : Element type.
C        (xice(),yice(),zice()) : The coordinates of the points of the
C                                 element.  They must be in the order
C                                 in which they appear in itet.
C
C     OUTPUT ARGUMENTS -
C
C        minangle,maxangle : The min and max angles of the element
C                            in radians.
C
C     CHANGE HISTORY -
C
C  $Log: dihangle_element.f,v $
C  Revision 2.00  2007/11/05 19:45:52  spchu
C  Import to CVS
C
CPVCS
CPVCS       Rev 1.0   Fri Aug 29 14:06:56 1997   dcg
CPVCS    Initial revision.
C
C ######################################################################
C
C
      implicit none
C
      include "local_element.h"
C
      character* 132 logmess
      integer icscode

      integer ielmtyp
      real*8 xice(1000000),yice(1000000),zice(1000000)
      real*8 minangle,maxangle

      real*8 f_angle
      integer i,j,k,l,ptcount,pointsi,pointsj,k1,l1,k2,l2
      real*8 x1,y1,z1,x2,y2,z2,dot,norm1,norm2
      real*8 xici(8),yici(8),zici(8),xicj(8),yicj(8),zicj(8)
C
C ######################################################################
C
C

         minangle = 8.0
         maxangle = -8.0

      if ((ielmtyp .eq. ifelmtet) .or.
     *    (ielmtyp .eq. ifelmpyr) .or.
     *    (ielmtyp .eq. ifelmpri) .or.
     *    (ielmtyp .eq. ifelmhex) .or.
     *    (ielmtyp .eq. ifelmhyb)) then
         do i=1,nelmnef(ielmtyp)-1
            do j=i+1,nelmnef(ielmtyp)
               ptcount = 0
               do k=1,ielmface0(i,ielmtyp)
                  k1 = ielmface1(k,i,ielmtyp)
                  do l=1,ielmface0(j,ielmtyp)
C  Check to see how many points these faces meet in
                     l1 = ielmface1(l,j,ielmtyp)
                     if (k1 .eq. l1) then
                        ptcount = ptcount + 1
                     endif
                  enddo
C  Record the coordinates of the point k on face i
                  xici(k)=xice(k1)
                  yici(k)=yice(k1)
                  zici(k)=zice(k1)
               enddo
               pointsi = ielmface0(i,ielmtyp)
C  Record the coordinates of all points on face j
C  Didn't do it above to avoid doing it multiple times
               do l=1,ielmface0(j,ielmtyp)
                  l1 = ielmface1(l,j,ielmtyp)
                  xicj(l)=xice(l1)
                  yicj(l)=yice(l1)
                  zicj(l)=zice(l1)
               enddo
               pointsj = ielmface0(j,ielmtyp)
               if (ptcount .ge. 2) then
                  call dihangle_face(pointsi,xici,yici,zici,
     *                               pointsj,xicj,yicj,zicj,f_angle)
                  if (f_angle .lt. minangle) then
                     minangle = f_angle
                  endif
                  if (f_angle .gt. maxangle) then
                     maxangle = f_angle
                  endif
               endif
               if (ptcount .gt. 2) then
                  write (logmess,"('Too many face-to-face ',
     *                             'intersections.')")
                  call writloga('default',0,logmess,0,icscode)
                  return
               endif
            enddo
         enddo
      elseif ((ielmtyp .eq. ifelmtri) .or.
     *        (ielmtyp .eq. ifelmqud)) then
         do i=1,nelmnee(ielmtyp)-1
            do j=i+1,nelmnee(ielmtyp)
               ptcount = 0
               do k=1,2
                  k1 = ielmedge1(k,i,ielmtyp)
                  do l=1,2
C  Check to see if these edges meet
                     l1 = ielmedge1(l,j,ielmtyp)
                     if (k1 .eq. l1) then
                        ptcount = ptcount + 1
C  Get the other point on each edge
                        k2 = ielmedge1(3-k,i,ielmtyp)
                        l2 = ielmedge1(3-l,j,ielmtyp)
                        x1 = xice(k2) - xice(k1)
                        y1 = yice(k2) - yice(k1)
                        z1 = zice(k2) - zice(k1)
                        x2 = xice(l2) - xice(l1)
                        y2 = yice(l2) - yice(l1)
                        z2 = zice(l2) - zice(l1)
                        norm1 = sqrt(x1*x1 + y1*y1 + z1*z1)
                        norm2 = sqrt(x2*x2 + y2*y2 + z2*z2)
                        x1 = x1 / norm1
                        y1 = y1 / norm1
                        z1 = z1 / norm1
                        x2 = x2 / norm2
                        y2 = y2 / norm2
                        z2 = z2 / norm2

                        dot = x1*x2 + y1*y2 + z1*z2
                        f_angle = acos(dot)
                        if (f_angle .lt. minangle) then
                           minangle = f_angle
                        endif
                        if (f_angle .gt. maxangle) then
                           maxangle = f_angle
                        endif
                     endif
                  enddo
               enddo
               if (ptcount .gt. 1) then
                  write (logmess,"('Too many edge-to-edge ',
     *                             'intersections.')")
                  call writloga('default',0,logmess,0,icscode)
                  return
               endif
            enddo
         enddo
      else
         write (logmess,"('Only triangles, quads and 3D elements ',
     *                    'may have their angles computed.')")
         call writloga('default',0,logmess,0,icscode)
         return
      endif

      return
      end


! Curso: Desarrollo Experimental II (2018-1)
! Proyecto III:

PROGRAM DMLJ
  IMPLICIT REAL*8 (a-h,o-z)
  PARAMETER ( N=250)       !NUMERO DE PARTICULAS
  PARAMETER ( NN2=5000)    !CONFIGURACIOS DE EQUILIBRIO A GUARDAR
  PARAMETER ( NENER=50000) !CONFIGURACIONES ANTES DE LA DE EQUILIBRIO
  PARAMETER ( FREE=3.0)    !GRADOS DE LIBERTAD
  PARAMETER ( PI=3.1415927)
  REAL, EXTERNAL ::ZRAN    !GENERADOR DE NUMEROS ALEATORIOS

  !BLOQUE CONTENEDORES (MODULO)
  COMMON /BLOCK1/ RX, RY, RZ, VX, VY, VZ, FX, FY, FZ
  COMMON /VALORES/ DENS,RCUT,BOX,NSTEP
  COMMON /BLOCK2/ RXC, RYC, RZC

  !INIZIALIZANDO ARREGLOS DE:
  DIMENSION CX(n,nn2),CY(n,nn2),CZ(n,nn2)    !ARREGLOS DE CONFIG G(r)
  DIMENSION CXR(n,nn2),CYR(n,nn2),CZR(n,nn2) !ARREGLOS DE CONGFIG W(t) D(t)
  DIMENSION FX(N), FY(N), FZ(N), VX(N), VY(N), VZ(N) !FUERZA Y VELOCIDADES
  DIMENSION RX(N), RY(N), RZ(N), RXC(N), RYC(N), RZC(N) !POSICIONES

  NSTEP=150000 !NUMERO DE PASOS
  IPRINT=10000 !FRECUENCIA DE IMPRESION EN PANTALLA
  NFREC=20     !FRECUENCIA DE GUARDADO DE CONFIGS
  DT=0.0001    !PASO DE TIEMPO

  DENS=0.6     !CONCENTRACION REDUCIDA
  TEMP=1.5     !TEMPERATURA REDUCIDA

  XM=1.0       !MASA PARTICULA
  SIGMA=1.0    !DIAMETRO ||

  !CALCULOS PREVIOS
  A=1.0/3.0
  BOX=(N/DENS)**A  !Longitud DE CELDA
  RCUT=BOX/2.0     !RADIO DE CORTE
  TEMPI=TEMP       !TEMPERATURA INICIAL
  KI2=0            !CONTADOR DE CONFIG GUARDADAS

  !IMPRESION DE PARAMETROS DE SIMULACION
  WRITE(*,*)'LENNARD-JONES'
  WRITE(*,'('' NUMBER OF ATOMS = '',I10 )') N
  WRITE(*,'('' NUMBER OF STEPS = '',I10 )') NSTEP
  WRITE(*,'('' OUTPUT FREQUENCY = '',I10 )') IPRINT
  WRITE(*,'('' POTENTIAL CUTOFF = '',F10.4)') RCUT
  WRITE(*,'('' DENSITY = '',F10.4)') DENS
  WRITE(*,'('' RED. TEMPERATURE = '',F10.4)') TEMP
  WRITE(*,'('' MASS = '',F10.4)') XM
  WRITE(*,'('' TIME STEP = '',F10.6)') DT

  !ABRIENDO ARCHIVOS
  OPEN(15,FILE='cfdm0.dat',STATUS='UNKNOWN') !CONFIG FINAL
  OPEN(12,FILE='vfdm0.dat',STATUS='UNKNOWN') !VELOCIDAD FINAL
  OPEN(13,FILE='vidm0.dat',STATUS='UNKNOWN') !VELOCIDAD INICIAL
  OPEN(14,FILE='tedm0.dat',STATUS='UNKNOWN') !TERMALIZACION

  CALL CONFIGINI (BOX,RX,RY,RZ) !GENERA LA CONFIGURACION INICIAL
  CALL COMVEL (TEMP)            !GENERA VELOCIDADES ALEATORIAS

  DO INIV=1 , N

     WRITE(13,*)VX(INIV),VY(INIV),VZ(INIV)

  End Do

  !INICIANDO EN CERO
  ACV = 0.0
  ACE = 0.0
  ACP = 0.0
  ACT = 0.0
  ACVSQ = 0.0
  ACESQ = 0.0
  ACPSQ = 0.0
  ACTSQ = 0.0
  FLV = 0.0
  FLE = 0.0
  FLP = 0.0
  FLT = 0.0

  SR3 = ( SIGMA / RCUT ) ** 3
  SR9 = SR3 ** 3
  BOXCUB = 1.0/BOX** 3 !INVERSO DEL VOLUMEN DE LA CELDA
  VLRC = ( 8.0 /9.0 ) * PI * DENS * REAL ( N )* ( SR9 - 3.0 * SR3) !CORRECION LARGO ALCANCE ENERGIA
  WLRC = ( 16.0 / 9.0 ) * PI * DENS * REAL ( N )* ( 2.0 * SR9 -3.0 * SR3 )!CORRECION LARGO ALCANCE TERMINO VIRIAL PARA LA PRESION

  CALL FORCE (SIGMA, RCUT, BOX, V, W )!CALCULO DE FUERZAS
  WRITE(*,*)'    STEP   ','   EN-MEC   ','   EN-CIN   ','   EN-POT   ','    PRES   ','     TEMP   '

  !CALCULANDO LAS CONFIG
  DO ISTEP = 1, NSTEP
     CALL MOVEA ( DT, XM ) !IMPLEMETA PARTE 1 USA CONDICIONES DE IMAGEN MIN
     CALL FORCE ( SIGMA, RCUT, BOX, V, W )
     CALL MOVEB ( DT, XM, XK )!SEGUNDA ETAPA DEL ALGORITMO

     V = V + VLRC !AGRAGANDO LARGO ALCANCE ENERGIA POTENCIAL
     W = (W + WLRC)*BOXCUB !PRESION (DEL VIRIAL)
     E = XK + V !ENERGIA MECANICA

     VN = V / REAL ( N ) !ENEGIA POT POR PARTICULA
     XKN = XK / REAL ( N ) !ENERGIA CINETICA PORPARTICULA
     EN = E / REAL ( N ) !ENERGIA TOTAL POR PARTICULA
     TEMP = 2.0 * XKN / FREE !TEMPERATURA
     PRES = DENS * TEMP + W !PRESION

     ! COMENTARIO LYR: TERMOSTATO PARA MANTENER LA TEMPERATURA DEL
     ! SISTEMA CONSISTENTE CON LA TEMPERATURA DEL BANO TERMICO PARA
     ! UNA DESCRIPCION NVT.
     ALFA=SQRT(TEMPI/TEMP)

     Do IS= 1, N , 1

        VX(IS)=ALFA*VX(IS)
        VY(IS)=ALFA*VY(IS)
        VZ(IS)=ALFA*VZ(IS)

     End Do

     ! CONCLUYE COMDENTARIO LYR.

     !MONITOREO EN PANTALLA
     IF ( MOD( ISTEP, IPRINT ) .EQ. 0 ) THEN

        WRITE(*,'(1X,I8,6(2X,F10.4))') ISTEP, EN, XKN, VN, PRES, TEMP

     End If
     !GUARDAR CONFIG FINAL Y VELOCIDADES FINAL
     IF(ISTEP.EQ.NSTEP)THEN

        DO JFIN=1,N

           WRITE(15,*)RX(JFIN), RY(JFIN), RZ(JFIN)

        End DO

        DO JFIN=1,N

           WRITE(12,*)VX(JFIN), VY(JFIN), VZ(JFIN)

        End DO

     ENDIF

     !SALIDA A ARCHIVO DE TERMALIZACION
     WRITE(14,*)ISTEP,EN,XKN,VN,PRES,TEMP

     xmod=mod(ISTEP,nfrec)
     if(xmod.eq.0.0 .and.ISTEP.GT.NENER)then
        if(ISTEP.LE.NSTEP)then
           ki2=ki2+1 !CONTADOR DE CONFIGURACIONES

           ACE = ACE + EN
           ACK = ACK + XKN
           ACV = ACV + VN
           ACP = ACP + PRES

           ACESQ = ACESQ + EN ** 2
           ACKSQ = ACKSQ + XKN ** 2
           ACVSQ = ACVSQ + VN ** 2
           ACPSQ = ACPSQ + PRES ** 2

           !GUARDAR CONFIGURACIONES DE EQUILIBRIO
           do i=1,n
              CX(I,KI2)=RX(I)
              CY(I,KI2)=RY(I)
              CZ(I,KI2)=RZ(I)

              CXR(I,KI2)=RXC(I)
              CYR(I,KI2)=RYC(I)
              CZR(I,KI2)=RZC(I)
           End do
        End If
     End IF
  End DO

  XNORM = REAL ( KI2 )

  AVE = ACE / XNORM
  AVK = ACK / XNORM
  AVV = ACV / XNORM
  AVP = ACP / XNORM
  ACESQ = ( ACESQ / XNORM ) - AVE ** 2
  ACKSQ = ( ACKSQ / XNORM ) - AVK ** 2
  ACVSQ = ( ACVSQ / XNORM ) - AVV ** 2
  ACPSQ = ( ACPSQ / XNORM ) - AVP ** 2

  IF ( ACESQ .GT. 0.0 ) FLE = SQRT ( ACESQ )
  IF ( ACKSQ .GT. 0.0 ) FLK = SQRT ( ACKSQ )
  IF ( ACVSQ .GT. 0.0 ) FLV = SQRT ( ACVSQ )
  IF ( ACPSQ .GT. 0.0 ) FLP = SQRT ( ACPSQ )
  AVT = AVK * 2.0 / FREE
  FLT = FLK * 2.0 / FREE

  WRITE(*,'('' AVE = '',F10.4)') AVE
  WRITE(*,'('' FLE = '',F10.4)') FLE
  WRITE(*,'('' AVV = '',F10.4)') AVV
  WRITE(*,'('' FLV = '',F10.4)') FLV
  WRITE(*,'('' AVP = '',F10.4)') AVP
  WRITE(*,'('' FLP = '',F10.4)') FLP
  WRITE(*,'('' AVT = '',F10.4)') AVT
  WRITE(*,'('' FLT = '',F10.4)') FLT

  !CALCULO DE PROPIEDADES
  CALL GDR(CX,CY,CZ,KI2)
  CALL WDT(CXR,CYR,CZR,KI2,DT,NFREC)

END program DMLJ
!===============================================================================================================
!===============================================================================================================
!===============================================================================================================
SUBROUTINE FORCE (SIGMA, RCUT, BOX, V, W )
  IMPLICIT REAL*8 (a-h,o-z)
  PARAMETER ( N = 250 )
  COMMON /BLOCK1/ RX, RY, RZ, VX, VY, VZ, FX, FY, FZ
  DIMENSION RX(N), RY(N), RZ(N), VX(N), VY(N), VZ(N), FX(N),FY(N), FZ(N)

  BOXINV = 1.0 / BOX !INVERSO DE LA LONGITUD DE LA CELDA
  RCUTSQ = RCUT ** 2 !EL CUADRADO DEL RADIO DE CORTE
  SIGSQ = SIGMA ** 2 !TAMBIEN EL DIAMETRO
  EPS4 = 4.0
  EPS24 = 24.0

  !TODO A CEROS
  Do  I = 1, N
     FX(I) = 0.0
     FY(I) = 0.0
     FZ(I) = 0.0
  End Do

  V = 0.0
  W = 0.0

  DO  I = 1, N - 1

     RXI = RX(I)
     RYI = RY(I)
     RZI = RZ(I)
     FXI = FX(I)
     FYI = FY(I)
     FZI = FZ(I)

     DO  J = I + 1, N
        !CALCULO DE DISTANCIAS CON IMAGEN MINIMA
        RXIJ = RXI - RX(J)
        RYIJ = RYI - RY(J)
        RZIJ = RZI - RZ(J)
        RXIJ = RXIJ - ANINT ( RXIJ * BOXINV ) * BOX
        RYIJ = RYIJ - ANINT ( RYIJ * BOXINV ) * BOX
        RZIJ = RZIJ - ANINT ( RZIJ * BOXINV ) * BOX
        RIJSQ = RXIJ ** 2 + RYIJ ** 2 + RZIJ ** 2

        !CHECANDO INTERACION
        IF ( RIJSQ .LT. RCUTSQ ) THEN

           !POTENCIAL Y INFO PARA EL CALCULO DE FUERZAS
           SR2 = SIGSQ / RIJSQ
           SR6 = SR2 * SR2 * SR2
           SR12 = SR6 ** 2

           VIJ = SR12 - SR6
           V = V + VIJ
           WIJ = VIJ + SR12
           W = W + WIJ
           FIJ = WIJ / RIJSQ
           FXIJ = FIJ * RXIJ
           FYIJ = FIJ * RYIJ
           FZIJ = FIJ * RZIJ

           FXI = FXI + FXIJ
           FYI = FYI + FYIJ
           FZI = FZI + FZIJ
           FX(J) = FX(J) - FXIJ
           FY(J) = FY(J) - FYIJ
           FZ(J) = FZ(J) - FZIJ
        End If
     End Do
     FX(I) = FXI
     FY(I) = FYI
     FZ(I) = FZI
  End Do

  Do I = 1, N
     FX(I) = FX(I) * EPS24
     FY(I) = FY(I) * EPS24
     FZ(I) = FZ(I) * EPS24
  End Do

  V = V * EPS4
  W = W * EPS24 / 3.0

  !RETURN
END SUBROUTINE FORCE
!===============================================================================================================
!===============================================================================================================
!===============================================================================================================
SUBROUTINE MOVEA ( DT, XM )
  IMPLICIT REAL*8 (a-h,o-z)
  PARAMETER ( N = 250 )
  COMMON /BLOCK1/ RX, RY, RZ, VX, VY, VZ, FX, FY, FZ
  COMMON /BLOCK2/ RXC, RYC, RZC
  COMMON /VALORES/ DENS,RCUT,BOX,NSTEP

  DIMENSION RX(N), RY(N), RZ(N), RXC(N), RYC(N), RZC(N)
  DIMENSION VX(N), VY(N), VZ(N), FX(N), FY(N), FZ(N)

  DT2 = DT / 2.0
  DTSQ2 = DT * DT2

  DO I = 1, N

     !PROPIEDADES ESTRUCTURALES
     RX(I) = RX(I) + DT * VX(I) + DTSQ2 * FX(I) / XM
     RY(I) = RY(I) + DT * VY(I) + DTSQ2 * FY(I) / XM
     RZ(I) = RZ(I) + DT * VZ(I) + DTSQ2 * FZ(I) / XM

     !PROPIEDADES DINAMICAS
     RXC(I) = RXC(I) + DT * VX(I) + DTSQ2 * FX(I) / XM
     RYC(I) = RYC(I) + DT * VY(I) + DTSQ2 * FY(I) / XM
     RZC(I) = RZC(I) + DT * VZ(I) + DTSQ2 * FZ(I) / XM

     !IMAGEN MINIMA
     RX(I)=RX(I)-BOX*ANINT(RX(I)/BOX)
     RY(I)=RY(I)-BOX*ANINT(RY(I)/BOX)
     RZ(I)=RZ(I)-BOX*ANINT(RZ(I)/BOX)

     !PRIMERA ETAPA DEL ALGORITMO
     VX(I) = VX(I) + DT2 * FX(I) / XM
     VY(I) = VY(I) + DT2 * FY(I) / XM
     VZ(I) = VZ(I) + DT2 * FZ(I) / XM
  End Do

END SUBROUTINE MOVEA
!===============================================================================================================
!===============================================================================================================
!===============================================================================================================
SUBROUTINE MOVEB ( DT, XM, XK )
  IMPLICIT REAL*8 (a-h,o-z)
  PARAMETER ( N = 250 )
  COMMON /BLOCK1/ RX, RY, RZ, VX, VY, VZ, FX, FY, FZ
  DIMENSION RX(N), RY(N), RZ(N)
  DIMENSION VX(N), VY(N), VZ(N), FX(N), FY(N), FZ(N)

  DT2 = DT / 2.0
  XK = 0.0

  Do I = 1, N

     !SEGUNDA ETAPA DEL ALGORITMO
     VX(I) = VX(I) + DT2 * FX(I) / XM
     VY(I) = VY(I) + DT2 * FY(I) / XM
     VZ(I) = VZ(I) + DT2 * FZ(I) / XM

     XK = XK + VX(I) ** 2 + VY(I) ** 2 + VZ(I) ** 2 !CUADRADO DE LA VELOCIDAD
  End Do

  XK = 0.5 * XM * XK !ENERGIA CINETICA

END SUBROUTINE MOVEB
!===============================================================================================================
!===============================================================================================================
!===============================================================================================================
SUBROUTINE COMVEL ( TEMP )
  IMPLICIT REAL*8 (a-h,o-z)
  PARAMETER ( N = 250 )
  COMMON /BLOCK1/ RX, RY, RZ, VX, VY, VZ, FX, FY, FZ
  COMMON /semillas/iseed3,iseed2,iseed1
  DIMENSION RX(N), RY(N), RZ(N)
  DIMENSION VX(N), VY(N), VZ(N), FX(N), FY(N), FZ(N)

  !SEMILLAS
  ISEED = 43560
  ISEED1= 39467
  ISEED2= 148420
  ISEED3= 7845901

  !NUMERO ALEATORIO GAUSSIANO
  CALL AZARG(iseed,AX)
  CALL AZARG(iseed,AY)
  CALL AZARG(iseed,AZ)

  RTEMP = SQRT ( TEMP ) !RAIZ DE INTERVALO TEMPORAL

  !FIJA VEL ALEATORIAS
  DO I = 1, N
     VX(I) = RTEMP * AX
     VY(I) = RTEMP * AY
     VZ(I) = RTEMP * AZ
  End DO

  SUMX = 0.0
  SUMY = 0.0
  SUMZ = 0.0

  !SUMA DE LAS VELOCIDADES
  DO I = 1, N
     SUMX = SUMX + VX(I)
     SUMY = SUMY + VY(I)
     SUMZ = SUMZ + VZ(I)
  End DO

  !PROMEDIO DE LAS VELOCIDADES
  SUMX = SUMX / REAL ( N )
  SUMY = SUMY / REAL ( N )
  SUMZ = SUMZ / REAL ( N )

  !DESVIACION DE LA MEDIA
  DO  I = 1, N
     VX(I) = VX(I) - SUMX
     VY(I) = VY(I) - SUMY
     VZ(I) = VZ(I) - SUMZ
  End DO

END SUBROUTINE COMVEL
!===============================================================================================================
!===============================================================================================================
!===============================================================================================================
SUBROUTINE CONFIGINI (BOX,RX,RY,RZ)
  IMPLICIT REAL*8 (a-h,o-z)
  PARAMETER ( N = 250 )
  COMMON /BLOCK2/ RXC, RYC, RZC

  DIMENSION RX(N), RY(N), RZ(N), RXC(N), RYC(N), RZC(N)
  DIMENSION X(N), Y(N), Z(N)

  OPEN(11,FILE='cidm0.dat',STATUS='UNKNOWN')

  NP=N !PARTICULAS (NO LO CONSIDERO NECESARIO)
  A=1.0/3.0
  ISEED1=456808
  ISEED2=780
  ISEED3=7598

  DO I=1,NP

     !POSICION ALEATORIA DENTRO DE LA CELDA
2    R=zran(iseed1)-0.5d0
     S=zran(iseed2)-0.5D0
     T=zran(iseed3)-0.5D0

     !GUARDANDO POSICION
     X(I)=R*BOX
     Y(I)=S*BOX
     Z(I)=T*BOX

     !CHECANDO POR TRASLAPES
     DO J=1 , I-1

        xij=X(I)-X(J)
        yij=Y(I)-Y(J)
        zij=Z(I)-Z(J)

        RO=(xij)**2+(yij)**2+(zij)**2

        IF (RO.LE.1.0) THEN

           WRITE(*,*)'traslape',I,J
           GO TO 2

        ENDIF

     End DO
     !GUARDANDO POSICIONES EN LOS BLOQUES
     RX(I)=X(I)
     RY(I)=Y(I)
     RZ(I)=Z(I)
     RXC(I)=X(I)
     RYC(I)=Y(I)
     RZC(I)=Z(I)
     WRITE(11,*)SNGL(RX(I)),SNGL(RY(I)),SNGL(RZ(I))

  End DO

END SUBROUTINE CONFIGINI
!===============================================================================================================
!===============================================================================================================
!===============================================================================================================
SUBROUTINE GDR(CX,CY,CZ,KI)
  IMPLICIT DOUBLE PRECISION(A-H,O-Z)
  PARAMETER ( N = 250 )
  PARAMETER(NN2=5000)
  PARAMETER(NN3=3500)
  INTEGER NHIST(NN3)
  COMMON /VALORES/ DENS,RCUT,BOX,NSTEP
  DIMENSION CX(n,nn2),CY(n,nn2),CZ(n,nn2)

  NP=N

  NHIST=0

  DELTAR=0.01E0 !TAMANO DE LA CINTA
  MAXBIN=INT(RCUT/DELTAR) !NUMERO DE CINTAS
  PI=3.141592
  NTMAX=KI !NUMERO DE CONFIGURACIONES

  DO L=1,NP

     DO M=1,NP

        IF (M /= L) then

        DO J=1,NTMAX

           !CONFIGURACIONES
           XL0=CX(L,J)
           XLT=CX(M,J)
           XL0T=XL0-XLT

           YL0=CY(L,J)
           YLT=CY(M,J)
           YL0T=YL0-YLT

           ZL0=CZ(L,J)
           ZLT=CZ(M,J)
           ZL0T=ZL0-ZLT

           !IMAGEN MINIMA
           XL0T=XL0T-BOX*ANINT(XL0T/BOX)
           YL0T=YL0T-BOX*ANINT(YL0T/BOX)
           ZL0T=ZL0T-BOX*ANINT(ZL0T/BOX)
           R0T=SQRT(XL0T**2+YL0T**2+ZL0T**2)
           NBIN=INT(R0T/DELTAR)+1

           IF (NBIN.LE.MAXBIN)THEN
              NHIST(NBIN)=NHIST(NBIN)+1
           ENDIF

        End Do
     End IF
     End Do
  End DO

  C1=(4.0/3.0)*(PI*DENS) !PRECALCULO PARA G(r)

  OPEN(60,FILE='grdm0.dat',STATUS='UNKNOWN')

  DO NBIN=1,MAXBIN

     RL=REAL(NBIN-1)*DELTAR !CINTA INFERIOR
     RU=RL+DELTAR           !SUPERIOR
     RT=RL+DELTAR/2.0       !MEDIA
     C2=C1*(RU**3-RL**3)
     GDRTA=REAL(NHIST(NBIN))/REAL(NTMAX)/REAL(NP)/C2 !CALCULO DE GDR
     WRITE(60,*)SNGL(RT),SNGL(GDRTA)

  End DO

  CLOSE(60)
  RETURN
END SUBROUTINE GDR

!===============================================================================================================
!===============================================================================================================
!===============================================================================================================

SUBROUTINE WDT(CX,CY,CZ,KI,DT,NFREC)
  PARAMETER (N=250)
  PARAMETER (NN2=5000)
  IMPLICIT REAL*8 (A-H,O-Z)
  DIMENSION CX(N,NN2),CY(N,NN2),CZ(N,NN2)
  COMMON /VALORES/ DENS,RCUT,BOX,NSTEP

  TIM=REAL(NFREC)*DT

  open(50,file='wtdm0.dat',STATUS='UNKNOWN')

  DO I=1, KI-1
     NTMAX=KI-I
     WTX=0.d0
     WTY=0.d0
     WTZ=0.d0
     WT= 0.d0

     DO L=1,N
        DO J=1,NTMAX
           !CONFIGS
           WTX=WTX+( CX(L,I+J)-CX(L,J) )**2
           WTY=WTY+( CY(L,I+J)-CY(L,J) )**2
           WTZ=WTZ+( CZ(L,I+J)-CZ(L,J) )**2

        END DO
     END DO
     !CALCULO PROP DINAMICAS
     TIME=TIM*REAL(I)
     WT=(WTX+WTY+WTZ)/REAL(NTMAX)/REAL(N)/6.D0
     DIF=WT/TIME
     WRITE(50,*)TIME,WT,DIF
     if(time.gt.10)goto 11

  END DO
11 CLOSE(50)

END SUBROUTINE WDT
!===============================================================================================================
!===============================================================================================================
!===============================================================================================================

! GENERADOR DE NUMEROS ALEATORIOS CON DISTRIBUCION UNIFORME
! computers in physics
! vol. 8, No. 1 (1994) pag.117

FUNCTION ZRAN(ISEED)
  implicit real*8 (a-h,o-z)
  common/semillas/iseed3,iseed2,iseed1
  mzran=iseed3-iseed1

  if(mzran.lt.0) mzran=mzran+2147483579
  iseed3=iseed2
  iseed2=iseed1
  iseed1=mzran
  iseed=ishft(3533*ishft(iseed,-16)+iand(iseed,125535),16)+3533*iand(iseed,65535)
  mzran=mzran+iseed
  zran=.5+.2328306d-9*mzran

  RETURN
END FUNCTION ZRAN
!===============================================================================================================
!===============================================================================================================
!===============================================================================================================
! GENERADOR DE NUMEROS ALEATORIOS CON DISTRIBUCION GAUSSIANA
SUBROUTINE AZARG( ISEED,X )
  IMPLICIT DOUBLE PRECISION (A-H,O-Z)
  external zran
  common/semillas/iseed3,iseed2,iseed1

  pi=4.0*atan(1.0)
  R=zran(iseed)
  S=zran(iseed)
  X=SQRT(-2.0*LOG(R))*COS(2.0*PI*S)

  RETURN
END SUBROUTINE AZARG


module potential

!use calculus
implicit none
include 'mpif.h'
include 'dmumps_struc.h'

integer, dimension(:), pointer, save :: mumps_perm   !cached permutation, unclear whether save is necessary...

contains

  function elliptic2D(srcterm,SigP,SigH,Vminx2,Vmaxx2,Vminx3,Vmaxx3,dx2,dx2i,dx3,dx3i,perflag)

    !------------------------------------------------------------
    !-------SOLVE POISSONS'S EQUATION IN 2D USING MUMPS
    !------------------------------------------------------------

    real(wp), dimension(:,:), intent(in) :: srcterm,SigP,SigH
    real(wp), dimension(:), intent(in) :: Vminx2,Vmaxx2
    real(wp), dimension(:), intent(in) :: Vminx3,Vmaxx3
    real(wp), dimension(0:), intent(in) :: dx2
    real(wp), dimension(:), intent(in) :: dx2i
    real(wp), dimension(0:), intent(in) :: dx3
    real(wp), dimension(:), intent(in) :: dx3i
    logical, intent(in) :: perflag

    real(wp), dimension(1:size(SigP,1),1:size(SigP,2)) :: SigPh2
    real(wp), dimension(1:size(SigP,1),1:size(SigP,2)) :: SigPh3
    real(wp), dimension(1:size(SigP,1),1:size(SigP,2)) :: gradSigH2,gradSigH3

    integer :: ix2,ix3,lx2,lx3
    integer :: lPhi,lent
    integer :: iPhi,ient
    integer, dimension(:), allocatable :: ir,ic
    real(wp), dimension(:), allocatable :: M
    real(wp), dimension(:), allocatable :: b
    real(wp) :: tstart,tfin
    type (DMUMPS_STRUC) mumps_par
    integer :: myid, ierr

    real(wp), dimension(size(SigP,1),size(SigP,2)) :: elliptic2D

    call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr)

    !ONLY ROOT NEEDS TO ASSEMBLE THE MATRIX
    if (myid==0) then
      lx2=size(SigP,1)
      lx3=size(SigP,2)
      lPhi=lx2*lx3
      lent=5*(lx2-2)*(lx3-2)+2*lx2+2*(lx3-2)
      allocate(ir(lent),ic(lent),M(lent),b(lPhi))

      !PREP INPUT DATA FOR SOLUTION OF SYSTEM
      SigPh2(1,:)=0.0
      SigPh2(2:lx2,:)=0.5*(SigP(1:lx2-1,:)+SigP(2:lx2,:))
      SigPh3(:,1)=0.0
      SigPh3(:,2:lx3)=0.5*(SigP(:,1:lx3-1)+SigP(:,2:lx3))

!      gradSigH2=grad2D1(SigH,dx2(1:lx2))
!      gradSigH3=grad2D1(SigH,dx3(1:lx3))
      gradSigH2=0d0
      gradSigH3=0d0

      !------------------------------------------------------------
      !-------DEFINE A MATRIX USING SPARSE STORAGE (CENTRALIZED
      !-------ASSEMBLED MATRIX INPUT, SEE SECTION 4.5 OF MUMPS USER
      !-------GUIDE).
      !------------------------------------------------------------

      !LOAD UP MATRIX ELEMENTS
      M(:)=0.0
      b=pack(srcterm,.true.)           !boundaries overwritten later
      ient=1
      do ix3=1,lx3
        do ix2=1,lx2
          iPhi=lx2*(ix3-1)+ix2     !linear index referencing Phi(ix2,ix3) as a column vector.  Also row of big matrix

          if (ix2==1) then          !BOTTOM GRID POINTS + CORNER
            ir(ient)=iPhi
            ic(ient)=iPhi
            M(ient)=1.0
            b(iPhi)=Vminx2(ix3)
            ient=ient+1
          elseif (ix2==lx2) then    !TOP GRID POINTS + CORNER
            ir(ient)=iPhi
            ic(ient)=iPhi
            M(ient)=1.0
            b(iPhi)=Vmaxx2(ix3)
            ient=ient+1
          elseif (ix3==1) then      !LEFT BOUNDARY
            ir(ient)=iPhi
            ic(ient)=iPhi
            M(ient)=1.0
            b(iPhi)=Vminx3(ix2)
            ient=ient+1
          elseif (ix3==lx3) then    !RIGHT BOUNDARY
            ir(ient)=iPhi
            ic(ient)=iPhi
            M(ient)=1.0
            b(iPhi)=Vmaxx3(ix2)
            ient=ient+1
          else                      !INTERIOR
            !ix2,ix3-1 grid point in ix2,ix3 equation
            ir(ient)=iPhi
            ic(ient)=iPhi-lx2
            M(ient)=SigPh3(ix2,ix3)/(dx3i(ix3)*dx3(ix3))+gradSigH2(ix2,ix3)/(dx3(ix3)+dx3(ix3+1))
            ient=ient+1

            !ix2-1,ix3 grid point
            ir(ient)=iPhi
            ic(ient)=iPhi-1
            M(ient)=SigPh2(ix2,ix3)/(dx2i(ix2)*dx2(ix2))-gradSigH3(ix2,ix3)/(dx2(ix2)+dx2(ix2+1))
            ient=ient+1

            !ix2,ix3 grid point
            ir(ient)=iPhi
            ic(ient)=iPhi
            M(ient)=-1.0*SigPh2(ix2+1,ix3)/(dx2i(ix2)*dx2(ix2+1)) &
                    -1.0*SigPh2(ix2,ix3)/(dx2i(ix2)*dx2(ix2)) &
                    -1.0*SigPh3(ix2,ix3+1)/(dx3i(ix3)*dx3(ix3+1)) &
                    -1.0*SigPh3(ix2,ix3)/(dx3i(ix3)*dx3(ix3))
            ient=ient+1

            !ix2+1,ix3 grid point
            ir(ient)=iPhi
            ic(ient)=iPhi+1
            M(ient)=SigPh2(ix2+1,ix3)/(dx2i(ix2)*dx2(ix2+1))+gradSigH3(ix2,ix3)/(dx2(ix2)+dx2(ix2+1))
            ient=ient+1

            !ix2,ix3+1 grid point
            ir(ient)=iPhi
            ic(ient)=iPhi+lx2
            M(ient)=SigPh3(ix2,ix3+1)/(dx3i(ix3)*dx3(ix3+1))-gradSigH2(ix2,ix3)/(dx3(ix3)+dx3(ix3+1))
            ient=ient+1
          end if
        end do
      end do
    end if

    !FIRE UP MUMPS
    mumps_par%COMM = MPI_COMM_WORLD
    mumps_par%JOB = -1
    mumps_par%SYM = 0
    mumps_par%PAR = 1
    call DMUMPS(mumps_par)

    !LOAD OUR PROBLEM
    if ( mumps_par%MYID==0 ) then
      mumps_par%N=lPhi
      mumps_par%NZ=lent
      allocate( mumps_par%IRN ( mumps_par%NZ ) )
      allocate( mumps_par%JCN ( mumps_par%NZ ) )
      allocate( mumps_par%A( mumps_par%NZ ) )
      allocate( mumps_par%RHS ( mumps_par%N  ) )
      mumps_par%IRN=ir
      mumps_par%JCN=ic
      mumps_par%A=M
      mumps_par%RHS=b
      deallocate(ir,ic,M,b)     !clear memory before solve begins!!!

      if (perflag) then       !used cached permutation
        allocate(mumps_par%PERM_IN(mumps_par%N))
        mumps_par%PERM_IN=mumps_perm
        mumps_par%ICNTL(7)=1
      end if
    end if

    !SOLVE (ALL WORKERS NEED TO SEE THIS CALL)
    mumps_par%JOB = 6
    call DMUMPS(mumps_par)

    !STORE PERMUTATION USED, SAVE RESULTS, CLEAN UP MUMPS ARRAYS
    !(can save ~25% execution time and improves scaling with openmpi
    ! ~25% more going from 1-2 processors)
    if ( mumps_par%MYID==0 ) then
      mumps_perm=mumps_par%SYM_PERM
      elliptic2D=reshape(mumps_par%RHS,[lx2,lx3])

      deallocate( mumps_par%IRN )
      deallocate( mumps_par%JCN )
      deallocate( mumps_par%A   )
      deallocate( mumps_par%RHS )
    end if
    mumps_par%JOB = -2
    call DMUMPS(mumps_par)
  end function elliptic2D

  function poisson2D(rho,Vminx1,Vmaxx1,Vminx2,Vmaxx2,dx1,perflag)

    !------------------------------------------------------------
    !-------SOLVE POISSONS'S EQUATION IN 2D USING MUMPS
    !------------------------------------------------------------

    real(wp), dimension(:,:), intent(in) :: rho
    real(wp), dimension(:), intent(in) :: Vminx1,Vmaxx1
    real(wp), dimension(:), intent(in) :: Vminx2,Vmaxx2
    real(wp), intent(in) :: dx1
!    real(wp), dimension(:), intent(in) :: dx2
    logical, intent(in) :: perflag

    integer :: ix1,ix2,lx1,lx2
    integer :: lPhi, lent
    integer :: iPhi,ient
    integer, dimension(:), allocatable :: ir,ic
    real(wp), dimension(:), allocatable :: M
    real(wp), dimension(:), allocatable :: b
    real(wp) :: tstart,tfin
    type (DMUMPS_STRUC) mumps_par
    integer :: myid, ierr
    real(wp), dimension(size(rho,1),size(rho,2)) :: poisson2D

    call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr)

    if (myid==0) then
    !------------------------------------------------------------
    !-------DEFINE A MATRIX USING SPARSE STORAGE (CENTRALIZED
    !-------ASSEMBLED MATRIX INPUT, SEE SECTION 4.5 OF MUMPS USER
    !-------GUIDE).
    !------------------------------------------------------------
    lx1=size(rho,1)
    lx2=size(rho,2)
    lPhi=lx1*lx2
    lent=5*(lx1-2)*(lx2-2)+2*lx1+2*(lx2-2)
    allocate(ir(lent),ic(lent),M(lent),b(lPhi))

    !LOAD UP MATRIX ELEMENTS
    M(:)=0.0
    b=pack(rho,.true.)           !boundaries overwritten later
    ient=1
    do ix2=1,lx2
      do ix1=1,lx1
        iPhi=lx1*(ix2-1)+ix1     !linear index referencing Phi(ix1,ix2) as a column vector.  Also row of big matrix

        if (ix1==1) then          !BOTTOM GRID POINTS + CORNER
          ir(ient)=iPhi
          ic(ient)=iPhi
          M(ient)=1.0
          b(iPhi)=Vminx1(ix2)
          ient=ient+1
        elseif (ix1==lx1) then    !TOP GRID POINTS + CORNER
          ir(ient)=iPhi
          ic(ient)=iPhi
          M(ient)=1.0
          b(iPhi)=Vmaxx1(ix2)
          ient=ient+1
        elseif (ix2==1) then      !LEFT BOUNDARY
          ir(ient)=iPhi
          ic(ient)=iPhi
          M(ient)=1.0
          b(iPhi)=Vminx2(ix1)
          ient=ient+1
        elseif (ix2==lx2) then    !RIGHT BOUNDARY
          ir(ient)=iPhi
          ic(ient)=iPhi
          M(ient)=1.0
          b(iPhi)=Vmaxx2(ix1)
          ient=ient+1
        else                      !INTERIOR
          !ix1,ix2-1 grid point in ix1,ix2 equation
          ir(ient)=iPhi
          ic(ient)=iPhi-lx1
          M(ient)=1.0
          ient=ient+1

          !ix1-1,ix2 grid point
          ir(ient)=iPhi
          ic(ient)=iPhi-1
          M(ient)=1.0
          ient=ient+1

          !ix1,ix2 grid point
          ir(ient)=iPhi
          ic(ient)=iPhi
          M(ient)=-4.0
          ient=ient+1

          !ix1+1,ix2 grid point
          ir(ient)=iPhi
          ic(ient)=iPhi+1
          M(ient)=1.0
          ient=ient+1

          !ix1,ix2+1 grid point
          ir(ient)=iPhi
          ic(ient)=iPhi+lx1
          M(ient)=1.0
          ient=ient+1
        end if
      end do
    end do

    !CORRECT FOR DX /= 1
    b=b*dx1**2
    end if

    !FIRE UP MUMPS
    mumps_par%COMM = MPI_COMM_WORLD
    mumps_par%JOB = -1
    mumps_par%SYM = 0
    mumps_par%PAR = 1
    call DMUMPS(mumps_par)

    !LOAD OUR PROBLEM
    if ( mumps_par%MYID .eq. 0 ) then
      mumps_par%N=lPhi
      mumps_par%NZ=lent
      allocate( mumps_par%IRN ( mumps_par%NZ ) )
      allocate( mumps_par%JCN ( mumps_par%NZ ) )
      allocate( mumps_par%A( mumps_par%NZ ) )
      allocate( mumps_par%RHS ( mumps_par%N  ) )
      mumps_par%IRN=ir
      mumps_par%JCN=ic
      mumps_par%A=M
      mumps_par%RHS=b
      deallocate(ir,ic,M,b)     !clear memory before solve begins!!!

      if (perflag) then       !used cached permutation
        allocate(mumps_par%PERM_IN(mumps_par%N))
        mumps_par%PERM_IN=mumps_perm
        mumps_par%ICNTL(7)=1
      end if
    end if

    !SOLVE OUR PROBLEM (ALL WORKERS NEED TO SEE THIS CALL)
    mumps_par%JOB = 6
    call cpu_time(tstart)
    call DMUMPS(mumps_par)
    call cpu_time(tfin)
    write(*,*) 'Solve took ',tfin-tstart,' seconds...'

    !STORE PERMUTATION USED, SAVE RESULTS, CLEAN UP MUMPS ARRAYS
    !(can save ~25% execution time and improves scaling with openmpi
    ! ~25% more going from 1-2 processors)
    if ( mumps_par%MYID .eq. 0 ) then
      mumps_perm=mumps_par%SYM_PERM
      poisson2D=reshape(mumps_par%RHS/dx1**2,[lx1,lx2])

      deallocate( mumps_par%IRN )
      deallocate( mumps_par%JCN )
      deallocate( mumps_par%A   )
      deallocate( mumps_par%RHS )
    end if
    mumps_par%JOB = -2
    call DMUMPS(mumps_par)
  end function poisson2D

end module potential


      subroutine Tfactor2(x1,x2,t0,t1,t2)
      implicit double precision(a-h,o-z)

      dimension x1(3),x2(3),r(3),dx(3)
      dimension t1(3),t2(3,3)

      dimension rr02(3),rr03(3),rr05(3),rr07(3),rr09(3),rr011(3)
      dimension rr023(3),rr025(3),rr027(3),rr029(3),rr0211(3)

      dimension pair(3,3)
c x1 and x2 are the coordinates of the two atoms

c r is the vector from x2 to x1
c and r0 is the distance, |r|.

      r0=0.d0
      do k=1,3
      r(k)=x1(k)-x2(k)
      r0=r0+r(k)**2
      enddo

      r02=1.d0/r0
      r0=1.d0/sqrt(r0)
      r03=r0*r02

c  First we equate the t factors, t1, t2, etc to the part ofeach
c that has no delta functions

      do k1=1,3
       rr02(k1)=r(k1)*r02
       rr023(k1)=-3.d0*rr02(k1)
       rr03(k1)=-r(k1)*r03
      enddo

      t0=r0

      do k1=1,3
      t1(k1)=rr03(k1)
      do k2=k1,3
      t2(k1,k2)=t1(k1)*rr023(k2)
      enddo
      enddo

c Now we add on the parts that have one delta function, by using the
c same index for two array indices.

      do k1=1,3
      t2(k1,k1)=t2(k1,k1)-r03
      enddo

      go to 3003

c temp check
      write(6,*)'t0'
      write(6,*)t0
      write(6,*)'t1'
      do k1=1,3
      write(6,1)k1,t1(k1)
      enddo
      write(6,*)'t2'
      do k1=1,3
      do k2=k1,3
       write(6,2)k1,k2,t2(k1,k2)
      enddo
      enddo

1     format(i3,e15.6)
2     format(2i3,e15.6)

3003  continue
      return
      end


      subroutine zbeskg (x1r,x1i, alpha, kode, n, yr,yi, nz,ierr)
c     Author Serge Steer, Copyright INRIA, 2005
c     extends cbesk for the case where alpha is negative
      double precision alpha
      double precision x1r, x1i, yr(n), yi(n)
      integer kode,n,nz,ierr
c
      double precision inf,xr, xi,a1,temp
      double precision dlamch
      inf=dlamch('o')*2.0d0

      xr=x1r
      xi=x1i
      ierr=0
      if (xr.ne.xr.or.xi.ne.xi.or.alpha.ne.alpha) then
c     .  NaN case
         call dset(n,inf-inf,yr,1)
         call dset(n,inf-inf,yi,1)
         ierr=4
      elseif (alpha.ge.0.0d0) then
         call zbesk(xr,xi,abs(alpha),kode,n,yr,yi,nz,ierr)
         if (ierr.eq.2) then
            call dset(n,inf,yr,1)
            call dset(n,inf,yi,1)
         elseif(ierr.ge.4) then
            call dset(n,inf-inf,yr,1)
            call dset(n,inf-inf,yi,1)
         endif
      else
         if(alpha-1.0d0+n.ge.0.0d0) then
c     .     0 is between alpha and alpha+n
            nn=int(-alpha)+1
         else
            nn=n
         endif
         a1=-(alpha-1.0d0+nn)
         call zbesk(xr,xi,a1,kode,nn,yr,yi,nz,ierr)
         if (ierr.eq.0) then
c     .      swap the result to have it in correct order
            if (nn.ge.2) then
               do i=1,nn/2
                  temp=yr(i)
                  yr(i)=yr(nn+1-i)
                  yr(nn+1-i)=temp
                  temp=yi(i)
                  yi(i)=yi(nn+1-i)
                  yi(nn+1-i)=temp
               enddo
            endif
         elseif(ierr.eq.2) then
            call dset(n,inf,yr,1)
            call dset(n,inf,yi,1)
         elseif(ierr.ge.4) then
            call dset(n,inf-inf,yr,1)
            call dset(n,inf-inf,yi,1)
         endif
         if (n.gt.nn) then
            a1=1.0d0-a1
            call zbesk(xr,xi,a1,kode,n-nn,yr(nn+1),yi(nn+1),nz,ier)
            if (ier.eq.2) then
               call dset(n-nn,inf,yr(nn+1),1)
               call dset(n-nn,inf,yi(nn+1),1)
            elseif(ierr.ge.4) then
               call dset(n-nn,inf-inf,yr(nn+1),1)
               call dset(n-nn,inf-inf,yi(nn+1),1)
            endif
            ierr=max(ierr,ier)
         endif
      endif

      end

      subroutine zbeskv (xr,xi,nx,alpha,na, kode,yr,yi,wr,wi,ierr)
c     Author Serge Steer, Copyright INRIA, 2005
c     compute besseli function for x and alpha given by vectors
c     w : working array of size 2*na (used only if nz>0 and alpha contains negative
C         values
      double precision xr(nx),xi(nx),alpha(na),yr(*),yi(*),wr(*),wi(*)
      double precision e,dlamch,eps
      integer kode,nx,na,ier
      eps=dlamch('p')
      ierr=0
      if (na.lt.0) then
c     .  element wise case x and alpha are supposed to have the same size
         do i=1,nx
            call  zbeskg (xr(i), xi(i), alpha(i), kode, 1, yr(i),yi(i),
     $            nz,ier)
            ierr=max(ierr,ier)
         enddo
      elseif (na.eq.1) then
        do i=1,nx
           call  zbeskg (xr(i), xi(i), alpha(1), kode, 1, yr(i),yi(i),
     $          nz,ier)
           ierr=max(ierr,ier)
        enddo
      else
c     .  compute besseli(x(i),y(j)), i=1,nx,j=1,na
         j0=1
 05      n=0
 10      n=n+1
         j=j0+n
         if (j.le.na.and.abs((1.0d0+alpha(j-1))-alpha(j)).le.eps) then
            goto 10
         endif

         do i=1,nx
            call zbeskg (xr(i),xi(i),alpha(j0),kode, n, wr, wi, nz,ier)
            ierr=max(ierr,ier)
            call dcopy(n,wr,1,yr(i+(j0-1)*nx),nx)
            call dcopy(n,wi,1,yi(i+(j0-1)*nx),nx)
         enddo
         j0=j
         if (j0.le.na) goto 05
      endif
      end


C
C**************************  STORMB ************************************
C
C  calculate volume of change in storage
C  between the current time level and the previous time level.
C  DSTORE > 0 for net increase in storage.
C
C***********************************************************************
C
      SUBROUTINE STORMB(PNEW,PTIMEP,SWNEW,SWTIMEP,N,DSTORE,VOLNOD,SNODI,
     1                  PNODI)
C
      IMPLICIT NONE
      INTEGER  K
      INTEGER  N
      REAL*8   DSTORE
      REAL*8   PNEW(*),PTIMEP(*),VOLNOD(*),SNODI(*),PNODI(*)
      REAL*8   SWNEW(*),SWTIMEP(*)
C
      DSTORE=0.0D0
      DO K=1,N
CM       DSTORE=DSTORE + VOLNOD(K)*(ETAI(K) + ETAIP(K))*0.5D0*
CM   1                             (PNEW(K) - PTIMEP(K))
         DSTORE=DSTORE + VOLNOD(K)*(SNODI(K)*(SWNEW(K)+SWTIMEP(K))
     1       *0.5*(PNEW(K)-PTIMEP(K))+PNODI(K)*(SWNEW(K)-SWTIMEP(K)))
      END DO
C
      RETURN
      END


      SUBROUTINE W3FB10(DLAT1, DLON1, DLAT2, DLON2, BEARD, GCDKM)
C$$$  SUBPROGRAM DOCUMENTATION BLOCK
C                .      .    .                                       .
C SUBPROGRAM:  W3FB10        LAT/LONG PAIR TO COMPASS BEARING, GCD
C   PRGMMR: CHASE            ORG: NMC421      DATE:88-10-26
C
C ABSTRACT: GIVEN A PAIR OF POINTS (1) AND (2) GIVEN BY LATITUDE AND
C   LONGITUDE, W3FB10 COMPUTES THE BEARING AND GREAT CIRCLE DISTANCE
C   FROM POINT (1) TO POINT (2) ASSUMING A SPHERICAL EARTH.  THE
C   NORTH AND SOUTH POLES ARE SPECIAL CASES.  IF LATITUDE OF POINT
C   (1) IS WITHIN 1E-10 DEGREES OF THE NORTH POLE, BEARING IS THE
C   NEGATIVE LONGITUDE OF POINT (2) BY CONVENTION.  IF LATITUDE OF
C   POINT (1) IS WITHIN 1E-10 DEGREES OF THE SOUTH POLE, BEARING IS
C   THE LONGITUDE OF POINT (2) BY CONVENTION.  IF POINT (2) IS WITHIN
C   1E-6 RADIANS OF THE ANTIPODE OF POINT (1), THE BEARING WILL BE
C   SET TO ZERO.  IF POINT (1) AND POINT (2) ARE WITHIN 1E-10 RADIANS
C   OF EACH OTHER, BOTH BEARING AND DISTANCE WILL BE SET TO ZERO.
C
C PROGRAM HISTORY LOG:
C   88-08-29  CHASE, P.
C   88-09-23  CHASE, P.   FIX DUMB SOUTH POLE ERROR
C   88-10-05  CHASE, P.   FIX BEARING AMBIGUITY
C   90-04-12  R.E.JONES   CONVERT TO CFT77 FORTRAN
C
C USAGE:    CALL W3FB10(DLAT1, DLON1, DLAT2, DLON2, BEARD, GCDKM)
C
C   INPUT ARGUMENT LIST:
C     DLAT1    - REAL  LATITUDE OF POINT (1) IN DEGREES NORTH.
C     DLON1    - REAL  LONGITUDE OF POINT (1) IN DEGREES EAST.
C     DLAT2    - REAL  LATITUDE OF POINT (2) IN DEGREES NORTH.
C     DLON2    - REAL  LONGITUDE OF POINT (2) IN DEGREES EAST.
C
C   OUTPUT ARGUMENT LIST:
C     BEARD    - REAL  BEARING OF POINT (2) FROM POINT (1) IN
C                COMPASS DEGREES WITH NORTH = 0.0, VALUES FROM
C                -180.0 TO +180.0 DEGREES.
C     GCDKM    - REAL  GREAT CIRCLE DISTANCE FROM POINT (1) TO
C                POINT (2) IN KILOMETERS.
C
C   SUBPROGRAMS CALLED: NONE
C
C REMARKS:  ACCORDING TO THE NMC HANDBOOK, THE EARTH'S RADIUS IS
C   6371.2 KILOMETERS.  THIS IS WHAT WE USE, EVEN THOUGH THE VALUE
C   RECOMMENDED BY THE SMITHSONIAN METEOROLOGICAL HANDBOOK IS
C   6371.221 KM.  (I WOULDN'T WANT YOU TO THINK THAT I DIDN'T KNOW
C   WHAT THE CORRECT VALUE WAS.)
C   METHOD:  THE POLES ARE SPECIAL CASES, AND HANDLED SEPARATELY.
C   OTHERWISE, FROM SPHERICAL TRIGONOMETRY, THE LAW OF COSINES IS USED
C   TO CALCULATE THE THIRD SIDE OF THE SPHERICAL TRIANGLE HAVING
C   SIDES FROM THE POLE TO POINTS (1) AND (2) (THE COLATITUDES).
C   THEN THE LAW OF SINES IS USED TO CALCULATE THE ANGLE AT POINT
C   (1).  A TEST IS APPLIED TO SEE WHETHER THE ARCSINE RESULT MAY BE
C   BE USED AS SUCH, GIVING AN ACUTE ANGLE AS THE BEARING, OR WHETHER
C   THE ARCSINE RESULT SHOULD BE SUBTRACTED FROM PI, GIVING AN OBTUSE
C   ANGLE AS THE BEARING.  THIS TEST IS DERIVED BY CONSTRUCTING A
C   RIGHT SPHERICAL TRIANGLE USING THE POLE, POINT (2), AND THE
C   MERIDIAN THROUGH POINT(1).  THE LATITUDE OF THE RIGHT-ANGLED
C   VERTEX THEN PROVIDES A TEST--IF LATITUDE (1) IS GREATER THAN THIS
C   LATITUDE, THE BEARING ANGLE MUST BE OBTUSE, OTHERWISE ACUTE.
C         IF THE TWO POINTS ARE WITHIN 1E-6 RADIANS OF EACH OTHER
C   A FLAT EARTH IS ASSUMED, AND THE FOUR-QUADRANT ARCTANGENT
C   FUNCTION IS USED TO FIND THE BEARING.  THE Y-DISPLACEMENT IS
C   THE DIFFERENCE IN LATITUDE AND THE X-DISPLACEMENT IS THE
C   DIFFERENCE IN LONGITUDE TIMES COSINE LATITUDE, BOTH IN RADIANS.
C   DISTANCE IS THEN THE DIAGONAL.
C         FUNDAMENTAL TRIGONOMETRIC IDENTITIES ARE USED FREELY, SUCH
C   AS THAT COS(X) = SIN(PI/2 - X), ETC.  SEE ALMOST ANY MATHEMATICAL
C   HANDBOOK, SUCH AS THE C.R.C. STANDARD MATH TABLES UNDER 'RELATIONS
C   IN ANY SPHERICAL TRIANGLE', OR THE NATIONAL BUREAU OF STANDARDS
C   'HANDBOOK OF MATHEMATICAL FUNCTIONS' UNDER SECTION 4.3.149,
C   FORMULAS FOR SOLUTION OF SPHERICAL TRIANGLES.
C         DOUBLE PRECISION IS USED INTERNALLY BECAUSE OF THE WIDE
C   RANGE OF GEOGRAPHIC VALUES THAT MAY BE USED.
C
C ATTRIBUTES:
C   LANGUAGE: CRAY CFT77 FORTRAN
C   MACHINE:  CRAY Y-MP8/832
C
C$$$
C
C *** IMPLICIT TYPE DEFAULTS.....
C
      IMPLICIT REAL  (A-H,O-Z)
C
C *** CONSTANTS......
C
      REAL   PI
      REAL   HALFPI
      REAL   DR
      REAL   RD
      REAL   TDEG, TRAD, TPOD, TFLT
      REAL   EARTHR
      REAL   WHOLCD, HALFCD, QUARCD
C
C *** VARIABLES......
C
      REAL   RLAT1,  RLAT2,  COSLA1, COSLA2, SINLA1, SINLA2
      REAL   DLOND,  RLOND,  COSLO,  SINLO,  SANGG,  ABEAR
      REAL   YDISP,  XDISP,  DDLAT1, DDLAT2, DBANG
      REAL   DLAT1,  DLAT2,  DLON1,  DLON2,  BEARD,  GCDKM
C
C *** CONVERT LATITUDES AND LONGITUDE DIFFERENCE TO RADIANS.
C
      DATA   PI    /3.141592653589793238462643/
      DATA   HALFPI/1.570796326794896619231322/
      DATA   DR    /0.017453292519943295769237/
      DATA   RD    /57.295779513082320876798155/
      DATA   TDEG  /1E-10/, TRAD/1E-10/, TPOD/1E-6/, TFLT/1E-6/
      DATA   EARTHR/6371.2/
      DATA   WHOLCD/360.0/, HALFCD/180.0/, QUARCD/90.0/

      DDLAT1 = DLAT1
      DDLAT2 = DLAT2
      RLAT1  = DR    * DDLAT1
      RLAT2  = DR    * DDLAT2
      DLOND  = DLON2 - DLON1
      IF (DLOND .GT. HALFCD)  DLOND = DLOND - WHOLCD
      IF (DLOND .LT. -HALFCD) DLOND = DLOND + WHOLCD
      RLOND = DR * DLOND
C
C *** FIRST WE ATTACK THE CASES WHERE POINT 1 IS VERY CLOSE TO THE
C *** NORTH OR SOUTH POLES.
C *** HERE WE USE CONVENTIONAL VALUE FOR BEARING.. - LONG (2) AT THE
C *** NORTH POLE, AND + LONG (2) AT THE SOUTH POLE.
C
      IF (ABS(DDLAT1-QUARCD) .LT. TDEG) THEN
        IF (ABS(DDLAT2-QUARCD) .LT. TDEG) THEN
          DBANG = 0.0
          SANGG = 0.0
        ELSE IF (ABS(DDLAT2+QUARCD) .LT. TDEG) THEN
          DBANG = 0.0
          SANGG = PI
        ELSE
          DBANG = -DLON2
          SANGG = HALFPI - RLAT2
        ENDIF
      ELSE IF (ABS(DDLAT1+QUARCD) .LT. TDEG) THEN
        IF (ABS(DDLAT2-QUARCD) .LT. TDEG) THEN
          DBANG = 0.0
          SANGG = PI
        ELSE IF (ABS(DDLAT2+QUARCD) .LT. TDEG) THEN
          DBANG = 0.0
          SANGG = 0.0
        ELSE
          DBANG = +DLON2
          SANGG = HALFPI + RLAT2
        ENDIF
C
C *** NEXT WE ATTACK THE CASES WHERE POINT 2 IS VERY CLOSE TO THE
C *** NORTH OR SOUTH POLES.
C *** HERE BEARING IS SIMPLY 0 OR 180 DEGREES.
C
      ELSE IF (ABS(DDLAT2-QUARCD) .LT. TDEG) THEN
        DBANG = 0.0
        SANGG = HALFPI - RLAT1
      ELSE IF (ABS(DDLAT2+QUARCD) .LT. TDEG) THEN
        DBANG = HALFCD
        SANGG = HALFPI + RLAT1
C
C *** THE CASE REMAINS THAT NEITHER POINT IS AT EITHER POLE.
C *** FIND COSINE AND SINE OF LATITUDES AND LONGITUDE DIFFERENCE
C *** SINCE THEY ARE USED IN MORE THAN ONE FORMULA.
C
      ELSE
        COSLA1 = COS(RLAT1)
        SINLA1 = SIN(RLAT1)
        COSLA2 = COS(RLAT2)
        SINLA2 = SIN(RLAT2)
        COSLO  = COS(RLOND)
        SINLO  = SIN(RLOND)
C
C *** FOLLOWING IS FORMULA FOR GREAT CIRCLE SUBTENDED ANGLE BETWEEN
C *** POINTS IN RADIAN MEASURE.
C
        SANGG = ACOS(SINLA1*SINLA2 + COSLA1*COSLA2*COSLO)
C
C *** IF THE GREAT CIRCLE SUBTENDED ANGLE IS VERY SMALL, FORCE BOTH
C *** BEARING AND DISTANCE TO BE ZERO.
C
        IF (ABS(SANGG) .LT. TRAD) THEN
          DBANG = 0.0
          SANGG = 0.0
C
C *** IF THE GREAT CIRCLE SUBTENDED ANGLE IS JUST SMALL, ASSUME A
C *** FLAT EARTH AND CALCULATE Y- AND X-DISPLACEMENTS.  THEN FIND
C *** BEARING USING THE ARCTANGENT FUNCTION AND DISTANCE USING THE
C *** SQUARE ROOT.
C
        ELSE IF (ABS(SANGG) .LT. TFLT) THEN
          YDISP = RLAT2-RLAT1
          XDISP = RLOND*COSLA2
          ABEAR = ATAN2(XDISP, YDISP)
          DBANG = RD*ABEAR
          SANGG = SQRT(YDISP**2 + XDISP**2)
C
C *** IF THE ANGLE IS RATHER CLOSE TO PI RADIANS, FORCE BEARING TO
C *** BE ZERO AND DISTANCE TO BE PI.
C *** THE TEST FOR 'CLOSE TO PI' IS MORE RELAXED THAN THE TEST FOR
C *** 'CLOSE TO ZERO' TO ALLOW FOR GREATER RELATIVE ERROR.
C
        ELSE IF (ABS(SANGG-PI) .LT. TPOD) THEN
          DBANG = 0.0
          SANGG = PI
C
C *** OTHERWISE COMPUTE THE PRINCIPAL VALUE OF THE BEARING ANGLE
C *** USING THE LAW OF SINES.  THE DIVISION BY THE SINE FORCES US TO
C *** LIMIT THE DOMAIN OF THE ARCSINE TO (-1,1).
C
        ELSE
          ABEAR = ASIN(AMAX1(-1.0,AMIN1(+1.0,COSLA2*SINLO/
     &            SIN(SANGG))))
C
C *** IF THE LONGITUDE DIFFERENCE IS LESS THAN PI/2 IT IS NECESSARY
C *** TO CHECK WHETHER THE BEARING ANGLE IS ACUTE OR OBTUSE BY
C *** COMPARING LATITUDE (1) WITH THE LATITUDE OF THE GREAT CIRCLE
C *** THROUGH POINT (2) NORMAL TO MERIDIAN OF LONGITUDE (1).  IF
C *** LATITUDE (1) IS GREATER, BEARING IS OBTUSE AND THE ACTUAL
C *** BEARING ANGLE IS THE SUPPLEMENT OF THE ANGLE CALCULATED ABOVE.
C
          IF (0.0 .LE. COSLA1*SINLA2 .AND. COSLA1*SINLA2 .LE.
     &    COSLA2*SINLA1*COSLO .OR. COSLA1*SINLA2 .LE. 0.0 .AND.
     &    COSLA2*SINLA1*COSLO .GE. COSLA1*SINLA2) ABEAR =
     &    SIGN(PI,ABEAR) - ABEAR
          DBANG = RD * ABEAR
        ENDIF
      ENDIF
C
C *** THIS FINISHES THE CASE WHERE POINTS ARE NOT AT THE POLES.
C *** NOW CONVERT BEARING TO DEGREES IN RANGE -180 TO +180 AND FIND
C *** GREAT CIRCLE DISTANCE IN KILOMETERS.
C
      IF (DBANG .LE. -HALFCD) DBANG = DBANG + WHOLCD
      IF (DBANG .GT. HALFCD)  DBANG = DBANG - WHOLCD
      GCDKM = EARTHR * SANGG
      BEARD = DBANG
      RETURN
      END