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 ffx.numerics.math.DoubleMath;
41 import ffx.potential.MolecularAssembly;
42 import ffx.potential.bonded.AminoAcidUtils.AminoAcid3;
43 import ffx.potential.bonded.AminoAcidUtils.SideChainType;
44 import ffx.potential.parameters.AtomType;
45 import ffx.potential.parameters.BioType;
46 import ffx.potential.parameters.BondType;
47 import ffx.potential.parameters.ForceField;
48 import ffx.utilities.StringUtils;
49
50 import java.io.Serial;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Comparator;
54 import java.util.List;
55 import java.util.Optional;
56 import java.util.logging.Level;
57 import java.util.logging.Logger;
58 import java.util.stream.Collectors;
59
60 import static ffx.numerics.math.DoubleMath.length;
61 import static ffx.numerics.math.DoubleMath.normalize;
62 import static ffx.numerics.math.DoubleMath.scale;
63 import static ffx.numerics.math.DoubleMath.sub;
64 import static ffx.potential.bonded.Bond.logNoBondType;
65 import static ffx.potential.bonded.NamingUtils.nameAcetylCap;
66 import static java.lang.String.format;
67 import static java.lang.System.arraycopy;
68 import static org.apache.commons.math3.util.FastMath.abs;
69 import static org.apache.commons.math3.util.FastMath.cos;
70 import static org.apache.commons.math3.util.FastMath.max;
71 import static org.apache.commons.math3.util.FastMath.sin;
72 import static org.apache.commons.math3.util.FastMath.sqrt;
73 import static org.apache.commons.math3.util.FastMath.toRadians;
74
75
76
77
78
79
80
81 public class BondedUtils {
82
83 private static final Logger logger = Logger.getLogger(BondedUtils.class.getName());
84 private static final double eps = 0.0000001d;
85
86
87
88
89
90
91
92
93 public static boolean atomAttachedToAtom(Atom a1, Atom a2) {
94 assert a1 != a2;
95 return a1.getBonds().stream().anyMatch((Bond b) -> b.get1_2(a1) == a2);
96 }
97
98
99
100
101
102
103
104
105
106
107 public static Bond buildBond(Atom a1, Atom a2, ForceField forceField, List<Bond> bondList) {
108 Bond bond = new Bond(a1, a2);
109 BondType bondType = forceField.getBondType(a1.getAtomType(), a2.getAtomType());
110 if (bondType == null) {
111 logNoBondType(a1, a2, forceField);
112 } else {
113 bond.setBondType(bondType);
114 }
115 if (bondList != null) {
116 bondList.add(bond);
117 }
118 return bond;
119 }
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public static Atom buildHeavy(MSGroup residue, String atomName, Atom bondedTo,
134 int key, ForceField forceField, List<Bond> bondList)
135 throws MissingHeavyAtomException, MissingAtomTypeException {
136 Atom atom = (Atom) residue.getAtomNode(atomName);
137 AtomType atomType = findAtomType(key, forceField);
138 if (atomType == null) {
139 Residue res = (Residue) residue;
140 throw new MissingAtomTypeException(res, atom);
141 }
142 if (atom == null) {
143 throw new MissingHeavyAtomException(atomName, atomType, bondedTo);
144 }
145 atom.setAtomType(atomType);
146 if (bondedTo != null) {
147 buildBond(atom, bondedTo, forceField, bondList);
148 }
149 return atom;
150 }
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168 public static Atom buildHeavy(MSGroup residue, String atomName, Atom ia, double bond,
169 Atom ib, double angle1, Atom ic, double angle2, int chiral, int lookUp,
170 ForceField forceField) {
171 AtomType atomType = findAtomType(lookUp, forceField);
172 return buildHeavyAtom(residue, atomName, ia, bond, ib, angle1, ic, angle2, chiral, atomType);
173 }
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192 public static Atom buildHeavy(MSGroup residue, String atomName, Atom ia, double bond, Atom ib,
193 double angle1, Atom ic, double angle2, int chiral, int lookUp, ForceField forceField,
194 List<Bond> bondList) {
195 AtomType atomType = findAtomType(lookUp, forceField);
196 return buildHeavyAtom(residue, atomName, ia, bond, ib, angle1, ic, angle2, chiral, atomType,
197 forceField, bondList);
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216 public static Atom buildHeavy(MSGroup residue, SideChainType atomName, Atom ia, double bond,
217 Atom ib, double angle1, Atom ic, double angle2, int chiral, ForceField forceField,
218 List<Bond> bondList) {
219 AtomType atomType = findAtomType(atomName.getType(), forceField);
220 return buildHeavyAtom(residue, atomName.name(), ia, bond, ib, angle1, ic, angle2, chiral,
221 atomType, forceField, bondList);
222 }
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241 public static Atom buildH(MSGroup residue, String atomName, Atom ia, double bond, Atom ib,
242 double angle1, Atom ic, double angle2, int chiral, int lookUp, ForceField forceField,
243 List<Bond> bondList) {
244 AtomType atomType = findAtomType(lookUp, forceField);
245 return buildHydrogenAtom(residue, atomName, ia, bond, ib, angle1, ic, angle2, chiral,
246 atomType, forceField, bondList);
247 }
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 public static Atom buildH(MSGroup residue, SideChainType atomName, Atom ia, double bond,
266 Atom ib, double angle1, Atom ic, double angle2, int chiral, ForceField forceField,
267 List<Bond> bondList) {
268 AtomType atomType = findAtomType(atomName.getType(), forceField);
269 return buildHydrogenAtom(residue, atomName.name(), ia, bond, ib, angle1, ic, angle2,
270 chiral, atomType, forceField, bondList);
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290 public static Atom buildHydrogenAtom(MSGroup residue, String atomName, Atom ia, double bond,
291 Atom ib, double angle1, Atom ic, double angle2, int chiral, AtomType atomType,
292 ForceField forceField, List<Bond> bondList) {
293 if (atomType == null) {
294 return null;
295 }
296 Atom atom = (Atom) residue.getAtomNode(atomName);
297
298 if (atom == null) {
299 String dAtomName = atomName.replaceFirst("H", "D");
300 atom = (Atom) residue.getAtomNode(dAtomName);
301 }
302
303
304 if (atom == null && residue instanceof Molecule molecule) {
305 if (StringUtils.looksLikeWater(molecule.getName())) {
306 atom = (Atom) molecule.getAtomNode("H");
307 if (atom == null) {
308 atom = (Atom) molecule.getAtomNode("D");
309 }
310 if (atom != null) {
311 atom.setName(atomName);
312 }
313 }
314 }
315
316 if (atom == null) {
317 boolean buildDeuterium = forceField.getBoolean("build-deuterium", false);
318 if (buildDeuterium && atomName.startsWith("H")) {
319 atomName = atomName.replaceFirst("H", "D");
320 }
321 String resName = ia.getResidueName();
322 int resSeq = ia.getResidueNumber();
323 Character chainID = ia.getChainID();
324 Character altLoc = ia.getAltLoc();
325 String segID = ia.getSegID();
326 double occupancy = ia.getOccupancy();
327 double tempFactor = ia.getTempFactor();
328 atom = new Atom(0, atomName, altLoc, new double[3], resName, resSeq, chainID,
329 occupancy, tempFactor, segID, true);
330 residue.addMSNode(atom);
331 intxyz(atom, ia, bond, ib, angle1, ic, angle2, chiral);
332 }
333 atom.setAtomType(atomType);
334 buildBond(ia, atom, forceField, bondList);
335 return atom;
336 }
337
338
339
340
341
342
343
344
345 public static AtomType findAtomType(int key, ForceField forceField) {
346 BioType bioType = forceField.getBioType(Integer.toString(key));
347 if (bioType != null) {
348 AtomType atomType = forceField.getAtomType(Integer.toString(bioType.atomType));
349 if (atomType != null) {
350 return atomType;
351 } else {
352 logger.severe(format("The atom type %s was not found for biotype %s.",
353 bioType.atomType, bioType));
354 }
355 }
356 return null;
357 }
358
359
360
361
362
363
364
365
366 public static List<Atom> findAtomsOfElement(Residue residue, int element) {
367 return residue.getAtomList().stream()
368 .filter((Atom a) -> a.getAtomType().atomicNumber == element)
369 .collect(Collectors.toList());
370 }
371
372
373
374
375
376
377
378
379 public static List<Atom> findBondedAtoms(Atom atom, int element) {
380 return findBondedAtoms(atom, null, element);
381 }
382
383
384
385
386
387
388
389
390
391
392 public static List<Atom> findBondedAtoms(Atom atom, Atom toExclude, int element) {
393 return atom.getBonds().stream()
394 .map((Bond b) -> b.get1_2(atom))
395 .filter((Atom a) -> a != toExclude)
396 .filter((Atom a) -> a.getAtomType().atomicNumber == element)
397 .collect(Collectors.toList());
398 }
399
400
401
402
403
404
405
406 public static Atom findNitrogenAtom(Residue residue) {
407 assert residue.getResidueType() == Residue.ResidueType.AA;
408
409
410 List<Atom> nitrogenCandidates = new ArrayList<>(2);
411
412 switch (residue.getAminoAcid3()) {
413 case LYS, LYD -> {
414
415 List<Atom> nitrogenList = findAtomsOfElement(residue, 7);
416 for (Atom nitrogen : nitrogenList) {
417 List<Atom> carbons = findBondedAtoms(nitrogen, 6);
418 if (carbons.size() == 2) {
419 nitrogenCandidates.add(nitrogen);
420 } else if (findBondedAtoms(carbons.get(0), 1).size() < 2) {
421 nitrogenCandidates.add(nitrogen);
422 }
423 }
424 if (nitrogenCandidates.isEmpty()) {
425 throw new IllegalArgumentException(
426 format(" Could not identify N atom of residue %s!", residue));
427 }
428 }
429
430 case ARG, HIS, HIE, HID -> {
431
432
433
434
435 List<Atom> nitrogenList = findAtomsOfElement(residue, 7);
436 Atom commonC = findAtomsOfElement(residue, 6).stream()
437 .filter((Atom carbon) -> findBondedAtoms(carbon, 7).size() >= 2)
438 .findAny().get();
439 nitrogenCandidates = nitrogenList.stream()
440 .filter((Atom nitr) -> !atomAttachedToAtom(nitr, commonC))
441 .collect(Collectors.toList());
442 }
443 case ASN, GLN -> {
444
445
446
447
448 List<Atom> nitrogenList = findAtomsOfElement(residue, 7);
449 for (Atom nitrogen : nitrogenList) {
450 List<Atom> bondedCarbs = findBondedAtoms(nitrogen, 6);
451 for (Atom carbon : bondedCarbs) {
452 if (!hasAttachedAtom(carbon, 8)) {
453 nitrogenCandidates.add(nitrogen);
454 }
455 }
456 }
457 if (nitrogenCandidates.isEmpty()) {
458 throw new IllegalArgumentException(
459 format(" Could not identify N atom of residue %s!", residue));
460 }
461 }
462 case TRP -> {
463
464
465
466
467 List<Atom> nitrogenList = findAtomsOfElement(residue, 7);
468 for (Atom nitrogen : nitrogenList) {
469 List<Atom> bondedCarbs = findBondedAtoms(nitrogen, 6);
470 if (bondedCarbs.size() == 1) {
471 nitrogenCandidates.add(nitrogen);
472 }
473 for (Atom carbon : bondedCarbs) {
474 if (hasAttachedAtom(carbon, 8)) {
475 nitrogenCandidates.add(nitrogen);
476 }
477 }
478 }
479 if (nitrogenCandidates.isEmpty()) {
480 throw new IllegalArgumentException(
481 format(" Could not identify N atom of residue %s!", residue));
482 }
483 }
484 case ACE -> {
485 return null;
486 }
487
488 default -> nitrogenCandidates = findAtomsOfElement(residue, 7);
489 }
490
491 switch (nitrogenCandidates.size()) {
492 case 0 -> {
493 logger.warning(
494 " Did not find any atoms that might be the amide nitrogen for residue " + residue);
495 return null;
496 }
497 case 1 -> {
498 return nitrogenCandidates.get(0);
499 }
500 case 2 -> {
501 logger.fine(format(
502 " Probable NME C-terminal cap attached to residue %s, some atom names may be duplicated!",
503 residue));
504 Atom N = null;
505 for (Atom nitro : nitrogenCandidates) {
506 nitro.setName("N");
507 Optional<Atom> capMethyl = findBondedAtoms(nitro, 6).stream()
508 .filter((Atom carb) -> findBondedAtoms(carb, 1).size() == 3)
509 .findAny();
510 if (capMethyl.isPresent()) {
511 findBondedAtoms(nitro, 1).get(0).setName("H");
512 Atom theCap = capMethyl.get();
513 theCap.setName("CH3");
514 List<Atom> capHydrogenList = findBondedAtoms(theCap, 1);
515 for (int i = 0; i < 3; i++) {
516 capHydrogenList.get(i).setName(format("H%d", i + 1));
517 }
518 } else {
519 N = nitro;
520 }
521 }
522 return N;
523 }
524
525
526 default -> {
527 if(logger.isLoggable(Level.FINE)){
528 logger.warning(format(" Nitrogen could not be mapped for amide nitrogen of residue %s ", residue));
529 }
530 return null;
531 }
532 }
533 }
534
535
536
537
538
539
540
541
542 public static Optional<Atom> findNucleotideO4s(Residue residue) {
543 assert residue.getResidueType() == Residue.ResidueType.NA;
544 return findAtomsOfElement(residue, 8).stream()
545 .filter(o -> findBondedAtoms(o, 6).size() == 2)
546 .findAny();
547 }
548
549
550
551
552
553
554
555
556 public static Atom getAlphaCarbon(Residue residue, Atom N) {
557 List<Atom> resAtoms = residue.getAtomList();
558 List<Atom> caCandidates = findBondedAtoms(N, 6).stream().filter(resAtoms::contains).toList();
559
560 if (residue.getAminoAcid3() == AminoAcid3.PRO) {
561 Atom CA = null;
562 Atom CD = null;
563 Atom aceC = null;
564 for (Atom caCand : caCandidates) {
565 if (hasAttachedAtom(caCand, 8)) {
566 aceC = caCand;
567 } else {
568 List<Atom> attachedH = findBondedAtoms(caCand, 1);
569 if (attachedH.size() == 1) {
570 CA = caCand;
571 } else if (attachedH.size() == 2) {
572 CD = caCand;
573 } else {
574 throw new IllegalArgumentException(format(" Error in parsing proline %s", residue));
575 }
576 }
577 }
578 assert CA != null && CD != null;
579 if (aceC != null) {
580 nameAcetylCap(residue, aceC);
581 }
582 return CA;
583 }
584 if (caCandidates.size() == 1) {
585 return caCandidates.get(0);
586 } else {
587 Atom CA = null;
588 Atom aceC = null;
589 for (Atom caCand : caCandidates) {
590 if (hasAttachedAtom(caCand, 8)) {
591 aceC = caCand;
592 } else {
593 CA = caCand;
594 }
595 }
596 nameAcetylCap(residue, aceC);
597 return CA;
598 }
599 }
600
601
602
603
604
605
606
607
608 public static boolean hasAttachedAtom(Atom atom, int element) {
609 return atom.getBonds().stream()
610 .map((Bond b) -> b.get1_2(atom))
611 .anyMatch((Atom a) -> a.getAtomType().atomicNumber == element);
612 }
613
614
615
616
617
618
619
620
621
622
623
624
625
626 public static void intxyz(
627 Atom atom, Atom ia, double bond, Atom ib, double angle1, Atom ic, double angle2, int chiral) {
628 double[] xa = new double[3];
629 xa = (ia == null) ? null : ia.getXYZ(xa);
630 double[] xb = new double[3];
631 xb = (ib == null) ? null : ib.getXYZ(xb);
632 double[] xc = new double[3];
633 xc = (ic == null) ? null : ic.getXYZ(xc);
634 atom.moveTo(determineIntxyz(xa, bond, xb, angle1, xc, angle2, chiral));
635 }
636
637
638
639
640
641
642 public static void numberAtoms(MolecularAssembly molecularAssembly) {
643 int index = 1;
644 for (Atom a : molecularAssembly.getAtomArray()) {
645 a.setXyzIndex(index++);
646 }
647 index--;
648 if (logger.isLoggable(Level.INFO)) {
649 logger.info(format(" Total number of atoms: %d\n", index));
650 }
651
652 Polymer[] polymers = molecularAssembly.getChains();
653 if (polymers != null) {
654 for (Polymer p : polymers) {
655 List<Residue> residues = p.getResidues();
656 for (Residue r : residues) {
657 r.reOrderAtoms();
658 }
659 }
660 }
661 List<MSNode> molecules = molecularAssembly.getMolecules();
662 for (MSNode n : molecules) {
663 MSGroup m = (MSGroup) n;
664 m.reOrderAtoms();
665 }
666 List<MSNode> water = molecularAssembly.getWater();
667 for (MSNode n : water) {
668 MSGroup m = (MSGroup) n;
669 m.reOrderAtoms();
670 }
671 List<MSNode> ions = molecularAssembly.getIons();
672 for (MSNode n : ions) {
673 MSGroup m = (MSGroup) n;
674 m.reOrderAtoms();
675 }
676 }
677
678
679
680
681
682
683
684
685 public static Atom[] sortAtomsByDistance(Atom reference, List<Atom> toCompare) {
686 Atom[] theAtoms = toCompare.toArray(new Atom[0]);
687 sortAtomsByDistance(reference, theAtoms);
688 return theAtoms;
689 }
690
691
692
693
694
695
696
697 public static void sortAtomsByDistance(Atom reference, Atom[] toCompare) {
698 final double[] refXYZ = reference.getXYZ(new double[3]);
699 Arrays.sort(
700 toCompare,
701 Comparator.comparingDouble(
702 a -> {
703 double[] atXYZ = a.getXYZ(new double[3]);
704 return DoubleMath.dist2(refXYZ, atXYZ);
705 }));
706 }
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723 private static Atom buildHeavyAtom(MSGroup residue, String atomName, Atom ia, double bond,
724 Atom ib, double angle1, Atom ic, double angle2, int chiral, AtomType atomType) {
725 Atom atom = (Atom) residue.getAtomNode(atomName);
726 if (atomType == null) {
727 return null;
728 }
729 if (atom == null) {
730 String resName = ia.getResidueName();
731 int resSeq = ia.getResidueNumber();
732 Character chainID = ia.getChainID();
733 Character altLoc = ia.getAltLoc();
734 String segID = ia.getSegID();
735 double occupancy = ia.getOccupancy();
736 double tempFactor = ia.getTempFactor();
737 atom =
738 new Atom(
739 0,
740 atomName,
741 altLoc,
742 new double[3],
743 resName,
744 resSeq,
745 chainID,
746 occupancy,
747 tempFactor,
748 segID,
749 true);
750 residue.addMSNode(atom);
751 intxyz(atom, ia, bond, ib, angle1, ic, angle2, chiral);
752 }
753 atom.setAtomType(atomType);
754 return atom;
755 }
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774 private static Atom buildHeavyAtom(MSGroup residue, String atomName, Atom ia, double bond,
775 Atom ib, double angle1, Atom ic, double angle2, int chiral, AtomType atomType,
776 ForceField forceField, List<Bond> bondList) {
777 Atom atom =
778 buildHeavyAtom(residue, atomName, ia, bond, ib, angle1, ic, angle2, chiral, atomType);
779 buildBond(ia, atom, forceField, bondList);
780 return atom;
781 }
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804 public static double[] determineIntxyz(double[] ia, double bond, double[] ib, double angle1,
805 double[] ic, double angle2, int chiral) {
806 if (ia != null && !Double.isFinite(bond)) {
807 throw new IllegalArgumentException(
808 String.format(" Passed bond length is non-finite %f", bond));
809 } else if (ib != null && !Double.isFinite(angle1)) {
810 throw new IllegalArgumentException(String.format(" Passed angle is non-finite %f", angle1));
811 } else if (ic != null && !Double.isFinite(angle2)) {
812 throw new IllegalArgumentException(
813 String.format(" Passed dihedral/improper is non-finite %f", angle2));
814 }
815
816 if (chiral == 3) {
817 double[] negChiral = determineIntxyz(ia, bond, ib, angle1, ic, angle2, -1);
818 double[] posChiral = determineIntxyz(ia, bond, ib, angle1, ic, angle2, 1);
819 double[] displacement = new double[3];
820 double dispMag = 0;
821
822 for (int i = 0; i < 3; i++) {
823
824 displacement[i] = 0.5 * (posChiral[i] + negChiral[i]);
825
826 displacement[i] -= ia[i];
827
828 dispMag += (displacement[i] * displacement[i]);
829 }
830
831 dispMag = sqrt(dispMag);
832 double extend = bond / dispMag;
833 assert extend > 0.999;
834
835 double[] outXYZ = new double[3];
836 for (int i = 0; i < 3; i++) {
837 displacement[i] *= extend;
838 outXYZ[i] = displacement[i] + ia[i];
839 }
840 return outXYZ;
841 }
842
843 angle1 = toRadians(angle1);
844 angle2 = toRadians(angle2);
845 double zcos0 = cos(angle1);
846 double zcos1 = cos(angle2);
847 double zsin0 = sin(angle1);
848 double zsin1 = sin(angle2);
849 double[] ret = new double[3];
850 double[] x = new double[3];
851
852
853 if (ia == null) {
854 x[0] = x[1] = x[2] = 0.0;
855 } else if (ib == null) {
856 double[] xa = new double[3];
857 arraycopy(ia, 0, xa, 0, ia.length);
858
859 x[0] = xa[0];
860 x[1] = xa[1];
861 x[2] = xa[2] + bond;
862 } else if (ic == null) {
863 double[] xa = new double[3];
864 double[] xb = new double[3];
865 double[] xab = new double[3];
866 for (int i = 0; i < ia.length; i++) {
867 xa[i] = ia[i];
868 xb[i] = ib[i];
869 }
870
871 sub(xa, xb, xab);
872 double rab = length(xab);
873 normalize(xab, xab);
874 double cosb = xab[2];
875 double sinb = sqrt(xab[0] * xab[0] + xab[1] * xab[1]);
876 double cosg, sing;
877 if (sinb == 0.0d) {
878 cosg = 1.0d;
879 sing = 0.0d;
880 } else {
881 cosg = xab[1] / sinb;
882 sing = xab[0] / sinb;
883 }
884 double xtmp = bond * zsin0;
885 double ztmp = rab - bond * zcos0;
886 x[0] = xb[0] + xtmp * cosg + ztmp * sing * sinb;
887 x[1] = xb[1] - xtmp * sing + ztmp * cosg * sinb;
888 x[2] = xb[2] + ztmp * cosb;
889 } else if (chiral == 0) {
890 double[] xa = new double[3];
891 double[] xb = new double[3];
892 double[] xc = new double[3];
893 double[] xab = new double[3];
894 double[] xbc = new double[3];
895 double[] xt = new double[3];
896 double[] xu = new double[3];
897 for (int i = 0; i < ia.length; i++) {
898 xa[i] = ia[i];
899 xb[i] = ib[i];
900 xc[i] = ic[i];
901 }
902
903 sub(xa, xb, xab);
904 normalize(xab, xab);
905 sub(xb, xc, xbc);
906 normalize(xbc, xbc);
907 xt[0] = xab[2] * xbc[1] - xab[1] * xbc[2];
908 xt[1] = xab[0] * xbc[2] - xab[2] * xbc[0];
909 xt[2] = xab[1] * xbc[0] - xab[0] * xbc[1];
910 double cosine = xab[0] * xbc[0] + xab[1] * xbc[1] + xab[2] * xbc[2];
911 double sine = sqrt(max(1.0d - cosine * cosine, eps));
912 if (abs(cosine) >= 1.0d) {
913 logger.warning("Undefined Dihedral");
914 }
915 scale(xt, 1.0d / sine, xt);
916 xu[0] = xt[1] * xab[2] - xt[2] * xab[1];
917 xu[1] = xt[2] * xab[0] - xt[0] * xab[2];
918 xu[2] = xt[0] * xab[1] - xt[1] * xab[0];
919 x[0] = xa[0] + bond * (xu[0] * zsin0 * zcos1 + xt[0] * zsin0 * zsin1 - xab[0] * zcos0);
920 x[1] = xa[1] + bond * (xu[1] * zsin0 * zcos1 + xt[1] * zsin0 * zsin1 - xab[1] * zcos0);
921 x[2] = xa[2] + bond * (xu[2] * zsin0 * zcos1 + xt[2] * zsin0 * zsin1 - xab[2] * zcos0);
922 } else if (abs(chiral) == 1) {
923 double[] xa = new double[3];
924 double[] xb = new double[3];
925 double[] xc = new double[3];
926 double[] xba = new double[3];
927 double[] xac = new double[3];
928 double[] xt = new double[3];
929 for (int i = 0; i < ia.length; i++) {
930 xa[i] = ia[i];
931 xb[i] = ib[i];
932 xc[i] = ic[i];
933 }
934 sub(xb, xa, xba);
935 normalize(xba, xba);
936 sub(xa, xc, xac);
937 normalize(xac, xac);
938 xt[0] = xba[2] * xac[1] - xba[1] * xac[2];
939 xt[1] = xba[0] * xac[2] - xba[2] * xac[0];
940 xt[2] = xba[1] * xac[0] - xba[0] * xac[1];
941 double cosine = xba[0] * xac[0] + xba[1] * xac[1] + xba[2] * xac[2];
942 double sine2 = max(1.0d - cosine * cosine, eps);
943 if (abs(cosine) >= 1.0d) {
944 logger.warning("Defining Atom Colinear");
945 }
946 double a = (-zcos1 - cosine * zcos0) / sine2;
947 double b = (zcos0 + cosine * zcos1) / sine2;
948 double c = (1.0d + a * zcos1 - b * zcos0) / sine2;
949 if (c > eps) {
950 c = chiral * sqrt(c);
951 } else if (c < -eps) {
952 c =
953 sqrt(
954 (a * xac[0] + b * xba[0]) * (a * xac[0] + b * xba[0])
955 + (a * xac[1] + b * xba[1]) * (a * xac[1] + b * xba[1])
956 + (a * xac[2] + b * xba[2]) * (a * xac[2] + b * xba[2]));
957 a /= c;
958 b /= c;
959 c = 0.0d;
960 } else {
961 c = 0.0d;
962 }
963 x[0] = xa[0] + bond * (a * xac[0] + b * xba[0] + c * xt[0]);
964 x[1] = xa[1] + bond * (a * xac[1] + b * xba[1] + c * xt[1]);
965 x[2] = xa[2] + bond * (a * xac[2] + b * xba[2] + c * xt[2]);
966 }
967 arraycopy(x, 0, ret, 0, ret.length);
968 return ret;
969 }
970
971
972 public static class MissingAtomTypeException extends Exception {
973
974 @Serial
975 private static final long serialVersionUID = 1L;
976
977 public final Residue residue;
978 public final Atom atom;
979
980 public MissingAtomTypeException(Residue residue, Atom atom) {
981 this.residue = residue;
982 this.atom = atom;
983 }
984
985 @Override
986 public String toString() {
987 StringBuilder sb = new StringBuilder();
988 sb.append(format(" Atom %s", atom));
989 if (residue != null) {
990 sb.append(format("\n of residue %c-%s", residue.getChainID(), residue));
991 }
992 sb.append("\n could not be assigned an atom type.\n");
993 return sb.toString();
994 }
995 }
996
997
998 public static class MissingHeavyAtomException extends Exception {
999
1000 @Serial
1001 private static final long serialVersionUID = 1L;
1002
1003 public final String atomName;
1004 public final AtomType atomType;
1005 final Atom bondedTo;
1006
1007 public MissingHeavyAtomException(String atomName, AtomType atomType, Atom bondedTo) {
1008 this.atomName = atomName;
1009 this.atomType = atomType;
1010 this.bondedTo = bondedTo;
1011 }
1012
1013 @Override
1014 public String toString() {
1015 StringBuilder sb = new StringBuilder();
1016 if (atomType != null) {
1017 sb.append(format("\n An atom of type\n %s\n", atomType));
1018 } else {
1019 sb.append(format("\n Atom %s", atomName));
1020 }
1021 sb.append(" was not found");
1022 if (bondedTo != null) {
1023 sb.append(format(" bonded to atom %s ", bondedTo));
1024 }
1025 sb.append(".\n");
1026 return sb.toString();
1027 }
1028 }
1029 }