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