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-2022 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/multi_array.hpp>
23
24#include <algorithm>
25#include <array>
26#include <cassert>
27#include <cmath>
28#include <cstddef>
29#include <functional>
30#include <numeric>
31#include <span>
32#include <stdexcept>
33#include <utility>
34#include <vector>
35
36namespace Utils {
37
38/**
39 * \brief Histogram in Cartesian coordinates.
40 * \tparam T Histogram data type.
41 * \tparam N Histogram data dimensionality.
42 * \tparam M Coordinates data dimensionality.
43 * \tparam U Coordinates data type.
44 */
45template <typename T, std::size_t N, std::size_t M = 3, typename U = double>
46class Histogram {
47 using array_type = boost::multi_array<T, M + 1>;
48 using count_type = boost::multi_array<std::size_t, M + 1>;
49
50protected:
51 using array_index = typename array_type::index;
52
53public:
54 /**
55 * \brief Histogram constructor.
56 * \param n_bins the number of bins in each histogram dimension.
57 * \param limits the minimum/maximum data values to consider for the
58 * histogram.
59 */
60 Histogram(std::array<std::size_t, M> n_bins,
61 std::array<std::pair<U, U>, M> limits)
62 : m_n_bins(std::move(n_bins)), m_limits(std::move(limits)),
63 m_bin_sizes(calc_bin_sizes()), m_array(m_array_dim()),
64 m_count(m_array_dim()) {
65 m_ones.fill(T{1});
66 }
67
68 virtual ~Histogram() = default;
69
70 /** \brief Get the number of bins for each dimension. */
71 std::array<std::size_t, M> get_n_bins() const { return m_n_bins; }
72
73 /** \brief Get the histogram data. */
74 std::vector<T> get_histogram() const {
75 return {m_array.data(), m_array.data() + m_array.num_elements()};
76 }
77
78 /** \brief Get the histogram count data. */
79 std::vector<std::size_t> get_tot_count() const {
80 return {m_count.data(), m_count.data() + m_count.num_elements()};
81 }
82
83 /** \brief Get the ranges (min, max) for each dimension. */
84 std::array<std::pair<U, U>, M> get_limits() const { return m_limits; }
85
86 /** \brief Get the bin sizes. */
87 std::array<U, M> get_bin_sizes() const { return m_bin_sizes; }
88
89 /**
90 * \brief Add data to the histogram.
91 * \param pos Position to update.
92 */
93 void update(std::span<const U> pos) { update(pos, m_ones); }
94
95 /**
96 * \brief Add data to the histogram.
97 * \param pos Position to update.
98 * \param value Value to add.
99 */
100 void update(std::span<const U> pos, std::span<const T> value) {
101 if (pos.size() != M) {
102 throw std::invalid_argument("Wrong dimensions for the coordinates");
103 }
104 if (value.size() != N) {
105 throw std::invalid_argument("Wrong dimensions for the value");
106 }
107 if (check_limits(pos)) {
108 boost::array<array_index, M + 1> index;
109 for (std::size_t i = 0; i < M; ++i) {
110 index[i] = calc_bin_index(pos[i], m_limits[i].first, m_bin_sizes[i]);
111 }
112 for (array_index i = 0; i < static_cast<array_index>(N); ++i) {
113 index.back() = i;
114 m_array(index) += value[static_cast<std::size_t>(i)];
115 m_count(index)++;
116 }
117 }
118 }
119
120 /** \brief Normalize histogram. */
121 virtual void normalize() {
122 auto const bin_volume = std::accumulate(
123 m_bin_sizes.begin(), m_bin_sizes.end(), U{1}, std::multiplies<U>());
124 std::transform(
125 m_array.data(), m_array.data() + m_array.num_elements(), m_array.data(),
126 [bin_volume](T v) { return static_cast<T>(v / bin_volume); });
127 }
128
129private:
130 /**
131 * \brief Calculate the bin index.
132 * \param value Position on that dimension.
133 * \param offset Bin offset on that dimension.
134 * \param size Bin size on that dimension.
135 */
136 array_index calc_bin_index(double value, double offset, double size) const {
137 return static_cast<array_index>(std::floor((value - offset) / size));
138 }
139
140 /**
141 * \brief Calculate the bin sizes.
142 */
143 std::array<U, M> calc_bin_sizes() const {
144 std::array<U, M> bin_sizes;
145 for (std::size_t i = 0; i < M; ++i) {
146 bin_sizes[i] = (m_limits[i].second - m_limits[i].first) /
147 static_cast<U>(m_n_bins[i]);
148 }
149 return bin_sizes;
150 }
151
152 /**
153 * \brief Check if the position lies within the histogram limits.
154 * \param pos Position to check.
155 */
156 bool check_limits(std::span<const U> pos) const {
157 assert(pos.size() == M);
158 bool within_range = true;
159 for (std::size_t i = 0; i < M; ++i) {
160 if (pos[i] < m_limits[i].first or pos[i] >= m_limits[i].second)
161 within_range = false;
162 }
163 return within_range;
164 }
165
166 std::array<std::size_t, M + 1> m_array_dim() const {
167 std::array<std::size_t, M + 1> dimensions;
168 std::copy(m_n_bins.begin(), m_n_bins.end(), dimensions.begin());
169 dimensions.back() = N;
170 return dimensions;
171 }
172
173protected:
174 /// Number of bins for each dimension.
175 std::array<std::size_t, M> m_n_bins;
176 /// Min and max values for each dimension.
177 std::array<std::pair<U, U>, M> m_limits;
178 /// Bin sizes for each dimension.
179 std::array<U, M> m_bin_sizes;
180 /// Histogram data.
181 array_type m_array;
182 /// Track the number of total hits per bin entry.
183 count_type m_count;
184 std::array<T, N> m_ones;
185};
186
187/**
188 * \brief Histogram in cylindrical coordinates.
189 * \tparam T Histogram data type.
190 * \tparam N Histogram data dimensionality.
191 * \tparam M Coordinates data dimensionality.
192 * \tparam U Coordinates data type.
193 */
194template <typename T, std::size_t N, std::size_t M = 3, typename U = double>
195class CylindricalHistogram : public Histogram<T, N, M, U> {
196 using Histogram<T, N, M, U>::m_n_bins;
197 using Histogram<T, N, M, U>::m_limits;
198 using Histogram<T, N, M, U>::m_bin_sizes;
199 using Histogram<T, N, M, U>::m_array;
201
202public:
203 using Histogram<T, N, M, U>::Histogram;
204
205 void normalize() override {
206 auto const min_r = m_limits[0].first;
207 auto const r_bin_size = m_bin_sizes[0];
208 auto const phi_bin_size = m_bin_sizes[1];
209 auto const z_bin_size = m_bin_sizes[2];
210 auto const n_bins_r = static_cast<array_index>(m_n_bins[0]);
211 for (array_index i = 0; i < n_bins_r; i++) {
212 auto const r_left = min_r + static_cast<U>(i) * r_bin_size;
213 auto const r_right = r_left + r_bin_size;
214 auto const bin_volume = (r_right * r_right - r_left * r_left) *
215 z_bin_size * phi_bin_size / U(2);
216 auto *begin = m_array[i].origin();
217 std::transform(
218 begin, begin + m_array[i].num_elements(), begin,
219 [bin_volume](T v) { return static_cast<T>(v / bin_volume); });
220 }
221 }
222};
223
224} // Namespace Utils
Histogram in cylindrical coordinates.
Histogram in Cartesian coordinates.
Definition Histogram.hpp:46
std::array< U, M > get_bin_sizes() const
Get the bin sizes.
Definition Histogram.hpp:87
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:51
std::array< T, N > m_ones
std::vector< std::size_t > get_tot_count() const
Get the histogram count data.
Definition Histogram.hpp:79
std::vector< T > get_histogram() const
Get the histogram data.
Definition Histogram.hpp:74
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:84
Histogram(std::array< std::size_t, M > n_bins, std::array< std::pair< U, U >, M > limits)
Histogram constructor.
Definition Histogram.hpp:60
std::array< std::size_t, M > get_n_bins() const
Get the number of bins for each dimension.
Definition Histogram.hpp:71
void update(std::span< const U > pos)
Add data to the histogram.
Definition Histogram.hpp:93