docs: def align_seqprop_to_structprop(self, seqprop, structprop, chains=None, outdir=None,
                                      engine='needle', structure_already_parsed=False, parse=True, force_rerun=False,
                                      **kwargs):
          """Run and store alignments of a SeqProp to chains in the ``mapped_chains`` attribute of a StructProp.
  
          Alignments are stored in the sequence_alignments attribute, with the IDs formatted as
          ``<SeqProp_ID>_<StructProp_ID>-<Chain_ID>``. Although it is more intuitive to align to individual ChainProps,
          StructProps should be loaded as little as possible to reduce run times so the alignment is done to the entire
          structure.
  
          Args:
              seqprop (SeqProp): SeqProp object with a loaded sequence
              structprop (StructProp): StructProp object with a loaded structure
              chains (str, list): Chain ID or IDs to map to. If not specified, ``mapped_chains`` attribute is inspected
                  for chains. If no chains there, all chains will be aligned to.
              outdir (str): Directory to output sequence alignment files (only if running with needle)
              engine (str): ``biopython`` or ``needle`` - which pairwise alignment program to use.
                  ``needle`` is the standard EMBOSS tool to run pairwise alignments.
                  ``biopython`` is Biopython's implementation of needle. Results can differ!
              structure_already_parsed (bool): If the structure has already been parsed and the chain sequences are
                  stored. Temporary option until Hadoop sequence file is implemented to reduce number of times a
                  structure is parsed.
              parse (bool): Store locations of mutations, insertions, and deletions in the alignment object (as an annotation)
              force_rerun (bool): If alignments should be rerun
              **kwargs: Other alignment options
  
          Todo:
              * Document **kwargs for alignment options
  
          """
  
          if not outdir:
              outdir = self.sequence_dir
  
          if not structure_already_parsed:
              # Parse the structure so chain sequences are stored
              structprop.parse_structure()
              # XTODO: remove and use the "parsed" attribute in a structprop instead
  
          if chains:
              chains_to_align_to = ssbio.utils.force_list(chains)
          elif structprop.mapped_chains:
              chains_to_align_to = structprop.mapped_chains
          else:
              log.warning('{}-{}: no chains specified in structure to align to, all chains will be aligned to'.format(seqprop.id,
                                                                                                                      structprop.id))
              chains_to_align_to = structprop.chains.list_attr('id')
  
          for chain_id in chains_to_align_to:
              full_structure_id = '{}-{}'.format(structprop.id, chain_id)
              aln_id = '{}_{}'.format(seqprop.id, full_structure_id)
              outfile = '{}.needle'.format(aln_id)
  
              if self.sequence_alignments.has_id(aln_id) and not force_rerun:
                  log.debug('{}: alignment already completed, skipping'.format(aln_id))
                  continue
  
              log.debug('{}: running pairwise alignment to structure {}, chain {}'.format(seqprop.id,
                                                                                          structprop.id,
                                                                                          chain_id))
  
              # Check if the chain ID actually exists
              if structprop.chains.has_id(chain_id):
                  chain_prop = structprop.chains.get_by_id(chain_id)
                  chain_seq_record = chain_prop.seq_record
              else:
                  log.error('{}: chain not present in structure file!'.format(full_structure_id))
                  continue
  
              # Check if the chain sequence was parsed
              if not chain_seq_record:
                  log.error('{}: chain sequence not available, was structure parsed?'.format(full_structure_id))
                  continue
  
              # Run the pairwise alignment
              try:
                  aln = ssbio.protein.sequence.utils.alignment.pairwise_sequence_alignment(a_seq=seqprop,
                                                                                           a_seq_id=seqprop.id,
                                                                                           b_seq=chain_seq_record,
                                                                                           b_seq_id=full_structure_id,
                                                                                           engine=engine,
                                                                                           outdir=outdir,
                                                                                           outfile=outfile,
                                                                                           force_rerun=force_rerun)
              except ValueError:
                  log.error('{}: alignment failed to run, unable to check structure\'s chain'.format(full_structure_id))
                  continue
  
              # Add an identifier to the MultipleSeqAlignment object for storage in a DictList
              aln.id = aln_id
              # Add annotations to keep track of what was aligned
              aln.annotations['a_seq'] = seqprop.id
              aln.annotations['b_seq'] = full_structure_id
              aln.annotations['structure_id'] = structprop.id
              aln.annotations['chain_id'] = chain_id
              aln.annotations['ssbio_type'] = 'structalign'
  
              # Optionally parse for locations of mutations, deletions, and insertions
              # Store mapping to chain index as letter annotations in the sequence
              # Store locations in the alignment's annotations
              if parse:
                  aln_df = ssbio.protein.sequence.utils.alignment.get_alignment_df(a_aln_seq=str(list(aln)[0].seq),
                                                                                   b_aln_seq=str(list(aln)[1].seq))
  
                  chain_indices = aln_df[pd.notnull(aln_df.id_a_pos)].id_b_pos.tolist()
                  seqprop.letter_annotations['{}_chain_index'.format(aln_id)] = chain_indices
  
                  aln.annotations['mutations'] = ssbio.protein.sequence.utils.alignment.get_mutations(aln_df)
                  aln.annotations['deletions'] = ssbio.protein.sequence.utils.alignment.get_deletions(aln_df)
                  aln.annotations['insertions'] = ssbio.protein.sequence.utils.alignment.get_insertions(aln_df)
  
              if force_rerun and self.sequence_alignments.has_id(aln.id):
                  self.sequence_alignments.remove(aln.id)
              self.sequence_alignments.append(aln)