! copyright 2005,  J. E. Akin, all rights reserved.
! file: testing_lib.f

SUBROUTINE DUMMY
! dummy  file
END SUBROUTINE DUMMY

SUBROUTINE FORM_FACING_ELEMENTS (N_ELEMS, NOD_PER_EL, MAX_NP,  &
                                 L_FIRST, L_LAST, NODES, NEEDS,&
                                 MAX_FACES, L_TO_L_NEIGH)
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *      
! BUILD LIST OF FACING ELEMENTS ADJACENT TO OTHER ELEMENTS        
! (THE LIST IS UNORDERED. ZEROS DENOTE SURFACE LOCATIONS.)
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *     
! Draft 2, revised and tested June 11, 1999, JEA
 IMPLICIT NONE
 INTEGER, INTENT(IN)  :: N_ELEMS, NOD_PER_EL, MAX_NP
 INTEGER, INTENT(IN)  :: NEEDS, MAX_FACES
 INTEGER, INTENT(IN)  :: L_FIRST (MAX_NP), L_LAST (MAX_NP)
 INTEGER, INTENT(IN)  :: NODES (N_ELEMS, NOD_PER_EL)
 INTEGER, INTENT(OUT) :: L_TO_L_NEIGH (MAX_FACES, N_ELEMS)

 INTEGER :: ELEM_NODES (NOD_PER_EL)
 INTEGER :: NEIG_NODES (NOD_PER_EL)
 INTEGER :: IE, IN, L_TEST, L_START, L_STOP, N_TEST
 INTEGER :: FOUND, KOUNT, NEED, NEXT, WHERE

! ELEM_NODES     = INCIDENCES ARRAY FOR A SINGLE ELEMENT     
! KOUNT          = NUMBER OF COMMON NODES
! L_FIRST (I)    = ELEMENT WHERE NODE I FIRST APPEARS              
! L_LAST (I)     = ELEMENT WHERE NODE I LAST APPEARS             
! L_TO_L_NEIGH (I) = NUMBER OF ELEM NEIGHBORS OF ELEMENT I
! MAX_FACES      = MAX NUMBER OF FACING ELEMENTS
! MAX_NP         = TOTAL NUMBER OF NODES                          
! NEEDS          = NUMBER OF COMMON NODES TO BE A NEIGHBOR
! N_ELEMS        = TOTAL NUMBER OF ELEMENTS
! NOD_PER_EL     = NUMBER OF NODES PER ELEMENT               
! NODES          = SYSTEM ARRAY OF INCIDENCES OF ALL ELEMENTS      
! WHERE          = LOCATION TO INSERT NEIGHBOR, <= MAX_FACES

  NEED       = MAX (1, NEEDS)   ! INITIALIZE
  L_TO_L_NEIGH = 0

  MAIN : DO IE = 1, N_ELEMS      ! LOOP OVER ALL ELEMENTS 
    FOUND = COUNT ( L_TO_L_NEIGH (:, IE) /= 0 )

!        EXTRACT INCIDENCES LIST FOR ELEMENT IE   
    CALL ELEMENT_NODES (IE, NOD_PER_EL, NODES, ELEM_NODES) 

!        ESTABLISH RANGE OF POSSIBLE ELEMENT NEIGHBORS    
    L_START = N_ELEMS+1 ; L_STOP = 0 
    DO IN = 1, NOD_PER_EL 
      L_START = MIN (L_START, L_FIRST (ELEM_NODES (IN)) )
      L_STOP  = MAX (L_STOP,  L_LAST  (ELEM_NODES (IN)) )
    END DO 
    L_START = MAX (L_START, (IE+1))

!        LOOP OVER POSSIBLE ELEMENT NEIGHBORS            
    IF ( L_START <= L_STOP) THEN 
      RANGE : DO L_TEST = L_START, L_STOP 
        KOUNT = 0  ! NO COMMON NODES

!      EXTRACT NODES OF L_TEST
        CALL ELEMENT_NODES (L_TEST, NOD_PER_EL, NODES, NEIG_NODES) 

!      LOOP OVER INCIDENCES OF POSSIBLE ELEMENT NEIGHBOR        
        LOCAL : DO IN = 1, NOD_PER_EL 
          N_TEST = NEIG_NODES (IN) 
          IF ( N_TEST <  1 ) CYCLE LOCAL ! to a real node
          IF ( L_FIRST (N_TEST)  > IE ) CYCLE LOCAL ! to next node
          IF ( L_LAST  (N_TEST)  < IE ) CYCLE LOCAL ! to next node

!            COMPARE WITH INCIDENCES OF ELEMENT IE             
            IF ( ANY ( ELEM_NODES == N_TEST ) ) THEN
              KOUNT = KOUNT + 1         ! SHARED NODE COUNT
              IF ( KOUNT == NEED ) THEN ! NEIGHBOR PAIR FOUND
                FOUND = FOUND + 1       ! INSERT THE PAIR

!               NOTE: THIS INSERT IS NOT ORDERED. IF A SPECIFIC FACE
!                     ORDER IS REQUIRED DETERMINE WHERE HERE

                WHERE = FOUND ! OR ORDER THE CURRENT FACE
                L_TO_L_NEIGH (WHERE, IE) = L_TEST     ! 1 OF 2

                NEXT  = COUNT ( L_TO_L_NEIGH(:, L_TEST) /= 0 )
                WHERE = NEXT+1 ! OR ORDER THE NEIGHBOR FACE
                L_TO_L_NEIGH (WHERE, L_TEST) = IE    ! 2 OF 2
                IF ( MAX_FACES == FOUND ) CYCLE MAIN  ! IE DONE
                CYCLE RANGE ! this L_TEST element search loop
              END IF ! NUMBER NEEDED
            END IF
        END DO LOCAL ! over in
      END DO RANGE ! over candidate element L_TEST
    END IF ! a possible candidate

!    LOCATE MISSING SURFACE NUMBERS, IF ANY, FOR IE
    IF ( ANY ( L_TO_L_NEIGH (:, IE) == 0 ) ) THEN
!     DO SURFACE SEARCH HERE
    END IF ! SURFACE SEARCH

!   NOW ANY ( L_TO_L_NEIGH (:, IE) == 0 ) NEEDS A NO NEIGHBORS OR 
!       MISSING SURFACE WARNING MESSAGE FOR ELEMENT IE
!       (IFF ALL ELEMENTS HAVE THE SAME MAX_FACES.)
  END DO MAIN ! over all elements
END SUBROUTINE FORM_FACING_ELEMENTS                        

SUBROUTINE FORM_FACING_OR_SURFS (N_ELEMS, NOD_PER_EL, MAX_NP,     &
                                 L_FIRST, L_LAST, NODES, NEEDS,   &
                                 MAX_FACES, L_TO_L_NEIGH, N_SURFS,&
                                 NOD_PER_SURF, NODES_SURF)
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *      
!       COUNT NUMBER OF FACING ELEMENTS OR SURFACES
!              ADJACENT TO OTHER ELEMENTS        
!  (INSERT NEGATIVE SURFACE CODES FIRST INTO L_TO_L_NEIGH)
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *     
 IMPLICIT NONE
 INTEGER, INTENT(IN)  :: N_ELEMS, NOD_PER_EL, MAX_NP
 INTEGER, INTENT(IN)  :: N_SURFS, NOD_PER_SURF 
 INTEGER, INTENT(IN)  :: NEEDS, MAX_FACES
 INTEGER, INTENT(IN)  :: L_FIRST (MAX_NP), L_LAST (MAX_NP)
 INTEGER, INTENT(IN)  :: NODES      (N_ELEMS, NOD_PER_EL)
 INTEGER, INTENT(IN)  :: NODES_SURF (N_SURFS, NOD_PER_SURF+1)
 INTEGER, INTENT(OUT) :: L_TO_L_NEIGH (MAX_FACES, N_ELEMS)

 INTEGER :: ELEM_NODES (NOD_PER_EL)
 INTEGER :: NEIG_NODES (NOD_PER_EL)
 INTEGER :: SURF_NODES (NOD_PER_SURF)
 INTEGER :: FOUND, IE, IN, L_TEST, L_START, L_STOP, N_TEST
 INTEGER :: NEED, NEXT, KOUNT

! ELEM_NODES     = INCIDENCES ARRAY FOR A SINGLE ELEMENT     
! KOUNT          = NUMBER OF COMMON NODES
! L_FIRST (I)    = ELEMENT WHERE NODE I FIRST APPEARS              
! L_LAST (I)     = ELEMENT WHERE NODE I LAST APPEARS             
! L_TO_L_NEIGH (I) = NUMBER OF ELEM NEIGHBORS OF ELEMENT I
! MAX_FACES      = MAX NUMBER OF FACING ELEMENTS
! MAX_NP         = TOTAL NUMBER OF NODES                          
! NEEDS          = NUMBER OF COMMON NODES TO BE A NEIGHBOR
! NEIG_NODES     = INCIDENCES ARRAY FOR A POSSIBLE NEIGHBOR
! N_ELEMS        = TOTAL NUMBER OF ELEMENTS
! N_SURFS        = NUMBER OF CODED SURFACES IN SYSTEM
! NOD_PER_EL     = NUMBER OF NODES PER ELEMENT               
! NOD_PER_SURF   = NUMBER OF NODES PER SURFACE
! NODES          = SYSTEM ARRAY OF INCIDENCES OF ALL ELEMENTS      
! NODES_SURF     = CODE NUMBER AND INCIDENCES OF ALL SURFACES
! SURF_NODES     = INCIDENCES ARRAY FOR A SINGLE SURFACE     

  NEED       = MAX (1, NEEDS)   ! INITIALIZE
  L_TO_L_NEIGH = 0

  SURF : DO IE = 1, N_SURFS      ! LOOP OVER ALL SURFENTS 

