Repository: popsim-consortium/stdpopsim (language: python)

Issue:
Not all genomes have chromosome synonyms
While updating some of the docs, realized that the tutorial section "Calculating the allele frequency spectrum" would not run as written, as "chr4" does not exist as a synonym for chromosome 4. We should probably include these basic synonyms for all species genomes.


Discussion / hints:
This is a function of the upstream data repos @apragsdale - "no" would be the short answer. 

They should exist for humans though - do the synonyms exist for the other chrs?
I see. That's too bad - as far as I can see, it's only humans that have synonyms. This means that while we previously could specify chr2L for DroMel and chr4 for AraTha (as the docs currently do), these are errors now. I think it's worth adding these obvious synonyms by hand for all species.
Well, feel free (but in a way that doesn't get clobbered by automatic updates from the repository).

Fix patch:
diff --git a/stdpopsim/catalog/AedAeg/species.py b/stdpopsim/catalog/AedAeg/species.py
index 2cc8ba363..768f7a707 100644
--- a/stdpopsim/catalog/AedAeg/species.py
+++ b/stdpopsim/catalog/AedAeg/species.py
@@ -63,7 +63,7 @@
         _KeightleyEtAl,
     ],
 )
-
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="AedAeg",
diff --git a/stdpopsim/catalog/AnaPla/species.py b/stdpopsim/catalog/AnaPla/species.py
index e77bf614a..0f15f21ec 100644
--- a/stdpopsim/catalog/AnaPla/species.py
+++ b/stdpopsim/catalog/AnaPla/species.py
@@ -167,6 +167,7 @@
         _HuangEtAl2006,
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="AnaPla",
diff --git a/stdpopsim/catalog/AnoCar/species.py b/stdpopsim/catalog/AnoCar/species.py
index 8d653d75d..83c7f4ec8 100644
--- a/stdpopsim/catalog/AnoCar/species.py
+++ b/stdpopsim/catalog/AnoCar/species.py
@@ -68,6 +68,7 @@
     mutation_rate=_mutation_rate,
     citations=[_BourgeoisEtAl],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="AnoCar",
diff --git a/stdpopsim/catalog/AnoGam/species.py b/stdpopsim/catalog/AnoGam/species.py
index 197f337fc..39c003e31 100644
--- a/stdpopsim/catalog/AnoGam/species.py
+++ b/stdpopsim/catalog/AnoGam/species.py
@@ -58,6 +58,7 @@
         _PombiEtAl,
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="AnoGam",
diff --git a/stdpopsim/catalog/AraTha/species.py b/stdpopsim/catalog/AraTha/species.py
index 51d77a87d..22362458a 100644
--- a/stdpopsim/catalog/AraTha/species.py
+++ b/stdpopsim/catalog/AraTha/species.py
@@ -49,6 +49,7 @@
         ),
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="AraTha",
diff --git a/stdpopsim/catalog/BosTau/species.py b/stdpopsim/catalog/BosTau/species.py
index 386f46b2e..948959bd9 100644
--- a/stdpopsim/catalog/BosTau/species.py
+++ b/stdpopsim/catalog/BosTau/species.py
@@ -78,6 +78,7 @@
         _MaEtAl,
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="BosTau",
diff --git a/stdpopsim/catalog/CanFam/species.py b/stdpopsim/catalog/CanFam/species.py
index cfa8c3ffd..f8e087538 100644
--- a/stdpopsim/catalog/CanFam/species.py
+++ b/stdpopsim/catalog/CanFam/species.py
@@ -102,6 +102,7 @@
         _LindbladTohEtAl.because(stdpopsim.CiteReason.ASSEMBLY),
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="CanFam",
diff --git a/stdpopsim/catalog/ChlRei/species.py b/stdpopsim/catalog/ChlRei/species.py
index 5cdd435fd..d33c9dc4a 100644
--- a/stdpopsim/catalog/ChlRei/species.py
+++ b/stdpopsim/catalog/ChlRei/species.py
@@ -77,6 +77,7 @@
         ),
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="ChlRei",
diff --git a/stdpopsim/catalog/DroMel/species.py b/stdpopsim/catalog/DroMel/species.py
index 495e8da49..e4f7718e8 100644
--- a/stdpopsim/catalog/DroMel/species.py
+++ b/stdpopsim/catalog/DroMel/species.py
@@ -66,7 +66,6 @@
         )
     )
 
-
 _genome = stdpopsim.Genome(
     chromosomes=_chromosomes,
     assembly_name=genome_data.data["assembly_name"],
@@ -78,6 +77,7 @@
         _ComeronEtAl.because(stdpopsim.CiteReason.REC_RATE),
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="DroMel",
diff --git a/stdpopsim/catalog/DroSec/species.py b/stdpopsim/catalog/DroSec/species.py
index 57045a702..3945e4a12 100644
--- a/stdpopsim/catalog/DroSec/species.py
+++ b/stdpopsim/catalog/DroSec/species.py
@@ -71,6 +71,7 @@
         _LegrandEtAl,
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 # Generation time was set to that used by
 # by Legrand et al. in an ABC selection of demographic
diff --git a/stdpopsim/catalog/EscCol/species.py b/stdpopsim/catalog/EscCol/species.py
index 8984b8717..430a81257 100644
--- a/stdpopsim/catalog/EscCol/species.py
+++ b/stdpopsim/catalog/EscCol/species.py
@@ -48,7 +48,7 @@
         _blattner_et_al.because(stdpopsim.CiteReason.ASSEMBLY),
     ],
 )
