Repository: Ecogenomics/GTDBTk (language: python)

Issue:
Problem with empty files (such as produced by metabat)
Metabat2 produces a few files during binning that can be empty, such as:  
sample.lowDepth.fa  
sample.tooShort.fa

GTDB-Tk thus has problems processing the folder containing the binned .fa files, because Mash produces an error for empty files. Should be an easy fix to remove size zero files from the list of files to process I hope - it would save me a fix to my pipeline anyway.

GTDB-Tk log:
```
[2023-08-02 09:44:22] INFO: GTDB-Tk v2.3.0
[2023-08-02 09:44:22] INFO: gtdbtk classify_wf --genome_dir scratch/takada/metabat/ -x fa --out_dir scratch/takada/annotation/ --cpus 32 --prefix takada --mash_db scratch/takada/annotation/takada.msh
[2023-08-02 09:44:22] INFO: Using GTDB-Tk reference data version r214: /nfs/nas22/fs2201/biol_micro_unix_modules/modules/software/GTDB-Tk/2.3.0-foss-2020b/data
[2023-08-02 09:44:22] INFO: Loading reference genomes.
[2023-08-02 09:44:22] INFO: Using Mash version 2.3
[2023-08-02 09:44:22] INFO: Creating Mash sketch file: scratch/takada/annotation/classify/ani_screen/intermediate_results/mash/takada.user_query_sketch.msh
[2023-08-02 09:44:22] INFO: Completed 2 genomes in 0.01 seconds (195.71 genomes/second).
[2023-08-02 09:44:22] ERROR: Error generating Mash sketch:
[2023-08-02 09:44:22] ERROR: Controlled exit resulting from an unrecoverable error or warning.
```

Mash log (edited) for command `mash sketch -l -p 32 <(ls scratch/takada/metabat/*fa) -o scratch/takada/annotation/takada.msh -k 16 -s 5000`:
```
<lots of files that work>
ERROR: Did not find fasta records in "input files".
```

AttributeError: module 'numpy' has no attribute 'bool'

## Environment
- pip list
Package    Version
---------- -------
DendroPy   4.5.2
gtdbtk     2.1.1
numpy      1.24.1
pip        22.3.1
setuptools 66.1.0
tqdm       4.64.1
wheel      0.38.4
## Server information
- RAM:49281284 kB
- OS: NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"

