C    Copyright(C) 1999-2020 National Technology & Engineering Solutions
C    of Sandia, LLC (NTESS).  Under the terms of Contract DE-NA0003525 with
C    NTESS, the U.S. Government retains certain rights in this software.
C
C    See packages/seacas/LICENSE for details

      SUBROUTINE APALSM (MXND, XN, YN, LXK, KXL, NXL, LXN, NNN, NNNOLD,
     &   NIT, TOL, RO, ALPHA, ERR)
C***********************************************************************

C  SUBROUTINE APALSM = AREA PULL AND LAPLACIAN MESH SMOOTHER

C***********************************************************************

C  NOTE:
C     IN THIS SMOOTHER EACH NODE IS SUCCESSIVELY MOVED BY
C     AN AMOUNT GIVEN AS A WEIGHTED AVERAGE OF AN *AREA PULL*
C     VECTOR AND THE LAPLACIAN VECTOR (AVERAGE OF VECTORS POINTING
C     TO NEIGHBORS).  THE *AREA PULL* VECTOR IS OBTAINED BY LETTING
C     EACH ELEMENT PULL IN PERPENDICULARLY ON ITS SIDES WITH FORCE
C     PROPORTIONAL TO THE LENGTH OF THAT SIDE TIMES A FACTOR
C     INVOLVING THE AREAS OF THIS ELEMENT AND ITS NEIGHBOR SHARING
C     THAT SIDE.

C***********************************************************************

C  VARIABLES USED:
C     NIT   = MAX ITERATIONS TO DO
C     TOL   = PERCENT OF SMALLEST CONNECTING LINE TO USE AS NODE MOVEMENT
C             CONVERGENCE TOLERANCE.
C     RO    = UNDER OR OVER-RELAXATION FACTOR.
C     ALPHA = WEIGHT GIVEN TO AREA PULL VECTOR.  USUALLY = 0.5.
C             WEIGHT GIVEN TO LAPLACIAN VECTOR = 1.-ALPHA.

C***********************************************************************

      DIMENSION LXK(4, MXND), KXL(2, 3*MXND), NXL(2, 3*MXND)
      DIMENSION LXN(4, MXND), XN(MXND), YN(MXND)
      DIMENSION LINES(20), NS1(4), NS2(4)
      DIMENSION KLIB(8), NLIB(4, 8), ALIB(8), XCLIB(8), YCLIB(8)

      LOGICAL BIG, ERR

      ERR = .FALSE.
      TOL2 = TOL**2
      BETA = 1. - ALPHA

C  ITERATION LOOP

      DO 160 IT = 1, NIT
         BIG = .FALSE.

C  NODE LOOP

         NNT = 0
         DO 150 NODE = NNNOLD  +  1, NNN

C  CHECK FOR CONTINUATIONS,  BOUNDARY,  OR RELAXED NODE

            IF ((LXN(3, NODE) .GE.  0) .AND. (LXN(2, NODE) .GT. 0)
     &         .AND. (LXN(1, NODE) .GT. 0)) THEN
               NNT = NNT + 1

C  INITIALIZE

               KNUM = 0
               XA = 0.
               YA = 0.
               XL = 0.
               YL = 0.

C  PROCESS EACH LINE CONNECTED TO THIS NODE

               CALL GETLXN (MXND, LXN, NODE, LINES, KOUNT, ERR)
               IF (ERR) RETURN
               DO 100 IL = 1, KOUNT
                  L = LINES(IL)
                  N1 = NXL(1, L)
                  N2 = NXL(2, L)

C  FETCH ELEMENT DATA

                  IF (KXL(1, L) .GT. 0) CALL APALIB (MXND, XN, YN, LXK,
     &               NXL, KXL(1, L), NS1, AREA1, XCEN1, YCEN1, KNUM,
     &               KLIB, NLIB, ALIB, XCLIB, YCLIB)
                  IF (KXL(2, L) .GT. 0) CALL APALIB (MXND, XN, YN, LXK,
     &               NXL, KXL(2, L), NS2, AREA2, XCEN2, YCEN2, KNUM,
     &               KLIB, NLIB, ALIB, XCLIB, YCLIB)

