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