!        EXTRACT INCIDENCES LIST FOR SURFENT IE   
    SURF_NODES = NODES_SURF (IE, 2:(NOD_PER_SURF+1))

!        ESTABLISH RANGE OF POSSIBLE ELEMENT NEIGHBORS    
    L_START = N_ELEMS+1 ; L_STOP = 0 
    DO IN = 1, NOD_PER_EL 
      L_START = MIN (L_START, L_FIRST (SURF_NODES (IN)) )
      L_STOP  = MAX (L_STOP,  L_LAST  (SURF_NODES (IN)) )
    END DO 

!        LOOP OVER POSSIBLE ELEMENT NEIGHBORS            
    IF ( L_START <= L_STOP) THEN 
      CHECK : DO L_TEST = L_START, L_STOP 
        FOUND = COUNT ( L_TO_L_NEIGH (:, L_TEST) /= 0 )
        KOUNT = 0  ! NO COMMON NODES

!      LOOP OVER INCIDENCES OF POSSIBLE ELEMENT NEIGHBOR        
        CALL ELEMENT_NODES (L_TEST, NOD_PER_EL, NODES, NEIG_NODES) 
        L_2_S : DO IN = 1, NOD_PER_EL 
          N_TEST = NEIG_NODES (IN) 
          IF ( N_TEST <  1 ) CYCLE L_2_S ! to a real node
          IF ( L_FIRST (N_TEST)  > IE ) CYCLE L_2_S ! to next node
          IF ( L_LAST  (N_TEST)  < IE ) CYCLE L_2_S ! to next node

!            COMPARE WITH INCIDENCES OF SURFENT IE             
            IF ( ANY ( SURF_NODES == N_TEST ) ) THEN
              KOUNT = KOUNT + 1         ! SHARED NODE COUNT
              IF ( KOUNT == NEED ) THEN ! SURF ON ELEM FOUND
                FOUND = FOUND + 1       ! INSERT THE SURF
                !b L_TO_L_NEIGH (FOUND, IE) = L_TEST     
                L_TO_L_NEIGH (FOUND, L_TEST) = NODES_SURF (IE, 1)
                IF ( MAX_FACES == FOUND ) CYCLE SURF  ! IE DONE
                CYCLE CHECK ! this L_TEST element search loop
              END IF ! NUMBER NEEDED
            END IF
        END DO L_2_S ! over in
        !b END IF not IE
      END DO CHECK ! over candidate element L_TEST
    END IF ! a possible candidate
  END DO SURF ! over all elements

  MAIN : DO IE = 1, N_ELEMS      ! LOOP OVER ALL ELEMENTS 
    FOUND = COUNT ( L_TO_L_NEIGH (:, IE) /= 0 )
!b print *, 'FOUND ', FOUND

!        EXTRACT INCIDENCES LIST FOR ELEMENT IE   
    CALL ELEMENT_NODES (IE, NOD_PER_EL, NODES, ELEM_NODES) 

!        ESTABLISH RANGE OF POSSIBLE ELEMENT NEIGHBORS    
    L_START = N_ELEMS+1 ; L_STOP = 0 
    DO IN = 1, NOD_PER_EL 
!     IF ( L_START > L_FIRST (ELEM_NODES (IN) ) ) &
!          L_START = L_FIRST (ELEM_NODES (IN) )
!     IF ( L_STOP  < L_LAST  (ELEM_NODES (IN) ) ) &
!          L_STOP  = L_LAST  (ELEM_NODES (IN) )
      L_START = MIN (L_START, L_FIRST (ELEM_NODES (IN)) )
      L_STOP  = MAX (L_STOP,  L_LAST  (ELEM_NODES (IN)) )
    END DO 
    L_START = MAX (L_START, (IE+1))

!        LOOP OVER POSSIBLE ELEMENT NEIGHBORS            
    IF ( L_START <= L_STOP) THEN 
      RANGE : DO L_TEST = L_START, L_STOP 
        !b IF ( L_TEST /= IE) THEN 
        KOUNT = 0  ! NO COMMON NODES

!      LOOP OVER INCIDENCES OF POSSIBLE ELEMENT NEIGHBOR        
        CALL ELEMENT_NODES (L_TEST, NOD_PER_EL, NODES, NEIG_NODES) 
        LOCAL : DO IN = 1, NOD_PER_EL 
          N_TEST = NEIG_NODES (IN) 
          IF ( N_TEST <  1 ) CYCLE LOCAL ! to a real node
          IF ( L_FIRST (N_TEST)  > IE ) CYCLE LOCAL ! to next node
          IF ( L_LAST  (N_TEST)  < IE ) CYCLE LOCAL ! to next node

!            COMPARE WITH INCIDENCES OF ELEMENT IE             
            IF ( ANY ( ELEM_NODES == N_TEST ) ) THEN
              KOUNT = KOUNT + 1         ! SHARED NODE COUNT
!b print *, 'IE, L_TEST, KOUNT       ', IE, L_TEST, KOUNT      
              IF ( KOUNT == NEED ) THEN ! NEIGHBOR PAIR FOUND
                FOUND = FOUND + 1       ! INSERT THE PAIR
                L_TO_L_NEIGH (FOUND, IE) = L_TEST     ! 1 OF 2
                NEXT = COUNT ( L_TO_L_NEIGH(:, L_TEST) /= 0 )
!b print *, 'IE, L_TEST, FOUND, NEXT ', IE, L_TEST, FOUND, NEXT
                L_TO_L_NEIGH (NEXT+1, L_TEST) = IE    ! 2 OF 2
                IF ( MAX_FACES == FOUND ) CYCLE MAIN  ! IE DONE
                CYCLE RANGE ! this L_TEST element search loop
              END IF ! NUMBER NEEDED
            END IF
        END DO LOCAL ! over in
        !b END IF not IE
      END DO RANGE ! over candidate element L_TEST
    END IF ! a possible candidate
  END DO MAIN ! over all elements
END SUBROUTINE FORM_FACING_OR_SURFS                        

SUBROUTINE TRAPEZOIDAL_TIME_RULE (I_DIAG, S_SKY, M_SKY, S_SKY_LOWER)
!                          not used
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!   APPLY GENERALIZED TRAPEZOIDAL RULE TO REFORM TWO SQUARE MATRICES
!                     M_SKY * D_DOT + S_SKY * D = C
!                 INTO THE TWO USED IN TIME INTEGRATION
! (M_SKY/T_STEP + TS_B*S_SKY) D_1 = (M_SKY/T_STEP - (1-TS_B)*S_SKY) D_0
!                                 + TS_B*C_1 + (1-TS_B)*C_0
!                                 OR
!         M_SKY D_1 = S_SKY D_0 + TS_B*C_1 + (1-TS_B)*C_0
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Use System_Constants  ! for N_COEFF, N_D_FRE, NOT_SYM, TS_B, TIME_STEP
 IMPLICIT NONE
 INTEGER,  INTENT(IN)    :: I_DIAG (N_D_FRE)
 REAL(DP), INTENT(INOUT) :: M_SKY (N_COEFF)
 REAL(DP), INTENT(INOUT) :: S_SKY (N_COEFF)
 REAL(DP), INTENT(INOUT) :: S_SKY_LOWER (NOT_SYM)

 INTEGER :: I
! N_D_FRE         = TOTAL NUMBER OF SYSTEM DEGREES OF FREEDOM
! N_COEFF         = NUMBER OF NON-ZERO COEFFICIENTS IN S_SKY
! NOT_SYM         = NUMBER OF NON-ZERO COEFF IN S_SKY_LOWER,
!                   = 0 OR N_COEFF
! M_SKY           = SYS. EQ. SQ. MASS MATRIX (SYMMETRIC)
! S_SKY           = SYS. EQ. SQ. MATRIX
! S_SKY_LOWER     = SYS. EQ. SQ. MATRIX

  IF ( I_BUG == 1 ) PRINT *, 'Entering TRAPEZOIDAL_TIME_RULE'

  M_SKY = M_SKY / TIME_STEP + TS_B * S_SKY
  S_SKY = M_SKY - S_SKY
END SUBROUTINE TRAPEZOIDAL_TIME_RULE

SUBROUTINE SKY_BC_NULL (N_RES_EQ, NDEX_C_EQ, M_SKY, I_DIAG)
!b SUBROUTINE SKY_BC_NULL (N_RES_EQ, NDEX_C_EQ, M_SKY, CC, I_DIAG)
!                          not used
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!   FOR TRANSIENT OR DYNAMIC ANALYSIS, ZERO ALL ROWS AND COLUMNS 
!     IN THE SYSTEM MASS MATRIX WHERE CONSTRAINTS ARE APPLIED
!        (THIS ASSUMES VELOCITES OF CONSTRAINTS ARE ZERO)
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Use System_Constants
 IMPLICIT NONE
 INTEGER,  INTENT(IN)    :: N_RES_EQ  (MAX_ACT),             &
                            NDEX_C_EQ (MAX_ACT, N_CEQ),      &
                            I_DIAG (N_D_FRE)
 !b REAL(DP), INTENT(INOUT) :: M_SKY (N_COEFF), CC (N_D_FRE)
 REAL(DP), INTENT(INOUT) :: M_SKY (N_COEFF)
 
 INTEGER  :: IC, IEQ, NEQ, NTEST, K
 !b REAL(DP) :: ZERO (MAX_ACT)               ! NULL BC COEFF
 REAL(DP) :: ZERO (MAX_ACT), CC (N_D_FRE) ! BOTH ZERO
                                      
