1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.potential.bonded;
39
40 import static ffx.numerics.math.DoubleMath.dihedralAngle;
41 import static ffx.numerics.math.ScalarMath.modToRange;
42 import static ffx.potential.bonded.AminoAcidUtils.AminoAcid3;
43 import static ffx.potential.bonded.AminoAcidUtils.getAminoAcid;
44 import static ffx.potential.bonded.BondedUtils.findAtomsOfElement;
45 import static ffx.potential.bonded.BondedUtils.findBondedAtoms;
46 import static ffx.potential.bonded.BondedUtils.findNitrogenAtom;
47 import static ffx.potential.bonded.BondedUtils.findNucleotideO4s;
48 import static ffx.potential.bonded.BondedUtils.getAlphaCarbon;
49 import static ffx.potential.bonded.BondedUtils.hasAttachedAtom;
50 import static ffx.potential.bonded.BondedUtils.sortAtomsByDistance;
51 import static ffx.potential.bonded.NucleicAcidUtils.NucleicAcid3;
52 import static java.lang.Integer.parseInt;
53 import static java.lang.Math.PI;
54 import static java.lang.String.format;
55
56 import ffx.potential.MolecularAssembly;
57 import ffx.potential.parsers.PDBFilter.PDBFileStandard;
58 import java.util.ArrayList;
59 import java.util.Comparator;
60 import java.util.HashMap;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Optional;
64 import java.util.logging.Level;
65 import java.util.logging.Logger;
66
67
68
69
70
71
72
73
74 public class NamingUtils {
75
76 private static final Logger logger = Logger.getLogger(NamingUtils.class.getName());
77
78
79
80
81
82
83
84
85
86 public static void checkHydrogenAtomNames(Residue residue, PDBFileStandard fileStandard) {
87 switch (fileStandard) {
88 case VERSION3_3:
89 return;
90 case VERSION3_2:
91 default:
92 break;
93 }
94
95 String residueType = residue.getName().toUpperCase();
96 List<Atom> resAtoms = residue.getAtomList();
97 for (Atom atom : resAtoms) {
98 if (atom == null) {
99 continue;
100 }
101 String atomName = atom.getName().toUpperCase();
102
103 if (atomName.contains("H")) {
104 try {
105 String firstChar = atomName.substring(0, 1);
106 parseInt(firstChar);
107 atomName = atomName.substring(1);
108 atomName = atomName.concat(firstChar);
109 atom.setName(atomName);
110 } catch (NumberFormatException e) {
111
112 }
113 }
114 }
115
116
117 List<Atom> betas;
118 List<Atom> gammas;
119 List<Atom> deltas;
120 List<Atom> epsilons;
121 List<Atom> zetas;
122 String atomName;
123 Atom OH;
124 Atom HH;
125 Atom HG;
126 Atom HD2;
127 switch (getAminoAcid(residueType)) {
128 case GLY:
129 List<Atom> alphas = new ArrayList<>();
130 for (Atom atom : resAtoms) {
131 if (atom.getName().toUpperCase().contains("HA")) {
132 alphas.add(atom);
133 }
134 }
135 renameGlycineAlphaHydrogen(residue, alphas);
136 break;
137 case ALA:
138
139 break;
140 case VAL:
141
142 break;
143 case LEU:
144 case SER:
145 case CYD:
146 case ASP:
147 betas = new ArrayList<>();
148 for (Atom atom : resAtoms) {
149 if (atom.getName().toUpperCase().contains("HB")) {
150 betas.add(atom);
151 }
152 }
153 renameBetaHydrogen(residue, betas, 23);
154 break;
155 case ILE:
156 List<Atom> ileAtoms = new ArrayList<>();
157 for (Atom atom : resAtoms) {
158 if (atom.getName().toUpperCase().contains("HG1")) {
159 ileAtoms.add(atom);
160 }
161 }
162 renameIsoleucineHydrogen(residue, ileAtoms);
163 break;
164 case THR:
165 Atom HG1 = (Atom) residue.getAtomNode("HG1");
166 if (HG1 == null) {
167 for (Atom atom : resAtoms) {
168 atomName = atom.getName().toUpperCase();
169
170
171 if (atomName.length() < 4 && atomName.contains("HG")) {
172 atom.setName("HG1");
173 break;
174 }
175 }
176 }
177 break;
178 case CYS:
179 betas = new ArrayList<>();
180 HG = (Atom) residue.getAtomNode("HG");
181 for (Atom atom : resAtoms) {
182 atomName = atom.getName().toUpperCase();
183 if (atomName.contains("HB")) {
184 betas.add(atom);
185 } else if (HG == null && atomName.contains("HG")) {
186 HG = atom;
187 HG.setName("HG");
188 }
189 }
190 renameBetaHydrogen(residue, betas, 23);
191 break;
192 case CYX:
193
194 break;
195 case PRO:
196 betas = new ArrayList<>();
197 gammas = new ArrayList<>();
198 deltas = new ArrayList<>();
199 for (Atom atom : resAtoms) {
200 atomName = atom.getName().toUpperCase();
201 if (atomName.contains("HB")) {
202 betas.add(atom);
203 } else if (atomName.contains("HG")) {
204 gammas.add(atom);
205 } else if (atomName.contains("HD")) {
206 deltas.add(atom);
207 }
208 }
209 renameBetaHydrogen(residue, betas, 23);
210 renameGammaHydrogen(residue, gammas, 23);
211 renameDeltaHydrogen(residue, deltas, 23);
212 break;
213 case PHE:
214 betas = new ArrayList<>();
215 deltas = new ArrayList<>();
216 epsilons = new ArrayList<>();
217 Atom HZ = (Atom) residue.getAtomNode("HZ");
218 for (Atom atom : resAtoms) {
219 atomName = atom.getName().toUpperCase();
220 if (atomName.contains("HB")) {
221 betas.add(atom);
222 } else if (atomName.contains("HD")) {
223 deltas.add(atom);
224 } else if (atomName.contains("HE")) {
225 epsilons.add(atom);
226 } else if (HZ == null && atomName.contains("HZ")) {
227 HZ = atom;
228 HZ.setName("HZ");
229 }
230 }
231 renameBetaHydrogen(residue, betas, 23);
232 renameDeltaHydrogen(residue, deltas, 12);
233 renameEpsilonHydrogen(residue, epsilons, 12);
234 break;
235 case TYR:
236 betas = new ArrayList<>();
237 deltas = new ArrayList<>();
238 epsilons = new ArrayList<>();
239 HH = (Atom) residue.getAtomNode("HH");
240 OH = (Atom) residue.getAtomNode("OH");
241 for (Atom atom : resAtoms) {
242 atomName = atom.getName().toUpperCase();
243 if (atomName.contains("HB")) {
244 betas.add(atom);
245 } else if (atomName.contains("HD")) {
246 deltas.add(atom);
247 } else if (atomName.contains("HE")) {
248 epsilons.add(atom);
249 } else if (HH == null && atomName.contains("HH")) {
250 HH = atom;
251 HH.setName("HH");
252 } else if (OH == null && atomName.contains("O") && atomName.contains("H")) {
253 OH = atom;
254 OH.setName("OH");
255 }
256 }
257 renameBetaHydrogen(residue, betas, 23);
258 renameDeltaHydrogen(residue, deltas, 12);
259 renameEpsilonHydrogen(residue, epsilons, 12);
260 break;
261 case TYD:
262 betas = new ArrayList<>();
263 deltas = new ArrayList<>();
264 epsilons = new ArrayList<>();
265 OH = (Atom) residue.getAtomNode("OH");
266 for (Atom atom : resAtoms) {
267 atomName = atom.getName().toUpperCase();
268 if (atomName.contains("HB")) {
269 betas.add(atom);
270 } else if (atomName.contains("HD")) {
271 deltas.add(atom);
272 } else if (atomName.contains("HE")) {
273 epsilons.add(atom);
274 } else if (OH == null && atomName.contains("O") && atomName.contains("H")) {
275 OH = atom;
276 OH.setName("OH");
277 }
278 }
279 renameBetaHydrogen(residue, betas, 23);
280 renameDeltaHydrogen(residue, deltas, 12);
281 renameEpsilonHydrogen(residue, epsilons, 12);
282 break;
283 case TRP:
284 betas = new ArrayList<>();
285 epsilons = new ArrayList<>();
286 zetas = new ArrayList<>();
287 Atom HD1 = (Atom) residue.getAtomNode("HD1");
288 Atom HH2 = (Atom) residue.getAtomNode("HH2");
289 for (Atom atom : resAtoms) {
290 atomName = atom.getName().toUpperCase();
291 if (atomName.contains("HB")) {
292 betas.add(atom);
293 } else if (atomName.contains("HE")) {
294 epsilons.add(atom);
295 } else if (atomName.contains("HZ")) {
296 zetas.add(atom);
297 } else if (HD1 == null && atomName.contains("HD")) {
298 HD1 = atom;
299 HD1.setName("HD1");
300 } else if (HH2 == null && atomName.contains("HH")) {
301 HH2 = atom;
302 HH2.setName("HH2");
303 }
304 }
305 renameBetaHydrogen(residue, betas, 23);
306 renameEpsilonHydrogen(residue, epsilons, 13);
307 renameZetaHydrogen(residue, zetas, 23);
308 break;
309 case HIS:
310 betas = new ArrayList<>();
311 deltas = new ArrayList<>();
312 epsilons = new ArrayList<>();
313 for (Atom atom : resAtoms) {
314 atomName = atom.getName().toUpperCase();
315 if (atomName.contains("HB")) {
316 betas.add(atom);
317 } else if (atomName.contains("HD")) {
318 deltas.add(atom);
319 } else if (atomName.contains("HE")) {
320 epsilons.add(atom);
321 }
322 }
323 renameBetaHydrogen(residue, betas, 23);
324 renameDeltaHydrogen(residue, deltas, 12);
325 renameEpsilonHydrogen(residue, epsilons, 12);
326 break;
327 case HID:
328 betas = new ArrayList<>();
329 deltas = new ArrayList<>();
330 Atom HE1 = (Atom) residue.getAtomNode("HE1");
331 for (Atom atom : resAtoms) {
332 atomName = atom.getName().toUpperCase();
333 if (atomName.contains("HB")) {
334 betas.add(atom);
335 } else if (atomName.contains("HD")) {
336 deltas.add(atom);
337 } else if (HE1 == null && atomName.contains("HE")) {
338 HE1 = atom;
339 HE1.setName("HE1");
340 }
341 }
342 renameBetaHydrogen(residue, betas, 23);
343 renameDeltaHydrogen(residue, deltas, 12);
344 break;
345 case HIE:
346 betas = new ArrayList<>();
347 epsilons = new ArrayList<>();
348 HD2 = (Atom) residue.getAtomNode("HD2");
349 for (Atom atom : resAtoms) {
350 atomName = atom.getName().toUpperCase();
351 if (atomName.contains("HB")) {
352 betas.add(atom);
353 } else if (atomName.contains("HE")) {
354 epsilons.add(atom);
355 } else if (HD2 == null && atomName.contains("HD")) {
356 HD2 = atom;
357 HD2.setName("HD2");
358 }
359 }
360 renameBetaHydrogen(residue, betas, 23);
361 renameEpsilonHydrogen(residue, epsilons, 12);
362 break;
363 case ASH:
364 betas = new ArrayList<>();
365 HD2 = (Atom) residue.getAtomNode("HD2");
366 for (Atom atom : resAtoms) {
367 atomName = atom.getName().toUpperCase();
368 if (atomName.contains("HB")) {
369 betas.add(atom);
370 } else if (HD2 == null && atomName.contains("HD")) {
371 HD2 = atom;
372 HD2.setName("HD2");
373 }
374 }
375 renameBetaHydrogen(residue, betas, 23);
376 break;
377 case ASD:
378 betas = new ArrayList<>();
379 deltas = new ArrayList<>();
380 for (Atom atom : resAtoms) {
381 atomName = atom.getName().toUpperCase();
382 if (atomName.contains("HB")) {
383 betas.add(atom);
384 } else if (atomName.contains("HD")) {
385 deltas.add(atom);
386 }
387 }
388 renameBetaHydrogen(residue, betas, 23);
389 renameDeltaHydrogen(residue, deltas, 12);
390 break;
391 case ASN:
392 betas = new ArrayList<>();
393 List<Atom> HD2s = new ArrayList<>();
394 for (Atom atom : resAtoms) {
395 atomName = atom.getName().toUpperCase();
396 if (atomName.contains("HB")) {
397 betas.add(atom);
398 } else if (atomName.contains("HD")) {
399 HD2s.add(atom);
400 }
401 }
402 renameBetaHydrogen(residue, betas, 23);
403 renameAsparagineHydrogen(residue, HD2s);
404 break;
405 case GLU:
406 case MET:
407 betas = new ArrayList<>();
408 gammas = new ArrayList<>();
409 for (Atom atom : resAtoms) {
410 atomName = atom.getName().toUpperCase();
411 if (atomName.contains("HB")) {
412 betas.add(atom);
413 } else if (atomName.contains("HG")) {
414 gammas.add(atom);
415 }
416 }
417 renameBetaHydrogen(residue, betas, 23);
418 renameGammaHydrogen(residue, gammas, 23);
419 break;
420 case GLH:
421 betas = new ArrayList<>();
422 gammas = new ArrayList<>();
423 Atom HE2 = (Atom) residue.getAtomNode("HE2");
424 for (Atom atom : resAtoms) {
425 atomName = atom.getName().toUpperCase();
426 if (atomName.contains("HB")) {
427 betas.add(atom);
428 } else if (atomName.contains("HG")) {
429 gammas.add(atom);
430 } else if (HE2 == null && atomName.contains("HE")) {
431 HE2 = atom;
432 HE2.setName("HE2");
433 }
434 }
435 renameBetaHydrogen(residue, betas, 23);
436 renameGammaHydrogen(residue, gammas, 23);
437 break;
438 case GLD:
439 betas = new ArrayList<>();
440 gammas = new ArrayList<>();
441 epsilons = new ArrayList<>();
442 for (Atom atom : resAtoms) {
443 atomName = atom.getName().toUpperCase();
444 if (atomName.contains("HB")) {
445 betas.add(atom);
446 } else if (atomName.contains("HG")) {
447 gammas.add(atom);
448 } else if (atomName.contains("HE")) {
449 epsilons.add(atom);
450 }
451 }
452 renameBetaHydrogen(residue, betas, 23);
453 renameGammaHydrogen(residue, gammas, 23);
454 renameEpsilonHydrogen(residue, epsilons, 12);
455 break;
456 case GLN:
457 betas = new ArrayList<>();
458 gammas = new ArrayList<>();
459 epsilons = new ArrayList<>();
460 for (Atom atom : resAtoms) {
461 atomName = atom.getName().toUpperCase();
462 if (atomName.contains("HB")) {
463 betas.add(atom);
464 } else if (atomName.contains("HG")) {
465 gammas.add(atom);
466 } else if (atomName.contains("HE")) {
467 epsilons.add(atom);
468 }
469 }
470 renameBetaHydrogen(residue, betas, 23);
471 renameGammaHydrogen(residue, gammas, 23);
472 renameGlutamineHydrogen(residue, epsilons);
473 break;
474
475 case LYS:
476 betas = new ArrayList<>();
477 gammas = new ArrayList<>();
478 deltas = new ArrayList<>();
479 epsilons = new ArrayList<>();
480
481 for (Atom atom : resAtoms) {
482 atomName = atom.getName().toUpperCase();
483 if (atomName.contains("HB")) {
484 betas.add(atom);
485 } else if (atomName.contains("HG")) {
486 gammas.add(atom);
487 } else if (atomName.contains("HD")) {
488 deltas.add(atom);
489 } else if (atomName.contains("HE")) {
490 epsilons.add(atom);
491 }
492 }
493 renameBetaHydrogen(residue, betas, 23);
494 renameGammaHydrogen(residue, gammas, 23);
495 renameDeltaHydrogen(residue, deltas, 23);
496 renameEpsilonHydrogen(residue, epsilons, 23);
497 break;
498 case LYD:
499 betas = new ArrayList<>();
500 gammas = new ArrayList<>();
501 deltas = new ArrayList<>();
502 epsilons = new ArrayList<>();
503 zetas = new ArrayList<>();
504 for (Atom atom : resAtoms) {
505 atomName = atom.getName().toUpperCase();
506 if (atomName.contains("HB")) {
507 betas.add(atom);
508 } else if (atomName.contains("HG")) {
509 gammas.add(atom);
510 } else if (atomName.contains("HD")) {
511 deltas.add(atom);
512 } else if (atomName.contains("HE")) {
513 epsilons.add(atom);
514 } else if (atomName.contains("HZ")) {
515 zetas.add(atom);
516 }
517 }
518 renameBetaHydrogen(residue, betas, 23);
519 renameGammaHydrogen(residue, gammas, 23);
520 renameDeltaHydrogen(residue, deltas, 23);
521 renameEpsilonHydrogen(residue, epsilons, 23);
522 renameZetaHydrogen(residue, zetas, 12);
523 break;
524 case ARG:
525 betas = new ArrayList<>();
526 gammas = new ArrayList<>();
527 deltas = new ArrayList<>();
528 Atom HE = (Atom) residue.getAtomNode("HE");
529 List<Atom> HHn = new ArrayList<>();
530 for (Atom atom : resAtoms) {
531 atomName = atom.getName().toUpperCase();
532 if (atomName.contains("HB")) {
533 betas.add(atom);
534 } else if (atomName.contains("HG")) {
535 gammas.add(atom);
536 } else if (atomName.contains("HD")) {
537 deltas.add(atom);
538 } else if (HE == null && atomName.contains("HE")) {
539 HE = atom;
540 HE.setName("HE");
541 } else if (atomName.contains("HH")) {
542 HHn.add(atom);
543 }
544 }
545 renameBetaHydrogen(residue, betas, 23);
546 renameGammaHydrogen(residue, gammas, 23);
547 renameDeltaHydrogen(residue, deltas, 23);
548 renameArginineHydrogen(residue, HHn);
549 break;
550 case ORN:
551 case AIB:
552 case PCA:
553 case UNK:
554 default:
555
556
557 break;
558 }
559 }
560
561
562
563
564
565
566
567 public static void nameAcetylCap(Residue residue, Atom aceC) {
568 logger.fine(format(" Probable ACE cap attached to residue %s; duplicate atom names may result.",
569 residue));
570 aceC.setName("C");
571 findBondedAtoms(aceC, 8).get(0).setName("O");
572 Atom CH3 = findBondedAtoms(aceC, 6).get(0);
573 CH3.setName("CH3");
574 List<Atom> ntermHs = findBondedAtoms(CH3, 1);
575 for (int i = 0; i < 3; i++) {
576 ntermHs.get(i).setName(format("H%d", (i + 1)));
577 }
578 }
579
580
581
582
583
584
585
586
587
588
589
590
591
592 public static Optional<Atom> renameAlkyl(Atom carbon, Atom priorAtom, int protonOffset,
593 char posName) {
594 carbon.setName(format("C%c", posName));
595 List<Atom> hydrogen = findBondedAtoms(carbon, 1);
596 int numH = hydrogen.size();
597 if (numH == 1) {
598 hydrogen.get(0).setName(format("H%c", posName));
599 } else {
600 for (int i = 0; i < numH; i++) {
601 hydrogen.get(i).setName(format("H%c%d", posName, i + protonOffset));
602 }
603 }
604
605 return carbon.getBonds().stream()
606 .map((Bond b) -> b.get1_2(carbon))
607 .filter((Atom a) -> a != priorAtom)
608 .filter((Atom a) -> !hydrogen.contains(a))
609 .findAny();
610 }
611
612
613
614
615
616
617 public static boolean renameAminoAcidToPDBStandard(Residue residue) {
618 if (residue.getChainID() == null) {
619 if (logger.isLoggable(Level.FINE)) {
620 logger.fine(" Setting Chain ID to Z for " + residue);
621 }
622 residue.setChainID('Z');
623 }
624 AminoAcid3 aa3 = residue.getAminoAcid3();
625 final Atom N = findNitrogenAtom(residue);
626 if (N != null) {
627
628 N.setName("N");
629
630
631 Atom CA = getAlphaCarbon(residue, N);
632 CA.setName("CA");
633
634
635 List<Atom> hydrogenForCA = findBondedAtoms(CA, 1);
636 switch (aa3) {
637 case NME -> {
638
639 findBondedAtoms(N, 1).get(0).setName("H");
640 CA.setName("CH3");
641 for (int i = 1; i <= 3; i++) {
642 hydrogenForCA.get(i - 1).setName(format("H%d", i));
643 }
644 return true;
645 }
646 case GLY -> {
647 hydrogenForCA.get(0).setName("HA2");
648 hydrogenForCA.get(1).setName("HA3");
649 }
650 default -> hydrogenForCA.get(0).setName("HA");
651 }
652
653
654 Atom C = null;
655
656 Atom CB = null;
657 for (Atom carbon : findBondedAtoms(CA, 6)) {
658
659 if (hasAttachedAtom(carbon, 8) && !hasAttachedAtom(carbon, 1)) {
660 C = carbon;
661 C.setName("C");
662 } else {
663 CB = carbon;
664 CB.setName("CB");
665 }
666 }
667 if (C == null) {
668 throw new IllegalArgumentException(
669 format(" The carbonyl carbon for residue %s could not be found.", residue));
670 }
671 if (CB == null && aa3 != AminoAcidUtils.AminoAcid3.GLY) {
672 throw new IllegalArgumentException(
673 format(" The beta carbon for residue %s could not be found.", residue));
674 }
675
676
677 List<Atom> cTerminalOxygen = findBondedAtoms(C, 8);
678 switch (cTerminalOxygen.size()) {
679 case 1 ->
680
681 cTerminalOxygen.get(0).setName("O");
682 case 2 -> {
683 Atom O = null;
684 for (Atom oxygen : cTerminalOxygen) {
685 if (oxygen.getBonds().size() == 2) {
686 O = oxygen;
687 O.setName("OH");
688 findBondedAtoms(O, 1).get(0).setName("HO");
689 }
690 }
691 if (O == null) {
692 cTerminalOxygen.get(0).setName("O");
693 cTerminalOxygen.get(1).setName("OXT");
694 }
695 }
696 }
697
698
699 List<Atom> amideProtons = findBondedAtoms(N, 1);
700 if (amideProtons.size() == 1) {
701 amideProtons.get(0).setName("H");
702 } else {
703 for (int i = 1; i <= amideProtons.size(); i++) {
704 amideProtons.get(i - 1).setName(format("H%d", i));
705 }
706 }
707
708
709 renameCommonAminoAcids(residue, aa3, CA, CB);
710 } else if (aa3 == AminoAcid3.ACE) {
711 Atom O = findAtomsOfElement(residue, 8).get(0);
712 O.setName("O");
713 Atom C = findBondedAtoms(O, 6).get(0);
714 C.setName("C");
715 Atom CH3 = findBondedAtoms(C, 6).get(0);
716 CH3.setName("CH3");
717 List<Atom> hydrogen = findBondedAtoms(CH3, 1);
718 for (int i = 1; i <= 3; i++) {
719 hydrogen.get(i - 1).setName(format("H%d", i));
720 }
721 } else {
722
723
724 logger.warning(format(" Mapping of nitrogen to residue (%s) failed. Trying to remap to molecule.", residue));
725 return false;
726 }
727 return true;
728 }
729
730
731
732
733
734
735
736 public static void renameArginineHydrogen(Residue residue, List<Atom> resAtoms) {
737 Atom HH11 = (Atom) residue.getAtomNode("HH11");
738 Atom HH12 = (Atom) residue.getAtomNode("HH12");
739 Atom HH21 = (Atom) residue.getAtomNode("HH21");
740 Atom HH22 = (Atom) residue.getAtomNode("HH22");
741 if (HH11 != null) {
742 resAtoms.remove(HH11);
743 }
744 if (HH12 != null) {
745 resAtoms.remove(HH12);
746 }
747 if (HH21 != null) {
748 resAtoms.remove(HH21);
749 }
750 if (HH22 != null) {
751 resAtoms.remove(HH22);
752 }
753 if (!resAtoms.isEmpty() && HH11 == null) {
754 resAtoms.get(0).setName("HH11");
755 resAtoms.remove(0);
756 }
757 if (!resAtoms.isEmpty() && HH12 == null) {
758 resAtoms.get(0).setName("HH12");
759 resAtoms.remove(0);
760 }
761 if (!resAtoms.isEmpty() && HH21 == null) {
762 resAtoms.get(0).setName("HH21");
763 resAtoms.remove(0);
764 }
765 if (!resAtoms.isEmpty() && HH22 == null) {
766 resAtoms.get(0).setName("HH22");
767 resAtoms.remove(0);
768 }
769 }
770
771
772
773
774
775
776
777 public static void renameAsparagineHydrogen(Residue residue, List<Atom> resAtoms) {
778 Atom HD21 = (Atom) residue.getAtomNode("HD21");
779 Atom HD22 = (Atom) residue.getAtomNode("HD22");
780 if (HD21 != null) {
781 resAtoms.remove(HD21);
782 }
783 if (HD22 != null) {
784 resAtoms.remove(HD22);
785 }
786 if (!resAtoms.isEmpty() && HD21 == null) {
787 resAtoms.get(0).setName("HD21");
788 resAtoms.remove(0);
789 }
790 if (!resAtoms.isEmpty() && HD22 == null) {
791 resAtoms.get(0).setName("HD21");
792 }
793 }
794
795
796
797
798
799
800
801
802
803
804 public static void renameAtomsToPDBStandard(MolecularAssembly molecularAssembly) {
805 Polymer[] polymers = molecularAssembly.getChains();
806 if (polymers != null) {
807 for (Polymer polymer : polymers) {
808 for (Residue residue : polymer.getResidues()) {
809 switch (residue.getResidueType()) {
810 case AA -> renameAminoAcidToPDBStandard(residue);
811 case NA -> renameNucleicAcidToPDBStandard(residue);
812 case UNK -> {
813
814 }
815 }
816 }
817 }
818 }
819 }
820
821
822
823
824
825
826
827
828 public static void renameBetaHydrogen(Residue residue, List<Atom> resAtoms, int indexes) {
829 Atom[] HBn = new Atom[3];
830 switch (indexes) {
831 case 12 -> {
832 HBn[0] = (Atom) residue.getAtomNode("HB1");
833 HBn[1] = (Atom) residue.getAtomNode("HB2");
834 }
835 case 13 -> {
836 HBn[0] = (Atom) residue.getAtomNode("HB1");
837 HBn[2] = (Atom) residue.getAtomNode("HB3");
838 }
839 case 23 -> {
840 HBn[1] = (Atom) residue.getAtomNode("HB2");
841 HBn[2] = (Atom) residue.getAtomNode("HB3");
842 }
843 default -> {
844 return;
845 }
846 }
847 for (Atom HBatom : HBn) {
848 resAtoms.remove(HBatom);
849 }
850 if (!resAtoms.isEmpty() && HBn[0] == null && (indexes == 12 || indexes == 13)) {
851 resAtoms.get(0).setName("HB1");
852 resAtoms.remove(0);
853 }
854 if (!resAtoms.isEmpty() && HBn[1] == null && (indexes == 12 || indexes == 23)) {
855 resAtoms.get(0).setName("HB2");
856 resAtoms.remove(0);
857 }
858 if (!resAtoms.isEmpty() && HBn[2] == null && (indexes == 13 || indexes == 23)) {
859 resAtoms.get(0).setName("HB3");
860 resAtoms.remove(0);
861 }
862 }
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879 public static Optional<Atom> renameBranchedAlkyl(Atom carbon, Atom priorAtom, int protonOffset,
880 int branchNum, char posName) {
881 carbon.setName(format("C%c%d", posName, branchNum));
882 List<Atom> hydrogen = findBondedAtoms(carbon, 1);
883 int numH = hydrogen.size();
884 if (numH == 1) {
885 hydrogen.get(0).setName(format("H%c%d", posName, branchNum));
886 } else {
887 for (int i = 0; i < numH; i++) {
888 hydrogen.get(i).setName(format("H%c%d%d", posName, branchNum, i + protonOffset));
889 }
890 }
891
892 return carbon.getBonds().stream()
893 .map((Bond b) -> b.get1_2(carbon))
894 .filter((Atom a) -> a != priorAtom)
895 .filter((Atom a) -> !hydrogen.contains(a))
896 .findAny();
897 }
898
899
900
901
902
903
904
905
906
907 public static void renameCommonAminoAcids(Residue residue, AminoAcid3 aa3, Atom CA, Atom CB) {
908 switch (aa3) {
909 case ALA: {
910 renameAlkyl(CB, CA, 1, 'B');
911 }
912 break;
913 case CYS:
914 case CYD: {
915 Atom SG = renameAlkyl(CB, CA, 2, 'B').get();
916 SG.setName("SG");
917 if (hasAttachedAtom(SG, 1)) {
918 assert aa3 == AminoAcidUtils.AminoAcid3.CYS;
919 findBondedAtoms(SG, 1).get(0).setName("HG");
920 } else if (hasAttachedAtom(SG, 16)) {
921 logger.finer(format(" SG atom %s likely part of a disulfide bond.", SG));
922 } else {
923 residue.setName("CYD");
924 }
925 }
926 break;
927 case ASP:
928 case ASH:
929 case ASD: {
930 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
931 CG.setName("CG");
932 List<Atom> ODs = findBondedAtoms(CG, 8);
933
934 int protonatedOD = -1;
935 for (int i = 0; i < 2; i++) {
936 if (hasAttachedAtom(ODs.get(i), 1)) {
937 protonatedOD = i;
938 break;
939 }
940 }
941
942
943 if (hasAttachedAtom(ODs.get(0), 1) && hasAttachedAtom(ODs.get(1), 1)) {
944 protonatedOD = 2;
945 }
946
947 switch (protonatedOD) {
948 case -1 -> {
949 ODs.get(0).setName("OD1");
950 ODs.get(1).setName("OD2");
951 }
952 case 0 -> {
953 if (aa3 != AminoAcid3.ASH) {
954 residue.setName("ASH");
955 }
956 ODs.get(0).setName("OD2");
957 findBondedAtoms(ODs.get(0), 1).get(0).setName("HD2");
958 ODs.get(1).setName("OD1");
959 }
960 case 1 -> {
961 if (aa3 != AminoAcid3.ASH) {
962 residue.setName("ASH");
963 }
964 ODs.get(1).setName("OD2");
965 findBondedAtoms(ODs.get(1), 1).get(0).setName("HD2");
966 ODs.get(0).setName("OD1");
967 }
968 case 2 -> {
969 if (aa3 != AminoAcid3.ASD) {
970 residue.setName("ASD");
971 }
972 ODs.get(0).setName("OD1");
973 findBondedAtoms(ODs.get(0), 1).get(0).setName("HD1");
974 ODs.get(1).setName("OD2");
975 findBondedAtoms(ODs.get(1), 1).get(0).setName("HD2");
976 }
977 }
978 }
979 break;
980 case GLU:
981 case GLH:
982 case GLD: {
983 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
984 Atom CD = renameAlkyl(CG, CB, 2, 'G').get();
985 CD.setName("CD");
986 List<Atom> OEs = findBondedAtoms(CD, 8);
987
988 int protonatedOE = -1;
989 for (int i = 0; i < 2; i++) {
990 if (hasAttachedAtom(OEs.get(i), 1)) {
991 protonatedOE = i;
992 break;
993 }
994 }
995
996
997 if (hasAttachedAtom(OEs.get(0), 1) && hasAttachedAtom(OEs.get(1), 1)) {
998 protonatedOE = 2;
999 }
1000
1001 switch (protonatedOE) {
1002 case -1 -> {
1003 OEs.get(0).setName("OE1");
1004 OEs.get(1).setName("OE2");
1005 }
1006 case 0 -> {
1007 if (aa3 != AminoAcid3.GLH) {
1008 residue.setName("GLH");
1009 }
1010 OEs.get(0).setName("OE2");
1011 findBondedAtoms(OEs.get(0), 1).get(0).setName("HE2");
1012 OEs.get(1).setName("OE1");
1013 }
1014 case 1 -> {
1015 if (aa3 != AminoAcid3.GLH) {
1016 residue.setName("GLH");
1017 }
1018 OEs.get(1).setName("OE2");
1019 findBondedAtoms(OEs.get(1), 1).get(0).setName("HE2");
1020 OEs.get(0).setName("OE1");
1021 }
1022 case 2 -> {
1023 if (aa3 != AminoAcid3.GLD) {
1024 residue.setName("GLD");
1025 }
1026 OEs.get(0).setName("OE1");
1027 findBondedAtoms(OEs.get(0), 1).get(0).setName("HE1");
1028 OEs.get(1).setName("OE2");
1029 findBondedAtoms(OEs.get(1), 1).get(0).setName("HE2");
1030 }
1031 }
1032 }
1033 break;
1034 case PHE: {
1035 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1036 CG.setName("CG");
1037 List<Atom> CDs = findBondedAtoms(CG, CB, 6);
1038
1039 Atom CZ = null;
1040 for (int i = 1; i <= 2; i++) {
1041 Atom CD = CDs.get(i - 1);
1042 Atom CE = renameBranchedAlkyl(CD, CG, 0, i, 'D').get();
1043 CZ = renameBranchedAlkyl(CE, CD, 0, i, 'E').get();
1044 }
1045 CZ.setName("CZ");
1046 findBondedAtoms(CZ, 1).get(0).setName("HZ");
1047 }
1048 break;
1049 case GLY:
1050 break;
1051 case HIS:
1052 case HIE:
1053 case HID: {
1054 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1055 CG.setName("CG");
1056
1057 Atom CD2 = findBondedAtoms(CG, 6).stream().filter((Atom a) -> a != CB).findAny().get();
1058 CD2.setName("CD2");
1059 findBondedAtoms(CD2, 1).get(0).setName("HD2");
1060
1061 Atom NE2 = findBondedAtoms(CD2, 7).get(0);
1062 NE2.setName("NE2");
1063 List<Atom> HE2 = findBondedAtoms(NE2, 1);
1064 boolean epsProtonated = (HE2 != null && !HE2.isEmpty());
1065 if (epsProtonated) {
1066 HE2.get(0).setName("HE2");
1067 }
1068
1069 Atom CE1 = findBondedAtoms(NE2, CD2, 6).get(0);
1070 CE1.setName("CE1");
1071 findBondedAtoms(CE1, 1).get(0).setName("HE1");
1072
1073 Atom ND1 = findBondedAtoms(CG, 7).get(0);
1074 ND1.setName("ND1");
1075 List<Atom> HD1 = findBondedAtoms(ND1, 1);
1076 boolean deltaProtonated = (HD1 != null && !HD1.isEmpty());
1077 if (deltaProtonated) {
1078 HD1.get(0).setName("HD1");
1079 }
1080
1081
1082 if (epsProtonated && deltaProtonated) {
1083 assert aa3 == AminoAcidUtils.AminoAcid3.HIS;
1084 } else if (epsProtonated) {
1085 residue.setName("HIE");
1086 } else if (deltaProtonated) {
1087 residue.setName("HID");
1088 } else {
1089 throw new IllegalArgumentException(
1090 format(" Histidine residue %s is doubly deprotonated!", residue));
1091 }
1092 }
1093 break;
1094 case ILE: {
1095 findBondedAtoms(CB, 1).get(0).setName("HB");
1096 List<Atom> CGs = findBondedAtoms(CB, CA, 6);
1097
1098 for (Atom CG : CGs) {
1099 List<Atom> HGs = findBondedAtoms(CG, 1);
1100 int numHGs = HGs.size();
1101 if (numHGs == 3) {
1102 renameBranchedAlkyl(CG, CB, 1, 2, 'G');
1103 } else if (numHGs == 2) {
1104 Atom CD1 = renameBranchedAlkyl(CG, CB, 2, 1, 'G').get();
1105 renameBranchedAlkyl(CD1, CG, 1, 1, 'D');
1106 } else {
1107 throw new IllegalArgumentException(
1108 format(
1109 " Isoleucine residue %s had %d gamma hydrogen, expecting 2-3!",
1110 residue, numHGs));
1111 }
1112 }
1113 }
1114 break;
1115 case LYS:
1116 case LYD: {
1117 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1118 Atom CD = renameAlkyl(CG, CB, 2, 'G').get();
1119 Atom CE = renameAlkyl(CD, CG, 2, 'D').get();
1120 Atom NZ = renameAlkyl(CE, CD, 2, 'E').get();
1121
1122 renameAlkyl(NZ, CE, 1, 'Z');
1123 NZ.setName("NZ");
1124 int numH = findBondedAtoms(NZ, 1).size();
1125 switch (numH) {
1126 case 2 -> residue.setName("LYD");
1127 case 3 -> {
1128 assert aa3 == AminoAcid3.LYS;
1129 }
1130 default -> throw new IllegalArgumentException(
1131 format(" Lysine residue %s had %d amine protons, expecting 2-3!", residue, numH));
1132 }
1133 }
1134 break;
1135 case LEU: {
1136 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1137 CG.setName("CG");
1138 findBondedAtoms(CG, 1).get(0).setName("HG");
1139 List<Atom> CDs = findBondedAtoms(CG, CB, 6);
1140
1141 for (int i = 0; i < 2; i++) {
1142 renameBranchedAlkyl(CDs.get(i), CG, 1, (i + 1), 'D');
1143 }
1144 }
1145 break;
1146 case MET: {
1147 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1148 Atom SD = renameAlkyl(CG, CB, 2, 'G').get();
1149 Atom CE = renameAlkyl(SD, CG, 0, 'D').get();
1150
1151 SD.setName("SD");
1152 renameAlkyl(CE, SD, 1, 'E');
1153 }
1154 break;
1155 case ASN: {
1156 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1157 CG.setName("CG");
1158 findBondedAtoms(CG, 8).get(0).setName("OD1");
1159 Atom ND2 = findBondedAtoms(CG, 7).get(0);
1160 renameBranchedAlkyl(ND2, CG, 1, 2, 'D');
1161
1162 ND2.setName("ND2");
1163 }
1164 break;
1165 case PRO: {
1166 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1167 Atom CD = renameAlkyl(CG, CB, 2, 'G').get();
1168 Atom N = renameAlkyl(CD, CG, 2, 'D').get();
1169 assert N.getName().equals("N");
1170 }
1171 break;
1172 case GLN: {
1173 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1174 Atom CD = renameAlkyl(CG, CB, 2, 'G').get();
1175 CD.setName("CD");
1176
1177 findBondedAtoms(CD, 8).get(0).setName("OE1");
1178 Atom NE2 = findBondedAtoms(CD, 7).get(0);
1179 renameBranchedAlkyl(NE2, CD, 1, 2, 'E');
1180
1181 NE2.setName("NE2");
1182 }
1183 break;
1184 case ARG: {
1185 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1186 Atom CD = renameAlkyl(CG, CB, 2, 'G').get();
1187 Atom NE = renameAlkyl(CD, CG, 2, 'D').get();
1188 Atom CZ = renameAlkyl(NE, CD, 0, 'E').get();
1189 NE.setName("NE");
1190 CZ.setName("CZ");
1191
1192 List<Atom> NHs = findBondedAtoms(CZ, NE, 7);
1193 assert NHs.size() == 2;
1194 for (int i = 0; i < 2; i++) {
1195 Atom NHx = NHs.get(i);
1196 renameBranchedAlkyl(NHx, CZ, 1, (i + 1), 'H');
1197 NHx.setName(format("NH%d", (i + 1)));
1198 }
1199 }
1200 break;
1201 case SER: {
1202 Atom OG = renameAlkyl(CB, CA, 2, 'B').get();
1203 renameAlkyl(OG, CB, 0, 'G');
1204 OG.setName("OG");
1205 }
1206 break;
1207 case THR: {
1208 CB.setName("CB");
1209 findBondedAtoms(CB, 1).get(0).setName("HB");
1210
1211 Atom OG1 = findBondedAtoms(CB, 8).get(0);
1212 OG1.setName("OG1");
1213 findBondedAtoms(OG1, 1).get(0).setName("HG1");
1214
1215 Atom CG2 = findBondedAtoms(CB, CA, 6).get(0);
1216 renameBranchedAlkyl(CG2, CB, 1, 2, 'G');
1217 }
1218 break;
1219 case VAL: {
1220 CB.setName("CB");
1221 findBondedAtoms(CB, 1).get(0).setName("HB");
1222
1223 List<Atom> CGs = findBondedAtoms(CB, CA, 6);
1224
1225 assert CGs.size() == 2;
1226 for (int i = 0; i < 2; i++) {
1227 Atom CGx = CGs.get(i);
1228 renameBranchedAlkyl(CGx, CB, 1, (i + 1), 'G');
1229 }
1230 }
1231 break;
1232 case TRP: {
1233 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1234 CG.setName("CG");
1235 List<Atom> CDs = findBondedAtoms(CG, CB, 6);
1236 Atom CD1 = null;
1237 Atom CD2 = null;
1238
1239 for (Atom CDx : CDs) {
1240 if (hasAttachedAtom(CDx, 1)) {
1241 CD1 = CDx;
1242 } else {
1243 CD2 = CDx;
1244 CD2.setName("CD2");
1245 }
1246 }
1247 Atom NE1 = renameBranchedAlkyl(CD1, CG, 0, 1, 'D').get();
1248 Atom CE2 = renameBranchedAlkyl(NE1, CD1, 0, 1, 'E').get();
1249 NE1.setName("NE1");
1250 CE2.setName("CE2");
1251
1252 Atom CZ2 = findBondedAtoms(CE2, CD2, 6).get(0);
1253 Atom CH2 = renameBranchedAlkyl(CZ2, CE2, 0, 2, 'Z').get();
1254 Atom CZ3 = renameBranchedAlkyl(CH2, CZ2, 0, 2, 'H').get();
1255 Atom CE3 = renameBranchedAlkyl(CZ3, CH2, 0, 3, 'Z').get();
1256 if (CD2 != renameBranchedAlkyl(CE3, CZ3, 0, 3, 'E').get()) {
1257 throw new IllegalArgumentException(
1258 format(" Error in cyclizing tryptophan %s!", residue));
1259 }
1260 }
1261 break;
1262 case TYR:
1263 case TYD: {
1264 Atom CG = renameAlkyl(CB, CA, 2, 'B').get();
1265 CG.setName("CG");
1266 List<Atom> CDs = findBondedAtoms(CG, CB, 6);
1267 Atom CZ = null;
1268
1269 assert CDs.size() == 2;
1270 for (int i = 1; i <= 2; i++) {
1271 Atom CDx = CDs.get(i - 1);
1272 Atom CEx = renameBranchedAlkyl(CDx, CG, 0, i, 'D').get();
1273 CZ = renameBranchedAlkyl(CEx, CDx, 0, i, 'E').get();
1274 }
1275
1276 CZ.setName("CZ");
1277 Atom OH = findBondedAtoms(CZ, 8).get(0);
1278 OH.setName("OH");
1279 if (hasAttachedAtom(OH, 1)) {
1280 assert aa3 == AminoAcidUtils.AminoAcid3.TYR;
1281 findBondedAtoms(OH, 1).get(0).setName("HH");
1282 } else {
1283 residue.setName("TYD");
1284 }
1285 }
1286 break;
1287 default:
1288 throw new IllegalArgumentException(
1289 (format(" Amino acid %s (%s) not recognized!", residue, aa3)));
1290 }
1291 }
1292
1293
1294
1295
1296
1297
1298
1299 public static void renameCommonNucleicAcid(Residue residue, NucleicAcid3 na3) {
1300 Optional<Atom> optO4s = findNucleotideO4s(residue);
1301 if (optO4s.isPresent()) {
1302
1303 Atom O4s = optO4s.get();
1304 O4s.setName("O4'");
1305
1306
1307 List<Atom> bondedC = findBondedAtoms(O4s, 6);
1308 Atom C4s = null;
1309 Atom C1s = null;
1310
1311 Atom N19 = null;
1312 Atom H1s = null;
1313 for (Atom c : bondedC) {
1314 if (hasAttachedAtom(c, 7)) {
1315 C1s = c;
1316 C1s.setName("C1'");
1317 H1s = findBondedAtoms(C1s, 1).get(0);
1318 H1s.setName("H1'");
1319 N19 = findBondedAtoms(C1s, 7).get(0);
1320 } else {
1321 C4s = c;
1322 C4s.setName("C4'");
1323 findBondedAtoms(C4s, 1).get(0).setName("H4'");
1324 }
1325 }
1326 assert C4s != null && C1s != null;
1327
1328 Atom C2s = findBondedAtoms(C1s, 6).get(0);
1329 C2s.setName("C2'");
1330
1331 bondedC = findBondedAtoms(C4s, 6);
1332 Atom C5s = null;
1333 Atom C3s;
1334 Atom O3s;
1335 for (Atom c : bondedC) {
1336 if (c.getBonds().stream().anyMatch(b -> b.get1_2(c) == C2s)) {
1337 C3s = c;
1338 C3s.setName("C3'");
1339 O3s = findBondedAtoms(C3s, 8).get(0);
1340 O3s.setName("O3'");
1341 findBondedAtoms(C3s, 1).get(0).setName("H3'");
1342 if (hasAttachedAtom(O3s, 1)) {
1343 findBondedAtoms(O3s, 1).get(0).setName("HO3'");
1344 }
1345 } else {
1346 C5s = c;
1347 C5s.setName("C5'");
1348 List<Atom> allH5List = findBondedAtoms(C5s, 1);
1349 Atom[] allH5s = allH5List.toArray(new Atom[0]);
1350 sortAtomsByDistance(O4s, allH5s);
1351 allH5s[0].setName("H5'");
1352 allH5s[1].setName("H5''");
1353 }
1354 }
1355
1356 if (hasAttachedAtom(C2s, 8)) {
1357 Atom O2s = findBondedAtoms(C2s, 8).get(0);
1358 O2s.setName("O2'");
1359 findBondedAtoms(O2s, 1).get(0).setName("HO2'");
1360 findBondedAtoms(C2s, 1).get(0).setName("H2'");
1361 } else {
1362 List<Atom> bothH2List = findBondedAtoms(C2s, 1);
1363 Atom[] bothH2 = bothH2List.toArray(new Atom[0]);
1364 sortAtomsByDistance(H1s, bothH2);
1365
1366 bothH2[0].setName("H2''");
1367 bothH2[1].setName("H2'");
1368 }
1369
1370
1371 Atom O5s = findBondedAtoms(C5s, 8).get(0);
1372 O5s.setName("O5'");
1373
1374 if (hasAttachedAtom(O5s, 1)) {
1375 findBondedAtoms(O5s, 1).get(0).setName("HO5'");
1376 } else if (hasAttachedAtom(O5s, 15)) {
1377 Atom P = findBondedAtoms(O5s, 15).get(0);
1378 P.setName("P");
1379 List<Atom> bondedO = findBondedAtoms(P, O5s, 8);
1380 List<Atom> thisResO = bondedO.stream().filter(o -> residue.getAtomList().contains(o))
1381 .toList();
1382 int nBonded = bondedO.size();
1383 int nRes = thisResO.size();
1384 if (nBonded == 0) {
1385
1386 } else if (nBonded == nRes) {
1387 Atom OP1 = bondedO.get(0);
1388 OP1.setName("OP1");
1389
1390 final double[] xyzC5s = C5s.getXYZ(new double[3]);
1391 final double[] xyzO5s = O5s.getXYZ(new double[3]);
1392 final double[] xyzP = P.getXYZ(new double[3]);
1393 final double[] xyzOP1 = OP1.getXYZ(new double[3]);
1394 double dihedral = dihedralAngle(xyzC5s, xyzO5s, xyzP, xyzOP1);
1395 double twoPiOver3 = 2.0 * PI / 3.0;
1396 double target = modToRange(dihedral + twoPiOver3, -PI, PI);
1397 List<Atom> otherO = bondedO.stream().filter(o -> o != OP1).sorted(
1398 Comparator.comparingDouble((Atom o) -> {
1399 double[] xyzO = o.getXYZ(new double[3]);
1400 double dihedO = dihedralAngle(xyzC5s, xyzO5s, xyzP, xyzO);
1401 double diff = dihedO - target;
1402 double twoPi = 2 * PI;
1403 diff = modToRange(diff, 0, twoPi);
1404 diff = diff < PI ? diff : twoPi - diff;
1405 return diff;
1406 })).toList();
1407 for (int i = 0; i < otherO.size(); i++) {
1408 otherO.get(i).setName(format("OP%d", i + 2));
1409 }
1410 } else {
1411 Atom nextO3s =
1412 bondedO.stream().filter(o -> !residue.getAtomList().contains(o)).findAny().get();
1413
1414
1415 final double[] xyzC5s = C5s.getXYZ(new double[3]);
1416 final double[] xyzO5s = O5s.getXYZ(new double[3]);
1417 final double[] xyzP = P.getXYZ(new double[3]);
1418 final double[] xyzNextO3s = nextO3s.getXYZ(new double[3]);
1419 double dihedral = dihedralAngle(xyzC5s, xyzO5s, xyzP, xyzNextO3s);
1420 double twoPiOver3 = 2.0 * PI / 3.0;
1421 double target = modToRange(dihedral + twoPiOver3, -PI, PI);
1422 List<Atom> otherO = bondedO.stream().filter(o -> o != nextO3s).sorted(
1423 Comparator.comparingDouble((Atom o) -> {
1424 double[] xyzO = o.getXYZ(new double[3]);
1425 double dihedO = dihedralAngle(xyzC5s, xyzO5s, xyzP, xyzO);
1426 double diff = dihedO - target;
1427 double twoPi = 2 * PI;
1428 diff = modToRange(diff, 0, twoPi);
1429 diff = diff < PI ? diff : twoPi - diff;
1430 return diff;
1431 })).toList();
1432 for (int i = 0; i < otherO.size(); i++) {
1433 otherO.get(i).setName(format("OP%d", i + 1));
1434 }
1435 }
1436
1437 for (Atom op : bondedO) {
1438 if (hasAttachedAtom(op, 1)) {
1439 findBondedAtoms(op, 1).get(0).setName("H" + op.getName());
1440 }
1441 }
1442 }
1443 renameCommonNucleobase(N19, C1s, na3);
1444 } else {
1445 logger.warning(" Could not find O4' for residue " + residue);
1446 }
1447 }
1448
1449
1450
1451
1452
1453
1454
1455
1456 public static void renameCommonNucleobase(Atom N19, Atom C1s, NucleicAcid3 na3) {
1457 switch (na3) {
1458 case ADE, DAD -> {
1459 Map<String, Atom> purineBase = renameCommonPurine(N19, C1s);
1460
1461 findBondedAtoms(purineBase.get("C2"), 1).get(0).setName("H2");
1462 Atom C6 = purineBase.get("C6");
1463 Atom N1 = purineBase.get("N1");
1464 Atom N6 = findBondedAtoms(C6, N1, 7).get(0);
1465 N6.setName("N6");
1466 List<Atom> allH6List = findBondedAtoms(N6, 1);
1467 Atom[] allH6 = sortAtomsByDistance(N1, allH6List);
1468 allH6[0].setName("H61");
1469 allH6[1].setName("H62");
1470 }
1471 case CYT, DCY -> {
1472 Map<String, Atom> pyrimidineBase = renameCommonPyrimidine(N19, C1s);
1473
1474 Atom C4 = pyrimidineBase.get("C4");
1475 Atom N3 = pyrimidineBase.get("N3");
1476 Atom N4 = findBondedAtoms(C4, N3, 7).get(0);
1477 N4.setName("N4");
1478 Atom[] allH4 = sortAtomsByDistance(N3, findBondedAtoms(N4, 1));
1479 allH4[0].setName("H41");
1480 allH4[1].setName("H42");
1481 }
1482 case GUA, DGU -> {
1483 Map<String, Atom> purineBase = renameCommonPurine(N19, C1s);
1484
1485 Atom N1 = purineBase.get("N1");
1486 Atom C2 = purineBase.get("C2");
1487 Atom C6 = purineBase.get("C6");
1488 findBondedAtoms(N1, 1).get(0).setName("H1");
1489 Atom N2 =
1490 findBondedAtoms(C2, N1, 7).stream()
1491 .filter(n -> hasAttachedAtom(n, 1))
1492 .findAny()
1493 .get();
1494 N2.setName("N2");
1495 Atom[] allH2 = sortAtomsByDistance(N1, findBondedAtoms(N2, 1));
1496 allH2[0].setName("H21");
1497 allH2[1].setName("H22");
1498 findBondedAtoms(C6, 8).get(0).setName("O6");
1499 }
1500 case URI -> {
1501 Map<String, Atom> pyrimidineBase = renameCommonPyrimidine(N19, C1s);
1502
1503 findBondedAtoms(pyrimidineBase.get("N3"), 1).get(0).setName("H3");
1504 findBondedAtoms(pyrimidineBase.get("C4"), 8).get(0).setName("O4");
1505 }
1506 case THY, DTY -> {
1507 Map<String, Atom> pyrimidineBase = renameCommonPyrimidine(N19, C1s);
1508
1509 findBondedAtoms(pyrimidineBase.get("N3"), 1).get(0).setName("H3");
1510 findBondedAtoms(pyrimidineBase.get("C4"), 8).get(0).setName("O4");
1511 Atom C5 = pyrimidineBase.get("C5");
1512 for (Atom c : findBondedAtoms(C5, 6)) {
1513 List<Atom> bondedH = findBondedAtoms(c, 1);
1514 if (bondedH != null && bondedH.size() == 3) {
1515 c.setName("C7");
1516 for (int i = 0; i < 3; i++) {
1517 bondedH.get(i).setName(format("H7%d", i + 1));
1518 }
1519 break;
1520 }
1521 }
1522 }
1523 }
1524 }
1525
1526
1527
1528
1529
1530
1531
1532
1533 public static Map<String, Atom> renameCommonPurine(Atom N9, Atom C1s) {
1534 Map<String, Atom> keyAtoms = new HashMap<>(10);
1535 N9.setName("N9");
1536 for (Atom c : findBondedAtoms(N9, C1s, 6)) {
1537 if (hasAttachedAtom(c, 1)) {
1538 Atom C8 = c;
1539 C8.setName("C8");
1540 findBondedAtoms(C8, 1).get(0).setName("H8");
1541 Atom N7 = findBondedAtoms(C8, N9, 7).get(0);
1542 N7.setName("N7");
1543 Atom C5 = findBondedAtoms(N7, C8, 6).get(0);
1544 C5.setName("C5");
1545 } else {
1546 Atom C4 = c;
1547 C4.setName("C4");
1548 Atom N3 = findBondedAtoms(C4, N9, 7).get(0);
1549 N3.setName("N3");
1550 Atom C2 = findBondedAtoms(N3, C4, 6).get(0);
1551 C2.setName("C2");
1552 keyAtoms.put("C2", C2);
1553 Atom N1 = findBondedAtoms(C2, N3, 7).get(0);
1554 N1.setName("N1");
1555 keyAtoms.put("N1", N1);
1556 Atom C6 = findBondedAtoms(N1, C2, 6).get(0);
1557 C6.setName("C6");
1558 keyAtoms.put("C6", C6);
1559 }
1560 }
1561
1562 return keyAtoms;
1563 }
1564
1565
1566
1567
1568
1569
1570
1571
1572 public static Map<String, Atom> renameCommonPyrimidine(Atom N1, Atom C1s) {
1573 Map<String, Atom> keyAtoms = new HashMap<>();
1574 N1.setName("N1");
1575 for (Atom c : findBondedAtoms(N1, C1s, 6)) {
1576 if (hasAttachedAtom(c, 8)) {
1577 Atom C2 = c;
1578 C2.setName("C2");
1579 findBondedAtoms(C2, 8).get(0).setName("O2");
1580 Atom N3 = findBondedAtoms(C2, N1, 7).get(0);
1581 N3.setName("N3");
1582 keyAtoms.put("N3", N3);
1583 Atom C4 = findBondedAtoms(N3, C2, 6).get(0);
1584 C4.setName("C4");
1585 keyAtoms.put("C4", C4);
1586 Atom C5 = findBondedAtoms(C4, 6).get(0);
1587 C5.setName("C5");
1588 keyAtoms.put("C5", C5);
1589 if (hasAttachedAtom(C5, 1)) {
1590 findBondedAtoms(C5, 1).get(0).setName("H5");
1591 }
1592 } else {
1593 Atom C6 = c;
1594 C6.setName("C6");
1595 findBondedAtoms(C6, 1).get(0).setName("H6");
1596 }
1597 }
1598
1599
1600 return keyAtoms;
1601 }
1602
1603
1604
1605
1606
1607
1608
1609
1610 public static void renameDeltaHydrogen(Residue residue, List<Atom> resAtoms, int indexes) {
1611 Atom[] HDn = new Atom[3];
1612 switch (indexes) {
1613 case 12 -> {
1614 HDn[0] = (Atom) residue.getAtomNode("HD1");
1615 HDn[1] = (Atom) residue.getAtomNode("HD2");
1616 }
1617 case 13 -> {
1618 HDn[0] = (Atom) residue.getAtomNode("HD1");
1619 HDn[2] = (Atom) residue.getAtomNode("HD3");
1620 }
1621 case 23 -> {
1622 HDn[1] = (Atom) residue.getAtomNode("HD2");
1623 HDn[2] = (Atom) residue.getAtomNode("HD3");
1624 }
1625 default -> {
1626 return;
1627 }
1628 }
1629 for (Atom HDatom : HDn) {
1630 resAtoms.remove(HDatom);
1631 }
1632 if (!resAtoms.isEmpty() && HDn[0] == null && (indexes == 12 || indexes == 13)) {
1633 resAtoms.get(0).setName("HD1");
1634 resAtoms.remove(0);
1635 }
1636 if (!resAtoms.isEmpty() && HDn[1] == null && (indexes == 12 || indexes == 23)) {
1637 resAtoms.get(0).setName("HD2");
1638 resAtoms.remove(0);
1639 }
1640 if (!resAtoms.isEmpty() && HDn[2] == null && (indexes == 13 || indexes == 23)) {
1641 resAtoms.get(0).setName("HD3");
1642 resAtoms.remove(0);
1643 }
1644 }
1645
1646
1647
1648
1649
1650
1651
1652
1653 public static void renameEpsilonHydrogen(Residue residue, List<Atom> resAtoms, int indexes) {
1654 Atom[] HEn = new Atom[3];
1655 switch (indexes) {
1656 case 12 -> {
1657 HEn[0] = (Atom) residue.getAtomNode("HE1");
1658 HEn[1] = (Atom) residue.getAtomNode("HE2");
1659 }
1660 case 13 -> {
1661 HEn[0] = (Atom) residue.getAtomNode("HE1");
1662 HEn[2] = (Atom) residue.getAtomNode("HE3");
1663 }
1664 case 23 -> {
1665 HEn[1] = (Atom) residue.getAtomNode("HE2");
1666 HEn[2] = (Atom) residue.getAtomNode("HE3");
1667 }
1668 default -> {
1669 return;
1670 }
1671 }
1672 for (Atom HEatom : HEn) {
1673 resAtoms.remove(HEatom);
1674 }
1675 if (!resAtoms.isEmpty() && HEn[0] == null && (indexes == 12 || indexes == 13)) {
1676 resAtoms.get(0).setName("HE1");
1677 resAtoms.remove(0);
1678 }
1679 if (!resAtoms.isEmpty() && HEn[1] == null && (indexes == 12 || indexes == 23)) {
1680 resAtoms.get(0).setName("HE2");
1681 resAtoms.remove(0);
1682 }
1683 if (!resAtoms.isEmpty() && HEn[2] == null && (indexes == 13 || indexes == 23)) {
1684 resAtoms.get(0).setName("HE3");
1685 resAtoms.remove(0);
1686 }
1687 }
1688
1689
1690
1691
1692
1693
1694
1695
1696 public static void renameGammaHydrogen(Residue residue, List<Atom> resAtoms, int indexes) {
1697 Atom[] HGn = new Atom[3];
1698 switch (indexes) {
1699 case 12 -> {
1700 HGn[0] = (Atom) residue.getAtomNode("HG1");
1701 HGn[1] = (Atom) residue.getAtomNode("HG2");
1702 }
1703 case 13 -> {
1704 HGn[0] = (Atom) residue.getAtomNode("HG1");
1705 HGn[2] = (Atom) residue.getAtomNode("HG3");
1706 }
1707 case 23 -> {
1708 HGn[1] = (Atom) residue.getAtomNode("HG2");
1709 HGn[2] = (Atom) residue.getAtomNode("HG3");
1710 }
1711 default -> {
1712 return;
1713 }
1714 }
1715 for (Atom HGatom : HGn) {
1716 resAtoms.remove(HGatom);
1717 }
1718 if (!resAtoms.isEmpty() && HGn[0] == null && (indexes == 12 || indexes == 13)) {
1719 resAtoms.get(0).setName("HG1");
1720 resAtoms.remove(0);
1721 }
1722 if (!resAtoms.isEmpty() && HGn[1] == null && (indexes == 12 || indexes == 23)) {
1723 resAtoms.get(0).setName("HG2");
1724 resAtoms.remove(0);
1725 }
1726 if (!resAtoms.isEmpty() && HGn[2] == null && (indexes == 13 || indexes == 23)) {
1727 resAtoms.get(0).setName("HG3");
1728 resAtoms.remove(0);
1729 }
1730 }
1731
1732
1733
1734
1735
1736
1737
1738 public static void renameGlutamineHydrogen(Residue residue, List<Atom> resAtoms) {
1739 Atom HE21 = (Atom) residue.getAtomNode("HE21");
1740 Atom HE22 = (Atom) residue.getAtomNode("HE22");
1741 if (HE21 != null) {
1742 resAtoms.remove(HE21);
1743 }
1744 if (HE22 != null) {
1745 resAtoms.remove(HE22);
1746 }
1747 if (!resAtoms.isEmpty() && HE21 == null) {
1748 resAtoms.get(0).setName("HE21");
1749 resAtoms.remove(0);
1750 }
1751 if (!resAtoms.isEmpty() && HE22 == null) {
1752 resAtoms.get(0).setName("HE21");
1753 }
1754 }
1755
1756
1757
1758
1759
1760
1761
1762 public static void renameGlycineAlphaHydrogen(Residue residue, List<Atom> resAtoms) {
1763 Atom HA2 = (Atom) residue.getAtomNode("HA2");
1764 Atom HA3 = (Atom) residue.getAtomNode("HA3");
1765 if (HA2 != null) {
1766 resAtoms.remove(HA2);
1767 }
1768 if (HA3 != null) {
1769 resAtoms.remove(HA3);
1770 }
1771 if (HA2 == null && !resAtoms.isEmpty()) {
1772 resAtoms.get(0).setName("HA2");
1773 resAtoms.remove(0);
1774 }
1775 if (HA3 == null && !resAtoms.isEmpty()) {
1776 resAtoms.get(0).setName("HA3");
1777 }
1778 }
1779
1780
1781
1782
1783
1784
1785
1786 public static void renameIsoleucineHydrogen(Residue residue, List<Atom> resAtoms) {
1787 Atom HG12 = (Atom) residue.getAtomNode("HG12");
1788 Atom HG13 = (Atom) residue.getAtomNode("HG13");
1789 if (HG12 != null) {
1790 resAtoms.remove(HG12);
1791 }
1792 if (HG13 != null) {
1793 resAtoms.remove(HG13);
1794 }
1795 if (HG12 == null && !resAtoms.isEmpty()) {
1796 resAtoms.get(0).setName("HG12");
1797 resAtoms.remove(0);
1798 }
1799 if (HG13 == null && !resAtoms.isEmpty()) {
1800 resAtoms.get(0).setName("HG13");
1801 }
1802 }
1803
1804
1805
1806
1807
1808
1809 public static void renameNTerminusHydrogen(Residue residue) {
1810 Atom[] h = new Atom[3];
1811 h[0] = (Atom) residue.getAtomNode("H1");
1812 h[1] = (Atom) residue.getAtomNode("H2");
1813 h[2] = (Atom) residue.getAtomNode("H3");
1814 int numAtoms = 0;
1815 for (Atom atom : h) {
1816 numAtoms += (atom == null ? 0 : 1);
1817 }
1818 if (numAtoms == 3) {
1819 return;
1820 }
1821 List<Atom> resAtoms = residue.getAtomList();
1822 for (Atom resAtom : resAtoms) {
1823
1824 boolean doContinue = false;
1825 for (Atom hAtom : h) {
1826 if (resAtom.equals(hAtom)) {
1827 doContinue = true;
1828 break;
1829 }
1830 }
1831 if (doContinue) {
1832 continue;
1833 }
1834
1835
1836 String atomName = resAtom.getName().toUpperCase();
1837 if (atomName.equals("H") || atomName.matches("H[1-3]") || atomName.matches("[1-3]H")) {
1838 ++numAtoms;
1839 for (int i = 0; i < h.length; i++) {
1840 if (h[i] == null) {
1841 resAtom.setName("H" + (i + 1));
1842 h[i] = resAtom;
1843 break;
1844 }
1845 }
1846 if (numAtoms == 3) {
1847 return;
1848 }
1849 }
1850 }
1851 }
1852
1853
1854
1855
1856
1857
1858 public static void renameNucleicAcidToPDBStandard(Residue residue) {
1859 if (residue.getChainID() == null) {
1860 logger.info(" Setting Chain ID to Z for " + residue);
1861 residue.setChainID('Z');
1862 }
1863 assert residue.getResidueType() == Residue.ResidueType.NA;
1864 NucleicAcid3 na3 = residue.getNucleicAcid3(true);
1865 residue.setName(na3.toString());
1866 switch (na3) {
1867 case ADE, DAD, CYT, DCY, GUA, DGU, THY, DTY, URI -> renameCommonNucleicAcid(residue, na3);
1868 default -> logger.info(" Could not rename atoms for nonstandard nucleic acid " + na3);
1869 }
1870 }
1871
1872
1873
1874
1875
1876
1877
1878
1879 public static void renameZetaHydrogen(Residue residue, List<Atom> resAtoms, int indexes) {
1880 Atom[] HZn = new Atom[3];
1881 switch (indexes) {
1882 case 12 -> {
1883 HZn[0] = (Atom) residue.getAtomNode("HZ1");
1884 HZn[1] = (Atom) residue.getAtomNode("HZ2");
1885 }
1886 case 13 -> {
1887 HZn[0] = (Atom) residue.getAtomNode("HZ1");
1888 HZn[2] = (Atom) residue.getAtomNode("HZ3");
1889 }
1890 case 23 -> {
1891 HZn[1] = (Atom) residue.getAtomNode("HZ2");
1892 HZn[2] = (Atom) residue.getAtomNode("HZ3");
1893 }
1894 default -> {
1895 return;
1896 }
1897 }
1898 for (Atom HZatom : HZn) {
1899 resAtoms.remove(HZatom);
1900 }
1901 if (!resAtoms.isEmpty() && HZn[0] == null && (indexes == 12 || indexes == 13)) {
1902 resAtoms.get(0).setName("HZ1");
1903 resAtoms.remove(0);
1904 }
1905 if (!resAtoms.isEmpty() && HZn[1] == null && (indexes == 12 || indexes == 23)) {
1906 resAtoms.get(0).setName("HZ2");
1907 resAtoms.remove(0);
1908 }
1909 if (!resAtoms.isEmpty() && HZn[2] == null && (indexes == 13 || indexes == 23)) {
1910 resAtoms.get(0).setName("HZ3");
1911 resAtoms.remove(0);
1912 }
1913 }
1914
1915
1916 public enum HetAtoms {
1917 BR,
1918 CA,
1919 CA2,
1920 CL,
1921 K,
1922 MG,
1923 MG2,
1924 NA,
1925 HOH,
1926 H2O,
1927 WAT,
1928 ZN,
1929 ZN2;
1930
1931
1932
1933
1934
1935
1936
1937 public static HetAtoms parse(String str) {
1938 String hName = str.toUpperCase().replaceFirst("[0-9+\\-]+$", "");
1939 return valueOf(hName);
1940 }
1941 }
1942 }