I.1.3 Initialize_Orthogonal_Multi_Mesh Procedure

The main documentation of the Initialize_Orthogonal_Multi_Mesh Procedure contains additional explanation of this code listing.

  subroutine Initialize_Orthogonal_MMesh (Mesh, NDimensions, &
    Coordinates_Nodes_X, Coordinates_Nodes_Y, Coordinates_Nodes_Z, &
    Mesh_Name, status)

    ! Input variables.

    type(integer), intent(in) :: NDimensions      ! Number of Dimensions.
    type(real,1)           :: Coordinates_Nodes_X ! X-coordinates of the nodes.
    type(real,1), optional :: Coordinates_Nodes_Y ! Y-coordinates of the nodes.
    type(real,1), optional :: Coordinates_Nodes_Z ! Z-coordinates of the nodes.
    type(character,*), intent(in), optional :: Mesh_Name ! Mesh name.

    ! Output variables.

    ! Multi_Mesh to be initialized.
    type(Multi_Mesh_type), intent(inout) :: Mesh 
    type(Status_type), intent(out), optional :: status  ! Exit status.

    ! Internal variables.

    ! Total number of cells in the X-, Y-, and Z-directions.
    type(integer) :: NCells_X_total, NCells_Y_total, NCells_Z_total
    type(character,name_length) :: Shape         ! Cell shape.
    type(character,name_length) :: Geometry      ! Cell geometry (Cartesian).
    type(character,name_length) :: Uniformity    ! Set to "Uniform".
    type(character,name_length) :: Orthogonality ! Set to "Orthogonal".
    type(character,name_length) :: Structure     ! Set to "Structured".
    type(logical)               :: AMR           ! Set to false.
    type(Status_type), dimension(20) :: allocate_status  ! Allocation Status.
    type(Status_type) :: consolidated_status             ! Consolidated Status.
    type(integer) :: NPEs_X ! Number of PEs in X.
    type(integer) :: NPEs_Y ! Number of PEs in Y.
    type(integer) :: NPEs_Z ! Number of PEs in Z.
    type(real,1) :: Lengths      ! Physical extent of the 
                                 ! domain in each direction.

    ! Structure length vectors, which give numbers for all PEs.
    type(integer,1) :: NCells_Vector   ! Number of cells.
    type(integer,1) :: NCells_X_Vector ! Number of cells in the X direction.
    type(integer,1) :: NCells_Y_Vector ! Number of cells in the Y direction.
    type(integer,1) :: NCells_Z_Vector ! Number of cells in the Z direction.
    type(integer,1) :: NFaces_Vector   ! Number of faces.
    type(integer,1) :: NNodes_Vector   ! Number of nodes.
    type(integer,1) :: NNodes_X_Vector ! Number of nodes in the X direction.
    type(integer,1) :: NNodes_Y_Vector ! Number of nodes in the Y direction.
    type(integer,1) :: NNodes_Z_Vector ! Number of nodes in the Z direction.

    ! Location of this_PE in the PE-mesh.
    type(integer) :: this_PE_X, this_PE_Y, this_PE_Z
    type(integer) :: node, pe_x, pe_y, pe_z     ! Loop parameters.

    ! Offsets - starting points for this PE.
    type(integer) :: Offset_PE_X, Offset_PE_Y, Offset_PE_Z ! 1st node indices.

    ! Mesh coordinates and indices.

    ! The coordinates of the nodes on this PE.
    type(real,2) :: Coordinates_Nodes_PE
    ! The nodes for the cells on this PE.
    type(integer,2) :: Nodes_of_Cells_PE
    ! The cells for the cells (that is, across each face) on this PE.
    type(integer,2) :: Cells_of_Cells_PE
    ! Face Flags for Structured Meshes.
    type(integer,2) :: Flag_Faces_of_Cells

    !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    ! Verify requirements.

    ! Mesh type info is valid.
    VERIFY(NDimensions .InInterval. (/1, 3/),5)

    ! Allocations and initializations.

    call Initialize (allocate_status)
    call Initialize (consolidated_status)

    ! Set NCells_D_total and physical dimensions of the mesh - Lengths.

    call Initialize (Lengths, NDimensions, allocate_status(1))
    select case (NDimensions)
    case (1)
      NCells_X_total = SIZE(Coordinates_Nodes_X) - 1
      NCells_Y_total = 1
      NCells_Z_total = 1
      Lengths(1) = Coordinates_Nodes_X(SIZE(Coordinates_Nodes_X)) &
                 - Coordinates_Nodes_X(1)
    case (2)
      NCells_X_total = SIZE(Coordinates_Nodes_X) - 1
      NCells_Y_total = SIZE(Coordinates_Nodes_Y) - 1
      NCells_Z_total = 1
      Lengths(1) = Coordinates_Nodes_X(SIZE(Coordinates_Nodes_X)) &
                 - Coordinates_Nodes_X(1)
      Lengths(2) = Coordinates_Nodes_Y(SIZE(Coordinates_Nodes_Y)) &
                 - Coordinates_Nodes_Y(1)
    case (3)
      NCells_X_total = SIZE(Coordinates_Nodes_X) - 1
      NCells_Y_total = SIZE(Coordinates_Nodes_Y) - 1
      NCells_Z_total = SIZE(Coordinates_Nodes_Z) - 1
      Lengths(1) = Coordinates_Nodes_X(SIZE(Coordinates_Nodes_X)) &
                 - Coordinates_Nodes_X(1)
      Lengths(2) = Coordinates_Nodes_Y(SIZE(Coordinates_Nodes_Y)) &
                 - Coordinates_Nodes_Y(1)
      Lengths(3) = Coordinates_Nodes_Z(SIZE(Coordinates_Nodes_Z)) &
                 - Coordinates_Nodes_Z(1)
    end select

    ! Generate the connectivity.

    call Gen_StructureMesh_Connectivity (NDimensions, Lengths, &
      NCells_X_total, NCells_Y_total, NCells_Z_total, Shape, Structure, AMR, &
      NPEs_X, NPEs_Y, NPEs_Z, this_PE_X, this_PE_Y, this_PE_Z, &
      NNodes_Vector, NCells_Vector, NFaces_Vector, &
      NCells_X_Vector, NCells_Y_Vector, NCells_Z_Vector, &
      NNodes_X_Vector, NNodes_Y_Vector, NNodes_Z_Vector, &
      Nodes_of_Cells_PE, Cells_of_Cells_PE, Flag_Faces_of_Cells, &
      allocate_status(2))

    ! Set mesh type info for an Orthogonal Mesh.

    Geometry = 'Cartesian'
    Uniformity = 'Nonuniform'
    Orthogonality = 'Orthogonal'

    ! Set Offsets for coordinates on this PE. Note that this is
    ! the integer offset of the indices, not the physical distance
    ! offset as is used in the Uniform mesh initialization.
    
    if (this_PE_X == 1) then
      Offset_PE_X   = 0
    else
      Offset_PE_X   = SUM(NCells_X_Vector(1:this_PE_X-1))
    end if
    if (NDimensions >= 2) then
      if (this_PE_Y == 1) then
        Offset_PE_Y = 0
      else
        Offset_PE_Y = SUM(NCells_Y_Vector(1:this_PE_Y-1))
      end if
    end if
    if (NDimensions == 3) then
      if (this_PE_Z == 1) then
        Offset_PE_Z = 0
      else
        Offset_PE_Z = SUM(NCells_Z_Vector(1:this_PE_Z-1))
      end if
    end if

    ! Set Node Coordinates on this PE. 

    call Initialize (Coordinates_Nodes_PE, NDimensions, &
                     NNodes_Vector(this_PE), allocate_status(3))
    node = 1
    do pe_z = 1, NNodes_Z_Vector(this_PE_Z)
      do pe_y = 1, NNodes_Y_Vector(this_PE_Y)
        do pe_x = 1, NNodes_X_Vector(this_PE_X)
          Coordinates_Nodes_PE(1,node)   = &
              Coordinates_Nodes_X(Offset_PE_X + pe_x)
          if (NDimensions >= 2) then
            Coordinates_Nodes_PE(2,node) = &
              Coordinates_Nodes_Y(Offset_PE_Y + pe_y)
          end if
          if (NDimensions == 3) then
            Coordinates_Nodes_PE(3,node) = &
              Coordinates_Nodes_Z(Offset_PE_Z + pe_z)
          end if

          ! Increment node.
          node = node + 1

        end do
      end do
    end do
    VERIFY((node-1)==NNodes_Vector(this_PE),5)

    ! Initialize the Multi-Mesh object.

    call Initialize_Base_Multi_Mesh (Mesh, NDimensions, Geometry, &
      Uniformity, Orthogonality, Structure, AMR, Shape, &
      NNodes_Vector, NCells_Vector, NFaces_Vector, &
      Coordinates_Nodes_PE, Nodes_of_Cells_PE, Mesh_Name, &
      allocate_status(4))

    ! Set physical dimensions of the mesh (Lengths).

    call Initialize (Mesh%Lengths, NDimensions, allocate_status(5))
    Mesh%Lengths = Lengths

    ! Set Mesh%Cells_of_Cells_Index and Mesh%Flag_Faces_of_Cells.

    call Initialize (Mesh%Cells_of_Cells_Index, Mesh%Cell_Structure, &
                     Mesh%Cell_Structure, &
                     Many_of_One_Array=Cells_of_Cells_PE, &
                     status=allocate_status(6))

    call Initialize (Mesh%Flag_Faces_of_Cells, NCells_Vector(this_PE), &
                     NDimensions*2, allocate_status(7))
    Mesh%Flag_Faces_of_Cells = Flag_Faces_of_Cells

    ! Finalize temporary variables.

    call Finalize (Cells_of_Cells_PE, allocate_status(8))
    call Finalize (Coordinates_Nodes_PE, allocate_status(9))
    call Finalize (Flag_Faces_of_Cells, allocate_status(10))
    call Finalize (NCells_Vector, allocate_status(11))
    call Finalize (NCells_X_Vector, allocate_status(12))
    call Finalize (NCells_Y_Vector, allocate_status(13))
    call Finalize (NCells_Z_Vector, allocate_status(14))
    call Finalize (NFaces_Vector, allocate_status(15))
    call Finalize (NNodes_Vector, allocate_status(16))
    call Finalize (NNodes_X_Vector, allocate_status(17))
    call Finalize (NNodes_Y_Vector, allocate_status(18))
    call Finalize (NNodes_Z_Vector, allocate_status(19))
    call Finalize (Nodes_of_Cells_PE, allocate_status(20))

    ! Consolidate and handle status.

    consolidated_status = allocate_status
    if (PRESENT(status)) then
      WARN_IF(Error(consolidated_status), 5)
      status = consolidated_status
    else
      VERIFY(Normal(consolidated_status), 5)
    end if
    call Finalize (consolidated_status)
    call Finalize (allocate_status)

  end subroutine Initialize_Orthogonal_MMesh

  subroutine Gen_StructureMesh_Connectivity (NDimensions, Lengths, &
      NCells_X_total, NCells_Y_total, NCells_Z_total, Shape, Structure, AMR, &
      NPEs_X, NPEs_Y, NPEs_Z, this_PE_X, this_PE_Y, this_PE_Z, &
      NNodes_Vector, NCells_Vector, NFaces_Vector, &
      NCells_X_Vector, NCells_Y_Vector, NCells_Z_Vector, &
      NNodes_X_Vector, NNodes_Y_Vector, NNodes_Z_Vector, &
      Nodes_of_Cells_PE, Cells_of_Cells_PE, Flag_Faces_of_Cells, status)

    ! Use associations.

    use Caesar_Mathematics_Module

    !~~~~~~~~~~~~~~~~~
    ! Input variables.
    !~~~~~~~~~~~~~~~~~

    ! Mesh type information.

    type(integer), intent(in) :: NDimensions    ! Number of Dimensions.
    type(real,1,np), intent(in) :: Lengths      ! Physical extent of the 
                                                ! domain in each direction.
    ! Total number of cells in the X-, Y-, and Z-directions.
    type(integer), intent(in)              :: NCells_X_total
    type(integer), intent(inout), optional :: NCells_Y_total
    type(integer), intent(inout), optional :: NCells_Z_total

    !~~~~~~~~~~~~~~~~~~
    ! Output variables.
    !~~~~~~~~~~~~~~~~~~

    type(Status_type), intent(out), optional :: status  ! Exit status.
    type(character,name_length), intent(out) :: Shape     ! Cell shape.
    type(character,name_length), intent(out) :: Structure ! Set to Structured.
    type(logical), intent(out)               :: AMR       ! Set to false.

    ! Structure length vectors, which give numbers for all PEs.
    type(integer,1) :: NCells_Vector   ! Number of cells.
    type(integer,1) :: NFaces_Vector   ! Number of faces.
    type(integer,1) :: NNodes_Vector   ! Number of nodes.
    type(integer,1) :: NCells_X_Vector ! Number of cells in the X direction.
    type(integer,1) :: NCells_Y_Vector ! Number of cells in the Y direction.
    type(integer,1) :: NCells_Z_Vector ! Number of cells in the Z direction.
    type(integer,1) :: NNodes_X_Vector ! Number of nodes in the X direction.
    type(integer,1) :: NNodes_Y_Vector ! Number of nodes in the Y direction.
    type(integer,1) :: NNodes_Z_Vector ! Number of nodes in the Z direction.

    type(integer), intent(out) :: NPEs_X ! Number of PEs in X.
    type(integer), intent(out) :: NPEs_Y ! Number of PEs in Y.
    type(integer), intent(out) :: NPEs_Z ! Number of PEs in Z.
    ! Location of this_PE in the PE-mesh.
    type(integer), intent(out) :: this_PE_X, this_PE_Y, this_PE_Z

    ! The nodes for the cells on this PE.
    type(integer,2) :: Nodes_of_Cells_PE
    ! The cells for the cells (that is, across each face) on this PE.
    type(integer,2) :: Cells_of_Cells_PE

    ! Face Flags for Structured Meshes. This is set to: 0 for internal, 
    ! 1 for left (-x), 2 for right (+x), 3 for front (-y), 4 for back (+y), 
    ! 5 for bottom (-z), 6 for top (+z).
    type(integer,2) :: Flag_Faces_of_Cells

    !~~~~~~~~~~~~~~~~~~~~
    ! Internal variables.
    !~~~~~~~~~~~~~~~~~~~~

    type(Status_type), dimension(18) :: allocate_status  ! Allocation Status.
    type(Status_type) :: consolidated_status             ! Consolidated Status.
    type(integer), dimension(32) :: NPE_Factors  ! Prime factors of NPEs.
    type(integer) :: NNPE_Factors                ! Number of NPE Factors.

    ! Structure length vectors, which give numbers for all PEs.
    type(integer) :: NCells_total      ! Total number of cells on all PEs.
    type(integer) :: NFaces_total      ! Total number of faces on all PEs.
    type(integer) :: NNodes_total      ! Total number of nodes on all PEs.

    type(integer) :: i, j, k                             ! Loop parameters.
    type(integer) :: cell, cell_x, cell_y, cell_z        ! Loop parameters.
    type(integer) :: pe, pe_x, pe_y, pe_z                ! Loop parameters.
    ! Nodes_of_Cells evaluation point.
    type(integer) :: node_cell_x, node_cell_y, node_cell_z, node_PE 
    type(integer) :: node_PE_X, node_PE_Y, node_PE_Z
    ! Cells_of_Cells evaluation point.
    type(integer) :: other_cell_x, other_cell_y, other_cell_z, other_cell_PE 
    type(integer) :: other_cell_PE_X, other_cell_PE_Y, other_cell_PE_Z

    ! Offsets - starting points for this PE.

    type(integer,1) :: Offset_Cells_Vector ! Global number of first cell.
    type(integer,1) :: Offset_Faces_Vector ! Global number of first face.
    type(integer,1) :: Offset_Nodes_Vector ! Global number of first node.

    !~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    ! Verify requirements.

    ! Mesh type info is valid.
    VERIFY(NDimensions .InInterval. (/1, 3/),5)
    VERIFY(SIZE(Lengths) == NDimensions,5)   ! Lengths is correct size.

    ! Allocations and initializations.

    call Initialize (allocate_status)
    call Initialize (consolidated_status)

    ! Initialize vectors.

    call Initialize (NCells_Vector,       NPEs, allocate_status(1))
    call Initialize (NFaces_Vector,       NPEs, allocate_status(2))
    call Initialize (NNodes_Vector,       NPEs, allocate_status(3))
    call Initialize (Offset_Cells_Vector, NPEs, allocate_status(4))
    call Initialize (Offset_Faces_Vector, NPEs, allocate_status(5))
    call Initialize (Offset_Nodes_Vector, NPEs, allocate_status(6))

    ! Set Shape, NCells_total based on NDimensions.

    select case (NDimensions)
    case (1)
      Shape = "Segmented    "
      NCells_total = NCells_X_total
      NCells_Y_total = 1
      NCells_Z_total = 1
      NFaces_total = NCells_total + 1
      NNodes_total = NCells_total + 1
    case (2)
      Shape = "Quadrilateral"
      NCells_total = NCells_X_total * NCells_Y_total
      NCells_Z_total = 1
      NFaces_total = 2*NCells_total + NCells_X_total + NCells_Y_total
      NNodes_total = (NCells_X_total + 1) * (NCells_Y_total + 1)
    case (3)
      Shape = "Hexahedral   "
      NCells_total = NCells_X_total * NCells_Y_total * NCells_Z_total
      NFaces_total = 3*NCells_total + NCells_X_total * NCells_Y_total  &
                              + NCells_Y_total * NCells_Z_total  &
                              + NCells_Z_total * NCells_X_total
      NNodes_total = (NCells_X_total + 1) * &
               (NCells_Y_total + 1) * &
               (NCells_Z_total + 1)
    end select

    ! Set mesh type info for a Structured Mesh.

    AMR = .false.
    Structure = 'Structured'

    ! Determine parallel distributions: the number of PEs in each 
    ! direction. The method used is to divide the problem between PEs so 
    ! that the PE blocks look like a structured mesh with dimensions 
    ! NPEs_X x NPEs_Y x NPEs_Z. Each block will have roughly the same number 
    ! of cells, so load-balancing will be good (in 3D, each block will have 
    ! roughly NCells_PE = (NCells_X_total/NPEs_X) * (NCells_Y_total/NPEs_Y) * 
    ! (NCells_Z_total/NPEs_Z) ).
    !
    ! To balance potential communication costs, the number of PEs in each 
    ! direction are chosen to keep the physical dimensions of the blocks as 
    ! equal as possible. The algorithm chosen is to prime factorize the 
    ! total NPEs, then to iteratively add the largest remaining factor to the 
    ! direction with the largest value of Length/NPEs until all the factors 
    ! are used. This should make the blocks more equal (cubical), but it may 
    ! not be the absolute optimal method. An additional requirement is that 
    ! the NPEs in any direction must remain less than or equal to the number 
    ! of cells in that direction. 
    !
    ! Better partitioning could be done if the overlying physics were known, 
    ! however the mesh has no knowledge of the physics, and could indeed be 
    ! used with different physics at different times.

    if (NDimensions /= 1) then
      call Prime_Factors (NPEs, NNPE_Factors, NPE_Factors)
    end if
    select case (NDimensions)
    case (1)
      NPEs_X = NPEs
      NPEs_Y = 1
      NPEs_Z = 1
      VERIFY(NPEs_X <= NCells_X_total,5)
    case (2)
      NPEs_X = 1
      NPEs_Y = 1
      NPEs_Z = 1
      do while (NNPE_Factors > 0)

        ! Increase NPEs of direction with largest Length/NPEs, unless
        ! that would make NPEs > NCells in that direction.

        if (Lengths(1)/NPEs_X > Lengths(2)/NPEs_Y .and. &
            NPEs_X*NPE_Factors(NNPE_Factors) <= NCells_X_total) then
          NPEs_X = NPEs_X * NPE_Factors(NNPE_Factors)
          NNPE_Factors = NNPE_Factors - 1
        else if (NPEs_Y*NPE_Factors(NNPE_Factors) <= NCells_Y_total) then
          NPEs_Y = NPEs_Y * NPE_Factors(NNPE_Factors)
          NNPE_Factors = NNPE_Factors - 1

        ! If unsuccessful, then try to increase NPEs in other directions, 
        ! retaining condition that NPEs must be less than or equal to 
        ! NCells in that direction.

        else if (NPEs_X*NPE_Factors(NNPE_Factors) <= NCells_X_total) then
          NPEs_X = NPEs_X * NPE_Factors(NNPE_Factors)
          NNPE_Factors = NNPE_Factors - 1

        ! This condition is only reached if this mesh cannot be split up 
        ! on the required number of PEs without leaving some PEs with 
        ! no cells.

        else
          VERIFY(.false.,1)
        end if
      end do
    case (3)
      NPEs_X = 1
      NPEs_Y = 1
      NPEs_Z = 1
      do while (NNPE_Factors > 0)

        ! Increase NPEs of direction with largest Length/NPEs, unless
        ! that would make NPEs > NCells in that direction.

        if (Lengths(1)/NPEs_X > Lengths(2)/NPEs_Y .and. &
            Lengths(1)/NPEs_X > Lengths(3)/NPEs_Z .and. &
            NPEs_X*NPE_Factors(NNPE_Factors) <= NCells_X_total) then
          NPEs_X = NPEs_X * NPE_Factors(NNPE_Factors)
          NNPE_Factors = NNPE_Factors - 1
        else if (Lengths(2)/NPEs_Y > Lengths(1)/NPEs_X .and. &
                 Lengths(2)/NPEs_Y > Lengths(3)/NPEs_Z .and. &
                 NPEs_Y*NPE_Factors(NNPE_Factors) <= NCells_Y_total) then
          NPEs_Y = NPEs_Y * NPE_Factors(NNPE_Factors)
          NNPE_Factors = NNPE_Factors - 1
        else if (NPEs_Z*NPE_Factors(NNPE_Factors) <= NCells_Z_total) then
          NPEs_Z = NPEs_Z * NPE_Factors(NNPE_Factors)
          NNPE_Factors = NNPE_Factors - 1

        ! If unsuccessful, then try to increase NPEs in other directions, 
        ! retaining condition that NPEs must be less than or equal to 
        ! NCells in that direction.

        else if (NPEs_X*NPE_Factors(NNPE_Factors) <= NCells_X_total) then
          NPEs_X = NPEs_X * NPE_Factors(NNPE_Factors)
          NNPE_Factors = NNPE_Factors - 1
        else if (NPEs_Y*NPE_Factors(NNPE_Factors) <= NCells_Y_total) then
          NPEs_Y = NPEs_Y * NPE_Factors(NNPE_Factors)
          NNPE_Factors = NNPE_Factors - 1

        ! This condition is only reached if this mesh cannot be split up 
        ! on the required number of PEs without leaving some PEs with 
        ! no cells.

        else
          VERIFY(.false.,1)
        end if
      end do
    end select

    ! Initialize NCells_D_Vectors, NNodes_D_Vectors.

    call Initialize (NCells_X_Vector, NPEs_X, allocate_status(7))
    call Initialize (NCells_Y_Vector, NPEs_Y, allocate_status(8))
    call Initialize (NCells_Z_Vector, NPEs_Z, allocate_status(9))
    call Initialize (NNodes_X_Vector, NPEs_X, allocate_status(10))
    call Initialize (NNodes_Y_Vector, NPEs_Y, allocate_status(11))
    call Initialize (NNodes_Z_Vector, NPEs_Z, allocate_status(12))

    ! Set NCells_D_Vectors (divide evenly among the PEs).

    call Generate_Even_Distribution (NCells_X_Vector, NCells_X_total)
    call Generate_Even_Distribution (NCells_Y_Vector, NCells_Y_total)
    call Generate_Even_Distribution (NCells_Z_Vector, NCells_Z_total)

    ! Set NNodes_D_Vectors -- extra node on last PE in each direction.

    NNodes_X_Vector = NCells_X_Vector
    NNodes_Y_Vector = NCells_Y_Vector
    NNodes_Z_Vector = NCells_Z_Vector
    NNodes_X_Vector(NPEs_X) = NNodes_X_Vector(NPEs_X) + 1
    if (NDimensions > 1) then
      NNodes_Y_Vector(NPEs_Y) = NNodes_Y_Vector(NPEs_Y) + 1
      if (NDimensions > 2) then
        NNodes_Z_Vector(NPEs_Z) = NNodes_Z_Vector(NPEs_Z) + 1
      end if
    end if

    ! Set NCells_Vector, NFaces_Vector, and NNodes_Vector.
    
    pe = 1
    do pe_z = 1, NPEs_Z
      do pe_y = 1, NPEs_Y
        do pe_x = 1, NPEs_X

          ! Set NCells_Vector.

          NCells_Vector(pe) = NCells_X_Vector(pe_x) * &
                              NCells_Y_Vector(pe_y) * &
                              NCells_Z_Vector(pe_z)

          ! Set NFaces_Vector.

          NFaces_Vector(pe) = NDimensions * NCells_Vector(pe)
          ! Add extra YZ faces for pe_x = NPEs_X plane.
          if (pe_x == NPEs_X) then
            NFaces_Vector(pe) = NFaces_Vector(pe) + &
                                NCells_Y_Vector(pe_y) * &
                                NCells_Z_Vector(pe_z)
          end if
          ! Add extra XZ faces for pe_y = NPEs_Y plane in 2-D and 3-D.
          if (pe_y == NPEs_Y .and. NDimensions /= 1) then
            NFaces_Vector(pe) = NFaces_Vector(pe) + &
                                NCells_X_Vector(pe_x) * &
                                NCells_Z_Vector(pe_z)
          end if
          ! Add extra XY faces for pe_z = NPEs_Z plane in 3-D.
          if (pe_z == NPEs_Z .and. NDimensions == 3) then
            NFaces_Vector(pe) = NFaces_Vector(pe) + &
                                NCells_X_Vector(pe_x) * &
                                NCells_Y_Vector(pe_y)
          end if

          ! Set NNodes_Vector.

          NNodes_Vector(pe) = NNodes_X_Vector(pe_x) * &
                              NNodes_Y_Vector(pe_y) * &
                              NNodes_Z_Vector(pe_z)

          ! Capture PE info.

          if (pe == this_PE) then
            this_PE_X = pe_x
            this_PE_Y = pe_y
            this_PE_Z = pe_z
          end if

          ! Increment pe.
          pe = pe + 1

        end do
      end do
    end do
    VERIFY((pe-1)==NPEs,8)
    VERIFY(SUM(NCells_Vector)==NCells_total,8)
    VERIFY(SUM(NFaces_Vector)==NFaces_total,8)
    VERIFY(SUM(NNodes_Vector)==NNodes_total,8)

    ! Set Offset Vectors.

    Offset_Cells_Vector(1) = 1
    Offset_Nodes_Vector(1) = 1
    Offset_Faces_Vector(1) = 1
    if (NPEs > 1) then
      pe = 1
      do pe_z = 1, NPEs_Z
        do pe_y = 1, NPEs_Y
          do pe_x = 1, NPEs_X

            ! Skip first pe -- it's already done.
            if (pe/=1) then

              ! Set Offset_Cells_Vector.

              Offset_Cells_Vector(pe) = Offset_Cells_Vector(pe-1) + &
                                        NCells_Vector(pe-1)

              ! Set Offset_Nodes_Vector.

              Offset_Nodes_Vector(pe) = Offset_Nodes_Vector(pe-1) + &
                                        NNodes_Vector(pe-1)

              ! Set Offset_Faces_Vector.

              Offset_Faces_Vector(pe) = Offset_Faces_Vector(pe-1) + &
                                        NFaces_Vector(pe-1)
            end if

            ! Increment pe.
            pe = pe + 1

          end do
        end do
      end do
    end if

    ! Set Nodes_of_Cells_PE.

    call Initialize (Nodes_of_Cells_PE, NCells_Vector(this_PE), &
                     2**NDimensions, allocate_status(13)) 
    cell = 1
    do cell_z = 1, NCells_Z_Vector(this_PE_Z)
      do cell_y = 1, NCells_Y_Vector(this_PE_Y)
        do cell_x = 1, NCells_X_Vector(this_PE_X)
          ! These next three loops loop over the nodes in a cell.
          do k = 0, 1
            do j = 0, 1
              do i = 0, 1

                ! Determine which PE the node is on, and set 
                ! node_cell_D and node_PE to reflect this.

                node_cell_x = cell_x + i
                node_cell_y = cell_y + j
                node_cell_z = cell_z + k
                node_PE = this_PE
                node_PE_X = this_PE_X
                node_PE_Y = this_PE_Y
                node_PE_Z = this_PE_Z
                if (node_cell_x > NNodes_X_Vector(this_PE_X)) then
                  node_cell_x = 1
                  node_PE = node_PE + 1
                  node_PE_X = node_PE_X + 1
                end if
                if (node_cell_y > NNodes_Y_Vector(this_PE_Y)) then
                  node_cell_y = 1
                  node_PE = node_PE + NPEs_X
                  node_PE_Y = node_PE_Y + 1
                end if
                if (node_cell_z > NNodes_Z_Vector(this_PE_Z)) then
                  node_cell_z = 1
                  node_PE = node_PE + NPEs_X * NPEs_Y
                  node_PE_Z = node_PE_Z + 1
                end if

                ! Use formula to set node number for this node within the 
                ! cell, now that the PE that the node is on is known. Note
                ! that the generalized multi-dimensional node numbering 
                ! (within a cell) is used.

                Nodes_of_Cells_PE(cell,i + 2*j + 4*k + 1) = &
                  Offset_Nodes_Vector(node_PE) + &
                  (node_cell_x - 1) + &
                  (node_cell_y - 1) * NNodes_X_Vector(node_PE_X) + &
                  (node_cell_z - 1) * NNodes_X_Vector(node_PE_X) * &
                                      NNodes_Y_Vector(node_PE_Y)

              end do
              if (NDimensions < 2) exit
            end do
            if (NDimensions < 3) exit
          end do

          ! Increment cell.
          cell = cell + 1

        end do
      end do
    end do

    ! Set Cells_of_Cells_PE and Flag_Faces_of_Cells.

    call Initialize (Cells_of_Cells_PE, NCells_Vector(this_PE), &
                     NDimensions*2, allocate_status(14)) 
    call Initialize (Flag_Faces_of_Cells, NCells_Vector(this_PE), &
                     NDimensions*2, allocate_status(15)) 
    cell = 0
    do cell_z = 1, NCells_Z_Vector(this_PE_Z)
      do cell_y = 1, NCells_Y_Vector(this_PE_Y)
        do cell_x = 1, NCells_X_Vector(this_PE_X)

          ! Increment cell.

          cell = cell + 1

          ! Loop over X Faces.

          do i = -1, 1, 2

            ! Determine which PE the "other" cell is on, and set 
            ! other_cell_D and other_cell_PE to reflect this. Notice 
            ! the loop-around which is done to allow periodic boundary 
            ! conditions.

            other_cell_x = cell_x + i
            other_cell_y = cell_y
            other_cell_z = cell_z
            other_cell_PE   = this_PE
            other_cell_PE_X = this_PE_X
            other_cell_PE_Y = this_PE_Y
            other_cell_PE_Z = this_PE_Z
            if (other_cell_x > NCells_X_Vector(this_PE_X)) then
              other_cell_PE   = other_cell_PE   + 1
              other_cell_PE_X = other_cell_PE_X + 1
              if (other_cell_PE_X > NPEs_X) then
                other_cell_PE_X = other_cell_PE_X - NPEs_X ! Should = 1.
                other_cell_PE   = other_cell_PE   - NPEs_X
                Flag_Faces_of_Cells(cell,2) = 2
              end if
              other_cell_x = 1
            end if
            if (other_cell_x < 1) then
              other_cell_PE   = other_cell_PE   - 1
              other_cell_PE_X = other_cell_PE_X - 1
              if (other_cell_PE_X < 1) then
                other_cell_PE_X = other_cell_PE_X + NPEs_X ! Should = NPEs_X.
                other_cell_PE   = other_cell_PE   + NPEs_X
                Flag_Faces_of_Cells(cell,1) = 1
              end if
              other_cell_x = NCells_X_Vector(other_cell_PE_X)
            end if

            ! Use formula to set cell number for the "other" cell, now 
            ! that the PE that the other cell is on is known. Note
            ! that the generalized multi-dimensional node numbering 
            ! (within a cell) is used. This sets values for faces 1 and 2.

            Cells_of_Cells_PE(cell,(i + 3)/2) = &
              Offset_Cells_Vector(other_cell_PE) + &
              (other_cell_x - 1) + &
              (other_cell_y - 1) * NCells_X_Vector(other_cell_PE_X) + &
              (other_cell_z - 1) * NCells_X_Vector(other_cell_PE_X) * &
                                   NCells_Y_Vector(other_cell_PE_Y)

          end do
          if (NDimensions < 2) cycle

          ! Loop over Y Faces.

          do j = -1, 1, 2  

            ! Determine which PE the "other" cell is on, and set 
            ! other_cell_D and other_cell_PE to reflect this. Notice 
            ! the loop-around which is done to allow periodic boundary 
            ! conditions.

            other_cell_x = cell_x
            other_cell_y = cell_y + j
            other_cell_z = cell_z
            other_cell_PE   = this_PE
            other_cell_PE_X = this_PE_X
            other_cell_PE_Y = this_PE_Y
            other_cell_PE_Z = this_PE_Z
            if (other_cell_y > NCells_Y_Vector(this_PE_Y)) then
              other_cell_PE   = other_cell_PE   + NPEs_X
              other_cell_PE_Y = other_cell_PE_Y + 1
              if (other_cell_PE_Y > NPEs_Y) then
                other_cell_PE_Y = other_cell_PE_Y - NPEs_Y ! Should = 1.
                other_cell_PE   = other_cell_PE   - NPEs_Y * NPEs_X
                Flag_Faces_of_Cells(cell,4) = 4
              end if
              other_cell_y = 1
            end if
            if (other_cell_y < 1) then
              other_cell_PE   = other_cell_PE   - NPEs_X
              other_cell_PE_Y = other_cell_PE_Y - 1
              if (other_cell_PE_Y < 1) then
                other_cell_PE_Y = other_cell_PE_Y + NPEs_Y ! Should = NPEs_Y.
                other_cell_PE   = other_cell_PE   + NPEs_Y * NPEs_X
                Flag_Faces_of_Cells(cell,3) = 3
              end if
              other_cell_y = NCells_Y_Vector(other_cell_PE_Y)
            end if

            ! Use formula to set cell number for the "other" cell, now 
            ! that the PE that the other cell is on is known. Note
            ! that the generalized multi-dimensional node numbering 
            ! (within a cell) is used. This sets values for faces 3 and 4.

            Cells_of_Cells_PE(cell,(j + 7)/2) = &
              Offset_Cells_Vector(other_cell_PE) + &
              (other_cell_x - 1) + &
              (other_cell_y - 1) * NCells_X_Vector(other_cell_PE_X) + &
              (other_cell_z - 1) * NCells_X_Vector(other_cell_PE_X) * &
                                   NCells_Y_Vector(other_cell_PE_Y)

          end do
          if (NDimensions < 3) cycle

          ! Loop over Z Faces.

          do k = -1, 1, 2  

            ! Determine which PE the "other" cell is on, and set 
            ! other_cell_D and other_cell_PE to reflect this. Notice 
            ! the loop-around which is done to allow periodic boundary 
            ! conditions.

            other_cell_x = cell_x
            other_cell_y = cell_y
            other_cell_z = cell_z + k
            other_cell_PE   = this_PE
            other_cell_PE_X = this_PE_X
            other_cell_PE_Y = this_PE_Y
            other_cell_PE_Z = this_PE_Z
            if (other_cell_z > NCells_Z_Vector(this_PE_Z)) then
              other_cell_PE   = other_cell_PE   + NPEs_X * NPEs_Y
              other_cell_PE_Z = other_cell_PE_Z + 1
              if (other_cell_PE_Z > NPEs_Z) then
                other_cell_PE_Z = other_cell_PE_Z - NPEs_Z ! Should = 1.
                other_cell_PE   = other_cell_PE   - NPEs_Z * NPEs_X * NPEs_Y
                Flag_Faces_of_Cells(cell,6) = 6
              end if
              other_cell_z = 1
            end if
            if (other_cell_z < 1) then
              other_cell_PE   = other_cell_PE   - NPEs_X * NPEs_Y
              other_cell_PE_Z = other_cell_PE_Z - 1
              if (other_cell_PE_Z < 1) then
                other_cell_PE_Z = other_cell_PE_Z + NPEs_Z ! Should = NPEs_Z.
                other_cell_PE   = other_cell_PE   + NPEs_Z * NPEs_X * NPEs_Y
                Flag_Faces_of_Cells(cell,5) = 5
              end if
              other_cell_z = NCells_Z_Vector(other_cell_PE_Z)
            end if

            ! Use formula to set cell number for the "other" cell, now 
            ! that the PE that the other cell is on is known. Note
            ! that the generalized multi-dimensional node numbering 
            ! (within a cell) is used. This sets values for faces 5 and 6.

            Cells_of_Cells_PE(cell,(k + 11)/2) = &
              Offset_Cells_Vector(other_cell_PE) + &
              (other_cell_x - 1) + &
              (other_cell_y - 1) * NCells_X_Vector(other_cell_PE_X) + &
              (other_cell_z - 1) * NCells_X_Vector(other_cell_PE_X) * &
                                   NCells_Y_Vector(other_cell_PE_Y)

          end do

        end do
      end do
    end do

    ! Finalize temporary variables.

    call Finalize (Offset_Cells_Vector, allocate_status(16))
    call Finalize (Offset_Faces_Vector, allocate_status(17))
    call Finalize (Offset_Nodes_Vector, allocate_status(18))

    ! Consolidate and handle status.

    consolidated_status = allocate_status
    if (PRESENT(status)) then
      WARN_IF(Error(consolidated_status), 5)
      status = consolidated_status
    else
      VERIFY(Normal(consolidated_status), 5)
    end if
    call Finalize (consolidated_status)
    call Finalize (allocate_status)

    return
  end subroutine Gen_StructureMesh_Connectivity



Michael L. Hall