! N_D_FRE         = TOTAL NUMBER OF SYSTEM DEGREES OF FREEDOM
! N_COEFF         = NUMBER OF NON-ZERO COEFFICIENTS IN M_SKY
! M_SKY           = SYS. EQ. SQ. MASS MATRIX (SYMMETRIC)
! CC              = SYS. EQ. COL. MATRIX
! N_G_DOF         = NO. PARAMETERS PER NODE
! N_RES_EQ        = NO OF CONSTRAINT EQS OF EACH TYPE
! NDEX_C_EQ (I,J) = CONSTRAINT EQS DOF NO I FOR EQ J
! N_CEQ           = NUMBER OF CONSTRAINT EQUATIONS

  ZERO (MAX_ACT) = 0.d0 ; CC (N_D_FRE) = 0.d0
  IF ( I_BUG == 1 ) THEN
    PRINT *, 'Entering SKY_BC_NULL'
  END IF
  IF ( STATIC ) THEN
    PRINT *, 'WARNING: SKY_BC_NULL CALLED IN STATIC RUN'
    N_WARN = N_WARN + 1
  END IF
 
!  WORK BACKWARD FROM LARGEST CONSTRAINT TYPE (DO TYPE 1 LAST)
  IF ( SUM(N_RES_EQ) < 1) THEN
    PRINT *, 'SKY_BC_NULL: WARNING, NO RESTRAINTS APPLIED'
    N_WARN = N_WARN + 1 ! INCREMENT WARNING
  END IF
  IEQ = SUM ( N_RES_EQ ) + 1  
  DO IC = MAX_ACT, 1, -1
    NTEST = N_RES_EQ (IC)
    IF ( NTEST > 0 ) THEN ! Apply constraint of type IC
      IF ( IC == 1 ) THEN
!-->         TYPE 1     D(L1) = C1
        DO NEQ = 1, NTEST
          IEQ = IEQ - 1 
          CALL SKY_TYPE_1_SYM (NDEX_C_EQ (1, IEQ), ZERO,     &
                               M_SKY, CC, I_DIAG)
!           ALSO ZERO DIAGONAL TERM
          M_SKY ( I_DIAG (NDEX_C_EQ (1, IEQ)) ) = 0.d0
        END DO ! FOR EACH SINGLE DOF

      ELSE ! IC >= 2
!-->         TYPE 2   D(L1) + C1*D(L2) = C2
!-->         TYPE 3   D(L1) + C1*D(L2) + C2*D(L3) = C3, ETC.
        DO NEQ = 1, NTEST
          IEQ = IEQ - 1 
          DO K = 1, IC
            CALL SKY_TYPE_1_SYM (NDEX_C_EQ (K, IEQ), ZERO,     &
                                 M_SKY, CC, I_DIAG)
!           ALSO ZERO DIAGONAL TERM
            M_SKY ( I_DIAG (NDEX_C_EQ (K, IEQ)) ) = 0.d0
          END DO ! FOR EACH COUPLED DOF
        END DO ! FOR COUPLED DOF SETS
      END IF
    END IF
  END DO
END SUBROUTINE SKY_BC_NULL

SUBROUTINE SET_L_SHAPE_FACE_NEEDS
Use System_Constants
Use Elem_Type_Data
   SELECT CASE (L_SHAPE) ! SHAPE OF ELEMENT
     CASE (1) ; NEEDS = 1 ; MAX_FACES = 2  ! LINE ELEMENT
     CASE (2) ; NEEDS = 2 ; MAX_FACES = 3  ! TRIANGULAR ELEMENT
     CASE (3) ; NEEDS = 2 ; MAX_FACES = 4  ! QUADRILATERAL ELEMENT
     CASE (4) ; NEEDS = 4 ; MAX_FACES = 6  ! HEXAHEDRA ELEMENT
     CASE (5) ; NEEDS = 3 ; MAX_FACES = 4  ! TETRAHEDRA ELEMENT
     CASE (6) ; NEEDS = 3 ; MAX_FACES = 5  ! WEDGE ELEMENT
     CASE DEFAULT ; NEEDS = 1 ; MAX_FACES = 6
   END SELECT
END SUBROUTINE SET_L_SHAPE_FACE_NEEDS

SUBROUTINE SET_EL_TYPE_SHAPE_FACE_NEEDS (LT_SHAP, LT_NEEDS, LT_FACES)
 IMPLICIT NONE
 INTEGER, INTENT (IN)  :: LT_SHAP
 INTEGER, INTENT (OUT) :: LT_NEEDS, LT_FACES
   SELECT CASE (LT_SHAP) ! SHAPE OF THIS ELEMENT TYPE
     CASE (1) ; LT_NEEDS = 1 ; LT_FACES = 2  ! LILT_NE ELEMENT
     CASE (2) ; LT_NEEDS = 2 ; LT_FACES = 3  ! TRIANGULAR ELEMENT
     CASE (3) ; LT_NEEDS = 2 ; LT_FACES = 4  ! QUADRILATERAL ELEMENT
     CASE (4) ; LT_NEEDS = 4 ; LT_FACES = 6  ! HEXAHEDRA ELEMENT
     CASE (5) ; LT_NEEDS = 3 ; LT_FACES = 4  ! TETRAHEDRA ELEMENT
     CASE (6) ; LT_NEEDS = 3 ; LT_FACES = 5  ! WEDGE ELEMENT
     CASE DEFAULT ; LT_NEEDS = 1 ; LT_FACES = 6
   END SELECT
END SUBROUTINE SET_EL_TYPE_SHAPE_FACE_NEEDS

SUBROUTINE SAVE_QP_SOURCE (N_FILE, XYZ, SOURCE)
! * * * * * * * * * * * * * * * * * * * * * * * * * * * *
! SAVE QUADRATURE POINT COORDINATES AND SOURCE TERM(S)
! * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Use System_Constants  ! for N_SPACE
 IMPLICIT NONE
  INTEGER,   INTENT(IN) :: N_FILE
  REAL(DP),  INTENT(IN) :: XYZ (N_SPACE), SOURCE

   WRITE (N_FILE, 40)  XYZ, SOURCE
   40 FORMAT ( (10(1PE13.5)) )
END SUBROUTINE SAVE_QP_SOURCE

SUBROUTINE OPEN_QP_SOURCE (N_FILE)
! OPEN TO SAVE QUADRATURE POINT COORDINATES AND SOURCE TERM
Use System_Constants  ! for N_WARN
 IMPLICIT NONE
  INTEGER, INTENT(IN) :: N_FILE ; INTEGER :: IO_1

   if ( N_FILE > 0 )  then !b
     OPEN (N_FILE, FILE='el_qp_xyz_sources.tmp', ACTION='WRITE', &
       STATUS='REPLACE', IOSTAT = IO_1) !b
     IF ( IO_1 > 0 ) THEN !b
       PRINT *, 'NOTE: FILE OPEN', N_FILE, ' FAILED' !b
     ELSE !b
       PRINT *, 'NOTE: el_qp_xyz_sources.tmp OPEN AS ', N_FILE !b
     END IF ! open failed !b
   else
    N_WARN = N_WARN + 1
    PRINT *, 'WARNING: OPEN_QP_SOURCE, el_qp_xyz_sources.tmp FAILED'
   end if ! N_FILE 
END SUBROUTINE OPEN_QP_SOURCE 

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

SUBROUTINE ELEM_CENTER_GEOMETRY (CENTER, RADIAL)
 Use System_Constants ! for N_SPACE
 Use Elem_Type_Data   ! for LT_N, COORD (LT_N, N_SPACE)
 IMPLICIT NONE
  REAL(DP), INTENT (OUT) :: CENTER (N_SPACE)       ! average of nodes
  REAL(DP), INTENT (OUT) :: RADIAL (LT_N, N_SPACE) ! relative positions
  INTEGER :: J ! loops

  CENTER = SUM ( COORD, DIM=1 ) / LT_N   ! average nodal positions
  DO J = 1, N_SPACE
    RADIAL (:, J) = COORD (:, J) - CENTER (J) ! From center
  END DO
END SUBROUTINE ELEM_CENTER_GEOMETRY 

SUBROUTINE GET_RADIALS_FROM_CENTER (CENTER, RADIAL)
 Use System_Constants ! for N_SPACE
 Use Elem_Type_Data   ! for LT_N, COORD (LT_N, N_SPACE)
 IMPLICIT NONE
  REAL(DP), INTENT (IN ) :: CENTER (N_SPACE)       ! average of nodes
  REAL(DP), INTENT (OUT) :: RADIAL (LT_N, N_SPACE) ! relative positions
  INTEGER :: J ! loops

  DO J = 1, N_SPACE
    RADIAL (:, J) = COORD (:, J) - CENTER (J) ! From center
  END DO
END SUBROUTINE GET_RADIALS_FROM_CENTER 

