// SPDX-FileCopyrightText: 2025 Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include #include "../common/Reflection.h" #include "../common/UnitCell.h" #include "../common/DiffractionExperiment.h" #include "IntegrationOutcome.h" #include struct MergeStatistics; struct TwinningAnalysisResult; // The error model as it is reported, already formatted. `isa` is the whole-range 1/sqrt(a*b), the // same quantity XDS's ISa denotes, so a file written here is directly comparable with a CORRECT.LP; // `isa_asymptotic` is the strong-reflection tier, which only the rotation path has. `a` and `b` are // in XDS's convention, sigma^2 = a*(sigma0^2 + b*I^2). Empty strings are written as unknown. struct ErrorModelReport { std::string isa; std::string isa_asymptotic; std::string a; std::string b; }; // nthreads: workers for the per-reflection row formatting, which is the bulk of the file. void WriteMmcifReflections(const std::vector &reflections, const UnitCell &unitCell, const DiffractionExperiment &experiment, const MergeStatistics &statistics, const ErrorModelReport &error_model, const TwinningAnalysisResult &twinning, const std::string &filename, size_t nthreads); void WriteMtzReflections(const std::vector &reflections, const UnitCell &unitCell, const DiffractionExperiment &experiment, const std::string &filename); // SHELX HKLF-4 text file (h k l I sigma(I), Bijvoet mates separate) for SHELXC / ANODE. // nthreads: workers for the per-reflection row formatting, as for the mmCIF. void WriteShelxHklReflections(const std::vector &reflections, const DiffractionExperiment &experiment, const std::string &filename, size_t nthreads); // Unmerged observations in the column and batch-header layout POINTLESS writes: aimless, pointless, // careless and iotbx.merging_statistics all read that layout. H K L are the ASU indices and M/ISYM // recovers the index the reflection was measured at (which is what careless needs to see the crystal // frame) and says whether the observation is a partial. // sum_partials: add the partials of each rocking event into one full, written at the batch of the // event's centroid with the summed rocking-curve fraction in FRACTIONCALC - the plain sum every // rotation program writes, over the events the combine would assemble (--min-partiality). False // writes one row per integrated box instead, flagged as partials for the reading program to sum. // Stills have no rocking events and are unaffected either way. // The intensities carry the deterministic per-reflection corrections and nothing else - Lorentz // and polarization in LP, the sensor's angle-dependent efficiency in QE, and the attenuation of the // flight-path medium in FLIGHT - so raw counts are I / LP * QE * FLIGHT. The partiality and the // per-image scale are left for the reading program to fit, since every program this file is for // fits a scale model of its own. void WriteUnmergedMtzReflections(const std::vector &outcomes, const UnitCell &unitCell, const DiffractionExperiment &experiment, bool sum_partials, const std::string &filename, size_t nthreads = 1); // The same file in two steps, for a caller that builds it while the outcomes' per-image scale fields // are still being written: UnmergedMtz reads everything but mosaicity_deg, which // SetUnmergedMtzMosaicity then copies into the batch headers. The Mtz points at the experiment's space // group, so the experiment has to outlive it. gemmi::Mtz UnmergedMtz(const std::vector &outcomes, const UnitCell &unitCell, const DiffractionExperiment &experiment, bool sum_partials, size_t nthreads = 1); void SetUnmergedMtzMosaicity(gemmi::Mtz &mtz, const std::vector &outcomes); void WriteReflections(const std::vector &reflections, const UnitCell &unitCell, const DiffractionExperiment &experiment, const MergeStatistics &statistics, const ErrorModelReport &error_model, const TwinningAnalysisResult &twinning, const std::string &filename, size_t nthreads);