Loading...
Searching...
No Matches
ForceNorm.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12#include "eon/ForceNorm.h"
13
14#include <algorithm>
15#include <cmath>
16#include <cstddef>
17#include <limits>
18
19#ifdef WITH_HIGHWAY
20
21// foreach_target.h re-includes this file once per SIMD target. The path is
22// relative to the project include root.
23#undef HWY_TARGET_INCLUDE
24#define HWY_TARGET_INCLUDE "client/ForceNorm.cpp"
25#include <hwy/foreach_target.h>
26#include <hwy/highway.h>
27
28HWY_BEFORE_NAMESPACE();
29namespace eonc {
30namespace HWY_NAMESPACE {
31
32namespace hn = hwy::HWY_NAMESPACE;
33
34double MaxFreeAtomForceNorm(const double *HWY_RESTRICT forces,
35 const double *HWY_RESTRICT fixed, long nAtoms) {
36 if (forces == nullptr || nAtoms <= 0) {
37 return 0.0;
38 }
39 const hn::ScalableTag<double> d;
40 const size_t lanes = hn::Lanes(d);
41 const size_t n = static_cast<size_t>(nAtoms);
42 const auto half = hn::Set(d, 0.5);
43 auto vmax = hn::Zero(d);
44
45 size_t i = 0;
46 for (; i + lanes <= n; i += lanes) {
47 auto x = hn::Zero(d);
48 auto y = hn::Zero(d);
49 auto z = hn::Zero(d);
50 hn::LoadInterleaved3(d, forces + 3 * i, x, y, z);
51 // Mul then Add, not MulAdd. A contracted FMA rounds once; the scalar
52 // tail rounds each product and the sum, and both paths must agree.
53 const auto nrm =
54 hn::Sqrt(hn::Add(hn::Add(hn::Mul(x, x), hn::Mul(y, y)), hn::Mul(z, z)));
55 if (fixed != nullptr) {
56 auto fx = hn::Zero(d);
57 auto fy = hn::Zero(d);
58 auto fz = hn::Zero(d);
59 hn::LoadInterleaved3(d, fixed + 3 * i, fx, fy, fz);
60 const auto allFixed = hn::And(
61 hn::Gt(fx, half), hn::And(hn::Gt(fy, half), hn::Gt(fz, half)));
62 const auto free = hn::IfThenElseZero(hn::Not(allFixed), nrm);
63 // hn::Max keeps or drops a NaN depending on the target (maxpd on x86,
64 // vmaxq on NEON), so a NaN is returned before it reaches the max.
65 if (!hn::AllFalse(d, hn::IsNaN(free))) {
66 return std::numeric_limits<double>::quiet_NaN();
67 }
68 vmax = hn::Max(vmax, free);
69 } else {
70 if (!hn::AllFalse(d, hn::IsNaN(nrm))) {
71 return std::numeric_limits<double>::quiet_NaN();
72 }
73 vmax = hn::Max(vmax, nrm);
74 }
75 }
76
77 const double tail = detail::maxFreeAtomForceNormScalar(
78 forces, fixed, static_cast<long>(i), nAtoms);
79 if (std::isnan(tail)) {
80 return tail;
81 }
82 return std::max(hn::ReduceMax(d, vmax), tail);
83}
84
85} // namespace HWY_NAMESPACE
86} // namespace eonc
87HWY_AFTER_NAMESPACE();
88
89#if HWY_ONCE
90namespace eonc {
91
92HWY_EXPORT(MaxFreeAtomForceNorm);
93
94double maxFreeAtomForceNorm(const double *forces, const double *fixed,
95 long nAtoms) {
96 return HWY_DYNAMIC_DISPATCH(MaxFreeAtomForceNorm)(forces, fixed, nAtoms);
97}
98
99} // namespace eonc
100#endif // HWY_ONCE
101
102#else // !WITH_HIGHWAY
103
104namespace eonc {
105
106double maxFreeAtomForceNorm(const double *forces, const double *fixed,
107 long nAtoms) {
108 return detail::maxFreeAtomForceNormScalar(forces, fixed, 0, nAtoms);
109}
110
111} // namespace eonc
112
113#endif // WITH_HIGHWAY
double maxFreeAtomForceNormScalar(const double *forces, const double *fixed, long begin, long nAtoms)
Scalar max of per-atom Euclidean norms on [begin, nAtoms).
Definition ForceNorm.h:25
RAII resource manager for the ARTn C library with global synchronization.
double maxFreeAtomForceNorm(const double *forces, const double *fixed, long nAtoms)
Max Euclidean norm over N x 3 row-major force rows.