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