Skip to content

Commit e117d14

Browse files
authored
Merge pull request #487 from SCOREC/apw/lgmetis
Add METIS splitter/balancer
2 parents b51b538 + 79e201d commit e117d14

31 files changed

+1171
-95
lines changed

.github/workflows/cmake.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ jobs:
1717
build_type: [Debug, Release]
1818
no_mpi: [OFF, ON]
1919
cxx_standard: [11, 20]
20+
metis: [OFF, ON]
2021

2122
steps:
2223
- uses: actions/checkout@v4
@@ -27,12 +28,15 @@ jobs:
2728
run: |
2829
sudo apt update
2930
sudo apt install gcc-10 g++-10 mpich
31+
- name: install metis
32+
if: matrix.metis == 'ON'
33+
run: sudo apt install libmetis-dev
3034

3135
- name: Configure CMake
3236
env:
3337
MPICH_CXX: ${{matrix.compiler.CXX}}
3438
MPICH_CC: ${{matrix.compiler.CC}}
35-
run: cmake -S ${{github.workspace}} -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=mpicxx -DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_STANDARD=${{matrix.cxx_standard}} -DCMAKE_VERBOSE_MAKEFILE=ON -DMESHES=${{github.workspace}}/pumi-meshes -DIS_TESTING=ON -DSCOREC_CXX_WARNINGS=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/build/install -DSCOREC_NO_MPI=${{matrix.no_mpi}}
39+
run: cmake -S ${{github.workspace}} -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=mpicxx -DCMAKE_C_COMPILER=mpicc -DCMAKE_CXX_STANDARD=${{matrix.cxx_standard}} -DCMAKE_VERBOSE_MAKEFILE=ON -DMESHES=${{github.workspace}}/pumi-meshes -DIS_TESTING=ON -DSCOREC_CXX_WARNINGS=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/build/install -DSCOREC_NO_MPI=${{matrix.no_mpi}} -DENABLE_METIS=${{matrix.metis}}
3640

3741
- name: Build
3842
env:

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ add_subdirectory(apf_sim)
196196
add_subdirectory(mds)
197197
add_subdirectory(parma)
198198
add_subdirectory(zoltan)
199+
add_subdirectory(metis)
199200
add_subdirectory(pumi)
200201
add_subdirectory(ma)
201202
add_subdirectory(crv)

Doxyfile.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,8 @@ INPUT = \
782782
@CMAKE_CURRENT_SOURCE_DIR@/spr/spr.h \
783783
@CMAKE_CURRENT_SOURCE_DIR@/parma/parma.dox \
784784
@CMAKE_CURRENT_SOURCE_DIR@/parma/parma.h \
785+
@CMAKE_CURRENT_SOURCE_DIR@/metis/metis.dox \
786+
@CMAKE_CURRENT_SOURCE_DIR@/metis/apfMETIS.h \
785787
@CMAKE_CURRENT_SOURCE_DIR@/phasta/phasta.dox \
786788
@CMAKE_CURRENT_SOURCE_DIR@/phasta/chef.h \
787789
@CMAKE_CURRENT_SOURCE_DIR@/phasta/chef.cc \

Doxyfile_internal.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,8 @@ INPUT = \
815815
@CMAKE_CURRENT_SOURCE_DIR@/spr/spr.h \
816816
@CMAKE_CURRENT_SOURCE_DIR@/parma/parma.dox \
817817
@CMAKE_CURRENT_SOURCE_DIR@/parma/parma.h \
818+
@CMAKE_CURRENT_SOURCE_DIR@/metis/metis.dox \
819+
@CMAKE_CURRENT_SOURCE_DIR@/metis/apfMETIS.h \
818820
@CMAKE_CURRENT_SOURCE_DIR@/phasta/phasta.dox \
819821
@CMAKE_CURRENT_SOURCE_DIR@/phasta/chef.h \
820822
@CMAKE_CURRENT_SOURCE_DIR@/phasta/chef.cc \

apf/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ set(SOURCES
2020
apfMesh.cc
2121
apfMesh2.cc
2222
apfMigrate.cc
23+
apfOpposites.cc
2324
apfScalarElement.cc
2425
apfScalarField.cc
2526
apfShape.cc

apf/apfMesh.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,20 @@ void unpackTagInfo(std::string& name, int& type, int& size, pcu::PCU *PCUObj);
627627

628628
extern char const* const dimName[4];
629629

630+
/**
631+
* \brief Tag boundary faces with global ids of opposite elements
632+
*
633+
* This function creates a LONG tag of one value and attaches to all mesh faces
634+
* (edges) classified on a partition model face (edge) (i.e., a face in 3d or
635+
* edge in 2d that has a remote copy in another part) the global id of the mesh
636+
* region (face) on the other side.
637+
*
638+
* \param gn global element numbering
639+
* \param name the name of the resulting tag
640+
* \return a new MeshTag called name with global IDs of opposite elements
641+
*/
642+
MeshTag* tagOpposites(GlobalNumbering* gn, const char* name);
643+
630644
} //namespace apf
631645

