// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include "gemmi/grid.hpp" #include "gemmi/recgrid.hpp" // The structure factors of a real-space map on the model path (Fcalc from the model density, Fmask // from the bulk-solvent mask): what gemmi::transform_map_to_f_phi(map, true) returns - the // conjugated forward transform (F = sum rho exp(+2 pi i h.x)) scaled by V/N, kept for l >= 0 on an (nu, nv, nw/2+1) grid in the map's own XYZ order, // ready for prepare_asu_data() - computed with FFTW rather than gemmi's single-threaded pocketfft. // The same transform, not the same bits: the two libraries order their arithmetic differently. // // Plans are FFTW_ESTIMATE only - MEASURE would time candidate algorithms and could pick a different // one from run to run - and made once per grid size and kept, so the rigid body's hundred-odd // evaluations on one grid plan once. Safe to call from several threads at once. gemmi::FPhiGrid MapToFPhi(const gemmi::Grid &map); // The inverse: the real-space map of an (nu, nv, nw/2+1) half-l coefficient grid in XYZ order, as // get_f_phi_on_grid() fills it - what gemmi::transform_f_phi_grid_to_map() returns (rho = 1/V sum F // exp(-2 pi i h.x) on an (nu, nv, 2*(nw-1)) grid, NaN coefficients read as zero), computed with FFTW // under the same planning rules. gemmi::Grid MapFromFPhi(const gemmi::FPhiGrid &hkl); // MapFromFPhi's transform itself, for a caller that fills its input: `in`, the (nu, nv, nw/2+1) half-l coefficients // u fastest as MapFromFPhi hands them to FFTW (conjugated, a NaN as zero), is overwritten, and `out` receives the // (nu, nv, nw) map before MapFromFPhi's 1/V. Both from fftwf_malloc, as a plan requires. void MapFromFPhiExecute(int nu, int nv, int nw, std::complex *in, float *out); // CropHalfL(MapToFPhi(map), reach) on `nthreads` threads, for a map of hundreds of millions of points (the // molecular transform's box), whose single-threaded transform is minutes of CPU: the r2c along w of every // (u, v) column, then the 2D transform of every plane l <= reach - each column and each plane by one plan, so // the result does not depend on the thread count. The same transform as MapToFPhi to float rounding, not bit // for bit: FFTW's 3D plan orders its arithmetic its own way. gemmi::FPhiGrid MapToFPhiInParallel(const gemmi::Grid &map, int reach, size_t nthreads); // The half-l grid `full` with only the indices |k|, l <= reach kept, on a grid of full.nu x (2 reach + 1) x // (reach + 1) that an index wraps into as it wraps into the whole one (the molecular transform's, which is // read no further out). gemmi::FPhiGrid CropHalfL(const gemmi::FPhiGrid &full, int reach);