Zaixi commited on
Commit
f572e51
·
1 Parent(s): 20f7859
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. pdbfixer/__init__.py +3 -0
  2. pdbfixer/__pycache__/__init__.cpython-310.pyc +0 -0
  3. pdbfixer/__pycache__/__init__.cpython-38.pyc +0 -0
  4. pdbfixer/__pycache__/pdbfixer.cpython-310.pyc +0 -0
  5. pdbfixer/__pycache__/pdbfixer.cpython-38.pyc +0 -0
  6. pdbfixer/__pycache__/ui.cpython-38.pyc +0 -0
  7. pdbfixer/__pycache__/uiserver.cpython-38.pyc +0 -0
  8. pdbfixer/html/addHeavyAtoms.html +15 -0
  9. pdbfixer/html/addHydrogens.html +197 -0
  10. pdbfixer/html/addResidues.html +16 -0
  11. pdbfixer/html/convertResidues.html +16 -0
  12. pdbfixer/html/error.html +9 -0
  13. pdbfixer/html/header.html +122 -0
  14. pdbfixer/html/quit.html +9 -0
  15. pdbfixer/html/removeChains.html +24 -0
  16. pdbfixer/html/saveFile.html +12 -0
  17. pdbfixer/html/start.html +39 -0
  18. pdbfixer/images/background.jpg +0 -0
  19. pdbfixer/images/logo.png +0 -0
  20. pdbfixer/images/logo.svg +388 -0
  21. pdbfixer/images/logo_small.png +0 -0
  22. pdbfixer/pdbfixer.py +1299 -0
  23. pdbfixer/setup.py +12 -0
  24. pdbfixer/soft.xml +0 -0
  25. pdbfixer/templates/A.pdb +24 -0
  26. pdbfixer/templates/ACE.pdb +5 -0
  27. pdbfixer/templates/ALA.pdb +7 -0
  28. pdbfixer/templates/ARG.pdb +13 -0
  29. pdbfixer/templates/ASN.pdb +10 -0
  30. pdbfixer/templates/ASP.pdb +10 -0
  31. pdbfixer/templates/C.pdb +22 -0
  32. pdbfixer/templates/CYS.pdb +8 -0
  33. pdbfixer/templates/DA.pdb +23 -0
  34. pdbfixer/templates/DC.pdb +21 -0
  35. pdbfixer/templates/DG.pdb +24 -0
  36. pdbfixer/templates/DT.pdb +22 -0
  37. pdbfixer/templates/G.pdb +25 -0
  38. pdbfixer/templates/GLN.pdb +11 -0
  39. pdbfixer/templates/GLU.pdb +11 -0
  40. pdbfixer/templates/GLY.pdb +6 -0
  41. pdbfixer/templates/HIS.pdb +12 -0
  42. pdbfixer/templates/ILE.pdb +10 -0
  43. pdbfixer/templates/LEU.pdb +10 -0
  44. pdbfixer/templates/LYS.pdb +11 -0
  45. pdbfixer/templates/MET.pdb +10 -0
  46. pdbfixer/templates/NME.pdb +4 -0
  47. pdbfixer/templates/PHE.pdb +13 -0
  48. pdbfixer/templates/PRO.pdb +9 -0
  49. pdbfixer/templates/SER.pdb +8 -0
  50. pdbfixer/templates/THR.pdb +9 -0
pdbfixer/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from __future__ import absolute_import
2
+ from .pdbfixer import PDBFixer
3
+
pdbfixer/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (224 Bytes). View file
 
pdbfixer/__pycache__/__init__.cpython-38.pyc ADDED
Binary file (235 Bytes). View file
 
pdbfixer/__pycache__/pdbfixer.cpython-310.pyc ADDED
Binary file (45 kB). View file
 
pdbfixer/__pycache__/pdbfixer.cpython-38.pyc ADDED
Binary file (44.2 kB). View file
 
pdbfixer/__pycache__/ui.cpython-38.pyc ADDED
Binary file (11.6 kB). View file
 
pdbfixer/__pycache__/uiserver.cpython-38.pyc ADDED
Binary file (3.16 kB). View file
 
