lgvar 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: lgvar
3
+ Version: 1.3.0
4
+ Summary: LGvar is a comprehensive toolset designed for large-scale structural variant (SV) detection based on genome assemblies. It excels in cross-species variant identification, particularly demonstrating superior performance in inversion detection compared to existing tools.
5
+ Author: Feifei Zhou
6
+ License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Science/Research
9
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Requires-Python: <3.10,>=3.9
14
+ Description-Content-Type: text/markdown
15
+
16
+ # LGvar: Large-scale Genomic VARiation
17
+ [![Conda version](https://img.shields.io/badge/conda-v1.3.0-green)](https://anaconda.org/channels/zhoufeifei/packages/lgvar/overview) [![Appatiner Image Version](https://img.shields.io/badge/singularity-v1.3.0-blue)](https://cloud.sylabs.io/library/feifeizhou/tool/lgvar) [![License](https://img.shields.io/badge/license-MIT-yellow)](https://github.com/YafeiMaoLab/LGvar/blob/main/LICENSE) [![release](https://img.shields.io/badge/releases-June%202026-purple)](https://github.com/YafeiMaoLab/LGvar/releases/tag/v1.3.0)
18
+
19
+ ## Quick Start
20
+ * [Why LGvar?](#WhyLGvar)
21
+ * [Installation](#installation)
22
+ * [Usage & Examples](#Usage-Examples)
23
+ * [Reverse-complement the genome](#reverse)
24
+ * [Generate orthologous chromosome pairs](#pair)
25
+ * [Whole genome SV identification](#run)
26
+ * [Output files](#output)
27
+ * [Plot whole genome alignment synteny](#plot)
28
+ * [Benchmarking](#benchmark)
29
+ * [Getting Help](#getting-help)
30
+ * [Citing LGvar](#citation)
31
+
32
+ ## Why LGvar <a id="WhyLGvar"></a>
33
+ **LGvar** is a comprehensive toolset designed for large-scale structural variant (SV) detection based on genome assemblies. It excels in **cross-species variant identification**, particularly demonstrating superior performance in **inversion detection** compared to existing tools.
34
+
35
+ - **Easily use:** Only FASTA and chromosome pairs (.tsv) are needed for input (users can alternatively provide pre-aligned PAF files).
36
+ - **Comprehensive and fast:** LGvar detects not only insertions, deletions, inversions, translocations, duplications, INDELs and SNVs, but also detects structurally divergent regions (SDRs) and nested-inversions (INV-INV).
37
+ - **More efficient in detection of inversions:** LGvar performs realignment within SDRs to detect inversions missed in initial alignment.
38
+
39
+ ## Installation <a id="installation"></a>
40
+
41
+ **Option 1: Conda (Recommended)**
42
+
43
+ ```Bash
44
+ # You can install LGvar from anaconda.org
45
+ conda install -c conda-forge -c bioconda lgvar
46
+ ```
47
+ ```Bash
48
+ # Or you can directly create a new environment
49
+ # Clone the repository
50
+ git clone https://github.com/YafeiMaoLab/LGvar.git
51
+ cd LGvar
52
+ # Create and activate environment
53
+ conda env create -f env.yml
54
+ conda activate LGvar
55
+ ```
56
+ **Option 2: Singularity**
57
+ ```Bash
58
+ # If conda is not available, use our pre-built images:
59
+ singularity pull library://feifeizhou/tool/lgvar:v1.3.0
60
+ # Execution example:
61
+ singularity exec lgvar_v1.3.0.sif /LGVAR/LGVAR [subcommands]
62
+ ```
63
+
64
+ ## Usage & Examples <a id="Usage-Examples"></a>
65
+
66
+ * LGvar provides several subcommands to streamline the genomic analysis pipeline:
67
+ <details>
68
+ <summary><b>Subcommands List (click to expand)</b></summary>
69
+
70
+ | Command | Function |
71
+ | :------ | :------- |
72
+ | **run** | **Run SV identification.** Detects SDRs, DELs, INSs, INVs, DUPs, INDELs, SNVs etc. |
73
+ | **plot** | **Syntenic Plot.** Visualizes the alignment between genomes. Blue regions represent syntenic blocks, while orange regions represent inversions. |
74
+ | **pair** | **Homology Mapping.** Generates homologous chromosome pair files. |
75
+ | **reverse** | **RC-Check.** Identifies and reverse-complements misoriented chromosomes. |
76
+
77
+ </details>
78
+
79
+
80
+ **1\. Reverse-complement the genome <a id="reverse"></a>**
81
+
82
+ * Before calling SVs, using `LGvar reverse` to check if query chromosomes need reverse-complementing to ensure inversion calling accuracy, this step identifies a chromosome as needing reverse complementation by checking if the total length of alignments mapped to the reverse strand exceeds `half` the chromosome’s total length in the PAF file.
83
+ ```Bash
84
+ # Recommended: align first then check orientation
85
+ LGVAR reverse -p align.paf -r reverse.txt -g original.genome.fa -n new.genome.fa
86
+ # Or use the script
87
+ bash /src/scripts/rc_chrom.sh align.paf reverse.txt original.genome.fa new.genome.fa
88
+ #Then use new.genome.fa as query genome to do SV calling
89
+ ```
90
+ `reverse.txt` contains the chromosomes that need to be reverse-complemented.
91
+
92
+ **2\. Generate orthologous chromosome pairs <a id="pair"></a>**
93
+
94
+ * LGvar identifies variants between each pair of orthologous chromosomes. If you do not have a tab-delimited TSV file containing `ref query` columns, you can generate the orthologous chromosome TSV file using the `LGvar pair` command.
95
+ ```Bash
96
+ LGVAR pair -p align.paf -l 1000000 -o pair
97
+ ```
98
+ where the `-l` parameter specifies the minimum alignment length (in base pairs) required to define a reliable orthologous chromosome pair; only alignments meeting or exceeding this `1Mbp` threshold will be used to determine the corresponding relationships between chromosomes. It is recommended to increase this value when analyzing two species with a large evolutionary divergence (e.g. human vs. lemur, `-l` can be set to `5Mbp`).
99
+
100
+ **3\. Whole genome SV identification <a id="run"></a>**
101
+
102
+ * Once you have prepared the reference genome, query genome and orthologous chromosome pair files, you can use `LGVAR run` to perform genome-wide variant detection. You may optionally provide files containing `centromere and telomere` coordinates of the reference genome to mask highly repetitive regions, thus improving running efficiency.
103
+ ```Bash
104
+ LGVAR run \
105
+ -r ref.fa \
106
+ -q1 query.hap1.fa \
107
+ -q2 query.hap2.fa \
108
+ -cp1 query.hap1.pair.tsv \
109
+ -cp2 query.hap2.pair.tsv \
110
+ -m sensitive \
111
+ -s query
112
+ ```
113
+ Centromere and telomere coordinate files for `T2T-CHM13` are available under the `examples/data` directory, namely `chm13_cen.tsv` and `chm13_telo.tsv`.
114
+
115
+ * Here is an running example. The test data is located in `examples/`. We'll use Human chromosome 21(T2T-CHM13) as reference and Chimpanzee (PTR) as query.
116
+ ```Bash
117
+ # 1. Setup working directory
118
+ mkdir LGvar_work
119
+ # 2. Decompress test genomes
120
+ cd examples/genome && gunzip *.gz
121
+ cd LGvar_work
122
+ # 3. Execution (Example with existing PAF alignments)
123
+ LGVAR run \
124
+ -r /examples/genome/chr21.chm13.fa \
125
+ -q1 /examples/genome/chr21.ptr.hap1.fa \
126
+ -q2 /examples/genome/chr21.ptr.hap2.fa \
127
+ -p1 /examples/align/align_hap1.paf \
128
+ -p2 /examples/align/align_hap2.paf \
129
+ -cp1 /examples/data/PTR_hap1_pairs.tsv \
130
+ -cp2 /examples/data/PTR_hap2_pairs.tsv \
131
+ -m sensitive \
132
+ -s PTR
133
+ ```
134
+
135
+ **4\. Output Files <a id="output"></a>**
136
+
137
+ * Results are saved in the `${work_dir}/results` folder:
138
+ <details>
139
+ <summary><b>results</b></summary>
140
+
141
+ | File | Function |
142
+ | :------ | :------- |
143
+ | **sortLGvar_all.vcf** | Combined results of SNVs, INDELs and SVs for both haplotypes. |
144
+ | **LGvarhap1(2).vcf** | Haplotype-specific SV details. |
145
+ | **LGvarhap1(2).bed** | Haplotype-specific SV details (with SDRs, DUPs, TRANs). |
146
+ | **LGvar.bed** | Merged variant regions (Hap1 + Hap2). |
147
+
148
+ </details>
149
+
150
+ | Label | Description |
151
+ | :---- | :---- |
152
+ | **SNVs** | Single Nucleotide Variation |
153
+ | **INDELs** | Small insertions and deletions (Length < 50bp) |
154
+ | **DELs/INSs** | Deletions and Insertions (filtered at 50bp threshold) |
155
+ | **INVs/INV-INV** | Simple and Nested Inversions |
156
+ | **DUP** | Duplications |
157
+ | **TRANS** | Translocations |
158
+ | **SDRs** | Structurally Divergent Regions |
159
+
160
+ **5\. Plot whole genome alignment synteny<a id="plot"></a>**
161
+
162
+ * You can visualize whole-genome alignments using `PAF` and `PAIR` files.
163
+ ```Bash
164
+ LGVAR plot -p align.paf -f pair.tsv -o align.pdf
165
+ ```
166
+ This is an example using T2T-CHM13 as reference and chimpanzee hap1 as query genome.
167
+ ![https://github.com/YafeiMaoLab/LGvar/images/alignment.png](https://github.com/YafeiMaoLab/LGvar/blob/main/images/alignment.png)
168
+
169
+ ## Benchmarking<a id="benchmark"></a>
170
+ * We benchmarked LGvar in many genomes include simulated, population and cross-species data (see forthcoming paper). See [Analysis.md](https://github.com/YafeiMaoLab/LGvar/blob/main/README.md):
171
+
172
+
173
+ ## Getting help <a id="getting-help"></a>
174
+
175
+ * If you encounter any issues or have questions about specific parameters, please [Open an Issue](https://github.com/YafeiMaoLab/LGvar) or contact zhoufeifei@sjtu.edu.cn.
176
+
177
+
178
+ ## Citing LGvar <a id="citation"></a>
179
+
180
+ * If you use LGvar in your research, please cite our repository (and forthcoming paper): (https://github.com/YafeiMaoLab/LGvar)
@@ -0,0 +1,7 @@
1
+ lgvar_cli.py,sha256=SvkoRfXGs3jmnkJ9O95CifB7HGSIxYpNqOfZkUP1lEw,861
2
+ utils.py,sha256=EtOnHWqYPmo5jD-be7PejLLcs22nID3N3OQQnnBU4mk,13422
3
+ lgvar-1.3.0.dist-info/METADATA,sha256=bEG5gqg-gVuTmou-oFAf13-A8t53EncJ9Hc12dINR3Q,9094
4
+ lgvar-1.3.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
5
+ lgvar-1.3.0.dist-info/entry_points.txt,sha256=sMh9SZ_2J1-LwWOVVzOET2CSnannqJMe4zeUHgic31w,41
6
+ lgvar-1.3.0.dist-info/top_level.txt,sha256=IvbxecdfQeXiz4T1zikvitPtJRveotHpZC0wgTrl_bk,16
7
+ lgvar-1.3.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ LGVAR = lgvar_cli:main
@@ -0,0 +1,2 @@
1
+ lgvar_cli
2
+ utils
lgvar_cli.py ADDED
@@ -0,0 +1,29 @@
1
+ import os
2
+ import subprocess
3
+ import sys
4
+
5
+
6
+ def main():
7
+ # 获取当前安装目录(即 site-packages/ 根目录)
8
+ package_dir = os.path.dirname(os.path.abspath(__file__))
9
+
10
+ # 此时 LGVAR 与 utils.py、src/ 都在 package_dir 下
11
+ lgvar_script = os.path.join(package_dir, "LGVAR")
12
+
13
+ if os.path.exists(lgvar_script):
14
+ os.chmod(lgvar_script, 0o755)
15
+ cmd = [lgvar_script] + sys.argv[1:]
16
+ try:
17
+ # 指定 working directory 为 package_dir,确保路径完全匹配
18
+ result = subprocess.run(cmd, cwd=package_dir)
19
+ sys.exit(result.returncode)
20
+ except Exception as e:
21
+ print(f"[Error] Failed to execute LGVAR: {e}")
22
+ sys.exit(1)
23
+ else:
24
+ print(f"[Error] Could not locate 'LGVAR' in {package_dir}")
25
+ sys.exit(1)
26
+
27
+
28
+ if __name__ == "__main__":
29
+ main()
utils.py ADDED
@@ -0,0 +1,390 @@
1
+ import os
2
+ import glob
3
+ import time
4
+ import sys
5
+ import logging
6
+ import subprocess
7
+ from pathlib import Path
8
+
9
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
10
+
11
+ def run_minimap(ref_path, hap_path, hap_label, chrom_pair_file):
12
+ """1. Whole genome alignments"""
13
+ cmd = [
14
+ "minimap2",
15
+ "-t", "12",
16
+ "-cx", "asm20",
17
+ "--secondary=no",
18
+ "--eqx",
19
+ "-m 10000",
20
+ "-z 10000,50",
21
+ "-r 50000",
22
+ "--end-bonus=100",
23
+ "-O 5,56",
24
+ "-E 4,1",
25
+ "-B 5",
26
+ ref_path,
27
+ hap_path,
28
+ "-o", f"align_{hap_label}.paf"
29
+ ]
30
+ logging.info("0.Map")
31
+ logging.info(f"Running minimap2 for {hap_label}...")
32
+
33
+ result = subprocess.run(cmd, check=True)
34
+
35
+ if result.returncode == 0:
36
+ logging.info(f"Minimap2 completed for {hap_label}")
37
+
38
+ logging.info(f"Selecting alignments for homologous chromosomes in {hap_label}...")
39
+ process_paf(f"align_{hap_label}.paf",chrom_pair_file,hap_label)
40
+
41
+ def process_paf(paf_file, chrom_pair_file, hap_label):
42
+ """2. Filter extra alignments"""
43
+ middle_dir = Path("half")
44
+ middle_dir.mkdir(exist_ok=True)
45
+ p_c_chrlen_file = middle_dir/f"{hap_label}_chrlen.txt"
46
+ output_file = f"align_{hap_label}.flt.paf"
47
+
48
+ # Read mapping
49
+ mapping = {}
50
+ with open(chrom_pair_file, 'r') as f:
51
+ for line in f:
52
+ line = line.strip()
53
+ if not line or line.startswith("#"): continue
54
+ key, value = line.split("\t")
55
+ mapping[key] = value.split(",")
56
+
57
+ # Filter PAF
58
+ logging.info(f"Filtering PAF for {hap_label}...")
59
+ with open(paf_file, 'r') as fin, open(output_file, 'w') as fout:
60
+ for line in fin:
61
+ line = line.strip()
62
+ if not line or line.startswith("#"): continue
63
+ content = line.split("\t")
64
+ try:
65
+ if content[5] in mapping.keys() and content[0] in mapping[content[5]]:
66
+ print(line, file=fout)
67
+ except Exception as e:
68
+ content = line.split(" ")
69
+ if content[5] in mapping.keys() and content[0] in mapping[content[5]]:
70
+ print(line, file=fout)
71
+
72
+ # Extract chrlen info
73
+ with open(output_file, 'r') as fin, open(p_c_chrlen_file, 'w') as fout:
74
+ for line in fin:
75
+ content = line.strip().split("\t")
76
+ if content:
77
+ print(f"{content[5]} {content[6]} {content[0]} {content[1]}", file=fout)
78
+
79
+ def process_filtering(hap_label, mode, divergence):
80
+ """3. Filter high-divergence alignments"""
81
+ nowdic = Path.cwd()
82
+ middle_dir = Path("half")
83
+ middle_dir.mkdir(exist_ok=True)
84
+
85
+ saffire_dir = Path(f"saffire{hap_label}")
86
+ saffire_dir.mkdir(exist_ok=True)
87
+ p_c_chrlen_file = middle_dir/f"{hap_label}_chrlen.txt"
88
+ output_file = middle_dir/f"{hap_label}_syntenic.tsv"
89
+
90
+ # run scripts
91
+ chaos_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/scripts/chaos_filt.r"
92
+
93
+ if mode == "insensitive":
94
+ cmd = [
95
+ "cp",
96
+ f"align_{hap_label}.flt.paf",
97
+ f"align_{hap_label}.final.paf"
98
+ ]
99
+
100
+ result = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
101
+
102
+ elif mode == "sensitive":
103
+ if divergence is None:
104
+ logging.error("Error: When using 'sensitive' mode, you must provide divergence argument.")
105
+ sys.exit(1)
106
+ cmd = [
107
+ "Rscript", str(chaos_script),
108
+ str(p_c_chrlen_file),
109
+ f"{str(saffire_dir)}/",
110
+ f"align_{hap_label}.flt.paf",
111
+ f"align_{hap_label}.final.paf",
112
+ str(mode),
113
+ str(divergence)
114
+ ]
115
+
116
+ logging.info(f"Running chaos filter for {hap_label} in 'sensitive' mode...")
117
+ result = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
118
+
119
+ cmd = f"awk 'BEGIN{{OFS=\"\\t\"}} {{print $6, $8, $9, ($8+$9)/2, $1, $3, $4, ($3+$4)/2, $5}}' align_{hap_label}.final.paf | sort -k1,1 -k2,2n > {output_file}"
120
+ subprocess.run(cmd, shell=True, check=True)
121
+
122
+ def run_cluster_and_call(ref_path, hap_path, hap_label, cluster, invcluster, threads, chunk_size, distance, fraction, refine_breakpoints, combine_inversion):
123
+ """4. Cluster and SV calling"""
124
+
125
+ denSDR_dir = Path(f"denSDR{hap_label}")
126
+ denSDR_dir.mkdir(exist_ok=True)
127
+ #dotplot_dir = Path(f"dotplot{hap_label}")
128
+ #dotplot_dir.mkdir(exist_ok=True)
129
+ sdrall_file = denSDR_dir/"SDRall.txt"
130
+ paf_file = f"align_{hap_label}.final.paf"
131
+ middle_dir = Path("half")
132
+ middle_dir.mkdir(exist_ok=True)
133
+
134
+ cluster_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/scripts/denSDR.r"
135
+ fun_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/scripts/denSDRfun.r"
136
+
137
+ cmd = [
138
+ "Rscript", str(cluster_script),
139
+ str(fun_script),
140
+ middle_dir/f"{hap_label}_syntenic.tsv",
141
+ middle_dir/f"{hap_label}_chrlen.txt",
142
+ f"{str(denSDR_dir)}/",
143
+ str(cluster),
144
+ str(invcluster),
145
+ str(distance),
146
+ str(refine_breakpoints), # this is TRUE or FALSE
147
+ str(combine_inversion),
148
+ paf_file
149
+ ]
150
+
151
+ logging.info(f"SV calling for {hap_label}...")
152
+
153
+ result = subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
154
+ logging.info(f"SV calling on {hap_label} finished...")
155
+
156
+ cmd = [
157
+ "cat", f"{denSDR_dir}/*end.tsv", ">", str(sdrall_file)
158
+ ]
159
+
160
+ subprocess.run(" ".join(cmd), shell=True, check=True)
161
+
162
+ # realign to find simple inversions from SDR
163
+ realign_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/vcf/realign.sh"
164
+
165
+ cmd = [
166
+ "bash", str(realign_script),
167
+ "-i", str(sdrall_file),
168
+ "-r", ref_path,
169
+ "-q", hap_path,
170
+ "-o", str(denSDR_dir/"SDRall_final.txt"),
171
+ "-t", str(threads),
172
+ "-c", str(chunk_size),
173
+ "-p", f"align_{hap_label}.final.paf",
174
+ "-f", str(fraction)
175
+ ]
176
+
177
+ subprocess.run(cmd, check=True)
178
+ logging.info(f"Inversion re-identification of {hap_label} finished")
179
+
180
+ # restore local alignments
181
+ restore_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/pyFiles/restore_paf.py"
182
+ cmd = [
183
+ "python", str(restore_script),
184
+ middle_dir/f"minimap2.paf",
185
+ middle_dir/f"restore.paf",
186
+ hap_path,
187
+ ref_path
188
+ ]
189
+ subprocess.run(cmd, check=True)
190
+ subprocess.run(f"cat half/restore.paf >> align_{hap_label}.final.paf", shell=True, check=True)
191
+ subprocess.run("rm -f temp/*.tmp", shell=True, check=True)
192
+ logging.info(f"Restore local alignment of {hap_label} finished")
193
+
194
+ def run_cigar_processing(ref_path, hap_path, hap_label):
195
+ """ 5.extract variationas from CIGAR """
196
+ cigar_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/pyFiles/CIGAR.py"
197
+ temp_dir = Path("temp")
198
+ temp_dir.mkdir(exist_ok=True)
199
+ middle_dir = Path("half")
200
+ middle_dir.mkdir(exist_ok=True)
201
+
202
+ cmd = [
203
+ "python", str(cigar_script),
204
+ "--r", ref_path,
205
+ "--q", hap_path,
206
+ "--paf", f"align_{hap_label}.final.paf",
207
+ "--o", middle_dir/f"{hap_label}cigar.txt"
208
+ ]
209
+
210
+ logging.info(f"Small variants identification for {hap_label}...")
211
+
212
+ subprocess.run(cmd, check=True)
213
+
214
+ output_file = middle_dir/f"{hap_label}cigarend.txt"
215
+
216
+ with open(output_file, 'w') as dest:
217
+ with open(os.path.join("half", f"{hap_label}cigar.txt"), 'r') as src:
218
+ for line in src:
219
+ dest.write(line)
220
+
221
+ for file_path in glob.glob(os.path.join("temp", "*.cigar")):
222
+ with open(file_path, 'r') as src:
223
+ for line in src:
224
+ dest.write(line)
225
+
226
+ cmd = [
227
+ "find", str(temp_dir), "-type", "f", "-name", "*.cigar", "-delete"
228
+ ]
229
+
230
+ subprocess.run(cmd, check=True)
231
+ logging.info(f"Small variants identification for {hap_label} finished...")
232
+ #print(f"CIGAR generated for {hap_label} finished at {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}", flush=True)
233
+
234
+ def run_dup_filtering(hap_label):
235
+ """6. dup filter"""
236
+ dup_filt_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/vcf/dup_filt.sh"
237
+ filt_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/pyFiles/dup_filt.py"
238
+ middle_dir = Path("half")
239
+ middle_dir.mkdir(exist_ok=True)
240
+ paf_file = f"align_{hap_label}.final.paf"
241
+ cigar_end_file = middle_dir/f"{hap_label}cigarend.txt"
242
+ output_file = middle_dir/f"{hap_label}cigarout.txt"
243
+
244
+ cmd = [
245
+ "bash", str(dup_filt_script),
246
+ paf_file,
247
+ cigar_end_file,
248
+ output_file,
249
+ filt_script
250
+ ]
251
+
252
+ logging.info("Filtering duplication...")
253
+ subprocess.run(cmd, check=True)
254
+
255
+ def generate_vcf(ref_path, hap_path, hap_label, variant_type):
256
+ """7. Generate variation results -- vcf"""
257
+ results_dir = Path(f"results")
258
+ results_dir.mkdir(exist_ok=True)
259
+
260
+ middle_dir = Path("half")
261
+ middle_dir.mkdir(exist_ok=True)
262
+ cigar_to_vcf_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/vcf/CIGAR2VCF.sh"
263
+ py_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/pyFiles/SDR2VCF.py"
264
+ cigar_out_file = middle_dir/f"{hap_label}cigarout.txt"
265
+ vcf_file = f"{results_dir}/LGvar{hap_label}.vcf"
266
+ sdrall_final_file = f"denSDR{hap_label}/SDRall_final.txt"
267
+ cigarsdr_txt_file = middle_dir/f"{hap_label}cigarsdr.txt"
268
+ lgvarend_bed_file = f"{results_dir}/LGvar{hap_label}.bed"
269
+
270
+ cmd = [
271
+ "bash", str(cigar_to_vcf_script),
272
+ cigar_out_file,
273
+ vcf_file,
274
+ sdrall_final_file,
275
+ ref_path,
276
+ hap_path,
277
+ cigarsdr_txt_file,
278
+ str(py_script),
279
+ lgvarend_bed_file,
280
+ str(variant_type)
281
+ ]
282
+
283
+
284
+ logging.info(f"Generating VCF of {hap_label}...")
285
+ subprocess.run(cmd, check=True)
286
+
287
+ def integrate_results(hap1_dir, hap2_dir, sample_name, max_distance, small_distance, similarity_threshold, variant_type):
288
+ """8. Variants classification"""
289
+ script_dir = os.path.dirname(os.path.abspath(__file__))
290
+ split_script = Path(script_dir) / "src/vcf/splitfile.sh"
291
+ hap_dir_obj = Path("hap1")
292
+ hap_dir_obj.mkdir(exist_ok=True)
293
+ cmd1 = [
294
+ "bash", str(split_script),
295
+ f"{str(hap_dir_obj)}/",
296
+ "results/LGvarhap1.vcf",
297
+ str(variant_type)
298
+ ]
299
+ subprocess.run(cmd1, check=True)
300
+
301
+ hap_dir_obj2 = Path("hap2")
302
+ hap_dir_obj2.mkdir(exist_ok=True)
303
+ cmd2 = [
304
+ "bash", str(split_script),
305
+ f"{str(hap_dir_obj2)}/",
306
+ "results/LGvarhap2.vcf",
307
+ str(variant_type)
308
+ ]
309
+ subprocess.run(cmd2, check=True)
310
+
311
+ """9. merge two haplotypes' variation"""
312
+ phenotype_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/pyFiles/phenotype.py"
313
+
314
+ variants = list(set(variant_type.split(',')))
315
+ output_vcf = f"sortLGvar_{'_'.join(sorted(variants))}.vcf"
316
+
317
+ cmd = [
318
+ "python", str(phenotype_script),
319
+ "--output", f"results/{output_vcf}",
320
+ "--max_distance", str(max_distance),
321
+ "--small_distance", str(small_distance),
322
+ "--similarity_threshold", str(similarity_threshold),
323
+ "--sample_name", sample_name
324
+ ]
325
+
326
+ cmd_extended = False
327
+ if "snv" in variants or "all" in variants:
328
+ cmd.extend(["--hap1_snv", f"{hap1_dir}/sortsnv.vcf.gz"])
329
+ cmd.extend(["--hap2_snv", f"{hap2_dir}/sortsnv.vcf.gz"])
330
+ cmd_extended = True
331
+ if "ins" in variants or "del" in variants or "all" in variants:
332
+ cmd.extend(["--hap1_indel", f"{hap1_dir}/sortindel.vcf.gz"])
333
+ cmd.extend(["--hap2_indel", f"{hap2_dir}/sortindel.vcf.gz"])
334
+ cmd_extended = True
335
+ if "inv" in variants or "all" in variants:
336
+ cmd.extend(["--hap1_sv", f"{hap1_dir}/sortSV.vcf.gz"])
337
+ cmd.extend(["--hap2_sv", f"{hap2_dir}/sortSV.vcf.gz"])
338
+ cmd_extended = True
339
+
340
+ if cmd_extended:
341
+ subprocess.run(cmd, check=True)
342
+
343
+ vcf2bedgt_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/vcf/vcf2bedGT.sh"
344
+
345
+ cmd = [
346
+ "bash", str(vcf2bedgt_script),
347
+ f"results/{output_vcf}",
348
+ f"results/LGvarhap1.bed",
349
+ f"results/LGvarhap2.bed",
350
+ f"results/LGvarall.bed"
351
+ ]
352
+
353
+ subprocess.run(cmd, check=True)
354
+ logging.info("Complete!")
355
+
356
+ def plot(paf, output, minimum, distance, file):
357
+ plot_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/pyFiles/plotpaf.py"
358
+ cmd = [
359
+ "python", str(plot_script),
360
+ "-p", paf,
361
+ "-o", output,
362
+ "-m", str(minimum),
363
+ "-d", str(distance),
364
+ "-f", file
365
+ ]
366
+ subprocess.run(cmd, check=True)
367
+ logging.info("Done!")
368
+
369
+ def pair(paf, length, output_prefix):
370
+ pair_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/scripts/chrom_pair.sh"
371
+ cmd = [
372
+ "bash", str(pair_script),
373
+ paf,
374
+ str(length),
375
+ output_prefix
376
+ ]
377
+ subprocess.run(cmd, check=True)
378
+
379
+ def reverse(paf, reverse, genome, new_genome):
380
+ reverse_script = Path(os.path.dirname(os.path.abspath(__file__))) / "src/scripts/rc_chrom.sh"
381
+ cmd = [
382
+ "bash", str(reverse_script),
383
+ paf,
384
+ reverse,
385
+ genome,
386
+ new_genome
387
+ ]
388
+ subprocess.run(cmd, check=True)
389
+
390
+