Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Baseline indexer part1 #25

Open
wants to merge 75 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
96733f0
Add start of an indexer program
jbeilstenedmands Oct 31, 2024
d60712b
Add xyz_to_rlp function as a first step working example
jbeilstenedmands Nov 1, 2024
8b68a89
Add fft3d with pocketfft
jbeilstenedmands Nov 1, 2024
29f3355
Tidying, comment out parser for now
jbeilstenedmands Nov 1, 2024
6da5128
Add last bit of indexing part 1 - getting candidate vecs
jbeilstenedmands Nov 4, 2024
cb7aa4f
Try extracting some metadata from the h5 file through the reader.
jbeilstenedmands Nov 5, 2024
160c521
Add reading of scan start and osc width
jbeilstenedmands Nov 5, 2024
b21a08b
Add a more formal beam model
jbeilstenedmands Nov 5, 2024
256e66e
Add initialization from json data
jbeilstenedmands Nov 5, 2024
2819da5
Add json serialization, add dx2 scan class
jbeilstenedmands Nov 6, 2024
d2a9d76
Add gonio dx2 model
jbeilstenedmands Nov 6, 2024
63fc297
Include eigen, move models dir
jbeilstenedmands Nov 6, 2024
4cf5210
Use dx2 repo
jbeilstenedmands Nov 6, 2024
c3404cc
Add command line parsing, remove reader, update to dx2 detector
jbeilstenedmands Nov 7, 2024
39b80be
Add pocketfft as dependency
jbeilstenedmands Nov 8, 2024
f6875de
Use new panel model from dx2
jbeilstenedmands Nov 8, 2024
ae90772
Start basis vector combination code
jbeilstenedmands Nov 8, 2024
30e86ff
Add basis vector combinations and index assignment
jbeilstenedmands Nov 13, 2024
d2670d4
Update and tidying
jbeilstenedmands Nov 13, 2024
7ab2569
Tidy up, add some extra arguments, output a simple experiment list
jbeilstenedmands Nov 26, 2024
3006891
Add some prediction and calculating reflection properties
jbeilstenedmands Nov 27, 2024
5117271
Add better results output for candidates
jbeilstenedmands Nov 28, 2024
bf2535c
Template array reading, add candidate vecs output json
jbeilstenedmands Dec 3, 2024
13c358a
Move into baseline_indexer folder, just to candidate vecs for now
jbeilstenedmands Dec 3, 2024
db0dff0
Output tidying
jbeilstenedmands Dec 9, 2024
96ba5c8
Use standard argparse for baseline indexer
jbeilstenedmands Dec 9, 2024
abcba8b
Updates
jbeilstenedmands Dec 10, 2024
f619de8
Add these back in
jbeilstenedmands Dec 10, 2024
89ea8f0
Remove code now in dx2
jbeilstenedmands Dec 10, 2024
d77563e
Revert changes to h5read.c
jbeilstenedmands Dec 10, 2024
3475bdd
Remove unneeded struct and import
jbeilstenedmands Dec 10, 2024
0b71440
Newlines
jbeilstenedmands Dec 10, 2024
450f9be
Revert changes to h5read.h
jbeilstenedmands Dec 10, 2024
43858cd
Output an example experiment list, add a test as an example of how to…
jbeilstenedmands Jan 10, 2025
41efadc
Allow setting of number of fft grid points
jbeilstenedmands Jan 13, 2025
c1e148b
Add more comments into code, function docstrings.
jbeilstenedmands Jan 17, 2025
542dfde
Merge branch 'main' into baseline_indexer_part1
jbeilstenedmands Jan 17, 2025
913a702
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 17, 2025
33f15af
Apply suggestions from code review - small changes/one-liners
jbeilstenedmands Jan 20, 2025
722b64c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 20, 2025
d438591
Add more suggestions from code review.
jbeilstenedmands Jan 20, 2025
a4b0ba9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 20, 2025
8230a13
More suggested changes.
jbeilstenedmands Jan 20, 2025
8287cd0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 20, 2025
99bc0f0
Some tidying
jbeilstenedmands Jan 20, 2025
fe5113a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 20, 2025
c2a7548
Add dx2 as a dependency
jbeilstenedmands Jan 17, 2025
32dce33
Use the new experiment class
jbeilstenedmands Jan 17, 2025
bed6bb1
Add autodiscovery and settable nthreads for fft
jbeilstenedmands Jan 20, 2025
1813a2d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 20, 2025
44f83bc
Merge branch 'DiamondLightSource:main' into baseline_indexer_part1
jbeilstenedmands Jan 20, 2025
a2a17ab
Use spdlog to log to console and file with suitable log levels
jbeilstenedmands Jan 20, 2025
2badc8d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 20, 2025
86cb071
Use parser.is_used for nthreads parameter
jbeilstenedmands Jan 20, 2025
4102681
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 20, 2025
24e7912
Remove CUDA requirements from CMakelists and fix cmake warnings
jbeilstenedmands Jan 20, 2025
8e04756
Add a dev flag for cmake to switch between dx2 as a subdirectory or t…
jbeilstenedmands Jan 20, 2025
8fadfa7
Pin to the merged dx2 commit.
jbeilstenedmands Jan 20, 2025
a0b93b3
Update file paths in test, tidying
jbeilstenedmands Jan 21, 2025
1b9639f
Update dx2 source discovery
jbeilstenedmands Jan 22, 2025
fae88d0
Update CMakeLists.txt
jbeilstenedmands Jan 22, 2025
70a7df3
Use an unordered map rather than array
jbeilstenedmands Jan 22, 2025
a5a0811
Update test due to sign changes
jbeilstenedmands Jan 22, 2025
9f8767d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 22, 2025
f2f4989
Merge branch 'main' into baseline_indexer_part1
jbeilstenedmands Jan 23, 2025
589029f
Use common logger
jbeilstenedmands Jan 23, 2025
984d8df
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 23, 2025
72833c8
Add unit test for xyz_to_rlp
jbeilstenedmands Jan 29, 2025
490da03
Add test for fft3d file
jbeilstenedmands Feb 3, 2025
806d18c
Add flood-fill test, move to tests subdirectory
jbeilstenedmands Feb 4, 2025
429c8c6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 4, 2025
a56cc95
Update test to test the rmsd cutoff filter
jbeilstenedmands Feb 4, 2025
81abd60
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 4, 2025
77c7b3b
Rename sites_to_vecs to peaks_to_rlvs, add test
jbeilstenedmands Feb 6, 2025
2b53b09
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,40 @@ FetchContent_Declare(
)
FetchContent_MakeAvailable(spdlog)

