@@ -135,19 +135,35 @@ def __init__(
135135
136136 if multiple_chains != None :
137137 self .universe_start .add_TopologyAttr ("chainID" )
138-
138+ offset = 0
139139 for r , res in enumerate (
140140 self .universe_start .select_atoms ("protein" ).residues
141141 ):
142- for atom in res .atoms :
143- atom .chainID = multiple_chains [r ]
142+ if res .atoms [0 ].resname == "ACE" or res .atoms [0 ].resname == "NME" :
143+ for atom in res .atoms :
144+ atom .chainID = 'X'
145+ offset += 1
146+ else :
147+ for atom in res .atoms :
148+ atom .chainID = multiple_chains [r - offset ]
144149
145150 self .universe_target .add_TopologyAttr ("chainID" )
151+ offset = 0
146152 for r , res in enumerate (
147153 self .universe_target .select_atoms ("protein" ).residues
148154 ):
149- for atom in res .atoms :
150- atom .chainID = multiple_chains [r ]
155+ if res .atoms [0 ].resname == "ACE" or res .atoms [0 ].resname == "NME" :
156+ for atom in res .atoms :
157+ atom .chainID = 'X'
158+ offset += 1
159+ continue
160+ else :
161+ for atom in res .atoms :
162+ atom .chainID = multiple_chains [r - offset ]
163+
164+ # now remove the X chains from the universe
165+ self .universe_start = mda .Merge (self .universe_start .select_atoms ("not chainid X" ))
166+ self .universe_target = mda .Merge (self .universe_target .select_atoms ("not chainid X" ))
151167
152168 # Create working directory if necessary
153169 os .makedirs (working_dir , exist_ok = True )
@@ -220,7 +236,7 @@ def morph(
220236 else :
221237 self .universe_target .atoms .write (join (local_path , "target.pdb" ))
222238
223- # Interpolate coordinates, (1-l)* original + l*(target-original )
239+ # Interpolate coordinates, (1-l)* original + l*(target)
224240 for n , l in enumerate (np .linspace (0 , 1 , number_of_intermediates )):
225241 intermediate_universe = self .universe_start .copy ()
226242
@@ -275,6 +291,7 @@ def make_models(
275291 include_residues = None ,
276292 poolsize = 12 ,
277293 mutagenesis = None ,
294+ disulphide_patches = None ,
278295 ):
279296 """Use the modeller package to generate fixed models based on the morphs already
280297 present in the folder structure. Caps are removed at this stage. Mutagenesis can be
@@ -288,6 +305,10 @@ def make_models(
288305 :type poolsize: int, optional
289306 :param mutagenesis: List of tuples of the form (residue_number, original_residue, new_residue), eg. (10, 'SER', 'ALA') for S10A mutation. \
290307 This is not supported for multichain proteins yet. Defaults to None
308+ :type mutagenesis: list<tuple<int, str, str>>, optional
309+ :param disulphide_patches: List of tuples of the form (residue_number1, chain_id1, residue_number2, chain_id2), eg. (10, A, 20, B) for a disulphide bridge between C10:A and C20:B. \
310+ Defaults to None
311+ :type disulphide_patches: list<tuple<int, str, int, str>>, optional
291312 """
292313
293314 if not self .morph_done :
@@ -345,7 +366,7 @@ def make_models(
345366 # The normal with statement doesn't work with pytest-cov
346367 pool = multiprocessing .Pool (poolsize )
347368 pool .starmap (
348- run_modeller , [(frame , number_of_models ) for frame in frame_paths ]
369+ run_modeller , [(frame , number_of_models , disulphide_patches ) for frame in frame_paths ]
349370 )
350371 pool .close ()
351372 pool .join ()
@@ -425,13 +446,6 @@ def process_models(
425446 :type asp_protonation_states: list, optional
426447 """
427448
428- # Currently multichain is not supported with caps
429-
430- if caps and self .multiple_chains :
431- raise RuntimeError (
432- "Currently, combining caps and multichain proteins isn't supported."
433- )
434-
435449 if not self .pathfinding_done :
436450 raise RuntimeError (
437451 "Need to search for the best path before processing models."
@@ -492,25 +506,67 @@ def process_models(
492506 else join (ref_folder , "termini_ref.pdb" )
493507 )
494508 for n in range (self .number_of_intermediates ):
495- cap_termini (
496- join (local_path , file_root + f"{ n } .pdb" ),
497- join (local_path , file_root + f"capped{ n } .pdb" ),
498- ref_file ,
499- self .residue_numbers [0 ],
500- self .residue_numbers [- 1 ],
501- )
509+ if not self .multiple_chains :
510+ cap_termini (
511+ join (local_path , file_root + f"{ n } .pdb" ),
512+ join (local_path , file_root + f"capped{ n } .pdb" ),
513+ ref_file ,
514+ self .residue_numbers [0 ],
515+ self .residue_numbers [- 1 ],
516+ )
517+
518+ # Rename the cap residues to fit the protein naming convention
519+ sed (
520+ join (local_path , file_root + f"capped{ n } .pdb" ),
521+ "ACE X 1" ,
522+ "ACE A" + str (self .residue_numbers [0 ] - 1 ).rjust (4 , " " ),
523+ )
524+ sed (
525+ join (local_path , file_root + f"capped{ n } .pdb" ),
526+ "NME X 4" ,
527+ "NME A" + str (self .residue_numbers [- 1 ] + 1 ).rjust (4 , " " ),
528+ )
529+ else :
530+ # get unique chain ids from multiple_chains list in order
531+
532+ unique_chain_ids = []
533+ for chain_id in self .multiple_chains :
534+ if chain_id not in unique_chain_ids :
535+ unique_chain_ids .append (chain_id )
536+
537+ last_filename = join (local_path , file_root + f"{ n } .pdb" )
538+ for c , chain_id in enumerate (unique_chain_ids ):
539+ # get the residue numbers that have this chain id
540+ chain_residue_numbers = [
541+ self .residue_numbers [i ]
542+ for i in range (len (self .residue_numbers ))
543+ if self .multiple_chains [i ] == chain_id
544+ ]
545+ print (chain_residue_numbers )
546+
547+ # cap the termini for this chain, save to a temporary file
548+ cap_termini (
549+ last_filename ,
550+ join (local_path , file_root + f"capped{ n } .pdb" ),
551+ ref_file ,
552+ chain_residue_numbers [0 ],
553+ chain_residue_numbers [- 1 ],
554+ cap_chain = chain_id
555+ )
556+ last_filename = join (local_path , file_root + f"capped{ n } .pdb" )
557+
558+ # Rename the cap residues to fit the protein naming convention
559+ sed (
560+ join (local_path , file_root + f"capped{ n } .pdb" ),
561+ "ACE X 1" ,
562+ f"ACE { chain_id } " + str (chain_residue_numbers [0 ] - 1 ).rjust (4 , " " ),
563+ )
564+ sed (
565+ join (local_path , file_root + f"capped{ n } .pdb" ),
566+ "NME X 4" ,
567+ f"NME { chain_id } " + str (chain_residue_numbers [- 1 ] + 1 ).rjust (4 , " " ),
568+ )
502569
503- # Rename the cap residues to fit the protein naming convention
504- sed (
505- join (local_path , file_root + f"capped{ n } .pdb" ),
506- "ACE X 1" ,
507- "ACE A" + str (self .residue_numbers [0 ] - 1 ).rjust (4 , " " ),
508- )
509- sed (
510- join (local_path , file_root + f"capped{ n } .pdb" ),
511- "NME X 4" ,
512- "NME A" + str (self .residue_numbers [- 1 ] + 1 ).rjust (4 , " " ),
513- )
514570 file_root = "framecapped"
515571
516572 # Copy over the forcefield to our root directory for gmx to use
0 commit comments