31#include <boost/mpi/collectives/gather.hpp>
32#include <boost/mpi/collectives/reduce.hpp>
33#include <boost/serialization/utility.hpp>
34#include <boost/serialization/vector.hpp>
47 std::vector<std::reference_wrapper<Particle const>>;
56 std::vector<int> m_ids;
58 virtual std::vector<double>
66 operator()(boost::mpi::communicator
const &comm)
const final;
67 std::vector<int>
const &
ids()
const {
return m_ids; }
76template <
class T>
struct shape_impl;
78template <>
struct shape_impl<double> {
79 static std::vector<std::size_t> eval(std::size_t ) {
return {1}; }
81template <
class _, std::
size_t N>
struct shape_impl<
Utils::Vector<_, N>> {
82 static std::vector<std::size_t> eval(std::size_t ) {
return {N}; }
84template <
class T>
struct shape_impl<std::
vector<T>> {
85 static std::vector<std::size_t> eval(std::size_t n_part) {
86 std::vector<std::size_t> ret{n_part};
87 std::ranges::copy(shape_impl<T>::eval(n_part), std::back_inserter(ret));
92template <
class T,
class U>
struct shape_impl<std::pair<T, U>> {
93 static std::vector<std::size_t> eval(std::size_t n_part) {
94 return shape_impl<T>::eval(n_part);
98inline auto get_argsort(boost::mpi::communicator
const &comm,
99 std::vector<int>
const &local_pids,
100 std::vector<int>
const &sorted_pids) {
101 std::vector<unsigned int> argsort{};
103 std::vector<std::vector<int>> global_pids;
104 boost::mpi::gather(comm, local_pids, global_pids, 0);
105 if (comm.rank() == 0) {
106 auto const n_part = sorted_pids.size();
107 std::vector<int> unsorted_pids;
108 unsorted_pids.reserve(n_part);
109 for (
auto const &vec : global_pids) {
110 for (
auto const pid : vec) {
111 unsorted_pids.emplace_back(pid);
115 argsort.reserve(n_part);
116 auto const pid_begin = std::begin(unsorted_pids);
117 for (
auto const pid : sorted_pids) {
118 auto const pid_pos = std::ranges::find(unsorted_pids, pid);
119 auto const hops = std::distance(pid_begin, pid_pos);
120 argsort.emplace_back(
static_cast<unsigned>(hops));
132get_all_particle_positions(boost::mpi::communicator
const &comm,
134 std::vector<int>
const &sorted_pids,
136 bool use_folded_positions =
false) {
137 using pos_type =
decltype(
traits.position(std::declval<Particle>()));
138 std::vector<pos_type> local_positions{};
139 std::vector<int> local_pids{};
140 local_positions.reserve(local_particles.size());
141 local_pids.reserve(local_particles.size());
143 for (
auto const &particle : local_particles) {
144 if (use_folded_positions) {
145 local_positions.emplace_back(
traits.position_folded(particle));
147 local_positions.emplace_back(
traits.position(particle));
149 local_pids.emplace_back(
traits.id(particle));
152 auto const argsort = detail::get_argsort(comm, local_pids, sorted_pids);
154 std::vector<std::vector<pos_type>> global_positions{};
155 global_positions.reserve(
static_cast<std::size_t
>(comm.size()));
156 boost::mpi::gather(comm, local_positions, global_positions, 0);
158 if (comm.rank() != 0) {
159 return std::vector<pos_type>();
162 std::vector<pos_type> global_positions_flattened{};
163 global_positions_flattened.reserve(sorted_pids.size());
164 for (
auto const &vec : global_positions) {
165 for (
auto const &pos : vec) {
166 global_positions_flattened.emplace_back(pos);
169 assert(global_positions_flattened.size() == sorted_pids.size());
171 std::vector<pos_type> positions_sorted{};
172 positions_sorted.reserve(sorted_pids.size());
173 for (
auto const i : argsort) {
174 positions_sorted.emplace_back(global_positions_flattened[i]);
177 return positions_sorted;
198 std::vector<std::size_t>
shape()
const override {
201 return detail::shape_impl<decltype(declval<ObsType>()(
202 declval<ParticleReferenceRange const &>()))>::eval(
ids().size());
205 template <
typename T>
struct is_map : std::false_type {};
206 template <
typename T>
214 std::vector<double> local_traits{};
215 local_traits.reserve(local_particles.size());
217 std::back_inserter(local_traits));
218 std::vector<int> local_pids{};
219 local_pids.reserve(local_particles.size());
221 std::back_inserter(local_pids));
223 std::vector<std::vector<double>> global_traits{};
224 boost::mpi::gather(comm, local_traits, global_traits, 0);
226 auto const argsort = detail::get_argsort(comm, local_pids,
ids());
228 if (comm.rank() != 0) {
233 auto const size = std::accumulate(
234 global_traits.begin(), global_traits.end(), 0u,
235 [](
auto const acc,
auto const &vec) { return acc + vec.size(); });
237 auto const n_dims = size /
ids().size();
239 std::vector<double> global_traits_flattened{};
240 global_traits_flattened.reserve(size);
242 for (
auto const &vec : global_traits) {
243 for (
auto const val : vec) {
244 global_traits_flattened.emplace_back(val);
248 std::vector<double> output{};
249 output.reserve(size);
251 for (
auto const i : argsort) {
252 for (std::size_t j = 0; j < n_dims; ++j) {
253 output.emplace_back(global_traits_flattened[i * n_dims + j]);
258 auto observable = ObsType{};
259 auto const local_result = observable(local_particles);
260 std::remove_const_t<
decltype(local_result)> result{};
261 boost::mpi::reduce(comm, local_result, result, observable, 0);
Vector implementation and trait types for boost qvm interoperability.
Base class for observables.
This class implements an interface to the particle_observables library that implements necessary algo...
std::vector< double > evaluate(boost::mpi::communicator const &comm, ParticleReferenceRange const &local_particles, ParticleObservables::traits< Particle > const &) const override
std::vector< std::size_t > shape() const override
Particle-based observable.
PidObservable(std::vector< int > ids)
std::vector< int > const & ids() const
std::vector< double > operator()(boost::mpi::communicator const &comm) const final
virtual std::vector< double > evaluate(boost::mpi::communicator const &comm, ParticleReferenceRange const &local_particles, const ParticleObservables::traits< Particle > &traits) const =0
std::vector< std::reference_wrapper< Particle const > > ParticleReferenceRange
void flatten(Range const &v, OutputIterator out)
Flatten a range of ranges.
std::vector< T, allocator< T > > vector