Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 14m24s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 14m32s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 14m40s
Build Packages / build:rpm (rocky8) (push) Successful in 15m8s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 15m12s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 15m25s
Build Packages / build:windows:nocuda (push) Successful in 15m47s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 15m50s
Build Packages / build:windows:cuda (push) Successful in 17m29s
Build Packages / XDS test (durin plugin) (push) Successful in 8m31s
Build Packages / Generate python client (push) Successful in 32s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 9m3s
Build Packages / Create release (push) Skipped
Build Packages / XDS test (neggia plugin) (push) Successful in 8m31s
Build Packages / Build documentation (push) Successful in 1m6s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 11m23s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 12m16s
Build Packages / build:rpm (rocky9) (push) Successful in 12m56s
Build Packages / DIALS test (push) Successful in 12m42s
Build Packages / Unit tests (push) Successful in 1h34m10s
De-novo two-pass indexing failed on large-cell / superstructure / modulated crystals (EcwtAL500 0%, EcwtCQ034 0%) and mis-handled a pseudo-symmetric one (EcwtCQ066 14%). The common cause: the choice of unit cell was made too early, on the raw pre-refinement spot fraction, which is an unreliable discriminator (a correct hexagonal cell indexes only ~13% of the un-refined accumulated spots, while a wrong larger cell can index more). Move the decision to after geometry refinement, where it is reliable: - FFTIndexer now OFFERS widened candidate cells instead of deciding. ReduceResults gains a `widen` flag: the standard path (9 shortest vectors) is unchanged; the widened path anchors the two short axes and lets the third range over all filtered vectors (+dedup) to reach the long axis of an elongated cell. FilterFFTResults takes the peak count as a parameter (30 standard, 60 widened). RunInternal appends widened candidates only when its standard best indexes poorly, so compact crystals are untouched. - RotationIndexer fully refines the top few candidates and keeps the one that indexes the most spots under its own refined geometry (IndexedFraction). Each refine is length-bounded (1.2x the found cell) so a free triclinic refine cannot drift onto a pseudo-translation / modulation supercell (CQ034's satellites). The earlier (primary) candidate is preferred: a later one is adopted only if it indexes clearly more and reasonably well in absolute terms, so a twin's noisy near-tie cannot displace it. Extra/twin lattices are only searched when the chosen cell is the FFT primary (lattice[0]), since MultiLatticeSearch's rotations are derived from that primary. - The pseudo-symmetry guard (de-novo only - a user-fixed space group is always honored) is a ratio of refined fractions: refine the primitive as triclinic and drop to P only if the constrained cell indexes less than half of it. A false promotion indexes badly under its constraint (CQ066 ratio ~0.1) while genuine higher symmetry, including R-centred, indexes comparably (Ins_H R3 ratio ~0.7) and is kept. Validated on the full /data/rotation_test battery: AL500 0->89% (C2), CQ034 0->99%, CQ066 14->93% (ISa 7.2->13.7); the other 15 crystals keep their exact cell, space group, indexing rate and ISa (no regressions). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
1.9 KiB
C++
54 lines
1.9 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include "Indexer.h"
|
|
#include "../../common/UnitCell.h"
|
|
#include "../../common/Coord.h"
|
|
#include "../../common/CrystalLattice.h"
|
|
#include "../../common/IndexingSettings.h"
|
|
#include "FFTResult.h"
|
|
|
|
#define FFT_MAX_SPOTS (64*1024)
|
|
|
|
class FFTIndexer : public Indexer {
|
|
|
|
protected:
|
|
const float min_length_A;
|
|
const float max_length_A;
|
|
const float min_angle_deg;
|
|
const float max_angle_deg;
|
|
|
|
const int nDirections;
|
|
float histogram_spacing;
|
|
int64_t histogram_size;
|
|
std::vector<Coord> direction_vectors;
|
|
|
|
size_t input_size;
|
|
size_t output_size;
|
|
|
|
// Per-direction best peaks (host)
|
|
std::vector<FFTResult> result_fft;
|
|
|
|
void SetupUnitCell(const std::optional<UnitCell> &cell) override;
|
|
// widen=false: combine only the shortest few filtered vectors (fast, correct for compact cells).
|
|
// widen=true: anchor the two short axes but let the third range over all vectors (reaches the long
|
|
// axis of a large/elongated cell). Used only as a fallback when the standard reduction indexes poorly.
|
|
std::vector<CrystalLattice> ReduceResults(const std::vector<Coord> &results, bool widen) const;
|
|
std::vector<Coord> FilterFFTResults(size_t max_vectors) const;
|
|
std::vector<CrystalLattice> ReduceAndRefine(const std::vector<Coord> &coord, size_t nspots,
|
|
const std::vector<Coord> &filtered, bool widen);
|
|
float IndexedFraction(const CrystalLattice &latt, const std::vector<Coord> &coord, size_t nspots) const;
|
|
void SetupDirectionVectors();
|
|
|
|
virtual void ExecuteFFT(const std::vector<Coord> &coord, size_t nspots) = 0;
|
|
public:
|
|
|
|
explicit FFTIndexer(const IndexingSettings& settings);
|
|
~FFTIndexer() override = default;
|
|
|
|
std::vector<CrystalLattice> RunInternal(const std::vector<Coord> &coord, size_t nspots) override;
|
|
};
|
|
|