Skip to content

Commit

Permalink
Permit combinational dihedral angle restraints derived from ambiguous…
Browse files Browse the repository at this point in the history
… atom name (6sy2)
  • Loading branch information
yokochi47 committed Jan 29, 2024
1 parent fae5152 commit 93df7d8
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 70 deletions.
5 changes: 3 additions & 2 deletions wwpdb/utils/nmr/NmrDpReport.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
# 12-Jan-2024 M. Yokochi - getNmrSeq1LetterCodeOf() returns '.' for missing residue, instead of whitespace (DAOTHER-9065)
# 16-Jan-2024 M. Yokochi - add 'nm-res-ari' file type for ARIA restraint format (DAOTHER-9079, NMR restraint remediation)
# 17-Jan-2024 M. Yokochi - add 'coordinate_issue' error (DAOTHER-9084)
# 29-Jan-2024 M. Yokochi - add 'ambiguous_dihedral_angle' warning type (NMR restraint remediation, 6sy2)
##
""" Wrapper class for NMR data processing report.
@author: Masashi Yokochi
Expand Down Expand Up @@ -2265,7 +2266,7 @@ def __init__(self, verbose=True, log=sys.stdout):
'disordered_index', 'sequence_mismatch',
'atom_nomenclature_mismatch', 'auth_atom_nomenclature_mismatch', 'ccd_mismatch', 'ambiguity_code_mismatch',
'skipped_saveframe_category', 'skipped_loop_category',
'anomalous_bond_length', 'anomalous_rdc_vector',
'anomalous_bond_length', 'ambiguous_dihedral_angle', 'anomalous_rdc_vector',
'anomalous_chemical_shift', 'unusual_chemical_shift',
'complemented_chemical_shift', 'incompletely_assigned_chemical_shift', 'incompletely_assigned_spectral_peak',
'anomalous_data', 'unusual_data', 'unusual/rare_data', 'insufficient_data',
Expand All @@ -2276,7 +2277,7 @@ def __init__(self, verbose=True, log=sys.stdout):

self.group_items = ('sequence_mismatch',
'atom_nomenclature_mismatch', 'auth_atom_nomenclature_mismatch', 'ccd_mismatch', 'ambiguity_code_mismatch',
'anomalous_bond_length', 'anomalous_rdc_vector',
'anomalous_bond_length', 'ambiguous_dihedral_angle', 'anomalous_rdc_vector',
'complemented_chemical_shift', 'incompletely_assigned_chemical_shift', 'incompletely_assigned_spectral_peak',
'unusual/rare_data', 'insufficient_data',
'conflicted_data', 'inconsistent_data', 'redundant_data',
Expand Down
53 changes: 53 additions & 0 deletions wwpdb/utils/nmr/NmrDpUtility.py
Original file line number Diff line number Diff line change
Expand Up @@ -31154,6 +31154,14 @@ def __validateLegacyMr(self):
if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")

elif warn.startswith('[Ambiguous dihedral angle]'):
self.report.warning.appendDescription('ambiguous_dihedral_angle',
{'file_name': file_name, 'description': warn})
self.report.setWarning()

if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")

elif warn.startswith('[Anomalous RDC vector]'):
self.report.warning.appendDescription('anomalous_rdc_vector',
{'file_name': file_name, 'description': warn})
Expand Down Expand Up @@ -31348,6 +31356,14 @@ def __validateLegacyMr(self):
if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")

elif warn.startswith('[Ambiguous dihedral angle]'):
self.report.warning.appendDescription('ambiguous_dihedral_angle',
{'file_name': file_name, 'description': warn})
self.report.setWarning()

if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")

elif warn.startswith('[Anomalous RDC vector]'):
self.report.warning.appendDescription('anomalous_rdc_vector',
{'file_name': file_name, 'description': warn})
Expand Down Expand Up @@ -31720,6 +31736,13 @@ def __validateLegacyMr(self):
# if self.__verbose:
# self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")
# """
elif warn.startswith('[Ambiguous dihedral angle]'):
self.report.warning.appendDescription('ambiguous_dihedral_angle',
{'file_name': file_name, 'description': warn})
self.report.setWarning()

