diff --git a/rugnux/RugnuxFirstPass.cpp b/rugnux/RugnuxFirstPass.cpp index 08166d5a4..cc9f06b22 100644 --- a/rugnux/RugnuxFirstPass.cpp +++ b/rugnux/RugnuxFirstPass.cpp @@ -1423,11 +1423,14 @@ auto Rugnux::FirstPassRun::IndexSchemes() -> FirstPass { // neither the frame count nor the occupancy of the classes it adds tells it from a superstructure. // What does is the class of nodes on neither domain. Two domains put every reflection on the union of // their own index-n sub-lattices K1 and K2 and leave the (1 - 1/n)^2 of the nodes on neither empty but -// for chance coincidences. One crystal cannot: whatever sub-lattice its reflections favour (a -// pseudo-translation), the nodes off K1 that lie on K2 and those that lie on neither are the same -// cosets of it, and as dense in spots per node. The two sub-lattices are the same lattice in two -// orientations, so their cells must also agree. Where all of this holds the stronger domain's own -// cell is adopted, and the other domain is left to the leftover-lattice census, which names it. +// for chance coincidences, while the nodes on both (the overlap) and on each alone are populated. One +// crystal cannot do that: whatever sub-lattice its reflections favour (a pseudo-translation), the +// nodes off K1 that lie on K2 and those that lie on neither are the same cosets of it and as dense in +// spots, and a crystal whose spots avoid one coset of a sub-lattice thins the overlap with the class +// on neither alike. So the class on neither must be far emptier than every other class. The two +// sub-lattices are the same lattice in two orientations, so their cells must also agree. Where all +// of this holds the stronger domain's own cell is adopted, and the other domain is left to the +// leftover-lattice census, which names it. // Following Friedel's twin index (Friedel 1926, Lecons de cristallographie) and the obverse/reverse // reflection classes of Herbst-Irmer & Sheldrick (2002) Acta Cryst. B58, 477-481 auto Rugnux::FirstPassRun::TwinCompositeHypothesis(FirstPass best) -> FirstPass { @@ -1482,19 +1485,20 @@ auto Rugnux::FirstPassRun::TwinCompositeHypothesis(FirstPass best) -> FirstPass const bool congruent = std::abs(u1.a - u2.a) < 0.02 * u1.a && std::abs(u1.b - u2.b) < 0.02 * u1.b && std::abs(u1.c - u2.c) < 0.02 * u1.c && std::abs(u1.alpha - u2.alpha) < 2.0 && std::abs(u1.beta - u2.beta) < 2.0 && std::abs(u1.gamma - u2.gamma) < 2.0; - // Spots per node: each domain alone holds 1/n - 1/n^2 of the nodes, neither (1 - 1/n)^2. One - // crystal reads the two densities alike, up to the scatter of the pair picked as the emptiest - // out of 7 or 13 forms (up to 1.6 where the run has a hundred spots); two domains read them - // tens of times apart. - const double weak_density = c.only2 / (1.0 / n - 1.0 / (n * n)); + // Spots per node: each domain alone holds 1/n - 1/n^2 of the nodes, both 1/n^2, neither + // (1 - 1/n)^2. One crystal reads the emptiest of the first three about as dense as neither (up + // to 1.5 times, the scatter of picking the emptiest pair out of 7 or 13 forms); two domains read + // it tens of times denser. + const double other_density = std::min(c.only2 / (1.0 / n - 1.0 / (n * n)), + c.both * static_cast(n * n)); const double neither_density = c.neither / ((1.0 - 1.0 / n) * (1.0 - 1.0 / n)); - const bool composite = congruent && c.only2 >= 20 && weak_density > 4.0 * neither_density; + const bool composite = congruent && c.only2 >= 20 && other_density > 4.0 * neither_density; logger.Info("Twin-composite hypothesis, index {}: of {} validation spots on the standing cell " "({:.0f} A^3), the sub-lattices ({},{},{}) and ({},{},{}) hold {} and {} alone, {} " - "together and {} on neither - per node {:.1f} on the weaker one's own class against " + "together and {} on neither - per node {:.1f} on the emptiest of the other classes against " "{:.1f} on neither{} => {}", n, hkl.size(), best.vol, forms[bi][0], forms[bi][1], forms[bi][2], forms[bj][0], forms[bj][1], forms[bj][2], - c.only1, c.only2, c.both, c.neither, weak_density, neither_density, + c.only1, c.only2, c.both, c.neither, other_density, neither_density, congruent ? "" : ", cells not congruent", composite ? "two domains on their coincidence lattice" : "one lattice"); if (!composite)