diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index 31b89117..fff06f75 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -2130,8 +2130,18 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b const auto want_system = fixed_sg->crystal_system() == gemmi::CrystalSystem::Trigonal && fixed_sg->centring_type() == 'P' ? gemmi::CrystalSystem::Hexagonal : fixed_sg->crystal_system(); - const auto cand = LatticeSearchForClass(*end_msg.rotation_lattice, want_system, - fixed_sg->centring_type()); + // Search the PRIMITIVE cell. rotation_lattice is the conventional one and its centring + // is carried beside it, but the character table is reached through a Niggli reduction + // that takes whatever it is handed to be primitive - so a centred conventional cell + // reduces as a lattice with the wrong point set. This arm is entered exactly when the + // centrings differ, which includes every centred indexed lattice, so the distinction is + // not academic: a C-centred orthorhombic conventional cell read as primitive is itself a + // perfectly good oP metric, and -S P212121 on one would match, reindex by the identity + // and merge in a group whose absence rule deletes half the reflections that exist. + // The other lattice arithmetic in this file goes through ToPrimitive for the same reason. + const auto cand = LatticeSearchForClass( + end_msg.rotation_lattice->ToPrimitive(indexed_centering), want_system, + fixed_sg->centring_type()); if (cand && reindex_into(*cand)) { const auto &uc = *result.consensus_cell; logger.Info("Reindexed the {}-centred indexed lattice into the {}-centred setting the " diff --git a/rugnux_vs_xds.py b/rugnux_vs_xds.py index 854d67b7..9f994020 100755 --- a/rugnux_vs_xds.py +++ b/rugnux_vs_xds.py @@ -250,7 +250,7 @@ def find_rugnux(explicit): return None -def run_rugnux(master, workdir, name, xds, rugnux_bin, threads, timeout, reuse, extra_args=""): +def run_rugnux(master, workdir, name, xds, rugnux_bin, threads, timeout, reuse, extra_args="", force_sg=False): """Run rugnux; return (cif_path, error). Resolution + anomalous matched to XDS.""" workdir.mkdir(parents=True, exist_ok=True) cif = workdir / f"{name}.cif" @@ -267,6 +267,8 @@ def run_rugnux(master, workdir, name, xds, rugnux_bin, threads, timeout, reuse, cmd += ["--scaling-low-resolution", f"{xds['dmax']:.3f}"] if threads: cmd += ["-N", str(threads)] + if force_sg and xds.get("sg"): + cmd += ["-S", str(xds["sg"])] if extra_args: import shlex cmd += shlex.split(extra_args) @@ -391,6 +393,9 @@ def main(): ap.add_argument("--reuse", action="store_true", help="skip rugnux where its .cif already exists") ap.add_argument("--xds-only", action="store_true", help="only parse+print the XDS side (no rugnux run)") ap.add_argument("--progress", action="store_true", help="print per-crystal progress to stderr") + ap.add_argument("--force-xds-sg", action="store_true", + help="pin each crystal to the XDS reference space group with -S, instead of " + "letting rugnux determine it de novo (exercises the fixed-group path)") ap.add_argument("--extra-args", default="", help="extra args appended to every rugnux command (e.g. '--rotation-refine-geometry')") args = ap.parse_args() @@ -432,7 +437,7 @@ def main(): file=sys.stderr, flush=True) cif, err, elapsed = run_rugnux(master, workdir / name, name, xds, rugnux_bin, args.threads, args.timeout, args.reuse, - args.extra_args) + args.extra_args, args.force_xds_sg) if cif: try: rug = parse_rugnux_cif(cif)