SUBROUTINE UPWIND_DIST_AND_LOGIC (RADIAL, UNIT_V, DOWN, &
                                   LENGTH, IS_DOWNWIND)
 Use System_Constants ! for N_SPACE
 Use Elem_Type_Data   ! for LT_N
 IMPLICIT NONE
  REAL(DP), INTENT (IN ) :: RADIAL (LT_N, N_SPACE) ! relative positions
  REAL(DP), INTENT (IN ) :: UNIT_V (N_SPACE)       ! vel unit vector
  REAL(DP), INTENT (OUT) :: DOWN   (LT_N), LENGTH  ! downwind if > 0
  LOGICAL,  INTENT (OUT) :: IS_DOWNWIND (LT_N)     ! true if downwind 
  REAL(DP) :: L_POS, L_NEG 
  INTEGER :: K
  LOGICAL :: LOGIC (LT_N)   ! debug
  
!       L_POS  = MAXVAL (DOWN, IS_DOWNWIND)
!       L_NEG  = ABS (MINVAL (DOWN, .NOT. IS_DOWNWIND))

   IF ( ALL (UNIT_V == 0.d0) ) STOP 'Zero unit vector. u_d_a_l'
   IF ( ALL (RADIAL == 0.d0) ) STOP 'Zero radial array. u_d_a_l'

!          IDENTIFY DOWNWIND DISTANCES, AND LOGIC
   DOWN  = MATMUL ( RADIAL, UNIT_V )    ! POSITIVE & NEGATIVE
   IF ( ALL (DOWN == 0.d0) ) STOP 'Zero DW vector. '

   L_POS = DOWN (1) ; L_NEG = DOWN (1) 
   DO K = 1, LT_N
     IF ( DOWN (K) >= 0.d0 ) THEN
       IS_DOWNWIND (K) = .TRUE.
     ELSE
       IS_DOWNWIND (K) = .FALSE.
     END IF
     IF ( L_POS < DOWN (K) ) L_POS = DOWN (K)
     IF ( L_NEG > DOWN (K) ) L_NEG = DOWN (K)
   END DO

!        GET MAXIMUM STREAMLINE DISTANCES
   L_NEG = ABS (L_NEG) ; LENGTH = L_NEG + L_POS

!       WHERE ( DOWN >= 0.d0 ) ; IS_DOWNWIND = .TRUE.
!       ELSEWHERE              ; IS_DOWNWIND = .FALSE.  ; END WHERE
   WHERE ( DOWN >= 0.d0 ) ; LOGIC = .TRUE.
   ELSEWHERE              ; LOGIC = .FALSE.  ; END WHERE

if ( any (IS_DOWNWIND .NEQV. LOGIC) ) then
  print *,'ERROR inconsistent logic for element ', THIS_EL
  print *,'IS_DW ', IS_DOWNWIND
  print *,'LOGIC ', LOGIC 
  STOP 'UPWIND_DIST_AND_LOGIC: ERROR in logic'
end if

if ( all(IS_DOWNWIND) ) then
  print *,'ERROR all nodes downwind for element ', THIS_EL
  print *,'RADIAL '; call rprint (RADIAL, LT_N, N_SPACE, 1)
  print *,'UNIT_V ', UNIT_V
  STOP 'UPWIND_DIST_AND_LOGIC: ERROR all nodes downwind'
end if

!if ( THIS_EL == 1 ) then
!  print *,'============== UPWIND_DIST_AND_LOGIC ================'
!  print *,'RADIAL '; call rprint (RADIAL, LT_N, N_SPACE, 1)
!  print *,'UNIT_V ', UNIT_V
!  print *,'DOWN ', DOWN
!  print *,'L_NEG, L_POS, LENGTH ', L_NEG, L_POS, LENGTH
!  print *,'IS_DW ', IS_DOWNWIND
!  print *,'LOGIC ', LOGIC 
!end if

    WHERE ( IS_DOWNWIND ) ; DOWN = DOWN + L_NEG
    ELSEWHERE             ; DOWN = ABS (DOWN) + L_POS ; END WHERE

!if ( THIS_EL == 1 ) then
!  print *,'DOWN ', DOWN
!end if
END SUBROUTINE UPWIND_DIST_AND_LOGIC 

SUBROUTINE GET_MAX_DOWNWIND_DIST (DOWN, DOWN_MAX)
!b SUBROUTINE GET_NODAL_DOWNWIND_DIST (DOWN, DOWN_NODAL) -> MAX
 Use System_Constants ! for N_SPACE
 Use Elem_Type_Data   ! for LT_N
 IMPLICIT NONE
  REAL(DP), INTENT (IN ) :: DOWN     (LT_N) ! downwind wrt center
  REAL(DP), INTENT (OUT) :: DOWN_MAX (LT_N) ! downwind total 
  REAL(DP) :: L_POS, L_NEG 
  
!        GET AVERAGE STREAMLINE DISTANCES
        L_POS  = MAXVAL (DOWN)
        L_NEG  = ABS (MINVAL (DOWN))
if ( L_POS <= 0.d0 ) then
  print *,'ERROR, no DW dist for element ', THIS_EL
  print *,'DOWN dist ', DOWN
  stop    'GET_MAX_DOWNWIND_DIST: ERROR, no DW dist for element '
end if
        
!        GET TOTAL DOWNWIND MAX DISTANCES
        WHERE ( DOWN >= 0.d0 ) 
          DOWN_MAX = DOWN + L_NEG
        ELSEWHERE             
          DOWN_MAX = ABS (DOWN) + L_POS ; END WHERE
!if ( THIS_EL == 1 ) then
!  print *,'DOWN     ', DOWN    
!  print *,'L_NEG, L_POS ',  L_NEG, L_POS
!  print *,'DOWN_MAX ', DOWN_MAX
!end if
END SUBROUTINE GET_MAX_DOWNWIND_DIST 

SUBROUTINE GET_AVE_DOWNWIND_DIST (DOWN, DOWN_AVE)
!b SUBROUTINE GET_AVE_DOWNWIND_DIST (DOWN, IS_DOWNWIND, DOWN_AVE)
 Use System_Constants ! for N_SPACE
 Use Elem_Type_Data   ! for LT_N
 IMPLICIT NONE
  REAL(DP), INTENT (IN ) :: DOWN        (LT_N)  ! downwind if > 0
  !b LOGICAL,  INTENT (IN ) :: IS_DOWNWIND (LT_N)  ! true if downwind 
  REAL(DP), INTENT (OUT) :: DOWN_AVE    (LT_N)  ! downwind total      
  LOGICAL  :: IS_DOWNWIND (LT_N)  ! true if downwind 
  LOGICAL  :: LOGIC (LT_N)
  REAL(DP) :: AVE_POS, AVE_NEG 
  INTEGER  :: C_DW, C_UW, K  ! down or up wind
  
!if ( THIS_EL == 1 ) then
!  print *,'GET_AVE_DOWNWIND_DIST =======', THIS_EL !b , IS_DOWNWIND
!  print *,'DOWN ', DOWN
!end if

AVE_POS = 0 ; AVE_NEG = 0 ; C_DW = 0 ; C_UW = 0
DO K = 1, LT_N
  IF ( DOWN (K) >= 0.d0 ) THEN
    AVE_POS = AVE_POS + DOWN (K)
    C_DW    = C_DW + 1
    LOGIC (K) = .TRUE.
  ELSE
    AVE_NEG = AVE_NEG + DOWN (K)
    C_UW    = C_UW + 1
    LOGIC (K) = .FALSE.
  END IF
  !b IF ( LOGIC (K) .NEQV. IS_DOWNWIND (K) ) PRINT *, &
    !b 'ERROR, Logic mismatch at node, el: ',         &
     !b K, THIS_EL, IS_DOWNWIND (K), LOGIC (K)
END DO
AVE_POS = AVE_POS / C_DW
AVE_NEG = ABS (AVE_NEG) / C_UW

!        GET AVERAGE STREAMLINE DISTANCES
!b !  C_UW = COUNT ((.NOT. IS_DOWNWIND))
!b !  C_DW = LT_N - C_UW ! by pass compiler bug COUNT (IS_DOWNWIND)

   IF ( C_DW < 1 .OR. C_DW >= LT_N .OR. &
        C_UW < 1 .OR. C_UW >= LT_N ) THEN
     PRINT *,'IMPOSSIBLE DOWNWIND COUNT, LT, UP', C_DW, LT_N, C_UW
     PRINT *,'THIS_EL     ', THIS_EL    
     !b PRINT *,'IS_DOWNWIND ', IS_DOWNWIND
     PRINT *,'LOGIC       ', LOGIC
     PRINT *,'DOWN dist', DOWN
     STOP 'GET_AVE_DOWNWIND_DIST: ERROR, IMPOSSIBLE DOWNWIND COUNT'
   END IF

!b !  AVE_POS = SUM (DOWN, IS_DOWNWIND) / C_DW
!b !  AVE_NEG = ABS (SUM (DOWN, .NOT. IS_DOWNWIND)) / C_UW

if ( AVE_POS <= 0.d0 ) then
  print *,'ERROR, no DW dist for element ', THIS_EL, AVE_POS
  print *,'DOWN dist ', DOWN
  !b PRINT *,'IS_DOWNWIND ', IS_DOWNWIND
  stop    'GET_AVE_DOWNWIND_DIST: ERROR, no DW dist for element '
end if