pdbfixer/html/addHeavyAtoms.html ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The following residues are missing heavy atoms, which will be added.
2
+ <p>
3
+ <form id="mainform" method="post" action="/">
4
+ <table border="1">
5
+ <tr><th>Chain</th><th>Residue</th><th>Missing Atoms</th></tr>
6
+ %s
7
+ </table>
8
+ <p>
9
+ <input type="button" value="Continue" onclick="submitWithSpinner()"/>
10
+ </form>
11
+ <script>
12
+ setCurrentStep(5)
13
+ </script>
14
+ </body>
15
+ <html>
pdbfixer/html/addHydrogens.html ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script>
2
+ function validateForm() {
3
+ if (document.getElementById("addHydrogensCheckbox").checked) {
4
+ var ph = document.getElementById("phfield").value
5
+ if (!(ph > 0 && ph < 14)) {
6
+ alert("pH must be a number between 0 and 14.")
7
+ return false
8
+ }
9
+ }
10
+ if (document.getElementById("addWaterCheckbox").checked) {
11
+ var custombox = document.getElementById("customBoxRadio").checked
12
+ var xsize = document.getElementById("boxxfield").value
13
+ var ysize = document.getElementById("boxyfield").value
14
+ var zsize = document.getElementById("boxzfield").value
15
+ if (custombox && !(xsize > 0 && ysize > 0 && zsize > 0)) {
16
+ alert("Box dimensions must be positive numbers.")
17
+ return false
18
+ }
19
+ var geombox = document.getElementById("geometricBoxRadio").checked
20
+ var padsize = document.getElementById("geomPadding").value
21
+ if (geombox && padsize < 0) {
22
+ alert("Box padding cannot be negative.")
23
+ return false
24
+ }
25
+ var strength = document.getElementById("ionicstrengthfield").value
26
+ if (!(strength >= 0)) {
27
+ alert("Ionic strength must be a nonnegative number.")
28
+ return false
29
+ }
30
+ }
31
+ if (document.getElementById("addMembraneCheckbox").checked) {
32
+ var padsize = document.getElementById("membranePadding").value
33
+ if (padsize < 0) {
34
+ alert("Box padding cannot be negative.")
35
+ return false
36
+ }
37
+ var strength = document.getElementById("ionicstrengthfield").value
38
+ if (!(strength >= 0)) {
39
+ alert("Ionic strength must be a nonnegative number.")
40
+ return false
41
+ }
42
+ }
43
+ return true
44
+ }
45
+ function applyPaddingToWaterBox(padding) {
46
+ padding = parseFloat(padding);
47
+ if(!padding || padding<0) padding = 0;
48
+ if (document.getElementById('cubicPaddingCheckBox').checked){
49
+ var maxAxis = Math.max.apply(Math, molxyz);
50
+ for (var i = pbcxyz.length; i--;) {
51
+ pbcxyz[i].value = Math.ceil((padding+maxAxis)*1000) / 1000;
52
+ }
53
+ }
54
+ else {
55
+ for (var i = pbcxyz.length; i--;) {
56
+ pbcxyz[i].value = Math.ceil((padding+molxyz[i])*1000) / 1000;
57
+ }
58
+ }
59
+ }
60
+ function waterBoxChanged() {
61
+ checked = document.getElementById('addWaterCheckbox').checked;
62
+ document.getElementById('addMembraneCheckbox').checked = false;
63
+ enableControls(document.getElementById('waterInputs'), checked);
64
+ enableControls(document.getElementById('ionInputs'), checked);
65
+ enableControls(document.getElementById('membraneInputs'), false);
66
+ }
67
+ function membraneBoxChanged() {
68
+ checked = document.getElementById('addMembraneCheckbox').checked;
69
+ document.getElementById('addWaterCheckbox').checked = false;
70
+ enableControls(document.getElementById('membraneInputs'), checked);
71
+ enableControls(document.getElementById('ionInputs'), checked);
72
+ enableControls(document.getElementById('waterInputs'), false);
73
+ }
74
+ </script>
75
+ <style>
76
+ a.tooltip {
77
+ display: inline;
78
+ position: relative;
79
+ color: #000;
80
+ border-bottom: 1px dotted #000;
81
+ text-decoration: none;
82
+ }
83
+ a.tooltip:hover:after {
84
+ content: attr(rel);
85
+ text-align: center;
86
+ font-size: 80%%;
87
+ background: #000;
88
+ color: #fff;
89
+ width: 250px;
90
+ bottom: 30px;
91
+ left: -125px;
92
+ padding: 10px 15px;
93
+ position: absolute;
94
+ z-index: 100;
95
+ /* Effects */
96
+ background: rgba(0,0,0,0.75);
97
+ border-radius: 5px;
98
+ box-shadow: 0 0 25px #333;
99
+ }
100
+ /* Down arrow */
101
+ a.tooltip:hover:before {
102
+ border: solid;
103
+ border-color: #333 transparent;
104
+ border-width: 10px 10px 0 10px;
105
+ bottom: 20px;
106
+ left: -6px;
107
+ content: "";
108
+ position: absolute;
109
+ z-index: 99;
110
+ /* Effects */
111
+ border-color: rgba(0,0,0,0.75) transparent;
112
+ }
113
+ </style>
114
+ <form id="mainform" method="post" action="/">
115
+ <h1>Add Missing Hydrogens</h1>
116
+ Add missing hydrogen atoms?
117
+ <p>
118
+ <input type="checkbox" id="addHydrogensCheckbox" name="addhydrogens" onchange="document.getElementById('phfield').disabled=!document.getElementById('addHydrogensCheckbox').checked" checked> Add hydrogens appropriate for pH <input type="text" id="phfield" name="ph" value="7.0" size="5">
119
+ <h1>Add Water/Membrane</h1>
120
+ Add a water box or membrane surrounding the model?
121
+ <p>
122
+ <input type="checkbox" id="addWaterCheckbox" name="addwater" onchange="waterBoxChanged()"> Add water box
123
+ <div id="waterInputs" style="margin-left:50px">
124
+ <table style="text-align:right">
125
+ <tr>
126
+ <td style="text-align:left"><input id="customBoxRadio" type="radio" name="boxType" value="custom" checked>Custom box dimensions (in nm):</td>
127
+ <div id="customBoxDimensions">
128
+ <td><input type="text" id="boxxfield" name="boxx" size="8"></td>
129
+ <td><input type="text" id="boxyfield" name="boxy" size="8"></td>
130
+ <td><input type="text" id="boxzfield" name="boxz" size="8"></td>
131
+ </div>
132
+ </tr>
133
+
134
+ <tr>
135
+ <td style="text-align:left"><input id="geometricBoxRadio" type="radio" name="boxType" value="geometry">Geometry with padding (in nm):</td>
136
+ <div id="geometricBoxDimensions">
137
+ <td><input type="text" id="geomPadding" name="geomPadding" size="8">
138
+ </td>
139
+ <td colspan="2">
140
+ <select id="geometryDropdown" name="geometryDropdown">
141
+ <option value="cube" selected="selected">Cube</option>
142
+ <option value="truncatedOctahedron">Truncated octahedron</option>
143
+ <option value="rhombicDodecahedron">Rhombic dodecahedron</option>
144
+ </select>
145
+ <a href="#" rel="The specified padding will be applied on the largest molecular axis, while maintaining selected geometry." class="tooltip">?</a>
146
+ </div>
147
+ </td>
148
+ </tr>
149
+ %s
150
+ </table>
151
+ </div>
152
+ <p>
153
+ <input type="checkbox" id="addMembraneCheckbox" name="addmembrane" onchange="membraneBoxChanged()"> Add membrane and water
154
+ <div id="membraneInputs" style="margin-left:50px">
155
+ The membrane will lie in the XY plane and be centered at Z=0. Make sure the protein is properly oriented and positioned.
156
+ When possible, it is easiest to use a PDB file from <a href="http://opm.phar.umich.edu/">OPM</a> since they are already properly positioned.
157
+ <p>
158
+ Lipid type:
159
+ <select id="lipidType" name="lipidType">
160
+ <option value="POPC" selected="selected">POPC</option>
161
+ <option value="POPE">POPE</option>
162
+ <option value="DLPC">DLPC</option>
163
+ <option value="DLPE">DLPE</option>
164
+ <option value="DMPC">DMPC</option>
165
+ <option value="DOPC">DOPC</option>
166
+ <option value="DPPC">DPPC</option>
167
+ </select>
168
+ <p>
169
+ Minimum padding around protein: <input type="text" id="membranePadding" name="membranePadding" value="1.0" size="5"> (nm)
170
+ </div>
171
+ <div id="ionInputs">
172
+ <p>
173
+ Ions will be added to neutralize the model. You can optionally add more ions based on a desired bulk ionic strength.
174
+ <p>
175
+ <table>
176
+ <tr><td style="text-align:right">Ionic strength (molar):</td><td><input type="text" id="ionicstrengthfield" name="ionicstrength" size="8" value="0.0"></td></tr>
177
+ <tr><td style="text-align:right">Positive ion:</td><td><select name="positiveion"><option value="Cs">Cs+</option><option value="K">K+</option><option value="Li">Li+</option><option value="Na" selected>Na+</option><option value="Rb">Rb+</option></select></td></tr>
178
+ <tr><td style="text-align:right">Negative ion:</td><td><select name="negativeion"><option value="Cl" selected>Cl-</option><option value="Br">Br-</option><option value="F">F-</option><option value="I">I-</option></select></td></tr>
179
+ </table>
180
+ </div>
181
+ <p>
182
+ <input type="button" value="Continue" onclick="if (validateForm()) submitWithSpinner()"/>
183
+ </form>
184
+ <script>
185
+ setCurrentStep(6);
186
+ enableControls(document.getElementById('waterInputs'), false);
187
+ enableControls(document.getElementById('membraneInputs'), false);
188
+ enableControls(document.getElementById('ionInputs'), false);
189
+ var molxyz = [].slice.call(document.getElementById('boxContainingAllAtoms').getElementsByTagName('td')).splice(1,3).map(function(td){ return parseFloat(td.textContent)});
190
+ var hiddenInput = document.createElement("input");
191
+ hiddenInput.setAttribute("type", "hidden");
192
+ hiddenInput.setAttribute("name", "maxMolecularAxis");
193
+ hiddenInput.setAttribute("value", Math.max.apply(Math, molxyz));
194
+ document.getElementById("mainform").appendChild(hiddenInput);
195
+ </script>
196
+ </body>
197
+ <html>
pdbfixer/html/addResidues.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The sequence records in this PDB file include residues that are missing from the atom data section. Do you want to add the missing residues?
2
+ <p>
3
+ <form id="mainform" method="post" action="/">
4
+ <table border="1" id="table">
5
+ <tr><th>Chain</th><th>Residue Positions</th><th>Sequence</th><th>Add?</th></tr>
6
+ %s
7
+ </table>
8
+ <input type="button" value="Select All" onclick="setCheckboxes(true);"/><input type="button" value="Select None" onclick="setCheckboxes(false);"/>
9
+ <p>
10
+ <input type="button" value="Continue" onclick="submitWithSpinner()"/>
11
+ </form>
12
+ <script>
13
+ setCurrentStep(3)
14
+ </script>
15
+ </body>
16
+ <html>
pdbfixer/html/convertResidues.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This PDB file contains non-standard residues. Do you want to convert them to standard residues?
2
+ <p>
3
+ <form id="mainform" method="post" action="/">
4
+ <table border="1" id="table">
5
+ <tr><th>Chain</th><th>Residue</th><th>Convert To</th><th>Convert?</th></tr>
6
+ %s
7
+ </table>
8
+ <input type="button" value="Select All" onclick="setCheckboxes(true);"/><input type="button" value="Select None" onclick="setCheckboxes(false);"/>
9
+ <p>
10
+ <input type="button" value="Continue" onclick="submitWithSpinner()"/>
11
+ </form>
12
+ <script>
13
+ setCurrentStep(4)
14
+ </script>
15
+ </body>
16
+ <html>
pdbfixer/html/error.html ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ <form method="post" action="/controls">
3
+ <input type="submit" name="newfile" value="Start Over"/>
4
+ </form>
5
+ <script>
6
+ setCurrentStep(1)
7
+ </script>
8
+ </body>
9
+ <html>
pdbfixer/html/header.html ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html>
2
+ <head>
3
+ <title>PDBFixer</title>
4
+ <script>
5
+ function setCurrentStep(step) {
6
+ for (var i = 1; i < step; i++)
7
+ document.getElementById("step"+i).style.color="black"
8
+ document.getElementById("step"+step).style.color="red"
9
+ for (var i = step+1; i < 8; i++)
10
+ document.getElementById("step"+i).style.color="gray"
11
+ if (step == 1)
12
+ document.getElementById("newfile").disabled=true
13
+ }
14
+ function setCheckboxes(selected) {
15
+ checkboxes = document.getElementById("table").getElementsByTagName("input")
16
+ for (var i = 0; i < checkboxes.length; i++)
17
+ checkboxes[i].checked = selected
18
+ }
19
+ function enableControls(container, enabled) {
20
+ tags = ["input", "select"]
21
+ for (var i = 0; i < tags.length; i++) {
22
+ controls = container.getElementsByTagName(tags[i])
23
+ for (var j = 0; j < controls.length; j++)
24
+ controls[j].disabled = !enabled
25
+ }
26
+ }
27
+ function submitWithSpinner() {
28
+ animateSpinner.animateIndex = 0
29
+ setTimeout(function() {animateSpinner();document.getElementById('overlay').style.visibility='visible';}, 1500)
30
+ document.getElementById('mainform').submit()
31
+ }
32
+ function animateSpinner() {
33
+ for (var i = 0; i < 3; i++) {
34
+ var bar = document.getElementById("progress"+(i+1))
35
+ if (i == animateSpinner.animateIndex)
36
+ bar.style.backgroundColor = "black"
37
+ else
38
+ bar.style.backgroundColor = "lightgray"
39
+ }
40
+ animateSpinner.animateIndex = (animateSpinner.animateIndex+1)%3
41
+ setTimeout(function() {animateSpinner()}, 1000)
42
+ }
43
+ </script>
44
+ <style>
45
+ body {
46
+ margin-top: 0;
47
+ font-family:sans-serif;
48
+ background-color: rgb(217,217,217);
49
+ background-image: url("/image?name=background.jpg");
50
+ background-size: 80%;
51
+ background-repeat: repeat-y
52
+ }
53
+ input {
54
+ font-size: small
55
+ }
56
+ select {
57
+ font-size: small
58
+ }
59
+ table {
60
+ border-collapse: collapse;
61
+ margin: 7pt
62
+ }
63
+ th {
64
+ padding: 2pt;
65
+ background-color: lightgray
66
+ }
67
+ td {
68
+ padding: 2pt
69
+ }
70
+ #progressParent {
71
+ position: fixed;
72
+ top: 40%;
73
+ left: 50%;
74
+ margin-top: -75px;
75
+ margin-left: -125px
76
+ }
77
+ #overlay {
78
+ position: fixed;
79
+ width: 100%;
80
+ height: 100%;
81
+ top: 0px;
82
+ left: 0px;
83
+ background-color: rgba(255,255,255,0.75);
84
+ visibility:hidden;
85
+ }
86
+ #progressMessage {
87
+ width: 100%;
88
+ position: fixed;
89
+ top: 40%;
90
+ margin-top: 90px;
91
+ text-align: center
92
+ }
93
+ </style>
94
+ </head>
95
+ <body>
96
+ <div id="overlay">
97
+ <div id="progressParent">
98
+ <div id="progress1" style="width:50px;height:150px;position:absolute;left:0px"></div>
99
+ <div id="progress2" style="width:50px;height:150px;position:absolute;left:100px"></div>
100
+ <div id="progress3" style="width:50px;height:150px;position:absolute;left:200px"></div>
101
+ </div>
102
+ <div id="progressMessage">
103
+ <div style="font-size:xx-large">Processing</div>
104
+ This may take some time.
105
+ </div>
106
+ </div>
107
+ <span style="font-style:italic;font-size:large;float:right;position:relative;top:0;right:0;height:100%;padding:10px 10px">
108
+ <img src="/image?name=logo_small.png"/>
109
+ <p/>
110
+ <form method="post" action="/controls">
111
+ <input type="submit" name="newfile" value="New File" id="newfile"/>
112
+ <input type="submit" name="quit" value="Quit" style="float:right"/>
113
+ </form>
114
+ <p id="step1">Load File</p>
115
+ <p id="step2">Select Chains</p>
116
+ <p id="step3">Add Residues</p>
117
+ <p id="step4">Convert Residues</p>
118
+ <p id="step5">Add Heavy Atoms</p>
119
+ <p id="step6">Add Water/Hydrogens</p>
120
+ <p id="step7">Save File</p>
121
+ </span>
122
+ <p/>
pdbfixer/html/quit.html ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ <html>
2
+ <head>
3
+ <title>PDBFixer</title>
4
+ </head>
5
+ <body>
6
+ <h1 style="text-align:center">See You Again Soon!</h1>
7
+ PDBFixer has exited. You can close your browser window.
8
+ </body>
9
+ </html>
pdbfixer/html/removeChains.html ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This PDB file contains %d chains. Select which ones to include.
2
+ <p>
3
+ <form id="mainform" method="post" action="/">
4
+ <table border="1" id="table">
5
+ <tr><th>Chain</th><th># Residues</th><th>Content</th><th>Include?</th></tr>
6
+ %s
7
+ </table>
8
+ <input type="button" value="Select All" onclick="setCheckboxes(true);"/><input type="button" value="Select None" onclick="setCheckboxes(false);"/>
9
+ <p>
10
+ A heterogen is any residue other than a standard amino acid or nucleotide. Do you want to delete heterogens?
11
+ <p>
12
+ <select name="heterogens">
13
+ <option value="all" selected>Keep all heterogens</option>
14
+ <option value="water">Delete heterogens except water</option>
15
+ <option value="none">Delete all heterogens</option>
16
+ </select>
17
+ <p>
18
+ <input type="button" value="Continue" onclick="submitWithSpinner()"/>
19
+ </form>
20
+ <script>
21
+ setCurrentStep(2)
22
+ </script>
23
+ </body>
24
+ <html>
pdbfixer/html/saveFile.html ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The fixed PDB file is ready to save.
2
+ <p>
3
+ <form method="post" action="/">
4
+ <input type="submit" name="pdb" value="Save PDB File"/>
5
+ <input type="submit" name="pdbx" value="Save PDBx/mmCIF File"/>
6
+ <input type="submit" name="newfile" value="Process Another File"/>
7
+ </form>
8
+ <script>
9
+ setCurrentStep(7)
10
+ </script>
11
+ </body>
12
+ <html>
pdbfixer/html/start.html ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script>
2
+ function enableInputs() {
3
+ if (document.getElementById("localtype").checked) {
4
+ document.getElementById("pdbfile").disabled = false;
5
+ document.getElementById("pdbid").disabled = true;
6
+ document.getElementById("submitButton").disabled = (document.getElementById("pdbfile").value == "");
7
+ }
8
+ else {
9
+ document.getElementById("pdbfile").disabled = true;
10
+ document.getElementById("pdbid").disabled = false;
11
+ document.getElementById("submitButton").disabled = (document.getElementById("pdbid").value.length != 4);
12
+ }
13
+ }
14
+ </script>
15
+ <noscript>
16
+ <div style='color:red'>
17
+ <h1>Javascript Disabled</h1>
18
+ PDBFixer requires Javascript to operate. Please enable Javascript in your browser, then reload this page.
19
+ </div>
20
+ </noscript>
21
+ <h1>Welcome To PDBFixer!</h1>
22
+ Select a PDB file to load. It will be analyzed for problems.
23
+ <p>
24
+ <form id="mainform" method="post" action="/" enctype="multipart/form-data">
25
+ <input type="radio" name="type" id="localtype" value="local" onchange="enableInputs()" checked>Load a local file
26
+ <p style="margin-left:50px">
27
+ PDB File: <input type="file" id="pdbfile" name="pdbfile" onchange="enableInputs()"/>
28
+ </p>
29
+ <input type="radio" name="type" id="remotetype" value="remote" onchange="enableInputs()">Download a file from RCSB
30
+ <p style="margin-left:50px">
31
+ PDB Identifier: <input type="text" id="pdbid" name="pdbid" size="4" onchange="enableInputs()" onkeyup="enableInputs()" oninput="enableInputs()" disabled>
32
+ </p>
33
+ <input type="button" id="submitButton" value="Analyze File" onclick="submitWithSpinner()" disabled/>
34
+ </form>
35
+ <script>
36
+ setCurrentStep(1)
37
+ </script>
38
+ </body>
39
+ </html>
pdbfixer/images/background.jpg ADDED
pdbfixer/images/logo.png ADDED
pdbfixer/images/logo.svg ADDED
pdbfixer/images/logo_small.png ADDED
pdbfixer/pdbfixer.py ADDED
@@ -0,0 +1,1299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pdbfixer.py: Fixes problems in PDB files
3
+
4
+ This is part of the OpenMM molecular simulation toolkit originating from
5
+ Simbios, the NIH National Center for Physics-Based Simulation of
6
+ Biological Structures at Stanford, funded under the NIH Roadmap for
7
+ Medical Research, grant U54 GM072970. See https://simtk.org.
8
+
9
+ Portions copyright (c) 2013-2023 Stanford University and the Authors.
10
+ Authors: Peter Eastman
11
+ Contributors:
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a
14
+ copy of this software and associated documentation files (the "Software"),
15
+ to deal in the Software without restriction, including without limitation
16
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
17
+ and/or sell copies of the Software, and to permit persons to whom the
18
+ Software is furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in
21
+ all copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26
+ THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
27
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
28
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
29
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+ """
31
+ from __future__ import absolute_import
32
+ __author__ = "Peter Eastman"
33
+ __version__ = "1.7"
34
+
35
+ import openmm as mm
36
+ import openmm.app as app
37
+ import openmm.unit as unit
38
+ from openmm.app.internal.pdbstructure import PdbStructure
39
+ from openmm.app.internal.pdbx.reader.PdbxReader import PdbxReader
40
+ from openmm.app.element import hydrogen, oxygen
41
+ from openmm.app.forcefield import NonbondedGenerator
42
+
43
+ # Support Cythonized functions in OpenMM 7.3
44
+ # and also implementations in older versions.
45
+ try:
46
+ from openmm.app.internal import compiled
47
+ matchResidue = compiled.matchResidueToTemplate
48
+ except ImportError:
49
+ matchResidue = app.forcefield._matchResidue
50
+
51
+ import numpy as np
52
+ import numpy.linalg as lin
53
+ import sys
54
+ import os
55
+ import os.path
56
+ import math
57
+
58
+ from pkg_resources import resource_filename
59
+
60
+ if sys.version_info >= (3,0):
61
+ from urllib.request import urlopen
62
+ from io import StringIO
63
+ else:
64
+ from urllib2 import urlopen
65
+ from cStringIO import StringIO
66
+
67
+ substitutions = {
68
+ '2AS':'ASP', '3AH':'HIS', '5HP':'GLU', '5OW':'LYS', 'ACL':'ARG', 'AGM':'ARG', 'AIB':'ALA', 'ALM':'ALA', 'ALO':'THR', 'ALY':'LYS', 'ARM':'ARG',
69
+ 'ASA':'ASP', 'ASB':'ASP', 'ASK':'ASP', 'ASL':'ASP', 'ASQ':'ASP', 'AYA':'ALA', 'BCS':'CYS', 'BHD':'ASP', 'BMT':'THR', 'BNN':'ALA',
70
+ 'BUC':'CYS', 'BUG':'LEU', 'C5C':'CYS', 'C6C':'CYS', 'CAS':'CYS', 'CCS':'CYS', 'CEA':'CYS', 'CGU':'GLU', 'CHG':'ALA', 'CLE':'LEU', 'CME':'CYS',
71
+ 'CSD':'ALA', 'CSO':'CYS', 'CSP':'CYS', 'CSS':'CYS', 'CSW':'CYS', 'CSX':'CYS', 'CXM':'MET', 'CY1':'CYS', 'CY3':'CYS', 'CYG':'CYS',
72
+ 'CYM':'CYS', 'CYQ':'CYS', 'DAH':'PHE', 'DAL':'ALA', 'DAR':'ARG', 'DAS':'ASP', 'DCY':'CYS', 'DGL':'GLU', 'DGN':'GLN', 'DHA':'ALA',
73
+ 'DHI':'HIS', 'DIL':'ILE', 'DIV':'VAL', 'DLE':'LEU', 'DLY':'LYS', 'DNP':'ALA', 'DPN':'PHE', 'DPR':'PRO', 'DSN':'SER', 'DSP':'ASP',
74
+ 'DTH':'THR', 'DTR':'TRP', 'DTY':'TYR', 'DVA':'VAL', 'EFC':'CYS', 'FLA':'ALA', 'FME':'MET', 'GGL':'GLU', 'GL3':'GLY', 'GLZ':'GLY',
75
+ 'GMA':'GLU', 'GSC':'GLY', 'HAC':'ALA', 'HAR':'ARG', 'HIC':'HIS', 'HIP':'HIS', 'HMR':'ARG', 'HPQ':'PHE', 'HTR':'TRP', 'HYP':'PRO',
76
+ 'IAS':'ASP', 'IIL':'ILE', 'IYR':'TYR', 'KCX':'LYS', 'LLP':'LYS', 'LLY':'LYS', 'LTR':'TRP', 'LYM':'LYS', 'LYZ':'LYS', 'MAA':'ALA', 'MEN':'ASN',
77
+ 'MHS':'HIS', 'MIS':'SER', 'MK8':'LEU', 'MLE':'LEU', 'MPQ':'GLY', 'MSA':'GLY', 'MSE':'MET', 'MVA':'VAL', 'NEM':'HIS', 'NEP':'HIS', 'NLE':'LEU',
78
+ 'NLN':'LEU', 'NLP':'LEU', 'NMC':'GLY', 'OAS':'SER', 'OCS':'CYS', 'OMT':'MET', 'PAQ':'TYR', 'PCA':'GLU', 'PEC':'CYS', 'PHI':'PHE',
79
+ 'PHL':'PHE', 'PR3':'CYS', 'PRR':'ALA', 'PTR':'TYR', 'PYX':'CYS', 'SAC':'SER', 'SAR':'GLY', 'SCH':'CYS', 'SCS':'CYS', 'SCY':'CYS',
80
+ 'SEL':'SER', 'SEP':'SER', 'SET':'SER', 'SHC':'CYS', 'SHR':'LYS', 'SMC':'CYS', 'SOC':'CYS', 'STY':'TYR', 'SVA':'SER', 'TIH':'ALA',
81
+ 'TPL':'TRP', 'TPO':'THR', 'TPQ':'ALA', 'TRG':'LYS', 'TRO':'TRP', 'TYB':'TYR', 'TYI':'TYR', 'TYQ':'TYR', 'TYS':'TYR', 'TYY':'TYR'
82
+ }
83
+ proteinResidues = ['ALA', 'ASN', 'CYS', 'GLU', 'HIS', 'LEU', 'MET', 'PRO', 'THR', 'TYR', 'ARG', 'ASP', 'GLN', 'GLY', 'ILE', 'LYS', 'PHE', 'SER', 'TRP', 'VAL']
84
+ rnaResidues = ['A', 'G', 'C', 'U', 'I']
85
+ dnaResidues = ['DA', 'DG', 'DC', 'DT', 'DI']
86
+
87
+ class Sequence(object):
88
+ """Sequence holds the sequence of a chain, as specified by SEQRES records."""
89
+ def __init__(self, chainId, residues):
90
+ self.chainId = chainId
91
+ self.residues = residues
92
+
93
+ class ModifiedResidue(object):
94
+ """ModifiedResidue holds information about a modified residue, as specified by a MODRES record."""
95
+ def __init__(self, chainId, number, residueName, standardName):
96
+ self.chainId = chainId
97
+ self.number = number
98
+ self.residueName = residueName
99
+ self.standardName = standardName
100
+
101
+ def _guessFileFormat(file, filename):
102
+ """Guess whether a file is PDB or PDBx/mmCIF based on its filename and contents."""
103
+ filename = filename.lower()
104
+ if '.pdbx' in filename or '.cif' in filename:
105
+ return 'pdbx'
106
+ if '.pdb' in filename:
107
+ return 'pdb'
108
+ for line in file:
109
+ if line.startswith('data_') or line.startswith('loop_'):
110
+ file.seek(0)
111
+ return 'pdbx'
112
+ if line.startswith('HEADER') or line.startswith('REMARK') or line.startswith('TITLE '):
113
+ file.seek(0)
114
+ return 'pdb'
115
+
116
+ # It's certainly not a valid PDBx/mmCIF. Guess that it's a PDB.
117
+
118
+ file.seek(0)
119
+ return 'pdb'
120
+
121
+ def _overlayPoints(points1, points2):
122
+ """Given two sets of points, determine the translation and rotation that matches them as closely as possible.
123
+
124
+ Parameters
125
+ ----------
126
+ points1 (numpy array of openmm.unit.Quantity with units compatible with distance) - reference set of coordinates
127
+ points2 (numpy array of openmm.unit.Quantity with units compatible with distance) - set of coordinates to be rotated
128
+
129
+ Returns
130
+ -------
131
+ translate2 - vector to translate points2 by in order to center it
132
+ rotate - rotation matrix to apply to centered points2 to map it on to points1
133
+ center1 - center of points1
134
+
135
+ Notes
136
+ -----
137
+ This is based on W. Kabsch, Acta Cryst., A34, pp. 828-829 (1978).
138
+
139
+ """
140
+
141
+ if len(points1) == 0:
142
+ return (mm.Vec3(0, 0, 0), np.identity(3), mm.Vec3(0, 0, 0))
143
+ if len(points1) == 1:
144
+ return (points1[0], np.identity(3), -1*points2[0])
145
+
146
+ # Compute centroids.
147
+
148
+ center1 = unit.sum(points1)/float(len(points1))
149
+ center2 = unit.sum(points2)/float(len(points2))
150
+
151
+ # Compute R matrix.
152
+
153
+ R = np.zeros((3, 3))
154
+ for p1, p2 in zip(points1, points2):
155
+ x = p1-center1
156
+ y = p2-center2
157
+ for i in range(3):
158
+ for j in range(3):
159
+ R[i][j] += y[i]*x[j]
160
+
161
+ # Use an SVD to compute the rotation matrix.
162
+
163
+ (u, s, v) = lin.svd(R)
164
+ return (-1*center2, np.dot(u, v).transpose(), center1)
165
+
166
+ def _findUnoccupiedDirection(point, positions):
167
+ """Given a point in space and a list of atom positions, find the direction in which the local density of atoms is lowest."""
168
+
169
+ point = point.value_in_unit(unit.nanometers)
170
+ direction = mm.Vec3(0, 0, 0)
171
+ for pos in positions.value_in_unit(unit.nanometers):
172
+ delta = pos-point
173
+ distance = unit.norm(delta)
174
+ if distance > 0.1:
175
+ distance2 = distance*distance
176
+ direction -= delta/(distance2*distance2)
177
+ direction /= unit.norm(direction)
178
+ return direction
179
+
180
+ class PDBFixer(object):
181
+ """PDBFixer implements many tools for fixing problems in PDB and PDBx/mmCIF files.
182
+ """
183
+
184
+ def __init__(self, filename=None, pdbfile=None, pdbxfile=None, url=None, pdbid=None):
185
+ """Create a new PDBFixer instance to fix problems in a PDB or PDBx/mmCIF file.
186
+
187
+ Parameters
188
+ ----------
189
+ filename : str, optional, default=None
190
+ The name of the file to read. The format is determined automatically based on the filename extension, or if
191
+ that is ambiguous, by looking at the file content.
192
+ pdbfile : file, optional, default=None
193
+ A file-like object from which the PDB file is to be read.
194
+ The file is not closed after reading.
195
+ pdbxfile : file, optional, default=None
196
+ A file-like object from which the PDBx/mmCIF file is to be read.
197
+ The file is not closed after reading.
198
+ url : str, optional, default=None
199
+ A URL specifying the internet location from which the file contents should be retrieved. The format is
200
+ determined automatically by looking for a filename extension in the URL, or if that is ambiguous, by looking
201
+ at the file content.
202
+ pdbid : str, optional, default=None
203
+ A four-letter PDB code specifying the structure to be retrieved from the RCSB.
204
+
205
+ Notes
206
+ -----
207
+ Only one of structure, filename, pdbfile, pdbxfile, url, or pdbid may be specified or an exception will be thrown.
208
+
209
+ Examples
210
+ --------
211
+
212
+ Start from a filename.
213
+
214
+ >>> filename = resource_filename('pdbfixer', 'tests/data/test.pdb')
215
+ >>> fixer = PDBFixer(filename=filename)
216
+
217
+ Start from a file object.
218
+
219
+ >>> with open(filename) as f:
220
+ ... fixer = PDBFixer(pdbfile=f)
221
+
222
+ Start from a URL.
223
+
224
+ >>> fixer = PDBFixer(url='http://www.rcsb.org/pdb/files/1VII.pdb')
225
+
226
+ Start from a PDB code.
227
+
228
+ >>> fixer = PDBFixer(pdbid='1VII')
229
+
230
+ """
231
+
232
+ # Check to make sure only one option has been specified.
233
+ if bool(filename) + bool(pdbfile) + bool(pdbxfile) + bool(url) + bool(pdbid) != 1:
234
+ raise Exception("Exactly one option [filename, pdbfile, pdbxfile, url, pdbid] must be specified.")
235
+
236
+ self.source = None
237
+ if pdbid:
238
+ # A PDB id has been specified.
239
+ url = 'http://www.rcsb.org/pdb/files/%s.pdb' % pdbid
240
+ if filename:
241
+ # A local file has been specified.
242
+ self.source = filename
243
+ file = open(filename, 'r')
244
+ if _guessFileFormat(file, filename) == 'pdbx':
245
+ self._initializeFromPDBx(file)
246
+ else:
247
+ self._initializeFromPDB(file)
248
+ file.close()
249
+ elif pdbfile:
250
+ # A file-like object has been specified.
251
+ self._initializeFromPDB(pdbfile)
252
+ elif pdbxfile:
253
+ # A file-like object has been specified.
254
+ self._initializeFromPDBx(pdbxfile)
255
+ elif url:
256
+ # A URL has been specified.
257
+ self.source = url
258
+ file = urlopen(url)
259
+ contents = file.read().decode('utf-8')
260
+ file.close()
261
+ file = StringIO(contents)
262
+ if _guessFileFormat(file, url) == 'pdbx':
263
+ self._initializeFromPDBx(contents)
264
+ else:
265
+ self._initializeFromPDB(StringIO(contents))
266
+
267
+ # Check the structure has some atoms in it.
268
+ atoms = list(self.topology.atoms())
269
+ if len(atoms) == 0:
270
+ raise Exception("Structure contains no atoms.")
271
+
272
+ # Load the templates.
273
+
274
+ self.templates = {}
275
+ templatesPath = os.path.join(os.path.dirname(__file__), 'templates')
276
+ for file in os.listdir(templatesPath):
277
+ templatePdb = app.PDBFile(os.path.join(templatesPath, file))
278
+ name = next(templatePdb.topology.residues()).name
279
+ self.templates[name] = templatePdb
280
+
281
+ def _initializeFromPDB(self, file):
282
+ """Initialize this object by reading a PDB file."""
283
+
284
+ structure = PdbStructure(file)
285
+ pdb = app.PDBFile(structure)
286
+ self.topology = pdb.topology
287
+ self.positions = pdb.positions
288
+ self.sequences = [Sequence(s.chain_id, s.residues) for s in structure.sequences]
289
+ self.modifiedResidues = [ModifiedResidue(r.chain_id, r.number, r.residue_name, r.standard_name) for r in structure.modified_residues]
290
+
291
+ def _initializeFromPDBx(self, file):
292
+ """Initialize this object by reading a PDBx/mmCIF file."""
293
+
294
+ pdbx = app.PDBxFile(file)
295
+ self.topology = pdbx.topology
296
+ self.positions = pdbx.positions
297
+
298
+ # PDBxFile doesn't record the information about sequence or modified residues, so we need to read them separately.
299
+
300
+ file.seek(0)
301
+ reader = PdbxReader(file)
302
+ data = []
303
+ reader.read(data)
304
+ block = data[0]
305
+
306
+ # Load the sequence data.
307
+
308
+ sequenceData = block.getObj('entity_poly_seq')
309
+ sequences = {}
310
+ if sequenceData is not None:
311
+ entityIdCol = sequenceData.getAttributeIndex('entity_id')
312
+ residueCol = sequenceData.getAttributeIndex('mon_id')
313
+ for row in sequenceData.getRowList():
314
+ entityId = row[entityIdCol]
315
+ residue = row[residueCol]
316
+ if entityId not in sequences:
317
+ sequences[entityId] = []
318
+ sequences[entityId].append(residue)
319
+
320
+ # Sequences are stored by "entity". There could be multiple chains that are all the same entity, so we need to
321
+ # convert from entities to chains.
322
+
323
+ asymData = block.getObj('struct_asym')
324
+ self.sequences = []
325
+ if asymData is not None:
326
+ asymIdCol = asymData.getAttributeIndex('id')
327
+ entityIdCol = asymData.getAttributeIndex('entity_id')
328
+ for row in asymData.getRowList():
329
+ asymId = row[asymIdCol]
330
+ entityId = row[entityIdCol]
331
+ if entityId in sequences:
332
+ self.sequences.append(Sequence(asymId, sequences[entityId]))
333
+
334
+ # Load the modified residues.
335
+
336
+ modData = block.getObj('pdbx_struct_mod_residue')
337
+ self.modifiedResidues = []
338
+ if modData is not None:
339
+ asymIdCol = modData.getAttributeIndex('label_asym_id')
340
+ resNameCol = modData.getAttributeIndex('label_comp_id')
341
+ resNumCol = modData.getAttributeIndex('auth_seq_id')
342
+ standardResCol = modData.getAttributeIndex('parent_comp_id')
343
+ if -1 not in (asymIdCol, resNameCol, resNumCol, standardResCol):
344
+ for row in modData.getRowList():
345
+ self.modifiedResidues.append(ModifiedResidue(row[asymIdCol], int(row[resNumCol]), row[resNameCol], row[standardResCol]))
346
+
347
+ def _addAtomsToTopology(self, heavyAtomsOnly, omitUnknownMolecules):
348
+ """Create a new Topology in which missing atoms have been added.
349
+
350
+ Parameters
351
+ ----------
352
+ heavyAtomsOnly : bool
353
+ If True, only heavy atoms will be added to the topology.
354
+ omitUnknownMolecules : bool
355
+ If True, unknown molecules will be omitted from the topology.
356
+
357
+ Returns
358
+ -------
359
+ newTopology : openmm.app.Topology
360
+ A new Topology object containing atoms from the old.
361
+ newPositions : list of openmm.unit.Quantity with units compatible with nanometers
362
+ Atom positions for the new Topology object.
363
+ newAtoms : openmm.app.Topology.Atom
364
+ New atom objects.
365
+ existingAtomMap : dict
366
+ Mapping from old atoms to new atoms.
367
+
368
+ """
369
+
370
+ newTopology = app.Topology()
371
+ newPositions = []*unit.nanometer
372
+ newAtoms = []
373
+ existingAtomMap = {}
374
+ addedAtomMap = {}
375
+ addedOXT = []
376
+ residueCenters = [self._computeResidueCenter(res).value_in_unit(unit.nanometers) for res in self.topology.residues()]*unit.nanometers
377
+ for chain in self.topology.chains():
378
+ if omitUnknownMolecules and not any(residue.name in self.templates for residue in chain.residues()):
379
+ continue
380
+ chainResidues = list(chain.residues())
381
+ newChain = newTopology.addChain(chain.id)
382
+ for indexInChain, residue in enumerate(chain.residues()):
383
+
384
+ # Insert missing residues here.
385
+
386
+ if (chain.index, indexInChain) in self.missingResidues:
387
+ insertHere = self.missingResidues[(chain.index, indexInChain)]
388
+ endPosition = self._computeResidueCenter(residue)
389
+ if indexInChain > 0:
390
+ startPosition = self._computeResidueCenter(chainResidues[indexInChain-1])
391
+ loopDirection = _findUnoccupiedDirection((startPosition+endPosition)/2, residueCenters)
392
+ else:
393
+ outward = _findUnoccupiedDirection(endPosition, residueCenters)*unit.nanometers
394
+ norm = unit.norm(outward)
395
+ if norm > 0*unit.nanometer:
396
+ outward *= len(insertHere)*0.5*unit.nanometer/norm
397
+ startPosition = endPosition+outward
398
+ loopDirection = None
399
+ firstIndex = int(residue.id)-len(insertHere)
400
+ self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, loopDirection, residue, newAtoms, newPositions, firstIndex)
401
+
402
+ # Create the new residue and add existing heavy atoms.
403
+
404
+ newResidue = newTopology.addResidue(residue.name, newChain, residue.id, residue.insertionCode)
405
+ for atom in residue.atoms():
406
+ if not heavyAtomsOnly or (atom.element is not None and atom.element != hydrogen):
407
+ if atom.name == 'OXT' and (chain.index, indexInChain+1) in self.missingResidues:
408
+ continue # Remove terminal oxygen, since we'll add more residues after this one
409
+ newAtom = newTopology.addAtom(atom.name, atom.element, newResidue)
410
+ existingAtomMap[atom] = newAtom
411
+ newPositions.append(self.positions[atom.index])
412
+ if residue in self.missingAtoms:
413
+
414
+ # Find corresponding atoms in the residue and the template.
415
+
416
+ template = self.templates[residue.name]
417
+ atomPositions = dict((atom.name, self.positions[atom.index]) for atom in residue.atoms())
418
+ points1 = []
419
+ points2 = []
420
+ for atom in template.topology.atoms():
421
+ if atom.name in atomPositions:
422
+ points1.append(atomPositions[atom.name].value_in_unit(unit.nanometer))
423
+ points2.append(template.positions[atom.index].value_in_unit(unit.nanometer))
424
+
425
+ # Compute the optimal transform to overlay them.
426
+
427
+ (translate2, rotate, translate1) = _overlayPoints(points1, points2)
428
+
429
+ # Add the missing atoms.
430
+
431
+ addedAtomMap[residue] = {}
432
+ for atom in self.missingAtoms[residue]:
433
+ newAtom = newTopology.addAtom(atom.name, atom.element, newResidue)
434
+ newAtoms.append(newAtom)
435
+ addedAtomMap[residue][atom] = newAtom
436
+ templatePosition = template.positions[atom.index].value_in_unit(unit.nanometer)
437
+ newPositions.append((mm.Vec3(*np.dot(rotate, templatePosition+translate2))+translate1)*unit.nanometer)
438
+ if residue in self.missingTerminals:
439
+ terminalsToAdd = self.missingTerminals[residue]
440
+ else:
441
+ terminalsToAdd = None
442
+
443
+ # If this is the end of the chain, add any missing residues that come after it.
444
+
445
+ if residue == chainResidues[-1] and (chain.index, indexInChain+1) in self.missingResidues:
446
+ insertHere = self.missingResidues[(chain.index, indexInChain+1)]
447
+ if len(insertHere) > 0:
448
+ startPosition = self._computeResidueCenter(residue)
449
+ outward = _findUnoccupiedDirection(startPosition, residueCenters)*unit.nanometers
450
+ norm = unit.norm(outward)
451
+ if norm > 0*unit.nanometer:
452
+ outward *= len(insertHere)*0.5*unit.nanometer/norm
453
+ endPosition = startPosition+outward
454
+ firstIndex = int(residue.id)+1
455
+ self._addMissingResiduesToChain(newChain, insertHere, startPosition, endPosition, None, residue, newAtoms, newPositions, firstIndex)
456
+ newResidue = list(newChain.residues())[-1]
457
+ if newResidue.name in proteinResidues:
458
+ terminalsToAdd = ['OXT']
459
+ else:
460
+ terminalsToAdd = None
461
+
462
+ # If a terminal OXT is missing, add it.
463
+
464
+ if terminalsToAdd is not None:
465
+ atomPositions = dict((atom.name, newPositions[atom.index].value_in_unit(unit.nanometer)) for atom in newResidue.atoms())
466
+ if 'OXT' in terminalsToAdd:
467
+ newAtom = newTopology.addAtom('OXT', oxygen, newResidue)
468
+ newAtoms.append(newAtom)
469
+ addedOXT.append(newAtom)
470
+ d_ca_o = atomPositions['O']-atomPositions['CA']
471
+ d_ca_c = atomPositions['C']-atomPositions['CA']
472
+ d_ca_c /= unit.sqrt(unit.dot(d_ca_c, d_ca_c))
473
+ v = d_ca_o - d_ca_c*unit.dot(d_ca_c, d_ca_o)
474
+ newPositions.append((atomPositions['O']+2*v)*unit.nanometer)
475
+ newTopology.setUnitCellDimensions(self.topology.getUnitCellDimensions())
476
+ newTopology.createStandardBonds()
477
+ newTopology.createDisulfideBonds(newPositions)
478
+
479
+ # Add the bonds between atoms in heterogens.
480
+
481
+ for a1,a2 in self.topology.bonds():
482
+ if a1 in existingAtomMap and a2 in existingAtomMap and (a1.residue.name not in app.Topology._standardBonds or a2.residue.name not in app.Topology._standardBonds):
483
+ newTopology.addBond(existingAtomMap[a1], existingAtomMap[a2])
484
+
485
+ # Return the results.
486
+
487
+ return (newTopology, newPositions, newAtoms, existingAtomMap)
488
+
489
+ def _computeResidueCenter(self, residue):
490
+ """Compute the centroid of a residue."""
491
+ return unit.sum([self.positions[atom.index] for atom in residue.atoms()])/len(list(residue.atoms()))
492
+
493
+ def _addMissingResiduesToChain(self, chain, residueNames, startPosition, endPosition, loopDirection, orientTo, newAtoms, newPositions, firstIndex):
494
+ """Add a series of residues to a chain."""
495
+ orientToPositions = dict((atom.name, self.positions[atom.index]) for atom in orientTo.atoms())
496
+ if loopDirection is None:
497
+ loopDirection = mm.Vec3(0, 0, 0)
498
+
499
+ # We'll add the residues in an arc connecting the endpoints. Figure out the height of that arc.
500
+
501
+ length = unit.norm(endPosition-startPosition)
502
+ numResidues = len(residueNames)
503
+ if length > numResidues*0.3*unit.nanometers:
504
+ loopHeight = 0*unit.nanometers
505
+ else:
506
+ loopHeight = (numResidues*0.3*unit.nanometers-length)/2
507
+
508
+ # Add the residues.
509
+
510
+ for i, residueName in enumerate(residueNames):
511
+ template = self.templates[residueName]
512
+
513
+ # Find a translation that best matches the adjacent residue.
514
+
515
+ points1 = []
516
+ points2 = []
517
+ for atom in template.topology.atoms():
518
+ if atom.name in orientToPositions:
519
+ points1.append(orientToPositions[atom.name].value_in_unit(unit.nanometer))
520
+ points2.append(template.positions[atom.index].value_in_unit(unit.nanometer))
521
+ (translate2, rotate, translate1) = _overlayPoints(points1, points2)
522
+
523
+ # Create the new residue.
524
+
525
+ newResidue = chain.topology.addResidue(residueName, chain, "%d" % ((firstIndex+i)%10000))
526
+ fraction = (i+1.0)/(numResidues+1.0)
527
+ translate = startPosition + (endPosition-startPosition)*fraction + loopHeight*math.sin(fraction*math.pi)*loopDirection
528
+ templateAtoms = list(template.topology.atoms())
529
+ if newResidue == next(chain.residues()):
530
+ templateAtoms = [atom for atom in templateAtoms if atom.name not in ('P', 'OP1', 'OP2')]
531
+ for atom in templateAtoms:
532
+ newAtom = chain.topology.addAtom(atom.name, atom.element, newResidue)
533
+ newAtoms.append(newAtom)
534
+ templatePosition = template.positions[atom.index].value_in_unit(unit.nanometer)
535
+ newPositions.append(mm.Vec3(*np.dot(rotate, templatePosition))*unit.nanometer+translate)
536
+
537
+ def removeChains(self, chainIndices=None, chainIds=None):
538
+ """Remove a set of chains from the structure.
539
+
540
+ Parameters
541
+ ----------
542
+ chainIndices : list of int, optional, default=None
543
+ List of indices of chains to remove.
544
+ chainIds : list of str, optional, default=None
545
+ List of chain ids of chains to remove.
546
+
547
+ Examples
548
+ --------
549
+
550
+ Load a PDB file with two chains and eliminate the second chain.
551
+
552
+ >>> fixer = PDBFixer(pdbid='4J7F')
553
+ >>> fixer.removeChains(chainIndices=[1])
554
+
555
+ Load a PDB file with two chains and eliminate chains named 'B' and 'D'.
556
+
557
+ >>> fixer = PDBFixer(pdbid='4J7F')
558
+ >>> fixer.removeChains(chainIds=['B','D'])
559
+
560
+ """
561
+ modeller = app.Modeller(self.topology, self.positions)
562
+ allChains = list(self.topology.chains())
563
+
564
+ if chainIndices == None:
565
+ chainIndices = list()
566
+ if chainIds != None:
567
+ # Add all chains that match the selection to the list.
568
+ for (chainNumber, chain) in enumerate(allChains):
569
+ if chain.id in chainIds:
570
+ chainIndices.append(chainNumber)
571
+ # Ensure only unique entries remain.
572
+ chainIndices = list(set(chainIndices))
573
+
574
+ # Do nothing if no chains will be deleted.
575
+ if len(chainIndices) == 0:
576
+ return
577
+
578
+ modeller.delete(allChains[i] for i in chainIndices)
579
+ self.topology = modeller.topology
580
+ self.positions = modeller.positions
581
+
582
+ return
583
+
584
+ def findMissingResidues(self):
585
+ """Find residues that are missing from the structure.
586
+
587
+ The results are stored into the missingResidues field, which is a dict. Each key is a tuple consisting of
588
+ the index of a chain, and the residue index within that chain at which new residues should be inserted.
589
+ The corresponding value is a list of the names of residues to insert there.
590
+
591
+ Examples
592
+ --------
593
+
594
+ >>> fixer = PDBFixer(pdbid='1VII')
595
+ >>> fixer.findMissingResidues()
596
+ >>> missing_residues = fixer.missingResidues
597
+
598
+ """
599
+ chains = [c for c in self.topology.chains() if len(list(c.residues())) > 0]
600
+ chainWithGaps = {}
601
+
602
+ # Find the sequence of each chain, with gaps for missing residues.
603
+
604
+ for chain in chains:
605
+ residues = list(chain.residues())
606
+ ids = [int(r.id) for r in residues]
607
+ for i, res in enumerate(residues):
608
+ if res.insertionCode not in ('', ' '):
609
+ for j in range(i, len(residues)):
610
+ ids[j] += 1
611
+ minResidue = min(ids)
612
+ maxResidue = max(ids)
613
+ chainWithGaps[chain] = [None]*(maxResidue-minResidue+1)
614
+ for r, id in zip(residues, ids):
615
+ chainWithGaps[chain][id-minResidue] = r.name
616
+
617
+ # Try to find the chain that matches each sequence.
618
+
619
+ chainSequence = {}
620
+ chainOffset = {}
621
+ for sequence in self.sequences:
622
+ for chain in chains:
623
+ if chain.id != sequence.chainId:
624
+ continue
625
+ if chain in chainSequence:
626
+ continue
627
+ for offset in range(len(sequence.residues)-len(chainWithGaps[chain])+1):
628
+ if all(a == b or b == None for a,b in zip(sequence.residues[offset:], chainWithGaps[chain])):
629
+ chainSequence[chain] = sequence
630
+ chainOffset[chain] = offset
631
+ break
632
+ if chain in chainSequence:
633
+ break
634
+
635
+ # Now build the list of residues to add.
636
+
637
+ self.missingResidues = {}
638
+ for chain in self.topology.chains():
639
+ if chain in chainSequence:
640
+ offset = chainOffset[chain]
641
+ sequence = chainSequence[chain].residues
642
+ gappedSequence = chainWithGaps[chain]
643
+ index = 0
644
+ for i in range(len(sequence)):
645
+ if i < offset or i >= len(gappedSequence)+offset or gappedSequence[i-offset] is None:
646
+ key = (chain.index, index)
647
+ if key not in self.missingResidues:
648
+ self.missingResidues[key] = []
649
+ residueName = sequence[i]
650
+ if residueName in substitutions:
651
+ residueName = substitutions[sequence[i]]
652
+ self.missingResidues[key].append(residueName)
653
+ else:
654
+ index += 1
655
+
656
+ def findNonstandardResidues(self):
657
+ """Identify non-standard residues found in the structure, and select standard residues to replace them with.
658
+
659
+ The results are stored into the nonstandardResidues field, which is a map of Residue objects to the names
660
+ of suggested replacement residues.
661
+
662
+ Examples
663
+ --------
664
+
665
+ Find nonstandard residues.
666
+
667
+ >>> fixer = PDBFixer(pdbid='1YRI')
668
+ >>> fixer.findNonstandardResidues()
669
+ >>> nonstandard_residues = fixer.nonstandardResidues
670
+
671
+ """
672
+
673
+ # First find residues based on our table of standard substitutions.
674
+
675
+ nonstandard = dict((r, substitutions[r.name]) for r in self.topology.residues() if r.name in substitutions)
676
+
677
+ # Now add ones based on MODRES records.
678
+
679
+ modres = dict(((m.chainId, str(m.number), m.residueName), m.standardName) for m in self.modifiedResidues)
680
+ for chain in self.topology.chains():
681
+ for residue in chain.residues():
682
+ key = (chain.id, residue.id, residue.name)
683
+ if key in modres:
684
+ replacement = modres[key]
685
+ if replacement == 'DU':
686
+ replacement = 'DT'
687
+ if replacement in self.templates:
688
+ nonstandard[residue] = replacement
689
+ self.nonstandardResidues = [(r, nonstandard[r]) for r in sorted(nonstandard, key=lambda r: r.index)]
690
+
691
+ def replaceNonstandardResidues(self):
692
+ """Replace every residue listed in the nonstandardResidues field with the specified standard residue.
693
+
694
+ Notes
695
+ -----
696
+ You must have first called findNonstandardResidues() to identify nonstandard residues.
697
+
698
+ Examples
699
+ --------
700
+
701
+ Find and replace nonstandard residues using replacement templates stored in the 'templates' field of PDBFixer object.
702
+
703
+ >>> fixer = PDBFixer(pdbid='1YRI')
704
+ >>> fixer.findNonstandardResidues()
705
+ >>> fixer.replaceNonstandardResidues()
706
+
707
+ """
708
+ if len(self.nonstandardResidues) > 0:
709
+ deleteAtoms = []
710
+
711
+ # Find atoms that should be deleted.
712
+
713
+ for residue, replaceWith in self.nonstandardResidues:
714
+ residue.name = replaceWith
715
+ template = self.templates[replaceWith]
716
+ standardAtoms = set(atom.name for atom in template.topology.atoms())
717
+ for atom in residue.atoms():
718
+ if atom.element in (None, hydrogen) or atom.name not in standardAtoms:
719
+ deleteAtoms.append(atom)
720
+
721
+ # Delete them.
722
+
723
+ modeller = app.Modeller(self.topology, self.positions)
724
+ modeller.delete(deleteAtoms)
725
+ self.topology = modeller.topology
726
+ self.positions = modeller.positions
727
+
728
+
729
+ def applyMutations(self, mutations, chain_id):
730
+ """Apply a list of amino acid substitutions to make a mutant protein.
731
+
732
+ Parameters
733
+ ----------
734
+ mutations : list of strings
735
+ Each string must include the resName (original), index,
736
+ and resName (target). For example, ALA-133-GLY will mutate
737
+ alanine 133 to glycine.
738
+ chain_id : str
739
+ String based chain ID of the single chain you wish to mutate.
740
+
741
+ Notes
742
+ -----
743
+
744
+ We require three letter codes to avoid possible ambiguitities.
745
+ We can't guarantee that the resulting model is a good one; for
746
+ significant changes in sequence, you should probably be using
747
+ a standalone homology modelling tool.
748
+
749
+ Examples
750
+ --------
751
+
752
+ Find nonstandard residues.
753
+
754
+ >>> fixer = PDBFixer(pdbid='1VII')
755
+ >>> fixer.applyMutations(["ALA-57-GLY"], "A")
756
+ >>> fixer.findMissingResidues()
757
+ >>> fixer.findMissingAtoms()
758
+ >>> fixer.addMissingAtoms()
759
+ >>> fixer.addMissingHydrogens(7.0)
760
+
761
+ """
762
+ # Retrieve all residues that match the specified chain_id.
763
+ # NOTE: Multiple chains may have the same chainid, but must have unique resSeq entries.
764
+ resSeq_to_residue = dict() # resSeq_to_residue[resid] is the residue in the requested chain corresponding to residue identifier 'resid'
765
+ for chain in self.topology.chains():
766
+ if chain.id == chain_id:
767
+ for residue in chain.residues():
768
+ resSeq_to_residue[int(residue.id)] = residue
769
+
770
+ # Make a map of residues to mutate based on requested mutation list.
771
+ residue_map = dict() # residue_map[residue] is the name of the new residue to mutate to, if a mutation is desired
772
+ for mut_str in mutations:
773
+ old_name, resSeq, new_name = mut_str.split("-")
774
+ resSeq = int(resSeq)
775
+
776
+ if resSeq not in resSeq_to_residue:
777
+ raise(KeyError("Cannot find chain %s residue %d in system!" % (chain_id, resSeq)))
778
+
779
+ residue = resSeq_to_residue[resSeq] # retrieve the requested residue
780
+
781
+ if residue.name != old_name:
782
+ raise(ValueError("You asked to mutate chain %s residue %d name %s, but that residue is actually %s!" % (chain_id, resSeq, old_name, residue.name)))
783
+
784
+ try:
785
+ template = self.templates[new_name]
786
+ except KeyError:
787
+ raise(KeyError("Cannot find residue %s in template library!" % new_name))
788
+
789
+ # Store mutation
790
+ residue_map[residue] = new_name
791
+
792
+ # If there are mutations to be made, make them.
793
+ if len(residue_map) > 0:
794
+ deleteAtoms = [] # list of atoms to delete
795
+
796
+ # Find atoms that should be deleted.
797
+ for residue in residue_map.keys():
798
+ replaceWith = residue_map[residue]
799
+ residue.name = replaceWith
800
+ template = self.templates[replaceWith]
801
+ standardAtoms = set(atom.name for atom in template.topology.atoms())
802
+ for atom in residue.atoms():
803
+ if atom.element in (None, hydrogen) or atom.name not in standardAtoms:
804
+ deleteAtoms.append(atom)
805
+
806
+ # Delete atoms queued to be deleted.
807
+ modeller = app.Modeller(self.topology, self.positions)
808
+ modeller.delete(deleteAtoms)
809
+ self.topology = modeller.topology
810
+ self.positions = modeller.positions
811
+
812
+
813
+ def findMissingAtoms(self):
814
+ """Find heavy atoms that are missing from the structure.
815
+
816
+ The results are stored into two fields: missingAtoms and missingTerminals. Each of these is a dict whose keys
817
+ are Residue objects and whose values are lists of atom names. missingAtoms contains standard atoms that should
818
+ be present in any residue of that type. missingTerminals contains terminal atoms that should be present at the
819
+ start or end of a chain.
820
+
821
+ Notes
822
+ -----
823
+ You must have first called findMissingResidues().
824
+
825
+ Examples
826
+ --------
827
+
828
+ Find missing heavy atoms in Abl kinase structure.
829
+
830
+ >>> fixer = PDBFixer(pdbid='2F4J')
831
+ >>> fixer.findMissingResidues()
832
+ >>> fixer.findMissingAtoms()
833
+ >>> # Retrieve missing atoms.
834
+ >>> missingAtoms = fixer.missingAtoms
835
+ >>> # Retrieve missing terminal atoms.
836
+ >>> missingTerminals = fixer.missingTerminals
837
+
838
+ """
839
+ missingAtoms = {}
840
+ missingTerminals = {}
841
+
842
+ # Loop over residues.
843
+
844
+ for chain in self.topology.chains():
845
+ chainResidues = list(chain.residues())
846
+ for residue in chain.residues():
847
+ if residue.name in self.templates:
848
+ template = self.templates[residue.name]
849
+ atomNames = set(atom.name for atom in residue.atoms())
850
+ templateAtoms = list(template.topology.atoms())
851
+ if residue == chainResidues[0] and (chain.index, 0) not in self.missingResidues:
852
+ templateAtoms = [atom for atom in templateAtoms if atom.name not in ('P', 'OP1', 'OP2')]
853
+
854
+ # Add atoms from the template that are missing.
855
+
856
+ missing = []
857
+ for atom in templateAtoms:
858
+ if atom.name not in atomNames:
859
+ missing.append(atom)
860
+ if len(missing) > 0:
861
+ missingAtoms[residue] = missing
862
+
863
+ # Add missing terminal atoms.
864
+
865
+ terminals = []
866
+ if residue == chainResidues[-1] and (chain.index, len(chainResidues)) not in self.missingResidues:
867
+ templateNames = set(atom.name for atom in template.topology.atoms())
868
+ if 'OXT' not in atomNames and all(name in templateNames for name in ['C', 'O', 'CA']):
869
+ terminals.append('OXT')
870
+ if len(terminals) > 0:
871
+ missingTerminals[residue] = terminals
872
+ self.missingAtoms = missingAtoms
873
+ self.missingTerminals = missingTerminals
874
+
875
+ def addMissingAtoms(self, seed=None):
876
+ """Add all missing heavy atoms, as specified by the missingAtoms, missingTerminals, and missingResidues fields.
877
+
878
+ Parameters
879
+ ----------
880
+ seed : int
881
+ Integer to set the random seed number of the integrator used in the minimization of the
882
+ coordinates of the newly-added atoms.
883
+
884
+ Notes
885
+ -----
886
+ You must already have called findMissingAtoms() to have identified atoms to be added.
887
+
888
+ Examples
889
+ --------
890
+
891
+ Find missing heavy atoms in Abl kinase structure.
892
+
893
+ >>> fixer = PDBFixer(pdbid='2F4J')
894
+ >>> fixer.findMissingResidues()
895
+ >>> fixer.findMissingAtoms()
896
+ >>> fixer.addMissingAtoms()
897
+
898
+ """
899
+
900
+ # Create a Topology that 1) adds missing atoms, 2) removes all hydrogens, and 3) removes unknown molecules.
901
+
902
+ (newTopology, newPositions, newAtoms, existingAtomMap) = self._addAtomsToTopology(True, True)
903
+ if len(newAtoms) == 0:
904
+
905
+ # No atoms were added, but new bonds might have been created.
906
+
907
+ newBonds = set(newTopology.bonds())
908
+ for atom1, atom2 in self.topology.bonds():
909
+ if atom1 in existingAtomMap and atom2 in existingAtomMap:
910
+ a1 = existingAtomMap[atom1]
911
+ a2 = existingAtomMap[atom2]
912
+ if (a1, a2) in newBonds:
913
+ newBonds.remove((a1, a2))
914
+ elif (a2, a1) in newBonds:
915
+ newBonds.remove((a2, a1))
916
+
917
+ # Add the new bonds to the original Topology.
918
+
919
+ inverseAtomMap = dict((y,x) for (x,y) in existingAtomMap.items())
920
+ for atom1, atom2 in newBonds:
921
+ self.topology.addBond(inverseAtomMap[atom1], inverseAtomMap[atom2])
922
+ else:
923
+
924
+ # Create a System for energy minimizing it.
925
+
926
+ forcefield = self._createForceField(newTopology, False)
927
+ system = forcefield.createSystem(newTopology)
928
+
929
+ # Set any previously existing atoms to be massless, they so won't move.
930
+
931
+ for atom in existingAtomMap.values():
932
+ system.setParticleMass(atom.index, 0.0)
933
+
934
+ # If any heavy atoms were omitted, add them back to avoid steric clashes.
935
+
936
+ nonbonded = [f for f in system.getForces() if isinstance(f, mm.CustomNonbondedForce)][0]
937
+ for atom in self.topology.atoms():
938
+ if atom.element not in (None, hydrogen) and atom not in existingAtomMap:
939
+ system.addParticle(0.0)
940
+ nonbonded.addParticle([])
941
+ newPositions.append(self.positions[atom.index])
942
+
943
+ # For efficiency, only compute interactions that involve a new atom.
944
+
945
+ nonbonded.addInteractionGroup([atom.index for atom in newAtoms], range(system.getNumParticles()))
946
+
947
+ # Do an energy minimization.
948
+
949
+ integrator = mm.LangevinIntegrator(300*unit.kelvin, 10/unit.picosecond, 5*unit.femtosecond)
950
+ if seed is not None:
951
+ integrator.setRandomNumberSeed(seed)
952
+ context = mm.Context(system, integrator)
953
+ context.setPositions(newPositions)
954
+ mm.LocalEnergyMinimizer.minimize(context)
955
+ state = context.getState(getPositions=True)
956
+ if newTopology.getNumResidues() > 1:
957
+ # When looking for pairs of atoms that are too close to each other, exclude pairs that
958
+ # are in the same residue or are directly bonded to each other.
959
+
960
+ exclusions = dict((atom, {a.index for a in atom.residue.atoms()}) for atom in newAtoms)
961
+ for a1, a2 in newTopology.bonds():
962
+ if a1 in exclusions:
963
+ exclusions[a1].add(a2.index)
964
+ if a2 in exclusions:
965
+ exclusions[a2].add(a1.index)
966
+ cutoff = 0.13
967
+ nearest = self._findNearestDistance(context, newAtoms, cutoff, exclusions)
968
+ if nearest < cutoff:
969
+
970
+ # Some atoms are very close together. Run some dynamics while slowly increasing the strength of the
971
+ # repulsive interaction to try to improve the result.
972
+
973
+ for i in range(10):
974
+ context.setParameter('C', 0.15*(i+1))
975
+ integrator.step(200)
976
+ d = self._findNearestDistance(context, newAtoms, cutoff, exclusions)
977
+ if d > nearest:
978
+ nearest = d
979
+ state = context.getState(getPositions=True)
980
+ if nearest >= cutoff:
981
+ break
982
+ context.setState(state)
983
+ context.setParameter('C', 1.0)
984
+ mm.LocalEnergyMinimizer.minimize(context)
985
+ state = context.getState(getPositions=True)
986
+
987
+ # Now create a new Topology, including all atoms from the original one and adding the missing atoms.
988
+
989
+ (newTopology2, newPositions2, newAtoms2, existingAtomMap2) = self._addAtomsToTopology(False, False)
990
+
991
+ # Copy over the minimized positions for the new atoms.
992
+
993
+ for a1, a2 in zip(newAtoms, newAtoms2):
994
+ newPositions2[a2.index] = state.getPositions()[a1.index]
995
+ self.topology = newTopology2
996
+ self.positions = newPositions2
997
+
998
+ def removeHeterogens(self, keepWater=True):
999
+ """Remove all heterogens from the structure.
1000
+
1001
+ Parameters
1002
+ ----------
1003
+ keepWater : bool, optional, default=True
1004
+ If True, water molecules will not be removed.
1005
+
1006
+ Examples
1007
+ --------
1008
+
1009
+ Remove heterogens in Abl structure complexed with imatinib.
1010
+
1011
+ >>> fixer = PDBFixer(pdbid='2F4J')
1012
+ >>> fixer.removeHeterogens(keepWater=False)
1013
+
1014
+ """
1015
+
1016
+ keep = set(proteinResidues).union(dnaResidues).union(rnaResidues)
1017
+ keep.add('N')
1018
+ keep.add('UNK')
1019
+ if keepWater:
1020
+ keep.add('HOH')
1021
+ toDelete = []
1022
+ for residue in self.topology.residues():
1023
+ if residue.name not in keep:
1024
+ toDelete.append(residue)
1025
+ modeller = app.Modeller(self.topology, self.positions)
1026
+ modeller.delete(toDelete)
1027
+ self.topology = modeller.topology
1028
+ self.positions = modeller.positions
1029
+
1030
+ def addMissingHydrogens(self, pH=7.0, forcefield=None):
1031
+ """Add missing hydrogen atoms to the structure.
1032
+
1033
+ Parameters
1034
+ ----------
1035
+ pH : float, optional, default=7.0
1036
+ The pH based on which to select hydrogens.
1037
+ forcefield : ForceField, optional, default=None
1038
+ The forcefield used when adding and minimizing hydrogens. If None, a default forcefield is used.
1039
+
1040
+ Notes
1041
+ -----
1042
+ No extensive electrostatic analysis is performed; only default residue pKas are used.
1043
+
1044
+ Examples
1045
+ --------
1046
+
1047
+ Examples
1048
+ --------
1049
+
1050
+ Add missing hydrogens appropriate for pH 8.
1051
+
1052
+ >>> fixer = PDBFixer(pdbid='1VII')
1053
+ >>> fixer.addMissingHydrogens(pH=8.0)
1054
+
1055
+ """
1056
+ modeller = app.Modeller(self.topology, self.positions)
1057
+ modeller.addHydrogens(pH=pH, forcefield=forcefield)
1058
+ self.topology = modeller.topology
1059
+ self.positions = modeller.positions
1060
+
1061
+ def addSolvent(self, boxSize=None, padding=None, boxVectors=None, positiveIon='Na+', negativeIon='Cl-', ionicStrength=0*unit.molar, boxShape='cube'):
1062
+ """Add a solvent box surrounding the structure.
1063
+
1064
+ Parameters
1065
+ ----------
1066
+ boxSize : openmm.Vec3, optional, default=None
1067
+ The size of the box to fill with water. If specified, padding and boxVectors must not be specified.
1068
+ padding : openmm.unit.Quantity compatible with nanometers, optional, default=None
1069
+ Padding around macromolecule for filling box with water. If specified, boxSize and boxVectors must not be specified.
1070
+ boxVectors : 3-tuple of openmm.Vec3, optional, default=None
1071
+ Three vectors specifying the geometry of the box. If specified, padding and boxSize must not be specified.
1072
+ positiveIon : str, optional, default='Na+'
1073
+ The type of positive ion to add. Allowed values are 'Cs+', 'K+', 'Li+', 'Na+', and 'Rb+'.
1074
+ negativeIon : str, optional, default='Cl-'
1075
+ The type of negative ion to add. Allowed values are 'Cl-', 'Br-', 'F-', and 'I-'.
1076
+ ionicStrength : openmm.unit.Quantity with units compatible with molar, optional, default=0*molar
1077
+ The total concentration of ions (both positive and negative) to add. This does not include ions that are added to neutralize the system.
1078
+ boxShape: str='cube'
1079
+ the box shape to use. Allowed values are 'cube', 'dodecahedron', and 'octahedron'. If padding is None, this is ignored.
1080
+
1081
+ Examples
1082
+ --------
1083
+
1084
+ Add missing residues, heavy atoms, and hydrogens, and then solvate with 10 A padding.
1085
+
1086
+ >>> fixer = PDBFixer(pdbid='1VII')
1087
+ >>> fixer.findMissingResidues()
1088
+ >>> fixer.findMissingAtoms()
1089
+ >>> fixer.addMissingAtoms()
1090
+ >>> fixer.addMissingHydrogens(pH=8.0)
1091
+ >>> fixer.addSolvent(padding=10*unit.angstrom, ionicStrength=0.050*unit.molar)
1092
+
1093
+ """
1094
+
1095
+ modeller = app.Modeller(self.topology, self.positions)
1096
+ forcefield = self._createForceField(self.topology, True)
1097
+ modeller.addSolvent(forcefield, padding=padding, boxSize=boxSize, boxVectors=boxVectors, boxShape=boxShape, positiveIon=positiveIon, negativeIon=negativeIon, ionicStrength=ionicStrength)
1098
+ chains = list(modeller.topology.chains())
1099
+ if len(chains) == 1:
1100
+ chains[0].id = 'A'
1101
+ else:
1102
+ chains[-1].id = chr(ord(chains[-2].id)+1)
1103
+ self.topology = modeller.topology
1104
+ self.positions = modeller.positions
1105
+
1106
+ def addMembrane(self, lipidType='POPC', membraneCenterZ=0*unit.nanometer, minimumPadding=1*unit.nanometer, positiveIon='Na+', negativeIon='Cl-', ionicStrength=0*unit.molar):
1107
+ """Add a lipid membrane to the structure.
1108
+
1109
+ This method adds both lipids and water, so you should call either addSolvent() or addMembrane(),
1110
+ but not both. See Modeller.addMembrane() for more details.
1111
+
1112
+ Parameters
1113
+ ----------
1114
+ lipidType : string='POPC'
1115
+ the type of lipid to use. Supported values are 'POPC', 'POPE', 'DLPC', 'DLPE', 'DMPC', 'DOPC', and 'DPPC'.
1116
+ membraneCenterZ: distance=0*nanometer
1117
+ the position along the Z axis of the center of the membrane
1118
+ minimumPadding : distance=1*nanometer
1119
+ the padding distance to use
1120
+ positiveIon : str, optional, default='Na+'
1121
+ The type of positive ion to add. Allowed values are 'Cs+', 'K+', 'Li+', 'Na+', and 'Rb+'.
1122
+ negativeIon : str, optional, default='Cl-'
1123
+ The type of negative ion to add. Allowed values are 'Cl-', 'Br-', 'F-', and 'I-'.
1124
+ ionicStrength : openmm.unit.Quantity with units compatible with molar, optional, default=0*molar
1125
+ The total concentration of ions (both positive and negative) to add. This does not include ions that are added to neutralize the system.
1126
+ """
1127
+ modeller = app.Modeller(self.topology, self.positions)
1128
+ forcefield = self._createForceField(self.topology, True)
1129
+ modeller.addMembrane(forcefield, lipidType=lipidType, minimumPadding=minimumPadding, positiveIon=positiveIon, negativeIon=negativeIon, ionicStrength=ionicStrength)
1130
+ chains = list(modeller.topology.chains())
1131
+ if len(chains) == 1:
1132
+ chains[0].id = 'A'
1133
+ else:
1134
+ chains[-1].id = chr(ord(chains[-2].id)+1)
1135
+ self.topology = modeller.topology
1136
+ self.positions = modeller.positions
1137
+
1138
+ def _createForceField(self, newTopology, water):
1139
+ """Create a force field to use for optimizing the positions of newly added atoms."""
1140
+
1141
+ if water:
1142
+ forcefield = app.ForceField('amber14-all.xml', 'amber14/tip3p.xml')
1143
+ nonbonded = [f for f in forcefield._forces if isinstance(f, NonbondedGenerator)][0]
1144
+ radii = {'H':0.198, 'Li':0.203, 'C':0.340, 'N':0.325, 'O':0.299, 'F':0.312, 'Na':0.333, 'Mg':0.141,
1145
+ 'P':0.374, 'S':0.356, 'Cl':0.347, 'K':0.474, 'Br':0.396, 'Rb':0.527, 'I':0.419, 'Cs':0.605}
1146
+ else:
1147
+ forcefield = app.ForceField(os.path.join(os.path.dirname(__file__), 'soft.xml'))
1148
+
1149
+ # The Topology may contain residues for which the ForceField does not have a template.
1150
+ # If so, we need to create new templates for them.
1151
+
1152
+ atomTypes = {}
1153
+ bondedToAtom = []
1154
+ for atom in newTopology.atoms():
1155
+ bondedToAtom.append(set())
1156
+ for atom1, atom2 in newTopology.bonds():
1157
+ bondedToAtom[atom1.index].add(atom2.index)
1158
+ bondedToAtom[atom2.index].add(atom1.index)
1159
+ for residue in newTopology.residues():
1160
+
1161
+ # Make sure the ForceField has a template for this residue.
1162
+
1163
+ signature = app.forcefield._createResidueSignature([atom.element for atom in residue.atoms()])
1164
+ if signature in forcefield._templateSignatures:
1165
+ if any(matchResidue(residue, t, bondedToAtom) is not None for t in forcefield._templateSignatures[signature]):
1166
+ continue
1167
+
1168
+ # Create a new template.
1169
+
1170
+ resName = "extra_"+residue.name
1171
+ template = app.ForceField._TemplateData(resName)
1172
+ forcefield._templates[resName] = template
1173
+ indexInResidue = {}
1174
+ for atom in residue.atoms():
1175
+ element = atom.element
1176
+ typeName = 'extra_'+element.symbol
1177
+ if element not in atomTypes:
1178
+ atomTypes[element] = app.ForceField._AtomType(typeName, '', 0.0, element)
1179
+ forcefield._atomTypes[typeName] = atomTypes[element]
1180
+ if water:
1181
+ # Select a reasonable vdW radius for this atom type.
1182
+
1183
+ if element.symbol in radii:
1184
+ sigma = radii[element.symbol]
1185
+ else:
1186
+ sigma = 0.5
1187
+ nonbonded.registerAtom({'type':typeName, 'charge':'0', 'sigma':str(sigma), 'epsilon':'0'})
1188
+ indexInResidue[atom.index] = len(template.atoms)
1189
+ template.atoms.append(app.ForceField._TemplateAtomData(atom.name, typeName, element))
1190
+ for atom in residue.atoms():
1191
+ for bondedTo in bondedToAtom[atom.index]:
1192
+ if bondedTo in indexInResidue:
1193
+ b = (indexInResidue[atom.index], indexInResidue[bondedTo])
1194
+ if b[0] < b[1]:
1195
+ template.bonds.append(b)
1196
+ template.atoms[b[0]].bondedTo.append(b[1])
1197
+ template.atoms[b[1]].bondedTo.append(b[0])
1198
+ else:
1199
+ b = indexInResidue[atom.index]
1200
+ template.externalBonds.append(b)
1201
+ template.atoms[b].externalBonds += 1
1202
+ if signature in forcefield._templateSignatures:
1203
+ forcefield._templateSignatures[signature].append(template)
1204
+ else:
1205
+ forcefield._templateSignatures[signature] = [template]
1206
+ return forcefield
1207
+
1208
+ def _findNearestDistance(self, context, newAtoms, cutoff, exclusions):
1209
+ """Given a set of newly added atoms, find the closest distance between one of those atoms and another atom."""
1210
+
1211
+ positions = context.getState(getPositions=True).getPositions(asNumpy=True).value_in_unit(unit.nanometer)
1212
+ boxSize = np.max(positions, axis=0)-np.min(positions, axis=0)
1213
+ boxVectors = [(boxSize[0], 0, 0), (0, boxSize[1], 0), (0, 0, boxSize[2])]
1214
+ cells = app.modeller._CellList(positions, cutoff, boxVectors, False)
1215
+ nearest_squared = sys.float_info.max
1216
+ for atom in newAtoms:
1217
+ excluded = exclusions[atom]
1218
+ for i in cells.neighbors(positions[atom.index]):
1219
+ if i not in excluded:
1220
+ p = positions[atom.index]-positions[i]
1221
+ dist_squared = np.dot(p, p)
1222
+ if dist_squared < nearest_squared:
1223
+ nearest_squared = dist_squared
1224
+ return np.sqrt(nearest_squared)
1225
+
1226
+
1227
+ def main():
1228
+ if len(sys.argv) < 2:
1229
+ # Display the UI.
1230
+ from . import ui
1231
+ ui.launchUI()
1232
+ else:
1233
+ # Run in command line mode.
1234
+
1235
+ from optparse import OptionParser
1236
+ parser = OptionParser(usage="Usage: %prog\n %prog filename [options] \n\nWhen run with no arguments, it launches the user interface. If any arguments are specified, it runs in command line mode.")
1237
+ parser.add_option('--pdbid', default=None, dest='pdbid', metavar='PDBID', help='PDB id to retrieve from RCSB [default: None]')
1238
+ parser.add_option('--url', default=None, dest='url', metavar='URL', help='URL to retrieve PDB from [default: None]')
1239
+ parser.add_option('--output', default='output.pdb', dest='output', metavar='FILENAME', help='output pdb file [default: output.pdb]')
1240
+ parser.add_option('--add-atoms', default='all', dest='atoms', choices=('all', 'heavy', 'hydrogen', 'none'), help='which missing atoms to add: all, heavy, hydrogen, or none [default: all]')
1241
+ parser.add_option('--keep-heterogens', default='all', dest='heterogens', choices=('all', 'water', 'none'), metavar='OPTION', help='which heterogens to keep: all, water, or none [default: all]')
1242
+ parser.add_option('--replace-nonstandard', action='store_true', default=False, dest='nonstandard', help='replace nonstandard residues with standard equivalents')
1243
+ parser.add_option('--add-residues', action='store_true', default=False, dest='residues', help='add missing residues')
1244
+ parser.add_option('--water-box', dest='box', type='float', nargs=3, metavar='X Y Z', help='add a water box. The value is the box dimensions in nm [example: --water-box=2.5 2.4 3.0]')
1245
+ parser.add_option('--ph', type='float', default=7.0, dest='ph', help='the pH to use for adding missing hydrogens [default: 7.0]')
1246
+ parser.add_option('--positive-ion', default='Na+', dest='positiveIon', choices=('Cs+', 'K+', 'Li+', 'Na+', 'Rb+'), metavar='ION', help='positive ion to include in the water box: Cs+, K+, Li+, Na+, or Rb+ [default: Na+]')
1247
+ parser.add_option('--negative-ion', default='Cl-', dest='negativeIon', choices=('Cl-', 'Br-', 'F-', 'I-'), metavar='ION', help='negative ion to include in the water box: Cl-, Br-, F-, or I- [default: Cl-]')
1248
+ parser.add_option('--ionic-strength', type='float', default=0.0, dest='ionic', metavar='STRENGTH', help='molar concentration of ions to add to the water box [default: 0.0]')
1249
+ parser.add_option('--verbose', default=False, action='store_true', dest='verbose', metavar='VERBOSE', help='Print verbose output')
1250
+ (options, args) = parser.parse_args()
1251
+ if (len(args) == 0) and (options.pdbid==None) and (options.url==None):
1252
+ parser.error('No filename specified')
1253
+ if len(args) > 1:
1254
+ parser.error('Must specify a single filename or --pdbid or --url')
1255
+ if options.pdbid != None:
1256
+ if options.verbose: print('Retrieving PDB "' + options.pdbid + '" from RCSB...')
1257
+ fixer = PDBFixer(pdbid=options.pdbid)
1258
+ elif options.url != None:
1259
+ if options.verbose: print('Retrieving PDB from URL "' + options.url + '"...')
1260
+ fixer = PDBFixer(url=options.url)
1261
+ else:
1262
+ fixer = PDBFixer(filename=sys.argv[1])
1263
+ if options.residues:
1264
+ if options.verbose: print('Finding missing residues...')
1265
+ fixer.findMissingResidues()
1266
+ else:
1267
+ fixer.missingResidues = {}
1268
+ if options.nonstandard:
1269
+ if options.verbose: print('Finding nonstandard residues...')
1270
+ fixer.findNonstandardResidues()
1271
+ if options.verbose: print('Replacing nonstandard residues...')
1272
+ fixer.replaceNonstandardResidues()
1273
+ if options.heterogens == 'none':
1274
+ fixer.removeHeterogens(False)
1275
+ elif options.heterogens == 'water':
1276
+ fixer.removeHeterogens(True)
1277
+ if options.verbose: print('Finding missing atoms...')
1278
+ fixer.findMissingAtoms()
1279
+ if options.atoms not in ('all', 'heavy'):
1280
+ fixer.missingAtoms = {}
1281
+ fixer.missingTerminals = {}
1282
+ if options.verbose: print('Adding missing atoms...')
1283
+ fixer.addMissingAtoms()
1284
+ if options.atoms in ('all', 'hydrogen'):
1285
+ if options.verbose: print('Adding missing hydrogens...')
1286
+ fixer.addMissingHydrogens(options.ph)
1287
+ if options.box is not None:
1288
+ if options.verbose: print('Adding solvent...')
1289
+ fixer.addSolvent(boxSize=options.box*unit.nanometer, positiveIon=options.positiveIon,
1290
+ negativeIon=options.negativeIon, ionicStrength=options.ionic*unit.molar)
1291
+ with open(options.output, 'w') as f:
1292
+ if options.verbose: print('Writing output...')
1293
+ if fixer.source is not None:
1294
+ f.write("REMARK 1 PDBFIXER FROM: %s\n" % fixer.source)
1295
+ app.PDBFile.writeFile(fixer.topology, fixer.positions, f, True)
1296
+ if options.verbose: print('Done.')
1297
+
1298
+ if __name__ == '__main__':
1299
+ main()
pdbfixer/setup.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="pdbfixer",
5
+ version="1.9.0",
6
+ packages=find_packages(),
7
+ include_package_data=True,
8
+ package_data={
9
+ "pdbfixer": ["templates/*", "*.py", "**/*.py"],
10
+ },
11
+ install_requires=["numpy", "openmm>=7.1"],
12
+ )
pdbfixer/soft.xml ADDED
The diff for this file is too large to render. See raw diff
 
pdbfixer/templates/A.pdb ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 P A A 1 -2.114 -1.179 3.781 1.00 0.00
2
+ ATOM 2 OP1 A A 1 -1.822 -1.633 5.165 1.00 0.00
3
+ ATOM 3 OP2 A A 1 -3.263 -0.267 3.555 1.00 0.00
4
+ ATOM 4 O5' A A 1 -0.800 -0.568 3.129 1.00 0.00
5
+ ATOM 5 C5' A A 1 0.456 -1.202 3.303 1.00 0.00
6
+ ATOM 6 C4' A A 1 1.422 -0.825 2.211 1.00 0.00
7
+ ATOM 7 O4' A A 1 0.959 -1.348 0.941 1.00 0.00
8
+ ATOM 8 C3' A A 1 1.591 0.662 1.951 1.00 0.00
9
+ ATOM 9 O3' A A 1 2.443 1.298 2.889 1.00 0.00
10
+ ATOM 10 C2' A A 1 2.123 0.687 0.523 1.00 0.00
11
+ ATOM 11 O2' A A 1 3.512 0.395 0.500 1.00 0.00
12
+ ATOM 12 C1' A A 1 1.371 -0.491 -0.105 1.00 0.00
13
+ ATOM 13 N9 A A 1 0.184 -0.049 -0.863 1.00 0.00
14
+ ATOM 14 C8 A A 1 -1.080 0.283 -0.428 1.00 0.00
15
+ ATOM 15 N7 A A 1 -1.892 0.655 -1.393 1.00 0.00
16
+ ATOM 16 C5 A A 1 -1.105 0.569 -2.537 1.00 0.00
17
+ ATOM 17 C6 A A 1 -1.360 0.829 -3.900 1.00 0.00
18
+ ATOM 18 N6 A A 1 -2.535 1.248 -4.391 1.00 0.00
19
+ ATOM 19 N1 A A 1 -0.337 0.635 -4.764 1.00 0.00
20
+ ATOM 20 C2 A A 1 0.851 0.217 -4.302 1.00 0.00
21
+ ATOM 21 N3 A A 1 1.212 -0.058 -3.054 1.00 0.00
22
+ ATOM 22 C4 A A 1 0.177 0.144 -2.219 1.00 0.00
23
+ TER 23 A A 1
24
+ END
pdbfixer/templates/ACE.pdb ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ ATOM 1 C ACE A 1 0.336 0.267 -0.194 1.00 0.00
2
+ ATOM 2 O ACE A 1 -0.134 -0.667 -0.918 1.00 0.00
3
+ ATOM 3 CH3 ACE A 1 -0.201 0.401 1.112 1.00 0.00
4
+ TER 4 ACE A 1
5
+ END
pdbfixer/templates/ALA.pdb ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ ATOM 1 N ALA A 1 -1.444 -0.596 0.968 1.00 0.00
2
+ ATOM 2 CA ALA A 1 -0.194 -0.546 0.198 1.00 0.00
3
+ ATOM 3 CB ALA A 1 -0.584 -0.626 -1.282 1.00 0.00
4
+ ATOM 4 C ALA A 1 0.716 0.684 0.478 1.00 0.00
5
+ ATOM 5 O ALA A 1 1.506 1.084 -0.362 1.00 0.00
6
+ TER 6 ALA A 1
7
+ END
pdbfixer/templates/ARG.pdb ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N ARG A 1 -0.592 -1.491 -3.605 1.00 0.00
2
+ ATOM 2 CA ARG A 1 -0.082 -1.191 -2.255 1.00 0.00
3
+ ATOM 3 CB ARG A 1 -1.242 -0.801 -1.315 1.00 0.00
4
+ ATOM 4 CG ARG A 1 -0.782 -0.301 0.075 1.00 0.00
5
+ ATOM 5 CD ARG A 1 -0.102 1.079 0.035 1.00 0.00
6
+ ATOM 6 NE ARG A 1 0.598 1.429 1.295 1.00 0.00
7
+ ATOM 7 CZ ARG A 1 0.068 1.769 2.455 1.00 0.00
8
+ ATOM 8 NH1 ARG A 1 -1.212 1.659 2.695 1.00 0.00
9
+ ATOM 9 NH2 ARG A 1 0.808 2.279 3.405 1.00 0.00
10
+ ATOM 10 C ARG A 1 0.748 -2.351 -1.695 1.00 0.00
11
+ ATOM 11 O ARG A 1 1.788 -2.081 -1.095 1.00 0.00
12
+ TER 12 ARG A 1
13
+ END
pdbfixer/templates/ASN.pdb ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N ASN A 1 -0.065 -1.086 -1.454 1.00 0.00
2
+ ATOM 2 CA ASN A 1 0.635 -0.556 -0.284 1.00 0.00
3
+ ATOM 3 CB ASN A 1 -0.375 0.064 0.696 1.00 0.00
4
+ ATOM 4 CG ASN A 1 -1.175 1.204 0.086 1.00 0.00
5
+ ATOM 5 OD1 ASN A 1 -0.665 2.274 -0.194 1.00 0.00
6
+ ATOM 6 ND2 ASN A 1 -2.455 1.024 -0.124 1.00 0.00
7
+ ATOM 7 C ASN A 1 1.495 -1.616 0.426 1.00 0.00
8
+ ATOM 8 O ASN A 1 2.605 -1.306 0.846 1.00 0.00
9
+ TER 9 ASN A 1
10
+ END
pdbfixer/templates/ASP.pdb ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N ASP A 1 -0.296 1.560 1.112 1.00 0.00
2
+ ATOM 2 CA ASP A 1 0.554 0.380 1.103 1.00 0.00
3
+ ATOM 3 CB ASP A 1 -0.246 -0.830 0.562 1.00 0.00
4
+ ATOM 4 CG ASP A 1 -1.176 -0.590 -0.648 1.00 0.00
5
+ ATOM 5 OD1 ASP A 1 -0.926 0.310 -1.478 1.00 0.00
6
+ ATOM 6 OD2 ASP A 1 -2.206 -1.310 -0.667 1.00 0.00
7
+ ATOM 7 C ASP A 1 1.894 0.660 0.383 1.00 0.00
8
+ ATOM 8 O ASP A 1 2.404 -0.180 -0.368 1.00 0.00
9
+ TER 9 ASP A 1
10
+ END
pdbfixer/templates/C.pdb ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 P C A 1 -2.563 1.193 2.810 1.00 0.00
2
+ ATOM 2 OP1 C A 1 -2.884 2.401 2.011 1.00 0.00
3
+ ATOM 3 OP2 C A 1 -2.477 1.317 4.283 1.00 0.00
4
+ ATOM 4 O5' C A 1 -1.243 0.497 2.246 1.00 0.00
5
+ ATOM 5 C5' C A 1 -0.850 -0.791 2.702 1.00 0.00
6
+ ATOM 6 C4' C A 1 0.199 -1.413 1.809 1.00 0.00
7
+ ATOM 7 O4' C A 1 -0.344 -1.649 0.484 1.00 0.00
8
+ ATOM 8 C3' C A 1 1.442 -0.581 1.550 1.00 0.00
9
+ ATOM 9 O3' C A 1 2.367 -0.613 2.625 1.00 0.00
10
+ ATOM 10 C2' C A 1 1.975 -1.189 0.255 1.00 0.00
11
+ ATOM 11 O2' C A 1 2.641 -2.418 0.508 1.00 0.00
12
+ ATOM 12 C1' C A 1 0.675 -1.498 -0.484 1.00 0.00
13
+ ATOM 13 N1 C A 1 0.289 -0.415 -1.415 1.00 0.00
14
+ ATOM 14 C2 C A 1 0.999 -0.291 -2.603 1.00 0.00
15
+ ATOM 15 O2 C A 1 1.926 -1.086 -2.824 1.00 0.00
16
+ ATOM 16 N3 C A 1 0.659 0.687 -3.476 1.00 0.00
17
+ ATOM 17 C4 C A 1 -0.344 1.523 -3.212 1.00 0.00
18
+ ATOM 18 N4 C A 1 -0.627 2.463 -4.117 1.00 0.00
19
+ ATOM 19 C5 C A 1 -1.092 1.422 -2.008 1.00 0.00
20
+ ATOM 20 C6 C A 1 -0.741 0.447 -1.154 1.00 0.00
21
+ TER 21 C A 1
22
+ END
pdbfixer/templates/CYS.pdb ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N CYS A 1 0.155 0.347 1.678 1.00 0.00
2
+ ATOM 2 CA CYS A 1 -0.145 0.107 0.268 1.00 0.00
3
+ ATOM 3 CB CYS A 1 1.135 0.017 -0.562 1.00 0.00
4
+ ATOM 4 SG CYS A 1 1.895 1.667 -0.722 1.00 0.00
5
+ ATOM 5 C CYS A 1 -1.045 -1.123 0.048 1.00 0.00
6
+ ATOM 6 O CYS A 1 -1.995 -1.013 -0.712 1.00 0.00
7
+ TER 7 CYS A 1
8
+ END
pdbfixer/templates/DA.pdb ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 P DA A 1 3.455 -3.048 -0.447 1.00 0.00
2
+ ATOM 2 OP1 DA A 1 3.367 -2.804 -1.906 1.00 0.00
3
+ ATOM 3 OP2 DA A 1 3.853 -4.386 0.047 1.00 0.00
4
+ ATOM 4 O5' DA A 1 2.057 -2.670 0.231 1.00 0.00
5
+ ATOM 5 C5' DA A 1 1.984 -2.545 1.646 1.00 0.00
6
+ ATOM 6 C4' DA A 1 0.732 -1.793 2.062 1.00 0.00
7
+ ATOM 7 O4' DA A 1 0.860 -0.386 1.748 1.00 0.00
8
+ ATOM 8 C3' DA A 1 -0.540 -2.217 1.345 1.00 0.00
9
+ ATOM 9 O3' DA A 1 -1.083 -3.380 1.961 1.00 0.00
10
+ ATOM 10 C2' DA A 1 -1.428 -0.996 1.557 1.00 0.00
11
+ ATOM 11 C1' DA A 1 -0.430 0.158 1.535 1.00 0.00
12
+ ATOM 12 N9 DA A 1 -0.431 0.892 0.273 1.00 0.00
13
+ ATOM 13 C8 DA A 1 0.420 0.716 -0.781 1.00 0.00
14
+ ATOM 14 N7 DA A 1 0.190 1.518 -1.793 1.00 0.00
15
+ ATOM 15 C5 DA A 1 -0.891 2.276 -1.375 1.00 0.00
16
+ ATOM 16 C6 DA A 1 -1.617 3.311 -1.997 1.00 0.00
17
+ ATOM 17 N6 DA A 1 -1.340 3.769 -3.223 1.00 0.00
18
+ ATOM 18 N1 DA A 1 -2.641 3.854 -1.304 1.00 0.00
19
+ ATOM 19 C2 DA A 1 -2.916 3.391 -0.077 1.00 0.00
20
+ ATOM 20 N3 DA A 1 -2.303 2.427 0.609 1.00 0.00
21
+ ATOM 21 C4 DA A 1 -1.289 1.903 -0.103 1.00 0.00
22
+ TER 22 DA A 1
23
+ END
pdbfixer/templates/DC.pdb ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 P DC A 1 0.295 -2.226 -3.343 1.00 0.00
2
+ ATOM 2 OP1 DC A 1 1.043 -3.343 -3.965 1.00 0.00
3
+ ATOM 3 OP2 DC A 1 -1.169 -2.387 -3.225 1.00 0.00
4
+ ATOM 4 O5' DC A 1 0.883 -1.865 -1.892 1.00 0.00
5
+ ATOM 5 C5' DC A 1 2.189 -1.294 -1.719 1.00 0.00
6
+ ATOM 6 C4' DC A 1 2.331 -0.489 -0.428 1.00 0.00
7
+ ATOM 7 O4' DC A 1 1.384 0.603 -0.437 1.00 0.00
8
+ ATOM 8 C3' DC A 1 2.062 -1.218 0.887 1.00 0.00
9
+ ATOM 9 O3' DC A 1 3.255 -1.815 1.397 1.00 0.00
10
+ ATOM 10 C2' DC A 1 1.590 -0.107 1.824 1.00 0.00
11
+ ATOM 11 C1' DC A 1 1.067 0.976 0.890 1.00 0.00
12
+ ATOM 12 N1 DC A 1 -0.402 1.178 0.991 1.00 0.00
13
+ ATOM 13 C2 DC A 1 -0.894 2.298 1.673 1.00 0.00
14
+ ATOM 14 O2 DC A 1 -0.091 3.098 2.179 1.00 0.00
15
+ ATOM 15 N3 DC A 1 -2.241 2.473 1.753 1.00 0.00
16
+ ATOM 16 C4 DC A 1 -3.071 1.588 1.193 1.00 0.00
17
+ ATOM 17 N4 DC A 1 -4.387 1.805 1.302 1.00 0.00
18
+ ATOM 18 C5 DC A 1 -2.582 0.441 0.498 1.00 0.00
19
+ ATOM 19 C6 DC A 1 -1.255 0.277 0.423 1.00 0.00
20
+ TER 20 DC A 1
21
+ END
pdbfixer/templates/DG.pdb ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 P DG A 1 1.161 1.329 -4.498 1.00 0.00
2
+ ATOM 2 OP1 DG A 1 1.796 0.851 -5.745 1.00 0.00
3
+ ATOM 3 OP2 DG A 1 -0.048 2.176 -4.567 1.00 0.00
4
+ ATOM 4 O5' DG A 1 0.805 0.084 -3.560 1.00 0.00
5
+ ATOM 5 C5' DG A 1 1.762 -0.946 -3.341 1.00 0.00
6
+ ATOM 6 C4' DG A 1 1.846 -1.326 -1.871 1.00 0.00
7
+ ATOM 7 O4' DG A 1 1.636 -0.145 -1.058 1.00 0.00
8
+ ATOM 8 C3' DG A 1 0.791 -2.290 -1.343 1.00 0.00
9
+ ATOM 9 O3' DG A 1 1.106 -3.631 -1.683 1.00 0.00
10
+ ATOM 10 C2' DG A 1 0.949 -2.063 0.154 1.00 0.00
11
+ ATOM 11 C1' DG A 1 1.187 -0.555 0.219 1.00 0.00
12
+ ATOM 12 N9 DG A 1 -0.028 0.159 0.606 1.00 0.00
13
+ ATOM 13 C8 DG A 1 -0.940 0.835 -0.171 1.00 0.00
14
+ ATOM 14 N7 DG A 1 -1.931 1.350 0.512 1.00 0.00
15
+ ATOM 15 C5 DG A 1 -1.657 0.985 1.826 1.00 0.00
16
+ ATOM 16 C6 DG A 1 -2.358 1.244 3.030 1.00 0.00
17
+ ATOM 17 O6 DG A 1 -3.413 1.875 3.205 1.00 0.00
18
+ ATOM 18 N1 DG A 1 -1.704 0.676 4.123 1.00 0.00
19
+ ATOM 19 C2 DG A 1 -0.533 -0.046 4.077 1.00 0.00
20
+ ATOM 20 N2 DG A 1 -0.051 -0.517 5.237 1.00 0.00
21
+ ATOM 21 N3 DG A 1 0.126 -0.294 2.961 1.00 0.00
22
+ ATOM 22 C4 DG A 1 -0.493 0.251 1.889 1.00 0.00
23
+ TER 23 DG A 1
24
+ END
pdbfixer/templates/DT.pdb ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 P DT A 1 -1.137 0.745 4.665 1.00 0.00
2
+ ATOM 2 OP1 DT A 1 -2.403 1.211 5.278 1.00 0.00
3
+ ATOM 3 OP2 DT A 1 -0.705 -0.655 4.875 1.00 0.00
4
+ ATOM 4 O5' DT A 1 -1.190 1.013 3.082 1.00 0.00
5
+ ATOM 5 C5' DT A 1 -1.958 0.196 2.186 1.00 0.00
6
+ ATOM 6 C4' DT A 1 -2.031 0.805 0.789 1.00 0.00
7
+ ATOM 7 O4' DT A 1 -0.707 0.882 0.198 1.00 0.00
8
+ ATOM 8 C3' DT A 1 -2.890 0.053 -0.226 1.00 0.00
9
+ ATOM 9 O3' DT A 1 -3.701 0.978 -0.945 1.00 0.00
10
+ ATOM 10 C2' DT A 1 -1.866 -0.623 -1.137 1.00 0.00
11
+ ATOM 11 C1' DT A 1 -0.766 0.430 -1.140 1.00 0.00
12
+ ATOM 12 N1 DT A 1 0.603 -0.035 -1.562 1.00 0.00
13
+ ATOM 13 C2 DT A 1 0.971 0.054 -2.892 1.00 0.00
14
+ ATOM 14 O2 DT A 1 0.235 0.484 -3.765 1.00 0.00
15
+ ATOM 15 N3 DT A 1 2.244 -0.389 -3.167 1.00 0.00
16
+ ATOM 16 C4 DT A 1 3.169 -0.897 -2.270 1.00 0.00
17
+ ATOM 17 O4 DT A 1 4.288 -1.269 -2.619 1.00 0.00
18
+ ATOM 18 C5 DT A 1 2.725 -0.959 -0.899 1.00 0.00
19
+ ATOM 19 C7 DT A 1 3.643 -1.492 0.163 1.00 0.00
20
+ ATOM 20 C6 DT A 1 1.484 -0.529 -0.617 1.00 0.00
21
+ TER 21 DT A 1
22
+ END
pdbfixer/templates/G.pdb ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 P G A 1 -3.890 3.026 0.738 1.00 0.00
2
+ ATOM 2 OP1 G A 1 -3.347 3.996 -0.254 1.00 0.00
3
+ ATOM 3 OP2 G A 1 -4.455 3.555 2.014 1.00 0.00
4
+ ATOM 4 O5' G A 1 -2.795 1.882 1.048 1.00 0.00
5
+ ATOM 5 C5' G A 1 -2.835 1.119 2.259 1.00 0.00
6
+ ATOM 6 C4' G A 1 -1.720 0.091 2.340 1.00 0.00
7
+ ATOM 7 O4' G A 1 -1.679 -0.704 1.121 1.00 0.00
8
+ ATOM 8 C3' G A 1 -0.295 0.626 2.465 1.00 0.00
9
+ ATOM 9 O3' G A 1 0.046 1.086 3.774 1.00 0.00
10
+ ATOM 10 C2' G A 1 0.547 -0.561 1.985 1.00 0.00
11
+ ATOM 11 O2' G A 1 0.709 -1.536 3.011 1.00 0.00
12
+ ATOM 12 C1' G A 1 -0.344 -1.143 0.879 1.00 0.00
13
+ ATOM 13 N9 G A 1 0.102 -0.691 -0.466 1.00 0.00
14
+ ATOM 14 C8 G A 1 -0.447 0.259 -1.306 1.00 0.00
15
+ ATOM 15 N7 G A 1 0.226 0.442 -2.421 1.00 0.00
16
+ ATOM 16 C5 G A 1 1.309 -0.426 -2.320 1.00 0.00
17
+ ATOM 17 C6 G A 1 2.388 -0.666 -3.222 1.00 0.00
18
+ ATOM 18 O6 G A 1 2.613 -0.146 -4.319 1.00 0.00
19
+ ATOM 19 N1 G A 1 3.267 -1.620 -2.739 1.00 0.00
20
+ ATOM 20 C2 G A 1 3.122 -2.258 -1.538 1.00 0.00
21
+ ATOM 21 N2 G A 1 4.078 -3.144 -1.251 1.00 0.00
22
+ ATOM 22 N3 G A 1 2.135 -2.053 -0.678 1.00 0.00
23
+ ATOM 23 C4 G A 1 1.254 -1.125 -1.124 1.00 0.00
24
+ TER 24 G A 1
25
+ END
pdbfixer/templates/GLN.pdb ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N GLN A 1 -0.269 -2.647 0.620 1.00 0.00
2
+ ATOM 2 CA GLN A 1 0.731 -1.587 0.440 1.00 0.00
3
+ ATOM 3 CB GLN A 1 0.271 -0.507 -0.570 1.00 0.00
4
+ ATOM 4 CG GLN A 1 -0.889 0.383 -0.120 1.00 0.00
5
+ ATOM 5 CD GLN A 1 -1.129 1.583 -1.050 1.00 0.00
6
+ ATOM 6 OE1 GLN A 1 -1.969 1.563 -1.940 1.00 0.00
7
+ ATOM 7 NE2 GLN A 1 -0.409 2.673 -0.880 1.00 0.00
8
+ ATOM 8 C GLN A 1 1.271 -0.987 1.750 1.00 0.00
9
+ ATOM 9 O GLN A 1 2.391 -0.477 1.750 1.00 0.00
10
+ TER 10 GLN A 1
11
+ END
pdbfixer/templates/GLU.pdb ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N GLU A 1 -1.989 -1.946 0.778 1.00 0.00
2
+ ATOM 2 CA GLU A 1 -0.599 -1.546 0.468 1.00 0.00
3
+ ATOM 3 CB GLU A 1 -0.599 -0.126 -0.142 1.00 0.00
4
+ ATOM 4 CG GLU A 1 0.761 0.324 -0.712 1.00 0.00
5
+ ATOM 5 CD GLU A 1 0.731 1.684 -1.442 1.00 0.00
6
+ ATOM 6 OE1 GLU A 1 1.141 2.704 -0.822 1.00 0.00
7
+ ATOM 7 OE2 GLU A 1 0.371 1.724 -2.642 1.00 0.00
8
+ ATOM 8 C GLU A 1 0.301 -1.656 1.718 1.00 0.00
9
+ ATOM 9 O GLU A 1 -0.119 -1.166 2.798 1.00 0.00
10
+ TER 10 GLU A 1
11
+ END
pdbfixer/templates/GLY.pdb ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ ATOM 1 N GLY A 1 -1.452 -0.970 -0.205 1.00 0.00
2
+ ATOM 2 CA GLY A 1 -0.522 0.060 -0.665 1.00 0.00
3
+ ATOM 3 C GLY A 1 0.388 0.570 0.455 1.00 0.00
4
+ ATOM 4 O GLY A 1 1.587 0.340 0.415 1.00 0.00
5
+ TER 5 GLY A 1
6
+ END
pdbfixer/templates/HIS.pdb ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N HIS A 1 -0.532 -1.545 -1.177 1.00 0.00
2
+ ATOM 2 CA HIS A 1 0.668 -0.725 -1.257 1.00 0.00
3
+ ATOM 3 CB HIS A 1 0.958 -0.235 0.173 1.00 0.00
4
+ ATOM 4 CG HIS A 1 -0.222 0.485 0.783 1.00 0.00
5
+ ATOM 5 ND1 HIS A 1 -1.102 -0.065 1.713 1.00 0.00
6
+ ATOM 6 CE1 HIS A 1 -2.082 0.835 1.883 1.00 0.00
7
+ ATOM 7 NE2 HIS A 1 -1.862 1.915 1.113 1.00 0.00
8
+ ATOM 8 CD2 HIS A 1 -0.682 1.725 0.413 1.00 0.00
9
+ ATOM 9 C HIS A 1 1.868 -1.425 -1.927 1.00 0.00
10
+ ATOM 10 O HIS A 1 2.988 -0.965 -1.717 1.00 0.00
11
+ TER 11 HIS A 1
12
+ END
pdbfixer/templates/ILE.pdb ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N ILE A 1 -0.264 1.893 -0.670 1.00 0.00
2
+ ATOM 2 CA ILE A 1 0.446 0.733 -0.110 1.00 0.00
3
+ ATOM 3 CB ILE A 1 -0.494 -0.498 -0.030 1.00 0.00
4
+ ATOM 4 CG2 ILE A 1 0.276 -1.757 0.420 1.00 0.00
5
+ ATOM 5 CG1 ILE A 1 -1.674 -0.238 0.930 1.00 0.00
6
+ ATOM 6 CD1 ILE A 1 -2.864 -1.177 0.730 1.00 0.00
7
+ ATOM 7 C ILE A 1 1.736 0.483 -0.910 1.00 0.00
8
+ ATOM 8 O ILE A 1 2.836 0.562 -0.360 1.00 0.00
9
+ TER 9 ILE A 1
10
+ END
pdbfixer/templates/LEU.pdb ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N LEU A 1 0.876 0.196 -1.980 1.00 0.00
2
+ ATOM 2 CA LEU A 1 0.876 -0.194 -0.560 1.00 0.00
3
+ ATOM 3 CB LEU A 1 -0.574 -0.304 -0.040 1.00 0.00
4
+ ATOM 4 CG LEU A 1 -1.294 1.036 0.190 1.00 0.00
5
+ ATOM 5 CD1 LEU A 1 -2.774 0.796 0.500 1.00 0.00
6
+ ATOM 6 CD2 LEU A 1 -0.694 1.786 1.380 1.00 0.00
7
+ ATOM 7 C LEU A 1 1.616 -1.514 -0.320 1.00 0.00
8
+ ATOM 8 O LEU A 1 1.966 -1.804 0.830 1.00 0.00
9
+ TER 9 LEU A 1
10
+ END
pdbfixer/templates/LYS.pdb ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N LYS A 1 -0.320 -2.683 0.169 1.00 0.00
2
+ ATOM 2 CA LYS A 1 0.500 -1.623 0.779 1.00 0.00
3
+ ATOM 3 CB LYS A 1 -0.150 -0.253 0.479 1.00 0.00
4
+ ATOM 4 CG LYS A 1 -0.110 0.217 -0.971 1.00 0.00
5
+ ATOM 5 CD LYS A 1 -0.990 1.467 -1.121 1.00 0.00
6
+ ATOM 6 CE LYS A 1 -0.730 2.197 -2.431 1.00 0.00
7
+ ATOM 7 NZ LYS A 1 0.120 3.387 -2.231 1.00 0.00
8
+ ATOM 8 C LYS A 1 0.640 -1.813 2.309 1.00 0.00
9
+ ATOM 9 O LYS A 1 1.040 -0.893 3.019 1.00 0.00
10
+ TER 10 LYS A 1
11
+ END
pdbfixer/templates/MET.pdb ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N MET A 1 -0.356 2.184 0.403 1.00 0.00
2
+ ATOM 2 CA MET A 1 0.594 1.064 0.333 1.00 0.00
3
+ ATOM 3 CB MET A 1 -0.076 -0.216 -0.208 1.00 0.00
4
+ ATOM 4 CG MET A 1 -1.146 -0.756 0.753 1.00 0.00
5
+ ATOM 5 SD MET A 1 -1.606 -2.486 0.483 1.00 0.00
6
+ ATOM 6 CE MET A 1 -2.216 -2.456 -1.228 1.00 0.00
7
+ ATOM 7 C MET A 1 1.844 1.434 -0.497 1.00 0.00
8
+ ATOM 8 O MET A 1 2.964 1.234 -0.038 1.00 0.00
9
+ TER 9 MET A 1
10
+ END
pdbfixer/templates/NME.pdb ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ ATOM 1 N NME A 1 -0.237 0.562 -0.354 1.00 0.00
2
+ ATOM 2 C NME A 1 0.237 -0.562 0.354 1.00 0.00
3
+ TER 3 NME A 1
4
+ END
pdbfixer/templates/PHE.pdb ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N PHE A 1 0.069 -0.545 3.506 1.00 0.00
2
+ ATOM 2 CA PHE A 1 -0.321 -0.545 2.096 1.00 0.00
3
+ ATOM 3 CB PHE A 1 0.809 0.085 1.266 1.00 0.00
4
+ ATOM 4 CG PHE A 1 0.519 0.445 -0.194 1.00 0.00
5
+ ATOM 5 CD1 PHE A 1 -0.791 0.565 -0.704 1.00 0.00
6
+ ATOM 6 CE1 PHE A 1 -1.001 0.935 -2.044 1.00 0.00
7
+ ATOM 7 CZ PHE A 1 0.099 1.185 -2.884 1.00 0.00
8
+ ATOM 8 CE2 PHE A 1 1.399 1.085 -2.374 1.00 0.00
9
+ ATOM 9 CD2 PHE A 1 1.609 0.715 -1.044 1.00 0.00
10
+ ATOM 10 C PHE A 1 -0.721 -1.925 1.566 1.00 0.00
11
+ ATOM 11 O PHE A 1 -1.671 -1.995 0.806 1.00 0.00
12
+ TER 12 PHE A 1
13
+ END
pdbfixer/templates/PRO.pdb ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N PRO A 1 1.094 0.154 -0.780 1.00 0.00
2
+ ATOM 2 CD PRO A 1 1.964 0.224 0.390 1.00 0.00
3
+ ATOM 3 CG PRO A 1 1.284 -0.636 1.450 1.00 0.00
4
+ ATOM 4 CB PRO A 1 -0.196 -0.386 1.170 1.00 0.00
5
+ ATOM 5 CA PRO A 1 -0.256 -0.246 -0.350 1.00 0.00
6
+ ATOM 6 C PRO A 1 -1.406 0.684 -0.790 1.00 0.00
7
+ ATOM 7 O PRO A 1 -2.486 0.204 -1.090 1.00 0.00
8
+ TER 8 PRO A 1
9
+ END
pdbfixer/templates/SER.pdb ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N SER A 1 -1.192 -0.063 1.270 1.00 0.00
2
+ ATOM 2 CA SER A 1 -0.052 -0.153 0.330 1.00 0.00
3
+ ATOM 3 CB SER A 1 -0.512 -0.243 -1.130 1.00 0.00
4
+ ATOM 4 OG SER A 1 -1.332 -1.373 -1.370 1.00 0.00
5
+ ATOM 5 C SER A 1 0.938 1.017 0.460 1.00 0.00
6
+ ATOM 6 O SER A 1 2.148 0.817 0.440 1.00 0.00
7
+ TER 7 SER A 1
8
+ END
pdbfixer/templates/THR.pdb ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ ATOM 1 N THR A 1 -1.233 0.087 1.536 1.00 0.00
2
+ ATOM 2 CA THR A 1 -0.113 -0.063 0.586 1.00 0.00
3
+ ATOM 3 CB THR A 1 -0.633 -0.183 -0.854 1.00 0.00
4
+ ATOM 4 CG2 THR A 1 0.457 -0.443 -1.884 1.00 0.00
5
+ ATOM 5 OG1 THR A 1 -1.493 -1.293 -0.954 1.00 0.00
6
+ ATOM 6 C THR A 1 0.907 1.067 0.716 1.00 0.00
7
+ ATOM 7 O THR A 1 2.107 0.827 0.856 1.00 0.00
8
+ TER 8 THR A 1
9
+ END