deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
process_grid.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2017 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
14
15#include <deal.II/lac/scalapack.templates.h>
16
18
19namespace
20{
31 std::pair<int, int>
32 compute_processor_grid_sizes(const MPI_Comm mpi_comm,
33 const unsigned int m,
34 const unsigned int n,
35 const unsigned int block_size_m,
36 const unsigned int block_size_n)
37 {
38 // Few notes from the ScaLAPACK user guide:
39 // It is possible to predict the best grid shape given the number of
40 // processes available: Pr x Pc <= P This, however, depends on the task to
41 // be done. LU , QR and QL factorizations perform better for “flat” process
42 // grids (Pr < Pc ) For large N, Pc = 2*Pr is a good choice, whereas for
43 // small N, one should choose small Pr Square or near square grids are more
44 // optimal for Cholesky factorization. LQ and RQ factorizations take
45 // advantage of “tall” grids (Pr > Pc )
46
47 // Below we always try to create 2d processor grids:
48
49 const int n_processes = Utilities::MPI::n_mpi_processes(mpi_comm);
50
51 // Get the total number of cores we can occupy in a rectangular dense matrix
52 // with rectangular blocks when every core owns only a single block:
53 const int n_processes_heuristic = int(std::ceil((1. * m) / block_size_m)) *
54 int(std::ceil((1. * n) / block_size_n));
55 const int Np = std::min(n_processes_heuristic, n_processes);
56
57 // Now we need to split Np into Pr x Pc. Assume we know the shape/ratio
58 // Pc =: ratio * Pr
59 // therefore
60 // Np = Pc * Pc / ratio
61 // for quadratic matrices the ratio equals 1
62 const double ratio = double(n) / m;
63 int Pc = static_cast<int>(std::sqrt(ratio * Np));
64
65 // one could rounds up Pc to the number which has zero remainder from the
66 // division of Np while ( Np % Pc != 0 )
67 // ++Pc;
68 // but this affects the grid shape dramatically, i.e. 10 cores 3x3 becomes
69 // 2x5.
70 // limit our estimate to be in [2, Np]
71 int n_process_columns = std::min(Np, std::max(2, Pc));
72 // finally, get the rows:
73 int n_process_rows = Np / n_process_columns;
74
75 Assert(n_process_columns >= 1 && n_process_rows >= 1 &&
76 n_processes >= n_process_rows * n_process_columns,
78 "error in process grid: " + std::to_string(n_process_rows) + "x" +
79 std::to_string(n_process_columns) + "=" +
80 std::to_string(n_process_rows * n_process_columns) + " out of " +
81 std::to_string(n_processes)));
82
83 return std::make_pair(n_process_rows, n_process_columns);
84
85 // For example,
86 // 320x320 with 32x32 blocks and 16 cores:
87 // Pc = 1.0 * Pr => 4x4 grid
88 // Pc = 0.5 * Pr => 8x2 grid
89 // Pc = 2.0 * Pr => 3x5 grid
90 }
91} // namespace
92
93namespace Utilities
94{
95 namespace MPI
96 {
98 const MPI_Comm mpi_comm,
99 const std::pair<unsigned int, unsigned int> &grid_dimensions)
100 : mpi_communicator(mpi_comm)
101 , this_mpi_process(Utilities::MPI::this_mpi_process(mpi_communicator))
102 , n_mpi_processes(Utilities::MPI::n_mpi_processes(mpi_communicator))
103 , n_process_rows(grid_dimensions.first)
104 , n_process_columns(grid_dimensions.second)
105 {
106 Assert(grid_dimensions.first > 0,
107 ExcMessage("Number of process grid rows has to be positive."));
108 Assert(grid_dimensions.second > 0,
109 ExcMessage("Number of process grid columns has to be positive."));
110
111 Assert(
112 grid_dimensions.first * grid_dimensions.second <= n_mpi_processes,
114 "Size of process grid is larger than number of available MPI processes."));
115
116 // processor grid order.
117 const bool column_major = false;
118
119 // Initialize Cblas context from the provided communicator
120 blacs_context = Csys2blacs_handle(mpi_communicator);
121 const char *order = (column_major ? "Col" : "Row");
122 // Note that blacs_context can be modified below. Thus Cblacs2sys_handle
123 // may not return the same MPI communicator.
124 Cblacs_gridinit(&blacs_context, order, n_process_rows, n_process_columns);
125
126 // Blacs may modify the grid size on processes which are not used
127 // in the grid. So provide copies below:
128 int procrows_ = n_process_rows;
129 int proccols_ = n_process_columns;
130 Cblacs_gridinfo(blacs_context,
131 &procrows_,
132 &proccols_,
135
136 // If this MPI core is not on the grid, flag it as inactive and
137 // skip all jobs
138 // Note that a different condition is used in FORTRAN code here
139 // https://stackoverflow.com/questions/18516915/calling-blacs-with-more-processes-than-used
141 mpi_process_is_active = false;
142 else
144
145 // Create an auxiliary communicator which has root and all inactive cores.
146 // Assume that inactive cores start with
147 // id=n_process_rows*n_process_columns
148 const unsigned int n_active_mpi_processes =
151 this_mpi_process >= n_active_mpi_processes,
153
154 std::vector<int> inactive_with_root_ranks;
155 inactive_with_root_ranks.push_back(0);
156 for (unsigned int i = n_active_mpi_processes; i < n_mpi_processes; ++i)
157 inactive_with_root_ranks.push_back(i);
158
159 // Get the group of processes in mpi_communicator
160 int ierr = 0;
161 MPI_Group all_group;
162 ierr = MPI_Comm_group(mpi_communicator, &all_group);
163 AssertThrowMPI(ierr);
164
165 // Construct the group containing all ranks we need:
166 MPI_Group inactive_with_root_group;
167 const int n = inactive_with_root_ranks.size();
168 ierr = MPI_Group_incl(all_group,
169 n,
170 inactive_with_root_ranks.data(),
171 &inactive_with_root_group);
172 AssertThrowMPI(ierr);
173
174 // Create the communicator based on inactive_with_root_group.
175 // Note that on all the active MPI processes (except for the one with
176 // rank 0) the resulting MPI_Comm mpi_communicator_inactive_with_root
177 // will be MPI_COMM_NULL.
178 const int mpi_tag =
180
181 ierr = MPI_Comm_create_group(mpi_communicator,
182 inactive_with_root_group,
183 mpi_tag,
185 AssertThrowMPI(ierr);
186
187 ierr = MPI_Group_free(&all_group);
188 AssertThrowMPI(ierr);
189 ierr = MPI_Group_free(&inactive_with_root_group);
190 AssertThrowMPI(ierr);
191
192 // Double check that the process with rank 0 in subgroup is active:
193 if constexpr (running_in_debug_mode())
194 {
195 if (mpi_communicator_inactive_with_root != MPI_COMM_NULL &&
199 }
200 }
201
202
203
205 const unsigned int n_rows_matrix,
206 const unsigned int n_columns_matrix,
207 const unsigned int row_block_size,
208 const unsigned int column_block_size)
209 : ProcessGrid(mpi_comm,
210 compute_processor_grid_sizes(mpi_comm,
211 n_rows_matrix,
212 n_columns_matrix,
213 row_block_size,
214 column_block_size))
215 {}
216
217
218
220 const unsigned int n_rows,
221 const unsigned int n_columns)
222 : ProcessGrid(mpi_comm, std::make_pair(n_rows, n_columns))
223 {}
224
225
226
235
236
237
238 template <typename NumberType>
239 void
240 ProcessGrid::send_to_inactive(NumberType *value, const int count) const
241 {
242 Assert(count > 0, ExcInternalError());
243 if (mpi_communicator_inactive_with_root != MPI_COMM_NULL)
244 {
245 const int ierr =
246 MPI_Bcast(value,
247 count,
248 Utilities::MPI::mpi_type_id_for_type<decltype(*value)>,
249 0 /*from root*/,
251 AssertThrowMPI(ierr);
252 }
253 }
254
255 } // namespace MPI
256} // namespace Utilities
257
258// instantiations
259
260template void
261Utilities::MPI::ProcessGrid::send_to_inactive<double>(double *,
262 const int) const;
263template void
264Utilities::MPI::ProcessGrid::send_to_inactive<float>(float *, const int) const;
265template void
266Utilities::MPI::ProcessGrid::send_to_inactive<int>(int *, const int) const;
267
ProcessGrid(const MPI_Comm mpi_communicator, const unsigned int n_rows, const unsigned int n_columns)
MPI_Comm mpi_communicator_inactive_with_root
void send_to_inactive(NumberType *value, const int count=1) const
const unsigned int n_mpi_processes
const unsigned int this_mpi_process
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
#define Assert(cond, exc)
#define AssertThrowMPI(error_code)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
@ process_grid_constructor
ProcessGrid::ProcessGrid.
Definition mpi_tags.h:122
unsigned int n_mpi_processes(const MPI_Comm mpi_communicator)
Definition mpi.cc:103
unsigned int this_mpi_process(const MPI_Comm mpi_communicator)
Definition mpi.cc:118
const MPI_Datatype mpi_type_id_for_type
Definition mpi.h:1685
void free_communicator(MPI_Comm mpi_communicator)
Definition mpi.cc:165
STL namespace.
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)