!b call at(1488)
!        GET TOTAL DOWNWIND AVE DISTANCES
        WHERE ( DOWN >= 0.d0 ) 
          DOWN_AVE = DOWN + AVE_NEG
        ELSEWHERE             
          DOWN_AVE = ABS (DOWN) + AVE_POS 
        END WHERE
!b call at(1495)

!if ( THIS_EL == 1 ) then
!  print *,'GET_AVE_DOWNWIND_DIST ', THIS_EL
!  print *,'POS_AVE, NEG_AVE ', AVE_POS, AVE_NEG
!  PRINT *,'DOWN dist', DOWN
!  print *,'DOWN_AVE ', DOWN_AVE
!  !b PRINT *,'IS_DOWNWIND ', IS_DOWNWIND
!  PRINT *,'LOGIC       ', LOGIC
!end if
END SUBROUTINE GET_AVE_DOWNWIND_DIST 

SUBROUTINE GET_DOWNWIND_LOGIC (RADIAL, UNIT_V, DOWN, IS_DOWNWIND)
 Use System_Constants ! for N_SPACE
 Use Elem_Type_Data   ! for LT_N
 IMPLICIT NONE
  REAL(DP), INTENT (IN ) :: RADIAL (LT_N, N_SPACE) ! relative positions
  REAL(DP), INTENT (IN ) :: UNIT_V (N_SPACE)       ! vel unit vector
  REAL(DP), INTENT (OUT) :: DOWN   (LT_N)          ! downwind if > 0
  LOGICAL,  INTENT (OUT) :: IS_DOWNWIND (LT_N)     ! true if downwind 
  INTEGER :: K
  !b LOGICAL :: LOGIC (LT_N)   ! debug

   IF ( ALL (UNIT_V == 0.d0) ) STOP 'Zero unit vector. g-dw-l' 
   IF ( ALL (RADIAL == 0.d0) ) STOP 'Zero radial array. g-dw-l' 
  
!          IDENTIFY DOWNWIND DISTANCES, AND LOGIC
    DOWN  = MATMUL ( RADIAL, UNIT_V )    ! POSITIVE & NEGATIVE
    IF ( ALL (DOWN == 0.d0) ) STOP 'Zero DW vector. g-dw-l'

    WHERE ( DOWN >= 0.d0 ) ; IS_DOWNWIND = .TRUE.
    ELSEWHERE              ; IS_DOWNWIND = .FALSE.  ; END WHERE

   !b DO K = 1, LT_N
     !b IF ( DOWN (K) >= 0.d0 ) THEN
       !b LOGIC (K) = .TRUE.
     !b ELSE
       !b LOGIC (K) = .FALSE.
     !b END IF
   !b END DO

if ( all(IS_DOWNWIND) ) then
  print *,'ERROR all nodes downwind for element ', THIS_EL
  print *,'RADIAL '; call rprint (RADIAL, LT_N, N_SPACE, 1)
  print *,'UNIT_V ', UNIT_V
  STOP 'GET_DOWNWIND_LOGIC: ERROR all nodes downwind'
end if

!b if ( any (IS_DOWNWIND .NEQV. LOGIC) ) then
  !b print *,'ERROR inconsistent logic for element ', THIS_EL
  !b print *,'IS_DW ', IS_DOWNWIND
  !b print *,'LOGIC ', LOGIC
  !b STOP 'GET_DOWNWIND_LOGIC: ERROR in logic'
!b end if

!if ( THIS_EL == 1 ) then
!  print *,'============== GET_DOWNWIND_LOGIC ================'
!  print *,'RADIAL '; call rprint (RADIAL, LT_N, N_SPACE, 1)
!  print *,'UNIT_V ', UNIT_V
!  print *,'DOWN ', DOWN
!  print *,'IS_DW ', IS_DOWNWIND
!  !b print *,'LOGIC ', LOGIC
!end if
END SUBROUTINE GET_DOWNWIND_LOGIC 

FUNCTION VOLUME_UPWIND_LENGTH (H_INTG, DOWN_NODAL)  RESULT (LENGTH)
! Get DOWN from UPWIND_DIST_AND_LOGIC
 Use System_Constants ! for N_SPACE
 Use Elem_Type_Data   ! for LT_N
 IMPLICIT NONE
  REAL(DP), INTENT (IN ) :: H_INTG     (LT_N)  ! H integral    
  REAL(DP), INTENT (IN ) :: DOWN_NODAL (LT_N)  ! nodal downwind lengths
  REAL(DP)               :: LENGTH         ! volume average downwind
  REAL(DP) :: VOLUME                       ! element volume          
  
   IF ( ANY (DOWN_NODAL < 0.d0) ) THEN
     PRINT *,'WARNING: VOLUME_UPWIND_LENGTH, NEGATIVE DATA.'
     PRINT *,'DOWN_NODAL ', DOWN_NODAL
     N_WARN = N_WARN + 1
   END IF ! negative lengths
   VOLUME = SUM (H_INTG)
   IF ( VOLUME <= 0.d0 ) STOP 'VOLUME_UPWIND_LENGTH: VOLUME <= 0'

   LENGTH = DOT_PRODUCT (H_INTG, DOWN_NODAL) / VOLUME ! Integral average
!b if ( THIS_EL == 1 ) print *,'VOLUME_UPWIND_LENGTH ', LENGTH !b
END FUNCTION VOLUME_UPWIND_LENGTH 

SUBROUTINE STOCASTIC_VOLATILITY_VECTOR (XYZ, U)
USE SYSTEM_CONSTANTS   ! FOR N_SPACE, THIS_EL
Use Sys_Properties_Data
 IMPLICIT NONE
 REAL(DP), INTENT (IN)  :: XYZ    (N_SPACE)        ! point
 REAL(DP), INTENT (OUT) :: U      (N_SPACE)        ! velocity

 !  Variable E, u items
  REAL(DP), SAVE :: Sigma, Kappa, Rho, rate     ! Global data
  REAL(DP), SAVE :: Theta, Lamda, source        ! Global data

  IF ( THIS_EL == 1 ) THEN ! Get global data
! Misc reals are: 1-Sigma 2-Kappa 3-Rho 4-r 5-Theta 6-Lamda 7-Q
     Sigma = 0.9d0 ; Kappa = 5 ; Rho = 0.1d0    ! Clarke-Parrott
     Rate = 0.1d0 ; Theta = 0.16d0              ! Clarke-Parrott
     Lamda = 0 ; Source = 0                     ! Clarke-Parrott
     IF ( REALS > 0 ) Sigma  = GET_REAL_MISC (1)
     IF ( REALS > 1 ) Kappa  = GET_REAL_MISC (2)
     IF ( REALS > 2 ) Rho    = GET_REAL_MISC (3)
     IF ( REALS > 3 ) Rate   = GET_REAL_MISC (4)
     IF ( REALS > 4 ) Theta  = GET_REAL_MISC (5)
     IF ( REALS > 5 ) Lamda  = GET_REAL_MISC (6)
     IF ( REALS > 6 ) Source = GET_REAL_MISC (7)
  END IF ! first el

  U (1) = -(Rate - XYZ (2) - Rho * Sigma / 2) * XYZ (1)
  U (2) = -Kappa * (Theta - XYZ (2)) + Sigma * Sigma / 2  &
        + (Lamda + Rho * Sigma / 2) * XYZ (2)

END SUBROUTINE STOCASTIC_VOLATILITY_VECTOR 

SUBROUTINE VELOCITY_AT_POINT (XYZ, U, UNIT_V, SPEED)
USE SYSTEM_CONSTANTS   ! FOR N_SPACE
Use Sys_Properties_Data
 IMPLICIT NONE
 REAL(DP), INTENT (IN)  :: XYZ    (N_SPACE)        ! point
 REAL(DP), INTENT (OUT) :: U      (N_SPACE)        ! velocity
 REAL(DP), INTENT (OUT) :: UNIT_V (N_SPACE), SPEED ! unit vec, speed

 INTEGER  :: FLOW_CASE !b , SCALE
 REAL(DP) :: RATE, ANGLE, RATIO, Y_ZERO, SCALE
 REAL(DP), PARAMETER :: NIL = EPSILON (1.d0)
 REAL(DP), PARAMETER :: y_1 = 0.25d0, y_2 = 0.33333333333333d0, &
                        y_12 = 0.0833333333333d03

  FLOW_CASE = 0 ; IF ( INTEGERS > 0 ) FLOW_CASE = GET_INTEGER_MISC (1)
  SCALE     = 1 ; IF ( INTEGERS > 1 ) SCALE     = GET_INTEGER_MISC (2)
  if ( SCALE ==  0.d0 ) print *,'NOTE SCALE = 0 gives zero velocity'
  !b IF ( SCALE < 0.d0 ) SCALE = SQRT ( ABS (SCALE) )

  SELECT CASE (FLOW_CASE)
    CASE (1) ! Smith-Hutton
      U(1) =  2.d0 * xyz(2) * (1.d0-xyz(1)**2) ! Smith-Hutton
      U(2) = -2.d0 * xyz(1) * (1.d0-xyz(2)**2) ! Smith-Hutton
    CASE (2) ! Donald's money
      RATE = 1.d0 ; IF ( REALS > 2 ) RATE = GET_REAL_MISC (4)
      U    = -RATE * XYZ
    CASE (3) ! ! Hughes cosine hill
      U(1) = - xyz(2) ; U(2) = xyz(1)          ! Brooks-Hughes
    CASE (4) ! Hughes advection skew
      ANGLE = 0.d0 ; IF ( INTEGERS > 2 ) ANGLE =  GET_INTEGER_MISC (3)
      ANGLE = ANGLE / 57.3d0     ! radians
      y_zero = Y_1 + XYZ(1) * TAN (ANGLE)
      IF ( XYZ (2) <= y_zero ) THEN
        U(1) = cos (angle) ; U(2) = sin (angle)
      ELSE IF ( XYZ (2) >= (y_zero + y_12) ) THEN
        U = 0.d0
      ELSE
        RATIO = (y_12 - (XYZ (2) - y_zero))/y_12
        U(1) = cos (angle) * ratio
        U(2) = sin (angle) * ratio
      END IF
    CASE (5) ! Constant advection skew
      ANGLE = 0.d0 ; IF ( INTEGERS > 2 ) ANGLE =  GET_INTEGER_MISC (3)
      ANGLE = ANGLE / 57.3d0     ! radians
      U(1) = cos (angle) ; U(2) = sin (angle)
    CASE DEFAULT ! Unit flow in x
      U = 0.d0 ; U(1) = 1.d0
  END SELECT
  U = U * SCALE

