code: def get_chain_mutations(self, pdb_id_1, chain_1, pdb_id_2, chain_2):
          '''Returns a list of tuples each containing a SEQRES Mutation object and an ATOM Mutation object representing the
             mutations from pdb_id_1, chain_1 to pdb_id_2, chain_2.
             SequenceMaps are constructed in this function between the chains based on the alignment.
             PDBMutationPair are returned as they are hashable and amenable to Set construction to eliminate duplicates.
             '''
          p1 = self.add_pdb(pdb_id_1)
          p2 = self.add_pdb(pdb_id_2)
          sifts_1, pdb_1 = p1['sifts'], p1['pdb']
          sifts_2, pdb_2 = p2['sifts'], p2['pdb']
          seqres_to_atom_sequence_maps_1 = sifts_1.seqres_to_atom_sequence_maps.get(chain_1, {})
          seqres_1, atom_1 = pdb_1.seqres_sequences.get(chain_1), pdb_1.atom_sequences.get(chain_1)
          seqres_2, atom_2 = pdb_2.seqres_sequences.get(chain_2), pdb_2.atom_sequences.get(chain_2)
          if not seqres_1: raise Exception('No SEQRES sequence for chain {0} of {1}.'.format(chain_1, pdb_1))
          if not atom_1: raise Exception('No ATOM sequence for chain {0} of {1}.'.format(chain_1, pdb_1))
          if not seqres_2: raise Exception('No SEQRES sequence for chain {0} of {1}.'.format(chain_2, pdb_2))
          if not atom_2: raise Exception('No ATOM sequence for chain {0} of {1}.'.format(chain_2, pdb_2))
          seqres_str_1 = str(seqres_1)
          seqres_str_2 = str(seqres_2)
          sa = SequenceAligner()
          sa.add_sequence('{0}_{1}'.format(pdb_id_1, chain_1), seqres_str_1)
          sa.add_sequence('{0}_{1}'.format(pdb_id_2, chain_2), seqres_str_2)
          sa.align()
          seqres_residue_mapping, seqres_match_mapping = sa.get_residue_mapping()
          seqres_sequence_map = SequenceMap()
          assert(sorted(seqres_residue_mapping.keys()) == sorted(seqres_match_mapping.keys()))
          for k, v in seqres_residue_mapping.iteritems():
              seqres_sequence_map.add(k, v, seqres_match_mapping[k])
          self.seqres_sequence_maps[(pdb_id_1, chain_1)][(pdb_id_2, chain_2)] = seqres_sequence_map
          mutations = []
          clustal_symbols = SubstitutionScore.clustal_symbols
          for seqres_res_id, v in seqres_match_mapping.iteritems():
              if clustal_symbols[v.clustal] != '*':
                  seqres_wt_residue = seqres_1[seqres_res_id]
                  seqres_mutant_residue = seqres_2[seqres_residue_mapping[seqres_res_id]]
                  atom_res_id = None
                  atom_chain_res_id = seqres_to_atom_sequence_maps_1.get(seqres_res_id)
                  try:
                      if atom_chain_res_id:
                          assert(atom_chain_res_id[0] == chain_1)
                          atom_residue = atom_1[atom_chain_res_id]
                          atom_res_id = atom_chain_res_id[1:]
                          assert(atom_residue.ResidueAA == seqres_wt_residue.ResidueAA)
                          assert(atom_residue.ResidueID == atom_res_id)
                  except:
                      atom_res_id = None
                      if seqres_wt_residue.ResidueAA != 'X':
                          raise
                  seqres_mutation = ChainMutation(seqres_wt_residue.ResidueAA, seqres_res_id,seqres_mutant_residue.ResidueAA, Chain = chain_1)
                  atom_mutation = None
                  if atom_res_id:
                      atom_mutation = ChainMutation(seqres_wt_residue.ResidueAA, atom_res_id, seqres_mutant_residue.ResidueAA, Chain = chain_1)
                  mutations.append(PDBMutationPair(seqres_mutation, atom_mutation))
          return mutations