## Debugging information
- 
[gtdbtk.log](https://github.com/Ecogenomics/GTDBTk/files/10471602/gtdbtk.log)


## Additional comments
<!--- Include any additional comments which may help with debugging here. -->

feat(fix of #576):
The summary files are still produces even if all genomes fail the prodigal step
Attempting to add duplicate row
Hello,

I annotated some bins with **GTDB-TK v2.3.2** and always encountered this error.
Is there any parameter I can set to skip this error?

```
==> Running pplacer v1.1.alpha19-0-g807f6f3 analysis on gtdbtk-classify/classify/intermediate_results/pplacer/tree_1/user_msa_file.fasta.
==> Step 2 of 9: Pre-masking sequences.
[2024-03-08 10:52:54] INFO: Calculating RED values based on reference tree.
[2024-03-08 10:53:00] TASK: Traversing tree to determine classification method.
[2024-03-08 10:53:00] INFO: Completed 1 genome in 0.00 seconds (1,468.59 genomes/second).
[2024-03-08 10:53:00] TASK: Calculating average nucleotide identity using FastANI (v1.33).
[2024-03-08 10:53:07] INFO: Completed 36 comparisons in 6.71 seconds (5.37 comparisons/second).
[2024-03-08 10:53:08] INFO: 0 genome(s) have been classified using FastANI and pplacer.
[2024-03-08 10:53:09] ERROR: Attempting to add duplicate row: X01
[2024-03-08 10:53:09] ERROR: Controlled exit resulting from an unrecoverable error or warning.
```

Specify in documentation that the `--genes` parameter requires proteins as input.
### Problem Description

Currently, the CLI and the documentation state the following for the `--genes` parameter: 

```
indicates input files contain called genes (skip gene calling).Warning: This flag will skip the ANI comparison steps (ani_screen and classification).
```

I wasn't sure if I should provide the CDS gene predictions in nucleotide format or the protein translations of those CDS.

When I fed the pipeline my `cds.fna` files, it failed as expected. However, I had to dig into GitHub pull requests and source code to determine that the `--genes` parameter requires proteins as input.

### Proposed Solution

Specify in the documentation that the pipeline expects proteins as input when the `--genes` flag is specified.

```
indicates input files contain predicted proteins (skip gene calling). Warning: This flag will skip the ANI comparison steps (ani_screen and classification).
```

classify_wf errors out when all inputs are unclassified
The classify_ wf pipeline will error out if all input query genomes are unclassifiable as it does not produce a bac120 or ar53 summary file. However, if the input genomes contain at least one classifiable genome, then all other unclassifiable genomes get placed into the bac120 summary file and look like this:

```
user_genome	classification	fastani_reference	fastani_reference_radius	fastani_taxonomy	fastani_ani	fastani_af	closest_placement_reference	closest_placement_radius	closest_placement_taxonomy	closest_placement_ani	closest_placement_af	pplacer_taxonomy	classification_method	note	other_related_references(genome_id,species_name,radius,ANI,AF)	msa_percent	translation_table	red_value	warnings
00949864-6241-46e4-8c40-0efc0b7c7c55	Unclassified	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	N/A	No bacterial or archaeal marker
```

In this example, I ran one classifiable Archaeal genome and an unclassifiable genome which produced both the ar53 and bac120 summary files in the classify output.  When running only the unclassifiable genome, I get a FileNotFound Error.

It would be nice if the bac120/ar53 summary files still get produced and take the form of the example above even if all inputs are unclassifiable instead of throwing an error.


Discussion / hints:
Hello,
Thanks for your feedback,
We will add a test to disregard any empty genome files. This will be available in the next Tk release 
The following installation solved the issue: 
conda create -n gtdbtk-2.1.1 -c conda-forge -c bioconda gtdbtk=2.1.1 numpy=1.23.1  






Fix patch:
diff --git a/Dockerfile b/Dockerfile
index 1ea956aa..696d7350 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,6 @@
 # How to build and deploy the Docker image:
 # docker build --build-arg VER=1.2.3 --no-cache -t ecogenomic/gtdbtk:latest -t ecogenomic/gtdbtk:1.2.3 .
+# docker run -v /host/gtdbtk_io:/data -v /host/release_data:/refdata ecogenomic/gtdbtk classify_wf --genome_dir /data/genomes --out_dir /data/output
 # docker push ecogenomic/gtdbtk:latest && sudo docker push ecogenomic/gtdbtk:1.2.3
 
 FROM python:3.8-slim-bullseye
@@ -15,6 +16,8 @@ RUN apt-get update -y -m && \
         libgomp1 \
         libgsl25 \
         libgslcblas0 \
+        build-essential \
+        curl \
         hmmer=3.* \
         mash=2.2.* \
         prodigal=1:2.6.* \
@@ -22,7 +25,10 @@ RUN apt-get update -y -m && \
         unzip && \
     apt-get clean && \
     rm -rf /var/lib/apt/lists/* && \
-    ln -s /usr/bin/fasttreeMP /usr/bin/FastTreeMP
+    ln -s /usr/bin/fasttreeMP /usr/bin/FastTreeMP && \
+    curl https://sh.rustup.rs -sSf | sh -s -- -y
+
+ENV PATH="/root/.cargo/bin:${PATH}"
 
 # ---------------------------------------------------------------------------- #
 # ----------------------------- INSTALL PPLACER ------------------------------ #
@@ -34,11 +40,15 @@ RUN wget https://github.com/matsen/pplacer/releases/download/v1.1.alpha19/pplace
     rm -rf pplacer-Linux-v1.1.alpha19
 
 # ---------------------------------------------------------------------------- #
-# ----------------------------- INSTALL FASTANI ------------------------------ #
+# ------------------------------ INSTALL SKANI ------------------------------- #
 # ---------------------------------------------------------------------------- #
-RUN wget https://github.com/ParBLiSS/FastANI/releases/download/v1.32/fastANI-Linux64-v1.32.zip -q && \
-    unzip fastANI-Linux64-v1.32.zip -d /usr/bin && \
-    rm fastANI-Linux64-v1.32.zip
+
+RUN wget https://github.com/bluenote-1577/skani/archive/refs/tags/v0.2.1.tar.gz
+RUN tar -xvf v0.2.1.tar.gz
+RUN cd skani-0.2.1 && cargo install --path . --root /usr
+RUN chmod +x /usr/bin/skani
+RUN cd ../
+RUN rm -rf v0.2.1.tar.gz skani-0.2.1
 
 # ---------------------------------------------------------------------------- #
 # --------------------- SET GTDB-TK MOUNTED DIRECTORIES ---------------------- #
@@ -51,7 +61,7 @@ ENV GTDBTK_DATA_PATH="/refdata/"
 # --------------------------- INSTALL PIP PACKAGES --------------------------- #
 # ---------------------------------------------------------------------------- #
 RUN python -m pip install --upgrade pip && \
-    python -m pip install gtdbtk==${VER}
+    python -m pip install gtdbtk==${VER} \
 
 # ---------------------------------------------------------------------------- #
 # ---------------------------- SET THE ENTRYPOINT ---------------------------- #
diff --git a/README.md b/README.md
index 4d49df3e..cff01462 100644
--- a/README.md
+++ b/README.md
@@ -37,8 +37,8 @@ Documentation for GTDB-Tk can be found [here](https://ecogenomics.github.io/GTDB
 
 ## ✨ New Features
 
-GTDB-Tk v2.3.0+ includes the following new features:
-- New functionality ``convert_to_species`` function to convert GTDB genome IDs to GTDB species names
+GTDB-Tk v2.4.0+ includes the following new features:
+- `FastANI` has been replaced by `skani` as the primary tool for computing Average Nucleotide Identity (ANI).Users may notice slight variations in the results compared to those obtained using `FastANI`.
 
 
 ## 📈 Performance
@@ -63,6 +63,7 @@ We strongly encourage you to cite the following 3rd party dependencies:
 
 * Matsen FA, et al. 2010. [pplacer: linear time maximum-likelihood and Bayesian phylogenetic placement of sequences onto a fixed reference tree](https://www.ncbi.nlm.nih.gov/pubmed/21034504). <i>BMC Bioinformatics</i>, 11:538.
 * Jain C, et al. 2019. [High-throughput ANI Analysis of 90K Prokaryotic Genomes Reveals Clear Species Boundaries](https://www.nature.com/articles/s41467-018-07641-9). <i>Nat. Communications</i>, doi: 10.1038/s41467-018-07641-9.
+* Shaw J. and Yu Y.W. 2023. [Fast and robust metagenomic sequence comparison through sparse chaining with skani](https://www.nature.com/articles/s41592-023-02018-3). <i>Nature Methods</i>, 20, pages1661–1665 (2023).
 * Hyatt D, et al. 2010. [Prodigal: prokaryotic gene recognition and translation initiation site identification](https://www.ncbi.nlm.nih.gov/pubmed/20211023). <i>BMC Bioinformatics</i>, 11:119. doi: 10.1186/1471-2105-11-119.
 * Price MN, et al. 2010. [FastTree 2 - Approximately Maximum-Likelihood Trees for Large Alignments](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2835736/). <i>PLoS One</i>, 5, e9490.
 * Eddy SR. 2011. [Accelerated profile HMM searches](https://www.ncbi.nlm.nih.gov/pubmed/22039361). <i>PLOS Comp. Biol.</i>, 7:e1002195.
diff --git a/docs/src/announcements.rst b/docs/src/announcements.rst
index 2a6bfdf8..cea910f6 100644
--- a/docs/src/announcements.rst
+++ b/docs/src/announcements.rst
@@ -1,6 +1,17 @@
 Announcements
 =============
 
+GTDB-Tk 2.4.0 available
+-----------------------
+
+*April 24, 2024*
+
+* GTDB-Tk version ``2.4.0`` is now available.
+* This version of GTDB-Tk requires a new version of the GTDB-Tk reference package (Release 220).
+  `gtdbtk_r220_data.tar.gz <https://data.gtdb.ecogenomic.org/releases/release220/220.0/auxillary_files/gtdbtk_package/>`_.
+
+
+
 GTDB-Tk 2.3.0 available
 -----------------------
 
diff --git a/docs/src/changelog.rst b/docs/src/changelog.rst
index d1c7f0a0..ad20e241 100644
--- a/docs/src/changelog.rst
+++ b/docs/src/changelog.rst
@@ -2,6 +2,36 @@
 Change log
 ==========
 
+
+2.4.0
+-----
+
+Bug Fixes:
+
+* (`#576 <https://github.com/Ecogenomics/GTDBTk/issues/576>`_) When all genomes fail the prodigal step in the classify_wf, The
+bac120 summary file is still produced with the all failed genomes listed as 'Unclassified'
+* (`#573 <https://github.com/Ecogenomics/GTDBTk/issues/573>`_) When running the 3 classify steps independently, a genome can be filtered out in the align
+step but still be classified in the identify step. To avoid duplication of row, the genome is classified with a warning.
+* (`#540 <https://github.com/Ecogenomics/GTDBTk/issues/540>`_) Empty files are skipped during the sketch step of Mash,
+they are then catch in the prodigal step and are returned as 'Unclassified'
+* (`#549 <https://github.com/Ecogenomics/GTDBTk/issues/549>`_) : `--force` has been modified to deal with #540. Prodigal
+wasn't returning the empty files as failed genomes, it was only skipping them. These genomes are now returned in the summary file and flagged as Unclassified.
+
+Major Changes:
+
+* FastANI has been replaced by skani as the primary tool for computing Average Nucleotide Identity (ANI).Users may notice slight variations in the results compared to those obtained using FastANI.
+* In the generated `summary.tsv` files, several columns have been renamed for clarity and consistency. The following columns have been affected:
+
+    - "`fastani_reference`" column has been renamed to "`closest_genome_reference`".
+    - "`fastani_reference_radius`" column has been renamed to "`closest_genome_reference_radius`".
+    - "`fastani_taxonomy`" column has been renamed to "`closest_genome_taxonomy`".
+    - "`fastani_ani`" column has been renamed to "`closest_genome_ani`".
+    - "`fastani_af`" column has been renamed to "`closest_genome_af`".
+
+ These changes have been implemented to improve the readability and understanding of the data within the `summary.tsv` files. Users should update their scripts or processes accordingly to reflect these renamed column headers.
+
+
+
 2.3.2
 -----
 
diff --git a/docs/src/commands/ani_rep.rst b/docs/src/commands/ani_rep.rst
index b63f1cb4..1a2249a2 100644
--- a/docs/src/commands/ani_rep.rst
+++ b/docs/src/commands/ani_rep.rst
@@ -49,20 +49,21 @@ Output
 
 .. code-block:: text
 
-    [2020-04-13 10:51:58] INFO: GTDB-Tk v1.1.0
-    [2020-04-13 10:51:58] INFO: gtdbtk ani_rep --genome_dir genomes/ --out_dir ani_rep/ --cpus 70
-    [2020-04-13 10:51:58] INFO: Using GTDB-Tk reference data version r89: /release89
-    [2020-04-13 10:51:59] INFO: Using Mash version 2.2.2
-    [2020-04-13 10:51:59] INFO: Creating Mash sketch file: ani_rep/intermediate_results/mash/gtdbtk.user_query_sketch.msh
-    ==> Sketching 3 of 3 (100.0%) genomes
-    [2020-04-13 10:51:59] INFO: Creating Mash sketch file: ani_rep/intermediate_results/mash/gtdbtk.gtdb_ref_sketch.msh
-    ==> Sketching 24706 of 24706 (100.0%) genomes
-    [2020-04-13 10:53:13] INFO: Calculating Mash distances.
-    [2020-04-13 10:53:14] INFO: Calculating ANI with FastANI.
-    ==> Processing 874 of 874 (100.0%) comparisons.
-    [2020-04-13 10:53:23] INFO: Summary of results saved to: ani_rep/gtdbtk.ani_summary.tsv
-    [2020-04-13 10:53:23] INFO: Closest representative hits saved to: ani_rep/gtdbtk.ani_closest.tsv
-    [2020-04-13 10:53:23] INFO: Done.
+    [2024-03-27 16:43:25] INFO: GTDB-Tk v2.3.2
+    [2024-03-27 16:43:25] INFO: gtdbtk ani_rep --batchfile genomes/500_batchfile.tsv --out_dir user_vs_reps --cpus 90
+    [2024-03-27 16:43:25] INFO: Using GTDB-Tk reference data version r214: /srv/db/gtdbtk/official/release214_skani/release214
+    [2024-03-27 16:43:25] INFO: Loading reference genomes.
+    [2024-03-27 16:43:25] INFO: Using Mash version 2.2.2
+    [2024-03-27 16:43:25] INFO: Creating Mash sketch file: user_vs_reps/intermediate_results/mash/gtdbtk.user_query_sketch.msh
+    [2024-03-27 16:43:27] INFO: Completed 500 genomes in 1.42 seconds (351.61 genomes/second).
+    [2024-03-27 16:43:27] INFO: Creating Mash sketch file: user_vs_reps/intermediate_results/mash/gtdbtk.gtdb_ref_sketch.msh
+    [2024-03-27 16:46:55] INFO: Completed 85,205 genomes in 3.47 minutes (24,519.48 genomes/minute).
+    [2024-03-27 16:46:55] INFO: Calculating Mash distances.
+    [2024-03-27 16:47:37] INFO: Calculating ANI with skani v0.2.1.
+    [2024-03-27 16:47:45] INFO: Completed 4,383 comparisons in 7.68 seconds (570.58 comparisons/second).
+    [2024-03-27 16:47:46] INFO: Summary of results saved to: user_vs_reps/gtdbtk.ani_summary.tsv
+    [2024-03-27 16:47:46] INFO: Closest representative hits saved to: user_vs_reps/gtdbtk.ani_closest.tsv
+    [2024-03-27 16:47:46] INFO: Done.
 
 
 
diff --git a/docs/src/commands/check_install.rst b/docs/src/commands/check_install.rst
index b2506f9c..81de98a1 100644
--- a/docs/src/commands/check_install.rst
+++ b/docs/src/commands/check_install.rst
@@ -42,7 +42,7 @@ Output
     [2020-11-04 09:35:16] INFO: Checking that all third-party software are on the system path:
     [2020-11-04 09:35:16] INFO:          |-- FastTree         OK
     [2020-11-04 09:35:16] INFO:          |-- FastTreeMP       OK
-    [2020-11-04 09:35:16] INFO:          |-- fastANI          OK
+    [2020-11-04 09:35:16] INFO:          |-- skani            OK
     [2020-11-04 09:35:16] INFO:          |-- guppy            OK
     [2020-11-04 09:35:16] INFO:          |-- hmmalign         OK
     [2020-11-04 09:35:16] INFO:          |-- hmmsearch        OK
@@ -57,6 +57,6 @@ Output
     [2020-11-04 09:35:20] INFO:          |-- msa              OK
     [2020-11-04 09:35:20] INFO:          |-- metadata         OK
     [2020-11-04 09:35:20] INFO:          |-- taxonomy         OK
-    [2020-11-04 09:47:36] INFO:          |-- fastani          OK
+    [2020-11-04 09:47:36] INFO:          |-- skani            OK
     [2020-11-04 09:47:36] INFO:          |-- mrca_red         OK
     [2020-11-04 09:47:36] INFO: Done.
diff --git a/docs/src/commands/classify.rst b/docs/src/commands/classify.rst
index e525beb4..d190483e 100644
--- a/docs/src/commands/classify.rst
+++ b/docs/src/commands/classify.rst
@@ -67,33 +67,79 @@ Output
 
 .. code-block:: text
 
-    [2023-02-15 08:37:11] INFO: GTDB-Tk v2.2.2
-    [2023-02-15 08:37:11] INFO: gtdbtk classify --align_dir align_3lines/ --batchfile 3lines_batchfile.tsv --out_dir 3classify_ani --mash_db mash_db_dir/ --cpus 20
-    [2023-02-15 08:37:11] INFO: Using GTDB-Tk reference data version r207: /srv/projects/gtdbtk/test_new_features/release207_v2/
-    [2023-02-15 08:37:12] INFO: Loading reference genomes.
-    [2023-02-15 08:37:13] INFO: Using Mash version 2.2.2
-    [2023-02-15 08:37:13] INFO: Loading data from existing Mash sketch file: 3classify_ani/classify/ani_screen/intermediate_results/mash/gtdbtk.user_query_sketch.msh
-    [2023-02-15 08:37:13] INFO: Loading data from existing Mash sketch file: mash_db_dir/gtdb_ref_sketch.msh
-    [2023-02-15 08:37:16] INFO: Calculating Mash distances.
-    [2023-02-15 08:37:20] INFO: Calculating ANI with FastANI v1.3.
-    [2023-02-15 08:37:21] INFO: Completed 12 comparisons in 0.62 seconds (19.21 comparisons/second).
-    [2023-02-15 08:37:21] INFO: 1 genome(s) have been classified using the ANI pre-screening step.
-    [2023-02-15 08:37:21] TASK: Placing 2 bacterial genomes into backbone reference tree with pplacer using 20 CPUs (be patient).
-    [2023-02-15 08:37:21] INFO: pplacer version: v1.1.alpha19-0-g807f6f3
-    [2023-02-15 08:39:24] INFO: Calculating RED values based on reference tree.
-    [2023-02-15 08:39:25] INFO: 2 out of 2 have an class assignments. Those genomes will be reclassified.
-    [2023-02-15 08:39:25] TASK: Placing 1 bacterial genomes into class-level reference tree 6 (1/2) with pplacer using 20 CPUs (be patient).
-    [2023-02-15 08:43:39] INFO: Calculating RED values based on reference tree.
-    [2023-02-15 08:43:42] TASK: Traversing tree to determine classification method.
-    [2023-02-15 08:43:42] INFO: Completed 1 genome in 0.00 seconds (2,451.38 genomes/second).
-    [2023-02-15 08:43:42] TASK: Calculating average nucleotide identity using FastANI (v1.3).
-    [2023-02-15 08:43:43] INFO: Completed 34 comparisons in 0.90 seconds (37.77 comparisons/second).
-    [2023-02-15 08:43:43] INFO: 0 genome(s) have been classified using FastANI and pplacer.
-    [2023-02-15 08:43:43] TASK: Placing 1 bacterial genomes into class-level reference tree 5 (2/2) with pplacer using 20 CPUs (be patient).
-    [2023-02-15 08:46:38] INFO: Calculating RED values based on reference tree.
-    [2023-02-15 08:46:40] TASK: Traversing tree to determine classification method.
-    [2023-02-15 08:46:40] INFO: Completed 1 genome in 0.05 seconds (20.80 genomes/second).
-    [2023-02-15 08:46:40] INFO: 0 genome(s) have been classified using FastANI and pplacer.
-    [2023-02-15 08:46:41] WARNING: 1 of 3 genome has a warning (see summary file).
-    [2023-02-15 08:46:41] INFO: Note that Tk classification mode is insufficient for publication of new taxonomic designations. New designations should be based on one or more de novo trees, an example of which can be produced by Tk in de novo mode.
-    [2023-02-15 08:46:41] INFO: Done.
\ No newline at end of file
+    [2024-03-27 15:46:26] INFO: GTDB-Tk v2.3.2
+    [2024-03-27 15:46:26] INFO: gtdbtk classify --align_dir 500_align --out_dir 500_classify --mash_db mash_db.msh --cpus 90 --batchfile genomes/500_batchfile.tsv
+    [2024-03-27 15:46:26] INFO: Using GTDB-Tk reference data version r214: /srv/db/gtdbtk/official/release214_skani/release214
+    [2024-03-27 15:46:27] WARNING: Setting pplacer CPUs to 64, as pplacer is known to hang if >64 are used. You can override this using: --pplacer_cpus
+    [2024-03-27 15:46:27] INFO: Loading reference genomes.
+    [2024-03-27 15:46:27] INFO: Using Mash version 2.2.2
+    [2024-03-27 15:46:27] INFO: Creating Mash sketch file: 500_classify/classify/ani_screen/intermediate_results/mash/gtdbtk.user_query_sketch.msh
+    [2024-03-27 15:46:29] INFO: Completed 500 genomes in 1.74 seconds (286.68 genomes/second).
+    [2024-03-27 15:46:29] INFO: Loading data from existing Mash sketch file: mash_db.msh
+    [2024-03-27 15:46:32] INFO: Calculating Mash distances.
+    [2024-03-27 15:47:17] INFO: Calculating ANI with skani v0.2.1.
+    [2024-03-27 15:47:28] INFO: Completed 4,383 comparisons in 10.51 seconds (417.14 comparisons/second).
+    [2024-03-27 15:47:30] INFO: 357 genome(s) have been classified using the ANI pre-screening step.
+    [2024-03-27 15:47:30] TASK: Placing 143 bacterial genomes into backbone reference tree with pplacer using 64 CPUs (be patient).
+    [2024-03-27 15:47:30] INFO: pplacer version: v1.1.alpha19-0-g807f6f3
+    [2024-03-27 15:50:19] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 15:50:20] INFO: 143 out of 143 have an class assignments. Those genomes will be reclassified.
+    [2024-03-27 15:50:20] TASK: Placing 30 bacterial genomes into class-level reference tree 3 (1/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-27 15:58:09] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 15:58:12] TASK: Traversing tree to determine classification method.
+    [2024-03-27 15:58:12] INFO: Completed 30 genomes in 0.02 seconds (1,364.18 genomes/second).
+    [2024-03-27 15:58:15] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-27 15:58:16] INFO: Completed 505 comparisons in 1.32 seconds (381.38 comparisons/second).
+    [2024-03-27 15:58:19] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-27 15:58:19] TASK: Placing 27 bacterial genomes into class-level reference tree 2 (2/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-27 16:05:53] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 16:05:56] TASK: Traversing tree to determine classification method.
+    [2024-03-27 16:05:56] INFO: Completed 27 genomes in 0.04 seconds (606.99 genomes/second).
+    [2024-03-27 16:05:59] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-27 16:06:00] INFO: Completed 317 comparisons in 1.07 seconds (297.29 comparisons/second).
+    [2024-03-27 16:06:03] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-27 16:06:03] TASK: Placing 26 bacterial genomes into class-level reference tree 6 (3/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-27 16:12:28] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 16:12:30] TASK: Traversing tree to determine classification method.
+    [2024-03-27 16:12:30] INFO: Completed 26 genomes in 0.05 seconds (497.16 genomes/second).
+    [2024-03-27 16:12:31] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-27 16:12:32] INFO: Completed 41 comparisons in 0.83 seconds (49.26 comparisons/second).
+    [2024-03-27 16:12:33] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-27 16:12:34] TASK: Placing 22 bacterial genomes into class-level reference tree 7 (4/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-27 16:18:24] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 16:18:27] TASK: Traversing tree to determine classification method.
+    [2024-03-27 16:18:27] INFO: Completed 22 genomes in 0.03 seconds (715.55 genomes/second).
+    [2024-03-27 16:18:28] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-27 16:18:28] INFO: Completed 117 comparisons in 0.84 seconds (138.63 comparisons/second).
+    [2024-03-27 16:18:30] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-27 16:18:30] TASK: Placing 22 bacterial genomes into class-level reference tree 1 (5/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-27 16:26:01] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 16:26:04] TASK: Traversing tree to determine classification method.
+    [2024-03-27 16:26:04] INFO: Completed 22 genomes in 0.05 seconds (486.20 genomes/second).
+    [2024-03-27 16:26:05] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-27 16:26:06] INFO: Completed 373 comparisons in 1.05 seconds (354.70 comparisons/second).
+    [2024-03-27 16:26:08] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-27 16:26:08] TASK: Placing 8 bacterial genomes into class-level reference tree 4 (6/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-27 16:32:45] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 16:32:48] TASK: Traversing tree to determine classification method.
+    [2024-03-27 16:32:48] INFO: Completed 8 genomes in 0.15 seconds (52.49 genomes/second).
+    [2024-03-27 16:32:48] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-27 16:32:49] INFO: Completed 176 comparisons in 0.90 seconds (195.38 comparisons/second).
+    [2024-03-27 16:32:50] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-27 16:32:50] TASK: Placing 4 bacterial genomes into class-level reference tree 8 (7/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-27 16:35:30] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 16:35:31] TASK: Traversing tree to determine classification method.
+    [2024-03-27 16:35:31] INFO: Completed 4 genomes in 0.00 seconds (5,959.93 genomes/second).
+    [2024-03-27 16:35:32] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-27 16:35:33] INFO: Completed 31 comparisons in 0.93 seconds (33.24 comparisons/second).
+    [2024-03-27 16:35:33] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-27 16:35:33] TASK: Placing 4 bacterial genomes into class-level reference tree 5 (8/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-27 16:40:57] INFO: Calculating RED values based on reference tree.
+    [2024-03-27 16:40:59] TASK: Traversing tree to determine classification method.
+    [2024-03-27 16:40:59] INFO: Completed 4 genomes in 0.00 seconds (4,607.86 genomes/second).
+    [2024-03-27 16:40:59] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-27 16:41:00] INFO: Completed 46 comparisons in 0.86 seconds (53.34 comparisons/second).
+    [2024-03-27 16:41:00] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-27 16:41:00] WARNING: 5 of 500 genomes have a warning (see summary file).
+    [2024-03-27 16:41:00] INFO: Note that Tk classification mode is insufficient for publication of new taxonomic designations. New designations should be based on one or more de novo trees, an example of which can be produced by Tk in de novo mode.
+    [2024-03-27 16:41:00] INFO: Done.
\ No newline at end of file
diff --git a/docs/src/commands/classify_wf.rst b/docs/src/commands/classify_wf.rst
index 96c0dad1..afbe496b 100644
--- a/docs/src/commands/classify_wf.rst
+++ b/docs/src/commands/classify_wf.rst
@@ -15,7 +15,7 @@ For arguments and output files, see each of the individual steps:
 The classify workflow consists of four steps: ``ani_screen``, ``identify``, ``align``, and ``classify``.
 
 The ``ani_screen`` step compares user genomes against a `Mash <https://genomebiology.biomedcentral.com/articles/10.1186/s13059-016-0997-x>`_ database composed of all GTDB representative genomes,
-then verify the best mash hits using `FastANI <https://www.nature.com/articles/s41467-018-07641-9>`_. User genomes classified with FastANI are not run through the rest of the pipeline (``identify``, ``align``, ``classify``)
+then verify the best mash hits using `skani <https://www.nature.com/articles/s41592-023-02018-3>`_. User genomes classified with skani are not run through the rest of the pipeline (``identify``, ``align``, ``classify``)
 and are reported in the summary file.
 
 The ``identify`` step calls genes using `Prodigal <http://compbio.ornl.gov/prodigal/>`_,
@@ -86,82 +86,108 @@ Output
 
 .. code-block:: text
 
-    [2023-02-22 16:10:50] INFO: GTDB-Tk v2.2.3
-    [2023-02-22 16:10:50] INFO: gtdbtk classify_wf --batchfile 3lines_batchfile.tsv --out_dir classify_wf_outdir_test --keep_intermediates --cpus 20 --mash_db mash_sketch/cli/mash_db.msh
-    [2023-02-22 16:10:50] INFO: Using GTDB-Tk reference data version r207: /srv/projects/gtdbtk/test_new_features/release207_v2/
-    [2023-02-22 16:10:50] INFO: Loading reference genomes.
-    [2023-02-22 16:10:51] INFO: Using Mash version 2.3
-    [2023-02-22 16:10:51] INFO: Loading data from existing Mash sketch file: classify_wf_outdir_test/classify/ani_screen/intermediate_results/mash/gtdbtk.user_query_sketch.msh
-    [2023-02-22 16:10:51] INFO: Creating Mash sketch file: mash_sketch/cli/mash_db.msh
-    [2023-02-22 16:10:51] INFO: Calculating RED values based on reference tree.
-    [2023-02-22 16:10:54] TASK: Traversing tree to determine classification method.
-    [2023-02-22 16:10:54] INFO: Completed 1 genome in 0.00 seconds (2,335.36 genomes/second).
-    [2023-02-22 16:10:54] TASK: Calculating average nucleotide identity using FastANI (v1.3).
-    [2023-02-22 16:10:57] INFO: Completed 34 comparisons in 2.27 seconds (14.95 comparisons/second).
-    [2023-02-22 16:10:57] INFO: 0 genome(s) have been classified using FastANI and pplacer.
-    [2023-02-22 16:10:57] TASK: Placing 1 bacterial genomes into class-level reference tree 5 (2/2) with pplacer using 20 CPUs (be patient).
-    [2023-02-22 16:14:29] INFO: Calculating RED values based on reference tree.
-    [2023-02-22 16:14:31] TASK: Traversing tree to determine classification method.
-    [2023-02-22 16:14:31] INFO: Completed 1 genome in 0.06 seconds (16.77 genomes/second).
-    [2023-02-22 16:14:31] INFO: 0 genome(s) have been classified using FastANI and pplacer.
-    [2023-02-22 16:14:31] WARNING: 1 of 3 genome has a warning (see summary file).
-    [2023-02-22 16:14:31] INFO: Note that Tk classification mode is insufficient for publication of new taxonomic designations. New designations should be based on one or more de novo trees, an example of which can be produced by Tk in de novo mode.
-    [2023-02-22 16:14:31] INFO: Done.
-    [2023-02-22 16:20:06] INFO: Completed 65,703 genomes in 9.25 minutes (7,103.32 genomes/minute).
-    [2023-02-22 16:20:06] INFO: Calculating Mash distances.
-    [2023-02-22 16:20:10] INFO: Calculating ANI with FastANI v1.3.
-    [2023-02-22 16:20:11] INFO: Completed 12 comparisons in 0.63 seconds (18.90 comparisons/second).
-    [2023-02-22 16:20:11] INFO: Summary of results saved to: classify_wf_outdir_test_mash/classify/ani_screen/gtdbtk.bac120.ani_summary.tsv
-    [2023-02-22 16:20:11] INFO: 1 genome(s) have been classified using the ANI pre-screening step.
-    [2023-02-22 16:20:11] INFO: Done.
-    [2023-02-22 16:20:11] INFO: 1 genome(s) have been classified using the ANI pre-screening step.
-    [2023-02-22 16:20:11] INFO: Done.
-    [2023-02-22 16:20:11] INFO: Identifying markers in 2 genomes with 20 threads.
-    [2023-02-22 16:20:11] TASK: Running Prodigal V2.6.3 to identify genes.
-    [2023-02-22 16:20:12] INFO: Completed 2 genomes in 0.22 seconds (9.07 genomes/second).
-    [2023-02-22 16:20:12] WARNING: Prodigal skipped 2 genomes due to pre-existing data, see warnings.log
-    [2023-02-22 16:20:12] TASK: Identifying TIGRFAM protein families.
-    [2023-02-22 16:20:12] INFO: Completed 2 genomes in 0.03 seconds (65.39 genomes/second).
-    [2023-02-22 16:20:12] WARNING: TIGRFAM skipped 2 genomes due to pre-existing data, see warnings.log
-    [2023-02-22 16:20:12] TASK: Identifying Pfam protein families.
-    [2023-02-22 16:20:12] INFO: Completed 2 genomes in 0.03 seconds (68.36 genomes/second).
-    [2023-02-22 16:20:12] WARNING: Pfam skipped 2 genomes due to pre-existing data, see warnings.log
-    [2023-02-22 16:20:12] INFO: Annotations done using HMMER 3.1b2 (February 2015).
-    [2023-02-22 16:20:12] TASK: Summarising identified marker genes.
-    [2023-02-22 16:20:12] INFO: Completed 2 genomes in 0.06 seconds (32.55 genomes/second).
-    [2023-02-22 16:20:12] INFO: Done.
-    [2023-02-22 16:20:12] INFO: Aligning markers in 2 genomes with 20 CPUs.
-    [2023-02-22 16:20:12] INFO: Processing 2 genomes identified as bacterial.
-    [2023-02-22 16:20:21] INFO: Read concatenated alignment for 62,291 GTDB genomes.
-    [2023-02-22 16:20:21] TASK: Generating concatenated alignment for each marker.
-    [2023-02-22 16:20:22] INFO: Completed 2 genomes in 0.03 seconds (79.85 genomes/second).
-    [2023-02-22 16:20:23] TASK: Aligning 100 identified markers using hmmalign 3.1b2 (February 2015).
-    [2023-02-22 16:20:25] INFO: Completed 100 markers in 1.06 seconds (93.94 markers/second).
-    [2023-02-22 16:20:25] TASK: Masking columns of bacterial multiple sequence alignment using canonical mask.
-    [2023-02-22 16:22:21] INFO: Completed 62,293 sequences in 1.93 minutes (32,233.24 sequences/minute).
-    [2023-02-22 16:22:21] INFO: Masked bacterial alignment from 41,084 to 5,036 AAs.
-    [2023-02-22 16:22:21] INFO: 0 bacterial user genomes have amino acids in <10.0% of columns in filtered MSA.
-    [2023-02-22 16:22:22] INFO: Creating concatenated alignment for 62,293 bacterial GTDB and user genomes.
-    [2023-02-22 16:22:46] INFO: Creating concatenated alignment for 2 bacterial user genomes.
-    [2023-02-22 16:22:46] INFO: Done.
-    [2023-02-22 16:22:47] TASK: Placing 2 bacterial genomes into backbone reference tree with pplacer using 20 CPUs (be patient).
-    [2023-02-22 16:22:47] INFO: pplacer version: v1.1.alpha19-0-g807f6f3
-    [2023-02-22 16:25:01] INFO: Calculating RED values based on reference tree.
-    [2023-02-22 16:25:02] INFO: 2 out of 2 have an class assignments. Those genomes will be reclassified.
-    [2023-02-22 16:25:02] TASK: Placing 1 bacterial genomes into class-level reference tree 6 (1/2) with pplacer using 20 CPUs (be patient).
-    [2023-02-22 16:29:46] INFO: Calculating RED values based on reference tree.
-    [2023-02-22 16:29:48] TASK: Traversing tree to determine classification method.
-    [2023-02-22 16:29:48] INFO: Completed 1 genome in 0.00 seconds (2,391.28 genomes/second).
-    [2023-02-22 16:29:48] TASK: Calculating average nucleotide identity using FastANI (v1.3).
-    [2023-02-22 16:29:50] INFO: Completed 34 comparisons in 1.53 seconds (22.22 comparisons/second).
-    [2023-02-22 16:29:50] INFO: 0 genome(s) have been classified using FastANI and pplacer.
-    [2023-02-22 16:29:50] TASK: Placing 1 bacterial genomes into class-level reference tree 5 (2/2) with pplacer using 20 CPUs (be patient).
-    [2023-02-22 16:33:17] INFO: Calculating RED values based on reference tree.
-    [2023-02-22 16:33:19] TASK: Traversing tree to determine classification method.
-    [2023-02-22 16:33:19] INFO: Completed 1 genome in 0.06 seconds (17.02 genomes/second).
-    [2023-02-22 16:33:19] INFO: 0 genome(s) have been classified using FastANI and pplacer.
-    [2023-02-22 16:33:19] WARNING: 1 of 3 genome has a warning (see summary file).
-    [2023-02-22 16:33:19] INFO: 0 genome(s) have been classified using FastANI and pplacer.
-    [2023-02-22 16:33:19] WARNING: 1 of 3 genome has a warning (see summary file).
-    [2023-02-22 16:33:19] INFO: Note that Tk classification mode is insufficient for publication of new taxonomic designations. New designations should be based on one or more de novo trees, an example of which can be produced by Tk in de novo mode.
-    [2023-02-22 16:33:19] INFO: Done.
+    [2024-03-25 12:25:02] INFO: GTDB-Tk v2.3.2
+    [2024-03-25 12:25:02] INFO: gtdbtk classify_wf --batchfile genomes/5K_batchfile.tsv --out_dir 5K_classify_wf --cpus 64 --mash_db mash_db.msh
+    [2024-03-25 12:25:02] INFO: Using GTDB-Tk reference data version r214: /srv/db/gtdbtk/official/release214_skani/release214
+    [2024-03-25 12:25:02] INFO: Loading reference genomes.
+    [2024-03-25 12:25:03] INFO: Using Mash version 2.2.2
+    [2024-03-25 12:25:03] INFO: Creating Mash sketch file: 5K_classify_wf/classify/ani_screen/intermediate_results/mash/gtdbtk.user_query_sketch.msh
+    [2024-03-25 12:25:12] INFO: Completed 5,000 genomes in 9.35 seconds (534.79 genomes/second).
+    [2024-03-25 12:25:12] INFO: Loading data from existing Mash sketch file: mash_db.msh
+    [2024-03-25 12:25:16] INFO: Calculating Mash distances.
+    [2024-03-25 12:33:09] INFO: Calculating ANI with skani v0.2.1.
+    [2024-03-25 12:34:26] INFO: Completed 40,673 comparisons in 1.26 minutes (32,186.56 comparisons/minute).
+    [2024-03-25 12:39:22] INFO: Summary of results saved to: 5K_classify_wf/classify/ani_screen/gtdbtk.bac120.ani_summary.tsv
+    [2024-03-25 12:39:22] INFO: 3528 genome(s) have been classified using the ANI pre-screening step.
+    [2024-03-25 12:39:22] INFO: Done.
+    [2024-03-25 12:39:23] INFO: Identifying markers in 1,472 genomes with 64 threads.
+    [2024-03-25 12:39:23] TASK: Running Prodigal V2.6.3 to identify genes.
+    [2024-03-25 12:45:33] INFO: Completed 1,472 genomes in 6.17 minutes (238.71 genomes/minute).
+    [2024-03-25 12:45:33] TASK: Identifying TIGRFAM protein families.
+    [2024-03-25 12:47:22] INFO: Completed 1,472 genomes in 1.82 minutes (809.77 genomes/minute).
+    [2024-03-25 12:47:22] TASK: Identifying Pfam protein families.
+    [2024-03-25 12:47:29] INFO: Completed 1,472 genomes in 6.77 seconds (217.31 genomes/second).
+    [2024-03-25 12:47:29] INFO: Annotations done using HMMER 3.1b2 (February 2015).
+    [2024-03-25 12:47:29] TASK: Summarising identified marker genes.
+    [2024-03-25 12:48:02] INFO: Completed 1,472 genomes in 32.99 seconds (44.62 genomes/second).
+    [2024-03-25 12:48:02] INFO: Done.
+    [2024-03-25 12:48:03] INFO: Aligning markers in 1,472 genomes with 64 CPUs.
+    [2024-03-25 12:48:03] INFO: Processing 1,472 genomes identified as bacterial.
+    [2024-03-25 12:48:15] INFO: Read concatenated alignment for 80,789 GTDB genomes.
+    [2024-03-25 12:48:15] TASK: Generating concatenated alignment for each marker.
+    [2024-03-25 12:48:23] INFO: Completed 1,472 genomes in 1.38 seconds (1,068.13 genomes/second).
+    [2024-03-25 12:48:24] TASK: Aligning 120 identified markers using hmmalign 3.1b2 (February 2015).
+    [2024-03-25 12:48:50] INFO: Completed 120 markers in 18.95 seconds (6.33 markers/second).
+    [2024-03-25 12:48:51] TASK: Masking columns of bacterial multiple sequence alignment using canonical mask.
+    [2024-03-25 12:51:17] INFO: Completed 82,261 sequences in 2.42 minutes (33,935.16 sequences/minute).
+    [2024-03-25 12:51:17] INFO: Masked bacterial alignment from 41,084 to 5,035 AAs.
+    [2024-03-25 12:51:17] INFO: 0 bacterial user genomes have amino acids in <10.0% of columns in filtered MSA.
+    [2024-03-25 12:51:17] INFO: Creating concatenated alignment for 82,261 bacterial GTDB and user genomes.
+    [2024-03-25 12:51:42] INFO: Creating concatenated alignment for 1,472 bacterial user genomes.
+    [2024-03-25 12:51:42] INFO: Done.
+    [2024-03-25 12:51:44] TASK: Placing 1,472 bacterial genomes into backbone reference tree with pplacer using 64 CPUs (be patient).
+    [2024-03-25 12:51:44] INFO: pplacer version: v1.1.alpha19-0-g807f6f3
+    [2024-03-25 12:56:39] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 12:56:45] INFO: 1472 out of 1472 have an class assignments. Those genomes will be reclassified.
+    [2024-03-25 12:56:45] TASK: Placing 316 bacterial genomes into class-level reference tree 7 (1/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-25 13:03:32] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 13:03:34] TASK: Traversing tree to determine classification method.
+    [2024-03-25 13:03:34] INFO: Completed 316 genomes in 0.13 seconds (2,474.33 genomes/second).
+    [2024-03-25 13:03:35] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-25 13:03:59] INFO: Completed 3,690 comparisons in 24.41 seconds (151.18 comparisons/second).
+    [2024-03-25 13:04:26] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-25 13:04:26] TASK: Placing 284 bacterial genomes into class-level reference tree 3 (2/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-25 13:13:22] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 13:13:25] TASK: Traversing tree to determine classification method.
+    [2024-03-25 13:13:26] INFO: Completed 284 genomes in 0.60 seconds (477.15 genomes/second).
+    [2024-03-25 13:13:28] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-25 13:14:12] INFO: Completed 6,618 comparisons in 43.91 seconds (150.72 comparisons/second).
+    [2024-03-25 13:14:37] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-25 13:14:38] TASK: Placing 201 bacterial genomes into class-level reference tree 1 (3/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-25 13:22:53] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 13:22:56] TASK: Traversing tree to determine classification method.
+    [2024-03-25 13:22:56] INFO: Completed 201 genomes in 0.30 seconds (673.19 genomes/second).
+    [2024-03-25 13:22:57] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-25 13:23:15] INFO: Completed 2,230 comparisons in 17.76 seconds (125.60 comparisons/second).
+    [2024-03-25 13:23:31] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-25 13:23:31] TASK: Placing 174 bacterial genomes into class-level reference tree 4 (4/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-25 13:30:40] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 13:30:42] TASK: Traversing tree to determine classification method.
+    [2024-03-25 13:30:43] INFO: Completed 174 genomes in 0.70 seconds (249.85 genomes/second).
+    [2024-03-25 13:30:44] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-25 13:31:14] INFO: Completed 4,185 comparisons in 29.77 seconds (140.56 comparisons/second).
+    [2024-03-25 13:31:29] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-25 13:31:30] TASK: Placing 170 bacterial genomes into class-level reference tree 6 (5/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-25 13:38:23] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 13:38:25] TASK: Traversing tree to determine classification method.
+    [2024-03-25 13:38:26] INFO: Completed 170 genomes in 0.35 seconds (485.99 genomes/second).
+    [2024-03-25 13:38:26] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-25 13:38:44] INFO: Completed 2,192 comparisons in 17.03 seconds (128.74 comparisons/second).
+    [2024-03-25 13:38:58] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-25 13:38:58] TASK: Placing 139 bacterial genomes into class-level reference tree 2 (6/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-25 13:47:20] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 13:47:23] TASK: Traversing tree to determine classification method.
+    [2024-03-25 13:47:24] INFO: Completed 139 genomes in 0.17 seconds (814.69 genomes/second).
+    [2024-03-25 13:47:24] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-25 13:47:41] INFO: Completed 1,854 comparisons in 15.93 seconds (116.40 comparisons/second).
+    [2024-03-25 13:47:51] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-25 13:47:52] TASK: Placing 123 bacterial genomes into class-level reference tree 8 (7/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-25 13:50:55] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 13:50:56] TASK: Traversing tree to determine classification method.
+    [2024-03-25 13:50:56] INFO: Completed 123 genomes in 0.02 seconds (5,030.07 genomes/second).
+    [2024-03-25 13:50:57] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-25 13:51:06] INFO: Completed 651 comparisons in 8.51 seconds (76.49 comparisons/second).
+    [2024-03-25 13:51:17] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-25 13:51:17] TASK: Placing 65 bacterial genomes into class-level reference tree 5 (8/8) with pplacer using 64 CPUs (be patient).
+    [2024-03-25 13:57:05] INFO: Calculating RED values based on reference tree.
+    [2024-03-25 13:57:08] TASK: Traversing tree to determine classification method.
+    [2024-03-25 13:57:08] INFO: Completed 65 genomes in 0.15 seconds (436.27 genomes/second).
+    [2024-03-25 13:57:08] TASK: Calculating average nucleotide identity using skani (v0.2.1).
+    [2024-03-25 13:57:18] INFO: Completed 799 comparisons in 9.03 seconds (88.47 comparisons/second).
+    [2024-03-25 13:57:24] INFO: 0 genome(s) have been classified using skani and pplacer.
+    [2024-03-25 13:57:24] WARNING: 14 of 5000 genomes have a warning (see summary file).
+    [2024-03-25 13:57:24] INFO: Note that Tk classification mode is insufficient for publication of new taxonomic designations. New designations should be based on one or more de novo trees, an example of which can be produced by Tk in de novo mode.
+    [2024-03-25 13:57:24] INFO: Done.
+    [2024-03-25 13:57:24] INFO: Removing intermediate files.
+    [2024-03-25 13:57:43] INFO: Intermediate files removed.
+    [2024-03-25 13:57:43] INFO: Done.
diff --git a/docs/src/faq.rst b/docs/src/faq.rst
index de3d2de0..a4e39b8b 100644
--- a/docs/src/faq.rst
+++ b/docs/src/faq.rst
@@ -75,15 +75,26 @@ Using the ``--scratch_dir`` parameter and ``--pplacer_cpus 1`` may help.
 How is GTDB-Tk validating species assignments using average nucleotide identity?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-GTDB-Tk uses `FastANI <https://github.com/ParBLiSS/FastANI>`_ to estimate the ANI between genomes.
-We recommend you have FastANI >= 1.32 as this version introduces a fix that makes the results deterministic.
+GTDB-Tk uses `skani <https://github.com/bluenote-1577/skani>`_ ( it was using fastANI until v2.3.2) to estimate the ANI between genomes.
 A query genome is only classified as belonging to the same species as a reference genome if the ANI between the
 genomes is within the species ANI circumscription radius (typically, 95%) and the alignment fraction (AF) is >=0.5.
 In some circumstances, the phylogenetic placement of a query genome may not support the species assignment.
-GTDB r207 strictly uses ANI to circumscribe species and GTDB-Tk follows this methodology.
+GTDB r207+ strictly uses ANI to circumscribe species and GTDB-Tk follows this methodology.
 The species-specific ANI circumscription radii are available from the `GTDB <https://gtdb.ecogenomic.org/>`_ website.
 
 
+What is the difference between the mutually exclusive options ``--mash_db`` and ``--skip_ani_screen``?
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+| Starting with GTDB-Tk v2.2+, the ``classify_wf`` and ``classify`` function require an extra parameter to run: ``--mash_db`` or ``--skip_ani_screen``.
+| With this new version of Tk, The first stage of ``classify`` pipelines (``classify_wf`` and ``classify``) is to compare all user genomes to all reference genomes and annotate them, if possible, based on ANI matches.
+| Using the ``--mash_db`` option will indicate to GTDB-Tk the path of the sketched Mash database require for ANI screening.
+| If no database are available ( i.e. this is the first time running classify ), the ``--mash_db`` option will sketch a new Mash database that can be used for subsequent calls.
+| The ``--skip_ani_screen`` option will skip the pre-screening step and classify all genomes similar to previous versions of GTDB-Tk.
+
+Deprecated FAQ
+---------------
+
 Why is FastANI using more threads than allocated?
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -108,13 +119,4 @@ From GTDB-Tk v2.0.0 the conda environment will automatically have FastANI v1.3 i
 
 From GTDB-Tk v2.2.2 the Docker container will automatically have FastANI v1.32 installed. Otherwise, manually
 build the container from the `Dockerfile <https://github.com/Ecogenomics/GTDBTk/blob/master/Dockerfile>`_, making
-sure to specify FastANI v1.32.
-
-What is the difference between the mutually exclusive options ``--mash_db`` and ``--skip_ani_screen``?
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-| Starting with GTDB-Tk v2.2+, the ``classify_wf`` and ``classify`` function require an extra parameter to run: ``--mash_db`` or ``--skip_ani_screen``.
-| With this new version of Tk, The first stage of ``classify`` pipelines (``classify_wf`` and ``classify``) is to compare all user genomes to all reference genomes and annotate them, if possible, based on ANI matches.
-| Using the ``--mash_db`` option will indicate to GTDB-Tk the path of the sketched Mash database require for ANI screening.
-| If no database are available ( i.e. this is the first time running classify ), the ``--mash_db`` option will sketch a new Mash database that can be used for subsequent calls.
-| The ``--skip_ani_screen`` option will skip the pre-screening step and classify all genomes similar to previous versions of GTDB-Tk.
\ No newline at end of file
+sure to specify FastANI v1.32.
\ No newline at end of file
diff --git a/docs/src/files/mash_distances.msh.rst b/docs/src/files/mash_distances.msh.rst
index 6d1ba7d2..cfbb0ae7 100644
--- a/docs/src/files/mash_distances.msh.rst
+++ b/docs/src/files/mash_distances.msh.rst
@@ -16,13 +16,17 @@ Example
 
 .. code-block:: text
 
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000285855.2_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.000426011	1/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000433035.1_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.000242431	1/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000434315.1_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.000303707	1/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000437515.1_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.00024794	1/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000806015.1_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.000125283	1/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000961725.1_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.000460228	1/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000989525.1_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.000167986	1/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000993685.1_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.000175098	1/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_000996035.1_genomic.fna.gz	genomes/genome_1.fna	0.263022	8.80681e-09	2/1000
-    /srv/db/gtdbtk/official/release89/fastani/database/GCA_001003475.1_genomic.fna.gz	genomes/genome_1.fna	0.295981	0.000127206	1/1000
+/path/to/gtdbtk_package/release214/skani/database/GCF/000/226/295/GCF_000226295.1_genomic.fna.gz       2010170002_1.fna.gz     0.140234        0       280/5000
+/path/to/gtdbtk_package/release214/skani/database/GCF/018/304/765/GCF_018304765.1_genomic.fna.gz       2010170002_1.fna.gz     0.143722        0       264/5000
+/path/to/gtdbtk_package/release214/skani/database/GCF/000/011/905/GCF_000011905.1_genomic.fna.gz       2013843002_3.fna.gz     0.120075        0       395/5000
+/path/to/gtdbtk_package/release214/skani/database/GCF/000/830/885/GCF_000830885.1_genomic.fna.gz       2013843002_3.fna.gz     0.0113383       0       3577/5000
+/path/to/gtdbtk_package/release214/skani/database/GCF/001/889/305/GCF_001889305.1_genomic.fna.gz       2013843002_3.fna.gz     0.123871        0       370/5000
+/path/to/gtdbtk_package/release214/skani/database/GCA/015/295/985/GCA_015295985.1_genomic.fna.gz       2014031006_1.fna.gz     0.0748498       0       889/5000
+/path/to/gtdbtk_package/release214/skani/database/GCF/000/013/205/GCF_000013205.1_genomic.fna.gz       2014031006_1.fna.gz     0.129151        0       338/5000
+/path/to/gtdbtk_package/release214/skani/database/GCF/000/013/225/GCF_000013225.1_genomic.fna.gz       2014031006_1.fna.gz     0.0242123       0       2569/5000
+/path/to/gtdbtk_package/release214/skani/database/GCF/000/516/515/GCF_000516515.1_genomic.fna.gz       2022920003_1.fna.gz     0.0949923       0       614/5000
+/path/to/gtdbtk_package/release214/skani/database/GCF/000/735/195/GCF_000735195.1_genomic.fna.gz       2022920003_1.fna.gz     0.0279099       0       2352/5000
+/path/to/gtdbtk_package/release214/skani/database/GCA/000/980/735/GCA_000980735.1_genomic.fna.gz       2029527001_1.fna.gz     0.023154        0       2636/5000
+/path/to/gtdbtk_package/release214/skani/database/GCA/002/420/705/GCA_002420705.1_genomic.fna.gz       2029527003_1.fna.gz     0.0879852       0       697/5000
+/path/to/gtdbtk_package/release214/skani/database/GCA/002/454/415/GCA_002454415.1_genomic.fna.gz       2029527003_1.fna.gz     0.1472  0       249/5000
+/path/to/gtdbtk_package/release214/skani/database/GCA/002/500/745/GCA_002500745.1_genomic.fna.gz       2029527003_1.fna.gz     0.14768 0       247/5000
diff --git a/docs/src/files/summary.tsv.rst b/docs/src/files/summary.tsv.rst
index f853110d..d0a13679 100644
--- a/docs/src/files/summary.tsv.rst
+++ b/docs/src/files/summary.tsv.rst
@@ -8,11 +8,11 @@ Classifications provided by the GTDB-Tk are in the files \<prefix>.bac120.summar
 
 * user_genome: Unique identifier of query genome taken from the FASTA file of the genome.
 * classification: GTDB taxonomy string inferred by the GTDB-Tk. An unassigned species (i.e., ``s__``) indicates that the query genome is either i) placed outside a named genus or ii) the ANI to the closest intra-genus reference genome with an AF >=0.65 is not within the species-specific ANI circumscription radius.
-* fastani_reference: indicates the accession number of the reference genome (species) to which a user genome was assigned based on ANI and AF. ANI values are only calculated when a query genome is placed within a defined genus and are evaluated for all reference genomes in that genus.
-* fastani_reference_radius: indicates the species-specific ANI circumscription radius of the reference genomes used to determine if a query genome should be classified to the same species as the reference.
-* fastani_taxonomy: indicates the GTDB taxonomy of the above reference genome.
-* fastani_ani: indicates the ANI between the query and above reference genome.
-* fastani_af: indicates the alignment fraction (AF) between the query and above reference genome.
+* closest_genome_reference: indicates the accession number of the reference genome (species) to which a user genome was assigned based on ANI and AF. ANI values are only calculated when a query genome is placed within a defined genus and are evaluated for all reference genomes in that genus.
+* closest_genome_reference_radius: indicates the species-specific ANI circumscription radius of the reference genomes used to determine if a query genome should be classified to the same species as the reference.
+* closest_genome_taxonomy: indicates the GTDB taxonomy of the above reference genome.
+* closest_genome_ani: indicates the ANI between the query and above reference genome.
+* closest_genome_af: indicates the alignment fraction (AF) between the query and above reference genome.
 * closest_placement_reference: indicates the accession number of the reference genome when a genome is placed on a terminal branch. 
 * closest_placement_taxonomy: indicates the GTDB taxonomy of the above reference genome.
 * closest_placement_ani: indicates the ANI between the query and above reference genome.
diff --git a/docs/src/installing/index.rst b/docs/src/installing/index.rst
index 78f0079b..5140e65a 100644
--- a/docs/src/installing/index.rst
+++ b/docs/src/installing/index.rst
@@ -33,13 +33,13 @@ Hardware requirements
      - Storage
      - Time
    * - Archaea
-     - ~45 GB
-     - ~85 GB
-     - ~1 hour / 1,000 genomes @ 64 CPUs
+     - ~60 GB
+     - ~106 GB
+     - ~90 minutes / 1,000 genomes @ 64 CPUs
    * - Bacteria
-     - ~65GB (410 GB when using --full_tree)
-     - ~85 GB
-     - ~1 hour / 1,000 genomes @ 64 CPUs
+     - ~90GB (545 GB when using --full_tree)
+     - ~106 GB
+     - ~90 minutes / 1,000 genomes @ 64 CPUs
 
 .. note::
    The amount reported of memory reported can vary depending on the number of pplacer threads.
@@ -98,9 +98,9 @@ GTDB-Tk makes use of the following 3rd party dependencies and assumes they are o
    * - `pplacer <http://matsen.fhcrc.org/pplacer/>`_
      - >= 1.1
      - Matsen FA, et al. 2010. `pplacer: linear time maximum-likelihood and Bayesian phylogenetic placement of sequences onto a fixed reference tree <https://www.ncbi.nlm.nih.gov/pubmed/21034504>`_. *BMC Bioinformatics*, 11:538.
-   * - `FastANI <https://github.com/ParBLiSS/FastANI>`_
-     - >= 1.32
-     - Jain C, et al. 2019. `High-throughput ANI Analysis of 90K Prokaryotic Genomes Reveals Clear Species Boundaries <https://www.nature.com/articles/s41467-018-07641-9>`_. *Nat. Communications*, doi: 10.1038/s41467-018-07641-9.
+   * - `skani <https://github.com/bluenote-1577/skani/>`_
+     - >= 0.2.1
+     - Shaw J. and Yu Y.W. 2023. `Fast and robust metagenomic sequence comparison through sparse chaining with skani <https://www.nature.com/articles/s41592-023-02018-3>`_. *Nature Methods*, 20, pages1661–1665 (2023).
    * - `FastTree <http://www.microbesonline.org/fasttree/>`_
      - >= 2.1.9
      - Price MN, et al. 2010. `FastTree 2 - Approximately Maximum-Likelihood Trees for Large Alignments <https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2835736/>`_. *PLoS One*, 5, e9490.
@@ -117,12 +117,12 @@ Please cite these tools if you use GTDB-Tk in your work.
 GTDB-Tk reference data
 ----------------------
 
-GTDB-Tk requires ~84G of external data that needs to be downloaded and unarchived:
+GTDB-Tk requires ~110G of external data that needs to be downloaded and unarchived:
 
 .. code-block:: bash
 
-    wget https://data.gtdb.ecogenomic.org/releases/latest/auxillary_files/gtdbtk_data.tar.gz
-    wget https://data.ace.uq.edu.au/public/gtdb/data/releases/latest/auxillary_files/gtdbtk_data.tar.gz  (or, mirror)
+    wget https://data.gtdb.ecogenomic.org/releases/latest/auxillary_files/gtdbtk_package/full_package/gtdbtk_data.tar.gz
+    wget https://data.ace.uq.edu.au/public/gtdb/data/releases/latest/auxillary_files/gtdbtk_package/full_package/gtdbtk_data.tar.gz  (or, mirror)
     tar xvzf gtdbtk_data.tar.gz
 
 
@@ -137,13 +137,17 @@ GTDB-Tk requires ~84G of external data that needs to be downloaded and unarchive
      - Minimum version
      - Maximum version
      - MD5
+   * - `R220 <https://data.gtdb.ecogenomic.org/releases/release220/220.0/auxillary_files/gtdbtk_package/full_package/gtdbtk_r220_data.tar.gz>`_
+     - 2.4.0
+     - Current
+     - 5aafa1b9c27ceda003d75adf238ed9e0
    * - `R214 <https://data.gtdb.ecogenomic.org/releases/release214/214.0/auxillary_files/gtdbtk_r214_data.tar.gz>`_
      - 2.1.0
-     - Current
+     - 2.3.2
      - 630745840850c532546996b22da14c27
    * - `R207_v2 <https://data.gtdb.ecogenomic.org/releases/release207/207.0/auxillary_files/gtdbtk_r207_v2_data.tar.gz>`_
      - 2.1.0
-     - Current
+     - 2.3.2
      - df468d63265e8096d8ca01244cb95f30
    * - `R207 <https://data.gtdb.ecogenomic.org/releases/release207/207.0/auxillary_files/gtdbtk_r207_data.tar.gz>`_
      - 2.0.0
diff --git a/docs/src/references.rst b/docs/src/references.rst
index d59f960b..7aadd923 100644
--- a/docs/src/references.rst
+++ b/docs/src/references.rst
@@ -37,14 +37,14 @@ References
      - Eddy SR. 2011. `Accelerated profile HMM searches <https://www.ncbi.nlm.nih.gov/pubmed/22039361>`_. *PLOS Comp. Biol.*, 7:e1002195.
    * - `pplacer <http://matsen.fhcrc.org/pplacer/>`_
      - Matsen FA, et al. 2010. `pplacer: linear time maximum-likelihood and Bayesian phylogenetic placement of sequences onto a fixed reference tree <https://www.ncbi.nlm.nih.gov/pubmed/21034504>`_. *BMC Bioinformatics*, 11:538.
-   * - `FastANI <https://github.com/ParBLiSS/FastANI>`_
-     - Jain C, et al. 2019. `High-throughput ANI Analysis of 90K Prokaryotic Genomes Reveals Clear Species Boundaries <https://www.nature.com/articles/s41467-018-07641-9>`_. *Nat. Communications*, doi: 10.1038/s41467-018-07641-9.
+   * - `skani <https://github.com/bluenote-1577/skani>`_
+     - Shaw J. and Yu Y.W. 2023. `Fast and robust metagenomic sequence comparison through sparse chaining with skani <https://www.nature.com/articles/s41592-023-02018-3>`_. *Nature Methods*, 20, pages1661–1665 (2023).
    * - `FastTree <http://www.microbesonline.org/fasttree/>`_
      - Price MN, et al. 2010. `FastTree 2 - Approximately Maximum-Likelihood Trees for Large Alignments <https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2835736/>`_. *PLoS One*, 5, e9490.
    * - `Mash <https://github.com/marbl/Mash>`_
      - Ondov BD, et al. 2016. `Mash: fast genome and metagenome distance estimation using MinHash <https://genomebiology.biomedcentral.com/articles/10.1186/s13059-016-0997-x>`_. *Genome Biol* 17, 132. doi: doi: 10.1186/s13059-016-0997-x.
    * - `DendroPy <https://dendropy.org/>`_
-     - Sukumaran, J. and Mark T. Holder. 2010. DendroPy: A Python library for phylogenetic computing. Bioinformatics 26: 1569-1571.
+     - Sukumaran J. and Mark T. Holder. 2010. DendroPy: A Python library for phylogenetic computing. Bioinformatics 26: 1569-1571.
    * - `NumPy <https://numpy.org/>`_
      - Harris, C.R., Millman, K.J., van der Walt, S.J. et al. Array programming with NumPy. Nature 585, 357–362 (2020). DOI: `0.1038/s41586-020-2649-2 <https://doi.org/10.1038/s41586-020-2649-2>`_
    * - `tqdm <https://github.com/tqdm/tqdm>`_
diff --git a/gtdbtk/__init__.py b/gtdbtk/__init__.py
index 50a9dc77..9a6a5f1c 100644
--- a/gtdbtk/__init__.py
+++ b/gtdbtk/__init__.py
@@ -29,4 +29,4 @@
 __status__ = 'Production'
 __title__ = 'GTDB-Tk'
 __url__ = 'https://github.com/Ecogenomics/GTDBTk'
-__version__ = '2.3.2'
+__version__ = '2.4.0'
diff --git a/gtdbtk/ani_rep.py b/gtdbtk/ani_rep.py
index 9d807075..ef9905b6 100644
--- a/gtdbtk/ani_rep.py
+++ b/gtdbtk/ani_rep.py
@@ -9,7 +9,7 @@
 from gtdbtk.config.common import CONFIG
 from gtdbtk.config.output import DIR_ANI_REP_INT_MASH
 from gtdbtk.exceptions import GTDBTkExit
-from gtdbtk.external.fastani import FastANI
+from gtdbtk.external.skani import SkANI
 from gtdbtk.external.mash import Mash
 from gtdbtk.files.gtdb_radii import GTDBRadiiFile
 from gtdbtk.tools import get_ref_genomes
@@ -38,7 +38,7 @@ def check_dependencies(no_mash):
         no_mash : bool
             True if Mash will be used, False otherwise.
         """
-        dependencies = ['fastANI']
+        dependencies = ['skani']
         if not no_mash:
             dependencies.append('mash')
         check_dependencies(dependencies)
@@ -71,22 +71,22 @@ def run(self, genomes, no_mash, mash_d, out_dir, prefix, mash_k, mash_v, mash_s,
             The path to read/write the pre-computed Mash reference sketch database.
         """
         max_mash_dist = mash_d
-        fastani_results = self.run_mash_fastani(genomes, no_mash, mash_d, out_dir,
+        skani_results = self.run_mash_skani(genomes, no_mash, mash_d, out_dir,
                                                 prefix, mash_k, mash_v,
                                                 mash_s, max_mash_dist, mash_db=mash_db)
 
         taxonomy = Taxonomy().read(CONFIG.TAXONOMY_FILE, canonical_ids=True)
-        ani_summary_file = ANISummaryFile(out_dir, prefix, fastani_results, taxonomy)
+        ani_summary_file = ANISummaryFile(out_dir, prefix, skani_results, taxonomy)
         ani_summary_file.write()
         ANIClosestFile(out_dir,
                        prefix,
-                       fastani_results,
+                       skani_results,
                        genomes,
                        min_af,
                        taxonomy)
 
-    def run_mash_fastani(self,genomes, no_mash, max_d, out_dir, prefix, mash_k, mash_v, mash_s, mash_max_dist=100, mash_db=None):
-        """Runs the mash and fastani pipeline.
+    def run_mash_skani(self,genomes, no_mash, max_d, out_dir, prefix, mash_k, mash_v, mash_s, mash_max_dist=100, mash_db=None):
+        """Runs the mash and skani pipeline.
         This step is separated from the run function because it is called from 2 different
         functions in the gtdbtk ( classify and ani_reps).
 
@@ -136,10 +136,10 @@ def run_mash_fastani(self,genomes, no_mash, max_d, out_dir, prefix, mash_k, mash
             for qry_gid in genomes:
                 d_compare[qry_gid] = set(ref_genomes.keys())
 
-        self.logger.info(f'Calculating ANI with FastANI v{FastANI._get_version()}.')
-        fastani = FastANI(self.cpus, force_single=True)
-        fastani_results = fastani.run(d_compare, d_paths)
-        return fastani_results
+        self.logger.info(f'Calculating ANI with skani v{SkANI._get_version()}.')
+        skani = SkANI(self.cpus, force_single=True)
+        skani_results = skani.run(d_compare, d_paths)
+        return skani_results
 
 class ANISummaryFile(object):
     name = 'ani_summary.tsv'
@@ -154,7 +154,7 @@ def __init__(self, root=None, prefix=None, results=None, taxonomy=None,marker_se
         prefix : str
             The output file prefix.
         results: dict[str, dict[str, dict[str, float]]]
-            FastANI results.
+            skani results.
         taxonomy : dict[str, tuple[str, str, str, str, str, str, str]]
             d[unique_id] -> [d__<taxon>, ..., s__<taxon>]
         """
@@ -175,8 +175,8 @@ def get_col_order(ani_screen_step=False) -> List[str]:
         then format the row in that specific order."""
         cols = ['user_genome',
                    'reference_genome',
-                   'fastani_ani',
-                   'fastani_af',
+                   'skani_ani',
+                   'skani_af',
                    'reference_taxonomy']
         if ani_screen_step:
             cols += ['other_related_references(genome_id,species_name,radius,ANI,AF)']
@@ -231,7 +231,7 @@ def __init__(self, root, prefix, results, genomes, min_af, taxonomy):
         prefix : str
             The output file prefix.
         results: dict[str, dict[str, dict[str, float]]]
-            FastANI results.
+            skani results.
         genomes : dict[str, str]
             Dict[genome_id] = fasta_path
         min_af : float
@@ -250,7 +250,7 @@ def __init__(self, root, prefix, results, genomes, min_af, taxonomy):
 
     def _write(self):
         with open(self.path, 'w') as fh:
-            fh.write('user_genome\treference_genome\tfastani_ani\tfastani_af\t'
+            fh.write('user_genome\treference_genome\tskani_ani\tskani_af\t'
                      'reference_taxonomy\tsatisfies_gtdb_circumscription_criteria\n')
             for gid in sorted(self.genomes):
                 if gid in self.results:
diff --git a/gtdbtk/ani_screen.py b/gtdbtk/ani_screen.py
index 4b461a5d..7b474fb1 100644
--- a/gtdbtk/ani_screen.py
+++ b/gtdbtk/ani_screen.py
@@ -3,6 +3,7 @@
 
 from gtdbtk.ani_rep import ANIRep, ANISummaryFile
 from gtdbtk.biolib_lite.common import make_sure_path_exists, canonical_gid
+from gtdbtk.biolib_lite.seq_io import read_fasta
 
 from gtdbtk.biolib_lite.taxonomy import Taxonomy
 from gtdbtk.classify import Classify
@@ -14,7 +15,7 @@
 class ANIScreener(object):
     """Computes a list of genomes to a list of representatives."""
 
-    def __init__(self, cpus):
+    def __init__(self, cpus,af_threshold=None):
         """Instantiate the ANI rep class.
 
         Parameters
@@ -24,14 +25,15 @@ def __init__(self, cpus):
         """
         self.logger = logging.getLogger('timestamp')
         self.cpus = cpus
+        self.af_threshold = af_threshold if af_threshold else CONFIG.AF_THRESHOLD
         self.gtdb_radii = GTDBRadiiFile()
 
     def run_aniscreen(self,genomes, no_mash,out_dir,prefix, mash_k, mash_v, mash_s, mash_max_dist, mash_db):
 
         # If prescreen is set to True, then we will first run all genomes against a mash database
         # of all genomes in the reference package. The next step will be to classify those genomes with
-        # FastANI.
-        # All genomes classified with FastANI will be removed from the input genomes list for the
+        # skani.
+        # All genomes classified with skani will be removed from the input genomes list for the
         # rest of the pipeline.
         mash_classified_user_genomes = {}
         #if mash_db finishes with a backslash, it should be considered a directory
@@ -45,13 +47,31 @@ def run_aniscreen(self,genomes, no_mash,out_dir,prefix, mash_k, mash_v, mash_s,
 
         ani_rep = ANIRep(self.cpus)
         # we store all the mash information in the classify directory
-        fastani_results = ani_rep.run_mash_fastani(genomes, no_mash, mash_d, os.path.join(out_dir, DIR_ANISCREEN),
+
+        #we createa copy of the genomes dictionary to avoid modifying it
+        genomes_copy = genomes.copy()
+        # we remove all empty files from genomes.
+        for k in list(genomes_copy.keys()):
+            if os.path.getsize(genomes_copy[k]) == 0:
+                self.logger.warning(f'Genome {k} file is invalid for Mash. It will be removed from the sketch step.')
+                del genomes_copy[k]
+            else:
+                # we check if at least one contig is longer than the kmer size
+                seqs = read_fasta(genomes_copy[k])
+                if all(len(seq) < mash_k for seq in seqs.values()):
+                    self.logger.warning(
+                        f'Genome {k} file has all fasta sequences shorter than the k-mer size ({mash_k}).It will be removed from the sketch step.')
+                    del genomes_copy[k]
+
+
+
+        skani_results = ani_rep.run_mash_skani(genomes_copy, no_mash, mash_d, os.path.join(out_dir, DIR_ANISCREEN),
                                                     prefix, mash_k, mash_v, mash_s, mash_max_dist, mash_db)
 
         taxonomy = Taxonomy().read(CONFIG.TAXONOMY_FILE, canonical_ids=True)
 
-        mash_classified_user_genomes = self.sort_fastani_ani_screen(
-             fastani_results,taxonomy)
+        mash_classified_user_genomes = self.sort_skani_ani_screen(
+             skani_results,taxonomy)
 
         #We write the results in 2 different files for each domain
         reports = {}
@@ -71,33 +91,33 @@ def run_aniscreen(self,genomes, no_mash,out_dir,prefix, mash_k, mash_v, mash_s,
 
         return mash_classified_user_genomes,reports
 
-    def sort_fastani_ani_screen(self,fastani_results,taxonomy,bac_ar_diff=None):
-        """ When run mash/FastANI on all genomes before using pplacer, we need to sort those results and store them for
+    def sort_skani_ani_screen(self,skani_results,taxonomy,bac_ar_diff=None):
+        """ When run mash/skani on all genomes before using pplacer, we need to sort those results and store them for
         a later use
 
         Parameters
         ----------
-        fastani_results : dict
-            The results of the FastANI run
+        skani_results : dict
+            The results of the skani run
         taxonomy : dict
             The taxonomy of the reference genomes
         """
         classified_user_genomes = {}
 
         # sort the dictionary by ani then af
-        for gid in fastani_results.keys():
-            thresh_results = [(ref_gid, hit) for (ref_gid, hit) in fastani_results[gid].items() if
-                              hit['af'] >= CONFIG.AF_THRESHOLD and hit['ani'] >= self.gtdb_radii.get_rep_ani(
+        for gid in skani_results.keys():
+            thresh_results = [(ref_gid, hit) for (ref_gid, hit) in skani_results[gid].items() if
+                              hit['af'] >= self.af_threshold and hit['ani'] >= self.gtdb_radii.get_rep_ani(
                                   canonical_gid(ref_gid))]
-            all_results = [(ref_gid, hit) for (ref_gid, hit) in fastani_results[gid].items()]
+            all_results = [(ref_gid, hit) for (ref_gid, hit) in skani_results[gid].items()]
             closest = sorted(thresh_results, key=lambda x: (-x[1]['ani'], -x[1]['af']))
             all_closest = sorted(all_results, key=lambda x: (-x[1]['ani'], -x[1]['af']))
             if len(closest) > 0:
                 ref_gid, hit = closest[0]
                 hit_taxonomy = taxonomy[canonical_gid(ref_gid)]
-                if len(all_closest) > 1:
+                if len(all_results) > 1:
                     other_ref = '; '.join(Classify.formatnote(
-                        closest,taxonomy,Classify.parse_radius_file(), [ref_gid]))
+                        all_results,taxonomy,Classify.parse_radius_file(), [ref_gid]))
                     if len(other_ref) > 0:
                         hit['other_related_refs'] = other_ref
 
diff --git a/gtdbtk/biolib_lite/prodigal_biolib.py b/gtdbtk/biolib_lite/prodigal_biolib.py
index 527409e2..8639b521 100644
--- a/gtdbtk/biolib_lite/prodigal_biolib.py
+++ b/gtdbtk/biolib_lite/prodigal_biolib.py
@@ -85,7 +85,9 @@ def _producer(self, genome_file):
             if len(seqs) == 0:
                 self.logger.warning('Cannot call Prodigal on an empty genome. '
                                     'Skipped: {}'.format(genome_file))
-                return None
+                return (
+                genome_id, aa_gene_file, nt_gene_file, gff_file, best_translation_table, table_coding_density[4],
+                table_coding_density[11], True)
 
             with tempfile.TemporaryDirectory('gtdbtk_prodigal_tmp_') as tmp_dir:
 
@@ -180,7 +182,7 @@ def _producer(self, genome_file):
                                              genome_id + '.gff'), gff_file)
 
         return (genome_id, aa_gene_file, nt_gene_file, gff_file, best_translation_table, table_coding_density[4],
-                table_coding_density[11])
+                table_coding_density[11],False)
 
     def _consumer(self, produced_data, consumer_data):
         """Consume results from producer processes.
@@ -205,18 +207,19 @@ def _consumer(self, produced_data, consumer_data):
 
         ConsumerData = namedtuple(
             'ConsumerData',
-            'aa_gene_file nt_gene_file gff_file best_translation_table coding_density_4 coding_density_11')
+            'aa_gene_file nt_gene_file gff_file best_translation_table coding_density_4 coding_density_11 is_empty')
         if consumer_data is None:
             consumer_data = defaultdict(ConsumerData)
 
-        genome_id, aa_gene_file, nt_gene_file, gff_file, best_translation_table, coding_density_4, coding_density_11 = produced_data
+        genome_id, aa_gene_file, nt_gene_file, gff_file, best_translation_table, coding_density_4, coding_density_11, is_empty = produced_data
 
         consumer_data[genome_id] = ConsumerData(aa_gene_file,
                                                 nt_gene_file,
                                                 gff_file,
                                                 best_translation_table,
                                                 coding_density_4,
-                                                coding_density_11)
+                                                coding_density_11,
+                                                is_empty)
 
         return consumer_data
 
diff --git a/gtdbtk/classify.py b/gtdbtk/classify.py
index 4301912e..fc6666e5 100644
--- a/gtdbtk/classify.py
+++ b/gtdbtk/classify.py
@@ -37,8 +37,8 @@
 from gtdbtk.biolib_lite.taxonomy import Taxonomy
 from gtdbtk.config.output import *
 from gtdbtk.exceptions import GenomeMarkerSetUnknown, GTDBTkExit
-from gtdbtk.external.fastani import FastANI
 from gtdbtk.external.pplacer import Pplacer
+from gtdbtk.external.skani import SkANI
 
 from gtdbtk.files.classify_summary import ClassifySummaryFileAR53, ClassifySummaryFileBAC120, ClassifySummaryFileRow
 from gtdbtk.files.marker.copy_number import CopyNumberFileAR53, CopyNumberFileBAC120
@@ -65,7 +65,7 @@ class Classify(object):
     def __init__(self, cpus=1, pplacer_cpus=None, af_threshold=None,skip_pplacer=False):
         """Initialize."""
 
-        check_dependencies(['pplacer', 'guppy', 'fastANI'])
+        check_dependencies(['pplacer', 'guppy', 'skani'])
 
         self.skip_pplacer = skip_pplacer
 
@@ -339,16 +339,17 @@ def run(self,
             mash_max_dist=CONFIG.MASH_MAX_DISTANCE,
             mash_db=None,
             ani_summary_files=None,
-            all_classified_ani=False):
+            all_classified_ani=False,
+            all_failed_prodigal=False):
         """Classify genomes based on position in reference tree."""
-        if not all_classified_ani:
+        if not all_classified_ani and not all_failed_prodigal:
             _bac_gids, _ar_gids, bac_ar_diff = Markers().genome_domain(align_dir, prefix)
 
 
         # If prescreen is set to True, then we will first run all genomes against a mash database
         # of all genomes in the reference package. The next step will be to classify those genomes with
-        # FastANI.
-        # All genomes classified with FastANI will be removed from the input genomes list for the
+        # skani.
+        # All genomes classified with skani will be removed from the input genomes list for the
         # rest of the pipeline.
         mash_classified_user_genomes = {}
         if not skip_ani_screen:
@@ -367,10 +368,10 @@ def run(self,
 
             ani_rep = ANIRep(self.cpus)
             # we store all the mash information in the classify directory
-            fastani_results = ani_rep.run_mash_fastani(genomes, no_mash, mash_d, os.path.join(out_dir, DIR_ANISCREEN), prefix, mash_k, mash_v, mash_s,mash_max_dist, mash_db )
+            skani_results = ani_rep.run_mash_skani(genomes, no_mash, mash_d, os.path.join(out_dir, DIR_ANISCREEN), prefix, mash_k, mash_v, mash_s,mash_max_dist, mash_db )
 
-            mash_classified_user_genomes = self._sort_fastani_results_pre_pplacer(
-                fastani_results,bac_ar_diff)
+            mash_classified_user_genomes = self._sort_skani_results_pre_pplacer(
+                skani_results,bac_ar_diff)
 
             len_mash_classified_bac120 = len(mash_classified_user_genomes['bac120']) \
                 if 'bac120' in mash_classified_user_genomes else 0
@@ -385,11 +386,11 @@ def run(self,
         if skip_ani_screen and ani_summary_files is not None:
             # if the ani_Screen step was run, we need to load the results from the ani_summary_files
             # and use them to generate the final taxonomy file
-            mash_classified_user_genomes = self.load_fastani_results_pre_pplacer(ani_summary_files)
+            mash_classified_user_genomes = self.load_skani_results_pre_pplacer(ani_summary_files)
 
         output_files = {}
 
-        # if all genomes were classified with FastANI, we can stop here
+        # if all genomes were classified with skani, we can stop here
         # we write the summary files
         if all_classified_ani:
             # if mash_classified_user_genomes is has key marker_set_id, we
@@ -416,7 +417,7 @@ def run(self,
                                   os.path.join(out_dir, os.path.basename(PATH_AR53_SUMMARY_OUT.format(prefix=prefix))))
                     output_files.setdefault(marker_set_id, []).append(summary_file.path)
             return output_files
-        else:
+        elif not all_failed_prodigal:
             for marker_set_id in ('ar53', 'bac120'):
                 warning_counter, prodigal_failed_counter = 0, 0
                 if marker_set_id == 'ar53':
@@ -477,7 +478,22 @@ def run(self,
                     if mash_classified_user_genomes and marker_set_id in mash_classified_user_genomes:
                         list_summary_rows = mash_classified_user_genomes.get(marker_set_id)
                         for row in list_summary_rows:
-                            summary_file.add_row(row)
+                            # in some instance, when running the 3 classify steps independently, the genome might have been filtered out in the alignment step
+                            # but its still present in the ani screening and have a % > 95 ( this can happen with partial genomes) so Tk would try to report it twice in
+                            # the summary file, instead we report it as classify with ani, but with a warning from the alignment step
+                            # skani should reduce the number of such cases
+                            if row.gid in summary_file.rows:
+                                row_to_update = summary_file.get_row(row.gid)
+                                existing_warnings = row_to_update.warnings
+                                if existing_warnings is not None and row.warnings:
+                                    row.warnings.append(existing_warnings)
+                                elif existing_warnings is not None:
+                                    row.warnings = [existing_warnings]
+                                if row.warnings:
+                                    row.warnings = ';'.join(row.warnings)
+                                summary_file.update_row(row)
+                            else:
+                                summary_file.add_row(row)
 
                     if summary_file.has_row():
                         summary_file.write()
@@ -737,6 +753,27 @@ def run(self,
                     output_files.setdefault(marker_set_id, []).append(disappearing_genomes_file.path)
                 summary_file.write()
                 output_files.setdefault(marker_set_id, []).append(summary_file.path)
+        elif all_failed_prodigal:
+            marker_set_id ='bac120'
+            summary_file = ClassifySummaryFileBAC120(out_dir, prefix)
+
+            # Add failed genomes from prodigal and genomes with no markers in the bac120 summary file
+            # This is an executive direction: failed prodigal and genomes with no markers are not bacterial or archaeal
+            # but they need to be included in one of the summary file
+            prodigal_failed_counter = self.add_failed_genomes_to_summary(align_dir, summary_file, prefix)
+
+            if summary_file.has_row():
+                summary_file.write()
+                output_files.setdefault(marker_set_id, []).append(summary_file.path)
+                # Symlink to the summary file from the root
+                symlink_f(PATH_BAC120_SUMMARY_OUT.format(prefix=prefix),
+                              os.path.join(out_dir, os.path.basename(PATH_BAC120_SUMMARY_OUT.format(prefix=prefix))))
+                if prodigal_failed_counter > 0:
+                    self.logger.warning(f"{prodigal_failed_counter} of {len(genomes)} "
+                                        f"genome{'' if prodigal_failed_counter == 1 else 's'} "
+                                        f"ha{'s' if prodigal_failed_counter == 1 else 've'} been labeled as 'Unclassified'.")
+
+
         return output_files
 
     def _add_warning_to_row(self,row,msa_dict,
@@ -794,7 +831,7 @@ def _place_in_low_tree(self, tree_iter, number_low_trees, idx_tree, listg, msa_d
         return low_classify_tree, submsa_file_path
 
     @staticmethod
-    def _get_fastani_verification(tree, reference_ids, tt):
+    def _get_skani_verification(tree, reference_ids, tt):
         """
 
         Parameters
@@ -1177,7 +1214,7 @@ def _classify_red_topology(self, tree, msa_dict, percent_multihit_dict, trans_ta
     def _parse_tree(self, tree, genomes, msa_dict, percent_multihit_dict,genes, trans_table_dict, bac_ar_diff,
                     user_msa_file, red_dict, summary_file, pplacer_taxonomy_dict,warning_counter, high_classification,
                     debug_file, debugopt, tree_mapping_file, tree_iter, tree_mapping_dict_reverse):
-        # Genomes can be classified by using FastANI or RED values
+        # Genomes can be classified by using skani or RED values
         # We go through all leaves of the tree. if the leaf is a user
         # genome we take its parent node and look at all the leaves
         # for this node.
@@ -1187,34 +1224,34 @@ def _parse_tree(self, tree, genomes, msa_dict, percent_multihit_dict,genes, tran
 
         self.logger.log(CONFIG.LOG_TASK, 'Traversing tree to determine classification method.')
         if genes:
-            fastani_verification = {}
+            skani_verification = {}
         else:
-            fastani_verification, qury_nodes = self._get_fastani_verification(tree, self.reference_ids, tt)
+            skani_verification, qury_nodes = self._get_skani_verification(tree, self.reference_ids, tt)
 
-        #DEBUG: Skip FastANI step
-        #fastani_verification = {}
+        #DEBUG: Skip skani step
+        #skani_verification = {}
 
-        # we run a fastani comparison for each user genomes against the
+        # we run a skani comparison for each user genomes against the
         # selected genomes in the same genus
-        ##if not prescreening and len(fastani_verification) > 0:
-        if len(fastani_verification) > 0:
-            fastani = FastANI(cpus=self.cpus, force_single=True)
-            d_ani_compare, d_paths = self._get_fastani_genome_path(
-                fastani_verification, genomes)
+        ##if not prescreening and len(skani_verification) > 0:
+        if len(skani_verification) > 0:
+            skani = SkANI(cpus=self.cpus, force_single=True)
+            d_ani_compare, d_paths = self._get_skani_genome_path(
+                skani_verification, genomes)
             self.logger.log(CONFIG.LOG_TASK,
                             f'Calculating average nucleotide identity using '
-                            f'FastANI (v{fastani.version}).')
-            all_fastani_dict = fastani.run(d_ani_compare, d_paths)
+                            f'skani (v{skani.version}).')
+            all_skani_dict = skani.run(d_ani_compare, d_paths)
         else:
-            all_fastani_dict = {}
+            all_skani_dict = {}
 
-        classified_user_genomes, unclassified_user_genomes,warning_counter = self._sort_fastani_results(
-            fastani_verification, pplacer_taxonomy_dict, all_fastani_dict, msa_dict, percent_multihit_dict,
+        classified_user_genomes, unclassified_user_genomes,warning_counter = self._sort_skani_results(
+            skani_verification, pplacer_taxonomy_dict, all_skani_dict, msa_dict, percent_multihit_dict,
             trans_table_dict, bac_ar_diff,warning_counter, summary_file)
         #if not prescreening:
         if not genes:
             self.logger.info(f'{len(classified_user_genomes):,} genome(s) have '
-                             f'been classified using FastANI and pplacer.')
+                             f'been classified using skani and pplacer.')
         else:
             self.logger.info('ANI classification has been skipped (--genes option used).')
 
@@ -1227,7 +1264,7 @@ def _parse_tree(self, tree, genomes, msa_dict, percent_multihit_dict,genes, tran
                 mapping_row.rule = 'Rule 7'
                 tree_mapping_file.add_row(mapping_row)
 
-        # Iterate over each leaf node that was not classified with FastANI.
+        # Iterate over each leaf node that was not classified with skani.
         class_in_spe_tree = None
         phyla_in_spe_tree = None
         if tree_mapping_dict_reverse:
@@ -1394,12 +1431,28 @@ def add_filtered_genomes_to_summary(self, align_dir,warning_counter, summary_fil
             with open(filtered_file) as fin:
                 for line in fin:
                     infos = line.strip().split('\t')
-                    summary_row = ClassifySummaryFileRow()
-                    summary_row.gid = infos[0]
-                    summary_row.classification = f'Unclassified {domain}'
-                    summary_row.warnings = infos[1]
-                    summary_file.add_row(summary_row)
-                    warning_counter += 1
+                    # in some instance, when running the 3 classify steps independently, the genome might have been filtered out in the alignment step
+                    # but its still present in the ani screening and have a % > 95 ( this can happen with partial genomes) so Tk would try to report it twice in
+                    # the summary file, instead we report it as classified with ani, but with a warning from the alignment step
+                    # skani should reduce the number of such cases
+                    if infos[0] in summary_file.rows:
+                        row_to_update = summary_file.rows[infos[0]]
+                        # extra check
+                        if row_to_update.classification_method == 'ani_screen':
+                            existing_warnings = row_to_update.warnings
+                            if existing_warnings is not None:
+                                row_to_update.warnings = existing_warnings + ';' + infos[1]
+                            else:
+                                row_to_update.warnings = infos[1]
+                                warning_counter += 1
+                    else:
+                        summary_row = ClassifySummaryFileRow()
+                        summary_row.gid = infos[0]
+                        summary_row.classification = f'Unclassified {domain}'
+                        summary_row.warnings = infos[1]
+                        summary_file.add_row(summary_row)
+                        warning_counter += 1
+
         return warning_counter
 
     def add_failed_genomes_to_summary(self, align_dir, summary_file, prefix):
@@ -1451,100 +1504,104 @@ def formatnote(sorted_dict,gtdb_taxonomy,species_radius, labels):
         return note_list
 
 
-    def _sort_fastani_results_pre_pplacer(self,fastani_results,bac_ar_diff):
-        """ When run mash/FastANI on all genomes before using pplacer, we need to sort those results and store them for
+    def _sort_skani_results_pre_pplacer(self,skani_results,bac_ar_diff):
+        """ When run mash/skani on all genomes before using pplacer, we need to sort those results and store them for
         a later use
 
         Parameters
         ----------
-        fastani_results : dictionary listing the fastani ANI for each user genomes against the potential genomes
+        skani_results : dictionary listing the skani ANI for each user genomes against the potential genomes
 
         """
         classified_user_genomes = {}
 
         # sort the dictionary by ani then af
-        for gid in fastani_results.keys():
-            thresh_results = [(ref_gid, hit) for (ref_gid, hit) in fastani_results[gid].items() if
-                              hit['af'] >= CONFIG.AF_THRESHOLD and hit['ani'] >= self.gtdb_radii.get_rep_ani(
-                                  canonical_gid(ref_gid))]
+        for gid in skani_results.keys():
+            thresh_results = [(ref_gid, hit) for (ref_gid, hit) in skani_results[gid].items() if \
+                              hit['af'] >= self.af_threshold]
             closest = sorted(thresh_results, key=lambda x: (-x[1]['ani'], -x[1]['af']))
             if len(closest) > 0:
-                summary_row = ClassifySummaryFileRow()
-                summary_row.gid = gid
-                summary_row.classification_method = 'ani_screen'
-                if len(closest) > 1:
-                    other_ref = '; '.join(self.formatnote(
-                        closest,self.gtdb_taxonomy,self.species_radius, [gid]))
-                    if len(other_ref) == 0:
-                        summary_row.other_related_refs = None
-                    else:
-                        summary_row.other_related_refs = other_ref
-                summary_row.note = 'classification based on ANI only'
+                set_to_closest_rep = False
+                closest_rep,closet_rep_infos = closest[0]
+                if closet_rep_infos['ani'] >= self.gtdb_radii.get_rep_ani(canonical_gid(closest_rep)):
+                    set_to_closest_rep = True
+                    # remove the closest rep from the list
+                    trimmed_closest = closest[1:]
+                    other_ref = None
+                    summary_row = ClassifySummaryFileRow()
+                    summary_row.gid = gid
+                    summary_row.classification_method = 'ani_screen'
 
-                fastani_matching_reference = closest[0][0]
-                current_ani = fastani_results.get(gid).get(
-                    fastani_matching_reference).get('ani')
-                current_af = fastani_results.get(gid).get(
-                    fastani_matching_reference).get('af')
-
-                summary_row.fastani_ref = fastani_matching_reference
-                summary_row.fastani_ref_radius = str(
-                    self.species_radius.get(fastani_matching_reference))
-                summary_row.fastani_tax = ";".join(self.gtdb_taxonomy.get(
-                    add_ncbi_prefix(fastani_matching_reference)))
-                summary_row.fastani_ani = round(current_ani, 2)
-                summary_row.fastani_af = round(current_af, 3)
-                taxa_str = ";".join(self.gtdb_taxonomy.get(
-                    add_ncbi_prefix(fastani_matching_reference)))
-                summary_row.classification = standardise_taxonomy(
-                    taxa_str)
+                    if len(trimmed_closest) > 1:
+                        other_ref = '; '.join(self.formatnote(
+                            closest,self.gtdb_taxonomy,self.species_radius, [gid]))
+                        summary_row.other_related_refs = other_ref
+                    summary_row.note = 'classification based on ANI only'
+
+                    skani_matching_reference = closest_rep
+                    current_ani = skani_results.get(gid).get(
+                        skani_matching_reference).get('ani')
+                    current_af = skani_results.get(gid).get(
+                        skani_matching_reference).get('af')
+
+                    summary_row.closest_genome_ref = skani_matching_reference
+                    summary_row.closest_genome_ref_radius = str(
+                        self.species_radius.get(skani_matching_reference))
+                    summary_row.closest_genome_tax = ";".join(self.gtdb_taxonomy.get(
+                        add_ncbi_prefix(skani_matching_reference)))
+                    summary_row.closest_genome_ani = round(current_ani, 2)
+                    summary_row.closest_genome_af = round(current_af, 3)
+                    taxa_str = ";".join(self.gtdb_taxonomy.get(
+                        add_ncbi_prefix(skani_matching_reference)))
+                    summary_row.classification = standardise_taxonomy(
+                        taxa_str)
 
-                warnings = []
-                if gid in bac_ar_diff:
-                    warnings.append('Genome domain questionable ( {}% Bacterial, {}% Archaeal)'.format(
-                        bac_ar_diff.get(gid).get('bac120'),
-                        bac_ar_diff.get(gid).get('ar53')))
+                    warnings = []
+                    if gid in bac_ar_diff:
+                        warnings.append('Genome domain questionable ( {}% Bacterial, {}% Archaeal)'.format(
+                            bac_ar_diff.get(gid).get('bac120'),
+                            bac_ar_diff.get(gid).get('ar53')))
 
-                if len(warnings) > 0:
-                    summary_row.warnings = warnings
+                    if len(warnings) > 0:
+                        summary_row.warnings = warnings
 
-                if (taxa_str.split(';')[0]).split('__')[1] == 'Bacteria':
-                    domain = 'bac120'
-                else:
-                    domain = 'ar53'
-                classified_user_genomes.setdefault(domain, []).append(summary_row)
+                    if (taxa_str.split(';')[0]).split('__')[1] == 'Bacteria':
+                        domain = 'bac120'
+                    else:
+                        domain = 'ar53'
+                    classified_user_genomes.setdefault(domain, []).append(summary_row)
 
         return classified_user_genomes
 
 
 
 
-    def _sort_fastani_results(self, fastani_verification, pplacer_taxonomy_dict,
-                              all_fastani_dict, msa_dict, percent_multihit_dict,
+    def _sort_skani_results(self, skani_verification, pplacer_taxonomy_dict,
+                              all_skani_dict, msa_dict, percent_multihit_dict,
                               trans_table_dict, bac_ar_diff,warning_counter, summary_file):
         """Format the note field by concatenating all information in a sorted dictionary
 
         Parameters
         ----------
-        fastani_verification : dictionary listing the potential genomes associated with a user genome
+        skani_verification : dictionary listing the potential genomes associated with a user genome
         d[user_genome] = {"potential_g": [(potential_genome_in_same_genus,patristic distance)],
         "pplacer_g": genome_of_reference_selected_by_pplacer(if any)}
-        all_fastani_dict : dictionary listing the fastani ANI for each user genomes against the potential genomes
+        all_skani_dict : dictionary listing the skani ANI for each user genomes against the potential genomes
         d[user_genome]={ref_genome1:{"af":af,"ani":ani},ref_genome2:{"af":af,"ani":ani}}
         summaryfout: output file
 
         Returns
         -------
-        classified_user_genomes: list of genomes where FastANI and Placement in the reference tree have
+        classified_user_genomes: list of genomes where skani and Placement in the reference tree have
         predicted a taxonomy
-        unclassified_user_genomes: dictionary of genomes where FastANI and Placement in the reference tree have not
+        unclassified_user_genomes: dictionary of genomes where skani and Placement in the reference tree have not
         predicted a taxonomy
 
         """
         classified_user_genomes = {}
         unclassified_user_genomes = {}
 
-        for userleaf, potential_nodes in fastani_verification.items():
+        for userleaf, potential_nodes in skani_verification.items():
 
             summary_row = ClassifySummaryFileRow()
 
@@ -1561,26 +1618,26 @@ def _sort_fastani_results(self, fastani_verification, pplacer_taxonomy_dict,
                 pplacer_leafnode = potential_nodes.get("pplacer_g").taxon.label
                 if pplacer_leafnode[0:3] in ['RS_', 'GB_']:
                     pplacer_leafnode = pplacer_leafnode[3:]
-                if userleaf.taxon.label in all_fastani_dict:
+                if userleaf.taxon.label in all_skani_dict:
                     # import IPython; IPython.embed()
                     prefilter_af_reference_dictionary = {k: v for k, v in
-                                                         all_fastani_dict.get(userleaf.taxon.label).items() if v.get(
+                                                         all_skani_dict.get(userleaf.taxon.label).items() if v.get(
                             'af') >= self.af_threshold}
                     sorted_prefilter_af_dict = sorted(iter(prefilter_af_reference_dictionary.items()),
                                                       key=lambda _x_y1: (_x_y1[1]['ani'], _x_y1[1]['af']), reverse=True)
 
-                    sorted_dict = sorted(iter(all_fastani_dict.get(
+                    sorted_dict = sorted(iter(all_skani_dict.get(
                         userleaf.taxon.label).items()), key=lambda _x_y: (_x_y[1]['ani'], _x_y[1]['af']), reverse=True)
 
-                    fastani_matching_reference = None
+                    skani_matching_reference = None
                     if len(sorted_prefilter_af_dict) > 0:
                         if sorted_prefilter_af_dict[0][1].get('ani') >= self.species_radius.get(
                                 sorted_prefilter_af_dict[0][0]):
-                            fastani_matching_reference = sorted_prefilter_af_dict[0][0]
-                            current_ani = all_fastani_dict.get(userleaf.taxon.label).get(
-                                fastani_matching_reference).get('ani')
-                            current_af = all_fastani_dict.get(userleaf.taxon.label).get(
-                                fastani_matching_reference).get('af')
+                            skani_matching_reference = sorted_prefilter_af_dict[0][0]
+                            current_ani = all_skani_dict.get(userleaf.taxon.label).get(
+                                skani_matching_reference).get('ani')
+                            current_af = all_skani_dict.get(userleaf.taxon.label).get(
+                                skani_matching_reference).get('af')
                         else:
                             warnings.append(
                                 "Genome not assigned to closest species as it falls outside its pre-defined ANI radius")
@@ -1598,28 +1655,28 @@ def _sort_fastani_results(self, fastani_verification, pplacer_taxonomy_dict,
                     if len(warnings) > 0:
                         summary_row.warnings = ';'.join(warnings)
 
-                    if fastani_matching_reference is not None:
-                        summary_row.fastani_ref = fastani_matching_reference
-                        summary_row.fastani_ref_radius = str(
-                            self.species_radius.get(fastani_matching_reference))
-                        summary_row.fastani_tax = ";".join(self.gtdb_taxonomy.get(
-                            add_ncbi_prefix(fastani_matching_reference)))
-                        summary_row.fastani_ani = round(current_ani, 2)
-                        summary_row.fastani_af = round(current_af,3)
-                        if pplacer_leafnode == fastani_matching_reference:
+                    if skani_matching_reference is not None:
+                        summary_row.closest_genome_ref = skani_matching_reference
+                        summary_row.closest_genome_ref_radius = str(
+                            self.species_radius.get(skani_matching_reference))
+                        summary_row.closest_genome_tax = ";".join(self.gtdb_taxonomy.get(
+                            add_ncbi_prefix(skani_matching_reference)))
+                        summary_row.closest_genome_ani = round(current_ani, 2)
+                        summary_row.closest_genome_af = round(current_af,3)
+                        if pplacer_leafnode == skani_matching_reference:
                             if taxa_str.endswith("s__"):
                                 taxa_str = taxa_str + pplacer_leafnode
                             summary_row.classification = standardise_taxonomy(
                                 taxa_str)
-                            summary_row.closest_placement_ref = summary_row.fastani_ref
-                            summary_row.closest_placement_radius = summary_row.fastani_ref_radius
-                            summary_row.closest_placement_tax = summary_row.fastani_tax
-                            summary_row.closest_placement_ani = summary_row.fastani_ani
-                            summary_row.closest_placement_af = summary_row.fastani_af
+                            summary_row.closest_placement_ref = summary_row.closest_genome_ref
+                            summary_row.closest_placement_radius = summary_row.closest_genome_ref_radius
+                            summary_row.closest_placement_tax = summary_row.closest_genome_tax
+                            summary_row.closest_placement_ani = summary_row.closest_genome_ani
+                            summary_row.closest_placement_af = summary_row.closest_genome_af
                             summary_row.note = 'topological placement and ANI have congruent species assignments'
                             if len(sorted_dict) > 0:
                                 other_ref = '; '.join(self.formatnote(
-                                    sorted_dict,self.gtdb_taxonomy,self.species_radius, [fastani_matching_reference]))
+                                    sorted_dict,self.gtdb_taxonomy,self.species_radius, [skani_matching_reference]))
                                 if len(other_ref) == 0:
                                     summary_row.other_related_refs = None
                                 else:
@@ -1627,7 +1684,7 @@ def _sort_fastani_results(self, fastani_verification, pplacer_taxonomy_dict,
 
                         else:
                             taxa_str = ";".join(self.gtdb_taxonomy.get(
-                                add_ncbi_prefix(fastani_matching_reference)))
+                                add_ncbi_prefix(skani_matching_reference)))
                             summary_row.classification = standardise_taxonomy(
                                 taxa_str)
                             summary_row.closest_placement_ref = pplacer_leafnode
@@ -1635,16 +1692,16 @@ def _sort_fastani_results(self, fastani_verification, pplacer_taxonomy_dict,
                                 self.species_radius.get(pplacer_leafnode))
                             summary_row.closest_placement_tax = ";".join(self.gtdb_taxonomy.get(
                                 add_ncbi_prefix(pplacer_leafnode)))
-                            if pplacer_leafnode in all_fastani_dict.get(userleaf.taxon.label):
-                                summary_row.closest_placement_ani = round(all_fastani_dict.get(
+                            if pplacer_leafnode in all_skani_dict.get(userleaf.taxon.label):
+                                summary_row.closest_placement_ani = round(all_skani_dict.get(
                                     userleaf.taxon.label).get(pplacer_leafnode).get('ani'), 2)
-                                summary_row.closest_placement_af = round(all_fastani_dict.get(
+                                summary_row.closest_placement_af = round(all_skani_dict.get(
                                     userleaf.taxon.label).get(pplacer_leafnode).get('af'),3)
-                            summary_row.classification_method = 'ANI'
+                            summary_row.classification_method = 'taxonomic classification defined by topology and ANI'
 
                             if len(sorted_dict) > 0:
                                 other_ref = '; '.join(self.formatnote(
-                                    sorted_dict,self.gtdb_taxonomy,self.species_radius, [fastani_matching_reference, pplacer_leafnode]))
+                                    sorted_dict,self.gtdb_taxonomy,self.species_radius, [skani_matching_reference, pplacer_leafnode]))
                                 if len(other_ref) == 0:
                                     summary_row.other_related_refs = None
                                 else:
@@ -1657,10 +1714,10 @@ def _sort_fastani_results(self, fastani_verification, pplacer_taxonomy_dict,
                             self.species_radius.get(pplacer_leafnode))
                         summary_row.closest_placement_tax = ";".join(self.gtdb_taxonomy.get(
                             add_ncbi_prefix(pplacer_leafnode)))
-                        if pplacer_leafnode in all_fastani_dict.get(userleaf.taxon.label):
-                            summary_row.closest_placement_ani = round(all_fastani_dict.get(
+                        if pplacer_leafnode in all_skani_dict.get(userleaf.taxon.label):
+                            summary_row.closest_placement_ani = round(all_skani_dict.get(
                                 userleaf.taxon.label).get(pplacer_leafnode).get('ani'), 2)
-                            summary_row.closest_placement_af = round(all_fastani_dict.get(
+                            summary_row.closest_placement_af = round(all_skani_dict.get(
                                 userleaf.taxon.label).get(pplacer_leafnode).get('af'),3)
 
                         if len(sorted_dict) > 0:
@@ -1672,19 +1729,19 @@ def _sort_fastani_results(self, fastani_verification, pplacer_taxonomy_dict,
                                 summary_row.other_related_refs = other_ref
                         unclassified_user_genomes[userleaf.taxon.label] = summary_row
 
-            elif userleaf.taxon.label in all_fastani_dict:
+            elif userleaf.taxon.label in all_skani_dict:
                 prefilter_af_reference_dictionary = {k: v for k, v in
-                                                     all_fastani_dict.get(userleaf.taxon.label).items() if v.get(
+                                                     all_skani_dict.get(userleaf.taxon.label).items() if v.get(
                         'af') >= self.af_threshold}
                 sorted_prefilter_af_dict = sorted(iter(prefilter_af_reference_dictionary.items()),
                                                   key=lambda _x_y1: (_x_y1[1]['ani'], _x_y1[1]['af']), reverse=True)
-                sorted_dict = sorted(iter(all_fastani_dict.get(
+                sorted_dict = sorted(iter(all_skani_dict.get(
                     userleaf.taxon.label).items()), key=lambda _x_y2: (_x_y2[1]['ani'], _x_y2[1]['af']), reverse=True)
 
                 summary_row.gid = userleaf.taxon.label
                 summary_row.pplacer_tax = pplacer_taxonomy_dict.get(
                     userleaf.taxon.label)
-                summary_row.classification_method = 'ANI'
+                summary_row.classification_method = 'taxonomic classification defined by topology and ANI'
                 summary_row.msa_percent = aa_percent_msa(
                     msa_dict.get(summary_row.gid))
                 summary_row.tln_table = trans_table_dict.get(summary_row.gid)
@@ -1705,25 +1762,25 @@ def _sort_fastani_results(self, fastani_verification, pplacer_taxonomy_dict,
                         warning_counter += 1
                     if sorted_prefilter_af_dict[0][1].get('ani') >= self.species_radius.get(
                             sorted_prefilter_af_dict[0][0]):
-                        fastani_matching_reference = sorted_prefilter_af_dict[0][0]
-                        exception_genomes.append(fastani_matching_reference)
+                        skani_matching_reference = sorted_prefilter_af_dict[0][0]
+                        exception_genomes.append(skani_matching_reference)
 
                         taxa_str = ";".join(self.gtdb_taxonomy.get(
-                            add_ncbi_prefix(fastani_matching_reference)))
+                            add_ncbi_prefix(skani_matching_reference)))
                         summary_row.classification = standardise_taxonomy(
                             taxa_str)
 
-                        summary_row.fastani_ref = fastani_matching_reference
-                        summary_row.fastani_ref_radius = str(
-                            self.species_radius.get(fastani_matching_reference))
-                        summary_row.fastani_tax = ";".join(self.gtdb_taxonomy.get(
-                            add_ncbi_prefix(fastani_matching_reference)))
-                        current_ani = all_fastani_dict.get(userleaf.taxon.label).get(
-                            fastani_matching_reference).get('ani')
-                        summary_row.fastani_ani = round(current_ani, 2)
-                        current_af = all_fastani_dict.get(userleaf.taxon.label).get(
-                            fastani_matching_reference).get('af')
-                        summary_row.fastani_af = round(current_af,3)
+                        summary_row.closest_genome_ref = skani_matching_reference
+                        summary_row.closest_genome_ref_radius = str(
+                            self.species_radius.get(skani_matching_reference))
+                        summary_row.closest_genome_tax = ";".join(self.gtdb_taxonomy.get(
+                            add_ncbi_prefix(skani_matching_reference)))
+                        current_ani = all_skani_dict.get(userleaf.taxon.label).get(
+                            skani_matching_reference).get('ani')
+                        summary_row.closest_genome_ani = round(current_ani, 2)
+                        current_af = all_skani_dict.get(userleaf.taxon.label).get(
+                            skani_matching_reference).get('af')
+                        summary_row.closest_genome_af = round(current_af,3)
                         summary_row.note = 'topological placement and ANI have incongruent species assignments'
                         if len(warnings) > 0:
                             summary_row.warnings = ';'.join(warnings)
@@ -2131,7 +2188,7 @@ def root_with_outgroup(self, input_tree, taxonomy, outgroup_taxa):
 
         leaves_in_tree = sum([1 for _ in new_tree.leaf_node_iter()])
         while True:
-            rnd_ingroup_leaf = random.sample(ingroup_in_tree, 1)[0]
+            rnd_ingroup_leaf = random.sample(tuple(ingroup_in_tree), 1)[0]
             new_tree.reroot_at_edge(rnd_ingroup_leaf.edge,
                                     length1=0.5 * rnd_ingroup_leaf.edge_length,
                                     length2=0.5 * rnd_ingroup_leaf.edge_length)
@@ -2159,12 +2216,12 @@ def root_with_outgroup(self, input_tree, taxonomy, outgroup_taxa):
 
         return new_tree
 
-    def _get_fastani_genome_path(self, fastani_verification, genomes):
+    def _get_skani_genome_path(self, skani_verification, genomes):
         """Generates a queue of comparisons to be made and the paths to
         the corresponding genome id."""
         dict_compare, dict_paths = dict(), dict()
 
-        for qry_node, qry_dict in fastani_verification.items():
+        for qry_node, qry_dict in skani_verification.items():
             user_label = qry_node.taxon.label
             dict_paths[user_label] = genomes[user_label]
             dict_compare[user_label] = set()
@@ -2176,17 +2233,17 @@ def _get_fastani_genome_path(self, fastani_verification, genomes):
                 # TODEL UBA genomes
                 if shortleaf.startswith("UBA"):
                     ref_path = os.path.join(
-                        CONFIG.FASTANI_GENOMES,
+                        CONFIG.SKANI_GENOMES,
                         'UBA',
-                        shortleaf + CONFIG.FASTANI_GENOMES_EXT)
+                        shortleaf + CONFIG.SKANI_GENOMES_EXT)
                 else:
                     ref_path = os.path.join(
-                        CONFIG.FASTANI_GENOMES,
+                        CONFIG.SKANI_GENOMES,
                         self.parse_leaf_to_dir_path(shortleaf),
-                        shortleaf + CONFIG.FASTANI_GENOMES_EXT)
+                        shortleaf + CONFIG.SKANI_GENOMES_EXT)
 
                 if not os.path.isfile(ref_path):
-                    raise GTDBTkExit(f'Reference genome missing from FastANI database: {ref_path}')
+                    raise GTDBTkExit(f'Reference genome missing from skani database: {ref_path}')
 
                 dict_compare[user_label].add(shortleaf)
                 dict_paths[shortleaf] = ref_path
@@ -2200,45 +2257,45 @@ def get_authorised_rank(self, order_in_spe_tree,rank_index):
                 results.append(v[rank_index])
         return list(set(results))
 
-    def load_fastani_results_pre_pplacer(self,ani_summary_files):
-        """Load the FastANI results for the genomes classified with ANI Screen."""
-        fastani_results={}
+    def load_skani_results_pre_pplacer(self,ani_summary_files):
+        """Load the skani results for the genomes classified with ANI Screen."""
+        skani_results={}
         for domain,ani_summary_file_path in ani_summary_files.items():
             ani_summary_file = ANISummaryFile(ani_summary_file_path)
-            fastani_results[domain]=ani_summary_file.read()
+            skani_results[domain]=ani_summary_file.read()
 
         classified_user_genomes = {}
 
         # sort the dictionary by ani then af
-        for domain,fastani_results in fastani_results.items():
-            for gid,ani_results in fastani_results.items():
+        for domain,skani_results in skani_results.items():
+            for gid,ani_results in skani_results.items():
                 summary_row = ClassifySummaryFileRow()
                 summary_row.gid = gid
                 summary_row.classification_method = 'ani_screen'
                 summary_row.note = 'classification based on ANI only'
 
-                fastani_matching_reference = list(ani_results)[0]
+                skani_matching_reference = list(ani_results)[0]
 
-                if 'other_refs' in fastani_results.get(gid).get(
-                    fastani_matching_reference):
-                    other_refs = fastani_results.get(gid).get(
-                        fastani_matching_reference).get('other_refs')
+                if 'other_refs' in skani_results.get(gid).get(
+                    skani_matching_reference):
+                    other_refs = skani_results.get(gid).get(
+                        skani_matching_reference).get('other_refs')
                     if other_refs:
                         summary_row.other_related_refs = other_refs
-                current_ani = fastani_results.get(gid).get(
-                        fastani_matching_reference).get('ani')
-                current_af = fastani_results.get(gid).get(
-                        fastani_matching_reference).get('af')
-
-                summary_row.fastani_ref = fastani_matching_reference
-                summary_row.fastani_ref_radius = str(
-                        self.species_radius.get(fastani_matching_reference))
-                summary_row.fastani_tax = fastani_results.get(gid).get(
-                        fastani_matching_reference).get('taxonomy')
-                summary_row.fastani_ani = round(current_ani, 2)
-                summary_row.fastani_af = round(current_af,3)
+                current_ani = skani_results.get(gid).get(
+                        skani_matching_reference).get('ani')
+                current_af = skani_results.get(gid).get(
+                        skani_matching_reference).get('af')
+
+                summary_row.closest_genome_ref = skani_matching_reference
+                summary_row.closest_genome_ref_radius = str(
+                        self.species_radius.get(skani_matching_reference))
+                summary_row.closest_genome_tax = skani_results.get(gid).get(
+                        skani_matching_reference).get('taxonomy')
+                summary_row.closest_genome_ani = round(current_ani, 2)
+                summary_row.closest_genome_af = round(current_af,3)
                 taxa_str = ";".join(self.gtdb_taxonomy.get(
-                        add_ncbi_prefix(fastani_matching_reference)))
+                        add_ncbi_prefix(skani_matching_reference)))
                 summary_row.classification = standardise_taxonomy(
                         taxa_str)
 
@@ -2251,11 +2308,11 @@ def convert_rows_to_dict(self,list_of_summary_rows):
         for domain,rows in list_of_summary_rows.items():
             for row in rows:
                 if row.other_related_refs:
-                    infos ={row.gid:{row.fastani_ref: {'ani': row.fastani_ani,
-                                                 'af': row.fastani_af,
+                    infos ={row.gid:{row.closest_genome_ref: {'ani': row.closest_genome_ani,
+                                                 'af': row.closest_genome_af,
                                                  'other_refs': row.other_related_refs}}}
                 else:
-                    infos ={row.gid:{row.fastani_ref: {'ani': row.fastani_ani, 'af': row.fastani_af}}}
+                    infos ={row.gid:{row.closest_genome_ref: {'ani': row.closest_genome_ani, 'af': row.closest_genome_af}}}
                 dict_of_rows.setdefault(domain, {}).update(infos)
 
         return dict_of_rows
diff --git a/gtdbtk/cli.py b/gtdbtk/cli.py
index 739e1160..6cb79f7b 100644
--- a/gtdbtk/cli.py
+++ b/gtdbtk/cli.py
@@ -33,8 +33,8 @@ def __temp_dir(group):
 
 def __genes(group):
     group.add_argument('--genes', action='store_true', default=False,
-                       help='indicates input files contain called genes (skip gene calling).Warning: This flag will also skip '
-                            'the ANI comparison steps (ani_screen and classification).')
+                       help='indicates input files contain predicted proteins as amino acids (skip gene calling).'
+                            'Warning: This flag will skip the ANI comparison steps (ani_screen and classification).')
 
 
 def __genome_dir(group):
@@ -76,11 +76,11 @@ def __extension(group):
 
 def __skip_ani_screen(group):
     group.add_argument('--skip_ani_screen', action="store_true", default=False,
-                       help="Skip the ani_screening step to classify genomes using mash and FastANI ")
+                       help="Skip the ani_screening step to classify genomes using mash and skani.")
 
 def __skip_gtdb_refs(group):
     group.add_argument('--skip_gtdb_refs', action="store_true", default=False,
-                       help='do not include GTDB reference genomes in multiple sequence alignment')
+                       help='do not include GTDB reference genomes in multiple sequence alignment.')
 
 
 def __taxa_filter(group):
@@ -544,7 +544,7 @@ def get_main_parser():
             __mash_d(grp)
             __mash_v(grp)
             __mash_db(grp)
-        with arg_group(parser, 'optional FastANI arguments') as grp:
+        with arg_group(parser, 'optional skani arguments') as grp:
             __min_af(grp)
         with arg_group(parser, 'optional arguments') as grp:
             __extension(grp)
diff --git a/gtdbtk/config/common.py b/gtdbtk/config/common.py
index 8a18e90f..2c016c70 100644
--- a/gtdbtk/config/common.py
+++ b/gtdbtk/config/common.py
@@ -11,8 +11,8 @@ class __GTDBTkCommonConfig:
     that requires the setting of the GTDB-Tk reference data path.
     """
 
-    MIN_REF_DATA_VERSION = 'r207'
-    COMPATIBLE_REF_DATA_VERSIONS = ['r207','r214']
+    MIN_REF_DATA_VERSION = 'r220'
+    COMPATIBLE_REF_DATA_VERSIONS = ['r220']
 
     BACKBONE_PPLACER_REF_PKG = 'gtdbtk_package_backbone.refpkg'
     CLASS_LEVEL_PPLACER_REF_PKG = 'gtdbtk.package.{iter}.refpkg'
@@ -88,15 +88,15 @@ class __GTDBTkCommonConfig:
     AR_MARKER_COUNT = 53
     BAC_MARKER_COUNT = 120
 
-    # Information about alignment Fraction to resolve fastANI results
+    # Information about alignment Fraction to resolve skani results
     AF_THRESHOLD = 0.5
 
     PPLACER_MIN_RAM_BAC_FULL = 320
     PPLACER_MIN_RAM_BAC_SPLIT = 55
     PPLACER_MIN_RAM_ARC = 40
 
-    FASTANI_SPECIES_THRESHOLD = 95.0
-    FASTANI_GENOMES_EXT = "_genomic.fna.gz"
+    SKANI_SPECIES_THRESHOLD = 95.0
+    SKANI_GENOMES_EXT = "_genomic.fna.gz"
 
     # Mash configuration
     MASH_SKETCH_FILE = 'gtdb_ref_sketch.msh'
@@ -150,8 +150,8 @@ def PPLACER_DIR(self):
         return os.path.join(self.GENERIC_PATH, 'pplacer/')
 
     @property
-    def FASTANI_DIR(self):
-        return os.path.join(self.GENERIC_PATH, 'fastani/')
+    def SKANI_DIR(self):
+        return os.path.join(self.GENERIC_PATH, 'skani/')
 
     @property
     def MASH_DIR(self):
@@ -313,12 +313,12 @@ def PPLACER_RPS23_REF_PKG(self):
         return f"gtdb_{self.VERSION_DATA}_rps23.refpkg"
 
     @property
-    def FASTANI_GENOMES(self):
-        return os.path.join(self.FASTANI_DIR, "database/")
+    def SKANI_GENOMES(self):
+        return os.path.join(self.SKANI_DIR, "database/")
 
     @property
-    def FASTANI_GENOME_LIST(self):
-        return os.path.join(self.FASTANI_DIR, "genome_paths.tsv")
+    def SKANI_GENOME_LIST(self):
+        return os.path.join(self.SKANI_DIR, "genome_paths.tsv")
 
     @property
     def MRCA_RED_BAC120(self):
@@ -333,31 +333,20 @@ def get_REF_HASHES(self,version=None):
         if version is not None and version not in compatible_versions:
             raise ValueError(f"Version {version} is not compatible with this version of GTDB-Tk. Compatible versions are {compatible_versions}")
 
-        if version is None or version==214:
+        if version is None or version==220:
             return {
-                self.PPLACER_DIR: '6786e9fc16b31db7d6eaaa9f8cfa87a8a4974434',
-                self.MASK_DIR: '8d5a2139feabbb70789c62155f3761d2aeed1601',
+                self.PPLACER_DIR: '75fdd0e093c9af6a73cb510c3d0cd2041265e093',
+                self.MASK_DIR: 'f4b8ebfa59526a7a86f09752b47e8de1efc384c7',
                 self.MARKER_DIR: '163f542c3f0a40f59df45d453aa235b39aa96e27',
-                self.RADII_DIR: '4753acc920001a1400788ee89cb4632900449055',
-                self.MSA_FOLDER: '75df495678a121497e14346b453caf42f4b03922',
-                self.METADATA_DIR: 'a089cc36bf79a40c7506019accc5f93e940d9fed',
-                self.TAX_FOLDER: '89b12cf8106f326887599dcb30ef94ebba142035',
-                self.FASTANI_DIR: 'e12824beccc15fe67a373e2aa8eee72feecf89c6',
-                self.RED_DIR: 'c24a2f48bb0c1df38f92a8f526aa846f596c94c6'
-            }
-        elif version==207:
-            return {
-                self.PPLACER_DIR: '20903925a856a58b102a7b0ce160c5cbd2cf675b',
-                self.MASK_DIR: '50e414a9de18170e8cb97f990f89ff60a0fe29d5',
-                self.MARKER_DIR: '163f542c3f0a40f59df45d453aa235b39aa96e27',
-                self.RADII_DIR: '8fd13b1c5d7a7b073ba96fb628581613b293a374',
-                self.MSA_FOLDER: '24f250d7cf0eb0bc65dccd2f3c9247e553ea322f',
-                self.METADATA_DIR: '9772fbeac1311b31e10293fa610eb33aa1ec8e15',
-                self.TAX_FOLDER: '6fb0233b05633242369b40c026fd1ee53e266afa',
-                self.FASTANI_DIR: '973c456c02f55bb82908a6811c7076e207e9b206',
-                self.RED_DIR: '7b8b67b3157204b470c9eb809d3c39c4effffabc'
+                self.RADII_DIR: '63d06ecc8b4547addd22c5b06ada4a28c5332bcc',
+                self.MSA_FOLDER: '3d5c1cf5346b244fcb0a9d48d2f1a9358a71cc7a',
+                self.METADATA_DIR: '01b8c23253cef097b1bc233d609dae9eb84c98e2',
+                self.TAX_FOLDER: '6758173fa61ae4a77f5588ec2874ea52ed345feb',
+                self.SKANI_DIR: 'ff58a1d7e0584da324d140701ee12cead4f0df9d',
+                self.RED_DIR: '206bd781997fffbac951b4437dd75e6543139fd6'
             }
 
+
     REF_HASHES = property(get_REF_HASHES)
 
 
diff --git a/gtdbtk/config/output.py b/gtdbtk/config/output.py
index 00205fdb..dded566d 100644
--- a/gtdbtk/config/output.py
+++ b/gtdbtk/config/output.py
@@ -63,8 +63,8 @@
 #Command: ani_screen
 DIR_ANISCREEN = join(DIR_CLASSIFY, 'ani_screen')
 DIR_ANISCREEN_INTERMEDIATE = join(DIR_ANISCREEN, 'intermediate_results')
-PATH_BAC120_ANI_FASTANI = join(DIR_ANISCREEN, '{prefix}.bac120_fastani.summary.tsv')
-PATH_AR53_ANI_FASTANI = join(DIR_ANISCREEN, '{prefix}.ar53_fastani.summary.tsv')
+PATH_BAC120_ANI_SKANI = join(DIR_ANISCREEN, '{prefix}.bac120_skani.summary.tsv')
+PATH_AR53_ANI_SKANI = join(DIR_ANISCREEN, '{prefix}.ar53_skani.summary.tsv')
 
 
 DIR_PPLACER = join(DIR_CLASSIFY_INTERMEDIATE, 'pplacer')
diff --git a/gtdbtk/exceptions.py b/gtdbtk/exceptions.py
index d20eb33c..7bfa06b3 100644
--- a/gtdbtk/exceptions.py
+++ b/gtdbtk/exceptions.py
@@ -121,8 +121,8 @@ def __init__(self, message=''):
         GTDBTkException.__init__(self, message)
 
 
-class FastANIException(GTDBTkException):
-    """ Thrown when an exception is encountered while running FastANI. """
+class SkANIException(GTDBTkException):
+    """ Thrown when an exception is encountered while running skani. """
 
     def __init__(self, message=''):
         GTDBTkException.__init__(self, message)
diff --git a/gtdbtk/external/pplacer.py b/gtdbtk/external/pplacer.py
index c91c5aeb..cb7ef2b4 100644
--- a/gtdbtk/external/pplacer.py
+++ b/gtdbtk/external/pplacer.py
@@ -21,6 +21,7 @@
 import queue
 import re
 import subprocess
+import sys
 
 from tqdm import tqdm
 
@@ -76,6 +77,8 @@ def run(self, cpus, model, ref_pkg, json_out, msa_file, pplacer_out,
 
         out_q = mp.Queue()
         pid = mp.Value('i', 0)
+        # print(f'Running pplacer with the following command: {" ".join(args)}')
+        # sys.exit(0)
         p_worker = mp.Process(target=self._worker, args=(
             args, out_q, pplacer_out, pid))
         p_writer = mp.Process(target=self._writer, args=(out_q, pid))
diff --git a/gtdbtk/external/prodigal.py b/gtdbtk/external/prodigal.py
index 28d6c9ae..d6410abf 100644
--- a/gtdbtk/external/prodigal.py
+++ b/gtdbtk/external/prodigal.py
@@ -93,7 +93,7 @@ def _run_prodigal(self, genome_id, fasta_path, usr_tln_table):
         if all([file_has_checksum(x) for x in out_files]):
             tln_table_file.read()
             self.warnings.info(f'Skipped Prodigal processing for: {genome_id}')
-            return aa_gene_file, nt_gene_file, gff_file, tln_table_file.path, tln_table_file.best_tln_table, True
+            return aa_gene_file, nt_gene_file, gff_file, tln_table_file.path, tln_table_file.best_tln_table, True , False
 
         # Run Prodigal
         prodigal = BioLibProdigal(1, False)
@@ -110,25 +110,34 @@ def _run_prodigal(self, genome_id, fasta_path, usr_tln_table):
 
         summary_stats = summary_stats[list(summary_stats.keys())[0]]
 
-        # rename output files to adhere to GTDB conventions and desired genome
-        # ID
-        shutil.move(summary_stats.aa_gene_file, aa_gene_file)
-        shutil.move(summary_stats.nt_gene_file, nt_gene_file)
-        shutil.move(summary_stats.gff_file, gff_file)
+        if summary_stats.is_empty:
+            shutil.rmtree(output_dir)
+            if self.force:
+                return summary_stats
+            else:
+                raise Exception("An error was encountered while running Prodigal.")
 
-        # save translation table information
-        tln_table_file.best_tln_table = summary_stats.best_translation_table
-        tln_table_file.coding_density_4 = round(summary_stats.coding_density_4 * 100, 2)
-        tln_table_file.coding_density_11 = round(summary_stats.coding_density_11 * 100, 2)
-        tln_table_file.write()
+        else:
+            # rename output files to adhere to GTDB conventions and desired genome
+            # ID
+            shutil.move(summary_stats.aa_gene_file, aa_gene_file)
+            shutil.move(summary_stats.nt_gene_file, nt_gene_file)
+            shutil.move(summary_stats.gff_file, gff_file)
 
-        # Create a hash of each file
-        for out_file in out_files:
-            if out_file is not None:
-                with open(out_file + CHECKSUM_SUFFIX, 'w') as fh:
-                    fh.write(sha256(out_file))
+            # save translation table information
+            tln_table_file.best_tln_table = summary_stats.best_translation_table
+            tln_table_file.coding_density_4 = round(summary_stats.coding_density_4 * 100, 2)
+            tln_table_file.coding_density_11 = round(summary_stats.coding_density_11 * 100, 2)
+            tln_table_file.write()
 
-        return aa_gene_file, nt_gene_file, gff_file, tln_table_file.path, summary_stats.best_translation_table, False
+            # Create a hash of each file
+            for out_file in out_files:
+                if out_file is not None:
+                    with open(out_file + CHECKSUM_SUFFIX, 'w') as fh:
+                        fh.write(sha256(out_file))
+
+
+        return aa_gene_file, nt_gene_file, gff_file, tln_table_file.path, summary_stats.best_translation_table, False , summary_stats.is_empty
 
     def _worker(self, out_dict, worker_queue, writer_queue, n_skipped):
         """This worker function is invoked in a process."""
@@ -144,7 +153,7 @@ def _worker(self, out_dict, worker_queue, writer_queue, n_skipped):
 
             # Only proceed if an error didn't occur in BioLib Prodigal
             if rtn_files:
-                aa_gene_file, nt_gene_file, gff_file, translation_table_file, best_translation_table, was_skipped = rtn_files
+                aa_gene_file, nt_gene_file, gff_file, translation_table_file, best_translation_table, was_skipped ,is_empty = rtn_files
                 if was_skipped:
                     with n_skipped.get_lock():
                         n_skipped.value += 1
@@ -152,7 +161,8 @@ def _worker(self, out_dict, worker_queue, writer_queue, n_skipped):
                                   "nt_gene_path": nt_gene_file,
                                   "gff_path": gff_file,
                                   "translation_table_path": translation_table_file,
-                                  "best_translation_table": best_translation_table}
+                                  "best_translation_table": best_translation_table,
+                                  "is_empty": is_empty}
 
                 out_dict[genome_id] = prodigal_infos
             writer_queue.put(genome_id)
@@ -225,35 +235,46 @@ def run(self, genomic_files, tln_tables):
             self.logger.warning(f'Prodigal skipped {n_skipped.value} {genome_s} '
                                 f'due to pre-existing data, see warnings.log')
 
+
         # Report on any genomes which failed to have any genes called
         result_dict = dict()
         lq_gids = list()
+        empty_gids = list()
         fails = open(self.failed_genomes_file,'w')
         for gid, gid_dict in out_dict.items():
-            if os.path.getsize(gid_dict['aa_gene_path']) <= 1:
+            if gid_dict['is_empty']:
+                empty_gids.append(gid)
+            elif os.path.getsize(gid_dict['aa_gene_path']) <= 1:
                 lq_gids.append(gid)
             else:
                 result_dict[gid] = gid_dict
 
         if len(lq_gids) > 0:
-            self.logger.warning(f'Skipping {len(lq_gids)} of {len(genomic_files)} '
+            self.logger.warning(f'Skipping {len(lq_gids+empty_gids)} of {len(genomic_files)} '
                                 f'genomes as no genes were called by Prodigal. '
                                 f'Check the genome quality (see gtdbtk.warnings.log).')
-            self.warnings.warning(f'The following {len(lq_gids)} genomes have '
+            self.warnings.warning(f'The following {len(lq_gids+empty_gids)} genomes have '
                                   f'been excluded from analysis due to Prodigal '
                                   f'failing to call any genes:')
 
 
             # If there are few low-quality genomes just output to console.
-            if len(lq_gids) > 10:
+            if len(lq_gids+empty_gids) > 10:
                 for lq_gid in lq_gids:
                     self.warnings.info(lq_gid)
                     fails.write(f'{lq_gid}\tNo genes were called by Prodigal\n')
+                for empty_gid in empty_gids:
+                    self.warnings.info(empty_gid)
+                    fails.write(f'{empty_gid}\tEmpty file\n')
             else:
                 for lq_gid in lq_gids:
                     self.logger.warning(f'Skipping: {lq_gid}')
                     self.warnings.info(lq_gid)
                     fails.write(f'{lq_gid}\tNo genes were called by Prodigal\n')
+                for empty_gid in empty_gids:
+                    self.logger.warning(f'Skipping: {empty_gid}')
+                    self.warnings.info(empty_gid)
+                    fails.write(f'{empty_gid}\tEmpty file\n')
 
         fails.close()
         return result_dict
diff --git a/gtdbtk/external/skani.py b/gtdbtk/external/skani.py
new file mode 100644
index 00000000..335b1256
--- /dev/null
+++ b/gtdbtk/external/skani.py
@@ -0,0 +1,276 @@
+###############################################################################
+#                                                                             #
+#    This program is free software: you can redistribute it and/or modify     #
+#    it under the terms of the GNU General Public License as published by     #
+#    the Free Software Foundation, either version 3 of the License, or        #
+#    (at your option) any later version.                                      #
+#                                                                             #
+#    This program is distributed in the hope that it will be useful,          #
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
+#    GNU General Public License for more details.                             #
+#                                                                             #
+#    You should have received a copy of the GNU General Public License        #
+#    along with this program. If not, see <http://www.gnu.org/licenses/>.     #
+#                                                                             #
+###############################################################################
+
+import logging
+import multiprocessing as mp
+import os
+import re
+import shutil
+import subprocess
+import tempfile
+
+from gtdbtk.exceptions import GTDBTkExit
+from gtdbtk.tools import tqdm_log
+
+
+class SkANI(object):
+    """Python wrapper for skani (https://github.com/bluenote-1577/skani)"""
+
+    def __init__(self, cpus, force_single):
+        """Instantiate the class.
+
+        Parameters
+        ----------
+        cpus : int
+            The number of CPUs to use.
+        force_single : bool
+            True if the ql and rl calls should be done individually.
+        """
+        self.cpus = max(cpus, 1)
+        self.force_single = force_single
+        self.logger = logging.getLogger('timestamp')
+        self.version = self._get_version()
+        self._suppress_v1_warning = False
+
+    @staticmethod
+    def _get_version():
+        """Returns the version of skani on the system path.
+
+        Returns
+        -------
+        str
+            The string containing the skani version.
+        """
+        try:
+            proc = subprocess.Popen(['skani', '-V'], stdout=subprocess.PIPE,
+                                    stderr=subprocess.PIPE, encoding='utf-8')
+            stdout, stderr = proc.communicate()
+            version = re.search(r'skani (.+)', stdout)
+            return version.group(1)
+        except Exception as e:
+            return 'unknown'
+
+
+    def run(self, dict_compare, dict_paths,preset='--medium', report_progress=True):
+        """Runs skani in batch mode.
+
+        Parameters
+        ----------
+        dict_compare : dict[str, set[str]]
+            All query to reference comparisons to be made.
+        dict_paths : dict[str, str]
+            The path for each genome id being compared.
+
+        Returns
+        -------
+        dict[str, dict[str, dict[str, float]]]
+            A dictionary containing the ANI and AF for each comparison."""
+
+        # Create the multiprocessing items.
+        manager = mp.Manager()
+        q_worker = manager.Queue()
+        q_writer = manager.Queue()
+        q_results = manager.Queue()
+
+        # Populate the queue of comparisons in forwards and reverse direction.
+        n_total = 0
+        list_comparisons = [(user_id, ref_id) for user_id, values in dict_compare.items() for ref_id in values]
+        for qry, ref in list_comparisons:
+            n_total += 1
+            q_worker.put((qry, ref, dict_paths.get(qry), dict_paths.get(ref), preset))
+
+        # Set the terminate condition for each worker thread.
+        [q_worker.put(None) for _ in range(self.cpus)]
+
+        # Create each of the processes
+        p_workers = [mp.Process(target=self._worker,
+                                args=(q_worker, q_writer, q_results))
+                     for _ in range(self.cpus)]
+
+        p_writer = mp.Process(target=self._writer, args=(q_writer, n_total))
+
+        # Start each of the threads.
+        try:
+            # Start the writer and each processing thread.
+            p_writer.start()
+            for p_worker in p_workers:
+                p_worker.start()
+
+            # Wait until each worker has finished.
+            for p_worker in p_workers:
+                p_worker.join()
+
+                # Gracefully terminate the program.
+                if p_worker.exitcode != 0:
+                    raise GTDBTkExit('skani returned a non-zero exit code.')
+
+            # Stop the writer thread.
+            q_writer.put(None)
+            p_writer.join()
+
+        except Exception:
+            for p in p_workers:
+                p.terminate()
+            p_writer.terminate()
+            raise
+
+        # Process and return each of the results obtained
+        q_results.put(None)
+        return self._parse_result_queue(q_results)
+
+    def _worker(self, q_worker, q_writer, q_results):
+        """Operates skani in list mode.
+
+        Parameters
+        ----------
+        q_worker : mp.Queue
+            A multiprocessing queue containing the available jobs.
+        q_writer : mp.Queue
+            A multiprocessing queue to track progress.
+        q_results : mp.Queue
+            A multiprocessing queue containing raw results.
+        """
+        while True:
+            # Retrieve the next item, stop if the sentinel is found.
+            job = q_worker.get(block=True)
+            if job is None:
+                break
+
+            # Extract the values
+            q, r, q_path,r_path,preset = job
+
+            # Run skani
+            result = self.run_proc(q, r, q_path, r_path, preset)
+            q_results.put(result)
+            q_writer.put(True)
+
+        return True
+
+    def _writer(self, q_writer, n_total):
+        """The writer function, which reports the progress of the workers.
+
+        Parameters
+        ----------
+        q_writer : mp.Queue
+            A queue of genome ids which have been processed.
+        n_total : int
+            The total number of items to be processed.
+        """
+        with tqdm_log(unit='comparison', total=n_total) as p_bar:
+            for _ in iter(q_writer.get, None):
+                p_bar.update()
+
+    def run_proc(self, qid, rid, ql, rl, preset):
+        """Runs the skani process.
+
+        Parameters
+        ----------
+        q : str
+            The path to the query genome.
+        r : str
+            The path to the reference genome.
+        ql : str
+            The path to the query list file.
+        rl : str
+            The path to the reference list file.
+        output : str
+            The path to the output file.
+
+        Returns
+        -------
+        dict[str, dict[str, float]]
+            The ANI/AF of the query genomes to the reference genomes.
+        """
+        args = ['skani', 'dist',
+                preset,
+                '-q', ql,
+                '-r', rl,
+                '-o', '/dev/stdout']
+        # self.logger.debug(' '.join(args))
+        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE, encoding='utf-8')
+        stdout, stderr = proc.communicate()
+
+        if proc.returncode != 0:
+            self.logger.error('STDOUT:\n' + stdout)
+            self.logger.error('STDERR:\n' + stderr)
+            raise GTDBTkExit('skani returned a non-zero exit code.')
+
+        result_lines = stdout.splitlines()
+        if len(result_lines) == 2:
+            tokens = result_lines[1].split('\t')
+            ani = float(tokens[2])
+            af_r = float(tokens[3])
+            af_q = float(tokens[4])
+            ani_af = (qid, rid, ani, af_r, af_q)
+        else:
+        #     # genomes too divergent to determine ANI and AF
+        #     # with skani so default to zeros
+            ani_af = "null"
+        #     ani_af = (qid, rid, 0.0, 0.0, 0.0)
+
+
+        return ani_af
+
+
+    def _maybe_write_list(self, d_genomes, path):
+        """Writes a query/reference list to disk.
+
+        Parameters
+        ----------
+        d_genomes : dict[str, str]
+            A dictionary containing the key as the accession and value as path.
+        path : str
+            The path to write the file to.
+        """
+        if d_genomes is None or path is None:
+            return
+        with open(path, 'w') as fh:
+            for gid, gid_path in d_genomes.items():
+                fh.write(f'{gid_path}\n')
+
+    def _parse_result_queue(self, q_results):
+        """Creates the output dictionary given the results from skani
+
+        Parameters
+        ----------
+        q_results : mp.Queue
+            A multiprocessing queue containing raw results.
+
+        Returns
+        -------
+        dict[str, dict[str, dict[str, float]]]
+            The ANI/AF of the query genome to all reference genomes.
+        """
+        out = dict()
+        while True:
+            job = q_results.get(block=True)
+            if job == 'null':
+                continue
+            if job is None:
+                break
+
+            qid, rid, ani, af_r, af_q = job
+            max_af = max(af_r, af_q)
+            # af is a percent, we need to divide it by 100
+            max_af = max_af/100
+            if qid not in out:
+                out[qid] = {rid: {'ani': ani, 'af': max_af}}
+            else:
+                out[qid][rid] = {'ani': ani, 'af': max_af}
+
+        return out
diff --git a/gtdbtk/files/classify_summary.py b/gtdbtk/files/classify_summary.py
index 78c86a0a..d0d5fef7 100644
--- a/gtdbtk/files/classify_summary.py
+++ b/gtdbtk/files/classify_summary.py
@@ -27,8 +27,8 @@
 class ClassifySummaryFileRow:
     """A row contained within the ClassifySummaryFile object."""
 
-    __slots__ = ('gid', 'classification', 'fastani_ref', 'fastani_ref_radius',
-                 'fastani_tax', 'fastani_ani', 'fastani_af', 'closest_placement_ref',
+    __slots__ = ('gid', 'classification', 'closest_genome_ref', 'closest_genome_ref_radius',
+                 'closest_genome_tax', 'closest_genome_ani', 'closest_genome_af', 'closest_placement_ref',
                  'closest_placement_radius', 'closest_placement_tax', 'closest_placement_ani',
                  'closest_placement_af', 'pplacer_tax', 'classification_method',
                  'note', 'other_related_refs', 'msa_percent', 'tln_table',
@@ -38,11 +38,11 @@ def __init__(self):
         """Initialise the row, default all the values to None."""
         self.gid: Optional[str] = None
         self.classification: Optional[str] = None
-        self.fastani_ref: Optional[str] = None
-        self.fastani_ref_radius: Optional[float] = None
-        self.fastani_tax: Optional[str] = None
-        self.fastani_ani: Optional[float] = None
-        self.fastani_af: Optional[float] = None
+        self.closest_genome_ref: Optional[str] = None
+        self.closest_genome_ref_radius: Optional[float] = None
+        self.closest_genome_tax: Optional[str] = None
+        self.closest_genome_ani: Optional[float] = None
+        self.closest_genome_af: Optional[float] = None
         self.closest_placement_ref: Optional[str] = None
         self.closest_placement_radius: Optional[float] = None
         self.closest_placement_tax: Optional[str] = None
@@ -78,11 +78,11 @@ def get_col_order(row: ClassifySummaryFileRow = None) -> Tuple[List[str], List[U
             row = ClassifySummaryFileRow()
         mapping = [('user_genome', row.gid),
                    ('classification', row.classification),
-                   ('fastani_reference', row.fastani_ref),
-                   ('fastani_reference_radius', row.fastani_ref_radius),
-                   ('fastani_taxonomy', row.fastani_tax),
-                   ('fastani_ani', row.fastani_ani),
-                   ('fastani_af', row.fastani_af),
+                   ('closest_genome_reference', row.closest_genome_ref),
+                   ('closest_genome_reference_radius', row.closest_genome_ref_radius),
+                   ('closest_genome_taxonomy', row.closest_genome_tax),
+                   ('closest_genome_ani', row.closest_genome_ani),
+                   ('closest_genome_af', row.closest_genome_af),
                    ('closest_placement_reference', row.closest_placement_ref),
                    ('closest_placement_radius', row.closest_placement_radius),
                    ('closest_placement_taxonomy', row.closest_placement_tax),
@@ -107,6 +107,16 @@ def add_row(self, row: ClassifySummaryFileRow):
             raise GTDBTkExit(f'Attempting to add duplicate row: {row.gid}')
         self.rows[row.gid] = row
 
+    def get_row(self, gid: str) -> ClassifySummaryFileRow:
+        if gid not in self.rows:
+            raise GTDBTkExit(f'Attempting to get non-existent row: {gid}')
+        return self.rows[gid]
+
+    def update_row(self, row: ClassifySummaryFileRow):
+        if row.gid not in self.rows:
+            raise GTDBTkExit(f'Attempting to update non-existent row: {row.gid}')
+        self.rows[row.gid] = row
+
     def has_row(self) -> bool:
         if self.rows.items():
             return True
@@ -153,11 +163,11 @@ def read(self):
                 row = ClassifySummaryFileRow()
                 row.gid = data[0]
                 row.classification = data[1]
-                row.fastani_ref = data[2]
-                row.fastani_ref_radius = data[3]
-                row.fastani_tax = data[4]
-                row.fastani_ani = data[5]
-                row.fastani_af = data[6]
+                row.closest_genome_ref = data[2]
+                row.closest_genome_ref_radius = data[3]
+                row.closest_genome_tax = data[4]
+                row.closest_genome_ani = data[5]
+                row.closest_genome_af = data[6]
                 row.closest_placement_ref = data[7]
                 row.closest_placement_radius = data[8]
                 row.closest_placement_tax = data[9]
diff --git a/gtdbtk/files/stage_logger.py b/gtdbtk/files/stage_logger.py
index 6555d2fb..6cdb7272 100644
--- a/gtdbtk/files/stage_logger.py
+++ b/gtdbtk/files/stage_logger.py
@@ -29,6 +29,7 @@ class ANIScreenStep(Steps):
     mash_s: Optional[str]
     mash_v: Optional[str]
     mash_max_dist: Optional[str]
+    min_af: Optional[str]
     mash_db: Optional[str]
     output_files: Optional[Dict]
 
diff --git a/gtdbtk/main.py b/gtdbtk/main.py
index bf881dd1..fdc927d7 100644
--- a/gtdbtk/main.py
+++ b/gtdbtk/main.py
@@ -364,7 +364,7 @@ def align(self, options):
         make_sure_path_exists(options.out_dir)
 
         markers = Markers(options.cpus, options.debug)
-        reports = markers.align(options.identify_dir,
+        reports,all_failed_prodigal = markers.align(options.identify_dir,
                       options.skip_gtdb_refs,
                       options.taxa_filter,
                       options.min_perc_aa,
@@ -390,7 +390,7 @@ def align(self, options):
         self.stage_logger.steps.append(align_step)
 
         self.logger.info('Done.')
-
+        return all_failed_prodigal
 
     def infer(self, options):
         """Infer a tree from a user specified MSA.
@@ -536,7 +536,7 @@ def run_test(self, options):
         self.logger.info('Test has successfully finished.')
         return True
 
-    def classify(self, options,all_classified_ani=False):
+    def classify(self, options,all_classified_ani=False,all_failed_prodigal=False):
         """Determine taxonomic classification of genomes.
 
         Parameters
@@ -600,7 +600,8 @@ def classify(self, options,all_classified_ani=False):
                      mash_db=options.mash_db,
                      mash_max_dist=options.mash_max_distance,
                      ani_summary_files=ani_summary_files,
-                     all_classified_ani=all_classified_ani
+                     all_classified_ani=all_classified_ani,
+                     all_failed_prodigal=all_failed_prodigal
                      )
 
         classify_step.ends_at = datetime.now()
@@ -625,7 +626,7 @@ def classify(self, options,all_classified_ani=False):
         self.logger.info('Done.')
 
     def ani_screen(self, options ):
-        """Run a mash/FastANI screen of all user genomes
+        """Run a mash/skani screen of all user genomes
         against the reference genomes.
 
         Parameters
@@ -641,6 +642,7 @@ def ani_screen(self, options ):
         ani_step.mash_k = options.mash_k
         ani_step.mash_v = options.mash_v
         ani_step.mash_s = options.mash_s
+        ani_step.min_af = options.min_af
         ani_step.mash_max_dist = options.mash_max_distance
 
         if options.genome_dir:
@@ -653,18 +655,11 @@ def ani_screen(self, options ):
 
         make_sure_path_exists(options.out_dir)
 
-        genomes, tln_tables = self._genomes_to_process(options.genome_dir,
-                                                       options.batchfile,
-                                                       options.extension)
-        self.genomes_to_process = genomes
-
-        make_sure_path_exists(options.out_dir)
-
         genomes, _ = self._genomes_to_process(options.genome_dir,
                                               options.batchfile,
                                               options.extension)
 
-        aniscreener = ANIScreener(options.cpus)
+        aniscreener = ANIScreener(options.cpus,options.min_af)
         classified_genomes,reports = aniscreener.run_aniscreen(
             genomes=genomes,
             no_mash=options.no_mash,
@@ -1083,7 +1078,7 @@ def parse_options(self, options):
         elif options.subparser_name == 'classify_wf':
 
             check_dependencies(['prodigal', 'hmmalign', 'pplacer', 'guppy',
-                                'fastANI'])
+                                'skani'])
 
             if options.write_single_copy_genes and not options.keep_intermediates:
                 self.logger.warning('--write_single_copy_genes flag is set to True,'
@@ -1120,7 +1115,7 @@ def parse_options(self, options):
                         self.logger.warning('The ani_screen step has already been completed, we load existing results.')
                         ani_summary_files = previous_ani_step.output_files
                         classify_method = Classify()
-                        classified_genomes=classify_method.load_fastani_results_pre_pplacer(ani_summary_files)
+                        classified_genomes=classify_method.load_skani_results_pre_pplacer(ani_summary_files)
                         classified_genomes = classify_method.convert_rows_to_dict(classified_genomes)
                         len_mash_classified_bac120 = len(classified_genomes['bac120']) \
                             if 'bac120' in classified_genomes else 0
@@ -1161,10 +1156,11 @@ def parse_options(self, options):
             # if all genomes have been classified by the ani_screen step, we do not need to run the identify step
             options.identify_dir = options.out_dir
             options.align_dir = options.out_dir
+            all_failed_prodigal = False
             if all_classified_ani:
                 self.logger.info('All genomes have been classified by the ANI screening step, Identify and Align steps will be skipped.')
             else:
-                self.identify(options,classified_genomes)
+                self.identify(options, classified_genomes)
                 options.taxa_filter = None
                 options.custom_msa_filters = False
                 # Added here due to the other mutex argument being included above.
@@ -1177,13 +1173,13 @@ def parse_options(self, options):
                 options.rnd_seed = None
                 options.skip_trimming = False
 
-                self.align(options)
+                all_failed_prodigal = self.align(options)
 
             # because we run ani_screen before the identify step, we do not need to rerun the
             #ani step again, so we set the skip_aniscreen to True
             options.skip_ani_screen = True
 
-            self.classify(options,all_classified_ani= all_classified_ani)
+            self.classify(options,all_classified_ani= all_classified_ani,all_failed_prodigal=all_failed_prodigal)
             if not options.keep_intermediates:
                 self.remove_intermediate_files(options.out_dir,'classify_wf')
 
@@ -1223,7 +1219,7 @@ def parse_options(self, options):
             self.export_msa(options)
         elif options.subparser_name == 'test':
             check_dependencies(['prodigal', 'hmmalign', 'pplacer', 'guppy',
-                                'fastANI'])
+                                'skani'])
             self.run_test(options)
         elif options.subparser_name == 'check_install':
             self.check_install(options)
diff --git a/gtdbtk/markers.py b/gtdbtk/markers.py
index fd9ad6cd..5698db38 100644
--- a/gtdbtk/markers.py
+++ b/gtdbtk/markers.py
@@ -221,11 +221,17 @@ def identify(self, genomes, tln_tables, out_dir, prefix, force, genes, write_sin
                 make_sure_path_exists(symlink_protein_dir)
                 symlink_f(os.path.abspath(gpath), os.path.join(symlink_protein_dir,gid+self.protein_file_suffix))
 
+        gene_files = [(db_genome_id, genome_dictionary[db_genome_id]['aa_gene_path'])
+                      for db_genome_id in genome_dictionary.keys()]
+        # if gene_files is empty, we have no genomes to process
+        # we still need to write all the genomes failing the identify step to the bac120_summary file
+        if len(gene_files) == 0:
+            self.logger.warning('All genomes failed the identify step.')
+            return reports
+
         # annotated genes against TIGRFAM and Pfam databases
         self.logger.log(CONFIG.LOG_TASK,
                         'Identifying TIGRFAM protein families.')
-        gene_files = [(db_genome_id, genome_dictionary[db_genome_id]['aa_gene_path'])
-                      for db_genome_id in genome_dictionary.keys()]
         tigr_search = TigrfamSearch(self.cpus,
                                     self.tigrfam_hmms,
                                     self.protein_file_suffix,
@@ -481,6 +487,15 @@ def align(self,
         else:
             failed_genomes = list()
 
+        if failed_genomes and not os.path.isfile(TlnTableSummaryFile(identify_dir, prefix).path):
+            self.logger.warning(
+                f'Skipping align step.')
+            if identify_dir != out_dir and os.path.isfile(failed_genomes_file):
+                identify_path = os.path.join(out_dir, DIR_IDENTIFY)
+                make_sure_path_exists(identify_path)
+                copy(failed_genomes_file, identify_path)
+            return reports,True
+
         # If the user is re-running this step, check if the identify step is consistent.
         genomic_files = self._path_to_identify_data(
             identify_dir, identify_dir != out_dir)
@@ -700,7 +715,7 @@ def align(self,
                     fout.write(f'{no_marker_gid}\tNo bacterial or archaeal marker\n')
             reports.setdefault('all', []).append(no_marker_filtered_genomes)
 
-        return reports
+        return reports,False
 
     def _write_individual_markers(self, user_msa, marker_set_id, marker_list, out_dir, prefix):
         marker_dir = join(out_dir, DIR_ALIGN_MARKERS)
diff --git a/gtdbtk/misc.py b/gtdbtk/misc.py
index 925ea0e4..796561f0 100644
--- a/gtdbtk/misc.py
+++ b/gtdbtk/misc.py
@@ -248,7 +248,7 @@ def check_install(self,db_version):
 
         # Check that all programs are on the system path.
         self.logger.info(f'Checking that all third-party software are on the system path:')
-        names = {'prodigal', 'hmmsearch', 'fastANI', 'mash', 'pplacer', 'guppy',
+        names = {'prodigal', 'hmmsearch', 'skani', 'mash', 'pplacer', 'guppy',
                  'FastTree', 'FastTreeMP', 'hmmalign'}
         for name in sorted(names):
             on_path = False
diff --git a/gtdbtk/reroot_tree.py b/gtdbtk/reroot_tree.py
index cd8a30ad..89755a3d 100644
--- a/gtdbtk/reroot_tree.py
+++ b/gtdbtk/reroot_tree.py
@@ -80,7 +80,7 @@ def root_with_outgroup(self, input_tree: str, output_tree: str, outgroup: Set[st
         mrca = tree.mrca(taxa=outgroup_in_tree)
         mrca_leaves = len(mrca.leaf_nodes())
         while True:
-            rnd_ingroup = random.sample(ingroup_leaves, 1)[0]
+            rnd_ingroup = random.sample(tuple(ingroup_leaves), 1)[0]
             tree.reroot_at_edge(rnd_ingroup.edge,
                                 length1=0.5 * rnd_ingroup.edge_length,
                                 length2=0.5 * rnd_ingroup.edge_length)
diff --git a/gtdbtk/tools.py b/gtdbtk/tools.py
index 77f33959..7da669b0 100644
--- a/gtdbtk/tools.py
+++ b/gtdbtk/tools.py
@@ -53,12 +53,12 @@ def get_ref_genomes():
         Dict[genome_id] = fasta_path
     """
     ref_genomes = dict()
-    with open(CONFIG.FASTANI_GENOME_LIST) as g_path_file:
+    with open(CONFIG.SKANI_GENOME_LIST) as g_path_file:
         for line in g_path_file:
             (full_name, path) = line.strip().split()
-            if full_name.endswith(CONFIG.FASTANI_GENOMES_EXT):
-                accession = full_name.split(CONFIG.FASTANI_GENOMES_EXT)[0]
-            ref_genomes[accession] = os.path.join(CONFIG.FASTANI_DIR, path, full_name)
+            if full_name.endswith(CONFIG.SKANI_GENOMES_EXT):
+                accession = full_name.split(CONFIG.SKANI_GENOMES_EXT)[0]
+            ref_genomes[accession] = os.path.join(CONFIG.SKANI_DIR, path, full_name)
     return ref_genomes
 
 def aa_percent_msa(aa_string):
diff --git a/gtdbtk/trim_msa.py b/gtdbtk/trim_msa.py
index 0b8eba68..ec93fe35 100755
--- a/gtdbtk/trim_msa.py
+++ b/gtdbtk/trim_msa.py
@@ -241,7 +241,7 @@ def subsample_msa(self, seqs, markers):
                 lack_cols_marker_ids.append(marker_id)
 
             offset_valid_cols = [i + start for i in valid_cols]
-            sel_cols = random.sample(offset_valid_cols, min(
+            sel_cols = random.sample(tuple(offset_valid_cols), min(
                 self.subset, len(offset_valid_cols)))
             sampled_cols.extend(sel_cols)
 
diff --git a/scripts/rename_UBAs/prepare_gtdbtk_package.py b/scripts/rename_UBAs/prepare_gtdbtk_package.py
index 8c7a2ed3..a6281d41 100644
--- a/scripts/rename_UBAs/prepare_gtdbtk_package.py
+++ b/scripts/rename_UBAs/prepare_gtdbtk_package.py
@@ -53,8 +53,8 @@ def __init__(self):
     def parse_options(self, options):
         """Parse user options and call the correct pipeline(s)"""
 
-        if options.subparser_name == 'convert_fastani_genomes':
-            self.convert_fastani_genomes(options.dirin, options.dirout)
+        if options.subparser_name == 'convert_skani_genomes':
+            self.convert_skani_genomes(options.dirin, options.dirout)
         elif options.subparser_name == 'unroot_tree':
             self.unroot(options.input_tree, options.output_tree)
         else:
@@ -64,7 +64,7 @@ def parse_options(self, options):
 
         return 0
 
-    def convert_fastani_genomes(self, dirin, dirout):
+    def convert_skani_genomes(self, dirin, dirout):
 
         # get mapping from published UBA genomes to NCBI accessions
         __location__ = os.path.realpath(os.path.join(
@@ -80,11 +80,11 @@ def convert_fastani_genomes(self, dirin, dirout):
                 else:
                     uba_acc[line_split[0]] = {"uba": line_split[1]}
 
-        fastanis = glob.glob(os.path.join(dirin, "*"))
-        fastani_dir = os.path.join(dirout, 'fastani')
-        if not os.path.exists(fastani_dir):
-            os.makedirs(fastani_dir)
-        for genome in fastanis:
+        skanis = glob.glob(os.path.join(dirin, "*"))
+        skani_dir = os.path.join(dirout, 'skani')
+        if not os.path.exists(skani_dir):
+            os.makedirs(skani_dir)
+        for genome in skanis:
             filenamef = os.path.basename(genome)
             filenamef = filenamef.replace("_genomic.fna", "")
             if filenamef.startswith("U_"):
@@ -92,13 +92,13 @@ def convert_fastani_genomes(self, dirin, dirout):
                 subdict = uba_acc.get(filenamef)
                 if "gca" in subdict.keys():
                     copyfile(genome, os.path.join(
-                        fastani_dir, subdict.get("gca")[3:] + "_genomic.fna"))
+                        skani_dir, subdict.get("gca")[3:] + "_genomic.fna"))
                 else:
                     copyfile(genome, os.path.join(
-                        fastani_dir, subdict.get("uba") + "_genomic.fna"))
+                        skani_dir, subdict.get("uba") + "_genomic.fna"))
             else:
                 copyfile(genome, os.path.join(
-                    fastani_dir, filenamef + "_genomic.fna"))
+                    skani_dir, filenamef + "_genomic.fna"))
 
     def unroot(self, rooted_tree, unrooted_tree):
         # get taxonomic classification of each user genome
@@ -114,7 +114,7 @@ def unroot(self, rooted_tree, unrooted_tree):
         return True
 
     def run(self, dirin, dirout, gtr, release):
-        """ renaming genome files for fastani"""
+        """ renaming genome files for skani"""
 
         # get list of genomes to retain (based on genome list 1014)
         genomes_to_retain = set()
@@ -158,12 +158,12 @@ def run(self, dirin, dirout, gtr, release):
                         taxout.write(line)
         taxout.close()
 
-        # renaming genome files for fastani
-        fastanis = glob.glob(os.path.join(dirin, 'fastani', "*"))
-        fastani_dir = os.path.join(dirout, 'fastani')
-        if not os.path.exists(fastani_dir):
-            os.makedirs(fastani_dir)
-        for genome in fastanis:
+        # renaming genome files for skani
+        skanis = glob.glob(os.path.join(dirin, 'skani', "*"))
+        skani_dir = os.path.join(dirout, 'skani')
+        if not os.path.exists(skani_dir):
+            os.makedirs(skani_dir)
+        for genome in skanis:
             filenamef = os.path.basename(genome)
             filenamef = filenamef.replace("_genomic.fna", "")
             if filenamef.startswith("U_"):
@@ -171,16 +171,16 @@ def run(self, dirin, dirout, gtr, release):
                 if filenamef == "U_74684":
                     print(subdict)
                     print(genome)
-                    print(os.path.join(fastani_dir, subdict.get("gca")[3:] + "_genomic.fna"))
+                    print(os.path.join(skani_dir, subdict.get("gca")[3:] + "_genomic.fna"))
                 if "gca" in subdict.keys():
                     copyfile(genome, os.path.join(
-                        fastani_dir, subdict.get("gca")[3:] + "_genomic.fna"))
+                        skani_dir, subdict.get("gca")[3:] + "_genomic.fna"))
                 else:
                     copyfile(genome, os.path.join(
-                        fastani_dir, subdict.get("uba") + "_genomic.fna"))
+                        skani_dir, subdict.get("uba") + "_genomic.fna"))
             else:
                 copyfile(genome, os.path.join(
-                    fastani_dir, filenamef + "_genomic.fna"))
+                    skani_dir, filenamef + "_genomic.fna"))
 
         for dom in ['bac120', 'ar53']:
             # MSA renaming
@@ -291,7 +291,7 @@ def print_help():
       blah
 
     External files for GTDB-Tk package:
-      convert_fastani_genomes  -> Rename genomic.fna files to UBAs or GCAs.
+      convert_skani_genomes  -> Rename genomic.fna files to UBAs or GCAs.
       unroot_tree              -> Unroot tree.
 
     ''')
@@ -306,14 +306,14 @@ def print_help():
     subparsers = parser.add_subparsers(help="--", dest='subparser_name')
 
     # generate convert genomic file for GTDB website
-    fastani_convert_parser = subparsers.add_parser('convert_fastani_genomes',
+    skani_convert_parser = subparsers.add_parser('convert_skani_genomes',
                                                    formatter_class=CustomHelpFormatter,
                                                    description='rename Genomic.fna files to UBAs or GCAs.')
-    fastani_convert_parser.add_argument('--input', dest="dirin",
+    skani_convert_parser.add_argument('--input', dest="dirin",
                                         required=True, help='path to taxonomy file.')
-    fastani_convert_parser.add_argument('--output', dest="dirout",
+    skani_convert_parser.add_argument('--output', dest="dirout",
                                         required=True, help='path to processed output file')
-    fastani_convert_parser.add_argument(
+    skani_convert_parser.add_argument(
         '--silent', help="suppress output", action='store_true')
 
     # generate convert genomic file for GTDB website
diff --git a/scripts/verify_official_package.py b/scripts/verify_official_package.py
index 6a2ad421..bda62a47 100644
--- a/scripts/verify_official_package.py
+++ b/scripts/verify_official_package.py
@@ -46,7 +46,7 @@ def __init__(self, pack_dir):
         """Initialization."""
         self.pack_dir = pack_dir
         self.list_dirsinpackage = [
-            'fastani', 'markers', 'masks', 'metadata', 'msa', 'pplacer', 'radii', 'taxonomy']
+            'skani', 'markers', 'masks', 'metadata', 'msa', 'pplacer', 'radii', 'taxonomy']
 
     def run(self, outf):
 
@@ -62,9 +62,9 @@ def run(self, outf):
                 if line.startswith('VERSION_DATA'):
                     version = line.strip().split('=')[1]
 
-        # List genomes in fastani folder
+        # List genomes in skani folder
         list_genomes = [os.path.basename(x) for x in glob.glob(os.path.join(
-            self.pack_dir, 'fastani', 'database/*.gz'))]
+            self.pack_dir, 'skani', 'database/*.gz'))]
         list_genomes = [x.replace('_genomic.fna.gz', '').replace('GCA_', 'GB_GCA_').replace('GCF_', 'RS_GCF_') for x in
                         list_genomes]
 
@@ -206,7 +206,7 @@ def run(self, outf):
         print('Length trimmed bac120 MSA: {}'.format(len(bac_pplacer_msa.get(list(bac_pplacer_msa.keys())[0]))))
         print('Length trimmed ar53 MSA: {}'.format(len(ar_pplacer_msa.get(list(ar_pplacer_msa.keys())[0]))))
         print('')
-        print('Number of genomes in fastani/database: {}'.format(len(list_genomes)))
+        print('Number of genomes in skani/database: {}'.format(len(list_genomes)))
         print('Number of genomes in radii file: {}'.format(len(radii_dict)))
         print('Number of genomes in taxonomy file: {}'.format(len(tax_dict)))
 
diff --git a/setup.py b/setup.py
index 7a34af65..55ac0542 100644
--- a/setup.py
+++ b/setup.py
@@ -45,7 +45,7 @@ def readme():
             'gtdbtk = gtdbtk.__main__:main'
         ]
     },
-    install_requires=["dendropy>=4.1.0", 'numpy>=1.9.0', 'tqdm>=4.35.0', 'pydantic>=1.9.2,<2.0a1'],
+    install_requires=["dendropy ~= 4.1", 'numpy ~= 1.9', 'tqdm ~= 4.35', 'pydantic ~= 1.9'],
     license=meta['license'],
     long_description=readme(),
     long_description_content_type='text/markdown',


Test patch:
diff --git a/tests/test_gtdbtk/test_external/test_fasttree.py b/tests/test_gtdbtk/test_external/test_fasttree.py
index d4536c09..8dc01726 100644
--- a/tests/test_gtdbtk/test_external/test_fasttree.py
+++ b/tests/test_gtdbtk/test_external/test_fasttree.py
@@ -28,7 +28,7 @@ class TestFastTree(unittest.TestCase):
     def setUp(self):
         self.dir_tmp = tempfile.mkdtemp(prefix='gtdbtk_tmp_')
         self.cpus = 1
-        self.genome_root = CONFIG.FASTANI_GENOMES
+        self.genome_root = CONFIG.SKANI_GENOMES
         self.test_msa = 'tests/data/example.faa'
         self.test_msa_gz = 'tests/data/example.faa.gz'
 
diff --git a/tests/test_gtdbtk/test_external/test_skani.py b/tests/test_gtdbtk/test_external/test_skani.py
new file mode 100644
index 00000000..119a972e
--- /dev/null
+++ b/tests/test_gtdbtk/test_external/test_skani.py
@@ -0,0 +1,65 @@
+###############################################################################
+#                                                                             #
+#    This program is free software: you can redistribute it and/or modify     #
+#    it under the terms of the GNU General Public License as published by     #
+#    the Free Software Foundation, either version 3 of the License, or        #
+#    (at your option) any later version.                                      #
+#                                                                             #
+#    This program is distributed in the hope that it will be useful,          #
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
+#    GNU General Public License for more details.                             #
+#                                                                             #
+#    You should have received a copy of the GNU General Public License        #
+#    along with this program. If not, see <http://www.gnu.org/licenses/>.     #
+#                                                                             #
+###############################################################################
+import os
+import shutil
+import tempfile
+import unittest
+import json
+from gtdbtk.config.common import CONFIG
+from gtdbtk.external.skani import SkANI
+
+
+class TestSkANI(unittest.TestCase):
+
+    def setUp(self):
+        self.dir_tmp = tempfile.mkdtemp(prefix='gtdbtk_tmp_')
+        self.cpus = 1
+        self.genome_root = CONFIG.SKANI_GENOMES
+
+    def tearDown(self):
+        shutil.rmtree(self.dir_tmp)
+
+    def test_run(self):
+        """Test that skani produces the expected output (version dependent)"""
+
+        fa = SkANI(self.cpus, force_single=True)
+
+        """
+        a = d__Archaea; p__Altiarchaeota; c__Altiarchaeia; o__Altiarchaeales; f__Altiarchaeaceae; g__Altiarchaeum; s__Altiarchaeum sp001873845
+        b = d__Archaea; p__Altiarchaeota; c__Altiarchaeia; o__Altiarchaeales; f__Altiarchaeaceae; g__Altiarchaeum; s__Altiarchaeum sp002083985
+        c = d__Bacteria; p__Aquificota; c__Desulfurobacteriia; o__Desulfurobacteriales; f__Desulfurobacteriaceae; g__Thermovibrio; s__Thermovibrio ammonificans
+        x = d__Archaea; p__Altiarchaeota; c__Altiarchaeia; o__GCA-002841105; f__GCA-002841105; g__GCA-002841105; s__GCA-002841105 sp002841105
+        y = d__Bacteria; p__Aerophobota; c__Aerophobia; o__Aerophobales; f__Aerophobaceae; g__Aerophobus; s__Aerophobus profundus
+        z = d__Archaea; p__Halobacterota; c__Archaeoglobi; o__JdFR-21; f__JdFR-21; g__JdFR-21; s__JdFR-21 sp002011165
+        """
+        d_compare = {'a': {'x', 'y'},
+                     'b': {'x'},
+                     'c': {'z'}}
+        d_paths = {'a': os.path.join(self.genome_root,'GCA/001/873/845', 'GCA_001873845.1_genomic.fna.gz'),
+                   'b': os.path.join(self.genome_root,'GCA/002/083/985', 'GCA_002083985.1_genomic.fna.gz'),
+                   'c': os.path.join(self.genome_root,'GCF/000/185/805', 'GCF_000185805.1_genomic.fna.gz'),
+                   'x': os.path.join(self.genome_root,'GCA/002/841/105', 'GCA_002841105.1_genomic.fna.gz'),
+                   'y': os.path.join(self.genome_root,'GCA/000/402/295', 'GCA_000402295.1_genomic.fna.gz'),
+                   'z': os.path.join(self.genome_root,'GCA/002/011/165', 'GCA_002011165.1_genomic.fna.gz')}
+
+        result = fa.run(d_compare, d_paths)
+
+        expected = {'a': {'x': {'ani': 82.5201, 'af': 0.57},
+                          'y': {'ani': 74.5154, 'af': 0.0}},
+                    'b': {'x': {'ani': 84.5846, 'af': 0.55}},
+                    'c': {'z': {'ani': 74.4978, 'af': 0.01}}}
+        self.assertEqual(json.dumps(result, sort_keys=True), json.dumps(expected, sort_keys=True))