// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "WriteModel.h" #include #include // make_mmcif_document #include // write_cif_to_stream #include // write_pdb #include // setup_entities #include "../common/Logger.h" void WritePlacedModel(const gemmi::Structure &placed, const UnitCell &cell, const gemmi::SpaceGroup &space_group, const std::string &output_prefix, Logger &logger) { const std::string cif_path = output_prefix + "_model.cif"; // PDB as well as mmCIF, because the fragment-screening tools this file exists for take a PDB: // PanDDA's per-dataset input is .pdb beside .mtz, and dimple produces the same pair. // Same coordinates, same frame, both formats - a PDB cannot hold every cell, so it is written // when it can be and skipped, with a line saying so, when it cannot. const std::string pdb_path = output_prefix + "_model.pdb"; gemmi::Structure st = placed; // Not the model's own cell and group but the data's, taken from the same two values the reflection // files are written from. The coordinates already sit in this cell; what is set here is the label // the file carries, which must match the .mtz beside it - the enantiomorph in particular, since // --model can adopt the model's and that is neither the data's original label nor, necessarily, // the one the input model arrived with. st.cell = cell; st.spacegroup_hm = space_group.xhm(); st.setup_cell_images(); // Fills in entity types and label_asym_id for a model read from a PDB, which carries neither. // Both are no-ops where the input already had them, i.e. for an mmCIF input. // setup_entities and make_mmcif_document can both throw, and this runs BEFORE the reflection files // are written: a convenience deliverable must not be able to take the run's actual output with it. // The map writer does the same. try { gemmi::setup_entities(st); std::ofstream os(cif_path); gemmi::cif::write_cif_to_stream(os, gemmi::make_mmcif_document(st)); if (!os) { logger.Error("Model validation: cannot write the placed model to {}", cif_path); return; } } catch (const std::exception &e) { logger.Warning("Model validation: could not write the placed model to {} ({})", cif_path, e.what()); return; } bool pdb_written = false; try { std::ofstream os(pdb_path); gemmi::write_pdb(st, os); pdb_written = static_cast(os); } catch (const std::exception &e) { logger.Warning("Model validation: the placed model could not also be written as PDB ({}); " "{} has it", e.what(), cif_path); } logger.Info("Model validation: the model as placed against these data written to {}{} " "(cell and space group {} as in the reflection files)", cif_path, pdb_written ? " and " + pdb_path : "", space_group.short_name()); }