ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
Vector.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2014-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/**
23 * @file
24 *
25 * @brief Vector implementation and trait types
26 * for boost qvm interoperability.
27 */
28
29#include <boost/qvm/deduce_vec.hpp>
30#include <boost/qvm/vec_traits.hpp>
31
32#include "utils/Array.hpp"
33#include "utils/attributes.hpp"
35
36#include <algorithm>
37#include <cassert>
38#include <cmath>
39#include <concepts>
40#include <cstddef>
41#include <functional>
42#include <initializer_list>
43#include <iterator>
44#include <numeric>
45#include <ranges>
46#include <span>
47#include <stdexcept>
48#include <tuple>
49#include <type_traits>
50#include <utility>
51#include <vector>
52
53namespace Utils {
54
55template <typename T, std::size_t N> class Vector : public Array<T, N> {
56 using Base = Array<T, N>;
57
58public:
59 using Base::at;
60 using Base::Base;
61 using Base::operator[];
62 using Base::back;
63 using Base::begin;
64 using Base::cbegin;
65 using Base::cend;
66 using Base::data;
67 using Base::empty;
68 using Base::end;
69 using Base::fill;
70 using Base::front;
71 using Base::max_size;
72 using Base::size;
73
74 template <class U> struct is_vector : std::false_type {};
75 template <class U, std::size_t Np>
76 struct is_vector<Vector<U, Np>> : std::true_type {};
77
81
82 void swap(Vector &rhs) { std::ranges::swap_ranges(*this, rhs); }
83
84private:
85 constexpr void copy_init(T const *values) noexcept {
86 for (std::size_t i{0}; i != N; ++i) {
87 (*this)[i] = values[i];
88 }
89 }
90
91public:
92 // range-based ctor that excludes Vector<T,N> to avoid ambiguous calls with
93 // the copy ctor, move ctor and cast operator; std::ranges::input_range is
94 // not used due to conflicts with move assignment in recursive variant types
95 // and T[N] must be excluded to avoid shadowing the noexcept ctor
96 template <class Range>
98 not std::is_same_v<std::remove_cvref_t<Range>, T[N]>)
99 explicit constexpr Vector(Range &&rng)
100 : Vector(std::begin(rng), std::end(rng)) {}
101
102#if __cpp_lib_containers_ranges
103 template <std::ranges::input_range Range>
104 Vector(std::from_range_t, Range &&rng)
105 : Vector(std::begin(rng), std::end(rng)) {}
106#endif
107
108 explicit constexpr Vector(T const (&v)[N]) noexcept : Base() {
109 if constexpr (N != 0) {
110 copy_init(std::cbegin(v));
111 }
112 }
113
114 explicit constexpr Vector(Array<T, N> const &array) noexcept : Base(array) {}
115
116 constexpr Vector(std::initializer_list<T> v) : Base() {
117 if (N != v.size()) {
118 throw std::length_error(
119 "Construction of Vector from Container of wrong length.");
120 }
121 if constexpr (N != 0) {
122 copy_init(v.begin());
123 }
124 }
125
126 template <typename InputIterator>
128 if (std::distance(first, last) == N) {
129 std::copy_n(first, N, begin());
130 } else {
131 throw std::length_error(
132 "Construction of Vector from Container of wrong length.");
133 }
134 }
135
136 /** @brief Create a vector that has all entries set to the same value. */
137 DEVICE_QUALIFIER static constexpr Vector<T, N>
138 broadcast(typename Base::value_type const &value) noexcept {
140 for (std::size_t i = 0u; i != N; ++i) {
141 ret[i] = value;
142 }
143 return ret;
144 }
145
146 std::vector<T> as_vector() const { return std::vector<T>(begin(), end()); }
147
148 operator std::vector<T>() const { return as_vector(); }
149
150 constexpr std::span<const T, N> as_span() const noexcept {
151 return std::span<const T, N>(begin(), N);
152 }
153
154 constexpr operator std::span<const T, N>() const noexcept {
155 return as_span();
156 }
157
158 template <class U> explicit operator Vector<U, N>() const {
160
161 std::ranges::transform(*this, ret.begin(),
162 [](T const &e) { return static_cast<U>(e); });
163
164 return ret;
165 }
166
167 constexpr T norm2() const { return (*this) * (*this); }
168 T norm() const { return std::sqrt(norm2()); }
169
170 /*
171 * @brief Normalize the vector.
172 *
173 * Normalize the vector by its length,
174 * if not zero, otherwise the vector is unchanged.
175 */
177 auto const l = norm();
178 if (l != T(0)) {
179 *this /= l;
180 }
181
182 return *this;
183 }
184
185 /*
186 * @brief Return a normalized copy of the vector.
187 *
188 * Normalize the vector by its length,
189 * if not zero, otherwise the vector is unchanged.
190 */
192 auto const l = norm();
193 return (l != T(0)) ? (*this) / l : *this;
194 }
195};
196
197template <class T> using Vector3 = Vector<T, 3>;
198
199template <std::size_t N> using VectorXd = Vector<double, N>;
205
206template <std::size_t N> using VectorXf = Vector<float, N>;
208
209template <std::size_t N> using VectorXi = Vector<int, N>;
211
212namespace detail {
213template <std::size_t N, typename T, typename U, typename Op>
214auto binary_op(Vector<T, N> const &a, Vector<U, N> const &b, Op op) {
215 // we must use the non-range version std::transform for Vector<T, N> because:
216 // GCC 12 cannot inspect -Wmaybe-uninitialized through std::ranges::transform
217 // Clang/Xcode libc++ cannot select the binary form of std::ranges::transform
218 using R = decltype(std::declval<T>() + std::declval<U>());
220
221 // NOLINTNEXTLINE(modernize-use-ranges)
222 std::transform(std::begin(a), std::end(a), std::begin(b), std::begin(ret),
223 op);
224
225 return ret;
226}
227} // namespace detail
228
229#define ESPRESSO_VECTOR_COMPARISON(op) \
230 template <std::size_t N, typename T> \
231 constexpr bool \
232 operator op(Vector<T, N> const &a, Vector<T, N> const &b) noexcept( \
233 noexcept(std::declval<T const &>() op std::declval<T const &>())) { \
234 for (std::size_t i = 0u; i < N; ++i) { \
235 if (not(a[i] op b[i])) { \
236 return false; \
237 } \
238 } \
239 return true; \
240 }
241// synthesize all operators, except a!=b which C++20 rewrites as !(a==b)
247#undef ESPRESSO_VECTOR_COMPARISON
248
249template <std::size_t N, typename T, typename U>
250auto operator+(Vector<T, N> const &a, Vector<U, N> const &b) {
251 return detail::binary_op(a, b, std::plus<>());
252}
253
254template <std::size_t N, typename T>
256 Vector<T, N> const &b) {
257 std::ranges::transform(a, b, std::begin(a), std::plus<T>());
258 return a;
259}
260
261template <std::size_t N, typename T, typename U>
262auto operator-(Vector<T, N> const &a, Vector<U, N> const &b) {
263 return detail::binary_op(a, b, std::minus<>());
264}
265
266template <std::size_t N, typename T>
269 std::ranges::transform(a, std::begin(ret), std::negate<T>());
270 return ret;
271}
272
273template <std::size_t N, typename T>
276 std::ranges::transform(a, b, std::begin(a), std::minus<T>());
277 return a;
278}
279
280/* Scalar multiplication */
281template <std::size_t N, typename T, class U>
282 requires(std::is_arithmetic_v<U>)
283constexpr auto operator*(U const &a, Vector<T, N> const &b) {
284 using R = decltype(a * std::declval<T>());
286 std::ranges::transform(b, std::begin(ret), [a](T const &v) { return a * v; });
287 return ret;
288}
289
290template <std::size_t N, typename T, class U>
291 requires(std::is_arithmetic_v<U>)
292constexpr auto operator*(Vector<T, N> const &a, U const &b) {
293 using R = decltype(std::declval<T>() * b);
295 std::ranges::transform(a, std::begin(ret), [b](T const &v) { return b * v; });
296 return ret;
297}
298
299template <std::size_t N, typename T>
300auto &operator*=(Vector<T, N> &b, T const &a) {
301 std::ranges::transform(b, std::begin(b), [a](T const &v) { return a * v; });
302 return b;
303}
304
305/* Scalar division */
306template <std::size_t N, typename T, class U>
307auto operator/(Vector<T, N> const &a, U const &b) {
308 using R = decltype(std::declval<T>() / b);
310 std::ranges::transform(a, std::begin(ret), [b](T const &v) { return v / b; });
311 return ret;
312}
313
314template <std::size_t N, typename T, class U>
315auto operator/(U const &a, Vector<T, N> const &b) {
316 using R = decltype(a / std::declval<T>());
318 std::ranges::transform(b, std::begin(ret), [a](T const &v) { return a / v; });
319 return ret;
320}
321
322template <std::size_t N, typename T>
323auto &operator/=(Vector<T, N> &a, T const &b) {
324 std::ranges::transform(a, std::begin(a), [b](T const &v) { return v / b; });
325 return a;
326}
327
328namespace detail {
329template <class T> using is_vector = Vector<int, 1>::is_vector<T>;
330} // namespace detail
331
332/* Scalar product */
333template <std::size_t N, typename T, class U>
334 requires(not(detail::is_vector<T>::value or detail::is_vector<U>::value))
335auto constexpr operator*(Vector<T, N> const &a, Vector<U, N> const &b) {
336 using R = decltype(std::declval<T>() * std::declval<U>());
337 // std::inner_product isn't always inlined on Intel CPUs even with -O3,
338 // but a for loop can be inlined
339 R acc{};
340 for (std::size_t i = 0u; i < N; ++i) {
341 acc += a[i] * b[i];
342 }
343 return acc;
344}
345
346template <std::size_t N, typename T, class U>
347 requires(std::is_integral_v<T> and std::is_integral_v<U>)
348auto operator%(Vector<T, N> const &a, Vector<U, N> const &b) {
349 using R = decltype(std::declval<T>() % std::declval<U>());
351 std::ranges::transform(a, b, std::begin(ret), std::modulus<>());
352 return ret;
353}
354
355/* Componentwise square root */
356template <std::size_t N, typename T> auto sqrt(Vector<T, N> const &a) {
357 using std::sqrt;
358 using R = decltype(sqrt(std::declval<T>()));
360 std::ranges::transform(a, ret.begin(), [](T const &v) { return sqrt(v); });
361 return ret;
362}
363
364template <class T>
366 // use noexcept constructor to elide the exception logic
367 T v[3] = {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2],
368 a[0] * b[1] - a[1] * b[0]};
369 return Vector<T, 3>(v);
370}
371
372// Product of array elements.
373template <class T, std::size_t N> T product(Vector<T, N> const &v) {
374 return std::accumulate(v.cbegin(), v.cend(), T{1}, std::multiplies<T>());
375}
376
377template <class T, class U, std::size_t N>
379 using R = decltype(std::declval<T>() * std::declval<U>());
380 auto constexpr proj = std::identity{}; // required by Clang/Xcode libc++
382 std::ranges::transform(a, b, ret.begin(), std::multiplies<>(), proj, proj);
383 return ret;
384}
385
386// specialization for when one or both operands is a scalar depending on
387// compile time features (e.g. when PARTICLE_ANISOTROPY is not enabled)
388template <typename T, typename U>
389 requires(not(detail::is_vector<T>::value and detail::is_vector<U>::value))
390auto hadamard_product(T const &a, U const &b) {
391 return a * b;
392}
393
394template <class T, class U, std::size_t N>
396 using R = decltype(std::declval<T>() / std::declval<U>());
397 auto constexpr proj = std::identity{}; // required by Clang/Xcode libc++
399 std::ranges::transform(a, b, std::begin(ret), std::divides<>(), proj, proj);
400 return ret;
401}
402
403// specialization for when one or both operands is a scalar depending on
404// compile time features (e.g. when PARTICLE_ANISOTROPY is not enabled)
405template <typename T, typename U>
406 requires(not(detail::is_vector<T>::value and detail::is_vector<U>::value))
407auto hadamard_division(T const &a, U const &b) {
408 return a / b;
409}
410
411template <typename T> Vector<T, 3> unit_vector(unsigned int i) {
412 if (i == 0u)
413 return {T{1}, T{0}, T{0}};
414 if (i == 1u)
415 return {T{0}, T{1}, T{0}};
416 if (i == 2u)
417 return {T{0}, T{0}, T{1}};
418 throw std::domain_error("coordinate out of range");
419}
420
421/**
422 * @brief Meta function to turn a Vector<T, 1> into T.
423 */
424template <typename T> struct decay_to_scalar {};
425template <typename T, std::size_t N> struct decay_to_scalar<Vector<T, N>> {
427};
428
429template <typename T> struct decay_to_scalar<Vector<T, 1>> {
430 using type = T;
431};
432
433template <std::size_t I, class T, std::size_t N>
434T &get(Vector<T, N> &a) noexcept {
435 return a[I];
436}
437
438template <std::size_t I, class T, std::size_t N>
439T const &get(Vector<T, N> const &a) noexcept {
440 return a[I];
441}
442
443} // namespace Utils
444
445template <std::size_t I, class T, std::size_t N>
446struct std::tuple_element<I, Utils::Vector<T, N>> {
447 static_assert(I < N, "Utils::Vector index must be in range");
448 using type = T;
449};
450
451template <class T, std::size_t N>
452struct std::tuple_size<Utils::Vector<T, N>>
453 : std::integral_constant<std::size_t, N> {};
454
455namespace boost::qvm {
456
457template <class T, std::size_t N> struct vec_traits<::Utils::Vector<T, N>> {
458
459 static constexpr std::size_t dim = N;
460 using scalar_type = T;
461
462 template <std::size_t I>
463 static constexpr inline scalar_type &write_element(::Utils::Vector<T, N> &v) {
464 return v[I];
465 }
466
467 template <std::size_t I>
468 static constexpr inline scalar_type
470 return v[I];
471 }
472
473 static inline scalar_type read_element_idx(std::size_t i,
474 ::Utils::Vector<T, N> const &v) {
475 return v[i];
476 }
477 static inline scalar_type &write_element_idx(std::size_t i,
479 return v[i];
480 }
481};
482
483template <typename T> struct deduce_vec<Utils::Vector<T, 3>, 3> {
485};
486
487} // namespace boost::qvm
488
Array implementation with CUDA support.
#define ESPRESSO_VECTOR_COMPARISON(op)
Definition Vector.hpp:229
#define UTILS_ARRAY_BOOST_MPI_T(Container, N)
Mark array types as MPI data types.
Definition array.hpp:50
#define UTILS_ARRAY_BOOST_BIT_S(Container, N)
Mark array types as MPI bitwise serializable.
Definition array.hpp:63
#define UTILS_ARRAY_BOOST_CLASS(Container, N, ImplementationLevel)
Redefinition of BOOST_CLASS_IMPLEMENTATION for array types.
Definition array.hpp:78
#define UTILS_ARRAY_BOOST_TRACK(Container, N, TrackingLevel)
Redefinition of BOOST_CLASS_TRACKING for array types.
Definition array.hpp:97
Compiler-attribute macros shared across ESPResSo headers.
#define ESPRESSO_ATTR_ALWAYS_INLINE
Vector() noexcept=default
Vector & normalize()
Definition Vector.hpp:176
T norm() const
Definition Vector.hpp:168
constexpr Vector(T const (&v)[N]) noexcept
Definition Vector.hpp:108
void swap(Vector &rhs)
Definition Vector.hpp:82
DEVICE_QUALIFIER constexpr iterator begin() noexcept
Definition Array.hpp:141
Vector normalized() const
Definition Vector.hpp:191
DEVICE_QUALIFIER constexpr const_iterator cbegin() const noexcept
Definition Array.hpp:149
std::vector< T > as_vector() const
Definition Vector.hpp:146
Vector(InputIterator first, InputIterator last)
Definition Vector.hpp:127
constexpr Vector(std::initializer_list< T > v)
Definition Vector.hpp:116
DEVICE_QUALIFIER constexpr const_iterator cend() const noexcept
Definition Array.hpp:161
constexpr std::span< const T, N > as_span() const noexcept
Definition Vector.hpp:150
DEVICE_QUALIFIER constexpr iterator end() noexcept
Definition Array.hpp:153
static DEVICE_QUALIFIER constexpr Vector< T, N > broadcast(typename Base::value_type const &value) noexcept
Create a vector that has all entries set to the same value.
Definition Vector.hpp:138
constexpr Vector(Range &&rng)
Definition Vector.hpp:99
constexpr T norm2() const
Definition Vector.hpp:167
constexpr Vector(Array< T, N > const &array) noexcept
Definition Vector.hpp:114
cudaStream_t stream[1]
CUDA streams for parallel computing on CPU and GPU.
#define DEVICE_QUALIFIER
auto operator+(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:250
Vector< T, 3 > unit_vector(unsigned int i)
Definition Vector.hpp:411
T product(Vector< T, N > const &v)
Definition Vector.hpp:373
Vector< T, 3 > vector_product(Vector< T, 3 > const &a, Vector< T, 3 > const &b)
Definition Vector.hpp:365
T & get(Array< T, N > &a) noexcept
Definition Array.hpp:210
auto operator/(Vector< T, N > const &a, U const &b)
Definition Vector.hpp:307
ESPRESSO_ATTR_ALWAYS_INLINE auto & operator+=(Vector< T, N > &a, Vector< T, N > const &b)
Definition Vector.hpp:255
ESPRESSO_ATTR_ALWAYS_INLINE Vector< T, N > & operator-=(Vector< T, N > &a, Vector< T, N > const &b)
Definition Vector.hpp:275
auto hadamard_division(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:395
auto & operator*=(Vector< T, N > &b, T const &a)
Definition Vector.hpp:300
auto hadamard_product(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:378
auto operator-(Vector< T, N > const &a, Vector< U, N > const &b)
Definition Vector.hpp:262
auto sqrt(Vector< T, N > const &a)
Definition Vector.hpp:356
auto & operator/=(Vector< T, N > &a, T const &b)
Definition Vector.hpp:323
STL namespace.
DEVICE_QUALIFIER constexpr reference at(size_type i)
Definition Array.hpp:98
DEVICE_QUALIFIER constexpr bool empty() const noexcept
Definition Array.hpp:165
DEVICE_QUALIFIER constexpr reference back()
Definition Array.hpp:127
DEVICE_QUALIFIER constexpr pointer data() noexcept
Definition Array.hpp:133
DEVICE_QUALIFIER constexpr size_type max_size() const noexcept
Definition Array.hpp:169
DEVICE_QUALIFIER constexpr iterator begin() noexcept
Definition Array.hpp:141
DEVICE_QUALIFIER constexpr const_iterator cbegin() const noexcept
Definition Array.hpp:149
DEVICE_QUALIFIER constexpr const_iterator cend() const noexcept
Definition Array.hpp:161
DEVICE_QUALIFIER constexpr size_type size() const noexcept
Definition Array.hpp:167
DEVICE_QUALIFIER constexpr reference front()
Definition Array.hpp:123
DEVICE_QUALIFIER constexpr iterator end() noexcept
Definition Array.hpp:153
DEVICE_QUALIFIER void fill(const value_type &value)
Definition Array.hpp:171
Meta function to turn a Vector<T, 1> into T.
Definition Vector.hpp:424
static constexpr scalar_type read_element(::Utils::Vector< T, N > const &v)
Definition Vector.hpp:469
static scalar_type & write_element_idx(std::size_t i, ::Utils::Vector< T, N > &v)
Definition Vector.hpp:477
static constexpr scalar_type & write_element(::Utils::Vector< T, N > &v)
Definition Vector.hpp:463
static scalar_type read_element_idx(std::size_t i, ::Utils::Vector< T, N > const &v)
Definition Vector.hpp:473