Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
inverse_transform.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * Put a field stored on a G-vector set onto the real-space grid.
5 *
6 * A saved density and a saved wavefunction are both lists of coefficients
7 * against Miller indices; every volumetric format wants samples on a grid. The
8 * transform between them is `f(r) = sum_G f(G) exp(i G·r)`, which is exactly
9 * what an unnormalized inverse FFT computes, so this is a scatter onto the
10 * grid followed by `FFTGrid::inverse_raw()` — and the grid's own
11 * `g_vector_to_fft_grid_index()` decides where each G lands, so the ordering
12 * convention is the engine's rather than a second copy of it here.
13 *
14 * G-vectors that do not fit the grid are dropped rather than wrapped. A
15 * sphere cut at the density cutoff always fits the grid that cutoff sized, so
16 * this only fires for a mismatched pair of inputs, where wrapping would fold a
17 * high-frequency component onto a low one and produce a plausible, wrong
18 * field.
19 */
20
21#include <algorithm>
22#include <array>
23#include <cmath>
24#include <cstddef>
25#include <vector>
26
29
30namespace q3 {
31
32/**
33 * `f(r) = sum_G f(G) exp(i G·r)` on the grid, complex.
34 *
35 * @param holds_half_sphere true for a gamma-only set, which stores only one
36 * of each ±G pair; the missing half is filled by conjugation, which is
37 * what makes the result real for a real field.
38 */
39inline std::vector<Complex>
40complex_field_from_reciprocal_space(const std::array<int, 3>& grid_dimensions,
41 const std::vector<IntegerVector3D>& miller_indices,
42 const std::vector<Complex>& coefficients,
43 bool holds_half_sphere) {
44 SerialFFTGrid grid;
45 grid.init(grid_dimensions[0],
46 grid_dimensions[1],
47 grid_dimensions[2],
48 /*use_measured_planning=*/false);
49
50 std::vector<Complex> work(static_cast<std::size_t>(grid.total_grid_points()),
51 Complex(0.0, 0.0));
52 const std::size_t count = std::min(miller_indices.size(), coefficients.size());
53 for (std::size_t index = 0; index < count; index++) {
54 const IntegerVector3D& miller = miller_indices[index];
55 if (std::abs(miller.x) > grid_dimensions[0] / 2 ||
56 std::abs(miller.y) > grid_dimensions[1] / 2 ||
57 std::abs(miller.z) > grid_dimensions[2] / 2) {
58 continue;
59 }
60 work[grid.g_vector_to_fft_grid_index(miller.x, miller.y, miller.z)] = coefficients[index];
61 const bool is_origin = miller.x == 0 && miller.y == 0 && miller.z == 0;
62 if (holds_half_sphere && !is_origin) {
63 work[grid.g_vector_to_fft_grid_index(-miller.x, -miller.y, -miller.z)] =
64 std::conj(coefficients[index]);
65 }
66 }
67
68 grid.inverse_raw(work.data());
69 return work;
70}
71
72/// The same transform for a field that is real by construction, e.g. a density.
73inline std::vector<Real>
74real_field_from_reciprocal_space(const std::array<int, 3>& grid_dimensions,
75 const std::vector<IntegerVector3D>& miller_indices,
76 const std::vector<Complex>& coefficients,
77 bool holds_half_sphere) {
78 const std::vector<Complex> complex_field = complex_field_from_reciprocal_space(
79 grid_dimensions, miller_indices, coefficients, holds_half_sphere);
80 std::vector<Real> field(complex_field.size());
81 for (std::size_t index = 0; index < complex_field.size(); index++)
82 field[index] = complex_field[index].real();
83 return field;
84}
85
86} // namespace q3
void init(int dimension_1, int dimension_2, int dimension_3, bool use_measured_planning=false) override
void inverse_raw(Complex *data) const override
int total_grid_points() const override
int g_vector_to_fft_grid_index(int miller_1, int miller_2, int miller_3) const override
std::vector< Real > real_field_from_reciprocal_space(const std::array< int, 3 > &grid_dimensions, const std::vector< IntegerVector3D > &miller_indices, const std::vector< Complex > &coefficients, bool holds_half_sphere)
The same transform for a field that is real by construction, e.g. a density.
std::complex< double > Complex
std::vector< Complex > complex_field_from_reciprocal_space(const std::array< int, 3 > &grid_dimensions, const std::vector< IntegerVector3D > &miller_indices, const std::vector< Complex > &coefficients, bool holds_half_sphere)
Integer 3-vector for Miller indices / FFT grid indices.