Aye10032's picture
download
raw
14 kB
import sys
from pathlib import Path
import click
from loguru import logger
from bioflow_sim import __version__
from bioflow_sim.core.config import load_batch_config
from bioflow_sim.orchestration.batch import run_batch, select_cases
from bioflow_sim.simulators.bulk_rna import (
BULK_RNA_LAYOUTS,
BULK_RNA_STRANDEDNESS,
simulate_bulk_rna,
)
from bioflow_sim.simulators.chromatin import CHROMATIN_ASSAYS, simulate_chromatin
from bioflow_sim.simulators.dna import DNA_TECHNOLOGY_NAMES, simulate_dna
from bioflow_sim.simulators.hic import HIC_ENZYMES, simulate_hic
from bioflow_sim.simulators.methylation import METHYLATION_PROTOCOLS, simulate_methylation
from bioflow_sim.simulators.scrna import SCRNA_PROTOCOLS, simulate_scrna
from bioflow_sim.simulators.tumor_normal import simulate_tumor_normal
def configure_logging(verbose: bool) -> None:
logger.remove()
logger.add(
sys.stderr,
level='DEBUG' if verbose else 'INFO',
format='<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | {message}',
colorize=sys.stderr.isatty(),
)
@click.group(context_settings={'help_option_names': ['-h', '--help']})
@click.version_option(__version__)
@click.option('-v', '--verbose', is_flag=True, help='Enable debug logging.')
def cli(verbose: bool) -> None:
"""Generate deterministic sequencing fixtures from reference sequences."""
configure_logging(verbose)
@cli.command('batch')
@click.argument(
'config_path',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
)
@click.option(
'--case',
'case_names',
multiple=True,
help='Run only the selected named case; repeat to select multiple cases.',
)
@click.option('--list-cases', is_flag=True, help='List configured cases without running them.')
@click.option('--dry-run', is_flag=True, help='Resolve and display cases without generating files.')
@click.option('--continue-on-error', is_flag=True, help='Continue after a case fails.')
def batch_command(
config_path: Path,
case_names: tuple[str, ...],
list_cases: bool,
dry_run: bool,
continue_on_error: bool,
) -> None:
"""Generate multiple cases from a TOML configuration file."""
try:
config = load_batch_config(config_path)
if list_cases:
cases = select_cases(config, case_names) if case_names else config.cases
for case in cases:
state = 'enabled' if case.enabled else 'disabled'
click.echo(f'{case.name}\t{case.simulator}\t{state}')
return
results = run_batch(
config,
selected_names=case_names,
dry_run=dry_run,
continue_on_error=continue_on_error,
)
except (OSError, TypeError, ValueError) as error:
raise click.ClickException(str(error)) from error
completed = sum(result.status == 'completed' for result in results)
planned = sum(result.status == 'planned' for result in results)
failed = sum(result.status == 'failed' for result in results)
click.echo(f'Batch summary: completed={completed} planned={planned} failed={failed}')
if failed:
raise click.ClickException(f'{failed} case(s) failed')
@cli.command('dna')
@click.option(
'--reference',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
required=True,
help='Genome FASTA, optionally gzip-compressed.',
)
@click.option('--output-dir', type=click.Path(path_type=Path, file_okay=False), required=True)
@click.option('--sample', default='S1', show_default=True)
@click.option(
'--technology',
'technology_name',
type=click.Choice(DNA_TECHNOLOGY_NAMES),
required=True,
)
@click.option('--reads', type=click.IntRange(min=1), default=1_000, show_default=True)
@click.option('--seed', type=int, default=114514, show_default=True)
@click.option('--read-length', type=click.IntRange(min=20), default=None)
@click.option('--fragment-mean', type=click.IntRange(min=40), default=350, show_default=True)
@click.option('--fragment-sd', type=click.IntRange(min=0), default=35, show_default=True)
@click.option('--long-read-mean', type=click.IntRange(min=100), default=None)
@click.option('--long-read-sd', type=click.IntRange(min=0), default=None)
@click.option('--snvs', type=click.IntRange(min=0), default=0, show_default=True)
def dna_command(**kwargs: object) -> None:
"""Simulate Illumina, PacBio, or Oxford Nanopore genomic reads."""
try:
simulate_dna(**kwargs)
except (OSError, ValueError) as error:
raise click.ClickException(str(error)) from error
@cli.command('tumor-normal')
@click.option(
'--reference',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
required=True,
help='Genome FASTA, optionally gzip-compressed.',
)
@click.option(
'--candidate-targets',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
required=True,
help='BED intervals from which the simulated target panel is selected.',
)
@click.option('--output-dir', type=click.Path(path_type=Path, file_okay=False), required=True)
@click.option('--pair-name', default='PAIR1', show_default=True)
@click.option('--normal-sample', default='NORMAL', show_default=True)
@click.option('--tumor-sample', default='TUMOR', show_default=True)
@click.option('--target-count', type=click.IntRange(min=1), default=10, show_default=True)
@click.option('--target-width', type=click.IntRange(min=1), default=1_000, show_default=True)
@click.option('--normal-depth', type=click.FloatRange(min=0, min_open=True), default=30.0, show_default=True)
@click.option('--tumor-depth', type=click.FloatRange(min=0, min_open=True), default=80.0, show_default=True)
@click.option('--tumor-purity', type=click.FloatRange(min=0, max=1), default=0.6, show_default=True)
@click.option('--germline-snvs', type=click.IntRange(min=0), default=10, show_default=True)
@click.option('--clonal-snvs', type=click.IntRange(min=0), default=5, show_default=True)
@click.option('--subclonal-snvs', type=click.IntRange(min=0), default=5, show_default=True)
@click.option('--subclone-fraction', type=click.FloatRange(min=0, max=1), default=0.25, show_default=True)
@click.option('--read-length', type=click.IntRange(min=20), default=100, show_default=True)
@click.option('--fragment-mean', type=click.IntRange(min=40), default=300, show_default=True)
@click.option('--fragment-sd', type=click.IntRange(min=0), default=40, show_default=True)
@click.option('--seed', type=int, default=114514, show_default=True)
def tumor_normal_command(**kwargs: object) -> None:
"""Simulate a targeted normal-tumor DNA pair with expected somatic VAFs."""
try:
simulate_tumor_normal(**kwargs)
except (OSError, ValueError) as error:
raise click.ClickException(str(error)) from error
@cli.command('bulk-rna')
@click.option(
'--transcripts-path',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
required=True,
)
@click.option(
'--annotation-path',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
default=None,
)
@click.option('--output-dir', type=click.Path(path_type=Path, file_okay=False), required=True)
@click.option('--sample-prefix', default='RNA', show_default=True)
@click.option('--samples-per-group', type=click.IntRange(min=1), default=2, show_default=True)
@click.option('--reads-per-sample', type=click.IntRange(min=1), default=1_000, show_default=True)
@click.option('--layout', type=click.Choice(BULK_RNA_LAYOUTS), default='pe', show_default=True)
@click.option(
'--strandedness',
type=click.Choice(BULK_RNA_STRANDEDNESS),
default='unstranded',
show_default=True,
)
@click.option('--read-length', type=click.IntRange(min=20), default=150, show_default=True)
@click.option('--fragment-mean', type=click.IntRange(min=40), default=350, show_default=True)
@click.option('--fragment-sd', type=click.IntRange(min=0), default=35, show_default=True)
@click.option('--fold-change', type=click.FloatRange(min=0.01), default=4.0, show_default=True)
@click.option('--seed', type=int, default=114514, show_default=True)
def bulk_rna_command(**kwargs: object) -> None:
"""Simulate control/treatment bulk RNA-seq samples."""
try:
simulate_bulk_rna(**kwargs)
except (OSError, ValueError) as error:
raise click.ClickException(str(error)) from error
@cli.command('chromatin')
@click.option(
'--reference',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
required=True,
)
@click.option('--output-dir', type=click.Path(path_type=Path, file_okay=False), required=True)
@click.option('--sample', default='EP1', show_default=True)
@click.option('--assay', type=click.Choice(CHROMATIN_ASSAYS), required=True)
@click.option('--reads', type=click.IntRange(min=1), default=1_000, show_default=True)
@click.option('--peaks', type=click.IntRange(min=1), default=20, show_default=True)
@click.option('--peak-width', type=click.IntRange(min=10), default=500, show_default=True)
@click.option('--enrichment', type=click.FloatRange(min=0, max=1), default=0.8, show_default=True)
@click.option('--read-length', type=click.IntRange(min=20), default=75, show_default=True)
@click.option('--fragment-mean', type=click.IntRange(min=40), default=250, show_default=True)
@click.option('--fragment-sd', type=click.IntRange(min=0), default=50, show_default=True)
@click.option('--seed', type=int, default=114514, show_default=True)
def chromatin_command(**kwargs: object) -> None:
"""Simulate ATAC-seq, ChIP-seq, or CUT&Tag paired reads."""
try:
simulate_chromatin(**kwargs)
except (OSError, ValueError) as error:
raise click.ClickException(str(error)) from error
@cli.command('methylation')
@click.option(
'--reference',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
required=True,
)
@click.option('--output-dir', type=click.Path(path_type=Path, file_okay=False), required=True)
@click.option('--sample', default='METH1', show_default=True)
@click.option('--protocol', type=click.Choice(METHYLATION_PROTOCOLS), required=True)
@click.option('--reads', type=click.IntRange(min=1), default=1_000, show_default=True)
@click.option('--sites', type=click.IntRange(min=1), default=500, show_default=True)
@click.option('--methylation-rate', type=click.FloatRange(min=0, max=1), default=0.7, show_default=True)
@click.option('--conversion-rate', type=click.FloatRange(min=0, max=1), default=0.99, show_default=True)
@click.option('--read-length', type=click.IntRange(min=20), default=100, show_default=True)
@click.option('--fragment-mean', type=click.IntRange(min=40), default=300, show_default=True)
@click.option('--fragment-sd', type=click.IntRange(min=0), default=40, show_default=True)
@click.option('--seed', type=int, default=114514, show_default=True)
def methylation_command(**kwargs: object) -> None:
"""Simulate WGBS or EM-seq paired reads and CpG truth."""
try:
simulate_methylation(**kwargs)
except (OSError, ValueError) as error:
raise click.ClickException(str(error)) from error
@cli.command('hic')
@click.option(
'--reference',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
required=True,
)
@click.option('--output-dir', type=click.Path(path_type=Path, file_okay=False), required=True)
@click.option('--sample', default='HIC1', show_default=True)
@click.option('--enzyme', 'enzyme_name', type=click.Choice(HIC_ENZYMES), default='mboi', show_default=True)
@click.option('--reads', type=click.IntRange(min=1), default=1_000, show_default=True)
@click.option('--read-length', type=click.IntRange(min=20), default=100, show_default=True)
@click.option('--intra-rate', type=click.FloatRange(min=0, max=1), default=0.9, show_default=True)
@click.option('--mean-distance', type=click.IntRange(min=1), default=50_000, show_default=True)
@click.option('--seed', type=int, default=114514, show_default=True)
def hic_command(**kwargs: object) -> None:
"""Simulate restriction-enzyme Hi-C reads and contact truth."""
try:
simulate_hic(**kwargs)
except (OSError, ValueError) as error:
raise click.ClickException(str(error)) from error
@cli.command('scrna')
@click.option(
'--transcripts-path',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
required=True,
help='Transcript FASTA, optionally gzip-compressed.',
)
@click.option(
'--annotation-path',
type=click.Path(path_type=Path, exists=True, dir_okay=False, readable=True),
default=None,
help='Matching GTF used to produce a gene-by-cell matrix.',
)
@click.option('--output-dir', type=click.Path(path_type=Path, file_okay=False), required=True)
@click.option('--sample', default='SC1', show_default=True)
@click.option(
'--protocol',
type=click.Choice(SCRNA_PROTOCOLS),
required=True,
)
@click.option('--cells', type=click.IntRange(min=1), default=8, show_default=True)
@click.option('--reads-per-cell', type=click.IntRange(min=1), default=1_000, show_default=True)
@click.option('--seed', type=int, default=114514, show_default=True)
@click.option('--cdna-read-length', type=click.IntRange(min=20), default=90, show_default=True)
@click.option('--smartseq-read-length', type=click.IntRange(min=20), default=150, show_default=True)
@click.option('--fragment-mean', type=click.IntRange(min=40), default=400, show_default=True)
@click.option('--fragment-sd', type=click.IntRange(min=0), default=50, show_default=True)
def scrna_command(**kwargs: object) -> None:
"""Simulate Smart-seq2 paired FASTQ or 10x-style barcode/UMI reads."""
try:
simulate_scrna(**kwargs)
except (OSError, ValueError) as error:
raise click.ClickException(str(error)) from error
if __name__ == '__main__':
cli()

Xet Storage Details

Size:
14 kB
·
Xet hash:
b713b1fcc5ee338ad3370bb318c9f5344ee24865ed07c30f11113a1d545a5916

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.