51 integer,
parameter :: &
56 integer,
parameter :: &
73 subroutine mesh_partition(mesh, namespace, space, lapl_stencil, vsize)
74 type(mesh_t),
intent(inout) :: mesh
75 type(namespace_t),
intent(in) :: namespace
76 class(space_t),
intent(in) :: space
77 type(stencil_t),
intent(in) :: lapl_stencil
78 integer,
intent(in) :: vsize
84 integer(int64) :: ivtmp, inb
85 integer :: ix(space%dim), jx(space%dim)
90 integer(imetis) :: ne_global
91 integer(imetis) :: nv_global
101 integer(imetis),
allocatable :: xadj_global(:)
102 integer(imetis),
allocatable :: adjncy_global(:)
103 integer(imetis),
allocatable :: adjncy(:)
104 integer(imetis),
allocatable :: xadj(:)
106 integer(imetis),
allocatable :: options(:)
108 integer(imetis) :: edgecut
110 real(real32),
allocatable :: tpwgts(:)
113 integer,
allocatable :: rcounts(:)
114 integer(imetis) :: tmp
115 integer(imetis),
allocatable :: rdispls(:)
116 integer(imetis),
allocatable :: part(:)
117 integer(imetis),
allocatable :: part_global(:)
118 integer(imetis),
allocatable :: vtxdist(:)
120 type(stencil_t) :: stencil
121 integer :: stencil_to_use, default_method, method
123 integer,
parameter :: STAR = 1, laplacian = 2
124 integer(imetis),
allocatable :: istart(:)
125 integer,
allocatable :: lsize(:)
127 integer :: default, ierr
131 if (mesh%np_global == 0)
then
132 message(1) =
'The mesh is empty and cannot be partitioned.'
154 call parse_variable(namespace,
'MeshPartitionPackage', default, library)
156#if !defined(HAVE_METIS)
157 if (library == metis)
then
158 message(1) =
'METIS was requested, but Octopus was compiled without it.'
162#if !defined(HAVE_PARMETIS)
164 message(1) =
'PARMETIS was requested, but Octopus was compiled without it.'
184 call parse_variable(namespace,
'MeshPartitionStencil', star, stencil_to_use)
186 if (stencil_to_use == star)
then
188 else if (stencil_to_use == laplacian)
then
195 if (mesh%np_global > huge(nv_global))
then
196 message(1) =
"Error: too many grid points for this version of metis/parmetis."
197 message(2) =
"Please use the 64-bit version."
205 npart = mesh%mpi_grp%size
206 ipart = mesh%mpi_grp%rank + 1
208 safe_allocate(istart(1:npart))
209 safe_allocate(lsize(1:npart))
210 safe_allocate(vtxdist(1:npart+1))
212 assert(ivtmp <= huge(0_imetis))
216 safe_allocate(part(1:nv))
222 call mesh%mpi_grp%allgather(nv, 1, mpi_integer, lsize(1), 1, mpi_integer)
224 vtxdist(1:npart) = istart(1:npart)
225 vtxdist(npart+1) = nv_global + 1
229 safe_allocate(xadj(1:nv+1))
230 safe_allocate(adjncy(1:
i4_to_imetis(stencil%size - 1)*nv))
233 do iv = 1, lsize(ipart)
240 do jp = 1, stencil%size
241 if (jp == stencil%center) cycle
245 jx = ix + stencil%points(:, jp)
247 if (all(jx >= mesh%idx%nr(1, :)) .and. all(jx <= mesh%idx%nr(2, :)))
then
251 if (inb /= 0 .and. inb <= nv_global)
then
270 if (
debug%info .or. library == metis)
then
272 safe_allocate(rcounts(1:npart))
273 safe_allocate(rdispls(1:npart))
275 safe_allocate(xadj_global(1:nv_global + 1))
277 rcounts(1:npart) = lsize(1:npart)
278 rdispls(1:npart) = vtxdist(1:npart) - 1
279 call mesh%mpi_grp%allgatherv(xadj(2:), lsize(ipart),
mpi_metis_int, &
282 do iv = 1, lsize(jpart)
283 xadj_global(istart(jpart) + iv) = xadj_global(istart(jpart) + iv) + xadj_global(istart(jpart)) - 1
287 ne_global = xadj_global(nv_global + 1)
288 safe_allocate(adjncy_global(1:ne_global))
290 rdispls(jpart) = xadj_global(vtxdist(jpart)) - 1
291 tmp = xadj_global(vtxdist(jpart+1)) - 1 - rdispls(jpart)
292 assert(tmp < huge(0_int32))
295 assert(xadj(nv+1)-1 < huge(0_int32))
299 safe_deallocate_a(rcounts)
300 safe_deallocate_a(rdispls)
306 message(1) =
'Info: Adjacency lists of the graph representing the grid'
307 message(2) =
'Info: are stored in debug/mesh_partition/mesh_graph.txt.'
308 message(3) =
'Info: Compatible with METIS programs pmetis and kmetis.'
309 message(4) =
'Info: First line contains number of vertices and edges.'
310 message(5) =
'Info: Edges are not directed and appear twice in the lists.'
313 call io_mkdir(
'debug/mesh_partition', namespace)
314 iunit =
io_open(
'debug/mesh_partition/mesh_graph.txt', namespace, action=
'write')
315 write(iunit, *) nv_global, ne_global/2
317 write(iunit, *) adjncy_global(xadj_global(iv):xadj_global(iv+1) - 1)
321 message(1) =
"Info: Done writing mesh_graph.txt."
327 safe_allocate(tpwgts(1:vsize))
328 tpwgts(1:vsize) = real(1.0, real32)/real(vsize, real32)
330 select case (library)
332 safe_allocate(options(1:40))
342 default_method =
graph
357 call parse_variable(namespace,
'MeshPartition', default_method, method)
359 safe_allocate(part_global(1:nv_global))
364 message(1) =
'Info: Using METIS 5 multilevel recursive bisection to partition the mesh.'
368 i4_to_imetis(vsize), tpwgts(1), 1.01_4, options(1), edgecut, part_global(1))
372 message(1) =
'Info: Using METIS 5 multilevel k-way algorithm to partition the mesh.'
376 i4_to_imetis(vsize), tpwgts(1), 1.01_4, options(1), edgecut, part_global(1))
380 message(1) =
'Selected partition method is not available in METIS 5.'
384 part(1:nv) = part_global(istart(ipart):istart(ipart) + nv - 1)
386 safe_deallocate_a(options)
387 safe_deallocate_a(part_global)
391 safe_allocate(options(1:3))
394 write(
message(1),
'(a)')
'Info: Using ParMETIS multilevel k-way algorithm to partition the mesh.'
400 1_imetis,
i4_to_imetis(mesh%mpi_grp%size), tpwgts(1), 1.05_4, &
401 options(1), edgecut, part(1), mesh%mpi_grp%comm%MPI_VAL)
405 safe_deallocate_a(options)
409 safe_deallocate_a(vtxdist)
411 if (
debug%info .or. library == metis)
then
412 safe_deallocate_a(xadj_global)
413 safe_deallocate_a(adjncy_global)
415 safe_deallocate_a(tpwgts)
416 safe_deallocate_a(xadj)
417 safe_deallocate_a(adjncy)
418 safe_deallocate_a(istart)
419 safe_deallocate_a(lsize)
422 assert(all(part(1:nv) > 0))
423 assert(all(part(1:nv) <= vsize))
426 safe_deallocate_a(part)
434 integer,
intent(in) :: ierr
442 message(1) =
"Metis: Input error."
445 message(1) =
"Metis: Unable to allocate required memory."
448 message(1) =
"Metis: Some type of error."
461 type(
mesh_t),
intent(inout) :: mesh
462 type(
mesh_t),
intent(in) :: parent
464 integer :: np, ip_local
465 integer(int64) :: istart, ip_global
466 integer(int64),
allocatable :: points(:)
467 integer,
allocatable :: part(:)
477 safe_allocate(points(1:np))
478 safe_allocate(part(1:np))
480 ip_global = istart + ip_local - 1
490 safe_deallocate_a(points)
491 safe_deallocate_a(part)
499 type(
mesh_t),
intent(in) :: mesh
500 integer,
intent(in) :: vsize
501 integer,
intent(out) :: ierr
504 character(len=6) :: numstring
516 message(1) =
"Debug: Writing mesh partition restart."
520 write(numstring,
'(i6.6)') vsize
524 message(1) =
"Unable to write inner mesh partition to 'inner_partition_"//trim(numstring)//
".obf'"
530 message(1) =
"Debug: Writing mesh partition restart done."
541 integer,
intent(out) :: ierr
544 character(len=6) :: numstring
545 character(len=MAX_PATH_LEN) :: filename, dir
558 message(1) =
"Debug: Reading mesh partition restart."
564 write(numstring,
'(i6.6)') mesh%mpi_grp%size
568 filename =
'inner_partition_'//numstring//
'.obf'
570 message(1) =
"Unable to read inner mesh partition, file '"//trim(filename)//
"' does not exist."
576 message(1) =
"Unable to read inner mesh partition from '"//trim(filename)//
"'."
588 message(1) =
"Debug: Reading mesh partition restart done."
598 type(
mesh_t),
intent(in) :: mesh
599 integer,
optional,
intent(in) :: iunit
600 type(
namespace_t),
optional,
intent(in) :: namespace
602 integer(int64) :: npoints
604 integer,
allocatable :: nghost(:), nbound(:), nlocal(:), nneigh(:)
605 integer :: num_ghost, num_bound, num_local, num_neigh
606 real(real64) :: quality
609 logical,
allocatable :: is_a_neigh(:)
615 npart = mesh%mpi_grp%size
616 npoints = mesh%np_part_global
618 safe_allocate(nghost(1:npart))
619 safe_allocate(nbound(1:npart))
620 safe_allocate(nlocal(1:npart))
621 safe_allocate(nneigh(1:npart))
622 safe_allocate(is_a_neigh(1:npart))
624 ipart = mesh%mpi_grp%rank + 1
627 num_ghost = mesh%pv%np_ghost
628 num_bound = mesh%pv%np_bndry
630 is_a_neigh = mesh%pv%ghost_rcounts > 0
631 num_neigh = count(is_a_neigh(1:npart))
633 safe_deallocate_a(is_a_neigh)
635 call mesh%mpi_grp%gather(num_neigh, 1, mpi_integer, nneigh(1), 1, mpi_integer, 0)
636 call mesh%mpi_grp%gather(num_local, 1, mpi_integer, nlocal(1), 1, mpi_integer, 0)
637 call mesh%mpi_grp%gather(num_ghost, 1, mpi_integer, nghost(1), 1, mpi_integer, 0)
638 call mesh%mpi_grp%gather(num_bound, 1, mpi_integer, nbound(1), 1, mpi_integer, 0)
641 scal = real(npart, real64) /npoints
645 quality = quality + (real(maxval(nlocal), real64) - real(minval(nlocal), real64))**3
646 quality = quality + (sum(real(nghost, real64) **2))
655 'Info: Mesh partition:'
659 write(
message(1),
'(a,e16.6)') &
660 ' Partition quality:', quality
665 ' Neighbours Ghost points'
666 write(
message(2),
'(a,i5,a,i10)') &
667 ' Average : ', nint(sum(real(nneigh, real64) )/npart),
' ', nint(sum(real(nghost, real64) )/npart)
668 write(
message(3),
'(a,i5,a,i10)') &
669 ' Minimum : ', minval(nneigh),
' ', minval(nghost)
670 write(
message(4),
'(a,i5,a,i10)') &
671 ' Maximum : ', maxval(nneigh),
' ', maxval(nghost)
677 ' Nodes in domain-group ', ipart
678 write(
message(2),
'(a,i10,a,i10)') &
679 ' Neighbours :', nneigh(ipart), &
680 ' Local points :', nlocal(ipart)
681 write(
message(3),
'(a,i10,a,i10)') &
682 ' Ghost points :', nghost(ipart), &
683 ' Boundary points :', nbound(ipart)
691 safe_deallocate_a(nghost)
692 safe_deallocate_a(nbound)
693 safe_deallocate_a(nlocal)
694 safe_deallocate_a(nneigh)
701 type(
mesh_t),
intent(in) :: mesh
705 integer(int64) :: ipg
707 character(len=6) :: filenum
709 if (.not.
debug%info)
return
713 call io_mkdir(
'debug/mesh_partition', namespace)
716 write(filenum,
'(i6.6)') mesh%mpi_grp%rank+1
719 iunit =
io_open(
'debug/mesh_partition/mesh_partition.'//filenum, &
720 namespace, action=
'write')
728 iunit =
io_open(
'debug/mesh_partition/mesh_partition_all.'//filenum, &
729 namespace, action=
'write')
730 do ip = 1, mesh%np_part
738 iunit =
io_open(
'debug/mesh_partition/mesh_partition_boundary', &
739 namespace, action=
'write')
740 do ipg = mesh%np_global+1, mesh%np_part_global
subroutine metis_error_code(ierr)
type(debug_t), save, public debug
real(real64), parameter, public m_zero
real(real64), parameter, public m_one
This module implements the index, used for the mesh points.
subroutine, public io_close(iunit, grp)
logical function, public io_file_exists(filename, msg)
Returns true if a file with name 'filename' exists and issues a reminder.
subroutine, public io_mkdir(fname, namespace, parents)
integer function, public io_open(file, namespace, action, status, form, position, die, recl, grp)
This module defines the meshes, which are used in Octopus.
subroutine, public mesh_global_index_to_coords(mesh, ipg, ix)
Given a global point index, this function returns the set of integer coordinates of the point.
integer(int64) function, public mesh_global_index_from_coords(mesh, ix)
This function returns the true global index of the point for a given vector of integer coordinates.
integer(int64) function, public mesh_local2global(mesh, ip)
This function returns the global mesh index for a given local index.
real(real64) function, dimension(1:mesh%box%dim), public mesh_x_global(mesh, ipg)
integer, parameter hilbert
subroutine, public mesh_partition_from_parent(mesh, parent)
create a mesh partition from a given parent mesh
subroutine, public mesh_partition_write_info(mesh, iunit, namespace)
subroutine, public mesh_partition_messages_debug(mesh, namespace)
integer, parameter parmetis
subroutine, public mesh_partition(mesh, namespace, space, lapl_stencil, vsize)
This routine converts the mesh given by grid points into a graph.
subroutine, public mesh_partition_dump(restart, mesh, vsize, ierr)
subroutine, public mesh_partition_load(restart, mesh, ierr)
subroutine, public messages_warning(no_lines, all_nodes, namespace)
subroutine, public messages_info(no_lines, iunit, verbose_limit, stress, all_nodes, namespace)
character(len=256), dimension(max_lines), public message
to be output by fatal, warning
subroutine, public messages_fatal(no_lines, only_root_writes, namespace)
subroutine, public messages_input_error(namespace, var, details, row, column)
This module contains interfaces for METIS and PARMETIS routines.
integer, parameter metis_ok
Returned normally.
integer, parameter metis_option_numbering
integer, parameter metis_error_memory
Returned due to insufficient memory.
integer, parameter metis_error_input
Returned due to erroneous inputs and/or options.
type(mpi_datatype), parameter mpi_metis_int
integer, parameter metis_error
Some other errors.
logical function mpi_grp_is_root(grp)
Is the current MPI process of grpcomm, root.
type(mpi_grp_t), public mpi_world
subroutine, public partition_init(partition, np_global, mpi_grp)
initialize the partition table
subroutine, public partition_set(partition, part)
subroutine, public partition_dump(partition, dir, filename, ierr)
write the partition data
subroutine, public partition_end(partition)
subroutine, public partition_load(partition, dir, filename, ierr)
read the partition data
subroutine, public partition_get_local_size(partition, istart, np_local)
character(len=max_path_len) function, public restart_dir(restart)
Returns the name of the directory containing the restart information. The use of this function should...
logical pure function, public restart_skip(restart)
Returns true if the restart information should neither be read nor written. This might happen because...
This module defines stencils used in Octopus.
subroutine, public stencil_end(this)
subroutine, public stencil_copy(input, output)
This module defines routines, generating operators for a star stencil.
subroutine, public stencil_star_get_lapl(this, dim, order)
Describes mesh distribution to nodes.