• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //          Copyright Alain Miniussi 2014.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 // Authors: Alain Miniussi
7 
8 /** @file cartesian_communicator.hpp
9  *
10  *  This header defines facilities to support MPI communicators with
11  *  cartesian topologies.
12  *  If known at compiled time, the dimension of the implied grid
13  *  can be statically enforced, through the templatized communicator
14  *  class. Otherwise, a non template, dynamic, base class is provided.
15  *
16  */
17 #ifndef BOOST_MPI_CARTESIAN_COMMUNICATOR_HPP
18 #define BOOST_MPI_CARTESIAN_COMMUNICATOR_HPP
19 
20 #include <boost/mpi/communicator.hpp>
21 
22 #include <vector>
23 #include <utility>
24 #include <iostream>
25 #include <utility>
26 #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
27 #include <initializer_list>
28 #endif // BOOST_NO_CXX11_HDR_INITIALIZER_LIST
29 
30 // Headers required to implement cartesian topologies
31 #include <boost/shared_array.hpp>
32 #include <boost/assert.hpp>
33 #include <boost/foreach.hpp>
34 
35 namespace boost { namespace mpi {
36 
37 /**
38  * @brief Specify the size and periodicity of the grid in a single dimension.
39  *
40  * POD lightweight object.
41  */
42 struct cartesian_dimension {
43   /** The size of the grid n this dimension. */
44   int size;
45   /** Is the grid periodic in this dimension. */
46   bool periodic;
47 
cartesian_dimensionboost::mpi::cartesian_dimension48   cartesian_dimension(int sz = 0, bool p = false) : size(sz), periodic(p) {}
49 
50 private:
51   friend class boost::serialization::access;
52   template<class Archive>
serializeboost::mpi::cartesian_dimension53   void serialize(Archive & ar, const unsigned int version)
54   {
55     ar & size & periodic;
56   }
57 
58 };
59 
60 template <>
61 struct is_mpi_datatype<cartesian_dimension> : mpl::true_ { };
62 
63 /**
64  * @brief Test if the dimensions values are identical.
65  */
66 inline
67 bool
operator ==(cartesian_dimension const & d1,cartesian_dimension const & d2)68 operator==(cartesian_dimension const& d1, cartesian_dimension const& d2) {
69   return &d1 == &d2 || (d1.size == d2.size && d1.periodic == d2.periodic);
70 }
71 
72 /**
73  * @brief Test if the dimension values are different.
74  */
75 inline
76 bool
operator !=(cartesian_dimension const & d1,cartesian_dimension const & d2)77 operator!=(cartesian_dimension const& d1, cartesian_dimension const& d2) {
78   return !(d1 == d2);
79 }
80 
81 /**
82  * @brief Pretty printing of a cartesian dimension (size, periodic)
83  */
84 std::ostream& operator<<(std::ostream& out, cartesian_dimension const& d);
85 
86 /**
87  * @brief Describe the topology of a cartesian grid.
88  *
89  * Behave mostly like a sequence of @c cartesian_dimension with the notable
90  * exception that its size is fixed.
91  * This is a lightweight object, so that any constructor that could be considered
92  * missing could be replaced with a function (move constructor provided when supported).
93  */
94 class BOOST_MPI_DECL cartesian_topology
95   : private std::vector<cartesian_dimension> {
96   friend class cartesian_communicator;
97   typedef std::vector<cartesian_dimension> super;
98  public:
99   /**
100    * Retrieve a specific dimension.
101    */
102   using super::operator[];
103   /**
104    * @brief Topology dimentionality.
105    */
106   using super::size;
107   using super::begin;
108   using super::end;
109   using super::swap;
110 
111 #if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)
112   cartesian_topology() = delete;
113 #endif
114 #if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
115   cartesian_topology(cartesian_topology const&) = default;
116   cartesian_topology& operator=(cartesian_topology const&) = default;
117   // There is apparently no macro for checking the support of move constructor.
118   // Assume that defaulted function is close enough.
119 #if !defined(BOOST_NO_CXX11_DEFAULTED_MOVES)
cartesian_topology(cartesian_topology && other)120   cartesian_topology(cartesian_topology&& other) : super(other) {}
operator =(cartesian_topology && other)121   cartesian_topology& operator=(cartesian_topology&& other) {
122     stl().swap(other.stl());
123     return *this;
124   }
125 #endif
126   ~cartesian_topology() = default;
127 #endif
128   /**
129    * @brief Create a N dimension space.
130    * Each dimension is initialized as non periodic of size 0.
131    */
cartesian_topology(int ndim)132   cartesian_topology(int ndim)
133     : super(ndim) {}
134 
135   /**
136    * @brief Use the provided dimensions specification as initial values.
137    */
cartesian_topology(std::vector<cartesian_dimension> const & dims)138   cartesian_topology(std::vector<cartesian_dimension> const& dims)
139     : super(dims) {}
140 
141   /**
142    * @brief Use dimensions specification provided in the sequence container as initial values.
143    * #param dims must be a sequence container.
144    */
145   template<class InitArr>
cartesian_topology(InitArr dims)146   explicit cartesian_topology(InitArr dims)
147     : super(0) {
148     BOOST_FOREACH(cartesian_dimension const& d, dims) {
149       push_back(d);
150     }
151   }
152 #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
153   /**
154    * @brief Use dimensions specification provided in the initialization list as initial values.
155    * #param dims can be of the form { dim_1, false}, .... {dim_n, true}
156    */
cartesian_topology(std::initializer_list<cartesian_dimension> dims)157   explicit cartesian_topology(std::initializer_list<cartesian_dimension> dims)
158     : super(dims) {}
159 #endif
160   /**
161    * @brief Use dimensions specification provided in the array.
162    * #param dims can be of the form { dim_1, false}, .... {dim_n, true}
163    */
164   template<int NDIM>
cartesian_topology(cartesian_dimension (& dims)[NDIM])165   explicit cartesian_topology(cartesian_dimension (&dims)[NDIM])
166     : super(dims, dims+NDIM) {}
167 
168   /**
169    * @brief Use dimensions specification provided in the input ranges
170    * The ranges do not need to be the same size. If the sizes are different,
171    * the missing values will be complete with zeros of the dim and assumed non periodic.
172    * @param dim_rg     the dimensions, values must convert to integers.
173    * @param period_rg  the periodicities, values must convert to booleans.
174    * #param dims can be of the form { dim_1, false}, .... {dim_n, true}
175    */
176   template<class DimRg, class PerRg>
cartesian_topology(DimRg const & dim_rg,PerRg const & period_rg)177   cartesian_topology(DimRg const& dim_rg, PerRg const& period_rg)
178     : super(0) {
179     BOOST_FOREACH(int d, dim_rg) {
180       super::push_back(cartesian_dimension(d));
181     }
182     super::iterator it = begin();
183     BOOST_FOREACH(bool p, period_rg) {
184       if (it < end()) {
185         it->periodic = p;
186       } else {
187         push_back(cartesian_dimension(0,p));
188       }
189       ++it;
190     }
191   }
192 
193 
194   /**
195    * @brief Iterator based initializer.
196    * Will use the first n iterated values.
197    * Both iterators can be single pass.
198    * @param dit dimension iterator, value must convert to integer type.
199    * @param pit periodicity iterator, value must convert to booleans..
200    */
201   template<class DimIter, class PerIter>
cartesian_topology(DimIter dit,PerIter pit,int n)202   cartesian_topology(DimIter dit, PerIter pit, int n)
203     : super(n) {
204     for(int i = 0; i < n; ++i) {
205       (*this)[i] = cartesian_dimension(*dit++, *pit++);
206     }
207   }
208 
209   /**
210    * Export as an stl sequence.
211    */
stl()212   std::vector<cartesian_dimension>& stl() { return *this; }
213   /**
214    * Export as an stl sequence.
215    */
stl() const216   std::vector<cartesian_dimension> const& stl() const{ return *this; }
217   /**
218    * Split the topology in two sequences of sizes and periodicities.
219    */
220   void split(std::vector<int>& dims, std::vector<bool>& periodics) const;
221 };
222 
223 inline
224 bool
operator ==(cartesian_topology const & t1,cartesian_topology const & t2)225 operator==(cartesian_topology const& t1, cartesian_topology const& t2) {
226   return t1.stl() == t2.stl();
227 }
228 
229 inline
230 bool
operator !=(cartesian_topology const & t1,cartesian_topology const & t2)231 operator!=(cartesian_topology const& t1, cartesian_topology const& t2) {
232   return t1.stl() != t2.stl();
233 }
234 
235 /**
236  * @brief Pretty printing of a cartesian topology
237  */
238 std::ostream& operator<<(std::ostream& out, cartesian_topology const& t);
239 
240 /**
241  * @brief An MPI communicator with a cartesian topology.
242  *
243  * A @c cartesian_communicator is a communicator whose topology is
244  * expressed as a grid. Cartesian communicators have the same
245  * functionality as (intra)communicators, but also allow one to query
246  * the relationships among processes and the properties of the grid.
247  */
248 class BOOST_MPI_DECL cartesian_communicator : public communicator
249 {
250   friend class communicator;
251 
252   /**
253    * INTERNAL ONLY
254    *
255    * Construct a cartesian communicator given a shared pointer to the
256    * underlying MPI_Comm (which must have a cartesian topology).
257    * This operation is used for "casting" from a communicator to
258    * a cartesian communicator.
259    */
cartesian_communicator(const shared_ptr<MPI_Comm> & comm_ptr)260   explicit cartesian_communicator(const shared_ptr<MPI_Comm>& comm_ptr)
261     : communicator()
262   {
263     this->comm_ptr = comm_ptr;
264     BOOST_ASSERT(has_cartesian_topology());
265   }
266 
267 public:
268   /**
269    * Build a new Boost.MPI cartesian communicator based on the MPI
270    * communicator @p comm with cartesian topology.
271    *
272    * @p comm may be any valid MPI communicator. If @p comm is
273    * MPI_COMM_NULL, an empty communicator (that cannot be used for
274    * communication) is created and the @p kind parameter is
275    * ignored. Otherwise, the @p kind parameter determines how the
276    * Boost.MPI communicator will be related to @p comm:
277    *
278    *   - If @p kind is @c comm_duplicate, duplicate @c comm to create
279    *   a new communicator. This new communicator will be freed when
280    *   the Boost.MPI communicator (and all copies of it) is
281    *   destroyed. This option is only permitted if the underlying MPI
282    *   implementation supports MPI 2.0; duplication of
283    *   intercommunicators is not available in MPI 1.x.
284    *
285    *   - If @p kind is @c comm_take_ownership, take ownership of @c
286    *   comm. It will be freed automatically when all of the Boost.MPI
287    *   communicators go out of scope.
288    *
289    *   - If @p kind is @c comm_attach, this Boost.MPI communicator
290    *   will reference the existing MPI communicator @p comm but will
291    *   not free @p comm when the Boost.MPI communicator goes out of
292    *   scope. This option should only be used when the communicator is
293    *   managed by the user.
294    */
cartesian_communicator(const MPI_Comm & comm,comm_create_kind kind)295   cartesian_communicator(const MPI_Comm& comm, comm_create_kind kind)
296     : communicator(comm, kind)
297   {
298     BOOST_ASSERT(has_cartesian_topology());
299   }
300 
301   /**
302    *  Create a new communicator whose topology is described by the
303    *  given cartesian. The indices of the vertices in the cartesian will be
304    *  assumed to be the ranks of the processes within the
305    *  communicator. There may be fewer vertices in the cartesian than
306    *  there are processes in the communicator; in this case, the
307    *  resulting communicator will be a NULL communicator.
308    *
309    *  @param comm The communicator that the new, cartesian communicator
310    *  will be based on.
311    *
312    *  @param dims the cartesian dimension of the new communicator. The size indicate
313    *  the number of dimension. Some dimensions be set to zero, in which case
314    *  the corresponding dimension value is left to the system.
315    *
316    *  @param reorder Whether MPI is permitted to re-order the process
317    *  ranks within the returned communicator, to better optimize
318    *  communication. If false, the ranks of each process in the
319    *  returned process will match precisely the rank of that process
320    *  within the original communicator.
321    */
322   cartesian_communicator(const communicator&       comm,
323                          const cartesian_topology& dims,
324                          bool                      reorder = false);
325 
326   /**
327    * Create a new cartesian communicator whose topology is a subset of
328    * an existing cartesian cimmunicator.
329    * @param comm the original communicator.
330    * @param keep and array containiing the dimension to keep from the existing
331    * communicator.
332    */
333   cartesian_communicator(const cartesian_communicator& comm,
334                          const std::vector<int>&       keep );
335 
336   using communicator::rank;
337 
338   /**
339    * Retrive the number of dimension of the underlying toppology.
340    */
341   int ndims() const;
342 
343   /**
344    * Return the rank of the process at the given coordinates.
345    * @param coords the coordinates. the size must match the communicator's topology.
346    */
347   int rank(const std::vector<int>& coords) const;
348   /**
349    * Return the rank of the source and target destination process through a shift.
350    * @param dim the dimension in which the shift takes place. 0 <= dim <= ndim().
351    * @param disp the shift displacement, can be positive (upward) or negative (downward).
352    */
353   std::pair<int, int> shifted_ranks(int dim, int disp) const;
354   /**
355    * Provides the coordinates of the process with the given rank.
356    * @param rk the ranks in this communicator.
357    * @returns the coordinates.
358    */
359   std::vector<int> coordinates(int rk) const;
360   /**
361    * Retrieve the topology and coordinates of this process in the grid.
362    *
363    */
364   void topology( cartesian_topology&  dims, std::vector<int>& coords ) const;
365   /**
366    * Retrieve the topology of the grid.
367    *
368    */
369   cartesian_topology topology() const;
370 };
371 
372 /**
373  * Given en number of processes, and a partially filled sequence
374  * of dimension, try to complete the dimension sequence.
375  * @param nb_proc the numer of mpi processes.fill a sequence of dimension.
376  * @param dims a sequence of positive or null dimensions. Non zero dimension
377  *  will be left untouched.
378  */
379 std::vector<int>& cartesian_dimensions(int nb_proc, std::vector<int>&  dims);
380 
381 } } // end namespace boost::mpi
382 
383 #endif // BOOST_MPI_CARTESIAN_COMMUNICATOR_HPP
384