!       GET SPEED, AND UNIT VECTOR
   SPEED = SQRT ( SUM (U **2) )        ! SPEED
   !b IF ( N_SPACE == 1 ) SPEED = SIGN (SPEED, U(1))
   IF ( SPEED > NIL ) THEN             ! no divide by 0
     UNIT_V = U / SPEED                ! UPWIND UNIT VECTOR
   ELSE ! Null vectors, place at equal angles to axes
     SPEED = NIL   
     UNIT_V = 1.d0 / SQRT (DBLE(N_SPACE)) ! for N_SPACE components
     U = NIL * UNIT_V                     ! for N_SPACE components
   END IF
END SUBROUTINE VELOCITY_AT_POINT

SUBROUTINE UNIT_VECTOR_FORM (U, UNIT_V, SPEED)
USE SYSTEM_CONSTANTS   ! FOR N_SPACE
Use Sys_Properties_Data
 IMPLICIT NONE
 REAL(DP), INTENT (OUT) :: U      (N_SPACE)        ! velocity
 REAL(DP), INTENT (OUT) :: UNIT_V (N_SPACE), SPEED ! unit vec, speed

 REAL(DP), PARAMETER :: NIL = EPSILON (1.d0)
 REAL(DP), PARAMETER :: ROOT_2 = 0.70710678d0, ROOT_3 = 0.57735021d0

!       GET SPEED, AND UNIT VECTOR
   SPEED = SQRT ( SUM (U **2) )        ! SPEED
   IF ( SPEED > NIL ) THEN             ! no divide by 0
     UNIT_V = U / SPEED                ! UPWIND UNIT VECTOR
   ELSE ! Null vectors, place at equal angles to axes
     SELECT CASE (N_SPACE)
       CASE (1) ; UNIT_V = 1.d0
       CASE (2) ; UNIT_V = ROOT_2
       CASE (3) ; UNIT_V = ROOT_3
       CASE DEFAULT
         UNIT_V = 1.d0 / SQRT (DBLE(N_SPACE)) ! for N_SPACE components
     END SELECT
     SPEED = NIL   
     U = NIL * UNIT_V                     ! for N_SPACE components
   END IF
END SUBROUTINE UNIT_VECTOR_FORM

SUBROUTINE DIFFUSION_UPWIND (E, UNIT_V, E_UPWIND, E_CROSS)
 Use System_Constants ! for N_SPACE, N_R_B
 IMPLICIT NONE
  REAL(DP), INTENT (IN)  :: E (N_R_B, N_R_B) ! constitutive law
  REAL(DP), INTENT (IN)  :: UNIT_V (N_SPACE) ! unit vector
  REAL(DP), INTENT (OUT) :: E_UPWIND, E_CROSS
  REAL(DP) :: E_LOCAL (N_R_B, N_R_B), LAMDA (N_SPACE, N_SPACE)

   IF ( N_SPACE == 1 ) THEN ! SPECIAL CASE
    E_UPWIND = E (1, 1) ; E_CROSS = 0.d0
    RETURN
   END IF ! 1-D

   IF ( N_SPACE /= 2 .OR. N_R_B /= 2 )                         &
     STOP 'DIFFUSION_UPWIND invalid if N_SPACE /= 2, N_R_B /= 2'

!   SET DIRECTION COSINES
   LAMDA (1, 1:2) = UNIT_V (1:2)
   LAMDA (2, 1) = -UNIT_V (2) ; LAMDA (2, 2) = UNIT_V (1)

!   TRANSFORM
   E_LOCAL = MATMUL (LAMDA, MATMUL (E, TRANSPOSE (LAMDA)))
   E_UPWIND = E_LOCAL (1, 1)
   E_CROSS  = E_LOCAL (2, 2)
END SUBROUTINE DIFFUSION_UPWIND 

SUBROUTINE DIFFUSION_LOCAL (E, UNIT_V, E_LOCAL)
! Get diffusion value in the direction of a unit vector
! Vector is usually // to streamline, but may be otherwise
 Use System_Constants ! for N_SPACE, N_R_B
 IMPLICIT NONE
  REAL(DP), INTENT (IN)  :: E (N_R_B, N_R_B) ! constitutive law
  REAL(DP), INTENT (IN)  :: UNIT_V (N_SPACE) ! unit vector
  REAL(DP), INTENT (OUT) :: E_LOCAL          ! principle value

   IF ( N_SPACE /= N_R_B )                            &
     STOP 'DIFFUSION_LOCAL invalid if N_SPACE /= N_R_B'

   IF ( N_SPACE == 1 ) THEN ! SPECIAL CASE
    E_LOCAL = E (1, 1) ; RETURN
   END IF ! 1-D

!   TRANSFORM (FOR ONE PRINCIPLE VALUE)
   E_LOCAL = DOT_PRODUCT (UNIT_V, MATMUL (E, UNIT_V))
END SUBROUTINE DIFFUSION_LOCAL 

SUBROUTINE CENTER_DGH_D2GH (DGH, D2GH, DET_WT)
! GET PHYSICAL DERIVATIVES AT THE ELEMENT CENTER
 Use System_Constants ! for N_SPACE, N_2_DER, CONSTANT_J
 Use Elem_Type_Data   ! for LT_N, LT_PARM, DLH (LT_PARM, LT_N),
                      ! H (LT_N), COORD (LT_N, N_SPACE),
                      ! D2LH (N_2_DER, LT_N)
 IMPLICIT NONE
  REAL(DP), INTENT (OUT) :: DGH  (N_SPACE, LT_N)    
  REAL(DP), INTENT (OUT) :: D2GH (N_2_DER, LT_N), DET_WT
!         Automatic Arrays
  REAL(DP) :: AJ (N_SPACE, N_SPACE), AJ_INV (N_SPACE, N_SPACE), DET
  REAL(DP) :: ONE_PT (LT_PARM), ONE_WT         ! 1 pt rule     
!!        Second derivatives
!  REAL(DP) :: P_AJ      (N_2_DER, N_2_DER)    ! AJ PRODUCTS
!  REAL(DP) :: P_AJ_INV  (N_2_DER, N_2_DER)    ! PRODUCTS INVERSE
!  INTEGER  :: I_ERROR
!  LOGICAL  :: CONSTANT_J = .FALSE. !b move to System_Constants XXX

!        LOCAL AND GLOBAL FIRST DERIVATIVES AT CENTROID
    CALL GET_ONE_PT_RULE (ONE_PT, ONE_WT)       ! local point
    CALL SCALAR_DERIVS (ONE_PT, DLH)     ! SCALAR DERIVATIVES
    AJ  = MATMUL (DLH, COORD)                      ! JACOBIAN
    CALL INVERT_JACOBIAN (AJ, AJ_INV, DET, N_SPACE) ! INVERSE
    DGH = MATMUL (AJ_INV, DLH)         ! PHYSICAL DERIVATIVES
    DET_WT = DET * ONE_WT
!b if ( THIS_EL == 1 ) print *,'DET, ONE_WT ', DET, ONE_WT

D2GH = 0.d0  ! default here, enhance later, uncomment below
!!       SECOND DERIVATIVES
!    CALL SCALAR_2ND_DERIVS (ONE_PT, D2LH) 
!
!    CALL JACOBIAN_PRODUCTS (AJ, P_AJ) ! ALWAYS NEEDED
!    ! NOTE: D2GH     = P_AJ_INV (D2HL - HESSIAN * DGH)
!    !       HESSIAN  = D2HL * COORD
!    CALL INV_SMALL_MAT (N_2_DER, P_AJ, P_AJ_INV, I_ERROR)
!    IF ( I_ERROR > 0 ) THEN
!      PRINT *, 'ERROR: CENTER_DGH_D2GH, NO PRODUCT INVERSE'
!      STOP     'ERROR: CENTER_DGH_D2GH, NO PRODUCT INVERSE'
!    END IF ! INVERSION ERROR
!    IF ( CONSTANT_J ) THEN ! HESSIAN = 0
!      D2GH = MATMUL (P_AJ_INV, D2LH)
!    ELSE
!      D2GH = MATMUL (P_AJ_INV, (D2LH            &
!           - MATMUL (MATMUL (D2LH, COORD), DGH)))
!    END IF ! ZERO HESSIAN
END SUBROUTINE CENTER_DGH_D2GH

