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