if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")
else:
self.report.error.appendDescription('internal_error', "+NmrDpUtility.__validateLegacyMr() ++ KeyError - " + warn)
self.report.setError()
Expand Down Expand Up @@ -31901,6 +31924,14 @@ def __validateLegacyMr(self):
if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")

elif warn.startswith('[Ambiguous dihedral angle]'):
self.report.warning.appendDescription('ambiguous_dihedral_angle',
{'file_name': file_name, 'description': warn})
self.report.setWarning()

if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")

elif warn.startswith('[Unsupported data]'):
self.report.warning.appendDescription('unsupported_mr_data',
{'file_name': file_name, 'description': warn})
Expand Down Expand Up @@ -32072,6 +32103,13 @@ def __validateLegacyMr(self):
# if self.__verbose:
# self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")
# """
elif warn.startswith('[Ambiguous dihedral angle]'):
self.report.warning.appendDescription('ambiguous_dihedral_angle',
{'file_name': file_name, 'description': warn})
self.report.setWarning()

if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")
else:
self.report.error.appendDescription('internal_error', "+NmrDpUtility.__validateLegacyMr() ++ KeyError - " + warn)
self.report.setError()
Expand Down Expand Up @@ -32370,6 +32408,13 @@ def __validateLegacyMr(self):
# if self.__verbose:
# self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")
# """
elif warn.startswith('[Ambiguous dihedral angle]'):
self.report.warning.appendDescription('ambiguous_dihedral_angle',
{'file_name': file_name, 'description': warn})
self.report.setWarning()

if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")
else:
self.report.error.appendDescription('internal_error', "+NmrDpUtility.__validateLegacyMr() ++ KeyError - " + warn)
self.report.setError()
Expand Down Expand Up @@ -32871,6 +32916,14 @@ def __validateLegacyMr(self):
if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")

elif warn.startswith('[Ambiguous dihedral angle]'):
self.report.warning.appendDescription('ambiguous_dihedral_angle',
{'file_name': file_name, 'description': warn})
self.report.setWarning()

if self.__verbose:
self.__lfh.write(f"+NmrDpUtility.__validateLegacyMr() ++ Warning - {warn}\n")

