Skip to content

Commit d36b89f

Browse files
authored
Merge pull request #78 from SCOREC/cws/mpiSpr
support SPR with multiple processes
2 parents 52aaa41 + 4005dd8 commit d36b89f

File tree

5 files changed

+67
-20
lines changed

5 files changed

+67
-20
lines changed

.github/workflows/cmake.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ jobs:
2727
sudo apt-get install -yq cmake
2828
cmake --version
2929
30+
# There is a known bug using MPICH ubuntu-24.04 (ubuntu-latest as of 11/10/2025)
31+
# https://bugs.launchpad.net/ubuntu/+source/mpich/+bug/2072338
32+
- name: Install mpi
33+
run: |
34+
sudo apt-get update -yq
35+
sudo apt-get install -yq openmpi-bin libopenmpi-dev
36+
3037
## Kokkos
3138
- name: Kokkos Checkout repo
3239
uses: actions/checkout@v4

CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ option(MeshFields_USE_Cabana "Build with the Cabana storage backend" OFF)
99

1010
find_package(Kokkos REQUIRED)
1111
find_package(Omega_h REQUIRED)
12+
find_package(MPI REQUIRED)
1213

1314
# Verify Omega_h was built with Kokkos enabled
1415
if(NOT Omega_h_USE_Kokkos)
@@ -234,6 +235,18 @@ add_test_property(OmegahSprAdapt
234235
PASS_REGULAR_EXPRESSION
235236
"afterAdapt nTri: 14439; solution_1 min -4084.78 max 213.322")
236237

238+
test_func(OmegahSprAdapt_p2
239+
${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} 2 ${MPIEXEC_PREFLAGS}
240+
./OmegahSprAdapt ${CMAKE_SOURCE_DIR}/meshes/thwaites_basal_effectiveStrain.osh/
241+
thwaitesAdapted_p2 #output prefix
242+
0.1 # adapt ratio
243+
1.0 # min size
244+
16.0 # max size
245+
)
246+
add_test_property(OmegahSprAdapt_p2
247+
PASS_REGULAR_EXPRESSION
248+
"afterAdapt nTri: 14468; solution_1 min -4098.9 max 213.322")
249+
237250
#Code Coverage set up -------------------------------------------------------
238251

239252
option(meshfields_ENABLE_COVERAGE_BUILD "Do a coverage build" OFF)

