Repository: Changwanseo/FunVIP (language: python)

Issue:
README Changes pull request



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 3ccd082..2ca2d52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,8 @@
 /Result/*/*
 !/Result/Clean.py
 /funid/src/*.nwk
+/funip/src/*.nwk
+/funvip/src/*.nwk
 /*.nhr
 /*.nin
 /*.nsq
@@ -13,7 +15,15 @@
 !/funid/external/mmseqs_Windows/bin/*
 !/funid/external/mmseqs_Windows/bin/*.exe
 !/funid/external/mmseqs_Windows/bin/*.dll
+!/funip/external/mmseqs_Windows/bin/*
+!/funip/external/mmseqs_Windows/bin/*.exe
+!/funip/external/mmseqs_Windows/bin/*.dll
+!/funvip/external/mmseqs_Windows/bin/*
+!/funvip/external/mmseqs_Windows/bin/*.exe
+!/funvip/external/mmseqs_Windows/bin/*.dll
 /funid/__pycache__/*.pyc
+/funip/__pycache__/*.pyc
+/funvip/__pycache__/*.pyc
 /DB/*.xlsx
 /sftp-config.json
 /tmp.hash
@@ -35,12 +45,16 @@
 /DB/mmseqs/*/*
 !/DB/mmseqs/*/*.gitignore
 /FunID-win32-x64/*
+/FunIP-win32-x64/*
+/FunVIP-win32-x64/*
 /node_modules/*
 /package.json
 /package-lock.json
 /build/*
 /dist/*
 /FunID.egg-info/*
+/FunIP.egg-info/*
+/FunVIP.egg-info/*
 /Amanita/*
 /test/*
 /amanita_*/*
diff --git a/README.md b/README.md
index 90b038c..07bec42 100644
--- a/README.md
+++ b/README.md
@@ -15,8 +15,8 @@
 
 
 
-# FunIP
-"Fun"gal "I"dentification "P"ipeline
+# FunVIP
+"Fun"gal "V"alidation & "I"dentification "P"ipeline
 
 A automatic tree-based sequence identification pipeline for fungal species
 
