mod_octatree.F90 Source File


This file depends on

sourcefile~~mod_octatree.f90~~EfferentGraph sourcefile~mod_octatree.f90 mod_octatree.F90 sourcefile~mod_tree.f90 mod_tree.F90 sourcefile~mod_octatree.f90->sourcefile~mod_tree.f90 sourcefile~mod_constants.f90 mod_constants.F90 sourcefile~mod_octatree.f90->sourcefile~mod_constants.f90 sourcefile~mod_fmm_utils.f90 mod_fmm_utils.F90 sourcefile~mod_octatree.f90->sourcefile~mod_fmm_utils.f90 sourcefile~mod_profiling.f90 mod_profiling.F90 sourcefile~mod_octatree.f90->sourcefile~mod_profiling.f90 sourcefile~mod_adjacency_mat.f90 mod_adjacency_mat.F90 sourcefile~mod_octatree.f90->sourcefile~mod_adjacency_mat.f90 sourcefile~mod_tree.f90->sourcefile~mod_constants.f90 sourcefile~mod_tree.f90->sourcefile~mod_fmm_utils.f90 sourcefile~mod_tree.f90->sourcefile~mod_adjacency_mat.f90 sourcefile~mod_fmm_utils.f90->sourcefile~mod_constants.f90 sourcefile~mod_profiling.f90->sourcefile~mod_constants.f90 sourcefile~mod_memory.f90 mod_memory.F90 sourcefile~mod_profiling.f90->sourcefile~mod_memory.f90 sourcefile~mod_io.f90 mod_io.F90 sourcefile~mod_profiling.f90->sourcefile~mod_io.f90 sourcefile~mod_adjacency_mat.f90->sourcefile~mod_memory.f90 sourcefile~mod_utils.f90 mod_utils.F90 sourcefile~mod_adjacency_mat.f90->sourcefile~mod_utils.f90 sourcefile~mod_memory.f90->sourcefile~mod_constants.f90 sourcefile~mod_memory.f90->sourcefile~mod_io.f90 sourcefile~mod_utils.f90->sourcefile~mod_constants.f90 sourcefile~mod_utils.f90->sourcefile~mod_memory.f90 sourcefile~mod_io.f90->sourcefile~mod_constants.f90

Files dependent on this one

sourcefile~~mod_octatree.f90~~AfferentGraph sourcefile~mod_octatree.f90 mod_octatree.F90 sourcefile~mod_fmm_interface.f90 mod_fmm_interface.F90 sourcefile~mod_fmm_interface.f90->sourcefile~mod_octatree.f90 sourcefile~mod_electrostatics.f90 mod_electrostatics.F90 sourcefile~mod_electrostatics.f90->sourcefile~mod_fmm_interface.f90 sourcefile~mod_qm_helper.f90 mod_qm_helper.F90 sourcefile~mod_qm_helper.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_mmpol.f90 mod_mmpol.F90 sourcefile~mod_qm_helper.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_prm.f90 mod_prm.F90 sourcefile~mod_qm_helper.f90->sourcefile~mod_prm.f90 sourcefile~mod_link_atom.f90 mod_link_atom.F90 sourcefile~mod_qm_helper.f90->sourcefile~mod_link_atom.f90 sourcefile~rotate_multipoles.f90 rotate_multipoles.F90 sourcefile~rotate_multipoles.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_mmpol.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_mmpol.f90->sourcefile~mod_link_atom.f90 sourcefile~mod_prm.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_link_atom.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_link_atom.f90->sourcefile~mod_prm.f90 sourcefile~mod_iohdf5.f90 mod_iohdf5.F90 sourcefile~mod_iohdf5.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_iohdf5.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_polarization.f90 mod_polarization.F90 sourcefile~mod_polarization.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_polarization.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_solvers.f90 mod_solvers.F90 sourcefile~mod_polarization.f90->sourcefile~mod_solvers.f90 sourcefile~mod_solvers.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_geomgrad.f90 mod_geomgrad.F90 sourcefile~mod_geomgrad.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_geomgrad.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_geomgrad.f90->sourcefile~mod_polarization.f90 sourcefile~mod_inputloader.f90 mod_inputloader.F90 sourcefile~mod_inputloader.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_inputloader.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_inputloader.f90->sourcefile~mod_prm.f90 sourcefile~mod_interface.f90 mod_interface.F90 sourcefile~mod_interface.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_interface.f90->sourcefile~mod_qm_helper.f90 sourcefile~mod_interface.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_interface.f90->sourcefile~mod_prm.f90 sourcefile~mod_interface.f90->sourcefile~mod_link_atom.f90 sourcefile~mod_interface.f90->sourcefile~mod_iohdf5.f90 sourcefile~mod_interface.f90->sourcefile~mod_polarization.f90 sourcefile~mod_interface.f90->sourcefile~mod_geomgrad.f90 sourcefile~mod_interface.f90->sourcefile~mod_inputloader.f90 sourcefile~mod_c_interface.f90 mod_c_interface.F90 sourcefile~mod_c_interface.f90->sourcefile~mod_electrostatics.f90 sourcefile~mod_c_interface.f90->sourcefile~mod_qm_helper.f90 sourcefile~mod_c_interface.f90->sourcefile~mod_mmpol.f90 sourcefile~mod_c_interface.f90->sourcefile~mod_interface.f90