# Handle inclusion of dx2 dependency in a choice of several ways
set(dx2_SOURCE "" CACHE FILEPATH "Location of dx2 source directory")
set(_dx2_already_populated "${dx2_SOURCE_DIR}")
if(dx2_SOURCE)
# Explicitly told where to find dx2
add_subdirectory(${dx2_SOURCE} dx2)
set(dx2_SOURCE_DIR "${dx2_SOURCE}")
elseif(EXISTS ${CMAKE_SOURCE_DIR}/dx2)
# dx2 exists as a local checkout
add_subdirectory(dx2)
set(dx2_SOURCE_DIR "${CMAKE_SOURCE_DIR}/dx2")
else()
# Try finding an installed dx2, or fetch
FetchContent_Declare(
dx2
GIT_REPOSITORY https://github.com/dials/dx2
GIT_TAG 2654929bc73e0d699dfdfcc2d924c3454e434ccf
FIND_PACKAGE_ARGS
)
FetchContent_MakeAvailable(dx2)
endif()
# Only print a message when as a new action
if (NOT dx2_SOURCE_DIR STREQUAL _dx2_already_populated)
message(STATUS "Found dx2: ${dx2_SOURCE_DIR}")
endif()

# Make a small library that we can link to to get the version
project(version CXX)
configure_file(version.cc.in version.cc @ONLY)
add_library(version STATIC version.cc)

enable_testing()

add_subdirectory(h5read)
add_subdirectory(baseline)
add_subdirectory(spotfinder)
add_subdirectory(baseline_indexer)
48 changes: 48 additions & 0 deletions baseline_indexer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
project(indexer CXX)

