Loading...
Searching...
No Matches
simd_distance.hpp
Go to the documentation of this file.
1#ifndef SEAMS_SIMD_DISTANCE_H_
2#define SEAMS_SIMD_DISTANCE_H_
3
4#include <cmath>
5#include <algorithm>
6#include <cstddef>
7#if defined(__has_include)
8#if __has_include(<span>)
9#include <span>
10#define SEAMS_HAS_STD_SPAN 1
11#endif
12#endif
13
14#ifdef SEAMS_HAS_MINIMAGE
15#include <minimage.h>
16#endif
17
18#ifdef SEAMS_HAS_HWY
19
20#include "hwy/highway.h"
21
22namespace seams {
23
24namespace hn = hwy::HWY_NAMESPACE;
25
26// Compute squared periodic distances for a batch of atom pairs.
27// Orthorhombic lengths only: one independent wrap per axis.
28// A LAMMPS dump with tilt (PointCloud.box.size() >= 6) stores bound
29// spans in box[0..2], not lx, ly, lz. Call gen::periodicDistSq or
30// gen::batchPeriodicDistSq for those clouds.
31// dx, dy, dz: coordinate differences (length n)
32// bx, by, bz: periodic box dimensions
33// out: output squared distances (length n)
34inline HWY_ATTR void BatchPeriodicDistSq(const double* HWY_RESTRICT dx,
35 const double* HWY_RESTRICT dy,
36 const double* HWY_RESTRICT dz,
37 double bx, double by, double bz,
38 double* HWY_RESTRICT out, size_t n) {
39#ifdef SEAMS_HAS_MINIMAGE
40 mi_dist2_ortho_diffs(dx, dy, dz, bx, by, bz, out, n);
41 return;
42#endif
43 const hn::ScalableTag<double> d;
44 const size_t N = hn::Lanes(d);
45
46 const auto vbx = hn::Set(d, bx);
47 const auto vby = hn::Set(d, by);
48 const auto vbz = hn::Set(d, bz);
49
50 // The box is fixed across the batch, so the three divisions of the minimum
51 // image convention collapse into one reciprocal each, hoisted out of the
52 // loop. Vector division has several times the latency of multiplication.
53 const double rbx = 1.0 / bx;
54 const double rby = 1.0 / by;
55 const double rbz = 1.0 / bz;
56 const auto vrbx = hn::Set(d, rbx);
57 const auto vrby = hn::Set(d, rby);
58 const auto vrbz = hn::Set(d, rbz);
59
60 size_t i = 0;
61 for (; i + N <= n; i += N) {
62 // Callers pass std::vector<double> scratch (16-byte typical). Load/Store
63 // require native vector alignment and fault under AVX2/AVX-512.
64 auto vdx = hn::LoadU(d, dx + i);
65 auto vdy = hn::LoadU(d, dy + i);
66 auto vdz = hn::LoadU(d, dz + i);
67
68 // Absolute values
69 vdx = hn::Abs(vdx);
70 vdy = hn::Abs(vdy);
71 vdz = hn::Abs(vdz);
72
73 // Periodic wrap: dr -= box * round(dr * (1 / box))
74 vdx = hn::NegMulAdd(vbx, hn::Round(hn::Mul(vdx, vrbx)), vdx);
75 vdy = hn::NegMulAdd(vby, hn::Round(hn::Mul(vdy, vrby)), vdy);
76 vdz = hn::NegMulAdd(vbz, hn::Round(hn::Mul(vdz, vrbz)), vdz);
77
78 // r2 = dx*dx + dy*dy + dz*dz
79 auto r2 = hn::MulAdd(vdx, vdx, hn::MulAdd(vdy, vdy, hn::Mul(vdz, vdz)));
80 hn::StoreU(r2, d, out + i);
81 }
82
83 // Scalar remainder
84 for (; i < n; i++) {
85 double ddx = std::fabs(dx[i]);
86 double ddy = std::fabs(dy[i]);
87 double ddz = std::fabs(dz[i]);
88 ddx -= bx * std::round(ddx * rbx);
89 ddy -= by * std::round(ddy * rby);
90 ddz -= bz * std::round(ddz * rbz);
91 out[i] = ddx * ddx + ddy * ddy + ddz * ddz;
92 }
93}
94
95} // namespace seams
96
97#else // !SEAMS_HAS_HWY
98
99// Scalar fallback
100namespace seams {
101inline void BatchPeriodicDistSq(const double* dx, const double* dy,
102 const double* dz, double bx, double by,
103 double bz, double* out, size_t n) {
104#ifdef SEAMS_HAS_MINIMAGE
105 mi_dist2_ortho_diffs(dx, dy, dz, bx, by, bz, out, n);
106 return;
107#endif
108 // Matches the vectorised path: one reciprocal per axis for the whole batch
109 const double rbx = 1.0 / bx;
110 const double rby = 1.0 / by;
111 const double rbz = 1.0 / bz;
112 for (size_t i = 0; i < n; i++) {
113 double ddx = std::fabs(dx[i]);
114 double ddy = std::fabs(dy[i]);
115 double ddz = std::fabs(dz[i]);
116 ddx -= bx * std::round(ddx * rbx);
117 ddy -= by * std::round(ddy * rby);
118 ddz -= bz * std::round(ddz * rbz);
119 out[i] = ddx * ddx + ddy * ddy + ddz * ddz;
120 }
121}
122} // namespace seams
123
124#endif // SEAMS_HAS_HWY
125
126namespace seams {
127
138#ifdef SEAMS_HAS_STD_SPAN
139inline void BatchPeriodicDistSq(std::span<const double> dx,
140 std::span<const double> dy,
141 std::span<const double> dz, double bx,
142 double by, double bz, std::span<double> out) {
143 const size_t n = std::min({dx.size(), dy.size(), dz.size(), out.size()});
144 BatchPeriodicDistSq(dx.data(), dy.data(), dz.data(), bx, by, bz, out.data(),
145 n);
146}
147#endif
148
149} // namespace seams
150
151#endif // SEAMS_SIMD_DISTANCE_H_
void BatchPeriodicDistSq(const double *dx, const double *dy, const double *dz, double bx, double by, double bz, double *out, size_t n)