elif warn.startswith('[Anomalous RDC vector]'):
self.report.warning.appendDescription('anomalous_rdc_vector',
{'file_name': file_name, 'description': warn})
Expand Down
43 changes: 33 additions & 10 deletions wwpdb/utils/nmr/mr/BiosymMRParserListener.py
Original file line number Diff line number Diff line change
Expand Up @@ -2044,9 +2044,14 @@ def exitDihedral_angle_restraint(self, ctx: BiosymMRParser.Dihedral_angle_restra

if len(self.atomSelectionSet) < 4:
return

if not self.areUniqueCoordAtoms('a Dihedral angle'):
"""
if not self.areUniqueCoordAtoms('a dihedral angle'):
return
"""
len_f = len(self.__f)
self.areUniqueCoordAtoms('a dihedral angle',
allow_ambig=True, allow_ambig_warn_title='Ambiguous dihedral angle')
combinationId = '.' if len_f == len(self.__f) else 0

if self.__createSfDict:
sf = self.__getSf(potentialType=getPotentialType(self.__file_type, self.__cur_subtype, dstFunc))
Expand Down Expand Up @@ -2074,6 +2079,8 @@ def exitDihedral_angle_restraint(self, ctx: BiosymMRParser.Dihedral_angle_restra
_dstFunc += f" {dstFunc4}"
print(f"subtype={self.__cur_subtype} id={self.dihedRestraints} angleName={angleName} "
f"atom1={atom1} atom2={atom2} atom3={atom3} atom4={atom4} {_dstFunc}")
if isinstance(combinationId, int):
combinationId += 1
if self.__createSfDict and sf is not None:
if first_item:
sf['id'] += 1
Expand All @@ -2083,7 +2090,7 @@ def exitDihedral_angle_restraint(self, ctx: BiosymMRParser.Dihedral_angle_restra
dstFunc = self.selectRealisticChi2AngleConstraint(atom1, atom2, atom3, atom4,
dstFunc)
row = getRow(self.__cur_subtype, sf['id'], sf['index_id'],
'.' if dstFunc2 is None else 1, None, angleName,
combinationId if dstFunc2 is None else 1, None, angleName,
sf['list_id'], self.__entryId, dstFunc,
self.__authToStarSeq, self.__authToOrigSeq, self.__authToInsCode, self.__offsetHolder,
atom1, atom2, atom3, atom4)
Expand Down Expand Up @@ -2193,9 +2200,14 @@ def exitDihedral_angle_constraint(self, ctx: BiosymMRParser.Dihedral_angle_const

if len(self.atomSelectionSet) < 4:
return

if not self.areUniqueCoordAtoms('a Dihedral angle'):
"""
if not self.areUniqueCoordAtoms('a dihedral angle'):
return
"""
len_f = len(self.__f)
self.areUniqueCoordAtoms('a dihedral angle',
allow_ambig=True, allow_ambig_warn_title='Ambiguous dihedral angle')
combinationId = '.' if len_f == len(self.__f) else 0

if self.__createSfDict:
sf = self.__getSf(potentialType=getPotentialType(self.__file_type, self.__cur_subtype, dstFunc))
Expand All @@ -2219,13 +2231,15 @@ def exitDihedral_angle_constraint(self, ctx: BiosymMRParser.Dihedral_angle_const
if self.__debug:
print(f"subtype={self.__cur_subtype} id={self.dihedRestraints} angleName={angleName} "
f"atom1={atom1} atom2={atom2} atom3={atom3} atom4={atom4} {dstFunc}")
if isinstance(combinationId, int):
combinationId += 1
if self.__createSfDict and sf is not None:
if first_item:
sf['id'] += 1
first_item = False
sf['index_id'] += 1
row = getRow(self.__cur_subtype, sf['id'], sf['index_id'],
'.', None, angleName,
combinationId, None, angleName,
sf['list_id'], self.__entryId, dstFunc,
self.__authToStarSeq, self.__authToOrigSeq, self.__authToInsCode, self.__offsetHolder,
atom1, atom2, atom3, atom4)
Expand Down Expand Up @@ -2346,7 +2360,7 @@ def exitChirality_constraint(self, ctx: BiosymMRParser.Chirality_constraintConte
if len(self.atomSelectionSet) < 2:
return

if not self.areUniqueCoordAtoms('a Chirality'):
if not self.areUniqueCoordAtoms('a chirality'):
return

if self.__createSfDict:
Expand Down Expand Up @@ -2446,21 +2460,30 @@ def exitProchirality_constraint(self, ctx: BiosymMRParser.Prochirality_constrain
atom5['chain_id'], atom5['seq_id'], atom5['comp_id'], atom5['atom_id'],
sf['list_id']])

def areUniqueCoordAtoms(self, subtype_name):
def areUniqueCoordAtoms(self, subtype_name, allow_ambig=False, allow_ambig_warn_title=''):
""" Check whether atom selection sets are uniquely assigned.
"""

for _atomSelectionSet in self.atomSelectionSet:
_lenAtomSelectionSet = len(_atomSelectionSet)

if _lenAtomSelectionSet == 0:
return False # raised error already

if len(_atomSelectionSet) < 2:
if _lenAtomSelectionSet == 1:
continue

for (atom1, atom2) in itertools.combinations(_atomSelectionSet, 2):
if atom1['chain_id'] != atom2['chain_id']:
continue
if atom1['seq_id'] != atom2['seq_id']:
continue
self.__f.append(f"[Invalid atom selection] {self.__getCurrentRestraint()}"
if allow_ambig:
self.__f.append(f"[{allow_ambig_warn_title}] {self.__getCurrentRestraint()}"
f"Ambiguous atom selection '{atom1['chain_id']}:{atom1['seq_id']}:{atom1['comp_id']}:{atom1['atom_id']} or "
f"{atom2['atom_id']}' found in {subtype_name} restraint.")
continue
self.__f.append(f"[Invalid data] {self.__getCurrentRestraint()}"
f"Ambiguous atom selection '{atom1['chain_id']}:{atom1['seq_id']}:{atom1['comp_id']}:{atom1['atom_id']} or "
f"{atom2['atom_id']}' is not allowed as {subtype_name} restraint.")
return False
Expand Down
22 changes: 16 additions & 6 deletions wwpdb/utils/nmr/mr/CharmmMRParserListener.py
Original file line number Diff line number Diff line change
Expand Up @@ -939,11 +939,16 @@ def exitDihedral_angle_restraint(self, ctx: CharmmMRParser.Dihedral_angle_restra

if len(self.atomSelectionSet) != 4:
return

"""
if not self.areUniqueCoordAtoms('a dihedral angle (DIHE)'):
if len(self.__g) > 0:
self.__f.extend(self.__g)
return
"""
len_f = len(self.__f)
self.areUniqueCoordAtoms('a dihedral angle (DIHE)',
allow_ambig=True, allow_ambig_warn_title='Ambiguous dihedral angle')
combinationId = '.' if len_f == len(self.__f) else 0

if self.__createSfDict:
sf = self.__getSf(potentialType=getPotentialType(self.__file_type, self.__cur_subtype, dstFunc))
Expand All @@ -967,13 +972,15 @@ def exitDihedral_angle_restraint(self, ctx: CharmmMRParser.Dihedral_angle_restra
if self.__debug:
print(f"subtype={self.__cur_subtype} (DIHE) id={self.dihedRestraints} angleName={angleName} "
f"atom1={atom1} atom2={atom2} atom3={atom3} atom4={atom4} {dstFunc}")
if isinstance(combinationId, int):
combinationId += 1
if self.__createSfDict and sf is not None:
if first_item:
sf['id'] += 1
first_item = False
sf['index_id'] += 1
row = getRow(self.__cur_subtype, sf['id'], sf['index_id'],
'.', None, angleName,
combinationId, None, angleName,
sf['list_id'], self.__entryId, dstFunc,
self.__authToStarSeq, self.__authToOrigSeq, self.__authToInsCode, self.__offsetHolder,
atom1, atom2, atom3, atom4)
Expand Down Expand Up @@ -1631,16 +1638,14 @@ def validateAngleRange(self, weight, misc_dict,

return dstFunc

def areUniqueCoordAtoms(self, subtype_name, skip_col=None):
def areUniqueCoordAtoms(self, subtype_name, allow_ambig=False, allow_ambig_warn_title=''):
""" Check whether atom selection sets are uniquely assigned.
"""

for col, _atomSelectionSet in enumerate(self.atomSelectionSet):
for _atomSelectionSet in self.atomSelectionSet:
_lenAtomSelectionSet = len(_atomSelectionSet)

if _lenAtomSelectionSet == 0:
if skip_col is not None and col in skip_col:
continue
return False # raised error already

if _lenAtomSelectionSet == 1:
Expand All @@ -1651,6 +1656,11 @@ def areUniqueCoordAtoms(self, subtype_name, skip_col=None):
continue
if atom1['seq_id'] != atom2['seq_id']:
continue
if allow_ambig:
self.__f.append(f"[{allow_ambig_warn_title}] {self.__getCurrentRestraint()}"
f"Ambiguous atom selection '{atom1['chain_id']}:{atom1['seq_id']}:{atom1['comp_id']}:{atom1['atom_id']} or "
f"{atom2['atom_id']}' found in {subtype_name} restraint.")
continue
self.__f.append(f"[Invalid data] {self.__getCurrentRestraint()}"
f"Ambiguous atom selection '{atom1['chain_id']}:{atom1['seq_id']}:{atom1['comp_id']}:{atom1['atom_id']} or "
f"{atom2['atom_id']}' is not allowed as {subtype_name} restraint.")
Expand Down
Loading

0 comments on commit 93df7d8

Please sign in to comment.