src/MeshField_Integrate.hpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,12 @@ auto getJacobianDeterminants(FieldElement &fes,
196196
* - `post`: Called after the integration process ends.
197197
* - `atPoints`: Called for each integration point to perform user-defined
198198
* computations.
199+
* - `parallelReduce`: Called after `atPoints` to reduce process-local
200+
* integrations into a global mesh integration.
199201
*
200202
* To use this class, derive from it and implement the `atPoints` method.
201-
* Optionally, override `pre` and `post` for additional setup or cleanup.
203+
* Optionally, override `parallelReduce`, `pre` and `post` for distributed
204+
* memory support, additional setup, or cleanup.
202205
*/
203206
class Integrator {
204207
public:
@@ -227,9 +230,18 @@ class Integrator {
227230
*/
228231
virtual void atPoints(Kokkos::View<Real **> p, Kokkos::View<Real *> w,
229232
Kokkos::View<Real *> dV) = 0;
233+
234+
/** \brief User callback: distributed memory accumulation.
235+
*
236+
* \details If needed, this function supports the use of
237+
* inter-process communication (e.g., MPI) to reduce
238+
* process-local integrations into a global
239+
* mesh integration.
240+
*/
241+
virtual void parallelReduce(){};
242+
230243
/** \brief Run the Integrator over the local field elements.
231-
* \param fes FieldElement
232-
* FIXME make the sensible
244+
* \param fes FieldElement
233245
* */
234246
template <typename FieldElement> void process(FieldElement &fes) {
235247
constexpr auto topo = decltype(FieldElement::elm2dof)::getTopology();
@@ -239,9 +251,8 @@ class Integrator {
239251
auto weights = getIntegrationPointWeights(fes, ip);
240252
auto dV = getJacobianDeterminants(fes, localCoords, ip.size());
241253
atPoints(localCoords, weights, dV);
254+
parallelReduce();
242255
post();
243-
// TODO support distributed meshes by running a parallel reduction with user
244-
// functor
245256
}
246257

247258
protected:

src/MeshField_SPR_ErrorEstimator.hpp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ static double getNaN() { return std::numeric_limits<double>::quiet_NaN(); }
1919
/* common base for Scalar Integrator. */
2020
class SInt : public MeshField::Integrator {
2121
public:
22-
SInt(int order) : MeshField::Integrator(order), r(0) {}
22+
SInt(int order, Omega_h::CommPtr comm_in)
23+
: MeshField::Integrator(order), r(0), comm(comm_in) {}
2324
void reset() { r = 0; }
2425
MeshField::Real r;
26+
Omega_h::CommPtr comm;
27+
void parallelReduce() { r = comm->allreduce(r, OMEGA_H_SUM); }
2528
};
2629

2730
template <typename ShapeField> class Estimation {
@@ -83,8 +86,8 @@ template <typename EstimationT, typename OmegahMeshField>
8386
class SelfProduct : public SInt {
8487
public:
8588
SelfProduct(EstimationT &estimation_in, OmegahMeshField &omf_in)
86-
: SInt(estimation_in.integration_order), estimation(estimation_in),
87-
omf(omf_in) {}
89+
: SInt(estimation_in.integration_order, estimation_in.mesh.comm()),
90+
estimation(estimation_in), omf(omf_in) {}
8891
void atPoints(Kokkos::View<MeshField::Real **> p,
8992
Kokkos::View<MeshField::Real *> w,
9093
Kokkos::View<MeshField::Real *> dV) {
@@ -155,8 +158,9 @@ template <typename EstimationT, typename OmegahMeshField>
155158
class Error : public SInt {
156159
public:
157160
Error(EstimationT &estimation_in, OmegahMeshField &omf_in)
158-
: SInt(estimation_in.integration_order), estimation(estimation_in),
159-
omf(omf_in), errorNorm("errorNorm", estimation_in.mesh.nelems()) {}
161+
: SInt(estimation_in.integration_order, estimation_in.mesh.comm()),
162+
estimation(estimation_in), omf(omf_in),
163+
errorNorm("errorNorm", estimation_in.mesh.nelems()) {}
160164
void atPoints(Kokkos::View<MeshField::Real **> p,
161165
Kokkos::View<MeshField::Real *> w,
162166
Kokkos::View<MeshField::Real *> dV) {
@@ -270,7 +274,8 @@ template <typename EstimationT, typename OmegahMeshField, typename FieldElement>
270274
std::tuple<Kokkos::View<MeshField::Real *>, MeshField::Real>
271275
getSprSizeField(EstimationT &e, OmegahMeshField &omf, FieldElement &coordFe) {
272276
Error errorIntegrator(e, omf);
273-
errorIntegrator.process(coordFe);
277+
errorIntegrator.process(
278+
coordFe); // FIXME - needs to sync results across processes
274279
computeSizeFactor(e, omf, coordFe, errorIntegrator);
275280
getElementSizeField(e, errorIntegrator);
276281
return {averageToVertex(e.mesh, e.element_size), errorIntegrator.r};

test/testSprThwaitesAdapt.cpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ int main(int argc, char **argv) {
157157
auto world = lib.world();
158158
Omega_h::Mesh mesh(&lib);
159159
Omega_h::binary::read(argv[1], world, &mesh);
160+
160161
const auto prefix = std::string(argv[2]);
161162
// adaptRatio = 0.1 is used in scorec/core:cws/sprThwaites test/spr_test.cc
162163
const MeshField::Real adaptRatio = std::stof(argv[3]);
@@ -166,13 +167,21 @@ int main(int argc, char **argv) {
166167
argv[5]); // a value of 8 or 16 roughly maintains the boundary shape
167168
// in the downstream parts of the domain
168169
// where there is significant coarsening
169-
std::cout << "input mesh: " << argv[1] << " outputMeshPrefix: " << prefix
170-
<< " adaptRatio: " << adaptRatio << " max_size: " << max_size
171-
<< " min_size: " << min_size << "\n";
170+
if (!mesh.comm()->rank()) {
171+
std::cout << "input mesh: " << argv[1] << " outputMeshPrefix: " << prefix
172+
<< " adaptRatio: " << adaptRatio << " max_size: " << max_size
173+
<< " min_size: " << min_size << "\n";
174+
}
172175
const auto outname = prefix + "_adaptRatio_" + std::string(argv[3]) +
173176
"_maxSz_" + std::to_string(max_size) + "_minSz_" +
174177
std::to_string(min_size);
175178

179+
// equally distribute elements among processes; required when loading a serial
180+
// mesh
181+
mesh.balance();
182+
// Omega_h::project_by_fit used by spr requires ghosts
183+
mesh.set_parting(Omega_h_Parting::OMEGA_H_GHOSTED);
184+
176185
auto effectiveStrain = getEffectiveStrainRate(mesh);
177186
auto recoveredStrain = recoverLinearStrain(mesh, effectiveStrain);
178187
mesh.add_tag<Real>(VERT, "recoveredStrain", 1, recoveredStrain, false,
@@ -203,10 +212,10 @@ int main(int argc, char **argv) {
203212
Omega_h::ArrayType::VectorND);
204213

205214
{ // write vtk
206-
const std::string vtkFileName = "beforeAdapt" + outname + ".vtk";
215+
const std::string vtkFileName = "beforeAdapt_" + outname + ".vtk";
207216
Omega_h::vtk::write_parallel(vtkFileName, &mesh, 2);
208217
const std::string vtkFileName_edges =
209-
"beforeAdapt" + outname + "_edges.vtk";
218+
"beforeAdapt_" + outname + "_edges.vtk";
210219
Omega_h::vtk::write_parallel(vtkFileName_edges, &mesh, 1);
211220
}
212221

@@ -223,12 +232,14 @@ int main(int argc, char **argv) {
223232
printTriCount(&mesh, "afterAdapt");
224233

225234
const auto sol = mesh.get_array<Omega_h::Real>(VERT, "solution_1");
226-
const auto sol_min = Omega_h::get_min(sol);
227-
const auto sol_max = Omega_h::get_max(sol);
228-
std::cout << "solution_1 min " << sol_min << " max " << sol_max << '\n';
235+
const auto sol_min = Omega_h::get_min(mesh.comm(), sol);
236+
const auto sol_max = Omega_h::get_max(mesh.comm(), sol);
237+
if (!mesh.comm()->rank()) {
238+
std::cout << "solution_1 min " << sol_min << " max " << sol_max << '\n';
239+
}
229240

230241
{ // write vtk and osh for adapted mesh
231-
const std::string outfilename = "afterAdapt" + outname;
242+
const std::string outfilename = "afterAdapt_" + outname;
232243
Omega_h::vtk::write_parallel(outfilename + ".vtk", &mesh, 2);
233244
Omega_h::binary::write(outfilename + ".osh", &mesh);
234245
std::cout << "wrote adapted mesh: " << outfilename + ".osh"

0 commit comments

Comments
 (0)