SUBROUTINE SECOND_GLOBAL_DERIV_H (ONE_PT, DGH, D2GH)
! ---------------------------------------------------------
!    GET PHYSICAL DERIVATIVES AT A POINT, RE_BUILD JACOBIAN
! ---------------------------------------------------------
 Use System_Constants ! for N_SPACE, N_2_DER, CONSTANT_J
 Use Elem_Type_Data   ! for LT_N, LT_PARM, DLH (LT_PARM, LT_N),
                      ! H (LT_N), COORD (LT_N, N_SPACE),
                      ! D2LH (N_2_DER, LT_N)
  REAL(DP), INTENT (IN)  :: ONE_PT (LT_PARM)
  REAL(DP), INTENT (OUT) :: DGH  (N_SPACE, LT_N)    
  REAL(DP), INTENT (OUT) :: D2GH (N_2_DER, LT_N)
!         Automatic Arrays
  REAL(DP) :: AJ (N_SPACE, N_SPACE), AJ_INV (N_SPACE, N_SPACE), DET
!        Second derivatives
  REAL(DP) :: P_AJ      (N_2_DER, N_2_DER)    ! AJ PRODUCTS
  REAL(DP) :: P_AJ_INV  (N_2_DER, N_2_DER)    ! PRODUCTS INVERSE
  INTEGER, SAVE :: I_ERROR = 0

!        GLOBAL FIRST DERIVATIVES 
    AJ  = MATMUL (DLH, COORD)                       ! JACOBIAN
    CALL INVERT_JACOBIAN (AJ, AJ_INV, DET, N_SPACE) ! INVERSE
    DGH = MATMUL (AJ_INV, DLH)         ! PHYSICAL DERIVATIVES

!       SECOND DERIVATIVES
    CALL SCALAR_2ND_DERIVS (ONE_PT, D2LH) 

    CALL JACOBIAN_PRODUCTS (AJ, P_AJ) ! ALWAYS NEEDED
    ! NOTE: D2GH     = P_AJ_INV (D2HL - HESSIAN * DGH)
    !       HESSIAN  = D2HL * COORD
    CALL INV_SMALL_MAT (N_2_DER, P_AJ, P_AJ_INV, I_ERROR)
    IF ( I_ERROR > 0 ) THEN
      PRINT *, 'ERROR: SECOND_GLOBAL_DERIV_H, NO PRODUCT INVERSE'
      STOP     'ERROR: SECOND_GLOBAL_DERIV_H, NO PRODUCT INVERSE'
    END IF ! INVERSION ERROR
    IF ( CONSTANT_J ) THEN ! HESSIAN = 0
      D2GH = MATMUL (P_AJ_INV, D2LH)
    ELSE
      D2GH = MATMUL (P_AJ_INV, (D2LH            &
           - MATMUL (MATMUL (D2LH, COORD), DGH)))
    END IF ! ZERO HESSIAN
END SUBROUTINE SECOND_GLOBAL_DERIV_H

!SUBROUTINE GET_PT_UPWIND_DATA (XYZ, RADIAL, DGH, E_UPWIND, &
!                               PECLET, UP_WIND, GEOM_H  )
! Use System_Constants ! for N_SPACE, N_2_DER, CONSTANT_J
! Use Elem_Type_Data   ! for LT_N, LT_PARM, DLH (LT_PARM, LT_N),
!                      ! H (LT_N), COORD (LT_N, N_SPACE),
!                      ! D2LH (N_2_DER, LT_N)
! IMPLICIT NONE
!  REAL(DP), INTENT (IN) :: XYZ    (N_SPACE)       ! pt in space
!  REAL(DP), INTENT (IN) :: RADIAL (LT_N, N_SPACE) ! relative positions
!  REAL(DP), INTENT (IN) :: DGH    (N_SPACE, LT_N) ! global deriv of H
!  REAL(DP), INTENT (IN) :: E_UPWIND               ! diffusion value
!  REAL(DP), INTENT (OUT) :: PECLET                ! local Peclet number
!  REAL(DP), INTENT (OUT) :: GEOM_H                ! geometric length
!  REAL(DP), INTENT (OUT) :: UP_WIND (LT_N)        ! upwind additions
!
!!          Automatic arrays
!  REAL(DP) :: U           (N_SPACE)          ! VELOCITY
!  REAL(DP) :: UNIT_V      (N_SPACE), SPEED   ! Unit vec, speed
!  REAL(DP) :: U_DGH       (LT_N)             ! streamline gradient
!  REAL(DP) :: DOWN        (LT_N)             ! downwind if > 0
!  LOGICAL  :: IS_DOWNWIND (LT_N)             ! true if downwind node
!  !b REAL(DP) :: D2GH   (N_2_DER, LT_N)     ! SUPG
!
!   CALL VELOCITY_AT_POINT (XYZ, U, UNIT_V, SPEED) 
!!       GEOMETRY BASED TAU
!   CALL UPWIND_DIST_AND_LOGIC (RADIAL, UNIT_V, DOWN, &
!                               GEOM_H  , IS_DOWNWIND)
!   PECLET = ABS (SPEED) * GEOM_H   / E_UPWIND
!if ( THIS_EL == 1 ) print *,'PECLET, GEOM_H   ', PECLET, GEOM_H   
!
!!        DOT VELOCITY WITH SOLUTION GRADIENT
!   U_DGH  = MATMUL (U, DGH) ! v_x dp/dx + v_y dp/dy + v_z dp/dz
!
!   UP_WIND (:) = DOWN (:) * U_DGH (:) / SPEED ! later * ALPHA/2
!END SUBROUTINE GET_PT_UPWIND_DATA

SUBROUTINE PECLET_OPTIMAL_RULE (P, R)
Use System_Constants ! for DP
IMPLICIT NONE
  REAL(DP), INTENT (IN ) :: P         ! local Peclet number
  REAL(DP), INTENT (OUT) :: R         ! optimal rule 0 <= R < 1

! Optimal Rule: R = coth(P) - 1/P, underestimated < 1 %
  IF ( P > 3.d0 ) THEN ! Critical approximation
    R = 1 - 1 / P
  ELSE ! continued fraction fit
    R = P * (315 + 14 * P **2) / (945 + 105 * P **2 + P **4)
  END IF
END SUBROUTINE PECLET_OPTIMAL_RULE 

SUBROUTINE INTEGRAL_H_ON_L3 (LENGTH, C_E)
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
! INTEGRATE SHAPE FUNCTIONS OF A 3 NODE LINE ELEMENT
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
Use Precision_Module  ! for DP
 IMPLICIT NONE
 REAL(DP), INTENT(IN)  :: LENGTH   ! PHYSICAL LENGTH
 REAL(DP), INTENT(OUT) :: C_E (3)  ! INTEGRAL OF H
! LOCAL NODE COORD. ARE -1,0,+1    1-----2-----3

  C_E = LENGTH * (/ 1.d0, 4.d0, 1.d0 /) / 6.d0
END SUBROUTINE INTEGRAL_H_ON_L3

SUBROUTINE INTEGRAL_HT_H_ON_L3 (LENGTH, MASS_E)
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
! INTEGRATE H-TRANSPOSE H ON A 3 NODE LINE ELEMENT
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
Use Precision_Module  ! for DP
 IMPLICIT NONE
 REAL(DP), INTENT(IN)  :: LENGTH         ! PHYSICAL LENGTH
 REAL(DP), INTENT(OUT) :: MASS_E (3, 3)  ! INTEGRAL OF H' H
 REAL(DP), PARAMETER   :: MASS (3, 3) = RESHAPE (       &
   (/ 4, 2, -1, 2, 16, 2, -1, 2, 4 /), (/3, 3/) ) / 30.d0 
! LOCAL NODE COORD. ARE -1,0,+1    1-----2-----3

  MASS_E = LENGTH * MASS
END SUBROUTINE INTEGRAL_HT_H_ON_L3

SUBROUTINE INTEGRAL_DHDXT_DHDX_ON_L3 (LENGTH, STIFF_E)
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
! INTEGRATE DHDX-TRANSPOSE DHDX ON A 3 NODE LINE ELEMENT
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
Use Precision_Module  ! for DP
 IMPLICIT NONE
 REAL(DP), INTENT(IN)  :: LENGTH         ! PHYSICAL LENGTH  
 REAL(DP), INTENT(OUT) :: STIFF_E (3, 3) ! INTEGRAL DHDX' DHDX
 REAL(DP), PARAMETER   :: STIFF (3, 3) = RESHAPE (       &
   (/ 7, -8, 1, -8, 16, -8, 1, -8, 7/), (/3, 3/) ) / 3.d0 
! LOCAL NODE COORD. ARE -1,0,+1    1-----2-----3

  STIFF_E = STIFF / LENGTH
END SUBROUTINE INTEGRAL_DHDXT_DHDX_ON_L3

SUBROUTINE INTEGRAL_HT_DHDX_ON_L3 (U_E)
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
! INTEGRATE H-TRANSPOSE DHDX ON A 3 NODE LINE ELEMENT
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
Use Precision_Module  ! for DP
 IMPLICIT NONE
 REAL(DP), INTENT(OUT) :: U_E (3, 3) ! INTEGRAL H' DHDX
 REAL(DP), PARAMETER   :: U (3, 3) = RESHAPE (       &
   (/ -3, -4, 1, 4, 0, -4, -1, 4, 3/), (/3, 3/) ) / 6.d0 