C  GET FORCE DIRECTION MODULO PI RADIANS.
C  CORRECT FOR WRONG DIRECTION BY ALIGNING WITH THE VECTOR
C  FROM (XCEN1, YCEN1) TO (XCEN2, YCEN2).

                  XF = -(YN(N2) - YN(N1))
                  YF = XN(N2) - XN(N1)
                  DOT = XF*(XCEN2 - XCEN1) + YF*(YCEN2 - YCEN1)
                  IF (DOT  .LT.  0.) THEN
                     XF = -XF
                     YF = -YF
                  END IF

C  TAKE CARE OF ZERO AREAS

                  IF ((AREA1  .LE.  0) .OR. (AREA2  .LE.  0)) THEN
                     AREA1 = 1.0
                     AREA2 = 1.0
                  END IF

C  UPDATE AREA PULL VECTOR SUM

                  WEIGHT = (AREA2 - AREA1)/(AREA2 + AREA1)
                  XA = XA  +  WEIGHT*XF
                  YA = YA  +  WEIGHT*YF

C  UPDATE LAPLACIAN VECTOR SUM

                  NOE = N1 + N2 - NODE
                  DX = XN(NOE) - XN(NODE)
                  DY = YN(NOE) - YN(NODE)
                  XL = XL + DX
                  YL = YL + DY

C  UPDATE LEAST LENGTH

                  DIST2 = DX*DX  +  DY*DY
                  IF (IL .EQ. 1) DMIN2 = DIST2
                  DMIN2 = MIN(DMIN2, DIST2)
  100          CONTINUE

C  COMPUTE NET MOVEMENT VECTOR

               RK = 1.0/DBLE(KOUNT)
               XNET = (ALPHA*XA  +  BETA*XL)*RK
               YNET = (ALPHA*YA  +  BETA*YL)*RK

C  MOVE THE NODE

               YN(NODE) = YN(NODE)  +  YNET * RO
               XN(NODE) = XN(NODE)  +  XNET * RO

C  CHECK FOR SIGNIFICANT MOVEMENT

               IF (XNET*XNET + YNET*YNET .GT. TOL2*DMIN2) THEN

C  SIGNIFICANT MOVEMENT - REMOVE RELAXATION FLAGS

C  FIRST FROM DIRECTLY CONNECTED NODES

                  DO 110 IL = 1, KOUNT
                     L = LINES(IL)
                     NOE = NXL(1, L) + NXL(2, L) - NODE
                     LXN(3, NOE) = ABS(LXN(3, NOE))
  110             CONTINUE

C  NEXT FROM DIAGONALLY OPPOSITE NODES (MAX 8)

                  DO 140 IK = 1, KNUM
                     DO 120 I = 1, 4
                        IF (NLIB(I, IK) .EQ. NODE) THEN
                           IDIAG = I + 2
                           IF (IDIAG .GE.  5) IDIAG = IDIAG - 4
                           NDIAG = NLIB(IDIAG, IK)
                           LXN(3, NDIAG) = ABS(LXN(3, NDIAG))
                           GO TO 130
                        END IF
  120                CONTINUE
                     CALL MESSAGE('ERROR IN APALSM')
                     ERR = .TRUE.
                     RETURN
  130                CONTINUE
  140             CONTINUE

C  INSIGNIFICANT MOVEMENT
C  INSERT RELAXATION FLAG

               ELSE
                  LXN(3, NODE) = -ABS(LXN(3, NODE))
               END IF

            END IF
  150    CONTINUE
         IF (.NOT.BIG) GO TO 170
  160 CONTINUE
      IT = NIT

C  REMOVE ALL FLAGS

  170 CONTINUE
      DO 180 NODE = NNNOLD  +  1, NNN
         LXN(3, NODE) = ABS(LXN(3, NODE))
  180 CONTINUE

      RETURN
      END