Reflection files record their provenance: commit, command line, input file
The merged .mtz and the unmerged .mtz carry, after the existing "From Rugnux <version>" record, HISTORY records with the git commit and date, the command line, the input file (absolute path) and a cheap identity of it - size and modification time, the master file's for HDF5, nothing hashed - and the collection date. MTZ allows 30 records of 80 characters; a longer value runs on over records starting with '+', cut so that no record ends in a blank a reader would strip. The mmCIF names the commit in _software.description and the command line, input file and identity under _jfjoch_provenance (the local prefix the other private items use; no PDBx item holds them), the first two as text fields so any character survives. OutputProvenance is filled once in the CLI and travels in ProcessConfig to every writer of a reflection file. Reflection data are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVmAWnzCmRKAXVUCdc4iNi
This commit is contained in:
@@ -110,6 +110,36 @@ std::string IsoDate(const std::string ×tamp) {
|
||||
return timestamp.substr(0, 10);
|
||||
}
|
||||
|
||||
// The commit this build was made from, with its date; empty when the build does not know it.
|
||||
std::string GitDescription() {
|
||||
return jfjoch_git_sha1().empty() ? std::string() : jfjoch_git_sha1() + " " + jfjoch_git_date();
|
||||
}
|
||||
|
||||
// The MTZ HISTORY records saying where a file came from, after the one naming the program: the
|
||||
// commit, the command line and the input file. MTZ holds at most 30 records of 80 characters
|
||||
// (mtzformat.html), so a long value runs on over as many records as it needs, up to that limit, each
|
||||
// continuation starting with '+'. Readers strip the blanks at both ends of a record, so a record is
|
||||
// never cut just after a space: the space starts the next one, behind its '+'.
|
||||
void AddProvenanceHistory(gemmi::Mtz &mtz, const OutputProvenance &provenance) {
|
||||
const auto add = [&mtz](const std::string &label, const std::string &value) {
|
||||
if (value.empty())
|
||||
return;
|
||||
std::string rest = label + value;
|
||||
while (!rest.empty() && mtz.history.size() < 30) {
|
||||
size_t n = std::min<size_t>(80, rest.size());
|
||||
while (n > 1 && n < rest.size() && rest[n - 1] == ' ')
|
||||
--n;
|
||||
mtz.history.push_back(rest.substr(0, n));
|
||||
rest = n < rest.size() ? "+" + rest.substr(n) : std::string();
|
||||
}
|
||||
};
|
||||
add("Rugnux git ", GitDescription());
|
||||
add("Command: ", provenance.command_line);
|
||||
add("Input: ", provenance.input_file);
|
||||
add("Input file: ", provenance.input_identity);
|
||||
add("Collected: ", provenance.collection_date);
|
||||
}
|
||||
|
||||
// _entry, _cell and _symmetry: what every data block of the file restates, so each one stands alone.
|
||||
void WriteBlockHeader(std::ostream &out, const std::string &entry_id, const UnitCell &unitCell,
|
||||
const gemmi::SpaceGroup &sg) {
|
||||
@@ -253,14 +283,29 @@ void WriteMmcifReflections(const std::vector<MergedReflection> &reflections,
|
||||
|
||||
// ---------- _software ----------
|
||||
// Rugnux integrated the images (data reduction) and scaled and merged them (data scaling), the
|
||||
// two rows the wwPDB asks a data-processing program to be named under.
|
||||
// two rows the wwPDB asks a data-processing program to be named under; the description is the
|
||||
// commit the build was made from.
|
||||
const std::string software_description = CifStr(GitDescription().empty() ? "" : "git " + GitDescription());
|
||||
out << "loop_\n";
|
||||
out << "_software.pdbx_ordinal\n";
|
||||
out << "_software.name\n";
|
||||
out << "_software.version\n";
|
||||
out << "_software.classification\n";
|
||||
out << "1 Rugnux " << CifStr(jfjoch_version()) << " 'data reduction'\n";
|
||||
out << "2 Rugnux " << CifStr(jfjoch_version()) << " 'data scaling'\n";
|
||||
out << "_software.description\n";
|
||||
out << "1 Rugnux " << CifStr(jfjoch_version()) << " 'data reduction' " << software_description << "\n";
|
||||
out << "2 Rugnux " << CifStr(jfjoch_version()) << " 'data scaling' " << software_description << "\n";
|
||||
out << "#\n";
|
||||
|
||||
// ---------- provenance ----------
|
||||
// How this file was made and from what: the command line and the input file, which no PDBx item
|
||||
// holds, under the jfjoch local prefix as the other private items here. The two that can carry
|
||||
// any character are text fields.
|
||||
if (!provenance.command_line.empty())
|
||||
out << "_jfjoch_provenance.command_line\n;" << provenance.command_line << "\n;\n";
|
||||
if (!provenance.input_file.empty())
|
||||
out << "_jfjoch_provenance.input_file\n;" << provenance.input_file << "\n;\n";
|
||||
if (!provenance.input_identity.empty())
|
||||
out << "_jfjoch_provenance.input_identity " << CifStr(provenance.input_identity) << "\n";
|
||||
out << "#\n";
|
||||
|
||||
// ---------- _exptl_crystal / _diffrn ----------
|
||||
@@ -624,6 +669,7 @@ void WriteMmcifReflections(const std::vector<MergedReflection> &reflections,
|
||||
void WriteMtzReflections(const std::vector<MergedReflection> &reflections,
|
||||
const UnitCell &unitCell,
|
||||
const DiffractionExperiment &experiment,
|
||||
const OutputProvenance &provenance,
|
||||
const std::string &filename) {
|
||||
gemmi::Mtz mtz;
|
||||
|
||||
@@ -634,6 +680,7 @@ void WriteMtzReflections(const std::vector<MergedReflection> &reflections,
|
||||
// Producing-software provenance in the MTZ header (title + HISTORY, the CCP4 convention).
|
||||
mtz.title = "Rugnux merged reflections";
|
||||
mtz.history.push_back("From Rugnux " + jfjoch_version() + ", data reduction");
|
||||
AddProvenanceHistory(mtz, provenance);
|
||||
|
||||
// Add dataset. HKL_base first, so the data dataset is id 1: a dataset written as id 0 is the
|
||||
// reserved base dataset as far as CCP4's mtzlib is concerned, and the wavelength on it is
|
||||
@@ -954,12 +1001,14 @@ gemmi::Mtz UnmergedMtz(const std::vector<IntegrationOutcome> &outcomes,
|
||||
const UnitCell &unitCell,
|
||||
const DiffractionExperiment &experiment,
|
||||
bool sum_partials,
|
||||
const OutputProvenance &provenance,
|
||||
size_t nthreads) {
|
||||
gemmi::Mtz mtz;
|
||||
mtz.spacegroup = &experiment.GetSpaceGroupOrP1();
|
||||
mtz.set_cell_for_all(unitCell);
|
||||
mtz.title = "Rugnux unmerged observations";
|
||||
mtz.history.push_back("From Rugnux " + jfjoch_version() + ", data reduction");
|
||||
AddProvenanceHistory(mtz, provenance);
|
||||
mtz.add_base(); // the HKL_base dataset and the H K L columns
|
||||
gemmi::Mtz::Dataset &ds = mtz.add_dataset("unmerged");
|
||||
ds.crystal_name = experiment.GetSampleName();
|
||||
@@ -1223,9 +1272,10 @@ void WriteUnmergedMtzReflections(const std::vector<IntegrationOutcome> &outcomes
|
||||
const UnitCell &unitCell,
|
||||
const DiffractionExperiment &experiment,
|
||||
bool sum_partials,
|
||||
const OutputProvenance &provenance,
|
||||
const std::string &filename,
|
||||
size_t nthreads) {
|
||||
gemmi::Mtz mtz = UnmergedMtz(outcomes, unitCell, experiment, sum_partials, nthreads);
|
||||
gemmi::Mtz mtz = UnmergedMtz(outcomes, unitCell, experiment, sum_partials, provenance, nthreads);
|
||||
SetUnmergedMtzMosaicity(mtz, outcomes);
|
||||
mtz.write_to_file(filename);
|
||||
}
|
||||
@@ -1244,7 +1294,7 @@ void WriteReflections(const std::vector<MergedReflection> &reflections,
|
||||
// phenix reflection tools, mmCIF for deposition and as the self-describing native format, HKLF-4 for
|
||||
// SHELXL refinement and SHELXC / ANODE substructure solution). The .hkl holds the unmerged scaled
|
||||
// fulls where the merge provides them (rotation), the merged reflections otherwise (stills).
|
||||
WriteMtzReflections(reflections, unitCell, experiment, filename + ".mtz");
|
||||
WriteMtzReflections(reflections, unitCell, experiment, provenance, filename + ".mtz");
|
||||
WriteMmcifReflections(reflections, unitCell, experiment, statistics, error_model, twinning,
|
||||
scaled_fulls ? *scaled_fulls : std::vector<ScaledFull>{}, provenance,
|
||||
filename + ".cif", nthreads);
|
||||
|
||||
Reference in New Issue
Block a user