Skip to content

Commit 2a6810b

Browse files
authored
Merge pull request #1157 from fls-bioinformatics-core/make_fastqs-10x_multiome-use-illumina-indexes
'make_fastqs': enable 10x multiome and ATAC Fastq generation to operate with Illumina indexes
2 parents d603584 + 8ed98cf commit 2a6810b

6 files changed

Lines changed: 824 additions & 99 deletions

File tree

auto_process_ngs/bcl2fastq/pipeline.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22
#
33
# bcl2fastq.pipeline.py: pipelines for Fastq generation
4-
# Copyright (C) University of Manchester 2020-2025 Peter Briggs
4+
# Copyright (C) University of Manchester 2020-2026 Peter Briggs
55
#
66

77
"""
@@ -1015,6 +1015,12 @@ def _build_pipeline(self):
10151015
# Switch to standard pipeline for 10x Visium without 10x
10161016
# indexes
10171017
pipeline_variant = "standard"
1018+
elif pipeline_variant == "10x_cellranger-atac" and not has_10x_indexes:
1019+
# Switch to standard pipeline for 10x ATAC without 10x indexes
1020+
pipeline_variant = "standard"
1021+
elif pipeline_variant == "10x_cellranger-arc" and not has_10x_indexes:
1022+
# Switch to standard pipeline for 10x multiome without 10x indexes
1023+
pipeline_variant = "standard"
10181024
self.report("- Pipeline variant: %s" % pipeline_variant)
10191025

10201026
#########################

auto_process_ngs/bcl2fastq/protocols.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22
#
33
# bcl2fastq.protocols.py: Fastq generation protocol definitions
4-
# Copyright (C) University of Manchester 2025 Peter Briggs
4+
# Copyright (C) University of Manchester 2025-2026 Peter Briggs
55
#
66

77
"""
@@ -88,12 +88,14 @@
8888
# -- disable adapter trimming
8989
"description": "10x Genomics Chromium single cell ATAC-seq data",
9090
"pipeline_variant": "10x_cellranger-atac",
91-
"supported_indexes": ("10X",),
91+
"supported_indexes": ("ILLUMINA", "10X",),
9292
"r1_length": 50,
9393
"r2_length": 16,
9494
"r3_length": 50,
9595
"i1_length": 8,
9696
"override_template": "RIRR",
97+
"minimum_trimmed_read_length": 8,
98+
"mask_short_adapter_reads": 8,
9799
"tenx_filter_single_index": True,
98100
"no_lane_splitting": False,
99101
"create_fastq_for_index_read": True,
@@ -128,12 +130,14 @@
128130
"description": "10x Genomics single cell multiome ATAC-seq data "
129131
"(pooled with GEX data)",
130132
"pipeline_variant": "10x_cellranger-arc",
131-
"supported_indexes": ("10X",),
133+
"supported_indexes": ("ILLUMINA", "10X",),
132134
"r1_length": 50,
133135
"r2_length": 24,
134136
"r3_length": 49,
135137
"i1_length": 8,
136138
"override_template": "RIRR",
139+
"minimum_trimmed_read_length": 8,
140+
"mask_short_adapter_reads": 8,
137141
"tenx_filter_single_index": True,
138142
"no_lane_splitting": False,
139143
"create_fastq_for_index_read": True,
@@ -151,11 +155,13 @@
151155
"description": "10x Genomics single cell multiome GEX data "
152156
"(pooled with ATAC data)",
153157
"pipeline_variant": "10x_cellranger-arc",
154-
"supported_indexes": ("10X",),
158+
"supported_indexes": ("ILLUMINA", "10X",),
155159
"r1_length": 28,
156160
"r2_length": 90,
157161
"i1_length": 10,
158162
"i2_length": 10,
163+
"minimum_trimmed_read_length": 8,
164+
"mask_short_adapter_reads": 8,
159165
"tenx_filter_dual_index": True,
160166
"no_lane_splitting": False,
161167
"create_fastq_for_index_read": True,

auto_process_ngs/test/bcl2fastq/pipeline/test_10x_atac.py

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,195 @@ def test_makefastqs_10x_atac_protocol_200(self):
210210
os.path.join(analysis_dir,filen)),
211211
"Missing file: %s" % filen)
212212

