Gemmi C++ API
Loading...
Searching...
No Matches
formfact.hpp
Go to the documentation of this file.
1// Copyright 2019 Global Phasing Ltd.
2
3// Calculation of atomic form factors approximated by a sum of Gaussians.
4// Tables with numeric coefficient are in it92.hpp and c4322.hpp.
5
6#ifndef GEMMI_FORMFACT_HPP_
7#define GEMMI_FORMFACT_HPP_
8
9#include <cmath> // for exp, sqrt
10#include <cstring> // for memcpy
11#include <limits> // for numeric_limits
12#include <utility> // for pair
13#include "math.hpp" // for pi()
14
15namespace gemmi {
16
17// NOTE: the argument x must be between -88 and 88.
18// It is based on expapprox() from
19// https://github.com/jhjourdan/SIMD-math-prims/blob/master/simd_math_prims.h
20// Relative error is below 1e-5.
21inline float unsafe_expapprox(float x) {
22 static_assert(std::numeric_limits<float>::is_iec559, "float is not IEEE 754?");
23 //static float zero = 0.f; // non-const to disable optimization
24 float val = 12102203.1615614f * x + 1065353216.f;
25 //val = std::max(val, zero); // check if x < -88.02969
26 std::int32_t vali = static_cast<std::int32_t>(val);
27 std::int32_t xu1 = vali & 0x7F800000;
28 std::int32_t xu2 = (vali & 0x7FFFFF) | 0x3F800000;
29 float a, b;
30 std::memcpy(&a, &xu1, 4);
31 std::memcpy(&b, &xu2, 4);
32 return a * (0.509871020f + b * (0.312146713f + b * (0.166617139f + b *
33 (-2.190619930e-3f + b * 1.3555747234e-2f))));
34}
35
36// precalculated density of an isotropic atom
37template<int N, typename Real>
38struct ExpSum {
39 Real a[N], b[N];
40
42 Real density = 0;
43 for (int i = 0; i < N; ++i)
44 density += a[i] * std::exp(b[i] * r2);
45 return density;
46 }
47
48 std::pair<Real,Real> calculate_with_derivative(Real r) const {
49 Real density = 0;
50 Real derivative = 0;
51 for (int i = 0; i < N; ++i) {
52 Real y = a[i] * std::exp(b[i] * (r * r));
53 density += y;
54 derivative += 2 * b[i] * r * y;
55 }
56 return std::make_pair(density, derivative);
57 }
58};
59
60template<int N>
61struct ExpSum<N, float> {
62 float a[N], b[N];
63 float calculate(float r2) const {
64 float density = 0;
65 float tmp[N];
66 for (int i = 0; i < N; ++i)
67 tmp[i] = std::max(b[i] * r2, -88.f);
68 for (int i = 0; i < N; ++i)
70 return density;
71 }
72
73 std::pair<float,float> calculate_with_derivative(float r) const {
74 float density = 0;
75 float derivative = 0;
76 float tmp[N];
77 for (int i = 0; i < N; ++i)
78 tmp[i] = std::max(b[i] * (r * r), -88.f);
79 for (int i = 0; i < N; ++i) {
80 float y = a[i] * unsafe_expapprox(tmp[i]);
81 density += y;
82 derivative += y * b[i] * (2 * r);
83 }
84 return std::make_pair(density, derivative);
85 }
86};
87
88// precalculated density of an anisotropic atom
89template<int N, typename Real>
93
94 Real calculate(const Vec3& r) const {
95 Real density = 0;
96 for (int i = 0; i < N; ++i)
97 density += a[i] * std::exp(b[i].r_u_r(r));
98 return density;
99 }
100};
101
102template<int N>
104 float a[N];
106
107 float calculate(const Vec3& r_) const {
108 Vec3f r((float)r_.x, (float)r_.y, (float)r_.z);
109 float density = 0;
110 float tmp[N];
111 for (int i = 0; i < N; ++i)
112 tmp[i] = std::max(b[i].r_u_r(r), -88.f);
113 for (int i = 0; i < N; ++i)
115 return density;
116 }
117};
118
119template<typename Real>
120Real pow15(Real x) { return x * std::sqrt(x); }
121
122// Gaussian coefficients with functions to calculate sf and density.
123template<int N, int WithC, typename Real>
126 static const int ncoeffs = N;
127 std::array<Real, 2*N+WithC> coefs;
128 Real a(int n) const { return coefs[n]; }
129 Real b(int n) const { return coefs[N+n]; }
130 Real c() const { return WithC ? coefs[2*N] : 0; }
131
132 void set_coefs(const std::array<Real, 2*N+WithC>& c) { coefs = c; }
133
134 // argument: (sin(theta)/lambda)^2
135 Real calculate_sf(Real stol2) const {
136 Real sf = c();
137 for (int i = 0; i < N; ++i)
138 sf += a(i) * std::exp(-b(i)*stol2);
139 return sf;
140 }
141
143 constexpr Real _4pi = Real(4 * pi());
144 Real r2pi = Real(r2 * pi());
145 Real density = c() * pow15(_4pi / B) * std::exp(-(_4pi / B) * r2pi);
146 for (int i = 0; i < N; ++i) {
147 Real t = _4pi / (b(i)+B);
148 density += a(i) * pow15(t) * std::exp(-t*r2pi);
149 }
150 return density;
151 }
152
153 // note: addend is considered only if WithC (addend is usually dispersion f')
156 constexpr Real _4pi = Real(4 * pi());
157 for (int i = 0; i < N; ++i) {
158 Real t = _4pi / (b(i)+B);
159 prec.a[i] = a(i) * pow15(t);
160 prec.b[i] = -t * Real(pi());
161 }
162 if (WithC) {
163 Real t = _4pi / B;
164 prec.a[N] = (c() + addend) * pow15(t);
165 prec.b[N] = -t * Real(pi());
166 }
167 return prec;
168 }
169
171 constexpr Real pi2 = sq(pi());
172 const SMat33<Real> B = U.scaled(8 * pi2);
173 Real density = c() * pow15(4 * pi()) / std::sqrt(B.determinant()) *
174 std::exp(-4 * pi2 * B.inverse().r_u_r(r));
175 for (int i = 0; i < N; ++i) {
176 SMat33<Real> Bb = B.added_kI(b(i));
177 density += a(i) * pow15(4 * pi()) / std::sqrt(Bb.determinant()) *
178 std::exp(-4 * pi2 * Bb.inverse().r_u_r(r));
179 }
180 return density;
181 }
182
184 Real addend=0) const {
185 constexpr Real m4pi2 = Real(-4 * sq(pi()));
186 constexpr Real pow_4pi_15 = (Real) 44.546623974653663; // pow15(4 * pi())
188 for (int i = 0; i < N; ++i) {
189 SMat33<Real> Bb = B.added_kI(b(i));
190 prec.a[i] = a(i) * pow_4pi_15 / std::sqrt(Bb.determinant());
191 prec.b[i] = Bb.inverse().scaled(m4pi2);
192 }
193 if (WithC) {
194 prec.a[N] = (c() + addend) * pow_4pi_15 / std::sqrt(B.determinant());
195 prec.b[N] = B.inverse().scaled(m4pi2);
196 }
197 return prec;
198 }
199
201 Real addend=0) const {
202 constexpr Real UtoB = 8 * sq(pi());
203 return precalculate_density_aniso_b(U.scaled(UtoB), addend);
204 }
205};
206
207} // namespace gemmi
208#endif
Real pow15(Real x)
Definition formfact.hpp:120
constexpr float sq(float x)
Definition math.hpp:34
Vec3_< double > Vec3
Definition math.hpp:101
float unsafe_expapprox(float x)
Definition formfact.hpp:21
constexpr double pi()
Definition math.hpp:16
float calculate(const Vec3 &r_) const
Definition formfact.hpp:107
Real calculate(const Vec3 &r) const
Definition formfact.hpp:94
SMat33< Real > b[N]
Definition formfact.hpp:92
std::pair< float, float > calculate_with_derivative(float r) const
Definition formfact.hpp:73
float calculate(float r2) const
Definition formfact.hpp:63
Real calculate(Real r2) const
Definition formfact.hpp:41
std::pair< Real, Real > calculate_with_derivative(Real r) const
Definition formfact.hpp:48
void set_coefs(const std::array< Real, 2 *N+WithC > &c)
Definition formfact.hpp:132
static const int ncoeffs
Definition formfact.hpp:126
Real calculate_sf(Real stol2) const
Definition formfact.hpp:135
ExpAnisoSum< N+WithC, Real > precalculate_density_aniso_b(const SMat33< Real > &B, Real addend=0) const
Definition formfact.hpp:183
ExpSum< N+WithC, Real > precalculate_density_iso(Real B, Real addend=0) const
Definition formfact.hpp:154
Real b(int n) const
Definition formfact.hpp:129
ExpAnisoSum< N+WithC, Real > precalculate_density_aniso_u(const SMat33< float > &U, Real addend=0) const
Definition formfact.hpp:200
Real a(int n) const
Definition formfact.hpp:128
Real calculate_density_aniso(const Vec3 &r, const SMat33< float > &U) const
Definition formfact.hpp:170
std::array< Real, 2 *N+WithC > coefs
Definition formfact.hpp:127
Real calculate_density_iso(Real r2, Real B) const
Definition formfact.hpp:142