Loading [MathJax]/jax/output/HTML-CSS/config.js
ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages Concepts
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