$extrastylesheet
parallel_bin_sorter.C
Go to the documentation of this file.
00001 // The libMesh Finite Element Library.
00002 // Copyright (C) 2002-2014 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
00003 
00004 // This library is free software; you can redistribute it and/or
00005 // modify it under the terms of the GNU Lesser General Public
00006 // License as published by the Free Software Foundation; either
00007 // version 2.1 of the License, or (at your option) any later version.
00008 
00009 // This library is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // Lesser General Public License for more details.
00013 
00014 // You should have received a copy of the GNU Lesser General Public
00015 // License along with this library; if not, write to the Free Software
00016 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 
00019 // C++ includes
00020 #include <algorithm>  // std::lower_bound
00021 
00022 // Local includes
00023 #include "libmesh/libmesh_common.h"
00024 #include "libmesh/parallel_bin_sorter.h"
00025 #include "libmesh/parallel_histogram.h"
00026 #ifdef LIBMESH_HAVE_LIBHILBERT
00027 #  include "hilbert.h"
00028 #endif
00029 #include "libmesh/parallel.h"
00030 #include "libmesh/parallel_conversion_utils.h"
00031 
00032 namespace libMesh
00033 {
00034 
00035 
00036 
00037 namespace Parallel {
00038 
00039 template <typename KeyType, typename IdxType>
00040 BinSorter<KeyType,IdxType>::BinSorter (const Parallel::Communicator &comm_in,
00041                                        const std::vector<KeyType>& d) :
00042   ParallelObject(comm_in),
00043   data(d)
00044 {
00045   // Assume (& libmesh_assert) we are working with a sorted range
00046 
00047   // Ah...  is_sorted is an STL extension!
00048   //libmesh_assert (std::is_sorted (data.begin(), data.end()));
00049 
00050   // Home-grown is_sorted
00051   libmesh_assert (Parallel::Utils::is_sorted (data));
00052 }
00053 
00054 
00055 
00056 template <typename KeyType, typename IdxType>
00057 void BinSorter<KeyType,IdxType>::binsort (const IdxType nbins,
00058                                           KeyType max,
00059                                           KeyType min)
00060 {
00061   libmesh_assert_less (min, max);
00062 
00063   // Build a histogram in parallel from our data.
00064   // Use this to create quasi-uniform bins.
00065   Parallel::Histogram<KeyType,IdxType> phist (this->comm(), data);
00066   phist.make_histogram (nbins*50, max, min);
00067   phist.build_histogram ();
00068 
00069   const std::vector<IdxType>& histogram =
00070     phist.get_histogram();
00071 
00072 
00073   // Now we will locate the bin boundaries so
00074   // that each bin is roughly equal size
00075   {
00076     // Find the total size of the data set
00077     IdxType local_data_size = cast_int<IdxType>(data.size());
00078     IdxType global_data_size = cast_int<IdxType>(local_data_size);
00079 
00080     this->comm().sum(global_data_size);
00081 
00082     std::vector<IdxType> target_bin_size (nbins, global_data_size / nbins);
00083 
00084     // Equally distribute the remainder
00085     for (IdxType i=0; i<(global_data_size % nbins); i++)
00086       ++target_bin_size[i];
00087 
00088     // Set the iterators corresponding to the bin boundaries
00089     {
00090       std::vector<double> bin_bounds (nbins+1);
00091       bin_iters.resize  (nbins+1, data.begin());
00092 
00093       // Set the minimum bin boundary iterator
00094       bin_iters[0]  = data.begin();
00095       bin_bounds[0] = Parallel::Utils::to_double(min);
00096 
00097       // The current location in the histogram
00098       IdxType current_histogram_bin = 0;
00099 
00100       // How much above (+) or below (-) we are from the
00101       // target size for the last bin.
00102       // Note that when delta is (-) we will
00103       // accept a slightly larger size for the next bin,
00104       // the goal being to keep the whole mess average
00105       int delta = 0;
00106 
00107       // Set the internal bin boundary iterators
00108       for (IdxType b=0; b<nbins; ++b)
00109         {
00110           // The size of bin b.  We want this to
00111           // be ~= target_bin_size[b]
00112           int current_bin_size = 0;
00113 
00114           // Step through the histogram until we have the
00115           // desired bin size
00116           while ((current_bin_size + histogram[current_histogram_bin] + delta) <= target_bin_size[b])
00117             {
00118               // Don't index out of the histogram!
00119               if ((current_histogram_bin+1) == phist.n_bins())
00120                 break;
00121 
00122               current_bin_size += histogram[current_histogram_bin++];
00123             }
00124 
00125           delta += current_bin_size - target_bin_size[b];
00126 
00127           // Set the upper bound of the bin
00128           bin_bounds[b+1] = phist.upper_bound (current_histogram_bin);
00129           bin_iters[b+1]  = std::lower_bound(bin_iters[b], data.end(),
00130                                              Parallel::Utils::to_key_type<KeyType>(bin_bounds[b+1]));
00131         }
00132 
00133       // Just be sure the last boundary points to the right place
00134       bin_iters[nbins]  = data.end();
00135       // This gets destructed here anyway
00136       // bin_bounds[nbins] = Parallel::Utils::to_double(max);
00137     }
00138   }
00139 }
00140 
00141 }
00142 
00143 
00144 // Explicitly instantiate for int, double
00145 template class Parallel::BinSorter<int, unsigned int>;
00146 template class Parallel::BinSorter<double, unsigned int>;
00147 #ifdef LIBMESH_HAVE_LIBHILBERT
00148 template class Parallel::BinSorter<Hilbert::HilbertIndices, unsigned int>;
00149 #endif
00150 
00151 } // namespace libMesh