docs: def _process_genotype_features(self, limit=None):
          """
          Here we process the genotype_features file, which lists genotypes
          together with any intrinsic sequence alterations, their zygosity,
          and affected gene.
          Because we don't necessarily get allele pair (VSLC) ids
          in a single row, we iterate through the file and build up a hash
          that contains all of a genotype's partonomy.
          We then assemble a genotype based on that partonomy.
          This table does not list the background genotype/strain:
          that is listed elsewhere.
  
          ZFIN "ALT" objects are mapped to sequence alterations in our system.
  
          By the end of this method, we have built up the intrinsic genotype,
          with Monarch-style labels.
          All ZFIN labels are added as synonyms (including the "sup" html tags).
  
          We make assumptions here that any variants that affect the same locus
          are in trans.
          All of the genotype parts are created as BNodes at the moment,
          to avoid minting new Monarch ids, which means for anyone consuming this
          data they are inherently unstable.  This may change in the future.
  
  
          :param limit:
          :return:
  
          """
          raw = '/'.join((self.rawdir, self.files['geno']['file']))
  
          if self.test_mode:
              graph = self.testgraph
          else:
              graph = self.graph
  
          model = Model(graph)
          taxon_id = self.globaltt['Danio rerio']
  
          geno_hash = {}  # This is used to store the genotype partonomy
          gvc_hash = {}
  
          LOG.info("Processing Genotypes")
          line_counter = 0
          geno = Genotype(graph)
          with open(raw, 'r', encoding="utf8") as csvfile:
              filereader = csv.reader(csvfile, delimiter='\t', quotechar='\"')
              for row in filereader:
                  line_counter += 1
  
                  (genotype_num, genotype_name, genotype_unique_name, allele_num,
                   allele_name, allele_ab, allele_type, allele_disp_type,
                   gene_symbol, gene_num, zygosity, construct_name,
                   construct_num
                   # , empty
                  ) = row
  
                  if self.test_mode and genotype_num not in self.test_ids['genotype']:
                      continue
  
                  # add the genotype to the graph
                  # not adding the genotype label here,
                  # since it doesn't include the background
                  # that will be done in another method
                  genotype_id = 'ZFIN:' + genotype_num.strip()
                  geno.addGenotype(genotype_id, None)
  
                  # add the given name and uniquename as synonyms
                  model.addSynonym(genotype_id, genotype_name)
                  model.addSynonym(genotype_id, genotype_unique_name)
  
                  # store the alleles of the genotype,
                  # in order to use when processing fish
                  if genotype_num not in self.geno_alleles:
                      self.geno_alleles[genotype_num] = set()
                  self.geno_alleles[genotype_num].add(allele_num)
  
                  if genotype_id not in geno_hash:
                      geno_hash[genotype_id] = {}
  
                  genoparts = geno_hash[genotype_id]
  
                  # reassign the allele_type to a proper GENO or SO class
                  # allele_type = self._map_allele_type_to_geno(allele_type)
                  allele_type_id = self.resolve(allele_type, False)
                  if allele_type_id == allele_type:
                      allele_type_id = self.globaltt['unspecified']  # is geno: not zfa:
  
                  allele_id = 'ZFIN:' + allele_num.strip()
  
                  if allele_num != '':
                      self.id_label_map[allele_id] = allele_name
  
                  # alleles in zfin are really sequence alterations in our system
                  geno.addSequenceAlteration(allele_id, allele_name, allele_type_id)
                  model.addSynonym(allele_id, allele_ab)
  
                  # here, we assemble the items into a genotype hash
                  # we need to do this because each row only holds one allele
                  # of a gene but a genotype may have many alleles and therefore
                  # many rows so we loop through the file once to build a hash of
                  # genotype components
                  if gene_num is not None and gene_num.strip() != '':
                      # add the gene to the graph, along with it's symbol
                      # as the primary label
                      gene_id = 'ZFIN:' + gene_num.strip()
                      geno.addGene(gene_id, gene_symbol)
                      self.id_label_map[gene_id] = gene_symbol
  
                      # if it's a transgenic construct,
                      # then we'll have to get the other bits
                      if construct_num is not None and construct_num.strip() != '':
                          construct_id = 'ZFIN:' + construct_num.strip()
                          geno.addSequenceDerivesFrom(allele_id, construct_id)
                          self.id_label_map[construct_id] = construct_name
  
                      # allele to gene
                      if allele_id not in self.variant_loci_genes:
                          self.variant_loci_genes[allele_id] = [gene_id]
                      else:
                          if gene_id not in self.variant_loci_genes[allele_id]:
                              self.variant_loci_genes[allele_id] += [gene_id]
  
                      if gene_id not in genoparts:
                          genoparts[gene_id] = [allele_id]
                      else:
                          genoparts[gene_id] += [allele_id]
  
                      other_allele = self._get_other_allele_by_zygosity(
                          allele_id, zygosity)
                      if other_allele is not None:
                          genoparts[gene_id] += [other_allele]
  
                  else:
                      # if the gene is not known,
                      # still need to add the allele to the genotype hash
                      # these will be added as sequence alterations.
                      genoparts[allele_id] = [allele_id]
                      other_allele = self._get_other_allele_by_zygosity(
                          allele_id, zygosity)
                      if other_allele is not None:
                          genoparts[allele_id] += [other_allele]
  
                  geno_hash[genotype_id] = genoparts
  
                  # fetch the other affected genes,
                  # and make sure they are in the geno hash
                  # we have to do this because some of the affected genes
                  # are not listed in this file
                  genes_from_hash = None
                  if allele_id in self.variant_loci_genes:
                      genes_from_hash = self.variant_loci_genes[allele_id]
                  else:
                      pass
                      # LOG.info('no gene found for %s', allele_id)
  
                  if genes_from_hash is not None \
                          and genes_from_hash != [gene_id] \
                          and gene_id not in genes_from_hash:
                      LOG.info(
                          "***Found genes not in genotype_features for %s: %s",
                          allele_id, genes_from_hash)
                      for gh in genes_from_hash:
                          if gh not in genoparts:
                              genoparts[gh] = [allele_id]
                          else:
                              genoparts[gh] += [allele_id]
  
                          other_allele = self._get_other_allele_by_zygosity(
                              allele_id, zygosity)
                          if other_allele is not None:
                              genoparts[gh].append(other_allele)
  
                  if not self.test_mode and limit is not None and line_counter > limit:
                      break
  
                      # end loop through file
          csvfile.close()
          LOG.info("Finished parsing file")
  
          # ############## BUILD THE INTRINSIC GENOTYPES ###############
          # using the geno_hash, build the genotype parts,
          # and add them to the graph
          # the hash is organized like:
          # genotype_id : {
          #   gene_id : [list, of, alleles], # for located things
          #   allele_id : [list, of, alleles] # for unlocated things
          #   }
          # now loop through the geno_hash, and build the vslcs
  
          LOG.info("Building intrinsic genotypes from partonomy")
          for gt in geno_hash:
              if self.test_mode and re.sub(r'ZFIN:', '', gt) \
                      not in self.test_ids['genotype']:
                  print('skipping ', gt)
                  continue
  
              if gt not in gvc_hash:
                  gvc_hash[gt] = []
              gvcparts = gvc_hash[gt]
  
              for locus_id in geno_hash[gt]:
                  # LOG.info("locus id %s",locus_id)
                  locus_label = self.id_label_map[locus_id]
                  variant_locus_parts = geno_hash.get(gt).get(locus_id)
                  # LOG.info(
                  #   'vl parts: %s',pprint.pformat(variant_locus_parts))
                  # if the locus == part, then it isn't a gene,
                  # rather a variant not in a specific gene
                  if locus_id in variant_locus_parts:
                      # set the gene_id to none
                      gene_id = None
                  else:
                      gene_id = locus_id
  
                  allele1_id = variant_locus_parts[0]
                  if allele1_id not in self.id_label_map:
                      allele1_label = allele1_id
                      LOG.error('allele1 %s not in hash', allele1_id)
                  else:
                      allele1_label = self.id_label_map[allele1_id]
                  allele2_id = None
                  allele2_label = None
                  zygosity_id = None
  
                  if len(variant_locus_parts) > 2:
                      LOG.error(
                          "There may be a problem. >2 parts for this locus (%s): %s",
                          locus_id, variant_locus_parts)
                  elif len(variant_locus_parts) > 1:
                      allele2_id = variant_locus_parts[1]
                      if allele2_id not in ['0', '?']:
                          allele2_label = self.id_label_map[allele2_id]
                      else:
                          allele2_label = allele2_id
                  if allele2_id is not None:
                      if allele2_id == '?':
                          zygosity_id = self.globaltt['indeterminate']
                          allele2_id = 'UN'
                      elif allele2_id == '0':
                          zygosity_id = self.globaltt['hemizygous']
                      elif allele1_id != allele2_id:
                          zygosity_id = self.globaltt['compound heterozygous']
                      elif allele1_id == allele2_id:
                          zygosity_id = self.globaltt['homozygous']
                  else:
                      zygosity_id = self.globaltt['simple heterozygous']
                      allele2_label = '+'
                      allele2_id = 'WT'
  
                  # make variant_loci
                  vloci2 = vloci2_label = None
                  if gene_id is not None:
                      vloci1 = self._make_variant_locus_id(gene_id, allele1_id)
                      vloci1_label = geno.make_variant_locus_label(
                          locus_label, allele1_label)
                      geno.addSequenceAlterationToVariantLocus(
                          allele1_id, vloci1)
                      geno.addAlleleOfGene(vloci1, gene_id)
                      model.addIndividualToGraph(
                          vloci1, vloci1_label, self.globaltt['variant_locus'])
                      if allele2_id is not None and allele2_id not in ['WT', '0', 'UN']:
                          vloci2 = self._make_variant_locus_id(
                              gene_id, allele2_id)
                          vloci2_label = geno.make_variant_locus_label(
                              locus_label, allele2_label)
                          geno.addSequenceAlterationToVariantLocus(
                              allele2_id, vloci2)
                          model.addIndividualToGraph(
                              vloci2, vloci2_label, self.globaltt['variant_locus'])
                          geno.addAlleleOfGene(vloci2, gene_id)
                  else:
                      vloci1 = allele1_id
                      vloci1_label = allele1_label
                      vloci2 = None
                      if allele2_id not in ['WT', '0', 'UN']:
                          vloci2 = allele2_id
                      vloci2_label = allele2_label
  
                  # create the vslc
                  gene_label = ''
                  if gene_id is None:
                      gn = 'UN'
                  else:
                      gn = gene_id
                      gene_label = self.id_label_map[gene_id]
  
                  # TODO also consider adding this to Genotype.py
                  vslc_id = '-'.join((gn, allele1_id, allele2_id))
                  vslc_id = '_:' + re.sub(r'(ZFIN)?:', '', vslc_id)
  
                  vslc_label = geno.make_vslc_label(
                      gene_label, allele1_label, allele2_label)
  
                  # add to global hash
                  self.id_label_map[vslc_id] = vslc_label
  
                  model.addIndividualToGraph(
                      vslc_id, vslc_label,
                      self.globaltt['variant single locus complement'])
                  geno.addPartsToVSLC(
                      vslc_id, vloci1, vloci2, zygosity_id,
                      self.globaltt['has_variant_part'],
                      self.globaltt['has_variant_part'])
  
                  gvcparts += [vslc_id]
  
              gvc_hash[gt] = gvcparts
  
          # end loop through geno_hash
  
          LOG.info('Finished finding all the intrinsic genotype parts')
          LOG.info('Build pretty genotype labels')
          # now loop through the gvc_hash, and build the gvc
          for gt in gvc_hash:
              if self.test_mode and re.sub(r'ZFIN:', '', gt) \
                      not in self.test_ids['genotype']:
                  continue
  
              gvc_parts = gvc_hash[gt]
  
              # only need to make a gvc specifically if there's >1 vslc
              if len(gvc_parts) > 1:
                  gvc_labels = []
                  # put these in order so they will always make the same id
                  gvc_parts.sort()
  
                  gvc_id = '-'.join(gvc_parts)
                  gvc_id = re.sub(r'(ZFIN)?:', '', gvc_id)
                  gvc_id = '_:' + re.sub(r'^_*', '', gvc_id)
  
                  for vslc_id in gvc_parts:
                      # add the vslc to the gvc
                      geno.addVSLCtoParent(vslc_id, gvc_id)
  
                      # build the gvc label
                      vslc_label = self.id_label_map[vslc_id]
                      if vslc_label is not None:
                          gvc_labels += [vslc_label]
                      else:
                          gvc_labels += [vslc_id]
  
                  gvc_labels.sort()
                  gvc_label = '; '.join(gvc_labels)
  
                  # add the gvc to the id-label hash
                  self.id_label_map[gvc_id] = gvc_label
  
                  # add the gvc
                  model.addIndividualToGraph(
                      gvc_id, gvc_label, self.globaltt['genomic_variation_complement'])
              elif len(gvc_parts) == 1:
                  # assign the vslc to be also a gvc
                  vslc_id = gvc_parts[0]
                  gvc_id = vslc_id
                  gvc_label = self.id_label_map[vslc_id]
                  model.addType(vslc_id, self.globaltt['genomic_variation_complement'])
              else:
                  gvc_id = None
                  gvc_label = ''
                  LOG.error("No GVC parts for %s", gt)
  
              if gt in self.genotype_backgrounds:
                  background_id = self.genotype_backgrounds[gt]
                  if background_id in self.id_label_map:
                      background_label = self.id_label_map[background_id]
                  else:
                      background_label = background_id
                      LOG.error("We don't have the label for %s stored", background_id)
  
              else:
                  background_num = re.sub(r'ZFIN:', '', gt)
                  background_id = '_:bkgd-'+background_num
                  background_label = 'n.s. (' + background_num + ')'
                  background_desc = 'This genomic background is unknown. ' +\
                      'This is a placeholder background for ' + gt + '.'
                  # there is no background for this genotype;
                  # need to add the taxon to this one!
                  # make an anonymous background for this genotype
                  geno.addGenomicBackground(
                      background_id, background_label, None, background_desc)
                  geno.addGenomicBackgroundToGenotype(background_id, gt)
                  background_label = 'n.s.'
  
              geno.addTaxon(taxon_id, background_id)
  
              genotype_name = gvc_label + ' [' + background_label + ']'
  
              geno.addGenotype(gt, genotype_name)
  
              self.id_label_map[gt] = genotype_name
  
              # Add the GVC to the genotype
              geno.addParts(gvc_id, gt, self.globaltt['has_variant_part'])
  
              # end of gvc loop
  
          # end of genotype loop
  
          # TODO this is almost complete;
          # deficiencies with >1 locus deleted are still not right
          LOG.info("Finished building genotype labels")
  
          LOG.info("Done with genotypes")
  
          return