213+
#@unittest.skip("Skipped")
214+
def test_makefastqs_10x_atac_protocol_bcl2fastq_illumina_indexes(self):
215+
"""
216+
MakeFastqs: '10x_atac' protocol (bcl2fastq/Illumina indexes)
217+
"""
218+
# Create mock source data
219+
illumina_run = MockIlluminaRun(
220+
"171020_NB500968_00002_AHGXXXX",
221+
"nextseq",
222+
bases_mask="y101,I8,I20,y101",
223+
top_dir=self.wd)
224+
illumina_run.create()
225+
run_dir = illumina_run.dirn
226+
# Sample sheet with 10xGenomics Chromium SC ATAC-seq indices
227+
samplesheet_chromium_sc_atac_indices = """[Header]
228+
IEMFileVersion,4
229+
Assay,Nextera XT
230+
231+
[Reads]
232+
76
233+
76
234+
235+
[Settings]
236+
ReverseComplement,0
237+
Adapter,CTGTCTCTTATACACATCT
238+
239+
[Data]
240+
Sample_ID,Sample_Name,Sample_Plate,Sample_Well,I7_Index_ID,index,I5_Index_ID,index2,Sample_Project,Description
241+
smpl1,smpl1,,,SI-NA-A1,TGTCCCAACG,SI-NA-A1,TGGACATCGA,10xGenomics,
242+
smpl2,smpl2,,,SI-NA-B1,AGTCCCATCA,SI-NA-B1,GTCACGTTCG,10xGenomics,
243+
"""
244+
sample_sheet = os.path.join(self.wd,"SampleSheet.csv")
245+
with open(sample_sheet,'w') as fp:
246+
fp.write(samplesheet_chromium_sc_atac_indices)
247+
# Create mock bcl2fastq
248+
MockBcl2fastq2Exe.create(os.path.join(self.bin,
249+
"bcl2fastq"),
250+
assert_bases_mask="Y50N51,I8,Y16N4,Y50N51")
251+
os.environ['PATH'] = "%s:%s" % (self.bin,
252+
os.environ['PATH'])
253+
analysis_dir = os.path.join(self.wd,"analysis")
254+
os.mkdir(analysis_dir)
255+
# Do the test
256+
p = MakeFastqs(run_dir,sample_sheet,protocol="10x_atac")
257+
status = p.run(analysis_dir,
258+
poll_interval=POLL_INTERVAL)
259+
self.assertEqual(status,0)
260+
# Check outputs
261+
self.assertEqual(p.output.platform,"nextseq")
262+
self.assertEqual(p.output.flow_cell_mode,None)
263+
self.assertEqual(p.output.primary_data_dir,
264+
os.path.join(analysis_dir,
265+
"primary_data"))
266+
self.assertEqual(p.output.bcl2fastq_info,
267+
(os.path.join(self.bin,"bcl2fastq"),
268+
"bcl2fastq",
269+
"2.20.0.422"))
270+
self.assertEqual(p.output.cellranger_atac_info, None)
271+
self.assertTrue(p.output.acquired_primary_data)
272+
self.assertEqual(p.output.stats_file,
273+
os.path.join(analysis_dir,"statistics.info"))
274+
self.assertEqual(p.output.stats_full,
275+
os.path.join(analysis_dir,"statistics_full.info"))
276+
self.assertEqual(p.output.per_lane_stats,
277+
os.path.join(analysis_dir,
278+
"per_lane_statistics.info"))
279+
self.assertEqual(p.output.per_lane_sample_stats,
280+
os.path.join(analysis_dir,
281+
"per_lane_sample_stats.info"))
282+
self.assertEqual(p.output.seq_len_stats,
283+
os.path.join(analysis_dir,
284+
"seq_len_statistics.info"))
285+
self.assertEqual(p.output.missing_fastqs,[])
286+
for subdir in (os.path.join("primary_data",
287+
"171020_NB500968_00002_AHGXXXX"),
288+
"bcl2fastq",
289+
"barcode_analysis",):
290+
self.assertTrue(os.path.isdir(
291+
os.path.join(analysis_dir,subdir)),
292+
"Missing subdir: %s" % subdir)
293+
self.assertTrue(os.path.islink(
294+
os.path.join(analysis_dir,
295+
"primary_data",
296+
"171020_NB500968_00002_AHGXXXX")))
297+
for filen in ("statistics.info",
298+
"statistics_full.info",
299+
"per_lane_statistics.info",
300+
"per_lane_sample_stats.info",
301+
"seq_len_statistics.info",
302+
"processing_qc.html",):
303+
self.assertTrue(os.path.isfile(
304+
os.path.join(analysis_dir,filen)),
305+
"Missing file: %s" % filen)
306+
307+
#@unittest.skip("Skipped")
308+
def test_makefastqs_10x_atac_protocol_bclconvert_illumina_indexes(self):
309+
"""
310+
MakeFastqs: '10x_atac' protocol (bcl-convert/Illumina indexes)
311+
"""
312+
# Create mock source data
313+
illumina_run = MockIlluminaRun(
314+
"171020_NB500968_00002_AHGXXXX",
315+
"nextseq",
316+
bases_mask="y101,I8,I20,y101",
317+
top_dir=self.wd)
318+
illumina_run.create()
319+
run_dir = illumina_run.dirn
320+
# Sample sheet with 10xGenomics Chromium SC ATAC-seq indices
321+
samplesheet_chromium_sc_atac_indices = """[Header]
322+
IEMFileVersion,4
323+
Assay,Nextera XT
324+
325+
[Reads]
326+
76
327+
76
328+
329+
[Settings]
330+
ReverseComplement,0
331+
Adapter,CTGTCTCTTATACACATCT
332+
333+
[Data]
334+
Sample_ID,Sample_Name,Sample_Plate,Sample_Well,I7_Index_ID,index,I5_Index_ID,index2,Sample_Project,Description
335+
smpl1,smpl1,,,SI-NA-A1,TGTCCCAACG,SI-NA-A1,TGGACATCGA,10xGenomics,
336+
smpl2,smpl2,,,SI-NA-B1,AGTCCCATCA,SI-NA-B1,GTCACGTTCG,10xGenomics,
337+
"""
338+
sample_sheet = os.path.join(self.wd,"SampleSheet.csv")
339+
with open(sample_sheet,'w') as fp:
340+
fp.write(samplesheet_chromium_sc_atac_indices)
341+
# Create mock bcl-convert
342+
MockBclConvertExe.create(os.path.join(self.bin, "bcl-convert"),
343+
assert_override_cycles="Y50N51;I8;Y16N4;Y50N51")
344+
os.environ['PATH'] = "%s:%s" % (self.bin,
345+
os.environ['PATH'])
346+
analysis_dir = os.path.join(self.wd,"analysis")
347+
os.mkdir(analysis_dir)
348+
# Do the test
349+
p = MakeFastqs(run_dir,sample_sheet,
350+
protocol="10x_atac",
351+
bcl_converter="bcl-convert")
352+
status = p.run(analysis_dir,
353+
poll_interval=POLL_INTERVAL)
354+
self.assertEqual(status,0)
355+
# Check outputs
356+
self.assertEqual(p.output.platform,"nextseq")
357+
self.assertEqual(p.output.flow_cell_mode,None)
358+
self.assertEqual(p.output.primary_data_dir,
359+
os.path.join(analysis_dir,
360+
"primary_data"))
361+
self.assertEqual(p.output.bclconvert_info,
362+
(os.path.join(self.bin, "bcl-convert"),
363+
"BCL Convert",
364+
"3.7.5"))
365+
self.assertEqual(p.output.cellranger_atac_info, None)
366+
self.assertTrue(p.output.acquired_primary_data)
367+
self.assertEqual(p.output.stats_file,
368+
os.path.join(analysis_dir,"statistics.info"))
369+
self.assertEqual(p.output.stats_full,
370+
os.path.join(analysis_dir,"statistics_full.info"))
371+
self.assertEqual(p.output.per_lane_stats,
372+
os.path.join(analysis_dir,
373+
"per_lane_statistics.info"))
374+
self.assertEqual(p.output.per_lane_sample_stats,
375+
os.path.join(analysis_dir,
376+
"per_lane_sample_stats.info"))
377+
self.assertEqual(p.output.seq_len_stats,
378+
os.path.join(analysis_dir,
379+
"seq_len_statistics.info"))
380+
self.assertEqual(p.output.missing_fastqs,[])
381+
for subdir in (os.path.join("primary_data",
382+
"171020_NB500968_00002_AHGXXXX"),
383+
"bcl2fastq",
384+
"barcode_analysis",):
385+
self.assertTrue(os.path.isdir(
386+
os.path.join(analysis_dir,subdir)),
387+
"Missing subdir: %s" % subdir)
388+
self.assertTrue(os.path.islink(
389+
os.path.join(analysis_dir,
390+
"primary_data",
391+
"171020_NB500968_00002_AHGXXXX")))
392+
for filen in ("statistics.info",
393+
"statistics_full.info",
394+
"per_lane_statistics.info",
395+
"per_lane_sample_stats.info",
396+
"seq_len_statistics.info",
397+
"processing_qc.html",):
398+
self.assertTrue(os.path.isfile(
399+
os.path.join(analysis_dir,filen)),
400+
"Missing file: %s" % filen)
401+
213402
#@unittest.skip("Skipped")
214403
def test_makefastqs_10x_atac_protocol_truncate_reads(self):
215404
"""

0 commit comments

Comments
 (0)