632646
#endif

apf/apfOpposites.cc

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (C) 2025 Scientific Computation Research Center
3+
*
4+
* This work is open source software, licensed under the terms of the
5+
* BSD license as described in the LICENSE file in the top-level directory.
6+
*/
7+
8+
#include <apfMesh.h>
9+
#include <apfNumbering.h>
10+
#include <pcu_util.h>
11+
12+
namespace apf {
13+
14+
static bool hasOtherSide(Mesh* m, MeshEntity* s)
15+
{
16+
if (m->isShared(s))
17+
return true;
18+
if (!m->hasMatching())
19+
return false;
20+
Matches matches;
21+
m->getMatches(s, matches);
22+
return matches.getSize();
23+
}
24+
25+
static Copy getOtherSide(Mesh* m, MeshEntity* s)
26+
{
27+
if (m->isShared(s))
28+
return getOtherCopy(m, s);
29+
Matches matches;
30+
m->getMatches(s, matches);
31+
PCU_ALWAYS_ASSERT(matches.getSize() == 1);
32+
return matches[0];
33+
}
34+
35+
static MeshEntity* getSideElement(Mesh* m, MeshEntity* s)
36+
{
37+
return m->getUpward(s, 0);
38+
}
39+
40+
static long getElementGid(GlobalNumbering* gn, MeshEntity* e)
41+
{
42+
return getNumber(gn, Node(e, 0));
43+
}
44+
45+
static void packOtherGid(GlobalNumbering* gn, MeshEntity* s)
46+
{
47+
Mesh* m = getMesh(gn);
48+
Copy other = getOtherSide(m, s);
49+
long gid = getElementGid(gn, getSideElement(m, s));
50+
m->getPCU()->Pack(other.peer, other.entity);
51+
m->getPCU()->Pack(other.peer, gid);
52+
}
53+
54+
static void unpackOtherGid(Mesh* m, MeshTag* t)
55+
{
56+
MeshEntity* s;
57+
m->getPCU()->Unpack(s);
58+
long gid;
59+
m->getPCU()->Unpack(gid);
60+
m->setLongTag(s, t, &gid);
61+
}
62+
63+
MeshTag* tagOpposites(GlobalNumbering* gn, const char* name)
64+
{
65+
Mesh* m = getMesh(gn);
66+
MeshTag* t = m->createLongTag(name, 1);
67+
int sideDim = m->getDimension() - 1;
68+
m->getPCU()->Begin();
69+
MeshIterator* it = m->begin(sideDim);
70+
MeshEntity* e;
71+
while ((e = m->iterate(it)))
72+
if (hasOtherSide(m, e))
73+
packOtherGid(gn, e);
74+
m->end(it);
75+
m->getPCU()->Send();
76+
while (m->getPCU()->Receive())
77+
unpackOtherGid(m, t);
78+
return t;
79+
}
80+
81+
} // namespace apf

cmake/FindMETIS.cmake

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright (C) 2025 Scientific Computation Research Center
2+
#
3+
# This work is open source software, licensed under the terms of the
4+
# BSD license as described in the LICENSE file in the top-level directory.
5+
#
6+
#[====================================[
7+
FindMETIS.cmake
8+
9+
Hints:
10+
- `METIS_PREFIX`: The prefix for the METIS installation.
11+
12+
Once done this will define:
13+
- `METIS_FOUND`: True if METIS is found.
14+
- `METIS::METIS`: The IMPORTED library if found.
15+
- `METIS_LIBRARIES`: METIS libraries required for linking.
16+
- `METIS_INCLUDE_DIRS`: Directories containing METIS headers.
17+
]====================================]
18+
19+
cmake_policy(PUSH)
20+
21+
set(METIS_PREFIX "${Trilinos_PREFIX}" CACHE STRING "METIS install directory")
22+
find_path(METIS_INCLUDE_DIR metis.h HINTS "${METIS_PREFIX}/include")
23+
set(METIS_INCLUDE_DIRS ${METIS_INCLUDE_DIR})
24+
file(STRINGS "${METIS_INCLUDE_DIR}/metis.h" _METIS_VERSION_STRS
25+
REGEX "#define METIS_VER_(MAJOR|(SUB)?MINOR)"
26+
)
27+
set(METIS_VERSION_STR "")
28+
foreach(ver_comp IN LISTS _METIS_VERSION_STRS)
29+
if(METIS_VERSION_STR)
30+
string(APPEND METIS_VERSION_STR .)
31+
endif()
32+
string(REGEX REPLACE
33+
"^#define METIS_VER_(MAJOR|(SUB)?MINOR)[ \t]+" ""
34+
comp_num "${ver_comp}"
35+
)
36+
string(APPEND METIS_VERSION_STR "${comp_num}")
37+
endforeach()
38+
find_library(METIS_LIBRARY metis HINTS "${METIS_PREFIX}/lib")
39+
# Add imported library.
40+
add_library(METIS::METIS UNKNOWN IMPORTED)
41+
set_target_properties(METIS::METIS PROPERTIES
42+
INTERFACE_INCLUDE_DIRECTORIES "${METIS_INCLUDE_DIR}"
43+
IMPORTED_LINK_INTERFACE_LANGUAGE "C;CXX"
44+
IMPORTED_LOCATION "${METIS_LIBRARY}"
45+
)
46+
# Sometimes GKLib is an external dependency; usually it is in METIS itself.
47+
find_library(GK_LIBRARY GKlib HINTS "${METIS_PREFIX}/lib")
48+
if(EXISTS "${GK_LIBRARY}")
49+
set_target_property(METIS::METIS PROPERTIES
50+
INTERFACE_LINK_LIBRARIES "${GK_LIBRARY}"
51+
)
52+
set(METIS_LIBRARIES "${METIS_LIBRARY}" "${GK_LIBRARY}")
53+
else()
54+
set(METIS_LIBRARIES "${METIS_LIBRARY}")
55+
endif()
56+
include(FindPackageHandleStandardArgs)
57+
find_package_handle_standard_args(METIS
58+
REQUIRED_VARS METIS_INCLUDE_DIR METIS_LIBRARY
59+
VERSION_VAR METIS_VERSION_STR
60+
)
61+
mark_as_advanced(METIS_INCLUDE_DIR METIS_LIBRARY)
62+
cmake_policy(POP)

