adjacency_node.F90 Source File


This file depends on

sourcefile~~adjacency_node.f90~~EfferentGraph sourcefile~adjacency_node.f90 adjacency_node.F90 sourcefile~core.f90 core.F90 sourcefile~adjacency_node.f90->sourcefile~core.f90 sourcefile~mesh.f90 mesh.F90 sourcefile~adjacency_node.f90->sourcefile~mesh.f90 sourcefile~allocate.f90 allocate.F90 sourcefile~core.f90->sourcefile~allocate.f90 sourcefile~check_range.f90 check_range.F90 sourcefile~core.f90->sourcefile~check_range.f90 sourcefile~deallocate.f90 deallocate.F90 sourcefile~core.f90->sourcefile~deallocate.f90 sourcefile~error.f90 error.F90 sourcefile~core.f90->sourcefile~error.f90 sourcefile~fortran_utils.f90 fortran_utils.F90 sourcefile~core.f90->sourcefile~fortran_utils.f90 sourcefile~string_utils.f90 string_utils.F90 sourcefile~core.f90->sourcefile~string_utils.f90 sourcefile~types.f90 types.F90 sourcefile~core.f90->sourcefile~types.f90 sourcefile~unique.f90 unique.F90 sourcefile~core.f90->sourcefile~unique.f90 sourcefile~vtk.f90 vtk.F90 sourcefile~core.f90->sourcefile~vtk.f90 sourcefile~vtk_constants.f90 vtk_constants.F90 sourcefile~core.f90->sourcefile~vtk_constants.f90 sourcefile~element.f90 element.F90 sourcefile~mesh.f90->sourcefile~element.f90 sourcefile~mesh_interface.f90 mesh_interface.F90 sourcefile~mesh.f90->sourcefile~mesh_interface.f90 sourcefile~side.f90 side.F90 sourcefile~mesh.f90->sourcefile~side.f90 sourcefile~allocate.f90->sourcefile~error.f90 sourcefile~deallocate.f90->sourcefile~error.f90 sourcefile~element_factory.f90 element_factory.F90 sourcefile~element.f90->sourcefile~element_factory.f90 sourcefile~element_interface.f90 element_interface.F90 sourcefile~element.f90->sourcefile~element_interface.f90 sourcefile~memory_stats_wrapper.f90 memory_stats_wrapper.F90 sourcefile~fortran_utils.f90->sourcefile~memory_stats_wrapper.f90 sourcefile~signal_flag_wrapper.f90 signal_flag_wrapper.F90 sourcefile~fortran_utils.f90->sourcefile~signal_flag_wrapper.f90 sourcefile~system_info_wrapper.f90 system_info_wrapper.F90 sourcefile~fortran_utils.f90->sourcefile~system_info_wrapper.f90 sourcefile~mesh_interface.f90->sourcefile~core.f90 sourcefile~side_factory.f90 side_factory.F90 sourcefile~side.f90->sourcefile~side_factory.f90 sourcefile~side_interface.f90 side_interface.F90 sourcefile~side.f90->sourcefile~side_interface.f90 sourcefile~string_utils.f90->sourcefile~allocate.f90 sourcefile~array.f90 array.F90 sourcefile~types.f90->sourcefile~array.f90 sourcefile~gauss.f90 gauss.F90 sourcefile~types.f90->sourcefile~gauss.f90 sourcefile~matrix.f90 matrix.F90 sourcefile~types.f90->sourcefile~matrix.f90 sourcefile~pointer.f90 pointer.F90 sourcefile~types.f90->sourcefile~pointer.f90 sourcefile~variable.f90 variable.F90 sourcefile~types.f90->sourcefile~variable.f90 sourcefile~vector.f90 vector.F90 sourcefile~types.f90->sourcefile~vector.f90 sourcefile~unique.f90->sourcefile~allocate.f90 sourcefile~vtk.f90->sourcefile~allocate.f90 sourcefile~vtk.f90->sourcefile~deallocate.f90 sourcefile~vtk.f90->sourcefile~types.f90 sourcefile~vtk.f90->sourcefile~unique.f90 sourcefile~vtk.f90->sourcefile~vtk_constants.f90 sourcefile~vtk_wrapper.f90 vtk_wrapper.F90 sourcefile~vtk.f90->sourcefile~vtk_wrapper.f90 sourcefile~vtu_wrapper.f90 vtu_wrapper.F90 sourcefile~vtk.f90->sourcefile~vtu_wrapper.f90 sourcefile~array.f90->sourcefile~allocate.f90 sourcefile~array.f90->sourcefile~deallocate.f90 sourcefile~element_factory.f90->sourcefile~core.f90 sourcefile~element_factory.f90->sourcefile~element_interface.f90 sourcefile~input.f90 input.F90 sourcefile~element_factory.f90->sourcefile~input.f90 sourcefile~element_interface.f90->sourcefile~core.f90 sourcefile~element_interface.f90->sourcefile~mesh_interface.f90 sourcefile~element_interface.f90->sourcefile~input.f90 sourcefile~matrix_coo.f90 matrix_coo.F90 sourcefile~matrix.f90->sourcefile~matrix_coo.f90 sourcefile~matrix_crs.f90 matrix_crs.F90 sourcefile~matrix.f90->sourcefile~matrix_crs.f90 sourcefile~matrix_dense.f90 matrix_dense.F90 sourcefile~matrix.f90->sourcefile~matrix_dense.f90 sourcefile~matrix_interface.f90 matrix_interface.F90 sourcefile~matrix.f90->sourcefile~matrix_interface.f90 sourcefile~c_utils.f90 c_utils.F90 sourcefile~memory_stats_wrapper.f90->sourcefile~c_utils.f90 sourcefile~side_factory.f90->sourcefile~core.f90 sourcefile~side_factory.f90->sourcefile~side_interface.f90 sourcefile~side_factory.f90->sourcefile~input.f90 sourcefile~side_interface.f90->sourcefile~core.f90 sourcefile~side_interface.f90->sourcefile~mesh_interface.f90 sourcefile~side_interface.f90->sourcefile~input.f90 sourcefile~signal_flag.f90 signal_flag.F90 sourcefile~signal_flag_wrapper.f90->sourcefile~signal_flag.f90 sourcefile~system_info_wrapper.f90->sourcefile~c_utils.f90 sourcefile~variable.f90->sourcefile~allocate.f90 sourcefile~c_utils.f90->sourcefile~signal_flag.f90 sourcefile~memory_stats.f90 memory_stats.F90 sourcefile~c_utils.f90->sourcefile~memory_stats.f90 sourcefile~system_info.f90 system_info.F90 sourcefile~c_utils.f90->sourcefile~system_info.f90 sourcefile~input_interface.f90 input_interface.F90 sourcefile~input.f90->sourcefile~input_interface.f90 sourcefile~matrix_coo.f90->sourcefile~allocate.f90 sourcefile~matrix_coo.f90->sourcefile~deallocate.f90 sourcefile~matrix_coo.f90->sourcefile~matrix_interface.f90 sourcefile~matrix_crs.f90->sourcefile~allocate.f90 sourcefile~matrix_crs.f90->sourcefile~deallocate.f90 sourcefile~matrix_crs.f90->sourcefile~matrix_interface.f90 sourcefile~matrix_dense.f90->sourcefile~allocate.f90 sourcefile~matrix_dense.f90->sourcefile~deallocate.f90 sourcefile~matrix_dense.f90->sourcefile~matrix_interface.f90 sourcefile~input_interface.f90->sourcefile~core.f90 sourcefile~project_settings.f90 project_settings.F90 sourcefile~input_interface.f90->sourcefile~project_settings.f90 sourcefile~project_settings.f90->sourcefile~core.f90

Files dependent on this one

sourcefile~~adjacency_node.f90~~AfferentGraph sourcefile~adjacency_node.f90 adjacency_node.F90 sourcefile~adjacency.f90 adjacency.F90 sourcefile~adjacency.f90->sourcefile~adjacency_node.f90 sourcefile~reordering.f90 reordering.F90 sourcefile~reordering.f90->sourcefile~adjacency_node.f90 sourcefile~domain.f90 domain.F90 sourcefile~domain.f90->sourcefile~adjacency.f90 sourcefile~domain.f90->sourcefile~reordering.f90 sourcefile~domain_manager.f90 domain_manager.F90 sourcefile~domain.f90->sourcefile~domain_manager.f90 sourcefile~domain_manager.f90->sourcefile~adjacency.f90 sourcefile~domain_manager.f90->sourcefile~reordering.f90 sourcefile~methods.f90 methods.F90 sourcefile~methods.f90->sourcefile~reordering.f90 sourcefile~to_original.f90 to_original.F90 sourcefile~to_original.f90->sourcefile~reordering.f90 sourcefile~to_reordered.f90 to_reordered.F90 sourcefile~to_reordered.f90->sourcefile~reordering.f90 sourcefile~boundary_interface.f90 boundary_interface.F90 sourcefile~boundary_interface.f90->sourcefile~domain.f90 sourcefile~boundary_manager.f90 boundary_manager.F90 sourcefile~boundary_manager.f90->sourcefile~domain.f90 sourcefile~boundary_manager.f90->sourcefile~boundary_interface.f90 sourcefile~ftdss.f90 ftdss.F90 sourcefile~ftdss.f90->sourcefile~domain.f90 sourcefile~boundary.f90 boundary.F90 sourcefile~ftdss.f90->sourcefile~boundary.f90 sourcefile~hydraulic.f90 hydraulic.F90 sourcefile~ftdss.f90->sourcefile~hydraulic.f90 sourcefile~initial.f90 initial.F90 sourcefile~ftdss.f90->sourcefile~initial.f90 sourcefile~output.f90 output.F90 sourcefile~ftdss.f90->sourcefile~output.f90 sourcefile~thermal.f90 thermal.F90 sourcefile~ftdss.f90->sourcefile~thermal.f90 sourcefile~hydraulic_assemble.f90 hydraulic_assemble.F90 sourcefile~hydraulic_assemble.f90->sourcefile~domain.f90 sourcefile~hydraulic_interface.f90 hydraulic_interface.F90 sourcefile~hydraulic_interface.f90->sourcefile~domain.f90 sourcefile~hydraulic_interface.f90->sourcefile~hydraulic_assemble.f90 sourcefile~hydraulic_interface.f90->sourcefile~boundary.f90 sourcefile~initial_interface.f90 initial_interface.F90 sourcefile~initial_interface.f90->sourcefile~domain.f90 sourcefile~initial_interface.f90->sourcefile~boundary.f90 sourcefile~initial_manager.f90 initial_manager.F90 sourcefile~initial_manager.f90->sourcefile~domain.f90 sourcefile~initial_manager.f90->sourcefile~initial_interface.f90 sourcefile~output_interface.f90 output_interface.F90 sourcefile~output_interface.f90->sourcefile~domain.f90 sourcefile~thermal_interface.f90 thermal_interface.F90 sourcefile~thermal_interface.f90->sourcefile~domain.f90 sourcefile~thermal_interface.f90->sourcefile~boundary.f90 sourcefile~boundary.f90->sourcefile~boundary_interface.f90 sourcefile~boundary.f90->sourcefile~boundary_manager.f90 sourcefile~boundary_adiabatic.f90 boundary_adiabatic.F90 sourcefile~boundary_adiabatic.f90->sourcefile~boundary_interface.f90 sourcefile~boundary_base.f90 boundary_base.F90 sourcefile~boundary_base.f90->sourcefile~boundary_interface.f90 sourcefile~boundary_dirichlet.f90 boundary_dirichlet.F90 sourcefile~boundary_dirichlet.f90->sourcefile~boundary_interface.f90 sourcefile~hydraulic.f90->sourcefile~hydraulic_interface.f90 sourcefile~hydraulic_crs.f90 hydraulic_crs.F90 sourcefile~hydraulic_crs.f90->sourcefile~hydraulic_interface.f90 sourcefile~initial.f90->sourcefile~initial_interface.f90 sourcefile~initial.f90->sourcefile~initial_manager.f90 sourcefile~initial_laplace.f90 initial_laplace.F90 sourcefile~initial_laplace.f90->sourcefile~initial_interface.f90 sourcefile~initial_uniform.f90 initial_uniform.F90 sourcefile~initial_uniform.f90->sourcefile~initial_interface.f90 sourcefile~output.f90->sourcefile~output_interface.f90 sourcefile~output_base.f90 output_base.F90 sourcefile~output_base.f90->sourcefile~output_interface.f90 sourcefile~output_observation.f90 output_observation.F90 sourcefile~output_observation.f90->sourcefile~output_interface.f90 sourcefile~output_overall_base.f90 output_overall_base.F90 sourcefile~output_overall_base.f90->sourcefile~output_interface.f90 sourcefile~output_overall_vtk.f90 output_overall_vtk.F90 sourcefile~output_overall_vtk.f90->sourcefile~output_interface.f90 sourcefile~output_overall_vtu.f90 output_overall_vtu.F90 sourcefile~output_overall_vtu.f90->sourcefile~output_interface.f90 sourcefile~output_system_logger.f90 output_system_logger.F90 sourcefile~output_system_logger.f90->sourcefile~output_interface.f90 sourcefile~thermal.f90->sourcefile~thermal_interface.f90 sourcefile~thermal_crs.f90 thermal_crs.F90 sourcefile~thermal_crs.f90->sourcefile~thermal_interface.f90

