@@ -84,19 +84,12 @@ def _check_chain_and_segid(mol, verbose):
84
84
85
85
86
86
def _generate_nonstandard_residues_ff (
87
- mol ,
88
- definition ,
89
- forcefield ,
90
- _molkit_ff = True ,
91
- outdir = None ,
92
- ignore_ns_errors = False ,
93
- residue_smiles = None ,
87
+ mol , definition , forcefield , _molkit_ff = True , outdir = None , residue_smiles = None
94
88
):
95
89
import tempfile
96
90
from moleculekit .tools .preparation_customres import _get_custom_ff
97
91
from moleculekit .tools .preparation_customres import (
98
92
_process_custom_residue ,
99
- _template_residue_from_smiles ,
100
93
_mol_to_dat_def ,
101
94
_mol_to_xml_def ,
102
95
_prepare_for_parameterize ,
@@ -111,64 +104,61 @@ def _generate_nonstandard_residues_ff(
111
104
if len (not_in_ff ) == 0 :
112
105
return definition , forcefield
113
106
114
- try :
115
- from aceprep .prepare import rdk_prepare
116
- except ImportError :
117
- if ignore_ns_errors :
118
- return definition , forcefield
107
+ residue_smiles = residue_smiles or {}
108
+ missing = np .setdiff1d (not_in_ff , list (residue_smiles .keys ()))
109
+ if len (missing ):
119
110
raise RuntimeError (
120
- "To protonate non-canonical aminoacids you need the aceprep library. Please contact Acellera info@acellera.com for more information or set ignore_ns_errors=True to ignore non-canonical residues in the protonation (this will leave the residues unprotonated)."
111
+ f"Missing topology for residues { missing } . "
112
+ "Please provide their SMILES in the residue_smiles dictionary or remove them from the input structure."
121
113
)
122
114
123
115
with tempfile .TemporaryDirectory () as tmpdir :
124
116
for res in not_in_ff :
125
- try :
126
- logger .info (f"Attempting to template non-canonical residue { res } ..." )
127
- # This removes the non-canonical hydrogens from the original mol object
128
- mol .remove ((mol .resname == res ) & (mol .element == "H" ), _logger = False )
129
- molc = mol .copy ()
130
-
131
- # Hacky way of getting the first molecule, if there are copies
132
- molresn = molc .resname == res
133
- firstname = molc .name [molresn ][0 ]
134
- lastname = molc .name [molresn ][- 1 ]
135
- start = np .where (molresn & (molc .name == firstname ))[0 ][0 ]
136
- end = np .where (molresn & (molc .name == lastname ))[0 ][0 ]
137
- # Remove all other stuff
138
- molc .filter (f"index { start } to { end } " , _logger = False )
139
- molc .guessBonds ()
140
-
141
- if len (np .unique (molc .name )) != molc .numAtoms :
142
- raise RuntimeError (
143
- f"Residue { res } contains duplicate atom names. Please rename the atoms to have unique names."
144
- )
145
-
146
- smiles = None
147
- if residue_smiles is not None and res in residue_smiles :
148
- smiles = residue_smiles [res ]
149
-
150
- if smiles is not None and os .path .isfile (smiles ):
151
- tmol = Molecule (smiles )
152
- else :
153
- tmol = _template_residue_from_smiles (molc , res , smiles = smiles )
154
- cres = _process_custom_residue (tmol )
155
- # Rename to correct resname
156
- cres .resname [:] = res
157
-
158
- _mol_to_xml_def (cres , os .path .join (tmpdir , f"{ res } .xml" ))
159
- _mol_to_dat_def (cres , os .path .join (tmpdir , f"{ res } .dat" ))
160
- if outdir is not None :
161
- os .makedirs (outdir , exist_ok = True )
162
- pres = _prepare_for_parameterize (cres )
163
- pres .write (os .path .join (outdir , f"{ res } .cif" ))
164
- logger .info (f"Succesfully templated non-canonical residue { res } ." )
165
- except Exception as e :
166
- import traceback
117
+ logger .info (f"Attempting to template non-canonical residue { res } ..." )
118
+ # This removes the non-canonical hydrogens from the original mol object
119
+ mol .remove ((mol .resname == res ) & (mol .element == "H" ), _logger = False )
120
+ molc = mol .copy ()
121
+
122
+ # Hacky way of getting the first molecule, if there are copies
123
+ molresn = molc .resname == res
124
+ firstname = molc .name [molresn ][0 ]
125
+ lastname = molc .name [molresn ][- 1 ]
126
+ start = np .where (molresn & (molc .name == firstname ))[0 ][0 ]
127
+ end = np .where (molresn & (molc .name == lastname ))[0 ][0 ]
128
+ # Remove all other stuff
129
+ molc .filter (f"index { start } to { end } " , _logger = False )
130
+ molc .guessBonds ()
131
+
132
+ if len (np .unique (molc .name )) != molc .numAtoms :
133
+ raise RuntimeError (
134
+ f"Residue { res } contains duplicate atom names. Please rename the atoms to have unique names."
135
+ )
167
136
168
- traceback .print_exc ()
137
+ smiles = None
138
+ if residue_smiles is not None and res in residue_smiles :
139
+ smiles = residue_smiles [res ]
140
+ if smiles is None :
169
141
raise RuntimeError (
170
- f"Failed to protonate non-canonical residue { res } . Please remove it from the protein or mutate it to continue preparation. Detailed error message: { e } "
142
+ f"Residue { res } is not in the residue_smiles dictionary . Please add it to the dictionary or remove it from the protein. "
171
143
)
144
+
145
+ if os .path .isfile (smiles ):
146
+ molc = Molecule (smiles )
147
+ else :
148
+ molc .templateResidueFromSmiles ("all" , smiles , addHs = True )
149
+
150
+ cres = _process_custom_residue (molc )
151
+ # Rename to correct resname
152
+ cres .resname [:] = res
153
+
154
+ _mol_to_xml_def (cres , os .path .join (tmpdir , f"{ res } .xml" ))
155
+ _mol_to_dat_def (cres , os .path .join (tmpdir , f"{ res } .dat" ))
156
+ if outdir is not None :
157
+ os .makedirs (outdir , exist_ok = True )
158
+ pres = _prepare_for_parameterize (cres )
159
+ pres .write (os .path .join (outdir , f"{ res } .cif" ))
160
+ logger .info (f"Succesfully templated non-canonical residue { res } ." )
161
+
172
162
definition , forcefield = _get_custom_ff (user_ff = tmpdir , molkit_ff = _molkit_ff )
173
163
return definition , forcefield
174
164
@@ -542,7 +532,6 @@ def systemPrepare(
542
532
return_details = False ,
543
533
hydrophobic_thickness = None ,
544
534
plot_pka = None ,
545
- ignore_ns_errors = False ,
546
535
_logger_level = "ERROR" ,
547
536
_molkit_ff = True ,
548
537
outdir = None ,
@@ -640,14 +629,14 @@ def systemPrepare(
640
629
ignoring the covalent bond, meaning it may break the bonds or add hydrogen atoms between the bonds.
641
630
plot_pka : str
642
631
Provide a file path with .png extension to draw the titration diagram for the system residues.
643
- ignore_ns_errors : bool
644
- If False systemPrepare will issue an error when it fails to protonate non-canonical residues in the protein.
645
- If True it will ignore errors on non-canonical residues leaving them unprotonated.
646
632
outdir : str
647
633
A path where to save custom residue cif files used for building
648
634
residue_smiles : dict
649
635
A dictionary with keys being residue names and values being the SMILES string of the residue. This is used to
650
- create protonated versions of non-canonical residues with the help of the aceprep library.
636
+ create protonated versions of non-canonical residues.
637
+ ignore_ns : bool
638
+ If False systemPrepare will issue an error when it fails to protonate non-canonical residues in the protein.
639
+ If True it will leave non-canonical residues unprotonated.
651
640
652
641
Returns
653
642
-------
@@ -745,7 +734,6 @@ def systemPrepare(
745
734
forcefield ,
746
735
_molkit_ff ,
747
736
outdir ,
748
- ignore_ns_errors = ignore_ns_errors ,
749
737
residue_smiles = residue_smiles ,
750
738
)
751
739
0 commit comments