set(CMAKE_CXX_STANDARD 20)

find_package (Threads)

# Automatic Dependencies
set(FETCHCONTENT_QUIET OFF)
include(FetchContent)
FetchContent_Declare(
Eigen3
GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git
GIT_TAG 3.4.0
EXCLUDE_FROM_ALL
FIND_PACKAGE_ARGS
)
FetchContent_Declare(
pocketfft
GIT_REPOSITORY https://github.com/mreineck/pocketfft
GIT_TAG cpp
)
FetchContent_MakeAvailable(Eigen3)
FetchContent_MakeAvailable(pocketfft)

FetchContent_GetProperties(pocketfft)
add_library(pocketfft INTERFACE)
target_include_directories(pocketfft INTERFACE ${pocketfft_SOURCE_DIR})

# GTest could have been made available under a different name
if(NOT TARGET GTest::gtest_main)
FetchContent_MakeAvailable(GTest)
endif()

add_subdirectory(tests)
add_executable(baseline_indexer
indexer.cc
)
target_link_libraries(baseline_indexer
PRIVATE
fmt
dx2
Eigen3::Eigen
pocketfft
argparse
nlohmann_json::nlohmann_json
spdlog::spdlog
${CMAKE_THREAD_LIBS_INIT}
)
177 changes: 177 additions & 0 deletions baseline_indexer/fft3d.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#include <assert.h>
#include <math.h>
#include <pocketfft_hdronly.h>

#include <Eigen/Dense>
#include <algorithm>
#include <chrono>
#include <map>
#include <stack>
#include <tuple>

#include "common.hpp"

using Eigen::Matrix3d;
using Eigen::Vector3d;
using Eigen::Vector3i;

#define _USE_MATH_DEFINES
#include <cmath>

using namespace pocketfft;

/**
* @brief map reciprocal space vectors onto a grid of size n_points^3.
* @param reciprocal_space_vectors Reciprocal space vectors to be mapped.
* @param data_in The vector (grid) which the data will be mapped to.
* @param selection The vector of the selection of points mapped to the grid.
* @param d_min A resolution limit for mapping to the grid.
* @param b_iso The isotropic B-factor used to weight the points as a function of resolution.
* @param n_points The size of each dimension of the FFT grid.
*/
void map_centroids_to_reciprocal_space_grid(
std::vector<Vector3d> const &reciprocal_space_vectors,
std::vector<std::complex<double>> &data_in,
std::vector<bool> &selection,
double d_min,
double b_iso = 0,
uint32_t n_points = 256) {
assert(data_in.size() == n_points * n_points * n_points);
// Determine the resolution span of the grid so we know how to map
// each coordinate to the grid.
// -dmin to +dmin spans the coordinate range from 0 to N (in each dimension),
// but the discrete FFT requires input coefficients 0 to N-1, and so our
// grid goes from 0 .. N-1 and we don't use data that maps to grid point N
// (which is equivalent to grid point 0)

const double rlgrid = 2 / (d_min * n_points);
const double one_over_rlgrid = 1 / rlgrid;
const int half_n_points = n_points / 2;
int count = 0;

for (int i = 0; i < reciprocal_space_vectors.size(); i++) {
const Vector3d v = reciprocal_space_vectors[i];
const double v_length = v.norm();
const double d_spacing = 1 / v_length;
if (d_spacing < d_min) {
selection[i] = false;
continue;
}
Vector3i coord;
// map to the nearest point in each dimension.
for (int j = 0; j < 3; j++) {
coord[j] = static_cast<int>(round(v[j] * one_over_rlgrid)) + half_n_points;
}
if ((coord.maxCoeff() >= n_points) || coord.minCoeff() < 0) {
// We can get maxcoeff == n_points if we anear +dmin, see comment above.
selection[i] = false;
continue;
}
// Use the b_iso to determine the weight for each coordinate.
double T;
if (b_iso != 0) {
T = std::exp(-b_iso * v_length * v_length / 4.0);
} else {
T = 1;
}
// unravel to the 1d index and write the complex<double> value.
size_t index =
coord[2] + (n_points * coord[1]) + (n_points * n_points * coord[0]);
if (!data_in[index].real()) {
count++;
}
data_in[index] = {T, 0.0};
}
logger->info("Number of centroids used: {}", count);
}

