OneScience commited on
Commit
3e62986
·
verified ·
1 Parent(s): 35cdf53

Upload folder using huggingface_hub (part 2)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. flax_model/alphafold3/structure/cpp/mmcif_altlocs.h +51 -0
  2. flax_model/alphafold3/structure/cpp/mmcif_atom_site.pyi +15 -0
  3. flax_model/alphafold3/structure/cpp/mmcif_atom_site_pybind.cc +83 -0
  4. flax_model/alphafold3/structure/cpp/mmcif_atom_site_pybind.h +24 -0
  5. flax_model/alphafold3/structure/cpp/mmcif_layout.h +146 -0
  6. flax_model/alphafold3/structure/cpp/mmcif_layout.pyi +18 -0
  7. flax_model/alphafold3/structure/cpp/mmcif_layout_lib.cc +213 -0
  8. flax_model/alphafold3/structure/cpp/mmcif_layout_pybind.cc +49 -0
  9. flax_model/alphafold3/structure/cpp/mmcif_layout_pybind.h +24 -0
  10. flax_model/alphafold3/structure/cpp/mmcif_struct_conn.h +34 -0
  11. flax_model/alphafold3/structure/cpp/mmcif_struct_conn.pyi +5 -0
  12. flax_model/alphafold3/structure/cpp/mmcif_struct_conn_lib.cc +380 -0
  13. flax_model/alphafold3/structure/cpp/mmcif_struct_conn_pybind.cc +68 -0
  14. flax_model/alphafold3/structure/cpp/mmcif_struct_conn_pybind.h +24 -0
  15. flax_model/alphafold3/structure/cpp/mmcif_utils.pyi +63 -0
  16. flax_model/alphafold3/structure/cpp/mmcif_utils_pybind.cc +787 -0
  17. flax_model/alphafold3/structure/cpp/mmcif_utils_pybind.h +24 -0
  18. flax_model/alphafold3/structure/cpp/string_array.pyi +42 -0
  19. flax_model/alphafold3/structure/cpp/string_array_pybind.cc +329 -0
  20. flax_model/alphafold3/structure/cpp/string_array_pybind.h +24 -0
  21. flax_model/alphafold3/structure/mmcif.py +321 -0
  22. flax_model/alphafold3/structure/parsing.py +1801 -0
  23. flax_model/alphafold3/structure/sterics.py +132 -0
  24. flax_model/alphafold3/structure/structure.py +0 -0
  25. flax_model/alphafold3/structure/structure_tables.py +811 -0
  26. flax_model/alphafold3/structure/table.py +555 -0
  27. flax_model/alphafold3/structure/test_utils.py +161 -0
  28. flax_model/alphafold3/test_data/__init__.py +0 -0
  29. flax_model/alphafold3/test_data/featurised_example.json +68 -0
  30. flax_model/alphafold3/test_data/miniature_databases/__init__.py +0 -0
  31. flax_model/alphafold3/test_data/miniature_databases/bfd-first_non_consensus_sequences__subsampled_1000.fasta +0 -0
  32. flax_model/alphafold3/test_data/miniature_databases/mgy_clusters__subsampled_1000.fa +0 -0
  33. flax_model/alphafold3/test_data/miniature_databases/nt_rna_2023_02_23_clust_seq_id_90_cov_80_rep_seq__subsampled_1000.fasta +0 -0
  34. flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/5y2e.cif +562 -0
  35. flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/6s61.cif +0 -0
  36. flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/6ydw.cif +0 -0
  37. flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/7rye.cif +893 -0
  38. flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/__init__.py +0 -0
  39. flax_model/alphafold3/test_data/miniature_databases/pdb_seqres_2022_09_28__subsampled_1000.fasta +0 -0
  40. flax_model/alphafold3/test_data/miniature_databases/rfam_14_4_clustered_rep_seq__subsampled_1000.fasta +0 -0
  41. flax_model/alphafold3/test_data/miniature_databases/rnacentral_active_seq_id_90_cov_80_linclust__subsampled_1000.fasta +0 -0
  42. flax_model/alphafold3/test_data/miniature_databases/uniprot_all__subsampled_1000.fasta +0 -0
  43. flax_model/alphafold3/test_data/miniature_databases/uniref90__subsampled_1000.fasta +0 -0
  44. flax_model/alphafold3/test_data/model_config.json +231 -0
  45. flax_model/alphafold3/version.py +13 -0
  46. inputs/7r6r_data.json +0 -0
  47. inputs/t1119_search.json +19 -0
  48. scripts/infer.sh +28 -0
  49. scripts/infer_jackhmmer.sh +53 -0
  50. scripts/infer_mmseqs.sh +42 -0
flax_model/alphafold3/structure/cpp/mmcif_altlocs.h ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Copyright 2024 DeepMind Technologies Limited
3
+ *
4
+ * AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
5
+ * this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
6
+ *
7
+ * To request access to the AlphaFold 3 model parameters, follow the process set
8
+ * out at https://github.com/google-deepmind/alphafold3. You may only use these
9
+ * if received directly from Google. Use is subject to terms of use available at
10
+ * https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
11
+ */
12
+
13
+ #ifndef ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_ALTLOCS_H_
14
+ #define ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_ALTLOCS_H_
15
+
16
+ #include <cstddef>
17
+ #include <cstdint>
18
+ #include <string>
19
+ #include <vector>
20
+
21
+ #include "absl/types/span.h"
22
+ #include "alphafold3/structure/cpp/mmcif_layout.h"
23
+
24
+ namespace alphafold3 {
25
+
26
+ // Returns the list of indices that should be kept after resolving alt-locs.
27
+ // 1) Partial Residue. Each cycle of alt-locs are resolved separately with the
28
+ // highest occupancy alt-loc. Tie-breaks are resolved alphabetically. See
29
+ // tests for examples.
30
+ // 2) Whole Residue. These are resolved in two passes.
31
+ // a) The residue with the highest occupancy is chosen.
32
+ // b) The locations for a given residue are resolved.
33
+ // All tie-breaks are resolved alphabetically. See tests for examples.
34
+ //
35
+ // Preconditions: layout and comp_ids, alt_ids, occupancies are all from same
36
+ // mmCIF file and chain_indices are monotonically increasing and less than
37
+ // layout.num_chains().
38
+ //
39
+ // comp_ids from '_atom_site.label_comp_id'.
40
+ // alt_ids from '_atom_site.label_alt_id'.
41
+ // occupancies from '_atom_site.occupancy'.
42
+ std::vector<std::uint64_t> ResolveMmcifAltLocs(
43
+ const MmcifLayout& layout, absl::Span<const std::string> comp_ids,
44
+ absl::Span<const std::string> atom_ids,
45
+ absl::Span<const std::string> alt_ids,
46
+ absl::Span<const std::string> occupancies,
47
+ absl::Span<const std::size_t> chain_indices);
48
+
49
+ } // namespace alphafold3
50
+
51
+ #endif // ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_ALTLOCS_H_
flax_model/alphafold3/structure/cpp/mmcif_atom_site.pyi ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ from collections.abc import Callable
4
+ from flax_model.alphafold3.cpp import cif_dict
5
+
6
+
7
+ def get_internal_to_author_chain_id_map(
8
+ mmcif: cif_dict.CifDict
9
+ ) -> dict[str,str]: ...
10
+
11
+
12
+ def get_or_infer_type_symbol(
13
+ mmcif: cif_dict.CifDict,
14
+ atom_id_to_type_symbol: Callable[[str, str], str],
15
+ ) -> list[str]: ...
flax_model/alphafold3/structure/cpp/mmcif_atom_site_pybind.cc ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright 2024 DeepMind Technologies Limited
2
+ //
3
+ // AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
4
+ // this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
5
+ //
6
+ // To request access to the AlphaFold 3 model parameters, follow the process set
7
+ // out at https://github.com/google-deepmind/alphafold3. You may only use these
8
+ // if received directly from Google. Use is subject to terms of use available at
9
+ // https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
10
+
11
+ #include <cstddef>
12
+
13
+ #include "absl/container/flat_hash_map.h"
14
+ #include "absl/log/check.h"
15
+ #include "absl/strings/string_view.h"
16
+ #include "absl/types/span.h"
17
+ #include "alphafold3/parsers/cpp/cif_dict_lib.h"
18
+ #include "pybind11/gil.h"
19
+ #include "pybind11/pybind11.h"
20
+ #include "pybind11/pytypes.h"
21
+ #include "pybind11/stl.h"
22
+ #include "pybind11_abseil/absl_casters.h"
23
+
24
+ namespace alphafold3 {
25
+ namespace {
26
+ namespace py = pybind11;
27
+
28
+ // If present, returns the _atom_site.type_symbol. If not, infers it using
29
+ // _atom_site.label_comp_id (residue name), _atom_site.label_atom_id (atom name)
30
+ // and the CCD.
31
+ py::list GetOrInferTypeSymbol(const CifDict& mmcif,
32
+ const py::object& atom_id_to_type_symbol) {
33
+ const auto& type_symbol = mmcif["_atom_site.type_symbol"];
34
+ const int num_atom = mmcif["_atom_site.id"].size();
35
+ py::list patched_type_symbol(num_atom);
36
+ if (type_symbol.empty()) {
37
+ const auto& label_comp_id = mmcif["_atom_site.label_comp_id"];
38
+ const auto& label_atom_id = mmcif["_atom_site.label_atom_id"];
39
+ CHECK_EQ(label_comp_id.size(), num_atom);
40
+ CHECK_EQ(label_atom_id.size(), num_atom);
41
+ for (int i = 0; i < num_atom; i++) {
42
+ patched_type_symbol[i] =
43
+ atom_id_to_type_symbol(label_comp_id[i], label_atom_id[i]);
44
+ }
45
+ } else {
46
+ for (int i = 0; i < num_atom; i++) {
47
+ patched_type_symbol[i] = type_symbol[i];
48
+ }
49
+ }
50
+ return patched_type_symbol;
51
+ }
52
+
53
+ absl::flat_hash_map<absl::string_view, absl::string_view>
54
+ GetInternalToAuthorChainIdMap(const CifDict& mmcif) {
55
+ const auto& label_asym_ids = mmcif["_atom_site.label_asym_id"];
56
+ const auto& auth_asym_ids = mmcif["_atom_site.auth_asym_id"];
57
+ CHECK_EQ(label_asym_ids.size(), auth_asym_ids.size());
58
+
59
+ absl::flat_hash_map<absl::string_view, absl::string_view> mapping;
60
+ for (size_t i = 0, num_rows = label_asym_ids.size(); i < num_rows; ++i) {
61
+ // Use only the first internal_chain_id occurrence to generate the mapping.
62
+ // It should not matter as there should not be a case where a single
63
+ // internal chain ID would map to more than one author chain IDs (i.e. the
64
+ // mapping should be injective). Since we need this method to be fast, we
65
+ // choose not to check it.
66
+ mapping.emplace(label_asym_ids[i], auth_asym_ids[i]);
67
+ }
68
+ return mapping;
69
+ }
70
+
71
+ } // namespace
72
+
73
+ namespace py = pybind11;
74
+
75
+ void RegisterModuleMmcifAtomSite(pybind11::module m) {
76
+ m.def("get_or_infer_type_symbol", &GetOrInferTypeSymbol, py::arg("mmcif"),
77
+ py::arg("atom_id_to_type_symbol"));
78
+
79
+ m.def("get_internal_to_author_chain_id_map", &GetInternalToAuthorChainIdMap,
80
+ py::arg("mmcif"), py::call_guard<py::gil_scoped_release>());
81
+ }
82
+
83
+ } // namespace alphafold3
flax_model/alphafold3/structure/cpp/mmcif_atom_site_pybind.h ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Copyright 2024 DeepMind Technologies Limited
3
+ *
4
+ * AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
5
+ * this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
6
+ *
7
+ * To request access to the AlphaFold 3 model parameters, follow the process set
8
+ * out at https://github.com/google-deepmind/alphafold3. You may only use these
9
+ * if received directly from Google. Use is subject to terms of use available at
10
+ * https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
11
+ */
12
+
13
+ #ifndef ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_ATOM_SITE_PYBIND_H_
14
+ #define ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_ATOM_SITE_PYBIND_H_
15
+
16
+ #include "pybind11/pybind11.h"
17
+
18
+ namespace alphafold3 {
19
+
20
+ void RegisterModuleMmcifAtomSite(pybind11::module m);
21
+
22
+ }
23
+
24
+ #endif // ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_ATOM_SITE_PYBIND_H_
flax_model/alphafold3/structure/cpp/mmcif_layout.h ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Copyright 2024 DeepMind Technologies Limited
3
+ *
4
+ * AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
5
+ * this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
6
+ *
7
+ * To request access to the AlphaFold 3 model parameters, follow the process set
8
+ * out at https://github.com/google-deepmind/alphafold3. You may only use these
9
+ * if received directly from Google. Use is subject to terms of use available at
10
+ * https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
11
+ */
12
+
13
+ #ifndef ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_LAYOUT_H_
14
+ #define ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_LAYOUT_H_
15
+
16
+ #include <cstddef>
17
+ #include <cstdint>
18
+ #include <string>
19
+ #include <utility>
20
+ #include <vector>
21
+
22
+ #include "absl/status/statusor.h"
23
+ #include "absl/strings/string_view.h"
24
+ #include "absl/types/span.h"
25
+ #include "alphafold3/parsers/cpp/cif_dict_lib.h"
26
+
27
+ namespace alphafold3 {
28
+
29
+ // Holds the layout of a parsed mmCIF file.
30
+ class MmcifLayout {
31
+ public:
32
+ MmcifLayout(std::vector<std::size_t> chain_ends,
33
+ std::vector<std::size_t> residues, std::size_t model_offset,
34
+ std::size_t num_models)
35
+ : chain_ends_(std::move(chain_ends)),
36
+ residue_ends_(std::move(residues)),
37
+ model_offset_(model_offset),
38
+ num_models_(num_models) {}
39
+
40
+ // Reads a layout from a valid parsed mmCIF. If a valid model_id is provided
41
+ // the offsets will select that model from the mmCIF.
42
+ // If no model_id is specified, we calculate the layout of the first model
43
+ // only. Therefore it is a requirement that each model has identical atom
44
+ // layouts. An error is returned if the atom counts do not between models.
45
+ static absl::StatusOr<MmcifLayout> Create(const CifDict& mmcif,
46
+ absl::string_view model_id = "");
47
+
48
+ std::string ToDebugString() const;
49
+
50
+ // Returns the start index and one past the last residue index of a given
51
+ // chain. A chain_index of n refers to the n-th chain in the mmCIF. The
52
+ // returned residue indices are 0-based enumerations of residues in the
53
+ // _atom_site records, and therefore do not include missing residues.
54
+ std::pair<std::size_t, std::size_t> residue_range(
55
+ std::size_t chain_index) const {
56
+ if (chain_index > 0) {
57
+ return {chain_ends_[chain_index - 1], chain_ends_[chain_index]};
58
+ } else {
59
+ return {0, chain_ends_[0]};
60
+ }
61
+ }
62
+
63
+ // Returns the start index and one past the last index of a given residue.
64
+ // A residue_index of n refers to the n-th residue in the mmCIF, not
65
+ // including residues that are unresolved (i.e. only using _atom_site).
66
+ std::pair<std::size_t, std::size_t> atom_range(
67
+ std::size_t residue_index) const {
68
+ if (residue_index > 0) {
69
+ return {residue_ends_[residue_index - 1], residue_ends_[residue_index]};
70
+ } else {
71
+ return {model_offset_, residue_ends_[residue_index]};
72
+ }
73
+ }
74
+
75
+ // If model_id was provided during construction then this is 1, otherwise
76
+ // it is the number of models present in the mmCIF.
77
+ std::size_t num_models() const { return num_models_; }
78
+ // The number of atoms in the chosen model.
79
+ std::size_t num_atoms() const {
80
+ return residue_ends_.empty() ? 0 : residue_ends_.back() - model_offset_;
81
+ }
82
+ // The number of chains in the chosen model.
83
+ std::size_t num_chains() const { return chain_ends_.size(); }
84
+ // The number of residues in the chosen model, not counting unresolved
85
+ // residues.
86
+ std::size_t num_residues() const { return residue_ends_.size(); }
87
+
88
+ // Returns the first atom index that is part of the specified chain.
89
+ // The chain is specified using chain_index, which is a 0-based
90
+ // enumeration of the chains in the _atom_site table.
91
+ std::size_t atom_site_from_chain_index(std::size_t chain_index) const {
92
+ if (chain_index == 0) {
93
+ return model_offset_;
94
+ }
95
+ return atom_site_from_residue_index(chain_ends_[chain_index - 1]);
96
+ }
97
+
98
+ // Returns the first atom index that is part of the specified residue.
99
+ // The residue is specified using residue_index, which is a 0-based
100
+ // enumeration of the residues in the _atom_site table.
101
+ std::size_t atom_site_from_residue_index(std::size_t residues_index) const {
102
+ if (residues_index == 0) {
103
+ return model_offset_;
104
+ }
105
+ return residue_ends_[residues_index - 1];
106
+ }
107
+
108
+ // One past last residue index of each chain. The residue index does not
109
+ // include unresolved residues and is a simple 0-based enumeration of the
110
+ // residues in _atom_site table.
111
+ const std::vector<std::size_t>& chains() const { return chain_ends_; }
112
+
113
+ // Indices of the first atom of each chain. Note that this returns atom
114
+ // indices (like residue_starts()), not residue indices (like chains()).
115
+ std::vector<std::size_t> chain_starts() const;
116
+
117
+ // One past last atom index of each residue.
118
+ const std::vector<std::size_t>& residues() const { return residue_ends_; }
119
+
120
+ // Indices of the first atom of each residue.
121
+ std::vector<std::size_t> residue_starts() const {
122
+ std::vector<std::size_t> residue_starts;
123
+ if (!residue_ends_.empty()) {
124
+ residue_starts.reserve(residue_ends_.size());
125
+ residue_starts.push_back(model_offset_);
126
+ residue_starts.insert(residue_starts.end(), residue_ends_.begin(),
127
+ residue_ends_.end() - 1);
128
+ }
129
+ return residue_starts;
130
+ }
131
+
132
+ // The first atom index that is part of the specified model.
133
+ std::size_t model_offset() const { return model_offset_; }
134
+
135
+ void Filter(absl::Span<const std::uint64_t> keep_indices);
136
+
137
+ private:
138
+ std::vector<std::size_t> chain_ends_;
139
+ std::vector<std::size_t> residue_ends_;
140
+ std::size_t model_offset_;
141
+ std::size_t num_models_;
142
+ };
143
+
144
+ } // namespace alphafold3
145
+
146
+ #endif // ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_LAYOUT_H_
flax_model/alphafold3/structure/cpp/mmcif_layout.pyi ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ from flax_model.alphafold3.cpp import cif_dict
4
+
5
+ class MmcifLayout:
6
+ def atom_range(self, residue_index: int) -> tuple[int, int]: ...
7
+ def chain_starts(self) -> list[int]: ...
8
+ def chains(self) -> list[int]: ...
9
+ def model_offset(self) -> int: ...
10
+ def num_atoms(self) -> int: ...
11
+ def num_chains(self) -> int: ...
12
+ def num_models(self) -> int: ...
13
+ def num_residues(self) -> int: ...
14
+ def residue_range(self, chain_index: int) -> tuple[int, int]: ...
15
+ def residue_starts(self) -> list[int]: ...
16
+ def residues(self) -> list[int]: ...
17
+
18
+ def from_mmcif(mmcif: cif_dict.CifDict, model_id: str = ...) -> MmcifLayout: ...
flax_model/alphafold3/structure/cpp/mmcif_layout_lib.cc ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright 2024 DeepMind Technologies Limited
2
+ //
3
+ // AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
4
+ // this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
5
+ //
6
+ // To request access to the AlphaFold 3 model parameters, follow the process set
7
+ // out at https://github.com/google-deepmind/alphafold3. You may only use these
8
+ // if received directly from Google. Use is subject to terms of use available at
9
+ // https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
10
+
11
+ #include <algorithm>
12
+ #include <cstddef>
13
+ #include <cstdint>
14
+ #include <functional>
15
+ #include <iterator>
16
+ #include <string>
17
+ #include <utility>
18
+ #include <vector>
19
+
20
+ #include "absl/algorithm/container.h"
21
+ #include "absl/status/status.h"
22
+ #include "absl/status/statusor.h"
23
+ #include "absl/strings/str_cat.h"
24
+ #include "absl/strings/str_format.h"
25
+ #include "absl/strings/string_view.h"
26
+ #include "absl/types/span.h"
27
+ #include "alphafold3/parsers/cpp/cif_dict_lib.h"
28
+ #include "alphafold3/structure/cpp/mmcif_layout.h"
29
+
30
+ namespace alphafold3 {
31
+
32
+ std::string MmcifLayout::ToDebugString() const {
33
+ return absl::StrFormat(
34
+ "MmcifLayout(models=%d, chains=%d, num_residues=%d, atoms=%d)",
35
+ num_models(), num_chains(), num_residues(), num_atoms());
36
+ }
37
+
38
+ // Changes layout to match keep_indices removing empty chains/residues.
39
+ void MmcifLayout::Filter(absl::Span<const std::uint64_t> keep_indices) {
40
+ if (num_chains() == 0) {
41
+ return;
42
+ }
43
+ // Update residue indices.
44
+ auto keep_it = absl::c_lower_bound(keep_indices, residue_ends_.front());
45
+ for (auto& residue : residue_ends_) {
46
+ while (keep_it != keep_indices.end() && *keep_it < residue) {
47
+ ++keep_it;
48
+ }
49
+ residue = std::distance(keep_indices.begin(), keep_it);
50
+ }
51
+ // Unique residue_ends_ with updating chains.
52
+ auto first = residue_ends_.begin();
53
+ auto tail = first;
54
+ std::size_t num_skipped = 0;
55
+ std::size_t current = 0;
56
+ for (std::size_t& chain_end : chain_ends_) {
57
+ for (auto e = residue_ends_.begin() + chain_end; first != e; ++first) {
58
+ std::size_t next = *first;
59
+ *tail = next;
60
+ if (current != next) {
61
+ current = next;
62
+ ++tail;
63
+ } else {
64
+ ++num_skipped;
65
+ }
66
+ }
67
+ chain_end -= num_skipped;
68
+ }
69
+ residue_ends_.erase(tail, residue_ends_.end());
70
+
71
+ current = 0;
72
+ chain_ends_.erase(std::remove_if(chain_ends_.begin(), chain_ends_.end(),
73
+ [&current](std::size_t next) {
74
+ bool result = current == next;
75
+ current = next;
76
+ return result;
77
+ }),
78
+ chain_ends_.end());
79
+ model_offset_ = 0;
80
+ }
81
+
82
+ absl::StatusOr<MmcifLayout> MmcifLayout::Create(const CifDict& mmcif,
83
+ absl::string_view model_id) {
84
+ auto model_ids = mmcif["_atom_site.pdbx_PDB_model_num"];
85
+ auto chain_ids = mmcif["_atom_site.label_asym_id"]; // chain ID.
86
+ auto label_seq_ids = mmcif["_atom_site.label_seq_id"]; // residue ID.
87
+ auto auth_seq_ids = mmcif["_atom_site.auth_seq_id"]; // author residue ID.
88
+ auto insertion_codes = mmcif["_atom_site.pdbx_PDB_ins_code"];
89
+
90
+ if (model_ids.size() != chain_ids.size() ||
91
+ model_ids.size() != label_seq_ids.size() ||
92
+ (model_ids.size() != auth_seq_ids.size() && !auth_seq_ids.empty()) ||
93
+ (model_ids.size() != insertion_codes.size() &&
94
+ !insertion_codes.empty())) {
95
+ return absl::InvalidArgumentError(absl::StrCat(
96
+ "Invalid _atom_site table.", //
97
+ " len(_atom_site.pdbx_PDB_model_num): ", model_ids.size(),
98
+ " len(_atom_site.label_asym_id): ", chain_ids.size(),
99
+ " len(_atom_site.label_seq_id): ", label_seq_ids.size(),
100
+ " len(_atom_site.auth_seq_id): ", auth_seq_ids.size(),
101
+ " len(_atom_site.pdbx_PDB_ins_code): ", insertion_codes.size()));
102
+ }
103
+ std::size_t num_atoms = model_ids.size();
104
+ if (num_atoms == 0) {
105
+ return MmcifLayout({}, {}, 0, 0);
106
+ }
107
+ std::size_t model_offset = 0;
108
+ std::size_t num_models;
109
+ std::size_t num_atoms_per_model;
110
+ if (model_id.empty()) {
111
+ absl::string_view first_model_id = model_ids.front();
112
+
113
+ // Binary search for where the first model ends.
114
+ num_atoms_per_model = std::distance(
115
+ model_ids.begin(),
116
+ absl::c_upper_bound(model_ids, first_model_id, std::not_equal_to<>{}));
117
+ if (num_atoms % num_atoms_per_model != 0) {
118
+ return absl::InvalidArgumentError(absl::StrCat(
119
+ "Each model must have the same number of atoms: (", num_atoms, " % ",
120
+ num_atoms_per_model, " == ", num_atoms % num_atoms_per_model, ")."));
121
+ }
122
+ num_models = num_atoms / num_atoms_per_model;
123
+ // Test boundary conditions for each model hold.
124
+ for (std::size_t i = 1; i < num_models; ++i) {
125
+ if ((model_ids[i * num_atoms_per_model] !=
126
+ model_ids[(i + 1) * num_atoms_per_model - 1]) ||
127
+ (model_ids[i * num_atoms_per_model - 1] ==
128
+ model_ids[i * num_atoms_per_model])) {
129
+ return absl::InvalidArgumentError(
130
+ absl::StrCat("Each model must have the same number of atoms: (",
131
+ num_atoms, " % ", num_atoms_per_model,
132
+ " == ", num_atoms % num_atoms_per_model, ")."));
133
+ }
134
+ }
135
+ } else {
136
+ num_models = 1;
137
+ model_offset =
138
+ std::distance(model_ids.begin(), absl::c_find(model_ids, model_id));
139
+ if (model_offset == model_ids.size()) {
140
+ return absl::InvalidArgumentError(
141
+ absl::StrCat("Unknown model_id: ", model_id));
142
+ }
143
+ model_ids.remove_prefix(model_offset);
144
+ chain_ids.remove_prefix(model_offset);
145
+ label_seq_ids.remove_prefix(model_offset);
146
+ if (!auth_seq_ids.empty()) auth_seq_ids.remove_prefix(model_offset);
147
+ if (!insertion_codes.empty()) insertion_codes.remove_prefix(model_offset);
148
+
149
+ num_atoms_per_model = std::distance(
150
+ model_ids.begin(), std::upper_bound(model_ids.begin(), model_ids.end(),
151
+ model_id, std::not_equal_to<>{}));
152
+ num_atoms = num_atoms_per_model;
153
+ }
154
+ std::vector<std::size_t> residues;
155
+ std::vector<std::size_t> chains;
156
+ absl::string_view chain_id = chain_ids.front();
157
+ if (!auth_seq_ids.empty() && !insertion_codes.empty()) {
158
+ // If author residue IDs are present then these are preferred to
159
+ // label residue IDs because they work for multi-residue ligands (which
160
+ // are given constant "." label residue IDs).
161
+ // NB: Author residue IDs require both the auth_seq_id and the insertion
162
+ // code to be unique.
163
+ absl::string_view auth_seq_id = auth_seq_ids.front();
164
+ absl::string_view insertion_code = insertion_codes.front();
165
+ for (std::size_t i = 1; i < num_atoms_per_model; ++i) {
166
+ if (absl::string_view current_chain_id = chain_ids[i];
167
+ current_chain_id != chain_id) {
168
+ residues.push_back(i + model_offset);
169
+ chains.push_back(residues.size());
170
+ chain_id = current_chain_id;
171
+ auth_seq_id = auth_seq_ids[i];
172
+ insertion_code = insertion_codes[i];
173
+ } else if (absl::string_view current_seq_id = auth_seq_ids[i],
174
+ current_insertion_code = insertion_codes[i];
175
+ insertion_code != current_insertion_code ||
176
+ auth_seq_id != current_seq_id) {
177
+ residues.push_back(i + model_offset);
178
+ auth_seq_id = current_seq_id;
179
+ insertion_code = current_insertion_code;
180
+ }
181
+ }
182
+ } else {
183
+ absl::string_view label_seq_id = label_seq_ids.front();
184
+ for (std::size_t i = 1; i < num_atoms_per_model; ++i) {
185
+ if (absl::string_view current_chain_id = chain_ids[i];
186
+ current_chain_id != chain_id) {
187
+ residues.push_back(i + model_offset);
188
+ chains.push_back(residues.size());
189
+ chain_id = current_chain_id;
190
+ label_seq_id = label_seq_ids[i];
191
+ } else if (absl::string_view current_seq_id = label_seq_ids[i];
192
+ label_seq_id != current_seq_id) {
193
+ residues.push_back(i + model_offset);
194
+ label_seq_id = current_seq_id;
195
+ }
196
+ }
197
+ }
198
+ residues.push_back(num_atoms_per_model + model_offset);
199
+ chains.push_back(residues.size());
200
+ return MmcifLayout(std::move(chains), std::move(residues), model_offset,
201
+ num_models);
202
+ }
203
+
204
+ std::vector<std::size_t> MmcifLayout::chain_starts() const {
205
+ std::vector<std::size_t> chain_starts;
206
+ chain_starts.reserve(chain_ends_.size());
207
+ for (std::size_t index = 0; index < chain_ends_.size(); ++index) {
208
+ chain_starts.push_back(atom_site_from_chain_index(index));
209
+ }
210
+ return chain_starts;
211
+ }
212
+
213
+ } // namespace alphafold3
flax_model/alphafold3/structure/cpp/mmcif_layout_pybind.cc ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright 2024 DeepMind Technologies Limited
2
+ //
3
+ // AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
4
+ // this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
5
+ //
6
+ // To request access to the AlphaFold 3 model parameters, follow the process set
7
+ // out at https://github.com/google-deepmind/alphafold3. You may only use these
8
+ // if received directly from Google. Use is subject to terms of use available at
9
+ // https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
10
+
11
+ #include "alphafold3/structure/cpp/mmcif_layout.h"
12
+ #include "pybind11/pybind11.h"
13
+ #include "pybind11/pytypes.h"
14
+ #include "pybind11/stl.h"
15
+
16
+ namespace alphafold3 {
17
+
18
+ namespace py = pybind11;
19
+
20
+ void RegisterModuleMmcifLayout(pybind11::module m) {
21
+ py::class_<MmcifLayout>(m, "MmcifLayout")
22
+ .def("__str__", &MmcifLayout::ToDebugString)
23
+ .def("num_models", &MmcifLayout::num_models)
24
+ .def("num_chains", &MmcifLayout::num_chains)
25
+ .def("num_residues", &MmcifLayout::num_residues)
26
+ .def("num_atoms", &MmcifLayout::num_atoms)
27
+ .def("residue_range", &MmcifLayout::residue_range, py::arg("chain_index"))
28
+ .def("atom_range", &MmcifLayout::atom_range, py::arg("residue_index"))
29
+ .def("chains", &MmcifLayout::chains,
30
+ py::doc("Returns a list of indices one past the last residue of "
31
+ "each chain."))
32
+ .def(
33
+ "chain_starts", &MmcifLayout::chain_starts,
34
+ py::doc("Returns a list of indices of the first atom of each chain."))
35
+ .def("residues", &MmcifLayout::residues,
36
+ py::doc("Returns a list of indices one past the last atom of each "
37
+ "residue."))
38
+ .def("residue_starts", &MmcifLayout::residue_starts,
39
+ py::doc(
40
+ "Returns a list of indices of the first atom of each residue."))
41
+ .def("model_offset", &MmcifLayout::model_offset,
42
+ py::doc("Returns the first atom index that is part of the specified "
43
+ "model."));
44
+
45
+ m.def("from_mmcif", &MmcifLayout::Create, py::arg("mmcif"),
46
+ py::arg("model_id") = "");
47
+ }
48
+
49
+ } // namespace alphafold3
flax_model/alphafold3/structure/cpp/mmcif_layout_pybind.h ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Copyright 2024 DeepMind Technologies Limited
3
+ *
4
+ * AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
5
+ * this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
6
+ *
7
+ * To request access to the AlphaFold 3 model parameters, follow the process set
8
+ * out at https://github.com/google-deepmind/alphafold3. You may only use these
9
+ * if received directly from Google. Use is subject to terms of use available at
10
+ * https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
11
+ */
12
+
13
+ #ifndef ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_LAYOUT_PYBIND_H_
14
+ #define ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_LAYOUT_PYBIND_H_
15
+
16
+ #include "pybind11/pybind11.h"
17
+
18
+ namespace alphafold3 {
19
+
20
+ void RegisterModuleMmcifLayout(pybind11::module m);
21
+
22
+ }
23
+
24
+ #endif // ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_LAYOUT_PYBIND_H_
flax_model/alphafold3/structure/cpp/mmcif_struct_conn.h ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Copyright 2024 DeepMind Technologies Limited
3
+ *
4
+ * AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
5
+ * this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
6
+ *
7
+ * To request access to the AlphaFold 3 model parameters, follow the process set
8
+ * out at https://github.com/google-deepmind/alphafold3. You may only use these
9
+ * if received directly from Google. Use is subject to terms of use available at
10
+ * https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
11
+ */
12
+
13
+ #ifndef ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_STRUCT_CONN_H_
14
+ #define ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_STRUCT_CONN_H_
15
+
16
+ #include <utility>
17
+ #include <vector>
18
+
19
+ #include "absl/status/statusor.h"
20
+ #include "absl/strings/string_view.h"
21
+ #include "alphafold3/parsers/cpp/cif_dict_lib.h"
22
+
23
+ namespace alphafold3 {
24
+
25
+ // Returns a pair of atom indices for each row in the bonds table (aka
26
+ // _struct_conn). The indices are simple 0-based indexes into the columns of
27
+ // the _atom_site table in the input mmCIF, and do not necessarily correspond
28
+ // to the values in _atom_site.id, or any other column.
29
+ absl::StatusOr<std::pair<std::vector<std::size_t>, std::vector<std::size_t>>>
30
+ GetBondAtomIndices(const CifDict& mmcif, absl::string_view model_id);
31
+
32
+ } // namespace alphafold3
33
+
34
+ #endif // ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_STRUCT_CONN_H_
flax_model/alphafold3/structure/cpp/mmcif_struct_conn.pyi ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+
2
+
3
+ from flax_model.alphafold3.cpp import cif_dict
4
+
5
+ def get_bond_atom_indices(mmcif_dict: cif_dict.CifDict, model_id: str) -> tuple[list[int],list[int]]: ...
flax_model/alphafold3/structure/cpp/mmcif_struct_conn_lib.cc ADDED
@@ -0,0 +1,380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright 2024 DeepMind Technologies Limited
2
+ //
3
+ // AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
4
+ // this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
5
+ //
6
+ // To request access to the AlphaFold 3 model parameters, follow the process set
7
+ // out at https://github.com/google-deepmind/alphafold3. You may only use these
8
+ // if received directly from Google. Use is subject to terms of use available at
9
+ // https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
10
+
11
+ #include <cstddef>
12
+ #include <iterator>
13
+ #include <optional>
14
+ #include <string>
15
+ #include <utility>
16
+ #include <vector>
17
+
18
+ #include "absl/algorithm/container.h"
19
+ #include "absl/container/flat_hash_map.h"
20
+ #include "absl/container/flat_hash_set.h"
21
+ #include "absl/status/status.h"
22
+ #include "absl/status/statusor.h"
23
+ #include "absl/strings/str_cat.h"
24
+ #include "absl/strings/string_view.h"
25
+ #include "absl/types/span.h"
26
+ #include "alphafold3/parsers/cpp/cif_dict_lib.h"
27
+ #include "alphafold3/structure/cpp/mmcif_struct_conn.h"
28
+
29
+ namespace alphafold3 {
30
+
31
+ namespace {
32
+
33
+ struct AtomId {
34
+ absl::string_view chain_id;
35
+ absl::string_view res_id_1;
36
+ absl::string_view res_id_2;
37
+ absl::string_view atom_name;
38
+ absl::string_view alt_id;
39
+
40
+ friend bool operator==(const AtomId&, const AtomId&) = default;
41
+ template <typename H>
42
+ friend H AbslHashValue(H h, const AtomId& m) {
43
+ return H::combine(std::move(h), m.chain_id, m.res_id_1, m.res_id_2,
44
+ m.atom_name, m.alt_id);
45
+ }
46
+ };
47
+
48
+ using StringArrayRef = absl::Span<const std::string>;
49
+ using BondIndexByAtom = absl::flat_hash_map<AtomId, std::vector<std::size_t>>;
50
+ using BondAtomIndices = std::vector<std::size_t>;
51
+
52
+ // Returns whether each container is the same size.
53
+ template <typename C, typename... Cs>
54
+ bool AreSameSize(const C& c, const Cs&... cs) {
55
+ return ((c.size() == cs.size()) && ...);
56
+ }
57
+
58
+ struct ColumnSpec {
59
+ absl::string_view chain_id_col;
60
+ absl::string_view res_id_1_col;
61
+ absl::string_view res_id_2_col;
62
+ absl::string_view atom_name_col;
63
+ std::optional<absl::string_view> alt_id_col; // Not used by OpenMM.
64
+ };
65
+
66
+ class AtomColumns {
67
+ public:
68
+ static absl::StatusOr<AtomColumns> Create(const CifDict& mmcif,
69
+ const ColumnSpec& column_spec) {
70
+ StringArrayRef chain_id = mmcif[column_spec.chain_id_col];
71
+ StringArrayRef res_id_1 = mmcif[column_spec.res_id_1_col];
72
+ StringArrayRef res_id_2 = mmcif[column_spec.res_id_2_col];
73
+ StringArrayRef atom_name = mmcif[column_spec.atom_name_col];
74
+ if (!AreSameSize(chain_id, res_id_1, res_id_2, atom_name)) {
75
+ return absl::InvalidArgumentError(absl::StrCat(
76
+ "Atom columns are not the same size. ", //
77
+ "len(", column_spec.chain_id_col, ")=", chain_id.size(), //
78
+ ", len(", column_spec.res_id_1_col, ")=", res_id_1.size(), //
79
+ ", len(", column_spec.res_id_2_col, ")=", res_id_2.size(), //
80
+ ", len(", column_spec.atom_name_col, ")=", atom_name.size(), //
81
+ "."));
82
+ }
83
+ if (column_spec.alt_id_col.has_value()) {
84
+ StringArrayRef alt_id = mmcif[*column_spec.alt_id_col];
85
+ if (!AreSameSize(alt_id, chain_id)) {
86
+ return absl::InvalidArgumentError(absl::StrCat(
87
+ "Atom columns are not the same size. ", //
88
+ "len(", column_spec.chain_id_col, ")=", chain_id.size(), //
89
+ ", len(", *column_spec.alt_id_col, ")=", alt_id.size(), //
90
+ "."));
91
+ }
92
+ return AtomColumns(chain_id, res_id_1, res_id_2, atom_name, alt_id,
93
+ column_spec);
94
+ } else {
95
+ return AtomColumns(chain_id, res_id_1, res_id_2, atom_name, std::nullopt,
96
+ column_spec);
97
+ }
98
+ }
99
+
100
+ inline std::size_t size() const { return size_; }
101
+
102
+ absl::string_view GetNormalizedAltId(const std::size_t index) const {
103
+ constexpr absl::string_view kFullStop = ".";
104
+ if (alt_id_.has_value()) {
105
+ absl::string_view alt_id = (*alt_id_)[index];
106
+ return alt_id == "?" ? kFullStop : alt_id;
107
+ } else {
108
+ return kFullStop;
109
+ }
110
+ }
111
+
112
+ AtomId GetAtom(const std::size_t index) const {
113
+ return {.chain_id = chain_id_[index],
114
+ .res_id_1 = res_id_1_[index],
115
+ .res_id_2 = res_id_2_[index],
116
+ .atom_name = atom_name_[index],
117
+ .alt_id = GetNormalizedAltId(index)};
118
+ }
119
+
120
+ std::string GetAtomString(const std::size_t index) const {
121
+ std::string alt_id_col;
122
+ if (column_spec_.alt_id_col.has_value()) {
123
+ alt_id_col = *column_spec_.alt_id_col;
124
+ } else {
125
+ alt_id_col = "default label_alt_id";
126
+ }
127
+ return absl::StrCat(
128
+ column_spec_.chain_id_col, "=", chain_id_[index], ", ", //
129
+ column_spec_.res_id_1_col, "=", res_id_1_[index], ", ", //
130
+ column_spec_.res_id_2_col, "=", res_id_2_[index], ", ", //
131
+ column_spec_.atom_name_col, "=", atom_name_[index], ", ", //
132
+ alt_id_col, "=", GetNormalizedAltId(index)); //
133
+ }
134
+
135
+ private:
136
+ AtomColumns(StringArrayRef chain_id, StringArrayRef res_id_1,
137
+ StringArrayRef res_id_2, StringArrayRef atom_name,
138
+ std::optional<StringArrayRef> alt_id,
139
+ const ColumnSpec& column_spec)
140
+ : chain_id_(chain_id),
141
+ res_id_1_(res_id_1),
142
+ res_id_2_(res_id_2),
143
+ atom_name_(atom_name),
144
+ alt_id_(alt_id),
145
+ column_spec_(column_spec),
146
+ size_(chain_id.size()) {}
147
+ StringArrayRef chain_id_;
148
+ StringArrayRef res_id_1_;
149
+ StringArrayRef res_id_2_;
150
+ StringArrayRef atom_name_;
151
+ std::optional<StringArrayRef> alt_id_;
152
+ ColumnSpec column_spec_;
153
+ std::size_t size_;
154
+ };
155
+
156
+ // Adds the atom index to any rows in the bond table involving that atom.
157
+ absl::Status FillInBondsForAtom(const BondIndexByAtom& bond_index_by_atom,
158
+ const AtomId& atom,
159
+ const std::size_t atom_index,
160
+ BondAtomIndices& bond_atom_indices) {
161
+ if (auto bond_index_it = bond_index_by_atom.find(atom);
162
+ bond_index_it != bond_index_by_atom.end()) {
163
+ for (std::size_t bond_index : bond_index_it->second) {
164
+ if (bond_index < 0 || bond_index >= bond_atom_indices.size()) {
165
+ return absl::OutOfRangeError(
166
+ absl::StrCat("Bond index out of range: ", bond_index));
167
+ }
168
+ bond_atom_indices[bond_index] = atom_index;
169
+ }
170
+ }
171
+ return absl::OkStatus();
172
+ }
173
+
174
+ // Checks that the CifDict has all of the columns in the column spec.
175
+ bool HasAllColumns(const CifDict& mmcif, const ColumnSpec& columns) {
176
+ return mmcif.Contains(columns.chain_id_col) &&
177
+ mmcif.Contains(columns.res_id_1_col) &&
178
+ mmcif.Contains(columns.res_id_2_col) &&
179
+ mmcif.Contains(columns.atom_name_col) &&
180
+ (!columns.alt_id_col.has_value() ||
181
+ mmcif.Contains(*columns.alt_id_col));
182
+ }
183
+
184
+ // Fully specified ptnr1 atom.
185
+ constexpr ColumnSpec kStructConnPtnr1ColumnsFull{
186
+ .chain_id_col = "_struct_conn.ptnr1_label_asym_id",
187
+ .res_id_1_col = "_struct_conn.ptnr1_auth_seq_id",
188
+ .res_id_2_col = "_struct_conn.pdbx_ptnr1_PDB_ins_code",
189
+ .atom_name_col = "_struct_conn.ptnr1_label_atom_id",
190
+ .alt_id_col = "_struct_conn.pdbx_ptnr1_label_alt_id",
191
+ };
192
+
193
+ // Fully specified ptnr2 atom.
194
+ constexpr ColumnSpec kStructConnPtnr2ColumnsFull{
195
+ .chain_id_col = "_struct_conn.ptnr2_label_asym_id",
196
+ .res_id_1_col = "_struct_conn.ptnr2_auth_seq_id",
197
+ .res_id_2_col = "_struct_conn.pdbx_ptnr2_PDB_ins_code",
198
+ .atom_name_col = "_struct_conn.ptnr2_label_atom_id",
199
+ .alt_id_col = "_struct_conn.pdbx_ptnr2_label_alt_id",
200
+ };
201
+
202
+ // Columns used by OpenMM for ptnr1 atoms.
203
+ constexpr ColumnSpec kStructConnPtnr1OpenMM{
204
+ .chain_id_col = "_struct_conn.ptnr1_label_asym_id",
205
+ .res_id_1_col = "_struct_conn.ptnr1_label_seq_id",
206
+ .res_id_2_col = "_struct_conn.ptnr1_label_comp_id",
207
+ .atom_name_col = "_struct_conn.ptnr1_label_atom_id",
208
+ .alt_id_col = std::nullopt,
209
+ };
210
+
211
+ // Columns used by OpenMM for ptnr2 atoms.
212
+ constexpr ColumnSpec kStructConnPtnr2OpenMM{
213
+ .chain_id_col = "_struct_conn.ptnr2_label_asym_id",
214
+ .res_id_1_col = "_struct_conn.ptnr2_label_seq_id",
215
+ .res_id_2_col = "_struct_conn.ptnr2_label_comp_id",
216
+ .atom_name_col = "_struct_conn.ptnr2_label_atom_id",
217
+ .alt_id_col = std::nullopt,
218
+ };
219
+
220
+ // Fully specified atom sites.
221
+ constexpr ColumnSpec kAtomSiteColumnsFull{
222
+ .chain_id_col = "_atom_site.label_asym_id",
223
+ .res_id_1_col = "_atom_site.auth_seq_id",
224
+ .res_id_2_col = "_atom_site.pdbx_PDB_ins_code",
225
+ .atom_name_col = "_atom_site.label_atom_id",
226
+ .alt_id_col = "_atom_site.label_alt_id",
227
+ };
228
+
229
+ // Atom site columns used to match OpenMM _struct_conn tables.
230
+ constexpr ColumnSpec kAtomSiteColumnsOpenMM{
231
+ .chain_id_col = "_atom_site.label_asym_id",
232
+ .res_id_1_col = "_atom_site.label_seq_id",
233
+ .res_id_2_col = "_atom_site.label_comp_id",
234
+ .atom_name_col = "_atom_site.label_atom_id",
235
+ .alt_id_col = "_atom_site.label_alt_id",
236
+ };
237
+
238
+ } // namespace
239
+
240
+ absl::StatusOr<std::pair<BondAtomIndices, BondAtomIndices>> GetBondAtomIndices(
241
+ const CifDict& mmcif, absl::string_view model_id) {
242
+ ColumnSpec ptnr1_columns, ptnr2_columns, atom_site_columns;
243
+
244
+ if (HasAllColumns(mmcif, kStructConnPtnr1ColumnsFull) &&
245
+ HasAllColumns(mmcif, kStructConnPtnr2ColumnsFull)) {
246
+ ptnr1_columns = kStructConnPtnr1ColumnsFull;
247
+ ptnr2_columns = kStructConnPtnr2ColumnsFull;
248
+ atom_site_columns = kAtomSiteColumnsFull;
249
+ } else {
250
+ ptnr1_columns = kStructConnPtnr1OpenMM;
251
+ ptnr2_columns = kStructConnPtnr2OpenMM;
252
+ atom_site_columns = kAtomSiteColumnsOpenMM;
253
+ }
254
+
255
+ absl::StatusOr<AtomColumns> ptnr1_atoms =
256
+ AtomColumns::Create(mmcif, ptnr1_columns);
257
+ if (!ptnr1_atoms.ok()) {
258
+ return ptnr1_atoms.status();
259
+ }
260
+ absl::StatusOr<AtomColumns> ptnr2_atoms =
261
+ AtomColumns::Create(mmcif, ptnr2_columns);
262
+ if (!ptnr2_atoms.ok()) {
263
+ return ptnr2_atoms.status();
264
+ }
265
+ StringArrayRef struct_conn_id = mmcif["_struct_conn.id"];
266
+ if (!AreSameSize(struct_conn_id, *ptnr1_atoms, *ptnr2_atoms)) {
267
+ return absl::InvalidArgumentError(absl::StrCat(
268
+ "Invalid '_struct_conn.' loop. ", //
269
+ "len(id) = ", struct_conn_id.size(), ", ", //
270
+ "len(ptnr1_atoms) = ", ptnr1_atoms->size(), ", ", //
271
+ "len(ptnr2_atoms) = ", ptnr2_atoms->size(), "." //
272
+ ));
273
+ }
274
+
275
+ absl::StatusOr<AtomColumns> atoms =
276
+ AtomColumns::Create(mmcif, atom_site_columns);
277
+ if (!atoms.ok()) {
278
+ return atoms.status();
279
+ }
280
+ StringArrayRef atom_site_id = mmcif["_atom_site.id"];
281
+ StringArrayRef atom_site_model_id = mmcif["_atom_site.pdbx_PDB_model_num"];
282
+ if (!AreSameSize(atom_site_id, atom_site_model_id, *atoms)) {
283
+ return absl::InvalidArgumentError(absl::StrCat(
284
+ "Invalid '_atom_site.' loop. ", //
285
+ "len(id)= ", atom_site_id.size(), ", ", //
286
+ "len(pdbx_PDB_model_num)= ", atom_site_model_id.size(), ", ", //
287
+ "len(atoms)= ", atoms->size(), ".")); //
288
+ }
289
+
290
+ // Build maps from atom ID tuples to the rows in _struct_conn where that
291
+ // atom appears (NB could be multiple).
292
+ const std::size_t struct_conn_size = struct_conn_id.size();
293
+ BondIndexByAtom ptnr1_rows_by_atom(struct_conn_size);
294
+ BondIndexByAtom ptnr2_rows_by_atom(struct_conn_size);
295
+ for (std::size_t i = 0; i < struct_conn_size; ++i) {
296
+ ptnr1_rows_by_atom[ptnr1_atoms->GetAtom(i)].push_back(i);
297
+ ptnr2_rows_by_atom[ptnr2_atoms->GetAtom(i)].push_back(i);
298
+ }
299
+
300
+ // Allocate two output arrays with one element per row in struct_conn, where
301
+ // each element will be the index of that atom in the atom_site table.
302
+ // Fill the arrays with atom_site_size, which is an invalid value, so that
303
+ // we can check at the end that each atom has been found.
304
+ const std::size_t atom_site_size = atom_site_id.size();
305
+ BondAtomIndices ptnr1_atom_indices(struct_conn_size, atom_site_size);
306
+ BondAtomIndices ptnr2_atom_indices(struct_conn_size, atom_site_size);
307
+
308
+ bool model_id_ecountered = false;
309
+ absl::flat_hash_set<absl::string_view> seen_alt_ids;
310
+ for (std::size_t atom_i = 0; atom_i < atom_site_size; ++atom_i) {
311
+ if (atom_site_model_id[atom_i] != model_id) {
312
+ if (!model_id_ecountered) {
313
+ continue;
314
+ } else {
315
+ // Models are contiguous so once we see a different model ID after
316
+ // encountering our model ID then we can exit early.
317
+ break;
318
+ }
319
+ } else {
320
+ model_id_ecountered = true;
321
+ }
322
+ AtomId atom = atoms->GetAtom(atom_i);
323
+ seen_alt_ids.insert(atom.alt_id);
324
+
325
+ if (auto fill_in_bonds_status1 = FillInBondsForAtom(
326
+ ptnr1_rows_by_atom, atom, atom_i, ptnr1_atom_indices);
327
+ !fill_in_bonds_status1.ok()) {
328
+ return fill_in_bonds_status1;
329
+ }
330
+ if (auto fill_in_bonds_status2 = FillInBondsForAtom(
331
+ ptnr2_rows_by_atom, atom, atom_i, ptnr2_atom_indices);
332
+ !fill_in_bonds_status2.ok()) {
333
+ return fill_in_bonds_status2;
334
+ }
335
+ }
336
+ // The seen_alt_ids check is a workaround for a known PDB issue: some mmCIFs
337
+ // (2evw, 2g0v, 2g0x, 2g0z, 2g10, 2g11, 2g12, 2g14, 2grz, 2ntw as of 2024)
338
+ // have multiple models and they set different whole-chain altloc in each
339
+ // model. The bond table however doesn't distinguish between models, so there
340
+ // are bonds that are valid only for some models. E.g. 2grz has model 1 with
341
+ // chain A with altloc A, and model 2 with chain A with altloc B. The bonds
342
+ // table lists a bond for each of these.
343
+
344
+ // Check that a ptnr1 atom was found for every bond.
345
+ if (auto row_it = absl::c_find(ptnr1_atom_indices, atom_site_size);
346
+ row_it != ptnr1_atom_indices.end()) {
347
+ if (seen_alt_ids.size() > 1 || seen_alt_ids.contains(".") ||
348
+ seen_alt_ids.contains("?")) {
349
+ std::size_t i = std::distance(ptnr1_atom_indices.begin(), row_it);
350
+ return absl::InvalidArgumentError(
351
+ absl::StrCat("Error parsing \"", mmcif.GetDataName(), "\". ",
352
+ "Cannot find atom for bond ID ", struct_conn_id[i], ": ",
353
+ ptnr1_atoms->GetAtomString(i)));
354
+ }
355
+ }
356
+
357
+ // Check that a ptnr2 atom was found for every bond.
358
+ if (auto row_it = absl::c_find(ptnr2_atom_indices, atom_site_size);
359
+ row_it != ptnr2_atom_indices.end()) {
360
+ if (seen_alt_ids.size() > 1 || seen_alt_ids.contains(".") ||
361
+ seen_alt_ids.contains("?")) {
362
+ std::size_t i = std::distance(ptnr2_atom_indices.begin(), row_it);
363
+ return absl::InvalidArgumentError(
364
+ absl::StrCat("Error parsing \"", mmcif.GetDataName(), "\". ",
365
+ "Cannot find atom for bond ID ", struct_conn_id[i], ": ",
366
+ ptnr2_atoms->GetAtomString(i)));
367
+ }
368
+ }
369
+
370
+ if (!model_id_ecountered) {
371
+ return absl::InvalidArgumentError(absl::StrCat(
372
+ "Error parsing \"", mmcif.GetDataName(), "\". model_id \"", model_id,
373
+ "\" not found in _atom_site.pdbx_PDB_model_num."));
374
+ }
375
+
376
+ return std::make_pair(std::move(ptnr1_atom_indices),
377
+ std::move(ptnr2_atom_indices));
378
+ }
379
+
380
+ } // namespace alphafold3
flax_model/alphafold3/structure/cpp/mmcif_struct_conn_pybind.cc ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright 2024 DeepMind Technologies Limited
2
+ //
3
+ // AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
4
+ // this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
5
+ //
6
+ // To request access to the AlphaFold 3 model parameters, follow the process set
7
+ // out at https://github.com/google-deepmind/alphafold3. You may only use these
8
+ // if received directly from Google. Use is subject to terms of use available at
9
+ // https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
10
+
11
+ #include <string>
12
+
13
+ #include "absl/strings/string_view.h"
14
+ #include "alphafold3/parsers/cpp/cif_dict_lib.h"
15
+ #include "alphafold3/structure/cpp/mmcif_struct_conn.h"
16
+ #include "pybind11/gil.h"
17
+ #include "pybind11/pybind11.h"
18
+ #include "pybind11/pytypes.h"
19
+ #include "pybind11/stl.h"
20
+
21
+ namespace alphafold3 {
22
+
23
+ namespace py = pybind11;
24
+
25
+ constexpr char kGetBondAtomIndices[] = R"(
26
+ Extracts the indices of the atoms that participate in bonds.
27
+
28
+ This function has a workaround for a known PDB issue: some mmCIFs have
29
+ (2evw, 2g0v, 2g0x, 2g0z, 2g10, 2g11, 2g12, 2g14, 2grz, 2ntw as of 2024)
30
+ multiple models and they set different whole-chain altloc in each model.
31
+ The bond table however doesn't distinguish between models, so there are
32
+ bonds that are valid only for some models. E.g. 2grz has model 1 with
33
+ chain A with altloc A, and model 2 with chain A with altloc B. The bonds
34
+ table lists a bond for each of these. This case is rather rare (10 cases
35
+ in PDB as of 2024). For the offending bonds, the returned atom index is
36
+ set to the size of the atom_site table, i.e. it is an invalid index.
37
+
38
+ Args:
39
+ mmcif: The mmCIF object to process.
40
+ model_id: The ID of the model that the returned atoms will belong to. This
41
+ should be a value in the mmCIF's _atom_site.pdbx_PDB_model_num column.
42
+
43
+ Returns:
44
+ Two lists of atom indices, `from_atoms` and `to_atoms`, each one having
45
+ length num_bonds (as defined by _struct_conn, the bonds table). The bond
46
+ i, defined by the i'th row in _struct_conn, is a bond from atom at index
47
+ from_atoms[i], to the atom at index to_atoms[i]. The indices are simple
48
+ 0-based indexes into the columns of the _atom_site table in the input
49
+ mmCIF, and do not necessarily correspond to the values in _atom_site.id,
50
+ or any other column.
51
+ )";
52
+
53
+ void RegisterModuleMmcifStructConn(pybind11::module m) {
54
+ m.def(
55
+ "get_bond_atom_indices",
56
+ [](const CifDict& mmcif, absl::string_view model_id) {
57
+ auto result = GetBondAtomIndices(mmcif, model_id);
58
+ if (result.ok()) {
59
+ return *result;
60
+ }
61
+ throw py::value_error(std::string(result.status().message()));
62
+ },
63
+ py::arg("mmcif_dict"), py::arg("model_id"),
64
+ py::doc(kGetBondAtomIndices + 1),
65
+ py::call_guard<py::gil_scoped_release>());
66
+ }
67
+
68
+ } // namespace alphafold3
flax_model/alphafold3/structure/cpp/mmcif_struct_conn_pybind.h ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Copyright 2024 DeepMind Technologies Limited
3
+ *
4
+ * AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
5
+ * this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
6
+ *
7
+ * To request access to the AlphaFold 3 model parameters, follow the process set
8
+ * out at https://github.com/google-deepmind/alphafold3. You may only use these
9
+ * if received directly from Google. Use is subject to terms of use available at
10
+ * https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
11
+ */
12
+
13
+ #ifndef ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_STRUCT_CONN_PYBIND_H_
14
+ #define ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_STRUCT_CONN_PYBIND_H_
15
+
16
+ #include "pybind11/pybind11.h"
17
+
18
+ namespace alphafold3 {
19
+
20
+ void RegisterModuleMmcifStructConn(pybind11::module m);
21
+
22
+ }
23
+
24
+ #endif // ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_STRUCT_CONN_PYBIND_H_
flax_model/alphafold3/structure/cpp/mmcif_utils.pyi ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ from collections.abc import Sequence
4
+
5
+ import numpy as np
6
+
7
+ from flax_model.alphafold3.cpp import cif_dict
8
+ from flax_model.alphafold3.structure.python import mmcif_layout
9
+
10
+
11
+ def filter(
12
+ mmcif: cif_dict.CifDict,
13
+ include_nucleotides: bool,
14
+ include_ligands: bool = ...,
15
+ include_water: bool = ...,
16
+ include_other: bool = ...,
17
+ model_id: str = ...,
18
+ ) -> tuple[np.ndarray[int], mmcif_layout.MmcifLayout]: ...
19
+
20
+
21
+ def fix_residues(
22
+ layout: mmcif_layout.MmcifLayout,
23
+ comp_id: Sequence[str],
24
+ atom_id: Sequence[str],
25
+ atom_x: Sequence[float],
26
+ atom_y: Sequence[float],
27
+ atom_z: Sequence[float],
28
+ fix_arg: bool = ...,
29
+ ) -> None: ...
30
+
31
+
32
+ def read_layout(
33
+ mmcif: cif_dict.CifDict, model_id: str = ...
34
+ ) -> mmcif_layout.MmcifLayout: ...
35
+
36
+
37
+ def selected_ligand_residue_mask(
38
+ layout: mmcif_layout.MmcifLayout,
39
+ atom_site_label_asym_ids: list[str],
40
+ atom_site_label_seq_ids: list[str],
41
+ atom_site_auth_seq_ids: list[str],
42
+ atom_site_label_comp_ids: list[str],
43
+ atom_site_pdbx_pdb_ins_codes: list[str],
44
+ nonpoly_asym_ids: list[str],
45
+ nonpoly_auth_seq_ids: list[str],
46
+ nonpoly_pdb_ins_codes: list[str],
47
+ nonpoly_mon_ids: list[str],
48
+ branch_asym_ids: list[str],
49
+ branch_auth_seq_ids: list[str],
50
+ branch_pdb_ins_codes: list[str],
51
+ branch_mon_ids: list[str],
52
+ ) -> tuple[list[bool], list[bool]]: ...
53
+
54
+
55
+ def selected_polymer_residue_mask(
56
+ layout: mmcif_layout.MmcifLayout,
57
+ atom_site_label_asym_ids: list[str],
58
+ atom_site_label_seq_ids: list[str],
59
+ atom_site_label_comp_ids: list[str],
60
+ poly_seq_asym_ids: list[str],
61
+ poly_seq_seq_ids: list[str],
62
+ poly_seq_mon_ids: list[str],
63
+ ) -> list[bool]: ...
flax_model/alphafold3/structure/cpp/mmcif_utils_pybind.cc ADDED
@@ -0,0 +1,787 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright 2024 DeepMind Technologies Limited
2
+ //
3
+ // AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
4
+ // this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
5
+ //
6
+ // To request access to the AlphaFold 3 model parameters, follow the process set
7
+ // out at https://github.com/google-deepmind/alphafold3. You may only use these
8
+ // if received directly from Google. Use is subject to terms of use available at
9
+ // https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
10
+
11
+ #include <Python.h>
12
+
13
+ #include <algorithm>
14
+ #include <cstddef>
15
+ #include <cstdint>
16
+ #include <iterator>
17
+ #include <memory>
18
+ #include <new>
19
+ #include <optional>
20
+ #include <string>
21
+ #include <tuple>
22
+ #include <utility>
23
+ #include <vector>
24
+
25
+ #include "numpy/ndarrayobject.h"
26
+ #include "numpy/ndarraytypes.h"
27
+ #include "numpy/npy_common.h"
28
+ #include "absl/container/flat_hash_map.h"
29
+ #include "absl/container/flat_hash_set.h"
30
+ #include "absl/memory/memory.h"
31
+ #include "absl/strings/str_cat.h"
32
+ #include "absl/strings/string_view.h"
33
+ #include "absl/types/span.h"
34
+ #include "alphafold3/parsers/cpp/cif_dict_lib.h"
35
+ #include "alphafold3/structure/cpp/mmcif_altlocs.h"
36
+ #include "alphafold3/structure/cpp/mmcif_layout.h"
37
+ #include "pybind11/cast.h"
38
+ #include "pybind11/gil.h"
39
+ #include "pybind11/numpy.h"
40
+ #include "pybind11/pybind11.h"
41
+ #include "pybind11/pytypes.h"
42
+ #include "pybind11/stl.h"
43
+ #include "pybind11_abseil/absl_casters.h"
44
+
45
+ namespace alphafold3 {
46
+ namespace {
47
+ namespace py = pybind11;
48
+
49
+ struct PyObjectDeleter {
50
+ inline void operator()(PyObject* obj) const { Py_CLEAR(obj); }
51
+ };
52
+
53
+ using ScopedPyObject = std::unique_ptr<PyObject, PyObjectDeleter>;
54
+
55
+ using StringArrayRef = absl::Span<const std::string>;
56
+ using Indexer = absl::flat_hash_map<absl::string_view, std::size_t>;
57
+
58
+ // Returns the reverse look-up map of name to index.
59
+ Indexer MakeIndex(StringArrayRef col) {
60
+ Indexer index;
61
+ index.reserve(col.size());
62
+ for (std::size_t i = 0; i < col.size(); ++i) {
63
+ index[col[i]] = i;
64
+ }
65
+ return index;
66
+ }
67
+
68
+ // Returns whether each container is the same size.
69
+ template <typename C, typename... Cs>
70
+ bool AreSameSize(C c, const Cs&... cs) {
71
+ return ((c.size() == cs.size()) && ...);
72
+ }
73
+
74
+ // Stores references to columns in `_atom_site` ensuring they all exist and
75
+ // are the same size.
76
+ struct AtomSiteLoop {
77
+ explicit AtomSiteLoop(const CifDict& cif_dict)
78
+ : id(cif_dict["_atom_site.id"]),
79
+ model_id(cif_dict["_atom_site.pdbx_PDB_model_num"]),
80
+ chain_id(cif_dict["_atom_site.label_asym_id"]),
81
+ seq_id(cif_dict["_atom_site.label_seq_id"]),
82
+
83
+ comp_id(cif_dict["_atom_site.label_comp_id"]),
84
+ atom_id(cif_dict["_atom_site.label_atom_id"]),
85
+
86
+ alt_id(cif_dict["_atom_site.label_alt_id"]),
87
+ occupancy(cif_dict["_atom_site.occupancy"])
88
+
89
+ {
90
+ if (!AreSameSize(id, model_id, chain_id, seq_id, comp_id, atom_id, alt_id,
91
+ occupancy)) {
92
+ throw py::value_error(
93
+ absl::StrCat("Invalid '_atom_site.' loop. ", //
94
+ "len(id)=", id.size(), ", ", //
95
+ "len(pdbx_PDB_model_num)=", model_id.size(), ", ", //
96
+ "len(label_asym_id)=", chain_id.size(), ", ", //
97
+ "len(label_seq_id)=", seq_id.size(), ", ", //
98
+ "len(label_comp_id)=", comp_id.size(), ", ", //
99
+ "len(atom_id)=", atom_id.size(), ", ", //
100
+ "len(label_alt_id)=", alt_id.size(), ", ", //
101
+ "len(occupancy)=", occupancy.size()));
102
+ }
103
+ }
104
+ StringArrayRef id;
105
+ StringArrayRef model_id;
106
+ StringArrayRef chain_id;
107
+ StringArrayRef seq_id;
108
+ StringArrayRef comp_id;
109
+ StringArrayRef atom_id;
110
+ StringArrayRef alt_id;
111
+ StringArrayRef occupancy;
112
+ };
113
+
114
+ // Stores references to columns in `_entity` ensuring they all exist and are the
115
+ // same size.
116
+ struct EntityLoop {
117
+ explicit EntityLoop(const CifDict& cif_dict)
118
+ : id(cif_dict["_entity.id"]), type(cif_dict["_entity.type"]) {
119
+ if (!AreSameSize(id, type)) {
120
+ throw py::value_error(absl::StrCat("Invalid '_entity.' loop. ", //
121
+ "len(id)=", id.size(), ", ", //
122
+ "len(type)=", type.size()));
123
+ }
124
+ }
125
+ StringArrayRef id;
126
+ StringArrayRef type;
127
+ };
128
+
129
+ // Stores references to columns in `_entity_poly` ensuring they all exist and
130
+ // are the same size.
131
+ struct EntityPolyLoop {
132
+ explicit EntityPolyLoop(const CifDict& cif_dict)
133
+ : entity_id(cif_dict["_entity_poly.entity_id"]),
134
+ type(cif_dict["_entity_poly.type"]) {
135
+ if (!AreSameSize(entity_id, type)) {
136
+ throw py::value_error(absl::StrCat("Invalid '_entity_poly.' loop. ", //
137
+ "len(entity_id)=", entity_id.size(),
138
+ ", ", //
139
+ "len(type)=", type.size()));
140
+ }
141
+ }
142
+ StringArrayRef entity_id;
143
+ StringArrayRef type;
144
+ };
145
+
146
+ // Returns a set of entity names removing ones not included by the flags
147
+ // specified.
148
+ absl::flat_hash_set<absl::string_view> SelectChains(const CifDict& mmcif,
149
+ bool include_nucleotides,
150
+ bool include_ligands,
151
+ bool include_water,
152
+ bool include_other) {
153
+ EntityLoop entity_loop(mmcif);
154
+ EntityPolyLoop entity_poly(mmcif);
155
+ absl::flat_hash_set<absl::string_view> permitted_polymers{"polypeptide(L)"};
156
+ absl::flat_hash_set<absl::string_view> forbidden_polymers;
157
+ for (absl::string_view type :
158
+ {"polydeoxyribonucleotide", "polyribonucleotide",
159
+ "polydeoxyribonucleotide/polyribonucleotide hybrid"}) {
160
+ if (include_nucleotides) {
161
+ permitted_polymers.emplace(type);
162
+ } else {
163
+ forbidden_polymers.emplace(type);
164
+ }
165
+ }
166
+
167
+ absl::flat_hash_set<absl::string_view> permitted_nonpoly_entity_types;
168
+ absl::flat_hash_set<absl::string_view> forbidden_nonpoly_entity_types;
169
+ for (absl::string_view type : {"non-polymer", "branched"}) {
170
+ if (include_ligands) {
171
+ permitted_nonpoly_entity_types.emplace(type);
172
+ } else {
173
+ forbidden_nonpoly_entity_types.emplace(type);
174
+ }
175
+ }
176
+ absl::string_view water_type = "water";
177
+ if (include_water) {
178
+ permitted_nonpoly_entity_types.emplace(water_type);
179
+ } else {
180
+ forbidden_nonpoly_entity_types.emplace(water_type);
181
+ }
182
+
183
+ StringArrayRef chain_ids = mmcif["_struct_asym.id"];
184
+ StringArrayRef entity_ids = mmcif["_struct_asym.entity_id"];
185
+ Indexer chain_index = MakeIndex(chain_ids);
186
+ Indexer entity_poly_index = MakeIndex(entity_poly.entity_id);
187
+ Indexer entity_id_to_index = MakeIndex(entity_loop.id);
188
+
189
+ absl::flat_hash_set<absl::string_view> keep_chain_id;
190
+ for (std::size_t i = 0; i < chain_ids.size(); ++i) {
191
+ absl::string_view chain_id = chain_ids[i];
192
+ absl::string_view entity_id = entity_ids[i];
193
+ if (entity_id_to_index.empty() ||
194
+ entity_loop.type[entity_id_to_index[entity_id]] == "polymer") {
195
+ if (auto it = entity_poly_index.find(entity_id);
196
+ it != entity_poly_index.end()) {
197
+ absl::string_view poly_type = entity_poly.type[it->second];
198
+ if (include_other) {
199
+ if (!forbidden_polymers.contains(poly_type)) {
200
+ keep_chain_id.insert(chain_id);
201
+ }
202
+ } else {
203
+ if (permitted_polymers.contains(poly_type)) {
204
+ keep_chain_id.insert(chain_id);
205
+ }
206
+ }
207
+ }
208
+ } else {
209
+ absl::string_view entity_type =
210
+ entity_loop.type[entity_id_to_index[entity_id]];
211
+ if (include_other) {
212
+ if (!forbidden_nonpoly_entity_types.contains(entity_type)) {
213
+ keep_chain_id.insert(chain_id);
214
+ continue;
215
+ }
216
+ } else {
217
+ if (permitted_nonpoly_entity_types.contains(entity_type)) {
218
+ keep_chain_id.insert(chain_id);
219
+ continue;
220
+ }
221
+ }
222
+ }
223
+ }
224
+ return keep_chain_id;
225
+ }
226
+
227
+ class ProcessResidue {
228
+ public:
229
+ explicit ProcessResidue(const char* residue)
230
+ : residue_(PyUnicode_InternFromString(residue)) {}
231
+ bool IsResidue(PyObject* residue) {
232
+ return ArePyObjectsEqual(residue_.get(), residue);
233
+ }
234
+
235
+ static bool ArePyObjectsEqual(PyObject* lhs, PyObject* rhs) {
236
+ switch (PyObject_RichCompareBool(lhs, rhs, Py_EQ)) {
237
+ case -1:
238
+ PyErr_Clear();
239
+ return false;
240
+ case 0:
241
+ return false;
242
+ default:
243
+ return true;
244
+ }
245
+ }
246
+
247
+ private:
248
+ ScopedPyObject residue_;
249
+ };
250
+
251
+ struct Position3 {
252
+ float x;
253
+ float y;
254
+ float z;
255
+ };
256
+
257
+ float DistanceSquared(Position3 v1, Position3 v2) {
258
+ float dx = v1.x - v2.x;
259
+ float dy = v1.y - v2.y;
260
+ float dz = v1.z - v2.z;
261
+ return dx * dx + dy * dy + dz * dz;
262
+ }
263
+
264
+ class FixArginine : public ProcessResidue {
265
+ public:
266
+ FixArginine()
267
+ : ProcessResidue("ARG"),
268
+ cd_(PyUnicode_InternFromString("CD")),
269
+ nh1_(PyUnicode_InternFromString("NH1")),
270
+ nh2_(PyUnicode_InternFromString("NH2")),
271
+ hh11_(PyUnicode_InternFromString("HH11")),
272
+ hh21_(PyUnicode_InternFromString("HH21")),
273
+ hh12_(PyUnicode_InternFromString("HH12")),
274
+ hh22_(PyUnicode_InternFromString("HH22")) {}
275
+ void Fix(absl::Span<PyObject*> atom_ids, absl::Span<const float> atom_x,
276
+ absl::Span<const float> atom_y, absl::Span<const float> atom_z) {
277
+ std::ptrdiff_t cd_index = -1;
278
+ std::ptrdiff_t nh1_index = -1;
279
+ std::ptrdiff_t nh2_index = -1;
280
+ std::ptrdiff_t hh11_index = -1;
281
+ std::ptrdiff_t hh21_index = -1;
282
+ std::ptrdiff_t hh12_index = -1;
283
+ std::ptrdiff_t hh22_index = -1;
284
+ for (std::ptrdiff_t index = 0; index < atom_ids.size(); ++index) {
285
+ PyObject* atom_id = atom_ids[index];
286
+ if (cd_index == -1 && ArePyObjectsEqual(atom_id, cd_.get())) {
287
+ cd_index = index;
288
+ } else if (nh1_index == -1 && ArePyObjectsEqual(atom_id, nh1_.get())) {
289
+ nh1_index = index;
290
+ } else if (nh2_index == -1 && ArePyObjectsEqual(atom_id, nh2_.get())) {
291
+ nh2_index = index;
292
+ } else if (hh11_index == -1 && ArePyObjectsEqual(atom_id, hh11_.get())) {
293
+ hh11_index = index;
294
+ } else if (hh21_index == -1 && ArePyObjectsEqual(atom_id, hh21_.get())) {
295
+ hh21_index = index;
296
+ } else if (hh12_index == -1 && ArePyObjectsEqual(atom_id, hh12_.get())) {
297
+ hh12_index = index;
298
+ } else if (hh22_index == -1 && ArePyObjectsEqual(atom_id, hh22_.get())) {
299
+ hh22_index = index;
300
+ }
301
+ }
302
+ if (cd_index < 0 || nh1_index < 0 || nh2_index < 0) {
303
+ return;
304
+ }
305
+ Position3 cd_pos(atom_x[cd_index], atom_y[cd_index], atom_z[cd_index]);
306
+ Position3 nh1_pos(atom_x[nh1_index], atom_y[nh1_index], atom_z[nh1_index]);
307
+ Position3 nh2_pos(atom_x[nh2_index], atom_y[nh2_index], atom_z[nh2_index]);
308
+ if (DistanceSquared(nh1_pos, cd_pos) <= DistanceSquared(nh2_pos, cd_pos)) {
309
+ return;
310
+ }
311
+ std::swap(atom_ids[nh1_index], atom_ids[nh2_index]);
312
+ if (hh11_index >= 0 && hh21_index >= 0) {
313
+ std::swap(atom_ids[hh11_index], atom_ids[hh21_index]);
314
+ } else if (hh11_index >= 0) {
315
+ Py_DECREF(atom_ids[hh11_index]);
316
+ Py_INCREF(hh21_.get());
317
+ atom_ids[hh11_index] = hh21_.get();
318
+ } else if (hh21_index >= 0) {
319
+ Py_DECREF(atom_ids[hh21_index]);
320
+ Py_INCREF(hh11_.get());
321
+ atom_ids[hh21_index] = hh11_.get();
322
+ }
323
+ if (hh12_index >= 0 && hh22_index >= 0) {
324
+ std::swap(atom_ids[hh12_index], atom_ids[hh22_index]);
325
+ } else if (hh12_index >= 0) {
326
+ Py_DECREF(atom_ids[hh12_index]);
327
+ Py_INCREF(hh22_.get());
328
+ atom_ids[hh12_index] = hh22_.get();
329
+ } else if (hh22_index >= 0) {
330
+ Py_DECREF(atom_ids[hh22_index]);
331
+ Py_INCREF(hh21_.get());
332
+ atom_ids[hh22_index] = hh21_.get();
333
+ }
334
+ }
335
+
336
+ private:
337
+ ScopedPyObject cd_;
338
+ ScopedPyObject nh1_;
339
+ ScopedPyObject nh2_;
340
+ ScopedPyObject hh11_;
341
+ ScopedPyObject hh21_;
342
+ ScopedPyObject hh12_;
343
+ ScopedPyObject hh22_;
344
+ };
345
+
346
+ // Returns the layout of the mmCIF `_atom_site` table.
347
+ inline MmcifLayout ReadMmcifLayout(const CifDict& mmcif,
348
+ absl::string_view model_id = "") {
349
+ py::gil_scoped_release release;
350
+ auto mmcif_layout = MmcifLayout::Create(mmcif, model_id);
351
+ if (mmcif_layout.ok()) {
352
+ return *mmcif_layout;
353
+ }
354
+
355
+ throw py::value_error(std::string(mmcif_layout.status().message()));
356
+ }
357
+
358
+ std::pair<py::object, MmcifLayout> MmcifFilter( //
359
+ const CifDict& mmcif, //
360
+ bool include_nucleotides, //
361
+ bool include_ligands, //
362
+ bool include_water, //
363
+ bool include_other, //
364
+ absl::string_view model_id) {
365
+ if (_import_array() < 0) {
366
+ throw py::import_error("Failed to import NumPy.");
367
+ }
368
+ auto layout = ReadMmcifLayout(mmcif, model_id);
369
+ std::unique_ptr<std::vector<std::uint64_t>> keep_indices;
370
+ size_t new_num_atoms;
371
+
372
+ {
373
+ py::gil_scoped_release release;
374
+
375
+ AtomSiteLoop atom_site(mmcif);
376
+
377
+ auto keep_chain_ids =
378
+ SelectChains(mmcif, include_nucleotides, include_ligands, include_water,
379
+ include_other);
380
+
381
+ std::vector<std::size_t> chain_indices;
382
+ chain_indices.reserve(keep_chain_ids.size());
383
+ for (std::size_t i = 0; i < layout.num_chains(); ++i) {
384
+ if (keep_chain_ids.contains(
385
+ atom_site.chain_id[layout.atom_site_from_chain_index(i)])) {
386
+ chain_indices.push_back(i);
387
+ }
388
+ }
389
+
390
+ keep_indices =
391
+ absl::WrapUnique(new std::vector<std::uint64_t>(ResolveMmcifAltLocs(
392
+ layout, atom_site.comp_id, atom_site.atom_id, atom_site.alt_id,
393
+ atom_site.occupancy, chain_indices)));
394
+ new_num_atoms = keep_indices->size();
395
+
396
+ if (layout.num_models() > 1) {
397
+ keep_indices->reserve(layout.num_models() * new_num_atoms);
398
+ std::uint64_t* start = &(*keep_indices->begin());
399
+ std::size_t num_atom = keep_indices->size();
400
+ // Copy first model indices into all model indices offsetting each copy.
401
+ for (std::size_t i = 1; i < layout.num_models(); ++i) {
402
+ std::size_t offset = i * layout.num_atoms();
403
+ std::transform(start, start + num_atom,
404
+ std::back_inserter(*keep_indices),
405
+ [offset](std::size_t v) { return v + offset; });
406
+ }
407
+ }
408
+ }
409
+
410
+ layout.Filter(*keep_indices);
411
+
412
+ npy_intp shape[] = {static_cast<npy_intp>(layout.num_models()),
413
+ static_cast<npy_intp>(new_num_atoms)};
414
+ PyObject* arr =
415
+ PyArray_SimpleNewFromData(2, shape, NPY_INT64, keep_indices->data());
416
+ // Create a capsule to hold the memory of the buffer so NumPy knows how to
417
+ // delete it when done with it.
418
+ PyObject* capsule = PyCapsule_New(
419
+ keep_indices.release(), nullptr, +[](PyObject* capsule_cleanup) {
420
+ void* memory = PyCapsule_GetPointer(capsule_cleanup, nullptr);
421
+ delete static_cast<std::vector<std::size_t>*>(memory);
422
+ });
423
+ PyArray_SetBaseObject(reinterpret_cast<PyArrayObject*>(arr), capsule);
424
+
425
+ return std::make_pair(py::reinterpret_steal<py::object>(arr),
426
+ std::move(layout));
427
+ }
428
+
429
+ void MmcifFixResidues( //
430
+ const MmcifLayout& layout, //
431
+ absl::Span<PyObject*> comp_id, //
432
+ absl::Span<PyObject*> atom_id, //
433
+ absl::Span<const float> atom_x, //
434
+ absl::Span<const float> atom_y, //
435
+ absl::Span<const float> atom_z, //
436
+ bool fix_arginine //
437
+ ) {
438
+ std::optional<FixArginine> arginine;
439
+ std::size_t num_atoms = layout.num_atoms();
440
+ if (comp_id.size() != num_atoms || atom_id.size() != num_atoms ||
441
+ atom_x.size() != num_atoms || atom_y.size() != num_atoms ||
442
+ atom_z.size() != num_atoms) {
443
+ throw py::value_error(
444
+ absl::StrCat("Sizes must match. ", //
445
+ "num_atoms=", num_atoms, ", ", //
446
+ "len(comp_id)=", comp_id.size(), ", ", //
447
+ "len(atom_id)=", atom_id.size(), ", ", //
448
+ "len(atom_x)=", atom_x.size(), ", ", //
449
+ "len(atom_y)=", atom_y.size(), ", ", //
450
+ "len(atom_z)=", atom_z.size()));
451
+ }
452
+
453
+ if (fix_arginine) {
454
+ arginine.emplace();
455
+ }
456
+ if (!arginine.has_value()) {
457
+ return;
458
+ }
459
+
460
+ for (std::size_t res_index = 0; res_index < layout.num_residues();
461
+ ++res_index) {
462
+ auto [atom_start, atom_end] = layout.atom_range(res_index);
463
+ std::size_t atom_count = atom_end - atom_start;
464
+ PyObject* resname = comp_id[atom_start];
465
+ if (arginine.has_value() && arginine->IsResidue(resname)) {
466
+ arginine->Fix(atom_id.subspan(atom_start, atom_count),
467
+ atom_x.subspan(atom_start, atom_count),
468
+ atom_y.subspan(atom_start, atom_count),
469
+ atom_z.subspan(atom_start, atom_count));
470
+ }
471
+ }
472
+ }
473
+
474
+ std::vector<bool> SelectedPolymerResidueMask(
475
+ const MmcifLayout& layout,
476
+ const std::vector<absl::string_view>& atom_site_label_asym_ids, //
477
+ const std::vector<absl::string_view>& atom_site_label_seq_ids, //
478
+ const std::vector<absl::string_view>& atom_site_label_comp_ids, //
479
+ const std::vector<absl::string_view>& poly_seq_asym_ids, //
480
+ const std::vector<absl::string_view>& poly_seq_seq_ids, //
481
+ const std::vector<absl::string_view>& poly_seq_mon_ids //
482
+ ) {
483
+ absl::flat_hash_map<std::pair<absl::string_view, absl::string_view>,
484
+ absl::string_view>
485
+ selected;
486
+ selected.reserve(layout.num_residues());
487
+ // layout.residues() is O(1) while layout.residue_starts() is O(num_res).
488
+ const std::vector<std::size_t>& residue_starts = layout.residue_starts();
489
+ for (int i = 0; i < layout.residues().size(); ++i) {
490
+ std::size_t res_start = residue_starts[i];
491
+ std::size_t res_end = layout.residues()[i];
492
+ if (res_start == res_end) {
493
+ continue; // Skip empty residues (containing no atoms).
494
+ }
495
+
496
+ absl::string_view label_seq_id = atom_site_label_seq_ids[i];
497
+ if (label_seq_id == ".") {
498
+ continue; // Skip non-polymers.
499
+ }
500
+
501
+ absl::string_view label_asym_id = atom_site_label_asym_ids[i];
502
+ absl::string_view label_comp_id = atom_site_label_comp_ids[i];
503
+ selected[std::make_pair(label_asym_id, label_seq_id)] = label_comp_id;
504
+ }
505
+
506
+ std::vector<bool> mask;
507
+ mask.reserve(poly_seq_mon_ids.size());
508
+ for (int i = 0; i < poly_seq_mon_ids.size(); ++i) {
509
+ absl::string_view poly_seq_asym_id = poly_seq_asym_ids[i];
510
+ absl::string_view poly_seq_seq_id = poly_seq_seq_ids[i];
511
+ absl::string_view poly_seq_mon_id = poly_seq_mon_ids[i];
512
+
513
+ auto it = selected.find(std::make_pair(poly_seq_asym_id, poly_seq_seq_id));
514
+ if (it != selected.end()) {
515
+ mask.push_back(it->second == poly_seq_mon_id);
516
+ } else {
517
+ mask.push_back(true); // Missing residues are not heterogeneous.
518
+ }
519
+ }
520
+ return mask;
521
+ }
522
+
523
+ std::pair<std::vector<bool>, std::vector<bool>> SelectedLigandResidueMask(
524
+ const MmcifLayout& layout, //
525
+ const std::vector<absl::string_view>& atom_site_label_asym_ids, //
526
+ const std::vector<absl::string_view>& atom_site_label_seq_ids, //
527
+ const std::vector<absl::string_view>& atom_site_auth_seq_ids, //
528
+ const std::vector<absl::string_view>& atom_site_label_comp_ids, //
529
+ const std::vector<absl::string_view>& atom_site_pdbx_pdb_ins_codes, //
530
+ const std::vector<absl::string_view>& nonpoly_asym_ids, //
531
+ const std::vector<absl::string_view>& nonpoly_auth_seq_ids, //
532
+ const std::vector<absl::string_view>& nonpoly_pdb_ins_codes, //
533
+ const std::vector<absl::string_view>& nonpoly_mon_ids, //
534
+ const std::vector<absl::string_view>& branch_asym_ids, //
535
+ const std::vector<absl::string_view>& branch_auth_seq_ids, //
536
+ const std::vector<absl::string_view>& branch_pdb_ins_codes, //
537
+ const std::vector<absl::string_view>& branch_mon_ids) {
538
+ absl::flat_hash_map<
539
+ std::tuple<absl::string_view, absl::string_view, absl::string_view>,
540
+ absl::string_view>
541
+ selected;
542
+ selected.reserve(layout.num_residues());
543
+ // layout.residues() is O(1) while layout.residue_starts() is O(num_res).
544
+ const std::vector<std::size_t>& residue_starts = layout.residue_starts();
545
+ for (int i = 0; i < layout.residues().size(); ++i) {
546
+ std::size_t res_start = residue_starts[i];
547
+ std::size_t res_end = layout.residues()[i];
548
+ if (res_start == res_end) {
549
+ continue; // Skip empty residues (containing no atoms).
550
+ }
551
+
552
+ absl::string_view label_seq_id = atom_site_label_seq_ids[i];
553
+ if (label_seq_id != ".") {
554
+ continue; // Skip polymers.
555
+ }
556
+
557
+ absl::string_view label_asym_id = atom_site_label_asym_ids[i];
558
+ absl::string_view auth_seq_id = atom_site_auth_seq_ids[i];
559
+ absl::string_view ins_code = atom_site_pdbx_pdb_ins_codes[i];
560
+ ins_code = ins_code == "?" ? "." : ins_code; // Remap unknown to unset.
561
+ absl::string_view label_comp_id = atom_site_label_comp_ids[i];
562
+ selected[std::make_tuple(label_asym_id, auth_seq_id, ins_code)] =
563
+ label_comp_id;
564
+ }
565
+
566
+ std::vector<bool> nonpoly_mask;
567
+ nonpoly_mask.reserve(nonpoly_asym_ids.size());
568
+ for (int i = 0; i < nonpoly_asym_ids.size(); ++i) {
569
+ absl::string_view nonpoly_asym_id = nonpoly_asym_ids[i];
570
+ absl::string_view nonpoly_auth_seq_id = nonpoly_auth_seq_ids[i];
571
+ absl::string_view nonpoly_ins_code = nonpoly_pdb_ins_codes[i];
572
+ // Remap unknown to unset.
573
+ nonpoly_ins_code = nonpoly_ins_code == "?" ? "." : nonpoly_ins_code;
574
+ absl::string_view nonpoly_mon_id = nonpoly_mon_ids[i];
575
+
576
+ auto it = selected.find(std::make_tuple(
577
+ nonpoly_asym_id, nonpoly_auth_seq_id, nonpoly_ins_code));
578
+ if (it != selected.end()) {
579
+ nonpoly_mask.push_back(it->second == nonpoly_mon_id);
580
+ } else {
581
+ nonpoly_mask.push_back(true); // Missing residues are not heterogeneous.
582
+ }
583
+ }
584
+
585
+ std::vector<bool> branch_mask;
586
+ branch_mask.reserve(branch_asym_ids.size());
587
+ for (int i = 0; i < branch_asym_ids.size(); ++i) {
588
+ absl::string_view branch_asym_id = branch_asym_ids[i];
589
+ absl::string_view branch_auth_seq_id = branch_auth_seq_ids[i];
590
+
591
+ // Insertion codes in _pdbx_branch_scheme are not required and can be
592
+ // missing. Default to unset ('.') in such case.
593
+ absl::string_view branch_ins_code;
594
+ if (i < branch_pdb_ins_codes.size()) {
595
+ branch_ins_code = branch_pdb_ins_codes[i];
596
+ // Remap unknown to unset.
597
+ branch_ins_code = branch_ins_code == "?" ? "." : branch_ins_code;
598
+ } else {
599
+ branch_ins_code = ".";
600
+ }
601
+
602
+ absl::string_view branch_mon_id = branch_mon_ids[i];
603
+
604
+ auto it = selected.find(
605
+ std::make_tuple(branch_asym_id, branch_auth_seq_id, branch_ins_code));
606
+ if (it != selected.end()) {
607
+ branch_mask.push_back(it->second == branch_mon_id);
608
+ } else {
609
+ branch_mask.push_back(true); // Missing residues are not heterogeneous.
610
+ }
611
+ }
612
+
613
+ return std::make_pair(nonpoly_mask, branch_mask);
614
+ }
615
+
616
+ constexpr char kReadMmcifLayout[] = R"(
617
+ Returns the layout of the cif_dict.
618
+
619
+ Args:
620
+ mmcif: mmCIF to calculate the layout for.
621
+ model_id: If non-empty the layout of the given model is returned
622
+ otherwise the layout of all models are returned.
623
+ Raises:
624
+ ValueError: if the mmCIF is malformed or the number of atoms in each
625
+ model are inconsistent.
626
+ )";
627
+
628
+ constexpr char kMmcifFilter[] = R"(
629
+ Returns NumpyArray of selected rows in `_atom_site` and new layout.
630
+
631
+ Args:
632
+ mmcif: mmCIF to filter.
633
+ include_nucleotides: Whether to include polymer entities of type:
634
+ "polypeptide(L)\", "polydeoxyribonucleotide", "polyribonucleotide".
635
+ Otherwise only "polypeptide(L)\". ("polypeptide(D)\" is never included.)
636
+ include_ligands: Whether to include non-polymer entities of type:
637
+ "non-polymer", "branched".
638
+ include_water: Whether to include entities of type water.
639
+ include_other: Whether to include other (non-standard) entity types
640
+ that are not covered by any of the above parameters.
641
+ model_id: If non-empty the model with given name is selected otherwise
642
+ all models are selected.
643
+
644
+ Returns:
645
+ A tuple containing a numpy array with a shape (num_models, num_atoms)
646
+ with the atom_site indices selected and the new layout.
647
+
648
+ Raises:
649
+ ValueError error if mmCIF dict does not have all required fields.
650
+ )";
651
+
652
+ constexpr char kMmcifFixResidues[] = R"(
653
+ Fixes residue columns in-place.
654
+
655
+ Args:
656
+ layout: layout from filter command.
657
+ comp_id: '_atom_site.label_comp_id' of first model.
658
+ group: '_atom_site.group_PDB' of first model.
659
+ atom_id: '_atom_site.label_atom_id' of first model.
660
+ type_symbol: '_atom_site.type_symbol' of first model.
661
+ atom_x: '_atom_site.Cartn_x' of first model.
662
+ atom_y: '_atom_site.Cartn_y' of first model.
663
+ atom_z: '_atom_site.Cartn_z' of first model.
664
+ fix_mse: Whether to convert MSE residues into MET residues.
665
+ fix_arg: Whether to ensure the atoms in ARG are in the correct order.
666
+ fix_unknown_dna: Whether to convert DNA residues from N to DN.
667
+ dna_mask: Which atoms are from DNA chains.
668
+
669
+ Raises:
670
+ ValueError: If shapes are invalid.
671
+ )";
672
+
673
+ constexpr char kSelectedPolymerResidueMask[] = R"(
674
+ Returns a _pdbx_poly_seq_scheme mask for selected hetero residues.
675
+
676
+ Should be called after filtering the layout using mmcif_utils.filter.
677
+
678
+ Args:
679
+ layout: Layout defining the _atom_site residue selection.
680
+ atom_site_label_asym_ids: Internal (label) chain ID, per selected residue.
681
+ atom_site_label_seq_ids: Internal (label) residue ID, per selected residue.
682
+ atom_site_label_comp_ids: Residue name, per selected residue.
683
+ poly_seq_asym_ids: Internal (label) chain ID, per residue.
684
+ poly_seq_seq_ids: Internal (label) residue ID, per residue.
685
+ poly_seq_mon_ids: Residue name, per residue.
686
+
687
+ Returns:
688
+ A mask for the _pdbx_poly_seq_scheme table. If residues are selected
689
+ using this mask, they will have consistent heterogeneous residue
690
+ selection with the _atom_site table.
691
+ )";
692
+
693
+ constexpr char kSelectedLigandResidueMask[] = R"(
694
+ Returns masks for selected ligand hetero residues.
695
+
696
+ Should be called after filtering the layout using mmcif_utils.filter.
697
+
698
+ Args:
699
+ layout: Layout defining the _atom_site residue selection.
700
+ atom_site_label_asym_ids: Internal (label) chain ID, per selected residue.
701
+ atom_site_label_seq_ids: Internal (author) residue ID, per selected residue.
702
+ atom_site_auth_seq_ids: External (author) residue ID, per selected residue.
703
+ atom_site_label_comp_ids: Residue name, per selected residue.
704
+ atom_site_pdbx_pdb_ins_codes: Insertion code, per selected residue.
705
+ nonpoly_asym_ids: Internal (label) chain ID, per residue from
706
+ _pdbx_nonpoly_scheme.
707
+ nonpoly_auth_seq_ids: External (author) residue ID, per residue from
708
+ _pdbx_nonpoly_scheme.
709
+ nonpoly_pdb_ins_codes: Residue name, per residue from
710
+ _pdbx_nonpoly_scheme.
711
+ nonpoly_mon_ids: Insertion code, per residue from _pdbx_nonpoly_scheme.
712
+ branch_asym_ids: Internal (label) chain ID, per residue from
713
+ _pdbx_branch_scheme.
714
+ branch_auth_seq_ids: External (author) residue ID, per residue from
715
+ _pdbx_branch_scheme.
716
+ branch_pdb_ins_codes: Residue name, per residue from _pdbx_branch_scheme.
717
+ branch_mon_ids: Insertion code, per residue from _pdbx_branch_scheme.
718
+
719
+ Returns:
720
+ A tuple with masks for _pdbx_nonpoly_scheme and _pdbx_branch_scheme. If
721
+ residues are selected using these masks, they will have consistent
722
+ heterogeneous residue selection with the _atom_site table.
723
+ )";
724
+
725
+ } // namespace
726
+
727
+ void RegisterModuleMmcifUtils(pybind11::module m) {
728
+ m.def("read_layout", ReadMmcifLayout,
729
+ py::arg("mmcif"), //
730
+ py::arg("model_id") = "", //
731
+ py::doc(kReadMmcifLayout + 1) //
732
+ );
733
+
734
+ m.def("filter", MmcifFilter, //
735
+ py::arg("mmcif"), //
736
+ py::arg("include_nucleotides"), //
737
+ py::arg("include_ligands") = false, //
738
+ py::arg("include_water") = false, //
739
+ py::arg("include_other") = false, //
740
+ py::arg("model_id") = "", //
741
+ py::doc(kMmcifFilter + 1) //
742
+ );
743
+
744
+ m.def("fix_residues", MmcifFixResidues,
745
+ py::arg("layout"), //
746
+ py::arg("comp_id"), //
747
+ py::arg("atom_id"), //
748
+ py::arg("atom_x"), //
749
+ py::arg("atom_y"), //
750
+ py::arg("atom_z"), //
751
+ py::arg("fix_arg") = false, //
752
+ py::doc(kMmcifFixResidues + 1) //
753
+ );
754
+
755
+ m.def("selected_polymer_residue_mask", SelectedPolymerResidueMask,
756
+ py::arg("layout"), //
757
+ py::arg("atom_site_label_asym_ids"), //
758
+ py::arg("atom_site_label_seq_ids"), //
759
+ py::arg("atom_site_label_comp_ids"), //
760
+ py::arg("poly_seq_asym_ids"), //
761
+ py::arg("poly_seq_seq_ids"), //
762
+ py::arg("poly_seq_mon_ids"), //
763
+ py::call_guard<py::gil_scoped_release>(), //
764
+ py::doc(kSelectedPolymerResidueMask + 1) //
765
+ );
766
+
767
+ m.def("selected_ligand_residue_mask", SelectedLigandResidueMask,
768
+ py::arg("layout"), //
769
+ py::arg("atom_site_label_asym_ids"), //
770
+ py::arg("atom_site_label_seq_ids"), //
771
+ py::arg("atom_site_auth_seq_ids"), //
772
+ py::arg("atom_site_label_comp_ids"), //
773
+ py::arg("atom_site_pdbx_pdb_ins_codes"), //
774
+ py::arg("nonpoly_asym_ids"), //
775
+ py::arg("nonpoly_auth_seq_ids"), //
776
+ py::arg("nonpoly_pdb_ins_codes"), //
777
+ py::arg("nonpoly_mon_ids"), //
778
+ py::arg("branch_asym_ids"), //
779
+ py::arg("branch_auth_seq_ids"), //
780
+ py::arg("branch_pdb_ins_codes"), //
781
+ py::arg("branch_mon_ids"), //
782
+ py::call_guard<py::gil_scoped_release>(), //
783
+ py::doc(kSelectedLigandResidueMask + 1) //
784
+ );
785
+ }
786
+
787
+ } // namespace alphafold3
flax_model/alphafold3/structure/cpp/mmcif_utils_pybind.h ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Copyright 2024 DeepMind Technologies Limited
3
+ *
4
+ * AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
5
+ * this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
6
+ *
7
+ * To request access to the AlphaFold 3 model parameters, follow the process set
8
+ * out at https://github.com/google-deepmind/alphafold3. You may only use these
9
+ * if received directly from Google. Use is subject to terms of use available at
10
+ * https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
11
+ */
12
+
13
+ #ifndef ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_UTILS_PYBIND_H_
14
+ #define ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_UTILS_PYBIND_H_
15
+
16
+ #include "pybind11/pybind11.h"
17
+
18
+ namespace alphafold3 {
19
+
20
+ void RegisterModuleMmcifUtils(pybind11::module m);
21
+
22
+ }
23
+
24
+ #endif // ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_MMCIF_UTILS_PYBIND_H_
flax_model/alphafold3/structure/cpp/string_array.pyi ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ from collections.abc import Sequence
4
+ from typing import Any, overload
5
+
6
+ import numpy as np
7
+
8
+
9
+ def format_float_array(
10
+ values: Sequence[float], num_decimal_places: int
11
+ ) -> list[str]: ...
12
+
13
+
14
+ def isin(
15
+ array: np.ndarray[object],
16
+ test_elements: set[str | bytes],
17
+ *,
18
+ invert: bool = ...,
19
+ ) -> np.ndarray[bool]: ...
20
+
21
+
22
+ @overload
23
+ def remap(
24
+ array: np.ndarray[object],
25
+ mapping: dict[str, str],
26
+ default_value: str,
27
+ inplace: bool = ...,
28
+ ) -> np.ndarray[object]: ...
29
+
30
+
31
+ @overload
32
+ def remap(
33
+ array: np.ndarray[object],
34
+ mapping: dict[str, str],
35
+ inplace: bool = ...,
36
+ ) -> np.ndarray[object]: ...
37
+
38
+
39
+ def remap_multiple(
40
+ arrays: Sequence[np.ndarray[object]],
41
+ mapping: dict[tuple[Any], int],
42
+ ) -> np.ndarray[int]: ...
flax_model/alphafold3/structure/cpp/string_array_pybind.cc ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright 2024 DeepMind Technologies Limited
2
+ //
3
+ // AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
4
+ // this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
5
+ //
6
+ // To request access to the AlphaFold 3 model parameters, follow the process set
7
+ // out at https://github.com/google-deepmind/alphafold3. You may only use these
8
+ // if received directly from Google. Use is subject to terms of use available at
9
+ // https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
10
+
11
+ #include <Python.h>
12
+
13
+ #include <algorithm>
14
+ #include <cstddef>
15
+ #include <cstdint>
16
+ #include <cstring>
17
+ #include <iterator>
18
+ #include <limits>
19
+ #include <string>
20
+ #include <vector>
21
+
22
+ #include "numpy/arrayobject.h"
23
+ #include "numpy/ndarrayobject.h"
24
+ #include "numpy/ndarraytypes.h"
25
+ #include "numpy/npy_common.h"
26
+ #include "absl/algorithm/container.h"
27
+ #include "absl/container/flat_hash_set.h"
28
+ #include "absl/strings/str_format.h"
29
+ #include "absl/strings/string_view.h"
30
+ #include "absl/types/span.h"
31
+ #include "pybind11/cast.h"
32
+ #include "pybind11/numpy.h"
33
+ #include "pybind11/pybind11.h"
34
+ #include "pybind11/pytypes.h"
35
+ #include "pybind11_abseil/absl_casters.h"
36
+
37
+ namespace {
38
+
39
+ namespace py = pybind11;
40
+
41
+ PyObject* RemapNumpyArrayObjects(PyObject* array, PyObject* mapping,
42
+ bool inplace, PyObject* default_value) {
43
+ import_array();
44
+ if (!PyArray_Check(array)) {
45
+ PyErr_SetString(PyExc_TypeError, "'array' must be a np.ndarray.");
46
+ return nullptr;
47
+ }
48
+ if (!PyDict_Check(mapping)) {
49
+ PyErr_SetString(PyExc_TypeError, "'mapping' must be a Python dict.");
50
+ return nullptr;
51
+ }
52
+
53
+ PyArrayObject* array_obj = reinterpret_cast<PyArrayObject*>(array);
54
+ if (PyArray_TYPE(array_obj) != NPY_OBJECT) {
55
+ PyErr_SetString(PyExc_TypeError, "`array` must be an array of objects.");
56
+ return nullptr;
57
+ }
58
+
59
+ if (inplace) {
60
+ // We are returning original array so we need to increase the ref count.
61
+ Py_INCREF(array);
62
+ } else {
63
+ // We are returning a fresh copy.
64
+ array = PyArray_NewCopy(array_obj, NPY_CORDER);
65
+ if (array == nullptr) {
66
+ PyErr_SetString(PyExc_MemoryError, "Out of memory!");
67
+ return nullptr;
68
+ }
69
+ array_obj = reinterpret_cast<PyArrayObject*>(array);
70
+ }
71
+
72
+ if (PyArray_SIZE(array_obj) == 0) {
73
+ return array;
74
+ }
75
+
76
+ if (default_value == nullptr && PyDict_Size(mapping) == 0) {
77
+ return array;
78
+ }
79
+
80
+ NpyIter* iter = NpyIter_New(
81
+ array_obj, NPY_ITER_READWRITE | NPY_ITER_EXTERNAL_LOOP | NPY_ITER_REFS_OK,
82
+ NPY_KEEPORDER, NPY_NO_CASTING, nullptr);
83
+ if (iter == nullptr) {
84
+ PyErr_SetString(PyExc_MemoryError, "Out of memory!");
85
+ Py_XDECREF(array);
86
+ return nullptr;
87
+ }
88
+
89
+ NpyIter_IterNextFunc* iter_next = NpyIter_GetIterNext(iter, nullptr);
90
+ if (iter_next == nullptr) {
91
+ NpyIter_Deallocate(iter);
92
+ Py_XDECREF(array);
93
+ PyErr_SetString(PyExc_MemoryError, "Out of memory!");
94
+ return nullptr;
95
+ }
96
+
97
+ // Iterating arrays taken from:
98
+ // https://numpy.org/doc/stable/reference/c-api/iterator.html
99
+ char** data_pointer = NpyIter_GetDataPtrArray(iter);
100
+ npy_intp* stride_pointer = NpyIter_GetInnerStrideArray(iter);
101
+ npy_intp* inner_size_pointer = NpyIter_GetInnerLoopSizePtr(iter);
102
+ do {
103
+ char* data = *data_pointer;
104
+ npy_intp stride = *stride_pointer;
105
+ npy_intp count = *inner_size_pointer;
106
+ for (size_t i = 0; i < count; ++i) {
107
+ PyObject* entry;
108
+ std::memcpy(&entry, data, sizeof(PyObject*));
109
+ PyObject* result = PyDict_GetItem(mapping, entry);
110
+ if (result != nullptr) {
111
+ // Replace entry.
112
+ Py_INCREF(result);
113
+ Py_XDECREF(entry);
114
+ std::memcpy(data, &result, sizeof(PyObject*));
115
+ } else if (default_value != nullptr) {
116
+ // Replace entry with a default value.
117
+ Py_INCREF(default_value);
118
+ Py_XDECREF(entry);
119
+ std::memcpy(data, &default_value, sizeof(PyObject*));
120
+ }
121
+ data += stride;
122
+ }
123
+ } while (iter_next(iter));
124
+
125
+ NpyIter_Deallocate(iter);
126
+ return array;
127
+ }
128
+
129
+ // Convert 1D Numpy float array to a list of strings where each string has fixed
130
+ // number of decimal points. This is faster than Python list comprehension.
131
+ std::vector<std::string> FormatFloatArray(absl::Span<const float> values,
132
+ int num_decimal_places) {
133
+ std::vector<std::string> output;
134
+ output.reserve(values.size());
135
+
136
+ absl::c_transform(values, std::back_inserter(output),
137
+ [num_decimal_places](float value) {
138
+ return absl::StrFormat("%.*f", num_decimal_places, value);
139
+ });
140
+ return output;
141
+ }
142
+
143
+ py::array_t<bool> IsIn(
144
+ const py::array_t<PyObject*, py::array::c_style>& array,
145
+ const absl::flat_hash_set<absl::string_view>& test_elements, bool invert) {
146
+ const size_t num_elements = array.size();
147
+ py::array_t<bool> output(num_elements);
148
+ std::fill(output.mutable_data(), output.mutable_data() + output.size(),
149
+ invert);
150
+
151
+ // Shortcut: The output will be trivially always false if test_elements empty.
152
+ if (test_elements.empty()) {
153
+ return output;
154
+ }
155
+
156
+ for (size_t i = 0; i < num_elements; ++i) {
157
+ // Compare the string values instead of comparing just object pointers.
158
+ py::handle handle = array.data()[i];
159
+ if (!PyUnicode_Check(handle.ptr()) && !PyBytes_Check(handle.ptr())) {
160
+ continue;
161
+ }
162
+ if (test_elements.contains(py::cast<absl::string_view>(handle))) {
163
+ output.mutable_data()[i] = !invert;
164
+ }
165
+ }
166
+ if (array.ndim() > 1) {
167
+ auto shape =
168
+ std::vector<ptrdiff_t>(array.shape(), array.shape() + array.ndim());
169
+ return output.reshape(shape);
170
+ }
171
+ return output;
172
+ }
173
+
174
+ py::array RemapMultipleArrays(
175
+ const std::vector<py::array_t<PyObject*, py::array::c_style>>& arrays,
176
+ const py::dict& mapping) {
177
+ size_t array_size = arrays[0].size();
178
+ for (const auto& array : arrays) {
179
+ if (array.size() != array_size) {
180
+ throw py::value_error("All arrays must have the same length.");
181
+ }
182
+ }
183
+
184
+ // Create a result buffer.
185
+ auto result = py::array_t<int64_t>(array_size);
186
+ absl::Span<int64_t> result_buffer(result.mutable_data(), array_size);
187
+ PyObject* entry = PyTuple_New(arrays.size());
188
+ if (entry == nullptr) {
189
+ throw py::error_already_set();
190
+ }
191
+ std::vector<absl::Span<PyObject* const>> array_spans;
192
+ array_spans.reserve(arrays.size());
193
+ for (const auto& array : arrays) {
194
+ array_spans.emplace_back(array.data(), array.size());
195
+ }
196
+
197
+ // Iterate over arrays and look up elements in the `py_dict`.
198
+ bool fail = false;
199
+ for (size_t i = 0; i < array_size; ++i) {
200
+ for (size_t j = 0; j < array_spans.size(); ++j) {
201
+ PyTuple_SET_ITEM(entry, j, array_spans[j][i]);
202
+ }
203
+ PyObject* result = PyDict_GetItem(mapping.ptr(), entry);
204
+ if (result != nullptr) {
205
+ int64_t result_value = PyLong_AsLongLong(result);
206
+ if (result_value == -1 && PyErr_Occurred()) {
207
+ fail = true;
208
+ break;
209
+ }
210
+ if (result_value > std::numeric_limits<int64_t>::max() ||
211
+ result_value < std::numeric_limits<int64_t>::lowest()) {
212
+ PyErr_SetString(PyExc_OverflowError, "Result value too large.");
213
+ fail = true;
214
+ break;
215
+ }
216
+ result_buffer[i] = result_value;
217
+ } else {
218
+ PyErr_Format(PyExc_KeyError, "%R", entry);
219
+ fail = true;
220
+ break;
221
+ }
222
+ }
223
+
224
+ for (size_t j = 0; j < array_spans.size(); ++j) {
225
+ PyTuple_SET_ITEM(entry, j, nullptr);
226
+ }
227
+ Py_XDECREF(entry);
228
+ if (fail) {
229
+ throw py::error_already_set();
230
+ }
231
+ return result;
232
+ }
233
+
234
+ constexpr char kRemapNumpyArrayObjects[] = R"(
235
+ Replace objects in NumPy array of objects using mapping.
236
+
237
+ Args:
238
+ array: NumPy array with dtype=object.
239
+ mapping: Dict mapping old values to new values.
240
+ inplace: Bool (default False) whether to replace values inplace or to
241
+ create a new array.
242
+ default_value: If given, what value to map to if the mapping is missing
243
+ for that particular item. If not given, such items are left unchanged.
244
+
245
+ Returns
246
+ NumPy array of dtype object with values replaced according to mapping.
247
+ If inplace is True the original array is modified inplace otherwise a
248
+ new array is returned.
249
+ )";
250
+
251
+ constexpr char kFormatFloatArrayDoc[] = R"(
252
+ Converts float -> string array with given number of decimal places.
253
+ )";
254
+
255
+ constexpr char kIsInDoc[] = R"(
256
+ Computes whether each element is in test_elements.
257
+
258
+ Same use as np.isin, but much faster. If len(array) = n, len(test_elements) = m:
259
+ * This function has complexity O(n).
260
+ * np.isin with arrays of objects has complexity O(m*log(m) + n * log(m)).
261
+
262
+ Args:
263
+ array: Input NumPy array with dtype=object.
264
+ test_elements: The values against which to test each value of array.
265
+ invert: If True, the values in the returned array are inverted, as if
266
+ calculating `element not in test_elements`. Default is False.
267
+ `isin(a, b, invert=True)` is equivalent to but faster than `~isin(a, b)`.
268
+
269
+ Returns
270
+ A boolean array of the same shape as the input array. Each value `val` is:
271
+ * `val in test_elements` if `invert=False`,
272
+ * `val not in test_elements` if `invert=True`.
273
+ )";
274
+
275
+ constexpr char kRemapMultipleDoc[] = R"(
276
+ Maps keys from multiple aligned arrays to a single array.
277
+
278
+ Args:
279
+ arrays: Numpy arrays of the same length. The tuple of aligned entries is used
280
+ as key for the mapping.
281
+ mapping: Dict mapping from tuples to integer values.
282
+
283
+ Returns
284
+ NumPy array of dtype `int` with values looked up in mapping according to the
285
+ tuple of aligned array entries as keys.
286
+ )";
287
+
288
+ } // namespace
289
+
290
+ namespace alphafold3 {
291
+
292
+ void RegisterModuleStringArray(pybind11::module m) {
293
+ m.def(
294
+ "remap",
295
+ [](py::object array, py::object mapping, bool inplace,
296
+ py::object default_value) -> py::object {
297
+ PyObject* result = RemapNumpyArrayObjects(array.ptr(), mapping.ptr(),
298
+ inplace, default_value.ptr());
299
+ if (result == nullptr) {
300
+ throw py::error_already_set();
301
+ }
302
+ return py::reinterpret_steal<py::object>(result);
303
+ },
304
+ py::return_value_policy::take_ownership, py::arg("array"),
305
+ py::arg("mapping"), py::arg("inplace") = false, py::arg("default_value"),
306
+ py::doc(kRemapNumpyArrayObjects + 1));
307
+ m.def(
308
+ "remap",
309
+ [](py::object array, py::object mapping, bool inplace) -> py::object {
310
+ PyObject* result = RemapNumpyArrayObjects(array.ptr(), mapping.ptr(),
311
+ inplace, nullptr);
312
+ if (result == nullptr) {
313
+ throw py::error_already_set();
314
+ }
315
+ return py::reinterpret_steal<py::object>(result);
316
+ },
317
+ py::return_value_policy::take_ownership, py::arg("array"),
318
+ py::arg("mapping"), py::arg("inplace") = false,
319
+ py::doc(kRemapNumpyArrayObjects + 1));
320
+ m.def("format_float_array", &FormatFloatArray, py::arg("values"),
321
+ py::arg("num_decimal_places"), py::doc(kFormatFloatArrayDoc + 1),
322
+ py::call_guard<py::gil_scoped_release>());
323
+ m.def("isin", &IsIn, py::arg("array"), py::arg("test_elements"),
324
+ py::kw_only(), py::arg("invert") = false, py::doc(kIsInDoc + 1));
325
+ m.def("remap_multiple", &RemapMultipleArrays, py::arg("arrays"),
326
+ py::arg("mapping"), py::doc(kRemapMultipleDoc + 1));
327
+ }
328
+
329
+ } // namespace alphafold3
flax_model/alphafold3/structure/cpp/string_array_pybind.h ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * Copyright 2024 DeepMind Technologies Limited
3
+ *
4
+ * AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
5
+ * this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
6
+ *
7
+ * To request access to the AlphaFold 3 model parameters, follow the process set
8
+ * out at https://github.com/google-deepmind/alphafold3. You may only use these
9
+ * if received directly from Google. Use is subject to terms of use available at
10
+ * https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
11
+ */
12
+
13
+ #ifndef ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_STRING_ARRAY_PYBIND_H_
14
+ #define ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_STRING_ARRAY_PYBIND_H_
15
+
16
+ #include "pybind11/pybind11.h"
17
+
18
+ namespace alphafold3 {
19
+
20
+ void RegisterModuleStringArray(pybind11::module m);
21
+
22
+ }
23
+
24
+ #endif // ALPHAFOLD3_SRC_ALPHAFOLD3_STRUCTURE_PYTHON_STRING_ARRAY_PYBIND_H_
flax_model/alphafold3/structure/mmcif.py ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ """Low level mmCIF parsing operations and wrappers for nicer C++/Py errors.
4
+
5
+ Note that the cif_dict.CifDict class has many useful methods to help with data
6
+ extraction which are not shown in this file. You can find them in cif_dict.clif
7
+ together with docstrings. The cif_dict.CifDict class behaves like an immutable
8
+ Python dictionary (some methods are not implemented though).
9
+ """
10
+ from collections.abc import Callable, Mapping, Sequence
11
+ import functools
12
+ import itertools
13
+ import re
14
+ from typing import ParamSpec, TypeAlias, TypeVar
15
+
16
+ from flax_model.alphafold3.constants import chemical_components
17
+ from flax_model.alphafold3.cpp import cif_dict
18
+ from flax_model.alphafold3.cpp import mmcif_atom_site
19
+ from flax_model.alphafold3.cpp import mmcif_struct_conn
20
+ from flax_model.alphafold3.cpp import string_array
21
+ import numpy as np
22
+
23
+ Mmcif = cif_dict.CifDict
24
+
25
+
26
+ _P = ParamSpec('_P')
27
+ _T = TypeVar('_T')
28
+ _WappedFn: TypeAlias = Callable[_P, _T]
29
+
30
+
31
+ @functools.lru_cache(maxsize=256)
32
+ def int_id_to_str_id(num: int) -> str:
33
+ """Encodes a number as a string, using reverse spreadsheet style naming.
34
+
35
+ Args:
36
+ num: A positive integer.
37
+
38
+ Returns:
39
+ A string that encodes the positive integer using reverse spreadsheet style,
40
+ naming e.g. 1 = A, 2 = B, ..., 27 = AA, 28 = BA, 29 = CA, ... This is the
41
+ usual way to encode chain IDs in mmCIF files.
42
+ """
43
+ if num <= 0:
44
+ raise ValueError(f'Only positive integers allowed, got {num}.')
45
+
46
+ num = num - 1 # 1-based indexing.
47
+ output = []
48
+ while num >= 0:
49
+ output.append(chr(num % 26 + ord('A')))
50
+ num = num // 26 - 1
51
+ return ''.join(output)
52
+
53
+
54
+ @functools.lru_cache(maxsize=256)
55
+ def str_id_to_int_id(str_id: str) -> int:
56
+ """Encodes an mmCIF-style string chain ID as an integer.
57
+
58
+ The integer IDs are one based so this function is the inverse of
59
+ int_id_to_str_id.
60
+
61
+ Args:
62
+ str_id: A string chain ID consisting only of upper case letters A-Z.
63
+
64
+ Returns:
65
+ An integer that can be used to order mmCIF chain IDs in the standard
66
+ (reverse spreadsheet style) ordering.
67
+ """
68
+ if not re.match('^[A-Z]+$', str_id):
69
+ raise ValueError(f'String ID must be upper case letters, got {str_id}.')
70
+
71
+ offset = ord('A') - 1
72
+ output = 0
73
+ for i, c in enumerate(str_id):
74
+ output += (ord(c) - offset) * int(26**i)
75
+ return output
76
+
77
+
78
+ def from_string(mmcif_string: str | bytes) -> Mmcif:
79
+ return cif_dict.from_string(mmcif_string)
80
+
81
+
82
+ def parse_multi_data_cif(cif_string: str) -> dict[str, Mmcif]:
83
+ """Parses a CIF string with multiple data records.
84
+
85
+ For instance, the CIF string:
86
+
87
+ ```
88
+ data_001
89
+ _foo bar
90
+ #
91
+ data_002
92
+ _foo baz
93
+ ```
94
+
95
+ is parsed as:
96
+
97
+ ```
98
+ {'001': Mmcif({'_foo': ['bar']}), '002': Mmcif({'_foo': ['baz']})}
99
+ ```
100
+
101
+ Args:
102
+ cif_string: The multi-data CIF string to be parsed.
103
+
104
+ Returns:
105
+ A dictionary mapping record names to Mmcif objects with data.
106
+ """
107
+ return cif_dict.parse_multi_data_cif(cif_string)
108
+
109
+
110
+ # def tokenize(mmcif_string: str) -> list[str]:
111
+ # return cif_dict.tokenize(mmcif_string)
112
+
113
+
114
+ # def split_line(line: str) -> list[str]:
115
+ # return cif_dict.split_line(line)
116
+
117
+
118
+ class BondParsingError(Exception):
119
+ """Exception raised by errors when getting bond atom indices."""
120
+
121
+
122
+ def get_bond_atom_indices(
123
+ mmcif: Mmcif,
124
+ model_id: str = '1',
125
+ ) -> tuple[Sequence[int], Sequence[int]]:
126
+ """Extracts the indices of the atoms that participate in bonds.
127
+
128
+ Args:
129
+ mmcif: The mmCIF object to process.
130
+ model_id: The ID of the model that the returned atoms will belong to. This
131
+ should be a value in the mmCIF's _atom_site.pdbx_PDB_model_num column.
132
+
133
+ Returns:
134
+ Two lists of atom indices, `from_atoms` and `to_atoms`, each one having
135
+ length num_bonds (as defined by _struct_conn, the bonds table). The bond
136
+ i, defined by the i'th row in _struct_conn, is a bond from atom at index
137
+ from_atoms[i], to the atom at index to_atoms[i]. The indices are simple
138
+ 0-based indexes into the columns of the _atom_site table in the input
139
+ mmCIF, and do not necessarily correspond to the values in _atom_site.id,
140
+ or any other column.
141
+
142
+ Raises:
143
+ BondParsingError: If any of the required tables or columns are not present
144
+ in
145
+ the mmCIF, or if the _struct_conn table refers to atoms that cannot
146
+ be found in the _atom_site table.
147
+ """
148
+ try:
149
+ return mmcif_struct_conn.get_bond_atom_indices(mmcif, model_id)
150
+ except ValueError as e:
151
+ raise BondParsingError(str(e)) from e
152
+
153
+
154
+ def get_or_infer_type_symbol(
155
+ mmcif: Mmcif, ccd: chemical_components.Ccd | None = None
156
+ ) -> Sequence[str]:
157
+ """Returns the type symbol (element) for all of the atoms.
158
+
159
+ Args:
160
+ mmcif: A parsed mmCIF file in the Mmcif format.
161
+ ccd: The chemical component dictionary. If not provided, defaults to the
162
+ cached CCD.
163
+
164
+ If present, returns the _atom_site.type_symbol. If not, infers it using
165
+ _atom_site.label_comp_id (residue name), _atom_site.label_atom_id (atom name)
166
+ and the CCD.
167
+ """
168
+ ccd = ccd or chemical_components.Ccd()
169
+ type_symbol_fn = lambda res_name, atom_name: chemical_components.type_symbol(
170
+ ccd, res_name, atom_name
171
+ )
172
+ return mmcif_atom_site.get_or_infer_type_symbol(mmcif, type_symbol_fn)
173
+
174
+
175
+ def get_chain_type_by_entity_id(mmcif: Mmcif) -> Mapping[str, str]:
176
+ """Returns mapping from entity ID to its type or polymer type if available.
177
+
178
+ If the entity is in the _entity_poly table, returns its polymer chain type.
179
+ If not, returns the type as specified in the _entity table.
180
+
181
+ Args:
182
+ mmcif: CifDict holding the mmCIF.
183
+ """
184
+ poly_entity_id = mmcif.get('_entity_poly.entity_id', [])
185
+ poly_type = mmcif.get('_entity_poly.type', [])
186
+ poly_type_by_entity_id = dict(zip(poly_entity_id, poly_type, strict=True))
187
+
188
+ chain_type_by_entity_id = {}
189
+ for entity_id, entity_type in zip(
190
+ mmcif.get('_entity.id', []), mmcif.get('_entity.type', []), strict=True
191
+ ):
192
+ chain_type = poly_type_by_entity_id.get(entity_id) or entity_type
193
+ chain_type_by_entity_id[entity_id] = chain_type
194
+
195
+ return chain_type_by_entity_id
196
+
197
+
198
+ def get_internal_to_author_chain_id_map(mmcif: Mmcif) -> Mapping[str, str]:
199
+ """Returns a mapping from internal chain ID to the author chain ID.
200
+
201
+ Note that this is not a bijection. One author chain ID can map to multiple
202
+ internal chain IDs. For example, a protein chain and a ligand bound to it will
203
+ share the same author chain ID, but they will each have a unique internal
204
+ chain ID).
205
+
206
+ Args:
207
+ mmcif: CifDict holding the mmCIF.
208
+ """
209
+ return mmcif_atom_site.get_internal_to_author_chain_id_map(mmcif)
210
+
211
+
212
+ def get_experimental_method(mmcif: Mmcif) -> str | None:
213
+ field = '_exptl.method'
214
+ return ','.join(mmcif[field]).lower() if field in mmcif else None
215
+
216
+
217
+ def get_release_date(mmcif: Mmcif) -> str | None:
218
+ """Returns the oldest revision date."""
219
+ if '_pdbx_audit_revision_history.revision_date' not in mmcif:
220
+ return None
221
+
222
+ # Release dates are ISO-8601, hence sort well.
223
+ return min(mmcif['_pdbx_audit_revision_history.revision_date'])
224
+
225
+
226
+ def get_resolution(mmcif: Mmcif) -> float | None:
227
+ """Returns the resolution of the structure.
228
+
229
+ More than one resolution can be reported in an mmCIF. This function returns
230
+ the first one (in the order _refine.ls_d_res_high,
231
+ _em_3d_reconstruction.resolution, _reflns.d_resolution_high) that appears
232
+ in the mmCIF as is parseable as a float.
233
+
234
+ Args:
235
+ mmcif: An `Mmcif` object.
236
+
237
+ Returns:
238
+ The resolution as reported in the mmCIF.
239
+ """
240
+ for res_key in ('_refine.ls_d_res_high',
241
+ '_em_3d_reconstruction.resolution',
242
+ '_reflns.d_resolution_high'):
243
+ if res_key in mmcif:
244
+ try:
245
+ raw_resolution = mmcif[res_key][0]
246
+ return float(raw_resolution)
247
+ except ValueError:
248
+ continue
249
+ return None
250
+
251
+
252
+ def parse_oper_expr(oper_expression: str) -> list[tuple[str, ...]]:
253
+ """Determines which transforms to apply based on an MMCIF oper_expression str.
254
+
255
+ Args:
256
+ oper_expression: the field oper_expression from MMCIF format data.
257
+ Transform ids may be either numbers or single letters. Hyphens are used to
258
+ denote a numeric range of transforms to apply, and commas are used to
259
+ delimit a sequence of transforms. Where two sets of parentheses are
260
+ adjacent without a comma, the two sets of transforms should be combined as
261
+ a cartesian product, i.e. all possible pairs.
262
+ example 1,2,3 -> generate 3 copies of each chain by applying 1, 2 or 3.
263
+ example (1-3) -> generate 3 copies of each chain by applying 1, 2 or 3.
264
+ example (1-3)(4-6) -> generate 9 copies of each chain by applying one of
265
+ [(1,4), (1,5), (1,6),
266
+ (2,4), (2,5), (2,6),
267
+ (3,4), (3,5), (3,6)]
268
+ example (P) -> apply transform with id P.
269
+
270
+ Raises:
271
+ ValueError: Failure to parse oper_expression.
272
+
273
+ Returns:
274
+ A list with one element for each chain copy that should be generated.
275
+ Each element is a list of transform ids to apply.
276
+ """
277
+ # Expand ranges, e.g. 1-4 -> 1,2,3,4.
278
+ def range_expander(match):
279
+ return ','.join(
280
+ [str(i) for i in range(int(match.group(1)),
281
+ int(match.group(2)) + 1)])
282
+
283
+ ranges_expanded = re.sub(r'\b(\d+)-(\d+)', range_expander, oper_expression)
284
+
285
+ if re.fullmatch(r'(\w+,)*\w+', ranges_expanded):
286
+ # No brackets, just a single range, e.g. "1,2,3".
287
+ return [(t,) for t in ranges_expanded.split(',')]
288
+ elif re.fullmatch(r'\((\w+,)*\w+\)', ranges_expanded):
289
+ # Single range in brackets, e.g. "(1,2,3)".
290
+ return [(t,) for t in ranges_expanded[1:-1].split(',')]
291
+ elif re.fullmatch(r'\((\w+,)*\w+\)\((\w+,)*\w+\)', ranges_expanded):
292
+ # Cartesian product of two ranges, e.g. "(1,2,3)(4,5)".
293
+ part1, part2 = ranges_expanded[1:-1].split(')(')
294
+ return list(itertools.product(part1.split(','), part2.split(',')))
295
+ else:
296
+ raise ValueError(f'Unsupported oper_expression format: {oper_expression}')
297
+
298
+
299
+ def format_float_array(
300
+ values: np.ndarray, num_decimal_places: int) -> Sequence[str]:
301
+ """Converts 1D array to a list of strings with the given number of decimals.
302
+
303
+ This function is faster than converting via Python list comprehension, e.g.:
304
+ atoms_x = ['%.3f' % x for x in atoms_x]
305
+
306
+ Args:
307
+ values: A numpy array with values to convert. This array is casted to
308
+ float32 before doing the conversion.
309
+ num_decimal_places: The number of decimal points to keep, including trailing
310
+ zeros. E.g. for 1.07 and num_decimal_places=1: 1.1,
311
+ num_decimal_places=2: 1.07, num_decimal_places=3: 1.070.
312
+
313
+ Returns:
314
+ A list of formatted strings.
315
+ """
316
+ if values.ndim != 1:
317
+ raise ValueError(f'The given array must be 1D, got {values.ndim}D')
318
+
319
+ return string_array.format_float_array(
320
+ values=values.astype(np.float32), num_decimal_places=num_decimal_places
321
+ )
flax_model/alphafold3/structure/parsing.py ADDED
@@ -0,0 +1,1801 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ """Module for parsing various data sources and producing Structures."""
4
+
5
+ from collections.abc import Collection, Mapping, MutableMapping, Sequence
6
+ import dataclasses
7
+ import datetime
8
+ import enum
9
+ import functools
10
+ import itertools
11
+ from typing import TypeAlias
12
+
13
+ from flax_model.alphafold3.constants import chemical_components
14
+ from flax_model.alphafold3.constants import mmcif_names
15
+ from flax_model.alphafold3.constants import residue_names
16
+ from flax_model.alphafold3.cpp import mmcif_utils
17
+ from flax_model.alphafold3.cpp import string_array
18
+ from flax_model.alphafold3.structure import bioassemblies
19
+ from flax_model.alphafold3.structure import bonds
20
+ from flax_model.alphafold3.structure import chemical_components as struc_chem_comps
21
+ from flax_model.alphafold3.structure import mmcif
22
+ from flax_model.alphafold3.structure import structure
23
+ from flax_model.alphafold3.structure import structure_tables
24
+ import numpy as np
25
+
26
+
27
+ ChainIndex: TypeAlias = int
28
+ ResIndex: TypeAlias = int
29
+ AtomName: TypeAlias = str
30
+ BondAtomId: TypeAlias = tuple[ChainIndex, ResIndex, AtomName]
31
+
32
+ _INSERTION_CODE_REMAP: Mapping[str, str] = {'.': '?'}
33
+
34
+
35
+ class NoAtomsError(Exception):
36
+ """Raise when the mmCIF does not have any atoms."""
37
+
38
+
39
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
40
+ class BondIndices:
41
+ from_indices: list[int]
42
+ dest_indices: list[int]
43
+
44
+
45
+ @enum.unique
46
+ class ModelID(enum.Enum):
47
+ """Values for specifying model IDs when parsing."""
48
+
49
+ FIRST = 1 # The first model in the file.
50
+ ALL = 2 # All models in the file.
51
+
52
+
53
+ @enum.unique
54
+ class SequenceFormat(enum.Enum):
55
+ """The possible formats for an input sequence."""
56
+
57
+ FASTA = 'fasta' # One-letter code used in FASTA.
58
+ CCD_CODES = 'ccd_codes' # Multiple-letter chemical components dictionary ids.
59
+ LIGAND_SMILES = 'ligand_smiles' # SMILES string defining a molecule.
60
+
61
+
62
+ def _create_bond_lookup(
63
+ bonded_atom_pairs: Sequence[tuple[BondAtomId, BondAtomId]],
64
+ ) -> Mapping[tuple[ChainIndex, ResIndex], Mapping[AtomName, BondIndices]]:
65
+ """Creates maps to help find bonds during a loop over residues."""
66
+ bond_lookup = {}
67
+ for bond_i, (from_atom_id, dest_atom_id) in enumerate(bonded_atom_pairs):
68
+ from_chain_i, from_res_i, from_atom_name = from_atom_id
69
+ dest_chain_i, dest_res_i, dest_atom_name = dest_atom_id
70
+ bonds_by_from_atom_name = bond_lookup.setdefault(
71
+ (from_chain_i, from_res_i), {}
72
+ )
73
+ bonds_by_dest_atom_name = bond_lookup.setdefault(
74
+ (dest_chain_i, dest_res_i), {}
75
+ )
76
+ bonds_by_from_atom_name.setdefault(
77
+ from_atom_name, BondIndices(from_indices=[], dest_indices=[])
78
+ ).from_indices.append(bond_i)
79
+ bonds_by_dest_atom_name.setdefault(
80
+ dest_atom_name, BondIndices(from_indices=[], dest_indices=[])
81
+ ).dest_indices.append(bond_i)
82
+ return bond_lookup
83
+
84
+
85
+ def _get_atom_element(
86
+ ccd: chemical_components.Ccd, res_name: str, atom_name: str
87
+ ) -> str:
88
+ type_symbol = chemical_components.type_symbol(
89
+ ccd, res_name=res_name, atom_name=atom_name
90
+ )
91
+ return type_symbol or '?'
92
+
93
+
94
+ def _get_representative_atom(
95
+ ccd: chemical_components.Ccd,
96
+ res_name: str,
97
+ chain_type: str,
98
+ sequence_format: SequenceFormat,
99
+ ) -> tuple[str, str]:
100
+ match sequence_format:
101
+ case SequenceFormat.CCD_CODES:
102
+ atom_name = _get_first_non_leaving_atom(ccd=ccd, res_name=res_name)
103
+ atom_element = _get_atom_element(
104
+ ccd=ccd, res_name=res_name, atom_name=atom_name
105
+ )
106
+ return atom_name, atom_element
107
+ case SequenceFormat.LIGAND_SMILES:
108
+ return '', '?'
109
+ case SequenceFormat.FASTA:
110
+ if chain_type in mmcif_names.PEPTIDE_CHAIN_TYPES:
111
+ return 'CA', 'C'
112
+ if chain_type in mmcif_names.NUCLEIC_ACID_CHAIN_TYPES:
113
+ return "C1'", 'C'
114
+ else:
115
+ raise ValueError(chain_type)
116
+ case _:
117
+ raise ValueError(sequence_format)
118
+
119
+
120
+ @functools.lru_cache(maxsize=128)
121
+ def _get_first_non_leaving_atom(
122
+ ccd: chemical_components.Ccd, res_name: str
123
+ ) -> str:
124
+ """Returns first definitely non-leaving atom if exists, as a stand-in."""
125
+ all_atoms = struc_chem_comps.get_all_atoms_in_entry(ccd, res_name=res_name)[
126
+ '_chem_comp_atom.atom_id'
127
+ ]
128
+ representative_atom = all_atoms[0]
129
+ if representative_atom == 'O1' and len(all_atoms) > 1:
130
+ representative_atom = all_atoms[1]
131
+ return representative_atom
132
+
133
+
134
+ def _add_ligand_to_chem_comp(
135
+ chem_comp: MutableMapping[str, struc_chem_comps.ChemCompEntry],
136
+ ligand_id: str,
137
+ ligand_smiles: str,
138
+ ):
139
+ """Adds a ligand to chemical components. Raises ValueError on mismatch."""
140
+ new_entry = struc_chem_comps.ChemCompEntry(
141
+ type='non-polymer', pdbx_smiles=ligand_smiles
142
+ )
143
+
144
+ existing_entry = chem_comp.get(ligand_id)
145
+ if existing_entry is None:
146
+ chem_comp[ligand_id] = new_entry
147
+ elif existing_entry != new_entry:
148
+ raise ValueError(
149
+ f'Mismatching data for ligand {ligand_id}: '
150
+ f'{new_entry} != {existing_entry}'
151
+ )
152
+
153
+
154
+ def _get_first_model_id(cif: mmcif.Mmcif) -> str:
155
+ """Returns cheaply the first model ID from the mmCIF."""
156
+ return cif.get_array(
157
+ '_atom_site.pdbx_PDB_model_num', dtype=object, gather=slice(1)
158
+ )[0]
159
+
160
+
161
+ def _get_str_model_id(
162
+ cif: mmcif.Mmcif,
163
+ model_id: ModelID | int,
164
+ ) -> str:
165
+ """Converts a user-specified model_id argument into a string."""
166
+ match model_id:
167
+ case int():
168
+ str_model_id = str(model_id)
169
+ case enum.Enum():
170
+ # We compare the enum's value attribute since regular enum comparison
171
+ # breaks when adhoc importing.
172
+ match model_id.value:
173
+ case ModelID.FIRST.value:
174
+ try:
175
+ str_model_id = _get_first_model_id(cif)
176
+ except IndexError as e:
177
+ raise NoAtomsError(
178
+ 'The mmCIF does not have any atoms or'
179
+ ' _atom_site.pdbx_PDB_model_num is missing.'
180
+ ) from e
181
+ case ModelID.ALL.value:
182
+ str_model_id = ''
183
+ case _:
184
+ raise ValueError(
185
+ f'Model ID {model_id} with value {model_id.value} not recognized.'
186
+ )
187
+ case _:
188
+ raise ValueError(
189
+ f'Model ID {model_id} with type {type(model_id)} not recognized.'
190
+ )
191
+ return str_model_id
192
+
193
+
194
+ def _parse_bonds(
195
+ cif: mmcif.Mmcif,
196
+ atom_key: np.ndarray,
197
+ model_id: str,
198
+ ) -> bonds.Bonds:
199
+ """Returns the bonds table extracted from the mmCIF.
200
+
201
+ Args:
202
+ cif: The raw mmCIF to extract the bond information from.
203
+ atom_key: A numpy array defining atom key for each atom in _atom_site. Note
204
+ that the atom key must be computed before resolving alt-locs since this
205
+ function operates on the raw mmCIF!
206
+ model_id: The ID of the model to get bonds for.
207
+ """
208
+ if '_struct_conn.id' not in cif:
209
+ # This is the category key item for the _struct_conn table, therefore
210
+ # we use it to determine whether to parse bond info.
211
+ return bonds.Bonds.make_empty()
212
+ from_atom, dest_atom = mmcif.get_bond_atom_indices(cif, model_id)
213
+ from_atom = np.array(from_atom, dtype=np.int64)
214
+ dest_atom = np.array(dest_atom, dtype=np.int64)
215
+ num_bonds = from_atom.shape[0]
216
+ bond_key = np.arange(num_bonds, dtype=np.int64)
217
+ bond_type = cif.get_array('_struct_conn.conn_type_id', dtype=object)
218
+ if '_struct_conn.pdbx_role' in cif: # This column isn't always present.
219
+ bond_role = cif.get_array('_struct_conn.pdbx_role', dtype=object)
220
+ else:
221
+ bond_role = np.full((num_bonds,), '?', dtype=object)
222
+
223
+ bonds_mask = np.ones((num_bonds,), dtype=bool)
224
+ # Symmetries other than 1_555 imply the atom is not part of the asymmetric
225
+ # unit, and therefore this is a bond that only exists in the expanded
226
+ # bioassembly.
227
+ # We do not currently support parsing these types of bonds.
228
+ if '_struct_conn.ptnr1_symmetry' in cif:
229
+ ptnr1_symmetry = cif.get_array('_struct_conn.ptnr1_symmetry', dtype=object)
230
+ np.logical_and(bonds_mask, ptnr1_symmetry == '1_555', out=bonds_mask)
231
+ if '_struct_conn.ptnr2_symmetry' in cif:
232
+ ptnr2_symmetry = cif.get_array('_struct_conn.ptnr2_symmetry', dtype=object)
233
+ np.logical_and(bonds_mask, ptnr2_symmetry == '1_555', out=bonds_mask)
234
+ # Remove bonds that involve atoms that are not part of the structure,
235
+ # e.g. waters if include_water=False. In a rare case this also removes invalid
236
+ # bonds that are indicated by a key that is set to _atom_site size.
237
+ np.logical_and(bonds_mask, np.isin(from_atom, atom_key), out=bonds_mask)
238
+ np.logical_and(bonds_mask, np.isin(dest_atom, atom_key), out=bonds_mask)
239
+ return bonds.Bonds(
240
+ key=bond_key[bonds_mask],
241
+ type=bond_type[bonds_mask],
242
+ role=bond_role[bonds_mask],
243
+ from_atom_key=from_atom[bonds_mask],
244
+ dest_atom_key=dest_atom[bonds_mask],
245
+ )
246
+
247
+
248
+ @dataclasses.dataclass(frozen=True, slots=True)
249
+ class _MmcifHeader:
250
+ name: str
251
+ resolution: float | None
252
+ release_date: datetime.date | None
253
+ structure_method: str | None
254
+ bioassembly_data: bioassemblies.BioassemblyData | None
255
+ chemical_components_data: struc_chem_comps.ChemicalComponentsData | None
256
+
257
+
258
+ def _get_mmcif_header(
259
+ cif: mmcif.Mmcif,
260
+ fix_mse: bool,
261
+ fix_unknown_dna: bool,
262
+ ) -> _MmcifHeader:
263
+ """Extract header fields from an mmCIF object."""
264
+ entry_id = cif.get('_entry.id')
265
+ name = entry_id[0] if entry_id else cif.get_data_name()
266
+ resolution = mmcif.get_resolution(cif)
267
+
268
+ release_date = mmcif.get_release_date(cif)
269
+ if release_date is not None:
270
+ release_date = datetime.date.fromisoformat(release_date)
271
+
272
+ experiments = cif.get('_exptl.method')
273
+ structure_method = ','.join(experiments) if experiments else None
274
+
275
+ try:
276
+ bioassembly_data = bioassemblies.BioassemblyData.from_mmcif(cif)
277
+ except bioassemblies.MissingBioassemblyDataError:
278
+ bioassembly_data = None
279
+
280
+ try:
281
+ chemical_components_data = (
282
+ struc_chem_comps.ChemicalComponentsData.from_mmcif(
283
+ cif, fix_mse=fix_mse, fix_unknown_dna=fix_unknown_dna
284
+ )
285
+ )
286
+ except struc_chem_comps.MissingChemicalComponentsDataError:
287
+ chemical_components_data = None
288
+
289
+ return _MmcifHeader(
290
+ name=name,
291
+ resolution=resolution,
292
+ release_date=release_date,
293
+ structure_method=structure_method,
294
+ bioassembly_data=bioassembly_data,
295
+ chemical_components_data=chemical_components_data,
296
+ )
297
+
298
+
299
+ def from_parsed_mmcif(
300
+ mmcif_object: mmcif.Mmcif,
301
+ *,
302
+ name: str | None = None,
303
+ fix_mse_residues: bool = False,
304
+ fix_arginines: bool = False,
305
+ fix_unknown_dna: bool = False,
306
+ include_water: bool = False,
307
+ include_other: bool = False,
308
+ include_bonds: bool = False,
309
+ model_id: int | ModelID = ModelID.FIRST,
310
+ ) -> structure.Structure:
311
+ """Construct a Structure from a parsed mmCIF object.
312
+
313
+ This function is called by `from_mmcif` but can be useful when an mmCIF has
314
+ already been parsed e.g. to extract extra information from the header before
315
+ then converting to Structure for further manipulation.
316
+
317
+ Args:
318
+ mmcif_object: A parsed mmcif.Mmcif object.
319
+ name: Optional name for the structure. If not provided, the name will be
320
+ taken from the mmCIF data_ field.
321
+ fix_mse_residues: If True, selenium atom sites (SE) in selenomethionine
322
+ (MSE) residues will be changed to sulphur atom sites (SD). This is because
323
+ methionine (MET) residues are often replaced with MSE to aid X-Ray
324
+ crystallography. If False, the SE MSE atom sites won't be modified.
325
+ fix_arginines: If True, NH1 and NH2 in arginine will be swapped if needed so
326
+ that NH1 is always closer to CD than NH2. If False, no atom sites in
327
+ arginine will be touched. Note that HH11, HH12, HH21, HH22 are fixed too.
328
+ fix_unknown_dna: If True, residues with name N in DNA chains will have their
329
+ res_name replaced with DN. Atoms are not changed.
330
+ include_water: If True, water (HOH) molecules will be parsed. Water
331
+ molecules may be grouped into chains, where number of residues > 1. Water
332
+ molecules are usually grouped into chains but do not necessarily all share
333
+ the same chain ID.
334
+ include_other: If True, all other atoms that are not included by any of the
335
+ above parameters will be included. This covers e.g. "polypeptide(D)" and
336
+ "macrolide" entities, as well as all other non-standard types.
337
+ include_bonds: If True, bond information will be parsed from the mmCIF and
338
+ stored in the Structure.
339
+ model_id: Either the integer model ID to parse, or one of ModelID.FIRST to
340
+ parse the first model, or ModelID.ALL to parse all models.
341
+
342
+ Returns:
343
+ A Structure representation of the mmCIF object.
344
+ """
345
+ str_model_id = _get_str_model_id(cif=mmcif_object, model_id=model_id)
346
+ header = _get_mmcif_header(
347
+ mmcif_object, fix_mse=fix_mse_residues, fix_unknown_dna=fix_unknown_dna
348
+ )
349
+
350
+ chains, residues, atoms = get_tables(
351
+ cif=mmcif_object,
352
+ fix_mse_residues=fix_mse_residues,
353
+ fix_arginines=fix_arginines,
354
+ fix_unknown_dna=fix_unknown_dna,
355
+ include_water=include_water,
356
+ include_other=include_other,
357
+ model_id=str_model_id,
358
+ )
359
+
360
+ if include_bonds and atoms.size > 0:
361
+ # NB: parsing the atom table before the bonds table allows for a more
362
+ # informative error message when dealing with bad multi-model mmCIFs.
363
+ # Also always use a specific model ID, even when parsing all models.
364
+ if str_model_id == '': # pylint: disable=g-explicit-bool-comparison
365
+ bonds_model_id = _get_first_model_id(mmcif_object)
366
+ else:
367
+ bonds_model_id = str_model_id
368
+
369
+ bonds_table = _parse_bonds(
370
+ mmcif_object,
371
+ atom_key=atoms.key,
372
+ model_id=bonds_model_id,
373
+ )
374
+ else:
375
+ bonds_table = bonds.Bonds.make_empty()
376
+
377
+ return structure.Structure(
378
+ name=name if name is not None else header.name,
379
+ resolution=header.resolution,
380
+ release_date=header.release_date,
381
+ structure_method=header.structure_method,
382
+ bioassembly_data=header.bioassembly_data,
383
+ chemical_components_data=header.chemical_components_data,
384
+ bonds=bonds_table,
385
+ chains=chains,
386
+ residues=residues,
387
+ atoms=atoms,
388
+ )
389
+
390
+
391
+ def from_mmcif(
392
+ mmcif_string: str | bytes,
393
+ *,
394
+ name: str | None = None,
395
+ fix_mse_residues: bool = False,
396
+ fix_arginines: bool = False,
397
+ fix_unknown_dna: bool = False,
398
+ include_water: bool = False,
399
+ include_other: bool = False,
400
+ include_bonds: bool = False,
401
+ model_id: int | ModelID = ModelID.FIRST,
402
+ ) -> structure.Structure:
403
+ """Construct a Structure from a mmCIF string.
404
+
405
+ Args:
406
+ mmcif_string: The string contents of an mmCIF file.
407
+ name: Optional name for the structure. If not provided, the name will be
408
+ taken from the mmCIF data_ field.
409
+ fix_mse_residues: If True, selenium atom sites (SE) in selenomethionine
410
+ (MSE) residues will be changed to sulphur atom sites (SD). This is because
411
+ methionine (MET) residues are often replaced with MSE to aid X-Ray
412
+ crystallography. If False, the SE MSE atom sites won't be modified.
413
+ fix_arginines: If True, NH1 and NH2 in arginine will be swapped if needed so
414
+ that NH1 is always closer to CD than NH2. If False, no atom sites in
415
+ arginine will be touched. Note that HH11, HH12, HH21, HH22 are fixed too.
416
+ fix_unknown_dna: If True, residues with name N in DNA chains will have their
417
+ res_name replaced with DN. Atoms are not changed.
418
+ include_water: If True, water (HOH) molecules will be parsed. Water
419
+ molecules may be grouped into chains, where number of residues > 1. Water
420
+ molecules are usually grouped into chains but do not necessarily all share
421
+ the same chain ID.
422
+ include_other: If True, all other atoms that are not included by any of the
423
+ above parameters will be included. This covers e.g. "polypeptide(D)" and
424
+ "macrolide" entities, as well as all other non-standard types.
425
+ include_bonds: If True, bond information will be parsed from the mmCIF and
426
+ stored in the Structure.
427
+ model_id: Either the integer model ID to parse, or one of ModelID.FIRST to
428
+ parse the first model, or ModelID.ALL to parse all models.
429
+
430
+ Returns:
431
+ A Structure representation of the mmCIF string.
432
+ """
433
+ mmcif_object = mmcif.from_string(mmcif_string)
434
+
435
+ return from_parsed_mmcif(
436
+ mmcif_object,
437
+ name=name,
438
+ fix_mse_residues=fix_mse_residues,
439
+ fix_arginines=fix_arginines,
440
+ fix_unknown_dna=fix_unknown_dna,
441
+ include_water=include_water,
442
+ include_other=include_other,
443
+ include_bonds=include_bonds,
444
+ model_id=model_id,
445
+ )
446
+
447
+
448
+ def from_res_arrays(atom_mask: np.ndarray, **kwargs) -> structure.Structure:
449
+ """Returns Structure created from from arrays with a residue dimension.
450
+
451
+ All unset fields are filled with defaults (e.g. 1.0 for occupancy) or
452
+ unset/unknown values (e.g. UNK for residue type, or '.' for atom element).
453
+
454
+ Args:
455
+ atom_mask: A array with shape (num_res, num_atom). This is used to decide
456
+ which atoms in the atom dimension are present in a given residue. Present
457
+ atoms should have a nonzero value, e.g. 1.0 or True.
458
+ **kwargs: A mapping from field name to values. For all array-valued fields
459
+ these arrays must have a dimension of length num_res. Chain and residue
460
+ fields should have this as their only dimension and atom fields should be
461
+ shaped (num_res, num_atom). Coordinate fields may also have arbitrary
462
+ leading dimensions (they must be the same across all coordinate fields).
463
+ See structure.{CHAIN,RESIDUE,ATOM}_FIELDS for a list of allowed fields.
464
+ """
465
+ num_res, num_atom = atom_mask.shape
466
+ included_indices = np.flatnonzero(atom_mask)
467
+
468
+ array_fields = (
469
+ structure.CHAIN_FIELDS.keys()
470
+ | structure.RESIDUE_FIELDS.keys()
471
+ | structure.ATOM_FIELDS.keys()
472
+ )
473
+ initializer_kwargs = {}
474
+ fields = {}
475
+ for k, val in kwargs.items():
476
+ if k not in array_fields:
477
+ # The kwarg key isn't an array field name. Such kwargs are forwarded as-is
478
+ # to the constructor. They are expected to be global fields (e.g. name).
479
+ # Other values will raise an error when the constructor is called.
480
+ if k in structure.TABLE_FIELDS:
481
+ raise ValueError(f'Table fields must not be set. Got {k}.')
482
+ initializer_kwargs[k] = val
483
+ continue
484
+ elif val is None:
485
+ raise ValueError(f'{k} must be non-None.')
486
+
487
+ if not isinstance(val, np.ndarray):
488
+ raise TypeError(f'Value for {k} must be a NumPy array. Got {type(val)}.')
489
+ if k in structure.CHAIN_FIELDS or k in structure.RESIDUE_FIELDS:
490
+ if val.shape != (num_res,):
491
+ raise ValueError(
492
+ f'{k} must have shape ({num_res=},). Got {val.shape=}.'
493
+ )
494
+ # Do not reshape the chain/residue arrays, they have the shape we need.
495
+ fields[k] = val
496
+ else:
497
+ assert k in structure.ATOM_FIELDS
498
+ if val.shape[-2:] != (num_res, num_atom):
499
+ raise ValueError(
500
+ f'{k} must have final two dimensions of length '
501
+ f'{(num_res, num_atom)=}. Got {val.shape=}.'
502
+ )
503
+ leading_dims = val.shape[:-2]
504
+ flat_val = val.reshape(leading_dims + (-1,), order='C')
505
+ masked_val = flat_val[..., included_indices]
506
+ fields[k] = masked_val
507
+
508
+ # Get chain IDs or assume this is a single-chain structure.
509
+ chain_id = kwargs.get('chain_id', np.array(['A'] * num_res, dtype=object))
510
+ # Find chain starts in res-sized arrays, use these to make chain-sized arrays.
511
+ chain_start = np.concatenate(
512
+ ([0], np.where(chain_id[1:] != chain_id[:-1])[0] + 1)
513
+ )
514
+ if len(set(chain_id)) != len(chain_start):
515
+ raise ValueError(f'Chain IDs must be contiguous, but got {chain_id}')
516
+
517
+ chain_lengths = np.diff(chain_start, append=len(chain_id))
518
+ chain_key = np.repeat(np.arange(len(chain_start)), chain_lengths)
519
+
520
+ chain_entity_id = fields.get('chain_entity_id')
521
+ if chain_entity_id is not None:
522
+ entity_id = chain_entity_id[chain_start]
523
+ else:
524
+ entity_id = np.array(
525
+ [str(mmcif.str_id_to_int_id(cid)) for cid in chain_id[chain_start]],
526
+ dtype=object,
527
+ )
528
+ chain_str_empty = np.full((num_res,), '.', dtype=object)
529
+ chains_table = structure_tables.Chains(
530
+ key=chain_key[chain_start],
531
+ id=chain_id[chain_start],
532
+ type=fields.get('chain_type', chain_str_empty)[chain_start],
533
+ auth_asym_id=fields.get('chain_auth_asym_id', chain_id)[chain_start],
534
+ entity_id=entity_id,
535
+ entity_desc=fields.get('chain_entity_desc', chain_str_empty)[chain_start],
536
+ )
537
+
538
+ # Since all arrays are residue-shaped, we can use them directly.
539
+ res_key = np.arange(num_res, dtype=np.int64)
540
+ res_id = fields.get('res_id', res_key + 1).astype(np.int32)
541
+ residues_table = structure_tables.Residues(
542
+ key=res_key,
543
+ chain_key=chain_key,
544
+ id=res_id,
545
+ name=fields.get('res_name', np.full(num_res, 'UNK', dtype=object)),
546
+ auth_seq_id=fields.get(
547
+ 'res_auth_seq_id', np.char.mod('%d', res_id).astype(object)
548
+ ),
549
+ insertion_code=fields.get(
550
+ 'res_insertion_code', np.full(num_res, '?', dtype=object)
551
+ ),
552
+ )
553
+
554
+ # The atom-sized arrays have already been masked and reshaped.
555
+ num_atoms_per_res = np.sum(atom_mask, axis=1, dtype=np.int32)
556
+ num_atoms_total = np.sum(num_atoms_per_res, dtype=np.int32)
557
+ # Structure is immutable, so use the same array multiple times to save RAM.
558
+ atom_str_empty = np.full(num_atoms_total, '.', dtype=object)
559
+ atom_float32_zeros = np.zeros(num_atoms_total, dtype=np.float32)
560
+ atom_float32_ones = np.ones(num_atoms_total, dtype=np.float32)
561
+ atoms_table = structure_tables.Atoms(
562
+ key=np.arange(num_atoms_total, dtype=np.int64),
563
+ chain_key=np.repeat(chain_key, num_atoms_per_res),
564
+ res_key=np.repeat(res_key, num_atoms_per_res),
565
+ name=fields.get('atom_name', atom_str_empty),
566
+ element=fields.get('atom_element', atom_str_empty),
567
+ x=fields.get('atom_x', atom_float32_zeros),
568
+ y=fields.get('atom_y', atom_float32_zeros),
569
+ z=fields.get('atom_z', atom_float32_zeros),
570
+ b_factor=fields.get('atom_b_factor', atom_float32_zeros),
571
+ occupancy=fields.get('atom_occupancy', atom_float32_ones),
572
+ )
573
+
574
+ return structure.Structure(
575
+ chains=chains_table,
576
+ residues=residues_table,
577
+ atoms=atoms_table,
578
+ bonds=structure_tables.Bonds.make_empty(), # Currently not set.
579
+ **initializer_kwargs,
580
+ )
581
+
582
+
583
+ def expand_sequence(
584
+ sequence: str, chain_type: str, sequence_format: SequenceFormat
585
+ ) -> Sequence[str]:
586
+ """Returns full residue names based on a sequence string.
587
+
588
+ Args:
589
+ sequence: A string representing the sequence.
590
+ chain_type: The chain type of the sequence.
591
+ sequence_format: The format of the sequence argument.
592
+ """
593
+ match sequence_format:
594
+ case SequenceFormat.FASTA:
595
+ if not all(c.isalpha() for c in sequence):
596
+ raise ValueError(f'Sequence "{sequence}" has non-alphabetic characters')
597
+ match chain_type:
598
+ case mmcif_names.PROTEIN_CHAIN:
599
+ res_name_map = residue_names.PROTEIN_COMMON_ONE_TO_THREE
600
+ default_res_name = residue_names.UNK
601
+ case mmcif_names.RNA_CHAIN:
602
+ res_name_map = {r: r for r in residue_names.RNA_TYPES}
603
+ default_res_name = residue_names.UNK_RNA
604
+ case mmcif_names.DNA_CHAIN:
605
+ res_name_map = residue_names.DNA_COMMON_ONE_TO_TWO
606
+ default_res_name = residue_names.UNK_DNA
607
+ case _:
608
+ raise ValueError(f'{chain_type=} not supported for FASTA format.')
609
+ return [
610
+ res_name_map.get(one_letter_res, default_res_name)
611
+ for one_letter_res in sequence
612
+ ]
613
+ case SequenceFormat.CCD_CODES:
614
+ return sequence.strip('()').split(')(')
615
+ case SequenceFormat.LIGAND_SMILES:
616
+ ligand_id, _ = sequence.split(':', maxsplit=1)
617
+ return [ligand_id]
618
+
619
+
620
+ def from_sequences_and_bonds(
621
+ *,
622
+ sequences: Sequence[str],
623
+ chain_types: Sequence[str],
624
+ sequence_formats: Sequence[SequenceFormat],
625
+ bonded_atom_pairs: Sequence[tuple[BondAtomId, BondAtomId]] | None,
626
+ ccd: chemical_components.Ccd,
627
+ chain_ids: Sequence[str] | None = None,
628
+ name: str = 'from_sequences_and_bonds',
629
+ bond_type: str | None = None,
630
+ **constructor_args,
631
+ ) -> structure.Structure:
632
+ """Returns a minimal structure for the input sequences and bonds.
633
+
634
+ The returned structure will have at least one atom per residue. If the
635
+ residue has any bonded atoms, according to `bonded_atom_pairs`, then
636
+ all (and only) those atoms will be present for that residue. If the residue
637
+ is not involved in any bond then an arbitrary atom will be created.
638
+
639
+ Args:
640
+ sequences: A sequence of strings, each one representing a single chain.
641
+ chain_types: The types of each chain, e.g. polypeptide(L). The n-th element
642
+ describes the n-th sequence in `sequences`.
643
+ sequence_formats: The format of each sequence. The n-th element describes
644
+ the n-th sequence in `sequences`.
645
+ bonded_atom_pairs: A sequence of bonded atom pairs. Each atom is described
646
+ as a tuple of (chain_index, res_index, atom_name), where the first two
647
+ values are 0-based indices. The chain_index is the index of the chain in
648
+ the `sequences` argument, and the res_index is the index of the residue in
649
+ that sequence. The atom_name is the name of the atom in the residue, e.g.
650
+ CA. If the atom is not found in the standard atoms for that residue
651
+ (according to the CCD) then an error is raised.
652
+ ccd: The chemical components dictionary.
653
+ chain_ids: A sequence of chain IDs, one for each chain in `sequences`. If
654
+ not provided, then the chain IDs will be generated automatically based on
655
+ sequence indices.
656
+ name: A name for the returned structure.
657
+ bond_type: This type will be used for all bonds in the structure, where type
658
+ follows PDB scheme, e.g. unknown (?), hydrog, metalc, covale, disulf.
659
+ **constructor_args: These arguments are passed directly to the
660
+ structure.Structure constructor.
661
+ """
662
+ chain_id = []
663
+ chain_type = []
664
+ chain_res_count = []
665
+ res_id = []
666
+ res_name = []
667
+ res_atom_count = []
668
+ atom_name = []
669
+ atom_element = []
670
+ chem_comp = {}
671
+
672
+ num_bonds = len(bonded_atom_pairs or ())
673
+ from_atom_key = np.full((num_bonds,), -1, dtype=np.int64)
674
+ dest_atom_key = np.full((num_bonds,), -1, dtype=np.int64)
675
+
676
+ # Create map (chain_i, res_i) -> {atom_name -> (from_idxs dest_idxs)}.
677
+ # This allows quick lookup of whether a residue has any bonded atoms, and
678
+ # which bonds those atoms participate in.
679
+ bond_lookup = _create_bond_lookup(bonded_atom_pairs or ())
680
+
681
+ current_atom_key = 0
682
+ for chain_i, (sequence, curr_chain_type, sequence_format) in enumerate(
683
+ zip(sequences, chain_types, sequence_formats, strict=True)
684
+ ):
685
+ if chain_ids is not None:
686
+ current_chain_id = chain_ids[chain_i]
687
+ else:
688
+ current_chain_id = mmcif.int_id_to_str_id(chain_i + 1)
689
+ num_chain_residues = 0
690
+ for res_i, full_res_name in enumerate(
691
+ expand_sequence(sequence, curr_chain_type, sequence_format)
692
+ ):
693
+ current_res_id = res_i + 1
694
+ num_res_atoms = 0
695
+
696
+ # Look for bonded atoms in the bond lookup and if any are found, add
697
+ # their atom keys to the bond atom_key columns.
698
+ if bond_indices_by_atom_name := bond_lookup.get((chain_i, res_i)):
699
+ comp_atoms = None
700
+ if sequence_format != SequenceFormat.LIGAND_SMILES:
701
+ comp_atoms = set(ccd.get(full_res_name)['_chem_comp_atom.atom_id'])
702
+ for bond_atom_name, bond_indices in bond_indices_by_atom_name.items():
703
+ if comp_atoms is not None and bond_atom_name not in comp_atoms:
704
+ raise ValueError(
705
+ f'Bonded atom "{bond_atom_name}" was not found in the list of'
706
+ f' atoms of the chemical component {full_res_name}. Valid atom'
707
+ f' names for {full_res_name} are: {sorted(comp_atoms)}.'
708
+ ' This is likely caused by an invalid atom name in the bonded'
709
+ f' atom (chain_id={current_chain_id}, res_id={current_res_id},'
710
+ f' atom_name={bond_atom_name}) specified in `bondedAtomPairs`'
711
+ ' in the input JSON.'
712
+ )
713
+ atom_name.append(bond_atom_name)
714
+ atom_element.append(
715
+ _get_atom_element(
716
+ ccd=ccd, res_name=full_res_name, atom_name=bond_atom_name
717
+ )
718
+ )
719
+ for from_bond_i in bond_indices.from_indices:
720
+ from_atom_key[from_bond_i] = current_atom_key
721
+ for dest_bond_i in bond_indices.dest_indices:
722
+ dest_atom_key[dest_bond_i] = current_atom_key
723
+ current_atom_key += 1
724
+ num_res_atoms += 1
725
+ else:
726
+ # If this residue has no bonded atoms then we need to add one atom
727
+ # like in from_sequences.
728
+ assert num_res_atoms == 0
729
+ rep_atom_name, rep_atom_element = _get_representative_atom(
730
+ ccd=ccd,
731
+ res_name=full_res_name,
732
+ chain_type=curr_chain_type,
733
+ sequence_format=sequence_format,
734
+ )
735
+ atom_name.append(rep_atom_name)
736
+ atom_element.append(rep_atom_element)
737
+ num_res_atoms += 1
738
+ current_atom_key += 1
739
+
740
+ if sequence_format == SequenceFormat.LIGAND_SMILES:
741
+ # Sequence expect to be in the format <ligand_id>:<ligand_smiles>,
742
+ # which always corresponds to a single-residue chain.
743
+ ligand_id, ligand_smiles = sequence.split(':', maxsplit=1)
744
+ if ccd.get(ligand_id) is not None:
745
+ raise ValueError(
746
+ f'Ligand name {ligand_id} is in CCD - it is not supported to give'
747
+ ' ligands created from SMILES the same name as CCD components.'
748
+ )
749
+ # We need to provide additional chemical components metadata for
750
+ # ligands specified via SMILES strings since they might not be in CCD.
751
+ _add_ligand_to_chem_comp(chem_comp, ligand_id, ligand_smiles)
752
+
753
+ assert num_res_atoms >= 1
754
+ res_atom_count.append(num_res_atoms)
755
+ num_chain_residues += 1
756
+ res_id.append(current_res_id)
757
+ res_name.append(full_res_name)
758
+
759
+ chain_id.append(current_chain_id)
760
+ chain_type.append(curr_chain_type)
761
+ chain_res_count.append(num_chain_residues)
762
+
763
+ chem_comp_data = struc_chem_comps.ChemicalComponentsData(chem_comp)
764
+ chem_comp_data = struc_chem_comps.populate_missing_ccd_data(
765
+ ccd=ccd,
766
+ chemical_components_data=chem_comp_data,
767
+ chemical_component_ids=set(res_name),
768
+ )
769
+
770
+ if bonded_atom_pairs is not None:
771
+ unknown_bond_col = np.full((num_bonds,), '?', dtype=object)
772
+ if bond_type is None:
773
+ bond_type_col = unknown_bond_col
774
+ else:
775
+ bond_type_col = np.full((num_bonds,), bond_type, dtype=object)
776
+ bonds_table = bonds.Bonds(
777
+ key=np.arange(num_bonds, dtype=np.int64),
778
+ type=bond_type_col,
779
+ role=unknown_bond_col,
780
+ from_atom_key=from_atom_key,
781
+ dest_atom_key=dest_atom_key,
782
+ )
783
+ else:
784
+ bonds_table = structure_tables.Bonds.make_empty()
785
+
786
+ chain_key = np.arange(len(sequences), dtype=np.int64) # 1 chain per sequence.
787
+ chain_id = np.array(chain_id, dtype=object)
788
+ chains_table = structure_tables.Chains(
789
+ key=chain_key,
790
+ id=chain_id,
791
+ type=np.array(chain_type, dtype=object),
792
+ auth_asym_id=chain_id,
793
+ entity_id=np.char.mod('%d', chain_key + 1).astype(object),
794
+ entity_desc=np.array(['.'] * len(chain_key), dtype=object),
795
+ )
796
+
797
+ res_key = np.arange(len(res_name), dtype=np.int64)
798
+ res_chain_key = np.repeat(chain_key, chain_res_count)
799
+ residues_table = structure_tables.Residues(
800
+ key=res_key,
801
+ chain_key=res_chain_key,
802
+ id=np.array(res_id, dtype=np.int32),
803
+ name=np.array(res_name, dtype=object),
804
+ auth_seq_id=np.char.mod('%d', res_id).astype(object),
805
+ insertion_code=np.full(len(res_name), '?', dtype=object),
806
+ )
807
+
808
+ num_atoms = current_atom_key
809
+ atom_float32_zeros = np.zeros(num_atoms, dtype=np.float32)
810
+ atoms_table = structure_tables.Atoms(
811
+ key=np.arange(num_atoms, dtype=np.int64),
812
+ chain_key=np.repeat(res_chain_key, res_atom_count),
813
+ res_key=np.repeat(res_key, res_atom_count),
814
+ name=np.array(atom_name, dtype=object),
815
+ element=np.array(atom_element, dtype=object),
816
+ x=atom_float32_zeros,
817
+ y=atom_float32_zeros,
818
+ z=atom_float32_zeros,
819
+ b_factor=atom_float32_zeros,
820
+ occupancy=np.ones(num_atoms, np.float32),
821
+ )
822
+
823
+ return structure.Structure(
824
+ name=name,
825
+ atoms=atoms_table,
826
+ residues=residues_table,
827
+ chains=chains_table,
828
+ bonds=bonds_table,
829
+ chemical_components_data=chem_comp_data,
830
+ **constructor_args,
831
+ )
832
+
833
+
834
+ class _ChainResBuilder:
835
+ """Class for incrementally building chain and residue tables."""
836
+
837
+ def __init__(
838
+ self,
839
+ *,
840
+ chain_key_by_chain_id: Mapping[str, int],
841
+ entity_id_by_chain_id: Mapping[str, str],
842
+ chain_type_by_entity_id: Mapping[str, str],
843
+ entity_desc_by_entity_id: Mapping[str, str],
844
+ fix_mse_residues: bool,
845
+ fix_unknown_dna: bool,
846
+ ):
847
+ # Len: num_chains.
848
+ self.chain_key = []
849
+ self.chain_id = []
850
+ self.chain_type = []
851
+ self.chain_auth_asym_id = []
852
+ self.chain_entity_id = []
853
+ self.chain_entity_desc = []
854
+
855
+ # Len: num_residues.
856
+ self.res_key = []
857
+ self.res_chain_key = []
858
+ self.res_id = []
859
+ self.res_name = []
860
+ self.res_auth_seq_id = []
861
+ self.res_insertion_code = []
862
+
863
+ self.chain_key_by_chain_id = chain_key_by_chain_id
864
+ self.entity_id_by_chain_id = entity_id_by_chain_id
865
+ self.chain_type_by_entity_id = chain_type_by_entity_id
866
+ self.entity_desc_by_entity_id = entity_desc_by_entity_id
867
+ self.key_for_res: dict[tuple[str, str, str, str], int] = {}
868
+
869
+ self._fix_mse_residues = fix_mse_residues
870
+ self._fix_unknown_dna = fix_unknown_dna
871
+
872
+ def add_residues(
873
+ self,
874
+ *,
875
+ chain_ids: np.ndarray,
876
+ chain_auth_asym_ids: np.ndarray,
877
+ res_ids: np.ndarray,
878
+ res_names: np.ndarray,
879
+ res_auth_seq_ids: np.ndarray,
880
+ res_ins_codes: np.ndarray,
881
+ ):
882
+ """Adds a residue (and its chain) to the tables."""
883
+ # Create chain table data.
884
+ if chain_ids.size == 0:
885
+ return
886
+
887
+ chain_ids_with_prev = np.concatenate(
888
+ (([self.chain_id[-1] if self.chain_id else None], chain_ids))
889
+ )
890
+ chain_change_mask = chain_ids_with_prev[:-1] != chain_ids_with_prev[1:]
891
+ chain_change_ids = chain_ids[chain_change_mask]
892
+ chain_keys = string_array.remap(
893
+ chain_change_ids, self.chain_key_by_chain_id, inplace=False
894
+ )
895
+ self.chain_key.extend(chain_keys)
896
+ self.chain_id.extend(chain_change_ids)
897
+ self.chain_auth_asym_id.extend(chain_auth_asym_ids[chain_change_mask])
898
+ chain_entity_id = string_array.remap(
899
+ chain_change_ids, self.entity_id_by_chain_id, inplace=False
900
+ )
901
+ self.chain_entity_id.extend(chain_entity_id)
902
+ chain_type = string_array.remap(
903
+ chain_entity_id, self.chain_type_by_entity_id, inplace=False
904
+ )
905
+ self.chain_type.extend(chain_type)
906
+ chain_entity_desc = string_array.remap(
907
+ chain_entity_id, self.entity_desc_by_entity_id, inplace=False
908
+ )
909
+ self.chain_entity_desc.extend(chain_entity_desc)
910
+
911
+ # Create residue table data.
912
+ num_prev_res = len(self.res_id)
913
+ res_keys = np.arange(num_prev_res, num_prev_res + len(res_ids))
914
+ res_iter = zip(
915
+ chain_ids,
916
+ res_auth_seq_ids,
917
+ res_names,
918
+ res_ins_codes,
919
+ strict=True,
920
+ )
921
+ key_for_res_update = {
922
+ res_unique_id: res_key
923
+ for res_key, res_unique_id in enumerate(res_iter, num_prev_res)
924
+ }
925
+ self.key_for_res.update(key_for_res_update)
926
+ self.res_key.extend(res_keys)
927
+ self.res_chain_key.extend(
928
+ string_array.remap(chain_ids, self.chain_key_by_chain_id, inplace=False)
929
+ )
930
+ self.res_id.extend(res_ids)
931
+ self.res_name.extend(res_names)
932
+ self.res_auth_seq_id.extend(res_auth_seq_ids)
933
+ self.res_insertion_code.extend(res_ins_codes)
934
+
935
+ def make_chains_table(self) -> structure_tables.Chains:
936
+ """Returns the Structure chains table."""
937
+ chain_key = np.array(self.chain_key, dtype=np.int64)
938
+ if not np.all(chain_key[:-1] <= chain_key[1:]):
939
+ # If the order is inconsistent with the atoms table, sort so that it is.
940
+ order = np.argsort(self.chain_key, kind='stable')
941
+ return structure_tables.Chains(
942
+ key=chain_key[order],
943
+ id=np.array(self.chain_id, dtype=object)[order],
944
+ type=np.array(self.chain_type, dtype=object)[order],
945
+ auth_asym_id=np.array(self.chain_auth_asym_id, dtype=object)[order],
946
+ entity_id=np.array(self.chain_entity_id, dtype=object)[order],
947
+ entity_desc=np.array(self.chain_entity_desc, dtype=object)[order],
948
+ )
949
+ return structure_tables.Chains(
950
+ key=chain_key,
951
+ id=np.array(self.chain_id, dtype=object),
952
+ type=np.array(self.chain_type, dtype=object),
953
+ auth_asym_id=np.array(self.chain_auth_asym_id, dtype=object),
954
+ entity_id=np.array(self.chain_entity_id, dtype=object),
955
+ entity_desc=np.array(self.chain_entity_desc, dtype=object),
956
+ )
957
+
958
+ def make_residues_table(self) -> structure_tables.Residues:
959
+ """Returns the Structure residues table."""
960
+ res_name = np.array(self.res_name, dtype=object)
961
+ res_chain_key = np.array(self.res_chain_key, dtype=np.int64)
962
+
963
+ if self._fix_mse_residues:
964
+ string_array.remap(res_name, mapping={'MSE': 'MET'}, inplace=True)
965
+
966
+ if self._fix_unknown_dna:
967
+ # Remap residues from N -> DN in DNA chains only.
968
+ dna_chain_mask = (
969
+ np.array(self.chain_type, dtype=object) == mmcif_names.DNA_CHAIN
970
+ )
971
+ dna_chain_key = np.array(self.chain_key, dtype=object)[dna_chain_mask]
972
+ res_name[(res_name == 'N') & np.isin(res_chain_key, dna_chain_key)] = 'DN'
973
+
974
+ if not np.all(res_chain_key[:-1] <= res_chain_key[1:]):
975
+ # If the order is inconsistent with the atoms table, sort so that it is.
976
+ order = np.argsort(res_chain_key, kind='stable')
977
+ return structure_tables.Residues(
978
+ key=np.array(self.res_key, dtype=np.int64)[order],
979
+ chain_key=res_chain_key[order],
980
+ id=np.array(self.res_id, dtype=np.int32)[order],
981
+ name=res_name[order],
982
+ auth_seq_id=np.array(self.res_auth_seq_id, dtype=object)[order],
983
+ insertion_code=np.array(self.res_insertion_code, dtype=object)[order],
984
+ )
985
+ return structure_tables.Residues(
986
+ key=np.array(self.res_key, dtype=np.int64),
987
+ chain_key=res_chain_key,
988
+ id=np.array(self.res_id, dtype=np.int32),
989
+ name=res_name,
990
+ auth_seq_id=np.array(self.res_auth_seq_id, dtype=object),
991
+ insertion_code=np.array(self.res_insertion_code, dtype=object),
992
+ )
993
+
994
+
995
+ def _get_string_array_default(cif: mmcif.Mmcif, key: str, default: list[str]):
996
+ try:
997
+ return cif.get_array(key, dtype=object)
998
+ except KeyError:
999
+ return default
1000
+
1001
+
1002
+ def _generate_required_tables_if_missing(
1003
+ cif: mmcif.Mmcif,
1004
+ ) -> Mapping[str, Sequence[str]]:
1005
+ """Generates all required tables and columns if missing."""
1006
+ update = {}
1007
+
1008
+ atom_site_entities = _get_string_array_default(
1009
+ cif, '_atom_site.label_entity_id', []
1010
+ )
1011
+
1012
+ # OpenMM produces files that don't have any of the tables and also have
1013
+ # _atom_site.label_entity_id set to '?' for all atoms. We infer the entities
1014
+ # based on the _atom_site.label_asym_id column. We start with cheaper O(1)
1015
+ # checks to prevent running the expensive O(n) check on most files.
1016
+ if (
1017
+ len(atom_site_entities) > 0 # pylint: disable=g-explicit-length-test
1018
+ and '_entity.id' not in cif # Ignore if the _entity table exists.
1019
+ and atom_site_entities[0] == '?' # Cheap check.
1020
+ and set(atom_site_entities) == {'?'} # Expensive check.
1021
+ ):
1022
+ label_asym_ids = cif.get_array('_atom_site.label_asym_id', dtype=object)
1023
+ atom_site_entities = [
1024
+ str(mmcif.str_id_to_int_id(cid)) for cid in label_asym_ids
1025
+ ]
1026
+ # Update _atom_site.label_entity_id to be consistent with the new tables.
1027
+ update['_atom_site.label_entity_id'] = atom_site_entities
1028
+
1029
+ # Check table existence by checking the presence of its primary key.
1030
+ if '_struct_asym.id' not in cif:
1031
+ # Infer the _struct_asym table using the _atom_site table.
1032
+ asym_ids = _get_string_array_default(cif, '_atom_site.label_asym_id', [])
1033
+
1034
+ if len(atom_site_entities) == 0 or len(asym_ids) == 0: # pylint: disable=g-explicit-length-test
1035
+ raise ValueError(
1036
+ 'Could not parse an mmCIF with no _struct_asym table and also no '
1037
+ '_atom_site.label_entity_id or _atom_site.label_asym_id columns.'
1038
+ )
1039
+
1040
+ # Deduplicate, but keep the order intact - dict.fromkeys maintains order.
1041
+ entity_id_chain_id_pairs = list(
1042
+ dict.fromkeys(zip(atom_site_entities, asym_ids, strict=True))
1043
+ )
1044
+ update['_struct_asym.entity_id'] = [e for e, _ in entity_id_chain_id_pairs]
1045
+ update['_struct_asym.id'] = [c for _, c in entity_id_chain_id_pairs]
1046
+
1047
+ if '_entity.id' not in cif:
1048
+ # Infer the _entity_poly and _entity tables using the _atom_site table.
1049
+ residues = _get_string_array_default(cif, '_atom_site.label_comp_id', [])
1050
+ group_pdb = _get_string_array_default(cif, '_atom_site.group_PDB', [])
1051
+ if '_atom_site.label_entity_id' in cif:
1052
+ entities = atom_site_entities
1053
+ else:
1054
+ # If _atom_site.label_entity_id not set, use the asym_id -> entity_id map.
1055
+ asym_to_entity = dict(
1056
+ zip(
1057
+ cif['_struct_asym.id'], cif['_struct_asym.entity_id'], strict=True
1058
+ )
1059
+ )
1060
+ entities = string_array.remap(
1061
+ cif.get_array('_atom_site.label_asym_id', dtype=object),
1062
+ mapping=asym_to_entity,
1063
+ )
1064
+
1065
+ entity_ids = []
1066
+ entity_types = []
1067
+ entity_poly_entity_ids = []
1068
+ entity_poly_types = []
1069
+ entity_poly_table_missing = '_entity_poly.entity_id' not in cif
1070
+ for entity_id, group in itertools.groupby(
1071
+ zip(entities, residues, group_pdb, strict=True), key=lambda e: e[0]
1072
+ ):
1073
+ _, entity_residues, entity_group_pdb = zip(*group, strict=True)
1074
+ entity_type = _guess_entity_type(
1075
+ chain_residues=entity_residues, atom_types=entity_group_pdb
1076
+ )
1077
+ entity_ids.append(entity_id)
1078
+ entity_types.append(entity_type)
1079
+
1080
+ if entity_poly_table_missing and entity_type == mmcif_names.POLYMER_CHAIN:
1081
+ polymer_type = mmcif_names.guess_polymer_type(entity_residues)
1082
+ entity_poly_entity_ids.append(entity_id)
1083
+ entity_poly_types.append(polymer_type)
1084
+
1085
+ update['_entity.id'] = entity_ids
1086
+ update['_entity.type'] = entity_types
1087
+ if entity_poly_table_missing:
1088
+ update['_entity_poly.entity_id'] = entity_poly_entity_ids
1089
+ update['_entity_poly.type'] = entity_poly_types
1090
+
1091
+ if '_atom_site.type_symbol' not in cif:
1092
+ update['_atom_site.type_symbol'] = mmcif.get_or_infer_type_symbol(cif)
1093
+
1094
+ return update
1095
+
1096
+
1097
+ def _maybe_add_missing_scheme_tables(
1098
+ cif: mmcif.Mmcif,
1099
+ res_starts: Sequence[int],
1100
+ label_asym_ids: np.ndarray,
1101
+ label_seq_ids: np.ndarray,
1102
+ label_comp_ids: np.ndarray,
1103
+ auth_seq_ids: np.ndarray,
1104
+ pdb_ins_codes: np.ndarray,
1105
+ ) -> Mapping[str, Sequence[str]]:
1106
+ """If missing, infers the scheme tables from the _atom_site table."""
1107
+ update = {}
1108
+
1109
+ required_poly_seq_scheme_cols = (
1110
+ '_pdbx_poly_seq_scheme.asym_id',
1111
+ '_pdbx_poly_seq_scheme.pdb_seq_num',
1112
+ '_pdbx_poly_seq_scheme.pdb_ins_code',
1113
+ '_pdbx_poly_seq_scheme.seq_id',
1114
+ '_pdbx_poly_seq_scheme.mon_id',
1115
+ '_pdbx_poly_seq_scheme.pdb_strand_id',
1116
+ )
1117
+ if not all(col in cif for col in required_poly_seq_scheme_cols):
1118
+ # Create a mask for atoms where each polymer residue start.
1119
+ entity_id_by_chain_id = dict(
1120
+ zip(cif['_struct_asym.id'], cif['_struct_asym.entity_id'], strict=True)
1121
+ )
1122
+ chain_type_by_entity_id = dict(
1123
+ zip(cif['_entity.id'], cif['_entity.type'], strict=True)
1124
+ )
1125
+ # Remap asym ID -> entity ID.
1126
+ label_entity_id = string_array.remap(
1127
+ label_asym_ids, mapping=entity_id_by_chain_id, inplace=False
1128
+ )
1129
+ # Remap entity ID -> chain type.
1130
+ chain_type = string_array.remap(
1131
+ label_entity_id, mapping=chain_type_by_entity_id, inplace=False
1132
+ )
1133
+ res_mask = np.zeros_like(label_seq_ids, dtype=bool)
1134
+ res_mask[res_starts] = True
1135
+ res_mask &= chain_type == mmcif_names.POLYMER_CHAIN
1136
+
1137
+ entity_poly_seq_cols = (
1138
+ '_entity_poly_seq.entity_id',
1139
+ '_entity_poly_seq.num',
1140
+ '_entity_poly_seq.mon_id',
1141
+ )
1142
+ if all(col in cif for col in entity_poly_seq_cols):
1143
+ # Use _entity_poly_seq if available.
1144
+ poly_seq_num = cif.get_array('_entity_poly_seq.num', dtype=object)
1145
+ poly_seq_mon_id = cif.get_array('_entity_poly_seq.mon_id', dtype=object)
1146
+ poly_seq_entity_id = cif.get_array(
1147
+ '_entity_poly_seq.entity_id', dtype=object
1148
+ )
1149
+ # We have to add the entity ID to the residue ID because multiple residues
1150
+ # can share the same ID. This also allows using string_array.remap.
1151
+ label_seq_id_to_auth_seq_id = dict(
1152
+ zip(
1153
+ np.char.add(label_entity_id[res_mask], label_seq_ids[res_mask]),
1154
+ auth_seq_ids[res_mask],
1155
+ strict=True,
1156
+ )
1157
+ )
1158
+ scheme_pdb_seq_num = string_array.remap(
1159
+ np.char.add(poly_seq_entity_id, poly_seq_num),
1160
+ mapping=label_seq_id_to_auth_seq_id,
1161
+ default_value='.',
1162
+ )
1163
+ label_seq_id_to_ins_code = dict(
1164
+ zip(label_seq_ids[res_mask], pdb_ins_codes[res_mask], strict=True)
1165
+ )
1166
+ scheme_pdb_ins_code = string_array.remap(
1167
+ poly_seq_num, mapping=label_seq_id_to_ins_code, default_value='.'
1168
+ )
1169
+
1170
+ # The _entity_poly_seq table is entity-based, while _pdbx_poly_seq_scheme
1171
+ # is chain-based. A single entity could mean multiple chains (asym_ids),
1172
+ # we therefore need to replicate each entity for all of the chains.
1173
+ scheme_asym_id = []
1174
+ select = []
1175
+ indices = np.arange(len(poly_seq_entity_id), dtype=np.int32)
1176
+ for asym_id, entity_id in zip(
1177
+ cif['_struct_asym.id'], cif['_struct_asym.entity_id'], strict=True
1178
+ ):
1179
+ entity_mask = poly_seq_entity_id == entity_id
1180
+ select.extend(indices[entity_mask])
1181
+ scheme_asym_id.extend([asym_id] * sum(entity_mask))
1182
+
1183
+ scheme_pdb_strand_id = string_array.remap(
1184
+ np.array(scheme_asym_id, dtype=object),
1185
+ mapping=mmcif.get_internal_to_author_chain_id_map(cif),
1186
+ inplace=False,
1187
+ )
1188
+
1189
+ update['_pdbx_poly_seq_scheme.asym_id'] = scheme_asym_id
1190
+ update['_pdbx_poly_seq_scheme.pdb_strand_id'] = scheme_pdb_strand_id
1191
+ update['_pdbx_poly_seq_scheme.pdb_seq_num'] = scheme_pdb_seq_num[select]
1192
+ update['_pdbx_poly_seq_scheme.pdb_ins_code'] = scheme_pdb_ins_code[select]
1193
+ update['_pdbx_poly_seq_scheme.seq_id'] = poly_seq_num[select]
1194
+ update['_pdbx_poly_seq_scheme.mon_id'] = poly_seq_mon_id[select]
1195
+ else:
1196
+ # _entity_poly_seq not available, fallback to _atom_site.
1197
+ res_asym_ids = label_asym_ids[res_mask]
1198
+ res_strand_ids = string_array.remap(
1199
+ array=res_asym_ids,
1200
+ mapping=mmcif.get_internal_to_author_chain_id_map(cif),
1201
+ inplace=False,
1202
+ )
1203
+ update['_pdbx_poly_seq_scheme.asym_id'] = res_asym_ids
1204
+ update['_pdbx_poly_seq_scheme.pdb_strand_id'] = res_strand_ids
1205
+ update['_pdbx_poly_seq_scheme.pdb_seq_num'] = auth_seq_ids[res_mask]
1206
+ update['_pdbx_poly_seq_scheme.pdb_ins_code'] = pdb_ins_codes[res_mask]
1207
+ update['_pdbx_poly_seq_scheme.seq_id'] = label_seq_ids[res_mask]
1208
+ update['_pdbx_poly_seq_scheme.mon_id'] = label_comp_ids[res_mask]
1209
+
1210
+ required_nonpoly_scheme_cols = (
1211
+ '_pdbx_nonpoly_scheme.mon_id',
1212
+ '_pdbx_nonpoly_scheme.asym_id',
1213
+ '_pdbx_nonpoly_scheme.pdb_seq_num',
1214
+ '_pdbx_nonpoly_scheme.pdb_ins_code',
1215
+ )
1216
+ required_branch_scheme_cols = (
1217
+ '_pdbx_branch_scheme.mon_id',
1218
+ '_pdbx_branch_scheme.asym_id',
1219
+ '_pdbx_branch_scheme.pdb_seq_num',
1220
+ )
1221
+
1222
+ # Generate _pdbx_nonpoly_scheme only if both tables are missing.
1223
+ if not (
1224
+ all(col in cif for col in required_nonpoly_scheme_cols)
1225
+ or all(col in cif for col in required_branch_scheme_cols)
1226
+ ):
1227
+ # To be strictly semantically correct, multi-residue ligands should be
1228
+ # written in _pdbx_branch_scheme. However, Structure parsing handles
1229
+ # correctly multi-residue ligands in _pdbx_nonpoly_scheme and the tables
1230
+ # constructed here live only while parsing, hence this is unnecessary.
1231
+ entity_id_by_chain_id = dict(
1232
+ zip(cif['_struct_asym.id'], cif['_struct_asym.entity_id'], strict=True)
1233
+ )
1234
+ chain_type_by_entity_id = dict(
1235
+ zip(cif['_entity.id'], cif['_entity.type'], strict=True)
1236
+ )
1237
+ # Remap asym ID -> entity ID.
1238
+ chain_type = string_array.remap(
1239
+ label_asym_ids, mapping=entity_id_by_chain_id, inplace=False
1240
+ )
1241
+ # Remap entity ID -> chain type.
1242
+ string_array.remap(
1243
+ chain_type, mapping=chain_type_by_entity_id, inplace=True
1244
+ )
1245
+ res_mask = np.zeros_like(label_seq_ids, dtype=bool)
1246
+ res_mask[res_starts] = True
1247
+ res_mask &= chain_type != mmcif_names.POLYMER_CHAIN
1248
+
1249
+ if not np.any(res_mask):
1250
+ return update # Shortcut: no non-polymer residues.
1251
+
1252
+ ins_codes = string_array.remap(
1253
+ pdb_ins_codes[res_mask], mapping={'?': '.'}, inplace=False
1254
+ )
1255
+
1256
+ update['_pdbx_nonpoly_scheme.asym_id'] = label_asym_ids[res_mask]
1257
+ update['_pdbx_nonpoly_scheme.pdb_seq_num'] = auth_seq_ids[res_mask]
1258
+ update['_pdbx_nonpoly_scheme.pdb_ins_code'] = ins_codes
1259
+ update['_pdbx_nonpoly_scheme.mon_id'] = label_comp_ids[res_mask]
1260
+
1261
+ return update
1262
+
1263
+
1264
+ def _get_chain_key_by_chain_id(
1265
+ resolved_chain_ids: np.ndarray, struct_asym_chain_ids: np.ndarray
1266
+ ) -> Mapping[str, int]:
1267
+ """Returns chain key for each chain ID respecting resolved chain ordering."""
1268
+ # Check that all chain IDs found in the (potentially filtered) _atom_site
1269
+ # table are present in the _struct_asym table.
1270
+ unique_resolved_chain_ids = set(resolved_chain_ids)
1271
+ if not unique_resolved_chain_ids.issubset(set(struct_asym_chain_ids)):
1272
+ unique_resolved_chain_ids = sorted(unique_resolved_chain_ids)
1273
+ unique_struct_asym_chain_ids = sorted(set(struct_asym_chain_ids))
1274
+ raise ValueError(
1275
+ 'Bad mmCIF: chain IDs in _atom_site.label_asym_id '
1276
+ f'{unique_resolved_chain_ids} is not a subset of chain IDs in '
1277
+ f'_struct_asym.id {unique_struct_asym_chain_ids}.'
1278
+ )
1279
+
1280
+ resolved_mask = string_array.isin(
1281
+ struct_asym_chain_ids, unique_resolved_chain_ids
1282
+ )
1283
+ # For all resolved chains, use the _atom_site order they appear in. E.g.
1284
+ # resolved_chain_ids = [B A E D F]
1285
+ # struct_asym_chain_ids = [A B C D E F]
1286
+ # consistent_chain_order = [B A C E D F]
1287
+ # chain_keys = [0 1 2 3 4 5]
1288
+ consistent_chain_order = struct_asym_chain_ids.copy()
1289
+ consistent_chain_order[resolved_mask] = resolved_chain_ids
1290
+ return dict(zip(consistent_chain_order, range(len(struct_asym_chain_ids))))
1291
+
1292
+
1293
+ def get_tables(
1294
+ cif: mmcif.Mmcif,
1295
+ fix_mse_residues: bool,
1296
+ fix_arginines: bool,
1297
+ fix_unknown_dna: bool,
1298
+ include_water: bool,
1299
+ include_other: bool,
1300
+ model_id: str,
1301
+ ) -> tuple[
1302
+ structure_tables.Chains, structure_tables.Residues, structure_tables.Atoms
1303
+ ]:
1304
+ """Returns chain, residue, and atom tables from a parsed mmcif.
1305
+
1306
+ Args:
1307
+ cif: A parsed mmcif.Mmcif.
1308
+ fix_mse_residues: See from_mmcif.
1309
+ fix_arginines: See from_mmcif.
1310
+ fix_unknown_dna: See from_mmcif.
1311
+ include_water: See from_mmcif.
1312
+ include_other: See from_mmcif.
1313
+ model_id: A string defining which model ID to use. If set, only coordinates,
1314
+ b-factors and occupancies for the given model are returned. If empty,
1315
+ coordinates, b-factors and occupanciesall for models are returned with a
1316
+ leading dimension of num_models. Note that the model_id argument in
1317
+ from_mmcif is an integer and has slightly different use (see from_mmcif).
1318
+ """
1319
+ # Add any missing tables and columns we require for parsing.
1320
+ if cif_update := _generate_required_tables_if_missing(cif):
1321
+ cif = cif.copy_and_update(cif_update)
1322
+
1323
+ # Resolve alt-locs, selecting only a single option for each residue. Also
1324
+ # computes the layout, which defines where chain and residue boundaries are.
1325
+ atom_site_all_models, layout = mmcif_utils.filter(
1326
+ cif,
1327
+ include_nucleotides=True,
1328
+ include_ligands=True,
1329
+ include_water=include_water,
1330
+ include_other=include_other,
1331
+ model_id=model_id,
1332
+ )
1333
+ atom_site_first_model = atom_site_all_models[0]
1334
+
1335
+ # Get atom information from the _atom_site table.
1336
+ def _first_model_string_array(col: str) -> np.ndarray:
1337
+ return cif.get_array(col, dtype=object, gather=atom_site_first_model)
1338
+
1339
+ def _requested_models_float_array(col: str) -> np.ndarray:
1340
+ if not model_id:
1341
+ # Return data for all models with a leading dimension of num_models.
1342
+ return cif.get_array(col, dtype=np.float32, gather=atom_site_all_models)
1343
+ else:
1344
+ # Return data only for the single requested model.
1345
+ return cif.get_array(col, dtype=np.float32, gather=atom_site_first_model)
1346
+
1347
+ # These columns are the same for all models, fetch them just for the 1st one.
1348
+ label_comp_ids = _first_model_string_array('_atom_site.label_comp_id')
1349
+ label_asym_ids = _first_model_string_array('_atom_site.label_asym_id')
1350
+ label_seq_ids = _first_model_string_array('_atom_site.label_seq_id')
1351
+ label_atom_ids = _first_model_string_array('_atom_site.label_atom_id')
1352
+ if '_atom_site.auth_seq_id' in cif:
1353
+ auth_seq_ids = _first_model_string_array('_atom_site.auth_seq_id')
1354
+ else:
1355
+ auth_seq_ids = label_seq_ids # auth_seq_id unset, fallback to label_seq_id.
1356
+ type_symbols = _first_model_string_array('_atom_site.type_symbol')
1357
+ pdbx_pdb_ins_codes = _first_model_string_array('_atom_site.pdbx_PDB_ins_code')
1358
+
1359
+ # These columns are different for all models, fetch them as requested.
1360
+ atom_x = _requested_models_float_array('_atom_site.Cartn_x')
1361
+ atom_y = _requested_models_float_array('_atom_site.Cartn_y')
1362
+ atom_z = _requested_models_float_array('_atom_site.Cartn_z')
1363
+ atom_b_factor = _requested_models_float_array('_atom_site.B_iso_or_equiv')
1364
+ atom_occupancy = _requested_models_float_array('_atom_site.occupancy')
1365
+
1366
+ # Make sure the scheme (residue) tables exist in case they are not present.
1367
+ if cif_update := _maybe_add_missing_scheme_tables(
1368
+ cif,
1369
+ res_starts=layout.residue_starts(),
1370
+ label_asym_ids=label_asym_ids,
1371
+ label_seq_ids=label_seq_ids,
1372
+ label_comp_ids=label_comp_ids,
1373
+ auth_seq_ids=auth_seq_ids,
1374
+ pdb_ins_codes=pdbx_pdb_ins_codes,
1375
+ ):
1376
+ cif = cif.copy_and_update(cif_update)
1377
+
1378
+ # Fix common issues found in mmCIF files, like swapped arginine NH atoms.
1379
+ mmcif_utils.fix_residues(
1380
+ layout,
1381
+ comp_id=label_comp_ids,
1382
+ atom_id=label_atom_ids,
1383
+ atom_x=atom_x[0] if not model_id else atom_x,
1384
+ atom_y=atom_y[0] if not model_id else atom_y,
1385
+ atom_z=atom_z[0] if not model_id else atom_z,
1386
+ fix_arg=fix_arginines,
1387
+ )
1388
+
1389
+ # Get keys for chains in the order they appear in _atom_site while also
1390
+ # dealing with empty chains.
1391
+ resolved_chain_ids = label_asym_ids[layout.chain_starts()]
1392
+ struct_asym_chain_ids = cif.get_array('_struct_asym.id', dtype=object)
1393
+
1394
+ chain_key_by_chain_id = _get_chain_key_by_chain_id(
1395
+ resolved_chain_ids=resolved_chain_ids,
1396
+ struct_asym_chain_ids=struct_asym_chain_ids,
1397
+ )
1398
+ entity_id_by_chain_id = dict(
1399
+ zip(struct_asym_chain_ids, cif['_struct_asym.entity_id'], strict=True)
1400
+ )
1401
+ entity_description = cif.get(
1402
+ '_entity.pdbx_description', ['?'] * len(cif['_entity.id'])
1403
+ )
1404
+ entity_desc_by_entity_id = dict(
1405
+ zip(cif['_entity.id'], entity_description, strict=True)
1406
+ )
1407
+ chain_type_by_entity_id = mmcif.get_chain_type_by_entity_id(cif)
1408
+ auth_asym_id_by_chain_id = mmcif.get_internal_to_author_chain_id_map(cif)
1409
+
1410
+ chain_res_builder = _ChainResBuilder(
1411
+ chain_key_by_chain_id=chain_key_by_chain_id,
1412
+ entity_id_by_chain_id=entity_id_by_chain_id,
1413
+ chain_type_by_entity_id=chain_type_by_entity_id,
1414
+ entity_desc_by_entity_id=entity_desc_by_entity_id,
1415
+ fix_mse_residues=fix_mse_residues,
1416
+ fix_unknown_dna=fix_unknown_dna,
1417
+ )
1418
+
1419
+ # Collect data for polymer chain and residue tables. _pdbx_poly_seq_scheme is
1420
+ # guaranteed to be present thanks to _maybe_add_missing_scheme_tables.
1421
+ def _get_poly_seq_scheme_col(col: str) -> np.ndarray:
1422
+ return cif.get_array(key=f'_pdbx_poly_seq_scheme.{col}', dtype=object)
1423
+
1424
+ poly_seq_asym_ids = _get_poly_seq_scheme_col('asym_id')
1425
+ poly_seq_pdb_seq_nums = _get_poly_seq_scheme_col('pdb_seq_num')
1426
+ poly_seq_seq_ids = _get_poly_seq_scheme_col('seq_id')
1427
+ poly_seq_mon_ids = _get_poly_seq_scheme_col('mon_id')
1428
+ poly_seq_pdb_strand_ids = _get_poly_seq_scheme_col('pdb_strand_id')
1429
+ poly_seq_pdb_ins_codes = _get_poly_seq_scheme_col('pdb_ins_code')
1430
+ string_array.remap(
1431
+ poly_seq_pdb_ins_codes, mapping=_INSERTION_CODE_REMAP, inplace=True
1432
+ )
1433
+
1434
+ # We resolved alt-locs earlier for the atoms table. In cases of heterogeneous
1435
+ # residues (a residue with an alt-loc that is of different residue type), we
1436
+ # need to also do the same resolution in the residues table. Compute a mask
1437
+ # for the residues that were selected in the atoms table.
1438
+ poly_seq_mask = mmcif_utils.selected_polymer_residue_mask(
1439
+ layout=layout,
1440
+ atom_site_label_asym_ids=label_asym_ids[layout.residue_starts()],
1441
+ atom_site_label_seq_ids=label_seq_ids[layout.residue_starts()],
1442
+ atom_site_label_comp_ids=label_comp_ids[layout.residue_starts()],
1443
+ poly_seq_asym_ids=poly_seq_asym_ids,
1444
+ poly_seq_seq_ids=poly_seq_seq_ids,
1445
+ poly_seq_mon_ids=poly_seq_mon_ids,
1446
+ )
1447
+
1448
+ if not include_other and poly_seq_mask:
1449
+ # Mask filtered-out residues so that they are not treated as missing.
1450
+ # Instead, we don't want them included in the chains/residues tables at all.
1451
+ keep_mask = string_array.remap(
1452
+ poly_seq_asym_ids,
1453
+ mapping={cid: True for cid in resolved_chain_ids},
1454
+ default_value=False,
1455
+ inplace=False,
1456
+ ).astype(bool)
1457
+ poly_seq_mask &= keep_mask
1458
+
1459
+ chain_res_builder.add_residues(
1460
+ chain_ids=poly_seq_asym_ids[poly_seq_mask],
1461
+ chain_auth_asym_ids=poly_seq_pdb_strand_ids[poly_seq_mask],
1462
+ res_ids=poly_seq_seq_ids[poly_seq_mask].astype(np.int32),
1463
+ res_names=poly_seq_mon_ids[poly_seq_mask],
1464
+ res_auth_seq_ids=poly_seq_pdb_seq_nums[poly_seq_mask],
1465
+ res_ins_codes=poly_seq_pdb_ins_codes[poly_seq_mask],
1466
+ )
1467
+
1468
+ # Collect data for ligand chain and residue tables. _pdbx_nonpoly_scheme
1469
+ # could be empty/unset if there are only branched ligands.
1470
+ def _get_nonpoly_scheme_col(col: str) -> np.ndarray:
1471
+ key = f'_pdbx_nonpoly_scheme.{col}'
1472
+ if f'_pdbx_nonpoly_scheme.{col}' in cif:
1473
+ return cif.get_array(key=key, dtype=object)
1474
+ else:
1475
+ return np.array([], dtype=object)
1476
+
1477
+ nonpoly_asym_ids = _get_nonpoly_scheme_col('asym_id')
1478
+ nonpoly_auth_seq_ids = _get_nonpoly_scheme_col('pdb_seq_num')
1479
+ nonpoly_pdb_ins_codes = _get_nonpoly_scheme_col('pdb_ins_code')
1480
+ nonpoly_mon_ids = _get_nonpoly_scheme_col('mon_id')
1481
+ nonpoly_auth_asym_id = string_array.remap(
1482
+ nonpoly_asym_ids, mapping=auth_asym_id_by_chain_id, inplace=False
1483
+ )
1484
+
1485
+ def _get_branch_scheme_col(col: str) -> np.ndarray:
1486
+ key = f'_pdbx_branch_scheme.{col}'
1487
+ if f'_pdbx_branch_scheme.{col}' in cif:
1488
+ return cif.get_array(key=key, dtype=object)
1489
+ else:
1490
+ return np.array([], dtype=object)
1491
+
1492
+ branch_asym_ids = _get_branch_scheme_col('asym_id')
1493
+ branch_auth_seq_ids = _get_branch_scheme_col('pdb_seq_num')
1494
+ branch_pdb_ins_codes = _get_branch_scheme_col('pdb_ins_code')
1495
+ branch_mon_ids = _get_branch_scheme_col('mon_id')
1496
+ branch_auth_asym_id = string_array.remap(
1497
+ branch_asym_ids, mapping=auth_asym_id_by_chain_id, inplace=False
1498
+ )
1499
+
1500
+ if branch_asym_ids.size > 0 and branch_pdb_ins_codes.size == 0:
1501
+ branch_pdb_ins_codes = np.array(['.'] * branch_asym_ids.size, dtype=object)
1502
+
1503
+ # Compute the heterogeneous residue masks as above, this time for ligands.
1504
+ nonpoly_mask, branch_mask = mmcif_utils.selected_ligand_residue_mask(
1505
+ layout=layout,
1506
+ atom_site_label_asym_ids=label_asym_ids[layout.residue_starts()],
1507
+ atom_site_label_seq_ids=label_seq_ids[layout.residue_starts()],
1508
+ atom_site_auth_seq_ids=auth_seq_ids[layout.residue_starts()],
1509
+ atom_site_label_comp_ids=label_comp_ids[layout.residue_starts()],
1510
+ atom_site_pdbx_pdb_ins_codes=pdbx_pdb_ins_codes[layout.residue_starts()],
1511
+ nonpoly_asym_ids=nonpoly_asym_ids,
1512
+ nonpoly_auth_seq_ids=nonpoly_auth_seq_ids,
1513
+ nonpoly_pdb_ins_codes=nonpoly_pdb_ins_codes,
1514
+ nonpoly_mon_ids=nonpoly_mon_ids,
1515
+ branch_asym_ids=branch_asym_ids,
1516
+ branch_auth_seq_ids=branch_auth_seq_ids,
1517
+ branch_pdb_ins_codes=branch_pdb_ins_codes,
1518
+ branch_mon_ids=branch_mon_ids,
1519
+ )
1520
+
1521
+ if not include_water:
1522
+ if nonpoly_mask:
1523
+ nonpoly_mask &= (nonpoly_mon_ids != 'HOH') & (nonpoly_mon_ids != 'DOD')
1524
+ if branch_mask:
1525
+ # Fix for bad mmCIFs that have water in the branch scheme table.
1526
+ branch_mask &= (branch_mon_ids != 'HOH') & (branch_mon_ids != 'DOD')
1527
+
1528
+ string_array.remap(
1529
+ pdbx_pdb_ins_codes, mapping=_INSERTION_CODE_REMAP, inplace=True
1530
+ )
1531
+ string_array.remap(
1532
+ nonpoly_pdb_ins_codes, mapping=_INSERTION_CODE_REMAP, inplace=True
1533
+ )
1534
+ string_array.remap(
1535
+ branch_pdb_ins_codes, mapping=_INSERTION_CODE_REMAP, inplace=True
1536
+ )
1537
+
1538
+ def _ligand_residue_ids(chain_ids: np.ndarray) -> np.ndarray:
1539
+ """Computes internal residue ID for ligand residues that don't have it."""
1540
+
1541
+ # E.g. chain_ids=[A, A, A, B, C, C, D, D, D] -> [1, 2, 3, 1, 1, 2, 1, 2, 3].
1542
+ indices = np.arange(chain_ids.size, dtype=np.int32)
1543
+ return (indices + 1) - np.maximum.accumulate(
1544
+ indices * (chain_ids != np.roll(chain_ids, 1))
1545
+ )
1546
+
1547
+ branch_residue_ids = _ligand_residue_ids(branch_asym_ids[branch_mask])
1548
+ nonpoly_residue_ids = _ligand_residue_ids(nonpoly_asym_ids[nonpoly_mask])
1549
+
1550
+ chain_res_builder.add_residues(
1551
+ chain_ids=branch_asym_ids[branch_mask],
1552
+ chain_auth_asym_ids=branch_auth_asym_id[branch_mask],
1553
+ res_ids=branch_residue_ids,
1554
+ res_names=branch_mon_ids[branch_mask],
1555
+ res_auth_seq_ids=branch_auth_seq_ids[branch_mask],
1556
+ res_ins_codes=branch_pdb_ins_codes[branch_mask],
1557
+ )
1558
+
1559
+ chain_res_builder.add_residues(
1560
+ chain_ids=nonpoly_asym_ids[nonpoly_mask],
1561
+ chain_auth_asym_ids=nonpoly_auth_asym_id[nonpoly_mask],
1562
+ res_ids=nonpoly_residue_ids,
1563
+ res_names=nonpoly_mon_ids[nonpoly_mask],
1564
+ res_auth_seq_ids=nonpoly_auth_seq_ids[nonpoly_mask],
1565
+ res_ins_codes=nonpoly_pdb_ins_codes[nonpoly_mask],
1566
+ )
1567
+
1568
+ chains = chain_res_builder.make_chains_table()
1569
+ residues = chain_res_builder.make_residues_table()
1570
+
1571
+ # Construct foreign residue keys for the atoms table.
1572
+ res_ends = np.array(layout.residues(), dtype=np.int32)
1573
+ res_starts = np.array(layout.residue_starts(), dtype=np.int32)
1574
+ res_lengths = res_ends - res_starts
1575
+
1576
+ # Check just for HOH, DOD can be part e.g. of hydroxycysteine.
1577
+ if include_water:
1578
+ res_chain_types = chains.apply_array_to_column(
1579
+ column_name='type', arr=residues.chain_key
1580
+ )
1581
+ water_mask = res_chain_types != mmcif_names.WATER
1582
+ if 'HOH' in set(residues.name[water_mask]):
1583
+ raise ValueError('Bad mmCIF file: non-water entity has water molecules.')
1584
+ else:
1585
+ # Include resolved and unresolved residues.
1586
+ if 'HOH' in set(residues.name) | set(label_comp_ids[res_starts]):
1587
+ raise ValueError('Bad mmCIF file: non-water entity has water molecules.')
1588
+
1589
+ atom_chain_key = string_array.remap(
1590
+ label_asym_ids, mapping=chain_res_builder.chain_key_by_chain_id
1591
+ ).astype(int)
1592
+
1593
+ # If any of the residue lookups failed, the mmCIF is corrupted.
1594
+ try:
1595
+ atom_res_key_per_res = string_array.remap_multiple(
1596
+ (
1597
+ label_asym_ids[res_starts],
1598
+ auth_seq_ids[res_starts],
1599
+ label_comp_ids[res_starts],
1600
+ pdbx_pdb_ins_codes[res_starts],
1601
+ ),
1602
+ mapping=chain_res_builder.key_for_res,
1603
+ )
1604
+ except KeyError as e:
1605
+ raise ValueError(
1606
+ 'Lookup for the following atom from the _atom_site table failed: '
1607
+ f'(label_asym_id, auth_seq_id, res_name, ins_code)={e}. This typically '
1608
+ 'indicates that the _pdbx_poly_seq_scheme, _pdbx_nonpoly_scheme, or '
1609
+ '_pdbx_branch_scheme tables do not have data for all residues present '
1610
+ 'in the _atom_site table. It could also be due to a known issue with '
1611
+ 'a small number of multi-model mmCIFs.'
1612
+ ) from e
1613
+
1614
+ # The residue ID will be shared for all atoms within that residue.
1615
+ atom_res_key = np.repeat(atom_res_key_per_res, repeats=res_lengths)
1616
+
1617
+ if fix_mse_residues:
1618
+ met_residues_mask = (residues.name == 'MET')[atom_res_key]
1619
+ unfixed_mse_selenium_mask = met_residues_mask & (label_atom_ids == 'SE')
1620
+ label_atom_ids[unfixed_mse_selenium_mask] = 'SD'
1621
+ type_symbols[unfixed_mse_selenium_mask] = 'S'
1622
+
1623
+ atoms = structure_tables.Atoms(
1624
+ key=atom_site_first_model,
1625
+ chain_key=atom_chain_key,
1626
+ res_key=atom_res_key,
1627
+ name=label_atom_ids,
1628
+ element=type_symbols,
1629
+ x=atom_x,
1630
+ y=atom_y,
1631
+ z=atom_z,
1632
+ b_factor=atom_b_factor,
1633
+ occupancy=atom_occupancy,
1634
+ )
1635
+
1636
+ return chains, residues, atoms
1637
+
1638
+
1639
+ def from_atom_arrays(
1640
+ *,
1641
+ res_id: np.ndarray,
1642
+ name: str = 'unset',
1643
+ release_date: datetime.date | None = None,
1644
+ resolution: float | None = None,
1645
+ structure_method: str | None = None,
1646
+ all_residues: Mapping[str, Sequence[tuple[str, int]]] | None = None,
1647
+ bioassembly_data: bioassemblies.BioassemblyData | None = None,
1648
+ chemical_components_data: (
1649
+ struc_chem_comps.ChemicalComponentsData | None
1650
+ ) = None,
1651
+ bond_table: structure_tables.Bonds | None = None,
1652
+ chain_id: np.ndarray | None = None,
1653
+ chain_type: np.ndarray | None = None,
1654
+ res_name: np.ndarray | None = None,
1655
+ atom_key: np.ndarray | None = None,
1656
+ atom_name: np.ndarray | None = None,
1657
+ atom_element: np.ndarray | None = None,
1658
+ atom_x: np.ndarray | None = None,
1659
+ atom_y: np.ndarray | None = None,
1660
+ atom_z: np.ndarray | None = None,
1661
+ atom_b_factor: np.ndarray | None = None,
1662
+ atom_occupancy: np.ndarray | None = None,
1663
+ ) -> structure.Structure:
1664
+ """Returns a Structure constructed from atom array level data.
1665
+
1666
+ All fields except name and, res_id are optional, all array fields consist of a
1667
+ value for each atom in the structure - so residue and chain values should hold
1668
+ the same value for each atom in the chain or residue. Fields which are not
1669
+ defined are filled with default values.
1670
+
1671
+ Validation is performed by the Structure constructor where possible - but
1672
+ author_naming scheme and all_residues must be checked in this function.
1673
+
1674
+ It is not possible to construct structures with chains that do not contain
1675
+ any resolved residues using this function. If this is necessary, use the
1676
+ structure.Structure constructor directly.
1677
+
1678
+ Args:
1679
+ res_id: Integer array of shape [num_atom]. The unique residue identifier for
1680
+ each residue. mmCIF field - _atom_site.label_seq_id.
1681
+ name: The name of the structure. E.g. a PDB ID.
1682
+ release_date: The release date of the structure as a `datetime.date`.
1683
+ resolution: The resolution of the structure in Angstroms.
1684
+ structure_method: The method used to solve this structure's coordinates.
1685
+ all_residues: An optional mapping from each chain ID (i.e. label_asym_id) to
1686
+ a sequence of (label_comp_id, label_seq_id) tuples, one per residue. This
1687
+ can contain residues that aren't present in the atom arrays. This is
1688
+ common in experimental data where some residues are not resolved but are
1689
+ known to be present.
1690
+ bioassembly_data: An optional instance of bioassembly.BioassemblyData. If
1691
+ present then a new Structure representing a specific bioassembly can be
1692
+ extracted using `Structure.generate_bioassembly(assembly_id)`.
1693
+ chemical_components_data: An optional instance of ChemicalComponentsData.
1694
+ Its content will be used for providing metadata about chemical components
1695
+ in this Structure instance. If not specified information will be retrieved
1696
+ from the standard chemical component dictionary (CCD, for more details see
1697
+ https://www.wwpdb.org/data/ccd).
1698
+ bond_table: A table representing manually-specified bonds. This corresponds
1699
+ to the _struct_conn table in an mmCIF. Atoms are identified by their key,
1700
+ as specified by the atom_key column. If this table is provided then the
1701
+ atom_key column must also be defined.
1702
+ chain_id: String array of shape [num_atom] of unique chain identifiers.
1703
+ mmCIF field - _atom_site.label_asym_id.
1704
+ chain_type: String array of shape [num_atom]. The molecular type of the
1705
+ current chain (e.g. polyribonucleotide). mmCIF field - _entity_poly.type
1706
+ OR _entity.type (for non-polymers).
1707
+ res_name: String array of shape [num_atom].. The name of each residue,
1708
+ typically a 3 letter string for polypeptides or 1-2 letter strings for
1709
+ polynucleotides. mmCIF field - _atom_site.label_comp_id.
1710
+ atom_key: A unique sorted integer array, used only by the bonds table to
1711
+ identify the atoms participating in each bond. If the bonds table is
1712
+ specified then this column must be non-None.
1713
+ atom_name: String array of shape [num_atom]. The name of each atom (e.g CA,
1714
+ O2', etc.). mmCIF field - _atom_site.label_atom_id.
1715
+ atom_element: String array of shape [num_atom]. The element type of each
1716
+ atom (e.g. C, O, N, etc.). mmCIF field - _atom_site.type_symbol.
1717
+ atom_x: Float array of shape [..., num_atom] of atom x coordinates. May have
1718
+ arbitrary leading dimensions, provided that these are consistent across
1719
+ all coordinate fields.
1720
+ atom_y: Float array of shape [..., num_atom] of atom y coordinates. May have
1721
+ arbitrary leading dimensions, provided that these are consistent across
1722
+ all coordinate fields.
1723
+ atom_z: Float array of shape [..., num_atom] of atom z coordinates. May have
1724
+ arbitrary leading dimensions, provided that these are consistent across
1725
+ all coordinate fields.
1726
+ atom_b_factor: Float array of shape [..., num_atom] or [num_atom] of atom
1727
+ b-factors or equivalent. If there are no extra leading dimensions then
1728
+ these values are assumed to apply to all coordinates for a given atom. If
1729
+ there are leading dimensions then these must match those used by the
1730
+ coordinate fields.
1731
+ atom_occupancy: Float array of shape [..., num_atom] or [num_atom] of atom
1732
+ occupancies or equivalent. If there are no extra leading dimensions then
1733
+ these values are assumed to apply to all coordinates for a given atom. If
1734
+ there are leading dimensions then these must match those used by the
1735
+ coordinate fields.
1736
+ """
1737
+
1738
+ atoms, residues, chains = structure_tables.tables_from_atom_arrays(
1739
+ res_id=res_id,
1740
+ all_residues=all_residues,
1741
+ chain_id=chain_id,
1742
+ chain_type=chain_type,
1743
+ res_name=res_name,
1744
+ atom_key=atom_key,
1745
+ atom_name=atom_name,
1746
+ atom_element=atom_element,
1747
+ atom_x=atom_x,
1748
+ atom_y=atom_y,
1749
+ atom_z=atom_z,
1750
+ atom_b_factor=atom_b_factor,
1751
+ atom_occupancy=atom_occupancy,
1752
+ )
1753
+
1754
+ return structure.Structure(
1755
+ name=name,
1756
+ release_date=release_date,
1757
+ resolution=resolution,
1758
+ structure_method=structure_method,
1759
+ bioassembly_data=bioassembly_data,
1760
+ chemical_components_data=chemical_components_data,
1761
+ atoms=atoms,
1762
+ chains=chains,
1763
+ residues=residues,
1764
+ bonds=bond_table or structure_tables.Bonds.make_empty(),
1765
+ )
1766
+
1767
+
1768
+ def _guess_entity_type(
1769
+ chain_residues: Collection[str], atom_types: Collection[str]
1770
+ ) -> str:
1771
+ """Guess the entity type (polymer/non-polymer/water) based on residues/atoms.
1772
+
1773
+ We treat both arguments as unordered collections since we care only whether
1774
+ all elements satisfy come conditions. The chain_residues can be either
1775
+ grouped by residue (length num_res), or it can be raw (length num_atoms).
1776
+ Atom type is unique for each atom in a residue, so don't group atom_types.
1777
+
1778
+ Args:
1779
+ chain_residues: A sequence of full residue name (1-letter for DNA, 2-letters
1780
+ for RNA, 3 for protein). The _atom_site.label_comp_id column in mmCIF.
1781
+ atom_types: Atom type: ATOM or HETATM. The _atom_site.group_PDB column in
1782
+ mmCIF.
1783
+
1784
+ Returns:
1785
+ One of polymer/non-polymer/water based on the following criteria:
1786
+ * If all atoms are HETATMs and all residues are water -> water.
1787
+ * If all atoms are HETATMs and not all residues are water -> non-polymer.
1788
+ * Otherwise -> polymer.
1789
+ """
1790
+ if not chain_residues or not atom_types:
1791
+ raise ValueError(
1792
+ f'chain_residues (len {len(chain_residues)}) and atom_types (len '
1793
+ f'{len(atom_types)}) must be both non-empty. Got: {chain_residues=} '
1794
+ f'and {atom_types=}'
1795
+ )
1796
+
1797
+ if all(a == 'HETATM' for a in atom_types):
1798
+ if all(c in residue_names.WATER_TYPES for c in chain_residues):
1799
+ return mmcif_names.WATER
1800
+ return mmcif_names.NON_POLYMER_CHAIN
1801
+ return mmcif_names.POLYMER_CHAIN
flax_model/alphafold3/structure/sterics.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ """Functions relating to spatial locations of atoms within a structure."""
4
+
5
+ from collections.abc import Collection, Sequence
6
+
7
+ from flax_model.alphafold3 import structure
8
+ from flax_model.alphafold3.structure import mmcif
9
+ import numpy as np
10
+ import scipy
11
+
12
+
13
+ def _make_atom_has_clash_mask(
14
+ kd_query_result: np.ndarray,
15
+ struc: structure.Structure,
16
+ ignore_chains: Collection[str],
17
+ ) -> np.ndarray:
18
+ """Returns a boolean NumPy array representing whether each atom has a clash.
19
+
20
+ Args:
21
+ kd_query_result: NumPy array containing N-atoms arrays, each array
22
+ containing indices to atoms that clash with the N'th atom.
23
+ struc: Structure over which clashes were detected.
24
+ ignore_chains: Collection of chains that should not be considered clashing.
25
+ A boolean NumPy array of length N atoms.
26
+ """
27
+ atom_is_clashing = np.zeros((struc.num_atoms,), dtype=bool)
28
+ for atom_index, clashes in enumerate(kd_query_result):
29
+ chain_i = struc.chain_id[atom_index]
30
+ if chain_i in ignore_chains:
31
+ continue
32
+ islig_i = struc.is_ligand_mask[atom_index]
33
+ for clashing_atom_index in clashes:
34
+ chain_c = struc.chain_id[clashing_atom_index]
35
+ if chain_c in ignore_chains:
36
+ continue
37
+ islig_c = struc.is_ligand_mask[clashing_atom_index]
38
+ if (
39
+ clashing_atom_index == atom_index
40
+ or chain_i == chain_c
41
+ or islig_i != islig_c
42
+ ):
43
+ # Ignore clashes within chain or between ligand and polymer.
44
+ continue
45
+ atom_is_clashing[atom_index] = True
46
+ return atom_is_clashing
47
+
48
+
49
+ def find_clashing_chains(
50
+ struc: structure.Structure,
51
+ clash_thresh_angstrom: float = 1.7,
52
+ clash_thresh_fraction: float = 0.3,
53
+ ) -> Sequence[str]:
54
+ """Finds chains that clash with others.
55
+
56
+ Clashes are defined by polymer backbone atoms and all ligand atoms.
57
+ Ligand-polymer clashes are not dropped.
58
+
59
+ Will not find clashes if all coordinates are 0. Coordinates are all 0s if
60
+ the structure is generated from sequences only, as done for inference in
61
+ dendro for example.
62
+
63
+ Args:
64
+ struc: The structure defining the chains and atom positions.
65
+ clash_thresh_angstrom: Below this distance, atoms are considered clashing.
66
+ clash_thresh_fraction: Chains with more than this fraction of their atoms
67
+ considered clashing will be dropped. This value should be in the range (0,
68
+ 1].
69
+
70
+ Returns:
71
+ A sequence of chain ids for chains that clash.
72
+
73
+ Raises:
74
+ ValueError: If `clash_thresh_fraction` is not in range (0,1].
75
+ """
76
+ if not 0 < clash_thresh_fraction <= 1:
77
+ raise ValueError('clash_thresh_fraction must be in range (0,1]')
78
+
79
+ struc_backbone = struc.filter_polymers_to_single_atom_per_res()
80
+ if struc_backbone.num_chains == 0:
81
+ return []
82
+
83
+ # If the coordinates are all 0, do not search for clashes.
84
+ if not np.any(struc_backbone.coords):
85
+ return []
86
+
87
+ coord_kdtree = scipy.spatial.cKDTree(struc_backbone.coords)
88
+
89
+ # For each atom coordinate, find all atoms within the clash thresh radius.
90
+ clashing_per_atom = coord_kdtree.query_ball_point(
91
+ struc_backbone.coords, r=clash_thresh_angstrom
92
+ )
93
+ chain_ids = struc_backbone.chains
94
+ if struc_backbone.atom_occupancy is not None:
95
+ chain_occupancy = np.array([
96
+ np.mean(struc_backbone.atom_occupancy[start:end])
97
+ for start, end in struc_backbone.iter_chain_ranges()
98
+ ])
99
+ else:
100
+ chain_occupancy = None
101
+
102
+ # Remove chains until no more significant clashing.
103
+ chains_to_remove = set()
104
+ for _ in range(len(chain_ids)):
105
+ # Calculate maximally clashing.
106
+ atom_has_clash = _make_atom_has_clash_mask(
107
+ clashing_per_atom, struc_backbone, chains_to_remove
108
+ )
109
+ clashes_per_chain = np.array([
110
+ atom_has_clash[start:end].mean()
111
+ for start, end in struc_backbone.iter_chain_ranges()
112
+ ])
113
+ max_clash = np.max(clashes_per_chain)
114
+ if max_clash <= clash_thresh_fraction:
115
+ # None of the remaining chains exceed the clash fraction threshold, so
116
+ # we can exit.
117
+ break
118
+
119
+ # Greedily remove worst with the lowest occupancy.
120
+ most_clashes = np.nonzero(clashes_per_chain == max_clash)[0]
121
+ if chain_occupancy is not None:
122
+ occupancy_clashing = chain_occupancy[most_clashes]
123
+ last_lowest_occupancy = (
124
+ len(occupancy_clashing) - np.argmin(occupancy_clashing[::-1]) - 1
125
+ )
126
+ worst_and_last = most_clashes[last_lowest_occupancy]
127
+ else:
128
+ worst_and_last = most_clashes[-1]
129
+
130
+ chains_to_remove.add(chain_ids[worst_and_last])
131
+
132
+ return sorted(chains_to_remove, key=mmcif.str_id_to_int_id)
flax_model/alphafold3/structure/structure.py ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/structure/structure_tables.py ADDED
@@ -0,0 +1,811 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ """Table implementations for the Structure class."""
4
+
5
+ import collections
6
+ from collections.abc import Mapping, Sequence
7
+ import dataclasses
8
+ import functools
9
+ import itertools
10
+ import typing
11
+ from typing import Any, ClassVar, Self
12
+
13
+ from flax_model.alphafold3.constants import mmcif_names
14
+ from flax_model.alphafold3.constants import residue_names
15
+ from flax_model.alphafold3.cpp import aggregation
16
+ from flax_model.alphafold3.cpp import string_array
17
+ from flax_model.alphafold3.structure import bonds as bonds_module
18
+ from flax_model.alphafold3.structure import mmcif
19
+ from flax_model.alphafold3.structure import table
20
+ import numpy as np
21
+
22
+
23
+ Bonds = bonds_module.Bonds
24
+
25
+
26
+ def _residue_name_to_record_name(
27
+ residue_name: np.ndarray,
28
+ polymer_mask: np.ndarray,
29
+ ) -> np.ndarray:
30
+ """Returns record names (ATOM/HETATM) given residue names and polymer mask."""
31
+ record_name = np.array(['HETATM'] * len(residue_name), dtype=object)
32
+ record_name[polymer_mask] = string_array.remap(
33
+ residue_name[polymer_mask],
34
+ mapping={r: 'ATOM' for r in residue_names.STANDARD_POLYMER_TYPES},
35
+ default_value='HETATM',
36
+ )
37
+ return record_name
38
+
39
+
40
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
41
+ class AuthorNamingScheme:
42
+ """A mapping from internal values to author values in a mmCIF.
43
+
44
+ Fields:
45
+ auth_asym_id: A mapping from label_asym_id to auth_asym_id.
46
+ auth_seq_id: A mapping from label_asym_id to a mapping from
47
+ label_seq_id to auth_seq_id.
48
+ insertion_code: A mapping from label_asym_id to a mapping from
49
+ label_seq_id to insertion codes.
50
+ entity_id: A mapping from label_asym_id to _entity.id.
51
+ entity_desc: A mapping from _entity.id to _entity.pdbx_description.
52
+ """
53
+
54
+ auth_asym_id: Mapping[str, str]
55
+ auth_seq_id: Mapping[str, Mapping[int, str]]
56
+ insertion_code: Mapping[str, Mapping[int, str | None]]
57
+ entity_id: Mapping[str, str]
58
+ entity_desc: Mapping[str, str]
59
+
60
+
61
+ def _default(
62
+ candidate_value: np.ndarray | None, default_value: Sequence[Any], dtype: Any
63
+ ) -> np.ndarray:
64
+ if candidate_value is None:
65
+ return np.array(default_value, dtype=dtype)
66
+ return np.array(candidate_value, dtype=dtype)
67
+
68
+
69
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
70
+ class Atoms(table.Table):
71
+ """Table of atoms in a Structure."""
72
+
73
+ chain_key: np.ndarray
74
+ res_key: np.ndarray
75
+ name: np.ndarray
76
+ element: np.ndarray
77
+ x: np.ndarray
78
+ y: np.ndarray
79
+ z: np.ndarray
80
+ b_factor: np.ndarray
81
+ occupancy: np.ndarray
82
+ multimodel_cols: ClassVar[tuple[str, ...]] = (
83
+ 'x',
84
+ 'y',
85
+ 'z',
86
+ 'b_factor',
87
+ 'occupancy',
88
+ )
89
+
90
+ def __post_init__(self):
91
+ # Validates that the atom coordinates, b-factors and occupancies are finite.
92
+ for column_name in ('x', 'y', 'z', 'b_factor', 'occupancy'):
93
+ column = self.get_column(column_name)
94
+ if not np.isfinite(column).all():
95
+ raise ValueError(
96
+ f'Column {column_name} must not contain NaN/inf values.'
97
+ )
98
+ # super().__post_init__() can't be used as that causes the following error:
99
+ # TypeError: super(type, obj): obj must be an instance or subtype of type
100
+ super(Atoms, self).__post_init__()
101
+
102
+ @classmethod
103
+ def make_empty(cls) -> Self:
104
+ return cls(
105
+ key=np.array([], dtype=np.int64),
106
+ chain_key=np.array([], dtype=np.int64),
107
+ res_key=np.array([], dtype=np.int64),
108
+ name=np.array([], dtype=object),
109
+ element=np.array([], dtype=object),
110
+ x=np.array([], dtype=np.float32),
111
+ y=np.array([], dtype=np.float32),
112
+ z=np.array([], dtype=np.float32),
113
+ b_factor=np.array([], dtype=np.float32),
114
+ occupancy=np.array([], dtype=np.float32),
115
+ )
116
+
117
+ @classmethod
118
+ def from_defaults(
119
+ cls,
120
+ *,
121
+ chain_key: np.ndarray,
122
+ res_key: np.ndarray,
123
+ key: np.ndarray | None = None,
124
+ name: np.ndarray | None = None,
125
+ element: np.ndarray | None = None,
126
+ x: np.ndarray | None = None,
127
+ y: np.ndarray | None = None,
128
+ z: np.ndarray | None = None,
129
+ b_factor: np.ndarray | None = None,
130
+ occupancy: np.ndarray | None = None,
131
+ ) -> Self:
132
+ """Create an Atoms table with minimal user inputs."""
133
+ num_atoms = len(chain_key)
134
+ if not num_atoms:
135
+ return cls.make_empty()
136
+ return Atoms(
137
+ chain_key=chain_key,
138
+ res_key=res_key,
139
+ key=_default(key, np.arange(num_atoms), np.int64),
140
+ name=_default(name, ['?'] * num_atoms, object),
141
+ element=_default(element, ['?'] * num_atoms, object),
142
+ x=_default(x, [0.0] * num_atoms, np.float32),
143
+ y=_default(y, [0.0] * num_atoms, np.float32),
144
+ z=_default(z, [0.0] * num_atoms, np.float32),
145
+ b_factor=_default(b_factor, [0.0] * num_atoms, np.float32),
146
+ occupancy=_default(occupancy, [1.0] * num_atoms, np.float32),
147
+ )
148
+
149
+ def get_value_by_index(
150
+ self, column_name: str, index: int
151
+ ) -> table.TableEntry | np.ndarray:
152
+ if column_name in self.multimodel_cols:
153
+ return self.get_column(column_name)[..., index]
154
+ else:
155
+ return self.get_column(column_name)[index]
156
+
157
+ def copy_and_update_coords(self, coords: np.ndarray) -> Self:
158
+ """Returns a copy with the x, y and z columns updated."""
159
+ if coords.shape[-1] != 3:
160
+ raise ValueError(
161
+ f'Expecting 3-dimensional coordinates, got {coords.shape}'
162
+ )
163
+ return typing.cast(
164
+ Atoms,
165
+ self.copy_and_update(
166
+ x=coords[..., 0], y=coords[..., 1], z=coords[..., 2]
167
+ ),
168
+ )
169
+
170
+ @property
171
+ def shape(self) -> tuple[int, ...]:
172
+ return self.x.shape
173
+
174
+ @property
175
+ def ndim(self) -> int:
176
+ return len(self.shape)
177
+
178
+ @functools.cached_property
179
+ def num_models(self) -> int:
180
+ """The number of models of this Structure."""
181
+ leading_dims = self.shape[:-1]
182
+ match leading_dims:
183
+ case ():
184
+ return 1
185
+ case (single_leading_dim_size,):
186
+ return single_leading_dim_size
187
+ case _:
188
+ raise ValueError(
189
+ 'num_models not defined for atom tables with more than one '
190
+ 'leading dimension.'
191
+ )
192
+
193
+
194
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
195
+ class Residues(table.Table):
196
+ """Table of residues in a Structure."""
197
+
198
+ chain_key: np.ndarray
199
+ id: np.ndarray
200
+ name: np.ndarray
201
+ auth_seq_id: np.ndarray
202
+ insertion_code: np.ndarray
203
+
204
+ @classmethod
205
+ def make_empty(cls) -> Self:
206
+ return cls(
207
+ key=np.array([], dtype=np.int64),
208
+ chain_key=np.array([], dtype=np.int64),
209
+ id=np.array([], dtype=np.int32),
210
+ name=np.array([], dtype=object),
211
+ auth_seq_id=np.array([], dtype=object),
212
+ insertion_code=np.array([], dtype=object),
213
+ )
214
+
215
+ @classmethod
216
+ def from_defaults(
217
+ cls,
218
+ *,
219
+ id: np.ndarray, # pylint:disable=redefined-builtin
220
+ chain_key: np.ndarray,
221
+ key: np.ndarray | None = None,
222
+ name: np.ndarray | None = None,
223
+ auth_seq_id: np.ndarray | None = None,
224
+ insertion_code: np.ndarray | None = None,
225
+ ) -> Self:
226
+ """Create a Residues table with minimal user inputs."""
227
+ num_res = len(id)
228
+ if not num_res:
229
+ return cls.make_empty()
230
+ return Residues(
231
+ key=_default(key, np.arange(num_res), np.int64),
232
+ id=id,
233
+ chain_key=chain_key,
234
+ name=_default(name, ['UNK'] * num_res, object),
235
+ auth_seq_id=_default(auth_seq_id, id.astype(str), object),
236
+ insertion_code=_default(insertion_code, ['?'] * num_res, object),
237
+ )
238
+
239
+
240
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
241
+ class Chains(table.Table):
242
+ """Table of chains in a Structure."""
243
+
244
+ id: np.ndarray
245
+ type: np.ndarray
246
+ auth_asym_id: np.ndarray
247
+ entity_id: np.ndarray
248
+ entity_desc: np.ndarray
249
+
250
+ @classmethod
251
+ def make_empty(cls) -> Self:
252
+ return cls(
253
+ key=np.array([], dtype=np.int64),
254
+ id=np.array([], dtype=object),
255
+ type=np.array([], dtype=object),
256
+ auth_asym_id=np.array([], dtype=object),
257
+ entity_id=np.array([], dtype=object),
258
+ entity_desc=np.array([], dtype=object),
259
+ )
260
+
261
+ @classmethod
262
+ def from_defaults(
263
+ cls,
264
+ *,
265
+ id: np.ndarray, # pylint:disable=redefined-builtin
266
+ key: np.ndarray | None = None,
267
+ type: np.ndarray | None = None, # pylint:disable=redefined-builtin
268
+ auth_asym_id: np.ndarray | None = None,
269
+ entity_id: np.ndarray | None = None,
270
+ entity_desc: np.ndarray | None = None,
271
+ ) -> Self:
272
+ """Create a Chains table with minimal user inputs."""
273
+ num_chains = len(id)
274
+ if not num_chains:
275
+ return cls.make_empty()
276
+
277
+ return Chains(
278
+ key=_default(key, np.arange(num_chains), np.int64),
279
+ id=id,
280
+ type=_default(type, [mmcif_names.PROTEIN_CHAIN] * num_chains, object),
281
+ auth_asym_id=_default(auth_asym_id, id, object),
282
+ entity_id=_default(
283
+ entity_id, np.arange(1, num_chains + 1).astype(str), object
284
+ ),
285
+ entity_desc=_default(entity_desc, ['.'] * num_chains, object),
286
+ )
287
+
288
+
289
+ def to_mmcif_sequence_and_entity_tables(
290
+ chains: Chains,
291
+ residues: Residues,
292
+ atom_res_key: np.ndarray,
293
+ ) -> Mapping[str, Sequence[str]]:
294
+ """Returns raw sequence and entity mmCIF tables."""
295
+ raw_mmcif = collections.defaultdict(list)
296
+ chains_by_entity_id = {}
297
+ written_entity_poly_seq_ids = set()
298
+ present_res_keys = set(atom_res_key)
299
+
300
+ # Performance optimisation: Find residue indices for each chain in advance, so
301
+ # that we don't have to do redunant masking work for each chain.
302
+ res_indices_for_chain = aggregation.indices_grouped_by_value(
303
+ residues.chain_key
304
+ )
305
+
306
+ for chain in chains.iterrows():
307
+ # Add all chain information to the _struct_asym table.
308
+ chain_id = chain['id'] # Saves multiple dict lookups.
309
+ auth_asym_id = chain['auth_asym_id']
310
+ entity_id = chain['entity_id']
311
+ chains_by_entity_id.setdefault(entity_id, []).append(chain)
312
+ raw_mmcif['_struct_asym.id'].append(chain_id)
313
+ raw_mmcif['_struct_asym.entity_id'].append(entity_id)
314
+
315
+ res_chain_indices = res_indices_for_chain[chain['key']]
316
+ chain_type = chain['type']
317
+ is_polymer = chain_type in mmcif_names.POLYMER_CHAIN_TYPES
318
+ is_water = chain_type == mmcif_names.WATER
319
+ is_branched = len(res_chain_indices) > 1 and not is_polymer and not is_water
320
+ write_entity_poly_seq = entity_id not in written_entity_poly_seq_ids
321
+
322
+ # Iterate over the individual masked residue table columns, as that doesn't
323
+ # create a copy (only a view), while residues[res_chain_indices] does.
324
+ for res_key, res_name, res_id, pdb_seq_num, res_ins_code in zip(
325
+ residues.key[res_chain_indices],
326
+ residues.name[res_chain_indices],
327
+ residues.id[res_chain_indices],
328
+ residues.auth_seq_id[res_chain_indices],
329
+ residues.insertion_code[res_chain_indices],
330
+ strict=True,
331
+ ):
332
+ is_missing = res_key not in present_res_keys
333
+ str_res_id = str(res_id)
334
+ # While atom_site uses "?" for insertion codes, scheme tables use ".".
335
+ ins_code = (res_ins_code or '.').replace('?', '.')
336
+ auth_seq_num = '?' if is_missing else pdb_seq_num
337
+
338
+ if is_polymer:
339
+ raw_mmcif['_pdbx_poly_seq_scheme.asym_id'].append(chain_id)
340
+ raw_mmcif['_pdbx_poly_seq_scheme.entity_id'].append(entity_id)
341
+ raw_mmcif['_pdbx_poly_seq_scheme.seq_id'].append(str_res_id)
342
+ raw_mmcif['_pdbx_poly_seq_scheme.mon_id'].append(res_name)
343
+ raw_mmcif['_pdbx_poly_seq_scheme.pdb_seq_num'].append(pdb_seq_num)
344
+ raw_mmcif['_pdbx_poly_seq_scheme.auth_seq_num'].append(auth_seq_num)
345
+ raw_mmcif['_pdbx_poly_seq_scheme.pdb_strand_id'].append(auth_asym_id)
346
+ raw_mmcif['_pdbx_poly_seq_scheme.pdb_ins_code'].append(ins_code)
347
+ # Structure doesn't support heterogeneous sequences.
348
+ raw_mmcif['_pdbx_poly_seq_scheme.hetero'].append('n')
349
+ if write_entity_poly_seq:
350
+ raw_mmcif['_entity_poly_seq.entity_id'].append(entity_id)
351
+ raw_mmcif['_entity_poly_seq.num'].append(str_res_id)
352
+ raw_mmcif['_entity_poly_seq.mon_id'].append(res_name)
353
+ # Structure doesn't support heterogeneous sequences.
354
+ raw_mmcif['_entity_poly_seq.hetero'].append('n')
355
+ written_entity_poly_seq_ids.add(entity_id)
356
+ elif is_branched:
357
+ raw_mmcif['_pdbx_branch_scheme.asym_id'].append(chain_id)
358
+ raw_mmcif['_pdbx_branch_scheme.entity_id'].append(entity_id)
359
+ raw_mmcif['_pdbx_branch_scheme.mon_id'].append(res_name)
360
+ raw_mmcif['_pdbx_branch_scheme.num'].append(str_res_id)
361
+ raw_mmcif['_pdbx_branch_scheme.pdb_asym_id'].append(auth_asym_id)
362
+ raw_mmcif['_pdbx_branch_scheme.pdb_seq_num'].append(pdb_seq_num)
363
+ raw_mmcif['_pdbx_branch_scheme.auth_asym_id'].append(auth_asym_id)
364
+ raw_mmcif['_pdbx_branch_scheme.auth_seq_num'].append(auth_seq_num)
365
+ raw_mmcif['_pdbx_branch_scheme.pdb_ins_code'].append(ins_code)
366
+ # Structure doesn't support heterogeneous sequences.
367
+ raw_mmcif['_pdbx_branch_scheme.hetero'].append('n')
368
+ else:
369
+ raw_mmcif['_pdbx_nonpoly_scheme.asym_id'].append(chain_id)
370
+ raw_mmcif['_pdbx_nonpoly_scheme.entity_id'].append(entity_id)
371
+ raw_mmcif['_pdbx_nonpoly_scheme.mon_id'].append(res_name)
372
+ raw_mmcif['_pdbx_nonpoly_scheme.pdb_seq_num'].append(pdb_seq_num)
373
+ raw_mmcif['_pdbx_nonpoly_scheme.auth_seq_num'].append(auth_seq_num)
374
+ raw_mmcif['_pdbx_nonpoly_scheme.pdb_strand_id'].append(auth_asym_id)
375
+ raw_mmcif['_pdbx_nonpoly_scheme.pdb_ins_code'].append(ins_code)
376
+
377
+ # Add _entity and _entity_poly tables.
378
+ for entity_id, chains in chains_by_entity_id.items():
379
+ # chains should always be a non-empty list because of how we constructed
380
+ # chains_by_entity_id.
381
+ assert chains
382
+ # All chains for a given entity should have the same type and sequence
383
+ # so we can pick the first one without losing information.
384
+ key_chain = chains[0]
385
+ raw_mmcif['_entity.id'].append(entity_id)
386
+ raw_mmcif['_entity.pdbx_description'].append(key_chain['entity_desc'])
387
+ entity_type = key_chain['type']
388
+ if entity_type not in mmcif_names.POLYMER_CHAIN_TYPES:
389
+ raw_mmcif['_entity.type'].append(entity_type)
390
+ else:
391
+ raw_mmcif['_entity.type'].append('polymer')
392
+ raw_mmcif['_entity_poly.entity_id'].append(entity_id)
393
+ raw_mmcif['_entity_poly.type'].append(entity_type)
394
+
395
+ # _entity_poly.pdbx_strand_id is a comma-separated list of
396
+ # auth_asym_ids that are part of the entity.
397
+ raw_mmcif['_entity_poly.pdbx_strand_id'].append(
398
+ ','.join(chain['auth_asym_id'] for chain in chains)
399
+ )
400
+ return raw_mmcif
401
+
402
+
403
+ def to_mmcif_atom_site_and_bonds_table(
404
+ *,
405
+ chains: Chains,
406
+ residues: Residues,
407
+ atoms: Atoms,
408
+ bonds: Bonds,
409
+ coords_decimal_places: int,
410
+ ) -> Mapping[str, Sequence[str]]:
411
+ """Returns raw _atom_site and _struct_conn mmCIF tables."""
412
+ raw_mmcif = collections.defaultdict(list)
413
+ # Use [value] * num wherever possible since it is about 10x faster than list
414
+ # comprehension in such cases. Also use f-strings instead of str() - faster.
415
+ total_atoms = atoms.size * atoms.num_models
416
+ raw_mmcif['_atom_site.id'] = [f'{i}' for i in range(1, total_atoms + 1)]
417
+ raw_mmcif['_atom_site.label_alt_id'] = ['.'] * total_atoms
418
+ # Use format_float_array instead of list comprehension for performance.
419
+ raw_mmcif['_atom_site.Cartn_x'] = mmcif.format_float_array(
420
+ values=atoms.x.ravel(), num_decimal_places=coords_decimal_places
421
+ )
422
+ raw_mmcif['_atom_site.Cartn_y'] = mmcif.format_float_array(
423
+ values=atoms.y.ravel(), num_decimal_places=coords_decimal_places
424
+ )
425
+ raw_mmcif['_atom_site.Cartn_z'] = mmcif.format_float_array(
426
+ values=atoms.z.ravel(), num_decimal_places=coords_decimal_places
427
+ )
428
+
429
+ # atoms.b_factor or atoms.occupancy can be flat even when the coordinates have
430
+ # leading dimensions. In this case we tile it to match.
431
+ if atoms.b_factor.ndim == 1:
432
+ atom_b_factor = np.tile(atoms.b_factor, atoms.num_models)
433
+ else:
434
+ atom_b_factor = atoms.b_factor.ravel()
435
+ raw_mmcif['_atom_site.B_iso_or_equiv'] = mmcif.format_float_array(
436
+ values=atom_b_factor, num_decimal_places=2
437
+ )
438
+
439
+ if atoms.occupancy.ndim == 1:
440
+ atom_occupancy = np.tile(atoms.occupancy, atoms.num_models)
441
+ else:
442
+ atom_occupancy = atoms.occupancy.ravel()
443
+ raw_mmcif['_atom_site.occupancy'] = mmcif.format_float_array(
444
+ values=atom_occupancy.ravel(), num_decimal_places=2
445
+ )
446
+
447
+ label_atom_id = atoms.name
448
+ type_symbol = atoms.element
449
+ label_comp_id = residues.apply_array_to_column('name', atoms.res_key)
450
+ label_asym_id = chains.apply_array_to_column('id', atoms.chain_key)
451
+ label_entity_id = chains.apply_array_to_column('entity_id', atoms.chain_key)
452
+ # Performance optimisation: Do the int->str conversion on num_residue-sized,
453
+ # array, then select instead of selecting and then converting.
454
+ label_seq_id = residues.id.astype('str').astype(object)[
455
+ ..., residues.index_by_key[atoms.res_key]
456
+ ]
457
+
458
+ # _atom_site.label_seq_id is '.' for non-polymers.
459
+ non_polymer_chain_mask = string_array.isin(
460
+ chains.type, mmcif_names.POLYMER_CHAIN_TYPES, invert=True
461
+ )
462
+ non_polymer_chain_keys = chains.key[non_polymer_chain_mask]
463
+ non_polymer_atom_mask = np.isin(atoms.chain_key, non_polymer_chain_keys)
464
+ label_seq_id[non_polymer_atom_mask] = '.'
465
+
466
+ auth_asym_id = chains.apply_array_to_column('auth_asym_id', atoms.chain_key)
467
+ auth_seq_id = residues.apply_array_to_column('auth_seq_id', atoms.res_key)
468
+ pdbx_pdb_ins_code = residues.apply_array_to_column(
469
+ 'insertion_code', atoms.res_key
470
+ )
471
+ string_array.remap(pdbx_pdb_ins_code, mapping={None: '?'}, inplace=True)
472
+
473
+ group_pdb = _residue_name_to_record_name(
474
+ residue_name=label_comp_id, polymer_mask=~non_polymer_atom_mask
475
+ )
476
+
477
+ def tile_for_models(arr: np.ndarray) -> list[str]:
478
+ if atoms.num_models == 1:
479
+ return arr.tolist() # Memory optimisation: np.tile(arr, 1) does a copy.
480
+ return np.tile(arr, atoms.num_models).tolist()
481
+
482
+ raw_mmcif['_atom_site.group_PDB'] = tile_for_models(group_pdb)
483
+ raw_mmcif['_atom_site.label_atom_id'] = tile_for_models(label_atom_id)
484
+ raw_mmcif['_atom_site.type_symbol'] = tile_for_models(type_symbol)
485
+ raw_mmcif['_atom_site.label_comp_id'] = tile_for_models(label_comp_id)
486
+ raw_mmcif['_atom_site.label_asym_id'] = tile_for_models(label_asym_id)
487
+ raw_mmcif['_atom_site.label_entity_id'] = tile_for_models(label_entity_id)
488
+ raw_mmcif['_atom_site.label_seq_id'] = tile_for_models(label_seq_id)
489
+ raw_mmcif['_atom_site.auth_asym_id'] = tile_for_models(auth_asym_id)
490
+ raw_mmcif['_atom_site.auth_seq_id'] = tile_for_models(auth_seq_id)
491
+ raw_mmcif['_atom_site.pdbx_PDB_ins_code'] = tile_for_models(pdbx_pdb_ins_code)
492
+ model_id = np.array(
493
+ [str(i + 1) for i in range(atoms.num_models)], dtype=object
494
+ )
495
+ raw_mmcif['_atom_site.pdbx_PDB_model_num'] = np.repeat(
496
+ model_id, [atoms.size] * atoms.num_models
497
+ ).tolist()
498
+
499
+ if bonds.key.size > 0:
500
+ raw_mmcif.update(
501
+ bonds.to_mmcif_dict_from_atom_arrays(
502
+ atom_key=atoms.key,
503
+ chain_id=label_asym_id,
504
+ res_id=label_seq_id,
505
+ res_name=label_comp_id,
506
+ atom_name=label_atom_id,
507
+ auth_asym_id=auth_asym_id,
508
+ auth_seq_id=auth_seq_id,
509
+ insertion_code=np.array(pdbx_pdb_ins_code),
510
+ )
511
+ )
512
+ return raw_mmcif
513
+
514
+
515
+ def _flatten_author_naming_scheme_table(
516
+ res_table: Mapping[str, Mapping[int, str]],
517
+ chain_ids: np.ndarray,
518
+ res_chain_ids: np.ndarray,
519
+ res_ids: np.ndarray,
520
+ default_if_missing: str,
521
+ table_name: str,
522
+ ) -> np.ndarray:
523
+ """Flattens an author naming scheme table consistently with res_ids."""
524
+ if not set(chain_ids).issubset(res_table):
525
+ raise ValueError(
526
+ f'Chain IDs in the chain_id array must be a subset of {table_name} in '
527
+ 'author naming scheme:\n'
528
+ f'chain_ids: {sorted(chain_ids)}\n'
529
+ f'{table_name} keys: {sorted(res_table.keys())}'
530
+ )
531
+
532
+ chain_change_mask = res_chain_ids[1:] != res_chain_ids[:-1]
533
+ res_chain_boundaries = np.concatenate(
534
+ ([0], np.where(chain_change_mask)[0] + 1, [len(res_chain_ids)])
535
+ )
536
+
537
+ flat_vals = np.empty(len(res_ids), dtype=object)
538
+ for chain_start, chain_end in itertools.pairwise(res_chain_boundaries):
539
+ chain_id = res_chain_ids[chain_start]
540
+ chain_res_ids = res_ids[chain_start:chain_end]
541
+ chain_mapping = res_table[chain_id]
542
+ flat_vals[chain_start:chain_end] = [
543
+ chain_mapping.get(r, default_if_missing) for r in chain_res_ids
544
+ ]
545
+
546
+ return flat_vals
547
+
548
+
549
+ def tables_from_atom_arrays(
550
+ *,
551
+ res_id: np.ndarray,
552
+ author_naming_scheme: AuthorNamingScheme | None = None,
553
+ all_residues: Mapping[str, Sequence[tuple[str, int]]] | None = None,
554
+ chain_id: np.ndarray | None = None,
555
+ chain_type: np.ndarray | None = None,
556
+ res_name: np.ndarray | None = None,
557
+ atom_key: np.ndarray | None = None,
558
+ atom_name: np.ndarray | None = None,
559
+ atom_element: np.ndarray | None = None,
560
+ atom_x: np.ndarray | None = None,
561
+ atom_y: np.ndarray | None = None,
562
+ atom_z: np.ndarray | None = None,
563
+ atom_b_factor: np.ndarray | None = None,
564
+ atom_occupancy: np.ndarray | None = None,
565
+ ) -> tuple[Atoms, Residues, Chains]:
566
+ """Returns Structure tables constructed from atom array level data.
567
+
568
+ All fields except name and, res_id are optional, all array fields consist of a
569
+ value for each atom in the structure - so residue and chain values should hold
570
+ the same value for each atom in the chain or residue. Fields which are not
571
+ defined are filled with default values.
572
+
573
+ Validation is performed by the Structure constructor where possible - but
574
+ author_naming scheme and all_residues must be checked in this function.
575
+
576
+ It is not possible to construct structures with chains that do not contain
577
+ any resolved residues using this function. If this is necessary, use the
578
+ structure.Structure constructor directly.
579
+
580
+ Args:
581
+ res_id: Integer array of shape [num_atom]. The unique residue identifier for
582
+ each residue. mmCIF field - _atom_site.label_seq_id.
583
+ author_naming_scheme: An optional instance of AuthorNamingScheme to use when
584
+ converting this structure to mmCIF.
585
+ all_residues: An optional mapping from each chain ID (i.e. label_asym_id) to
586
+ a sequence of (label_comp_id, label_seq_id) tuples, one per residue. This
587
+ can contain residues that aren't present in the atom arrays. This is
588
+ common in experimental data where some residues are not resolved but are
589
+ known to be present.
590
+ chain_id: String array of shape [num_atom] of unique chain identifiers.
591
+ mmCIF field - _atom_site.label_asym_id.
592
+ chain_type: String array of shape [num_atom]. The molecular type of the
593
+ current chain (e.g. polyribonucleotide). mmCIF field - _entity_poly.type
594
+ OR _entity.type (for non-polymers).
595
+ res_name: String array of shape [num_atom].. The name of each residue,
596
+ typically a 3 letter string for polypeptides or 1-2 letter strings for
597
+ polynucleotides. mmCIF field - _atom_site.label_comp_id.
598
+ atom_key: A unique sorted integer array, used only by the bonds table to
599
+ identify the atoms participating in each bond. If the bonds table is
600
+ specified then this column must be non-None.
601
+ atom_name: String array of shape [num_atom]. The name of each atom (e.g CA,
602
+ O2', etc.). mmCIF field - _atom_site.label_atom_id.
603
+ atom_element: String array of shape [num_atom]. The element type of each
604
+ atom (e.g. C, O, N, etc.). mmCIF field - _atom_site.type_symbol.
605
+ atom_x: Float array of shape [..., num_atom] of atom x coordinates. May have
606
+ arbitrary leading dimensions, provided that these are consistent across
607
+ all coordinate fields.
608
+ atom_y: Float array of shape [..., num_atom] of atom y coordinates. May have
609
+ arbitrary leading dimensions, provided that these are consistent across
610
+ all coordinate fields.
611
+ atom_z: Float array of shape [..., num_atom] of atom z coordinates. May have
612
+ arbitrary leading dimensions, provided that these are consistent across
613
+ all coordinate fields.
614
+ atom_b_factor: Float array of shape [..., num_atom] or [num_atom] of atom
615
+ b-factors or equivalent. If there are no extra leading dimensions then
616
+ these values are assumed to apply to all coordinates for a given atom. If
617
+ there are leading dimensions then these must match those used by the
618
+ coordinate fields.
619
+ atom_occupancy: Float array of shape [..., num_atom] or [num_atom] of atom
620
+ occupancies or equivalent. If there are no extra leading dimensions then
621
+ these values are assumed to apply to all coordinates for a given atom. If
622
+ there are leading dimensions then these must match those used by the
623
+ coordinate fields.
624
+ """
625
+ num_atoms = len(res_id)
626
+
627
+ for arr_name, array, dtype in (
628
+ ('chain_id', chain_id, object),
629
+ ('chain_type', chain_type, object),
630
+ ('res_id', res_id, np.int32),
631
+ ('res_name', res_name, object),
632
+ ('atom_key', atom_key, np.int64),
633
+ ('atom_name', atom_name, object),
634
+ ('atom_element', atom_element, object),
635
+ ):
636
+ if array is not None and array.shape != (num_atoms,):
637
+ raise ValueError(f'{arr_name} shape {array.shape} != ({num_atoms},)')
638
+ if array is not None and array.dtype != dtype:
639
+ raise ValueError(f'{arr_name} dtype {array.dtype} != {dtype}')
640
+
641
+ for arr_name, array in (
642
+ ('atom_x', atom_x),
643
+ ('atom_y', atom_y),
644
+ ('atom_z', atom_z),
645
+ ('atom_b_factor', atom_b_factor),
646
+ ('atom_occupancy', atom_occupancy),
647
+ ):
648
+ if array is not None and array.shape[-1] != num_atoms:
649
+ raise ValueError(f'{arr_name} last dim {array.shape[-1]} != {num_atoms=}')
650
+ if (
651
+ array is not None
652
+ and array.dtype != np.float32
653
+ and array.dtype != np.float64
654
+ ):
655
+ raise ValueError(
656
+ f'{arr_name} must be np.float32 or np.float64, got {array.dtype=}'
657
+ )
658
+
659
+ if all_residues is not None and (res_name is None or res_id is None):
660
+ raise ValueError(
661
+ 'If all_residues != None, res_name and res_id must not be None either.'
662
+ )
663
+
664
+ if num_atoms == 0:
665
+ return Atoms.make_empty(), Residues.make_empty(), Chains.make_empty()
666
+
667
+ if chain_id is None:
668
+ chain_id = np.full(shape=num_atoms, fill_value='A', dtype=object)
669
+ if res_name is None:
670
+ res_name = np.full(shape=num_atoms, fill_value='UNK', dtype=object)
671
+
672
+ chain_change_mask = chain_id[1:] != chain_id[:-1]
673
+ chain_start = np.concatenate(([0], np.where(chain_change_mask)[0] + 1))
674
+ res_start = np.concatenate(
675
+ ([0], np.where((res_id[1:] != res_id[:-1]) | chain_change_mask)[0] + 1)
676
+ )
677
+
678
+ if len(set(chain_id)) != len(chain_start):
679
+ raise ValueError(f'Chain IDs must be contiguous, but got {chain_id}')
680
+
681
+ # We do not support chains with unresolved residues-only in this function.
682
+ chain_ids = chain_id[chain_start]
683
+ if all_residues and set(all_residues.keys()) != set(chain_ids):
684
+ raise ValueError(
685
+ 'all_residues must contain the same set of chain IDs as the chain_id '
686
+ f'array:\nall_residues keys: {sorted(all_residues.keys())}\n'
687
+ f'chain_ids: {sorted(chain_ids)}.'
688
+ )
689
+ # Make sure all_residue ordering is consistent with chain_id.
690
+ if all_residues and np.any(list(all_residues.keys()) != chain_ids):
691
+ all_residues = {cid: all_residues[cid] for cid in chain_ids}
692
+
693
+ # Create the chains table.
694
+ num_chains = len(chain_ids)
695
+ chain_keys = np.arange(num_chains, dtype=np.int64)
696
+ chain_key_by_chain_id = dict(zip(chain_ids, chain_keys, strict=True))
697
+
698
+ if chain_type is not None:
699
+ chain_types = chain_type[chain_start]
700
+ else:
701
+ chain_types = np.full(num_chains, mmcif_names.PROTEIN_CHAIN, dtype=object)
702
+
703
+ if author_naming_scheme is not None:
704
+ auth_asym_id = string_array.remap(
705
+ chain_ids, author_naming_scheme.auth_asym_id
706
+ )
707
+ entity_id = string_array.remap(
708
+ chain_ids, author_naming_scheme.entity_id, default_value='.'
709
+ )
710
+ entity_desc = string_array.remap(
711
+ entity_id, author_naming_scheme.entity_desc, default_value='.'
712
+ )
713
+ else:
714
+ auth_asym_id = chain_ids
715
+ entity_id = (chain_keys + 1).astype(str).astype(object)
716
+ entity_desc = np.full(num_chains, '.', dtype=object)
717
+
718
+ chains = Chains(
719
+ key=chain_keys,
720
+ id=chain_ids,
721
+ type=chain_types,
722
+ auth_asym_id=auth_asym_id,
723
+ entity_id=entity_id,
724
+ entity_desc=entity_desc,
725
+ )
726
+
727
+ # Create the residues table.
728
+ if all_residues is not None:
729
+ residue_order = []
730
+ for cid, residues in all_residues.items():
731
+ residue_order.extend((cid, rname, int(rid)) for (rname, rid) in residues)
732
+ res_chain_ids, res_names, res_ids = zip(*residue_order)
733
+ res_chain_ids = np.array(res_chain_ids, dtype=object)
734
+ res_ids = np.array(res_ids, dtype=np.int32)
735
+ res_names = np.array(res_names, dtype=object)
736
+ else:
737
+ res_chain_ids = chain_id[res_start]
738
+ res_ids = res_id[res_start]
739
+ res_names = res_name[res_start]
740
+ residue_order = list(zip(res_chain_ids, res_names, res_ids))
741
+
742
+ if author_naming_scheme is not None and author_naming_scheme.auth_seq_id:
743
+ auth_seq_id = _flatten_author_naming_scheme_table(
744
+ author_naming_scheme.auth_seq_id,
745
+ chain_ids=chain_ids,
746
+ res_chain_ids=res_chain_ids,
747
+ res_ids=res_ids,
748
+ default_if_missing='.',
749
+ table_name='auth_seq_id',
750
+ )
751
+ else:
752
+ auth_seq_id = res_ids.astype(str).astype(object)
753
+
754
+ if author_naming_scheme is not None and author_naming_scheme.insertion_code:
755
+ insertion_code = _flatten_author_naming_scheme_table(
756
+ author_naming_scheme.insertion_code,
757
+ chain_ids=chain_ids,
758
+ res_chain_ids=res_chain_ids,
759
+ res_ids=res_ids,
760
+ default_if_missing='?',
761
+ table_name='insertion_code',
762
+ )
763
+ # Make sure insertion code of None is mapped to '.'.
764
+ insertion_code = string_array.remap(insertion_code, {None: '?'})
765
+ else:
766
+ insertion_code = np.full(shape=len(res_ids), fill_value='?', dtype=object)
767
+
768
+ res_key_by_res = {res: i for i, res in enumerate(residue_order)}
769
+ res_keys = np.arange(len(residue_order), dtype=np.int64)
770
+ res_chain_keys = string_array.remap(
771
+ res_chain_ids, chain_key_by_chain_id
772
+ ).astype(np.int64)
773
+ residues = Residues(
774
+ chain_key=res_chain_keys,
775
+ key=res_keys,
776
+ id=res_ids,
777
+ name=res_names,
778
+ auth_seq_id=auth_seq_id,
779
+ insertion_code=insertion_code,
780
+ )
781
+
782
+ if atom_key is None:
783
+ atom_key = np.arange(num_atoms, dtype=np.int64)
784
+
785
+ atom_chain_keys = string_array.remap(chain_id, chain_key_by_chain_id).astype(
786
+ np.int64
787
+ )
788
+
789
+ try:
790
+ atom_res_keys = [res_key_by_res[r] for r in zip(chain_id, res_name, res_id)]
791
+ except KeyError as e:
792
+ missing_chain_id, missing_res_name, missing_res_id = e.args[0]
793
+ raise ValueError(
794
+ 'Inconsistent res_name, res_id and all_residues. Could not find '
795
+ f'residue with chain_id={missing_chain_id}, '
796
+ f'res_name={missing_res_name}, res_id={missing_res_id} in all_residues.'
797
+ ) from e
798
+
799
+ atoms = Atoms(
800
+ key=atom_key,
801
+ chain_key=atom_chain_keys,
802
+ res_key=np.array(atom_res_keys, dtype=np.int64),
803
+ name=_default(atom_name, ['?'] * num_atoms, object),
804
+ element=_default(atom_element, ['?'] * num_atoms, object),
805
+ x=_default(atom_x, [0.0] * num_atoms, np.float32),
806
+ y=_default(atom_y, [0.0] * num_atoms, np.float32),
807
+ z=_default(atom_z, [0.0] * num_atoms, np.float32),
808
+ b_factor=_default(atom_b_factor, [0.0] * num_atoms, np.float32),
809
+ occupancy=_default(atom_occupancy, [1.0] * num_atoms, np.float32),
810
+ )
811
+ return atoms, residues, chains
flax_model/alphafold3/structure/table.py ADDED
@@ -0,0 +1,555 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ """Table module for atom/residue/chain tables in Structure.
4
+
5
+ Tables are intended to be lightweight collections of columns, loosely based
6
+ on a pandas dataframe, for use in the Structure class.
7
+ """
8
+
9
+ import abc
10
+ from collections.abc import Callable, Collection, Iterable, Iterator, Mapping, Sequence
11
+ import dataclasses
12
+ import functools
13
+ import graphlib
14
+ import typing
15
+ from typing import Any, Protocol, Self, TypeAlias, TypeVar, overload
16
+
17
+ from flax_model.alphafold3.cpp import string_array
18
+ import numpy as np
19
+
20
+
21
+ TableEntry: TypeAlias = str | int | float | None
22
+ FilterPredicate: TypeAlias = (
23
+ TableEntry
24
+ | Iterable[Any] # Workaround for b/326384670. Tighten once fixed.
25
+ | Callable[[Any], bool] # Workaround for b/326384670. Tighten once fixed.
26
+ | Callable[[np.ndarray], bool]
27
+ )
28
+
29
+
30
+ class RowLookup(Protocol):
31
+
32
+ def get_row_by_key(
33
+ self,
34
+ key: int,
35
+ column_name_map: Mapping[str, str] | None = None,
36
+ ) -> Mapping[str, Any]:
37
+ ...
38
+
39
+
40
+ @dataclasses.dataclass(frozen=True, kw_only=True)
41
+ class Table:
42
+ """Parent class for structure tables.
43
+
44
+ A table is a collection of columns of equal length, where one column is the
45
+ key. The key uniquely identifies each row in the table.
46
+
47
+ A table can refer to other tables by including a foreign key column, whose
48
+ values are key values from the other table's key column. These column can have
49
+ arbitrary names and are treated like any other integer-valued column.
50
+
51
+ See the `Database` class in this module for utilities for handing sets of
52
+ tables that are related via foreign keys.
53
+
54
+ NB: This does not correspond to an mmCIF table.
55
+ """
56
+
57
+ key: np.ndarray
58
+
59
+ def __post_init__(self):
60
+ for col_name in self.columns:
61
+ if (col_len := self.get_column(col_name).shape[-1]) != self.size:
62
+ raise ValueError(
63
+ f'All columns should have length {self.size} but got "{col_name}"'
64
+ f' with length {col_len}.'
65
+ )
66
+ self.get_column(col_name).flags.writeable = False # Make col immutable.
67
+ if self.key.size and self.key.min() < 0:
68
+ raise ValueError(
69
+ 'Key values must be non-negative. Got negative values:'
70
+ f' {set(self.key[self.key < 0])}'
71
+ )
72
+ self.key.flags.writeable = False # Make key immutable.
73
+
74
+ def __getstate__(self) -> dict[str, Any]:
75
+ """Returns members with cached properties removed for pickling."""
76
+ cached_props = {
77
+ k
78
+ for k, v in self.__class__.__dict__.items()
79
+ if isinstance(v, functools.cached_property)
80
+ }
81
+ return {k: v for k, v in self.__dict__.items() if k not in cached_props}
82
+
83
+ @functools.cached_property
84
+ def index_by_key(self) -> np.ndarray:
85
+ """Mapping from key values to their index in the column arrays.
86
+
87
+ i.e.: self.key[index_by_key[k]] == k
88
+ """
89
+ if not self.key.size:
90
+ return np.array([], dtype=np.int64)
91
+ else:
92
+ index_by_key = np.zeros(np.max(self.key) + 1, dtype=np.int64)
93
+ index_by_key[self.key] = np.arange(self.size)
94
+ return index_by_key
95
+
96
+ @functools.cached_property
97
+ def columns(self) -> tuple[str, ...]:
98
+ """The names of the columns in the table, including the key column."""
99
+ return tuple(field.name for field in dataclasses.fields(self))
100
+
101
+ @functools.cached_property
102
+ def items(self) -> Mapping[str, np.ndarray]:
103
+ """Returns the mapping from column names to column values."""
104
+ return {col: getattr(self, col) for col in self.columns}
105
+
106
+ @functools.cached_property
107
+ def size(self) -> int:
108
+ """The number of rows in the table."""
109
+ return self.key.shape[-1]
110
+
111
+ def __len__(self) -> int:
112
+ return self.size
113
+
114
+ def get_column(self, column_name: str) -> np.ndarray:
115
+ """Gets a column by name."""
116
+ # Performance optimisation: use the cached columns, instead of getattr.
117
+ return self.items[column_name]
118
+
119
+ def apply_array(self, arr: np.ndarray) -> Self:
120
+ """Returns a sliced table using a key (!= index) array or a boolean mask."""
121
+ if arr.dtype == bool and np.all(arr):
122
+ return self # Shortcut: No-op, so just return.
123
+
124
+ return self.copy_and_update(**{
125
+ column_name: self.apply_array_to_column(column_name, arr)
126
+ for column_name in self.columns
127
+ })
128
+
129
+ def apply_index(self, index_arr: np.ndarray) -> Self:
130
+ """Returns a sliced table using an index (!= key) array."""
131
+ if index_arr.dtype == bool:
132
+ raise ValueError('The index array must not be a boolean mask.')
133
+
134
+ return self.copy_and_update(
135
+ **{col: self.get_column(col)[..., index_arr] for col in self.columns}
136
+ )
137
+
138
+ def apply_array_to_column(
139
+ self,
140
+ column_name: str,
141
+ arr: np.ndarray,
142
+ ) -> np.ndarray:
143
+ """Returns a sliced column array using a key array or a boolean mask."""
144
+ if arr.dtype == bool:
145
+ return self.get_column(column_name)[..., arr]
146
+ else:
147
+ return self.get_column(column_name)[..., self.index_by_key[arr]]
148
+
149
+ def get_value_by_index(self, column_name: str, index: int) -> Any:
150
+ return self.get_column(column_name)[index]
151
+
152
+ def get_value_by_key(
153
+ self,
154
+ column_name: str,
155
+ key: int | np.integer,
156
+ ) -> TableEntry:
157
+ """Gets the value of a column at the row with specified key value."""
158
+ return self.get_value_by_index(column_name, self.index_by_key[key])
159
+
160
+ @overload
161
+ def __getitem__(self, key: str) -> np.ndarray:
162
+ ...
163
+
164
+ @overload
165
+ def __getitem__(self, key: np.ndarray) -> 'Table':
166
+ ...
167
+
168
+ @overload
169
+ def __getitem__(self, key: tuple[str, int | np.integer]) -> TableEntry:
170
+ ...
171
+
172
+ @overload
173
+ def __getitem__(self, key: tuple[str, np.ndarray]) -> np.ndarray:
174
+ ...
175
+
176
+ def __getitem__(self, key):
177
+ match key:
178
+ case str():
179
+ return self.get_column(key)
180
+ case np.ndarray() as key_arr_or_mask:
181
+ return self.apply_array(key_arr_or_mask)
182
+ case str() as col, int() | np.integer() as key_val:
183
+ return self.get_value_by_key(col, key_val)
184
+ case str() as col, np.ndarray() as key_arr_or_mask:
185
+ return self.apply_array_to_column(col, key_arr_or_mask)
186
+ case _:
187
+ if isinstance(key, tuple):
188
+ err_msg = f'{key}, type: tuple({[type(v) for v in key]})'
189
+ else:
190
+ err_msg = f'{key}, type: {type(key)}'
191
+ raise KeyError(err_msg)
192
+
193
+ def get_row_by_key(
194
+ self,
195
+ key: int,
196
+ column_name_map: Mapping[str, str] | None = None,
197
+ ) -> dict[str, Any]:
198
+ """Gets the row with specified key value."""
199
+ return self.get_row_by_index(
200
+ self.index_by_key[key], column_name_map=column_name_map
201
+ )
202
+
203
+ def get_row_by_index(
204
+ self,
205
+ index: int,
206
+ column_name_map: Mapping[str, str] | None = None,
207
+ ) -> dict[str, Any]:
208
+ """Gets the row at the specified index."""
209
+ if column_name_map is not None:
210
+ return {
211
+ renamed_col: self.get_value_by_index(col, index)
212
+ for renamed_col, col in column_name_map.items()
213
+ }
214
+ else:
215
+ return {col: self.get_value_by_index(col, index) for col in self.columns}
216
+
217
+ def iterrows(
218
+ self,
219
+ *,
220
+ row_keys: np.ndarray | None = None,
221
+ column_name_map: Mapping[str, str] | None = None,
222
+ **table_by_foreign_key_col: RowLookup,
223
+ ) -> Iterator[Mapping[str, Any]]:
224
+ """Yields rows from the table.
225
+
226
+ This can be used to easily convert a table to a Pandas dataframe:
227
+
228
+ ```py
229
+ df = pd.DataFrame(table.iterrows())
230
+ ```
231
+
232
+ Args:
233
+ row_keys: An optional array of keys of rows to yield. If None, all rows
234
+ will be yielded.
235
+ column_name_map: An optional mapping from desired keys in the row dicts to
236
+ the names of the columns they correspond to.
237
+ **table_by_foreign_key_col: An optional mapping from column names in this
238
+ table, which are expected to be columns of foreign keys, to the table
239
+ that the foreign keys point into. If provided, then the yielded rows
240
+ will include data from the foreign tables at the appropriate key.
241
+ """
242
+ if row_keys is not None:
243
+ row_indices = self.index_by_key[row_keys]
244
+ else:
245
+ row_indices = range(self.size)
246
+ for i in row_indices:
247
+ row = self.get_row_by_index(i, column_name_map=column_name_map)
248
+ for key_col, table in table_by_foreign_key_col.items():
249
+ foreign_key = self[key_col][i]
250
+ foreign_row = table.get_row_by_key(foreign_key)
251
+ row.update(foreign_row)
252
+ yield row
253
+
254
+ def with_column_names(
255
+ self, column_name_map: Mapping[str, str]
256
+ ) -> 'RenamedTableView':
257
+ """Returns a view of this table with mapped column names."""
258
+ return RenamedTableView(self, column_name_map=column_name_map)
259
+
260
+ def make_filter_mask(
261
+ self,
262
+ mask: np.ndarray | None = None,
263
+ *,
264
+ apply_per_element: bool = False,
265
+ **predicate_by_col: FilterPredicate,
266
+ ) -> np.ndarray | None:
267
+ """Returns a boolean array of rows to keep, or None if all can be kept.
268
+
269
+ Args:
270
+ mask: See `Table.filter`.
271
+ apply_per_element: See `Table.filter`.
272
+ **predicate_by_col: See `Table.filter`.
273
+
274
+ Returns:
275
+ Either a boolean NumPy array of length `(self.size,)` denoting which rows
276
+ should be kept according to the input mask and predicates, or None. None
277
+ implies there is no filtering required, and is used where possible
278
+ instead of an all-True array to save time and space.
279
+ """
280
+ if mask is None:
281
+ if not predicate_by_col:
282
+ return None
283
+ else:
284
+ mask = np.ones((self.size,), dtype=bool)
285
+ else:
286
+ if mask.shape != (self.size,):
287
+ raise ValueError(
288
+ f'mask must have shape ({self.size},). Got: {mask.shape}.'
289
+ )
290
+ if mask.dtype != bool:
291
+ raise ValueError(f'mask must have dtype bool. Got: {mask.dtype}.')
292
+
293
+ for col, predicate in predicate_by_col.items():
294
+ if self[col].ndim > 1:
295
+ raise ValueError(
296
+ f'Cannot filter by column {col} with more than 1 dimension.'
297
+ )
298
+
299
+ callable_predicates = []
300
+ if not callable(predicate):
301
+ if isinstance(predicate, Iterable) and not isinstance(predicate, str):
302
+ target_vals = predicate
303
+ else:
304
+ target_vals = [predicate]
305
+ for target_val in target_vals:
306
+ callable_predicates.append(lambda x, target=target_val: x == target)
307
+ else:
308
+ callable_predicates.append(predicate)
309
+
310
+ field_mask = np.zeros_like(mask)
311
+ for callable_predicate in callable_predicates:
312
+ if not apply_per_element:
313
+ callable_predicate = typing.cast(
314
+ Callable[[np.ndarray], bool], callable_predicate
315
+ )
316
+ predicate_result = callable_predicate(self.get_column(col))
317
+ else:
318
+ predicate_result = np.array(
319
+ [callable_predicate(elem) for elem in self.get_column(col)]
320
+ )
321
+ np.logical_or(field_mask, predicate_result, out=field_mask)
322
+ np.logical_and(mask, field_mask, out=mask) # Update in-place.
323
+ return mask
324
+
325
+ def filter(
326
+ self,
327
+ mask: np.ndarray | None = None,
328
+ *,
329
+ apply_per_element: bool = False,
330
+ invert: bool = False,
331
+ **predicate_by_col: FilterPredicate,
332
+ ) -> Self:
333
+ """Filters the table using mask and/or predicates and returns a new table.
334
+
335
+ Predicates can be either:
336
+ 1. A constant value, e.g. `'CA'`. In this case then only rows that match
337
+ this value for the given column are retained.
338
+ 2. A (non-string) iterable e.g. `('A', 'B')`. In this
339
+ case then rows are retained if they match any of the provided values for
340
+ the given column.
341
+ 3. A boolean function e.g. `lambda b_fac: b_fac < 100.0`.
342
+ In this case then only rows that evaluate to `True` are retained. By
343
+ default this function's parameter is expected to be an array, unless
344
+ `apply_per_element=True`.
345
+
346
+ Args:
347
+ mask: An optional boolean NumPy array with length equal to the table size.
348
+ If provided then this will be combined with the other predicates so that
349
+ a row is included if it is masked-in *and* matches all the predicates.
350
+ apply_per_element: Whether apply predicates to each element in the column
351
+ individually, or to pass the whole column array to the predicate.
352
+ invert: If True then the returned table will contain exactly those rows
353
+ that would be removed if this was `False`.
354
+ **predicate_by_col: A mapping from column name to a predicate. Filtered
355
+ columns must be 1D arrays. If multiple columns are provided as keyword
356
+ arguments then each predicate is applied and the results are combined
357
+ using a boolean AND operation, so an atom is only retained if it passes
358
+ all predicates.
359
+
360
+ Returns:
361
+ A new table with the desired rows retained (or filtered out if
362
+ `invert=True`).
363
+
364
+ Raises:
365
+ ValueError: If mask is provided and is not a bool array with shape
366
+ `(num_atoms,)`.
367
+ """
368
+ filter_mask = self.make_filter_mask(
369
+ mask, apply_per_element=apply_per_element, **predicate_by_col
370
+ )
371
+ if filter_mask is None:
372
+ # No mask or predicate was specified, so we can return early.
373
+ if not invert:
374
+ return self
375
+ else:
376
+ return self[np.array((), dtype=np.int64)]
377
+ else:
378
+ return self[~filter_mask if invert else filter_mask]
379
+
380
+ def _validate_keys_are_column_names(self, keys: Collection[str]) -> None:
381
+ """Raises an error if any of the keys are not column names."""
382
+ if mismatches := set(keys) - set(self.columns):
383
+ raise ValueError(f'Invalid column names: {sorted(mismatches)}.')
384
+
385
+ def copy_and_update(self, **new_column_by_column_name: np.ndarray) -> Self:
386
+ """Returns a copy of this table with the specified changes applied.
387
+
388
+ Args:
389
+ **new_column_by_column_name: New values for the specified columns.
390
+
391
+ Raises:
392
+ ValueError: If a specified column name is not a column in this table.
393
+ """
394
+ self._validate_keys_are_column_names(new_column_by_column_name)
395
+ return dataclasses.replace(self, **new_column_by_column_name)
396
+
397
+ def copy_and_remap(
398
+ self, **mapping_by_col: Mapping[TableEntry, TableEntry]
399
+ ) -> Self:
400
+ """Returns a copy of the table with the specified columns remapped.
401
+
402
+ Args:
403
+ **mapping_by_col: Each kwarg key should be the name of one of this table's
404
+ columns, and each value should be a mapping. The values in the column
405
+ will be looked up in the mapping and replaced with the result if one is
406
+ found.
407
+
408
+ Raises:
409
+ ValueError: If a specified column name is not a column in this table.
410
+ """
411
+ self._validate_keys_are_column_names(mapping_by_col)
412
+ if not self.size:
413
+ return self
414
+ remapped_cols = {}
415
+ for column_name, mapping in mapping_by_col.items():
416
+ col_arr = self.get_column(column_name)
417
+ if col_arr.dtype == object:
418
+ remapped = string_array.remap(col_arr, mapping)
419
+ else:
420
+ remapped = np.vectorize(lambda x: mapping.get(x, x))(col_arr) # pylint: disable=cell-var-from-loop
421
+ remapped_cols[column_name] = remapped
422
+ return self.copy_and_update(**remapped_cols)
423
+
424
+
425
+ class RenamedTableView:
426
+ """View of a table with renamed column names."""
427
+
428
+ def __init__(self, table: Table, column_name_map: Mapping[str, str]):
429
+ self._table = table
430
+ self._column_name_map = column_name_map
431
+
432
+ def get_row_by_key(
433
+ self,
434
+ key: int,
435
+ column_name_map: Mapping[str, str] | None = None,
436
+ ) -> Mapping[str, Any]:
437
+ del column_name_map
438
+ return self._table.get_row_by_key(
439
+ key, column_name_map=self._column_name_map
440
+ )
441
+
442
+
443
+ _DatabaseT = TypeVar('_DatabaseT', bound='Database')
444
+
445
+
446
+ class Database(abc.ABC):
447
+ """Relational database base class."""
448
+
449
+ @property
450
+ @abc.abstractmethod
451
+ def tables(self) -> Collection[str]:
452
+ """The names of the tables in this database."""
453
+
454
+ @abc.abstractmethod
455
+ def get_table(self, table_name: str) -> Table:
456
+ """Gets the table with the given name."""
457
+
458
+ @property
459
+ @abc.abstractmethod
460
+ def foreign_keys(self) -> Mapping[str, Collection[tuple[str, str]]]:
461
+ """Describes the relationship between keys in the database.
462
+
463
+ Returns:
464
+ A map from table names to pairs of `(column_name, foreign_table_name)`
465
+ where `column_name` is a column containing foreign keys in the table named
466
+ by the key, and the `foreign_table_name` is the name of the table that
467
+ those foreign keys refer to.
468
+ """
469
+
470
+ @abc.abstractmethod
471
+ def copy_and_update(
472
+ self: _DatabaseT,
473
+ **new_field_by_field_name: ...,
474
+ ) -> _DatabaseT:
475
+ """Returns a copy of this database with the specified changes applied."""
476
+
477
+
478
+ def table_dependency_order(db: Database) -> Iterable[str]:
479
+ """Yields the names of the tables in the database in dependency order.
480
+
481
+ This order guarantees that a table appears after all other tables that
482
+ it refers to using foreign keys. Specifically A < B implies that A contains
483
+ no column that refers to B.key as a foreign key.
484
+
485
+ Args:
486
+ db: The database that defines the table names and foreign keys.
487
+ """
488
+ connections: dict[str, set[str]] = {}
489
+ for table_name in db.tables:
490
+ connection_set = set()
491
+ for _, foreign_table in db.foreign_keys.get(table_name, ()):
492
+ connection_set.add(foreign_table)
493
+ connections[table_name] = connection_set
494
+ yield from graphlib.TopologicalSorter(connections).static_order()
495
+
496
+
497
+ def concat_databases(dbs: Sequence[_DatabaseT]) -> _DatabaseT:
498
+ """Concatenates the tables across a sequence of databases.
499
+
500
+ Args:
501
+ dbs: A non-empty sequence of database instances of the same type.
502
+
503
+ Returns:
504
+ A new database containing the concatenated tables from the input databases.
505
+
506
+ Raises:
507
+ ValueError: If `dbs` is empty or `dbs` contains different Database
508
+ types.
509
+ """
510
+ if not dbs:
511
+ raise ValueError('Need at least one value to concatenate.')
512
+ distinct_db_types = {type(db) for db in dbs}
513
+ if len(distinct_db_types) > 1:
514
+ raise ValueError(
515
+ f'All `dbs` must be of the same type, got: {distinct_db_types}'
516
+ )
517
+
518
+ first_db, *other_dbs = dbs
519
+ concatted_tables: dict[str, Table] = {}
520
+ key_offsets: dict[str, list[int]] = {}
521
+ for table_name in table_dependency_order(first_db):
522
+ first_table = first_db.get_table(table_name)
523
+ columns: dict[str, list[np.ndarray]] = {
524
+ column_name: [first_table.get_column(column_name)]
525
+ for column_name in first_table.columns
526
+ }
527
+ key_offsets[table_name] = [
528
+ first_table.key.max() + 1 if first_table.size else 0
529
+ ]
530
+
531
+ for prev_index, db in enumerate(other_dbs):
532
+ table = db.get_table(table_name)
533
+ for col_name in table.columns:
534
+ columns[col_name].append(table.get_column(col_name))
535
+ key_offset = key_offsets[table_name][prev_index]
536
+ offset_key = table.key + key_offset
537
+ columns['key'][-1] = offset_key
538
+ if table.size:
539
+ key_offsets[table_name].append(offset_key.max() + 1)
540
+ else:
541
+ key_offsets[table_name].append(key_offsets[table_name][prev_index])
542
+ for fkey_col_name, foreign_table_name in first_db.foreign_keys.get(
543
+ table_name, []
544
+ ):
545
+ fkey_columns = columns[fkey_col_name]
546
+ fkey_columns[-1] = (
547
+ fkey_columns[-1] + key_offsets[foreign_table_name][prev_index]
548
+ )
549
+
550
+ concatted_columns = {
551
+ column_name: np.concatenate(values, axis=-1)
552
+ for column_name, values in columns.items()
553
+ }
554
+ concatted_tables[table_name] = (type(first_table))(**concatted_columns)
555
+ return first_db.copy_and_update(**concatted_tables)
flax_model/alphafold3/structure/test_utils.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ """Utilities for structure module testing."""
4
+
5
+ import dataclasses
6
+
7
+ from absl.testing import parameterized
8
+ from flax_model.alphafold3 import structure
9
+ from flax_model.alphafold3.common.testing import data
10
+ import numpy as np
11
+ import tree
12
+
13
+
14
+ class StructureTestCase(parameterized.TestCase):
15
+ """Testing utilities for working with structure.Structure."""
16
+
17
+ def assertAuthorNamingSchemeEqual(self, ans1, ans2): # pylint: disable=invalid-name
18
+ """Walks naming scheme, making sure all elements are equal."""
19
+ if ans1 is None or ans2 is None:
20
+ self.assertIsNone(ans1)
21
+ self.assertIsNone(ans2)
22
+ return
23
+ flat_ans1 = dict(tree.flatten_with_path(dataclasses.asdict(ans1)))
24
+ flat_ans2 = dict(tree.flatten_with_path(dataclasses.asdict(ans2)))
25
+ for k, v in flat_ans1.items():
26
+ self.assertEqual(v, flat_ans2[k], msg=str(k))
27
+ for k, v in flat_ans2.items():
28
+ self.assertEqual(v, flat_ans1[k], msg=str(k))
29
+
30
+ def assertAllResiduesEqual(self, all_res1, all_res2): # pylint: disable=invalid-name
31
+ """Walks all residues, making sure alll elements are equal."""
32
+ if all_res1 is None or all_res2 is None:
33
+ self.assertIsNone(all_res1)
34
+ self.assertIsNone(all_res2)
35
+ return
36
+ self.assertSameElements(all_res1.keys(), all_res2.keys())
37
+ for chain_id, chain_res in all_res1.items():
38
+ self.assertSequenceEqual(chain_res, all_res2[chain_id], msg=chain_id)
39
+
40
+ def assertBioassemblyDataEqual(self, data1, data2): # pylint: disable=invalid-name
41
+ if data1 is None or data2 is None:
42
+ self.assertIsNone(data1)
43
+ self.assertIsNone(data2)
44
+ return
45
+ self.assertDictEqual(data1.to_mmcif_dict(), data2.to_mmcif_dict())
46
+
47
+ def assertChemicalComponentsDataEqual( # pylint: disable=invalid-name
48
+ self,
49
+ data1,
50
+ data2,
51
+ allow_chem_comp_data_extension,
52
+ ):
53
+ """Checks whether two ChemicalComponentData objects are considered equal."""
54
+ if data1 is None or data2 is None:
55
+ self.assertIsNone(data1)
56
+ self.assertIsNone(data2)
57
+ return
58
+ if (not allow_chem_comp_data_extension) or (
59
+ data1.chem_comp.keys() ^ data2.chem_comp.keys()
60
+ ):
61
+ self.assertDictEqual(data1.chem_comp, data2.chem_comp)
62
+ else:
63
+ mismatching_values = []
64
+ for component_id in data1.chem_comp:
65
+ found = data1.chem_comp[component_id]
66
+ expected = data2.chem_comp[component_id]
67
+ if not found.extends(expected):
68
+ mismatching_values.append((component_id, expected, found))
69
+
70
+ if mismatching_values:
71
+ mismatch_err_msgs = '\n'.join(
72
+ f'{component_id}: {expected} or its extension expected,'
73
+ f' but {found} found.'
74
+ for component_id, expected, found in mismatching_values
75
+ )
76
+ self.fail(
77
+ f'Mismatching values for `_chem_comp` table: {mismatch_err_msgs}',
78
+ )
79
+
80
+ def assertBondsEqual(self, bonds1, bonds2, atom_key1, atom_key2): # pylint: disable=invalid-name
81
+ """Checks whether two Bonds objects are considered equal."""
82
+ # An empty bonds table is functionally equivalent to an empty bonds table.
83
+ # NB: this can only ever be None in structure v1.
84
+ if bonds1 is None or not bonds1.size or bonds2 is None or not bonds2.size:
85
+ self.assertTrue(bonds1 is None or not bonds1.size, msg=f'{bonds1=}')
86
+ self.assertTrue(bonds2 is None or not bonds2.size, msg=f'{bonds2=}')
87
+ return
88
+
89
+ ptnr1_indices1, ptnr2_indices1 = bonds1.get_atom_indices(atom_key1)
90
+ ptnr1_indices2, ptnr2_indices2 = bonds2.get_atom_indices(atom_key2)
91
+ np.testing.assert_array_equal(ptnr1_indices1, ptnr1_indices2)
92
+ np.testing.assert_array_equal(ptnr2_indices1, ptnr2_indices2)
93
+ np.testing.assert_array_equal(bonds1.type, bonds2.type)
94
+ np.testing.assert_array_equal(bonds1.role, bonds2.role)
95
+
96
+ def assertStructuresEqual( # pylint: disable=invalid-name
97
+ self,
98
+ struc1,
99
+ struc2,
100
+ *,
101
+ ignore_fields=None,
102
+ allow_chem_comp_data_extension=False,
103
+ atol=0,
104
+ ):
105
+ """Checks whether two Structure objects could be considered equal.
106
+
107
+ Args:
108
+ struc1: First Structure object.
109
+ struc2: Second Structure object.
110
+ ignore_fields: Fields not taken into account during comparison.
111
+ allow_chem_comp_data_extension: Whether to allow data of `_chem_comp`
112
+ table to differ if `struc2` is missing some fields, but `struc1` has
113
+ specific values for them.
114
+ atol: Absolute tolerance for floating point comparisons (in
115
+ np.testing.assert_allclose).
116
+ """
117
+ for field in sorted(structure.GLOBAL_FIELDS):
118
+ if ignore_fields and field in ignore_fields:
119
+ continue
120
+ if field == 'author_naming_scheme':
121
+ self.assertAuthorNamingSchemeEqual(struc1[field], struc2[field])
122
+ elif field == 'all_residues':
123
+ self.assertAllResiduesEqual(struc1[field], struc2[field])
124
+ elif field == 'bioassembly_data':
125
+ self.assertBioassemblyDataEqual(struc1[field], struc2[field])
126
+ elif field == 'chemical_components_data':
127
+ self.assertChemicalComponentsDataEqual(
128
+ struc1[field], struc2[field], allow_chem_comp_data_extension
129
+ )
130
+ elif field == 'bonds':
131
+ self.assertBondsEqual(
132
+ struc1.bonds, struc2.bonds, struc1.atom_key, struc2.atom_key
133
+ )
134
+ else:
135
+ self.assertEqual(struc1[field], struc2[field], msg=field)
136
+
137
+ # The chain order within a structure is arbitrary so in order to
138
+ # directly compare arrays we first align struc1 to struc2 and check that
139
+ # the number of atoms doesn't change.
140
+ num_atoms = struc1.num_atoms
141
+ self.assertEqual(struc2.num_atoms, num_atoms)
142
+ struc1 = struc1.order_and_drop_atoms_to_match(struc2)
143
+ self.assertEqual(struc1.num_atoms, num_atoms)
144
+
145
+ for field in sorted(structure.ARRAY_FIELDS):
146
+ if field == 'atom_key':
147
+ # atom_key has no external meaning, so it doesn't matter whether it
148
+ # differs between two structures.
149
+ continue
150
+ if ignore_fields and field in ignore_fields:
151
+ continue
152
+ self.assertEqual(struc1[field] is None, struc2[field] is None, msg=field)
153
+
154
+ if np.issubdtype(struc1[field].dtype, np.inexact):
155
+ np.testing.assert_allclose(
156
+ struc1[field], struc2[field], err_msg=field, atol=atol
157
+ )
158
+ else:
159
+ np.testing.assert_array_equal(
160
+ struc1[field], struc2[field], err_msg=field
161
+ )
flax_model/alphafold3/test_data/__init__.py ADDED
File without changes
flax_model/alphafold3/test_data/featurised_example.json ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "aatype": "46551530df4e79371352989100ae6af82ccd939009517b788173bdbb323ae40a",
4
+ "asym_id": "42c714885d17f6c0fcfebebd171b5ed7d7e77a56cf9c4983d9ff9ff90995bd1c",
5
+ "cleaned_struc": "<<structure>>",
6
+ "deletion_matrix": "54f980b5b3be8ce80cb6490c527e38d681deade50f239f2cb7d23cf9d0108c37",
7
+ "deletion_mean": "655a3ef0465a9f30fddf25f4dde0c19a05c6f9069b83961800c1944165955273",
8
+ "empty_output_struc": "<<structure>>",
9
+ "entity_id": "42c714885d17f6c0fcfebebd171b5ed7d7e77a56cf9c4983d9ff9ff90995bd1c",
10
+ "flat_output_layout": "<<atom-layout>>",
11
+ "frames_mask": "e8a48532fba32c318f7d809fae3ea0999baa01376b29d94ef3c27d82f12f6a90",
12
+ "is_dna": "1d83518b897b14e2943990eff655838246cc0207a7c95a5f3dfccc2e395f8bbf",
13
+ "is_ligand": "cfa4e080dc2885de94224db5075c2feb0805567d31e8898abdebaebc6646ce12",
14
+ "is_nonstandard_polymer_chain": "1d83518b897b14e2943990eff655838246cc0207a7c95a5f3dfccc2e395f8bbf",
15
+ "is_protein": "569b0e93b5f77c1a9d430b643f29e600c7db6f44ded72b96ab1dd25351d26075",
16
+ "is_rna": "1d83518b897b14e2943990eff655838246cc0207a7c95a5f3dfccc2e395f8bbf",
17
+ "is_water": "1d83518b897b14e2943990eff655838246cc0207a7c95a5f3dfccc2e395f8bbf",
18
+ "ligand_ligand_bonds": "<<None>>",
19
+ "msa": "871c93669d8a7dd046f8ba6a1ea7b9fc52e1a2e040c8d9bdc4ae73a77f95d3f7",
20
+ "msa_mask": "13a46a8a2c5305d2a07bb9587ad092d97749d2b2a2d10ad5d64474a4f0f4c803",
21
+ "num_alignments": "26b25d457597a7b0463f9620f666dd10aa2c4373a505967c7c8d70922a2d6ece",
22
+ "polymer_ligand_bonds": "<<None>>",
23
+ "pred_dense_atom_mask": "1e388e40167a4d31291903d74c98544868d0dc574706cdfd259ddd65f56d2993",
24
+ "profile": "6ec7da33d5f9801357ca6c82725416ef59025c7a8810bfd67fed2262845b480b",
25
+ "queries_to_keys:gather_idxs": "e0a2fd867fbae573b5613a98ee037eeb575cc5aa3285db2fe5158cda0e8c49cd",
26
+ "queries_to_keys:gather_mask": "7ddcbfda2904f013d9ab73c55517ac933ecb3dbfbfab9bb351ee43b5fd66e54e",
27
+ "queries_to_keys:input_shape": "cf801b14d3da833c486e3d73e355c3c2068523012b42c2b17decc45dbd8b9972",
28
+ "queries_to_token_atoms:gather_idxs": "3c2d0028564167c93b2f33e877974d45756c1e2e8934270c024fc5f1e85fcf14",
29
+ "queries_to_token_atoms:gather_mask": "1e388e40167a4d31291903d74c98544868d0dc574706cdfd259ddd65f56d2993",
30
+ "queries_to_token_atoms:input_shape": "cf801b14d3da833c486e3d73e355c3c2068523012b42c2b17decc45dbd8b9972",
31
+ "ref_atom_name_chars": "043f3242ff4dff04fd36699ccefdff718c57c325f72c71f33f4c4774e75a64dc",
32
+ "ref_charge": "27898ee238963105942db19f27a980c1fe4f7aeb396c1e5ea4e82da3876deef4",
33
+ "ref_element": "cc437747adeea094daf8a0e551be02f406c7e7d9b0a9328a84a59d907c5492b5",
34
+ "ref_mask": "1e388e40167a4d31291903d74c98544868d0dc574706cdfd259ddd65f56d2993",
35
+ "ref_space_uid": "97afd7d9d3ba835731457ef090ba4939d707bb2a270d48fde56369bde8e58e9f",
36
+ "residue_center_index": "dfef3704f73a7ee9a3be86b74e25ecd43e056c76574bc9bb65b6b446eeed87a7",
37
+ "residue_index": "6bf4ea803d0fcd31446e07f6ba9e012f794ced1fe5ad60b7181f66bb92601638",
38
+ "seq_length": "5a667734987b65ef5c4d07bf176148504bcf8378a5a1a0e23a7fa2c1121cc143",
39
+ "seq_mask": "a795c048f28bc4307f8599dc5140fe6e971bce6a34a0349b6c18d6cbb197f75f",
40
+ "sym_id": "5f170600b904a3464c72817993120957c1ce2eec8c2421af02d4912b5e8d8372",
41
+ "template_aatype": "6c5c3e7a5e23f064f1e8791f61bde8fb59ccf837095144b29e6c8e1607081398",
42
+ "template_atom_mask": "8a282b7476e13f6de5afbb52c14ea7baa3434281ed77b542bb790287e8c92e82",
43
+ "template_atom_positions": "b032ac0c813748a81b8d50ce94487c66510a177ad25e84581c7ea5f4b524987f",
44
+ "token_atoms_layout": "<<atom-layout>>",
45
+ "token_atoms_to_polymer_ligand_bonds:gather_idxs": "a0ee989ed2a0a2e3626520afa4032e06144865c8c8f6357293c9f4cd2069eaf2",
46
+ "token_atoms_to_polymer_ligand_bonds:gather_mask": "d13d4a8b3b8add19b5970157f09d00c12cbda4fed4d74d8493156523f7069b66",
47
+ "token_atoms_to_polymer_ligand_bonds:input_shape": "6ed5c8b9711af5b9c7c3d98ba048bed4dfe5974be758f1a632abd746b8278ed2",
48
+ "token_atoms_to_pseudo_beta:gather_idxs": "3fd8de4dd02450c6e249f33de9c6573694f7991e6a36528d6f32b6c6334a4d4f",
49
+ "token_atoms_to_pseudo_beta:gather_mask": "a795c048f28bc4307f8599dc5140fe6e971bce6a34a0349b6c18d6cbb197f75f",
50
+ "token_atoms_to_pseudo_beta:input_shape": "6ed5c8b9711af5b9c7c3d98ba048bed4dfe5974be758f1a632abd746b8278ed2",
51
+ "token_atoms_to_queries:gather_idxs": "50a1708678bb3d848be6fb49816196b13fcac36891817dac585f4a7556a8310e",
52
+ "token_atoms_to_queries:gather_mask": "3f4ac4f311cea0c7f6b8464e225815b791a767c9a8d15f9c7fb488c49f674fa9",
53
+ "token_atoms_to_queries:input_shape": "6ed5c8b9711af5b9c7c3d98ba048bed4dfe5974be758f1a632abd746b8278ed2",
54
+ "token_index": "77627a5d3856b90a27ae1f5bf5aa14754743c03c00a97bab68f4e7961a405454",
55
+ "tokens_to_keys:gather_idxs": "bc16a2a2b81bdbb3fb3ff180d984704b201866a7cdc373fb345a9be417aa246e",
56
+ "tokens_to_keys:gather_mask": "7ddcbfda2904f013d9ab73c55517ac933ecb3dbfbfab9bb351ee43b5fd66e54e",
57
+ "tokens_to_keys:input_shape": "5545412ee9a21a04712910d53fc1447c73ff94a29ddd1268131b4ed30d1dbe07",
58
+ "tokens_to_ligand_ligand_bonds:gather_idxs": "bac3003591bd8ef44d85b82cd4d23191d48496c1992e61d3fca2a37e6ddd34ea",
59
+ "tokens_to_ligand_ligand_bonds:gather_mask": "4d33ec9784aaf54079b4445118c1be26b4a5555e1d6034ccc4182defd86f2068",
60
+ "tokens_to_ligand_ligand_bonds:input_shape": "5545412ee9a21a04712910d53fc1447c73ff94a29ddd1268131b4ed30d1dbe07",
61
+ "tokens_to_polymer_ligand_bonds:gather_idxs": "a0ee989ed2a0a2e3626520afa4032e06144865c8c8f6357293c9f4cd2069eaf2",
62
+ "tokens_to_polymer_ligand_bonds:gather_mask": "d13d4a8b3b8add19b5970157f09d00c12cbda4fed4d74d8493156523f7069b66",
63
+ "tokens_to_polymer_ligand_bonds:input_shape": "5545412ee9a21a04712910d53fc1447c73ff94a29ddd1268131b4ed30d1dbe07",
64
+ "tokens_to_queries:gather_idxs": "f82b61cc7f007a744fc9186107647ff333b598642a681b046d61aef4994931e1",
65
+ "tokens_to_queries:gather_mask": "3f4ac4f311cea0c7f6b8464e225815b791a767c9a8d15f9c7fb488c49f674fa9",
66
+ "tokens_to_queries:input_shape": "5545412ee9a21a04712910d53fc1447c73ff94a29ddd1268131b4ed30d1dbe07"
67
+ }
68
+ ]
flax_model/alphafold3/test_data/miniature_databases/__init__.py ADDED
File without changes
flax_model/alphafold3/test_data/miniature_databases/bfd-first_non_consensus_sequences__subsampled_1000.fasta ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/mgy_clusters__subsampled_1000.fa ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/nt_rna_2023_02_23_clust_seq_id_90_cov_80_rep_seq__subsampled_1000.fasta ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/5y2e.cif ADDED
@@ -0,0 +1,562 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ data_5Y2E
2
+ #
3
+ _entry.id 5Y2E
4
+ #
5
+ loop_
6
+ _chem_comp.formula
7
+ _chem_comp.formula_weight
8
+ _chem_comp.id
9
+ _chem_comp.mon_nstd_flag
10
+ _chem_comp.name
11
+ _chem_comp.pdbx_synonyms
12
+ _chem_comp.type
13
+ "C3 H7 N O2" 89.093 ALA y ALANINE ? "L-peptide linking"
14
+ "C6 H15 N4 O2 1" 175.209 ARG y ARGININE ? "L-peptide linking"
15
+ "C4 H7 N O4" 133.103 ASP y "ASPARTIC ACID" ? "L-peptide linking"
16
+ "C5 H10 N2 O3" 146.144 GLN y GLUTAMINE ? "L-peptide linking"
17
+ "C5 H9 N O4" 147.129 GLU y "GLUTAMIC ACID" ? "L-peptide linking"
18
+ "C2 H5 N O2" 75.067 GLY y GLYCINE ? "peptide linking"
19
+ "C6 H10 N3 O2 1" 156.162 HIS y HISTIDINE ? "L-peptide linking"
20
+ "H2 O" 18.015 HOH . WATER ? non-polymer
21
+ "C6 H13 N O2" 131.173 ILE y ISOLEUCINE ? "L-peptide linking"
22
+ "C6 H13 N O2" 131.173 LEU y LEUCINE ? "L-peptide linking"
23
+ "C6 H15 N2 O2 1" 147.195 LYS y LYSINE ? "L-peptide linking"
24
+ "C5 H11 N O2 S" 149.211 MET y METHIONINE ? "L-peptide linking"
25
+ "C4 H9 N O3" 119.119 THR y THREONINE ? "L-peptide linking"
26
+ "C5 H11 N O2" 117.146 VAL y VALINE ? "L-peptide linking"
27
+ #
28
+ _entity.id 1
29
+ _entity.pdbx_description "Non-structural glycoprotein 4"
30
+ _entity.type polymer
31
+ #
32
+ _entity_poly.entity_id 1
33
+ _entity_poly.pdbx_strand_id A
34
+ _entity_poly.type polypeptide(L)
35
+ #
36
+ loop_
37
+ _entity_poly_seq.entity_id
38
+ _entity_poly_seq.hetero
39
+ _entity_poly_seq.mon_id
40
+ _entity_poly_seq.num
41
+ 1 n MET 1
42
+ 1 n ILE 2
43
+ 1 n GLU 3
44
+ 1 n LYS 4
45
+ 1 n GLN 5
46
+ 1 n MET 6
47
+ 1 n ASP 7
48
+ 1 n ARG 8
49
+ 1 n VAL 9
50
+ 1 n VAL 10
51
+ 1 n LYS 11
52
+ 1 n GLU 12
53
+ 1 n MET 13
54
+ 1 n ARG 14
55
+ 1 n ARG 15
56
+ 1 n GLN 16
57
+ 1 n LEU 17
58
+ 1 n GLU 18
59
+ 1 n MET 19
60
+ 1 n ILE 20
61
+ 1 n ASP 21
62
+ 1 n LYS 22
63
+ 1 n LEU 23
64
+ 1 n THR 24
65
+ 1 n THR 25
66
+ 1 n ARG 26
67
+ 1 n GLY 27
68
+ 1 n ILE 28
69
+ 1 n GLU 29
70
+ 1 n GLN 30
71
+ 1 n VAL 31
72
+ 1 n GLU 32
73
+ 1 n LEU 33
74
+ 1 n LEU 34
75
+ 1 n LYS 35
76
+ 1 n ARG 36
77
+ 1 n ILE 37
78
+ 1 n HIS 38
79
+ 1 n ASP 39
80
+ 1 n LYS 40
81
+ 1 n LEU 41
82
+ 1 n MET 42
83
+ 1 n ILE 43
84
+ 1 n ARG 44
85
+ 1 n ALA 45
86
+ 1 n VAL 46
87
+ 1 n ASP 47
88
+ #
89
+ _exptl.method "X-RAY DIFFRACTION"
90
+ #
91
+ _pdbx_audit_revision_history.revision_date 2018-03-14
92
+ #
93
+ _pdbx_database_status.recvd_initial_deposition_date 2018-03-14
94
+ #
95
+ loop_
96
+ _pdbx_poly_seq_scheme.asym_id
97
+ _pdbx_poly_seq_scheme.auth_seq_num
98
+ _pdbx_poly_seq_scheme.entity_id
99
+ _pdbx_poly_seq_scheme.hetero
100
+ _pdbx_poly_seq_scheme.mon_id
101
+ _pdbx_poly_seq_scheme.pdb_ins_code
102
+ _pdbx_poly_seq_scheme.pdb_seq_num
103
+ _pdbx_poly_seq_scheme.pdb_strand_id
104
+ _pdbx_poly_seq_scheme.seq_id
105
+ B 94 1 n MET . 94 A 1
106
+ B 95 1 n ILE . 95 A 2
107
+ B 96 1 n GLU . 96 A 3
108
+ B 97 1 n LYS . 97 A 4
109
+ B 98 1 n GLN . 98 A 5
110
+ B 99 1 n MET . 99 A 6
111
+ B 100 1 n ASP . 100 A 7
112
+ B 101 1 n ARG . 101 A 8
113
+ B 102 1 n VAL . 102 A 9
114
+ B 103 1 n VAL . 103 A 10
115
+ B 104 1 n LYS . 104 A 11
116
+ B 105 1 n GLU . 105 A 12
117
+ B 106 1 n MET . 106 A 13
118
+ B 107 1 n ARG . 107 A 14
119
+ B 108 1 n ARG . 108 A 15
120
+ B 109 1 n GLN . 109 A 16
121
+ B 110 1 n LEU . 110 A 17
122
+ B 111 1 n GLU . 111 A 18
123
+ B 112 1 n MET . 112 A 19
124
+ B 113 1 n ILE . 113 A 20
125
+ B 114 1 n ASP . 114 A 21
126
+ B 115 1 n LYS . 115 A 22
127
+ B 116 1 n LEU . 116 A 23
128
+ B 117 1 n THR . 117 A 24
129
+ B 118 1 n THR . 118 A 25
130
+ B 119 1 n ARG . 119 A 26
131
+ B 120 1 n GLY . 120 A 27
132
+ B 121 1 n ILE . 121 A 28
133
+ B 122 1 n GLU . 122 A 29
134
+ B 123 1 n GLN . 123 A 30
135
+ B 124 1 n VAL . 124 A 31
136
+ B 125 1 n GLU . 125 A 32
137
+ B 126 1 n LEU . 126 A 33
138
+ B 127 1 n LEU . 127 A 34
139
+ B 128 1 n LYS . 128 A 35
140
+ B 129 1 n ARG . 129 A 36
141
+ B 130 1 n ILE . 130 A 37
142
+ B 131 1 n HIS . 131 A 38
143
+ B 132 1 n ASP . 132 A 39
144
+ B 133 1 n LYS . 133 A 40
145
+ B 134 1 n LEU . 134 A 41
146
+ B 135 1 n MET . 135 A 42
147
+ B 136 1 n ILE . 136 A 43
148
+ B 137 1 n ARG . 137 A 44
149
+ B 138 1 n ALA . 138 A 45
150
+ B ? 1 n VAL . 139 A 46
151
+ B ? 1 n ASP . 140 A 47
152
+ #
153
+ _pdbx_struct_assembly.details author_and_software_defined_assembly
154
+ _pdbx_struct_assembly.id 1
155
+ _pdbx_struct_assembly.method_details PISA
156
+ _pdbx_struct_assembly.oligomeric_count 4
157
+ _pdbx_struct_assembly.oligomeric_details tetrameric
158
+ #
159
+ _pdbx_struct_assembly_gen.assembly_id 1
160
+ _pdbx_struct_assembly_gen.asym_id_list A,B,C,D,E,F,G
161
+ _pdbx_struct_assembly_gen.oper_expression 1
162
+ #
163
+ _pdbx_struct_oper_list.id 1
164
+ _pdbx_struct_oper_list.matrix[1][1] 1.0000000000
165
+ _pdbx_struct_oper_list.matrix[1][2] 0.0000000000
166
+ _pdbx_struct_oper_list.matrix[1][3] 0.0000000000
167
+ _pdbx_struct_oper_list.matrix[2][1] 0.0000000000
168
+ _pdbx_struct_oper_list.matrix[2][2] 1.0000000000
169
+ _pdbx_struct_oper_list.matrix[2][3] 0.0000000000
170
+ _pdbx_struct_oper_list.matrix[3][1] 0.0000000000
171
+ _pdbx_struct_oper_list.matrix[3][2] 0.0000000000
172
+ _pdbx_struct_oper_list.matrix[3][3] 1.0000000000
173
+ _pdbx_struct_oper_list.name 1_555
174
+ _pdbx_struct_oper_list.symmetry_operation x,y,z
175
+ _pdbx_struct_oper_list.type "identity operation"
176
+ _pdbx_struct_oper_list.vector[1] 0.0000000000
177
+ _pdbx_struct_oper_list.vector[2] 0.0000000000
178
+ _pdbx_struct_oper_list.vector[3] 0.0000000000
179
+ #
180
+ _refine.ls_d_res_high 2.70
181
+ #
182
+ _software.classification other
183
+ _software.name "DeepMind Structure Class"
184
+ _software.pdbx_ordinal 1
185
+ _software.version 2.0.0
186
+ #
187
+ _struct_asym.entity_id 1
188
+ _struct_asym.id B
189
+ #
190
+ loop_
191
+ _atom_site.group_PDB
192
+ _atom_site.id
193
+ _atom_site.type_symbol
194
+ _atom_site.label_atom_id
195
+ _atom_site.label_alt_id
196
+ _atom_site.label_comp_id
197
+ _atom_site.label_asym_id
198
+ _atom_site.label_entity_id
199
+ _atom_site.label_seq_id
200
+ _atom_site.pdbx_PDB_ins_code
201
+ _atom_site.Cartn_x
202
+ _atom_site.Cartn_y
203
+ _atom_site.Cartn_z
204
+ _atom_site.occupancy
205
+ _atom_site.B_iso_or_equiv
206
+ _atom_site.auth_seq_id
207
+ _atom_site.auth_asym_id
208
+ _atom_site.pdbx_PDB_model_num
209
+ ATOM 1 N N . MET B 1 1 ? 2.644 6.839 -46.028 1.00 83.71 94 A 1
210
+ ATOM 2 C CA . MET B 1 1 ? 1.400 7.612 -45.825 1.00 86.32 94 A 1
211
+ ATOM 3 C C . MET B 1 1 ? 1.210 7.971 -44.344 1.00 87.15 94 A 1
212
+ ATOM 4 O O . MET B 1 1 ? 1.479 7.188 -43.421 1.00 80.48 94 A 1
213
+ ATOM 5 C CB . MET B 1 1 ? 0.170 6.890 -46.382 1.00 88.07 94 A 1
214
+ ATOM 6 C CG . MET B 1 1 ? -0.124 7.275 -47.840 1.00 91.84 94 A 1
215
+ ATOM 7 S SD . MET B 1 1 ? -1.789 6.807 -48.325 1.00 103.69 94 A 1
216
+ ATOM 8 C CE . MET B 1 1 ? -2.264 8.027 -49.569 1.00 97.89 94 A 1
217
+ ATOM 9 N N . ILE B 1 2 ? 0.696 9.166 -44.185 1.00 87.93 95 A 1
218
+ ATOM 10 C CA . ILE B 1 2 ? 0.364 9.779 -42.948 1.00 84.67 95 A 1
219
+ ATOM 11 C C . ILE B 1 2 ? -0.678 8.946 -42.258 1.00 75.98 95 A 1
220
+ ATOM 12 O O . ILE B 1 2 ? -0.634 8.714 -41.080 1.00 67.98 95 A 1
221
+ ATOM 13 C CB . ILE B 1 2 ? -0.321 11.082 -43.309 1.00 89.99 95 A 1
222
+ ATOM 14 C CG1 . ILE B 1 2 ? 0.696 12.146 -43.577 1.00 94.59 95 A 1
223
+ ATOM 15 C CG2 . ILE B 1 2 ? -1.197 11.546 -42.182 1.00 92.79 95 A 1
224
+ ATOM 16 C CD1 . ILE B 1 2 ? 1.132 12.780 -42.294 1.00 93.36 95 A 1
225
+ ATOM 17 N N . GLU B 1 3 ? -1.625 8.499 -43.046 1.00 71.66 96 A 1
226
+ ATOM 18 C CA . GLU B 1 3 ? -2.745 7.716 -42.585 1.00 68.44 96 A 1
227
+ ATOM 19 C C . GLU B 1 3 ? -2.273 6.463 -41.883 1.00 69.98 96 A 1
228
+ ATOM 20 O O . GLU B 1 3 ? -2.890 6.024 -40.946 1.00 67.88 96 A 1
229
+ ATOM 21 C CB . GLU B 1 3 ? -3.613 7.332 -43.745 1.00 73.44 96 A 1
230
+ ATOM 22 C CG . GLU B 1 3 ? -4.382 8.485 -44.320 1.00 87.42 96 A 1
231
+ ATOM 23 C CD . GLU B 1 3 ? -3.735 9.039 -45.551 1.00 89.38 96 A 1
232
+ ATOM 24 O OE1 . GLU B 1 3 ? -4.454 9.413 -46.477 1.00 97.81 96 A 1
233
+ ATOM 25 O OE2 . GLU B 1 3 ? -2.511 9.105 -45.593 1.00 88.14 96 A 1
234
+ ATOM 26 N N . LYS B 1 4 ? -1.212 5.844 -42.364 1.00 71.45 97 A 1
235
+ ATOM 27 C CA . LYS B 1 4 ? -0.676 4.669 -41.671 1.00 72.76 97 A 1
236
+ ATOM 28 C C . LYS B 1 4 ? 0.097 5.062 -40.408 1.00 74.11 97 A 1
237
+ ATOM 29 O O . LYS B 1 4 ? 0.209 4.268 -39.462 1.00 77.84 97 A 1
238
+ ATOM 30 C CB . LYS B 1 4 ? 0.197 3.807 -42.578 1.00 66.63 97 A 1
239
+ ATOM 31 N N . GLN B 1 5 ? 0.624 6.270 -40.364 1.00 72.03 98 A 1
240
+ ATOM 32 C CA . GLN B 1 5 ? 1.295 6.749 -39.158 1.00 65.60 98 A 1
241
+ ATOM 33 C C . GLN B 1 5 ? 0.255 7.097 -38.099 1.00 58.64 98 A 1
242
+ ATOM 34 O O . GLN B 1 5 ? 0.462 6.888 -36.942 1.00 52.26 98 A 1
243
+ ATOM 35 C CB . GLN B 1 5 ? 2.143 7.976 -39.487 1.00 63.76 98 A 1
244
+ ATOM 36 N N . MET B 1 6 ? -0.861 7.648 -38.542 1.00 49.04 99 A 1
245
+ ATOM 37 C CA . MET B 1 6 ? -1.953 7.954 -37.679 1.00 45.66 99 A 1
246
+ ATOM 38 C C . MET B 1 6 ? -2.486 6.679 -37.100 1.00 50.63 99 A 1
247
+ ATOM 39 O O . MET B 1 6 ? -2.882 6.643 -35.948 1.00 50.11 99 A 1
248
+ ATOM 40 C CB . MET B 1 6 ? -3.050 8.640 -38.449 1.00 42.97 99 A 1
249
+ ATOM 41 C CG . MET B 1 6 ? -4.395 8.703 -37.753 1.00 42.67 99 A 1
250
+ ATOM 42 S SD . MET B 1 6 ? -5.430 9.916 -38.524 1.00 47.21 99 A 1
251
+ ATOM 43 C CE . MET B 1 6 ? -6.803 9.938 -37.397 1.00 48.85 99 A 1
252
+ ATOM 44 N N . ASP B 1 7 ? -2.531 5.622 -37.894 1.00 56.67 100 A 1
253
+ ATOM 45 C CA . ASP B 1 7 ? -3.070 4.383 -37.401 1.00 55.17 100 A 1
254
+ ATOM 46 C C . ASP B 1 7 ? -2.223 3.846 -36.257 1.00 53.54 100 A 1
255
+ ATOM 47 O O . ASP B 1 7 ? -2.785 3.365 -35.256 1.00 47.69 100 A 1
256
+ ATOM 48 C CB . ASP B 1 7 ? -3.067 3.354 -38.520 1.00 57.94 100 A 1
257
+ ATOM 49 C CG . ASP B 1 7 ? -3.936 2.164 -38.236 1.00 62.42 100 A 1
258
+ ATOM 50 O OD1 . ASP B 1 7 ? -5.111 2.416 -37.949 1.00 67.59 100 A 1
259
+ ATOM 51 O OD2 . ASP B 1 7 ? -3.401 1.020 -38.206 1.00 61.64 100 A 1
260
+ ATOM 52 N N . ARG B 1 8 ? -0.900 3.909 -36.428 1.00 54.78 101 A 1
261
+ ATOM 53 C CA . ARG B 1 8 ? -0.002 3.384 -35.432 1.00 61.23 101 A 1
262
+ ATOM 54 C C . ARG B 1 8 ? -0.168 4.177 -34.127 1.00 63.20 101 A 1
263
+ ATOM 55 O O . ARG B 1 8 ? -0.017 3.620 -33.077 1.00 67.94 101 A 1
264
+ ATOM 56 C CB . ARG B 1 8 ? 1.484 3.327 -35.943 1.00 64.73 101 A 1
265
+ ATOM 57 C CG . ARG B 1 8 ? 2.403 4.541 -35.635 1.00 73.16 101 A 1
266
+ ATOM 58 N N . VAL B 1 9 ? -0.406 5.483 -34.222 1.00 63.14 102 A 1
267
+ ATOM 59 C CA . VAL B 1 9 ? -0.546 6.334 -33.061 1.00 59.81 102 A 1
268
+ ATOM 60 C C . VAL B 1 9 ? -1.853 6.121 -32.347 1.00 55.82 102 A 1
269
+ ATOM 61 O O . VAL B 1 9 ? -1.880 6.066 -31.150 1.00 48.46 102 A 1
270
+ ATOM 62 C CB . VAL B 1 9 ? -0.421 7.813 -33.454 1.00 60.39 102 A 1
271
+ ATOM 63 C CG1 . VAL B 1 9 ? -0.731 8.703 -32.272 1.00 60.03 102 A 1
272
+ ATOM 64 C CG2 . VAL B 1 9 ? 0.994 8.091 -33.943 1.00 61.68 102 A 1
273
+ ATOM 65 N N . VAL B 1 10 ? -2.941 6.014 -33.079 1.00 56.18 103 A 1
274
+ ATOM 66 C CA . VAL B 1 10 ? -4.231 5.743 -32.481 1.00 53.53 103 A 1
275
+ ATOM 67 C C . VAL B 1 10 ? -4.236 4.419 -31.704 1.00 52.90 103 A 1
276
+ ATOM 68 O O . VAL B 1 10 ? -4.772 4.355 -30.607 1.00 49.99 103 A 1
277
+ ATOM 69 C CB . VAL B 1 10 ? -5.317 5.742 -33.553 1.00 51.75 103 A 1
278
+ ATOM 70 C CG1 . VAL B 1 10 ? -6.606 5.137 -33.045 1.00 52.18 103 A 1
279
+ ATOM 71 C CG2 . VAL B 1 10 ? -5.610 7.153 -33.983 1.00 52.67 103 A 1
280
+ ATOM 72 N N . LYS B 1 11 ? -3.601 3.402 -32.243 1.00 55.55 104 A 1
281
+ ATOM 73 C CA . LYS B 1 11 ? -3.521 2.104 -31.626 1.00 56.87 104 A 1
282
+ ATOM 74 C C . LYS B 1 11 ? -2.709 2.164 -30.350 1.00 60.76 104 A 1
283
+ ATOM 75 O O . LYS B 1 11 ? -2.986 1.464 -29.399 1.00 58.80 104 A 1
284
+ ATOM 76 C CB . LYS B 1 11 ? -2.837 1.155 -32.582 1.00 60.72 104 A 1
285
+ ATOM 77 C CG . LYS B 1 11 ? -3.729 0.487 -33.601 1.00 72.80 104 A 1
286
+ ATOM 78 C CD . LYS B 1 11 ? -2.915 -0.472 -34.451 1.00 79.85 104 A 1
287
+ ATOM 79 C CE . LYS B 1 11 ? -3.664 -0.963 -35.666 1.00 84.90 104 A 1
288
+ ATOM 80 N NZ . LYS B 1 11 ? -5.110 -0.693 -35.513 1.00 94.50 104 A 1
289
+ ATOM 81 N N . GLU B 1 12 ? -1.674 2.982 -30.369 1.00 56.70 105 A 1
290
+ ATOM 82 C CA . GLU B 1 12 ? -0.798 3.204 -29.249 1.00 54.71 105 A 1
291
+ ATOM 83 C C . GLU B 1 12 ? -1.507 3.946 -28.155 1.00 52.45 105 A 1
292
+ ATOM 84 O O . GLU B 1 12 ? -1.387 3.608 -27.007 1.00 51.88 105 A 1
293
+ ATOM 85 C CB . GLU B 1 12 ? 0.404 4.036 -29.667 1.00 60.63 105 A 1
294
+ ATOM 86 C CG . GLU B 1 12 ? 1.418 4.261 -28.569 1.00 71.75 105 A 1
295
+ ATOM 87 C CD . GLU B 1 12 ? 2.100 2.985 -28.105 1.00 83.66 105 A 1
296
+ ATOM 88 O OE1 . GLU B 1 12 ? 1.973 1.951 -28.793 1.00 97.71 105 A 1
297
+ ATOM 89 O OE2 . GLU B 1 12 ? 2.729 3.008 -27.039 1.00 91.62 105 A 1
298
+ ATOM 90 N N . MET B 1 13 ? -2.250 4.972 -28.495 1.00 50.99 106 A 1
299
+ ATOM 91 C CA . MET B 1 13 ? -2.993 5.703 -27.508 1.00 53.07 106 A 1
300
+ ATOM 92 C C . MET B 1 13 ? -4.045 4.874 -26.849 1.00 51.54 106 A 1
301
+ ATOM 93 O O . MET B 1 13 ? -4.220 4.974 -25.651 1.00 48.88 106 A 1
302
+ ATOM 94 C CB . MET B 1 13 ? -3.640 6.915 -28.107 1.00 61.60 106 A 1
303
+ ATOM 95 C CG . MET B 1 13 ? -2.651 8.032 -28.402 1.00 65.35 106 A 1
304
+ ATOM 96 S SD . MET B 1 13 ? -3.551 9.590 -28.498 1.00 76.88 106 A 1
305
+ ATOM 97 C CE . MET B 1 13 ? -3.620 9.931 -26.737 1.00 76.03 106 A 1
306
+ ATOM 98 N N . ARG B 1 14 ? -4.714 4.016 -27.571 1.00 53.52 107 A 1
307
+ ATOM 99 C CA . ARG B 1 14 ? -5.703 3.187 -26.952 1.00 50.53 107 A 1
308
+ ATOM 100 C C . ARG B 1 14 ? -5.040 2.268 -25.975 1.00 51.01 107 A 1
309
+ ATOM 101 O O . ARG B 1 14 ? -5.527 2.040 -24.903 1.00 48.89 107 A 1
310
+ ATOM 102 C CB . ARG B 1 14 ? -6.358 2.367 -28.011 1.00 54.79 107 A 1
311
+ ATOM 103 C CG . ARG B 1 14 ? -7.580 1.648 -27.566 1.00 60.71 107 A 1
312
+ ATOM 104 C CD . ARG B 1 14 ? -7.895 0.586 -28.570 1.00 70.83 107 A 1
313
+ ATOM 105 N NE . ARG B 1 14 ? -8.689 1.116 -29.658 1.00 78.79 107 A 1
314
+ ATOM 106 C CZ . ARG B 1 14 ? -8.278 1.182 -30.911 1.00 81.77 107 A 1
315
+ ATOM 107 N NH1 . ARG B 1 14 ? -7.086 0.746 -31.233 1.00 91.09 107 A 1
316
+ ATOM 108 N NH2 . ARG B 1 14 ? -9.071 1.675 -31.832 1.00 79.36 107 A 1
317
+ ATOM 109 N N . ARG B 1 15 ? -3.896 1.754 -26.355 1.00 46.73 108 A 1
318
+ ATOM 110 C CA . ARG B 1 15 ? -3.174 0.827 -25.534 1.00 45.05 108 A 1
319
+ ATOM 111 C C . ARG B 1 15 ? -2.681 1.503 -24.248 1.00 47.85 108 A 1
320
+ ATOM 112 O O . ARG B 1 15 ? -2.752 0.930 -23.189 1.00 50.40 108 A 1
321
+ ATOM 113 C CB . ARG B 1 15 ? -2.063 0.286 -26.404 1.00 50.23 108 A 1
322
+ ATOM 114 C CG . ARG B 1 15 ? -1.165 -0.750 -25.876 1.00 59.06 108 A 1
323
+ ATOM 115 C CD . ARG B 1 15 ? -0.358 -1.317 -27.046 1.00 59.59 108 A 1
324
+ ATOM 116 N NE . ARG B 1 15 ? 0.861 -1.991 -26.583 1.00 65.92 108 A 1
325
+ ATOM 117 N N . GLN B 1 16 ? -2.221 2.739 -24.349 1.00 49.74 109 A 1
326
+ ATOM 118 C CA . GLN B 1 16 ? -1.810 3.511 -23.193 1.00 47.85 109 A 1
327
+ ATOM 119 C C . GLN B 1 16 ? -2.993 3.774 -22.279 1.00 48.41 109 A 1
328
+ ATOM 120 O O . GLN B 1 16 ? -2.898 3.560 -21.091 1.00 46.33 109 A 1
329
+ ATOM 121 C CB . GLN B 1 16 ? -1.209 4.809 -23.611 1.00 49.18 109 A 1
330
+ ATOM 122 C CG . GLN B 1 16 ? 0.208 4.681 -24.112 1.00 53.75 109 A 1
331
+ ATOM 123 C CD . GLN B 1 16 ? 0.618 5.857 -24.974 1.00 61.24 109 A 1
332
+ ATOM 124 O OE1 . GLN B 1 16 ? -0.145 6.791 -25.159 1.00 69.70 109 A 1
333
+ ATOM 125 N NE2 . GLN B 1 16 ? 1.786 5.782 -25.546 1.00 63.00 109 A 1
334
+ ATOM 126 N N . LEU B 1 17 ? -4.098 4.266 -22.817 1.00 45.61 110 A 1
335
+ ATOM 127 C CA . LEU B 1 17 ? -5.278 4.506 -22.010 1.00 41.80 110 A 1
336
+ ATOM 128 C C . LEU B 1 17 ? -5.848 3.244 -21.329 1.00 44.01 110 A 1
337
+ ATOM 129 O O . LEU B 1 17 ? -6.378 3.314 -20.232 1.00 49.63 110 A 1
338
+ ATOM 130 C CB . LEU B 1 17 ? -6.380 5.133 -22.818 1.00 41.83 110 A 1
339
+ ATOM 131 C CG . LEU B 1 17 ? -6.545 6.610 -23.075 1.00 44.57 110 A 1
340
+ ATOM 132 C CD1 . LEU B 1 17 ? -6.481 7.448 -21.815 1.00 46.53 110 A 1
341
+ ATOM 133 C CD2 . LEU B 1 17 ? -5.534 7.076 -24.054 1.00 46.70 110 A 1
342
+ ATOM 134 N N . GLU B 1 18 ? -5.724 2.085 -21.974 1.00 48.92 111 A 1
343
+ ATOM 135 C CA . GLU B 1 18 ? -6.176 0.854 -21.354 1.00 50.45 111 A 1
344
+ ATOM 136 C C . GLU B 1 18 ? -5.278 0.446 -20.173 1.00 50.00 111 A 1
345
+ ATOM 137 O O . GLU B 1 18 ? -5.748 -0.088 -19.155 1.00 52.97 111 A 1
346
+ ATOM 138 C CB . GLU B 1 18 ? -6.361 -0.236 -22.405 1.00 54.05 111 A 1
347
+ ATOM 139 C CG . GLU B 1 18 ? -7.693 -0.095 -23.112 1.00 59.57 111 A 1
348
+ ATOM 140 C CD . GLU B 1 18 ? -7.804 -0.858 -24.438 1.00 68.03 111 A 1
349
+ ATOM 141 O OE1 . GLU B 1 18 ? -6.834 -1.521 -24.893 1.00 73.96 111 A 1
350
+ ATOM 142 O OE2 . GLU B 1 18 ? -8.896 -0.808 -25.069 1.00 73.24 111 A 1
351
+ ATOM 143 N N . MET B 1 19 ? -4.000 0.760 -20.284 1.00 48.79 112 A 1
352
+ ATOM 144 C CA . MET B 1 19 ? -3.094 0.512 -19.186 1.00 52.15 112 A 1
353
+ ATOM 145 C C . MET B 1 19 ? -3.371 1.430 -18.007 1.00 47.91 112 A 1
354
+ ATOM 146 O O . MET B 1 19 ? -3.429 0.986 -16.872 1.00 45.58 112 A 1
355
+ ATOM 147 C CB . MET B 1 19 ? -1.653 0.667 -19.617 1.00 54.11 112 A 1
356
+ ATOM 148 C CG . MET B 1 19 ? -1.053 -0.654 -20.052 1.00 58.96 112 A 1
357
+ ATOM 149 S SD . MET B 1 19 ? 0.471 -0.323 -20.923 1.00 65.09 112 A 1
358
+ ATOM 150 C CE . MET B 1 19 ? 1.501 -0.237 -19.466 1.00 67.56 112 A 1
359
+ ATOM 151 N N . ILE B 1 20 ? -3.541 2.711 -18.297 1.00 43.25 113 A 1
360
+ ATOM 152 C CA . ILE B 1 20 ? -3.900 3.677 -17.287 1.00 40.09 113 A 1
361
+ ATOM 153 C C . ILE B 1 20 ? -5.165 3.240 -16.596 1.00 40.28 113 A 1
362
+ ATOM 154 O O . ILE B 1 20 ? -5.297 3.383 -15.395 1.00 41.67 113 A 1
363
+ ATOM 155 C CB . ILE B 1 20 ? -4.079 5.077 -17.885 1.00 41.26 113 A 1
364
+ ATOM 156 C CG1 . ILE B 1 20 ? -2.700 5.633 -18.275 1.00 39.56 113 A 1
365
+ ATOM 157 C CG2 . ILE B 1 20 ? -4.715 6.029 -16.880 1.00 44.72 113 A 1
366
+ ATOM 158 C CD1 . ILE B 1 20 ? -2.722 6.963 -19.009 1.00 37.40 113 A 1
367
+ ATOM 159 N N . ASP B 1 21 ? -6.096 2.680 -17.346 1.00 41.39 114 A 1
368
+ ATOM 160 C CA . ASP B 1 21 ? -7.302 2.201 -16.728 1.00 46.35 114 A 1
369
+ ATOM 161 C C . ASP B 1 21 ? -7.074 1.044 -15.762 1.00 45.76 114 A 1
370
+ ATOM 162 O O . ASP B 1 21 ? -7.662 1.035 -14.682 1.00 43.56 114 A 1
371
+ ATOM 163 C CB . ASP B 1 21 ? -8.272 1.734 -17.759 1.00 51.71 114 A 1
372
+ ATOM 164 C CG . ASP B 1 21 ? -9.647 1.640 -17.208 1.00 60.74 114 A 1
373
+ ATOM 165 O OD1 . ASP B 1 21 ? -10.153 2.682 -16.742 1.00 68.50 114 A 1
374
+ ATOM 166 O OD2 . ASP B 1 21 ? -10.199 0.528 -17.210 1.00 73.36 114 A 1
375
+ ATOM 167 N N . LYS B 1 22 ? -6.258 0.070 -16.178 1.00 47.51 115 A 1
376
+ ATOM 168 C CA . LYS B 1 22 ? -5.942 -1.062 -15.321 1.00 50.59 115 A 1
377
+ ATOM 169 C C . LYS B 1 22 ? -5.255 -0.563 -14.028 1.00 52.11 115 A 1
378
+ ATOM 170 O O . LYS B 1 22 ? -5.648 -0.941 -12.919 1.00 55.27 115 A 1
379
+ ATOM 171 C CB . LYS B 1 22 ? -5.085 -2.111 -16.025 1.00 52.86 115 A 1
380
+ ATOM 172 C CG . LYS B 1 22 ? -5.829 -3.010 -17.008 1.00 57.80 115 A 1
381
+ ATOM 173 N N . LEU B 1 23 ? -4.266 0.312 -14.176 1.00 48.62 116 A 1
382
+ ATOM 174 C CA . LEU B 1 23 ? -3.594 0.880 -13.039 1.00 44.80 116 A 1
383
+ ATOM 175 C C . LEU B 1 23 ? -4.511 1.665 -12.106 1.00 44.04 116 A 1
384
+ ATOM 176 O O . LEU B 1 23 ? -4.412 1.565 -10.897 1.00 41.22 116 A 1
385
+ ATOM 177 C CB . LEU B 1 23 ? -2.492 1.781 -13.511 1.00 43.26 116 A 1
386
+ ATOM 178 C CG . LEU B 1 23 ? -1.353 1.039 -14.177 1.00 45.18 116 A 1
387
+ ATOM 179 C CD1 . LEU B 1 23 ? -0.449 2.064 -14.839 1.00 45.01 116 A 1
388
+ ATOM 180 C CD2 . LEU B 1 23 ? -0.582 0.210 -13.156 1.00 45.90 116 A 1
389
+ ATOM 181 N N . THR B 1 24 ? -5.402 2.445 -12.674 1.00 49.17 117 A 1
390
+ ATOM 182 C CA . THR B 1 24 ? -6.382 3.155 -11.886 1.00 49.60 117 A 1
391
+ ATOM 183 C C . THR B 1 24 ? -7.250 2.198 -11.085 1.00 51.60 117 A 1
392
+ ATOM 184 O O . THR B 1 24 ? -7.503 2.441 -9.918 1.00 50.82 117 A 1
393
+ ATOM 185 C CB . THR B 1 24 ? -7.248 4.024 -12.811 1.00 54.67 117 A 1
394
+ ATOM 186 O OG1 . THR B 1 24 ? -6.463 5.126 -13.253 1.00 58.01 117 A 1
395
+ ATOM 187 C CG2 . THR B 1 24 ? -8.464 4.559 -12.141 1.00 57.77 117 A 1
396
+ ATOM 188 N N . THR B 1 25 ? -7.718 1.116 -11.704 1.00 53.09 118 A 1
397
+ ATOM 189 C CA . THR B 1 25 ? -8.495 0.117 -10.981 1.00 50.55 118 A 1
398
+ ATOM 190 C C . THR B 1 25 ? -7.709 -0.503 -9.820 1.00 49.50 118 A 1
399
+ ATOM 191 O O . THR B 1 25 ? -8.267 -0.695 -8.720 1.00 50.25 118 A 1
400
+ ATOM 192 C CB . THR B 1 25 ? -8.933 -0.983 -11.947 1.00 53.52 118 A 1
401
+ ATOM 193 O OG1 . THR B 1 25 ? -9.796 -0.396 -12.916 1.00 60.17 118 A 1
402
+ ATOM 194 C CG2 . THR B 1 25 ? -9.619 -2.127 -11.260 1.00 58.18 118 A 1
403
+ ATOM 195 N N . ARG B 1 26 ? -6.447 -0.762 -10.054 1.00 48.62 119 A 1
404
+ ATOM 196 C CA . ARG B 1 26 ? -5.575 -1.319 -9.076 1.00 52.29 119 A 1
405
+ ATOM 197 C C . ARG B 1 26 ? -5.362 -0.349 -7.936 1.00 56.15 119 A 1
406
+ ATOM 198 O O . ARG B 1 26 ? -5.203 -0.758 -6.818 1.00 60.08 119 A 1
407
+ ATOM 199 C CB . ARG B 1 26 ? -4.262 -1.609 -9.738 1.00 57.25 119 A 1
408
+ ATOM 200 C CG . ARG B 1 26 ? -4.341 -2.608 -10.845 1.00 67.09 119 A 1
409
+ ATOM 201 C CD . ARG B 1 26 ? -4.671 -3.974 -10.307 1.00 78.77 119 A 1
410
+ ATOM 202 N NE . ARG B 1 26 ? -4.743 -4.923 -11.390 1.00 95.96 119 A 1
411
+ ATOM 203 C CZ . ARG B 1 26 ? -3.703 -5.558 -11.900 1.00 111.57 119 A 1
412
+ ATOM 204 N NH1 . ARG B 1 26 ? -3.881 -6.412 -12.892 1.00 120.54 119 A 1
413
+ ATOM 205 N NH2 . ARG B 1 26 ? -2.494 -5.350 -11.417 1.00 114.95 119 A 1
414
+ ATOM 206 N N . GLY B 1 27 ? -5.312 0.940 -8.241 1.00 57.29 120 A 1
415
+ ATOM 207 C CA . GLY B 1 27 ? -5.176 2.005 -7.265 1.00 55.03 120 A 1
416
+ ATOM 208 C C . GLY B 1 27 ? -6.391 2.105 -6.381 1.00 51.16 120 A 1
417
+ ATOM 209 O O . GLY B 1 27 ? -6.273 2.252 -5.174 1.00 48.31 120 A 1
418
+ ATOM 210 N N . ILE B 1 28 ? -7.570 1.983 -6.973 1.00 48.29 121 A 1
419
+ ATOM 211 C CA . ILE B 1 28 ? -8.807 2.040 -6.198 1.00 51.59 121 A 1
420
+ ATOM 212 C C . ILE B 1 28 ? -8.893 0.889 -5.227 1.00 54.18 121 A 1
421
+ ATOM 213 O O . ILE B 1 28 ? -9.376 1.068 -4.118 1.00 53.53 121 A 1
422
+ ATOM 214 C CB . ILE B 1 28 ? -10.053 2.141 -7.095 1.00 55.27 121 A 1
423
+ ATOM 215 C CG1 . ILE B 1 28 ? -10.187 3.601 -7.621 1.00 55.95 121 A 1
424
+ ATOM 216 C CG2 . ILE B 1 28 ? -11.306 1.807 -6.320 1.00 61.76 121 A 1
425
+ ATOM 217 C CD1 . ILE B 1 28 ? -11.126 3.822 -8.793 1.00 55.74 121 A 1
426
+ ATOM 218 N N . GLU B 1 29 ? -8.420 -0.273 -5.627 1.00 61.84 122 A 1
427
+ ATOM 219 C CA . GLU B 1 29 ? -8.319 -1.378 -4.700 1.00 67.68 122 A 1
428
+ ATOM 220 C C . GLU B 1 29 ? -7.415 -1.069 -3.537 1.00 65.03 122 A 1
429
+ ATOM 221 O O . GLU B 1 29 ? -7.791 -1.331 -2.393 1.00 64.70 122 A 1
430
+ ATOM 222 C CB . GLU B 1 29 ? -7.868 -2.634 -5.431 1.00 78.78 122 A 1
431
+ ATOM 223 C CG . GLU B 1 29 ? -9.069 -3.428 -5.962 1.00 86.76 122 A 1
432
+ ATOM 224 C CD . GLU B 1 29 ? -8.878 -4.019 -7.353 1.00 87.49 122 A 1
433
+ ATOM 225 O OE1 . GLU B 1 29 ? -7.768 -4.558 -7.636 1.00 84.73 122 A 1
434
+ ATOM 226 O OE2 . GLU B 1 29 ? -9.870 -3.959 -8.121 1.00 77.87 122 A 1
435
+ ATOM 227 N N . GLN B 1 30 ? -6.256 -0.484 -3.829 1.00 59.57 123 A 1
436
+ ATOM 228 C CA . GLN B 1 30 ? -5.315 -0.084 -2.773 1.00 55.23 123 A 1
437
+ ATOM 229 C C . GLN B 1 30 ? -5.910 0.955 -1.841 1.00 51.86 123 A 1
438
+ ATOM 230 O O . GLN B 1 30 ? -5.662 0.942 -0.653 1.00 44.12 123 A 1
439
+ ATOM 231 C CB . GLN B 1 30 ? -4.037 0.514 -3.362 1.00 55.92 123 A 1
440
+ ATOM 232 C CG . GLN B 1 30 ? -3.221 -0.371 -4.262 1.00 61.37 123 A 1
441
+ ATOM 233 C CD . GLN B 1 30 ? -2.863 -1.664 -3.614 1.00 62.35 123 A 1
442
+ ATOM 234 O OE1 . GLN B 1 30 ? -2.326 -1.663 -2.512 1.00 63.09 123 A 1
443
+ ATOM 235 N NE2 . GLN B 1 30 ? -3.188 -2.787 -4.276 1.00 70.63 123 A 1
444
+ ATOM 236 N N . VAL B 1 31 ? -6.703 1.858 -2.386 1.00 57.05 124 A 1
445
+ ATOM 237 C CA . VAL B 1 31 ? -7.323 2.883 -1.569 1.00 56.37 124 A 1
446
+ ATOM 238 C C . VAL B 1 31 ? -8.333 2.246 -0.629 1.00 61.68 124 A 1
447
+ ATOM 239 O O . VAL B 1 31 ? -8.461 2.699 0.470 1.00 58.51 124 A 1
448
+ ATOM 240 C CB . VAL B 1 31 ? -7.969 3.950 -2.456 1.00 57.06 124 A 1
449
+ ATOM 241 C CG1 . VAL B 1 31 ? -9.007 4.756 -1.702 1.00 58.79 124 A 1
450
+ ATOM 242 C CG2 . VAL B 1 31 ? -6.871 4.864 -2.974 1.00 58.78 124 A 1
451
+ ATOM 243 N N . GLU B 1 32 ? -9.044 1.205 -1.068 1.00 67.95 125 A 1
452
+ ATOM 244 C CA . GLU B 1 32 ? -9.940 0.481 -0.175 1.00 64.07 125 A 1
453
+ ATOM 245 C C . GLU B 1 32 ? -9.140 -0.246 0.887 1.00 61.43 125 A 1
454
+ ATOM 246 O O . GLU B 1 32 ? -9.521 -0.248 2.049 1.00 58.85 125 A 1
455
+ ATOM 247 C CB . GLU B 1 32 ? -10.821 -0.511 -0.911 1.00 70.57 125 A 1
456
+ ATOM 248 C CG . GLU B 1 32 ? -11.844 0.047 -1.875 1.00 81.60 125 A 1
457
+ ATOM 249 C CD . GLU B 1 32 ? -12.772 1.105 -1.306 1.00 92.01 125 A 1
458
+ ATOM 250 O OE1 . GLU B 1 32 ? -13.833 0.773 -0.713 1.00 101.96 125 A 1
459
+ ATOM 251 O OE2 . GLU B 1 32 ? -12.392 2.286 -1.482 1.00 95.64 125 A 1
460
+ ATOM 252 N N . LEU B 1 33 ? -8.042 -0.849 0.489 1.00 55.72 126 A 1
461
+ ATOM 253 C CA . LEU B 1 33 ? -7.201 -1.483 1.447 1.00 58.91 126 A 1
462
+ ATOM 254 C C . LEU B 1 33 ? -6.628 -0.487 2.476 1.00 56.58 126 A 1
463
+ ATOM 255 O O . LEU B 1 33 ? -6.271 -0.829 3.592 1.00 59.20 126 A 1
464
+ ATOM 256 C CB . LEU B 1 33 ? -6.089 -2.149 0.676 1.00 63.68 126 A 1
465
+ ATOM 257 C CG . LEU B 1 33 ? -5.181 -3.155 1.283 1.00 71.88 126 A 1
466
+ ATOM 258 C CD1 . LEU B 1 33 ? -4.453 -3.972 0.195 1.00 73.91 126 A 1
467
+ ATOM 259 C CD2 . LEU B 1 33 ? -4.179 -2.480 2.169 1.00 75.78 126 A 1
468
+ ATOM 260 N N . LEU B 1 34 ? -6.482 0.743 2.064 1.00 51.59 127 A 1
469
+ ATOM 261 C CA . LEU B 1 34 ? -6.054 1.821 2.952 1.00 47.43 127 A 1
470
+ ATOM 262 C C . LEU B 1 34 ? -7.162 2.293 3.867 1.00 49.18 127 A 1
471
+ ATOM 263 O O . LEU B 1 34 ? -6.917 2.748 4.956 1.00 48.29 127 A 1
472
+ ATOM 264 C CB . LEU B 1 34 ? -5.537 2.996 2.165 1.00 43.17 127 A 1
473
+ ATOM 265 C CG . LEU B 1 34 ? -4.031 2.968 1.890 1.00 42.73 127 A 1
474
+ ATOM 266 C CD1 . LEU B 1 34 ? -3.630 4.313 1.305 1.00 42.46 127 A 1
475
+ ATOM 267 C CD2 . LEU B 1 34 ? -3.088 2.610 3.005 1.00 43.31 127 A 1
476
+ ATOM 268 N N . LYS B 1 35 ? -8.399 2.214 3.408 1.00 52.51 128 A 1
477
+ ATOM 269 C CA . LYS B 1 35 ? -9.553 2.535 4.233 1.00 52.46 128 A 1
478
+ ATOM 270 C C . LYS B 1 35 ? -9.710 1.492 5.330 1.00 53.78 128 A 1
479
+ ATOM 271 O O . LYS B 1 35 ? -10.040 1.823 6.442 1.00 47.30 128 A 1
480
+ ATOM 272 C CB . LYS B 1 35 ? -10.830 2.641 3.381 1.00 54.62 128 A 1
481
+ ATOM 273 C CG . LYS B 1 35 ? -12.138 2.251 4.078 1.00 59.22 128 A 1
482
+ ATOM 274 N N . ARG B 1 36 ? -9.454 0.236 4.997 1.00 56.56 129 A 1
483
+ ATOM 275 C CA . ARG B 1 36 ? -9.471 -0.853 5.953 1.00 60.75 129 A 1
484
+ ATOM 276 C C . ARG B 1 36 ? -8.392 -0.650 7.014 1.00 59.48 129 A 1
485
+ ATOM 277 O O . ARG B 1 36 ? -8.620 -0.927 8.185 1.00 63.52 129 A 1
486
+ ATOM 278 C CB . ARG B 1 36 ? -9.292 -2.210 5.247 1.00 65.76 129 A 1
487
+ ATOM 279 C CG . ARG B 1 36 ? -10.535 -2.769 4.547 1.00 68.71 129 A 1
488
+ ATOM 280 C CD . ARG B 1 36 ? -10.167 -3.835 3.488 1.00 67.67 129 A 1
489
+ ATOM 281 N N . ILE B 1 37 ? -7.221 -0.161 6.611 1.00 54.56 130 A 1
490
+ ATOM 282 C CA . ILE B 1 37 ? -6.140 0.129 7.539 1.00 49.62 130 A 1
491
+ ATOM 283 C C . ILE B 1 37 ? -6.541 1.250 8.466 1.00 49.00 130 A 1
492
+ ATOM 284 O O . ILE B 1 37 ? -6.319 1.155 9.647 1.00 47.20 130 A 1
493
+ ATOM 285 C CB . ILE B 1 37 ? -4.839 0.472 6.806 1.00 51.01 130 A 1
494
+ ATOM 286 C CG1 . ILE B 1 37 ? -4.243 -0.830 6.231 1.00 57.41 130 A 1
495
+ ATOM 287 C CG2 . ILE B 1 37 ? -3.851 1.181 7.755 1.00 49.06 130 A 1
496
+ ATOM 288 C CD1 . ILE B 1 37 ? -2.922 -0.695 5.533 1.00 59.59 130 A 1
497
+ ATOM 289 N N . HIS B 1 38 ? -7.179 2.276 7.957 1.00 50.21 131 A 1
498
+ ATOM 290 C CA . HIS B 1 38 ? -7.618 3.356 8.797 1.00 53.53 131 A 1
499
+ ATOM 291 C C . HIS B 1 38 ? -8.670 2.891 9.780 1.00 62.35 131 A 1
500
+ ATOM 292 O O . HIS B 1 38 ? -8.744 3.378 10.880 1.00 61.76 131 A 1
501
+ ATOM 293 C CB . HIS B 1 38 ? -8.175 4.452 7.947 1.00 51.42 131 A 1
502
+ ATOM 294 C CG . HIS B 1 38 ? -9.034 5.412 8.688 1.00 54.14 131 A 1
503
+ ATOM 295 N ND1 . HIS B 1 38 ? -10.380 5.221 8.850 1.00 59.92 131 A 1
504
+ ATOM 296 C CD2 . HIS B 1 38 ? -8.749 6.586 9.283 1.00 55.36 131 A 1
505
+ ATOM 297 C CE1 . HIS B 1 38 ? -10.887 6.229 9.522 1.00 62.46 131 A 1
506
+ ATOM 298 N NE2 . HIS B 1 38 ? -9.918 7.073 9.795 1.00 61.48 131 A 1
507
+ ATOM 299 N N . ASP B 1 39 ? -9.476 1.928 9.394 1.00 63.84 132 A 1
508
+ ATOM 300 C CA . ASP B 1 39 ? -10.480 1.421 10.286 1.00 65.95 132 A 1
509
+ ATOM 301 C C . ASP B 1 39 ? -9.837 0.648 11.420 1.00 61.61 132 A 1
510
+ ATOM 302 O O . ASP B 1 39 ? -10.164 0.840 12.551 1.00 56.16 132 A 1
511
+ ATOM 303 C CB . ASP B 1 39 ? -11.456 0.577 9.509 1.00 73.60 132 A 1
512
+ ATOM 304 C CG . ASP B 1 39 ? -12.373 1.404 8.658 1.00 76.60 132 A 1
513
+ ATOM 305 O OD1 . ASP B 1 39 ? -12.277 2.631 8.703 1.00 70.60 132 A 1
514
+ ATOM 306 O OD2 . ASP B 1 39 ? -13.195 0.833 7.937 1.00 81.44 132 A 1
515
+ ATOM 307 N N . LYS B 1 40 ? -8.926 -0.245 11.106 1.00 59.87 133 A 1
516
+ ATOM 308 C CA . LYS B 1 40 ? -8.191 -0.985 12.129 1.00 59.13 133 A 1
517
+ ATOM 309 C C . LYS B 1 40 ? -7.573 -0.021 13.157 1.00 58.88 133 A 1
518
+ ATOM 310 O O . LYS B 1 40 ? -7.579 -0.267 14.361 1.00 63.83 133 A 1
519
+ ATOM 311 C CB . LYS B 1 40 ? -7.093 -1.864 11.503 1.00 58.69 133 A 1
520
+ ATOM 312 C CG . LYS B 1 40 ? -7.577 -3.018 10.646 1.00 62.42 133 A 1
521
+ ATOM 313 C CD . LYS B 1 40 ? -6.463 -3.844 9.987 1.00 62.93 133 A 1
522
+ ATOM 314 N N . LEU B 1 41 ? -7.038 1.086 12.674 1.00 60.19 134 A 1
523
+ ATOM 315 C CA . LEU B 1 41 ? -6.454 2.092 13.561 1.00 62.93 134 A 1
524
+ ATOM 316 C C . LEU B 1 41 ? -7.487 2.707 14.452 1.00 63.64 134 A 1
525
+ ATOM 317 O O . LEU B 1 41 ? -7.205 2.982 15.605 1.00 73.06 134 A 1
526
+ ATOM 318 C CB . LEU B 1 41 ? -5.765 3.197 12.764 1.00 64.98 134 A 1
527
+ ATOM 319 C CG . LEU B 1 41 ? -4.531 2.611 12.036 1.00 68.88 134 A 1
528
+ ATOM 320 C CD1 . LEU B 1 41 ? -4.332 3.201 10.638 1.00 78.49 134 A 1
529
+ ATOM 321 C CD2 . LEU B 1 41 ? -3.301 2.685 12.900 1.00 67.40 134 A 1
530
+ ATOM 322 N N . MET B 1 42 ? -8.669 2.955 13.913 1.00 63.76 135 A 1
531
+ ATOM 323 C CA . MET B 1 42 ? -9.752 3.512 14.704 1.00 67.57 135 A 1
532
+ ATOM 324 C C . MET B 1 42 ? -10.186 2.561 15.817 1.00 72.44 135 A 1
533
+ ATOM 325 O O . MET B 1 42 ? -10.413 2.985 16.975 1.00 66.48 135 A 1
534
+ ATOM 326 C CB . MET B 1 42 ? -10.932 3.853 13.816 1.00 69.82 135 A 1
535
+ ATOM 327 C CG . MET B 1 42 ? -10.710 5.112 13.020 1.00 74.54 135 A 1
536
+ ATOM 328 S SD . MET B 1 42 ? -10.062 6.547 13.958 1.00 82.27 135 A 1
537
+ ATOM 329 C CE . MET B 1 42 ? -11.073 6.663 15.446 1.00 83.33 135 A 1
538
+ ATOM 330 N N . ILE B 1 43 ? -10.271 1.274 15.477 1.00 81.93 136 A 1
539
+ ATOM 331 C CA . ILE B 1 43 ? -10.594 0.243 16.446 1.00 79.15 136 A 1
540
+ ATOM 332 C C . ILE B 1 43 ? -9.526 0.183 17.546 1.00 79.65 136 A 1
541
+ ATOM 333 O O . ILE B 1 43 ? -9.854 0.128 18.719 1.00 82.01 136 A 1
542
+ ATOM 334 C CB . ILE B 1 43 ? -10.781 -1.129 15.738 1.00 80.39 136 A 1
543
+ ATOM 335 C CG1 . ILE B 1 43 ? -12.176 -1.237 15.090 1.00 78.84 136 A 1
544
+ ATOM 336 C CG2 . ILE B 1 43 ? -10.586 -2.294 16.708 1.00 84.53 136 A 1
545
+ ATOM 337 C CD1 . ILE B 1 43 ? -12.480 -0.642 13.720 1.00 74.74 136 A 1
546
+ ATOM 338 N N . ARG B 1 44 ? -8.250 0.200 17.158 1.00 85.15 137 A 1
547
+ ATOM 339 C CA . ARG B 1 44 ? -7.131 0.216 18.111 1.00 90.78 137 A 1
548
+ ATOM 340 C C . ARG B 1 44 ? -7.103 1.498 18.957 1.00 91.00 137 A 1
549
+ ATOM 341 O O . ARG B 1 44 ? -6.632 1.453 20.068 1.00 96.79 137 A 1
550
+ ATOM 342 C CB . ARG B 1 44 ? -5.803 0.033 17.373 1.00 95.82 137 A 1
551
+ ATOM 343 C CG . ARG B 1 44 ? -4.883 -1.005 17.902 1.00 101.09 137 A 1
552
+ ATOM 344 C CD . ARG B 1 44 ? -3.772 -1.099 16.880 1.00 107.64 137 A 1
553
+ ATOM 345 N NE . ARG B 1 44 ? -2.978 -2.299 17.060 1.00 124.71 137 A 1
554
+ ATOM 346 C CZ . ARG B 1 44 ? -2.237 -2.882 16.112 1.00 137.30 137 A 1
555
+ ATOM 347 N NH1 . ARG B 1 44 ? -2.186 -2.387 14.871 1.00 128.35 137 A 1
556
+ ATOM 348 N NH2 . ARG B 1 44 ? -1.533 -3.990 16.400 1.00 147.87 137 A 1
557
+ ATOM 349 N N . ALA B 1 45 ? -7.688 2.601 18.506 1.00 92.41 138 A 1
558
+ ATOM 350 C CA . ALA B 1 45 ? -7.783 3.810 19.381 1.00 96.29 138 A 1
559
+ ATOM 351 C C . ALA B 1 45 ? -8.773 4.837 18.851 1.00 95.46 138 A 1
560
+ ATOM 352 O O . ALA B 1 45 ? -8.386 5.847 18.235 1.00 81.53 138 A 1
561
+ ATOM 353 C CB . ALA B 1 45 ? -6.408 4.475 19.603 1.00 97.91 138 A 1
562
+ #
flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/6s61.cif ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/6ydw.cif ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/7rye.cif ADDED
@@ -0,0 +1,893 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ data_7RYE
2
+ #
3
+ _entry.id 7RYE
4
+ #
5
+ loop_
6
+ _chem_comp.formula
7
+ _chem_comp.formula_weight
8
+ _chem_comp.id
9
+ _chem_comp.mon_nstd_flag
10
+ _chem_comp.name
11
+ _chem_comp.pdbx_synonyms
12
+ _chem_comp.type
13
+ "C3 H7 N O2" 89.093 ALA y ALANINE ? "L-peptide linking"
14
+ "C6 H15 N4 O2 1" 175.209 ARG y ARGININE ? "L-peptide linking"
15
+ "C4 H8 N2 O3" 132.118 ASN y ASPARAGINE ? "L-peptide linking"
16
+ "C4 H7 N O4" 133.103 ASP y "ASPARTIC ACID" ? "L-peptide linking"
17
+ "C3 H7 N O2 S" 121.158 CYS y CYSTEINE ? "L-peptide linking"
18
+ "C5 H10 N2 O3" 146.144 GLN y GLUTAMINE ? "L-peptide linking"
19
+ "C5 H9 N O4" 147.129 GLU y "GLUTAMIC ACID" ? "L-peptide linking"
20
+ "C2 H5 N O2" 75.067 GLY y GLYCINE ? "peptide linking"
21
+ "C6 H10 N3 O2 1" 156.162 HIS y HISTIDINE ? "L-peptide linking"
22
+ "C6 H13 N O2" 131.173 ILE y ISOLEUCINE ? "L-peptide linking"
23
+ "C6 H13 N O2" 131.173 LEU y LEUCINE ? "L-peptide linking"
24
+ "C6 H15 N2 O2 1" 147.195 LYS y LYSINE ? "L-peptide linking"
25
+ "C5 H11 N O2 S" 149.211 MET y METHIONINE ? "L-peptide linking"
26
+ "C9 H11 N O2" 165.189 PHE y PHENYLALANINE ? "L-peptide linking"
27
+ "C5 H9 N O2" 115.130 PRO y PROLINE ? "L-peptide linking"
28
+ "C3 H7 N O3" 105.093 SER y SERINE ? "L-peptide linking"
29
+ "C4 H9 N O3" 119.119 THR y THREONINE ? "L-peptide linking"
30
+ "C11 H12 N2 O2" 204.225 TRP y TRYPTOPHAN ? "L-peptide linking"
31
+ "C9 H11 N O3" 181.189 TYR y TYROSINE ? "L-peptide linking"
32
+ "C5 H11 N O2" 117.146 VAL y VALINE ? "L-peptide linking"
33
+ #
34
+ _entity.id 1
35
+ _entity.pdbx_description "Protein PrgI"
36
+ _entity.type polymer
37
+ #
38
+ _entity_poly.entity_id 1
39
+ _entity_poly.pdbx_strand_id J
40
+ _entity_poly.type polypeptide(L)
41
+ #
42
+ loop_
43
+ _entity_poly_seq.entity_id
44
+ _entity_poly_seq.hetero
45
+ _entity_poly_seq.mon_id
46
+ _entity_poly_seq.num
47
+ 1 n MET 1
48
+ 1 n ALA 2
49
+ 1 n THR 3
50
+ 1 n PRO 4
51
+ 1 n TRP 5
52
+ 1 n SER 6
53
+ 1 n GLY 7
54
+ 1 n TYR 8
55
+ 1 n LEU 9
56
+ 1 n ASP 10
57
+ 1 n ASP 11
58
+ 1 n VAL 12
59
+ 1 n SER 13
60
+ 1 n ALA 14
61
+ 1 n LYS 15
62
+ 1 n PHE 16
63
+ 1 n ASP 17
64
+ 1 n THR 18
65
+ 1 n GLY 19
66
+ 1 n VAL 20
67
+ 1 n ASP 21
68
+ 1 n ASN 22
69
+ 1 n LEU 23
70
+ 1 n GLN 24
71
+ 1 n THR 25
72
+ 1 n GLN 26
73
+ 1 n VAL 27
74
+ 1 n THR 28
75
+ 1 n GLU 29
76
+ 1 n ALA 30
77
+ 1 n LEU 31
78
+ 1 n ASP 32
79
+ 1 n LYS 33
80
+ 1 n LEU 34
81
+ 1 n ALA 35
82
+ 1 n ALA 36
83
+ 1 n LYS 37
84
+ 1 n PRO 38
85
+ 1 n SER 39
86
+ 1 n ASP 40
87
+ 1 n PRO 41
88
+ 1 n ALA 42
89
+ 1 n LEU 43
90
+ 1 n LEU 44
91
+ 1 n ALA 45
92
+ 1 n ALA 46
93
+ 1 n TYR 47
94
+ 1 n GLN 48
95
+ 1 n SER 49
96
+ 1 n LYS 50
97
+ 1 n LEU 51
98
+ 1 n SER 52
99
+ 1 n GLU 53
100
+ 1 n TYR 54
101
+ 1 n ASN 55
102
+ 1 n LEU 56
103
+ 1 n TYR 57
104
+ 1 n ARG 58
105
+ 1 n ASN 59
106
+ 1 n ALA 60
107
+ 1 n GLN 61
108
+ 1 n SER 62
109
+ 1 n ASN 63
110
+ 1 n THR 64
111
+ 1 n VAL 65
112
+ 1 n LYS 66
113
+ 1 n VAL 67
114
+ 1 n PHE 68
115
+ 1 n LYS 69
116
+ 1 n ASP 70
117
+ 1 n ILE 71
118
+ 1 n ASP 72
119
+ 1 n ALA 73
120
+ 1 n ALA 74
121
+ 1 n ILE 75
122
+ 1 n ILE 76
123
+ 1 n GLN 77
124
+ 1 n ASN 78
125
+ 1 n PHE 79
126
+ 1 n ARG 80
127
+ #
128
+ _exptl.method "ELECTRON MICROSCOPY"
129
+ #
130
+ _pdbx_audit_revision_history.revision_date 2021-11-10
131
+ #
132
+ _pdbx_database_status.recvd_initial_deposition_date 2021-11-10
133
+ #
134
+ loop_
135
+ _pdbx_poly_seq_scheme.asym_id
136
+ _pdbx_poly_seq_scheme.auth_seq_num
137
+ _pdbx_poly_seq_scheme.entity_id
138
+ _pdbx_poly_seq_scheme.hetero
139
+ _pdbx_poly_seq_scheme.mon_id
140
+ _pdbx_poly_seq_scheme.pdb_ins_code
141
+ _pdbx_poly_seq_scheme.pdb_seq_num
142
+ _pdbx_poly_seq_scheme.pdb_strand_id
143
+ _pdbx_poly_seq_scheme.seq_id
144
+ J ? 1 n MET . 1 J 1
145
+ J ? 1 n ALA . 2 J 2
146
+ J 3 1 n THR . 3 J 3
147
+ J 4 1 n PRO . 4 J 4
148
+ J 5 1 n TRP . 5 J 5
149
+ J 6 1 n SER . 6 J 6
150
+ J 7 1 n GLY . 7 J 7
151
+ J 8 1 n TYR . 8 J 8
152
+ J 9 1 n LEU . 9 J 9
153
+ J 10 1 n ASP . 10 J 10
154
+ J 11 1 n ASP . 11 J 11
155
+ J 12 1 n VAL . 12 J 12
156
+ J 13 1 n SER . 13 J 13
157
+ J 14 1 n ALA . 14 J 14
158
+ J 15 1 n LYS . 15 J 15
159
+ J 16 1 n PHE . 16 J 16
160
+ J 17 1 n ASP . 17 J 17
161
+ J 18 1 n THR . 18 J 18
162
+ J 19 1 n GLY . 19 J 19
163
+ J 20 1 n VAL . 20 J 20
164
+ J 21 1 n ASP . 21 J 21
165
+ J 22 1 n ASN . 22 J 22
166
+ J 23 1 n LEU . 23 J 23
167
+ J 24 1 n GLN . 24 J 24
168
+ J 25 1 n THR . 25 J 25
169
+ J 26 1 n GLN . 26 J 26
170
+ J 27 1 n VAL . 27 J 27
171
+ J 28 1 n THR . 28 J 28
172
+ J 29 1 n GLU . 29 J 29
173
+ J 30 1 n ALA . 30 J 30
174
+ J 31 1 n LEU . 31 J 31
175
+ J 32 1 n ASP . 32 J 32
176
+ J 33 1 n LYS . 33 J 33
177
+ J 34 1 n LEU . 34 J 34
178
+ J 35 1 n ALA . 35 J 35
179
+ J 36 1 n ALA . 36 J 36
180
+ J 37 1 n LYS . 37 J 37
181
+ J 38 1 n PRO . 38 J 38
182
+ J 39 1 n SER . 39 J 39
183
+ J 40 1 n ASP . 40 J 40
184
+ J 41 1 n PRO . 41 J 41
185
+ J 42 1 n ALA . 42 J 42
186
+ J 43 1 n LEU . 43 J 43
187
+ J 44 1 n LEU . 44 J 44
188
+ J 45 1 n ALA . 45 J 45
189
+ J 46 1 n ALA . 46 J 46
190
+ J 47 1 n TYR . 47 J 47
191
+ J 48 1 n GLN . 48 J 48
192
+ J 49 1 n SER . 49 J 49
193
+ J 50 1 n LYS . 50 J 50
194
+ J 51 1 n LEU . 51 J 51
195
+ J 52 1 n SER . 52 J 52
196
+ J 53 1 n GLU . 53 J 53
197
+ J 54 1 n TYR . 54 J 54
198
+ J 55 1 n ASN . 55 J 55
199
+ J 56 1 n LEU . 56 J 56
200
+ J 57 1 n TYR . 57 J 57
201
+ J 58 1 n ARG . 58 J 58
202
+ J 59 1 n ASN . 59 J 59
203
+ J 60 1 n ALA . 60 J 60
204
+ J 61 1 n GLN . 61 J 61
205
+ J 62 1 n SER . 62 J 62
206
+ J 63 1 n ASN . 63 J 63
207
+ J 64 1 n THR . 64 J 64
208
+ J 65 1 n VAL . 65 J 65
209
+ J 66 1 n LYS . 66 J 66
210
+ J 67 1 n VAL . 67 J 67
211
+ J 68 1 n PHE . 68 J 68
212
+ J 69 1 n LYS . 69 J 69
213
+ J 70 1 n ASP . 70 J 70
214
+ J 71 1 n ILE . 71 J 71
215
+ J 72 1 n ASP . 72 J 72
216
+ J 73 1 n ALA . 73 J 73
217
+ J 74 1 n ALA . 74 J 74
218
+ J 75 1 n ILE . 75 J 75
219
+ J 76 1 n ILE . 76 J 76
220
+ J 77 1 n GLN . 77 J 77
221
+ J 78 1 n ASN . 78 J 78
222
+ J 79 1 n PHE . 79 J 79
223
+ J 80 1 n ARG . 80 J 80
224
+ #
225
+ _pdbx_struct_assembly.details author_defined_assembly
226
+ _pdbx_struct_assembly.id 1
227
+ _pdbx_struct_assembly.method_details ?
228
+ _pdbx_struct_assembly.oligomeric_count 24
229
+ _pdbx_struct_assembly.oligomeric_details 24-meric
230
+ #
231
+ _pdbx_struct_assembly_gen.assembly_id 1
232
+ _pdbx_struct_assembly_gen.asym_id_list A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X
233
+ _pdbx_struct_assembly_gen.oper_expression 1
234
+ #
235
+ _pdbx_struct_oper_list.id 1
236
+ _pdbx_struct_oper_list.matrix[1][1] 1.0000000000
237
+ _pdbx_struct_oper_list.matrix[1][2] 0.0000000000
238
+ _pdbx_struct_oper_list.matrix[1][3] 0.0000000000
239
+ _pdbx_struct_oper_list.matrix[2][1] 0.0000000000
240
+ _pdbx_struct_oper_list.matrix[2][2] 1.0000000000
241
+ _pdbx_struct_oper_list.matrix[2][3] 0.0000000000
242
+ _pdbx_struct_oper_list.matrix[3][1] 0.0000000000
243
+ _pdbx_struct_oper_list.matrix[3][2] 0.0000000000
244
+ _pdbx_struct_oper_list.matrix[3][3] 1.0000000000
245
+ _pdbx_struct_oper_list.name 1_555
246
+ _pdbx_struct_oper_list.symmetry_operation ?
247
+ _pdbx_struct_oper_list.type "identity operation"
248
+ _pdbx_struct_oper_list.vector[1] 0.0000000000
249
+ _pdbx_struct_oper_list.vector[2] 0.0000000000
250
+ _pdbx_struct_oper_list.vector[3] 0.0000000000
251
+ #
252
+ _refine.ls_d_res_high 3.90
253
+ #
254
+ _software.classification other
255
+ _software.name "DeepMind Structure Class"
256
+ _software.pdbx_ordinal 1
257
+ _software.version 2.0.0
258
+ #
259
+ _struct_asym.entity_id 1
260
+ _struct_asym.id J
261
+ #
262
+ loop_
263
+ _atom_site.group_PDB
264
+ _atom_site.id
265
+ _atom_site.type_symbol
266
+ _atom_site.label_atom_id
267
+ _atom_site.label_alt_id
268
+ _atom_site.label_comp_id
269
+ _atom_site.label_asym_id
270
+ _atom_site.label_entity_id
271
+ _atom_site.label_seq_id
272
+ _atom_site.pdbx_PDB_ins_code
273
+ _atom_site.Cartn_x
274
+ _atom_site.Cartn_y
275
+ _atom_site.Cartn_z
276
+ _atom_site.occupancy
277
+ _atom_site.B_iso_or_equiv
278
+ _atom_site.auth_seq_id
279
+ _atom_site.auth_asym_id
280
+ _atom_site.pdbx_PDB_model_num
281
+ ATOM 1 N N . THR J 1 3 ? 180.798 156.981 49.506 1.00 100.87 3 J 1
282
+ ATOM 2 C CA . THR J 1 3 ? 180.336 158.053 50.380 1.00 100.87 3 J 1
283
+ ATOM 3 C C . THR J 1 3 ? 178.888 157.811 50.811 1.00 100.87 3 J 1
284
+ ATOM 4 O O . THR J 1 3 ? 178.076 157.312 50.030 1.00 100.87 3 J 1
285
+ ATOM 5 C CB . THR J 1 3 ? 180.468 159.436 49.691 1.00 100.87 3 J 1
286
+ ATOM 6 O OG1 . THR J 1 3 ? 180.169 160.478 50.627 1.00 100.87 3 J 1
287
+ ATOM 7 C CG2 . THR J 1 3 ? 179.541 159.546 48.488 1.00 100.87 3 J 1
288
+ ATOM 8 N N . PRO J 1 4 ? 178.569 158.139 52.065 1.00 96.05 4 J 1
289
+ ATOM 9 C CA . PRO J 1 4 ? 177.201 157.918 52.554 1.00 96.05 4 J 1
290
+ ATOM 10 C C . PRO J 1 4 ? 176.203 158.873 51.924 1.00 96.05 4 J 1
291
+ ATOM 11 O O . PRO J 1 4 ? 176.293 160.090 52.112 1.00 96.05 4 J 1
292
+ ATOM 12 C CB . PRO J 1 4 ? 177.326 158.152 54.065 1.00 96.05 4 J 1
293
+ ATOM 13 C CG . PRO J 1 4 ? 178.504 159.047 54.212 1.00 96.05 4 J 1
294
+ ATOM 14 C CD . PRO J 1 4 ? 179.457 158.664 53.117 1.00 96.05 4 J 1
295
+ ATOM 15 N N . TRP J 1 5 ? 175.251 158.334 51.169 1.00 82.05 5 J 1
296
+ ATOM 16 C CA . TRP J 1 5 ? 174.242 159.172 50.543 1.00 82.05 5 J 1
297
+ ATOM 17 C C . TRP J 1 5 ? 173.275 159.710 51.591 1.00 82.05 5 J 1
298
+ ATOM 18 O O . TRP J 1 5 ? 173.068 159.105 52.647 1.00 82.05 5 J 1
299
+ ATOM 19 C CB . TRP J 1 5 ? 173.467 158.390 49.486 1.00 82.05 5 J 1
300
+ ATOM 20 C CG . TRP J 1 5 ? 172.575 159.261 48.675 1.00 82.05 5 J 1
301
+ ATOM 21 C CD1 . TRP J 1 5 ? 172.953 160.130 47.697 1.00 82.05 5 J 1
302
+ ATOM 22 C CD2 . TRP J 1 5 ? 171.158 159.402 48.811 1.00 82.05 5 J 1
303
+ ATOM 23 N NE1 . TRP J 1 5 ? 171.856 160.778 47.189 1.00 82.05 5 J 1
304
+ ATOM 24 C CE2 . TRP J 1 5 ? 170.741 160.351 47.859 1.00 82.05 5 J 1
305
+ ATOM 25 C CE3 . TRP J 1 5 ? 170.199 158.807 49.634 1.00 82.05 5 J 1
306
+ ATOM 26 C CZ2 . TRP J 1 5 ? 169.408 160.720 47.709 1.00 82.05 5 J 1
307
+ ATOM 27 C CZ3 . TRP J 1 5 ? 168.877 159.176 49.482 1.00 82.05 5 J 1
308
+ ATOM 28 C CH2 . TRP J 1 5 ? 168.493 160.120 48.527 1.00 82.05 5 J 1
309
+ ATOM 29 N N . SER J 1 6 ? 172.676 160.860 51.291 1.00 78.94 6 J 1
310
+ ATOM 30 C CA . SER J 1 6 ? 171.730 161.484 52.213 1.00 78.94 6 J 1
311
+ ATOM 31 C C . SER J 1 6 ? 170.799 162.391 51.422 1.00 78.94 6 J 1
312
+ ATOM 32 O O . SER J 1 6 ? 171.235 163.417 50.892 1.00 78.94 6 J 1
313
+ ATOM 33 C CB . SER J 1 6 ? 172.462 162.265 53.300 1.00 78.94 6 J 1
314
+ ATOM 34 O OG . SER J 1 6 ? 173.149 161.389 54.176 1.00 78.94 6 J 1
315
+ ATOM 35 N N . GLY J 1 7 ? 169.526 162.014 51.347 1.00 80.09 7 J 1
316
+ ATOM 36 C CA . GLY J 1 7 ? 168.512 162.855 50.750 1.00 80.09 7 J 1
317
+ ATOM 37 C C . GLY J 1 7 ? 168.082 163.958 51.698 1.00 80.09 7 J 1
318
+ ATOM 38 O O . GLY J 1 7 ? 168.539 164.059 52.838 1.00 80.09 7 J 1
319
+ ATOM 39 N N . TYR J 1 8 ? 167.174 164.804 51.207 1.00 78.37 8 J 1
320
+ ATOM 40 C CA . TYR J 1 8 ? 166.760 165.964 51.991 1.00 78.37 8 J 1
321
+ ATOM 41 C C . TYR J 1 8 ? 166.084 165.523 53.284 1.00 78.37 8 J 1
322
+ ATOM 42 O O . TYR J 1 8 ? 166.332 166.093 54.351 1.00 78.37 8 J 1
323
+ ATOM 43 C CB . TYR J 1 8 ? 165.830 166.858 51.167 1.00 78.37 8 J 1
324
+ ATOM 44 C CG . TYR J 1 8 ? 165.412 168.151 51.846 1.00 78.37 8 J 1
325
+ ATOM 45 C CD1 . TYR J 1 8 ? 164.409 168.939 51.311 1.00 78.37 8 J 1
326
+ ATOM 46 C CD2 . TYR J 1 8 ? 166.073 168.622 52.974 1.00 78.37 8 J 1
327
+ ATOM 47 C CE1 . TYR J 1 8 ? 164.036 170.122 51.913 1.00 78.37 8 J 1
328
+ ATOM 48 C CE2 . TYR J 1 8 ? 165.705 169.801 53.581 1.00 78.37 8 J 1
329
+ ATOM 49 C CZ . TYR J 1 8 ? 164.688 170.550 53.044 1.00 78.37 8 J 1
330
+ ATOM 50 O OH . TYR J 1 8 ? 164.318 171.735 53.638 1.00 78.37 8 J 1
331
+ ATOM 51 N N . LEU J 1 9 ? 165.226 164.506 53.213 1.00 73.64 9 J 1
332
+ ATOM 52 C CA . LEU J 1 9 ? 164.645 163.967 54.439 1.00 73.64 9 J 1
333
+ ATOM 53 C C . LEU J 1 9 ? 165.730 163.390 55.340 1.00 73.64 9 J 1
334
+ ATOM 54 O O . LEU J 1 9 ? 165.719 163.604 56.560 1.00 73.64 9 J 1
335
+ ATOM 55 C CB . LEU J 1 9 ? 163.595 162.906 54.109 1.00 73.64 9 J 1
336
+ ATOM 56 C CG . LEU J 1 9 ? 162.192 163.335 53.661 1.00 73.64 9 J 1
337
+ ATOM 57 C CD1 . LEU J 1 9 ? 162.211 164.303 52.488 1.00 73.64 9 J 1
338
+ ATOM 58 C CD2 . LEU J 1 9 ? 161.355 162.112 53.321 1.00 73.64 9 J 1
339
+ ATOM 59 N N . ASP J 1 10 ? 166.683 162.668 54.750 1.00 75.23 10 J 1
340
+ ATOM 60 C CA . ASP J 1 10 ? 167.828 162.189 55.511 1.00 75.23 10 J 1
341
+ ATOM 61 C C . ASP J 1 10 ? 168.631 163.360 56.062 1.00 75.23 10 J 1
342
+ ATOM 62 O O . ASP J 1 10 ? 169.140 163.297 57.186 1.00 75.23 10 J 1
343
+ ATOM 63 C CB . ASP J 1 10 ? 168.695 161.293 54.624 1.00 75.23 10 J 1
344
+ ATOM 64 C CG . ASP J 1 10 ? 169.793 160.577 55.393 1.00 75.23 10 J 1
345
+ ATOM 65 O OD1 . ASP J 1 10 ? 169.921 160.785 56.617 1.00 75.23 10 J 1
346
+ ATOM 66 O OD2 . ASP J 1 10 ? 170.537 159.796 54.764 1.00 75.23 10 J 1
347
+ ATOM 67 N N . ASP J 1 11 ? 168.742 164.442 55.289 1.00 76.21 11 J 1
348
+ ATOM 68 C CA . ASP J 1 11 ? 169.449 165.624 55.770 1.00 76.21 11 J 1
349
+ ATOM 69 C C . ASP J 1 11 ? 168.755 166.229 56.984 1.00 76.21 11 J 1
350
+ ATOM 70 O O . ASP J 1 11 ? 169.416 166.658 57.932 1.00 76.21 11 J 1
351
+ ATOM 71 C CB . ASP J 1 11 ? 169.573 166.658 54.651 1.00 76.21 11 J 1
352
+ ATOM 72 C CG . ASP J 1 11 ? 170.510 166.210 53.546 1.00 76.21 11 J 1
353
+ ATOM 73 O OD1 . ASP J 1 11 ? 171.452 165.444 53.836 1.00 76.21 11 J 1
354
+ ATOM 74 O OD2 . ASP J 1 11 ? 170.303 166.623 52.386 1.00 76.21 11 J 1
355
+ ATOM 75 N N . VAL J 1 12 ? 167.423 166.274 56.973 1.00 71.19 12 J 1
356
+ ATOM 76 C CA . VAL J 1 12 ? 166.679 166.814 58.107 1.00 71.19 12 J 1
357
+ ATOM 77 C C . VAL J 1 12 ? 166.846 165.921 59.330 1.00 71.19 12 J 1
358
+ ATOM 78 O O . VAL J 1 12 ? 167.032 166.405 60.457 1.00 71.19 12 J 1
359
+ ATOM 79 C CB . VAL J 1 12 ? 165.197 166.989 57.729 1.00 71.19 12 J 1
360
+ ATOM 80 C CG1 . VAL J 1 12 ? 164.358 167.240 58.967 1.00 71.19 12 J 1
361
+ ATOM 81 C CG2 . VAL J 1 12 ? 165.039 168.124 56.733 1.00 71.19 12 J 1
362
+ ATOM 82 N N . SER J 1 13 ? 166.779 164.604 59.130 1.00 71.31 13 J 1
363
+ ATOM 83 C CA . SER J 1 13 ? 167.004 163.691 60.244 1.00 71.31 13 J 1
364
+ ATOM 84 C C . SER J 1 13 ? 168.397 163.882 60.831 1.00 71.31 13 J 1
365
+ ATOM 85 O O . SER J 1 13 ? 168.555 163.965 62.054 1.00 71.31 13 J 1
366
+ ATOM 86 C CB . SER J 1 13 ? 166.801 162.246 59.794 1.00 71.31 13 J 1
367
+ ATOM 87 O OG . SER J 1 13 ? 165.459 162.023 59.402 1.00 71.31 13 J 1
368
+ ATOM 88 N N . ALA J 1 14 ? 169.413 163.985 59.972 1.00 73.94 14 J 1
369
+ ATOM 89 C CA . ALA J 1 14 ? 170.773 164.209 60.451 1.00 73.94 14 J 1
370
+ ATOM 90 C C . ALA J 1 14 ? 170.904 165.566 61.130 1.00 73.94 14 J 1
371
+ ATOM 91 O O . ALA J 1 14 ? 171.700 165.728 62.061 1.00 73.94 14 J 1
372
+ ATOM 92 C CB . ALA J 1 14 ? 171.765 164.087 59.295 1.00 73.94 14 J 1
373
+ ATOM 93 N N . LYS J 1 15 ? 170.140 166.557 60.668 1.00 71.21 15 J 1
374
+ ATOM 94 C CA . LYS J 1 15 ? 170.140 167.860 61.321 1.00 71.21 15 J 1
375
+ ATOM 95 C C . LYS J 1 15 ? 169.612 167.753 62.742 1.00 71.21 15 J 1
376
+ ATOM 96 O O . LYS J 1 15 ? 170.146 168.383 63.660 1.00 71.21 15 J 1
377
+ ATOM 97 C CB . LYS J 1 15 ? 169.311 168.855 60.511 1.00 71.21 15 J 1
378
+ ATOM 98 C CG . LYS J 1 15 ? 170.142 169.773 59.635 1.00 71.21 15 J 1
379
+ ATOM 99 C CD . LYS J 1 15 ? 169.313 170.920 59.089 1.00 71.21 15 J 1
380
+ ATOM 100 C CE . LYS J 1 15 ? 170.176 171.888 58.300 1.00 71.21 15 J 1
381
+ ATOM 101 N NZ . LYS J 1 15 ? 169.369 172.970 57.676 1.00 71.21 15 J 1
382
+ ATOM 102 N N . PHE J 1 16 ? 168.559 166.963 62.943 1.00 68.61 16 J 1
383
+ ATOM 103 C CA . PHE J 1 16 ? 168.102 166.713 64.308 1.00 68.61 16 J 1
384
+ ATOM 104 C C . PHE J 1 16 ? 169.128 165.927 65.120 1.00 68.61 16 J 1
385
+ ATOM 105 O O . PHE J 1 16 ? 169.342 166.226 66.300 1.00 68.61 16 J 1
386
+ ATOM 106 C CB . PHE J 1 16 ? 166.749 166.005 64.309 1.00 68.61 16 J 1
387
+ ATOM 107 C CG . PHE J 1 16 ? 165.587 166.929 64.088 1.00 68.61 16 J 1
388
+ ATOM 108 C CD1 . PHE J 1 16 ? 165.736 168.293 64.256 1.00 68.61 16 J 1
389
+ ATOM 109 C CD2 . PHE J 1 16 ? 164.345 166.437 63.736 1.00 68.61 16 J 1
390
+ ATOM 110 C CE1 . PHE J 1 16 ? 164.670 169.147 64.068 1.00 68.61 16 J 1
391
+ ATOM 111 C CE2 . PHE J 1 16 ? 163.277 167.286 63.544 1.00 68.61 16 J 1
392
+ ATOM 112 C CZ . PHE J 1 16 ? 163.439 168.643 63.710 1.00 68.61 16 J 1
393
+ ATOM 113 N N . ASP J 1 17 ? 169.768 164.918 64.521 1.00 77.06 17 J 1
394
+ ATOM 114 C CA . ASP J 1 17 ? 170.752 164.149 65.286 1.00 77.06 17 J 1
395
+ ATOM 115 C C . ASP J 1 17 ? 171.927 165.017 65.719 1.00 77.06 17 J 1
396
+ ATOM 116 O O . ASP J 1 17 ? 172.413 164.888 66.848 1.00 77.06 17 J 1
397
+ ATOM 117 C CB . ASP J 1 17 ? 171.254 162.929 64.509 1.00 77.06 17 J 1
398
+ ATOM 118 C CG . ASP J 1 17 ? 170.136 162.119 63.888 1.00 77.06 17 J 1
399
+ ATOM 119 O OD1 . ASP J 1 17 ? 170.103 162.001 62.647 1.00 77.06 17 J 1
400
+ ATOM 120 O OD2 . ASP J 1 17 ? 169.286 161.602 64.642 1.00 77.06 17 J 1
401
+ ATOM 121 N N . THR J 1 18 ? 172.399 165.901 64.845 1.00 76.90 18 J 1
402
+ ATOM 122 C CA . THR J 1 18 ? 173.485 166.801 65.205 1.00 76.90 18 J 1
403
+ ATOM 123 C C . THR J 1 18 ? 173.016 167.991 66.028 1.00 76.90 18 J 1
404
+ ATOM 124 O O . THR J 1 18 ? 173.851 168.678 66.626 1.00 76.90 18 J 1
405
+ ATOM 125 C CB . THR J 1 18 ? 174.203 167.303 63.949 1.00 76.90 18 J 1
406
+ ATOM 126 O OG1 . THR J 1 18 ? 175.476 167.849 64.314 1.00 76.90 18 J 1
407
+ ATOM 127 C CG2 . THR J 1 18 ? 173.381 168.373 63.253 1.00 76.90 18 J 1
408
+ ATOM 128 N N . GLY J 1 19 ? 171.709 168.255 66.067 1.00 78.19 19 J 1
409
+ ATOM 129 C CA . GLY J 1 19 ? 171.207 169.320 66.916 1.00 78.19 19 J 1
410
+ ATOM 130 C C . GLY J 1 19 ? 171.381 169.012 68.389 1.00 78.19 19 J 1
411
+ ATOM 131 O O . GLY J 1 19 ? 171.777 169.879 69.172 1.00 78.19 19 J 1
412
+ ATOM 132 N N . VAL J 1 20 ? 171.089 167.780 68.785 1.00 76.12 20 J 1
413
+ ATOM 133 C CA . VAL J 1 20 ? 171.317 167.329 70.150 1.00 76.12 20 J 1
414
+ ATOM 134 C C . VAL J 1 20 ? 172.694 166.684 70.220 1.00 76.12 20 J 1
415
+ ATOM 135 O O . VAL J 1 20 ? 173.054 165.862 69.368 1.00 76.12 20 J 1
416
+ ATOM 136 C CB . VAL J 1 20 ? 170.212 166.362 70.606 1.00 76.12 20 J 1
417
+ ATOM 137 C CG1 . VAL J 1 20 ? 168.869 167.069 70.596 1.00 76.12 20 J 1
418
+ ATOM 138 C CG2 . VAL J 1 20 ? 170.158 165.132 69.713 1.00 76.12 20 J 1
419
+ ATOM 139 N N . ASP J 1 21 ? 173.487 167.090 71.210 1.00 78.41 21 J 1
420
+ ATOM 140 C CA . ASP J 1 21 ? 174.863 166.617 71.290 1.00 78.41 21 J 1
421
+ ATOM 141 C C . ASP J 1 21 ? 174.916 165.113 71.524 1.00 78.41 21 J 1
422
+ ATOM 142 O O . ASP J 1 21 ? 175.347 164.349 70.653 1.00 78.41 21 J 1
423
+ ATOM 143 C CB . ASP J 1 21 ? 175.608 167.363 72.396 1.00 78.41 21 J 1
424
+ ATOM 144 C CG . ASP J 1 21 ? 175.835 168.824 72.061 1.00 78.41 21 J 1
425
+ ATOM 145 O OD1 . ASP J 1 21 ? 175.946 169.149 70.861 1.00 78.41 21 J 1
426
+ ATOM 146 O OD2 . ASP J 1 21 ? 175.903 169.647 72.998 1.00 78.41 21 J 1
427
+ ATOM 147 N N . ASN J 1 22 ? 174.471 164.668 72.693 1.00 74.91 22 J 1
428
+ ATOM 148 C CA . ASN J 1 22 ? 174.565 163.267 73.084 1.00 74.91 22 J 1
429
+ ATOM 149 C C . ASN J 1 22 ? 173.277 162.819 73.765 1.00 74.91 22 J 1
430
+ ATOM 150 O O . ASN J 1 22 ? 173.294 162.151 74.799 1.00 74.91 22 J 1
431
+ ATOM 151 C CB . ASN J 1 22 ? 175.777 163.050 73.987 1.00 74.91 22 J 1
432
+ ATOM 152 C CG . ASN J 1 22 ? 175.825 164.032 75.138 1.00 74.91 22 J 1
433
+ ATOM 153 O OD1 . ASN J 1 22 ? 174.925 164.855 75.304 1.00 74.91 22 J 1
434
+ ATOM 154 N ND2 . ASN J 1 22 ? 176.881 163.956 75.938 1.00 74.91 22 J 1
435
+ ATOM 155 N N . LEU J 1 23 ? 172.134 163.193 73.182 1.00 72.21 23 J 1
436
+ ATOM 156 C CA . LEU J 1 23 ? 170.852 162.915 73.824 1.00 72.21 23 J 1
437
+ ATOM 157 C C . LEU J 1 23 ? 170.648 161.425 74.053 1.00 72.21 23 J 1
438
+ ATOM 158 O O . LEU J 1 23 ? 170.248 161.010 75.146 1.00 72.21 23 J 1
439
+ ATOM 159 C CB . LEU J 1 23 ? 169.708 163.483 72.989 1.00 72.21 23 J 1
440
+ ATOM 160 C CG . LEU J 1 23 ? 168.328 163.271 73.612 1.00 72.21 23 J 1
441
+ ATOM 161 C CD1 . LEU J 1 23 ? 168.229 163.983 74.946 1.00 72.21 23 J 1
442
+ ATOM 162 C CD2 . LEU J 1 23 ? 167.237 163.743 72.669 1.00 72.21 23 J 1
443
+ ATOM 163 N N . GLN J 1 24 ? 170.920 160.601 73.039 1.00 70.63 24 J 1
444
+ ATOM 164 C CA . GLN J 1 24 ? 170.840 159.157 73.230 1.00 70.63 24 J 1
445
+ ATOM 165 C C . GLN J 1 24 ? 171.802 158.707 74.320 1.00 70.63 24 J 1
446
+ ATOM 166 O O . GLN J 1 24 ? 171.425 157.974 75.242 1.00 70.63 24 J 1
447
+ ATOM 167 C CB . GLN J 1 24 ? 171.143 158.435 71.917 1.00 70.63 24 J 1
448
+ ATOM 168 C CG . GLN J 1 24 ? 170.834 156.949 71.947 1.00 70.63 24 J 1
449
+ ATOM 169 C CD . GLN J 1 24 ? 169.405 156.644 71.556 1.00 70.63 24 J 1
450
+ ATOM 170 O OE1 . GLN J 1 24 ? 168.697 157.505 71.042 1.00 70.63 24 J 1
451
+ ATOM 171 N NE2 . GLN J 1 24 ? 168.972 155.412 71.801 1.00 70.63 24 J 1
452
+ ATOM 172 N N . THR J 1 25 ? 173.052 159.167 74.242 1.00 72.05 25 J 1
453
+ ATOM 173 C CA . THR J 1 25 ? 174.029 158.829 75.267 1.00 72.05 25 J 1
454
+ ATOM 174 C C . THR J 1 25 ? 173.628 159.382 76.627 1.00 72.05 25 J 1
455
+ ATOM 175 O O . THR J 1 25 ? 173.752 158.681 77.636 1.00 72.05 25 J 1
456
+ ATOM 176 C CB . THR J 1 25 ? 175.407 159.349 74.865 1.00 72.05 25 J 1
457
+ ATOM 177 O OG1 . THR J 1 25 ? 175.777 158.785 73.601 1.00 72.05 25 J 1
458
+ ATOM 178 C CG2 . THR J 1 25 ? 176.447 158.961 75.898 1.00 72.05 25 J 1
459
+ ATOM 179 N N . GLN J 1 26 ? 173.147 160.627 76.676 1.00 73.20 26 J 1
460
+ ATOM 180 C CA . GLN J 1 26 ? 172.756 161.219 77.951 1.00 73.20 26 J 1
461
+ ATOM 181 C C . GLN J 1 26 ? 171.656 160.400 78.610 1.00 73.20 26 J 1
462
+ ATOM 182 O O . GLN J 1 26 ? 171.709 160.118 79.814 1.00 73.20 26 J 1
463
+ ATOM 183 C CB . GLN J 1 26 ? 172.301 162.663 77.739 1.00 73.20 26 J 1
464
+ ATOM 184 C CG . GLN J 1 26 ? 173.397 163.693 77.939 1.00 73.20 26 J 1
465
+ ATOM 185 C CD . GLN J 1 26 ? 172.920 165.105 77.674 1.00 73.20 26 J 1
466
+ ATOM 186 O OE1 . GLN J 1 26 ? 171.782 165.454 77.979 1.00 73.20 26 J 1
467
+ ATOM 187 N NE2 . GLN J 1 26 ? 173.791 165.926 77.101 1.00 73.20 26 J 1
468
+ ATOM 188 N N . VAL J 1 27 ? 170.660 159.989 77.824 1.00 70.38 27 J 1
469
+ ATOM 189 C CA . VAL J 1 27 ? 169.615 159.113 78.339 1.00 70.38 27 J 1
470
+ ATOM 190 C C . VAL J 1 27 ? 170.211 157.789 78.796 1.00 70.38 27 J 1
471
+ ATOM 191 O O . VAL J 1 27 ? 169.787 157.217 79.808 1.00 70.38 27 J 1
472
+ ATOM 192 C CB . VAL J 1 27 ? 168.521 158.911 77.274 1.00 70.38 27 J 1
473
+ ATOM 193 C CG1 . VAL J 1 27 ? 167.570 157.803 77.685 1.00 70.38 27 J 1
474
+ ATOM 194 C CG2 . VAL J 1 27 ? 167.761 160.204 77.059 1.00 70.38 27 J 1
475
+ ATOM 195 N N . THR J 1 28 ? 171.208 157.285 78.067 1.00 66.97 28 J 1
476
+ ATOM 196 C CA . THR J 1 28 ? 171.814 156.011 78.437 1.00 66.97 28 J 1
477
+ ATOM 197 C C . THR J 1 28 ? 172.453 156.080 79.821 1.00 66.97 28 J 1
478
+ ATOM 198 O O . THR J 1 28 ? 172.163 155.246 80.688 1.00 66.97 28 J 1
479
+ ATOM 199 C CB . THR J 1 28 ? 172.841 155.591 77.388 1.00 66.97 28 J 1
480
+ ATOM 200 O OG1 . THR J 1 28 ? 172.192 155.449 76.118 1.00 66.97 28 J 1
481
+ ATOM 201 C CG2 . THR J 1 28 ? 173.475 154.265 77.774 1.00 66.97 28 J 1
482
+ ATOM 202 N N . GLU J 1 29 ? 173.322 157.072 80.055 1.00 72.66 29 J 1
483
+ ATOM 203 C CA . GLU J 1 29 ? 173.906 157.156 81.396 1.00 72.66 29 J 1
484
+ ATOM 204 C C . GLU J 1 29 ? 172.874 157.531 82.451 1.00 72.66 29 J 1
485
+ ATOM 205 O O . GLU J 1 29 ? 172.996 157.088 83.594 1.00 72.66 29 J 1
486
+ ATOM 206 C CB . GLU J 1 29 ? 175.107 158.111 81.526 1.00 72.66 29 J 1
487
+ ATOM 207 C CG . GLU J 1 29 ? 176.428 157.677 80.874 1.00 72.66 29 J 1
488
+ ATOM 208 C CD . GLU J 1 29 ? 176.594 158.116 79.452 1.00 72.66 29 J 1
489
+ ATOM 209 O OE1 . GLU J 1 29 ? 175.740 158.874 78.981 1.00 72.66 29 J 1
490
+ ATOM 210 O OE2 . GLU J 1 29 ? 177.582 157.707 78.806 1.00 72.66 29 J 1
491
+ ATOM 211 N N . ALA J 1 30 ? 171.858 158.331 82.113 1.00 71.55 30 J 1
492
+ ATOM 212 C CA . ALA J 1 30 ? 170.840 158.641 83.113 1.00 71.55 30 J 1
493
+ ATOM 213 C C . ALA J 1 30 ? 170.124 157.377 83.571 1.00 71.55 30 J 1
494
+ ATOM 214 O O . ALA J 1 30 ? 169.974 157.134 84.776 1.00 71.55 30 J 1
495
+ ATOM 215 C CB . ALA J 1 30 ? 169.842 159.654 82.555 1.00 71.55 30 J 1
496
+ ATOM 216 N N . LEU J 1 31 ? 169.710 156.541 82.619 1.00 67.57 31 J 1
497
+ ATOM 217 C CA . LEU J 1 31 ? 169.026 155.299 82.949 1.00 67.57 31 J 1
498
+ ATOM 218 C C . LEU J 1 31 ? 169.947 154.332 83.681 1.00 67.57 31 J 1
499
+ ATOM 219 O O . LEU J 1 31 ? 169.513 153.645 84.613 1.00 67.57 31 J 1
500
+ ATOM 220 C CB . LEU J 1 31 ? 168.470 154.664 81.675 1.00 67.57 31 J 1
501
+ ATOM 221 C CG . LEU J 1 31 ? 167.565 153.444 81.827 1.00 67.57 31 J 1
502
+ ATOM 222 C CD1 . LEU J 1 31 ? 166.434 153.521 80.822 1.00 67.57 31 J 1
503
+ ATOM 223 C CD2 . LEU J 1 31 ? 168.355 152.161 81.641 1.00 67.57 31 J 1
504
+ ATOM 224 N N . ASP J 1 32 ? 171.217 154.256 83.275 1.00 72.67 32 J 1
505
+ ATOM 225 C CA . ASP J 1 32 ? 172.149 153.367 83.960 1.00 72.67 32 J 1
506
+ ATOM 226 C C . ASP J 1 32 ? 172.379 153.821 85.398 1.00 72.67 32 J 1
507
+ ATOM 227 O O . ASP J 1 32 ? 172.414 152.997 86.319 1.00 72.67 32 J 1
508
+ ATOM 228 C CB . ASP J 1 32 ? 173.467 153.305 83.188 1.00 72.67 32 J 1
509
+ ATOM 229 C CG . ASP J 1 32 ? 174.413 152.240 83.718 1.00 72.67 32 J 1
510
+ ATOM 230 O OD1 . ASP J 1 32 ? 174.073 151.558 84.706 1.00 72.67 32 J 1
511
+ ATOM 231 O OD2 . ASP J 1 32 ? 175.507 152.083 83.137 1.00 72.67 32 J 1
512
+ ATOM 232 N N . LYS J 1 33 ? 172.532 155.130 85.610 1.00 71.55 33 J 1
513
+ ATOM 233 C CA . LYS J 1 33 ? 172.715 155.655 86.958 1.00 71.55 33 J 1
514
+ ATOM 234 C C . LYS J 1 33 ? 171.496 155.382 87.824 1.00 71.55 33 J 1
515
+ ATOM 235 O O . LYS J 1 33 ? 171.628 155.006 88.994 1.00 71.55 33 J 1
516
+ ATOM 236 C CB . LYS J 1 33 ? 173.001 157.155 86.900 1.00 71.55 33 J 1
517
+ ATOM 237 C CG . LYS J 1 33 ? 174.385 157.517 86.392 1.00 71.55 33 J 1
518
+ ATOM 238 C CD . LYS J 1 33 ? 174.482 159.007 86.104 1.00 71.55 33 J 1
519
+ ATOM 239 C CE . LYS J 1 33 ? 174.396 159.829 87.379 1.00 71.55 33 J 1
520
+ ATOM 240 N NZ . LYS J 1 33 ? 174.607 161.279 87.119 1.00 71.55 33 J 1
521
+ ATOM 241 N N . LEU J 1 34 ? 170.296 155.566 87.269 1.00 70.34 34 J 1
522
+ ATOM 242 C CA . LEU J 1 34 ? 169.095 155.291 88.045 1.00 70.34 34 J 1
523
+ ATOM 243 C C . LEU J 1 34 ? 168.944 153.807 88.358 1.00 70.34 34 J 1
524
+ ATOM 244 O O . LEU J 1 34 ? 168.263 153.458 89.327 1.00 70.34 34 J 1
525
+ ATOM 245 C CB . LEU J 1 34 ? 167.865 155.824 87.305 1.00 70.34 34 J 1
526
+ ATOM 246 C CG . LEU J 1 34 ? 166.493 155.819 87.987 1.00 70.34 34 J 1
527
+ ATOM 247 C CD1 . LEU J 1 34 ? 165.645 156.923 87.403 1.00 70.34 34 J 1
528
+ ATOM 248 C CD2 . LEU J 1 34 ? 165.773 154.496 87.808 1.00 70.34 34 J 1
529
+ ATOM 249 N N . ALA J 1 35 ? 169.587 152.930 87.582 1.00 66.69 35 J 1
530
+ ATOM 250 C CA . ALA J 1 35 ? 169.434 151.494 87.785 1.00 66.69 35 J 1
531
+ ATOM 251 C C . ALA J 1 35 ? 169.942 151.034 89.144 1.00 66.69 35 J 1
532
+ ATOM 252 O O . ALA J 1 35 ? 169.492 149.995 89.640 1.00 66.69 35 J 1
533
+ ATOM 253 C CB . ALA J 1 35 ? 170.153 150.726 86.678 1.00 66.69 35 J 1
534
+ ATOM 254 N N . ALA J 1 36 ? 170.865 151.775 89.759 1.00 67.09 36 J 1
535
+ ATOM 255 C CA . ALA J 1 36 ? 171.402 151.403 91.059 1.00 67.09 36 J 1
536
+ ATOM 256 C C . ALA J 1 36 ? 171.016 152.357 92.179 1.00 67.09 36 J 1
537
+ ATOM 257 O O . ALA J 1 36 ? 171.066 151.959 93.347 1.00 67.09 36 J 1
538
+ ATOM 258 C CB . ALA J 1 36 ? 172.933 151.307 90.996 1.00 67.09 36 J 1
539
+ ATOM 259 N N . LYS J 1 37 ? 170.632 153.590 91.860 1.00 66.94 37 J 1
540
+ ATOM 260 C CA . LYS J 1 37 ? 170.209 154.579 92.850 1.00 66.94 37 J 1
541
+ ATOM 261 C C . LYS J 1 37 ? 168.843 155.099 92.431 1.00 66.94 37 J 1
542
+ ATOM 262 O O . LYS J 1 37 ? 168.736 156.111 91.726 1.00 66.94 37 J 1
543
+ ATOM 263 C CB . LYS J 1 37 ? 171.229 155.709 92.979 1.00 66.94 37 J 1
544
+ ATOM 264 C CG . LYS J 1 37 ? 172.582 155.242 93.477 1.00 66.94 37 J 1
545
+ ATOM 265 C CD . LYS J 1 37 ? 172.450 154.552 94.823 1.00 66.94 37 J 1
546
+ ATOM 266 C CE . LYS J 1 37 ? 173.808 154.194 95.400 1.00 66.94 37 J 1
547
+ ATOM 267 N NZ . LYS J 1 37 ? 173.680 153.478 96.700 1.00 66.94 37 J 1
548
+ ATOM 268 N N . PRO J 1 38 ? 167.768 154.426 92.850 1.00 68.68 38 J 1
549
+ ATOM 269 C CA . PRO J 1 38 ? 166.442 154.759 92.315 1.00 68.68 38 J 1
550
+ ATOM 270 C C . PRO J 1 38 ? 165.804 155.974 92.952 1.00 68.68 38 J 1
551
+ ATOM 271 O O . PRO J 1 38 ? 165.061 156.684 92.265 1.00 68.68 38 J 1
552
+ ATOM 272 C CB . PRO J 1 38 ? 165.630 153.501 92.616 1.00 68.68 38 J 1
553
+ ATOM 273 C CG . PRO J 1 38 ? 166.222 153.021 93.902 1.00 68.68 38 J 1
554
+ ATOM 274 C CD . PRO J 1 38 ? 167.701 153.332 93.834 1.00 68.68 38 J 1
555
+ ATOM 275 N N . SER J 1 39 ? 166.056 156.240 94.230 1.00 70.54 39 J 1
556
+ ATOM 276 C CA . SER J 1 39 ? 165.383 157.320 94.933 1.00 70.54 39 J 1
557
+ ATOM 277 C C . SER J 1 39 ? 166.204 158.596 94.992 1.00 70.54 39 J 1
558
+ ATOM 278 O O . SER J 1 39 ? 165.705 159.612 95.488 1.00 70.54 39 J 1
559
+ ATOM 279 C CB . SER J 1 39 ? 165.027 156.887 96.360 1.00 70.54 39 J 1
560
+ ATOM 280 O OG . SER J 1 39 ? 166.196 156.601 97.104 1.00 70.54 39 J 1
561
+ ATOM 281 N N . ASP J 1 40 ? 167.425 158.579 94.500 1.00 71.51 40 J 1
562
+ ATOM 282 C CA . ASP J 1 40 ? 168.310 159.702 94.730 1.00 71.51 40 J 1
563
+ ATOM 283 C C . ASP J 1 40 ? 167.868 160.912 93.909 1.00 71.51 40 J 1
564
+ ATOM 284 O O . ASP J 1 40 ? 167.348 160.766 92.800 1.00 71.51 40 J 1
565
+ ATOM 285 C CB . ASP J 1 40 ? 169.741 159.324 94.374 1.00 71.51 40 J 1
566
+ ATOM 286 C CG . ASP J 1 40 ? 170.519 158.829 95.569 1.00 71.51 40 J 1
567
+ ATOM 287 O OD1 . ASP J 1 40 ? 170.038 159.024 96.701 1.00 71.51 40 J 1
568
+ ATOM 288 O OD2 . ASP J 1 40 ? 171.606 158.245 95.379 1.00 71.51 40 J 1
569
+ ATOM 289 N N . PRO J 1 41 ? 168.040 162.115 94.445 1.00 72.15 41 J 1
570
+ ATOM 290 C CA . PRO J 1 41 ? 167.725 163.323 93.677 1.00 72.15 41 J 1
571
+ ATOM 291 C C . PRO J 1 41 ? 168.792 163.594 92.630 1.00 72.15 41 J 1
572
+ ATOM 292 O O . PRO J 1 41 ? 169.806 162.902 92.531 1.00 72.15 41 J 1
573
+ ATOM 293 C CB . PRO J 1 41 ? 167.709 164.427 94.737 1.00 72.15 41 J 1
574
+ ATOM 294 C CG . PRO J 1 41 ? 167.558 163.713 96.031 1.00 72.15 41 J 1
575
+ ATOM 295 C CD . PRO J 1 41 ? 168.278 162.420 95.862 1.00 72.15 41 J 1
576
+ ATOM 296 N N . ALA J 1 42 ? 168.537 164.627 91.831 1.00 68.90 42 J 1
577
+ ATOM 297 C CA . ALA J 1 42 ? 169.449 165.183 90.837 1.00 68.90 42 J 1
578
+ ATOM 298 C C . ALA J 1 42 ? 169.710 164.237 89.674 1.00 68.90 42 J 1
579
+ ATOM 299 O O . ALA J 1 42 ? 170.388 164.632 88.718 1.00 68.90 42 J 1
580
+ ATOM 300 C CB . ALA J 1 42 ? 170.795 165.605 91.445 1.00 68.90 42 J 1
581
+ ATOM 301 N N . LEU J 1 43 ? 169.199 163.008 89.712 1.00 66.88 43 J 1
582
+ ATOM 302 C CA . LEU J 1 43 ? 169.214 162.168 88.523 1.00 66.88 43 J 1
583
+ ATOM 303 C C . LEU J 1 43 ? 167.793 161.722 88.216 1.00 66.88 43 J 1
584
+ ATOM 304 O O . LEU J 1 43 ? 167.436 161.561 87.048 1.00 66.88 43 J 1
585
+ ATOM 305 C CB . LEU J 1 43 ? 170.136 160.956 88.678 1.00 66.88 43 J 1
586
+ ATOM 306 C CG . LEU J 1 43 ? 169.723 159.783 89.565 1.00 66.88 43 J 1
587
+ ATOM 307 C CD1 . LEU J 1 43 ? 170.578 158.575 89.251 1.00 66.88 43 J 1
588
+ ATOM 308 C CD2 . LEU J 1 43 ? 169.875 160.147 91.011 1.00 66.88 43 J 1
589
+ ATOM 309 N N . LEU J 1 44 ? 166.976 161.526 89.255 1.00 62.81 44 J 1
590
+ ATOM 310 C CA . LEU J 1 44 ? 165.564 161.231 89.034 1.00 62.81 44 J 1
591
+ ATOM 311 C C . LEU J 1 44 ? 164.891 162.349 88.255 1.00 62.81 44 J 1
592
+ ATOM 312 O O . LEU J 1 44 ? 164.229 162.103 87.240 1.00 62.81 44 J 1
593
+ ATOM 313 C CB . LEU J 1 44 ? 164.850 161.025 90.366 1.00 62.81 44 J 1
594
+ ATOM 314 C CG . LEU J 1 44 ? 164.662 159.603 90.876 1.00 62.81 44 J 1
595
+ ATOM 315 C CD1 . LEU J 1 44 ? 164.144 159.655 92.300 1.00 62.81 44 J 1
596
+ ATOM 316 C CD2 . LEU J 1 44 ? 163.706 158.844 89.980 1.00 62.81 44 J 1
597
+ ATOM 317 N N . ALA J 1 45 ? 165.051 163.589 88.717 1.00 63.51 45 J 1
598
+ ATOM 318 C CA . ALA J 1 45 ? 164.565 164.727 87.951 1.00 63.51 45 J 1
599
+ ATOM 319 C C . ALA J 1 45 ? 165.278 164.805 86.611 1.00 63.51 45 J 1
600
+ ATOM 320 O O . ALA J 1 45 ? 164.660 165.085 85.577 1.00 63.51 45 J 1
601
+ ATOM 321 C CB . ALA J 1 45 ? 164.759 166.015 88.748 1.00 63.51 45 J 1
602
+ ATOM 322 N N . ALA J 1 46 ? 166.587 164.547 86.611 1.00 63.88 46 J 1
603
+ ATOM 323 C CA . ALA J 1 46 ? 167.322 164.470 85.356 1.00 63.88 46 J 1
604
+ ATOM 324 C C . ALA J 1 46 ? 166.799 163.335 84.489 1.00 63.88 46 J 1
605
+ ATOM 325 O O . ALA J 1 46 ? 166.675 163.486 83.270 1.00 63.88 46 J 1
606
+ ATOM 326 C CB . ALA J 1 46 ? 168.815 164.297 85.627 1.00 63.88 46 J 1
607
+ ATOM 327 N N . TYR J 1 47 ? 166.483 162.190 85.100 1.00 60.60 47 J 1
608
+ ATOM 328 C CA . TYR J 1 47 ? 165.886 161.089 84.350 1.00 60.60 47 J 1
609
+ ATOM 329 C C . TYR J 1 47 ? 164.618 161.528 83.639 1.00 60.60 47 J 1
610
+ ATOM 330 O O . TYR J 1 47 ? 164.476 161.336 82.427 1.00 60.60 47 J 1
611
+ ATOM 331 C CB . TYR J 1 47 ? 165.579 159.921 85.282 1.00 60.60 47 J 1
612
+ ATOM 332 C CG . TYR J 1 47 ? 164.985 158.730 84.578 1.00 60.60 47 J 1
613
+ ATOM 333 C CD1 . TYR J 1 47 ? 165.771 157.885 83.811 1.00 60.60 47 J 1
614
+ ATOM 334 C CD2 . TYR J 1 47 ? 163.629 158.457 84.675 1.00 60.60 47 J 1
615
+ ATOM 335 C CE1 . TYR J 1 47 ? 165.223 156.798 83.168 1.00 60.60 47 J 1
616
+ ATOM 336 C CE2 . TYR J 1 47 ? 163.073 157.376 84.037 1.00 60.60 47 J 1
617
+ ATOM 337 C CZ . TYR J 1 47 ? 163.872 156.550 83.285 1.00 60.60 47 J 1
618
+ ATOM 338 O OH . TYR J 1 47 ? 163.317 155.471 82.646 1.00 60.60 47 J 1
619
+ ATOM 339 N N . GLN J 1 48 ? 163.681 162.118 84.380 1.00 61.22 48 J 1
620
+ ATOM 340 C CA . GLN J 1 48 ? 162.418 162.506 83.769 1.00 61.22 48 J 1
621
+ ATOM 341 C C . GLN J 1 48 ? 162.618 163.572 82.703 1.00 61.22 48 J 1
622
+ ATOM 342 O O . GLN J 1 48 ? 162.021 163.485 81.623 1.00 61.22 48 J 1
623
+ ATOM 343 C CB . GLN J 1 48 ? 161.441 163.000 84.832 1.00 61.22 48 J 1
624
+ ATOM 344 C CG . GLN J 1 48 ? 160.073 163.333 84.276 1.00 61.22 48 J 1
625
+ ATOM 345 C CD . GLN J 1 48 ? 159.096 163.765 85.344 1.00 61.22 48 J 1
626
+ ATOM 346 O OE1 . GLN J 1 48 ? 159.445 163.860 86.519 1.00 61.22 48 J 1
627
+ ATOM 347 N NE2 . GLN J 1 48 ? 157.858 164.028 84.941 1.00 61.22 48 J 1
628
+ ATOM 348 N N . SER J 1 49 ? 163.461 164.571 82.975 1.00 66.01 49 J 1
629
+ ATOM 349 C CA . SER J 1 49 ? 163.666 165.643 82.010 1.00 66.01 49 J 1
630
+ ATOM 350 C C . SER J 1 49 ? 164.298 165.117 80.729 1.00 66.01 49 J 1
631
+ ATOM 351 O O . SER J 1 49 ? 163.843 165.437 79.628 1.00 66.01 49 J 1
632
+ ATOM 352 C CB . SER J 1 49 ? 164.529 166.744 82.623 1.00 66.01 49 J 1
633
+ ATOM 353 O OG . SER J 1 49 ? 164.839 167.732 81.658 1.00 66.01 49 J 1
634
+ ATOM 354 N N . LYS J 1 50 ? 165.348 164.301 80.851 1.00 62.23 50 J 1
635
+ ATOM 355 C CA . LYS J 1 50 ? 165.992 163.755 79.662 1.00 62.23 50 J 1
636
+ ATOM 356 C C . LYS J 1 50 ? 165.074 162.806 78.908 1.00 62.23 50 J 1
637
+ ATOM 357 O O . LYS J 1 50 ? 165.080 162.796 77.674 1.00 62.23 50 J 1
638
+ ATOM 358 C CB . LYS J 1 50 ? 167.292 163.045 80.036 1.00 62.23 50 J 1
639
+ ATOM 359 C CG . LYS J 1 50 ? 168.320 163.948 80.688 1.00 62.23 50 J 1
640
+ ATOM 360 C CD . LYS J 1 50 ? 168.804 165.014 79.725 1.00 62.23 50 J 1
641
+ ATOM 361 C CE . LYS J 1 50 ? 169.815 165.927 80.391 1.00 62.23 50 J 1
642
+ ATOM 362 N NZ . LYS J 1 50 ? 171.093 165.218 80.672 1.00 62.23 50 J 1
643
+ ATOM 363 N N . LEU J 1 51 ? 164.282 162.004 79.620 1.00 55.13 51 J 1
644
+ ATOM 364 C CA . LEU J 1 51 ? 163.386 161.078 78.942 1.00 55.13 51 J 1
645
+ ATOM 365 C C . LEU J 1 51 ? 162.306 161.830 78.175 1.00 55.13 51 J 1
646
+ ATOM 366 O O . LEU J 1 51 ? 161.984 161.482 77.034 1.00 55.13 51 J 1
647
+ ATOM 367 C CB . LEU J 1 51 ? 162.773 160.118 79.955 1.00 55.13 51 J 1
648
+ ATOM 368 C CG . LEU J 1 51 ? 161.865 159.044 79.373 1.00 55.13 51 J 1
649
+ ATOM 369 C CD1 . LEU J 1 51 ? 162.675 158.158 78.450 1.00 55.13 51 J 1
650
+ ATOM 370 C CD2 . LEU J 1 51 ? 161.246 158.227 80.487 1.00 55.13 51 J 1
651
+ ATOM 371 N N . SER J 1 52 ? 161.739 162.876 78.783 1.00 58.66 52 J 1
652
+ ATOM 372 C CA . SER J 1 52 ? 160.769 163.700 78.069 1.00 58.66 52 J 1
653
+ ATOM 373 C C . SER J 1 52 ? 161.418 164.423 76.896 1.00 58.66 52 J 1
654
+ ATOM 374 O O . SER J 1 52 ? 160.815 164.553 75.825 1.00 58.66 52 J 1
655
+ ATOM 375 C CB . SER J 1 52 ? 160.119 164.699 79.023 1.00 58.66 52 J 1
656
+ ATOM 376 O OG . SER J 1 52 ? 159.266 164.041 79.940 1.00 58.66 52 J 1
657
+ ATOM 377 N N . GLU J 1 53 ? 162.646 164.910 77.081 1.00 65.81 53 J 1
658
+ ATOM 378 C CA . GLU J 1 53 ? 163.360 165.549 75.985 1.00 65.81 53 J 1
659
+ ATOM 379 C C . GLU J 1 53 ? 163.519 164.593 74.816 1.00 65.81 53 J 1
660
+ ATOM 380 O O . GLU J 1 53 ? 163.285 164.966 73.663 1.00 65.81 53 J 1
661
+ ATOM 381 C CB . GLU J 1 53 ? 164.729 166.027 76.468 1.00 65.81 53 J 1
662
+ ATOM 382 C CG . GLU J 1 53 ? 164.823 167.501 76.834 1.00 65.81 53 J 1
663
+ ATOM 383 C CD . GLU J 1 53 ? 164.613 168.437 75.659 1.00 65.81 53 J 1
664
+ ATOM 384 O OE1 . GLU J 1 53 ? 164.395 167.965 74.524 1.00 65.81 53 J 1
665
+ ATOM 385 O OE2 . GLU J 1 53 ? 164.659 169.667 75.875 1.00 65.81 53 J 1
666
+ ATOM 386 N N . TYR J 1 54 ? 163.901 163.351 75.104 1.00 59.32 54 J 1
667
+ ATOM 387 C CA . TYR J 1 54 ? 164.070 162.346 74.065 1.00 59.32 54 J 1
668
+ ATOM 388 C C . TYR J 1 54 ? 162.742 162.013 73.396 1.00 59.32 54 J 1
669
+ ATOM 389 O O . TYR J 1 54 ? 162.683 161.828 72.175 1.00 59.32 54 J 1
670
+ ATOM 390 C CB . TYR J 1 54 ? 164.710 161.104 74.676 1.00 59.32 54 J 1
671
+ ATOM 391 C CG . TYR J 1 54 ? 164.986 159.991 73.707 1.00 59.32 54 J 1
672
+ ATOM 392 C CD1 . TYR J 1 54 ? 166.049 160.070 72.828 1.00 59.32 54 J 1
673
+ ATOM 393 C CD2 . TYR J 1 54 ? 164.208 158.845 73.697 1.00 59.32 54 J 1
674
+ ATOM 394 C CE1 . TYR J 1 54 ? 166.316 159.055 71.945 1.00 59.32 54 J 1
675
+ ATOM 395 C CE2 . TYR J 1 54 ? 164.470 157.819 72.819 1.00 59.32 54 J 1
676
+ ATOM 396 C CZ . TYR J 1 54 ? 165.527 157.929 71.944 1.00 59.32 54 J 1
677
+ ATOM 397 O OH . TYR J 1 54 ? 165.800 156.912 71.061 1.00 59.32 54 J 1
678
+ ATOM 398 N N . ASN J 1 55 ? 161.668 161.931 74.183 1.00 55.92 55 J 1
679
+ ATOM 399 C CA . ASN J 1 55 ? 160.344 161.659 73.629 1.00 55.92 55 J 1
680
+ ATOM 400 C C . ASN J 1 55 ? 159.925 162.748 72.647 1.00 55.92 55 J 1
681
+ ATOM 401 O O . ASN J 1 55 ? 159.524 162.458 71.511 1.00 55.92 55 J 1
682
+ ATOM 402 C CB . ASN J 1 55 ? 159.340 161.529 74.777 1.00 55.92 55 J 1
683
+ ATOM 403 C CG . ASN J 1 55 ? 157.922 161.278 74.306 1.00 55.92 55 J 1
684
+ ATOM 404 O OD1 . ASN J 1 55 ? 157.659 161.123 73.117 1.00 55.92 55 J 1
685
+ ATOM 405 N ND2 . ASN J 1 55 ? 156.998 161.213 75.253 1.00 55.92 55 J 1
686
+ ATOM 406 N N . LEU J 1 56 ? 160.012 164.010 73.071 1.00 57.85 56 J 1
687
+ ATOM 407 C CA . LEU J 1 56 ? 159.659 165.115 72.183 1.00 57.85 56 J 1
688
+ ATOM 408 C C . LEU J 1 56 ? 160.587 165.185 70.980 1.00 57.85 56 J 1
689
+ ATOM 409 O O . LEU J 1 56 ? 160.148 165.503 69.871 1.00 57.85 56 J 1
690
+ ATOM 410 C CB . LEU J 1 56 ? 159.665 166.437 72.945 1.00 57.85 56 J 1
691
+ ATOM 411 C CG . LEU J 1 56 ? 158.428 166.781 73.772 1.00 57.85 56 J 1
692
+ ATOM 412 C CD1 . LEU J 1 56 ? 158.257 165.920 74.998 1.00 57.85 56 J 1
693
+ ATOM 413 C CD2 . LEU J 1 56 ? 158.539 168.224 74.179 1.00 57.85 56 J 1
694
+ ATOM 414 N N . TYR J 1 57 ? 161.872 164.895 71.176 1.00 60.05 57 J 1
695
+ ATOM 415 C CA . TYR J 1 57 ? 162.800 164.844 70.054 1.00 60.05 57 J 1
696
+ ATOM 416 C C . TYR J 1 57 ? 162.362 163.824 69.013 1.00 60.05 57 J 1
697
+ ATOM 417 O O . TYR J 1 57 ? 162.299 164.134 67.817 1.00 60.05 57 J 1
698
+ ATOM 418 C CB . TYR J 1 57 ? 164.203 164.534 70.578 1.00 60.05 57 J 1
699
+ ATOM 419 C CG . TYR J 1 57 ? 165.239 164.289 69.513 1.00 60.05 57 J 1
700
+ ATOM 420 C CD1 . TYR J 1 57 ? 165.328 163.058 68.881 1.00 60.05 57 J 1
701
+ ATOM 421 C CD2 . TYR J 1 57 ? 166.114 165.288 69.127 1.00 60.05 57 J 1
702
+ ATOM 422 C CE1 . TYR J 1 57 ? 166.257 162.825 67.909 1.00 60.05 57 J 1
703
+ ATOM 423 C CE2 . TYR J 1 57 ? 167.052 165.063 68.149 1.00 60.05 57 J 1
704
+ ATOM 424 C CZ . TYR J 1 57 ? 167.118 163.828 67.545 1.00 60.05 57 J 1
705
+ ATOM 425 O OH . TYR J 1 57 ? 168.044 163.579 66.567 1.00 60.05 57 J 1
706
+ ATOM 426 N N . ARG J 1 58 ? 162.069 162.598 69.446 1.00 58.38 58 J 1
707
+ ATOM 427 C CA . ARG J 1 58 ? 161.680 161.562 68.498 1.00 58.38 58 J 1
708
+ ATOM 428 C C . ARG J 1 58 ? 160.373 161.916 67.804 1.00 58.38 58 J 1
709
+ ATOM 429 O O . ARG J 1 58 ? 160.238 161.726 66.587 1.00 58.38 58 J 1
710
+ ATOM 430 C CB . ARG J 1 58 ? 161.566 160.217 69.208 1.00 58.38 58 J 1
711
+ ATOM 431 C CG . ARG J 1 58 ? 162.900 159.553 69.491 1.00 58.38 58 J 1
712
+ ATOM 432 C CD . ARG J 1 58 ? 163.871 159.707 68.335 1.00 58.38 58 J 1
713
+ ATOM 433 N NE . ARG J 1 58 ? 165.241 159.437 68.753 1.00 58.38 58 J 1
714
+ ATOM 434 C CZ . ARG J 1 58 ? 166.293 159.472 67.948 1.00 58.38 58 J 1
715
+ ATOM 435 N NH1 . ARG J 1 58 ? 166.171 159.760 66.663 1.00 58.38 58 J 1
716
+ ATOM 436 N NH2 . ARG J 1 58 ? 167.498 159.214 68.445 1.00 58.38 58 J 1
717
+ ATOM 437 N N . ASN J 1 59 ? 159.399 162.432 68.558 1.00 58.16 59 J 1
718
+ ATOM 438 C CA . ASN J 1 59 ? 158.133 162.817 67.945 1.00 58.16 59 J 1
719
+ ATOM 439 C C . ASN J 1 59 ? 158.327 163.928 66.924 1.00 58.16 59 J 1
720
+ ATOM 440 O O . ASN J 1 59 ? 157.735 163.893 65.839 1.00 58.16 59 J 1
721
+ ATOM 441 C CB . ASN J 1 59 ? 157.133 163.240 69.017 1.00 58.16 59 J 1
722
+ ATOM 442 C CG . ASN J 1 59 ? 156.401 162.065 69.619 1.00 58.16 59 J 1
723
+ ATOM 443 O OD1 . ASN J 1 59 ? 156.905 161.403 70.521 1.00 58.16 59 J 1
724
+ ATOM 444 N ND2 . ASN J 1 59 ? 155.203 161.796 69.117 1.00 58.16 59 J 1
725
+ ATOM 445 N N . ALA J 1 60 ? 159.154 164.925 67.250 1.00 62.06 60 J 1
726
+ ATOM 446 C CA . ALA J 1 60 ? 159.407 166.012 66.314 1.00 62.06 60 J 1
727
+ ATOM 447 C C . ALA J 1 60 ? 160.081 165.504 65.052 1.00 62.06 60 J 1
728
+ ATOM 448 O O . ALA J 1 60 ? 159.709 165.896 63.940 1.00 62.06 60 J 1
729
+ ATOM 449 C CB . ALA J 1 60 ? 160.257 167.092 66.980 1.00 62.06 60 J 1
730
+ ATOM 450 N N . GLN J 1 61 ? 161.067 164.621 65.203 1.00 65.39 61 J 1
731
+ ATOM 451 C CA . GLN J 1 61 ? 161.748 164.072 64.037 1.00 65.39 61 J 1
732
+ ATOM 452 C C . GLN J 1 61 ? 160.774 163.310 63.148 1.00 65.39 61 J 1
733
+ ATOM 453 O O . GLN J 1 61 ? 160.735 163.512 61.925 1.00 65.39 61 J 1
734
+ ATOM 454 C CB . GLN J 1 61 ? 162.895 163.171 64.494 1.00 65.39 61 J 1
735
+ ATOM 455 C CG . GLN J 1 61 ? 164.044 163.067 63.519 1.00 65.39 61 J 1
736
+ ATOM 456 C CD . GLN J 1 61 ? 165.192 162.262 64.080 1.00 65.39 61 J 1
737
+ ATOM 457 O OE1 . GLN J 1 61 ? 166.240 162.807 64.420 1.00 65.39 61 J 1
738
+ ATOM 458 N NE2 . GLN J 1 61 ? 164.998 160.957 64.189 1.00 65.39 61 J 1
739
+ ATOM 459 N N . SER J 1 62 ? 159.959 162.442 63.753 1.00 64.39 62 J 1
740
+ ATOM 460 C CA . SER J 1 62 ? 159.025 161.644 62.969 1.00 64.39 62 J 1
741
+ ATOM 461 C C . SER J 1 62 ? 157.998 162.522 62.267 1.00 64.39 62 J 1
742
+ ATOM 462 O O . SER J 1 62 ? 157.716 162.331 61.078 1.00 64.39 62 J 1
743
+ ATOM 463 C CB . SER J 1 62 ? 158.333 160.619 63.865 1.00 64.39 62 J 1
744
+ ATOM 464 O OG . SER J 1 62 ? 157.410 159.843 63.125 1.00 64.39 62 J 1
745
+ ATOM 465 N N . ASN J 1 63 ? 157.438 163.504 62.976 1.00 65.16 63 J 1
746
+ ATOM 466 C CA . ASN J 1 63 ? 156.437 164.361 62.357 1.00 65.16 63 J 1
747
+ ATOM 467 C C . ASN J 1 63 ? 157.033 165.219 61.252 1.00 65.16 63 J 1
748
+ ATOM 468 O O . ASN J 1 63 ? 156.389 165.421 60.216 1.00 65.16 63 J 1
749
+ ATOM 469 C CB . ASN J 1 63 ? 155.767 165.233 63.413 1.00 65.16 63 J 1
750
+ ATOM 470 C CG . ASN J 1 63 ? 154.853 164.439 64.316 1.00 65.16 63 J 1
751
+ ATOM 471 O OD1 . ASN J 1 63 ? 154.525 163.290 64.026 1.00 65.16 63 J 1
752
+ ATOM 472 N ND2 . ASN J 1 63 ? 154.434 165.045 65.416 1.00 65.16 63 J 1
753
+ ATOM 473 N N . THR J 1 64 ? 158.252 165.729 61.444 1.00 66.60 64 J 1
754
+ ATOM 474 C CA . THR J 1 64 ? 158.875 166.543 60.408 1.00 66.60 64 J 1
755
+ ATOM 475 C C . THR J 1 64 ? 159.147 165.725 59.153 1.00 66.60 64 J 1
756
+ ATOM 476 O O . THR J 1 64 ? 158.846 166.169 58.035 1.00 66.60 64 J 1
757
+ ATOM 477 C CB . THR J 1 64 ? 160.169 167.158 60.935 1.00 66.60 64 J 1
758
+ ATOM 478 O OG1 . THR J 1 64 ? 159.877 167.977 62.072 1.00 66.60 64 J 1
759
+ ATOM 479 C CG2 . THR J 1 64 ? 160.819 168.011 59.865 1.00 66.60 64 J 1
760
+ ATOM 480 N N . VAL J 1 65 ? 159.707 164.523 59.310 1.00 64.00 65 J 1
761
+ ATOM 481 C CA . VAL J 1 65 ? 159.978 163.718 58.124 1.00 64.00 65 J 1
762
+ ATOM 482 C C . VAL J 1 65 ? 158.671 163.316 57.452 1.00 64.00 65 J 1
763
+ ATOM 483 O O . VAL J 1 65 ? 158.580 163.285 56.220 1.00 64.00 65 J 1
764
+ ATOM 484 C CB . VAL J 1 65 ? 160.858 162.499 58.464 1.00 64.00 65 J 1
765
+ ATOM 485 C CG1 . VAL J 1 65 ? 162.203 162.957 58.999 1.00 64.00 65 J 1
766
+ ATOM 486 C CG2 . VAL J 1 65 ? 160.176 161.571 59.442 1.00 64.00 65 J 1
767
+ ATOM 487 N N . LYS J 1 66 ? 157.626 163.047 58.241 1.00 65.12 66 J 1
768
+ ATOM 488 C CA . LYS J 1 66 ? 156.341 162.691 57.649 1.00 65.12 66 J 1
769
+ ATOM 489 C C . LYS J 1 66 ? 155.745 163.847 56.855 1.00 65.12 66 J 1
770
+ ATOM 490 O O . LYS J 1 66 ? 155.240 163.645 55.745 1.00 65.12 66 J 1
771
+ ATOM 491 C CB . LYS J 1 66 ? 155.366 162.235 58.733 1.00 65.12 66 J 1
772
+ ATOM 492 C CG . LYS J 1 66 ? 153.992 161.855 58.202 1.00 65.12 66 J 1
773
+ ATOM 493 C CD . LYS J 1 66 ? 154.088 160.891 57.032 1.00 65.12 66 J 1
774
+ ATOM 494 C CE . LYS J 1 66 ? 152.711 160.542 56.496 1.00 65.12 66 J 1
775
+ ATOM 495 N NZ . LYS J 1 66 ? 151.754 160.214 57.587 1.00 65.12 66 J 1
776
+ ATOM 496 N N . VAL J 1 67 ? 155.779 165.064 57.402 1.00 65.92 67 J 1
777
+ ATOM 497 C CA . VAL J 1 67 ? 155.163 166.185 56.696 1.00 65.92 67 J 1
778
+ ATOM 498 C C . VAL J 1 67 ? 155.958 166.528 55.442 1.00 65.92 67 J 1
779
+ ATOM 499 O O . VAL J 1 67 ? 155.381 166.863 54.397 1.00 65.92 67 J 1
780
+ ATOM 500 C CB . VAL J 1 67 ? 154.993 167.401 57.629 1.00 65.92 67 J 1
781
+ ATOM 501 C CG1 . VAL J 1 67 ? 156.328 167.937 58.097 1.00 65.92 67 J 1
782
+ ATOM 502 C CG2 . VAL J 1 67 ? 154.200 168.492 56.929 1.00 65.92 67 J 1
783
+ ATOM 503 N N . PHE J 1 68 ? 157.288 166.435 55.509 1.00 66.56 68 J 1
784
+ ATOM 504 C CA . PHE J 1 68 ? 158.076 166.682 54.308 1.00 66.56 68 J 1
785
+ ATOM 505 C C . PHE J 1 68 ? 157.826 165.606 53.260 1.00 66.56 68 J 1
786
+ ATOM 506 O O . PHE J 1 68 ? 157.772 165.899 52.059 1.00 66.56 68 J 1
787
+ ATOM 507 C CB . PHE J 1 68 ? 159.558 166.785 54.657 1.00 66.56 68 J 1
788
+ ATOM 508 C CG . PHE J 1 68 ? 159.991 168.177 55.012 1.00 66.56 68 J 1
789
+ ATOM 509 C CD1 . PHE J 1 68 ? 160.099 168.568 56.333 1.00 66.56 68 J 1
790
+ ATOM 510 C CD2 . PHE J 1 68 ? 160.268 169.101 54.022 1.00 66.56 68 J 1
791
+ ATOM 511 C CE1 . PHE J 1 68 ? 160.490 169.850 56.660 1.00 66.56 68 J 1
792
+ ATOM 512 C CE2 . PHE J 1 68 ? 160.658 170.384 54.343 1.00 66.56 68 J 1
793
+ ATOM 513 C CZ . PHE J 1 68 ? 160.769 170.758 55.664 1.00 66.56 68 J 1
794
+ ATOM 514 N N . LYS J 1 69 ? 157.646 164.356 53.694 1.00 70.04 69 J 1
795
+ ATOM 515 C CA . LYS J 1 69 ? 157.283 163.302 52.755 1.00 70.04 69 J 1
796
+ ATOM 516 C C . LYS J 1 69 ? 155.929 163.584 52.123 1.00 70.04 69 J 1
797
+ ATOM 517 O O . LYS J 1 69 ? 155.731 163.326 50.934 1.00 70.04 69 J 1
798
+ ATOM 518 C CB . LYS J 1 69 ? 157.291 161.944 53.465 1.00 70.04 69 J 1
799
+ ATOM 519 C CG . LYS J 1 69 ? 157.067 160.718 52.570 1.00 70.04 69 J 1
800
+ ATOM 520 C CD . LYS J 1 69 ? 155.593 160.436 52.289 1.00 70.04 69 J 1
801
+ ATOM 521 C CE . LYS J 1 69 ? 155.424 159.353 51.239 1.00 70.04 69 J 1
802
+ ATOM 522 N NZ . LYS J 1 69 ? 153.999 158.953 51.085 1.00 70.04 69 J 1
803
+ ATOM 523 N N . ASP J 1 70 ? 154.979 164.093 52.908 1.00 70.15 70 J 1
804
+ ATOM 524 C CA . ASP J 1 70 ? 153.667 164.418 52.356 1.00 70.15 70 J 1
805
+ ATOM 525 C C . ASP J 1 70 ? 153.770 165.503 51.293 1.00 70.15 70 J 1
806
+ ATOM 526 O O . ASP J 1 70 ? 153.172 165.386 50.216 1.00 70.15 70 J 1
807
+ ATOM 527 C CB . ASP J 1 70 ? 152.719 164.850 53.472 1.00 70.15 70 J 1
808
+ ATOM 528 C CG . ASP J 1 70 ? 152.353 163.710 54.396 1.00 70.15 70 J 1
809
+ ATOM 529 O OD1 . ASP J 1 70 ? 152.337 162.552 53.930 1.00 70.15 70 J 1
810
+ ATOM 530 O OD2 . ASP J 1 70 ? 152.082 163.970 55.587 1.00 70.15 70 J 1
811
+ ATOM 531 N N . ILE J 1 71 ? 154.538 166.558 51.574 1.00 64.31 71 J 1
812
+ ATOM 532 C CA . ILE J 1 71 ? 154.721 167.619 50.588 1.00 64.31 71 J 1
813
+ ATOM 533 C C . ILE J 1 71 ? 155.375 167.070 49.326 1.00 64.31 71 J 1
814
+ ATOM 534 O O . ILE J 1 71 ? 154.941 167.362 48.203 1.00 64.31 71 J 1
815
+ ATOM 535 C CB . ILE J 1 71 ? 155.545 168.772 51.188 1.00 64.31 71 J 1
816
+ ATOM 536 C CG1 . ILE J 1 71 ? 154.860 169.327 52.435 1.00 64.31 71 J 1
817
+ ATOM 537 C CG2 . ILE J 1 71 ? 155.751 169.866 50.159 1.00 64.31 71 J 1
818
+ ATOM 538 C CD1 . ILE J 1 71 ? 155.778 170.127 53.325 1.00 64.31 71 J 1
819
+ ATOM 539 N N . ASP J 1 72 ? 156.423 166.261 49.492 1.00 68.85 72 J 1
820
+ ATOM 540 C CA . ASP J 1 72 ? 157.144 165.727 48.343 1.00 68.85 72 J 1
821
+ ATOM 541 C C . ASP J 1 72 ? 156.266 164.795 47.517 1.00 68.85 72 J 1
822
+ ATOM 542 O O . ASP J 1 72 ? 156.316 164.814 46.282 1.00 68.85 72 J 1
823
+ ATOM 543 C CB . ASP J 1 72 ? 158.398 165.005 48.825 1.00 68.85 72 J 1
824
+ ATOM 544 C CG . ASP J 1 72 ? 159.551 165.947 49.062 1.00 68.85 72 J 1
825
+ ATOM 545 O OD1 . ASP J 1 72 ? 160.691 165.462 49.183 1.00 68.85 72 J 1
826
+ ATOM 546 O OD2 . ASP J 1 72 ? 159.318 167.171 49.125 1.00 68.85 72 J 1
827
+ ATOM 547 N N . ALA J 1 73 ? 155.464 163.963 48.181 1.00 65.97 73 J 1
828
+ ATOM 548 C CA . ALA J 1 73 ? 154.553 163.081 47.466 1.00 65.97 73 J 1
829
+ ATOM 549 C C . ALA J 1 73 ? 153.501 163.878 46.715 1.00 65.97 73 J 1
830
+ ATOM 550 O O . ALA J 1 73 ? 153.128 163.520 45.593 1.00 65.97 73 J 1
831
+ ATOM 551 C CB . ALA J 1 73 ? 153.894 162.103 48.437 1.00 65.97 73 J 1
832
+ ATOM 552 N N . ALA J 1 74 ? 153.004 164.960 47.319 1.00 64.99 74 J 1
833
+ ATOM 553 C CA . ALA J 1 74 ? 152.063 165.819 46.610 1.00 64.99 74 J 1
834
+ ATOM 554 C C . ALA J 1 74 ? 152.700 166.405 45.358 1.00 64.99 74 J 1
835
+ ATOM 555 O O . ALA J 1 74 ? 152.080 166.428 44.288 1.00 64.99 74 J 1
836
+ ATOM 556 C CB . ALA J 1 74 ? 151.567 166.931 47.533 1.00 64.99 74 J 1
837
+ ATOM 557 N N . ILE J 1 75 ? 153.947 166.864 45.469 1.00 67.26 75 J 1
838
+ ATOM 558 C CA . ILE J 1 75 ? 154.634 167.425 44.307 1.00 67.26 75 J 1
839
+ ATOM 559 C C . ILE J 1 75 ? 154.797 166.367 43.222 1.00 67.26 75 J 1
840
+ ATOM 560 O O . ILE J 1 75 ? 154.522 166.615 42.042 1.00 67.26 75 J 1
841
+ ATOM 561 C CB . ILE J 1 75 ? 155.993 168.017 44.717 1.00 67.26 75 J 1
842
+ ATOM 562 C CG1 . ILE J 1 75 ? 155.807 169.113 45.765 1.00 67.26 75 J 1
843
+ ATOM 563 C CG2 . ILE J 1 75 ? 156.721 168.564 43.502 1.00 67.26 75 J 1
844
+ ATOM 564 C CD1 . ILE J 1 75 ? 154.983 170.274 45.286 1.00 67.26 75 J 1
845
+ ATOM 565 N N . ILE J 1 76 ? 155.244 165.170 43.607 1.00 71.16 76 J 1
846
+ ATOM 566 C CA . ILE J 1 76 ? 155.503 164.119 42.627 1.00 71.16 76 J 1
847
+ ATOM 567 C C . ILE J 1 76 ? 154.216 163.694 41.935 1.00 71.16 76 J 1
848
+ ATOM 568 O O . ILE J 1 76 ? 154.164 163.579 40.705 1.00 71.16 76 J 1
849
+ ATOM 569 C CB . ILE J 1 76 ? 156.205 162.925 43.299 1.00 71.16 76 J 1
850
+ ATOM 570 C CG1 . ILE J 1 76 ? 157.602 163.325 43.761 1.00 71.16 76 J 1
851
+ ATOM 571 C CG2 . ILE J 1 76 ? 156.302 161.755 42.342 1.00 71.16 76 J 1
852
+ ATOM 572 C CD1 . ILE J 1 76 ? 158.489 163.764 42.631 1.00 71.16 76 J 1
853
+ ATOM 573 N N . GLN J 1 77 ? 153.158 163.457 42.708 1.00 74.01 77 J 1
854
+ ATOM 574 C CA . GLN J 1 77 ? 151.870 163.093 42.139 1.00 74.01 77 J 1
855
+ ATOM 575 C C . GLN J 1 77 ? 151.276 164.203 41.291 1.00 74.01 77 J 1
856
+ ATOM 576 O O . GLN J 1 77 ? 150.522 163.917 40.355 1.00 74.01 77 J 1
857
+ ATOM 577 C CB . GLN J 1 77 ? 150.903 162.718 43.266 1.00 74.01 77 J 1
858
+ ATOM 578 C CG . GLN J 1 77 ? 149.608 162.063 42.818 1.00 74.01 77 J 1
859
+ ATOM 579 C CD . GLN J 1 77 ? 148.547 163.075 42.436 1.00 74.01 77 J 1
860
+ ATOM 580 O OE1 . GLN J 1 77 ? 148.119 163.881 43.261 1.00 74.01 77 J 1
861
+ ATOM 581 N NE2 . GLN J 1 77 ? 148.126 163.046 41.178 1.00 74.01 77 J 1
862
+ ATOM 582 N N . ASN J 1 78 ? 151.610 165.453 41.583 1.00 75.47 78 J 1
863
+ ATOM 583 C CA . ASN J 1 78 ? 151.007 166.584 40.903 1.00 75.47 78 J 1
864
+ ATOM 584 C C . ASN J 1 78 ? 151.656 166.793 39.532 1.00 75.47 78 J 1
865
+ ATOM 585 O O . ASN J 1 78 ? 151.100 167.503 38.692 1.00 75.47 78 J 1
866
+ ATOM 586 C CB . ASN J 1 78 ? 151.109 167.797 41.862 1.00 75.47 78 J 1
867
+ ATOM 587 C CG . ASN J 1 78 ? 150.672 169.138 41.268 1.00 75.47 78 J 1
868
+ ATOM 588 O OD1 . ASN J 1 78 ? 150.393 169.290 40.093 1.00 75.47 78 J 1
869
+ ATOM 589 N ND2 . ASN J 1 78 ? 150.555 170.120 42.149 1.00 75.47 78 J 1
870
+ ATOM 590 N N . PHE J 1 79 ? 152.755 166.084 39.244 1.00 76.77 79 J 1
871
+ ATOM 591 C CA . PHE J 1 79 ? 153.341 166.125 37.905 1.00 76.77 79 J 1
872
+ ATOM 592 C C . PHE J 1 79 ? 152.308 165.775 36.843 1.00 76.77 79 J 1
873
+ ATOM 593 O O . PHE J 1 79 ? 152.188 166.465 35.824 1.00 76.77 79 J 1
874
+ ATOM 594 C CB . PHE J 1 79 ? 154.513 165.146 37.794 1.00 76.77 79 J 1
875
+ ATOM 595 C CG . PHE J 1 79 ? 155.770 165.599 38.471 1.00 76.77 79 J 1
876
+ ATOM 596 C CD1 . PHE J 1 79 ? 155.885 166.866 39.000 1.00 76.77 79 J 1
877
+ ATOM 597 C CD2 . PHE J 1 79 ? 156.855 164.744 38.553 1.00 76.77 79 J 1
878
+ ATOM 598 C CE1 . PHE J 1 79 ? 157.054 167.261 39.614 1.00 76.77 79 J 1
879
+ ATOM 599 C CE2 . PHE J 1 79 ? 158.021 165.138 39.162 1.00 76.77 79 J 1
880
+ ATOM 600 C CZ . PHE J 1 79 ? 158.121 166.398 39.692 1.00 76.77 79 J 1
881
+ ATOM 601 N N . ARG J 1 80 ? 151.557 164.702 37.066 1.00 84.01 80 J 1
882
+ ATOM 602 C CA . ARG J 1 80 ? 150.552 164.246 36.118 1.00 84.01 80 J 1
883
+ ATOM 603 C C . ARG J 1 80 ? 149.236 164.973 36.361 1.00 84.01 80 J 1
884
+ ATOM 604 O O . ARG J 1 80 ? 148.856 165.220 37.506 1.00 84.01 80 J 1
885
+ ATOM 605 C CB . ARG J 1 80 ? 150.365 162.730 36.232 1.00 84.01 80 J 1
886
+ ATOM 606 C CG . ARG J 1 80 ? 149.526 162.108 35.127 1.00 84.01 80 J 1
887
+ ATOM 607 C CD . ARG J 1 80 ? 148.094 161.873 35.581 1.00 84.01 80 J 1
888
+ ATOM 608 N NE . ARG J 1 80 ? 148.029 160.982 36.733 1.00 84.01 80 J 1
889
+ ATOM 609 C CZ . ARG J 1 80 ? 146.964 160.838 37.510 1.00 84.01 80 J 1
890
+ ATOM 610 N NH1 . ARG J 1 80 ? 145.848 161.512 37.286 1.00 84.01 80 J 1
891
+ ATOM 611 N NH2 . ARG J 1 80 ? 147.022 159.999 38.541 1.00 84.01 80 J 1
892
+ ATOM 612 O OXT . ARG J 1 80 ? 148.527 165.332 35.421 1.00 84.01 80 J 1
893
+ #
flax_model/alphafold3/test_data/miniature_databases/pdb_mmcif/__init__.py ADDED
File without changes
flax_model/alphafold3/test_data/miniature_databases/pdb_seqres_2022_09_28__subsampled_1000.fasta ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/rfam_14_4_clustered_rep_seq__subsampled_1000.fasta ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/rnacentral_active_seq_id_90_cov_80_linclust__subsampled_1000.fasta ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/uniprot_all__subsampled_1000.fasta ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/miniature_databases/uniref90__subsampled_1000.fasta ADDED
The diff for this file is too large to render. See raw diff
 
flax_model/alphafold3/test_data/model_config.json ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "evoformer": {
3
+ "max_relative_chain": 2,
4
+ "max_relative_idx": 32,
5
+ "msa_channel": 64,
6
+ "msa_stack": {
7
+ "msa_attention": {
8
+ "num_head": 8
9
+ },
10
+ "msa_transition": {
11
+ "num_intermediate_factor": 4,
12
+ "use_glu_kernel": true
13
+ },
14
+ "num_layer": 4,
15
+ "outer_product_mean": {
16
+ "chunk_size": 128,
17
+ "num_outer_channel": 32
18
+ },
19
+ "pair_attention": {
20
+ "num_head": 4
21
+ },
22
+ "pair_transition": {
23
+ "num_intermediate_factor": 4,
24
+ "use_glu_kernel": true
25
+ },
26
+ "shard_transition_blocks": true,
27
+ "triangle_multiplication_incoming": {
28
+ "equation": "kjc,kic->ijc",
29
+ "use_glu_kernel": true
30
+ },
31
+ "triangle_multiplication_outgoing": {
32
+ "equation": "ikc,jkc->ijc",
33
+ "use_glu_kernel": true
34
+ }
35
+ },
36
+ "num_msa": 1024,
37
+ "pair_channel": 128,
38
+ "pairformer": {
39
+ "block_remat": false,
40
+ "num_layer": 48,
41
+ "pair_attention": {
42
+ "num_head": 4
43
+ },
44
+ "pair_transition": {
45
+ "num_intermediate_factor": 4,
46
+ "use_glu_kernel": true
47
+ },
48
+ "remat_block_size": 8,
49
+ "shard_transition_blocks": true,
50
+ "single_attention": {
51
+ "key_dim": null,
52
+ "num_head": 16,
53
+ "value_dim": null
54
+ },
55
+ "single_transition": {
56
+ "num_intermediate_factor": 4,
57
+ "use_glu_kernel": true
58
+ },
59
+ "triangle_multiplication_incoming": {
60
+ "equation": "kjc,kic->ijc",
61
+ "use_glu_kernel": true
62
+ },
63
+ "triangle_multiplication_outgoing": {
64
+ "equation": "ikc,jkc->ijc",
65
+ "use_glu_kernel": true
66
+ }
67
+ },
68
+ "per_atom_conditioning": {
69
+ "atom_transformer": {
70
+ "attention": {
71
+ "key_dim": 128,
72
+ "num_head": 4,
73
+ "value_dim": 128
74
+ },
75
+ "num_blocks": 3,
76
+ "num_intermediate_factor": 2
77
+ },
78
+ "per_atom_channels": 128,
79
+ "per_atom_pair_channels": 16,
80
+ "per_token_channels": 384
81
+ },
82
+ "seq_channel": 384,
83
+ "template": {
84
+ "dgram_features": {
85
+ "max_bin": 50.75,
86
+ "min_bin": 3.25,
87
+ "num_bins": 39
88
+ },
89
+ "num_channels": 64,
90
+ "template_stack": {
91
+ "num_layer": 2,
92
+ "pair_attention": {
93
+ "num_head": 4
94
+ },
95
+ "pair_transition": {
96
+ "num_intermediate_factor": 2,
97
+ "use_glu_kernel": true
98
+ },
99
+ "shard_transition_blocks": true,
100
+ "single_attention": null,
101
+ "single_transition": null,
102
+ "triangle_multiplication_incoming": {
103
+ "equation": "kjc,kic->ijc",
104
+ "use_glu_kernel": true
105
+ },
106
+ "triangle_multiplication_outgoing": {
107
+ "equation": "ikc,jkc->ijc",
108
+ "use_glu_kernel": true
109
+ }
110
+ }
111
+ }
112
+ },
113
+ "global_config": {
114
+ "bfloat16": "all",
115
+ "final_init": "zeros",
116
+ "flash_attention_implementation": "triton",
117
+ "pair_attention_chunk_size": [
118
+ [
119
+ 1536,
120
+ 128
121
+ ],
122
+ [
123
+ null,
124
+ 32
125
+ ]
126
+ ],
127
+ "pair_transition_shard_spec": [
128
+ [
129
+ 2048,
130
+ null
131
+ ],
132
+ [
133
+ null,
134
+ 1024
135
+ ]
136
+ ]
137
+ },
138
+ "heads": {
139
+ "confidence": {
140
+ "dgram_features": {
141
+ "max_bin": 50.75,
142
+ "min_bin": 3.25,
143
+ "num_bins": 39
144
+ },
145
+ "max_error_bin": 31.0,
146
+ "no_embedding_prob": 0.2,
147
+ "num_bins": 64,
148
+ "num_plddt_bins": 50,
149
+ "pae": {
150
+ "max_error_bin": 31.0,
151
+ "num_bins": 64
152
+ },
153
+ "pairformer": {
154
+ "num_layer": 4,
155
+ "pair_attention": {
156
+ "num_head": 4
157
+ },
158
+ "pair_transition": {
159
+ "num_intermediate_factor": 4,
160
+ "use_glu_kernel": true
161
+ },
162
+ "shard_transition_blocks": true,
163
+ "single_attention": {
164
+ "key_dim": null,
165
+ "num_head": 16,
166
+ "value_dim": null
167
+ },
168
+ "single_transition": {
169
+ "num_intermediate_factor": 4,
170
+ "use_glu_kernel": true
171
+ },
172
+ "triangle_multiplication_incoming": {
173
+ "equation": "kjc,kic->ijc",
174
+ "use_glu_kernel": true
175
+ },
176
+ "triangle_multiplication_outgoing": {
177
+ "equation": "ikc,jkc->ijc",
178
+ "use_glu_kernel": true
179
+ }
180
+ }
181
+ },
182
+ "diffusion": {
183
+ "atom_transformer": {
184
+ "attention": {
185
+ "key_dim": 128,
186
+ "num_head": 4,
187
+ "value_dim": 128
188
+ },
189
+ "num_blocks": 3,
190
+ "num_intermediate_factor": 2
191
+ },
192
+ "conditioning": {
193
+ "pair_channel": 128,
194
+ "prob": 0.8,
195
+ "seq_channel": 384
196
+ },
197
+ "eval": {
198
+ "gamma_0": 0.8,
199
+ "gamma_min": 1.0,
200
+ "noise_scale": 1.003,
201
+ "num_samples": 5,
202
+ "step_scale": 1.5,
203
+ "steps": 200
204
+ },
205
+ "eval_batch_dim_shard_size": 5,
206
+ "eval_batch_size": 5,
207
+ "per_atom_channels": 128,
208
+ "per_atom_pair_channels": 16,
209
+ "per_token_channels": 768,
210
+ "transformer": {
211
+ "attention": {
212
+ "key_dim": null,
213
+ "num_head": 16,
214
+ "value_dim": null
215
+ },
216
+ "block_remat": false,
217
+ "num_blocks": 24,
218
+ "num_intermediate_factor": 2,
219
+ "super_block_size": 4
220
+ }
221
+ },
222
+ "distogram": {
223
+ "first_break": 2.3125,
224
+ "last_break": 21.6875,
225
+ "num_bins": 64
226
+ }
227
+ },
228
+ "num_recycles": 10,
229
+ "return_distogram": false,
230
+ "return_embeddings": false
231
+ }
flax_model/alphafold3/version.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 DeepMind Technologies Limited
2
+ #
3
+ # AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
4
+ # this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
5
+ #
6
+ # To request access to the AlphaFold 3 model parameters, follow the process set
7
+ # out at https://github.com/google-deepmind/alphafold3. You may only use these
8
+ # if received directly from Google. Use is subject to terms of use available at
9
+ # https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
10
+
11
+ """Single source of truth for the AlphaFold version."""
12
+
13
+ __version__ = '3.0.1'
inputs/7r6r_data.json ADDED
The diff for this file is too large to render. See raw diff
 
inputs/t1119_search.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "dialect": "alphafold3",
3
+ "version": 1,
4
+ "name": "T1119",
5
+ "sequences": [
6
+ {
7
+ "protein": {
8
+ "id": "A",
9
+ "sequence": "QLEDSEVEAVAKGLEEMYANGVTEDNFKNYVKNNFAQQEISSVEEELNVNISDSCVANKIKDEFFAMISISAIVKAAQKKAWKELAVTVLRFAKANGLKTNAIIVAGQLALWAVQCG"
10
+ }
11
+ }
12
+ ],
13
+ "modelSeeds": [
14
+ 100
15
+ ],
16
+ "bondedAtomPairs": null,
17
+ "userCCD": null
18
+ }
19
+
scripts/infer.sh ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
+
7
+
8
+
9
+ export TF_CPP_MIN_LOG_LEVEL="${TF_CPP_MIN_LOG_LEVEL:-0}"
10
+ export XLA_PYTHON_CLIENT_MEM_FRACTION="${XLA_PYTHON_CLIENT_MEM_FRACTION:-0.95}"
11
+ export TRITON_ENABLE_GLOBAL_TO_LOCAL="${TRITON_ENABLE_GLOBAL_TO_LOCAL:-1}"
12
+ export TRITON_USE_MAKE_BLOCK_PTR="${TRITON_USE_MAKE_BLOCK_PTR:-1}"
13
+ export TRITON_DEFAULT_ENABLE_NUM_VGPRS512="${TRITON_DEFAULT_ENABLE_NUM_VGPRS512:-1}"
14
+
15
+ MODEL_ROOT="${ONESCIENCE_MODELS_DIR:-${PROJECT_ROOT}/weight}"
16
+ MODEL_DIR="${ALPHAFOLD3_MODEL_DIR:-${MODEL_ROOT}}"
17
+ JSON_PATH="${ALPHAFOLD3_JSON_PATH:-${PROJECT_ROOT}/inputs/7r6r_data.json}"
18
+ OUTPUT_DIR="${ALPHAFOLD3_OUTPUT_DIR:-${PROJECT_ROOT}/outputs}"
19
+ FLASH_ATTENTION="${ALPHAFOLD3_FLASH_ATTENTION:-triton}"
20
+
21
+ mkdir -p "${OUTPUT_DIR}"
22
+
23
+ python "${PROJECT_ROOT}/scripts/run_alphafold.py" \
24
+ --json_path="${JSON_PATH}" \
25
+ --model_dir="${MODEL_DIR}" \
26
+ --output_dir="${OUTPUT_DIR}" \
27
+ --run_data_pipeline=false \
28
+ --flash_attention_implementation="${FLASH_ATTENTION}"
scripts/infer_jackhmmer.sh ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
+
7
+
8
+ export PATH="${PROJECT_ROOT}/flax_model/alphafold3/_tools/hmmer/bin:${PATH}"
9
+ export TF_CPP_MIN_LOG_LEVEL="${TF_CPP_MIN_LOG_LEVEL:-0}"
10
+ export JAX_TRACEBACK_FILTERING="${JAX_TRACEBACK_FILTERING:-off}"
11
+ export XLA_CLIENT_MEM_FRACTION="${XLA_CLIENT_MEM_FRACTION:-0.95}"
12
+ export TRITON_ENABLE_GLOBAL_TO_LOCAL="${TRITON_ENABLE_GLOBAL_TO_LOCAL:-1}"
13
+ export TRITON_USE_MAKE_BLOCK_PTR="${TRITON_USE_MAKE_BLOCK_PTR:-1}"
14
+ export TRITON_DEFAULT_ENABLE_NUM_VGPRS512="${TRITON_DEFAULT_ENABLE_NUM_VGPRS512:-1}"
15
+ export HIP_VISIBLE_DEVICES="${HIP_VISIBLE_DEVICES:-0}"
16
+
17
+ MODEL_ROOT="${ONESCIENCE_MODELS_DIR:-${PROJECT_ROOT}/weight}"
18
+ MODEL_DIR="${ALPHAFOLD3_MODEL_DIR:-${MODEL_ROOT}}"
19
+ DATASET_ROOT="${ALPHAFOLD3_DATASET_ROOT:-${ONESCIENCE_DATASETS_DIR:-${PROJECT_ROOT}/data}/alphafold3}"
20
+ DB_DIRS="${ALPHAFOLD3_DB_DIR:-${DATASET_ROOT}/public_databases}"
21
+ MMSEQS_DB_DIRS="${ALPHAFOLD3_MMSEQS_DB_DIR:-${DATASET_ROOT}/mmseqsDB}"
22
+ JSON_PATH="${ALPHAFOLD3_JSON_PATH:-${PROJECT_ROOT}/inputs/t1119_search.json}"
23
+ OUTPUT_DIR="${ALPHAFOLD3_OUTPUT_DIR:-${PROJECT_ROOT}/outputs}"
24
+ RUN_INFERENCE="${ALPHAFOLD3_RUN_INFERENCE:-false}"
25
+ FLASH_ATTENTION="${ALPHAFOLD3_FLASH_ATTENTION:-cutlass}"
26
+
27
+ mkdir -p "${OUTPUT_DIR}"
28
+
29
+ python "${PROJECT_ROOT}/scripts/run_alphafold.py" \
30
+ --json_path="${JSON_PATH}" \
31
+ --model_dir="${MODEL_DIR}" \
32
+ --output_dir="${OUTPUT_DIR}" \
33
+ --run_data_pipeline=true \
34
+ --run_inference="${RUN_INFERENCE}" \
35
+ --flash_attention_implementation="${FLASH_ATTENTION}" \
36
+ --use_mmseqs=false \
37
+ --use_mmseqs_gpu=false \
38
+ --db_dir="${DB_DIRS}" \
39
+ --mmseqs_db_dir="${MMSEQS_DB_DIRS}" \
40
+ --small_bfd_database_path="${DATASET_ROOT}/jackhmmer_split/bfd-first_non_consensus_sequences.fasta@64" \
41
+ --small_bfd_z_value=65928866 \
42
+ --mgnify_database_path="${DATASET_ROOT}/jackhmmer_split/mgy_clusters_2022_05.fa@512" \
43
+ --mgnify_z_value=623796864 \
44
+ --uniprot_cluster_annot_database_path="${DATASET_ROOT}/jackhmmer_split/uniprot_cluster_annot_2021_04.fa@256" \
45
+ --uniprot_cluster_annot_z_value=225619586 \
46
+ --uniref90_database_path="${DATASET_ROOT}/jackhmmer_split/uniref90_2022_05.fa@128" \
47
+ --uniref90_z_value=153742194 \
48
+ --jackhmmer_n_cpu="${ALPHAFOLD3_JACKHMMER_N_CPU:-2}" \
49
+ --jackhmmer_max_parallel_shards="${ALPHAFOLD3_JACKHMMER_MAX_PARALLEL_SHARDS:-1}" \
50
+ --jackhmmer_max_threads="${ALPHAFOLD3_JACKHMMER_MAX_THREADS:-8}" \
51
+ --nhmmer_n_cpu="${ALPHAFOLD3_NHMMER_N_CPU:-2}" \
52
+ --nhmmer_max_parallel_shards="${ALPHAFOLD3_NHMMER_MAX_PARALLEL_SHARDS:-1}" \
53
+ --nhmmer_max_threads="${ALPHAFOLD3_NHMMER_MAX_THREADS:-8}"
scripts/infer_mmseqs.sh ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
6
+
7
+ MODEL_ROOT="${ONESCIENCE_MODELS_DIR:-${PROJECT_ROOT}/weight}"
8
+ MODEL_DIR="${ALPHAFOLD3_MODEL_DIR:-${MODEL_ROOT}}"
9
+ MMSEQS_HOME="${ALPHAFOLD3_MMSEQS_HOME:-${MODEL_DIR}/mmseqs}"
10
+ export PATH="${MMSEQS_HOME}/bin:${PATH}"
11
+ export LD_LIBRARY_PATH="${MMSEQS_HOME}/lib:${LD_LIBRARY_PATH:-}"
12
+ export TF_CPP_MIN_LOG_LEVEL="${TF_CPP_MIN_LOG_LEVEL:-0}"
13
+ export JAX_TRACEBACK_FILTERING="${JAX_TRACEBACK_FILTERING:-off}"
14
+ export XLA_PYTHON_CLIENT_ALLOCATOR="${XLA_PYTHON_CLIENT_ALLOCATOR:-platform}"
15
+ export TRITON_ENABLE_GLOBAL_TO_LOCAL="${TRITON_ENABLE_GLOBAL_TO_LOCAL:-1}"
16
+ export TRITON_USE_MAKE_BLOCK_PTR="${TRITON_USE_MAKE_BLOCK_PTR:-1}"
17
+ export TRITON_DEFAULT_ENABLE_NUM_VGPRS512="${TRITON_DEFAULT_ENABLE_NUM_VGPRS512:-1}"
18
+ export HIP_VISIBLE_DEVICES="${HIP_VISIBLE_DEVICES:-0}"
19
+
20
+ DATASET_ROOT="${ALPHAFOLD3_DATASET_ROOT:-${ONESCIENCE_DATASETS_DIR:-${PROJECT_ROOT}/data}/alphafold3}"
21
+ DB_DIRS="${ALPHAFOLD3_DB_DIR:-${DATASET_ROOT}/public_databases}"
22
+ MMSEQS_DB_DIRS="${ALPHAFOLD3_MMSEQS_DB_DIR:-${DATASET_ROOT}/mmseqsDB}"
23
+ JSON_PATH="${ALPHAFOLD3_JSON_PATH:-${PROJECT_ROOT}/inputs/t1119_search.json}"
24
+ OUTPUT_DIR="${ALPHAFOLD3_OUTPUT_DIR:-${PROJECT_ROOT}/outputs}"
25
+ RUN_INFERENCE="${ALPHAFOLD3_RUN_INFERENCE:-false}"
26
+ FLASH_ATTENTION="${ALPHAFOLD3_FLASH_ATTENTION:-triton}"
27
+ MMSEQS_OPTIONS="${ALPHAFOLD3_MMSEQS_OPTIONS:---num-iterations 1 --db-load-mode 2 -a --max-seqs 10000 --prefilter-mode 3}"
28
+
29
+ mkdir -p "${OUTPUT_DIR}"
30
+
31
+ python "${PROJECT_ROOT}/scripts/run_alphafold.py" \
32
+ --json_path="${JSON_PATH}" \
33
+ --model_dir="${MODEL_DIR}" \
34
+ --output_dir="${OUTPUT_DIR}" \
35
+ --run_data_pipeline=true \
36
+ --run_inference="${RUN_INFERENCE}" \
37
+ --flash_attention_implementation="${FLASH_ATTENTION}" \
38
+ --db_dir="${DB_DIRS}" \
39
+ --mmseqs_db_dir="${MMSEQS_DB_DIRS}" \
40
+ --use_mmseqs=true \
41
+ --use_mmseqs_gpu=true \
42
+ --mmseqs_options="${MMSEQS_OPTIONS}"