ma/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ target_link_libraries(ma
8181
mds
8282
parma
8383
apf_zoltan
84+
apf_metis
8485
pcu
8586
)
8687

ma/maBalance.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
#include <apfMesh.h>
2+
#include <apfShape.h>
3+
#include <lionPrint.h>
14
#include "maBalance.h"
25
#include "maAdapt.h"
36
#include <parma.h>
47
#include <apfZoltan.h>
8+
#include <apfMETIS.h>
59

610
#define MAX_ZOLTAN_GRAPH_RANKS 16*1024
711

@@ -114,6 +118,10 @@ void runParma(Adapt* a)
114118
runBalancer(a, Parma_MakeElmBalancer(a->mesh));
115119
}
116120

121+
void runMETIS(Adapt* a) {
122+
runBalancer(a, apf::makeMETISbalancer(a->mesh));
123+
}
124+
117125
void printEntityImbalance(Mesh* m)
118126
{
119127
double imbalance[4];
@@ -147,6 +155,10 @@ void preBalance(Adapt* a)
147155
runZoltan(a,apf::RIB);
148156
return;
149157
}
158+
if (in->shouldRunPreMetis) {
159+
runMETIS(a);
160+
return;
161+
}
150162
if (in->shouldRunPreParma) {
151163
runParma(a);
152164
return;
@@ -171,6 +183,10 @@ void preBalance(Adapt* a)
171183
runZoltan(a, apf::RIB);
172184
return;
173185
}
186+
#elif defined(PUMI_HAS_METIS)
187+
if (a->mesh->getPCU()->Peers() < APF_METIS_MAXRANKS) runMETIS(a);
188+
else runParma(a);
189+
return;
174190
#else
175191
runParma(a);
176192
return;
@@ -189,6 +205,10 @@ void midBalance(Adapt* a)
189205
runZoltan(a);
190206
return;
191207
}
208+
if (in->shouldRunMidMetis) {
209+
runMETIS(a);
210+
return;
211+
}
192212
if (in->shouldRunMidParma) {
193213
runParma(a);
194214
return;
@@ -211,6 +231,10 @@ void midBalance(Adapt* a)
211231
runZoltan(a, apf::RIB);
212232
return;
213233
}
234+
#elif defined(PUMI_HAS_METIS)
235+
if (a->mesh->getPCU()->Peers() < APF_METIS_MAXRANKS) runMETIS(a);
236+
else runParma(a);
237+
return;
214238
#else
215239
runParma(a);
216240
return;
@@ -235,6 +259,11 @@ void postBalance(Adapt* a)
235259
printEntityImbalance(a->mesh);
236260
return;
237261
}
262+
if (in->shouldRunPostMetis) {
263+
runMETIS(a);
264+
printEntityImbalance(a->mesh);
265+
return;
266+
}
238267
if (in->shouldRunPostParma) {
239268
runParma(a);
240269
printEntityImbalance(a->mesh);
@@ -261,6 +290,10 @@ void postBalance(Adapt* a)
261290
printEntityImbalance(a->mesh);
262291
return;
263292
}
293+
#elif defined(PUMI_HAS_METIS)
294+
if (a->mesh->getPCU()->Peers() < APF_METIS_MAXRANKS) runMETIS(a);
295+
else runParma(a);
296+
printEntityImbalance(a->mesh);
264297
#else
265298
runParma(a);
266299
printEntityImbalance(a->mesh);

0 commit comments

Comments
 (0)