Nodes of different colours represent the following:
Solid arrows point from a parent (sub)module to the submodule which is descended from it. Dashed arrows point from a module being used to the module or program unit using it. Where possible, edges connecting nodes are given different colours to make them easier to distinguish in large graphs.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | SurfNum | |||
| integer, | intent(in) | :: | NSides | 
Nodes of different colours represent the following:
Solid arrows point from a procedure to one which it calls. Dashed arrows point from an interface to procedures which implement that interface. This could include the module procedures in a generic interface or the implementation in a submodule of an interface in a parent module. Where possible, edges connecting nodes are given different colours to make them easier to distinguish in large graphs.
Nodes of different colours represent the following:
Solid arrows point from a procedure to one which it calls. Dashed arrows point from an interface to procedures which implement that interface. This could include the module procedures in a generic interface or the implementation in a submodule of an interface in a parent module. Where possible, edges connecting nodes are given different colours to make them easier to distinguish in large graphs.
SUBROUTINE CheckConvexity(SurfNum,NSides)
          ! SUBROUTINE INFORMATION:
          !       AUTHOR         Tyler Hoyt
          !       DATE WRITTEN   December 2010
          !       MODIFIED       CR8752 - incorrect note of non-convex polygons
          !       RE-ENGINEERED  na
          ! PURPOSE OF THIS SUBROUTINE: This subroutine verifies the convexity of a
          ! surface that is exposed to the sun in the case that full shading calculations
          ! are required. The calculation conveniently detects collinear points as well,
          ! and returns a list of indices that are collinear within the plane of the surface.
          ! METHODOLOGY EMPLOYED: First the surface is determined to have dimension 2 in
          ! either the xy, yz, or xz plane. That plane is selected to do the testing.
          ! Vectors representing the edges of the polygon and the perpendicular dot product
          ! between adjacent edges are computed. This allows the turning angle to be determined.
          ! If the turning angle is greater than pi/2, it turns to the right, and if it is
          ! less than pi/2, it turns left. The direction of the turn is stored, and if it
          ! changes as the edges are iterated the surface is not convex. Meanwhile it stores
          ! the indices of vertices that are collinear and are later removed.
          ! REFERENCES:
          ! http://mathworld.wolfram.com/ConvexPolygon.html
          ! USE STATEMENTS:
  USE General, ONLY: RoundSigDigits
  USE DataErrorTracking
  IMPLICIT NONE    ! Enforce explicit typing of all variables in this routine
          ! SUBROUTINE ARGUMENT DEFINITIONS:
  INTEGER, INTENT(IN)  :: SurfNum     ! Current surface number
  INTEGER, INTENT(IN)  :: NSides      ! Number of sides to figure
          ! SUBROUTINE PARAMETER DEFINITIONS:
  REAL(r64),PARAMETER :: TurnThreshold = 0.000001d0 ! Sensitivity of convexity test, in radians
  CHARACTER(len=*), PARAMETER :: ErrFmt="(' (',F8.3,',',F8.3,',',F8.3,')')"
          ! INTERFACE BLOCK SPECIFICATIONS
          ! na
          ! DERIVED TYPE DEFINITIONS
          ! na
          ! SUBROUTINE LOCAL VARIABLE DECLARATIONS:
  INTEGER   :: N             ! Loop index
  INTEGER   :: Np1             ! Loop index
  INTEGER   :: Np2             ! Loop index
  REAL(r64) :: Det           ! Determinant for picking projection plane
  REAL(r64) :: DotProd       ! Dot product for determining angle
  REAL(r64) :: Theta         ! Angle between edge vectors
  REAL(r64) :: LastTheta     ! Angle between edge vectors
  REAL(r64) :: V1len         ! Edge vector length
  REAL(r64) :: V2len         ! Edge vector length
  LOGICAL   :: SignFlag      ! Direction of edge turn : .true. is right, .false. is left
  LOGICAL   :: PrevSignFlag  ! Container for the sign of the previous iteration's edge turn
  LOGICAL   :: FirstTimeFlag ! Flag indicating first iteration
  REAL(r64), ALLOCATABLE, SAVE, DIMENSION(:) :: X ! containers for x,y,z vertices of the surface
  REAL(r64), ALLOCATABLE, SAVE, DIMENSION(:) :: Y
  REAL(r64), ALLOCATABLE, SAVE, DIMENSION(:) :: Z
  REAL(r64), ALLOCATABLE, SAVE, DIMENSION(:) :: A    ! containers for convexity test
  REAL(r64), ALLOCATABLE, SAVE, DIMENSION(:) :: B
  INTEGER, ALLOCATABLE, SAVE, DIMENSION(:) :: SurfCollinearVerts ! Array containing indices of collinear vertices
  INTEGER, SAVE :: VertSize  ! size of X,Y,Z,A,B arrays
  REAL(r64) :: cosarg
  INTEGER   :: M             ! Array index for SurfCollinearVerts container
  INTEGER   :: J             ! Loop index
  INTEGER   :: K             ! Loop index
  INTEGER   :: Ind           ! Location of surface vertex to be removed
  LOGICAL, SAVE :: firstTime=.true.
  REAL(r64), SAVE :: ACosZero  ! set on firstTime
  LOGICAL :: SurfCollinearWarning
  CHARACTER(len=132) :: ErrLineOut=Blank ! Character string for producing error messages
  if (firstTime) then
    ACosZero=ACOS(0.0d0)
    ALLOCATE(X(MaxVerticesPerSurface+2))
    ALLOCATE(Y(MaxVerticesPerSurface+2))
    ALLOCATE(Z(MaxVerticesPerSurface+2))
    ALLOCATE(A(MaxVerticesPerSurface+2))
    ALLOCATE(B(MaxVerticesPerSurface+2))
    ALLOCATE(SurfCollinearVerts(MaxVerticesPerSurface))
    VertSize=MaxVerticesPerSurface
    firstTime=.false.
  endif
  if (NSides > VertSize) then
    DEALLOCATE(X)
    DEALLOCATE(Y)
    DEALLOCATE(Z)
    DEALLOCATE(A)
    DEALLOCATE(B)
    DEALLOCATE(SurfCollinearVerts)
    ALLOCATE(X(MaxVerticesPerSurface+2))
    ALLOCATE(Y(MaxVerticesPerSurface+2))
    ALLOCATE(Z(MaxVerticesPerSurface+2))
    ALLOCATE(A(MaxVerticesPerSurface+2))
    ALLOCATE(B(MaxVerticesPerSurface+2))
    ALLOCATE(SurfCollinearVerts(MaxVerticesPerSurface))
    VertSize=MaxVerticesPerSurface
  endif
  DO N = 1, NSides
    X(N) = SurfaceTmp(SurfNum)%Vertex(N)%X
    Y(N) = SurfaceTmp(SurfNum)%Vertex(N)%Y
    Z(N) = SurfaceTmp(SurfNum)%Vertex(N)%Z
  END DO
  X(Nsides+1) = SurfaceTmp(SurfNum)%Vertex(1)%X
  Y(Nsides+1) = SurfaceTmp(SurfNum)%Vertex(1)%Y
  Z(Nsides+1) = SurfaceTmp(SurfNum)%Vertex(1)%Z
  X(Nsides+2) = SurfaceTmp(SurfNum)%Vertex(2)%X
  Y(Nsides+2) = SurfaceTmp(SurfNum)%Vertex(2)%Y
  Z(Nsides+2) = SurfaceTmp(SurfNum)%Vertex(2)%Z
  ! Determine a suitable plane in which to do the tests
  Det = 0.0d0
  DO N = 1, NSides
    Det = Det + X(N)*Y(N+1) - X(N+1)*Y(N)
  END DO
  IF (ABS(Det) > 1.d-4) THEN
    A = X
    B = Y
  ELSE
    Det = 0.0d0
    DO N = 1, NSides
      Det = Det + X(N)*Z(N+1) - X(N+1)*Z(N)
    END DO
    IF (ABS(Det) > 1.d-4) THEN
      A = X
      B = Z
    ELSE
      Det = 0.0d0
      DO N = 1, NSides
        Det = Det + Y(N)*Z(N+1) - Y(N+1)*Z(N)
      END DO
      IF (ABS(Det) > 1.d-4) THEN
        A = Y
        B = Z
      ELSE
        ! This condition should not be reached if the surfaces are guaranteed to be planar already
        CALL ShowSevereError('CheckConvexity: Surface="'//TRIM(SurfaceTmp(SurfNum)%Name)//  &
          '" is non-planar.')
        CALL ShowContinueError('Coincident Vertices will be removed as possible.')
        DO N=1,SurfaceTmp(SurfNum)%Sides
          WRITE(ErrLineOut,ErrFmt) SurfaceTmp(SurfNum)%Vertex(N)
          CALL ShowContinueError(ErrLineOut)
        ENDDO
      END IF
    END IF
  END IF
  M = 0
  FirstTimeFlag = .true.
  SurfCollinearWarning=.false.
  DO N = 1, NSides   ! perform convexity test in the plane determined above.
    DotProd = (A(N+1)-A(N))*(B(N+2)-B(N+1))-(B(N+1)-B(N))*(A(N+2)-A(N+1))
    V1len = SQRT((A(N+1)-A(N))**2+(B(N+1)-B(N))**2)
    V2len = SQRT((A(N+2)-A(N+1))**2+(B(N+2)-B(N+1))**2)
    IF (V1Len <= 1.d-8 .or. V2Len <= 1.d-8) CYCLE
    cosarg=DotProd/(V1len * V2len)
    if (cosarg < -1.0d0) then
      cosarg=-1.0d0
    elseif (cosarg > 1.0d0) then
      cosarg=1.0d0
    endif
    Theta   = ACOS(cosarg)
    IF (Theta < (ACosZero-TurnThreshold)) THEN
      SignFlag = .true.
    ELSE
      IF (Theta > (ACosZero+TurnThreshold)) THEN
        SignFlag = .false.
      ELSE   ! Store the index of the collinear vertex for removal
        IF (.not. SurfCollinearWarning) THEN
          IF (DisplayExtraWarnings) THEN
            CALL ShowWarningError('CheckConvexity: Surface="'//TRIM(SurfaceTmp(SurfNum)%Name)//  &
               '", Collinear points have been removed.')
          ENDIF
          SurfCollinearWarning=.true.
        ENDIF
        TotalCoincidentVertices=TotalCoincidentVertices+1
        M = M + 1
        SurfCollinearVerts(M) = N + 1
        CYCLE
      END IF
    END IF
    IF (FirstTimeFlag) THEN
      PrevSignFlag = SignFlag
      FirstTimeFlag = .false.
      CYCLE
    END IF
    IF (SignFlag .neqv. PrevSignFlag) THEN
      IF (SolarDistribution /= MinimalShadowing .and. SurfaceTmp(SurfNum)%ExtSolar) THEN
        IF (DisplayExtraWarnings) THEN
          CALL ShowWarningError('CheckConvexity: Zone="'//trim(Zone(SurfaceTmp(SurfNum)%Zone)%Name)//  &
             '", Surface="'//TRIM(SurfaceTmp(SurfNum)%Name)//'" is non-convex.')
          Np1=N+1
          IF (Np1 > NSides) Np1=Np1-NSides
          Np2=N+2
          IF (Np2 > NSides) Np2=Np2-NSides
          CALL ShowContinueError('...vertex '//trim(RoundSigDigits(N))//' to vertex '//trim(RoundSigDigits(Np1))//  &
             ' to vertex '//trim(RoundSigDigits(Np2)))
          CALL ShowContinueError('...vertex '//trim(RoundSigDigits(N))//'=['//  &
             trim(RoundSigDigits(X(N),2))//','//  &
             trim(RoundSigDigits(Y(N),2))//','//  &
             trim(RoundSigDigits(Z(N),2))//']')
          CALL ShowContinueError('...vertex '//trim(RoundSigDigits(Np1))//'=['//  &
             trim(RoundSigDigits(X(N+1),2))//','//  &
             trim(RoundSigDigits(Y(N+1),2))//','//  &
             trim(RoundSigDigits(Z(N+1),2))//']')
          CALL ShowContinueError('...vertex '//trim(RoundSigDigits(Np2))//'=['//  &
             trim(RoundSigDigits(X(N+2),2))//','//  &
             trim(RoundSigDigits(Y(N+2),2))//','//  &
             trim(RoundSigDigits(Z(N+2),2))//']')
!          CALL ShowContinueError('...theta angle=['//trim(RoundSigDigits(Theta,6))//']')
!          CALL ShowContinueError('...last theta angle=['//trim(RoundSigDigits(LastTheta,6))//']')
        ENDIF
        SurfaceTmp(SurfNum)%IsConvex=.false.
        EXIT
      END IF
    END IF
    PrevSignFlag = SignFlag
    LastTheta=Theta
  END DO
  ! must check to make sure don't remove NSides below 3
  IF (M > 0) THEN ! Remove the collinear points determined above
    IF (NSides-M > 2) THEN
      SurfaceTmp(SurfNum)%Sides = NSides - M
    ELSE  ! too many
      IF (DisplayExtraWarnings) THEN
        CALL ShowWarningError('CheckConvexity: Surface="'//TRIM(SurfaceTmp(SurfNum)%Name)//'" has ['//  &
             trim(RoundSigDigits(M))//'] collinear points.')
        CALL ShowContinueError('...too many to remove all.  Will leave the surface with 3 sides. '//  &
           'But this is now a degenerate surface')
      ENDIF
      TotalDegenerateSurfaces=TotalDegenerateSurfaces+1
      SurfaceTmp(SurfNum)%Sides = MAX(NSides-M,3)
      M=NSides-SurfaceTmp(SurfNum)%Sides
    ENDIF
    DO J = 1, M
      Ind = SurfCollinearVerts(J)
      DO K = Ind, NSides - J
        SurfaceTmp(SurfNum)%Vertex(K-J+1)%X = SurfaceTmp(SurfNum)%Vertex(K-J+2)%X
        SurfaceTmp(SurfNum)%Vertex(K-J+1)%Y = SurfaceTmp(SurfNum)%Vertex(K-J+2)%Y
        SurfaceTmp(SurfNum)%Vertex(K-J+1)%Z = SurfaceTmp(SurfNum)%Vertex(K-J+2)%Z
      END DO
    END DO
  END IF
END SUBROUTINE CheckConvexity