/**
* @brief Perform a 3D FFT of the reciprocal space coordinates (spots).
* @param reciprocal_space_vectors The input vector of reciprocal space coordinates.
* @param real_out The (real) array that the FFT result will be written to.
* @param d_min Cut the data at this resolution limit for the FFT
* @param b_iso The isotropic B-factor used to weight the points as a function of resolution.
* @param n_points The size of each dimension of the FFT grid.
* @returns A boolean array indicating which coordinates were used for the FFT.
*/
std::vector<bool> fft3d(std::vector<Vector3d> const &reciprocal_space_vectors,
std::vector<double> &real_out,
double d_min,
double b_iso = 0,
uint32_t n_points = 256,
size_t nthreads = 1) {
auto start = std::chrono::system_clock::now();
assert(real_out.size() == n_points * n_points * n_points);

// We want to write out the real part of the FFT, but the pocketfft functions require
// complex vectors (we are using c2c i.e. complex to complex), so initialise these vectors.
// Note we should be able to use c2r rather than c2c, but I couldn't get this to work with
// the output ordering in c2r (JBE).
std::vector<std::complex<double>> complex_data_in(n_points * n_points * n_points);

// A boolean array of whether the vectors were used for the FFT.
std::vector<bool> used_in_indexing(reciprocal_space_vectors.size(), true);
auto t1 = std::chrono::system_clock::now();

// Map the vectors onto a discrete grid. The values of the grid points are weights
// determined by b_iso.
map_centroids_to_reciprocal_space_grid(reciprocal_space_vectors,
complex_data_in,
used_in_indexing,
d_min,
b_iso,
n_points);
auto t2 = std::chrono::system_clock::now();

// Prepare the required objects for the FFT.
shape_t shape_in{n_points, n_points, n_points};
int stride_x = sizeof(std::complex<double>);
int stride_y = static_cast<int>(sizeof(std::complex<double>) * n_points);
int stride_z = static_cast<int>(sizeof(std::complex<double>) * n_points * n_points);
stride_t stride_in{
stride_x, stride_y, stride_z}; // must have the size of each element. Must have
// size() equal to shape_in.size()
stride_t stride_out{
stride_x, stride_y, stride_z}; // must have the size of each element. Must
// have size() equal to shape_in.size()
shape_t axes{0, 1, 2}; // 0 to shape.size()-1 inclusive
bool forward{FORWARD};

double fct{1.0f};
// note, threads can be higher than the number of hardware threads.
// It is not clear what the best value is for this.
logger->info("Performing FFT with nthreads={}", nthreads);
// Do the FFT.
c2c(
shape_in,
stride_in,
stride_out,
axes,
forward,
complex_data_in.data(),
complex_data_in
.data(), // this is the output array, we are going to overwrite the input as we don't need it
fct,
nthreads);
auto t3 = std::chrono::system_clock::now();

// Take the square of the real part as the output.
for (int i = 0; i < real_out.size(); ++i) {
real_out[i] = std::pow(complex_data_in[i].real(), 2);
}
auto t4 = std::chrono::system_clock::now();

std::chrono::duration<double> elapsed_seconds = t4 - start;
std::chrono::duration<double> elapsed_map = t2 - t1;
std::chrono::duration<double> elapsed_make_arrays = t1 - start;
std::chrono::duration<double> elapsed_c2c = t3 - t2;
std::chrono::duration<double> elapsed_square = t4 - t3;
logger->debug("Total time for fft3d: {:.5f}s", elapsed_seconds.count());
logger->debug("elapsed time for making data arrays: {:.5f}s",
elapsed_make_arrays.count());
logger->debug("elapsed time for map_to_recip: {:.5f}s", elapsed_map.count());
logger->debug("elapsed time for c2c: {:.5f}s", elapsed_c2c.count());
logger->debug("elapsed time for squaring: {:.5f}s", elapsed_square.count());

return used_in_indexing;
}
Loading