Gemmi C++ API
Loading...
Searching...
No Matches
fourier.hpp
Go to the documentation of this file.
1// Copyright 2019 Global Phasing Ltd.
2//
3// Fourier transform applied to map coefficients.
4
5#ifndef GEMMI_FOURIER_HPP_
6#define GEMMI_FOURIER_HPP_
7
8#include <array>
9#include <complex> // for std::conj
10#include "recgrid.hpp" // for ReciprocalGrid
11#include "math.hpp" // for rad
12#include "symmetry.hpp" // for GroupOps, Op
13#include "fail.hpp" // for fail
14
15#ifdef __MINGW32__ // MinGW may have problem with std::mutex etc
16# define POCKETFFT_CACHE_SIZE 0
17#endif
18#define POCKETFFT_NO_MULTITHREADING
19#include "third_party/pocketfft_hdronly.h"
20
21namespace gemmi {
22
23template<typename T>
24double phase_in_angles(const std::complex<T>& v) {
25 double angle = gemmi::deg(std::arg(v));
26 if (angle < 0)
27 angle += 360.;
28 if (static_cast<T>(angle) == 360.f)
29 angle = 0.;
30 return angle;
31}
32
33// the first arg is usually Mtz::data
34inline void add_asu_f_phi_to_float_vector(std::vector<float>& float_data,
35 const AsuData<std::complex<float>>& asu_data) {
36 float_data.reserve(float_data.size() + asu_data.v.size() * 5);
37 for (const auto& item : asu_data.v) {
38 for (int i = 0; i != 3; ++i)
39 float_data.push_back((float) item.hkl[i]);
40 float_data.push_back(std::abs(item.value));
41 float_data.push_back((float) gemmi::phase_in_angles(item.value));
42 }
43}
44
45template<typename DataProxy>
46std::array<int, 3> get_size_for_hkl(const DataProxy& data,
47 std::array<int, 3> min_size,
48 double sample_rate) {
49 // adjust min_size by checking Miller indices in the data
50 for (size_t i = 0; i < data.size(); i += data.stride()) {
51 Miller hkl = data.get_hkl(i);
52 for (int j = 0; j != 3; ++j) {
53 int v = 2 * std::abs(hkl[j]) + 1;
54 if (v > min_size[j])
55 min_size[j] = v;
56 }
57 }
58 std::array<double, 3> dsize{{(double)min_size[0],
59 (double)min_size[1],
60 (double)min_size[2]}};
61 if (sample_rate > 0) {
62 const UnitCell& cell = data.unit_cell();
63 double max_1_d2 = 0;
64 for (size_t i = 0; i < data.size(); i += data.stride())
65 max_1_d2 = std::max(max_1_d2, cell.calculate_1_d2(data.get_hkl(i)));
66 double inv_d_min = std::sqrt(max_1_d2);
67 std::array<double, 3> cellr = {{cell.ar, cell.br, cell.cr}};
68 for (int i = 0; i < 3; ++i)
69 dsize[i] = std::max(dsize[i], sample_rate * inv_d_min / cellr[i]);
70 }
71 return good_grid_size(dsize, GridSizeRounding::Up, data.spacegroup());
72}
73
74template<typename DataProxy>
75bool data_fits_into(const DataProxy& data, std::array<int, 3> size) {
76 for (size_t i = 0; i < data.size(); i += data.stride()) {
77 Miller hkl = data.get_hkl(i);
78 for (int j = 0; j != 3; ++j)
79 if (2 * std::abs(hkl[j]) >= size[j])
80 return false;
81 }
82 return true;
83}
84
85template<typename T>
87 const T default_val = T(); // initialized to 0 or 0+0i
88 if (grid.axis_order == AxisOrder::XYZ) {
89 for (int w = 0; w != (grid.half_l ? 1 : grid.nw); ++w) {
90 int w_ = w == 0 ? 0 : grid.nw - w;
91 for (int v = 0; v != grid.nv; ++v) {
92 int v_ = v == 0 ? 0 : grid.nv - v;
93 for (int u = 0; u != grid.nu; ++u) {
94 size_t idx = grid.index_q(u, v, w);
95 if (grid.data[idx] == default_val) {
96 int u_ = u == 0 ? 0 : grid.nu - u;
97 size_t inv_idx = grid.index_q(u_, v_, w_);
98 grid.data[idx] = friedel_mate_value(grid.data[inv_idx]);
99 }
100 }
101 }
102 }
103 } else { // grid.axis_order == AxisOrder::ZYX
104 for (int w = 0; w != grid.nw; ++w) {
105 int w_ = w == 0 ? 0 : grid.nw - w;
106 for (int v = 0; v != grid.nv; ++v) {
107 int v_ = v == 0 ? 0 : grid.nv - v;
108 if (grid.half_l) {
109 size_t idx = grid.index_q(0, v, w);
110 if (grid.data[idx] == default_val) {
111 size_t inv_idx = grid.index_q(0, v_, w_);
112 grid.data[idx] = friedel_mate_value(grid.data[inv_idx]);
113 }
114 } else {
115 for (int u = 0; u != grid.nu; ++u) {
116 size_t idx = grid.index_q(u, v, w);
117 if (grid.data[idx] == default_val) {
118 int u_ = u == 0 ? 0 : grid.nu - u;
119 size_t inv_idx = grid.index_q(u_, v_, w_);
120 grid.data[idx] = friedel_mate_value(grid.data[inv_idx]);
121 }
122 }
123 }
124 }
125 }
126 }
127}
128
129template<typename T, typename DataProxy>
130void initialize_hkl_grid(ReciprocalGrid<T>& grid, const DataProxy& data,
131 std::array<int, 3> size, bool half_l,
132 AxisOrder axis_order) {
133 if (data.size() == 0)
134 fail("No data.");
135 if (!data.spacegroup())
136 fail("No spacegroup.");
137 check_grid_factors(data.spacegroup(), size);
138 grid.unit_cell = data.unit_cell();
139 grid.half_l = half_l;
140 grid.axis_order = axis_order;
141 grid.spacegroup = data.spacegroup();
142 if (half_l)
143 size[2] = size[2] / 2 + 1;
144 if (axis_order == AxisOrder::ZYX)
145 std::swap(size[0], size[2]);
146 grid.set_size_without_checking(size[0], size[1], size[2]);
147}
148
149template<typename DataProxy>
150struct FPhiProxy : DataProxy {
151 FPhiProxy(const DataProxy& data_proxy, size_t f_col, size_t phi_col)
152 : DataProxy(data_proxy), f_col_(f_col), phi_col_(phi_col) {
154 fail("Map coefficients not found.");
155 }
156 using real = typename DataProxy::num_type;
157 real get_f(size_t offset) const { return this->get_num(offset + f_col_); }
158 double get_phi(size_t offset) const { return rad(this->get_num(offset + phi_col_)); }
159private:
160 size_t f_col_, phi_col_;
161};
162
163// If half_l is true, grid has only data with l>=0.
164// Parameter size can be obtained from get_size_for_hkl().
165template<typename T, typename FPhi>
167 std::array<int, 3> size, bool half_l,
168 AxisOrder axis_order=AxisOrder::XYZ) {
169 FPhiGrid<T> grid;
170 initialize_hkl_grid(grid, fphi, size, half_l, axis_order);
171 const std::complex<T> default_val; // initialized to 0+0i
172 GroupOps ops = grid.spacegroup->operations();
173 for (size_t i = 0; i < fphi.size(); i += fphi.stride()) {
174 Miller hkl = fphi.get_hkl(i);
175 T f = (T) fphi.get_f(i);
176 if (f == 0.f) // is there enough of F=0 to justify this 'if'?
177 continue;
178 double phi = fphi.get_phi(i);
179 for (const Op& op : ops.sym_ops) {
180 auto hklp = op.apply_to_hkl(hkl);
181 int lp = hklp[2];
182 if (axis_order == AxisOrder::ZYX)
183 std::swap(hklp[0], hklp[2]);
184 if (!grid.has_index(hklp[0], hklp[1], hklp[2]))
185 continue;
186 int sign = (!half_l || lp >= 0 ? 1 : -1);
187 size_t idx = grid.index_n(sign * hklp[0], sign * hklp[1], sign * hklp[2]);
188 if (grid.data[idx] == default_val) {
189 T theta = sign * T(phi + op.phase_shift(hkl));
190 grid.data[idx] = std::complex<T>(f * std::cos(theta), f * std::sin(theta));
191 }
192 }
193 }
194 if (!ops.is_centrosymmetric())
195 add_friedel_mates(grid);
196 return grid;
197}
198
199template<typename T, typename DataProxy>
200ReciprocalGrid<T> get_value_on_grid(const DataProxy& data, size_t column,
201 std::array<int, 3> size, bool half_l,
202 AxisOrder axis_order=AxisOrder::XYZ) {
204 initialize_hkl_grid(grid, data, size, half_l, axis_order);
205
206 if (column >= data.stride())
207 fail("Map coefficients not found.");
208 GroupOps ops = grid.spacegroup->operations();
209 for (size_t i = 0; i < data.size(); i += data.stride()) {
210 Miller hkl = data.get_hkl(i);
211 T val = (T) data.get_num(i + column);
212 if (val != 0.) {
213 for (const Op& op : ops.sym_ops) {
214 auto hklp = op.apply_to_hkl(hkl);
215 int lp = hklp[2];
216 if (axis_order == AxisOrder::ZYX)
217 std::swap(hklp[0], hklp[2]);
218 if (!grid.has_index(hklp[0], hklp[1], hklp[2]))
219 continue;
220 int sign = (!half_l || lp >= 0 ? 1 : -1);
221 size_t idx = grid.index_n(sign * hklp[0], sign * hklp[1], sign * hklp[2]);
222 if (grid.data[idx] == 0.) // 0 is the default value
223 grid.data[idx] = val;
224 }
225 }
226 }
227 if (!ops.is_centrosymmetric())
228 add_friedel_mates(grid);
229 return grid;
230}
231
232
233template<typename T>
234void transform_f_phi_grid_to_map_(FPhiGrid<T>&& hkl, Grid<T>& map) {
235 // NaNs are not good for FFT, so we change them to 0.
236 // x -> conj(x) is equivalent to changing axis direction before FFT.
237 for (std::complex<T>& x : hkl.data)
238 if (std::isnan(x.imag()))
239 x = 0;
240 else
241 x.imag(-x.imag());
242 map.spacegroup = hkl.spacegroup;
243 map.unit_cell = hkl.unit_cell;
244 map.axis_order = hkl.axis_order;
245 if (hkl.axis_order == AxisOrder::XYZ) {
246 int nw = hkl.half_l ? 2 * (hkl.nw - 1) : hkl.nw;
247 map.set_size(hkl.nu, hkl.nv, nw);
248 } else { // hkl.axis_order == AxisOrder::ZYX
249 int nu = hkl.half_l ? 2 * (hkl.nu - 1) : hkl.nu;
250 check_grid_factors(map.spacegroup, {{hkl.nw, hkl.nv, nu}});
251 map.set_size_without_checking(nu, hkl.nv, hkl.nw);
252 }
253 // FIXME set_size_without_checking is changing axis_order - bad
254 map.axis_order = hkl.axis_order;
255 pocketfft::shape_t shape{(size_t)hkl.nw, (size_t)hkl.nv, (size_t)hkl.nu};
256 std::ptrdiff_t s = sizeof(T);
257 pocketfft::stride_t stride{2*s * hkl.nv * hkl.nu, 2*s * hkl.nu, 2*s};
258 pocketfft::shape_t axes{2, 1, 0};
259 if (hkl.axis_order == AxisOrder::ZYX)
260 std::swap(axes[0], axes[2]);
261 T norm = T(1.0 / hkl.unit_cell.volume);
262 if (hkl.half_l) {
263 size_t last_axis = axes.back();
264 axes.pop_back();
265 pocketfft::c2c<T>(shape, stride, stride, axes, pocketfft::BACKWARD,
266 &hkl.data[0], &hkl.data[0], norm);
267 pocketfft::stride_t stride_out{s * map.nu * map.nv, s * map.nu, s};
268 shape[0] = (size_t) map.nw;
269 shape[2] = (size_t) map.nu;
270 pocketfft::c2r<T>(shape, stride, stride_out, last_axis, pocketfft::BACKWARD,
271 &hkl.data[0], &map.data[0], 1.0f);
272 } else {
273 pocketfft::c2c<T>(shape, stride, stride, axes, pocketfft::BACKWARD,
274 &hkl.data[0], &hkl.data[0], norm);
275 assert(map.data.size() == hkl.data.size());
276 for (size_t i = 0; i != map.data.size(); ++i)
277 map.data[i] = hkl.data[i].real();
278 }
279}
280
281template<typename T>
283 Grid<T> map;
284 transform_f_phi_grid_to_map_(std::forward<FPhiGrid<T>>(hkl), map);
285 return map;
286}
287
288template<typename T, typename FPhi>
290 std::array<int, 3> size,
291 double sample_rate,
292 bool exact_size=false,
293 AxisOrder order=AxisOrder::XYZ) {
294 if (exact_size) {
295 gemmi::check_grid_factors(fphi.spacegroup(), size);
296 } else {
297 size = get_size_for_hkl(fphi, size, sample_rate);
298 }
299 return transform_f_phi_grid_to_map(get_f_phi_on_grid<T>(fphi, size, true, order));
300}
301
302template<typename T, typename FPhi>
304 std::array<int, 3> min_size,
305 double sample_rate,
306 std::array<int, 3> exact_size,
307 AxisOrder order=AxisOrder::XYZ) {
308 bool exact = (exact_size[0] != 0 || exact_size[1] != 0 || exact_size[2] != 0);
310 sample_rate, exact, order);
311}
312
313template<typename T>
314FPhiGrid<T> transform_map_to_f_phi(const Grid<T>& map, bool half_l, bool use_scale=true) {
315 if (half_l && map.axis_order == AxisOrder::ZYX)
316 fail("transform_map_to_f_phi(): half_l + ZYX order are not supported yet");
317 FPhiGrid<T> hkl;
318 hkl.unit_cell = map.unit_cell;
319 hkl.spacegroup = map.spacegroup;
320 hkl.axis_order = map.axis_order;
321 hkl.half_l = half_l;
322 int half_nw = map.nw / 2 + 1;
323 hkl.set_size_without_checking(map.nu, map.nv, half_l ? half_nw : map.nw);
324 T norm = use_scale ? T(map.unit_cell.volume / map.point_count()) : 1;
325 pocketfft::shape_t shape{(size_t)map.nw, (size_t)map.nv, (size_t)map.nu};
326 std::ptrdiff_t s = sizeof(T);
327 pocketfft::stride_t stride_in{s * hkl.nv * hkl.nu, s * hkl.nu, s};
328 pocketfft::stride_t stride{2*s * hkl.nv * hkl.nu, 2*s * hkl.nu, 2*s};
329 pocketfft::r2c<T>(shape, stride_in, stride, /*axis=*/0, pocketfft::FORWARD,
330 &map.data[0], &hkl.data[0], norm);
331 shape[0] = half_nw;
332 pocketfft::c2c<T>(shape, stride, stride, {1, 2}, pocketfft::FORWARD,
333 &hkl.data[0], &hkl.data[0], 1.0f);
334 if (!half_l) // add Friedel pairs
335 for (int w = half_nw; w != hkl.nw; ++w) {
336 int w_ = hkl.nw - w;
337 for (int v = 0; v != hkl.nv; ++v) {
338 int v_ = v == 0 ? 0 : hkl.nv - v;
339 for (int u = 0; u != hkl.nu; ++u) {
340 int u_ = u == 0 ? 0 : hkl.nu - u;
341 size_t idx = hkl.index_q(u, v, w);
342 size_t inv_idx = hkl.index_q(u_, v_, w_);
343 hkl.data[idx] = hkl.data[inv_idx]; // conj() is called later
344 }
345 }
346 }
347 for (int i = 0; i != hkl.nu * hkl.nv * half_nw; ++i)
348 hkl.data[i].imag(-hkl.data[i].imag());
349 return hkl;
350}
351
352} // namespace gemmi
353#endif
AxisOrder
Order of grid axis.
Definition grid.hpp:25
double phase_in_angles(const std::complex< T > &v)
Definition fourier.hpp:24
MtzDataProxy data_proxy(const Mtz &mtz)
Definition mtz.hpp:1096
signed char friedel_mate_value(signed char v)
Definition recgrid.hpp:13
constexpr double deg(double angle)
Definition math.hpp:31
FPhiGrid< T > get_f_phi_on_grid(const FPhi &fphi, std::array< int, 3 > size, bool half_l, AxisOrder axis_order=AxisOrder::XYZ)
Definition fourier.hpp:166
void add_friedel_mates(ReciprocalGrid< T > &grid)
Definition fourier.hpp:86
std::array< int, 3 > Miller
A synonym for convenient passing of hkl.
Definition unitcell.hpp:128
void add_asu_f_phi_to_float_vector(std::vector< float > &float_data, const AsuData< std::complex< float > > &asu_data)
Definition fourier.hpp:34
constexpr double rad(double angle)
Definition math.hpp:32
Grid< T > transform_f_phi_to_map2(const FPhi &fphi, std::array< int, 3 > min_size, double sample_rate, std::array< int, 3 > exact_size, AxisOrder order=AxisOrder::XYZ)
Definition fourier.hpp:303
FPhiGrid< T > transform_map_to_f_phi(const Grid< T > &map, bool half_l, bool use_scale=true)
Definition fourier.hpp:314
std::array< int, 3 > good_grid_size(const std::array< double, 3 > &limit, GridSizeRounding rounding, const SpaceGroup *sg)
Definition grid.hpp:79
void check_grid_factors(const SpaceGroup *sg, std::array< int, 3 > size)
Definition grid.hpp:121
Grid< T > transform_f_phi_grid_to_map(FPhiGrid< T > &&hkl)
Definition fourier.hpp:282
void initialize_hkl_grid(ReciprocalGrid< T > &grid, const DataProxy &data, std::array< int, 3 > size, bool half_l, AxisOrder axis_order)
Definition fourier.hpp:130
void fail(const std::string &msg)
Definition fail.hpp:59
std::array< int, 3 > get_size_for_hkl(const DataProxy &data, std::array< int, 3 > min_size, double sample_rate)
Definition fourier.hpp:46
Grid< T > transform_f_phi_to_map(const FPhi &fphi, std::array< int, 3 > size, double sample_rate, bool exact_size=false, AxisOrder order=AxisOrder::XYZ)
Definition fourier.hpp:289
bool data_fits_into(const DataProxy &data, std::array< int, 3 > size)
Definition fourier.hpp:75
ReciprocalGrid< T > get_value_on_grid(const DataProxy &data, size_t column, std::array< int, 3 > size, bool half_l, AxisOrder axis_order=AxisOrder::XYZ)
Definition fourier.hpp:200
Definition seqid.hpp:149
FPhiProxy(const DataProxy &data_proxy, size_t f_col, size_t phi_col)
Definition fourier.hpp:151
double get_phi(size_t offset) const
Definition fourier.hpp:158
typename DataProxy::num_type real
Definition fourier.hpp:156
real get_f(size_t offset) const
Definition fourier.hpp:157
size_t stride() const
Definition mtz.hpp:1067
Unit cell.
Definition unitcell.hpp:139
double ar
reciprocal parameters a*, b*, c*, alpha*, beta*, gamma*
Definition unitcell.hpp:154
double calculate_1_d2(const Miller &hkl) const
Definition unitcell.hpp:529