Contents

Source Code


Source Code

module mod_octatree
    use mod_tree
    use mod_constants, only: ip, rp
    use mod_fmm_utils, only: fmm_error
    use mod_profiling

    implicit none
    private

    public :: init_as_octatree
    
contains

    subroutine init_as_octatree(t, c_particle, dfar, min_cell_size_in)
        !! Build an adaptive octatree
        use iso_c_binding, only: c_int32_t
        use mod_adjacency_mat, only: compress_list, free_yale_sparse, yale_sparse

        implicit none 

        type(fmm_tree_type), intent(inout) :: t
        !! Tree data structure to populate
        real(rp), target, intent(in) :: c_particle(:,:)
        !! Coordinates of the particles to insert in the tree
        real(rp), intent(in) :: dfar
        !! Threshold distance for near to far field
        real(rp), intent(in), optional :: min_cell_size_in
        !! Minimum radius for a node, if a node is below this threshold it 
        !! won't be split

        integer(ip) :: d, i_node, nnode_guess, max_lev, i_part, idx, pl_idx(8), iparent, ilev, j, &
                       b_level, e_level, i, npart, ii, jj, l
        integer(ip), allocatable :: t_children(:,:), particle_lst(:,:)
        real(rp), allocatable :: n_dimension(:, :), n_centers(:, :)
        real(rp) :: minc(3), maxc(3), root_dim(3), root_center(3), dist, min_cell_size
        type(yale_sparse) :: node2particle        


        if(present(min_cell_size_in)) then
            min_cell_size = max(0.0_rp, min_cell_size_in)
        else
            min_cell_size = 0.0_rp
        end if

        t%tree_degree = 8
        t%n_particles = size(c_particle, 2)
        t%particles_coords => c_particle
        
        ! Compute the center of the space and its dimension
        do d=1, 3
            minc = minval(c_particle(d,:))
            maxc = maxval(c_particle(d,:))
            root_dim(d) = maxc(d) - minc(d)
            root_center(d) = minc(d) + root_dim(d) * .5
        end do

        max_lev = ceiling(log(maxval(root_dim)/2.0)/log(2.0)) + 1
        
        if(ip == c_int32_t .and. max_lev >= 10) then
          call fmm_error("The code is compiled with int32 but for the &
                         &size of this system int64 are required!")
        end if

        nnode_guess = (8**(max_lev) - 1) / 7 ! Sum of a geometric seies

        allocate(t_children(8, nnode_guess))
        allocate(n_dimension(3, nnode_guess))
        allocate(n_centers(3, nnode_guess))
        allocate(particle_lst(8, t%n_particles))

        node2particle%n = nnode_guess
        allocate(node2particle%ri(node2particle%n+1))
        allocate(node2particle%ci(max_lev*t%n_particles))
      
        t_children = 0
        ! Initialize root node
        i_node = 1
        b_level = 1
        n_centers(:, i_node) = root_center(:)
        n_dimension(:, i_node) = root_dim(:)
        e_level = 1

        node2particle%ri(1) = 1
        node2particle%ri(2) = t%n_particles + 1
        do i=1, t%n_particles
            node2particle%ci(i) = i
        end do
        
        do ilev=1, max_lev
            do iparent=b_level, e_level
                ! We should stop here?
                npart = node2particle%ri(iparent+1) - node2particle%ri(iparent)
                if(npart < 2) then
                    cycle
                end if

                ! TODO: breaks far field
                if(norm2(n_dimension(:,iparent)) / 2.0 < min_cell_size) then
                    ! Splitting this cell is going to create all-near children for sure,
                    ! so we can stop here
                    cycle
                end if
             
                ! If all the tests above are passed, the node should be split
                ! Divide the particles in parent node into children nodes
                pl_idx = 0
                do i=node2particle%ri(iparent), node2particle%ri(iparent+1)-1
                    i_part = node2particle%ci(i)

                    ! Compute the index of the cell in which the particle will be assigned
                    idx = 1
                    if(c_particle(1,i_part) > n_centers(1,iparent)) idx = idx + 1
                    if(c_particle(2,i_part) > n_centers(2,iparent)) idx = idx + 2
                    if(c_particle(3,i_part) > n_centers(3,iparent)) idx = idx + 4
                    
                    pl_idx(idx) = pl_idx(idx) + 1
                    particle_lst(idx,pl_idx(idx)) = i_part
                end do

                do i=1, 8
                    if(pl_idx(i) /= 0) then
                        ! The node is not empty so it should be created
                        i_node = i_node + 1
                        t_children(i,iparent) = i_node
                        node2particle%ri(i_node+1) = node2particle%ri(i_node)
                        ! The node is not empty
                        if(pl_idx(i) == 1) then
                            ! Only one particle in this node
                            n_dimension(:,i_node) = 1.0
                            n_centers(:,i_node) = c_particle(:,particle_lst(i,1))
                            node2particle%ci(node2particle%ri(i_node+1)) = particle_lst(i,1)
                            node2particle%ri(i_node+1) = node2particle%ri(i_node+1) + 1
                        else
                            n_centers(:,i_node) = 0.0
                            minc = c_particle(:,particle_lst(i,1))
                            maxc = minc
                            do j=1, pl_idx(i)
                                node2particle%ci(node2particle%ri(i_node+1)) = particle_lst(i,j)
                                node2particle%ri(i_node+1) = node2particle%ri(i_node+1) + 1

                                do d=1, 3
                                    minc(d) = min(minc(d), c_particle(d,particle_lst(i,j)))
                                    maxc(d) = max(maxc(d), c_particle(d,particle_lst(i,j)))
                                end do
                            end do
                            n_dimension(:,i_node) = (maxc - minc)
                            n_centers(:,i_node) = minc + n_dimension(:,i_node) * .5
                        end if
                    end if
                end do
            end do

            b_level = e_level + 1
            e_level = i_node
                    
            if(e_level - b_level < 0) exit
        end do
       
        ! Populate the octatree
        t%breadth = ilev
        t%n_nodes = i_node 
        call allocate_tree(t)
        
        t%parent(1) = 0
        t%node_level(1) = 1
        t%node_centroid(:,:) = n_centers(:,:t%n_nodes)
        
        t%children(:,:) = 0
        t%particle_list%ri(1) = 1
        do i=1, t%n_nodes
            if(i > 1) t%node_level(i) = t%node_level(t%parent(i)) + 1
            !t%node_dimension(i) = maxval(n_dimension(:,i)) * 0.7072 !sqrt(2.0) / 2.0
            
            ii = 1
            do j=1, t%tree_degree
                if(t_children(j,i) > 0) then
                    t%children(ii,i) = t_children(j,i)
                    t%parent(t%children(ii,i)) = i
                    ii = ii + 1
                end if
            end do

            t%particle_list%ri(i+1) = t%particle_list%ri(i)
            if(all(t%children(:,i) == 0)) then
                ! Node is a leaf, list all particles in this node
                do j=node2particle%ri(i), node2particle%ri(i+1)-1
                    t%particle_list%ci(t%particle_list%ri(i+1)) = node2particle%ci(j)
                    t%particle_list%ri(i+1) = t%particle_list%ri(i+1) + 1
                end do
            end if
        end do

        call populate_level_list(t)
        call populate_leaf_list(t)
        
        do i=1, t%n_nodes
            if(all(t%children(:,i) == 0)) then
                t%node_dimension(i) = norm2(n_dimension(:,i)) / 2.0
            end if 
        end do

        do i=t%breadth, 1, -1
            do jj=t%level_list%ri(i), t%level_list%ri(i+1)-1
                j = t%level_list%ci(jj)
                if((any(t%children(:,j) /= 0))) then
                    do ii=1, t%tree_degree
                        l = t%children(ii,j)
                        if(l == 0)  cycle
                        dist = norm2(t%node_centroid(:,j)- t%node_centroid(:,l))
                        t%node_dimension(j) = max(t%node_dimension(j), dist + t%node_dimension(l))
                    end do 
                end if
            end do
        end do

        call tree_populate_farnear_lists(t, dfar)
        
        t%particle_to_node = 0
        do i=1, t%n_nodes
            if(t%children(1,i) /= 0) cycle
            ! The node is a leaf
            do j=t%particle_list%ri(i), t%particle_list%ri(i+1) - 1
                t%particle_to_node(t%particle_list%ci(j)) = i
            end do
        end do

        do i=1, t%n_particles
            if(t%particle_to_node(i) == 0) then
                call fmm_error("Some particles aren't in any node")
            end if
        end do
    end subroutine
    
end module