Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:46:54

0001 //          Copyright Alain Miniussi 2014.
0002 // Distributed under the Boost Software License, Version 1.0.
0003 //    (See accompanying file LICENSE_1_0.txt or copy at
0004 //          http://www.boost.org/LICENSE_1_0.txt)
0005 
0006 // Authors: Alain Miniussi
0007 
0008 /** @file cartesian_communicator.hpp
0009  *
0010  *  This header defines facilities to support MPI communicators with
0011  *  cartesian topologies.
0012  *  If known at compiled time, the dimension of the implied grid 
0013  *  can be statically enforced, through the templatized communicator 
0014  *  class. Otherwise, a non template, dynamic, base class is provided.
0015  *  
0016  */
0017 #ifndef BOOST_MPI_CARTESIAN_COMMUNICATOR_HPP
0018 #define BOOST_MPI_CARTESIAN_COMMUNICATOR_HPP
0019 
0020 #include <boost/mpi/communicator.hpp>
0021 
0022 #include <vector>
0023 #include <utility>
0024 #include <iostream>
0025 #include <utility>
0026 #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
0027 #include <initializer_list>
0028 #endif // BOOST_NO_CXX11_HDR_INITIALIZER_LIST
0029 
0030 // Headers required to implement cartesian topologies
0031 #include <boost/shared_array.hpp>
0032 #include <boost/assert.hpp>
0033 #include <boost/foreach.hpp>
0034 
0035 namespace boost { namespace mpi {
0036 
0037 /**
0038  * @brief Specify the size and periodicity of the grid in a single dimension.
0039  *
0040  * POD lightweight object.
0041  */
0042 struct cartesian_dimension {
0043   /** The size of the grid n this dimension. */
0044   int size;
0045   /** Is the grid periodic in this dimension. */
0046   bool periodic;
0047   
0048   cartesian_dimension(int sz = 0, bool p = false) : size(sz), periodic(p) {}
0049   
0050 private:
0051   friend class boost::serialization::access;
0052   template<class Archive>
0053   void serialize(Archive & ar, const unsigned int version)
0054   {
0055     ar & size & periodic;
0056   }
0057 
0058 };
0059 
0060 template <>
0061 struct is_mpi_datatype<cartesian_dimension> : mpl::true_ { };
0062 
0063 /**
0064  * @brief Test if the dimensions values are identical.
0065  */
0066 inline
0067 bool
0068 operator==(cartesian_dimension const& d1, cartesian_dimension const& d2) {
0069   return &d1 == &d2 || (d1.size == d2.size && d1.periodic == d2.periodic);
0070 }
0071 
0072 /**
0073  * @brief Test if the dimension values are different.
0074  */
0075 inline
0076 bool
0077 operator!=(cartesian_dimension const& d1, cartesian_dimension const& d2) {
0078   return !(d1 == d2);
0079 }
0080 
0081 /**
0082  * @brief Pretty printing of a cartesian dimension (size, periodic)
0083  */
0084 std::ostream& operator<<(std::ostream& out, cartesian_dimension const& d);
0085 
0086 /**
0087  * @brief Describe the topology of a cartesian grid.
0088  * 
0089  * Behave mostly like a sequence of @c cartesian_dimension with the notable 
0090  * exception that its size is fixed.
0091  * This is a lightweight object, so that any constructor that could be considered 
0092  * missing could be replaced with a function (move constructor provided when supported).
0093  */
0094 class BOOST_MPI_DECL cartesian_topology 
0095   : private std::vector<cartesian_dimension> {
0096   friend class cartesian_communicator;
0097   typedef std::vector<cartesian_dimension> super;
0098  public:
0099   /** 
0100    * Retrieve a specific dimension.
0101    */
0102   using super::operator[];
0103   /** 
0104    * @brief Topology dimentionality.
0105    */
0106   using super::size;
0107   using super::begin;
0108   using super::end;
0109   using super::swap;
0110 
0111 #if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)
0112   cartesian_topology() = delete;
0113 #endif
0114 #if !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
0115   cartesian_topology(cartesian_topology const&) = default;
0116   cartesian_topology& operator=(cartesian_topology const&) = default;
0117   // There is apparently no macro for checking the support of move constructor.
0118   // Assume that defaulted function is close enough.
0119 #if !defined(BOOST_NO_CXX11_DEFAULTED_MOVES)
0120   cartesian_topology(cartesian_topology&& other) : super(other) {}
0121   cartesian_topology& operator=(cartesian_topology&& other) { 
0122     stl().swap(other.stl());
0123     return *this;
0124   }
0125 #endif
0126   ~cartesian_topology() = default;
0127 #endif
0128   /**
0129    * @brief Create a N dimension space.
0130    * Each dimension is initialized as non periodic of size 0.
0131    */
0132   cartesian_topology(int ndim) 
0133     : super(ndim) {}
0134   
0135   /**
0136    * @brief Use the provided dimensions specification as initial values.
0137    */
0138   cartesian_topology(std::vector<cartesian_dimension> const& dims) 
0139     : super(dims) {}
0140 
0141   /**
0142    * @brief Use dimensions specification provided in the sequence container as initial values.
0143    * #param dims must be a sequence container.
0144    */  
0145   template<class InitArr>
0146   explicit cartesian_topology(InitArr dims)
0147     : super(0) {
0148     BOOST_FOREACH(cartesian_dimension const& d, dims) {
0149       push_back(d);
0150     }
0151   }
0152 #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
0153   /**
0154    * @brief Use dimensions specification provided in the initialization list as initial values.
0155    * #param dims can be of the form { dim_1, false}, .... {dim_n, true}
0156    */    
0157   explicit cartesian_topology(std::initializer_list<cartesian_dimension> dims)
0158     : super(dims) {}
0159 #endif
0160   /**
0161    * @brief Use dimensions specification provided in the array.
0162    * #param dims can be of the form { dim_1, false}, .... {dim_n, true}
0163    */    
0164   template<int NDIM>
0165   explicit cartesian_topology(cartesian_dimension (&dims)[NDIM])
0166     : super(dims, dims+NDIM) {}
0167 
0168   /**
0169    * @brief Use dimensions specification provided in the input ranges
0170    * The ranges do not need to be the same size. If the sizes are different, 
0171    * the missing values will be complete with zeros of the dim and assumed non periodic.
0172    * @param dim_rg     the dimensions, values must convert to integers.
0173    * @param period_rg  the periodicities, values must convert to booleans.
0174    * #param dims can be of the form { dim_1, false}, .... {dim_n, true}
0175    */    
0176   template<class DimRg, class PerRg>
0177   cartesian_topology(DimRg const& dim_rg, PerRg const& period_rg) 
0178     : super(0) {
0179     BOOST_FOREACH(int d, dim_rg) {
0180       super::push_back(cartesian_dimension(d));
0181     }
0182     super::iterator it = begin();
0183     BOOST_FOREACH(bool p, period_rg) {
0184       if (it < end()) {
0185         it->periodic = p;
0186       } else {
0187         push_back(cartesian_dimension(0,p));
0188       }
0189       ++it;
0190     }
0191   }
0192 
0193   
0194   /**
0195    * @brief Iterator based initializer.
0196    * Will use the first n iterated values. 
0197    * Both iterators can be single pass.
0198    * @param dit dimension iterator, value must convert to integer type.
0199    * @param pit periodicity iterator, value must convert to booleans..
0200    */
0201   template<class DimIter, class PerIter>
0202   cartesian_topology(DimIter dit, PerIter pit, int n) 
0203     : super(n) {
0204     for(int i = 0; i < n; ++i) {
0205       (*this)[i] = cartesian_dimension(*dit++, *pit++);
0206     }
0207   }
0208   
0209   /**
0210    * Export as an stl sequence.
0211    */
0212   std::vector<cartesian_dimension>& stl() { return *this; }
0213   /**
0214    * Export as an stl sequence.
0215    */
0216   std::vector<cartesian_dimension> const& stl() const{ return *this; }
0217   /** 
0218    * Split the topology in two sequences of sizes and periodicities.
0219    */
0220   void split(std::vector<int>& dims, std::vector<bool>& periodics) const;
0221 };
0222 
0223 inline
0224 bool
0225 operator==(cartesian_topology const& t1, cartesian_topology const& t2) {
0226   return t1.stl() == t2.stl();
0227 }
0228 
0229 inline
0230 bool
0231 operator!=(cartesian_topology const& t1, cartesian_topology const& t2) {
0232   return t1.stl() != t2.stl();
0233 }
0234 
0235 /**
0236  * @brief Pretty printing of a cartesian topology
0237  */
0238 std::ostream& operator<<(std::ostream& out, cartesian_topology const& t);
0239 
0240 /**
0241  * @brief An MPI communicator with a cartesian topology.
0242  *
0243  * A @c cartesian_communicator is a communicator whose topology is
0244  * expressed as a grid. Cartesian communicators have the same
0245  * functionality as (intra)communicators, but also allow one to query
0246  * the relationships among processes and the properties of the grid.
0247  */
0248 class BOOST_MPI_DECL cartesian_communicator : public communicator
0249 {
0250   friend class communicator;
0251 
0252   /**
0253    * INTERNAL ONLY
0254    *
0255    * Construct a cartesian communicator given a shared pointer to the
0256    * underlying MPI_Comm (which must have a cartesian topology).
0257    * This operation is used for "casting" from a communicator to 
0258    * a cartesian communicator.
0259    */
0260   explicit cartesian_communicator(const shared_ptr<MPI_Comm>& comm_ptr)
0261     : communicator()
0262   {
0263     this->comm_ptr = comm_ptr;
0264     BOOST_ASSERT(has_cartesian_topology());    
0265   }
0266 
0267 public:
0268   /**
0269    * Build a new Boost.MPI cartesian communicator based on the MPI
0270    * communicator @p comm with cartesian topology.
0271    *
0272    * @p comm may be any valid MPI communicator. If @p comm is
0273    * MPI_COMM_NULL, an empty communicator (that cannot be used for
0274    * communication) is created and the @p kind parameter is
0275    * ignored. Otherwise, the @p kind parameter determines how the
0276    * Boost.MPI communicator will be related to @p comm:
0277    *
0278    *   - If @p kind is @c comm_duplicate, duplicate @c comm to create
0279    *   a new communicator. This new communicator will be freed when
0280    *   the Boost.MPI communicator (and all copies of it) is
0281    *   destroyed. This option is only permitted if the underlying MPI
0282    *   implementation supports MPI 2.0; duplication of
0283    *   intercommunicators is not available in MPI 1.x.
0284    *
0285    *   - If @p kind is @c comm_take_ownership, take ownership of @c
0286    *   comm. It will be freed automatically when all of the Boost.MPI
0287    *   communicators go out of scope.
0288    *
0289    *   - If @p kind is @c comm_attach, this Boost.MPI communicator
0290    *   will reference the existing MPI communicator @p comm but will
0291    *   not free @p comm when the Boost.MPI communicator goes out of
0292    *   scope. This option should only be used when the communicator is
0293    *   managed by the user.
0294    */
0295   cartesian_communicator(const MPI_Comm& comm, comm_create_kind kind)
0296     : communicator(comm, kind)
0297   { 
0298     BOOST_ASSERT(has_cartesian_topology());
0299   }
0300 
0301   /**
0302    *  Create a new communicator whose topology is described by the
0303    *  given cartesian. The indices of the vertices in the cartesian will be
0304    *  assumed to be the ranks of the processes within the
0305    *  communicator. There may be fewer vertices in the cartesian than
0306    *  there are processes in the communicator; in this case, the
0307    *  resulting communicator will be a NULL communicator.
0308    *
0309    *  @param comm The communicator that the new, cartesian communicator
0310    *  will be based on. 
0311    * 
0312    *  @param dims the cartesian dimension of the new communicator. The size indicate 
0313    *  the number of dimension. Some dimensions be set to zero, in which case
0314    *  the corresponding dimension value is left to the system.
0315    *  
0316    *  @param reorder Whether MPI is permitted to re-order the process
0317    *  ranks within the returned communicator, to better optimize
0318    *  communication. If false, the ranks of each process in the
0319    *  returned process will match precisely the rank of that process
0320    *  within the original communicator.
0321    */
0322   cartesian_communicator(const communicator&       comm,
0323                          const cartesian_topology& dims,
0324                          bool                      reorder = false);
0325   
0326   /**
0327    * Create a new cartesian communicator whose topology is a subset of
0328    * an existing cartesian cimmunicator.
0329    * @param comm the original communicator.
0330    * @param keep and array containiing the dimension to keep from the existing 
0331    * communicator.
0332    */
0333   cartesian_communicator(const cartesian_communicator& comm,
0334                          const std::vector<int>&       keep );
0335     
0336   using communicator::rank;
0337 
0338   /** 
0339    * Retrive the number of dimension of the underlying toppology.
0340    */
0341   int ndims() const;
0342   
0343   /**
0344    * Return the rank of the process at the given coordinates.
0345    * @param coords the coordinates. the size must match the communicator's topology.
0346    */
0347   int rank(const std::vector<int>& coords) const;
0348   /**
0349    * Return the rank of the source and target destination process through a shift.
0350    * @param dim the dimension in which the shift takes place. 0 <= dim <= ndim().
0351    * @param disp the shift displacement, can be positive (upward) or negative (downward).
0352    */
0353   std::pair<int, int> shifted_ranks(int dim, int disp) const;
0354   /**
0355    * Provides the coordinates of the process with the given rank.
0356    * @param rk the ranks in this communicator.
0357    * @returns the coordinates.
0358    */
0359   std::vector<int> coordinates(int rk) const;
0360   /**
0361    * Retrieve the topology and coordinates of this process in the grid.
0362    *
0363    */
0364   void topology( cartesian_topology&  dims, std::vector<int>& coords ) const;
0365   /**
0366    * Retrieve the topology of the grid.
0367    *
0368    */
0369   cartesian_topology topology() const;
0370 };
0371 
0372 /**
0373  * Given en number of processes, and a partially filled sequence 
0374  * of dimension, try to complete the dimension sequence.
0375  * @param nb_proc the numer of mpi processes.fill a sequence of dimension.
0376  * @param dims a sequence of positive or null dimensions. Non zero dimension 
0377  *  will be left untouched.
0378  */
0379 std::vector<int>& cartesian_dimensions(int nb_proc, std::vector<int>&  dims);
0380 
0381 } } // end namespace boost::mpi
0382 
0383 #endif // BOOST_MPI_CARTESIAN_COMMUNICATOR_HPP