Source Code

!================================================================!
! domain_adjacency_adjacency_node (ハイブリッド形式 / 自己ループ対応)
!
! CSR行列構築のため、自己ループ(対角成分)を含む隣接関係を
! COO形式とCSR形式の両方で保持するモジュール。
!================================================================!
module domain_adjacency_adjacency_node
    use, intrinsic :: iso_fortran_env, only: int32, int64
    use :: stdlib_sorting, only:sort
    use :: module_core, only:allocate_array, deallocate_array, unique
    use :: module_mesh, only:holder_sides, holder_elements

    implicit none
    private
    public :: type_node_adjacency

    !> @type type_node_adjacency
    !! @brief ノードの隣接関係をCOO形式とCSR形式の両方で保持する型。
    type :: type_node_adjacency
        integer(int32) :: num_nodes = 0
        integer(int32) :: nnz = 0

        ! COO (Coordinate list) 形式のデータ (ソート済み)
        integer(int32), allocatable :: row(:)
        integer(int32), allocatable :: col(:)

        ! CSR (Compressed Sparse Row) 形式のデータ
        integer(int32), allocatable :: ptr(:)
        integer(int32), allocatable :: ind(:)
    contains
        procedure, pass(self), public :: initialize => initialize_hybrid_from_mesh
        procedure, pass(self), public :: get_num_nodes => get_num_nodes
        procedure, pass(self), public :: get_degree => get_degree_csr
        procedure, pass(self), public :: get_neighbors => get_neighbors_csr
        procedure, pass(self), public :: get_nnz => get_nnz
        procedure, pass(self), public :: get_coo => get_coo
        procedure, pass(self), public :: get_csr => get_csr
        procedure, pass(self), public :: destroy => destroy_hybrid
    end type type_node_adjacency