-
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="EscCol",
@@ -70,5 +70,4 @@
     ],
 )
 
-
 stdpopsim.register_species(_species)
diff --git a/stdpopsim/catalog/HelMel/species.py b/stdpopsim/catalog/HelMel/species.py
index fe06751ac..43c487845 100644
--- a/stdpopsim/catalog/HelMel/species.py
+++ b/stdpopsim/catalog/HelMel/species.py
@@ -70,6 +70,7 @@
         ),
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="HelMel",
diff --git a/stdpopsim/catalog/HomSap/species.py b/stdpopsim/catalog/HomSap/species.py
index 01517060e..d4f521bf8 100644
--- a/stdpopsim/catalog/HomSap/species.py
+++ b/stdpopsim/catalog/HomSap/species.py
@@ -87,6 +87,7 @@
         _hapmap2007.because(stdpopsim.CiteReason.REC_RATE),
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="HomSap",
diff --git a/stdpopsim/catalog/PonAbe/species.py b/stdpopsim/catalog/PonAbe/species.py
index b7b00adea..16201c919 100644
--- a/stdpopsim/catalog/PonAbe/species.py
+++ b/stdpopsim/catalog/PonAbe/species.py
@@ -67,6 +67,7 @@
     assembly_accession=genome_data.data["assembly_accession"],
     citations=[_nater2017],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 _species = stdpopsim.Species(
     id="PonAbe",
diff --git a/stdpopsim/catalog/StrAga/species.py b/stdpopsim/catalog/StrAga/species.py
index 8f69bed77..ce1414eda 100644
--- a/stdpopsim/catalog/StrAga/species.py
+++ b/stdpopsim/catalog/StrAga/species.py
@@ -57,6 +57,7 @@
         _DaCunha_et_al.because(stdpopsim.CiteReason.MUT_RATE),
     ],
 )
+stdpopsim.utils.append_common_synonyms(_genome)
 
 # Generation time :
 #  No estimation was done for S agalactiae, but we can use estimates from E coli (which
diff --git a/stdpopsim/utils.py b/stdpopsim/utils.py
index f7b5d487f..bf1756ef1 100644
--- a/stdpopsim/utils.py
+++ b/stdpopsim/utils.py
@@ -148,3 +148,25 @@ def mask_tree_sequence(ts, mask_intervals, exclude):
     else:
         ts = ts.delete_intervals(mask_intervals)
     return ts
+
+
+def append_common_synonyms(genome):
+    """
+    For common chromosome IDs, add their obvious synonyms if they do not exist already.
+    The input is a stdpopsim.Genome object, and synonyms are appended in place.
+    """
+
+    def add_if_unique(chrom, syn):
+        if syn not in chrom.synonyms:
+            chrom.synonyms.append(syn)
+
+    for chrom in genome.chromosomes:
+        # "1" -> "chr1" and "2L" -> "chr2L"
+        if chrom.id[0].isdigit():
+            add_if_unique(chrom, "chr" + chrom.id)
+        # sex chroms
+        if chrom.id in ["X", "Y", "W", "Z"]:
+            add_if_unique(chrom, "chr" + chrom.id)
+        # Mt
+        if chrom.id == "MT":
+            add_if_unique(chrom, "chr" + "M")


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 3f77df832..4c8b7c701 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -8,6 +8,7 @@
 import tempfile
 
 from stdpopsim import utils
+from stdpopsim import Chromosome, Genome
 
 
 class TestValidDemographicModelId(unittest.TestCase):
@@ -372,3 +373,64 @@ def filt(info):
                     utils.untar(tar, dest)
                 rm_f(filename)
                 rm_f(tar)
+
+
+class TestSynonyms(unittest.TestCase):
+    def test_add_digit_autosomes(self):
+        chroms = [
+            Chromosome(
+                id="1",
+                length=100,
+                synonyms=[],
+                mutation_rate=1e-8,
+                recombination_rate=1e-8,
+            )
+        ]
+        genome = Genome(chroms)
+        utils.append_common_synonyms(genome)
+        self.assertTrue("chr1" in genome.chromosomes[0].synonyms)
+
+    def test_add_drosophila_like_synonyms(self):
+        chroms = [
+            Chromosome(
+                id="1a",
+                length=100,
+                synonyms=[],
+                mutation_rate=1e-8,
+                recombination_rate=1e-8,
+            )
+        ]
+        genome = Genome(chroms)
+        utils.append_common_synonyms(genome)
+        self.assertTrue("chr1a" in genome.chromosomes[0].synonyms)
+
+    def test_add_sex_chrom_synonyms(self):
+        chroms = [
+            Chromosome(
+                id=s,
+                length=100,
+                synonyms=[],
+                mutation_rate=1e-8,
+                recombination_rate=1e-8,
+            )
+            for s in ["X", "Y", "Z", "W"]
+        ]
+        genome = Genome(chroms)
+        utils.append_common_synonyms(genome)
+        for chrom in genome.chromosomes:
+            self.assertTrue("chr" + chrom.id in chrom.synonyms)
+
+    def test_add_only_unique_synonyms(self):
+        chroms = [
+            Chromosome(
+                id="1",
+                length=100,
+                synonyms=["chr1"],
+                mutation_rate=1e-8,
+                recombination_rate=1e-8,
+            )
+        ]
+        genome = Genome(chroms)
+        utils.append_common_synonyms(genome)
+        self.assertTrue("chr1" in genome.chromosomes[0].synonyms)
+        self.assertEqual(len(genome.chromosomes[0].synonyms), 1)