AlphaFold3 / flax_model /alphafold3 /structure /cpp /mmcif_utils_pybind.cc
OneScience's picture
Upload folder using huggingface_hub (part 2)
3e62986 verified
Raw
History Blame Contribute Delete
30.1 kB
// 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 <Python.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <new>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#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<PyObject, PyObjectDeleter>;
using StringArrayRef = absl::Span<const std::string>;
using Indexer = absl::flat_hash_map<absl::string_view, std::size_t>;
// 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 <typename C, typename... Cs>
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<absl::string_view> 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<absl::string_view> permitted_polymers{"polypeptide(L)"};
absl::flat_hash_set<absl::string_view> 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<absl::string_view> permitted_nonpoly_entity_types;
absl::flat_hash_set<absl::string_view> 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<absl::string_view> 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<PyObject*> atom_ids, absl::Span<const float> atom_x,
absl::Span<const float> atom_y, absl::Span<const float> 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<py::object, MmcifLayout> 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<std::vector<std::uint64_t>> 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<std::size_t> 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<std::uint64_t>(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<npy_intp>(layout.num_models()),
static_cast<npy_intp>(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<std::vector<std::size_t>*>(memory);
});
PyArray_SetBaseObject(reinterpret_cast<PyArrayObject*>(arr), capsule);
return std::make_pair(py::reinterpret_steal<py::object>(arr),
std::move(layout));
}
void MmcifFixResidues( //
const MmcifLayout& layout, //
absl::Span<PyObject*> comp_id, //
absl::Span<PyObject*> atom_id, //
absl::Span<const float> atom_x, //
absl::Span<const float> atom_y, //
absl::Span<const float> atom_z, //
bool fix_arginine //
) {
std::optional<FixArginine> 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<bool> SelectedPolymerResidueMask(
const MmcifLayout& layout,
const std::vector<absl::string_view>& atom_site_label_asym_ids, //
const std::vector<absl::string_view>& atom_site_label_seq_ids, //
const std::vector<absl::string_view>& atom_site_label_comp_ids, //
const std::vector<absl::string_view>& poly_seq_asym_ids, //
const std::vector<absl::string_view>& poly_seq_seq_ids, //
const std::vector<absl::string_view>& poly_seq_mon_ids //
) {
absl::flat_hash_map<std::pair<absl::string_view, absl::string_view>,
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<std::size_t>& 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<bool> 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<bool>, std::vector<bool>> SelectedLigandResidueMask(
const MmcifLayout& layout, //
const std::vector<absl::string_view>& atom_site_label_asym_ids, //
const std::vector<absl::string_view>& atom_site_label_seq_ids, //
const std::vector<absl::string_view>& atom_site_auth_seq_ids, //
const std::vector<absl::string_view>& atom_site_label_comp_ids, //
const std::vector<absl::string_view>& atom_site_pdbx_pdb_ins_codes, //
const std::vector<absl::string_view>& nonpoly_asym_ids, //
const std::vector<absl::string_view>& nonpoly_auth_seq_ids, //
const std::vector<absl::string_view>& nonpoly_pdb_ins_codes, //
const std::vector<absl::string_view>& nonpoly_mon_ids, //
const std::vector<absl::string_view>& branch_asym_ids, //
const std::vector<absl::string_view>& branch_auth_seq_ids, //
const std::vector<absl::string_view>& branch_pdb_ins_codes, //
const std::vector<absl::string_view>& branch_mon_ids) {
absl::flat_hash_map<
std::tuple<absl::string_view, absl::string_view, absl::string_view>,
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<std::size_t>& 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<bool> 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<bool> 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::gil_scoped_release>(), //
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::gil_scoped_release>(), //
py::doc(kSelectedLigandResidueMask + 1) //
);
}
} // namespace alphafold3