2727import org .biojava .nbio .structure .io .mmcif .ChemCompProvider ;
2828import org .biojava .nbio .structure .io .mmcif .model .ChemComp ;
2929import org .biojava .nbio .structure .io .mmcif .model .ChemCompBond ;
30+ import org .biojava .nbio .structure .io .mmcif .model .StructConn ;
3031import org .biojava .nbio .structure .io .util .PDBTemporaryStorageUtils .LinkRecord ;
3132import org .slf4j .Logger ;
3233import org .slf4j .LoggerFactory ;
3334
3435import java .util .ArrayList ;
36+ import java .util .HashSet ;
3537import java .util .List ;
38+ import java .util .Set ;
3639
3740/**
3841 * Adds polymer bonds for peptides and nucleotides based on distance cutoffs and
@@ -52,6 +55,21 @@ public class BondMaker {
5255
5356 private static final Logger logger = LoggerFactory .getLogger (BondMaker .class );
5457
58+ /**
59+ * The types of bonds that are read from struct_conn (type specified in field conn_type_id)
60+ */
61+ public static final Set <String > BOND_TYPES_TO_PARSE ;
62+ static {
63+ BOND_TYPES_TO_PARSE = new HashSet <>();
64+ BOND_TYPES_TO_PARSE .add ("disulf" );
65+ BOND_TYPES_TO_PARSE .add ("covale" );
66+ BOND_TYPES_TO_PARSE .add ("covale_base" );
67+ BOND_TYPES_TO_PARSE .add ("covale_phosphate" );
68+ BOND_TYPES_TO_PARSE .add ("covale_sugar" );
69+ BOND_TYPES_TO_PARSE .add ("modres" );
70+ }
71+
72+
5573 /**
5674 * Maximum peptide (C - N) bond length considered for bond formation
5775 */
@@ -61,10 +79,12 @@ public class BondMaker {
6179 */
6280 private static final double MAX_NUCLEOTIDE_BOND_LENGTH = 2.1 ;
6381
64- private Structure structure = null ;
82+ private Structure structure ;
83+ private FileParsingParameters params ;
6584
66- public BondMaker (Structure structure ) {
85+ public BondMaker (Structure structure , FileParsingParameters params ) {
6786 this .structure = structure ;
87+ this .params = params ;
6888 }
6989
7090 /**
@@ -220,19 +240,17 @@ private void trimBondLists() {
220240 * Creates disulfide bond objects and references in the corresponding Atoms objects, given
221241 * a list of {@link SSBondImpl}s parsed from a PDB/mmCIF file.
222242 * @param disulfideBonds
223- * @param parseCAonly
224- * @return the list of bonds
225243 */
226- public List < Bond > formDisulfideBonds (List <SSBondImpl > disulfideBonds , boolean parseCAonly ) {
244+ public void formDisulfideBonds (List <SSBondImpl > disulfideBonds ) {
227245 List <Bond > bonds = new ArrayList <>();
228246 for (SSBondImpl disulfideBond : disulfideBonds ) {
229- Bond bond = formDisulfideBond (disulfideBond , parseCAonly );
247+ Bond bond = formDisulfideBond (disulfideBond );
230248 if (bond !=null ) bonds .add (bond );
231249 }
232- return bonds ;
250+ structure . setSSBonds ( bonds ) ;
233251 }
234252
235- private Bond formDisulfideBond (SSBondImpl disulfideBond , boolean parseCAonly ) {
253+ private Bond formDisulfideBond (SSBondImpl disulfideBond ) {
236254 try {
237255 Atom a = getAtomFromRecord ("SG" , "" , "CYS" ,
238256 disulfideBond .getChainID1 (), disulfideBond .getResnum1 (),
@@ -249,11 +267,10 @@ private Bond formDisulfideBond(SSBondImpl disulfideBond, boolean parseCAonly) {
249267
250268 } catch (StructureException e ) {
251269 // Note, in Calpha only mode the CYS SG's are not present.
252- if (! parseCAonly ) {
253- logger .error ("Error with the following SSBond: {}" ,disulfideBond .toString ());
254- throw new RuntimeException (e );
270+ if (! params .isParseCAOnly ()) {
271+ logger .warn ("Could not find atoms specified in SSBOND record: {}" ,disulfideBond .toString ());
255272 } else {
256- logger .debug ("StructureException caught while forming disulfide bonds in parseCAonly mode. Error: " + e . getMessage () );
273+ logger .debug ("Could not find atoms specified in SSBOND record while parsing in parseCAonly mode." );
257274 }
258275
259276 return null ;
@@ -263,9 +280,8 @@ private Bond formDisulfideBond(SSBondImpl disulfideBond, boolean parseCAonly) {
263280 /**
264281 * Creates bond objects from a LinkRecord as parsed from a PDB file
265282 * @param linkRecord
266- * @param parseCAonly
267283 */
268- public void formLinkRecordBond (LinkRecord linkRecord , boolean parseCAonly ) {
284+ public void formLinkRecordBond (LinkRecord linkRecord ) {
269285 // only work with atoms that aren't alternate locations
270286 if (linkRecord .getAltLoc1 ().equals (" " )
271287 || linkRecord .getAltLoc2 ().equals (" " ))
@@ -287,19 +303,85 @@ public void formLinkRecordBond(LinkRecord linkRecord, boolean parseCAonly) {
287303 new BondImpl (a , b , 1 );
288304 } catch (StructureException e ) {
289305 // Note, in Calpha only mode the link atoms may not be present.
290- if (! parseCAonly ) {
291- logger .error ("Error with the following link record: {}" ,linkRecord .toString ());
292- //e.printStackTrace();
293- throw new RuntimeException (e );
306+ if (! params .isParseCAOnly ()) {
307+ logger .warn ("Could not find atoms specified in LINK record: {}" ,linkRecord .toString ());
294308 } else {
295- logger .debug ("StructureException caught while forming link record bonds in parseCAonly mode. Error: " + e . getMessage () );
309+ logger .debug ("Could not find atoms specified in LINK record while parsing in parseCAonly mode." );
296310 }
297311
298312 }
299313 }
300314
315+ public void formBondsFromStructConn (List <StructConn > structConn ) {
316+
317+ final String symop = "1_555" ; // For now - accept bonds within origin asymmetric unit.
318+
319+ List <Bond > ssbonds = new ArrayList <>();
320+
321+ for (StructConn conn : structConn ) {
322+
323+ if (!BOND_TYPES_TO_PARSE .contains (conn .getConn_type_id ())) continue ;
324+
325+ String chainId1 = conn .getPtnr1_auth_asym_id ();
326+ String chainId2 = conn .getPtnr2_auth_asym_id ();
327+
328+ String insCode1 = "" ;
329+ if (!conn .getPdbx_ptnr1_PDB_ins_code ().equals ("?" )) insCode1 = conn .getPdbx_ptnr1_PDB_ins_code ();
330+ String insCode2 = "" ;
331+ if (!conn .getPdbx_ptnr2_PDB_ins_code ().equals ("?" )) insCode2 = conn .getPdbx_ptnr2_PDB_ins_code ();
332+
333+ String seqId1 = conn .getPtnr1_auth_seq_id ();
334+ String seqId2 = conn .getPtnr2_auth_seq_id ();
335+ String resName1 = conn .getPtnr1_label_comp_id ();
336+ String resName2 = conn .getPtnr2_label_comp_id ();
337+ String atomName1 = conn .getPtnr1_label_atom_id ();
338+ String atomName2 = conn .getPtnr2_label_atom_id ();
339+ String altLoc1 = "" ;
340+ if (!conn .getPdbx_ptnr1_label_alt_id ().equals ("?" )) altLoc1 = conn .getPdbx_ptnr1_label_alt_id ();
341+ String altLoc2 = "" ;
342+ if (!conn .getPdbx_ptnr2_label_alt_id ().equals ("?" )) altLoc2 = conn .getPdbx_ptnr2_label_alt_id ();
343+
344+ Atom a1 = null ;
345+ Atom a2 = null ;
346+
347+ try {
348+ a1 = getAtomFromRecord (atomName1 , altLoc1 , resName1 , chainId1 , seqId1 , insCode1 );
349+
350+ } catch (StructureException e ) {
351+ logger .warn ("Could not find atom specified in struct_conn record: {}{}({}) in chain {}, atom {}" , seqId1 , insCode1 , resName1 , chainId1 , atomName1 );
352+ continue ;
353+ }
354+ try {
355+ a2 = getAtomFromRecord (atomName2 , altLoc2 , resName2 , chainId2 , seqId2 , insCode2 );
356+ } catch (StructureException e ) {
357+ logger .warn ("Could not find atom specified in struct_conn record: {}{}({}) in chain {}, atom {}" , seqId2 , insCode2 , resName2 , chainId2 , atomName2 );
358+ continue ;
359+ }
360+
361+
362+ // TODO: when issue 220 is implemented, add robust symmetry handling to allow bonds between symmetry-related molecules.
363+ if (!conn .getPtnr1_symmetry ().equals (symop ) || !conn .getPtnr2_symmetry ().equals (symop ) ) {
364+ logger .info ("Skipping bond between atoms {}({}) and {}({}) belonging to different symmetry partners, because it is not supported yet" ,
365+ a1 .getPDBserial (), a1 .getName (), a2 .getPDBserial (), a2 .getName ());
366+ continue ;
367+ }
368+
369+ // assuming order 1 for all bonds, no information is provided by struct_conn
370+ Bond bond = new BondImpl (a1 , a2 , 1 );
371+
372+ if (conn .getConn_type_id ().equals ("disulf" )) {
373+ ssbonds .add (bond );
374+ }
375+
376+ }
377+
378+ // only for ss bonds we add a specific map in structure, all the rests are linked only from Atom.getBonds
379+ structure .setSSBonds (ssbonds );
380+ }
381+
301382 private Atom getAtomFromRecord (String name , String altLoc , String resName , String chainID , String resSeq , String iCode )
302383 throws StructureException {
384+
303385 if (iCode ==null || iCode .isEmpty ()) {
304386 iCode = " " ; // an insertion code of ' ' is ignored
305387 }
@@ -308,11 +390,14 @@ private Atom getAtomFromRecord(String name, String altLoc, String resName, Strin
308390 ResidueNumber resNum = new ResidueNumber (chainID , Integer .parseInt (resSeq ), iCode .charAt (0 ));
309391 Group group = chain .getGroupByPDB (resNum );
310392
393+ Group g = group ;
311394 // there is an alternate location
312395 if (!altLoc .isEmpty ()) {
313- group = group .getAltLocGroup (altLoc .charAt (0 ));
396+ g = group .getAltLocGroup (altLoc .charAt (0 ));
397+ if (g ==null )
398+ throw new StructureException ("Could not find altLoc code " +altLoc +" in group " +resSeq +iCode +" of chain " + chainID );
314399 }
315400
316- return group .getAtom (name );
401+ return g .getAtom (name );
317402 }
318403}
0 commit comments