stcrpy 1.0.0__py3-none-any.whl → 1.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. stcrpy/__init__.py +1 -1
  2. stcrpy/tcr_formats/tcr_formats.py +20 -1
  3. stcrpy/tcr_geometry/TCRAngle.py +177 -0
  4. stcrpy/tcr_geometry/TCRDock.py +4 -1
  5. stcrpy/tcr_geometry/reference_data/Acoreset.txt +30 -0
  6. stcrpy/tcr_geometry/reference_data/Bcoreset.txt +30 -0
  7. stcrpy/tcr_geometry/reference_data/consensus_A.pdb +31 -0
  8. stcrpy/tcr_geometry/reference_data/consensus_B.pdb +31 -0
  9. stcrpy/tcr_geometry/reference_data/consensus_D.pdb +31 -0
  10. stcrpy/tcr_geometry/reference_data/consensus_G.pdb +31 -0
  11. stcrpy/tcr_geometry/reference_data/pcA.txt +3 -0
  12. stcrpy/tcr_geometry/reference_data/pcB.txt +3 -0
  13. stcrpy/tcr_interactions/TCRInteractionProfiler.py +1 -1
  14. stcrpy/tcr_interactions/TCRpMHC_PLIP_Model_Parser.py +21 -0
  15. stcrpy/tcr_methods/tcr_batch_operations.py +14 -10
  16. stcrpy/tcr_methods/tcr_methods.py +23 -22
  17. stcrpy/tcr_metrics/tcr_dockq.py +404 -0
  18. stcrpy/tcr_processing/Chemical_components.py +4 -4
  19. stcrpy/tcr_processing/Entity.py +15 -16
  20. stcrpy/tcr_processing/MHC.py +456 -4
  21. stcrpy/tcr_processing/TCR.py +462 -14
  22. stcrpy/tcr_processing/TCRParser.py +364 -193
  23. stcrpy/tcr_processing/annotate.py +35 -24
  24. stcrpy/tcr_processing/utils/common.py +3 -2
  25. stcrpy/tcr_processing/utils/constants.py +4 -3
  26. stcrpy/tcr_processing/utils/region_definitions.py +9 -0
  27. stcrpy/tcr_processing/utils/symmetry_mates.py +90 -0
  28. stcrpy-1.0.5.dist-info/METADATA +285 -0
  29. {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/RECORD +33 -22
  30. {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/WHEEL +1 -1
  31. stcrpy-1.0.0.dist-info/METADATA +0 -173
  32. {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/licenses/LICENCE +0 -0
  33. {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/licenses/stcrpy/tcr_geometry/TCRCoM_LICENCE +0 -0
  34. {stcrpy-1.0.0.dist-info → stcrpy-1.0.5.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,17 @@
1
1
  """
2
2
  Created on 3rd April 2024
3
- @author: Nele Quast based on work by Dunbar and Leem
3
+ Nele Quast based on work by Dunbar and Leem
4
4
  The TCR class.
5
5
  """
6
6
 
7
7
  import sys
8
8
  import warnings
9
9
 
10
+ from Bio import BiopythonWarning
11
+
10
12
  from .Entity import Entity
13
+ from .TCRchain import TCRchain
14
+ from .utils.region_definitions import IMGT_VARIABLE_DOMAIN
11
15
 
12
16
  try:
13
17
  from .. import tcr_interactions
@@ -18,17 +22,34 @@ except ImportError as e:
18
22
  print(e)
19
23
 
20
24
 
25
+ class TCRError(Exception):
26
+ """Error raised when there is an issue with the TCR."""
27
+
28
+
21
29
  class TCR(Entity):
22
30
  """
23
- TCR class. This is generic which is inherited later.
24
- Holds paired TCR chains.
31
+ TCR class. Inherits from PDB.Entity.
32
+ This is a base class for TCR strucutres, enabling antigen and MHC association.
33
+ abTCR and gdTCR are the instantiated subclasses of this class.
25
34
  """
26
35
 
27
36
  def _add_antigen(self, antigen=None):
37
+ """
38
+ Append associated antigen to TCR antigen field.
39
+
40
+ Args:
41
+ antigen (Antigen, optional): Antigen to associate with TCR. Defaults to None.
42
+ """
28
43
  if antigen not in self.antigen:
29
44
  self.antigen.append(antigen)
30
45
 
31
46
  def _add_mhc(self, mhc=None):
47
+ """
48
+ Append associated MHC to TCR MHC field. If antigen are associted with MHC but not TCR, add them to TCR antigen.
49
+
50
+ Args:
51
+ mhc (MHC, optional): MHC to associate with TCR. Defaults to None.
52
+ """
32
53
  if mhc not in self.MHC:
33
54
  self.MHC.append(mhc)
34
55
  # If there are any het antigens that are in the MHC but not in close proximity of the TCR
@@ -36,19 +57,41 @@ class TCR(Entity):
36
57
  if set(mhc.antigen) - set(self.antigen):
37
58
  self.antigen.extend(mhc.antigen)
38
59
 
60
+ def copy(self, copy_siblings = True ):
61
+ """
62
+ Return a copy of the TCR object. This returns a shallow copy of the TCR object.
63
+ If the copy_siblings flag is set to True, the antigen and MHC objects will also be copied. Warning - if the copy_siblings flag is set to False, the antigen and MHC objects will not be copied, and the reference will still point to the same MHC and antigen objects as the original.
64
+
65
+ copy_siblings: Whether to copy sibling entities (ie. MHC and Antigen objects). Default True.
66
+
67
+ """
68
+ shallow = super().copy()
69
+ if copy_siblings:
70
+ shallow.antigen = [a.copy() for a in self.get_antigen()]
71
+ shallow.MHC = [m.copy(copy_siblings=False) for m in self.get_MHC()]
72
+ for m in shallow.MHC:
73
+ m.tcr = [t.copy(copy_siblings=False) if t.id != shallow.id else shallow for t in m.tcr]
74
+ m.antigen = [ag.copy() if ag.id not in [a.id for a in shallow.antigen] else [a for a in shallow.antigen if a.id==ag.id][0] for ag in m.antigen]
75
+
76
+ return shallow
77
+
39
78
  def get_antigen(self):
40
79
  """
41
- Return a list of bound antigens.
80
+ Return a list of TCR associated antigens.
42
81
  """
43
82
  return self.antigen
44
83
 
45
84
  def get_MHC(self):
46
- """ """
85
+ """
86
+ Return a list of TCR associated MHCs.
87
+ """
47
88
  return self.MHC
48
89
 
49
90
  def is_bound(self):
50
- """
51
- Check whether this TCR is bound to an antigen
91
+ """True or False if the TCR is associated with an antigen.
92
+
93
+ Returns:
94
+ bool: Whether TCR is associated with an antigen.
52
95
  """
53
96
  if self.get_antigen():
54
97
  return True
@@ -56,22 +99,40 @@ class TCR(Entity):
56
99
  return False
57
100
 
58
101
  def get_chains(self):
102
+ """Returns generator of TCR chains.
103
+
104
+ Yields:
105
+ Chain: TCR chain
106
+ """
59
107
  for c in self:
60
108
  yield c
61
109
 
62
110
  def get_residues(self):
111
+ """Returns generator of TCR residues.
112
+
113
+ Yields:
114
+ Residue: TCR residue
115
+ """
63
116
  for c in self.get_chains():
64
117
  for r in c:
65
118
  yield r
66
119
 
67
120
  def get_atoms(self):
121
+ """Returns generator of TCR atoms.
122
+
123
+ Yields:
124
+ Atom: TCR atoms
125
+ """
68
126
  for r in self.get_residues():
69
127
  for a in r:
70
128
  yield a
71
129
 
72
130
  def get_frameworks(self):
73
131
  """
74
- Obtain framework regions from a TCR structure object.
132
+ Obtain framework regions from a TCR structure object as generator.
133
+
134
+ Yields:
135
+ Fragment: TCR framework regions
75
136
  """
76
137
  for f in self.get_fragments():
77
138
  if "fw" in f.id:
@@ -79,15 +140,20 @@ class TCR(Entity):
79
140
 
80
141
  def get_CDRs(self):
81
142
  """
82
- Obtain CDR loops from a TCR structure object
143
+ Obtain complementarity determining regions (CDRs) from a TCR structure object as generator.
144
+
145
+ Yields:
146
+ Fragment: TCR CDR regions
83
147
  """
84
148
  for f in self.get_fragments():
85
149
  if "cdr" in f.id:
86
150
  yield f
87
151
 
88
152
  def get_TCR_type(self):
89
- """
90
- Get the TCR type
153
+ """Get TCR type according to variable region assignments.
154
+
155
+ Returns:
156
+ str: TCR type (abTCR, gdTCR, dbTCR)
91
157
  """
92
158
  if hasattr(self, "tcr_type"):
93
159
  return self.tcr_type
@@ -102,9 +168,22 @@ class TCR(Entity):
102
168
  return self.tcr_type
103
169
 
104
170
  def get_germline_assignments(self):
171
+ """Retrive germline assignments for all TCR chains.
172
+ This is a dictionary with the chain ID as key and the germline assignments as value.
173
+
174
+ Returns:
175
+ dict: dict with TCR chain ID as key and germline assignments as value
176
+ """
105
177
  return {c.id: c.get_germline_assignments() for c in self.get_chains()}
106
178
 
107
179
  def get_MHC_allele_assignments(self):
180
+ """
181
+ Retrieve MHC allele assignments for all TCR associated MHCs.
182
+ This is a list of dictionaries with the MHC ID as key and the allele assignments as value.
183
+
184
+ Returns:
185
+ dict: dict with MHC chain ID as key and allele assignments as value
186
+ """
108
187
  return [
109
188
  (
110
189
  mhc.get_allele_assignments()
@@ -116,6 +195,11 @@ class TCR(Entity):
116
195
  ]
117
196
 
118
197
  def get_germlines_and_alleles(self):
198
+ """Get all germline and allele assignments for TCR and MHC chains as a dictionary with the chain ID as key and the germline assignments as value.
199
+
200
+ Returns:
201
+ dict: Dictionary of TCR germline and MHC allele assignemnts with amino acid sequences.
202
+ """
119
203
  from ..tcr_formats.tcr_formats import get_sequences
120
204
 
121
205
  germlines_and_alleles = {}
@@ -166,23 +250,79 @@ class TCR(Entity):
166
250
 
167
251
  return germlines_and_alleles
168
252
 
253
+ def get_chain_mapping(self):
254
+ """Get a dictionary of chain IDs to chain types.
255
+
256
+ Returns:
257
+ dict: Dictionary of chain IDs to chain types
258
+ """
259
+ tcr_chain_mapping = {v: k for k, v in self.get_domain_assignment().items()}
260
+ antigen_chain_mapping = {c.id: "Ag" for c in self.get_antigen()}
261
+ mhc_chain_mapping = {
262
+ c.id: c.chain_type for m in self.get_MHC() for c in m.get_chains()
263
+ }
264
+ chain_mapping = {
265
+ **tcr_chain_mapping,
266
+ **antigen_chain_mapping,
267
+ **mhc_chain_mapping,
268
+ }
269
+
270
+ return chain_mapping
271
+
169
272
  def save(self, save_as=None, tcr_only: bool = False, format: str = "pdb"):
273
+ """Save TCR object as PDB or MMCIF file.
274
+
275
+ Args:
276
+ save_as (str, optional): File path to save TCR to. Defaults to None.
277
+ tcr_only (bool, optional): Whether to save TCR only or to include MHC and antigen. Defaults to False.
278
+ format (str, optional): Whether to save as PDB or MMCIF. Defaults to "pdb".
279
+ """
170
280
  from . import TCRIO
171
281
 
172
282
  tcrio = TCRIO.TCRIO()
173
283
  tcrio.save(self, save_as=save_as, tcr_only=tcr_only, format=format)
174
284
 
175
285
  def get_scanning_angle(self, mode="rudolph"):
286
+ """
287
+ Returns TCR:pMHC complex scanning (aka crossing, incident) angle of TCR to MHC.
288
+ See paper for details.
289
+
290
+ Args:
291
+ mode (str, optional): Mode for calculating the scanning angle. Options "rudolph", "cys", "com". Defaults to "rudolph".
292
+
293
+ Returns:
294
+ float: Scanning angle of TCR to MHC in degrees
295
+ """
176
296
  if not hasattr(self, "geometry") or self.geometry.mode != mode:
177
297
  self.calculate_docking_geometry(mode=mode)
178
298
  return self.geometry.get_scanning_angle()
179
299
 
180
- def get_pitch_angle(self, mode="rudolph"):
300
+ def get_pitch_angle(self, mode="cys"):
301
+ """
302
+ Returns TCR:pMHC complex pitch angle of TCR to MHC.
303
+ See paper for details.
304
+
305
+ Args:
306
+ mode (str, optional): Mode for calculating the scanning angle. Options "rudolph", "cys", "com". Defaults to "cys".
307
+
308
+ Returns:
309
+ float: Pitch angle of TCR to MHC in degrees
310
+ """
181
311
  if not hasattr(self, "geometry") or self.geometry.mode != mode:
182
312
  self.calculate_docking_geometry(mode=mode)
183
313
  return self.geometry.get_pitch_angle()
184
314
 
185
315
  def calculate_docking_geometry(self, mode="rudolph", as_df=False):
316
+ """Calculate docking geometry of TCR to MHC.
317
+ This is a wrapper function for the TCRGeom class.
318
+
319
+ Args:
320
+ mode (str, optional): Mode for calculating the geometry. Options "rudolph", "cys", "com". Defaults to "rudolph".
321
+ as_df (bool, optional): Whether to return as dictionary or dataframe. Defaults to False.
322
+
323
+ Returns:
324
+ [dict, DataFrame]: TCR to MHC geometry.
325
+ """
186
326
  if len(self.get_MHC()) == 0:
187
327
  warnings.warn(
188
328
  f"No MHC found for TCR {self}. Docking geometry cannot be calcuated"
@@ -203,20 +343,39 @@ class TCR(Entity):
203
343
  return self.geometry.to_dict()
204
344
 
205
345
  def score_docking_geometry(self, **kwargs):
346
+ """
347
+ Score docking geometry of TCR to MHC.
348
+ This is a wrapper function for the TCRGeomFiltering class.
349
+ The score is calculated as the negative log of the TCR:pMHC complex geometry feature probabilities based on the distributions fit by maximum likelihood estimation of TCR to Class I MHC strucutres from STCRDab.
350
+ Please see the paper methods for details.
351
+
352
+ Returns:
353
+ float: TCR:pMHC complex score as negative log of TCR:pMHC complex geometry feature probabilities
354
+ """
206
355
  from ..tcr_geometry.TCRGeomFiltering import DockingGeometryFilter
207
356
 
208
357
  geom_filter = DockingGeometryFilter()
209
358
  if not hasattr(self, "geometry"):
210
359
  self.calculate_docking_geometry(mode="com")
211
360
  return geom_filter.score_docking_geometry(
212
- self.geometry.get_scanning_angle(),
213
- self.geometry.get_pitch_angle(),
361
+ self.geometry.get_scanning_angle(mode="com"),
362
+ self.geometry.get_pitch_angle(mode="com"),
214
363
  self.geometry.tcr_com[-1], # z component of TCR centre of mass
215
364
  )
216
365
 
217
366
  def profile_peptide_interactions(
218
367
  self, renumber: bool = True, save_to: str = None, **kwargs
219
368
  ) -> "pd.DataFrame":
369
+ """
370
+ Profile the interactions of the peptide to the TCR and the MHC.
371
+
372
+ Args:
373
+ renumber (bool, optional): Whether to renumber the interacting residues. Defaults to True.
374
+ save_to (str, optional): Path to save intraction data to as csv. Defaults to None.
375
+
376
+ Returns:
377
+ pd.DataFrame: Dataframe of peptide interactions
378
+ """
220
379
  if len(self.get_antigen()) == 0:
221
380
  warnings.warn(
222
381
  f"No peptide antigen found for TCR {self}. Peptide interactions cannot be profiled"
@@ -238,6 +397,25 @@ class TCR(Entity):
238
397
  return interactions
239
398
 
240
399
  def get_interaction_heatmap(self, plotting_kwargs={}, **interaction_kwargs):
400
+ """
401
+ Get interaction heatmap of TCR to MHC and peptide.
402
+ Generates heatmap image.
403
+ Plotting kwargs are passed to heatmap function.
404
+
405
+ Args:
406
+ plotting_kwargs (dict, optional):
407
+ save_as: path to save heatmap image to
408
+ interaction_type: type of interaction (eg. saltbridge, h_bond) to plot. All interactions are plotted by default.
409
+ antigen_name: name of antigen for plot title
410
+ mutation_index: index of antigen residues to highlight in plot
411
+ Defaults to {
412
+ save_as:None,
413
+ interaction_type:None,
414
+ antigen_name:None,
415
+ mutation_index:None
416
+ }.
417
+ interaction_kwargs: kwargs for TCRInteractionProfiler class. See TCRInteractionProfiler for details.
418
+ """
241
419
  from ..tcr_interactions import TCRInteractionProfiler
242
420
 
243
421
  interaction_profiler = TCRInteractionProfiler.TCRInteractionProfiler(
@@ -251,6 +429,44 @@ class TCR(Entity):
251
429
  def profile_MHC_interactions(self):
252
430
  raise NotImplementedError
253
431
 
432
+ def get_TCR_angles(self):
433
+ from ..tcr_geometry.TCRAngle import TCRAngle
434
+
435
+ return TCRAngle().calculate_angles(self)
436
+
437
+ def crop(self, *, crop_mhc: bool = True, remove_het_atoms: bool = True) -> None:
438
+ """Crop TCR to variable domain and optionally crop MHC to antigen binding domain.
439
+
440
+ This method mutates the TCR object.
441
+
442
+ Args:
443
+ crop_mhc: crop mhc to antigen binding domain
444
+ remove_het_atoms: remove het atoms from structure as well
445
+
446
+ """
447
+ new_child_dict = {}
448
+ for chain in self:
449
+ new_chain = TCRchain(chain.id)
450
+
451
+ for residue in chain:
452
+ if residue.id[1] in IMGT_VARIABLE_DOMAIN or (not remove_het_atoms and residue.id[0] != ' '):
453
+ new_chain.add(residue.copy())
454
+
455
+ new_chain.analyse(chain.chain_type)
456
+ new_chain.set_engineered(chain.engineered)
457
+ new_chain.xtra.update(chain.xtra)
458
+ new_child_dict[new_chain.id] = new_chain
459
+
460
+ for chain_id in new_child_dict:
461
+ del self[chain_id]
462
+
463
+ for new_chain in new_child_dict.values():
464
+ self.add(new_chain)
465
+
466
+ if crop_mhc:
467
+ for mhc in self.get_MHC():
468
+ mhc.crop(remove_het_atoms=remove_het_atoms)
469
+
254
470
  def _create_interaction_visualiser(self):
255
471
  """Function called during TCR initialisation checks if pymol is installed and assigns a visualisation method accordingly.
256
472
  If pymol is installed, method to generate interaction visualisations is returned.
@@ -266,6 +482,15 @@ class TCR(Entity):
266
482
  def visualise_interactions(
267
483
  save_as=None, antigen_residues_to_highlight=None, **interaction_kwargs
268
484
  ):
485
+ """Visualise peptide interactions in pymol.
486
+
487
+ Args:
488
+ save_as (str, optional): path to save pymol session to. Defaults to None.
489
+ antigen_residues_to_highlight (list[int], optional): antigen residues to highlight red in pymol session. Defaults to None.
490
+ **interaction_kwargs: kwargs for TCRInteractionProfiler class. See TCRInteractionProfiler for details.
491
+ Returns:
492
+ str: path to saved pymol session
493
+ """
269
494
  from ..tcr_interactions import TCRInteractionProfiler
270
495
 
271
496
  interaction_profiler = TCRInteractionProfiler.TCRInteractionProfiler(
@@ -304,9 +529,39 @@ class TCR(Entity):
304
529
 
305
530
  return visualise_interactions
306
531
 
532
+ def standardise_chain_names(self):
533
+ """Raises NotImplementedError."""
534
+ raise NotImplementedError()
535
+
536
+ def _validate_chain_standardising(self) -> None:
537
+ if (hasattr(self, 'antigen') and len(self.antigen) > 1) or (hasattr(self, 'MHC') and len(self.MHC) > 1):
538
+ msg = 'More than one antigen or MHC molecule is not currently supported for standardising.'
539
+ raise TCRError(msg)
540
+
541
+ def _standardise_antigen_chain_names(self) -> None:
542
+ """Will give the antigen the chain id C. Does not support multiple antigens."""
543
+ with warnings.catch_warnings():
544
+ warnings.simplefilter('ignore', BiopythonWarning)
545
+ self.antigen[0].id = 'C'
546
+
547
+ def _standardise_mhc_chain_names(self) -> None:
548
+ """Will give the MHC first chain id A and second chain B. Does not support more than one MHC molecule."""
549
+ self.MHC[0].standardise_chain_names()
550
+
307
551
 
308
552
  class abTCR(TCR):
553
+ """
554
+ abTCR class. Inherits from TCR.
555
+ This is a subclass of TCR for TCRs with alpha and beta chains.
556
+ """
309
557
  def __init__(self, c1, c2):
558
+ """
559
+ Initialise abTCR object. This is a subclass of TCR for TCRs with alpha and beta chains.
560
+
561
+ Args:
562
+ c1 (TCRchain): alpha or beta type TCR chain
563
+ c2 (TCRchain): alpha or beta type TCR chain
564
+ """
310
565
 
311
566
  if c1.chain_type == "B":
312
567
  Entity.__init__(self, c1.id + c2.id)
@@ -328,9 +583,22 @@ class abTCR(TCR):
328
583
  self.visualise_interactions = self._create_interaction_visualiser()
329
584
 
330
585
  def __repr__(self):
586
+ """
587
+ String representation of the abTCR object.
588
+
589
+ Returns:
590
+ str: String representation of the abTCR objec
591
+ """
331
592
  return "<TCR %s%s beta=%s; alpha=%s>" % (self.VB, self.VA, self.VB, self.VA)
332
593
 
333
594
  def _add_domain(self, chain):
595
+ """
596
+ Add a variable alpha or variable beta domain to the TCR object.
597
+ Links the domain to the chain ID.
598
+
599
+ Args:
600
+ chain (TCRchain): TCR chain whose domain is being added.
601
+ """
334
602
  if chain.chain_type == "B":
335
603
  self.VB = chain.id
336
604
  elif chain.chain_type == "A" or chain.chain_type == "D":
@@ -340,14 +608,32 @@ class abTCR(TCR):
340
608
  self.add(chain)
341
609
 
342
610
  def get_VB(self):
611
+ """
612
+ Retrieve the variable beta chain of the TCR
613
+
614
+ Returns:
615
+ TCRchain: VB chain
616
+ """
343
617
  if hasattr(self, "VB"):
344
618
  return self.child_dict[self.VB]
345
619
 
346
620
  def get_VA(self):
621
+ """
622
+ Retrieve the variable alpha chain of the TCR
623
+
624
+ Returns:
625
+ TCRchain: VA chain
626
+ """
347
627
  if hasattr(self, "VA"):
348
628
  return self.child_dict[self.VA]
349
629
 
350
630
  def get_domain_assignment(self):
631
+ """
632
+ Retrieve the domain assignment of the TCR as a dict with variable domain type as key and chain ID as value.
633
+
634
+ Returns:
635
+ dict: domain assignment from domain to chain ID, e.g. {"VA": "D", "VB": "E"}
636
+ """
351
637
  try:
352
638
  return {"VA": self.VA, "VB": self.VB}
353
639
  except AttributeError:
@@ -358,6 +644,12 @@ class abTCR(TCR):
358
644
  return None
359
645
 
360
646
  def is_engineered(self):
647
+ """
648
+ Flag for engineered TCRs.
649
+
650
+ Returns:
651
+ bool: Flag for engineered TCRs
652
+ """
361
653
  if self.engineered:
362
654
  return True
363
655
  else:
@@ -371,6 +663,12 @@ class abTCR(TCR):
371
663
  return False
372
664
 
373
665
  def get_fragments(self):
666
+ """
667
+ Retrieve the fragments, ie FW and CDR loops of the TCR as a generator.
668
+
669
+ Yields:
670
+ Fragment: fragment of TCR chain.
671
+ """
374
672
  vb, va = self.get_VB(), self.get_VA()
375
673
 
376
674
  # If a variable domain exists
@@ -379,6 +677,56 @@ class abTCR(TCR):
379
677
  for frag in var_domain.get_fragments():
380
678
  yield frag
381
679
 
680
+ def standardise_chain_names(self) -> None:
681
+ """
682
+ Standardise the TCR, antigen, and MHC chain names to the following convention.
683
+
684
+ Convention:
685
+ - A - MHC chain 1
686
+ - B - MHC chain 2 (eg B2M)
687
+ - C - antigen chain
688
+ - D - TCR alpha chain
689
+ - E - TCR beta chain
690
+
691
+ Note, this mutates the original object.
692
+
693
+ Raises:
694
+ TCRError: if there is more than one antigen or MHC molecules attached to the TCR.
695
+
696
+ """
697
+ self._validate_chain_standardising()
698
+
699
+ new_id = []
700
+ new_child_dict = {}
701
+
702
+ if hasattr(self, 'VB'):
703
+ new_child_dict['E'] = self.child_dict[self.VB]
704
+ self.VB = 'E'
705
+ new_id.append('E')
706
+
707
+ if hasattr(self, 'VA'):
708
+ new_child_dict['D'] = self.child_dict[self.VA]
709
+ self.VA = 'D'
710
+ new_id.append('D')
711
+
712
+ with warnings.catch_warnings():
713
+ warnings.simplefilter('ignore', BiopythonWarning)
714
+
715
+ for chain_id, chain in new_child_dict.items():
716
+ chain.id = chain_id
717
+
718
+ self.child_dict = new_child_dict
719
+
720
+ if hasattr(self, 'antigen') and self.antigen:
721
+ self._standardise_antigen_chain_names()
722
+
723
+ if hasattr(self, 'MHC') and self.MHC:
724
+ self._standardise_mhc_chain_names()
725
+
726
+ with warnings.catch_warnings():
727
+ warnings.simplefilter('ignore', BiopythonWarning)
728
+ self.id = ''.join(new_id)
729
+
382
730
 
383
731
  class gdTCR(TCR):
384
732
 
@@ -455,6 +803,56 @@ class gdTCR(TCR):
455
803
  for frag in var_domain.get_fragments():
456
804
  yield frag
457
805
 
806
+ def standardise_chain_names(self) -> None:
807
+ """
808
+ Standardise the TCR, antigen, and MHC chain names to the following convention.
809
+
810
+ Convention:
811
+ - A - MHC chain 1
812
+ - B - MHC chain 2 (eg B2M)
813
+ - C - antigen chain
814
+ - D - TCR delta chain
815
+ - E - TCR gamma chain
816
+
817
+ Note, this mutates the original object.
818
+
819
+ Raises:
820
+ TCRError: if there is more than one antigen or MHC molecules attached to the TCR.
821
+
822
+ """
823
+ self._validate_chain_standardising()
824
+
825
+ new_id = []
826
+ new_child_dict = {}
827
+
828
+ if hasattr(self, 'VG'):
829
+ new_child_dict['E'] = self.child_dict[self.VG]
830
+ self.VG = 'E'
831
+ new_id.append('E')
832
+
833
+ if hasattr(self, 'VD'):
834
+ new_child_dict['D'] = self.child_dict[self.VD]
835
+ self.VD = 'D'
836
+ new_id.append('D')
837
+
838
+ with warnings.catch_warnings():
839
+ warnings.simplefilter('ignore', BiopythonWarning)
840
+
841
+ for chain_id, chain in new_child_dict.items():
842
+ chain.id = chain_id
843
+
844
+ self.child_dict = new_child_dict
845
+
846
+ if hasattr(self, 'antigen') and self.antigen:
847
+ self._standardise_antigen_chain_names()
848
+
849
+ if hasattr(self, 'MHC') and self.MHC:
850
+ self._standardise_mhc_chain_names()
851
+
852
+ with warnings.catch_warnings():
853
+ warnings.simplefilter('ignore', BiopythonWarning)
854
+ self.id = ''.join(new_id)
855
+
458
856
 
459
857
  class dbTCR(TCR):
460
858
  def __init__(self, c1, c2):
@@ -530,3 +928,53 @@ class dbTCR(TCR):
530
928
  if var_domain:
531
929
  for frag in var_domain.get_fragments():
532
930
  yield frag
931
+
932
+ def standardise_chain_names(self) -> None:
933
+ """
934
+ Standardise the TCR, antigen, and MHC chain names to the following convention.
935
+
936
+ Convention:
937
+ - A - MHC chain 1
938
+ - B - MHC chain 2 (eg B2M)
939
+ - C - antigen chain
940
+ - D - TCR delta chain
941
+ - E - TCR beta chain
942
+
943
+ Note, this mutates the original object.
944
+
945
+ Raises:
946
+ TCRError: if there is more than one antigen or MHC molecules attached to the TCR.
947
+
948
+ """
949
+ self._validate_chain_standardising()
950
+
951
+ new_id = []
952
+ new_child_dict = {}
953
+
954
+ if hasattr(self, 'VB'):
955
+ new_child_dict['E'] = self.child_dict[self.VB]
956
+ self.VB = 'E'
957
+ new_id.append('E')
958
+
959
+ if hasattr(self, 'VD'):
960
+ new_child_dict['D'] = self.child_dict[self.VD]
961
+ self.VD = 'D'
962
+ new_id.append('D')
963
+
964
+ with warnings.catch_warnings():
965
+ warnings.simplefilter('ignore', BiopythonWarning)
966
+
967
+ for chain_id, chain in new_child_dict.items():
968
+ chain.id = chain_id
969
+
970
+ self.child_dict = new_child_dict
971
+
972
+ if hasattr(self, 'antigen') and self.antigen:
973
+ self._standardise_antigen_chain_names()
974
+
975
+ if hasattr(self, 'MHC') and self.MHC:
976
+ self._standardise_mhc_chain_names()
977
+
978
+ with warnings.catch_warnings():
979
+ warnings.simplefilter('ignore', BiopythonWarning)
980
+ self.id = ''.join(new_id)