contains

    !================================================================!
    ! メインの初期化処理 (COOとCSRを両方構築)
    !================================================================!
    subroutine initialize_hybrid_from_mesh(self, num_nodes, computation_dimension, sides, elements)
        implicit none
        class(type_node_adjacency), intent(inout) :: self
        integer(int32), intent(in) :: num_nodes
        integer(int32), intent(in) :: computation_dimension
        class(holder_sides), intent(in) :: sides(:)
        class(holder_elements), intent(in) :: elements(:)

        integer(int32) :: estimated_nnz, actual_nnz
        integer(int32), allocatable :: temp_row_indices(:), temp_col_indices(:)

        self%num_nodes = num_nodes
        if (self%num_nodes <= 0) return

        ! 1. 一時COO配列の最大サイズを見積もる
        estimated_nnz = estimate_max_coo_size(computation_dimension, sides, elements)
        if (estimated_nnz <= 0) return

        call allocate_array(temp_row_indices, estimated_nnz)
        call allocate_array(temp_col_indices, estimated_nnz)

        ! 2. メッシュから重複を含むCOOリストを生成
        call create_coo_from_mesh(computation_dimension, sides, elements, temp_row_indices, temp_col_indices, actual_nnz)

        ! 3.【COO構築】一時COOリストからユニークなCOOリストを作成
        call create_unique_coo(self, temp_row_indices(1:actual_nnz), temp_col_indices(1:actual_nnz))

        ! 4.【CSR構築】作成したユニークCOOリストからCSR形式を構築
        call build_csr_from_coo(self)

        call deallocate_array(temp_row_indices)
        call deallocate_array(temp_col_indices)
    end subroutine initialize_hybrid_from_mesh

    !================================================================!
    ! COO配列の最大サイズを見積もる
    !================================================================!
    function estimate_max_coo_size(computation_dimension, sides, elements) result(max_size)
        implicit none
        integer(int32), intent(in) :: computation_dimension
        class(holder_sides), intent(in) :: sides(:)
        class(holder_elements), intent(in) :: elements(:)
        integer(int32) :: max_size
        integer(int32) :: i

        max_size = 0

        if (computation_dimension >= 2) then
            !$omp parallel do reduction(+:max_size) private(i)
            do i = 1, size(elements)
                max_size = max_size + elements(i)%e%get_num_nodes()**2
            end do
            !$omp end parallel do
        end if

        if (computation_dimension >= 1) then
            !$omp parallel do reduction(+:max_size) private(i)
            do i = 1, size(sides)
                max_size = max_size + sides(i)%s%get_num_nodes()**2
            end do
            !$omp end parallel do
        end if
    end function estimate_max_coo_size

    !================================================================!
    ! メッシュ情報からCOOリストを生成 (並列化対応)
    !================================================================!
    subroutine create_coo_from_mesh(computation_dimension, sides, elements, row_indices, col_indices, counter)
        implicit none
        integer(int32), intent(in) :: computation_dimension
        class(holder_sides), intent(in) :: sides(:)
        class(holder_elements), intent(in) :: elements(:)
        integer(int32), intent(out) :: row_indices(:), col_indices(:)
        integer(int32), intent(out) :: counter

        integer(int32) :: num_elements, num_sides
        integer(int32), allocatable :: offsets_e(:), offsets_s(:)
        integer(int32) :: i, j, k, base, nodes_per_mesh
        integer(int32), pointer :: p_conn(:) => null()

        num_elements = size(elements)
        num_sides = size(sides)

        ! 並列書き込みのためのオフセット計算
        call allocate_array(offsets_e, num_elements + 1)
        call allocate_array(offsets_s, num_sides + 1)
        offsets_e(1) = 0
        if (computation_dimension >= 2) then
            do i = 1, num_elements
                offsets_e(i + 1) = offsets_e(i) + elements(i)%e%get_num_nodes()**2
            end do
        else
            offsets_e(2:) = 0
        end if

        offsets_s(1) = 0
        if (computation_dimension >= 1) then
            do i = 1, num_sides
                offsets_s(i + 1) = offsets_s(i) + sides(i)%s%get_num_nodes()**2
            end do
        else
            offsets_s(2:) = 0
        end if

        !$omp parallel do private(i, j, k, base, nodes_per_mesh, p_conn)
        do i = 1, num_elements
            if (computation_dimension < 2) cycle
            nodes_per_mesh = elements(i)%e%get_num_nodes()
            p_conn => elements(i)%e%get_connectivity()
            base = offsets_e(i)
            do j = 1, nodes_per_mesh
                do k = 1, nodes_per_mesh
                    row_indices(base + (j - 1) * nodes_per_mesh + k) = p_conn(j)
                    col_indices(base + (j - 1) * nodes_per_mesh + k) = p_conn(k)
                end do
            end do
        end do
        !$omp end parallel do

        base = offsets_e(num_elements + 1)
        !$omp parallel do private(i, j, k, nodes_per_mesh, p_conn)
        do i = 1, num_sides
            if (computation_dimension < 1) cycle
            nodes_per_mesh = sides(i)%s%get_num_nodes()
            p_conn => sides(i)%s%get_connectivity()
            do j = 1, nodes_per_mesh
                do k = 1, nodes_per_mesh
                    row_indices(base + offsets_s(i) + (j - 1) * nodes_per_mesh + k) = p_conn(j)
                    col_indices(base + offsets_s(i) + (j - 1) * nodes_per_mesh + k) = p_conn(k)
                end do
            end do
        end do
        !$omp end parallel do

        counter = offsets_e(num_elements + 1) + offsets_s(num_sides + 1)
        deallocate (offsets_e, offsets_s)
    end subroutine create_coo_from_mesh

    !================================================================!
    !【COO構築】一時COOリストからユニークなCOOリストを生成 (自己ループ対応)
    !================================================================!
    subroutine create_unique_coo(self, temp_row, temp_col)
        implicit none
        class(type_node_adjacency), intent(inout) :: self
        integer(int32), intent(in) :: temp_row(:), temp_col(:)

        integer(int64), allocatable :: packed_edges(:), unique_packed_edges(:)
        integer(int32) :: i, n1, n2, edge_count

        if (size(temp_row) == 0) return

        ! (i,j) と (j,i) の両方を持つ対称COOリストを作成するため、2倍のサイズを確保
        allocate (packed_edges(size(temp_row) * 2))
        edge_count = 0
        do i = 1, size(temp_row)
            n1 = temp_row(i)
            n2 = temp_col(i)

            if (n1 == n2) then
                edge_count = edge_count + 1
                packed_edges(edge_count) = ishft(int(n1, int64), 32) + int(n2, int64)
            else
                edge_count = edge_count + 1
                packed_edges(edge_count) = ishft(int(n1, int64), 32) + int(n2, int64)
                edge_count = edge_count + 1
                packed_edges(edge_count) = ishft(int(n2, int64), 32) + int(n1, int64)
            end if
        end do

        ! ソートしてユニークなエッジのみを抽出し、ソート済みCOOを生成
        call unique(packed_edges(1:edge_count), unique_packed_edges)
        deallocate (packed_edges)

        self%nnz = size(unique_packed_edges)
        call allocate_array(self%row, self%nnz)
        call allocate_array(self%col, self%nnz)

        ! 結果を自身のCOOメンバに格納
        do i = 1, self%nnz
            self%row(i) = int(ishft(unique_packed_edges(i), -32), kind=int32)
            self%col(i) = int(iand(unique_packed_edges(i), int(z'FFFFFFFF', int64)), kind=int32)
        end do
        deallocate (unique_packed_edges)
    end subroutine create_unique_coo

    !================================================================!
    !【CSR構築】自身のCOOメンバからCSR形式を構築 (修正版)
    !================================================================!
    subroutine build_csr_from_coo(self)
        implicit none
        class(type_node_adjacency), intent(inout) :: self
        integer(int32) :: i

        if (self%nnz == 0) then
            if (self%num_nodes > 0) then
                call allocate_array(self%ptr, self%num_nodes + 1)
                self%ptr = 1
            end if
            call allocate_array(self%ind, 0)
            return
        end if

        ! COOデータはcreate_unique_cooによって行でソート済み
        call allocate_array(self%ptr, self%num_nodes + 1)
        call allocate_array(self%ind, self%nnz)
        self%ind = self%col ! col配列をindにコピー
        self%ptr = 0

        ! 1. 各行の非ゼロ要素数を数える (次数を計算)
        do i = 1, self%nnz
            self%ptr(self%row(i) + 1) = self%ptr(self%row(i) + 1) + 1
        end do

        ! 2. 次数の累積和からptr配列を構築
        do i = 1, self%num_nodes
            self%ptr(i + 1) = self%ptr(i) + self%ptr(i + 1)
        end do

        ! Fortranは1-based indexなので、全体に1を加算
        self%ptr = self%ptr + 1

        ! 3. 各行の列インデックスをソートする
        do i = 1, self%num_nodes
            if (self%ptr(i + 1) > self%ptr(i)) then
                call sort(self%ind(self%ptr(i):self%ptr(i + 1) - 1))
            end if
        end do
    end subroutine build_csr_from_coo

    !================================================================!
    ! 照会・解放用サブルーチン
    !================================================================!
    pure function get_num_nodes(self) result(n_nodes)
        implicit none
        class(type_node_adjacency), intent(in) :: self
        integer(int32) :: n_nodes
        n_nodes = self%num_nodes
    end function get_num_nodes

    pure function get_degree_csr(self, node_id) result(degree)
        implicit none
        class(type_node_adjacency), intent(in) :: self
        integer(int32), intent(in) :: node_id
        integer(int32) :: degree
        if (node_id < 1 .or. node_id > self%num_nodes) then
            degree = 0; return
        end if
        degree = self%ptr(node_id + 1) - self%ptr(node_id)
    end function get_degree_csr

    subroutine get_neighbors_csr(self, node_id, neighbors)
        implicit none
        class(type_node_adjacency), intent(in) :: self
        integer(int32), intent(in) :: node_id
        integer(int32), allocatable, intent(out) :: neighbors(:)
        integer(int32) :: start_p, end_p, degree

        if (node_id < 1 .or. node_id > self%num_nodes) then
            allocate (neighbors(0)); return
        end if
        start_p = self%ptr(node_id)
        end_p = self%ptr(node_id + 1) - 1
        degree = end_p - start_p + 1
        if (degree <= 0) then
            allocate (neighbors(0)); return
        end if
        allocate (neighbors(degree))
        neighbors = self%ind(start_p:end_p)
    end subroutine get_neighbors_csr

    pure function get_nnz(self) result(nnz)
        implicit none
        class(type_node_adjacency), intent(in) :: self
        integer(int32) :: nnz
        nnz = self%nnz
    end function get_nnz

    subroutine get_coo(self, row_out, col_out)
        implicit none
        class(type_node_adjacency), intent(in) :: self
        integer(int32), intent(inout), allocatable :: row_out(:)
        integer(int32), intent(inout), allocatable :: col_out(:)

        if (self%nnz <= 0) return
        call allocate_array(row_out, self%nnz)
        call allocate_array(col_out, self%nnz)
        row_out(:) = self%row(:)
        col_out(:) = self%col(:)
    end subroutine get_coo

    subroutine get_csr(self, ptr_out, ind_out)
        implicit none
        class(type_node_adjacency), intent(in) :: self
        integer(int32), intent(inout), allocatable :: ptr_out(:)
        integer(int32), intent(inout), allocatable :: ind_out(:)

        if (self%nnz <= 0) return
        call allocate_array(ptr_out, self%num_nodes + 1_int32)
        call allocate_array(ind_out, self%nnz)
        ptr_out(:) = self%ptr(:)
        ind_out(:) = self%ind(:)
    end subroutine get_csr

    subroutine destroy_hybrid(self)
        implicit none
        class(type_node_adjacency), intent(inout) :: self

        call deallocate_array(self%row)
        call deallocate_array(self%col)
        call deallocate_array(self%ptr)
        call deallocate_array(self%ind)
        self%num_nodes = 0
        self%nnz = 0
    end subroutine destroy_hybrid

end module domain_adjacency_adjacency_node