Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1b57a83e0 | |||
| de6646b95e | |||
| 0d92ea47d0 | |||
| 1a993f4ecb | |||
| fa69ac89f3 | |||
| 33410e1544 | |||
| fe87291c1c |
@@ -19,9 +19,10 @@ struct NiggliClass {
|
||||
gemmi::Mat33 reindex;
|
||||
gemmi::CrystalSystem system;
|
||||
char centering;
|
||||
int lowest_spacegroup_number;
|
||||
};
|
||||
|
||||
LatticeSearchResult LatticeSearch(const CrystalLattice &L, double dist_tolerance, double angle_tolerance) {
|
||||
LatticeSearchResult LatticeSearch(const CrystalLattice &L, double dist_tolerance, double angle_tolerance, int ngc_id) {
|
||||
UnitCell uc = L.GetUnitCell();
|
||||
gemmi::UnitCell g_uc(uc.a, uc.b, uc.c, uc.alpha, uc.beta, uc.gamma);
|
||||
gemmi::GruberVector g_vec(g_uc, 'P', true);
|
||||
@@ -43,268 +44,274 @@ LatticeSearchResult LatticeSearch(const CrystalLattice &L, double dist_tolerance
|
||||
1, 1,
|
||||
true, true, A / 2, A / 2, A / 2, false, false,
|
||||
gemmi::Mat33{1, -1, 1, 1, 1, -1, -1, 1, 1},
|
||||
gemmi::CrystalSystem::Cubic, 'F'
|
||||
gemmi::CrystalSystem::Cubic, 'F', 196
|
||||
},
|
||||
{
|
||||
2, 1,
|
||||
true, true, D, D, D, false, false,
|
||||
{1, -1, 0, -1, 0, 1, -1, -1, -1},
|
||||
gemmi::CrystalSystem::Trigonal, 'R'
|
||||
gemmi::CrystalSystem::Trigonal, 'R', 146
|
||||
},
|
||||
{
|
||||
3, 2,
|
||||
true, true, 0, 0, 0, false, false,
|
||||
gemmi::Mat33{1, 0, 0, 0, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Cubic, 'P'
|
||||
gemmi::CrystalSystem::Cubic, 'P', 195
|
||||
},
|
||||
{
|
||||
5, 2,
|
||||
true, true, -A / 3, -A / 3, -A / 3, false, false,
|
||||
gemmi::Mat33{1, 0, 1, 1, 1, 0, 0, 1, 1},
|
||||
gemmi::CrystalSystem::Cubic, 'I'
|
||||
gemmi::CrystalSystem::Cubic, 'I', 197
|
||||
},
|
||||
{
|
||||
4, 2,
|
||||
true, true, D, D, D, false, false,
|
||||
{1, -1, 0, -1, 0, 1, -1, -1, -1},
|
||||
gemmi::CrystalSystem::Trigonal, 'R'
|
||||
gemmi::CrystalSystem::Trigonal, 'R', 146
|
||||
},
|
||||
{
|
||||
6, 2,
|
||||
true, true, D, D, F, true, false,
|
||||
{0, 1, 1, 1, 0, 1, 1, 1, 0},
|
||||
gemmi::CrystalSystem::Tetragonal, 'I'
|
||||
gemmi::CrystalSystem::Tetragonal, 'I', 79
|
||||
},
|
||||
{
|
||||
7, 2,
|
||||
true, true, D, E, E, true, false,
|
||||
{1, 0, 1, 1, 1, 0, 0, 1, 1},
|
||||
gemmi::CrystalSystem::Tetragonal, 'I'
|
||||
gemmi::CrystalSystem::Tetragonal, 'I', 79
|
||||
},
|
||||
{
|
||||
8, 2,
|
||||
true, true, D, E, F, true, false,
|
||||
{-1, -1, 0, -1, 0, -1, 0, -1, -1},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'I'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'I', 23
|
||||
},
|
||||
{
|
||||
9, 1,
|
||||
true, false, A / 2, A / 2, A / 2, false, false,
|
||||
{1, 0, 0, -1, 1, 0, -1, -1, -3},
|
||||
gemmi::CrystalSystem::Trigonal, 'R'
|
||||
gemmi::CrystalSystem::Trigonal, 'R', 146
|
||||
},
|
||||
|
||||
{
|
||||
10, 1,
|
||||
true, false, D, D, F, false, false,
|
||||
{1, 1, 0, 1, -1, 0, 0, 0, -1},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
11, 2,
|
||||
true, false, 0, 0, 0, false, false,
|
||||
{1, 0, 0, 0, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Tetragonal, 'P'
|
||||
gemmi::CrystalSystem::Tetragonal, 'P', 75
|
||||
},
|
||||
{
|
||||
12, 2,
|
||||
true, false, 0, 0, -A / 2, false, false,
|
||||
{1, 0, 0, 0, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Hexagonal, 'P'
|
||||
gemmi::CrystalSystem::Hexagonal, 'P', 143
|
||||
},
|
||||
{
|
||||
13, 2,
|
||||
true, false, 0, 0, F, false, false,
|
||||
{1, 1, 0, -1, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C', 20
|
||||
},
|
||||
{
|
||||
15, 2,
|
||||
true, false, -A / 2, -A / 2, 0, false, false,
|
||||
{1, 0, 0, 0, 1, 0, 1, 1, 2},
|
||||
gemmi::CrystalSystem::Tetragonal, 'I'
|
||||
gemmi::CrystalSystem::Tetragonal, 'I', 79
|
||||
},
|
||||
{
|
||||
16, 2,
|
||||
true, false, D, D, F, true, false,
|
||||
{-1, -1, 0, 1, -1, 0, 1, 1, 2},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'F'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'F', 22
|
||||
},
|
||||
{
|
||||
14, 2,
|
||||
true, false, D, D, F, false, false,
|
||||
{1, 1, 0, -1, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
17, 2,
|
||||
true, false, D, E, F, true, false,
|
||||
{1, -1, 0, 1, 1, 0, -1, 0, -1},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
18, 1,
|
||||
false, true, A / 4, A / 2, A / 2, false, false,
|
||||
{0, -1, 1, 1, -1, -1, 1, 0, 0},
|
||||
gemmi::CrystalSystem::Tetragonal, 'I'
|
||||
gemmi::CrystalSystem::Tetragonal, 'I', 79
|
||||
},
|
||||
{
|
||||
19, 1,
|
||||
false, true, D, A / 2, A / 2, false, false,
|
||||
{-1, 0, 0, 0, -1, 1, -1, 1, 1},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'I'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'I', 23
|
||||
},
|
||||
{
|
||||
20, 1,
|
||||
false, true, D, E, E, false, false,
|
||||
{0, 1, 1, 0, 1, -1, -1, 0, 0},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
21, 2,
|
||||
false, true, 0, 0, 0, false, false,
|
||||
{0, 1, 0, 0, 0, 1, 1, 0, 0},
|
||||
gemmi::CrystalSystem::Tetragonal, 'P'
|
||||
gemmi::CrystalSystem::Tetragonal, 'P', 75
|
||||
},
|
||||
{
|
||||
22, 2,
|
||||
false, true, -B / 2, 0, 0, false, false,
|
||||
{0, 1, 0, 0, 0, 1, 1, 0, 0},
|
||||
gemmi::CrystalSystem::Hexagonal, 'P'
|
||||
gemmi::CrystalSystem::Hexagonal, 'P', 143
|
||||
},
|
||||
{
|
||||
23, 2,
|
||||
false, true, D, 0, 0, false, false,
|
||||
{0, 1, 1, 0, -1, 1, 1, 0, 0},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C', 20
|
||||
},
|
||||
{
|
||||
24, 2,
|
||||
false, true, D, -A / 3, -A / 3, true, false,
|
||||
{1, 2, 1, 0, -1, 1, 1, 0, 0},
|
||||
gemmi::CrystalSystem::Trigonal, 'R'
|
||||
gemmi::CrystalSystem::Trigonal, 'R', 146
|
||||
},
|
||||
{
|
||||
25, 2,
|
||||
false, true, D, E, E, false, false,
|
||||
{0, 1, 1, 0, -1, 1, 1, 0, 0},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
26, 1,
|
||||
false, false, A / 4, A / 2, A / 2, false, false,
|
||||
{1, 0, 0, -1, 2, 0, -1, 0, 2},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'F'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'F', 22
|
||||
},
|
||||
{
|
||||
27, 1,
|
||||
false, false, D, A / 2, A / 2, false, false,
|
||||
{-1, 2, 0, -1, 0, 0, 0, -1, 1},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
28, 1,
|
||||
false, false, D, A / 2, 2 * D, false, false,
|
||||
{-1, 0, 0, -1, 0, 2, 0, 1, 0},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
29, 1,
|
||||
false, false, D, 2 * D, A / 2, false, false,
|
||||
{1, 0, 0, 1, -2, 0, 0, 0, -1},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
30, 1,
|
||||
false, false, B / 2, E, 2 * E, false, false,
|
||||
{0, 1, 0, 0, 1, -2, -1, 0, 0},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
|
||||
{
|
||||
31, 1,
|
||||
false, false, D, E, F, false, false,
|
||||
{1, 0, 0, 0, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Triclinic, 'P'
|
||||
gemmi::CrystalSystem::Triclinic, 'P', 75
|
||||
},
|
||||
|
||||
{
|
||||
32, 2,
|
||||
false, false, 0, 0, 0, false, false,
|
||||
{1, 0, 0, 0, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'P'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'P', 16
|
||||
},
|
||||
{
|
||||
40, 2,
|
||||
false, false, -B / 2, 0, 0, false, false,
|
||||
{0, -1, 0, -1, 0, 0, 0, 0, -1},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C', 20
|
||||
},
|
||||
{
|
||||
35, 2,
|
||||
false, false, D, 0, 0, false, false,
|
||||
{0, -1, 0, -1, 0, 0, 0, 0, -1},
|
||||
gemmi::CrystalSystem::Monoclinic, 'P'
|
||||
gemmi::CrystalSystem::Monoclinic, 'P', 3
|
||||
},
|
||||
{
|
||||
36, 2,
|
||||
false, false, 0, -A / 2, 0, false, false,
|
||||
{1, 0, 0, -1, 0, -2, 0, 1, 0},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C', 20
|
||||
},
|
||||
{
|
||||
33, 2,
|
||||
false, false, 0, E, 0, false, false,
|
||||
{1, 0, 0, 0, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Monoclinic, 'P'
|
||||
gemmi::CrystalSystem::Monoclinic, 'P', 3
|
||||
},
|
||||
{
|
||||
38, 2,
|
||||
false, false, 0, 0, -A / 2, false, false,
|
||||
{-1, 0, 0, 1, 2, 0, 0, 0, -1},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'C', 20
|
||||
},
|
||||
{
|
||||
34, 2,
|
||||
false, false, 0, 0, F, false, false,
|
||||
{-1, 0, 0, 0, 0, -1, 0, -1, 0},
|
||||
gemmi::CrystalSystem::Monoclinic, 'P'
|
||||
gemmi::CrystalSystem::Monoclinic, 'P', 3
|
||||
},
|
||||
{
|
||||
42, 2,
|
||||
false, false, -B / 2, -A / 2, 0, false, false,
|
||||
{-1, 0, 0, 0, -1, 0, 1, 1, 2},
|
||||
gemmi::CrystalSystem::Orthorhombic, 'I'
|
||||
gemmi::CrystalSystem::Orthorhombic, 'I', 23
|
||||
},
|
||||
{
|
||||
41, 2,
|
||||
false, false, -B / 2, E, 0, false, false,
|
||||
{0, -1, -2, 0, -1, 0, -1, 0, 0},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
37, 2,
|
||||
false, false, D, -A / 2, 0, false, false,
|
||||
{1, 0, 2, 1, 0, 0, 0, 1, 0},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
39, 2,
|
||||
false, false, D, 0, -A / 2, false, false,
|
||||
{-1, -2, 0, -1, 0, 0, 0, 0, -1},
|
||||
gemmi::CrystalSystem::Monoclinic, 'C'
|
||||
gemmi::CrystalSystem::Monoclinic, 'C', 5
|
||||
},
|
||||
{
|
||||
44, 2,
|
||||
false, false, D, E, F, false, false,
|
||||
{1, 0, 0, 0, 1, 0, 0, 0, 1},
|
||||
gemmi::CrystalSystem::Triclinic, 'P'
|
||||
gemmi::CrystalSystem::Triclinic, 'P', 1
|
||||
}
|
||||
};
|
||||
|
||||
const auto uc_reduced = L_niggli.GetUnitCell();
|
||||
|
||||
for (const auto &c: niggli_classes) {
|
||||
std::vector<NiggliClass> niggli_class_selected;
|
||||
if (ngc_id != -1)
|
||||
niggli_class_selected = {niggli_classes[ngc_id]};
|
||||
else
|
||||
niggli_class_selected = niggli_classes;
|
||||
|
||||
for (const auto &c: niggli_class_selected) {
|
||||
if (c.type == 1 && uc_reduced.beta >= 90 - angle_tolerance )
|
||||
continue;
|
||||
|
||||
@@ -339,6 +346,7 @@ LatticeSearchResult LatticeSearch(const CrystalLattice &L, double dist_tolerance
|
||||
.conventional = L_niggli.Multiply(c.reindex),
|
||||
.system = c.system,
|
||||
.centering = c.centering,
|
||||
.lowest_spacegroup_number = c.lowest_spacegroup_number,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -349,5 +357,19 @@ LatticeSearchResult LatticeSearch(const CrystalLattice &L, double dist_tolerance
|
||||
.conventional = L,
|
||||
.system = gemmi::CrystalSystem::Triclinic,
|
||||
.centering = 'P',
|
||||
.lowest_spacegroup_number = 1,
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<LatticeSearchResult> LatticeCandidates(const CrystalLattice &L, double dist_tolerance, double angle_tolerance) {
|
||||
std::vector<LatticeSearchResult> result;
|
||||
int n_possible_niggli_classes = 44;
|
||||
|
||||
for (int id = 1; id <= n_possible_niggli_classes; id++) {
|
||||
LatticeSearchResult lattice_found = LatticeSearch(L, dist_tolerance, angle_tolerance, id);
|
||||
if (lattice_found.niggli_class != 44)
|
||||
result.push_back(lattice_found);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -16,8 +16,11 @@ struct LatticeSearchResult {
|
||||
CrystalLattice conventional;
|
||||
gemmi::CrystalSystem system = gemmi::CrystalSystem::Triclinic;
|
||||
char centering = 'P'; // 'P','A','B','C','I','F','R'
|
||||
int lowest_spacegroup_number = 1;
|
||||
};
|
||||
|
||||
LatticeSearchResult LatticeSearch(const CrystalLattice& L, double dist_tolerance = 0.03, double angle_tolerance_deg = 3);
|
||||
LatticeSearchResult LatticeSearch(const CrystalLattice& L, double dist_tolerance = 0.03, double angle_tolerance_deg = 3, int ngc_id = -1);
|
||||
|
||||
std::vector<LatticeSearchResult> LatticeCandidates(const CrystalLattice& L, double dist_tolerance = 0.03, double angle_tolerance_deg = 3);
|
||||
|
||||
#endif //JFJOCH_LATTICESEARCH_H
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
@@ -17,6 +18,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "../../common/ResolutionShells.h"
|
||||
#include "../../common/Logger.h"
|
||||
|
||||
namespace {
|
||||
struct HKLKey {
|
||||
@@ -235,14 +237,113 @@ namespace {
|
||||
int img_id = 0;
|
||||
int hkl_slot = -1;
|
||||
double sigma = 0.0;
|
||||
mutable bool selected = true;
|
||||
};
|
||||
|
||||
struct CorrectedObs {
|
||||
int hkl_slot;
|
||||
double I_corr;
|
||||
double sigma_corr;
|
||||
double weight;
|
||||
};
|
||||
|
||||
void select_reflections_by_quasi_random(const std::vector<ObsRef> &obs,
|
||||
const ScaleMergeOptions &opt,
|
||||
std::vector<bool> &hkl_selected,
|
||||
Logger &logger) {
|
||||
float stat_d_min = std::numeric_limits<float>::max();
|
||||
float stat_d_max = 0.0f;
|
||||
|
||||
const gemmi::SpaceGroup &sg = *opt.space_group;
|
||||
const gemmi::GroupOps gops = sg.operations();
|
||||
int n_operator = gops.order();
|
||||
|
||||
struct HKLStats {
|
||||
int n = 0;
|
||||
float d = std::numeric_limits<float>::max();
|
||||
int shell_id = 0;
|
||||
};
|
||||
const int nhkl = static_cast<int>(hkl_selected.size());
|
||||
std::vector<HKLStats> per_hkl(nhkl);
|
||||
int reflection_above_cutoff = 0;
|
||||
|
||||
for (const auto &o: obs) {
|
||||
if (o.r->I / o.r->sigma < opt.filter_sigma_cutoff) {
|
||||
o.selected = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto d = o.r->d;
|
||||
reflection_above_cutoff += 1;
|
||||
if (std::isfinite(d) && d > 0.0f) {
|
||||
if (opt.d_min_limit_A > 0.0 && d < static_cast<float>(opt.d_min_limit_A))
|
||||
continue;
|
||||
stat_d_min = std::min(stat_d_min, d);
|
||||
stat_d_max = std::max(stat_d_max, d);
|
||||
auto &hs = per_hkl[o.hkl_slot];
|
||||
hs.n += 1;
|
||||
hs.d = d;
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("Reflections of I/sigma > {} : {}", opt.filter_sigma_cutoff, reflection_above_cutoff);
|
||||
if (reflection_above_cutoff < opt.filter_min_per_bin * opt.filter_n_resolution_bins) {
|
||||
logger.Info("No additional selection applied before scaling");
|
||||
return;
|
||||
}
|
||||
|
||||
if (stat_d_min < stat_d_max && stat_d_min > 0.0f) {
|
||||
const float d_min_pad = stat_d_min * 0.999f;
|
||||
const float d_max_pad = stat_d_max * 1.001f;
|
||||
ResolutionShells scaling_shells(d_min_pad, d_max_pad, opt.filter_n_resolution_bins);
|
||||
|
||||
for (int h = 0; h < nhkl; ++h) {
|
||||
const auto d = per_hkl[h].d;
|
||||
if (std::isfinite(d) && d > 0.0f) {
|
||||
if (opt.d_min_limit_A > 0.0 && d < static_cast<float>(opt.d_min_limit_A))
|
||||
continue;
|
||||
auto s = scaling_shells.GetShell(d);
|
||||
if (s.has_value())
|
||||
per_hkl[h].shell_id = s.value();
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulators per shell
|
||||
struct ShellAccum {
|
||||
int obs_unique = 0;
|
||||
int obs_total = 0;
|
||||
bool selected = true;
|
||||
};
|
||||
std::vector<ShellAccum> shell_acc(opt.filter_n_resolution_bins);
|
||||
|
||||
for (int h = 0; h < nhkl; ++h) {
|
||||
auto &sa = shell_acc[per_hkl[h].shell_id];
|
||||
if (sa.obs_unique * n_operator > opt.filter_min_per_bin * 1.2 || sa.obs_total > opt.filter_max_per_bin)
|
||||
hkl_selected[h] = false;
|
||||
else
|
||||
sa.obs_unique += 1;
|
||||
sa.obs_total += per_hkl[h].n;
|
||||
}
|
||||
|
||||
const auto shell_min_res = scaling_shells.GetShellMinRes();
|
||||
|
||||
logger.Info("| d-mean | n_refl_uni | n_refl_tot | selection |");
|
||||
for (int n=0; n < opt.filter_n_resolution_bins; ++n) {
|
||||
if (shell_acc[n].obs_unique * n_operator < opt.filter_min_per_bin)
|
||||
shell_acc[n].selected = false;
|
||||
if (shell_min_res[n] <= 0.0f) continue;
|
||||
logger.Info("| {:6.3f} | {:10d} | {:10d} | {:9d} |",
|
||||
shell_min_res[n], shell_acc[n].obs_unique, shell_acc[n].obs_total, shell_acc[n].selected);
|
||||
}
|
||||
|
||||
for (int h = 0; h < nhkl; ++h) {
|
||||
auto &sa = shell_acc[per_hkl[h].shell_id];
|
||||
if (!sa.selected)
|
||||
hkl_selected[h] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scale(const ScaleMergeOptions &opt,
|
||||
std::vector<double> &g,
|
||||
std::vector<double> &mosaicity,
|
||||
@@ -250,10 +351,13 @@ namespace {
|
||||
const std::vector<uint8_t> &image_slot_used,
|
||||
bool rotation_crystallography,
|
||||
size_t nhkl,
|
||||
const std::vector<ObsRef> &obs) {
|
||||
const std::vector<ObsRef> &obs,
|
||||
bool selection,
|
||||
Logger &logger) {
|
||||
ceres::Problem problem;
|
||||
|
||||
std::vector<double> Itrue(nhkl, 0.0);
|
||||
std::vector<bool> hkl_selected(nhkl, true);
|
||||
|
||||
// Initialize Itrue from per-HKL median of observed intensities
|
||||
{
|
||||
@@ -277,8 +381,12 @@ namespace {
|
||||
|
||||
double wedge = opt.wedge_deg.value_or(0.0);
|
||||
|
||||
if (selection) select_reflections_by_quasi_random(obs, opt, hkl_selected, logger);
|
||||
|
||||
std::vector<bool> is_valid_hkl_slot(nhkl, false);
|
||||
for (const auto &o: obs) {
|
||||
if (!o.selected) continue;
|
||||
if (!hkl_selected[o.hkl_slot]) continue;
|
||||
switch (opt.partiality_model) {
|
||||
case ScaleMergeOptions::PartialityModel::Rotation: {
|
||||
auto *cost = new ceres::AutoDiffCostFunction<IntensityRotResidual, 1, 1, 1, 1, 1>(
|
||||
@@ -398,8 +506,9 @@ namespace {
|
||||
options.function_tolerance = 1e-4;
|
||||
|
||||
ceres::Solver::Summary summary;
|
||||
logger.Info("Now start the ceres-solver with residual blocks: {}", problem.NumResidualBlocks());
|
||||
ceres::Solve(options, &problem, &summary);
|
||||
std::cout << summary.FullReport() << std::endl;
|
||||
logger.Info(summary.FullReport());
|
||||
}
|
||||
|
||||
void merge(size_t nhkl, ScaleMergeResult &out, const std::vector<CorrectedObs> &corr_obs) {
|
||||
@@ -408,18 +517,19 @@ namespace {
|
||||
struct HKLAccum {
|
||||
double sum_wI = 0.0;
|
||||
double sum_w = 0.0;
|
||||
double sum_wI2 = 0.0;
|
||||
int count = 0;
|
||||
// double sum_wI2 = 0.0;
|
||||
double sum_wsigma2 = 0.0;
|
||||
// int count = 0;
|
||||
};
|
||||
std::vector<HKLAccum> accum(nhkl);
|
||||
|
||||
for (const auto &co: corr_obs) {
|
||||
const double w = 1.0 / (co.sigma_corr * co.sigma_corr);
|
||||
// const double w = 1.0 / (co.sigma_corr * co.sigma_corr);
|
||||
const double w = co.weight / (co.sigma_corr * co.sigma_corr);
|
||||
auto &a = accum[co.hkl_slot];
|
||||
a.sum_wI += w * co.I_corr;
|
||||
a.sum_w += w;
|
||||
a.sum_wI2 += w * co.I_corr * co.I_corr;
|
||||
a.count++;
|
||||
a.sum_wsigma2 += w * w * co.sigma_corr * co.sigma_corr;
|
||||
}
|
||||
|
||||
for (int h = 0; h < nhkl; ++h) {
|
||||
@@ -428,15 +538,7 @@ namespace {
|
||||
continue;
|
||||
|
||||
out.merged[h].I = a.sum_wI / a.sum_w;
|
||||
const double sigma_stat = std::sqrt(1.0 / a.sum_w);
|
||||
|
||||
if (a.count >= 2) {
|
||||
const double var_internal = a.sum_wI2 - (a.sum_wI * a.sum_wI) / a.sum_w;
|
||||
const double sigma_int = std::sqrt(var_internal / (a.sum_w * (a.count - 1)));
|
||||
out.merged[h].sigma = std::max(sigma_stat, sigma_int);
|
||||
} else {
|
||||
out.merged[h].sigma = sigma_stat;
|
||||
}
|
||||
out.merged[h].sigma = std::sqrt(a.sum_wsigma2) / a.sum_w;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,7 +745,8 @@ namespace {
|
||||
corr_obs.push_back({
|
||||
o.hkl_slot,
|
||||
static_cast<double>(r.I) / correction,
|
||||
o.sigma / correction
|
||||
o.sigma / correction,
|
||||
1 / correction,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -694,6 +797,7 @@ namespace {
|
||||
o.img_id = img_id;
|
||||
o.hkl_slot = hkl_slot;
|
||||
o.sigma = sigma;
|
||||
o.selected = true;
|
||||
obs.push_back(o);
|
||||
}
|
||||
}
|
||||
@@ -703,10 +807,14 @@ namespace {
|
||||
|
||||
ScaleMergeResult ScaleAndMergeReflectionsCeres(const std::vector<std::vector<Reflection> > &observations,
|
||||
const ScaleMergeOptions &opt) {
|
||||
|
||||
Logger logger("ScaleAndMergeReflections");
|
||||
|
||||
if (opt.image_cluster <= 0)
|
||||
throw std::invalid_argument("image_cluster must be positive");
|
||||
|
||||
const bool rotation_crystallography = opt.wedge_deg.has_value();
|
||||
const bool selection = opt.selection;
|
||||
|
||||
size_t nrefl = 0;
|
||||
for (const auto &i: observations)
|
||||
@@ -744,7 +852,8 @@ ScaleMergeResult ScaleAndMergeReflectionsCeres(const std::vector<std::vector<Ref
|
||||
}
|
||||
}
|
||||
|
||||
scale(opt, g, mosaicity, R_sq, image_slot_used, rotation_crystallography, nhkl, obs);
|
||||
logger.Info("Now scale the reflections: {}", nrefl);
|
||||
scale(opt, g, mosaicity, R_sq, image_slot_used, rotation_crystallography, nhkl, obs, selection, logger);
|
||||
|
||||
ScaleMergeResult out;
|
||||
|
||||
|
||||
@@ -50,6 +50,12 @@ struct ScaleMergeOptions {
|
||||
|
||||
bool refine_wedge = false;
|
||||
|
||||
bool selection = true;
|
||||
double filter_sigma_cutoff = 1.0;
|
||||
int filter_min_per_bin = 10000; // needs optimization
|
||||
int filter_max_per_bin = 80000;
|
||||
int filter_n_resolution_bins = 20;
|
||||
|
||||
enum class PartialityModel {Fixed, Rotation, Unity, Still} partiality_model = PartialityModel::Fixed;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
@@ -394,8 +395,16 @@ SearchSpaceGroupResult SearchSpaceGroup(
|
||||
if (merged.empty())
|
||||
return result;
|
||||
|
||||
std::vector<gemmi::SpaceGroup> candidates;
|
||||
|
||||
const auto lookup = BuildMergedLookup(merged, opt);
|
||||
const auto candidates = EnumerateCandidateSpaceGroups(opt.crystal_system, opt.centering);
|
||||
|
||||
if (opt.candidate_space_groups.has_value())
|
||||
candidates = opt.candidate_space_groups.value();
|
||||
else
|
||||
candidates = EnumerateCandidateSpaceGroups(opt.crystal_system, opt.centering);
|
||||
|
||||
std::cout << "No. of SG-search candidates: " << candidates.size() << std::endl;
|
||||
|
||||
for (const auto& sg : candidates) {
|
||||
SpaceGroupCandidateScore score{.space_group = sg};
|
||||
|
||||
@@ -91,6 +91,8 @@ struct SearchSpaceGroupOptions {
|
||||
// Acceptance thresholds for absent/allowed <I/sig> ratios.
|
||||
double max_absent_to_allowed_i_over_sigma_ratio = 0.20;
|
||||
double max_absent_to_allowed_i_over_sigma_ratio_in_any_bin = 0.50;
|
||||
|
||||
std::optional<std::vector<gemmi::SpaceGroup>> candidate_space_groups;
|
||||
};
|
||||
|
||||
struct SearchSpaceGroupResult {
|
||||
|
||||
+178
-66
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include "../compression/JFJochCompressor.h"
|
||||
#include "../image_analysis/scale_merge/FrenchWilson.h"
|
||||
#include "../image_analysis/scale_merge/SearchSpaceGroup.h"
|
||||
#include "../image_analysis/lattice_search/LatticeSearch.h"
|
||||
#include "../image_analysis/WriteMmcif.h"
|
||||
|
||||
void print_usage(Logger &logger) {
|
||||
@@ -44,12 +46,15 @@ void print_usage(Logger &logger) {
|
||||
logger.Info(" -d<num> High resolution limit for spot finding (default: 1.5)");
|
||||
logger.Info(" -D<num> High resolution limit for scaling/merging (default: 0.0; no limit)");
|
||||
logger.Info(" -S<num> Space group number");
|
||||
logger.Info(" -M Scale and merge (refine mosaicity) and write scaled.hkl + image.dat");
|
||||
logger.Info(" -M[num] Scale and merge (refine mosaicity) and write scaled.hkl + image.dat, unless indexing rate is below threshold (default: 0.5)");
|
||||
logger.Info(" -P<txt> Partiality refinement fixed|rot|unity (default: fixed)");
|
||||
logger.Info(" -A Anomalous mode (don't merge Friedel pairs)");
|
||||
logger.Info(" -C<cell> Fix reference unit cell: -C\"a,b,c,alpha,beta,gamma\" (comma-separated, no spaces; quotes optional)");
|
||||
logger.Info(" -c<num> Max spot count (default: 250)");
|
||||
logger.Info(" -W HDF5 file with analysis results is written");
|
||||
logger.Info(" -W<txt> HDF5 file with analysis results is written. 'l' or 'light' deactivates image-output");
|
||||
logger.Info(" -T<num> Noise sigma level for spot finding (default: 3.0)");
|
||||
logger.Info(" -m<num> Min unique reflections per bin used for scaling. Use all when no value is specified (default: 10000)");
|
||||
logger.Info(" -w Refine wedge in scaling (default: false)");
|
||||
}
|
||||
|
||||
void trim_in_place(std::string& t) {
|
||||
@@ -60,6 +65,50 @@ void trim_in_place(std::string& t) {
|
||||
t = t.substr(b, e - b);
|
||||
};
|
||||
|
||||
void print_statistics(Logger &logger, const MergeStatistics &stats) {
|
||||
logger.Info("");
|
||||
logger.Info(" {:>8s} {:>8s} {:>8s} {:>8s} {:>8s} {:>10s}",
|
||||
"d_min", "N_obs", "N_uniq", "Rmeas", "<I/sig>", "Complete");
|
||||
logger.Info(" {:->8s} {:->8s} {:->8s} {:->8s} {:->8s} {:->10s}",
|
||||
"", "", "", "", "", "");
|
||||
for (const auto &sh: stats.shells) {
|
||||
if (sh.unique_reflections == 0)
|
||||
continue;
|
||||
std::string compl_str = (sh.completeness > 0.0)
|
||||
? fmt::format("{:8.1f}%", sh.completeness * 100.0)
|
||||
: " N/A";
|
||||
logger.Info(" {:8.2f} {:8d} {:8d} {:8.3f}% {:8.1f} {:>10s}",
|
||||
sh.d_min, sh.total_observations, sh.unique_reflections,
|
||||
sh.rmeas * 100, sh.mean_i_over_sigma, compl_str);
|
||||
}
|
||||
{
|
||||
const auto &ov = stats.overall;
|
||||
logger.Info(" {:->8s} {:->8s} {:->8s} {:->8s} {:->8s} {:->10s}",
|
||||
"", "", "", "", "", "");
|
||||
std::string compl_str = (ov.completeness > 0.0)
|
||||
? fmt::format("{:8.1f}%", ov.completeness * 100.0)
|
||||
: " N/A";
|
||||
logger.Info(" {:>8s} {:8d} {:8d} {:8.3f}% {:8.1f} {:>10s}",
|
||||
"Overall", ov.total_observations, ov.unique_reflections,
|
||||
ov.rmeas * 100, ov.mean_i_over_sigma, compl_str);
|
||||
}
|
||||
logger.Info("");
|
||||
}
|
||||
|
||||
void print_unitcell(Logger &logger, const CrystalLattice &lattice, bool print_vector) {
|
||||
auto vec0 = lattice.Vec0();
|
||||
auto vec1 = lattice.Vec1();
|
||||
auto vec2 = lattice.Vec2();
|
||||
auto uc = lattice.GetUnitCell();
|
||||
if (print_vector) {
|
||||
logger.Info("Lattice vec0: {:.3f}, {:.3f}, {:.3f}", vec0.x, vec0.y, vec0.z);
|
||||
logger.Info("Lattice vec1: {:.3f}, {:.3f}, {:.3f}", vec1.x, vec1.y, vec1.z);
|
||||
logger.Info("Lattice vec2: {:.3f}, {:.3f}, {:.3f}", vec2.x, vec2.y, vec2.z);
|
||||
}
|
||||
logger.Info("Lattice: a={:.2f} b={:.2f} c={:.2f} alpha={:.2f} beta={:.2f} gamma={:.2f}",
|
||||
uc.a, uc.b, uc.c, uc.alpha, uc.beta, uc.gamma);
|
||||
}
|
||||
|
||||
std::optional<UnitCell> parse_unit_cell_arg(const char* arg) {
|
||||
if (!arg)
|
||||
return std::nullopt;
|
||||
@@ -132,8 +181,14 @@ int main(int argc, char **argv) {
|
||||
std::optional<int> space_group_number;
|
||||
std::optional<UnitCell> fixed_reference_unit_cell;
|
||||
bool write_output = false;
|
||||
bool write_output_noimage = false;
|
||||
bool filtering = true;
|
||||
std::optional<int16_t> filter_min_per_bin;
|
||||
std::optional<int64_t> max_spot_count_override;
|
||||
float sigma_spot_finding = 3.0;
|
||||
std::optional<float> merging_threshold;
|
||||
IndexingAlgorithmEnum indexing_algorithm = IndexingAlgorithmEnum::Auto;
|
||||
bool refine_wedge = false;
|
||||
|
||||
ScaleMergeOptions::PartialityModel partiality_model = ScaleMergeOptions::PartialityModel::Fixed;
|
||||
|
||||
@@ -146,7 +201,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "o:N:s:e:vc:R::FX:xd:S:MP:AD:C:W")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "o:N:s:e:vc:R::FX:xd:S:M::P:AD:C:T:W:m::w")) != -1) {
|
||||
switch (opt) {
|
||||
case 'o':
|
||||
output_prefix = optarg;
|
||||
@@ -162,6 +217,10 @@ int main(int argc, char **argv) {
|
||||
break;
|
||||
case 'W':
|
||||
write_output = true;
|
||||
if (strcmp(optarg, "light") == 0 || strcmp(optarg, "l") == 0) {
|
||||
write_output_noimage = true;
|
||||
logger.Warning("Image data will not be saved.");
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
@@ -204,6 +263,14 @@ int main(int argc, char **argv) {
|
||||
case 'x':
|
||||
refine_beam_center = false;
|
||||
break;
|
||||
case 'm':
|
||||
filtering = false;
|
||||
if (optarg)
|
||||
filter_min_per_bin = atoi(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
refine_wedge = true;
|
||||
break;
|
||||
case 'D':
|
||||
d_min_scale_merge = atof(optarg);
|
||||
logger.Info("High resolution limit for scaling/merging set to {:.2f} A", d_min_spot_finding);
|
||||
@@ -213,10 +280,15 @@ int main(int argc, char **argv) {
|
||||
break;
|
||||
case 'M':
|
||||
run_scaling = true;
|
||||
if (optarg) merging_threshold = atof(optarg);
|
||||
break;
|
||||
case 'A':
|
||||
anomalous_mode = true;
|
||||
break;
|
||||
case 'T':
|
||||
sigma_spot_finding = atof(optarg);
|
||||
logger.Info("Noise threshold level for spot finding set to {:.2f} sigma", sigma_spot_finding);
|
||||
break;
|
||||
case 'C': {
|
||||
auto uc = parse_unit_cell_arg(optarg);
|
||||
if (!uc.has_value()) {
|
||||
@@ -296,8 +368,11 @@ int main(int argc, char **argv) {
|
||||
experiment.OverwriteExistingFiles(true);
|
||||
experiment.PolarizationFactor(0.99);
|
||||
|
||||
if (fixed_reference_unit_cell.has_value())
|
||||
if (fixed_reference_unit_cell.has_value()) {
|
||||
experiment.SetUnitCell(*fixed_reference_unit_cell);
|
||||
} else {
|
||||
experiment.SetUnitCell({});
|
||||
}
|
||||
|
||||
if (max_spot_count_override.has_value()) {
|
||||
experiment.MaxSpotCount(max_spot_count_override.value());
|
||||
@@ -308,6 +383,8 @@ int main(int argc, char **argv) {
|
||||
IndexingSettings indexing_settings;
|
||||
indexing_settings.Algorithm(indexing_algorithm);
|
||||
indexing_settings.RotationIndexing(rotation_indexing);
|
||||
if (rotation_indexing)
|
||||
logger.Info("Rotation indexing is activated.");
|
||||
if (rotation_indexing_range.has_value())
|
||||
indexing_settings.RotationIndexingMinAngularRange_deg(rotation_indexing_range.value());
|
||||
|
||||
@@ -317,10 +394,27 @@ int main(int argc, char **argv) {
|
||||
indexing_settings.GeomRefinementAlgorithm(GeomRefinementAlgorithmEnum::None);
|
||||
experiment.ImportIndexingSettings(indexing_settings);
|
||||
|
||||
switch (experiment.GetIndexingAlgorithm()) {
|
||||
case IndexingAlgorithmEnum::FFBIDX:
|
||||
logger.Info("Indexer used: FFBIDX");
|
||||
break;
|
||||
case IndexingAlgorithmEnum::FFTW:
|
||||
logger.Info("Indexer used: FFTW");
|
||||
break;
|
||||
case IndexingAlgorithmEnum::FFT:
|
||||
logger.Info("Indexer used: FFT (CUDA)");
|
||||
break;
|
||||
case IndexingAlgorithmEnum::None:
|
||||
logger.Warning("Indexer not defined!");
|
||||
return 0;
|
||||
default: ;
|
||||
}
|
||||
|
||||
SpotFindingSettings spot_settings;
|
||||
spot_settings.enable = true;
|
||||
spot_settings.indexing = true;
|
||||
spot_settings.high_resolution_limit = d_min_spot_finding;
|
||||
spot_settings.signal_to_noise_threshold = sigma_spot_finding;
|
||||
if (d_min_scale_merge > 0)
|
||||
spot_settings.high_resolution_limit = d_min_spot_finding;
|
||||
|
||||
@@ -396,6 +490,10 @@ int main(int argc, char **argv) {
|
||||
compressed_buffer.resize(MaxCompressedSize(experiment.GetCompressionAlgorithm(),
|
||||
experiment.GetPixelsNum(),
|
||||
experiment.GetByteDepthImage()));
|
||||
auto size = compressor.Compress(compressed_buffer.data(),
|
||||
compressed_buffer.data(),
|
||||
experiment.GetPixelsNum(),
|
||||
sizeof(uint8_t));
|
||||
|
||||
// Thread-local analysis resources
|
||||
MXAnalysisWithoutFPGA analysis(experiment, mapping, pixel_mask, indexer);
|
||||
@@ -446,10 +544,11 @@ int main(int argc, char **argv) {
|
||||
auto image_end_time = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<float> image_duration = image_end_time - image_start_time;
|
||||
|
||||
auto size = compressor.Compress(compressed_buffer.data(),
|
||||
img->Image().data(),
|
||||
experiment.GetPixelsNum(),
|
||||
sizeof(int32_t));
|
||||
if (!write_output_noimage)
|
||||
size = compressor.Compress(compressed_buffer.data(),
|
||||
img->Image().data(),
|
||||
experiment.GetPixelsNum(),
|
||||
sizeof(int32_t));
|
||||
|
||||
msg.image = CompressedImage(compressed_buffer.data(),
|
||||
size, experiment.GetXPixelsNum(),
|
||||
@@ -482,7 +581,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Progress log
|
||||
if (current_idx_offset > 0 && current_idx_offset % 100 == 0) {
|
||||
if ((current_idx_offset > 0 && (current_idx_offset+1) % 100 == 0) || image_idx == end_image - 1) {
|
||||
std::optional<float> indexing_rate;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(plots_mutex);
|
||||
@@ -491,11 +590,21 @@ int main(int argc, char **argv) {
|
||||
|
||||
if (indexing_rate.has_value()) {
|
||||
logger.Info("Processed {} / {} images (indexing rate {:.1f}%)",
|
||||
current_idx_offset, images_to_process,
|
||||
current_idx_offset+1, images_to_process,
|
||||
indexing_rate.value() * 100.0f);
|
||||
} else {
|
||||
logger.Info("Processed {} / {} images (indexing rate N/A)",
|
||||
current_idx_offset, images_to_process);
|
||||
current_idx_offset+1, images_to_process);
|
||||
}
|
||||
if (image_idx == end_image - 1) {
|
||||
if (!merging_threshold.has_value()) merging_threshold = 0.5f;
|
||||
if (!indexing_rate.has_value()) {
|
||||
run_scaling = false;
|
||||
} else if (indexing_rate.value() < merging_threshold) {
|
||||
run_scaling = false;
|
||||
logger.Warning("Not proceed to scale and merge with lower indexing rate: {:.1f}%",
|
||||
indexing_rate.value()*100.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -544,10 +653,11 @@ int main(int argc, char **argv) {
|
||||
.niggli_class = rotation_indexer_ret->search_result.niggli_class,
|
||||
.crystal_system = rotation_indexer_ret->search_result.system
|
||||
};
|
||||
logger.Info("Rotation Indexing found lattice");
|
||||
logger.Info("Rotation Indexing found lattice: {} {}",
|
||||
gemmi::crystal_system_str(rotation_indexer_ret->search_result.system),
|
||||
rotation_indexer_ret->search_result.centering);
|
||||
}
|
||||
|
||||
// --- Optional: run scaling (mosaicity refinement) on accumulated reflections ---
|
||||
// --- Optional: run scaling (mosaicity refinement) on accumulated reflections ---
|
||||
if (run_scaling) {
|
||||
logger.Info("Running scaling (mosaicity refinement) ...");
|
||||
@@ -558,6 +668,16 @@ int main(int argc, char **argv) {
|
||||
scale_opts.max_solver_time_s = 240.0; // generous cutoff for now
|
||||
scale_opts.merge_friedel = !anomalous_mode;
|
||||
scale_opts.d_min_limit_A = d_min_scale_merge.value_or(0.0);
|
||||
scale_opts.refine_wedge = refine_wedge;
|
||||
// scale_opts.min_partiality_for_merge = 0.4;
|
||||
if (filter_min_per_bin.has_value()) {
|
||||
scale_opts.selection = true;
|
||||
scale_opts.filter_min_per_bin = filter_min_per_bin.value();
|
||||
} else {
|
||||
scale_opts.selection = filtering;
|
||||
}
|
||||
if (rotation_indexing)
|
||||
scale_opts.filter_min_per_bin = scale_opts.filter_min_per_bin * 0.25;
|
||||
|
||||
const bool fixed_space_group = space_group || experiment.GetGemmiSpaceGroup().has_value();
|
||||
|
||||
@@ -565,27 +685,48 @@ int main(int argc, char **argv) {
|
||||
scale_opts.space_group = *space_group;
|
||||
else
|
||||
scale_opts.space_group = experiment.GetGemmiSpaceGroup();
|
||||
if (scale_opts.space_group->number == 0) scale_opts.space_group = *gemmi::find_spacegroup_by_number(1);
|
||||
|
||||
logger.Info("Starting SG-no.: {}", scale_opts.space_group->number);
|
||||
auto scale_start = std::chrono::steady_clock::now();
|
||||
auto scale_result = indexer.ScaleRotationData(scale_opts);
|
||||
auto scale_end = std::chrono::steady_clock::now();
|
||||
double scale_time = std::chrono::duration<double>(scale_end - scale_start).count();
|
||||
|
||||
if (scale_result) print_statistics(logger, scale_result->statistics);
|
||||
// if (scale_opts.wedge_deg.has_value()) logger.Info("Refined wedge: {:.3f}", scale_opts.wedge_deg.value());
|
||||
|
||||
if (scale_result && !fixed_space_group) {
|
||||
logger.Info("Searching for space group from P1-merged reflections ...");
|
||||
|
||||
std::vector<gemmi::SpaceGroup> candidates_preset;
|
||||
if (rotation_indexer_ret.has_value()) {
|
||||
std::vector<LatticeSearchResult> lattice_candidates = LatticeCandidates(rotation_indexer_ret->lattice);
|
||||
for (const auto &lc: lattice_candidates) {
|
||||
logger.Info("Possible lattice : {} {} ({})", gemmi::crystal_system_str(lc.system), lc.centering, lc.lowest_spacegroup_number);
|
||||
print_unitcell(logger, lc.conventional, false);
|
||||
candidates_preset.push_back(*gemmi::find_spacegroup_by_number(lc.lowest_spacegroup_number));
|
||||
}
|
||||
candidates_preset.push_back(*gemmi::find_spacegroup_by_number(1));
|
||||
}
|
||||
|
||||
SearchSpaceGroupOptions sg_opts;
|
||||
sg_opts.crystal_system.reset();
|
||||
sg_opts.centering = '\0';
|
||||
sg_opts.merge_friedel = !anomalous_mode;
|
||||
sg_opts.d_min_limit_A = d_min_scale_merge.value_or(0.0);
|
||||
sg_opts.min_i_over_sigma = 0.0;
|
||||
sg_opts.min_i_over_sigma = 4.0; // 0.0; follows the DIALS's default
|
||||
sg_opts.min_operator_cc = 0.80;
|
||||
sg_opts.min_pairs_per_operator = 20;
|
||||
sg_opts.min_total_compared = 100;
|
||||
sg_opts.test_systematic_absences = true;
|
||||
if (~candidates_preset.empty())
|
||||
sg_opts.candidate_space_groups = candidates_preset;
|
||||
|
||||
auto sg_search_start = std::chrono::steady_clock::now();
|
||||
const auto sg_search = SearchSpaceGroup(scale_result->merged, sg_opts);
|
||||
auto sg_search_end = std::chrono::steady_clock::now();
|
||||
double sg_search_time = std::chrono::duration<double>(sg_search_end - sg_search_start).count();
|
||||
|
||||
logger.Info("");
|
||||
{
|
||||
@@ -599,17 +740,24 @@ int main(int argc, char **argv) {
|
||||
logger.Info("");
|
||||
|
||||
if (sg_search.best_space_group.has_value()) {
|
||||
logger.Info("Re-running scaling in detected space group {}", sg_search.best_space_group->short_name());
|
||||
logger.Info("SG-search wall-clock time: {:.2f} s", sg_search_time);
|
||||
if (sg_search.best_space_group->number != 0) {
|
||||
if (sg_search.best_space_group->number != scale_opts.space_group->number) {
|
||||
logger.Info("Re-running scaling in detected space group {}", sg_search.best_space_group->short_name());
|
||||
|
||||
scale_opts.space_group = *sg_search.best_space_group;
|
||||
scale_opts.space_group = *sg_search.best_space_group;
|
||||
|
||||
auto rescale_start = std::chrono::steady_clock::now();
|
||||
auto refined_scale_result = indexer.ScaleRotationData(scale_opts);
|
||||
auto rescale_end = std::chrono::steady_clock::now();
|
||||
auto rescale_start = std::chrono::steady_clock::now();
|
||||
auto refined_scale_result = indexer.ScaleRotationData(scale_opts);
|
||||
auto rescale_end = std::chrono::steady_clock::now();
|
||||
|
||||
if (refined_scale_result) {
|
||||
scale_result = std::move(refined_scale_result);
|
||||
scale_time += std::chrono::duration<double>(rescale_end - rescale_start).count();
|
||||
if (refined_scale_result) {
|
||||
scale_result = std::move(refined_scale_result);
|
||||
scale_time += std::chrono::duration<double>(rescale_end - rescale_start).count();
|
||||
}
|
||||
} else {
|
||||
logger.Info("No space group update indicated.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.Warning("No space group accepted; keeping P1-merged result");
|
||||
@@ -623,36 +771,7 @@ int main(int argc, char **argv) {
|
||||
scale_time, scale_result->merged.size());
|
||||
|
||||
// Print resolution-shell statistics table
|
||||
{
|
||||
const auto &stats = scale_result->statistics;
|
||||
logger.Info("");
|
||||
logger.Info(" {:>8s} {:>8s} {:>8s} {:>8s} {:>8s} {:>10s}",
|
||||
"d_min", "N_obs", "N_uniq", "Rmeas", "<I/sig>", "Complete");
|
||||
logger.Info(" {:->8s} {:->8s} {:->8s} {:->8s} {:->8s} {:->10s}",
|
||||
"", "", "", "", "", "");
|
||||
for (const auto &sh: stats.shells) {
|
||||
if (sh.unique_reflections == 0)
|
||||
continue;
|
||||
std::string compl_str = (sh.completeness > 0.0)
|
||||
? fmt::format("{:8.1f}%", sh.completeness * 100.0)
|
||||
: " N/A";
|
||||
logger.Info(" {:8.2f} {:8d} {:8d} {:8.3f}% {:8.1f} {:>10s}",
|
||||
sh.d_min, sh.total_observations, sh.unique_reflections,
|
||||
sh.rmeas * 100, sh.mean_i_over_sigma, compl_str);
|
||||
}
|
||||
{
|
||||
const auto &ov = stats.overall;
|
||||
logger.Info(" {:->8s} {:->8s} {:->8s} {:->8s} {:->8s} {:->10s}",
|
||||
"", "", "", "", "", "");
|
||||
std::string compl_str = (ov.completeness > 0.0)
|
||||
? fmt::format("{:8.1f}%", ov.completeness * 100.0)
|
||||
: " N/A";
|
||||
logger.Info(" {:>8s} {:8d} {:8d} {:8.3f}% {:8.1f} {:>10s}",
|
||||
"Overall", ov.total_observations, ov.unique_reflections,
|
||||
ov.rmeas * 100, ov.mean_i_over_sigma, compl_str);
|
||||
}
|
||||
logger.Info("");
|
||||
}
|
||||
print_statistics(logger, scale_result->statistics);
|
||||
|
||||
{
|
||||
const std::string img_path = output_prefix + "_image.dat";
|
||||
@@ -698,6 +817,8 @@ int main(int argc, char **argv) {
|
||||
cif_meta.unit_cell = rotation_indexer_ret->lattice.GetUnitCell();
|
||||
} else if (experiment.GetUnitCell().has_value()) {
|
||||
cif_meta.unit_cell = experiment.GetUnitCell().value();
|
||||
} else {
|
||||
logger.Warning("No UnitCell output");
|
||||
}
|
||||
|
||||
if (scale_opts.space_group.has_value()) {
|
||||
@@ -745,12 +866,11 @@ int main(int argc, char **argv) {
|
||||
double frame_rate = static_cast<double>(images_to_process) / processing_time;
|
||||
|
||||
logger.Info("");
|
||||
logger.Info("Processing time: {:.2f} s", processing_time);
|
||||
logger.Info("Frame rate: {:.2f} Hz", frame_rate);
|
||||
logger.Info("Total throughput:{:.2f} MB/s", throughput_MBs);
|
||||
|
||||
logger.Info("Processing time (excl. scaling): {:8.2f} s", processing_time);
|
||||
logger.Info("Frame rate: {:8.2f} Hz", frame_rate);
|
||||
logger.Info("Total throughput: {:8.2f} MB/s", throughput_MBs);
|
||||
// Print extended stats similar to Receiver
|
||||
if (!end_msg.indexing_rate.has_value()) {
|
||||
if (end_msg.indexing_rate.has_value()) {
|
||||
logger.Info("Indexing rate: {:.2f}%", end_msg.indexing_rate.value() * 100.0);
|
||||
}
|
||||
|
||||
@@ -771,14 +891,6 @@ int main(int argc, char **argv) {
|
||||
latt = latt.Multiply(rot);
|
||||
}
|
||||
|
||||
auto vec0 = rotation_indexer_ret->lattice.Vec0();
|
||||
auto vec1 = rotation_indexer_ret->lattice.Vec1();
|
||||
auto vec2 = rotation_indexer_ret->lattice.Vec2();
|
||||
auto uc = rotation_indexer_ret->lattice.GetUnitCell();
|
||||
logger.Info("Lattice vec0: {:.3f}, {:.3f}, {:.3f}", vec0.x, vec0.y, vec0.z);
|
||||
logger.Info("Lattice vec1: {:.3f}, {:.3f}, {:.3f}", vec1.x, vec1.y, vec1.z);
|
||||
logger.Info("Lattice vec2: {:.3f}, {:.3f}, {:.3f}", vec2.x, vec2.y, vec2.z);
|
||||
logger.Info("Lattice: a={:.2f} b={:.2f} c={:.2f} alpha={:.2f} beta={:.2f} gamma={:.2f}",
|
||||
uc.a, uc.b, uc.c, uc.alpha, uc.beta, uc.gamma);
|
||||
print_unitcell(logger, rotation_indexer_ret->lattice, true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user