The per-direction peak search starts at the 10 A floor, so a lattice row shorter than that was reported at a multiple of itself past it and the cell built from such rows was an exact integer supercell. A second first-pass hypothesis at a 5 A floor (the short-axis pass) won the true cell back on frame counts, and could not reach an axis below 5 A at all. On rotation data (a scanning axis) every shortlisted row reported within three times the floor is now asked, by a direct sum over the accumulated spots, at each whole fraction of its length down to 3 A; the shortest fraction that keeps half the row's amplitude replaces it. The cells of those rows are reduced (widened: a small cell's in-plane combinations fill the standard window) and refined beside the standard cells, and ranked with them, so where a cell and its supercell index alike the smaller one is taken. Protein shortlists have no rows that short, so nothing changes for them; stills and grid scans keep the search they had. The short-axis pass, its speculative pool and its live warm-up are removed; the refined-length bound of the cell fit goes from 5 to 2.5 A. The 40 % overlap test of the candidate refinement no longer deletes a cell for its overlap with an integer multiple or sub-cell of it - one lattice contains the other, so that overlap is total by construction. Such candidates are kept after every cell the test accepts, which keep their order, so the first lattice and the extra crystals are what they were. The rotation indexer carries the refined integer supercells of the cell it chooses, and the twin-composite test asks them as well as the standing cell: once the search offers a twin domain's own cell, the coincidence lattice it used to stand on is one of those. Measured (battery subsets vs rc181-b2): a small-molecule crystal with an axis under 5 A, indexed as an index-5 supercell now gives its true cell and group; the 13 sets the short-axis pass rescued keep their verdicts; the sparse reticular twin keeps its domain and overlap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UqXYdL4qBGYRiSXSGXfCLu
43 lines
1.6 KiB
C++
43 lines
1.6 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <vector>
|
|
#include <optional>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <Eigen/Dense>
|
|
|
|
#include "../common/CrystalLattice.h"
|
|
|
|
constexpr float REFINE_CANDIDATE_SPOT_COUNT_RATIO_THRESHOLD = 0.9f;
|
|
constexpr float REFINE_CANDIDATE_VOLUME_RATIO_THRESHOLD = 1.05f;
|
|
constexpr float REFINE_CANDIDATE_OVERLAP_RATIO_THRESHOLD = 0.4f;
|
|
constexpr float REFINE_MIN_VOLUME_EPSILON = 1e-12f;
|
|
constexpr float REFINE_MIN_REFERENCE_LENGTH_EPSILON = 1e-6f;
|
|
// Max deviation (deg) of a candidate cell angle from the reference cell angle, compared after
|
|
// folding both to the acute complement (min(x,180-x)) so the obtuse/acute setting is irrelevant.
|
|
constexpr float REFINE_ANGLE_TOLERANCE_VS_REFERENCE_DEG = 10.0f;
|
|
|
|
struct RefineParameters {
|
|
int64_t viable_cell_min_spots;
|
|
float dist_tolerance_vs_reference;
|
|
bool step_down_to_reference = false;
|
|
std::optional<UnitCell> reference_unit_cell;
|
|
float min_length_A;
|
|
float max_length_A;
|
|
float min_angle_deg;
|
|
float max_angle_deg;
|
|
float indexing_tolerance;
|
|
unsigned refine_threads = 1;
|
|
};
|
|
|
|
bool IntegerRelated(const Eigen::Matrix3f &a, const Eigen::Matrix3f &b);
|
|
|
|
std::vector<CrystalLattice> Refine(const std::vector<Coord> &in_spots,
|
|
size_t nspots,
|
|
Eigen::MatrixX3<float> &oCell,
|
|
Eigen::VectorX<float> &scores,
|
|
RefineParameters &p); |