Skip to content
Snippets Groups Projects
Commit 8e71918e authored by Simon Praetorius's avatar Simon Praetorius
Browse files

Rewrite FindParMETIS to provide imported targets

Test for ParMETIS added

Move HAVE_PARMETIS and dune_register_package_flags to AddParMETISFlags

Add ENABLE_PTSCOTCH_PARMETIS for enabling ptscotch replacement for ParMETIS
parent 9a33a279
No related branches found
No related tags found
No related merge requests found
......@@ -10,20 +10,23 @@
# A list of targets to use ParMETIS with.
#
# set HAVE_PARMETIS for config.h
set(HAVE_PARMETIS ${ParMETIS_FOUND})
# register all ParMETIS related flags
if(PARMETIS_FOUND)
dune_register_package_flags(
COMPILE_DEFINITIONS "ENABLE_PARMETIS=1"
LIBRARIES "ParMETIS::ParMETIS"
)
endif()
# add function to link against the ParMETIS library
function(add_dune_parmetis_flags _targets)
if(PARMETIS_FOUND)
foreach(_target ${_targets})
target_link_libraries(${_target} ${PARMETIS_LIBRARY} ${METIS_LIBRARY})
GET_TARGET_PROPERTY(_props ${_target} INCLUDE_DIRECTORIES)
string(REPLACE "_props-NOTFOUND" "" _props "${_props}")
SET_TARGET_PROPERTIES(${_target} PROPERTIES INCLUDE_DIRECTORIES
"${_props};${PARMETIS_INCLUDE_DIRS}")
GET_TARGET_PROPERTY(_props ${_target} COMPILE_DEFINITIONS)
string(REPLACE "_props-NOTFOUND" "" _props "${_props}")
SET_TARGET_PROPERTIES(${_target} PROPERTIES COMPILE_DEFINITIONS
"${_props};ENABLE_PARMETIS")
endforeach(_target ${_targets})
add_dune_mpi_flags(${_targets})
endif(PARMETIS_FOUND)
target_link_libraries(${_target} ParMETIS::ParMETIS)
target_compile_definitions(${_target} "ENABLE_PARMETIS=1")
endforeach(_target)
endif()
endfunction(add_dune_parmetis_flags)
......@@ -97,7 +97,6 @@ unset(METIS_HEADER_FILE CACHE)
# Scotch provides METIS-3 interface only in version < 6.07, but provides an option to
# select the API-version in later Scotch releases
if(ENABLE_SCOTCH_METIS)
include(CMakeFindDependencyMacro)
find_package(PTScotch QUIET COMPONENTS SCOTCH)
set(HAVE_SCOTCH_METIS ${PTScotch_FOUND})
if (PTScotch_FOUND)
......
# .. cmake_module::
#
# Module that checks whether ParMETIS is available.
#
# You may set the following variables to configure this modules behavior:
#
# :ref:`PARMETIS_ROOT`
# Prefix where ParMETIS is installed.
#
# :ref:`PARMETIS_LIB_NAME`
# Name of the ParMETIS library (default: parmetis).
#
# :ref:`PARMETIS_LIBRARY`
# Full path of the ParMETIS library
#
# Sets the following variables:
#
# :code:`PARMETIS_FOUND`
# True if ParMETIS was found.
#
# :code:`PARMETIS_LIBRARY`
# Full path of the ParMETIS library.
#
# :code:`PARMETIS_LIBRARIES`
# List of all libraries needed for linking with ParMETIS,
#
# .. cmake_variable:: PARMETIS_ROOT
#
# You may set this variable to have :ref:`FindParMETIS` look
# for the ParMETIS library and includes in the given path
# before inspecting default system paths.
#
# .. cmake_variable:: PARMETIS_LIB_NAME
#
# You may set this variable to specify the name of the ParMETIS
# library that :ref:`FindParMETIS` looks for.
#
# .. cmake_variable:: PARMETIS_LIBRARY
#
# You may set this variable to specify the full path to the ParMETIS
# library, that should be used by :ref:`FindParMETIS`.
#
# find METIS first
#[=======================================================================[.rst:
FindParMETIS
------------
Find Parallel Graph Partitioning library ParMETIS
(see http://glaros.dtc.umn.edu/gkhome/metis/parmetis/overview)
Imported targets
^^^^^^^^^^^^^^^^
This module defines the following :prop_tgt:`IMPORTED` target:
``ParMETIS::ParMETIS``
The libraries, flags, and includes to use for ParMETIS, if found.
Result Variables
^^^^^^^^^^^^^^^^
This module defines the following variables:
``ParMETIS_FOUND``
The ParMETIS library with all its dependencies is found
Cache Variables
^^^^^^^^^^^^^^^
The following variables may be set to influence this module's behavior:
``PARMETIS_INCLUDE_DIR``
Include directory where the parmetis.h is found.
``PARMETIS_LIBRARY``
Full path to the ParMETIS library
``ENABLE_PTSCOTCH_PARMETIS``
Use the PTScotch library as ParMETIS compatibility library. This library
provides an interface of some ParMETIS library functions.
#]=======================================================================]
# text for feature summary
include(FeatureSummary)
set_package_properties("ParMETIS" PROPERTIES
DESCRIPTION "Parallel Graph Partitioning"
)
# The PTScotch library provides a wrapper around some functions of ParMETIS, since not
# the full interface, you have to request it explicitly.
option(ENABLE_PTSCOTCH_PARMETIS "Use the (PT)Scotch library as ParMETIS compatibility library" OFF)
# find package dependencies first
find_package(METIS QUIET)
if(NOT METIS_FOUND)
find_package_handle_standard_args(
"ParMETIS"
DEFAULT_MSG "METIS not found which is required for ParMETIS."
)
find_package(MPI QUIET)
find_path(PARMETIS_INCLUDE_DIR parmetis.h
PATH_SUFFIXES parmetis)
# search ParMETIS library
find_library(PARMETIS_LIBRARY parmetis)
if(ENABLE_PTSCOTCH_PARMETIS)
find_library(PARMETIS_LIBRARY ptscotchparmetis)
endif()
mark_as_advanced(PARMETIS_INCLUDE_DIR PARMETIS_LIBRARY)
find_path(PARMETIS_INCLUDE_DIR parmetis.h
PATHS ${PARMETIS_DIR} ${PARMETIS_ROOT}
PATH_SUFFIXES include parmetis
NO_DEFAULT_PATH
DOC "Include directory of ParMETIS")
find_path(PARMETIS_INCLUDE_DIR parmetis.h
PATH_SUFFIXES include parmetis)
set(PARMETIS_LIB_NAME parmetis
CACHE STRING "Name of the ParMETIS library (default: parmetis).")
set(PARMETIS_LIBRARY ParMETIS_LIBRARY-NOTFOUND
CACHE FILEPATH "Full path of the ParMETIS library")
# check ParMETIS headers
include(CMakePushCheckState)
cmake_push_check_state() # Save variables
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${MPI_DUNE_INCLUDE_PATH} ${METIS_INCLUDE_DIRS} ${PARMETIS_INCLUDE_DIR})
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${MPI_DUNE_COMPILE_FLAGS}")
# set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES} ${METIS_LIBRARIES}")
check_include_file(parmetis.h PARMETIS_FOUND)
if(PARMETIS_FOUND)
set(ParMETIS_INCLUDE_PATH ${CMAKE_REQUIRED_INCLUDES})
set(ParMETIS_COMPILE_FLAGS "${CMAKE_REQUIRED_FLAGS} -DENABLE_PARMETIS=1")
# search ParMETIS library
find_library(PARMETIS_LIBRARY ${PARMETIS_LIB_NAME}
PATHS ${PARMETIS_DIR} ${PARMETIS_ROOT}
PATH_SUFFIXES lib
NO_DEFAULT_PATH)
find_library(PARMETIS_LIBRARY ${PARMETIS_LIB_NAME})
# check ParMETIS library
if(PARMETIS_LIBRARY)
set(_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") # do a backup
set(_PARMETIS_LIBRARIES "${PARMETIS_LIBRARY};${MPI_DUNE_LIBRARIES}")
list(APPEND CMAKE_REQUIRED_LIBRARIES "${_PARMETIS_LIBRARIES};${METIS_LIBRARIES}")
include(CheckFunctionExists)
check_function_exists(ParMETIS_V3_PartKway HAVE_PARMETIS)
if(NOT HAVE_PARMETIS)
# Maybe we are using static scotch libraries. In this case we need to link
# the other scotch libraries too. Let's make a best effort.
# Get the path where ParMETIS_LIBRARY resides
get_filename_component(_lib_root ${METIS_LIBRARY} DIRECTORY)
# Search for additional libs only in this directory.
# Otherwise we might find incompatible ones, e.g. for int instead of long
find_library(PTSCOTCH_LIBRARY ptscotch PATHS ${_lib_root} "The PT-Scotch library."
NO_DEFAULT_PATH)
find_library(PTSCOTCHERR_LIBRARY ptscotcherr PATHS ${_lib_root} "The Scotch error library."
NO_DEFAULT_PATH)
if(PTSCOTCH_LIBRARY AND PTSCOTCHERR_LIBRARY)
set(_PARMETIS_LIBRARIES ${PARMETIS_LIBRARY} ${PTSCOTCH_LIBRARY}
${PTSCOTCHERR_LIBRARY} ${METIS_LIBRARIES} ${MPI_DUNE_LIBRARIES})
set(CMAKE_REQUIRED_LIBRARIES ${_PARMETIS_LIBRARIES}
${_CMAKE_REQUIRED_LIBRARIES})
unset(HAVE_PARMETIS CACHE)
check_function_exists(ParMETIS_V3_PartKway HAVE_PARMETIS)
endif()
endif()
set(CMAKE_REQUIRED_LIBRARIES "${_CMAKE_REQUIRED_LIBRARIES}") # get backup
# determine version of ParMETIS installation
find_file(PARMETIS_HEADER_FILE parmetis.h
PATHS ${PARMETIS_INCLUDE_DIR}
NO_DEFAULT_PATH)
if(PARMETIS_HEADER_FILE)
file(READ "${PARMETIS_HEADER_FILE}" parmetisheader)
string(REGEX REPLACE ".*#define PARMETIS_MAJOR_VERSION[ ]+([0-9]+).*" "\\1"
ParMETIS_MAJOR_VERSION "${parmetisheader}")
string(REGEX REPLACE ".*#define PARMETIS_MINOR_VERSION[ ]+([0-9]+).*" "\\1"
ParMETIS_MINOR_VERSION "${parmetisheader}")
if(ParMETIS_MAJOR_VERSION GREATER_EQUAL 0 AND ParMETIS_MINOR_VERSION GREATER_EQUAL 0)
set(ParMETIS_VERSION "${ParMETIS_MAJOR_VERSION}.${ParMETIS_MINOR_VERSION}")
endif()
endif()
unset(PARMETIS_HEADER_FILE CACHE)
# set a flag whether all ParMETIS dependencies are found correctly
if(METIS_FOUND AND MPI_FOUND)
set(PARMETIS_DEPENDENCIES_FOUND TRUE)
# minimal requires METIS version 5.0 for ParMETIS >= 4.0
if (ParMETIS_VERSION VERSION_GREATER_EQUAL "4.0"
AND METIS_VERSION VERSION_LESS "5.0")
set(PARMETIS_DEPENDENCIES_FOUND FALSE)
endif()
endif()
# If ptscotch-parmetis is requested, find package PTScotch
if(ENABLE_PTSCOTCH_PARMETIS)
find_package(PTScotch QUIET COMPONENTS PTSCOTCH)
set(HAVE_PTSCOTCH_PARMETIS ${PTScotch_FOUND})
if(PTScotch_FOUND AND MPI_FOUND)
set(PARMETIS_DEPENDENCIES_FOUND TRUE)
endif()
endif()
# behave like a CMake module is supposed to behave
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
"ParMETIS"
DEFAULT_MSG
PARMETIS_INCLUDE_DIR
PARMETIS_LIBRARY
HAVE_PARMETIS
find_package_handle_standard_args("ParMETIS"
REQUIRED_VARS
PARMETIS_LIBRARY PARMETIS_INCLUDE_DIR PARMETIS_DEPENDENCIES_FOUND
VERSION_VAR
ParMETIS_VERSION
)
mark_as_advanced(PARMETIS_INCLUDE_DIR PARMETIS_LIBRARY PARMETIS_LIB_NAME)
#restore old values
cmake_pop_check_state()
if(PARMETIS_FOUND)
set(PARMETIS_INCLUDE_DIRS ${PARMETIS_INCLUDE_DIR})
set(PARMETIS_LIBRARIES "${_PARMETIS_LIBRARIES}"
CACHE FILEPATH "ParMETIS libraries needed for linking")
set(PARMETIS_LINK_FLAGS "${DUNE_MPI_LINK_FLAGS}"
CACHE STRING "ParMETIS link flags")
# log result
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
"Determing location of ParMETIS succeeded:\n"
"Include directory: ${PARMETIS_INCLUDE_DIRS}\n"
"Library directory: ${PARMETIS_LIBRARIES}\n\n")
# deprecate versions < 4
file(READ "${PARMETIS_INCLUDE_DIR}/parmetis.h" parmetisheader)
string(REGEX MATCH "#define PARMETIS_MAJOR_VERSION[ ]+[0-9]+" versionMacroDef "${parmetisheader}")
string(REGEX MATCH "[0-9]+" ParMetisMajorVersion "${versionMacroDef}")
if("${versionMacroDef}" STREQUAL "" OR "${ParMetisMajorVersion}" LESS 4)
message(AUTHOR_WARNING "Support for ParMETIS older than version 4.x is deprecated in Dune 2.7")
endif()
else()
# log erroneous result
file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
"Determing location of ParMETIS failed:\n"
"Include directory: ${PARMETIS_INCLUDE_DIR}\n"
"ParMETIS library directory: ${PARMETIS_LIBRARY}\n\n")
endif()
# create imported target ParMETIS::ParMETIS
if(PARMETIS_FOUND AND NOT TARGET ParMETIS::ParMETIS)
add_library(ParMETIS::ParMETIS UNKNOWN IMPORTED)
set_target_properties(ParMETIS::ParMETIS PROPERTIES
IMPORTED_LOCATION ${PARMETIS_LIBRARY}
INTERFACE_INCLUDE_DIRECTORIES ${PARMETIS_INCLUDE_DIR}
INTERFACE_LINK_LIBRARIES MPI::MPI_C
)
# register all ParMETIS related flags
if(PARMETIS_FOUND)
dune_register_package_flags(COMPILE_DEFINITIONS "ENABLE_PARMETIS=1"
LIBRARIES "${PARMETIS_LIBRARIES}"
INCLUDE_DIRS "${PARMETIS_INCLUDE_DIRS}")
# link against PTScotch or METIS if needed
if(ENABLE_PTSCOTCH_PARMETIS AND PTScotch_FOUND)
set_property(TARGET ParMETIS::ParMETIS APPEND PROPERTY
INTERFACE_LINK_LIBRARIES PTScotch::PTScotch)
else()
set_property(TARGET ParMETIS::ParMETIS APPEND PROPERTY
INTERFACE_LINK_LIBRARIES METIS::METIS)
endif()
endif()
......@@ -180,11 +180,11 @@
/* Define to 1 if METIS is available */
#cmakedefine HAVE_METIS 1
/* Define to 1 if you have the ParMETIS library. */
#cmakedefine HAVE_PARMETIS 1
/* Define to ENABLE_PARMETIS if you have the Parmetis library.
This is only true if MPI was found
by configure _and_ if the application uses the PARMETIS_CPPFLAGS */
#cmakedefine HAVE_PARMETIS ENABLE_PARMETIS
/* Define to 1 if you have the PTScotch replacement for ParMETIS is used. */
#cmakedefine HAVE_PTSCOTCH_PARMETIS 1
/* Define to 1 if PT-Scotch is available */
#cmakedefine HAVE_PTSCOTCH 1
......
......@@ -274,6 +274,17 @@ dune_add_test(SOURCES parametertreetest.cc
LINK_LIBRARIES dunecommon
LABELS quick)
find_package(ParMETIS 4.0)
dune_add_test(SOURCES parmetistest.cc
MPI_RANKS 4
TIMEOUT 300
COMPILE_DEFINITIONS "HAVE_PARMETIS"
CMAKE_GUARD ParMETIS_FOUND
LABELS quick)
if (ParMETIS_FOUND)
target_link_libraries(parmetistest ParMETIS::ParMETIS)
endif()
dune_add_test(SOURCES pathtest.cc
LINK_LIBRARIES dunecommon
LABELS quick)
......
#include <config.h>
#include <cassert>
#include <iostream>
#include <vector>
#if ! HAVE_PARMETIS
#error "ParMETIS is required for this test."
#endif
#include <mpi.h>
#if HAVE_PTSCOTCH_PARMETIS
extern "C" {
#include <ptscotch.h>
}
#endif
extern "C" {
#include <parmetis.h>
}
int main(int argc, char **argv)
{
#if defined(REALTYPEWIDTH)
using real_t = ::real_t;
#else
using real_t = float;
#endif
#if defined(IDXTYPEWIDTH)
using idx_t = ::idx_t;
#elif HAVE_PTSCOTCH_PARMETIS
using idx_t = SCOTCH_Num;
#else
using idx_t = int;
#endif
MPI_Init(&argc, &argv);
MPI_Comm comm;
MPI_Comm_dup(MPI_COMM_WORLD, &comm);
int rank, size;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &size);
// This test is design for 3 cores
assert(size == 3);
// local adjacency structure of the graph
std::vector<idx_t> xadj; // size n+1
std::vector<idx_t> adjncy; // size 2*m
if (rank == 0) {
xadj = std::vector<idx_t>{0,2,5,8,11,13};
adjncy = std::vector<idx_t>{1,5,0,2,6,1,3,7,2,4,8,3,9};
}
else if (rank == 1) {
xadj = std::vector<idx_t>{0,3,7,11,15,18};
adjncy = std::vector<idx_t>{0,6,10,1,5,7,11,2,6,8,12,3,7,9,13,4,8,14};
}
else if (rank == 2) {
xadj = std::vector<idx_t>{0,2,5,8,11,13};
adjncy = std::vector<idx_t>{5,11,6,10,12,7,11,13,8,12,14,9,13};
}
// Array describing how the vertices of the graph are distributed among the processors.
std::vector<idx_t> vtxdist{0,5,10,15};
// No weights
idx_t wgtflag = 0;
// C-style numbering that starts from 0.
idx_t numflag = 0;
// Number of weights that each vertex has
idx_t ncon = 1;
// Number of sub-domains
idx_t nparts = size;
// Fraction of vertex weight that should be distributed to each sub-domain for each
// balance constraint
std::vector<real_t> tpwgts(ncon * nparts, 1.0/nparts);
std::vector<real_t> ubvec(ncon, 1.05);
std::vector<idx_t> options{0, 0, 0};
idx_t edgecut;
std::vector<idx_t> part(xadj.size()-1, 0);
ParMETIS_V3_PartKway(vtxdist.data(), xadj.data(), adjncy.data(),
nullptr, nullptr, &wgtflag, &numflag, &ncon, &nparts, tpwgts.data(),
ubvec.data(), options.data(), &edgecut, part.data(), &comm);
for (std::size_t part_i = 0; part_i < part.size(); ++part_i) {
std::cout << "[" << rank << "] " << part_i << " => " << part[part_i] << std::endl;
}
MPI_Finalize();
return 0;
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment