Skip to content

Commit 7c2fce9

Browse files
Add sigma_b, sigma_m calculation to the integrators (#81)
1 parent dee4a5c commit 7c2fce9

File tree

5 files changed

+229
-13
lines changed

5 files changed

+229
-13
lines changed

baseline/indexer/indexer.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "flood_fill.cc"
3232
#include "gemmi/symmetry.hpp"
3333
#include "peaks_to_rlvs.cc"
34+
#include "scan_static_predictor.cc"
3435
#include "score_crystals.cc"
3536
#include "xyz_to_rlp.cc"
3637

@@ -460,6 +461,14 @@ int main(int argc, char **argv) {
460461
}
461462
}
462463

464+
strong_reflections.add_column(std::string("entering"), enterings);
465+
// Call the predictor to get xyzcal values in the output.
466+
simple_reflection_predictor(expt.beam(),
467+
expt.goniometer(),
468+
expt.crystal().get_A_matrix(),
469+
expt.detector().panels()[0],
470+
strong_reflections);
471+
463472
// Save the indexed reflection table.
464473
std::string output_filename = "indexed.refl";
465474
strong_reflections.write(output_filename);

baseline/integrator/integrator.cc

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "extent.cc"
2323
#include "ffs_logger.hpp"
2424
#include "kabsch.cc"
25+
#include "math/math_utils.cuh"
26+
#include "sigma_estimation.cc"
2527
#include "version.hpp"
2628