! LOCAL NODE COORD. ARE -1,0,+1    1-----2-----3

  U_E = U 
END SUBROUTINE INTEGRAL_HT_DHDX_ON_L3

SUBROUTINE SPACE_TIME_EBC_UPDATE (NDEX_C_EQ, DD, COEF_C_EQ)
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
! PUSH CONTINUOUS SPACE-TIME RESULTS, DD, INTO PREVIOUS
!  ESSENTIAL BOUNDARY CONDITION VALUES, COEF_C_EQ
! *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-* *-*
Use System_Constants  ! for DP, MAX_ACT, N_CEQ, N_D_FRE
 IMPLICIT NONE
 INTEGER,  INTENT(IN)    :: NDEX_C_EQ (MAX_ACT, N_CEQ)
 REAL(DP), INTENT(IN)    :: DD        (N_D_FRE)
 REAL(DP), INTENT(INOUT) :: COEF_C_EQ (MAX_ACT, N_CEQ)
 INTEGER :: ID, IEQ, IT, KOUNT

! N_CEQ           = NUMBER OF CONSTRAINT EQUATIONS
! COEF_C_EQ (I,J) = COEFF TERM I+1 OF CONSTRAINT EQ J
! NDEX_C_EQ (I,J) = DOF NUMBER OF TERM I OF CONSTRAINT J
! DD              = SYSTEM LIST OF NODAL PARAMETERS
! In subroutine INPUT_CONSTRAINT_EQS: (Type 1)
!    NDEX_C_EQ (1, IEQ) = N_G_DOF * (NODE_1 - 1) + IPAR_1
!    COEF_C_EQ (1, IEQ) = A_1

! Move the last half of the system dof into EBC for
! the first half for the next space-time step
!b call at(1920); PRINT *,'N_CEQ = ', N_CEQ

! Loop over dof
  KOUNT = 0
  DOF: DO ID = (N_D_FRE/2 + 1), N_D_FRE
    ! Search for corresponding EBC
    IT = ID - N_D_FRE/2
!b print *,'ID, IT = ', ID, IT
    DO IEQ = 1, N_CEQ
      !b print *, 'IEQ, NDEX ', IEQ, NDEX_C_EQ (1, IEQ) 
      IF ( NDEX_C_EQ (1, IEQ) == IT ) THEN
        !b print *, 'COEFF, DD ', COEF_C_EQ (1, IEQ), DD (ID)
        COEF_C_EQ (1, IEQ) = DD (ID)
        KOUNT = KOUNT + 1
        CYCLE DOF
      END IF
    END DO ! over constraints
  END DO DOF ! over half system dof
  IF ( KOUNT /= N_D_FRE/2 ) THEN ! error
    PRINT *, 'WARNING, SPACE_TIME_EBC_UPDATE COUNT IS ', KOUNT
    N_WARN = N_WARN + 1
  END IF

END SUBROUTINE SPACE_TIME_EBC_UPDATE 

SUBROUTINE ONE_STEP_TRANSIENT_AT_EL (K, M, F)
! ..............................................................
!          CONVERT S, EL_M, C INTO K, M, F FOR
!   ASSEMBLE OF ELEMENT MATRICES INTO ALGEBRAIC SYSTEM FOR
!          A ONE STEP TIME INTEGRATION METHOD
! ..............................................................
! So element matrix     S * D (t) + EL_M * D_dot (t) = C (t)
! Converts to algebraic K * D (t) = F (t) with
! K a function of S, EL_M, STEP_SIZE
! M a function of S, EL_M, STEP_SIZE, so that
! F is a function of M * D and C
! Accesses globals: TIME_METHOD, TIME_STEP
Use System_Constants
Use Elem_Type_Data     ! for PT (LT_PARM, LT_QP), WT (LT_QP),
                       ! G (LT_GEOM, LT_QP), H (LT_FREE),
                       ! DLG (LT_PARM, LT_GEOM, LT_QP),
                       ! DLH (LT_PARM, LT_FREE, LT_QP),
                       ! S (LT_FREE, LT_FREE), C (LT_FREE),
                       ! EL_M (LT_FREE, LT_FREE), DIAG_M (LT_FREE),
                       ! D (LT_FREE), ELEM_NODES (LT_N)
IMPLICIT NONE
REAL(DP), INTENT(OUT) :: K (LT_FREE, LT_FREE), F (LT_FREE)
REAL(DP), INTENT(OUT) :: M (LT_FREE, LT_FREE)
REAL(DP) :: Del_t
INTEGER  :: J                   

!          SQUARE MATRICES (STIFFNESS & MASS)
!   S = S + MATMUL (TRANSPOSE(DGH), DGH)* WT (IQ) * DX_DR * K_e
!   EL_M = EL_M + OUTER_PRODUCT (H, H)  * WT (IQ) * DX_DR * Rho_e

   IF ( AVERAGE_MASS ) THEN  ! average consistent & diagonal forms
     CALL DIAGONALIZE_SQ_MATRIX (LT_FREE, EL_M, DIAG_M) 
     EL_M = 0.5d0 * EL_M
     DO J = 1, LT_FREE
       EL_M (J, J) = EL_M (J, J) + DIAG_M (J) * 0.5d0
     END DO
   END IF
   IF ( DIAGONAL_MASS ) THEN  ! use the scaled diagonal mass form
     IF ( DEBUG_EL_SQ ) PRINT *,'BEFORE DIAG_M AT ELEMENT ', THIS_EL
     CALL DIAGONALIZE_SQ_MATRIX (LT_FREE, EL_M, DIAG_M) 
     EL_M = 0.d0
     DO J = 1, LT_FREE
       EL_M (J, J) = DIAG_M (J) 
     END DO
   END IF

    IF ( debug_el_sq .AND. THIS_EL == 1 ) THEN
      PRINT *, 'EL_M, EL_S, EL_C for one step method = ', TIME_METHOD
      CALL RPRINT (EL_M, LT_FREE, LT_FREE, 1)
      CALL RPRINT (S, LT_FREE, LT_FREE, 1)
      CALL RPRINT (C, LT_FREE, 1      , 1)
    END IF

   !b print *,'THIS_EL, D ', THIS_EL, D
   Del_t = TIME_STEP
   SELECT CASE ( TIME_METHOD ) ! for integration rule
     CASE ( 2 ) ! Crank-Nicolson
        F = C 
        M = EL_M / Del_t - S / 2 ! multiplies D
        K = EL_M / Del_t + S / 2
     CASE ( 3 ) ! Galerkin continuous in time
        F = C
        M = EL_M / Del_t - S / 3 ! multiplies D
        K = 2 * S / 3.d0 + EL_M / Del_t
     CASE ( 4 ) ! Least Squares in time, assuming C constant
          ! check if F signs are both - not +
        F = -MATMUL (TRANSPOSE(S),C) * Del_t / 2   &
          -  MATMUL (TRANSPOSE(EL_M),C) 
        M = MATMUL (TRANSPOSE(EL_M), EL_M) / Del_t &
          + MATMUL (TRANSPOSE(S),EL_M) / 2         &
          - MATMUL (TRANSPOSE(EL_M),S) / 2         &
          - MATMUL (TRANSPOSE(S),S) * Del_t / 6
        K = MATMUL (TRANSPOSE(S),S) * Del_t / 3    &
          + MATMUL (TRANSPOSE(S),EL_M) / 2         &
          + MATMUL (TRANSPOSE(EL_M),S) / 2         &
          + MATMUL (TRANSPOSE(EL_M),EL_M) / Del_t 
     CASE DEFAULT ! Method 1, Euler forward difference
        F = C 
        M = EL_M / Del_t 
        K = S + EL_M / Del_t
   END SELECT

    IF ( debug_el_sq .AND. THIS_EL == 1 ) THEN
      PRINT *, 'EL_S, EL_M, K_t, M_t, F_t, D_t for method', TIME_METHOD
      CALL RPRINT (S, LT_FREE, LT_FREE, 1)
      CALL RPRINT (EL_M, LT_FREE, LT_FREE, 1)
      CALL RPRINT (K, LT_FREE, LT_FREE, 1)
      CALL RPRINT (M, LT_FREE, LT_FREE, 1)
      CALL RPRINT (F, LT_FREE, 1      , 1)
      CALL RPRINT (D, LT_FREE, 1      , 1)
    END IF

!   APPLY GENERALIZED TRAPEZOIDAL RULE TO REFORM TWO SQUARE MATRICES
!                     M_SKY * D_DOT + S_SKY * D = C
!                 INTO THE TWO USED IN TIME INTEGRATION
! (M_SKY/T_STEP + TS_B*S_SKY)*D_1 = (M_SKY/T_STEP + TS_A*S_SKY)*D_0
!                                 + TS_B*C_1 + TS_A*C_0
!       BUT, TS_A = 1-TS_B, AND C_0 = C_1 =C, ASSUMED CONSTANT
!         S_SKY_new*D_1 = M_SKY_new*D_0 + TS_B*C_1 + TS_A*C_0
!                       = M_SKY_new*D_0 + C 
!!          NOW FORM CURRENT RHS VECTOR               
!    DO J = 1, N_D_FRE
!      TEMP       = CC (J)
!      CC (J)     = CC (J) * TS_B + CC_OLD (J) * TS_A + C_MD (J)
!      CC_OLD (J) = TEMP
!    END DO ! FOR C_1
END SUBROUTINE ONE_STEP_TRANSIENT_AT_EL
