ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
Histogram.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2016-2026 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <boost/array.hpp>
23#include <boost/multi_array.hpp>
24
25#include <algorithm>
26#include <array>
27#include <cassert>
28#include <cmath>
29#include <cstddef>
30#include <functional>
31#include <numeric>
32#include <span>
33#include <stdexcept>
34#include <utility>
35#include <vector>
36
37namespace Utils {
38
39/**
40 * \brief Histogram in Cartesian coordinates.
41 * \tparam T Histogram data type.
42 * \tparam N Histogram data dimensionality.
43 * \tparam M Coordinates data dimensionality.
44 * \tparam U Coordinates data type.
45 */
46template <typename T, std::size_t N, std::size_t M = 3, typename U = double>
47class Histogram {
48 using array_type = boost::multi_array<T, M + 1>;
49 using count_type = boost::multi_array<std::size_t, M + 1>;
50
51protected:
52 using array_index = typename array_type::index;
53
54public:
55 /**
56 * \brief Histogram constructor.
57 * \param n_bins the number of bins in each histogram dimension.
58 * \param limits the minimum/maximum data values to consider for the
59 * histogram.
60 */
61 Histogram(std::array<std::size_t, M> n_bins,
62 std::array<std::pair<U, U>, M> limits)
63 : m_n_bins(std::move(n_bins)), m_limits(std::move(limits)),
64 m_bin_sizes(calc_bin_sizes()), m_array(m_array_dim()),
65 m_count(m_array_dim()) {
66 m_ones.fill(T{1});
67 }
68
69 virtual ~Histogram() = default;
70
71 /** \brief Get the number of bins for each dimension. */
72 std::array<std::size_t, M> get_n_bins() const { return m_n_bins; }
73
74 /** \brief Get the histogram data. */
75 std::vector<T> get_histogram() const {
76 return {m_array.data(), m_array.data() + m_array.num_elements()};
77 }
78
79 /** \brief Get the histogram count data. */
80 std::vector<std::size_t> get_tot_count() const {
81 return {m_count.data(), m_count.data() + m_count.num_elements()};
82 }
83
84 /** \brief Get the ranges (min, max) for each dimension. */
85 std::array<std::pair<U, U>, M> get_limits() const { return m_limits; }
86
87 /** \brief Get the bin sizes. */
88 std::array<U, M> get_bin_sizes() const { return m_bin_sizes; }
89
90 /**
91 * \brief Add data to the histogram.
92 * \param pos Position to update.
93 */
94 void update(std::span<const U> pos) { update(pos, m_ones); }
95
96 /**
97 * \brief Add data to the histogram.
98 * \param pos Position to update.
99 * \param value Value to add.
100 */
101 void update(std::span<const U> pos, std::span<const T> value) {
102 if (pos.size() != M) {
103 throw std::invalid_argument("Wrong dimensions for the coordinates");
104 }
105 if (value.size() != N) {
106 throw std::invalid_argument("Wrong dimensions for the value");
107 }
108 if (check_limits(pos)) {
109 auto index = calc_bin_index(pos);
110 for (std::size_t i = 0; i < N; ++i) {
111 index.back() = static_cast<array_index>(i);
112 m_array(index) += value[i];
113 m_count(index)++;
114 }
115 }
116 }
117
118 /** \brief Normalize histogram. */
119 virtual void normalize() {
120 auto const bin_volume = std::accumulate(
121 m_bin_sizes.begin(), m_bin_sizes.end(), U{1}, std::multiplies<U>());
122 std::ranges::transform(
123 std::span(m_array.data(), m_array.num_elements()), m_array.data(),
124 [bin_volume](T v) { return static_cast<T>(v / bin_volume); });
125 }
126
127private:
128 /**
129 * \brief Calculate the bin index.
130 * \param pos Position.
131 */
132 auto calc_bin_index(std::span<const U> const &pos) const {
133 boost::array<array_index, M + 1> index;
134 for (std::size_t i = 0; i < M; ++i) {
135 auto const offset = m_limits[i].first;
136 auto const size = m_bin_sizes[i];
137 auto const n_bins = static_cast<long>(m_n_bins[i]);
138 auto const bin = static_cast<long>(std::floor((pos[i] - offset) / size));
139 // handle edge cases when the position is exactly between two bins:
140 // due to precision loss in the offset subtraction, the bin index might
141 // be off by one, so we fold it here back inside the valid range
142 index[i] = static_cast<array_index>(std::clamp(bin, 0l, n_bins - 1l));
143 }
144 return index;
145 }
146
147 /**
148 * \brief Calculate the bin sizes.
149 */
150 std::array<U, M> calc_bin_sizes() const {
151 std::array<U, M> bin_sizes;
152 for (std::size_t i = 0; i < M; ++i) {
153 bin_sizes[i] = (m_limits[i].second - m_limits[i].first) /
154 static_cast<U>(m_n_bins[i]);
155 }
156 return bin_sizes;
157 }
158
159 /**
160 * \brief Check if the position lies within the histogram limits.
161 * \param pos Position to check.
162 */
163 bool check_limits(std::span<const U> const &pos) const {
164 assert(pos.size() == M);
165 auto it_limits = m_limits.begin();
166 return std::ranges::all_of(pos, [&it_limits](U const value) {
167 auto const [lower, upper] = *it_limits;
168 ++it_limits;
169 return value >= lower and value < upper;
170 });
171 }
172
173 std::array<std::size_t, M + 1> m_array_dim() const {
174 std::array<std::size_t, M + 1> dimensions;
175 std::ranges::copy(m_n_bins, dimensions.begin());
176 dimensions.back() = N;
177 return dimensions;
178 }
179
180protected:
181 /// Number of bins for each dimension.
182 std::array<std::size_t, M> m_n_bins;
183 /// Min and max values for each dimension.
184 std::array<std::pair<U, U>, M> m_limits;
185 /// Bin sizes for each dimension.
186 std::array<U, M> m_bin_sizes;
187 /// Histogram data.
188 array_type m_array;
189 /// Track the number of total hits per bin entry.
190 count_type m_count;
191 std::array<T, N> m_ones;
192};
193
194/**
195 * \brief Histogram in cylindrical coordinates.
196 * \tparam T Histogram data type.
197 * \tparam N Histogram data dimensionality.
198 * \tparam M Coordinates data dimensionality.
199 * \tparam U Coordinates data type.
200 */
201template <typename T, std::size_t N, std::size_t M = 3, typename U = double>
202class CylindricalHistogram : public Histogram<T, N, M, U> {
204 using Base::m_array;
205 using Base::m_bin_sizes;
206 using Base::m_limits;
207 using Base::m_n_bins;
209
210public:
211 using Base::Histogram;
212
213 void normalize() override {
214 auto const min_r = m_limits[0].first;
215 auto const r_bin_size = m_bin_sizes[0];
216 auto const phi_bin_size = m_bin_sizes[1];
217 auto const z_bin_size = m_bin_sizes[2];
218 auto const n_bins_r = static_cast<array_index>(m_n_bins[0]);
219 for (array_index i = 0; i < n_bins_r; i++) {
220 auto const r_left = min_r + static_cast<U>(i) * r_bin_size;
221 auto const r_right = r_left + r_bin_size;
222 auto const bin_volume = (r_right * r_right - r_left * r_left) *
224 auto *begin = m_array[i].origin();
225 std::ranges::transform(
226 std::span(begin, m_array[i].num_elements()), begin,
227 [bin_volume](T v) { return static_cast<T>(v / bin_volume); });
228 }
229 }
230};
231
232} // Namespace Utils
Histogram in cylindrical coordinates.
Histogram in Cartesian coordinates.
Definition Histogram.hpp:47
std::array< U, M > get_bin_sizes() const
Get the bin sizes.
Definition Histogram.hpp:88
std::array< std::size_t, M > m_n_bins
Number of bins for each dimension.
std::array< U, M > m_bin_sizes
Bin sizes for each dimension.
virtual void normalize()
Normalize histogram.
std::array< std::pair< U, U >, M > m_limits
Min and max values for each dimension.
virtual ~Histogram()=default
count_type m_count
Track the number of total hits per bin entry.
typename array_type::index array_index
Definition Histogram.hpp:52
std::array< T, N > m_ones
std::vector< std::size_t > get_tot_count() const
Get the histogram count data.
Definition Histogram.hpp:80
std::vector< T > get_histogram() const
Get the histogram data.
Definition Histogram.hpp:75
void update(std::span< const U > pos, std::span< const T > value)
Add data to the histogram.
array_type m_array
Histogram data.
std::array< std::pair< U, U >, M > get_limits() const
Get the ranges (min, max) for each dimension.
Definition Histogram.hpp:85
Histogram(std::array< std::size_t, M > n_bins, std::array< std::pair< U, U >, M > limits)
Histogram constructor.
Definition Histogram.hpp:61
std::array< std::size_t, M > get_n_bins() const
Get the number of bins for each dimension.
Definition Histogram.hpp:72
void update(std::span< const U > pos)
Add data to the histogram.
Definition Histogram.hpp:94
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
STL namespace.