2729
using json = nlohmann::json;
@@ -84,6 +86,13 @@ class BaselineIntegratorArgumentParser : public FFSArgumentParser {
8486
.metavar("σb")
8587
.scan<'f', float>();
8688

89+
add_argument("--sigma_estimation.min_bbox_depth", "--min_bbox_depth")
90+
.help(
91+
"When calculating sigma_m, only use reflections that span at least this "
92+
"number of images.")
93+
.default_value<int>(6)
94+
.scan<'i', int>();
95+
8796
add_argument("output")
8897
.help("Output file path")
8998
.metavar("output.h5")
@@ -102,19 +111,9 @@ int main(int argc, char **argv) {
102111
const auto reflection_file = parser.reflections();
103112
const auto experiment_file = parser.experiment();
104113

105-
float sigma_m =
106-
parser.get<float>("sigma_m") * (M_PI / 180.0f); // Convert to radians
107-
float sigma_b =
108-
parser.get<float>("sigma_b") * (M_PI / 180.0f); // Convert to radians
109114
float timeout = parser.get<float>("timeout");
110115
std::string output_file = parser.get<std::string>("output");
111116

112-
logger.info("Parameters: sigma_m={:.6f}, sigma_b={:.6f}, timeout={:.1f}, output={}",
113-
sigma_m,
114-
sigma_b,
115-
timeout,
116-
output_file);
117-
118117
// Guard against missing files
119118
if (!std::filesystem::exists(reflection_file)) {
120119
logger.error("Reflection file not found: {}", reflection_file);
@@ -202,6 +201,53 @@ int main(int argc, char **argv) {
202201
logger.info(" Oscillation: start={:.3f}°, width={:.3f}°", osc_start, osc_width);
203202
logger.info(" Image range: {} to {}", image_range_start, image_range_end);
204203

204+
// If input is a predicted refl, then we require sigma_b, sigma_m as we will not be
205+
// able to estimate it from the data
206+
// Else the input as an indexed.refl/refined.refl with the sigma variance columns,
207+
// then we can calculate sigma_b, sigma_m but will have to also run the predict
208+
// code in this program.
209+
float sigma_b = 0.0;
210+
float sigma_m = 0.0;
211+
if (parser.is_used("sigma_m")) {
212+
sigma_m = degrees_to_radians(
213+
parser.get<float>("sigma_m")); // Use radians for calculations
214+
}
215+
if (parser.is_used("sigma_b")) {
216+
sigma_b = degrees_to_radians(
217+
parser.get<float>("sigma_b")); // Use radians for calculations
218+
}
219+
220+
// Estimate sigmas
221+
auto sigma_b_data = reflections.column<double>("sigma_b_variance");
222+
auto sigma_m_data = reflections.column<double>("sigma_m_variance");
223+
auto extent_z_data = reflections.column<int>("spot_extent_z");
224+
if (sigma_b_data && sigma_m_data && extent_z_data) {
225+
int min_bbox_depth = parser.get<int>("sigma_estimation.min_bbox_depth");
226+
// Estimate the values from the data, and use if user hasn't specified values.
227+
auto [sigma_b_calc, sigma_m_calc, sigma_rmsd_calc] =
228+
estimate_sigmas(reflections, expt, min_bbox_depth);
229+
// Note we might want to inflate sigma_b_calc to include the rmsd too, but we just report
230+
// it for now.
231+
if (sigma_m == 0.0) {
232+
sigma_m = sigma_m_calc;
233+
}
234+
if (sigma_b == 0.0) {
235+
sigma_b = sigma_b_calc;
236+
}
237+
}
238+
if (sigma_b == 0.0) {
239+
throw std::runtime_error(
240+
"No value for sigma_b. This must either be provided as input, or an input "
241+
"reflection "
242+
"table containing sigma_b_variance must be used.");
243+
}
244+
if (sigma_m == 0.0) {
245+
throw std::runtime_error(
246+
"No value for sigma_m. This must either be provided as input, or an input "
247+
"reflection "
248+
"table containing sigma_m_variance and spot_extent_z must be used.");
249+
}
250+
205251
// Compute bounding boxes using baseline CPU algorithms
206252
logger.info("Computing Kabsch bounding boxes using baseline CPU algorithms...");
207253
auto computed_bounding_boxes = compute_kabsch_bounding_boxes(s0,
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#include <dx2/beam.hpp>
2+
#include <dx2/detector.hpp>
3+
#include <dx2/experiment.hpp>
4+
#include <dx2/reflection.hpp>
5+
#include <math/math_utils.cuh>
6+
#include <tuple>
7+
8+
#include "ffs_logger.hpp"
9+
10+
constexpr size_t indexed_flag = (1 << 2); // 4
11+
/*
12+
Function to calculate the square deviation in kabsch space between
13+
the predicted and observed positions.
14+
*/
15+
double squaredev_in_kabsch_space(const Vector3d &xyzcal, //mm
16+
const Vector3d &xyzobs, //mm
17+
const Vector3d &s0,
18+
const Panel &panel) {
19+
Vector3d s1cal = panel.get_lab_coord(xyzcal[0], xyzcal[1]);
20+
Vector3d s1obs = panel.get_lab_coord(xyzobs[0], xyzobs[1]);
21+
22+
Vector3d e1 = s1cal.cross(s0);
23+
e1.normalize();
24+
Vector3d e2 = s1cal.cross(e1);
25+
e2.normalize();
26+
double mags1 = std::sqrt(s1cal.dot(s1cal));
27+
Vector3d delta_s1 = s1obs - s1cal;
28+
double eps1 = e1.dot(delta_s1) / mags1;
29+
double eps2 = e2.dot(delta_s1) / mags1;
30+
double var = (eps1 * eps1) + (eps2 * eps2);
31+
return var;
32+
}
33+
34+
std::tuple<double, double, double> estimate_sigmas(ReflectionTable const &indexed,
35+
Experiment<MonochromaticBeam> &expt,
36+
int min_bbox_depth = 6) {
37+
auto flags = indexed.column<std::size_t>("flags");
38+
auto &flags_data = flags.value();
39+
std::vector<bool> selection(flags_data.extent(0), false);
40+
for (int i = 0; i < flags_data.size(); ++i) {
41+
if (flags_data(i, 0) & indexed_flag) {
42+
selection[i] = true;
43+
}
44+
}
45+
ReflectionTable filtered = indexed.select(selection);
46+
auto filtered_sigma_b = filtered.column<double>("sigma_b_variance");
47+
auto &filtered_sigma_b_data = filtered_sigma_b.value();
48+
auto filtered_sigma_m = filtered.column<double>("sigma_m_variance");
49+
auto &filtered_sigma_m_data = filtered_sigma_m.value();
50+
auto extent_z = filtered.column<int>("spot_extent_z");
51+
auto &extent_z_data = extent_z.value();
52+
double sigma_b_total = 0;
53+
double sigma_m_total = 0;
54+
int n_sigma_m = 0;
55+
for (int i = 0; i < filtered_sigma_b_data.extent(0); ++i) {
56+
sigma_b_total += filtered_sigma_b_data(i, 0);
57+
if (extent_z_data(i, 0) >= min_bbox_depth) {
58+
sigma_m_total += filtered_sigma_m_data(i, 0);
59+
n_sigma_m++;
60+
}
61+
}
62+
double sigma_b_radians =
63+
std::pow(sigma_b_total / filtered_sigma_b_data.extent(0), 0.5);
64+
logger.info("Sigma b estimate (degrees): {:.6f} on {} reflections",
65+
radians_to_degrees(sigma_b_radians),
66+
filtered_sigma_b_data.extent(0));
67+
if (n_sigma_m == 0) {
68+
throw std::runtime_error(
69+
"Unable to estimate sigma_m, no reflections above min_bbox_depth.");
70+
}
71+
double sigma_m_radians = std::pow(sigma_m_total / n_sigma_m, 0.5);
72+
logger.info(
73+
"Sigma m estimate (degrees): {:.6f} on {} reflections with min_bbox_depth={}",
74+
radians_to_degrees(sigma_m_radians),
75+
n_sigma_m,
76+
min_bbox_depth);
77+
// loop through refls - map s1 to recip space
78+
auto xyz = filtered.column<double>("xyzobs.mm.value");
79+
auto &xyzobs = xyz.value();
80+
auto xyz2 = filtered.column<double>("xyzcal.mm");
81+
auto &xyzcal = xyz2.value();
82+
Panel p = expt.detector().panels()[0];
83+
double tot_rmsd = 0;
84+
int count = 0;
85+
for (int i = 0; i < xyzcal.extent(0); ++i) {
86+
Eigen::Map<Vector3d> xyzcal_this(&xyzcal(i, 0));
87+
Eigen::Map<Vector3d> xyzobs_this(&xyzobs(i, 0));
88+
double val =
89+
squaredev_in_kabsch_space(xyzcal_this, xyzobs_this, expt.beam().get_s0(), p);
90+
if (radians_to_degrees(std::pow(val, 0.5))
91+
< 0.1) { // Guard against mispredictions in indexing.
92+
tot_rmsd += val;
93+
count++;
94+
}
95+
}
96+
if (count == 0) {
97+
throw std::runtime_error(
98+
"Unable to estimate rmsd deviation, predicted reflections are too far from "
99+
"observed");
100+
}
101+
double rmsd_deviation_radians = std::pow(tot_rmsd / count, 0.5);
102+
logger.info("Sigma rmsd (degrees): {:.6f} on {} reflections",
103+
radians_to_degrees(rmsd_deviation_radians),
104+
count);
105+
return std::make_tuple(sigma_b_radians, sigma_m_radians, rmsd_deviation_radians);
106+
}

integrator/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_executable(integrator
1313
target_include_directories(integrator
1414
PRIVATE
1515
${CMAKE_CURRENT_SOURCE_DIR}/../spotfinder
16+
${CMAKE_CURRENT_SOURCE_DIR}/../baseline/integrator
1617
)
1718

1819
target_link_libraries(integrator

integrator/integrator.cc

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "math/device_precision.cuh"
3333
#include "math/math_utils.cuh"
3434
#include "math/vector3d.cuh"
35+
#include "sigma_estimation.cc"
3536
#include "version.hpp"
3637

3738
using namespace std::chrono_literals;
@@ -135,6 +136,13 @@ class IntegratorArgumentParser : public CUDAArgumentParser {
135136
"Sigma_b: Standard deviation of the beam direction in reciprocal space.")
136137
.metavar("σb")
137138
.scan<'f', float>();
139+
140+
add_argument("--sigma_estimation.min_bbox_depth", "--min_bbox_depth")
141+
.help(
142+
"When calculating sigma_m, only use reflections that span at least this "
143+
"number of images.")
144+
.default_value<int>(6)
145+
.scan<'i', int>();
138146
}
139147
};
140148
#pragma endregion Argument Parsing
@@ -149,9 +157,6 @@ int main(int argc, char **argv) {
149157
const auto reflection_file = parser.reflections();
150158
const auto experiment_file = parser.experiment();
151159
float wait_timeout = parser.get<float>("timeout");
152-
// These two will be optional later, should be gettable from reflection table
153-
float sigma_m = parser.get<float>("sigma_m");
154-
float sigma_b = parser.get<float>("sigma_b");
155160

156161
// Guard against missing files
157162
if (!std::filesystem::exists(reflection_file)) {
@@ -252,6 +257,55 @@ int main(int argc, char **argv) {
252257

253258
#pragma endregion Data preparation
254259

260+
#pragma region Sigma estimation
261+
// If input is a predicted refl, then we require sigma_b, sigma_m as we will not be
262+
// able to estimate it from the data
263+
// Else the input as an indexed.refl/refined.refl with the sigma variance columns,
264+
// then we can calculate sigma_b, sigma_m but will have to also run the predict
265+
// code in this program.
266+
float sigma_b = 0.0;
267+
float sigma_m = 0.0;
268+
if (parser.is_used("sigma_m")) {
269+
sigma_m = degrees_to_radians(
270+
parser.get<float>("sigma_m")); // Use radians for calculations
271+
}
272+
if (parser.is_used("sigma_b")) {
273+
sigma_b = degrees_to_radians(
274+
parser.get<float>("sigma_b")); // Use radians for calculations
275+
}
276+
277+
// Estimate sigmas
278+
auto sigma_b_data = reflections.column<double>("sigma_b_variance");
279+
auto sigma_m_data = reflections.column<double>("sigma_m_variance");
280+
auto extent_z_data = reflections.column<int>("spot_extent_z");
281+
if (sigma_b_data && sigma_m_data && extent_z_data) {
282+
int min_bbox_depth = parser.get<int>("sigma_estimation.min_bbox_depth");
283+
// Estimate the values from the data, and use if user hasn't specified values.
284+
auto [sigma_b_calc, sigma_m_calc, sigma_rmsd_calc] =
285+
estimate_sigmas(reflections, expt, min_bbox_depth);
286+
// Note we might want to inflate sigma_b_calc to include the rmsd too, but we just report
287+
// it for now.
288+
if (sigma_m == 0.0) {
289+
sigma_m = sigma_m_calc;
290+
}
291+
if (sigma_b == 0.0) {
292+
sigma_b = sigma_b_calc;
293+
}
294+
}
295+
if (sigma_b == 0.0) {
296+
throw std::runtime_error(
297+
"No value for sigma_b. This must either be provided as input, or an input "
298+
"reflection "
299+
"table containing sigma_b_variance must be used.");
300+
}
301+
if (sigma_m == 0.0) {
302+
throw std::runtime_error(
303+
"No value for sigma_m. This must either be provided as input, or an input "
304+
"reflection "
305+
"table containing sigma_m_variance and spot_extent_z must be used.");
306+
}
307+
#pragma endregion Sigma estimation
308+
255309
#pragma region Image Reading and Threading
256310
// Now set up for multi-threaded image reading and processing
257311
logger.info("Setting up image reading and threading");

0 commit comments

Comments
 (0)