// Copyright 2024 DeepMind Technologies Limited // // AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of // this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/ // // To request access to the AlphaFold 3 model parameters, follow the process set // out at https://github.com/google-deepmind/alphafold3. You may only use these // if received directly from Google. Use is subject to terms of use available at // https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md #include #include #include #include #include #include #include #include #include #include #include #include #include "numpy/ndarrayobject.h" #include "numpy/ndarraytypes.h" #include "numpy/npy_common.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" #include "alphafold3/parsers/cpp/cif_dict_lib.h" #include "alphafold3/structure/cpp/mmcif_altlocs.h" #include "alphafold3/structure/cpp/mmcif_layout.h" #include "pybind11/cast.h" #include "pybind11/gil.h" #include "pybind11/numpy.h" #include "pybind11/pybind11.h" #include "pybind11/pytypes.h" #include "pybind11/stl.h" #include "pybind11_abseil/absl_casters.h" namespace alphafold3 { namespace { namespace py = pybind11; struct PyObjectDeleter { inline void operator()(PyObject* obj) const { Py_CLEAR(obj); } }; using ScopedPyObject = std::unique_ptr; using StringArrayRef = absl::Span; using Indexer = absl::flat_hash_map; // Returns the reverse look-up map of name to index. Indexer MakeIndex(StringArrayRef col) { Indexer index; index.reserve(col.size()); for (std::size_t i = 0; i < col.size(); ++i) { index[col[i]] = i; } return index; } // Returns whether each container is the same size. template bool AreSameSize(C c, const Cs&... cs) { return ((c.size() == cs.size()) && ...); } // Stores references to columns in `_atom_site` ensuring they all exist and // are the same size. struct AtomSiteLoop { explicit AtomSiteLoop(const CifDict& cif_dict) : id(cif_dict["_atom_site.id"]), model_id(cif_dict["_atom_site.pdbx_PDB_model_num"]), chain_id(cif_dict["_atom_site.label_asym_id"]), seq_id(cif_dict["_atom_site.label_seq_id"]), comp_id(cif_dict["_atom_site.label_comp_id"]), atom_id(cif_dict["_atom_site.label_atom_id"]), alt_id(cif_dict["_atom_site.label_alt_id"]), occupancy(cif_dict["_atom_site.occupancy"]) { if (!AreSameSize(id, model_id, chain_id, seq_id, comp_id, atom_id, alt_id, occupancy)) { throw py::value_error( absl::StrCat("Invalid '_atom_site.' loop. ", // "len(id)=", id.size(), ", ", // "len(pdbx_PDB_model_num)=", model_id.size(), ", ", // "len(label_asym_id)=", chain_id.size(), ", ", // "len(label_seq_id)=", seq_id.size(), ", ", // "len(label_comp_id)=", comp_id.size(), ", ", // "len(atom_id)=", atom_id.size(), ", ", // "len(label_alt_id)=", alt_id.size(), ", ", // "len(occupancy)=", occupancy.size())); } } StringArrayRef id; StringArrayRef model_id; StringArrayRef chain_id; StringArrayRef seq_id; StringArrayRef comp_id; StringArrayRef atom_id; StringArrayRef alt_id; StringArrayRef occupancy; }; // Stores references to columns in `_entity` ensuring they all exist and are the // same size. struct EntityLoop { explicit EntityLoop(const CifDict& cif_dict) : id(cif_dict["_entity.id"]), type(cif_dict["_entity.type"]) { if (!AreSameSize(id, type)) { throw py::value_error(absl::StrCat("Invalid '_entity.' loop. ", // "len(id)=", id.size(), ", ", // "len(type)=", type.size())); } } StringArrayRef id; StringArrayRef type; }; // Stores references to columns in `_entity_poly` ensuring they all exist and // are the same size. struct EntityPolyLoop { explicit EntityPolyLoop(const CifDict& cif_dict) : entity_id(cif_dict["_entity_poly.entity_id"]), type(cif_dict["_entity_poly.type"]) { if (!AreSameSize(entity_id, type)) { throw py::value_error(absl::StrCat("Invalid '_entity_poly.' loop. ", // "len(entity_id)=", entity_id.size(), ", ", // "len(type)=", type.size())); } } StringArrayRef entity_id; StringArrayRef type; }; // Returns a set of entity names removing ones not included by the flags // specified. absl::flat_hash_set SelectChains(const CifDict& mmcif, bool include_nucleotides, bool include_ligands, bool include_water, bool include_other) { EntityLoop entity_loop(mmcif); EntityPolyLoop entity_poly(mmcif); absl::flat_hash_set permitted_polymers{"polypeptide(L)"}; absl::flat_hash_set forbidden_polymers; for (absl::string_view type : {"polydeoxyribonucleotide", "polyribonucleotide", "polydeoxyribonucleotide/polyribonucleotide hybrid"}) { if (include_nucleotides) { permitted_polymers.emplace(type); } else { forbidden_polymers.emplace(type); } } absl::flat_hash_set permitted_nonpoly_entity_types; absl::flat_hash_set forbidden_nonpoly_entity_types; for (absl::string_view type : {"non-polymer", "branched"}) { if (include_ligands) { permitted_nonpoly_entity_types.emplace(type); } else { forbidden_nonpoly_entity_types.emplace(type); } } absl::string_view water_type = "water"; if (include_water) { permitted_nonpoly_entity_types.emplace(water_type); } else { forbidden_nonpoly_entity_types.emplace(water_type); } StringArrayRef chain_ids = mmcif["_struct_asym.id"]; StringArrayRef entity_ids = mmcif["_struct_asym.entity_id"]; Indexer chain_index = MakeIndex(chain_ids); Indexer entity_poly_index = MakeIndex(entity_poly.entity_id); Indexer entity_id_to_index = MakeIndex(entity_loop.id); absl::flat_hash_set keep_chain_id; for (std::size_t i = 0; i < chain_ids.size(); ++i) { absl::string_view chain_id = chain_ids[i]; absl::string_view entity_id = entity_ids[i]; if (entity_id_to_index.empty() || entity_loop.type[entity_id_to_index[entity_id]] == "polymer") { if (auto it = entity_poly_index.find(entity_id); it != entity_poly_index.end()) { absl::string_view poly_type = entity_poly.type[it->second]; if (include_other) { if (!forbidden_polymers.contains(poly_type)) { keep_chain_id.insert(chain_id); } } else { if (permitted_polymers.contains(poly_type)) { keep_chain_id.insert(chain_id); } } } } else { absl::string_view entity_type = entity_loop.type[entity_id_to_index[entity_id]]; if (include_other) { if (!forbidden_nonpoly_entity_types.contains(entity_type)) { keep_chain_id.insert(chain_id); continue; } } else { if (permitted_nonpoly_entity_types.contains(entity_type)) { keep_chain_id.insert(chain_id); continue; } } } } return keep_chain_id; } class ProcessResidue { public: explicit ProcessResidue(const char* residue) : residue_(PyUnicode_InternFromString(residue)) {} bool IsResidue(PyObject* residue) { return ArePyObjectsEqual(residue_.get(), residue); } static bool ArePyObjectsEqual(PyObject* lhs, PyObject* rhs) { switch (PyObject_RichCompareBool(lhs, rhs, Py_EQ)) { case -1: PyErr_Clear(); return false; case 0: return false; default: return true; } } private: ScopedPyObject residue_; }; struct Position3 { float x; float y; float z; }; float DistanceSquared(Position3 v1, Position3 v2) { float dx = v1.x - v2.x; float dy = v1.y - v2.y; float dz = v1.z - v2.z; return dx * dx + dy * dy + dz * dz; } class FixArginine : public ProcessResidue { public: FixArginine() : ProcessResidue("ARG"), cd_(PyUnicode_InternFromString("CD")), nh1_(PyUnicode_InternFromString("NH1")), nh2_(PyUnicode_InternFromString("NH2")), hh11_(PyUnicode_InternFromString("HH11")), hh21_(PyUnicode_InternFromString("HH21")), hh12_(PyUnicode_InternFromString("HH12")), hh22_(PyUnicode_InternFromString("HH22")) {} void Fix(absl::Span atom_ids, absl::Span atom_x, absl::Span atom_y, absl::Span atom_z) { std::ptrdiff_t cd_index = -1; std::ptrdiff_t nh1_index = -1; std::ptrdiff_t nh2_index = -1; std::ptrdiff_t hh11_index = -1; std::ptrdiff_t hh21_index = -1; std::ptrdiff_t hh12_index = -1; std::ptrdiff_t hh22_index = -1; for (std::ptrdiff_t index = 0; index < atom_ids.size(); ++index) { PyObject* atom_id = atom_ids[index]; if (cd_index == -1 && ArePyObjectsEqual(atom_id, cd_.get())) { cd_index = index; } else if (nh1_index == -1 && ArePyObjectsEqual(atom_id, nh1_.get())) { nh1_index = index; } else if (nh2_index == -1 && ArePyObjectsEqual(atom_id, nh2_.get())) { nh2_index = index; } else if (hh11_index == -1 && ArePyObjectsEqual(atom_id, hh11_.get())) { hh11_index = index; } else if (hh21_index == -1 && ArePyObjectsEqual(atom_id, hh21_.get())) { hh21_index = index; } else if (hh12_index == -1 && ArePyObjectsEqual(atom_id, hh12_.get())) { hh12_index = index; } else if (hh22_index == -1 && ArePyObjectsEqual(atom_id, hh22_.get())) { hh22_index = index; } } if (cd_index < 0 || nh1_index < 0 || nh2_index < 0) { return; } Position3 cd_pos(atom_x[cd_index], atom_y[cd_index], atom_z[cd_index]); Position3 nh1_pos(atom_x[nh1_index], atom_y[nh1_index], atom_z[nh1_index]); Position3 nh2_pos(atom_x[nh2_index], atom_y[nh2_index], atom_z[nh2_index]); if (DistanceSquared(nh1_pos, cd_pos) <= DistanceSquared(nh2_pos, cd_pos)) { return; } std::swap(atom_ids[nh1_index], atom_ids[nh2_index]); if (hh11_index >= 0 && hh21_index >= 0) { std::swap(atom_ids[hh11_index], atom_ids[hh21_index]); } else if (hh11_index >= 0) { Py_DECREF(atom_ids[hh11_index]); Py_INCREF(hh21_.get()); atom_ids[hh11_index] = hh21_.get(); } else if (hh21_index >= 0) { Py_DECREF(atom_ids[hh21_index]); Py_INCREF(hh11_.get()); atom_ids[hh21_index] = hh11_.get(); } if (hh12_index >= 0 && hh22_index >= 0) { std::swap(atom_ids[hh12_index], atom_ids[hh22_index]); } else if (hh12_index >= 0) { Py_DECREF(atom_ids[hh12_index]); Py_INCREF(hh22_.get()); atom_ids[hh12_index] = hh22_.get(); } else if (hh22_index >= 0) { Py_DECREF(atom_ids[hh22_index]); Py_INCREF(hh21_.get()); atom_ids[hh22_index] = hh21_.get(); } } private: ScopedPyObject cd_; ScopedPyObject nh1_; ScopedPyObject nh2_; ScopedPyObject hh11_; ScopedPyObject hh21_; ScopedPyObject hh12_; ScopedPyObject hh22_; }; // Returns the layout of the mmCIF `_atom_site` table. inline MmcifLayout ReadMmcifLayout(const CifDict& mmcif, absl::string_view model_id = "") { py::gil_scoped_release release; auto mmcif_layout = MmcifLayout::Create(mmcif, model_id); if (mmcif_layout.ok()) { return *mmcif_layout; } throw py::value_error(std::string(mmcif_layout.status().message())); } std::pair MmcifFilter( // const CifDict& mmcif, // bool include_nucleotides, // bool include_ligands, // bool include_water, // bool include_other, // absl::string_view model_id) { if (_import_array() < 0) { throw py::import_error("Failed to import NumPy."); } auto layout = ReadMmcifLayout(mmcif, model_id); std::unique_ptr> keep_indices; size_t new_num_atoms; { py::gil_scoped_release release; AtomSiteLoop atom_site(mmcif); auto keep_chain_ids = SelectChains(mmcif, include_nucleotides, include_ligands, include_water, include_other); std::vector chain_indices; chain_indices.reserve(keep_chain_ids.size()); for (std::size_t i = 0; i < layout.num_chains(); ++i) { if (keep_chain_ids.contains( atom_site.chain_id[layout.atom_site_from_chain_index(i)])) { chain_indices.push_back(i); } } keep_indices = absl::WrapUnique(new std::vector(ResolveMmcifAltLocs( layout, atom_site.comp_id, atom_site.atom_id, atom_site.alt_id, atom_site.occupancy, chain_indices))); new_num_atoms = keep_indices->size(); if (layout.num_models() > 1) { keep_indices->reserve(layout.num_models() * new_num_atoms); std::uint64_t* start = &(*keep_indices->begin()); std::size_t num_atom = keep_indices->size(); // Copy first model indices into all model indices offsetting each copy. for (std::size_t i = 1; i < layout.num_models(); ++i) { std::size_t offset = i * layout.num_atoms(); std::transform(start, start + num_atom, std::back_inserter(*keep_indices), [offset](std::size_t v) { return v + offset; }); } } } layout.Filter(*keep_indices); npy_intp shape[] = {static_cast(layout.num_models()), static_cast(new_num_atoms)}; PyObject* arr = PyArray_SimpleNewFromData(2, shape, NPY_INT64, keep_indices->data()); // Create a capsule to hold the memory of the buffer so NumPy knows how to // delete it when done with it. PyObject* capsule = PyCapsule_New( keep_indices.release(), nullptr, +[](PyObject* capsule_cleanup) { void* memory = PyCapsule_GetPointer(capsule_cleanup, nullptr); delete static_cast*>(memory); }); PyArray_SetBaseObject(reinterpret_cast(arr), capsule); return std::make_pair(py::reinterpret_steal(arr), std::move(layout)); } void MmcifFixResidues( // const MmcifLayout& layout, // absl::Span comp_id, // absl::Span atom_id, // absl::Span atom_x, // absl::Span atom_y, // absl::Span atom_z, // bool fix_arginine // ) { std::optional arginine; std::size_t num_atoms = layout.num_atoms(); if (comp_id.size() != num_atoms || atom_id.size() != num_atoms || atom_x.size() != num_atoms || atom_y.size() != num_atoms || atom_z.size() != num_atoms) { throw py::value_error( absl::StrCat("Sizes must match. ", // "num_atoms=", num_atoms, ", ", // "len(comp_id)=", comp_id.size(), ", ", // "len(atom_id)=", atom_id.size(), ", ", // "len(atom_x)=", atom_x.size(), ", ", // "len(atom_y)=", atom_y.size(), ", ", // "len(atom_z)=", atom_z.size())); } if (fix_arginine) { arginine.emplace(); } if (!arginine.has_value()) { return; } for (std::size_t res_index = 0; res_index < layout.num_residues(); ++res_index) { auto [atom_start, atom_end] = layout.atom_range(res_index); std::size_t atom_count = atom_end - atom_start; PyObject* resname = comp_id[atom_start]; if (arginine.has_value() && arginine->IsResidue(resname)) { arginine->Fix(atom_id.subspan(atom_start, atom_count), atom_x.subspan(atom_start, atom_count), atom_y.subspan(atom_start, atom_count), atom_z.subspan(atom_start, atom_count)); } } } std::vector SelectedPolymerResidueMask( const MmcifLayout& layout, const std::vector& atom_site_label_asym_ids, // const std::vector& atom_site_label_seq_ids, // const std::vector& atom_site_label_comp_ids, // const std::vector& poly_seq_asym_ids, // const std::vector& poly_seq_seq_ids, // const std::vector& poly_seq_mon_ids // ) { absl::flat_hash_map, absl::string_view> selected; selected.reserve(layout.num_residues()); // layout.residues() is O(1) while layout.residue_starts() is O(num_res). const std::vector& residue_starts = layout.residue_starts(); for (int i = 0; i < layout.residues().size(); ++i) { std::size_t res_start = residue_starts[i]; std::size_t res_end = layout.residues()[i]; if (res_start == res_end) { continue; // Skip empty residues (containing no atoms). } absl::string_view label_seq_id = atom_site_label_seq_ids[i]; if (label_seq_id == ".") { continue; // Skip non-polymers. } absl::string_view label_asym_id = atom_site_label_asym_ids[i]; absl::string_view label_comp_id = atom_site_label_comp_ids[i]; selected[std::make_pair(label_asym_id, label_seq_id)] = label_comp_id; } std::vector mask; mask.reserve(poly_seq_mon_ids.size()); for (int i = 0; i < poly_seq_mon_ids.size(); ++i) { absl::string_view poly_seq_asym_id = poly_seq_asym_ids[i]; absl::string_view poly_seq_seq_id = poly_seq_seq_ids[i]; absl::string_view poly_seq_mon_id = poly_seq_mon_ids[i]; auto it = selected.find(std::make_pair(poly_seq_asym_id, poly_seq_seq_id)); if (it != selected.end()) { mask.push_back(it->second == poly_seq_mon_id); } else { mask.push_back(true); // Missing residues are not heterogeneous. } } return mask; } std::pair, std::vector> SelectedLigandResidueMask( const MmcifLayout& layout, // const std::vector& atom_site_label_asym_ids, // const std::vector& atom_site_label_seq_ids, // const std::vector& atom_site_auth_seq_ids, // const std::vector& atom_site_label_comp_ids, // const std::vector& atom_site_pdbx_pdb_ins_codes, // const std::vector& nonpoly_asym_ids, // const std::vector& nonpoly_auth_seq_ids, // const std::vector& nonpoly_pdb_ins_codes, // const std::vector& nonpoly_mon_ids, // const std::vector& branch_asym_ids, // const std::vector& branch_auth_seq_ids, // const std::vector& branch_pdb_ins_codes, // const std::vector& branch_mon_ids) { absl::flat_hash_map< std::tuple, absl::string_view> selected; selected.reserve(layout.num_residues()); // layout.residues() is O(1) while layout.residue_starts() is O(num_res). const std::vector& residue_starts = layout.residue_starts(); for (int i = 0; i < layout.residues().size(); ++i) { std::size_t res_start = residue_starts[i]; std::size_t res_end = layout.residues()[i]; if (res_start == res_end) { continue; // Skip empty residues (containing no atoms). } absl::string_view label_seq_id = atom_site_label_seq_ids[i]; if (label_seq_id != ".") { continue; // Skip polymers. } absl::string_view label_asym_id = atom_site_label_asym_ids[i]; absl::string_view auth_seq_id = atom_site_auth_seq_ids[i]; absl::string_view ins_code = atom_site_pdbx_pdb_ins_codes[i]; ins_code = ins_code == "?" ? "." : ins_code; // Remap unknown to unset. absl::string_view label_comp_id = atom_site_label_comp_ids[i]; selected[std::make_tuple(label_asym_id, auth_seq_id, ins_code)] = label_comp_id; } std::vector nonpoly_mask; nonpoly_mask.reserve(nonpoly_asym_ids.size()); for (int i = 0; i < nonpoly_asym_ids.size(); ++i) { absl::string_view nonpoly_asym_id = nonpoly_asym_ids[i]; absl::string_view nonpoly_auth_seq_id = nonpoly_auth_seq_ids[i]; absl::string_view nonpoly_ins_code = nonpoly_pdb_ins_codes[i]; // Remap unknown to unset. nonpoly_ins_code = nonpoly_ins_code == "?" ? "." : nonpoly_ins_code; absl::string_view nonpoly_mon_id = nonpoly_mon_ids[i]; auto it = selected.find(std::make_tuple( nonpoly_asym_id, nonpoly_auth_seq_id, nonpoly_ins_code)); if (it != selected.end()) { nonpoly_mask.push_back(it->second == nonpoly_mon_id); } else { nonpoly_mask.push_back(true); // Missing residues are not heterogeneous. } } std::vector branch_mask; branch_mask.reserve(branch_asym_ids.size()); for (int i = 0; i < branch_asym_ids.size(); ++i) { absl::string_view branch_asym_id = branch_asym_ids[i]; absl::string_view branch_auth_seq_id = branch_auth_seq_ids[i]; // Insertion codes in _pdbx_branch_scheme are not required and can be // missing. Default to unset ('.') in such case. absl::string_view branch_ins_code; if (i < branch_pdb_ins_codes.size()) { branch_ins_code = branch_pdb_ins_codes[i]; // Remap unknown to unset. branch_ins_code = branch_ins_code == "?" ? "." : branch_ins_code; } else { branch_ins_code = "."; } absl::string_view branch_mon_id = branch_mon_ids[i]; auto it = selected.find( std::make_tuple(branch_asym_id, branch_auth_seq_id, branch_ins_code)); if (it != selected.end()) { branch_mask.push_back(it->second == branch_mon_id); } else { branch_mask.push_back(true); // Missing residues are not heterogeneous. } } return std::make_pair(nonpoly_mask, branch_mask); } constexpr char kReadMmcifLayout[] = R"( Returns the layout of the cif_dict. Args: mmcif: mmCIF to calculate the layout for. model_id: If non-empty the layout of the given model is returned otherwise the layout of all models are returned. Raises: ValueError: if the mmCIF is malformed or the number of atoms in each model are inconsistent. )"; constexpr char kMmcifFilter[] = R"( Returns NumpyArray of selected rows in `_atom_site` and new layout. Args: mmcif: mmCIF to filter. include_nucleotides: Whether to include polymer entities of type: "polypeptide(L)\", "polydeoxyribonucleotide", "polyribonucleotide". Otherwise only "polypeptide(L)\". ("polypeptide(D)\" is never included.) include_ligands: Whether to include non-polymer entities of type: "non-polymer", "branched". include_water: Whether to include entities of type water. include_other: Whether to include other (non-standard) entity types that are not covered by any of the above parameters. model_id: If non-empty the model with given name is selected otherwise all models are selected. Returns: A tuple containing a numpy array with a shape (num_models, num_atoms) with the atom_site indices selected and the new layout. Raises: ValueError error if mmCIF dict does not have all required fields. )"; constexpr char kMmcifFixResidues[] = R"( Fixes residue columns in-place. Args: layout: layout from filter command. comp_id: '_atom_site.label_comp_id' of first model. group: '_atom_site.group_PDB' of first model. atom_id: '_atom_site.label_atom_id' of first model. type_symbol: '_atom_site.type_symbol' of first model. atom_x: '_atom_site.Cartn_x' of first model. atom_y: '_atom_site.Cartn_y' of first model. atom_z: '_atom_site.Cartn_z' of first model. fix_mse: Whether to convert MSE residues into MET residues. fix_arg: Whether to ensure the atoms in ARG are in the correct order. fix_unknown_dna: Whether to convert DNA residues from N to DN. dna_mask: Which atoms are from DNA chains. Raises: ValueError: If shapes are invalid. )"; constexpr char kSelectedPolymerResidueMask[] = R"( Returns a _pdbx_poly_seq_scheme mask for selected hetero residues. Should be called after filtering the layout using mmcif_utils.filter. Args: layout: Layout defining the _atom_site residue selection. atom_site_label_asym_ids: Internal (label) chain ID, per selected residue. atom_site_label_seq_ids: Internal (label) residue ID, per selected residue. atom_site_label_comp_ids: Residue name, per selected residue. poly_seq_asym_ids: Internal (label) chain ID, per residue. poly_seq_seq_ids: Internal (label) residue ID, per residue. poly_seq_mon_ids: Residue name, per residue. Returns: A mask for the _pdbx_poly_seq_scheme table. If residues are selected using this mask, they will have consistent heterogeneous residue selection with the _atom_site table. )"; constexpr char kSelectedLigandResidueMask[] = R"( Returns masks for selected ligand hetero residues. Should be called after filtering the layout using mmcif_utils.filter. Args: layout: Layout defining the _atom_site residue selection. atom_site_label_asym_ids: Internal (label) chain ID, per selected residue. atom_site_label_seq_ids: Internal (author) residue ID, per selected residue. atom_site_auth_seq_ids: External (author) residue ID, per selected residue. atom_site_label_comp_ids: Residue name, per selected residue. atom_site_pdbx_pdb_ins_codes: Insertion code, per selected residue. nonpoly_asym_ids: Internal (label) chain ID, per residue from _pdbx_nonpoly_scheme. nonpoly_auth_seq_ids: External (author) residue ID, per residue from _pdbx_nonpoly_scheme. nonpoly_pdb_ins_codes: Residue name, per residue from _pdbx_nonpoly_scheme. nonpoly_mon_ids: Insertion code, per residue from _pdbx_nonpoly_scheme. branch_asym_ids: Internal (label) chain ID, per residue from _pdbx_branch_scheme. branch_auth_seq_ids: External (author) residue ID, per residue from _pdbx_branch_scheme. branch_pdb_ins_codes: Residue name, per residue from _pdbx_branch_scheme. branch_mon_ids: Insertion code, per residue from _pdbx_branch_scheme. Returns: A tuple with masks for _pdbx_nonpoly_scheme and _pdbx_branch_scheme. If residues are selected using these masks, they will have consistent heterogeneous residue selection with the _atom_site table. )"; } // namespace void RegisterModuleMmcifUtils(pybind11::module m) { m.def("read_layout", ReadMmcifLayout, py::arg("mmcif"), // py::arg("model_id") = "", // py::doc(kReadMmcifLayout + 1) // ); m.def("filter", MmcifFilter, // py::arg("mmcif"), // py::arg("include_nucleotides"), // py::arg("include_ligands") = false, // py::arg("include_water") = false, // py::arg("include_other") = false, // py::arg("model_id") = "", // py::doc(kMmcifFilter + 1) // ); m.def("fix_residues", MmcifFixResidues, py::arg("layout"), // py::arg("comp_id"), // py::arg("atom_id"), // py::arg("atom_x"), // py::arg("atom_y"), // py::arg("atom_z"), // py::arg("fix_arg") = false, // py::doc(kMmcifFixResidues + 1) // ); m.def("selected_polymer_residue_mask", SelectedPolymerResidueMask, py::arg("layout"), // py::arg("atom_site_label_asym_ids"), // py::arg("atom_site_label_seq_ids"), // py::arg("atom_site_label_comp_ids"), // py::arg("poly_seq_asym_ids"), // py::arg("poly_seq_seq_ids"), // py::arg("poly_seq_mon_ids"), // py::call_guard(), // py::doc(kSelectedPolymerResidueMask + 1) // ); m.def("selected_ligand_residue_mask", SelectedLigandResidueMask, py::arg("layout"), // py::arg("atom_site_label_asym_ids"), // py::arg("atom_site_label_seq_ids"), // py::arg("atom_site_auth_seq_ids"), // py::arg("atom_site_label_comp_ids"), // py::arg("atom_site_pdbx_pdb_ins_codes"), // py::arg("nonpoly_asym_ids"), // py::arg("nonpoly_auth_seq_ids"), // py::arg("nonpoly_pdb_ins_codes"), // py::arg("nonpoly_mon_ids"), // py::arg("branch_asym_ids"), // py::arg("branch_auth_seq_ids"), // py::arg("branch_pdb_ins_codes"), // py::arg("branch_mon_ids"), // py::call_guard(), // py::doc(kSelectedLigandResidueMask + 1) // ); } } // namespace alphafold3