1pub use ferritin_core::info::atom37::{
11 AAAtom, ATOM37_NAMES, NUM_ATOM37, NUM_RESIDUES, atom_order, atom37_index,
12};
13
14#[rustfmt::skip]
23pub fn chi_angles_atoms(res3: &str) -> &'static [(&'static str, &'static str, &'static str, &'static str)] {
24 match res3 {
25 "ALA" | "GLY" | "UNK" => &[],
26 "ARG" => &[
27 ("N","CA","CB","CG"), ("CA","CB","CG","CD"),
28 ("CB","CG","CD","NE"), ("CG","CD","NE","CZ"),
29 ],
30 "ASN" => &[
31 ("N","CA","CB","CG"), ("CA","CB","CG","OD1"),
32 ],
33 "ASP" => &[
34 ("N","CA","CB","CG"), ("CA","CB","CG","OD1"),
35 ],
36 "CYS" => &[
37 ("N","CA","CB","SG"),
38 ],
39 "GLN" => &[
40 ("N","CA","CB","CG"), ("CA","CB","CG","CD"), ("CB","CG","CD","OE1"),
41 ],
42 "GLU" => &[
43 ("N","CA","CB","CG"), ("CA","CB","CG","CD"), ("CB","CG","CD","OE1"),
44 ],
45 "HIS" => &[
46 ("N","CA","CB","CG"), ("CA","CB","CG","ND1"),
47 ],
48 "ILE" => &[
49 ("N","CA","CB","CG1"), ("CA","CB","CG1","CD1"),
50 ],
51 "LEU" => &[
52 ("N","CA","CB","CG"), ("CA","CB","CG","CD1"),
53 ],
54 "LYS" => &[
55 ("N","CA","CB","CG"), ("CA","CB","CG","CD"),
56 ("CB","CG","CD","CE"), ("CG","CD","CE","NZ"),
57 ],
58 "MET" => &[
59 ("N","CA","CB","CG"), ("CA","CB","CG","SD"), ("CB","CG","SD","CE"),
60 ],
61 "PHE" => &[
62 ("N","CA","CB","CG"), ("CA","CB","CG","CD1"),
63 ],
64 "PRO" => &[
65 ("N","CA","CB","CG"), ("CA","CB","CG","CD"),
66 ],
67 "SER" => &[
68 ("N","CA","CB","OG"),
69 ],
70 "THR" => &[
71 ("N","CA","CB","OG1"),
72 ],
73 "TRP" => &[
74 ("N","CA","CB","CG"), ("CA","CB","CG","CD1"),
75 ],
76 "TYR" => &[
77 ("N","CA","CB","CG"), ("CA","CB","CG","CD1"),
78 ],
79 "VAL" => &[
80 ("N","CA","CB","CG1"),
81 ],
82 _ => &[],
83 }
84}
85
86#[rustfmt::skip]
94pub fn atom14_to_atom37_for_residue(res3: &str) -> [Option<usize>; 14] {
95 let names: [Option<&str>; 14] = match res3 {
96 "ALA" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),None,None,None,None,None,None,None,None,None],
97 "CYS" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("SG"),None,None,None,None,None,None,None,None],
98 "ASP" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("OD1"),Some("OD2"),None,None,None,None,None,None],
99 "GLU" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD"),Some("OE1"),Some("OE2"),None,None,None,None,None],
100 "PHE" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD1"),Some("CD2"),Some("CE1"),Some("CE2"),Some("CZ"),None,None,None],
101 "GLY" => [Some("N"),Some("CA"),Some("C"),Some("O"),None,None,None,None,None,None,None,None,None,None],
102 "HIS" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("ND1"),Some("CD2"),Some("CE1"),Some("NE2"),None,None,None,None],
103 "ILE" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG1"),Some("CG2"),Some("CD1"),None,None,None,None,None,None],
104 "LYS" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD"),Some("CE"),Some("NZ"),None,None,None,None,None],
105 "LEU" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD1"),Some("CD2"),None,None,None,None,None,None],
106 "MET" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("SD"),Some("CE"),None,None,None,None,None,None],
107 "ASN" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("OD1"),Some("ND2"),None,None,None,None,None,None],
108 "PRO" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD"),None,None,None,None,None,None,None],
109 "GLN" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD"),Some("OE1"),Some("NE2"),None,None,None,None,None],
110 "ARG" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD"),Some("NE"),Some("CZ"),Some("NH1"),Some("NH2"),None,None,None],
111 "SER" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("OG"),None,None,None,None,None,None,None,None],
112 "THR" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("OG1"),Some("CG2"),None,None,None,None,None,None,None],
113 "VAL" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG1"),Some("CG2"),None,None,None,None,None,None,None],
114 "TRP" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD1"),Some("CD2"),Some("CE2"),Some("CE3"),Some("NE1"),Some("CZ2"),Some("CZ3"),Some("CH2")],
115 "TYR" => [Some("N"),Some("CA"),Some("C"),Some("O"),Some("CB"),Some("CG"),Some("CD1"),Some("CD2"),Some("CE1"),Some("CE2"),Some("CZ"),Some("OH"),None,None],
116 _ => [None; 14],
117 };
118
119 let mut out = [None; 14];
120 for (i, name_opt) in names.iter().enumerate() {
121 out[i] = name_opt.and_then(atom37_index);
122 }
123 out
124}
125
126#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_atom_order_completeness() {
134 let order = atom_order();
135 assert_eq!(order.len(), NUM_ATOM37);
136 assert_eq!(order["N"], 0);
137 assert_eq!(order["CA"], 1);
138 assert_eq!(order["C"], 2);
139 assert_eq!(order["CB"], 3);
140 assert_eq!(order["O"], 4);
141 }
142
143 #[test]
144 fn test_atom37_index_roundtrip() {
145 for (i, &name) in ATOM37_NAMES.iter().enumerate() {
146 assert_eq!(atom37_index(name), Some(i));
147 }
148 assert_eq!(atom37_index("ZZZ"), None);
149 }
150
151 #[test]
152 fn test_chi_gly_ala_empty() {
153 assert!(chi_angles_atoms("GLY").is_empty());
154 assert!(chi_angles_atoms("ALA").is_empty());
155 }
156
157 #[test]
158 fn test_chi_arg_four_angles() {
159 assert_eq!(chi_angles_atoms("ARG").len(), 4);
160 assert_eq!(chi_angles_atoms("ARG")[0], ("N", "CA", "CB", "CG"));
161 }
162
163 #[test]
164 fn test_chi_val_one_angle() {
165 let chis = chi_angles_atoms("VAL");
166 assert_eq!(chis.len(), 1);
167 assert_eq!(chis[0], ("N", "CA", "CB", "CG1"));
168 }
169
170 #[test]
171 fn test_atom14_to_atom37_gly() {
172 let mapping = atom14_to_atom37_for_residue("GLY");
173 assert_eq!(mapping[0], Some(0)); assert_eq!(mapping[1], Some(1)); assert_eq!(mapping[2], Some(2)); assert_eq!(mapping[3], Some(4)); assert_eq!(mapping[4], None); #[allow(clippy::needless_range_loop)] for i in 4..14 {
180 assert_eq!(mapping[i], None, "GLY slot {i} should be None");
181 }
182 }
183
184 #[test]
185 fn test_atom14_to_atom37_ala() {
186 let mapping = atom14_to_atom37_for_residue("ALA");
187 assert_eq!(mapping[4], Some(3)); assert_eq!(mapping[5], None);
189 }
190
191 #[test]
192 fn test_atom14_to_atom37_trp_full() {
193 let mapping = atom14_to_atom37_for_residue("TRP");
194 assert!(
195 mapping.iter().all(|m| m.is_some()),
196 "TRP should fill all 14 slots"
197 );
198 }
199
200 #[test]
201 fn test_atom14_to_atom37_unk() {
202 let mapping = atom14_to_atom37_for_residue("UNK");
203 assert!(mapping.iter().all(|m| m.is_none()));
204 }
205}