diff --git a/build_manual.md b/build_manual.md
index 1587424..74dcb3a 100644
--- a/build_manual.md
+++ b/build_manual.md
@@ -6,29 +6,28 @@ pip install ./
 # pypi build
 In new conda environment
 ```
-conda create -n FunID-build python=3.10
-conda activate FunID-build
-pip install twine
-python setup.py bdist_wheel --universal
-python setup.py sdist
-twine upload dist/FunID-{YOUR_VERSION}* 	// use current build number
+conda create -n FunVIP-build python=3.10
+conda activate FunVIP-build
+pip install twine setuptools-ext build
+python -m build
+twine upload dist/FunVIP-{YOUR_VERSION}* --config-file {your pypirc file} 	// use current build number
 conda deactivate
-conda env remove -n FunID-build
+conda env remove -n FunVIP-build
 ```
 
 
 # conda build
 In new conda environment
 ```
-conda create -n FunID-condabuild python=3.9
-conda activate FunID-condabuild
+conda create -n FunVIP-condabuild python=3.9
+conda activate FunVIP-condabuild
 conda install --yes -c conda-forge grayskull packaging
 conda install --yes conda-build conda-verify anaconda-client git urllib3=1.26.15
-grayskull pypi FunID // Check if version is as you expected during this step
+grayskull pypi FunVIP // Check if version is as you expected during this step
 anaconda login
 conda config --set anaconda_upload no
 conda config --add channels funid
-conda-build ./funid -c conda-forge // copy tar.bz2 location when this one ends
+conda-build ./funvip -c conda-forge // copy tar.bz2 location when this one ends
 conda install anaconda-project --yes
 anaconda upload {Build file location} // tar.bz2 in conda-build log
 conda deactivate
diff --git a/funid/FunID.egg-info/PKG-INFO b/funid/FunID.egg-info/PKG-INFO
deleted file mode 100644
index 723c98b..0000000
--- a/funid/FunID.egg-info/PKG-INFO
+++ /dev/null
@@ -1,13 +0,0 @@
-Metadata-Version: 2.1
-Name: FunID
-Version: 0.2.0.0.0.0
-Summary: Fungal Identification Pipeline
-Home-page: https://github.com/Changwanseo/FunID-dev
-Author: Changwan Seo
-Author-email: wan101010@snu.ac.kr
-License: GPL3
-Platform: UNKNOWN
-Requires-Python: >= 3.11
-
-UNKNOWN
-
diff --git a/funid/FunID.egg-info/SOURCES.txt b/funid/FunID.egg-info/SOURCES.txt
deleted file mode 100644
index 2c9a4be..0000000
--- a/funid/FunID.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-README.md
-setup.py
-funid/FunID.egg-info/PKG-INFO
-funid/FunID.egg-info/SOURCES.txt
-funid/FunID.egg-info/dependency_links.txt
-funid/FunID.egg-info/entry_points.txt
-funid/FunID.egg-info/not-zip-safe
-funid/FunID.egg-info/requires.txt
-funid/FunID.egg-info/top_level.txt
\ No newline at end of file
diff --git a/funid/FunID.egg-info/entry_points.txt b/funid/FunID.egg-info/entry_points.txt
deleted file mode 100644
index 86dcfd8..0000000
--- a/funid/FunID.egg-info/entry_points.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-[console_scripts]
-FunID = funid.main:main
-
diff --git a/funid/FunID.egg-info/not-zip-safe b/funid/FunID.egg-info/not-zip-safe
deleted file mode 100644
index 8b13789..0000000
--- a/funid/FunID.egg-info/not-zip-safe
+++ /dev/null
@@ -1,1 +0,0 @@
-
diff --git a/funid/FunID.egg-info/requires.txt b/funid/FunID.egg-info/requires.txt
deleted file mode 100644
index be72b35..0000000
--- a/funid/FunID.egg-info/requires.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-biopython
-ete3
-GenMine
-lxml
-matplotlib
-numpy
-openpyxl
-pandas
-plotly
-scikit-learn
-streamlit
-unidecode
-xlrd
-xmltodict
diff --git a/funid/FunID.egg-info/top_level.txt b/funid/FunID.egg-info/top_level.txt
deleted file mode 100644
index c4d43a2..0000000
--- a/funid/FunID.egg-info/top_level.txt
+++ /dev/null
@@ -1,1 +0,0 @@
-funid
diff --git a/funid/FunID.yaml b/funid/FunID.yaml
deleted file mode 100644
index 2c075b6..0000000
--- a/funid/FunID.yaml
+++ /dev/null
@@ -1,302 +0,0 @@
-name: FunID
-channels:
-  - conda-forge
-  - defaults
-  - bioconda
-  - default
-dependencies:
-  - _libgcc_mutex=0.1=conda_forge
-  - _openmp_mutex=4.5=2_kmp_llvm
-  - abseil-cpp=20211102.0=hd4dd3e8_0
-  - altair=4.1.0=py_1
-  - argon2-cffi=21.3.0=pyhd3eb1b0_0
-  - argon2-cffi-bindings=21.2.0=py39h7f8727e_0
-  - arrow-cpp=4.0.1=py39hcd1404d_38_cpu
-  - asttokens=2.0.5=pyhd3eb1b0_0
-  - attrs=21.4.0=pyhd3eb1b0_0
-  - aws-c-cal=0.5.11=h95a6274_0
-  - aws-c-common=0.6.2=h27cfd23_0
-  - aws-c-event-stream=0.2.7=h3541f99_13
-  - aws-c-io=0.10.5=hfb6a706_0
-  - aws-checksums=0.1.11=ha31a3da_7
-  - aws-sdk-cpp=1.8.186=hb4091e7_3
-  - backcall=0.2.0=pyhd3eb1b0_0
-  - backoff=1.11.1=pyhd8ed1ab_0
-  - beautifulsoup4=4.11.1=py39h06a4308_0
-  - binutils_impl_linux-64=2.36.1=h193b22a_2
-  - biopython=1.78=py39h7f8727e_0
-  - blas=1.0=mkl
-  - blast=2.12.0=hf3cf87c_4
-  - bleach=4.1.0=pyhd3eb1b0_0
-  - blinker=1.4=py39h06a4308_0
-  - boltons=21.0.0=pyhd8ed1ab_0
-  - boost-cpp=1.70.0=ha2d47e9_1
-  - bottleneck=1.3.4=py39hce1f21e_0
-  - brotli=1.0.9=he6710b0_2
-  - brotlipy=0.7.0=py39h27cfd23_1003
-  - bzip2=1.0.8=h7b6447c_0
-  - c-ares=1.18.1=h7f8727e_0
-  - ca-certificates=2022.07.19=h06a4308_0
-  - cachetools=4.2.2=pyhd3eb1b0_0
-  - certifi=2022.6.15=py39h06a4308_0
-  - cffi=1.15.0=py39hd667e15_1
-  - chardet=4.0.0=py39hf3d152e_3
-  - charset-normalizer=2.0.4=pyhd3eb1b0_0
-  - click=8.0.4=py39h06a4308_0
-  - click-spinner=0.1.10=pyh9f0ad1d_0
-  - colorama=0.4.4=pyhd3eb1b0_0
-  - colorlog=6.6.0=py39hf3d152e_1
-  - commonmark=0.9.1=pyhd3eb1b0_0
-  - cryptography=37.0.1=py39h9ce1e76_0
-  - curl=7.82.0=h7f8727e_0
-  - cycler=0.11.0=pyhd3eb1b0_0
-  - dacite=1.6.0=pyhd3deb0d_0
-  - dataclasses=0.8=pyh6d0b6a4_7
-  - datapane=0.14.0=pyhd8ed1ab_0
-  - dbus=1.13.18=hb2f20db_0
-  - debugpy=1.5.1=py39h295c915_0
-  - decorator=5.1.1=pyhd3eb1b0_0
-  - defusedxml=0.7.1=pyhd3eb1b0_0
-  - dominate=2.6.0=pyhd8ed1ab_0
-  - entrez-direct=16.2=he881be0_0
-  - entrypoints=0.4=py39h06a4308_0
-  - et_xmlfile=1.1.0=py39h06a4308_0
-  - ete3=3.1.2=pyh9f0ad1d_0
-  - executing=0.8.3=pyhd3eb1b0_0
-  - expat=2.4.4=h295c915_0
-  - face=20.1.1=py_0
-  - fontconfig=2.13.96=ha180cfb_0
-  - fonttools=4.25.0=pyhd3eb1b0_0
-  - freetype=2.11.0=h70c0345_0
-  - furl=2.1.3=pyhd8ed1ab_0
-  - future=0.18.2=py39h06a4308_1
-  - gawk=5.1.0=h7b6447c_0
-  - gblocks=0.91b=h9ee0642_2
-  - gcc=12.1.0=h9ea6d83_10
-  - gcc_impl_linux-64=12.1.0=hea43390_16
-  - gflags=2.2.2=he6710b0_0
-  - giflib=5.2.1=h7b6447c_0
-  - gitdb=4.0.7=pyhd3eb1b0_0
-  - gitpython=3.1.18=pyhd3eb1b0_1
-  - glib=2.69.1=h4ff587b_1
-  - glog=0.5.0=h2531618_0
-  - glom=20.11.0=pyhd3deb0d_0
-  - grpc-cpp=1.45.2=he70e3f0_3
-  - gst-plugins-base=1.14.0=h8213a91_2
-  - gstreamer=1.14.0=h28cd5cc_2
-  - icu=58.2=he6710b0_3
-  - idna=3.3=pyhd3eb1b0_0
-  - importlib-metadata=4.11.3=py39h06a4308_0
-  - importlib_resources=5.7.1=pyhd8ed1ab_1
-  - intel-openmp=2021.4.0=h06a4308_3561
-  - ipykernel=6.9.1=py39h06a4308_0
-  - ipython=8.3.0=py39h06a4308_0
-  - ipython_genutils=0.2.0=pyhd3eb1b0_1
-  - ipywidgets=7.6.5=pyhd3eb1b0_1
-  - iqtree=2.2.0.3=hb97b32f_0
-  - jedi=0.18.1=py39h06a4308_1
-  - jinja2=3.0.3=pyhd3eb1b0_0
-  - joblib=1.1.0=pyhd3eb1b0_0
-  - jpeg=9e=h7f8727e_0
-  - jsonschema=4.4.0=py39h06a4308_0
-  - jupyter_client=7.2.2=py39h06a4308_0
-  - jupyter_core=4.10.0=py39h06a4308_0
-  - jupyterlab_pygments=0.1.2=py_0
-  - jupyterlab_widgets=1.0.0=pyhd3eb1b0_1
-  - kernel-headers_linux-64=2.6.32=he073ed8_15
-  - kiwisolver=1.4.2=py39h295c915_0
-  - krb5=1.19.2=hac12032_0
-  - lcms2=2.12=h3be6417_0
-  - ld_impl_linux-64=2.36.1=hea4e1c9_2
-  - libbrotlicommon=1.0.9=h166bdaf_7
-  - libbrotlidec=1.0.9=h166bdaf_7
-  - libbrotlienc=1.0.9=h166bdaf_7
-  - libcurl=7.82.0=h0b77cf5_0
-  - libedit=3.1.20210910=h7f8727e_0
-  - libev=4.33=h7f8727e_1
-  - libevent=2.1.10=h9b69904_4
-  - libffi=3.3=he6710b0_2
-  - libgcc-devel_linux-64=12.1.0=h1ec3361_16
-  - libgcc-ng=12.1.0=h8d9b700_16
-  - libgfortran-ng=7.5.0=ha8ba4b0_17
-  - libgfortran4=7.5.0=ha8ba4b0_17
-  - libgomp=12.1.0=h8d9b700_16
-  - libidn2=2.3.2=h7f8727e_0
-  - libnghttp2=1.46.0=hce63b2e_0
-  - libnsl=2.0.0=h7f98852_0
-  - libpng=1.6.37=hbc83047_0
-  - libprotobuf=3.20.1=h4ff587b_0
-  - libsanitizer=12.1.0=ha89aaad_16
-  - libsodium=1.0.18=h7b6447c_0
-  - libssh2=1.10.0=h8f2d780_0
-  - libstdcxx-ng=12.1.0=ha89aaad_16
-  - libthrift=0.16.0=h519c5ea_1
-  - libtiff=4.2.0=h2818925_1
-  - libunistring=0.9.10=h27cfd23_0
-  - libutf8proc=2.7.0=h7f98852_0
-  - libuuid=2.32.1=h7f98852_1000
-  - libwebp=1.2.2=h55f646e_0
-  - libwebp-base=1.2.2=h7f8727e_0
-  - libxcb=1.15=h7f8727e_0
-  - libxml2=2.9.14=h74e7548_0
-  - libxslt=1.1.35=h4e12654_0
-  - libzlib=1.2.12=h166bdaf_0
-  - llvm-openmp=14.0.4=he0ac6c6_0
-  - lxml=4.8.0=py39h1f438cf_0
-  - lz4-c=1.9.3=h295c915_1
-  - mafft=7.505=hec16e2b_0
-  - markupsafe=2.1.1=py39h7f8727e_0
-  - matplotlib=3.5.1=py39h06a4308_1
-  - matplotlib-base=3.5.1=py39ha18d171_1
-  - matplotlib-inline=0.1.2=pyhd3eb1b0_2
-  - micawber=0.5.4=pyhd8ed1ab_0
-  - mistune=0.8.4=py39h27cfd23_1000
-  - mkl=2021.4.0=h06a4308_640
-  - mkl-service=2.4.0=py39h7f8727e_0
-  - mkl_fft=1.3.1=py39hd3c417c_0
-  - mkl_random=1.2.2=py39h51133e4_0
-  - mmseqs2=13.45111=pl5321hf1761c0_2
-  - modeltest-ng=0.1.7=h5c6ebe3_0
-  - monotonic=1.5=py_0
-  - mpi=1.0=openmpi
-  - munch=2.5.0=py_0
-  - munkres=1.0.7=py_1
-  - nbclient=0.5.13=py39h06a4308_0
-  - nbconvert=6.4.4=py39h06a4308_0
-  - nbformat=5.3.0=py39h06a4308_0
-  - ncurses=6.3=h7f8727e_2
-  - nest-asyncio=1.5.5=py39h06a4308_0
-  - notebook=6.4.11=py39h06a4308_0
-  - numexpr=2.8.1=py39h6abb31d_0
-  - numpy=1.22.3=py39he7a7128_0
-  - numpy-base=1.22.3=py39hf524024_0
-  - openmpi=4.1.3=h8b79891_4
-  - openpyxl=3.0.9=pyhd3eb1b0_0
-  - openssl=1.1.1q=h7f8727e_0
-  - orc=1.7.4=h07ed6aa_0
-  - orderedmultidict=1.0.1=py_0
-  - packaging=21.3=pyhd3eb1b0_0
-  - pandas=1.4.2=py39h295c915_0
-  - pandocfilters=1.5.0=pyhd3eb1b0_0
-  - parso=0.8.3=pyhd3eb1b0_0
-  - pcre=8.45=h295c915_0
-  - perl=5.32.1=2_h7f98852_perl5
-  - perl-archive-tar=2.40=pl5321hdfd78af_0
-  - perl-common-sense=3.75=pl5321hdfd78af_0
-  - perl-compress-raw-bzip2=2.103=pl5321h87f3376_0
-  - perl-compress-raw-zlib=2.101=pl5321h87f3376_2
-  - perl-encode=3.17=pl5321hec16e2b_0
-  - perl-exporter=5.72=pl5321hdfd78af_2
-  - perl-exporter-tiny=1.002002=pl5321hdfd78af_0
-  - perl-io-compress=2.102=pl5321h87f3376_1
-  - perl-io-zlib=1.11=pl5321hdfd78af_0
-  - perl-json=4.06=pl5321hdfd78af_0
-  - perl-json-xs=2.34=pl5321h9f5acd7_5
-  - perl-list-moreutils=0.430=pl5321hdfd78af_0
-  - perl-list-moreutils-xs=0.430=pl5321hec16e2b_1
-  - perl-parent=0.236=pl5321hdfd78af_2
-  - perl-pathtools=3.40=0
-  - perl-scalar-list-utils=1.62=pl5321hec16e2b_0
-  - perl-threaded=5.32.1=hdfd78af_1
-  - perl-types-serialiser=1.01=pl5321hdfd78af_0
-  - pexpect=4.8.0=pyhd3eb1b0_3
-  - pickleshare=0.7.5=pyhd3eb1b0_1003
-  - pillow=9.0.1=py39h22f2fdc_0
-  - pip=21.2.4=py39h06a4308_0
-  - plotly=5.9.0=py39h06a4308_0
-  - posthog=1.4.8=pyhd8ed1ab_0
-  - prometheus_client=0.13.1=pyhd3eb1b0_0
-  - prompt-toolkit=3.0.20=pyhd3eb1b0_0
-  - protobuf=3.20.1=py39h295c915_0
-  - ptyprocess=0.7.0=pyhd3eb1b0_2
-  - pure_eval=0.2.2=pyhd3eb1b0_0
-  - pyarrow=4.0.1=py39he0739d4_3
-  - pycparser=2.21=pyhd3eb1b0_0
-  - pydantic=1.9.1=py39hb9d737c_0
-  - pydeck=0.7.1=pyh6c4a22f_0
-  - pygments=2.11.2=pyhd3eb1b0_0
-  - pympler=0.9=py_0
-  - pyopenssl=22.0.0=pyhd3eb1b0_0
-  - pyparsing=3.0.4=pyhd3eb1b0_0
-  - pyqt=5.9.2=py39h2531618_6
-  - pyrsistent=0.18.0=py39heee7806_0
-  - pysocks=1.7.1=py39h06a4308_0
-  - python=3.9.12=h12debd9_1
-  - python-dateutil=2.8.2=pyhd3eb1b0_0
-  - python-fastjsonschema=2.15.1=pyhd3eb1b0_0
-  - python_abi=3.9=2_cp39
-  - pytz=2022.1=py39h06a4308_0
-  - pyyaml=6.0=py39h7f8727e_1
-  - pyzmq=22.3.0=py39h295c915_2
-  - qt=5.9.7=h5867ecd_1
-  - raxml=8.2.12=hec16e2b_4
-  - re2=2022.04.01=h295c915_0
-  - readline=8.1.2=h7f8727e_1
-  - requests=2.27.1=pyhd3eb1b0_0
-  - requests-toolbelt=0.9.1=py_0
-  - rich=10.16.2=pyhd3eb1b0_0
-  - s2n=1.0.10=h9b69904_0
-  - scikit-learn=1.0.2=py39h51133e4_1
-  - scipy=1.7.3=py39hc147768_0
-  - semver=2.13.0=pyhd3eb1b0_0
-  - send2trash=1.8.0=pyhd3eb1b0_1
-  - setuptools=61.2.0=py39h06a4308_0
-  - sip=4.19.13=py39h295c915_0
-  - six=1.16.0=pyhd3eb1b0_1
-  - smmap=4.0.0=pyhd3eb1b0_0
-  - snappy=1.1.9=h295c915_0
-  - soupsieve=2.3.1=pyhd3eb1b0_0
-  - sqlite=3.38.3=hc218d9a_0
-  - stack_data=0.2.0=pyhd3eb1b0_0
-  - streamlit=1.10.0=pyhd8ed1ab_1
-  - streamlit_tags=1.2.8=pyhd8ed1ab_0
-  - stringcase=1.2.0=py_0
-  - sysroot_linux-64=2.12=he073ed8_15
-  - tabulate=0.8.9=pyhd8ed1ab_0
-  - tenacity=8.0.1=py39h06a4308_1
-  - terminado=0.13.1=py39h06a4308_0
-  - testpath=0.5.0=pyhd3eb1b0_0
-  - threadpoolctl=2.2.0=pyh0d69192_0
-  - tk=8.6.12=h1ccaba5_0
-  - toml=0.10.2=pyhd3eb1b0_0
-  - toolz=0.11.2=pyhd3eb1b0_0
-  - tornado=6.1=py39h27cfd23_0
-  - traitlets=5.1.1=pyhd3eb1b0_0
-  - trimal=1.4.1=h9f5acd7_6
-  - typing-extensions=4.1.1=hd3eb1b0_0
-  - typing_extensions=4.1.1=pyh06a4308_0
-  - tzdata=2022a=hda174b7_0
-  - tzlocal=2.1=py39h06a4308_0
-  - unidecode=1.2.0=pyhd3eb1b0_0
-  - upsetplot=0.6.1=pyhd8ed1ab_0
-  - urllib3=1.26.9=py39h06a4308_0
-  - validators=0.18.2=pyhd3deb0d_0
-  - vega_datasets=0.9.0=pyhd3deb0d_0
-  - venn=0.1.3=pyhd8ed1ab_0
-  - watchdog=2.1.6=py39h06a4308_0
-  - wcwidth=0.2.5=pyhd3eb1b0_0
-  - webencodings=0.5.1=py39h06a4308_1
-  - wget=1.21.3=h0b77cf5_0
-  - wheel=0.37.1=pyhd3eb1b0_0
-  - widgetsnbextension=3.5.2=py39h06a4308_0
-  - xlrd=2.0.1=pyhd3eb1b0_0
-  - xlsxwriter=3.0.3=pyhd3eb1b0_0
-  - xmltodict=0.12.0=pyhd3eb1b0_0
-  - xorg-kbproto=1.0.7=h7f98852_1002
-  - xorg-libice=1.0.10=h7f98852_0
-  - xorg-libsm=1.2.3=hd9c2040_1000
-  - xorg-libx11=1.7.2=h7f98852_0
-  - xorg-libxau=1.0.9=h7f98852_0
-  - xorg-libxdmcp=1.1.3=h7f98852_0
-  - xorg-libxext=1.3.4=h7f98852_1
-  - xorg-libxrender=0.9.10=h7f98852_1003
-  - xorg-renderproto=0.11.1=h7f98852_1002
-  - xorg-xextproto=7.3.0=h7f98852_1002
-  - xorg-xproto=7.0.31=h27cfd23_1007
-  - xz=5.2.5=h7f8727e_1
-  - yaml=0.2.5=h7b6447c_0
-  - zeromq=4.3.4=h2531618_0
-  - zipp=3.8.0=py39h06a4308_0
-  - zlib=1.2.12=h166bdaf_0
-  - zstd=1.5.2=ha4553b6_0
-prefix: /home/cwseo/anaconda3/envs/FunID
diff --git a/funid/external/BLAST_Windows/bin/nghttp2.dll b/funid/external/BLAST_Windows/bin/nghttp2.dll
deleted file mode 100644
index 850efeb..0000000
Binary files a/funid/external/BLAST_Windows/bin/nghttp2.dll and /dev/null differ
diff --git a/funid/src/logger.py b/funid/src/logger.py
deleted file mode 100644
index f11ebd0..0000000
--- a/funid/src/logger.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from datetime import datetime
-import shelve
-
-
-def initialize(log):
-    global log_file
-    log_file = log
-
-
-# Logging Functions
-def Time_now():
-    now = datetime.now()
-    Date_time = "%s-%s-%s %s:%s:%s " % (
-        now.year,
-        now.month,
-        now.day,
-        now.hour,
-        now.minute,
-        now.second,
-    )
-    return Date_time
-
-
-def Mes(text):
-    text = str(text)
-    text = Time_now() + text
-
-    try:
-        log_file.write(text + "\n")
-    except:
-        pass
-
-    print(text)
-
-
-# should be deleted
-"""
-def Save(path):
-    save = shelve.open(path.save, "n")
-    for key in dir():
-        try:
-            save[key] = globals()[key]
-        except TypeError:
-            print(f"ERROR shelving: {key}")
-    save.close()
-"""
diff --git a/funid/.gitattributes b/funvip/.gitattributes
similarity index 100%
rename from funid/.gitattributes
rename to funvip/.gitattributes
diff --git a/funid/DB_documentation.txt b/funvip/DB_documentation.txt
similarity index 100%
rename from funid/DB_documentation.txt
rename to funvip/DB_documentation.txt
diff --git a/funvip/FunVIP.egg-info/PKG-INFO b/funvip/FunVIP.egg-info/PKG-INFO
new file mode 100644
index 0000000..74e97d9
--- /dev/null
+++ b/funvip/FunVIP.egg-info/PKG-INFO
@@ -0,0 +1,31 @@
+Metadata-Version: 2.1
+Name: FunVIP
+Version: 0.3.21
+Summary: Fungal Validation & Identification Pipeline
+Author-email: Changwan Seo <wan101010@snu.ac.kr>
+License: GPL-3.0
+Project-URL: Homepage, https://github.com/Changwanseo/FunVIP
+Requires-Python: <3.13,>3.8
+License-File: LICENSE
+Requires-Dist: biopython==1.78
+Requires-Dist: ete3==3.1.3
+Requires-Dist: Cython
+Requires-Dist: dendropy
+Requires-Dist: GenMine>=1.0.13
+Requires-Dist: lxml
+Requires-Dist: matplotlib
+Requires-Dist: numpy
+Requires-Dist: openpyxl==3.0.9
+Requires-Dist: pandas==1.4.2
+Requires-Dist: plotly==5.9.0
+Requires-Dist: psutil
+Requires-Dist: pyyaml
+Requires-Dist: sip>=4.19.4
+Requires-Dist: scikit-learn
+Requires-Dist: scipy
+Requires-Dist: tabulate
+Requires-Dist: unidecode==1.2.0
+Requires-Dist: xlrd==2.0.1
+Requires-Dist: xlsxwriter
+Requires-Dist: xmltodict==0.12.0
+Requires-Dist: PyQt5>=5.9.2; sys_platform != "darwin"
diff --git a/funvip/FunVIP.egg-info/SOURCES.txt b/funvip/FunVIP.egg-info/SOURCES.txt
new file mode 100644
index 0000000..64fb2ff
--- /dev/null
+++ b/funvip/FunVIP.egg-info/SOURCES.txt
@@ -0,0 +1,39 @@
+LICENSE
+README.md
+pyproject.toml
+funvip/FunVIP.egg-info/PKG-INFO
+funvip/FunVIP.egg-info/SOURCES.txt
+funvip/FunVIP.egg-info/dependency_links.txt
+funvip/FunVIP.egg-info/entry_points.txt
+funvip/FunVIP.egg-info/requires.txt
+funvip/FunVIP.egg-info/top_level.txt
+funvip/data/__init__.py
+funvip/external/__init__.py
+funvip/external/BLAST_Windows/bin/cleanup-blastdb-volumes.py
+funvip/src/__init__.py
+funvip/src/align.py
+funvip/src/cluster.py
+funvip/src/command.py
+funvip/src/concatenate.py
+funvip/src/dataset.py
+funvip/src/ext.py
+funvip/src/hasher.py
+funvip/src/initialize.py
+funvip/src/logger.py
+funvip/src/logics.py
+funvip/src/modeltest.py
+funvip/src/ncbi.py
+funvip/src/opt_generator.py
+funvip/src/patch.py
+funvip/src/reporter.py
+funvip/src/save.py
+funvip/src/search.py
+funvip/src/tool.py
+funvip/src/tree.py
+funvip/src/tree_interpretation.py
+funvip/src/tree_interpretation_pipe.py
+funvip/src/trim.py
+funvip/src/validate_input.py
+funvip/src/validate_option.py
+funvip/src/validation.py
+funvip/src/version.py
\ No newline at end of file
diff --git a/funid/FunID.egg-info/dependency_links.txt b/funvip/FunVIP.egg-info/dependency_links.txt
similarity index 100%
rename from funid/FunID.egg-info/dependency_links.txt
rename to funvip/FunVIP.egg-info/dependency_links.txt
diff --git a/funvip/FunVIP.egg-info/entry_points.txt b/funvip/FunVIP.egg-info/entry_points.txt
new file mode 100644
index 0000000..e3a5de3
--- /dev/null
+++ b/funvip/FunVIP.egg-info/entry_points.txt
@@ -0,0 +1,3 @@
+[console_scripts]
+FunID = funvip.main:main
+FunVIP = funvip.main:main
diff --git a/funvip/FunVIP.egg-info/requires.txt b/funvip/FunVIP.egg-info/requires.txt
new file mode 100644
index 0000000..75c9abe
--- /dev/null
+++ b/funvip/FunVIP.egg-info/requires.txt
@@ -0,0 +1,24 @@
+biopython==1.78
+ete3==3.1.3
+Cython
+dendropy
+GenMine>=1.0.13
+lxml
+matplotlib
+numpy
+openpyxl==3.0.9
+pandas==1.4.2
+plotly==5.9.0
+psutil
+pyyaml
+sip>=4.19.4
+scikit-learn
+scipy
+tabulate
+unidecode==1.2.0
+xlrd==2.0.1
+xlsxwriter
+xmltodict==0.12.0
+
+[:sys_platform != "darwin"]
+PyQt5>=5.9.2
diff --git a/funvip/FunVIP.egg-info/top_level.txt b/funvip/FunVIP.egg-info/top_level.txt
new file mode 100644
index 0000000..505952b
--- /dev/null
+++ b/funvip/FunVIP.egg-info/top_level.txt
@@ -0,0 +1,5 @@
+data
+external
+preset
+src
+test_dataset
diff --git a/funvip/FunVIP.yaml b/funvip/FunVIP.yaml
new file mode 100644
index 0000000..9ef3ae5
--- /dev/null
+++ b/funvip/FunVIP.yaml
@@ -0,0 +1,135 @@
+name: FunVIP
+channels:
+  - conda-forge
+  - bioconda
+  - defaults
+dependencies:
+  - _libgcc_mutex=0.1=conda_forge
+  - _openmp_mutex=4.5=2_gnu
+  - aria2=1.37.0=h347180d_1
+  - blast=2.15.0=pl5321h6f7f691_1
+  - bzip2=1.0.8=hd590300_5
+  - c-ares=1.28.1=hd590300_0
+  - ca-certificates=2024.2.2=hbcca054_0
+  - curl=8.7.1=hca28451_0
+  - entrez-direct=21.6=he881be0_0
+  - fasttree=2.1.11=h031d066_3
+  - gawk=5.3.0=ha916aea_0
+  - gblocks=0.91b=h9ee0642_2
+  - gettext=0.22.5=h59595ed_2
+  - gettext-tools=0.22.5=h59595ed_2
+  - gmp=6.3.0=h59595ed_1
+  - icu=73.2=h59595ed_0
+  - iqtree=2.3.3=h21ec9f0_0
+  - keyutils=1.6.1=h166bdaf_0
+  - krb5=1.21.2=h659d440_0
+  - ld_impl_linux-64=2.40=h55db66e_0
+  - libasprintf=0.22.5=h661eb56_2
+  - libasprintf-devel=0.22.5=h661eb56_2
+  - libcurl=8.7.1=hca28451_0
+  - libedit=3.1.20191231=he28a2e2_2
+  - libev=4.33=hd590300_2
+  - libexpat=2.6.2=h59595ed_0
+  - libffi=3.4.2=h7f98852_5
+  - libgcc-ng=13.2.0=h77fa898_7
+  - libgettextpo=0.22.5=h59595ed_2
+  - libgettextpo-devel=0.22.5=h59595ed_2
+  - libgfortran-ng=13.2.0=h69a702a_7
+  - libgfortran5=13.2.0=hca663fb_7
+  - libgomp=13.2.0=h77fa898_7
+  - libiconv=1.17=hd590300_2
+  - libidn2=2.3.7=hd590300_0
+  - libnghttp2=1.58.0=h47da74e_1
+  - libnsl=2.0.1=hd590300_0
+  - libsqlite=3.45.3=h2797004_0
+  - libssh2=1.11.0=h0841786_0
+  - libstdcxx-ng=13.2.0=hc0a3c3a_7
+  - libunistring=0.9.10=h7f98852_0
+  - libuuid=2.38.1=h0b41bf4_0
+  - libxcrypt=4.4.36=hd590300_1
+  - libxml2=2.12.7=hc051c1a_0
+  - libzlib=1.2.13=hd590300_5
+  - mafft=7.525=h031d066_1
+  - mmseqs2=15.6f452=pl5321h6a68c12_2
+  - modeltest-ng=0.1.7=h103dbdd_2
+  - mpfr=4.2.1=h9458935_1
+  - mpi=1.0=openmpi
+  - ncbi-vdb=3.1.0=h4ac6f70_2
+  - ncurses=6.5=h59595ed_0
+  - openmpi=4.1.6=hc5af2df_101
+  - openssl=3.3.0=hd590300_0
+  - pcre=8.45=h9c3ff4c_0
+  - perl=5.32.1=7_hd590300_perl5
+  - perl-archive-tar=2.40=pl5321hdfd78af_0
+  - perl-carp=1.38=pl5321hdfd78af_4
+  - perl-common-sense=3.75=pl5321hdfd78af_0
+  - perl-compress-raw-bzip2=2.201=pl5321h87f3376_1
+  - perl-compress-raw-zlib=2.105=pl5321h87f3376_0
+  - perl-encode=3.19=pl5321hec16e2b_1
+  - perl-exporter=5.72=pl5321hdfd78af_2
+  - perl-exporter-tiny=1.002002=pl5321hdfd78af_0
+  - perl-extutils-makemaker=7.70=pl5321hd8ed1ab_0
+  - perl-io-compress=2.201=pl5321hdbdd923_2
+  - perl-io-zlib=1.14=pl5321hdfd78af_0
+  - perl-json=4.10=pl5321hdfd78af_0
+  - perl-json-xs=2.34=pl5321h4ac6f70_6
+  - perl-list-moreutils=0.430=pl5321hdfd78af_0
+  - perl-list-moreutils-xs=0.430=pl5321h031d066_2
+  - perl-parent=0.236=pl5321hdfd78af_2
+  - perl-pathtools=3.75=pl5321hec16e2b_3
+  - perl-scalar-list-utils=1.62=pl5321hec16e2b_1
+  - perl-types-serialiser=1.01=pl5321hdfd78af_0
+  - pip=24.0=pyhd8ed1ab_0
+  - python=3.12.3=hab00c5b_0_cpython
+  - raxml=8.2.13=h031d066_1
+  - readline=8.2=h8228510_1
+  - setuptools=69.5.1=pyhd8ed1ab_0
+  - tk=8.6.13=noxft_h4845f30_101
+  - trimal=1.4.1=h4ac6f70_9
+  - tzdata=2024a=h0c530f3_0
+  - wget=1.21.4=hda4d442_0
+  - wheel=0.43.0=pyhd8ed1ab_1
+  - xz=5.2.6=h166bdaf_0
+  - zlib=1.2.13=hd590300_5
+  - zstd=1.5.6=ha6fb4c9_0
+  - pip:
+    - biopython==1.78
+    - contourpy==1.2.1
+    - cycler==0.12.1
+    - cython==3.0.10
+    - dendropy==4.6.1
+    - et-xmlfile==1.1.0
+    - ete3==3.1.3
+    - fonttools==4.51.0
+    - funvip==0.3.20.0.0.0
+    - genmine==1.0.13
+    - joblib==1.4.2
+    - kiwisolver==1.4.5
+    - lxml==5.2.2
+    - matplotlib==3.9.0
+    - numpy==1.26.4
+    - openpyxl==3.0.9
+    - packaging==24.0
+    - pandas==1.4.2
+    - pillow==10.3.0
+    - plotly==5.9.0
+    - psutil==5.9.8
+    - pyparsing==3.1.2
+    - pyqt5==5.15.10
+    - pyqt5-qt5==5.15.2
+    - pyqt5-sip==12.13.0
+    - python-dateutil==2.9.0.post0
+    - pytz==2024.1
+    - pyyaml==6.0.1
+    - scikit-learn==1.4.2
+    - scipy==1.13.0
+    - sip==6.8.3
+    - six==1.16.0
+    - tabulate==0.9.0
+    - tenacity==8.3.0
+    - threadpoolctl==3.5.0
+    - unidecode==1.2.0
+    - xlrd==2.0.1
+    - xlsxwriter==3.2.0
+    - xmltodict==0.12.0
+prefix: /home/cwseo/anaconda3/envs/FunVIP
diff --git a/funid/FunID_GUI.py b/funvip/FunVIP_GUI.py
similarity index 99%
rename from funid/FunID_GUI.py
rename to funvip/FunVIP_GUI.py
index 5886b17..35ce8d4 100644
--- a/funid/FunID_GUI.py
+++ b/funvip/FunVIP_GUI.py
@@ -78,7 +78,6 @@ def set_option(
         )
 
     if not (isnan(value)):
-
         value = value.replace(" ", "").split(",")
         return expander.selectbox(
             label=criterion, index=value.index(obj), options=value, help=help_
@@ -116,7 +115,6 @@ def set_option(
 
 # for designated value
 def set_value(type_, value=np.NaN):
-
     if isnan(value) is True:
         value = ""
 
@@ -155,7 +153,6 @@ def set_value(type_, value=np.NaN):
         st.session_state["option"] = False
 
     with st.sidebar.form(key="Input_option"):
-
         st.header("Option")
 
         option_submit = st.form_submit_button(label="Confirm")
@@ -332,7 +329,6 @@ def set_value(type_, value=np.NaN):
 
 # When Query submit
 if query_submit:
-
     # remove previous query files in the location
     # after using session id, we might not need this
 
@@ -425,8 +421,7 @@ def set_value(type_, value=np.NaN):
         print("Updating run status")
 
         # Running
-        with st.spinner("Running FunID"):
-
+        with st.spinner("Running FunVIP"):
             query_string = " ".join(
                 [
                     f"./Query/{get_script_run_ctx().session_id}_{i}.fasta"
@@ -443,7 +438,6 @@ def set_value(type_, value=np.NaN):
 
 if "run" in st.session_state:
     if st.session_state.run == "finished_run":
-
         # Figure out Runname
         runname = input_option["BASIC"]["RUN_NAME"]
         path_root = f"./Result/{runname}"
diff --git a/funid/FunID_Windows.yaml b/funvip/FunVIP_Windows.yaml
similarity index 99%
rename from funid/FunID_Windows.yaml
rename to funvip/FunVIP_Windows.yaml
index 4ab38ee..347e786 100644
--- a/funid/FunID_Windows.yaml
+++ b/funvip/FunVIP_Windows.yaml
@@ -1,4 +1,4 @@
-name: FunID_Windows
+name: FunVIP_Windows
 channels:
   - plotly
   - etetoolkit
diff --git a/funid/__init__.py b/funvip/__init__.py
similarity index 100%
rename from funid/__init__.py
rename to funvip/__init__.py
diff --git a/funid/data/Option_manager.xlsx b/funvip/data/Option_manager.xlsx
similarity index 100%
rename from funid/data/Option_manager.xlsx
rename to funvip/data/Option_manager.xlsx
diff --git a/funid/data/__init__.py b/funvip/data/__init__.py
similarity index 100%
rename from funid/data/__init__.py
rename to funvip/data/__init__.py
diff --git a/funid/data/genus_line.txt b/funvip/data/genus_line.txt
similarity index 100%
rename from funid/data/genus_line.txt
rename to funvip/data/genus_line.txt
diff --git a/funid/download_tmp b/funvip/download_tmp
similarity index 100%
rename from funid/download_tmp
rename to funvip/download_tmp
diff --git a/funid/external/BLAST_Windows/Uninstall-ncbi-blast-2.12.0+.exe b/funvip/external/BLAST_Windows/Uninstall-ncbi-blast-2.12.0+.exe
similarity index 100%
rename from funid/external/BLAST_Windows/Uninstall-ncbi-blast-2.12.0+.exe
rename to funvip/external/BLAST_Windows/Uninstall-ncbi-blast-2.12.0+.exe
diff --git a/funid/external/BLAST_Windows/bin/blastn.exe b/funvip/external/BLAST_Windows/bin/blastn.exe
similarity index 60%
rename from funid/external/BLAST_Windows/bin/blastn.exe
rename to funvip/external/BLAST_Windows/bin/blastn.exe
index 5208b69..68ddd2b 100644
Binary files a/funid/external/BLAST_Windows/bin/blastn.exe and b/funvip/external/BLAST_Windows/bin/blastn.exe differ
diff --git a/funid/external/BLAST_Windows/bin/cleanup-blastdb-volumes.py b/funvip/external/BLAST_Windows/bin/cleanup-blastdb-volumes.py
similarity index 100%
rename from funid/external/BLAST_Windows/bin/cleanup-blastdb-volumes.py
rename to funvip/external/BLAST_Windows/bin/cleanup-blastdb-volumes.py
diff --git a/funid/external/BLAST_Windows/bin/get_species_taxids.sh b/funvip/external/BLAST_Windows/bin/get_species_taxids.sh
similarity index 98%
rename from funid/external/BLAST_Windows/bin/get_species_taxids.sh
rename to funvip/external/BLAST_Windows/bin/get_species_taxids.sh
index 4791594..45964e6 100644
--- a/funid/external/BLAST_Windows/bin/get_species_taxids.sh
+++ b/funvip/external/BLAST_Windows/bin/get_species_taxids.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# $Id: get_species_taxids.sh 631570 2021-05-19 13:54:06Z ivanov $
+# $Id: get_species_taxids.sh 631039 2021-05-12 14:35:54Z fongah2 $
 # ===========================================================================
 #
 #                            PUBLIC DOMAIN NOTICE
diff --git a/funid/external/BLAST_Windows/bin/legacy_blast.pl b/funvip/external/BLAST_Windows/bin/legacy_blast.pl
similarity index 100%
rename from funid/external/BLAST_Windows/bin/legacy_blast.pl
rename to funvip/external/BLAST_Windows/bin/legacy_blast.pl
diff --git a/funvip/external/BLAST_Windows/bin/makeblastdb.exe b/funvip/external/BLAST_Windows/bin/makeblastdb.exe
new file mode 100644
index 0000000..212a1b9
Binary files /dev/null and b/funvip/external/BLAST_Windows/bin/makeblastdb.exe differ
diff --git a/funvip/external/BLAST_Windows/bin/nghttp2.dll b/funvip/external/BLAST_Windows/bin/nghttp2.dll
new file mode 100644
index 0000000..ac94fca
Binary files /dev/null and b/funvip/external/BLAST_Windows/bin/nghttp2.dll differ
diff --git a/funid/external/BLAST_Windows/bin/update_blastdb.pl b/funvip/external/BLAST_Windows/bin/update_blastdb.pl
similarity index 62%
rename from funid/external/BLAST_Windows/bin/update_blastdb.pl
rename to funvip/external/BLAST_Windows/bin/update_blastdb.pl
index 790ec24..5808b1a 100644
--- a/funid/external/BLAST_Windows/bin/update_blastdb.pl
+++ b/funvip/external/BLAST_Windows/bin/update_blastdb.pl
@@ -1,5 +1,5 @@
 #!/usr/bin/env perl
-# $Id: update_blastdb.pl 632592 2021-06-03 13:20:59Z ivanov $
+# $Id: update_blastdb.pl 672917 2023-09-18 17:55:38Z ivanov $
 # ===========================================================================
 #
 #                            PUBLIC DOMAIN NOTICE
@@ -49,6 +49,7 @@
 use constant DEBUG => 0;
 use constant MAX_DOWNLOAD_ATTEMPTS => 3;
 use constant EXIT_FAILURE => 1;
+use constant LEGACY_EXIT_FAILURE => 2;
 
 use constant AWS_URL => "http://s3.amazonaws.com";
 use constant AMI_URL => "http://169.254.169.254/latest/meta-data/local-hostname";
@@ -58,7 +59,8 @@
 use constant GCP_URL => "http://metadata.google.internal/computeMetadata/v1/instance/id";
 use constant GCP_BUCKET => "blast-db";
 
-use constant BLASTDB_MANIFEST => "blastdb-manifest.json";
+# TODO: deprecate this in the next release 2.14.x
+#use constant BLASTDB_MANIFEST => "blastdb-manifest.json";
 use constant BLASTDB_MANIFEST_VERSION => "1.0";
 
 use constant BLASTDB_METADATA => "blastdb-metadata-1-1.json";
@@ -77,7 +79,9 @@
 my $opt_decompress = 0;
 my $opt_source;
 my $opt_legacy_exit_code = 0;
-my $opt_nt = &get_num_cores();
+my $opt_nt = &compute_default_num_threads();
+my $opt_gcp_prj = undef;
+my $opt_use_ftp_protocol = 0;
 my $result = GetOptions("verbose+"          =>  \$opt_verbose,
                         "quiet"             =>  \$opt_quiet,
                         "force"             =>  \$opt_force_download,
@@ -88,8 +92,10 @@
                         "blastdb_version:i" =>  \$opt_blastdb_ver,
                         "decompress"        =>  \$opt_decompress,
                         "source=s"          =>  \$opt_source,
+                        "gcp-project=s"     =>  \$opt_gcp_prj,
                         "num_threads=i"     =>  \$opt_nt,
                         "legacy_exit_code"  =>  \$opt_legacy_exit_code,
+                        "force_ftp"         =>  \$opt_use_ftp_protocol,
                         "help"              =>  \$opt_help);
 $opt_verbose = 0 if $opt_quiet;
 die "Failed to parse command line options\n" unless $result;
@@ -106,8 +112,16 @@
                -msg => "Invalid BLAST database version: $opt_blastdb_ver. Supported values: 4, 5"}) 
         unless ($opt_blastdb_ver == 4 or $opt_blastdb_ver == 5);
 }
+
 pod2usage({-exitval => 1, -verbose => 0, -msg => "Invalid number of threads"}) 
-    if ($opt_nt <= 0);
+    if ($opt_nt < 0);
+my $max_num_cores = &get_num_cores();
+$opt_nt = $max_num_cores if ($opt_nt == 0);
+if ($opt_nt > $max_num_cores) {
+    print STDERR "WARNING: num_threads setting of $opt_nt is higher than the available number of cores ($max_num_cores); resetting it to $max_num_cores\n";
+    $opt_nt = $max_num_cores;
+}
+
 if (length($opt_passive) and $opt_passive =~ /n|no/i) {
     $opt_passive = 0;
 } else {
@@ -117,12 +131,14 @@
 $|++;
 
 if ($opt_show_version) {
-    my $revision = '$Revision: 632592 $';
+    my $revision = '$Revision: 672917 $';
     $revision =~ s/\$Revision: | \$//g;
     print "$0 version $revision\n";
     exit($exit_code);
 }
 my $curl = &get_curl_path();
+my $gsutil = &get_gsutil_path();
+my $gcloud = &get_gcloud_path();
 
 my $location = "NCBI";
 # If provided, the source takes precedence over any attempts to determine the closest location
@@ -161,10 +177,20 @@
         print "Location is $location\n" if DEBUG;
     }
 }
-if ($location =~ /aws|gcp/i and not defined $curl) {
-    print "Error: $0 depends on curl to fetch data from cloud storage, please install this utility to access these data sources.\n";
+if ($location =~ /aws/i and not defined $curl) {
+    print "Error: $0 depends on curl to fetch data from cloud storage, please install this utility to access this data source.\n";
+    exit(EXIT_FAILURE);
+}
+if ($location =~ /gcp/i and defined($opt_gcp_prj) and (not defined $gsutil or not defined $gcloud)) {
+    print "Error: when providing a GCP project, $0 depends on gsutil and gcloud to fetch data from cloud storage, please install these utilities to access this data source.\n";
     exit(EXIT_FAILURE);
 }
+my $gcp_prj = $opt_gcp_prj;
+#my $gcp_prj = ($location =~ /gcp/i) ? &get_gcp_project() : undef;
+#if ($location =~ /gcp/i and not defined $gcp_prj) {
+#    print "Error: $0 depends on gcloud being configured to fetch data from cloud storage, please configure it per the instructions in https://cloud.google.com/sdk/docs/initializing .\n";
+#    exit(EXIT_FAILURE);
+#}
 
 my $ftp;
 
@@ -211,7 +237,7 @@ sub showall_from_metadata_file
 }
 
 # Display metadata from version 1.1 of BLASTDB metadata files
-sub showall_from_metadata_file_1_1
+sub showall_from_metadata_file_1_1($$)
 {
     my $json = shift;
     my $url = shift;
@@ -240,6 +266,20 @@ sub showall_from_metadata_file_1_1
     }
 }
 
+sub get_files_from_json_metadata_1_1($$)
+{
+    my $json = shift;
+    my $url = shift;
+    my @retval = ();
+    &validate_metadata_file($json, $url);
+    my $metadata = decode_json($json);
+    foreach my $db (sort @$metadata) {
+        next if ($$db{version} ne BLASTDB_METADATA_VERSION);
+        push @retval, map { s,ftp://,https://,; $_; } @{$$db{files}};
+    }
+    return @retval;
+}
+
 if ($location ne "NCBI") {
     die "Only BLASTDB version 5 is supported at GCP and AWS\n" if (defined $opt_blastdb_ver and $opt_blastdb_ver != 5);
     my $latest_dir = &get_latest_dir($location);
@@ -249,17 +289,27 @@ sub showall_from_metadata_file_1_1
         exit(EXIT_FAILURE);
     }
     print "Connected to $location\n" if $opt_verbose;
+    print "Metadata source $url\n" if ($opt_verbose > 3);
     &validate_metadata_file($json, $url);
     my $metadata = decode_json($json);
     if (defined($opt_showall)) {
-        &showall_from_metadata_file($json, $url);
+        &showall_from_metadata_file_1_1($json, $url);
     } else {
+        &ensure_available_disk_space($json, $url);
         my @files2download;
         for my $requested_db (@ARGV) {
-            if (exists $$metadata{$requested_db}) {
-                push @files2download, @{$$metadata{$requested_db}{files}};
-            } else {
-                print STDERR "Warning: $requested_db does not exist in $location ($latest_dir)\n";
+            my $found = 0;
+            foreach my $dbm (sort @$metadata) {
+                if ($$dbm{'dbname'} eq $requested_db) {
+                    push @files2download, @{$$dbm{files}};
+                    $found = 1;
+                    last;
+                }
+            }
+            if (not $found) {
+                print STDERR "ERROR: $requested_db does not exist in $location ($latest_dir)\n";
+                my $exit_code = ($opt_legacy_exit_code == 1 ? LEGACY_EXIT_FAILURE : EXIT_FAILURE);
+                exit $exit_code;
             }
         }
         if (@files2download) {
@@ -267,8 +317,8 @@ sub showall_from_metadata_file_1_1
             my $awscli = &get_awscli_path();
             my $cmd;
             my $fh = File::Temp->new();
-            if ($location eq "GCP" and defined($gsutil)) {
-                $cmd = "$gsutil ";
+            if ($location eq "GCP" and defined($gcp_prj)) {
+                $cmd = "$gsutil -u $gcp_prj ";
                 if ($opt_nt > 1) {
                     $cmd .= "-m -q ";
                     $cmd .= "-o 'GSUtil:parallel_thread_count=1' -o 'GSUtil:parallel_process_count=$opt_nt' ";
@@ -286,7 +336,7 @@ sub showall_from_metadata_file_1_1
                 $cmd .= " -t" if $opt_verbose > 3;
                 $cmd .= " $aws_cmd {} .";
                 $cmd .= " <$fh " ;
-            } else { # fall back to  curl
+            } else { # fall back to curl
                 my $url = $location eq "AWS" ? AWS_URL : GCS_URL;
                 s,gs://,$url/, foreach (@files2download);
                 s,s3://,$url/, foreach (@files2download);
@@ -297,31 +347,33 @@ sub showall_from_metadata_file_1_1
                     $cmd .= " $curl -sSOR";
                     $cmd .= " <$fh " ;
                 } else {
-                    $cmd = "$curl -sSR";
-                    $cmd .= " -O $_" foreach (@files2download);
+                    $cmd = "$curl -sSR --remote-name-all ";
+                    $cmd .= join(" " , @files2download);
                 }
             }
             print "$cmd\n" if $opt_verbose > 3;
-            system($cmd);
+            unless (system($cmd) == 0) {
+                print STDERR "Failed to run '$cmd': $!!\n";
+                return EXIT_FAILURE;
+            }
         }
     }
 
 } else {
     # Connect and download files
     $ftp = &connect_to_ftp();
+    my ($json, $url) = &get_blastdb_metadata($location, '', $ftp);
+    unless (length($json)) {
+        print STDERR "ERROR: Missing manifest file $url, please report to blast-help\@ncbi.nlm.nih.gov\n";
+        exit(EXIT_FAILURE);
+    }
+    print "Metadata source $url\n" if ($opt_verbose > 3);
+
     if (defined $opt_showall) {
-        my ($json, $url) = &get_blastdb_metadata($location, '');
-        unless (length($json)) {
-            print "$_\n" foreach (sort(&get_available_databases($ftp->ls())));
-        } else {
-            if (ref($json) eq 'HASH') {
-                &showall_from_metadata_file($json, $url)
-            } else {
-                &showall_from_metadata_file_1_1($json, $url)
-            }
-        }
+        &showall_from_metadata_file_1_1($json, $url)
     } else {
-        my @files = sort(&get_files_to_download());
+        &ensure_available_disk_space($json, $url);
+        my @files = sort(&get_files_to_download($ftp, $json, $url));
         my @files2decompress;
         $exit_code = &download(\@files, \@files2decompress);
         if ($exit_code == 1) {
@@ -334,7 +386,7 @@ sub showall_from_metadata_file_1_1
             $exit_code = ($exit_code == 1 ? 0 : $exit_code);
         }
     }
-    $ftp->quit();
+    $ftp->quit() unless not defined($ftp);
 }
 
 exit($exit_code);
@@ -342,6 +394,11 @@ sub showall_from_metadata_file_1_1
 # Connects to NCBI ftp server
 sub connect_to_ftp
 {
+    return undef if ($^O =~ /darwin/);  # Net::FTP appears unreliable on Mac
+
+    # HTTPS access to NCBI FTP site works better than using the FTP protocol
+    return undef unless ($opt_use_ftp_protocol);
+
     my %ftp_opts;
     $ftp_opts{'Passive'} = 1 if $opt_passive;
     $ftp_opts{'Timeout'} = $opt_timeout if ($opt_timeout >= 0);
@@ -378,10 +435,107 @@ sub get_available_databases
     return grep { ! $seen{$_} ++ } @retval;
 }
 
+# Returns the last modified date for a file on the NCBI FTP or the number of
+# seconds since epoch at the time of invocation of this function
+sub get_last_modified_date_from_ncbi_ftp
+{
+    my $file = shift;
+    my $retval = time();
+    if (defined($ftp)) {
+        $retval = $ftp->mdtm($file);
+    } else {
+        use Time::Local;
+        my %month2int = (Jan=>0, Feb=>1, Mar=>2, Apr=>3, May=>4, Jun=>5,
+            Jul=>6, Aug=>8, Sep=>8, Oct=>9, Nov=>10, Dec=>11);
+        my $cmd = "$curl --user " . USER . ":" . PASSWORD . " -sI $file | grep ^Last-Modified";
+        chomp(my $date_str = `$cmd`);
+        if ($date_str =~ /Last-Modified:\s+\w+, (\d+) (\w+) (\d+) (\d+):(\d+):(\d+) GMT/) {
+            # Sample output: Wed, 07 Dec 2022 10:37:08 GMT
+            my $mday = int($1);
+            my $mon = $month2int{$2};
+            my $year = int($3) - 1900;
+            my $hour = int($4);
+            my $min = int($5);
+            my $sec = int($6);
+            $retval = Time::Local::timegm($sec, $min, $hour, $mday, $mon, $year);
+            print "$file $retval\n" if DEBUG;
+        }
+    }
+    return $retval;
+}
+
+# This function exits the program if not enough disk space is available for the
+# selected BLASTDBs
+sub ensure_available_disk_space
+{
+    my $json = shift;
+    my $url = shift;
+    my $space_needed = 0;
+    my $space_available = &get_available_disk_space;
+    print "Available disk space in bytes: $space_available\n" if ($opt_verbose > 3);
+    return unless $space_available;
+
+    for my $requested_db (@ARGV) {
+        my $x = &get_database_size_from_metadata_1_1($requested_db, $json, $url);
+        $space_needed += $x if ($x > 0);
+    }
+    print "Needed disk space in bytes: $space_needed\n" if ($opt_verbose > 3);
+    if ($space_needed > $space_available) {
+        my $msg = "ERROR: Need $space_needed bytes and only ";
+        $msg .= "$space_available bytes are available\n";
+        print STDERR $msg;
+        my $exit_code = ($opt_legacy_exit_code == 1 ? LEGACY_EXIT_FAILURE : EXIT_FAILURE);
+        exit $exit_code;
+    }
+}
+
+# Returns the available disk space in bytes of the current working directory
+# Not supported in windows
+sub get_available_disk_space
+{
+    my $retval = 0;
+    return $retval if ($^O =~ /mswin/i);
+
+    my $BLK_SIZE = 512;
+    my $cmd = "df -P --block-size $BLK_SIZE .";
+    $cmd = "df -P -b ." if ($^O =~ /darwin/i);
+    foreach (`$cmd 2>/dev/null`) {
+        chomp;
+        next if (/^Filesystem/);
+        my @F = split;
+        $retval = $F[3] * $BLK_SIZE if (scalar(@F) == 6);
+    }
+    print STDERR "WARNING: unable to compute available disk space\n" unless $retval;
+    return $retval;
+}
+
+sub get_database_size_from_metadata_1_1
+{
+    my $db = shift;
+    my $json = shift;
+    my $url = shift;
+    my $retval = -1;
+    &validate_metadata_file($json, $url);
+    my $metadata = decode_json($json);
+    foreach my $dbm (sort @$metadata) {
+        if ($$dbm{'dbname'} eq $db) {
+            $retval = $$dbm{'bytes-total'};
+            last;
+        }
+    }
+    print STDERR "Warning: No BLASTDB metadata for $db\n" if ($retval == -1);
+    return $retval;
+}
+
 # Obtains the list of files to download
-sub get_files_to_download
+sub get_files_to_download($$$)
 {
-    my @blast_db_files = $ftp->ls();
+    my $ftp = shift;
+    my $json = shift;
+    my $url = shift;
+    my @blast_db_files = (defined($ftp) 
+        ? $ftp->ls() 
+        : &get_files_from_json_metadata_1_1($json, $url));
     my @retval = ();
 
     if ($opt_verbose > 2) {
@@ -392,7 +546,10 @@ sub get_files_to_download
     for my $requested_db (@ARGV) {
         for my $file (@blast_db_files) {
             next unless ($file =~ /\.tar\.gz$/);    
-            if ($file =~ /^$requested_db\..*/) {
+            if (defined($ftp) and $file =~ /^$requested_db\..*/) {
+                push @retval, $file;
+            } elsif ($file =~ /\/$requested_db\..*/) {
+                # for Mac, which no longer relies on Net::FTP
                 push @retval, $file;
             }
         }
@@ -431,27 +588,36 @@ ($$)
 
         # We preserve the checksum files as evidence of the downloaded archive
         my $checksum_file = "$file.md5";
-        my $new_download = (-e $checksum_file ? 0 : 1);
-        my $update_available = ($new_download or 
-                    ((stat($checksum_file))->mtime < $ftp->mdtm($checksum_file)));
-        if (-e $file and (stat($file)->mtime < $ftp->mdtm($file))) {
+        my $rmt_checksum_file_mtime = &get_last_modified_date_from_ncbi_ftp($checksum_file);
+        my $rmt_file_mtime = &get_last_modified_date_from_ncbi_ftp($file);
+        my $lcl_checksum_file_mtime = (-e &rm_protocol($checksum_file)
+            ? stat(&rm_protocol($checksum_file))->mtime : 0);
+        print "RMT checksum file mtime $rmt_checksum_file_mtime\n" if DEBUG;
+        print "LCL checksum file mtime $lcl_checksum_file_mtime\n" if DEBUG;
+        my $update_available = ($lcl_checksum_file_mtime < $rmt_checksum_file_mtime);
+        if (-e $file and (stat(&rm_protocol($file))->mtime < $rmt_file_mtime)) {
             $update_available = 1;
         }
 
 download_file:
-        if ($opt_force_download or $new_download or $update_available) {
+        if ($opt_force_download or $update_available) {
             print "Downloading $file..." if $opt_verbose;
-            $ftp->get($file);
-            unless ($ftp->get($checksum_file)) {
-                print STDERR "Failed to download $checksum_file!\n";
-                return EXIT_FAILURE;
+            if (defined($ftp)) {
+                # Download errors will be checked later when reading checksum files
+                $ftp->get($file);
+                $ftp->get($checksum_file);
+            } else {
+                my $cmd = "$curl --user " . USER . ":" . PASSWORD . " -sSR ";
+                $cmd .= "--remote-name-all $file $file.md5";
+                print "$cmd\n" if $opt_verbose > 3;
+                system($cmd);
             }
             my $rmt_digest = &read_md5_file($checksum_file);
             my $lcl_digest = &compute_md5_checksum($file);
             print "\nRMT $file Digest $rmt_digest" if (DEBUG);
             print "\nLCL $file Digest $lcl_digest\n" if (DEBUG);
             if ($lcl_digest ne $rmt_digest) {
-                unlink $file, $checksum_file;
+                unlink &rm_protocol($file), &rm_protocol($checksum_file);
                 if (++$attempts >= MAX_DOWNLOAD_ATTEMPTS) {
                     print STDERR "too many failures, aborting download!\n";
                     return EXIT_FAILURE;
@@ -488,9 +654,10 @@ ($)
         my $cmd = "tar -zxf $file 2>/dev/null";
         return 1 unless (system($cmd));
     }
-    unless ($^O =~ /win/i) {
+    unless ($^O =~ /mswin/i) {
         local $ENV{PATH} = "/bin:/usr/bin";
         my $cmd = "gzip -cd $file 2>/dev/null | tar xf - 2>/dev/null";
+        print "$cmd\n" if $opt_verbose > 3;
         return 1 unless (system($cmd));
     }
     return Archive::Tar->extract_archive($file, 1);
@@ -501,6 +668,7 @@ ($)
 sub decompress($)
 {
     my $file = shift;
+    $file = &rm_protocol($file);
     print "Decompressing $file ..." unless ($opt_quiet);
     my $succeeded = &_decompress_impl($file);
     unless ($succeeded) {
@@ -509,6 +677,7 @@ ($)
         print STDERR "$msg\n";
         return EXIT_FAILURE;
     }
+    print "rm $file\n" if $opt_verbose > 3;
     unlink $file;   # Clean up archive, but preserve the checksum file
     print " [OK]\n" unless ($opt_quiet);
     return 1;
@@ -518,6 +687,7 @@ ($)
 {
     my $file = shift;
     my $digest = "N/A";
+    $file = &rm_protocol($file);
     if (open(DOWNLOADED_FILE, $file)) {
         binmode(DOWNLOADED_FILE);
         $digest = Digest::MD5->new->addfile(*DOWNLOADED_FILE)->hexdigest;
@@ -526,9 +696,22 @@ ($)
     return $digest;
 }
 
+# Removes the protocol prefix from the file name passed in
+sub rm_protocol($)
+{
+    my $retval = shift;
+    if ($retval =~ /^https:/) {
+        my $prefix = "https://" . NCBI_FTP . BLAST_DB_DIR . "/";
+        $retval =~ s/$prefix//;
+    }
+    return $retval;
+}
+
 sub read_md5_file($)
 {
     my $md5file = shift;
+    $md5file = &rm_protocol($md5file);
+    return '' unless (-f $md5file);
     open(IN, $md5file);
     $_ = <IN>;
     close(IN);
@@ -575,15 +758,25 @@ sub get_num_volumes
     return $retval + 1;
 }
 
-# Retrieves the name of the 'subdirectory' where the latest BLASTDBs residue in GCP
+# Retrieves the name of the 'subdirectory' where the latest BLASTDBs reside
 sub get_latest_dir
 {
     my $source = shift;
-    my $url = GCS_URL . "/" . GCP_BUCKET . "/latest-dir";
-    $url = AWS_URL . "/" . AWS_BUCKET . "/latest-dir" if ($source eq "AWS");
-    my $cmd = "$curl -s $url";
+    my ($retval, $url, $cmd);
+    if ($source eq "AWS") {
+        $url = AWS_URL . "/" . AWS_BUCKET . "/latest-dir";
+        $cmd = "$curl -s $url";
+    } else {
+        if (defined($gcp_prj)) {
+            $url = 'gs://' . GCP_BUCKET . "/latest-dir";
+            $cmd = "$gsutil -u $gcp_prj cat $url";
+        } else {
+            $url = GCS_URL . "/" . GCP_BUCKET . "/latest-dir";
+            $cmd = "$curl -s $url";
+        }
+    }
     print "$cmd\n" if DEBUG;
-    chomp(my $retval = `$cmd`);
+    chomp($retval = `$cmd`);
     unless (length($retval)) {
         print STDERR "ERROR: Missing file $url, please try again or report to blast-help\@ncbi.nlm.nih.gov\n";
         exit(EXIT_FAILURE);
@@ -592,48 +785,149 @@ sub get_latest_dir
     return $retval;
 }
 
-# Fetches the JSON text containing the BLASTDB metadata in GCS
+# Fetches the JSON text containing the BLASTDB metadata
 sub get_blastdb_metadata
 {
     my $source = shift;
     my $latest_dir = shift;
-    my $url = GCS_URL . "/" . GCP_BUCKET . "/$latest_dir/" . BLASTDB_MANIFEST;
-    $url = AWS_URL . "/" . AWS_BUCKET . "/$latest_dir/" . BLASTDB_MANIFEST if ($source eq "AWS");
-    $url = 'ftp://' . NCBI_FTP . "/blast/db/" . BLASTDB_METADATA if ($source eq 'NCBI');
-    my $cmd = "curl -sf $url";
-    print "$cmd\n" if DEBUG;
-    chomp(my $retval = `$cmd`);
+    my $ftp = shift;
+    my ($url, $cmd);
+    my $retval;
+
+    if ($source eq "AWS") {
+        $url = AWS_URL . "/" . AWS_BUCKET . "/$latest_dir/" . BLASTDB_METADATA;
+        $cmd = "curl -sf $url";
+    } elsif ($source eq "GCP") {
+        if (defined($gcp_prj)) {
+            $url = 'gs://' . GCP_BUCKET . "/$latest_dir/" . BLASTDB_METADATA;
+            $cmd = "$gsutil -u $gcp_prj cat $url";
+        } else {
+            $url = GCS_URL . "/" . GCP_BUCKET . "/$latest_dir/" . BLASTDB_METADATA;
+            $cmd = "curl -sf $url";
+        }
+    } else {
+        $url = 'https://' . NCBI_FTP . "/blast/db/" . BLASTDB_METADATA;
+        $cmd = "curl -sf $url";
+    }
+    if (defined $ftp) {
+        my $tmpfh = File::Temp->new();
+        print "Downloading " . BLASTDB_METADATA . " to $tmpfh\n" if ($opt_verbose > 3);
+        $ftp->get(BLASTDB_METADATA, $tmpfh);
+        $tmpfh->seek(0, 0);
+        $retval = do {
+            local $/ = undef;
+            <$tmpfh>;
+        };
+    } else {
+        print "$cmd\n" if DEBUG;
+        chomp($retval = `$cmd`);
+    }
     return ($retval, $url);
 }
 
 # Returns the path to the gsutil utility or undef if it is not found
 sub get_gsutil_path
 {
+    return undef if ($^O =~ /mswin/i);
     foreach (qw(/google/google-cloud-sdk/bin /usr/local/bin /usr/bin /snap/bin)) {
         my $path = "$_/gsutil";
         return $path if (-f $path);
     }
+    chomp(my $retval = `which gsutil`);
+    return $retval if (-f $retval);
+    return undef;
+}
+
+sub get_gcloud_path
+{
+    return undef if ($^O =~ /mswin/i);
+    foreach (qw(/google/google-cloud-sdk/bin /usr/local/bin /usr/bin /snap/bin)) {
+        my $path = "$_/gcloud";
+        return $path if (-f $path);
+    }
+    chomp(my $retval = `which gcloud`);
+    return $retval if (-f $retval);
     return undef;
 }
 
+sub get_gcp_project
+{
+    return undef if ($^O =~ /mswin/i);
+    my $gcloud = &get_gcloud_path();
+    chomp(my $retval = `$gcloud config get-value project`);
+    return $retval;
+}
+
 # Returns the path to the aws CLI utility or undef if it is not found
 sub get_awscli_path
 {
+    return undef if ($^O =~ /mswin/i);
     foreach (qw(/usr/local/bin /usr/bin)) {
         my $path = "$_/aws";
         return $path if (-f $path);
     }
+    chomp(my $retval = `which aws`);
+    return $retval if (-f $retval);
     return undef;
 }
 
+# Returns the number of cores, assuming lscpu is available, otherwise undef
+sub read_lscpu_output 
+{
+    my $retval;
+    if (open(my $fh, "-|", "/usr/bin/lscpu")) {
+        my ($num_threads, $num_threads_per_core);
+        while (<$fh>) {
+            /^CPU.s.:\s*(\d+)/ and $num_threads = $1;
+            /^Thread.s. per core:\s*(\d+)/ and $num_threads_per_core = $1;
+        }
+        if ($num_threads_per_core and $num_threads) {
+            $retval = $num_threads / $num_threads_per_core;
+        }
+        close($fh);
+    }
+    return $retval;
+}
+
+sub compute_default_num_threads
+{
+    my $retval = 1;
+    return $retval if ($^O =~ /mswin/i);
+    my $quarter = int(&get_num_cores()*.25);
+    return ($quarter > $retval ? $quarter : $retval);
+}
+
+# Returns the number of cores based on /proc/cpuinfo, otherwise undef
+sub read_proc_cpuinfo 
+{
+    my $retval;
+    if (open(my $fh, "<", "/proc/cpuinfo")) {
+        my ($num_siblings, $num_processors, $num_cpu_cores);
+        while (<$fh>) {
+            /^siblings\s+:\s+(\d+)/ and $num_siblings = $1;
+            /^processor/ and $num_processors++;
+            /^cpu cores\s+:\s+(\d+)/ and $num_cpu_cores = $1;
+        }
+        close($fh);
+        if ($num_siblings == $num_cpu_cores) {
+            $retval = $num_processors;
+        } else {
+            $retval = $num_processors / ($num_siblings / $num_cpu_cores);
+        }
+    }
+    return $retval;
+}
+
 # Returns the number of cores, or 1 if unknown
 sub get_num_cores
 {
     my $retval = 1;
     if ($^O =~ /linux/i) {
-        open my $fh, "/proc/cpuinfo" or return $retval;
-        $retval = scalar(map /^processor/, <$fh>);
-        close($fh);
+        $retval = &read_lscpu_output();
+        unless (defined($retval)) {
+            $retval = &read_proc_cpuinfo();
+        }
+        $retval = 1 unless defined ($retval);
     } elsif ($^O =~ /darwin/i) {
         chomp($retval = `/usr/sbin/sysctl -n hw.ncpu`);
     }
@@ -674,13 +968,14 @@ =head1 OPTIONS
 =item B<--source>
 
 Location to download BLAST databases from (default: auto-detect closest location).
-Supported values: ncbi, aws, or gcp.
+Supported values: C<ncbi>, C<aws>, or C<gcp>.
 
 =item B<--decompress>
 
 Downloads, decompresses the archives in the current working directory, and
 deletes the downloaded archive to save disk space, while preserving the
 archive checksum files (default: false).
+This is only applicable when the download source is C<ncbi>.
 
 =item B<--showall>
 
@@ -688,7 +983,7 @@ =head1 OPTIONS
 of this option lists the database names which should be used when
 requesting downloads or updates using this script.
 
-It accepts the optional arguments: 'tsv' and 'pretty' to produce tab-separated values
+It accepts the optional arguments: C<tsv> and C<pretty> to produce tab-separated values
 and a human-readable format respectively. These parameters elicit the display of
 additional metadata if this is available to the program.
 This metadata is displayed in columnar format; the columns represent:
@@ -700,11 +995,6 @@ =head1 OPTIONS
 Specify which BLAST database version to download (default: 5).
 Supported values: 4, 5
 
-=item B<--passive>
-
-Use passive FTP, useful when behind a firewall or working in the cloud (default: true).
-To disable passive FTP, configure this option as follows: --passive no
-
 =item B<--timeout>
 
 Timeout on connection to NCBI (default: 120 seconds).
@@ -730,7 +1020,9 @@ =head1 OPTIONS
 =item B<--num_threads>
 
 Sets the number of threads to utilize to perform downloads in parallel when data comes from the cloud.
-Defaults to use all available CPUs on the machine (Linux and macos only).
+On Windows it defaults to 1.
+On Linux and macos: defaults to max(1, (number_of_available_cores/4)) and if
+set to 0, the script uses all available cores on the machine.
 
 =item B<--legacy_exit_code>
 
@@ -740,6 +1032,17 @@ =head1 OPTIONS
 0 for successful operations that result in no downloads, 1 for successful
 downloads, and 2 for errors.
 
+=item B<--force_ftp>
+
+Forces downloads using the FTP protocol from the NCBI (Linux and Windows only).
+If the location from which to download is not NCBI, this option is ignored.
+
+=item B<--passive>
+
+When using the <force_ftp> option, this flag enables passive FTP, useful when
+behind a firewall or working in the cloud (default: true).
+To disable passive FTP, configure this option as follows: --passive no
+
 =back
 
 =head1 DESCRIPTION
diff --git a/funid/external/BLAST_Windows/doc/README.txt b/funvip/external/BLAST_Windows/doc/README.txt
similarity index 100%
rename from funid/external/BLAST_Windows/doc/README.txt
rename to funvip/external/BLAST_Windows/doc/README.txt
diff --git a/funid/external/FastTree_Windows/FastTree.exe b/funvip/external/FastTree_Windows/FastTree.exe
similarity index 100%
rename from funid/external/FastTree_Windows/FastTree.exe
rename to funvip/external/FastTree_Windows/FastTree.exe
diff --git a/funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/Documentation/Gblocks_documentation.html b/funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/Documentation/Gblocks_documentation.html
similarity index 100%
rename from funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/Documentation/Gblocks_documentation.html
rename to funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/Documentation/Gblocks_documentation.html
diff --git a/funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/Gblocks.exe b/funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/Gblocks.exe
similarity index 100%
rename from funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/Gblocks.exe
rename to funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/Gblocks.exe
diff --git a/funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/cox2.pir b/funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/cox2.pir
similarity index 100%
rename from funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/cox2.pir
rename to funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/cox2.pir
diff --git a/funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/cytb.pir b/funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/cytb.pir
similarity index 100%
rename from funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/cytb.pir
rename to funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/cytb.pir
diff --git a/funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/nad3.pir b/funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/nad3.pir
similarity index 100%
rename from funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/nad3.pir
rename to funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/nad3.pir
diff --git a/funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/nad5.pir b/funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/nad5.pir
similarity index 100%
rename from funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/nad5.pir
rename to funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/more_alignments/nad5.pir
diff --git a/funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/nad3.pir b/funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/nad3.pir
similarity index 100%
rename from funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/nad3.pir
rename to funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/nad3.pir
diff --git a/funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/paths b/funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/paths
similarity index 100%
rename from funid/external/Gblocks_Windows_0.91b/Gblocks_0.91b/paths
rename to funvip/external/Gblocks_Windows_0.91b/Gblocks_0.91b/paths
diff --git a/funid/external/MAFFT_LICENSE b/funvip/external/MAFFT_LICENSE
similarity index 100%
rename from funid/external/MAFFT_LICENSE
rename to funvip/external/MAFFT_LICENSE
diff --git a/funid/external/MAFFT_Windows.zip b/funvip/external/MAFFT_Windows.zip
similarity index 100%
rename from funid/external/MAFFT_Windows.zip
rename to funvip/external/MAFFT_Windows.zip
diff --git a/funid/external/RAxML_Windows/GPL-3.0.txt b/funvip/external/RAxML_Windows/GPL-3.0.txt
similarity index 100%
rename from funid/external/RAxML_Windows/GPL-3.0.txt
rename to funvip/external/RAxML_Windows/GPL-3.0.txt
diff --git a/funid/external/RAxML_Windows/README b/funvip/external/RAxML_Windows/README
similarity index 100%
rename from funid/external/RAxML_Windows/README
rename to funvip/external/RAxML_Windows/README
diff --git a/funid/external/RAxML_Windows/raxmlHPC-PTHREADS-AVX2.exe b/funvip/external/RAxML_Windows/raxmlHPC-PTHREADS-AVX2.exe
similarity index 100%
rename from funid/external/RAxML_Windows/raxmlHPC-PTHREADS-AVX2.exe
rename to funvip/external/RAxML_Windows/raxmlHPC-PTHREADS-AVX2.exe
diff --git a/funid/external/__init__.py b/funvip/external/__init__.py
similarity index 100%
rename from funid/external/__init__.py
rename to funvip/external/__init__.py
diff --git a/funid/external/BLAST_Windows/bin/makeblastdb.exe b/funvip/external/iqtree/bin/iqtree2-click.exe
similarity index 55%
rename from funid/external/BLAST_Windows/bin/makeblastdb.exe
rename to funvip/external/iqtree/bin/iqtree2-click.exe
index 331a2bf..69b5ba4 100644
Binary files a/funid/external/BLAST_Windows/bin/makeblastdb.exe and b/funvip/external/iqtree/bin/iqtree2-click.exe differ
diff --git a/funid/external/iqtree-2.2.2.7-Windows/bin/iqtree2.exe b/funvip/external/iqtree/bin/iqtree2.exe
similarity index 63%
rename from funid/external/iqtree-2.2.2.7-Windows/bin/iqtree2.exe
rename to funvip/external/iqtree/bin/iqtree2.exe
index d9b5190..d6a9530 100644
Binary files a/funid/external/iqtree-2.2.2.7-Windows/bin/iqtree2.exe and b/funvip/external/iqtree/bin/iqtree2.exe differ
diff --git a/funid/external/iqtree-2.2.2.7-Windows/bin/libiomp5md.dll b/funvip/external/iqtree/bin/libiomp5md.dll
similarity index 100%
rename from funid/external/iqtree-2.2.2.7-Windows/bin/libiomp5md.dll
rename to funvip/external/iqtree/bin/libiomp5md.dll
diff --git a/funid/external/iqtree-2.2.2.7-Windows/example.cf b/funvip/external/iqtree/example.cf
similarity index 100%
rename from funid/external/iqtree-2.2.2.7-Windows/example.cf
rename to funvip/external/iqtree/example.cf
diff --git a/funid/external/iqtree-2.2.2.7-Windows/example.nex b/funvip/external/iqtree/example.nex
similarity index 100%
rename from funid/external/iqtree-2.2.2.7-Windows/example.nex
rename to funvip/external/iqtree/example.nex
diff --git a/funid/external/iqtree-2.2.2.7-Windows/example.phy b/funvip/external/iqtree/example.phy
similarity index 100%
rename from funid/external/iqtree-2.2.2.7-Windows/example.phy
rename to funvip/external/iqtree/example.phy
diff --git a/funid/external/iqtree-2.2.2.7-Windows/models.nex b/funvip/external/iqtree/models.nex
similarity index 100%
rename from funid/external/iqtree-2.2.2.7-Windows/models.nex
rename to funvip/external/iqtree/models.nex
diff --git a/funid/external/mmseqs_Windows/LICENSE.md b/funvip/external/mmseqs_Windows/LICENSE.md
similarity index 100%
rename from funid/external/mmseqs_Windows/LICENSE.md
rename to funvip/external/mmseqs_Windows/LICENSE.md
diff --git a/funid/external/mmseqs_Windows/README.md b/funvip/external/mmseqs_Windows/README.md
similarity index 100%
rename from funid/external/mmseqs_Windows/README.md
rename to funvip/external/mmseqs_Windows/README.md
diff --git a/funid/external/mmseqs_Windows/bin/busybox.exe b/funvip/external/mmseqs_Windows/bin/busybox.exe
similarity index 100%
rename from funid/external/mmseqs_Windows/bin/busybox.exe
rename to funvip/external/mmseqs_Windows/bin/busybox.exe
diff --git a/funid/external/mmseqs_Windows/bin/cygbz2-1.dll b/funvip/external/mmseqs_Windows/bin/cygbz2-1.dll
similarity index 100%
rename from funid/external/mmseqs_Windows/bin/cygbz2-1.dll
rename to funvip/external/mmseqs_Windows/bin/cygbz2-1.dll
diff --git a/funid/external/mmseqs_Windows/bin/cyggcc_s-seh-1.dll b/funvip/external/mmseqs_Windows/bin/cyggcc_s-seh-1.dll
similarity index 100%
rename from funid/external/mmseqs_Windows/bin/cyggcc_s-seh-1.dll
rename to funvip/external/mmseqs_Windows/bin/cyggcc_s-seh-1.dll
diff --git a/funid/external/mmseqs_Windows/bin/cyggomp-1.dll b/funvip/external/mmseqs_Windows/bin/cyggomp-1.dll
similarity index 100%
rename from funid/external/mmseqs_Windows/bin/cyggomp-1.dll
rename to funvip/external/mmseqs_Windows/bin/cyggomp-1.dll
diff --git a/funid/external/mmseqs_Windows/bin/cygstdc++-6.dll b/funvip/external/mmseqs_Windows/bin/cygstdc++-6.dll
similarity index 100%
rename from funid/external/mmseqs_Windows/bin/cygstdc++-6.dll
rename to funvip/external/mmseqs_Windows/bin/cygstdc++-6.dll
diff --git a/funid/external/mmseqs_Windows/bin/cygwin1.dll b/funvip/external/mmseqs_Windows/bin/cygwin1.dll
similarity index 100%
rename from funid/external/mmseqs_Windows/bin/cygwin1.dll
rename to funvip/external/mmseqs_Windows/bin/cygwin1.dll
diff --git a/funid/external/mmseqs_Windows/bin/cygz.dll b/funvip/external/mmseqs_Windows/bin/cygz.dll
similarity index 100%
rename from funid/external/mmseqs_Windows/bin/cygz.dll
rename to funvip/external/mmseqs_Windows/bin/cygz.dll
diff --git a/funid/external/mmseqs_Windows/bin/mmseqs.exe b/funvip/external/mmseqs_Windows/bin/mmseqs.exe
similarity index 100%
rename from funid/external/mmseqs_Windows/bin/mmseqs.exe
rename to funvip/external/mmseqs_Windows/bin/mmseqs.exe
diff --git a/funid/external/mmseqs_Windows/examples/QUERY.fasta b/funvip/external/mmseqs_Windows/examples/QUERY.fasta
similarity index 100%
rename from funid/external/mmseqs_Windows/examples/QUERY.fasta
rename to funvip/external/mmseqs_Windows/examples/QUERY.fasta
diff --git a/funid/external/mmseqs_Windows/matrices/PAM10.out b/funvip/external/mmseqs_Windows/matrices/PAM10.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM10.out
rename to funvip/external/mmseqs_Windows/matrices/PAM10.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM100.out b/funvip/external/mmseqs_Windows/matrices/PAM100.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM100.out
rename to funvip/external/mmseqs_Windows/matrices/PAM100.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM110.out b/funvip/external/mmseqs_Windows/matrices/PAM110.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM110.out
rename to funvip/external/mmseqs_Windows/matrices/PAM110.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM120.out b/funvip/external/mmseqs_Windows/matrices/PAM120.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM120.out
rename to funvip/external/mmseqs_Windows/matrices/PAM120.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM130.out b/funvip/external/mmseqs_Windows/matrices/PAM130.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM130.out
rename to funvip/external/mmseqs_Windows/matrices/PAM130.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM140.out b/funvip/external/mmseqs_Windows/matrices/PAM140.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM140.out
rename to funvip/external/mmseqs_Windows/matrices/PAM140.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM150.out b/funvip/external/mmseqs_Windows/matrices/PAM150.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM150.out
rename to funvip/external/mmseqs_Windows/matrices/PAM150.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM160.out b/funvip/external/mmseqs_Windows/matrices/PAM160.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM160.out
rename to funvip/external/mmseqs_Windows/matrices/PAM160.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM170.out b/funvip/external/mmseqs_Windows/matrices/PAM170.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM170.out
rename to funvip/external/mmseqs_Windows/matrices/PAM170.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM180.out b/funvip/external/mmseqs_Windows/matrices/PAM180.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM180.out
rename to funvip/external/mmseqs_Windows/matrices/PAM180.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM190.out b/funvip/external/mmseqs_Windows/matrices/PAM190.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM190.out
rename to funvip/external/mmseqs_Windows/matrices/PAM190.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM20.out b/funvip/external/mmseqs_Windows/matrices/PAM20.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM20.out
rename to funvip/external/mmseqs_Windows/matrices/PAM20.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM30.out b/funvip/external/mmseqs_Windows/matrices/PAM30.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM30.out
rename to funvip/external/mmseqs_Windows/matrices/PAM30.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM40.out b/funvip/external/mmseqs_Windows/matrices/PAM40.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM40.out
rename to funvip/external/mmseqs_Windows/matrices/PAM40.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM50.out b/funvip/external/mmseqs_Windows/matrices/PAM50.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM50.out
rename to funvip/external/mmseqs_Windows/matrices/PAM50.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM60.out b/funvip/external/mmseqs_Windows/matrices/PAM60.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM60.out
rename to funvip/external/mmseqs_Windows/matrices/PAM60.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM70.out b/funvip/external/mmseqs_Windows/matrices/PAM70.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM70.out
rename to funvip/external/mmseqs_Windows/matrices/PAM70.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM80.out b/funvip/external/mmseqs_Windows/matrices/PAM80.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM80.out
rename to funvip/external/mmseqs_Windows/matrices/PAM80.out
diff --git a/funid/external/mmseqs_Windows/matrices/PAM90.out b/funvip/external/mmseqs_Windows/matrices/PAM90.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/PAM90.out
rename to funvip/external/mmseqs_Windows/matrices/PAM90.out
diff --git a/funid/external/mmseqs_Windows/matrices/VTML10.out b/funvip/external/mmseqs_Windows/matrices/VTML10.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/VTML10.out
rename to funvip/external/mmseqs_Windows/matrices/VTML10.out
diff --git a/funid/external/mmseqs_Windows/matrices/VTML120.out b/funvip/external/mmseqs_Windows/matrices/VTML120.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/VTML120.out
rename to funvip/external/mmseqs_Windows/matrices/VTML120.out
diff --git a/funid/external/mmseqs_Windows/matrices/VTML160.out b/funvip/external/mmseqs_Windows/matrices/VTML160.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/VTML160.out
rename to funvip/external/mmseqs_Windows/matrices/VTML160.out
diff --git a/funid/external/mmseqs_Windows/matrices/VTML20.out b/funvip/external/mmseqs_Windows/matrices/VTML20.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/VTML20.out
rename to funvip/external/mmseqs_Windows/matrices/VTML20.out
diff --git a/funid/external/mmseqs_Windows/matrices/VTML40.out b/funvip/external/mmseqs_Windows/matrices/VTML40.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/VTML40.out
rename to funvip/external/mmseqs_Windows/matrices/VTML40.out
diff --git a/funid/external/mmseqs_Windows/matrices/VTML80.out b/funvip/external/mmseqs_Windows/matrices/VTML80.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/VTML80.out
rename to funvip/external/mmseqs_Windows/matrices/VTML80.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum100.out b/funvip/external/mmseqs_Windows/matrices/blosum100.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum100.out
rename to funvip/external/mmseqs_Windows/matrices/blosum100.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum30.out b/funvip/external/mmseqs_Windows/matrices/blosum30.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum30.out
rename to funvip/external/mmseqs_Windows/matrices/blosum30.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum35.out b/funvip/external/mmseqs_Windows/matrices/blosum35.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum35.out
rename to funvip/external/mmseqs_Windows/matrices/blosum35.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum40.out b/funvip/external/mmseqs_Windows/matrices/blosum40.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum40.out
rename to funvip/external/mmseqs_Windows/matrices/blosum40.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum45.out b/funvip/external/mmseqs_Windows/matrices/blosum45.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum45.out
rename to funvip/external/mmseqs_Windows/matrices/blosum45.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum50.out b/funvip/external/mmseqs_Windows/matrices/blosum50.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum50.out
rename to funvip/external/mmseqs_Windows/matrices/blosum50.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum55.out b/funvip/external/mmseqs_Windows/matrices/blosum55.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum55.out
rename to funvip/external/mmseqs_Windows/matrices/blosum55.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum60.out b/funvip/external/mmseqs_Windows/matrices/blosum60.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum60.out
rename to funvip/external/mmseqs_Windows/matrices/blosum60.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum62.out b/funvip/external/mmseqs_Windows/matrices/blosum62.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum62.out
rename to funvip/external/mmseqs_Windows/matrices/blosum62.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum65.out b/funvip/external/mmseqs_Windows/matrices/blosum65.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum65.out
rename to funvip/external/mmseqs_Windows/matrices/blosum65.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum70.out b/funvip/external/mmseqs_Windows/matrices/blosum70.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum70.out
rename to funvip/external/mmseqs_Windows/matrices/blosum70.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum75.out b/funvip/external/mmseqs_Windows/matrices/blosum75.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum75.out
rename to funvip/external/mmseqs_Windows/matrices/blosum75.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum80.out b/funvip/external/mmseqs_Windows/matrices/blosum80.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum80.out
rename to funvip/external/mmseqs_Windows/matrices/blosum80.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum85.out b/funvip/external/mmseqs_Windows/matrices/blosum85.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum85.out
rename to funvip/external/mmseqs_Windows/matrices/blosum85.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum90.out b/funvip/external/mmseqs_Windows/matrices/blosum90.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum90.out
rename to funvip/external/mmseqs_Windows/matrices/blosum90.out
diff --git a/funid/external/mmseqs_Windows/matrices/blosum95.out b/funvip/external/mmseqs_Windows/matrices/blosum95.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/blosum95.out
rename to funvip/external/mmseqs_Windows/matrices/blosum95.out
diff --git a/funid/external/mmseqs_Windows/matrices/nucleotide.out b/funvip/external/mmseqs_Windows/matrices/nucleotide.out
similarity index 100%
rename from funid/external/mmseqs_Windows/matrices/nucleotide.out
rename to funvip/external/mmseqs_Windows/matrices/nucleotide.out
diff --git a/funid/external/mmseqs_Windows/mmseqs.bat b/funvip/external/mmseqs_Windows/mmseqs.bat
similarity index 100%
rename from funid/external/mmseqs_Windows/mmseqs.bat
rename to funvip/external/mmseqs_Windows/mmseqs.bat
diff --git a/funid/external/mmseqs_Windows/util/bash-completion.sh b/funvip/external/mmseqs_Windows/util/bash-completion.sh
similarity index 100%
rename from funid/external/mmseqs_Windows/util/bash-completion.sh
rename to funvip/external/mmseqs_Windows/util/bash-completion.sh
diff --git a/funid/external/trimal.v1.4/trimAl/AUTHORS b/funvip/external/trimal.v1.4/trimAl/AUTHORS
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/AUTHORS
rename to funvip/external/trimal.v1.4/trimAl/AUTHORS
diff --git a/funid/external/trimal.v1.4/trimAl/CHANGELOG b/funvip/external/trimal.v1.4/trimAl/CHANGELOG
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/CHANGELOG
rename to funvip/external/trimal.v1.4/trimAl/CHANGELOG
diff --git a/funid/external/trimal.v1.4/trimAl/LICENSE b/funvip/external/trimal.v1.4/trimAl/LICENSE
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/LICENSE
rename to funvip/external/trimal.v1.4/trimAl/LICENSE
diff --git a/funid/external/trimal.v1.4/trimAl/README b/funvip/external/trimal.v1.4/trimAl/README
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/README
rename to funvip/external/trimal.v1.4/trimAl/README
diff --git a/funid/external/trimal.v1.4/trimAl/bin/libgcc_s_dw2-1.dll b/funvip/external/trimal.v1.4/trimAl/bin/libgcc_s_dw2-1.dll
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/bin/libgcc_s_dw2-1.dll
rename to funvip/external/trimal.v1.4/trimAl/bin/libgcc_s_dw2-1.dll
diff --git a/funid/external/trimal.v1.4/trimAl/bin/libstdc++-6.dll b/funvip/external/trimal.v1.4/trimAl/bin/libstdc++-6.dll
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/bin/libstdc++-6.dll
rename to funvip/external/trimal.v1.4/trimAl/bin/libstdc++-6.dll
diff --git a/funid/external/trimal.v1.4/trimAl/bin/trimal.exe b/funvip/external/trimal.v1.4/trimAl/bin/trimal.exe
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/bin/trimal.exe
rename to funvip/external/trimal.v1.4/trimAl/bin/trimal.exe
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/check_codon_alignments.py b/funvip/external/trimal.v1.4/trimAl/scripts/check_codon_alignments.py
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/check_codon_alignments.py
rename to funvip/external/trimal.v1.4/trimAl/scripts/check_codon_alignments.py
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/compare_trimmed_msas.sh b/funvip/external/trimal.v1.4/trimAl/scripts/compare_trimmed_msas.sh
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/compare_trimmed_msas.sh
rename to funvip/external/trimal.v1.4/trimAl/scripts/compare_trimmed_msas.sh
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/generateRandomAlignmentsUsingAsSeedRealAlignments.py b/funvip/external/trimal.v1.4/trimAl/scripts/generateRandomAlignmentsUsingAsSeedRealAlignments.py
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/generateRandomAlignmentsUsingAsSeedRealAlignments.py
rename to funvip/external/trimal.v1.4/trimAl/scripts/generateRandomAlignmentsUsingAsSeedRealAlignments.py
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/generate_trimmed_msas.sh b/funvip/external/trimal.v1.4/trimAl/scripts/generate_trimmed_msas.sh
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/generate_trimmed_msas.sh
rename to funvip/external/trimal.v1.4/trimAl/scripts/generate_trimmed_msas.sh
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/get_sequence_representative_from_alignment.py b/funvip/external/trimal.v1.4/trimAl/scripts/get_sequence_representative_from_alignment.py
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/get_sequence_representative_from_alignment.py
rename to funvip/external/trimal.v1.4/trimAl/scripts/get_sequence_representative_from_alignment.py
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/get_sequences_gaps_ratio.py b/funvip/external/trimal.v1.4/trimAl/scripts/get_sequences_gaps_ratio.py
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/get_sequences_gaps_ratio.py
rename to funvip/external/trimal.v1.4/trimAl/scripts/get_sequences_gaps_ratio.py
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/remove_shorter_sequences.py b/funvip/external/trimal.v1.4/trimAl/scripts/remove_shorter_sequences.py
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/remove_shorter_sequences.py
rename to funvip/external/trimal.v1.4/trimAl/scripts/remove_shorter_sequences.py
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/selective_trimming_for_dNdS_analyses.based_neighbours.py b/funvip/external/trimal.v1.4/trimAl/scripts/selective_trimming_for_dNdS_analyses.based_neighbours.py
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/selective_trimming_for_dNdS_analyses.based_neighbours.py
rename to funvip/external/trimal.v1.4/trimAl/scripts/selective_trimming_for_dNdS_analyses.based_neighbours.py
diff --git a/funid/external/trimal.v1.4/trimAl/scripts/set_manual_boundaries.py b/funvip/external/trimal.v1.4/trimAl/scripts/set_manual_boundaries.py
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/scripts/set_manual_boundaries.py
rename to funvip/external/trimal.v1.4/trimAl/scripts/set_manual_boundaries.py
diff --git a/funid/external/trimal.v1.4/trimAl/source/alignment.cpp b/funvip/external/trimal.v1.4/trimAl/source/alignment.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/alignment.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/alignment.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/alignment.h b/funvip/external/trimal.v1.4/trimAl/source/alignment.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/alignment.h
rename to funvip/external/trimal.v1.4/trimAl/source/alignment.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/alignment.o b/funvip/external/trimal.v1.4/trimAl/source/alignment.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/alignment.o
rename to funvip/external/trimal.v1.4/trimAl/source/alignment.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/autAlignment.cpp b/funvip/external/trimal.v1.4/trimAl/source/autAlignment.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/autAlignment.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/autAlignment.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/autAlignment.o b/funvip/external/trimal.v1.4/trimAl/source/autAlignment.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/autAlignment.o
rename to funvip/external/trimal.v1.4/trimAl/source/autAlignment.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/compareFiles.cpp b/funvip/external/trimal.v1.4/trimAl/source/compareFiles.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/compareFiles.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/compareFiles.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/compareFiles.h b/funvip/external/trimal.v1.4/trimAl/source/compareFiles.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/compareFiles.h
rename to funvip/external/trimal.v1.4/trimAl/source/compareFiles.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/compareFiles.o b/funvip/external/trimal.v1.4/trimAl/source/compareFiles.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/compareFiles.o
rename to funvip/external/trimal.v1.4/trimAl/source/compareFiles.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/defines.h b/funvip/external/trimal.v1.4/trimAl/source/defines.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/defines.h
rename to funvip/external/trimal.v1.4/trimAl/source/defines.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/main.cpp b/funvip/external/trimal.v1.4/trimAl/source/main.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/main.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/main.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/makefile b/funvip/external/trimal.v1.4/trimAl/source/makefile
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/makefile
rename to funvip/external/trimal.v1.4/trimAl/source/makefile
diff --git a/funid/external/trimal.v1.4/trimAl/source/makefile.MacOS b/funvip/external/trimal.v1.4/trimAl/source/makefile.MacOS
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/makefile.MacOS
rename to funvip/external/trimal.v1.4/trimAl/source/makefile.MacOS
diff --git a/funid/external/trimal.v1.4/trimAl/source/readAl.cpp b/funvip/external/trimal.v1.4/trimAl/source/readAl.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/readAl.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/readAl.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/readal.exe b/funvip/external/trimal.v1.4/trimAl/source/readal.exe
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/readal.exe
rename to funvip/external/trimal.v1.4/trimAl/source/readal.exe
diff --git a/funid/external/trimal.v1.4/trimAl/source/rwAlignment.cpp b/funvip/external/trimal.v1.4/trimAl/source/rwAlignment.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/rwAlignment.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/rwAlignment.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/rwAlignment.o b/funvip/external/trimal.v1.4/trimAl/source/rwAlignment.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/rwAlignment.o
rename to funvip/external/trimal.v1.4/trimAl/source/rwAlignment.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/sequencesMatrix.cpp b/funvip/external/trimal.v1.4/trimAl/source/sequencesMatrix.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/sequencesMatrix.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/sequencesMatrix.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/sequencesMatrix.h b/funvip/external/trimal.v1.4/trimAl/source/sequencesMatrix.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/sequencesMatrix.h
rename to funvip/external/trimal.v1.4/trimAl/source/sequencesMatrix.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/sequencesMatrix.o b/funvip/external/trimal.v1.4/trimAl/source/sequencesMatrix.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/sequencesMatrix.o
rename to funvip/external/trimal.v1.4/trimAl/source/sequencesMatrix.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/similarityMatrix.cpp b/funvip/external/trimal.v1.4/trimAl/source/similarityMatrix.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/similarityMatrix.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/similarityMatrix.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/similarityMatrix.h b/funvip/external/trimal.v1.4/trimAl/source/similarityMatrix.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/similarityMatrix.h
rename to funvip/external/trimal.v1.4/trimAl/source/similarityMatrix.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/similarityMatrix.o b/funvip/external/trimal.v1.4/trimAl/source/similarityMatrix.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/similarityMatrix.o
rename to funvip/external/trimal.v1.4/trimAl/source/similarityMatrix.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/statAl.cpp b/funvip/external/trimal.v1.4/trimAl/source/statAl.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statAl.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/statAl.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/statal.exe b/funvip/external/trimal.v1.4/trimAl/source/statal.exe
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statal.exe
rename to funvip/external/trimal.v1.4/trimAl/source/statal.exe
diff --git a/funid/external/trimal.v1.4/trimAl/source/statisticsConservation.cpp b/funvip/external/trimal.v1.4/trimAl/source/statisticsConservation.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statisticsConservation.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/statisticsConservation.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/statisticsConservation.h b/funvip/external/trimal.v1.4/trimAl/source/statisticsConservation.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statisticsConservation.h
rename to funvip/external/trimal.v1.4/trimAl/source/statisticsConservation.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/statisticsConservation.o b/funvip/external/trimal.v1.4/trimAl/source/statisticsConservation.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statisticsConservation.o
rename to funvip/external/trimal.v1.4/trimAl/source/statisticsConservation.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/statisticsFiles.cpp b/funvip/external/trimal.v1.4/trimAl/source/statisticsFiles.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statisticsFiles.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/statisticsFiles.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/statisticsFiles.h b/funvip/external/trimal.v1.4/trimAl/source/statisticsFiles.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statisticsFiles.h
rename to funvip/external/trimal.v1.4/trimAl/source/statisticsFiles.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/statisticsGaps.cpp b/funvip/external/trimal.v1.4/trimAl/source/statisticsGaps.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statisticsGaps.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/statisticsGaps.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/statisticsGaps.h b/funvip/external/trimal.v1.4/trimAl/source/statisticsGaps.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statisticsGaps.h
rename to funvip/external/trimal.v1.4/trimAl/source/statisticsGaps.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/statisticsGaps.o b/funvip/external/trimal.v1.4/trimAl/source/statisticsGaps.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/statisticsGaps.o
rename to funvip/external/trimal.v1.4/trimAl/source/statisticsGaps.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/utils.cpp b/funvip/external/trimal.v1.4/trimAl/source/utils.cpp
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/utils.cpp
rename to funvip/external/trimal.v1.4/trimAl/source/utils.cpp
diff --git a/funid/external/trimal.v1.4/trimAl/source/utils.h b/funvip/external/trimal.v1.4/trimAl/source/utils.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/utils.h
rename to funvip/external/trimal.v1.4/trimAl/source/utils.h
diff --git a/funid/external/trimal.v1.4/trimAl/source/utils.o b/funvip/external/trimal.v1.4/trimAl/source/utils.o
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/utils.o
rename to funvip/external/trimal.v1.4/trimAl/source/utils.o
diff --git a/funid/external/trimal.v1.4/trimAl/source/values.h b/funvip/external/trimal.v1.4/trimAl/source/values.h
similarity index 100%
rename from funid/external/trimal.v1.4/trimAl/source/values.h
rename to funvip/external/trimal.v1.4/trimAl/source/values.h
diff --git a/funid/main.py b/funvip/main.py
similarity index 62%
rename from funid/main.py
rename to funvip/main.py
index 86bd0e6..7d0ae40 100644
--- a/funid/main.py
+++ b/funvip/main.py
@@ -1,27 +1,30 @@
 def main():
-    from funid.src import align
-    from funid.src import tree_interpretation_pipe
-    from funid.src import cluster
-    from funid.src import dataset
-    from funid.src import ext
-    from funid.src import hasher
-    from funid.src import initialize
-    from funid.src import logger
-    from funid.src import manage_input
-    from funid.src import modeltest
-    from funid.src import concatenate
-    from funid.src import patch
-    from funid.src import reporter
-    from funid.src import tool
-    from funid.src import save
-    from funid.src import search
-    from funid.src import tree
-    from funid.src import trim
-    from funid.src.command import CommandParser
-    from funid.src.tool import index_step
-    from funid.src.opt_generator import opt_generator
+    from funvip.src import align
+    from funvip.src import tree_interpretation_pipe
+    from funvip.src import cluster
+    from funvip.src import dataset
+    from funvip.src import ext
+    from funvip.src import hasher
+    from funvip.src import initialize
+    from funvip.src import logger
+    from funvip.src import validate_input
+    from funvip.src import modeltest
+    from funvip.src import concatenate
+    from funvip.src import patch
+    from funvip.src import reporter
+    from funvip.src import tool
+    from funvip.src import save
+    from funvip.src import search
+    from funvip.src import tree
+    from funvip.src import trim
+    from funvip.src.command import CommandParser
+    from funvip.src.tool import index_step
+    from funvip.src.opt_generator import opt_generator
+    from funvip.src.version import Version
     from time import time
+    from time import sleep
     import pandas as pd
+    import subprocess
     import multiprocessing as mp
     import os
     import shelve
@@ -32,8 +35,14 @@ def main():
     import logging
     import PyQt5
 
-    # Patch library errors, currently for ete3
-    # patch.patch()
+    # To initialize logger to print ANSI color
+    _ = subprocess.call("", shell=True)
+
+    if "FunID" in sys.argv[0]:
+        print(
+            "\x1b[92m The name of the software changed to 'FunVIP'. Please use 'FunVIP' when citation. \x1b[0m"
+        )
+        sleep(3)
 
     # For starting time stamp
     time_start = time()
@@ -54,27 +63,13 @@ def main():
     )
     tool.initialize_path(path)
 
-    # setup logging
-    logging.basicConfig(
-        format="%(asctime)s [%(levelname)s] %(message)s",
-        datefmt="%m/%d/%Y %I:%M:%S %p",
-        encoding="utf-8",
-        level=tool.get_level(opt.verbose),
-        handlers=[logging.FileHandler(path.log), logging.StreamHandler()],
-    )
-
-    # Delayed logging for option parsing
-    for info in list_info:
-        logging.info(info)
+    # Initialize version management
+    version = Version(opt, path)
 
-    for warning in list_warning:
-        logging.warning(warning)
+    logger.setup_logging(list_info, list_warning, list_error, path, opt, tool)
 
-    for error in list_error:
-        logging.error(error)
-
-    # V contains all intermediate variables for FunID Run
-    V = dataset.FunID_var()
+    # V contains all intermediate variables for FunVIP Run
+    V = dataset.FunVIP_var()
     # R includes all reporting objects such as warnings, errors, statistics
     R = reporter.Report()
 
@@ -90,6 +85,10 @@ def main():
         if "model_dict" in var:
             model_dict = var["model_dict"]
 
+    # To deal with location changes when rerun
+    path_root = f"{os.getcwd()}/{opt.runname}"
+    path.init_workspace(path_root, opt)
+
     ##########################################################################
     #     End of initializing blocks should not be moved for function!!!     #
     ##########################################################################
@@ -106,12 +105,21 @@ def main():
         # save metadata input to report
         # R.initialize_metadata(opt)
         # get input data
-        V, R, opt = manage_input.data_input(V, R, opt, path)
+        V, R, opt, GenMine_flag = validate_input.data_input(V, R, opt, path)
 
         # get possible genus list for recognizing genus name
         V = initialize.get_genus_list(V, opt, path)
 
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
+
+        # logging works well from here
 
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
@@ -130,9 +138,16 @@ def main():
         V = concatenate.concatenate_df(V, path, opt)
 
         # Update gene assignment of query sequence inputs
-        manage_input.update_queryfile(V, path, opt)
-
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        validate_input.update_queryfile(V, path, opt)
+
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         time_search = time()
@@ -153,7 +168,7 @@ def main():
         # Append query column to concatenated_df as query assigned
         # V = cluster.append_concatenated_query_group(V)
 
-        # Both gene and group was assigned, dataset was confirmed
+        # Both gene and group was assigned, dataset are confirmed
         V.generate_dataset(opt)
 
         # Appending outgroup
@@ -168,7 +183,14 @@ def main():
         # Save dataset to outgroups
         V.save_dataset(path, opt)
 
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         time_cluster = time()
@@ -179,7 +201,14 @@ def main():
         logging.info("ALIGNMENT")
         V, path, opt = align.pipe_alignment(V, path, opt)
 
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         time_align = time()
@@ -190,8 +219,15 @@ def main():
         logging.info("TRIMMING")
         V, path, opt = trim.pipe_trimming(V, path, opt)
         # Alignment validations - whether some of the sequences does not have overlapping regions
-        V.validate_alignments(V=V, path=path, opt=opt)
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        V.validate_alignments(path=path, opt=opt)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         time_trim = time()
@@ -204,7 +240,14 @@ def main():
         # Multigene
         V = concatenate.combine_alignment(V, opt, path)
 
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         time_concatenate = time()
@@ -217,7 +260,14 @@ def main():
         # most of the modeltest algorithms are well working with multiprocessing
         model_dict = modeltest.modeltest(V, path, opt)
 
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         time_modeltest = time()
@@ -230,14 +280,28 @@ def main():
         # Build phylogenetic trees
         V, path, opt = tree.pipe_tree(V, path, opt, model_dict)
 
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         # move tree and image files
         tool.cleanup_tree(path)
         tool.cleanup_tree_image(path)
 
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         time_tree = time()
@@ -251,13 +315,18 @@ def main():
         # Running visualization
         V, path, opt = tree_interpretation_pipe.pipe_tree_interpretation(V, path, opt)
 
-        # raise Exception
-
         # move tree image files
         tool.cleanup_tree(path)
         tool.cleanup_tree_image(path)
 
-        R.update_report(V=V, path=path, opt=opt, step=step)
+        R.update_report(
+            V=V,
+            path=path,
+            opt=opt,
+            step=step,
+            version=version,
+            GenMine_flag=GenMine_flag,
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=dir())
 
         time_visualize = time()
@@ -270,53 +339,64 @@ def main():
         # Reporting
         V.homogenize_dataset()
 
-        R.update_report(V, path, opt, step=step)
+        # Check inconsistent
+        V.check_inconsistent()
+
+        R.update_report(
+            V, path, opt, step=step, version=version, GenMine_flag=GenMine_flag
+        )
         save.save_session(opt=opt, path=path, global_var=locals(), var=vars())
 
         time_end = time()
-        logging.info(f"FunID ended in {time_end - time_start}")
+        logging.info(f"FunVIP ended in {round(time_end - time_start,3)}s")
 
         try:
-            logging.info(f"Time setup : {time_setup-time_start}s")
+            logging.info(f"Time input validation : {round(time_setup-time_start,3)}s")
         except:
             logging.warning(f"Failed logging initialization time")
 
         try:
-            logging.info(f"Time search : {time_search-time_setup}s")
+            logging.info(f"Time search : {round(time_search-time_setup,3)}s")
         except:
             logging.warning(f"Failed logging search time")
 
         try:
-            logging.info(f"Time cluster : {time_cluster-time_search}s")
+            logging.info(f"Time cluster : {round(time_cluster-time_search,3)}s")
         except:
             logging.warning(f"Failed logging cluster time")
 
         try:
-            logging.info(f"Time alignment : {time_align-time_cluster}s")
+            logging.info(f"Time alignment : {round(time_align-time_cluster,3)}s")
         except:
             logging.warning(f"Failed logging alignment time")
 
         try:
-            logging.info(f"Time trimming : {time_trim-time_align}s")
+            logging.info(f"Time trimming : {round(time_trim-time_align,3)}s")
         except:
             logging.warning(f"Failed logging trimming time")
 
         try:
-            logging.info(f"Time concatenatation : {time_concatenate-time_trim}s")
+            logging.info(
+                f"Time concatenatation : {round(time_concatenate-time_trim,3)}s"
+            )
         except:
             logging.warning(f"Failed logging concatenation time")
 
         try:
-            logging.info(f"Time tree construction : {time_tree-time_concatenate}s")
+            logging.info(
+                f"Time tree construction : {round(time_tree-time_concatenate,3)}s"
+            )
         except:
             logging.warning(f"Failed logging tree construction time")
 
         try:
-            logging.info(f"Time tree interpretation : {time_visualize-time_tree}s")
+            logging.info(
+                f"Time tree interpretation : {round(time_visualize-time_tree,3)}s"
+            )
         except:
             logging.warning(f"Failed logging tree interpretation time")
 
         try:
-            logging.info(f"Time report generation: {time_end-time_visualize}s")
+            logging.info(f"Time report generation: {round(time_end-time_visualize,3)}s")
         except:
             logging.warning(f"Failed logging reoprt generation time")
diff --git a/funid/meta.yaml b/funvip/meta.yaml
similarity index 100%
rename from funid/meta.yaml
rename to funvip/meta.yaml
diff --git a/funid/metal.yaml b/funvip/metal.yaml
similarity index 100%
rename from funid/metal.yaml
rename to funvip/metal.yaml
diff --git a/funid/package-lock.json b/funvip/package-lock.json
similarity index 100%
rename from funid/package-lock.json
rename to funvip/package-lock.json
diff --git a/funid/package.json b/funvip/package.json
similarity index 100%
rename from funid/package.json
rename to funvip/package.json
diff --git a/funid/preset/.gitignore b/funvip/preset/.gitignore
similarity index 100%
rename from funid/preset/.gitignore
rename to funvip/preset/.gitignore
diff --git a/funid/preset/accurate.yaml b/funvip/preset/accurate.yaml
similarity index 100%
rename from funid/preset/accurate.yaml
rename to funvip/preset/accurate.yaml
diff --git a/funid/preset/fast.yaml b/funvip/preset/fast.yaml
similarity index 100%
rename from funid/preset/fast.yaml
rename to funvip/preset/fast.yaml
diff --git a/funid/src/.gitignore b/funvip/src/.gitignore
similarity index 100%
rename from funid/src/.gitignore
rename to funvip/src/.gitignore
diff --git a/funid/src/__init__.py b/funvip/src/__init__.py
similarity index 100%
rename from funid/src/__init__.py
rename to funvip/src/__init__.py
diff --git a/funid/src/__pycache__/CATV_pipe.cpython-310.pyc b/funvip/src/__pycache__/CATV_pipe.cpython-310.pyc
similarity index 100%
rename from funid/src/__pycache__/CATV_pipe.cpython-310.pyc
rename to funvip/src/__pycache__/CATV_pipe.cpython-310.pyc
diff --git a/funid/src/__pycache__/CAT_V.cpython-310.pyc b/funvip/src/__pycache__/CAT_V.cpython-310.pyc
similarity index 100%
rename from funid/src/__pycache__/CAT_V.cpython-310.pyc
rename to funvip/src/__pycache__/CAT_V.cpython-310.pyc
diff --git a/funid/src/__pycache__/CAT_V.cpython-39.pyc b/funvip/src/__pycache__/CAT_V.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/CAT_V.cpython-39.pyc
rename to funvip/src/__pycache__/CAT_V.cpython-39.pyc
diff --git a/funid/src/__pycache__/CAT_V_pipe.cpython-39.pyc b/funvip/src/__pycache__/CAT_V_pipe.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/CAT_V_pipe.cpython-39.pyc
rename to funvip/src/__pycache__/CAT_V_pipe.cpython-39.pyc
diff --git a/funid/src/__pycache__/__init__.cpython-310.pyc b/funvip/src/__pycache__/__init__.cpython-310.pyc
similarity index 100%
rename from funid/src/__pycache__/__init__.cpython-310.pyc
rename to funvip/src/__pycache__/__init__.cpython-310.pyc
diff --git a/funid/src/__pycache__/__init__.cpython-39.pyc b/funvip/src/__pycache__/__init__.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/__init__.cpython-39.pyc
rename to funvip/src/__pycache__/__init__.cpython-39.pyc
diff --git a/funid/src/__pycache__/align.cpython-310.pyc b/funvip/src/__pycache__/align.cpython-310.pyc
similarity index 100%
rename from funid/src/__pycache__/align.cpython-310.pyc
rename to funvip/src/__pycache__/align.cpython-310.pyc
diff --git a/funid/src/__pycache__/cluster.cpython-39.pyc b/funvip/src/__pycache__/cluster.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/cluster.cpython-39.pyc
rename to funvip/src/__pycache__/cluster.cpython-39.pyc
diff --git a/funid/src/__pycache__/dataset.cpython-39.pyc b/funvip/src/__pycache__/dataset.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/dataset.cpython-39.pyc
rename to funvip/src/__pycache__/dataset.cpython-39.pyc
diff --git a/funid/src/__pycache__/ext.cpython-310.pyc b/funvip/src/__pycache__/ext.cpython-310.pyc
similarity index 100%
rename from funid/src/__pycache__/ext.cpython-310.pyc
rename to funvip/src/__pycache__/ext.cpython-310.pyc
diff --git a/funid/src/__pycache__/ext.cpython-39.pyc b/funvip/src/__pycache__/ext.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/ext.cpython-39.pyc
rename to funvip/src/__pycache__/ext.cpython-39.pyc
diff --git a/funid/src/__pycache__/hasher.cpython-39.pyc b/funvip/src/__pycache__/hasher.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/hasher.cpython-39.pyc
rename to funvip/src/__pycache__/hasher.cpython-39.pyc
diff --git a/funid/src/__pycache__/initialize.cpython-39.pyc b/funvip/src/__pycache__/initialize.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/initialize.cpython-39.pyc
rename to funvip/src/__pycache__/initialize.cpython-39.pyc
diff --git a/funid/src/__pycache__/io.cpython-310.pyc b/funvip/src/__pycache__/io.cpython-310.pyc
similarity index 100%
rename from funid/src/__pycache__/io.cpython-310.pyc
rename to funvip/src/__pycache__/io.cpython-310.pyc
diff --git a/funid/src/__pycache__/io.cpython-39.pyc b/funvip/src/__pycache__/io.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/io.cpython-39.pyc
rename to funvip/src/__pycache__/io.cpython-39.pyc
diff --git a/funid/src/__pycache__/logger.cpython-39.pyc b/funvip/src/__pycache__/logger.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/logger.cpython-39.pyc
rename to funvip/src/__pycache__/logger.cpython-39.pyc
diff --git a/funid/src/__pycache__/multigene.cpython-39.pyc b/funvip/src/__pycache__/multigene.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/multigene.cpython-39.pyc
rename to funvip/src/__pycache__/multigene.cpython-39.pyc
diff --git a/funid/src/__pycache__/ncbi.cpython-39.pyc b/funvip/src/__pycache__/ncbi.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/ncbi.cpython-39.pyc
rename to funvip/src/__pycache__/ncbi.cpython-39.pyc
diff --git a/funid/src/__pycache__/opt_generator.cpython-39.pyc b/funvip/src/__pycache__/opt_generator.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/opt_generator.cpython-39.pyc
rename to funvip/src/__pycache__/opt_generator.cpython-39.pyc
diff --git a/funid/src/__pycache__/reporter.cpython-39.pyc b/funvip/src/__pycache__/reporter.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/reporter.cpython-39.pyc
rename to funvip/src/__pycache__/reporter.cpython-39.pyc
diff --git a/funid/src/__pycache__/search.cpython-39.pyc b/funvip/src/__pycache__/search.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/search.cpython-39.pyc
rename to funvip/src/__pycache__/search.cpython-39.pyc
diff --git a/funid/src/__pycache__/tool.cpython-39.pyc b/funvip/src/__pycache__/tool.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/tool.cpython-39.pyc
rename to funvip/src/__pycache__/tool.cpython-39.pyc
diff --git a/funid/src/__pycache__/validation.cpython-39.pyc b/funvip/src/__pycache__/validation.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/validation.cpython-39.pyc
rename to funvip/src/__pycache__/validation.cpython-39.pyc
diff --git a/funid/src/__pycache__/visualize.cpython-39.pyc b/funvip/src/__pycache__/visualize.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/visualize.cpython-39.pyc
rename to funvip/src/__pycache__/visualize.cpython-39.pyc
diff --git a/funid/src/align.py b/funvip/src/align.py
similarity index 98%
rename from funid/src/align.py
rename to funvip/src/align.py
index 5d1877b..6b5979d 100644
--- a/funid/src/align.py
+++ b/funvip/src/align.py
@@ -1,5 +1,5 @@
-from funid.src import ext
-from funid.src.opt_generator import opt_generator
+from funvip.src import ext
+from funvip.src.opt_generator import opt_generator
 import logging
 from Bio import SeqIO
 import multiprocessing as mp
diff --git a/funid/src/cluster.py b/funvip/src/cluster.py
similarity index 96%
rename from funid/src/cluster.py
rename to funvip/src/cluster.py
index 0690aac..0d756ab 100644
--- a/funid/src/cluster.py
+++ b/funvip/src/cluster.py
@@ -7,12 +7,12 @@
 from functools import lru_cache
 from time import sleep
 
-from Bio.Blast import NCBIXML
+# from Bio.Blast import NCBIXML
 from Bio import SeqIO
 
 import logging
 import gc
-from funid.src.ext import mmseqs
+from funvip.src.ext import mmseqs
 
 
 # return list of original group of given FI
@@ -29,6 +29,11 @@ def append_query_group(V):
 
     V.cSR["query_group"] = V.cSR["qseqid"].apply(lambda x: group_dict.get(x))
 
+    # Indicate queries without any corresponding group
+    for FI in V.list_FI:
+        if FI.adjusted_group == "" and not ("noseq" in FI.issues):
+            FI.issues.add("nodb")
+
     return V
 
 
@@ -247,7 +252,7 @@ def append_outgroup(V, df_search, gene, group, path, opt):
         cutoff_df = df_search[df_search["bitscore"] > 0]
         cutoff_df = cutoff_df[cutoff_df["subject_group"] != group]
 
-    # Garbage collection for lower memory consumption in this process
+    # Garbage collection to reduce memory consumption in this process
     del df_search
     gc.collect()
 
@@ -480,6 +485,15 @@ def pipe_append_outgroup(V, path, opt):
             V.dict_dataset[group][gene].list_og_FI = outgroup
             # Add ambiguous group to FI
             V.dict_dataset[group][gene].list_db_FI += ambiguous_group
+            # Add outgroup and db in to dict_hash_FI
+
+            # Should find out why this does not work at the end
+            """
+            for FI in outgroup + ambiguous_group:
+                # If already in dict_hash_FI, they have priority
+                if not (FI.hash in V.dict_hash_FI):
+                    V.dict_hash_FI[FI.hash] = FI
+            """
 
     groups = deepcopy(list(V.dict_dataset.keys()))
 
diff --git a/funid/src/command.py b/funvip/src/command.py
similarity index 91%
rename from funid/src/command.py
rename to funvip/src/command.py
index d53398e..f9d66e4 100644
--- a/funid/src/command.py
+++ b/funvip/src/command.py
@@ -1,12 +1,13 @@
-# FunID_dev/FunID/src/command.py
+# FunVIP_dev/FunVIP/src/command.py
 
 import argparse
+from importlib.metadata import version
 
 
 class CommandParser:
     def __init__(self) -> None:
         self.parser = argparse.ArgumentParser(
-            description="Fungal Identification Pipeline", prog="FunID"
+            description="Fungal Validation & Identification Pipeline", prog="FunVIP"
         )
 
     def get_args(self) -> argparse.Namespace:
@@ -15,20 +16,24 @@ def get_args(self) -> argparse.Namespace:
             title="required", description="Main options"
         )
         group_required.add_argument(
-            "--query", "-q", nargs="*", help="Query fasta or table files", type=str
+            "--query",
+            "-q",
+            nargs="*",
+            help="Query fasta or table files, delimitated by space",
+            type=str,
         )
         group_required.add_argument(
             "--db",
             "-d",
             nargs="+",
-            help="Database table files",
+            help="Database table files, delimitated by space",
             type=str,
         )
         group_required.add_argument(
             "--gene", "-g", nargs="*", help="Gene names to be analyzed", type=str
         )
 
-        # Mandatory options - ncbi notations
+        # Mandatory when NCBI download - raise Exception when manage input
         group_ncbi = self.parser.add_mutually_exclusive_group(required=False)
         group_ncbi.add_argument(
             "--email",
@@ -101,7 +106,7 @@ def get_args(self) -> argparse.Namespace:
         group_run.add_argument(
             "--all",
             action="store_true",
-            help="Run FunID for all database sequences, regardless of corrresponding sequences exists in query, default : False",
+            help="Run FunVIP for all database sequences, regardless of corrresponding sequences exists in query, default : False",
         )
         group_run.add_argument(
             "--confident",
@@ -279,7 +284,7 @@ def get_args(self) -> argparse.Namespace:
         group_advanced.add_argument(
             "--allow-innertrimming",
             dest="allow_innertrimming",
-            help="Turn off FunID adjustment to not to trim inner alignment columns",
+            help="Turn off FunVIP adjustment to not to trim inner alignment columns",
             action="store_true",
         )
         group_advanced.add_argument(
@@ -319,10 +324,18 @@ def get_args(self) -> argparse.Namespace:
         group_save = self.parser.add_argument_group(
             title="save", description="Run saving options"
         )
+        # Compatibility test required for this one
+        """
         group_save.add_argument(
-            "--matrixformat",
-            help="Default format for search matrix files, [csv, xlsx, parquet, feather], default : csv",
+            "--tableformat",
+            help="Default format for table files, [csv, xlsx, parquet, feather], default : csv",
         )
+        """
+        group_save.add_argument(
+            "--tableformat",
+            help="Default format for table files, [csv, xlsx], default : csv",
+        )
+
         group_save.add_argument(
             "--nosearchresult",
             action="store_true",
@@ -339,4 +352,9 @@ def get_args(self) -> argparse.Namespace:
             type=str,
         )
 
+        # version
+        self.parser.add_argument(
+            "--version", action="version", version=f"FunVIP {version('FunVIP')}"
+        )
+
         return self.parser.parse_args()
diff --git a/funid/src/concatenate.py b/funvip/src/concatenate.py
similarity index 95%
rename from funid/src/concatenate.py
rename to funvip/src/concatenate.py
index 9aa6e03..e2f71c7 100644
--- a/funid/src/concatenate.py
+++ b/funvip/src/concatenate.py
@@ -6,7 +6,7 @@
 from functools import reduce
 import pandas as pd
 from copy import deepcopy
-from funid.src import search, hasher
+from funvip.src import search, hasher
 from scipy.optimize import minimize
 import numpy as np
 import shutil
@@ -27,6 +27,8 @@ def combine_alignment(V, opt, path):
 
                 for gene in V.dict_dataset[group]:
                     if not (gene == "concatenated"):
+                        gene_list.append(gene)
+
                         fasta_list = list(
                             SeqIO.parse(
                                 f"{path.out_alignment}/{opt.runname}_trimmed_{group}_{gene}.fasta",
@@ -61,13 +63,17 @@ def combine_alignment(V, opt, path):
                                 seq_dict[gene][seq.description] = seq
                                 hash_set.add(seq.description)
 
+                # Save partition information
+                V.partition[group] = {"len": len_dict, "order": gene_list}
+
                 # Generate partition file
                 with open(
                     f"{path.out_alignment}/{opt.runname}_{group}.partition", "w"
                 ) as fw:
                     tot_len = 0
-                    for gene in sorted(len_dict.keys()):
-                        gene_list.append(gene)
+                    print(gene_list)
+                    for gene in gene_list:
+                        # gene_list.append(gene)
                         fw.write(f"DNA, {gene}= {tot_len+1}-{tot_len+len_dict[gene]}\n")
                         tot_len += len_dict[gene]
 
@@ -118,6 +124,12 @@ def combine_alignment(V, opt, path):
                 ) as fw:
                     fw.write(f"DNA, {singlegene}= 1-{gene_length}\n")
 
+                # Save partition information
+                V.partition[group] = {
+                    "len": {singlegene: gene_length},
+                    "order": [singlegene],
+                }
+
             else:
                 logging.error(f"V.dict_dataset {group}: {V.dict_dataset[group]}")
                 logging.error(
@@ -337,8 +349,8 @@ def apply_prediction(row, gene_list, coeff, grad):
     if opt.nosearchresult is False:
         search.save_df(
             hasher.decode_df(hash_dict=V.dict_id_hash, df=V.cSR),
-            f"{path.out_matrix}/{opt.runname}_BLAST_result_concatenated.{opt.matrixformat}",
-            fmt=opt.matrixformat,
+            f"{path.out_matrix}/{opt.runname}_BLAST_result_concatenated.{opt.tableformat}",
+            fmt=opt.tableformat,
         )
 
     return V
diff --git a/funid/src/dataset.py b/funvip/src/dataset.py
similarity index 90%
rename from funid/src/dataset.py
rename to funvip/src/dataset.py
index 27422e3..1e150ae 100644
--- a/funid/src/dataset.py
+++ b/funvip/src/dataset.py
@@ -1,5 +1,5 @@
-from funid.src import save
-from funid.src import hasher
+from funvip.src import save
+from funvip.src import hasher
 from Bio import SeqIO
 import os
 import sys
@@ -26,12 +26,12 @@ def __init__(self, gene, group, list_qr, list_db, list_og):
         self.list_og_FI = list_og
 
     def __repr__(self):
-        return f"<< FunID_Dataset object >> Gene: {self.gene} | Group: {self.group} | Query: {len(self.list_qr_FI)} | DB: {len(self.list_db_FI)} | Outgroup: {len(self.list_og_FI)}\n"
+        return f"<< FunVIP_Dataset object >> Gene: {self.gene} | Group: {self.group} | Query: {len(self.list_qr_FI)} | DB: {len(self.list_db_FI)} | Outgroup: {len(self.list_og_FI)}\n"
 
 
-### FunID all dataset variables bundle
+### FunVIP all dataset variables bundle
 # Called as "V" in main pipeline
-class FunID_var:
+class FunVIP_var:
     def __init__(self):
         # dataset class to manage datasets used for run
         # FI : funinfo
@@ -94,11 +94,15 @@ def __init__(self):
         # multigene_list
         self.multigene_list = []
 
+        # Partition information by dataset
+        # group : {"len" : dict, "order" : list} format
+        self.partition = {}
+
     def __repr__(self):
         out_dict = {}
         for key in self.dict_dataset:
             out_dict[key] = len(self.dict_dataset[key])
-        return f"<< FunID_var object >>\nNumber of FI: {len(self.list_FI)}\nDB genes:{self.list_db_gene}\nQuery genes:{self.list_qr_gene}\ngroups:{self.list_group}\nDataset_dict:{json.dumps(out_dict, indent=2)}"
+        return f"<< FunVIP_var object >>\nNumber of FI: {len(self.list_FI)}\nDB genes:{self.list_db_gene}\nQuery genes:{self.list_qr_gene}\ngroups:{self.list_group}\nDataset_dict:{json.dumps(out_dict, indent=2)}"
 
     def add_dataset(self, group, gene, list_qr, list_db, list_og):
         data = Dataset(group, gene, list_qr, list_db, list_og)
@@ -353,7 +357,7 @@ def homogenize_dataset(self):
                         FI.adjusted_group = self.dict_hash_FI[h].group
                         self.dict_hash_FI[h].adjusted_group = self.dict_hash_FI[h].group
                     else:
-                        logging.error(f"{FI.id} does not have assigned group!")
+                        logging.warning(f"{FI.id} does not have assigned group!")
 
                 # bygene_species
                 if FI.bygene_species != self.dict_hash_FI[h].bygene_species:
@@ -428,11 +432,11 @@ def save_dataset(self, path, opt):
                         )
 
     # Validate if any multiple sequence alignment has no overlapping region
-    def validate_alignments(self, V, path, opt):
+    def validate_alignments(self, path, opt):
         fail_list = []
 
         remove_dict = {}
-        tree_hash_dict = hasher.encode(V.list_FI, newick=True)
+        tree_hash_dict = hasher.encode(self.list_FI, newick=True)
         for group in self.dict_dataset:
             remove_dict[group] = {}
             for gene in self.dict_dataset[group]:
@@ -563,6 +567,15 @@ def validate_alignments(self, V, path, opt):
             self.dict_dataset[group].pop(gene)
             logging.warning(f"Alignment for {group} {gene} has removed from analysis")
 
+        # Add issue
+        for fail in fail_list:
+            for FI in self.list_FI:
+                if FI.adjusted_group == fail[0]:
+                    if FI.seq[fail[1]] != "":
+                        FI.issues.add(f"alignfail:{fail[1]}")
+
+        print(fail_list)
+
         logging.debug("Remove dict")
         logging.debug(remove_dict)
 
@@ -642,7 +655,7 @@ def validate_alignments(self, V, path, opt):
             gene = fail[1]
             self.dict_dataset[group].pop(gene)
 
-        # If concatenated is only left dataset, remove entire group
+        # If concatenated is the only left dataset, remove entire group
         group_pop_list = []
         for group in self.dict_dataset:
             if len(self.dict_dataset[group].keys()) == 0:
@@ -658,3 +671,46 @@ def validate_alignments(self, V, path, opt):
             logging.warning(
                 f"No alignment left for group {group}, removed from analysis"
             )
+
+        # Add issue: the number of sequences are insufficient
+        for fail in final_fail_list:
+            for FI in V.list_FI:
+                if FI.adjusted_group == fail[0]:
+                    if FI.seq[fail[1]] != "":
+                        FI.issues.add(f"lackseq")
+
+        # return V
+
+    # check inconsistency exists along identification result of each genes
+    def check_inconsistent(self):
+        set_gene = set(self.list_db_gene + self.list_qr_gene)
+
+        for _hash in self.dict_hash_FI:
+            FI = self.dict_hash_FI[_hash]
+
+            # Collect result from only used sequences
+            if FI.adjusted_group in self.dict_dataset:
+                # If any of appropriate gene used
+                if any(
+                    key in self.dict_dataset[FI.adjusted_group] for key in FI.seq.keys()
+                ):
+                    inconsistent_flag = 0
+
+                    for gene in set_gene:
+                        # Check if data analysis had performed for specific FI, group, gene combination
+                        if (
+                            gene in FI.bygene_species
+                            and len(FI.seq[gene]) > 0
+                            and gene in self.dict_dataset[FI.adjusted_group]
+                        ):
+                            # Check inconsistent identification across genes
+                            if not (
+                                any(
+                                    _sp in FI.final_species
+                                    for _sp in FI.bygene_species[gene].split("/")
+                                )
+                            ):
+                                inconsistent_flag = 1
+
+                    if inconsistent_flag == 1:
+                        FI.issues.add("inconsistent")
diff --git a/funid/src/ext.py b/funvip/src/ext.py
similarity index 96%
rename from funid/src/ext.py
rename to funvip/src/ext.py
index d2fd265..4a959b4 100644
--- a/funid/src/ext.py
+++ b/funvip/src/ext.py
@@ -7,8 +7,8 @@
 import psutil
 from copy import deepcopy
 from pathlib import Path
-from funid.src.save import save_tree
-from funid.src.tool import mkdir
+from funvip.src.save import save_tree
+from funvip.src.tool import mkdir
 
 
 # Search methods
@@ -96,7 +96,7 @@ def makeblastdb(fasta, db, path):
         return_code = subprocess.call(CMD, shell=True)
 
         if return_code != 0:
-            logging.error(f"[ERROR] Make blast_db failed!!")
+            logging.error(f"Make blast_db failed!!")
             install_flag = 1
 
         # Change db names
@@ -292,7 +292,7 @@ def ModelFinder(fasta, opt, path, thread):
     if platform == "win32":
         if " " in fasta:
             fasta = f'"{fasta}"'
-        CMD = f"{path.sys_path}/external/iqtree-2.1.3-Windows/bin/iqtree2.exe --seqtype DNA -s {fasta} {model_term} -merit {opt.criterion} -T {thread} -mem {opt.memory}"
+        CMD = f"{path.sys_path}/external/iqtree/bin/iqtree2.exe --seqtype DNA -s {fasta} {model_term} -merit {opt.criterion} -T {thread} -mem {opt.memory}"
     else:
         # not final
         CMD = f"iqtree --seqtype DNA -s '{fasta}' {model_term} -merit {opt.criterion} -T {thread} -mem {opt.memory}"
@@ -336,6 +336,10 @@ def RAxML(
     logging.info(CMD)
     Run = subprocess.call(CMD, shell=True)
 
+    if Run != 0:
+        logging.error(f"RAxML Failed!")
+        raise Exception
+
     # Return result to original directory
     os.chdir(path_ori)
     file = out.split("/")[-1]
@@ -405,7 +409,7 @@ def IQTREE(
         else:
             tmp_fasta = fasta
 
-        CMD = f"{path.sys_path}/external/iqtree-2.2.2.7-Windows/bin/iqtree2.exe -s {tmp_fasta} -B {bootstrap} -T {thread} {model}"
+        CMD = f"{path.sys_path}/external/iqtree/bin/iqtree2.exe -s {tmp_fasta} -B {bootstrap} -T {thread} {model}"
     else:
         CMD = f"iqtree -s {fasta} -B {bootstrap} -T {thread} {model}"
 
diff --git a/funid/src/hasher.py b/funvip/src/hasher.py
similarity index 100%
rename from funid/src/hasher.py
rename to funvip/src/hasher.py
diff --git a/funid/src/initialize.py b/funvip/src/initialize.py
similarity index 98%
rename from funid/src/initialize.py
rename to funvip/src/initialize.py
index 78c037b..7c818b7 100644
--- a/funid/src/initialize.py
+++ b/funvip/src/initialize.py
@@ -9,14 +9,14 @@
 import re
 
 from Bio import SeqIO
-from funid.src.logics import isnan
-from funid.src.tool import (
+from funvip.src.logics import isnan
+from funvip.src.tool import (
     initialize_path,
     get_genus_species,
     mkdir,
 )
-from funid.src.logger import Mes
-from funid.src.validate_option import initialize_option
+
+from funvip.src.validate_option import initialize_option
 
 INF = 99999999
 
@@ -134,7 +134,7 @@ def err_msg(type__):
     return obj
 
 
-# Change it to FunIDPath
+# Change it to FunVIPPath
 # Class with all path
 # Use print instead of logging here, because logging has not been defined here
 class Path:
diff --git a/funvip/src/logger.py b/funvip/src/logger.py
new file mode 100644
index 0000000..003e247
--- /dev/null
+++ b/funvip/src/logger.py
@@ -0,0 +1,69 @@
+from datetime import datetime
+import os
+import shelve
+import subprocess
+import logging
+
+
+class CustomFormatter(logging.Formatter):
+    # Initialize ansicode on windows
+    if os.name == "nt":  # Only if we are running on Windows
+        from ctypes import windll
+
+        k = windll.kernel32
+        k.SetConsoleMode(k.GetStdHandle(-11), 7)
+
+    # ANSI \x1b
+    # UNICODE \u001b
+
+    grey = "\x1b[38;20m"
+    yellow = "\x1b[33;20m"
+    red = "\x1b[31;20m"
+    bold_red = "\x1b[31;1m"
+    white = "\x1b[38;97m"
+    reset = "\x1b[0m"
+    fmt = "%(asctime)s [%(levelname)s] %(message)s"
+    fmt_err = "%(asctime)s [%(levelname)s] %(message)s (%(filename)s:%(lineno)d)"
+
+    FORMATS = {
+        logging.DEBUG: grey + fmt + reset,
+        logging.INFO: white + fmt + reset,
+        logging.WARNING: yellow + fmt_err + reset,
+        logging.ERROR: bold_red + fmt_err + reset,
+        logging.CRITICAL: bold_red + fmt_err + reset,
+    }
+
+    def format(self, record):
+        log_fmt = self.FORMATS.get(record.levelno)
+        formatter = logging.Formatter(log_fmt, datefmt="%m/%d/%Y %I:%M:%S %p")
+        return formatter.format(record)
+
+
+def setup_logging(list_info, list_warning, list_error, path, opt, tool):
+    # setup logging
+    logging.basicConfig(
+        format="%(asctime)s [%(levelname)s] %(message)s",
+        datefmt="%m/%d/%Y %I:%M:%S %p",
+        encoding="utf-8",
+        level=tool.get_level(opt.verbose),
+        handlers=[logging.FileHandler(path.log), logging.StreamHandler()],
+    )
+
+    # Set the custom formatter
+    formatter = CustomFormatter()
+    for handler in logging.root.handlers:
+        handler.setFormatter(formatter)
+
+    # Delayed logging for option parsing
+
+    # I don't know why, but in some environment, ANSI color works only after first subprocess.call was done
+    _ = subprocess.call("", shell=True)
+
+    for info in list_info:
+        logging.info(info)
+
+    for warning in list_warning:
+        logging.warning(warning)
+
+    for error in list_error:
+        logging.error(error)
diff --git a/funid/src/logics.py b/funvip/src/logics.py
similarity index 100%
rename from funid/src/logics.py
rename to funvip/src/logics.py
diff --git a/funid/src/ncbi.py b/funvip/src/ncbi.py
similarity index 100%
rename from funid/src/ncbi.py
rename to funvip/src/ncbi.py
diff --git a/funid/src/opt_generator.py b/funvip/src/opt_generator.py
similarity index 100%
rename from funid/src/opt_generator.py
rename to funvip/src/opt_generator.py
diff --git a/funid/src/reporter.py b/funvip/src/reporter.py
similarity index 80%
rename from funid/src/reporter.py
rename to funvip/src/reporter.py
index c883ede..304a95c 100644
--- a/funid/src/reporter.py
+++ b/funvip/src/reporter.py
@@ -4,12 +4,10 @@
 import io
 import logging
 import plotly.express as px
+import sys
 from tabulate import tabulate
-from funid.src.tool import index_step
-from funid.src.save import save_df
-
-# For version reporting
-__version__ = "0.3.18.6"
+from funvip.src.tool import index_step
+from funvip.src.save import save_df
 
 
 ### Temporary report for tree_interpretation_pipe
@@ -83,9 +81,9 @@ def __init__(self):
         self.statistics = {
             "GROUP": [],
             "IDENTIFIED": [],
-            "AMBIGUOUS": [],
             "NEW SPECIES CANDIDATE": [],
             "MISIDENTIFIED": [],
+            "AMBIGUOUS": [],
             "ERROR": [],
             "TOTAL": [],
         }
@@ -93,24 +91,24 @@ def __init__(self):
         # For linking in html, should be added later (on GUI)
         # self.statistics_link = {}
 
-        # By gene results, and sequence should be added
         self.result = {
             "ID": [],
             "HASH": [],
             "DATATYPE": [],
+            "ISSUES": [],
             "GROUP_ORIGINAL": [],
             "GROUP_ASSIGNED": [],
             "SPECIES_ORIGINAL": [],
             "SPECIES_ASSIGNED": [],
-            "FLAT_BRANCH": [],
-            "INCONSISTENT": [],
-            "AMBIGUOUS": [],
             "STATUS": [],
         }
 
         self.query_result = None
 
     def update_dataset(self, V, opt):
+        # Refresh when updating dataset
+        self.dataset = {"GROUP": []}
+
         for gene in V.list_qr_gene:
             self.dataset[gene.upper()] = []
 
@@ -158,46 +156,56 @@ def update_result(self, V, opt):
         self.result["ID"] = []
         self.result["HASH"] = []
         self.result["DATATYPE"] = []
+        self.result["ISSUES"] = []
         self.result["GROUP_ORIGINAL"] = []
         self.result["GROUP_ASSIGNED"] = []
         self.result["SPECIES_ORIGINAL"] = []
         self.result["SPECIES_ASSIGNED"] = []
-        self.result["FLAT_BRANCH"] = []
-        self.result["INCONSISTENT"] = []
-        self.result["AMBIGUOUS"] = []
         self.result["STATUS"] = []
 
+        # Make FI categorizing system
+        FI_category = {}
+        dict_og = {}
+        for group in V.dict_dataset:
+            for gene in V.dict_dataset[group]:
+                for FI in V.dict_dataset[group][gene].list_og_FI:
+                    dict_og[FI.hash] = group
+
         for _hash in V.dict_hash_FI:
             FI = V.dict_hash_FI[_hash]
-
-            # Collect result from only used sequences
             if FI.adjusted_group in V.dict_dataset:
-                # If any of appropriate gene used
-                if any(
-                    key in V.dict_dataset[FI.adjusted_group] for key in FI.seq.keys()
-                ):
-                    # Default results
-                    self.result["ID"].append(FI.original_id)
-                    self.result["HASH"].append(FI.hash)
-                    self.result["DATATYPE"].append(FI.datatype)
-
-                    if str(FI.group).strip() == "":
-                        self.result["GROUP_ORIGINAL"].append("-")
-                    else:
-                        self.result["GROUP_ORIGINAL"].append(FI.group)
+                FI_category[_hash] = FI.datatype
+            elif FI.hash in dict_og:
+                FI_category[FI.hash] = "outgroup"
 
-                    self.result["GROUP_ASSIGNED"].append(FI.adjusted_group)
+        for _hash in V.dict_hash_FI:
+            FI = V.dict_hash_FI[_hash]
 
-                    if f"{FI.ori_genus} {FI.ori_species}".strip() == "":
-                        self.result["SPECIES_ORIGINAL"].append("-")
-                    else:
-                        self.result["SPECIES_ORIGINAL"].append(
-                            f"{FI.ori_genus} {FI.ori_species}"
-                        )
+            # Common results
+            self.result["ID"].append(FI.original_id)
+            self.result["HASH"].append(FI.hash)
+            if str(FI.group).strip() == "":
+                self.result["GROUP_ORIGINAL"].append("-")
+            else:
+                self.result["GROUP_ORIGINAL"].append(FI.group)
 
-                    # Collect identification result for each gene analysis
-                    inconsistent_flag = 0
+            if f"{FI.ori_genus} {FI.ori_species}".strip() == "":
+                self.result["SPECIES_ORIGINAL"].append("-")
+            else:
+                self.result["SPECIES_ORIGINAL"].append(
+                    f"{FI.ori_genus} {FI.ori_species}"
+                )
+            self.result["ISSUES"].append(FI.get_issue_str())
+
+            # Collect result from only used sequences
+            if _hash in FI_category:
+                self.result["DATATYPE"].append(FI_category[_hash])
 
+                # If any of appropriate gene used
+                if FI.adjusted_group in V.dict_dataset:
+                    self.result["GROUP_ASSIGNED"].append(FI.adjusted_group)
+
+                    # Collect assigned result
                     for gene in set_gene:
                         # Check if data analysis had performed for specific FI, group, gene combination
                         if (
@@ -208,14 +216,7 @@ def update_result(self, V, opt):
                             self.result[f"{gene.upper()}_ASSIGNED"].append(
                                 FI.bygene_species[gene]
                             )
-                            # Check inconsistent identification across genes
-                            if not (
-                                any(
-                                    _sp in FI.final_species
-                                    for _sp in FI.bygene_species[gene].split("/")
-                                )
-                            ):
-                                inconsistent_flag = 1
+
                         else:
                             self.result[f"{gene.upper()}_ASSIGNED"].append("-")
 
@@ -225,40 +226,52 @@ def update_result(self, V, opt):
                     else:
                         self.result["SPECIES_ASSIGNED"].append("UNDETERMINED")
 
-                    ## Add abnormalities
-                    # Flat
-                    self.result["FLAT_BRANCH"].append("/".join(FI.flat))
-                    # Inconsistency
-
-                    if inconsistent_flag == 1:
-                        self.result["INCONSISTENT"].append("inconsistent")
+                    # Should fix here
+                    if FI_category[_hash] == "query":
+                        if FI.final_species.strip() == "":
+                            self.result["STATUS"].append("failed")
+                        elif f"{FI.ori_genus} {FI.ori_species}".strip() == "":
+                            if "sp." in FI.final_species.strip():
+                                self.result["STATUS"].append("new species")
+                            else:
+                                self.result["STATUS"].append("assigned")
+
+                        elif FI.final_species == f"{FI.ori_genus} {FI.ori_species}":
+                            self.result["STATUS"].append("match")
+
+                        elif FI.final_species != f"{FI.ori_genus} {FI.ori_species}":
+                            self.result["STATUS"].append("conflict")
                     else:
-                        self.result["INCONSISTENT"].append("-")
-
-                    self.result["AMBIGUOUS"].append(FI.species_identifier)
-
-                    if FI.final_species.strip() == "":
-                        self.result["STATUS"].append("ERROR")
-                    elif (
-                        f"{FI.ori_genus} {FI.ori_species}".strip() == ""
-                        and "sp." in FI.final_species.strip()
-                    ):
-                        self.result["STATUS"].append("new species candidate")
-                    elif f"{FI.ori_genus} {FI.ori_species}".strip() == "":
-                        self.result["STATUS"].append("undetermined")
-                    elif FI.final_species == f"{FI.ori_genus} {FI.ori_species}":
-                        self.result["STATUS"].append("correctly identified")
+                        self.result["STATUS"].append("-")
+                # For outgroup or ambiguous db
+                else:
+                    self.result["GROUP_ASSIGNED"].append("-")
+                    # Collect assigned result
+                    for gene in set_gene:
+                        self.result[f"{gene.upper()}_ASSIGNED"].append("-")
+                    if FI.final_species != "":
+                        self.result["SPECIES_ASSIGNED"].append(f"{FI.final_species}")
                     else:
-                        self.result["STATUS"].append("misidentified")
+                        self.result["SPECIES_ASSIGNED"].append("UNDETERMINED")
+                    self.result["STATUS"].append("-")
+
+            # For unused FI
             else:
-                # print(f"DEBUGGING {self.dataset['GROUP']} {FI.adjusted_group}")
-                # logging.info(f"{FI} removed from report because not used in analysis")
-                pass
-
-        ## update query only result
-        # print(self.result)
-        for key in self.result:
-            print(f"{key} : {len(self.result[key])}")
+                self.result["DATATYPE"].append("unused")
+
+                for gene in set_gene:
+                    # Check if data analysis had performed for specific FI, group, gene combination
+                    self.result[f"{gene.upper()}_ASSIGNED"].append("-")
+
+                # Add final identification result
+                if FI.final_species != "":
+                    self.result["SPECIES_ASSIGNED"].append(f"{FI.final_species}")
+                else:
+                    self.result["SPECIES_ASSIGNED"].append("-")
+
+                self.result["STATUS"].append("unused")
+
+        ## update query only result on report.txt
         self.query_result = pd.DataFrame(self.result)
         # Filter if queryonly is True
         if opt.queryonly is True:
@@ -276,6 +289,7 @@ def update_result(self, V, opt):
             df_group = df_result_group.get_group(group)
 
             # Collect statistics
+            """
             cnt_correctly_identified = list(df_group["STATUS"]).count(
                 "correctly identified"
             )
@@ -294,8 +308,10 @@ def update_result(self, V, opt):
                     cnt_error,
                 )
             )
+            """
 
             # Write into dictionary
+            """
             self.statistics["GROUP"].append(group)
             self.statistics["IDENTIFIED"].append(cnt_correctly_identified)
             self.statistics["AMBIGUOUS"].append(cnt_undetermined)
@@ -303,8 +319,10 @@ def update_result(self, V, opt):
             self.statistics["MISIDENTIFIED"].append(cnt_misidentified)
             self.statistics["ERROR"].append(cnt_error)
             self.statistics["TOTAL"].append(cnt_total)
+            """
 
         # Add final summations
+        """
         self.statistics["GROUP"].append("TOTAL")
         self.statistics["IDENTIFIED"].append(sum(self.statistics["IDENTIFIED"]))
         self.statistics["AMBIGUOUS"].append(sum(self.statistics["AMBIGUOUS"]))
@@ -314,36 +332,38 @@ def update_result(self, V, opt):
         self.statistics["MISIDENTIFIED"].append(sum(self.statistics["MISIDENTIFIED"]))
         self.statistics["ERROR"].append(sum(self.statistics["ERROR"]))
         self.statistics["TOTAL"].append(sum(self.statistics["TOTAL"]))
+        """
 
     ### Main report runner
     # Update report by pipeline step
-    def update_report(self, V, path, opt, step):
+    def update_report(self, V, path, opt, step, version, GenMine_flag):
         if step == "setup":
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         elif step == "search":
             self.report_table(V, path, opt, step)
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         elif step == "cluster":
             self.update_dataset(V, opt)
             self.report_table(V, path, opt, step)
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
             # Datasets are made after clustering
         elif step == "align":
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         elif step == "trim":
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         elif step == "concatenate":
-            self.report_text(V, path, opt, step)
+            self.update_dataset(V, opt)  # To check align fails
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         elif step == "modeltest":
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         elif step == "tree":
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         elif step == "visualize":
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         elif step == "report":
             self.update_result(V, opt)
             self.report_table(V, path, opt, step)
-            self.report_text(V, path, opt, step)
+            self.report_text(V, path, opt, step, version, GenMine_flag)
         else:
             logging.error(
                 f"DEVELOPMENTAL ERROR : BAD STEP INPUT {step} WHILE UPDATE REPORT"
@@ -351,11 +371,14 @@ def update_report(self, V, path, opt, step):
             raise Exception
 
     # Generate report in text file
-    def report_text(self, V, path, opt, step):
+    def report_text(self, V, path, opt, step, version, GenMine_flag):
         # Rewrite everytime when called, the io step won't be that much
         with open(f"{path.root}/{opt.runname}.report.txt", "wt", encoding="UTF8") as f:
             if index_step(step) >= 0:
-                f.write("FunID Report\n\n")
+                f.write(
+                    "Visualization of this file is optimized to web browsers (such as chrome) or width unlimited text editors (such as visual studio code)\n\n"
+                )
+                f.write("FunVIP Report\n\n")
                 ## Write runinfo
                 f.write("[INFO]\n")
                 # Runname
@@ -370,7 +393,14 @@ def report_text(self, V, path, opt, step):
                 # f.write(f"WARNINGS     : {}\n")
                 # Number of errors
                 # f.write(f"ERRORS       : {}\n")
-                f.write(f"FINAL STEP   : {step}")
+                if step == "report":
+                    step_status = "pipeline succesfully finished"
+                else:
+                    step_status = (
+                        "pipeline unexpectly terminated - please check log file"
+                    )
+
+                f.write(f"FINAL STEP   : {step} ({step_status})\n")
 
             f.write("\n")
 
@@ -385,14 +415,24 @@ def report_text(self, V, path, opt, step):
             if index_step(step) >= 2:
                 # Datset table can be written after cluster and outgroup selection step
                 f.write(
-                    f"* Dataset table can be found in {path.root}/{opt.runname}_Dataset.{opt.matrixformat}\n"
+                    f"* Dataset table can be found in {path.root}/{opt.runname}_Dataset.{opt.tableformat}\n"
                 )
                 # Identification table can be written after cluster step
                 f.write(
-                    f"* Identification result table can be found in {path.root}/{opt.runname}_Identification.{opt.matrixformat}\n"
+                    f"* Identification result table can be found in {path.root}/{opt.runname}_Identification.{opt.tableformat}\n"
                 )
                 f.write(f"* Log files can be found in {path.log}\n")
-                f.write("\n")
+
+            if index_step(step) >= 8:
+                f.write(f"* Tree files can be found in {path.out_tree}\n")
+
+            f.write("\n")
+
+            ## Write full commands used
+            if index_step(step) >= 0:
+                f.write(f"[COMMAND]\n")
+                f.write(f"If you want to regenerate this result, use\n")
+                f.write(f"\n\tFUNVIP {' '.join(sys.argv[:-1])}\n\n")
 
             ## Write options used (in concise form)
             if index_step(step) >= 0:
@@ -423,7 +463,7 @@ def report_text(self, V, path, opt, step):
                 f.write(f"AVX:                    {opt.avx}\n")
                 f.write(f"CACHEDB:                {opt.cachedb}\n")
                 f.write(f"USECACHE:               {opt.usecache}\n")
-                f.write(f"MATRIXFORMAT:           {opt.matrixformat}\n")
+                f.write(f"TABLEFORMAT:            {opt.tableformat}\n")
                 f.write(f"NOSEARCHRESULT:         {opt.nosearchresult}\n")
                 f.write(f"METHOD:                 \n")
                 f.write(f" - SEARCH:              {opt.method.search}\n")
@@ -462,12 +502,13 @@ def report_text(self, V, path, opt, step):
                 f.write("\n\n")
                 f.write("O : Group - Gene dataset analysis done\n")
                 f.write(
-                    "- : Group - Gene dataset analysis cannot be performed (absence of query sequence, no appropriate outgroup etc..)\n"
+                    "- : Group - Gene dataset analysis cannot be performed (absence of query sequence, no appropriate outgroup, alignment fail etc..)\n"
                 )
                 f.write("\n\n")
 
             ## Write identification statistics
             # Should be written after tree_interpretation
+            '''
             if index_step(step) >= 9:
                 f.write(f"[STATISTICS]\n")
                 f.write(tabulate(self.statistics, headers=self.statistics.keys()))
@@ -475,9 +516,11 @@ def report_text(self, V, path, opt, step):
                 f.write(
                     "IDENTIFIED : Number of well-identified strains without any concerns. \n"
                 )
+                """
                 f.write(
                     "AMBIGUOUS : Multiple clades with same taxon name. Your database may contain misidentified sequences. \n"
                 )
+                """
                 f.write(
                     "NEW SPECIES CANDIDATE : New species candidate strains found by topology, phylogenetic distance and bootstrap criteria\n"
                 )
@@ -488,6 +531,7 @@ def report_text(self, V, path, opt, step):
                     "ERROR : Strains that cannot be analyzed. Please check if appropriate database sequence / outgroup sequences are given\n"
                 )
                 f.write("\n\n")
+            '''
 
             ## Write identification result
             # Should be written after tree_interpretation
@@ -510,10 +554,10 @@ def report_text(self, V, path, opt, step):
                 )
                 f.write("DATATYPE : query or database\n")
                 f.write("GROUP_ORIGINAL : group name given by user\n")
-                f.write("GROUP_ASSIGNED : group assigned by FunID clustering\n")
+                f.write("GROUP_ASSIGNED : group assigned by FunVIP clustering\n")
                 f.write("SPECIES_ORIGINAL : species name given by user\n")
                 f.write(
-                    "SPECIES_ASSIGNED : final species name (usually result from concatenated analysis) assigned by FunID tree_interpretation\n"
+                    "SPECIES_ASSIGNED : final species name (usually result from concatenated analysis) assigned by FunVIP tree_interpretation\n"
                 )
                 f.write(
                     "FLAT_BRANCH : Strains with flat_branch in phylogenetic analysis. If checked, please check your barcode region have enough taxonomic resolution\n"
@@ -521,9 +565,11 @@ def report_text(self, V, path, opt, step):
                 f.write(
                     "INCONSISTENT : Strains that show different identification result across genes. Please check sequences were contaminated or misused. \n"
                 )
+                """
                 f.write(
                     "AMBIGUOUS : Multiple clades with same taxon name. Your database may contain misidentified sequences. \n"
                 )
+                """
 
                 f.write("\n\n")
 
@@ -531,7 +577,7 @@ def report_text(self, V, path, opt, step):
             # Can be written after clustering step
             if index_step(step) >= 1:
                 f.write(f"[METHOD]\n")
-                f.write(f"Sequences were identified with FunID {__version__}\n")
+                f.write(f"Sequences were identified with FunVIP {version.FunVIP}\n")
                 f.write("\n")
 
                 cnt_db = len([FI for FI in V.list_FI if FI.datatype == "db"])
@@ -550,15 +596,15 @@ def report_text(self, V, path, opt, step):
                 )
                 f.write("\n")
                 """
-                for warning in manage_input_warning:
+                for warning in validate_input_warning:
                     f.write(f"\n")
-                for error in manage_input_error:
+                for error in validate_input_error:
                     f.write(f"\n")
                 """
                 f.write(
                     "* Most of the warnings in this step are usually typo problems "
                     "(blanks, tabs, foreign languages that cannot be used in certain programs - like german umlauts) "
-                    "and can be automatically fixed by FunID. So you don't have to consider about it that much if you are not going to directly publish.\n"
+                    "and can be automatically fixed by FunVIP. So you don't have to consider about it that much if you are not going to directly publish.\n"
                 )
                 f.write("\n")
 
@@ -685,7 +731,7 @@ def report_text(self, V, path, opt, step):
 
                 """
                 f.write(
-                    f"As a result of FunID analysis, a total of {cnt_query} strains were identified"
+                    f"As a result of FunVIP analysis, a total of {cnt_query} strains were identified"
                     f"{cnt_query} sequences constists of"
                     f"{cnt_consistent} well identified strains,"
                     f"{cnt_ambiguous} strains that showed different results by genes,"
@@ -706,7 +752,10 @@ def report_text(self, V, path, opt, step):
             f.write("\n")
 
             # Generate software list by options
-            software_list = ["FunID"]
+            software_list = ["FunVIP"]
+            if GenMine_flag != 0:
+                software_list.append("GenMine")
+
             if index_step(step) >= 1:
                 if opt.method.search == "blast":
                     software_list.append("BLASTn")
@@ -738,18 +787,20 @@ def report_text(self, V, path, opt, step):
                     software_list.append("RAxML")
 
             # append software list by step
+            # Should add GenMine version here
             dict_version = {
-                "FunID": f"{__version__}",
-                "BLASTn": "2.13.0",
-                "MMseqs2": "14.7e284",
-                "MAFFT": "7.453",
-                "Gblocks": "0.91b",
-                "TrimAl": "1.4",
-                "Modeltest-NG": "0.1.7",
-                "Partitionfinder": "2.2.0.3",
-                "FastTree": "2.1.11",
-                "IQTREE2": "2.2.2.7",
-                "RAxML": "8.2.12",
+                "FunVIP": version.FunVIP,
+                "GenMine": version.GenMine,
+                "BLASTn": version.BLASTn,
+                "MMseqs2": version.MMseqs2,
+                "MAFFT": version.MAFFT,
+                "Gblocks": version.Gblocks,
+                "TrimAl": version.trimAl,
+                "Modeltest-NG": version.Modeltest_NG,
+                "Partitionfinder": version.IQTREE2,
+                "FastTree": version.FastTree,
+                "IQTREE2": version.IQTREE2,
+                "RAxML": version.RAxML,
             }
 
             ### Version for each software notation
@@ -763,7 +814,8 @@ def report_text(self, V, path, opt, step):
             ### Write citations
             f.write(f"[CITATION]\n")
             dict_citation = {
-                "FunID": "https://github.com/Changwanseo/FunID",
+                "FunVIP": "https://github.com/Changwanseo/FunVIP",
+                "GenMine": "Seo, C. W., Kim, S. H., Lim, Y. W., & Park, M. S. (2022). Re-identification on Korean Penicillium sequences in GenBank collected by software GenMine. Mycobiology, 50(4), 231-237.",
                 "BLASTn": "Altschul, S. F., Gish, W., Miller, W., Myers, E. W., & Lipman, D. J. (1990). Basic local alignment search tool. Journal of molecular biology, 215(3), 403-410.",
                 "MMseqs2": "Steinegger, M., & Söding, J. (2017). MMseqs2 enables sensitive protein sequence searching for the analysis of massive data sets. Nature biotechnology, 35(11), 1026-1028.",
                 "MAFFT": "Katoh, K., & Standley, D. M. (2013). MAFFT multiple sequence alignment software version 7: improvements in performance and usability. Molecular biology and evolution, 30(4), 772-780.",
@@ -790,28 +842,28 @@ def report_table(self, V, path, opt, step):
 
         if index_step(step) >= 9:
             list_table.append("identification")
-            list_table.append("statistics")
+            # list_table.append("statistics")
 
         # Write tables
         for table in list_table:
             if table == "dataset":
                 save_df(
                     df=pd.DataFrame(self.dataset),
-                    out=f"{path.root}/{opt.runname}.dataset.{opt.matrixformat}",
-                    fmt=opt.matrixformat,
+                    out=f"{path.root}/{opt.runname}.dataset.{opt.tableformat}",
+                    fmt=opt.tableformat,
                 )
             elif table == "identification":
                 save_df(
                     df=pd.DataFrame(self.result),
-                    out=f"{path.root}/{opt.runname}.result.{opt.matrixformat}",
-                    fmt=opt.matrixformat,
+                    out=f"{path.root}/{opt.runname}.result.{opt.tableformat}",
+                    fmt=opt.tableformat,
                 )
 
             elif table == "statistics":
                 save_df(
                     df=pd.DataFrame(self.statistics),
-                    out=f"{path.root}/{opt.runname}.statistics.{opt.matrixformat}",
-                    fmt=opt.matrixformat,
+                    out=f"{path.root}/{opt.runname}.statistics.{opt.tableformat}",
+                    fmt=opt.tableformat,
                 )
             else:
                 logging.error(
diff --git a/funid/src/save.py b/funvip/src/save.py
similarity index 97%
rename from funid/src/save.py
rename to funvip/src/save.py
index 77fc698..6fccf86 100644
--- a/funid/src/save.py
+++ b/funvip/src/save.py
@@ -10,14 +10,14 @@
 import logging
 from unidecode import unidecode
 from Bio import SeqIO
-from funid.src.tool import (
+from funvip.src.tool import (
     initialize_path,
     get_genus_species,
     get_id,
     manage_unicode,
 )
-from funid.src.logics import isnewicklegal
-from funid.src.hasher import decode, newick_legal, hash_funinfo_list
+from funvip.src.logics import isnewicklegal
+from funvip.src.hasher import decode, newick_legal, hash_funinfo_list
 import shelve
 
 
diff --git a/funid/src/search.py b/funvip/src/search.py
similarity index 97%
rename from funid/src/search.py
rename to funvip/src/search.py
index 0c1bb39..64f9507 100644
--- a/funid/src/search.py
+++ b/funvip/src/search.py
@@ -1,6 +1,6 @@
-from funid.src import cluster, tool, hasher, manage_input, save
-from funid.src.ext import blast, makeblastdb, mmseqs, makemmseqsdb
-from funid.src.save import save_df
+from funvip.src import cluster, tool, hasher, validate_input, save
+from funvip.src.ext import blast, makeblastdb, mmseqs, makemmseqsdb
+from funvip.src.save import save_df
 import copy
 import pandas as pd
 import numpy as np
@@ -314,8 +314,8 @@ def search_df(V, path, opt):
             if opt.nosearchresult is False:
                 save_df(
                     hasher.decode_df(V.dict_id_hash, df_search),
-                    f"{path.out_matrix}/{opt.runname}_BLAST_result_{gene}.{opt.matrixformat}",
-                    fmt=opt.matrixformat,
+                    f"{path.out_matrix}/{opt.runname}_BLAST_result_{gene}.{opt.tableformat}",
+                    fmt=opt.tableformat,
                 )
 
     # if query sequence exists
@@ -389,7 +389,7 @@ def search_df(V, path, opt):
             if query_state == 0:
                 V.list_qr_gene.remove(gene)
             else:
-                # If db column and query column does not matches -> should be moved to manage_input
+                # If db column and query column does not matches -> should be moved to validate_input
                 if not (gene in V.list_db_gene):
                     logging.error(
                         f"Gene {gene} found in query, but not found in database. Please add {gene} column to database"
@@ -417,8 +417,8 @@ def search_df(V, path, opt):
             if opt.nosearchresult is False:
                 save_df(
                     hasher.decode_df(V.dict_id_hash, df_search),
-                    f"{path.out_matrix}/{opt.runname}_BLAST_result_{gene}.{opt.matrixformat}",
-                    opt.matrixformat,
+                    f"{path.out_matrix}/{opt.runname}_BLAST_result_{gene}.{opt.tableformat}",
+                    opt.tableformat,
                 )
 
         # remove tmp file after search
diff --git a/funid/src/templates/template.html b/funvip/src/templates/template.html
similarity index 100%
rename from funid/src/templates/template.html
rename to funvip/src/templates/template.html
diff --git a/funid/src/tool.py b/funvip/src/tool.py
similarity index 92%
rename from funid/src/tool.py
rename to funvip/src/tool.py
index 41845a5..8000269 100644
--- a/funid/src/tool.py
+++ b/funvip/src/tool.py
@@ -11,7 +11,7 @@
 from unidecode import unidecode
 
 
-# Maybe we should move this to /funid/src/toolbox/ and split these to multiple files
+# Maybe we should move this to /funvip/src/toolbox/ and split these to multiple files
 def initialize_path(path):
     global genus_file
     genus_file = path.genusdb
@@ -37,27 +37,29 @@ def manage_unicode(string, column="", row=""):
         string.encode("ascii")
         return string
     except:
-        if row != "":
-            row_string = f"{row}th row "
-        else:
-            row_string = ""
+        pass
 
-        if column != "":
-            column_string = f"in {column} column, "
-        else:
-            column_string = ""
+    if row != "":
+        row_string = f"{row}th row "
+    else:
+        row_string = ""
 
-        logging.info(
-            f"Illegal unicode character found {column_string}{row_string}: {string}. Trying flexible solve"
-        )
+    if column != "":
+        column_string = f"in {column} column, "
+    else:
+        column_string = ""
 
-        try:
-            return unidecode(string)
-        except:
-            logging.error(
-                f"Flexible solve failed to {string}. Please change the cell with available ascii strings"
-            )
-            raise Exception
+    logging.info(
+        f"Illegal unicode character found {column_string}{row_string}: {unidecode(string)}. Trying flexible solve"
+    )
+
+    try:
+        return unidecode(string)
+    except:
+        logging.error(
+            f"Flexible solve failed to {string}. Please change the cell with available ascii strings"
+        )
+        raise Exception
 
 
 @lru_cache(maxsize=10000)
@@ -289,7 +291,7 @@ def index_step(step):
 
 
 def check_avx():
-    return platform.machine() == "x86_64"
+    return platform.machine() == "x86_64" or platform.machine() == "AMD64"
 
 
 # Return logging
diff --git a/funid/src/tree.py b/funvip/src/tree.py
similarity index 97%
rename from funid/src/tree.py
rename to funvip/src/tree.py
index c9a208f..d87a6be 100644
--- a/funid/src/tree.py
+++ b/funvip/src/tree.py
@@ -1,5 +1,5 @@
-from funid.src import ext, hasher
-from funid.src.opt_generator import opt_generator
+from funvip.src import ext, hasher
+from funvip.src.opt_generator import opt_generator
 from copy import deepcopy
 import multiprocessing as mp
 import logging
@@ -12,6 +12,12 @@ def pipe_tree(V, path, opt, model_dict):
     tree_hash_dict = hasher.encode(V.list_FI, newick=True)
 
     # remove tree files already exists to prevent error
+    try:
+        for file in [f for f in os.listdir(path.tmp) if f.endswith(".nwk")]:
+            os.remove(f"{path.tmp}/{file}")
+    except:
+        pass
+
     try:
         for file in [f for f in os.listdir(path.out_tree) if f.endswith(".nwk")]:
             os.remove(f"{path.out_tree}/{file}")
diff --git a/funid/src/tree_interpretation.py b/funvip/src/tree_interpretation.py
similarity index 85%
rename from funid/src/tree_interpretation.py
rename to funvip/src/tree_interpretation.py
index 5959e4a..fa41a4a 100644
--- a/funid/src/tree_interpretation.py
+++ b/funvip/src/tree_interpretation.py
@@ -14,7 +14,7 @@
 import lxml.etree as ET
 import pandas as pd
 from functools import lru_cache
-from funid.src.tool import get_id, get_genus_species
+from funvip.src.tool import get_id, get_genus_species
 import dendropy
 import collections
 import os
@@ -25,6 +25,12 @@
 # Default zero length branch for concatenation
 CONCAT_ZERO = 0  # for better binding
 
+# For colored logging
+bold_red = "\x1b[31;1m"
+yellow = "\x1b[33;20m"
+green = "\x1b[92m"
+reset = "\x1b[0m"
+
 
 ## Get maximum tree distance among all leaf pairs in given tree
 def get_max_distance(tree):
@@ -218,6 +224,8 @@ def __init__(self, tree, Tree_style, group, gene, opt):
         self.additional_clustering = True
         self.zero = 0.00000100000050002909
 
+        self.flat_clades = []
+
     # to find out already existing new species number to avoid overlapping
     # e.g. avoid sp 5 if P. sp 5 already exsits in database
     def reserve_sp(self):
@@ -252,11 +260,13 @@ def decide_type(self, string, by="hash", priority="query"):
                 return "none"
 
         else:
-            print("[ERROR] DEVELOPMENTAL ERROR, UNEXPECTED by for decide_type")
+            print(
+                f"{bold_red}[ERROR] DEVELOPMENTAL ERROR, UNEXPECTED by for decide_type{reset}"
+            )
             raise Exception
 
     # Calculate zero length branch length cutoff with given tree and alignment
-    def calculate_zero(self, alignment_file):
+    def calculate_zero(self, alignment_file, gene, partition_dict):
         # Parse alignment
         seq_list = list(SeqIO.parse(alignment_file, "fasta"))
 
@@ -268,18 +278,19 @@ def calculate_zero(self, alignment_file):
             hash_list_alignment
         ):
             print(
-                f"[ERROR] content of tree and alignment is not identical for {self.tree_name}"
+                f"{bold_red}[ERROR] content of tree and alignment is not identical for {self.tree_name}{reset}"
             )
             raise Exception
 
         # Find identical or including pairs in alignment
-        pairs = []
+        identical_pairs = []
+        different_pairs = []
         for seq1 in seq_list:
             for seq2 in seq_list:
                 if not (
                     str(seq1.id).strip() == str(seq2.id).strip()
-                    or (seq1.id, seq2.id) in pairs
-                    or (seq2.id, seq1.id) in pairs
+                    or (seq1.id, seq2.id) in identical_pairs
+                    or (seq2.id, seq1.id) in identical_pairs
                 ):
                     # Chenge unusable chars into gap
                     seq1_str = str(seq1.seq).lower()
@@ -295,28 +306,103 @@ def calculate_zero(self, alignment_file):
                     # To prevent distance among different region detected as zero in concatenated analysis
                     overlapping_cnt = 0
 
-                    # for valid part
-                    for n, i in enumerate(seq1_str):
-                        if seq1_str[n] != "-" and seq2_str[n] != "-":
+                    if gene == "concatenated":
+                        len_dict = partition_dict["len"]
+                        gene_order = partition_dict["order"]
+
+                        valid_index = []
+
+                        # calculate valid index to check
+                        previous_index = 0
+
+                        for gene in gene_order:
+                            start = previous_index
+                            end = previous_index + len_dict[gene] - 1
+
+                            for n in range(
+                                previous_index, len_dict[gene] + previous_index
+                            ):
+                                if seq1_str[n] != "-" and seq2_str[n] != "-":
+                                    start = n
+                                    break
+
+                            for n in range(
+                                len_dict[gene] + previous_index - 1,
+                                previous_index - 1,
+                                -1,
+                            ):
+                                if seq1_str[n] != "-" and seq2_str[n] != "-":
+                                    end = n
+                                    break
+
+                            for n in range(start, end + 1):
+                                valid_index.append(n)
+
+                            previous_index += len_dict[gene]
+
+                        # for valid part
+                        for n in valid_index:
+                            # connected with or to evaluate insertions or deletions
                             if seq1_str[n] != seq2_str[n]:
                                 identical_flag = False
+                            else:
                                 overlapping_cnt += 1
+
+                    else:
+                        start = 0
+                        end = len(seq1_str) - 1
+                        # calculate start and end
+                        for n in range(len(seq1_str)):
+                            if seq1_str[n] != "-" and seq2_str[n] != "-":
+                                start = n
+                                break
+
+                        for n in range(len(seq1_str)):
+                            if (
+                                seq1_str[len(seq1_str) - n - 1] != "-"
+                                and seq2_str[len(seq1_str) - n - 1] != "-"
+                            ):
+                                end = len(seq1_str) - n
                                 break
 
+                        # for valid part
+                        for n in range(start, end):
+                            # connected with or to evaluate insertions or deletions
+                            if seq1_str[n] != seq2_str[n]:
+                                identical_flag = False
+                            else:
+                                overlapping_cnt += 1
+
                     if identical_flag is True and overlapping_cnt > 0:
-                        pairs.append(
+                        identical_pairs.append(
+                            tuple(sorted([str(seq1.id).strip(), str(seq2.id).strip()]))
+                        )
+                    elif identical_flag is False:
+                        different_pairs.append(
                             tuple(sorted([str(seq1.id).strip(), str(seq2.id).strip()]))
                         )
 
         # make phylogenetic distance matrix
         pdc = self.dendro_t.phylogenetic_distance_matrix().as_data_table()._data
 
-        # For each alignment pairs, find tree length
-        for pair in pairs:
+        # print(pdc)
+
+        # For each alignment identical_pairs, find tree length
+        for pair in identical_pairs:
             if pdc[pair[0]][pair[1]] > self.zero:
+                # print(f"Updated zero to {pdc[pair[0]][pair[1]]} from {pair}")
                 self.zero = pdc[pair[0]][pair[1]]
 
-        # returm maximum
+        diff_min = 999999
+        for pair in different_pairs:
+            if pdc[pair[0]][pair[1]] < diff_min:
+                # print(f"Updated diff_min to {pdc[pair[0]][pair[1]]} from {pair}")
+                diff_min = pdc[pair[0]][pair[1]]
+
+        if diff_min < self.zero:
+            self.zero = diff_min - 0.00000001
+
+        # I think also finding minimal distance between non-identical sequences are also needed
         return self.zero
 
     def reroot_outgroup(self, out):
@@ -352,17 +438,19 @@ def reroot_outgroup(self, out):
                 self.t.ladderize(direction=1)
                 self.outgroup_clade = outgroup_leaves[0]
             else:
-                print(f"[ERROR] no outgroup selected in {self.tree_name}")
+                print(
+                    f"{bold_red}[ERROR] no outgroup selected in {self.tree_name}{reset}"
+                )
                 raise Exception
 
             # If number of outgroup leaves and outgroup clade does not matches, paraphyletic
             if len(outgroup_leaves) != len(self.outgroup_clade):
                 print(
-                    f"[WARNING] outgroup seems to be paraphyletic in {self.tree_name}"
+                    f"{yellow}[WARNING] outgroup seems to be paraphyletic in {self.tree_name}{reset}"
                 )
 
         except:
-            print(f"[WARNING] no outgroup selected in {self.tree_name}")
+            print(f"{yellow}[WARNING] no outgroup selected in {self.tree_name}{reset}")
 
             outgroup_flag = False
             # if outgroup_clade is on the root side, reroot with other leaf temporarily and reroot again
@@ -383,20 +471,22 @@ def reroot_outgroup(self, out):
 
             if outgroup_flag is False:
                 # never erase this for debugging
-                print(f"[ERROR] Outgroup not selected in {self.tree_name}")
-                print(f"[ERROR] local variable outgroup_leaves : {outgroup_leaves}")
-                print(f"[ERROR] tree_info.outgroup : {self.outgroup}")
-                print(f"[ERROR] tree_info.outgroup_clade : {self.outgroup_clade}")
+                print(
+                    f"{bold_red}[ERROR] Outgroup not selected in {self.tree_name}{reset}"
+                )
+                print(
+                    f"{bold_red}[ERROR] local variable outgroup_leaves : {outgroup_leaves}{reset}"
+                )
+                print(f"{bold_red}[ERROR] tree_info.outgroup : {self.outgroup}{reset}")
+                print(
+                    f"{bold_red}[ERROR] tree_info.outgroup_clade : {self.outgroup_clade}{reset}"
+                )
                 raise Exception
 
         self.Tree_style.ts.show_leaf_name = True
         for node in self.t.traverse():
             node.img_style["size"] = 0  # removing circles whien size is 0
 
-        from funid.src.patch import patch
-
-        # To prevent ete3 bug for all processors
-        patch()
         self.t.render(f"{out}", tree_style=self.Tree_style.ts)
         self.Tree_style.ts.show_leaf_name = False
 
@@ -554,12 +644,13 @@ def collapse(self, collapse_info, clade, taxon):
                 collapse_info.n_query += 1
             else:
                 print(
-                    f"[ERROR] DEVELOPMENTAL ERROR : UNEXPECTED LEAF TYPE FOR {leaf.name}"
+                    f"{bold_red}[ERROR] DEVELOPMENTAL ERROR : UNEXPECTED LEAF TYPE FOR {leaf.name}{reset}"
                 )
                 print(self.tree_name)
                 print(f"Query: {sorted([FI.hash for FI in self.query_list])}")
                 print(f"DB: {sorted([FI.hash for FI in self.db_list])}")
                 print(f"Outgroup: {sorted([FI.hash for FI in self.outgroup])}")
+                raise Exception
 
     def decide_clade(self, clade, gene):
         taxon_dict = self.taxon_count(clade, gene)
@@ -685,10 +776,12 @@ def local_generate_collapse_information(self, clade, opt=None):
                         continue
                     else:
                         break
+            """
             print(
                 f"[INFO] Generating collapse information on {self.group} {self.gene} for taxon {taxon}          ",
                 end="\r",
             )
+            """
 
             if not (taxon in self.collapse_dict):
                 self.collapse_dict[taxon] = [collapse_info]
@@ -705,7 +798,7 @@ def local_generate_collapse_information(self, clade, opt=None):
         elif len(clade.children) == 2:
             for child_clade in clade.children:
                 # Calculate root distance between two childs to check flat
-                flat = (
+                self.flat = (
                     True if child_clade.dist <= self.opt.collapsedistcutoff else False
                 )
 
@@ -725,7 +818,7 @@ def local_generate_collapse_information(self, clade, opt=None):
         # if error (more than two branches or no branches)
         else:
             print(
-                f"[ERROR] DEVELOPMENTAL ERROR : FAILED TREE SEARCH ON LEAF {clade.children}"
+                f"{bold_red}[ERROR] DEVELOPMENTAL ERROR : FAILED TREE SEARCH ON LEAF {clade.children}{reset}"
             )
             raise Exception
         # end of tree_search
@@ -746,7 +839,9 @@ def consist(c):
                         query += 1
 
                 if db == 0 and query == 0:
-                    print(f"[ERROR] DEVELOPMENTAL ON CONSIST, {c} {db} {query}")
+                    print(
+                        f"{bold_red}[ERROR] DEVELOPMENTAL ON CONSIST, {c} {db} {query}{reset}"
+                    )
                     raise Exception
                 elif db == 0 and query != 0:
                     return "query"
@@ -766,7 +861,9 @@ def t(leaf):
                             self.funinfo_dict[leaf.name].bygene_species[gene],
                         )
                     except:
-                        print(leaf.name)
+                        print(
+                            f"{bold_red}[DEVELOPMENTAL ERROR] in leaf.name tree_interpretation.py line 869 {resety}"
+                        )
                         raise Exception
 
                 taxon_dict = {}
@@ -781,7 +878,7 @@ def t(leaf):
 
                     if len(taxon_dict) == 0:
                         print(
-                            f"[DEVELOPMENTAL ERROR] ERROR in tree_interpretation.py line 780 {taxon_dict}\n {c}"
+                            f"{bold_red}[DEVELOPMENTAL ERROR] in tree_interpretation.py line 884 {taxon_dict}\n {c}{reset}"
                         )
                         raise Exception
                     # If only one species in the clade
@@ -794,15 +891,23 @@ def t(leaf):
 
                 # If only query in the clade
                 elif mode == "query":
+                    for leaf in c:
+                        if ("", "") in taxon_dict:
+                            taxon_dict[("", "")] += 1
+                        else:
+                            taxon_dict[("", "")] = 1
+
+                    """
                     for leaf in c:
                         if t in taxon_dict:
                             taxon_dict[t(leaf)] += 1
                         else:
                             taxon_dict[t(leaf)] = 1
+                    """
 
                     if len(taxon_dict) == 0:
                         print(
-                            f"[DEVELOPMENTAL ERROR] Error in tree_interpretation.py line 795 {taxon_dict}\n {c}"
+                            f"{bold_red}[DEVELOPMENTAL ERROR] Error in tree_interpretation.py line 912 {taxon_dict}\n {c}{reset}"
                         )
                         raise Exception
                     elif len(taxon_dict) == 1:
@@ -833,7 +938,7 @@ def t(leaf):
                             condition = True
 
                         if condition is True:
-                            if not (t in taxon_dict):
+                            if not (t(leaf) in taxon_dict):
                                 taxon_dict[t(leaf)] = 1
                             else:
                                 taxon_dict[t(leaf)] += 1
@@ -885,7 +990,7 @@ def seperate_clade(clade, gene, clade_list):
 
                 return clade_list
 
-            ## Start of function solve_flat
+            ## Start of function: solve_flat
             root_dist = clade.dist
             root_support = clade.support
 
@@ -943,6 +1048,8 @@ def seperate_clade(clade, gene, clade_list):
 
                 # seperate this that order should not be affected by non-zero branch
                 tmp_final_clade = []
+
+                flat_issue_cnt = 0
                 for taxon in taxon_to_merge:
                     l = clade_dict[taxon]  # l for list of results
                     r_list = [r[1] for r in l]  # result clade list
@@ -951,10 +1058,13 @@ def seperate_clade(clade, gene, clade_list):
 
                     # concatenate within taxon clades
                     concatenated_clade = concat_all(
-                        clade_tuple=r_tuple, root_dist=self.zero, root_support=0
+                        clade_tuple=r_tuple, root_dist=0, root_support=0
                     )
                     tmp_final_clade.append(concatenated_clade)
 
+                    if taxon != ("", "") and concatenated_clade.dist <= self.zero:
+                        flat_issue_cnt += 1
+
                 final_clade = tmp_final_clade + final_clade
 
                 final = concat_all(
@@ -963,6 +1073,14 @@ def seperate_clade(clade, gene, clade_list):
                     root_support=root_support,
                 )
 
+                # If 2+ species are connected to flat, add flat issue
+                if flat_issue_cnt >= 2:
+                    # print(taxon_to_merge)
+                    for leaf in final:
+                        self.flat_clades.append(leaf.name)
+
+                # print(f"Status flat: {self.flat_clades}")
+
                 return final.copy("newick")
             ## end of solve flat
 
@@ -1008,7 +1126,9 @@ def get_bgcolor(self):
     ### Collapse tree
     def collapse_tree(self):
         # collect taxon string to decide in polishing
-        taxon_string_list = []
+        # taxon_string_list = []
+        taxon_string_dict = {}
+
         for collapse_taxon in self.collapse_dict:
             # collapse_info.taxon should be taxon
             for collapse_info in self.collapse_dict[collapse_taxon]:
@@ -1017,6 +1137,11 @@ def collapse_tree(self):
                 # if only one clade with same name exists
                 if len(self.collapse_dict[collapse_taxon]) == 1:
                     taxon_string = " ".join(collapse_info.taxon)
+                    taxon_string_dict[taxon_string] = (
+                        collapse_info.taxon[0],
+                        collapse_info.taxon[1],
+                        "",
+                    )
                 else:
                     collapse_info.clade_cnt = (
                         self.collapse_dict[collapse_taxon].index(collapse_info) + 1
@@ -1024,7 +1149,13 @@ def collapse_tree(self):
                     taxon_string = (
                         f'{" ".join(collapse_info.taxon)}-{collapse_info.clade_cnt}'
                     )
-                taxon_string_list.append(taxon_string)
+                    taxon_string_dict[taxon_string] = (
+                        collapse_info.taxon[0],
+                        collapse_info.taxon[1],
+                        f"-{collapse_info.clade_cnt}",
+                    )
+
+                # taxon_string_list.append(taxon_string)
 
                 taxon_text = TextFace(
                     taxon_string,
@@ -1126,16 +1257,16 @@ def collapse_tree(self):
                     position="float",
                 )
 
-        return taxon_string_list
+        return taxon_string_dict
 
     ### end of collapse tree
 
     ### edit svg image from initial output from ete3
-    def polish_image(self, out, taxon_string_list, genus_list):
-        # make it to tmp svg file and parse
-        # from funid.src.patch import patch
+    def polish_image(self, out, taxon_string_dict, genus_list):
+        # runname_group_gene.svg file enters here
+        # the tree has rectangle collapsed group, tmpseperator, and hash
 
-        # patch()
+        # Render it to temporary svg file and re-parse with xml
         self.t.render(f"{out}", tree_style=self.Tree_style.ts)
         tree_xml = ET.parse(f"{out}")
 
@@ -1171,7 +1302,8 @@ def polish_image(self, out, taxon_string_list, genus_list):
                 if text.text == "0.05":
                     text_type = "scale"
                 elif any(
-                    taxon.strip() == text.text.strip() for taxon in taxon_string_list
+                    taxon.strip() == text.text.strip()
+                    for taxon in taxon_string_dict.keys()
                 ):
                     text_type = "taxon"
                 else:
@@ -1181,12 +1313,9 @@ def polish_image(self, out, taxon_string_list, genus_list):
             text.set("y", f'{int(float(text.get("y")))-2}')
 
             if text_type == "taxon":
-                genus = get_genus_species(text.text, genus_list=genus_list)[0]
-                species = get_genus_species(text.text, genus_list=genus_list)[1]
-
-                rest = (
-                    text.text.replace(genus, "").replace(species, "").replace(" ", "")
-                )
+                genus = taxon_string_dict[text.text][0]
+                species = taxon_string_dict[text.text][1]
+                rest = taxon_string_dict[text.text][2]
 
                 # split genus, species, rest of parent into tspan
                 text.text = ""
@@ -1198,7 +1327,7 @@ def polish_image(self, out, taxon_string_list, genus_list):
 
                 if species != "":
                     tspan = ET.SubElement(text, "{http://www.w3.org/2000/svg}tspan")
-                    tspan.text = species + " "
+                    tspan.text = species
                     try:
                         int(species)
                     except:
@@ -1236,6 +1365,8 @@ def polish_image(self, out, taxon_string_list, genus_list):
                     except:
                         pass
 
+        # raise Exception
+
         # fit size of tree_xml to svg
         # find svg from tree_xml
         svg = list(tree_xml.iter("{http://www.w3.org/2000/svg}svg"))[0]
diff --git a/funid/src/tree_interpretation_pipe.py b/funvip/src/tree_interpretation_pipe.py
similarity index 94%
rename from funid/src/tree_interpretation_pipe.py
rename to funvip/src/tree_interpretation_pipe.py
index 457b54f..70607d2 100644
--- a/funid/src/tree_interpretation_pipe.py
+++ b/funvip/src/tree_interpretation_pipe.py
@@ -1,9 +1,9 @@
 # Performing multiple tree interpretation
 from ete3 import Tree
-from funid.src import tree_interpretation
-from funid.src.tool import initialize_path, get_genus_species
-from funid.src.hasher import encode, decode
-from funid.src.reporter import Singlereport
+from funvip.src import tree_interpretation
+from funvip.src.tool import initialize_path, get_genus_species
+from funvip.src.hasher import encode, decode
+from funvip.src.reporter import Singlereport
 from copy import deepcopy
 import pandas as pd
 import re
@@ -29,6 +29,7 @@ def pipe_module_tree_interpretation(
     hash_dict = V.dict_hash_name
     query_list = V.dict_dataset[group][gene].list_qr_FI
     outgroup = V.dict_dataset[group][gene].list_og_FI
+    partition = V.partition[group]
 
     # for unexpectively included sequence during clustering
     db_list = list(
@@ -78,9 +79,18 @@ def pipe_module_tree_interpretation(
 
     # Main phase
     # calculate zero distance with alignment
-    tree_info.calculate_zero(
-        f"{path.out_alignment}/hash/{opt.runname}_hash_trimmed_{group}_{gene}.fasta"
-    )
+    if gene == "concatenated":
+        tree_info.calculate_zero(
+            alignment_file=f"{path.out_alignment}/hash/{opt.runname}_hash_trimmed_{group}_{gene}.fasta",
+            gene=gene,
+            partition_dict=partition,
+        )
+    else:
+        tree_info.calculate_zero(
+            alignment_file=f"{path.out_alignment}/hash/{opt.runname}_hash_trimmed_{group}_{gene}.fasta",
+            gene=gene,
+            partition_dict=None,
+        )
 
     # Reroot outgroup and save original tree into image
     tree_info.reroot_outgroup(
@@ -103,7 +113,9 @@ def pipe_module_tree_interpretation(
 
     # Reconstruct flat branches if option given
     if opt.solveflat is True:
-        tree_info.t = tree_info.reconstruct(tree_info.t.copy("newick"), gene, opt)
+        tree_info.t = tree_info.reconstruct(
+            clade=tree_info.t.copy("newick"), gene=gene, opt=opt
+        )
 
     # reorder tree for pretty look
     tree_info.t.ladderize(direction=1)
@@ -129,25 +141,6 @@ def pipe_module_tree_interpretation(
         newick=True,
     )
 
-    # print(tree_info.t_publish)
-    """
-    for collapse_taxon in tree_info.collapse_dict:
-        for collapse_info in tree_info.collapse_dict[collapse_taxon]:
-            clade = collapse_info.clade
-            for c in tree_info.t_publish.traverse():
-                set1 = set(l1.name for l1 in c.iter_leaves())
-                set2 = set(l2.name for l2 in clade.iter_leaves())
-                if set1 == set2:
-                    print(True)
-
-                # if any of hash in set in query list
-                # color bgcolor with given color
-                # if taxon is new species
-                # color bg color with another color
-                # change taxon order
-    """
-    # raise Exception
-
     return tree_info
 
 
@@ -460,14 +453,14 @@ def pipe_module_tree_visualization(
     genus_list = tuple(genus_list)
 
     # Collapse tree branches for visualization
-    taxon_string_list = tree_info.collapse_tree()
+    taxon_string_dict = tree_info.collapse_tree()
 
     # print(f"taxon_string_list | {group} {gene}:\n {taxon_string_list}\n")
 
     # Polish tree image
     tree_info.polish_image(
         f"{path.out_tree}/{opt.runname}_{group}_{gene}.svg",
-        taxon_string_list,
+        taxon_string_dict,
         genus_list,
     )
 
@@ -538,6 +531,11 @@ def pipe_tree_interpretation(V, path, opt):
     # Generate tree_interpretation opt to run
     tree_interpretation_opt = []
 
+    # Reset bygene_species for rerun
+    for key in V.dict_hash_FI:  # funinfo_dict
+        for gene in V.dict_hash_FI[key].bygene_species:
+            V.dict_hash_FI[key].bygene_species[gene] = V.dict_hash_FI[key].ori_species
+
     for group in V.dict_dataset:
         for gene in V.dict_dataset[group]:
             logging.debug(f"pipe_tree_interpretation {group} {gene}")
@@ -595,6 +593,12 @@ def pipe_tree_interpretation(V, path, opt):
             for option in tree_interpretation_opt
         ]
 
+    # Gather flat branch issues
+    for tree_info in tree_info_list:
+        for flat_hash in tree_info.flat_clades:
+            FI = V.dict_hash_FI[flat_hash]
+            FI.issues.add(f"flat:{tree_info.gene}")
+
     synchronized_tree_info_list = synchronize(V, path, tree_info_list)
     tree_info_list = synchronized_tree_info_list
     # Generate visualization option to run
@@ -645,6 +649,10 @@ def pipe_tree_interpretation(V, path, opt):
             if cond > 0:
                 FI.final_species = singlereport.species_assigned
                 FI.species_identifier = singlereport.ambiguous
+
+                if singlereport.ambiguous > 0:
+                    FI.issues.add("polyphyly:concatenated")
+
                 if singlereport.flat is True:
                     FI.flat.append("concatenated")
 
@@ -658,6 +666,10 @@ def pipe_tree_interpretation(V, path, opt):
                 # In each gene tree, follow the group which taxon analyzed from concatenated tree
                 if hash_dict_analysis[singlereport.hash] == singlereport.group_analysis:
                     FI.bygene_species[singlereport.gene] = singlereport.species_assigned
+
+                    if singlereport.ambiguous > 0:
+                        FI.issues.add(f"polyphyly:{singlereport.gene}")
+
                     if singlereport.flat is True:
                         FI.flat.append(singlereport.gene)
             else:
diff --git a/funid/src/trim.py b/funvip/src/trim.py
similarity index 96%
rename from funid/src/trim.py
rename to funvip/src/trim.py
index 539324e..27ba97a 100644
--- a/funid/src/trim.py
+++ b/funvip/src/trim.py
@@ -1,5 +1,5 @@
-from funid.src import ext
-from funid.src.opt_generator import opt_generator
+from funvip.src import ext
+from funvip.src.opt_generator import opt_generator
 from Bio import AlignIO
 from Bio import SeqIO
 from time import sleep
@@ -9,7 +9,7 @@
 import shutil
 
 
-## Trimming function of FunID
+## Trimming function of FunVIP
 # As most of the trimming functions affects inside of alignment
 def trimming(alignment, out, path, opt):
     logging.debug("Entered trimming module")
@@ -57,7 +57,7 @@ def trimming(alignment, out, path, opt):
     return trimming_result
 
 
-# Trimming pipeline of FunID
+# Trimming pipeline of FunVIP
 def pipe_trimming(V, path, opt):
     trimming_opt = opt_generator(V, opt, path, step="trimming")
     # run multiprocessing start
diff --git a/funid/src/manage_input.py b/funvip/src/validate_input.py
similarity index 85%
rename from funid/src/manage_input.py
rename to funvip/src/validate_input.py
index 1d4b290..609a1ef 100644
--- a/funid/src/manage_input.py
+++ b/funvip/src/validate_input.py
@@ -12,7 +12,7 @@
 
 from unidecode import unidecode
 from Bio import SeqIO
-from funid.src.tool import (
+from funvip.src.tool import (
     initialize_path,
     get_genus_species,
     get_id,
@@ -21,9 +21,9 @@
 )
 from pathlib import Path
 
-from funid.src import save
-from funid.src.logics import isnewicklegal, isuniquecolumn, isvalidcolor
-from funid.src.hasher import decode, newick_legal, hash_funinfo_list
+from funvip.src import save
+from funvip.src.logics import isnewicklegal, isuniquecolumn, isvalidcolor
+from funvip.src.hasher import decode, newick_legal, hash_funinfo_list
 
 
 ## newick illegal characters
@@ -32,7 +32,8 @@
 # fmt: on
 
 
-# default funinfo class
+# Default funinfo class
+# Abbreviated as FI in most of the codes
 class Funinfo:
     def __init__(self):
         self.original_id = ""  # original id, can be newick illegal
@@ -53,7 +54,8 @@ def __init__(self):
         self.seq = {}
         self.unclassified_seq = []
         self.color = None  # color for highlighting in phylogenetic tree
-        self.flat = []  # list of flat genes
+        self.flat = []  # list of flat species in concatenated tree
+        self.issues = set()  # list of issues to this FI
 
     def update_seqrecord(self, seq, gene=None):
         flag = 0
@@ -106,7 +108,8 @@ def update_genus(self, genus):
             genus = ""
 
         # Genus with space causes error while mafft
-        genus = genus.strip().replace(" ", "_")
+        # Genus with slash causes error while tree construction
+        genus = genus.strip().replace(" ", "_").replace("/", "_")
         genus = manage_unicode(genus, column="Genus")
 
         # Check ambiguity
@@ -130,7 +133,10 @@ def update_ori_species(self, species):
         # Try to solve illegal unicode characters
         if pd.isnull(species):
             species = ""
-        species = species.strip()
+
+        # Genus with space causes error while mafft
+        # Genus with slash causes error while tree construction
+        species = species.strip().replace(" ", "_").replace("/", "_")
         species = manage_unicode(species, column="Species")
 
         # Check ambiguity
@@ -217,6 +223,51 @@ def update_id(self, id_, regexs=None):
     def update_hash(self, n):
         self.hash = f"HS{n}HE"
 
+    def get_issue_str(self):
+        flat_issues = []
+        alignfail_issues = []
+        polyphyly_issues = []
+        other_issues = []
+        for issue in self.issues:
+            if issue.startswith("flat"):
+                flat_issues.append(issue)
+            elif issue.startswith("alignfail"):
+                alignfail_issues.append(issue)
+            elif issue.startswith("polyphyly"):
+                polyphyly_issues.append(issue)
+            else:
+                other_issues.append(issue)
+
+        issues_string = ", ".join(other_issues)
+        if len(flat_issues) > 0:
+            flat_issue_str = "flat:" + "/".join(
+                sorted([issue.split(":")[1] for issue in flat_issues])
+            )
+            if len(issues_string) == 0:
+                issues_string = flat_issue_str
+            else:
+                issues_string += f", {flat_issue_str}"
+
+        if len(alignfail_issues) > 0:
+            alignfail_issue_str = "alignfail:" + "/".join(
+                sorted([issue.split(":")[1] for issue in alignfail_issues])
+            )
+            if len(issues_string) == 0:
+                issues_string = alignfail_issue_str
+            else:
+                issues_string += f", {alignfail_issue_str}"
+
+        if len(polyphyly_issues) > 0:
+            polyphyly_issue_str = "polyphyly:" + "/".join(
+                sorted([issue.split(":")[1] for issue in polyphyly_issues])
+            )
+            if len(issues_string) == 0:
+                issues_string = polyphyly_issue_str
+            else:
+                issues_string += f", {polyphyly_issue_str}"
+
+        return issues_string
+
     def __repr__(self):
         return f"FI: {self.id}"
 
@@ -254,9 +305,10 @@ def input_fasta(path, opt, fasta_list, funinfo_dict, datatype):
         logging.info(f"{file}: Fasta file")
 
         error_flag = 0
+
         try:
-            fasta_list = list(SeqIO.parse(file, "fasta"))
-            for seq in fasta_list:
+            seq_list = list(SeqIO.parse(file, "fasta"))
+            for seq in seq_list:
                 if not opt.regex == None:
                     id_ = get_id(seq.description, tuple(opt.regex))
                 else:
@@ -303,6 +355,10 @@ def input_fasta(path, opt, fasta_list, funinfo_dict, datatype):
         except:
             logging.warning(f"{file} does not seems to be valid fasta file skipping")
 
+        if len(seq_list) == 0:
+            logging.error(f"Fasta file {file} seems to be empty please check")
+            raise Exception
+
         if error_flag < 0:
             raise Exception
 
@@ -311,6 +367,8 @@ def input_fasta(path, opt, fasta_list, funinfo_dict, datatype):
 
 # getting datafile from excel or tabular file
 def input_table(funinfo_dict, path, opt, table_list, datatype):
+    # Whether to check if GenMine has run
+    GenMine_flag = 0
     string_error = 0
 
     initialize_path(path)  # this one is ugly
@@ -441,12 +499,17 @@ def input_table(funinfo_dict, path, opt, table_list, datatype):
 
             # if NCBI accessions detected in sequence part, download it
             if len(download_set) > 0:
-                print(f"{path.GenMine}")
+                GenMine_flag = 1
+                if opt.email == "":
+                    logging.error(
+                        "Your database includes GenBank accession but email not provided. --email is required to connect to GenBank"
+                    )
+                    raise Exception
 
                 logging.info(
                     f"Running GenMine to download {len(download_set)} sequences from GenBank"
                 )
-                logging.info(download_set)
+                # logging.info(download_set)
 
                 # Write GenMine input file
                 with open(f"{path.GenMine}/Accessions.txt", "w") as fg:
@@ -463,7 +526,8 @@ def input_table(funinfo_dict, path, opt, table_list, datatype):
                     GenMine_path = path.GenMine
 
                 cmd = f"GenMine -c {accession_path} -o {GenMine_path} -e {opt.email}"
-                logging.info(f"{cmd}")
+                logging.info(cmd)
+
                 return_code = subprocess.call(cmd, shell=True)
 
                 if return_code != 0:
@@ -534,6 +598,15 @@ def update_from_GenMine(string):
             lambda x: manage_unicode(str(x), column="ID/Accession")
         )
 
+        # To prevent errors on genus / spcies column
+        # if this function operates with query mode, there might be no genus or species column
+        if "genus" in df.columns:
+            df["genus"] = df["genus"].apply(lambda x: x.replace(" ", "_"))
+            df["genus"] = df["genus"].apply(lambda x: x.replace("/", "_"))
+        if "species" in df.columns:
+            df["species"] = df["species"].apply(lambda x: x.replace(" ", "_"))
+            df["species"] = df["species"].apply(lambda x: x.replace("/", "_"))
+
         # Empty id check
         empty_error = []
         for n, acc in enumerate(df["id"]):
@@ -625,17 +698,17 @@ def update_from_GenMine(string):
         # After successfully parsed this table, save it
         save.save_df(
             df,
-            f"{path.out_db}/Saved_{'.'.join(table.split('/')[-1].split('.')[:-1])}.{opt.matrixformat}",
-            fmt=opt.matrixformat,
+            f"{path.out_db}/Saved_{'.'.join(table.split('/')[-1].split('.')[:-1])}.{opt.tableformat}",
+            fmt=opt.tableformat,
         )
-    return funinfo_dict
+    return funinfo_dict, GenMine_flag
 
 
 def db_input(funinfo_dict, opt, path) -> list:
     # Get DB input
     logging.info(f"Input DB list: {opt.db}")
 
-    funinfo_dict = input_table(
+    funinfo_dict, GenMine_flag = input_table(
         funinfo_dict=funinfo_dict, path=path, opt=opt, table_list=opt.db, datatype="db"
     )
 
@@ -661,7 +734,7 @@ def db_input(funinfo_dict, opt, path) -> list:
             f"Sequences in database of some group has lower number than MINIMUM_OUTGROUP_COUNT. It may cause error when outgroup selection, or may select not most appropriate outgroup to group. Please lower number of MINIMUM_OUTGROUP_COUNT in option or add more sequences to these groups"
         )
 
-    return funinfo_dict
+    return funinfo_dict, GenMine_flag
 
 
 def query_input(funinfo_dict, opt, path):
@@ -679,7 +752,7 @@ def query_input(funinfo_dict, opt, path):
         )
     ]
 
-    funinfo_dict = input_table(
+    funinfo_dict, GenMine_flag = input_table(
         funinfo_dict=funinfo_dict,
         path=path,
         opt=opt,
@@ -705,7 +778,7 @@ def query_input(funinfo_dict, opt, path):
         f"Total {len([funinfo_dict[key].datatype =='query' for key in funinfo_dict.keys()])} sequences parsed from query"
     )
 
-    return funinfo_dict
+    return funinfo_dict, GenMine_flag
 
 
 # combined db and query input
@@ -713,10 +786,19 @@ def data_input(V, R, opt, path):
     funinfo_dict = {}
 
     # get database input
-    funinfo_dict = db_input(funinfo_dict=funinfo_dict, opt=opt, path=path)
+    funinfo_dict, GenMine_flag_db = db_input(
+        funinfo_dict=funinfo_dict, opt=opt, path=path
+    )
 
     # get query input
-    funinfo_dict = query_input(funinfo_dict=funinfo_dict, opt=opt, path=path)
+    funinfo_dict, GenMine_flag_query = query_input(
+        funinfo_dict=funinfo_dict, opt=opt, path=path
+    )
+
+    if GenMine_flag_db != 0 or GenMine_flag_query != 0:
+        GenMine_flag = 1
+    else:
+        GenMine_flag = 0
 
     # combine all data
     V.list_FI = [funinfo_dict[key] for key in funinfo_dict]
@@ -728,7 +810,17 @@ def data_input(V, R, opt, path):
     for FI in V.list_FI:
         V.dict_hash_FI[FI.hash] = FI
 
-    return V, R, opt
+    # If all genes are empty, indicate them
+    for FI in V.list_FI:
+        flag_available_gene = 0
+        for gene in FI.seq:
+            if FI.seq[gene] != "":
+                flag_available_gene = 1
+
+        if flag_available_gene == 0:
+            FI.issues.add("noseq")
+
+    return V, R, opt, GenMine_flag
 
 
 # This function updates initial input query files to saved files with matrices and downloaded sequences
@@ -750,5 +842,5 @@ def update_queryfile(V, path, opt):
 
     df_out = pd.DataFrame(out_dict)
     save.save_df(
-        df_out, f"{path.out_query}/Saved_query.{opt.matrixformat}", fmt=opt.matrixformat
+        df_out, f"{path.out_query}/Saved_query.{opt.tableformat}", fmt=opt.tableformat
     )
diff --git a/funid/src/validate_option.py b/funvip/src/validate_option.py
similarity index 94%
rename from funid/src/validate_option.py
rename to funvip/src/validate_option.py
index 84fecd4..05abd6b 100644
--- a/funid/src/validate_option.py
+++ b/funvip/src/validate_option.py
@@ -7,11 +7,11 @@
 import datetime
 import re
 import psutil
-from funid.src.logics import isvalidcolor
-from funid.src.tool import check_avx
+from funvip.src.logics import isvalidcolor
+from funvip.src.tool import check_avx
 
 
-# FunID Option class definition
+# FunVIP Option class definition
 class Option:
     # Method option
     class Method_Option:
@@ -87,7 +87,7 @@ def __init__(self):
         self.avx = True
         self.cachedb = True
         self.usecache = True
-        self.matrixformat = "csv"
+        self.tableformat = "csv"
         self.nosearchresult = False
 
         # Method options
@@ -173,8 +173,8 @@ def update_from_preset(self, preset_file):
                 self.cachedb = parser_dict[key]
             elif key.lower() in ("usecache"):
                 self.usecache = parser_dict[key]
-            elif key.lower() in ("matrixformat"):
-                self.matrixformat = parser_dict[key]
+            elif key.lower() in ("tableformat"):
+                self.tableformat = parser_dict[key]
             elif key.lower() in ("nosearchresult"):
                 self.nosearchresult = parser_dict[key]
             elif key.lower() in ("confident"):
@@ -547,8 +547,8 @@ def update_from_parser(self, parser):
             pass
 
         try:
-            if not parser.matrixformat is None:
-                self.matrixformat = parser.matrixformat
+            if not parser.tableformat is None:
+                self.tableformat = parser.tableformat
         except:
             pass
 
@@ -647,7 +647,12 @@ def validate(self):
                 list_error.append(f"Type for email should be string")
             email_pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
             if not (re.match(email_pattern, self.email)):
-                list_error.append(f"Email {self.email} is not a valid email")
+                if self.email == "":
+                    list_warning.append(
+                        f"Email not provided. If your database includes GenBank accession, please provide email"
+                    )
+                else:
+                    list_error.append(f"Email {self.email} is not a valid email")
 
         # api
         # Check if api number is in valid format
@@ -720,7 +725,7 @@ def validate(self):
 
         if float(self.memory[:-1]) < 4:
             list_warning.append(
-                f"At least 4G of memory required for FunID. Try using 4G"
+                f"At least 4G of memory required for FunVIP. Try using 4G"
             )
             if psutil.virtual_memory().total / (1024**3) < 4:
                 list_error.append(f"Less than 4G of memory (RAM) detected ! Aborted")
@@ -819,7 +824,7 @@ def validate(self):
         else:
             if not self.step is None:
                 list_warning.append(
-                    f"As --continue is not designated, step will be ignored"
+                    f"--continue is not designated, --step will be ignored"
                 )
 
         # level
@@ -1217,7 +1222,7 @@ def validate(self):
         # If IQTREE selected and bootstrap number under 1000, change to 1000
         if self.method.tree == "fasttree":
             list_warning.append(
-                f"Fasttree does not supports bootstrap, but bootstrap option selected. Will be ignored"
+                f"Fasttree does not supports bootstrap. --bootstrap will be ignored"
             )
             self.bootstrap = None
         else:
@@ -1432,23 +1437,23 @@ def validate(self):
         else:
             self.usecache = True
 
-        # matrixformat
+        # tableformat
         # should be either [csv, xlsx, parquet, feather]
         # if ftr, change it to feather
         # if excel, change it to xlsx
         try:
-            self.matrixformat = str(self.matrixformat)
+            self.tableformat = str(self.tableformat)
             if not (
-                self.matrixformat.lower()
+                self.tableformat.lower()
                 in ("csv", "tsv", "xlsx", "parquet", "ftr", "feather")
             ):
                 list_error.append(
-                    "matrixformat should be one of csv, tsv, xlsx, parquet, ftr, or feather"
+                    "tableformat should be one of csv, tsv, xlsx, parquet, ftr, or feather"
                 )
 
         except:
             list_error.append(
-                "matrixformat should be one of csv, tsv, xlsx, parquet, ftr, or feather"
+                "tableformat should be one of csv, tsv, xlsx, parquet, ftr, or feather"
             )
 
         # nosearchresult
@@ -1461,18 +1466,29 @@ def validate(self):
         # Printing logs while parsing validate options
         # Written in print functions, because logging can be loaded after option parsing
         print("--INFO--")
-        for info in list_info:
-            print(f"[INFO] {info}")
+        if len(list_info) > 0:
+            for info in list_info:
+                print(f"[INFO] {info}")
+        else:
+            print(f"[INFO] No information to declare during input validation")
 
         print("\n")
         print("--WARNING--")
-        for warning in list_warning:
-            print(f"[WARNING] {warning}")
+        if len(list_warning) > 0:
+            for warning in list_warning:
+                print(f"[WARNING] {warning}")
+        else:
+            print(f"[INFO] No warnings to declare during input validation")
 
         print("\n")
         print("--ERROR--")
-        for error in list_error:
-            print(f"[ERROR] {error}")
+        if len(list_error) > 0:
+            for error in list_error:
+                print(f"[ERROR] {error}")
+        else:
+            print(f"[INFO] No errors to declare during input validation")
+
+        print("\n\n")
 
         if len(list_error) > 0:
             raise Exception
@@ -1497,8 +1513,13 @@ def initialize_option(parser, path_run):
     path_test = f"{os.path.dirname(__file__)}/../test_dataset"
     path_preset = f"{os.path.dirname(__file__)}/../preset"
 
+    overwrite_preset = None
     # *1 - if test option selected
     if not (parser.test is None):
+        # If preset should be overwrited, save it
+        if not (parser.preset is None):
+            overwrite_preset = copy.deepcopy(parser.preset)
+
         # *1 - if test value is valid dataset
         if parser.test.lower() in os.listdir(path_test):
             if os.path.exists(f"{path_test}/{parser.test.lower()}/preset.yaml"):
@@ -1544,6 +1565,26 @@ def initialize_option(parser, path_run):
                 print(f"Cannot find preset file : {parser.preset}")
                 raise Exception
 
+    # Overwrite test preset if needed
+    if not (overwrite_preset is None):
+        if str(overwrite_preset).lower() == "fast":  # *1 - fast mode
+            overwrite_preset = f"{path_preset}/fast.yaml"  # - connect to fast.yaml
+            print("Using fast preset as default option")
+            opt.update_from_preset(overwrite_preset)
+        elif str(overwrite_preset).lower() == "accurate":  # *1 - accurate mode
+            overwrite_preset = (
+                f"{path_preset}/accurate.yaml"  # - connect to accurate.yaml
+            )
+            print("Using accurate preset as default option")
+            opt.update_from_preset(overwrite_preset)
+        else:
+            if os.path.exists(overwrite_preset):
+                print(f"[DEBUG] {overwrite_preset}")
+                opt.update_from_preset(overwrite_preset)
+            else:
+                print(f"Cannot find preset file : {overwrite_preset}")
+                raise Exception
+
     ### Then, update other options in parser
     opt.update_from_parser(ori_parser)
 
@@ -1551,17 +1592,17 @@ def initialize_option(parser, path_run):
     list_info, list_warning, list_error = opt.validate()
 
     ### stdout input options
-    print("[OPTIONS INPUT]")
+    # print("[OPTIONS INPUT]")
 
     ## Print and log output options
     for attr, value in opt.__dict__.items():
         if isinstance(value, (str, float, bool, int, list, type(None))):
             list_info.append(f"Option {attr} : {value}")
-            print(f"Option {attr} : {value}")
+            # print(f"Option {attr} : {value}")
         else:
             for attr_, value_ in value.__dict__.items():
                 list_info.append(f"Option {attr}-{attr_}: {value_}")
-                print(f"Option {attr}-{attr_}: {value_}")
+                # print(f"Option {attr}-{attr_}: {value_}")
 
     print("------------------------------------")
 
diff --git a/funid/src/validation.py b/funvip/src/validation.py
similarity index 100%
rename from funid/src/validation.py
rename to funvip/src/validation.py
index eb92771..67b1df2 100644
--- a/funid/src/validation.py
+++ b/funvip/src/validation.py
@@ -6,9 +6,9 @@
 
 # from .logger import Mes
 
+
 # validate if string is good sequence
 def validate_seq(seqstring):
-
     if seqstring.startswith(">"):
         tmp = StringIO(seqstring)
 
diff --git a/funvip/src/version.py b/funvip/src/version.py
new file mode 100644
index 0000000..6a70992
--- /dev/null
+++ b/funvip/src/version.py
@@ -0,0 +1,337 @@
+# Version management module
+import sys
+import subprocess
+from importlib.metadata import version
+
+# import
+
+
+"""
+version = {
+    "FunVIP": "0.3.19.0.1.3",
+    "BLASTn": "",
+    "MMseqs2": "",
+    "MAFFT": "",
+    "TrimAl": "",
+    "Gblocks": "0.91b",
+    "FastTree": "",
+    "IQTREE2": "",
+    "RAxML": "",
+}
+"""
+
+
+class Version:
+    def __init__(self, opt, path):
+        self.FunVIP = ""
+        self.GenMine = ""
+        self.BLASTn = ""
+        self.MMseqs2 = ""
+        self.MAFFT = ""
+        self.trimAl = ""
+        self.Gblocks = "0.91b"
+        self.Modeltest_NG = ""
+        self.FastTree = ""
+        self.IQTREE2 = ""
+        self.RAxML = ""
+
+        # For windows platform
+        if sys.platform == "win32":
+            ### FunVIP
+            self.FunVIP = version("FunVIP")
+
+            ### GenMine
+            self.GenMine = version("GenMine")
+
+            ### BLASTn
+            CMD = [f"{path.sys_path}/external/BLAST_Windows/bin/blastn.exe", "-version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            ## tableformat
+            # blastn: blastn: 2.12.0+
+            #   Package: blast 2.12.0, build Jun  4 2021 03:25:07
+            self.BLASTn = stdout_str.split("\n")[0].split(" ")[1].strip()
+            # print("BLASTn", self.BLASTn)
+
+            ### MMSeqs2
+            CMD = [
+                f"{path.sys_path}/external/mmseqs_Windows/mmseqs.bat",
+                "-h",
+            ]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.MMseqs2 = stdout_str.split("Version: ")[1].split("\n")[0].strip()
+            # print("MMseqs2", self.MMseqs2)
+
+            ### MAFFT
+            CMD = [
+                f"{path.sys_path}/external/MAFFT_Windows/mafft-win/mafft.bat",
+                "--version",
+            ]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            # MAFFT, output is on the stderr
+            stderr_str = stderr.decode("utf-8")
+            self.MAFFT = stderr_str.split("\n")[-2].split(" ")[0].strip()
+            # print("MAFFT", self.MAFFT)
+
+            ### TrimAl
+            CMD = [
+                f"{path.sys_path}/external/trimal.v1.4/trimAl/bin/trimal.exe",
+                "--version",
+            ]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.trimAl = stdout_str.split("\n")[1].split(" ")[1]
+            # print("trimAl", self.trimAl)
+
+            ### Modeltest-ng
+            ## Not supported in Windows
+            self.Modeltest_NG = "not supported"
+
+            ### FastTree
+            ## Also use stderr of FastTree
+            CMD = [
+                f"{path.sys_path}/external/FastTree_Windows/FastTree.exe",
+                "-expert",
+            ]
+
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            stderr_str = stderr.decode("utf-8")
+            self.FastTree = stderr_str.split(" ")[4]
+            # print("FastTree", self.FastTree)
+
+            ### IQTREE
+            CMD = [
+                f"{path.sys_path}/external/iqtree/bin/iqtree2.exe",
+                "--version",
+            ]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.IQTREE2 = stdout_str.split(" ")[3]
+            # print("IQTREE2", self.IQTREE2)
+
+            ### RAxML
+            if opt.avx is True:
+                CMD = [
+                    f"{path.sys_path}/external/RAxML_Windows/raxmlHPC-PTHREADS-AVX2.exe",
+                    "-v",
+                ]
+            else:
+                CMD = [
+                    f"{path.sys_path}/external/RAxML_Windows/raxmlHPC-PTHREADS-SSE3.exe",
+                    "-v",
+                ]
+
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.RAxML = stdout_str.split("\n")[2].split(" ")[4]
+            # print("RAxML", self.RAxML)
+
+        # For apple silicon platform
+        elif sys.platform == "darwin":
+            ### FunVIP
+            self.FunVIP = version("FunVIP")
+
+            ### GenMine
+            self.GenMine = version("GenMine")
+
+            ### BLASTn
+            CMD = ["blastn", "-version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            ## Format
+            # blastn: blastn: 2.12.0+
+            #   Package: blast 2.12.0, build Jun  4 2021 03:25:07
+            self.BLASTn = stdout_str.split("\n")[0].split(" ")[1].strip()
+            # print("BLASTn", self.BLASTn)
+
+            ### MMSeqs2
+            CMD = ["mmseqs", "-h"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.MMseqs2 = stdout_str.split("Version: ")[1].split("\n")[0].strip()
+            # print("MMseqs2", self.MMseqs2)
+
+            ### MAFFT
+            CMD = ["mafft", "--version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            # MAFFT, output is on the stderr
+            stderr_str = stderr.decode("utf-8")
+            self.MAFFT = stderr_str.split("\n")[-2].split(" ")[0].strip()
+            # print("MAFFT", self.MAFFT)
+
+            ### TrimAl
+            CMD = ["trimal", "--version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.trimAl = stdout_str.split("\n")[1].split(" ")[1]
+            # print("trimAl", self.trimAl)
+
+            ### Modeltest-ng
+            ## Not supported in apple silicon
+            self.Modeltest_NG = "not supported"
+
+            ### FastTree
+            ## Also use stderr of FastTree
+            CMD = ["FastTree", "-expert"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stderr_str = stderr.decode("utf-8")
+            self.FastTree = stderr_str.split(" ")[4]
+            # print("FastTree", self.FastTree)
+
+            ### IQTREE
+            CMD = ["iqtree", "--version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.IQTREE2 = stdout_str.split(" ")[3]
+            # print("IQTREE2", self.IQTREE2)
+
+            ### RAxML
+            if opt.avx is True:
+                CMD = ["raxmlHPC-PTHREADS-AVX2", "-v"]
+            else:
+                CMD = ["raxmlHPC-PTHREADS-SSE3", "-v"]
+
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.RAxML = stdout_str.split("\n")[2].split(" ")[4]
+            # print("RAxML", self.RAxML)
+
+        # For linux platform
+        else:
+            ### FunVIP
+            self.FunVIP = version("FunVIP")
+
+            ### GenMine
+            self.GenMine = version("GenMine")
+
+            ### BLASTn
+            CMD = ["blastn", "-version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            ## Format
+            # blastn: blastn: 2.12.0+
+            #   Package: blast 2.12.0, build Jun  4 2021 03:25:07
+            self.BLASTn = stdout_str.split("\n")[0].split(" ")[1].strip()
+            # print("BLASTn", self.BLASTn)
+
+            ### MMSeqs2
+            CMD = ["mmseqs", "-h"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.MMseqs2 = stdout_str.split("Version: ")[1].split("\n")[0].strip()
+            # print("MMseqs2", self.MMseqs2)
+
+            ### MAFFT
+            CMD = ["mafft", "--version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            # MAFFT, output is on the stderr
+            stderr_str = stderr.decode("utf-8")
+            self.MAFFT = stderr_str.split("\n")[-2].split(" ")[0].strip()
+            # print("MAFFT", self.MAFFT)
+
+            ### TrimAl
+            CMD = ["trimal", "--version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.trimAl = stdout_str.split("\n")[1].split(" ")[1]
+            # print("trimAl", self.trimAl)
+
+            ### Modeltest-ng
+            ## Not supported in Windows
+            CMD = ["modeltest-ng", "--version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.Modeltest_NG = stdout_str.split("ModelTest-NG ")[1].split(" ")[0]
+
+            ### FastTree
+            ## Also use stderr of FastTree
+            CMD = ["FastTree", "-expert"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stderr_str = stderr.decode("utf-8")
+            self.FastTree = stderr_str.split(" ")[4]
+            # print("FastTree", self.FastTree)
+
+            ### IQTREE
+            CMD = ["iqtree", "--version"]
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.IQTREE2 = stdout_str.split(" ")[3]
+            # print("IQTREE2", self.IQTREE2)
+
+            ### RAxML
+            if opt.avx is True:
+                CMD = ["raxmlHPC-PTHREADS-AVX2", "-v"]
+            else:
+                CMD = ["raxmlHPC-PTHREADS-SSE3", "-v"]
+
+            result = subprocess.Popen(
+                CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
+            stdout, stderr = result.communicate()
+            stdout_str = stdout.decode("utf-8")
+            self.RAxML = stdout_str.split("\n")[2].split(" ")[4]
diff --git a/legacy/legacy_setup.py b/legacy/legacy_setup.py
new file mode 100644
index 0000000..5078a68
--- /dev/null
+++ b/legacy/legacy_setup.py
@@ -0,0 +1,91 @@
+# FunVIP/setup.py
+from setuptools import setup, find_packages
+from sys import platform
+import subprocess
+
+name = "FunVIP"
+__version__ = "0.3.20.0.0.0"
+
+
+# Define common setup options
+setup_options = dict(
+    name=name,
+    version=__version__,
+    description="Fungal Validation & Identification Pipeline",
+    author="Changwan Seo",
+    author_email="wan101010@snu.ac.kr",
+    url="https://github.com/Changwanseo/FunVIP",
+    python_requires="<3.13, >3.8",
+    packages=[
+        "funvip",
+        "funvip.src",
+        "funvip.data",
+        "funvip.test_dataset",
+        "funvip.preset",
+    ],
+    install_requires=[
+        "biopython==1.78",
+        "ete3==3.1.3",
+        "Cython",
+        "dendropy",
+        "GenMine>=1.0.13",
+        "lxml",
+        "matplotlib",
+        "numpy",
+        "openpyxl==3.0.9",
+        "pandas==1.4.2",
+        "plotly==5.9.0",
+        "psutil",
+        "pyyaml",
+        "sip>=4.19.4",
+        "scikit-learn",
+        "scipy",
+        "tabulate",
+        "unidecode==1.2.0",
+        "xlrd==2.0.1",
+        "xlsxwriter",
+        "xmltodict==0.12.0",
+    ],
+    zip_safe=False,
+    entry_points={
+        "console_scripts": ["FunID = funvip.main:main", "FunVIP = funvip.main:main"]
+    },
+    package_dir={"funvip": "funvip"},
+    package_data={
+        "funvip.data": ["*.xlsx", "*.txt"],
+        "funvip.test_dataset": ["**"],
+        "funvip.preset": ["**"],
+    },
+    include_package_data=True,
+    license="GPL3",
+)
+
+# Modify setup options based on platform
+if platform == "darwin":
+    pass
+elif platform == "win32":
+    setup_options["packages"].append("funvip.external")
+    setup_options["install_requires"].append("PyQt5>=5.9.2")
+    setup_options["package_data"]["funvip.external"] = ["**"]
+else:  # linux maybe
+    setup_options["install_requires"].append("PyQt5>=5.9.2")
+
+
+def run_tests():
+    result = subprocess.run(["FunVIP", "-h"], capture_output=True, text=True)
+    print(result.stdout)
+    print(result.stderr)
+
+
+# Change setup options by platform
+if platform == "win32":
+    setup_options["packages"].append("funvip.external")
+    setup_options["package_data"]["funvip.external"] = ["**"]
+else:
+    pass
+
+print(setup_options)
+
+
+# Run setup
+setup(**setup_options, cmdclass={"test": run_tests})
diff --git a/funid/src/patch.py b/legacy/patch.py
similarity index 99%
rename from funid/src/patch.py
rename to legacy/patch.py
index 21cc207..2285ce5 100644
--- a/funid/src/patch.py
+++ b/legacy/patch.py
@@ -1,12 +1,13 @@
 # ete3 patch
+# After using ete3 3.1.3, we don't need this anymore
 from __future__ import absolute_import
 from __future__ import print_function
 import ete3
 from copy import deepcopy
 
+
 # Patch for ete3 3.1.2 colliding with higher version PyQt5
 def save(scene, imgName, w=None, h=None, dpi=90, take_region=False, units="px"):
-
     import colorsys
     import random
     import re
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..16e45f5
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,53 @@
+[project]
+name = "FunVIP"
+version = "0.3.21"
+description = "Fungal Validation & Identification Pipeline"
+authors = [{name = "Changwan Seo", email = "wan101010@snu.ac.kr"}]
+urls = { "Homepage" = "https://github.com/Changwanseo/FunVIP" }
+requires-python = ">3.8, <3.13"
+license = {text = "GPL-3.0"}
+dependencies = [
+    "biopython==1.78",
+    "ete3==3.1.3",
+    "Cython",
+    "dendropy",
+    "GenMine>=1.0.13",
+    "lxml",
+    "matplotlib",
+    "numpy",
+    "openpyxl==3.0.9",
+    "pandas==1.4.2",
+    "plotly==5.9.0",
+    "psutil",
+    "pyyaml",
+    "sip>=4.19.4",
+    "scikit-learn",
+    "scipy",
+    "tabulate",
+    "unidecode==1.2.0",
+    "xlrd==2.0.1",
+    "xlsxwriter",
+    "xmltodict==0.12.0",
+    "PyQt5>=5.9.2; sys_platform!='darwin'"
+]
+
+[project.scripts]
+FunID = "funvip.main:main"
+FunVIP = "funvip.main:main"
+
+[tool.setuptools]
+include-package-data = true
+
+[tool.setuptools.packages.find]
+where = ["."]
+include = ["funvip"]
+
+
+
+[build-system]
+requires = ["setuptools", "setuptools-ext", "wheel"]
+build-backend = "setuptools_ext"
+
+[metadata]
+obsoletes-dist = "FunID"
+
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 24d5524..0000000
--- a/setup.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# FunID/setup.py
-from setuptools import setup, find_packages
-from sys import platform
-import subprocess
-
-name = "FunID"
-__version__ = "0.3.18.6.2"
-release = "0.3.18.6.2"
-
-
-## Default setup options
-# PyQt5 should be independently installed for m1 architecture
-if platform == "darwin":
-    setup_options = dict(
-        name=name,
-        version=__version__,
-        description="Fungal Identification Pipeline",
-        author="Changwan Seo",
-        author_email="wan101010@snu.ac.kr",
-        url="https://github.com/Changwanseo/FunID",
-        python_requires="<3.13, >3.8",
-        packages=[
-            "funid",
-            "funid.src",
-            "funid.data",
-            "funid.test_dataset",
-            "funid.preset",
-        ],
-        install_requires=[
-            "biopython==1.78",
-            "ete3==3.1.3",
-            "Cython",
-            "dendropy",
-            "GenMine>=1.0.11",
-            "lxml",
-            "matplotlib",
-            "numpy",
-            "openpyxl==3.0.9",
-            "pandas==1.4.2",
-            "plotly==5.9.0",
-            "psutil",
-            "PyQt5>=5.9.2",
-            "pyyaml",
-            "sip>=4.19.4",
-            "scikit-learn",
-            "scipy",
-            "tabulate",
-            "unidecode==1.2.0",
-            "xlrd==2.0.1",
-            "xlsxwriter",
-            "xmltodict==0.12.0",
-        ],
-        zip_safe=False,
-        entry_points={"console_scripts": ["FunID = funid.main:main"]},
-        package_dir={"funid": "funid"},
-        package_data={
-            "funid.data": ["*.xlsx", "*.txt"],
-            "funid.test_dataset": ["**"],
-            "funid.preset": ["**"],
-        },
-        include_package_data=True,
-        license="GPL3",
-    )
-
-else:
-    # Windows: python 3.12, 3.11, 3.10, 3.9 pass
-    # Linux: python 3.10 pass
-    setup_options = dict(
-        name=name,
-        version=__version__,
-        description="Fungal Identification Pipeline",
-        author="Changwan Seo",
-        author_email="wan101010@snu.ac.kr",
-        url="https://github.com/Changwanseo/FunID",
-        python_requires="<3.13, >3.8",
-        packages=[
-            "funid",
-            "funid.src",
-            "funid.data",
-            "funid.test_dataset",
-            "funid.preset",
-        ],
-        install_requires=[
-            "biopython==1.78",
-            "ete3==3.1.3",
-            "Cython",
-            "dendropy",
-            "GenMine>=1.0.10",
-            "lxml",
-            "matplotlib",
-            "numpy",
-            "openpyxl==3.0.9",
-            "pandas==1.4.2",
-            "plotly==5.9.0",
-            "psutil",
-            "PyQt5",
-            "pyyaml",
-            "sip>=4.19.4",
-            "scikit-learn",
-            "scipy",
-            "tabulate",
-            "unidecode==1.2.0",
-            "xlrd==2.0.1",
-            "xlsxwriter",
-            "xmltodict==0.12.0",
-        ],
-        zip_safe=False,
-        entry_points={"console_scripts": ["FunID = funid.main:main"]},
-        package_dir={"funid": "funid"},
-        package_data={
-            "funid.data": ["*.xlsx", "*.txt"],
-            "funid.test_dataset": ["**"],
-            "funid.preset": ["**"],
-        },
-        include_package_data=True,
-        license="GPL3",
-    )
-
-
-def run_tests():
-    result = subprocess.run(["FunID", "-h"], capture_output=True, text=True)
-    print(result.stdout)
-    print(result.stderr)
-
-
-# Change setup options by platform
-if platform == "win32":
-    setup_options["packages"].append("funid.external")
-    setup_options["package_data"]["funid.external"] = ["**"]
-else:
-    pass
-
-print(setup_options)
-
-
-# Run setup
-setup(**setup_options, cmdclass={"test": run_tests})


Test patch:
diff --git a/funid/test_dataset/terrei/Options.config b/funid/test_dataset/terrei/Options.config
deleted file mode 100644
index 75a5ec5..0000000
--- a/funid/test_dataset/terrei/Options.config
+++ /dev/null
@@ -1,83 +0,0 @@
-{
-    "BASIC": {
-        "RUN_NAME": "20220622-152845",
-        "MODE": "identification",
-        "QUERY_ONLY": true,
-        "EMAIL": "wan101010@snu.ac.kr",
-        "GENE": [
-           "ITS" , "BenA", "RPB2", "CaM"     ],
-        "CONCATENATE": true,
-        "DB": [
-            "DB_Penicillium.xlsx"
-        ],
-        "MAXIMUM_OUTGROUP_COUNT": 3
-    },
-    "METHOD": {
-        "NEW_RUN": true,
-        "RUNNING_FROM": "visualize",
-        "SEARCH_METHOD": "mmseqs",
-        "ALIGNMENT_METHOD": "mafft",
-        "TRIMMING_METHOD": "none",
-        "MODEL_METHOD": "none",
-        "TREE_METHOD": "fasttree"
-    },
-    "VISUALIZE": {
-        "BOOTSTRAP_CUTOFF": 70,
-        "DESIGNATED_GENUS": "unknowngenus",
-        "SHORTEN_GENUS": true,
-        "CONST_WIDTH": 1000,
-        "CONST_HEIGHT": 6,
-        "CONST_MAX_LENGTH": 48,
-        "BLACK": "#000000",
-        "HIGHLIGHT": "#bb0000",
-        "SOURCECOLOR": [
-            "#14c4e4",
-            "#c4e414",
-            "#e49c14",
-            "#145ce4"
-        ],
-        "BGCOLOR": [
-            "#f4f4f4",
-            "#c6c6c6"
-        ],
-        "OUTGROUPCOLOR": "#999999",
-        "FONT_TYPE": "Arial",
-        "FONT_SIZE": 10,
-        "FONT_SIZE_BOOTSTRAP": 9
-    },
-    "OPTIONAL": {
-        "THREADS": 12,
-        "REGEX_QUERY": [
-        ],
-        "CLUSTERING_CUTOFF": 0.97,
-        "INCLUDE_AMBIGUOUS_SEQS" : true,
-        "MAFFT_ALGORITHM": "auto",
-        "TRIMAL_ALGORITHM": "gt",
-        "COLLAPSING_CUTOFF": 0.01,
-        "BOOTSTRAP": 1000,
-        "SOLVE_FLAT": true,
-        "ZERO": 1.00000050002909e-06
-    },
-    "ADVANCED": {
-        "DEBUG": true,
-        "CONFIDENT_DB": true,
-        "SPECIES_BLACKLIST": [
-            "sp."
-        ],
-        "USE_DEFAULT_GENUS_LIST" : true,
-        "SAVE_SEARCH_MATRIX": true,
-        "SAVE_SEARCH_RESULT": true,
-        "MATRIX_FORMAT" : "csv",
-        "HIERACHY_OUTGROUP": "section",
-        "WORD_SIZE": 7,
-        "E-VALUE": 0.00000001,
-        "MAX_ITERATE": 1000,
-        "gt": 0.2,
-        "op": 1.3,
-        "ep": 0.1,
-        "AVX": true,
-        "SAVE_RUN": true,
-        "SAVE_SEARCH_DB" : true,
-        "USE_SAVED_DB" : true
-    }
-}
\ No newline at end of file
diff --git a/funid/src/__pycache__/modeltest.cpython-39.pyc b/funvip/src/__pycache__/modeltest.cpython-39.pyc
similarity index 100%
rename from funid/src/__pycache__/modeltest.cpython-39.pyc
rename to funvip/src/__pycache__/modeltest.cpython-39.pyc
diff --git a/funid/src/modeltest.py b/funvip/src/modeltest.py
similarity index 99%
rename from funid/src/modeltest.py
rename to funvip/src/modeltest.py
index e254228..fc8059c 100644
--- a/funid/src/modeltest.py
+++ b/funvip/src/modeltest.py
@@ -1,4 +1,4 @@
-from funid.src import ext
+from funvip.src import ext
 import os, shutil, copy
 import logging
 
@@ -197,6 +197,7 @@ def parse_model(modeltest_file, opt, path):
             raise Exception
 
         # read modeltest result file
+        print(modeltest_file)
         try:
             with open(modeltest_file, "r", encoding="UTF-8") as f:
                 lines = f.readlines()
diff --git a/funid/test_dataset/penicillium/DB/DB_Penicillium.xlsx b/funvip/test_dataset/penicillium/DB/DB_Penicillium.xlsx
similarity index 100%
rename from funid/test_dataset/penicillium/DB/DB_Penicillium.xlsx
rename to funvip/test_dataset/penicillium/DB/DB_Penicillium.xlsx
diff --git a/funid/test_dataset/penicillium/Options.config b/funvip/test_dataset/penicillium/Options.config
similarity index 100%
rename from funid/test_dataset/penicillium/Options.config
rename to funvip/test_dataset/penicillium/Options.config
diff --git a/funid/test_dataset/penicillium/Query/Query.xlsx b/funvip/test_dataset/penicillium/Query/Query.xlsx
similarity index 100%
rename from funid/test_dataset/penicillium/Query/Query.xlsx
rename to funvip/test_dataset/penicillium/Query/Query.xlsx
diff --git a/funid/test_dataset/penicillium/preset.yaml b/funvip/test_dataset/penicillium/preset.yaml
similarity index 100%
rename from funid/test_dataset/penicillium/preset.yaml
rename to funvip/test_dataset/penicillium/preset.yaml
diff --git a/funid/test_dataset/terrei/DB/FunID_Aspergillus_db.xlsx b/funvip/test_dataset/terrei/DB/FunVIP_Aspergillus_db.xlsx
similarity index 100%
rename from funid/test_dataset/terrei/DB/FunID_Aspergillus_db.xlsx
rename to funvip/test_dataset/terrei/DB/FunVIP_Aspergillus_db.xlsx
diff --git a/funid/test_dataset/terrei/Query/FunID_Aspergillus_query.xlsx b/funvip/test_dataset/terrei/Query/FunVIP_Aspergillus_query.xlsx
similarity index 100%
rename from funid/test_dataset/terrei/Query/FunID_Aspergillus_query.xlsx
rename to funvip/test_dataset/terrei/Query/FunVIP_Aspergillus_query.xlsx
diff --git a/funid/test_dataset/terrei/preset.yaml b/funvip/test_dataset/terrei/preset.yaml
similarity index 89%
rename from funid/test_dataset/terrei/preset.yaml
rename to funvip/test_dataset/terrei/preset.yaml
index fddd491..bffb8e5 100644
--- a/funid/test_dataset/terrei/preset.yaml
+++ b/funvip/test_dataset/terrei/preset.yaml
@@ -7,9 +7,9 @@ GENE:
 - CaM
 CONCATENATE: true
 DB:
-- FunID_Aspergillus_db.xlsx
+- FunVIP_Aspergillus_db.xlsx
 QUERY:
-- FunID_Aspergillus_query.xlsx
+- FunVIP_Aspergillus_query.xlsx
 LEVEL: family
 MAXOUTGROUP: 1
 CONTINUE: false